@particle-academy/fancy-cms-ui 0.3.1 → 0.5.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/dist/{chunk-6Q7DKNDZ.js → chunk-5VNXIAHG.js} +10 -54
- package/dist/chunk-5VNXIAHG.js.map +1 -0
- package/dist/{chunk-GOXVGEKU.js → chunk-FDCP3LVR.js} +78 -49
- package/dist/chunk-FDCP3LVR.js.map +1 -0
- package/dist/{chunk-QYNWSECG.js → chunk-Z2X7B6YL.js} +3 -3
- package/dist/{chunk-QYNWSECG.js.map → chunk-Z2X7B6YL.js.map} +1 -1
- package/dist/editor.cjs +85 -111
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +3 -2
- package/dist/editor.d.ts +3 -2
- package/dist/editor.js +22 -23
- package/dist/editor.js.map +1 -1
- package/dist/index.cjs +87 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -35
- package/dist/index.d.ts +23 -35
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +46 -12
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +5 -4
- package/dist/react.d.ts +5 -4
- package/dist/react.js +2 -2
- package/dist/{registry-D5y5IgCR.d.cts → registry-CvzbUd5E.d.cts} +89 -12
- package/dist/{registry-D5y5IgCR.d.ts → registry-CvzbUd5E.d.ts} +89 -12
- package/dist/{useEditor-BQtAHLHl.d.cts → useEditor-BGKnLQuK.d.ts} +13 -3
- package/dist/{useEditor-CpX-LbfH.d.ts → useEditor-Dr_Gg7gu.d.cts} +13 -3
- package/package.json +14 -8
- package/dist/chunk-6Q7DKNDZ.js.map +0 -1
- package/dist/chunk-GOXVGEKU.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
+
import { DocNode } from '@particle-academy/fancy-doc-commons';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Stages document model — see `fancy-ui/docs/fancy-cms.md` §4.
|
|
@@ -8,6 +9,8 @@ import { ReactNode } from 'react';
|
|
|
8
9
|
* containers where structure is wanted. Nodes live in a **flat map** keyed by id
|
|
9
10
|
* (Figma-style) so ops target a stable identity and collab merges stay clean.
|
|
10
11
|
*/
|
|
12
|
+
|
|
13
|
+
/** A node id. Alias of doc-commons' `DocId`, kept for readability in CMS code. */
|
|
11
14
|
type NodeId = string;
|
|
12
15
|
type ScrollMode = "snap" | "smooth";
|
|
13
16
|
/** Ordered, mobile-first. Convention: `"base" | "md" | "lg"`. */
|
|
@@ -122,16 +125,18 @@ interface Action {
|
|
|
122
125
|
on: ActionEvent;
|
|
123
126
|
do: ActionHandler;
|
|
124
127
|
}
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
/**
|
|
129
|
+
* A page node.
|
|
130
|
+
*
|
|
131
|
+
* Extends {@link DocNode} from `fancy-doc-commons` rather than redeclaring it:
|
|
132
|
+
* id / type / parent / order / props are the shared substrate every Fancy
|
|
133
|
+
* document surface uses, and the CMS adds its own domain fields on top. That
|
|
134
|
+
* is what lets `registerDocBridge` drive a CMS page and a fancy-screens screen
|
|
135
|
+
* through the same tools, with no CMS-specific bridge.
|
|
136
|
+
*/
|
|
137
|
+
interface Node extends DocNode<Record<string, Bound<Json>>> {
|
|
127
138
|
/** Addon key: `section | text | image | shape | stack | grid | frame | <addon>`. */
|
|
128
139
|
type: string;
|
|
129
|
-
/** `null` = a top-level section. */
|
|
130
|
-
parent: NodeId | null;
|
|
131
|
-
/** Fractional index — collab-safe ordering among siblings. */
|
|
132
|
-
order: string;
|
|
133
|
-
/** Addon-specific props; any value may be a {@link Binding}. */
|
|
134
|
-
props: Record<string, Bound<Json>>;
|
|
135
140
|
/** How this node arranges its children. */
|
|
136
141
|
layout?: LayoutMode;
|
|
137
142
|
/** How this node positions itself within its parent. */
|
|
@@ -171,9 +176,17 @@ interface PageDoc {
|
|
|
171
176
|
theme: ThemeTokens;
|
|
172
177
|
/** Ordered, mobile-first. */
|
|
173
178
|
breakpoints: Breakpoint[];
|
|
174
|
-
/**
|
|
175
|
-
|
|
176
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Flat map — not a nested tree. This is a {@link DocTree}, so every
|
|
181
|
+
* doc-commons walk (`childrenOf`, `roots`, `descendantsOf`) works on it
|
|
182
|
+
* directly.
|
|
183
|
+
*
|
|
184
|
+
* There is no separate `sections[]` any more. Top-level order used to live in
|
|
185
|
+
* its own array while every other level used fractional `order` keys, which
|
|
186
|
+
* meant two ordering mechanisms that could disagree — a node could be a root
|
|
187
|
+
* by `parent === null` and absent from `sections`, and then simply not
|
|
188
|
+
* render. Roots are now ordered exactly like any other sibling group.
|
|
189
|
+
*/
|
|
177
190
|
nodes: Record<NodeId, Node>;
|
|
178
191
|
}
|
|
179
192
|
/** Default breakpoint → min-width (px) map. `base` has no media query. */
|
|
@@ -181,6 +194,70 @@ declare const DEFAULT_BREAKPOINTS: Record<string, number>;
|
|
|
181
194
|
/** A minimal empty document. */
|
|
182
195
|
declare function emptyDoc(id: string, slug?: string): PageDoc;
|
|
183
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Forward migration for documents saved before roots were ordered like every
|
|
199
|
+
* other sibling group.
|
|
200
|
+
*
|
|
201
|
+
* ## Why this cannot be skipped
|
|
202
|
+
*
|
|
203
|
+
* The old model carried top-level order TWICE: a `sections: NodeId[]` array, and
|
|
204
|
+
* an `order` key on every node like any other sibling. The old `reorder_sections`
|
|
205
|
+
* op permuted the array and **left the order keys untouched** — so the moment a
|
|
206
|
+
* user dragged a section, the two disagreed, and `sections[]` was the one that
|
|
207
|
+
* won (it was what the renderer walked).
|
|
208
|
+
*
|
|
209
|
+
* Which means: dropping `sections[]` and falling back to the order keys does not
|
|
210
|
+
* merely lose a redundant field. On every page whose sections were ever
|
|
211
|
+
* rearranged, it silently reverts them to the order they were first created in.
|
|
212
|
+
* The page still loads, nothing throws, and the layout is wrong.
|
|
213
|
+
*
|
|
214
|
+
* So the migration rewrites root `order` keys to agree with `sections[]` before
|
|
215
|
+
* the array is discarded. `sections[]` is treated as authoritative for exactly
|
|
216
|
+
* the reason above.
|
|
217
|
+
*
|
|
218
|
+
* ## Idempotent, and safe on already-migrated documents
|
|
219
|
+
*
|
|
220
|
+
* A doc with no `sections` is returned unchanged (same reference). Running this
|
|
221
|
+
* twice is a no-op, so a host can call it unconditionally on load rather than
|
|
222
|
+
* tracking a schema version.
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
/** A `PageDoc` as persisted before this change — with the extra array. */
|
|
226
|
+
type LegacyPageDoc = PageDoc & {
|
|
227
|
+
sections?: NodeId[];
|
|
228
|
+
};
|
|
229
|
+
interface MigrateResult {
|
|
230
|
+
doc: PageDoc;
|
|
231
|
+
/** True when the input actually carried `sections[]` and was rewritten. */
|
|
232
|
+
migrated: boolean;
|
|
233
|
+
/**
|
|
234
|
+
* Ids listed in `sections[]` with no matching node. Skipped rather than
|
|
235
|
+
* faulted — a dangling id is a stale write, and refusing to open the document
|
|
236
|
+
* over it would be a worse outcome than dropping it.
|
|
237
|
+
*/
|
|
238
|
+
danglingIds: NodeId[];
|
|
239
|
+
/**
|
|
240
|
+
* Roots present in `nodes` but missing from `sections[]`. Under the old model
|
|
241
|
+
* these did not render at all. They are appended after the listed sections, in
|
|
242
|
+
* their existing order-key order, because making a node visible is recoverable
|
|
243
|
+
* and deleting one is not.
|
|
244
|
+
*/
|
|
245
|
+
unlistedRootIds: NodeId[];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Migrate a possibly-legacy document, reporting what was found.
|
|
249
|
+
*
|
|
250
|
+
* Use {@link migrateDoc} unless you want the diagnostics.
|
|
251
|
+
*/
|
|
252
|
+
declare function migrateDocVerbose(input: LegacyPageDoc | PageDoc): MigrateResult;
|
|
253
|
+
/**
|
|
254
|
+
* Migrate a possibly-legacy document. Safe to call on every load — an
|
|
255
|
+
* already-migrated document is returned unchanged, by reference.
|
|
256
|
+
*/
|
|
257
|
+
declare function migrateDoc(input: LegacyPageDoc | PageDoc): PageDoc;
|
|
258
|
+
/** True when the document still carries the pre-migration `sections[]` array. */
|
|
259
|
+
declare function needsMigration(input: LegacyPageDoc | PageDoc): boolean;
|
|
260
|
+
|
|
184
261
|
/** The page data context bindings resolve against (e.g. server props, repeater `item`). */
|
|
185
262
|
type DataContext = Record<string, unknown>;
|
|
186
263
|
interface ElementContext {
|
|
@@ -208,4 +285,4 @@ declare function resolveValue(v: Bound<Json> | undefined, data?: DataContext): u
|
|
|
208
285
|
/** Built-in native elements. Containers render their children; leaves render content. */
|
|
209
286
|
declare const defaultRegistry: ElementRegistry;
|
|
210
287
|
|
|
211
|
-
export { type Action as A, type Binding as B, type Constraints as C, DEFAULT_BREAKPOINTS as D, type ElementContext as E, type Json as J, type Keyframe as K, type LayoutMode as L, type Node as N, type PageDoc as P, type ScrollMode as S, type ThemeTokens as T, type ActionEvent as a, type ActionHandler as b, type Anim as c, type AnimDriver as d, type Bound as e, type Breakpoint as f, type DataContext as g, type ElementRegistry as h, type ElementRenderer as i, type
|
|
288
|
+
export { type Action as A, type Binding as B, type Constraints as C, DEFAULT_BREAKPOINTS as D, type ElementContext as E, type Json as J, type Keyframe as K, type LayoutMode as L, type MigrateResult as M, type Node as N, type PageDoc as P, type ScrollMode as S, type ThemeTokens as T, type ActionEvent as a, type ActionHandler as b, type Anim as c, type AnimDriver as d, type Bound as e, type Breakpoint as f, type DataContext as g, type ElementRegistry as h, type ElementRenderer as i, type LegacyPageDoc as j, type Length as k, type LengthUnit as l, type NodeId as m, type PageMeta as n, type PerBreakpoint as o, type SizeMode as p, type StyleProps as q, defaultRegistry as r, emptyDoc as s, getPath as t, isBinding as u, migrateDoc as v, migrateDocVerbose as w, needsMigration as x, resolveValue as y };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
+
import { DocNode } from '@particle-academy/fancy-doc-commons';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Stages document model — see `fancy-ui/docs/fancy-cms.md` §4.
|
|
@@ -8,6 +9,8 @@ import { ReactNode } from 'react';
|
|
|
8
9
|
* containers where structure is wanted. Nodes live in a **flat map** keyed by id
|
|
9
10
|
* (Figma-style) so ops target a stable identity and collab merges stay clean.
|
|
10
11
|
*/
|
|
12
|
+
|
|
13
|
+
/** A node id. Alias of doc-commons' `DocId`, kept for readability in CMS code. */
|
|
11
14
|
type NodeId = string;
|
|
12
15
|
type ScrollMode = "snap" | "smooth";
|
|
13
16
|
/** Ordered, mobile-first. Convention: `"base" | "md" | "lg"`. */
|
|
@@ -122,16 +125,18 @@ interface Action {
|
|
|
122
125
|
on: ActionEvent;
|
|
123
126
|
do: ActionHandler;
|
|
124
127
|
}
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
/**
|
|
129
|
+
* A page node.
|
|
130
|
+
*
|
|
131
|
+
* Extends {@link DocNode} from `fancy-doc-commons` rather than redeclaring it:
|
|
132
|
+
* id / type / parent / order / props are the shared substrate every Fancy
|
|
133
|
+
* document surface uses, and the CMS adds its own domain fields on top. That
|
|
134
|
+
* is what lets `registerDocBridge` drive a CMS page and a fancy-screens screen
|
|
135
|
+
* through the same tools, with no CMS-specific bridge.
|
|
136
|
+
*/
|
|
137
|
+
interface Node extends DocNode<Record<string, Bound<Json>>> {
|
|
127
138
|
/** Addon key: `section | text | image | shape | stack | grid | frame | <addon>`. */
|
|
128
139
|
type: string;
|
|
129
|
-
/** `null` = a top-level section. */
|
|
130
|
-
parent: NodeId | null;
|
|
131
|
-
/** Fractional index — collab-safe ordering among siblings. */
|
|
132
|
-
order: string;
|
|
133
|
-
/** Addon-specific props; any value may be a {@link Binding}. */
|
|
134
|
-
props: Record<string, Bound<Json>>;
|
|
135
140
|
/** How this node arranges its children. */
|
|
136
141
|
layout?: LayoutMode;
|
|
137
142
|
/** How this node positions itself within its parent. */
|
|
@@ -171,9 +176,17 @@ interface PageDoc {
|
|
|
171
176
|
theme: ThemeTokens;
|
|
172
177
|
/** Ordered, mobile-first. */
|
|
173
178
|
breakpoints: Breakpoint[];
|
|
174
|
-
/**
|
|
175
|
-
|
|
176
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Flat map — not a nested tree. This is a {@link DocTree}, so every
|
|
181
|
+
* doc-commons walk (`childrenOf`, `roots`, `descendantsOf`) works on it
|
|
182
|
+
* directly.
|
|
183
|
+
*
|
|
184
|
+
* There is no separate `sections[]` any more. Top-level order used to live in
|
|
185
|
+
* its own array while every other level used fractional `order` keys, which
|
|
186
|
+
* meant two ordering mechanisms that could disagree — a node could be a root
|
|
187
|
+
* by `parent === null` and absent from `sections`, and then simply not
|
|
188
|
+
* render. Roots are now ordered exactly like any other sibling group.
|
|
189
|
+
*/
|
|
177
190
|
nodes: Record<NodeId, Node>;
|
|
178
191
|
}
|
|
179
192
|
/** Default breakpoint → min-width (px) map. `base` has no media query. */
|
|
@@ -181,6 +194,70 @@ declare const DEFAULT_BREAKPOINTS: Record<string, number>;
|
|
|
181
194
|
/** A minimal empty document. */
|
|
182
195
|
declare function emptyDoc(id: string, slug?: string): PageDoc;
|
|
183
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Forward migration for documents saved before roots were ordered like every
|
|
199
|
+
* other sibling group.
|
|
200
|
+
*
|
|
201
|
+
* ## Why this cannot be skipped
|
|
202
|
+
*
|
|
203
|
+
* The old model carried top-level order TWICE: a `sections: NodeId[]` array, and
|
|
204
|
+
* an `order` key on every node like any other sibling. The old `reorder_sections`
|
|
205
|
+
* op permuted the array and **left the order keys untouched** — so the moment a
|
|
206
|
+
* user dragged a section, the two disagreed, and `sections[]` was the one that
|
|
207
|
+
* won (it was what the renderer walked).
|
|
208
|
+
*
|
|
209
|
+
* Which means: dropping `sections[]` and falling back to the order keys does not
|
|
210
|
+
* merely lose a redundant field. On every page whose sections were ever
|
|
211
|
+
* rearranged, it silently reverts them to the order they were first created in.
|
|
212
|
+
* The page still loads, nothing throws, and the layout is wrong.
|
|
213
|
+
*
|
|
214
|
+
* So the migration rewrites root `order` keys to agree with `sections[]` before
|
|
215
|
+
* the array is discarded. `sections[]` is treated as authoritative for exactly
|
|
216
|
+
* the reason above.
|
|
217
|
+
*
|
|
218
|
+
* ## Idempotent, and safe on already-migrated documents
|
|
219
|
+
*
|
|
220
|
+
* A doc with no `sections` is returned unchanged (same reference). Running this
|
|
221
|
+
* twice is a no-op, so a host can call it unconditionally on load rather than
|
|
222
|
+
* tracking a schema version.
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
/** A `PageDoc` as persisted before this change — with the extra array. */
|
|
226
|
+
type LegacyPageDoc = PageDoc & {
|
|
227
|
+
sections?: NodeId[];
|
|
228
|
+
};
|
|
229
|
+
interface MigrateResult {
|
|
230
|
+
doc: PageDoc;
|
|
231
|
+
/** True when the input actually carried `sections[]` and was rewritten. */
|
|
232
|
+
migrated: boolean;
|
|
233
|
+
/**
|
|
234
|
+
* Ids listed in `sections[]` with no matching node. Skipped rather than
|
|
235
|
+
* faulted — a dangling id is a stale write, and refusing to open the document
|
|
236
|
+
* over it would be a worse outcome than dropping it.
|
|
237
|
+
*/
|
|
238
|
+
danglingIds: NodeId[];
|
|
239
|
+
/**
|
|
240
|
+
* Roots present in `nodes` but missing from `sections[]`. Under the old model
|
|
241
|
+
* these did not render at all. They are appended after the listed sections, in
|
|
242
|
+
* their existing order-key order, because making a node visible is recoverable
|
|
243
|
+
* and deleting one is not.
|
|
244
|
+
*/
|
|
245
|
+
unlistedRootIds: NodeId[];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Migrate a possibly-legacy document, reporting what was found.
|
|
249
|
+
*
|
|
250
|
+
* Use {@link migrateDoc} unless you want the diagnostics.
|
|
251
|
+
*/
|
|
252
|
+
declare function migrateDocVerbose(input: LegacyPageDoc | PageDoc): MigrateResult;
|
|
253
|
+
/**
|
|
254
|
+
* Migrate a possibly-legacy document. Safe to call on every load — an
|
|
255
|
+
* already-migrated document is returned unchanged, by reference.
|
|
256
|
+
*/
|
|
257
|
+
declare function migrateDoc(input: LegacyPageDoc | PageDoc): PageDoc;
|
|
258
|
+
/** True when the document still carries the pre-migration `sections[]` array. */
|
|
259
|
+
declare function needsMigration(input: LegacyPageDoc | PageDoc): boolean;
|
|
260
|
+
|
|
184
261
|
/** The page data context bindings resolve against (e.g. server props, repeater `item`). */
|
|
185
262
|
type DataContext = Record<string, unknown>;
|
|
186
263
|
interface ElementContext {
|
|
@@ -208,4 +285,4 @@ declare function resolveValue(v: Bound<Json> | undefined, data?: DataContext): u
|
|
|
208
285
|
/** Built-in native elements. Containers render their children; leaves render content. */
|
|
209
286
|
declare const defaultRegistry: ElementRegistry;
|
|
210
287
|
|
|
211
|
-
export { type Action as A, type Binding as B, type Constraints as C, DEFAULT_BREAKPOINTS as D, type ElementContext as E, type Json as J, type Keyframe as K, type LayoutMode as L, type Node as N, type PageDoc as P, type ScrollMode as S, type ThemeTokens as T, type ActionEvent as a, type ActionHandler as b, type Anim as c, type AnimDriver as d, type Bound as e, type Breakpoint as f, type DataContext as g, type ElementRegistry as h, type ElementRenderer as i, type
|
|
288
|
+
export { type Action as A, type Binding as B, type Constraints as C, DEFAULT_BREAKPOINTS as D, type ElementContext as E, type Json as J, type Keyframe as K, type LayoutMode as L, type MigrateResult as M, type Node as N, type PageDoc as P, type ScrollMode as S, type ThemeTokens as T, type ActionEvent as a, type ActionHandler as b, type Anim as c, type AnimDriver as d, type Bound as e, type Breakpoint as f, type DataContext as g, type ElementRegistry as h, type ElementRenderer as i, type LegacyPageDoc as j, type Length as k, type LengthUnit as l, type NodeId as m, type PageMeta as n, type PerBreakpoint as o, type SizeMode as p, type StyleProps as q, defaultRegistry as r, emptyDoc as s, getPath as t, isBinding as u, migrateDoc as v, migrateDocVerbose as w, needsMigration as x, resolveValue as y };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
|
-
import { N as Node,
|
|
2
|
+
import { N as Node, m as NodeId, L as LayoutMode, f as Breakpoint, q as StyleProps, C as Constraints, c as Anim, A as Action, n as PageMeta, T as ThemeTokens, P as PageDoc, h as ElementRegistry, g as DataContext, j as LegacyPageDoc } from './registry-CvzbUd5E.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* `PageOp` — the closed set of mutations. Every edit (human drag, agent tool
|
|
@@ -60,7 +60,7 @@ type PageOp = {
|
|
|
60
60
|
t: "set_theme";
|
|
61
61
|
patch: Partial<ThemeTokens>;
|
|
62
62
|
} | {
|
|
63
|
-
t: "
|
|
63
|
+
t: "reorder_roots";
|
|
64
64
|
order: NodeId[];
|
|
65
65
|
};
|
|
66
66
|
type PageOpType = PageOp["t"];
|
|
@@ -179,7 +179,17 @@ type EditorAction = {
|
|
|
179
179
|
type: "replace";
|
|
180
180
|
doc: PageDoc;
|
|
181
181
|
};
|
|
182
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Seed the editor from a document.
|
|
184
|
+
*
|
|
185
|
+
* Migrates a pre-`sections[]` document on the way in. Doing it here rather than
|
|
186
|
+
* leaving it to the host is deliberate: the legacy failure is SILENT — a page
|
|
187
|
+
* whose sections were ever reordered would simply open in the wrong order, with
|
|
188
|
+
* nothing thrown and nothing logged — so a host that has not heard of the
|
|
189
|
+
* migration is exactly the one that needs it. Already-migrated documents pass
|
|
190
|
+
* through by reference.
|
|
191
|
+
*/
|
|
192
|
+
declare function initEditor(doc: LegacyPageDoc | PageDoc): EditorState;
|
|
183
193
|
declare function editorReduce(state: EditorState, action: EditorAction): EditorState;
|
|
184
194
|
|
|
185
195
|
interface EditorApi {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
|
-
import { N as Node,
|
|
2
|
+
import { N as Node, m as NodeId, L as LayoutMode, f as Breakpoint, q as StyleProps, C as Constraints, c as Anim, A as Action, n as PageMeta, T as ThemeTokens, P as PageDoc, h as ElementRegistry, g as DataContext, j as LegacyPageDoc } from './registry-CvzbUd5E.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* `PageOp` — the closed set of mutations. Every edit (human drag, agent tool
|
|
@@ -60,7 +60,7 @@ type PageOp = {
|
|
|
60
60
|
t: "set_theme";
|
|
61
61
|
patch: Partial<ThemeTokens>;
|
|
62
62
|
} | {
|
|
63
|
-
t: "
|
|
63
|
+
t: "reorder_roots";
|
|
64
64
|
order: NodeId[];
|
|
65
65
|
};
|
|
66
66
|
type PageOpType = PageOp["t"];
|
|
@@ -179,7 +179,17 @@ type EditorAction = {
|
|
|
179
179
|
type: "replace";
|
|
180
180
|
doc: PageDoc;
|
|
181
181
|
};
|
|
182
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Seed the editor from a document.
|
|
184
|
+
*
|
|
185
|
+
* Migrates a pre-`sections[]` document on the way in. Doing it here rather than
|
|
186
|
+
* leaving it to the host is deliberate: the legacy failure is SILENT — a page
|
|
187
|
+
* whose sections were ever reordered would simply open in the wrong order, with
|
|
188
|
+
* nothing thrown and nothing logged — so a host that has not heard of the
|
|
189
|
+
* migration is exactly the one that needs it. Already-migrated documents pass
|
|
190
|
+
* through by reference.
|
|
191
|
+
*/
|
|
192
|
+
declare function initEditor(doc: LegacyPageDoc | PageDoc): EditorState;
|
|
183
193
|
declare function editorReduce(state: EditorState, action: EditorAction): EditorState;
|
|
184
194
|
|
|
185
195
|
interface EditorApi {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@particle-academy/fancy-cms-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Editor + isomorphic renderer for fancy-cms — the Stages document model, PageOp op-spine, the JS style→CSS emitter (one half of the dual-emitter pair with the PHP particle-academy/fancy-cms), and the React renderer. Built on the Fancy UI suite.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "tsup",
|
|
55
55
|
"dev": "tsup --watch",
|
|
56
|
-
"lint": "tsc --noEmit",
|
|
56
|
+
"lint": "tsc --noEmit && eslint .",
|
|
57
57
|
"test": "vitest run",
|
|
58
58
|
"test:watch": "vitest",
|
|
59
59
|
"parity": "node parity/run.mjs",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
],
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"@particle-academy/react-fancy": ">=3",
|
|
75
|
-
"react": "^
|
|
75
|
+
"react": "^19.0.0"
|
|
76
76
|
},
|
|
77
77
|
"peerDependenciesMeta": {
|
|
78
78
|
"@particle-academy/react-fancy": {
|
|
@@ -82,17 +82,20 @@
|
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@particle-academy/react-fancy": "^4.10.0",
|
|
84
84
|
"@types/node": "^20.0.0",
|
|
85
|
-
"@types/react": "^19.
|
|
86
|
-
"@types/react-dom": "^19.
|
|
85
|
+
"@types/react": "^19.2.18",
|
|
86
|
+
"@types/react-dom": "^19.2.4",
|
|
87
|
+
"eslint": "^10.8.0",
|
|
88
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
87
89
|
"jsdom": "^29.1.1",
|
|
88
|
-
"react": "^19.
|
|
89
|
-
"react-dom": "^19.
|
|
90
|
+
"react": "^19.2.8",
|
|
91
|
+
"react-dom": "^19.2.8",
|
|
90
92
|
"tsup": "^8.5.0",
|
|
91
93
|
"typescript": "^5.8.0",
|
|
94
|
+
"typescript-eslint": "^8.65.0",
|
|
92
95
|
"vitest": "^4.1.8"
|
|
93
96
|
},
|
|
94
97
|
"engines": {
|
|
95
|
-
"node": ">=
|
|
98
|
+
"node": ">=22"
|
|
96
99
|
},
|
|
97
100
|
"publishConfig": {
|
|
98
101
|
"access": "public"
|
|
@@ -100,5 +103,8 @@
|
|
|
100
103
|
"license": "MIT",
|
|
101
104
|
"overrides": {
|
|
102
105
|
"esbuild": "^0.28.1"
|
|
106
|
+
},
|
|
107
|
+
"dependencies": {
|
|
108
|
+
"@particle-academy/fancy-doc-commons": ">=0.2 <2.0"
|
|
103
109
|
}
|
|
104
110
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/document/fractional.ts","../src/editor/state.ts","../src/editor/useEditor.ts","../src/editor/Canvas.tsx","../src/editor/LayersPanel.tsx","../src/editor/Inspector.tsx","../src/editor/insert.ts","../src/editor/useLatestRef.ts","../src/editor/Editor.tsx"],"names":["useState","jsx","jsxs","Fragment","useRef","useMemo","useEffect"],"mappings":";;;;;AAaA,IAAM,IAAA,GAAO,gEAAA;AACb,IAAM,QAAQ,IAAA,CAAK,MAAA;AACnB,IAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,CAAC,CAAA;AAEhC,SAAS,IAAI,CAAA,EAAmB;AAC9B,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AACxB,EAAA,OAAO,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA;AACrB;AAOO,SAAS,UAAA,CAAW,GAAkB,CAAA,EAA0B;AACrE,EAAA,IAAI,CAAA,KAAM,MAAM,CAAA,GAAI,EAAA;AACpB,EAAA,IAAI,MAAM,IAAA,EAAM;AAEd,IAAA,OAAO,CAAA,GAAI,KAAK,GAAG,CAAA;AAAA,EACrB;AACA,EAAA,IAAI,KAAK,CAAA,EAAG;AACV,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,yBAAA,EAA4B,CAAC,CAAA,4BAAA,EAA+B,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,EAClF;AAEA,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,IAAI,CAAA,GAAI,CAAA;AAER,EAAA,WAAS;AACP,IAAA,MAAM,EAAA,GAAK,IAAI,CAAA,CAAE,MAAA,GAAS,IAAI,CAAA,CAAE,CAAC,CAAW,CAAA,GAAI,CAAA;AAChD,IAAA,MAAM,EAAA,GAAK,IAAI,CAAA,CAAE,MAAA,GAAS,IAAI,CAAA,CAAE,CAAC,CAAW,CAAA,GAAI,KAAA;AAChD,IAAA,IAAI,EAAA,GAAK,KAAK,CAAA,EAAG;AACf,MAAA,MAAA,IAAU,KAAK,EAAA,GAAK,IAAA,CAAK,OAAO,EAAA,GAAK,EAAA,IAAM,CAAC,CAAC,CAAA;AAC7C,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,IAAI,OAAO,EAAA,EAAI;AACb,MAAA,MAAA,IAAU,KAAK,EAAE,CAAA;AACjB,MAAA,CAAA,EAAA;AACA,MAAA;AAAA,IACF;AAGA,IAAA,MAAA,IAAU,KAAK,EAAE,CAAA;AACjB,IAAA,CAAA,EAAA;AACA,IAAA,WAAS;AACP,MAAA,MAAM,EAAA,GAAK,IAAI,CAAA,CAAE,MAAA,GAAS,IAAI,CAAA,CAAE,CAAC,CAAW,CAAA,GAAI,CAAA;AAChD,MAAA,IAAI,KAAA,GAAQ,KAAK,CAAA,EAAG;AAClB,QAAA,MAAA,IAAU,KAAK,EAAA,GAAK,IAAA,CAAK,OAAO,KAAA,GAAQ,EAAA,IAAM,CAAC,CAAC,CAAA;AAChD,QAAA,OAAO,MAAA;AAAA,MACT;AACA,MAAA,MAAA,IAAU,KAAK,EAAE,CAAA;AACjB,MAAA,CAAA,EAAA;AAAA,IACF;AAAA,EACF;AACF;ACxCO,SAAS,WAAW,GAAA,EAA2B;AACpD,EAAA,OAAO,EAAE,KAAK,IAAA,EAAM,IAAI,MAAA,EAAQ,EAAC,EAAG,SAAA,EAAW,IAAA,EAAK;AACtD;AAEO,SAAS,YAAA,CAAa,OAAoB,MAAA,EAAmC;AAClF,EAAA,QAAQ,OAAO,IAAA;AAAM,IACnB,KAAK,OAAA,EAAS;AACZ,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,KAAA,CAAM,GAAA,EAAK,OAAO,EAAE,CAAA;AACxC,MAAA,IAAI,IAAA,KAAS,KAAA,CAAM,GAAA,EAAK,OAAO,KAAA;AAC/B,MAAA,OAAO;AAAA,QACL,GAAG,KAAA;AAAA,QACH,GAAA,EAAK,IAAA;AAAA,QACL,IAAA,EAAM,CAAC,GAAG,KAAA,CAAM,IAAA,EAAM,MAAM,GAAG,CAAA,CAAE,KAAA,CAAM,IAAc,CAAA;AAAA,QACrD,QAAQ;AAAC,OACX;AAAA,IACF;AAAA,IACA,KAAK,MAAA,EAAQ;AACX,MAAA,IAAI,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA;AACpC,MAAA,MAAM,OAAO,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,SAAS,CAAC,CAAA;AAC7C,MAAA,OAAO;AAAA,QACL,GAAG,KAAA;AAAA,QACH,GAAA,EAAK,IAAA;AAAA,QACL,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,GAAG,EAAE,CAAA;AAAA,QAC5B,QAAQ,CAAC,KAAA,CAAM,GAAA,EAAK,GAAG,MAAM,MAAM;AAAA,OACrC;AAAA,IACF;AAAA,IACA,KAAK,MAAA,EAAQ;AACX,MAAA,IAAI,KAAA,CAAM,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA;AACtC,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA;AAC3B,MAAA,OAAO;AAAA,QACL,GAAG,KAAA;AAAA,QACH,GAAA,EAAK,IAAA;AAAA,QACL,MAAM,CAAC,GAAG,KAAA,CAAM,IAAA,EAAM,MAAM,GAAG,CAAA;AAAA,QAC/B,MAAA,EAAQ,KAAA,CAAM,MAAA,CAAO,KAAA,CAAM,CAAC;AAAA,OAC9B;AAAA,IACF;AAAA,IACA,KAAK,QAAA;AACH,MAAA,OAAO,KAAA,CAAM,SAAA,KAAc,MAAA,CAAO,EAAA,GAAK,KAAA,GAAQ,EAAE,GAAG,KAAA,EAAO,SAAA,EAAW,MAAA,CAAO,EAAA,EAAG;AAAA,IAClF,KAAK,SAAA;AACH,MAAA,OAAO,KAAA,CAAM,GAAA,KAAQ,MAAA,CAAO,GAAA,GAAM,KAAA,GAAQ,EAAE,GAAG,KAAA,EAAO,GAAA,EAAK,MAAA,CAAO,GAAA,EAAK,MAAA,EAAQ,EAAC,EAAE;AAAA,IACpF;AACE,MAAA,OAAO,KAAA;AAAA;AAEb;ACnDO,SAAS,UAAU,UAAA,EAAgC;AACxD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,IAAI,UAAA,CAAW,YAAA,EAAc,YAAY,UAAU,CAAA;AACzE,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,CAAC,EAAA,KAAe,QAAA,CAAS,EAAE,IAAA,EAAM,OAAA,EAAS,EAAA,EAAI,CAAA,EAAG,EAAE,CAAA;AAC7E,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,MAAM,QAAA,CAAS,EAAE,MAAM,MAAA,EAAQ,CAAA,EAAG,EAAE,CAAA;AAC7D,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,MAAM,QAAA,CAAS,EAAE,MAAM,MAAA,EAAQ,CAAA,EAAG,EAAE,CAAA;AAC7D,EAAA,MAAM,MAAA,GAAS,WAAA,CAAY,CAAC,EAAA,KAAsB,QAAA,CAAS,EAAE,IAAA,EAAM,QAAA,EAAU,EAAA,EAAI,CAAA,EAAG,EAAE,CAAA;AACtF,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,CAAC,GAAA,KAAiB,QAAA,CAAS,EAAE,IAAA,EAAM,SAAA,EAAW,GAAA,EAAK,CAAA,EAAG,EAAE,CAAA;AAEpF,EAAA,OAAO,OAAA;AAAA,IACL,OAAO;AAAA,MACL,KAAA;AAAA,MACA,KAAA;AAAA,MACA,IAAA;AAAA,MACA,IAAA;AAAA,MACA,MAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA,EAAS,KAAA,CAAM,IAAA,CAAK,MAAA,GAAS,CAAA;AAAA,MAC7B,OAAA,EAAS,KAAA,CAAM,MAAA,CAAO,MAAA,GAAS;AAAA,KACjC,CAAA;AAAA,IACA,CAAC,KAAA,EAAO,KAAA,EAAO,IAAA,EAAM,IAAA,EAAM,QAAQ,OAAO;AAAA,GAC5C;AACF;ACFO,SAAS,MAAA,CAAO,EAAE,GAAA,EAAK,SAAA,EAAW,UAAU,KAAA,EAAO,QAAA,EAAU,MAAK,EAA8B;AACrG,EAAA,MAAM,GAAA,GAAM,OAAuB,IAAI,CAAA;AACvC,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAI,SAAqB,IAAI,CAAA;AAC/C,EAAA,MAAM,IAAA,GAAO,OAAmE,IAAI,CAAA;AAEpF,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,MAAM,OAAO,GAAA,CAAI,OAAA;AACjB,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,SAAA,EAAW;AACvB,MAAA,MAAA,CAAO,IAAI,CAAA;AACX,MAAA;AAAA,IACF;AACA,IAAA,MAAM,KAAK,IAAA,CAAK,aAAA,CAAc,cAAc,SAAA,CAAU,SAAS,CAAC,CAAA,EAAA,CAAI,CAAA;AACpE,IAAA,IAAI,CAAC,EAAA,EAAI;AACP,MAAA,MAAA,CAAO,IAAI,CAAA;AACX,MAAA;AAAA,IACF;AACA,IAAA,MAAM,CAAA,GAAI,GAAG,qBAAA,EAAsB;AACnC,IAAA,MAAM,QAAA,GAAW,KAAK,qBAAA,EAAsB;AAC5C,IAAA,MAAA,CAAO;AAAA,MACL,CAAA,EAAG,CAAA,CAAE,IAAA,GAAO,QAAA,CAAS,OAAO,IAAA,CAAK,UAAA;AAAA,MACjC,CAAA,EAAG,CAAA,CAAE,GAAA,GAAM,QAAA,CAAS,MAAM,IAAA,CAAK,SAAA;AAAA,MAC/B,GAAG,CAAA,CAAE,KAAA;AAAA,MACL,GAAG,CAAA,CAAE;AAAA,KACN,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,SAAA,EAAW,GAAG,CAAC,CAAA;AAEnB,EAAA,MAAM,WAAA,GAAc,CAAC,CAAA,KAAuC;AAC1D,IAAA,MAAM,MAAA,GAAU,CAAA,CAAE,MAAA,CAAuB,OAAA,CAAQ,YAAY,CAAA;AAC7D,IAAA,QAAA,CAAS,MAAA,GAAS,MAAA,CAAO,YAAA,CAAa,UAAU,IAAI,IAAI,CAAA;AAAA,EAC1D,CAAA;AAEA,EAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,KAAyC;AAClE,IAAA,IAAI,CAAC,SAAA,EAAW;AAChB,IAAA,MAAM,CAAA,GAAI,GAAA,CAAI,KAAA,CAAM,SAAS,GAAG,WAAA,EAAa,IAAA;AAC7C,IAAA,MAAM,IAAA,GAAO,CAAA,IAAK,CAAA,CAAE,IAAA,IAAQ,OAAO,EAAE,IAAA,KAAS,QAAA,GAAW,CAAA,CAAE,IAAA,CAAK,KAAA,GAAQ,CAAA;AACxE,IAAA,MAAM,GAAA,GAAM,CAAA,IAAK,CAAA,CAAE,GAAA,IAAO,OAAO,EAAE,GAAA,KAAQ,QAAA,GAAW,CAAA,CAAE,GAAA,CAAI,KAAA,GAAQ,CAAA;AACpE,IAAA,IAAA,CAAK,OAAA,GAAU,EAAE,CAAA,EAAG,CAAA,CAAE,SAAS,CAAA,EAAG,CAAA,CAAE,OAAA,EAAS,IAAA,EAAM,GAAA,EAAI;AACvD,IAAA,CAAA,CAAE,aAAA,CAAc,iBAAA,CAAkB,CAAA,CAAE,SAAS,CAAA;AAC7C,IAAA,CAAA,CAAE,eAAA,EAAgB;AAAA,EACpB,CAAA;AAEA,EAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,KAAyC;AAClE,IAAA,MAAM,IAAI,IAAA,CAAK,OAAA;AACf,IAAA,IAAI,CAAC,CAAA,IAAK,CAAC,SAAA,EAAW;AACtB,IAAA,KAAA,CAAM;AAAA,MACJ,CAAA,EAAG,iBAAA;AAAA,MACH,EAAA,EAAI,SAAA;AAAA,MACJ,UAAA,EAAY,MAAA;AAAA,MACZ,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,EAAE,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,IAAA,IAAQ,CAAA,CAAE,OAAA,GAAU,CAAA,CAAE,CAAA,CAAE,CAAA,EAAG,MAAM,IAAA,EAAK;AAAA,QAClE,GAAA,EAAK,EAAE,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,GAAA,IAAO,CAAA,CAAE,OAAA,GAAU,CAAA,CAAE,CAAA,CAAE,CAAA,EAAG,MAAM,IAAA;AAAK;AAClE,KACD,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,kBAAkB,MAAM;AAC5B,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,OAAA,GAAyB;AAAA,IAC7B,QAAA,EAAU,UAAA;AAAA,IACV,QAAA,EAAU,MAAA;AAAA,IACV,MAAA,EAAQ,MAAA;AAAA,IACR,UAAA,EAAY;AAAA,GACd;AACA,EAAA,MAAM,UAAyB,GAAA,GAC3B;AAAA,IACE,QAAA,EAAU,UAAA;AAAA,IACV,MAAM,GAAA,CAAI,CAAA;AAAA,IACV,KAAK,GAAA,CAAI,CAAA;AAAA,IACT,OAAO,GAAA,CAAI,CAAA;AAAA,IACX,QAAQ,GAAA,CAAI,CAAA;AAAA,IACZ,OAAA,EAAS,uCAAA;AAAA,IACT,aAAA,EAAe,EAAA;AAAA,IACf,MAAA,EAAQ,MAAA;AAAA,IACR,WAAA,EAAa;AAAA,GACf,GACA,EAAE,OAAA,EAAS,MAAA,EAAO;AAEtB,EAAA,4BACG,KAAA,EAAA,EAAI,GAAA,EAAU,OAAA,EAAS,WAAA,EAAa,OAAO,OAAA,EAC1C,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,GAAA,EAAU,QAAA,EAAoB,IAAA,EAAY,CAAA;AAAA,oBACnD,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,aAAA,EAAe,iBAAA;AAAA,QACf,aAAA,EAAe,iBAAA;AAAA,QACf,WAAA,EAAa,eAAA;AAAA,QACb,KAAA,EAAO;AAAA;AAAA;AACT,GAAA,EACF,CAAA;AAEJ;AAEA,SAAS,UAAU,KAAA,EAAuB;AACxC,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,QAAA,EAAU,MAAM,CAAA;AACvC;ACtHA,IAAM,IAAA,GAAO,wBAAA;AAGb,SAAS,gBAAA,CAAiB,GAAA,EAAc,EAAA,EAAY,aAAA,EAAgC;AAClF,EAAA,KAAA,IAAS,GAAA,GAAqB,IAAI,GAAA,EAAK,GAAA,GAAM,IAAI,KAAA,CAAM,GAAG,CAAA,EAAG,MAAA,IAAU,IAAA,EAAM;AAC3E,IAAA,IAAI,GAAA,KAAQ,eAAe,OAAO,IAAA;AAAA,EACpC;AACA,EAAA,OAAO,KAAA;AACT;AAGO,SAAS,YAAY,EAAE,GAAA,EAAK,SAAA,EAAW,QAAA,EAAU,QAAO,EAAmC;AAChG,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIA,SAAwB,IAAI,CAAA;AAExD,EAAA,uBACEC,GAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,QAAA,EAAU,MAAA,EAAQ,OAAA,EAAS,CAAA,EAAG,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,uBAAA,IACnE,QAAA,EAAA,GAAA,CAAI,QAAA,CAAS,GAAA,CAAI,CAAC,EAAA,qBACjBA,GAAAA,CAAC,QAAA,EAAA,EAAkB,KAAU,EAAA,EAAQ,KAAA,EAAO,CAAA,EAAG,SAAA,EAAsB,UAAoB,MAAA,EAAgB,MAAA,EAAgB,SAAA,EAAA,EAA1G,EAAgI,CAChJ,CAAA,EACH,CAAA;AAEJ;AAaA,SAAS,QAAA,CAAS,EAAE,GAAA,EAAK,EAAA,EAAI,KAAA,EAAO,WAAW,QAAA,EAAU,MAAA,EAAQ,MAAA,EAAQ,SAAA,EAAU,EAAuC;AACxH,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,KAAA,CAAM,EAAE,CAAA;AACzB,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,MAAM,WAAW,SAAA,KAAc,EAAA;AAC/B,EAAA,MAAM,SAAA,GAAY,QAAQ,MAAM,CAAA;AAEhC,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAsB;AAClC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,IAAA,SAAA,CAAU,IAAI,CAAA;AACd,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,MAAM,MAAA,GAAS,CAAA,CAAE,YAAA,CAAa,OAAA,CAAQ,IAAI,CAAA;AAE1C,IAAA,IAAI,CAAC,UAAU,MAAA,KAAW,EAAA,IAAM,iBAAiB,GAAA,EAAK,EAAA,EAAI,MAAM,CAAA,EAAG;AAEnE,IAAA,MAAM,QAAA,GAAW,UAAA,CAAW,GAAA,EAAK,IAAA,CAAK,MAAM,CAAA;AAC5C,IAAA,MAAM,MAAM,QAAA,CAAS,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,EAAE,CAAA;AACjD,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,GAAA,GAAM,CAAC,CAAA;AAC7B,IAAA,MAAA,CAAO,MAAA,EAAQ,IAAA,CAAK,MAAA,EAAQ,UAAA,CAAW,IAAA,CAAK,OAAO,IAAA,GAAO,IAAA,CAAK,KAAA,GAAQ,IAAI,CAAC,CAAA;AAAA,EAC9E,CAAA;AAEA,EAAA,uBACEC,KAAC,QAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAAA,IAAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,QAAA;AAAA,QACL,SAAA;AAAA,QACA,OAAA,EAAS,MAAM,QAAA,CAAS,EAAE,CAAA;AAAA,QAC1B,WAAA,EAAa,CAAC,CAAA,KAAM;AAClB,UAAA,CAAA,CAAE,YAAA,CAAa,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAA;AAC/B,UAAA,CAAA,CAAE,aAAa,aAAA,GAAgB,MAAA;AAAA,QACjC,CAAA;AAAA,QACA,UAAA,EAAY,CAAC,CAAA,KAAM;AACjB,UAAA,IAAI,CAAC,MAAA,EAAQ;AACb,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,CAAA,CAAE,aAAa,UAAA,GAAa,MAAA;AAC5B,UAAA,IAAI,MAAA,KAAW,EAAA,EAAI,SAAA,CAAU,EAAE,CAAA;AAAA,QACjC,CAAA;AAAA,QACA,WAAA,EAAa,MAAM,MAAA,KAAW,EAAA,IAAM,UAAU,IAAI,CAAA;AAAA,QAClD,MAAA,EAAQ,IAAA;AAAA,QACR,KAAA,EAAO;AAAA,UACL,OAAA,EAAS,OAAA;AAAA,UACT,KAAA,EAAO,MAAA;AAAA,UACP,SAAA,EAAW,MAAA;AAAA,UACX,MAAA,EAAQ,MAAA;AAAA,UACR,YAAA,EAAc,CAAA;AAAA,UACd,MAAA,EAAQ,YAAY,MAAA,GAAS,SAAA;AAAA,UAC7B,OAAA,EAAS,SAAA;AAAA,UACT,WAAA,EAAa,IAAI,KAAA,GAAQ,EAAA;AAAA,UACzB,UAAA,EAAY,WAAW,oBAAA,GAAuB,aAAA;AAAA,UAC9C,KAAA,EAAO,WAAW,oBAAA,GAAuB,oBAAA;AAAA,UACzC,IAAA,EAAM,SAAA;AAAA,UACN,SAAA,EAAW,MAAA,KAAW,EAAA,GAAK,8CAAA,GAAiD;AAAA,SAC9E;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAAD,GAAAA,CAAC,UAAK,KAAA,EAAO,EAAE,SAAS,GAAA,EAAI,EAAI,eAAK,IAAA,EAAK,CAAA;AAAA,UAAO,QAAA;AAAA,UAAI;AAAA;AAAA;AAAA,KACvD;AAAA,IACC,UAAA,CAAW,GAAA,EAAK,EAAE,CAAA,CAAE,GAAA,CAAI,CAAC,KAAA,qBACxBA,GAAAA,CAAC,QAAA,EAAA,EAAwB,GAAA,EAAU,EAAA,EAAI,MAAM,EAAA,EAAI,KAAA,EAAO,KAAA,GAAQ,CAAA,EAAG,SAAA,EAAsB,QAAA,EAAoB,QAAgB,MAAA,EAAgB,SAAA,EAAA,EAA9H,KAAA,CAAM,EAA8I,CACpK;AAAA,GAAA,EACH,CAAA;AAEJ;AClGA,IAAM,KAAA,GAAuB;AAAA,EAC3B,OAAA,EAAS,OAAA;AAAA,EACT,QAAA,EAAU,EAAA;AAAA,EACV,aAAA,EAAe,WAAA;AAAA,EACf,aAAA,EAAe,QAAA;AAAA,EACf,KAAA,EAAO,mBAAA;AAAA,EACP,MAAA,EAAQ;AACV,CAAA;AACA,IAAM,KAAA,GAAuB;AAAA,EAC3B,KAAA,EAAO,MAAA;AAAA,EACP,SAAA,EAAW,YAAA;AAAA,EACX,OAAA,EAAS,SAAA;AAAA,EACT,YAAA,EAAc,CAAA;AAAA,EACd,MAAA,EAAQ,8BAAA;AAAA,EACR,UAAA,EAAY,sBAAA;AAAA,EACZ,KAAA,EAAO,gBAAA;AAAA,EACP,IAAA,EAAM,SAAA;AAAA,EACN,QAAA,EAAU;AACZ,CAAA;AAcO,SAAS,SAAA,CAAU,EAAE,GAAA,EAAK,SAAA,EAAW,OAAM,EAAiC;AAIjF,EAAA,MAAM,MAAM,KAAA,EAAM;AAClB,EAAA,MAAM,UAAU,CAAC,GAAA,KAAgB,CAAA,EAAG,GAAG,IAAI,GAAG,CAAA,CAAA;AAE9C,EAAA,MAAM,IAAA,GAAO,SAAA,GAAY,GAAA,CAAI,KAAA,CAAM,SAAS,CAAA,GAAI,IAAA;AAChD,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,uBACEA,GAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,EAAA,EAAI,KAAA,EAAO,mBAAA,EAAqB,UAAA,EAAY,uBAAA,EAAyB,QAAA,EAAU,EAAA,IAAM,QAAA,EAAA,4BAAA,EAE5G,CAAA;AAAA,EAEJ;AAEA,EAAA,MAAM,CAAA,GAAI,KAAK,KAAA,CAAM,IAAA;AACrB,EAAA,MAAM,QAAA,GAAW,CAAC,KAAA,KAChB,KAAA,CAAM,EAAE,CAAA,EAAG,WAAA,EAAa,EAAA,EAAI,IAAA,CAAK,EAAA,EAAI,UAAA,EAAY,MAAA,EAAQ,OAAO,CAAA;AAClE,EAAA,MAAM,UAAU,CAAC,GAAA,EAAa,UAC5B,KAAA,CAAM,EAAE,GAAG,WAAA,EAAa,EAAA,EAAI,IAAA,CAAK,EAAA,EAAI,OAAO,EAAE,CAAC,GAAG,GAAG,KAAA,IAAS,CAAA;AAEhE,EAAA,uBACEC,IAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,QAAA,EAAU,MAAA,EAAQ,OAAA,EAAS,EAAA,EAAI,YAAY,uBAAA,EAAyB,MAAA,EAAQ,MAAA,EAAQ,SAAA,EAAW,cAAa,EACxH,QAAA,EAAA;AAAA,oBAAAD,GAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,gBAAA,EAAiB,EAAI,QAAA,EAAA,IAAA,CAAK,IAAA,EAAK,CAAA;AAAA,oBACnFA,GAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,KAAA,EAAO,mBAAA,EAAqB,YAAA,EAAc,CAAA,EAAE,EAAI,eAAK,EAAA,EAAG,CAAA;AAAA,IAEnF,KAAK,IAAA,KAAS,MAAA,mBACbC,IAAAA,CAAAC,YAAA,EACE,QAAA,EAAA;AAAA,sBAAAF,GAAAA,CAAC,WAAM,KAAA,EAAO,KAAA,EAAO,SAAS,OAAA,CAAQ,SAAS,GAAG,QAAA,EAAA,SAAA,EAAO,CAAA;AAAA,sBACzDA,GAAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,EAAA,EAAI,QAAQ,SAAS,CAAA;AAAA,UACrB,gBAAA,EAAe,SAAA;AAAA,UACf,OAAO,EAAE,GAAG,OAAO,SAAA,EAAW,EAAA,EAAI,QAAQ,UAAA,EAAW;AAAA,UACrD,KAAA,EAAO,OAAO,IAAA,CAAK,KAAA,CAAM,YAAY,QAAA,GAAW,IAAA,CAAK,MAAM,OAAA,GAAU,EAAA;AAAA,UACrE,UAAU,CAAC,CAAA,KAAM,QAAQ,SAAA,EAAW,CAAA,CAAE,OAAO,KAAK;AAAA;AAAA;AACpD,KAAA,EACF,CAAA,GACE,IAAA;AAAA,IAEH,KAAK,IAAA,KAAS,OAAA,mBACbC,IAAAA,CAAAC,YAAA,EACE,QAAA,EAAA;AAAA,sBAAAF,GAAAA,CAAC,WAAM,KAAA,EAAO,KAAA,EAAO,SAAS,OAAA,CAAQ,KAAK,GAAG,QAAA,EAAA,QAAA,EAAM,CAAA;AAAA,sBACpDA,GAAAA;AAAA,QAAC,OAAA;AAAA,QAAA;AAAA,UACC,EAAA,EAAI,QAAQ,KAAK,CAAA;AAAA,UACjB,gBAAA,EAAe,KAAA;AAAA,UACf,KAAA,EAAO,KAAA;AAAA,UACP,KAAA,EAAO,OAAO,IAAA,CAAK,KAAA,CAAM,QAAQ,QAAA,GAAW,IAAA,CAAK,MAAM,GAAA,GAAM,EAAA;AAAA,UAC7D,UAAU,CAAC,CAAA,KAAM,QAAQ,KAAA,EAAO,CAAA,CAAE,OAAO,KAAK;AAAA;AAAA;AAChD,KAAA,EACF,CAAA,GACE,IAAA;AAAA,oBAEJA,IAAC,OAAA,EAAA,EAAM,KAAA,EAAO,OAAO,OAAA,EAAS,OAAA,CAAQ,YAAY,CAAA,EAAG,QAAA,EAAA,YAAA,EAAU,CAAA;AAAA,oBAC/DA,GAAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACC,EAAA,EAAI,QAAQ,YAAY,CAAA;AAAA,QACxB,gBAAA,EAAe,YAAA;AAAA,QACf,KAAA,EAAO,KAAA;AAAA,QACP,WAAA,EAAY,kCAAA;AAAA,QACZ,KAAA,EAAO,EAAE,UAAA,IAAc,EAAA;AAAA,QACvB,QAAA,EAAU,CAAC,CAAA,KAAM,QAAA,CAAS,EAAE,UAAA,EAAY,CAAA,CAAE,MAAA,CAAO,KAAA,EAAO;AAAA;AAAA,KAC1D;AAAA,oBAEAA,IAAC,OAAA,EAAA,EAAM,KAAA,EAAO,OAAO,OAAA,EAAS,OAAA,CAAQ,OAAO,CAAA,EAAG,QAAA,EAAA,YAAA,EAAU,CAAA;AAAA,oBAC1DA,GAAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACC,EAAA,EAAI,QAAQ,OAAO,CAAA;AAAA,QACnB,gBAAA,EAAe,OAAA;AAAA,QACf,KAAA,EAAO,KAAA;AAAA,QACP,WAAA,EAAY,SAAA;AAAA,QACZ,KAAA,EAAO,EAAE,KAAA,IAAS,EAAA;AAAA,QAClB,QAAA,EAAU,CAAC,CAAA,KAAM,QAAA,CAAS,EAAE,KAAA,EAAO,CAAA,CAAE,MAAA,CAAO,KAAA,EAAO;AAAA;AAAA,KACrD;AAAA,oBAEAA,IAAC,OAAA,EAAA,EAAM,KAAA,EAAO,OAAO,OAAA,EAAS,OAAA,CAAQ,UAAU,CAAA,EAAG,QAAA,EAAA,gBAAA,EAAc,CAAA;AAAA,oBACjEA,GAAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACC,EAAA,EAAI,QAAQ,UAAU,CAAA;AAAA,QACtB,gBAAA,EAAe,UAAA;AAAA,QACf,IAAA,EAAK,QAAA;AAAA,QACL,KAAA,EAAO,KAAA;AAAA,QACP,KAAA,EAAO,CAAA,CAAE,QAAA,EAAU,KAAA,IAAS,EAAA;AAAA,QAC5B,UAAU,CAAC,CAAA,KAAM,QAAA,CAAS,EAAE,UAAU,EAAE,KAAA,EAAO,MAAA,CAAO,CAAA,CAAE,OAAO,KAAK,CAAA,IAAK,GAAG,IAAA,EAAM,IAAA,IAAQ;AAAA;AAAA,KAC5F;AAAA,oBAEAA,IAAC,OAAA,EAAA,EAAM,KAAA,EAAO,OAAO,OAAA,EAAS,OAAA,CAAQ,SAAS,CAAA,EAAG,QAAA,EAAA,cAAA,EAAY,CAAA;AAAA,oBAC9DA,GAAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACC,EAAA,EAAI,QAAQ,SAAS,CAAA;AAAA,QACrB,gBAAA,EAAe,SAAA;AAAA,QACf,IAAA,EAAK,QAAA;AAAA,QACL,KAAA,EAAO,KAAA;AAAA,QACP,KAAA,EAAO,CAAA,CAAE,OAAA,EAAS,KAAA,IAAS,EAAA;AAAA,QAC3B,UAAU,CAAC,CAAA,KAAM,QAAA,CAAS,EAAE,SAAS,EAAE,KAAA,EAAO,MAAA,CAAO,CAAA,CAAE,OAAO,KAAK,CAAA,IAAK,GAAG,IAAA,EAAM,IAAA,IAAQ;AAAA;AAAA,KAC3F;AAAA,oBAEAA,IAAC,OAAA,EAAA,EAAM,KAAA,EAAO,OAAO,OAAA,EAAS,OAAA,CAAQ,SAAS,CAAA,EAAG,QAAA,EAAA,SAAA,EAAO,CAAA;AAAA,oBACzDA,GAAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACC,EAAA,EAAI,QAAQ,SAAS,CAAA;AAAA,QACrB,gBAAA,EAAe,SAAA;AAAA,QACf,IAAA,EAAK,QAAA;AAAA,QACL,IAAA,EAAK,KAAA;AAAA,QACL,GAAA,EAAI,GAAA;AAAA,QACJ,GAAA,EAAI,GAAA;AAAA,QACJ,KAAA,EAAO,KAAA;AAAA,QACP,KAAA,EAAO,EAAE,OAAA,IAAW,EAAA;AAAA,QACpB,QAAA,EAAU,CAAC,CAAA,KAAM,QAAA,CAAS,EAAE,OAAA,EAAS,MAAA,CAAO,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,EAAG;AAAA;AAAA;AAC/D,GAAA,EACF,CAAA;AAEJ;;;ACnIO,IAAM,eAAA,uBAAsB,GAAA,CAAI;AAAA,EACrC,SAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAC;AAkBM,IAAM,YAAA,GAGT;AAAA,EACF,IAAA,EAAM,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,EAAE,OAAA,EAAS,UAAA,EAAW,EAAG,KAAA,EAAO,EAAE,KAAA,EAAO,WAAU,EAAE;AAAA,EAClF,OAAA,EAAS,EAAE,IAAA,EAAM,SAAA,EAAW,OAAO,EAAE,OAAA,EAAS,eAAc,EAAG,KAAA,EAAO,EAAE,QAAA,EAAU,EAAE,OAAO,EAAA,EAAI,IAAA,EAAM,MAAK,EAAG,UAAA,EAAY,KAAI,EAAE;AAAA,EAC/H,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,OAAO,EAAE,KAAA,EAAO,QAAA,EAAU,IAAA,EAAM,KAAK,OAAA,EAAS,SAAA,EAAU,EAAG,KAAA,EAAO,EAAC,EAAE;AAAA,EAC/F,IAAA,EAAM,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAO,EAAE,OAAA,EAAS,WAAA,EAAa,IAAA,EAAM,KAAI,EAAG,KAAA,EAAO,EAAE,KAAA,EAAO,WAAU,EAAE;AAAA,EAC9F,KAAA,EAAO,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,EAAE,GAAA,EAAK,EAAA,EAAI,GAAA,EAAK,EAAA,EAAG,EAAG,KAAA,EAAO,EAAC,EAAE;AAAA,EAC/D,KAAK,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,EAAC,EAAG,KAAA,EAAO,EAAE,OAAA,EAAS,EAAE,KAAA,EAAO,EAAA,EAAI,IAAA,EAAM,IAAA,IAAO,EAAE;AAAA,EAC/E,OAAO,EAAE,IAAA,EAAM,SAAS,KAAA,EAAO,IAAI,KAAA,EAAO,EAAE,GAAA,EAAK,EAAE,OAAO,EAAA,EAAI,IAAA,EAAM,MAAK,EAAE,EAAG,QAAQ,OAAA,EAAQ;AAAA,EAC9F,MAAM,EAAE,IAAA,EAAM,QAAQ,KAAA,EAAO,IAAI,KAAA,EAAO,EAAE,GAAA,EAAK,EAAE,OAAO,EAAA,EAAI,IAAA,EAAM,MAAK,EAAE,EAAG,QAAQ,MAAA,EAAO;AAAA,EAC3F,IAAA,EAAM,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,EAAC,EAAG,KAAA,EAAO,EAAE,OAAA,EAAS,EAAE,KAAA,EAAO,IAAI,IAAA,EAAM,IAAA,EAAK,EAAG,MAAA,EAAQ,EAAE,KAAA,EAAO,EAAA,EAAI,IAAA,EAAM,IAAA,EAAK,EAAG,MAAA,EAAQ,mBAAA,EAAoB,EAAE;AAAA,EAC/I,OAAA,EAAS,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,EAAE,OAAA,EAAS,oCAAA,EAAiC,OAAA,EAAS,MAAA,EAAO,EAAG,KAAA,EAAO,EAAC,EAAE;AAAA,EAC5G,OAAA,EAAS,EAAE,IAAA,EAAM,SAAA,EAAW,OAAO,EAAC,EAAG,KAAA,EAAO,EAAC,EAAE;AAAA,EACjD,IAAA,EAAM,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,EAAE,OAAA,EAAS,2CAAA,EAA6C,IAAA,EAAM,MAAA,EAAO,EAAG,KAAA,EAAO,EAAC,EAAE;AAAA,EAC/G,QAAA,EAAU,EAAE,IAAA,EAAM,UAAA,EAAY,KAAA,EAAO,EAAE,IAAA,EAAM,oEAAA,EAAqE,EAAG,KAAA,EAAO,EAAC,EAAE;AAAA,EAC/H,QAAA,EAAU,EAAE,IAAA,EAAM,UAAA,EAAY,OAAO,EAAE,KAAA,EAAO,IAAG,EAAG,KAAA,EAAO,EAAE,GAAA,EAAK,EAAE,OAAO,EAAA,EAAI,IAAA,EAAM,MAAK,EAAE,EAAG,QAAQ,OAAA;AACzG;AAEO,IAAM,QAAA,GAAoD;AAAA,EAC/D,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,SAAA,EAAU;AAAA,EACpC,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC9B,EAAE,IAAA,EAAM,QAAA,EAAU,KAAA,EAAO,QAAA,EAAS;AAAA,EAClC,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC9B,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,OAAA,EAAQ;AAAA,EAChC,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC9B,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,SAAA,EAAU;AAAA,EACpC,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,OAAA,EAAQ;AAAA,EAChC,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC9B,EAAE,IAAA,EAAM,KAAA,EAAO,KAAA,EAAO,KAAA,EAAM;AAAA,EAC5B,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,SAAA,EAAU;AAAA,EACpC,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC9B,EAAE,IAAA,EAAM,UAAA,EAAY,KAAA,EAAO,WAAA,EAAY;AAAA,EACvC,EAAE,IAAA,EAAM,UAAA,EAAY,KAAA,EAAO,UAAA;AAC7B;AASO,SAAS,mBAAA,CAAoB,KAAc,UAAA,EAA0C;AAC1F,EAAA,MAAM,QAAA,GAAW,UAAA,GAAa,GAAA,CAAI,KAAA,CAAM,UAAU,CAAA,GAAI,IAAA;AACtD,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,gBAAgB,GAAA,CAAI,QAAA,CAAS,IAAI,CAAA,GAAI,QAAA,CAAS,KAAK,QAAA,CAAS,MAAA;AAAA,EACrE;AACA,EAAA,OAAO,IAAI,QAAA,CAAS,GAAA,CAAI,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA,IAAK,IAAA;AAClD;AAQO,SAAS,aAAA,CACd,GAAA,EACA,IAAA,EACA,UAAA,EAEA,QAAA,EAC4B;AAC5B,EAAA,MAAM,MAAA,GACJ,QAAA,KAAa,MAAA,GACT,QAAA,IAAY,GAAA,CAAI,MAAM,QAAQ,CAAA,GAC5B,eAAA,CAAgB,GAAA,CAAI,GAAA,CAAI,KAAA,CAAM,QAAQ,CAAA,CAAG,IAAI,CAAA,GAC3C,QAAA,GACA,GAAA,CAAI,KAAA,CAAM,QAAQ,CAAA,CAAG,MAAA,GACvB,IAAA,GACF,mBAAA,CAAoB,GAAA,EAAK,UAAU,CAAA;AAEzC,EAAA,MAAM,QAAA,GAAW,UAAA,CAAW,GAAA,EAAK,MAAM,CAAA;AACvC,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,QAAA,CAAS,MAAA,GAAS,QAAA,CAAS,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA,CAAG,KAAA,GAAQ,IAAA,EAAM,IAAI,CAAA;AAC5F,EAAA,MAAM,EAAA,GAAK,CAAA,CAAA,EAAI,GAAA,CAAI,GAAA,GAAM,CAAC,CAAA,CAAA,EAAI,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,GAAA,EAAK,CAAC,CAAA,CAAA;AAC3D,EAAA,MAAM,GAAA,GAAM,aAAa,IAAI,CAAA;AAE7B,EAAA,OAAO;AAAA,IACL,EAAA;AAAA,IACA,EAAA,EAAI;AAAA,MACF,CAAA,EAAG,aAAA;AAAA,MACH,IAAA,EAAM;AAAA,QACJ,EAAA;AAAA,QACA,MAAM,GAAA,CAAI,IAAA;AAAA,QACV,MAAA;AAAA,QACA,KAAA;AAAA,QACA,QAAQ,GAAA,CAAI,MAAA;AAAA,QACZ,KAAA,EAAO,EAAE,GAAG,GAAA,CAAI,KAAA,EAAM;AAAA,QACtB,OAAO,EAAE,IAAA,EAAM,EAAE,GAAG,GAAA,CAAI,OAAM;AAAE;AAClC;AACF,GACF;AACF;AC/HO,SAAS,aAAgB,KAAA,EAAwB;AACtD,EAAA,MAAM,GAAA,GAAMG,OAAO,KAAK,CAAA;AACxB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,GAAA,CAAI,OAAA,GAAU,KAAA;AAAA,EAChB,CAAC,CAAA;AACD,EAAA,OAAO,GAAA;AACT;ACyBO,SAAS,OAAO,EAAE,YAAA,EAAc,QAAA,EAAU,QAAA,EAAU,MAAK,EAA8B;AAI5F,EAAA,MAAM,OAAA,GAAUC,QAAQ,MAAM,YAAA,IAAgB,SAAS,UAAU,CAAA,EAAG,CAAC,YAAY,CAAC,CAAA;AAClF,EAAA,MAAM,EAAA,GAAK,UAAU,OAAO,CAAA;AAC5B,EAAA,MAAM,KAAA,GAAQD,OAAO,IAAI,CAAA;AAKzB,EAAA,MAAM,WAAA,GAAc,aAAa,QAAQ,CAAA;AACzC,EAAAE,UAAU,MAAM;AACd,IAAA,IAAI,MAAM,OAAA,EAAS;AACjB,MAAA,KAAA,CAAM,OAAA,GAAU,KAAA;AAChB,MAAA;AAAA,IACF;AACA,IAAA,WAAA,CAAY,OAAA,GAAU,EAAA,CAAG,KAAA,CAAM,GAAG,CAAA;AAAA,EACpC,GAAG,CAAC,EAAA,CAAG,KAAA,CAAM,GAAA,EAAK,WAAW,CAAC,CAAA;AAE9B,EAAA,MAAM,KAAA,GAAuB;AAAA,IAC3B,OAAA,EAAS,MAAA;AAAA,IACT,mBAAA,EAAqB,iBAAA;AAAA,IACrB,MAAA,EAAQ,MAAA;AAAA,IACR,SAAA,EAAW,CAAA;AAAA,IACX,UAAA,EAAY,gBAAA;AAAA,IACZ,KAAA,EAAO,gBAAA;AAAA,IACP,MAAA,EAAQ,8BAAA;AAAA,IACR,YAAA,EAAc,EAAA;AAAA,IACd,QAAA,EAAU;AAAA,GACZ;AAEA,EAAA,uBACEJ,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,kBAAA,EAAmB,OAAO,KAAA,EACvC,QAAA,EAAA;AAAA,oBAAAD,GAAAA,CAAC,WAAO,QAAA,EAAA,UAAA,EAAW,CAAA;AAAA,oBACnBC,IAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,WAAA,EAAa,8BAAA,EAAgC,OAAA,EAAS,MAAA,EAAQ,aAAA,EAAe,QAAA,EAAU,SAAA,EAAW,GAAE,EAChH,QAAA,EAAA;AAAA,sBAAAD,GAAAA,CAAC,WAAQ,EAAA,EAAQ,CAAA;AAAA,sBACjBA,GAAAA;AAAA,QAAC,WAAA;AAAA,QAAA;AAAA,UACC,GAAA,EAAK,GAAG,KAAA,CAAM,GAAA;AAAA,UACd,SAAA,EAAW,GAAG,KAAA,CAAM,SAAA;AAAA,UACpB,UAAU,EAAA,CAAG,MAAA;AAAA,UACb,MAAA,EAAQ,CAAC,EAAA,EAAI,MAAA,EAAQ,KAAA,KAAU,EAAA,CAAG,KAAA,CAAM,EAAE,CAAA,EAAG,WAAA,EAAa,EAAA,EAAI,MAAA,EAAQ,OAAO;AAAA;AAAA;AAC/E,KAAA,EACF,CAAA;AAAA,oBACAA,GAAAA,CAAC,MAAA,EAAA,EAAO,KAAK,EAAA,CAAG,KAAA,CAAM,KAAK,SAAA,EAAW,EAAA,CAAG,KAAA,CAAM,SAAA,EAAW,UAAU,EAAA,CAAG,MAAA,EAAQ,OAAO,EAAA,CAAG,KAAA,EAAO,UAAoB,IAAA,EAAY,CAAA;AAAA,oBAChIA,GAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,YAAY,8BAAA,EAAgC,SAAA,EAAW,CAAA,EAAE,EACrE,QAAA,kBAAAA,GAAAA,CAAC,aAAU,GAAA,EAAK,EAAA,CAAG,KAAA,CAAM,GAAA,EAAK,SAAA,EAAW,EAAA,CAAG,MAAM,SAAA,EAAW,KAAA,EAAO,EAAA,CAAG,KAAA,EAAO,CAAA,EAChF;AAAA,GAAA,EACF,CAAA;AAEJ;AAEA,SAAS,OAAA,CAAQ,EAAE,EAAA,EAAG,EAAoC;AACxD,EAAA,MAAM,GAAA,GAAqB;AAAA,IACzB,IAAA,EAAM,SAAA;AAAA,IACN,QAAA,EAAU,EAAA;AAAA,IACV,OAAA,EAAS,UAAA;AAAA,IACT,YAAA,EAAc,CAAA;AAAA,IACd,MAAA,EAAQ,8BAAA;AAAA,IACR,UAAA,EAAY,gBAAA;AAAA,IACZ,KAAA,EAAO,gBAAA;AAAA,IACP,MAAA,EAAQ;AAAA,GACV;AACA,EAAA,uBACEC,IAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,MAAA;AAAA,QACT,GAAA,EAAK,CAAA;AAAA,QACL,OAAA,EAAS,CAAA;AAAA,QACT,YAAA,EAAc,8BAAA;AAAA,QACd,UAAA,EAAY;AAAA,OACd;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAAD,GAAAA,CAAC,YAAO,IAAA,EAAK,QAAA,EAAS,OAAO,EAAE,GAAG,KAAK,OAAA,EAAS,EAAA,CAAG,UAAU,CAAA,GAAI,GAAA,IAAO,QAAA,EAAU,CAAC,GAAG,OAAA,EAAS,OAAA,EAAS,EAAA,CAAG,IAAA,EAAM,QAAA,EAAA,MAAA,EAEjH,CAAA;AAAA,wBACAA,IAAC,QAAA,EAAA,EAAO,IAAA,EAAK,UAAS,KAAA,EAAO,EAAE,GAAG,GAAA,EAAK,OAAA,EAAS,EAAA,CAAG,UAAU,CAAA,GAAI,GAAA,IAAO,QAAA,EAAU,CAAC,GAAG,OAAA,EAAS,OAAA,EAAS,EAAA,CAAG,IAAA,EAAM,QAAA,EAAA,MAAA,EAEjH,CAAA;AAAA,wBACAA,GAAAA,CAAC,cAAA,EAAA,EAAe,EAAA,EAAQ,GAAA,EAAU;AAAA;AAAA;AAAA,GACpC;AAEJ;AAWA,SAAS,cAAA,CAAe,EAAE,EAAA,EAAI,GAAA,EAAI,EAAwD;AACxF,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAID,SAAS,KAAK,CAAA;AAGtC,EAAAM,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,KAAK,CAAA;AACjC,IAAA,MAAM,QAAQ,CAAC,CAAA,KAAqB,EAAE,GAAA,KAAQ,QAAA,IAAY,QAAQ,KAAK,CAAA;AACvE,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,KAAK,CAAA;AACtC,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,KAAK,CAAA;AACxC,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,mBAAA,CAAoB,SAAS,KAAK,CAAA;AACzC,MAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,KAAK,CAAA;AAAA,IAC7C,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,MAAM,GAAA,GAAM,CAAC,IAAA,KAAkB;AAC7B,IAAA,MAAM,EAAE,EAAA,EAAI,EAAA,EAAG,GAAI,aAAA,CAAc,EAAA,CAAG,KAAA,CAAM,GAAA,EAAK,IAAA,EAAM,EAAA,CAAG,KAAA,CAAM,SAAS,CAAA;AACvE,IAAA,EAAA,CAAG,MAAM,EAAE,CAAA;AACX,IAAA,EAAA,CAAG,OAAO,EAAE,CAAA;AACZ,IAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,EACf,CAAA;AAEA,EAAA,uBACEJ,IAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,QAAA,EAAU,UAAA,EAAY,UAAA,EAAY,MAAA,IAAU,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,iBAAgB,EAC1F,QAAA,EAAA;AAAA,oBAAAD,GAAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,QAAA;AAAA,QACL,KAAA,EAAO,EAAE,GAAG,GAAA,EAAK,aAAa,6BAAA,EAA8B;AAAA,QAC5D,eAAA,EAAc,MAAA;AAAA,QACd,eAAA,EAAe,IAAA;AAAA,QACf,sBAAA,EAAqB,EAAA;AAAA,QACrB,SAAS,MAAM,OAAA,CAAQ,CAAC,CAAA,KAAM,CAAC,CAAC,CAAA;AAAA,QACjC,QAAA,EAAA;AAAA;AAAA,KAED;AAAA,IACC,wBACCA,GAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,MAAA;AAAA,QACL,mBAAA,EAAkB,EAAA;AAAA,QAClB,KAAA,EAAO;AAAA,UACL,QAAA,EAAU,UAAA;AAAA,UACV,GAAA,EAAK,kBAAA;AAAA,UACL,IAAA,EAAM,CAAA;AAAA,UACN,MAAA,EAAQ,EAAA;AAAA,UACR,QAAA,EAAU,GAAA;AAAA,UACV,SAAA,EAAW,GAAA;AAAA,UACX,SAAA,EAAW,MAAA;AAAA,UACX,OAAA,EAAS,CAAA;AAAA,UACT,YAAA,EAAc,CAAA;AAAA,UACd,MAAA,EAAQ,8BAAA;AAAA,UACR,UAAA,EAAY,gBAAA;AAAA,UACZ,SAAA,EAAW;AAAA,SACb;AAAA,QAEC,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,IAAA,qBACbA,GAAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAEC,IAAA,EAAK,QAAA;AAAA,YACL,IAAA,EAAK,UAAA;AAAA,YACL,gBAAc,IAAA,CAAK,IAAA;AAAA,YACnB,KAAA,EAAO;AAAA,cACL,GAAG,GAAA;AAAA,cACH,OAAA,EAAS,OAAA;AAAA,cACT,KAAA,EAAO,MAAA;AAAA,cACP,SAAA,EAAW,MAAA;AAAA,cACX,MAAA,EAAQ,CAAA;AAAA,cACR,UAAA,EAAY;AAAA,aACd;AAAA,YACA,OAAA,EAAS,MAAM,GAAA,CAAI,IAAA,CAAK,IAAI,CAAA;AAAA,YAE3B,QAAA,EAAA,IAAA,CAAK;AAAA,WAAA;AAAA,UAdD,IAAA,CAAK;AAAA,SAgBb;AAAA;AAAA;AACH,GAAA,EAEJ,CAAA;AAEJ;AASA,IAAM,SAAA,GACJ,mSAAA;AAIF,IAAM,UAAA,GACJ,8PAAA;AAGF,IAAM,UAAA,GACJ,2CAA2C,UAAU,CAAA,sDAAA,EACG,SAAS,CAAA,wDAAA,EACR,UAAU,wDACZ,SAAS,CAAA,CAAA,CAAA","file":"chunk-6Q7DKNDZ.js","sourcesContent":["/**\n * Minimal collab-safe fractional ordering keys.\n *\n * Sibling `order` is a string over a base-62 alphabet interpreted as a fraction\n * in (0, 1). `keyBetween(a, b)` returns a key that sorts strictly between `a`\n * and `b` (lexicographically), extending precision when the bounds are\n * adjacent. Concurrent inserts at the same spot produce different keys (vary by\n * actor), so reorders never collide.\n *\n * This is intentionally compact for Phase 0. Before live collaboration ships it\n * should be swapped for a hardened implementation with jitter + bucket bounds.\n */\n\nconst BASE = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\nconst RADIX = BASE.length; // 62\nconst MID = Math.floor(RADIX / 2);\n\nfunction val(c: string): number {\n const i = BASE.indexOf(c);\n return i < 0 ? 0 : i;\n}\n\n/**\n * Return a key strictly between `a` and `b`. Pass `null` for an open bound\n * (`keyBetween(null, null)` for the first key, `keyBetween(last, null)` to\n * append, `keyBetween(null, first)` to prepend).\n */\nexport function keyBetween(a: string | null, b: string | null): string {\n if (a === null) a = \"\";\n if (b === null) {\n // Append: a key greater than `a`. Appending a mid digit always sorts after.\n return a + BASE[MID];\n }\n if (a >= b) {\n throw new Error(`keyBetween: lower bound \"${a}\" is not below upper bound \"${b}\"`);\n }\n\n let result = \"\";\n let i = 0;\n // Walk the common prefix, then split at the first differing digit.\n for (;;) {\n const ca = i < a.length ? val(a[i] as string) : 0;\n const cb = i < b.length ? val(b[i] as string) : RADIX;\n if (cb - ca > 1) {\n result += BASE[ca + Math.floor((cb - ca) / 2)];\n return result;\n }\n if (ca === cb) {\n result += BASE[ca];\n i++;\n continue;\n }\n // Adjacent digits (cb - ca === 1): take the lower path, then descend into\n // `a`'s remainder with RADIX as the upper bound until a gap opens.\n result += BASE[ca];\n i++;\n for (;;) {\n const da = i < a.length ? val(a[i] as string) : 0;\n if (RADIX - da > 1) {\n result += BASE[da + Math.floor((RADIX - da) / 2)];\n return result;\n }\n result += BASE[da];\n i++;\n }\n }\n}\n","/**\n * Editor state engine — pure and framework-agnostic so it's unit-testable.\n * `useEditor` wraps this with `useReducer`. Undo/redo uses document snapshots\n * (simple + robust); the op-level `invert` in the spine is for collab/op-stream\n * undo.\n */\nimport type { PageDoc } from \"../document/types\";\nimport type { PageOp } from \"../document/ops\";\nimport { reduce } from \"../document/reduce\";\n\nexport interface EditorState {\n doc: PageDoc;\n past: PageDoc[];\n future: PageDoc[];\n selection: string | null;\n}\n\nexport type EditorAction =\n | { type: \"apply\"; op: PageOp }\n | { type: \"undo\" }\n | { type: \"redo\" }\n | { type: \"select\"; id: string | null }\n | { type: \"replace\"; doc: PageDoc };\n\nconst HISTORY_LIMIT = 100;\n\nexport function initEditor(doc: PageDoc): EditorState {\n return { doc, past: [], future: [], selection: null };\n}\n\nexport function editorReduce(state: EditorState, action: EditorAction): EditorState {\n switch (action.type) {\n case \"apply\": {\n const next = reduce(state.doc, action.op);\n if (next === state.doc) return state; // invalid op → no-op, no history entry\n return {\n ...state,\n doc: next,\n past: [...state.past, state.doc].slice(-HISTORY_LIMIT),\n future: [],\n };\n }\n case \"undo\": {\n if (state.past.length === 0) return state;\n const prev = state.past[state.past.length - 1]!;\n return {\n ...state,\n doc: prev,\n past: state.past.slice(0, -1),\n future: [state.doc, ...state.future],\n };\n }\n case \"redo\": {\n if (state.future.length === 0) return state;\n const next = state.future[0]!;\n return {\n ...state,\n doc: next,\n past: [...state.past, state.doc],\n future: state.future.slice(1),\n };\n }\n case \"select\":\n return state.selection === action.id ? state : { ...state, selection: action.id };\n case \"replace\":\n return state.doc === action.doc ? state : { ...state, doc: action.doc, future: [] };\n default:\n return state;\n }\n}\n","import { useCallback, useMemo, useReducer } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport type { PageOp } from \"../document/ops\";\nimport { editorReduce, initEditor, type EditorState } from \"./state\";\n\nexport interface EditorApi {\n state: EditorState;\n /** Dispatch a mutation through the op-spine (records an undo snapshot). */\n apply: (op: PageOp) => void;\n undo: () => void;\n redo: () => void;\n select: (id: string | null) => void;\n /** Replace the document wholesale (e.g. a controlled `value` change). */\n replace: (doc: PageDoc) => void;\n canUndo: boolean;\n canRedo: boolean;\n}\n\nexport function useEditor(initialDoc: PageDoc): EditorApi {\n const [state, dispatch] = useReducer(editorReduce, initialDoc, initEditor);\n const apply = useCallback((op: PageOp) => dispatch({ type: \"apply\", op }), []);\n const undo = useCallback(() => dispatch({ type: \"undo\" }), []);\n const redo = useCallback(() => dispatch({ type: \"redo\" }), []);\n const select = useCallback((id: string | null) => dispatch({ type: \"select\", id }), []);\n const replace = useCallback((doc: PageDoc) => dispatch({ type: \"replace\", doc }), []);\n\n return useMemo(\n () => ({\n state,\n apply,\n undo,\n redo,\n select,\n replace,\n canUndo: state.past.length > 0,\n canRedo: state.future.length > 0,\n }),\n [state, apply, undo, redo, select, replace],\n );\n}\n","import {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type MouseEvent as ReactMouseEvent,\n type PointerEvent as ReactPointerEvent,\n type ReactElement,\n} from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport type { PageOp } from \"../document/ops\";\nimport type { DataContext, ElementRegistry } from \"../react/registry\";\nimport { CmsPage } from \"../react/CmsPage\";\n\nexport interface CanvasProps {\n doc: PageDoc;\n selection: string | null;\n onSelect: (id: string | null) => void;\n apply: (op: PageOp) => void;\n /** Custom element registry so custom node types render on the canvas (not a blank placeholder). */\n registry?: ElementRegistry;\n /** Data context for `{ $bind }` props + repeaters previewed on the canvas. */\n data?: DataContext;\n}\n\ninterface Box {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/**\n * The edit surface: renders the live page and overlays a selection box. Click to\n * select; drag the box to move (px constraints). Phase 1 cut: free-parent\n * move + resize handles, stack/grid reordering, and snapping come next.\n */\nexport function Canvas({ doc, selection, onSelect, apply, registry, data }: CanvasProps): ReactElement {\n const ref = useRef<HTMLDivElement>(null);\n const [box, setBox] = useState<Box | null>(null);\n const drag = useRef<{ x: number; y: number; left: number; top: number } | null>(null);\n\n useLayoutEffect(() => {\n const root = ref.current;\n if (!root || !selection) {\n setBox(null);\n return;\n }\n const el = root.querySelector(`[data-cms=\"${cssEscape(selection)}\"]`);\n if (!el) {\n setBox(null);\n return;\n }\n const r = el.getBoundingClientRect();\n const rootRect = root.getBoundingClientRect();\n setBox({\n x: r.left - rootRect.left + root.scrollLeft,\n y: r.top - rootRect.top + root.scrollTop,\n w: r.width,\n h: r.height,\n });\n }, [selection, doc]);\n\n const handleClick = (e: ReactMouseEvent<HTMLDivElement>) => {\n const target = (e.target as HTMLElement).closest(\"[data-cms]\");\n onSelect(target ? target.getAttribute(\"data-cms\") : null);\n };\n\n const handlePointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (!selection) return;\n const c = doc.nodes[selection]?.constraints?.base;\n const left = c && c.left && typeof c.left === \"object\" ? c.left.value : 0;\n const top = c && c.top && typeof c.top === \"object\" ? c.top.value : 0;\n drag.current = { x: e.clientX, y: e.clientY, left, top };\n e.currentTarget.setPointerCapture(e.pointerId);\n e.stopPropagation();\n };\n\n const handlePointerMove = (e: ReactPointerEvent<HTMLDivElement>) => {\n const d = drag.current;\n if (!d || !selection) return;\n apply({\n t: \"set_constraints\",\n id: selection,\n breakpoint: \"base\",\n patch: {\n left: { value: Math.round(d.left + (e.clientX - d.x)), unit: \"px\" },\n top: { value: Math.round(d.top + (e.clientY - d.y)), unit: \"px\" },\n },\n });\n };\n\n const handlePointerUp = () => {\n drag.current = null;\n };\n\n const surface: CSSProperties = {\n position: \"relative\",\n overflow: \"auto\",\n height: \"100%\",\n background: \"var(--fcms-canvas)\",\n };\n const overlay: CSSProperties = box\n ? {\n position: \"absolute\",\n left: box.x,\n top: box.y,\n width: box.w,\n height: box.h,\n outline: \"2px solid var(--fcms-accent, #8b5cf6)\",\n outlineOffset: -1,\n cursor: \"move\",\n touchAction: \"none\",\n }\n : { display: \"none\" };\n\n return (\n <div ref={ref} onClick={handleClick} style={surface}>\n <CmsPage doc={doc} registry={registry} data={data} />\n <div\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n style={overlay}\n />\n </div>\n );\n}\n\nfunction cssEscape(value: string): string {\n return value.replace(/[\"\\\\]/g, \"\\\\$&\");\n}\n","import { Fragment, useState, type DragEvent as ReactDragEvent, type ReactElement } from \"react\";\nimport type { NodeId, PageDoc } from \"../document/types\";\nimport { childrenOf } from \"../document/reduce\";\nimport { keyBetween } from \"../document/fractional\";\n\nexport interface LayersPanelProps {\n doc: PageDoc;\n selection: string | null;\n onSelect: (id: string) => void;\n /** Reparent/reorder a node (drag-to-reorder). Omit to disable dragging. */\n onMove?: (id: NodeId, parent: NodeId | null, order: string) => void;\n}\n\nconst MIME = \"application/x-cms-node\";\n\n/** True when `maybeAncestor` is `id` itself or one of its ancestors. */\nfunction isAncestorOrSelf(doc: PageDoc, id: string, maybeAncestor: string): boolean {\n for (let cur: string | null = id; cur; cur = doc.nodes[cur]?.parent ?? null) {\n if (cur === maybeAncestor) return true;\n }\n return false;\n}\n\n/** The section/layer tree. Click a row to select; drag a row onto another to reorder. */\nexport function LayersPanel({ doc, selection, onSelect, onMove }: LayersPanelProps): ReactElement {\n const [overId, setOverId] = useState<string | null>(null);\n\n return (\n <div style={{ overflow: \"auto\", padding: 8, fontSize: 13, fontFamily: \"system-ui, sans-serif\" }}>\n {doc.sections.map((id) => (\n <LayerRow key={id} doc={doc} id={id} depth={0} selection={selection} onSelect={onSelect} onMove={onMove} overId={overId} setOverId={setOverId} />\n ))}\n </div>\n );\n}\n\ninterface LayerRowProps {\n doc: PageDoc;\n id: string;\n depth: number;\n selection: string | null;\n onSelect: (id: string) => void;\n onMove?: (id: NodeId, parent: NodeId | null, order: string) => void;\n overId: string | null;\n setOverId: (id: string | null) => void;\n}\n\nfunction LayerRow({ doc, id, depth, selection, onSelect, onMove, overId, setOverId }: LayerRowProps): ReactElement | null {\n const node = doc.nodes[id];\n if (!node) return null;\n const selected = selection === id;\n const draggable = Boolean(onMove);\n\n const drop = (e: ReactDragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setOverId(null);\n if (!onMove) return;\n const dragId = e.dataTransfer.getData(MIME);\n // Reject no-op + any move that would drop a node into its own subtree.\n if (!dragId || dragId === id || isAncestorOrSelf(doc, id, dragId)) return;\n // Place the dragged node immediately after this row, among this row's siblings.\n const siblings = childrenOf(doc, node.parent);\n const idx = siblings.findIndex((n) => n.id === id);\n const next = siblings[idx + 1];\n onMove(dragId, node.parent, keyBetween(node.order, next ? next.order : null));\n };\n\n return (\n <Fragment>\n <button\n type=\"button\"\n draggable={draggable}\n onClick={() => onSelect(id)}\n onDragStart={(e) => {\n e.dataTransfer.setData(MIME, id);\n e.dataTransfer.effectAllowed = \"move\";\n }}\n onDragOver={(e) => {\n if (!onMove) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n if (overId !== id) setOverId(id);\n }}\n onDragLeave={() => overId === id && setOverId(null)}\n onDrop={drop}\n style={{\n display: \"block\",\n width: \"100%\",\n textAlign: \"left\",\n border: \"none\",\n borderRadius: 6,\n cursor: draggable ? \"grab\" : \"pointer\",\n padding: \"4px 8px\",\n paddingLeft: 8 + depth * 14,\n background: selected ? \"var(--fcms-sel-bg)\" : \"transparent\",\n color: selected ? \"var(--fcms-sel-fg)\" : \"var(--fcms-row-fg)\",\n font: \"inherit\",\n boxShadow: overId === id ? \"inset 0 -2px 0 0 var(--fcms-accent, #8b5cf6)\" : \"none\",\n }}\n >\n <span style={{ opacity: 0.6 }}>{node.type}</span> · {id}\n </button>\n {childrenOf(doc, id).map((child) => (\n <LayerRow key={child.id} doc={doc} id={child.id} depth={depth + 1} selection={selection} onSelect={onSelect} onMove={onMove} overId={overId} setOverId={setOverId} />\n ))}\n </Fragment>\n );\n}\n","import { useId, type CSSProperties, type ReactElement } from \"react\";\nimport type { PageDoc, StyleProps } from \"../document/types\";\nimport type { PageOp } from \"../document/ops\";\n\nexport interface InspectorProps {\n doc: PageDoc;\n selection: string | null;\n apply: (op: PageOp) => void;\n}\n\nconst label: CSSProperties = {\n display: \"block\",\n fontSize: 11,\n textTransform: \"uppercase\",\n letterSpacing: \"0.04em\",\n color: \"var(--fcms-muted)\",\n margin: \"10px 0 4px\",\n};\nconst input: CSSProperties = {\n width: \"100%\",\n boxSizing: \"border-box\",\n padding: \"6px 8px\",\n borderRadius: 6,\n border: \"1px solid var(--fcms-border)\",\n background: \"var(--fcms-input-bg)\",\n color: \"var(--fcms-fg)\",\n font: \"inherit\",\n fontSize: 13,\n};\n\n/**\n * Contextual property editor for the selected node. Every change is one op.\n *\n * Each `<label>` here is a SIBLING of its control rather than wrapping it, so\n * none of them were associated: clicking a label focused nothing and a screen\n * reader announced the panel as unlabelled boxes. (`NodeInspector` wraps its\n * controls, which associates them implicitly — these did not.)\n *\n * `htmlFor`/`id` fixes that, and `data-cms-field` gives each control the stable\n * handle the Human+ contract asks for so an agent addresses a field by name\n * instead of guessing at the DOM.\n */\nexport function Inspector({ doc, selection, apply }: InspectorProps): ReactElement {\n // Before the early return — every hook must run on every render. fancy-flow\n // shipped a blank-editor crash by getting this wrong in the same shape of\n // component.\n const uid = useId();\n const fieldId = (key: string) => `${uid}-${key}`;\n\n const node = selection ? doc.nodes[selection] : null;\n if (!node) {\n return (\n <div style={{ padding: 16, color: \"var(--fcms-muted)\", fontFamily: \"system-ui, sans-serif\", fontSize: 13 }}>\n Select an element to edit.\n </div>\n );\n }\n\n const s = node.style.base;\n const setStyle = (patch: Partial<StyleProps>) =>\n apply({ t: \"set_style\", id: node.id, breakpoint: \"base\", patch });\n const setProp = (key: string, value: unknown) =>\n apply({ t: \"set_props\", id: node.id, patch: { [key]: value } });\n\n return (\n <div style={{ overflow: \"auto\", padding: 16, fontFamily: \"system-ui, sans-serif\", height: \"100%\", boxSizing: \"border-box\" }}>\n <div style={{ fontSize: 13, fontWeight: 600, color: \"var(--fcms-fg)\" }}>{node.type}</div>\n <div style={{ fontSize: 11, color: \"var(--fcms-muted)\", marginBottom: 8 }}>{node.id}</div>\n\n {node.type === \"text\" ? (\n <>\n <label style={label} htmlFor={fieldId(\"content\")}>Content</label>\n <textarea\n id={fieldId(\"content\")}\n data-cms-field=\"content\"\n style={{ ...input, minHeight: 64, resize: \"vertical\" }}\n value={typeof node.props.content === \"string\" ? node.props.content : \"\"}\n onChange={(e) => setProp(\"content\", e.target.value)}\n />\n </>\n ) : null}\n\n {node.type === \"image\" ? (\n <>\n <label style={label} htmlFor={fieldId(\"src\")}>Source</label>\n <input\n id={fieldId(\"src\")}\n data-cms-field=\"src\"\n style={input}\n value={typeof node.props.src === \"string\" ? node.props.src : \"\"}\n onChange={(e) => setProp(\"src\", e.target.value)}\n />\n </>\n ) : null}\n\n <label style={label} htmlFor={fieldId(\"background\")}>Background</label>\n <input\n id={fieldId(\"background\")}\n data-cms-field=\"background\"\n style={input}\n placeholder=\"#ffffff | gradient | url(…)\"\n value={s.background ?? \"\"}\n onChange={(e) => setStyle({ background: e.target.value })}\n />\n\n <label style={label} htmlFor={fieldId(\"color\")}>Text color</label>\n <input\n id={fieldId(\"color\")}\n data-cms-field=\"color\"\n style={input}\n placeholder=\"#0f172a\"\n value={s.color ?? \"\"}\n onChange={(e) => setStyle({ color: e.target.value })}\n />\n\n <label style={label} htmlFor={fieldId(\"fontSize\")}>Font size (px)</label>\n <input\n id={fieldId(\"fontSize\")}\n data-cms-field=\"fontSize\"\n type=\"number\"\n style={input}\n value={s.fontSize?.value ?? \"\"}\n onChange={(e) => setStyle({ fontSize: { value: Number(e.target.value) || 0, unit: \"px\" } })}\n />\n\n <label style={label} htmlFor={fieldId(\"padding\")}>Padding (px)</label>\n <input\n id={fieldId(\"padding\")}\n data-cms-field=\"padding\"\n type=\"number\"\n style={input}\n value={s.padding?.value ?? \"\"}\n onChange={(e) => setStyle({ padding: { value: Number(e.target.value) || 0, unit: \"px\" } })}\n />\n\n <label style={label} htmlFor={fieldId(\"opacity\")}>Opacity</label>\n <input\n id={fieldId(\"opacity\")}\n data-cms-field=\"opacity\"\n type=\"number\"\n step=\"0.1\"\n min=\"0\"\n max=\"1\"\n style={input}\n value={s.opacity ?? \"\"}\n onChange={(e) => setStyle({ opacity: Number(e.target.value) })}\n />\n </div>\n );\n}\n","import type { Json, LayoutMode, NodeId, PageDoc, StyleProps } from \"../document/types\";\nimport type { PageOp } from \"../document/ops\";\nimport { childrenOf } from \"../document/reduce\";\nimport { keyBetween } from \"../document/fractional\";\n\n/**\n * Element insertion — shared by `EditablePage` (inline editing) and `Editor`\n * (the standalone WYSIWYG shell).\n *\n * This used to live entirely inside EditablePage, which meant the embeddable\n * `Editor` had no way to add an element at all: starting from `emptyDoc()` gave\n * a blank canvas with nothing to select and no control to insert anything, so\n * a host embedding `Editor` could not author a page from scratch (#3).\n * EditablePage had the menu but no `onChange` to hand the document back —\n * each surface had half of what an embedding host needs.\n */\n\n/** Node types that accept children, so an insert lands *inside* them. */\nexport const CONTAINER_TYPES = new Set([\n \"section\",\n \"frame\",\n \"stack\",\n \"grid\",\n \"shape\",\n \"card\",\n \"device\",\n \"repeater\",\n]);\n\nexport type AddKind =\n | \"text\"\n | \"heading\"\n | \"button\"\n | \"link\"\n | \"image\"\n | \"box\"\n | \"stack\"\n | \"grid\"\n | \"card\"\n | \"callout\"\n | \"divider\"\n | \"code\"\n | \"richtext\"\n | \"repeater\";\n\nexport const ADD_DEFAULTS: Record<\n AddKind,\n { type: string; props: Record<string, Json>; style: StyleProps; layout?: LayoutMode }\n> = {\n text: { type: \"text\", props: { content: \"New text\" }, style: { color: \"inherit\" } },\n heading: { type: \"heading\", props: { content: \"New heading\" }, style: { fontSize: { value: 28, unit: \"px\" }, fontWeight: 700 } },\n button: { type: \"button\", props: { label: \"Button\", href: \"#\", variant: \"primary\" }, style: {} },\n link: { type: \"link\", props: { content: \"link text\", href: \"#\" }, style: { color: \"#7c3aed\" } },\n image: { type: \"image\", props: { src: \"\", alt: \"\" }, style: {} },\n box: { type: \"frame\", props: {}, style: { padding: { value: 16, unit: \"px\" } } },\n stack: { type: \"stack\", props: {}, style: { gap: { value: 12, unit: \"px\" } }, layout: \"stack\" },\n grid: { type: \"grid\", props: {}, style: { gap: { value: 12, unit: \"px\" } }, layout: \"grid\" },\n card: { type: \"card\", props: {}, style: { padding: { value: 16, unit: \"px\" }, radius: { value: 12, unit: \"px\" }, border: \"1px solid #e2e8f0\" } },\n callout: { type: \"callout\", props: { content: \"Heads up — this is a callout.\", variant: \"info\" }, style: {} },\n divider: { type: \"divider\", props: {}, style: {} },\n code: { type: \"code\", props: { content: \"npm install @particle-academy/react-fancy\", lang: \"bash\" }, style: {} },\n richtext: { type: \"richtext\", props: { html: \"<p>Rich <strong>text</strong> with <em>inline</em> formatting.</p>\" }, style: {} },\n repeater: { type: \"repeater\", props: { items: \"\" }, style: { gap: { value: 12, unit: \"px\" } }, layout: \"stack\" },\n};\n\nexport const ADD_MENU: Array<{ kind: AddKind; label: string }> = [\n { kind: \"heading\", label: \"Heading\" },\n { kind: \"text\", label: \"Text\" },\n { kind: \"button\", label: \"Button\" },\n { kind: \"link\", label: \"Link\" },\n { kind: \"image\", label: \"Image\" },\n { kind: \"card\", label: \"Card\" },\n { kind: \"callout\", label: \"Callout\" },\n { kind: \"stack\", label: \"Stack\" },\n { kind: \"grid\", label: \"Grid\" },\n { kind: \"box\", label: \"Box\" },\n { kind: \"divider\", label: \"Divider\" },\n { kind: \"code\", label: \"Code\" },\n { kind: \"richtext\", label: \"Rich text\" },\n { kind: \"repeater\", label: \"Repeater\" },\n];\n\n/**\n * Where a new node of `kind` should land, given the current selection.\n *\n * Into the selected container; else alongside the selected node; else the last\n * section — and when the page has no sections at all, at the top level, which\n * is what makes authoring from `emptyDoc()` possible.\n */\nexport function resolveInsertParent(doc: PageDoc, selectedId: NodeId | null): NodeId | null {\n const selected = selectedId ? doc.nodes[selectedId] : null;\n if (selected) {\n return CONTAINER_TYPES.has(selected.type) ? selected.id : selected.parent;\n }\n return doc.sections[doc.sections.length - 1] ?? null;\n}\n\n/**\n * Build the `insert_node` op for adding `kind` to `doc`.\n *\n * Pure — returns the op plus the new id so the caller can select it. Callers\n * apply the op through their own editor state.\n */\nexport function buildInsertOp(\n doc: PageDoc,\n kind: AddKind,\n selectedId: NodeId | null,\n /** Explicit target, overriding selection — used by drag-and-drop. */\n targetId?: NodeId | null,\n): { op: PageOp; id: NodeId } {\n const parent =\n targetId !== undefined\n ? targetId && doc.nodes[targetId]\n ? CONTAINER_TYPES.has(doc.nodes[targetId]!.type)\n ? targetId\n : doc.nodes[targetId]!.parent\n : null\n : resolveInsertParent(doc, selectedId);\n\n const siblings = childrenOf(doc, parent);\n const order = keyBetween(siblings.length ? siblings[siblings.length - 1]!.order : null, null);\n const id = `n${doc.seq + 1}-${Math.floor(performance.now())}`;\n const def = ADD_DEFAULTS[kind];\n\n return {\n id,\n op: {\n t: \"insert_node\",\n node: {\n id,\n type: def.type,\n parent,\n order,\n layout: def.layout,\n props: { ...def.props },\n style: { base: { ...def.style } },\n },\n } as PageOp,\n };\n}\n","import { useEffect, useRef, type RefObject } from \"react\";\n\n/**\n * Hold the latest `value` in a ref, synced in an effect after every render.\n *\n * Notify effects read `ref.current` instead of closing over a callback prop, so\n * their dependency arrays stay data-only (`doc`, `selection`, …). A consumer\n * passing an inline closure — new identity every render, the most natural\n * usage — can then never re-trigger the notify: without this, notify → consumer\n * setState → render → new closure identity → effect re-fires → notify → … loops\n * unbounded (#1). Standard uncontrolled-with-notify hygiene.\n */\nexport function useLatestRef<T>(value: T): RefObject<T> {\n const ref = useRef(value);\n useEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n","import { useEffect, useMemo, useRef, useState, type CSSProperties, type ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport type { DataContext, ElementRegistry } from \"../react/registry\";\nimport { useEditor, type EditorApi } from \"./useEditor\";\nimport { useLatestRef } from \"./useLatestRef\";\nimport { Canvas } from \"./Canvas\";\nimport { LayersPanel } from \"./LayersPanel\";\nimport { Inspector } from \"./Inspector\";\nimport { ADD_MENU, buildInsertOp, type AddKind } from \"./insert\";\nimport { emptyDoc } from \"../document/types\";\n\nexport interface EditorProps {\n /**\n * Initial document; edits are surfaced via {@link EditorProps.onChange}.\n * Omit to start from an empty page.\n */\n defaultValue?: PageDoc;\n onChange?: (doc: PageDoc) => void;\n /**\n * Custom element registry — mirrors {@link CmsPageProps.registry} so the edit\n * canvas renders your own node types instead of a blank placeholder. Pass the\n * same registry you give `CmsPage` at runtime.\n */\n registry?: ElementRegistry;\n /** Data context that `{ $bind: \"…\" }` props + repeaters preview against on the canvas. */\n data?: DataContext;\n}\n\n/**\n * The fancy-cms editor: layers · canvas · inspector over the op-spine.\n *\n * The chrome is plain markup **deliberately**, and stays that way. It themes\n * through the `--fcms-*` layer defined at the bottom of this file — light by\n * default, dark under `prefers-color-scheme` or a `.dark` ancestor — and\n * react-fancy's primitives are hardcoded Tailwind palette classes that read no\n * custom properties, so swapping them in would ignore `--fcms-accent` and strand\n * a themed host with a half-themed editor.\n *\n * An earlier revision of this comment said the chrome \"graduates to react-fancy\n * next\". That predates the token layer by three weeks and is no longer the\n * plan; it is recorded here because it is the kind of note that gets acted on\n * long after it stopped being true.\n */\nexport function Editor({ defaultValue, onChange, registry, data }: EditorProps): ReactElement {\n // Render an empty page rather than throwing on a missing document. Reading\n // `.sections` off undefined crashed the entire editor, which is a hostile way\n // to tell a host \"you haven't passed a doc yet\" (#3).\n const initial = useMemo(() => defaultValue ?? emptyDoc(\"untitled\"), [defaultValue]);\n const ed = useEditor(initial);\n const first = useRef(true);\n\n // Notify depends ONLY on the doc — the latest onChange lives in a ref, so an\n // inline-closure consumer (whose handler identity changes every render) can't\n // re-trigger the notify and loop it (#1).\n const onChangeRef = useLatestRef(onChange);\n useEffect(() => {\n if (first.current) {\n first.current = false;\n return;\n }\n onChangeRef.current?.(ed.state.doc);\n }, [ed.state.doc, onChangeRef]);\n\n const shell: CSSProperties = {\n display: \"grid\",\n gridTemplateColumns: \"220px 1fr 300px\",\n height: \"100%\",\n minHeight: 0,\n background: \"var(--fcms-bg)\",\n color: \"var(--fcms-fg)\",\n border: \"1px solid var(--fcms-border)\",\n borderRadius: 12,\n overflow: \"hidden\",\n };\n\n return (\n <div className=\"fancy-cms-editor\" style={shell}>\n <style>{CHROME_CSS}</style>\n <div style={{ borderRight: \"1px solid var(--fcms-border)\", display: \"flex\", flexDirection: \"column\", minHeight: 0 }}>\n <Toolbar ed={ed} />\n <LayersPanel\n doc={ed.state.doc}\n selection={ed.state.selection}\n onSelect={ed.select}\n onMove={(id, parent, order) => ed.apply({ t: \"move_node\", id, parent, order })}\n />\n </div>\n <Canvas doc={ed.state.doc} selection={ed.state.selection} onSelect={ed.select} apply={ed.apply} registry={registry} data={data} />\n <div style={{ borderLeft: \"1px solid var(--fcms-border)\", minHeight: 0 }}>\n <Inspector doc={ed.state.doc} selection={ed.state.selection} apply={ed.apply} />\n </div>\n </div>\n );\n}\n\nfunction Toolbar({ ed }: { ed: EditorApi }): ReactElement {\n const btn: CSSProperties = {\n font: \"inherit\",\n fontSize: 12,\n padding: \"4px 10px\",\n borderRadius: 6,\n border: \"1px solid var(--fcms-border)\",\n background: \"var(--fcms-bg)\",\n color: \"var(--fcms-fg)\",\n cursor: \"pointer\",\n };\n return (\n <div\n style={{\n display: \"flex\",\n gap: 6,\n padding: 8,\n borderBottom: \"1px solid var(--fcms-border)\",\n fontFamily: \"system-ui, sans-serif\",\n }}\n >\n <button type=\"button\" style={{ ...btn, opacity: ed.canUndo ? 1 : 0.4 }} disabled={!ed.canUndo} onClick={ed.undo}>\n Undo\n </button>\n <button type=\"button\" style={{ ...btn, opacity: ed.canRedo ? 1 : 0.4 }} disabled={!ed.canRedo} onClick={ed.redo}>\n Redo\n </button>\n <AddElementMenu ed={ed} btn={btn} />\n </div>\n );\n}\n\n/**\n * Insert control for the standalone editor.\n *\n * Without this, `Editor` could not add an element at all: a host starting from\n * `emptyDoc()` got a blank canvas with nothing to select and no way to put\n * anything on it (#3). The palette itself was only ever wired into\n * `EditablePage`, which has no `onChange` to hand the document back — so\n * neither surface alone was embeddable.\n */\nfunction AddElementMenu({ ed, btn }: { ed: EditorApi; btn: CSSProperties }): ReactElement {\n const [open, setOpen] = useState(false);\n\n // Dismiss on any outside click or Escape.\n useEffect(() => {\n if (!open) return;\n const close = () => setOpen(false);\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && setOpen(false);\n window.addEventListener(\"click\", close);\n window.addEventListener(\"keydown\", onKey);\n return () => {\n window.removeEventListener(\"click\", close);\n window.removeEventListener(\"keydown\", onKey);\n };\n }, [open]);\n\n const add = (kind: AddKind) => {\n const { op, id } = buildInsertOp(ed.state.doc, kind, ed.state.selection);\n ed.apply(op);\n ed.select(id);\n setOpen(false);\n };\n\n return (\n <div style={{ position: \"relative\", marginLeft: \"auto\" }} onClick={(e) => e.stopPropagation()}>\n <button\n type=\"button\"\n style={{ ...btn, borderColor: \"var(--fcms-accent, #7c3aed)\" }}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n data-cms-add-trigger=\"\"\n onClick={() => setOpen((v) => !v)}\n >\n + Add\n </button>\n {open && (\n <div\n role=\"menu\"\n data-cms-add-menu=\"\"\n style={{\n position: \"absolute\",\n top: \"calc(100% + 4px)\",\n left: 0,\n zIndex: 20,\n minWidth: 150,\n maxHeight: 280,\n overflowY: \"auto\",\n padding: 4,\n borderRadius: 8,\n border: \"1px solid var(--fcms-border)\",\n background: \"var(--fcms-bg)\",\n boxShadow: \"0 12px 32px rgba(0,0,0,.28)\",\n }}\n >\n {ADD_MENU.map((item) => (\n <button\n key={item.kind}\n type=\"button\"\n role=\"menuitem\"\n data-cms-add={item.kind}\n style={{\n ...btn,\n display: \"block\",\n width: \"100%\",\n textAlign: \"left\",\n border: 0,\n background: \"transparent\",\n }}\n onClick={() => add(item.kind)}\n >\n {item.label}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n/**\n * Editor chrome tokens. Light by default; dark under `@media (prefers-color-scheme:\n * dark)` OR an ancestor `.dark` / `[data-theme=\"dark\"]` (Tailwind/class strategy),\n * with an explicit `.light` / `[data-theme=\"light\"]` ancestor forcing light back.\n * The panels below read `var(--fcms-*)`, so the whole editor embeds cleanly in a\n * dark app. `color-scheme` flips native inputs + scrollbars too.\n */\nconst DARK_VARS =\n \"color-scheme:dark;--fcms-bg:#0b0f19;--fcms-fg:#e5e7eb;--fcms-muted:#94a3b8;--fcms-border:#27272a;\" +\n \"--fcms-canvas:#0f141f;--fcms-input-bg:#111827;--fcms-row-fg:#cbd5e1;\" +\n \"--fcms-sel-bg:color-mix(in oklab, #8b5cf6 24%, transparent);--fcms-sel-fg:#ddd6fe;\" +\n \"--fcms-drop:#ec4899;--fcms-danger:#fca5a5;\";\nconst LIGHT_VARS =\n \"color-scheme:light;--fcms-bg:#ffffff;--fcms-fg:#0f172a;--fcms-muted:#64748b;--fcms-border:#e2e8f0;\" +\n \"--fcms-canvas:#f8fafc;--fcms-input-bg:#ffffff;--fcms-row-fg:#334155;--fcms-sel-bg:#ede9fe;--fcms-sel-fg:#5b21b6;\" +\n \"--fcms-drop:#db2777;--fcms-danger:#dc2626;\";\nconst CHROME_CSS =\n `.fancy-cms-editor{--fcms-accent:#8b5cf6;${LIGHT_VARS}}` +\n `@media (prefers-color-scheme:dark){.fancy-cms-editor{${DARK_VARS}}}` +\n `:where(.light,[data-theme=\"light\"]) .fancy-cms-editor{${LIGHT_VARS}}` +\n `:where(.dark,[data-theme=\"dark\"]) .fancy-cms-editor{${DARK_VARS}}`;\n"]}
|