The pending-devices table's four p-card cells overflow a 360-430 px
viewport, and the actions cell - Approve - ended up past the right edge
of an overflow-auto wrapper inside the page's overflow-x-hidden column:
the button existed but could not be seen or reached (landscape is wide
enough, which is why it "worked" there). The name cell now flexes and
truncates, and the fingerprint/age columns collapse into a sub-line
under the name on narrow screens instead of widening the row.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`<CardContent className="p-0">` never removed the padding it looked like it
removed: tailwind-merge resolves conflicts within a variant, so `p-0` cancels
`p-4` but leaves `sm:p-6` standing. Every call site that used it nests a
CardHeader inside, which brings its own `sm:p-6` — so at >=640px the title sat
24px further in than its neighbours'. Visible on the Plugins page as 'Installed
plugins' not lining up with 'Plugin runner'.
Give CardContent a `flush` prop that omits the padding outright, and use it at
the three call sites (Store, Pairing, Stats) rather than fixing the symptom
three times and leaving the footgun in place.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pairing:
- Refresh the paired-devices list after a native PIN pairing (the happy path never
invalidated it, so a newly paired device stayed hidden until remount).
- Moonlight PIN: a 204 means "PIN delivered to the waiting handshake", NOT paired, so
it now reads "PIN sent" instead of a false "Paired successfully".
- Hide the Moonlight pairing card on native-only hosts (HostInfo.gamestream) — it could
never receive a PIN there.
- Per-row pending on unpair/approve/deny; PIN input maxLength 16 (was 8).
Displays / Library:
- "Arrange displays" save refreshes the settings card (it rewrites the policy), without
clobbering unsaved Custom edits (re-seed only when the draft still matches the server).
- Live-display list wrapped in QueryState so errors don't read as "no displays".
- "Forever" keep-alive option in the custom editor; edit-game form round-trips the logo
artwork (was dropped on save); per-card delete pending.
Stats:
- Distinct colour for the native "queue" latency stage (it collided with "capture").
- "Not measured on this path" note on the GameStream health chart; configured-bitrate
target line on throughput; host-authoritative elapsed timer; LiveCard surfaces
non-404 errors.
Shell / auth / i18n:
- SSR-stable locale: first client render matches the base-locale SSR (no hydration
mismatch), then adopts the persisted/browser locale post-hydration.
- BFF proxy maps an upstream (mgmt-token) 401 to 502 so a logged-in user isn't bounced
into a post-login redirect loop.
- Logout checks the POST result before navigating; logs dedup by seq (StrictMode);
login "next" keeps query/hash; Dashboard shows the active-session count.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Marketing/store screenshots of the console, captured from the built Storybook
with headless Chromium (web/tools/screenshots.mjs) — every Pages/* + Shell/*
story rendered at 1440x900@2x. The page stories render from fixtures, so no live
mgmt API, login, or GPU is needed (the web analogue of apple.yml's screenshots
job). Gated to stable release tags in a standalone best-effort workflow; PNGs
upload as a 30-day artifact, not committed.
- Add Stats + Pairing stories (the two pages that lacked them) with stats/pairing
fixtures typed against the generated models.
- Extract a pure PairingView (index.tsx -> view.tsx), matching the
Dashboard/Clients/Stats split, so the page renders host-free from mock state
instead of racing its polling queries. Container wiring is behaviour-identical.
- Playwright driver + a chromium-capable tag-gated job.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>