Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit da102eab935821d0e19695a72c31bb1dfa83de36
parent e4429059d864fd7c1239574f666482e2f9a0ac3d
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Sun, 13 Sep 2026 11:18:58 -0400

channels: the focus is a line, and it counts what it holds

The focus control was a bordered card sitting above the table with the same
weight as the table itself. It is a statement about the corpus, so it reads
as one: an eyebrow, the label, a rule beneath, controls at h-7.

It also states the held COUNT - "Focus: site Foo - 61 held". That number is
the corpus-wide half of what the 61 orange per-row notes were saying, which
is what earns each row the right to shrink to a chip.

aria-label="channel focus", "focus site", "Focus site", "End focus",
"focus error" and the "Focus: {label}" / "No focus" text are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Meditor/app/channels/components/ChannelFocusBar.tsx | 22++++++++++++++++++----
Meditor/app/channels/components/ChannelsTable.tsx | 12+++++++++++-
2 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/editor/app/channels/components/ChannelFocusBar.tsx b/editor/app/channels/components/ChannelFocusBar.tsx @@ -51,12 +51,17 @@ export type FocusSite = { siteId: string; title: string }; export function ChannelFocusBar({ sites, focusLabel, + heldCount = 0, }: { sites: FocusSite[]; // What is focused right now, already resolved by the page ("site Foo", // "2 channels"), or null when nothing is. The full banner with the per-lane // pending numbers is S4's FocusBanner; this bar is only the control. focusLabel: string | null; + // How many rows the focus is holding. Stated HERE, once, because it is one + // corpus-wide fact — which is what lets each held row shrink to a chip + // instead of repeating the same sentence 61 times. + heldCount?: number; }) { const { pending, error, run } = useBarAction(); const [siteId, setSiteId] = useState(sites[0]?.siteId ?? ""); @@ -64,12 +69,21 @@ export function ChannelFocusBar({ if (sites.length === 0 && !focusLabel) return null; return ( + // A STATEMENT, not a panel: one line under the header with a rule beneath + // it, not a card competing with the rack for weight. The eyebrow names it + // the way the nav groups and the deck's verb groups are named. <div aria-label="channel focus" - className="flex flex-wrap items-center gap-2 rounded border border-border bg-card px-3 py-2 text-sm" + className="flex flex-wrap items-center gap-3 border-b border-border px-1 py-1.5 text-sm" > + <span className="font-mono text-[10px] uppercase tracking-[0.16em] text-muted-foreground"> + Focus + </span> <span className="text-muted-foreground"> {focusLabel ? `Focus: ${focusLabel}` : "No focus"} + {focusLabel && heldCount > 0 && ( + <span className="tabular-nums"> · {heldCount} held</span> + )} </span> {sites.length > 0 && ( <> @@ -78,7 +92,7 @@ export function ChannelFocusBar({ value={siteId} disabled={pending} onChange={(e) => setSiteId(e.target.value)} - className="rounded-md border border-border bg-card px-2 py-1 text-xs disabled:opacity-50" + className="h-7 rounded-md border border-border bg-card px-2 text-xs disabled:opacity-50" > {sites.map((s) => ( <option key={s.siteId} value={s.siteId}> @@ -90,7 +104,7 @@ export function ChannelFocusBar({ type="button" disabled={pending || !siteId} onClick={() => run(() => focusSiteAction(siteId))} - className="rounded-md border border-border px-2 py-1 text-xs hover:bg-muted disabled:opacity-50" + className="h-7 rounded-md border border-border px-2 text-xs hover:bg-muted disabled:opacity-50" > Focus site </button> @@ -101,7 +115,7 @@ export function ChannelFocusBar({ type="button" disabled={pending} onClick={() => run(() => endFocusAction())} - className="rounded-md border border-border px-2 py-1 text-xs hover:bg-muted disabled:opacity-50" + className="h-7 rounded-md border border-border px-2 text-xs hover:bg-muted disabled:opacity-50" > End focus </button> diff --git a/editor/app/channels/components/ChannelsTable.tsx b/editor/app/channels/components/ChannelsTable.tsx @@ -314,13 +314,23 @@ export function ChannelsTable({ return () => observer.disconnect(); }, []); + // HOW MANY ROWS THE FOCUS IS HOLDING, for the focus line's one-line summary. + // The reason itself stays per-row (each held chip carries the full sentence + // as its screen-reader text); the COUNT is the corpus-wide fact, and stating + // it once above the table is what lets the rows stop repeating the sentence. + const heldCount = channels.filter((c) => c.priority.heldReason).length; + return ( // On md+ this is a pane of the page's viewport-height flex column: the // instrument bar and the deck are fixed chrome and only the region between // them scrolls. Below md it has no height of its own and the document // scrolls, exactly as it always has. <div className="flex min-h-0 flex-1 flex-col"> - <ChannelFocusBar sites={sites} focusLabel={focusLabel} /> + <ChannelFocusBar + sites={sites} + focusLabel={focusLabel} + heldCount={heldCount} + /> {/* THE INSTRUMENT BAR: what the rack is showing (grouping) on the left, how to read it (the band legend, and the two-route explainer behind a disclosure) on the right. Both used to live in an 11px stack UNDER 67