f9c56eaf5cfb89c279253008ab83e09ee5dc8de2
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b9b0df349d |
fix(web): the charts stop lying about time, and logging out logs you out
The stats charts drew every sample as an evenly-spaced slot, because recharts defaults to a category axis. A capture that idled for two minutes rendered that gap as a single step — so the one view you open specifically to find where the time went was the view least able to show it. All three charts use a numeric time axis now, so the spacing is the elapsed time. They also joined samples across a session boundary into one continuous line, implying a continuity that never existed: the stream stopped and somebody else started a new one. A capture is split at each `session_id` change now. And the live card plotted the whole capture-so-far every 2 s, re-serialising and re-plotting an unbounded series for a capture left running all evening; it plots a bounded tail and says so, with the full series still in the saved recording. Logging out only deleted the browser's copy of the cookie. The session is stateless, so a value captured beforehand — a shared machine, a shell history, a TLS-inspecting proxy — stayed valid for its full 7-day TTL and there was nothing the operator could do about it. Sessions carry an epoch now and logging out bumps it, which invalidates every cookie issued so far. Verified end to end: a captured cookie works, survives nothing across a logout, and a fresh login still works. The rest: - The update card could not show its own timeout warning. It was suppressed by a `job` field read from the last snapshot — which, when the host has gone away mid-job, is exactly the case the warning exists for. Nothing ever cleared the applying state either, so the card waited forever with no way out; there is a button now. "Check now" also surfaces the host's 429 instead of looking dead. - A running install survives a reload: the job id lived only in component state, so refreshing lost sight of an install that was still running while the Install buttons stayed armed against a host that answers 409. The host keeps the list — ask it. - An all-sources-failed catalog said "no plugins available". That is a successful request carrying nothing, not an empty store; it names the sources that failed. - The Installed tab rendered "vundefined" for a plugin with no recorded version (nullable in the contract, typed required here). - The Displays "In effect" badges were computed from the local draft, so they restated the operator's unsaved edits back to them as though the host had adopted them. They read the API's `effective` now. A failed background poll no longer replaces a form someone is editing, and leaving the page with unsaved edits prompts — the old `beforeunload` guard never fired for in-app navigation, which is how you actually leave. - Enter or Space on a preset's rename/update/delete icon applied the preset instead of running the action: keydown bubbled to the card. - Dates follow the console's locale, not the browser's. The dashboard's PIN-pending tile says "Waiting"/"None" instead of "●"/"—". - The Bun entry warns when TLS is half-configured, or when PUNKTFUNK_UI_SECURE is set without it — both of which silently break login. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
9e505aba41 |
fix(web): the console stops swallowing the host's answer when it says no
The host writes genuinely useful refusals — "entry is owned by provider `x`, update it through its reconcile" — and a dozen call sites threw them away. The pattern was always one of two: a mutation whose `error` nothing rendered, or an `await mutateAsync(...)` with no catch, which additionally produced an unhandled rejection. Either way the operator clicked, nothing visible happened, and the thing they asked for silently hadn't. Fixed at each site, with the host's own message shown where there is one: - Adding or editing a library entry kept the form open and said why, instead of closing it as if it had saved and taking the typing with it. Deleting one reports the refusal rather than leaving the card sitting there. - The GPU preference, capture start/stop, recording delete and download, and the dashboard's stop-session / request-keyframe / end-game all report failure. The failed capture STOP is the one that mattered most: it is "stop & save", so a swallowed error meant minutes of recording vanished with nothing on screen. - The recordings Download had a comment claiming the detail view surfaces its errors. It only does that for the selected row, and Download is on every row. Two related fixes in the same area: - `apiFetch` no longer navigates to /login synchronously from inside whichever call noticed a 401 — very often a background poll the user never started. Tearing the page down mid-render took unsaved editing state with it, which the Displays page explicitly models. It defers a beat and coalesces, so a burst of parallel 401s schedules one navigation. - The plugin liveness probe treated the auth gate's 302 → /login → 200 HTML as a healthy plugin, and rendered the console's own login page inside the plugin's iframe. It also gave up permanently on the first failed probe, so the runner restart at the end of every install threw away whatever was open in another plugin. It rejects the redirect and keeps probing on a slower beat while down. `apiErrorMessage` moves out of the display card into src/lib/errors.ts, since half the console needs it now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
75b3c94f60 |
fix(web): console sweep — pairing, displays, stats, logs, auth, i18n
ci / rust (push) Failing after 45s
ci / web (push) Successful in 52s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
ci / docs-site (push) Successful in 1m6s
decky / build-publish (push) Successful in 33s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 10s
ci / bench (push) Successful in 6m1s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8m33s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9m40s
docker / deploy-docs (push) Successful in 26s
windows-host / package (push) Successful in 15m34s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m24s
arch / build-publish (push) Successful in 20m32s
android / android (push) Successful in 20m50s
deb / build-publish (push) Successful in 20m33s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 21m41s
apple / swift (push) Has been cancelled
apple / screenshots (push) Has been cancelled
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> |
||
|
|
ae51276a03 |
feat(web): consolidate paired devices, self-contained sections, docs + lint
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> |
||
|
|
187738ed86 | improve web ui | ||
|
|
7d64bb5a43 |
feat(web): management console — TanStack Start + orval + shadcn + Paraglide
Browser UI for the host's management REST API (mgmt.rs / docs/api/openapi.json). Stack, exactly as specified: - TanStack Start (Vite, SPA mode) — file-based routes, SSR shell + client hydration. - React Query via orval codegen from the checked-in OpenAPI spec: a custom fetch mutator (src/api/fetcher.ts) centralizes the base URL, the bearer token (Settings → localStorage), JSON, and a throwing ApiError; the query client skips retries on 4xx. orval returns the response body directly (includeHttpResponseReturnType:false) so a query's `.data` is the typed payload; GET→useQuery, POST/DELETE→useMutation by method. - shadcn/ui on Tailwind v4 (CSS-first tokens, dark-first) — button/card/badge/input/label/ table/skeleton primitives hand-authored from the canonical source. - Paraglide i18n (en + de) with a reactive useLocale() hook and a language switcher. Pages: dashboard (live status — video/audio/session/stream, stop-session + request-IDR, 2s polling), host (identity/codecs/ports), clients (paired list + unpair), pairing (PIN submit, polls pin_pending), settings (API token + language). Dev server proxies /api → 127.0.0.1:47990 (same-origin, no CORS; PUNKTFUNK_MGMT_URL to override). Generated code (orval client, paraglide runtime, routeTree) is gitignored and reproduced by `pnpm codegen` (prepare/pre* scripts). Validated live against `serve`: API shapes match, dev proxy works, SSR shell renders the localized nav, build + tsc green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |