Flight Deck & Overlay Composer
Last Updated: 2026-07-21
This page covers the Flight Deck — the browsable console for a single flight — and the overlay composer, the workbench surface where an owner turns an on-board video into a burned-in telemetry clip. It is the UI half of the render pipeline; the model it drives is the overlay pipeline, and the jobs it starts are tracked by render jobs.
Route & server actions
The workbench flight route lives at
apps/web/src/app/workbench/flights/[id]/page.tsx. Today that file is a thin
redirect that survives only to honour old links — it resolves the flight's model,
checks ownership, and forwards to the canonical rocket-scoped flight page
(/workbench/rockets/{modelId}/flights/{id}), where the Flight Deck actually
renders.
The pipeline mutations are Server Actions in the sibling
overlay-actions.ts ('use server'). They cover the whole upload → proxy →
sync → render flow: startVideoUploadAction / completeVideoUploadAction
(multipart upload), startProxyAction / startSyncAction (create the
RenderJob and, for raw camera files, kick off the proxy pass),
startRenderAction (mark liftoff and start the burn-in), and the reuse-sync
actions (findReusableSyncAction / startReuseSyncRenderAction). Every Lambda
invoke is fire-and-forget (InvocationType: 'Event') — a Vercel function
cannot sit for the ~1–2 minutes a proxy or render takes — so the client polls the
RenderJob node for status instead.
The Deck components
The Deck lives in apps/web/src/components/flights/deck/; the top-level pieces
(most re-exported via deck/index.ts, the rest — phase-presets,
reuse-render-panel, upload-cta — imported from their own module files) are:
FlightDeck— the client shell. It owns a single playheadt(viauseFlightTransport), a single chart registry, the per-source visibility state, and the oneunittoggle, then threads them into the left rail and the 2×2 tile grid so every chart, the stat strip, and the synced video move together.DeckHeader— the flown-configuration editor inputs and the unit toggle, threaded from the route page.DeckRail— the left rail: the flight switcher, source checkboxes, and the phase presets.DeckTiles— the 2×2 chart surface:[1]video + live band,[2]Altitude,[3]Velocity,[4]Attitude (tilt/gyro). It builds its series from the same datasets and<TelemetryChart>primitives the legacy dashboard uses.MediaPanel— the "Media & renders" section below the grid. It branches onsurface: the workbench view shows the Videos list, the overlay composer, and a Renders list (with the self-describing "video × variant × chunk" titles, progress bars, and downloads); the public view shows only finished renders as sign-in-free download rows.VideoTile— tile 1: the clean, gauge-free clip presigned inline, with the live telemetry band beneath it. It owns notof its own — play/seek flow through the transport.OverlayBand— the live telemetry band, drawn with the samebuildOverlayModel→computeLayout→drawOverlayFramepipeline the Lambda burns in; only the playhead source differs (see the overlay pipeline).StatStrip— the flight's summary numbers (max altitude, max velocity, max accel, …).AttitudeTile— the tilt/gyro attitude view.SourceSpecCard— one telemetry source's parsed spec (serial/firmware, deploy settings, sim params), parsed server-side from the source'smeta.PhasePresets(phase-presets) — the rail's Phases section: one row per phase preset, each zooming every deck chart to that range.ReuseRenderPanel(reuse-render-panel) — re-render an already-uploaded, already-synced source with a new style/clip, without re-uploading, re-proxying, or re-scrubbing. Keyed on the source's real identity (its job's server-resolvedsourceKey), never on a freshly-picked local file.UploadCta(upload-cta) — the workbench-only "+ Upload video or telemetry" affordance, gated client-side so a public deck stays cacheable.useFlightTransport(use-flight-transport) — the single source of playback time. It ownst(seconds since detected liftoff) and the play/pause clock, and works identically with a video element or with data alone.
The overlay composer
The composer lives in apps/web/src/components/workbench/overlay/:
OverlayPanel(overlay-panel) — the one-path flow: pick a file → pick a variant + chunk → mark liftoff → render → download. For a browser-decodable container (mp4/mov/webm) the sync wizard scrubs the local file the instant it's picked; for the raw AVI the camera writes, the file goes up first and the Lambda builds a proxy to scrub.SyncWizard(sync-wizard) — scrub the video to mark the liftoff frame. That offset is the only bridge between flight time and source time.VariantChunkPicker(variant-chunk-picker) — choose the(variant, chunk)to render. Each variant option shows a live, client-side preview; only the available named chunks are offered (fullalways, plusboost/apogee/descentwhen the flight's markers yield that phase).useRenderJob(use-render-job) — polls the job for status.useMultipartUpload(use-multipart-upload) — drives the multipart video upload.
The v1 picker emits named chunks only. custom is API-ready but has no
UI (owner decision — see render-selection.ts / phase-markers.ts).
Previews are free; burn-in is not
Every variant preview is client-side and free: it renders from the flight's telemetry alone — no video, no Lambda — using the same overlay model the render uses, so what you pick is what you get. Burn-in happens only when a render is started against a real uploaded video: that is the one path that invokes the Lambda and produces a downloadable clip. The mechanics of "one renderer, three hosts — identical pixels" are on the overlay pipeline page.