@particle-academy/fancy-cms-ui 0.7.0 → 0.8.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 +395 -0
- package/LICENSE +21 -0
- package/agent-guide/professional-pages.md +107 -0
- package/dist/{chunk-YOAZRQKN.js → chunk-IOMIYI45.js} +6 -4
- package/dist/{chunk-YOAZRQKN.js.map → chunk-IOMIYI45.js.map} +1 -1
- package/dist/{chunk-IWQQINCF.js → chunk-SJYKJ6SG.js} +300 -83
- package/dist/chunk-SJYKJ6SG.js.map +1 -0
- package/dist/{chunk-BWGAUTKG.js → chunk-V6QIOJI7.js} +3 -3
- package/dist/{chunk-BWGAUTKG.js.map → chunk-V6QIOJI7.js.map} +1 -1
- package/dist/editor.cjs +313 -94
- 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 +301 -81
- 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 +288 -62
- 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-CiPir27w.d.cts → registry-B4FaQDip.d.cts} +31 -1
- package/dist/{registry-CiPir27w.d.ts → registry-B4FaQDip.d.ts} +31 -1
- package/dist/{useEditor-CoxNeC53.d.cts → useEditor-CIRm2oi-.d.cts} +1 -1
- package/dist/{useEditor-DAE36OYU.d.ts → useEditor-DZDD9qpS.d.ts} +1 -1
- package/package.json +107 -105
- package/dist/chunk-IWQQINCF.js.map +0 -1
package/dist/editor.cjs
CHANGED
|
@@ -6,17 +6,59 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
6
6
|
var reactFancy = require('@particle-academy/react-fancy');
|
|
7
7
|
|
|
8
8
|
// src/editor/Editor.tsx
|
|
9
|
+
|
|
10
|
+
// src/document/types.ts
|
|
11
|
+
function isBinding(v) {
|
|
12
|
+
return typeof v === "object" && v !== null && typeof v.$bind === "string";
|
|
13
|
+
}
|
|
14
|
+
function hasNodeMap(value) {
|
|
15
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
16
|
+
const nodes = value.nodes;
|
|
17
|
+
return typeof nodes === "object" && nodes !== null && !Array.isArray(nodes);
|
|
18
|
+
}
|
|
19
|
+
var DEFAULT_BREAKPOINTS = {
|
|
20
|
+
base: 0,
|
|
21
|
+
md: 768,
|
|
22
|
+
lg: 1024
|
|
23
|
+
};
|
|
24
|
+
function emptyDoc(id, slug = "/") {
|
|
25
|
+
return {
|
|
26
|
+
id,
|
|
27
|
+
seq: 0,
|
|
28
|
+
meta: { title: "Untitled", slug, scrollMode: "smooth" },
|
|
29
|
+
theme: { name: "default" },
|
|
30
|
+
breakpoints: ["base", "md", "lg"],
|
|
31
|
+
nodes: {}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/document/own.ts
|
|
36
|
+
var has = Object.prototype.hasOwnProperty;
|
|
37
|
+
function own(map, key) {
|
|
38
|
+
return map != null && has.call(map, key) ? map[key] : void 0;
|
|
39
|
+
}
|
|
40
|
+
function setOwn(map, key, value) {
|
|
41
|
+
Object.defineProperty(map, key, { value, writable: true, enumerable: true, configurable: true });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/document/migrate.ts
|
|
45
|
+
function normalizeDoc(input3) {
|
|
46
|
+
if (hasNodeMap(input3)) return input3;
|
|
47
|
+
const id = typeof input3 === "object" && input3 !== null && !Array.isArray(input3) && typeof input3.id === "string" ? input3.id : "malformed";
|
|
48
|
+
return emptyDoc(id);
|
|
49
|
+
}
|
|
9
50
|
function migrateDocVerbose(input3) {
|
|
10
|
-
const
|
|
51
|
+
const normalized = normalizeDoc(input3);
|
|
52
|
+
const legacy = normalized;
|
|
11
53
|
const sections = legacy.sections;
|
|
12
54
|
if (!Array.isArray(sections)) {
|
|
13
|
-
return { doc:
|
|
55
|
+
return { doc: normalized, migrated: false, danglingIds: [], unlistedRootIds: [] };
|
|
14
56
|
}
|
|
15
57
|
const danglingIds = [];
|
|
16
58
|
const ordered = [];
|
|
17
59
|
const seen = /* @__PURE__ */ new Set();
|
|
18
60
|
for (const id of sections) {
|
|
19
|
-
if (!
|
|
61
|
+
if (!own(normalized.nodes, id)) {
|
|
20
62
|
danglingIds.push(id);
|
|
21
63
|
continue;
|
|
22
64
|
}
|
|
@@ -24,13 +66,13 @@ function migrateDocVerbose(input3) {
|
|
|
24
66
|
seen.add(id);
|
|
25
67
|
ordered.push(id);
|
|
26
68
|
}
|
|
27
|
-
const unlistedRootIds = Object.values(
|
|
69
|
+
const unlistedRootIds = Object.values(normalized.nodes).filter((n) => n.parent === null && !seen.has(n.id)).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0).map((n) => n.id);
|
|
28
70
|
const finalOrder = [...ordered, ...unlistedRootIds];
|
|
29
|
-
const nodes = { ...
|
|
71
|
+
const nodes = { ...normalized.nodes };
|
|
30
72
|
let prev = null;
|
|
31
73
|
for (const id of finalOrder) {
|
|
32
74
|
const key = fancyDocCommons.fractionalKey(prev, null);
|
|
33
|
-
nodes
|
|
75
|
+
setOwn(nodes, id, { ...own(nodes, id), order: key });
|
|
34
76
|
prev = key;
|
|
35
77
|
}
|
|
36
78
|
const { sections: _dropped, ...rest } = legacy;
|
|
@@ -48,6 +90,7 @@ function childrenOf(doc, parent) {
|
|
|
48
90
|
return fancyDocCommons.childrenOf(doc, parent);
|
|
49
91
|
}
|
|
50
92
|
function rootIds(doc) {
|
|
93
|
+
if (!hasNodeMap(doc)) return [];
|
|
51
94
|
return childrenOf(doc, null).map((n) => n.id);
|
|
52
95
|
}
|
|
53
96
|
function lastRootId(doc) {
|
|
@@ -58,7 +101,7 @@ function descendantIds(doc, id) {
|
|
|
58
101
|
return fancyDocCommons.descendantsOf(doc, id);
|
|
59
102
|
}
|
|
60
103
|
function patchNode(doc, id, patch) {
|
|
61
|
-
const node = doc.nodes
|
|
104
|
+
const node = own(doc.nodes, id);
|
|
62
105
|
return {
|
|
63
106
|
...doc,
|
|
64
107
|
nodes: { ...doc.nodes, [id]: { ...node, ...patch } },
|
|
@@ -72,8 +115,8 @@ function reduce(doc, op, opts = {}) {
|
|
|
72
115
|
};
|
|
73
116
|
switch (op.t) {
|
|
74
117
|
case "insert_node": {
|
|
75
|
-
if (doc.nodes
|
|
76
|
-
if (op.node.parent !== null && !doc.nodes
|
|
118
|
+
if (own(doc.nodes, op.node.id)) return invalid("node id already exists");
|
|
119
|
+
if (op.node.parent !== null && !own(doc.nodes, op.node.parent)) return invalid("parent not found");
|
|
77
120
|
return {
|
|
78
121
|
...doc,
|
|
79
122
|
nodes: { ...doc.nodes, [op.node.id]: op.node },
|
|
@@ -81,11 +124,11 @@ function reduce(doc, op, opts = {}) {
|
|
|
81
124
|
};
|
|
82
125
|
}
|
|
83
126
|
case "remove_node": {
|
|
84
|
-
if (!doc.nodes
|
|
127
|
+
if (!own(doc.nodes, op.id)) return invalid("node not found");
|
|
85
128
|
const dead = /* @__PURE__ */ new Set([op.id, ...descendantIds(doc, op.id)]);
|
|
86
129
|
const nodes = {};
|
|
87
130
|
for (const id of Object.keys(doc.nodes)) {
|
|
88
|
-
if (!dead.has(id)) nodes
|
|
131
|
+
if (!dead.has(id)) setOwn(nodes, id, own(doc.nodes, id));
|
|
89
132
|
}
|
|
90
133
|
return {
|
|
91
134
|
...doc,
|
|
@@ -94,10 +137,10 @@ function reduce(doc, op, opts = {}) {
|
|
|
94
137
|
};
|
|
95
138
|
}
|
|
96
139
|
case "move_node": {
|
|
97
|
-
const node = doc.nodes
|
|
140
|
+
const node = own(doc.nodes, op.id);
|
|
98
141
|
if (!node) return invalid("node not found");
|
|
99
142
|
if (op.parent === op.id) return invalid("cannot parent a node to itself");
|
|
100
|
-
if (op.parent !== null && !doc.nodes
|
|
143
|
+
if (op.parent !== null && !own(doc.nodes, op.parent)) return invalid("new parent not found");
|
|
101
144
|
if (op.parent !== null && descendantIds(doc, op.id).includes(op.parent)) {
|
|
102
145
|
return invalid("move would create a cycle");
|
|
103
146
|
}
|
|
@@ -108,34 +151,35 @@ function reduce(doc, op, opts = {}) {
|
|
|
108
151
|
};
|
|
109
152
|
}
|
|
110
153
|
case "set_node_props": {
|
|
111
|
-
|
|
154
|
+
const node = own(doc.nodes, op.id);
|
|
155
|
+
if (!node) return invalid("node not found");
|
|
112
156
|
return patchNode(doc, op.id, {
|
|
113
|
-
props: { ...
|
|
157
|
+
props: { ...node.props, ...op.patch }
|
|
114
158
|
});
|
|
115
159
|
}
|
|
116
160
|
case "set_layout": {
|
|
117
|
-
if (!doc.nodes
|
|
161
|
+
if (!own(doc.nodes, op.id)) return invalid("node not found");
|
|
118
162
|
return patchNode(doc, op.id, { layout: op.layout });
|
|
119
163
|
}
|
|
120
164
|
case "set_style": {
|
|
121
|
-
const node = doc.nodes
|
|
165
|
+
const node = own(doc.nodes, op.id);
|
|
122
166
|
if (!node) return invalid("node not found");
|
|
123
|
-
const prev = node.style
|
|
167
|
+
const prev = own(node.style, op.breakpoint) ?? {};
|
|
124
168
|
return patchNode(doc, op.id, {
|
|
125
169
|
style: { ...node.style, [op.breakpoint]: { ...prev, ...op.patch } }
|
|
126
170
|
});
|
|
127
171
|
}
|
|
128
172
|
case "set_constraints": {
|
|
129
|
-
const node = doc.nodes
|
|
173
|
+
const node = own(doc.nodes, op.id);
|
|
130
174
|
if (!node) return invalid("node not found");
|
|
131
175
|
const base = node.constraints ?? { base: { width: "hug", height: "hug" } };
|
|
132
|
-
const prev = base
|
|
176
|
+
const prev = own(base, op.breakpoint) ?? {};
|
|
133
177
|
return patchNode(doc, op.id, {
|
|
134
178
|
constraints: { ...base, [op.breakpoint]: { ...prev, ...op.patch } }
|
|
135
179
|
});
|
|
136
180
|
}
|
|
137
181
|
case "set_animation": {
|
|
138
|
-
const node = doc.nodes
|
|
182
|
+
const node = own(doc.nodes, op.id);
|
|
139
183
|
if (!node) return invalid("node not found");
|
|
140
184
|
const list = node.animations ?? [];
|
|
141
185
|
const idx = list.findIndex((a) => a.id === op.anim.id);
|
|
@@ -144,14 +188,14 @@ function reduce(doc, op, opts = {}) {
|
|
|
144
188
|
});
|
|
145
189
|
}
|
|
146
190
|
case "remove_animation": {
|
|
147
|
-
const node = doc.nodes
|
|
191
|
+
const node = own(doc.nodes, op.id);
|
|
148
192
|
if (!node) return invalid("node not found");
|
|
149
193
|
return patchNode(doc, op.id, {
|
|
150
194
|
animations: (node.animations ?? []).filter((a) => a.id !== op.animId)
|
|
151
195
|
});
|
|
152
196
|
}
|
|
153
197
|
case "set_action": {
|
|
154
|
-
const node = doc.nodes
|
|
198
|
+
const node = own(doc.nodes, op.id);
|
|
155
199
|
if (!node) return invalid("node not found");
|
|
156
200
|
const list = node.actions ?? [];
|
|
157
201
|
let actions;
|
|
@@ -167,13 +211,13 @@ function reduce(doc, op, opts = {}) {
|
|
|
167
211
|
case "reorder_roots": {
|
|
168
212
|
const currentRoots = childrenOf(doc, null);
|
|
169
213
|
const current = new Set(currentRoots.map((n) => n.id));
|
|
170
|
-
const same = op.order.length === currentRoots.length && op.order.every((id) => current.has(id));
|
|
214
|
+
const same = op.order.length === currentRoots.length && new Set(op.order).size === op.order.length && op.order.every((id) => current.has(id));
|
|
171
215
|
if (!same) return invalid("reorder must be a permutation of the current roots");
|
|
172
216
|
const nodes = { ...doc.nodes };
|
|
173
217
|
let prev = null;
|
|
174
218
|
for (const id of op.order) {
|
|
175
219
|
prev = fancyDocCommons.fractionalKey(prev, null);
|
|
176
|
-
nodes
|
|
220
|
+
setOwn(nodes, id, { ...own(nodes, id), order: prev });
|
|
177
221
|
}
|
|
178
222
|
return { ...doc, nodes, seq: doc.seq + 1 };
|
|
179
223
|
}
|
|
@@ -255,36 +299,37 @@ function useLatestRef(value) {
|
|
|
255
299
|
return ref;
|
|
256
300
|
}
|
|
257
301
|
|
|
258
|
-
// src/document/types.ts
|
|
259
|
-
function isBinding(v) {
|
|
260
|
-
return typeof v === "object" && v !== null && typeof v.$bind === "string";
|
|
261
|
-
}
|
|
262
|
-
var DEFAULT_BREAKPOINTS = {
|
|
263
|
-
base: 0,
|
|
264
|
-
md: 768,
|
|
265
|
-
lg: 1024
|
|
266
|
-
};
|
|
267
|
-
function emptyDoc(id, slug = "/") {
|
|
268
|
-
return {
|
|
269
|
-
id,
|
|
270
|
-
seq: 0,
|
|
271
|
-
meta: { title: "Untitled", slug, scrollMode: "smooth" },
|
|
272
|
-
theme: { name: "default" },
|
|
273
|
-
breakpoints: ["base", "md", "lg"],
|
|
274
|
-
nodes: {}
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
302
|
// src/render/css.ts
|
|
279
|
-
|
|
303
|
+
function cssString(value) {
|
|
304
|
+
let out = "";
|
|
305
|
+
for (const ch of value) {
|
|
306
|
+
const code = ch.codePointAt(0);
|
|
307
|
+
if (ch === "\\" || ch === '"') out += `\\${ch}`;
|
|
308
|
+
else if (code < 32 || code === 127 || ch === "<") out += `\\${code.toString(16)} `;
|
|
309
|
+
else out += ch;
|
|
310
|
+
}
|
|
311
|
+
return out;
|
|
312
|
+
}
|
|
313
|
+
var selectorFor = (id) => `[data-cms="${cssString(id)}"]`;
|
|
314
|
+
var UNITS = /* @__PURE__ */ new Set(["px", "%", "vw", "vh", "rem", "fr"]);
|
|
280
315
|
function lengthToCss(len2) {
|
|
281
|
-
|
|
316
|
+
if (typeof len2 !== "object" || len2 === null) return null;
|
|
317
|
+
const { value, unit } = len2;
|
|
318
|
+
if (typeof value !== "number" || !Number.isFinite(value) || typeof unit !== "string" || !UNITS.has(unit)) return null;
|
|
319
|
+
return `${value}${unit}`;
|
|
282
320
|
}
|
|
283
321
|
function sizeToCss(size) {
|
|
284
322
|
if (size === "fill") return "100%";
|
|
285
323
|
if (size === "hug") return "auto";
|
|
286
324
|
return lengthToCss(size);
|
|
287
325
|
}
|
|
326
|
+
function numberToCss(value) {
|
|
327
|
+
return typeof value === "number" && Number.isFinite(value) ? String(value) : null;
|
|
328
|
+
}
|
|
329
|
+
function putLen(d, props, len2) {
|
|
330
|
+
const css = lengthToCss(len2);
|
|
331
|
+
if (css !== null) for (const p of props) d[p] = css;
|
|
332
|
+
}
|
|
288
333
|
var ALIGN = {
|
|
289
334
|
start: "flex-start",
|
|
290
335
|
center: "center",
|
|
@@ -298,51 +343,150 @@ var JUSTIFY = {
|
|
|
298
343
|
between: "space-between",
|
|
299
344
|
around: "space-around"
|
|
300
345
|
};
|
|
346
|
+
function safeCssValue(value) {
|
|
347
|
+
if (/[{}<]/.test(value)) return null;
|
|
348
|
+
let depth = 0;
|
|
349
|
+
let quote = null;
|
|
350
|
+
for (const ch of value) {
|
|
351
|
+
if (quote) {
|
|
352
|
+
if (ch === quote) quote = null;
|
|
353
|
+
} else if (ch === '"' || ch === "'") {
|
|
354
|
+
quote = ch;
|
|
355
|
+
} else if (ch === "(") {
|
|
356
|
+
depth++;
|
|
357
|
+
} else if (ch === ")") {
|
|
358
|
+
depth = Math.max(0, depth - 1);
|
|
359
|
+
} else if (ch === ";" && depth === 0) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return value;
|
|
364
|
+
}
|
|
365
|
+
function put(d, prop, value) {
|
|
366
|
+
if (value === void 0) return;
|
|
367
|
+
const safe = safeCssValue(value);
|
|
368
|
+
if (safe !== null) d[prop] = safe;
|
|
369
|
+
}
|
|
370
|
+
var OVERFLOW = /* @__PURE__ */ new Set(["visible", "hidden", "auto"]);
|
|
371
|
+
var TEXT_TRANSFORM = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
|
|
372
|
+
var BACKGROUND_SIZE = /* @__PURE__ */ new Set(["cover", "contain", "auto"]);
|
|
373
|
+
var TEXT_ALIGN = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
|
|
374
|
+
var DIRECTIONS = /* @__PURE__ */ new Set(["row", "column"]);
|
|
375
|
+
function keyword(map, value) {
|
|
376
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(map, value) ? map[value] : null;
|
|
377
|
+
}
|
|
378
|
+
function inSet(set, value) {
|
|
379
|
+
return typeof value === "string" && set.has(value) ? value : null;
|
|
380
|
+
}
|
|
301
381
|
function styleDecls(style) {
|
|
302
382
|
const d = {};
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (style.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (style.
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if (style.
|
|
319
|
-
|
|
320
|
-
if (
|
|
321
|
-
|
|
383
|
+
putLen(d, ["padding"], style.padding);
|
|
384
|
+
putLen(d, ["padding-left", "padding-right"], style.paddingX);
|
|
385
|
+
putLen(d, ["padding-top", "padding-bottom"], style.paddingY);
|
|
386
|
+
putLen(d, ["margin"], style.margin);
|
|
387
|
+
if (style.marginX === "auto") d["margin-left"] = d["margin-right"] = "auto";
|
|
388
|
+
else putLen(d, ["margin-left", "margin-right"], style.marginX);
|
|
389
|
+
putLen(d, ["margin-top", "margin-bottom"], style.marginY);
|
|
390
|
+
putLen(d, ["border-radius"], style.radius);
|
|
391
|
+
putLen(d, ["max-width"], style.maxWidth);
|
|
392
|
+
putLen(d, ["min-height"], style.minHeight);
|
|
393
|
+
put(d, "aspect-ratio", typeof style.aspectRatio === "string" ? style.aspectRatio : void 0);
|
|
394
|
+
if (style.sticky === true) {
|
|
395
|
+
d["position"] = "sticky";
|
|
396
|
+
d["top"] = "0";
|
|
397
|
+
}
|
|
398
|
+
if (typeof style.zIndex === "number" && Number.isFinite(style.zIndex)) d["z-index"] = String(Math.trunc(style.zIndex));
|
|
399
|
+
const overflow = inSet(OVERFLOW, style.overflow);
|
|
400
|
+
if (overflow) d["overflow"] = overflow;
|
|
401
|
+
put(d, "background", typeof style.background === "string" ? style.background : void 0);
|
|
402
|
+
const backgroundSize = inSet(BACKGROUND_SIZE, style.backgroundSize);
|
|
403
|
+
if (backgroundSize) d["background-size"] = backgroundSize;
|
|
404
|
+
put(d, "background-position", typeof style.backgroundPosition === "string" ? style.backgroundPosition : void 0);
|
|
405
|
+
put(d, "color", typeof style.color === "string" ? style.color : void 0);
|
|
406
|
+
const opacity = numberToCss(style.opacity);
|
|
407
|
+
if (opacity !== null) d["opacity"] = opacity;
|
|
408
|
+
put(d, "font-family", typeof style.fontFamily === "string" ? style.fontFamily : void 0);
|
|
409
|
+
putLen(d, ["font-size"], style.fontSize);
|
|
410
|
+
const fontWeight = numberToCss(style.fontWeight);
|
|
411
|
+
if (fontWeight !== null) d["font-weight"] = fontWeight;
|
|
412
|
+
const lineHeight = numberToCss(style.lineHeight);
|
|
413
|
+
if (lineHeight !== null) d["line-height"] = lineHeight;
|
|
414
|
+
const textAlign = inSet(TEXT_ALIGN, style.textAlign);
|
|
415
|
+
if (textAlign) d["text-align"] = textAlign;
|
|
416
|
+
putLen(d, ["letter-spacing"], style.letterSpacing);
|
|
417
|
+
const textTransform = inSet(TEXT_TRANSFORM, style.textTransform);
|
|
418
|
+
if (textTransform) d["text-transform"] = textTransform;
|
|
419
|
+
put(d, "border", typeof style.border === "string" ? style.border : void 0);
|
|
420
|
+
put(d, "box-shadow", typeof style.boxShadow === "string" ? style.boxShadow : void 0);
|
|
421
|
+
put(d, "transform", typeof style.transform === "string" ? style.transform : void 0);
|
|
422
|
+
put(d, "filter", typeof style.filter === "string" ? style.filter : void 0);
|
|
423
|
+
putLen(d, ["gap"], style.gap);
|
|
424
|
+
const align = keyword(ALIGN, style.align);
|
|
425
|
+
if (align) d["align-items"] = align;
|
|
426
|
+
const justify = keyword(JUSTIFY, style.justify);
|
|
427
|
+
if (justify) d["justify-content"] = justify;
|
|
322
428
|
return d;
|
|
323
429
|
}
|
|
324
|
-
function
|
|
430
|
+
function shownDisplay(node) {
|
|
431
|
+
if (node.layout === "stack") return "flex";
|
|
432
|
+
if (node.layout === "grid") return "grid";
|
|
433
|
+
return "block";
|
|
434
|
+
}
|
|
435
|
+
function gridColumns(style) {
|
|
436
|
+
const min = lengthToCss(style.columnMin);
|
|
437
|
+
if (min !== null) return `repeat(auto-fit, minmax(min(${min}, 100%), 1fr))`;
|
|
438
|
+
if (typeof style.columns === "number" && Number.isFinite(style.columns)) return `repeat(${Math.max(1, Math.trunc(style.columns))}, 1fr)`;
|
|
439
|
+
return void 0;
|
|
440
|
+
}
|
|
441
|
+
function containerDecls(node, style, isBase) {
|
|
325
442
|
const d = {};
|
|
326
443
|
if (node.layout === "stack") {
|
|
327
|
-
d["display"] = "flex";
|
|
328
|
-
|
|
444
|
+
if (isBase) d["display"] = "flex";
|
|
445
|
+
const direction = inSet(DIRECTIONS, style.direction);
|
|
446
|
+
if (isBase || direction) d["flex-direction"] = direction ?? "column";
|
|
447
|
+
if (style.wrap !== void 0) d["flex-wrap"] = style.wrap ? "wrap" : "nowrap";
|
|
329
448
|
} else if (node.layout === "grid") {
|
|
330
|
-
d["display"] = "grid";
|
|
331
|
-
|
|
332
|
-
|
|
449
|
+
if (isBase) d["display"] = "grid";
|
|
450
|
+
const cols = gridColumns(style);
|
|
451
|
+
if (isBase || cols !== void 0) d["grid-template-columns"] = cols ?? "repeat(1, 1fr)";
|
|
452
|
+
} else if (node.layout === "free" && isBase) {
|
|
333
453
|
d["position"] = "relative";
|
|
334
454
|
}
|
|
335
455
|
return d;
|
|
336
456
|
}
|
|
457
|
+
function visibilityDecls(node, style, isBase) {
|
|
458
|
+
if (style.hidden === true) return { display: "none" };
|
|
459
|
+
if (style.hidden === false && !isBase) return { display: shownDisplay(node) };
|
|
460
|
+
return {};
|
|
461
|
+
}
|
|
462
|
+
function themeDecls(theme) {
|
|
463
|
+
const d = {};
|
|
464
|
+
const walk = (value, path) => {
|
|
465
|
+
if (typeof value === "string" || typeof value === "number" && Number.isFinite(value)) {
|
|
466
|
+
const key = path.map((p) => p.toLowerCase().replace(/[^a-z0-9-]/g, "")).filter(Boolean).join("-");
|
|
467
|
+
if (key) put(d, `--cms-${key}`, String(value));
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
471
|
+
for (const [k, v] of Object.entries(value)) walk(v, [...path, k]);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
if (typeof theme === "object" && theme !== null && !Array.isArray(theme)) {
|
|
475
|
+
for (const [k, v] of Object.entries(theme)) {
|
|
476
|
+
if (k !== "name") walk(v, [k]);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return d;
|
|
480
|
+
}
|
|
337
481
|
function constraintDecls(c, parentFree) {
|
|
338
482
|
const d = {};
|
|
339
483
|
const transforms = [];
|
|
340
484
|
if (parentFree) {
|
|
341
485
|
d["position"] = "absolute";
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
486
|
+
putLen(d, ["left"], c.left);
|
|
487
|
+
putLen(d, ["right"], c.right);
|
|
488
|
+
putLen(d, ["top"], c.top);
|
|
489
|
+
putLen(d, ["bottom"], c.bottom);
|
|
346
490
|
if (c.centerX) {
|
|
347
491
|
d["left"] = "50%";
|
|
348
492
|
transforms.push("translateX(-50%)");
|
|
@@ -353,13 +497,15 @@ function constraintDecls(c, parentFree) {
|
|
|
353
497
|
}
|
|
354
498
|
if (transforms.length) d["transform"] = transforms.join(" ");
|
|
355
499
|
}
|
|
356
|
-
|
|
357
|
-
if (
|
|
500
|
+
const width = sizeToCss(c.width);
|
|
501
|
+
if (width !== null) d["width"] = width;
|
|
502
|
+
const height = sizeToCss(c.height);
|
|
503
|
+
if (height !== null) d["height"] = height;
|
|
358
504
|
return d;
|
|
359
505
|
}
|
|
360
506
|
function isParentFree(doc, node) {
|
|
361
507
|
if (node.parent === null) return false;
|
|
362
|
-
const parent = doc.nodes
|
|
508
|
+
const parent = own(doc.nodes, node.parent);
|
|
363
509
|
return !parent || parent.layout === void 0 || parent.layout === "free";
|
|
364
510
|
}
|
|
365
511
|
function serializeRule(selector, decls, indent) {
|
|
@@ -371,16 +517,20 @@ ${body2}
|
|
|
371
517
|
${indent}}`;
|
|
372
518
|
}
|
|
373
519
|
function emitDocCss(doc) {
|
|
520
|
+
if (!hasNodeMap(doc)) return "";
|
|
374
521
|
const breakpointPx = DEFAULT_BREAKPOINTS;
|
|
375
522
|
const blocks = [];
|
|
523
|
+
const themeRule = serializeRule(":root", themeDecls(doc.theme), "");
|
|
524
|
+
if (themeRule) blocks.push(themeRule);
|
|
376
525
|
for (const id of Object.keys(doc.nodes).sort()) {
|
|
377
526
|
const node = doc.nodes[id];
|
|
378
527
|
const selector = selectorFor(id);
|
|
379
528
|
const parentFree = isParentFree(doc, node);
|
|
380
529
|
const base = {
|
|
381
|
-
...
|
|
530
|
+
...containerDecls(node, node.style.base, true),
|
|
382
531
|
...node.constraints ? constraintDecls(node.constraints.base, parentFree) : {},
|
|
383
|
-
...styleDecls(node.style.base)
|
|
532
|
+
...styleDecls(node.style.base),
|
|
533
|
+
...visibilityDecls(node, node.style.base, true)
|
|
384
534
|
};
|
|
385
535
|
const baseRule = serializeRule(selector, base, "");
|
|
386
536
|
if (baseRule) blocks.push(baseRule);
|
|
@@ -388,9 +538,12 @@ function emitDocCss(doc) {
|
|
|
388
538
|
if (bp === "base") continue;
|
|
389
539
|
const px = breakpointPx[bp];
|
|
390
540
|
if (px === void 0) continue;
|
|
541
|
+
const bpStyle = node.style[bp];
|
|
391
542
|
const decls = {
|
|
543
|
+
...bpStyle ? containerDecls(node, bpStyle, false) : {},
|
|
392
544
|
...node.constraints?.[bp] ? constraintDecls(node.constraints[bp], parentFree) : {},
|
|
393
|
-
...
|
|
545
|
+
...bpStyle ? styleDecls(bpStyle) : {},
|
|
546
|
+
...bpStyle ? visibilityDecls(node, bpStyle, false) : {}
|
|
394
547
|
};
|
|
395
548
|
const inner = serializeRule(selector, decls, " ");
|
|
396
549
|
if (inner) blocks.push(`@media (min-width: ${px}px) {
|
|
@@ -408,6 +561,48 @@ function resolveValue(v, data) {
|
|
|
408
561
|
if (isBinding(v)) return getPath(data, v.$bind);
|
|
409
562
|
return v;
|
|
410
563
|
}
|
|
564
|
+
var LANDMARK_TAGS = ["section", "header", "nav", "main", "footer", "article", "aside", "div"];
|
|
565
|
+
var LANDMARKS = new Set(LANDMARK_TAGS);
|
|
566
|
+
var CONTAINERS = /* @__PURE__ */ new Set(["stack", "grid", "frame", "shape", "card"]);
|
|
567
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
568
|
+
function safeHref(raw) {
|
|
569
|
+
const href = raw.trim();
|
|
570
|
+
if (!href) return null;
|
|
571
|
+
const probe = href.replace(/[\u0000- \u007f]+/g, "");
|
|
572
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(probe);
|
|
573
|
+
if (scheme && !SAFE_SCHEMES.has(scheme[1].toLowerCase())) return null;
|
|
574
|
+
return href;
|
|
575
|
+
}
|
|
576
|
+
var ANCHOR_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
|
|
577
|
+
function anchorFor(node, text) {
|
|
578
|
+
const anchor = text(node.props.anchor);
|
|
579
|
+
return ANCHOR_PATTERN.test(anchor) ? anchor : null;
|
|
580
|
+
}
|
|
581
|
+
function elementFor(node, text) {
|
|
582
|
+
const as = text(node.props.as);
|
|
583
|
+
switch (node.type) {
|
|
584
|
+
case "heading": {
|
|
585
|
+
const level = Number(text(node.props.level) || 2);
|
|
586
|
+
return { tag: Number.isInteger(level) && level >= 1 && level <= 6 ? `h${level}` : "h2" };
|
|
587
|
+
}
|
|
588
|
+
case "text":
|
|
589
|
+
return { tag: "p" };
|
|
590
|
+
case "section":
|
|
591
|
+
return { tag: LANDMARKS.has(as) ? as : "section" };
|
|
592
|
+
case "list":
|
|
593
|
+
return { tag: text(node.props.ordered) === "true" ? "ol" : "ul" };
|
|
594
|
+
case "button":
|
|
595
|
+
case "link": {
|
|
596
|
+
const href = safeHref(text(node.props.href));
|
|
597
|
+
if (href === null) return { tag: node.type === "link" ? "span" : "div" };
|
|
598
|
+
const attrs = { href };
|
|
599
|
+
if (/^https?:\/\//i.test(href)) attrs.rel = "noopener noreferrer";
|
|
600
|
+
return { tag: "a", attrs };
|
|
601
|
+
}
|
|
602
|
+
default:
|
|
603
|
+
return { tag: CONTAINERS.has(node.type) && LANDMARKS.has(as) ? as : "div" };
|
|
604
|
+
}
|
|
605
|
+
}
|
|
411
606
|
var defaultRegistry = {
|
|
412
607
|
section: ({ children }) => children,
|
|
413
608
|
stack: ({ children }) => children,
|
|
@@ -420,7 +615,12 @@ var defaultRegistry = {
|
|
|
420
615
|
// Rich text — inline-formatted HTML (gradient spans, links, <code>, …) that
|
|
421
616
|
// plain `text` can't express. The author owns the markup (it's their content).
|
|
422
617
|
richtext: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: text(node.props.html) } }),
|
|
423
|
-
|
|
618
|
+
// The [data-cms] element is already the <a> (see elementFor), so a link renders its label only.
|
|
619
|
+
link: ({ node, text }) => text(node.props.content) || "link",
|
|
620
|
+
list: ({ node, resolve }) => {
|
|
621
|
+
const items = resolve(node.props.items);
|
|
622
|
+
return Array.isArray(items) ? items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item === null || item === void 0 ? "" : String(item) }, i)) : null;
|
|
623
|
+
},
|
|
424
624
|
divider: () => /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { border: "none", borderTop: "1px solid #e2e8f0", margin: 0 } }),
|
|
425
625
|
callout: ({ node, text }) => {
|
|
426
626
|
const tone = { info: "#3b82f6", success: "#10b981", warning: "#f59e0b", danger: "#ef4444" }[text(node.props.variant)] ?? "#3b82f6";
|
|
@@ -432,15 +632,20 @@ var defaultRegistry = {
|
|
|
432
632
|
const variant = text(node.props.variant) || "primary";
|
|
433
633
|
return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cms-button": variant, style: buttonStyle(variant), children: label2 });
|
|
434
634
|
},
|
|
435
|
-
image: ({ node, text }) =>
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
635
|
+
image: ({ node, text }) => {
|
|
636
|
+
const fit = text(node.props.fit);
|
|
637
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
638
|
+
"img",
|
|
639
|
+
{
|
|
640
|
+
src: text(node.props.src),
|
|
641
|
+
alt: text(node.props.alt),
|
|
642
|
+
loading: text(node.props.loading) === "eager" ? "eager" : "lazy",
|
|
643
|
+
style: { width: "100%", height: "100%", objectFit: IMAGE_FITS.has(fit) ? fit : "cover", display: "block" }
|
|
644
|
+
}
|
|
645
|
+
);
|
|
646
|
+
}
|
|
443
647
|
};
|
|
648
|
+
var IMAGE_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "none", "scale-down"]);
|
|
444
649
|
function buttonStyle(variant) {
|
|
445
650
|
const base = {
|
|
446
651
|
display: "inline-flex",
|
|
@@ -459,7 +664,7 @@ function buttonStyle(variant) {
|
|
|
459
664
|
return { ...base, background: "#7c3aed", color: "#fff", border: "1px solid #7c3aed" };
|
|
460
665
|
}
|
|
461
666
|
function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
462
|
-
const node = doc.nodes
|
|
667
|
+
const node = own(doc.nodes, id);
|
|
463
668
|
if (!node) return null;
|
|
464
669
|
if (node.type === "repeater") {
|
|
465
670
|
const arr = resolveValue(node.props.items, data);
|
|
@@ -475,7 +680,19 @@ function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
|
475
680
|
};
|
|
476
681
|
const renderer = registry[node.type];
|
|
477
682
|
const inner = renderer ? renderer({ node, doc, children: kids, data, resolve, text }) : node.island ? null : kids;
|
|
478
|
-
|
|
683
|
+
const { tag, attrs } = elementFor(node, text);
|
|
684
|
+
const anchor = anchorFor(node, text);
|
|
685
|
+
return react.createElement(
|
|
686
|
+
tag,
|
|
687
|
+
{
|
|
688
|
+
"data-cms": node.id,
|
|
689
|
+
"data-cms-island": node.island ? node.type : void 0,
|
|
690
|
+
className: node.className,
|
|
691
|
+
...anchor ? { id: anchor } : {},
|
|
692
|
+
...attrs
|
|
693
|
+
},
|
|
694
|
+
inner
|
|
695
|
+
);
|
|
479
696
|
}
|
|
480
697
|
function CmsPage({
|
|
481
698
|
doc: input3,
|
|
@@ -793,7 +1010,8 @@ var ADD_DEFAULTS = {
|
|
|
793
1010
|
divider: { type: "divider", props: {}, style: {} },
|
|
794
1011
|
code: { type: "code", props: { content: "npm install @particle-academy/react-fancy", lang: "bash" }, style: {} },
|
|
795
1012
|
richtext: { type: "richtext", props: { html: "<p>Rich <strong>text</strong> with <em>inline</em> formatting.</p>" }, style: {} },
|
|
796
|
-
repeater: { type: "repeater", props: { items: "" }, style: { gap: { value: 12, unit: "px" } }, layout: "stack" }
|
|
1013
|
+
repeater: { type: "repeater", props: { items: "" }, style: { gap: { value: 12, unit: "px" } }, layout: "stack" },
|
|
1014
|
+
list: { type: "list", props: { items: ["First point", "Second point"], ordered: false }, style: {} }
|
|
797
1015
|
};
|
|
798
1016
|
var ADD_MENU = [
|
|
799
1017
|
{ kind: "heading", label: "Heading" },
|
|
@@ -809,6 +1027,7 @@ var ADD_MENU = [
|
|
|
809
1027
|
{ kind: "divider", label: "Divider" },
|
|
810
1028
|
{ kind: "code", label: "Code" },
|
|
811
1029
|
{ kind: "richtext", label: "Rich text" },
|
|
1030
|
+
{ kind: "list", label: "List" },
|
|
812
1031
|
{ kind: "repeater", label: "Repeater" }
|
|
813
1032
|
];
|
|
814
1033
|
function resolveInsertParent(doc, selectedId) {
|