Applies the fix round from the finish-reviewer's disposition on commit
4b19cbd, in one batch:
- System's disk table is unreadable below 560px: horizontal-scrolling a
6-column table hid the usage bar and the temperature, the two
readings that matter most (discipline 3 — one shared ruler — was
unauditable as a result). Added a disk_row() macro and a phone-only
.tt-disk-rows reflow (name + full 20-cell bar + temp on one line,
device/free/total beneath), CSS-toggled against the existing table
at the same breakpoint.
- Contrast failure on the highest-severity state: white text on the
red .tt-field.crit/.tt-toast.error/.tt-btn.armed reversals measured
4.0:1, under the 4.5:1 floor. Switched to black text (5.25:1),
matching the black-on-yellow .warn pattern already in use — and
fixed a second instance of the same bug: .tt-field.crit had no
.tt-field-meta override, unlike .warn.
- Two type families where the direction contract names one blocky
monospace: unified on a single self-hosted face (Space Mono, one
weight, no bold) for both body and the double-height row.
- FIRST VIEWPORT proportion: an unpromised 6-item Just Added grid was
competing with NOW SHOWING for the fold instead of the contract's
"lower two-thirds in double-height cyan." Cut to a fixed 3-item,
fixed-3-column row, which also fixes the orphaned-tile grid defect
as a side effect (a fixed count can't leave a lone tile beside empty
tracks).
- Three flat mid-grey fills contradicted discipline 1 (no in-between
tone): .tt-tile-art, .tt-btn:disabled, and .tt-err pre all painted a
solid --ink-dimmer background. Centralized the dither .tt-tile-art
already used correctly into a shared --dither-mid generator and
applied it to all three.
- Google Fonts dependency on a LAN-only home server: a dropped webfont
request was a silent identity change, not a graceful fallback.
Self-hosted Space Mono under static/fonts/ via @font-face; dropped
the <link> tags from _shell.html and login.html.
- Deliberately deferred: "pages differ in stock" (discipline 5) still
fires on only one band service-wide. Every other finding was a
bounded fix to existing code; this one asks for a new mechanism (an
aggregate status band on P100) rather than a correction, so it's
flagged for a follow-up rather than rushed into this batch.
Recaptured desktop.png, mobile.png, desktop-system.png and
mobile-system.png (the four files the reviewer's findings cited)
through the same true-390px iframe method as the prior round, and
returned them for the fix round's verdict pass.
Known follow-up: DESIGN.md's typography section (written by the
documenter before this batch) still names the old two-family pairing
and needs a re-run now that the font system changed under it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBw1QodG8ZMYHrGeQfqyDS
163 lines
7.5 KiB
HTML
163 lines
7.5 KiB
HTML
{% extends "_shell.html" %}
|
|
{% from "_macros.html" import service_error, bar, disk_state, disk_row, trend %}
|
|
{% block title %}P105 System{% endblock %}
|
|
|
|
{% block content %}
|
|
<h1 class="tt-dh bloom tt-masthead">SYSTEM</h1>
|
|
|
|
{#- ── Service transmission status ───────────────────────────────
|
|
Six services, one row each, colour carrying up or down. This is
|
|
what the old header pills were, given room to say something. -#}
|
|
<div class="tt-field"><span>Transmission</span></div>
|
|
<div class="tt-rows">
|
|
{% for name, svc in [('Sonarr', sonarr), ('Radarr', radarr), ('SABnzbd', sabnzbd),
|
|
('Tautulli', tautulli), ('Prowlarr', prowlarr), ('Ombi', ombi)] %}
|
|
<div class="tt-row">
|
|
<span class="tt-row-name">{{ name }}</span>
|
|
<span class="tt-row-body"></span>
|
|
<span class="tt-row-val {{ 'ok' if svc.status == 'ok' else 'alert' }}">
|
|
{{- 'ON AIR' if svc.status == 'ok' else 'OFF AIR' -}}
|
|
</span>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
{#- ── Array ───────────────────────────────────────────────────── -#}
|
|
{% if unraid.status != 'ok' %}
|
|
<div class="tt-field crit"><span>Array</span><span class="tt-field-meta">unreachable</span></div>
|
|
{{ service_error('Unraid', unraid.error) }}
|
|
{% else %}
|
|
{%- set apct = unraid.array_used_pct | default(0) | float -%}
|
|
<div class="tt-field {% if apct >= 90 %}crit{% elif apct >= 75 %}warn{% endif %}">
|
|
<span>Array</span>
|
|
<span class="tt-field-meta">
|
|
{{ unraid.array_state | default('') }}
|
|
{%- if unraid.parities %} · parity
|
|
{%- for p in unraid.parities %} {{ p.temp if p.temp is not none else '—' }}°C{% endfor %}
|
|
{%- endif %}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="tt-figs">
|
|
<div class="tt-fig">
|
|
<div class="tt-dh tt-fig-v {{ 'alert' if apct >= 90 else ('warn' if apct >= 75 else 'ok') }}">{{ apct | round | int }}%</div>
|
|
<div class="tt-fig-l">Used</div>
|
|
</div>
|
|
<div class="tt-fig">
|
|
<div class="tt-dh tt-fig-v">{{ unraid.array_free | default('—') }}</div>
|
|
<div class="tt-fig-l">Free</div>
|
|
</div>
|
|
<div class="tt-fig">
|
|
<div class="tt-dh tt-fig-v">{{ unraid.array_total | default('—') }}</div>
|
|
<div class="tt-fig-l">Total</div>
|
|
</div>
|
|
<div class="tt-fig">
|
|
<div class="tt-dh tt-fig-v">{{ unraid.disk_count | default(0) }}</div>
|
|
<div class="tt-fig-l">Disks</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if unraid.disks %}
|
|
{#- Desktop: a real table — columns line up, a new metric is a new
|
|
column. Hidden below 560px, where six columns stop fitting and
|
|
a horizontal scroll would hide the bar and the temperature,
|
|
the two readings that matter most on this page. -#}
|
|
<div class="tt-table-scroll">
|
|
<table class="tt-table">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Disk</th>
|
|
<th scope="col">Device</th>
|
|
<th scope="col" class="num">Size</th>
|
|
<th scope="col" class="num">Free</th>
|
|
<th scope="col">Usage</th>
|
|
<th scope="col" class="num">Temp</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for dk in unraid.disks %}
|
|
<tr>
|
|
<th scope="row">{{ dk.name }}</th>
|
|
<td>{{ dk.device | default('—') }}</td>
|
|
<td class="num">{{ dk.total | default('—') }}</td>
|
|
<td class="num">{{ dk.free | default('—') }}</td>
|
|
<td>{{ bar(dk.used_pct | default(0)) }} <span class="val">{{ dk.used_pct | default(0) | round | int }}%</span></td>
|
|
<td class="num">{{ disk_state(dk) }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
{% for ck in unraid.caches | default([]) %}
|
|
<tr>
|
|
<th scope="row">{{ ck.name }}</th>
|
|
<td>{{ ck.device | default('—') }}</td>
|
|
<td class="num">{{ ck.total | default('—') }}</td>
|
|
<td class="num">{{ ck.free | default('—') }}</td>
|
|
<td>{{ bar(ck.used_pct | default(0)) }} <span class="val">{{ ck.used_pct | default(0) | round | int }}%</span></td>
|
|
<td class="num">{{ disk_state(ck) }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{#- Phone (≤560px): the .tt-row-derived reflow — name, the shared
|
|
20-cell bar and the temperature on one line, device/size/free
|
|
beneath. Hidden above 560px in favour of the table. -#}
|
|
<div class="tt-disk-rows">
|
|
{% for dk in unraid.disks %}{{ disk_row(dk) }}{% endfor %}
|
|
{% for ck in unraid.caches | default([]) %}{{ disk_row(ck) }}{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
{#- ── Downloads ─────────────────────────────────────────────────
|
|
The one place the service holds real history, so the one place a
|
|
trend arrow is honest: current speed against the 7-day average. -#}
|
|
{% if sabnzbd.status == 'ok' %}
|
|
<div class="tt-field"><span>Downloads</span><span class="tt-field-meta">{{ sabnzbd.sab_status | default('') }}</span></div>
|
|
<div class="tt-figs">
|
|
<div class="tt-fig">
|
|
<div class="tt-dh tt-fig-v"><span id="sab-queue">{{ sabnzbd.queue_count }}</span></div>
|
|
<div class="tt-fig-l">In queue</div>
|
|
</div>
|
|
<div class="tt-fig">
|
|
<div class="tt-dh tt-fig-v"><span id="sab-speed">{{ sabnzbd.speed | default('0') }}</span> {{ trend(sabnzbd.speed_kbps, sabnzbd.avg_speed) }}</div>
|
|
<div class="tt-fig-l">Speed vs 7-day avg</div>
|
|
</div>
|
|
<div class="tt-fig">
|
|
<div class="tt-dh tt-fig-v"><span id="sab-remaining">{{ sabnzbd.sizeleft | default('—') }}</span></div>
|
|
<div class="tt-fig-l">Remaining</div>
|
|
</div>
|
|
<div class="tt-fig">
|
|
<div class="tt-dh tt-fig-v">{{ sabnzbd.week_size | default('—') }}</div>
|
|
<div class="tt-fig-l">This week</div>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="tt-field crit"><span>Downloads</span><span class="tt-field-meta">off air</span></div>
|
|
{{ service_error('SABnzbd', sabnzbd.error) }}
|
|
{% endif %}
|
|
|
|
{#- ── Indexers ────────────────────────────────────────────────── -#}
|
|
{% if prowlarr.status == 'ok' %}
|
|
<div class="tt-field">
|
|
<span>Indexers</span>
|
|
<span class="tt-field-meta">{{ prowlarr.up_count }} of {{ prowlarr.total }} up</span>
|
|
</div>
|
|
<div class="tt-rows">
|
|
{% for idx in prowlarr.indexers %}
|
|
<div class="tt-row">
|
|
<span class="tt-row-name">{{ idx.protocol | default('') }}</span>
|
|
<span class="tt-row-body">
|
|
<span class="tt-row-title">{{ idx.name }}</span>
|
|
{% if prowlarr.has_stats %}<span class="tt-row-sub">{{ idx.grabs | default(0) }} grabs · {{ idx.queries | default(0) }} queries</span>{% endif %}
|
|
</span>
|
|
{% if prowlarr.has_stats %}{{ bar(idx.grab_share | default(0), 'ok' if idx.up else 'alert') }}{% endif %}
|
|
<span class="tt-row-val {{ 'ok' if idx.up else 'alert' }}">{{ 'UP' if idx.up else 'DOWN' }}</span>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="tt-field crit"><span>Indexers</span><span class="tt-field-meta">off air</span></div>
|
|
{{ service_error('Prowlarr', prowlarr.error) }}
|
|
{% endif %}
|
|
{% endblock %}
|