Commit Graph

7 Commits

Author SHA1 Message Date
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 b7a6670b4a feat(apple): brand accent color (#6656F2) via the asset catalog
ci / rust (push) Has been cancelled
AccentColor color set + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME on all four app
configurations — the platform-sanctioned global tint, so the host-card icons, prominent
buttons, toggles, pickers and links all carry the brand violet on macOS and iOS without
any per-view styling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:56:20 +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 b26f138699 feat(apple): session audio — host playback + mic uplink, device pickers in Settings
ci / rust (push) Has been cancelled
Both directions of the audio plane, on CoreAudio's built-in Opus codec
(kAudioFormatOpus — no bundled libopus; OpusCodec.swift, round trip unit-tested):

- Playback: a drain thread pulls nextAudio() packets, decodes, and writes a priming
  jitter ring feeding an AVAudioSourceNode (~20 ms prefill, adaptive to the device's
  render quantum so large-buffer devices don't oscillate prime/dropout; a high-water
  clamp sheds stall backlog so one network hiccup can't permanently lag audio behind
  video; underrun re-primes — one dip, not sustained crackle).
- Mic: a second engine taps the input device, resamples to 48 kHz stereo, Opus-encodes
  20 ms chunks and sendMic()s them into the host's virtual PipeWire source. Permission
  via AVCaptureDevice (NSMicrophoneUsageDescription added to the Xcode target).
- Settings: Speaker + Microphone pickers (CoreAudio HAL enumeration, persisted by
  device UID — "System default" leaves the engine unpinned so it follows macOS device
  changes) and a "Send microphone" toggle (default on). Applies from the next session.
- Audio starts with streaming, never during the trust prompt (no host sound — and no
  mic uplink — before the user trusted the host); teardown stops audio before close().

Adversarial-review fixes baked in: stop() and the dangling mic-permission callback
share one lock+flag protocol (no hot mic with no owner), the connect-success handler
bails when the attempt was abandoned mid-handshake (no session/mic for a dead window),
SessionAudio gets a deinit backstop (a dropped instance can't pin the connection via
its drain thread), and the render scratch buffer is block-owned (was leaked per
session).

Verified live against the box: remote test decodes 100 host Opus packets to PCM and
the host opens its virtual mic on the first uplinked frame ("punktfunk/1 virtual mic
ready"); on-glass session runs with both engines up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 09:39:15 +02:00
enricobuehler dc42d6a375 feat: app icon (Icon Composer) + Xcode project settings for it
punktfunk_Logo.icon (Icon Composer 2.0) in App/, ASSETCATALOG_COMPILER_APPICON_NAME set.
Compiles with Xcode 27 beta's actool; Xcode 26.5's actool crashes on EVERY .icon file
(known regression, Apple FB20183399, expo/expo#46121) — build with the beta (or 26.4.1)
until a 26.x fix lands. The icon itself is fine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 16:15:15 +02:00
enricobuehler b21fffc3d1 feat: Xcode app project for the macOS client (Punktfunk.app)
ci / rust (push) Has been cancelled
clients/apple was a bare Swift package — fine for swift run/test, but app icons, a real
bundle (Info.plist, signing identity, TCC), and the normal Xcode build/run flow need an
app target. Punktfunk.xcodeproj (synchronized-folder format) wraps the SAME sources as
the CLI dev shell (Sources/PunktfunkClient) plus App/Assets.xcassets, and links
PunktfunkKit from the local package — no source duplication, both flows stay green:
swift build / swift test / swift run PunktfunkClient, and xcodebuild -scheme Punktfunk.

The asset catalog ships an empty AppIcon slot ready for the Icon Composer .icon
(drag in + set as App Icon + drop the placeholder; see README — including the actool
crash observed with the current icon bundle). Package tests on ⌘U need one GUI step
(Edit Scheme → Test → +); a hand-written package-test scheme reference doesn't resolve
headlessly.

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