09792a9a0510fac5f6208edcafe4da0a93821c85
624 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
09792a9a05 |
chore: release v0.6.1
Build Release / create-release (push) Successful in 4s
Build Android APK / build-android (push) Failing after 9s
Build Release / build-linux (push) Successful in 2m13s
Build Release / build-docker (push) Successful in 3m9s
Build Release / build-windows (push) Successful in 4m6s
|
||
|
|
75ca487be1 |
feat(ui): per-surface card presentation modes (C/M/D/R)
Adds a comfortable/compact/dense/row toggle to every card grid in the
app. Each surface (LED devices, targets, automations, scenes, sources,
streams, dashboard subsections, etc.) remembers its mode independently.
Persistence mirrors dashboard-layout: localStorage cache for first paint,
debounced PUT to /api/v1/preferences/card-modes (new endpoint) for
cross-browser sync. Surface registry is open — any non-empty key
accepted server-side; modes validated against {comfortable, compact,
dense, row}.
CSS is token-driven: grid min-width and gap come from --card-grid-min /
--card-grid-gap / --card-grid-min-narrow / --card-grid-gap-narrow /
--templates-grid-min / --templates-grid-gap defined on :root, overridden
per [data-card-mode]. Dense/row also hide .mod-leds, collapse secondary
button labels, and tighten .mod-metrics; row collapses the grid to one
full-width column. Coexists with the existing per-section [data-density]
on the dashboard tab — different attribute, additive concern.
Toggle UI auto-mounts into every CardSection header (18+ surfaces) plus
the six dashboard subsections via post-render mount; teardown tracking
keeps the listener Set bounded across re-renders.
i18n: card_mode.{tooltip,comfortable,compact,dense,row} in en/ru/zh.
Tests: 9 new cases in tests/test_preferences_card_modes_api.py covering
defaults, round-trip, validation, open-registry keys, row mode, delete.
|
||
|
|
e65dcb41f4 |
chore: clean up cfg abbreviation and stale TODO link
Rename `cfg` parameter/local in resolve_mqtt_password to `config` for PEP 8 compliance. Drop the broken reference to the long-removed docs/plans/device-typed-configs.md from TODO.md. |
||
|
|
6a07a6b1a2 |
fix(shutdown): apply target stop actions before tearing down HA/MQTT
Reorder the lifespan shutdown so processor_manager.stop_all() runs before ha_manager.shutdown(), mqtt_manager.shutdown(), and mqtt_service.stop(). HA-light targets check `_ha_runtime.is_connected` before applying their `stop_action` (turn_off / restore) and silently skip when HA is already disconnected; MQTT-output devices need the broker connection alive to send restore frames. The previous order tore those down first, turning "stop_targets" into a no-op for those targets — most visible when closing via the tray Shutdown button. Also moves automation_engine.stop(), discovery_watcher.stop(), and the OS notification listener stop ahead of processor stop so they can no longer fire events into a shutting-down processor manager. Independent services (weather, update checker, auto-backup) now run last, where their order does not matter. Bonus: if the daemon-thread join times out (10 s) and the rest of shutdown is cut short, the user-visible part — targets stopping — has already run. |
||
|
|
0f5850ef80 |
feat(ui): customisable card icon for all entity types
Extends the icon-plate work from devices and output targets to every
remaining card type — 18 new entities, 20 in total. Users can now pick
a curated icon (with optional colour override) for any card on any tab,
and the picker reuses the same modal, recent-strip, search, and
category tabs introduced for the device picker.
Foundation:
- icon-picker.ts — replace the hardcoded 2-entry adapter record with a
Map<EntityType, EntityTypeAdapter> and expose
registerIconEntityType() + makeSimpleIconAdapter() so each feature
module owns its own adapter (~6 lines per type).
- bodyExtras hook on adapters, keyed off id, lets discriminated routes
(output-targets target_type, picture-sources stream_type, audio /
value / color-strip-sources source_type) accept icon-only PUTs.
- core/card-icon.ts — new makeCardIconFields(type, id, entity) helper
spreads iconHtml / iconColor / iconAttrs into a mod-card head in one
line.
- _onDocumentClick now accepts any registered type instead of a
hardcoded device/target check.
Backend (purely additive — no migrations needed thanks to JSON-blob
storage):
- 18 dataclasses gained icon: str = "" + icon_color: str = "" with
emit-when-truthy serialisation and "" defaults on load.
- All matching Create / Update / Response Pydantic schemas gained the
fields with the standard Optional[str] + max_length=64/32 +
description set.
- All routes' response builders use
getattr(entity, "icon", "") or "" so existing rows render unchanged.
- ValueSource and CSS handle icon/icon_color on the base class so all
source-type subclasses inherit them automatically.
Frontend wiring (12 modules):
- streams.ts — picture sources, capture templates, PP templates,
CSPT, audio sources, audio templates, gradients (built-in
gradients keep no plate).
- automations, scene-presets, sync-clocks, weather-sources,
value-sources, mqtt-sources, home-assistant-sources,
game-integration, audio-processing-templates, assets,
color-strips/cards.
- pattern-templates skipped — uses the legacy wrapCard({content,
actions}) string API, separate migration.
Dashboard cards now also display the chosen icon:
- Targets already had it (with device inheritance for LED targets).
- Sync clocks, automations, and scene presets gained the same plate
via a shared _dashboardIconPlate helper that mirrors the mod-card
layout (mod-head--with-icon class flips on when present).
i18n: 20 new device.icon.entity.<type> labels in en/ru/zh.
Verification:
- ruff check src/ tests/ — clean.
- npx tsc --noEmit — clean.
- npm run build — 2.6 MB bundle.
- pytest tests/ --no-cov — 949 passed (no regressions).
Pending: manual smoke test on each card type — open picker, save, and
confirm the channel-color preview matches the live card.
|
||
|
|
a79f4bf73c |
feat(ha-light): broadcast a single Color Value Source to all entities
HALightOutputTarget gains a `source_kind` field with two modes: - `css` (existing): per-mapping LED segments averaged from a ColorStripSource. - `color_vs` (new): one colour from a colour-returning ValueSource pushed to every mapped entity (mapping LED ranges are ignored in this mode). Backend wiring: - Schema/route: add `source_kind` + `color_value_source_id` to create/update/ response payloads, with VS existence + return_type=color validation. - Storage: persist new fields, with defensive `or ""` coalesce so legacy rows written via resolve_ref with None survive the str-typed response schema. - Processor: ha_light_target_processor reworked to drive both source kinds (incl. update_target_settings hot-swap of source mode). New unit tests in tests/core/test_ha_light_target_processor.py and extended store tests. Frontend: - ha-light editor modal: collapsed Color Strip + Color VS into one "Color Source" picker with grouped headers; mappings list shows a mode-aware hint when broadcasting a single colour. - EntityPalette: support non-selectable header rows (with keyboard / filter handling) for grouped source pickers. Bundled UI polish (icon inheritance + cleanup): - Custom card icons now flow into more surfaces: command palette, dashboard target cards, scene-preset target picker, calibration test-device picker, and the LED-target device picker. LED targets inherit their device's icon when none is set on the target itself. - Empty mod-card icon plates render as a dashed "+" placeholder when an icon-picker hook is wired, so the action stays discoverable. - Icon picker: distinct "HA light target" eyebrow label and supports HA-light cards (data-ha-target-id) for channel-colour resolution. - Update banner: "View release" now opens the in-app Update settings tab instead of an external link; uses the sparkles icon. - Color-strip delete: cleaner toast on 409 conflict. |
||
|
|
ced72fc864 |
feat(targets): customisable card icon + HA-light stop action
Extends the icon-plate work from the device cards to LED and HA-light output targets, and adds finalization behaviour for HA-light targets. Targets: - Add `icon` and `icon_color` fields to OutputTarget (LED + HA-light). - LED target cards inherit the icon from their referenced device when no override is set; the icon picker shows an "inherited" indicator. - Promote the device link from the meta line to a chip with the device's custom icon, leaving the head row free for the icon plate. HA-light: - New `stop_action` field with three modes: `none` / `turn_off` / `restore`. Processor snapshots mapped-entity states at start and applies the chosen action on stop (rgb / hs / color_temp / brightness restored where present). - Editor modal exposes the choice via an IconSelect of three modes. Adjacent fixes: - Fader slider hit-zone now overlays the visible track exactly, regardless of label/value column widths. - Dashboard customise drag-drop indicator splits into before/after rather than highlighting the whole row. - Picture-source EntitySelect resyncs its visible value on load. |
||
|
|
49ddabbc36 |
feat(ui): customisable card icon plate for devices
A user-chosen icon ("mouse", "motherboard", "keyboard"…) renders as a
44x44 instrument-panel face plate at the leading edge of .mod-head on
device cards. Optional per-card; null hides the plate and reverts to
the existing badge-only head.
- Storage/schema: new icon, icon_color fields on Device + DeviceUpdate /
DeviceResponse. SQLite stores entities as a JSON blob, so no migration
is needed; from_dict defaults handle existing rows.
- Curated 47-icon library across six categories (Hardware / Lighting /
Rooms / Media / Signal / Ambience), reusing the existing Lucide path
module; adds circuit-board, bed, armchair, leaf paths.
- mod-card.ts: ModHeadOpts gains iconHtml / iconColor / iconAttrs;
ModMenuItemOpts gains optional dataAttrs. The plate is rendered when
iconHtml is supplied; otherwise no layout change.
- Picker modal (icon-picker.html + features/icon-picker.ts): live
preview, search, six category tabs, recent strip, channel-color
override toggle. Wired through document-level click delegation on
[data-icon-picker-trigger="<deviceId>"] — no window globals, no
inline onclick string. Sets the precedent for migrating other card
actions off window in a follow-up.
- en/ru/zh locales for picker UI + categories.
Includes a docs/ mockup that's the source-of-truth for the design.
|
||
|
|
a026f0b349 |
ci(android): fail-fast on missing release keystore before SDK setup
Move the keystore guard from after the Decode step (step 9) to right after Resolve build label (step 3). A release tag pushed without ANDROID_KEYSTORE_BASE64 configured now fails in seconds instead of after JDK + Python + Android SDK + NDK install (~3-5 min of wasted runner time). Switched the condition from steps.keystore.outputs.present to env.ANDROID_KEYSTORE_BASE64 since the env var is set at job level and the keystore decode step has not yet run at the new position. |
||
|
|
5ef6ac1317 |
chore: release v0.6.0
Build Release / create-release (push) Successful in 3s
Build Android APK / build-android (push) Failing after 3m52s
Build Release / build-linux (push) Successful in 5m20s
Build Release / build-docker (push) Successful in 6m20s
Build Release / build-windows (push) Successful in 7m7s
|
||
|
|
0980cf4dde |
fix(ui): audio-source modal — preserve device on refresh, relocate refresh action
- Move the device refresh button into the label row next to "Audio Device:" so it can no longer overflow the Source panel edge; introduces a small .label-row-action style alongside .hint-toggle. - Restore device selection after refresh by matching on (index, loopback) value first, with a trimmed name fallback for OS-side reindexing. - _selectAudioDevice now syncs the EntitySelect trigger so the visible label matches the underlying <select> when the modal opens in edit mode. - Drop unused min-width/overflow on .transport-status. |
||
|
|
fdac26b9d9 |
feat: daylight tz, camera engine, value stream + modal/UI polish
- daylight: new daylight_settings module + daylight-tz frontend helper; expanded daylight_stream behavior - camera engine: capture path additions plus new test_camera_engine suite - value stream: schema + processing updates (~178 lines) - color strip: drop cycle effect (cycle.py / color-cycle.ts removed), tighten static path - modal CSS: large refactor (+883), components.css polish (+110) - templates: settings, css-editor, value-source-editor, test-template, display-picker, image-lightbox - frontend core: state, modal, icons, graph-nodes, app - frontend features: displays, streams, streams-capture-templates, value-sources, settings, color-strips/cards - locales: en/ru/zh - storage: color_strip, picture_source, value_source loaders touched - preferences/sync_clocks/picture_sources routes; home_assistant + templates schemas |
||
|
|
816a27db73 |
refactor(ui): drop app footer, move author info to About panel
The "Created by Alexei Dolgolyov..." line lived in a global app footer that took up vertical space on every page. Move the author + contact details into the About tab of the global settings modal (rendered by renderAboutPanel), where they sit next to the version pill and license. Adds a localized "donation.about_author" key (en/ru/zh) and matching .about-hero .about-author styles. Removes the now-unused .app-footer / .footer-content rules. |
||
|
|
797b806972 |
feat: LED hot-path perf, tutorials expansion, modal markup polish
Performance (LED hot path, allocation-free per-frame): - Adalight: dedicated single-worker tx executor (avoids asyncio.to_thread overhead), pre-allocated wire buffer + uint8 scratch, header struct precomputed. New tests cover header format, buffer reuse, non-contiguous input, and brightness scaling. - DDP: pre-built struct.Struct for the 10-byte header, allocation-free send buffer + memoryview emit path. New tests cover RGB/RGBW packets, sequence/PUSH semantics, and multi-packet fragmentation. - Calibration: precomputed Phase 3 skip-LED resampling (floor/ceil indices, fractional weights, take/blend scratch buffers) — per-frame work is now np.take + in-place blend, no allocations. - WLED target processor: matching hot-path tightening. Tutorials: - Sub-tab switching, breadcrumb header, and prepare/switchSubTab hooks so a tour can open/close the dashboard customize panel and resolve targets behind sub-tabs. - New steps for integrations tab, dashboard customize panel (presets, global, sections, perf cells), targets, scenes, sync-clocks. - en/ru/zh locales updated with the new tour strings. Dashboard layout: - Structural deep-equal so the "modified" indicator reflects truth after a user edits then reverts, instead of a stale flag. UI polish: - Mod-card / modal markup pass across ~33 modal templates and the tutorial overlay partial. - appearance.css, modal.css, tutorials.css refresh. Tooling: - Add .mcp.json with code-review-graph MCP server config so the graph tools are available to the team out of the box. |
||
|
|
9d4a534ec6 |
feat(ui): release notes overlay v2 + settings/streams/dashboard polish
Release Notes overlay redesign (scoped via .release-notes-shell)
- Backend exposes release.assets (name/size/download_url) through
UpdateReleaseInfo so the frontend can render real download links.
- New masthead: eyebrow + display-font title + tag/published/pre-release
chip strip + close/external action buttons; opts out of layout.css's
global `header { height: 60px }` and `header::before` accent bar that
were leaking into the overlay's <header>.
- Markdown body: <code> filenames are wrapped in clickable <a> via fuzzy
asset match (exact basename, then same-extension token-overlap), with
per-asset description tooltip and a small download glyph.
- Per-asset description derived from filename pattern (Windows installer
/portable/msi, Linux tarball/AppImage/deb/rpm, macOS dmg/pkg, Android
apk/aab, iOS ipa, generic archives) with i18n keys in en/ru/zh.
- Hide checksum / signature side-files (.sha256/.sha512/.sig/.asc/...).
Settings modal & dashboard polish
- ds-section refresh, rail-channel routing, notif matrix updates.
- Dashboard customize panel + per-account layout updates.
- New docs/settings-modal-redesign.html design reference.
Streams / targets / color-strip
- Stream cards rewrite (cards.css, streams.css, streams.ts).
- Composite stream + metrics history adjustments.
- WLED target processor + color-strip pipeline refinements.
- Color-strip WS source streamer touch-ups.
Misc
- Perf charts overhaul; tabular game-integration / HA / MQTT / weather
source cards; donation/sync-clocks/scene-presets minor polish.
- New i18n keys across en/ru/zh.
Test infrastructure
- conftest pre-creates the test DB so main.py's legacy-data migration
doesn't shovel the user's production DB into the test temp dir.
- test_preferences_notifications wipes its own setting at the start of
the defaults test (was relying on isolation it never enforced).
Pre-commit gates: ruff clean, tsc clean, npm run build clean,
pytest 899/899 passing.
|
||
|
|
51eebf21d5 |
feat(ui): redesign target pipeline as compact strip + chip row
Drops the legacy "Pipeline details" collapsible block on running LED target cards. Instead: - Always-visible 4px segmented timing bar (extract / map / smooth / send for video, read / fft / render / send for audio) — same stage colors as before, scaled by per-segment ms cost - One chip row beneath it: total ms / frames count / keepalive count, using a new .chip--inline variant (display-weighted number + tiny mono-caps unit) - _patchTargetMetrics now writes only the bar's segments and the data-tm spans — bar wrapper survives across polls so the flex-transition animates smoothly between samples - _buildLedTimingHTML replaced by _buildLedTimingSegments (no more header / total / legend wrappers — those live in the chip row) Cleanup - Drop .target-metrics-collapse / -toggle / -animate / -expanded CSS — no callers remain - Drop targets.metrics.pipeline from en/ru/zh locales — toggle label is gone |
||
|
|
9067db2639 |
feat(ui): align Targets metric cells with dashboard pattern
mod-card.ts - ModMetricOpts.extra: raw HTML appended after the .v cell — used to embed a sparkline canvas inside the FPS metric block - ModMetricOpts.valueDataAttrs: data-attrs on the .v element so live-update patchers can target the value directly LED target card - FPS sparkline (mod-metric-spark-canvas) is embedded INSIDE the FPS cell as a sibling of .v — was a separate target-fps-row block before, which floated under the metrics grid - Label hardcoded to "FPS" (the i18n value "Target FPS:" was meant for the editor field, not the readout) - Uptime cell gets ICON_CLOCK; Errors cell gets ICON_OK / ICON_WARNING based on count — matches dashboard cell decorations - Drops the leading FPS icon (display-font number is the focal element; no icon needed) - _patchTargetMetrics now emits the dashboard FPS shape: current<span.dashboard-fps-target>/target</span> <span.dashboard-fps-avg>avg N.N</span> — picks up dashboard.css styling for free HA Light target card - Same icon treatment (Uptime → clock; HA → ok/warning by ha_connected); FPS icon dropped Grid sizing - .devices-grid bumped from minmax(300px, 1fr) / gap 20px to minmax(min(380px, 100%), 1fr) / gap 14px — matches the dashboard's section grid so metric values like "1m 43s" stop truncating at the typical desktop width |
||
|
|
233b463ac3 |
feat(ui): migrate Targets cards to mod-card system
LED targets and HA Light targets adopt the dashboard's instrument- readout vocabulary (mod-head, mod-leds, mod-metrics, mod-foot, mod-patch, mod-btn, kebab menu) — same classes and tokens already used by Dashboard and device cards. mod-card.ts - ModBtnOpts.dataAttrs for arbitrary data-attrs (used by the LED preview toggle's data-led-preview-btn binding) - ModBodyOpts.extraHtml escape-hatch for live-update widgets that don't fit the predefined slots (FPS sparkline canvas, entity swatch grid, collapsible pipeline metrics) LED target card (targets.ts) - Badge "LED · TGT" pairs with device "WLED · OUT"-style badges - Meta row: device link → protocol badge → fps → pixel count - LED bezel: 1-3 dots reflecting checking / streaming / online / offline / unreachable - Headline metrics on running cards (FPS / ERR / UPTIME) preserve data-tm selectors so _patchTargetMetrics still patches in place - Chips for CSS source link, brightness/value-source, threshold - Patch indicator: STREAMING / UNREACHABLE / STANDBY / OFFLINE / CHECKING - Foot: START/STOP go/stop variant + LED preview + Edit - Kebab menu: Duplicate / Hide / Delete (replaces top-right trash) - FPS sparkline + collapsible pipeline preserved via extraHtml - Tag chips and LED preview panel appended after wrap (mirrors devices.ts pattern) HA Light target card (ha-light-targets.ts) - Badge "HA · LIGHT" - Meta: HA source link → light count → update rate - LEDs: blink running, fault when ha_connected === false, off idle - Running metrics: RATE / UPTIME / HA status - Patch: STREAMING / DISCONNECTED / STANDBY / NOT CONFIGURED - Buttons keep [data-action] for initHALightTargetDelegation - Live entity color swatches preserved via extraHtml Misc - Chip border-radius dropped from 999px (pill) to var(--lux-r-sm, 3px) — sharp corners match badges/metrics/buttons elsewhere - _patchTargetMetrics FPS readout uses <small> for the target fraction instead of the legacy target-fps-target span |
||
|
|
de13f44f24 |
feat(autostart): suppress browser auto-open on Windows login
When the user enables "Start with Windows" in the installer, the app launches on every PC login. Previously each login popped a fresh WebUI tab, which is noisy for a tray-resident background service. The autostart shortcut now passes --autostart to start-hidden.vbs, which sets LEDGRAB_AUTOSTART=1 in the child env. __main__ checks this flag alongside LEDGRAB_RESTART when deciding whether to open the browser. Manual launches (desktop/start-menu shortcuts) and the installer's post-install "Launch LedGrab" finish-page action are unchanged — they don't pass the arg, so they still open the WebUI tab. |
||
|
|
1c9acc5afb |
feat(api-input): make SegmentPayload start/length optional
start defaults to 0, length defaults to led_count - start (the rest of the strip from start). A single segment with only mode + color now fills the entire strip — no more length: 9999 magic value clients have to pass. Buffer auto-grow only fires for segments with an explicit length past the current end; implicit "to the end" segments adapt to the current strip size. |
||
|
|
a56569b02f |
feat(ui): cards redesign + settings, modal, toolbar polish
Dashboard cards (mod-card system) - New mod-card / mod-menu modules backing dashboard cards - Reworked card colors, sections, dashboard layout, perf charts - Channel-stripe styling, hairline borders, signal-flow animation on running cards, refined metric grid Multiselect bulk toolbar - Replaced tri-state checkbox with explicit Select-all / Deselect-all icon buttons; both disable when not applicable - Dim + slight blur on non-selected siblings during selection mode so the active picks pop; selected card gains a subtle lift + primary-color glow halo - Bulk tick uses ICON_CHECK from the icon registry (was U+2713) and scale-pops in via a cubic-bezier overshoot keyframe - Toolbar restyled with luxury gradient bg, top accent stripe, glass blur, neon hover glows on each button group Settings modal - Tab bar converted to icon-only (cog / hard-drive / bell / palette / refresh / help) so labels never overflow at any locale; title and aria-label preserve translated names. Tabs distribute evenly via flex: 1 1 0 + space-around — no overflow possible - IconSelect auto-populates <option> elements when the underlying select is empty, fixing the blank notification triggers (root cause: setting .value on an empty select is a no-op) - Tab activation calls scrollIntoView on the active button as a safety net for narrow viewports Modal exit animation - Added symmetric fadeOut + slideDown keyframes; .modal.closing applies them with animation-fill-mode: forwards - Modal.forceClose() defers display:none until animationend (with timer fallback). State cleanup (focus, body lock, stack) runs immediately so callers querying state get correct values - isOpen returns false during the close animation; open() cancels any in-flight close so re-open works during the animation - prefers-reduced-motion disables all modal animations Locale picker - Dropped redundant English/Русский/中文 long-form labels — picker now shows only EN / RU / ZH - IconSelect trigger/cell hides empty icon/label slots via :empty so the layout collapses cleanly for minimal items Filter input (cards section) - Embedded magnifier icon via data URI (no HTML change); monospace uppercase placeholder, lux-bg-0 background, neon focus ring with inset shadow + outer glow - Reset button only shows when the input has content (CSS-only via :placeholder-shown sibling selector — JS-resilient) Snack toast - Glass background (gradient + backdrop-blur) with top channel-color accent stripe matching the modal/toolbar language - Per-type --toast-ch drives border/glow/timer color (success → primary, error → danger, info → info) - Undo button gets a tinted hover with channel-color halo Top header toolbar - Removed hairline border from .header-btn for a flatter look; hover keeps the subtle background tint and primary-color glow Device URL hyperlink - Styled .mod-meta__link to pick up the card's --ch accent (instead of inheriting browser-blue underline). Dotted underline at rest solidifies on hover; soft text-shadow glow; web icon dims at rest, brightens on hover Misc - ICON_CHECK and ICON_HARD_DRIVE added to the icon registry - Existing card-redesign demos checked in under docs/ - Removed obsolete docs/plans/device-typed-configs.md |
||
|
|
ccf4406349 |
Merge branch 'feat/device-event-notifications'
Configurable device-event notifications: snackbar + Web Notifications for online/offline (configured targets) and discovery (new WLED/serial on the LAN/USB) events, with per-event channel matrix and background discovery toggle. |
||
|
|
8aa3a323d6 |
feat(notifications): device event notifications (snack + Web Notifications)
Surface device connection state changes (configured target online/offline) and discovery events (new WLED on LAN, new serial port, devices that disappear) through a configurable per-event channel matrix: none / snack / OS / both. - Backend: long-running mDNS browser + 10 s serial poller in core/devices/discovery_watcher.py, gated by user pref. Reuses the existing device_health_changed event for online/offline transitions. New GET/PUT /api/v1/preferences/notifications endpoint with Pydantic v2 schema (channel matrix + background-discovery flag + grace/debounce). 13 new tests, full suite still 899 passing. - Frontend: features/notifications-watcher.ts with startup-grace + flap-debounce + bulk-coalesce pipeline. Web Notifications API for the OS channel (no platform-specific code, works in PWA shell). New "Notifications" tab in Settings with 4 IconSelect rows + bg toggle + permission row + test button. en/ru/zh translations. Defaults: device_offline=both (urgent), online/discovered=snack, lost=none, background discovery on. Already-configured devices are filtered from discovery events to avoid double-notifications. |
||
|
|
8e109f32b9 |
fix(pwa): add mobile-web-app-capable meta tag
Chrome deprecated apple-mobile-web-app-capable in favor of the standard mobile-web-app-capable. Add the new tag while keeping the Apple variant for iOS Safari compatibility. |
||
|
|
033c1f6a92 |
ci: add workflow_dispatch and skip lint/test on release commits
Release-bump commits don't change code that affects lint/tests, and release.yml already runs in parallel. Manual dispatch lets us re-run on demand if needed. |
||
|
|
0804f54537 |
chore: release v0.5.0
Build Release / create-release (push) Successful in 3s
Build Android APK / build-android (push) Failing after 2m16s
Build Release / build-linux (push) Successful in 3m54s
Build Release / build-docker (push) Successful in 7m30s
Build Release / build-windows (push) Successful in 8m37s
Lint & Test / test (push) Successful in 8m45s
|
||
|
|
66f921c07f |
Merge branch 'feat/lumenworks-ui-redesign'
Lint & Test / test (push) Successful in 2m36s
Lumenworks studio-console redesign + per-account dashboard customization + Inputs/Integrations/Graph treatment + transport-bar uptime + server shutdown action. Sub-features (in order on the branch): - feat(ui): Lumenworks tokens, fonts, transport bar, channel-strip sidebar - feat(ui): dashboard polish, perf strip, transport-bar controls - feat(dashboard): per-account customizable dashboard with slide-in panel - feat(ui): item-card restyle, perf hover tooltips, FPS ceiling - feat(ui): Lumenworks treatment for Inputs / Integrations / Graph tabs - fix(ui): cards on pure black/white, decoupled from bg-anim - fix(ui): single-row header + readable sidebar labels at narrow widths - feat: server shutdown action with public cancel_task lifecycle method - feat(ui): live card-color picker, monotonic uptime ticker, default preset uses base palette - fix(ui): channel stripe paints only on custom-color or running cards - chore: harden test isolation, gitignore stale src/data, mark TODO done Pre-merge audit: - 886/886 pytest passed twice in a row - ruff + tsc clean - frontend bundle rebuilt at static/dist - python package reinstalled in editable mode (dev WebUI now reports 0.4.2 instead of stale 0.3.0 dist-info) |
||
|
|
80f01d4813 |
chore: harden test isolation, gitignore stale src/data, mark shutdown action done
- ``tests/test_preferences_api.py`` no longer captures the auth API key at module-import time. The new ``client`` fixture resolves it inside its body and bakes the Bearer header into ``TestClient.headers``, so the e2e conftest swapping the global config singleton during collection cannot leave the test holding a stale 401-bound header. Same proven pattern as ``test_audio_processing_templates_api.py``. - ``.gitignore`` now anchors ``/server/src/data/`` defensively. If the server is launched from ``server/src/`` (uncommon but possible during ad-hoc debugging), its relative ``data/`` resolves there. Templates now live in SQLite (``capture_templates`` / ``pattern_templates`` / ``postprocessing_templates`` tables); any stale ``*.json`` that lands in that directory is a runtime export and must not be committed. - Three such stale exports were untracked at the start of the pre-merge audit and have been deleted from the working tree. - ``TODO.md`` flips the shutdown-action checklist to done and notes that real-hardware verification (WLED + serial after Ctrl+C) is still pending. |
||
|
|
b1ee3c3942 |
fix(ui): channel stripe paints only on custom-color or running cards
Reported during pre-merge review of the Lumenworks redesign: non-dashboard cards (Inputs, Integrations, Targets) all showed aggressive cyan / green left stripes "regardless of custom color set or not", and even the ``+`` Add card carried a stripe. Root cause: ``.template-card`` defaulted to ``--ch: cyan`` and ``::before`` painted it unconditionally; ``.add-template-card`` inherits ``.template-card`` so it picked the stripe up too. Fix: gate the ``::before`` channel stripe behind opt-in selectors. It now paints only when the card carries ``data-has-color="1"`` (the user picked a personal colour via the picker, set by ``wrapCard``) or has the ``card-running`` class (the "patched and live" indicator). Dashboard module rows are unchanged — their ``::before`` already runs at ``opacity: 0.6`` and was approved as the visual benchmark. ``add-template-card::before`` is hidden unconditionally with ``!important`` since the Add card is not an entity and should never carry a channel hue. |
||
|
|
e0ff40f4f5 |
feat(ui): live card-color picker, monotonic uptime ticker tweaks, default preset uses base palette
Three adjacent UI fixes that surfaced while soaking the Lumenworks redesign: - ``card-colors.ts`` now writes the user's picked color through to *every* card representing the same entity (e.g. the targets-tab card AND its dashboard mirror), not just the one that owns the picker. Sets the ``--ch`` custom property on each match instead of a literal ``border-left``, which avoided the double-stripe (custom border + Lumenworks ``::before`` channel stripe) the old approach produced and reaches mirrors the picker callback's ``.closest()`` lookup couldn't. - ``appearance.ts`` "default" preset now *clears* its colour overrides instead of stamping the historic muted greys (#1a1a1a / #2d2d2d / #f5f5f5 / #ffffff). With the redesign's pure-black / pure-white base palette in ``base.css``, "default" should mean "use the base" — the preset swatch in Appearance now matches what ships out of the box. Existing users with "default" selected will see a one-time visual shift to the new neutrals on next reload; this is intentional. - ``dashboard.css`` mod-metric label row gets explicit sizing for the small status glyphs (clock / check / warning) so they sit beside the mono-caps label without competing with the big value. Errors cell picks up the coral channel tint when the count is non-zero. |
||
|
|
3f80ef2101 |
feat: server shutdown action with public cancel_task lifecycle method
Lets users choose what happens to LED targets when the server shuts
down. Default ("stop_targets") runs the existing per-device stop
sequence, so devices with auto-restore replay their prior state.
"Nothing" cancels the capture tasks without sending restore frames,
so the LEDs keep displaying their last frame on shutdown.
Backend:
- New setting ``shutdown_action`` persisted in db.settings
(``stop_targets`` default | ``nothing``) with GET/PUT
``/api/v1/system/shutdown-action`` endpoints
- ``ProcessorManager.stop_all(restore_devices: bool = True)`` now
picks the path based on the flag — ``proc.stop()`` for the normal
branch, public ``proc.cancel_task()`` for the "nothing" branch.
- ``TargetProcessor.cancel_task()`` (new, on the abstract base) cancels
the loop task and *awaits* its termination so no half-written frame
is in flight when the process exits. Replaces an earlier draft that
reached into the private ``_task`` attribute via ``getattr``.
- Lifespan in ``main.py`` reads the setting at shutdown and forwards
the flag; falls back to ``stop_targets`` on any read error.
- ``/health`` exposes ``uptime_seconds`` (process-wide monotonic clock
captured at first import of ``api.routes.system``) so the WebUI can
show the *server's* uptime instead of the browser session's.
Browser launch:
- ``__main__._open_browser`` now polls ``/health`` for up to 30 s
instead of sleeping a flat 2 s, so the tab opens once the server
actually accepts requests.
Frontend:
- New "Shutdown action" picker in Settings → General, rendered via
IconSelect with ICON_SQUARE / ICON_CIRCLE (added to ``core/icons.ts``
+ ``circle`` path to ``icon-paths.ts``).
- Transport-bar uptime ticker reads ``window.__serverUptime`` (typed
in ``global.d.ts``); shows "—" until the first /health response
lands so refresh doesn't briefly flash 00:00:00. After 99 h the
format widens to "Dd HH:MM:SS".
- New i18n keys for the action picker (label, hint, opt.stop /
opt.nothing + descriptions, saved / save_error toasts) in en/ru/zh.
No data migration needed — the setting is additive and defaults to
the existing behavior.
|
||
|
|
2bae304107 |
fix(ui): single-row header + readable sidebar labels at narrow widths
At ≤1100px the header grid only declared 3 tracks for 4 children, so the toolbar wrapped to a second row, doubling the header height. Add a 4th track, tighten the meta cluster, and hide non-essential toolbar items (API link, tour-restart) so everything fits in one row. At ≤900px drop CPU/Mem cells (Uptime + Poll remain) so the toolbar still fits beside the meta cluster. Sidebar tab captions on the 56 px icon rail were ellipsis-truncated to "DASHBO…" / "AUTOMA…" / "INTEGR…". Switch to a 2-line clamp with tighter font/tracking so each label renders in full. |
||
|
|
dd415e2813 |
fix(ui): cards on pure black/white, decoupled from bg-anim
Three related fixes after the Phase-4 migration landed:
- `--card-bg` flipped from `#101216` / `#f5f6f8` to pure `#000000` /
`#ffffff` in base.css. Off-pure greys read as muddy when sitting on a
pure-black/white page background; pure values keep card surfaces flush
with the rest of the chrome and let the channel stripe + corner
bracket carry all the visual differentiation.
- Removed the `[data-bg-anim="on"] .card { background: rgba(...) }`
block that turned every entity card translucent whenever the WebGL
background was enabled. Card backgrounds are now stable across the
toggle — the shader bleeds through `body { background: transparent }`
only, not through cards. The same card now reads identically with the
shader on or off.
- WebGL shader base colour (`_bgColor` in bg-anim.ts and bg-shaders.ts)
was using the legacy mid-grey `#1a1a1a` / `#f5f5f5`. That added a
constant grey haze under the additive accent glow that didn't exist
on the surrounding pure-black/white page. Switched to `[0,0,0]` /
`[1,1,1]` so the shader composes against the same base as the page.
- Reverted two leftovers from the Phase-4 commit where I had migrated
`.template-card` and `.graph-node-body` away from `var(--card-bg)`
toward `var(--lux-bg-1, …)`. Those backgrounds now live on
`var(--card-bg)` again, matching every other migrated card.
|
||
|
|
b43e1cf375 |
feat(ui): Lumenworks treatment for Inputs / Integrations / Graph tabs
Brings the remaining tabs in line with the Channels-tab visual language: - .template-card now mirrors .card and .dashboard-target — channel stripe on the left edge with glow, silkscreened corner bracket top-right, hairline border on --lux-bg-1, hover lift + stripe widen-and-glow. Covers streams, capture / pp / cspt / pattern / audio templates and every Integrations card (HA / MQTT / weather / value / sync clocks / game integrations). - Channel mapping extended in cards.css. Direct attribute hooks for the per-domain ids; section-scoped hooks via [data-card-section="…"] for the cards that share a generic data-id (HA / MQTT / weather / value → cyan, game-integrations → amber, sync-clocks → violet, HA-light-targets → signal). No JS changes — uses the section markup CardSection.render already emits. - Graph editor nodes pick up the studio-console palette: --lux-bg-1 fill with hairline stroke, hover bold-line, selected/running stroke --ch-signal with drop-shadow glow. Title font moved off Big Shoulders Display (which read as "stretched" at 12 px) onto --font-body (Manrope); subtitle keeps the mono-uppercase caption treatment with a conservative letter-spacing. Running gradient now rides the channel palette (signal → cyan → signal) rather than the legacy primary / success colours. Port labels and grid dots adopt --lux-line tokens. - Graph node titles get real text-overflow:ellipsis behaviour. SVG <text> can't do that natively, so renderNodes runs a post-mount fit pass that binary-searches the longest character prefix that fits inside the clip rect (with 2 px slack), suffixed with "…". Trailing whitespace is stripped before the ellipsis so we never get "Foo …". Full text is stashed on data-full-text so the fit can be re-run on re-renders. Also bundles two perf-charts fixes from the same session: - Hover regression — listener was bound to .perf-charts-grid, which rerenderPerfGrid() replaces. Moved to document.body with a guard, and the cursor → sample math now uses the same sliceN as the spark rendering so the tooltip stays accurate when the user changes the window setting. - Color picker on every perf cell. Patches / Total FPS / Devices now expose the same color picker as the spark cells; defaults added to METRIC_CSS_VARS. Each card gets an inline --perf-accent on render so saved colours apply immediately, including across rerenderPerfGrid. |
||
|
|
56853b7123 |
feat(dashboard): per-account customizable dashboard with slide-in panel
Open-registry section/perf-cell schema persisted server-side under
db.get_setting('dashboard_layout'); localStorage cache for instant
first-paint, server sync after auth. 5 built-in presets
(Studio/Operator/Showrunner/Diagnostics/TV); JSON export/import.
Slide-in Customize panel toggles section + perf-cell visibility,
reorders via hand-rolled HTML5 drag (with up/down buttons for
keyboard/TV-remote use), changes density per section, and exposes
global Width / Animations / Perf-mode / Window with per-cell Inherit
overrides.
Window setting now drives the actual sparkline slice (30s/1m/2m/5m at
configurable poll interval) instead of always rendering 120 fixed
samples. Perf-grid edits re-render in place — sparklines repaint from
persistent module-level history, value labels replay from cached
last-fetch payload, so there is no flicker frame and no zero-data
window between layout change and next poll. initPerfCharts now fires
an immediate fetch on init so reload no longer shows "—" until the
first interval tick.
Reset confirmation uses the project's themed showConfirm modal
instead of the browser dialog. Reserved registry keys (audio-meters,
alerts, led-preview, source-thumbs, pinned, flow) are forward-
compatible so v1.1 cards slot in without a schema bump.
Backend exposes GET/PUT/DELETE /api/v1/preferences/dashboard-layout
treating the body as opaque JSON with a numeric version gate; covered
by 6 round-trip / validation / unknown-field tests.
|
||
|
|
70c95d1c09 |
feat(ui): item-card restyle, perf hover tooltips, FPS ceiling
Item cards (Automations, Channels, Inputs, Integrations):
- `.card-title` — bumped to weight 700, -0.01em tracking, solid --lux-ink
for better presence against the flat card bg.
- `.card-subtitle` / `.card-meta` — mono font, 0.04em tracking, tighter
gap so rule chips pack in a readable row.
- `.stream-card-prop` rule chips — rectangular 2px radius + hairline
border + flat dark bg (was rounded 10px grey pill). Channel-signal
icon tint; hover fades in a channel-green wash with matching border.
- `.badge` generic — rectangular 2px radius, mono 0.62rem, 0.12em
tracking, hairline border slot for variants.
- `.badge-automation-active` — channel-signal tinted bg + border +
soft outer glow so the "ACTIVE" state reads at a glance.
- `.badge-automation-inactive` / `-disabled` — transparent with a
hairline outline so they sit quietly alongside the active variant.
- `.device-url-badge` — switched from rounded pill to rectangular
hairline mono chip; hover shifts to filled bg + bolder border +
brighter ink.
- `.card-actions` — 1px hairline top divider, 6px gap.
- `.btn-icon` — 7/10px padding, 1rem icon, hairline border, channel-
signal glow on hover (replaces the old scale(1.1) jiggle).
- `.btn-icon.btn-warning` — amber ink + hairline + amber hover glow
(drives the "disable" action in the automation card).
- `.btn-icon.btn-success` — signal-green ink + hairline + green hover
glow ("enable" action).
Cross-link navigation highlight:
- `cardHighlight` keyframes were using an undefined `--primary-rgb` var,
so the outer glow fell back to 59/130/246 (the Tailwind blue default).
Rewritten with `var(--ch-signal)` + color-mix so the highlight tracks
the accent picker and reads as signal-green. Added double-layer
box-shadow (ring + 32px/10px bloom) so the highlight is obvious on
the flat dark/light card surfaces. Added .dashboard-target to the
selector + `isolation: isolate` so the glow isn't clipped inside
overflow: hidden containers (perf strip cells, tree-nav panels).
Perf strip (follow-up polish):
- Total FPS cell shows `/<N>` ceiling suffix next to the live value —
sum of fps_target across running targets, styled like the Patches
"/12". A dashed horizontal reference line at that ceiling is rendered
on the sparkline so the live value reads as "percentage of max
achievable throughput." Y-axis ceiling grows to targetSum * 1.1 so
the dashed line never clips.
- Removed the empty `.perf-chart-app` pill in the FPS cell (no app
variant). Added `:empty { display: none }` as a safety so any other
unpopulated cell doesn't render a ghost pill.
- Hover tooltips on all sparks — single floating `.perf-chart-tooltip`
in <body> with fixed positioning; event-delegated from the perf
grid so re-renders don't need rebinding. Shows metric label + sys
value + app value (in both-mode) + "−Ns ago" age line derived from
the poll interval. Vertical marker line follows the cursor over the
spark; `cursor: crosshair` on the spark container signals interact-
ability. `pointer-events: none` shifted from the spark container
down to the inner SVG so hover events land on the container.
Grid:
- Perf strip capped at 4 cols even on widescreen; wraps to 2 rows ×
4 when the full 7 cells are present. Responsive breakpoints at
1100 / 760 / 480 px.
- Big value font uses `clamp(1.8rem, 2.8vw, 2.8rem)` so readouts
like "18.9/31.8 GB" fit a 1fr cell at desktop while still scaling
down on narrow viewports. `white-space: nowrap; flex-wrap: nowrap;
overflow: hidden; text-overflow: clip` prevents mid-text wrapping.
- `.perf-chart-spark` uses `margin-top: auto` so sparkline baselines
align across cells regardless of whether a subtitle is present
(CPU/GPU model name, FPS min/max).
Dashboard target meta:
- Integrations card stripe reverted to the default signal color so it
matches the overall accent picker; the health-dot inside the card
carries the connection state. Removed the per-integration channel
override in both cards.css and dashboard.css.
Section headers:
- `.dashboard-section-header` / `.subtab-section-header` underline
switched from dashed to solid; channel-green 40px accent rule on
the left remains.
- Section count badge (`.dashboard-section-count`) restyled to match
the rest of the badge family (mono tabular-nums, 2px radius, hairline
border, --lux-bg-3 fill).
Build: tsc --noEmit clean; CSS bundle stable at ~216 KB.
|
||
|
|
e5a2af9821 |
feat(ui): dashboard polish, richer perf strip, transport-bar controls
Dashboard perf strip:
- Unified rack-module shell with hairline-divided cells (mockup parity)
replacing 3 separate perf cards. Cells auto-wrap to 2 rows of 4 on
widescreen; responsive breakpoints at 1100 / 760 / 480 px.
- Active Patches cell (first) shows running/total channel count plus up
to 4 live FPS readouts with channel-colored stripes; bottom-right
radial glow anchors the "live channel bank" corner.
- Total FPS cell — aggregate throughput across running targets, mono
"fps" unit suffix, session-peak-scaled sparkline with a 60 FPS floor.
- Devices cell — online/total count + per-device dot strip (green when
online with signal-glow, coral when offline, tooltip with name +
latency), fed from /devices/batch/states (added to the dashboard
batch poll).
- Value font uses clamp(1.8rem, 2.8vw, 2.8rem) + white-space: nowrap so
long readouts (RAM "18.9/31.8 GB", GPU "50% · 37°C") scale down
instead of wrapping.
- Sparklines anchor to the cell bottom via margin-top: auto so baselines
align across cells regardless of subtitle presence.
- App-load tag ("APP 3.1%") moved to a pinned top-right position per
card, accent-colored pill; replaces the subdued inline badge.
- Perf mode toggle (System / App / Both) triggers an immediate poll so
positioning updates without waiting for the next tick.
- Chart.js removed from perf-charts — inline SVG sparklines with
drop-shadow filter for the "lit instrument" feel. Chart.js still used
for per-target FPS charts via chart-utils (now owns the registration).
- Fixed history seed bug: app_ram is MB in the server history payload,
not percent — convert to percent using sample's ram_total before
pushing into _appHistory.ram. Skip seeding app_gpu_mem since the
history schema has no gpu_memory_total.
- Temperature card reveals with an explanatory hint when the backend
reports cpu_temp_hint_key (e.g. Windows without LibreHardwareMonitor)
instead of silently hiding; .perf-chart-card-hint neutralizes the big
display font so the message reads as plain body copy.
Transport bar:
- LED brand mark — 28 px, double-layer signal glow (0 22px + 0 8px),
brandPulse animation. Brand-stack wraps the title + version so
"LED GRAB" sits above "V0.3.0" on a single line each.
- Transport status chip — bigger (9/18 padding), mono uppercase,
inner+outer signal glow when .is-armed.
- Transport meta cells — Uptime (JS-local session ticker), CPU (app
CPU share), Mem (app RAM, G/M format) as stacked KEY/VALUE mono
readouts with hairline separators.
- New interactive Poll cell cycles through 1/2/5/10s presets on click;
replaces the range slider that used to live in the Dashboard toolbar
(it controlled the whole app, not just the Dashboard).
- Header icon buttons — hairline-bordered 30 px squares with channel-
glow on hover, replacing the pill container.
- Perf poll moved to global bootstrap so transport CPU / Mem stay live
across all tabs (was paused when leaving the Dashboard).
- Connection pip (#server-status) hidden; the brand mark itself turns
coral when offline via :has() selector on .header-title.
Dashboard cards:
- renderDashboardTarget now emits full rack-module markup with CH badge,
name, meta, LED cluster, 3-cell metric grid (FPS / Uptime / Errors),
and patch-label + stop button. Running cards get the signal-flow
strip at the bottom. data-fps-text / data-uptime-text / data-errors-
text hooks preserved so _updateRunningMetrics updates in place.
- LED count surfaced in the target card meta line (e.g. "LED · WLED ·
144 LED · GRADIENT") when the linked device reports led_count > 0.
- Integrations (HA + MQTT) picked up .mod-head markup — compact module
layout with online/offline patch indicator. Integration card stripe
uses the default signal color (not cyan or amber).
- Scene presets, sync clocks, automations gain the same compact module
treatment. Automations/scenes dropped into a dashboard-autostart-grid
so they share the visual language.
- Perf mode toggle, stream sub-tabs, cs-count / tree-count /
tab-badge / dashboard-section-count badges all use the mono
rectangular style with tabular-nums.
Command palette:
- Flat background (no gradient), channel-accent rule across the top,
mono placeholder / group headers / footer, active result gets a
channel-green left stripe.
Modals:
- Popover + backdrop get a stronger radial dim + 6 px blur.
- Per-modal-ID channel lanes (target→green, source→cyan, audio→magenta,
automation/scene→violet, settings→amber, confirm→coral) via --modal-ch
override.
- Modal header picks up a vertical channel stripe + hairline divider;
footer gets hairline top + subtle wash.
Components:
- Inputs use hairline borders + tabular-nums mono for number fields;
focus state has channel-green ring + soft glow.
- Buttons switch to mono-uppercase with signal-glow on primary,
coral-glow on danger, hairline border on secondary.
- Card background flattened — removed gradient wash in favor of solid
--lux-bg-1 for both dark (#0e1014) and light (#f6f8fb).
- Page background: pure black for dark, pure white for light.
Color-picker:
- Always detaches to <body> with fixed positioning when its swatch sits
inside an overflow: hidden / auto / clip ancestor (perf strip, modal
bodies, tree-dd panels). Prevents the popover getting clipped.
Settings modal:
- Remembers the last-opened tab via localStorage key
settings_active_tab; falls back to 'general' if the tab id no longer
exists. Explicit overrides (donation → about, update badge →
updates) still work because callers invoke switchSettingsTab after
openSettingsModal.
Microcopy:
- Sidebar / transport localization for en/ru/zh:
sidebar.workspaces · transport.meta.{uptime,cpu,mem,poll,poll_hint}
· transport.status.{ready,armed} · dashboard.perf.{active_patches,
total_fps,devices}
Backend (coordinated with frontend):
- /system/performance now returns cpu_temp_hint_key when no live CPU
temperature is available, so the Temperature card can render an
actionable explainer instead of being hidden. Frontend respects the
key via t() lookup.
Section headers:
- Underline switched from dashed to solid; channel-green accent rule
(40 px) on the left remains.
Build / tests:
- ruff clean on touched Python files.
- tsc --noEmit clean.
- Python metrics-provider tests: 18 passed.
- CSS bundle ~214 KB.
|
||
|
|
539e43195f |
feat(ui): Lumenworks studio-console WebUI redesign
Full-app UI/UX refresh committing to a tech-instrument / studio-console aesthetic inspired by hardware synths, Eurorack panels, and DAW layouts. Design tokens and fonts: - Embed Manrope (body), JetBrains Mono (labels/metrics), Big Shoulders Display (numeric readouts) as local .woff2 variable fonts with latin + latin-ext + cyrillic + cyrillic-ext subsets via unicode-range. - New Lumenworks token layer in base.css: --lux-bg-0..3, --lux-line(-bold), --lux-ink(-dim/-mute/-faint), --ch-signal/-cyan/-magenta/-amber/-coral/ -violet channel palette, --lux-signal-glow, --lux-shadow-rack, all theme-aware for dark + light. Existing tokens untouched for compat. Shell (header + sidebar): - Header rebuilt as a 3-column CSS-grid transport bar (brand | center | toolbar) with a glowing LED brand mark rendered via pseudo-elements on .header-title. Gradient channel-color rule under the bottom border. - New sidebar.css introduces a vertical channel-strip nav. Active tab gets a glowing left stripe + radial tint + LED pip. .sidebar-foot contains a live CPU/FPS meter plate. - Sidebar collapses to a 56 px icon rail at <=1100 px and hides via display:contents at <=600 px so mobile.css's fixed bottom tab-bar flows through unchanged. Cards and dashboard: - .card gets channel stripe (data-card-type + .ch-* utilities auto-map from data-target-id / data-stream-id / data-automation-id etc.), corner bracket, gradient background, subtle rack shadow. - .card-running replaces the old @property --border-angle conic-gradient rotating border with a lightweight signalFlow linear-gradient strip on the bottom edge (cheaper paint, no GPU layer compositing per card). - Skeleton loaders rewritten: left hairline + corner bracket + gradient shimmer instead of the old text-color opacity pulse. - .dashboard-target rows pick up the same channel-stripe + signalFlow treatment. Section headers use mono micro-caps with a channel-green underline accent consistent across the app. - .perf-chart-card: channel stripe replaces old border-top; per-metric accents moved to the channel palette (CPU=coral, RAM=violet, GPU=green, temp=amber). Metric values use tabular-nums + a soft glow. Live bindings (no new endpoints): - _updateSidebarMeter: binds the sidebar Load + FPS bars to the existing /system/performance poll. - _updateTransportStatus: toggles the transport chip between "Ready" and "Armed - N live" whenever the dashboard's running-target set is recomputed. Tree-nav + sub-tabs: - tree-nav.css trigger pill gets a channel-stripe left edge that glows when open; panel has a gradient channel-accent rule across the top; group headers use silkscreened micro-caps; active leaf has a pulsing LED pip + channel tint. - .stream-tab-btn / .subtab-section-header adopt the same mono-caps + channel-underline language for consistency. - Graph editor toolbar gets gradient + hairline + rack shadow + backdrop blur. Canvas and nodes untouched. Modals (40+ modals share modal.css): - Radial-dim + 6 px blur backdrop. Content gets a gradient background, hairline border, deep rack shadow, top channel-accent rule driven by --modal-ch, bottom-right corner bracket (hidden on mobile fullscreen). - Per-modal-ID channel lanes: target editors = green, source/input editors = cyan, audio = magenta, automation/scene/game = violet, settings/auth = amber, confirm = coral. - Modal headers: vertical channel stripe left of the title + hairline divider. Modal footers: hairline top border + subtle gradient wash. Forms: - Inputs use hairline borders; number inputs switch to mono + tabular-nums for column alignment. Focus state: channel-green ring + soft glow. - Buttons use mono-uppercase type with signal-glow on primary and coral- glow on danger. Mobile (<=600 px): - Fixed bottom .tab-bar gets the full Lumenworks treatment: gradient fill, top channel-accent rule matching the transport bar, backdrop blur. Active tab has an LED pip above the icon + channel tint + icon recolor. - Fullscreen modals: corner bracket hidden, header stripe slimmed. Microcopy (en / ru / zh): - "Targets" -> "Channels" / "Каналы" / "通道" - "Sources" -> "Inputs" / "Входы" / "输入" - Internal tab keys (dashboard/automations/targets/streams/integrations/ graph) kept stable so no JS or localStorage migration is needed. - Added: sidebar.workspaces, sidebar.load, sidebar.fps, transport.status.ready, transport.status.armed. Compatibility: - All existing class hooks preserved (.tab-bar, .tab-btn, .card, .card-running, .tree-dd-*, .cs-*, .perf-chart-card, .modal-content, .dashboard-target, etc.). No JS or API changes required for the new look to take effect. - Tour selectors survive (header .header-title, #tab-btn-*, onclick markers on theme/settings/search, #cp-wrap-accent, etc.). - Mobile <=600 px bottom tab-bar keeps working via display:contents fall-through in the new sidebar. Build: tsc --noEmit clean; npm run build clean. CSS bundle grew from ~177 KB to ~201 KB for the full new visual system. Fonts loaded lazily per unicode-range subset (~98 KB critical path for English). Phased plan + deferred follow-ups (dashboard hero strip, legacy-token cleanup) recorded at the top of TODO.md. Reference mockup: server/docs/ui-redesign-mockup.html. |
||
|
|
c44bb38c43 |
docs(release): refresh v0.4.2 notes with fix(release) and refactor commits
Build Release / create-release (push) Successful in 3s
Build Android APK / build-android (push) Failing after 2m5s
Build Release / build-linux (push) Successful in 4m53s
Build Release / build-docker (push) Successful in 5m39s
Build Release / build-windows (push) Successful in 6m55s
Lint & Test / test (push) Successful in 7m13s
|
||
|
|
be2d5e1670 |
refactor(color-strips): move Key Colors test from lightbox into test-css-source modal
Lint & Test / test (push) Successful in 6m37s
Removes the inlined FPS select and auto-refresh button from the shared image lightbox and rehosts the Key Colors live preview inside the dedicated test-css-source modal alongside the other CSS test views. - Drop initLightbox() / lightbox-fps-select IconSelect — the lightbox no longer owns streaming controls. - Add #css-test-kc-view (canvas + meta) and .css-test-kc-* styles. - Reroute _testKeyColorsSource() through the existing modal session lifecycle so KC, CSPT, and standard CSS tests share teardown paths. |
||
|
|
5db6eddcf8 |
fix(release): ship prebuilt assets and bump fallback version
Two release-blocking bugs traced to the same root cause: the unanchored
`data/` rule in .gitignore matched server/src/ledgrab/data/, which is
where shipped package assets live (prebuilt sounds, game adapters).
The files were never `git add`-able without -f, so they never reached
the v0.4.2 tag and CI builds couldn't include them.
- .gitignore: anchor /data/ and /server/data/ so nested package data
dirs are not ignored.
- Track previously-excluded shipped assets:
- server/src/ledgrab/data/prebuilt_sounds/{alert,bell,chime,ping,pop}.wav
- server/src/ledgrab/data/game_adapters/{minecraft,rocket_league,valorant}.yaml
- Bump _FALLBACK_VERSION 0.3.0 -> 0.4.2 to match pyproject.toml.
The Windows installer strips ledgrab-*.dist-info, so
importlib.metadata falls back to this literal — which is why
v0.4.2 reports v0.3.0 in the WebUI.
- Patch _FALLBACK_VERSION at bundle time in build-common.sh and
build-dist.ps1 so future drift is auto-corrected by the build.
|
||
|
|
a8a4296a56 |
chore: release v0.4.2
Build Android APK / build-android (push) Failing after 1m48s
Build Release / create-release (push) Successful in 3s
Build Release / build-linux (push) Successful in 3m58s
Build Release / build-docker (push) Successful in 5m6s
Build Release / build-windows (push) Successful in 5m54s
Lint & Test / test (push) Successful in 6m14s
|
||
|
|
9ce1dc33bf | feat(ui): restyle enhanced header locale picker as LED-accent badge | ||
|
|
03d2e6b1f2 |
ci(release): publish .sha256 sidecars alongside release assets
Lint & Test / test (push) Successful in 2m4s
The in-app update service (`ledgrab.core.update.update_service`) refuses to install any downloaded artifact that has no published sha256 — either as a sibling `<asset>.sha256` asset on the Gitea release, or embedded in the release body. The release workflow uploaded the ZIP, setup.exe, and Linux tarball but never published checksums, so every auto-update 500'd with "Update checksum unavailable; install aborted". Generate sha256sum sidecars for the Windows ZIP, Windows setup.exe, and Linux tar.gz and upload them next to the primary asset on each tagged release. Existing v0.4.x releases stay broken — ship v0.4.2 (or manually upload sidecars to v0.4.1) to unblock in-app updates. |
||
|
|
c2c9af3c60 |
chore: release v0.4.1
Build Release / create-release (push) Successful in 4s
Build Android APK / build-android (push) Failing after 1m41s
Build Release / build-linux (push) Successful in 3m3s
Build Release / build-docker (push) Successful in 4m13s
Lint & Test / test (push) Successful in 5m24s
Build Release / build-windows (push) Successful in 5m33s
|
||
|
|
4f7794ccd4 |
fix(installer): bundle cryptography + just-playback, set TCL env, clean stale debug.bat
Lint & Test / test (push) Successful in 2m20s
Windows installer silently failed to launch because build-dist-windows.sh maintained its own DEPS list that drifted from server/pyproject.toml and was missing `cryptography` — ledgrab.utils.secret_box imports AESGCM at module load, so pythonw.exe crashed before the tray icon appeared. Also missing: just-playback (lazy import, silent until a sound triggers). - Add cryptography + just-playback to DEPS with a sync-with-pyproject warning comment - Extend the post-cleanup on-disk check to abort the build if cryptography / cffi / just_playback go missing again - Launcher now exports TCL_LIBRARY / TK_LIBRARY so the screen-overlay tkinter thread stops logging "Can't find init.tcl" at startup - Installer wipes stale debug.bat / debug.log on install and uninstall (leftovers from the pre-rename wled_controller era produced a misleading ModuleNotFoundError when users tried to diagnose launch failures) |
||
|
|
a0d63a3663 |
docs(release): drop stale WLED-rename task, document android signing secrets
Lint & Test / test (push) Successful in 2m1s
- Remove the top-of-file "IMPORTANT: Remove WLED naming throughout the app" checklist. The effort was absorbed by the multi-backend refactor (BLE / USB-serial / ESP-NOW / MQTT / OpenRGB providers all shipped), and the remaining user-facing copy has been swept in separate commits. - Add an "Android Signing Secrets (Gitea)" section covering the four secrets the release APK CI expects, the one-off `keytool` command to generate `release.jks`, the consequences of losing the keystore, and a checklist of the remaining setup steps before tagging v0.4.1. |
||
|
|
35b75a2ed8 |
ci(android): fix keystore env scoping, fail loudly on release without key
Lint & Test / test (push) Successful in 3m17s
Primary bug — step-level env is not visible in that same step's `if:`
expression. `Decode signing keystore` had
if: env.ANDROID_KEYSTORE_BASE64 != ''
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
so the env context seen by the `if:` evaluator was empty regardless of
whether the secret was configured. The step was skipped, keystore.present
never became 'true', and every release tag silently fell back to
assembleDebug. Result: APKs named `LedGrab-0.4.0-android-debug.apk` that
can't upgrade a previously-release-signed install (signature mismatch).
Fix — move ANDROID_KEYSTORE_BASE64 to the job-level env block. It's now
resolvable in the if-expression of any step in the job, and the shell
inherits it exactly the same way as before.
Secondary — add a "Guard release tag against missing keystore" step that
fires between the decode attempt and the gradle build. If is_release=true
but keystore.present!='true', the job fails with a clear error directing
the operator to configure the four signing secrets. Previously a
misconfigured Gitea silently shipped debug APKs labeled as releases.
|
||
|
|
4ed099d564 |
docs(release): drop WLED-specific language from auto-generated release notes
The "discover your WLED devices" line predates BLE / USB-serial / ESP-NOW / MQTT / OpenRGB support and misrepresents what the app does. Replaced with a generic "add your LED devices" — the device-add UI lists what's actually supported, and INSTALLATION.md carries the long-form detail. |
||
|
|
d467eb5dae |
chore: release v0.4.0
Build Release / create-release (push) Successful in 3s
Build Android APK / build-android (push) Successful in 5m57s
Build Release / build-linux (push) Successful in 5m44s
Build Release / build-docker (push) Successful in 7m51s
Lint & Test / test (push) Successful in 8m59s
Build Release / build-windows (push) Successful in 8m51s
|