@uniweb/runtime 0.5.10 → 0.5.12
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 +62 -134
- package/dist/ssr.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Background.jsx +26 -3
- package/src/components/BlockRenderer.jsx +80 -65
- package/src/components/Layout.jsx +3 -3
- package/src/components/PageRenderer.jsx +4 -2
- package/src/hooks/useRememberScroll.js +2 -3
- package/src/index.jsx +15 -0
- package/src/prepare-props.js +0 -39
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,79 +122,19 @@ 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",
|
|
218
128
|
IMAGE: "image",
|
|
219
129
|
VIDEO: "video"
|
|
220
130
|
};
|
|
131
|
+
function resolveUrl(url) {
|
|
132
|
+
if (!url || !url.startsWith("/")) return url;
|
|
133
|
+
const basePath = globalThis.uniweb?.activeWebsite?.basePath || "";
|
|
134
|
+
if (!basePath) return url;
|
|
135
|
+
if (url.startsWith(basePath + "/") || url === basePath) return url;
|
|
136
|
+
return basePath + url;
|
|
137
|
+
}
|
|
221
138
|
function GradientOverlay({ gradient, opacity = 0.5 }) {
|
|
222
139
|
const {
|
|
223
140
|
start = "rgba(0,0,0,0.7)",
|
|
@@ -263,6 +180,10 @@ function ColorBackground({ color }) {
|
|
|
263
180
|
}
|
|
264
181
|
function GradientBackground({ gradient }) {
|
|
265
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
|
+
}
|
|
266
187
|
const {
|
|
267
188
|
start = "transparent",
|
|
268
189
|
end = "transparent",
|
|
@@ -307,7 +228,7 @@ function ImageBackground({ image }) {
|
|
|
307
228
|
const style = {
|
|
308
229
|
position: "absolute",
|
|
309
230
|
inset: 0,
|
|
310
|
-
backgroundImage: `url(${src})`,
|
|
231
|
+
backgroundImage: `url(${resolveUrl(src)})`,
|
|
311
232
|
backgroundPosition: position,
|
|
312
233
|
backgroundSize: size,
|
|
313
234
|
backgroundRepeat: "no-repeat"
|
|
@@ -338,7 +259,10 @@ function VideoBackground({ video }) {
|
|
|
338
259
|
height: "100%",
|
|
339
260
|
objectFit: "cover"
|
|
340
261
|
};
|
|
341
|
-
const sourceList = sources || inferSources(src)
|
|
262
|
+
const sourceList = (sources || inferSources(src)).map((s) => ({
|
|
263
|
+
...s,
|
|
264
|
+
src: resolveUrl(s.src)
|
|
265
|
+
}));
|
|
342
266
|
return /* @__PURE__ */ jsx(
|
|
343
267
|
"video",
|
|
344
268
|
{
|
|
@@ -348,7 +272,7 @@ function VideoBackground({ video }) {
|
|
|
348
272
|
loop,
|
|
349
273
|
muted,
|
|
350
274
|
playsInline: true,
|
|
351
|
-
poster,
|
|
275
|
+
poster: resolveUrl(poster),
|
|
352
276
|
"aria-hidden": "true",
|
|
353
277
|
children: sourceList.map(({ src: sourceSrc, type }, index) => /* @__PURE__ */ jsx("source", { src: sourceSrc, type }, index))
|
|
354
278
|
}
|
|
@@ -423,6 +347,11 @@ const getWrapperProps = (block) => {
|
|
|
423
347
|
if (background.mode) {
|
|
424
348
|
style.position = "relative";
|
|
425
349
|
}
|
|
350
|
+
if (block.contextOverrides) {
|
|
351
|
+
for (const [key, value] of Object.entries(block.contextOverrides)) {
|
|
352
|
+
style[`--${key}`] = value;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
426
355
|
const sectionId = block.stableId || block.id;
|
|
427
356
|
return {
|
|
428
357
|
id: `section-${sectionId}`,
|
|
@@ -432,54 +361,55 @@ const getWrapperProps = (block) => {
|
|
|
432
361
|
};
|
|
433
362
|
};
|
|
434
363
|
function BlockRenderer({ block, pure = false, as = "section", extra = {} }) {
|
|
435
|
-
const [runtimeData, setRuntimeData] = useState(null);
|
|
436
|
-
const [fetchError, setFetchError] = useState(null);
|
|
437
364
|
const Component = block.initComponent();
|
|
438
|
-
const
|
|
439
|
-
const
|
|
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);
|
|
440
369
|
useEffect(() => {
|
|
441
|
-
|
|
370
|
+
setAsyncData(null);
|
|
371
|
+
}, [block]);
|
|
372
|
+
useEffect(() => {
|
|
373
|
+
if (resolved.status !== "pending") return;
|
|
442
374
|
let cancelled = false;
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
if (result.error) {
|
|
447
|
-
setFetchError(result.error);
|
|
448
|
-
}
|
|
449
|
-
if (result.data) {
|
|
450
|
-
setRuntimeData({ [fetchConfig.schema]: result.data });
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
doFetch();
|
|
375
|
+
entityStore.fetch(block, meta).then((result) => {
|
|
376
|
+
if (!cancelled && result.data) setAsyncData(result.data);
|
|
377
|
+
});
|
|
454
378
|
return () => {
|
|
455
379
|
cancelled = true;
|
|
456
380
|
};
|
|
457
|
-
}, [
|
|
381
|
+
}, [block]);
|
|
382
|
+
const entityData = resolved.status === "ready" ? resolved.data : asyncData;
|
|
383
|
+
block.dataLoading = resolved.status === "pending" && !entityData;
|
|
458
384
|
if (!Component) {
|
|
459
385
|
return /* @__PURE__ */ jsxs("div", { className: "block-error", style: { padding: "1rem", background: "#fef2f2", color: "#dc2626" }, children: [
|
|
460
386
|
"Component not found: ",
|
|
461
387
|
block.type
|
|
462
388
|
] });
|
|
463
389
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
// Keep original for components that need raw access
|
|
478
|
-
};
|
|
479
|
-
if (runtimeData && shouldFetchAtRuntime) {
|
|
480
|
-
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
|
+
}
|
|
481
403
|
}
|
|
404
|
+
content.data = merged;
|
|
405
|
+
}
|
|
406
|
+
const { background, ...wrapperProps } = getWrapperProps(block);
|
|
407
|
+
const componentClassName = Component.className;
|
|
408
|
+
if (componentClassName) {
|
|
409
|
+
wrapperProps.className = wrapperProps.className ? `${wrapperProps.className} ${componentClassName}` : componentClassName;
|
|
482
410
|
}
|
|
411
|
+
const hasBackground = background?.mode && meta?.background !== "self";
|
|
412
|
+
block.hasBackground = hasBackground;
|
|
483
413
|
const componentProps = {
|
|
484
414
|
content,
|
|
485
415
|
params,
|
|
@@ -488,10 +418,8 @@ function BlockRenderer({ block, pure = false, as = "section", extra = {} }) {
|
|
|
488
418
|
if (pure) {
|
|
489
419
|
return /* @__PURE__ */ jsx(Component, { ...componentProps, extra });
|
|
490
420
|
}
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
-
const hasBackground = background?.mode && meta?.background !== "manual";
|
|
494
|
-
const Wrapper = as === false ? React.Fragment : as;
|
|
421
|
+
const componentAs = Component.as;
|
|
422
|
+
const Wrapper = as === false ? React.Fragment : as !== "section" ? as : componentAs || "section";
|
|
495
423
|
const wrapperElementProps = as === false ? {} : wrapperProps;
|
|
496
424
|
if (hasBackground) {
|
|
497
425
|
return /* @__PURE__ */ jsxs(Wrapper, { ...wrapperElementProps, children: [
|
|
@@ -517,9 +445,9 @@ function Blocks({ blocks, extra = {} }) {
|
|
|
517
445
|
}
|
|
518
446
|
function DefaultLayout({ header, body, footer }) {
|
|
519
447
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
520
|
-
header,
|
|
521
|
-
body,
|
|
522
|
-
footer
|
|
448
|
+
header && /* @__PURE__ */ jsx("header", { children: header }),
|
|
449
|
+
body && /* @__PURE__ */ jsx("main", { children: body }),
|
|
450
|
+
footer && /* @__PURE__ */ jsx("footer", { children: footer })
|
|
523
451
|
] });
|
|
524
452
|
}
|
|
525
453
|
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/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.
|
|
3
|
+
"version": "0.5.12",
|
|
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.
|
|
34
|
+
"@uniweb/core": "0.4.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@vitejs/plugin-react": "^4.5.2",
|
|
@@ -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
|
*/
|
|
@@ -100,6 +114,12 @@ function ColorBackground({ color }) {
|
|
|
100
114
|
function GradientBackground({ gradient }) {
|
|
101
115
|
if (!gradient) return null
|
|
102
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
|
+
|
|
103
123
|
const {
|
|
104
124
|
start = 'transparent',
|
|
105
125
|
end = 'transparent',
|
|
@@ -161,7 +181,7 @@ function ImageBackground({ image }) {
|
|
|
161
181
|
const style = {
|
|
162
182
|
position: 'absolute',
|
|
163
183
|
inset: 0,
|
|
164
|
-
backgroundImage: `url(${src})`,
|
|
184
|
+
backgroundImage: `url(${resolveUrl(src)})`,
|
|
165
185
|
backgroundPosition: position,
|
|
166
186
|
backgroundSize: size,
|
|
167
187
|
backgroundRepeat: 'no-repeat',
|
|
@@ -213,7 +233,10 @@ function VideoBackground({ video }) {
|
|
|
213
233
|
}
|
|
214
234
|
|
|
215
235
|
// Build source list: explicit sources array, or infer from src
|
|
216
|
-
const sourceList = sources || inferSources(src)
|
|
236
|
+
const sourceList = (sources || inferSources(src)).map(s => ({
|
|
237
|
+
...s,
|
|
238
|
+
src: resolveUrl(s.src)
|
|
239
|
+
}))
|
|
217
240
|
|
|
218
241
|
return (
|
|
219
242
|
<video
|
|
@@ -223,7 +246,7 @@ function VideoBackground({ video }) {
|
|
|
223
246
|
loop={loop}
|
|
224
247
|
muted={muted}
|
|
225
248
|
playsInline
|
|
226
|
-
poster={poster}
|
|
249
|
+
poster={resolveUrl(poster)}
|
|
227
250
|
aria-hidden="true"
|
|
228
251
|
>
|
|
229
252
|
{sourceList.map(({ src: sourceSrc, type }, index) => (
|
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Bridges Block data to foundation components.
|
|
5
5
|
* Handles theming, wrapper props, and runtime guarantees.
|
|
6
|
-
*
|
|
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
|
-
//
|
|
74
|
-
const
|
|
75
|
-
const
|
|
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
|
-
|
|
78
|
-
|
|
81
|
+
// Async data for when resolve returns 'pending' (runtime fetches)
|
|
82
|
+
const [asyncData, setAsyncData] = useState(null)
|
|
79
83
|
|
|
80
|
-
|
|
84
|
+
// Reset async data when block changes (SPA navigation)
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
setAsyncData(null)
|
|
87
|
+
}, [block])
|
|
81
88
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
// Fetch missing data asynchronously
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (resolved.status !== 'pending') return
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
100
|
+
// Use sync resolved data when available, fall back to async
|
|
101
|
+
const entityData = resolved.status === 'ready' ? resolved.data : asyncData
|
|
95
102
|
|
|
96
|
-
|
|
97
|
-
|
|
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,40 +113,51 @@ 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
|
|
112
|
-
// 2.
|
|
113
|
-
// 3.
|
|
114
|
-
|
|
115
|
-
let
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const { background, ...wrapperProps } = getWrapperProps(block)
|
|
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
|
|
143
149
|
}
|
|
144
150
|
|
|
151
|
+
// Check if component handles its own background (background: 'self' in meta.js)
|
|
152
|
+
// Components that render their own background layer (solid colors, insets, effects)
|
|
153
|
+
// opt out so the runtime doesn't render an occluded layer underneath.
|
|
154
|
+
const hasBackground = background?.mode && meta?.background !== 'self'
|
|
155
|
+
|
|
156
|
+
// Signal to the component that the engine is rendering a background.
|
|
157
|
+
// Components check block.hasBackground to skip their own opaque bg
|
|
158
|
+
// and let the engine background show through.
|
|
159
|
+
block.hasBackground = hasBackground
|
|
160
|
+
|
|
145
161
|
const componentProps = {
|
|
146
162
|
content,
|
|
147
163
|
params,
|
|
@@ -152,14 +168,13 @@ export default function BlockRenderer({ block, pure = false, as = 'section', ext
|
|
|
152
168
|
return <Component {...componentProps} extra={extra} />
|
|
153
169
|
}
|
|
154
170
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
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')
|
|
163
178
|
// Fragment doesn't accept props, so only pass them for real elements
|
|
164
179
|
const wrapperElementProps = as === false ? {} : wrapperProps
|
|
165
180
|
|
|
@@ -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
|
}
|
|
@@ -56,9 +56,11 @@ export default function PageRenderer() {
|
|
|
56
56
|
const website = uniweb?.activeWebsite
|
|
57
57
|
const siteName = website?.name || ''
|
|
58
58
|
|
|
59
|
-
//
|
|
60
|
-
// This
|
|
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
|
-
//
|
|
44
|
-
//
|
|
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)
|
package/src/prepare-props.js
CHANGED
|
@@ -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) {
|