@particle-academy/fancy-cms-ui 0.0.1-dev.2 → 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/chunk-7YYLYBLI.js +47 -0
- package/dist/chunk-7YYLYBLI.js.map +1 -0
- package/dist/editor.cjs +138 -53
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +7 -8
- package/dist/editor.d.ts +7 -8
- package/dist/editor.js +128 -53
- package/dist/editor.js.map +1 -1
- package/dist/index.js +1 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// src/document/fractional.ts
|
|
2
|
+
var BASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
3
|
+
var RADIX = BASE.length;
|
|
4
|
+
var MID = Math.floor(RADIX / 2);
|
|
5
|
+
function val(c) {
|
|
6
|
+
const i = BASE.indexOf(c);
|
|
7
|
+
return i < 0 ? 0 : i;
|
|
8
|
+
}
|
|
9
|
+
function keyBetween(a, b) {
|
|
10
|
+
if (a === null) a = "";
|
|
11
|
+
if (b === null) {
|
|
12
|
+
return a + BASE[MID];
|
|
13
|
+
}
|
|
14
|
+
if (a >= b) {
|
|
15
|
+
throw new Error(`keyBetween: lower bound "${a}" is not below upper bound "${b}"`);
|
|
16
|
+
}
|
|
17
|
+
let result = "";
|
|
18
|
+
let i = 0;
|
|
19
|
+
for (; ; ) {
|
|
20
|
+
const ca = i < a.length ? val(a[i]) : 0;
|
|
21
|
+
const cb = i < b.length ? val(b[i]) : RADIX;
|
|
22
|
+
if (cb - ca > 1) {
|
|
23
|
+
result += BASE[ca + Math.floor((cb - ca) / 2)];
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
if (ca === cb) {
|
|
27
|
+
result += BASE[ca];
|
|
28
|
+
i++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
result += BASE[ca];
|
|
32
|
+
i++;
|
|
33
|
+
for (; ; ) {
|
|
34
|
+
const da = i < a.length ? val(a[i]) : 0;
|
|
35
|
+
if (RADIX - da > 1) {
|
|
36
|
+
result += BASE[da + Math.floor((RADIX - da) / 2)];
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
result += BASE[da];
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { keyBetween };
|
|
46
|
+
//# sourceMappingURL=chunk-7YYLYBLI.js.map
|
|
47
|
+
//# sourceMappingURL=chunk-7YYLYBLI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/document/fractional.ts"],"names":[],"mappings":";AAaA,IAAM,IAAA,GAAO,gEAAA;AACb,IAAM,QAAQ,IAAA,CAAK,MAAA;AACnB,IAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,CAAC,CAAA;AAEhC,SAAS,IAAI,CAAA,EAAmB;AAC9B,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AACxB,EAAA,OAAO,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA;AACrB;AAOO,SAAS,UAAA,CAAW,GAAkB,CAAA,EAA0B;AACrE,EAAA,IAAI,CAAA,KAAM,MAAM,CAAA,GAAI,EAAA;AACpB,EAAA,IAAI,MAAM,IAAA,EAAM;AAEd,IAAA,OAAO,CAAA,GAAI,KAAK,GAAG,CAAA;AAAA,EACrB;AACA,EAAA,IAAI,KAAK,CAAA,EAAG;AACV,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,yBAAA,EAA4B,CAAC,CAAA,4BAAA,EAA+B,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,EAClF;AAEA,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,IAAI,CAAA,GAAI,CAAA;AAER,EAAA,WAAS;AACP,IAAA,MAAM,EAAA,GAAK,IAAI,CAAA,CAAE,MAAA,GAAS,IAAI,CAAA,CAAE,CAAC,CAAW,CAAA,GAAI,CAAA;AAChD,IAAA,MAAM,EAAA,GAAK,IAAI,CAAA,CAAE,MAAA,GAAS,IAAI,CAAA,CAAE,CAAC,CAAW,CAAA,GAAI,KAAA;AAChD,IAAA,IAAI,EAAA,GAAK,KAAK,CAAA,EAAG;AACf,MAAA,MAAA,IAAU,KAAK,EAAA,GAAK,IAAA,CAAK,OAAO,EAAA,GAAK,EAAA,IAAM,CAAC,CAAC,CAAA;AAC7C,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,IAAI,OAAO,EAAA,EAAI;AACb,MAAA,MAAA,IAAU,KAAK,EAAE,CAAA;AACjB,MAAA,CAAA,EAAA;AACA,MAAA;AAAA,IACF;AAGA,IAAA,MAAA,IAAU,KAAK,EAAE,CAAA;AACjB,IAAA,CAAA,EAAA;AACA,IAAA,WAAS;AACP,MAAA,MAAM,EAAA,GAAK,IAAI,CAAA,CAAE,MAAA,GAAS,IAAI,CAAA,CAAE,CAAC,CAAW,CAAA,GAAI,CAAA;AAChD,MAAA,IAAI,KAAA,GAAQ,KAAK,CAAA,EAAG;AAClB,QAAA,MAAA,IAAU,KAAK,EAAA,GAAK,IAAA,CAAK,OAAO,KAAA,GAAQ,EAAA,IAAM,CAAC,CAAC,CAAA;AAChD,QAAA,OAAO,MAAA;AAAA,MACT;AACA,MAAA,MAAA,IAAU,KAAK,EAAE,CAAA;AACjB,MAAA,CAAA,EAAA;AAAA,IACF;AAAA,EACF;AACF","file":"chunk-7YYLYBLI.js","sourcesContent":["/**\n * Minimal collab-safe fractional ordering keys.\n *\n * Sibling `order` is a string over a base-62 alphabet interpreted as a fraction\n * in (0, 1). `keyBetween(a, b)` returns a key that sorts strictly between `a`\n * and `b` (lexicographically), extending precision when the bounds are\n * adjacent. Concurrent inserts at the same spot produce different keys (vary by\n * actor), so reorders never collide.\n *\n * This is intentionally compact for Phase 0. Before live collaboration ships it\n * should be swapped for a hardened implementation with jitter + bucket bounds.\n */\n\nconst BASE = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\nconst RADIX = BASE.length; // 62\nconst MID = Math.floor(RADIX / 2);\n\nfunction val(c: string): number {\n const i = BASE.indexOf(c);\n return i < 0 ? 0 : i;\n}\n\n/**\n * Return a key strictly between `a` and `b`. Pass `null` for an open bound\n * (`keyBetween(null, null)` for the first key, `keyBetween(last, null)` to\n * append, `keyBetween(null, first)` to prepend).\n */\nexport function keyBetween(a: string | null, b: string | null): string {\n if (a === null) a = \"\";\n if (b === null) {\n // Append: a key greater than `a`. Appending a mid digit always sorts after.\n return a + BASE[MID];\n }\n if (a >= b) {\n throw new Error(`keyBetween: lower bound \"${a}\" is not below upper bound \"${b}\"`);\n }\n\n let result = \"\";\n let i = 0;\n // Walk the common prefix, then split at the first differing digit.\n for (;;) {\n const ca = i < a.length ? val(a[i] as string) : 0;\n const cb = i < b.length ? val(b[i] as string) : RADIX;\n if (cb - ca > 1) {\n result += BASE[ca + Math.floor((cb - ca) / 2)];\n return result;\n }\n if (ca === cb) {\n result += BASE[ca];\n i++;\n continue;\n }\n // Adjacent digits (cb - ca === 1): take the lower path, then descend into\n // `a`'s remainder with RADIX as the upper bound until a gap opens.\n result += BASE[ca];\n i++;\n for (;;) {\n const da = i < a.length ? val(a[i] as string) : 0;\n if (RADIX - da > 1) {\n result += BASE[da + Math.floor((RADIX - da) / 2)];\n return result;\n }\n result += BASE[da];\n i++;\n }\n }\n}\n"]}
|
package/dist/editor.cjs
CHANGED
|
@@ -697,17 +697,42 @@ function Toolbar({ ed }) {
|
|
|
697
697
|
}
|
|
698
698
|
);
|
|
699
699
|
}
|
|
700
|
+
|
|
701
|
+
// src/document/fractional.ts
|
|
702
|
+
var BASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
703
|
+
var RADIX = BASE.length;
|
|
704
|
+
var MID = Math.floor(RADIX / 2);
|
|
705
|
+
function keyBetween(a, b) {
|
|
706
|
+
if (a === null) a = "";
|
|
707
|
+
{
|
|
708
|
+
return a + BASE[MID];
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
var Z = 2147483e3;
|
|
700
712
|
function EditablePage({
|
|
701
713
|
doc,
|
|
702
714
|
registry = defaultRegistry,
|
|
703
|
-
defaultEditing = false,
|
|
704
715
|
timelineDock
|
|
705
716
|
}) {
|
|
706
717
|
const ed = useEditor(doc);
|
|
707
|
-
const [editing, setEditing] = react.useState(
|
|
718
|
+
const [editing, setEditing] = react.useState(false);
|
|
719
|
+
const [revealed, setRevealed] = react.useState(false);
|
|
708
720
|
const pageRef = react.useRef(null);
|
|
709
721
|
const [box, setBox] = react.useState(null);
|
|
710
722
|
const { selection } = ed.state;
|
|
723
|
+
const selNode = selection ? ed.state.doc.nodes[selection] : null;
|
|
724
|
+
react.useEffect(() => {
|
|
725
|
+
const onKey = (e) => setRevealed(e.ctrlKey && e.shiftKey);
|
|
726
|
+
const onBlur = () => setRevealed(false);
|
|
727
|
+
window.addEventListener("keydown", onKey);
|
|
728
|
+
window.addEventListener("keyup", onKey);
|
|
729
|
+
window.addEventListener("blur", onBlur);
|
|
730
|
+
return () => {
|
|
731
|
+
window.removeEventListener("keydown", onKey);
|
|
732
|
+
window.removeEventListener("keyup", onKey);
|
|
733
|
+
window.removeEventListener("blur", onBlur);
|
|
734
|
+
};
|
|
735
|
+
}, []);
|
|
711
736
|
react.useLayoutEffect(() => {
|
|
712
737
|
if (!editing || !selection || !pageRef.current) {
|
|
713
738
|
setBox(null);
|
|
@@ -727,65 +752,130 @@ function EditablePage({
|
|
|
727
752
|
window.removeEventListener("resize", update);
|
|
728
753
|
};
|
|
729
754
|
}, [editing, selection, ed.state.doc]);
|
|
755
|
+
react.useEffect(() => {
|
|
756
|
+
if (!editing || !selection || !pageRef.current) return;
|
|
757
|
+
if (selNode?.type !== "text") return;
|
|
758
|
+
const el = pageRef.current.querySelector(`[data-cms="${cssEscape2(selection)}"]`);
|
|
759
|
+
if (!el) return;
|
|
760
|
+
el.setAttribute("contenteditable", "true");
|
|
761
|
+
el.style.outline = "none";
|
|
762
|
+
el.focus();
|
|
763
|
+
const commit = () => {
|
|
764
|
+
ed.apply({ t: "set_props", id: selection, patch: { content: el.innerText } });
|
|
765
|
+
};
|
|
766
|
+
el.addEventListener("blur", commit);
|
|
767
|
+
return () => {
|
|
768
|
+
el.removeEventListener("blur", commit);
|
|
769
|
+
el.removeAttribute("contenteditable");
|
|
770
|
+
};
|
|
771
|
+
}, [editing, selection, selNode?.type, ed]);
|
|
730
772
|
const onClickCapture = (e) => {
|
|
731
773
|
if (!editing) return;
|
|
732
774
|
const el = e.target.closest("[data-cms]");
|
|
733
775
|
if (el) {
|
|
776
|
+
if (el.getAttribute("contenteditable") === "true") return;
|
|
734
777
|
e.preventDefault();
|
|
735
778
|
e.stopPropagation();
|
|
736
779
|
ed.select(el.getAttribute("data-cms"));
|
|
737
780
|
}
|
|
738
781
|
};
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
}
|
|
782
|
+
const addText = react.useCallback(() => {
|
|
783
|
+
const parent = selNode && selNode.type !== "text" ? selNode.id : ed.state.doc.sections[0] ?? null;
|
|
784
|
+
const siblings = childrenOf(ed.state.doc, parent);
|
|
785
|
+
const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null);
|
|
786
|
+
const id = `n${ed.state.doc.seq + 1}-${Math.floor(performance.now())}`;
|
|
787
|
+
ed.apply({
|
|
788
|
+
t: "insert_node",
|
|
789
|
+
node: { id, type: "text", parent, order, props: { content: "New text" }, style: { base: { color: "inherit" } } }
|
|
790
|
+
});
|
|
791
|
+
ed.select(id);
|
|
792
|
+
}, [ed, selNode]);
|
|
793
|
+
const setStyle = (patch) => selection && ed.apply({ t: "set_style", id: selection, breakpoint: "base", patch });
|
|
750
794
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
751
795
|
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: pageRef, onClickCapture, children: /* @__PURE__ */ jsxRuntime.jsx(CmsPage, { doc: ed.state.doc, registry }) }),
|
|
752
|
-
editing &&
|
|
753
|
-
|
|
796
|
+
editing && box ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
797
|
+
"div",
|
|
798
|
+
{
|
|
799
|
+
style: {
|
|
800
|
+
position: "fixed",
|
|
801
|
+
left: box.x - 1,
|
|
802
|
+
top: box.y - 1,
|
|
803
|
+
width: box.w + 2,
|
|
804
|
+
height: box.h + 2,
|
|
805
|
+
outline: "2px solid #8b5cf6",
|
|
806
|
+
borderRadius: 4,
|
|
807
|
+
pointerEvents: "none",
|
|
808
|
+
zIndex: Z
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
) : null,
|
|
812
|
+
editing && box && selNode ? /* @__PURE__ */ jsxRuntime.jsx(FloatingToolbar, { box, isText: selNode.type === "text", base: selNode.style.base, setStyle }) : null,
|
|
813
|
+
revealed || editing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
754
814
|
EditBar,
|
|
755
815
|
{
|
|
756
816
|
editing,
|
|
757
|
-
onToggle: () =>
|
|
817
|
+
onToggle: () => {
|
|
818
|
+
setEditing((v) => !v);
|
|
819
|
+
ed.select(null);
|
|
820
|
+
},
|
|
758
821
|
canUndo: ed.canUndo,
|
|
759
822
|
canRedo: ed.canRedo,
|
|
760
823
|
onUndo: ed.undo,
|
|
761
824
|
onRedo: ed.redo,
|
|
762
|
-
|
|
825
|
+
onAddText: addText
|
|
763
826
|
}
|
|
764
|
-
),
|
|
765
|
-
editing &&
|
|
766
|
-
|
|
827
|
+
) : null,
|
|
828
|
+
editing && timelineDock ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "fixed", left: 0, right: 0, bottom: 0, zIndex: Z }, children: timelineDock }) : null
|
|
829
|
+
] });
|
|
830
|
+
}
|
|
831
|
+
function FloatingToolbar({
|
|
832
|
+
box,
|
|
833
|
+
isText,
|
|
834
|
+
base,
|
|
835
|
+
setStyle
|
|
836
|
+
}) {
|
|
837
|
+
const top = Math.max(8, box.y - 46);
|
|
838
|
+
const wrap = {
|
|
839
|
+
position: "fixed",
|
|
840
|
+
left: box.x,
|
|
841
|
+
top,
|
|
842
|
+
display: "flex",
|
|
843
|
+
alignItems: "center",
|
|
844
|
+
gap: 8,
|
|
845
|
+
padding: "6px 10px",
|
|
846
|
+
background: "#0f172a",
|
|
847
|
+
color: "#fff",
|
|
848
|
+
border: "1px solid rgba(255,255,255,0.12)",
|
|
849
|
+
borderRadius: 999,
|
|
850
|
+
boxShadow: "0 8px 24px -8px rgba(0,0,0,0.5)",
|
|
851
|
+
zIndex: Z + 2,
|
|
852
|
+
fontFamily: "system-ui, sans-serif",
|
|
853
|
+
fontSize: 12
|
|
854
|
+
};
|
|
855
|
+
const swatch = { width: 22, height: 22, border: "none", background: "transparent", padding: 0, cursor: "pointer" };
|
|
856
|
+
const num = { width: 52, background: "#1e293b", color: "#fff", border: "1px solid #334155", borderRadius: 6, padding: "3px 6px", font: "inherit" };
|
|
857
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: wrap, onMouseDown: (e) => e.preventDefault(), children: [
|
|
858
|
+
isText ? /* @__PURE__ */ jsxRuntime.jsxs("label", { title: "Text color", style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
|
|
859
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { opacity: 0.7 }, children: "A" }),
|
|
860
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "color", style: swatch, value: base.color ?? "#0f172a", onChange: (e) => setStyle({ color: e.target.value }) })
|
|
861
|
+
] }) : null,
|
|
862
|
+
isText ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
863
|
+
"input",
|
|
864
|
+
{
|
|
865
|
+
type: "number",
|
|
866
|
+
title: "Font size (px)",
|
|
867
|
+
style: num,
|
|
868
|
+
placeholder: "size",
|
|
869
|
+
value: base.fontSize?.value ?? "",
|
|
870
|
+
onChange: (e) => setStyle({ fontSize: { value: Number(e.target.value) || 0, unit: "px" } })
|
|
871
|
+
}
|
|
872
|
+
) : null,
|
|
873
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { title: "Background", style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
|
|
874
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { opacity: 0.7 }, children: "BG" }),
|
|
875
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "color", style: swatch, value: normalizeColor(base.background), onChange: (e) => setStyle({ background: e.target.value }) })
|
|
876
|
+
] })
|
|
767
877
|
] });
|
|
768
878
|
}
|
|
769
|
-
var inspectorPanel = {
|
|
770
|
-
position: "fixed",
|
|
771
|
-
top: 56,
|
|
772
|
-
right: 12,
|
|
773
|
-
bottom: 12,
|
|
774
|
-
width: 300,
|
|
775
|
-
background: "#fff",
|
|
776
|
-
border: "1px solid #e2e8f0",
|
|
777
|
-
borderRadius: 12,
|
|
778
|
-
boxShadow: "0 10px 30px -10px rgba(0,0,0,0.25)",
|
|
779
|
-
overflow: "auto",
|
|
780
|
-
zIndex: 2147483001
|
|
781
|
-
};
|
|
782
|
-
var dockSlot = {
|
|
783
|
-
position: "fixed",
|
|
784
|
-
left: 0,
|
|
785
|
-
right: 0,
|
|
786
|
-
bottom: 0,
|
|
787
|
-
zIndex: 2147483e3
|
|
788
|
-
};
|
|
789
879
|
function EditBar({
|
|
790
880
|
editing,
|
|
791
881
|
onToggle,
|
|
@@ -793,7 +883,7 @@ function EditBar({
|
|
|
793
883
|
canRedo,
|
|
794
884
|
onUndo,
|
|
795
885
|
onRedo,
|
|
796
|
-
|
|
886
|
+
onAddText
|
|
797
887
|
}) {
|
|
798
888
|
const bar = {
|
|
799
889
|
position: "fixed",
|
|
@@ -807,28 +897,23 @@ function EditBar({
|
|
|
807
897
|
color: "#fff",
|
|
808
898
|
borderRadius: 999,
|
|
809
899
|
boxShadow: "0 8px 24px -8px rgba(0,0,0,0.4)",
|
|
810
|
-
zIndex:
|
|
900
|
+
zIndex: Z + 3,
|
|
811
901
|
fontFamily: "system-ui, sans-serif",
|
|
812
902
|
fontSize: 12
|
|
813
903
|
};
|
|
814
|
-
const btn = {
|
|
815
|
-
font: "inherit",
|
|
816
|
-
border: "none",
|
|
817
|
-
background: "transparent",
|
|
818
|
-
color: "inherit",
|
|
819
|
-
cursor: "pointer",
|
|
820
|
-
padding: "4px 8px",
|
|
821
|
-
borderRadius: 999
|
|
822
|
-
};
|
|
904
|
+
const btn = { font: "inherit", border: "none", background: "transparent", color: "inherit", cursor: "pointer", padding: "4px 8px", borderRadius: 999 };
|
|
823
905
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: bar, children: [
|
|
824
906
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, background: editing ? "#8b5cf6" : "transparent" }, onClick: onToggle, children: editing ? "\u25CF Editing" : "Edit" }),
|
|
825
907
|
editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
908
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onClick: onAddText, title: "Add a text element", children: "\uFF0B Text" }),
|
|
826
909
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: canUndo ? 1 : 0.4 }, disabled: !canUndo, onClick: onUndo, children: "\u21B6" }),
|
|
827
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: canRedo ? 1 : 0.4 }, disabled: !canRedo, onClick: onRedo, children: "\u21B7" })
|
|
828
|
-
!hasDock ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { opacity: 0.5, paddingRight: 6 }, children: "timeline: fancy-motion \u2192" }) : null
|
|
910
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: canRedo ? 1 : 0.4 }, disabled: !canRedo, onClick: onRedo, children: "\u21B7" })
|
|
829
911
|
] }) : null
|
|
830
912
|
] });
|
|
831
913
|
}
|
|
914
|
+
function normalizeColor(v) {
|
|
915
|
+
return v && /^#[0-9a-fA-F]{6}$/.test(v) ? v : "#ffffff";
|
|
916
|
+
}
|
|
832
917
|
function cssEscape2(value) {
|
|
833
918
|
return value.replace(/["\\]/g, "\\$&");
|
|
834
919
|
}
|