@uniweb/runtime 0.5.10 → 0.5.11

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
@@ -218,6 +218,13 @@ const MODES = {
218
218
  IMAGE: "image",
219
219
  VIDEO: "video"
220
220
  };
221
+ function resolveUrl(url) {
222
+ if (!url || !url.startsWith("/")) return url;
223
+ const basePath = globalThis.uniweb?.activeWebsite?.basePath || "";
224
+ if (!basePath) return url;
225
+ if (url.startsWith(basePath + "/") || url === basePath) return url;
226
+ return basePath + url;
227
+ }
221
228
  function GradientOverlay({ gradient, opacity = 0.5 }) {
222
229
  const {
223
230
  start = "rgba(0,0,0,0.7)",
@@ -307,7 +314,7 @@ function ImageBackground({ image }) {
307
314
  const style = {
308
315
  position: "absolute",
309
316
  inset: 0,
310
- backgroundImage: `url(${src})`,
317
+ backgroundImage: `url(${resolveUrl(src)})`,
311
318
  backgroundPosition: position,
312
319
  backgroundSize: size,
313
320
  backgroundRepeat: "no-repeat"
@@ -338,7 +345,10 @@ function VideoBackground({ video }) {
338
345
  height: "100%",
339
346
  objectFit: "cover"
340
347
  };
341
- const sourceList = sources || inferSources(src);
348
+ const sourceList = (sources || inferSources(src)).map((s) => ({
349
+ ...s,
350
+ src: resolveUrl(s.src)
351
+ }));
342
352
  return /* @__PURE__ */ jsx(
343
353
  "video",
344
354
  {
@@ -348,7 +358,7 @@ function VideoBackground({ video }) {
348
358
  loop,
349
359
  muted,
350
360
  playsInline: true,
351
- poster,
361
+ poster: resolveUrl(poster),
352
362
  "aria-hidden": "true",
353
363
  children: sourceList.map(({ src: sourceSrc, type }, index) => /* @__PURE__ */ jsx("source", { src: sourceSrc, type }, index))
354
364
  }
@@ -480,6 +490,12 @@ function BlockRenderer({ block, pure = false, as = "section", extra = {} }) {
480
490
  content.data = mergeIntoData(content.data, runtimeData[fetchConfig.schema], fetchConfig.schema, fetchConfig.merge);
481
491
  }
482
492
  }
493
+ const { background, ...wrapperProps } = getWrapperProps(block);
494
+ const meta = getComponentMeta(block.type);
495
+ const hasBackground = background?.mode && meta?.background !== "self";
496
+ if (hasBackground) {
497
+ params = { ...params, _hasBackground: true };
498
+ }
483
499
  const componentProps = {
484
500
  content,
485
501
  params,
@@ -488,9 +504,6 @@ function BlockRenderer({ block, pure = false, as = "section", extra = {} }) {
488
504
  if (pure) {
489
505
  return /* @__PURE__ */ jsx(Component, { ...componentProps, extra });
490
506
  }
491
- const { background, ...wrapperProps } = getWrapperProps(block);
492
- const meta = getComponentMeta(block.type);
493
- const hasBackground = background?.mode && meta?.background !== "manual";
494
507
  const Wrapper = as === false ? React.Fragment : as;
495
508
  const wrapperElementProps = as === false ? {} : wrapperProps;
496
509
  if (hasBackground) {
@@ -517,9 +530,9 @@ function Blocks({ blocks, extra = {} }) {
517
530
  }
518
531
  function DefaultLayout({ header, body, footer }) {
519
532
  return /* @__PURE__ */ jsxs(Fragment, { children: [
520
- header,
521
- body,
522
- footer
533
+ header && /* @__PURE__ */ jsx("header", { children: header }),
534
+ body && /* @__PURE__ */ jsx("main", { children: body }),
535
+ footer && /* @__PURE__ */ jsx("footer", { children: footer })
523
536
  ] });
524
537
  }
525
538
  function initializeAllBlocks(...blockGroups) {
package/dist/ssr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ssr.js","sources":["../src/prepare-props.js","../src/data-fetcher-client.js","../src/components/Background.jsx","../src/components/BlockRenderer.jsx","../src/components/Blocks.jsx","../src/components/Layout.jsx","../src/ssr.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 imgs: item.imgs || [],\n lists: item.lists || [],\n icons: item.icons || [],\n videos: item.videos || [],\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 subtitle2: content.subtitle2 || '',\n alignment: content.alignment || null,\n\n // Flat body fields\n paragraphs: content.paragraphs || [],\n links: content.links || [],\n imgs: content.imgs || [],\n lists: content.lists || [],\n icons: content.icons || [],\n videos: content.videos || [],\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 * Apply cascaded data based on component's inheritData setting\n *\n * @param {Object} localData - content.data from the section itself\n * @param {Object} cascadedData - Data from page/site level fetches\n * @param {boolean|Array} inheritData - Component's inheritData setting\n * @returns {Object} Merged data object\n */\nfunction applyCascadedData(localData, cascadedData, inheritData) {\n if (!inheritData || !cascadedData || Object.keys(cascadedData).length === 0) {\n return localData\n }\n\n if (inheritData === true) {\n // Inherit all: cascaded data as base, local data overrides\n return { ...cascadedData, ...localData }\n }\n\n if (Array.isArray(inheritData)) {\n // Selective: only specified schemas, local data takes precedence\n const result = { ...localData }\n for (const key of inheritData) {\n if (cascadedData[key] !== undefined && result[key] === undefined) {\n result[key] = cascadedData[key]\n }\n }\n return result\n }\n\n return localData\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 cascaded data based on component's inheritData setting\n const inheritData = meta?.inheritData\n const cascadedData = block.cascadedData || {}\n if (inheritData) {\n content.data = applyCascadedData(content.data, cascadedData, inheritData)\n }\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 * Client-side Data Fetcher\n *\n * Executes fetch operations in the browser for runtime data loading.\n * Used when prerender: false is set on fetch configurations.\n *\n * @module @uniweb/runtime/data-fetcher-client\n */\n\n/**\n * Get a nested value from an object using dot notation\n *\n * @param {object} obj - Source object\n * @param {string} path - Dot-separated path (e.g., 'data.items')\n * @returns {any} The nested value or undefined\n */\nfunction getNestedValue(obj, path) {\n if (!obj || !path) return obj\n\n const parts = path.split('.')\n let current = obj\n\n for (const part of parts) {\n if (current === null || current === undefined) return undefined\n current = current[part]\n }\n\n return current\n}\n\n/**\n * Execute a fetch operation in the browser\n *\n * @param {object} config - Normalized fetch config\n * @param {string} config.path - Local path (relative to site root)\n * @param {string} config.url - Remote URL\n * @param {string} config.schema - Schema key for data\n * @param {string} config.transform - Optional path to extract from response\n * @returns {Promise<{ data: any, error?: string }>} Fetched data or error\n *\n * @example\n * const result = await executeFetchClient({\n * path: '/data/team.json',\n * schema: 'team'\n * })\n * // result.data contains the parsed JSON array\n */\nexport async function executeFetchClient(config) {\n if (!config) return { data: null }\n\n const { path, url, transform } = config\n\n try {\n // Determine the fetch URL\n // For local paths, prepend base URL (for subpath deployments)\n // For remote URLs (http/https), use as-is\n let fetchUrl = path || url\n\n if (!fetchUrl) {\n return { data: [], error: 'No path or url specified' }\n }\n\n // Prepend base URL for relative paths (not absolute URLs)\n if (fetchUrl.startsWith('/') && !fetchUrl.startsWith('//')) {\n const baseUrl = import.meta.env?.BASE_URL || '/'\n // BASE_URL has trailing slash, path has leading slash - remove one\n fetchUrl = baseUrl.replace(/\\/$/, '') + fetchUrl\n }\n\n const response = await fetch(fetchUrl)\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n // Parse response based on content type\n const contentType = response.headers.get('content-type') || ''\n let data\n\n if (contentType.includes('application/json')) {\n data = await response.json()\n } else {\n // Try JSON first\n const text = await response.text()\n try {\n data = JSON.parse(text)\n } catch {\n // Return text as-is if not JSON\n console.warn('[data-fetcher] Response is not JSON, returning as text')\n data = text\n }\n }\n\n // Apply transform if specified (extract nested path)\n if (transform && data) {\n data = getNestedValue(data, transform)\n }\n\n return { data: data ?? [] }\n } catch (error) {\n console.warn(`[data-fetcher] Client fetch failed: ${error.message}`)\n return { data: [], error: error.message }\n }\n}\n\n/**\n * Merge fetched data into existing content.data\n *\n * @param {object} currentData - Current content.data object\n * @param {any} fetchedData - Data from fetch\n * @param {string} schema - Schema key to store under\n * @param {boolean} [merge=false] - If true, merge with existing; if false, replace\n * @returns {object} Updated data object\n */\nexport function mergeIntoData(currentData, fetchedData, schema, merge = false) {\n if (fetchedData === null || fetchedData === undefined || !schema) {\n return currentData\n }\n\n const result = { ...(currentData || {}) }\n\n if (merge && result[schema] !== undefined) {\n // Merge mode: combine with existing data\n const existing = result[schema]\n\n if (Array.isArray(existing) && Array.isArray(fetchedData)) {\n // Arrays: concatenate\n result[schema] = [...existing, ...fetchedData]\n } else if (\n typeof existing === 'object' &&\n existing !== null &&\n typeof fetchedData === 'object' &&\n fetchedData !== null &&\n !Array.isArray(existing) &&\n !Array.isArray(fetchedData)\n ) {\n // Objects: shallow merge\n result[schema] = { ...existing, ...fetchedData }\n } else {\n // Different types: fetched data wins\n result[schema] = fetchedData\n }\n } else {\n // Replace mode (default): fetched data overwrites\n result[schema] = fetchedData\n }\n\n return result\n}\n\nexport default executeFetchClient\n","/**\n * Background\n *\n * Renders section backgrounds (color, gradient, image, video) with optional overlay.\n * Positioned absolutely behind content with proper z-index stacking.\n *\n * @module @uniweb/runtime/components/Background\n */\n\nimport React from 'react'\n\n/**\n * Background modes\n */\nconst MODES = {\n COLOR: 'color',\n GRADIENT: 'gradient',\n IMAGE: 'image',\n VIDEO: 'video',\n}\n\n/**\n * Default overlay colors\n */\nconst OVERLAY_COLORS = {\n light: 'rgba(255, 255, 255, 0.5)',\n dark: 'rgba(0, 0, 0, 0.5)',\n}\n\n/**\n * Render gradient overlay\n */\nfunction GradientOverlay({ gradient, opacity = 0.5 }) {\n const {\n start = 'rgba(0,0,0,0.7)',\n end = 'rgba(0,0,0,0)',\n angle = 180,\n startPosition = 0,\n endPosition = 100,\n } = gradient\n\n const style = {\n position: 'absolute',\n inset: 0,\n background: `linear-gradient(${angle}deg, ${start} ${startPosition}%, ${end} ${endPosition}%)`,\n opacity,\n pointerEvents: 'none',\n }\n\n return <div className=\"background-overlay background-overlay--gradient\" style={style} aria-hidden=\"true\" />\n}\n\n/**\n * Render solid overlay\n */\nfunction SolidOverlay({ type = 'dark', opacity = 0.5 }) {\n const baseColor = type === 'light' ? '255, 255, 255' : '0, 0, 0'\n\n const style = {\n position: 'absolute',\n inset: 0,\n backgroundColor: `rgba(${baseColor}, ${opacity})`,\n pointerEvents: 'none',\n }\n\n return <div className=\"background-overlay background-overlay--solid\" style={style} aria-hidden=\"true\" />\n}\n\n/**\n * Render overlay (gradient or solid)\n */\nfunction Overlay({ overlay }) {\n if (!overlay?.enabled) return null\n\n if (overlay.gradient) {\n return <GradientOverlay gradient={overlay.gradient} opacity={overlay.opacity} />\n }\n\n return <SolidOverlay type={overlay.type} opacity={overlay.opacity} />\n}\n\n/**\n * Color background\n */\nfunction ColorBackground({ color }) {\n if (!color) return null\n\n const style = {\n position: 'absolute',\n inset: 0,\n backgroundColor: color,\n }\n\n return <div className=\"background-color\" style={style} aria-hidden=\"true\" />\n}\n\n/**\n * Gradient background\n */\nfunction GradientBackground({ gradient }) {\n if (!gradient) return null\n\n const {\n start = 'transparent',\n end = 'transparent',\n angle = 0,\n startPosition = 0,\n endPosition = 100,\n startOpacity = 1,\n endOpacity = 1,\n } = gradient\n\n // Convert colors to rgba if opacity is specified\n const startColor = startOpacity < 1 ? withOpacity(start, startOpacity) : start\n const endColor = endOpacity < 1 ? withOpacity(end, endOpacity) : end\n\n const style = {\n position: 'absolute',\n inset: 0,\n background: `linear-gradient(${angle}deg, ${startColor} ${startPosition}%, ${endColor} ${endPosition}%)`,\n }\n\n return <div className=\"background-gradient\" style={style} aria-hidden=\"true\" />\n}\n\n/**\n * Convert hex color to rgba with opacity\n */\nfunction withOpacity(color, opacity) {\n // Handle hex colors\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 // Handle rgb/rgba\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 // Fallback - return as is\n return color\n}\n\n/**\n * Image background\n */\nfunction ImageBackground({ image }) {\n if (!image?.src) return null\n\n const {\n src,\n position = 'center',\n size = 'cover',\n lazy = true,\n } = image\n\n const style = {\n position: 'absolute',\n inset: 0,\n backgroundImage: `url(${src})`,\n backgroundPosition: position,\n backgroundSize: size,\n backgroundRepeat: 'no-repeat',\n }\n\n // For lazy loading, we could use an img tag with loading=\"lazy\"\n // But for backgrounds, CSS is more appropriate\n // The lazy prop could be used for future intersection observer optimization\n\n return <div className=\"background-image\" style={style} aria-hidden=\"true\" />\n}\n\n/**\n * Check if user prefers reduced motion\n */\nfunction prefersReducedMotion() {\n if (typeof window === 'undefined') return false\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches\n}\n\n/**\n * Video background\n *\n * Supports multiple source formats with automatic fallback.\n * Respects prefers-reduced-motion by showing poster image instead.\n */\nfunction VideoBackground({ video }) {\n if (!video?.src) return null\n\n const {\n src,\n sources, // Array of { src, type } for multiple formats\n poster,\n loop = true,\n muted = true,\n } = video\n\n // Respect reduced motion preference - show poster image instead\n if (prefersReducedMotion() && poster) {\n return <ImageBackground image={{ src: poster, size: 'cover', position: 'center' }} />\n }\n\n const style = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n }\n\n // Build source list: explicit sources array, or infer from src\n const sourceList = sources || inferSources(src)\n\n return (\n <video\n className=\"background-video\"\n style={style}\n autoPlay\n loop={loop}\n muted={muted}\n playsInline\n poster={poster}\n aria-hidden=\"true\"\n >\n {sourceList.map(({ src: sourceSrc, type }, index) => (\n <source key={index} src={sourceSrc} type={type} />\n ))}\n </video>\n )\n}\n\n/**\n * Infer multiple source formats from a single src\n *\n * If given \"video.mp4\", also tries \"video.webm\" (better compression)\n * Browser will use first supported format\n */\nfunction inferSources(src) {\n const sources = []\n const ext = src.split('.').pop()?.toLowerCase()\n const basePath = src.slice(0, src.lastIndexOf('.'))\n\n // Prefer webm (better compression), fall back to original\n if (ext === 'mp4') {\n sources.push({ src: `${basePath}.webm`, type: 'video/webm' })\n sources.push({ src, type: 'video/mp4' })\n } else if (ext === 'webm') {\n sources.push({ src, type: 'video/webm' })\n sources.push({ src: `${basePath}.mp4`, type: 'video/mp4' })\n } else {\n // Single source for other formats\n sources.push({ src, type: getVideoMimeType(src) })\n }\n\n return sources\n}\n\n/**\n * Get video MIME type from URL\n */\nfunction getVideoMimeType(src) {\n if (src.endsWith('.webm')) return 'video/webm'\n if (src.endsWith('.ogg') || src.endsWith('.ogv')) return 'video/ogg'\n return 'video/mp4'\n}\n\n/**\n * Background component\n *\n * @param {Object} props\n * @param {string} props.mode - Background mode: 'color', 'gradient', 'image', 'video'\n * @param {string} props.color - Color value (for color mode)\n * @param {Object} props.gradient - Gradient configuration\n * @param {Object} props.image - Image configuration\n * @param {Object} props.video - Video configuration\n * @param {Object} props.overlay - Overlay configuration\n * @param {string} props.className - Additional CSS class\n */\nexport default function Background({\n mode,\n color,\n gradient,\n image,\n video,\n overlay,\n className = '',\n}) {\n // No background to render\n if (!mode) return null\n\n const containerStyle = {\n position: 'absolute',\n inset: 0,\n overflow: 'hidden',\n zIndex: 0,\n }\n\n return (\n <div\n className={`background background--${mode} ${className}`.trim()}\n style={containerStyle}\n aria-hidden=\"true\"\n >\n {/* Render background based on mode */}\n {mode === MODES.COLOR && <ColorBackground color={color} />}\n {mode === MODES.GRADIENT && <GradientBackground gradient={gradient} />}\n {mode === MODES.IMAGE && <ImageBackground image={image} />}\n {mode === MODES.VIDEO && <VideoBackground video={video} />}\n\n {/* Overlay on top of background */}\n <Overlay overlay={overlay} />\n </div>\n )\n}\n\n/**\n * Export background modes for external use\n */\nexport { MODES as BackgroundModes }\n","/**\n * BlockRenderer\n *\n * Bridges Block data to foundation components.\n * Handles theming, wrapper props, and runtime guarantees.\n * Supports runtime data fetching for prerender: false configs.\n */\n\nimport React, { useState, useEffect } from 'react'\nimport { prepareProps, getComponentMeta } from '../prepare-props.js'\nimport { executeFetchClient, mergeIntoData } from '../data-fetcher-client.js'\nimport Background from './Background.jsx'\n\n/**\n * Valid color contexts\n */\nconst VALID_CONTEXTS = ['light', 'medium', 'dark']\n\n/**\n * Build wrapper props from block configuration\n */\nconst getWrapperProps = (block) => {\n const theme = block.themeName\n const blockClassName = block.state?.className || ''\n\n // Build context class (context-light, context-medium, context-dark)\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 for z-index stacking\n if (background.mode) {\n style.position = 'relative'\n }\n\n // Use stableId for DOM ID if available (stable across reordering)\n // Falls back to positional id for backwards compatibility\n const sectionId = block.stableId || block.id\n\n return {\n id: `section-${sectionId}`,\n style,\n className,\n background\n }\n}\n\n/**\n * BlockRenderer component\n *\n * @param {Object} props\n * @param {Block} props.block - Block instance to render\n * @param {boolean} props.pure - If true, render component without wrapper\n * @param {string|false} props.as - Element type to render as ('section', 'div', 'article', etc.) or false for Fragment\n * @param {Object} props.extra - Extra props to pass to the component\n */\nexport default function BlockRenderer({ block, pure = false, as = 'section', extra = {} }) {\n // State for runtime-fetched data (when prerender: false)\n const [runtimeData, setRuntimeData] = useState(null)\n const [fetchError, setFetchError] = useState(null)\n\n const Component = block.initComponent()\n\n // Runtime fetch for prerender: false configurations\n const fetchConfig = block.fetch\n const shouldFetchAtRuntime = fetchConfig && fetchConfig.prerender === false\n\n useEffect(() => {\n if (!shouldFetchAtRuntime) return\n\n let cancelled = false\n\n async function doFetch() {\n const result = await executeFetchClient(fetchConfig)\n if (cancelled) return\n\n if (result.error) {\n setFetchError(result.error)\n }\n if (result.data) {\n setRuntimeData({ [fetchConfig.schema]: result.data })\n }\n }\n\n doFetch()\n\n return () => {\n cancelled = true\n }\n }, [shouldFetchAtRuntime, fetchConfig])\n\n if (!Component) {\n return (\n <div className=\"block-error\" style={{ padding: '1rem', background: '#fef2f2', color: '#dc2626' }}>\n Component not found: {block.type}\n </div>\n )\n }\n\n // Build content and params with runtime guarantees\n // Sources:\n // 1. parsedContent._isPoc - simple PoC format (hardcoded content)\n // 2. parsedContent - semantic parser output (flat: title, paragraphs, links, etc.)\n // 3. block.properties - params from frontmatter (theme, alignment, etc.)\n // 4. meta - defaults from component meta.js\n let content, params\n\n if (block.parsedContent?._isPoc) {\n // Simple PoC format - content was passed directly\n content = block.parsedContent._pocContent\n params = block.properties\n } else {\n // Get runtime metadata for this component (has defaults, data binding, etc.)\n const meta = getComponentMeta(block.type)\n\n // Prepare props with runtime guarantees:\n // - Apply param defaults from meta.js\n // - Guarantee content structure exists\n // - Apply cascaded data based on inheritData\n const prepared = prepareProps(block, meta)\n params = prepared.params\n\n // Merge prepared content with raw access for components that need it\n content = {\n ...prepared.content,\n ...block.properties, // Frontmatter params overlay (legacy support)\n _prosemirror: block.parsedContent // Keep original for components that need raw access\n }\n\n // Merge runtime-fetched data if available\n if (runtimeData && shouldFetchAtRuntime) {\n content.data = mergeIntoData(content.data, runtimeData[fetchConfig.schema], fetchConfig.schema, fetchConfig.merge)\n }\n }\n\n const componentProps = {\n content,\n params,\n block\n }\n\n if (pure) {\n return <Component {...componentProps} extra={extra} />\n }\n\n const { background, ...wrapperProps } = getWrapperProps(block)\n\n // Check if component handles its own background (background: 'manual' in meta.js)\n const meta = getComponentMeta(block.type)\n const hasBackground = background?.mode && meta?.background !== 'manual'\n\n // Determine wrapper element: string tag name, or Fragment if false\n const Wrapper = as === false ? React.Fragment : as\n // Fragment doesn't accept props, so only pass them for real elements\n const wrapperElementProps = as === false ? {} : wrapperProps\n\n // Render with or without background\n if (hasBackground) {\n return (\n <Wrapper {...wrapperElementProps}>\n {/* Background layer (positioned absolutely) */}\n <Background\n mode={background.mode}\n color={background.color}\n gradient={background.gradient}\n image={background.image}\n video={background.video}\n overlay={background.overlay}\n />\n\n {/* Content layer (above background) */}\n <div className=\"relative z-10\">\n <Component {...componentProps} />\n </div>\n </Wrapper>\n )\n }\n\n // No background - simpler render without extra wrapper\n return (\n <Wrapper {...wrapperElementProps}>\n <Component {...componentProps} />\n </Wrapper>\n )\n}\n","/**\n * Blocks\n *\n * Renders an array of blocks for a layout area (header, body, footer, panels).\n * Used by the Layout component to pre-render each area.\n */\n\nimport React from 'react'\nimport BlockRenderer from './BlockRenderer.jsx'\n\n/**\n * Render a list of blocks\n *\n * @param {Object} props\n * @param {Block[]} props.blocks - Array of Block instances to render\n * @param {Object} [props.extra] - Extra props to pass to each block\n */\nexport default function Blocks({ blocks, extra = {} }) {\n if (!blocks || blocks.length === 0) return null\n\n return blocks.map((block, index) => (\n <React.Fragment key={block.id || index}>\n <BlockRenderer block={block} extra={extra} />\n </React.Fragment>\n ))\n}\n","/**\n * Layout\n *\n * Orchestrates page rendering by assembling layout areas (header, body, footer, panels).\n * Supports foundation-provided custom Layout components via website.getRemoteLayout().\n *\n * Layout Areas:\n * - header: Top navigation, branding (from @header page)\n * - body: Main page content (from page sections)\n * - footer: Bottom navigation, copyright (from @footer page)\n * - left: Left sidebar/panel (from @left page)\n * - right: Right sidebar/panel (from @right page)\n *\n * Custom Layouts:\n * Foundations can provide a custom Layout via src/exports.js:\n *\n * ```jsx\n * // src/exports.js\n * import Layout from './components/Layout'\n *\n * export default {\n * Layout,\n * props: {\n * themeToggleEnabled: true,\n * }\n * }\n * ```\n *\n * The Layout component receives pre-rendered areas as props:\n * - page, website: Runtime context\n * - header, body, footer: Pre-rendered React elements\n * - left, right (or leftPanel, rightPanel): Sidebar panels\n */\n\nimport Blocks from './Blocks.jsx'\n\n/**\n * Default layout - renders header, body, footer in sequence\n * (no panels in default layout)\n */\nfunction DefaultLayout({ header, body, footer }) {\n return (\n <>\n {header}\n {body}\n {footer}\n </>\n )\n}\n\n/**\n * Initialize all blocks to ensure cross-block communication works.\n * Must be called before rendering so getNextBlockInfo() can access sibling contexts.\n *\n * @param {Block[][]} blockGroups - Arrays of blocks from all layout areas\n */\nfunction initializeAllBlocks(...blockGroups) {\n for (const blocks of blockGroups) {\n if (!blocks) continue\n for (const block of blocks) {\n block.initComponent()\n }\n }\n}\n\n/**\n * Layout component\n *\n * @param {Object} props\n * @param {Page} props.page - Current page instance\n * @param {Website} props.website - Website instance\n */\nexport default function Layout({ page, website }) {\n // Check if foundation provides a custom Layout\n const RemoteLayout = website.getRemoteLayout()\n\n // Get block groups from page (respects layout preferences)\n const headerBlocks = page.getHeaderBlocks()\n const bodyBlocks = page.getBodyBlocks()\n const footerBlocks = page.getFooterBlocks()\n const leftBlocks = page.getLeftBlocks()\n const rightBlocks = page.getRightBlocks()\n\n // Pre-initialize all blocks before rendering any.\n // This ensures cross-block communication (getNextBlockInfo, getPrevBlockInfo)\n // can access sibling block contexts that are set in initComponent().\n initializeAllBlocks(headerBlocks, bodyBlocks, footerBlocks, leftBlocks, rightBlocks)\n\n // Pre-render each area as React elements\n const headerElement = headerBlocks ? <Blocks blocks={headerBlocks} /> : null\n const bodyElement = bodyBlocks ? <Blocks blocks={bodyBlocks} /> : null\n const footerElement = footerBlocks ? <Blocks blocks={footerBlocks} /> : null\n const leftElement = leftBlocks ? <Blocks blocks={leftBlocks} /> : null\n const rightElement = rightBlocks ? <Blocks blocks={rightBlocks} /> : null\n\n // Use foundation's custom Layout if provided\n if (RemoteLayout) {\n return (\n <RemoteLayout\n page={page}\n website={website}\n header={headerElement}\n body={bodyElement}\n footer={footerElement}\n left={leftElement}\n right={rightElement}\n // Aliases for backwards compatibility\n leftPanel={leftElement}\n rightPanel={rightElement}\n />\n )\n }\n\n // Default layout\n return (\n <DefaultLayout\n header={headerElement}\n body={bodyElement}\n footer={footerElement}\n />\n )\n}\n","/**\n * @uniweb/runtime/ssr - Server-Side Rendering Entry Point\n *\n * Node.js-compatible exports for SSG/prerendering.\n * This module is built to a standalone bundle that can be imported\n * directly by Node.js without Vite transpilation.\n *\n * Usage in prerender.js:\n * import { renderPage, Blocks, BlockRenderer } from '@uniweb/runtime/ssr'\n */\n\nimport React from 'react'\n\n// Props preparation (no browser APIs)\nexport {\n prepareProps,\n applySchemas,\n applyDefaults,\n guaranteeContentStructure,\n getComponentMeta,\n getComponentDefaults\n} from './prepare-props.js'\n\n// Components for rendering\nexport { default as BlockRenderer } from './components/BlockRenderer.jsx'\nexport { default as Blocks } from './components/Blocks.jsx'\nexport { default as Layout } from './components/Layout.jsx'\n\n// Re-export Layout's DefaultLayout for direct use\nimport LayoutComponent from './components/Layout.jsx'\n\n/**\n * Render a page to React elements\n *\n * This is the main entry point for SSG. It returns a React element\n * that can be passed to renderToString().\n *\n * @param {Object} props\n * @param {Page} props.page - The page instance to render\n * @param {Website} props.website - The website instance\n * @returns {React.ReactElement}\n */\nexport function PageElement({ page, website }) {\n return React.createElement(\n 'main',\n null,\n React.createElement(LayoutComponent, { page, website })\n )\n}\n"],"names":["meta","LayoutComponent"],"mappings":"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"}
1
+ {"version":3,"file":"ssr.js","sources":["../src/prepare-props.js","../src/data-fetcher-client.js","../src/components/Background.jsx","../src/components/BlockRenderer.jsx","../src/components/Blocks.jsx","../src/components/Layout.jsx","../src/ssr.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 imgs: item.imgs || [],\n lists: item.lists || [],\n icons: item.icons || [],\n videos: item.videos || [],\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 subtitle2: content.subtitle2 || '',\n alignment: content.alignment || null,\n\n // Flat body fields\n paragraphs: content.paragraphs || [],\n links: content.links || [],\n imgs: content.imgs || [],\n lists: content.lists || [],\n icons: content.icons || [],\n videos: content.videos || [],\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 * Apply cascaded data based on component's inheritData setting\n *\n * @param {Object} localData - content.data from the section itself\n * @param {Object} cascadedData - Data from page/site level fetches\n * @param {boolean|Array} inheritData - Component's inheritData setting\n * @returns {Object} Merged data object\n */\nfunction applyCascadedData(localData, cascadedData, inheritData) {\n if (!inheritData || !cascadedData || Object.keys(cascadedData).length === 0) {\n return localData\n }\n\n if (inheritData === true) {\n // Inherit all: cascaded data as base, local data overrides\n return { ...cascadedData, ...localData }\n }\n\n if (Array.isArray(inheritData)) {\n // Selective: only specified schemas, local data takes precedence\n const result = { ...localData }\n for (const key of inheritData) {\n if (cascadedData[key] !== undefined && result[key] === undefined) {\n result[key] = cascadedData[key]\n }\n }\n return result\n }\n\n return localData\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 cascaded data based on component's inheritData setting\n const inheritData = meta?.inheritData\n const cascadedData = block.cascadedData || {}\n if (inheritData) {\n content.data = applyCascadedData(content.data, cascadedData, inheritData)\n }\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 * Client-side Data Fetcher\n *\n * Executes fetch operations in the browser for runtime data loading.\n * Used when prerender: false is set on fetch configurations.\n *\n * @module @uniweb/runtime/data-fetcher-client\n */\n\n/**\n * Get a nested value from an object using dot notation\n *\n * @param {object} obj - Source object\n * @param {string} path - Dot-separated path (e.g., 'data.items')\n * @returns {any} The nested value or undefined\n */\nfunction getNestedValue(obj, path) {\n if (!obj || !path) return obj\n\n const parts = path.split('.')\n let current = obj\n\n for (const part of parts) {\n if (current === null || current === undefined) return undefined\n current = current[part]\n }\n\n return current\n}\n\n/**\n * Execute a fetch operation in the browser\n *\n * @param {object} config - Normalized fetch config\n * @param {string} config.path - Local path (relative to site root)\n * @param {string} config.url - Remote URL\n * @param {string} config.schema - Schema key for data\n * @param {string} config.transform - Optional path to extract from response\n * @returns {Promise<{ data: any, error?: string }>} Fetched data or error\n *\n * @example\n * const result = await executeFetchClient({\n * path: '/data/team.json',\n * schema: 'team'\n * })\n * // result.data contains the parsed JSON array\n */\nexport async function executeFetchClient(config) {\n if (!config) return { data: null }\n\n const { path, url, transform } = config\n\n try {\n // Determine the fetch URL\n // For local paths, prepend base URL (for subpath deployments)\n // For remote URLs (http/https), use as-is\n let fetchUrl = path || url\n\n if (!fetchUrl) {\n return { data: [], error: 'No path or url specified' }\n }\n\n // Prepend base URL for relative paths (not absolute URLs)\n if (fetchUrl.startsWith('/') && !fetchUrl.startsWith('//')) {\n const baseUrl = import.meta.env?.BASE_URL || '/'\n // BASE_URL has trailing slash, path has leading slash - remove one\n fetchUrl = baseUrl.replace(/\\/$/, '') + fetchUrl\n }\n\n const response = await fetch(fetchUrl)\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n // Parse response based on content type\n const contentType = response.headers.get('content-type') || ''\n let data\n\n if (contentType.includes('application/json')) {\n data = await response.json()\n } else {\n // Try JSON first\n const text = await response.text()\n try {\n data = JSON.parse(text)\n } catch {\n // Return text as-is if not JSON\n console.warn('[data-fetcher] Response is not JSON, returning as text')\n data = text\n }\n }\n\n // Apply transform if specified (extract nested path)\n if (transform && data) {\n data = getNestedValue(data, transform)\n }\n\n return { data: data ?? [] }\n } catch (error) {\n console.warn(`[data-fetcher] Client fetch failed: ${error.message}`)\n return { data: [], error: error.message }\n }\n}\n\n/**\n * Merge fetched data into existing content.data\n *\n * @param {object} currentData - Current content.data object\n * @param {any} fetchedData - Data from fetch\n * @param {string} schema - Schema key to store under\n * @param {boolean} [merge=false] - If true, merge with existing; if false, replace\n * @returns {object} Updated data object\n */\nexport function mergeIntoData(currentData, fetchedData, schema, merge = false) {\n if (fetchedData === null || fetchedData === undefined || !schema) {\n return currentData\n }\n\n const result = { ...(currentData || {}) }\n\n if (merge && result[schema] !== undefined) {\n // Merge mode: combine with existing data\n const existing = result[schema]\n\n if (Array.isArray(existing) && Array.isArray(fetchedData)) {\n // Arrays: concatenate\n result[schema] = [...existing, ...fetchedData]\n } else if (\n typeof existing === 'object' &&\n existing !== null &&\n typeof fetchedData === 'object' &&\n fetchedData !== null &&\n !Array.isArray(existing) &&\n !Array.isArray(fetchedData)\n ) {\n // Objects: shallow merge\n result[schema] = { ...existing, ...fetchedData }\n } else {\n // Different types: fetched data wins\n result[schema] = fetchedData\n }\n } else {\n // Replace mode (default): fetched data overwrites\n result[schema] = fetchedData\n }\n\n return result\n}\n\nexport default executeFetchClient\n","/**\n * Background\n *\n * Renders section backgrounds (color, gradient, image, video) with optional overlay.\n * Positioned absolutely behind content with proper z-index stacking.\n *\n * @module @uniweb/runtime/components/Background\n */\n\nimport React from 'react'\n\n/**\n * Background modes\n */\nconst MODES = {\n COLOR: 'color',\n GRADIENT: 'gradient',\n IMAGE: 'image',\n VIDEO: 'video',\n}\n\n/**\n * Default overlay colors\n */\nconst OVERLAY_COLORS = {\n light: 'rgba(255, 255, 255, 0.5)',\n dark: 'rgba(0, 0, 0, 0.5)',\n}\n\n/**\n * Resolve a URL against the site's base path\n * Prepends basePath to absolute URLs (starting with /) so they work\n * under subdirectory deployments (e.g., /templates/international/)\n */\nfunction resolveUrl(url) {\n if (!url || !url.startsWith('/')) return url\n const basePath = globalThis.uniweb?.activeWebsite?.basePath || ''\n if (!basePath) return url\n // Avoid double-prepending\n if (url.startsWith(basePath + '/') || url === basePath) return url\n return basePath + url\n}\n\n/**\n * Render gradient overlay\n */\nfunction GradientOverlay({ gradient, opacity = 0.5 }) {\n const {\n start = 'rgba(0,0,0,0.7)',\n end = 'rgba(0,0,0,0)',\n angle = 180,\n startPosition = 0,\n endPosition = 100,\n } = gradient\n\n const style = {\n position: 'absolute',\n inset: 0,\n background: `linear-gradient(${angle}deg, ${start} ${startPosition}%, ${end} ${endPosition}%)`,\n opacity,\n pointerEvents: 'none',\n }\n\n return <div className=\"background-overlay background-overlay--gradient\" style={style} aria-hidden=\"true\" />\n}\n\n/**\n * Render solid overlay\n */\nfunction SolidOverlay({ type = 'dark', opacity = 0.5 }) {\n const baseColor = type === 'light' ? '255, 255, 255' : '0, 0, 0'\n\n const style = {\n position: 'absolute',\n inset: 0,\n backgroundColor: `rgba(${baseColor}, ${opacity})`,\n pointerEvents: 'none',\n }\n\n return <div className=\"background-overlay background-overlay--solid\" style={style} aria-hidden=\"true\" />\n}\n\n/**\n * Render overlay (gradient or solid)\n */\nfunction Overlay({ overlay }) {\n if (!overlay?.enabled) return null\n\n if (overlay.gradient) {\n return <GradientOverlay gradient={overlay.gradient} opacity={overlay.opacity} />\n }\n\n return <SolidOverlay type={overlay.type} opacity={overlay.opacity} />\n}\n\n/**\n * Color background\n */\nfunction ColorBackground({ color }) {\n if (!color) return null\n\n const style = {\n position: 'absolute',\n inset: 0,\n backgroundColor: color,\n }\n\n return <div className=\"background-color\" style={style} aria-hidden=\"true\" />\n}\n\n/**\n * Gradient background\n */\nfunction GradientBackground({ gradient }) {\n if (!gradient) return null\n\n const {\n start = 'transparent',\n end = 'transparent',\n angle = 0,\n startPosition = 0,\n endPosition = 100,\n startOpacity = 1,\n endOpacity = 1,\n } = gradient\n\n // Convert colors to rgba if opacity is specified\n const startColor = startOpacity < 1 ? withOpacity(start, startOpacity) : start\n const endColor = endOpacity < 1 ? withOpacity(end, endOpacity) : end\n\n const style = {\n position: 'absolute',\n inset: 0,\n background: `linear-gradient(${angle}deg, ${startColor} ${startPosition}%, ${endColor} ${endPosition}%)`,\n }\n\n return <div className=\"background-gradient\" style={style} aria-hidden=\"true\" />\n}\n\n/**\n * Convert hex color to rgba with opacity\n */\nfunction withOpacity(color, opacity) {\n // Handle hex colors\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 // Handle rgb/rgba\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 // Fallback - return as is\n return color\n}\n\n/**\n * Image background\n */\nfunction ImageBackground({ image }) {\n if (!image?.src) return null\n\n const {\n src,\n position = 'center',\n size = 'cover',\n lazy = true,\n } = image\n\n const style = {\n position: 'absolute',\n inset: 0,\n backgroundImage: `url(${resolveUrl(src)})`,\n backgroundPosition: position,\n backgroundSize: size,\n backgroundRepeat: 'no-repeat',\n }\n\n // For lazy loading, we could use an img tag with loading=\"lazy\"\n // But for backgrounds, CSS is more appropriate\n // The lazy prop could be used for future intersection observer optimization\n\n return <div className=\"background-image\" style={style} aria-hidden=\"true\" />\n}\n\n/**\n * Check if user prefers reduced motion\n */\nfunction prefersReducedMotion() {\n if (typeof window === 'undefined') return false\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches\n}\n\n/**\n * Video background\n *\n * Supports multiple source formats with automatic fallback.\n * Respects prefers-reduced-motion by showing poster image instead.\n */\nfunction VideoBackground({ video }) {\n if (!video?.src) return null\n\n const {\n src,\n sources, // Array of { src, type } for multiple formats\n poster,\n loop = true,\n muted = true,\n } = video\n\n // Respect reduced motion preference - show poster image instead\n if (prefersReducedMotion() && poster) {\n return <ImageBackground image={{ src: poster, size: 'cover', position: 'center' }} />\n }\n\n const style = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n }\n\n // Build source list: explicit sources array, or infer from src\n const sourceList = (sources || inferSources(src)).map(s => ({\n ...s,\n src: resolveUrl(s.src)\n }))\n\n return (\n <video\n className=\"background-video\"\n style={style}\n autoPlay\n loop={loop}\n muted={muted}\n playsInline\n poster={resolveUrl(poster)}\n aria-hidden=\"true\"\n >\n {sourceList.map(({ src: sourceSrc, type }, index) => (\n <source key={index} src={sourceSrc} type={type} />\n ))}\n </video>\n )\n}\n\n/**\n * Infer multiple source formats from a single src\n *\n * If given \"video.mp4\", also tries \"video.webm\" (better compression)\n * Browser will use first supported format\n */\nfunction inferSources(src) {\n const sources = []\n const ext = src.split('.').pop()?.toLowerCase()\n const basePath = src.slice(0, src.lastIndexOf('.'))\n\n // Prefer webm (better compression), fall back to original\n if (ext === 'mp4') {\n sources.push({ src: `${basePath}.webm`, type: 'video/webm' })\n sources.push({ src, type: 'video/mp4' })\n } else if (ext === 'webm') {\n sources.push({ src, type: 'video/webm' })\n sources.push({ src: `${basePath}.mp4`, type: 'video/mp4' })\n } else {\n // Single source for other formats\n sources.push({ src, type: getVideoMimeType(src) })\n }\n\n return sources\n}\n\n/**\n * Get video MIME type from URL\n */\nfunction getVideoMimeType(src) {\n if (src.endsWith('.webm')) return 'video/webm'\n if (src.endsWith('.ogg') || src.endsWith('.ogv')) return 'video/ogg'\n return 'video/mp4'\n}\n\n/**\n * Background component\n *\n * @param {Object} props\n * @param {string} props.mode - Background mode: 'color', 'gradient', 'image', 'video'\n * @param {string} props.color - Color value (for color mode)\n * @param {Object} props.gradient - Gradient configuration\n * @param {Object} props.image - Image configuration\n * @param {Object} props.video - Video configuration\n * @param {Object} props.overlay - Overlay configuration\n * @param {string} props.className - Additional CSS class\n */\nexport default function Background({\n mode,\n color,\n gradient,\n image,\n video,\n overlay,\n className = '',\n}) {\n // No background to render\n if (!mode) return null\n\n const containerStyle = {\n position: 'absolute',\n inset: 0,\n overflow: 'hidden',\n zIndex: 0,\n }\n\n return (\n <div\n className={`background background--${mode} ${className}`.trim()}\n style={containerStyle}\n aria-hidden=\"true\"\n >\n {/* Render background based on mode */}\n {mode === MODES.COLOR && <ColorBackground color={color} />}\n {mode === MODES.GRADIENT && <GradientBackground gradient={gradient} />}\n {mode === MODES.IMAGE && <ImageBackground image={image} />}\n {mode === MODES.VIDEO && <VideoBackground video={video} />}\n\n {/* Overlay on top of background */}\n <Overlay overlay={overlay} />\n </div>\n )\n}\n\n/**\n * Export background modes for external use\n */\nexport { MODES as BackgroundModes }\n","/**\n * BlockRenderer\n *\n * Bridges Block data to foundation components.\n * Handles theming, wrapper props, and runtime guarantees.\n * Supports runtime data fetching for prerender: false configs.\n */\n\nimport React, { useState, useEffect } from 'react'\nimport { prepareProps, getComponentMeta } from '../prepare-props.js'\nimport { executeFetchClient, mergeIntoData } from '../data-fetcher-client.js'\nimport Background from './Background.jsx'\n\n/**\n * Valid color contexts\n */\nconst VALID_CONTEXTS = ['light', 'medium', 'dark']\n\n/**\n * Build wrapper props from block configuration\n */\nconst getWrapperProps = (block) => {\n const theme = block.themeName\n const blockClassName = block.state?.className || ''\n\n // Build context class (context-light, context-medium, context-dark)\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 for z-index stacking\n if (background.mode) {\n style.position = 'relative'\n }\n\n // Use stableId for DOM ID if available (stable across reordering)\n // Falls back to positional id for backwards compatibility\n const sectionId = block.stableId || block.id\n\n return {\n id: `section-${sectionId}`,\n style,\n className,\n background\n }\n}\n\n/**\n * BlockRenderer component\n *\n * @param {Object} props\n * @param {Block} props.block - Block instance to render\n * @param {boolean} props.pure - If true, render component without wrapper\n * @param {string|false} props.as - Element type to render as ('section', 'div', 'article', etc.) or false for Fragment\n * @param {Object} props.extra - Extra props to pass to the component\n */\nexport default function BlockRenderer({ block, pure = false, as = 'section', extra = {} }) {\n // State for runtime-fetched data (when prerender: false)\n const [runtimeData, setRuntimeData] = useState(null)\n const [fetchError, setFetchError] = useState(null)\n\n const Component = block.initComponent()\n\n // Runtime fetch for prerender: false configurations\n const fetchConfig = block.fetch\n const shouldFetchAtRuntime = fetchConfig && fetchConfig.prerender === false\n\n useEffect(() => {\n if (!shouldFetchAtRuntime) return\n\n let cancelled = false\n\n async function doFetch() {\n const result = await executeFetchClient(fetchConfig)\n if (cancelled) return\n\n if (result.error) {\n setFetchError(result.error)\n }\n if (result.data) {\n setRuntimeData({ [fetchConfig.schema]: result.data })\n }\n }\n\n doFetch()\n\n return () => {\n cancelled = true\n }\n }, [shouldFetchAtRuntime, fetchConfig])\n\n if (!Component) {\n return (\n <div className=\"block-error\" style={{ padding: '1rem', background: '#fef2f2', color: '#dc2626' }}>\n Component not found: {block.type}\n </div>\n )\n }\n\n // Build content and params with runtime guarantees\n // Sources:\n // 1. parsedContent._isPoc - simple PoC format (hardcoded content)\n // 2. parsedContent - semantic parser output (flat: title, paragraphs, links, etc.)\n // 3. block.properties - params from frontmatter (theme, alignment, etc.)\n // 4. meta - defaults from component meta.js\n let content, params\n\n if (block.parsedContent?._isPoc) {\n // Simple PoC format - content was passed directly\n content = block.parsedContent._pocContent\n params = block.properties\n } else {\n // Get runtime metadata for this component (has defaults, data binding, etc.)\n const meta = getComponentMeta(block.type)\n\n // Prepare props with runtime guarantees:\n // - Apply param defaults from meta.js\n // - Guarantee content structure exists\n // - Apply cascaded data based on inheritData\n const prepared = prepareProps(block, meta)\n params = prepared.params\n\n // Merge prepared content with raw access for components that need it\n content = {\n ...prepared.content,\n ...block.properties, // Frontmatter params overlay (legacy support)\n _prosemirror: block.parsedContent // Keep original for components that need raw access\n }\n\n // Merge runtime-fetched data if available\n if (runtimeData && shouldFetchAtRuntime) {\n content.data = mergeIntoData(content.data, runtimeData[fetchConfig.schema], fetchConfig.schema, fetchConfig.merge)\n }\n }\n\n const { background, ...wrapperProps } = getWrapperProps(block)\n\n // Check if component handles its own background (background: 'self' in meta.js)\n // Components that render their own background layer (solid colors, insets, effects)\n // opt out so the runtime doesn't render an occluded layer underneath.\n const meta = getComponentMeta(block.type)\n const hasBackground = background?.mode && meta?.background !== 'self'\n\n // Signal to the component that the author set a background in frontmatter.\n // Components can check params._hasBackground to skip their own opaque bg color\n // and let the engine background show through.\n if (hasBackground) {\n params = { ...params, _hasBackground: true }\n }\n\n const componentProps = {\n content,\n params,\n block\n }\n\n if (pure) {\n return <Component {...componentProps} extra={extra} />\n }\n\n // Determine wrapper element: string tag name, or Fragment if false\n const Wrapper = as === false ? React.Fragment : as\n // Fragment doesn't accept props, so only pass them for real elements\n const wrapperElementProps = as === false ? {} : wrapperProps\n\n // Render with or without background\n if (hasBackground) {\n return (\n <Wrapper {...wrapperElementProps}>\n {/* Background layer (positioned absolutely) */}\n <Background\n mode={background.mode}\n color={background.color}\n gradient={background.gradient}\n image={background.image}\n video={background.video}\n overlay={background.overlay}\n />\n\n {/* Content layer (above background) */}\n <div className=\"relative z-10\">\n <Component {...componentProps} />\n </div>\n </Wrapper>\n )\n }\n\n // No background - simpler render without extra wrapper\n return (\n <Wrapper {...wrapperElementProps}>\n <Component {...componentProps} />\n </Wrapper>\n )\n}\n","/**\n * Blocks\n *\n * Renders an array of blocks for a layout area (header, body, footer, panels).\n * Used by the Layout component to pre-render each area.\n */\n\nimport React from 'react'\nimport BlockRenderer from './BlockRenderer.jsx'\n\n/**\n * Render a list of blocks\n *\n * @param {Object} props\n * @param {Block[]} props.blocks - Array of Block instances to render\n * @param {Object} [props.extra] - Extra props to pass to each block\n */\nexport default function Blocks({ blocks, extra = {} }) {\n if (!blocks || blocks.length === 0) return null\n\n return blocks.map((block, index) => (\n <React.Fragment key={block.id || index}>\n <BlockRenderer block={block} extra={extra} />\n </React.Fragment>\n ))\n}\n","/**\n * Layout\n *\n * Orchestrates page rendering by assembling layout areas (header, body, footer, panels).\n * Supports foundation-provided custom Layout components via website.getRemoteLayout().\n *\n * Layout Areas:\n * - header: Top navigation, branding (from @header page)\n * - body: Main page content (from page sections)\n * - footer: Bottom navigation, copyright (from @footer page)\n * - left: Left sidebar/panel (from @left page)\n * - right: Right sidebar/panel (from @right page)\n *\n * Custom Layouts:\n * Foundations can provide a custom Layout via src/exports.js:\n *\n * ```jsx\n * // src/exports.js\n * import Layout from './components/Layout'\n *\n * export default {\n * Layout,\n * props: {\n * themeToggleEnabled: true,\n * }\n * }\n * ```\n *\n * The Layout component receives pre-rendered areas as props:\n * - page, website: Runtime context\n * - header, body, footer: Pre-rendered React elements\n * - left, right (or leftPanel, rightPanel): Sidebar panels\n */\n\nimport Blocks from './Blocks.jsx'\n\n/**\n * Default layout - renders header, body, footer in sequence\n * (no panels in default layout)\n */\nfunction DefaultLayout({ header, body, footer }) {\n return (\n <>\n {header && <header>{header}</header>}\n {body && <main>{body}</main>}\n {footer && <footer>{footer}</footer>}\n </>\n )\n}\n\n/**\n * Initialize all blocks to ensure cross-block communication works.\n * Must be called before rendering so getNextBlockInfo() can access sibling contexts.\n *\n * @param {Block[][]} blockGroups - Arrays of blocks from all layout areas\n */\nfunction initializeAllBlocks(...blockGroups) {\n for (const blocks of blockGroups) {\n if (!blocks) continue\n for (const block of blocks) {\n block.initComponent()\n }\n }\n}\n\n/**\n * Layout component\n *\n * @param {Object} props\n * @param {Page} props.page - Current page instance\n * @param {Website} props.website - Website instance\n */\nexport default function Layout({ page, website }) {\n // Check if foundation provides a custom Layout\n const RemoteLayout = website.getRemoteLayout()\n\n // Get block groups from page (respects layout preferences)\n const headerBlocks = page.getHeaderBlocks()\n const bodyBlocks = page.getBodyBlocks()\n const footerBlocks = page.getFooterBlocks()\n const leftBlocks = page.getLeftBlocks()\n const rightBlocks = page.getRightBlocks()\n\n // Pre-initialize all blocks before rendering any.\n // This ensures cross-block communication (getNextBlockInfo, getPrevBlockInfo)\n // can access sibling block contexts that are set in initComponent().\n initializeAllBlocks(headerBlocks, bodyBlocks, footerBlocks, leftBlocks, rightBlocks)\n\n // Pre-render each area as React elements\n const headerElement = headerBlocks ? <Blocks blocks={headerBlocks} /> : null\n const bodyElement = bodyBlocks ? <Blocks blocks={bodyBlocks} /> : null\n const footerElement = footerBlocks ? <Blocks blocks={footerBlocks} /> : null\n const leftElement = leftBlocks ? <Blocks blocks={leftBlocks} /> : null\n const rightElement = rightBlocks ? <Blocks blocks={rightBlocks} /> : null\n\n // Use foundation's custom Layout if provided\n if (RemoteLayout) {\n return (\n <RemoteLayout\n page={page}\n website={website}\n header={headerElement}\n body={bodyElement}\n footer={footerElement}\n left={leftElement}\n right={rightElement}\n // Aliases for backwards compatibility\n leftPanel={leftElement}\n rightPanel={rightElement}\n />\n )\n }\n\n // Default layout\n return (\n <DefaultLayout\n header={headerElement}\n body={bodyElement}\n footer={footerElement}\n />\n )\n}\n","/**\n * @uniweb/runtime/ssr - Server-Side Rendering Entry Point\n *\n * Node.js-compatible exports for SSG/prerendering.\n * This module is built to a standalone bundle that can be imported\n * directly by Node.js without Vite transpilation.\n *\n * Usage in prerender.js:\n * import { renderPage, Blocks, BlockRenderer } from '@uniweb/runtime/ssr'\n */\n\nimport React from 'react'\n\n// Props preparation (no browser APIs)\nexport {\n prepareProps,\n applySchemas,\n applyDefaults,\n guaranteeContentStructure,\n getComponentMeta,\n getComponentDefaults\n} from './prepare-props.js'\n\n// Components for rendering\nexport { default as BlockRenderer } from './components/BlockRenderer.jsx'\nexport { default as Blocks } from './components/Blocks.jsx'\nexport { default as Layout } from './components/Layout.jsx'\n\n// Re-export Layout's DefaultLayout for direct use\nimport LayoutComponent from './components/Layout.jsx'\n\n/**\n * Render a page to React elements\n *\n * This is the main entry point for SSG. It returns a React element\n * that can be passed to renderToString().\n *\n * @param {Object} props\n * @param {Page} props.page - The page instance to render\n * @param {Website} props.website - The website instance\n * @returns {React.ReactElement}\n */\nexport function PageElement({ page, website }) {\n return React.createElement(\n 'main',\n null,\n React.createElement(LayoutComponent, { page, website })\n )\n}\n"],"names":["meta","LayoutComponent"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/runtime",
3
- "version": "0.5.10",
3
+ "version": "0.5.11",
4
4
  "description": "Minimal runtime for loading Uniweb foundations",
5
5
  "type": "module",
6
6
  "exports": {
@@ -27,6 +27,20 @@ const OVERLAY_COLORS = {
27
27
  dark: 'rgba(0, 0, 0, 0.5)',
28
28
  }
29
29
 
30
+ /**
31
+ * Resolve a URL against the site's base path
32
+ * Prepends basePath to absolute URLs (starting with /) so they work
33
+ * under subdirectory deployments (e.g., /templates/international/)
34
+ */
35
+ function resolveUrl(url) {
36
+ if (!url || !url.startsWith('/')) return url
37
+ const basePath = globalThis.uniweb?.activeWebsite?.basePath || ''
38
+ if (!basePath) return url
39
+ // Avoid double-prepending
40
+ if (url.startsWith(basePath + '/') || url === basePath) return url
41
+ return basePath + url
42
+ }
43
+
30
44
  /**
31
45
  * Render gradient overlay
32
46
  */
@@ -161,7 +175,7 @@ function ImageBackground({ image }) {
161
175
  const style = {
162
176
  position: 'absolute',
163
177
  inset: 0,
164
- backgroundImage: `url(${src})`,
178
+ backgroundImage: `url(${resolveUrl(src)})`,
165
179
  backgroundPosition: position,
166
180
  backgroundSize: size,
167
181
  backgroundRepeat: 'no-repeat',
@@ -213,7 +227,10 @@ function VideoBackground({ video }) {
213
227
  }
214
228
 
215
229
  // Build source list: explicit sources array, or infer from src
216
- const sourceList = sources || inferSources(src)
230
+ const sourceList = (sources || inferSources(src)).map(s => ({
231
+ ...s,
232
+ src: resolveUrl(s.src)
233
+ }))
217
234
 
218
235
  return (
219
236
  <video
@@ -223,7 +240,7 @@ function VideoBackground({ video }) {
223
240
  loop={loop}
224
241
  muted={muted}
225
242
  playsInline
226
- poster={poster}
243
+ poster={resolveUrl(poster)}
227
244
  aria-hidden="true"
228
245
  >
229
246
  {sourceList.map(({ src: sourceSrc, type }, index) => (
@@ -142,6 +142,21 @@ export default function BlockRenderer({ block, pure = false, as = 'section', ext
142
142
  }
143
143
  }
144
144
 
145
+ const { background, ...wrapperProps } = getWrapperProps(block)
146
+
147
+ // Check if component handles its own background (background: 'self' in meta.js)
148
+ // Components that render their own background layer (solid colors, insets, effects)
149
+ // opt out so the runtime doesn't render an occluded layer underneath.
150
+ const meta = getComponentMeta(block.type)
151
+ const hasBackground = background?.mode && meta?.background !== 'self'
152
+
153
+ // Signal to the component that the author set a background in frontmatter.
154
+ // Components can check params._hasBackground to skip their own opaque bg color
155
+ // and let the engine background show through.
156
+ if (hasBackground) {
157
+ params = { ...params, _hasBackground: true }
158
+ }
159
+
145
160
  const componentProps = {
146
161
  content,
147
162
  params,
@@ -152,12 +167,6 @@ export default function BlockRenderer({ block, pure = false, as = 'section', ext
152
167
  return <Component {...componentProps} extra={extra} />
153
168
  }
154
169
 
155
- const { background, ...wrapperProps } = getWrapperProps(block)
156
-
157
- // Check if component handles its own background (background: 'manual' in meta.js)
158
- const meta = getComponentMeta(block.type)
159
- const hasBackground = background?.mode && meta?.background !== 'manual'
160
-
161
170
  // Determine wrapper element: string tag name, or Fragment if false
162
171
  const Wrapper = as === false ? React.Fragment : as
163
172
  // Fragment doesn't accept props, so only pass them for real elements
@@ -41,9 +41,9 @@ import Blocks from './Blocks.jsx'
41
41
  function DefaultLayout({ header, body, footer }) {
42
42
  return (
43
43
  <>
44
- {header}
45
- {body}
46
- {footer}
44
+ {header && <header>{header}</header>}
45
+ {body && <main>{body}</main>}
46
+ {footer && <footer>{footer}</footer>}
47
47
  </>
48
48
  )
49
49
  }