The code cited its plan instead of saying why: `E28 review J4`, `(R6 S3b)`, `S1 justified…`,
`deep-review round 5`. That plan lives in the private ops repo, so for every reader of this one the
citation was noise, and a sentence whose subject was the plan step ("round 2 showed…") hid its
lesson behind an id. Each one is gone and the "why" kept: a bare citation is deleted, a sentence
carrying a lesson is reworded around it ("a later attempt showed…").
Measured (`scripts/audit_metrics.py`, product code): 652 lines in 270 files carried an epic code by
the definition the next commit holds (507 / 241 by the old, two-digit-only one) -> 0. Across all
code, tests, scripts and configs: ~1100 epic-code lines plus ~320 sprint/round ids.
Behaviour unchanged, checked mechanically: every changed .py file has the same AST once docstrings
are blanked; css/sh/toml/yml/json files are identical once comments are stripped; the only
non-comment changes in TS/JS are test titles, one description string in a contract table, and five
ESLint messages, whose text is all the gates snapshot diff holds. Not touched: Markdown (docs and
the changelog keep their history), Alembic migrations, TV episode notation (`S2 · E5`), DESIGN.md
ids (`C1`, `B4`, `S3h`), tracker ids (`SEC-16`, `DX-17`).
One stale claim fixed on the way: test_hls_reaper's docstring said the player checkpoints "every
10 s whether or not it is playing", which plex/stream.py calls wrong; it now says what the code does.
144 lines
5.0 KiB
TypeScript
144 lines
5.0 KiB
TypeScript
import clsx from "clsx";
|
|
import { Bookmark, Check, ListPlus, MoreHorizontal, Star } from "lucide-react";
|
|
import { Reference, Sample } from "./labKit";
|
|
import { MediaCard, MediaPlayGlyph } from "../components/ui/MediaCard";
|
|
import {
|
|
MEDIA_BADGE,
|
|
MEDIA_BAR,
|
|
MEDIA_HOVER_LAYER,
|
|
MEDIA_PICTURE,
|
|
mediaPictureBox,
|
|
mediaTitleClass,
|
|
} from "../components/ui/mediaCardStyles";
|
|
import { iconButtonClass } from "../components/ui/buttonStyles";
|
|
import { HOVER_YIELD } from "../components/ui/reveal";
|
|
|
|
// The one media card, DECIDED (owner, 2026-09-26/27: anatomy C, shelf density, the preview
|
|
// C, the Home hero A, list row B, info layout B). Drawn with the real ui/MediaCard and its classes;
|
|
// hover a card to see its Play and its hover row. The preview panel, the hero and the info page are
|
|
// live in the app (Plex, Home), not copied here.
|
|
|
|
const BAR_BUTTON = iconButtonClass({ variant: "media", size: "sm" });
|
|
|
|
function HoverRow({ save = false }: { save?: boolean }) {
|
|
return (
|
|
<div className={MEDIA_BAR}>
|
|
<span className="flex gap-1">
|
|
{[Check, ...(save ? [Bookmark] : []), ListPlus, MoreHorizontal].map((Icon, i) => (
|
|
<span key={i} className={BAR_BUTTON}>
|
|
<Icon />
|
|
</span>
|
|
))}
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Picture({
|
|
aspect,
|
|
length,
|
|
small = false,
|
|
save = false,
|
|
}: {
|
|
aspect: "poster" | "wide";
|
|
length: string;
|
|
small?: boolean;
|
|
save?: boolean;
|
|
}) {
|
|
return (
|
|
<div className={mediaPictureBox(aspect)}>
|
|
<div className={clsx(MEDIA_PICTURE, "bg-linear-to-br from-accent/45 to-card")}>
|
|
<span className={clsx(MEDIA_BADGE, "bottom-1.5 right-1.5", HOVER_YIELD)}>{length}</span>
|
|
<div className={MEDIA_HOVER_LAYER}>
|
|
<MediaPlayGlyph small={small} />
|
|
</div>
|
|
</div>
|
|
<HoverRow save={save} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const score = (
|
|
<span className="inline-flex items-center gap-0.5">
|
|
<Star className="h-3 w-3 fill-current text-rating" aria-hidden />
|
|
7.8
|
|
</span>
|
|
);
|
|
const plexMeta = (
|
|
<div className="flex items-center gap-1.5 text-xs leading-6 text-muted">
|
|
<span>2003</span>
|
|
<span aria-hidden>·</span>
|
|
{score}
|
|
</div>
|
|
);
|
|
|
|
export function BatchD() {
|
|
return (
|
|
<div className="flex flex-col gap-4">
|
|
<Reference
|
|
id="s3h-card"
|
|
code="S3h"
|
|
title="The media card"
|
|
rule="One frame: the picture, the title, the meta pinned to the bottom edge. The length is a badge on the picture; the Play shows only where the click plays; the hover row docks bottom-right and the length steps aside. A grid card: two title lines and the full meta. On a shelf: one title line over one meta line, a 232px slot, and a landscape picture takes the smaller Play."
|
|
>
|
|
<div className="flex flex-wrap items-stretch gap-4">
|
|
<Sample label="Grid · a film (poster)">
|
|
<div className="grid w-44 grid-cols-1">
|
|
<MediaCard
|
|
dense
|
|
picture={<Picture aspect="poster" length="2h 34m" />}
|
|
title={
|
|
<div className={clsx("min-h-6", mediaTitleClass(true))}>
|
|
The Last Samurai, a title long enough to wrap
|
|
</div>
|
|
}
|
|
meta={plexMeta}
|
|
/>
|
|
</div>
|
|
</Sample>
|
|
<Sample label="Grid · a video">
|
|
<div className="grid w-72 grid-cols-1">
|
|
<MediaCard
|
|
picture={<Picture aspect="wide" length="18:04" save />}
|
|
title={<div className={mediaTitleClass(false)}>A video title</div>}
|
|
meta={
|
|
<>
|
|
<div className="text-sm text-muted">A channel</div>
|
|
<div className="mt-0.5 text-xs text-muted">12K views · 3 days ago</div>
|
|
</>
|
|
}
|
|
/>
|
|
</div>
|
|
</Sample>
|
|
<Sample label="Shelf · 232px, landscape, the smaller Play">
|
|
<div className="grid w-58 grid-cols-1">
|
|
<MediaCard
|
|
dense
|
|
picture={<Picture aspect="wide" length="2h 34m" small />}
|
|
title={
|
|
<div className={clsx("min-h-6", mediaTitleClass(true, true))}>
|
|
The Last Samurai, cut to one line on a shelf
|
|
</div>
|
|
}
|
|
meta={plexMeta}
|
|
/>
|
|
</div>
|
|
</Sample>
|
|
</div>
|
|
</Reference>
|
|
<Reference
|
|
id="s3h-measure"
|
|
code="S3h"
|
|
title="The reading measure"
|
|
rule="Prose (a synopsis, a biography, a channel's description) runs at most ~65 characters a line: the `measure` utility, wherever its container can be wider."
|
|
>
|
|
<p className="measure text-sm leading-relaxed text-muted">
|
|
Bourne is brought out of hiding once again by a reporter who is trying to unveil an
|
|
operation, an upgrade to a programme, in a series of newspaper columns. Information from
|
|
the reporter stirs a new set of memories, and Bourne must finally uncover his dark past.
|
|
</p>
|
|
</Reference>
|
|
</div>
|
|
);
|
|
}
|