The structured log viewer opened its WebSocket once and never tried to
recover. When the remote node dropped the tunnel mid-tail or the central
restarted, log output went silent with no indicator. The 10k row buffer
kept prior content on screen, so the silence looked like the stack had
just stopped producing logs.
Adds exponential-backoff reconnect (1s, 2s, 4s, 8s, 16s, cap 30s) with
a "reconnecting..." banner replacing the green "following" pip while
the socket is down. Backoff resets to the first delay after every
successful re-open.
docker logs -f has no resumable offset, so on successful reconnect the
viewer drops a synthetic warning sentinel into the row stream:
--- reconnected; older lines may be missing ---
The sentinel renders at 70% opacity to distinguish it from container
output. Operators see at a glance that there was a gap.
The closedByCleanup flag prevents the reconnect loop from racing the
effect cleanup path: when the component unmounts or stackName changes,
the flag is set BEFORE ws.close() so the onclose handler short-circuits
instead of scheduling another reconnect.
Resolves M-1 from the stack-management audit.
The log viewer's per-line timestamp now renders HH:mm:ss again. The
HH:mm:ss.SSS variant was busier than the actual log content needed and
made the column harder to scan at a glance. The underlying ISO
timestamp from docker logs -t is still preserved on each row, so
download / copy still carries the original precision.
The rAF-based flush from the same area stays in place; that is what
makes lines feel real-time, not the timestamp width.
Two compounding issues made logs feel laggy and timestamps look duplicated:
1. State updates were batched in a 250ms setInterval, so a burst of
lines all rendered together every quarter second. Replace with a
requestAnimationFrame scheduler: lines flush on the next paint
(~16ms at 60Hz) while still collapsing a single burst into one
React commit. Cleanup uses cancelAnimationFrame.
2. The timestamp formatter rendered HH:mm:ss only, dropping the
sub-second precision that docker logs -t already emits. Two lines
logged within the same second appeared identically. Render
HH:mm:ss.SSS so successive lines remain visually distinct.
* feat(stack-view): per-container health strip and structured logs viewer
Replaces the flat container list with a per-container health strip showing
healthcheck state, uptime, port mapping with an open-app link, and live
cpu/memory/network sparklines fed by a 60-sample ring buffer on the stats
WebSocket.
Adds a structured logs viewer that parses docker timestamps (emitted by the
-t flag on the logs stream) and classifies each line by level. Rows render
as a DOM grid with filter pills (all / info / warn / err with count),
following indicator, and plain-text download. A segmented toggle switches
between the structured viewer and the original xterm view; the choice is
persisted in localStorage.
* fix(stack-view): disable no-control-regex for ANSI escape pattern
ANSI escape sequences start with ESC (0x1B), which is a control character.
The regex is intentional and cannot be rewritten without it.