// @ts-nocheck import { captureModeSuffix, isSyntheticLoopMode } from "./config.js"; import { captureFrame, drawSyntheticFrame } from "./captureCanvas.js"; import { setButtonState, setCaptureStatus, setLoopButtonState } from "./captureUi.js"; import { clearScheduledScan, scheduleNextScan, startCountdownTimer, stopCountdownTimer, updateCountdown } from "./timing.js"; import { state } from "./state.js"; export function createCaptureFlow({ requestAnalysis, recoverStaleAnalysis, setAnalysisPending }) { async function captureOnceAndAnalyze() { if (isSyntheticLoopMode()) { drawSyntheticFrame(); setCaptureStatus(`Analyzing current desktop capture${captureModeSuffix()}...`); state.allowAnalysisClick = true; requestAnalysis(false); state.allowAnalysisClick = false; return; } const source = (document.querySelector('#desktop-capture-source select, #desktop-capture-source input')?.value || state.source || '🖥 Screen').toLowerCase(); if (!navigator.mediaDevices?.getDisplayMedia) { setCaptureStatus('Desktop capture is not available in this browser or iframe.'); return; } let stream = null; let video = null; try { setCaptureStatus('Waiting for browser screen picker...'); stream = await navigator.mediaDevices.getDisplayMedia({ video: { cursor: 'always', displaySurface: source.includes('window') ? 'window' : 'monitor' }, monitorTypeSurfaces: 'include', selfBrowserSurface: 'exclude', surfaceSwitching: 'include', audio: false, }); video = document.createElement('video'); video.autoplay = true; video.muted = true; video.playsInline = true; video.srcObject = stream; await video.play(); state.video = video; state.stream = stream; state.sessionStartedAt = Date.now(); state.active = true; state.loopPaused = false; state.sessionPausedMs = 0; state.sessionPauseStartedAt = null; captureFrame(); state.allowAnalysisClick = true; setCaptureStatus('Analyzing selected desktop frame...'); requestAnalysis(false); state.allowAnalysisClick = false; } catch (error) { if (stream) for (const track of stream.getTracks()) track.stop(); state.video = null; state.stream = null; state.active = false; state.sessionStartedAt = null; state.loopPaused = false; state.sessionPausedMs = 0; state.sessionPauseStartedAt = null; if (state.previewTimer) { window.clearInterval(state.previewTimer); state.previewTimer = null; } setButtonState(false); const message = error?.name === 'NotAllowedError' ? 'Desktop capture was cancelled or blocked. Pick a screen/window to analyze one frame.' : `Desktop capture failed: ${error?.message || error}`; setCaptureStatus(message); console.warn('One-shot desktop capture failed:', error); } finally { if (video) video.srcObject = null; if (stream) for (const track of stream.getTracks()) track.stop(); if (state.previewTimer) { window.clearInterval(state.previewTimer); state.previewTimer = null; } state.video = null; state.stream = null; state.active = false; state.sessionStartedAt = null; state.loopPaused = false; state.sessionPausedMs = 0; state.sessionPauseStartedAt = null; setButtonState(false); } } function captureAndAnalyze(resetTimer = false, rearmNextScan = true) { window['__trackwhatDesktopCaptureTrace']?.('analysis.capture', { resetTimer, active: state.active, analysisPending: state.analysisPending, scanTimer: Boolean(state.scanTimer), }); if (resetTimer && state.scanTimer) { window.clearTimeout(state.scanTimer); state.scanTimer = null; } state.nextScanAt = null; if (captureFrame()) window.setTimeout(() => requestAnalysis(rearmNextScan), 250); else if (state.active) recoverStaleAnalysis('frame-not-ready'); } state.captureAndAnalyze = captureAndAnalyze; function startLivePreviewLoop() { if (state.previewTimer) window.clearInterval(state.previewTimer); state.previewTimer = window.setInterval(() => { if (state.active) captureFrame(false); }, 1000); } async function startCapture() { if (state.active) return; window['__trackwhatDesktopCaptureTrace']?.('capture.start', { active: state.active, automationMode: state.automationMode }); const source = (document.querySelector('#desktop-capture-source select, #desktop-capture-source input')?.value || state.source || '🖥 Screen').toLowerCase(); let stream = null; let video = null; try { if (isSyntheticLoopMode()) { state.sessionStartedAt = Date.now(); state.loopPaused = false; state.sessionPausedMs = 0; state.sessionPauseStartedAt = null; setButtonState(true); state.active = true; captureAndAnalyze(); startCountdownTimer(); return; } if (!navigator.mediaDevices?.getDisplayMedia) throw new Error('Screen capture is not available in this browser or iframe.'); setCaptureStatus('Waiting for browser screen picker...'); stream = await navigator.mediaDevices.getDisplayMedia({ video: { cursor: 'always', displaySurface: source.includes('window') ? 'window' : 'monitor' }, monitorTypeSurfaces: 'include', selfBrowserSurface: 'exclude', surfaceSwitching: 'include', audio: false, }); state.stream = stream; video = document.createElement('video'); state.video = video; video.autoplay = true; video.muted = true; video.playsInline = true; video.srcObject = stream; await video.play(); state.active = true; state.sessionStartedAt = Date.now(); state.loopPaused = false; setButtonState(true); captureAndAnalyze(); startLivePreviewLoop(); startCountdownTimer(); const track = stream.getVideoTracks()[0]; if (track) track.addEventListener('ended', stopCapture); return; } catch (error) { setButtonState(false); const message = error?.name === 'NotAllowedError' ? 'Desktop capture was cancelled or blocked. Click Start desktop capture and choose a screen/window in the browser picker.' : `Desktop capture failed: ${error?.message || error}`; setCaptureStatus(message); console.warn('Desktop capture start failed:', error); if (stream) for (const track of stream.getTracks()) track.stop(); state.video = null; state.stream = null; state.active = false; state.sessionStartedAt = null; state.loopPaused = false; if (state.previewTimer) { window.clearInterval(state.previewTimer); state.previewTimer = null; } } } function stopCapture() { window['__trackwhatDesktopCaptureTrace']?.('capture.stop', { active: state.active, analysisPending: state.analysisPending, scanTimer: Boolean(state.scanTimer), previewTimer: Boolean(state.previewTimer) }); if (state.timer) { window.clearInterval(state.timer); state.timer = null; } if (state.analysisGuardTimer) { window.clearTimeout(state.analysisGuardTimer); state.analysisGuardTimer = null; } if (state.scanTimer) { window.clearTimeout(state.scanTimer); state.scanTimer = null; } if (state.previewTimer) { window.clearInterval(state.previewTimer); state.previewTimer = null; } stopCountdownTimer(); if (state.analysisStartTimer) { window.clearTimeout(state.analysisStartTimer); state.analysisStartTimer = null; } setAnalysisPending(false); state.allowAnalysisClick = false; state.nextScanAt = null; state.loopPaused = false; state.sessionPausedMs = 0; state.sessionPauseStartedAt = null; if (state.stream) { for (const track of state.stream.getTracks()) track.stop(); state.stream = null; } state.active = false; state.sessionStartedAt = null; state.lastProjectName = ''; state.sameProjectStreak = 0; state.smartDelayMs = 0; setButtonState(false); } function pauseCaptureLoop() { if (!state.active || state.loopPaused) return; window['__trackwhatDesktopCaptureTrace']?.('capture.pause', { active: state.active, analysisPending: state.analysisPending, scanTimer: Boolean(state.scanTimer), previewTimer: Boolean(state.previewTimer), }); state.loopPaused = true; state.sessionPauseStartedAt = Date.now(); if (state.scanTimer) { window.clearTimeout(state.scanTimer); state.scanTimer = null; } if (state.previewTimer) { window.clearInterval(state.previewTimer); state.previewTimer = null; } clearScheduledScan(); setButtonState(true); setLoopButtonState(true, true); updateCountdown(); setCaptureStatus(`Desktop capture paused${captureModeSuffix()}. Click Resume loop to continue.`); } function resumeCaptureLoop() { if (!state.active || !state.loopPaused) return; window['__trackwhatDesktopCaptureTrace']?.('capture.resume', { active: state.active, analysisPending: state.analysisPending, }); if (state.sessionPauseStartedAt) { state.sessionPausedMs = (state.sessionPausedMs || 0) + Math.max(0, Date.now() - state.sessionPauseStartedAt); } state.sessionPauseStartedAt = null; state.loopPaused = false; startLivePreviewLoop(); setButtonState(true); setLoopButtonState(false, true); startCountdownTimer(); if (!state.analysisPending) { scheduleNextScan(); } else { updateCountdown(); } } async function toggleCapture() { if (state.active) stopCapture(); else await startCapture(); } function toggleCaptureLoop() { if (!state.active) return; if (state.loopPaused) resumeCaptureLoop(); else pauseCaptureLoop(); } return { captureOnceAndAnalyze, captureAndAnalyze, startLivePreviewLoop, startCapture, stopCapture, toggleCapture, pauseCaptureLoop, resumeCaptureLoop, toggleCaptureLoop }; }