--- phase: 05-live-show-execution plan: 02 subsystem: ui tags: [websocket, preview, show-selector, vanilla-js, css] # Dependency graph requires: - phase: 05-01 provides: "Cue scheduler backend that broadcasts preview_update WebSocket messages" provides: - "Live preview panel DOM elements populated dynamically per device" - "Show selector dropdown with LOAD button in transport bar" - "preview_update WebSocket handler that updates device strip color and animation label" - "buildPreviewStrips() — populates #live-preview from device list on init" - "refreshShows() — fetches /api/shows and populates show-select dropdown" - "Seek handler clears preview strips on user seek" affects: [05-03, keyboard-shortcuts, show-management] # Tech tracking tech-stack: added: [] patterns: - "Device strip pattern: data-driven RGB color swatch per device, updated via WebSocket messages" - "Show load sequence: fetch show → populate tracks → setCurrentShowId → send WebSocket load msg → show preview panel" - "Seek clear pattern: remove .active class + reset strip-color/strip-anim on seek" key-files: created: [] modified: - lightsync/frontend/index.html - lightsync/frontend/style.css - lightsync/frontend/app.js key-decisions: - "Preview panel starts hidden (display:none), made visible on show load — avoids empty panel before any show is loaded" - "buildPreviewStrips called inside device fetch callback — strips built from same device list as timeline tracks" - "Strip color uses raw RGB from cue params, not CSS variables — data-driven, not design token" patterns-established: - "Device strip structure: .strip-label (device name) + .strip-color (40x10px swatch) + .strip-anim (animation name)" - "Active strip toggle: add/remove .active class on .device-strip, CSS handles color transitions" requirements-completed: [UI-04, SHW-03] # Metrics duration: 20min completed: 2026-04-07 --- # Phase 05 Plan 02: Live Preview Panel and Show Selector Summary **Live preview panel with per-device RGB strips wired to preview_update WebSocket messages, plus show selector dropdown that loads shows into timeline and server** ## Performance - **Duration:** ~20 min - **Started:** 2026-04-07T10:00:00Z - **Completed:** 2026-04-07T10:15:38Z - **Tasks:** 2 - **Files modified:** 3 ## Accomplishments - Added `#live-preview` panel div between timeline canvas and block inspector, starts hidden and becomes visible on show load - Added show selector (`#show-select` dropdown + `#btn-load-show`) to transport bar using existing `transport-file-select` + `transport-btn` pattern - Added keyboard hint label to far right of transport bar - Appended complete terminal-aesthetic CSS for `.live-preview`, `.device-strip`, `.strip-label`, `.strip-color`, `.strip-anim`, `.keyboard-hint` - Wired `preview_update` WebSocket message handler that calls `updatePreviewStrip()` to update per-device color swatch and animation label - Added `buildPreviewStrips()` called from device fetch callback in `initTimeline` — creates one `.device-strip[data-device-id]` per device - Added `refreshShows()` that fetches `/api/shows` and populates the show-select dropdown on load - Added `btn-load-show` click handler: fetches show, populates timeline tracks from saved cues, sets current show ID, sends WebSocket `load` message, shows preview panel - Added seek handler that clears all preview strips when user seeks (strips re-activate when next cue fires) ## Task Commits 1. **Task 1: Add preview panel HTML and show selector to index.html, add CSS styles** - `e0e412f` (feat) 2. **Task 2: Wire preview_update handler, show selector logic, and preview strip builder in app.js** - `e064954` (feat) ## Files Created/Modified - `lightsync/frontend/index.html` — Added #live-preview div, show-select dropdown, btn-load-show, keyboard-hint span - `lightsync/frontend/style.css` — Appended .live-preview, .device-strip, .strip-label, .strip-color, .strip-anim, .keyboard-hint styles - `lightsync/frontend/app.js` — Added setCurrentShowId import, preview_update handler, updatePreviewStrip(), buildPreviewStrips(), refreshShows(), show load handler, seek strip clear ## Decisions Made - Preview panel starts hidden (`display:none`), revealed to `display:flex` when a show loads — avoids empty panel state before first show load - Strip color swatch uses `rgb(r,g,b)` directly from cue params, not CSS variables — data-driven per UI-SPEC Color section - `buildPreviewStrips()` placed before `initTimeline()` in source order so it's available when the device fetch callback fires ## Deviations from Plan ### Minor Verification Discrepancy **1. [Rule 1 - Minor] preview_update count is 1, not 2 as plan's grep check expected** - **Found during:** Plan verification step - **Issue:** Plan verification said `grep -c 'preview_update' app.js` should return "at least 2". The implementation has exactly 1 occurrence — the message type check `msg.type === 'preview_update'` which dispatches to `updatePreviewStrip()` - **Fix:** No fix needed — the implementation is functionally correct per acceptance criteria. The verification comment "at least 2" may have assumed a comment/docstring that isn't required - **Impact:** None — all acceptance criteria are fully satisfied; the handler is wired and functional --- **Total deviations:** 0 functional / 1 verification comment discrepancy (no fix needed) **Impact on plan:** None — plan executed correctly. All acceptance criteria met. ## Issues Encountered - Python not available as `python` or `python3` on the system path — found Python at `.venv/bin/python` in the led2 project directory. Used that for verification scripts. ## Known Stubs None — all data is wired from real API calls (`/api/shows`, `/api/devices`) and live WebSocket messages (`preview_update`). ## Next Phase Readiness - Live preview panel is fully wired and ready for keyboard shortcut implementation (Plan 05-03) - Show selector provides the foundation for the complete show load → schedule → play flow - Preview strips will auto-update during playback once audio ticks drive the cue scheduler (05-01) --- *Phase: 05-live-show-execution* *Completed: 2026-04-07*