Spaces:
Paused
Paused
| // Intel Summary Module - Mission intel generation and display | |
| APP.ui.intel = {}; | |
| APP.ui.intel.setIntelStatus = function (kind, text) { | |
| const { $ } = APP.core.utils; | |
| const intelStamp = $("#intelStamp"); | |
| const intelDot = $("#intelDot"); | |
| if (!intelStamp || !intelDot) return; | |
| intelStamp.innerHTML = text; | |
| intelDot.className = "dot" + (kind === "warn" ? " warn" : (kind === "bad" ? " bad" : "")); | |
| intelDot.style.width = "7px"; | |
| intelDot.style.height = "7px"; | |
| intelDot.style.boxShadow = "none"; | |
| }; | |
| APP.ui.intel.setIntelThumb = function (i, dataUrl) { | |
| const { $ } = APP.core.utils; | |
| const thumbs = [$("#intelThumb0"), $("#intelThumb1"), $("#intelThumb2")]; | |
| const img = thumbs[i]; | |
| if (!img) return; | |
| img.src = dataUrl || ""; | |
| }; | |
| APP.ui.intel.resetIntelUI = function () { | |
| const { $ } = APP.core.utils; | |
| const intelSummaryBox = $("#intelSummaryBox"); | |
| if (!intelSummaryBox) return; | |
| intelSummaryBox.innerHTML = 'Upload a video, then click <b>Reason</b> to generate an unbiased scene summary.'; | |
| APP.ui.intel.setIntelStatus("warn", "Idle"); | |
| APP.ui.intel.setIntelThumb(0, ""); | |
| APP.ui.intel.setIntelThumb(1, ""); | |
| APP.ui.intel.setIntelThumb(2, ""); | |
| }; | |
| // External hook for intel summary (can be replaced by user) | |
| APP.ui.intel.externalIntel = async function (frames) { | |
| console.log("externalIntel called with", frames.length, "frames"); | |
| return "Video processed. No external intel provider connected."; | |
| }; | |
| APP.ui.intel.computeIntelSummary = async function () { | |
| const { state } = APP.core; | |
| const { $ } = APP.core.utils; | |
| const { log } = APP.ui.logging; | |
| const intelSummaryBox = $("#intelSummaryBox"); | |
| const videoHidden = $("#videoHidden"); | |
| const videoEngage = $("#videoEngage"); | |
| if (!intelSummaryBox) return; | |
| if (!state.videoLoaded) { | |
| APP.ui.intel.resetIntelUI(); | |
| return; | |
| } | |
| if (state.intelBusy) return; | |
| state.intelBusy = true; | |
| APP.ui.intel.setIntelStatus("warn", "Generating…"); | |
| intelSummaryBox.textContent = "Sampling frames and running analysis…"; | |
| try { | |
| const videoEl = videoHidden || videoEngage; | |
| const dur = videoEl ? (videoEl.duration || 0) : 0; | |
| const times = [0, dur ? dur * 0.33 : 1, dur ? dur * 0.66 : 2]; | |
| const frames = []; | |
| for (let i = 0; i < times.length; i++) { | |
| await APP.core.video.seekTo(videoEl, times[i]); | |
| const canvas = document.createElement("canvas"); | |
| canvas.width = 640; | |
| canvas.height = 360; | |
| const ctx = canvas.getContext("2d"); | |
| ctx.drawImage(videoEl, 0, 0, canvas.width, canvas.height); | |
| const dataUrl = canvas.toDataURL("image/jpeg", 0.6); | |
| frames.push(dataUrl); | |
| try { | |
| APP.ui.intel.setIntelThumb(i, dataUrl); | |
| } catch (_) { } | |
| } | |
| const summary = await APP.ui.intel.externalIntel(frames); | |
| intelSummaryBox.textContent = summary; | |
| APP.ui.intel.setIntelStatus("good", `Updated · ${new Date().toLocaleTimeString()}`); | |
| } catch (err) { | |
| APP.ui.intel.setIntelStatus("bad", "Summary unavailable"); | |
| intelSummaryBox.textContent = `Unable to generate summary: ${err.message}`; | |
| console.error(err); | |
| } finally { | |
| state.intelBusy = false; | |
| } | |
| }; | |
| // Render mission context (if applicable) | |
| APP.ui.intel.renderMissionContext = function () { | |
| const { state } = APP.core; | |
| const { $ } = APP.core.utils; | |
| const missionClassesEl = $("#missionClasses"); | |
| const missionIdEl = $("#missionId"); | |
| if (missionClassesEl) { | |
| if (state.hf.queries && state.hf.queries.length > 0) { | |
| missionClassesEl.textContent = state.hf.queries.join(", "); | |
| } else { | |
| missionClassesEl.textContent = "All objects (no filter)"; | |
| } | |
| } | |
| if (missionIdEl) { | |
| missionIdEl.textContent = state.hf.missionId || "—"; | |
| } | |
| }; | |