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
+62 -47
View File
@@ -536,82 +536,99 @@ pre.diff .add { color: var(--ok); background: color-mix(in srgb, var(--ok) 12%,
.dtable td.row-end { text-align: right; white-space: nowrap; width: 1%; } .dtable .badge { margin-left: 0; }
/* ---- Top: a machine drawn the way btop draws it ----
Its own palette, not the skin's: black ground, dim blue hairlines, dotted curves that climb
green → red. Fixed-width everywhere, because that is the look. */
.top { position: absolute; inset: 0; background: #000; color: #cccccc; font-family: var(--mono); font-size: 13px; line-height: 1.35;
padding: 6px; display: grid; gap: 4px; grid-template-columns: 1fr 1fr 1.6fr; grid-template-rows: minmax(200px, 1fr) auto minmax(170px, 1fr); overflow-y: auto; }
The layout and the dots are btop's; the colours are the skin's. The ground is the skin's
terminal background and everything on it comes from the skin's variables and its sixteen
terminal colours (Top.tsx hands the few it needs over as --t-*), so switching skin reskins
this page like any other. Fixed-width everywhere, because that is the look. */
.top { position: absolute; inset: 0; background: var(--term-bg); color: var(--fg); font-family: var(--mono); font-size: 13px; line-height: 1.35;
padding: 6px; display: grid; gap: 4px; grid-template-columns: 1fr 1fr 2.2fr; grid-template-rows: minmax(200px, 1fr) auto minmax(170px, 1fr); overflow-y: auto; }
/* every box says how little it can be: a window too short to hold them all scrolls, rather than
cutting the bottom off whichever box the grid ran out of room for */
.t-cpu-graph { min-height: 120px; } .t-net-graphs { min-height: 130px; } .top-proc { min-height: 300px; }
.top-mem, .top-disks { min-height: 165px; } /* a grid row set to auto will otherwise squeeze them to nothing */
.top-cpu { grid-column: 1 / -1; } .top-mem { grid-column: 1; grid-row: 2; } .top-disks { grid-column: 2; grid-row: 2; }
.top-net { grid-column: 1 / 3; grid-row: 3; } .top-proc { grid-column: 3; grid-row: 2 / 4; }
.tbox { position: relative; border: 1px solid #303a4a; border-radius: 3px; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.tbox { position: relative; border: 1px solid var(--border); border-radius: 3px; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.tbox-in { flex: 1; min-height: 0; min-width: 0; padding: 9px 8px 6px; display: flex; flex-direction: column; overflow: hidden; }
.tbox-name, .tbox-right { position: absolute; top: -9px; background: #000; padding: 0 4px; font-size: 12px; white-space: nowrap; z-index: 1; }
.tbox-name { left: 8px; color: #dddddd; } .tbox-name b { color: #ee4b4b; font-weight: 400; }
.tbox-right { right: 8px; color: #7b8496; display: flex; align-items: center; gap: 8px; }
.tbox-right button { background: none; border: 0; color: #7b8496; font-family: var(--mono); font-size: 12px; padding: 0 3px; cursor: pointer; }
.tbox-right button:hover, .tbox-right button.on { color: #ee4b4b; }
.t-clock { color: #dddddd; } .t-rate { display: inline-flex; align-items: center; gap: 2px; }
.t-dim { color: #6b7280; } .top .spacer { flex: 1; }
.tbox-name, .tbox-right { position: absolute; top: -9px; background: var(--term-bg); padding: 0 4px; font-size: 12px; white-space: nowrap; z-index: 1; }
.tbox-name { left: 8px; color: var(--fg); } .tbox-name b { color: var(--accent); font-weight: 400; }
.tbox-right { right: 8px; color: var(--muted); display: flex; align-items: center; gap: 8px; }
.tbox-right button { background: none; border: 0; color: var(--muted); font-family: var(--mono); font-size: 12px; padding: 0 3px; cursor: pointer; }
.tbox-right button:hover:not(:disabled), .tbox-right button.on { color: var(--accent); } .tbox-right button:disabled { opacity: .35; cursor: default; }
.t-clock { color: var(--fg); } .t-rate { display: inline-flex; align-items: center; gap: 2px; }
.t-dim { color: var(--muted); } .top .spacer { flex: 1; }
/* cpu */
.t-cpu { display: flex; gap: 8px; min-width: 0; flex: 1; min-height: 0; }
.t-cpu-graph { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.t-up { color: #6b7280; font-size: 12px; padding-top: 2px; }
.t-cores { width: 310px; flex-shrink: 0; border: 1px solid #303a4a; border-radius: 3px; padding: 4px 6px; display: flex; flex-direction: column; gap: 1px; overflow-y: auto; scrollbar-width: thin; }
.t-cores-head { display: flex; gap: 8px; color: #7b8496; font-size: 12px; white-space: nowrap; }
.t-cores-head b { color: #dddddd; font-weight: 400; overflow: hidden; text-overflow: ellipsis; }
.t-up { color: var(--muted); font-size: 12px; padding-top: 2px; }
.t-cores { width: 310px; flex-shrink: 0; border: 1px solid var(--border); border-radius: 3px; padding: 4px 6px; display: flex; flex-direction: column; gap: 1px; overflow-y: auto; scrollbar-width: thin; }
.t-cores-head { display: flex; gap: 8px; color: var(--muted); font-size: 12px; white-space: nowrap; }
.t-cores-head b { color: var(--fg); font-weight: 400; overflow: hidden; text-overflow: ellipsis; }
.t-core { display: flex; align-items: center; gap: 6px; }
.t-core-n { width: 30px; color: #cccccc; } .t-core-p { width: 34px; text-align: right; color: #dddddd; }
.t-core-g { flex: 1; color: #00d35c; letter-spacing: 0; overflow: hidden; white-space: nowrap; }
.t-core .tm { flex: 1; } .t-core-t { flex: 0 0 62px; display: flex; } .t-core-c { width: 42px; text-align: right; color: #4c8cff; }
.t-load { display: flex; margin-top: 2px; color: #cccccc; }
.t-core-n { width: 30px; } .t-core-p { width: 34px; text-align: right; }
.t-core-g { flex: 1; color: var(--ok); letter-spacing: 0; overflow: hidden; white-space: nowrap; }
.t-core .tm { flex: 1; } .t-core-t { flex: 0 0 62px; display: flex; } .t-core-c { width: 42px; text-align: right; color: var(--t-blue); }
.t-load { display: flex; margin-top: 2px; }
/* the dot graph and the dotted meter */
.tg { width: 100%; height: 100%; flex: 1; min-height: 0; overflow: hidden; }
.tm { position: relative; display: inline-block; height: 9px; flex: 1; min-width: 24px; vertical-align: middle; }
.tm-t, .tm-f { position: absolute; inset: 0; -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 5px); mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 5px); }
.tm-t { background: #23293a; }
.tm-t { background: var(--border); }
/* mem and disks */
.t-row { display: flex; align-items: center; gap: 6px; } .t-row b { color: #dddddd; font-weight: 400; }
.t-sub { color: #7b8496; font-size: 12px; }
.t-mem { margin-bottom: 4px; } .t-mem .t-sub { margin-top: -2px; } .t-pct { width: 34px; color: #dddddd; }
.t-row { display: flex; align-items: center; gap: 6px; } .t-row b { font-weight: 400; }
.t-sub { color: var(--muted); font-size: 12px; }
.t-mem { margin-bottom: 4px; } .t-mem .t-sub { margin-top: -2px; } .t-pct { width: 34px; color: var(--fg); }
.t-mem > .tm { display: block; width: 100%; }
.t-amt { width: 76px; text-align: right; color: #cccccc; }
.t-disks { overflow-y: auto; display: flex; flex-direction: column; gap: 6px; }
.t-disk .t-row + .t-row { margin-top: -1px; }
.t-amt { width: 76px; text-align: right; color: var(--fg); white-space: nowrap; }
.t-disks { overflow-y: auto; display: flex; flex-direction: column; gap: 6px; scrollbar-width: thin; }
.t-disk .t-row + .t-row { margin-top: -1px; } .t-disk b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* net */
.t-net { display: flex; gap: 10px; min-width: 0; flex: 1; min-height: 0; }
.t-net-graphs { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 140px; }
.t-net-g { position: relative; flex: 1; min-height: 0; display: flex; } .t-scale { position: absolute; left: 2px; top: 0; font-size: 11px; color: #6b7280; z-index: 1; }
.t-net-g { position: relative; flex: 1; min-height: 0; display: flex; } .t-scale { position: absolute; left: 2px; top: 0; font-size: 11px; color: var(--muted); z-index: 1; background: var(--term-bg); padding-right: 3px; }
.t-scale-b { top: auto; bottom: 0; }
.t-net-nums { width: 300px; flex-shrink: 0; border: 1px solid #303a4a; border-radius: 3px; padding: 4px 8px; }
.t-dl { color: #e050d0; } .t-ul { color: #50a0ff; } .t-gap { height: 6px; } .t-up-label { text-align: right; }
.t-if { color: #dddddd; }
.t-net-nums { width: 300px; flex-shrink: 0; border: 1px solid var(--border); border-radius: 3px; padding: 4px 8px; white-space: nowrap; overflow: hidden; }
.t-dl { color: var(--t-magenta); } .t-ul { color: var(--t-blue); } .t-gap { height: 6px; } .t-up-label { text-align: right; }
.t-if { color: var(--fg); }
/* proc */
/* proc — the columns that fit are the columns shown: the box measures itself (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 the threads, then the command, rather than crushing Command to one letter */
.top-proc { container: tproc / inline-size; }
.t-proc { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.t-filter { background: #0a0d14; border: 1px solid #303a4a; border-radius: 3px; color: #dddddd; font-family: var(--mono); font-size: 12px; padding: 3px 6px; margin-bottom: 4px; }
.t-thead, .t-prow { display: grid; grid-template-columns: 58px 116px minmax(0, 1fr) 54px 58px 50px 56px 42px; gap: 7px; align-items: center; white-space: nowrap; }
.t-thead { padding-right: 8px; } /* the rows scroll under a scrollbar; keep the headings over their columns */
.t-thead { color: #7b8496; font-size: 12px; border-bottom: 1px solid #23293a; padding-bottom: 2px; }
.t-filter { background: var(--bg); border: 1px solid var(--border); border-radius: 3px; color: var(--fg); font-family: var(--mono); font-size: 12px; padding: 3px 6px; margin-bottom: 4px; }
.t-thead, .t-prow { display: grid; grid-template-columns: 58px 116px minmax(0, 1fr) 62px 58px 50px 56px 42px; gap: 7px; align-items: center; white-space: nowrap; }
.t-thead > span, .t-prow > span { min-width: 0; overflow: hidden; } /* a heading never runs into its neighbour */
.t-thead { padding-right: 8px; color: var(--muted); font-size: 12px; border-bottom: 1px solid var(--border); padding-bottom: 2px; }
.t-rows { flex: 1; min-height: 0; overflow-y: auto; scrollbar-width: thin; }
.t-prow { cursor: pointer; } .t-prow:hover { background: #12161f; } .t-prow.on { background: #1b2230; }
.t-prow .c-name { color: #00d35c; overflow: hidden; text-overflow: ellipsis; }
.t-prow .c-cmd { color: #9aa4b2; overflow: hidden; text-overflow: ellipsis; }
.t-prow .c-user { color: #cccccc; overflow: hidden; text-overflow: ellipsis; }
.t-prow { cursor: pointer; } .t-prow:hover { background: var(--bg2); } .t-prow.on { background: var(--bg3); }
.t-prow .c-name { color: var(--ok); text-overflow: ellipsis; } /* --ok, not the terminal's green: it is the one each skin was measured readable with */
.t-prow .c-cmd { color: var(--muted); text-overflow: ellipsis; }
.t-prow .c-user { text-overflow: ellipsis; }
.c-pid, .c-thr, .c-mem, .c-cpu { text-align: right; } .t-thead .c-pid, .t-thead .c-name, .t-thead .c-cmd, .t-thead .c-user { text-align: left; }
.c-g { color: #6b7280; text-align: right; overflow: hidden; }
.t-foot { display: flex; align-items: center; gap: 8px; border-top: 1px solid #23293a; padding-top: 3px; margin-top: 2px; font-size: 12px; }
.t-foot button { background: none; border: 1px solid #303a4a; border-radius: 3px; color: #cccccc; font-family: var(--mono); font-size: 12px; padding: 1px 6px; cursor: pointer; }
.t-foot button:hover { border-color: #ee4b4b; color: #ee4b4b; } .t-count { color: #7b8496; }
.c-g { color: var(--muted); text-align: right; }
@container tproc (max-width: 640px) {
.t-thead, .t-prow { grid-template-columns: 58px 116px minmax(0, 1fr) 62px 58px 50px 42px; }
.t-thead .c-g, .t-prow .c-g { display: none; }
}
@container tproc (max-width: 560px) {
.t-thead, .t-prow { grid-template-columns: 58px 110px minmax(0, 1fr) 58px 50px 42px; }
.t-thead .c-thr, .t-prow .c-thr { display: none; }
}
@container tproc (max-width: 470px) {
.t-thead, .t-prow { grid-template-columns: 52px minmax(0, 1fr) 52px 48px 40px; gap: 6px; }
.t-thead .c-cmd, .t-prow .c-cmd { display: none; }
}
.t-foot { display: flex; align-items: center; gap: 8px; border-top: 1px solid var(--border); padding-top: 3px; margin-top: 2px; font-size: 12px; }
.t-foot button { background: none; border: 1px solid var(--border); border-radius: 3px; color: var(--fg); font-family: var(--mono); font-size: 12px; padding: 1px 6px; cursor: pointer; }
.t-foot button:hover { border-color: var(--bad); color: var(--bad); } .t-count { color: var(--muted); }
.t-sorts { display: inline-flex; gap: 2px; }
@media (max-width: 1100px) {
@media (max-width: 1280px) {
.top { grid-template-columns: 1fr 1fr; grid-template-rows: minmax(230px, auto) minmax(220px, auto) minmax(200px, auto) minmax(420px, 1fr); }
.top-mem, .top-disks { min-height: 220px; }
.top-cpu { grid-column: 1 / -1; grid-row: 1; } .top-mem { grid-column: 1; grid-row: 2; } .top-disks { grid-column: 2; grid-row: 2; }
@@ -623,6 +640,4 @@ pre.diff .add { color: var(--ok); background: color-mix(in srgb, var(--ok) 12%,
.t-cpu { flex-direction: column; } .t-cpu-graph { height: 130px; flex: 0 0 130px; } .t-cores { width: auto; max-height: 40vh; }
.t-net { flex-direction: column; } .t-net-nums { width: auto; } .t-net-graphs { flex: 0 0 130px; }
.top-proc { height: 70vh; min-height: 420px; }
.t-thead, .t-prow { grid-template-columns: 52px 1fr 46px 46px 40px; }
.t-thead .c-cmd, .t-prow .c-cmd, .t-thead .c-thr, .t-prow .c-thr, .t-thead .c-g, .t-prow .c-g { display: none; }
}