commit f4cc389be809bfdd8b3354277371ffea76a34415
parent 28018de287518d49f453ab47048cf7a84d926621
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Sun, 20 Sep 2026 19:01:35 -0400
storage: one badge per row, and the count cell is a count
Three e2e failures, one cause each, all mine.
The Location column drew a SECOND MediaLocationBadge on every relocated row —
the Slug cell already carries one. Two elements with the same accessible name
is a strict-mode violation for the suite and the same sentence read twice for a
screen reader. The column answers "which disk"; the badge answers "can it be
reached"; one of each per row is the right number.
On /storage the "list them" link was folded into the element whose accessible
name is "location channels", whose exact text two specs assert. It gets its own
row of the grid.
And the saved-video store's "every reader keeps working" check went to
/saved-videos, which lists POINTER sidecars written by the retention rule, not
the store's contents — a store seeded directly has nothing for it to list. The
claim is a filesystem one: read the container back through the symlinked path.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
3 files changed, 44 insertions(+), 21 deletions(-)
diff --git a/editor/app/channels/components/ChannelsTable.tsx b/editor/app/channels/components/ChannelsTable.tsx
@@ -809,14 +809,15 @@ function ChannelTableRow({
className={`whitespace-nowrap text-xs${dim}`}
title={c.media?.target ?? undefined}
>
- <span className="inline-flex items-center gap-1.5">
- {c.volumeLabel}
- {/* The unreachable / in-transition marking the badge already owns.
- The COLUMN says where the media is; the badge says whether it can
- be reached, which is a different fact and the one that makes every
- other number on the row suspect. */}
- <MediaLocationBadge media={c.media} compact />
- </span>
+ {/* THE LABEL ONLY — NO SECOND BADGE. The unreachable / in-transition
+ marking is the badge's, and the badge is already on this row, in the
+ Slug cell beside the channel's name; drawing another here put two
+ elements with the same accessible name on every relocated row, which
+ is a strict-mode violation for the suite and a screen reader reading
+ the same sentence twice for a human. The COLUMN answers "which
+ disk"; the badge answers "can it be reached", and one of each per
+ row is the right number. */}
+ {c.volumeLabel}
</Td>
<Td
ariaLabel={`media size for ${c.slug}`}
diff --git a/editor/app/storage/components/StorageLocationsTable.tsx b/editor/app/storage/components/StorageLocationsTable.tsx
@@ -196,11 +196,16 @@ function LocationCard({
"unknown — nothing here can ask (no findmnt, or a container)")}
</dd>
<dt className="text-muted-foreground">Channels</dt>
- <dd aria-label="location channels">
- {row.channelsText}
- {row.channels.total > 0 && (
- <>
- {" — "}
+ {/* THE COUNT AND THE LINK ARE TWO ELEMENTS, not one. `location channels`
+ is the read-out's accessible name and the suite asserts its exact
+ text; a link folded into it appends "— list them" to that text and
+ breaks every caller. The link gets its own `<dd>` on the next row of
+ the grid. */}
+ <dd aria-label="location channels">{row.channelsText}</dd>
+ {row.channels.total > 0 && (
+ <>
+ <dt className="text-muted-foreground">Browse</dt>
+ <dd>
{/* THE ROW'S OWN LIST, largest first. A summary that cannot be
opened is a number the operator has to go and re-derive by
hand, which on this page is opening 71 channel pages. */}
@@ -209,11 +214,11 @@ function LocationCard({
aria-label={`list channels on ${row.id}`}
className="underline hover:text-foreground"
>
- list them
+ the {row.channels.total} channel(s) on this location
</Link>
- </>
- )}
- </dd>
+ </dd>
+ </>
+ )}
<dt className="text-muted-foreground">Media</dt>
<dd aria-label="location media bytes">{row.bytesText}</dd>
<dt className="text-muted-foreground">Free</dt>
diff --git a/editor/e2e/storage-locations.spec.ts b/editor/e2e/storage-locations.spec.ts
@@ -1,4 +1,12 @@
-import { mkdir, readlink, rename, rm, symlink, writeFile } from "node:fs/promises";
+import {
+ mkdir,
+ readFile,
+ readlink,
+ rename,
+ rm,
+ symlink,
+ writeFile,
+} from "node:fs/promises";
import { join } from "node:path";
import { test, expect, type Page } from "@playwright/test";
import { baseUrl } from "./baseUrl";
@@ -404,9 +412,18 @@ test("the saved-video store moves onto a location, and back", async ({
}>("test-settings.json");
expect(after.storage.savedVideosLocationId).toBe("cold");
- // EVERY READER KEEPS WORKING, through the link, unchanged.
- await page.goto("/saved-videos");
- await expect(page.locator("body")).toContainText(SLUG);
+ // EVERY READER KEEPS WORKING, THROUGH THE LINK, UNCHANGED — which is the
+ // whole claim the symlink design rests on, and it is a filesystem fact, not a
+ // page one. (`/saved-videos` lists POINTER sidecars written into each video
+ // dir by the retention rule, not the store's contents, so a store seeded
+ // directly has nothing for it to list — that page is not the reader this
+ // proves.)
+ expect(
+ await readFile(
+ join(storeDir, SLUG, VIDEO, "source-media.mp4"),
+ "utf8",
+ ),
+ ).toBe("not really an mp4");
// --- back ---------------------------------------------------------------
await page.goto("/storage");