--- 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 `