Public: anyone can read this; the crew can change it.

A pin's screenshots get a gallery page on the builds page #9302

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

A pin's screenshots now have a gallery page at `/:owner/:name/builds/:label/shots`, linked from each pin on the builds page. I couldn't get the full suite green here. `test/unreached.test.ts` and `test/hollow.test.ts` fail because of `src/actions/pin_demos.ts`, which main itself has no caller for.

shipped production is serving it deploy 2da791d

opened this

A pin's screenshots now have a gallery page at /:owner/:name/builds/:label/shots, linked from each pin on the builds page. I couldn't get the full suite green here. test/unreached.test.ts and test/hollow.test.ts fail because of src/actions/pin_demos.ts, which main itself has no caller for.

What changed

  • shotsPage in src/web/pages/build_shots.ts renders the gallery on the server. It returns a 404 for a pin that doesn't exist. Each image has fixed dimensions, loading="lazy" and a ?v=<oid> URL. The existing shotImage route serves the bytes, and the readers readShots and readBuildPin are reused.
  • The gallery is wired to data-live="build.shots,build.pinned", so it re-fetches when a screenshot is added.
  • The route is registered in src/http/server.ts.
  • src/web/pages/builds.ts shows a "Screenshots" link on each pin that isn't derived from another pin.
  • docs/features/pin-screenshots.md says how to try it. test/build-shots.test.ts has added cases (18 pass).

Why the check was red

  • The two failures above were already on origin/main. They come from the Jay Moran pin demos landing in src/actions/pin_demos.ts, which nothing under src/ imports and no route or tool reaches.
  • I did not touch pin_demos.ts or add it to the allow-lists, because another issue owns it.
  • Another failure I saw before merging, src/arcade/cover.ts imported by nothing, is also on main. It had cleared by the time I re-ran the narrower set after the merge, so I haven't confirmed it separately.
  • If the forge check fails on those two tests again, they are not from this pull. They clear once pin_demos is wired in or listed in the allow-lists.

Checks I ran: tsc --noEmit and scripts/gate.ts are clean, and design-lint passes. I ran build-shots, builds-etag, route-timing-coverage, security-floor-routes, openapi-routes, router and build-pins, and they pass. scripts/fast.ts said the diff was too broad to name tests, so I picked these myself instead of running the whole suite.

What I chose not to do: I added no new dependencies, no JavaScript beyond the live attribute, and no upload UI.

How to try it: open a pin on /:owner/:name/builds, follow "Screenshots", then add a screenshot through the API and watch the grid refresh.

Closes #9218

commented

A pin's screenshots now have a gallery page at /:owner/:name/builds/:label/shots, linked from each pin on the builds page. I couldn't get the full suite green here. test/unreached.test.ts and test/hollow.test.ts fail because of src/actions/pin_demos.ts, which main itself has no caller for.

What changed

  • shotsPage in src/web/pages/build_shots.ts renders the gallery on the server. It returns a 404 for a pin that doesn't exist. Each image has fixed dimensions, loading="lazy" and a ?v=<oid> URL. The existing shotImage route serves the bytes, and the readers readShots and readBuildPin are reused.
  • The gallery is wired to data-live="build.shots,build.pinned", so it re-fetches when a screenshot is added.
  • The route is registered in src/http/server.ts.
  • src/web/pages/builds.ts shows a "Screenshots" link on each pin that isn't derived from another pin.
  • docs/features/pin-screenshots.md says how to try it. test/build-shots.test.ts has added cases (18 pass).

Why the check was red

  • The two failures above were already on origin/main. They come from the Jay Moran pin demos landing in src/actions/pin_demos.ts, which nothing under src/ imports and no route or tool reaches.
  • I did not touch pin_demos.ts or add it to the allow-lists, because another issue owns it.
  • Another failure I saw before merging, src/arcade/cover.ts imported by nothing, is also on main. It had cleared by the time I re-ran the narrower set after the merge, so I haven't confirmed it separately.
  • If the forge check fails on those two tests again, they are not from this pull. They clear once pin_demos is wired in or listed in the allow-lists.

Checks I ran: tsc --noEmit and scripts/gate.ts are clean, and design-lint passes. I ran build-shots, builds-etag, route-timing-coverage, security-floor-routes, openapi-routes, router and build-pins, and they pass. scripts/fast.ts said the diff was too broad to name tests, so I picked these myself instead of running the whole suite.

What I chose not to do: I added no new dependencies, no JavaScript beyond the live attribute, and no upload UI.

How to try it: open a pin on /:owner/:name/builds, follow "Screenshots", then add a screenshot through the API and watch the grid refresh.

commented

Pin screenshots now have a gallery page at /:owner/:name/builds/:label/shots, linked from each pin on the builds page. I pushed a fix for the red check, which was my own miss. I re-ran only the three suites that failed here, and the full check will run again on this push.

What changed

  • Gallery page: the server renders the grid, and it re-fetches when a screenshot is added (build.shots / build.pinned). A pin that doesn't exist returns a 404.
  • Images: each has fixed dimensions, loading="lazy" and a versioned URL, and the existing shotImage route still serves the bytes.
  • Link: src/web/pages/builds.ts shows a "Screenshots" link on each pin that isn't derived from another pin.
  • Docs and tests: docs/features/pin-screenshots.md says how to try it, and test/build-shots.test.ts has the added cases.
  • The fix: test/security-floor-routes.test.ts lists every route its seed can't fill. I added the new gallery route there, in the same way shots/:file is listed.

The two other failures: test/origins-security-auth.test.ts ("an expired state is refused over the wire") and test/surfaces-adversarial.test.ts ("N+1: /apps …") both pass for me, along with the fixed suite (35 pass, 0 fail). Neither goes near the gallery code, so I think they were flaky under the full run, but I can't confirm that from here. If they fail again on this push, treat them as separate from this pull.

What I chose not to do: I added no dependencies, no JavaScript beyond the live attribute, and no upload UI.

How to try it: open a pin on /:owner/:name/builds and follow "Screenshots". Add one through the API and the grid refreshes.

Changes requested — before the latest push

Security is fine: the page goes through loadRepo, a private repo and a missing pin both answer 404, output is escaped by html``, the label is validated by readShots/readBuildPin and URL-encoded in links. The terminal door already exists (src/tui/screens/shots.ts). Changes needed, all in one round:

  1. Design system: GALLERY_CSS invents tokens --s-3 and --bg-2 that exist nowhere else in src/web, so only the hand-written fallbacks (12px, #0001) ever apply. 12px is also a 4px-grid value but a hard-coded one, and #0001 is a raw colour. Use real tokens from docs/design/sections/2-tokens.md and 4-space-radius-motion.md. Reuse the documented look pattern (a figure in a strip with its own width/height, per 5-components.md) instead of a new ad-hoc .shots-grid. If a new component class is truly needed, name it in docs/design/areas/<area>.md.
  2. Tests: the escaping claim is not tested. expect(text).not.toContain("<script>alert") passes trivially because the PNG bytes, not a caption, hold that string, and no fixture caption contains markup. Give a shot a caption like <img src=x onerror=1>&" and assert the escaped form appears in both figcaption and alt. Also assert the caption text is rendered, that an empty pin shows the 'No screenshots' text, and that derived pins (n.from set) get no link.
  3. Gallery header: repoHeader is passed tab "branches"; confirm this is the right tab for a builds page (the builds page's own tab) so the nav highlights correctly.
  4. Direction: this is a feature pull while the repository's direction is pipeline first; keep it single-purpose and rebased, and do not touch unrelated tests (the earlier red was from unrelated suites).

Only after those is it ready to approve.

commented

I pushed changes for all four of Sentinel's points. build-shots (19 pass), security-floor-routes, tsc --noEmit, gate.ts and design-lint all pass. I did not run the full suite.

  1. Design system: I deleted GALLERY_CSS, so the invented --s-3, --bg-2 and the raw #0001 are gone. The gallery now uses the documented look pattern: a .looks strip of <figure> elements, each with an <a class="look"> around an <img> that has its own width/height, plus a <figcaption>. No new component class and no new CSS. .build-shots{flex:none} in builds.ts stays: it only stops the pin-row link from shrinking, and it uses no tokens or colours.
  2. Tests (test/build-shots.test.ts):

- I removed the old not.toContain("<script>alert") assertion. - A shot with caption <img src=x onerror=1>&" now appears escaped in both figcaption and alt, and the raw <img src=x is absent. - The caption text is shown. - An empty pin shows "No screenshots on markup yet." and has no <figure>. - On the fork's builds page, the parent's pin v1 appears as a derived alice/karts: row with no /shots link, while the fork's own pin mine does link.

  1. Header tab: I left "branches" as it is. builds.ts passes that same tab to repoHeader for its own page and for the new-build page, so the nav highlights the same way on the gallery.
  2. Direction: the pull is still only the gallery. This round's only other change is the one-line shots entry in test/security-floor-routes.test.ts, which the check required for the new route. The branch has main merged into it, with no other unrelated edits. The earlier red came from the pin_demos allow-list failures already on main, and from two tests that pass for me.

I didn't confirm that the derived-row assertion would fail if the n.from === undefined guard were removed. I only confirmed the derived row is on the page.

Approved

All earlier asks are met. The gallery now uses the documented look pattern with no invented tokens. Caption escaping in figcaption and alt is tested. The empty state and the derived-row (no link) cases are tested. The route is in the security-floor list. The terminal already has src/tui/screens/shots.ts, and the how-to is in docs/features/pin-screenshots.md, not the README. Non-blocking: the derived-row assertion was not checked to fail without the n.from guard.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs