Commit Graph

10 Commits

Author SHA1 Message Date
enricobuehler a17997bb01 fix(apple): pairing copy points at the web console for the PIN
ci / rust (push) Has been cancelled
The PIN now surfaces in the host's web admin UI (port 3000 → Pairing), which is where
users will actually read it — the pairing sheet's footer, field prompts, the tvOS
keyboard title, and the wrong-PIN/failure errors all reference the console instead of
the host log / --allow-pairing flag (the log mention stays in the README as the
secondary path).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 14:31:24 +02:00
enricobuehler f01b07a973 fix(apple/tvOS): pushed routes instead of modal covers — the Settings-app navigation feel
ci / rust (push) Has been cancelled
Add Host, Settings and PIN pairing were fullScreenCover overlays, which is why
navigating felt unlike the system Settings app (no push animation, no Menu-pops-a-level
semantics). They are now navigationDestination ROUTES pushed inside the home
NavigationStack:

- the system push/pop animation and Menu-button back navigation come for free;
- the Settings pickers' navigationLink pushes reuse the same stack (its inner
  NavigationStack wrapper is gone, as is the tvOS Done row — Menu pops, like Settings);
- Add Host is a real full-screen page (system navigation title, Settings-style rows on
  the standard backdrop) instead of a floating dialog, same for the pairing page;
- the thickMaterial cover backdrops became unnecessary and are gone. The system
  keyboard entries stay as covers — that presentation is system-owned either way.

iOS/macOS keep their sheets. Verified by screenshot: Add Host renders as a pushed
full-screen route with the title top-center.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 14:03:10 +02:00
enricobuehler 06a2d5e0ca fix(apple/tvOS): system fullscreen keyboard for all text entry — no inline fields
ci / rust (push) Has been cancelled
SwiftUI's inline TextField on tvOS is structurally wrong for television: it grows when
activated, shows a full-width editing surface behind the pill, and floats labels
off-center — none of it stylable into the Settings-app look. Per Apple's tvOS text
input guidance, real tvOS apps never edit inline: a field is a value ROW, and pressing
it raises the SYSTEM fullscreen keyboard.

- TVTextEntry (UIViewControllerRepresentable): a UITextField that becomesFirstResponder
  on appear, presenting the standard tvOS fullscreen keyboard with the field's prompt;
  done/dismiss commits the text. TVFieldRow is the Settings-style label+value lozenge.
- Add Host and PIN pairing on tvOS now use rows + keyboard covers exclusively (the
  port row also fixes the off-center value text for good — it's a Text, not a field);
  the port input validates 1...65535.
- No SwiftUI TextField remains in any tvOS code path.

Verified by screenshot: the dialog rows render exactly like the Settings app, and the
address row raises the system linear keyboard with prompt + done.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:56:39 +02:00
enricobuehler f292b3fe3a fix(apple/tvOS): focus-native home grid, separated actions, Form-free dialogs
ci / rust (push) Has been cancelled
Three more tvOS-isms, all the same lesson — let the focus engine own the chrome:

- Host cards drew their own material platter + accent ring INSIDE the .card button
  style, muting the native grow/tilt focus motion. On tvOS the card style now owns the
  platter outright (material/ring stay on the pointer platforms), and the grid gets
  48 pt spacing so the focused card swells without overlapping siblings.
- Add Host and Settings no longer sit in the hosts row: they're a compact button row
  below the grid (and the empty state gains a Settings button, since tvOS has no
  toolbar).
- The Add Host and pairing dialogs drop Form entirely on tvOS — list rows added a
  full-width focus fill plus a row platter behind every field's own pill (the
  "second outer pill"). As standalone fields in a centered dialog over the dimmed
  home, each input is exactly one pill with vertically centered text.

Verified by screenshot in the Apple TV simulator (home grid + Add Host dialog).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:47:27 +02:00
enricobuehler 9e57a5a1ff fix(apple/tvOS): native form controls — pushed pickers, single-pill fields, centered values
ci / rust (push) Has been cancelled
The inline iOS form widgets fought the tvOS focus system at every turn: focused fields
showed nested pills, rows darkened oddly and grew on activation, the Compositor picker
was not even focusable, and prefilled fields (port, client name) floated their label
inside the pill, shoving the value off-center.

- Settings is now a fully tv-native screen: NO inline text entry — the stream mode is
  a preset picker (This TV native / 720p / 1080p / 4K, plus a Custom entry preserving
  a mode set on another platform) and both pickers use .navigationLink style (pushed
  selection lists, exactly like the system Settings app — and properly focusable; the
  cover wraps in a NavigationStack for the pushes).
- Where text entry is unavoidable (Add Host, PIN pairing), the fields keep their stock
  single-pill chrome (the grouped form style stays off tvOS — its row platters were
  one of the nested pills) and prefilled fields hide their floating label so values
  center vertically.
- All earlier row-clearing experiments reverted.

Verified by screenshot in the Apple TV simulator: Settings rows render as single
focus lozenges with chevrons; the Add Host pills are uniform with centered text.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:38:37 +02:00
enricobuehler bfd8c7be93 feat(apple): tvOS client — third app target, first-lit in the Apple TV simulator
ci / rust (push) Has been cancelled
The same app now runs on tvOS (target Punktfunk-tvOS, bundle io.unom.punktfunk.tvos),
validated live against the box: vkcube at 1280x720@60, 60 fps in the Apple TV 4K
simulator, glass HUD with a focusable Disconnect button.

- PunktfunkCore.xcframework grows tvOS device + universal-simulator slices. These are
  TIER-3 Rust targets (no prebuilt std): BUILD_TVOS=1 builds them with nightly and
  -Zbuild-std from rust-src — the full quic stack (quinn/rustls-ring/tokio) compiles
  for tvOS unchanged.
- The UIKit stream view covers iOS AND tvOS, with pointer interaction, pointer lock,
  touch forwarding and InputCapture gated to iOS — tvOS is view-only until gamepad
  capture lands (the natural tvOS input).
- SessionAudio on tvOS: .playback session, no mic (no app-accessible microphone).
- App chrome gates: keyboardShortcut/textSelection/controlSize/statusBarHidden are
  iOS/macOS-only; host cards use the focus-native .card button style on tvOS; the
  Audio settings section hides (system-routed); mode seeding works from the TV screen
  (1920x1080@60).
- Package platforms += .tvOS(.v17); new Xcode target + shared scheme
  (TARGETED_DEVICE_FAMILY 3, local-network usage description included).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:10:40 +02:00
enricobuehler ee12e535ee feat(apple): styling pass — dark-mode accent, recent-host state, glass HUD, security-sheet polish
ci / rust (push) Has been cancelled
Working through the brand-color follow-ups:

- AccentColor gains a dark-appearance variant (#8678F5 — the brand violet lifted one
  step toward the icon's light periwinkle) so tinted controls keep contrast on dark.
- Host cards remember sessions: StoredHost.lastConnected (set when a session reaches
  streaming) renders as a "Connected … ago" relative-time line, and the most recent
  host's card carries a subtle accent ring — the grid finally has hierarchy.
- The HUD swaps the pre-glass black-50% rectangle for .regularMaterial with an accent
  live-dot; hint lines use semantic .secondary instead of opacity.
- Security moments: the trust card's lock.shield and the pairing sheet's header take
  the brand tint; the PIN field is larger monospaced and uses the number pad on iOS.

Icon ↔ accent decision: the accent stays the exact brand #6656F2; the Icon Composer
layers keep their adjacent palette (#6C5BF3 family) — close enough to read as one
brand, and the icon remains the design-tool source of truth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:59:55 +02:00
enricobuehler 7c24832ad0 fix(apple/iOS): touch-first control sizing — toolbar circles + large sheet buttons
ci / rust (push) Has been cancelled
The iOS chrome inherited macOS dialog sizing and read as undersized on a phone:

- Toolbar: the two trailing actions shared one compact glass pill; on iOS 26+ each now
  gets its own full-size circle (explicit .topBarTrailing placements split by a fixed
  ToolbarSpacer — the system-app look, e.g. Files), with the grouped-pill fallback on
  iOS 17–18. The buttons are extracted so macOS keeps SettingsLink + .help untouched.
- Sheets and CTAs (AddHostSheet, PairSheet, trust card, empty-state Add Host) get
  .controlSize(.large) on iOS — proper touch targets instead of macOS dialog buttons.

Verified in the iPhone 17 simulator: two ~44 pt glass circles matching the Files app's
toolbar sizing; macOS suite and app build unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 11:47:30 +02:00
enricobuehler e1af4d57c6 feat(apple): iOS/iPadOS client — touch, pointer lock, shared SwiftUI shell
ci / rust (push) Has been cancelled
The whole client now runs on iPadOS/iOS from the same sources, first-lit live in the
iPad simulator against the real host at 1280x720@60 (60 fps on the HUD, capture state
machine active, mic permission flow shown).

- PunktfunkCore.xcframework grows iOS device + universal-simulator slices
  (BUILD_IOS=1; rustup targets aarch64-apple-ios{,-sim} + x86_64-apple-ios).
- The decode pump is extracted into a shared StreamPump (identical IDR re-gate logic on
  both platforms); the iOS StreamView (StreamViewIOS.swift) has the same name/signature
  as the macOS one, so ContentView & co. are byte-identical across platforms — hosted
  in a UIViewController for prefersPointerLocked (the iPadOS cursor capture; see README
  note 9 for the UIHostingController forwarding caveat).
- Touch is always forwarded: per-finger wire ids, coordinates mapped through the
  aspect-fit letterbox into LIVE host-mode pixels (surface == host mode, identity
  rescale host-side; follows mid-stream requestMode switches).
- InputCapture is cross-platform: GC works the same on iPadOS, ⌘⎋ is detected from the
  HID stream there; stale-⌘ tracking after focus loss fixed on both platforms
  (releaseAll now drops the modifier/latch state — a ⌘ released in another app
  otherwise hijacked Esc forever).
- SessionAudio: AVAudioSession on iOS (.playAndRecord + .defaultToSpeaker — without it
  iPhones route host audio to the EARPIECE; deactivated with
  notifyOthersOnDeactivation on stop so interrupted background audio resumes); HAL
  device pinning + the Settings pickers stay macOS-only.
- New Punktfunk-iOS app target (shared synchronized sources, generated Info.plist with
  mic + local-network usage descriptions — QUIC to a LAN host trips local network
  privacy on real devices — scene manifest + indirect input events for Stage Manager /
  external displays), shared scheme, macOS min-window frames gated off iOS.

For the iPad-on-an-external-screen idea: with multiple scenes + indirect input enabled,
Stage Manager iPads can drag the punktfunk window onto the external display and drive
the PC with keyboard/mouse/touch. Known gaps (README note 9): the pointer-lock
preference isn't consulted through UIHostingController (relative mouse works, the local
cursor just stays visible) and AVAudioSession interruptions don't auto-restart audio.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 11:18:25 +02:00
enricobuehler 0494e0200a feat(apple): adapt the macOS client to ABI v2 — client identity + SPAKE2 PIN pairing
ci / rust (push) Has been cancelled
The pairing/renegotiation batch bumped the punktfunk/1 ABI to v2 and the host now
hard-rejects v1 Hellos (m3.rs), so streaming from the Mac was dead until the bundled
PunktfunkCore.xcframework is rebuilt — it is gitignored, so that is a per-checkout step:
bash scripts/build-xcframework.sh. The Swift wrapper itself was already adapted upstream;
this lands the app on top of it.

- ClientIdentityStore: persistent client identity in the login Keychain, presented on
  every connect so paired hosts recognize this Mac. Keychain access failure throws
  instead of regenerating (a fresh identity would silently un-pair this Mac from every
  --require-pairing host); a lost first-run race resolves toward the stored identity;
  pairing uses the strict loadForPairing() so a memory-only identity can't strand a
  ceremony.
- PairSheet: the SPAKE2 PIN ceremony, reachable from a host card's context menu and from
  the trust prompt's "Pair with PIN instead…" (which drops the live session first — the
  host's accept loop is sequential). Success pins the verified fingerprint and connects;
  an in-flight ceremony self-discards when the sheet is dismissed, so a late success
  can't pin + auto-connect behind the user's back. Wrong PIN and Keychain failures get
  distinct, actionable error text.
- Tests: identity unit tests; the full pairing ceremony + --require-pairing gate on
  loopback (test-loopback.sh arms a second host, parses its PIN from the log, and gives
  both hosts throwaway config homes — no more writes to the real ~/.config/punktfunk);
  remote pairing + pinned stream over the LAN (PUNKTFUNK_REMOTE_PIN, _PORT).

Validated live against the box: SPAKE2 ceremony with the host's arming PIN → verified
fingerprint → pinned + identified 720p60 session (host persisted the client identity);
first light 60/60 AUs decoded to pixels; vkcube on glass through the app.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 21:49:43 +02:00