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.toggle_password": "Показать/скрыть пароль",
"auth.prompt_enter": "Enter your API key:",
"auth.prompt_update": "Current API key is set. Enter new key to update or leave blank to remove:",
"api_key.login": "Войти",
"displays.title": "Доступные Дисплеи",
"displays.layout": "Дисплеи",
"displays.information": "Информация о Дисплеях",
@@ -291,6 +292,12 @@
"device.health.offline": "Недоступен",
"device.health.streaming_unreachable": "Недоступен во время стриминга",
"device.health.checking": "Проверка...",
"device.last_seen.label": "Последний раз",
"device.last_seen.just_now": "только что",
"device.last_seen.seconds": "%d с назад",
"device.last_seen.minutes": "%d мин назад",
"device.last_seen.hours": "%d ч назад",
"device.last_seen.days": "%d д назад",
"device.tutorial.start": "Начать обучение",
"device.tip.metadata": "Информация об устройстве (кол-во LED, тип, цветовые каналы) определяется автоматически",
"device.tip.brightness": "Перетащите для регулировки яркости",
@@ -402,9 +409,11 @@
"error.network": "Сетевая ошибка",
"error.unknown": "Произошла ошибка",
"modal.discard_changes": "У вас есть несохранённые изменения. Отменить их?",
"confirm.title": "Подтверждение Действия",
"confirm.title": "Подтверждение",
"confirm.yes": "Да",
"confirm.no": "Нет",
"confirm.stop_all": "Остановить все запущенные цели?",
"confirm.turn_off_device": "Выключить это устройство?",
"common.loading": "Загрузка...",
"common.delete": "Удалить",
"common.edit": "Редактировать",
@@ -584,6 +593,7 @@
"targets.section.color_strips": "Источники цветовых полос",
"targets.section.targets": "Цели",
"targets.section.specific_settings": "Специальные настройки",
"targets.section.advanced": "Расширенные",
"targets.add": "Добавить Цель",
"targets.edit": "Редактировать Цель",
"targets.loading": "Загрузка целей...",
@@ -953,6 +963,11 @@
"color_strip.gradient.preset.cool": "Холодный",
"color_strip.gradient.preset.neon": "Неон",
"color_strip.gradient.preset.pastel": "Пастельный",
"color_strip.gradient.preset.save_button": "Сохранить как пресет…",
"color_strip.gradient.preset.save_prompt": "Введите название пресета:",
"color_strip.gradient.preset.saved": "Пресет сохранён",
"color_strip.gradient.preset.deleted": "Пресет удалён",
"color_strip.gradient.preset.apply": "Применить",
"color_strip.animation": "Анимация",
"color_strip.animation.type": "Эффект:",
"color_strip.animation.type.hint": "Эффект анимации.",
@@ -1043,6 +1058,15 @@
"color_strip.notification.test.ok": "Уведомление отправлено",
"color_strip.notification.test.no_streams": "Нет запущенных потоков для этого источника",
"color_strip.notification.test.error": "Не удалось отправить уведомление",
"color_strip.notification.history.title": "История уведомлений",
"color_strip.notification.history.hint": "Последние ОС-уведомления, захваченные слушателем (новейшие сверху). До 50 записей.",
"color_strip.notification.history.empty": "Уведомления ещё не захвачены",
"color_strip.notification.history.unavailable": "Слушатель уведомлений ОС недоступен на этой платформе",
"color_strip.notification.history.error": "Не удалось загрузить историю уведомлений",
"color_strip.notification.history.refresh": "Обновить",
"color_strip.notification.history.unknown_app": "Неизвестное приложение",
"color_strip.notification.history.fired": "Потоков запущено",
"color_strip.notification.history.filtered": "Потоков отфильтровано",
"color_strip.test.title": "Предпросмотр",
"color_strip.test.connecting": "Подключение...",
"color_strip.test.error": "Не удалось подключиться к потоку предпросмотра",
@@ -1188,6 +1212,7 @@
"audio_source.type.mono": "Моно",
"audio_source.device": "Аудиоустройство:",
"audio_source.device.hint": "Источник аудиосигнала. Устройства обратной петли захватывают системный звук; устройства ввода — микрофон или линейный вход.",
"audio_source.refresh_devices": "Обновить устройства",
"audio_source.parent": "Родительский источник:",
"audio_source.parent.hint": "Многоканальный источник для извлечения канала",
"audio_source.channel": "Канал:",
@@ -1375,6 +1400,13 @@
"search.group.value": "Источники значений",
"search.group.scenes": "Пресеты сцен",
"search.group.cspt": "Шаблоны обработки полос",
"search.group.sync_clocks": "Синхронные часы",
"search.group.actions": "Действия",
"search.action.start": "Запустить",
"search.action.stop": "Остановить",
"search.action.activate": "Активировать",
"search.action.enable": "Включить",
"search.action.disable": "Отключить",
"settings.backup.label": "Резервное копирование",
"settings.backup.hint": "Скачать всю конфигурацию (устройства, цели, потоки, шаблоны, автоматизации) в виде одного JSON-файла.",
"settings.backup.button": "Скачать резервную копию",
@@ -1388,7 +1420,15 @@
"settings.restore.error": "Ошибка восстановления",
"settings.restore.restarting": "Сервер перезапускается...",
"settings.restore.restart_timeout": "Сервер не отвечает. Обновите страницу вручную.",
"settings.restart_server": "Перезапустить сервер",
"settings.restart_confirm": "Перезапустить сервер? Активные цели будут остановлены.",
"settings.restarting": "Перезапуск сервера...",
"settings.button.close": "Закрыть",
"settings.log_level.label": "Уровень логирования",
"settings.log_level.hint": "Изменить подробность логов сервера в реальном времени. DEBUG — максимум деталей, CRITICAL — только критические ошибки.",
"settings.log_level.save": "Применить",
"settings.log_level.saved": "Уровень логирования изменён",
"settings.log_level.save_error": "Не удалось изменить уровень логирования",
"settings.auto_backup.label": "Авто-бэкап",
"settings.auto_backup.hint": "Автоматическое создание периодических резервных копий конфигурации. Старые копии удаляются при превышении максимального количества.",
"settings.auto_backup.enable": "Включить авто-бэкап",
@@ -1407,6 +1447,32 @@
"settings.saved_backups.delete": "Удалить",
"settings.saved_backups.delete_confirm": "Удалить эту резервную копию?",
"settings.saved_backups.delete_error": "Не удалось удалить копию",
"settings.saved_backups.type.auto": "авто",
"settings.saved_backups.type.manual": "ручной",
"settings.mqtt.label": "MQTT",
"settings.mqtt.hint": "Настройте подключение к MQTT-брокеру для условий и триггеров автоматизации.",
"settings.mqtt.enabled": "Включить MQTT",
"settings.mqtt.host_label": "Хост брокера",
"settings.mqtt.port_label": "Порт",
"settings.mqtt.username_label": "Имя пользователя",
"settings.mqtt.password_label": "Пароль",
"settings.mqtt.password_set_hint": "Пароль задан — оставьте пустым, чтобы сохранить",
"settings.mqtt.client_id_label": "Идентификатор клиента",
"settings.mqtt.base_topic_label": "Базовый топик",
"settings.mqtt.save": "Сохранить настройки MQTT",
"settings.mqtt.saved": "Настройки MQTT сохранены",
"settings.mqtt.save_error": "Не удалось сохранить настройки MQTT",
"settings.mqtt.error_host_required": "Требуется указать хост брокера",
"settings.logs.label": "Журнал сервера",
"settings.logs.hint": "Просмотр журнала сервера в реальном времени. Используйте фильтр для отображения нужных уровней.",
"settings.logs.connect": "Подключить",
"settings.logs.disconnect": "Отключить",
"settings.logs.clear": "Очистить",
"settings.logs.error": "Ошибка подключения к журналу",
"settings.logs.filter.all": "Все уровни",
"settings.logs.filter.info": "Info+",
"settings.logs.filter.warning": "Warning+",
"settings.logs.filter.error": "Только ошибки",
"device.error.power_off_failed": "Не удалось выключить устройство",
"device.removed": "Устройство удалено",
"device.error.remove_failed": "Не удалось удалить устройство",
@@ -1415,6 +1481,7 @@
"device.error.required": "Пожалуйста, заполните все поля",
"device.error.update": "Не удалось обновить устройство",
"device.error.save": "Не удалось сохранить настройки",
"device.error.clone_failed": "Не удалось клонировать устройство",
"device_discovery.error.fill_all_fields": "Пожалуйста, заполните все поля",
"device_discovery.added": "Устройство успешно добавлено",
"device_discovery.error.add_failed": "Не удалось добавить устройство",
@@ -1506,6 +1573,7 @@
"sync_clock.resumed": "Часы возобновлены",
"sync_clock.reset_done": "Часы сброшены на ноль",
"sync_clock.delete.confirm": "Удалить эти часы синхронизации? Привязанные источники потеряют синхронизацию и будут работать на скорости по умолчанию.",
"sync_clock.elapsed": "Прошло времени",
"color_strip.clock": "Часы синхронизации:",
"color_strip.clock.hint": "Привязка к часам для синхронизации анимации между источниками. Скорость управляется на часах.",
"graph.title": "Граф",
@@ -1570,5 +1638,50 @@
"graph.help.right_click_desc": "Отсоединить связь",
"automation.enabled": "Автоматизация включена",
"automation.disabled": "Автоматизация выключена",
"scene_preset.activated": "Пресет активирован"
"scene_preset.activated": "Пресет активирован",
"scene_preset.used_by": "Используется в %d автоматизации(ях)",
"settings.api_keys.label": "API-ключи",
"settings.api_keys.hint": "API-ключи определяются в конфигурационном файле сервера (config.yaml). Отредактируйте файл и перезапустите сервер для применения изменений.",
"settings.api_keys.empty": "API-ключи не настроены",
"settings.api_keys.load_error": "Не удалось загрузить API-ключи",
"settings.partial.label": "Частичный экспорт / импорт",
"settings.partial.hint": "Экспортировать или импортировать один тип объектов. Импорт заменяет или объединяет данные и перезапускает сервер.",
"settings.partial.store.devices": "Устройства",
"settings.partial.store.output_targets": "LED-цели",
"settings.partial.store.color_strip_sources": "Цветные полосы",
"settings.partial.store.picture_sources": "Источники изображений",
"settings.partial.store.audio_sources": "Аудио-источники",
"settings.partial.store.audio_templates": "Аудио-шаблоны",
"settings.partial.store.capture_templates": "Шаблоны захвата",
"settings.partial.store.postprocessing_templates": "Шаблоны постобработки",
"settings.partial.store.color_strip_processing_templates": "Шаблоны обработки полос",
"settings.partial.store.pattern_templates": "Шаблоны паттернов",
"settings.partial.store.value_sources": "Источники значений",
"settings.partial.store.sync_clocks": "Синхронные часы",
"settings.partial.store.automations": "Автоматизации",
"settings.partial.store.scene_presets": "Пресеты сцен",
"settings.partial.export_button": "Экспорт",
"settings.partial.import_button": "Импорт из файла",
"settings.partial.merge_label": "Объединить (добавить/перезаписать, сохранить существующие)",
"settings.partial.export_success": "Экспорт выполнен",
"settings.partial.export_error": "Ошибка экспорта",
"settings.partial.import_success": "Импорт выполнен",
"settings.partial.import_error": "Ошибка импорта",
"settings.partial.import_confirm_replace": "Это ЗАМЕНИТ все данные {store} и перезапустит сервер. Продолжить?",
"settings.partial.import_confirm_merge": "Это ОБЪЕДИНИТ данные {store} и перезапустит сервер. Продолжить?",
"section.empty.devices": "Устройств пока нет. Нажмите + для добавления.",
"section.empty.targets": "LED-целей пока нет. Нажмите + для добавления.",
"section.empty.kc_targets": "Целей ключевых цветов пока нет. Нажмите + для добавления.",
"section.empty.pattern_templates": "Шаблонов паттернов пока нет. Нажмите + для добавления.",
"section.empty.picture_sources": "Источников пока нет. Нажмите + для добавления.",
"section.empty.capture_templates": "Шаблонов захвата пока нет. Нажмите + для добавления.",
"section.empty.pp_templates": "Шаблонов постобработки пока нет. Нажмите + для добавления.",
"section.empty.audio_sources": "Аудио-источников пока нет. Нажмите + для добавления.",
"section.empty.audio_templates": "Аудио-шаблонов пока нет. Нажмите + для добавления.",
"section.empty.color_strips": "Цветных полос пока нет. Нажмите + для добавления.",
"section.empty.value_sources": "Источников значений пока нет. Нажмите + для добавления.",
"section.empty.sync_clocks": "Синхронных часов пока нет. Нажмите + для добавления.",
"section.empty.cspt": "Шаблонов обработки полос пока нет. Нажмите + для добавления.",
"section.empty.automations": "Автоматизаций пока нет. Нажмите + для добавления.",
"section.empty.scenes": "Пресетов сцен пока нет. Нажмите + для добавления."
}