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
|
|
@ -1,107 +1,80 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Settings — Media Centre</title>
|
||||
<link rel="icon" href="{{ url_for('static', filename='favicon.svg') }}">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/base.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
|
||||
</head>
|
||||
<body data-url-base="{{ request.script_root }}">
|
||||
{% extends "_shell.html" %}
|
||||
{% block title %}P900 Settings{% endblock %}
|
||||
|
||||
<header>
|
||||
<svg width="30" height="30" viewBox="0 0 100 100" aria-hidden="true">
|
||||
<rect width="100" height="100" rx="22" fill="var(--accent)"/>
|
||||
<path d="M40 32 L72 50 L40 68 Z" fill="var(--bg)"/>
|
||||
</svg>
|
||||
<h1>Settings</h1>
|
||||
<a href="{{ url_for('dashboard') }}" class="back-btn">← Dashboard</a>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="grid">
|
||||
|
||||
<!-- General -->
|
||||
<div class="card full">
|
||||
<div class="card-header"><h2>General</h2></div>
|
||||
<div class="card-body">
|
||||
<div class="field">
|
||||
<label for="timezone">Timezone</label>
|
||||
<select id="timezone">
|
||||
{% set tzs = [
|
||||
'America/New_York','America/Chicago','America/Denver','America/Los_Angeles',
|
||||
'America/Toronto','America/Vancouver','America/Phoenix','America/Halifax',
|
||||
'Europe/London','Europe/Paris','Europe/Berlin','Europe/Amsterdam',
|
||||
'Europe/Stockholm','Europe/Rome','Europe/Madrid','Europe/Zurich',
|
||||
'Australia/Sydney','Australia/Melbourne','Australia/Brisbane','Australia/Perth',
|
||||
'Pacific/Auckland','Asia/Tokyo','Asia/Singapore','Asia/Dubai','Asia/Kolkata'
|
||||
] %}
|
||||
{% for tz in tzs %}
|
||||
<option value="{{ tz }}" {% if tz == timezone %}selected{% endif %}>{{ tz }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="arr_host">Host IP (ARR_HOST)</label>
|
||||
<input type="text" id="arr_host" value="{{ arr_host }}" placeholder="e.g. 192.168.86.33">
|
||||
<p class="hint">LAN address of the machine running Sonarr, Radarr, and the rest.</p>
|
||||
</div>
|
||||
<button class="save" onclick="saveGeneral()">Save general</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Change password -->
|
||||
<div class="card full">
|
||||
<div class="card-header"><h2>Change password</h2></div>
|
||||
<div class="card-body">
|
||||
<div class="field">
|
||||
<label for="pw_current">Current password</label>
|
||||
<input type="password" id="pw_current" autocomplete="current-password">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="pw_new">New password</label>
|
||||
<input type="password" id="pw_new" placeholder="At least 6 characters" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="pw_confirm">Confirm new password</label>
|
||||
<input type="password" id="pw_confirm" autocomplete="new-password">
|
||||
<div class="error-inline" id="pw-error" role="alert"></div>
|
||||
</div>
|
||||
<button class="save" onclick="changePassword()">Update password</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Service API keys -->
|
||||
{% for svc, label in [
|
||||
('sonarr','Sonarr'), ('radarr','Radarr'), ('sabnzbd','SABnzbd'),
|
||||
('tautulli','Tautulli'), ('prowlarr','Prowlarr'), ('ombi','Ombi')
|
||||
] %}
|
||||
<div class="card">
|
||||
<div class="card-header"><h2>{{ label }}</h2></div>
|
||||
<div class="card-body">
|
||||
<div class="field">
|
||||
<label for="{{ svc }}_api_key">API key</label>
|
||||
<input type="password" id="{{ svc }}_api_key"
|
||||
value="{{ {'sonarr': sonarr_api_key, 'radarr': radarr_api_key,
|
||||
'sabnzbd': sabnzbd_api_key, 'tautulli': tautulli_api_key,
|
||||
'prowlarr': prowlarr_api_key, 'ombi': ombi_api_key}[svc] }}"
|
||||
autocomplete="off">
|
||||
</div>
|
||||
<button class="save" onclick="saveService('{{ svc }}')">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% block content %}
|
||||
<h1 class="tt-dh bloom tt-masthead">SETTINGS</h1>
|
||||
<p class="tt-empty">Keys saved here override the values in <code>.env</code>. Both need a container restart to take effect.</p>
|
||||
|
||||
{#- ── General ────────────────────────────────────────────────── -#}
|
||||
<div class="tt-field"><span>General</span></div>
|
||||
<div class="tt-form">
|
||||
<div class="tt-field-row">
|
||||
<label class="tt-label" for="timezone">Timezone</label>
|
||||
<select class="tt-input tt-select" id="timezone">
|
||||
{% set tzs = [
|
||||
'America/New_York','America/Chicago','America/Denver','America/Los_Angeles',
|
||||
'America/Toronto','America/Vancouver','America/Phoenix','America/Halifax',
|
||||
'Europe/London','Europe/Paris','Europe/Berlin','Europe/Amsterdam',
|
||||
'Europe/Stockholm','Europe/Rome','Europe/Madrid','Europe/Zurich',
|
||||
'Australia/Sydney','Australia/Melbourne','Australia/Brisbane','Australia/Perth',
|
||||
'Pacific/Auckland','Asia/Tokyo','Asia/Singapore','Asia/Dubai','Asia/Kolkata'
|
||||
] %}
|
||||
{% for tz in tzs %}
|
||||
<option value="{{ tz }}" {% if tz == timezone %}selected{% endif %}>{{ tz }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</main>
|
||||
<div class="tt-field-row">
|
||||
<label class="tt-label" for="arr_host">Host IP (ARR_HOST)</label>
|
||||
<input class="tt-input" type="text" id="arr_host" value="{{ arr_host }}" placeholder="192.168.86.33">
|
||||
<p class="tt-hint">LAN address of the machine running Sonarr, Radarr and the rest.</p>
|
||||
</div>
|
||||
<button class="tt-btn" onclick="saveGeneral()">Save General</button>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
||||
{#- ── Password ───────────────────────────────────────────────── -#}
|
||||
<div class="tt-field"><span>Change Password</span></div>
|
||||
<div class="tt-form">
|
||||
<div class="tt-field-row">
|
||||
<label class="tt-label" for="pw_current">Current password</label>
|
||||
<input class="tt-input" type="password" id="pw_current" autocomplete="current-password">
|
||||
</div>
|
||||
<div class="tt-field-row">
|
||||
<label class="tt-label" for="pw_new">New password</label>
|
||||
<input class="tt-input" type="password" id="pw_new" placeholder="At least 6 characters" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="tt-field-row">
|
||||
<label class="tt-label" for="pw_confirm">Confirm new password</label>
|
||||
<input class="tt-input" type="password" id="pw_confirm" autocomplete="new-password">
|
||||
<div class="tt-inline-err" id="pw-error" role="alert"></div>
|
||||
</div>
|
||||
<button class="tt-btn" onclick="changePassword()">Update Password</button>
|
||||
</div>
|
||||
|
||||
{#- ── Keys ───────────────────────────────────────────────────── -#}
|
||||
<div class="tt-field"><span>Service Keys</span></div>
|
||||
<div class="tt-keys">
|
||||
{% for svc, label in [
|
||||
('sonarr','Sonarr'), ('radarr','Radarr'), ('sabnzbd','SABnzbd'),
|
||||
('tautulli','Tautulli'), ('prowlarr','Prowlarr'), ('ombi','Ombi')
|
||||
] %}
|
||||
<div class="tt-form">
|
||||
<div class="tt-field-row">
|
||||
<label class="tt-label" for="{{ svc }}_api_key">{{ label }} API key</label>
|
||||
<input class="tt-input" type="password" id="{{ svc }}_api_key"
|
||||
value="{{ {'sonarr': sonarr_api_key, 'radarr': radarr_api_key,
|
||||
'sabnzbd': sabnzbd_api_key, 'tautulli': tautulli_api_key,
|
||||
'prowlarr': prowlarr_api_key, 'ombi': ombi_api_key}[svc] }}"
|
||||
autocomplete="off">
|
||||
</div>
|
||||
<button class="tt-btn tt-btn-quiet" onclick="saveService('{{ svc }}')">Save {{ label }}</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="tt-toast" id="toast" role="status" aria-live="polite"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{{ url_for('static', filename='js/settings.js') }}"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue