The NSPasteboard bridge completing Phase 1 (design/clipboard-and-file-transfer.md
§5) — with the host backends on this branch, copy/paste now crosses the wire in
both directions on macOS. Lazy in both directions:
- PunktfunkConnection grows the clipboard plane: its own clipboardLock (close()
joins it like the other pullers), hostCaps/hostSupportsClipboard from the
Welcome, the typed ClipEvent vocabulary, and the six ABI wrappers
(clipControl/clipOffer/clipFetch/clipServe/clipCancel/nextClipboard — borrowed
event payloads copied out before the next poll).
- ClipboardSync (PunktfunkKit, macOS-only): one drain thread bridging
NSPasteboard.general ↔ the QUIC clipboard plane. Local copies announce format
lists via a 500 ms changeCount poll (+ immediate on app activation); bytes
leave only on a host FetchRequest, answered from the live pasteboard and
seq-guarded against staleness. Host copies install one NSPasteboardItem whose
data provider fires only when a Mac app actually pastes, then blocks its
provider thread (never main) on a 10 s-bounded fetch. Concealed/Transient
pasteboards (password managers) are never announced; our own writes are
changeCount-suppressed (§3.4). Text/RTF/HTML/PNG; files ride Phase 2.
- UI: per-host "Share clipboard with this host" toggle (StoredHost.clipboardSync,
optional for saved-JSON forward-compat — wire-format tests extended), a
mid-session Share/Stop Sharing Clipboard item in the Stream menu (⌃⌥⇧C,
greyed without HOST_CAP_CLIPBOARD), SessionModel owning the lifecycle
(start on streaming after the trust gate, drain joined off-main on teardown).
swift build + swift test green (macOS). Requires the ABI v8 xcframework
(scripts/build-xcframework.sh).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Foundation milestone for Live Activities & Widgets (design/apple-live-
activities-and-widgets.md). No user-visible change beyond the URL scheme.
- New dependency-free PunktfunkShared SwiftPM target (+ library product) so a
future widget extension can link it WITHOUT PunktfunkKit (Rust staticlib +
presentation layer). Moves StoredHost (model + JSON codec), DefaultsKeys, and
punktfunkDefaultMgmtPort there; adds AppGroup.suiteName and the punktfunk://
DeepLink builder/parser. PunktfunkKit @_exported-imports it (no call-site
churn for consumers; intra-Kit files import it explicitly since imports are
file-scoped).
- HostStore reads/writes the shared App-Group suite (group.io.unom.punktfunk)
with a one-time migration from UserDefaults.standard (old value left in place
for staged rollout); reloads the "PunktfunkHosts" widget timeline on change.
- App Group entitlement on iOS/tvOS + macOS.
- CFBundleURLTypes scheme `punktfunk`; ContentView.onOpenURL routes
connect/<uuid>[?launch=<GameEntry.id>] into the existing connect() path
(unknown host / already-streaming guards; never tears down a live session).
- Round-trip tests: StoredHost JSON codec (+ legacy missing-optional decode),
DeepLink grammar. `swift build` + `swift test` green (142 tests, 0 failures).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>