Skip to content

feat(Throw): add ceiling flight projection app - #293

Open
kyleve wants to merge 91 commits into
mainfrom
codex/throw-v1
Open

feat(Throw): add ceiling flight projection app#293
kyleve wants to merge 91 commits into
mainfrom
codex/throw-v1

Conversation

@kyleve

@kyleve kyleve commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Posted by an AI agent on kve's behalf.

Summary

  • User-facing: Add Throw, an iPhone and iPad app that projects live aircraft onto a ceiling or on-device preview.
  • User-facing: Provide geographic Map and altitude-aware True Sky projection modes with shared calibration controls.
  • User-facing: Support adsb.lol, local readsb, ADS-B Exchange Personal through RapidAPI, and Flightradar24.
  • User-facing: Add offline geography, regional Map centers, aircraft silhouettes, airline accents, route labels, motion prediction, and ambient flight-activity cues.
  • User-facing: Support external-display scenes, full-screen fallback, Preview, quiet hours, location refresh, onboarding, and source diagnostics.
  • User-facing: Introduce Projection Views. Air & Space is enabled. Transit, Stars, and Satellites remain visible as planned work.
  • Internal: Add typed ThrowCore and ThrowUI boundaries for sources, layers, experiences, frames, credentials, preferences, and runtime ownership.
  • Internal: Harden View preparation, lifecycle generations, source selection, and actor updates against stale or reordered work.
  • Developer: Add deterministic data generators, attribution, architecture rules, snapshot coverage, and ./Throw/install.

Why

Throw needs one reliable path from aircraft data to a restrained projector surface. Every controller and output scene must share one runtime.

The projection also needs geographic context without online map tiles. Bundled data keeps Map rendering private, deterministic, and available offline.

Product behavior

Projection and output

  • Map uses great-circle distance and bearing around a selected regional center. A dim observer ring preserves the actual location.
  • True Sky uses WGS84 ECEF and ENU geometry to derive aircraft azimuth and elevation.
  • Calibration controls true bearing, rotation, flips, and a safe inset. The projector remains responsible for focus and keystone.
  • Preview, full-screen output, and external displays use the same opaque-black ProjectionSurface.
  • Quiet standby keeps the output black and stops polling. Output demand controls the idle timer and foreground feed lifecycle.

Aircraft and geography

  • Source selection is explicit. Throw never mixes provider frames or changes providers automatically after an error.
  • Flight labels show route and flight metadata when the source provides it or optional ADSBDB enrichment resolves it.
  • Aircraft without a known route remain visible at reduced prominence. Ground aircraft stay hidden unless Map settings include them.
  • Six silhouette families, airline color dots, subtle fades, correction easing, and dead reckoning keep updates readable without dashboard chrome.
  • Local arrival and departure estimates use routes, motion, altitude, nearby airports, and open runway geometry.
  • Map shows contextual runway anchors. True Sky keeps aircraft cues but omits geographic and airport lines.
  • Offline geography includes generalized global features and detailed United States boundaries and primary roads.
  • The Geography layer has a separate low-intensity control and remains absent from True Sky and quiet output.

Projection Views

  • Air & Space contains Geography, Flights, and the planned Stars and Satellites layers.
  • One playlist controls all outputs. Each configured View owns a validated dwell duration and health state.
  • The coordinator prepares a target before a switch, fades to black, exchanges the complete frame, and fades back in.
  • Automatic rotation remains dormant while Air & Space is the only configured View. Transit provider work is deferred.

Privacy and credentials

  • Cloud sources receive a center rounded to 0.1 degrees. Throw applies exact local filtering after each response.
  • Local receiver URLs, observer coordinates, aircraft identities, callsigns, response bodies, and credentials stay out of logs.
  • Paid-provider credentials use device-only Keychain storage. Preferences store only typed credential references and non-secret settings.
  • Aircraft snapshots, routes, motion history, and usage estimates remain memory-only.

Architecture

  • The app delegate creates one ThrowRuntime and one shared ThrowSession for every controller and output scene.
  • ThrowCore owns typed domain values, providers, polling, projection math, prediction, preferences, credentials, schedules, and bundled data.
  • ThrowUI owns onboarding, settings, calibration, focused runtime actors, projection preparation, and the shared renderer.
  • AirAndSpaceRuntime owns one aircraft poller, semantic Flights frames, route enrichment, and consecutive-sample motion state.
  • ProjectionExperienceCoordinator owns selection, prewarming, rotation, readiness deadlines, and per-experience health.
  • Coordinator commands use one ordered async stream. Concurrent callers cannot overwrite an unhandled command.
  • Prepared frames carry an activation generation. A switch accepts only the exact activation that produced the prepared frame.
  • Lifecycle generations reject superseded Air & Space work after every actor suspension point.
  • Rendered visible counts update the runtime directly and only for the generation that produced the rendered frame.
  • Monotonic playlist revisions reject configuration tasks that reach the coordinator out of logical order.
  • Aircraft source selection is one enum. Its cases carry only the configuration and credential role that the source supports.
  • Layer IDs, mark IDs, line-style IDs, and layer payload kinds are closed types. Unsupported combinations fail at compile time.
  • Type erasure exists only at the layer catalog boundary. Production callers cannot construct raw erased layer frames.
  • Projected frames contain ordered mark and line layers. Static geography uses a bounded cache outside the 30 Hz mark path.
  • Version-two preferences separate global, playlist, and Air & Space state while preserving version-one installs and Keychain identifiers.
  • Bumper rules enforce runtime construction, session construction, live dependency ownership, and the layer-erasure boundary.

Design decisions

  • Throw uses compile-time View and layer catalogs. It does not add runtime plugins or an AnyView boundary.
  • Offline geography replaces Apple Maps and map tiles. It sends no observer location to a map provider.
  • The selected Map center is independent from the observer and persists by coarse observer region.
  • FR24 antimeridian polls use two atomic requests. Polar bounds use conservative spherical caps and outward edge rounding.
  • Activity cues are ambient estimates. They do not claim exact touchdown or liftoff timing.

Compatibility

  • Throw uses a separate persistence domain. This change does not modify Where data, backups, or CloudKit data.
  • Version-one Throw preferences migrate to one configured Air & Space View without repeating onboarding.
  • Existing RapidAPI and FR24 Keychain identifiers remain unchanged.
  • Invalid preferences enter setup repair. Missing credentials never trigger provider fallback.

Review focus

  • Review shared runtime ownership across controller, full-screen, Preview, and external-display scenes.
  • Review source cancellation, lifecycle generations, playlist revisions, readiness deadlines, and frame exchange at black.
  • Review Map, ECEF, ENU, antimeridian, polar, calibration, prediction, and correction geometry.
  • Review provider decoding, request accounting, credential handling, redacted logging, and failure classification.
  • Review generated geography, airport, runway, aircraft-type, attribution, and provenance resources.
  • Review the compile-time layer and source invariants and their architecture tripwires.

Rollout and follow-ups

  • This PR targets a personal beta.
  • Throw/TODOs.md tracks remaining projector, Apple TV, provider, dense-traffic, quiet-hours, and overnight acceptance work.
  • Revalidate the iOS 27 scene-accessory path against the GM SDK before release.
  • Recheck provider prices, quotas, endpoints, and terms before release.
  • Public distribution requires the applicable provider authorization and credential architecture.

Testing

  • ./ide --no-open — passed.
  • ./test ThrowUITests — 101 tests passed.
  • ./test ThrowCoreTests — 235 tests passed.
  • ./test --everything --review — 2,328 iOS unit tests and all 58 shared snapshot suites passed.
  • ./test SnapshotKitTestingTests --only 'SnapshotKitTestingTests/SnapshotReferenceDiffTests' — 11 tests passed.
  • Throw snapshot review — all 73 captures match their reviewed references byte for byte.
  • Architecture validation — passed with the new Throw ownership and erasure rules.
  • ./swiftformat --lint, ./sf-symbols --lint, ./xcstrings --lint, ./shellcheck, and ./attribution --check — passed.
  • git diff --check — passed.

Add the iOS 26 Throw app, typed projection and layer foundations, explicit ADS-B providers including user-owned RapidAPI credentials, shared external-display runtime, controller experience, and deterministic test coverage.
guard externalDisplayRegistration == nil else { return }

let configuration = Self.externalDisplayConfiguration()
let accessory = UISceneAccessory.externalNonInteractive(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posted by an AI agent on kve’s behalf.

Review focus: this registration path compiles against the installed iOS 27 beta SDK, but the final UISceneAccessory lifecycle contract still needs GM-SDK and physical connect/disconnect plus controller-window recreation validation. That release gate is recorded in Throw/TODOs.md.

kyleve added 17 commits August 24, 2026 19:56
Treat the always-available on-device output as ready when selected, while retaining preview-opened state as informational feedback. Show the full-screen Exit control on entry, keep it available to VoiceOver, and cover both behaviors with unit and image snapshots.
Add a macOS command that builds, signs, installs, and launches Throw on an exact paired iPhone or iPad. Preserve the Where installer safety contract with shared fake-device coverage, dependency-free help, a non-mutating dry run, and documented signing setup.
Keep the on-device projection escape control in the tree instead of auto-hiding it behind an undiscoverable tap target. Use a large persistent button and the standard accessibility escape action.
Avoid the iOS 27 Label-inside-LabeledContent layout bug that greedily expands a wrapped status value. Mirror the established device-status workaround with an explicit HStack and a decorative accessibility-hidden symbol.
Commit Xcode’s extracted CFBundleName entry alongside the manually authored Throw Info.plist strings so catalog extraction remains stable.
Replace the coarse v1 map with a deterministic v2 archive generated from pinned Natural Earth 1:10m and 2025 Census boundary and primary-road sources.

Add typed wide, standard, and local detail tiers; source provenance; bounded path densification; cancel-safe shared loading; and category-specific dim rendering behind aircraft. Update documentation, generator coverage, Core/UI tests, and reviewed snapshots.
Replace the tiny aircraft and hairline arc with a larger aircraft and bold projection ribbon. Keep the opaque black field, warm-white mark, and safe margins for small Home Screen sizes.
Render projection labels in a smaller monospaced style at reduced luminance so aircraft marks remain the primary visual signal. Update projection snapshots and guard the styling relationship with a focused unit test.
@kyleve kyleve changed the title feat(Throw): add ceiling flight projection app [NO-CI] feat(Throw): add ceiling flight projection app Aug 25, 2026
kyleve added 10 commits August 25, 2026 13:25
Add FR24 position and inline route decoding, device-only credential settings, cadence controls, onboarding, dashboard status, and provider-specific failure handling. Normalize route availability across providers so unresolved aircraft dim consistently, and filter FR24 ground positions through the shared ground-aircraft policy. Refine projection label prominence and document flight-activity cues in Projection Appearance.
kyleve added 30 commits August 31, 2026 18:02
Ship Throw with a valid privacy manifest declaring CA92.1 for its app-only preferences. Verify the exact declaration in the built Throw.app so resource wiring regressions fail the app test bundle.
Bind each SwiftUI controller root to its UIWindowScene and deliver typed foreground, background, and disconnect events to the process runtime. The runtime owns the foreground controller-scene identity set and exposes only its aggregate presence to ThrowSession, keeping external-display demand separate. Guard against restoring application-level lifecycle callbacks and cover two-scene interleavings deterministically.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant