YYayaw
Docs

Component Catalog

Internal UI component inventory, preview rules, and V2 registry contract.

Overview

Yayaw includes an internal component catalog at:

  • /dashboard/content/components

The catalog supports a mixed inventory:

  • local components from src/components/ui/*
  • imported components stored in DB snapshots (non-executable)

Related docs:

CMS Role

The component catalog is the CMS rendering inventory. It documents and previews the low-level UI pieces that sections can use, including local components and imported source snapshots. Components are not authored content by themselves; they are the safe rendering primitives used by reusable sections and direct page references.

Keep component imports non-executable, compile them into validated recipes, and publish only entries that pass compile and smoke-render checks.

Goals:

  • list UI components from src/components/ui
  • list imported UI components from DB revisions
  • preview compatible components with live props controls
  • show source files used by each entry
  • validate token-aware rendering with the current runtime design tokens

Scope and Conventions

Local source of truth remains explicit: src/components/ui/catalog/registry.ts.

Current scope:

  • top-level files in src/components/ui/*.tsx
  • key entries for src/components/ui/kibo-ui/*/index.tsx
  • key entry for src/components/ui/yayaw-table/index.ts
  • imported entries from DB (ui_component_registry_items) merged into the same inventory

Conventions:

  • one catalog entry per key component
  • one key component per folder for nested component directories
  • target entrypoint is index.tsx for nested folders (legacy exceptions are explicit in the registry)
  • imported entries are global-scope registry items with source snapshots only
  • collision rule is deterministic: imported > local for the same slug/id

Preview Model

Preview behavior is recipe-driven and shared with runtime rendering:

  • src/lib/server/services/components/ui-component-recipe-compiler.ts
  • src/components/ui/catalog/ui-recipe-renderer-server.tsx
  • src/components/ui/catalog/ui-recipe-renderer-client.tsx

Flow:

  1. Source snapshot is compiled to UiComponentRecipeV1.
  2. Catalog preview renders the recipe with generated controls from propSchema, apiSchema, and harnessSchema.
  3. Block runtime resolves published recipe revisions and uses the same renderer path.

creative-composition is intentionally narrower: it accepts runtime inputs only through propSchema. Its apiSchema and harnessSchema must be omitted or empty, its fixtures must be empty, and the related API, harness, and fixture settings cannot act as hidden runtime content channels.

Runtime-only preview policy:

  • catalog preview always uses the internal recipe renderer
  • no external docs iframe is embedded in the preview panel
  • preview parity checks happen through variant tabs when multiple libraries expose the same component key

Publication gating:

  • compiled + smoke render OK => published (auto-publication for imports/recompile)
  • otherwise => draft_blocked

Soft delete:

  • imported components are removed from catalog with publication status deleted
  • source snapshots remain in DB for audit/replay

AI SDK Integration

AI-driven compile fallback and classification now use Vercel AI SDK:

  • src/lib/server/services/components/ui-component-ai-client.ts
  • src/lib/server/services/components/ui-component-recipe-ai-fallback.ts
  • src/lib/server/services/components/ui-component-import-classifier.ts

Rules:

  • all AI outputs are validated with strict zod schemas
  • model usage follows a built-in resilient fallback chain; page/block builders can still request a preferred model from UI controls
  • deterministic fallback is always available if AI is unavailable
  • no AI failure blocks import or compile pipelines

Library and Category Classification

Imported components are classified with a hybrid strategy:

  1. cache by sourceHash (reuse previous classification metadata)
  2. deterministic heuristics (library + group)
  3. AI SDK enrichment (library/category refinement)

Metadata persisted on registry rows:

  • library
  • group
  • classificationSource
  • classificationVersion
  • classificationModel (when AI is used)

Category policy:

  • normalized to lowercase kebab-case
  • new categories are accepted automatically after normalization

Library policy:

  • controlled values: shadcn-ui, kibo-ui, yayaw, ai-sdk, custom

AI Elements Vendored Components

AI Elements is vendored under a dedicated namespace:

  • src/components/ui/ai-elements/*
  • catalog local registry ids use ai-elements-* to avoid collisions with core shadcn ids

Install/update script:

  • bun run components:install-ai-elements

Script behavior:

  • resolves @ai-elements registry index
  • installs each registry item into src/components/ui/ai-elements
  • skips existing files by default (--all to reinstall every item)
  • regenerates src/components/ui/ai-elements/index.ts
  • regenerates deterministic composite preview overrides in src/lib/server/services/components/ui-component-ai-elements-preview-overrides.generated.ts
  • continues on per-item failure and reports a summary

Catalog behavior:

  • vendored AI Elements files are listed in src/components/ui/catalog/registry.ts
  • they are tagged as library ai-sdk
  • unknown AI Elements families compile to composite recipes by default to keep previews available
  • local ai-elements-* composite recipes are enriched by deterministic overrides generated from source snapshots (components:generate-ai-elements-previews)
  • imported AI Elements items automatically use ai-elements-* slug prefix when their base slug would collide with an existing local catalog id
  • when multiple libraries expose the same canonical component key, preview tabs let users switch variants and display whether their normalized sources are identical or different
  • the dashboard loads only catalog summaries first; the selected variant's recipe and source are loaded on demand so opening the Components page does not compile or transfer every component preview

Unified Entries and Variant Tabs

The inventory is grouped by logical component key, not by raw registry id.

Rules:

  • one inventory row per canonical component key
  • each row can expose multiple library variants (tabs in preview)
  • selecting a library filter keeps a row visible when it has at least one variant from that library
  • selecting a category filter keeps a row visible when at least one variant matches that category

Canonical Key Grouping Rules

Canonical key resolution is defined in:

  • src/components/ui/catalog/catalog-identity.ts

Current normalization:

  • ai-elements-* ids are grouped under their stripped base key
  • all other ids keep their original value as canonical key

Library variant order is deterministic:

  • shadcn-ui
  • ai-sdk
  • kibo-ui
  • yayaw
  • custom

Import Flows and Registry Sync

V2 supports component management from the catalog UI for users with components:manage.

Supported add flows:

  • shadcn command input
  • full registry alias sync import (defaults to onlyNew=true)
  • source snapshot paste (name, sourceFile, sourceSnapshot)
  • MCP source snapshot import through yayaw_component_import
  • MCP declarative recipe import through yayaw_component_recipe_import

Registry sync action:

  • syncRegistryAliasComponents(registryAlias, { onlyNew: true })

Non-blocking policy for new library releases:

  • registry discovery uses index payload candidates (index.json and registry.json)
  • items not yet vendored locally can still be imported through DB snapshot pipeline
  • allowlist validation remains enforced for source domains

MCP component tools expose the same DB-backed catalog boundary:

  • yayaw_components_list and yayaw_component_get inspect component inventory; set includeSchemas=true on the list call to receive prop/API/harness schemas, runtime capabilities, usage hints, and ready-to-use component references
  • yayaw_component_import stores and compiles a source snapshot
  • yayaw_component_recipe_import validates and stores a declarative UiComponentRecipeV1 as draft_ready without executing supplied code
  • yayaw_component_recompile recompiles a stored snapshot
  • yayaw_component_publish publishes a compiled smoke-renderable revision
  • yayaw_component_delete soft-deletes an imported entry

These tools do not mutate repository-local source files in production. If an agent needs to add or modify first-party React component files, that is a code change and should happen through a branch and pull request.

Declarative imports are restricted to first-party recipe kinds implemented by the checked-in runtime. Their complete payload, defaults, fixtures, and recursive prop value shapes must be bounded JSON. Import creates a draft-ready revision; publication remains an explicit separate call. For contextual design_system_extension, import and publication alone do not prove use: the final plan must reference the exact published component revision from a newly created composition section. A component import carrying designContextId is accepted only after the context has an explicit selected prototype. The compiler writes that context ID onto the immutable recipe revision; catalog inventory reads provenance from the exact published revision rather than mutable registry metadata. Recompiling or publishing another revision therefore does not inherit provenance accidentally. For creative-composition, the generic fixture allowance does not apply: fixtures must be empty, while apiSchema and harnessSchema must be omitted or empty. An exact slug@revision runtime reference is accepted only after that revision has been published at least once. Immutable publishedAt provenance keeps an older approved reference renderable after the catalog pointer advances, while a compiled draft cannot be injected into a generic page.

creative-composition recipes can express bounded art-directed topology: custom grid ratios, responsive order and spans, canvas overlap, sticky narratives with a linear mobile fallback, focal image crops and masks, display type roles, decorative shapes, an explicit email upload fallback, and purposeful motion with a runtime-owned reduced-motion state. Form and native upload nodes are fail-closed behind a checked-in registry of exact public CMS handlers. The registry contains one multipart handler: POST /api/cms/forms/personalization-request. Its required controls are email (email), firstName (text), consent (checkbox), and photo (native images upload); birthDate (date) and message (textarea) are optional. Names, types, and required flags must match this literal contract. The photo must be JPEG, PNG, or WebP and no larger than 8 MB. The server auto-rotates and re-encodes it without source metadata, re-inspects the canonical bytes, and stores only that canonical image in private submission storage rather than promoting it into CMS media. Upload and deletion states are durable database ledger states; non-ready and expired requests never appear in operator reads. This endpoint creates a personalization request for later operator follow-up. It is not a checkout, order-confirmation, or payment endpoint.

honest_form_controls additionally requires fields and a submit control inside that registered actionable form; file_upload requires its exact multipart POST contract. Arbitrary same-origin actions remain rejected. For every other handler or action, only email-fallback honestly represents file collection. Admin operators process private submissions with yayaw_cms_personalization_requests_list, yayaw_cms_personalization_request_get, yayaw_cms_personalization_request_status_update, yayaw_cms_personalization_request_delete, and yayaw_cms_personalization_requests_prune. The prune operation supports dry-run and removes requests after their 180-day retention deadline. The long-lived Page AI worker runs bounded maintenance independently of queued AI runs, recovering interrupted upload/deletion objects and enforcing retention; a small best-effort prune after successful submission only supplements that durable worker path. A display type capability requires an allowlisted display style plus an explicit family role (brand-sans, brand-serif, or brand-mono) that maps to the active design-token font family. Surface props use stable semantic roles such as background, foreground, card, muted, primary, accent, and border; they resolve through the active theme rather than promise a literal color. Older color-like surface names remain compatibility aliases and do not prove semantic_surfaces. Functional props also carry bounded value types and required fields, so a string such as "true" cannot stand in for a boolean runtime behavior. The server infers creativeCapabilities only from a valid exact declarative tree and rejects a supplied manifest that claims primitives the tree does not implement. A selected prototype declares requiredCapabilities; before page persistence, each mapped region must use the exact published recipe revision whose server-derived structural and implementation manifests match the canonical topology and prop names/schema/tree registered for that region. That exact revision must be the mapped section's published root component, and its resolved instance bindings must match the registered render manifest. new_recipe requires a same-context created revision and cannot reuse a compositionHash topology already present on a published creative-composition in the prepared inventory; a new ID, slug, or revision does not make a clone new. reuse_published requires its declared published component. A nested occurrence of the promised component does not satisfy the contract.

The checked-in runtime also exposes these bounded fidelity primitives through yayaw_components_list(includeSchemas=true):

  • canvas.height accepts content for content-owned height and band for a compact stage; canvas.mobileGap controls the bounded gap when children stack on mobile
  • image.aspect, image.mobileAspect, and image.desktopAspect accept auto, cinema, landscape, panorama, portrait, square, strip, tall, or video; image.height accepts auto, band, panel, or hero
  • decoration.treatment accepts solid, outline, or wash; wash renders a layered pigment-like accent and does not replace required imagery
  • frame.material accepts clean, paper, deckle, or taped; frame.rotation and placement.rotation accept the bounded values -6, -3, 0, 3, or 6, and text.style: "note" provides a handwritten-note treatment
  • text.weight accepts normal, medium, semibold, bold, or black
  • visibility.show accepts all, mobile, or desktop, while disclosure renders a bounded semantic details/summary control with required label and optional align, appearance: "bare" | "framed", and surface

These nodes infer compact_band, responsive_media, watercolor_wash, paper_collage, typographic_weight, responsive_recomposition, and responsive_navigation only from material behavior: compact_band requires height: "band", responsive_media requires distinct non-auto mobile and desktop aspects without a fixed height, and responsive_navigation requires a mobile disclosure plus a non-empty desktop branch. Prototype planning must keep a per-region feasibility ledger. Responsive order props (desktopOrder or mobileOrder) require a direct grid or stack parent; responsive span props (desktopSpan or mobileSpan) require a direct grid parent. A qualifying ancestor is not sufficient. Every region declares exact allowlisted nodes, canonical paths (tree, then tree.children.N recursively), bounded props, the exact declarative recipePropSchema, an equal-key recipePropNames list, and the exact recipePropBindings for its intended section instance. Required props without defaults need bindings, binding keys must belong to the schema, and every image.src must be exactly $props.<identifier> with that prop bound as media_asset or localized_media_asset to durable prepared-inventory media. mediaAssetIds must be the exact deduplicated set of asset IDs used by all media bindings. The server rebuilds and validates the recipe through the canonical runtime contract. It computes compositionHash from node names, canonical paths, and structural props only, excluding content, media, actions, surface/tone palette choices, and other non-structural props while normalizing runtime references. It separately computes implementationHash from the prop names/schema/tree and renderManifestHash from that implementation plus its bindings. It compares all server-owned manifests with the exact published root recipe instance used by the mapped section. A different tree with the same coarse capabilities, a nested substitute, or changed bindings fails. A global capability union cannot hide an infeasible region. For design_system_extension, every A/B/C candidate must visibly map at least one region to new_recipe. Prototype-visible text, links, labels, placeholders, alternative text, and upload messages must each be a direct $props.<identifier> sink. Text-like bindings are exact localized literals for every prepared locale; links are bounded safe literal or localized targets. Prototype-critical global-data bindings are rejected rather than treated as frozen. repeat, $item, and $index are also rejected: visible repeated content must be expanded into explicit planned nodes so the registered topology and bindings match the reviewed runtime. Prototype registration also requires the complete A/B, A/C, and B/C pairwise comparison set, with exact artifact-region evidence for both candidates in every stable region. Ordered regional compositionHash values form the structural signature. The normalized page-shell manifest is pinned separately and must match the selected candidate exactly in the plan and runtime; it is not part of the candidate-distinctness signature. Ordered regional implementationHash and renderManifestHash values form the implementation and render signatures. All three A/B/C signatures of every kind must differ. Content, media, palette, schema-default, or binding changes cannot make unchanged topology a separate direction. A server-computed normalized 16 × 16 grayscale edge-matrix fingerprint additionally rejects cloned or insufficiently different full boards. It is only a coarse full-board structure guard, never sufficient proof of a distinct direction or fidelity, and supplements the three signatures, pairwise critique, visual inspection, and substantive Visual QA.

Exact-runtime prototype registration uses kind: "generated_set_v2". Before registration, every story or recipe image used by these bindings must be finalized with image MIME, dimensions, and SHA-256 provenance, then admitted by the single pre-selection media-only inventory refresh. Board assets and post-registration runtime screenshots are queried directly by asset ID and must not trigger another inventory refresh. Each candidate supplies an exact page shell (layout plus bounded page designTokens or null) and integer pixel artifactBounds for every visible region. A region is at least 200 × 120 pixels and 2% of board area, all regions together cover at least 40%, and pairwise overlap is at most 50% of the smaller region. Page-scoped tokens must be null outside global page scope. For every candidate pair, at least one matching region fingerprint must be materially distinct; board chrome alone cannot distinguish a direction.

Clients provide planned recipe trees, schemas, bindings, and bounds, but never the derived composition, implementation, render, page-shell, fingerprint, or runtime hashes. The server computes those fields and resolves each candidate through the canonical page and recipe runtime. Registration returns signed runtime previews and a per-candidate runtimeHash. Visual QA compares each board with every prepared locale in desktop/mobile and light/dark, uploads each distinct full-page capture with the exact returned context, runtime hash, and role, then calls yayaw_cms_prototype_runtime_review_complete. The visual_qa value is a logical role on the current actor, not proof of an independently authenticated reviewer. Boards and runtime previews remain separate visual artifacts: the manifests and hashes detect substitution or drift, but do not prove pixel equivalence.

Production runtime resolution fails closed without an immutable fingerprint from NEXT_DEPLOYMENT_ID, resolved gitCommitSha such as DEPLOYMENT_GIT_COMMIT_SHA, or resolved deploymentId; "local" is a non-production fallback only. The deployment fingerprint and renderer contract version enter runtimeHash, so a renderer build change invalidates earlier review.

Selection is blocked until this runtime review is current. A renderer, deployment, recipe, media, screenshot, or token change can stale it. A design-token change after registration requires a new prepared context and three fresh candidates. After selection, exact recipe revisions can be imported and published, followed by one same-token inventory refresh before the strict version-2 plan. That plan reproduces the selected candidate's exact page-shell layout and page-scoped token layer.

Production enables v2 registration only after the reader-first rollout: CMS_PROTOTYPE_RUNTIME_V2_WRITES_ENABLED defaults off in production, stays false while the compatible reader build replaces and drains every pre-reader instance, and is set to true only after that build becomes the rollback floor. The separate v2 field does not make a mixed fleet safe. Once any v2 context exists, never roll back to a pre-reader build; roll forward until those contexts are retired or migrated. Legacy registration remains readable and writable during the reader-first phase for transition compatibility, but a new exact workflow must not downgrade to it. The disabled writer returns cms_design_prototype_runtime_rollout_pending. Local and other non-production environments default v2 writes on when the variable is absent.

Block Planner Usage Hints

The block AI planner consumes catalog usage hints when available:

  • short example snippet from CLI/persisted context
  • key registry dependencies
  • docs/examples links

This metadata is read-only and passed in prompt payload to improve component and prop selection in generated block trees. It does not execute any DB source code.

CLI-First Context Enrichment

Imports now resolve context from the official shadcn CLI first, then apply deterministic fallbacks.

Implementation points:

  • src/lib/server/actions/components/component-registry-actions.ts
  • src/lib/server/services/components/ui-component-shadcn-cli-context.ts
  • src/lib/server/services/components/shadcn-cli-runner.ts
  • src/lib/server/services/components/ui-component-recipe-import-enrichment.ts
  • src/lib/server/services/components/ui-component-import-preview-evidence.ts

Behavior:

  • CLI runner is local-first (bunx shadcn) with npx -y shadcn@latest fallback, timeout, retry, and structured logs
  • resolver aggregates:
    • shadcn view payload metadata/files
    • shadcn docs --json links for core shadcn components
    • shadcn search + shadcn view for registry example blocks
  • resolved context includes:
    • sourceTitle / sourceDescription
    • exampleSnippets
    • registryDependencies
    • docs links (docs, examples)
    • interaction hint (click-trigger, selection, or inline fallback)
  • preview evidence priority is deterministic: CLI -> payload -> web -> source snapshot
  • context is cached in import metadata by sourceHash (contextVersion, links, snippets, dependencies), so repeated imports avoid repeated CLI calls
  • when CLI context is unavailable, import remains non-blocking and compile/publish gating behavior is unchanged
  • no external source code is executed; only metadata and snapshots are parsed

Interaction-first policy:

  • overlay-like previews should prefer explicit click triggers over always-open states
  • composite AI blueprints are neutral by default (no AI/chat-specific microcopy unless source evidence explicitly requires it)

Design Token Behavior

The catalog preview does not rely only on baseline CSS.

It resolves effective runtime tokens for the current session (global + organization overrides) and applies scoped CSS variables to the preview surface. This lets dashboard users validate UI components against the same semantic token values used at runtime.

Validation

Registry consistency is validated by tests:

  • unique entry ids
  • source file existence
  • demoId references resolved by the demo map

Runtime/catalog guards:

  • no DB code execution (source snapshots are never evaluated)
  • publication blocked when compile diagnostics or smoke render fails
  • design-system extensions import new recipes only after prototype selection with the active designContextId; the refreshed inventory carries provenance from the exact published immutable revision
  • created_component_not_new blocks a component that already existed in the initial prepared baseline, while created_component_context_mismatch blocks a component imported by another workflow
  • contextual design reports created_component_not_used when a component declared as newly created is absent from every new composition, including when only a different revision of the same slug is referenced

Useful commands:

bun test src/components/ui/catalog/registry.test.ts
bun test src/lib/server/services/components/ui-component-recipe-compiler.test.ts
bun test src/lib/server/services/components/ui-component-catalog-query.test.ts
bun run components:generate-ai-elements-previews

Environment Variables

  • OPENAI_API_KEY
  • OPENAI_COMPONENTS_AI_FALLBACK

These OpenAI variables are optional and only affect AI SDK enrichment for imported components. Local AI Elements preview overrides are fully deterministic and do not require OPENAI_API_KEY.

Contract Direction

  • global product scope
  • source snapshot storage for audit/replay
  • no runtime execution of DB-stored code

Reference type:

  • UiComponentRegistryItemV1 in src/components/ui/catalog/types.ts

DB tables:

  • ui_component_registry_items
  • ui_component_recipe_revisions
  • ui_component_publications