Replace plain <select> dropdowns with a searchable command palette modal
for 16 entity selectors across 6 editors (targets, streams, CSS sources,
value sources, audio sources, pattern templates). Unified EntityPalette
singleton + EntitySelect wrapper in core/entity-palette.js.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add `startup` automation condition type that activates on server boot,
replacing the per-target `auto_start` flag
- Remove `auto_start` field from targets, scene snapshots, and all API layers
- Remove auto-start UI section and star buttons from dashboard and target cards
- Remove `color` field from scene presets (backend, API, modal, frontend)
- Add card color support to scene preset cards (color picker + border style)
- Show localStorage-backed card colors on all dashboard cards (targets,
automations, sync clocks, scene presets)
- Fix card color picker updating wrong card when duplicate data attributes
exist by using closest() from picker wrapper instead of global querySelector
- Add sync clocks step to Sources tab tutorial
- Bump SW cache v9 → v10
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces Synchronization Clocks — shared, controllable time bases
that CSS sources can optionally reference for synchronized animation.
Backend:
- New SyncClock dataclass, JSON store, Pydantic schemas, REST API
- Runtime clock with thread-safe pause/resume/reset and speed control
- Ref-counted runtime pool with eager creation for API control
- clock_id field on all ColorStripSource types
- Stream integration: clock time/speed replaces source-local values
- Paused clock skips rendering (saves CPU + stops frame pushes)
- Included in backup/restore via STORE_MAP
Frontend:
- Sync Clocks tab in Streams section with cards and controls
- Clock dropdown in CSS editor (hidden speed slider when clock set)
- Clock crosslink badge on CSS source cards (replaces speed badge)
- Targets tab uses DataCache for picture/audio sources and sync clocks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Zone picker UI in device add/settings modals with per-zone checkbox selection
- Combined mode: pixels distributed sequentially across zones
- Separate mode: full effect resampled independently to each zone via linear interpolation
- Per-zone LED preview in target cards: one canvas strip per zone with hover overlay labels
- Zone badges on device cards enriched with actual LED counts from OpenRGB API
- Fix stale led_count by using device_led_count discovered at connect time
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Color picker popover now uses fixed positioning on small screens to
escape the header toolbar overflow container. Section titles, sub-tab
labels, and filter placeholders use data-i18n attributes so they update
automatically on language change. Display picker title switches to
"Select a Device" for engine-owned display lists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add stop-all buttons to LED targets and KC targets section headers
(visible only when targets are running, uses headerExtra on CardSection)
- Add reset ability to performance chart color pickers (removes custom
color from localStorage and reverts to default)
- Remove CODEBASE_REVIEW.md
- Add prioritized TODO.md with P1/P2/P3 feature roadmap
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CardSection._animateEntrance: skip after first render to prevent
card fade-in replaying on every data refresh
- automations: use reconcile() on subsequent renders instead of full
innerHTML replacement that destroyed and recreated all cards
- streams: same reconcile() approach for all 9 CardSections
- targets/dashboard/streams: only show setTabRefreshing loading bar
on first render when the tab is empty
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use error.detail from API responses instead of generic i18n messages
so users see specific reasons for failures (e.g. "Device is referenced
by target(s): ...").
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce localStorage-backed card color assignment for all card types
with a reusable wrapCard() helper that provides consistent card shell
structure (top actions, bottom actions with color picker). Move color
picker from top-right to bottom-right action bar. Add color reset
button to clear card color back to default.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New WS device type: broadcaster singleton + LEDClient that sends binary
frames to connected WebSocket clients during processing
- FastAPI WS endpoint at /api/v1/devices/{device_id}/ws with token auth
- Frontend: add/edit WS devices, connection URL with copy button in settings
- Add health_check and auto_restore capabilities to WLED and Serial providers;
hide health interval and auto-restore toggle for devices without them
- Skip health check loop for virtual devices (Mock, MQTT, WS) — set always-online
- Copy buttons and labels for API CSS push endpoints (REST POST / WebSocket)
- Hide mock:// and ws:// URLs in target device dropdown
- Hide filter textbox when card section is collapsed (cs-collapsed CSS class)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stability:
- Fix race condition: set _is_running before create_task in target processors
- Await probe task after cancel in wled_target_processor
- Replace raw fetch() with fetchWithAuth() across devices, kc-targets, pattern-templates
- Add try/catch to showTestTemplateModal in streams.js
- Wrap blocking I/O in asyncio.to_thread (picture_targets, system restore)
- Fix dashboardStopAll to filter only running targets with ok guard
Performance:
- Vectorize fire effect spark loop with numpy in effect_stream
- Vectorize FFT band binning with cumulative sum in analysis.py
- Rewrite pixel_processor with vectorized numpy (accept ndarray or list)
- Add httpx.AsyncClient connection pooling with lock in wled_provider
- Optimize _send_pixels_http to avoid np.hstack allocation in wled_client
- Mutate chart arrays in-place in dashboard, perf-charts, targets
- Merge dashboard 2-batch fetch into single Promise.all
- Hoist frame_time outside loop in mapped_stream
Usability:
- Fix health check interval load/save in device settings
- Swap confirm modal button classes (No=secondary, Yes=danger)
- Add aria-modal to audio/value source editors, fix close button aria-labels
- Add modal footer close button to settings modal
- Add dedicated calibration LED count validation error keys
i18n:
- Replace ~50 hardcoded English strings with t() calls across 12 JS files
- Add 50 new keys to en.json, ru.json, zh.json
- Localize inline toasts in index.html with window.t fallback
- Add data-i18n to command palette footer
- Add localization policy to CLAUDE.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace all emoji characters across WebUI with inline Lucide SVG icons
for cross-platform consistency (icon paths in icon-paths.js)
- Add accent color picker popover with 9 preset colors + custom picker,
persisted to localStorage, updates all CSS custom properties
- Remove subtab separator line for cleaner look
- Color badge icons with accent color for visual pop
- Remove processing badge from target cards
- Fix hardcoded #4CAF50 in FPS labels and active badges to use CSS vars
- Replace CSS content emoji (▶) with pure CSS triangle
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add sub-tutorials for Dashboard, Targets, Sources, and Profiles tabs
with ? trigger buttons, en/ru/zh translations, and hidden-ancestor
skip via offsetParent check
- Add expand/collapse all buttons to Profiles tab toolbar
- Move dashboard poll slider from section header to toolbar
- Fix cardEnter animation forcing opacity:1 on disabled profile cards
- Use data-card-section selectors instead of data-cs-toggle to avoid
z-index misalignment during tutorial spotlight
- Add tutorial sync convention to CLAUDE.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dashboard cards (targets, auto-start, profiles) are now clickable,
navigating to the full entity card on the appropriate tab. Card
sections support drag-and-drop reordering via grip handles with
localStorage persistence. Fix crosslink navigation scoping to avoid
matching dashboard cards, and fix highlight race on rapid clicks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add protocol field (ddp/http) to storage, API schemas, routes, processor
- WledTargetProcessor passes protocol to create_led_client(use_ddp=...)
- Target editor: protocol dropdown + keepalive in collapsible Specific Settings
- FPS, brightness threshold, adaptive FPS moved to main form area
- Hide Specific Settings section for serial devices (protocol is WLED-only)
- Card badge: show DDP/HTTP for WLED devices, Serial for serial devices
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DDP uses fire-and-forget UDP, so when a WiFi device becomes overwhelmed
by sustained traffic, sends appear successful while the device is
actually unreachable. This adds:
- HTTP liveness probe (GET /json/info, 2s timeout) every 10s during
streaming, exposed as device_streaming_reachable in target state
- Adaptive FPS (opt-in): exponential backoff when device is unreachable,
gradual recovery when it stabilizes — finds sustainable send rate
- Honest health checks: removed the lie that forced device_online=true
during streaming; now runs actual health checks regardless
- Target editor toggle, FPS display shows effective rate when throttled,
health dot reflects streaming reachability, red highlight when
unreachable
- Auto-backup scheduling support in settings modal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stability: Add outer try/except/finally with _running=False cleanup to all 6
processing loop methods (live, color_strip, effect, audio, composite, mapped).
Add exponential backoff on consecutive capture errors in live_stream. Move
audio stream.stop() outside lock scope.
Performance: Replace per-pixel Python loop with np.array().tobytes() in
ddp_client. Vectorize pixelate filter with cv2.resize down+up. Vectorize
gradient rendering with np.searchsorted.
Frontend: Add lockBody/unlockBody re-entrancy counter. Add {once:true} to
fetchWithAuth abort listener. Null ws.onclose before ws.close() in LED preview.
Backend: Remove auth token prefix from log messages. Add atomic_write_json
helper (tempfile + os.replace) and update all 10 stores. Add name uniqueness
checks to all update methods. Fix DELETE status codes to 204 in audio_sources
and value_sources. Fix get_source() silent bug in color_strip_sources.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add real-time value source test: WebSocket endpoint streams get_value() at
~20Hz, frontend renders scrolling time-series chart with min/max/current stats
- Add auto-gain for audio value sources: rolling peak normalization with slow
decay, sensitivity range increased to 0.1-20.0
- Always show brightness overlay on LED preview when brightness source is set
- Refactor ValueStreamManager to shared ref-counted streams (value streams
produce scalars, not LED-count-dependent, so sharing is correct)
- Simplify acquire/release API: remove consumer_id parameter since streams
are no longer consumer-dependent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New per-target property: when effective output brightness
(max pixel value × device/source brightness) falls below
the threshold, LEDs turn off completely. Useful for cutting
dim flicker in audio-reactive and ambient setups.
Threshold slider (0–254) in target editor, badge on card,
hot-swap to running processors, persisted in storage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show effective brightness percentage on the LED preview when a
value source dims below 100%. Prepend a brightness byte to the
preview WebSocket wire format. Increase preview height to 32px.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The dashboard panel appears before the targets panel in the DOM and both
use data-target-id. document.querySelector was finding the dashboard
element (which has no data-tm children) instead of the target card.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Charts: blue filled area for fps_actual (rolling avg), green line for
fps_current (real-time sends/sec)
- Labels: fps_current/fps_target as primary, avg fps_actual as secondary
- Track fps_current in metrics history for dashboard chart preload
- Applied to both LED targets page and dashboard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix inverted gamma formula: use (i/255)^gamma instead of (i/255)^(1/gamma)
so gamma>1 correctly darkens midtones (standard LED gamma correction)
- Fix frame interpolation flicker: move interp buffer update after temporal
smoothing so idle-tick output is consistent with new-frame output
- Fix target card hover/animation reset: use stable placeholder values in
card HTML for volatile metrics (data-tm attributes), patch real values
in-place after reconcile instead of replacing entire card DOM element
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add tab refresh loading bar animation for all 4 tab loaders
- Add profiles loading guard to prevent concurrent fetches
- Centralize theme colors into CSS variables (--text-secondary, --text-muted,
--bg-secondary, --success-color, --shadow-color) for both dark/light themes
- Replace hardcoded gray values across 10 CSS files with variables
- Fix duplicate .btn-sm definition in modal.css
- Fix z-index: toast 2001→2500 to safely clear modals at 2000
- Add :focus-visible keyboard navigation indicators for all interactive elements
- Add responsive breakpoints for tab bar and header on narrow screens
- Prevent background page scroll when command palette is open
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add loading guard to loadPictureSources to prevent concurrent fetches
- Pause perf chart polling and uptime timer when browser tab is hidden
- Disconnect KC and LED preview WebSockets when leaving targets tab
- Add error toasts to loadCaptureTemplates and saveKCBrightness
- Skip auto-refresh polling when document is hidden
- Widen auto-start dashboard cards for better text display
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add auto_start boolean field to PictureTarget model (persisted per-target)
- Wire auto_start through API schemas, routes, and store
- Auto-start targets on server boot in main.py lifespan
- Add star toggle button on target cards (next to delete button)
- Add auto-start section on dashboard between performance and profiles
- Remove auto-start section from profiles tab
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Keyboard shortcuts: Ctrl+1-4 for tab switching
- URL hash routing: #tab/subtab format with browser back/forward support
- Tab count badges: running targets and active profiles counts
- Cross-entity quick links: clickable references navigate to related cards
- Command palette (Ctrl+K): global search across all entities with keyboard navigation
- Expand/collapse all sections: buttons in sub-tab bars
- Sticky section headers: headers pin while scrolling long card grids
- Improved section filter: better styling with reset button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract getValueSourceIcon() into value-sources.js and use it for
brightness source dropdowns and card badges in both LED and KC targets.
Icons now match value source cards: 📊 static, 🔄 animated, 🎵 audio,
🕐 adaptive_time, 🌤️ adaptive_scene.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add nonlocal declarations for _u16_a, _u16_b, _i32 in nested functions
_blend_u16 and _apply_corrections — Python treats augmented assignments
(*=, +=, >>=) as local variable bindings, causing UnboundLocalError
that prevented any frames from being sent to devices.
Move 🔢 emoji from brightness source label to dropdown option items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instrument AudioCaptureStream with read/FFT timing and
AudioColorStripStream with render timing. Display audio-specific
timing segments (read/fft/render/send) in the target card
breakdown bar when an audio source is active.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stream real-time LED colors from running WLED targets to the browser via
binary WebSocket (RGB bytes, throttled to ~15 fps). Toggle button on
target card opens a compact canvas strip that renders each frame using
ImageData. Cached last frame is re-rendered after card reconciliation to
prevent flicker during auto-refresh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add uptime metric to both LED and KC target cards in the targets tab.
Move formatUptime() to shared ui.js module. Fix dashboard uptime freezing
when switching tabs by re-caching DOM element refs on early return paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces a new Value Source entity that produces a scalar float (0.0-1.0)
for dynamic brightness modulation. Three subtypes: Static (constant),
Animated (sine/triangle/square/sawtooth waveform), and Audio-reactive
(RMS/peak/beat from mono audio source). Value sources can be optionally
attached to LED targets to control brightness each frame.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CardSection now diffs cards by key attributes instead of rebuilding innerHTML,
preserving DOM elements, filter input focus, scroll position, and Chart.js
instances across the 2s targets tab auto-refresh cycle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces CardSection class that wraps each card grid with a collapsible
header and inline filter input. Collapse state persists in localStorage,
filter value survives auto-refresh re-renders. When filter is active the
add-card button is hidden. Applied to all 13 sections across Targets,
Sources, and Profiles tabs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Segments are redundant now that the "mapped" CSS type handles spatial
multiplexing internally. Each target now references one color_strip_source_id
instead of an array of segments with start/end/reverse ranges.
Backward compat: existing targets with old segments format are migrated
on load by extracting the first segment's CSS source ID.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Audio sources moved to separate tab with dedicated CRUD API, store, and editor modal
- New "mapped" color strip source type: assigns different CSS sources to distinct LED sub-ranges (zones)
- Mapped stream runtime with per-zone sub-streams, auto-sizing, hot-update support
- Target editor auto-collapses segments UI when mapped CSS is selected
- Delete protection for CSS sources referenced by mapped zones
- Compact header/footer layout
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clone button on every card opens the editor in create mode pre-filled
with copied data and a "(Copy)" name suffix. Cancelling discards the
clone — entity is only persisted on Save.
Supported: LED targets, color strip sources, KC targets, pattern
templates, picture sources, capture templates, PP templates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Audio capture now produces per-channel FFT spectrum and RMS alongside
the existing mono mix. Each audio color strip source can select which
channel to visualize via a new "Channel" dropdown. This enables stereo
setups with separate left/right segments on the same LED strip.
Also shows the device LED count under the device selector in the target
editor for quick reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each target now has a segments list where each segment maps a color strip
source to a pixel range (start/end) on the device with optional reverse.
This enables composing multiple visualizations on a single LED strip.
Old targets auto-migrate from the single source format on load.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LED skip: set first N and last M LEDs to black on a target. Color sources
(static, gradient, effect, color cycle) render across only the active
(non-skipped) LEDs. Processor pads with blacks before sending to device.
Rename standby_interval → keepalive_interval across all Python, API
schemas, and JS. from_dict falls back to old key for existing configs.
Remove legacy migration functions (_migrate_devices_to_targets,
_migrate_targets_to_color_strips) and legacy fields from target model.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Filter Template meta-filter: reference existing PP templates inside others
for composable, DRY filter chains. Filters are recursively expanded at
pipeline build time with cycle detection. New `select` option type with
dynamic choices populated by the API.
Keepalive optimization: serial devices (Adalight, AmbiLED) don't need
keepalive — they hold last frame indefinitely. Check `standby_required`
capability at processor start, skip keepalive sends for serial targets,
and hide keepalive metrics in the UI. Rename "Standby Interval" to
"Keep Alive Interval" throughout the frontend.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: add batch endpoints for target states, metrics, and device
health to replace O(N) individual API calls per poll cycle.
Frontend: use batch endpoints in dashboard/targets/profiles tabs,
fix Chart.js instance leaks, debounce server event reloads, add
i18n active-tab guards, clean up ResizeObserver on pattern editor
close, cache uptime timer DOM refs, increase KC auto-refresh to 2s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Draw dashed orange line on target FPS sparkline showing hardware max FPS
- Prevent loadTargetsTab polling from rebuilding DOM while a button action
(start/stop/overlay/delete) is in flight; add reentry guard on the
refresh function itself
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add dynamic "Hardware max ≈ N fps" recommendation below FPS slider,
computed from LED count (WLED: protocol timing) or baud rate (serial).
Reuses shared _computeMaxFps from devices.js with named constants.
- Fix gradient looking different across targets sharing the same stream:
configure() now uses max LED count across all consumers; _fit_to_device
uses np.interp linear interpolation instead of truncate/tile.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FPS is a consumption property (how fast to send to a device), not a
production property. Two targets sharing the same source may need
different FPS. This moves the fps field from PictureColorStripSource
to WledPictureTarget across the full stack.
The capture stream now auto-adjusts its rate to max(all connected
target FPS values) via ColorStripStreamManager tracking per-consumer
FPS. UI updates: FPS slider in target editor, FPS badge on target
cards, LED count repositioned in CSS editor, consistent speed icons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>