feat(web): consolidate paired devices, self-contained sections, docs + lint
apple / swift (push) Successful in 1m6s
ci / rust (push) Successful in 5m51s
android / android (push) Successful in 6m21s
ci / web (push) Successful in 49s
ci / docs-site (push) Successful in 58s
windows-host / package (push) Successful in 8m6s
release / apple (push) Successful in 8m17s
deb / build-publish (push) Successful in 3m26s
decky / build-publish (push) Successful in 25s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
ci / bench (push) Successful in 4m42s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 2m36s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m17s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Failing after 19s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 51s
apple / screenshots (push) Successful in 5m45s
docker / deploy-docs (push) Successful in 22s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Failing after 22s

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>
This commit is contained in:
2026-06-30 19:05:22 +02:00
parent e1bc9fda22
commit ba39b08e09
86 changed files with 2726 additions and 2019 deletions
+9 -1
View File
@@ -121,7 +121,8 @@ pub fn run(opts: Punktfunk1Options) -> Result<()> {
// (harmless — the loops' `is_armed()` gate is always false). The unified `serve` shares one
// recorder across mgmt + both streaming paths instead.
let stats = StatsRecorder::new(crate::stats_recorder::default_dir());
rt.block_on(serve(opts, np, stats))
// Standalone `punktfunk1-host` runs no management API, so advertise no `mgmt` port (0).
rt.block_on(serve(opts, 0, np, stats))
}
fn fingerprint_hex(fp: &[u8; 32]) -> String {
@@ -139,6 +140,9 @@ pub(crate) struct NativeServe {
/// insecure; `serve --open` turns it off (trusted single-user setups). Pairing is armed on
/// demand from the web console (arm → PIN); paired devices persist.
pub require_pairing: bool,
/// The management API's TCP port, advertised over mDNS so a client browses the game library on
/// the same host IP (the unified `serve` always runs the mgmt API, so this is its bind port).
pub mgmt_port: u16,
}
/// Options for the native host when the unified `serve --native` runs it: real virtual capture,
@@ -166,6 +170,7 @@ pub(crate) fn native_serve_opts(cfg: &NativeServe) -> Punktfunk1Options {
pub(crate) async fn serve(
opts: Punktfunk1Options,
mgmt_port: u16,
np: Arc<NativePairing>,
stats: Arc<StatsRecorder>,
) -> Result<()> {
@@ -198,6 +203,8 @@ pub(crate) async fn serve(
&fingerprint_hex(&fingerprint),
opts.require_pairing,
&h.uniqueid,
// 0 = standalone `punktfunk1-host` (no mgmt API) → don't advertise an `mgmt` port.
(mgmt_port != 0).then_some(mgmt_port),
)
.map_err(|e| tracing::warn!(error = %format!("{e:#}"), "native mDNS advertise failed (continuing)"))
.ok(),
@@ -3864,6 +3871,7 @@ mod tests {
pairing_pin: None,
paired_store: None, // unused: the shared `np` IS the store handle
},
0, // no mgmt API in this test → advertise no `mgmt` mDNS port
np_host,
StatsRecorder::new(
std::env::temp_dir().join(format!("pf-approval-stats-{}", std::process::id())),