feat(agent_v1): toast paused supervisée Tkinter + Plan B + threshold FIND-TEXT 0.75
Démo GHT 8 mai 2026 — Dom utilise UNIQUEMENT Léa V1 sur Windows pendant la démo (pas le frontend VWB Linux), donc les pause_message du serveur doivent être visuellement évidents sur l'écran Windows. Modifications client validées par Dom + redéployées via SCP (procédure 2026-04-28). 1. ui/paused_toast.py (NEW) — Toast Tkinter custom autonome : Toplevel topmost overrideredirect, fond bleu Léa (#2563EB), 380px, haut-droite, auto-close 15s, click-to-close. Re-pin -topmost à 100/500/2000 ms (Windows démet le flag quand le focus part). Rate limit 3s sur message identique. Aucune dépendance externe (tkinter stdlib uniquement). Thread-safe : root.after si Tk root existe, sinon Tk dédié dans un daemon thread. Remplace plyer qui s'avère silencieux sur Windows 11 (Focus Assist + manque app-id COM). 2. ui/chat_window.py — _add_paused_bubble force la visibilité : La fenêtre Léa démarrait avec root.withdraw() — la bulle paused était bien rendue mais invisible. Ajout deiconify+lift+focus_force avant render, plus appel à show_paused_toast en complément. 3. ui/notifications.py — niveau BLOCAGE déclenche aussi le toast : Quand notify_message reçoit un MessageUtilisateur.BLOCAGE (cible non trouvée, mode apprentissage, fenêtre incorrecte), appelle show_paused_toast en plus de plyer. Couvre la branche supervision client (executor.py:1012) qui ne passe pas par Plan B serveur. 4. core/executor.py — Plan B replay_paused (lignes 1812-1850) : Intercepte data["replay_paused"]=True dans la réponse /replay/next, appelle chat_window._add_paused_bubble si _chat_window_ref défini, sinon fallback notifier.notify. Idempotence via _last_pause_msg_shown pour ne pas spammer (1 toast par (replay_id, message) unique). Threshold FIND-TEXT _find_text_on_screen : 0.50 → 0.75 pour rejeter les faux positifs (placeholders italiques, tabs voisins) et tomber en mode apprentissage humain plutôt qu'un clic au pif. 5. main.py — Wiring ChatWindow → Executor pour Plan B. 6. tools/test_lea_toast.py + ui/_test_paused_toast.py (NEW) — Scripts de test isolé pour validation visuelle rapide sans relancer un replay complet (commande dans les docstrings). Validé visuellement sur DESKTOP-58D5CAC. Toasts apparaissent en haut- droite, fond bleu, auto-close 15s. Test isolé Dom : 3 toasts successifs visibles sans accroc. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -838,10 +838,38 @@ class ChatWindow:
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _add_paused_bubble(self, payload: Dict[str, Any]) -> None:
|
||||
"""Ajouter une bulle paused interactive (asset démo : Léa demande de l'aide)."""
|
||||
"""Ajouter une bulle paused interactive (asset démo : Léa demande de l'aide).
|
||||
|
||||
IMPORTANT (8 mai 2026, démo GHT) : par défaut la fenêtre démarre cachée
|
||||
(`root.withdraw()`). Il FAUT la rendre visible et la forcer au premier
|
||||
plan, sinon Dom ne voit jamais la bulle. On exécute dans le thread
|
||||
tkinter via `root.after(0, ...)`.
|
||||
"""
|
||||
if self._root is None:
|
||||
return
|
||||
self._root.after(0, lambda: self._render_paused_bubble(payload))
|
||||
|
||||
def _show_and_render():
|
||||
try:
|
||||
self._do_show()
|
||||
# Re-pin topmost pour passer devant les apps actives
|
||||
self._root.attributes("-topmost", True)
|
||||
self._root.lift()
|
||||
# Toast topmost en complément (visible même si la chat est
|
||||
# masquée par une fenêtre d'app)
|
||||
try:
|
||||
from .paused_toast import show_paused_toast
|
||||
reason = payload.get("reason") or "Action en attente."
|
||||
show_paused_toast(
|
||||
title="Léa a besoin de votre aide",
|
||||
message=str(reason)[:300],
|
||||
)
|
||||
except Exception:
|
||||
logger.debug("paused_toast launch silenced", exc_info=True)
|
||||
except Exception:
|
||||
logger.debug("force-show chat_window silenced", exc_info=True)
|
||||
self._render_paused_bubble(payload)
|
||||
|
||||
self._root.after(0, _show_and_render)
|
||||
|
||||
def _render_paused_bubble(self, payload: Dict[str, Any]) -> None:
|
||||
tk = self._tk
|
||||
|
||||
Reference in New Issue
Block a user