Files
local-mcp/static/js/status.js
2026-03-27 18:32:25 +08:00

137 lines
4.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* static/js/status.js
* Renders the server status and agent activity panels,
* and the config settings panel.
*/
import { state } from './state.js';
// ── Time helpers ──────────────────────────────────────────────────────────
function fmtTime(isoStr) {
if (!isoStr) return '';
const d = new Date(isoStr);
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
}
function fmtRelative(isoStr) {
if (!isoStr) return '';
const d = new Date(isoStr);
const diff = Math.floor((Date.now() - d.getTime()) / 1000);
if (diff < 5) return 'just now';
if (diff < 60) return `${diff}s ago`;
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
return `${Math.floor(diff / 3600)}h ago`;
}
// ── Server online indicator (header) ─────────────────────────────────────
function updateHeaderLeds(serverOnline, status) {
const serverLed = document.getElementById('led-server');
const agentLed = document.getElementById('led-agent');
if (!serverLed || !agentLed) return;
if (state.get('sseReconnecting')) {
serverLed.className = 'led led--amber led--pulse';
serverLed.querySelector('.led__label').textContent = 'Reconnecting…';
} else if (serverOnline) {
serverLed.className = 'led led--green led--pulse';
serverLed.querySelector('.led__label').textContent = 'Server Online';
} else if (!serverOnline) {
serverLed.className = 'led led--red';
serverLed.querySelector('.led__label').textContent = 'Server Offline';
}
if (status?.agent?.connected) {
agentLed.className = 'led led--cyan led--pulse';
agentLed.querySelector('.led__label').textContent = 'Agent Connected';
} else {
agentLed.className = 'led led--muted';
agentLed.querySelector('.led__label').textContent = 'Agent Idle';
}
}
// ── Status sidebar panel ──────────────────────────────────────────────────
function renderStatusPanel(status) {
const el = document.getElementById('status-panel-body');
if (!el || !status) return;
const agent = status.agent ?? {};
const queue = {
pending_count: status.queue?.pending_count ?? 0,
consumed_count: status.queue?.consumed_count ?? 0,
};
const serverType = status.server?.type || 'unknown';
const version = status.server?.version || '';
el.innerHTML = `
<div class="stat-row">
<span class="stat-label">Server Up</span>
<span class="stat-value">${fmtTime(status.server?.started_at)}</span>
</div>
<div class="stat-row">
<span class="stat-label">Version</span>
<span class="stat-value">v${escapeHtml(version)}</span>
</div>
<div class="stat-row">
<span class="stat-label">Server Type</span>
<span class="stat-value">${escapeHtml(serverType)}</span>
</div>
<div class="stat-row">
<span class="stat-label">Pending</span>
<span class="stat-value stat-value--cyan">${queue.pending_count}</span>
</div>
<div class="stat-row">
<span class="stat-label">Consumed</span>
<span class="stat-value stat-value--amber">${queue.consumed_count}</span>
</div>
<div class="stat-row">
<span class="stat-label">Agent</span>
<span class="stat-value">${agent.agent_id ? escapeHtml(agent.agent_id) : ''}</span>
</div>
<div class="stat-row">
<span class="stat-label">Last Seen</span>
<span class="stat-value" data-ts-rel="${agent.last_seen_at || ''}">${fmtRelative(agent.last_seen_at)}</span>
</div>
<div class="stat-row">
<span class="stat-label">Last Fetch</span>
<span class="stat-value" data-ts-rel="${agent.last_fetch_at || ''}">${fmtRelative(agent.last_fetch_at)}</span>
</div>
`;
}
/** Called by app.js on a timer to keep relative times fresh. */
export function refreshStatusTimestamps() {
document.querySelectorAll('[data-ts-rel]').forEach(el => {
const iso = el.dataset.tsRel;
if (iso) el.textContent = fmtRelative(iso);
});
}
export function initStatus() {
state.subscribe('serverOnline', (online) => {
updateHeaderLeds(online, state.get('status'));
});
state.subscribe('status', (status) => {
updateHeaderLeds(state.get('serverOnline'), status);
renderStatusPanel(status);
});
}
// ── Config panel ──────────────────────────────────────────────────────────
export function initConfig() {
// Intentionally empty: the web UI no longer exposes editable settings.
}
function escapeHtml(str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}