@particle-academy/fancy-cms-ui 0.0.1-dev.6 → 0.0.1-dev.8

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.cjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  var react = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
+ var reactFancy = require('@particle-academy/react-fancy');
5
6
 
6
7
  // src/editor/Editor.tsx
7
8
 
@@ -233,8 +234,8 @@ var DEFAULT_BREAKPOINTS = {
233
234
 
234
235
  // src/render/css.ts
235
236
  var selectorFor = (id) => `[data-cms="${id}"]`;
236
- function lengthToCss(len) {
237
- return `${len.value}${len.unit}`;
237
+ function lengthToCss(len2) {
238
+ return `${len2.value}${len2.unit}`;
238
239
  }
239
240
  function sizeToCss(size) {
240
241
  if (size === "fill") return "100%";
@@ -321,9 +322,9 @@ function isParentFree(doc, node) {
321
322
  function serializeRule(selector, decls, indent) {
322
323
  const props = Object.keys(decls).sort();
323
324
  if (props.length === 0) return "";
324
- const body = props.map((p) => `${indent} ${p}: ${decls[p]};`).join("\n");
325
+ const body2 = props.map((p) => `${indent} ${p}: ${decls[p]};`).join("\n");
325
326
  return `${indent}${selector} {
326
- ${body}
327
+ ${body2}
327
328
  ${indent}}`;
328
329
  }
329
330
  function emitDocCss(doc) {
@@ -368,6 +369,12 @@ var defaultRegistry = {
368
369
  frame: ({ children }) => children,
369
370
  shape: ({ children }) => children,
370
371
  text: ({ node }) => literal(node.props.content),
372
+ heading: ({ node }) => literal(node.props.content) || "Heading",
373
+ button: ({ node }) => {
374
+ const label2 = literal(node.props.label) || literal(node.props.content) || "Button";
375
+ const variant = literal(node.props.variant) || "primary";
376
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cms-button": variant, style: buttonStyle(variant), children: label2 });
377
+ },
371
378
  image: ({ node }) => /* @__PURE__ */ jsxRuntime.jsx(
372
379
  "img",
373
380
  {
@@ -377,6 +384,23 @@ var defaultRegistry = {
377
384
  }
378
385
  )
379
386
  };
387
+ function buttonStyle(variant) {
388
+ const base = {
389
+ display: "inline-flex",
390
+ alignItems: "center",
391
+ gap: 6,
392
+ padding: "9px 16px",
393
+ borderRadius: 8,
394
+ fontWeight: 600,
395
+ fontSize: 14,
396
+ lineHeight: 1,
397
+ cursor: "pointer",
398
+ userSelect: "none"
399
+ };
400
+ if (variant === "ghost") return { ...base, background: "transparent", color: "inherit", border: "1px solid currentColor" };
401
+ if (variant === "outline") return { ...base, background: "transparent", color: "#7c3aed", border: "1px solid #7c3aed" };
402
+ return { ...base, background: "#7c3aed", color: "#fff", border: "1px solid #7c3aed" };
403
+ }
380
404
  function RenderNode({ doc, id, registry = defaultRegistry }) {
381
405
  const node = doc.nodes[id];
382
406
  if (!node) return null;
@@ -702,13 +726,345 @@ function Toolbar({ ed }) {
702
726
  var BASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
703
727
  var RADIX = BASE.length;
704
728
  var MID = Math.floor(RADIX / 2);
729
+ function val(c) {
730
+ const i = BASE.indexOf(c);
731
+ return i < 0 ? 0 : i;
732
+ }
705
733
  function keyBetween(a, b) {
706
734
  if (a === null) a = "";
707
- {
735
+ if (b === null) {
708
736
  return a + BASE[MID];
709
737
  }
738
+ if (a >= b) {
739
+ throw new Error(`keyBetween: lower bound "${a}" is not below upper bound "${b}"`);
740
+ }
741
+ let result = "";
742
+ let i = 0;
743
+ for (; ; ) {
744
+ const ca = i < a.length ? val(a[i]) : 0;
745
+ const cb = i < b.length ? val(b[i]) : RADIX;
746
+ if (cb - ca > 1) {
747
+ result += BASE[ca + Math.floor((cb - ca) / 2)];
748
+ return result;
749
+ }
750
+ if (ca === cb) {
751
+ result += BASE[ca];
752
+ i++;
753
+ continue;
754
+ }
755
+ result += BASE[ca];
756
+ i++;
757
+ for (; ; ) {
758
+ const da = i < a.length ? val(a[i]) : 0;
759
+ if (RADIX - da > 1) {
760
+ result += BASE[da + Math.floor((RADIX - da) / 2)];
761
+ return result;
762
+ }
763
+ result += BASE[da];
764
+ i++;
765
+ }
766
+ }
710
767
  }
768
+ var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
769
+ function NodeInspector({
770
+ node,
771
+ transform,
772
+ measured,
773
+ onProps,
774
+ onStyle,
775
+ onTransform,
776
+ onRemove,
777
+ onClose
778
+ }) {
779
+ const base = node.style.base ?? {};
780
+ const setT = (patch) => onTransform({ ...transform, ...patch });
781
+ const handled = /* @__PURE__ */ new Set(["content", "label"]);
782
+ const extraKeys = Object.keys(node.props).filter(
783
+ (k) => !handled.has(k) && ["string", "number", "boolean"].includes(typeof node.props[k])
784
+ );
785
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: panel, onPointerDown: (e) => e.stopPropagation(), children: [
786
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headRow, children: [
787
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 2, minWidth: 0 }, children: [
788
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { style: { fontSize: 12, textTransform: "capitalize" }, children: node.type }),
789
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, opacity: 0.5, fontFamily: "ui-monospace, monospace", overflow: "hidden", textOverflow: "ellipsis" }, children: node.id })
790
+ ] }),
791
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1 } }),
792
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...iconBtn, color: "#fca5a5" }, title: "Delete element", onClick: onRemove, children: "\u{1F5D1}" }),
793
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: iconBtn, title: "Close", onClick: onClose, children: "\u2715" })
794
+ ] }),
795
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: body, children: [
796
+ "content" in node.props ? /* @__PURE__ */ jsxRuntime.jsx(Group, { label: "Content", children: /* @__PURE__ */ jsxRuntime.jsx(
797
+ "textarea",
798
+ {
799
+ style: { ...input2, minHeight: 64, resize: "vertical", lineHeight: 1.4 },
800
+ value: String(node.props.content ?? ""),
801
+ onChange: (e) => onProps({ content: e.target.value })
802
+ }
803
+ ) }) : null,
804
+ "label" in node.props ? /* @__PURE__ */ jsxRuntime.jsx(Group, { label: "Label", children: /* @__PURE__ */ jsxRuntime.jsx("input", { style: input2, value: String(node.props.label ?? ""), onChange: (e) => onProps({ label: e.target.value }) }) }) : null,
805
+ extraKeys.length ? /* @__PURE__ */ jsxRuntime.jsx(Group, { label: "Properties", children: extraKeys.map((k) => /* @__PURE__ */ jsxRuntime.jsx(PropField, { name: k, value: node.props[k], onChange: (v) => onProps({ [k]: v }) }, k)) }) : null,
806
+ /* @__PURE__ */ jsxRuntime.jsxs(Group, { label: "Position & size", children: [
807
+ /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
808
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "X", value: transform.x, placeholder: "0", onChange: (v) => setT({ x: v }) }),
809
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Y", value: transform.y, placeholder: "0", onChange: (v) => setT({ y: v }) })
810
+ ] }),
811
+ /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
812
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "W", value: transform.w, placeholder: `${Math.round(measured.w)}`, onChange: (v) => setT({ w: v }) }),
813
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "H", value: transform.h, placeholder: `${Math.round(measured.h)}`, onChange: (v) => setT({ h: v }) })
814
+ ] }),
815
+ /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
816
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Scale", value: transform.scale, placeholder: "1", step: 0.05, onChange: (v) => setT({ scale: v }) }),
817
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Rotate", value: transform.rotate, placeholder: "0", onChange: (v) => setT({ rotate: v }) })
818
+ ] }),
819
+ /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
820
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Opacity", value: transform.opacity, placeholder: "1", min: 0, max: 1, step: 0.05, onChange: (v) => setT({ opacity: v }) }),
821
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...miniBtn, alignSelf: "end" }, onClick: () => onTransform({}), title: "Clear transform", children: "Reset" })
822
+ ] })
823
+ ] }),
824
+ !CONTAINER_TYPES.has(node.type) ? /* @__PURE__ */ jsxRuntime.jsxs(Group, { label: "Text", children: [
825
+ /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
826
+ /* @__PURE__ */ jsxRuntime.jsx(Color, { label: "Color", value: base.color ?? "#0f172a", onChange: (v) => onStyle({ color: v }) }),
827
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Size", value: base.fontSize?.value, placeholder: "16", onChange: (v) => onStyle({ fontSize: v == null ? void 0 : len(v) }) })
828
+ ] }),
829
+ /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
830
+ /* @__PURE__ */ jsxRuntime.jsx(
831
+ Sel,
832
+ {
833
+ label: "Weight",
834
+ value: String(base.fontWeight ?? ""),
835
+ options: [["", "\u2014"], ["400", "Regular"], ["500", "Medium"], ["600", "Semibold"], ["700", "Bold"], ["800", "Heavy"]],
836
+ onChange: (v) => onStyle({ fontWeight: v ? Number(v) : void 0 })
837
+ }
838
+ ),
839
+ /* @__PURE__ */ jsxRuntime.jsx(
840
+ Sel,
841
+ {
842
+ label: "Align",
843
+ value: base.textAlign ?? "",
844
+ options: [["", "\u2014"], ["left", "Left"], ["center", "Center"], ["right", "Right"], ["justify", "Justify"]],
845
+ onChange: (v) => onStyle({ textAlign: v || void 0 })
846
+ }
847
+ )
848
+ ] })
849
+ ] }) : null,
850
+ /* @__PURE__ */ jsxRuntime.jsxs(Group, { label: "Box", children: [
851
+ /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
852
+ /* @__PURE__ */ jsxRuntime.jsx(Color, { label: "Background", value: hex(base.background), onChange: (v) => onStyle({ background: v }) }),
853
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Radius", value: base.radius?.value, placeholder: "0", onChange: (v) => onStyle({ radius: v == null ? void 0 : len(v) }) })
854
+ ] }),
855
+ /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
856
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Padding", value: base.padding?.value, placeholder: "0", onChange: (v) => onStyle({ padding: v == null ? void 0 : len(v) }) }),
857
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Gap", value: base.gap?.value, placeholder: "0", onChange: (v) => onStyle({ gap: v == null ? void 0 : len(v) }) })
858
+ ] })
859
+ ] })
860
+ ] })
861
+ ] });
862
+ }
863
+ function Group({ label: label2, children }) {
864
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, paddingBottom: 12, marginBottom: 12, borderBottom: "1px solid #1e293b" }, children: [
865
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase", opacity: 0.5 }, children: label2 }),
866
+ children
867
+ ] });
868
+ }
869
+ function Row({ children }) {
870
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: 8 }, children });
871
+ }
872
+ function PropField({ name, value, onChange }) {
873
+ if (typeof value === "boolean") {
874
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, fontSize: 12 }, children: [
875
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: value, onChange: (e) => onChange(e.target.checked) }),
876
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { textTransform: "capitalize" }, children: name })
877
+ ] });
878
+ }
879
+ if (name === "variant") {
880
+ return /* @__PURE__ */ jsxRuntime.jsx(Sel, { label: name, value: String(value), options: [["primary", "Primary"], ["ghost", "Ghost"], ["outline", "Outline"]], onChange });
881
+ }
882
+ const isNum = typeof value === "number";
883
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
884
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: fieldLabel, children: name }),
885
+ /* @__PURE__ */ jsxRuntime.jsx(
886
+ "input",
887
+ {
888
+ style: input2,
889
+ type: isNum ? "number" : "text",
890
+ value: String(value),
891
+ onChange: (e) => onChange(isNum ? Number(e.target.value) : e.target.value)
892
+ }
893
+ )
894
+ ] });
895
+ }
896
+ function Num({ label: label2, value, placeholder, onChange, step, min, max }) {
897
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
898
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: fieldLabel, children: label2 }),
899
+ /* @__PURE__ */ jsxRuntime.jsx(
900
+ "input",
901
+ {
902
+ style: input2,
903
+ type: "number",
904
+ step,
905
+ min,
906
+ max,
907
+ value: value ?? "",
908
+ placeholder,
909
+ onChange: (e) => onChange(e.target.value === "" ? void 0 : Number(e.target.value))
910
+ }
911
+ )
912
+ ] });
913
+ }
914
+ function Color({ label: label2, value, onChange }) {
915
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
916
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: fieldLabel, children: label2 }),
917
+ /* @__PURE__ */ jsxRuntime.jsx("input", { style: { ...input2, padding: 2, height: 30 }, type: "color", value, onChange: (e) => onChange(e.target.value) })
918
+ ] });
919
+ }
920
+ function Sel({ label: label2, value, options, onChange }) {
921
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
922
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: fieldLabel, children: label2 }),
923
+ /* @__PURE__ */ jsxRuntime.jsx("select", { style: input2, value, onChange: (e) => onChange(e.target.value), children: options.map(([v, l]) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: v, children: l }, v)) })
924
+ ] });
925
+ }
926
+ var len = (value) => ({ value, unit: "px" });
927
+ var hex = (v) => v && /^#[0-9a-fA-F]{6}$/.test(v) ? v : "#ffffff";
711
928
  var Z = 2147483e3;
929
+ var panel = {
930
+ position: "fixed",
931
+ top: 60,
932
+ right: 12,
933
+ bottom: 96,
934
+ width: 264,
935
+ display: "flex",
936
+ flexDirection: "column",
937
+ background: "#0b1220",
938
+ color: "#e2e8f0",
939
+ border: "1px solid #1e293b",
940
+ borderRadius: 12,
941
+ boxShadow: "0 18px 48px -16px rgba(0,0,0,0.6)",
942
+ zIndex: Z + 2,
943
+ fontFamily: "system-ui, sans-serif",
944
+ overflow: "hidden"
945
+ };
946
+ var headRow = { display: "flex", alignItems: "center", gap: 8, padding: "10px 12px", borderBottom: "1px solid #1e293b" };
947
+ var body = { padding: 12, overflowY: "auto" };
948
+ var field = { display: "flex", flexDirection: "column", gap: 4, flex: 1, minWidth: 0 };
949
+ var fieldLabel = { fontSize: 10, opacity: 0.6 };
950
+ var input2 = {
951
+ width: "100%",
952
+ boxSizing: "border-box",
953
+ background: "#0f172a",
954
+ color: "#e2e8f0",
955
+ border: "1px solid #334155",
956
+ borderRadius: 6,
957
+ padding: "5px 7px",
958
+ font: "inherit",
959
+ fontSize: 12
960
+ };
961
+ var iconBtn = { background: "transparent", border: "none", color: "#e2e8f0", cursor: "pointer", fontSize: 13, padding: 4, borderRadius: 6 };
962
+ var miniBtn = { font: "inherit", fontSize: 11, color: "#e2e8f0", background: "#334155", border: "1px solid #475569", borderRadius: 6, padding: "5px 10px", cursor: "pointer" };
963
+
964
+ // src/editor/editorOps.ts
965
+ function childrenOfMap(nodes, parent) {
966
+ return Object.values(nodes).filter((n) => n.parent === parent).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
967
+ }
968
+ function subtreeBfs(nodes, root) {
969
+ const out = [];
970
+ const queue = [root];
971
+ while (queue.length) {
972
+ const cur = queue.shift();
973
+ out.push(cur);
974
+ for (const child of childrenOfMap(nodes, cur)) queue.push(child.id);
975
+ }
976
+ return out;
977
+ }
978
+ function cloneFrom(nodes, rootId, newParent, newOrder, seqBase) {
979
+ if (!nodes[rootId]) return { ops: [], newRootId: null };
980
+ const order = subtreeBfs(nodes, rootId);
981
+ const idMap = {};
982
+ order.forEach((old, i) => idMap[old] = `${old}_c${seqBase}_${i}`);
983
+ const ops = order.map((old) => {
984
+ const src = nodes[old];
985
+ const cloned = JSON.parse(JSON.stringify(src));
986
+ cloned.id = idMap[old];
987
+ cloned.parent = old === rootId ? newParent : idMap[src.parent];
988
+ if (old === rootId) cloned.order = newOrder;
989
+ return { t: "insert_node", node: cloned };
990
+ });
991
+ return { ops, newRootId: idMap[rootId] };
992
+ }
993
+ function duplicateOps(doc, id) {
994
+ const node = doc.nodes[id];
995
+ if (!node) return { ops: [], newRootId: null };
996
+ const sibs = childrenOf(doc, node.parent);
997
+ const idx = sibs.findIndex((s) => s.id === id);
998
+ const newOrder = keyBetween(node.order, sibs[idx + 1]?.order ?? null);
999
+ const { ops, newRootId } = cloneFrom(doc.nodes, id, node.parent, newOrder, doc.seq);
1000
+ if (node.parent === null && newRootId) {
1001
+ const base = doc.sections;
1002
+ const at = base.indexOf(id);
1003
+ ops.push({ t: "reorder_sections", order: [...base.slice(0, at + 1), newRootId, ...base.slice(at + 1)] });
1004
+ }
1005
+ return { ops, newRootId };
1006
+ }
1007
+ function pasteOps(doc, clip, target, containerTypes) {
1008
+ const sel = target ? doc.nodes[target] : null;
1009
+ const parent = sel ? containerTypes.has(sel.type) ? sel.id : sel.parent : doc.sections[doc.sections.length - 1] ?? null;
1010
+ const sibs = childrenOf(doc, parent);
1011
+ const newOrder = keyBetween(sibs.length ? sibs[sibs.length - 1].order : null, null);
1012
+ const { ops, newRootId } = cloneFrom(clip.nodes, clip.rootId, parent, newOrder, doc.seq);
1013
+ return { ops, newRootId };
1014
+ }
1015
+ function reorderOps(doc, id, dir) {
1016
+ const node = doc.nodes[id];
1017
+ if (!node) return [];
1018
+ if (node.parent === null) {
1019
+ const s = [...doc.sections];
1020
+ const i = s.indexOf(id);
1021
+ if (i < 0) return [];
1022
+ s.splice(i, 1);
1023
+ if (dir === "front") s.unshift(id);
1024
+ else if (dir === "back") s.push(id);
1025
+ else s.splice(Math.max(0, Math.min(s.length, dir === "up" ? i - 1 : i + 1)), 0, id);
1026
+ return [{ t: "reorder_sections", order: s }];
1027
+ }
1028
+ const sibs = childrenOf(doc, node.parent);
1029
+ const idx = sibs.findIndex((sb) => sb.id === id);
1030
+ if (idx < 0) return [];
1031
+ const last = sibs.length - 1;
1032
+ let newOrder;
1033
+ if (dir === "up") {
1034
+ if (idx <= 0) return [];
1035
+ newOrder = keyBetween(sibs[idx - 2]?.order ?? null, sibs[idx - 1].order);
1036
+ } else if (dir === "down") {
1037
+ if (idx >= last) return [];
1038
+ newOrder = keyBetween(sibs[idx + 1].order, sibs[idx + 2]?.order ?? null);
1039
+ } else if (dir === "front") {
1040
+ if (idx >= last) return [];
1041
+ newOrder = keyBetween(sibs[last].order, null);
1042
+ } else {
1043
+ if (idx <= 0) return [];
1044
+ newOrder = keyBetween(null, sibs[0].order);
1045
+ }
1046
+ return [{ t: "move_node", id, parent: node.parent, order: newOrder }];
1047
+ }
1048
+ function wrapInBoxOps(doc, id) {
1049
+ const node = doc.nodes[id];
1050
+ if (!node || node.parent === null) return { ops: [], newRootId: null };
1051
+ const frameId = `box_${doc.seq}`;
1052
+ return {
1053
+ ops: [
1054
+ { t: "insert_node", node: { id: frameId, type: "frame", parent: node.parent, order: node.order, props: {}, style: { base: { padding: { value: 16, unit: "px" } } } } },
1055
+ { t: "move_node", id, parent: frameId, order: "a" }
1056
+ ],
1057
+ newRootId: frameId
1058
+ };
1059
+ }
1060
+ function snapshotSubtree(doc, id) {
1061
+ if (!doc.nodes[id]) return null;
1062
+ const ids = subtreeBfs(doc.nodes, id);
1063
+ const nodes = {};
1064
+ for (const nid of ids) nodes[nid] = JSON.parse(JSON.stringify(doc.nodes[nid]));
1065
+ return { rootId: id, nodes };
1066
+ }
1067
+ var Z2 = 2147483e3;
712
1068
  function EditablePage({
713
1069
  doc,
714
1070
  registry = defaultRegistry,
@@ -717,7 +1073,8 @@ function EditablePage({
717
1073
  onNodeTransform,
718
1074
  onSelect,
719
1075
  frames = 1,
720
- onProgress
1076
+ onProgress,
1077
+ pinned = true
721
1078
  }) {
722
1079
  const ed = useEditor(doc);
723
1080
  const [editing, setEditing] = react.useState(false);
@@ -746,22 +1103,16 @@ function EditablePage({
746
1103
  root.querySelectorAll("[data-cms]").forEach((el) => {
747
1104
  const id = el.dataset.cms;
748
1105
  const t = id ? transforms?.[id] : void 0;
749
- if (t) {
750
- el.style.transform = `translate3d(${t.x ?? 0}px, ${t.y ?? 0}px, 0) scale(${t.scale ?? 1})`;
751
- el.style.opacity = t.opacity != null ? String(t.opacity) : "";
752
- } else {
753
- el.style.transform = "";
754
- el.style.opacity = "";
755
- }
1106
+ applyTransform(el, t);
756
1107
  });
757
1108
  }, [transforms, ed.state.doc]);
758
1109
  react.useEffect(() => {
759
1110
  const spacer = spacerRef.current;
760
1111
  if (!spacer || !onProgress) return;
761
1112
  const compute = () => {
762
- const len = spacer.offsetHeight - window.innerHeight;
1113
+ const len2 = spacer.offsetHeight - window.innerHeight;
763
1114
  const top = spacer.getBoundingClientRect().top;
764
- onProgress(len > 0 ? Math.min(1, Math.max(0, -top / len)) : 0);
1115
+ onProgress(len2 > 0 ? Math.min(1, Math.max(0, -top / len2)) : 0);
765
1116
  };
766
1117
  compute();
767
1118
  window.addEventListener("scroll", compute, { passive: true });
@@ -786,7 +1137,7 @@ function EditablePage({
786
1137
  window.removeEventListener("scroll", measureBox);
787
1138
  window.removeEventListener("resize", measureBox);
788
1139
  };
789
- }, [measureBox, ed.state.doc, transforms]);
1140
+ }, [measureBox, ed.state.doc]);
790
1141
  react.useEffect(() => {
791
1142
  if (!editing || !selection || !pageRef.current || selNode?.type !== "text") return;
792
1143
  const el = pageRef.current.querySelector(`[data-cms="${cssEscape2(selection)}"]`);
@@ -811,17 +1162,96 @@ function EditablePage({
811
1162
  ed.select(el.getAttribute("data-cms"));
812
1163
  }
813
1164
  };
814
- const addText = react.useCallback(() => {
815
- const parent = selNode && selNode.type !== "text" ? selNode.id : ed.state.doc.sections[0] ?? null;
816
- const siblings = childrenOf(ed.state.doc, parent);
817
- const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null);
818
- const id = `n${ed.state.doc.seq + 1}-${Math.floor(performance.now())}`;
819
- ed.apply({ t: "insert_node", node: { id, type: "text", parent, order, props: { content: "New text" }, style: { base: { color: "inherit" } } } });
820
- ed.select(id);
821
- }, [ed, selNode]);
1165
+ const addNode = react.useCallback(
1166
+ (kind) => {
1167
+ const doc2 = ed.state.doc;
1168
+ const parent = selNode ? CONTAINER_TYPES2.has(selNode.type) ? selNode.id : selNode.parent : doc2.sections[doc2.sections.length - 1] ?? null;
1169
+ const siblings = childrenOf(doc2, parent);
1170
+ const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
1171
+ const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
1172
+ const def = ADD_DEFAULTS[kind];
1173
+ ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, props: { ...def.props }, style: { base: { ...def.style } } } });
1174
+ ed.select(id);
1175
+ },
1176
+ [ed, selNode]
1177
+ );
822
1178
  const setStyle = (patch) => selection && ed.apply({ t: "set_style", id: selection, breakpoint: "base", patch });
1179
+ const setProps = (patch) => selection && ed.apply({ t: "set_props", id: selection, patch });
1180
+ const removeSelected = () => {
1181
+ if (!selection) return;
1182
+ ed.apply({ t: "remove_node", id: selection });
1183
+ ed.select(null);
1184
+ };
1185
+ const clip = react.useRef(null);
1186
+ const runOps = (result) => {
1187
+ result.ops.forEach((op) => ed.apply(op));
1188
+ if (result.newRootId) ed.select(result.newRootId);
1189
+ };
1190
+ const duplicate = (id) => id && runOps(duplicateOps(ed.state.doc, id));
1191
+ const reorder = (id, dir) => id && reorderOps(ed.state.doc, id, dir).forEach((op) => ed.apply(op));
1192
+ const wrapInBox = (id) => id && runOps(wrapInBoxOps(ed.state.doc, id));
1193
+ const copy = (id) => {
1194
+ if (id) clip.current = snapshotSubtree(ed.state.doc, id);
1195
+ };
1196
+ const paste = (target) => {
1197
+ if (clip.current) runOps(pasteOps(ed.state.doc, clip.current, target, CONTAINER_TYPES2));
1198
+ };
1199
+ const startEditText = (id) => {
1200
+ if (!id) return;
1201
+ ed.select(id);
1202
+ requestAnimationFrame(() => pageRef.current?.querySelector(`[data-cms="${cssEscape2(id)}"]`)?.focus());
1203
+ };
1204
+ react.useEffect(() => {
1205
+ if (!editing) return;
1206
+ const onKey = (e) => {
1207
+ const t = e.target;
1208
+ const typing = t && (t.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName));
1209
+ if (e.key === "Escape") return void ed.select(null);
1210
+ if (typing) return;
1211
+ if ((e.key === "Delete" || e.key === "Backspace") && selection) {
1212
+ e.preventDefault();
1213
+ removeSelected();
1214
+ } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "d" && selection) {
1215
+ e.preventDefault();
1216
+ duplicate(selection);
1217
+ } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "c" && selection) {
1218
+ copy(selection);
1219
+ } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "v") {
1220
+ e.preventDefault();
1221
+ paste(selection);
1222
+ }
1223
+ };
1224
+ window.addEventListener("keydown", onKey);
1225
+ return () => window.removeEventListener("keydown", onKey);
1226
+ }, [editing, selection]);
1227
+ const onContextMenuSelect = (e) => {
1228
+ if (!editing) return;
1229
+ const el = e.target.closest("[data-cms]");
1230
+ if (el) ed.select(el.getAttribute("data-cms"));
1231
+ else ed.select(null);
1232
+ };
1233
+ const page = /* @__PURE__ */ jsxRuntime.jsx("div", { ref: pageRef, onClickCapture, onContextMenu: onContextMenuSelect, children: /* @__PURE__ */ jsxRuntime.jsx(CmsPage, { doc: ed.state.doc, registry }) });
1234
+ const pageShell = pinned ? /* @__PURE__ */ jsxRuntime.jsx("div", { ref: spacerRef, style: { height: `${Math.max(1, frames) * 100}vh` }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "sticky", top: 0, height: "100vh", overflow: "hidden" }, children: page }) }) : page;
823
1235
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
824
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: spacerRef, style: { height: `${Math.max(1, frames) * 100}vh` }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "sticky", top: 0, height: "100vh", overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: pageRef, onClickCapture, children: /* @__PURE__ */ jsxRuntime.jsx(CmsPage, { doc: ed.state.doc, registry }) }) }) }),
1236
+ editing ? /* @__PURE__ */ jsxRuntime.jsxs(reactFancy.ContextMenu, { children: [
1237
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Trigger, { children: pageShell }),
1238
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Content, { className: "!z-[2147483646]", children: /* @__PURE__ */ jsxRuntime.jsx(
1239
+ EditMenuItems,
1240
+ {
1241
+ node: selNode ?? null,
1242
+ hasClipboard: Boolean(clip.current),
1243
+ isContainer: selNode ? CONTAINER_TYPES2.has(selNode.type) : false,
1244
+ onEditText: () => startEditText(selection),
1245
+ onAdd: addNode,
1246
+ onDuplicate: () => duplicate(selection),
1247
+ onCopy: () => copy(selection),
1248
+ onPaste: () => paste(selection),
1249
+ onWrap: () => wrapInBox(selection),
1250
+ onReorder: (d) => reorder(selection, d),
1251
+ onDelete: removeSelected
1252
+ }
1253
+ ) })
1254
+ ] }) : pageShell,
825
1255
  editing && box && selection ? /* @__PURE__ */ jsxRuntime.jsx(
826
1256
  SelectionOverlay,
827
1257
  {
@@ -833,7 +1263,20 @@ function EditablePage({
833
1263
  onLive: measureBox
834
1264
  }
835
1265
  ) : null,
836
- editing && box && selNode ? /* @__PURE__ */ jsxRuntime.jsx(FloatingToolbar, { box, isText: selNode.type === "text", base: selNode.style.base, setStyle }) : null,
1266
+ editing && box && selNode ? /* @__PURE__ */ jsxRuntime.jsx(FloatingToolbar, { box, isText: selNode.type === "text" || selNode.type === "heading", base: selNode.style.base, setStyle }) : null,
1267
+ editing && selNode && box ? /* @__PURE__ */ jsxRuntime.jsx(
1268
+ NodeInspector,
1269
+ {
1270
+ node: selNode,
1271
+ transform: (selection ? transforms?.[selection] : void 0) ?? {},
1272
+ measured: { w: box.w, h: box.h },
1273
+ onProps: setProps,
1274
+ onStyle: setStyle,
1275
+ onTransform: (t) => selection && onNodeTransform?.(selection, t),
1276
+ onRemove: removeSelected,
1277
+ onClose: () => ed.select(null)
1278
+ }
1279
+ ) : null,
837
1280
  revealed || editing ? /* @__PURE__ */ jsxRuntime.jsx(
838
1281
  EditBar,
839
1282
  {
@@ -846,12 +1289,30 @@ function EditablePage({
846
1289
  canRedo: ed.canRedo,
847
1290
  onUndo: ed.undo,
848
1291
  onRedo: ed.redo,
849
- onAddText: addText
1292
+ onAdd: addNode
850
1293
  }
851
1294
  ) : null,
852
- editing && timelineDock ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "fixed", left: 0, right: 0, bottom: 0, zIndex: Z }, children: timelineDock }) : null
1295
+ editing && timelineDock ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "fixed", left: 0, right: 0, bottom: 0, zIndex: Z2 }, children: timelineDock }) : null
853
1296
  ] });
854
1297
  }
1298
+ var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
1299
+ var ADD_DEFAULTS = {
1300
+ text: { type: "text", props: { content: "New text" }, style: { color: "inherit" } },
1301
+ heading: { type: "heading", props: { content: "New heading" }, style: { fontSize: { value: 28, unit: "px" }, fontWeight: 700 } },
1302
+ button: { type: "button", props: { label: "Button", href: "#", variant: "primary" }, style: {} },
1303
+ image: { type: "image", props: { src: "", alt: "" }, style: {} },
1304
+ box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } }
1305
+ };
1306
+ var GRIPS = [
1307
+ { key: "nw", north: true, west: true, cursor: "nwse-resize" },
1308
+ { key: "n", north: true, cursor: "ns-resize" },
1309
+ { key: "ne", north: true, east: true, cursor: "nesw-resize" },
1310
+ { key: "e", east: true, cursor: "ew-resize" },
1311
+ { key: "se", south: true, east: true, cursor: "nwse-resize" },
1312
+ { key: "s", south: true, cursor: "ns-resize" },
1313
+ { key: "sw", south: true, west: true, cursor: "nesw-resize" },
1314
+ { key: "w", west: true, cursor: "ew-resize" }
1315
+ ];
855
1316
  function SelectionOverlay({
856
1317
  box,
857
1318
  base,
@@ -863,29 +1324,48 @@ function SelectionOverlay({
863
1324
  const drag = react.useRef(null);
864
1325
  const apply = (t) => {
865
1326
  const el = getEl();
866
- if (el) {
867
- el.style.transform = `translate3d(${t.x ?? 0}px, ${t.y ?? 0}px, 0) scale(${t.scale ?? 1})`;
868
- el.style.opacity = t.opacity != null ? String(t.opacity) : "";
869
- }
1327
+ if (el) applyTransform(el, t);
870
1328
  };
871
- const start = (kind) => (e) => {
1329
+ const startMove = (e) => {
872
1330
  e.stopPropagation();
873
1331
  e.preventDefault();
874
1332
  e.currentTarget.setPointerCapture(e.pointerId);
875
- drag.current = { kind, x: e.clientX, y: e.clientY, w: box.w, live: { ...base } };
1333
+ drag.current = { x: e.clientX, y: e.clientY, w: box.w, h: box.h, live: { ...base } };
876
1334
  };
877
- const move = (e) => {
1335
+ const moveMove = (e) => {
878
1336
  const d = drag.current;
879
1337
  if (!d) return;
880
- if (d.kind === "move") {
881
- d.live = { ...base, x: (base.x ?? 0) + (e.clientX - d.x), y: (base.y ?? 0) + (e.clientY - d.y) };
882
- } else {
883
- const factor = Math.max(0.1, (d.w + (e.clientX - d.x)) / d.w);
884
- d.live = { ...base, scale: Number(((base.scale ?? 1) * factor).toFixed(3)) };
885
- }
1338
+ d.live = { ...base, x: (base.x ?? 0) + (e.clientX - d.x), y: (base.y ?? 0) + (e.clientY - d.y) };
886
1339
  apply(d.live);
887
1340
  onLive();
888
1341
  };
1342
+ const startResize = (g) => (e) => {
1343
+ e.stopPropagation();
1344
+ e.preventDefault();
1345
+ e.currentTarget.setPointerCapture(e.pointerId);
1346
+ drag.current = { x: e.clientX, y: e.clientY, w: base.w ?? box.w, h: base.h ?? box.h, live: { ...base } };
1347
+ e.currentTarget.dataset.grip = g.key;
1348
+ };
1349
+ const resizeMove = (g) => (e) => {
1350
+ const d = drag.current;
1351
+ if (!d) return;
1352
+ const dx = e.clientX - d.x;
1353
+ const dy = e.clientY - d.y;
1354
+ const live = { ...base };
1355
+ if (g.east) live.w = Math.max(16, Math.round(d.w + dx));
1356
+ if (g.west) {
1357
+ live.w = Math.max(16, Math.round(d.w - dx));
1358
+ live.x = (base.x ?? 0) + (d.w - live.w);
1359
+ }
1360
+ if (g.south) live.h = Math.max(16, Math.round(d.h + dy));
1361
+ if (g.north) {
1362
+ live.h = Math.max(16, Math.round(d.h - dy));
1363
+ live.y = (base.y ?? 0) + (d.h - live.h);
1364
+ }
1365
+ d.live = live;
1366
+ apply(live);
1367
+ onLive();
1368
+ };
889
1369
  const end = () => {
890
1370
  const d = drag.current;
891
1371
  if (d) onTransform(d.live);
@@ -900,17 +1380,23 @@ function SelectionOverlay({
900
1380
  outline: "2px solid #8b5cf6",
901
1381
  borderRadius: 4,
902
1382
  pointerEvents: "none",
903
- zIndex: Z
1383
+ zIndex: Z2
904
1384
  };
905
1385
  const handle = {
906
1386
  position: "fixed",
907
- width: 16,
908
- height: 16,
909
- background: "#8b5cf6",
910
- border: "2px solid #fff",
911
- borderRadius: 5,
912
- zIndex: Z + 1,
913
- touchAction: "none"
1387
+ width: 12,
1388
+ height: 12,
1389
+ background: "#fff",
1390
+ border: "2px solid #8b5cf6",
1391
+ borderRadius: 3,
1392
+ zIndex: Z2 + 1,
1393
+ touchAction: "none",
1394
+ boxSizing: "border-box"
1395
+ };
1396
+ const gripPos = (g) => {
1397
+ const cx = g.west ? box.x : g.east ? box.x + box.w : box.x + box.w / 2;
1398
+ const cy = g.north ? box.y : g.south ? box.y + box.h : box.y + box.h / 2;
1399
+ return { left: cx - 6, top: cy - 6, cursor: g.cursor };
914
1400
  };
915
1401
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
916
1402
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: outline }),
@@ -919,22 +1405,23 @@ function SelectionOverlay({
919
1405
  "div",
920
1406
  {
921
1407
  title: "Drag to move",
922
- onPointerDown: start("move"),
923
- onPointerMove: move,
1408
+ onPointerDown: startMove,
1409
+ onPointerMove: moveMove,
924
1410
  onPointerUp: end,
925
- style: { ...handle, left: box.x - 9, top: box.y - 9, cursor: "move", borderRadius: 9 }
1411
+ style: { ...handle, width: 18, height: 18, borderRadius: 9, background: "#8b5cf6", border: "2px solid #fff", left: box.x - 22, top: box.y - 22, cursor: "move" }
926
1412
  }
927
1413
  ),
928
- /* @__PURE__ */ jsxRuntime.jsx(
1414
+ GRIPS.map((g) => /* @__PURE__ */ jsxRuntime.jsx(
929
1415
  "div",
930
1416
  {
931
1417
  title: "Drag to resize",
932
- onPointerDown: start("resize"),
933
- onPointerMove: move,
1418
+ onPointerDown: startResize(g),
1419
+ onPointerMove: resizeMove(g),
934
1420
  onPointerUp: end,
935
- style: { ...handle, left: box.x + box.w - 7, top: box.y + box.h - 7, cursor: "nwse-resize" }
936
- }
937
- )
1421
+ style: { ...handle, ...gripPos(g) }
1422
+ },
1423
+ g.key
1424
+ ))
938
1425
  ] }) : null
939
1426
  ] });
940
1427
  }
@@ -957,7 +1444,7 @@ function FloatingToolbar({
957
1444
  border: "1px solid rgba(255,255,255,0.12)",
958
1445
  borderRadius: 999,
959
1446
  boxShadow: "0 8px 24px -8px rgba(0,0,0,0.5)",
960
- zIndex: Z + 2,
1447
+ zIndex: Z2 + 2,
961
1448
  fontFamily: "system-ui, sans-serif",
962
1449
  fontSize: 12
963
1450
  };
@@ -975,6 +1462,13 @@ function FloatingToolbar({
975
1462
  ] })
976
1463
  ] });
977
1464
  }
1465
+ var ADD_MENU = [
1466
+ { kind: "text", label: "Text" },
1467
+ { kind: "heading", label: "Heading" },
1468
+ { kind: "button", label: "Button" },
1469
+ { kind: "image", label: "Image" },
1470
+ { kind: "box", label: "Box" }
1471
+ ];
978
1472
  function EditBar({
979
1473
  editing,
980
1474
  onToggle,
@@ -982,19 +1476,87 @@ function EditBar({
982
1476
  canRedo,
983
1477
  onUndo,
984
1478
  onRedo,
985
- onAddText
1479
+ onAdd
986
1480
  }) {
987
- const bar = { position: "fixed", top: 12, right: 12, display: "flex", gap: 6, alignItems: "center", padding: 6, background: "#0f172a", color: "#fff", borderRadius: 999, boxShadow: "0 8px 24px -8px rgba(0,0,0,0.4)", zIndex: Z + 3, fontFamily: "system-ui, sans-serif", fontSize: 12 };
1481
+ const [open, setOpen] = react.useState(false);
1482
+ const bar = { position: "fixed", top: 12, right: 12, display: "flex", gap: 6, alignItems: "center", padding: 6, background: "#0f172a", color: "#fff", borderRadius: 999, boxShadow: "0 8px 24px -8px rgba(0,0,0,0.4)", zIndex: Z2 + 3, fontFamily: "system-ui, sans-serif", fontSize: 12 };
988
1483
  const btn = { font: "inherit", border: "none", background: "transparent", color: "inherit", cursor: "pointer", padding: "4px 8px", borderRadius: 999 };
989
1484
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: bar, children: [
990
1485
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, background: editing ? "#8b5cf6" : "transparent" }, onClick: onToggle, children: editing ? "\u25CF Editing" : "Edit" }),
991
1486
  editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
992
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onClick: onAddText, title: "Add a text element", children: "\uFF0B Text" }),
1487
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
1488
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, background: open ? "#1e293b" : "transparent" }, onClick: () => setOpen((v) => !v), title: "Add an element", children: "\uFF0B Add \u25BE" }),
1489
+ open ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", top: "calc(100% + 6px)", right: 0, background: "#0f172a", border: "1px solid #1e293b", borderRadius: 10, padding: 4, display: "flex", flexDirection: "column", minWidth: 132, boxShadow: "0 12px 32px -10px rgba(0,0,0,0.6)" }, children: ADD_MENU.map((m) => /* @__PURE__ */ jsxRuntime.jsx(
1490
+ "button",
1491
+ {
1492
+ type: "button",
1493
+ style: { ...btn, textAlign: "left", borderRadius: 6, padding: "6px 10px" },
1494
+ onClick: () => {
1495
+ onAdd(m.kind);
1496
+ setOpen(false);
1497
+ },
1498
+ children: m.label
1499
+ },
1500
+ m.kind
1501
+ )) }) : null
1502
+ ] }),
993
1503
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: canUndo ? 1 : 0.4 }, disabled: !canUndo, onClick: onUndo, children: "\u21B6" }),
994
1504
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: canRedo ? 1 : 0.4 }, disabled: !canRedo, onClick: onRedo, children: "\u21B7" })
995
1505
  ] }) : null
996
1506
  ] });
997
1507
  }
1508
+ function EditMenuItems({
1509
+ node,
1510
+ hasClipboard,
1511
+ isContainer,
1512
+ onEditText,
1513
+ onAdd,
1514
+ onDuplicate,
1515
+ onCopy,
1516
+ onPaste,
1517
+ onWrap,
1518
+ onReorder,
1519
+ onDelete
1520
+ }) {
1521
+ const editable = node && (node.type === "text" || node.type === "heading");
1522
+ const nested = Boolean(node && node.parent !== null);
1523
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1524
+ editable ? /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: onEditText, children: "Edit text" }) : null,
1525
+ /* @__PURE__ */ jsxRuntime.jsxs(reactFancy.ContextMenu.Sub, { children: [
1526
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.SubTrigger, { children: isContainer ? "Add inside" : "Add" }),
1527
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.SubContent, { className: "!z-[2147483647]", children: ADD_MENU.map((m) => /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: () => onAdd(m.kind), children: m.label }, m.kind)) })
1528
+ ] }),
1529
+ node ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1530
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Separator, {}),
1531
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: onDuplicate, children: "Duplicate" }),
1532
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: onCopy, children: "Copy" }),
1533
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { disabled: !hasClipboard, onClick: onPaste, children: "Paste" }),
1534
+ nested ? /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: onWrap, children: "Wrap in Box" }) : null,
1535
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Separator, {}),
1536
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: () => onReorder("up"), children: "Move up" }),
1537
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: () => onReorder("down"), children: "Move down" }),
1538
+ nested ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1539
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: () => onReorder("front"), children: "Bring to front" }),
1540
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: () => onReorder("back"), children: "Send to back" })
1541
+ ] }) : null,
1542
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Separator, {}),
1543
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { danger: true, onClick: onDelete, children: "Delete" })
1544
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { disabled: !hasClipboard, onClick: onPaste, children: "Paste" })
1545
+ ] });
1546
+ }
1547
+ function applyTransform(el, t) {
1548
+ if (t && (t.x != null || t.y != null || t.scale != null || t.rotate != null || t.opacity != null || t.w != null || t.h != null)) {
1549
+ el.style.transform = `translate3d(${t.x ?? 0}px, ${t.y ?? 0}px, 0) scale(${t.scale ?? 1}) rotate(${t.rotate ?? 0}deg)`;
1550
+ el.style.opacity = t.opacity != null ? String(t.opacity) : "";
1551
+ el.style.width = t.w != null ? `${t.w}px` : "";
1552
+ el.style.height = t.h != null ? `${t.h}px` : "";
1553
+ } else {
1554
+ el.style.transform = "";
1555
+ el.style.opacity = "";
1556
+ el.style.width = "";
1557
+ el.style.height = "";
1558
+ }
1559
+ }
998
1560
  function normalizeColor(v) {
999
1561
  return v && /^#[0-9a-fA-F]{6}$/.test(v) ? v : "#ffffff";
1000
1562
  }
@@ -1007,6 +1569,7 @@ exports.EditablePage = EditablePage;
1007
1569
  exports.Editor = Editor;
1008
1570
  exports.Inspector = Inspector;
1009
1571
  exports.LayersPanel = LayersPanel;
1572
+ exports.NodeInspector = NodeInspector;
1010
1573
  exports.editorReduce = editorReduce;
1011
1574
  exports.initEditor = initEditor;
1012
1575
  exports.useEditor = useEditor;