Top wears the skin; the refresh rate reaches every machine; bug bash 4

The process table's columns fit the box they are in, the rate control saves, curves are
timed on the viewer's clock and keep to the stride, the power icon is drawn, and a young
history draws in from the right instead of being stretched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-21 10:12:45 -07:00
co-authored by Claude Fable 5.1
parent ef3dbdf543
commit 668e716601
13 changed files with 394 additions and 107 deletions
+82
View File
@@ -415,3 +415,85 @@ Found while building them, ✅ fixed:
and cron rechecked on both sizes.
- The Duplicate dialog and the stack page under it shared a React key, so the dialog
multiplied. An undeployed stack's dot was green ("0 of 0 running"); now neutral.
# Bug bash 4 — 2026-09-21: the Top tab, and the refresh rate it didn't follow
Started from Mitchell's first look at Top on his own machine (Firefox on Windows, a window
1392 CSS px wide — none of which the tab had been drawn for): "Command" printed over
"Threads", the rate in Settings ignored, the symbol beside *logout* a box of hex digits;
and make it follow the skins. Method as before, plus two things new: **Firefox** (Playwright's
build; the GTK libraries it wants are unpacked from their `.deb`s into
`~/.cache/ms-playwright/fflibs` and given to it as `LD_LIBRARY_PATH`, with
`PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS=1` — nothing is installed on `core`), and a layout
checker that measures instead of looking — every heading against its neighbour and its own
text, every row against its box, every border label, at eight window sizes from 900 to 1920.
Run first against the live v83 it reproduced the screenshot to the pixel ("the Command column
is 11px wide" at 1392); run after, nothing, in both browsers.
## Bugs — all ✅ fixed and verified by the script that found them
1. ✅ **Between about 1280 and 1500 px the Command column collapsed to a letter** and its
heading ran under "Threads:" (itself 4 px too narrow for its own text at every size). The
table's fixed columns were sized for one window. The process box now measures *itself*
(a container query — it is a third of a wide window and all of a narrow one, so the
window's width says nothing) and drops the curve, then Threads, then Command, as it
narrows; the box also gets half the width rather than 44 %, and no heading can spill
into the next. Stacked layout now starts at 1280, not 1100.
2. ✅ **The refresh rate reached a machine only through its Dashboard page.** The sampler on
each machine was told the rate by an open Dashboard and nothing else — so with the rate
at 250 ms, `grid`, `wyse` and the Mac were still sampling at 1 s (measured, live), and
Top, Processes, Disks and every widget of theirs "ignored" the setting until somebody
opened that machine's Dashboard. The rate is now the fleet's: saved with the UI prefs on
the hub, which sets its own sampler and passes it to every machine (`PUT
/api/prefs/sampler`, remembered in each `state.json`), and says it again at start-up for
a node that restarted or joined since. Verified: Settings 2 s → 250 ms moves the sampler
with no Dashboard visit.
3. ✅ **Top read the process table at most once a second whatever the rate** — a clamp of
my own, exactly what `REFRESH` promises not to do. It reads at the rate: 20 reads in 5 s
at 250 ms, 10 at 500 ms. (59 frames a second with 390 rows at 250 ms, no stalls, both
browsers — the rows are only redrawn when the table is read.)
4. ✅ **Top's `− rate +` changed the rate for half a minute.** It set the browser's copy
and never saved it, so the next sync with the hub put it back. It saves — which is also
what moves the samplers — survives a reload, and each end is disabled at the limit.
5. ✅ **A viewer whose clock is wrong froze the curves.** New points were timed against the
machine's timestamps: a browser clock 7 minutes slow waited 7 minutes for its next point
(Top *and* the Dashboard); 7 fast, and it added one per tick. Timed on the browser's own
clock now. Verified with the page's clock shifted both ways: 5 points in 10 s at a 2 s
stride, both pages, both browsers.
6. ✅ **Every other point was being skipped.** "Has a stride passed?" was asked with `>=`
of a timer that fires a few ms early half the time — so at a 2 s rate and a 2 s stride a
curve gained 3 points in 10 s, not 5, and its time axis was quietly wrong. The same
question on the server kept every bucket one sample long (14 s points at a 12 s stride).
Both now allow half a tick of slack and keep to the stride's own beat; the server times
from the last point kept, so a stalled or sleeping machine just keeps the next sample it
gets. Unit-tested with jittered samples: 240 samples → exactly 40 points, 12 000 ± 100 ms
apart, each the mean of its own six.
7. ✅ **The power symbol was a typed `⏻`** (U+23FB, Unicode 9) — not in Windows' fonts, so
Firefox there drew the missing-glyph box. Drawn as an SVG like the other icons. Every
other symbol the console types was checked against the same risk: all older.
8. ✅ **A handful of samples was stretched into a bar chart.** A machine ten seconds into
its history filled the whole CPU box with five fat columns. The box is now always the
whole history window — a young curve draws in from the right, as btop's does, and a full
one fills it edge to edge, at any window (checked at 10 min and at 1 min).
9. ✅ **Idle curves vanished**: a core at 10 % rounded to no dots at all in its mini graph.
Anything above zero lights the bottom row, which is also btop's rule.
## Skins
✅ Top now wears the skin. The ground is the skin's terminal background and every curve and
meter is built from its sixteen terminal colours (chosen to be read on exactly that ground),
the rest from the usual variables — so the dark skins look like btop and the light ones like
btop on paper, and a skin arriving from another device repaints the page in place. Measured
in all thirteen: text ≥ 4.5 : 1, everything else ≥ 3 : 1, dots ≥ 2.2 : 1. Two failed —
process names in Latte (2.75) and Solarized Light (2.62), where a terminal green is too
pale for small type — and use `--ok`, the green each skin was already measured readable
with.
## Checked and fine
Sort by pid, name, mem and cpu, and reverse; filter by name, command, user and pid, with the
count showing matched/total and an empty result saying so; a selection that holds while the
table is re-read under it; terminate, on a throwaway `sleep`, asks by name and pid, ends it,
and the footer stops offering a process that is gone. Sampler values outside 100 ms–60 s are
refused, and a rate slower than the collector's 10 s samples at 10 s. All 14 pages at his
window size and on a phone, in both browsers: no errors, no overflow.