QnoM commited on
Commit
5925fe4
·
verified ·
1 Parent(s): f81a19e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +73 -89
app.py CHANGED
@@ -1,38 +1,41 @@
1
  import gradio as gr
2
  from gradio_htmlinjector import HTMLInjector
3
 
4
- # ---------- logique serveur ----------
5
- def on_submit(user_msg: str, conv: list[tuple[str, str]]):
6
- # 1) ajoute (user_msg, question) et indique qu'il faut ouvrir la popup
7
- if conv is None:
8
- conv = []
9
- user_msg = (user_msg or "").strip()
10
- if not user_msg:
11
- return conv, False, "" # pas de popup
12
-
13
- bot_question = "Ceci est une réponse de démo.\n\n**Confirmez-vous l’action ?**"
14
- new_conv = conv + [(user_msg, bot_question)]
15
- confirm_needed = True
16
- return new_conv, confirm_needed, "" # (maj chatbot via state_conv, flag popup, clear textbox)
17
-
18
- def handle_choice(choice: str, conv: list[tuple[str, str]]):
19
- # 3) ajoute (Oui/Non, confirmation) ; ignore les resets vides
20
- if conv is None:
21
- conv = []
22
  c = (choice or "").strip().lower()
23
- if not c:
24
- return conv, "" # ignore le "change" déclenché par le reset
25
-
26
  if c in {"oui", "o", "yes", "y"}:
27
- user_choice, bot_reply = "Oui", "✅ Vous avez choisi **OUI**. Action validée."
 
28
  elif c in {"non", "n", "no"}:
29
- user_choice, bot_reply = "Non", "❌ Vous avez choisi **NON**. Action annulée."
 
30
  else:
31
- return conv, ""
 
 
32
 
33
- return conv + [(user_choice, bot_reply)], "" # reset du pont via ""
 
 
 
34
 
35
- # ---------- assets popup ----------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  def inject_assets():
37
  js = r"""
38
  function setGradioValue(elemId, value){
@@ -45,90 +48,71 @@ def inject_assets():
45
  input.dispatchEvent(new Event('input', {bubbles:true}));
46
  input.dispatchEvent(new Event('change', {bubbles:true}));
47
  }
48
- """
49
- css = r"""
50
- #confirm-modal{
51
- display:none; position:fixed; inset:0; z-index:9999;
52
- background: rgba(0,0,0,.45);
53
- align-items:center; justify-content:center;
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  }
55
- #confirm-card{
56
- background:#fff; border:1px solid #ccc; border-radius:10px;
57
- padding:20px; min-width:320px; max-width:90vw; box-shadow:0 10px 30px rgba(0,0,0,.2);
 
 
58
  }
 
 
 
 
 
 
59
  #confirm-actions{ display:flex; gap:8px; justify-content:flex-end; margin-top:14px; }
 
 
60
  #bridge-choice{ display:none; } /* pont caché mais monté */
61
  """
62
  html = r"""
63
  <div id="confirm-modal" role="dialog" aria-modal="true" aria-labelledby="confirm-title">
64
  <div id="confirm-card">
65
  <h3 id="confirm-title">Confirmer ?</h3>
66
- <p>Souhaitez-vous poursuivre ?</p>
67
  <div id="confirm-actions">
68
- <button type="button"
69
- onclick="setGradioValue('bridge-choice','non'); document.getElementById('confirm-modal').style.display='none'; document.body.style.overflow=''">
70
- Non
71
- </button>
72
- <button type="button" class="primary"
73
- onclick="setGradioValue('bridge-choice','oui'); document.getElementById('confirm-modal').style.display='none'; document.body.style.overflow=''">
74
- Oui
75
- </button>
76
  </div>
77
  </div>
78
  </div>
79
  """
80
  return {"css": css, "js": js, "body_html": html}
81
 
82
- # ---------- UI ----------
83
- with gr.Blocks(title="Chat + Popup (avec .then)") as demo:
84
  html_injector = HTMLInjector()
85
-
86
- chat = gr.Chatbot(height=420)
87
  user_in = gr.Textbox(placeholder="Tapez un message…", lines=2)
88
- state_conv = gr.State([]) # vérité de la conversation
89
- confirm_flag = gr.State(False) # flag “ouvrir popup ?”
90
  bridge_choice = gr.Textbox(visible=True, elem_id="bridge-choice") # pont oui/non
91
 
92
- # (A) submit -> met à jour état & textbox
93
- evt = user_in.submit(
94
- fn=on_submit,
95
- inputs=[user_in, state_conv],
96
- outputs=[state_conv, confirm_flag, user_in], # maj state_conv, flag, clear input
97
- )
98
-
99
- # (B) refléter l'état dans le Chatbot (immédiatement après)
100
- evt = evt.then(
101
- fn=lambda s: s, inputs=state_conv, outputs=chat
102
- )
103
-
104
- # (C) puis ouvrir la popup côté client *si* confirm_flag = True
105
- evt.then(
106
- fn=None, inputs=[confirm_flag], outputs=None,
107
- js="""
108
- (flag) => {
109
- if(!flag) return;
110
- const start = Date.now();
111
- (function tick(){
112
- const modal = document.getElementById('confirm-modal');
113
- if (modal) {
114
- modal.style.display = 'flex';
115
- document.body.style.overflow = 'hidden';
116
- return;
117
- }
118
- if (Date.now() - start < 3000) setTimeout(tick, 60);
119
- })();
120
- }
121
- """,
122
- )
123
 
124
- # (D) choix utilisateur -> ajoute (Oui/Non, confirmation) à la conversation + reflète dans Chatbot
125
- bridge_choice.change(
126
- fn=handle_choice,
127
- inputs=[bridge_choice, state_conv],
128
- outputs=[state_conv, bridge_choice],
129
- ).then(
130
- fn=lambda s: s, inputs=state_conv, outputs=chat
131
- )
132
 
133
  demo.load(fn=inject_assets, inputs=None, outputs=[html_injector])
134
 
 
1
  import gradio as gr
2
  from gradio_htmlinjector import HTMLInjector
3
 
4
+ # 1) Choix dans la popup -> ajoute (Oui/Non, confirmation)
5
+ def handle_choice(choice: str, chat_history: list[tuple[str, str]]):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  c = (choice or "").strip().lower()
 
 
 
7
  if c in {"oui", "o", "yes", "y"}:
8
+ user_choice = "Oui"
9
+ reply = "✅ Vous avez choisi **OUI**. Action validée."
10
  elif c in {"non", "n", "no"}:
11
+ user_choice = "Non"
12
+ reply = "❌ Vous avez choisi **NON**. Action annulée."
13
  else:
14
+ # ignore les valeurs inattendues (évite les "choix non reconnu" parasites)
15
+ return chat_history, ""
16
+ return chat_history + [(user_choice, reply)], "" # reset du pont
17
 
18
+ # 2) Soumission utilisateur : réponse fixe + ouverture popup
19
+ def on_submit(user_msg: str, chat_history: list[tuple[str, str]]):
20
+ if not user_msg:
21
+ return chat_history, "", {}
22
 
23
+ assistant_reply = "Ceci est une réponse de démonstration.\n\nConfirmez-vous l’action ?"
24
+ chat_history = chat_history + [(user_msg, assistant_reply)]
25
+
26
+ # IIFE + retry: ouvre la popup quand showModal est prêt
27
+ open_popup_js = """
28
+ (function openWhenReady(){
29
+ var start = Date.now();
30
+ (function tick(){
31
+ if (typeof showModal === 'function') { showModal(); return; }
32
+ if (Date.now() - start < 3000) { setTimeout(tick, 60); }
33
+ })();
34
+ })();
35
+ """
36
+ return chat_history, "", {"js": open_popup_js} # clear textbox
37
+
38
+ # 3) Injection: définit showModal/hideModal + HTML/CSS de la popup
39
  def inject_assets():
40
  js = r"""
41
  function setGradioValue(elemId, value){
 
48
  input.dispatchEvent(new Event('input', {bubbles:true}));
49
  input.dispatchEvent(new Event('change', {bubbles:true}));
50
  }
51
+ let _modalKeyHandler = null, _lastFocused = null;
52
+ function getFocusable(el){
53
+ return Array.from(el.querySelectorAll('button,[href],input,select,textarea,[tabindex]:not([tabindex="-1"])'))
54
+ .filter(e => !e.hasAttribute('disabled') && e.offsetParent !== null);
55
+ }
56
+ function showModal(){
57
+ const modal = document.getElementById('confirm-modal'); if(!modal) return;
58
+ modal.style.display = 'flex'; document.body.style.overflow = 'hidden';
59
+ modal.setAttribute('aria-hidden','false'); _lastFocused = document.activeElement;
60
+ const foc = getFocusable(modal); const first=foc[0], last=foc[foc.length-1]; if(first) first.focus();
61
+ _modalKeyHandler = function(e){
62
+ if (e.key==='Escape'){ e.preventDefault(); e.stopPropagation(); return false; }
63
+ if (e.key==='Tab'){
64
+ if (foc.length===0){ e.preventDefault(); return; }
65
+ if (e.shiftKey){ if (document.activeElement===first){ e.preventDefault(); last.focus(); } }
66
+ else { if (document.activeElement===last){ e.preventDefault(); first.focus(); } }
67
+ }
68
+ };
69
+ document.addEventListener('keydown', _modalKeyHandler, true);
70
  }
71
+ function hideModal(){
72
+ const modal = document.getElementById('confirm-modal'); if(!modal) return;
73
+ modal.style.display = 'none'; modal.setAttribute('aria-hidden','true'); document.body.style.overflow = '';
74
+ if (_modalKeyHandler){ document.removeEventListener('keydown', _modalKeyHandler, true); _modalKeyHandler=null; }
75
+ if (_lastFocused && typeof _lastFocused.focus==='function'){ try{ _lastFocused.focus(); } catch{} }
76
  }
77
+ """
78
+ css = r"""
79
+ #confirm-modal{ display:none; position:fixed; inset:0; z-index:9999;
80
+ background: rgba(0,0,0,.45); align-items:center; justify-content:center; }
81
+ #confirm-card{ background:#fff; color:#111; border:1px solid #ccc; border-radius:10px;
82
+ padding:20px; min-width:320px; max-width:90vw; box-shadow:0 10px 30px rgba(0,0,0,.2); }
83
  #confirm-actions{ display:flex; gap:8px; justify-content:flex-end; margin-top:14px; }
84
+ #confirm-actions button{ cursor:pointer; padding:8px 14px; border-radius:8px; border:1px solid #bbb; background:#f4f4f5; }
85
+ #confirm-actions button.primary{ background:#2563eb; color:#fff; border-color:transparent; }
86
  #bridge-choice{ display:none; } /* pont caché mais monté */
87
  """
88
  html = r"""
89
  <div id="confirm-modal" role="dialog" aria-modal="true" aria-labelledby="confirm-title">
90
  <div id="confirm-card">
91
  <h3 id="confirm-title">Confirmer ?</h3>
92
+ <p>Souhaitez-vous continuer ?</p>
93
  <div id="confirm-actions">
94
+ <button type="button" onclick="setGradioValue('bridge-choice','non'); hideModal()">Non</button>
95
+ <button type="button" class="primary" onclick="setGradioValue('bridge-choice','oui'); hideModal()">Oui</button>
 
 
 
 
 
 
96
  </div>
97
  </div>
98
  </div>
99
  """
100
  return {"css": css, "js": js, "body_html": html}
101
 
102
+ # 4) UI
103
+ with gr.Blocks(title="Démo Chat + Popup", fill_height=True) as demo:
104
  html_injector = HTMLInjector()
105
+ chat = gr.Chatbot(label="Chatbot", height=420)
 
106
  user_in = gr.Textbox(placeholder="Tapez un message…", lines=2)
 
 
107
  bridge_choice = gr.Textbox(visible=True, elem_id="bridge-choice") # pont oui/non
108
 
109
+ # A) soumission -> affiche (user, question) + ouvre popup (IIFE via HTMLInjector)
110
+ user_in.submit(fn=on_submit, inputs=[user_in, chat],
111
+ outputs=[chat, user_in, html_injector])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
+ # B) clic Oui/Non -> ajoute (Oui/Non, confirmation)
114
+ bridge_choice.change(fn=handle_choice, inputs=[bridge_choice, chat],
115
+ outputs=[chat, bridge_choice])
 
 
 
 
 
116
 
117
  demo.load(fn=inject_assets, inputs=None, outputs=[html_injector])
118