From 59beb9490f045c57d679a7b78ac61afd60d7bb2a Mon Sep 17 00:00:00 2001 From: Barely Removable Date: Sun, 30 Aug 2026 09:11:59 -0700 Subject: [PATCH] Fix design-review findings: self-linking nav, colour reuse, badge clash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From a frontend-design skill review of all pages, after last session's chrome changes (top nav, all-tabs nav, header download row): - The top/closing fastext nav now mutes the entry matching the current page (.fastext-here) instead of rendering a link to the page you're already on. - Colour in fastext is reserved for the four entries that still carry a real keyboard shortcut (r/g/y/c); the other four render in plain ink instead of reusing k-red/k-green/k-yellow/k-cyan for a destination they don't semantically mean (e.g. Requests no longer shows in the app's own off-air red). - Discover's badge slot meant "critic score" on its RT tiles and "release year" on its new search tiles at the same time. Search results (Library and Discover) now fold year/held-state into the sub-line instead, leaving the badge exclusively for critic score. - System's Transmission rows now carry a small ↗ glyph, since they silently changed from in-app navigation to opening each service's own UI in a new tab with no visual cue. - SABnzbd polling drops from 5s to 20s on pages that only show the header's download figure (everywhere but System/Activity, which keep 5s for their own live figures) — a number idle 95% of the time doesn't need second-level freshness sitewide. - DESIGN.md's fastext description, now-current per this session's nav changes. --- CLAUDE.md | 7 ++++++- DESIGN.md | 2 +- app.py | 15 +++++++++------ static/css/teletext.css | 17 ++++++++++++++--- static/js/service.js | 5 ++++- templates/_shell.html | 4 ++++ templates/discover.html | 6 +++++- templates/library.html | 2 +- templates/system.html | 2 +- 9 files changed, 45 insertions(+), 15 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 49d525f..57172c2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -107,7 +107,12 @@ the user's Unraid home server. Server-side project directory is still named with `HOME_FASTEXT`, the original four-entry row, as its own closing line, since its body already lists every destination once. Only those original four (Index/Library/Discover/System) carry an `r`/ - `g`/`y`/`c` keyboard shortcut; the rest are click-only. + `g`/`y`/`c` keyboard shortcut and a colour swatch — colour is + semantic everywhere else in this app, so the other four entries + render in plain ink rather than reusing one of the four colours for + a destination it doesn't mean. Whichever entry matches the current + page renders as a `.fastext-here` span instead of a link — a page + linking to itself did nothing, so it isn't offered as a click. - **Nothing is clipped, hidden, or sticky.** All seven page numbers and every fastext entry stay visible at every width, reflowing to another row rather than scrolling, truncating, or folding into a menu. No diff --git a/DESIGN.md b/DESIGN.md index 8934f65..ecaf3e4 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -140,7 +140,7 @@ components: Media Centre is a broadcast information service, not a dashboard. It refuses the bordered-card grid that every self-hosted *arr dashboard ships — including this one, until this redesign. The world is teletext: Ceefax and the American cable guide channel, fused. Eight colours at full saturation on pure black, one blocky monospace at exactly two heights, a character-cell grid, and grouping carried entirely by colour fields and row weight. The visitor types or clicks a page number, reads the machine's state off colour alone, and leaves knowing whether anything needs them. -The density is high and unapologetic. A teletext page was 40 columns; this one gets 92 (`--page-cols: 92ch`) because the screen allows it, but the character grid is the substrate everywhere — padding is measured in `ch`, bars are measured in cells, and nothing floats free of the row rhythm (`--row`, one character row). There is no chrome. The header is a row of the page, not furniture bolted above it, so it scrolls away with everything else. The fastext colour keys sit at the bottom of the page where they always sat, not fixed to the viewport. +The density is high and unapologetic. A teletext page was 40 columns; this one gets 92 (`--page-cols: 92ch`) because the screen allows it, but the character grid is the substrate everywhere — padding is measured in `ch`, bars are measured in cells, and nothing floats free of the row rhythm (`--row`, one character row). There is no chrome. The header is a row of the page, not furniture bolted above it, so it scrolls away with everything else. The fastext keys sit at the top of every page but the index — which closes with them instead, since its own body already is the full nav — never fixed to the viewport. The confirmed anti-reference is the world this replaced and every dashboard that shares its reflexes: the rounded, bordered, drop-shadowed card grid with a neutral dark-grey palette and a soft accent. Nothing here is enclosed. There is not one `border-radius` and not one `box-shadow` in the codebase — the single `text-shadow` is a phosphor bloom, and it is spent deliberately. Emphasis is bought with colour and with doubled height, never with a weight ramp, never with a third type size, never with a container. diff --git a/app.py b/app.py index e6db2dc..c30c8c6 100644 --- a/app.py +++ b/app.py @@ -1304,17 +1304,20 @@ def service_urls(): # Every destination, for the top of every page but the index. Order -# matches nav_items; the four letter keys keep the destinations they -# always had, cycling colour for the rest since only four exist. +# matches nav_items. Colour is semantic everywhere else in this app +# (red = off-air, magenta = needs a decision, ...), so only the four +# that still carry a real teletext keyboard shortcut (r/g/y/c) get a +# colour swatch; the rest render in plain ink rather than borrowing +# one of the four colours for a destination it doesn't mean. FULL_FASTEXT = [ ("k-red", "r", "100", "Index", "/"), - ("k-cyan", "", "101", "Watch", "/watch"), + ("", "", "101", "Watch", "/watch"), ("k-green", "g", "102", "Library", "/library"), ("k-yellow", "y", "103", "Discover", "/discover"), - ("k-red", "", "104", "Requests", "/requests"), + ("", "", "104", "Requests", "/requests"), ("k-cyan", "c", "105", "System", "/system"), - ("k-green", "", "106", "Activity", "/activity"), - ("k-yellow", "", "900", "Settings", "/settings"), + ("", "", "106", "Activity", "/activity"), + ("", "", "900", "Settings", "/settings"), ] # The index's own closing row: just the four letter keys, since the diff --git a/static/css/teletext.css b/static/css/teletext.css index a424731..d2f374e 100644 --- a/static/css/teletext.css +++ b/static/css/teletext.css @@ -167,6 +167,10 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); } text-transform: uppercase; min-width: 11ch; } +/* Marks a row that leaves the app (System's Transmission links to each + service's own UI) — everywhere else a .tt-row stays inside Media + Centre, so leaving needs its own quiet signal. */ +.tt-row-ext { color: var(--ink-dim); } .tt-row-body { flex: 1; min-width: 0; } .tt-row-title { overflow-wrap: anywhere; @@ -494,7 +498,7 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); } .fastext-top { margin: var(--row) auto calc(var(--row) * 1.5); } -.fastext a { +.fastext a, .fastext-here { display: flex; align-items: center; min-width: 0; @@ -510,18 +514,25 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); } } /* The key's colour is carried by a solid cell at its head — the physical coloured button on the remote. */ -.fastext a > span { +.fastext a > span, .fastext-here > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; } -.fastext a::before { +.fastext a::before, .fastext-here::before { content: ""; flex: 0 0 auto; width: 1.2ch; height: 1em; background: currentColor; } +/* The page you're already on: present for orientation, same shape as + its neighbours, but not a link — clicking your own destination did + nothing useful, so now it isn't offered as a click at all. */ +.fastext-here { + color: var(--ink-dim); + cursor: default; +} .fastext .k-red { color: var(--alert); } .fastext .k-green { color: var(--ok); } .fastext .k-yellow { color: var(--voice); } diff --git a/static/js/service.js b/static/js/service.js index 475dfcd..5be1550 100644 --- a/static/js/service.js +++ b/static/js/service.js @@ -199,8 +199,11 @@ .catch(function () { /* as above */ }); } + /* Second-accurate only where the page shows the full figures + (System, Activity); everywhere else it's a header glance at a + number that's idle most of the time, so it can poll slower. */ pollSab(); - setInterval(pollSab, 5000); + setInterval(pollSab, document.getElementById('sab-queue') ? 5000 : 20000); if (document.getElementById('tt-streams')) { pollTautulli(); setInterval(pollTautulli, 15000); diff --git a/templates/_shell.html b/templates/_shell.html index ba4430f..a9aaa3a 100644 --- a/templates/_shell.html +++ b/templates/_shell.html @@ -58,9 +58,13 @@ provenance. {% set fastext_nav %} {% endset %} diff --git a/templates/discover.html b/templates/discover.html index 5063a0b..38a8c07 100644 --- a/templates/discover.html +++ b/templates/discover.html @@ -27,8 +27,12 @@ {{ service_error('Sonarr/Radarr', search.error) }} {% elif search.results %}
+ {#- No badge here: the badge slot means "critic score" below, + from the RT feed. A search result has no score, so its + year and held-state live in the sub-line instead of + overloading that slot with a second meaning. -#} {% for it in search.results %} - {{ tile(it.title, it.kind ~ (' · Held' if it.held else ''), it.poster, it.year, ('' if it.held else '+ Add'), it.target) }} + {{ tile(it.title, it.kind ~ (' · ' ~ it.year if it.year else '') ~ (' · Held' if it.held else ''), it.poster, '', ('' if it.held else '+ Add'), it.target) }} {% endfor %}
{% else %} diff --git a/templates/library.html b/templates/library.html index 5904e65..2a7edb6 100644 --- a/templates/library.html +++ b/templates/library.html @@ -22,7 +22,7 @@ {% if search_results %}
{% for it in search_results %} - {{ tile(it.title, it.kind, it.poster, it.year) }} + {{ tile(it.title, it.kind ~ (' · ' ~ it.year if it.year else ''), it.poster) }} {% endfor %}
{% else %} diff --git a/templates/system.html b/templates/system.html index 712d417..14a7d6e 100644 --- a/templates/system.html +++ b/templates/system.html @@ -14,7 +14,7 @@ {% for name, key, svc in [('Sonarr', 'sonarr', sonarr), ('Radarr', 'radarr', radarr), ('SABnzbd', 'sabnzbd', sabnzbd), ('Tautulli', 'tautulli', tautulli), ('Prowlarr', 'prowlarr', prowlarr), ('Ombi', 'ombi', ombi)] %} - {{ name }} + {{ name }} {{- 'ON AIR' if svc.status == 'ok' else 'OFF AIR' -}}