@uniweb/runtime 0.9.4 → 0.9.6
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/app/_importmap/@uniweb-core.js +1 -1
- package/dist/app/assets/core-BI26Dvfl.js +133 -0
- package/dist/app/assets/{index-C2Sw5X24.js → index-C-EVgXSD.js} +18 -18
- package/dist/app/index.html +2 -2
- package/dist/app/manifest.json +3 -3
- package/dist/shims/uniweb-core.js +7 -0
- package/dist/ssr.js +12 -0
- package/dist/ssr.js.map +1 -1
- package/dist/worker-runtime.js +328 -89
- package/package.json +4 -3
- package/src/ssr-renderer.js +45 -0
- package/src/ssr.js +6 -1
- package/dist/app/_importmap/@uniweb-core.js.map +0 -1
- package/dist/app/_importmap/react-dom-server.js.map +0 -1
- package/dist/app/_importmap/react-dom.js.map +0 -1
- package/dist/app/_importmap/react-jsx-dev-runtime.js.map +0 -1
- package/dist/app/_importmap/react-jsx-runtime.js.map +0 -1
- package/dist/app/_importmap/react.js.map +0 -1
- package/dist/app/assets/_commonjsHelpers-CqkleIqs.js.map +0 -1
- package/dist/app/assets/_importmap_react-CrAkajDz.js.map +0 -1
- package/dist/app/assets/core-CSPVyulX.js +0 -133
- package/dist/app/assets/core-CSPVyulX.js.map +0 -1
- package/dist/app/assets/index-C2Sw5X24.js.map +0 -1
- package/dist/app/assets/index-CxUeIbEw.js.map +0 -1
- package/dist/app/assets/index-VvOM7xKK.js.map +0 -1
- package/dist/app/assets/jsx-runtime-0PlsXv6K.js.map +0 -1
package/dist/worker-runtime.js
CHANGED
|
@@ -16362,6 +16362,8 @@ var __P2 = __toESM(require_jsx_runtime(), 1);
|
|
|
16362
16362
|
var src_exports = {};
|
|
16363
16363
|
__export(src_exports, {
|
|
16364
16364
|
Block: () => Block,
|
|
16365
|
+
DATA_DIR: () => DATA_DIR,
|
|
16366
|
+
DATA_URL_PREFIX: () => DATA_URL_PREFIX,
|
|
16365
16367
|
DataStore: () => DataStore,
|
|
16366
16368
|
EntityStore: () => EntityStore,
|
|
16367
16369
|
FetcherDispatcher: () => FetcherDispatcher,
|
|
@@ -16370,16 +16372,21 @@ __export(src_exports, {
|
|
|
16370
16372
|
Theme: () => Theme,
|
|
16371
16373
|
Uniweb: () => Uniweb,
|
|
16372
16374
|
Website: () => Website,
|
|
16375
|
+
collectionDataUrl: () => collectionDataUrl,
|
|
16376
|
+
collectionNameFromUrl: () => collectionNameFromUrl,
|
|
16373
16377
|
createUniweb: () => createUniweb,
|
|
16374
16378
|
deriveCacheKey: () => deriveCacheKey,
|
|
16375
16379
|
evaluateWhere: () => evaluate,
|
|
16376
16380
|
getUniweb: () => getUniweb,
|
|
16377
16381
|
hasDarkScheme: () => hasDarkScheme,
|
|
16382
|
+
isDataUrl: () => isDataUrl,
|
|
16378
16383
|
isRichSchema: () => isRichSchema,
|
|
16379
16384
|
isWildcardLanguages: () => isWildcardLanguages,
|
|
16380
16385
|
listRequestStyleNames: () => listStyleNames,
|
|
16381
16386
|
matchWhere: () => match,
|
|
16382
16387
|
normalizeLanguageList: () => normalizeLanguageList,
|
|
16388
|
+
normalizeSchema: () => normalizeSchema,
|
|
16389
|
+
recordDataUrl: () => recordDataUrl,
|
|
16383
16390
|
resolveDefaultLocale: () => resolveDefaultLocale,
|
|
16384
16391
|
resolvePublishableLocales: () => resolvePublishableLocales,
|
|
16385
16392
|
resolveRequestStyle: () => resolveStyle,
|
|
@@ -22732,6 +22739,13 @@ function createSequenceElement(node, options = {}) {
|
|
|
22732
22739
|
children: processSequence({ content })
|
|
22733
22740
|
};
|
|
22734
22741
|
}
|
|
22742
|
+
case "concept_block": {
|
|
22743
|
+
return {
|
|
22744
|
+
type: "concept_block",
|
|
22745
|
+
tag: attrs?.tag,
|
|
22746
|
+
children: processSequence({ content }, options)
|
|
22747
|
+
};
|
|
22748
|
+
}
|
|
22735
22749
|
case "dataBlock":
|
|
22736
22750
|
return {
|
|
22737
22751
|
type: "dataBlock",
|
|
@@ -22773,6 +22787,12 @@ function createSequenceElement(node, options = {}) {
|
|
|
22773
22787
|
attrs: parseImgBlock(attrs)
|
|
22774
22788
|
};
|
|
22775
22789
|
case "image":
|
|
22790
|
+
if (attrs?.role === "video") {
|
|
22791
|
+
return {
|
|
22792
|
+
type: "video",
|
|
22793
|
+
attrs: parseMarkdownVideo(attrs)
|
|
22794
|
+
};
|
|
22795
|
+
}
|
|
22776
22796
|
return {
|
|
22777
22797
|
type: "image",
|
|
22778
22798
|
attrs: parseImgBlock(attrs || {})
|
|
@@ -22798,10 +22818,32 @@ function createSequenceElement(node, options = {}) {
|
|
|
22798
22818
|
attrs
|
|
22799
22819
|
};
|
|
22800
22820
|
}
|
|
22821
|
+
case "table": {
|
|
22822
|
+
const rows = (content || []).filter((row) => row.type === "tableRow").map((row) => ({
|
|
22823
|
+
cells: (row.content || []).filter((cell) => cell.type === "tableCell").map((cell) => ({
|
|
22824
|
+
children: processSequence({ content: cell.content }, options),
|
|
22825
|
+
header: cell.attrs?.header === true,
|
|
22826
|
+
align: cell.attrs?.align || null,
|
|
22827
|
+
colspan: cell.attrs?.colspan || 1,
|
|
22828
|
+
rowspan: cell.attrs?.rowspan || 1
|
|
22829
|
+
}))
|
|
22830
|
+
}));
|
|
22831
|
+
return { type: "table", rows, attrs };
|
|
22832
|
+
}
|
|
22833
|
+
// `divider` is what content-reader emits; `horizontalRule` and
|
|
22834
|
+
// `DividerBlock` are the editor's spellings. All three are one element.
|
|
22835
|
+
//
|
|
22836
|
+
// The reader's own name was NOT listed here until 2026-07-30 — markdown
|
|
22837
|
+
// dividers reached consumers only by falling through `default:`, which
|
|
22838
|
+
// happens to produce `{ type: 'divider', content: '' }` and happens to
|
|
22839
|
+
// match what everything downstream keys on. It worked by coincidence,
|
|
22840
|
+
// and the coincidence would have broken the moment `default:` changed.
|
|
22841
|
+
case "divider":
|
|
22801
22842
|
case "DividerBlock":
|
|
22802
22843
|
case "horizontalRule":
|
|
22803
22844
|
return {
|
|
22804
|
-
type: "divider"
|
|
22845
|
+
type: "divider",
|
|
22846
|
+
attrs
|
|
22805
22847
|
};
|
|
22806
22848
|
// Custom TipTap elements
|
|
22807
22849
|
case "card-group":
|
|
@@ -22814,6 +22856,11 @@ function createSequenceElement(node, options = {}) {
|
|
|
22814
22856
|
type: "document-group",
|
|
22815
22857
|
documents: node.content?.filter((c) => c.type === "document").map((doc) => parseDocumentBlock(doc.attrs)) || []
|
|
22816
22858
|
};
|
|
22859
|
+
// The editor renamed this node to `StructuredContent` (2026-07-31,
|
|
22860
|
+
// editor-internal). Both are accepted: the new name is what the editor
|
|
22861
|
+
// emits now, and the old one still arrives from documents authored
|
|
22862
|
+
// before the rename, which the framework never rewrites.
|
|
22863
|
+
case "StructuredContent":
|
|
22817
22864
|
case "FormBlock":
|
|
22818
22865
|
let formData = attrs?.data;
|
|
22819
22866
|
if (typeof formData === "string") {
|
|
@@ -22983,6 +23030,12 @@ function processInlineElements(content) {
|
|
|
22983
23030
|
type: "icon",
|
|
22984
23031
|
attrs: parseUniwebIcon(item.attrs)
|
|
22985
23032
|
});
|
|
23033
|
+
} else if (item.type === "image" || item.type === "ImageBlock") {
|
|
23034
|
+
const isVideo = item.attrs?.role === "video";
|
|
23035
|
+
items.push({
|
|
23036
|
+
type: isVideo ? "video" : "image",
|
|
23037
|
+
attrs: isVideo ? parseMarkdownVideo(item.attrs) : parseImgBlock(item.attrs || {})
|
|
23038
|
+
});
|
|
22986
23039
|
} else if (item.type === "math-inline" || item.type === "math_inline") {
|
|
22987
23040
|
items.push(item);
|
|
22988
23041
|
} else if (item.type === "inset_placeholder") {
|
|
@@ -23057,10 +23110,10 @@ function parseDocumentBlock(itemAttrs) {
|
|
|
23057
23110
|
return ele;
|
|
23058
23111
|
}
|
|
23059
23112
|
function parseUniwebIcon(itemAttrs) {
|
|
23060
|
-
const { svg, url, size, color, preserveColors, href, target, library, name } = itemAttrs || {};
|
|
23113
|
+
const { svg, url, src, size, color, preserveColors, href, target, library, name } = itemAttrs || {};
|
|
23061
23114
|
const icon = {};
|
|
23062
23115
|
if (svg) icon.svg = svg;
|
|
23063
|
-
if (url) icon.url = url;
|
|
23116
|
+
if (url || src) icon.url = url || src;
|
|
23064
23117
|
if (size) icon.size = size;
|
|
23065
23118
|
if (color) icon.color = color;
|
|
23066
23119
|
if (preserveColors) icon.preserveColors = preserveColors;
|
|
@@ -23078,8 +23131,11 @@ function parseUniwebIcon(itemAttrs) {
|
|
|
23078
23131
|
return icon;
|
|
23079
23132
|
}
|
|
23080
23133
|
function parseIconBlock(itemAttrs) {
|
|
23081
|
-
|
|
23082
|
-
|
|
23134
|
+
const { svg, theme } = itemAttrs || {};
|
|
23135
|
+
const icon = {};
|
|
23136
|
+
if (svg) icon.svg = svg;
|
|
23137
|
+
if (theme) icon.theme = theme;
|
|
23138
|
+
return icon;
|
|
23083
23139
|
}
|
|
23084
23140
|
function parseImgBlock(itemAttrs) {
|
|
23085
23141
|
let {
|
|
@@ -23096,9 +23152,30 @@ function parseImgBlock(itemAttrs) {
|
|
|
23096
23152
|
theme,
|
|
23097
23153
|
role,
|
|
23098
23154
|
credit = "",
|
|
23099
|
-
id
|
|
23155
|
+
id,
|
|
23100
23156
|
// {#fig-id} cross-reference label — preserved so Press
|
|
23101
23157
|
// adapters can emit \label{id} (LaTeX) / <id> (Typst).
|
|
23158
|
+
// ── Markdown-authored attributes ────────────────────────────────
|
|
23159
|
+
// This function was written for the EDITOR's `ImageBlock` node and
|
|
23160
|
+
// later reused for the markdown `image` node (see `case "image"`), so
|
|
23161
|
+
// its vocabulary is the editor's: contentType, direction, filter,
|
|
23162
|
+
// theme, credit. Everything markdown declares and the editor does not
|
|
23163
|
+
// was therefore tokenized, declared, and then dropped one layer before
|
|
23164
|
+
// delivery — the published "Image Attributes" table
|
|
23165
|
+
// (docs/reference/content-structure.md) reached no component, and
|
|
23166
|
+
// neither did the `role=pdf` set added 2026-07-29 for the editor's
|
|
23167
|
+
// `document` fold-in. Carried 2026-07-30.
|
|
23168
|
+
width,
|
|
23169
|
+
height,
|
|
23170
|
+
loading,
|
|
23171
|
+
fit,
|
|
23172
|
+
position,
|
|
23173
|
+
preview,
|
|
23174
|
+
// role=pdf — the preview image
|
|
23175
|
+
author,
|
|
23176
|
+
// role=pdf — resource metadata, rendered beside the preview
|
|
23177
|
+
description
|
|
23178
|
+
// role=pdf — describes the RESOURCE, not the image (≠ alt)
|
|
23102
23179
|
} = itemAttrs;
|
|
23103
23180
|
let { contentType, viewType, contentId, identifier } = imgInfo || {};
|
|
23104
23181
|
const sizes = {
|
|
@@ -23123,14 +23200,64 @@ function parseImgBlock(itemAttrs) {
|
|
|
23123
23200
|
direction,
|
|
23124
23201
|
filter,
|
|
23125
23202
|
imgPos: direction === "left" || direction === "right" ? direction : "",
|
|
23203
|
+
// NOTE: `size` here is the direction-derived LAYOUT size
|
|
23204
|
+
// (basic/lg/full), not the author's `{size=…}` — that attribute is
|
|
23205
|
+
// icon-only and is read by parseUniwebIcon. Do not wire `{size=…}`
|
|
23206
|
+
// through here; the two meanings collide on one key.
|
|
23126
23207
|
size: sizes[direction] || "basic",
|
|
23127
23208
|
href,
|
|
23128
23209
|
target,
|
|
23129
23210
|
theme,
|
|
23130
23211
|
role,
|
|
23131
23212
|
credit,
|
|
23132
|
-
id: id || void 0
|
|
23213
|
+
id: id || void 0,
|
|
23214
|
+
// Omitted when absent, so an existing consumer sees no new empty keys.
|
|
23215
|
+
...width !== void 0 && width !== null && { width },
|
|
23216
|
+
...height !== void 0 && height !== null && { height },
|
|
23217
|
+
...loading && { loading },
|
|
23218
|
+
...fit && { fit },
|
|
23219
|
+
...position && { position },
|
|
23220
|
+
...preview && { preview },
|
|
23221
|
+
...author && { author },
|
|
23222
|
+
...description && { description }
|
|
23223
|
+
};
|
|
23224
|
+
}
|
|
23225
|
+
function parseMarkdownVideo(itemAttrs) {
|
|
23226
|
+
const {
|
|
23227
|
+
src,
|
|
23228
|
+
url,
|
|
23229
|
+
info,
|
|
23230
|
+
poster,
|
|
23231
|
+
alt = "",
|
|
23232
|
+
caption = "",
|
|
23233
|
+
autoplay,
|
|
23234
|
+
muted,
|
|
23235
|
+
loop,
|
|
23236
|
+
controls,
|
|
23237
|
+
href = "",
|
|
23238
|
+
target = "",
|
|
23239
|
+
role,
|
|
23240
|
+
id
|
|
23241
|
+
// {#fig-id} cross-reference label, same as parseImgBlock
|
|
23242
|
+
} = itemAttrs || {};
|
|
23243
|
+
const video = {
|
|
23244
|
+
// `identifier` (a CDN-resolvable asset id) wins when present, matching
|
|
23245
|
+
// parseImgBlock — an editor-deployed asset with a stale src must not
|
|
23246
|
+
// shadow the CDN copy.
|
|
23247
|
+
src: makeAssetUrl({ src: url || src, ...info }),
|
|
23248
|
+
alt,
|
|
23249
|
+
caption: stripTags(caption),
|
|
23250
|
+
role,
|
|
23251
|
+
href,
|
|
23252
|
+
target
|
|
23133
23253
|
};
|
|
23254
|
+
if (poster) video.poster = poster;
|
|
23255
|
+
if (autoplay !== void 0) video.autoplay = autoplay;
|
|
23256
|
+
if (muted !== void 0) video.muted = muted;
|
|
23257
|
+
if (loop !== void 0) video.loop = loop;
|
|
23258
|
+
if (controls !== void 0) video.controls = controls;
|
|
23259
|
+
if (id) video.id = id;
|
|
23260
|
+
return video;
|
|
23134
23261
|
}
|
|
23135
23262
|
function parseVideoBlock(itemAttrs) {
|
|
23136
23263
|
let {
|
|
@@ -23295,6 +23422,7 @@ function flattenGroup(group) {
|
|
|
23295
23422
|
headings: group.body.headings || []
|
|
23296
23423
|
};
|
|
23297
23424
|
if (group.body.math && group.body.math.length) flat.math = group.body.math;
|
|
23425
|
+
if (group.body.tables && group.body.tables.length) flat.tables = group.body.tables;
|
|
23298
23426
|
return flat;
|
|
23299
23427
|
}
|
|
23300
23428
|
function processGroups(sequence, options = {}) {
|
|
@@ -23317,11 +23445,11 @@ function processGroups(sequence, options = {}) {
|
|
|
23317
23445
|
items: []
|
|
23318
23446
|
};
|
|
23319
23447
|
}
|
|
23320
|
-
const groups = splitBySlices(sequence);
|
|
23448
|
+
const groups = splitBySlices(sequence, options);
|
|
23321
23449
|
const processedGroups = groups.map((group) => processGroupContent(group));
|
|
23322
23450
|
let mainGroup = null;
|
|
23323
23451
|
let itemGroups = [];
|
|
23324
|
-
const shouldBeMain = identifyMainContent(processedGroups);
|
|
23452
|
+
const shouldBeMain = options.alwaysItems ? false : identifyMainContent(processedGroups);
|
|
23325
23453
|
if (shouldBeMain) {
|
|
23326
23454
|
mainGroup = processedGroups[0];
|
|
23327
23455
|
itemGroups = processedGroups.slice(1);
|
|
@@ -23349,7 +23477,7 @@ function processGroups(sequence, options = {}) {
|
|
|
23349
23477
|
items: flatItems
|
|
23350
23478
|
};
|
|
23351
23479
|
}
|
|
23352
|
-
function splitBySlices(sequence) {
|
|
23480
|
+
function splitBySlices(sequence, options = {}) {
|
|
23353
23481
|
const groups = [];
|
|
23354
23482
|
let currentGroup = [];
|
|
23355
23483
|
for (let i = 0; i < sequence.length; i++) {
|
|
@@ -23367,7 +23495,7 @@ function splitBySlices(sequence) {
|
|
|
23367
23495
|
groups.push(currentGroup);
|
|
23368
23496
|
currentGroup = [];
|
|
23369
23497
|
}
|
|
23370
|
-
const headingBlock = readHeadingGroup(sequence, i);
|
|
23498
|
+
const headingBlock = readHeadingGroup(sequence, i, options);
|
|
23371
23499
|
currentGroup.push(...headingBlock);
|
|
23372
23500
|
i += headingBlock.length - 1;
|
|
23373
23501
|
} else {
|
|
@@ -23385,7 +23513,7 @@ function isPreTitle(sequence, i) {
|
|
|
23385
23513
|
function isBannerImage(sequence, i) {
|
|
23386
23514
|
return i === 0 && i + 1 < sequence.length && sequence[i].type === "image" && (sequence[i].role === "banner" || sequence[i + 1].type === "heading");
|
|
23387
23515
|
}
|
|
23388
|
-
function readHeadingGroup(sequence, startIdx) {
|
|
23516
|
+
function readHeadingGroup(sequence, startIdx, options = {}) {
|
|
23389
23517
|
const elements = [sequence[startIdx]];
|
|
23390
23518
|
let hasGoneDeeper = false;
|
|
23391
23519
|
for (let i = startIdx + 1; i < sequence.length; i++) {
|
|
@@ -23403,7 +23531,7 @@ function readHeadingGroup(sequence, startIdx) {
|
|
|
23403
23531
|
elements.push(element);
|
|
23404
23532
|
continue;
|
|
23405
23533
|
}
|
|
23406
|
-
if (element.level === previousElement.level && !hasGoneDeeper) {
|
|
23534
|
+
if (element.level === previousElement.level && !hasGoneDeeper && !options.alwaysItems) {
|
|
23407
23535
|
elements.push(element);
|
|
23408
23536
|
continue;
|
|
23409
23537
|
}
|
|
@@ -23522,6 +23650,14 @@ function processGroupContent(elements) {
|
|
|
23522
23650
|
const quoteContent = processGroupContent(element.children);
|
|
23523
23651
|
body.quotes.push(quoteContent.body);
|
|
23524
23652
|
break;
|
|
23653
|
+
case "concept_block": {
|
|
23654
|
+
const children = element.children || [];
|
|
23655
|
+
body.data[element.tag] = {
|
|
23656
|
+
items: processGroups(children, { alwaysItems: true }).items,
|
|
23657
|
+
sequence: children
|
|
23658
|
+
};
|
|
23659
|
+
break;
|
|
23660
|
+
}
|
|
23525
23661
|
case "dataBlock":
|
|
23526
23662
|
body.data[element.tag] = element.data;
|
|
23527
23663
|
break;
|
|
@@ -23538,6 +23674,12 @@ function processGroupContent(elements) {
|
|
|
23538
23674
|
mathml: element.mathml || ""
|
|
23539
23675
|
});
|
|
23540
23676
|
break;
|
|
23677
|
+
case "table":
|
|
23678
|
+
(body.tables ||= []).push({
|
|
23679
|
+
rows: element.rows || [],
|
|
23680
|
+
attrs: element.attrs || {}
|
|
23681
|
+
});
|
|
23682
|
+
break;
|
|
23541
23683
|
case "inset":
|
|
23542
23684
|
body.insets.push({ refId: element.refId });
|
|
23543
23685
|
break;
|
|
@@ -23596,6 +23738,8 @@ function processInlineElements2(children, body) {
|
|
|
23596
23738
|
} else {
|
|
23597
23739
|
body.images.push(item.attrs);
|
|
23598
23740
|
}
|
|
23741
|
+
} else if (item.type === "video") {
|
|
23742
|
+
body.videos.push(item.attrs);
|
|
23599
23743
|
} else if (item.type === "link") {
|
|
23600
23744
|
body.links.push(item.attrs);
|
|
23601
23745
|
}
|
|
@@ -24228,6 +24372,7 @@ function isGoogleFontsUrl(url) {
|
|
|
24228
24372
|
return false;
|
|
24229
24373
|
}
|
|
24230
24374
|
}
|
|
24375
|
+
var FONT_LINKS_MARKER = "<!--uniweb-fonts-->";
|
|
24231
24376
|
function generateFontCSS(fontRoles = {}, fonts = {}, base = "/") {
|
|
24232
24377
|
const cssLines = [];
|
|
24233
24378
|
const linkTags = [];
|
|
@@ -26324,6 +26469,23 @@ function substitutePlaceholders(value, context, options = {}) {
|
|
|
26324
26469
|
return value;
|
|
26325
26470
|
}
|
|
26326
26471
|
|
|
26472
|
+
// ../core/src/data-paths.js
|
|
26473
|
+
var DATA_DIR = "data";
|
|
26474
|
+
var DATA_URL_PREFIX = `/${DATA_DIR}/`;
|
|
26475
|
+
function collectionDataUrl(name) {
|
|
26476
|
+
return `${DATA_URL_PREFIX}${name}.json`;
|
|
26477
|
+
}
|
|
26478
|
+
function recordDataUrl(collection, slug) {
|
|
26479
|
+
return `${DATA_URL_PREFIX}${collection}/${slug}.json`;
|
|
26480
|
+
}
|
|
26481
|
+
function collectionNameFromUrl(path) {
|
|
26482
|
+
if (typeof path !== "string") return "";
|
|
26483
|
+
return path.replace(new RegExp(`^/?${DATA_DIR}/`), "").replace(/\.json$/i, "");
|
|
26484
|
+
}
|
|
26485
|
+
function isDataUrl(path) {
|
|
26486
|
+
return typeof path === "string" && path.startsWith(DATA_URL_PREFIX);
|
|
26487
|
+
}
|
|
26488
|
+
|
|
26327
26489
|
// ../core/src/fetch-config.js
|
|
26328
26490
|
function isFetchRefinement(cfg) {
|
|
26329
26491
|
return cfg?.refine === true || cfg?.inherit === true;
|
|
@@ -26331,7 +26493,7 @@ function isFetchRefinement(cfg) {
|
|
|
26331
26493
|
function localizeConfig(cfg, locale, defaultLocale) {
|
|
26332
26494
|
if (!cfg.path) return cfg;
|
|
26333
26495
|
if (!locale || locale === defaultLocale) return cfg;
|
|
26334
|
-
if (!cfg.path
|
|
26496
|
+
if (!isDataUrl(cfg.path)) return cfg;
|
|
26335
26497
|
return { ...cfg, path: `/${locale}${cfg.path}` };
|
|
26336
26498
|
}
|
|
26337
26499
|
function applyDeferredDetail(cfg, collections) {
|
|
@@ -26342,7 +26504,7 @@ function applyDeferredDetail(cfg, collections) {
|
|
|
26342
26504
|
if (!collConfig || typeof collConfig !== "object") return cfg;
|
|
26343
26505
|
const deferred = Array.isArray(collConfig.deferred) ? collConfig.deferred : null;
|
|
26344
26506
|
if (!deferred || deferred.length === 0) return cfg;
|
|
26345
|
-
const pattern = typeof collConfig.detailUrl === "string" ? collConfig.detailUrl :
|
|
26507
|
+
const pattern = typeof collConfig.detailUrl === "string" ? collConfig.detailUrl : recordDataUrl(schema4, "{slug}");
|
|
26346
26508
|
return { ...cfg, detail: pattern };
|
|
26347
26509
|
}
|
|
26348
26510
|
function resolveFetchConfigs(sources, options = {}) {
|
|
@@ -27097,6 +27259,32 @@ function validateLanguageConfig(config = {}) {
|
|
|
27097
27259
|
return { errors, warnings };
|
|
27098
27260
|
}
|
|
27099
27261
|
|
|
27262
|
+
// ../core/src/route-match.js
|
|
27263
|
+
var PARAM_NAME = "[A-Za-z0-9_-]+";
|
|
27264
|
+
var REGEX_SPECIALS = /[.*+?^${}()|[\]\\]/g;
|
|
27265
|
+
function normalizeRoute(route) {
|
|
27266
|
+
if (typeof route !== "string" || route === "") return "/";
|
|
27267
|
+
return route === "/" ? "/" : route.replace(/\/+$/, "") || "/";
|
|
27268
|
+
}
|
|
27269
|
+
function routePatternToRegex(pattern) {
|
|
27270
|
+
const paramNames = [];
|
|
27271
|
+
const source = normalizeRoute(pattern).replace(REGEX_SPECIALS, "\\$&").replace(new RegExp(`:(${PARAM_NAME})`, "g"), (_, name) => {
|
|
27272
|
+
paramNames.push(name);
|
|
27273
|
+
return "([^/]+)";
|
|
27274
|
+
});
|
|
27275
|
+
return { regex: new RegExp(`^${source}$`), paramNames };
|
|
27276
|
+
}
|
|
27277
|
+
function matchDynamicRoute(pattern, path) {
|
|
27278
|
+
const { regex, paramNames } = routePatternToRegex(pattern);
|
|
27279
|
+
const match2 = normalizeRoute(path).match(regex);
|
|
27280
|
+
if (!match2) return null;
|
|
27281
|
+
const params = {};
|
|
27282
|
+
paramNames.forEach((name, i) => {
|
|
27283
|
+
params[name] = decodeURIComponent(match2[i + 1]);
|
|
27284
|
+
});
|
|
27285
|
+
return { params };
|
|
27286
|
+
}
|
|
27287
|
+
|
|
27100
27288
|
// ../core/src/website.js
|
|
27101
27289
|
var Website = class {
|
|
27102
27290
|
constructor({
|
|
@@ -27457,19 +27645,7 @@ var Website = class {
|
|
|
27457
27645
|
* @returns {Object|null} Match result with params, or null if no match
|
|
27458
27646
|
*/
|
|
27459
27647
|
_matchDynamicRoute(pattern, path) {
|
|
27460
|
-
|
|
27461
|
-
const regexStr = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/:(\w+)/g, (_, paramName) => {
|
|
27462
|
-
paramNames.push(paramName);
|
|
27463
|
-
return "([^/]+)";
|
|
27464
|
-
});
|
|
27465
|
-
const regex = new RegExp(`^${regexStr}$`);
|
|
27466
|
-
const match2 = path.match(regex);
|
|
27467
|
-
if (!match2) return null;
|
|
27468
|
-
const params = {};
|
|
27469
|
-
paramNames.forEach((name, i) => {
|
|
27470
|
-
params[name] = decodeURIComponent(match2[i + 1]);
|
|
27471
|
-
});
|
|
27472
|
-
return { params };
|
|
27648
|
+
return matchDynamicRoute(pattern, path);
|
|
27473
27649
|
}
|
|
27474
27650
|
/**
|
|
27475
27651
|
* Create a dynamic page instance with concrete route and params
|
|
@@ -28598,26 +28774,6 @@ var Uniweb = class {
|
|
|
28598
28774
|
// ../core/src/theme.js
|
|
28599
28775
|
var SHADE_LEVELS4 = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
|
|
28600
28776
|
var VALID_CONTEXTS = ["light", "medium", "dark"];
|
|
28601
|
-
var DEFAULT_CONTEXT_TOKENS2 = {
|
|
28602
|
-
light: {
|
|
28603
|
-
bg: "var(--neutral-50)",
|
|
28604
|
-
text: "var(--neutral-950)",
|
|
28605
|
-
heading: "var(--neutral-900)",
|
|
28606
|
-
link: "var(--primary-600)"
|
|
28607
|
-
},
|
|
28608
|
-
medium: {
|
|
28609
|
-
bg: "var(--neutral-100)",
|
|
28610
|
-
text: "var(--neutral-950)",
|
|
28611
|
-
heading: "var(--neutral-900)",
|
|
28612
|
-
link: "var(--primary-600)"
|
|
28613
|
-
},
|
|
28614
|
-
dark: {
|
|
28615
|
-
bg: "var(--neutral-900)",
|
|
28616
|
-
text: "var(--neutral-50)",
|
|
28617
|
-
heading: "white",
|
|
28618
|
-
link: "var(--primary-400)"
|
|
28619
|
-
}
|
|
28620
|
-
};
|
|
28621
28777
|
var Theme = class {
|
|
28622
28778
|
/**
|
|
28623
28779
|
* Create a Theme instance
|
|
@@ -28721,36 +28877,42 @@ var Theme = class {
|
|
|
28721
28877
|
// ============================================================
|
|
28722
28878
|
// Context Access
|
|
28723
28879
|
// ============================================================
|
|
28724
|
-
|
|
28725
|
-
*
|
|
28880
|
+
/*
|
|
28881
|
+
* REMOVED 2026-07-28: getContextToken(context, token) and
|
|
28882
|
+
* getContextTokens(context), plus the DEFAULT_CONTEXT_TOKENS table backing
|
|
28883
|
+
* them. Read this before reintroducing either.
|
|
28726
28884
|
*
|
|
28727
|
-
*
|
|
28728
|
-
*
|
|
28729
|
-
*
|
|
28885
|
+
* They were unreachable-by-design rather than merely stale. A lookup keyed on
|
|
28886
|
+
* (context, token) answers "what does the .context-<name> class set by
|
|
28887
|
+
* default?", but the question callers ask is "what colour is --heading HERE",
|
|
28888
|
+
* and those diverge for two reasons no such lookup can see:
|
|
28730
28889
|
*
|
|
28731
|
-
*
|
|
28732
|
-
*
|
|
28733
|
-
*
|
|
28734
|
-
*/
|
|
28735
|
-
getContextToken(context, token) {
|
|
28736
|
-
const customContext = this._contexts[context];
|
|
28737
|
-
if (customContext && customContext[token]) {
|
|
28738
|
-
return customContext[token];
|
|
28739
|
-
}
|
|
28740
|
-
const defaults = DEFAULT_CONTEXT_TOKENS2[context];
|
|
28741
|
-
return defaults?.[token] || null;
|
|
28742
|
-
}
|
|
28743
|
-
/**
|
|
28744
|
-
* Get all tokens for a context
|
|
28890
|
+
* - a section's own `theme:` overrides, emitted as `#section-{id} { … }` by
|
|
28891
|
+
* @uniweb/theming's buildSectionOverrides;
|
|
28892
|
+
* - the active site scheme, where `.scheme-dark` redefines the root tokens.
|
|
28745
28893
|
*
|
|
28746
|
-
*
|
|
28747
|
-
*
|
|
28894
|
+
* So a correct-looking answer would still be wrong whenever a section
|
|
28895
|
+
* overrides tokens or the visitor is in dark mode — the failure being a
|
|
28896
|
+
* confident wrong value, not an error. (The table had also drifted: it held
|
|
28897
|
+
* `bg`/`text`, retired in favour of `section`/`body`, and four tokens where
|
|
28898
|
+
* the live set is ~25. Repopulating it would have made a misleading API look
|
|
28899
|
+
* trustworthy, which is worse than leaving it visibly incomplete.)
|
|
28900
|
+
*
|
|
28901
|
+
* What to use instead:
|
|
28902
|
+
* - an actually-resolved value (canvas, SVG, a chart matching the theme):
|
|
28903
|
+
* getComputedStyle(el).getPropertyValue('--heading') — this accounts for
|
|
28904
|
+
* both section overrides and the active scheme, which no static table can;
|
|
28905
|
+
* - the defaults table itself (tooling, a theme editor):
|
|
28906
|
+
* getDefaultContextTokens() from @uniweb/theming, which owns it;
|
|
28907
|
+
* - ordinary component styling: the CSS variables directly. Reading tokens
|
|
28908
|
+
* into JS to branch on them is the `isDark ? … : …` pattern semantic
|
|
28909
|
+
* tokens exist to remove.
|
|
28910
|
+
*
|
|
28911
|
+
* SHADE_LEVELS below is duplicated from @uniweb/theming too, and was left
|
|
28912
|
+
* deliberately: both copies are identical and the 11-step scale is fixed by
|
|
28913
|
+
* the design, so there is no drift to prevent — noted so the next reader
|
|
28914
|
+
* knows it was considered rather than missed.
|
|
28748
28915
|
*/
|
|
28749
|
-
getContextTokens(context) {
|
|
28750
|
-
const defaults = DEFAULT_CONTEXT_TOKENS2[context] || {};
|
|
28751
|
-
const custom = this._contexts[context] || {};
|
|
28752
|
-
return { ...defaults, ...custom };
|
|
28753
|
-
}
|
|
28754
28916
|
/**
|
|
28755
28917
|
* Get the CSS class name for a context
|
|
28756
28918
|
*
|
|
@@ -29075,6 +29237,25 @@ function isRichSchema(schema4) {
|
|
|
29075
29237
|
if (schema4.childSchema && typeof schema4.childSchema === "object") return true;
|
|
29076
29238
|
return false;
|
|
29077
29239
|
}
|
|
29240
|
+
function normalizeSchema(schema4) {
|
|
29241
|
+
if (!schema4 || typeof schema4 !== "object" || Array.isArray(schema4))
|
|
29242
|
+
return null;
|
|
29243
|
+
if (Array.isArray(schema4.fields)) return schema4;
|
|
29244
|
+
if (schema4.isComposite === true || schema4.childSchema) return schema4;
|
|
29245
|
+
if (schema4.sections !== void 0) return null;
|
|
29246
|
+
const mapToFields = (map2) => Object.entries(map2).map(
|
|
29247
|
+
([id, spec]) => typeof spec === "string" ? { id, type: spec } : { id, ...spec }
|
|
29248
|
+
);
|
|
29249
|
+
if (schema4.fields && typeof schema4.fields === "object") {
|
|
29250
|
+
const { fields, ...rest } = schema4;
|
|
29251
|
+
return { ...rest, fields: mapToFields(fields) };
|
|
29252
|
+
}
|
|
29253
|
+
const entries = Object.entries(schema4);
|
|
29254
|
+
if (!entries.length) return null;
|
|
29255
|
+
const isFieldSpec = ([, v]) => v && typeof v === "object" && !Array.isArray(v) && v.type !== void 0;
|
|
29256
|
+
if (!entries.every(isFieldSpec)) return null;
|
|
29257
|
+
return { fields: mapToFields(schema4) };
|
|
29258
|
+
}
|
|
29078
29259
|
|
|
29079
29260
|
// ../core/src/request-styles/json-body.js
|
|
29080
29261
|
var jsonBody = {
|
|
@@ -29417,7 +29598,14 @@ function getUniweb() {
|
|
|
29417
29598
|
return globalThis.uniweb;
|
|
29418
29599
|
}
|
|
29419
29600
|
function createUniweb(content, foundation = null, extensions = [], { defaultFetcher = null, transport = null, dev = false } = {}) {
|
|
29420
|
-
const instance = new Uniweb({
|
|
29601
|
+
const instance = new Uniweb({
|
|
29602
|
+
content,
|
|
29603
|
+
foundation,
|
|
29604
|
+
extensions,
|
|
29605
|
+
defaultFetcher,
|
|
29606
|
+
transport,
|
|
29607
|
+
dev
|
|
29608
|
+
});
|
|
29421
29609
|
globalThis.uniweb = instance;
|
|
29422
29610
|
return instance;
|
|
29423
29611
|
}
|
|
@@ -29672,6 +29860,20 @@ function getComponentMeta(componentName) {
|
|
|
29672
29860
|
function getComponentDefaults(componentName) {
|
|
29673
29861
|
return globalThis.uniweb?.getComponentDefaults?.(componentName) || {};
|
|
29674
29862
|
}
|
|
29863
|
+
var PARAM_NAME2 = "[A-Za-z0-9_-]+";
|
|
29864
|
+
var REGEX_SPECIALS2 = /[.*+?^${}()|[\]\\]/g;
|
|
29865
|
+
function normalizeRoute2(route) {
|
|
29866
|
+
if (typeof route !== "string" || route === "") return "/";
|
|
29867
|
+
return route === "/" ? "/" : route.replace(/\/+$/, "") || "/";
|
|
29868
|
+
}
|
|
29869
|
+
function routePatternToRegex2(pattern) {
|
|
29870
|
+
const paramNames = [];
|
|
29871
|
+
const source = normalizeRoute2(pattern).replace(REGEX_SPECIALS2, "\\$&").replace(new RegExp(`:(${PARAM_NAME2})`, "g"), (_, name) => {
|
|
29872
|
+
paramNames.push(name);
|
|
29873
|
+
return "([^/]+)";
|
|
29874
|
+
});
|
|
29875
|
+
return { regex: new RegExp(`^${source}$`), paramNames };
|
|
29876
|
+
}
|
|
29675
29877
|
function default404Html(basePath = "") {
|
|
29676
29878
|
const homeHref = basePath ? `${basePath}/` : "/";
|
|
29677
29879
|
return `<div class="page-not-found" style="min-height:80vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center"><h1 style="font-size:3rem;font-weight:bold;color:#1f2937;margin-bottom:1rem">404</h1><p style="color:#64748b;margin-bottom:2rem">Page not found</p><a href="${homeHref}" style="color:#3b82f6;text-decoration:underline">Go to homepage</a></div>`;
|
|
@@ -29735,6 +29937,23 @@ function resolveLayoutTransitions(areaNames, explicit) {
|
|
|
29735
29937
|
for (const name of areaNames) transitions[name] = toIdent(name);
|
|
29736
29938
|
return explicit ? { ...transitions, ...explicit } : transitions;
|
|
29737
29939
|
}
|
|
29940
|
+
function resolveLayoutLayers(areaNames, explicit) {
|
|
29941
|
+
if (explicit === false) return {};
|
|
29942
|
+
const defaults = {};
|
|
29943
|
+
for (const name of areaNames) defaults[name] = 1;
|
|
29944
|
+
return explicit ? { ...defaults, ...explicit } : defaults;
|
|
29945
|
+
}
|
|
29946
|
+
function areaWrapperStyle(region, transitions, layers) {
|
|
29947
|
+
const style = {};
|
|
29948
|
+
const name = transitions?.[region];
|
|
29949
|
+
if (name) style.viewTransitionName = name;
|
|
29950
|
+
const layer = layers?.[region];
|
|
29951
|
+
if (layer != null) {
|
|
29952
|
+
style.position = "relative";
|
|
29953
|
+
style.zIndex = layer;
|
|
29954
|
+
}
|
|
29955
|
+
return Object.keys(style).length > 0 ? style : null;
|
|
29956
|
+
}
|
|
29738
29957
|
function applyBootScheme(respectSystem, fallback) {
|
|
29739
29958
|
var stored = null;
|
|
29740
29959
|
try {
|
|
@@ -29988,15 +30207,17 @@ function renderLayout(page, website) {
|
|
|
29988
30207
|
const layoutMeta = website.getLayoutMeta(layoutName);
|
|
29989
30208
|
const bodyBlocks = page.getBodyBlocks();
|
|
29990
30209
|
const areas = page.getLayoutAreas();
|
|
29991
|
-
const
|
|
29992
|
-
const
|
|
29993
|
-
|
|
29994
|
-
|
|
30210
|
+
const areaNames = Object.keys(areas);
|
|
30211
|
+
const transitions = website.viewTransitions ? resolveLayoutTransitions(areaNames, layoutMeta?.transitions) : null;
|
|
30212
|
+
const layers = resolveLayoutLayers(areaNames, layoutMeta?.layers);
|
|
30213
|
+
const wrapArea = (name, element) => {
|
|
30214
|
+
const style = areaWrapperStyle(name, transitions, layers);
|
|
30215
|
+
return style ? import_react.default.createElement("div", { style }, element) : element;
|
|
29995
30216
|
};
|
|
29996
|
-
const bodyElement = bodyBlocks ?
|
|
30217
|
+
const bodyElement = bodyBlocks ? wrapArea("body", renderBlocks(bodyBlocks)) : null;
|
|
29997
30218
|
const areaElements = {};
|
|
29998
30219
|
for (const [name, blocks] of Object.entries(areas)) {
|
|
29999
|
-
areaElements[name] =
|
|
30220
|
+
areaElements[name] = wrapArea(name, renderBlocks(blocks));
|
|
30000
30221
|
}
|
|
30001
30222
|
if (RemoteLayout) {
|
|
30002
30223
|
const params = { ...layoutMeta?.defaults || {}, ...page.getLayoutParams() || {} };
|
|
@@ -30011,9 +30232,9 @@ function renderLayout(page, website) {
|
|
|
30011
30232
|
return import_react.default.createElement(
|
|
30012
30233
|
import_react.default.Fragment,
|
|
30013
30234
|
null,
|
|
30014
|
-
areaElements.header && import_react.default.createElement("header",
|
|
30235
|
+
areaElements.header && import_react.default.createElement("header", null, areaElements.header),
|
|
30015
30236
|
bodyElement && import_react.default.createElement("main", null, bodyElement),
|
|
30016
|
-
areaElements.footer && import_react.default.createElement("footer",
|
|
30237
|
+
areaElements.footer && import_react.default.createElement("footer", null, areaElements.footer)
|
|
30017
30238
|
);
|
|
30018
30239
|
}
|
|
30019
30240
|
function initPrerenderForLocale(content, foundation, locale, extensionsOrOptions, maybeOptions) {
|
|
@@ -30092,6 +30313,9 @@ async function prefetchIcons(siteContent, uniweb, onProgress = () => {
|
|
|
30092
30313
|
siteContent._iconCache = Object.fromEntries(uniweb.iconCache);
|
|
30093
30314
|
}
|
|
30094
30315
|
}
|
|
30316
|
+
function resolvePage(website, route) {
|
|
30317
|
+
return website.getPage(route);
|
|
30318
|
+
}
|
|
30095
30319
|
function classifyRenderError(err) {
|
|
30096
30320
|
const msg = err.message || "";
|
|
30097
30321
|
if (msg.includes("Invalid hook call") || msg.includes("useState") || msg.includes("useEffect")) {
|
|
@@ -30113,6 +30337,14 @@ function classifyRenderError(err) {
|
|
|
30113
30337
|
}
|
|
30114
30338
|
function renderPage(page, website) {
|
|
30115
30339
|
website.setActivePage(page.route);
|
|
30340
|
+
if (page.hasContent?.() && page.getBodyBlocks().length === 0) {
|
|
30341
|
+
return {
|
|
30342
|
+
error: {
|
|
30343
|
+
type: "content-not-loaded",
|
|
30344
|
+
message: `page "${page.route}" declares content but has no loaded sections \u2014 its sections are not in the payload and this renderer does not fetch them`
|
|
30345
|
+
}
|
|
30346
|
+
};
|
|
30347
|
+
}
|
|
30116
30348
|
const element = renderLayout(page, website);
|
|
30117
30349
|
let renderedContent;
|
|
30118
30350
|
try {
|
|
@@ -30137,13 +30369,22 @@ function injectPageContent(html, renderedContent, page, options = {}) {
|
|
|
30137
30369
|
</head>`);
|
|
30138
30370
|
}
|
|
30139
30371
|
}
|
|
30140
|
-
const
|
|
30372
|
+
const themeData = page?.website?.themeData;
|
|
30373
|
+
const themeCss = themeData?.css;
|
|
30141
30374
|
if (themeCss && !result.includes('id="uniweb-theme"')) {
|
|
30142
30375
|
result = result.replace(
|
|
30143
30376
|
"</head>",
|
|
30144
30377
|
` <style id="uniweb-theme">
|
|
30145
30378
|
${themeCss}
|
|
30146
30379
|
</style>
|
|
30380
|
+
</head>`
|
|
30381
|
+
);
|
|
30382
|
+
}
|
|
30383
|
+
if (themeData?.links && !result.includes(FONT_LINKS_MARKER)) {
|
|
30384
|
+
result = result.replace(
|
|
30385
|
+
"</head>",
|
|
30386
|
+
` ${FONT_LINKS_MARKER}
|
|
30387
|
+
${themeData.links}
|
|
30147
30388
|
</head>`
|
|
30148
30389
|
);
|
|
30149
30390
|
}
|
|
@@ -30198,10 +30439,7 @@ ${options.sectionOverrideCSS}
|
|
|
30198
30439
|
}
|
|
30199
30440
|
function generate404Html({ baseHtml, website, siteContent }) {
|
|
30200
30441
|
const dynamicTemplates = siteContent.pages?.filter((p) => p.isDynamic) || [];
|
|
30201
|
-
const routePatterns = dynamicTemplates.map((p) =>
|
|
30202
|
-
const escaped = p.route.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/:[^/]+/g, "[^\\/]+");
|
|
30203
|
-
return `^${escaped}\\/?$`;
|
|
30204
|
-
});
|
|
30442
|
+
const routePatterns = dynamicTemplates.map((p) => routePatternToRegex2(p.route).regex.source);
|
|
30205
30443
|
let html = baseHtml;
|
|
30206
30444
|
const notFoundPage = website.getNotFoundPage();
|
|
30207
30445
|
if (notFoundPage) {
|
|
@@ -30220,7 +30458,7 @@ function generate404Html({ baseHtml, website, siteContent }) {
|
|
|
30220
30458
|
}
|
|
30221
30459
|
if (routePatterns.length > 0) {
|
|
30222
30460
|
const patternList = routePatterns.map((p) => `/${p}/`).join(",");
|
|
30223
|
-
const dynamicScript = `<script>(function(){var p=[${patternList}],r=window.location.pathname;if(p.some(function(x){return x.test(r)})){var el=document.getElementById('root');if(el)el.innerHTML='';}})()<\/script>`;
|
|
30461
|
+
const dynamicScript = `<script>(function(){var p=[${patternList}],r=window.location.pathname.replace(/\\/+$/,'')||'/';if(p.some(function(x){return x.test(r)})){var el=document.getElementById('root');if(el)el.innerHTML='';}})()<\/script>`;
|
|
30224
30462
|
html = html.replace("</body>", `${dynamicScript}
|
|
30225
30463
|
</body>`);
|
|
30226
30464
|
}
|
|
@@ -30254,6 +30492,7 @@ export {
|
|
|
30254
30492
|
renderBlocks,
|
|
30255
30493
|
renderLayout,
|
|
30256
30494
|
renderPage,
|
|
30495
|
+
resolvePage,
|
|
30257
30496
|
sliceContentForLocale
|
|
30258
30497
|
};
|
|
30259
30498
|
/*! Bundled license information:
|