@nextlyhq/plugin-page-builder 0.0.2-alpha.31

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.
@@ -0,0 +1,355 @@
1
+ // src/core/types.ts
2
+ var MAX_DEPTH = 12;
3
+ var MAX_NODES = 5e3;
4
+ var DEFAULT_SLOT = "default";
5
+
6
+ // src/core/registry.ts
7
+ function createBlockRegistry() {
8
+ const map = /* @__PURE__ */ new Map();
9
+ return {
10
+ register(def) {
11
+ if (!def.type.includes("/")) {
12
+ throw new Error(
13
+ `Block type "${def.type}" must be namespaced, e.g. "core/${def.type}".`
14
+ );
15
+ }
16
+ map.set(def.type, def);
17
+ },
18
+ get(type) {
19
+ return map.get(type);
20
+ },
21
+ has(type) {
22
+ return map.has(type);
23
+ },
24
+ all() {
25
+ return [...map.values()];
26
+ }
27
+ };
28
+ }
29
+ var defaultBlockRegistry = createBlockRegistry();
30
+ function defineBlock(def) {
31
+ defaultBlockRegistry.register(def);
32
+ return def;
33
+ }
34
+ function createControlRegistry() {
35
+ const map = /* @__PURE__ */ new Map();
36
+ return {
37
+ register(control) {
38
+ map.set(control.type, control);
39
+ },
40
+ get(type) {
41
+ return map.get(type);
42
+ },
43
+ all() {
44
+ return [...map.values()];
45
+ }
46
+ };
47
+ }
48
+ var defaultControlRegistry = createControlRegistry();
49
+
50
+ // src/core/tree.ts
51
+ function newId() {
52
+ return `pb-${crypto.randomUUID()}`;
53
+ }
54
+ function makeNode(type, props = {}, style, slots) {
55
+ const node = { id: newId(), type, props };
56
+ if (style) node.style = style;
57
+ if (slots) node.slots = slots;
58
+ return node;
59
+ }
60
+ function walk(node, fn, parent) {
61
+ fn(node, parent);
62
+ if (!node.slots) return;
63
+ for (const children of Object.values(node.slots)) {
64
+ for (const child of children) walk(child, fn, node);
65
+ }
66
+ }
67
+ function findNode(node, id) {
68
+ if (node.id === id) return node;
69
+ if (!node.slots) return void 0;
70
+ for (const children of Object.values(node.slots)) {
71
+ for (const child of children) {
72
+ const hit = findNode(child, id);
73
+ if (hit) return hit;
74
+ }
75
+ }
76
+ return void 0;
77
+ }
78
+ function mapTree(node, fn) {
79
+ const mapped = fn(node);
80
+ if (!mapped.slots) return mapped;
81
+ const slots = {};
82
+ for (const [name, children] of Object.entries(mapped.slots)) {
83
+ slots[name] = children.map((c) => mapTree(c, fn));
84
+ }
85
+ return { ...mapped, slots };
86
+ }
87
+ function insertNode(root, parentId, slot, node, index) {
88
+ return mapTree(root, (n) => {
89
+ if (n.id !== parentId) return n;
90
+ const slots = { ...n.slots ?? {} };
91
+ const children = [...slots[slot] ?? []];
92
+ children.splice(Math.max(0, Math.min(index, children.length)), 0, node);
93
+ slots[slot] = children;
94
+ return { ...n, slots };
95
+ });
96
+ }
97
+ function removeNode(root, id) {
98
+ return mapTree(root, (n) => {
99
+ if (!n.slots) return n;
100
+ const slots = {};
101
+ for (const [name, children] of Object.entries(n.slots)) {
102
+ slots[name] = children.filter((c) => c.id !== id);
103
+ }
104
+ return { ...n, slots };
105
+ });
106
+ }
107
+ function isSelfOrDescendant(root, ancestorId, id) {
108
+ const ancestor = findNode(root, ancestorId);
109
+ return !!ancestor && !!findNode(ancestor, id);
110
+ }
111
+ function moveNode(root, id, parentId, slot, index) {
112
+ if (id === parentId || isSelfOrDescendant(root, id, parentId)) return root;
113
+ const found = findNode(root, id);
114
+ if (!found) return root;
115
+ const without = removeNode(root, id);
116
+ return insertNode(without, parentId, slot, found, index);
117
+ }
118
+ function duplicateNode(root, id) {
119
+ const found = findNode(root, id);
120
+ if (!found) return root;
121
+ const clone = (n) => {
122
+ const copy = { ...structuredClone(n), id: newId() };
123
+ if (n.slots) {
124
+ const slots = {};
125
+ for (const [name, children] of Object.entries(n.slots)) {
126
+ slots[name] = children.map(clone);
127
+ }
128
+ copy.slots = slots;
129
+ }
130
+ return copy;
131
+ };
132
+ return mapTree(root, (n) => {
133
+ if (!n.slots) return n;
134
+ let changed = false;
135
+ const slots = {};
136
+ for (const [name, children] of Object.entries(n.slots)) {
137
+ if (children.some((c) => c.id === id)) {
138
+ changed = true;
139
+ const out = [];
140
+ for (const c of children) {
141
+ out.push(c);
142
+ if (c.id === id) out.push(clone(found));
143
+ }
144
+ slots[name] = out;
145
+ } else {
146
+ slots[name] = children;
147
+ }
148
+ }
149
+ return changed ? { ...n, slots } : n;
150
+ });
151
+ }
152
+ function updateNode(root, id, patch) {
153
+ return mapTree(root, (n) => n.id === id ? { ...n, ...patch } : n);
154
+ }
155
+
156
+ // src/core/style-compiler.ts
157
+ import * as csstree from "css-tree";
158
+ var DEFAULT_BREAKPOINTS = [
159
+ { id: "tablet", maxWidth: 1024 },
160
+ { id: "mobile", maxWidth: 640 }
161
+ ];
162
+ var DEFAULT_TOKENS = {
163
+ "color.primary": "#4f46e5",
164
+ "color.secondary": "#0ea5e9",
165
+ "color.accent": "#f59e0b",
166
+ "color.text": "#111827",
167
+ "color.muted": "#6b7280",
168
+ "color.surface": "#f8fafc"
169
+ };
170
+ function compileTokensCss(rootClass, tokens = DEFAULT_TOKENS) {
171
+ const decls = [];
172
+ for (const [key, value] of Object.entries(tokens)) {
173
+ const v = safeValue(value);
174
+ if (v) decls.push(`--nx-${key.replace(/\./g, "-")}: ${v}`);
175
+ }
176
+ return decls.length ? `.${rootClass} { ${decls.join("; ")}; }` : "";
177
+ }
178
+ function nodeClass(id) {
179
+ let h = 2166136261;
180
+ for (let i = 0; i < id.length; i++) {
181
+ h ^= id.charCodeAt(i);
182
+ h = Math.imul(h, 16777619);
183
+ }
184
+ return `nx-pb-${(h >>> 0).toString(36)}`;
185
+ }
186
+ function resolveScalar(v) {
187
+ if (typeof v === "object" && v !== null && "token" in v) {
188
+ return `var(--nx-${String(v.token).replace(/\./g, "-")})`;
189
+ }
190
+ return String(v);
191
+ }
192
+ function safeValue(v) {
193
+ if (v == null || v === "") return null;
194
+ if (/[{};<>]/.test(v)) return null;
195
+ try {
196
+ csstree.parse(v, {
197
+ context: "value",
198
+ onParseError: (e) => {
199
+ throw e;
200
+ }
201
+ });
202
+ return v;
203
+ } catch {
204
+ return null;
205
+ }
206
+ }
207
+ function safeUrl(url) {
208
+ const u = url.trim();
209
+ if (/^(javascript|data|vbscript):/i.test(u)) return null;
210
+ if (/["')\\]/.test(u) || /[\n\r]/.test(u)) return null;
211
+ return u;
212
+ }
213
+ var SIMPLE = [
214
+ ["backgroundColor", "background-color"],
215
+ ["color", "color"],
216
+ ["fontSize", "font-size"],
217
+ ["lineHeight", "line-height"],
218
+ ["textAlign", "text-align"],
219
+ ["width", "width"],
220
+ ["maxWidth", "max-width"],
221
+ ["height", "height"],
222
+ ["borderRadius", "border-radius"],
223
+ ["display", "display"],
224
+ ["gridTemplateColumns", "grid-template-columns"],
225
+ ["gap", "gap"],
226
+ ["justifyContent", "justify-content"],
227
+ ["alignItems", "align-items"]
228
+ ];
229
+ function compileStyleValues(sv) {
230
+ const out = [];
231
+ const box = (prop) => {
232
+ const sides = sv[prop];
233
+ if (!sides) return;
234
+ for (const side of ["top", "right", "bottom", "left"]) {
235
+ const raw = sides[side];
236
+ if (raw == null) continue;
237
+ const v = safeValue(raw);
238
+ if (v) out.push(`${prop}-${side}: ${v}`);
239
+ }
240
+ };
241
+ box("margin");
242
+ box("padding");
243
+ for (const [key, cssName] of SIMPLE) {
244
+ const raw = sv[key];
245
+ if (raw == null) continue;
246
+ const v = safeValue(resolveScalar(raw));
247
+ if (v) out.push(`${cssName}: ${v}`);
248
+ }
249
+ if (sv.backgroundImage != null) {
250
+ const url = safeUrl(resolveScalar(sv.backgroundImage));
251
+ if (url) out.push(`background-image: url("${url}")`);
252
+ }
253
+ return out;
254
+ }
255
+ function compileNodeCss(node, opts = {}) {
256
+ const bps = opts.breakpoints ?? DEFAULT_BREAKPOINTS;
257
+ const cls = nodeClass(node.id);
258
+ const blocks = [];
259
+ const hasHover = !!node.styleHover && Object.keys(node.styleHover).length > 0;
260
+ if (hasHover) blocks.push(`.${cls} { transition: all 0.2s ease; }`);
261
+ const emit = (style, suffix) => {
262
+ if (!style) return;
263
+ const base = style.base ? compileStyleValues(style.base) : [];
264
+ if (base.length) blocks.push(`.${cls}${suffix} { ${base.join("; ")}; }`);
265
+ for (const bp of bps) {
266
+ const sv = style[bp.id];
267
+ const decls = sv ? compileStyleValues(sv) : [];
268
+ if (decls.length) {
269
+ blocks.push(
270
+ `@media (max-width: ${bp.maxWidth}px) { .${cls}${suffix} { ${decls.join("; ")}; } }`
271
+ );
272
+ }
273
+ }
274
+ };
275
+ emit(node.style, "");
276
+ emit(node.styleHover, ":hover");
277
+ return blocks.join("\n");
278
+ }
279
+ function compileDocumentCss(doc, opts = {}) {
280
+ const parts = [];
281
+ walk(doc.root, (n) => {
282
+ const css = compileNodeCss(n, opts);
283
+ if (css) parts.push(css);
284
+ });
285
+ return parts.join("\n");
286
+ }
287
+
288
+ // src/core/bindings.ts
289
+ function getPath(obj, path) {
290
+ return path.split(".").reduce((acc, key) => {
291
+ if (acc && typeof acc === "object") {
292
+ return acc[key];
293
+ }
294
+ return void 0;
295
+ }, obj);
296
+ }
297
+ function applyTransform(value, transform) {
298
+ if (!transform) return value;
299
+ const idx = transform.indexOf(":");
300
+ const name = (idx === -1 ? transform : transform.slice(0, idx)).trim();
301
+ switch (name) {
302
+ case "uppercase":
303
+ return typeof value === "string" ? value.toUpperCase() : value;
304
+ case "lowercase":
305
+ return typeof value === "string" ? value.toLowerCase() : value;
306
+ case "date": {
307
+ if (typeof value !== "string" && typeof value !== "number") return value;
308
+ const d = new Date(value);
309
+ return Number.isNaN(d.getTime()) ? value : d.toISOString().slice(0, 10);
310
+ }
311
+ default:
312
+ return value;
313
+ }
314
+ }
315
+ function resolveBindings(node, item) {
316
+ const out = { ...node.props };
317
+ if (node.bindings) {
318
+ for (const [prop, binding] of Object.entries(node.bindings)) {
319
+ out[prop] = applyTransform(
320
+ getPath(item, binding.path),
321
+ binding.transform
322
+ );
323
+ }
324
+ }
325
+ return out;
326
+ }
327
+
328
+ export {
329
+ MAX_DEPTH,
330
+ MAX_NODES,
331
+ DEFAULT_SLOT,
332
+ createBlockRegistry,
333
+ defaultBlockRegistry,
334
+ defineBlock,
335
+ createControlRegistry,
336
+ defaultControlRegistry,
337
+ newId,
338
+ makeNode,
339
+ walk,
340
+ findNode,
341
+ insertNode,
342
+ removeNode,
343
+ moveNode,
344
+ duplicateNode,
345
+ updateNode,
346
+ DEFAULT_BREAKPOINTS,
347
+ DEFAULT_TOKENS,
348
+ compileTokensCss,
349
+ nodeClass,
350
+ compileNodeCss,
351
+ compileDocumentCss,
352
+ getPath,
353
+ resolveBindings
354
+ };
355
+ //# sourceMappingURL=chunk-ABMSYCSD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/types.ts","../src/core/registry.ts","../src/core/tree.ts","../src/core/style-compiler.ts","../src/core/bindings.ts"],"sourcesContent":["/**\n * Core contracts for the page builder — isomorphic and runtime-React-free.\n *\n * Only *type-only* imports of React / Nextly are allowed here (they are erased at\n * build, so the `.` bundle has no React/Nextly runtime dependency). The registry\n * stores block-definition objects (including their `render` functions) but core\n * never calls React itself.\n */\nimport type { ReactNode } from \"react\";\n\n// ---------------------------------------------------------------------------\n// Document + node model (spec §6)\n// ---------------------------------------------------------------------------\n\n/** Document-format version. Bumped only for envelope-shape changes (migrations). */\nexport type DocumentVersion = 1;\n\n/** Reserved doors (spec §13): templates/parts and i18n are designed-for, not built. */\nexport type BlockDocumentKind = \"page\" | \"template\" | \"part\";\n\nexport interface BlockDocument {\n version: DocumentVersion;\n /** Reserved — defaults to \"page\". Enables Theme-Builder-style templates later. */\n kind?: BlockDocumentKind;\n /** Reserved (i18n) — the locale this document's content is authored in. */\n locale?: string;\n /** Reserved (i18n) — links documents that are translations of one another. */\n translationGroup?: string;\n root: BlockNode;\n /** Reserved — page-level settings (SEO, etc.). */\n settings?: { seo?: Record<string, unknown> };\n /** Reserved — a usage index so media/relationship refs are trackable without a full walk. */\n assets?: { mediaIds?: string[] };\n}\n\nexport interface BlockNode {\n /** Stable unique id (crypto.randomUUID). Stable across locales; drives the scoped CSS class. */\n id: string;\n /** Namespaced block type, e.g. \"core/heading\". */\n type: string;\n /** Schema version of the block instance; compared to the definition's `version` for migrations. */\n definitionVersion?: number;\n /** Content/config values. Literal values only — bound values live in `bindings`. */\n props: Record<string, unknown>;\n /** Typed, responsive style overrides (spec §8). */\n style?: ResponsiveStyle;\n /** Responsive style overrides applied on `:hover` (spec §8, hover states). */\n styleHover?: ResponsiveStyle;\n /** Named child regions. \"default\" is the primary slot; only container blocks have slots. */\n slots?: Record<string, BlockNode[]>;\n /** Typed data bindings, keyed by the prop they fill. Kept separate from `props` (spec §10). */\n bindings?: Record<string, Binding>;\n /** Author escape hatch. */\n customClass?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Styling (spec §8) — typed; never arbitrary strings compiled straight to CSS\n// ---------------------------------------------------------------------------\n\nexport type Breakpoint = string; // project-configurable id, e.g. \"base\" | \"tablet\" | \"mobile\"\n\nexport interface BoxSides {\n top?: string;\n right?: string;\n bottom?: string;\n left?: string;\n}\n\n/** A style value may be a literal or a design-token reference (spec §8). */\nexport type TokenRef = { token: string };\nexport type StyleScalar = string | TokenRef;\n\nexport interface StyleValues {\n margin?: BoxSides;\n padding?: BoxSides;\n backgroundColor?: StyleScalar;\n backgroundImage?: StyleScalar;\n color?: StyleScalar;\n fontSize?: StyleScalar;\n lineHeight?: StyleScalar;\n textAlign?: \"left\" | \"center\" | \"right\" | \"justify\";\n width?: StyleScalar;\n maxWidth?: StyleScalar;\n height?: StyleScalar;\n borderRadius?: StyleScalar;\n display?: string;\n gridTemplateColumns?: StyleScalar;\n gap?: StyleScalar;\n justifyContent?: string;\n alignItems?: string;\n}\n\n/** Per-breakpoint style overrides. The base breakpoint holds defaults; others override. */\nexport type ResponsiveStyle = Partial<Record<Breakpoint, StyleValues>>;\n\n// ---------------------------------------------------------------------------\n// Data binding (spec §10) — typed, schema-driven, access-controlled\n// ---------------------------------------------------------------------------\n\nexport interface Binding {\n source: \"field\";\n /** Dot-path into the current Query Loop item, e.g. \"title\" or \"author.name\". */\n path: string;\n /** Optional display transform, e.g. \"date:MMM d, yyyy\". */\n transform?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Block definition + control contracts (spec §7) — the extensibility core\n// ---------------------------------------------------------------------------\n\nexport type BlockCategory = \"basic\" | \"layout\" | \"media\" | \"dynamic\";\n\nexport interface SlotSpec {\n name: string;\n /** Namespaced block types allowed in this slot. Omit for \"any\". */\n allowedBlocks?: string[];\n}\n\n/** A reference from a block definition to a style control + the style key it edits. */\nexport interface ControlRef {\n /** Control type registered in the control registry, e.g. \"spacing\" | \"color\" | \"dimension\". */\n control: string;\n /** Style key this control writes (e.g. \"padding\", \"backgroundColor\"). */\n styleKey: string;\n label: string;\n}\n\nexport interface BlockRenderArgs<P = Record<string, unknown>> {\n props: P;\n node: BlockNode;\n slots: Record<string, ReactNode>;\n /** The scoped class the block MUST apply to its own root element (no wrapper div). */\n className: string;\n}\n\nexport interface BlockDefinition<P = Record<string, unknown>> {\n /** Namespaced type, e.g. \"core/heading\". */\n type: string;\n /** Schema version; drives per-block migrations. */\n version: number;\n label: string;\n icon: string;\n category: BlockCategory;\n isContainer?: boolean;\n slots?: SlotSpec[];\n /**\n * Content fields — reuse Nextly's field system so the inspector \"Content\" tab and\n * dynamic-data binding are driven by one schema. Typed loosely here to avoid coupling\n * core's compile to Nextly's full type surface; the admin narrows it to `FieldConfig[]`.\n */\n contentFields?: unknown[];\n /** Style/visual controls (page-builder control registry) driving the Style/Responsive tabs. */\n styleControls?: ControlRef[];\n defaultProps: P;\n defaultStyle?: ResponsiveStyle;\n /** Prop keys that are translatable (i18n door; metadata only for MVP). */\n localized?: string[];\n /** Server-safe by default; may be a client component when interactive. */\n render: (args: BlockRenderArgs<P>) => ReactNode;\n /** Pure JSON→JSON upgrade for instances older than `version`. */\n migrate?: (\n old: unknown,\n fromVersion: number\n ) => { props: P; style?: ResponsiveStyle };\n /** Extra per-instance validation beyond the core invariants. */\n validate?: (node: BlockNode) => true | string;\n}\n\nexport interface ControlDef {\n /** Control type key, e.g. \"spacing\" | \"color\" | \"dimension\" | \"align\" | \"media\" | \"link\". */\n type: string;\n /** The React control component (registered from the admin entry). Opaque to core. */\n Component: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Limits (spec §14)\n// ---------------------------------------------------------------------------\n\nexport const MAX_DEPTH = 12;\nexport const MAX_NODES = 5000;\nexport const DEFAULT_SLOT = \"default\";\n","/**\n * Open, string-keyed registries — the single extensibility seam (spec §7).\n *\n * The validator, renderer, and inspector all read the SAME block registry, so a\n * third party adds a block with one `registerBlock()` call and no core edit.\n * Types are namespaced (`core/heading`, `acme/pricing-table`) to stay collision-free.\n */\nimport type { BlockDefinition, ControlDef } from \"./types\";\n\nexport interface BlockRegistry {\n register(def: BlockDefinition): void;\n get(type: string): BlockDefinition | undefined;\n has(type: string): boolean;\n all(): BlockDefinition[];\n}\n\nexport function createBlockRegistry(): BlockRegistry {\n const map = new Map<string, BlockDefinition>();\n return {\n register(def) {\n if (!def.type.includes(\"/\")) {\n throw new Error(\n `Block type \"${def.type}\" must be namespaced, e.g. \"core/${def.type}\".`\n );\n }\n map.set(def.type, def);\n },\n get(type) {\n return map.get(type);\n },\n has(type) {\n return map.has(type);\n },\n all() {\n return [...map.values()];\n },\n };\n}\n\n/** The default registry that built-in `core/*` blocks register into on import. */\nexport const defaultBlockRegistry: BlockRegistry = createBlockRegistry();\n\n/**\n * Declare a block and register it into the default registry. This is the Puck-style\n * declarative model: one definition drives validator + renderer + inspector.\n */\nexport function defineBlock<P>(def: BlockDefinition<P>): BlockDefinition<P> {\n defaultBlockRegistry.register(def as BlockDefinition);\n return def;\n}\n\nexport interface ControlRegistry {\n register(control: ControlDef): void;\n get(type: string): ControlDef | undefined;\n all(): ControlDef[];\n}\n\nexport function createControlRegistry(): ControlRegistry {\n const map = new Map<string, ControlDef>();\n return {\n register(control) {\n map.set(control.type, control);\n },\n get(type) {\n return map.get(type);\n },\n all() {\n return [...map.values()];\n },\n };\n}\n\n/** The default style/visual control registry (extensible — novel controls register here). */\nexport const defaultControlRegistry: ControlRegistry = createControlRegistry();\n","/**\n * Slot-aware, immutable operations over the block tree (spec §5/§6). Pure and\n * React-free. Every mutation returns a new tree; the input is never modified.\n */\nimport type { BlockNode, ResponsiveStyle } from \"./types\";\nimport { DEFAULT_SLOT } from \"./types\";\n\n/** Stable unique id. `crypto.randomUUID` is available in Node ≥18 and modern browsers. */\nexport function newId(): string {\n return `pb-${crypto.randomUUID()}`;\n}\n\nexport function makeNode(\n type: string,\n props: Record<string, unknown> = {},\n style?: ResponsiveStyle,\n slots?: Record<string, BlockNode[]>\n): BlockNode {\n const node: BlockNode = { id: newId(), type, props };\n if (style) node.style = style;\n if (slots) node.slots = slots;\n return node;\n}\n\n/** Depth-first visit: the node, then each slot's children in order. */\nexport function walk(\n node: BlockNode,\n fn: (n: BlockNode, parent?: BlockNode) => void,\n parent?: BlockNode\n): void {\n fn(node, parent);\n if (!node.slots) return;\n for (const children of Object.values(node.slots)) {\n for (const child of children) walk(child, fn, node);\n }\n}\n\nexport function findNode(node: BlockNode, id: string): BlockNode | undefined {\n if (node.id === id) return node;\n if (!node.slots) return undefined;\n for (const children of Object.values(node.slots)) {\n for (const child of children) {\n const hit = findNode(child, id);\n if (hit) return hit;\n }\n }\n return undefined;\n}\n\n/** Immutably rebuild the tree, applying `fn` to every node (parents before children). */\nfunction mapTree(node: BlockNode, fn: (n: BlockNode) => BlockNode): BlockNode {\n const mapped = fn(node);\n if (!mapped.slots) return mapped;\n const slots: Record<string, BlockNode[]> = {};\n for (const [name, children] of Object.entries(mapped.slots)) {\n slots[name] = children.map(c => mapTree(c, fn));\n }\n return { ...mapped, slots };\n}\n\nexport function insertNode(\n root: BlockNode,\n parentId: string,\n slot: string,\n node: BlockNode,\n index: number\n): BlockNode {\n return mapTree(root, n => {\n if (n.id !== parentId) return n;\n const slots = { ...(n.slots ?? {}) };\n const children = [...(slots[slot] ?? [])];\n children.splice(Math.max(0, Math.min(index, children.length)), 0, node);\n slots[slot] = children;\n return { ...n, slots };\n });\n}\n\nexport function removeNode(root: BlockNode, id: string): BlockNode {\n return mapTree(root, n => {\n if (!n.slots) return n;\n const slots: Record<string, BlockNode[]> = {};\n for (const [name, children] of Object.entries(n.slots)) {\n slots[name] = children.filter(c => c.id !== id);\n }\n return { ...n, slots };\n });\n}\n\n/** True if `id` is `ancestorId` itself or nested anywhere inside it. */\nfunction isSelfOrDescendant(\n root: BlockNode,\n ancestorId: string,\n id: string\n): boolean {\n const ancestor = findNode(root, ancestorId);\n return !!ancestor && !!findNode(ancestor, id);\n}\n\nexport function moveNode(\n root: BlockNode,\n id: string,\n parentId: string,\n slot: string,\n index: number\n): BlockNode {\n if (id === parentId || isSelfOrDescendant(root, id, parentId)) return root; // cycle guard\n const found = findNode(root, id);\n if (!found) return root;\n const without = removeNode(root, id);\n return insertNode(without, parentId, slot, found, index);\n}\n\nexport function duplicateNode(root: BlockNode, id: string): BlockNode {\n const found = findNode(root, id);\n if (!found) return root;\n const clone = (n: BlockNode): BlockNode => {\n const copy: BlockNode = { ...structuredClone(n), id: newId() };\n if (n.slots) {\n const slots: Record<string, BlockNode[]> = {};\n for (const [name, children] of Object.entries(n.slots)) {\n slots[name] = children.map(clone);\n }\n copy.slots = slots;\n }\n return copy;\n };\n return mapTree(root, n => {\n if (!n.slots) return n;\n let changed = false;\n const slots: Record<string, BlockNode[]> = {};\n for (const [name, children] of Object.entries(n.slots)) {\n if (children.some(c => c.id === id)) {\n changed = true;\n const out: BlockNode[] = [];\n for (const c of children) {\n out.push(c);\n if (c.id === id) out.push(clone(found));\n }\n slots[name] = out;\n } else {\n slots[name] = children;\n }\n }\n return changed ? { ...n, slots } : n;\n });\n}\n\nexport function updateNode(\n root: BlockNode,\n id: string,\n patch: Partial<BlockNode>\n): BlockNode {\n return mapTree(root, n => (n.id === id ? { ...n, ...patch } : n));\n}\n\nexport { DEFAULT_SLOT };\n","/**\n * Typed style → scoped CSS compiler (spec §8). React-free.\n *\n * - Values are validated through a real CSS parser (css-tree) so nothing can break\n * out of its declaration block; URLs get explicit scheme checks (css-tree accepts\n * `url(\"javascript:…\")` syntactically, so a parser alone is not enough).\n * - Design-token refs compile to CSS custom properties.\n * - Breakpoints are project-configurable DATA; default cascade is DESKTOP-FIRST.\n */\nimport * as csstree from \"css-tree\";\n\nimport { walk } from \"./tree\";\nimport type {\n BlockDocument,\n BlockNode,\n ResponsiveStyle,\n StyleScalar,\n StyleValues,\n} from \"./types\";\n\nexport interface BreakpointDef {\n id: string;\n maxWidth: number;\n}\n\n/** Desktop-first defaults (base = desktop; these override downward). */\nexport const DEFAULT_BREAKPOINTS: BreakpointDef[] = [\n { id: \"tablet\", maxWidth: 1024 },\n { id: \"mobile\", maxWidth: 640 },\n];\n\nexport interface CompileOptions {\n breakpoints?: BreakpointDef[];\n}\n\n/**\n * Default design-token palette (spec §8). Token refs (`{ token: \"color.primary\" }`)\n * compile to `var(--nx-color-primary)`; these values back those vars out of the box so\n * tokens work without extra config. A host can override via `PageRenderer`'s `tokens`\n * prop (a fuller project-config surface is a future door).\n */\nexport const DEFAULT_TOKENS: Record<string, string> = {\n \"color.primary\": \"#4f46e5\",\n \"color.secondary\": \"#0ea5e9\",\n \"color.accent\": \"#f59e0b\",\n \"color.text\": \"#111827\",\n \"color.muted\": \"#6b7280\",\n \"color.surface\": \"#f8fafc\",\n};\n\n/** Emit the token palette as CSS custom properties on the page root. */\nexport function compileTokensCss(\n rootClass: string,\n tokens: Record<string, string> = DEFAULT_TOKENS\n): string {\n const decls: string[] = [];\n for (const [key, value] of Object.entries(tokens)) {\n const v = safeValue(value);\n if (v) decls.push(`--nx-${key.replace(/\\./g, \"-\")}: ${v}`);\n }\n return decls.length ? `.${rootClass} { ${decls.join(\"; \")}; }` : \"\";\n}\n\n/** Deterministic, short, stable scoped class for a node id (FNV-1a → base36). */\nexport function nodeClass(id: string): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < id.length; i++) {\n h ^= id.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return `nx-pb-${(h >>> 0).toString(36)}`;\n}\n\nfunction resolveScalar(v: StyleScalar): string {\n if (typeof v === \"object\" && v !== null && \"token\" in v) {\n return `var(--nx-${String(v.token).replace(/\\./g, \"-\")})`;\n }\n return String(v);\n}\n\n/** Validate a CSS *value*. Returns the value if safe, else null (dropped). */\nfunction safeValue(v: string): string | null {\n if (v == null || v === \"\") return null;\n if (/[{};<>]/.test(v)) return null; // fast reject declaration/tag breakout\n try {\n csstree.parse(v, {\n context: \"value\",\n onParseError: e => {\n throw e;\n },\n });\n return v;\n } catch {\n return null;\n }\n}\n\n/** Validate a URL for url(). css-tree accepts quoted `javascript:` urls, so check the scheme. */\nfunction safeUrl(url: string): string | null {\n const u = url.trim();\n if (/^(javascript|data|vbscript):/i.test(u)) return null;\n if (/[\"')\\\\]/.test(u) || /[\\n\\r]/.test(u)) return null; // avoid url() breakout\n return u;\n}\n\nconst SIMPLE: [keyof StyleValues, string][] = [\n [\"backgroundColor\", \"background-color\"],\n [\"color\", \"color\"],\n [\"fontSize\", \"font-size\"],\n [\"lineHeight\", \"line-height\"],\n [\"textAlign\", \"text-align\"],\n [\"width\", \"width\"],\n [\"maxWidth\", \"max-width\"],\n [\"height\", \"height\"],\n [\"borderRadius\", \"border-radius\"],\n [\"display\", \"display\"],\n [\"gridTemplateColumns\", \"grid-template-columns\"],\n [\"gap\", \"gap\"],\n [\"justifyContent\", \"justify-content\"],\n [\"alignItems\", \"align-items\"],\n];\n\nfunction compileStyleValues(sv: StyleValues): string[] {\n const out: string[] = [];\n\n const box = (prop: \"margin\" | \"padding\") => {\n const sides = sv[prop];\n if (!sides) return;\n for (const side of [\"top\", \"right\", \"bottom\", \"left\"] as const) {\n const raw = sides[side];\n if (raw == null) continue;\n const v = safeValue(raw);\n if (v) out.push(`${prop}-${side}: ${v}`);\n }\n };\n box(\"margin\");\n box(\"padding\");\n\n for (const [key, cssName] of SIMPLE) {\n const raw = sv[key] as StyleScalar | undefined;\n if (raw == null) continue;\n const v = safeValue(resolveScalar(raw));\n if (v) out.push(`${cssName}: ${v}`);\n }\n\n if (sv.backgroundImage != null) {\n const url = safeUrl(resolveScalar(sv.backgroundImage));\n if (url) out.push(`background-image: url(\"${url}\")`);\n }\n\n return out;\n}\n\nexport function compileNodeCss(\n node: BlockNode,\n opts: CompileOptions = {}\n): string {\n const bps = opts.breakpoints ?? DEFAULT_BREAKPOINTS;\n const cls = nodeClass(node.id);\n const blocks: string[] = [];\n\n const hasHover = !!node.styleHover && Object.keys(node.styleHover).length > 0;\n // Smooth the normal → hover change (Elementor-style).\n if (hasHover) blocks.push(`.${cls} { transition: all 0.2s ease; }`);\n\n const emit = (style: ResponsiveStyle | undefined, suffix: string): void => {\n if (!style) return;\n const base = style.base ? compileStyleValues(style.base) : [];\n if (base.length) blocks.push(`.${cls}${suffix} { ${base.join(\"; \")}; }`);\n for (const bp of bps) {\n const sv = style[bp.id];\n const decls = sv ? compileStyleValues(sv) : [];\n if (decls.length) {\n blocks.push(\n `@media (max-width: ${bp.maxWidth}px) { .${cls}${suffix} { ${decls.join(\"; \")}; } }`\n );\n }\n }\n };\n\n emit(node.style, \"\");\n emit(node.styleHover, \":hover\");\n\n return blocks.join(\"\\n\");\n}\n\n/** One <style> block worth of CSS for the whole document. */\nexport function compileDocumentCss(\n doc: BlockDocument,\n opts: CompileOptions = {}\n): string {\n const parts: string[] = [];\n walk(doc.root, n => {\n const css = compileNodeCss(n, opts);\n if (css) parts.push(css);\n });\n return parts.join(\"\\n\");\n}\n","/**\n * Typed data-binding resolution for the Query Loop (spec §10). React-free.\n *\n * Bindings live in `node.bindings` (kept separate from literal props). At render time\n * each binding is resolved from the current loop item via a dot-path, optionally\n * transformed, and merged over the node's literal props.\n */\nimport type { BlockNode } from \"./types\";\n\n/** Safe dotted read: getPath({a:{b:2}}, \"a.b\") === 2. */\nexport function getPath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce<unknown>((acc, key) => {\n if (acc && typeof acc === \"object\") {\n return (acc as Record<string, unknown>)[key];\n }\n return undefined;\n }, obj);\n}\n\n/** Minimal, extensible transform registry. Unknown transforms pass the value through. */\nfunction applyTransform(value: unknown, transform?: string): unknown {\n if (!transform) return value;\n const idx = transform.indexOf(\":\");\n const name = (idx === -1 ? transform : transform.slice(0, idx)).trim();\n switch (name) {\n case \"uppercase\":\n return typeof value === \"string\" ? value.toUpperCase() : value;\n case \"lowercase\":\n return typeof value === \"string\" ? value.toLowerCase() : value;\n case \"date\": {\n if (typeof value !== \"string\" && typeof value !== \"number\") return value;\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? value : d.toISOString().slice(0, 10);\n }\n default:\n return value;\n }\n}\n\n/**\n * Return a props object with each `node.bindings[prop]` resolved from `item`\n * (dot-path + optional transform) merged over `node.props`. Literal props untouched.\n */\nexport function resolveBindings(\n node: BlockNode,\n item: Record<string, unknown>\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...node.props };\n if (node.bindings) {\n for (const [prop, binding] of Object.entries(node.bindings)) {\n out[prop] = applyTransform(\n getPath(item, binding.path),\n binding.transform\n );\n }\n }\n return out;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ // src/render/ErrorBoundary.tsx
3
+ import { Component } from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ var BlockErrorBoundary = class extends Component {
6
+ state = { failed: false };
7
+ static getDerivedStateFromError() {
8
+ return { failed: true };
9
+ }
10
+ render() {
11
+ if (this.state.failed) {
12
+ return this.props.fallback ?? /* @__PURE__ */ jsx("div", { "data-nx-block-error": "1" });
13
+ }
14
+ return this.props.children;
15
+ }
16
+ };
17
+
18
+ export {
19
+ BlockErrorBoundary
20
+ };
21
+ //# sourceMappingURL=chunk-E4G7NNXW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/render/ErrorBoundary.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Per-block error isolation (spec §10). The single intentional client island in\n * `render/`: a throwing block renders a small fallback instead of taking down the\n * whole page/canvas. `getDerivedStateFromError` also works under\n * `renderToStaticMarkup`, so server output is protected too.\n */\nimport { Component, type ReactNode } from \"react\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n}\ninterface State {\n failed: boolean;\n}\n\nexport class BlockErrorBoundary extends Component<Props, State> {\n state: State = { failed: false };\n\n static getDerivedStateFromError(): State {\n return { failed: true };\n }\n\n render(): ReactNode {\n if (this.state.failed) {\n return this.props.fallback ?? <div data-nx-block-error=\"1\" />;\n }\n return this.props.children;\n }\n}\n"],"mappings":";AAQA,SAAS,iBAAiC;AAmBN;AAT7B,IAAM,qBAAN,cAAiC,UAAwB;AAAA,EAC9D,QAAe,EAAE,QAAQ,MAAM;AAAA,EAE/B,OAAO,2BAAkC;AACvC,WAAO,EAAE,QAAQ,KAAK;AAAA,EACxB;AAAA,EAEA,SAAoB;AAClB,QAAI,KAAK,MAAM,QAAQ;AACrB,aAAO,KAAK,MAAM,YAAY,oBAAC,SAAI,uBAAoB,KAAI;AAAA,IAC7D;AACA,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;","names":[]}
@@ -0,0 +1,119 @@
1
+ import {
2
+ MAX_DEPTH,
3
+ MAX_NODES,
4
+ defaultBlockRegistry
5
+ } from "./chunk-ABMSYCSD.js";
6
+
7
+ // src/core/validate.ts
8
+ function validateDocument(doc, registry, opts = {}) {
9
+ if (!doc || typeof doc !== "object") return "document must be an object";
10
+ const d = doc;
11
+ if (d.version !== 1) {
12
+ return `unsupported document version ${String(d.version)}`;
13
+ }
14
+ if (!d.root || typeof d.root !== "object") return "document.root is required";
15
+ const seen = /* @__PURE__ */ new Set();
16
+ let count = 0;
17
+ const check = (n, depth) => {
18
+ if (depth > MAX_DEPTH) return `tree exceeds max depth ${MAX_DEPTH}`;
19
+ if (++count > MAX_NODES) return `tree exceeds max node count ${MAX_NODES}`;
20
+ if (!n || typeof n.id !== "string" || !n.id) return "node is missing an id";
21
+ if (seen.has(n.id)) return `duplicate node id ${n.id}`;
22
+ seen.add(n.id);
23
+ if (typeof n.type !== "string" || !n.type.includes("/")) {
24
+ return `node type must be namespaced: ${String(n.type)}`;
25
+ }
26
+ const def = registry.get(n.type);
27
+ if (!def && !opts.allowUnknown) return `unknown block type ${n.type}`;
28
+ if (n.slots) {
29
+ if (def && !def.isContainer) {
30
+ return `${n.type} cannot have slots (not a container)`;
31
+ }
32
+ for (const [slotName, children] of Object.entries(n.slots)) {
33
+ const spec = def?.slots?.find((s) => s.name === slotName);
34
+ for (const child of children) {
35
+ if (spec?.allowedBlocks && !spec.allowedBlocks.includes(child.type)) {
36
+ return `${child.type} is not allowed in slot "${slotName}" of ${n.type}`;
37
+ }
38
+ const e = check(child, depth + 1);
39
+ if (e) return e;
40
+ }
41
+ }
42
+ }
43
+ if (def?.validate) {
44
+ const r = def.validate(n);
45
+ if (r !== true) return r;
46
+ }
47
+ return null;
48
+ };
49
+ return check(d.root, 0) ?? true;
50
+ }
51
+
52
+ // src/collections/pageBuilderField.ts
53
+ import { json } from "nextly/config";
54
+ var FIELD_COMPONENT_PATH = "@nextlyhq/plugin-page-builder/admin#PageBuilderField";
55
+ function pageBuilderField(name, opts = {}) {
56
+ return json({
57
+ name,
58
+ label: opts.label,
59
+ admin: { component: FIELD_COMPONENT_PATH, condition: opts.condition },
60
+ // An unset builder field is valid (e.g. when another editor mode is chosen).
61
+ validate: (value) => value == null ? true : validateDocument(value, defaultBlockRegistry, { allowUnknown: true })
62
+ });
63
+ }
64
+
65
+ // src/collections/pageBuilderEntry.ts
66
+ import { option, select } from "nextly/config";
67
+ var PAGE_BUILDER_CONTENT_FIELD = "content";
68
+ var EDITOR_MODE_FIELD = "editormode";
69
+ var PAGE_BUILDER_TYPE = "page-builder";
70
+ var PAGE_BUILDER_FIELD_TYPE = {
71
+ type: PAGE_BUILDER_TYPE,
72
+ storage: "json",
73
+ component: FIELD_COMPONENT_PATH,
74
+ layout: "takeover"
75
+ };
76
+ function pageBuilderFields(opts = {}) {
77
+ const defaultMode = opts.defaultMode ?? "default";
78
+ return [
79
+ select({
80
+ // All-lowercase so the name is identical for code-first AND UI-created collections
81
+ // (the schema builder lowercases field names); keeps the condition/detection stable.
82
+ name: EDITOR_MODE_FIELD,
83
+ label: "Editor",
84
+ defaultValue: defaultMode,
85
+ options: [
86
+ option("Default", "default"),
87
+ option("Page Builder", "builder")
88
+ ],
89
+ // Hidden field: it stores the per-entry mode but never renders as a field.
90
+ // The choice is surfaced as a toolbar toggle (contributes.admin
91
+ // .entryFormToolbarSlot) and it's kept out of the schema-builder field list.
92
+ admin: { hidden: true, description: "Choose how to edit this entry." }
93
+ }),
94
+ pageBuilderField(PAGE_BUILDER_CONTENT_FIELD, {
95
+ label: "Page Builder",
96
+ condition: { field: EDITOR_MODE_FIELD, equals: "builder" }
97
+ })
98
+ ];
99
+ }
100
+ function withPageBuilder(config, opts = {}) {
101
+ const defaultMode = opts.defaultMode ?? "default";
102
+ return {
103
+ ...config,
104
+ fields: [...config.fields ?? [], ...pageBuilderFields({ defaultMode })]
105
+ };
106
+ }
107
+
108
+ export {
109
+ validateDocument,
110
+ FIELD_COMPONENT_PATH,
111
+ pageBuilderField,
112
+ PAGE_BUILDER_CONTENT_FIELD,
113
+ EDITOR_MODE_FIELD,
114
+ PAGE_BUILDER_TYPE,
115
+ PAGE_BUILDER_FIELD_TYPE,
116
+ pageBuilderFields,
117
+ withPageBuilder
118
+ };
119
+ //# sourceMappingURL=chunk-EGU6QGUC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/validate.ts","../src/collections/pageBuilderField.ts","../src/collections/pageBuilderEntry.ts"],"sourcesContent":["/**\n * Document validation invariants (spec §14). Returns `true` when valid, else a\n * human-readable error string. Used as the `pages.content` field validator (M3) and\n * defensively in the editor. Pure and React-free.\n */\nimport type { BlockRegistry } from \"./registry\";\nimport type { BlockDocument, BlockNode } from \"./types\";\nimport { MAX_DEPTH, MAX_NODES } from \"./types\";\n\nexport interface ValidateOptions {\n /** Preserve/accept unknown block types (resilience, spec §12). Default false. */\n allowUnknown?: boolean;\n}\n\nexport function validateDocument(\n doc: unknown,\n registry: BlockRegistry,\n opts: ValidateOptions = {}\n): true | string {\n if (!doc || typeof doc !== \"object\") return \"document must be an object\";\n const d = doc as BlockDocument;\n if (d.version !== 1) {\n return `unsupported document version ${String((d as { version?: unknown }).version)}`;\n }\n if (!d.root || typeof d.root !== \"object\") return \"document.root is required\";\n\n const seen = new Set<string>();\n let count = 0;\n\n const check = (n: BlockNode, depth: number): string | null => {\n if (depth > MAX_DEPTH) return `tree exceeds max depth ${MAX_DEPTH}`;\n if (++count > MAX_NODES) return `tree exceeds max node count ${MAX_NODES}`;\n if (!n || typeof n.id !== \"string\" || !n.id) return \"node is missing an id\";\n if (seen.has(n.id)) return `duplicate node id ${n.id}`;\n seen.add(n.id);\n if (typeof n.type !== \"string\" || !n.type.includes(\"/\")) {\n return `node type must be namespaced: ${String(n.type)}`;\n }\n\n const def = registry.get(n.type);\n if (!def && !opts.allowUnknown) return `unknown block type ${n.type}`;\n\n if (n.slots) {\n if (def && !def.isContainer) {\n return `${n.type} cannot have slots (not a container)`;\n }\n for (const [slotName, children] of Object.entries(n.slots)) {\n const spec = def?.slots?.find(s => s.name === slotName);\n for (const child of children) {\n if (spec?.allowedBlocks && !spec.allowedBlocks.includes(child.type)) {\n return `${child.type} is not allowed in slot \"${slotName}\" of ${n.type}`;\n }\n const e = check(child, depth + 1);\n if (e) return e;\n }\n }\n }\n\n if (def?.validate) {\n const r = def.validate(n);\n if (r !== true) return r;\n }\n return null;\n };\n\n return check(d.root, 0) ?? true;\n}\n","import { json } from \"nextly/config\";\n\nimport { defaultBlockRegistry, validateDocument } from \"../core\";\n\n/** String path the host admin resolves to our field editor (registered on `./admin`). */\nexport const FIELD_COMPONENT_PATH =\n \"@nextlyhq/plugin-page-builder/admin#PageBuilderField\";\n\nexport interface PageBuilderFieldOptions {\n /** Admin label for the field editor. */\n label?: string;\n /**\n * Show the editor only when a sibling field matches — enables an Elementor-style\n * \"choose your editor\" workflow (e.g. show the builder only when a `mode` select is\n * \"page-builder\"). Maps to the field's `admin.condition`.\n */\n condition?: {\n field: string;\n equals?: unknown;\n notEquals?: unknown;\n exists?: boolean;\n };\n}\n\n/**\n * A page-builder editor as a custom field (spec §9/§11). Stores the BlockDocument as\n * JSON and renders our editor via `admin.component` (D24, the plugin-supplied field\n * editor). Works in BOTH collections and singles — the host form persists the value.\n * Register the component with `@nextlyhq/plugin-page-builder/admin`.\n *\n * Node-side the block registry is empty at config-load (renderers live in `./render`),\n * so the validator runs with `allowUnknown: true` — it still enforces the structural\n * invariants (depth / node count / unique ids / namespaced types).\n */\nexport function pageBuilderField(\n name: string,\n opts: PageBuilderFieldOptions = {}\n) {\n return json({\n name,\n label: opts.label,\n admin: { component: FIELD_COMPONENT_PATH, condition: opts.condition },\n // An unset builder field is valid (e.g. when another editor mode is chosen).\n validate: value =>\n value == null\n ? true\n : validateDocument(value, defaultBlockRegistry, { allowUnknown: true }),\n });\n}\n","import { option, select } from \"nextly/config\";\n\nimport { FIELD_COMPONENT_PATH, pageBuilderField } from \"./pageBuilderField\";\n\n/** Reserved system field holding the BlockDocument JSON (spec §2). */\nexport const PAGE_BUILDER_CONTENT_FIELD = \"content\";\n/** Per-entry editor-choice select field. All-lowercase so it survives the schema builder's\n * field-name normalization (which lowercases), identical to the code-first name. */\nexport const EDITOR_MODE_FIELD = \"editormode\";\n/** Plugin-registered field type id (spec §4.1). */\nexport const PAGE_BUILDER_TYPE = \"page-builder\";\n\nexport type EditorMode = \"default\" | \"builder\";\n\n/**\n * @deprecated The editor-choice is now signalled purely by the presence of the\n * `page-builder` field (a `layout: \"takeover\"` field type) — no collection-level\n * flag is read anymore. Retained only for back-compat of existing type imports.\n */\nexport interface PageBuilderAdminConfig {\n enabled?: boolean;\n defaultMode?: EditorMode;\n}\n\n/**\n * Field-type descriptor registered via the plugin's `contributes.fieldTypes`.\n * `layout: \"takeover\"` tells the admin entry form to show only this field (plus\n * its editor-mode controller) when the field is visible — hiding the rest.\n */\nexport const PAGE_BUILDER_FIELD_TYPE = {\n type: PAGE_BUILDER_TYPE,\n storage: \"json\",\n component: FIELD_COMPONENT_PATH,\n layout: \"takeover\",\n} as const;\n\n/**\n * The two fields an entity needs to offer the Default / Page Builder choice: an `editorMode`\n * select and the reserved `content` page-builder field (shown only in builder mode). The\n * entity's OWN fields serve as the \"Default\" editor; the admin hides the non-essential ones\n * in builder mode (a later stage). Works in both `defineCollection` and `defineSingle`.\n */\nexport function pageBuilderFields(opts: { defaultMode?: EditorMode } = {}) {\n const defaultMode: EditorMode = opts.defaultMode ?? \"default\";\n return [\n select({\n // All-lowercase so the name is identical for code-first AND UI-created collections\n // (the schema builder lowercases field names); keeps the condition/detection stable.\n name: EDITOR_MODE_FIELD,\n label: \"Editor\",\n defaultValue: defaultMode,\n options: [\n option(\"Default\", \"default\"),\n option(\"Page Builder\", \"builder\"),\n ],\n // Hidden field: it stores the per-entry mode but never renders as a field.\n // The choice is surfaced as a toolbar toggle (contributes.admin\n // .entryFormToolbarSlot) and it's kept out of the schema-builder field list.\n admin: { hidden: true, description: \"Choose how to edit this entry.\" },\n }),\n pageBuilderField(PAGE_BUILDER_CONTENT_FIELD, {\n label: \"Page Builder\",\n condition: { field: EDITOR_MODE_FIELD, equals: \"builder\" },\n }),\n ];\n}\n\n/**\n * Opt a CODE-FIRST collection/single config into the Page Builder editor choice:\n * appends `pageBuilderFields()`. The presence of the `page-builder` field is the\n * signal — no collection-level flag needed. Wrap the config passed to\n * `defineCollection`/`defineSingle`:\n *\n * ```ts\n * defineCollection(withPageBuilder({ slug: \"landing\", fields: [text({ name: \"title\" })] }));\n * ```\n */\nexport function withPageBuilder<T extends { fields?: unknown[] }>(\n config: T,\n opts: { defaultMode?: EditorMode } = {}\n): T {\n const defaultMode: EditorMode = opts.defaultMode ?? \"default\";\n return {\n ...config,\n fields: [...(config.fields ?? []), ...pageBuilderFields({ defaultMode })],\n };\n}\n"],"mappings":"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","names":[]}