Redesign Media Centre as a teletext broadcast information service
Replaces the neutral dark card-based theme with "Page 100": teletext's
locked eight colours at full saturation on pure black, numbered pages
reachable by typing the number (or the r/g/y/c fastext keys), and no
borders, boxes, or radius anywhere — grouping is carried by full-bleed
colour fields and row weight instead.
Chosen via the impeccable skill's replacement-world flow: seven visual
candidates derived from the audience's own world, a mandatory roll
assigned Ceefax/US-cable-guide teletext, six catalog challengers weighed
against it, and the user locked the assigned direction on the decision
page (seed aab4510e).
- New templates for all nine pages (home/watch/library/discover/
requests/system/activity/login/settings) behind a shared _shell.html
carrying the direction contract and _macros.html for the row/bar/tile
vocabulary; deletes the old single-page dashboard.html and its
dedicated stylesheets/script.
- static/css/base.css + teletext.css: the locked palette, a two-height
type scale (Chivo Mono / DotGothic16), and the five disciplines the
system is held to (no in-between tone, no enclosure, one shared bar
ruler, trend only where real history exists, pages weighted by state).
- static/js/service.js: header clock, page-number jump, fastext keys,
two-tap arm/confirm add, guarded live polling — replaces dashboard.js.
- app.py: nav_items now carry page numbers; fastext defaults added to
the page-chrome context processor; version bumped to 2.0.0.
- DESIGN.md + .impeccable/design.json regenerated from the built world
by the documenter pass; PRODUCT.md and CLAUDE.md updated to describe
the new system in place of the retired one.
- Fixed a live bug found along the way: settings.html called
url_for('dashboard'), an endpoint removed earlier in the branch, which
would have 500'd the Settings page.
- Fixed a One-Ruler violation the finish review's recapture request
surfaced: --bar-cells disagreed with the bar() macro's hardcoded cell
count below the 760px breakpoint, so a fill could land mid-cell on a
phone. Removed the mobile override; the ruler is now 20 cells at every
width, matching the macro unconditionally.
Passed two inspection rounds (desktop + true-390px mobile, via an
iframe harness since macOS Chrome clamps --window-size) and a finish
review; QA screenshots and interview logs are gitignored as scratch,
not shipped in the repo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBw1QodG8ZMYHrGeQfqyDS
This commit is contained in:
parent
9a277b9031
commit
4b19cbdfb2
25 changed files with 3002 additions and 2343 deletions
109
templates/_macros.html
Normal file
109
templates/_macros.html
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
{#- ═══════════════════════════════════════════════════════
|
||||
Shared fragments, in the service's own vocabulary.
|
||||
|
||||
Every one of these obeys the two disciplines that get broken
|
||||
by reflex: nothing is enclosed in a border or a box, and every
|
||||
bar is drawn at the one shared width so any two percentages in
|
||||
the service compare directly.
|
||||
═══════════════════════════════════════════════════════ -#}
|
||||
|
||||
{#- A degraded service. Teletext's failure was a page that did not
|
||||
arrive, said in red where the content should have been — not a
|
||||
panel with an icon and a left border. -#}
|
||||
{% macro service_error(name, raw) %}
|
||||
<div class="tt-err" role="alert">
|
||||
<div class="tt-err-cause">{{ raw | friendly_error(name) }}</div>
|
||||
{% if raw %}
|
||||
<details>
|
||||
<summary>Technical detail</summary>
|
||||
<pre>{{ raw | redact_secrets }}</pre>
|
||||
</details>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#- THE ONE RULER. Every bar in the service is drawn here and
|
||||
nowhere else, at --bar-cells wide, filled in whole cells. Pass a
|
||||
percentage; the macro rounds it to cells so a bar never ends
|
||||
mid-character. `tone` picks the colour by threshold. -#}
|
||||
{% macro bar(pct, tone='') %}
|
||||
{%- set p = [[pct | float(0), 0] | max, 100] | min -%}
|
||||
{%- set cells = 20 -%}
|
||||
{%- set filled = ((p / 100) * cells) | round(0, 'common') | int -%}
|
||||
{%- set cls = tone if tone else ('alert' if p >= 90 else ('warn' if p >= 75 else 'ok')) -%}
|
||||
<span class="tt-bar {{ cls }}" role="img"
|
||||
aria-label="{{ p | round | int }} percent"><i style="width: {{ (filled / cells * 100) | round(2) }}%"></i></span>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#- Direction of travel. Only ever called where the backend really
|
||||
carries a comparison — never to decorate a number that has no
|
||||
history behind it. -#}
|
||||
{% macro trend(now, reference, unit='') %}
|
||||
{%- if now is not none and reference is not none and reference > 0 -%}
|
||||
{%- set delta = ((now - reference) / reference) * 100 -%}
|
||||
{%- if delta > 12 -%}
|
||||
<span class="tt-trend up" title="Above the 7-day average">▲</span>
|
||||
{%- elif delta < -12 -%}
|
||||
<span class="tt-trend down" title="Below the 7-day average">▼</span>
|
||||
{%- else -%}
|
||||
<span class="tt-trend flat" title="At the 7-day average">▬</span>
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#- Disk temperature / spin state. A spun-down disk reports no usable
|
||||
temperature, so Standby replaces the reading rather than showing a
|
||||
stale or zero degree value. Colour carries the state; there is no
|
||||
badge shape to put it in. -#}
|
||||
{% macro disk_state(dk) %}
|
||||
{%- if dk.spinning == false -%}
|
||||
<span class="tt-row-val off">STANDBY</span>
|
||||
{%- elif dk.temp is not none -%}
|
||||
<span class="tt-row-val {% if dk.temp >= 55 %}alert{% elif dk.temp >= 45 %}warn{% else %}ok{% endif %}">{{ dk.temp }}°C</span>
|
||||
{%- else -%}
|
||||
<span class="tt-row-val off">—</span>
|
||||
{%- endif -%}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#- A title tile. Artwork is the only thing in this world not made of
|
||||
characters, so it sits as a flat rectangle on the grid with its
|
||||
label below — no frame, no rounding, no shadow. -#}
|
||||
{% macro tile(title, sub, poster, badge='', action='', target='') %}
|
||||
<div class="tt-tile">
|
||||
{% if poster %}
|
||||
<img class="tt-tile-art" src="{{ poster }}" alt="" loading="lazy">
|
||||
{% else %}
|
||||
<span class="tt-tile-art tt-tile-art-empty" aria-hidden="true"></span>
|
||||
{% endif %}
|
||||
<span class="tt-tile-t">{{ title }}</span>
|
||||
{% if sub or badge %}
|
||||
<span class="tt-tile-s">
|
||||
{%- if badge %}<span class="tt-tile-badge">{{ badge }}</span>{% endif %}
|
||||
{%- if badge and sub %} {% endif %}{{ sub }}</span>
|
||||
{% endif %}
|
||||
{% if action %}
|
||||
<span class="tt-tile-p">
|
||||
<button class="tt-btn tt-btn-sm" data-title="{{ title }}" data-target="{{ target }}"
|
||||
onclick="armAdd(this)">{{ action }}</button>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% 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='') %}
|
||||
<div class="tt-row">
|
||||
{% if name %}<span class="tt-row-name">{{ name }}</span>{% endif %}
|
||||
<span class="tt-row-body">
|
||||
<span class="tt-row-title">{{ title }}</span>
|
||||
{% if sub %}<span class="tt-row-sub">{{ sub }}</span>{% endif %}
|
||||
</span>
|
||||
{% if value %}<span class="tt-row-val {{ tone }}">{{ value }}</span>{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue