diff --git a/visual_workflow_builder/backend/api/screen_capture.py b/visual_workflow_builder/backend/api/screen_capture.py index af86e6f7f..780be3b16 100644 --- a/visual_workflow_builder/backend/api/screen_capture.py +++ b/visual_workflow_builder/backend/api/screen_capture.py @@ -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 diff --git a/visual_workflow_builder/backend/api_v3/dag_execute.py b/visual_workflow_builder/backend/api_v3/dag_execute.py index b141d1429..464efa348 100644 --- a/visual_workflow_builder/backend/api_v3/dag_execute.py +++ b/visual_workflow_builder/backend/api_v3/dag_execute.py @@ -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', diff --git a/visual_workflow_builder/frontend_v4/src/components/CapturePanel.tsx b/visual_workflow_builder/frontend_v4/src/components/CapturePanel.tsx index b3f04f3ca..a5296a5ba 100644 --- a/visual_workflow_builder/frontend_v4/src/components/CapturePanel.tsx +++ b/visual_workflow_builder/frontend_v4/src/components/CapturePanel.tsx @@ -111,9 +111,30 @@ export default function CapturePanel({ } }; + // Capture intelligente — auto-détection OS + const doSmartCapture = async () => { + const isWindows = navigator.platform?.includes('Win') || navigator.userAgent?.includes('Windows'); + if (isWindows) { + try { + const resp = await fetch('/api/screen-capture/capture-windows', { method: 'POST' }); + const data = await resp.json(); + if (data.image) { + setCurrentCapture({ + screenshot_base64: data.image, + width: data.width, + height: data.height, + source: 'windows', + } as any); + } + } catch {} + } else { + onCapture(); + } + }; + const handleTimerCapture = () => { if (timerSeconds === 0) { - onCapture(); + doSmartCapture(); return; } @@ -127,7 +148,7 @@ export default function CapturePanel({ } else { clearInterval(interval); setCountdown(null); - onCapture(); + doSmartCapture(); } }, 1000); }; @@ -145,33 +166,10 @@ export default function CapturePanel({

Capture

- {/* Contrôles de capture */} + {/* Capture — auto-détection OS navigateur */}
- -