Spaces:
Sleeping
Sleeping
| // @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 }; | |
| } | |