Skip to content

UI Editor Page

OneSeventyFour edited this page May 14, 2026 · 1 revision

UI: Editor page (Show Builder)

The biggest single page in the app. Where you actually design a show. Lives in ShowBuilder.jsx and a small village of supporting components under src/components/builder/.

📷 Screenshot placeholder: the Editor with a show open, the timeline mid-screen, the Target Grid sub-tab visible below.

Layout

Top to bottom:

  1. Show State Header — show identity, save controls, top-level metadata.
  2. Audio tracks + waveform — multi-track audio for pyromusicals.
  3. Timeline — drag/drop pyro items in time and to a zone:target.
  4. Sub-tabs — Target Grid, Racks, Test Show Builder, Show Layout.

1. Show State Header

Where you create / pick / rename / save the show.

Controls:

  • + New show — opens a modal. Fill in name and authorization code. Returns a new show row in SQLite.
  • Show selector — dropdown of every saved show. Pick one to load it into the editor.
  • Show name — editable inline. Saved with the next Save.
  • Authorization code — editable inline. The Console requires this to load the show; the Editor requires it (the original one, before edits) to save.
  • Stats panel — duration, item count, max simultaneous concurrency, total cost (sum of inventory.unit_cost × references).
  • Save — pushes a PATCH to /api/shows/[id]. Will prompt for the auth code on first save in this session. Subsequent saves are silent. Auto-save kicks in after the first manual save: every 2 s of inactivity triggers a debounced PATCH if there are unsaved changes.
  • Clear — wipes the timeline (with confirmation).

📷 Screenshot placeholder: the show state header with stats.

2. Audio tracks + waveform

For pyromusical support. Each show can have one or more audio tracks; the Editor uses WaveSurfer.js to render the waveform.

Controls:

  • Track tabs — switch between tracks (e.g. main song + intro / outro).
  • Upload — POSTs a file to /api/shows/upload-audio. The file is stored under public/uploads/audio/, and a reference ({ url, durationSec, ... }) is stored on the show row's audio_file JSON.
  • Play / pause / scrub — preview the audio.
  • BPM tools — auto-detect via analyzeAudioFile (a built-in beat detector) or tap-tempo. Once you have a BPM, snap timeline items to beats.
  • Track offset — move the audio relative to the show start (separate from the per-show audioOffsetMs you'd dial in on the Console).
  • Sequential playback toggle — if you have multiple tracks, play them back-to-back during preview rather than overlapping.

The waveform is the primary alignment surface — drag items below it to align cues with musical hits.

3. Timeline

A horizontal time-axis (in seconds) with rows for each receiver/zone in the show. Items are placed onto rows; their horizontal position = startTime, their length = duration.

📷 Screenshot placeholder: timeline with several items spread across two zone rows.

Adding items

  • Click + Add item at any time on any row → AddItemModal.
  • Pick an inventory item. Set zone, target, optional fuse delay overrides.
  • For shells (with lift delay), the visible item start = startTime, but the actual cue fire time = startTime - lift_delay. The daemon stores the cue time, not the visible time.

Editing items

  • Click an item to open its edit modal. Change inventory reference, zone/target, timing, color overrides, etc.
  • Drag an item horizontally to reposition (snaps to grid; hold a modifier to free-move).
  • Drag vertically to a different zone row.
  • Multi-select with Cmd/Ctrl-click → Chain timing modal lets you align delays between selected items.
  • Copy item — pick a source item, then click anywhere on the timeline to drop a clone (zone/target asked in a modal first).

Item types

The major timeline item types:

Type What it represents Special editing
SHELL Single aerial shell. Just cue + visual time.
CAKE_FOUNTAIN / CAKE_200G / CAKE_500G Multi-shot cake. Pulls shot timestamps from the cake's firing profile (if any).
FUSED_LINE A chain of items on the same cue, fired in sequence by physical fuse. Built in FusedItemLineBuilderModal — pick items, their delays, their relative times.
FUSED_SHELL_LINE A chain of shells on the same cue, fused. FusedLineBuilderModal — pick shells, lead-in inches, inter-shell delays.
RACK_SHELLS One cue lights a fuse that propagates through cells of a rack. Built from a rack defined in the Racks sub-tab; uses RackShellsSelector to pick fired cells.

For cakes and fused lines, the firing profile (from YouTube analysis) lets the timeline visualize each individual shot inside the multi-shot item — useful for music alignment. See Inventory page.

What's stored

Each item ends up in Show.display_payload as an object with this shape (compressed):

{
  "id": "uuid",
  "type": "SHELL",
  "startTime": 12.5,
  "duration": 0.6,
  "delay": 1.5,
  "zone": "RX146",
  "target": 1,
  "inventoryId": 42,
  "color": "red"
}

(Plus type-specific fields like rackId, rackCells, fireableItem for racks; chainItems for fused lines.)

The daemon's process_display_payload later transforms each item into a firing-array entry: { startTime: startTime - delay, zone, target, id }. So the visible item's left edge in the timeline = the visual effect time; the daemon fires earlier by delay to make the visual happen on time.

4. Sub-tabs

Below the timeline are four sub-tabs.

4a. Target Grid

A 2D editor showing each receiver in the show as a row, with a column per cue. Click a cell to add/remove an item from that cue. Drag items between cells to reassign cues.

  • Add receiver opens ShowReceiverModal: pick a receiver from the global list, give it a label, mark which cues it controls in this show.
  • Edit receiver changes label or cue count.
  • Remove receiver is gated — refuses if there are items still pointing at this receiver.
  • Migrate zones — for renaming or restructuring; updates every item's zone:target in one go.

The Target Grid is the canonical target view — Show.show_receivers JSON column. The Receivers page in the global nav is a different concept (the global Receivers SQL table — what receivers physically exist).

4b. Racks

Per-show rack design. See RacksTab.jsx.

  • + Rack — give the rack a name and dimensions (X rows × Y rows, plus spacings in inches).
  • Rack tabs — multiple racks can exist per show.
  • ResizeRackResizeModal with a destructive-changes preview (it'll tell you which cells / fuses / RACK_SHELLS items will be invalidated).
  • Clone / Delete — copy or remove the rack.

Inside a rack, the RackGrid.jsx editor lets you:

  • Shells mode — assign an inventory shell to each cell. Click a cell, pick from the shell list, optionally pick which "shell number" of a shell pack it is.
  • Fusing mode — draw fuse chains. Pick a fuse type from inventory, set the lead-in (inches from ignition source to first cell), then click cells in the order the fuse propagates.

Once a rack has cells with shells and (optionally) fuses, you can reference it from the timeline as a RACK_SHELLS item — one cue ignites the rack's lead-in, and the fuse propagates to fire each shell at calculated delays from the rack geometry and fuse burn rate.

4c. Test Show Builder

Synthetic test patterns for confirming hardware works. Generates timed sequences across all assigned cues for things like:

  • Cycle-test (fire each cue once in sequence)
  • All-at-once (worst-case concurrency stress test)
  • Custom interval

Useful for shop testing without writing a "real" show.

4d. Show Layout

Satellite-map view (SpatialLayoutMap.jsx using Leaflet + Esri imagery). Place each receiver pin geographically, draw safety rings around them (computed from product metadata.safety_distance_ft if present), and view the inter-receiver distances.

  • Address Search (AddressSearch.jsx) — geocode an address via Nominatim, or paste a lat,lng, or use the browser's geolocation API.
  • Drag pins to position receivers.
  • Save Locations persists receiver_locations JSON on the show row.

This data is also used by the Loadout page to print a packing map.

Save and authorization

The first save of a session prompts for the show's authorization code. The auth code prevents accidental edits to the wrong show — you have to know it. After a successful save, the editor caches the code in memory and saves silently (auto-save) until you reload the page.

If you forget the code, you have to either dig it out of the SQL row directly or delete the show.

Implementation files

src/components/builder/
├── ShowBuilder.jsx                    # everything binds here
├── ShowStateHeader.jsx                # header + stats
├── Timeline.jsx                       # the timeline (also used in Console)
├── AudioWaveform.jsx                  # WaveSurfer integration
├── AudioTrackTabs.jsx                 # multi-track UI
├── TrackPlayer.jsx                    # play/pause for a track
├── AddItemModal.jsx
├── ChainTimingModal.jsx               # multi-select alignment
├── CopyItemModal.jsx
├── RacksTab.jsx
├── RackGrid.jsx
├── RackResizeModal.jsx
├── RackShellsSelector.jsx             # pick a fireable rack region
├── FuseModal.jsx                      # pick fuse type + lead-in
├── FusedLineBuilderModal.jsx          # FUSED_SHELL_LINE
├── FusedItemLineBuilderModal.jsx      # FUSED_LINE
├── CellShellSelector.jsx
├── ShowTargetGrid.jsx
├── ShowReceiverModal.jsx
├── SpatialLayoutMap.jsx
├── SatelliteMap.jsx                   # Leaflet wrapper
├── AddressSearch.jsx                  # Nominatim
└── ItemPreview.jsx

Clone this wiki locally