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
+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; }