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.