Files
arr/web/index.html
T
2026-08-23 19:23:19 +01:00

378 lines
17 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="dark" />
<title>arr</title>
<script type="module" src="/src/main.ts"></script>
</head>
<body>
<!--
THESIS: arr is a signal chain — TMDB and Prowlarr feed in, Transmission is
driven out — and health is lamps on that chain, read in one glance. Refuses
the admin-dashboard rut: no sidebar, no stat cards, no hero metric.
OWN-WORLD: broadcast master-control panel. Near-black charcoal ground,
machined panel modules with inset hairline borders, engraved uppercase
micro-labels, monospace readouts, status lamps (signal green / amber /
fault red) with a soft glow, phosphor-cyan for the one interactive control.
STORY: the operator opens the board, reads the master lamp, and knows in a
second whether arr can do its job — and which upstream is down, by name.
FIRST VIEWPORT: a top rail (wordmark + master lamp + verdict + version),
then the chain: TMDB and PROWLARR modules on the left feeding the central
arr module, TRANSMISSION driven on the right, joined by bus hairlines with
direction arrows. On a phone the chain stacks vertically.
FORM: broadcast master-control panel, #2 of 7 on the ordered list; external
roll (shell RANDOM → 2, witness 57910; concept-seed.mjs degraded to empty
output in this environment).
FINISH: unreviewed and undocumented is unfinished; this build ends with the
finish review, the verdict, and DESIGN.md.
SEARCH (§9.2, issue #30): one command input on the rail — the board's
command line. Typing swaps the chain for the search deck: IN LIBRARY
first, ON TMDB below, engraved group labels, machined rows. Enter on a
TMDB row expands an inline add panel with root and policy pre-filled;
a pasted magnet or .torrent routes to a manual-intake panel. There is
no separate add page, ever. Esc returns to the board; "/" focuses the
box from anywhere.
BUCKETS (§9.3, issue #31): a library row opens the release deck — the
policy engine's verdicts, inverted from Radarr: ELIGIBLE rows lead with
fixed-width attribute chips (score res source hdr audio size seed) in
aligned mono columns; WAIVED and REJECTED collapse to counts. The
release name is secondary, truncated, full on expand. Every rejected
row names its killing rule; one click on a waived row writes the
override and grabs. No horizontal scroll at any viewport. Esc returns
to the search deck.
LIBRARY (§4.2, issue #32): the LIBRARY control on the rail opens the
library deck — SERIES then MOVIES, machined rows with the title left
and chips right. Status is derived and only displayed: series carry a
§4.2 status chip (channel-violet activity, neutral satisfaction, the
mono word always present), movies carry their state chip. There is no
monitored flag anywhere; intent is wanted, at the leaf. The default
list shows what needs attention — airing and incomplete series,
wanted-and-missing movies — and everything satisfied collapses behind
ONE toggle with its count. A file imported under a waiver reads
honestly ("english, no dub") as a dashed amber chip, never as a clean
match. A movie row opens the release deck and returns here on Esc.
QUEUES (§5.2 + §5.7, issue #33): the QUEUES control opens the attention
deck — the only two places the app asks for a human. NO PT SOURCE holds
kids titles with no qualifying pt release; parked for months by design;
one click writes allow_english_audio and the normal search proceeds.
NEEDS DECISION holds titles that hard-failed twice on different
releases; the row opens the release deck to decide by hand. Both
sections always render, even empty — the operator visits to confirm
nothing needs them. The rail control carries an amber entry count,
refreshed with the health poll; amber is attention, cyan stays the
interaction hue.
-->
<header class="rail">
<div class="rail-id">
<span class="lamp" id="master-lamp" data-state="probing" aria-hidden="true"></span>
<h1 class="wordmark">arr</h1>
</div>
<nav class="rail-nav" aria-label="views">
<button type="button" class="control" id="nav-library" aria-pressed="false">
library
</button>
<button type="button" class="control" id="nav-queues" aria-pressed="false">
queues<span class="nav-count readout" id="queues-count" hidden></span>
</button>
<button type="button" class="control" id="nav-settings" aria-pressed="false">
settings
</button>
</nav>
<div class="rail-search">
<input
id="search"
class="search-input readout"
type="text"
spellcheck="false"
autocomplete="off"
placeholder="search · title / tmdb id / tt… / magnet"
aria-label="unified search — titles, TMDB or IMDb ids, magnets and .torrent links"
/>
<span class="search-hint readout" id="search-hint" aria-hidden="true">/</span>
</div>
<p class="rail-verdict readout" id="master-verdict" role="status">probing chain…</p>
<div class="rail-meta">
<span class="readout dim" id="version" data-testid="version">v —</span>
</div>
</header>
<main class="deck" id="deck" hidden>
<p class="deck-status readout" id="deck-status" role="status" hidden></p>
<section class="deck-group" id="group-library" hidden aria-labelledby="label-library">
<header class="deck-head">
<h2 class="deck-label" id="label-library">in library</h2>
<span class="deck-count readout" id="count-library"></span>
</header>
<ul class="deck-rows" id="rows-library"></ul>
</section>
<section class="deck-group" id="group-tmdb" hidden aria-labelledby="label-tmdb">
<header class="deck-head">
<h2 class="deck-label" id="label-tmdb">on tmdb</h2>
<span class="deck-count readout" id="count-tmdb"></span>
</header>
<ul class="deck-rows" id="rows-tmdb"></ul>
</section>
<section class="deck-group" id="group-manual" hidden aria-labelledby="label-manual">
<header class="deck-head">
<h2 class="deck-label" id="label-manual">manual grab</h2>
</header>
<article class="module intake" id="manual-intake"></article>
</section>
</main>
<main class="deck" id="library" hidden aria-label="library">
<header class="deck-head library-bar">
<h2 class="deck-label">library</h2>
<span class="deck-count readout" id="library-summary"></span>
<button
type="button"
class="bucket-toggle readout"
id="library-toggle"
aria-expanded="false"
aria-controls="library-series library-movies"
hidden
></button>
</header>
<p class="deck-status readout" id="library-status" role="status" hidden></p>
<section class="deck-group" id="library-series" hidden aria-labelledby="label-lib-series">
<header class="deck-head">
<h3 class="deck-label" id="label-lib-series">series</h3>
<span class="deck-count readout" id="count-lib-series"></span>
</header>
<ul class="deck-rows" id="rows-lib-series"></ul>
</section>
<section class="deck-group" id="library-movies" hidden aria-labelledby="label-lib-movies">
<header class="deck-head">
<h3 class="deck-label" id="label-lib-movies">movies</h3>
<span class="deck-count readout" id="count-lib-movies"></span>
</header>
<ul class="deck-rows" id="rows-lib-movies"></ul>
</section>
</main>
<main class="deck" id="queues" hidden aria-label="attention queues">
<header class="deck-head library-bar">
<h2 class="deck-label">attention</h2>
<span class="deck-count readout" id="queues-summary"></span>
</header>
<p class="deck-status readout" id="queues-status" role="status" hidden></p>
<section class="deck-group" id="queue-no-pt" hidden aria-labelledby="label-no-pt">
<header class="deck-head">
<h3 class="deck-label" id="label-no-pt">no pt source</h3>
<span class="deck-count readout" id="count-no-pt"></span>
</header>
<p class="queue-note readout dim">
kids titles with no qualifying pt release — parked here, sometimes for months, by
design. allow english writes a movie's override and its search proceeds; series wait
for the detail view.
</p>
<ul class="deck-rows" id="rows-no-pt"></ul>
</section>
<section class="deck-group" id="queue-decision" hidden aria-labelledby="label-decision">
<header class="deck-head">
<h3 class="deck-label" id="label-decision">needs decision</h3>
<span class="deck-count readout" id="count-decision"></span>
</header>
<p class="queue-note readout dim">
hard-failed twice on different releases — open a movie's releases and decide by hand;
series entries are display-only for now.
</p>
<ul class="deck-rows" id="rows-decision"></ul>
</section>
</main>
<main class="deck releases" id="releases" hidden>
<header class="releases-head">
<button type="button" class="control" id="releases-back">back</button>
<div class="releases-id">
<h2 class="releases-title" id="releases-title"></h2>
<span class="releases-year readout dim" id="releases-year"></span>
</div>
<button type="button" class="control" id="releases-sweep">search indexers</button>
<button
type="button"
class="control control-quiet"
id="releases-remove"
aria-expanded="false"
aria-controls="remove-panel"
>
remove
</button>
</header>
<div class="remove-panel" id="remove-panel" hidden></div>
<p class="deck-status readout" id="releases-status" role="status" hidden></p>
<section class="deck-group" id="bucket-eligible" hidden aria-labelledby="label-eligible">
<header class="deck-head">
<h3 class="deck-label" id="label-eligible">eligible</h3>
<span class="deck-count readout" id="count-eligible"></span>
</header>
<div class="colhead" aria-hidden="true">
<span class="cw cw-score">score</span>
<span class="cw cw-res">res</span>
<span class="cw cw-src">source</span>
<span class="cw cw-hdr">hdr</span>
<span class="cw cw-aud">audio</span>
<span class="cw cw-size">size</span>
<span class="cw cw-seed">seed</span>
</div>
<ul class="deck-rows" id="rows-eligible"></ul>
</section>
<section class="deck-group" id="bucket-waived" hidden aria-labelledby="label-waived">
<header class="deck-head bucket-head">
<h3 class="deck-label" id="label-waived">waived</h3>
<button
type="button"
class="bucket-toggle readout"
id="toggle-waived"
aria-expanded="false"
aria-controls="wrap-waived"
></button>
</header>
<div id="wrap-waived" hidden>
<div class="colhead" aria-hidden="true">
<span class="cw cw-score">score</span>
<span class="cw cw-res">res</span>
<span class="cw cw-src">source</span>
<span class="cw cw-hdr">hdr</span>
<span class="cw cw-aud">audio</span>
<span class="cw cw-size">size</span>
<span class="cw cw-seed">seed</span>
</div>
<ul class="deck-rows" id="rows-waived"></ul>
</div>
</section>
<section class="deck-group" id="bucket-rejected" hidden aria-labelledby="label-rejected">
<header class="deck-head bucket-head">
<h3 class="deck-label" id="label-rejected">rejected</h3>
<button
type="button"
class="bucket-toggle readout"
id="toggle-rejected"
aria-expanded="false"
aria-controls="wrap-rejected"
></button>
</header>
<div id="wrap-rejected" hidden>
<div class="colhead" aria-hidden="true">
<span class="cw cw-score">score</span>
<span class="cw cw-res">res</span>
<span class="cw cw-src">source</span>
<span class="cw cw-hdr">hdr</span>
<span class="cw cw-aud">audio</span>
<span class="cw cw-size">size</span>
<span class="cw cw-seed">seed</span>
</div>
<ul class="deck-rows" id="rows-rejected"></ul>
</div>
</section>
</main>
<main class="deck" id="settings" hidden aria-label="settings">
<header class="deck-head library-bar">
<h2 class="deck-label">settings</h2>
<span class="deck-count readout" id="settings-summary"></span>
</header>
<p class="deck-status readout" id="settings-status" role="status" hidden></p>
<section class="deck-group" id="group-roots" hidden aria-labelledby="label-roots">
<header class="deck-head bucket-head">
<h3 class="deck-label" id="label-roots">roots</h3>
<button type="button" class="control control-quiet readout" id="root-add">add root</button>
</header>
<ul class="deck-rows" id="rows-roots"></ul>
</section>
<section class="deck-group" id="group-policies" hidden aria-labelledby="label-policies">
<header class="deck-head bucket-head">
<h3 class="deck-label" id="label-policies">policies</h3>
<button type="button" class="control control-quiet readout" id="policy-add">
add policy
</button>
</header>
<p class="queue-note readout dim">
a policy edit rescores search decks immediately — open any title's releases to check it.
</p>
<ul class="deck-rows" id="rows-policies"></ul>
</section>
</main>
<main class="board">
<section class="chain" aria-label="signal chain">
<article class="module area-tmdb" data-check="tmdb">
<header class="module-head">
<span class="lamp" data-state="probing" aria-hidden="true"></span>
<h2 class="module-name">tmdb</h2>
<span class="module-status readout" data-role="status">probing</span>
</header>
<p class="module-role">metadata in</p>
<p class="module-detail readout" data-role="detail">titles, languages, release dates</p>
</article>
<div class="bus area-t1" aria-hidden="true">
<svg viewBox="0 0 40 8" preserveAspectRatio="none" class="bus-trace" data-trace="tmdb" aria-hidden="true">
<line x1="0" y1="4" x2="32" y2="4" />
<path d="M32 0.5 39 4 32 7.5 Z" class="bus-arrow" />
</svg>
</div>
<article class="module area-prowlarr" data-check="prowlarr">
<header class="module-head">
<span class="lamp" data-state="probing" aria-hidden="true"></span>
<h2 class="module-name">prowlarr</h2>
<span class="module-status readout" data-role="status">probing</span>
</header>
<p class="module-role">indexers in</p>
<p class="module-detail readout" data-role="detail">torznab search and rss</p>
</article>
<div class="bus area-t2" aria-hidden="true">
<svg viewBox="0 0 40 8" preserveAspectRatio="none" class="bus-trace" data-trace="prowlarr" aria-hidden="true">
<line x1="0" y1="4" x2="32" y2="4" />
<path d="M32 0.5 39 4 32 7.5 Z" class="bus-arrow" />
</svg>
</div>
<article class="module module-master area-master" data-check="master">
<header class="module-head">
<span class="lamp lamp-big" data-state="probing" aria-hidden="true"></span>
<h2 class="module-name">arr</h2>
<span class="module-status readout" data-role="status">probing</span>
</header>
<p class="module-role">reconcile core</p>
<p class="module-detail readout" data-role="detail">decide · find · fetch · import</p>
</article>
<div class="bus area-out" aria-hidden="true">
<svg viewBox="0 0 40 8" preserveAspectRatio="none" class="bus-trace" data-trace="master" aria-hidden="true">
<line x1="0" y1="4" x2="32" y2="4" />
<path d="M32 0.5 39 4 32 7.5 Z" class="bus-arrow" />
</svg>
</div>
<article class="module area-transmission" data-check="transmission">
<header class="module-head">
<span class="lamp" data-state="probing" aria-hidden="true"></span>
<h2 class="module-name">transmission</h2>
<span class="module-status readout" data-role="status">probing</span>
</header>
<p class="module-role">torrents out</p>
<p class="module-detail readout" data-role="detail">rpc · grab and seed</p>
</article>
</section>
</main>
</body>
</html>