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
144
templates/home.html
Normal file
144
templates/home.html
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
{% extends "_shell.html" %}
|
||||
{% from "_macros.html" import service_error, bar, tile %}
|
||||
{% block title %}P100 Media Centre{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{#- The masthead. Double height, the service's own colour, and the one
|
||||
place on the page bloom is allowed. -#}
|
||||
<h1 class="tt-dh bloom tt-masthead">MEDIA CENTRE</h1>
|
||||
|
||||
{#- ── The index IS the status page ──────────────────────────────
|
||||
Every destination row carries its own live figure in its own
|
||||
colour, so choosing where to go and reading how things are are
|
||||
the same act. This is the whole thesis in one block. -#}
|
||||
<div class="tt-rows">
|
||||
<a class="tt-row" href="{{ request.script_root }}/watch">
|
||||
<span class="tt-row-num">101</span>
|
||||
<span class="tt-row-name">Watch</span>
|
||||
<span class="tt-row-body"><span class="tt-row-sub">Who is streaming right now</span></span>
|
||||
{% if tautulli.status == 'ok' %}
|
||||
<span class="tt-row-val {{ 'ok' if tautulli.active_streams else 'off' }}" id="tt-streams-row">
|
||||
<span id="tt-streams">{{ tautulli.active_streams }}</span> STREAMING</span>
|
||||
{% else %}
|
||||
<span class="tt-row-val alert">OFF AIR</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
<a class="tt-row" href="{{ request.script_root }}/library">
|
||||
<span class="tt-row-num">102</span>
|
||||
<span class="tt-row-name">Library</span>
|
||||
<span class="tt-row-body"><span class="tt-row-sub">Everything already held</span></span>
|
||||
{% if sonarr.status == 'ok' or radarr.status == 'ok' %}
|
||||
<span class="tt-row-val">{{ sonarr.total_series | default(0) }} SHOWS {{ radarr.total_movies | default(0) }} FILMS</span>
|
||||
{% else %}
|
||||
<span class="tt-row-val alert">OFF AIR</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
<a class="tt-row" href="{{ request.script_root }}/discover">
|
||||
<span class="tt-row-num">103</span>
|
||||
<span class="tt-row-name">Discover</span>
|
||||
<span class="tt-row-body"><span class="tt-row-sub">Popular, and not yet held</span></span>
|
||||
<span class="tt-row-val">{{ (rt_movies | length) + (rt_tv | length) }} SUGGESTED</span>
|
||||
</a>
|
||||
|
||||
<a class="tt-row" href="{{ request.script_root }}/requests">
|
||||
<span class="tt-row-num">104</span>
|
||||
<span class="tt-row-name">Requests</span>
|
||||
<span class="tt-row-body"><span class="tt-row-sub">Waiting on a decision</span></span>
|
||||
{% if ombi.status == 'ok' %}
|
||||
<span class="tt-row-val {{ 'pending' if ombi.total_pending else 'off' }}">{{ ombi.total_pending | default(0) }} PENDING</span>
|
||||
{% else %}
|
||||
<span class="tt-row-val alert">OFF AIR</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
<a class="tt-row" href="{{ request.script_root }}/system">
|
||||
<span class="tt-row-num">105</span>
|
||||
<span class="tt-row-name">System</span>
|
||||
<span class="tt-row-body"><span class="tt-row-sub">Array, disks and indexers</span></span>
|
||||
{% if unraid.status == 'ok' %}
|
||||
{%- set pct = unraid.array_used_pct | default(0) | float -%}
|
||||
<span class="tt-row-val {{ 'alert' if pct >= 90 else ('warn' if pct >= 75 else 'ok') }}">{{ pct | round | int }}% FULL</span>
|
||||
{% else %}
|
||||
<span class="tt-row-val alert">OFF AIR</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
<a class="tt-row" href="{{ request.script_root }}/activity">
|
||||
<span class="tt-row-num">106</span>
|
||||
<span class="tt-row-name">Activity</span>
|
||||
<span class="tt-row-body"><span class="tt-row-sub">What the stack has been doing</span></span>
|
||||
{% if sabnzbd.status == 'ok' %}
|
||||
<span class="tt-row-val {{ '' if sabnzbd.queue_count else 'off' }}"><span id="sab-queue">{{ sabnzbd.queue_count }}</span> IN QUEUE</span>
|
||||
{% else %}
|
||||
<span class="tt-row-val alert">OFF AIR</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
<a class="tt-row" href="{{ request.script_root }}/settings">
|
||||
<span class="tt-row-num">900</span>
|
||||
<span class="tt-row-name">Settings</span>
|
||||
<span class="tt-row-body"><span class="tt-row-sub">Keys, timezone and password</span></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="tt-sep" aria-hidden="true"></div>
|
||||
|
||||
{#- ── Now showing ───────────────────────────────────────────────
|
||||
The lower two-thirds. The furthest-along stream is set at double
|
||||
height in the live colour, with its progress as whole mosaic
|
||||
cells on the service's one ruler. -#}
|
||||
<div class="tt-field">
|
||||
<span>Now Showing</span>
|
||||
{% if tautulli.status == 'ok' %}
|
||||
<span class="tt-field-meta">{{ tautulli.active_streams }} active</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if tautulli.status != 'ok' %}
|
||||
{{ service_error('Tautulli', tautulli.error) }}
|
||||
{% elif hero %}
|
||||
<div class="tt-now">
|
||||
<div class="tt-dh bloom tt-now-title">{{ hero.title }}</div>
|
||||
<div class="tt-now-meta">
|
||||
{{ hero.user or hero.username }}
|
||||
{%- if hero.player %} · {{ hero.player }}{% endif %}
|
||||
{%- if hero.quality %} · {{ hero.quality }}{% endif %}
|
||||
{%- if hero.state %} · {{ hero.state | upper }}{% endif %}
|
||||
</div>
|
||||
<div class="tt-now-line">
|
||||
{{ bar(hero.progress | default(0), 'ok') }}
|
||||
<span class="tt-now-pct">{{ hero.progress | default(0) | round | int }}%</span>
|
||||
{% if hero.remaining %}<span class="tt-row-sub">{{ hero.remaining }} LEFT</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if other_sessions %}
|
||||
<div class="tt-rows tt-rows-after">
|
||||
{% for s in other_sessions %}
|
||||
<div class="tt-row">
|
||||
<span class="tt-row-body">
|
||||
<span class="tt-row-title">{{ s.title }}</span>
|
||||
<span class="tt-row-sub">{{ s.user or s.username }}{% if s.player %} · {{ s.player }}{% endif %}</span>
|
||||
</span>
|
||||
{{ bar(s.progress | default(0), 'ok') }}
|
||||
<span class="tt-row-val">{{ s.progress | default(0) | round | int }}%</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="tt-empty">Nothing streaming. The service is quiet.</p>
|
||||
{% endif %}
|
||||
|
||||
{#- ── Just added ──────────────────────────────────────────────── -#}
|
||||
{% if recently_added %}
|
||||
<div class="tt-field"><span>Just Added</span></div>
|
||||
<div class="tt-tiles">
|
||||
{% for it in recently_added %}
|
||||
{{ tile(it.title, it.sub, it.poster, it.ago) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue