@@ -92,6 +96,7 @@
const SRC_LABEL={recently:'RECENTLY ADDED',unread:'ON DECK',list:'READING'};
const card=document.getElementById('card'), shelf=document.getElementById('shelf'), lbl=document.getElementById('lbl');
+ const list=document.getElementById('books');
const info=document.getElementById('info'), iT=document.getElementById('i-t'), iA=document.getElementById('i-a'), iP=document.getElementById('i-p'), sr=document.getElementById('sr');
let href='';
@@ -112,12 +117,17 @@
function hideInfo(){ info.classList.remove('show'); info.setAttribute('aria-hidden','true'); }
function render(books){
- shelf.querySelectorAll('.bk,.empty').forEach(n=>n.remove());
+ list.innerHTML='';
+ shelf.querySelectorAll('.empty').forEach(n=>n.remove());
+ /* Removing the bullets drops the list semantics in one engine, so the role
+ is declared even on a real list. It is set only while there are rows: an
+ empty list that declares the role reports as missing its children. */
+ list.removeAttribute('role');
if(!books.length){ const e=document.createElement('div'); e.className='empty'; e.textContent=wt('ui.noBooks','No books'); shelf.appendChild(e); return; }
+ list.setAttribute('role','list');
const tallestIdx=books.reduce((mi,b,i,a)=>(b._h>a[mi]._h?i:mi),0);
- const ledgeTop=shelf.querySelector('.ledge-top');
books.forEach((b,i)=>{
- const el=document.createElement('div'); el.className='bk';
+ const el=document.createElement('li'); el.className='bk';
el.style.width=b._w+'px'; el.style.height=b._h+'px'; el.style.background=b._color;
const dark = lum(b._color) > 0.62;
const sp=document.createElement('span'); sp.className='spine';
@@ -126,14 +136,14 @@
if(i===tallestIdx){ const rb=document.createElement('div'); rb.className='ribbon'; el.appendChild(rb); }
if(b._w>=17){ const d=document.createElement('div'); d.className='deco'; d.innerHTML='
'; el.appendChild(d); }
el.appendChild(sp);
- el.tabIndex=0; el.setAttribute('role','listitem');
+ el.tabIndex=0;
const [ptxt]=progressText(b);
el.setAttribute('aria-label',(b.title||wt('ui.untitled','Untitled'))+(b.author?' '+wt('ui.by','by')+' '+b.author:'')+', '+ptxt);
const on=()=>{ el.classList.add('up'); showInfo(b); };
const off=()=>{ el.classList.remove('up'); hideInfo(); };
el.addEventListener('mouseenter',on); el.addEventListener('mouseleave',off);
el.addEventListener('focus',on); el.addEventListener('blur',off);
- shelf.insertBefore(el, ledgeTop);
+ list.appendChild(el);
});
}
diff --git a/public/widgets/clock/digital.html b/public/widgets/clock/digital.html
index 8ece5ed..3e0c2d0 100644
--- a/public/widgets/clock/digital.html
+++ b/public/widgets/clock/digital.html
@@ -37,8 +37,8 @@
white-space: nowrap; line-height: 1; flex-shrink: 0;
padding-top: 2px;
}
- .mode-dark .date-line { color: rgba(255,255,255,0.38); }
- .mode-light .date-line { color: rgba(0,0,0,0.33); }
+ .mode-dark .date-line { color: rgba(255,255,255,0.55); }
+ .mode-light .date-line { color: rgba(0,0,0,0.55); }
@media (prefers-reduced-motion: reduce){ *{transition:none !important;animation:none !important} }
diff --git a/public/widgets/connections/connections-map.html b/public/widgets/connections/connections-map.html
index 556d2a8..d33f079 100644
--- a/public/widgets/connections/connections-map.html
+++ b/public/widgets/connections/connections-map.html
@@ -168,7 +168,7 @@
dot.style.backgroundColor=dotColor;
dot.style.boxShadow='0 0 5px '+dotColor;
it.append(dot,document.createTextNode(l.name));
- it.setAttribute('aria-label', l.name+(clickable?' — open admin':''));
+ it.setAttribute('aria-label', l.name+(clickable?', '+wt('ui.openAdmin','open admin'):''));
const emph=()=>{emphasis=l.role;it.style.background='rgba(255,255,255,.10)';legendEl.querySelectorAll('button').forEach(b=>{if(b!==it)b.style.opacity='.45';});kick();};
const clear=()=>{emphasis=null;legendEl.querySelectorAll('button').forEach(b=>{b.style.background='none';b.style.opacity='1';});kick();};
it.addEventListener('pointerenter',emph);
diff --git a/public/widgets/connections/i18n/de.json b/public/widgets/connections/i18n/de.json
index 206c50b..5bda644 100644
--- a/public/widgets/connections/i18n/de.json
+++ b/public/widgets/connections/i18n/de.json
@@ -68,5 +68,6 @@
"ui.noData": "Keine Daten",
"ui.worldMap": "Weltkarte der Verbindungen",
"ui.service": "Dienst",
- "ui.users": "Benutzer"
+ "ui.users": "Benutzer",
+ "ui.openAdmin": "Verwaltung öffnen"
}
diff --git a/public/widgets/connections/i18n/en.json b/public/widgets/connections/i18n/en.json
index 1aecf6d..20c74fe 100644
--- a/public/widgets/connections/i18n/en.json
+++ b/public/widgets/connections/i18n/en.json
@@ -68,5 +68,6 @@
"ui.noData": "No data",
"ui.worldMap": "World map of connections",
"ui.service": "Service",
- "ui.users": "Users"
+ "ui.users": "Users",
+ "ui.openAdmin": "open admin"
}
diff --git a/public/widgets/connections/i18n/es.json b/public/widgets/connections/i18n/es.json
index ba3e2c4..9209ddc 100644
--- a/public/widgets/connections/i18n/es.json
+++ b/public/widgets/connections/i18n/es.json
@@ -68,5 +68,6 @@
"ui.noData": "Sin datos",
"ui.worldMap": "Mapa mundial de conexiones",
"ui.service": "Servicio",
- "ui.users": "Usuarios"
+ "ui.users": "Usuarios",
+ "ui.openAdmin": "abrir administración"
}
diff --git a/public/widgets/connections/i18n/fa.json b/public/widgets/connections/i18n/fa.json
index e6cd6e7..738e15e 100644
--- a/public/widgets/connections/i18n/fa.json
+++ b/public/widgets/connections/i18n/fa.json
@@ -68,5 +68,6 @@
"ui.noData": "دادهای نیست",
"ui.worldMap": "نقشهٔ جهانی اتصالها",
"ui.service": "سرویس",
- "ui.users": "کاربران"
+ "ui.users": "کاربران",
+ "ui.openAdmin": "باز کردن مدیریت"
}
diff --git a/public/widgets/connections/i18n/fr.json b/public/widgets/connections/i18n/fr.json
index adf3b0b..4492afd 100644
--- a/public/widgets/connections/i18n/fr.json
+++ b/public/widgets/connections/i18n/fr.json
@@ -68,5 +68,6 @@
"ui.noData": "Aucune donnée",
"ui.worldMap": "Carte mondiale des connexions",
"ui.service": "Service",
- "ui.users": "Utilisateurs"
+ "ui.users": "Utilisateurs",
+ "ui.openAdmin": "ouvrir l'administration"
}
diff --git a/public/widgets/connections/i18n/zh-Hans.json b/public/widgets/connections/i18n/zh-Hans.json
index 7b0d6ef..1d79fdc 100644
--- a/public/widgets/connections/i18n/zh-Hans.json
+++ b/public/widgets/connections/i18n/zh-Hans.json
@@ -68,5 +68,6 @@
"ui.noData": "暂无数据",
"ui.worldMap": "全球连接地图",
"ui.service": "服务",
- "ui.users": "用户"
+ "ui.users": "用户",
+ "ui.openAdmin": "打开管理"
}
diff --git a/public/widgets/dashboard-switch/i18n/de.json b/public/widgets/dashboard-switch/i18n/de.json
index f0346a4..56fd3de 100644
--- a/public/widgets/dashboard-switch/i18n/de.json
+++ b/public/widgets/dashboard-switch/i18n/de.json
@@ -20,5 +20,6 @@
"openIn.opt.same": "Gleichem Tab",
"openIn.opt.new": "Neuem Tab",
"ui.switcher": "Dashboard-Umschalter",
- "ui.empty": "Fügen Sie in den Einstellungen ein Dashboard hinzu"
+ "ui.empty": "Fügen Sie in den Einstellungen ein Dashboard hinzu",
+ "ui.switchTo": "Zu {name} wechseln"
}
diff --git a/public/widgets/dashboard-switch/i18n/en.json b/public/widgets/dashboard-switch/i18n/en.json
index 88c55bc..c15e7fe 100644
--- a/public/widgets/dashboard-switch/i18n/en.json
+++ b/public/widgets/dashboard-switch/i18n/en.json
@@ -20,5 +20,6 @@
"openIn.opt.same": "Same tab",
"openIn.opt.new": "New tab",
"ui.switcher": "Dashboard switcher",
- "ui.empty": "Add a dashboard in settings"
+ "ui.empty": "Add a dashboard in settings",
+ "ui.switchTo": "Switch to {name}"
}
diff --git a/public/widgets/dashboard-switch/i18n/es.json b/public/widgets/dashboard-switch/i18n/es.json
index fda4e12..8970c0c 100644
--- a/public/widgets/dashboard-switch/i18n/es.json
+++ b/public/widgets/dashboard-switch/i18n/es.json
@@ -20,5 +20,6 @@
"openIn.opt.same": "La misma pestaña",
"openIn.opt.new": "Una pestaña nueva",
"ui.switcher": "Selector de paneles",
- "ui.empty": "Añade un panel en los ajustes"
+ "ui.empty": "Añade un panel en los ajustes",
+ "ui.switchTo": "Cambiar a {name}"
}
diff --git a/public/widgets/dashboard-switch/i18n/fa.json b/public/widgets/dashboard-switch/i18n/fa.json
index 0493aa4..5bbd8c0 100644
--- a/public/widgets/dashboard-switch/i18n/fa.json
+++ b/public/widgets/dashboard-switch/i18n/fa.json
@@ -20,5 +20,6 @@
"openIn.opt.same": "همین زبانه",
"openIn.opt.new": "زبانهٔ جدید",
"ui.switcher": "تعویضکنندهٔ داشبورد",
- "ui.empty": "در تنظیمات یک داشبورد اضافه کنید"
+ "ui.empty": "در تنظیمات یک داشبورد اضافه کنید",
+ "ui.switchTo": "رفتن به {name}"
}
diff --git a/public/widgets/dashboard-switch/i18n/fr.json b/public/widgets/dashboard-switch/i18n/fr.json
index 5d77744..6ed6e6c 100644
--- a/public/widgets/dashboard-switch/i18n/fr.json
+++ b/public/widgets/dashboard-switch/i18n/fr.json
@@ -20,5 +20,6 @@
"openIn.opt.same": "Le même onglet",
"openIn.opt.new": "Un nouvel onglet",
"ui.switcher": "Sélecteur de tableaux de bord",
- "ui.empty": "Ajoutez un tableau de bord dans les réglages"
+ "ui.empty": "Ajoutez un tableau de bord dans les réglages",
+ "ui.switchTo": "Basculer vers {name}"
}
diff --git a/public/widgets/dashboard-switch/i18n/zh-Hans.json b/public/widgets/dashboard-switch/i18n/zh-Hans.json
index 27e17cd..bdd47b2 100644
--- a/public/widgets/dashboard-switch/i18n/zh-Hans.json
+++ b/public/widgets/dashboard-switch/i18n/zh-Hans.json
@@ -20,5 +20,6 @@
"openIn.opt.same": "当前标签页",
"openIn.opt.new": "新标签页",
"ui.switcher": "面板切换器",
- "ui.empty": "请在设置中添加一个面板"
+ "ui.empty": "请在设置中添加一个面板",
+ "ui.switchTo": "切换到 {name}"
}
diff --git a/public/widgets/dashboard-switch/index.html b/public/widgets/dashboard-switch/index.html
index e9f5172..65d2ac9 100644
--- a/public/widgets/dashboard-switch/index.html
+++ b/public/widgets/dashboard-switch/index.html
@@ -122,7 +122,7 @@
const a = document.createElementNS(NS, 'a');
a.setAttribute('class', s.isCurrent ? 'kc cur' : 'kc');
a.setAttribute('tabindex', '0');
- a.setAttribute('aria-label', `Switch to ${s.name}`);
+ a.setAttribute('aria-label', wt('ui.switchTo', 'Switch to {name}').replace('{name}', s.name));
a.setAttributeNS(null, 'href', s.href);
if (model.openIn === 'new') { a.setAttribute('target', '_blank'); a.setAttribute('rel', 'noopener noreferrer'); }
else { a.setAttribute('target', '_top'); }
diff --git a/public/widgets/disk-health/i18n/de.json b/public/widgets/disk-health/i18n/de.json
index 76b9ab2..46df5a6 100644
--- a/public/widgets/disk-health/i18n/de.json
+++ b/public/widgets/disk-health/i18n/de.json
@@ -21,5 +21,7 @@
"ui.healthy": "In Ordnung",
"ui.warning": "Warnung",
"ui.failed": "Ausgefallen",
- "ui.ageAlert": "Alterswarnung"
+ "ui.ageAlert": "Alterswarnung",
+ "ui.bay": "Schacht {n}",
+ "ui.bayEmpty": "Schacht {n}: leer"
}
diff --git a/public/widgets/disk-health/i18n/en.json b/public/widgets/disk-health/i18n/en.json
index a7f77cf..f7c05b4 100644
--- a/public/widgets/disk-health/i18n/en.json
+++ b/public/widgets/disk-health/i18n/en.json
@@ -21,5 +21,7 @@
"ui.healthy": "Healthy",
"ui.warning": "Warning",
"ui.failed": "Failed",
- "ui.ageAlert": "Age Alert"
+ "ui.ageAlert": "Age Alert",
+ "ui.bay": "Bay {n}",
+ "ui.bayEmpty": "Bay {n}: empty"
}
diff --git a/public/widgets/disk-health/i18n/es.json b/public/widgets/disk-health/i18n/es.json
index 111d54e..87dddd9 100644
--- a/public/widgets/disk-health/i18n/es.json
+++ b/public/widgets/disk-health/i18n/es.json
@@ -21,5 +21,7 @@
"ui.healthy": "Correcto",
"ui.warning": "Aviso",
"ui.failed": "Fallido",
- "ui.ageAlert": "Aviso por antigüedad"
+ "ui.ageAlert": "Aviso por antigüedad",
+ "ui.bay": "Bahía {n}",
+ "ui.bayEmpty": "Bahía {n}: vacía"
}
diff --git a/public/widgets/disk-health/i18n/fa.json b/public/widgets/disk-health/i18n/fa.json
index 171e4e5..80fd6ac 100644
--- a/public/widgets/disk-health/i18n/fa.json
+++ b/public/widgets/disk-health/i18n/fa.json
@@ -21,5 +21,7 @@
"ui.healthy": "سالم",
"ui.warning": "هشدار",
"ui.failed": "خراب",
- "ui.ageAlert": "هشدار فرسودگی"
+ "ui.ageAlert": "هشدار فرسودگی",
+ "ui.bay": "جایگاه {n}",
+ "ui.bayEmpty": "جایگاه {n}: خالی"
}
diff --git a/public/widgets/disk-health/i18n/fr.json b/public/widgets/disk-health/i18n/fr.json
index 374e816..a86f900 100644
--- a/public/widgets/disk-health/i18n/fr.json
+++ b/public/widgets/disk-health/i18n/fr.json
@@ -21,5 +21,7 @@
"ui.healthy": "Bon état",
"ui.warning": "Avertissement",
"ui.failed": "En échec",
- "ui.ageAlert": "Alerte de vieillissement"
+ "ui.ageAlert": "Alerte de vieillissement",
+ "ui.bay": "Baie {n}",
+ "ui.bayEmpty": "Baie {n} : vide"
}
diff --git a/public/widgets/disk-health/i18n/zh-Hans.json b/public/widgets/disk-health/i18n/zh-Hans.json
index c46adf1..cdf8f32 100644
--- a/public/widgets/disk-health/i18n/zh-Hans.json
+++ b/public/widgets/disk-health/i18n/zh-Hans.json
@@ -21,5 +21,7 @@
"ui.healthy": "健康",
"ui.warning": "警告",
"ui.failed": "故障",
- "ui.ageAlert": "老化提醒"
+ "ui.ageAlert": "老化提醒",
+ "ui.bay": "硬盘位 {n}",
+ "ui.bayEmpty": "硬盘位 {n}:空"
}
diff --git a/public/widgets/disk-health/index.html b/public/widgets/disk-health/index.html
index 7ac8973..c95be4b 100644
--- a/public/widgets/disk-health/index.html
+++ b/public/widgets/disk-health/index.html
@@ -275,13 +275,13 @@
const cap = fmtCap(d.capacity);
const st = statusText(d.device_status ?? 0, d.hasSmart ?? false);
bay.setAttribute('role','button'); bay.setAttribute('tabindex','0');
- bay.setAttribute('aria-label', `Bay ${i+1}: ${shortName(d)}${cap ? ', '+cap : ''}${st ? ', '+st : ''}`);
+ bay.setAttribute('aria-label', `${wt('ui.bay','Bay {n}').replace('{n}', i+1)}: ${shortName(d)}${cap ? ', '+cap : ''}${st ? ', '+st : ''}`);
} else {
bay.classList.add('empty'); bay.classList.remove('assigned');
dot.className = '';
nm.textContent = '';
bay.setAttribute('role','img'); bay.removeAttribute('tabindex');
- bay.setAttribute('aria-label', `Bay ${i+1}: empty`);
+ bay.setAttribute('aria-label', wt('ui.bayEmpty','Bay {n}: empty').replace('{n}', i+1));
}
}
}
diff --git a/public/widgets/dns/index.html b/public/widgets/dns/index.html
index cf156c1..22534b1 100644
--- a/public/widgets/dns/index.html
+++ b/public/widgets/dns/index.html
@@ -61,7 +61,7 @@
-