@particle-academy/fancy-cms-ui 0.7.1 → 0.9.0
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/CHANGELOG.md +448 -0
- package/LICENSE +21 -0
- package/agent-guide/professional-pages.md +107 -0
- package/dist/{chunk-SYRU2X45.js → chunk-BVMSHUQF.js} +6 -4
- package/dist/{chunk-SYRU2X45.js.map → chunk-BVMSHUQF.js.map} +1 -1
- package/dist/chunk-IRG5IKKO.js +716 -0
- package/dist/chunk-IRG5IKKO.js.map +1 -0
- package/dist/{chunk-R7HDS4VG.js → chunk-TJL26EAX.js} +3 -3
- package/dist/{chunk-R7HDS4VG.js.map → chunk-TJL26EAX.js.map} +1 -1
- package/dist/editor.cjs +325 -103
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +2 -2
- package/dist/editor.d.ts +2 -2
- package/dist/editor.js +3 -3
- package/dist/index.cjs +335 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +4 -4
- package/dist/react.cjs +304 -85
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +2 -2
- package/dist/{registry-CUNaBqTs.d.cts → registry-F2TAMjhx.d.cts} +33 -1
- package/dist/{registry-CUNaBqTs.d.ts → registry-F2TAMjhx.d.ts} +33 -1
- package/dist/{useEditor-wdlYQMHg.d.cts → useEditor-B2kVZJA0.d.cts} +1 -1
- package/dist/{useEditor-aPH5falI.d.ts → useEditor-BQSictH3.d.ts} +1 -1
- package/package.json +107 -105
- package/dist/chunk-LY6ID2J2.js +0 -494
- package/dist/chunk-LY6ID2J2.js.map +0 -1
package/dist/editor.cjs
CHANGED
|
@@ -16,6 +16,12 @@ function hasNodeMap(value) {
|
|
|
16
16
|
const nodes = value.nodes;
|
|
17
17
|
return typeof nodes === "object" && nodes !== null && !Array.isArray(nodes);
|
|
18
18
|
}
|
|
19
|
+
function withNodeDefaults(node) {
|
|
20
|
+
const props = typeof node.props === "object" && node.props !== null ? node.props : {};
|
|
21
|
+
const style = typeof node.style === "object" && node.style !== null ? node.style : { base: {} };
|
|
22
|
+
const base = typeof style.base === "object" && style.base !== null ? style.base : {};
|
|
23
|
+
return props === node.props && style === node.style && base === style.base ? node : { ...node, props, style: { ...style, base } };
|
|
24
|
+
}
|
|
19
25
|
var DEFAULT_BREAKPOINTS = {
|
|
20
26
|
base: 0,
|
|
21
27
|
md: 768,
|
|
@@ -32,6 +38,15 @@ function emptyDoc(id, slug = "/") {
|
|
|
32
38
|
};
|
|
33
39
|
}
|
|
34
40
|
|
|
41
|
+
// src/document/own.ts
|
|
42
|
+
var has = Object.prototype.hasOwnProperty;
|
|
43
|
+
function own(map, key) {
|
|
44
|
+
return map != null && has.call(map, key) ? map[key] : void 0;
|
|
45
|
+
}
|
|
46
|
+
function setOwn(map, key, value) {
|
|
47
|
+
Object.defineProperty(map, key, { value, writable: true, enumerable: true, configurable: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
35
50
|
// src/document/migrate.ts
|
|
36
51
|
function normalizeDoc(input3) {
|
|
37
52
|
if (hasNodeMap(input3)) return input3;
|
|
@@ -49,7 +64,7 @@ function migrateDocVerbose(input3) {
|
|
|
49
64
|
const ordered = [];
|
|
50
65
|
const seen = /* @__PURE__ */ new Set();
|
|
51
66
|
for (const id of sections) {
|
|
52
|
-
if (!normalized.nodes
|
|
67
|
+
if (!own(normalized.nodes, id)) {
|
|
53
68
|
danglingIds.push(id);
|
|
54
69
|
continue;
|
|
55
70
|
}
|
|
@@ -63,7 +78,7 @@ function migrateDocVerbose(input3) {
|
|
|
63
78
|
let prev = null;
|
|
64
79
|
for (const id of finalOrder) {
|
|
65
80
|
const key = fancyDocCommons.fractionalKey(prev, null);
|
|
66
|
-
nodes
|
|
81
|
+
setOwn(nodes, id, { ...own(nodes, id), order: key });
|
|
67
82
|
prev = key;
|
|
68
83
|
}
|
|
69
84
|
const { sections: _dropped, ...rest } = legacy;
|
|
@@ -92,7 +107,7 @@ function descendantIds(doc, id) {
|
|
|
92
107
|
return fancyDocCommons.descendantsOf(doc, id);
|
|
93
108
|
}
|
|
94
109
|
function patchNode(doc, id, patch) {
|
|
95
|
-
const node = doc.nodes
|
|
110
|
+
const node = own(doc.nodes, id);
|
|
96
111
|
return {
|
|
97
112
|
...doc,
|
|
98
113
|
nodes: { ...doc.nodes, [id]: { ...node, ...patch } },
|
|
@@ -106,8 +121,8 @@ function reduce(doc, op, opts = {}) {
|
|
|
106
121
|
};
|
|
107
122
|
switch (op.t) {
|
|
108
123
|
case "insert_node": {
|
|
109
|
-
if (doc.nodes
|
|
110
|
-
if (op.node.parent !== null && !doc.nodes
|
|
124
|
+
if (own(doc.nodes, op.node.id)) return invalid("node id already exists");
|
|
125
|
+
if (op.node.parent !== null && !own(doc.nodes, op.node.parent)) return invalid("parent not found");
|
|
111
126
|
return {
|
|
112
127
|
...doc,
|
|
113
128
|
nodes: { ...doc.nodes, [op.node.id]: op.node },
|
|
@@ -115,11 +130,11 @@ function reduce(doc, op, opts = {}) {
|
|
|
115
130
|
};
|
|
116
131
|
}
|
|
117
132
|
case "remove_node": {
|
|
118
|
-
if (!doc.nodes
|
|
133
|
+
if (!own(doc.nodes, op.id)) return invalid("node not found");
|
|
119
134
|
const dead = /* @__PURE__ */ new Set([op.id, ...descendantIds(doc, op.id)]);
|
|
120
135
|
const nodes = {};
|
|
121
136
|
for (const id of Object.keys(doc.nodes)) {
|
|
122
|
-
if (!dead.has(id)) nodes
|
|
137
|
+
if (!dead.has(id)) setOwn(nodes, id, own(doc.nodes, id));
|
|
123
138
|
}
|
|
124
139
|
return {
|
|
125
140
|
...doc,
|
|
@@ -128,10 +143,10 @@ function reduce(doc, op, opts = {}) {
|
|
|
128
143
|
};
|
|
129
144
|
}
|
|
130
145
|
case "move_node": {
|
|
131
|
-
const node = doc.nodes
|
|
146
|
+
const node = own(doc.nodes, op.id);
|
|
132
147
|
if (!node) return invalid("node not found");
|
|
133
148
|
if (op.parent === op.id) return invalid("cannot parent a node to itself");
|
|
134
|
-
if (op.parent !== null && !doc.nodes
|
|
149
|
+
if (op.parent !== null && !own(doc.nodes, op.parent)) return invalid("new parent not found");
|
|
135
150
|
if (op.parent !== null && descendantIds(doc, op.id).includes(op.parent)) {
|
|
136
151
|
return invalid("move would create a cycle");
|
|
137
152
|
}
|
|
@@ -142,34 +157,35 @@ function reduce(doc, op, opts = {}) {
|
|
|
142
157
|
};
|
|
143
158
|
}
|
|
144
159
|
case "set_node_props": {
|
|
145
|
-
|
|
160
|
+
const node = own(doc.nodes, op.id);
|
|
161
|
+
if (!node) return invalid("node not found");
|
|
146
162
|
return patchNode(doc, op.id, {
|
|
147
|
-
props: { ...
|
|
163
|
+
props: { ...node.props, ...op.patch }
|
|
148
164
|
});
|
|
149
165
|
}
|
|
150
166
|
case "set_layout": {
|
|
151
|
-
if (!doc.nodes
|
|
167
|
+
if (!own(doc.nodes, op.id)) return invalid("node not found");
|
|
152
168
|
return patchNode(doc, op.id, { layout: op.layout });
|
|
153
169
|
}
|
|
154
170
|
case "set_style": {
|
|
155
|
-
const node = doc.nodes
|
|
171
|
+
const node = own(doc.nodes, op.id);
|
|
156
172
|
if (!node) return invalid("node not found");
|
|
157
|
-
const prev = node.style
|
|
173
|
+
const prev = own(node.style, op.breakpoint) ?? {};
|
|
158
174
|
return patchNode(doc, op.id, {
|
|
159
175
|
style: { ...node.style, [op.breakpoint]: { ...prev, ...op.patch } }
|
|
160
176
|
});
|
|
161
177
|
}
|
|
162
178
|
case "set_constraints": {
|
|
163
|
-
const node = doc.nodes
|
|
179
|
+
const node = own(doc.nodes, op.id);
|
|
164
180
|
if (!node) return invalid("node not found");
|
|
165
181
|
const base = node.constraints ?? { base: { width: "hug", height: "hug" } };
|
|
166
|
-
const prev = base
|
|
182
|
+
const prev = own(base, op.breakpoint) ?? {};
|
|
167
183
|
return patchNode(doc, op.id, {
|
|
168
184
|
constraints: { ...base, [op.breakpoint]: { ...prev, ...op.patch } }
|
|
169
185
|
});
|
|
170
186
|
}
|
|
171
187
|
case "set_animation": {
|
|
172
|
-
const node = doc.nodes
|
|
188
|
+
const node = own(doc.nodes, op.id);
|
|
173
189
|
if (!node) return invalid("node not found");
|
|
174
190
|
const list = node.animations ?? [];
|
|
175
191
|
const idx = list.findIndex((a) => a.id === op.anim.id);
|
|
@@ -178,14 +194,14 @@ function reduce(doc, op, opts = {}) {
|
|
|
178
194
|
});
|
|
179
195
|
}
|
|
180
196
|
case "remove_animation": {
|
|
181
|
-
const node = doc.nodes
|
|
197
|
+
const node = own(doc.nodes, op.id);
|
|
182
198
|
if (!node) return invalid("node not found");
|
|
183
199
|
return patchNode(doc, op.id, {
|
|
184
200
|
animations: (node.animations ?? []).filter((a) => a.id !== op.animId)
|
|
185
201
|
});
|
|
186
202
|
}
|
|
187
203
|
case "set_action": {
|
|
188
|
-
const node = doc.nodes
|
|
204
|
+
const node = own(doc.nodes, op.id);
|
|
189
205
|
if (!node) return invalid("node not found");
|
|
190
206
|
const list = node.actions ?? [];
|
|
191
207
|
let actions;
|
|
@@ -201,13 +217,13 @@ function reduce(doc, op, opts = {}) {
|
|
|
201
217
|
case "reorder_roots": {
|
|
202
218
|
const currentRoots = childrenOf(doc, null);
|
|
203
219
|
const current = new Set(currentRoots.map((n) => n.id));
|
|
204
|
-
const same = op.order.length === currentRoots.length && op.order.every((id) => current.has(id));
|
|
220
|
+
const same = op.order.length === currentRoots.length && new Set(op.order).size === op.order.length && op.order.every((id) => current.has(id));
|
|
205
221
|
if (!same) return invalid("reorder must be a permutation of the current roots");
|
|
206
222
|
const nodes = { ...doc.nodes };
|
|
207
223
|
let prev = null;
|
|
208
224
|
for (const id of op.order) {
|
|
209
225
|
prev = fancyDocCommons.fractionalKey(prev, null);
|
|
210
|
-
nodes
|
|
226
|
+
setOwn(nodes, id, { ...own(nodes, id), order: prev });
|
|
211
227
|
}
|
|
212
228
|
return { ...doc, nodes, seq: doc.seq + 1 };
|
|
213
229
|
}
|
|
@@ -290,15 +306,58 @@ function useLatestRef(value) {
|
|
|
290
306
|
}
|
|
291
307
|
|
|
292
308
|
// src/render/css.ts
|
|
293
|
-
|
|
309
|
+
function cssString(value) {
|
|
310
|
+
let out = "";
|
|
311
|
+
for (const ch of value) {
|
|
312
|
+
const code = ch.codePointAt(0);
|
|
313
|
+
if (ch === "\\" || ch === '"') out += `\\${ch}`;
|
|
314
|
+
else if (code < 32 || code === 127 || ch === "<") out += `\\${code.toString(16)} `;
|
|
315
|
+
else out += ch;
|
|
316
|
+
}
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
var selectorFor = (id) => `[data-cms="${cssString(id)}"]`;
|
|
320
|
+
var BASE_CSS = [
|
|
321
|
+
"/* fancy-cms base */",
|
|
322
|
+
":where([data-cms]) {\n box-sizing: border-box;\n}",
|
|
323
|
+
":where(h1, h2, h3, h4, h5, h6, p, ul, ol)[data-cms] {\n margin: 0;\n}",
|
|
324
|
+
":where(ul, ol)[data-cms] {\n padding-left: 1.25em;\n}",
|
|
325
|
+
":where(a[data-cms]) {\n color: inherit;\n}",
|
|
326
|
+
":where([data-cms] > img) {\n display: block;\n height: 100%;\n object-fit: cover;\n width: 100%;\n}",
|
|
327
|
+
':where([data-cms] > img[data-cms-fit="contain"]) {\n object-fit: contain;\n}',
|
|
328
|
+
':where([data-cms] > img[data-cms-fit="fill"]) {\n object-fit: fill;\n}',
|
|
329
|
+
':where([data-cms] > img[data-cms-fit="none"]) {\n object-fit: none;\n}',
|
|
330
|
+
':where([data-cms] > img[data-cms-fit="scale-down"]) {\n object-fit: scale-down;\n}',
|
|
331
|
+
":where([data-cms-button]) {\n align-items: center;\n border: 1px solid var(--cms-color-primary, #7c3aed);\n border-radius: 8px;\n cursor: pointer;\n display: inline-flex;\n font-weight: 600;\n gap: 6px;\n justify-content: center;\n line-height: 1.2;\n padding: 10px 18px;\n text-decoration: none;\n}",
|
|
332
|
+
':where([data-cms-button="primary"]) {\n background: var(--cms-color-primary, #7c3aed);\n color: #ffffff;\n}',
|
|
333
|
+
':where([data-cms-button="outline"]) {\n background: transparent;\n color: var(--cms-color-primary, #7c3aed);\n}',
|
|
334
|
+
':where([data-cms-button="ghost"]) {\n background: transparent;\n border-color: currentColor;\n color: inherit;\n}',
|
|
335
|
+
":where([data-cms-callout]) {\n background: #3b82f614;\n border-left: 3px solid #3b82f6;\n border-radius: 10px;\n padding: 12px 14px;\n}",
|
|
336
|
+
':where([data-cms-callout="success"]) {\n background: #10b98114;\n border-left-color: #10b981;\n}',
|
|
337
|
+
':where([data-cms-callout="warning"]) {\n background: #f59e0b14;\n border-left-color: #f59e0b;\n}',
|
|
338
|
+
':where([data-cms-callout="danger"]) {\n background: #ef444414;\n border-left-color: #ef4444;\n}',
|
|
339
|
+
":where([data-cms] > hr) {\n border: none;\n border-top: 1px solid #e2e8f0;\n margin: 0;\n}",
|
|
340
|
+
":where([data-cms] > pre) {\n background: #0b1220;\n border-radius: 10px;\n color: #e2e8f0;\n font-family: ui-monospace, monospace;\n font-size: 13px;\n margin: 0;\n overflow-x: auto;\n padding: 12px 14px;\n}"
|
|
341
|
+
].join("\n\n");
|
|
342
|
+
var UNITS = /* @__PURE__ */ new Set(["px", "%", "vw", "vh", "rem", "fr"]);
|
|
294
343
|
function lengthToCss(len2) {
|
|
295
|
-
|
|
344
|
+
if (typeof len2 !== "object" || len2 === null) return null;
|
|
345
|
+
const { value, unit } = len2;
|
|
346
|
+
if (typeof value !== "number" || !Number.isFinite(value) || typeof unit !== "string" || !UNITS.has(unit)) return null;
|
|
347
|
+
return `${value}${unit}`;
|
|
296
348
|
}
|
|
297
349
|
function sizeToCss(size) {
|
|
298
350
|
if (size === "fill") return "100%";
|
|
299
351
|
if (size === "hug") return "auto";
|
|
300
352
|
return lengthToCss(size);
|
|
301
353
|
}
|
|
354
|
+
function numberToCss(value) {
|
|
355
|
+
return typeof value === "number" && Number.isFinite(value) ? String(value) : null;
|
|
356
|
+
}
|
|
357
|
+
function putLen(d, props, len2) {
|
|
358
|
+
const css = lengthToCss(len2);
|
|
359
|
+
if (css !== null) for (const p of props) d[p] = css;
|
|
360
|
+
}
|
|
302
361
|
var ALIGN = {
|
|
303
362
|
start: "flex-start",
|
|
304
363
|
center: "center",
|
|
@@ -312,51 +371,150 @@ var JUSTIFY = {
|
|
|
312
371
|
between: "space-between",
|
|
313
372
|
around: "space-around"
|
|
314
373
|
};
|
|
374
|
+
function safeCssValue(value) {
|
|
375
|
+
if (/[{}<]/.test(value)) return null;
|
|
376
|
+
let depth = 0;
|
|
377
|
+
let quote = null;
|
|
378
|
+
for (const ch of value) {
|
|
379
|
+
if (quote) {
|
|
380
|
+
if (ch === quote) quote = null;
|
|
381
|
+
} else if (ch === '"' || ch === "'") {
|
|
382
|
+
quote = ch;
|
|
383
|
+
} else if (ch === "(") {
|
|
384
|
+
depth++;
|
|
385
|
+
} else if (ch === ")") {
|
|
386
|
+
depth = Math.max(0, depth - 1);
|
|
387
|
+
} else if (ch === ";" && depth === 0) {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
393
|
+
function put(d, prop, value) {
|
|
394
|
+
if (value === void 0) return;
|
|
395
|
+
const safe = safeCssValue(value);
|
|
396
|
+
if (safe !== null) d[prop] = safe;
|
|
397
|
+
}
|
|
398
|
+
var OVERFLOW = /* @__PURE__ */ new Set(["visible", "hidden", "auto"]);
|
|
399
|
+
var TEXT_TRANSFORM = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
|
|
400
|
+
var BACKGROUND_SIZE = /* @__PURE__ */ new Set(["cover", "contain", "auto"]);
|
|
401
|
+
var TEXT_ALIGN = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
|
|
402
|
+
var DIRECTIONS = /* @__PURE__ */ new Set(["row", "column"]);
|
|
403
|
+
function keyword(map, value) {
|
|
404
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(map, value) ? map[value] : null;
|
|
405
|
+
}
|
|
406
|
+
function inSet(set, value) {
|
|
407
|
+
return typeof value === "string" && set.has(value) ? value : null;
|
|
408
|
+
}
|
|
315
409
|
function styleDecls(style) {
|
|
316
410
|
const d = {};
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (style.
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (style.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (style.
|
|
333
|
-
|
|
334
|
-
if (
|
|
335
|
-
|
|
411
|
+
putLen(d, ["padding"], style.padding);
|
|
412
|
+
putLen(d, ["padding-left", "padding-right"], style.paddingX);
|
|
413
|
+
putLen(d, ["padding-top", "padding-bottom"], style.paddingY);
|
|
414
|
+
putLen(d, ["margin"], style.margin);
|
|
415
|
+
if (style.marginX === "auto") d["margin-left"] = d["margin-right"] = "auto";
|
|
416
|
+
else putLen(d, ["margin-left", "margin-right"], style.marginX);
|
|
417
|
+
putLen(d, ["margin-top", "margin-bottom"], style.marginY);
|
|
418
|
+
putLen(d, ["border-radius"], style.radius);
|
|
419
|
+
putLen(d, ["max-width"], style.maxWidth);
|
|
420
|
+
putLen(d, ["min-height"], style.minHeight);
|
|
421
|
+
put(d, "aspect-ratio", typeof style.aspectRatio === "string" ? style.aspectRatio : void 0);
|
|
422
|
+
if (style.sticky === true) {
|
|
423
|
+
d["position"] = "sticky";
|
|
424
|
+
d["top"] = "0";
|
|
425
|
+
}
|
|
426
|
+
if (typeof style.zIndex === "number" && Number.isFinite(style.zIndex)) d["z-index"] = String(Math.trunc(style.zIndex));
|
|
427
|
+
const overflow = inSet(OVERFLOW, style.overflow);
|
|
428
|
+
if (overflow) d["overflow"] = overflow;
|
|
429
|
+
put(d, "background", typeof style.background === "string" ? style.background : void 0);
|
|
430
|
+
const backgroundSize = inSet(BACKGROUND_SIZE, style.backgroundSize);
|
|
431
|
+
if (backgroundSize) d["background-size"] = backgroundSize;
|
|
432
|
+
put(d, "background-position", typeof style.backgroundPosition === "string" ? style.backgroundPosition : void 0);
|
|
433
|
+
put(d, "color", typeof style.color === "string" ? style.color : void 0);
|
|
434
|
+
const opacity = numberToCss(style.opacity);
|
|
435
|
+
if (opacity !== null) d["opacity"] = opacity;
|
|
436
|
+
put(d, "font-family", typeof style.fontFamily === "string" ? style.fontFamily : void 0);
|
|
437
|
+
putLen(d, ["font-size"], style.fontSize);
|
|
438
|
+
const fontWeight = numberToCss(style.fontWeight);
|
|
439
|
+
if (fontWeight !== null) d["font-weight"] = fontWeight;
|
|
440
|
+
const lineHeight = numberToCss(style.lineHeight);
|
|
441
|
+
if (lineHeight !== null) d["line-height"] = lineHeight;
|
|
442
|
+
const textAlign = inSet(TEXT_ALIGN, style.textAlign);
|
|
443
|
+
if (textAlign) d["text-align"] = textAlign;
|
|
444
|
+
putLen(d, ["letter-spacing"], style.letterSpacing);
|
|
445
|
+
const textTransform = inSet(TEXT_TRANSFORM, style.textTransform);
|
|
446
|
+
if (textTransform) d["text-transform"] = textTransform;
|
|
447
|
+
put(d, "border", typeof style.border === "string" ? style.border : void 0);
|
|
448
|
+
put(d, "box-shadow", typeof style.boxShadow === "string" ? style.boxShadow : void 0);
|
|
449
|
+
put(d, "transform", typeof style.transform === "string" ? style.transform : void 0);
|
|
450
|
+
put(d, "filter", typeof style.filter === "string" ? style.filter : void 0);
|
|
451
|
+
putLen(d, ["gap"], style.gap);
|
|
452
|
+
const align = keyword(ALIGN, style.align);
|
|
453
|
+
if (align) d["align-items"] = align;
|
|
454
|
+
const justify = keyword(JUSTIFY, style.justify);
|
|
455
|
+
if (justify) d["justify-content"] = justify;
|
|
336
456
|
return d;
|
|
337
457
|
}
|
|
338
|
-
function
|
|
458
|
+
function shownDisplay(node) {
|
|
459
|
+
if (node.layout === "stack") return "flex";
|
|
460
|
+
if (node.layout === "grid") return "grid";
|
|
461
|
+
return "block";
|
|
462
|
+
}
|
|
463
|
+
function gridColumns(style) {
|
|
464
|
+
const min = lengthToCss(style.columnMin);
|
|
465
|
+
if (min !== null) return `repeat(auto-fit, minmax(min(${min}, 100%), 1fr))`;
|
|
466
|
+
if (typeof style.columns === "number" && Number.isFinite(style.columns)) return `repeat(${Math.max(1, Math.trunc(style.columns))}, 1fr)`;
|
|
467
|
+
return void 0;
|
|
468
|
+
}
|
|
469
|
+
function containerDecls(node, style, isBase) {
|
|
339
470
|
const d = {};
|
|
340
471
|
if (node.layout === "stack") {
|
|
341
|
-
d["display"] = "flex";
|
|
342
|
-
|
|
472
|
+
if (isBase) d["display"] = "flex";
|
|
473
|
+
const direction = inSet(DIRECTIONS, style.direction);
|
|
474
|
+
if (isBase || direction) d["flex-direction"] = direction ?? "column";
|
|
475
|
+
if (style.wrap !== void 0) d["flex-wrap"] = style.wrap ? "wrap" : "nowrap";
|
|
343
476
|
} else if (node.layout === "grid") {
|
|
344
|
-
d["display"] = "grid";
|
|
345
|
-
|
|
346
|
-
|
|
477
|
+
if (isBase) d["display"] = "grid";
|
|
478
|
+
const cols = gridColumns(style);
|
|
479
|
+
if (isBase || cols !== void 0) d["grid-template-columns"] = cols ?? "repeat(1, 1fr)";
|
|
480
|
+
} else if (node.layout === "free" && isBase) {
|
|
347
481
|
d["position"] = "relative";
|
|
348
482
|
}
|
|
349
483
|
return d;
|
|
350
484
|
}
|
|
485
|
+
function visibilityDecls(node, style, isBase) {
|
|
486
|
+
if (style.hidden === true) return { display: "none" };
|
|
487
|
+
if (style.hidden === false && !isBase) return { display: shownDisplay(node) };
|
|
488
|
+
return {};
|
|
489
|
+
}
|
|
490
|
+
function themeDecls(theme) {
|
|
491
|
+
const d = {};
|
|
492
|
+
const walk = (value, path) => {
|
|
493
|
+
if (typeof value === "string" || typeof value === "number" && Number.isFinite(value)) {
|
|
494
|
+
const key = path.map((p) => p.toLowerCase().replace(/[^a-z0-9-]/g, "")).filter(Boolean).join("-");
|
|
495
|
+
if (key) put(d, `--cms-${key}`, String(value));
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
499
|
+
for (const [k, v] of Object.entries(value)) walk(v, [...path, k]);
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
if (typeof theme === "object" && theme !== null && !Array.isArray(theme)) {
|
|
503
|
+
for (const [k, v] of Object.entries(theme)) {
|
|
504
|
+
if (k !== "name") walk(v, [k]);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return d;
|
|
508
|
+
}
|
|
351
509
|
function constraintDecls(c, parentFree) {
|
|
352
510
|
const d = {};
|
|
353
511
|
const transforms = [];
|
|
354
512
|
if (parentFree) {
|
|
355
513
|
d["position"] = "absolute";
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
514
|
+
putLen(d, ["left"], c.left);
|
|
515
|
+
putLen(d, ["right"], c.right);
|
|
516
|
+
putLen(d, ["top"], c.top);
|
|
517
|
+
putLen(d, ["bottom"], c.bottom);
|
|
360
518
|
if (c.centerX) {
|
|
361
519
|
d["left"] = "50%";
|
|
362
520
|
transforms.push("translateX(-50%)");
|
|
@@ -367,13 +525,15 @@ function constraintDecls(c, parentFree) {
|
|
|
367
525
|
}
|
|
368
526
|
if (transforms.length) d["transform"] = transforms.join(" ");
|
|
369
527
|
}
|
|
370
|
-
|
|
371
|
-
if (
|
|
528
|
+
const width = sizeToCss(c.width);
|
|
529
|
+
if (width !== null) d["width"] = width;
|
|
530
|
+
const height = sizeToCss(c.height);
|
|
531
|
+
if (height !== null) d["height"] = height;
|
|
372
532
|
return d;
|
|
373
533
|
}
|
|
374
534
|
function isParentFree(doc, node) {
|
|
375
535
|
if (node.parent === null) return false;
|
|
376
|
-
const parent = doc.nodes
|
|
536
|
+
const parent = own(doc.nodes, node.parent);
|
|
377
537
|
return !parent || parent.layout === void 0 || parent.layout === "free";
|
|
378
538
|
}
|
|
379
539
|
function serializeRule(selector, decls, indent) {
|
|
@@ -388,24 +548,31 @@ function emitDocCss(doc) {
|
|
|
388
548
|
if (!hasNodeMap(doc)) return "";
|
|
389
549
|
const breakpointPx = DEFAULT_BREAKPOINTS;
|
|
390
550
|
const blocks = [];
|
|
551
|
+
const themeRule = serializeRule(":root", themeDecls(doc.theme), "");
|
|
552
|
+
if (themeRule) blocks.push(themeRule);
|
|
391
553
|
for (const id of Object.keys(doc.nodes).sort()) {
|
|
392
|
-
const node = doc.nodes[id];
|
|
554
|
+
const node = withNodeDefaults(doc.nodes[id]);
|
|
393
555
|
const selector = selectorFor(id);
|
|
394
556
|
const parentFree = isParentFree(doc, node);
|
|
395
557
|
const base = {
|
|
396
|
-
...
|
|
558
|
+
...containerDecls(node, node.style.base, true),
|
|
397
559
|
...node.constraints ? constraintDecls(node.constraints.base, parentFree) : {},
|
|
398
|
-
...styleDecls(node.style.base)
|
|
560
|
+
...styleDecls(node.style.base),
|
|
561
|
+
...visibilityDecls(node, node.style.base, true)
|
|
399
562
|
};
|
|
400
563
|
const baseRule = serializeRule(selector, base, "");
|
|
401
564
|
if (baseRule) blocks.push(baseRule);
|
|
402
565
|
for (const bp of doc.breakpoints) {
|
|
403
566
|
if (bp === "base") continue;
|
|
404
|
-
const px = breakpointPx
|
|
405
|
-
if (px
|
|
567
|
+
const px = own(breakpointPx, bp);
|
|
568
|
+
if (typeof px !== "number") continue;
|
|
569
|
+
const bpStyle = own(node.style, bp);
|
|
570
|
+
const bpConstraints = node.constraints ? own(node.constraints, bp) : void 0;
|
|
406
571
|
const decls = {
|
|
407
|
-
...
|
|
408
|
-
...
|
|
572
|
+
...bpStyle ? containerDecls(node, bpStyle, false) : {},
|
|
573
|
+
...bpConstraints ? constraintDecls(bpConstraints, parentFree) : {},
|
|
574
|
+
...bpStyle ? styleDecls(bpStyle) : {},
|
|
575
|
+
...bpStyle ? visibilityDecls(node, bpStyle, false) : {}
|
|
409
576
|
};
|
|
410
577
|
const inner = serializeRule(selector, decls, " ");
|
|
411
578
|
if (inner) blocks.push(`@media (min-width: ${px}px) {
|
|
@@ -413,7 +580,8 @@ ${inner}
|
|
|
413
580
|
}`);
|
|
414
581
|
}
|
|
415
582
|
}
|
|
416
|
-
|
|
583
|
+
if (blocks.length === 0 && Object.keys(doc.nodes).length === 0) return "";
|
|
584
|
+
return [BASE_CSS, ...blocks].join("\n\n") + "\n";
|
|
417
585
|
}
|
|
418
586
|
function getPath(data, path) {
|
|
419
587
|
return path.split(".").reduce((o, k) => o == null ? void 0 : o[k], data);
|
|
@@ -423,6 +591,55 @@ function resolveValue(v, data) {
|
|
|
423
591
|
if (isBinding(v)) return getPath(data, v.$bind);
|
|
424
592
|
return v;
|
|
425
593
|
}
|
|
594
|
+
var LANDMARK_TAGS = ["section", "header", "nav", "main", "footer", "article", "aside", "div"];
|
|
595
|
+
var LANDMARKS = new Set(LANDMARK_TAGS);
|
|
596
|
+
var CONTAINERS = /* @__PURE__ */ new Set(["stack", "grid", "frame", "shape", "card"]);
|
|
597
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
598
|
+
function safeHref(raw) {
|
|
599
|
+
const href = raw.trim();
|
|
600
|
+
if (!href) return null;
|
|
601
|
+
const probe = href.replace(/[\u0000- \u007f]+/g, "");
|
|
602
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(probe);
|
|
603
|
+
if (scheme && !SAFE_SCHEMES.has(scheme[1].toLowerCase())) return null;
|
|
604
|
+
return href;
|
|
605
|
+
}
|
|
606
|
+
var ANCHOR_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
|
|
607
|
+
function anchorFor(node, text) {
|
|
608
|
+
const anchor = text(node.props.anchor);
|
|
609
|
+
return ANCHOR_PATTERN.test(anchor) ? anchor : null;
|
|
610
|
+
}
|
|
611
|
+
function elementFor(node, text) {
|
|
612
|
+
const as = text(node.props.as);
|
|
613
|
+
switch (node.type) {
|
|
614
|
+
case "heading": {
|
|
615
|
+
const level = Number(text(node.props.level) || 2);
|
|
616
|
+
return { tag: Number.isInteger(level) && level >= 1 && level <= 6 ? `h${level}` : "h2" };
|
|
617
|
+
}
|
|
618
|
+
case "text":
|
|
619
|
+
return { tag: "p" };
|
|
620
|
+
case "section":
|
|
621
|
+
return { tag: LANDMARKS.has(as) ? as : "section" };
|
|
622
|
+
case "list":
|
|
623
|
+
return { tag: text(node.props.ordered) === "true" ? "ol" : "ul" };
|
|
624
|
+
case "button":
|
|
625
|
+
case "link": {
|
|
626
|
+
const href = safeHref(text(node.props.href));
|
|
627
|
+
const attrs = {};
|
|
628
|
+
if (href !== null) {
|
|
629
|
+
attrs.href = href;
|
|
630
|
+
if (/^https?:\/\//i.test(href)) attrs.rel = "noopener noreferrer";
|
|
631
|
+
}
|
|
632
|
+
if (node.type === "button") attrs["data-cms-button"] = BUTTON_VARIANTS.has(text(node.props.variant)) ? text(node.props.variant) : "primary";
|
|
633
|
+
return { tag: href !== null ? "a" : node.type === "link" ? "span" : "div", attrs };
|
|
634
|
+
}
|
|
635
|
+
case "callout": {
|
|
636
|
+
const variant = text(node.props.variant);
|
|
637
|
+
return { tag: "div", attrs: { "data-cms-callout": CALLOUT_VARIANTS.has(variant) ? variant : "info" } };
|
|
638
|
+
}
|
|
639
|
+
default:
|
|
640
|
+
return { tag: CONTAINERS.has(node.type) && LANDMARKS.has(as) ? as : "div" };
|
|
641
|
+
}
|
|
642
|
+
}
|
|
426
643
|
var defaultRegistry = {
|
|
427
644
|
section: ({ children }) => children,
|
|
428
645
|
stack: ({ children }) => children,
|
|
@@ -435,47 +652,38 @@ var defaultRegistry = {
|
|
|
435
652
|
// Rich text — inline-formatted HTML (gradient spans, links, <code>, …) that
|
|
436
653
|
// plain `text` can't express. The author owns the markup (it's their content).
|
|
437
654
|
richtext: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: text(node.props.html) } }),
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const
|
|
442
|
-
return
|
|
443
|
-
},
|
|
444
|
-
code: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { margin: 0, padding: "12px 14px", borderRadius: 10, background: "#0b1220", color: "#e2e8f0", fontFamily: "ui-monospace, monospace", fontSize: 13, overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: text(node.props.content) }) }),
|
|
445
|
-
button: ({ node, text }) => {
|
|
446
|
-
const label2 = text(node.props.label) || text(node.props.content) || "Button";
|
|
447
|
-
const variant = text(node.props.variant) || "primary";
|
|
448
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cms-button": variant, style: buttonStyle(variant), children: label2 });
|
|
655
|
+
// The [data-cms] element is already the <a> (see elementFor), so a link renders its label only.
|
|
656
|
+
link: ({ node, text }) => text(node.props.content) || "link",
|
|
657
|
+
list: ({ node, resolve }) => {
|
|
658
|
+
const items = resolve(node.props.items);
|
|
659
|
+
return Array.isArray(items) ? items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item === null || item === void 0 ? "" : String(item) }, i)) : null;
|
|
449
660
|
},
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
661
|
+
// Looks come from the base stylesheet (render/css.ts BASE_CSS), never from
|
|
662
|
+
// inline style objects, so the PHP renderer emits the same markup.
|
|
663
|
+
divider: () => /* @__PURE__ */ jsxRuntime.jsx("hr", {}),
|
|
664
|
+
callout: ({ node, text }) => text(node.props.content) || "Callout",
|
|
665
|
+
code: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx("pre", { children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: text(node.props.content) }) }),
|
|
666
|
+
button: ({ node, text }) => text(node.props.label) || text(node.props.content) || "Button",
|
|
667
|
+
image: ({ node, text }) => {
|
|
668
|
+
const fit = text(node.props.fit);
|
|
669
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
670
|
+
"img",
|
|
671
|
+
{
|
|
672
|
+
src: text(node.props.src) || void 0,
|
|
673
|
+
alt: text(node.props.alt),
|
|
674
|
+
loading: text(node.props.loading) === "eager" ? "eager" : "lazy",
|
|
675
|
+
"data-cms-fit": IMAGE_FITS.has(fit) && fit !== "cover" ? fit : void 0
|
|
676
|
+
}
|
|
677
|
+
);
|
|
678
|
+
}
|
|
458
679
|
};
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
alignItems: "center",
|
|
463
|
-
gap: 6,
|
|
464
|
-
padding: "9px 16px",
|
|
465
|
-
borderRadius: 8,
|
|
466
|
-
fontWeight: 600,
|
|
467
|
-
fontSize: 14,
|
|
468
|
-
lineHeight: 1,
|
|
469
|
-
cursor: "pointer",
|
|
470
|
-
userSelect: "none"
|
|
471
|
-
};
|
|
472
|
-
if (variant === "ghost") return { ...base, background: "transparent", color: "inherit", border: "1px solid currentColor" };
|
|
473
|
-
if (variant === "outline") return { ...base, background: "transparent", color: "#7c3aed", border: "1px solid #7c3aed" };
|
|
474
|
-
return { ...base, background: "#7c3aed", color: "#fff", border: "1px solid #7c3aed" };
|
|
475
|
-
}
|
|
680
|
+
var IMAGE_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "none", "scale-down"]);
|
|
681
|
+
var BUTTON_VARIANTS = /* @__PURE__ */ new Set(["primary", "outline", "ghost"]);
|
|
682
|
+
var CALLOUT_VARIANTS = /* @__PURE__ */ new Set(["info", "success", "warning", "danger"]);
|
|
476
683
|
function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
477
|
-
const
|
|
478
|
-
if (!
|
|
684
|
+
const stored = own(doc.nodes, id);
|
|
685
|
+
if (!stored) return null;
|
|
686
|
+
const node = withNodeDefaults(stored);
|
|
479
687
|
if (node.type === "repeater") {
|
|
480
688
|
const arr = resolveValue(node.props.items, data);
|
|
481
689
|
const items = Array.isArray(arr) ? arr : [];
|
|
@@ -490,7 +698,19 @@ function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
|
490
698
|
};
|
|
491
699
|
const renderer = registry[node.type];
|
|
492
700
|
const inner = renderer ? renderer({ node, doc, children: kids, data, resolve, text }) : node.island ? null : kids;
|
|
493
|
-
|
|
701
|
+
const { tag, attrs } = elementFor(node, text);
|
|
702
|
+
const anchor = anchorFor(node, text);
|
|
703
|
+
return react.createElement(
|
|
704
|
+
tag,
|
|
705
|
+
{
|
|
706
|
+
"data-cms": node.id,
|
|
707
|
+
"data-cms-island": node.island ? node.type : void 0,
|
|
708
|
+
className: node.className,
|
|
709
|
+
...anchor ? { id: anchor } : {},
|
|
710
|
+
...attrs
|
|
711
|
+
},
|
|
712
|
+
inner
|
|
713
|
+
);
|
|
494
714
|
}
|
|
495
715
|
function CmsPage({
|
|
496
716
|
doc: input3,
|
|
@@ -808,7 +1028,8 @@ var ADD_DEFAULTS = {
|
|
|
808
1028
|
divider: { type: "divider", props: {}, style: {} },
|
|
809
1029
|
code: { type: "code", props: { content: "npm install @particle-academy/react-fancy", lang: "bash" }, style: {} },
|
|
810
1030
|
richtext: { type: "richtext", props: { html: "<p>Rich <strong>text</strong> with <em>inline</em> formatting.</p>" }, style: {} },
|
|
811
|
-
repeater: { type: "repeater", props: { items: "" }, style: { gap: { value: 12, unit: "px" } }, layout: "stack" }
|
|
1031
|
+
repeater: { type: "repeater", props: { items: "" }, style: { gap: { value: 12, unit: "px" } }, layout: "stack" },
|
|
1032
|
+
list: { type: "list", props: { items: ["First point", "Second point"], ordered: false }, style: {} }
|
|
812
1033
|
};
|
|
813
1034
|
var ADD_MENU = [
|
|
814
1035
|
{ kind: "heading", label: "Heading" },
|
|
@@ -824,6 +1045,7 @@ var ADD_MENU = [
|
|
|
824
1045
|
{ kind: "divider", label: "Divider" },
|
|
825
1046
|
{ kind: "code", label: "Code" },
|
|
826
1047
|
{ kind: "richtext", label: "Rich text" },
|
|
1048
|
+
{ kind: "list", label: "List" },
|
|
827
1049
|
{ kind: "repeater", label: "Repeater" }
|
|
828
1050
|
];
|
|
829
1051
|
function resolveInsertParent(doc, selectedId) {
|