@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/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { B as Binding, P as PageDoc, m as NodeId, N as Node, J as Json, q as StyleProps, L as LayoutMode } from './registry-
|
|
2
|
-
export { A as Action, a as ActionEvent, b as ActionHandler, c as Anim, d as AnimDriver, e as Bound, f as Breakpoint, C as Constraints, D as DEFAULT_BREAKPOINTS, g as DataContext, E as ElementContext, h as ElementRegistry, i as ElementRenderer, K as Keyframe, j as LegacyPageDoc, k as Length, l as LengthUnit, M as MigrateResult, n as PageMeta, o as PerBreakpoint, S as ScrollMode, p as SizeMode, T as ThemeTokens, r as defaultRegistry, s as emptyDoc, t as getPath, u as hasNodeMap, v as isBinding, w as migrateDoc, x as migrateDocVerbose, y as needsMigration, z as resolveValue } from './registry-
|
|
3
|
-
import { P as PageOp } from './useEditor-
|
|
4
|
-
export { A as Actor, C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, O as OpEnvelope, h as PageOpType, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-
|
|
1
|
+
import { B as Binding, P as PageDoc, m as NodeId, N as Node, J as Json, q as StyleProps, L as LayoutMode } from './registry-F2TAMjhx.cjs';
|
|
2
|
+
export { A as Action, a as ActionEvent, b as ActionHandler, c as Anim, d as AnimDriver, e as Bound, f as Breakpoint, C as Constraints, D as DEFAULT_BREAKPOINTS, g as DataContext, E as ElementContext, h as ElementRegistry, i as ElementRenderer, K as Keyframe, j as LegacyPageDoc, k as Length, l as LengthUnit, M as MigrateResult, n as PageMeta, o as PerBreakpoint, S as ScrollMode, p as SizeMode, T as ThemeTokens, r as defaultRegistry, s as emptyDoc, t as getPath, u as hasNodeMap, v as isBinding, w as migrateDoc, x as migrateDocVerbose, y as needsMigration, z as resolveValue, F as withNodeDefaults } from './registry-F2TAMjhx.cjs';
|
|
3
|
+
import { P as PageOp } from './useEditor-B2kVZJA0.cjs';
|
|
4
|
+
export { A as Actor, C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, O as OpEnvelope, h as PageOpType, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-B2kVZJA0.cjs';
|
|
5
5
|
export { fractionalKey, fractionalKey as keyBetween } from '@particle-academy/fancy-doc-commons';
|
|
6
6
|
export { CmsPage, CmsPageProps, CmsRegion, CmsRegionProps, RenderNode, RenderNodeProps } from './react.cjs';
|
|
7
7
|
import 'react';
|
|
@@ -120,7 +120,7 @@ declare function emitDocCss(doc: PageDoc): string;
|
|
|
120
120
|
*/
|
|
121
121
|
/** Node types that accept children, so an insert lands *inside* them. */
|
|
122
122
|
declare const CONTAINER_TYPES: Set<string>;
|
|
123
|
-
type AddKind = "text" | "heading" | "button" | "link" | "image" | "box" | "stack" | "grid" | "card" | "callout" | "divider" | "code" | "richtext" | "repeater";
|
|
123
|
+
type AddKind = "text" | "heading" | "button" | "link" | "image" | "box" | "stack" | "grid" | "card" | "callout" | "divider" | "code" | "richtext" | "repeater" | "list";
|
|
124
124
|
declare const ADD_DEFAULTS: Record<AddKind, {
|
|
125
125
|
type: string;
|
|
126
126
|
props: Record<string, Json>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { B as Binding, P as PageDoc, m as NodeId, N as Node, J as Json, q as StyleProps, L as LayoutMode } from './registry-
|
|
2
|
-
export { A as Action, a as ActionEvent, b as ActionHandler, c as Anim, d as AnimDriver, e as Bound, f as Breakpoint, C as Constraints, D as DEFAULT_BREAKPOINTS, g as DataContext, E as ElementContext, h as ElementRegistry, i as ElementRenderer, K as Keyframe, j as LegacyPageDoc, k as Length, l as LengthUnit, M as MigrateResult, n as PageMeta, o as PerBreakpoint, S as ScrollMode, p as SizeMode, T as ThemeTokens, r as defaultRegistry, s as emptyDoc, t as getPath, u as hasNodeMap, v as isBinding, w as migrateDoc, x as migrateDocVerbose, y as needsMigration, z as resolveValue } from './registry-
|
|
3
|
-
import { P as PageOp } from './useEditor-
|
|
4
|
-
export { A as Actor, C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, O as OpEnvelope, h as PageOpType, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-
|
|
1
|
+
import { B as Binding, P as PageDoc, m as NodeId, N as Node, J as Json, q as StyleProps, L as LayoutMode } from './registry-F2TAMjhx.js';
|
|
2
|
+
export { A as Action, a as ActionEvent, b as ActionHandler, c as Anim, d as AnimDriver, e as Bound, f as Breakpoint, C as Constraints, D as DEFAULT_BREAKPOINTS, g as DataContext, E as ElementContext, h as ElementRegistry, i as ElementRenderer, K as Keyframe, j as LegacyPageDoc, k as Length, l as LengthUnit, M as MigrateResult, n as PageMeta, o as PerBreakpoint, S as ScrollMode, p as SizeMode, T as ThemeTokens, r as defaultRegistry, s as emptyDoc, t as getPath, u as hasNodeMap, v as isBinding, w as migrateDoc, x as migrateDocVerbose, y as needsMigration, z as resolveValue, F as withNodeDefaults } from './registry-F2TAMjhx.js';
|
|
3
|
+
import { P as PageOp } from './useEditor-BQSictH3.js';
|
|
4
|
+
export { A as Actor, C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, O as OpEnvelope, h as PageOpType, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-BQSictH3.js';
|
|
5
5
|
export { fractionalKey, fractionalKey as keyBetween } from '@particle-academy/fancy-doc-commons';
|
|
6
6
|
export { CmsPage, CmsPageProps, CmsRegion, CmsRegionProps, RenderNode, RenderNodeProps } from './react.js';
|
|
7
7
|
import 'react';
|
|
@@ -120,7 +120,7 @@ declare function emitDocCss(doc: PageDoc): string;
|
|
|
120
120
|
*/
|
|
121
121
|
/** Node types that accept children, so an insert lands *inside* them. */
|
|
122
122
|
declare const CONTAINER_TYPES: Set<string>;
|
|
123
|
-
type AddKind = "text" | "heading" | "button" | "link" | "image" | "box" | "stack" | "grid" | "card" | "callout" | "divider" | "code" | "richtext" | "repeater";
|
|
123
|
+
type AddKind = "text" | "heading" | "button" | "link" | "image" | "box" | "stack" | "grid" | "card" | "callout" | "divider" | "code" | "richtext" | "repeater" | "list";
|
|
124
124
|
declare const ADD_DEFAULTS: Record<AddKind, {
|
|
125
125
|
type: string;
|
|
126
126
|
props: Record<string, Json>;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { CmsRegion } from './chunk-
|
|
2
|
-
export { ADD_DEFAULTS, ADD_MENU, CONTAINER_TYPES, Canvas, Editor, Inspector, LayersPanel, buildInsertOp, editorReduce, initEditor, resolveInsertParent, useEditor } from './chunk-
|
|
3
|
-
import { isBinding } from './chunk-
|
|
4
|
-
export { CmsPage, DEFAULT_BREAKPOINTS, RenderNode, childrenOf, defaultRegistry, descendantIds, emitDocCss, emptyDoc, getPath, hasNodeMap, invert, isBinding, lastRootId, migrateDoc, migrateDocVerbose, needsMigration, reduce, reduceAll, resolveValue, rootIds } from './chunk-
|
|
1
|
+
export { CmsRegion } from './chunk-TJL26EAX.js';
|
|
2
|
+
export { ADD_DEFAULTS, ADD_MENU, CONTAINER_TYPES, Canvas, Editor, Inspector, LayersPanel, buildInsertOp, editorReduce, initEditor, resolveInsertParent, useEditor } from './chunk-BVMSHUQF.js';
|
|
3
|
+
import { isBinding } from './chunk-IRG5IKKO.js';
|
|
4
|
+
export { CmsPage, DEFAULT_BREAKPOINTS, RenderNode, childrenOf, defaultRegistry, descendantIds, emitDocCss, emptyDoc, getPath, hasNodeMap, invert, isBinding, lastRootId, migrateDoc, migrateDocVerbose, needsMigration, reduce, reduceAll, resolveValue, rootIds, withNodeDefaults } from './chunk-IRG5IKKO.js';
|
|
5
5
|
export { fractionalKey, fractionalKey as keyBetween } from '@particle-academy/fancy-doc-commons';
|
|
6
6
|
|
|
7
7
|
// src/document/bindings.ts
|
package/dist/react.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var react = require('react');
|
|
3
4
|
var fancyDocCommons = require('@particle-academy/fancy-doc-commons');
|
|
4
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
-
var react = require('react');
|
|
6
6
|
|
|
7
|
-
// src/
|
|
7
|
+
// src/react/RenderNode.tsx
|
|
8
8
|
|
|
9
9
|
// src/document/types.ts
|
|
10
10
|
function isBinding(v) {
|
|
@@ -15,6 +15,12 @@ function hasNodeMap(value) {
|
|
|
15
15
|
const nodes = value.nodes;
|
|
16
16
|
return typeof nodes === "object" && nodes !== null && !Array.isArray(nodes);
|
|
17
17
|
}
|
|
18
|
+
function withNodeDefaults(node) {
|
|
19
|
+
const props = typeof node.props === "object" && node.props !== null ? node.props : {};
|
|
20
|
+
const style = typeof node.style === "object" && node.style !== null ? node.style : { base: {} };
|
|
21
|
+
const base = typeof style.base === "object" && style.base !== null ? style.base : {};
|
|
22
|
+
return props === node.props && style === node.style && base === style.base ? node : { ...node, props, style: { ...style, base } };
|
|
23
|
+
}
|
|
18
24
|
var DEFAULT_BREAKPOINTS = {
|
|
19
25
|
base: 0,
|
|
20
26
|
md: 768,
|
|
@@ -31,6 +37,15 @@ function emptyDoc(id, slug = "/") {
|
|
|
31
37
|
};
|
|
32
38
|
}
|
|
33
39
|
|
|
40
|
+
// src/document/own.ts
|
|
41
|
+
var has = Object.prototype.hasOwnProperty;
|
|
42
|
+
function own(map, key) {
|
|
43
|
+
return map != null && has.call(map, key) ? map[key] : void 0;
|
|
44
|
+
}
|
|
45
|
+
function setOwn(map, key, value) {
|
|
46
|
+
Object.defineProperty(map, key, { value, writable: true, enumerable: true, configurable: true });
|
|
47
|
+
}
|
|
48
|
+
|
|
34
49
|
// src/document/reduce.ts
|
|
35
50
|
function childrenOf(doc, parent) {
|
|
36
51
|
return fancyDocCommons.childrenOf(doc, parent);
|
|
@@ -47,6 +62,55 @@ function resolveValue(v, data) {
|
|
|
47
62
|
if (isBinding(v)) return getPath(data, v.$bind);
|
|
48
63
|
return v;
|
|
49
64
|
}
|
|
65
|
+
var LANDMARK_TAGS = ["section", "header", "nav", "main", "footer", "article", "aside", "div"];
|
|
66
|
+
var LANDMARKS = new Set(LANDMARK_TAGS);
|
|
67
|
+
var CONTAINERS = /* @__PURE__ */ new Set(["stack", "grid", "frame", "shape", "card"]);
|
|
68
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
69
|
+
function safeHref(raw) {
|
|
70
|
+
const href = raw.trim();
|
|
71
|
+
if (!href) return null;
|
|
72
|
+
const probe = href.replace(/[\u0000- \u007f]+/g, "");
|
|
73
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(probe);
|
|
74
|
+
if (scheme && !SAFE_SCHEMES.has(scheme[1].toLowerCase())) return null;
|
|
75
|
+
return href;
|
|
76
|
+
}
|
|
77
|
+
var ANCHOR_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
|
|
78
|
+
function anchorFor(node, text) {
|
|
79
|
+
const anchor = text(node.props.anchor);
|
|
80
|
+
return ANCHOR_PATTERN.test(anchor) ? anchor : null;
|
|
81
|
+
}
|
|
82
|
+
function elementFor(node, text) {
|
|
83
|
+
const as = text(node.props.as);
|
|
84
|
+
switch (node.type) {
|
|
85
|
+
case "heading": {
|
|
86
|
+
const level = Number(text(node.props.level) || 2);
|
|
87
|
+
return { tag: Number.isInteger(level) && level >= 1 && level <= 6 ? `h${level}` : "h2" };
|
|
88
|
+
}
|
|
89
|
+
case "text":
|
|
90
|
+
return { tag: "p" };
|
|
91
|
+
case "section":
|
|
92
|
+
return { tag: LANDMARKS.has(as) ? as : "section" };
|
|
93
|
+
case "list":
|
|
94
|
+
return { tag: text(node.props.ordered) === "true" ? "ol" : "ul" };
|
|
95
|
+
case "button":
|
|
96
|
+
case "link": {
|
|
97
|
+
const href = safeHref(text(node.props.href));
|
|
98
|
+
const attrs = {};
|
|
99
|
+
if (href !== null) {
|
|
100
|
+
attrs.href = href;
|
|
101
|
+
if (/^https?:\/\//i.test(href)) attrs.rel = "noopener noreferrer";
|
|
102
|
+
}
|
|
103
|
+
if (node.type === "button") attrs["data-cms-button"] = BUTTON_VARIANTS.has(text(node.props.variant)) ? text(node.props.variant) : "primary";
|
|
104
|
+
return { tag: href !== null ? "a" : node.type === "link" ? "span" : "div", attrs };
|
|
105
|
+
}
|
|
106
|
+
case "callout": {
|
|
107
|
+
const variant = text(node.props.variant);
|
|
108
|
+
return { tag: "div", attrs: { "data-cms-callout": CALLOUT_VARIANTS.has(variant) ? variant : "info" } };
|
|
109
|
+
}
|
|
110
|
+
default:
|
|
111
|
+
return { tag: CONTAINERS.has(node.type) && LANDMARKS.has(as) ? as : "div" };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
50
114
|
var defaultRegistry = {
|
|
51
115
|
section: ({ children }) => children,
|
|
52
116
|
stack: ({ children }) => children,
|
|
@@ -59,47 +123,38 @@ var defaultRegistry = {
|
|
|
59
123
|
// Rich text — inline-formatted HTML (gradient spans, links, <code>, …) that
|
|
60
124
|
// plain `text` can't express. The author owns the markup (it's their content).
|
|
61
125
|
richtext: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: text(node.props.html) } }),
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
return
|
|
126
|
+
// The [data-cms] element is already the <a> (see elementFor), so a link renders its label only.
|
|
127
|
+
link: ({ node, text }) => text(node.props.content) || "link",
|
|
128
|
+
list: ({ node, resolve }) => {
|
|
129
|
+
const items = resolve(node.props.items);
|
|
130
|
+
return Array.isArray(items) ? items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item === null || item === void 0 ? "" : String(item) }, i)) : null;
|
|
67
131
|
},
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
},
|
|
74
|
-
image: ({ node, text }) =>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
132
|
+
// Looks come from the base stylesheet (render/css.ts BASE_CSS), never from
|
|
133
|
+
// inline style objects, so the PHP renderer emits the same markup.
|
|
134
|
+
divider: () => /* @__PURE__ */ jsxRuntime.jsx("hr", {}),
|
|
135
|
+
callout: ({ node, text }) => text(node.props.content) || "Callout",
|
|
136
|
+
code: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx("pre", { children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: text(node.props.content) }) }),
|
|
137
|
+
button: ({ node, text }) => text(node.props.label) || text(node.props.content) || "Button",
|
|
138
|
+
image: ({ node, text }) => {
|
|
139
|
+
const fit = text(node.props.fit);
|
|
140
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
141
|
+
"img",
|
|
142
|
+
{
|
|
143
|
+
src: text(node.props.src) || void 0,
|
|
144
|
+
alt: text(node.props.alt),
|
|
145
|
+
loading: text(node.props.loading) === "eager" ? "eager" : "lazy",
|
|
146
|
+
"data-cms-fit": IMAGE_FITS.has(fit) && fit !== "cover" ? fit : void 0
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
}
|
|
82
150
|
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
alignItems: "center",
|
|
87
|
-
gap: 6,
|
|
88
|
-
padding: "9px 16px",
|
|
89
|
-
borderRadius: 8,
|
|
90
|
-
fontWeight: 600,
|
|
91
|
-
fontSize: 14,
|
|
92
|
-
lineHeight: 1,
|
|
93
|
-
cursor: "pointer",
|
|
94
|
-
userSelect: "none"
|
|
95
|
-
};
|
|
96
|
-
if (variant === "ghost") return { ...base, background: "transparent", color: "inherit", border: "1px solid currentColor" };
|
|
97
|
-
if (variant === "outline") return { ...base, background: "transparent", color: "#7c3aed", border: "1px solid #7c3aed" };
|
|
98
|
-
return { ...base, background: "#7c3aed", color: "#fff", border: "1px solid #7c3aed" };
|
|
99
|
-
}
|
|
151
|
+
var IMAGE_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "none", "scale-down"]);
|
|
152
|
+
var BUTTON_VARIANTS = /* @__PURE__ */ new Set(["primary", "outline", "ghost"]);
|
|
153
|
+
var CALLOUT_VARIANTS = /* @__PURE__ */ new Set(["info", "success", "warning", "danger"]);
|
|
100
154
|
function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
101
|
-
const
|
|
102
|
-
if (!
|
|
155
|
+
const stored = own(doc.nodes, id);
|
|
156
|
+
if (!stored) return null;
|
|
157
|
+
const node = withNodeDefaults(stored);
|
|
103
158
|
if (node.type === "repeater") {
|
|
104
159
|
const arr = resolveValue(node.props.items, data);
|
|
105
160
|
const items = Array.isArray(arr) ? arr : [];
|
|
@@ -114,7 +169,19 @@ function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
|
114
169
|
};
|
|
115
170
|
const renderer = registry[node.type];
|
|
116
171
|
const inner = renderer ? renderer({ node, doc, children: kids, data, resolve, text }) : node.island ? null : kids;
|
|
117
|
-
|
|
172
|
+
const { tag, attrs } = elementFor(node, text);
|
|
173
|
+
const anchor = anchorFor(node, text);
|
|
174
|
+
return react.createElement(
|
|
175
|
+
tag,
|
|
176
|
+
{
|
|
177
|
+
"data-cms": node.id,
|
|
178
|
+
"data-cms-island": node.island ? node.type : void 0,
|
|
179
|
+
className: node.className,
|
|
180
|
+
...anchor ? { id: anchor } : {},
|
|
181
|
+
...attrs
|
|
182
|
+
},
|
|
183
|
+
inner
|
|
184
|
+
);
|
|
118
185
|
}
|
|
119
186
|
function normalizeDoc(input) {
|
|
120
187
|
if (hasNodeMap(input)) return input;
|
|
@@ -132,7 +199,7 @@ function migrateDocVerbose(input) {
|
|
|
132
199
|
const ordered = [];
|
|
133
200
|
const seen = /* @__PURE__ */ new Set();
|
|
134
201
|
for (const id of sections) {
|
|
135
|
-
if (!normalized.nodes
|
|
202
|
+
if (!own(normalized.nodes, id)) {
|
|
136
203
|
danglingIds.push(id);
|
|
137
204
|
continue;
|
|
138
205
|
}
|
|
@@ -146,7 +213,7 @@ function migrateDocVerbose(input) {
|
|
|
146
213
|
let prev = null;
|
|
147
214
|
for (const id of finalOrder) {
|
|
148
215
|
const key = fancyDocCommons.fractionalKey(prev, null);
|
|
149
|
-
nodes
|
|
216
|
+
setOwn(nodes, id, { ...own(nodes, id), order: key });
|
|
150
217
|
prev = key;
|
|
151
218
|
}
|
|
152
219
|
const { sections: _dropped, ...rest } = legacy;
|
|
@@ -162,15 +229,58 @@ function migrateDoc(input) {
|
|
|
162
229
|
}
|
|
163
230
|
|
|
164
231
|
// src/render/css.ts
|
|
165
|
-
|
|
232
|
+
function cssString(value) {
|
|
233
|
+
let out = "";
|
|
234
|
+
for (const ch of value) {
|
|
235
|
+
const code = ch.codePointAt(0);
|
|
236
|
+
if (ch === "\\" || ch === '"') out += `\\${ch}`;
|
|
237
|
+
else if (code < 32 || code === 127 || ch === "<") out += `\\${code.toString(16)} `;
|
|
238
|
+
else out += ch;
|
|
239
|
+
}
|
|
240
|
+
return out;
|
|
241
|
+
}
|
|
242
|
+
var selectorFor = (id) => `[data-cms="${cssString(id)}"]`;
|
|
243
|
+
var BASE_CSS = [
|
|
244
|
+
"/* fancy-cms base */",
|
|
245
|
+
":where([data-cms]) {\n box-sizing: border-box;\n}",
|
|
246
|
+
":where(h1, h2, h3, h4, h5, h6, p, ul, ol)[data-cms] {\n margin: 0;\n}",
|
|
247
|
+
":where(ul, ol)[data-cms] {\n padding-left: 1.25em;\n}",
|
|
248
|
+
":where(a[data-cms]) {\n color: inherit;\n}",
|
|
249
|
+
":where([data-cms] > img) {\n display: block;\n height: 100%;\n object-fit: cover;\n width: 100%;\n}",
|
|
250
|
+
':where([data-cms] > img[data-cms-fit="contain"]) {\n object-fit: contain;\n}',
|
|
251
|
+
':where([data-cms] > img[data-cms-fit="fill"]) {\n object-fit: fill;\n}',
|
|
252
|
+
':where([data-cms] > img[data-cms-fit="none"]) {\n object-fit: none;\n}',
|
|
253
|
+
':where([data-cms] > img[data-cms-fit="scale-down"]) {\n object-fit: scale-down;\n}',
|
|
254
|
+
":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}",
|
|
255
|
+
':where([data-cms-button="primary"]) {\n background: var(--cms-color-primary, #7c3aed);\n color: #ffffff;\n}',
|
|
256
|
+
':where([data-cms-button="outline"]) {\n background: transparent;\n color: var(--cms-color-primary, #7c3aed);\n}',
|
|
257
|
+
':where([data-cms-button="ghost"]) {\n background: transparent;\n border-color: currentColor;\n color: inherit;\n}',
|
|
258
|
+
":where([data-cms-callout]) {\n background: #3b82f614;\n border-left: 3px solid #3b82f6;\n border-radius: 10px;\n padding: 12px 14px;\n}",
|
|
259
|
+
':where([data-cms-callout="success"]) {\n background: #10b98114;\n border-left-color: #10b981;\n}',
|
|
260
|
+
':where([data-cms-callout="warning"]) {\n background: #f59e0b14;\n border-left-color: #f59e0b;\n}',
|
|
261
|
+
':where([data-cms-callout="danger"]) {\n background: #ef444414;\n border-left-color: #ef4444;\n}',
|
|
262
|
+
":where([data-cms] > hr) {\n border: none;\n border-top: 1px solid #e2e8f0;\n margin: 0;\n}",
|
|
263
|
+
":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}"
|
|
264
|
+
].join("\n\n");
|
|
265
|
+
var UNITS = /* @__PURE__ */ new Set(["px", "%", "vw", "vh", "rem", "fr"]);
|
|
166
266
|
function lengthToCss(len) {
|
|
167
|
-
|
|
267
|
+
if (typeof len !== "object" || len === null) return null;
|
|
268
|
+
const { value, unit } = len;
|
|
269
|
+
if (typeof value !== "number" || !Number.isFinite(value) || typeof unit !== "string" || !UNITS.has(unit)) return null;
|
|
270
|
+
return `${value}${unit}`;
|
|
168
271
|
}
|
|
169
272
|
function sizeToCss(size) {
|
|
170
273
|
if (size === "fill") return "100%";
|
|
171
274
|
if (size === "hug") return "auto";
|
|
172
275
|
return lengthToCss(size);
|
|
173
276
|
}
|
|
277
|
+
function numberToCss(value) {
|
|
278
|
+
return typeof value === "number" && Number.isFinite(value) ? String(value) : null;
|
|
279
|
+
}
|
|
280
|
+
function putLen(d, props, len) {
|
|
281
|
+
const css = lengthToCss(len);
|
|
282
|
+
if (css !== null) for (const p of props) d[p] = css;
|
|
283
|
+
}
|
|
174
284
|
var ALIGN = {
|
|
175
285
|
start: "flex-start",
|
|
176
286
|
center: "center",
|
|
@@ -184,51 +294,150 @@ var JUSTIFY = {
|
|
|
184
294
|
between: "space-between",
|
|
185
295
|
around: "space-around"
|
|
186
296
|
};
|
|
297
|
+
function safeCssValue(value) {
|
|
298
|
+
if (/[{}<]/.test(value)) return null;
|
|
299
|
+
let depth = 0;
|
|
300
|
+
let quote = null;
|
|
301
|
+
for (const ch of value) {
|
|
302
|
+
if (quote) {
|
|
303
|
+
if (ch === quote) quote = null;
|
|
304
|
+
} else if (ch === '"' || ch === "'") {
|
|
305
|
+
quote = ch;
|
|
306
|
+
} else if (ch === "(") {
|
|
307
|
+
depth++;
|
|
308
|
+
} else if (ch === ")") {
|
|
309
|
+
depth = Math.max(0, depth - 1);
|
|
310
|
+
} else if (ch === ";" && depth === 0) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return value;
|
|
315
|
+
}
|
|
316
|
+
function put(d, prop, value) {
|
|
317
|
+
if (value === void 0) return;
|
|
318
|
+
const safe = safeCssValue(value);
|
|
319
|
+
if (safe !== null) d[prop] = safe;
|
|
320
|
+
}
|
|
321
|
+
var OVERFLOW = /* @__PURE__ */ new Set(["visible", "hidden", "auto"]);
|
|
322
|
+
var TEXT_TRANSFORM = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
|
|
323
|
+
var BACKGROUND_SIZE = /* @__PURE__ */ new Set(["cover", "contain", "auto"]);
|
|
324
|
+
var TEXT_ALIGN = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
|
|
325
|
+
var DIRECTIONS = /* @__PURE__ */ new Set(["row", "column"]);
|
|
326
|
+
function keyword(map, value) {
|
|
327
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(map, value) ? map[value] : null;
|
|
328
|
+
}
|
|
329
|
+
function inSet(set, value) {
|
|
330
|
+
return typeof value === "string" && set.has(value) ? value : null;
|
|
331
|
+
}
|
|
187
332
|
function styleDecls(style) {
|
|
188
333
|
const d = {};
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (style.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (style.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (style.
|
|
205
|
-
|
|
206
|
-
if (
|
|
207
|
-
|
|
334
|
+
putLen(d, ["padding"], style.padding);
|
|
335
|
+
putLen(d, ["padding-left", "padding-right"], style.paddingX);
|
|
336
|
+
putLen(d, ["padding-top", "padding-bottom"], style.paddingY);
|
|
337
|
+
putLen(d, ["margin"], style.margin);
|
|
338
|
+
if (style.marginX === "auto") d["margin-left"] = d["margin-right"] = "auto";
|
|
339
|
+
else putLen(d, ["margin-left", "margin-right"], style.marginX);
|
|
340
|
+
putLen(d, ["margin-top", "margin-bottom"], style.marginY);
|
|
341
|
+
putLen(d, ["border-radius"], style.radius);
|
|
342
|
+
putLen(d, ["max-width"], style.maxWidth);
|
|
343
|
+
putLen(d, ["min-height"], style.minHeight);
|
|
344
|
+
put(d, "aspect-ratio", typeof style.aspectRatio === "string" ? style.aspectRatio : void 0);
|
|
345
|
+
if (style.sticky === true) {
|
|
346
|
+
d["position"] = "sticky";
|
|
347
|
+
d["top"] = "0";
|
|
348
|
+
}
|
|
349
|
+
if (typeof style.zIndex === "number" && Number.isFinite(style.zIndex)) d["z-index"] = String(Math.trunc(style.zIndex));
|
|
350
|
+
const overflow = inSet(OVERFLOW, style.overflow);
|
|
351
|
+
if (overflow) d["overflow"] = overflow;
|
|
352
|
+
put(d, "background", typeof style.background === "string" ? style.background : void 0);
|
|
353
|
+
const backgroundSize = inSet(BACKGROUND_SIZE, style.backgroundSize);
|
|
354
|
+
if (backgroundSize) d["background-size"] = backgroundSize;
|
|
355
|
+
put(d, "background-position", typeof style.backgroundPosition === "string" ? style.backgroundPosition : void 0);
|
|
356
|
+
put(d, "color", typeof style.color === "string" ? style.color : void 0);
|
|
357
|
+
const opacity = numberToCss(style.opacity);
|
|
358
|
+
if (opacity !== null) d["opacity"] = opacity;
|
|
359
|
+
put(d, "font-family", typeof style.fontFamily === "string" ? style.fontFamily : void 0);
|
|
360
|
+
putLen(d, ["font-size"], style.fontSize);
|
|
361
|
+
const fontWeight = numberToCss(style.fontWeight);
|
|
362
|
+
if (fontWeight !== null) d["font-weight"] = fontWeight;
|
|
363
|
+
const lineHeight = numberToCss(style.lineHeight);
|
|
364
|
+
if (lineHeight !== null) d["line-height"] = lineHeight;
|
|
365
|
+
const textAlign = inSet(TEXT_ALIGN, style.textAlign);
|
|
366
|
+
if (textAlign) d["text-align"] = textAlign;
|
|
367
|
+
putLen(d, ["letter-spacing"], style.letterSpacing);
|
|
368
|
+
const textTransform = inSet(TEXT_TRANSFORM, style.textTransform);
|
|
369
|
+
if (textTransform) d["text-transform"] = textTransform;
|
|
370
|
+
put(d, "border", typeof style.border === "string" ? style.border : void 0);
|
|
371
|
+
put(d, "box-shadow", typeof style.boxShadow === "string" ? style.boxShadow : void 0);
|
|
372
|
+
put(d, "transform", typeof style.transform === "string" ? style.transform : void 0);
|
|
373
|
+
put(d, "filter", typeof style.filter === "string" ? style.filter : void 0);
|
|
374
|
+
putLen(d, ["gap"], style.gap);
|
|
375
|
+
const align = keyword(ALIGN, style.align);
|
|
376
|
+
if (align) d["align-items"] = align;
|
|
377
|
+
const justify = keyword(JUSTIFY, style.justify);
|
|
378
|
+
if (justify) d["justify-content"] = justify;
|
|
208
379
|
return d;
|
|
209
380
|
}
|
|
210
|
-
function
|
|
381
|
+
function shownDisplay(node) {
|
|
382
|
+
if (node.layout === "stack") return "flex";
|
|
383
|
+
if (node.layout === "grid") return "grid";
|
|
384
|
+
return "block";
|
|
385
|
+
}
|
|
386
|
+
function gridColumns(style) {
|
|
387
|
+
const min = lengthToCss(style.columnMin);
|
|
388
|
+
if (min !== null) return `repeat(auto-fit, minmax(min(${min}, 100%), 1fr))`;
|
|
389
|
+
if (typeof style.columns === "number" && Number.isFinite(style.columns)) return `repeat(${Math.max(1, Math.trunc(style.columns))}, 1fr)`;
|
|
390
|
+
return void 0;
|
|
391
|
+
}
|
|
392
|
+
function containerDecls(node, style, isBase) {
|
|
211
393
|
const d = {};
|
|
212
394
|
if (node.layout === "stack") {
|
|
213
|
-
d["display"] = "flex";
|
|
214
|
-
|
|
395
|
+
if (isBase) d["display"] = "flex";
|
|
396
|
+
const direction = inSet(DIRECTIONS, style.direction);
|
|
397
|
+
if (isBase || direction) d["flex-direction"] = direction ?? "column";
|
|
398
|
+
if (style.wrap !== void 0) d["flex-wrap"] = style.wrap ? "wrap" : "nowrap";
|
|
215
399
|
} else if (node.layout === "grid") {
|
|
216
|
-
d["display"] = "grid";
|
|
217
|
-
|
|
218
|
-
|
|
400
|
+
if (isBase) d["display"] = "grid";
|
|
401
|
+
const cols = gridColumns(style);
|
|
402
|
+
if (isBase || cols !== void 0) d["grid-template-columns"] = cols ?? "repeat(1, 1fr)";
|
|
403
|
+
} else if (node.layout === "free" && isBase) {
|
|
219
404
|
d["position"] = "relative";
|
|
220
405
|
}
|
|
221
406
|
return d;
|
|
222
407
|
}
|
|
408
|
+
function visibilityDecls(node, style, isBase) {
|
|
409
|
+
if (style.hidden === true) return { display: "none" };
|
|
410
|
+
if (style.hidden === false && !isBase) return { display: shownDisplay(node) };
|
|
411
|
+
return {};
|
|
412
|
+
}
|
|
413
|
+
function themeDecls(theme) {
|
|
414
|
+
const d = {};
|
|
415
|
+
const walk = (value, path) => {
|
|
416
|
+
if (typeof value === "string" || typeof value === "number" && Number.isFinite(value)) {
|
|
417
|
+
const key = path.map((p) => p.toLowerCase().replace(/[^a-z0-9-]/g, "")).filter(Boolean).join("-");
|
|
418
|
+
if (key) put(d, `--cms-${key}`, String(value));
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
422
|
+
for (const [k, v] of Object.entries(value)) walk(v, [...path, k]);
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
if (typeof theme === "object" && theme !== null && !Array.isArray(theme)) {
|
|
426
|
+
for (const [k, v] of Object.entries(theme)) {
|
|
427
|
+
if (k !== "name") walk(v, [k]);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return d;
|
|
431
|
+
}
|
|
223
432
|
function constraintDecls(c, parentFree) {
|
|
224
433
|
const d = {};
|
|
225
434
|
const transforms = [];
|
|
226
435
|
if (parentFree) {
|
|
227
436
|
d["position"] = "absolute";
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
437
|
+
putLen(d, ["left"], c.left);
|
|
438
|
+
putLen(d, ["right"], c.right);
|
|
439
|
+
putLen(d, ["top"], c.top);
|
|
440
|
+
putLen(d, ["bottom"], c.bottom);
|
|
232
441
|
if (c.centerX) {
|
|
233
442
|
d["left"] = "50%";
|
|
234
443
|
transforms.push("translateX(-50%)");
|
|
@@ -239,13 +448,15 @@ function constraintDecls(c, parentFree) {
|
|
|
239
448
|
}
|
|
240
449
|
if (transforms.length) d["transform"] = transforms.join(" ");
|
|
241
450
|
}
|
|
242
|
-
|
|
243
|
-
if (
|
|
451
|
+
const width = sizeToCss(c.width);
|
|
452
|
+
if (width !== null) d["width"] = width;
|
|
453
|
+
const height = sizeToCss(c.height);
|
|
454
|
+
if (height !== null) d["height"] = height;
|
|
244
455
|
return d;
|
|
245
456
|
}
|
|
246
457
|
function isParentFree(doc, node) {
|
|
247
458
|
if (node.parent === null) return false;
|
|
248
|
-
const parent = doc.nodes
|
|
459
|
+
const parent = own(doc.nodes, node.parent);
|
|
249
460
|
return !parent || parent.layout === void 0 || parent.layout === "free";
|
|
250
461
|
}
|
|
251
462
|
function serializeRule(selector, decls, indent) {
|
|
@@ -260,24 +471,31 @@ function emitDocCss(doc) {
|
|
|
260
471
|
if (!hasNodeMap(doc)) return "";
|
|
261
472
|
const breakpointPx = DEFAULT_BREAKPOINTS;
|
|
262
473
|
const blocks = [];
|
|
474
|
+
const themeRule = serializeRule(":root", themeDecls(doc.theme), "");
|
|
475
|
+
if (themeRule) blocks.push(themeRule);
|
|
263
476
|
for (const id of Object.keys(doc.nodes).sort()) {
|
|
264
|
-
const node = doc.nodes[id];
|
|
477
|
+
const node = withNodeDefaults(doc.nodes[id]);
|
|
265
478
|
const selector = selectorFor(id);
|
|
266
479
|
const parentFree = isParentFree(doc, node);
|
|
267
480
|
const base = {
|
|
268
|
-
...
|
|
481
|
+
...containerDecls(node, node.style.base, true),
|
|
269
482
|
...node.constraints ? constraintDecls(node.constraints.base, parentFree) : {},
|
|
270
|
-
...styleDecls(node.style.base)
|
|
483
|
+
...styleDecls(node.style.base),
|
|
484
|
+
...visibilityDecls(node, node.style.base, true)
|
|
271
485
|
};
|
|
272
486
|
const baseRule = serializeRule(selector, base, "");
|
|
273
487
|
if (baseRule) blocks.push(baseRule);
|
|
274
488
|
for (const bp of doc.breakpoints) {
|
|
275
489
|
if (bp === "base") continue;
|
|
276
|
-
const px = breakpointPx
|
|
277
|
-
if (px
|
|
490
|
+
const px = own(breakpointPx, bp);
|
|
491
|
+
if (typeof px !== "number") continue;
|
|
492
|
+
const bpStyle = own(node.style, bp);
|
|
493
|
+
const bpConstraints = node.constraints ? own(node.constraints, bp) : void 0;
|
|
278
494
|
const decls = {
|
|
279
|
-
...
|
|
280
|
-
...
|
|
495
|
+
...bpStyle ? containerDecls(node, bpStyle, false) : {},
|
|
496
|
+
...bpConstraints ? constraintDecls(bpConstraints, parentFree) : {},
|
|
497
|
+
...bpStyle ? styleDecls(bpStyle) : {},
|
|
498
|
+
...bpStyle ? visibilityDecls(node, bpStyle, false) : {}
|
|
281
499
|
};
|
|
282
500
|
const inner = serializeRule(selector, decls, " ");
|
|
283
501
|
if (inner) blocks.push(`@media (min-width: ${px}px) {
|
|
@@ -285,7 +503,8 @@ ${inner}
|
|
|
285
503
|
}`);
|
|
286
504
|
}
|
|
287
505
|
}
|
|
288
|
-
|
|
506
|
+
if (blocks.length === 0 && Object.keys(doc.nodes).length === 0) return "";
|
|
507
|
+
return [BASE_CSS, ...blocks].join("\n\n") + "\n";
|
|
289
508
|
}
|
|
290
509
|
function CmsPage({
|
|
291
510
|
doc: input,
|