feat(clients/windows): game library page + session window placement + help polish
apple / swift (push) Successful in 1m10s
android / android (push) Successful in 4m56s
arch / build-publish (push) Successful in 5m56s
audit / bun-audit (push) Successful in 13s
audit / cargo-audit (push) Successful in 1m34s
ci / web (push) Successful in 1m11s
windows-host / package (push) Successful in 7m57s
ci / docs-site (push) Successful in 1m27s
release / apple (push) Successful in 8m39s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m26s
ci / bench (push) Successful in 5m0s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m16s
apple / screenshots (push) Successful in 5m35s
ci / rust (push) Successful in 10m32s
decky / build-publish (push) Successful in 13s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 7s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m47s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m36s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
deb / build-publish (push) Successful in 6m38s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m39s
flatpak / build-publish (push) Successful in 5m32s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 11m0s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 10m33s
docker / deploy-docs (push) Successful in 18s

UX polish batch 2 (plan workstreams D/E5/E6):

- D: a mouse/keyboard game library page over pf-client-core::library (the GTK
  ui_library.rs counterpart): responsive 2-6 column poster grid (2:3 portraits,
  store badge, monogram placeholder while art streams in), loading / error+retry
  / empty / grid states, tap-to-play via a normal spawn carrying --launch id.
  Poster bytes land in a disk cache (%LOCALAPPDATA%\punktfunk\art-cache) so the
  Image widget loads file:/// URIs (reactor has no from-bytes source) and
  revisits render instantly. Reached from a paired host's "Browse library..."
  menu item behind the new "Show game library (experimental)" Settings toggle
  (the shared library_enabled field, Apple/GTK parity). A Shared::library_gen
  generation guard keeps a superseded fetch from publishing (speed-test pattern).

- E5: the session window opens at the shell's own top-left (--window-pos, new
  SessionOpts::window_pos) instead of centered on the primary display - streams
  land on the monitor the shell is on, and the hide/restore handoff reads as one
  window changing content. Fullscreen follows that display.

- E6: the Help shortcuts add Alt+Enter and the controller escape chord
  (LB+RB+Start+Back, hold to disconnect).

Also: rustfmt normalization of the merged probe helper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 00:39:11 +02:00
parent f5f186b691
commit f4850625bd
13 changed files with 531 additions and 33 deletions
+46 -2
View File
@@ -79,6 +79,44 @@ fn initiate_opts(
}
}
/// Start a stream that launches a library title on connect (`--launch id`) — the library
/// page's tap-to-play. The library only opens for paired hosts, so the pin resolves like
/// a normal initiate; a host forgotten mid-visit routes to the PIN ceremony instead.
pub(crate) fn initiate_launch(
ctx: &Arc<AppCtx>,
target: Target,
launch: String,
set_screen: &AsyncSetState<Screen>,
set_status: &AsyncSetState<String>,
) {
*ctx.shared.target.lock().unwrap() = target.clone();
let known = KnownHosts::load();
let pin = target
.fp_hex
.as_deref()
.and_then(trust::parse_hex32)
.or_else(|| {
known
.find_by_addr(&target.addr, target.port)
.and_then(|k| trust::parse_hex32(&k.fp_hex))
});
let Some(pin) = pin else {
set_screen.call(Screen::Pair);
return;
};
connect_with(
ctx,
&target,
Some(pin),
set_screen,
set_status,
ConnectOpts {
launch: Some(launch),
..ConnectOpts::default()
},
);
}
/// The mode to request: explicit settings, with `0` fields resolved to the native size/refresh
/// of the display our window is on (mirrors the Linux/Swift clients' native-display default).
pub(crate) fn resolve_mode(s: &Settings) -> Mode {
@@ -181,6 +219,11 @@ pub(crate) struct ConnectOpts {
/// failure escalates to the visible "Waking…" wait. The wait's own redial clears the flag,
/// so it can't loop.
wake_on_fail: bool,
/// A library title id (`steam:570`, …) the host launches during the connect handshake —
/// the library page's tap-to-play. Spawn mode passes it as `--launch`; the legacy
/// in-process path has no launch plumbing (it predates the library and is slated for
/// deletion).
launch: Option<String>,
}
impl Default for ConnectOpts {
@@ -191,6 +234,7 @@ impl Default for ConnectOpts {
awaiting_approval: false,
cancel: None,
wake_on_fail: false,
launch: None,
}
}
}
@@ -396,7 +440,7 @@ fn connect_spawn(
&fp_arg,
opts.connect_timeout.as_secs(),
fullscreen,
None,
opts.launch.as_deref(),
child,
move |event| {
use crate::spawn::SpawnEvent;
@@ -544,7 +588,7 @@ pub(crate) fn request_access(props: &Svc, target: &Target) {
persist_paired: true,
awaiting_approval: true,
cancel: Some(cancel),
wake_on_fail: false,
..ConnectOpts::default()
},
);
}