@particle-academy/fancy-cms-ui 0.2.0 → 0.3.1
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/README.md +10 -1
- package/dist/{chunk-XQ3KHMBJ.js → chunk-6Q7DKNDZ.js} +181 -16
- package/dist/chunk-6Q7DKNDZ.js.map +1 -0
- package/dist/editor.cjs +209 -66
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +1 -1
- package/dist/editor.d.ts +1 -1
- package/dist/editor.js +24 -57
- package/dist/editor.js.map +1 -1
- package/dist/index.cjs +181 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -5
- package/dist/index.d.ts +50 -5
- package/dist/index.js +1 -1
- package/dist/{useEditor-CVT8sTum.d.cts → useEditor-BQtAHLHl.d.cts} +30 -5
- package/dist/{useEditor-CV5jFAgo.d.ts → useEditor-CpX-LbfH.d.ts} +30 -5
- package/package.json +1 -1
- package/dist/chunk-XQ3KHMBJ.js.map +0 -1
package/dist/editor.cjs
CHANGED
|
@@ -242,6 +242,17 @@ var DEFAULT_BREAKPOINTS = {
|
|
|
242
242
|
md: 768,
|
|
243
243
|
lg: 1024
|
|
244
244
|
};
|
|
245
|
+
function emptyDoc(id, slug = "/") {
|
|
246
|
+
return {
|
|
247
|
+
id,
|
|
248
|
+
seq: 0,
|
|
249
|
+
meta: { title: "Untitled", slug, scrollMode: "smooth" },
|
|
250
|
+
theme: { name: "default" },
|
|
251
|
+
breakpoints: ["base", "md", "lg"],
|
|
252
|
+
sections: [],
|
|
253
|
+
nodes: {}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
245
256
|
|
|
246
257
|
// src/render/css.ts
|
|
247
258
|
var selectorFor = (id) => `[data-cms="${id}"]`;
|
|
@@ -677,6 +688,8 @@ var input = {
|
|
|
677
688
|
fontSize: 13
|
|
678
689
|
};
|
|
679
690
|
function Inspector({ doc, selection, apply }) {
|
|
691
|
+
const uid = react.useId();
|
|
692
|
+
const fieldId = (key) => `${uid}-${key}`;
|
|
680
693
|
const node = selection ? doc.nodes[selection] : null;
|
|
681
694
|
if (!node) {
|
|
682
695
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 16, color: "var(--fcms-muted)", fontFamily: "system-ui, sans-serif", fontSize: 13 }, children: "Select an element to edit." });
|
|
@@ -688,10 +701,12 @@ function Inspector({ doc, selection, apply }) {
|
|
|
688
701
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, fontWeight: 600, color: "var(--fcms-fg)" }, children: node.type }),
|
|
689
702
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "var(--fcms-muted)", marginBottom: 8 }, children: node.id }),
|
|
690
703
|
node.type === "text" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
691
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, children: "Content" }),
|
|
704
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("content"), children: "Content" }),
|
|
692
705
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
693
706
|
"textarea",
|
|
694
707
|
{
|
|
708
|
+
id: fieldId("content"),
|
|
709
|
+
"data-cms-field": "content",
|
|
695
710
|
style: { ...input, minHeight: 64, resize: "vertical" },
|
|
696
711
|
value: typeof node.props.content === "string" ? node.props.content : "",
|
|
697
712
|
onChange: (e) => setProp("content", e.target.value)
|
|
@@ -699,60 +714,72 @@ function Inspector({ doc, selection, apply }) {
|
|
|
699
714
|
)
|
|
700
715
|
] }) : null,
|
|
701
716
|
node.type === "image" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
702
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, children: "Source" }),
|
|
717
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("src"), children: "Source" }),
|
|
703
718
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
704
719
|
"input",
|
|
705
720
|
{
|
|
721
|
+
id: fieldId("src"),
|
|
722
|
+
"data-cms-field": "src",
|
|
706
723
|
style: input,
|
|
707
724
|
value: typeof node.props.src === "string" ? node.props.src : "",
|
|
708
725
|
onChange: (e) => setProp("src", e.target.value)
|
|
709
726
|
}
|
|
710
727
|
)
|
|
711
728
|
] }) : null,
|
|
712
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, children: "Background" }),
|
|
729
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("background"), children: "Background" }),
|
|
713
730
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
714
731
|
"input",
|
|
715
732
|
{
|
|
733
|
+
id: fieldId("background"),
|
|
734
|
+
"data-cms-field": "background",
|
|
716
735
|
style: input,
|
|
717
736
|
placeholder: "#ffffff | gradient | url(\u2026)",
|
|
718
737
|
value: s.background ?? "",
|
|
719
738
|
onChange: (e) => setStyle({ background: e.target.value })
|
|
720
739
|
}
|
|
721
740
|
),
|
|
722
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, children: "Text color" }),
|
|
741
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("color"), children: "Text color" }),
|
|
723
742
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
724
743
|
"input",
|
|
725
744
|
{
|
|
745
|
+
id: fieldId("color"),
|
|
746
|
+
"data-cms-field": "color",
|
|
726
747
|
style: input,
|
|
727
748
|
placeholder: "#0f172a",
|
|
728
749
|
value: s.color ?? "",
|
|
729
750
|
onChange: (e) => setStyle({ color: e.target.value })
|
|
730
751
|
}
|
|
731
752
|
),
|
|
732
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, children: "Font size (px)" }),
|
|
753
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("fontSize"), children: "Font size (px)" }),
|
|
733
754
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
734
755
|
"input",
|
|
735
756
|
{
|
|
757
|
+
id: fieldId("fontSize"),
|
|
758
|
+
"data-cms-field": "fontSize",
|
|
736
759
|
type: "number",
|
|
737
760
|
style: input,
|
|
738
761
|
value: s.fontSize?.value ?? "",
|
|
739
762
|
onChange: (e) => setStyle({ fontSize: { value: Number(e.target.value) || 0, unit: "px" } })
|
|
740
763
|
}
|
|
741
764
|
),
|
|
742
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, children: "Padding (px)" }),
|
|
765
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("padding"), children: "Padding (px)" }),
|
|
743
766
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
744
767
|
"input",
|
|
745
768
|
{
|
|
769
|
+
id: fieldId("padding"),
|
|
770
|
+
"data-cms-field": "padding",
|
|
746
771
|
type: "number",
|
|
747
772
|
style: input,
|
|
748
773
|
value: s.padding?.value ?? "",
|
|
749
774
|
onChange: (e) => setStyle({ padding: { value: Number(e.target.value) || 0, unit: "px" } })
|
|
750
775
|
}
|
|
751
776
|
),
|
|
752
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, children: "Opacity" }),
|
|
777
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("opacity"), children: "Opacity" }),
|
|
753
778
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
754
779
|
"input",
|
|
755
780
|
{
|
|
781
|
+
id: fieldId("opacity"),
|
|
782
|
+
"data-cms-field": "opacity",
|
|
756
783
|
type: "number",
|
|
757
784
|
step: "0.1",
|
|
758
785
|
min: "0",
|
|
@@ -764,8 +791,82 @@ function Inspector({ doc, selection, apply }) {
|
|
|
764
791
|
)
|
|
765
792
|
] });
|
|
766
793
|
}
|
|
794
|
+
|
|
795
|
+
// src/editor/insert.ts
|
|
796
|
+
var CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
797
|
+
"section",
|
|
798
|
+
"frame",
|
|
799
|
+
"stack",
|
|
800
|
+
"grid",
|
|
801
|
+
"shape",
|
|
802
|
+
"card",
|
|
803
|
+
"device",
|
|
804
|
+
"repeater"
|
|
805
|
+
]);
|
|
806
|
+
var ADD_DEFAULTS = {
|
|
807
|
+
text: { type: "text", props: { content: "New text" }, style: { color: "inherit" } },
|
|
808
|
+
heading: { type: "heading", props: { content: "New heading" }, style: { fontSize: { value: 28, unit: "px" }, fontWeight: 700 } },
|
|
809
|
+
button: { type: "button", props: { label: "Button", href: "#", variant: "primary" }, style: {} },
|
|
810
|
+
link: { type: "link", props: { content: "link text", href: "#" }, style: { color: "#7c3aed" } },
|
|
811
|
+
image: { type: "image", props: { src: "", alt: "" }, style: {} },
|
|
812
|
+
box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } },
|
|
813
|
+
stack: { type: "stack", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "stack" },
|
|
814
|
+
grid: { type: "grid", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "grid" },
|
|
815
|
+
card: { type: "card", props: {}, style: { padding: { value: 16, unit: "px" }, radius: { value: 12, unit: "px" }, border: "1px solid #e2e8f0" } },
|
|
816
|
+
callout: { type: "callout", props: { content: "Heads up \u2014 this is a callout.", variant: "info" }, style: {} },
|
|
817
|
+
divider: { type: "divider", props: {}, style: {} },
|
|
818
|
+
code: { type: "code", props: { content: "npm install @particle-academy/react-fancy", lang: "bash" }, style: {} },
|
|
819
|
+
richtext: { type: "richtext", props: { html: "<p>Rich <strong>text</strong> with <em>inline</em> formatting.</p>" }, style: {} },
|
|
820
|
+
repeater: { type: "repeater", props: { items: "" }, style: { gap: { value: 12, unit: "px" } }, layout: "stack" }
|
|
821
|
+
};
|
|
822
|
+
var ADD_MENU = [
|
|
823
|
+
{ kind: "heading", label: "Heading" },
|
|
824
|
+
{ kind: "text", label: "Text" },
|
|
825
|
+
{ kind: "button", label: "Button" },
|
|
826
|
+
{ kind: "link", label: "Link" },
|
|
827
|
+
{ kind: "image", label: "Image" },
|
|
828
|
+
{ kind: "card", label: "Card" },
|
|
829
|
+
{ kind: "callout", label: "Callout" },
|
|
830
|
+
{ kind: "stack", label: "Stack" },
|
|
831
|
+
{ kind: "grid", label: "Grid" },
|
|
832
|
+
{ kind: "box", label: "Box" },
|
|
833
|
+
{ kind: "divider", label: "Divider" },
|
|
834
|
+
{ kind: "code", label: "Code" },
|
|
835
|
+
{ kind: "richtext", label: "Rich text" },
|
|
836
|
+
{ kind: "repeater", label: "Repeater" }
|
|
837
|
+
];
|
|
838
|
+
function resolveInsertParent(doc, selectedId) {
|
|
839
|
+
const selected = selectedId ? doc.nodes[selectedId] : null;
|
|
840
|
+
if (selected) {
|
|
841
|
+
return CONTAINER_TYPES.has(selected.type) ? selected.id : selected.parent;
|
|
842
|
+
}
|
|
843
|
+
return doc.sections[doc.sections.length - 1] ?? null;
|
|
844
|
+
}
|
|
845
|
+
function buildInsertOp(doc, kind, selectedId, targetId) {
|
|
846
|
+
const parent = resolveInsertParent(doc, selectedId);
|
|
847
|
+
const siblings = childrenOf(doc, parent);
|
|
848
|
+
const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
|
|
849
|
+
const id = `n${doc.seq + 1}-${Math.floor(performance.now())}`;
|
|
850
|
+
const def = ADD_DEFAULTS[kind];
|
|
851
|
+
return {
|
|
852
|
+
id,
|
|
853
|
+
op: {
|
|
854
|
+
t: "insert_node",
|
|
855
|
+
node: {
|
|
856
|
+
id,
|
|
857
|
+
type: def.type,
|
|
858
|
+
parent,
|
|
859
|
+
order,
|
|
860
|
+
layout: def.layout,
|
|
861
|
+
props: { ...def.props },
|
|
862
|
+
style: { base: { ...def.style } }
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
}
|
|
767
867
|
function Editor({ defaultValue, onChange, registry, data }) {
|
|
768
|
-
const
|
|
868
|
+
const initial = react.useMemo(() => defaultValue ?? emptyDoc("untitled"), [defaultValue]);
|
|
869
|
+
const ed = useEditor(initial);
|
|
769
870
|
const first = react.useRef(true);
|
|
770
871
|
const onChangeRef = useLatestRef(onChange);
|
|
771
872
|
react.useEffect(() => {
|
|
@@ -827,15 +928,90 @@ function Toolbar({ ed }) {
|
|
|
827
928
|
},
|
|
828
929
|
children: [
|
|
829
930
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: ed.canUndo ? 1 : 0.4 }, disabled: !ed.canUndo, onClick: ed.undo, children: "Undo" }),
|
|
830
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: ed.canRedo ? 1 : 0.4 }, disabled: !ed.canRedo, onClick: ed.redo, children: "Redo" })
|
|
931
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: ed.canRedo ? 1 : 0.4 }, disabled: !ed.canRedo, onClick: ed.redo, children: "Redo" }),
|
|
932
|
+
/* @__PURE__ */ jsxRuntime.jsx(AddElementMenu, { ed, btn })
|
|
831
933
|
]
|
|
832
934
|
}
|
|
833
935
|
);
|
|
834
936
|
}
|
|
835
|
-
|
|
836
|
-
|
|
937
|
+
function AddElementMenu({ ed, btn }) {
|
|
938
|
+
const [open, setOpen] = react.useState(false);
|
|
939
|
+
react.useEffect(() => {
|
|
940
|
+
if (!open) return;
|
|
941
|
+
const close = () => setOpen(false);
|
|
942
|
+
const onKey = (e) => e.key === "Escape" && setOpen(false);
|
|
943
|
+
window.addEventListener("click", close);
|
|
944
|
+
window.addEventListener("keydown", onKey);
|
|
945
|
+
return () => {
|
|
946
|
+
window.removeEventListener("click", close);
|
|
947
|
+
window.removeEventListener("keydown", onKey);
|
|
948
|
+
};
|
|
949
|
+
}, [open]);
|
|
950
|
+
const add = (kind) => {
|
|
951
|
+
const { op, id } = buildInsertOp(ed.state.doc, kind, ed.state.selection);
|
|
952
|
+
ed.apply(op);
|
|
953
|
+
ed.select(id);
|
|
954
|
+
setOpen(false);
|
|
955
|
+
};
|
|
956
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", marginLeft: "auto" }, onClick: (e) => e.stopPropagation(), children: [
|
|
957
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
958
|
+
"button",
|
|
959
|
+
{
|
|
960
|
+
type: "button",
|
|
961
|
+
style: { ...btn, borderColor: "var(--fcms-accent, #7c3aed)" },
|
|
962
|
+
"aria-haspopup": "menu",
|
|
963
|
+
"aria-expanded": open,
|
|
964
|
+
"data-cms-add-trigger": "",
|
|
965
|
+
onClick: () => setOpen((v) => !v),
|
|
966
|
+
children: "+ Add"
|
|
967
|
+
}
|
|
968
|
+
),
|
|
969
|
+
open && /* @__PURE__ */ jsxRuntime.jsx(
|
|
970
|
+
"div",
|
|
971
|
+
{
|
|
972
|
+
role: "menu",
|
|
973
|
+
"data-cms-add-menu": "",
|
|
974
|
+
style: {
|
|
975
|
+
position: "absolute",
|
|
976
|
+
top: "calc(100% + 4px)",
|
|
977
|
+
left: 0,
|
|
978
|
+
zIndex: 20,
|
|
979
|
+
minWidth: 150,
|
|
980
|
+
maxHeight: 280,
|
|
981
|
+
overflowY: "auto",
|
|
982
|
+
padding: 4,
|
|
983
|
+
borderRadius: 8,
|
|
984
|
+
border: "1px solid var(--fcms-border)",
|
|
985
|
+
background: "var(--fcms-bg)",
|
|
986
|
+
boxShadow: "0 12px 32px rgba(0,0,0,.28)"
|
|
987
|
+
},
|
|
988
|
+
children: ADD_MENU.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
989
|
+
"button",
|
|
990
|
+
{
|
|
991
|
+
type: "button",
|
|
992
|
+
role: "menuitem",
|
|
993
|
+
"data-cms-add": item.kind,
|
|
994
|
+
style: {
|
|
995
|
+
...btn,
|
|
996
|
+
display: "block",
|
|
997
|
+
width: "100%",
|
|
998
|
+
textAlign: "left",
|
|
999
|
+
border: 0,
|
|
1000
|
+
background: "transparent"
|
|
1001
|
+
},
|
|
1002
|
+
onClick: () => add(item.kind),
|
|
1003
|
+
children: item.label
|
|
1004
|
+
},
|
|
1005
|
+
item.kind
|
|
1006
|
+
))
|
|
1007
|
+
}
|
|
1008
|
+
)
|
|
1009
|
+
] });
|
|
1010
|
+
}
|
|
1011
|
+
var DARK_VARS = "color-scheme:dark;--fcms-bg:#0b0f19;--fcms-fg:#e5e7eb;--fcms-muted:#94a3b8;--fcms-border:#27272a;--fcms-canvas:#0f141f;--fcms-input-bg:#111827;--fcms-row-fg:#cbd5e1;--fcms-sel-bg:color-mix(in oklab, #8b5cf6 24%, transparent);--fcms-sel-fg:#ddd6fe;--fcms-drop:#ec4899;--fcms-danger:#fca5a5;";
|
|
1012
|
+
var LIGHT_VARS = "color-scheme:light;--fcms-bg:#ffffff;--fcms-fg:#0f172a;--fcms-muted:#64748b;--fcms-border:#e2e8f0;--fcms-canvas:#f8fafc;--fcms-input-bg:#ffffff;--fcms-row-fg:#334155;--fcms-sel-bg:#ede9fe;--fcms-sel-fg:#5b21b6;--fcms-drop:#db2777;--fcms-danger:#dc2626;";
|
|
837
1013
|
var CHROME_CSS = `.fancy-cms-editor{--fcms-accent:#8b5cf6;${LIGHT_VARS}}@media (prefers-color-scheme:dark){.fancy-cms-editor{${DARK_VARS}}}:where(.light,[data-theme="light"]) .fancy-cms-editor{${LIGHT_VARS}}:where(.dark,[data-theme="dark"]) .fancy-cms-editor{${DARK_VARS}}`;
|
|
838
|
-
var
|
|
1014
|
+
var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device"]);
|
|
839
1015
|
function NodeInspector({
|
|
840
1016
|
node,
|
|
841
1017
|
transform,
|
|
@@ -860,11 +1036,11 @@ function NodeInspector({
|
|
|
860
1036
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, opacity: 0.5, fontFamily: "ui-monospace, monospace", overflow: "hidden", textOverflow: "ellipsis" }, children: node.id })
|
|
861
1037
|
] }),
|
|
862
1038
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1 } }),
|
|
863
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...iconBtn, color: "#fca5a5" }, title: "Delete element", onClick: onRemove, children: "\u{1F5D1}" }),
|
|
1039
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...iconBtn, color: "var(--fcms-danger, #fca5a5)" }, title: "Delete element", onClick: onRemove, children: "\u{1F5D1}" }),
|
|
864
1040
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: iconBtn, title: "Close", onClick: onClose, children: "\u2715" })
|
|
865
1041
|
] }),
|
|
866
1042
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: body, children: [
|
|
867
|
-
"content" in node.props || node.type === "richtext" ? /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: 11, color: "var(--
|
|
1043
|
+
"content" in node.props || node.type === "richtext" ? /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: 11, color: "var(--fcms-muted, #94a3b8)", margin: "0 0 12px", lineHeight: 1.45, paddingBottom: 12, borderBottom: "1px solid var(--fcms-border, #1e293b)" }, children: [
|
|
868
1044
|
"\u270E Edit the text ",
|
|
869
1045
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "directly on the page" }),
|
|
870
1046
|
" \u2014 click in and type. This panel is for layout, style, and bindings."
|
|
@@ -889,7 +1065,7 @@ function NodeInspector({
|
|
|
889
1065
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...miniBtn, alignSelf: "end" }, onClick: () => onTransform({}), title: "Clear transform", children: "Reset" })
|
|
890
1066
|
] })
|
|
891
1067
|
] }),
|
|
892
|
-
!
|
|
1068
|
+
!CONTAINER_TYPES2.has(node.type) ? /* @__PURE__ */ jsxRuntime.jsxs(Group, { label: "Text", children: [
|
|
893
1069
|
/* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
|
|
894
1070
|
/* @__PURE__ */ jsxRuntime.jsx(Color, { label: "Color", value: base.color ?? "#0f172a", onChange: (v) => onStyle({ color: v }) }),
|
|
895
1071
|
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Size", value: base.fontSize?.value, placeholder: "16", onChange: (v) => onStyle({ fontSize: v == null ? void 0 : len(v) }) })
|
|
@@ -926,7 +1102,7 @@ function NodeInspector({
|
|
|
926
1102
|
] }),
|
|
927
1103
|
/* @__PURE__ */ jsxRuntime.jsx(Row, { children: /* @__PURE__ */ jsxRuntime.jsx(Txt, { label: "Border", value: base.border ?? "", placeholder: "1px solid #e2e8f0", onChange: (v) => onStyle({ border: v || void 0 }) }) })
|
|
928
1104
|
] }),
|
|
929
|
-
|
|
1105
|
+
CONTAINER_TYPES2.has(node.type) ? /* @__PURE__ */ jsxRuntime.jsxs(Group, { label: "Layout", children: [
|
|
930
1106
|
/* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
|
|
931
1107
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
932
1108
|
Sel,
|
|
@@ -964,7 +1140,7 @@ function NodeInspector({
|
|
|
964
1140
|
] });
|
|
965
1141
|
}
|
|
966
1142
|
function Group({ label: label2, children }) {
|
|
967
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, paddingBottom: 12, marginBottom: 12, borderBottom: "1px solid #1e293b" }, children: [
|
|
1143
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, paddingBottom: 12, marginBottom: 12, borderBottom: "1px solid var(--fcms-border, #1e293b)" }, children: [
|
|
968
1144
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase", opacity: 0.5 }, children: label2 }),
|
|
969
1145
|
children
|
|
970
1146
|
] });
|
|
@@ -980,7 +1156,7 @@ function PropField({ name, value, onChange }) {
|
|
|
980
1156
|
type: "button",
|
|
981
1157
|
title: bound ? "Unbind (use a literal value)" : "Bind to data (e.g. item.name)",
|
|
982
1158
|
onClick: () => onChange(bound ? "" : { $bind: "" }),
|
|
983
|
-
style: { background: "none", border: "none", cursor: "pointer", padding: 0, fontSize: 11, color: bound ? "#a78bfa" : "#64748b" },
|
|
1159
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: 0, fontSize: 11, color: bound ? "var(--fcms-accent, #a78bfa)" : "var(--fcms-muted, #64748b)" },
|
|
984
1160
|
children: "\u{1F517}"
|
|
985
1161
|
}
|
|
986
1162
|
);
|
|
@@ -991,7 +1167,7 @@ function PropField({ name, value, onChange }) {
|
|
|
991
1167
|
" ",
|
|
992
1168
|
link
|
|
993
1169
|
] }),
|
|
994
|
-
/* @__PURE__ */ jsxRuntime.jsx("input", { style: { ...input2, borderColor: "#7c3aed" }, placeholder: "data path \xB7 e.g. item.name", value: value.$bind, onChange: (e) => onChange({ $bind: e.target.value }) })
|
|
1170
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { style: { ...input2, borderColor: "var(--fcms-accent, #7c3aed)" }, placeholder: "data path \xB7 e.g. item.name", value: value.$bind, onChange: (e) => onChange({ $bind: e.target.value }) })
|
|
995
1171
|
] });
|
|
996
1172
|
}
|
|
997
1173
|
if (typeof value === "boolean") {
|
|
@@ -1078,7 +1254,7 @@ var panel = {
|
|
|
1078
1254
|
fontFamily: "system-ui, sans-serif",
|
|
1079
1255
|
overflow: "hidden"
|
|
1080
1256
|
};
|
|
1081
|
-
var headRow = { display: "flex", alignItems: "center", gap: 8, padding: "10px 12px", borderBottom: "1px solid #1e293b" };
|
|
1257
|
+
var headRow = { display: "flex", alignItems: "center", gap: 8, padding: "10px 12px", borderBottom: "1px solid var(--fcms-border, #1e293b)" };
|
|
1082
1258
|
var body = { padding: 12, overflowY: "auto" };
|
|
1083
1259
|
var field = { display: "flex", flexDirection: "column", gap: 4, flex: 1, minWidth: 0 };
|
|
1084
1260
|
var fieldLabel = { fontSize: 10, opacity: 0.6 };
|
|
@@ -1309,7 +1485,7 @@ function EditablePage({
|
|
|
1309
1485
|
const addNode = react.useCallback(
|
|
1310
1486
|
(kind) => {
|
|
1311
1487
|
const doc2 = ed.state.doc;
|
|
1312
|
-
const parent = selNode ?
|
|
1488
|
+
const parent = selNode ? CONTAINER_TYPES.has(selNode.type) ? selNode.id : selNode.parent : doc2.sections[doc2.sections.length - 1] ?? null;
|
|
1313
1489
|
const siblings = childrenOf(doc2, parent);
|
|
1314
1490
|
const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
|
|
1315
1491
|
const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
|
|
@@ -1323,7 +1499,7 @@ function EditablePage({
|
|
|
1323
1499
|
(kind, targetId) => {
|
|
1324
1500
|
const doc2 = ed.state.doc;
|
|
1325
1501
|
const target = targetId ? doc2.nodes[targetId] : null;
|
|
1326
|
-
const parent = target ?
|
|
1502
|
+
const parent = target ? CONTAINER_TYPES.has(target.type) ? target.id : target.parent : doc2.sections[doc2.sections.length - 1] ?? null;
|
|
1327
1503
|
const siblings = childrenOf(doc2, parent);
|
|
1328
1504
|
const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
|
|
1329
1505
|
const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
|
|
@@ -1356,7 +1532,7 @@ function EditablePage({
|
|
|
1356
1532
|
const node = ed.state.doc.nodes[id];
|
|
1357
1533
|
const r = el.getBoundingClientRect();
|
|
1358
1534
|
const rect = { x: r.left, y: r.top, w: r.width, h: r.height };
|
|
1359
|
-
if (node &&
|
|
1535
|
+
if (node && CONTAINER_TYPES.has(node.type)) return { id, edge: "inside", rect };
|
|
1360
1536
|
return { id, edge: clientY < r.top + r.height / 2 ? "before" : "after", rect };
|
|
1361
1537
|
};
|
|
1362
1538
|
const onPageDragOver = (e) => {
|
|
@@ -1412,7 +1588,7 @@ function EditablePage({
|
|
|
1412
1588
|
if (id) clip.current = snapshotSubtree(ed.state.doc, id);
|
|
1413
1589
|
};
|
|
1414
1590
|
const paste = (target) => {
|
|
1415
|
-
if (clip.current) runOps(pasteOps(ed.state.doc, clip.current, target,
|
|
1591
|
+
if (clip.current) runOps(pasteOps(ed.state.doc, clip.current, target, CONTAINER_TYPES));
|
|
1416
1592
|
};
|
|
1417
1593
|
const startEditText = (id) => {
|
|
1418
1594
|
if (!id) return;
|
|
@@ -1484,7 +1660,7 @@ function EditablePage({
|
|
|
1484
1660
|
{
|
|
1485
1661
|
node: selNode ?? null,
|
|
1486
1662
|
hasClipboard: Boolean(clip.current),
|
|
1487
|
-
isContainer: selNode ?
|
|
1663
|
+
isContainer: selNode ? CONTAINER_TYPES.has(selNode.type) : false,
|
|
1488
1664
|
onEditText: () => startEditText(selection),
|
|
1489
1665
|
onAdd: addNode,
|
|
1490
1666
|
onDuplicate: () => duplicate(selection),
|
|
@@ -1547,7 +1723,7 @@ function DropIndicator({
|
|
|
1547
1723
|
}) {
|
|
1548
1724
|
const r = hint.rect;
|
|
1549
1725
|
if (!r) return null;
|
|
1550
|
-
const color = "#ec4899";
|
|
1726
|
+
const color = "var(--fcms-drop, #ec4899)";
|
|
1551
1727
|
if (hint.edge === "inside") {
|
|
1552
1728
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "fixed", left: r.x, top: r.y, width: r.w, height: r.h, outline: `2px dashed ${color}`, outlineOffset: -2, borderRadius: 6, pointerEvents: "none", zIndex: Z2 + 4 } });
|
|
1553
1729
|
}
|
|
@@ -1566,9 +1742,9 @@ function ElementPalette({ open, onDragChange }) {
|
|
|
1566
1742
|
width: 216,
|
|
1567
1743
|
transform: open ? "translateX(0)" : "translateX(100%)",
|
|
1568
1744
|
transition: "transform 0.18s ease",
|
|
1569
|
-
background: "#0b1220",
|
|
1570
|
-
color: "#e2e8f0",
|
|
1571
|
-
borderLeft: "1px solid #1e293b",
|
|
1745
|
+
background: "var(--fcms-bg, #0b1220)",
|
|
1746
|
+
color: "var(--fcms-fg, #e2e8f0)",
|
|
1747
|
+
borderLeft: "1px solid var(--fcms-border, #1e293b)",
|
|
1572
1748
|
borderTopLeftRadius: 12,
|
|
1573
1749
|
borderBottomLeftRadius: 12,
|
|
1574
1750
|
boxShadow: "-18px 0 48px -20px rgba(0,0,0,0.6)",
|
|
@@ -1598,8 +1774,8 @@ function ElementPalette({ open, onDragChange }) {
|
|
|
1598
1774
|
gap: 4,
|
|
1599
1775
|
height: 56,
|
|
1600
1776
|
borderRadius: 8,
|
|
1601
|
-
background: "#0f172a",
|
|
1602
|
-
border: "1px solid #1e293b",
|
|
1777
|
+
background: "var(--fcms-canvas, #0f172a)",
|
|
1778
|
+
border: "1px solid var(--fcms-border, #1e293b)",
|
|
1603
1779
|
cursor: "grab",
|
|
1604
1780
|
fontSize: 12,
|
|
1605
1781
|
userSelect: "none"
|
|
@@ -1616,23 +1792,6 @@ function ElementPalette({ open, onDragChange }) {
|
|
|
1616
1792
|
}
|
|
1617
1793
|
);
|
|
1618
1794
|
}
|
|
1619
|
-
var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device", "repeater"]);
|
|
1620
|
-
var ADD_DEFAULTS = {
|
|
1621
|
-
text: { type: "text", props: { content: "New text" }, style: { color: "inherit" } },
|
|
1622
|
-
heading: { type: "heading", props: { content: "New heading" }, style: { fontSize: { value: 28, unit: "px" }, fontWeight: 700 } },
|
|
1623
|
-
button: { type: "button", props: { label: "Button", href: "#", variant: "primary" }, style: {} },
|
|
1624
|
-
link: { type: "link", props: { content: "link text", href: "#" }, style: { color: "#7c3aed" } },
|
|
1625
|
-
image: { type: "image", props: { src: "", alt: "" }, style: {} },
|
|
1626
|
-
box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } },
|
|
1627
|
-
stack: { type: "stack", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "stack" },
|
|
1628
|
-
grid: { type: "grid", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "grid" },
|
|
1629
|
-
card: { type: "card", props: {}, style: { padding: { value: 16, unit: "px" }, radius: { value: 12, unit: "px" }, border: "1px solid #e2e8f0" } },
|
|
1630
|
-
callout: { type: "callout", props: { content: "Heads up \u2014 this is a callout.", variant: "info" }, style: {} },
|
|
1631
|
-
divider: { type: "divider", props: {}, style: {} },
|
|
1632
|
-
code: { type: "code", props: { content: "npm install @particle-academy/react-fancy", lang: "bash" }, style: {} },
|
|
1633
|
-
richtext: { type: "richtext", props: { html: "<p>Rich <strong>text</strong> with <em>inline</em> formatting.</p>" }, style: {} },
|
|
1634
|
-
repeater: { type: "repeater", props: { items: "" }, style: { gap: { value: 12, unit: "px" } }, layout: "stack" }
|
|
1635
|
-
};
|
|
1636
1795
|
var GRIPS = [
|
|
1637
1796
|
{ key: "nw", north: true, west: true, cursor: "nwse-resize" },
|
|
1638
1797
|
{ key: "n", north: true, cursor: "ns-resize" },
|
|
@@ -1743,7 +1902,7 @@ function SelectionOverlay({
|
|
|
1743
1902
|
top: box.y - 1,
|
|
1744
1903
|
width: box.w + 2,
|
|
1745
1904
|
height: box.h + 2,
|
|
1746
|
-
outline: "2px solid #8b5cf6",
|
|
1905
|
+
outline: "2px solid var(--fcms-accent, #8b5cf6)",
|
|
1747
1906
|
borderRadius: 4,
|
|
1748
1907
|
pointerEvents: "none",
|
|
1749
1908
|
zIndex: Z2
|
|
@@ -1753,7 +1912,7 @@ function SelectionOverlay({
|
|
|
1753
1912
|
width: 12,
|
|
1754
1913
|
height: 12,
|
|
1755
1914
|
background: "#fff",
|
|
1756
|
-
border: "2px solid #8b5cf6",
|
|
1915
|
+
border: "2px solid var(--fcms-accent, #8b5cf6)",
|
|
1757
1916
|
borderRadius: 3,
|
|
1758
1917
|
zIndex: Z2 + 1,
|
|
1759
1918
|
touchAction: "none",
|
|
@@ -1830,22 +1989,6 @@ function FloatingToolbar({
|
|
|
1830
1989
|
] })
|
|
1831
1990
|
] });
|
|
1832
1991
|
}
|
|
1833
|
-
var ADD_MENU = [
|
|
1834
|
-
{ kind: "heading", label: "Heading" },
|
|
1835
|
-
{ kind: "text", label: "Text" },
|
|
1836
|
-
{ kind: "button", label: "Button" },
|
|
1837
|
-
{ kind: "link", label: "Link" },
|
|
1838
|
-
{ kind: "image", label: "Image" },
|
|
1839
|
-
{ kind: "card", label: "Card" },
|
|
1840
|
-
{ kind: "callout", label: "Callout" },
|
|
1841
|
-
{ kind: "stack", label: "Stack" },
|
|
1842
|
-
{ kind: "grid", label: "Grid" },
|
|
1843
|
-
{ kind: "box", label: "Box" },
|
|
1844
|
-
{ kind: "divider", label: "Divider" },
|
|
1845
|
-
{ kind: "code", label: "Code" },
|
|
1846
|
-
{ kind: "richtext", label: "Rich text" },
|
|
1847
|
-
{ kind: "repeater", label: "Repeater" }
|
|
1848
|
-
];
|
|
1849
1992
|
function EditBar({
|
|
1850
1993
|
editing,
|
|
1851
1994
|
onToggle,
|