diff --git a/README.md b/README.md
index 2ab336e..5d23813 100644
--- a/README.md
+++ b/README.md
@@ -276,7 +276,8 @@ in three kinds:
next upcoming release that carries artwork also gets a full-width hero tile
above the groups (its relayed thumb plus title and date), ringed in the same
origin hue. Each widget gets its own sidebar button for a
- single-widget view.
+ single-widget view, its left edge tinted with the same origin hue once the
+ widget has returned rows (an errored or empty widget stays accent-blue).
- Writes are admin-only when OIDC is on, reads any session - same gate as
services.
diff --git a/src/uwudash_mk3/static/app.js b/src/uwudash_mk3/static/app.js
index b3d4a8b..05c92ca 100644
--- a/src/uwudash_mk3/static/app.js
+++ b/src/uwudash_mk3/static/app.js
@@ -131,7 +131,8 @@ function renderSidebar() {
}
for (const widget of state.widgets) {
const button = document.createElement("button");
- button.className = "widget-btn" + (state.selected === widget.id ? " active" : "");
+ button.className =
+ "widget-btn" + widgetOriginClass(widget) + (state.selected === widget.id ? " active" : "");
button.title = widget.name;
button.innerHTML =
`${widgetIcon(widget)}` +
@@ -161,8 +162,18 @@ function widgetSub(widget) {
if (widget.kind === "notice") return (widget.body || "").slice(0, 60);
const data = widget.data || {};
if (data.error) return `⚠ ${data.error.error}`;
- const host = (widget.base_url || "").split("://", 1)[1];
- return `${(data.items || []).length} items · ${host}`;
+ // `split("://", 1)[1]` looked like Python but JS returns a one-element array,
+ // so the old code printed "undefined". URL() also keeps the port (`host`).
+ let host = "";
+ if (widget.base_url) {
+ try {
+ host = new URL(widget.base_url).host;
+ } catch (TypeError) {
+ host = widget.base_url;
+ }
+ }
+ const count = (data.items || []).length;
+ return host ? `${count} item${count === 1 ? "" : "s"} · ${host}` : `${count} item${count === 1 ? "" : "s"}`;
}
function renderTable() {
@@ -241,6 +252,16 @@ function originClass(item) {
return ORIGIN_LABEL[item ? item.origin : null] ? ` origin-${item.origin}` : "";
}
+// A widget's app is only knowable from the rows it returned (`calendar` covers
+// both Sonarr and Radarr), so the first recognised origin wins; an errored or
+// empty widget stays neutral.
+function widgetOriginClass(widget) {
+ for (const row of (widget.data || {}).items || []) {
+ if (ORIGIN_LABEL[row.origin]) return ` origin-${row.origin}`;
+ }
+ return "";
+}
+
function releaseCard(item) {
const thumb = item.thumb
? `
`
diff --git a/src/uwudash_mk3/static/styles.css b/src/uwudash_mk3/static/styles.css
index b216383..f51279a 100644
--- a/src/uwudash_mk3/static/styles.css
+++ b/src/uwudash_mk3/static/styles.css
@@ -373,7 +373,10 @@ body {
/* origin tints: Sonarr teal, Radarr violet, Plex orange. The merged timeline
mixes apps, so the card edge, the source chip and a legend chip in the group
- header all carry the same hue. */
+ header all carry the same hue; sidebar chips get it on their left edge too. */
+.widget-btn.origin-sonarr { border-left: 3px solid var(--origin-sonarr); }
+.widget-btn.origin-radarr { border-left: 3px solid var(--origin-radarr); }
+.widget-btn.origin-plex { border-left: 3px solid var(--origin-plex); }
.release-card.origin-sonarr { border-left: 3px solid var(--origin-sonarr); }
.release-card.origin-radarr { border-left: 3px solid var(--origin-radarr); }
.release-card.origin-plex { border-left: 3px solid var(--origin-plex); }