Import: a browsable preset panel with multi-select (#48) #98
No reviewers
Labels
No labels
area/ai
area/backend
area/frontend
area/infra
area/scheduler
area/wled
good-first-issue
priority/high
priority/low
priority/medium
type/bug
type/chore
type/ci-cd
type/docs
type/feature
type/qa
v1.0.0
v1.1.0
v1.2.0
v2.0.0
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/Iris-WLED!98
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/browsable-preset-import"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Batch C item 7 of #48 — the last one, and the largest.
The gap
Spec §3.6 has always called for parsing a WLED
presets.jsonand "displaying them in a browsable import panel" where the user "selects which presets to import". What existed was a textarea, a name-only<select>, and one preset per round trip.Frontend
The dialog moves out of
Toolbar.tsx(which was carrying it inline) intoImportPanel.tsx:Backend
PresetPreviewgains a representative palette — the non-black colour slots across a preset's segments, deduped and capped — which drives the swatches via the existingsegmentGradient.New
POST /import/presets/batchapplies the whole selection in one transaction under a single backup. All-or-nothing matters here specifically: a scheme id is derived as{event_id}_{phase}, so a partial import would leave phases the user never chose.import_preset's core is extracted to_apply_presetand shared by both endpoints.Collisions are resolved before sending
Because one bad row would discard every good one, the panel checks the selection against the current library up front: an approved slot blocks, a candidate warns it will be replaced, and two rows targeting one phase are caught client-side as well as server-side. Those rules live in
importUtils.tsso they're testable without rendering the dialog.One behaviour change worth flagging
segment_mappingnow distinguishesNone("no opinion" → match by WLED index) from{}("map nothing"), which the panel needs in order to express skipping every segment. Previously both fell through to the default mapping. No existing caller sent{}— the old UI's empty-field path is removed in this PR — and there's a test pinning both behaviours.Verification
ruff check,ruff format --check,mypyclean.node:22container:tsc -b,eslint,vitest(70 passed, 14 new inimportUtils.test.ts) andnpm run buildall clean.🤖 Generated with Claude Code
Spec §3.6 has always called for parsing a WLED presets.json and "displaying them in a browsable import panel" where the user "selects which presets to import". What existed was a textarea, a name-only dropdown, and one preset per round trip. The dialog moves out of Toolbar.tsx into ImportPanel.tsx: a file picker (paste kept behind a disclosure) over a scrollable list, one row per preset showing a colour swatch, name and source segment ids, with a checkbox and a per-row event/phase selector. Segment mapping becomes a row per source segment mapped onto this install's named segments, replacing the raw JSON field, and is seeded with what the backend would do on its own so the table shows the real outcome rather than every row reading "skip". Backend: PresetPreview gains a representative palette (non-black slots across the segments, deduped, capped) to drive the swatches, and a new POST /import/presets/batch applies the whole selection in one transaction under a single backup. All-or-nothing matters here: a scheme id is derived as {event_id}_{phase}, so a partial import would leave phases the user never chose. import_preset's core is extracted to _apply_preset and shared. Because one bad row would discard every good one, the panel resolves collisions against the current library before sending -- an approved slot blocks, a candidate warns it will be replaced, and two rows on one phase are caught client-side as well as server-side. Those rules live in importUtils.ts so they are testable without rendering the dialog. One behaviour change worth noting: segment_mapping now distinguishes None ("no opinion" -> match by WLED index) from {} ("map nothing"), which the panel needs to express skipping every segment. No existing caller sent {}. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>