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 0000000..2752f60 Binary files /dev/null and b/static/fonts/SpaceMono-Regular.woff2 differ 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 %}