- Replace URL-based image_source/url fields with image_asset_id/video_asset_id
on StaticImagePictureSource and VideoCaptureSource (clean break, no migration)
- Resolve asset IDs to file paths at runtime via AssetStore.get_file_path()
- Add EntitySelect asset pickers for image/video in stream editor modal
- Add notification sound configuration (global sound + per-app overrides)
- Unify per-app color and sound overrides into single "Per-App Overrides" section
- Persist notification history between server restarts
- Add asset management system (upload, edit, delete, soft-delete)
- Replace emoji buttons with SVG icons throughout UI
- Various backend improvements: SQLite stores, auth, backup, MQTT, webhooks
API Input:
- Add interpolation mode (linear/nearest/none) for LED count mismatch
between incoming data and device LED count
- New IconSelect in editor, i18n for en/ru/zh
- Mark crossfade as won't-do (client owns temporal transitions)
- Mark last-write-wins as already implemented
LED Preview:
- Fix zone-mode preview parsing composite wire format (0xFE header
bytes were rendered as color data, garbling multi-zone previews)
- Fix _restoreLedPreviewState to handle zone-mode panels
FPS Charts:
- Seed target card charts from server metrics-history on first load
- Add fetchMetricsHistory() with 5s TTL cache shared across
dashboard, targets, perf-charts, and graph-editor
- Fix chart padding: pass maxSamples per caller (120 for dashboard,
30 for target cards) instead of hardcoded 120
- Fix dashboard chart empty on tab switch (always fetch server history)
- Left-pad with nulls for consistent chart width across targets
Dashboard:
- Fix metrics row alignment (grid layout with fixed column widths)
- Fix FPS label overflow into uptime column
Collapsed filter cards in the modal showed raw template IDs (e.g.
pp_cb72e227) instead of resolving select options to their labels.
Card filter chain badges now include the referenced template name.
Weather card used ⏩ and 🌡 emoji, daylight card used 🕐 and ⏩.
Replaced with ICON_FAST_FORWARD, ICON_THERMOMETER, and ICON_CLOCK.
Added thermometer icon path.
The preview config was sending `palette` which defaults to "fire" on
the server, ignoring the user's selected gradient. Also removed the
dead fallback notification block and stale custom_palette check.
- Add warm, cool, neon, pastel built-in gradients (promoted from frontend presets)
- Change gradient seeding to add missing built-ins on every startup (not just first run)
- Add searchable option to IconSelect component for filtering by name
- Enable search on gradient, effect palette, and audio palette pickers
- Simplify modal titles: "Add Gradient" / "Edit Gradient" instead of "Add Color Strip Source: Gradient"
- Update INSTALLATION.md and .env.example
- Replace truncated plaintext release notes with full-screen overlay
rendered via `marked` library
- Server reconnection does a hard page reload instead of custom event
Replace 22 individual JSON store files with a single SQLite database
(data/ledgrab.db). All entity stores now use BaseSqliteStore backed by
SQLite with WAL mode, write-through caching, and thread-safe access.
- Add Database class with SQLite backup/restore API
- Add BaseSqliteStore as drop-in replacement for BaseJsonStore
- Convert all 16 entity stores to SQLite
- Move global settings (MQTT, external URL, auto-backup) to SQLite
settings table
- Replace JSON backup/restore with SQLite snapshot backups (.db files)
- Remove partial export/import feature (backend + frontend)
- Update demo seed to write directly to SQLite
- Add "Backup Now" button to settings UI
- Remove StorageConfig file path fields (single database_file remains)
The os_listener field existed in the backend model but was never
exposed in the UI. It defaulted to false, so OS notifications were
captured to history but never fired the visual effect. Now shows
a toggle at the top of the notification section, defaults to ON
for new sources.
Audio sources: type + device/parent/channel/band detail
Weather sources: provider + coordinates (updates on geolocation)
Sync clocks: "Sync Clocks · Nx" (updates on speed slider)
Automations: scene name + condition count/logic
Scene presets: "Scenes · N targets" (updates on add/remove)
Pattern templates: "Pattern Templates · N rects" (updates on add/remove)
All follow the same pattern: name auto-generates on create, stops
when user manually edits the name field.
Multichannel, Mono, and Band Extract each get their own subtab and
panel within the Audio group, replacing the single combined Audio
Sources tab. Cross-links from CSS, value sources, and command palette
updated to navigate to the correct subtab.
New audio source type that filters a parent source to a specific frequency
band (bass 20-250Hz, mid 250-4kHz, treble 4k-20kHz, or custom range).
Supports chaining with frequency range intersection and cycle detection.
Band filtering applied in both CSS audio streams and test WebSocket.
New standalone WeatherSource entity with pluggable provider architecture
(Open-Meteo v1, free, no API key). Full CRUD, test endpoint, browser
geolocation, IconSelect provider picker, CardSection with test/clone/edit.
WeatherColorStripStream maps WMO weather codes to ambient color palettes
with temperature hue shifting and thunderstorm flash effects. Ref-counted
WeatherManager polls API and caches data per source.
CSS editor integration: weather type with EntitySelect source picker,
speed and temperature influence sliders. Backup/restore support.
i18n for en/ru/zh.
Composite layers now support optional start/end LED range (toggleable)
and reverse flag, making composite a superset of mapped source. Layers
are collapsible with animated expand/collapse and consistent 0.85rem
font sizing. Delete button restyled as ghost icon button.
Also includes minor dashboard CSS overflow fixes.
- Add /api/v1/system/shutdown endpoint that triggers clean uvicorn exit
- Persist all 15 stores to disk during shutdown via _save_all_stores()
- Add force parameter to BaseJsonStore._save() to bypass restore freeze
- Restart script now requests graceful shutdown via API (15s timeout),
falls back to force-kill only if server doesn't exit in time
- Broadcast server_restarting event over WebSocket before shutdown
- Frontend shows "Server restarting..." overlay instantly on WS event,
replacing the old dynamically-created overlay from settings.ts
- Add server_ref module to share uvicorn Server + TrayManager refs
- Add i18n keys for restart overlay (en/ru/zh)
Populate <select> <option> elements from gradient entities before
creating IconSelect — the trigger display needs matching options to
sync correctly. Add _syncSelectOptions helper used by all three
palette pickers (gradient, effect, audio).
Replace the gradient stop editor (canvas, markers, stop list) in the
CSS editor gradient section with a simple gradient entity selector.
Gradients are now created/edited exclusively in the Gradients tab.
Fix effect and audio palette pickers to populate from gradient entities
dynamically instead of hardcoded HTML options.
Unify all gradient/palette pickers via _buildGradientEntityItems().
Also: rename "None (use own speed)" → "None (no sync)" for sync clocks.
Add i18n keys for gradient selector and missing error messages.
Fix ~68 pre-existing strict null errors across 13 feature modules.
Add non-null assertions for DOM element lookups, null coalescing for
optional values, and type guards for nullable properties. Zero tsc
errors now with --noEmit.
Add full gradient editor modal with name, description, visual stop
editor, tags, and dirty checking. Gradient editor now supports ID
prefix to avoid DOM conflicts between CSS editor and standalone modal.
Fix color picker popover clipped by template-card overflow:hidden.
Fix gradient canvas not sizing correctly in standalone modal.
New notification effects:
- Chase: light bounces across strip with Gaussian glow tail
- Gradient flash: bright center fades to edges with exponential decay
Queue priority: notifications with color_override get high priority and
interrupt the current effect.
Also fixes transient preview for notification sources — adds WebSocket
"fire" command so inline preview works without a saved source, plus
auto-fires on preview open so the effect is visible immediately.
Effects: add 7 new procedural effects (rain, comet, bouncing ball, fireworks,
sparkle rain, lava lamp, wave interference) and custom palette support via
user-defined [[pos,R,G,B],...] stops.
Gradient: add easing functions (linear, ease_in_out, step, cubic) for stop
interpolation, plus noise_perturb and hue_rotate animation types.
Daylight: add longitude field and NOAA solar equations for accurate
sunrise/sunset based on latitude, longitude, and day of year.
Candlelight: add wind simulation (correlated gusts), candle type presets
(taper/votive/bonfire), and wax drip effect with localized brightness dips.
Also fixes editor preview to include all new fields for inline LED test.
Add 5 WebGL shader background effects (Aurora, Plasma, Digital Rain,
Starfield, Warp Tunnel) via a new bg-shaders.ts engine that shares
a dedicated canvas. Add 5 style presets (Sakura, Ocean, Copper, Vapor,
Monolith) with distinctive font pairings. Remove CSS particles effect
in favor of shader-based alternatives. Fix dot grid visibility and
tune all shader intensities for subtle ambient appearance.
Add style presets (font + color combinations) and background effect
presets as a new Appearance tab in the settings modal. Style presets
include Default, Midnight, Ember, Arctic, Terminal, and Neon — each
with curated dark/light theme colors and Google Font pairings.
Background effects (Dot Grid, Gradient Mesh, Scanlines, Particles)
use a dedicated overlay div alongside the existing WebGL Noise Field.
All choices persist to localStorage and restore on page load.
Scene name and fallback scene in automation cards are now clickable,
navigating to the corresponding scene preset card. Also renders the
deactivation mode label which was previously set but never displayed.
- Notification history: replace text buttons with icon buttons, use modal-footer for proper positioning
- CSS test: reject 0-LED picture sources with clear error message, show WS close reason in UI
- Calibration: distribute LEDs by aspect ratio (16:9 default) instead of evenly across edges
- Value source schedule: replace native time input with custom HH:MM picker matching automation style
- Remove "No ... yet" empty state labels from all CardSection instances
- Replace winsdk (~35MB) with winrt packages (~2.5MB) for OS notification
listener. API is identical, 93% size reduction.
- Replace wmi (~3-5MB) with ctypes for monitor names (EnumDisplayDevicesW)
and camera names (SetupAPI). Zero external dependency.
- Migrate cv2.resize/imencode/LUT to Pillow/numpy in 5 files (filters,
preview helpers, kc_target_processor). OpenCV only needed for camera
and video stream now.
- Fix DefWindowProcW ctypes overflow on 64-bit Python (pre-existing bug
in platform_detector display power listener).
- Fix openLightbox import in streams-capture-templates.ts (was using
broken window cast instead of direct import).
- Add mandatory data migration policy to CLAUDE.md after silent data
loss incident from storage file rename without migration.
- Refactor process-picker.ts into generic NamePalette with two concrete
instances: ProcessPalette (running processes) and NotificationAppPalette
(OS notification history apps)
- Notification color strip app colors and filter list now use
NotificationAppPalette (shows display names like "Telegram" instead of
process names like "telegram.exe")
- Fix case-insensitive matching for app_colors and app_filter_list in
notification_stream.py
- Compact browse/remove buttons in notification app color rows with
proper search icon
- Remove old inline process-picker HTML/CSS (replaced by palette overlay)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Server: send initial frame immediately after metadata for api_input
sources so the client gets the current buffer (fallback color if
inactive) instead of never receiving a frame when push_generation
stays at 0.
Frontend: clear all test modal canvases on open to prevent stale
pixels from previous sessions. Reset FPS timestamps after metadata
so the initial bootstrap frame isn't counted in the FPS chart.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename all 54 .js files to .ts, update esbuild entry point
- Add tsconfig.json, TypeScript devDependency, typecheck script
- Create types.ts with 25+ interfaces matching backend Pydantic schemas
(Device, OutputTarget, ColorStripSource, PatternTemplate, ValueSource,
AudioSource, PictureSource, ScenePreset, SyncClock, Automation, etc.)
- Make DataCache generic (DataCache<T>) with typed state instances
- Type all state variables in state.ts with proper entity types
- Type all create*Card functions with proper entity interfaces
- Type all function parameters and return types across all 54 files
- Type core component constructors (CardSection, IconSelect, EntitySelect,
FilterList, TagInput, TreeNav, Modal) with exported option interfaces
- Add comprehensive global.d.ts for window function declarations
- Type fetchWithAuth with FetchAuthOpts interface
- Remove all (window as any) casts in favor of global.d.ts declarations
- Zero tsc errors, esbuild bundle unchanged
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bulk selection mode: Ctrl+Click or toggle button to enter, Escape to exit
- Shift+Click for range select, bottom toolbar with SVG icon action buttons
- All CardSections wired with bulk actions: Delete everywhere,
Start/Stop for targets, Enable/Disable for automations
- Remove expand/collapse all buttons (no collapsible sections remain)
- Fix graph node color picker overlay persisting after outside click
- Add Icons section to frontend.md conventions
- Add trash2, listChecks, circleOff icons to icon system
- Backend: processing loop performance improvements (monotonic timestamps,
deque-based FPS tracking)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously modals showed generic "Failed to add/save" messages. Now they
extract and display the actual error detail from the API response (e.g.,
"URL is required", "Name already exists"). Handles Pydantic validation
arrays by joining msg fields.
Fixed in 8 files: device-discovery, devices, calibration,
advanced-calibration, scene-presets, automations, command-palette.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Settings modal split into 3 tabs: General, Backup, MQTT
- Log viewer moved to full-screen overlay with compact toolbar
- External URL setting: API endpoints + UI for configuring server domain
used in webhook/WS URLs instead of auto-detected local IP
- Sources tab tree restructured: Picture Source (Screen Capture/Static/
Processed sub-groups), Color Strip, Audio, Utility
- TreeNav extended to support nested groups (3-level tree)
- Audio tab split into Sources and Templates sub-tabs
- Fix audio template test: device picker now filters by engine type
(was showing WASAPI indices for sounddevice templates)
- Audio template test device picker disabled during active test
- Rename "Input Source" to "Source" in CSS test preview (en/ru/zh)
- Fix i18n: log filter/level items deferred to avoid stale t() calls
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Graph editor:
- Floating FPS tooltip on hover over running output_target nodes (300ms delay)
- Shows errors, uptime, and FPS sparkline seeded from server metrics history
- Tooltip positioned below node with fade-in/out animation
- Uses pointerover/pointerout with relatedTarget check to prevent flicker
- Fixed-width tooltip (200px) with monospace values to prevent layout shift
- Node titles show full names (removed truncate), no native SVG <title> tooltips
Documentation:
- Added duration/numeric formatting conventions to contexts/frontend.md
- Added node hover tooltip docs to contexts/graph-editor.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New features:
- Override composite blend mode: per-pixel alpha from brightness
(black=transparent, bright=opaque). Ideal for API input over effects.
- API input test preview FPS chart uses shared createFpsSparkline
(same look as target card charts)
Fixes:
- Fix api_input source not surviving server restart: from_dict was
still passing removed led_count field to constructor
- Fix composite layer brightness/processing selectors not aligned:
labels get fixed width, selects fill remaining space
- Fix CSPT input selector showing in non-CSPT CSS test mode
- Fix test modal LED/FPS controls showing for api_input sources
- Server only sends test WS frames when api_input push_generation changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
API Input CSS rework:
- Remove led_count field from ApiInputColorStripSource (always auto-sizes)
- Add segment-based payload: solid, per_pixel, gradient modes
- Segments applied in order (last wins on overlap), auto-grow buffer
- Backward compatible: legacy {"colors": [...]} still works
- Pydantic validation: mode-specific field requirements
Test preview:
- Enable test preview button on api_input cards
- Hide LED/FPS controls for api_input (sender controls those)
- Show input source selector for all CSS tests (preselected)
- FPS sparkline chart using shared createFpsSparkline (same as target cards)
- Server only sends frames when push_generation changes (no idle frames)
HAOS integration:
- New light.py: ApiInputLight entity per api_input source (RGB + brightness)
- turn_on pushes solid segment, turn_off pushes fallback color
- Register wled_screen_controller.set_leds service for arbitrary segments
- New services.yaml with field definitions
- Coordinator: push_colors() and push_segments() methods
- Platform.LIGHT added to platforms list
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend: send border_width in WS metadata and frame_dims (width, height)
as a separate JSON message on first JPEG frame.
Frontend: render semi-transparent green overlay rectangles on each active
edge showing the sampling region depth, plus a small px label. Overlays
are proportionally sized based on border_width relative to frame dimensions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Key Colors test:
- New WS endpoint for live KC target test streaming (replaces REST polling)
- Auto-connect on lightbox open, auto-disconnect on close
- Uses same FPS/preview_width as CSS source test (no separate controls)
- Removed FPS selector, start/stop toggle, and updateAutoRefreshButton
Device cards:
- Fix full re-render on every poll caused by relative "Last seen" time in HTML
- Last seen label now patched in-place via data attribute (like FPS metrics)
- Remove overlay visualization button from LED target cards
Sync clocks:
- Fix card not updating start/stop icon: invalidate cache before reload
Other:
- Tab indicator respects bg-anim toggle (hidden when dynamic background off)
- Camera backend icon grid uses SVG icons instead of emoji
- Frontend context rule: no emoji in IconSelect items
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UI fixes:
- Automation card badge moved to flex layout — title truncates, badge stays visible
- Automation condition pills max-width increased to 280px
- Dashboard crosslinks fixed: pass correct sub-tab key (led-targets not led)
- navigateToCard only skips data load when tab already has cards in DOM
- Badge gets white-space:nowrap + flex-shrink:0 to prevent wrapping
New features:
- formatCompact() for large frame/error counters (1.2M, 45.2K) with hover title
- Log filter and log level selects replaced with IconSelect grids
- OpenRGB devices now support software brightness control
OpenRGB improvements:
- Added brightness_control capability (uses software brightness fallback)
- Change-threshold dedup compares raw pixels before brightness scaling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>