From bc7779e5906522bb439451cf449865de37104021 Mon Sep 17 00:00:00 2001 From: Barely Removable Date: Sat, 29 Aug 2026 21:18:11 -0700 Subject: [PATCH] Fix the finish review's 8 material findings on the teletext redesign MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 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 Claude-Session: https://claude.ai/code/session_01NBw1QodG8ZMYHrGeQfqyDS --- app.py | 7 +- static/css/base.css | 37 +++++++++-- static/css/teletext.css | 92 +++++++++++++++++++++++---- static/fonts/SpaceMono-Regular.woff2 | Bin 0 -> 16520 bytes templates/_macros.html | 20 ++++++ templates/_shell.html | 7 +- templates/home.html | 2 +- templates/login.html | 4 +- templates/system.html | 14 +++- 9 files changed, 155 insertions(+), 28 deletions(-) create mode 100644 static/fonts/SpaceMono-Regular.woff2 diff --git a/app.py b/app.py index d33e3c5..ff2a0b6 100644 --- a/app.py +++ b/app.py @@ -1484,7 +1484,12 @@ def home(): sonarr=sonarr, radarr=radarr, sabnzbd=r["sabnzbd"], tautulli=tautulli, prowlarr=r["prowlarr"], ombi=r["ombi"], unraid=r["unraid"], hero=hero, other_sessions=others, - recently_added=get_recently_added(6), + # 3, not 6: P100's Just Added is a glance, not the library — a + # single fixed row keeps NOW SHOWING holding the lower two-thirds + # of the first viewport per the direction contract, and never + # orphans a lone tile the way a variable-column auto-fill grid + # could at 6. The full feed is what /library is for. + recently_added=get_recently_added(3), rt_movies=_filter_rt(r["rt_movies"], radarr_titles, 6), rt_tv=_filter_rt(r["rt_tv"], sonarr_titles, 6), ) diff --git a/static/css/base.css b/static/css/base.css index 9006d13..634ca04 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -29,6 +29,23 @@ pages is a failure. ═══════════════════════════════════════════════════════ */ +/* Self-hosted, deliberately: this app runs on a LAN-only home server + behind a filtered/offline-capable network, where a Google Fonts + request can simply fail. A dropped @font-face request means every + character on the page silently falls back to whatever monospace the + OS ships — a different world by accident, not a variation of this + one. One weight only (400): there is no bold in this system. */ +@font-face { + font-family: 'Space Mono'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('../fonts/SpaceMono-Regular.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, + U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, + U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + :root { /* ── The locked eight ────────────────────────────────── Teletext had exactly these, at full saturation, and the @@ -67,18 +84,28 @@ --ink-dim: color-mix(in srgb, var(--tt-white) 58%, var(--tt-black)); --ink-dimmer: color-mix(in srgb, var(--tt-white) 30%, var(--tt-black)); + /* The one legal way to fill an area with a mid-tone: every "flat + grey panel" in the codebase (a disabled button, an unloaded poster, + a code block behind an error detail) draws from this generator + rather than declaring --ink-dimmer as a solid background. A solid + fill of a dimmed token is still a ninth colour by another name. */ + --dither-mid: repeating-linear-gradient(45deg, + var(--ink-dimmer) 0 1px, var(--tt-black) 1px calc(var(--dither-size) * 2)); + /* ── Type: one face, exactly two heights ─────────────── - Teletext's entire scale. Emphasis comes from colour and - from doubling the height, never from a weight ramp and - never from a third size. */ + Teletext's entire scale — ONE family, not one for body and a + different one for the double-height row. Emphasis comes from + colour and from doubling the height, never from a weight ramp + (this face loads a single weight, 400, on purpose — there is + no bold anywhere to reach for) and never from a third size. */ --fs: 0.9375rem; /* 15px — the single body size */ --fs2: 1.875rem; /* 30px — double height, the only other size */ --lh: 1.45; --row: calc(var(--fs) * var(--lh)); /* one character row */ --cell: 1ch; - --font: 'Chivo Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - --font-dh: 'DotGothic16', 'Chivo Mono', ui-monospace, Menlo, monospace; + --font: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + --font-dh: var(--font); /* ── Measure ─────────────────────────────────────────── A teletext page was 40 columns. A screen this wide gets diff --git a/static/css/teletext.css b/static/css/teletext.css index 168213e..ad36959 100644 --- a/static/css/teletext.css +++ b/static/css/teletext.css @@ -95,7 +95,15 @@ } /* A page carrying critical state gets a heavier field than a quiet one (discipline 5) — the band goes red and doubles in height. */ -.tt-field.crit { background: var(--alert); color: var(--tt-white); } +.tt-field.crit { + background: var(--alert); + /* Black, not white: white-on-red measures 4.0:1 here, under the + 4.5:1 floor for 15px uppercase text. Black-on-red clears 5.25:1 + and is the correct teletext reversal — every other coloured band + (.warn, a hover row) already reverses to black. */ + color: var(--tt-black); +} +.tt-field.crit .tt-field-meta { color: var(--tt-black); } .tt-field.warn { background: var(--voice); color: var(--tt-black); } .tt-field.warn .tt-field-meta { color: var(--tt-black); } @@ -273,6 +281,41 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); } .tt-table tbody tr:hover th, .tt-table tbody tr:hover td { background: var(--field); color: var(--tt-white); } +/* ── Disk rows (System, phone reflow of the table above) ── + Below 560px a six-column table stops fitting; scrolling it + horizontally hides the bar and the temperature, the two + readings that matter most here. This is the other rendering + of the same disk data, shown only below that width — see the + media query at the bottom of this file for the swap. */ +.tt-disk-rows { display: none; flex-direction: column; } +.tt-disk-row { padding: 0.4em 1ch; } +.tt-disk-row-top { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.4em 1ch; +} +.tt-disk-name { + flex: 0 0 auto; + min-width: 5ch; + color: var(--ink); + letter-spacing: 0.16em; + text-transform: uppercase; +} +.tt-disk-pct { + flex: 0 0 auto; + color: var(--live); + font-variant-numeric: tabular-nums; +} +.tt-disk-row-sub { + margin-top: 0.15em; + color: var(--ink-dim); +} +.tt-disk-row:hover { background: var(--field); } +.tt-disk-row:hover .tt-disk-name, +.tt-disk-row:hover .tt-disk-pct, +.tt-disk-row:hover .tt-disk-row-sub { color: var(--tt-white); } + /* ── Tiles (library / discover) ───────────────────────── Artwork is the one thing in this world that is not made of characters, so it is treated as an inserted transmission: a @@ -284,21 +327,28 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); } grid-template-columns: repeat(auto-fill, minmax(min(12.5ch, 100%), 1fr)); padding: 0 1ch; } +/* P100's Just Added: a fixed row, not a viewport-dependent auto-fill. + Paired with a fixed item count (see home()), this guarantees a full + row every time rather than leaving a lone tile beside empty tracks — + and it keeps this block compact, so NOW SHOWING above it can hold the + lower two-thirds of the first viewport the direction contract names. */ +.tt-tiles-row { + grid-template-columns: repeat(3, 1fr); +} +@media (max-width: 560px) { + .tt-tiles-row { grid-template-columns: repeat(1, 1fr); } +} .tt-tile { display: flex; flex-direction: column; min-width: 0; } .tt-tile-art { display: block; width: 100%; aspect-ratio: 2 / 3; object-fit: cover; - background: var(--ink-dimmer); -} -/* No artwork came back. A dithered field, not a fake poster. */ -.tt-tile-art-empty { - background-image: repeating-linear-gradient( - 45deg, - var(--ink-dimmer) 0 1px, - var(--tt-black) 1px calc(var(--dither-size) * 2) - ); + /* Covers the box while a real poster is still loading, and IS the + fallback for a poster that never arrives — dithered, per + discipline 1, never a flat mid-grey panel. */ + background-color: var(--tt-black); + background-image: var(--dither-mid); } .tt-tile-t { margin-top: 0.4em; @@ -330,8 +380,13 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); } cursor: pointer; } .tt-btn:hover:not(:disabled) { background: var(--live); } -.tt-btn:disabled { background: var(--ink-dimmer); color: var(--ink-dim); cursor: default; } -.tt-btn.armed { background: var(--alert); color: var(--tt-white); } +.tt-btn:disabled { + background-color: var(--tt-black); + background-image: var(--dither-mid); + color: var(--ink-dim); + cursor: default; +} +.tt-btn.armed { background: var(--alert); color: var(--tt-black); } .tt-btn.done { background: transparent; color: var(--ok); cursor: default; } .tt-btn-quiet { background: transparent; @@ -398,7 +453,8 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); } .tt-err pre { margin: 0.3em 0 0; padding: 0.5em 1ch; - background: var(--ink-dimmer); + background-color: var(--tt-black); + background-image: var(--dither-mid); color: var(--ink); font-family: inherit; font-size: inherit; @@ -539,6 +595,11 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); } .tt-row-val { margin-left: auto; white-space: normal; } .tt-head-name { letter-spacing: 0.12em; } .tt-head-clock { margin-left: 0; flex-basis: 100%; } + + /* The System disk table swaps for its row reflow here — see the + "Disk rows" block above. */ + .tt-table-scroll { display: none; } + .tt-disk-rows { display: flex; } } /* ── Search ───────────────────────────────────────────── @@ -615,7 +676,10 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); } pointer-events: none; } .tt-toast.show { opacity: 1; transform: translateY(0); } -.tt-toast.error { background: var(--alert); color: var(--tt-white); } +.tt-toast.error { + background: var(--alert); + color: var(--tt-black); /* same contrast fix as .tt-field.crit */ +} /* ── Sign-in ──────────────────────────────────────────── The one page with no navigation, so it centres its single diff --git a/static/fonts/SpaceMono-Regular.woff2 b/static/fonts/SpaceMono-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2752f605c1ece3797d3285191f5ffebe3c2873e1 GIT binary patch literal 16520 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 literal 0 HcmV?d00001 diff --git a/templates/_macros.html b/templates/_macros.html index 4461b27..252e87b 100644 --- a/templates/_macros.html +++ b/templates/_macros.html @@ -95,6 +95,26 @@ {% endmacro %} +{#- A disk/cache row for the phone reflow of the System table (below + 560px, where a six-column table stops fitting and its horizontal + scroll would hide the bar and the temperature — the two readings + that matter most). Name, the one shared bar, and the temperature + sit on one line; device/size/free sit beneath as a sub-line. The + desktop stays the table at wider widths; this is not a + replacement for it, only the reflow. -#} +{% macro disk_row(dk) %} +
+
+ {{ dk.name }} + {{ bar(dk.used_pct | default(0)) }} + {{ dk.used_pct | default(0) | round | int }}% + {{ disk_state(dk) }} +
+
{{ dk.device | default('—') }} · {{ dk.free | default('—') }} free of {{ dk.total | default('—') }}
+
+{% endmacro %} + + {#- A list row: number, name, body, value. The index rows and every listing in the service share this one shape. -#} {% macro row(name, title, sub='', value='', tone='') %} diff --git a/templates/_shell.html b/templates/_shell.html index 0777d8f..6970434 100644 --- a/templates/_shell.html +++ b/templates/_shell.html @@ -7,9 +7,10 @@ {% block title %}P{{ here[1] }} Media Centre{% endblock %} - - - + {#- Space Mono is self-hosted (see base.css's @font-face) — this app + runs on a LAN-only home server where a Google Fonts request can + simply fail, and a dropped webfont request is a silent identity + change, not a graceful fallback. -#} {% block css %}{% endblock %} diff --git a/templates/home.html b/templates/home.html index d71e836..b5fcefb 100644 --- a/templates/home.html +++ b/templates/home.html @@ -135,7 +135,7 @@ {#- ── Just added ──────────────────────────────────────────────── -#} {% if recently_added %}
Just Added
-
+
{% for it in recently_added %} {{ tile(it.title, it.sub, it.poster, it.ago) }} {% endfor %} diff --git a/templates/login.html b/templates/login.html index d99862a..217a8de 100644 --- a/templates/login.html +++ b/templates/login.html @@ -6,9 +6,7 @@ Media Centre - - - + {#- Space Mono is self-hosted — see base.css's @font-face. -#} diff --git a/templates/system.html b/templates/system.html index e28e7e9..1982e7e 100644 --- a/templates/system.html +++ b/templates/system.html @@ -1,5 +1,5 @@ {% extends "_shell.html" %} -{% from "_macros.html" import service_error, bar, disk_state, trend %} +{% from "_macros.html" import service_error, bar, disk_state, disk_row, trend %} {% block title %}P105 System{% endblock %} {% block content %} @@ -58,6 +58,10 @@
{% 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. -#}
@@ -94,6 +98,14 @@
+ + {#- 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. -#} +

+ {% for dk in unraid.disks %}{{ disk_row(dk) }}{% endfor %} + {% for ck in unraid.caches | default([]) %}{{ disk_row(ck) }}{% endfor %} +
{% endif %} {% endif %}