/** * Section registry types — the contract for a layout section. * * A section is what an admin drops onto the canvas in `/admin/pages`. * Each section type ships: * - metadata (name, icon, category, description) * - a Zod schema for its config — drives the auto-generated form * (`docs/plans/layout-and-pages.md` §7.10) * - a default config for "just added" state * - colSpan bounds (resize is clamped to `[minColSpan, maxColSpan]`) * - a Vue renderer * - optional config-schema migrations (lazy on read) * * Registries are LOADED THE SAME on server and client — they're a config * + import map. Built-in sections register at layer startup; future * code-registered sections (plan §3.1, Phase 9) register via the thin * app's `commonpub.config.ts` `sections:` array. * * This module is pure TypeScript + types; the Vue runtime registry that * holds the actual `Component` refs lives in `layers/base/sections/registry.ts` * (next session, Phase 1 continuation). */ import type { Component } from 'vue'; import type { ZodType } from 'zod'; /** Section category — drives palette grouping. */ export type SectionCategory = 'layout' | 'content' | 'data' | 'interactive' | 'editorial' | 'embed' | 'custom'; /** Lifecycle / visibility tag — orthogonal to category. */ export type SectionStatus = 'stable' | 'beta' | 'deprecated'; /** * Full section definition. TConfig is the type of the section's config * blob — Zod schema validates it, the renderer consumes it. */ export interface SectionDefinition = Record> { /** Unique slug. Used in `LayoutSection.type` + routes. Must be kebab-case. */ type: string; /** Display name shown in the palette + inspector. */ name: string; /** One-line description shown under the name. */ description: string; /** Font Awesome icon class (e.g. `'fa-image'`). */ icon: string; /** Category for palette grouping. */ category: SectionCategory; /** Lifecycle flag. Beta sections show a badge; deprecated ones warn on add. */ status?: SectionStatus; /** Zod schema for the section's `config` blob. Drives auto-form generation. */ configSchema: ZodType; /** Default config when the section is first dropped onto the canvas. */ defaultConfig: TConfig; /** Current schema version. Bump when configSchema breaks. */ schemaVersion: number; /** * Renderer Vue component. Default contract: receives * `{ config: TConfig; meta: SectionRenderMeta }`. Override the prop * shape via `propMap` (below) when pointing at an existing reusable * component (e.g. a Block*View or a homepage *Section.vue) that has * its own established prop contract. */ component: Component; /** * Optional prop transform — maps the standard `{config, meta}` shape * to whatever the target `component` actually expects. Use this when * reusing an existing component (Block*View takes `{content}`; * HeroSection.vue takes `{config: HomepageSectionConfig}`; etc.) so * we don't write redundant Section*.vue adapters. * * Default: identity — passes `{config, meta}` unchanged. * * Example: * component: BlockHeadingView, * propMap: ({ config }) => ({ content: config }), * * Lesson from session 159: layout engine = arranger for existing * components. See `feedback-reuse-existing-components` memory + * `docs/plans/stage-e-unification.md`. * * Type note: NOT tied to TConfig — most propMaps just route config * without caring about its specific shape, and tying to TConfig * makes SectionDefinition incompatible with spread+override patterns * (test fixtures pulling a base def into a different TConfig). * configSchema validates the shape at runtime. */ propMap?: (props: { config: Record; meta: SectionRenderMeta; }) => Record; /** Optional migrations: oldVersion → newConfig. */ migrations?: Record) => TConfig>; /** Feature flag that must be ON for this section to appear in the palette. */ featureGate?: string; /** Which zones this section is allowed in. Default: all zones the page declares. */ allowedZones?: string[]; /** Roles that can ADD this section in the editor (different from runtime visibility). */ addRoles?: ('staff' | 'admin')[]; /** Minimum colSpan this section's content tolerates (resize floor). */ minColSpan: number; /** Maximum colSpan. Almost always 12. Set lower if the section breaks at full width. */ maxColSpan: number; /** Initial colSpan when dropped into a row. Must satisfy [minColSpan, maxColSpan]. */ defaultColSpan: number; /** Whether the section can be resized. False = always `defaultColSpan`. */ resizable: boolean; /** Preview screenshot URL — shown in the palette tile if present. */ previewImage?: string; } /** Props the registered Vue component receives on render. */ export interface SectionRenderProps = Record> { config: TConfig; meta: SectionRenderMeta; } /** Context passed to a section renderer at runtime. */ export interface SectionRenderMeta { /** The route this layout is for ('/', '/about', etc.) — useful for canonical links. */ route: string; /** Zone slug the section is being rendered in. */ zone: string; /** Whether this render is happening inside the editor preview (skip side effects). */ isPreview: boolean; /** Resolved colSpan for the current viewport. */ effectiveColSpan: number; /** Section's stable id — useful for analytics + a11y labelling. */ sectionId: string; } /** * In-memory section registry. The layer registers built-in sections at * startup; thin apps register their own via `commonpub.config.ts` → * registered on the Nuxt plugin load. * * A new registry instance is created per process (server + client each * have their own); for SSR/hydration parity, the SAME registration code * runs on both sides. */ export declare class SectionRegistry { private entries; /** Register a section. Throws on type collision (registration is fail-fast). */ register>(def: SectionDefinition): void; /** Get a section definition by type slug, or null if not registered. */ get(type: string): SectionDefinition | null; /** Whether a section type is registered. */ has(type: string): boolean; /** List all registered section definitions. */ list(): SectionDefinition[]; /** Group registered sections by category, for palette rendering. */ byCategory(): Record; /** Clear all registrations — test-only helper. */ clear(): void; /** Snapshot the registry for serialisation (e.g. /api/sections endpoint). */ snapshot(): Array>; } /** * Resolve a section's `colSpan` for the current viewport, honouring the * fallback chain: `lg ↦ md ↦ sm ↦ base`. Mobile default is 12 (rows stack). */ export declare function resolveColSpan(baseColSpan: number, responsive: { sm?: number; md?: number; lg?: number; } | undefined, viewport: 'sm' | 'md' | 'lg'): number; /** * Apply per-type config migrations lazily on read. Walks the chain from * the section's stored `schemaVersion` to the registry's current version, * applying each step. Returns the migrated config + the new version. * * If a migration step is missing, returns the original config + a warning * (the caller — typically `` — surfaces an admin-only placeholder). */ export declare function migrateSectionConfig>(def: SectionDefinition, storedConfig: Record, storedVersion: number): { config: TConfig; version: number; warning?: string; }; //# sourceMappingURL=sections.d.ts.map