Skip to content

fix(templates): valid reading-list gallery covers + integrity guard#129

Merged
eliotlim merged 2 commits into
mainfrom
feat/template-reading-list-covers
Jul 12, 2026
Merged

fix(templates): valid reading-list gallery covers + integrity guard#129
eliotlim merged 2 commits into
mainfrom
feat/template-reading-list-covers

Conversation

@eliotlim

Copy link
Copy Markdown
Owner

Problem

The Reading list template's Gallery view names a coverPropertyId, but its
seeded rows shipped no covers — so the gallery rendered as empty cover slots
(placeholder document icons), looking broken out of the box. A first pass seeded
inline PNG covers, but one of the three (COVER_TEAL, on "Thinking, Fast and
Slow"
— the lead card of the To read shelf) was a corrupt PNG: its IDAT
zlib stream failed its Adler-32 check, so it rendered as colour-noise instead of a
book cover. The existing tests false-greened on it — a bare toBeVisible()
never decodes the image.

Notion epic: Epic: Templates that showcase OpenBook — interactive + slides (IA 2026-07) · closes TPL-7.

Solution

  • Seed three valid inline PNG data-URI book covers (one per shelf: teal /
    orange / blue), regenerated with a reliable encoder (zlib.deflateSync IDAT +
    correct per-chunk CRC-32 + a valid Adler-32) and verified to decode before
    embedding
    . Consistent layout: colored fill + darker left spine + light title
    band + small author-label block.
  • Broaden isImageUrl so a data:image/* cover renders straight into the
    gallery/thumbnail <img src> — display-only, no asset-store write, no upload
    allowlist change. Tightened to exclude the svg subtype
    (/^data:image\/(?!svg)/i), matching the code's own "PNG, never SVG" intent
    (Quinn's optional hardening).
  • Add an image-integrity test guard so a cover can't silently rot again: each
    seeded cover is base64-decoded, its IDAT inflated, and checked for the exact
    raw-scanline byte count plus a recomputed Adler-32 match.
  • No new deps (the integrity check reuses the existing fflate dependency).

The 50/50 coverage per shelf (one covered card + one placeholder) is intentional —
it keeps the placeholder/fallback path exercised and reads as a realistic library.

Before / After

Behaviour Before After
To read shelf — "Thinking, Fast and Slow" cover
empty cover slot (placeholder icon); the teal cover was a corrupt PNG rendering as colour-noise

a proper teal book cover (spine + title band)
Full shelf-grouped gallery
every card a placeholder slot

teal / orange / blue covers, one per shelf

Test procedure

  • pnpm verifygreen: sdk 171 + ui 968 + server 645 unit tests pass; MCP e2e 40 checks pass; typecheck + lint clean.
  • Reading-list web e2e (templates.spec.ts) — green: covers render as <img> and the first cover asserts naturalWidth > 0 (a decode failure now reddens the suite).
  • The new per-cover IDAT-integrity unit assertion passes: each seeded cover inflates to exactly height*(width*3+1) bytes with a matching Adler-32. (Fed the old corrupt teal it fails — 21,812 ≠ 28,920 decoded bytes + Adler mismatch — proving the guard bites.)
  • Table view still lists 6 rows (row count unchanged).

Operational notes

  • No migrations, no env changes. Covers are inline data: URIs, not
    asset-store objects — they seed identically on both transports (web PGlite +
    desktop IPC) with no upload call.
  • The isImageUrl change is a display-only broadening of which URL strings
    render in an <img src>; a data: URL in an <img> executes no script, and
    the svg-subtype exclusion keeps it in step with the store's image allowlist —
    no new XSS/exfil surface (Quinn's security-lens verdict).
  • Gates: code review ✓ Quinn (approved; this folds in his optional hardening),
    design review ✓ Devon (after the corrupt-cover fix).

pnpm verify green · gates: code ✓ Quinn · design ✓ Devon

🤖 Generated with Claude Code

https://claude.ai/code/session_01KFk2T9k3p7ghCjdzMfkA5w

eliotlim and others added 2 commits July 12, 2026 11:24
The reading-list template set `coverPropertyId` on its gallery view but seeded
no covers, so the gallery shipped with empty/broken cover slots. Seed three
tiny (<300 B) raster-PNG book covers as inline `data:` URLs on the files-typed
`p_cover` cells — one per shelf, so every gallery group leads with a real card.

The `files` property and the gallery cover render a URL string straight into
`<img src>` with no asset-store resolution seam, so covers are inlined rather
than routed through the content-addressed asset store (a store `assetId` would
not load there). This seeds identically on both transports (web PGlite + desktop
IPC) with no upload call. PNG, never SVG — honouring the store's image allowlist
even though nothing is stored.

Teach `isImageUrl` to recognise `data:image/*` URLs so `coverImageUrl` /
`firstImageUrl` resolve the inline covers (and the files-cell thumbnail renders
them). Display-only; unrelated to the asset upload allowlist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFk2T9k3p7ghCjdzMfkA5w
The seeded COVER_TEAL data-URI shipped a corrupt PNG: its IDAT zlib
stream failed the Adler-32 check, so "Thinking, Fast and Slow" (the lead
card of the To-read shelf) rendered as colour-noise instead of a cover.
The existing tests false-greened on it — a bare toBeVisible() never
decodes the image.

- Regenerate all three covers with a reliable encoder (zlib.deflateSync
  IDAT + correct per-chunk CRC-32 + valid Adler-32), verified to decode
  before embedding; consistent teal/orange/blue book-cover layout.
- Add a per-cover IDAT-integrity unit assertion (inflate + exact
  scanline size + recomputed Adler-32 match) so a corrupt cover can't
  silently rot again.
- e2e now asserts naturalWidth > 0 on the first rendered cover so a
  decode failure reddens the suite.
- Tighten isImageUrl's data-URI match to exclude the svg subtype
  (/^data:image\/(?!svg)/i), matching the code's "PNG, never SVG" intent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFk2T9k3p7ghCjdzMfkA5w
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app.book.pub Ready Ready Preview, Comment Jul 12, 2026 4:20am

Request Review

@eliotlim eliotlim merged commit 1216cfd into main Jul 12, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant