From 29db94479bc6133a94d135f5e4021f6d66ad2bf5 Mon Sep 17 00:00:00 2001
From: JSONbored <49853598+JSONbored@users.noreply.github.com>
Date: Wed, 15 Jul 2026 19:48:45 -0700
Subject: [PATCH] feat(review): add a visible caption under each Visual/Scroll
preview thumbnail
Advances #6324
The screenshot-table contract this bot enforces on contributors (see e.g.
.claude/skills/metagraphed/SKILL.md's Phase B2 in JSONbored/metagraphed)
requires "each image is a clickable thumbnail... with a one-line caption
underneath". The bot's own Visual preview / Scroll preview tables never
rendered one -- the route/viewport/theme label existed only as an
invisible alt attribute.
Adds a
label line (the same label already used for alt)
after each thumbnail, inside the same table cell (a literal newline would
break the GFM table row). Purely additive to both buildBeforeAfterCollapsible
and buildScrollPreviewCollapsible's cell() builders -- every existing
assertion about the /
markup still matches unchanged.
This is one of two fixes for #6324; the image-downscaling half (the
"before images are massive" file-size complaint) is a separate, larger
change and will land in its own follow-up PR.
---
src/review/unified-comment-bridge.ts | 10 ++++++++--
test/unit/visual-collapsible.test.ts | 16 ++++++++++++++++
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/src/review/unified-comment-bridge.ts b/src/review/unified-comment-bridge.ts
index 426da542c6..e3ffcc9222 100644
--- a/src/review/unified-comment-bridge.ts
+++ b/src/review/unified-comment-bridge.ts
@@ -458,8 +458,13 @@ export function buildBeforeAfterCollapsible(routes: CaptureRoute[]): UnifiedColl
.replace(/`/g, "\\`")
.replace(/\|/g, "\\|")
.replace(/[<>]/g, (char) => (char === "<" ? "<" : ">"))}\``;
+ // #6324: the same one-line caption UNDER the thumbnail that the screenshot-table contract itself requires
+ // of contributors (see e.g. .claude/skills/metagraphed/SKILL.md's Phase B2 in JSONbored/metagraphed) --
+ // previously only present as the invisible `alt` attribute, never rendered as visible text.
(not a
+ // literal newline, which would break the GFM table row) keeps the caption inside the same cell; is
+ // the same de-emphasized styling this table already uses for its own footer legend line below.
const cell = (url: string | undefined, label: string): string =>
- url ? `
` : "—";
+ url ? `})
${attr(label)}` : "—";
const rows: string[] = [];
let hasAnyDiff = false;
for (const route of routes) {
@@ -506,8 +511,9 @@ export function buildScrollPreviewCollapsible(routes: CaptureRoute[]): UnifiedCo
.replace(/`/g, "\\`")
.replace(/\|/g, "\\|")
.replace(/[<>]/g, (char) => (char === "<" ? "<" : ">"))}\``;
+ // #6324: same visible one-line caption as buildBeforeAfterCollapsible's own cell() -- see its doc comment.
const cell = (url: string | undefined, label: string): string =>
- url ? `
` : "—";
+ url ? `})
${attr(label)}` : "—";
const rows: string[] = [];
for (const route of routes) {
if (!route.beforeGifUrl && !route.afterGifUrl) continue;
diff --git a/test/unit/visual-collapsible.test.ts b/test/unit/visual-collapsible.test.ts
index 2effea8005..5ad3955434 100644
--- a/test/unit/visual-collapsible.test.ts
+++ b/test/unit/visual-collapsible.test.ts
@@ -50,6 +50,19 @@ describe("buildBeforeAfterCollapsible", () => {
expect(c?.body).toContain("| `/` | desktop | — | {
+ const c = buildBeforeAfterCollapsible(routes);
+ // The caption text is the SAME string already used as the (invisible) alt attribute -- now also visible.
+ expect(c?.body).toContain('
before /app/analytics');
+ expect(c?.body).toContain('
after /app/analytics');
+ });
+
+ it("#6324: a dash cell has no caption to escape (no
emitted for a missing slot)", () => {
+ const c = buildBeforeAfterCollapsible([{ path: "/", afterUrl: "https://api.example.dev/gittensory/shot?key=gittensory/shots/x.png" }]);
+ expect(c?.body).toContain("| `/` | desktop | — | ");
+ });
+
it("returns null when no route has any shot URL (no empty table)", () => {
expect(buildBeforeAfterCollapsible([])).toBeNull();
expect(buildBeforeAfterCollapsible([{ path: "/" }])).toBeNull();
@@ -169,6 +182,9 @@ describe("buildScrollPreviewCollapsible (#3612)", () => {
expect(c?.body).toContain('before /app/analytics (scroll)");
+ expect(c?.body).toContain("
after /app/analytics (scroll)");
});
it("returns null when no route has a scroll GIF — byte-identical to pre-#3612 for every non-opted-in repo", () => {