feat: capture Windows auto-détection OS, chat Léa agrandi, UX améliorée

- Capture auto : détecte OS navigateur → capture Windows ou Linux
- Timer capture utilise aussi la smart capture
- Heartbeat background permanent (même sans session)
- Tri screenshots par date (plus de vieilles captures)
- Chat Léa : 450x650, polices 11pt, redimensionnable, meilleur contraste
- Bouton Exécuter : "Linux" + "Windows" avec feedback visuel
- Délai 5s avant replay Windows (temps de réduire le navigateur)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-03-17 23:03:53 +01:00
parent 8175b39eba
commit 4e217e30dd
4 changed files with 81 additions and 58 deletions

View File

@@ -136,20 +136,23 @@ def capture_windows():
project_root = Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
live_dir = project_root / "data" / "training" / "live_sessions"
# Trouver la session la plus récente
sessions = sorted(live_dir.glob("sess_*/shots"), key=lambda p: p.parent.name, reverse=True)
if not sessions:
return jsonify({'error': 'Aucune session Windows trouvée'}), 404
# Chercher aussi dans les sous-dossiers machine (multi-machine)
import time as _time
# Chercher le screenshot plein écran le plus récent (full ou heartbeat, pas les crops)
latest_shot = None
for session_shots in sessions[:3]:
shots = [s for s in session_shots.glob("*.png")
if "full" in s.name or "heartbeat" in s.name or "focus" in s.name]
if shots:
shots.sort(key=lambda p: p.stat().st_mtime, reverse=True)
latest_shot = shots[0]
break
# Chercher le screenshot le plus récent dans TOUS les dossiers
all_shots = []
for pattern in ["bg_*/shots/heartbeat_*.png", "sess_*/shots/heartbeat_*.png",
"*/bg_*/shots/heartbeat_*.png", "bg_*/shots/shot_*_full.png",
"sess_*/shots/shot_*_full.png"]:
all_shots.extend(live_dir.glob(pattern))
if not all_shots:
return jsonify({'error': 'Aucun screenshot Windows. Lancez l\'agent V1 sur le PC cible.'}), 404
# Trier par date de modification (le plus récent en premier)
all_shots.sort(key=lambda p: p.stat().st_mtime, reverse=True)
latest_shot = all_shots[0]
age_seconds = _time.time() - latest_shot.stat().st_mtime
if not latest_shot:
return jsonify({'error': 'Aucun screenshot Windows disponible'}), 404
@@ -169,6 +172,8 @@ def capture_windows():
'source': 'windows',
'file': str(latest_shot.name),
'session': latest_shot.parent.parent.name,
'age_seconds': round(age_seconds, 1),
'fresh': age_seconds < 30,
})
except Exception as e:
return jsonify({'error': str(e)}), 500

View File

@@ -879,6 +879,16 @@ def execute_windows():
if not data:
return jsonify({'error': 'Aucune donnée'}), 400
# Injecter un délai de 5s avant la première action
# pour laisser le temps à l'utilisateur de réduire le navigateur
if 'actions' in data and data['actions']:
data['actions'].insert(0, {
'type': 'wait',
'action_id': 'wait_before_start',
'parameters': {'duration_ms': 5000},
'text': '',
})
# Mapper les types VWB → types executor Windows
TYPE_MAP = {
'click_anchor': 'click',