ae51276a03
Web console - Pairing/Library/Stats refactored into self-contained subsections that each own their own queries + mutations; a shared slot-based layout (view.tsx) is filled by the live page (containers) and Storybook (pure cards + fixtures) so the layout can't drift. - All paired devices in one list on Pairing with a protocol column (punktfunk/1 + Moonlight), routing each unpair to the right endpoint; the redundant Clients page is removed. - Library: overview grid split from the add/edit form into separate files. - Login screen links out to the docs. Docs - "Console login password" section on every host page (apt/RPM/Bazzite/SteamOS/Windows) plus a new "Forgot your Password?" troubleshooting page, linked from the login screen. - Console served as HTTP/1.1 over TLS (drop the unusable HTTP/3 advertising) across the Bun entry, launchers, systemd units, and packaging. Tooling - Biome now respects .gitignore (stops linting generated code), config migrated to 2.5.1; all lint issues fixed cleanly. Also includes this branch's in-progress host, Apple client, packaging, and CI changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
44 lines
1.1 KiB
TypeScript
44 lines
1.1 KiB
TypeScript
import type { FC, ReactNode } from "react";
|
|
import { m } from "@/paraglide/messages";
|
|
|
|
/** ms → `m:ss`. */
|
|
export function fmtDuration(ms: number): string {
|
|
const s = Math.max(0, Math.floor(ms / 1000));
|
|
return `${Math.floor(s / 60)}:${(s % 60).toString().padStart(2, "0")}`;
|
|
}
|
|
|
|
export function fmtTimestamp(unixMs: number): string {
|
|
if (!unixMs) return "—";
|
|
return new Date(unixMs).toLocaleString();
|
|
}
|
|
|
|
export function kindLabel(kind: string): string {
|
|
if (kind === "gamestream") return m.stats_kind_gamestream();
|
|
if (kind === "native") return m.stats_kind_native();
|
|
return kind;
|
|
}
|
|
|
|
export const Stat: FC<{ label: string; value: string }> = ({
|
|
label,
|
|
value,
|
|
}) => (
|
|
<div className="flex flex-col">
|
|
<dt className="text-xs text-muted-foreground">{label}</dt>
|
|
<dd className="font-medium">{value}</dd>
|
|
</div>
|
|
);
|
|
|
|
export const ChartBlock: FC<{
|
|
title: string;
|
|
desc?: string;
|
|
children: ReactNode;
|
|
}> = ({ title, desc, children }) => (
|
|
<div className="space-y-2">
|
|
<div>
|
|
<h3 className="text-sm font-medium">{title}</h3>
|
|
{desc && <p className="text-xs text-muted-foreground">{desc}</p>}
|
|
</div>
|
|
{children}
|
|
</div>
|
|
);
|