feat: Reduce server vitials panel height

This commit is contained in:
2026-09-07 08:14:47 +00:00
parent f9ef9c4929
commit 9d218cb19f
3 changed files with 30 additions and 16 deletions
+4 -4
View File
@@ -273,13 +273,13 @@ export default function ServerDetailPage() {
* (z-40) and under the nav drawer (z-50). The scroll container is
* AppShell's column, not the window, which is what sticky anchors to.
*/}
<div className="sticky top-14 z-20 border-b border-border bg-background/90 px-4 pt-4 backdrop-blur sm:px-6 lg:top-0 lg:px-8">
<div className="sticky top-14 z-20 border-b border-border bg-background/90 px-4 pt-3 backdrop-blur sm:pt-4 sm:px-6 lg:top-0 lg:px-8">
<Link href="/servers" className="text-sm text-text-secondary transition-colors hover:text-text-primary">
Servers
</Link>
<div className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-2">
<h1 className="text-2xl font-bold text-text-primary">{server.hostname}</h1>
<div className="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-2 sm:mt-2">
<h1 className="text-xl font-bold text-text-primary sm:text-2xl">{server.hostname}</h1>
<Badge variant={statusVariant(server.status)}>{server.status}</Badge>
<span className="font-mono text-sm text-text-secondary">{server.ip_address}</span>
<div className="ml-auto">
@@ -287,7 +287,7 @@ export default function ServerDetailPage() {
</div>
</div>
<div className="mt-2">
<div className="mt-1.5 sm:mt-2">
<TagChips serverId={server.server_id} tags={server.tags} editable />
</div>
+25 -11
View File
@@ -17,7 +17,7 @@ import { formatBytes, relativeAge } from "./format";
function Meter({ pct }: { pct: number }) {
const clamped = Math.max(0, Math.min(100, pct));
return (
<div className="mt-2 h-[3px] w-full overflow-hidden rounded-full bg-well">
<div className="mt-1.5 h-[3px] w-full overflow-hidden rounded-full bg-well sm:mt-2">
<div
className={clsx("h-full rounded-full transition-[width] duration-500", clamped >= 90 ? "bg-danger" : clamped >= 75 ? "bg-warning" : "bg-accent")}
style={{ width: `${clamped}%` }}
@@ -28,13 +28,16 @@ function Meter({ pct }: { pct: number }) {
function Vital({ label, value, pct, sub }: { label: string; value: string; pct?: number; sub?: string }) {
return (
<div className="min-w-0 bg-surface px-4 py-3">
<div className="flex items-baseline justify-between gap-3">
<span className="font-mono text-[0.62rem] uppercase tracking-[0.16em] text-text-secondary">{label}</span>
<span className="font-mono text-sm font-semibold tabular-nums text-text-primary">{value}</span>
// Label over value below sm: on a phone the two side by side wrap the
// value onto a second line, and four cells each a line taller is the
// whole viewport gone before the tabs.
<div className="min-w-0 bg-surface px-3 py-2.5 sm:px-4 sm:py-3">
<div className="flex flex-col gap-0.5 sm:flex-row sm:items-baseline sm:justify-between sm:gap-3">
<span className="truncate font-mono text-[0.58rem] uppercase tracking-[0.14em] text-text-secondary sm:text-[0.62rem] sm:tracking-[0.16em]">{label}</span>
<span className="truncate font-mono text-[0.8rem] font-semibold tabular-nums text-text-primary sm:text-sm">{value}</span>
</div>
{pct !== undefined ? <Meter pct={pct} /> : <div className="mt-2 h-[3px] w-full rounded-full bg-well" />}
{sub && <p className="mt-1.5 truncate text-xs text-text-tertiary">{sub}</p>}
{pct !== undefined ? <Meter pct={pct} /> : <div className="mt-1.5 h-[3px] w-full rounded-full bg-well sm:mt-2" />}
{sub && <p className="mt-1 truncate text-[0.68rem] text-text-tertiary sm:mt-1.5 sm:text-xs">{sub}</p>}
</div>
);
}
@@ -47,6 +50,17 @@ function primaryPartition(inv: Inventory) {
return parts.find((p) => p.mountpoint === "/") ?? parts.reduce((worst, p) => (p.used_bytes / (p.total_bytes || 1) > worst.used_bytes / (worst.total_bytes || 1) ? p : worst));
}
/** "10.8 / 16.0 GB" rather than "10.8 GB / 16.0 GB" when both halves carry the
* same unit — the repeated unit is what pushes the memory cell onto a second
* line on a phone, and it says nothing the right-hand half does not. */
function pair(used: number, total: number) {
const u = formatBytes(used);
const t = formatBytes(total);
const uu = u.split(" ")[1];
const tu = t.split(" ")[1];
return uu && uu === tu ? `${u.split(" ")[0]} / ${t}` : `${u} / ${t}`;
}
export function VitalsRail({ server, agentUpToDate }: { server: Server; agentUpToDate?: boolean }) {
const inv = server.inventory;
const disk = inv ? primaryPartition(inv) : undefined;
@@ -58,7 +72,7 @@ export function VitalsRail({ server, agentUpToDate }: { server: Server; agentUpT
return (
// One hairline grid rather than four cards: these are readings off one
// machine, and four bordered panels would read as four subjects.
<div className="mt-4 grid grid-cols-2 gap-px overflow-hidden rounded border border-border-soft bg-border-soft lg:grid-cols-4">
<div className="mt-3 grid grid-cols-2 gap-px sm:mt-4 overflow-hidden rounded border border-border-soft bg-border-soft lg:grid-cols-4">
{inv ? (
<>
<Vital
@@ -69,15 +83,15 @@ export function VitalsRail({ server, agentUpToDate }: { server: Server; agentUpT
/>
<Vital
label="Memory"
value={`${formatBytes(inv.memory.used_bytes)} / ${formatBytes(inv.memory.total_bytes)}`}
value={pair(inv.memory.used_bytes, inv.memory.total_bytes)}
pct={memPct}
sub={inv.swap_total_bytes > 0 ? `swap ${formatBytes(inv.swap_used_bytes)} / ${formatBytes(inv.swap_total_bytes)}` : "no swap"}
sub={inv.swap_total_bytes > 0 ? `swap ${pair(inv.swap_used_bytes, inv.swap_total_bytes)}` : "no swap"}
/>
<Vital
label={disk ? `Disk ${disk.mountpoint}` : "Disk"}
value={disk ? `${diskPct.toFixed(0)}%` : "—"}
pct={disk ? diskPct : undefined}
sub={disk ? `${formatBytes(disk.used_bytes)} / ${formatBytes(disk.total_bytes)}${disk.fstype ? ` · ${disk.fstype}` : ""}` : "no partitions reported"}
sub={disk ? `${pair(disk.used_bytes, disk.total_bytes)}${disk.fstype ? ` · ${disk.fstype}` : ""}` : "no partitions reported"}
/>
</>
) : (
File diff suppressed because one or more lines are too long