Add EntitySelect/IconSelect UI improvements across modals

- Portal IconSelect popups to document.body with position:fixed to prevent
  clipping by modal overflow-y:auto
- Replace custom scene selectors in automation editor with EntitySelect
  command-palette pickers (main scene + fallback scene)
- Add IconSelect grid for automation deactivation mode (none/revert/fallback)
- Add IconSelect grid for automation condition type and match type
- Replace mapped zone source dropdowns with EntitySelect pickers
- Replace scene target selector with EntityPalette.pick() pattern
- Remove effect palette preview bar from CSS editor
- Remove sensitivity badge from audio color strip source cards
- Clean up unused scene-selector CSS and scene-target-add-row CSS
- Add locale keys for all new UI elements across en/ru/zh

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 16:00:30 +03:00
parent 186940124c
commit 2712c6682e
32 changed files with 1204 additions and 391 deletions

View File

@@ -624,10 +624,13 @@
"automations.conditions.add": "Add Condition",
"automations.conditions.empty": "No conditions — automation is always active when enabled",
"automations.condition.always": "Always",
"automations.condition.always.desc": "Always active",
"automations.condition.always.hint": "Automation activates immediately when enabled and stays active.",
"automations.condition.startup": "Startup",
"automations.condition.startup.desc": "On server start",
"automations.condition.startup.hint": "Activates when the server starts and stays active while enabled.",
"automations.condition.application": "Application",
"automations.condition.application.desc": "App running/focused",
"automations.condition.application.apps": "Applications:",
"automations.condition.application.apps.hint": "Process names, one per line (e.g. firefox.exe)",
"automations.condition.application.browse": "Browse",
@@ -636,23 +639,31 @@
"automations.condition.application.match_type": "Match Type:",
"automations.condition.application.match_type.hint": "How to detect the application",
"automations.condition.application.match_type.running": "Running",
"automations.condition.application.match_type.topmost": "Topmost (foreground)",
"automations.condition.application.match_type.topmost_fullscreen": "Topmost + Fullscreen",
"automations.condition.application.match_type.running.desc": "Process is active",
"automations.condition.application.match_type.topmost": "Topmost",
"automations.condition.application.match_type.topmost.desc": "Foreground window",
"automations.condition.application.match_type.topmost_fullscreen": "Topmost + FS",
"automations.condition.application.match_type.topmost_fullscreen.desc": "Foreground + fullscreen",
"automations.condition.application.match_type.fullscreen": "Fullscreen",
"automations.condition.application.match_type.fullscreen.desc": "Any fullscreen app",
"automations.condition.time_of_day": "Time of Day",
"automations.condition.time_of_day.desc": "Time range",
"automations.condition.time_of_day.start_time": "Start Time:",
"automations.condition.time_of_day.end_time": "End Time:",
"automations.condition.time_of_day.overnight_hint": "For overnight ranges (e.g. 22:0006:00), set start time after end time.",
"automations.condition.system_idle": "System Idle",
"automations.condition.system_idle.desc": "User idle/active",
"automations.condition.system_idle.idle_minutes": "Idle Timeout (minutes):",
"automations.condition.system_idle.mode": "Trigger Mode:",
"automations.condition.system_idle.when_idle": "When idle",
"automations.condition.system_idle.when_active": "When active",
"automations.condition.display_state": "Display State",
"automations.condition.display_state.desc": "Monitor on/off",
"automations.condition.display_state.state": "Monitor State:",
"automations.condition.display_state.on": "On",
"automations.condition.display_state.off": "Off (sleeping)",
"automations.condition.mqtt": "MQTT",
"automations.condition.mqtt.desc": "MQTT message",
"automations.condition.mqtt.topic": "Topic:",
"automations.condition.mqtt.payload": "Payload:",
"automations.condition.mqtt.match_mode": "Match Mode:",
@@ -661,6 +672,7 @@
"automations.condition.mqtt.match_mode.regex": "Regex",
"automations.condition.mqtt.hint": "Activate when an MQTT topic receives a matching payload",
"automations.condition.webhook": "Webhook",
"automations.condition.webhook.desc": "HTTP callback",
"automations.condition.webhook.hint": "Activate via an HTTP call from external services (Home Assistant, IFTTT, curl, etc.)",
"automations.condition.webhook.url": "Webhook URL:",
"automations.condition.webhook.copy": "Copy",
@@ -673,9 +685,12 @@
"automations.scene.none_available": "No scenes available",
"automations.deactivation_mode": "Deactivation:",
"automations.deactivation_mode.hint": "What happens when conditions stop matching",
"automations.deactivation_mode.none": "None — keep current state",
"automations.deactivation_mode.revert": "Revert to previous state",
"automations.deactivation_mode.fallback_scene": "Activate fallback scene",
"automations.deactivation_mode.none": "None",
"automations.deactivation_mode.none.desc": "Keep current state",
"automations.deactivation_mode.revert": "Revert",
"automations.deactivation_mode.revert.desc": "Restore previous state",
"automations.deactivation_mode.fallback_scene": "Fallback",
"automations.deactivation_mode.fallback_scene.desc": "Activate a fallback scene",
"automations.deactivation_scene": "Fallback Scene:",
"automations.deactivation_scene.hint": "Scene to activate when this automation deactivates",
"automations.status.active": "Active",
@@ -702,6 +717,8 @@
"scenes.description.hint": "Optional description of what this scene does",
"scenes.targets": "Targets:",
"scenes.targets.hint": "Select which targets to include in this scene snapshot",
"scenes.targets.add": "Add Target",
"scenes.targets.search_placeholder": "Search targets...",
"scenes.capture": "Capture",
"scenes.activate": "Activate scene",
"scenes.recapture": "Recapture current state",
@@ -747,6 +764,9 @@
"color_strip.interpolation.average": "Average",
"color_strip.interpolation.median": "Median",
"color_strip.interpolation.dominant": "Dominant",
"color_strip.interpolation.average.desc": "Blend all sampled pixels into a smooth color",
"color_strip.interpolation.median.desc": "Pick the middle color, reducing outliers",
"color_strip.interpolation.dominant.desc": "Use the most frequent color in the sample",
"color_strip.smoothing": "Smoothing:",
"color_strip.smoothing.hint": "Temporal blending between frames (0=none, 1=full). Reduces flicker.",
"color_strip.frame_interpolation": "Frame Interpolation:",
@@ -773,6 +793,8 @@
"color_strip.type.hint": "Picture Source derives LED colors from a screen capture. Static Color fills all LEDs with a single constant color. Gradient distributes a color gradient across all LEDs. Color Cycle smoothly cycles through a user-defined list of colors. Composite stacks multiple sources as blended layers. Audio Reactive drives LEDs from real-time audio input. API Input receives raw LED colors from external clients via REST or WebSocket.",
"color_strip.type.picture": "Picture Source",
"color_strip.type.picture.desc": "Colors from screen capture",
"color_strip.type.picture_advanced": "Multi-Monitor",
"color_strip.type.picture_advanced.desc": "Line-based calibration across monitors",
"color_strip.type.static": "Static Color",
"color_strip.type.static.desc": "Single solid color fill",
"color_strip.type.gradient": "Gradient",
@@ -910,6 +932,7 @@
"color_strip.mapped.zone_end": "End LED",
"color_strip.mapped.zone_reverse": "Reverse",
"color_strip.mapped.zones_count": "zones",
"color_strip.mapped.select_source": "Search sources...",
"color_strip.mapped.error.no_source": "Each zone must have a source selected",
"color_strip.audio.visualization": "Visualization:",
"color_strip.audio.visualization.hint": "How audio data is rendered to LEDs.",
@@ -1197,6 +1220,29 @@
"calibration.error.save_failed": "Failed to save calibration",
"calibration.error.led_count_mismatch": "Total LEDs must equal the device LED count",
"calibration.error.led_count_exceeded": "Calibrated LEDs exceed the total LED count",
"calibration.mode.simple": "Simple",
"calibration.mode.advanced": "Advanced",
"calibration.switch_to_advanced": "Switch to Advanced",
"calibration.advanced.title": "Advanced Calibration",
"calibration.advanced.switch_to_simple": "Switch to Simple",
"calibration.advanced.lines_title": "Lines",
"calibration.advanced.canvas_hint": "Drag monitors to reposition. Click edges to select lines. Scroll to zoom, drag empty space to pan.",
"calibration.advanced.reset_view": "Reset view",
"calibration.advanced.line_properties": "Line Properties",
"calibration.advanced.picture_source": "Source:",
"calibration.advanced.picture_source.hint": "The picture source (monitor) this line samples from",
"calibration.advanced.edge": "Edge:",
"calibration.advanced.edge.hint": "Which screen edge to sample pixels from",
"calibration.advanced.led_count": "LEDs:",
"calibration.advanced.led_count.hint": "Number of LEDs mapped to this line",
"calibration.advanced.span_start": "Span Start:",
"calibration.advanced.span_start.hint": "Where sampling begins along the edge (0 = start, 1 = end). Use to cover only part of an edge.",
"calibration.advanced.span_end": "Span End:",
"calibration.advanced.span_end.hint": "Where sampling ends along the edge (0 = start, 1 = end). Together with Span Start, defines the active portion.",
"calibration.advanced.border_width": "Depth (px):",
"calibration.advanced.border_width.hint": "How many pixels deep from the edge to sample. Larger values capture more of the screen interior.",
"calibration.advanced.reverse": "Reverse",
"calibration.advanced.no_lines_warning": "Add at least one line",
"dashboard.error.automation_toggle_failed": "Failed to toggle automation",
"dashboard.error.start_failed": "Failed to start processing",
"dashboard.error.stop_failed": "Failed to stop processing",