Files
led2/.planning/phases/01-foundation/01-04-PLAN.md

15 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, gap_closure, must_haves
phase plan type wave depends_on files_modified autonomous requirements gap_closure must_haves
01-foundation 04 execute 3
01-03
lightsync/frontend/index.html
lightsync/frontend/style.css
lightsync/frontend/app.js
true
DEV-03
UI-02
true
truths artifacts key_links
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
path provides contains
lightsync/frontend/index.html Add-device form and panel IDs add-device-form
path provides contains
lightsync/frontend/app.js Device CRUD functions: apiPost, apiDelete, removeDevice, form handler apiPost
path provides contains
lightsync/frontend/style.css Canonical --accent CSS variable --accent: #00ffff
from to via pattern
lightsync/frontend/app.js /api/devices apiPost for form submit, apiDelete for remove button apiPost.*api/devices
from to via pattern
lightsync/frontend/index.html lightsync/frontend/app.js form#add-device-form submit event listener 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.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.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
<div class="panel" id="devices-panel" style="flex: 1; min-height: 0;">
    <div class="panel-header">DEVICES</div>
    <div class="panel-content" id="device-list">
        <span class="text-dim">loading...</span>
    </div>
    <form id="add-device-form" class="device-form">
        <input type="text" name="name" placeholder="Device name" required>
        <select name="strip_type" required>
            <option value="sk6812">SK6812</option>
            <option value="ws2801">WS2801</option>
            <option value="generic">Generic</option>
        </select>
        <input type="number" name="led_count" placeholder="LED count" min="1" max="1000" required>
        <input type="text" name="ip" placeholder="IP address" required>
        <input type="number" name="port" placeholder="Port" min="1" max="65535" value="21324" required>
        <button type="submit">+ ADD DEVICE</button>
    </form>
</div>
```

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 =>
    `<div class="device-row">
        <div class="device-row-info">
            <span class="text-accent">${escapeHtml(d.name)}</span>
            <span class="text-dim">${escapeHtml(d.strip_type)} / ${d.led_count} LEDs / ${escapeHtml(d.ip)}:${d.port}</span>
        </div>
        <button class="device-remove" onclick="removeDevice('${d.id}')" title="Remove device">&times;</button>
    </div>`
).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 `<div class="panel" style="flex: 0 0 120px;">`): Add `id="animations-panel"`:
   ```html
   <div class="panel" id="animations-panel" style="flex: 0 0 120px;">
   ```

2. The main-area timeline (currently line 35 `<main class="main-area">`): Wrap or add ID to the main element. Add a panel wrapper inside:
   ```html
   <main class="main-area">
       <div class="panel" id="timeline-panel" style="flex: 1; display: flex; align-items: center; justify-content: center;">
           <span>[ TIMELINE &#8212; PHASE 2+ ]</span>
       </div>
   </main>
   ```

3. The transport-bar footer (currently line 39 `<footer class="transport-bar">`): Add ID to the footer or add an inner panel:
   ```html
   <footer class="transport-bar" id="transport-panel">
       <span>[ TRANSPORT &#8212; PHASE 2+ ]</span>
   </footer>
   ```
cd /home/claude/led2 && grep -c "\-\-accent.*#00ffff" lightsync/frontend/style.css && grep "devices-panel" lightsync/frontend/index.html | head -1 && grep "animations-panel" lightsync/frontend/index.html | head -1 && grep "timeline-panel" lightsync/frontend/index.html | head -1 && grep "transport-panel" lightsync/frontend/index.html | head -1 - lightsync/frontend/style.css contains `--accent: #00ffff` (not `--accent: var(...)`) - lightsync/frontend/style.css contains `--text-accent: var(--accent)` (alias, not a separate #00ffff value) - lightsync/frontend/style.css contains `--border-bright: var(--accent)` (alias, not a separate #00ffff value) - lightsync/frontend/style.css contains `.device-form` rule with `display: flex` and `flex-direction: column` - lightsync/frontend/style.css contains `.device-remove` rule with `cursor: pointer` - lightsync/frontend/index.html contains `id="devices-panel"` - lightsync/frontend/index.html contains `id="animations-panel"` - lightsync/frontend/index.html contains `id="timeline-panel"` - lightsync/frontend/index.html contains `id="transport-panel"` CSS uses --accent: #00ffff as the canonical variable with --text-accent and --border-bright as aliases. All four panel IDs present in index.html. Device form and remove button have proper styling. 1. `grep "add-device-form" lightsync/frontend/index.html` returns a match 2. `grep "apiPost\|apiDelete\|removeDevice" lightsync/frontend/app.js` returns 3+ matches 3. `grep "\-\-accent:.*#00ffff" lightsync/frontend/style.css` returns a match 4. `grep "devices-panel\|animations-panel\|timeline-panel\|transport-panel" lightsync/frontend/index.html` returns 4 matches 5. After deploying: open https://lightsync.groll.cloud, fill the add-device form, submit, see device appear in list, click remove, device disappears

<success_criteria>

  • The add-device form is visible in the DEVICES panel with all 5 inputs and submit button
  • Submitting the form creates a device via POST /api/devices and refreshes the list
  • Each device row shows a remove button that calls DELETE /api/devices/{id}
  • CSS variable --accent: #00ffff exists as the single source of truth for the cyan accent color
  • All four panel IDs (devices-panel, animations-panel, timeline-panel, transport-panel) are present in index.html </success_criteria>
After completion, create `.planning/phases/01-foundation/01-04-SUMMARY.md`