Spaces:
Sleeping
Sleeping
| // @ts-nocheck | |
| import { captureModeSuffix, effectiveScanMs, formatScanInterval } from "./config.js"; | |
| import { applyAnalysisOutputs, getAnalyzeButton, readAnalysisRequestInputs, readResultSignature } from "./analysisDom.js"; | |
| import { updateSmartDelayFromLatestResult, updateCountdown } from "./timing.js"; | |
| function findAnalyzeDependency() { | |
| const dependencies = window.gradio_config?.dependencies || []; | |
| return dependencies.find((dependency) => dependency?.api_name === 'analyze_screenshot') | |
| || dependencies.find((dependency) => String(dependency?.api_name || '').includes('analyze_screenshot')) | |
| || dependencies[0] | |
| || {}; | |
| } | |
| function makeSessionHash() { | |
| return `trackwhat_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`; | |
| } | |
| function getApiOrigin() { | |
| return window.location.origin; | |
| } | |
| async function readJsonResponse(response) { | |
| const payload = await response.json().catch(() => ({})); | |
| if (!response.ok) { | |
| const error = new Error(payload?.detail || payload?.error || `Analysis request failed (${response.status})`); | |
| error.status = response.status; | |
| error.payload = payload; | |
| throw error; | |
| } | |
| return payload; | |
| } | |
| function shouldFallbackToQueue(error) { | |
| const message = String(error?.message || error || '').toLowerCase(); | |
| return error?.status === 404 | |
| || message.includes('queue') | |
| || message.includes('failed to fetch') | |
| || message.includes('direct http post'); | |
| } | |
| async function callAnalyzeDirect(data) { | |
| const dependency = findAnalyzeDependency(); | |
| const sessionHash = makeSessionHash(); | |
| const fnIndex = Number.isFinite(Number(dependency.id)) ? Number(dependency.id) : 0; | |
| const triggerId = Array.isArray(dependency.targets?.[0]) ? dependency.targets[0][0] : undefined; | |
| const response = await fetch(`${getApiOrigin()}/run/${String(dependency.api_name || 'analyze_screenshot').replace(/^\//, '')}`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| data, | |
| event_data: null, | |
| fn_index: fnIndex, | |
| session_hash: sessionHash, | |
| ...(triggerId !== undefined ? { trigger_id: triggerId } : {}), | |
| }), | |
| }); | |
| return readJsonResponse(response); | |
| } | |
| async function callAnalyzeQueue(data) { | |
| const dependency = findAnalyzeDependency(); | |
| const sessionHash = makeSessionHash(); | |
| const fnIndex = Number.isFinite(Number(dependency.id)) ? Number(dependency.id) : 0; | |
| const triggerId = Array.isArray(dependency.targets?.[0]) ? dependency.targets[0][0] : undefined; | |
| const joinBody = { | |
| data, | |
| event_data: null, | |
| fn_index: fnIndex, | |
| session_hash: sessionHash, | |
| }; | |
| if (triggerId !== undefined) joinBody.trigger_id = triggerId; | |
| await readJsonResponse(await fetch(`${getApiOrigin()}/queue/join`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(joinBody), | |
| })); | |
| return new Promise((resolve, reject) => { | |
| const events = new EventSource(`${getApiOrigin()}/queue/data?session_hash=${encodeURIComponent(sessionHash)}`); | |
| const timeout = window.setTimeout(() => { | |
| events.close(); | |
| reject(new Error('Queued analysis timed out.')); | |
| }, 180000); | |
| events.onmessage = (event) => { | |
| let message = {}; | |
| try { | |
| message = JSON.parse(event.data || '{}'); | |
| } catch (error) { | |
| return; | |
| } | |
| if (message.msg === 'process_completed') { | |
| window.clearTimeout(timeout); | |
| events.close(); | |
| if (message.success === false || message.output?.error) { | |
| reject(new Error(message.output?.error || 'Queued analysis failed.')); | |
| return; | |
| } | |
| resolve(message.output || {}); | |
| } | |
| }; | |
| events.onerror = () => { | |
| window.clearTimeout(timeout); | |
| events.close(); | |
| reject(new Error('Queued analysis connection failed.')); | |
| }; | |
| }); | |
| } | |
| async function callAnalyze(data) { | |
| try { | |
| return await callAnalyzeQueue(data); | |
| } catch (error) { | |
| if (!shouldFallbackToQueue(error)) throw error; | |
| window['__trackwhatDesktopCaptureTrace']?.('analysis.request.queue-fallback', { | |
| message: error?.message || String(error), | |
| status: error?.status || null, | |
| }); | |
| return callAnalyzeDirect(data); | |
| } | |
| } | |
| export function createAnalysisController({ state, scheduleNextScan, setCaptureStatus, onAnalysisPendingChange }) { | |
| function setAnalysisPending(pending) { | |
| state.analysisPending = pending; | |
| if (state.analysisGuardTimer) { | |
| window.clearTimeout(state.analysisGuardTimer); | |
| state.analysisGuardTimer = null; | |
| } | |
| if (pending) { | |
| state.analysisStartedAt = Date.now(); | |
| state.analysisStartSignature = readResultSignature(); | |
| state.analysisSawResultChange = false; | |
| } else { | |
| state.analysisStartedAt = 0; | |
| state.analysisStartSignature = ''; | |
| state.analysisSawResultChange = false; | |
| } | |
| const { button } = getAnalyzeButton(); | |
| if (button) button.dataset.analysisPending = pending ? 'true' : 'false'; | |
| const resultBox = document.getElementById('important-response'); | |
| if (resultBox) resultBox.dataset.analysisPending = pending ? 'true' : 'false'; | |
| const headerButton = document.getElementById('header-analyze-btn'); | |
| if (headerButton) headerButton.dataset.analysisPending = pending ? 'true' : 'false'; | |
| onAnalysisPendingChange?.(pending); | |
| } | |
| function retryAnalysisLater(message, delayMs = 1000) { | |
| window['__trackwhatDesktopCaptureTrace']?.('analysis.retry', { | |
| message, delayMs, active: state.active, analysisPending: state.analysisPending, nextScanAt: state.nextScanAt, | |
| }); | |
| setCaptureStatus(message); | |
| if (state.active) scheduleNextScan(delayMs); | |
| } | |
| function recoverStaleAnalysis(reason = 'manual') { | |
| if (!state.analysisPending) return false; | |
| window['__trackwhatDesktopCaptureTrace']?.('analysis.recover', { reason, startedAt: state.analysisStartedAt, pending: state.analysisPending }); | |
| setAnalysisPending(false); | |
| if (state.active) scheduleNextScan(); | |
| return true; | |
| } | |
| function markAnalysisComplete() { | |
| if (!state.analysisPending) return; | |
| const rearmNextScan = state.analysisRearmNextScan !== false; | |
| const manualOneShot = state.analysisOneShot === true; | |
| if (state.analysisGuardTimer) { | |
| window.clearTimeout(state.analysisGuardTimer); | |
| state.analysisGuardTimer = null; | |
| } | |
| setAnalysisPending(false); | |
| updateSmartDelayFromLatestResult(); | |
| if (state.active && rearmNextScan && !manualOneShot) { | |
| const delayText = state.smartDelayMs ? ` Smart delay added ${Math.round(state.smartDelayMs / 1000)}s.` : ''; | |
| setCaptureStatus(`Desktop capture is live${captureModeSuffix()}. Base interval ${formatScanInterval(effectiveScanMs())}.${delayText}`); | |
| scheduleNextScan(); | |
| } | |
| state.analysisRearmNextScan = true; | |
| state.analysisOneShot = false; | |
| } | |
| function waitForAnalysisCompletion() { | |
| if (state.analysisCompletionTicker) { | |
| window.clearInterval(state.analysisCompletionTicker); | |
| state.analysisCompletionTicker = null; | |
| } | |
| const startedAt = state.analysisStartedAt || Date.now(); | |
| const maxWaitMs = Math.max(10000, effectiveScanMs() * 2 + (state.smartDelayMs || 0) + 2500); | |
| state.analysisCompletionTicker = window.setInterval(() => { | |
| if (!state.analysisPending || startedAt !== state.analysisStartedAt) { | |
| if (state.analysisCompletionTicker) { | |
| window.clearInterval(state.analysisCompletionTicker); | |
| state.analysisCompletionTicker = null; | |
| } | |
| return; | |
| } | |
| const currentSignature = readResultSignature(); | |
| if (currentSignature && currentSignature !== state.analysisStartSignature) { | |
| if (state.analysisCompletionTicker) { | |
| window.clearInterval(state.analysisCompletionTicker); | |
| state.analysisCompletionTicker = null; | |
| } | |
| markAnalysisComplete(); | |
| return; | |
| } | |
| if (Date.now() - startedAt > maxWaitMs) { | |
| if (state.analysisCompletionTicker) { | |
| window.clearInterval(state.analysisCompletionTicker); | |
| state.analysisCompletionTicker = null; | |
| } | |
| markAnalysisComplete(); | |
| } | |
| }, 250); | |
| state.analysisGuardTimer = window.setTimeout(() => { | |
| if (!state.analysisPending || startedAt !== state.analysisStartedAt) return; | |
| markAnalysisComplete(); | |
| }, maxWaitMs); | |
| } | |
| function requestAnalysis(rearmNextScan = true) { | |
| const { frameData, lockedTrackName, note, useHintOnce, visionModelId } = readAnalysisRequestInputs(); | |
| if (!frameData) { | |
| retryAnalysisLater('Desktop frame is not ready yet. Next capture will retry.', 1000); | |
| return; | |
| } | |
| window['__trackwhatDesktopCaptureTrace']?.('analysis.request.start', { | |
| active: state.active, | |
| analysisPending: state.analysisPending, | |
| rearmNextScan, | |
| scanMs: state.scanMs, | |
| effectiveScanMs: effectiveScanMs(), | |
| requestMode: 'direct-api', | |
| }); | |
| state.analysisRearmNextScan = rearmNextScan; | |
| state.analysisOneShot = !rearmNextScan; | |
| setCaptureStatus('Analyzing current desktop capture...'); | |
| setAnalysisPending(true); | |
| waitForAnalysisCompletion(); | |
| (async () => { | |
| try { | |
| const payload = await callAnalyze([frameData, lockedTrackName, note, useHintOnce, visionModelId]); | |
| const outputs = Array.isArray(payload?.data) ? payload.data : []; | |
| window['__trackwhatDesktopCaptureTrace']?.('analysis.request.success', { | |
| outputs: outputs.length, | |
| track: outputs[0] || '', | |
| }); | |
| applyAnalysisOutputs(outputs); | |
| markAnalysisComplete(); | |
| } catch (error) { | |
| setAnalysisPending(false); | |
| retryAnalysisLater(`Could not start analysis: ${error?.message || error}`, 1500); | |
| window['__trackwhatDesktopCaptureTrace']?.('analysis.request.error', { | |
| message: error?.message || String(error), | |
| }); | |
| console.warn('Desktop capture analysis request failed:', error); | |
| } | |
| })(); | |
| } | |
| return { getAnalyzeButton, readResultSignature, setAnalysisPending, retryAnalysisLater, recoverStaleAnalysis, markAnalysisComplete, waitForAnalysisCompletion, requestAnalysis }; | |
| } | |