Fix UI review issues: accessibility, i18n, duplicate IDs, URL overflow
- Rename duplicate id="settings-error" to "device-settings-error" - Add missing i18n key value_source.scene_sensitivity.hint (en/ru/zh) - Add accessible label to password-toggle and Stop All buttons - Add aria-hidden toggle on connection overlay - Fix static image URL overflow with ellipsis truncation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -59,9 +59,11 @@
|
||||
}
|
||||
|
||||
.stream-card-prop-full {
|
||||
max-width: 100%;
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
flex: 1 1 100%;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,10 +129,10 @@ function _setConnectionState(online) {
|
||||
const banner = document.getElementById('connection-overlay');
|
||||
const badge = document.getElementById('server-status');
|
||||
if (online) {
|
||||
if (banner) banner.style.display = 'none';
|
||||
if (banner) { banner.style.display = 'none'; banner.setAttribute('aria-hidden', 'true'); }
|
||||
if (badge) badge.className = 'status-badge online';
|
||||
} else {
|
||||
if (banner) banner.style.display = 'flex';
|
||||
if (banner) { banner.style.display = 'flex'; banner.setAttribute('aria-hidden', 'false'); }
|
||||
if (badge) badge.className = 'status-badge offline';
|
||||
}
|
||||
return changed;
|
||||
|
||||
@@ -40,8 +40,8 @@ import { updateSubTabHash, updateTabBadge } from './tabs.js';
|
||||
// ── Card section instances ──
|
||||
const csDevices = new CardSection('led-devices', { titleKey: 'targets.section.devices', gridClass: 'devices-grid', addCardOnclick: "showAddDevice()", keyAttr: 'data-device-id' });
|
||||
const csColorStrips = new CardSection('led-css', { titleKey: 'targets.section.color_strips', gridClass: 'devices-grid', addCardOnclick: "showCSSEditor()", keyAttr: 'data-css-id' });
|
||||
const csLedTargets = new CardSection('led-targets', { titleKey: 'targets.section.targets', gridClass: 'devices-grid', addCardOnclick: "showTargetEditor()", keyAttr: 'data-target-id', headerExtra: `<button class="btn btn-sm btn-danger" onclick="event.stopPropagation(); stopAllLedTargets()" data-stop-all="led">${ICON_STOP}</button>` });
|
||||
const csKCTargets = new CardSection('kc-targets', { titleKey: 'targets.section.key_colors', gridClass: 'devices-grid', addCardOnclick: "showKCEditor()", keyAttr: 'data-kc-target-id', headerExtra: `<button class="btn btn-sm btn-danger" onclick="event.stopPropagation(); stopAllKCTargets()" data-stop-all="kc">${ICON_STOP}</button>` });
|
||||
const csLedTargets = new CardSection('led-targets', { titleKey: 'targets.section.targets', gridClass: 'devices-grid', addCardOnclick: "showTargetEditor()", keyAttr: 'data-target-id', headerExtra: `<button class="btn btn-sm btn-danger" onclick="event.stopPropagation(); stopAllLedTargets()" data-stop-all="led" data-i18n-title="targets.stop_all.button" data-i18n-aria-label="targets.stop_all.button">${ICON_STOP}</button>` });
|
||||
const csKCTargets = new CardSection('kc-targets', { titleKey: 'targets.section.key_colors', gridClass: 'devices-grid', addCardOnclick: "showKCEditor()", keyAttr: 'data-kc-target-id', headerExtra: `<button class="btn btn-sm btn-danger" onclick="event.stopPropagation(); stopAllKCTargets()" data-stop-all="kc" data-i18n-title="targets.stop_all.button" data-i18n-aria-label="targets.stop_all.button">${ICON_STOP}</button>` });
|
||||
const csPatternTemplates = new CardSection('kc-patterns', { titleKey: 'targets.section.pattern_templates', gridClass: 'templates-grid', addCardOnclick: "showPatternTemplateEditor()", keyAttr: 'data-pattern-template-id' });
|
||||
|
||||
// Re-render targets tab when language changes (only if tab is active)
|
||||
@@ -731,8 +731,8 @@ export async function loadTargetsTab() {
|
||||
const kcRunning = kcTargets.some(t => t.state && t.state.processing);
|
||||
const ledStopBtn = container.querySelector('[data-stop-all="led"]');
|
||||
const kcStopBtn = container.querySelector('[data-stop-all="kc"]');
|
||||
if (ledStopBtn) ledStopBtn.style.display = ledRunning ? '' : 'none';
|
||||
if (kcStopBtn) kcStopBtn.style.display = kcRunning ? '' : 'none';
|
||||
if (ledStopBtn) { ledStopBtn.style.display = ledRunning ? '' : 'none'; if (!ledStopBtn.title) { ledStopBtn.title = t('targets.stop_all.button'); ledStopBtn.setAttribute('aria-label', t('targets.stop_all.button')); } }
|
||||
if (kcStopBtn) { kcStopBtn.style.display = kcRunning ? '' : 'none'; if (!kcStopBtn.title) { kcStopBtn.title = t('targets.stop_all.button'); kcStopBtn.setAttribute('aria-label', t('targets.stop_all.button')); } }
|
||||
|
||||
// Patch volatile metrics in-place (avoids full card replacement on polls)
|
||||
for (const tgt of ledTargets) {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"auth.logout.success": "Logged out successfully",
|
||||
"auth.please_login": "Please login to view",
|
||||
"auth.session_expired": "Your session has expired or the API key is invalid. Please login again.",
|
||||
"auth.toggle_password": "Toggle password visibility",
|
||||
"displays.title": "Available Displays",
|
||||
"displays.layout": "Displays",
|
||||
"displays.information": "Display Information",
|
||||
@@ -1162,6 +1163,7 @@
|
||||
"value_source.auto_gain.enable": "Enable auto-gain",
|
||||
"value_source.sensitivity": "Sensitivity:",
|
||||
"value_source.sensitivity.hint": "Gain multiplier for the audio signal (higher = more reactive)",
|
||||
"value_source.scene_sensitivity.hint": "Gain multiplier for the luminance signal (higher = more reactive to brightness changes)",
|
||||
"value_source.smoothing": "Smoothing:",
|
||||
"value_source.smoothing.hint": "Temporal smoothing (0 = instant response, 1 = very smooth/slow)",
|
||||
"value_source.audio_min_value": "Min Value:",
|
||||
@@ -1309,6 +1311,7 @@
|
||||
"target.error.stop_failed": "Failed to stop target",
|
||||
"target.error.clone_failed": "Failed to clone target",
|
||||
"target.error.delete_failed": "Failed to delete target",
|
||||
"targets.stop_all.button": "Stop All",
|
||||
"targets.stop_all.none_running": "No targets are currently running",
|
||||
"targets.stop_all.stopped": "Stopped {count} target(s)",
|
||||
"targets.stop_all.error": "Failed to stop targets",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"auth.logout.success": "Выход выполнен успешно",
|
||||
"auth.please_login": "Пожалуйста, войдите для просмотра",
|
||||
"auth.session_expired": "Ваша сессия истекла или API ключ недействителен. Пожалуйста, войдите снова.",
|
||||
"auth.toggle_password": "Показать/скрыть пароль",
|
||||
"displays.title": "Доступные Дисплеи",
|
||||
"displays.layout": "Дисплеи",
|
||||
"displays.information": "Информация о Дисплеях",
|
||||
@@ -1162,6 +1163,7 @@
|
||||
"value_source.auto_gain.enable": "Включить авто-усиление",
|
||||
"value_source.sensitivity": "Чувствительность:",
|
||||
"value_source.sensitivity.hint": "Множитель усиления аудиосигнала (выше = более реактивный)",
|
||||
"value_source.scene_sensitivity.hint": "Множитель усиления сигнала яркости (выше = более чувствительный к изменениям яркости)",
|
||||
"value_source.smoothing": "Сглаживание:",
|
||||
"value_source.smoothing.hint": "Временное сглаживание (0 = мгновенный отклик, 1 = очень плавный/медленный)",
|
||||
"value_source.audio_min_value": "Мин. значение:",
|
||||
@@ -1309,6 +1311,7 @@
|
||||
"target.error.stop_failed": "Не удалось остановить цель",
|
||||
"target.error.clone_failed": "Не удалось клонировать цель",
|
||||
"target.error.delete_failed": "Не удалось удалить цель",
|
||||
"targets.stop_all.button": "Остановить все",
|
||||
"targets.stop_all.none_running": "Нет запущенных целей",
|
||||
"targets.stop_all.stopped": "Остановлено целей: {count}",
|
||||
"targets.stop_all.error": "Не удалось остановить цели",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"auth.logout.success": "已成功退出",
|
||||
"auth.please_login": "请先登录",
|
||||
"auth.session_expired": "会话已过期或 API 密钥无效,请重新登录。",
|
||||
"auth.toggle_password": "切换密码可见性",
|
||||
"displays.title": "可用显示器",
|
||||
"displays.layout": "显示器",
|
||||
"displays.information": "显示器信息",
|
||||
@@ -1162,6 +1163,7 @@
|
||||
"value_source.auto_gain.enable": "启用自动增益",
|
||||
"value_source.sensitivity": "灵敏度:",
|
||||
"value_source.sensitivity.hint": "音频信号的增益倍数(越高反应越灵敏)",
|
||||
"value_source.scene_sensitivity.hint": "亮度信号的增益倍数(越高对亮度变化越敏感)",
|
||||
"value_source.smoothing": "平滑:",
|
||||
"value_source.smoothing.hint": "时间平滑(0 = 即时响应,1 = 非常平滑/缓慢)",
|
||||
"value_source.audio_min_value": "最小值:",
|
||||
@@ -1309,6 +1311,7 @@
|
||||
"target.error.stop_failed": "停止目标失败",
|
||||
"target.error.clone_failed": "克隆目标失败",
|
||||
"target.error.delete_failed": "删除目标失败",
|
||||
"targets.stop_all.button": "全部停止",
|
||||
"targets.stop_all.none_running": "当前没有运行中的目标",
|
||||
"targets.stop_all.stopped": "已停止 {count} 个目标",
|
||||
"targets.stop_all.error": "停止目标失败",
|
||||
|
||||
Reference in New Issue
Block a user