@uniweb/runtime 0.5.11 → 0.5.13

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
@@ -106,33 +106,10 @@ function applyDefaults(params, defaults) {
106
106
  ...params || {}
107
107
  };
108
108
  }
109
- function applyCascadedData(localData, cascadedData, inheritData) {
110
- if (!inheritData || !cascadedData || Object.keys(cascadedData).length === 0) {
111
- return localData;
112
- }
113
- if (inheritData === true) {
114
- return { ...cascadedData, ...localData };
115
- }
116
- if (Array.isArray(inheritData)) {
117
- const result = { ...localData };
118
- for (const key of inheritData) {
119
- if (cascadedData[key] !== void 0 && result[key] === void 0) {
120
- result[key] = cascadedData[key];
121
- }
122
- }
123
- return result;
124
- }
125
- return localData;
126
- }
127
109
  function prepareProps(block, meta) {
128
110
  const defaults = meta?.defaults || {};
129
111
  const params = applyDefaults(block.properties, defaults);
130
112
  const content = guaranteeContentStructure(block.parsedContent);
131
- const inheritData = meta?.inheritData;
132
- const cascadedData = block.cascadedData || {};
133
- if (inheritData) {
134
- content.data = applyCascadedData(content.data, cascadedData, inheritData);
135
- }
136
113
  const schemas = meta?.schemas || null;
137
114
  if (schemas && content.data) {
138
115
  content.data = applySchemas(content.data, schemas);
@@ -145,73 +122,6 @@ function getComponentMeta(componentName) {
145
122
  function getComponentDefaults(componentName) {
146
123
  return globalThis.uniweb?.getComponentDefaults?.(componentName) || {};
147
124
  }
148
- function getNestedValue(obj, path) {
149
- if (!obj || !path) return obj;
150
- const parts = path.split(".");
151
- let current = obj;
152
- for (const part of parts) {
153
- if (current === null || current === void 0) return void 0;
154
- current = current[part];
155
- }
156
- return current;
157
- }
158
- async function executeFetchClient(config) {
159
- if (!config) return { data: null };
160
- const { path, url, transform } = config;
161
- try {
162
- let fetchUrl = path || url;
163
- if (!fetchUrl) {
164
- return { data: [], error: "No path or url specified" };
165
- }
166
- if (fetchUrl.startsWith("/") && !fetchUrl.startsWith("//")) {
167
- const baseUrl = "/";
168
- fetchUrl = baseUrl.replace(/\/$/, "") + fetchUrl;
169
- }
170
- const response = await fetch(fetchUrl);
171
- if (!response.ok) {
172
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
173
- }
174
- const contentType = response.headers.get("content-type") || "";
175
- let data;
176
- if (contentType.includes("application/json")) {
177
- data = await response.json();
178
- } else {
179
- const text = await response.text();
180
- try {
181
- data = JSON.parse(text);
182
- } catch {
183
- console.warn("[data-fetcher] Response is not JSON, returning as text");
184
- data = text;
185
- }
186
- }
187
- if (transform && data) {
188
- data = getNestedValue(data, transform);
189
- }
190
- return { data: data ?? [] };
191
- } catch (error) {
192
- console.warn(`[data-fetcher] Client fetch failed: ${error.message}`);
193
- return { data: [], error: error.message };
194
- }
195
- }
196
- function mergeIntoData(currentData, fetchedData, schema, merge = false) {
197
- if (fetchedData === null || fetchedData === void 0 || !schema) {
198
- return currentData;
199
- }
200
- const result = { ...currentData || {} };
201
- if (merge && result[schema] !== void 0) {
202
- const existing = result[schema];
203
- if (Array.isArray(existing) && Array.isArray(fetchedData)) {
204
- result[schema] = [...existing, ...fetchedData];
205
- } else if (typeof existing === "object" && existing !== null && typeof fetchedData === "object" && fetchedData !== null && !Array.isArray(existing) && !Array.isArray(fetchedData)) {
206
- result[schema] = { ...existing, ...fetchedData };
207
- } else {
208
- result[schema] = fetchedData;
209
- }
210
- } else {
211
- result[schema] = fetchedData;
212
- }
213
- return result;
214
- }
215
125
  const MODES = {
216
126
  COLOR: "color",
217
127
  GRADIENT: "gradient",
@@ -270,6 +180,10 @@ function ColorBackground({ color }) {
270
180
  }
271
181
  function GradientBackground({ gradient }) {
272
182
  if (!gradient) return null;
183
+ if (typeof gradient === "string") {
184
+ const style2 = { position: "absolute", inset: 0, background: gradient };
185
+ return /* @__PURE__ */ jsx("div", { className: "background-gradient", style: style2, "aria-hidden": "true" });
186
+ }
273
187
  const {
274
188
  start = "transparent",
275
189
  end = "transparent",
@@ -433,6 +347,11 @@ const getWrapperProps = (block) => {
433
347
  if (background.mode) {
434
348
  style.position = "relative";
435
349
  }
350
+ if (block.contextOverrides) {
351
+ for (const [key, value] of Object.entries(block.contextOverrides)) {
352
+ style[`--${key}`] = value;
353
+ }
354
+ }
436
355
  const sectionId = block.stableId || block.id;
437
356
  return {
438
357
  id: `section-${sectionId}`,
@@ -442,60 +361,55 @@ const getWrapperProps = (block) => {
442
361
  };
443
362
  };
444
363
  function BlockRenderer({ block, pure = false, as = "section", extra = {} }) {
445
- const [runtimeData, setRuntimeData] = useState(null);
446
- const [fetchError, setFetchError] = useState(null);
447
364
  const Component = block.initComponent();
448
- const fetchConfig = block.fetch;
449
- const shouldFetchAtRuntime = fetchConfig && fetchConfig.prerender === false;
365
+ const entityStore = block.website.entityStore;
366
+ const meta = getComponentMeta(block.type);
367
+ const resolved = entityStore.resolve(block, meta);
368
+ const [asyncData, setAsyncData] = useState(null);
369
+ useEffect(() => {
370
+ setAsyncData(null);
371
+ }, [block]);
450
372
  useEffect(() => {
451
- if (!shouldFetchAtRuntime) return;
373
+ if (resolved.status !== "pending") return;
452
374
  let cancelled = false;
453
- async function doFetch() {
454
- const result = await executeFetchClient(fetchConfig);
455
- if (cancelled) return;
456
- if (result.error) {
457
- setFetchError(result.error);
458
- }
459
- if (result.data) {
460
- setRuntimeData({ [fetchConfig.schema]: result.data });
461
- }
462
- }
463
- doFetch();
375
+ entityStore.fetch(block, meta).then((result) => {
376
+ if (!cancelled && result.data) setAsyncData(result.data);
377
+ });
464
378
  return () => {
465
379
  cancelled = true;
466
380
  };
467
- }, [shouldFetchAtRuntime, fetchConfig]);
381
+ }, [block]);
382
+ const entityData = resolved.status === "ready" ? resolved.data : asyncData;
383
+ block.dataLoading = resolved.status === "pending" && !entityData;
468
384
  if (!Component) {
469
385
  return /* @__PURE__ */ jsxs("div", { className: "block-error", style: { padding: "1rem", background: "#fef2f2", color: "#dc2626" }, children: [
470
386
  "Component not found: ",
471
387
  block.type
472
388
  ] });
473
389
  }
474
- let content, params;
475
- if (block.parsedContent?._isPoc) {
476
- content = block.parsedContent._pocContent;
477
- params = block.properties;
478
- } else {
479
- const meta2 = getComponentMeta(block.type);
480
- const prepared = prepareProps(block, meta2);
481
- params = prepared.params;
482
- content = {
483
- ...prepared.content,
484
- ...block.properties,
485
- // Frontmatter params overlay (legacy support)
486
- _prosemirror: block.parsedContent
487
- // Keep original for components that need raw access
488
- };
489
- if (runtimeData && shouldFetchAtRuntime) {
490
- content.data = mergeIntoData(content.data, runtimeData[fetchConfig.schema], fetchConfig.schema, fetchConfig.merge);
390
+ const prepared = prepareProps(block, meta);
391
+ let params = prepared.params;
392
+ let content = {
393
+ ...prepared.content,
394
+ ...block.properties
395
+ // Frontmatter params overlay (legacy support)
396
+ };
397
+ if (entityData) {
398
+ const merged = { ...content.data };
399
+ for (const key of Object.keys(entityData)) {
400
+ if (merged[key] === void 0) {
401
+ merged[key] = entityData[key];
402
+ }
491
403
  }
404
+ content.data = merged;
492
405
  }
493
406
  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 };
407
+ const componentClassName = Component.className;
408
+ if (componentClassName) {
409
+ wrapperProps.className = wrapperProps.className ? `${wrapperProps.className} ${componentClassName}` : componentClassName;
498
410
  }
411
+ const hasBackground = background?.mode && meta?.background !== "self";
412
+ block.hasBackground = hasBackground;
499
413
  const componentProps = {
500
414
  content,
501
415
  params,
@@ -504,7 +418,8 @@ function BlockRenderer({ block, pure = false, as = "section", extra = {} }) {
504
418
  if (pure) {
505
419
  return /* @__PURE__ */ jsx(Component, { ...componentProps, extra });
506
420
  }
507
- const Wrapper = as === false ? React.Fragment : as;
421
+ const componentAs = Component.as;
422
+ const Wrapper = as === false ? React.Fragment : as !== "section" ? as : componentAs || "section";
508
423
  const wrapperElementProps = as === false ? {} : wrapperProps;
509
424
  if (hasBackground) {
510
425
  return /* @__PURE__ */ jsxs(Wrapper, { ...wrapperElementProps, children: [
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 * 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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"}
1
+ {"version":3,"file":"ssr.js","sources":["../src/prepare-props.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 * 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 * 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 // Raw CSS gradient string (e.g., \"linear-gradient(to bottom, #000, #333)\")\n if (typeof gradient === 'string') {\n const style = { position: 'absolute', inset: 0, background: gradient }\n return <div className=\"background-gradient\" style={style} aria-hidden=\"true\" />\n }\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 * Uses EntityStore for entity-aware data resolution.\n */\n\nimport React, { useState, useEffect } from 'react'\nimport { prepareProps, getComponentMeta } from '../prepare-props.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 // Apply context overrides as inline CSS custom properties.\n // These override the context class tokens for this specific section.\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 // 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 const Component = block.initComponent()\n\n // Entity-aware data resolution via EntityStore\n const entityStore = block.website.entityStore\n const meta = getComponentMeta(block.type)\n const resolved = entityStore.resolve(block, meta)\n\n // Async data for when resolve returns 'pending' (runtime fetches)\n const [asyncData, setAsyncData] = useState(null)\n\n // Reset async data when block changes (SPA navigation)\n useEffect(() => {\n setAsyncData(null)\n }, [block])\n\n // Fetch missing data asynchronously\n useEffect(() => {\n if (resolved.status !== 'pending') return\n\n let cancelled = false\n entityStore.fetch(block, meta).then((result) => {\n if (!cancelled && result.data) setAsyncData(result.data)\n })\n return () => { cancelled = true }\n }, [block])\n\n // Use sync resolved data when available, fall back to async\n const entityData = resolved.status === 'ready' ? resolved.data : asyncData\n\n // Signal to component that data is loading\n block.dataLoading = resolved.status === 'pending' && !entityData\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 - semantic parser output (flat: title, paragraphs, links, etc.)\n // 2. block.properties - params from frontmatter (theme, alignment, etc.)\n // 3. meta - defaults from component meta.js\n const prepared = prepareProps(block, meta)\n let params = prepared.params\n\n let content = {\n ...prepared.content,\n ...block.properties, // Frontmatter params overlay (legacy support)\n }\n\n // Merge entity data resolved by EntityStore\n // Only fill in keys that don't already exist — section-level fetch data\n // (already in content.data from parsedContent) takes priority over inherited data.\n if (entityData) {\n const merged = { ...content.data }\n for (const key of Object.keys(entityData)) {\n if (merged[key] === undefined) {\n merged[key] = entityData[key]\n }\n }\n content.data = merged\n }\n\n const { background, ...wrapperProps } = getWrapperProps(block)\n\n // Merge Component.className (static classes declared on the component function)\n // Order: context-{theme} + block.state.className + Component.className\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 (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 hasBackground = background?.mode && meta?.background !== 'self'\n\n // Signal to the component that the engine is rendering a background.\n // Components check block.hasBackground to skip their own opaque bg\n // and let the engine background show through.\n block.hasBackground = hasBackground\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:\n // - as={false} → Fragment (no wrapper)\n // - as prop explicitly set (not default 'section') → use as prop\n // - Component.as → use component's declared tag\n // - fallback → 'section'\n const componentAs = Component.as\n const Wrapper = as === false ? React.Fragment : (as !== 'section' ? as : componentAs || 'section')\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":["style","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.11",
3
+ "version": "0.5.13",
4
4
  "description": "Minimal runtime for loading Uniweb foundations",
5
5
  "type": "module",
6
6
  "exports": {
@@ -31,7 +31,7 @@
31
31
  "node": ">=20.19"
32
32
  },
33
33
  "dependencies": {
34
- "@uniweb/core": "0.3.10"
34
+ "@uniweb/core": "0.4.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@vitejs/plugin-react": "^4.5.2",
@@ -114,6 +114,12 @@ function ColorBackground({ color }) {
114
114
  function GradientBackground({ gradient }) {
115
115
  if (!gradient) return null
116
116
 
117
+ // Raw CSS gradient string (e.g., "linear-gradient(to bottom, #000, #333)")
118
+ if (typeof gradient === 'string') {
119
+ const style = { position: 'absolute', inset: 0, background: gradient }
120
+ return <div className="background-gradient" style={style} aria-hidden="true" />
121
+ }
122
+
117
123
  const {
118
124
  start = 'transparent',
119
125
  end = 'transparent',
@@ -3,12 +3,11 @@
3
3
  *
4
4
  * Bridges Block data to foundation components.
5
5
  * Handles theming, wrapper props, and runtime guarantees.
6
- * Supports runtime data fetching for prerender: false configs.
6
+ * Uses EntityStore for entity-aware data resolution.
7
7
  */
8
8
 
9
9
  import React, { useState, useEffect } from 'react'
10
10
  import { prepareProps, getComponentMeta } from '../prepare-props.js'
11
- import { executeFetchClient, mergeIntoData } from '../data-fetcher-client.js'
12
11
  import Background from './Background.jsx'
13
12
 
14
13
  /**
@@ -42,6 +41,14 @@ const getWrapperProps = (block) => {
42
41
  style.position = 'relative'
43
42
  }
44
43
 
44
+ // Apply context overrides as inline CSS custom properties.
45
+ // These override the context class tokens for this specific section.
46
+ if (block.contextOverrides) {
47
+ for (const [key, value] of Object.entries(block.contextOverrides)) {
48
+ style[`--${key}`] = value
49
+ }
50
+ }
51
+
45
52
  // Use stableId for DOM ID if available (stable across reordering)
46
53
  // Falls back to positional id for backwards compatibility
47
54
  const sectionId = block.stableId || block.id
@@ -64,39 +71,37 @@ const getWrapperProps = (block) => {
64
71
  * @param {Object} props.extra - Extra props to pass to the component
65
72
  */
66
73
  export default function BlockRenderer({ block, pure = false, as = 'section', extra = {} }) {
67
- // State for runtime-fetched data (when prerender: false)
68
- const [runtimeData, setRuntimeData] = useState(null)
69
- const [fetchError, setFetchError] = useState(null)
70
-
71
74
  const Component = block.initComponent()
72
75
 
73
- // Runtime fetch for prerender: false configurations
74
- const fetchConfig = block.fetch
75
- const shouldFetchAtRuntime = fetchConfig && fetchConfig.prerender === false
76
+ // Entity-aware data resolution via EntityStore
77
+ const entityStore = block.website.entityStore
78
+ const meta = getComponentMeta(block.type)
79
+ const resolved = entityStore.resolve(block, meta)
76
80
 
77
- useEffect(() => {
78
- if (!shouldFetchAtRuntime) return
81
+ // Async data for when resolve returns 'pending' (runtime fetches)
82
+ const [asyncData, setAsyncData] = useState(null)
79
83
 
80
- let cancelled = false
84
+ // Reset async data when block changes (SPA navigation)
85
+ useEffect(() => {
86
+ setAsyncData(null)
87
+ }, [block])
81
88
 
82
- async function doFetch() {
83
- const result = await executeFetchClient(fetchConfig)
84
- if (cancelled) return
89
+ // Fetch missing data asynchronously
90
+ useEffect(() => {
91
+ if (resolved.status !== 'pending') return
85
92
 
86
- if (result.error) {
87
- setFetchError(result.error)
88
- }
89
- if (result.data) {
90
- setRuntimeData({ [fetchConfig.schema]: result.data })
91
- }
92
- }
93
+ let cancelled = false
94
+ entityStore.fetch(block, meta).then((result) => {
95
+ if (!cancelled && result.data) setAsyncData(result.data)
96
+ })
97
+ return () => { cancelled = true }
98
+ }, [block])
93
99
 
94
- doFetch()
100
+ // Use sync resolved data when available, fall back to async
101
+ const entityData = resolved.status === 'ready' ? resolved.data : asyncData
95
102
 
96
- return () => {
97
- cancelled = true
98
- }
99
- }, [shouldFetchAtRuntime, fetchConfig])
103
+ // Signal to component that data is loading
104
+ block.dataLoading = resolved.status === 'pending' && !entityData
100
105
 
101
106
  if (!Component) {
102
107
  return (
@@ -108,54 +113,50 @@ export default function BlockRenderer({ block, pure = false, as = 'section', ext
108
113
 
109
114
  // Build content and params with runtime guarantees
110
115
  // Sources:
111
- // 1. parsedContent._isPoc - simple PoC format (hardcoded content)
112
- // 2. parsedContent - semantic parser output (flat: title, paragraphs, links, etc.)
113
- // 3. block.properties - params from frontmatter (theme, alignment, etc.)
114
- // 4. meta - defaults from component meta.js
115
- let content, params
116
-
117
- if (block.parsedContent?._isPoc) {
118
- // Simple PoC format - content was passed directly
119
- content = block.parsedContent._pocContent
120
- params = block.properties
121
- } else {
122
- // Get runtime metadata for this component (has defaults, data binding, etc.)
123
- const meta = getComponentMeta(block.type)
124
-
125
- // Prepare props with runtime guarantees:
126
- // - Apply param defaults from meta.js
127
- // - Guarantee content structure exists
128
- // - Apply cascaded data based on inheritData
129
- const prepared = prepareProps(block, meta)
130
- params = prepared.params
131
-
132
- // Merge prepared content with raw access for components that need it
133
- content = {
134
- ...prepared.content,
135
- ...block.properties, // Frontmatter params overlay (legacy support)
136
- _prosemirror: block.parsedContent // Keep original for components that need raw access
137
- }
116
+ // 1. parsedContent - semantic parser output (flat: title, paragraphs, links, etc.)
117
+ // 2. block.properties - params from frontmatter (theme, alignment, etc.)
118
+ // 3. meta - defaults from component meta.js
119
+ const prepared = prepareProps(block, meta)
120
+ let params = prepared.params
121
+
122
+ let content = {
123
+ ...prepared.content,
124
+ ...block.properties, // Frontmatter params overlay (legacy support)
125
+ }
138
126
 
139
- // Merge runtime-fetched data if available
140
- if (runtimeData && shouldFetchAtRuntime) {
141
- content.data = mergeIntoData(content.data, runtimeData[fetchConfig.schema], fetchConfig.schema, fetchConfig.merge)
127
+ // Merge entity data resolved by EntityStore
128
+ // Only fill in keys that don't already exist — section-level fetch data
129
+ // (already in content.data from parsedContent) takes priority over inherited data.
130
+ if (entityData) {
131
+ const merged = { ...content.data }
132
+ for (const key of Object.keys(entityData)) {
133
+ if (merged[key] === undefined) {
134
+ merged[key] = entityData[key]
135
+ }
142
136
  }
137
+ content.data = merged
143
138
  }
144
139
 
145
140
  const { background, ...wrapperProps } = getWrapperProps(block)
146
141
 
142
+ // Merge Component.className (static classes declared on the component function)
143
+ // Order: context-{theme} + block.state.className + Component.className
144
+ const componentClassName = Component.className
145
+ if (componentClassName) {
146
+ wrapperProps.className = wrapperProps.className
147
+ ? `${wrapperProps.className} ${componentClassName}`
148
+ : componentClassName
149
+ }
150
+
147
151
  // Check if component handles its own background (background: 'self' in meta.js)
148
152
  // Components that render their own background layer (solid colors, insets, effects)
149
153
  // opt out so the runtime doesn't render an occluded layer underneath.
150
- const meta = getComponentMeta(block.type)
151
154
  const hasBackground = background?.mode && meta?.background !== 'self'
152
155
 
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
156
+ // Signal to the component that the engine is rendering a background.
157
+ // Components check block.hasBackground to skip their own opaque bg
155
158
  // and let the engine background show through.
156
- if (hasBackground) {
157
- params = { ...params, _hasBackground: true }
158
- }
159
+ block.hasBackground = hasBackground
159
160
 
160
161
  const componentProps = {
161
162
  content,
@@ -167,8 +168,13 @@ export default function BlockRenderer({ block, pure = false, as = 'section', ext
167
168
  return <Component {...componentProps} extra={extra} />
168
169
  }
169
170
 
170
- // Determine wrapper element: string tag name, or Fragment if false
171
- const Wrapper = as === false ? React.Fragment : as
171
+ // Determine wrapper element:
172
+ // - as={false} Fragment (no wrapper)
173
+ // - as prop explicitly set (not default 'section') → use as prop
174
+ // - Component.as → use component's declared tag
175
+ // - fallback → 'section'
176
+ const componentAs = Component.as
177
+ const Wrapper = as === false ? React.Fragment : (as !== 'section' ? as : componentAs || 'section')
172
178
  // Fragment doesn't accept props, so only pass them for real elements
173
179
  const wrapperElementProps = as === false ? {} : wrapperProps
174
180
 
@@ -56,9 +56,11 @@ export default function PageRenderer() {
56
56
  const website = uniweb?.activeWebsite
57
57
  const siteName = website?.name || ''
58
58
 
59
- // Get page from current URL path (not the potentially stale website.activePage)
60
- // This ensures correct page renders immediately on client-side navigation
59
+ // Resolve page from current URL and sync website.activePage immediately.
60
+ // This must happen synchronously (not in an effect) so child components
61
+ // like Header see the correct activePage during the same render cycle.
61
62
  let page = website?.getPage(location.pathname)
63
+ if (page && website) website.setActivePage(location.pathname)
62
64
 
63
65
  // If no page found, try the 404 page (do NOT fall back to activePage/homepage)
64
66
  const isNotFound = !page
@@ -40,9 +40,8 @@ export function useRememberScroll(options = {}) {
40
40
  const previousPath = previousPathRef.current
41
41
  const currentPath = location.pathname
42
42
 
43
- // Sync active page with current route
44
- // This keeps website.activePage in sync for code that depends on it
45
- website.setActivePage(currentPath)
43
+ // website.activePage is synced by PageRenderer during render.
44
+ // By the time this effect runs, activePage is already correct.
46
45
  const currentPage = website.activePage
47
46
 
48
47
  // Skip on first render
package/src/index.jsx CHANGED
@@ -17,6 +17,9 @@ import {
17
17
  useLocation
18
18
  } from 'react-router-dom'
19
19
 
20
+ // Data fetcher (registered on dataStore so core can call it without importing runtime)
21
+ import { executeFetchClient } from './data-fetcher-client.js'
22
+
20
23
  // Components
21
24
  import { ChildBlocks } from './components/PageRenderer.jsx'
22
25
  import WebsiteRenderer from './components/WebsiteRenderer.jsx'
@@ -230,6 +233,13 @@ function initUniweb(configData) {
230
233
  // Create singleton via @uniweb/core (also assigns to globalThis.uniweb)
231
234
  const uniwebInstance = createUniweb(configData)
232
235
 
236
+ // Pre-populate DataStore from build-time fetched data
237
+ if (configData.fetchedData && uniwebInstance.activeWebsite?.dataStore) {
238
+ for (const entry of configData.fetchedData) {
239
+ uniwebInstance.activeWebsite.dataStore.set(entry.config, entry.data)
240
+ }
241
+ }
242
+
233
243
  // Set up child block renderer for nested blocks
234
244
  uniwebInstance.childBlockRenderer = ChildBlocks
235
245
 
@@ -305,6 +315,11 @@ function render({ development = false, basename } = {}) {
305
315
  if (website.setBasePath) {
306
316
  website.setBasePath(routerBasename || '')
307
317
  }
318
+
319
+ // Register data fetcher on the DataStore so BlockRenderer can use it
320
+ if (website.dataStore) {
321
+ website.dataStore.registerFetcher(executeFetchClient)
322
+ }
308
323
  }
309
324
 
310
325
  const root = createRoot(container)
@@ -186,38 +186,6 @@ export function applyDefaults(params, defaults) {
186
186
  }
187
187
  }
188
188
 
189
- /**
190
- * Apply cascaded data based on component's inheritData setting
191
- *
192
- * @param {Object} localData - content.data from the section itself
193
- * @param {Object} cascadedData - Data from page/site level fetches
194
- * @param {boolean|Array} inheritData - Component's inheritData setting
195
- * @returns {Object} Merged data object
196
- */
197
- function applyCascadedData(localData, cascadedData, inheritData) {
198
- if (!inheritData || !cascadedData || Object.keys(cascadedData).length === 0) {
199
- return localData
200
- }
201
-
202
- if (inheritData === true) {
203
- // Inherit all: cascaded data as base, local data overrides
204
- return { ...cascadedData, ...localData }
205
- }
206
-
207
- if (Array.isArray(inheritData)) {
208
- // Selective: only specified schemas, local data takes precedence
209
- const result = { ...localData }
210
- for (const key of inheritData) {
211
- if (cascadedData[key] !== undefined && result[key] === undefined) {
212
- result[key] = cascadedData[key]
213
- }
214
- }
215
- return result
216
- }
217
-
218
- return localData
219
- }
220
-
221
189
  /**
222
190
  * Prepare props for a component with runtime guarantees
223
191
  *
@@ -233,13 +201,6 @@ export function prepareProps(block, meta) {
233
201
  // Guarantee content structure
234
202
  const content = guaranteeContentStructure(block.parsedContent)
235
203
 
236
- // Apply cascaded data based on component's inheritData setting
237
- const inheritData = meta?.inheritData
238
- const cascadedData = block.cascadedData || {}
239
- if (inheritData) {
240
- content.data = applyCascadedData(content.data, cascadedData, inheritData)
241
- }
242
-
243
204
  // Apply schemas to content.data
244
205
  const schemas = meta?.schemas || null
245
206
  if (schemas && content.data) {