Zhen Ye commited on
Commit
ec62a08
·
1 Parent(s): af0f84f

Rename UI terms: Reason->Detect, Engage->Track

Browse files
frontend/index.html CHANGED
@@ -5,7 +5,7 @@
5
  <meta charset="UTF-8" />
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
  <link rel="stylesheet" href="style.css">
8
- <title>HEL Perception & Engagement Reasoner (Weapon-Grade Demo)</title>
9
  </head>
10
 
11
  <body>
@@ -14,9 +14,8 @@
14
  <div class="brand">
15
  <div class="logo" aria-hidden="true"></div>
16
  <div>
17
- <h1>HEL Perception & Engagement Reasoner</h1>
18
- <div class="sub">Video → detection → expert features aimpoint + Intensity@Target HEL feasibility →
19
- closed-loop tracking & dwell control</div>
20
  </div>
21
  </div>
22
  <div class="status-row">
@@ -25,12 +24,12 @@
25
  <span id="sys-status">STANDBY · No video loaded</span>
26
  </div>
27
  <div class="pill">
28
- <span class="kbd">Reason</span>
29
- <span>Frame-1 inference</span>
30
  </div>
31
  <div class="pill">
32
- <span class="kbd">Engage</span>
33
- <span>Closed-loop track + dwell</span>
34
  </div>
35
  </div>
36
  </header>
@@ -40,7 +39,7 @@
40
  <div class="card">
41
  <h2>Video Input</h2>
42
  <div class="hint">Upload one video. Tab 1 uses only the first frame. Tab 2 reuses the same video for tracking
43
- and engagement.</div>
44
 
45
  <div class="row mt-md">
46
  <label for="videoFile">Video file</label>
@@ -92,7 +91,7 @@
92
  </select>
93
  </div>
94
 
95
- <label class="checkbox-row" for="enableDepthToggle">
96
  <input type="checkbox" id="enableDepthToggle">
97
  <span>Enable Legacy Depth Map (Slow)</span>
98
  </label>
@@ -117,8 +116,8 @@
117
 
118
  <main>
119
  <div class="tabs">
120
- <button class="tabbtn active" data-tab="frame">Tab 1 · Frame-1 Reason</button>
121
- <button class="tabbtn" data-tab="engage">Tab 2 · Video Engage</button>
122
  </div>
123
 
124
  <!-- ===== Tab 1 ===== -->
@@ -126,17 +125,17 @@
126
  <div class="frame-grid">
127
  <div class="panel panel-monitor">
128
  <h3>
129
- <span>First Frame · Detection + Aimpoints</span>
130
  <span class="rightnote" id="frameNote">Awaiting video</span>
131
  </h3>
132
  <div class="viewbox" id="frameViewBox">
133
  <canvas id="frameCanvas" width="1280" height="720"></canvas>
134
  <canvas id="frameOverlay" class="overlay" width="1280" height="720"></canvas>
135
- <div class="watermark">EO/IR · Track-ID · Aimpoint · Required Dwell</div>
136
  <div class="empty" id="frameEmpty">
137
  <div class="big">Upload a video to begin</div>
138
- <div class="small">This demo performs first-frame perception and engagement reasoning. Then it replays
139
- the same video with closed-loop tracking and dynamic dwell updates.</div>
140
  <div style="display:flex; gap:10px; margin-top:6px; flex-wrap:wrap; justify-content:center;">
141
  <span class="badge"><span class="dot"></span> If you are online, COCO-SSD loads automatically</span>
142
  </div>
@@ -144,15 +143,15 @@
144
  </div>
145
 
146
  <div class="btnrow" style="margin-top:10px">
147
- <button id="btnReason" class="btn">Reason</button>
148
  <button id="btnCancelReason" class="btn danger" style="display: none;">Cancel</button>
149
- <button id="btnRecompute" class="btn secondary">Recompute HEL</button>
150
  <button id="btnClear" class="btn secondary">Clear</button>
151
  </div>
152
 
153
  <div class="strip mt-md">
154
  <span class="chip" id="chipFrameDepth"
155
- title="Toggle depth view of first frame (if available)">VIEW:DEFAULT</span>
156
  </div>
157
  </div>
158
 
@@ -174,18 +173,18 @@
174
  <!-- Threat Chat Panel -->
175
  <div class="panel panel-chat" id="chatPanel">
176
  <h3>
177
- <span>Threat Analyst Chat</span>
178
  <button class="collapse-btn" id="chatToggle" style="font-size: 0.75rem;">▲ Close Chat</button>
179
  </h3>
180
  <div class="chat-container">
181
  <div class="chat-messages" id="chatMessages">
182
  <div class="chat-message chat-system">
183
  <span class="chat-icon">SYS</span>
184
- <span class="chat-content">Run detection first, then ask questions about detected threats.</span>
185
  </div>
186
  </div>
187
  <div class="chat-input-row">
188
- <input type="text" id="chatInput" placeholder="Ask about threats..." autocomplete="off">
189
  <button id="chatSend" class="btn">Send</button>
190
  </div>
191
  </div>
@@ -200,7 +199,7 @@
200
  <div class="engage-grid">
201
  <div class="panel">
202
  <h3>
203
- <span>Video Engage · Tracking + Dynamic Dwell</span>
204
  <div style="display: flex; gap: 8px; align-items: center;">
205
  <button class="collapse-btn" id="btnToggleSidebar">◀ Hide Sidebar</button>
206
  <span class="rightnote" id="engageNote">Awaiting video</span>
@@ -210,16 +209,16 @@
210
  <div class="viewbox" style="min-height: 420px;">
211
  <video id="videoEngage" playsinline muted></video>
212
  <canvas id="engageOverlay" class="overlay"></canvas>
213
- <div class="watermark">LOCK · DIST · DWELL · AIMPOINT · FIRE/ASSESS</div>
214
  <div class="empty" id="engageEmpty">
215
  <div class="big">No video loaded</div>
216
- <div class="small">Upload a video. Run <b>Reason</b> first to initialize aimpoints and baseline dwell.
217
- Then click <b>Engage</b>.</div>
218
  </div>
219
  </div>
220
 
221
  <div class="btnrow mt-md">
222
- <button id="btnEngage" class="btn">Engage</button>
223
  <button id="btnPause" class="btn secondary">Pause</button>
224
  <button id="btnReset" class="btn secondary">Reset</button>
225
  </div>
@@ -227,14 +226,14 @@
227
  <div class="strip mt-md">
228
  <span class="chip" id="chipPolicy">POLICY:AUTO</span>
229
  <span class="chip" id="chipTracks">TRACKS:0</span>
230
- <span class="chip" id="chipBeam">BEAM:OFF</span>
231
  <span class="chip" id="chipHz">DET:6Hz</span>
232
  <span class="chip" id="chipFeed" title="Toggle raw vs HF-processed feed (if available)">FEED:RAW</span>
233
- <span class="chip" id="chipDepth" title="Toggle depth view (if available)">VIEW:DEFAULT</span>
234
  </div>
235
 
236
  <div class="mt-md">
237
- <div class="row"><label>Active dwell progress (selected)</label><small class="mini"
238
  id="dwellText">—</small>
239
  </div>
240
  <div class="bar">
@@ -242,8 +241,7 @@
242
  </div>
243
  </div>
244
 
245
- <div class="hint mt-md">Manual targeting: choose Manual in Engagement Policy, then
246
- click a target in the video. The “beam” will track its aimpoint and accumulate dwell.</div>
247
  </div>
248
 
249
  <div class="engage-right">
@@ -261,8 +259,8 @@
261
  </div>
262
 
263
  <footer>
264
- <div>Demo mode · Unclassified visuals · Integrate your APIs where marked</div>
265
- <div class="mono" id="telemetry">HEL=60kW · VIS=16km · Cn²=5/10 · AO=7/10 · DET=6Hz</div>
266
  </footer>
267
 
268
  <!-- Hidden video used only for first-frame capture -->
 
5
  <meta charset="UTF-8" />
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
  <link rel="stylesheet" href="style.css">
8
+ <title>Mission Console</title>
9
  </head>
10
 
11
  <body>
 
14
  <div class="brand">
15
  <div class="logo" aria-hidden="true"></div>
16
  <div>
17
+ <h1>Mission Console</h1>
18
+ <div class="sub">Video → detection → analysistrackingmission assessment</div>
 
19
  </div>
20
  </div>
21
  <div class="status-row">
 
24
  <span id="sys-status">STANDBY · No video loaded</span>
25
  </div>
26
  <div class="pill">
27
+ <span class="kbd">Detect</span>
28
+ <span>First-frame analysis</span>
29
  </div>
30
  <div class="pill">
31
+ <span class="kbd">Track</span>
32
+ <span>Continuous monitoring</span>
33
  </div>
34
  </div>
35
  </header>
 
39
  <div class="card">
40
  <h2>Video Input</h2>
41
  <div class="hint">Upload one video. Tab 1 uses only the first frame. Tab 2 reuses the same video for tracking
42
+ and monitoring.</div>
43
 
44
  <div class="row mt-md">
45
  <label for="videoFile">Video file</label>
 
91
  </select>
92
  </div>
93
 
94
+ <label class="checkbox-row" for="enableDepthToggle" style="display:none">
95
  <input type="checkbox" id="enableDepthToggle">
96
  <span>Enable Legacy Depth Map (Slow)</span>
97
  </label>
 
116
 
117
  <main>
118
  <div class="tabs">
119
+ <button class="tabbtn active" data-tab="frame">Tab 1 · Detect & Analyze</button>
120
+ <button class="tabbtn" data-tab="engage">Tab 2 · Track & Monitor</button>
121
  </div>
122
 
123
  <!-- ===== Tab 1 ===== -->
 
125
  <div class="frame-grid">
126
  <div class="panel panel-monitor">
127
  <h3>
128
+ <span>First Frame · Detection + Analysis</span>
129
  <span class="rightnote" id="frameNote">Awaiting video</span>
130
  </h3>
131
  <div class="viewbox" id="frameViewBox">
132
  <canvas id="frameCanvas" width="1280" height="720"></canvas>
133
  <canvas id="frameOverlay" class="overlay" width="1280" height="720"></canvas>
134
+ <div class="watermark">EO/IR · Track-ID · Classification</div>
135
  <div class="empty" id="frameEmpty">
136
  <div class="big">Upload a video to begin</div>
137
+ <div class="small">This demo performs first-frame detection and analysis. Then it replays the video
138
+ with continuous object tracking.</div>
139
  <div style="display:flex; gap:10px; margin-top:6px; flex-wrap:wrap; justify-content:center;">
140
  <span class="badge"><span class="dot"></span> If you are online, COCO-SSD loads automatically</span>
141
  </div>
 
143
  </div>
144
 
145
  <div class="btnrow" style="margin-top:10px">
146
+ <button id="btnReason" class="btn">Detect</button>
147
  <button id="btnCancelReason" class="btn danger" style="display: none;">Cancel</button>
148
+ <button id="btnRecompute" class="btn secondary">Reanalyze</button>
149
  <button id="btnClear" class="btn secondary">Clear</button>
150
  </div>
151
 
152
  <div class="strip mt-md">
153
  <span class="chip" id="chipFrameDepth"
154
+ title="Toggle depth view of first frame (if available)" style="display:none">VIEW:DEFAULT</span>
155
  </div>
156
  </div>
157
 
 
173
  <!-- Threat Chat Panel -->
174
  <div class="panel panel-chat" id="chatPanel">
175
  <h3>
176
+ <span>Mission Analyst</span>
177
  <button class="collapse-btn" id="chatToggle" style="font-size: 0.75rem;">▲ Close Chat</button>
178
  </h3>
179
  <div class="chat-container">
180
  <div class="chat-messages" id="chatMessages">
181
  <div class="chat-message chat-system">
182
  <span class="chat-icon">SYS</span>
183
+ <span class="chat-content">Run detection first, then ask questions about detected objects.</span>
184
  </div>
185
  </div>
186
  <div class="chat-input-row">
187
+ <input type="text" id="chatInput" placeholder="Ask about detections..." autocomplete="off">
188
  <button id="chatSend" class="btn">Send</button>
189
  </div>
190
  </div>
 
199
  <div class="engage-grid">
200
  <div class="panel">
201
  <h3>
202
+ <span>Video Tracking · Continuous Monitoring</span>
203
  <div style="display: flex; gap: 8px; align-items: center;">
204
  <button class="collapse-btn" id="btnToggleSidebar">◀ Hide Sidebar</button>
205
  <span class="rightnote" id="engageNote">Awaiting video</span>
 
209
  <div class="viewbox" style="min-height: 420px;">
210
  <video id="videoEngage" playsinline muted></video>
211
  <canvas id="engageOverlay" class="overlay"></canvas>
212
+ <div class="watermark">TRACK · ID · DIST · STATUS</div>
213
  <div class="empty" id="engageEmpty">
214
  <div class="big">No video loaded</div>
215
+ <div class="small">Upload a video. Run <b>Detect</b> first to analyze objects.
216
+ Then click <b>Track</b>.</div>
217
  </div>
218
  </div>
219
 
220
  <div class="btnrow mt-md">
221
+ <button id="btnEngage" class="btn">Track</button>
222
  <button id="btnPause" class="btn secondary">Pause</button>
223
  <button id="btnReset" class="btn secondary">Reset</button>
224
  </div>
 
226
  <div class="strip mt-md">
227
  <span class="chip" id="chipPolicy">POLICY:AUTO</span>
228
  <span class="chip" id="chipTracks">TRACKS:0</span>
229
+ <span class="chip" id="chipBeam">MODE:AUTO</span>
230
  <span class="chip" id="chipHz">DET:6Hz</span>
231
  <span class="chip" id="chipFeed" title="Toggle raw vs HF-processed feed (if available)">FEED:RAW</span>
232
+ <span class="chip" id="chipDepth" title="Toggle depth view (if available)" style="display:none">VIEW:DEFAULT</span>
233
  </div>
234
 
235
  <div class="mt-md">
236
+ <div class="row"><label>Active track progress (selected)</label><small class="mini"
237
  id="dwellText">—</small>
238
  </div>
239
  <div class="bar">
 
241
  </div>
242
  </div>
243
 
244
+ <div class="hint mt-md">Manual selection: choose "Manual" in Policy, then click a target in the video.</div>
 
245
  </div>
246
 
247
  <div class="engage-right">
 
259
  </div>
260
 
261
  <footer>
262
+ <div>Mission Console · Unclassified visuals</div>
263
+ <div class="mono" id="telemetry">VIS=16km · DET=6Hz</div>
264
  </footer>
265
 
266
  <!-- Hidden video used only for first-frame capture -->
frontend/js/core/hel.js CHANGED
@@ -240,6 +240,6 @@ APP.core.hel.syncKnobDisplays = function () {
240
  // Update telemetry footer
241
  const telemetry = $("#telemetry");
242
  if (telemetry && helPower && atmVis && atmCn2 && aoQ && detHz) {
243
- telemetry.textContent = `HEL=${helPower.value}kW · VIS=${atmVis.value}km · Cn²=${atmCn2.value}/10 · AO=${aoQ.value}/10 · DET=${detHz.value}Hz`;
244
  }
245
  };
 
240
  // Update telemetry footer
241
  const telemetry = $("#telemetry");
242
  if (telemetry && helPower && atmVis && atmCn2 && aoQ && detHz) {
243
+ telemetry.textContent = `VIS=${atmVis.value}km · DET=${detHz.value}Hz`;
244
  }
245
  };
frontend/js/main.js CHANGED
@@ -100,13 +100,13 @@ document.addEventListener("DOMContentLoaded", () => {
100
  try {
101
  await captureFirstFrame();
102
  // Show placeholder message - actual frame will come from backend
103
- if (frameNote) frameNote.textContent = "Video loaded (run Reason for processed frame)";
104
- if (engageNote) engageNote.textContent = "Ready for Engage";
105
  } catch (err) {
106
  log(`First frame capture failed: ${err.message}`, "e");
107
  }
108
 
109
- setStatus("warn", "READY · Video loaded (run Reason)");
110
  log(`Video loaded: ${file.name}`, "g");
111
 
112
  // Load video-specific demo tracks (e.g., helicopter demo)
@@ -142,7 +142,7 @@ document.addEventListener("DOMContentLoaded", () => {
142
  await recomputeHEL();
143
  renderFrameOverlay();
144
 
145
- log("HEL parameters recomputed.", "g");
146
  });
147
  }
148
 
@@ -175,7 +175,7 @@ document.addEventListener("DOMContentLoaded", () => {
175
  btnPause.addEventListener("click", () => {
176
  if (videoEngage) videoEngage.pause();
177
  state.tracker.running = false;
178
- log("Engage paused.", "t");
179
  });
180
  }
181
 
@@ -190,7 +190,7 @@ document.addEventListener("DOMContentLoaded", () => {
190
  state.tracker.running = false;
191
  state.tracker.nextId = 1;
192
  renderFrameTrackList();
193
- log("Engage reset.", "t");
194
  });
195
  }
196
 
@@ -273,7 +273,7 @@ document.addEventListener("DOMContentLoaded", () => {
273
  chipFrameDepth.addEventListener("click", () => {
274
  if (!state.videoLoaded) return;
275
  if (!state.hf.depthFirstFrameUrl) {
276
- log("First frame depth not ready yet. Run Reason and wait for depth processing.", "w");
277
  return;
278
  }
279
  toggleFirstFrameDepthView();
@@ -309,7 +309,7 @@ document.addEventListener("DOMContentLoaded", () => {
309
  }
310
 
311
  if (state.isReasoning) {
312
- log("Reason already in progress. Please wait.", "w");
313
  return;
314
  }
315
 
@@ -329,7 +329,7 @@ document.addEventListener("DOMContentLoaded", () => {
329
  renderFrameTrackList();
330
  renderFrameOverlay();
331
 
332
- setStatus("warn", "REASONING · Running perception pipeline");
333
 
334
  // Agent cursor flair
335
  if (state.ui.cursorMode === "on" && moveCursorToRect) {
@@ -342,7 +342,7 @@ document.addEventListener("DOMContentLoaded", () => {
342
  const mode = detectorSelect ? detectorSelect.value : "hf_yolov8";
343
  const queries = missionText ? missionText.value.trim() : "";
344
  const enableGPT = $("#enableGPTToggle")?.checked || false;
345
- const enableDepth = $("#enableDepthToggle")?.checked || false;
346
 
347
  const form = new FormData();
348
  form.append("video", state.videoFile);
@@ -445,8 +445,8 @@ document.addEventListener("DOMContentLoaded", () => {
445
  : `${state.hf.baseUrl}${data.stream_url}`;
446
  log("Activating live stream...", "t");
447
  setStreamingMode(streamUrl);
448
- log("Live view available in 'Engage' tab.", "g");
449
- setStatus("warn", "Live processing... View in Engage tab");
450
 
451
  // Trigger resize/render for Tab 2
452
  resizeOverlays();
@@ -459,8 +459,8 @@ document.addEventListener("DOMContentLoaded", () => {
459
  stopStreamingMode();
460
 
461
  state.hasReasoned = true;
462
- setStatus("good", "READY · Reason complete (you can Engage)");
463
- log("Reason complete. Ready to Engage.", "g");
464
 
465
  // Seed tracks for Tab 2
466
  seedTracksFromTab1();
@@ -479,8 +479,8 @@ document.addEventListener("DOMContentLoaded", () => {
479
 
480
 
481
  } catch (err) {
482
- setStatus("bad", "ERROR · Reason failed");
483
- log(`Reason failed: ${err.message}`, "e");
484
  console.error(err);
485
  } finally {
486
  state.isReasoning = false;
@@ -634,14 +634,14 @@ document.addEventListener("DOMContentLoaded", () => {
634
  }
635
  if (btnCancelReason) btnCancelReason.style.display = "none";
636
 
637
- setStatus("warn", "CANCELLED · Reasoning stopped");
638
  setHfStatus("cancelled (stopped by user)");
639
- log("Reasoning cancelled by user.", "w");
640
  }
641
 
642
  function runEngage() {
643
  if (!state.hasReasoned) {
644
- log("Please run Reason first.", "w");
645
  return;
646
  }
647
 
@@ -673,7 +673,7 @@ document.addEventListener("DOMContentLoaded", () => {
673
  seedTracksFromTab1();
674
  }
675
 
676
- log("Engage sequence started.", "g");
677
  }
678
 
679
  function loop() {
 
100
  try {
101
  await captureFirstFrame();
102
  // Show placeholder message - actual frame will come from backend
103
+ if (frameNote) frameNote.textContent = "Video loaded (run Detect for processed frame)";
104
+ if (engageNote) engageNote.textContent = "Ready for Track";
105
  } catch (err) {
106
  log(`First frame capture failed: ${err.message}`, "e");
107
  }
108
 
109
+ setStatus("warn", "READY · Video loaded (run Detect)");
110
  log(`Video loaded: ${file.name}`, "g");
111
 
112
  // Load video-specific demo tracks (e.g., helicopter demo)
 
142
  await recomputeHEL();
143
  renderFrameOverlay();
144
 
145
+ log("Parameters recomputed.", "g");
146
  });
147
  }
148
 
 
175
  btnPause.addEventListener("click", () => {
176
  if (videoEngage) videoEngage.pause();
177
  state.tracker.running = false;
178
+ log("Tracking paused.", "t");
179
  });
180
  }
181
 
 
190
  state.tracker.running = false;
191
  state.tracker.nextId = 1;
192
  renderFrameTrackList();
193
+ log("Tracking reset.", "t");
194
  });
195
  }
196
 
 
273
  chipFrameDepth.addEventListener("click", () => {
274
  if (!state.videoLoaded) return;
275
  if (!state.hf.depthFirstFrameUrl) {
276
+ log("First frame depth not ready yet. Run Detect and wait for depth processing.", "w");
277
  return;
278
  }
279
  toggleFirstFrameDepthView();
 
309
  }
310
 
311
  if (state.isReasoning) {
312
+ log("Detection already in progress. Please wait.", "w");
313
  return;
314
  }
315
 
 
329
  renderFrameTrackList();
330
  renderFrameOverlay();
331
 
332
+ setStatus("warn", "DETECTING · Running perception pipeline");
333
 
334
  // Agent cursor flair
335
  if (state.ui.cursorMode === "on" && moveCursorToRect) {
 
342
  const mode = detectorSelect ? detectorSelect.value : "hf_yolov8";
343
  const queries = missionText ? missionText.value.trim() : "";
344
  const enableGPT = $("#enableGPTToggle")?.checked || false;
345
+ const enableDepth = false; // depth mode disabled
346
 
347
  const form = new FormData();
348
  form.append("video", state.videoFile);
 
445
  : `${state.hf.baseUrl}${data.stream_url}`;
446
  log("Activating live stream...", "t");
447
  setStreamingMode(streamUrl);
448
+ log("Live view available in 'Track' tab.", "g");
449
+ setStatus("warn", "Live processing... View in Track tab");
450
 
451
  // Trigger resize/render for Tab 2
452
  resizeOverlays();
 
459
  stopStreamingMode();
460
 
461
  state.hasReasoned = true;
462
+ setStatus("good", "READY · Detection complete (you can Track)");
463
+ log("Detection complete. Ready to Track.", "g");
464
 
465
  // Seed tracks for Tab 2
466
  seedTracksFromTab1();
 
479
 
480
 
481
  } catch (err) {
482
+ setStatus("bad", "ERROR · Detection failed");
483
+ log(`Detection failed: ${err.message}`, "e");
484
  console.error(err);
485
  } finally {
486
  state.isReasoning = false;
 
634
  }
635
  if (btnCancelReason) btnCancelReason.style.display = "none";
636
 
637
+ setStatus("warn", "CANCELLED · Detection stopped");
638
  setHfStatus("cancelled (stopped by user)");
639
+ log("Detection cancelled by user.", "w");
640
  }
641
 
642
  function runEngage() {
643
  if (!state.hasReasoned) {
644
+ log("Please run Detect first.", "w");
645
  return;
646
  }
647
 
 
673
  seedTracksFromTab1();
674
  }
675
 
676
+ log("Tracking started.", "g");
677
  }
678
 
679
  function loop() {
frontend/js/ui/chat.js CHANGED
@@ -56,7 +56,7 @@
56
 
57
  // Check if we have detections
58
  if (!state.detections || state.detections.length === 0) {
59
- appendMessage("system", "No threats detected yet. Run Reason first to analyze the scene.");
60
  return;
61
  }
62
 
@@ -66,7 +66,7 @@
66
  chatInput.disabled = true;
67
 
68
  // Show loading indicator
69
- const loadingId = appendMessage("assistant", "Analyzing threats...", true);
70
 
71
  try {
72
  const response = await APP.api.client.chatAboutThreats(question, state.detections);
 
56
 
57
  // Check if we have detections
58
  if (!state.detections || state.detections.length === 0) {
59
+ appendMessage("system", "No detections yet. Run Detect first to analyze the scene.");
60
  return;
61
  }
62
 
 
66
  chatInput.disabled = true;
67
 
68
  // Show loading indicator
69
+ const loadingId = appendMessage("assistant", "Analyzing scene...", true);
70
 
71
  try {
72
  const response = await APP.api.client.chatAboutThreats(question, state.detections);
frontend/js/ui/intel.js CHANGED
@@ -28,7 +28,7 @@ APP.ui.intel.resetIntelUI = function () {
28
  const intelSummaryBox = $("#intelSummaryBox");
29
 
30
  if (!intelSummaryBox) return;
31
- intelSummaryBox.innerHTML = 'Upload a video, then click <b>Reason</b> to generate an unbiased scene summary.';
32
  APP.ui.intel.setIntelStatus("warn", "Idle");
33
  APP.ui.intel.setIntelThumb(0, "");
34
  APP.ui.intel.setIntelThumb(1, "");
 
28
  const intelSummaryBox = $("#intelSummaryBox");
29
 
30
  if (!intelSummaryBox) return;
31
+ intelSummaryBox.innerHTML = 'Upload a video, then click <b>Detect</b> to generate an unbiased scene summary.';
32
  APP.ui.intel.setIntelStatus("warn", "Idle");
33
  APP.ui.intel.setIntelThumb(0, "");
34
  APP.ui.intel.setIntelThumb(1, "");