--- phase: 01-foundation plan: 02 type: execute wave: 1 depends_on: [] files_modified: - lightsync/frontend/index.html - lightsync/frontend/style.css - lightsync/frontend/app.js autonomous: true requirements: - UI-01 - UI-02 - DEV-03 must_haves: truths: - "The web app loads in a browser showing the full DAW skeleton layout" - "The UI has terminal/hacker aesthetic — dark background, monospace fonts, cyan accents, no generic SaaS look" - "All final panels are visible: DEVICES, ANIMATIONS, TIMELINE, TRANSPORT — most as labeled placeholders" - "The DEVICES panel has an add-device form and a device list area" - "Panel headers are uppercase and borders are dimmed cyan — matching terminal window pane style" artifacts: - path: "lightsync/frontend/index.html" provides: "Full DAW skeleton HTML structure with all panels" contains: "LIGHTSYNC" - path: "lightsync/frontend/style.css" provides: "Terminal aesthetic CSS — dark theme, monospace, cyan accents" contains: "--accent" - path: "lightsync/frontend/app.js" provides: "WebSocket client stub, device panel interactions, API fetch helpers" contains: "new WebSocket" key_links: - from: "lightsync/frontend/app.js" to: "/ws" via: "WebSocket connection for future position broadcasts" pattern: "new WebSocket.*ws" - from: "lightsync/frontend/app.js" to: "/api/devices" via: "fetch calls for device CRUD" pattern: "fetch.*api/devices" - from: "lightsync/frontend/index.html" to: "lightsync/frontend/style.css" via: "stylesheet link" pattern: "style.css" --- Build the complete terminal-aesthetic frontend shell: HTML with full DAW skeleton layout (all panels), CSS with dark theme / monospace / cyan accents, and JavaScript with WebSocket stub and device panel interactivity. Purpose: Establishes the visual identity and layout structure that all future phases will fill in. The user sees the final app skeleton from day one. Output: Three files in lightsync/frontend/ — index.html, style.css, app.js — that render the full DAW interface. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/01-foundation/01-CONTEXT.md @.planning/phases/01-foundation/01-RESEARCH.md Task 1: Create HTML structure and CSS terminal aesthetic lightsync/frontend/index.html lightsync/frontend/style.css .planning/phases/01-foundation/01-CONTEXT.md .planning/phases/01-foundation/01-RESEARCH.md Create lightsync/frontend/ directory. Create lightsync/frontend/index.html with this structure (per D-06 through D-10): DOCTYPE html, lang="en", meta charset UTF-8, viewport meta, title "LIGHTSYNC". Link to style.css, defer script app.js. Body layout using CSS Grid (per D-06 — full DAW skeleton from Phase 1): HEADER (per D-07): - div.header containing: - ASCII art or stylized text "LIGHTSYNC" in a pre or span element (use simple ASCII block letters or styled text — not an image) - span.status-indicator with id="status" text "OFFLINE" (will toggle to "CONNECTED" via JS) - span.show-name with id="show-name" text "No show loaded" LEFT SIDEBAR (per D-08): - div.sidebar containing two panels stacked vertically: - section.panel#devices-panel: - h2.panel-header text "DEVICES" - div#device-list (empty, populated by JS) - form#add-device-form with inputs: name (text), strip_type (select: sk6812, ws2801, generic), led_count (number, min=1, max=1000), ip (text), port (number, min=1, max=65535), and a submit button text "+ ADD DEVICE" - section.panel#animations-panel: - h2.panel-header text "ANIMATIONS" - div.placeholder-content text "[ Phase 3+ ]" MAIN CENTER (per D-09): - div.main containing: - section.panel#timeline-panel: - h2.panel-header text "TIMELINE" - div.placeholder-content text "[ TIMELINE -- Phase 2+ ]" BOTTOM BAR (per D-10): - div.transport containing: - section.panel#transport-panel: - h2.panel-header text "TRANSPORT" - div.placeholder-content text "[ TRANSPORT -- Phase 2+ ]" Create lightsync/frontend/style.css with terminal aesthetic (per D-11 through D-15): CSS custom properties on :root: - --bg: #0a0a0a (per D-12) - --bg-panel: #111111 - --accent: #00ffff (per D-11) - --accent-dim: #004444 (dimmed cyan for borders, per D-13) - --text: #cccccc - --text-bright: #ffffff - --text-dim: #666666 - --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace (per D-12) @import url for JetBrains Mono from Google Fonts (weight 400,700). *, *::before, *::after: box-sizing border-box, margin 0, padding 0. html, body: height 100%, background var(--bg), color var(--text), font-family var(--font-mono), font-size 14px, overflow hidden. Body grid layout: grid-template-areas "header header header" / "sidebar main main" / "sidebar main main" / "transport transport transport". grid-template-columns: 280px 1fr. grid-template-rows: auto 1fr auto. .header: grid-area header, display flex, align-items center, gap 1rem, padding 8px 16px, border-bottom 1px solid var(--accent-dim), background var(--bg-panel). .header pre (for ASCII title): color var(--accent), font-size 10px, line-height 1.1, white-space pre. .status-indicator: font-size 11px, text-transform uppercase, padding 2px 8px, border 1px solid var(--accent-dim). When connected: color var(--accent). Default (offline): color #ff4444. .show-name: margin-left auto, font-size 12px, color var(--text-dim). .sidebar: grid-area sidebar, display flex, flex-direction column, gap 0, border-right 1px solid var(--accent-dim), overflow-y auto. .main: grid-area main, display flex, flex-direction column, overflow hidden, padding 8px. .transport: grid-area transport, border-top 1px solid var(--accent-dim). .panel: background var(--bg-panel), border 1px solid var(--accent-dim) (per D-13), padding 0, margin 0. No border-radius (per D-15). No box-shadow (per D-15). .panel-header: text-transform uppercase (per D-14), font-size 11px, letter-spacing 2px, color var(--accent), padding 8px 12px, border-bottom 1px solid var(--accent-dim), font-weight 700. .placeholder-content: display flex, align-items center, justify-content center, min-height 200px, color var(--text-dim), font-size 13px, letter-spacing 1px. #devices-panel: flex 1. #animations-panel: flex 0 0 auto, border-top 1px solid var(--accent-dim). #timeline-panel: flex 1, display flex, flex-direction column. The .placeholder-content inside: flex 1. #transport-panel: padding 0. #transport-panel .placeholder-content: min-height 48px. #device-list: padding 8px 12px. Each .device-item: display flex, justify-content space-between, align-items center, padding 4px 0, border-bottom 1px solid var(--accent-dim), font-size 12px. .device-item .device-name: color var(--text-bright). .device-item .device-meta: color var(--text-dim), font-size 11px. .device-item .device-remove: background none, border none, color #ff4444, cursor pointer, font-family var(--font-mono), font-size 14px. #add-device-form: padding 8px 12px, display flex, flex-direction column, gap 6px. #add-device-form input, #add-device-form select: background var(--bg), border 1px solid var(--accent-dim), color var(--text), font-family var(--font-mono), font-size 12px, padding 4px 8px, outline none. Focus: border-color var(--accent). #add-device-form button: background var(--accent-dim), border 1px solid var(--accent), color var(--accent), font-family var(--font-mono), font-size 12px, padding 6px, cursor pointer, text-transform uppercase, letter-spacing 1px. Hover: background var(--accent), color var(--bg). Scrollbar styling for webkit: 6px wide, track var(--bg), thumb var(--accent-dim), thumb hover var(--accent). test -f /home/claude/led2/lightsync/frontend/index.html && test -f /home/claude/led2/lightsync/frontend/style.css && grep -c "LIGHTSYNC" /home/claude/led2/lightsync/frontend/index.html && grep -c "\-\-accent.*#00ffff" /home/claude/led2/lightsync/frontend/style.css && grep -c "JetBrains Mono" /home/claude/led2/lightsync/frontend/style.css - lightsync/frontend/index.html contains "LIGHTSYNC" in header area - lightsync/frontend/index.html contains id="devices-panel" and id="animations-panel" and id="timeline-panel" and id="transport-panel" - lightsync/frontend/index.html contains id="add-device-form" with inputs for name, strip_type, led_count, ip, port - lightsync/frontend/index.html contains id="device-list" - lightsync/frontend/index.html contains "Phase 3+" in animations panel and "Phase 2+" in timeline panel and "Phase 2+" in transport panel - lightsync/frontend/style.css contains `--accent: #00ffff` and `--bg: #0a0a0a` and `--font-mono` - lightsync/frontend/style.css contains `text-transform: uppercase` for panel headers - lightsync/frontend/style.css contains `border-radius` nowhere (per D-15 no rounded corners) OR only `border-radius: 0` - lightsync/frontend/style.css contains `font-family: 'JetBrains Mono'` or references var(--font-mono) on body - lightsync/frontend/style.css contains grid-template-areas with "header" and "sidebar" and "main" and "transport" HTML renders full DAW skeleton. CSS applies terminal aesthetic with dark background, monospace font, cyan accents, flat sharp edges. All panels visible with correct labels. Task 2: Create JavaScript — WebSocket client, device panel CRUD, API helpers lightsync/frontend/app.js lightsync/frontend/index.html .planning/phases/01-foundation/01-RESEARCH.md Create lightsync/frontend/app.js with these modules: 1. API helper functions: - async function apiGet(path): fetch(path), check response.ok, return response.json() - async function apiPost(path, data): fetch(path, {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(data)}), return response.json() - async function apiDelete(path): fetch(path, {method: "DELETE"}) 2. WebSocket client stub: - let ws = null - function connectWebSocket(): create new WebSocket(`ws://${location.host}/ws`), set onopen to update #status text to "CONNECTED" and add .connected class, set onclose to update #status text to "OFFLINE" and remove .connected class, set onmessage to parse JSON and call handleWsMessage(data), set onerror to console.error. Auto-reconnect: onclose sets setTimeout(connectWebSocket, 2000). - function handleWsMessage(msg): switch on msg.type — for now just console.log("WS:", msg). Phase 2 will add position/state handlers. 3. Device panel logic: - async function loadDevices(): call apiGet("/api/devices"), for each device call renderDevice(device), populate #device-list - function renderDevice(device): create div.device-item containing: span.device-name with device.name, span.device-meta with `${device.strip_type} | ${device.led_count} LEDs | ${device.ip}:${device.port}`, button.device-remove with text "x" and onclick calling removeDevice(device.id) - async function removeDevice(id): call apiDelete(`/api/devices/${id}`), then loadDevices() to refresh - Form submit handler on #add-device-form: preventDefault, collect form values (name, strip_type, led_count as int, ip, port as int), call apiPost("/api/devices", data), reset form, call loadDevices() 4. Initialization: - document.addEventListener("DOMContentLoaded", () => { connectWebSocket(); loadDevices(); }) Add .connected CSS class to style.css for the status indicator: .status-indicator.connected { color: var(--accent); border-color: var(--accent); } test -f /home/claude/led2/lightsync/frontend/app.js && grep -c "new WebSocket" /home/claude/led2/lightsync/frontend/app.js && grep -c "api/devices" /home/claude/led2/lightsync/frontend/app.js && grep -c "loadDevices" /home/claude/led2/lightsync/frontend/app.js && grep -c "connectWebSocket" /home/claude/led2/lightsync/frontend/app.js - lightsync/frontend/app.js contains `new WebSocket` connecting to `/ws` - lightsync/frontend/app.js contains `fetch` calls to `/api/devices` for GET, POST, and DELETE - lightsync/frontend/app.js contains `function loadDevices` and `function renderDevice` and `function removeDevice` - lightsync/frontend/app.js contains `DOMContentLoaded` listener calling `connectWebSocket()` and `loadDevices()` - lightsync/frontend/app.js contains `function handleWsMessage` with msg.type switch/check - lightsync/frontend/app.js contains auto-reconnect logic (setTimeout with connectWebSocket) - lightsync/frontend/app.js contains `apiGet` and `apiPost` and `apiDelete` helper functions JavaScript connects WebSocket, displays connection status, loads device list from API, supports adding and removing devices via the form. All interactions use the API helpers. 1. `ls lightsync/frontend/` shows index.html, style.css, app.js 2. index.html contains all four panel IDs: devices-panel, animations-panel, timeline-panel, transport-panel 3. style.css uses --accent: #00ffff and --bg: #0a0a0a 4. app.js connects to /ws and fetches from /api/devices - Opening the app in a browser shows: header with LIGHTSYNC title, left sidebar with DEVICES and ANIMATIONS panels, center TIMELINE placeholder, bottom TRANSPORT placeholder - Dark background (#0a0a0a), monospace fonts, cyan accent color throughout - No rounded corners, drop shadows, or gradients anywhere - Device add form is functional (sends POST to /api/devices) - WebSocket connects and shows CONNECTED status After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`