diff --git a/packages/proxy/web/src/lib/components/IdleTimer.svelte b/packages/proxy/web/src/lib/components/IdleTimer.svelte index 27c8f8c..25e7abd 100644 --- a/packages/proxy/web/src/lib/components/IdleTimer.svelte +++ b/packages/proxy/web/src/lib/components/IdleTimer.svelte @@ -1,4 +1,6 @@

Idle Timer

- {formatTime(remainingSeconds)} + {formatTime(localRemaining)} until idle check

Last activity: {formatDate(lastActivity)}

@@ -38,6 +63,9 @@ font-weight: 700; font-variant-numeric: tabular-nums; } + .time.urgent { + color: var(--orange); + } .label { font-size: 0.75rem; color: var(--text-muted); diff --git a/packages/proxy/web/src/lib/components/ServiceList.svelte b/packages/proxy/web/src/lib/components/ServiceList.svelte index 0991708..2e1706d 100644 --- a/packages/proxy/web/src/lib/components/ServiceList.svelte +++ b/packages/proxy/web/src/lib/components/ServiceList.svelte @@ -6,6 +6,14 @@ } let { services }: Props = $props(); + + function timeAgo(iso: string | null): string { + if (!iso) return 'never'; + const diff = Math.floor((Date.now() - new Date(iso).getTime()) / 1000); + if (diff < 5) return 'just now'; + if (diff < 60) return `${diff}s ago`; + return `${Math.floor(diff / 60)}m ago`; + }
@@ -19,7 +27,13 @@
{service.name} - {service.host} + {service.target} + + {service.healthy ? 'Healthy' : 'Unhealthy'} + {#if service.lastCheck} + · checked {timeAgo(service.lastCheck)} + {/if} +
{/each} @@ -50,7 +64,7 @@ width: 8px; height: 8px; border-radius: 50%; - background: var(--text-dim); + background: var(--red); flex-shrink: 0; } .dot.healthy { @@ -64,8 +78,14 @@ font-weight: 500; font-size: 0.875rem; } - .service-host { + .service-target { font-size: 0.75rem; color: var(--text-muted); + font-family: monospace; + } + .service-meta { + font-size: 0.7rem; + color: var(--text-dim); + margin-top: 0.15rem; } diff --git a/packages/proxy/web/src/lib/stores/machine.ts b/packages/proxy/web/src/lib/stores/machine.ts index 7959dcf..bb0bd66 100644 --- a/packages/proxy/web/src/lib/stores/machine.ts +++ b/packages/proxy/web/src/lib/stores/machine.ts @@ -11,6 +11,7 @@ export const machineState = derived(status, ($s) => $s?.state ?? MachineState.OF let ws: WebSocket | null = null; let reconnectTimer: ReturnType | null = null; +let pollTimer: ReturnType | null = null; function getWsUrl(): string { const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; @@ -20,13 +21,14 @@ function getWsUrl(): string { export function connectWs(): void { if (ws) return; + startPolling(); + try { ws = new WebSocket(getWsUrl()); ws.onopen = () => { connected.set(true); error.set(null); - // Fetch full status on connect refreshStatus(); }; @@ -78,7 +80,22 @@ export async function refreshStatus(): Promise { } } +function startPolling(): void { + if (pollTimer) return; + pollTimer = setInterval(() => { + refreshStatus(); + }, 5000); +} + +function stopPolling(): void { + if (pollTimer) { + clearInterval(pollTimer); + pollTimer = null; + } +} + export function disconnectWs(): void { + stopPolling(); if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null;