Confirmed shape brief: ops-console direction, balanced density, blue committed accent, readability emphasized. Design system: surface scale to 5 steps tinted toward hue 266; text contrast lifted (secondary 62->72%, tertiary 44->52, added text-disabled); borders gained faint variant; status tokens renamed semantically (signal-good/warn/bad/idle); typography upgraded (Inter 400/500/600, JetBrains Mono for numerics, new 2xl/3xl/tc scale steps). New components: tc-display timecode classes with blue glow; tally-word with good/warn/bad/rec variants; signal-strip flutter bar with modifiers; chip dense monospaced pill; manifest table styles. Topbar status strip: new js/topbar-strip.js auto-injects a 28px strip on every page with wall clock, page name, live API latency, and system-pulse dot. Per-page: recorders gain live signal-strip above fps line; capture timecode bumped 38->64px with blue glow; ingest drop zone slimmed 200->88px side-by-side layout so manifest gets the real estate.
56 lines
2.4 KiB
JavaScript
56 lines
2.4 KiB
JavaScript
// Operator status strip mounted at the top of every .main pane.
|
|
(function () {
|
|
function mount() {
|
|
const main = document.querySelector('.main');
|
|
if (!main || main.querySelector('.topbar-strip')) return;
|
|
const strip = document.createElement('div');
|
|
strip.className = 'topbar-strip';
|
|
strip.innerHTML =
|
|
'<span class="ts-dot" id="tsDot" title="System pulse"></span>' +
|
|
'<span class="ts-label">Z-AMPP</span>' +
|
|
'<span class="ts-now" id="tsNow">00:00:00</span>' +
|
|
'<span class="ts-sep"></span>' +
|
|
'<span class="ts-label">Page</span>' +
|
|
'<span class="ts-value" id="tsPage">' + pageName() + '</span>' +
|
|
'<span class="ts-sep"></span>' +
|
|
'<span class="ts-label">API</span>' +
|
|
'<span class="ts-value" id="tsApi">--</span>';
|
|
main.insertBefore(strip, main.firstChild);
|
|
tick(); setInterval(tick, 1000);
|
|
ping(); setInterval(ping, 10000);
|
|
}
|
|
function pageName() {
|
|
const p = location.pathname.replace(/\.html$/, '').replace(/^\//, '');
|
|
if (!p || p === 'index') return 'Library';
|
|
return p.charAt(0).toUpperCase() + p.slice(1);
|
|
}
|
|
function tick() {
|
|
const n = new Date();
|
|
const hh = String(n.getHours()).padStart(2, '0');
|
|
const mm = String(n.getMinutes()).padStart(2, '0');
|
|
const ss = String(n.getSeconds()).padStart(2, '0');
|
|
const el = document.getElementById('tsNow');
|
|
if (el) el.textContent = hh + ':' + mm + ':' + ss;
|
|
}
|
|
async function ping() {
|
|
const dot = document.getElementById('tsDot');
|
|
const apiEl = document.getElementById('tsApi');
|
|
const t0 = performance.now();
|
|
try {
|
|
const r = await fetch('/api/v1/projects', { credentials: 'include', signal: AbortSignal.timeout(4000) });
|
|
const ms = Math.round(performance.now() - t0);
|
|
if (r.ok) {
|
|
if (dot) { dot.style.background = 'var(--signal-good)'; dot.style.boxShadow = '0 0 8px var(--signal-good)'; }
|
|
if (apiEl) apiEl.textContent = ms + ' ms';
|
|
} else {
|
|
if (dot) { dot.style.background = 'var(--signal-warn)'; dot.style.boxShadow = '0 0 8px var(--signal-warn)'; }
|
|
if (apiEl) apiEl.textContent = 'HTTP ' + r.status;
|
|
}
|
|
} catch (e) {
|
|
if (dot) { dot.style.background = 'var(--signal-bad)'; dot.style.boxShadow = '0 0 8px var(--signal-bad)'; }
|
|
if (apiEl) apiEl.textContent = 'offline';
|
|
}
|
|
}
|
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mount);
|
|
else mount();
|
|
})();
|