252 lines
14 KiB
Markdown
252 lines
14 KiB
Markdown
---
|
|
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"
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/01-foundation/01-CONTEXT.md
|
|
@.planning/phases/01-foundation/01-RESEARCH.md
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Create HTML structure and CSS terminal aesthetic</name>
|
|
<files>
|
|
lightsync/frontend/index.html
|
|
lightsync/frontend/style.css
|
|
</files>
|
|
<read_first>
|
|
.planning/phases/01-foundation/01-CONTEXT.md
|
|
.planning/phases/01-foundation/01-RESEARCH.md
|
|
</read_first>
|
|
<action>
|
|
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).
|
|
</action>
|
|
<verify>
|
|
<automated>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</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- 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"
|
|
</acceptance_criteria>
|
|
<done>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.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Create JavaScript — WebSocket client, device panel CRUD, API helpers</name>
|
|
<files>
|
|
lightsync/frontend/app.js
|
|
</files>
|
|
<read_first>
|
|
lightsync/frontend/index.html
|
|
.planning/phases/01-foundation/01-RESEARCH.md
|
|
</read_first>
|
|
<action>
|
|
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); }
|
|
</action>
|
|
<verify>
|
|
<automated>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</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- 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
|
|
</acceptance_criteria>
|
|
<done>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.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
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
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- 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
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`
|
|
</output>
|