PRD: Separated Components Generation & Preview (API/CDN)
Status: Phase 1β2 implemented on PATTTTERNS (same Netlify site). Phase 3β4 not started.
Canonical doc for the chat βPlanβ β do not maintain a second copy; update this file instead.
Prompting contract (1st gen vs regenerate, image/metadata gaps): Components Generation Prompting.
Objective
Move component TSX out of the main PATTTTERNS out/ build into a headless API + CDN. All user-facing UI stays on PATTTTERNS. Generations must not rebuild the main site.
Locked split
| Layer | Lives where | Owns |
|---|---|---|
| All user-facing UI | PATTTTERNS always | Pattern Export, previewer, /debug, login modal |
| API + CDN | Headless backend (Phase 1: Netlify Functions + Blobs on this site; later extractable to its own repo) | Durable TSX, versions, authors, Gemini generate/regenerate |
There is no separate components website.
Goals
- Signed users can generate new components.
- Components may use primary or secondary pattern image; always exactly one immutable seed per pattern.
- Versions under a pattern are grouped and stamped with author name/avatar.
- PATTTTERNS does not rebuild on every generation.
- Backend may be Functions+Blobs now, or a dedicated repo/CI later β same API contract.
Problem
Today generated TSX starts as a build artifact of the main site:
- Written by
scripts/build-components-cache.mjsintopublic/components/code/ - Committed in-repo, then copied into Netlify
out/via Next static export (Phase 2 now stripscode/+history/from publish) - Preview/Export already HTTP-fetch by URL (
usePatternCode) β that part was fine
Debug regenerate APIs that write the local filesystem 404 on Netlify because out/ is immutable.
So the blocker is not βpreview canβt leave out/β β preview already loads by URL. The blocker is mutable generation with durable storage, while the main site remains a static CDN publish. Direction: component TSX leaves the main publish; UI stays on PATTTTERNS (no separate components website).
Architecture
flowchart TB
subgraph mainSite [PATTTTERNS UI only]
PatternPage[Pattern page + Export CTA]
DebugUI["/debug generations UI"]
PreviewModal[ArtifactPreviewModal]
end
subgraph componentsBackend [Headless API and CDN]
API[Auth-gated API]
GenWorker[Gemini generate/regenerate]
Store[(Durable store TSX + versions + authors)]
TsxCdn[CDN or Blob URLs for raw TSX]
end
subgraph identity [Shared]
Supabase[Supabase Auth]
end
PatternPage -->|"GET active / POST generate"| API
DebugUI -->|"GET list / POST generate / hide"| API
PreviewModal -->|"GET .tsx"| TsxCdn
API --> GenWorker --> Store
Store --> TsxCdn
API --> Supabase
Data model (per pattern)
patternId
seedVersionId: "seed"
activeVersionId: "vN" | "seed"
versions[]:
versionId, kind: seed|regenerate
createdAt, model, thinkingBudget
imageRole: primary|secondary
author: { id, name, email, image }
hiddenAt?
- Seed is immutable and never soft-deleted.
- Regenerates use seed TSX as prompt context and a lower thinking budget.
API contract
Base (Phase 1): /.netlify/functions/components-api
| Method | Query / body | Purpose |
|---|---|---|
GET | op=list | All visible generations (for /debug) |
GET | op=versions&id= | Versions for one pattern |
GET | op=active&id= | Active TSX (text/plain) |
GET | op=code&id=&version= | Specific version TSX |
POST | op=generate + JSON { id, imageRole? } | Seed or regenerate (auth required) |
POST | op=put-seed + JSON { id, code, title?, slug?, coverImage? } | Bootstrap immutable seed without Gemini (auth) |
POST | op=hide + JSON { id, versionId, confirm, confirmAgain } | Soft-hide non-seed (auth required) |
Image gap
Search-index today has only coverImage (primary). Secondary image requires a later index/Notion enrichment. Phase 1 uses primary only.
Phased delivery
Phase 1 β Same-site Functions + Blobs β
- Auth-gated Netlify Function API
- Netlify Blobs for TSX + version indexes
- Bootstrap seed from existing
/components/code/{id}.tsxon first generate if missing in Blobs - Wire
/debugregenerate/hide/list to the Function - Main Export may still fall back to static TSX until Phase 2
Phase 2 β Decouple main out/ β
closed
Checklist:
usePatternCodeuses Components API only in production; static/debug fallbacks remain fornext dev- Postbuild strips
out/components/codeandout/components/history(scripts/strip-components-from-out.mjs) - Artifact badges use
components-status.json(server + client), not published TSX op=put-seed+ migrate script bootstrap Blobs without Gemini- Optional
COMPONENTS_SEEDS_BASE_URLfor transitional seed fetch after strip - Clean local pipeline: first-gen β
put-seed/ migrate (Components Seeds Pipeline)
Ops note: Prefer npm run publish:content (includes components:seeds:upload) via Netlify CLI (netlify login + netlify link) so new seeds land in Blobs without a Supabase token. Targeted ops: npm run components:seeds:upload -- --ids=<id>. Optional --via-api keeps op=put-seed. See Components Seeds Pipeline.
Repo keeps public/components/code for local generation cache and migrate input; publish dir does not.
Phase 3 β Secondary image + grouped UX
- Secondary image in index
/debuggrouped-by-pattern author filters
Phase 4 β Optional extract
- Move Function/Blobs (or equivalent) into a dedicated components API repo/CDN host without changing PATTTTERNS UI
Env (Phase 1β2)
| Var | Where | Purpose |
|---|---|---|
GEMINI_API_KEY | Netlify Function | Regenerate |
NEXT_PUBLIC_SUPABASE_URL / anon | Function + UI | Auth |
NEXT_PUBLIC_COMPONENTS_API_BASE | UI (optional) | Override API base; default /.netlify/functions/components-api |
COMPONENTS_SEEDS_BASE_URL | Function (optional) | Transitional origin that still serves /components/code/{id}.tsx |
COMPONENTS_STRIP_OUT | Build (optional) | Set 0 to skip stripping out/ TSX |
PATTERN_REGENERATE_THINKING_BUDGET | Function (optional) | Default 1024; regenerate thinking tokens |
PATTERN_REGENERATE_GEMINI_TIMEOUT_MS | Function (optional) | Default 22000; Gemini abort for op=generate |
PATTERN_REGENERATE_TEMPERATURE / TOP_P | Function (optional) | Mild sampling bump for click-only regenerates |
Prompt contract: Components Generation Prompting.
Click-only regenerate quality (shipped): Plan Status.
PRD prompt
Build a headless Components API/CDN. PATTTTERNS remains the only user-facing UI. Signed Supabase users create an immutable seed and later regenerates (seed as context; primary/secondary image). Versions grouped by patternId with author attribution. Soft-hide never deletes seeds. PATTTTERNS must not ship TSX in
out/long-term and must not rebuild on generate. Do not build a separate components website.
Success metrics
- Regenerate works on Netlify without a main-site rebuild
- New versions persist in Blobs (not
out/) - Seeds remain available; authors recorded
- Export/Preview load active TSX from the Components API (no TSX in published
out/)