Fix unreachable mobile Settings link and raw error tracebacks
All checks were successful
Deploy / deploy (push) Successful in 2m6s

Settings lived inside .header-right, which is hidden below 600px,
leaving no way to reach it on mobile. It's now a standalone 44x44px
button next to the title.

Dashboard error cards showed raw Python/urllib3 exception strings.
Added a friendly_error() filter that classifies the failure (timeout,
connection refused, DNS, auth, 4xx/5xx, TLS) into plain-English copy,
with the raw string still available behind a details disclosure.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Barely Removable 2026-08-25 23:59:07 -07:00
parent 9804283396
commit 2e1f8efa33
2 changed files with 133 additions and 16 deletions

View file

@ -163,11 +163,40 @@
font-style: italic;
}
.header-right {
display: flex;
align-items: center;
gap: 0.7rem;
font-family: var(--brand-font);
font-size: 0.85rem;
color: var(--muted);
text-align: right;
}
.header-meta { text-align: right; }
.header-clock {
font-size: 1.1rem;
font-family: var(--brand-font);
color: var(--gold);
letter-spacing: 1px;
}
.header-room { font-size: 0.7rem; color: var(--muted); margin-top: 0.1rem; }
.settings-link {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.35rem;
min-width: 44px;
min-height: 44px;
padding: 0 0.7rem;
background: var(--navy);
border: 1px solid var(--border);
border-radius: 8px;
color: var(--muted);
text-decoration: none;
font-size: 0.78rem;
white-space: nowrap;
transition: color 0.2s, border-color 0.2s;
}
.settings-link:hover { color: var(--gold); border-color: var(--gold); }
/* ── Main layout ── */
main { padding: 1.5rem 2rem; max-width: 1280px; margin: 0 auto; }
@ -790,6 +819,34 @@
font-size: 0.85rem;
margin-bottom: 1rem;
}
.error-detail { margin-top: 0.5rem; }
.error-detail summary {
cursor: pointer;
font-size: 0.75rem;
color: var(--muted);
list-style: none;
display: inline-flex;
align-items: center;
gap: 0.3rem;
padding: 0.25rem 0;
}
.error-detail summary::-webkit-details-marker { display: none; }
.error-detail summary::before { content: "▸"; font-size: 0.7rem; }
.error-detail[open] summary::before { content: "▾"; }
.error-detail summary:hover { color: var(--text); }
.error-detail pre {
margin: 0.4rem 0 0;
padding: 0.6rem 0.7rem;
background: rgba(0,0,0,0.25);
border-radius: 6px;
font-size: 0.7rem;
line-height: 1.45;
color: var(--muted);
white-space: pre-wrap;
word-break: break-word;
overflow-x: auto;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.rt-error { font-size: 0.8rem; color: #ff8080; padding: 0.4rem 0; }
/* ── Footer quote ── */
@ -820,7 +877,12 @@
}
header h1 { font-size: 1.05rem; }
.header-sub { display: none; }
/* Title and Settings share the first row; the pill strip wraps below */
.header-left { order: 1; min-width: 0; flex: 1 1 auto; gap: 0.6rem; }
.header-left h1 { overflow-wrap: anywhere; }
.header-right { order: 2; margin-left: auto; flex: 0 0 auto; }
.header-services {
order: 3;
width: 100%;
overflow-x: auto;
flex-wrap: nowrap;
@ -901,12 +963,28 @@
.rt-title { font-size: 0.8rem; }
.rt-meta { font-size: 0.68rem; }
.svc-pill-stat { display: none; }
.header-right { display: none; }
/* Keep the Settings link reachable on mobile — only the clock/room drop out */
.header-meta { display: none; }
.settings-label { display: none; }
.settings-link { padding: 0; }
.stat .value { font-size: 1.4rem; }
.search-bar input { font-size: 16px; } /* prevent iOS zoom */
}
</style>
</head>
{#- Renders a degraded service as human copy, with the raw exception tucked
behind a disclosure for debugging. -#}
{% macro service_error(name, raw) %}
<div class="error" role="alert">
<div>{{ raw | friendly_error(name) }}</div>
{% if raw %}
<details class="error-detail">
<summary>Technical details</summary>
<pre>{{ raw }}</pre>
</details>
{% endif %}
</div>
{% endmacro %}
<body>
<header>
@ -1064,15 +1142,19 @@
</div>
<div class="header-right">
<div id="header-clock" style="font-size:1.1rem; font-family:var(--brand-font); color:var(--gold); letter-spacing:1px;"></div>
<div style="font-size:0.7rem; color:var(--muted); margin-top:0.1rem;">
{% if theme == 'office' %}Dunder Mifflin HQ
{% elif theme == 'parks' %}Pawnee City Hall
{% elif theme == 'potter' %}Hogwarts Great Hall
{% elif theme == 'starwars' %}Death Star Control Room
{% else %}Study Room F{% endif %}
&nbsp;·&nbsp; <a href="/settings" style="color:var(--muted); text-decoration:none;">⚙ Settings</a>
<div class="header-meta">
<div id="header-clock" class="header-clock"></div>
<div class="header-room">
{% if theme == 'office' %}Dunder Mifflin HQ
{% elif theme == 'parks' %}Pawnee City Hall
{% elif theme == 'potter' %}Hogwarts Great Hall
{% elif theme == 'starwars' %}Death Star Control Room
{% else %}Study Room F{% endif %}
</div>
</div>
<a href="{{ url_for('settings_page') }}" class="settings-link" aria-label="Settings">
<span aria-hidden="true"></span><span class="settings-label">Settings</span>
</a>
</div>
</header>
@ -1138,7 +1220,7 @@
</div>
{% endif %}
{% else %}
<div class="error">Could not load system stats: {{ unraid.error }}</div>
{{ service_error("Unraid", unraid.error) }}
{% endif %}
</div>
</div>
@ -1174,7 +1256,7 @@
</div>
</div>
{% else %}
<div class="error">Could not reach Sonarr: {{ sonarr.error }}</div>
{{ service_error("Sonarr", sonarr.error) }}
{% endif %}
<div class="section-title">🍅 Popular on RT</div>
@ -1242,7 +1324,7 @@
</div>
</div>
{% else %}
<div class="error">Could not reach Radarr: {{ radarr.error }}</div>
{{ service_error("Radarr", radarr.error) }}
{% endif %}
<div class="section-title">🍅 Top Box Office on RT</div>
@ -1301,7 +1383,7 @@
{% endfor %}
</div>
{% else %}
<div class="error">Could not reach Prowlarr: {{ prowlarr.error }}</div>
{{ service_error("Prowlarr", prowlarr.error) }}
{% endif %}
</div>
</div>
@ -1351,7 +1433,7 @@
⏱ ETA: <span id="sab-timeleft"></span>
</div>
{% else %}
<div class="error">Could not reach SABnzbd: {{ sabnzbd.error }}</div>
{{ service_error("SABnzbd", sabnzbd.error) }}
{% endif %}
</div>
</div>
@ -1387,7 +1469,7 @@
<div style="color:var(--muted); font-size:0.85rem;">No recent requests.</div>
{% endif %}
{% else %}
<div class="error">Could not reach Ombi: {{ ombi.error }}</div>
{{ service_error("Ombi", ombi.error) }}
{% endif %}
</div>
</div>
@ -1509,7 +1591,7 @@
</div>
{% else %}
<div class="error">Could not reach Tautulli: {{ tautulli.error }}</div>
{{ service_error("Tautulli", tautulli.error) }}
{% endif %}
</div>
</div>