@particle-academy/fancy-cms-ui 0.0.1-dev.1 → 0.0.1-dev.3

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/editor.js CHANGED
@@ -1,5 +1,6 @@
1
- import { CmsPage } from './chunk-MRWUUNJR.js';
2
- import { reduce, childrenOf } from './chunk-NWH2AJLH.js';
1
+ import { keyBetween } from './chunk-7YYLYBLI.js';
2
+ import { CmsPage, defaultRegistry } from './chunk-5PWZGIYZ.js';
3
+ import { reduce, childrenOf } from './chunk-HYHNMW47.js';
3
4
  import { useReducer, useCallback, useMemo, useRef, useState, useLayoutEffect, Fragment, useEffect } from 'react';
4
5
  import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
5
6
 
@@ -363,7 +364,216 @@ function Toolbar({ ed }) {
363
364
  }
364
365
  );
365
366
  }
367
+ var Z = 2147483e3;
368
+ function EditablePage({
369
+ doc,
370
+ registry = defaultRegistry,
371
+ timelineDock
372
+ }) {
373
+ const ed = useEditor(doc);
374
+ const [editing, setEditing] = useState(false);
375
+ const [revealed, setRevealed] = useState(false);
376
+ const pageRef = useRef(null);
377
+ const [box, setBox] = useState(null);
378
+ const { selection } = ed.state;
379
+ const selNode = selection ? ed.state.doc.nodes[selection] : null;
380
+ useEffect(() => {
381
+ const onKey = (e) => setRevealed(e.ctrlKey && e.shiftKey);
382
+ const onBlur = () => setRevealed(false);
383
+ window.addEventListener("keydown", onKey);
384
+ window.addEventListener("keyup", onKey);
385
+ window.addEventListener("blur", onBlur);
386
+ return () => {
387
+ window.removeEventListener("keydown", onKey);
388
+ window.removeEventListener("keyup", onKey);
389
+ window.removeEventListener("blur", onBlur);
390
+ };
391
+ }, []);
392
+ useLayoutEffect(() => {
393
+ if (!editing || !selection || !pageRef.current) {
394
+ setBox(null);
395
+ return;
396
+ }
397
+ const update = () => {
398
+ const el = pageRef.current?.querySelector(`[data-cms="${cssEscape2(selection)}"]`);
399
+ if (!el) return setBox(null);
400
+ const r = el.getBoundingClientRect();
401
+ setBox({ x: r.left, y: r.top, w: r.width, h: r.height });
402
+ };
403
+ update();
404
+ window.addEventListener("scroll", update, { passive: true });
405
+ window.addEventListener("resize", update);
406
+ return () => {
407
+ window.removeEventListener("scroll", update);
408
+ window.removeEventListener("resize", update);
409
+ };
410
+ }, [editing, selection, ed.state.doc]);
411
+ useEffect(() => {
412
+ if (!editing || !selection || !pageRef.current) return;
413
+ if (selNode?.type !== "text") return;
414
+ const el = pageRef.current.querySelector(`[data-cms="${cssEscape2(selection)}"]`);
415
+ if (!el) return;
416
+ el.setAttribute("contenteditable", "true");
417
+ el.style.outline = "none";
418
+ el.focus();
419
+ const commit = () => {
420
+ ed.apply({ t: "set_props", id: selection, patch: { content: el.innerText } });
421
+ };
422
+ el.addEventListener("blur", commit);
423
+ return () => {
424
+ el.removeEventListener("blur", commit);
425
+ el.removeAttribute("contenteditable");
426
+ };
427
+ }, [editing, selection, selNode?.type, ed]);
428
+ const onClickCapture = (e) => {
429
+ if (!editing) return;
430
+ const el = e.target.closest("[data-cms]");
431
+ if (el) {
432
+ if (el.getAttribute("contenteditable") === "true") return;
433
+ e.preventDefault();
434
+ e.stopPropagation();
435
+ ed.select(el.getAttribute("data-cms"));
436
+ }
437
+ };
438
+ const addText = useCallback(() => {
439
+ const parent = selNode && selNode.type !== "text" ? selNode.id : ed.state.doc.sections[0] ?? null;
440
+ const siblings = childrenOf(ed.state.doc, parent);
441
+ const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
442
+ const id = `n${ed.state.doc.seq + 1}-${Math.floor(performance.now())}`;
443
+ ed.apply({
444
+ t: "insert_node",
445
+ node: { id, type: "text", parent, order, props: { content: "New text" }, style: { base: { color: "inherit" } } }
446
+ });
447
+ ed.select(id);
448
+ }, [ed, selNode]);
449
+ const setStyle = (patch) => selection && ed.apply({ t: "set_style", id: selection, breakpoint: "base", patch });
450
+ return /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
451
+ /* @__PURE__ */ jsx("div", { ref: pageRef, onClickCapture, children: /* @__PURE__ */ jsx(CmsPage, { doc: ed.state.doc, registry }) }),
452
+ editing && box ? /* @__PURE__ */ jsx(
453
+ "div",
454
+ {
455
+ style: {
456
+ position: "fixed",
457
+ left: box.x - 1,
458
+ top: box.y - 1,
459
+ width: box.w + 2,
460
+ height: box.h + 2,
461
+ outline: "2px solid #8b5cf6",
462
+ borderRadius: 4,
463
+ pointerEvents: "none",
464
+ zIndex: Z
465
+ }
466
+ }
467
+ ) : null,
468
+ editing && box && selNode ? /* @__PURE__ */ jsx(FloatingToolbar, { box, isText: selNode.type === "text", base: selNode.style.base, setStyle }) : null,
469
+ revealed || editing ? /* @__PURE__ */ jsx(
470
+ EditBar,
471
+ {
472
+ editing,
473
+ onToggle: () => {
474
+ setEditing((v) => !v);
475
+ ed.select(null);
476
+ },
477
+ canUndo: ed.canUndo,
478
+ canRedo: ed.canRedo,
479
+ onUndo: ed.undo,
480
+ onRedo: ed.redo,
481
+ onAddText: addText
482
+ }
483
+ ) : null,
484
+ editing && timelineDock ? /* @__PURE__ */ jsx("div", { style: { position: "fixed", left: 0, right: 0, bottom: 0, zIndex: Z }, children: timelineDock }) : null
485
+ ] });
486
+ }
487
+ function FloatingToolbar({
488
+ box,
489
+ isText,
490
+ base,
491
+ setStyle
492
+ }) {
493
+ const top = Math.max(8, box.y - 46);
494
+ const wrap = {
495
+ position: "fixed",
496
+ left: box.x,
497
+ top,
498
+ display: "flex",
499
+ alignItems: "center",
500
+ gap: 8,
501
+ padding: "6px 10px",
502
+ background: "#0f172a",
503
+ color: "#fff",
504
+ border: "1px solid rgba(255,255,255,0.12)",
505
+ borderRadius: 999,
506
+ boxShadow: "0 8px 24px -8px rgba(0,0,0,0.5)",
507
+ zIndex: Z + 2,
508
+ fontFamily: "system-ui, sans-serif",
509
+ fontSize: 12
510
+ };
511
+ const swatch = { width: 22, height: 22, border: "none", background: "transparent", padding: 0, cursor: "pointer" };
512
+ const num = { width: 52, background: "#1e293b", color: "#fff", border: "1px solid #334155", borderRadius: 6, padding: "3px 6px", font: "inherit" };
513
+ return /* @__PURE__ */ jsxs("div", { style: wrap, onMouseDown: (e) => e.preventDefault(), children: [
514
+ isText ? /* @__PURE__ */ jsxs("label", { title: "Text color", style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
515
+ /* @__PURE__ */ jsx("span", { style: { opacity: 0.7 }, children: "A" }),
516
+ /* @__PURE__ */ jsx("input", { type: "color", style: swatch, value: base.color ?? "#0f172a", onChange: (e) => setStyle({ color: e.target.value }) })
517
+ ] }) : null,
518
+ isText ? /* @__PURE__ */ jsx(
519
+ "input",
520
+ {
521
+ type: "number",
522
+ title: "Font size (px)",
523
+ style: num,
524
+ placeholder: "size",
525
+ value: base.fontSize?.value ?? "",
526
+ onChange: (e) => setStyle({ fontSize: { value: Number(e.target.value) || 0, unit: "px" } })
527
+ }
528
+ ) : null,
529
+ /* @__PURE__ */ jsxs("label", { title: "Background", style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
530
+ /* @__PURE__ */ jsx("span", { style: { opacity: 0.7 }, children: "BG" }),
531
+ /* @__PURE__ */ jsx("input", { type: "color", style: swatch, value: normalizeColor(base.background), onChange: (e) => setStyle({ background: e.target.value }) })
532
+ ] })
533
+ ] });
534
+ }
535
+ function EditBar({
536
+ editing,
537
+ onToggle,
538
+ canUndo,
539
+ canRedo,
540
+ onUndo,
541
+ onRedo,
542
+ onAddText
543
+ }) {
544
+ const bar = {
545
+ position: "fixed",
546
+ top: 12,
547
+ right: 12,
548
+ display: "flex",
549
+ gap: 6,
550
+ alignItems: "center",
551
+ padding: 6,
552
+ background: "#0f172a",
553
+ color: "#fff",
554
+ borderRadius: 999,
555
+ boxShadow: "0 8px 24px -8px rgba(0,0,0,0.4)",
556
+ zIndex: Z + 3,
557
+ fontFamily: "system-ui, sans-serif",
558
+ fontSize: 12
559
+ };
560
+ const btn = { font: "inherit", border: "none", background: "transparent", color: "inherit", cursor: "pointer", padding: "4px 8px", borderRadius: 999 };
561
+ return /* @__PURE__ */ jsxs("div", { style: bar, children: [
562
+ /* @__PURE__ */ jsx("button", { type: "button", style: { ...btn, background: editing ? "#8b5cf6" : "transparent" }, onClick: onToggle, children: editing ? "\u25CF Editing" : "Edit" }),
563
+ editing ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
564
+ /* @__PURE__ */ jsx("button", { type: "button", style: btn, onClick: onAddText, title: "Add a text element", children: "\uFF0B Text" }),
565
+ /* @__PURE__ */ jsx("button", { type: "button", style: { ...btn, opacity: canUndo ? 1 : 0.4 }, disabled: !canUndo, onClick: onUndo, children: "\u21B6" }),
566
+ /* @__PURE__ */ jsx("button", { type: "button", style: { ...btn, opacity: canRedo ? 1 : 0.4 }, disabled: !canRedo, onClick: onRedo, children: "\u21B7" })
567
+ ] }) : null
568
+ ] });
569
+ }
570
+ function normalizeColor(v) {
571
+ return v && /^#[0-9a-fA-F]{6}$/.test(v) ? v : "#ffffff";
572
+ }
573
+ function cssEscape2(value) {
574
+ return value.replace(/["\\]/g, "\\$&");
575
+ }
366
576
 
367
- export { Canvas, Editor, Inspector, LayersPanel, editorReduce, initEditor, useEditor };
577
+ export { Canvas, EditablePage, Editor, Inspector, LayersPanel, editorReduce, initEditor, useEditor };
368
578
  //# sourceMappingURL=editor.js.map
369
579
  //# sourceMappingURL=editor.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/editor/state.ts","../src/editor/useEditor.ts","../src/editor/Canvas.tsx","../src/editor/LayersPanel.tsx","../src/editor/Inspector.tsx","../src/editor/Editor.tsx"],"names":["jsx","jsxs","Fragment","useRef"],"mappings":"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","file":"editor.js","sourcesContent":["/**\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 { 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}\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 }: 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: \"#f8fafc\",\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 #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} />\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, type ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { childrenOf } from \"../document/reduce\";\n\nexport interface LayersPanelProps {\n doc: PageDoc;\n selection: string | null;\n onSelect: (id: string) => void;\n}\n\n/** The section/layer tree. Click a row to select. */\nexport function LayersPanel({ doc, selection, onSelect }: LayersPanelProps): ReactElement {\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} />\n ))}\n </div>\n );\n}\n\ninterface LayerRowProps extends LayersPanelProps {\n id: string;\n depth: number;\n}\n\nfunction LayerRow({ doc, id, depth, selection, onSelect }: LayerRowProps): ReactElement | null {\n const node = doc.nodes[id];\n if (!node) return null;\n const selected = selection === id;\n return (\n <Fragment>\n <button\n type=\"button\"\n onClick={() => onSelect(id)}\n style={{\n display: \"block\",\n width: \"100%\",\n textAlign: \"left\",\n border: \"none\",\n borderRadius: 6,\n cursor: \"pointer\",\n padding: \"4px 8px\",\n paddingLeft: 8 + depth * 14,\n background: selected ? \"#ede9fe\" : \"transparent\",\n color: selected ? \"#5b21b6\" : \"#334155\",\n font: \"inherit\",\n }}\n >\n <span style={{ opacity: 0.6 }}>{node.type}</span> · {id}\n </button>\n {childrenOf(doc, id).map((child) => (\n <LayerRow\n key={child.id}\n doc={doc}\n id={child.id}\n depth={depth + 1}\n selection={selection}\n onSelect={onSelect}\n />\n ))}\n </Fragment>\n );\n}\n","import type { CSSProperties, 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: \"#94a3b8\",\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 #e2e8f0\",\n font: \"inherit\",\n fontSize: 13,\n};\n\n/** Contextual property editor for the selected node. Every change is one op. */\nexport function Inspector({ doc, selection, apply }: InspectorProps): ReactElement {\n const node = selection ? doc.nodes[selection] : null;\n if (!node) {\n return (\n <div style={{ padding: 16, color: \"#94a3b8\", 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: \"#0f172a\" }}>{node.type}</div>\n <div style={{ fontSize: 11, color: \"#94a3b8\", marginBottom: 8 }}>{node.id}</div>\n\n {node.type === \"text\" ? (\n <>\n <label style={label}>Content</label>\n <textarea\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}>Source</label>\n <input\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}>Background</label>\n <input\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}>Text color</label>\n <input\n style={input}\n placeholder=\"#0f172a\"\n value={s.color ?? \"\"}\n onChange={(e) => setStyle({ color: e.target.value })}\n />\n\n <label style={label}>Font size (px)</label>\n <input\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}>Padding (px)</label>\n <input\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}>Opacity</label>\n <input\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 { useEffect, useRef, type CSSProperties, type ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { useEditor, type EditorApi } from \"./useEditor\";\nimport { Canvas } from \"./Canvas\";\nimport { LayersPanel } from \"./LayersPanel\";\nimport { Inspector } from \"./Inspector\";\n\nexport interface EditorProps {\n /** Initial document; edits are surfaced via {@link EditorProps.onChange}. */\n defaultValue: PageDoc;\n onChange?: (doc: PageDoc) => void;\n}\n\n/**\n * The fancy-cms editor: layers · canvas · inspector over the op-spine. Phase 1\n * cut — chrome is plain markup for now; it graduates to react-fancy next.\n */\nexport function Editor({ defaultValue, onChange }: EditorProps): ReactElement {\n const ed = useEditor(defaultValue);\n const first = useRef(true);\n\n useEffect(() => {\n if (first.current) {\n first.current = false;\n return;\n }\n onChange?.(ed.state.doc);\n }, [ed.state.doc, onChange]);\n\n const shell: CSSProperties = {\n display: \"grid\",\n gridTemplateColumns: \"220px 1fr 300px\",\n height: \"100%\",\n minHeight: 0,\n background: \"#fff\",\n color: \"#0f172a\",\n border: \"1px solid #e2e8f0\",\n borderRadius: 12,\n overflow: \"hidden\",\n };\n\n return (\n <div style={shell}>\n <div style={{ borderRight: \"1px solid #e2e8f0\", display: \"flex\", flexDirection: \"column\", minHeight: 0 }}>\n <Toolbar ed={ed} />\n <LayersPanel doc={ed.state.doc} selection={ed.state.selection} onSelect={ed.select} />\n </div>\n <Canvas doc={ed.state.doc} selection={ed.state.selection} onSelect={ed.select} apply={ed.apply} />\n <div style={{ borderLeft: \"1px solid #e2e8f0\", 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 #e2e8f0\",\n background: \"#fff\",\n cursor: \"pointer\",\n };\n return (\n <div\n style={{\n display: \"flex\",\n gap: 6,\n padding: 8,\n borderBottom: \"1px solid #e2e8f0\",\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 </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/editor/state.ts","../src/editor/useEditor.ts","../src/editor/Canvas.tsx","../src/editor/LayersPanel.tsx","../src/editor/Inspector.tsx","../src/editor/Editor.tsx","../src/editor/EditablePage.tsx"],"names":["jsx","jsxs","Fragment","useRef","useState","useEffect","useLayoutEffect","cssEscape","useCallback"],"mappings":"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","file":"editor.js","sourcesContent":["/**\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 { 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}\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 }: 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: \"#f8fafc\",\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 #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} />\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, type ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { childrenOf } from \"../document/reduce\";\n\nexport interface LayersPanelProps {\n doc: PageDoc;\n selection: string | null;\n onSelect: (id: string) => void;\n}\n\n/** The section/layer tree. Click a row to select. */\nexport function LayersPanel({ doc, selection, onSelect }: LayersPanelProps): ReactElement {\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} />\n ))}\n </div>\n );\n}\n\ninterface LayerRowProps extends LayersPanelProps {\n id: string;\n depth: number;\n}\n\nfunction LayerRow({ doc, id, depth, selection, onSelect }: LayerRowProps): ReactElement | null {\n const node = doc.nodes[id];\n if (!node) return null;\n const selected = selection === id;\n return (\n <Fragment>\n <button\n type=\"button\"\n onClick={() => onSelect(id)}\n style={{\n display: \"block\",\n width: \"100%\",\n textAlign: \"left\",\n border: \"none\",\n borderRadius: 6,\n cursor: \"pointer\",\n padding: \"4px 8px\",\n paddingLeft: 8 + depth * 14,\n background: selected ? \"#ede9fe\" : \"transparent\",\n color: selected ? \"#5b21b6\" : \"#334155\",\n font: \"inherit\",\n }}\n >\n <span style={{ opacity: 0.6 }}>{node.type}</span> · {id}\n </button>\n {childrenOf(doc, id).map((child) => (\n <LayerRow\n key={child.id}\n doc={doc}\n id={child.id}\n depth={depth + 1}\n selection={selection}\n onSelect={onSelect}\n />\n ))}\n </Fragment>\n );\n}\n","import type { CSSProperties, 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: \"#94a3b8\",\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 #e2e8f0\",\n font: \"inherit\",\n fontSize: 13,\n};\n\n/** Contextual property editor for the selected node. Every change is one op. */\nexport function Inspector({ doc, selection, apply }: InspectorProps): ReactElement {\n const node = selection ? doc.nodes[selection] : null;\n if (!node) {\n return (\n <div style={{ padding: 16, color: \"#94a3b8\", 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: \"#0f172a\" }}>{node.type}</div>\n <div style={{ fontSize: 11, color: \"#94a3b8\", marginBottom: 8 }}>{node.id}</div>\n\n {node.type === \"text\" ? (\n <>\n <label style={label}>Content</label>\n <textarea\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}>Source</label>\n <input\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}>Background</label>\n <input\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}>Text color</label>\n <input\n style={input}\n placeholder=\"#0f172a\"\n value={s.color ?? \"\"}\n onChange={(e) => setStyle({ color: e.target.value })}\n />\n\n <label style={label}>Font size (px)</label>\n <input\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}>Padding (px)</label>\n <input\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}>Opacity</label>\n <input\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 { useEffect, useRef, type CSSProperties, type ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { useEditor, type EditorApi } from \"./useEditor\";\nimport { Canvas } from \"./Canvas\";\nimport { LayersPanel } from \"./LayersPanel\";\nimport { Inspector } from \"./Inspector\";\n\nexport interface EditorProps {\n /** Initial document; edits are surfaced via {@link EditorProps.onChange}. */\n defaultValue: PageDoc;\n onChange?: (doc: PageDoc) => void;\n}\n\n/**\n * The fancy-cms editor: layers · canvas · inspector over the op-spine. Phase 1\n * cut — chrome is plain markup for now; it graduates to react-fancy next.\n */\nexport function Editor({ defaultValue, onChange }: EditorProps): ReactElement {\n const ed = useEditor(defaultValue);\n const first = useRef(true);\n\n useEffect(() => {\n if (first.current) {\n first.current = false;\n return;\n }\n onChange?.(ed.state.doc);\n }, [ed.state.doc, onChange]);\n\n const shell: CSSProperties = {\n display: \"grid\",\n gridTemplateColumns: \"220px 1fr 300px\",\n height: \"100%\",\n minHeight: 0,\n background: \"#fff\",\n color: \"#0f172a\",\n border: \"1px solid #e2e8f0\",\n borderRadius: 12,\n overflow: \"hidden\",\n };\n\n return (\n <div style={shell}>\n <div style={{ borderRight: \"1px solid #e2e8f0\", display: \"flex\", flexDirection: \"column\", minHeight: 0 }}>\n <Toolbar ed={ed} />\n <LayersPanel doc={ed.state.doc} selection={ed.state.selection} onSelect={ed.select} />\n </div>\n <Canvas doc={ed.state.doc} selection={ed.state.selection} onSelect={ed.select} apply={ed.apply} />\n <div style={{ borderLeft: \"1px solid #e2e8f0\", 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 #e2e8f0\",\n background: \"#fff\",\n cursor: \"pointer\",\n };\n return (\n <div\n style={{\n display: \"flex\",\n gap: 6,\n padding: 8,\n borderBottom: \"1px solid #e2e8f0\",\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 </div>\n );\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type MouseEvent as ReactMouseEvent,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport type { PageDoc, StyleProps } from \"../document/types\";\nimport { childrenOf } from \"../document/reduce\";\nimport { keyBetween } from \"../document/fractional\";\nimport { CmsPage } from \"../react/CmsPage\";\nimport { defaultRegistry, type ElementRegistry } from \"../react/registry\";\nimport { useEditor } from \"./useEditor\";\n\nexport interface EditablePageProps {\n /** Seeded document (read-only demo: edits are in-memory, never persisted). */\n doc: PageDoc;\n registry?: ElementRegistry;\n /** Slot for the fancy-motion timeline dock (rendered at the bottom in EditMode). */\n timelineDock?: ReactNode;\n}\n\ninterface Box {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nconst Z = 2147483000;\n\n/**\n * Inline EditMode over a live, seeded CMS page. The Edit affordance is hidden\n * until you hold **Ctrl+Shift** (reveal gesture). Editing is **on the page**:\n * text is edited in place (contentEditable) and a compact **floating toolbar**\n * (dark, dark-mode-safe) sits above the selection for styling — no side flyout.\n * Read-only: edits live in memory, never saved.\n */\nexport function EditablePage({\n doc,\n registry = defaultRegistry,\n timelineDock,\n}: EditablePageProps): ReactElement {\n const ed = useEditor(doc);\n const [editing, setEditing] = useState(false);\n const [revealed, setRevealed] = useState(false);\n const pageRef = useRef<HTMLDivElement>(null);\n const [box, setBox] = useState<Box | null>(null);\n const { selection } = ed.state;\n const selNode = selection ? ed.state.doc.nodes[selection] : null;\n\n // Ctrl+Shift reveal gesture for the Edit affordance.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => setRevealed(e.ctrlKey && e.shiftKey);\n const onBlur = () => setRevealed(false);\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"keyup\", onKey);\n window.addEventListener(\"blur\", onBlur);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"keyup\", onKey);\n window.removeEventListener(\"blur\", onBlur);\n };\n }, []);\n\n // Track the selection box (fixed-positioned, follows scroll/resize).\n useLayoutEffect(() => {\n if (!editing || !selection || !pageRef.current) {\n setBox(null);\n return;\n }\n const update = () => {\n const el = pageRef.current?.querySelector(`[data-cms=\"${cssEscape(selection)}\"]`);\n if (!el) return setBox(null);\n const r = el.getBoundingClientRect();\n setBox({ x: r.left, y: r.top, w: r.width, h: r.height });\n };\n update();\n window.addEventListener(\"scroll\", update, { passive: true });\n window.addEventListener(\"resize\", update);\n return () => {\n window.removeEventListener(\"scroll\", update);\n window.removeEventListener(\"resize\", update);\n };\n }, [editing, selection, ed.state.doc]);\n\n // Inline contentEditable: when a text node is selected, make its element\n // editable in place; commit to the doc on blur (so React doesn't fight the caret).\n useEffect(() => {\n if (!editing || !selection || !pageRef.current) return;\n if (selNode?.type !== \"text\") return;\n const el = pageRef.current.querySelector<HTMLElement>(`[data-cms=\"${cssEscape(selection)}\"]`);\n if (!el) return;\n el.setAttribute(\"contenteditable\", \"true\");\n el.style.outline = \"none\";\n el.focus();\n const commit = () => {\n ed.apply({ t: \"set_props\", id: selection, patch: { content: el.innerText } });\n };\n el.addEventListener(\"blur\", commit);\n return () => {\n el.removeEventListener(\"blur\", commit);\n el.removeAttribute(\"contenteditable\");\n };\n }, [editing, selection, selNode?.type, ed]);\n\n const onClickCapture = (e: ReactMouseEvent<HTMLDivElement>) => {\n if (!editing) return;\n const el = (e.target as HTMLElement).closest(\"[data-cms]\");\n if (el) {\n // let an already-editing text element keep the caret on its own clicks\n if (el.getAttribute(\"contenteditable\") === \"true\") return;\n e.preventDefault();\n e.stopPropagation();\n ed.select(el.getAttribute(\"data-cms\"));\n }\n };\n\n const addText = useCallback(() => {\n const parent = selNode && selNode.type !== \"text\" ? selNode.id : (ed.state.doc.sections[0] ?? null);\n const siblings = childrenOf(ed.state.doc, parent);\n const order = keyBetween(siblings.length ? siblings[siblings.length - 1]!.order : null, null);\n const id = `n${ed.state.doc.seq + 1}-${Math.floor(performance.now())}`;\n ed.apply({\n t: \"insert_node\",\n node: { id, type: \"text\", parent, order, props: { content: \"New text\" }, style: { base: { color: \"inherit\" } } },\n });\n ed.select(id);\n }, [ed, selNode]);\n\n const setStyle = (patch: Partial<StyleProps>) =>\n selection && ed.apply({ t: \"set_style\", id: selection, breakpoint: \"base\", patch });\n\n return (\n <div style={{ position: \"relative\" }}>\n <div ref={pageRef} onClickCapture={onClickCapture}>\n <CmsPage doc={ed.state.doc} registry={registry} />\n </div>\n\n {/* selection outline */}\n {editing && box ? (\n <div\n style={{\n position: \"fixed\",\n left: box.x - 1,\n top: box.y - 1,\n width: box.w + 2,\n height: box.h + 2,\n outline: \"2px solid #8b5cf6\",\n borderRadius: 4,\n pointerEvents: \"none\",\n zIndex: Z,\n }}\n />\n ) : null}\n\n {/* floating toolbar above the selection (dark — dark-mode safe) */}\n {editing && box && selNode ? (\n <FloatingToolbar box={box} isText={selNode.type === \"text\"} base={selNode.style.base} setStyle={setStyle} />\n ) : null}\n\n {/* reveal-gated edit affordance / toolbar */}\n {revealed || editing ? (\n <EditBar\n editing={editing}\n onToggle={() => {\n setEditing((v) => !v);\n ed.select(null);\n }}\n canUndo={ed.canUndo}\n canRedo={ed.canRedo}\n onUndo={ed.undo}\n onRedo={ed.redo}\n onAddText={addText}\n />\n ) : null}\n\n {editing && timelineDock ? (\n <div style={{ position: \"fixed\", left: 0, right: 0, bottom: 0, zIndex: Z }}>{timelineDock}</div>\n ) : null}\n </div>\n );\n}\n\nfunction FloatingToolbar({\n box,\n isText,\n base,\n setStyle,\n}: {\n box: Box;\n isText: boolean;\n base: Partial<StyleProps>;\n setStyle: (patch: Partial<StyleProps>) => void;\n}): ReactElement {\n const top = Math.max(8, box.y - 46);\n const wrap: CSSProperties = {\n position: \"fixed\",\n left: box.x,\n top,\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"6px 10px\",\n background: \"#0f172a\",\n color: \"#fff\",\n border: \"1px solid rgba(255,255,255,0.12)\",\n borderRadius: 999,\n boxShadow: \"0 8px 24px -8px rgba(0,0,0,0.5)\",\n zIndex: Z + 2,\n fontFamily: \"system-ui, sans-serif\",\n fontSize: 12,\n };\n const swatch: CSSProperties = { width: 22, height: 22, border: \"none\", background: \"transparent\", padding: 0, cursor: \"pointer\" };\n const num: CSSProperties = { width: 52, background: \"#1e293b\", color: \"#fff\", border: \"1px solid #334155\", borderRadius: 6, padding: \"3px 6px\", font: \"inherit\" };\n\n return (\n <div style={wrap} onMouseDown={(e) => e.preventDefault() /* keep selection */}>\n {isText ? (\n <label title=\"Text color\" style={{ display: \"inline-flex\", alignItems: \"center\", gap: 4 }}>\n <span style={{ opacity: 0.7 }}>A</span>\n <input type=\"color\" style={swatch} value={base.color ?? \"#0f172a\"} onChange={(e) => setStyle({ color: e.target.value })} />\n </label>\n ) : null}\n {isText ? (\n <input\n type=\"number\"\n title=\"Font size (px)\"\n style={num}\n placeholder=\"size\"\n value={base.fontSize?.value ?? \"\"}\n onChange={(e) => setStyle({ fontSize: { value: Number(e.target.value) || 0, unit: \"px\" } })}\n />\n ) : null}\n <label title=\"Background\" style={{ display: \"inline-flex\", alignItems: \"center\", gap: 4 }}>\n <span style={{ opacity: 0.7 }}>BG</span>\n <input type=\"color\" style={swatch} value={normalizeColor(base.background)} onChange={(e) => setStyle({ background: e.target.value })} />\n </label>\n </div>\n );\n}\n\nfunction EditBar({\n editing,\n onToggle,\n canUndo,\n canRedo,\n onUndo,\n onRedo,\n onAddText,\n}: {\n editing: boolean;\n onToggle: () => void;\n canUndo: boolean;\n canRedo: boolean;\n onUndo: () => void;\n onRedo: () => void;\n onAddText: () => void;\n}): ReactElement {\n const bar: CSSProperties = {\n position: \"fixed\",\n top: 12,\n right: 12,\n display: \"flex\",\n gap: 6,\n alignItems: \"center\",\n padding: 6,\n background: \"#0f172a\",\n color: \"#fff\",\n borderRadius: 999,\n boxShadow: \"0 8px 24px -8px rgba(0,0,0,0.4)\",\n zIndex: Z + 3,\n fontFamily: \"system-ui, sans-serif\",\n fontSize: 12,\n };\n const btn: CSSProperties = { font: \"inherit\", border: \"none\", background: \"transparent\", color: \"inherit\", cursor: \"pointer\", padding: \"4px 8px\", borderRadius: 999 };\n return (\n <div style={bar}>\n <button type=\"button\" style={{ ...btn, background: editing ? \"#8b5cf6\" : \"transparent\" }} onClick={onToggle}>\n {editing ? \"● Editing\" : \"Edit\"}\n </button>\n {editing ? (\n <>\n <button type=\"button\" style={btn} onClick={onAddText} title=\"Add a text element\">\n + Text\n </button>\n <button type=\"button\" style={{ ...btn, opacity: canUndo ? 1 : 0.4 }} disabled={!canUndo} onClick={onUndo}>\n ↶\n </button>\n <button type=\"button\" style={{ ...btn, opacity: canRedo ? 1 : 0.4 }} disabled={!canRedo} onClick={onRedo}>\n ↷\n </button>\n </>\n ) : null}\n </div>\n );\n}\n\nfunction normalizeColor(v: string | undefined): string {\n return v && /^#[0-9a-fA-F]{6}$/.test(v) ? v : \"#ffffff\";\n}\n\nfunction cssEscape(value: string): string {\n return value.replace(/[\"\\\\]/g, \"\\\\$&\");\n}\n"]}