From ab162fb244174373fa7dd28340b27931e6800373 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 20:01:48 +0000 Subject: [PATCH] =?UTF-8?q?docs(01):=20create=20gap=20closure=20plan=2001-?= =?UTF-8?q?04=20=E2=80=94=20device=20CRUD=20UI=20+=20CSS=20variable=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .planning/ROADMAP.md | 7 +- .planning/phases/01-foundation/01-04-PLAN.md | 354 +++++++++++++++++++ 2 files changed, 358 insertions(+), 3 deletions(-) create mode 100644 .planning/phases/01-foundation/01-04-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 1abf332..b82dd16 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -12,7 +12,7 @@ LightSync is built bottom-up: data model first, audio engine second, UDP protoco Decimal phases appear between their surrounding integers in numeric order. -- [x] **Phase 1: Foundation** - FastAPI skeleton, show file schema (Pydantic), device registry, terminal-aesthetic frontend shell (completed 2026-04-05) +- [ ] **Phase 1: Foundation** - FastAPI skeleton, show file schema (Pydantic), device registry, terminal-aesthetic frontend shell (gap closure in progress) - [ ] **Phase 2: Audio Engine** - MPV IPC bridge, waveform rendering, playback controls, Windows timer fix - [ ] **Phase 3: Communication Protocol** - UDP packet spec, DRGB/DNRGB/DRGBW encoders, animation library, software simulator - [ ] **Phase 4: Timeline Editor** - DAW UI (tracks, drag-place blocks, resize, delete), undo/redo, beat detection, snap-to-beat @@ -32,12 +32,13 @@ Decimal phases appear between their surrounding integers in numeric order. 3. The web app loads in a browser with terminal/hacker aesthetic — dark background, monospace fonts, no generic SaaS look 4. FastAPI serves the frontend and exposes REST stubs for shows and devices 5. Adding a new strip type requires only a new device class — no changes to core engine code -**Plans**: 3 plans +**Plans**: 4 plans Plans: - [x] 01-01: Backend scaffold — FastAPI app, Pydantic show/device models, show store (JSON load/save), device registry - [x] 01-02: Frontend shell — HTML/CSS terminal aesthetic, WebSocket client stub, device panel, show panel - [x] 01-03: REST API — CRUD endpoints for devices and shows, static file serving, integration smoke test +- [ ] 01-04: Gap closure — device CRUD UI (add form, remove button), CSS variable naming fix, panel IDs **UI hint**: yes ### Phase 2: Audio Engine @@ -151,7 +152,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| -| 1. Foundation | 3/3 | Complete | 2026-04-05 | +| 1. Foundation | 3/4 | Gap closure | - | | 2. Audio Engine | 0/3 | Not started | - | | 3. Communication Protocol | 0/3 | Not started | - | | 4. Timeline Editor | 0/4 | Not started | - | diff --git a/.planning/phases/01-foundation/01-04-PLAN.md b/.planning/phases/01-foundation/01-04-PLAN.md new file mode 100644 index 0000000..8e7ad2d --- /dev/null +++ b/.planning/phases/01-foundation/01-04-PLAN.md @@ -0,0 +1,354 @@ +--- +phase: 01-foundation +plan: 04 +type: execute +wave: 3 +depends_on: + - 01-03 +files_modified: + - lightsync/frontend/index.html + - lightsync/frontend/style.css + - lightsync/frontend/app.js +autonomous: true +requirements: + - DEV-03 + - UI-02 +gap_closure: true + +must_haves: + truths: + - "The DEVICES panel has an add-device form with inputs for name, strip_type, led_count, ip, port and a submit button" + - "Submitting the add-device form creates a device via POST /api/devices and the new device appears in the list" + - "Each device row has a remove button that calls DELETE /api/devices/{id} and refreshes the list" + - "CSS uses --accent: #00ffff as the canonical accent variable name" + - "index.html has panel IDs: devices-panel, animations-panel, timeline-panel, transport-panel" + artifacts: + - path: "lightsync/frontend/index.html" + provides: "Add-device form and panel IDs" + contains: "add-device-form" + - path: "lightsync/frontend/app.js" + provides: "Device CRUD functions: apiPost, apiDelete, removeDevice, form handler" + contains: "apiPost" + - path: "lightsync/frontend/style.css" + provides: "Canonical --accent CSS variable" + contains: "--accent: #00ffff" + key_links: + - from: "lightsync/frontend/app.js" + to: "/api/devices" + via: "apiPost for form submit, apiDelete for remove button" + pattern: "apiPost.*api/devices" + - from: "lightsync/frontend/index.html" + to: "lightsync/frontend/app.js" + via: "form#add-device-form submit event listener" + pattern: "add-device-form" +--- + + +Close two verification gaps from 01-VERIFICATION.md: (1) add device CRUD UI — form, POST, DELETE, remove buttons; (2) fix CSS variable naming to use --accent and add missing panel IDs to index.html. + +Purpose: Completes DEV-03 (device list with UI interaction) and UI-02 (CSS contract alignment with plan spec). +Output: Updated index.html, app.js, and style.css with full device management UI and corrected CSS variable naming. + + + +@$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-VERIFICATION.md + + + + + + Task 1: Add device CRUD UI — form in index.html, POST/DELETE/remove in app.js + + lightsync/frontend/index.html + lightsync/frontend/app.js + + + lightsync/frontend/index.html + lightsync/frontend/app.js + lightsync/api/devices.py + + + **index.html changes:** + + In the sidebar, the first panel (DEVICES panel, currently lines 23-28) must get an id and have the add-device form inserted after the device-list div. Replace the current DEVICES panel block: + + ```html +
+
DEVICES
+
+ loading... +
+
+ + + + + + +
+
+ ``` + + The form goes AFTER the panel-content div but still inside the panel div, so it sits at the bottom of the devices panel. The form class is "device-form" (styles added in Task 2). + + **app.js changes:** + + Add two API helper functions before the LightSyncClient class (near the top, after the WS_URL const): + + ```javascript + async function apiPost(path, data) { + const res = await fetch(path, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data), + }); + if (!res.ok) throw new Error(`POST ${path}: HTTP ${res.status}`); + return res.json(); + } + + async function apiDelete(path) { + const res = await fetch(path, { method: "DELETE" }); + if (!res.ok && res.status !== 204) throw new Error(`DELETE ${path}: HTTP ${res.status}`); + } + ``` + + Modify the `loadDevices()` function: change the device row rendering to include a remove button. Replace the current `list.innerHTML = devices.map(...)` block with: + + ```javascript + list.innerHTML = devices.map(d => + `
+
+ ${escapeHtml(d.name)} + ${escapeHtml(d.strip_type)} / ${d.led_count} LEDs / ${escapeHtml(d.ip)}:${d.port} +
+ +
` + ).join(""); + ``` + + Add the `removeDevice` function (after `loadDevices`): + + ```javascript + async function removeDevice(id) { + try { + await apiDelete(`/api/devices/${id}`); + await loadDevices(); + } catch (err) { + console.error("[devices] remove failed:", err); + } + } + ``` + + Add a form submit handler. After the `loadDevices()` call at the bottom of the file, add: + + ```javascript + document.getElementById("add-device-form").addEventListener("submit", async (e) => { + e.preventDefault(); + const form = e.target; + const data = { + name: form.name.value.trim(), + strip_type: form.strip_type.value, + led_count: parseInt(form.led_count.value, 10), + ip: form.ip.value.trim(), + port: parseInt(form.port.value, 10), + }; + try { + await apiPost("/api/devices", data); + form.reset(); + form.port.value = "21324"; + await loadDevices(); + } catch (err) { + console.error("[devices] add failed:", err); + } + }); + ``` + + Note: Since app.js uses `type="module"`, the `removeDevice` function must be exported to global scope. Add this line after the function definition: + ```javascript + window.removeDevice = removeDevice; + ``` +
+ + cd /home/claude/led2 && grep -c "add-device-form" lightsync/frontend/index.html && grep -c "apiPost" lightsync/frontend/app.js && grep -c "apiDelete" lightsync/frontend/app.js && grep -c "removeDevice" lightsync/frontend/app.js && grep -c "device-remove" lightsync/frontend/index.html + + + - lightsync/frontend/index.html contains `id="add-device-form"` with a form element + - lightsync/frontend/index.html contains `name="name"` input, `name="strip_type"` select with options sk6812/ws2801/generic, `name="led_count"` input, `name="ip"` input, `name="port"` input + - lightsync/frontend/index.html contains `type="submit"` button with text "+ ADD DEVICE" + - lightsync/frontend/index.html contains `class="device-remove"` button with onclick calling removeDevice + - lightsync/frontend/app.js contains `async function apiPost(path, data)` with `method: "POST"` and `Content-Type: application/json` + - lightsync/frontend/app.js contains `async function apiDelete(path)` with `method: "DELETE"` + - lightsync/frontend/app.js contains `async function removeDevice(id)` that calls `apiDelete` with `/api/devices/${id}` + - lightsync/frontend/app.js contains `addEventListener("submit"` on `add-device-form` that calls `apiPost("/api/devices", data)` + - lightsync/frontend/app.js contains `window.removeDevice = removeDevice` to expose function globally for onclick handler + + The DEVICES panel has a working add-device form (name, strip_type select, led_count, ip, port, submit button). Each device row has a remove button. Form submit calls POST /api/devices, remove calls DELETE /api/devices/{id}, both refresh the device list. +
+ + + Task 2: Fix CSS variable naming to --accent and add panel IDs to index.html + + lightsync/frontend/style.css + lightsync/frontend/index.html + + + lightsync/frontend/style.css + lightsync/frontend/index.html + + + **style.css changes:** + + In the `:root` block (line 1-14), replace the current variable declarations with these renamed variables. The key change is adding `--accent: #00ffff` as the canonical accent variable. Keep `--text-accent` and `--border-bright` as aliases that reference `--accent`: + + Replace lines 1-14 with: + ```css + :root { + --bg-primary: #0a0a0a; + --bg-panel: #0f0f0f; + --bg-panel-dark: #080808; + --border-dim: #1a4a4a; + --accent: #00ffff; + --border-bright: var(--accent); + --text-primary: #cccccc; + --text-dim: #555555; + --text-accent: var(--accent); + --text-warning: #ff6600; + --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace; + --font-size: 13px; + --panel-gap: 1px; + } + ``` + + This makes `--accent` the single source of truth for `#00ffff`. The existing `--text-accent` and `--border-bright` usages throughout the CSS file continue to work because they now reference `--accent`. + + Add CSS rules for the device form and remove button (append after the existing `button:hover` rule at the end of the file): + + ```css + /* Device form */ + .device-form { + display: flex; + flex-direction: column; + gap: 4px; + padding: 8px 12px; + border-top: 1px solid var(--border-dim); + } + + .device-form button[type="submit"] { + margin-top: 4px; + text-transform: uppercase; + letter-spacing: 0.08em; + } + + /* Device remove button */ + .device-remove { + background: none; + border: none; + color: var(--text-dim); + font-family: var(--font-mono); + font-size: 16px; + cursor: pointer; + padding: 0 4px; + line-height: 1; + flex-shrink: 0; + } + + .device-remove:hover { + color: #ff3333; + border: none; + } + + .device-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 6px 0; + border-bottom: 1px solid var(--bg-panel-dark); + gap: 8px; + } + + .device-row-info { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + } + ``` + + Note: The existing `.device-row` rule (lines 143-152) should be replaced by this updated version that adds `align-items: center` and `justify-content: space-between` to accommodate the remove button alongside the info. + + **index.html changes:** + + Add panel IDs to the remaining panels that are missing them. After Task 1, devices-panel already has its ID. The following still need IDs: + + 1. The ANIMATIONS panel (currently line 29 `
`): Add `id="animations-panel"`: + ```html +
+ ``` + + 2. The main-area timeline (currently line 35 `
`): Wrap or add ID to the main element. Add a panel wrapper inside: + ```html +
+
+ [ TIMELINE — PHASE 2+ ] +
+
+ ``` + + 3. The transport-bar footer (currently line 39 `