Disks: the SMART table stays inside its card

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-21 21:09:43 -07:00
co-authored by Claude Opus 5
parent dbfe163a75
commit a7b8319974
3 changed files with 40 additions and 4 deletions
+24
View File
@@ -520,3 +520,27 @@ table is re-read under it; terminate, on a throwaway `sleep`, asks by name and p
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.
# Bug bash 4, later — 2026-09-21: the SMART table outside its card
Mitchell, from the Disks page of `hamtv` with SMART open: the attribute table's rows ran out
through the right-hand side of the card they belong to, the rules and the Thresh column
drawn over the page behind.
12. ✅ **A table cannot be narrower than its widest unbreakable cell, and one of `hamtv`'s
was 130px wide.** `.num` sets `white-space: nowrap`, and a SMART raw value is not always a
number — the temperature attribute reads `34 (Min/Max 28/39)`. That one cell set the
table's minimum width above the card's, so it spilled. Three changes, in this order,
because the first two each looked worse than the bug in their own way:
- the raw column wraps — but `overflow-wrap: anywhere` broke `4295163906` across two
lines as "429516 / 3906", which reads as two numbers. It wraps at spaces only now;
- a disk card is given room for six columns of SMART (`minmax(420px, 1fr)` rather than
the shared 320px), and the cells' side padding goes 6px → 4px, which together is the
~40px it was short by;
- if some drive ever reports a raw value long enough to still not fit, the table scrolls
inside its card rather than out of it.
Verified on all four machines at six widths from 1721px down to a phone, in Chromium and
Firefox: nothing leaves its card, no number is broken across lines, and the fallback
scrollbar is never reached. The same measurement was run over every other page's cards
and boxes — Dashboard, Top, Docker, Alerts, Machines and the rest — and found nothing.
+4 -2
View File
@@ -58,17 +58,19 @@ function SmartPanel({ dev, rotational }: { dev: string; rotational: boolean }) {
</div>
)}
{s.attrs.length > 0 && (
<div className="attrs-wrap">
<table className="attrs">
<thead><tr><th>ID</th><th>Attribute</th><th>Raw</th><th>Value</th><th>Worst</th><th>Thresh</th></tr></thead>
<tbody>
{s.attrs.map((a) => (
<tr key={a.id} className={a.warn ? 'warn' : ''}>
<td className="num">{a.id}</td><td>{a.name.replace(/_/g, ' ')}{a.failed && <span className="badge bad">{a.failed}</span>}</td>
<td className="num">{a.raw}</td><td className="num">{a.value}</td><td className="num">{a.worst}</td><td className="num">{a.thresh}</td>
<td className="num raw">{a.raw}</td><td className="num">{a.value}</td><td className="num">{a.worst}</td><td className="num">{a.thresh}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{!s.self_test_supported && <div className="muted small">Self-tests: {s.self_test_note || 'not available on this drive'}.</div>}
{s.self_test_supported && <div className="svc-actions">
@@ -231,7 +233,7 @@ export default function Disks() {
</div>
{!hasSmart && <div className="notice">SMART health needs smartmontools on this host. Install it with the button, then reload.</div>}
{task && <TaskOutput id={task} onDone={(t) => { toast(`${t.title}: ${t.exit_code === 0 ? 'finished' : `exited ${t.exit_code}`}`, t.exit_code === 0 ? 'ok' : 'err'); load() }} />}
<div className="cards">
<div className="cards disk-cards">
{devs.map((d) => <DeviceCard key={d.name} d={d} live={liveFor(d)} forceOpen={allSmart ? true : null} />)}
</div>
+12 -2
View File
@@ -262,9 +262,19 @@ h2.section { margin: 0; font-size: 13px; font-weight: 600; color: var(--muted);
.good { color: var(--ok); } .warn-text { color: var(--s2); }
.smart { display: flex; flex-direction: column; gap: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
.attrs { width: 100%; border-collapse: collapse; font-size: 12px; }
.attrs th { text-align: left; color: var(--muted); font-weight: 500; padding: 4px 6px; border-bottom: 1px solid var(--border); }
.attrs td { padding: 3px 6px; border-bottom: 1px solid var(--border); } .attrs td.num, .attrs th:not(:nth-child(2)) { text-align: right; }
.attrs th { text-align: left; color: var(--muted); font-weight: 500; padding: 4px 4px; border-bottom: 1px solid var(--border); }
.attrs td { padding: 3px 4px; border-bottom: 1px solid var(--border); } .attrs td.num, .attrs th:not(:nth-child(2)) { text-align: right; }
.attrs tr.warn td { color: var(--s2); }
/* A SMART raw value is not always a number — a temperature reads "36 (Min/Max 28/39)" — and a
table cannot be narrower than its widest unbreakable cell, so one of those pushed the whole
table out through the side of its card. The raw column wraps, but only where there is a
space: a number broken across two lines reads as two numbers. If some drive ever reports
one long enough to still not fit, the table scrolls inside its card rather than out of it. */
.attrs td.raw { white-space: normal; }
.attrs-wrap { overflow-x: auto; scrollbar-width: thin; }
/* Six columns of SMART want about 350px between them; a disk card is given room for that, so
the fallback above stays a fallback. */
.disk-cards { grid-template-columns: repeat(auto-fill, minmax(min(420px, 100%), 1fr)); }
.datasets { padding: 0; overflow-x: auto; }
.ds-row { display: grid; grid-template-columns: minmax(160px, 1fr) 80px 80px 80px 50px 50px minmax(120px, 1fr); gap: 8px; align-items: center; padding: 6px 12px; border-bottom: 1px solid var(--border); font-size: 13px; min-width: 700px; } /* the columns and gaps add up to 692: at 640 the row ended before its own cells did */
.ds-row.head { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .5px; }