// 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 Reason 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 || "—"; } };