@@ -56,6 +56,16 @@ const DASHBOARD_HTML = /* html */ `<!DOCTYPE html>
5656 .status-failed { background: rgba(239,68,68,0.15); color: var(--red); }
5757 .status-stopped { background: rgba(234,179,8,0.15); color: var(--yellow); }
5858 .status-idle { background: rgba(92,96,116,0.15); color: var(--text3); }
59+ .status-waiting_for_index { background: rgba(249,115,22,0.15); color: var(--orange); }
60+
61+ /* Light theme */
62+ body.light {
63+ --bg: #f5f5f7; --bg2: #ffffff; --bg3: #e8e8ed; --border: #d1d1d6;
64+ --text: #1d1d1f; --text2: #6e6e73; --text3: #aeaeb2;
65+ --bar-bg: #e8e8ed;
66+ }
67+ body.light .btn { background: #f0f0f5; }
68+ body.light .btn:hover:not(:disabled) { background: #e0e0e5; }
5969
6070 /* Progress bars */
6171 .progress-outer { background: var(--bar-bg); border-radius: 6px; height: 24px; overflow: hidden; position: relative; }
@@ -145,6 +155,7 @@ const DASHBOARD_HTML = /* html */ `<!DOCTYPE html>
145155 <span id="svc-host" class="muted"></span>
146156 <span id="svc-uptime"></span>
147157 <span>v<span id="svc-version">-</span></span>
158+ <button class="btn" id="btn-theme" style="padding:4px 8px;font-size:11px">Theme</button>
148159 <div class="heartbeat" id="heartbeat"></div>
149160 </div>
150161</div>
@@ -251,6 +262,12 @@ const DASHBOARD_HTML = /* html */ `<!DOCTYPE html>
251262 </div>
252263</div>
253264
265+ <!-- Index Status -->
266+ <div class="card full" style="margin-top:12px">
267+ <h3>Index Status <span id="idx-summary" style="font-weight:400;color:var(--text2)"></span></h3>
268+ <div id="idx-details" style="font-size:12px"></div>
269+ </div>
270+
254271<!-- Collections Table -->
255272<div class="card full" style="margin-top:12px">
256273 <h3>Collections <span id="coll-summary" style="font-weight:400;color:var(--text2)"></span></h3>
@@ -278,7 +295,15 @@ const DASHBOARD_HTML = /* html */ `<!DOCTYPE html>
278295 function $(id) { return document.getElementById(id); }
279296 function fmt(n) { return n == null ? '-' : Number(n).toLocaleString('en-US'); }
280297 function mb(b) { return b ? (b / 1024 / 1024).toFixed(0) + ' MB' : '-'; }
281- function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.textContent; }
298+
299+ // Theme toggle
300+ var theme = localStorage.getItem('viz-theme') || 'dark';
301+ if (theme === 'light') document.body.classList.add('light');
302+ $('btn-theme').addEventListener('click', function() {
303+ document.body.classList.toggle('light');
304+ theme = document.body.classList.contains('light') ? 'light' : 'dark';
305+ localStorage.setItem('viz-theme', theme);
306+ });
282307
283308 // Tab switching
284309 $('tab-bar').addEventListener('click', function(e) {
@@ -299,6 +324,7 @@ const DASHBOARD_HTML = /* html */ `<!DOCTYPE html>
299324
300325 function statusClass(s) {
301326 if (s === 'running' || s === 'stopping') return 'status-running';
327+ if (s === 'waiting_for_index') return 'status-waiting_for_index';
302328 if (s === 'paused') return 'status-paused';
303329 if (s === 'completed') return 'status-completed';
304330 if (s === 'failed') return 'status-failed';
@@ -575,6 +601,39 @@ const DASHBOARD_HTML = /* html */ `<!DOCTYPE html>
575601 ['Redis Error', redis.lastError || 'none', redis.lastError ? 'var(--red)' : 'var(--text3)'],
576602 ]);
577603
604+ // Index status
605+ var idx = d.indexStatus || { ready: 0, building: 0, failed: 0, details: [] };
606+ $('idx-summary').textContent = '(' + idx.ready + ' ready, ' + idx.building + ' building' + (idx.failed > 0 ? ', ' + idx.failed + ' failed' : '') + ')';
607+ var idxContainer = $('idx-details');
608+ while (idxContainer.firstChild) idxContainer.removeChild(idxContainer.firstChild);
609+ if (idx.building === 0 && idx.failed === 0) {
610+ var allReady = document.createElement('span');
611+ allReady.style.color = 'var(--green)';
612+ allReady.textContent = 'All indexes ready';
613+ idxContainer.appendChild(allReady);
614+ } else {
615+ for (var ii = 0; ii < idx.details.length; ii++) {
616+ var det = idx.details[ii];
617+ var detRow = document.createElement('div');
618+ detRow.style.cssText = 'display:flex;justify-content:space-between;padding:2px 0';
619+ var detName = document.createElement('span');
620+ detName.style.cssText = 'max-width:60%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px';
621+ detName.textContent = det.collection.length > 50 ? det.collection.slice(0, 20) + '...' + det.collection.slice(-8) : det.collection;
622+ detRow.appendChild(detName);
623+ var detStatus = document.createElement('span');
624+ detStatus.style.fontWeight = '600';
625+ if (det.status === 'building') {
626+ detStatus.style.color = 'var(--orange)';
627+ detStatus.textContent = 'building' + (det.elapsedSec ? ' (' + Math.round(det.elapsedSec / 60) + 'm)' : '');
628+ } else {
629+ detStatus.style.color = 'var(--red)';
630+ detStatus.textContent = 'failed';
631+ }
632+ detRow.appendChild(detStatus);
633+ idxContainer.appendChild(detRow);
634+ }
635+ }
636+
578637 // Collections
579638 var orch = d.orchestrator || {};
580639 $('coll-summary').textContent = '(' + (orch.totalCollections || 0) + ' total: '
0 commit comments