Comprehensive WebUI review: 41 UX/feature/CSS improvements

Safety & Correctness:
- Add confirmation dialogs to Stop All, turnOffDevice
- i18n confirm dialog (title, yes, no buttons)
- Fix duplicate tutorial-overlay ID
- Define missing CSS variables (--radius, --text-primary, --hover-bg, --input-bg)
- Fix toast z-index conflict with confirm dialog (2500 → 3000)

UX Consistency:
- Add backdrop-close to test modals
- Add device clone feature (only entity without it)
- Add sync clocks to command palette
- Replace 20+ hardcoded accent colors with CSS vars/color-mix()
- Remove dead .badge duplicate from components.css
- Make calibration elements keyboard-accessible (div → button)
- Add aria-labels to color picker swatches
- Fix pattern canvas mobile horizontal scroll
- Fix graph editor mobile bottom clipping

Polish:
- Add empty-state messages to all CardSection instances
- Convert 21 px font-sizes to rem
- Add scroll-behavior: smooth with reduced-motion override
- Add @media print styles
- Add :focus-visible to 4 missing interactive elements
- Fix settings modal close label (Cancel → Close)
- Fix api-key submit button i18n

New Features:
- Command palette actions: start/stop targets, activate scenes, enable/disable
- Bulk start/stop API endpoints (POST /output-targets/bulk/start|stop)
- OS notification history viewer modal
- Scene "used by" automation reference count on cards
- Clock elapsed time display on Streams tab cards
- Device "last seen" relative timestamp on cards
- Audio device refresh button in edit modal
- Composite layer drag-to-reorder
- MQTT settings panel (broker config with JSON persistence)
- WebSocket log viewer with level filtering and ring buffer
- Runtime log-level adjustment (GET/PUT endpoints + settings UI)
- Animated value source waveform canvas preview
- Gradient custom preset save/delete (localStorage)
- API key read-only display in settings
- Backup metadata (file size, auto/manual badges)
- Server restart button with confirm + overlay
- Partial config export/import per entity type
- Progressive disclosure in target editor (Advanced section)

CSS Architecture:
- Define radius scale tokens (--radius-sm/md/lg/pill)
- Scope .cs-filter selectors to remove 7 !important overrides
- Consolidate duplicate toggle switch (filter-list → settings-toggle)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-16 18:46:38 +03:00
parent a4a0e39b9b
commit 304fa24389
47 changed files with 2594 additions and 250 deletions

View File

@@ -29,6 +29,7 @@
"auth.prompt_update": "Current API key is set. Enter new key to update or leave blank to remove:",
"auth.prompt_enter": "Enter your API key:",
"auth.toggle_password": "Toggle password visibility",
"api_key.login": "Login",
"displays.title": "Available Displays",
"displays.layout": "Displays",
"displays.information": "Display Information",
@@ -291,6 +292,12 @@
"device.health.offline": "Offline",
"device.health.streaming_unreachable": "Unreachable during streaming",
"device.health.checking": "Checking...",
"device.last_seen.label": "Last seen",
"device.last_seen.just_now": "just now",
"device.last_seen.seconds": "%ds ago",
"device.last_seen.minutes": "%dm ago",
"device.last_seen.hours": "%dh ago",
"device.last_seen.days": "%dd ago",
"device.tutorial.start": "Start tutorial",
"device.tip.metadata": "Device info (LED count, type, color channels) is auto-detected from the device",
"device.tip.brightness": "Slide to adjust device brightness",
@@ -405,6 +412,8 @@
"confirm.title": "Confirm Action",
"confirm.yes": "Yes",
"confirm.no": "No",
"confirm.stop_all": "Stop all running targets?",
"confirm.turn_off_device": "Turn off this device?",
"common.loading": "Loading...",
"common.delete": "Delete",
"common.edit": "Edit",
@@ -584,6 +593,7 @@
"targets.section.color_strips": "Color Strip Sources",
"targets.section.targets": "Targets",
"targets.section.specific_settings": "Specific Settings",
"targets.section.advanced": "Advanced",
"targets.add": "Add Target",
"targets.edit": "Edit Target",
"targets.loading": "Loading targets...",
@@ -953,6 +963,11 @@
"color_strip.gradient.preset.cool": "Cool",
"color_strip.gradient.preset.neon": "Neon",
"color_strip.gradient.preset.pastel": "Pastel",
"color_strip.gradient.preset.save_button": "Save as preset…",
"color_strip.gradient.preset.save_prompt": "Enter a name for this preset:",
"color_strip.gradient.preset.saved": "Preset saved",
"color_strip.gradient.preset.deleted": "Preset deleted",
"color_strip.gradient.preset.apply": "Apply",
"color_strip.animation": "Animation",
"color_strip.animation.type": "Effect:",
"color_strip.animation.type.hint": "Animation effect to apply.",
@@ -1043,6 +1058,15 @@
"color_strip.notification.test.ok": "Notification sent",
"color_strip.notification.test.no_streams": "No running streams for this source",
"color_strip.notification.test.error": "Failed to send notification",
"color_strip.notification.history.title": "Notification History",
"color_strip.notification.history.hint": "Recent OS notifications captured by the listener (newest first). Up to 50 entries.",
"color_strip.notification.history.empty": "No notifications captured yet",
"color_strip.notification.history.unavailable": "OS notification listener is not available on this platform",
"color_strip.notification.history.error": "Failed to load notification history",
"color_strip.notification.history.refresh": "Refresh",
"color_strip.notification.history.unknown_app": "Unknown app",
"color_strip.notification.history.fired": "Streams triggered",
"color_strip.notification.history.filtered": "Streams filtered",
"color_strip.test.title": "Test Preview",
"color_strip.test.connecting": "Connecting...",
"color_strip.test.error": "Failed to connect to preview stream",
@@ -1188,6 +1212,7 @@
"audio_source.type.mono": "Mono",
"audio_source.device": "Audio Device:",
"audio_source.device.hint": "Audio input source. Loopback devices capture system audio output; input devices capture microphone or line-in.",
"audio_source.refresh_devices": "Refresh devices",
"audio_source.parent": "Parent Source:",
"audio_source.parent.hint": "Multichannel source to extract a channel from",
"audio_source.channel": "Channel:",
@@ -1375,6 +1400,13 @@
"search.group.value": "Value Sources",
"search.group.scenes": "Scene Presets",
"search.group.cspt": "Strip Processing Templates",
"search.group.sync_clocks": "Sync Clocks",
"search.group.actions": "Actions",
"search.action.start": "Start",
"search.action.stop": "Stop",
"search.action.activate": "Activate",
"search.action.enable": "Enable",
"search.action.disable": "Disable",
"settings.backup.label": "Backup Configuration",
"settings.backup.hint": "Download all configuration (devices, targets, streams, templates, automations) as a single JSON file.",
"settings.backup.button": "Download Backup",
@@ -1388,7 +1420,15 @@
"settings.restore.error": "Restore failed",
"settings.restore.restarting": "Server is restarting...",
"settings.restore.restart_timeout": "Server did not respond. Please refresh the page manually.",
"settings.restart_server": "Restart Server",
"settings.restart_confirm": "Restart the server? Active targets will be stopped.",
"settings.restarting": "Restarting server...",
"settings.button.close": "Close",
"settings.log_level.label": "Log Level",
"settings.log_level.hint": "Change the server log verbosity at runtime. DEBUG shows the most detail; CRITICAL shows only fatal errors.",
"settings.log_level.save": "Apply",
"settings.log_level.saved": "Log level changed",
"settings.log_level.save_error": "Failed to change log level",
"settings.auto_backup.label": "Auto-Backup",
"settings.auto_backup.hint": "Automatically create periodic backups of all configuration. Old backups are pruned when the maximum count is reached.",
"settings.auto_backup.enable": "Enable auto-backup",
@@ -1407,6 +1447,32 @@
"settings.saved_backups.delete": "Delete",
"settings.saved_backups.delete_confirm": "Delete this backup file?",
"settings.saved_backups.delete_error": "Failed to delete backup",
"settings.saved_backups.type.auto": "auto",
"settings.saved_backups.type.manual": "manual",
"settings.mqtt.label": "MQTT",
"settings.mqtt.hint": "Configure MQTT broker connection for automation conditions and triggers.",
"settings.mqtt.enabled": "Enable MQTT",
"settings.mqtt.host_label": "Broker Host",
"settings.mqtt.port_label": "Port",
"settings.mqtt.username_label": "Username",
"settings.mqtt.password_label": "Password",
"settings.mqtt.password_set_hint": "Password is set — leave blank to keep",
"settings.mqtt.client_id_label": "Client ID",
"settings.mqtt.base_topic_label": "Base Topic",
"settings.mqtt.save": "Save MQTT Settings",
"settings.mqtt.saved": "MQTT settings saved",
"settings.mqtt.save_error": "Failed to save MQTT settings",
"settings.mqtt.error_host_required": "Broker host is required",
"settings.logs.label": "Server Logs",
"settings.logs.hint": "Stream live server log output. Use the filter to show only relevant log levels.",
"settings.logs.connect": "Connect",
"settings.logs.disconnect": "Disconnect",
"settings.logs.clear": "Clear",
"settings.logs.error": "Log viewer connection failed",
"settings.logs.filter.all": "All levels",
"settings.logs.filter.info": "Info+",
"settings.logs.filter.warning": "Warning+",
"settings.logs.filter.error": "Error only",
"device.error.power_off_failed": "Failed to turn off device",
"device.removed": "Device removed",
"device.error.remove_failed": "Failed to remove device",
@@ -1415,6 +1481,7 @@
"device.error.required": "Please fill in all fields correctly",
"device.error.update": "Failed to update device",
"device.error.save": "Failed to save settings",
"device.error.clone_failed": "Failed to clone device",
"device_discovery.error.fill_all_fields": "Please fill in all fields",
"device_discovery.added": "Device added successfully",
"device_discovery.error.add_failed": "Failed to add device",
@@ -1506,6 +1573,7 @@
"sync_clock.resumed": "Clock resumed",
"sync_clock.reset_done": "Clock reset to zero",
"sync_clock.delete.confirm": "Delete this sync clock? Linked sources will lose synchronization and run at default speed.",
"sync_clock.elapsed": "Elapsed time",
"color_strip.clock": "Sync Clock:",
"color_strip.clock.hint": "Link to a sync clock to synchronize animation timing across sources. Speed is controlled on the clock.",
"graph.title": "Graph",
@@ -1570,5 +1638,50 @@
"graph.help.right_click_desc": "Detach connection",
"automation.enabled": "Automation enabled",
"automation.disabled": "Automation disabled",
"scene_preset.activated": "Preset activated"
"scene_preset.activated": "Preset activated",
"scene_preset.used_by": "Used by %d automation(s)",
"settings.api_keys.label": "API Keys",
"settings.api_keys.hint": "API keys are defined in the server config file (config.yaml). Edit the file and restart the server to apply changes.",
"settings.api_keys.empty": "No API keys configured",
"settings.api_keys.load_error": "Failed to load API keys",
"settings.partial.label": "Partial Export / Import",
"settings.partial.hint": "Export or import a single entity type. Import replaces or merges existing data and restarts the server.",
"settings.partial.store.devices": "Devices",
"settings.partial.store.output_targets": "LED Targets",
"settings.partial.store.color_strip_sources": "Color Strips",
"settings.partial.store.picture_sources": "Picture Sources",
"settings.partial.store.audio_sources": "Audio Sources",
"settings.partial.store.audio_templates": "Audio Templates",
"settings.partial.store.capture_templates": "Capture Templates",
"settings.partial.store.postprocessing_templates": "Post-processing Templates",
"settings.partial.store.color_strip_processing_templates": "CSS Processing Templates",
"settings.partial.store.pattern_templates": "Pattern Templates",
"settings.partial.store.value_sources": "Value Sources",
"settings.partial.store.sync_clocks": "Sync Clocks",
"settings.partial.store.automations": "Automations",
"settings.partial.store.scene_presets": "Scene Presets",
"settings.partial.export_button": "Export",
"settings.partial.import_button": "Import from File",
"settings.partial.merge_label": "Merge (add/overwrite, keep existing)",
"settings.partial.export_success": "Exported successfully",
"settings.partial.export_error": "Export failed",
"settings.partial.import_success": "Imported successfully",
"settings.partial.import_error": "Import failed",
"settings.partial.import_confirm_replace": "This will REPLACE all {store} data and restart the server. Continue?",
"settings.partial.import_confirm_merge": "This will MERGE into existing {store} data and restart the server. Continue?",
"section.empty.devices": "No devices yet. Click + to add one.",
"section.empty.targets": "No LED targets yet. Click + to add one.",
"section.empty.kc_targets": "No key color targets yet. Click + to add one.",
"section.empty.pattern_templates": "No pattern templates yet. Click + to add one.",
"section.empty.picture_sources": "No sources yet. Click + to add one.",
"section.empty.capture_templates": "No capture templates yet. Click + to add one.",
"section.empty.pp_templates": "No post-processing templates yet. Click + to add one.",
"section.empty.audio_sources": "No audio sources yet. Click + to add one.",
"section.empty.audio_templates": "No audio templates yet. Click + to add one.",
"section.empty.color_strips": "No color strips yet. Click + to add one.",
"section.empty.value_sources": "No value sources yet. Click + to add one.",
"section.empty.sync_clocks": "No sync clocks yet. Click + to add one.",
"section.empty.cspt": "No CSS processing templates yet. Click + to add one.",
"section.empty.automations": "No automations yet. Click + to add one.",
"section.empty.scenes": "No scene presets yet. Click + to add one."
}