@uniweb/runtime 0.6.25 → 0.6.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ssr.js CHANGED
@@ -280,7 +280,7 @@ function renderBackground(background) {
280
280
  "aria-hidden": "true"
281
281
  }, ...children);
282
282
  }
283
- function renderBlock(block, { pure = false, as = void 0 } = {}) {
283
+ function renderBlock(block, { as = "section" } = {}) {
284
284
  const Component = block.initComponent();
285
285
  if (!Component) {
286
286
  return React.createElement("div", {
@@ -306,7 +306,7 @@ function renderBlock(block, { pure = false, as = void 0 } = {}) {
306
306
  }
307
307
  }
308
308
  const componentProps = { content, params, block };
309
- if (pure) {
309
+ if (!as) {
310
310
  return React.createElement(Component, componentProps);
311
311
  }
312
312
  const { background, ...wrapperProps } = getWrapperProps(block);
@@ -316,7 +316,7 @@ function renderBlock(block, { pure = false, as = void 0 } = {}) {
316
316
  }
317
317
  const hasBackground = background?.mode && meta?.background !== "self";
318
318
  block.hasBackground = hasBackground;
319
- const wrapperTag = as !== void 0 && as !== "section" ? as : Component.as || "section";
319
+ const wrapperTag = as !== "section" ? as : Component.as || "section";
320
320
  if (hasBackground) {
321
321
  return React.createElement(
322
322
  wrapperTag,
@@ -389,13 +389,13 @@ function initPrerender(content, foundation, options = {}) {
389
389
  if (content.config?.base && uniweb.activeWebsite?.setBasePath) {
390
390
  uniweb.activeWebsite.setBasePath(content.config.base);
391
391
  }
392
- uniweb.childBlockRenderer = function InlineChildBlocks({ blocks, from, pure = false, as = "div" }) {
392
+ uniweb.childBlockRenderer = function InlineChildBlocks({ blocks, from, wrapAs }) {
393
393
  const blockList = blocks || from?.childBlocks || [];
394
394
  return blockList.map(
395
395
  (childBlock, index) => React.createElement(
396
396
  React.Fragment,
397
397
  { key: childBlock.id || index },
398
- renderBlock(childBlock, { pure, as })
398
+ renderBlock(childBlock, { as: wrapAs || null })
399
399
  )
400
400
  );
401
401
  };
package/dist/ssr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ssr.js","sources":["../src/prepare-props.js","../src/default-404.js","../src/ssr-renderer.js"],"sourcesContent":["/**\n * Props Preparation for Runtime Guarantees\n *\n * Prepares props for foundation components with:\n * - Param defaults from runtime schema\n * - Guaranteed content structure (no null checks needed)\n *\n * This enables simpler component code by ensuring predictable prop shapes.\n */\n\n/**\n * Guarantee item has flat content structure\n *\n * @param {Object} item - Raw item from parser\n * @returns {Object} Item with guaranteed flat structure\n */\nfunction guaranteeItemStructure(item) {\n return {\n title: item.title || '',\n pretitle: item.pretitle || '',\n subtitle: item.subtitle || '',\n paragraphs: item.paragraphs || [],\n links: item.links || [],\n images: item.images || [],\n lists: item.lists || [],\n icons: item.icons || [],\n videos: item.videos || [],\n snippets: item.snippets || [],\n buttons: item.buttons || [],\n data: item.data || {},\n cards: item.cards || [],\n documents: item.documents || [],\n forms: item.forms || [],\n quotes: item.quotes || [],\n headings: item.headings || [],\n }\n}\n\n/**\n * Guarantee content structure exists\n * Returns a flat content object with all standard fields guaranteed to exist\n *\n * @param {Object} parsedContent - Raw parsed content from semantic parser (flat structure)\n * @returns {Object} Content with guaranteed flat structure\n */\nexport function guaranteeContentStructure(parsedContent) {\n const content = parsedContent || {}\n\n return {\n // Flat header fields\n title: content.title || '',\n pretitle: content.pretitle || '',\n subtitle: content.subtitle || '',\n alignment: content.alignment || null,\n\n // Flat body fields\n paragraphs: content.paragraphs || [],\n links: content.links || [],\n images: content.images || [],\n lists: content.lists || [],\n icons: content.icons || [],\n videos: content.videos || [],\n insets: content.insets || [],\n snippets: content.snippets || [],\n buttons: content.buttons || [],\n data: content.data || {},\n cards: content.cards || [],\n documents: content.documents || [],\n forms: content.forms || [],\n quotes: content.quotes || [],\n headings: content.headings || [],\n\n // Items with guaranteed structure\n items: (content.items || []).map(guaranteeItemStructure),\n\n // Sequence for ordered rendering\n sequence: content.sequence || [],\n\n // Preserve raw content if present\n raw: content.raw,\n }\n}\n\n/**\n * Apply a schema to a single object\n * Only processes fields defined in the schema, preserves unknown fields\n *\n * @param {Object} obj - The object to process\n * @param {Object} schema - Schema definition (fieldName -> fieldDef)\n * @returns {Object} Object with schema defaults applied\n */\nfunction applySchemaToObject(obj, schema) {\n if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {\n return obj\n }\n\n const result = { ...obj }\n\n for (const [field, fieldDef] of Object.entries(schema)) {\n // Get the default value - handle both shorthand and full form\n const defaultValue = typeof fieldDef === 'object' ? fieldDef.default : undefined\n\n // Apply default if field is missing and default exists\n if (result[field] === undefined && defaultValue !== undefined) {\n result[field] = defaultValue\n }\n\n // For select fields with options, apply default if value is not among valid options\n if (typeof fieldDef === 'object' && fieldDef.options && Array.isArray(fieldDef.options)) {\n if (result[field] !== undefined && !fieldDef.options.includes(result[field])) {\n // Value exists but is not valid - apply default if available\n if (defaultValue !== undefined) {\n result[field] = defaultValue\n }\n }\n }\n\n // Handle nested object schema\n if (typeof fieldDef === 'object' && fieldDef.type === 'object' && fieldDef.schema && result[field]) {\n result[field] = applySchemaToObject(result[field], fieldDef.schema)\n }\n\n // Handle array with inline schema\n if (typeof fieldDef === 'object' && fieldDef.type === 'array' && fieldDef.of && result[field]) {\n if (typeof fieldDef.of === 'object') {\n result[field] = result[field].map(item => applySchemaToObject(item, fieldDef.of))\n }\n }\n }\n\n return result\n}\n\n/**\n * Apply a schema to a value (object or array of objects)\n *\n * @param {Object|Array} value - The value to process\n * @param {Object} schema - Schema definition\n * @returns {Object|Array} Value with schema defaults applied\n */\nfunction applySchemaToValue(value, schema) {\n if (Array.isArray(value)) {\n return value.map(item => applySchemaToObject(item, schema))\n }\n return applySchemaToObject(value, schema)\n}\n\n/**\n * Apply schemas to content.data\n * Only processes tags that have a matching schema, leaves others untouched\n *\n * @param {Object} data - The data object from content\n * @param {Object} schemas - Schema definitions from runtime meta\n * @returns {Object} Data with schemas applied\n */\nexport function applySchemas(data, schemas) {\n if (!schemas || !data || typeof data !== 'object') {\n return data || {}\n }\n\n const result = { ...data }\n\n for (const [tag, rawValue] of Object.entries(data)) {\n const schema = schemas[tag]\n if (!schema) continue // No schema for this tag - leave as-is\n\n result[tag] = applySchemaToValue(rawValue, schema)\n }\n\n return result\n}\n\n/**\n * Apply param defaults from runtime schema\n *\n * @param {Object} params - Params from frontmatter\n * @param {Object} defaults - Default values from runtime schema\n * @returns {Object} Merged params with defaults applied\n */\nexport function applyDefaults(params, defaults) {\n if (!defaults || Object.keys(defaults).length === 0) {\n return params || {}\n }\n\n return {\n ...defaults,\n ...(params || {}),\n }\n}\n\n/**\n * Prepare props for a component with runtime guarantees\n *\n * @param {Object} block - The block instance\n * @param {Object} meta - Runtime metadata for the component (from meta[componentName])\n * @returns {Object} Prepared props: { content, params }\n */\nexport function prepareProps(block, meta) {\n // Apply param defaults\n const defaults = meta?.defaults || {}\n const params = applyDefaults(block.properties, defaults)\n\n // Guarantee content structure\n const content = guaranteeContentStructure(block.parsedContent)\n\n // Apply schemas to content.data\n const schemas = meta?.schemas || null\n if (schemas && content.data) {\n content.data = applySchemas(content.data, schemas)\n }\n\n return { content, params }\n}\n\n/**\n * Get runtime metadata for a component from the global uniweb instance\n *\n * @param {string} componentName\n * @returns {Object|null}\n */\nexport function getComponentMeta(componentName) {\n return globalThis.uniweb?.getComponentMeta?.(componentName) || null\n}\n\n/**\n * Get default param values for a component\n *\n * @param {string} componentName\n * @returns {Object}\n */\nexport function getComponentDefaults(componentName) {\n return globalThis.uniweb?.getComponentDefaults?.(componentName) || {}\n}\n","/**\n * Default 404 Page Content\n *\n * Single source of truth for the fallback 404 page shown when a site\n * has no custom 404 page defined. Used by:\n * - PageRenderer.jsx (client-side, as React elements)\n * - ssr-renderer.js generate404Html (build-time, as HTML string)\n *\n * The wrapper uses min-height + flex centering so the 404 content\n * renders at the same position regardless of parent layout context.\n * This prevents a visible flash when React hydrates over the SSR content.\n */\n\nimport React from 'react'\n\nconst styles = {\n wrapper: {\n minHeight: '80vh',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '2rem',\n textAlign: 'center',\n },\n heading: { fontSize: '3rem', fontWeight: 'bold', color: '#1f2937', marginBottom: '1rem' },\n message: { color: '#64748b', marginBottom: '2rem' },\n link: { color: '#3b82f6', textDecoration: 'underline' },\n}\n\n/**\n * React element for client-side rendering (PageRenderer).\n * Reads basePath from the runtime so the homepage link works\n * in subdirectory deployments (e.g., /sites/testproject).\n */\nexport function Default404() {\n const basePath = globalThis.uniweb?.activeWebsite?.basePath || ''\n const homeHref = basePath ? `${basePath}/` : '/'\n return React.createElement('div', { className: 'page-not-found', style: styles.wrapper },\n React.createElement('h1', { style: styles.heading }, '404'),\n React.createElement('p', { style: styles.message }, 'Page not found'),\n React.createElement('a', { href: homeHref, style: styles.link }, 'Go to homepage')\n )\n}\n\n/**\n * Static HTML string for SSR injection (generate404Html).\n *\n * @param {string} [basePath] - Base path prefix for the homepage link (e.g., '/sites/testproject')\n */\nexport function default404Html(basePath = '') {\n const homeHref = basePath ? `${basePath}/` : '/'\n return (\n `<div class=\"page-not-found\" style=\"min-height:80vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center\">` +\n `<h1 style=\"font-size:3rem;font-weight:bold;color:#1f2937;margin-bottom:1rem\">404</h1>` +\n `<p style=\"color:#64748b;margin-bottom:2rem\">Page not found</p>` +\n `<a href=\"${homeHref}\" style=\"color:#3b82f6;text-decoration:underline\">Go to homepage</a>` +\n `</div>`\n )\n}\n","/**\n * SSR Renderer\n *\n * Hook-free rendering pipeline for SSG (build) and cloud SSR (unicloud).\n * Mirrors BlockRenderer.jsx + Background.jsx using React.createElement\n * directly — no hooks, no JSX, no browser APIs.\n *\n * This is the single source of truth for how blocks render during prerender.\n * When modifying BlockRenderer.jsx or Background.jsx, update this file to match.\n *\n * Exports three layers:\n * 1. Rendering functions (renderBlock, renderBlocks, renderLayout, renderBackground)\n * 2. Initialization (initPrerender, prefetchIcons)\n * 3. Per-page rendering (renderPage, classifyRenderError, injectPageContent, escapeHtml)\n */\n\nimport React from 'react'\nimport { renderToString } from 'react-dom/server'\nimport { createUniweb } from '@uniweb/core'\nimport { buildSectionOverrides } from '@uniweb/theming'\nimport { prepareProps, getComponentMeta } from './prepare-props.js'\nimport { default404Html } from './default-404.js'\n\n// ============================================================================\n// Layer 1: Rendering functions\n// ============================================================================\n\n/**\n * Valid color contexts for section theming\n */\nconst VALID_CONTEXTS = ['light', 'medium', 'dark']\n\n/**\n * Build wrapper props from block configuration.\n * Mirrors getWrapperProps in BlockRenderer.jsx.\n */\nexport function getWrapperProps(block) {\n const theme = block.themeName\n const blockClassName = block.state?.className || ''\n\n // Empty themeName = Auto → no context class → inherits tokens from :root\n // Non-empty = Pinned → context class sets tokens directly on the element\n let contextClass = ''\n if (theme && VALID_CONTEXTS.includes(theme)) {\n contextClass = `context-${theme}`\n }\n\n let className = contextClass\n if (blockClassName) {\n className = className ? `${className} ${blockClassName}` : blockClassName\n }\n\n const { background = {} } = block.standardOptions\n const style = {}\n\n // If background has content, ensure relative positioning and a stacking context\n // so the background's z-index stays contained within this section.\n if (background.mode) {\n style.position = 'relative'\n style.isolation = 'isolate'\n }\n\n // Apply context overrides as inline CSS custom properties\n if (block.contextOverrides) {\n for (const [key, value] of Object.entries(block.contextOverrides)) {\n style[`--${key}`] = value\n }\n }\n\n // Use stableId for DOM ID if available (stable across reordering)\n const sectionId = block.stableId || block.id\n\n return { id: `section-${sectionId}`, style, className, background }\n}\n\n/**\n * Convert hex/rgb color to rgba with opacity.\n * Mirrors withOpacity() in Background.jsx.\n */\nfunction withOpacity(color, opacity) {\n if (color.startsWith('#')) {\n const r = parseInt(color.slice(1, 3), 16)\n const g = parseInt(color.slice(3, 5), 16)\n const b = parseInt(color.slice(5, 7), 16)\n return `rgba(${r}, ${g}, ${b}, ${opacity})`\n }\n if (color.startsWith('rgb')) {\n const match = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/)\n if (match) {\n return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${opacity})`\n }\n }\n return color\n}\n\n/**\n * Resolve a URL against the site's base path.\n * Mirrors resolveUrl() in Background.jsx.\n */\nfunction resolveUrl(url) {\n if (!url || !url.startsWith('/')) return url\n const basePath = globalThis.uniweb?.activeWebsite?.basePath || ''\n if (!basePath) return url\n if (url.startsWith(basePath + '/') || url === basePath) return url\n return basePath + url\n}\n\n/**\n * Render a background element for SSR.\n * Mirrors Background.jsx (color, gradient, image — not video).\n * Video backgrounds require JS for autoplay and are skipped during SSR.\n */\nexport function renderBackground(background) {\n if (!background?.mode) return null\n\n const containerStyle = {\n position: 'absolute',\n inset: '0',\n overflow: 'hidden',\n zIndex: 0,\n }\n\n const children = []\n\n // Color background\n if (background.mode === 'color' && background.color) {\n children.push(\n React.createElement('div', {\n key: 'bg-color',\n className: 'background-color',\n style: { position: 'absolute', inset: '0', backgroundColor: background.color },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Gradient background (supports string or object with opacity)\n if (background.mode === 'gradient' && background.gradient) {\n const g = background.gradient\n\n let bgValue\n if (typeof g === 'string') {\n bgValue = g\n } else {\n const {\n start = 'transparent',\n end = 'transparent',\n angle = 0,\n startPosition = 0,\n endPosition = 100,\n startOpacity = 1,\n endOpacity = 1,\n } = g\n const startColor = startOpacity < 1 ? withOpacity(start, startOpacity) : start\n const endColor = endOpacity < 1 ? withOpacity(end, endOpacity) : end\n bgValue = `linear-gradient(${angle}deg, ${startColor} ${startPosition}%, ${endColor} ${endPosition}%)`\n }\n\n children.push(\n React.createElement('div', {\n key: 'bg-gradient',\n className: 'background-gradient',\n style: { position: 'absolute', inset: '0', background: bgValue },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Image background\n if (background.mode === 'image' && background.image?.src) {\n const img = background.image\n children.push(\n React.createElement('div', {\n key: 'bg-image',\n className: 'background-image',\n style: {\n position: 'absolute',\n inset: '0',\n backgroundImage: `url(${resolveUrl(img.src)})`,\n backgroundPosition: img.position || 'center',\n backgroundSize: img.size || 'cover',\n backgroundRepeat: 'no-repeat',\n },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Overlay (gradient or solid)\n if (background.overlay?.enabled) {\n const ov = background.overlay\n let overlayStyle\n\n if (ov.gradient) {\n const g = ov.gradient\n overlayStyle = {\n position: 'absolute', inset: '0', pointerEvents: 'none',\n background: `linear-gradient(${g.angle || 180}deg, ${g.start || 'rgba(0,0,0,0.7)'} ${g.startPosition || 0}%, ${g.end || 'rgba(0,0,0,0)'} ${g.endPosition || 100}%)`,\n opacity: ov.opacity ?? 0.5,\n }\n } else {\n const baseColor = ov.type === 'light' ? '255, 255, 255' : '0, 0, 0'\n overlayStyle = {\n position: 'absolute', inset: '0', pointerEvents: 'none',\n backgroundColor: `rgba(${baseColor}, ${ov.opacity ?? 0.5})`,\n }\n }\n\n children.push(\n React.createElement('div', {\n key: 'bg-overlay',\n className: ov.gradient ? 'background-overlay background-overlay--gradient' : 'background-overlay background-overlay--solid',\n style: overlayStyle,\n 'aria-hidden': 'true',\n })\n )\n }\n\n if (children.length === 0) return null\n\n return React.createElement('div', {\n className: `background background--${background.mode}`,\n style: containerStyle,\n 'aria-hidden': 'true',\n }, ...children)\n}\n\n/**\n * Render a single block for SSR.\n * Mirrors BlockRenderer.jsx but without hooks (no runtime data fetching).\n *\n * @param {Block} block - Block instance to render\n * @param {Object} [options]\n * @param {boolean} [options.pure=false] - Render component without section wrapper (used by ChildBlocks)\n * @returns {React.ReactElement}\n */\nexport function renderBlock(block, { pure = false, as = undefined } = {}) {\n const Component = block.initComponent()\n\n if (!Component) {\n return React.createElement('div', {\n className: 'block-error',\n style: { padding: '1rem', background: '#fef2f2', color: '#dc2626' },\n }, `Component not found: ${block.type}`)\n }\n\n // Build content and params with runtime guarantees\n const meta = getComponentMeta(block.type)\n const prepared = prepareProps(block, meta)\n const params = prepared.params\n const content = { ...prepared.content, ...block.properties }\n\n // Resolve inherited entity data (mirrors BlockRenderer.jsx)\n // EntityStore walks page/site hierarchy to find data matching meta.inheritData\n const entityStore = block.website?.entityStore\n if (entityStore) {\n const resolved = entityStore.resolve(block, meta)\n if (resolved.status === 'ready' && resolved.data) {\n const merged = { ...content.data }\n for (const key of Object.keys(resolved.data)) {\n if (merged[key] === undefined) {\n merged[key] = resolved.data[key]\n }\n }\n content.data = merged\n }\n }\n\n const componentProps = { content, params, block }\n\n // Pure mode: render component without section wrapper (used by ChildBlocks)\n if (pure) {\n return React.createElement(Component, componentProps)\n }\n\n // Background handling (mirrors BlockRenderer.jsx)\n const { background, ...wrapperProps } = getWrapperProps(block)\n\n // Merge Component.className (static classes declared on the component function)\n const componentClassName = Component.className\n if (componentClassName) {\n wrapperProps.className = wrapperProps.className\n ? `${wrapperProps.className} ${componentClassName}`\n : componentClassName\n }\n\n // Check if component handles its own background\n const hasBackground = background?.mode && meta?.background !== 'self'\n block.hasBackground = hasBackground\n\n // Use Component.as as the wrapper tag (default: 'section').\n // An explicit `as` prop (e.g. 'div' from ChildBlocks) overrides Component.as,\n // mirroring the BlockRenderer.jsx Wrapper resolution logic.\n const wrapperTag = (as !== undefined && as !== 'section') ? as : (Component.as || 'section')\n\n if (hasBackground) {\n return React.createElement(wrapperTag, wrapperProps,\n renderBackground(background),\n React.createElement('div', { style: { position: 'relative', zIndex: 10 } },\n React.createElement(Component, componentProps)\n )\n )\n }\n\n return React.createElement(wrapperTag, wrapperProps,\n React.createElement(Component, componentProps)\n )\n}\n\n/**\n * Render an array of blocks for SSR.\n */\nexport function renderBlocks(blocks) {\n if (!blocks || blocks.length === 0) return null\n return blocks.map((block, index) =>\n React.createElement(React.Fragment, { key: block.id || index },\n renderBlock(block)\n )\n )\n}\n\n/**\n * Render page layout for SSR.\n * Mirrors Layout.jsx but without hooks.\n */\nexport function renderLayout(page, website) {\n const layoutName = page.getLayoutName()\n const RemoteLayout = website.getRemoteLayout(layoutName)\n const layoutMeta = website.getLayoutMeta(layoutName)\n\n const bodyBlocks = page.getBodyBlocks()\n const areas = page.getLayoutAreas()\n\n const bodyElement = bodyBlocks ? renderBlocks(bodyBlocks) : null\n const areaElements = {}\n for (const [name, blocks] of Object.entries(areas)) {\n areaElements[name] = renderBlocks(blocks)\n }\n\n if (RemoteLayout) {\n const params = { ...(layoutMeta?.defaults || {}), ...(page.getLayoutParams() || {}) }\n return React.createElement(RemoteLayout, {\n page, website, params,\n body: bodyElement,\n ...areaElements,\n })\n }\n\n // Default layout\n return React.createElement(React.Fragment, null,\n areaElements.header && React.createElement('header', null, areaElements.header),\n bodyElement && React.createElement('main', null, bodyElement),\n areaElements.footer && React.createElement('footer', null, areaElements.footer)\n )\n}\n\n// ============================================================================\n// Layer 2: Initialization\n// ============================================================================\n\n/**\n * Create and configure the Uniweb runtime for prerendering.\n *\n * Handles the full initialization sequence in the correct order:\n * createUniweb → setFoundation → capabilities → layoutMeta → basePath → childBlockRenderer.\n *\n * Returns the configured uniweb instance. Consumers can add extras after:\n * - Build: pre-populate DataStore, load extensions\n * - Unicloud: (none needed — payload is complete)\n *\n * NOTE: Does NOT clone content. Cloning is the consumer's responsibility\n * (build modifies content before init; unicloud clones upfront).\n *\n * @param {Object} content - Site content JSON (pages, config, hierarchy)\n * @param {Object} foundation - Loaded foundation module\n * @param {Object} [options]\n * @param {function} [options.onProgress] - Progress callback\n * @returns {Object} Configured uniweb instance\n */\nexport function initPrerender(content, foundation, options = {}) {\n const { onProgress = () => {} } = options\n\n onProgress('Initializing runtime...')\n const uniweb = createUniweb(content)\n uniweb.setFoundation(foundation)\n\n // Set foundation capabilities (Layout, props, etc.)\n if (foundation.default?.capabilities) {\n uniweb.setFoundationConfig(foundation.default.capabilities)\n }\n\n // Attach layout metadata (areas, transitions, defaults)\n if (foundation.default?.layoutMeta && uniweb.foundationConfig) {\n uniweb.foundationConfig.layoutMeta = foundation.default.layoutMeta\n }\n\n // Set base path from site config for subdirectory deployments\n if (content.config?.base && uniweb.activeWebsite?.setBasePath) {\n uniweb.activeWebsite.setBasePath(content.config.base)\n }\n\n // Set childBlockRenderer so ChildBlocks/Visual/Render work during prerender.\n // Mirrors the client's ChildBlocks component in PageRenderer.jsx:\n // - default as='div' so nested blocks use <div> wrapper (not <section>)\n // matching the client and avoiding React hydration mismatch (error #418)\n uniweb.childBlockRenderer = function InlineChildBlocks({ blocks, from, pure = false, as = 'div' }) {\n const blockList = blocks || from?.childBlocks || []\n return blockList.map((childBlock, index) =>\n React.createElement(React.Fragment, { key: childBlock.id || index },\n renderBlock(childBlock, { pure, as })\n )\n )\n }\n\n return uniweb\n}\n\n/**\n * Pre-fetch icons from CDN and populate the Uniweb icon cache.\n * Stores the cache on siteContent._iconCache for embedding in HTML.\n *\n * @param {Object} siteContent - Site content JSON (mutated: _iconCache added)\n * @param {Object} uniweb - Configured uniweb instance\n * @param {function} [onProgress] - Progress callback\n */\nexport async function prefetchIcons(siteContent, uniweb, onProgress = () => {}) {\n const icons = siteContent.icons?.used || []\n if (icons.length === 0) return\n\n const cdnBase = siteContent.config?.icons?.cdnUrl || 'https://uniweb.github.io/icons'\n\n onProgress(`Fetching ${icons.length} icons for SSR...`)\n\n const results = await Promise.allSettled(\n icons.map(async (iconRef) => {\n const [family, name] = iconRef.split(':')\n const url = `${cdnBase}/${family}/${family}-${name}.svg`\n const response = await fetch(url)\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n const svg = await response.text()\n uniweb.iconCache.set(`${family}:${name}`, svg)\n })\n )\n\n const succeeded = results.filter(r => r.status === 'fulfilled').length\n const failed = results.filter(r => r.status === 'rejected').length\n if (failed > 0) {\n const msg = `Fetched ${succeeded}/${icons.length} icons (${failed} failed)`\n console.warn(`[prerender] ${msg}`)\n onProgress(` ${msg}`)\n }\n\n // Store icon cache on siteContent for embedding in HTML\n if (uniweb.iconCache.size > 0) {\n siteContent._iconCache = Object.fromEntries(uniweb.iconCache)\n }\n}\n\n// ============================================================================\n// Layer 3: Per-page rendering\n// ============================================================================\n\n/**\n * Classify an SSR rendering error.\n *\n * @param {Error} err\n * @returns {{ type: 'hooks'|'null-component'|'unknown', message: string }}\n */\nexport function classifyRenderError(err) {\n const msg = err.message || ''\n\n if (msg.includes('Invalid hook call') || msg.includes('useState') || msg.includes('useEffect')) {\n return {\n type: 'hooks',\n message: 'contains components with React hooks (renders client-side)',\n }\n }\n\n if (msg.includes('Element type is invalid') && msg.includes('null')) {\n return {\n type: 'null-component',\n message: 'a component resolved to null (often hook-related, renders client-side)',\n }\n }\n\n return {\n type: 'unknown',\n message: msg,\n }\n}\n\n/**\n * Render a single page to HTML.\n *\n * Handles the full per-page pipeline:\n * setActivePage → renderLayout → renderToString → error handling → section override CSS.\n *\n * @param {Page} page - Page instance to render\n * @param {Website} website - Website instance\n * @returns {{ renderedContent: string, sectionOverrideCSS: string } | { error: { type: string, message: string } }}\n */\nexport function renderPage(page, website) {\n website.setActivePage(page.route)\n\n const element = renderLayout(page, website)\n\n let renderedContent\n try {\n renderedContent = renderToString(element)\n } catch (err) {\n return { error: classifyRenderError(err) }\n }\n\n // Build per-page section override CSS (theme pinning, component vars)\n const appearance = website.themeData?.appearance\n const sectionOverrideCSS = buildSectionOverrides(page.getPageBlocks(), appearance)\n\n return { renderedContent, sectionOverrideCSS }\n}\n\n// ============================================================================\n// HTML injection\n// ============================================================================\n\n/**\n * Escape HTML special characters.\n */\nexport function escapeHtml(str) {\n if (!str) return ''\n return String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n}\n\n/**\n * Inject prerendered content into an HTML shell.\n *\n * Common operations shared by both build and cloud:\n * - Replace #root div with rendered HTML\n * - Update page title\n * - Add/update meta description\n * - Inject section override CSS\n *\n * Build layers its additional injections on top of this return value:\n * __SITE_CONTENT__ JSON, icon cache, theme CSS (build-specific).\n *\n * @param {string} html - HTML shell\n * @param {string} renderedContent - React renderToString output\n * @param {Object} page - Page data { title, description, route }\n * @param {Object} [options]\n * @param {string} [options.sectionOverrideCSS] - Per-page section override CSS\n * @returns {string} HTML with injected content\n */\nexport function injectPageContent(html, renderedContent, page, options = {}) {\n let result = html\n\n // Inject per-page section override CSS before </head>\n if (options.sectionOverrideCSS) {\n const overrideStyle = `<style id=\"uniweb-page-overrides\">\\n${options.sectionOverrideCSS}\\n</style>`\n result = result.replace('</head>', `${overrideStyle}\\n</head>`)\n }\n\n // Replace the empty root div with pre-rendered content\n result = result.replace(\n /<div id=\"root\">[\\s\\S]*?<\\/div>/,\n `<div id=\"root\">${renderedContent}</div>`\n )\n\n // Update page title (use getTitle() so isIndex pages inherit parent title)\n const pageTitle = page.getTitle?.() || page.title\n if (pageTitle) {\n result = result.replace(\n /<title>.*?<\\/title>/,\n `<title>${escapeHtml(pageTitle)}</title>`\n )\n }\n\n // Add/update meta description\n if (page.description) {\n const metaDesc = `<meta name=\"description\" content=\"${escapeHtml(page.description)}\">`\n if (result.includes('<meta name=\"description\"')) {\n result = result.replace(/<meta name=\"description\"[^>]*>/, metaDesc)\n } else {\n result = result.replace('</head>', `${metaDesc}\\n</head>`)\n }\n }\n\n return result\n}\n\n// ============================================================================\n// 404 fallback generation\n// ============================================================================\n\n/**\n * Generate 404.html content for static hosting fallback.\n *\n * Serves two purposes on static hosts (GitHub Pages, Cloudflare Pages, etc.):\n * 1. Real 404: pre-rendered custom 404 page content (or blank #root if none defined)\n * 2. Valid dynamic route (e.g. /blog/2): inline script clears #root so SPA renders fresh\n *\n * Flow: static host serves 404.html → inline script runs before React mounts →\n * - dynamic route: clears #root, React renders the page normally\n * - real 404: leaves #root with pre-rendered content, React re-renders same 404 page\n *\n * @param {Object} options\n * @param {string} options.baseHtml - Assembled HTML shell (with site content already injected)\n * @param {Object} options.website - Initialized Website instance (from initPrerender)\n * @param {Object} options.siteContent - Site content object (to find dynamic templates)\n * @returns {{ html: string, hasNotFoundPage: boolean }}\n */\nexport function generate404Html({ baseHtml, website, siteContent }) {\n // Extract patterns for routes that remain as dynamic templates (prerender: false)\n const dynamicTemplates = siteContent.pages?.filter((p) => p.isDynamic) || []\n const routePatterns = dynamicTemplates.map((p) => {\n // '/blog/:id' → /^\\/blog\\/[^\\/]+\\/?$/\n const escaped = p.route\n .replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/:[^/]+/g, '[^\\\\/]+')\n return `^${escaped}\\\\/?$`\n })\n\n let html = baseHtml\n\n // Pre-render the custom 404 page content into #root (if the site defines one),\n // otherwise inject a default 404 message so the page isn't blank before JS loads\n const notFoundPage = website.getNotFoundPage()\n if (notFoundPage) {\n const notFoundResult = renderPage(notFoundPage, website)\n if (notFoundResult && !notFoundResult.error) {\n html = injectPageContent(html, notFoundResult.renderedContent, notFoundPage, {\n sectionOverrideCSS: notFoundResult.sectionOverrideCSS,\n })\n }\n } else {\n const basePath = website.basePath || ''\n html = html.replace(\n /<div id=\"root\">[\\s\\S]*?<\\/div>/,\n `<div id=\"root\">${default404Html(basePath)}</div>`\n )\n }\n\n // Inject inline script: if path matches a dynamic route, clear #root before React mounts\n // so the SPA renders the correct page rather than the 404 content\n if (routePatterns.length > 0) {\n const patternList = routePatterns.map((p) => `/${p}/`).join(',')\n const dynamicScript =\n `<script>(function(){` +\n `var p=[${patternList}],r=window.location.pathname;` +\n `if(p.some(function(x){return x.test(r)})){` +\n `var el=document.getElementById('root');if(el)el.innerHTML='';` +\n `}})()</script>`\n html = html.replace('</body>', `${dynamicScript}\\n</body>`)\n }\n\n return { html, hasNotFoundPage: !!notFoundPage }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ssr.js","sources":["../src/prepare-props.js","../src/default-404.js","../src/ssr-renderer.js"],"sourcesContent":["/**\n * Props Preparation for Runtime Guarantees\n *\n * Prepares props for foundation components with:\n * - Param defaults from runtime schema\n * - Guaranteed content structure (no null checks needed)\n *\n * This enables simpler component code by ensuring predictable prop shapes.\n */\n\n/**\n * Guarantee item has flat content structure\n *\n * @param {Object} item - Raw item from parser\n * @returns {Object} Item with guaranteed flat structure\n */\nfunction guaranteeItemStructure(item) {\n return {\n title: item.title || '',\n pretitle: item.pretitle || '',\n subtitle: item.subtitle || '',\n paragraphs: item.paragraphs || [],\n links: item.links || [],\n images: item.images || [],\n lists: item.lists || [],\n icons: item.icons || [],\n videos: item.videos || [],\n snippets: item.snippets || [],\n buttons: item.buttons || [],\n data: item.data || {},\n cards: item.cards || [],\n documents: item.documents || [],\n forms: item.forms || [],\n quotes: item.quotes || [],\n headings: item.headings || [],\n }\n}\n\n/**\n * Guarantee content structure exists\n * Returns a flat content object with all standard fields guaranteed to exist\n *\n * @param {Object} parsedContent - Raw parsed content from semantic parser (flat structure)\n * @returns {Object} Content with guaranteed flat structure\n */\nexport function guaranteeContentStructure(parsedContent) {\n const content = parsedContent || {}\n\n return {\n // Flat header fields\n title: content.title || '',\n pretitle: content.pretitle || '',\n subtitle: content.subtitle || '',\n alignment: content.alignment || null,\n\n // Flat body fields\n paragraphs: content.paragraphs || [],\n links: content.links || [],\n images: content.images || [],\n lists: content.lists || [],\n icons: content.icons || [],\n videos: content.videos || [],\n insets: content.insets || [],\n snippets: content.snippets || [],\n buttons: content.buttons || [],\n data: content.data || {},\n cards: content.cards || [],\n documents: content.documents || [],\n forms: content.forms || [],\n quotes: content.quotes || [],\n headings: content.headings || [],\n\n // Items with guaranteed structure\n items: (content.items || []).map(guaranteeItemStructure),\n\n // Sequence for ordered rendering\n sequence: content.sequence || [],\n\n // Preserve raw content if present\n raw: content.raw,\n }\n}\n\n/**\n * Apply a schema to a single object\n * Only processes fields defined in the schema, preserves unknown fields\n *\n * @param {Object} obj - The object to process\n * @param {Object} schema - Schema definition (fieldName -> fieldDef)\n * @returns {Object} Object with schema defaults applied\n */\nfunction applySchemaToObject(obj, schema) {\n if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {\n return obj\n }\n\n const result = { ...obj }\n\n for (const [field, fieldDef] of Object.entries(schema)) {\n // Get the default value - handle both shorthand and full form\n const defaultValue = typeof fieldDef === 'object' ? fieldDef.default : undefined\n\n // Apply default if field is missing and default exists\n if (result[field] === undefined && defaultValue !== undefined) {\n result[field] = defaultValue\n }\n\n // For select fields with options, apply default if value is not among valid options\n if (typeof fieldDef === 'object' && fieldDef.options && Array.isArray(fieldDef.options)) {\n if (result[field] !== undefined && !fieldDef.options.includes(result[field])) {\n // Value exists but is not valid - apply default if available\n if (defaultValue !== undefined) {\n result[field] = defaultValue\n }\n }\n }\n\n // Handle nested object schema\n if (typeof fieldDef === 'object' && fieldDef.type === 'object' && fieldDef.schema && result[field]) {\n result[field] = applySchemaToObject(result[field], fieldDef.schema)\n }\n\n // Handle array with inline schema\n if (typeof fieldDef === 'object' && fieldDef.type === 'array' && fieldDef.of && result[field]) {\n if (typeof fieldDef.of === 'object') {\n result[field] = result[field].map(item => applySchemaToObject(item, fieldDef.of))\n }\n }\n }\n\n return result\n}\n\n/**\n * Apply a schema to a value (object or array of objects)\n *\n * @param {Object|Array} value - The value to process\n * @param {Object} schema - Schema definition\n * @returns {Object|Array} Value with schema defaults applied\n */\nfunction applySchemaToValue(value, schema) {\n if (Array.isArray(value)) {\n return value.map(item => applySchemaToObject(item, schema))\n }\n return applySchemaToObject(value, schema)\n}\n\n/**\n * Apply schemas to content.data\n * Only processes tags that have a matching schema, leaves others untouched\n *\n * @param {Object} data - The data object from content\n * @param {Object} schemas - Schema definitions from runtime meta\n * @returns {Object} Data with schemas applied\n */\nexport function applySchemas(data, schemas) {\n if (!schemas || !data || typeof data !== 'object') {\n return data || {}\n }\n\n const result = { ...data }\n\n for (const [tag, rawValue] of Object.entries(data)) {\n const schema = schemas[tag]\n if (!schema) continue // No schema for this tag - leave as-is\n\n result[tag] = applySchemaToValue(rawValue, schema)\n }\n\n return result\n}\n\n/**\n * Apply param defaults from runtime schema\n *\n * @param {Object} params - Params from frontmatter\n * @param {Object} defaults - Default values from runtime schema\n * @returns {Object} Merged params with defaults applied\n */\nexport function applyDefaults(params, defaults) {\n if (!defaults || Object.keys(defaults).length === 0) {\n return params || {}\n }\n\n return {\n ...defaults,\n ...(params || {}),\n }\n}\n\n/**\n * Prepare props for a component with runtime guarantees\n *\n * @param {Object} block - The block instance\n * @param {Object} meta - Runtime metadata for the component (from meta[componentName])\n * @returns {Object} Prepared props: { content, params }\n */\nexport function prepareProps(block, meta) {\n // Apply param defaults\n const defaults = meta?.defaults || {}\n const params = applyDefaults(block.properties, defaults)\n\n // Guarantee content structure\n const content = guaranteeContentStructure(block.parsedContent)\n\n // Apply schemas to content.data\n const schemas = meta?.schemas || null\n if (schemas && content.data) {\n content.data = applySchemas(content.data, schemas)\n }\n\n return { content, params }\n}\n\n/**\n * Get runtime metadata for a component from the global uniweb instance\n *\n * @param {string} componentName\n * @returns {Object|null}\n */\nexport function getComponentMeta(componentName) {\n return globalThis.uniweb?.getComponentMeta?.(componentName) || null\n}\n\n/**\n * Get default param values for a component\n *\n * @param {string} componentName\n * @returns {Object}\n */\nexport function getComponentDefaults(componentName) {\n return globalThis.uniweb?.getComponentDefaults?.(componentName) || {}\n}\n","/**\n * Default 404 Page Content\n *\n * Single source of truth for the fallback 404 page shown when a site\n * has no custom 404 page defined. Used by:\n * - PageRenderer.jsx (client-side, as React elements)\n * - ssr-renderer.js generate404Html (build-time, as HTML string)\n *\n * The wrapper uses min-height + flex centering so the 404 content\n * renders at the same position regardless of parent layout context.\n * This prevents a visible flash when React hydrates over the SSR content.\n */\n\nimport React from 'react'\n\nconst styles = {\n wrapper: {\n minHeight: '80vh',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '2rem',\n textAlign: 'center',\n },\n heading: { fontSize: '3rem', fontWeight: 'bold', color: '#1f2937', marginBottom: '1rem' },\n message: { color: '#64748b', marginBottom: '2rem' },\n link: { color: '#3b82f6', textDecoration: 'underline' },\n}\n\n/**\n * React element for client-side rendering (PageRenderer).\n * Reads basePath from the runtime so the homepage link works\n * in subdirectory deployments (e.g., /sites/testproject).\n */\nexport function Default404() {\n const basePath = globalThis.uniweb?.activeWebsite?.basePath || ''\n const homeHref = basePath ? `${basePath}/` : '/'\n return React.createElement('div', { className: 'page-not-found', style: styles.wrapper },\n React.createElement('h1', { style: styles.heading }, '404'),\n React.createElement('p', { style: styles.message }, 'Page not found'),\n React.createElement('a', { href: homeHref, style: styles.link }, 'Go to homepage')\n )\n}\n\n/**\n * Static HTML string for SSR injection (generate404Html).\n *\n * @param {string} [basePath] - Base path prefix for the homepage link (e.g., '/sites/testproject')\n */\nexport function default404Html(basePath = '') {\n const homeHref = basePath ? `${basePath}/` : '/'\n return (\n `<div class=\"page-not-found\" style=\"min-height:80vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center\">` +\n `<h1 style=\"font-size:3rem;font-weight:bold;color:#1f2937;margin-bottom:1rem\">404</h1>` +\n `<p style=\"color:#64748b;margin-bottom:2rem\">Page not found</p>` +\n `<a href=\"${homeHref}\" style=\"color:#3b82f6;text-decoration:underline\">Go to homepage</a>` +\n `</div>`\n )\n}\n","/**\n * SSR Renderer\n *\n * Hook-free rendering pipeline for SSG (build) and cloud SSR (unicloud).\n * Mirrors BlockRenderer.jsx + Background.jsx using React.createElement\n * directly — no hooks, no JSX, no browser APIs.\n *\n * This is the single source of truth for how blocks render during prerender.\n * When modifying BlockRenderer.jsx or Background.jsx, update this file to match.\n *\n * Exports three layers:\n * 1. Rendering functions (renderBlock, renderBlocks, renderLayout, renderBackground)\n * 2. Initialization (initPrerender, prefetchIcons)\n * 3. Per-page rendering (renderPage, classifyRenderError, injectPageContent, escapeHtml)\n */\n\nimport React from 'react'\nimport { renderToString } from 'react-dom/server'\nimport { createUniweb } from '@uniweb/core'\nimport { buildSectionOverrides } from '@uniweb/theming'\nimport { prepareProps, getComponentMeta } from './prepare-props.js'\nimport { default404Html } from './default-404.js'\n\n// ============================================================================\n// Layer 1: Rendering functions\n// ============================================================================\n\n/**\n * Valid color contexts for section theming\n */\nconst VALID_CONTEXTS = ['light', 'medium', 'dark']\n\n/**\n * Build wrapper props from block configuration.\n * Mirrors getWrapperProps in BlockRenderer.jsx.\n */\nexport function getWrapperProps(block) {\n const theme = block.themeName\n const blockClassName = block.state?.className || ''\n\n // Empty themeName = Auto → no context class → inherits tokens from :root\n // Non-empty = Pinned → context class sets tokens directly on the element\n let contextClass = ''\n if (theme && VALID_CONTEXTS.includes(theme)) {\n contextClass = `context-${theme}`\n }\n\n let className = contextClass\n if (blockClassName) {\n className = className ? `${className} ${blockClassName}` : blockClassName\n }\n\n const { background = {} } = block.standardOptions\n const style = {}\n\n // If background has content, ensure relative positioning and a stacking context\n // so the background's z-index stays contained within this section.\n if (background.mode) {\n style.position = 'relative'\n style.isolation = 'isolate'\n }\n\n // Apply context overrides as inline CSS custom properties\n if (block.contextOverrides) {\n for (const [key, value] of Object.entries(block.contextOverrides)) {\n style[`--${key}`] = value\n }\n }\n\n // Use stableId for DOM ID if available (stable across reordering)\n const sectionId = block.stableId || block.id\n\n return { id: `section-${sectionId}`, style, className, background }\n}\n\n/**\n * Convert hex/rgb color to rgba with opacity.\n * Mirrors withOpacity() in Background.jsx.\n */\nfunction withOpacity(color, opacity) {\n if (color.startsWith('#')) {\n const r = parseInt(color.slice(1, 3), 16)\n const g = parseInt(color.slice(3, 5), 16)\n const b = parseInt(color.slice(5, 7), 16)\n return `rgba(${r}, ${g}, ${b}, ${opacity})`\n }\n if (color.startsWith('rgb')) {\n const match = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/)\n if (match) {\n return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${opacity})`\n }\n }\n return color\n}\n\n/**\n * Resolve a URL against the site's base path.\n * Mirrors resolveUrl() in Background.jsx.\n */\nfunction resolveUrl(url) {\n if (!url || !url.startsWith('/')) return url\n const basePath = globalThis.uniweb?.activeWebsite?.basePath || ''\n if (!basePath) return url\n if (url.startsWith(basePath + '/') || url === basePath) return url\n return basePath + url\n}\n\n/**\n * Render a background element for SSR.\n * Mirrors Background.jsx (color, gradient, image — not video).\n * Video backgrounds require JS for autoplay and are skipped during SSR.\n */\nexport function renderBackground(background) {\n if (!background?.mode) return null\n\n const containerStyle = {\n position: 'absolute',\n inset: '0',\n overflow: 'hidden',\n zIndex: 0,\n }\n\n const children = []\n\n // Color background\n if (background.mode === 'color' && background.color) {\n children.push(\n React.createElement('div', {\n key: 'bg-color',\n className: 'background-color',\n style: { position: 'absolute', inset: '0', backgroundColor: background.color },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Gradient background (supports string or object with opacity)\n if (background.mode === 'gradient' && background.gradient) {\n const g = background.gradient\n\n let bgValue\n if (typeof g === 'string') {\n bgValue = g\n } else {\n const {\n start = 'transparent',\n end = 'transparent',\n angle = 0,\n startPosition = 0,\n endPosition = 100,\n startOpacity = 1,\n endOpacity = 1,\n } = g\n const startColor = startOpacity < 1 ? withOpacity(start, startOpacity) : start\n const endColor = endOpacity < 1 ? withOpacity(end, endOpacity) : end\n bgValue = `linear-gradient(${angle}deg, ${startColor} ${startPosition}%, ${endColor} ${endPosition}%)`\n }\n\n children.push(\n React.createElement('div', {\n key: 'bg-gradient',\n className: 'background-gradient',\n style: { position: 'absolute', inset: '0', background: bgValue },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Image background\n if (background.mode === 'image' && background.image?.src) {\n const img = background.image\n children.push(\n React.createElement('div', {\n key: 'bg-image',\n className: 'background-image',\n style: {\n position: 'absolute',\n inset: '0',\n backgroundImage: `url(${resolveUrl(img.src)})`,\n backgroundPosition: img.position || 'center',\n backgroundSize: img.size || 'cover',\n backgroundRepeat: 'no-repeat',\n },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Overlay (gradient or solid)\n if (background.overlay?.enabled) {\n const ov = background.overlay\n let overlayStyle\n\n if (ov.gradient) {\n const g = ov.gradient\n overlayStyle = {\n position: 'absolute', inset: '0', pointerEvents: 'none',\n background: `linear-gradient(${g.angle || 180}deg, ${g.start || 'rgba(0,0,0,0.7)'} ${g.startPosition || 0}%, ${g.end || 'rgba(0,0,0,0)'} ${g.endPosition || 100}%)`,\n opacity: ov.opacity ?? 0.5,\n }\n } else {\n const baseColor = ov.type === 'light' ? '255, 255, 255' : '0, 0, 0'\n overlayStyle = {\n position: 'absolute', inset: '0', pointerEvents: 'none',\n backgroundColor: `rgba(${baseColor}, ${ov.opacity ?? 0.5})`,\n }\n }\n\n children.push(\n React.createElement('div', {\n key: 'bg-overlay',\n className: ov.gradient ? 'background-overlay background-overlay--gradient' : 'background-overlay background-overlay--solid',\n style: overlayStyle,\n 'aria-hidden': 'true',\n })\n )\n }\n\n if (children.length === 0) return null\n\n return React.createElement('div', {\n className: `background background--${background.mode}`,\n style: containerStyle,\n 'aria-hidden': 'true',\n }, ...children)\n}\n\n/**\n * Render a single block for SSR.\n * Mirrors BlockRenderer.jsx but without hooks (no runtime data fetching).\n *\n * Two modes (mirrors client BlockRenderer):\n * - Bare (as=null/false): component only, no wrapper\n * - Section (as='section'/'div'/etc.): full treatment with wrapper, context, background\n *\n * @param {Block} block - Block instance to render\n * @param {Object} [options]\n * @param {string|null} [options.as='section'] - Wrapper element tag, or null/false for bare mode\n * @returns {React.ReactElement}\n */\nexport function renderBlock(block, { as = 'section' } = {}) {\n const Component = block.initComponent()\n\n if (!Component) {\n return React.createElement('div', {\n className: 'block-error',\n style: { padding: '1rem', background: '#fef2f2', color: '#dc2626' },\n }, `Component not found: ${block.type}`)\n }\n\n // Build content and params with runtime guarantees\n const meta = getComponentMeta(block.type)\n const prepared = prepareProps(block, meta)\n const params = prepared.params\n const content = { ...prepared.content, ...block.properties }\n\n // Resolve inherited entity data (mirrors BlockRenderer.jsx)\n // EntityStore walks page/site hierarchy to find data matching meta.inheritData\n const entityStore = block.website?.entityStore\n if (entityStore) {\n const resolved = entityStore.resolve(block, meta)\n if (resolved.status === 'ready' && resolved.data) {\n const merged = { ...content.data }\n for (const key of Object.keys(resolved.data)) {\n if (merged[key] === undefined) {\n merged[key] = resolved.data[key]\n }\n }\n content.data = merged\n }\n }\n\n const componentProps = { content, params, block }\n\n // Bare mode: component only, no wrapper or section chrome.\n // Used by ChildBlocks for grid cells, tab panels, inline children, insets.\n if (!as) {\n return React.createElement(Component, componentProps)\n }\n\n // Section mode: full treatment with wrapper, context classes, background.\n const { background, ...wrapperProps } = getWrapperProps(block)\n\n // Merge Component.className (static classes declared on the component function)\n const componentClassName = Component.className\n if (componentClassName) {\n wrapperProps.className = wrapperProps.className\n ? `${wrapperProps.className} ${componentClassName}`\n : componentClassName\n }\n\n // Check if component handles its own background\n const hasBackground = background?.mode && meta?.background !== 'self'\n block.hasBackground = hasBackground\n\n // Determine wrapper element:\n // - Explicit as (not 'section') → use as prop directly\n // - Component.as → use component's declared tag (e.g., Header.as = 'header')\n // - fallback → 'section'\n const wrapperTag = as !== 'section' ? as : (Component.as || 'section')\n\n if (hasBackground) {\n return React.createElement(wrapperTag, wrapperProps,\n renderBackground(background),\n React.createElement('div', { style: { position: 'relative', zIndex: 10 } },\n React.createElement(Component, componentProps)\n )\n )\n }\n\n return React.createElement(wrapperTag, wrapperProps,\n React.createElement(Component, componentProps)\n )\n}\n\n/**\n * Render an array of blocks for SSR.\n */\nexport function renderBlocks(blocks) {\n if (!blocks || blocks.length === 0) return null\n return blocks.map((block, index) =>\n React.createElement(React.Fragment, { key: block.id || index },\n renderBlock(block)\n )\n )\n}\n\n/**\n * Render page layout for SSR.\n * Mirrors Layout.jsx but without hooks.\n */\nexport function renderLayout(page, website) {\n const layoutName = page.getLayoutName()\n const RemoteLayout = website.getRemoteLayout(layoutName)\n const layoutMeta = website.getLayoutMeta(layoutName)\n\n const bodyBlocks = page.getBodyBlocks()\n const areas = page.getLayoutAreas()\n\n const bodyElement = bodyBlocks ? renderBlocks(bodyBlocks) : null\n const areaElements = {}\n for (const [name, blocks] of Object.entries(areas)) {\n areaElements[name] = renderBlocks(blocks)\n }\n\n if (RemoteLayout) {\n const params = { ...(layoutMeta?.defaults || {}), ...(page.getLayoutParams() || {}) }\n return React.createElement(RemoteLayout, {\n page, website, params,\n body: bodyElement,\n ...areaElements,\n })\n }\n\n // Default layout\n return React.createElement(React.Fragment, null,\n areaElements.header && React.createElement('header', null, areaElements.header),\n bodyElement && React.createElement('main', null, bodyElement),\n areaElements.footer && React.createElement('footer', null, areaElements.footer)\n )\n}\n\n// ============================================================================\n// Layer 2: Initialization\n// ============================================================================\n\n/**\n * Create and configure the Uniweb runtime for prerendering.\n *\n * Handles the full initialization sequence in the correct order:\n * createUniweb → setFoundation → capabilities → layoutMeta → basePath → childBlockRenderer.\n *\n * Returns the configured uniweb instance. Consumers can add extras after:\n * - Build: pre-populate DataStore, load extensions\n * - Unicloud: (none needed — payload is complete)\n *\n * NOTE: Does NOT clone content. Cloning is the consumer's responsibility\n * (build modifies content before init; unicloud clones upfront).\n *\n * @param {Object} content - Site content JSON (pages, config, hierarchy)\n * @param {Object} foundation - Loaded foundation module\n * @param {Object} [options]\n * @param {function} [options.onProgress] - Progress callback\n * @returns {Object} Configured uniweb instance\n */\nexport function initPrerender(content, foundation, options = {}) {\n const { onProgress = () => {} } = options\n\n onProgress('Initializing runtime...')\n const uniweb = createUniweb(content)\n uniweb.setFoundation(foundation)\n\n // Set foundation capabilities (Layout, props, etc.)\n if (foundation.default?.capabilities) {\n uniweb.setFoundationConfig(foundation.default.capabilities)\n }\n\n // Attach layout metadata (areas, transitions, defaults)\n if (foundation.default?.layoutMeta && uniweb.foundationConfig) {\n uniweb.foundationConfig.layoutMeta = foundation.default.layoutMeta\n }\n\n // Set base path from site config for subdirectory deployments\n if (content.config?.base && uniweb.activeWebsite?.setBasePath) {\n uniweb.activeWebsite.setBasePath(content.config.base)\n }\n\n // Set childBlockRenderer so ChildBlocks/Visual/Render work during prerender.\n // Mirrors the client's ChildBlocks component in PageRenderer.jsx:\n // - default bare rendering (no wrapAs) — component only, no wrapper\n // - pass wrapAs to opt into full section treatment\n uniweb.childBlockRenderer = function InlineChildBlocks({ blocks, from, wrapAs }) {\n const blockList = blocks || from?.childBlocks || []\n return blockList.map((childBlock, index) =>\n React.createElement(React.Fragment, { key: childBlock.id || index },\n renderBlock(childBlock, { as: wrapAs || null })\n )\n )\n }\n\n return uniweb\n}\n\n/**\n * Pre-fetch icons from CDN and populate the Uniweb icon cache.\n * Stores the cache on siteContent._iconCache for embedding in HTML.\n *\n * @param {Object} siteContent - Site content JSON (mutated: _iconCache added)\n * @param {Object} uniweb - Configured uniweb instance\n * @param {function} [onProgress] - Progress callback\n */\nexport async function prefetchIcons(siteContent, uniweb, onProgress = () => {}) {\n const icons = siteContent.icons?.used || []\n if (icons.length === 0) return\n\n const cdnBase = siteContent.config?.icons?.cdnUrl || 'https://uniweb.github.io/icons'\n\n onProgress(`Fetching ${icons.length} icons for SSR...`)\n\n const results = await Promise.allSettled(\n icons.map(async (iconRef) => {\n const [family, name] = iconRef.split(':')\n const url = `${cdnBase}/${family}/${family}-${name}.svg`\n const response = await fetch(url)\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n const svg = await response.text()\n uniweb.iconCache.set(`${family}:${name}`, svg)\n })\n )\n\n const succeeded = results.filter(r => r.status === 'fulfilled').length\n const failed = results.filter(r => r.status === 'rejected').length\n if (failed > 0) {\n const msg = `Fetched ${succeeded}/${icons.length} icons (${failed} failed)`\n console.warn(`[prerender] ${msg}`)\n onProgress(` ${msg}`)\n }\n\n // Store icon cache on siteContent for embedding in HTML\n if (uniweb.iconCache.size > 0) {\n siteContent._iconCache = Object.fromEntries(uniweb.iconCache)\n }\n}\n\n// ============================================================================\n// Layer 3: Per-page rendering\n// ============================================================================\n\n/**\n * Classify an SSR rendering error.\n *\n * @param {Error} err\n * @returns {{ type: 'hooks'|'null-component'|'unknown', message: string }}\n */\nexport function classifyRenderError(err) {\n const msg = err.message || ''\n\n if (msg.includes('Invalid hook call') || msg.includes('useState') || msg.includes('useEffect')) {\n return {\n type: 'hooks',\n message: 'contains components with React hooks (renders client-side)',\n }\n }\n\n if (msg.includes('Element type is invalid') && msg.includes('null')) {\n return {\n type: 'null-component',\n message: 'a component resolved to null (often hook-related, renders client-side)',\n }\n }\n\n return {\n type: 'unknown',\n message: msg,\n }\n}\n\n/**\n * Render a single page to HTML.\n *\n * Handles the full per-page pipeline:\n * setActivePage → renderLayout → renderToString → error handling → section override CSS.\n *\n * @param {Page} page - Page instance to render\n * @param {Website} website - Website instance\n * @returns {{ renderedContent: string, sectionOverrideCSS: string } | { error: { type: string, message: string } }}\n */\nexport function renderPage(page, website) {\n website.setActivePage(page.route)\n\n const element = renderLayout(page, website)\n\n let renderedContent\n try {\n renderedContent = renderToString(element)\n } catch (err) {\n return { error: classifyRenderError(err) }\n }\n\n // Build per-page section override CSS (theme pinning, component vars)\n const appearance = website.themeData?.appearance\n const sectionOverrideCSS = buildSectionOverrides(page.getPageBlocks(), appearance)\n\n return { renderedContent, sectionOverrideCSS }\n}\n\n// ============================================================================\n// HTML injection\n// ============================================================================\n\n/**\n * Escape HTML special characters.\n */\nexport function escapeHtml(str) {\n if (!str) return ''\n return String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n}\n\n/**\n * Inject prerendered content into an HTML shell.\n *\n * Common operations shared by both build and cloud:\n * - Replace #root div with rendered HTML\n * - Update page title\n * - Add/update meta description\n * - Inject section override CSS\n *\n * Build layers its additional injections on top of this return value:\n * __SITE_CONTENT__ JSON, icon cache, theme CSS (build-specific).\n *\n * @param {string} html - HTML shell\n * @param {string} renderedContent - React renderToString output\n * @param {Object} page - Page data { title, description, route }\n * @param {Object} [options]\n * @param {string} [options.sectionOverrideCSS] - Per-page section override CSS\n * @returns {string} HTML with injected content\n */\nexport function injectPageContent(html, renderedContent, page, options = {}) {\n let result = html\n\n // Inject per-page section override CSS before </head>\n if (options.sectionOverrideCSS) {\n const overrideStyle = `<style id=\"uniweb-page-overrides\">\\n${options.sectionOverrideCSS}\\n</style>`\n result = result.replace('</head>', `${overrideStyle}\\n</head>`)\n }\n\n // Replace the empty root div with pre-rendered content\n result = result.replace(\n /<div id=\"root\">[\\s\\S]*?<\\/div>/,\n `<div id=\"root\">${renderedContent}</div>`\n )\n\n // Update page title (use getTitle() so isIndex pages inherit parent title)\n const pageTitle = page.getTitle?.() || page.title\n if (pageTitle) {\n result = result.replace(\n /<title>.*?<\\/title>/,\n `<title>${escapeHtml(pageTitle)}</title>`\n )\n }\n\n // Add/update meta description\n if (page.description) {\n const metaDesc = `<meta name=\"description\" content=\"${escapeHtml(page.description)}\">`\n if (result.includes('<meta name=\"description\"')) {\n result = result.replace(/<meta name=\"description\"[^>]*>/, metaDesc)\n } else {\n result = result.replace('</head>', `${metaDesc}\\n</head>`)\n }\n }\n\n return result\n}\n\n// ============================================================================\n// 404 fallback generation\n// ============================================================================\n\n/**\n * Generate 404.html content for static hosting fallback.\n *\n * Serves two purposes on static hosts (GitHub Pages, Cloudflare Pages, etc.):\n * 1. Real 404: pre-rendered custom 404 page content (or blank #root if none defined)\n * 2. Valid dynamic route (e.g. /blog/2): inline script clears #root so SPA renders fresh\n *\n * Flow: static host serves 404.html → inline script runs before React mounts →\n * - dynamic route: clears #root, React renders the page normally\n * - real 404: leaves #root with pre-rendered content, React re-renders same 404 page\n *\n * @param {Object} options\n * @param {string} options.baseHtml - Assembled HTML shell (with site content already injected)\n * @param {Object} options.website - Initialized Website instance (from initPrerender)\n * @param {Object} options.siteContent - Site content object (to find dynamic templates)\n * @returns {{ html: string, hasNotFoundPage: boolean }}\n */\nexport function generate404Html({ baseHtml, website, siteContent }) {\n // Extract patterns for routes that remain as dynamic templates (prerender: false)\n const dynamicTemplates = siteContent.pages?.filter((p) => p.isDynamic) || []\n const routePatterns = dynamicTemplates.map((p) => {\n // '/blog/:id' → /^\\/blog\\/[^\\/]+\\/?$/\n const escaped = p.route\n .replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/:[^/]+/g, '[^\\\\/]+')\n return `^${escaped}\\\\/?$`\n })\n\n let html = baseHtml\n\n // Pre-render the custom 404 page content into #root (if the site defines one),\n // otherwise inject a default 404 message so the page isn't blank before JS loads\n const notFoundPage = website.getNotFoundPage()\n if (notFoundPage) {\n const notFoundResult = renderPage(notFoundPage, website)\n if (notFoundResult && !notFoundResult.error) {\n html = injectPageContent(html, notFoundResult.renderedContent, notFoundPage, {\n sectionOverrideCSS: notFoundResult.sectionOverrideCSS,\n })\n }\n } else {\n const basePath = website.basePath || ''\n html = html.replace(\n /<div id=\"root\">[\\s\\S]*?<\\/div>/,\n `<div id=\"root\">${default404Html(basePath)}</div>`\n )\n }\n\n // Inject inline script: if path matches a dynamic route, clear #root before React mounts\n // so the SPA renders the correct page rather than the 404 content\n if (routePatterns.length > 0) {\n const patternList = routePatterns.map((p) => `/${p}/`).join(',')\n const dynamicScript =\n `<script>(function(){` +\n `var p=[${patternList}],r=window.location.pathname;` +\n `if(p.some(function(x){return x.test(r)})){` +\n `var el=document.getElementById('root');if(el)el.innerHTML='';` +\n `}})()</script>`\n html = html.replace('</body>', `${dynamicScript}\\n</body>`)\n }\n\n return { html, hasNotFoundPage: !!notFoundPage }\n}\n"],"names":[],"mappings":";;;;AAgBA,SAAS,uBAAuB,MAAM;AACpC,SAAO;AAAA,IACL,OAAO,KAAK,SAAS;AAAA,IACrB,UAAU,KAAK,YAAY;AAAA,IAC3B,UAAU,KAAK,YAAY;AAAA,IAC3B,YAAY,KAAK,cAAc,CAAA;AAAA,IAC/B,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,QAAQ,KAAK,UAAU,CAAA;AAAA,IACvB,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,QAAQ,KAAK,UAAU,CAAA;AAAA,IACvB,UAAU,KAAK,YAAY,CAAA;AAAA,IAC3B,SAAS,KAAK,WAAW,CAAA;AAAA,IACzB,MAAM,KAAK,QAAQ,CAAA;AAAA,IACnB,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,WAAW,KAAK,aAAa,CAAA;AAAA,IAC7B,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,QAAQ,KAAK,UAAU,CAAA;AAAA,IACvB,UAAU,KAAK,YAAY,CAAA;AAAA,EAC/B;AACA;AASO,SAAS,0BAA0B,eAAe;AACvD,QAAM,UAAU,iBAAiB,CAAA;AAEjC,SAAO;AAAA;AAAA,IAEL,OAAO,QAAQ,SAAS;AAAA,IACxB,UAAU,QAAQ,YAAY;AAAA,IAC9B,UAAU,QAAQ,YAAY;AAAA,IAC9B,WAAW,QAAQ,aAAa;AAAA;AAAA,IAGhC,YAAY,QAAQ,cAAc,CAAA;AAAA,IAClC,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,QAAQ,QAAQ,UAAU,CAAA;AAAA,IAC1B,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,QAAQ,QAAQ,UAAU,CAAA;AAAA,IAC1B,QAAQ,QAAQ,UAAU,CAAA;AAAA,IAC1B,UAAU,QAAQ,YAAY,CAAA;AAAA,IAC9B,SAAS,QAAQ,WAAW,CAAA;AAAA,IAC5B,MAAM,QAAQ,QAAQ,CAAA;AAAA,IACtB,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,WAAW,QAAQ,aAAa,CAAA;AAAA,IAChC,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,QAAQ,QAAQ,UAAU,CAAA;AAAA,IAC1B,UAAU,QAAQ,YAAY,CAAA;AAAA;AAAA,IAG9B,QAAQ,QAAQ,SAAS,CAAA,GAAI,IAAI,sBAAsB;AAAA;AAAA,IAGvD,UAAU,QAAQ,YAAY,CAAA;AAAA;AAAA,IAG9B,KAAK,QAAQ;AAAA,EACjB;AACA;AAUA,SAAS,oBAAoB,KAAK,QAAQ;AACxC,MAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACzD,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,EAAE,GAAG,IAAG;AAEvB,aAAW,CAAC,OAAO,QAAQ,KAAK,OAAO,QAAQ,MAAM,GAAG;AAEtD,UAAM,eAAe,OAAO,aAAa,WAAW,SAAS,UAAU;AAGvE,QAAI,OAAO,KAAK,MAAM,UAAa,iBAAiB,QAAW;AAC7D,aAAO,KAAK,IAAI;AAAA,IAClB;AAGA,QAAI,OAAO,aAAa,YAAY,SAAS,WAAW,MAAM,QAAQ,SAAS,OAAO,GAAG;AACvF,UAAI,OAAO,KAAK,MAAM,UAAa,CAAC,SAAS,QAAQ,SAAS,OAAO,KAAK,CAAC,GAAG;AAE5E,YAAI,iBAAiB,QAAW;AAC9B,iBAAO,KAAK,IAAI;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,aAAa,YAAY,SAAS,SAAS,YAAY,SAAS,UAAU,OAAO,KAAK,GAAG;AAClG,aAAO,KAAK,IAAI,oBAAoB,OAAO,KAAK,GAAG,SAAS,MAAM;AAAA,IACpE;AAGA,QAAI,OAAO,aAAa,YAAY,SAAS,SAAS,WAAW,SAAS,MAAM,OAAO,KAAK,GAAG;AAC7F,UAAI,OAAO,SAAS,OAAO,UAAU;AACnC,eAAO,KAAK,IAAI,OAAO,KAAK,EAAE,IAAI,UAAQ,oBAAoB,MAAM,SAAS,EAAE,CAAC;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AASA,SAAS,mBAAmB,OAAO,QAAQ;AACzC,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,UAAQ,oBAAoB,MAAM,MAAM,CAAC;AAAA,EAC5D;AACA,SAAO,oBAAoB,OAAO,MAAM;AAC1C;AAUO,SAAS,aAAa,MAAM,SAAS;AAC1C,MAAI,CAAC,WAAW,CAAC,QAAQ,OAAO,SAAS,UAAU;AACjD,WAAO,QAAQ,CAAA;AAAA,EACjB;AAEA,QAAM,SAAS,EAAE,GAAG,KAAI;AAExB,aAAW,CAAC,KAAK,QAAQ,KAAK,OAAO,QAAQ,IAAI,GAAG;AAClD,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,CAAC,OAAQ;AAEb,WAAO,GAAG,IAAI,mBAAmB,UAAU,MAAM;AAAA,EACnD;AAEA,SAAO;AACT;AASO,SAAS,cAAc,QAAQ,UAAU;AAC9C,MAAI,CAAC,YAAY,OAAO,KAAK,QAAQ,EAAE,WAAW,GAAG;AACnD,WAAO,UAAU,CAAA;AAAA,EACnB;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAI,UAAU,CAAA;AAAA,EAClB;AACA;AASO,SAAS,aAAa,OAAO,MAAM;AAExC,QAAM,WAAW,MAAM,YAAY,CAAA;AACnC,QAAM,SAAS,cAAc,MAAM,YAAY,QAAQ;AAGvD,QAAM,UAAU,0BAA0B,MAAM,aAAa;AAG7D,QAAM,UAAU,MAAM,WAAW;AACjC,MAAI,WAAW,QAAQ,MAAM;AAC3B,YAAQ,OAAO,aAAa,QAAQ,MAAM,OAAO;AAAA,EACnD;AAEA,SAAO,EAAE,SAAS,OAAM;AAC1B;AAQO,SAAS,iBAAiB,eAAe;AAC9C,SAAO,WAAW,QAAQ,mBAAmB,aAAa,KAAK;AACjE;AAQO,SAAS,qBAAqB,eAAe;AAClD,SAAO,WAAW,QAAQ,uBAAuB,aAAa,KAAK,CAAA;AACrE;ACtLO,SAAS,eAAe,WAAW,IAAI;AAC5C,QAAM,WAAW,WAAW,GAAG,QAAQ,MAAM;AAC7C,SACE,+TAGY,QAAQ;AAGxB;AC7BA,MAAM,iBAAiB,CAAC,SAAS,UAAU,MAAM;AAM1C,SAAS,gBAAgB,OAAO;AACrC,QAAM,QAAQ,MAAM;AACpB,QAAM,iBAAiB,MAAM,OAAO,aAAa;AAIjD,MAAI,eAAe;AACnB,MAAI,SAAS,eAAe,SAAS,KAAK,GAAG;AAC3C,mBAAe,WAAW,KAAK;AAAA,EACjC;AAEA,MAAI,YAAY;AAChB,MAAI,gBAAgB;AAClB,gBAAY,YAAY,GAAG,SAAS,IAAI,cAAc,KAAK;AAAA,EAC7D;AAEA,QAAM,EAAE,aAAa,GAAE,IAAK,MAAM;AAClC,QAAM,QAAQ,CAAA;AAId,MAAI,WAAW,MAAM;AACnB,UAAM,WAAW;AACjB,UAAM,YAAY;AAAA,EACpB;AAGA,MAAI,MAAM,kBAAkB;AAC1B,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,gBAAgB,GAAG;AACjE,YAAM,KAAK,GAAG,EAAE,IAAI;AAAA,IACtB;AAAA,EACF;AAGA,QAAM,YAAY,MAAM,YAAY,MAAM;AAE1C,SAAO,EAAE,IAAI,WAAW,SAAS,IAAI,OAAO,WAAW,WAAU;AACnE;AAMA,SAAS,YAAY,OAAO,SAAS;AACnC,MAAI,MAAM,WAAW,GAAG,GAAG;AACzB,UAAM,IAAI,SAAS,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE;AACxC,UAAM,IAAI,SAAS,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE;AACxC,UAAM,IAAI,SAAS,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE;AACxC,WAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,OAAO;AAAA,EAC1C;AACA,MAAI,MAAM,WAAW,KAAK,GAAG;AAC3B,UAAM,QAAQ,MAAM,MAAM,gCAAgC;AAC1D,QAAI,OAAO;AACT,aAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,KAAK,OAAO;AAAA,IAC/D;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,WAAW,KAAK;AACvB,MAAI,CAAC,OAAO,CAAC,IAAI,WAAW,GAAG,EAAG,QAAO;AACzC,QAAM,WAAW,WAAW,QAAQ,eAAe,YAAY;AAC/D,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,IAAI,WAAW,WAAW,GAAG,KAAK,QAAQ,SAAU,QAAO;AAC/D,SAAO,WAAW;AACpB;AAOO,SAAS,iBAAiB,YAAY;AAC3C,MAAI,CAAC,YAAY,KAAM,QAAO;AAE9B,QAAM,iBAAiB;AAAA,IACrB,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA,EACZ;AAEE,QAAM,WAAW,CAAA;AAGjB,MAAI,WAAW,SAAS,WAAW,WAAW,OAAO;AACnD,aAAS;AAAA,MACP,MAAM,cAAc,OAAO;AAAA,QACzB,KAAK;AAAA,QACL,WAAW;AAAA,QACX,OAAO,EAAE,UAAU,YAAY,OAAO,KAAK,iBAAiB,WAAW,MAAK;AAAA,QAC5E,eAAe;AAAA,MACvB,CAAO;AAAA,IACP;AAAA,EACE;AAGA,MAAI,WAAW,SAAS,cAAc,WAAW,UAAU;AACzD,UAAM,IAAI,WAAW;AAErB,QAAI;AACJ,QAAI,OAAO,MAAM,UAAU;AACzB,gBAAU;AAAA,IACZ,OAAO;AACL,YAAM;AAAA,QACJ,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,gBAAgB;AAAA,QAChB,cAAc;AAAA,QACd,eAAe;AAAA,QACf,aAAa;AAAA,MACrB,IAAU;AACJ,YAAM,aAAa,eAAe,IAAI,YAAY,OAAO,YAAY,IAAI;AACzE,YAAM,WAAW,aAAa,IAAI,YAAY,KAAK,UAAU,IAAI;AACjE,gBAAU,mBAAmB,KAAK,QAAQ,UAAU,IAAI,aAAa,MAAM,QAAQ,IAAI,WAAW;AAAA,IACpG;AAEA,aAAS;AAAA,MACP,MAAM,cAAc,OAAO;AAAA,QACzB,KAAK;AAAA,QACL,WAAW;AAAA,QACX,OAAO,EAAE,UAAU,YAAY,OAAO,KAAK,YAAY,QAAO;AAAA,QAC9D,eAAe;AAAA,MACvB,CAAO;AAAA,IACP;AAAA,EACE;AAGA,MAAI,WAAW,SAAS,WAAW,WAAW,OAAO,KAAK;AACxD,UAAM,MAAM,WAAW;AACvB,aAAS;AAAA,MACP,MAAM,cAAc,OAAO;AAAA,QACzB,KAAK;AAAA,QACL,WAAW;AAAA,QACX,OAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,iBAAiB,OAAO,WAAW,IAAI,GAAG,CAAC;AAAA,UAC3C,oBAAoB,IAAI,YAAY;AAAA,UACpC,gBAAgB,IAAI,QAAQ;AAAA,UAC5B,kBAAkB;AAAA,QAC5B;AAAA,QACQ,eAAe;AAAA,MACvB,CAAO;AAAA,IACP;AAAA,EACE;AAGA,MAAI,WAAW,SAAS,SAAS;AAC/B,UAAM,KAAK,WAAW;AACtB,QAAI;AAEJ,QAAI,GAAG,UAAU;AACf,YAAM,IAAI,GAAG;AACb,qBAAe;AAAA,QACb,UAAU;AAAA,QAAY,OAAO;AAAA,QAAK,eAAe;AAAA,QACjD,YAAY,mBAAmB,EAAE,SAAS,GAAG,QAAQ,EAAE,SAAS,iBAAiB,IAAI,EAAE,iBAAiB,CAAC,MAAM,EAAE,OAAO,eAAe,IAAI,EAAE,eAAe,GAAG;AAAA,QAC/J,SAAS,GAAG,WAAW;AAAA,MAC/B;AAAA,IACI,OAAO;AACL,YAAM,YAAY,GAAG,SAAS,UAAU,kBAAkB;AAC1D,qBAAe;AAAA,QACb,UAAU;AAAA,QAAY,OAAO;AAAA,QAAK,eAAe;AAAA,QACjD,iBAAiB,QAAQ,SAAS,KAAK,GAAG,WAAW,GAAG;AAAA,MAChE;AAAA,IACI;AAEA,aAAS;AAAA,MACP,MAAM,cAAc,OAAO;AAAA,QACzB,KAAK;AAAA,QACL,WAAW,GAAG,WAAW,oDAAoD;AAAA,QAC7E,OAAO;AAAA,QACP,eAAe;AAAA,MACvB,CAAO;AAAA,IACP;AAAA,EACE;AAEA,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,SAAO,MAAM,cAAc,OAAO;AAAA,IAChC,WAAW,0BAA0B,WAAW,IAAI;AAAA,IACpD,OAAO;AAAA,IACP,eAAe;AAAA,EACnB,GAAK,GAAG,QAAQ;AAChB;AAeO,SAAS,YAAY,OAAO,EAAE,KAAK,UAAS,IAAK,CAAA,GAAI;AAC1D,QAAM,YAAY,MAAM,cAAa;AAErC,MAAI,CAAC,WAAW;AACd,WAAO,MAAM,cAAc,OAAO;AAAA,MAChC,WAAW;AAAA,MACX,OAAO,EAAE,SAAS,QAAQ,YAAY,WAAW,OAAO,UAAS;AAAA,IACvE,GAAO,wBAAwB,MAAM,IAAI,EAAE;AAAA,EACzC;AAGA,QAAM,OAAO,iBAAiB,MAAM,IAAI;AACxC,QAAM,WAAW,aAAa,OAAO,IAAI;AACzC,QAAM,SAAS,SAAS;AACxB,QAAM,UAAU,EAAE,GAAG,SAAS,SAAS,GAAG,MAAM,WAAU;AAI1D,QAAM,cAAc,MAAM,SAAS;AACnC,MAAI,aAAa;AACf,UAAM,WAAW,YAAY,QAAQ,OAAO,IAAI;AAChD,QAAI,SAAS,WAAW,WAAW,SAAS,MAAM;AAChD,YAAM,SAAS,EAAE,GAAG,QAAQ,KAAI;AAChC,iBAAW,OAAO,OAAO,KAAK,SAAS,IAAI,GAAG;AAC5C,YAAI,OAAO,GAAG,MAAM,QAAW;AAC7B,iBAAO,GAAG,IAAI,SAAS,KAAK,GAAG;AAAA,QACjC;AAAA,MACF;AACA,cAAQ,OAAO;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,iBAAiB,EAAE,SAAS,QAAQ,MAAK;AAI/C,MAAI,CAAC,IAAI;AACP,WAAO,MAAM,cAAc,WAAW,cAAc;AAAA,EACtD;AAGA,QAAM,EAAE,YAAY,GAAG,aAAY,IAAK,gBAAgB,KAAK;AAG7D,QAAM,qBAAqB,UAAU;AACrC,MAAI,oBAAoB;AACtB,iBAAa,YAAY,aAAa,YAClC,GAAG,aAAa,SAAS,IAAI,kBAAkB,KAC/C;AAAA,EACN;AAGA,QAAM,gBAAgB,YAAY,QAAQ,MAAM,eAAe;AAC/D,QAAM,gBAAgB;AAMtB,QAAM,aAAa,OAAO,YAAY,KAAM,UAAU,MAAM;AAE5D,MAAI,eAAe;AACjB,WAAO,MAAM;AAAA,MAAc;AAAA,MAAY;AAAA,MACrC,iBAAiB,UAAU;AAAA,MAC3B,MAAM;AAAA,QAAc;AAAA,QAAO,EAAE,OAAO,EAAE,UAAU,YAAY,QAAQ,KAAI;AAAA,QACtE,MAAM,cAAc,WAAW,cAAc;AAAA,MACrD;AAAA,IACA;AAAA,EACE;AAEA,SAAO,MAAM;AAAA,IAAc;AAAA,IAAY;AAAA,IACrC,MAAM,cAAc,WAAW,cAAc;AAAA,EACjD;AACA;AAKO,SAAS,aAAa,QAAQ;AACnC,MAAI,CAAC,UAAU,OAAO,WAAW,EAAG,QAAO;AAC3C,SAAO,OAAO;AAAA,IAAI,CAAC,OAAO,UACxB,MAAM;AAAA,MAAc,MAAM;AAAA,MAAU,EAAE,KAAK,MAAM,MAAM,MAAK;AAAA,MAC1D,YAAY,KAAK;AAAA,IACvB;AAAA,EACA;AACA;AAMO,SAAS,aAAa,MAAM,SAAS;AAC1C,QAAM,aAAa,KAAK,cAAa;AACrC,QAAM,eAAe,QAAQ,gBAAgB,UAAU;AACvD,QAAM,aAAa,QAAQ,cAAc,UAAU;AAEnD,QAAM,aAAa,KAAK,cAAa;AACrC,QAAM,QAAQ,KAAK,eAAc;AAEjC,QAAM,cAAc,aAAa,aAAa,UAAU,IAAI;AAC5D,QAAM,eAAe,CAAA;AACrB,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK,GAAG;AAClD,iBAAa,IAAI,IAAI,aAAa,MAAM;AAAA,EAC1C;AAEA,MAAI,cAAc;AAChB,UAAM,SAAS,EAAE,GAAI,YAAY,YAAY,IAAK,GAAI,KAAK,gBAAe,KAAM,GAAG;AACnF,WAAO,MAAM,cAAc,cAAc;AAAA,MACvC;AAAA,MAAM;AAAA,MAAS;AAAA,MACf,MAAM;AAAA,MACN,GAAG;AAAA,IACT,CAAK;AAAA,EACH;AAGA,SAAO,MAAM;AAAA,IAAc,MAAM;AAAA,IAAU;AAAA,IACzC,aAAa,UAAU,MAAM,cAAc,UAAU,MAAM,aAAa,MAAM;AAAA,IAC9E,eAAe,MAAM,cAAc,QAAQ,MAAM,WAAW;AAAA,IAC5D,aAAa,UAAU,MAAM,cAAc,UAAU,MAAM,aAAa,MAAM;AAAA,EAClF;AACA;AAyBO,SAAS,cAAc,SAAS,YAAY,UAAU,CAAA,GAAI;AAC/D,QAAM,EAAE,aAAa,MAAM;AAAA,EAAC,MAAM;AAElC,aAAW,yBAAyB;AACpC,QAAM,SAAS,aAAa,OAAO;AACnC,SAAO,cAAc,UAAU;AAG/B,MAAI,WAAW,SAAS,cAAc;AACpC,WAAO,oBAAoB,WAAW,QAAQ,YAAY;AAAA,EAC5D;AAGA,MAAI,WAAW,SAAS,cAAc,OAAO,kBAAkB;AAC7D,WAAO,iBAAiB,aAAa,WAAW,QAAQ;AAAA,EAC1D;AAGA,MAAI,QAAQ,QAAQ,QAAQ,OAAO,eAAe,aAAa;AAC7D,WAAO,cAAc,YAAY,QAAQ,OAAO,IAAI;AAAA,EACtD;AAMA,SAAO,qBAAqB,SAAS,kBAAkB,EAAE,QAAQ,MAAM,UAAU;AAC/E,UAAM,YAAY,UAAU,MAAM,eAAe,CAAA;AACjD,WAAO,UAAU;AAAA,MAAI,CAAC,YAAY,UAChC,MAAM;AAAA,QAAc,MAAM;AAAA,QAAU,EAAE,KAAK,WAAW,MAAM,MAAK;AAAA,QAC/D,YAAY,YAAY,EAAE,IAAI,UAAU,KAAI,CAAE;AAAA,MACtD;AAAA,IACA;AAAA,EACE;AAEA,SAAO;AACT;AAUO,eAAe,cAAc,aAAa,QAAQ,aAAa,MAAM;AAAC,GAAG;AAC9E,QAAM,QAAQ,YAAY,OAAO,QAAQ,CAAA;AACzC,MAAI,MAAM,WAAW,EAAG;AAExB,QAAM,UAAU,YAAY,QAAQ,OAAO,UAAU;AAErD,aAAW,YAAY,MAAM,MAAM,mBAAmB;AAEtD,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,MAAM,IAAI,OAAO,YAAY;AAC3B,YAAM,CAAC,QAAQ,IAAI,IAAI,QAAQ,MAAM,GAAG;AACxC,YAAM,MAAM,GAAG,OAAO,IAAI,MAAM,IAAI,MAAM,IAAI,IAAI;AAClD,YAAM,WAAW,MAAM,MAAM,GAAG;AAChC,UAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,QAAQ,SAAS,MAAM,EAAE;AAC3D,YAAM,MAAM,MAAM,SAAS,KAAI;AAC/B,aAAO,UAAU,IAAI,GAAG,MAAM,IAAI,IAAI,IAAI,GAAG;AAAA,IAC/C,CAAC;AAAA,EACL;AAEE,QAAM,YAAY,QAAQ,OAAO,OAAK,EAAE,WAAW,WAAW,EAAE;AAChE,QAAM,SAAS,QAAQ,OAAO,OAAK,EAAE,WAAW,UAAU,EAAE;AAC5D,MAAI,SAAS,GAAG;AACd,UAAM,MAAM,WAAW,SAAS,IAAI,MAAM,MAAM,WAAW,MAAM;AACjE,YAAQ,KAAK,eAAe,GAAG,EAAE;AACjC,eAAW,KAAK,GAAG,EAAE;AAAA,EACvB;AAGA,MAAI,OAAO,UAAU,OAAO,GAAG;AAC7B,gBAAY,aAAa,OAAO,YAAY,OAAO,SAAS;AAAA,EAC9D;AACF;AAYO,SAAS,oBAAoB,KAAK;AACvC,QAAM,MAAM,IAAI,WAAW;AAE3B,MAAI,IAAI,SAAS,mBAAmB,KAAK,IAAI,SAAS,UAAU,KAAK,IAAI,SAAS,WAAW,GAAG;AAC9F,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,IACf;AAAA,EACE;AAEA,MAAI,IAAI,SAAS,yBAAyB,KAAK,IAAI,SAAS,MAAM,GAAG;AACnE,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,IACf;AAAA,EACE;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,EACb;AACA;AAYO,SAAS,WAAW,MAAM,SAAS;AACxC,UAAQ,cAAc,KAAK,KAAK;AAEhC,QAAM,UAAU,aAAa,MAAM,OAAO;AAE1C,MAAI;AACJ,MAAI;AACF,sBAAkB,eAAe,OAAO;AAAA,EAC1C,SAAS,KAAK;AACZ,WAAO,EAAE,OAAO,oBAAoB,GAAG,EAAC;AAAA,EAC1C;AAGA,QAAM,aAAa,QAAQ,WAAW;AACtC,QAAM,qBAAqB,sBAAsB,KAAK,cAAa,GAAI,UAAU;AAEjF,SAAO,EAAE,iBAAiB,mBAAkB;AAC9C;AASO,SAAS,WAAW,KAAK;AAC9B,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,OAAO,GAAG,EACd,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO;AAC1B;AAqBO,SAAS,kBAAkB,MAAM,iBAAiB,MAAM,UAAU,CAAA,GAAI;AAC3E,MAAI,SAAS;AAGb,MAAI,QAAQ,oBAAoB;AAC9B,UAAM,gBAAgB;AAAA,EAAuC,QAAQ,kBAAkB;AAAA;AACvF,aAAS,OAAO,QAAQ,WAAW,GAAG,aAAa;AAAA,QAAW;AAAA,EAChE;AAGA,WAAS,OAAO;AAAA,IACd;AAAA,IACA,kBAAkB,eAAe;AAAA,EACrC;AAGE,QAAM,YAAY,KAAK,WAAQ,KAAQ,KAAK;AAC5C,MAAI,WAAW;AACb,aAAS,OAAO;AAAA,MACd;AAAA,MACA,UAAU,WAAW,SAAS,CAAC;AAAA,IACrC;AAAA,EACE;AAGA,MAAI,KAAK,aAAa;AACpB,UAAM,WAAW,qCAAqC,WAAW,KAAK,WAAW,CAAC;AAClF,QAAI,OAAO,SAAS,0BAA0B,GAAG;AAC/C,eAAS,OAAO,QAAQ,kCAAkC,QAAQ;AAAA,IACpE,OAAO;AACL,eAAS,OAAO,QAAQ,WAAW,GAAG,QAAQ;AAAA,QAAW;AAAA,IAC3D;AAAA,EACF;AAEA,SAAO;AACT;AAuBO,SAAS,gBAAgB,EAAE,UAAU,SAAS,YAAW,GAAI;AAElE,QAAM,mBAAmB,YAAY,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAA;AAC1E,QAAM,gBAAgB,iBAAiB,IAAI,CAAC,MAAM;AAEhD,UAAM,UAAU,EAAE,MACf,QAAQ,uBAAuB,MAAM,EACrC,QAAQ,WAAW,SAAS;AAC/B,WAAO,IAAI,OAAO;AAAA,EACpB,CAAC;AAED,MAAI,OAAO;AAIX,QAAM,eAAe,QAAQ,gBAAe;AAC5C,MAAI,cAAc;AAChB,UAAM,iBAAiB,WAAW,cAAc,OAAO;AACvD,QAAI,kBAAkB,CAAC,eAAe,OAAO;AAC3C,aAAO,kBAAkB,MAAM,eAAe,iBAAiB,cAAc;AAAA,QAC3E,oBAAoB,eAAe;AAAA,MAC3C,CAAO;AAAA,IACH;AAAA,EACF,OAAO;AACL,UAAM,WAAW,QAAQ,YAAY;AACrC,WAAO,KAAK;AAAA,MACV;AAAA,MACA,kBAAkB,eAAe,QAAQ,CAAC;AAAA,IAChD;AAAA,EACE;AAIA,MAAI,cAAc,SAAS,GAAG;AAC5B,UAAM,cAAc,cAAc,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG;AAC/D,UAAM,gBACJ,8BACU,WAAW;AAIvB,WAAO,KAAK,QAAQ,WAAW,GAAG,aAAa;AAAA,QAAW;AAAA,EAC5D;AAEA,SAAO,EAAE,MAAM,iBAAiB,CAAC,CAAC,aAAY;AAChD;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/runtime",
3
- "version": "0.6.25",
3
+ "version": "0.6.27",
4
4
  "description": "Minimal runtime for loading Uniweb foundations",
5
5
  "type": "module",
6
6
  "exports": {
@@ -41,7 +41,7 @@
41
41
  "devDependencies": {
42
42
  "@vitejs/plugin-react": "^4.5.2",
43
43
  "vite": "^7.3.1",
44
- "@uniweb/build": "0.8.28"
44
+ "@uniweb/build": "0.8.30"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "react": "^18.0.0 || ^19.0.0",
@@ -68,13 +68,16 @@ const getWrapperProps = (block) => {
68
68
  /**
69
69
  * BlockRenderer component
70
70
  *
71
+ * Two rendering modes:
72
+ * - Bare (as=null/false): component only, no wrapper — used by ChildBlocks (default)
73
+ * - Section (as='section'/'div'/etc.): full treatment with wrapper element, context
74
+ * classes, background, section ID — used by page-level Blocks component
75
+ *
71
76
  * @param {Object} props
72
77
  * @param {Block} props.block - Block instance to render
73
- * @param {boolean} props.pure - If true, render component without wrapper
74
- * @param {string|false} props.as - Element type to render as ('section', 'div', 'article', etc.) or false for Fragment
75
- * @param {Object} props.extra - Extra props to pass to the component
78
+ * @param {string|null} props.as - Wrapper element tag for section mode, or null/false for bare mode
76
79
  */
77
- export default function BlockRenderer({ block, pure = false, as = 'section', extra = {} }) {
80
+ export default function BlockRenderer({ block, as = 'section' }) {
78
81
  const Component = block.initComponent()
79
82
 
80
83
  // Entity-aware data resolution via EntityStore
@@ -142,6 +145,19 @@ export default function BlockRenderer({ block, pure = false, as = 'section', ext
142
145
  content.data = merged
143
146
  }
144
147
 
148
+ const componentProps = {
149
+ content,
150
+ params,
151
+ block
152
+ }
153
+
154
+ // Bare mode — component only, no wrapper or section chrome.
155
+ // Used by ChildBlocks for grid cells, tab panels, inline children, insets.
156
+ if (!as) {
157
+ return <Component {...componentProps} />
158
+ }
159
+
160
+ // Section mode — full treatment with wrapper, context classes, background.
145
161
  const { background, ...wrapperProps } = getWrapperProps(block)
146
162
 
147
163
  // Merge Component.className (static classes declared on the component function)
@@ -163,30 +179,16 @@ export default function BlockRenderer({ block, pure = false, as = 'section', ext
163
179
  // and let the engine background show through.
164
180
  block.hasBackground = hasBackground
165
181
 
166
- const componentProps = {
167
- content,
168
- params,
169
- block
170
- }
171
-
172
- if (pure) {
173
- return <Component {...componentProps} extra={extra} />
174
- }
175
-
176
182
  // Determine wrapper element:
177
- // - as={false}Fragment (no wrapper)
178
- // - as prop explicitly set (not default 'section') → use as prop
179
- // - Component.as → use component's declared tag
183
+ // - Explicit as prop (not 'section') use as prop directly
184
+ // - Component.as use component's declared tag (e.g., Header.as = 'header')
180
185
  // - fallback → 'section'
181
- const componentAs = Component.as
182
- const Wrapper = as === false ? React.Fragment : (as !== 'section' ? as : componentAs || 'section')
183
- // Fragment doesn't accept props, so only pass them for real elements
184
- const wrapperElementProps = as === false ? {} : wrapperProps
186
+ const Wrapper = as !== 'section' ? as : (Component.as || 'section')
185
187
 
186
188
  // Render with or without background
187
189
  if (hasBackground) {
188
190
  return (
189
- <Wrapper {...wrapperElementProps}>
191
+ <Wrapper {...wrapperProps}>
190
192
  {/* Background layer (positioned absolutely) */}
191
193
  <Background
192
194
  mode={background.mode}
@@ -207,7 +209,7 @@ export default function BlockRenderer({ block, pure = false, as = 'section', ext
207
209
 
208
210
  // No background - simpler render without extra wrapper
209
211
  return (
210
- <Wrapper {...wrapperElementProps}>
212
+ <Wrapper {...wrapperProps}>
211
213
  <Component {...componentProps} />
212
214
  </Wrapper>
213
215
  )
@@ -9,18 +9,19 @@ import React from 'react'
9
9
  import BlockRenderer from './BlockRenderer.jsx'
10
10
 
11
11
  /**
12
- * Render a list of blocks
12
+ * Render a list of blocks as top-level sections.
13
+ * Used by Layout to pre-render each area (header, body, footer, panels).
14
+ * Each block gets full section treatment (wrapper, context classes, background).
13
15
  *
14
16
  * @param {Object} props
15
17
  * @param {Block[]} props.blocks - Array of Block instances to render
16
- * @param {Object} [props.extra] - Extra props to pass to each block
17
18
  */
18
- export default function Blocks({ blocks, extra = {} }) {
19
+ export default function Blocks({ blocks }) {
19
20
  if (!blocks || blocks.length === 0) return null
20
21
 
21
22
  return blocks.map((block, index) => (
22
23
  <React.Fragment key={block.id || index}>
23
- <BlockRenderer block={block} extra={extra} />
24
+ <BlockRenderer block={block} />
24
25
  </React.Fragment>
25
26
  ))
26
27
  }
@@ -16,29 +16,34 @@ import { Default404 } from '../default-404.js'
16
16
 
17
17
  /**
18
18
  * ChildBlocks - renders child blocks of a block
19
- * Exposed for use by foundation components
19
+ * Exposed for use by foundation components via @uniweb/kit
20
+ *
21
+ * By default, renders each child as a bare component (no wrapper element,
22
+ * no context classes, no background). This is the common case for grid cells,
23
+ * tab panels, carousel slides, and inline children.
24
+ *
25
+ * Pass `wrapAs` to opt into full section treatment (wrapper element, context
26
+ * classes, background, section ID) — rare, for independent nested sections.
20
27
  *
21
28
  * @param {Object} props
22
29
  * @param {Block[]} props.blocks - Explicit array of blocks to render
23
30
  * @param {Block} props.from - Parent block to extract childBlocks from (convenience shorthand)
24
- * @param {boolean} props.pure - If true, render components without wrapper
25
- * @param {string|false} props.as - Element type to render as (default: 'div' for nested blocks)
26
- * @param {Object} props.extra - Extra props to pass to each component
31
+ * @param {string} [props.wrapAs] - Wrapper element tag for section treatment ('div', 'article', etc.)
27
32
  *
28
33
  * @example
29
- * // Explicit blocks array
30
- * <ChildBlocks blocks={filteredBlocks} />
34
+ * // Bare rendering (default) — grid cells, tabs, inline children
35
+ * <ChildBlocks from={block} />
31
36
  *
32
37
  * @example
33
- * // Extract from parent block
34
- * <ChildBlocks from={block} />
38
+ * // Section treatment each child gets its own wrapper, context, background
39
+ * <ChildBlocks from={block} wrapAs="div" />
35
40
  */
36
- export function ChildBlocks({ blocks, from, pure = false, as = 'div', extra = {} }) {
41
+ export function ChildBlocks({ blocks, from, wrapAs }) {
37
42
  const blockList = blocks || from?.childBlocks || []
38
43
 
39
44
  return blockList.map((childBlock, index) => (
40
45
  <React.Fragment key={childBlock.id || index}>
41
- <BlockRenderer block={childBlock} pure={pure} as={as} extra={extra} />
46
+ <BlockRenderer block={childBlock} as={wrapAs || null} />
42
47
  </React.Fragment>
43
48
  ))
44
49
  }
@@ -229,12 +229,16 @@ export function renderBackground(background) {
229
229
  * Render a single block for SSR.
230
230
  * Mirrors BlockRenderer.jsx but without hooks (no runtime data fetching).
231
231
  *
232
+ * Two modes (mirrors client BlockRenderer):
233
+ * - Bare (as=null/false): component only, no wrapper
234
+ * - Section (as='section'/'div'/etc.): full treatment with wrapper, context, background
235
+ *
232
236
  * @param {Block} block - Block instance to render
233
237
  * @param {Object} [options]
234
- * @param {boolean} [options.pure=false] - Render component without section wrapper (used by ChildBlocks)
238
+ * @param {string|null} [options.as='section'] - Wrapper element tag, or null/false for bare mode
235
239
  * @returns {React.ReactElement}
236
240
  */
237
- export function renderBlock(block, { pure = false, as = undefined } = {}) {
241
+ export function renderBlock(block, { as = 'section' } = {}) {
238
242
  const Component = block.initComponent()
239
243
 
240
244
  if (!Component) {
@@ -268,12 +272,13 @@ export function renderBlock(block, { pure = false, as = undefined } = {}) {
268
272
 
269
273
  const componentProps = { content, params, block }
270
274
 
271
- // Pure mode: render component without section wrapper (used by ChildBlocks)
272
- if (pure) {
275
+ // Bare mode: component only, no wrapper or section chrome.
276
+ // Used by ChildBlocks for grid cells, tab panels, inline children, insets.
277
+ if (!as) {
273
278
  return React.createElement(Component, componentProps)
274
279
  }
275
280
 
276
- // Background handling (mirrors BlockRenderer.jsx)
281
+ // Section mode: full treatment with wrapper, context classes, background.
277
282
  const { background, ...wrapperProps } = getWrapperProps(block)
278
283
 
279
284
  // Merge Component.className (static classes declared on the component function)
@@ -288,10 +293,11 @@ export function renderBlock(block, { pure = false, as = undefined } = {}) {
288
293
  const hasBackground = background?.mode && meta?.background !== 'self'
289
294
  block.hasBackground = hasBackground
290
295
 
291
- // Use Component.as as the wrapper tag (default: 'section').
292
- // An explicit `as` prop (e.g. 'div' from ChildBlocks) overrides Component.as,
293
- // mirroring the BlockRenderer.jsx Wrapper resolution logic.
294
- const wrapperTag = (as !== undefined && as !== 'section') ? as : (Component.as || 'section')
296
+ // Determine wrapper element:
297
+ // - Explicit as (not 'section') use as prop directly
298
+ // - Component.as → use component's declared tag (e.g., Header.as = 'header')
299
+ // - fallback 'section'
300
+ const wrapperTag = as !== 'section' ? as : (Component.as || 'section')
295
301
 
296
302
  if (hasBackground) {
297
303
  return React.createElement(wrapperTag, wrapperProps,
@@ -401,13 +407,13 @@ export function initPrerender(content, foundation, options = {}) {
401
407
 
402
408
  // Set childBlockRenderer so ChildBlocks/Visual/Render work during prerender.
403
409
  // Mirrors the client's ChildBlocks component in PageRenderer.jsx:
404
- // - default as='div' so nested blocks use <div> wrapper (not <section>)
405
- // matching the client and avoiding React hydration mismatch (error #418)
406
- uniweb.childBlockRenderer = function InlineChildBlocks({ blocks, from, pure = false, as = 'div' }) {
410
+ // - default bare rendering (no wrapAs) component only, no wrapper
411
+ // - pass wrapAs to opt into full section treatment
412
+ uniweb.childBlockRenderer = function InlineChildBlocks({ blocks, from, wrapAs }) {
407
413
  const blockList = blocks || from?.childBlocks || []
408
414
  return blockList.map((childBlock, index) =>
409
415
  React.createElement(React.Fragment, { key: childBlock.id || index },
410
- renderBlock(childBlock, { pure, as })
416
+ renderBlock(childBlock, { as: wrapAs || null })
411
417
  )
412
418
  )
413
419
  }