Producer-visible demo gate. /review hits POST /api/generate on mount, lays
the resulting BannerSpecs into a grid of Konva canvases, plays each on its
own GSAP timeline, and opens a right-side drawer showing AI reasoning +
the resolved spec JSON when a card is clicked.
UI:
- apps/web/app/review/page.tsx: thin shell, lazy-loads ReviewClient via
next/dynamic(ssr:false). Mirrors /parity (Konva touches window; dropflow
uses top-level await — neither survives SSG).
- ReviewClient.tsx: init → loading → ready | error state machine. Calls
ensureBrowserEngine() then fetch('/api/generate'). Reconstructs feed-row
order by slotting skipped/error rows at their declared rowIndex and
filling the gaps with the ok specs in arrival order.
- components/BannerCanvas.tsx: pure Konva renderer. Gray Rect fallback,
hero Image with manual cover-fit (Konva has no native object-fit:cover),
text layers wrapped in <Group ref> keyed by layer_id so GSAP can tween
attrs.opacity. Typography is looked up from DEMO_TEMPLATE_300x250 since
ResolvedLayer doesn't carry it.
- components/BannerCard.tsx: one card per spec. Builds its GSAP timeline
on mount inside a requestAnimationFrame (so Konva refs are populated),
registers a restart fn upstream, kills the timeline on unmount.
- components/BannerGrid.tsx: feed-row ordered grid. Skipped/error rows get
gray placeholder cards with status pills so producers see exactly which
row was which.
- components/AiReasoningDrawer.tsx: slide-in 420px drawer. Closes on X,
backdrop click, or Escape. Four labeled reasoning sections + collapsible
<details> with pretty-printed BannerSpec JSON.
- lib/build-timeline.ts: pure factory. fade_in/fade_out map to gsap tweens
on node.attrs.opacity; hold is a no-op; one timeline-level onUpdate
drives stage.batchDraw(). Initial opacity(0) is set before play to avoid
a first-frame flash.
- lib/typography-lookup.ts: walks the unresolved template (incl. group
children) for TypographySpec by layer_id.
- styles.module.css: scoped grid/card/drawer styles.
Other:
- apps/web/package.json: konva@^9, react-konva@^18.2.10, gsap@^3.12.
- apps/web/app/page.tsx: /review listed as "review grid (live)".
Verified: pnpm -F @banner-studio/web build is clean (/review in route
table at 1.21 kB). All 26 Day-2 tests still pass. Dev server boots and
serves /review at HTTP 200 with the expected CSR bail-out.
End-of-day gate (VERTICAL_SLICE.md:154): A producer-shaped user could see
this and immediately understand what the product does. They see 5 banners
generated from 5 feed rows, animating, with reasoning visible.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>