028462ef6d8a402f3bbafceaf8b1d871457a7b92
10
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d8252bb777 |
fix(client/apple): the sort menu was brand-purple beside two plain toolbar buttons
A Menu draws its label in the ACCENT colour where a Button draws it in the label colour, so this one came out tinted next to Add Host and Settings. macOS only: on iOS every toolbar item is accent-tinted already, and pinning this one to primary would make it the odd one out there instead of in step. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2e7b4aea79 |
fix(client/apple): a pinned card belongs to its profile, not to its host's binding
Grouping by profile asked each HOST which profile it was bound to, then handed the answer to that host's every card. So a card visibly wearing a "Gaming" chip — a pin, on a host bound to nothing — was filed under "No Profile" along with the rest. Every pinned card was, which is most of what the grouping exists to separate. Pins are not bindings. The arrangement works on CARDS now: a host's own card follows its binding, a pinned card follows its pin, and both can land in the same band when a host pinned the profile it is also bound to. The expansion moved out of the view and into the arrangement, because it is exactly what the grouping has to see. The regression is pinned by a test whose fixture is the shape that broke: a host bound to nothing, carrying two pins. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f071467cbb |
feat(client/apple): sort and group the host grid
The grid had no ordering story — hosts sat in the order they were added and that was the whole of it. A toolbar menu now offers Sort by (Date Added, Name, Last Connected) and Group by (None, Profile, Status), both per device: this is a window on a list, not something about how a host streams. The control stays out of the toolbar until there are two hosts to arrange. Grouping by profile is the one the profiles made possible: bands in catalog order, each header wearing its profile's colour, then the unbound hosts. Pinned cards stay with their host — a pin is presentation of that host, not a host of its own — and a dangling binding lands in "No Profile" rather than in a band named after a profile that no longer exists. Empty bands aren't drawn. The ordering is pure and tested, which earned its keep immediately: the test caught that undated hosts were sorting first, and made me work out whether that was a bug. It isn't — no date means saved before `addedAt` existed, which means older, and in a real store those are a prefix, so the default sort leaves an upgraded grid exactly as it was. Two other traps are pinned by tests: `sorted` is not stable in Swift, so every comparison tie-breaks on the stored index or equal rows swap on redraw; and a never-connected host sorts LAST under Last Connected, not first as `.distantPast` would have it. `StoredHost` gains `addedAt` for the date the sort actually needs — optional and appended last, so the widget contract holds and hosts saved before it keep working. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0a32f4eab1 |
fix(client/apple): the iOS slice hit the type-checker wall, and tvOS couldn't be checked at all
`ContentView.body` grew two modifiers and stopped type-checking on the iOS slice — "unable to type-check this expression in reasonable time", a failure macOS builds never show and the one Apple CI never builds. Split into the screen plus its lifecycle drivers, then the prompt chain, with each alert's presentation Binding lifted out the way the deep-link one already was. tvOS couldn't be checked from the command line at all: HomeView imports the slide transition, which ships with the Xcode project only because its manifest breaks SwiftPM's whole-graph validation. `canImport` instead of a bare `os(tvOS)` gate makes the tvOS sources compile without it, and the app still gets the transition because there the module is present. Both slices now typecheck by hand. The scope switcher is tvOS-gated with them: a name prompt and a nested management menu are not what a remote does well, and §5.4 keeps profile EDITING off controller-first surfaces in v1 — they honor bindings and render pinned cards. Also: release the session's settings latch when a connect fails, is refused, or is abandoned mid-handshake. Only `disconnect` cleared it, so a failed dial left the would-be session's resolution latched over the globals. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
25b127803f |
feat(client/apple): bind a host to a profile, connect with one, pin one as its own card
The host surfaces (design §5.2/§5.2a). A bound host wears a tinted chip that says what a click will do. The card menu grows "Connect with ▸" — a ONE-OFF that never rebinds, with a checkmark on the binding and an explicit "Set Default Profile" as its last item for the users who do want to rebind from there — plus "Pin as Card ▸" and "Copy Link". A pinned host+profile combo becomes its own card next to its host: same record, same live status, the profile as the prominent subtitle, one click to connect. It is an extra grid ENTRY, never a duplicated host record — duplicating would fork pairing, Wake-on-LAN and renames. Its menu carries only connect-shaped actions; edit, pair, forget and remove stay on the primary card, where the thing they act on lives. On the gamepad carousel and tvOS the same pins are tiles, which is the whole point: focus-and-press is what those surfaces do well, and menus are not. The edit sheet binds and pins; both rows vanish when no profiles exist, so a user who never makes one sees exactly today's sheet. A dangling binding renders as "Default settings (profile deleted)" and is cleaned up on save. The speed test finally writes where the tested host reads. Unbound: the global, as before. Bound to a profile that overrides bitrate: that override. Bound to one that inherits it: both are offered, because either is defensible and guessing would silently pick one. Every button names its target, and the probe now runs at the mode that host would actually stream — the measurement is the streaming path. Shortcuts gains `ProfileEntity` over the App-Group catalog and a Profile parameter on Connect, still round-tripping through the URL router rather than opening a second connect path. Connect and Wake drop their iOS wall — AppIntents is real on macOS and tvOS, and "Stream Desktop with Work" from Spotlight was the ask; only the phrases provider stays iOS-gated, since it bundles the LiveActivityIntent. The deep-link observer moved out with them: an intent that posts to nobody is a shortcut that silently does nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
3ba19f28a2 |
feat(apple): the gamepad UI comes to tvOS - focus-driven, with real session controls
ci / rust (push) Failing after 50s
ci / web (push) Successful in 57s
decky / build-publish (push) Successful in 14s
ci / docs-site (push) Successful in 1m2s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 29s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 6s
flatpak / build-publish (push) Failing after 2m14s
ci / bench (push) Successful in 5m34s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5m48s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5m46s
docker / deploy-docs (push) Successful in 23s
windows-host / package (push) Successful in 11m40s
arch / build-publish (push) Successful in 18m10s
deb / build-publish (push) Successful in 18m9s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 4m21s
android / android (push) Successful in 21m4s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 17m2s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m37s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 5m38s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m3s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 1m2s
release / apple (push) Has been cancelled
apple / swift (push) Has been cancelled
apple / screenshots (push) Has been cancelled
The console UI now runs on tvOS through the NATIVE focus engine: carousel cards and settings rows are focusable Buttons (Siri Remote and pads both navigate; imperative scrollTo replaces the drop-prone scrollPosition binding), while iOS/macOS keep the 60 Hz poll untouched - on tvOS it carries only what focus has no concept of: X/Y screen actions and left/right value adjust with the poll's dominant-axis feel (onMoveCommand proved input-source-dependent: keyboard intercepted, pad dpad not -> double steps). Text entry uses the system fullscreen keyboard (TVTextEntry); pairing + library present as covers under the launcher; the game library defaults ON; settings values slide a quiet 14 pt in the step's direction. Session controls: controller/remote input routes EXCLUSIVELY through GameController during a stream (GCEventViewController, interaction disabled) - a pad's B no longer doubles as a UIKit menu press that ended sessions mid-game. Deliberate exits only: the cross-client escape chord (hold L1+R1+Start+Select 1.5 s - pf-client-core's contract, now implemented on all Apple platforms) and holding the remote's Back >= 1 s; the start-of-stream banner (now also on tvOS) teaches both. The Siri Remote's touch surface drives the host pointer - press = left click, Play/Pause = right click, release-tail jumps gated so motion stays truly relative. tvOS 26 regressions fixed at the root: the app-wide brand tint rendered every unfocused control as a blank pill (tint dropped on tvOS) and the 17 pt root font shrank the whole platform (29 pt there), plus 10-foot sizing across host cards, the gamepad screens, and the stats HUD (whose misleading "Press Menu" hint is gone). Acknowledgements scrolls by focus-sized chunks and Menu pops instead of suspending; full-width focusSections make the home actions reachable from any column. The presenter defaults to stage-3 glass pacing on tvOS (a 60 Hz panel fed a 60 fps stream is the sticky-FIFO worst case behind the 50 ms display stage) and is pickable from the gamepad settings; HDR capability advertises from AVPlayer.eligibleForHDRPlayback instead of the current mode's EDR headroom, so an SDR home screen no longer hides an HDR TV. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
01428ced58 |
feat(clients): reachability-probed presence + shareable Decky host management
apple / swift (push) Failing after 27s
release / apple (push) Failing after 26s
apple / screenshots (push) Has been skipped
windows-host / package (push) Has been cancelled
arch / build-publish (push) Has been cancelled
android / android (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / web (push) Has been cancelled
ci / docs-site (push) Has been cancelled
ci / bench (push) Has been cancelled
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Has been cancelled
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Has been cancelled
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Has been cancelled
docker / deploy-docs (push) Has been cancelled
flatpak / build-publish (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Has been cancelled
windows / build (aarch64-pc-windows-msvc) (push) Has been cancelled
windows / build (x86_64-pc-windows-msvc) (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (push) Has been cancelled
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Has been cancelled
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Has been cancelled
Add a bounded, trust-agnostic, mDNS-INDEPENDENT QUIC reachability probe and surface it everywhere saved-host presence is shown, so a host reached over a routed network (Tailscale/VPN/multicast-filtering LAN) no longer reads Offline just because it isn't advertising — the display-side companion to the 0.8.4 dial-first connect fix. Core: - punktfunk-core: NativeClient::probe (bounded handshake; a real host answers even on trust mismatch, a wrong/closed/TCP-only port fails) + punktfunk_probe C ABI (ABI_VERSION 3->4, header regenerated). - pf-client-core: trust::probe_reachable_many (parallel per-host sweep). Presence pips now read `advertising OR probed-reachable`, refreshed by a ~10-12s background sweep off the UI thread: - Linux (relm4): ui_hosts probed map + HostsMsg::Probed sweep. - Windows (windows-reactor): pf-probe worker -> HostsProps.probed. - Apple (SwiftUI): HostStore.refreshReachability, driven by HomeView + GamepadHomeView .task. - Android (Compose): nativeProbe JNI seam + periodic LaunchedEffect (LNP-gated), online dot added to the touch HostCard. - Decky already probes via --list-hosts --probe. Decky client: make the flatpak client's known-hosts store the single source of truth via new headless CLI modes (--list-hosts / --add-host / --set-host / --forget-host / --reset / --reachable). The plugin can now add a host by address, edit/forget hosts, reset all state (keeping the client identity), and shows probe-backed online pips — state is shared with the desktop client, not duplicated. Also lands in-progress Android 17 LNP groundwork (targetSdk 37 + ACCESS_LOCAL_NETWORK runtime flow, permission dialogs) that was already present in the working tree. Verified: cargo check + clippy clean (punktfunk-core, pf-client-core, linux, android native); android assembleDebug BUILD SUCCESSFUL; decky typecheck + rollup build clean; probe true/false-positive behaviour exercised against a live host. Windows and Apple were not compiled locally (no MSVC/Xcode on this Linux box). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
60121f1ea9 |
feat(apple): wake-until-up overlay + host edit with MAC prefill
- HostWaker + WakeOverlay: after sending the Wake-on-LAN packet, wait until the host is really back (resend + mDNS poll, timeout, cancel/retry) before connecting. macOS-only in practice — WoL stays gated off on iOS/tvOS pending the multicast entitlement. - Add/Edit host sheet gains a Wake-on-LAN MAC field, prefilled from the stored MAC or the live mDNS advert; parseMacs validates aa:bb:cc:dd:ee:ff. - Gamepad chrome/home and glass-style polish. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fb709d68ce |
feat(clients): Wake-on-LAN in apple/linux/windows/android/decky
Each client learns a host's MAC from the mDNS `mac` TXT while it's awake, persists it on the saved-host record, and — when reconnecting to an offline host — sends a magic packet before connecting, plus an explicit "Wake host" action. Apple wraps the C-ABI; linux/windows call the core fn directly (linux also gains a --wake CLI mode); android via a new nativeWakeOnLan JNI export (the mDNS browse record gains a 7th mac field); decky shells out to the linux client's --wake before launching the stream. iOS/tvOS need the managed com.apple.developer.networking.multicast entitlement (pending Apple approval), so the wake path + UI are gated off via PunktfunkConnection.wakeOnLANAvailable and the entitlement is commented out — keeping iOS/tvOS releasable. MAC-learning stays active on every platform so it lights up the moment it's ungated. macOS works today. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
34fe896ade |
feat(apple): gamepad UI v2 — controller settings + add host, aurora, macOS
Sources reorganized (client: Home/Session/Settings/Stores/Support/Trust; kit: Audio/Connection/Gamepad/Input/Support/Video/Views) with the big files split along the same seams. The gamepad mode is couch-complete, and now on macOS too (the living-room Mac case), not just iOS/iPadOS: - GamepadSettingsView: a console-style, fully controller-navigable settings screen (X from the launcher) — up/down moves focus, left/right steps values (clamped, boundary thud), A cycles/toggles, B closes; the focused row shows a one-line description. Backed by GamepadMenuList, the vertical sibling of GamepadCarousel, and SettingsOptions — the option lists hoisted out of SettingsView statics and shared by the touch, tvOS and gamepad settings. - GamepadAddHostView + GamepadKeyboard: register a host end to end with a pad — field rows open an on-screen controller keyboard (dpad grid, A types, X backspaces, B done); the launcher carousel ends in an Add Host tile, so the dead-end "add one with touch first" empty state is gone. - Launcher polish: contextual hint bar with the pad's real button glyphs, controller name + battery chip, one shared console chrome. - GamepadScreenBackground: an animated aurora (TimelineView-driven drifting blobs in the brand's violet family, breathing radii, slow hue shift, legibility scrim; freezes under Reduce Motion). Pure SwiftUI on purpose — a .metal library only bundles reliably in one of the two build systems (SPM vs the xcodeproj's synced folders) these sources compile under. - macOS port: settings/add-host/library present as sized sheets (a macOS sheet takes its content's IDEAL size, and the GeometryReader-driven screens collapsed to nothing), NSScreen-based mode lists, scroll indicators .never (the "always show scroll bars" setting overrides .hidden), tray scrims so scrolled rows dim under the pinned title/hints, extra title clearance, and a PUNKTFUNK_FORCE_GAMEPAD_UI=1 dev hook — launcher/settings/add-host/keyboard/ library render-verified live on a real Mac + LAN hosts. - GamepadMenuInput: X button support, and (re)start now snapshots held buttons so a controller handoff press never fires twice (the B that closed the keyboard no longer also cancels the screen underneath). - Cleanups: one "Connection failed" alert in ContentView instead of one per home screen; HostDiscovery.advertises/unsaved shared by both home screens. - host: can_encode_444 stub for the non-Linux/Windows host build (the macOS synthetic-source loopback used by the Swift tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |