@tarviks/lexical-rich-editor 1.0.11 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1249,6 +1249,46 @@ var ContentEditorLevel = /* @__PURE__ */ ((ContentEditorLevel2) => {
1249
1249
  ContentEditorLevel2["Pro"] = "pro";
1250
1250
  return ContentEditorLevel2;
1251
1251
  })(ContentEditorLevel || {});
1252
+
1253
+ // src/Types/PageSetup.ts
1254
+ var DEFAULT_PAGE_SETUP = {
1255
+ size: "pageless",
1256
+ orientation: "portrait",
1257
+ margin: "normal"
1258
+ };
1259
+ var PAGE_SIZE_OPTIONS = [
1260
+ { key: "a4", label: 'A4 (8.27" x 11.69")', widthIn: 8.27, heightIn: 11.69 },
1261
+ { key: "letter", label: 'Letter (8.5" x 11")', widthIn: 8.5, heightIn: 11 },
1262
+ { key: "legal", label: 'Legal (8.5" x 14")', widthIn: 8.5, heightIn: 14 },
1263
+ { key: "tabloid", label: 'Tabloid (11" x 17")', widthIn: 11, heightIn: 17 },
1264
+ { key: "a3", label: 'A3 (11.69" x 16.54")', widthIn: 11.69, heightIn: 16.54 },
1265
+ { key: "a5", label: 'A5 (5.83" x 8.27")', widthIn: 5.83, heightIn: 8.27 },
1266
+ { key: "b4", label: 'B4 (9.84" x 13.90")', widthIn: 9.84, heightIn: 13.9 },
1267
+ { key: "b5", label: 'B5 (6.93" x 9.84")', widthIn: 6.93, heightIn: 9.84 },
1268
+ { key: "statement", label: 'Statement (5.5" x 8.5")', widthIn: 5.5, heightIn: 8.5 },
1269
+ { key: "executive", label: 'Executive (7.25" x 10.5")', widthIn: 7.25, heightIn: 10.5 },
1270
+ { key: "folio", label: 'Folio (8.5" x 13")', widthIn: 8.5, heightIn: 13 }
1271
+ ];
1272
+ var MARGIN_OPTIONS = [
1273
+ { key: "narrow", label: 'Narrow (0.25")', valueIn: 0.25 },
1274
+ { key: "normal", label: 'Normal (0.4")', valueIn: 0.4 },
1275
+ { key: "moderate", label: 'Moderate (0.75")', valueIn: 0.75 },
1276
+ { key: "wide", label: 'Wide (1")', valueIn: 1 }
1277
+ ];
1278
+ var CSS_PX_PER_INCH = 96;
1279
+ function resolvePageCanvasMetrics(value) {
1280
+ const margin = MARGIN_OPTIONS.find((o) => o.key === value.margin) ?? MARGIN_OPTIONS[1];
1281
+ if (value.size === "pageless") {
1282
+ return { widthPx: void 0, paddingPx: 20 };
1283
+ }
1284
+ const size = PAGE_SIZE_OPTIONS.find((o) => o.key === value.size);
1285
+ if (!size) return { widthPx: void 0, paddingPx: 20 };
1286
+ const widthIn = value.orientation === "landscape" ? size.heightIn : size.widthIn;
1287
+ return {
1288
+ widthPx: Math.round(widthIn * CSS_PX_PER_INCH),
1289
+ paddingPx: Math.round(margin.valueIn * CSS_PX_PER_INCH)
1290
+ };
1291
+ }
1252
1292
  var AutocompleteNode = class _AutocompleteNode extends lexical.TextNode {
1253
1293
  static getType() {
1254
1294
  return "autocomplete";
@@ -2487,6 +2527,236 @@ function CharacterStylesPopupPlugin(props) {
2487
2527
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
2488
2528
  return useCharacterStylesPopup(editor, props);
2489
2529
  }
2530
+
2531
+ // src/Utils/Sanitize.ts
2532
+ var DROP_ENTIRELY = /* @__PURE__ */ new Set([
2533
+ "script",
2534
+ "noscript",
2535
+ "style",
2536
+ "object",
2537
+ "embed",
2538
+ "form",
2539
+ "input",
2540
+ "button",
2541
+ "select",
2542
+ "textarea",
2543
+ "meta",
2544
+ "link",
2545
+ "base"
2546
+ ]);
2547
+ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
2548
+ // Block
2549
+ "p",
2550
+ "h1",
2551
+ "h2",
2552
+ "h3",
2553
+ "h4",
2554
+ "h5",
2555
+ "h6",
2556
+ "ul",
2557
+ "ol",
2558
+ "li",
2559
+ "blockquote",
2560
+ "pre",
2561
+ "div",
2562
+ "table",
2563
+ "thead",
2564
+ "tbody",
2565
+ "tfoot",
2566
+ "tr",
2567
+ "td",
2568
+ "th",
2569
+ // Inline
2570
+ "span",
2571
+ "a",
2572
+ "strong",
2573
+ "b",
2574
+ "em",
2575
+ "i",
2576
+ "u",
2577
+ "s",
2578
+ "del",
2579
+ "strike",
2580
+ "sub",
2581
+ "sup",
2582
+ "mark",
2583
+ "code",
2584
+ "br",
2585
+ "hr",
2586
+ // Media / embeds
2587
+ "img",
2588
+ "iframe"
2589
+ ]);
2590
+ var ALLOWED_ATTRS = /* @__PURE__ */ new Set([
2591
+ // Presentation
2592
+ "class",
2593
+ "style",
2594
+ "dir",
2595
+ "lang",
2596
+ // Anchors
2597
+ "href",
2598
+ "target",
2599
+ "rel",
2600
+ // Images
2601
+ "src",
2602
+ "alt",
2603
+ "width",
2604
+ "height",
2605
+ // Tables
2606
+ "colspan",
2607
+ "rowspan",
2608
+ // Lists — <ol type="a"> and <ol start="2">
2609
+ "start",
2610
+ "type",
2611
+ // Lexical-internal data markers
2612
+ "data-lex-block",
2613
+ "data-kind",
2614
+ "data-lexical-decorator",
2615
+ // Misc
2616
+ "title",
2617
+ "allowfullscreen"
2618
+ ]);
2619
+ var DANGEROUS_URL = /^\s*(javascript|data\s*:|vbscript)/i;
2620
+ function sanitizeElement(el) {
2621
+ for (const child of Array.from(el.children)) {
2622
+ sanitizeElement(child);
2623
+ }
2624
+ const tag = el.tagName.toLowerCase();
2625
+ if (DROP_ENTIRELY.has(tag)) {
2626
+ el.parentNode?.removeChild(el);
2627
+ return;
2628
+ }
2629
+ if (tag === "iframe") {
2630
+ const src = el.getAttribute("src") ?? "";
2631
+ const isYouTube = /^\s*https:\/\/(www\.)?(youtube\.com|youtube-nocookie\.com)\/embed\/[^?&/]+/i.test(src);
2632
+ if (!isYouTube) {
2633
+ el.parentNode?.removeChild(el);
2634
+ return;
2635
+ }
2636
+ }
2637
+ if (!ALLOWED_TAGS.has(tag)) {
2638
+ while (el.firstChild) {
2639
+ el.parentNode?.insertBefore(el.firstChild, el);
2640
+ }
2641
+ el.parentNode?.removeChild(el);
2642
+ return;
2643
+ }
2644
+ for (const { name, value } of Array.from(el.attributes)) {
2645
+ const lname = name.toLowerCase();
2646
+ if (!ALLOWED_ATTRS.has(lname)) {
2647
+ el.removeAttribute(name);
2648
+ continue;
2649
+ }
2650
+ if (lname === "href" || lname === "src") {
2651
+ if (DANGEROUS_URL.test(value)) {
2652
+ el.removeAttribute(name);
2653
+ }
2654
+ }
2655
+ if (lname === "style") {
2656
+ const safe = value.replace(/expression\s*\(/gi, "(").replace(/url\s*\(\s*['"]?\s*javascript:/gi, "url(");
2657
+ el.setAttribute("style", safe);
2658
+ }
2659
+ }
2660
+ if (tag === "a" && (el.getAttribute("target") || "").toLowerCase() === "_blank") {
2661
+ const rel = (el.getAttribute("rel") || "").trim();
2662
+ const tokens = new Set(
2663
+ rel.split(/\s+/).filter(Boolean).map((t) => t.toLowerCase())
2664
+ );
2665
+ tokens.add("noopener");
2666
+ tokens.add("noreferrer");
2667
+ el.setAttribute("rel", Array.from(tokens).join(" "));
2668
+ }
2669
+ }
2670
+ function sanitizeHtml(html) {
2671
+ if (!html || typeof html !== "string") return "";
2672
+ const doc = new DOMParser().parseFromString(html, "text/html");
2673
+ for (const child of Array.from(doc.body.children)) {
2674
+ sanitizeElement(child);
2675
+ }
2676
+ return doc.body.innerHTML;
2677
+ }
2678
+ function postProcessOutput(html) {
2679
+ if (!html) return html;
2680
+ const doc = new DOMParser().parseFromString(html, "text/html");
2681
+ for (const inner of Array.from(doc.querySelectorAll("b > strong"))) {
2682
+ const outer = inner.parentElement;
2683
+ _mergeAttributes(inner, outer);
2684
+ while (inner.firstChild) outer.insertBefore(inner.firstChild, inner);
2685
+ outer.removeChild(inner);
2686
+ }
2687
+ for (const inner of Array.from(doc.querySelectorAll("i > em"))) {
2688
+ const outer = inner.parentElement;
2689
+ _mergeAttributes(inner, outer);
2690
+ while (inner.firstChild) outer.insertBefore(inner.firstChild, inner);
2691
+ outer.removeChild(inner);
2692
+ }
2693
+ for (const p of Array.from(doc.querySelectorAll("p"))) {
2694
+ const style = p.style;
2695
+ const hasMargin = !!(style.margin || style.marginTop || style.marginRight || style.marginBottom || style.marginLeft);
2696
+ if (!hasMargin) style.margin = "0";
2697
+ if (!style.lineHeight) style.lineHeight = "1.25";
2698
+ }
2699
+ return doc.body.innerHTML;
2700
+ }
2701
+ function _mergeAttributes(src, dst) {
2702
+ for (const { name, value } of Array.from(src.attributes)) {
2703
+ if (name === "style") {
2704
+ const prev = dst.getAttribute("style") || "";
2705
+ dst.setAttribute("style", prev ? `${prev}; ${value}` : value);
2706
+ } else if (name === "class") {
2707
+ const prev = dst.getAttribute("class") || "";
2708
+ dst.setAttribute("class", prev ? `${prev} ${value}` : value);
2709
+ }
2710
+ }
2711
+ }
2712
+ var BLOCK_TAGS = /* @__PURE__ */ new Set([
2713
+ "p",
2714
+ "h1",
2715
+ "h2",
2716
+ "h3",
2717
+ "h4",
2718
+ "h5",
2719
+ "h6",
2720
+ "div",
2721
+ "ul",
2722
+ "ol",
2723
+ "li",
2724
+ "table",
2725
+ "blockquote",
2726
+ "pre",
2727
+ "hr"
2728
+ ]);
2729
+ function normalizeToBlockHtml(html) {
2730
+ if (!html) return "";
2731
+ const doc = new DOMParser().parseFromString(html, "text/html");
2732
+ const body = doc.body;
2733
+ const childNodes = Array.from(body.childNodes);
2734
+ const needsWrap = childNodes.some((node) => {
2735
+ if (node.nodeType === Node.TEXT_NODE) return !!node.nodeValue?.trim();
2736
+ if (node.nodeType === Node.ELEMENT_NODE)
2737
+ return !BLOCK_TAGS.has(node.tagName.toLowerCase());
2738
+ return false;
2739
+ });
2740
+ if (!needsWrap) return html;
2741
+ while (body.firstChild) body.removeChild(body.firstChild);
2742
+ let pendingP = null;
2743
+ for (const node of childNodes) {
2744
+ const isBlock = node.nodeType === Node.ELEMENT_NODE && BLOCK_TAGS.has(node.tagName.toLowerCase());
2745
+ const isWhitespace = node.nodeType === Node.TEXT_NODE && !node.nodeValue?.trim();
2746
+ if (isBlock) {
2747
+ if (pendingP) {
2748
+ body.appendChild(pendingP);
2749
+ pendingP = null;
2750
+ }
2751
+ body.appendChild(node);
2752
+ } else if (!isWhitespace) {
2753
+ if (!pendingP) pendingP = doc.createElement("p");
2754
+ pendingP.appendChild(node);
2755
+ }
2756
+ }
2757
+ if (pendingP) body.appendChild(pendingP);
2758
+ return body.innerHTML;
2759
+ }
2490
2760
  var CustomOnChangePlugin = ({ value, onChange }) => {
2491
2761
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
2492
2762
  const initializedRef = React9.useRef(false);
@@ -2507,7 +2777,7 @@ var CustomOnChangePlugin = ({ value, onChange }) => {
2507
2777
  }, [editor, value]);
2508
2778
  const handleChange = React9.useCallback((editorState) => {
2509
2779
  editorState.read(() => {
2510
- onChangeRef.current(html.$generateHtmlFromNodes(editor));
2780
+ onChangeRef.current(postProcessOutput(html.$generateHtmlFromNodes(editor)));
2511
2781
  });
2512
2782
  }, [editor]);
2513
2783
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -3488,310 +3758,112 @@ function $onDragStart2(event) {
3488
3758
  if (!node) return false;
3489
3759
  const dataTransfer = event.dataTransfer;
3490
3760
  if (!dataTransfer) return false;
3491
- dataTransfer.setData("text/plain", "_");
3492
- dataTransfer.setDragImage(img2, 0, 0);
3493
- dataTransfer.setData(
3494
- "application/x-lexical-drag",
3495
- JSON.stringify({
3496
- data: {
3497
- altText: node.__altText,
3498
- caption: node.__caption,
3499
- height: node.__height,
3500
- key: node.getKey(),
3501
- showCaption: node.__showCaption,
3502
- src: node.__src,
3503
- width: node.__width
3504
- },
3505
- type: "image"
3506
- })
3507
- );
3508
- return true;
3509
- }
3510
- var $onDragover2 = (event) => {
3511
- const node = $getImageNodeInSelection2();
3512
- if (!node) return false;
3513
- if (!canDropImage2(event)) {
3514
- event.preventDefault();
3515
- }
3516
- return true;
3517
- };
3518
- var $onDrop2 = (event, editor) => {
3519
- const node = $getImageNodeInSelection2();
3520
- if (!node) return false;
3521
- const data = getDragImageData2(event);
3522
- if (!data) return false;
3523
- event.preventDefault();
3524
- if (canDropImage2(event)) {
3525
- const range = getDragSelection2(event);
3526
- node.remove();
3527
- const rangeSelection = lexical.$createRangeSelection();
3528
- if (range !== null && range !== void 0) {
3529
- rangeSelection.applyDOMRange(range);
3530
- }
3531
- lexical.$setSelection(rangeSelection);
3532
- editor.dispatchCommand(INSERT_INLINE_IMAGE_COMMAND, data);
3533
- }
3534
- return true;
3535
- };
3536
- var $getImageNodeInSelection2 = () => {
3537
- const selection = lexical.$getSelection();
3538
- if (!lexical.$isNodeSelection(selection)) return null;
3539
- const nodes = selection.getNodes();
3540
- const node = nodes[0];
3541
- return $isInlineImageNode(node) ? node : null;
3542
- };
3543
- var getDragImageData2 = (event) => {
3544
- const dragData = event.dataTransfer?.getData("application/x-lexical-drag");
3545
- if (!dragData) return null;
3546
- const { type, data } = JSON.parse(dragData);
3547
- if (type !== "image") return null;
3548
- return data;
3549
- };
3550
- var canDropImage2 = (event) => {
3551
- const target = event.target;
3552
- return !!(lexical.isHTMLElement(target) && !target.closest("code, span.editor-image") && lexical.isHTMLElement(target.parentElement) && target.parentElement.closest("div.ContentEditable__root"));
3553
- };
3554
- var getDragSelection2 = (event) => {
3555
- let range;
3556
- const domSelection = lexical.getDOMSelectionFromTarget(event.target);
3557
- if (document.caretRangeFromPoint) {
3558
- range = document.caretRangeFromPoint(event.clientX, event.clientY);
3559
- } else if (event.rangeParent && domSelection !== null) {
3560
- domSelection.collapse(event.rangeParent, event.rangeOffset || 0);
3561
- range = domSelection.getRangeAt(0);
3562
- } else {
3563
- throw Error("Cannot get the selection when dragging");
3564
- }
3565
- return range;
3566
- };
3567
- var INSERT_PAGE_BREAK = lexical.createCommand();
3568
- function PageBreakPlugin() {
3569
- const [editor] = LexicalComposerContext.useLexicalComposerContext();
3570
- React9.useEffect(() => {
3571
- if (!editor.hasNodes([PageBreakNode])) {
3572
- throw new Error(
3573
- "PageBreakPlugin: PageBreakNode is not registered on editor"
3574
- );
3575
- }
3576
- return utils.mergeRegister(
3577
- editor.registerCommand(
3578
- INSERT_PAGE_BREAK,
3579
- () => {
3580
- const selection = lexical.$getSelection();
3581
- if (!lexical.$isRangeSelection(selection)) {
3582
- return false;
3583
- }
3584
- const focusNode = selection.focus.getNode();
3585
- if (focusNode !== null) {
3586
- const pgBreak = $createPageBreakNode();
3587
- utils.$insertNodeToNearestRoot(pgBreak);
3588
- }
3589
- return true;
3590
- },
3591
- lexical.COMMAND_PRIORITY_EDITOR
3592
- )
3593
- );
3594
- }, [editor]);
3595
- return null;
3596
- }
3597
-
3598
- // src/Utils/Sanitize.ts
3599
- var DROP_ENTIRELY = /* @__PURE__ */ new Set([
3600
- "script",
3601
- "noscript",
3602
- "style",
3603
- "object",
3604
- "embed",
3605
- "form",
3606
- "input",
3607
- "button",
3608
- "select",
3609
- "textarea",
3610
- "meta",
3611
- "link",
3612
- "base"
3613
- ]);
3614
- var ALLOWED_TAGS = /* @__PURE__ */ new Set([
3615
- // Block
3616
- "p",
3617
- "h1",
3618
- "h2",
3619
- "h3",
3620
- "h4",
3621
- "h5",
3622
- "h6",
3623
- "ul",
3624
- "ol",
3625
- "li",
3626
- "blockquote",
3627
- "pre",
3628
- "div",
3629
- "table",
3630
- "thead",
3631
- "tbody",
3632
- "tfoot",
3633
- "tr",
3634
- "td",
3635
- "th",
3636
- // Inline
3637
- "span",
3638
- "a",
3639
- "strong",
3640
- "b",
3641
- "em",
3642
- "i",
3643
- "u",
3644
- "s",
3645
- "del",
3646
- "strike",
3647
- "sub",
3648
- "sup",
3649
- "mark",
3650
- "code",
3651
- "br",
3652
- "hr",
3653
- // Media / embeds
3654
- "img",
3655
- "iframe"
3656
- ]);
3657
- var ALLOWED_ATTRS = /* @__PURE__ */ new Set([
3658
- // Presentation
3659
- "class",
3660
- "style",
3661
- "dir",
3662
- "lang",
3663
- // Anchors
3664
- "href",
3665
- "target",
3666
- "rel",
3667
- // Images
3668
- "src",
3669
- "alt",
3670
- "width",
3671
- "height",
3672
- // Tables
3673
- "colspan",
3674
- "rowspan",
3675
- // Lists — <ol type="a"> and <ol start="2">
3676
- "start",
3677
- "type",
3678
- // Lexical-internal data markers
3679
- "data-lex-block",
3680
- "data-kind",
3681
- "data-lexical-decorator",
3682
- // Misc
3683
- "title",
3684
- "allowfullscreen"
3685
- ]);
3686
- var DANGEROUS_URL = /^\s*(javascript|data\s*:|vbscript)/i;
3687
- function sanitizeElement(el) {
3688
- for (const child of Array.from(el.children)) {
3689
- sanitizeElement(child);
3690
- }
3691
- const tag = el.tagName.toLowerCase();
3692
- if (DROP_ENTIRELY.has(tag)) {
3693
- el.parentNode?.removeChild(el);
3694
- return;
3695
- }
3696
- if (tag === "iframe") {
3697
- const src = el.getAttribute("src") ?? "";
3698
- const isYouTube = /^\s*https:\/\/(www\.)?(youtube\.com|youtube-nocookie\.com)\/embed\/[^?&/]+/i.test(src);
3699
- if (!isYouTube) {
3700
- el.parentNode?.removeChild(el);
3701
- return;
3702
- }
3703
- }
3704
- if (!ALLOWED_TAGS.has(tag)) {
3705
- while (el.firstChild) {
3706
- el.parentNode?.insertBefore(el.firstChild, el);
3707
- }
3708
- el.parentNode?.removeChild(el);
3709
- return;
3710
- }
3711
- for (const { name, value } of Array.from(el.attributes)) {
3712
- const lname = name.toLowerCase();
3713
- if (!ALLOWED_ATTRS.has(lname)) {
3714
- el.removeAttribute(name);
3715
- continue;
3716
- }
3717
- if (lname === "href" || lname === "src") {
3718
- if (DANGEROUS_URL.test(value)) {
3719
- el.removeAttribute(name);
3720
- }
3721
- }
3722
- if (lname === "style") {
3723
- const safe = value.replace(/expression\s*\(/gi, "(").replace(/url\s*\(\s*['"]?\s*javascript:/gi, "url(");
3724
- el.setAttribute("style", safe);
3725
- }
3726
- }
3727
- if (tag === "a" && (el.getAttribute("target") || "").toLowerCase() === "_blank") {
3728
- const rel = (el.getAttribute("rel") || "").trim();
3729
- const tokens = new Set(
3730
- rel.split(/\s+/).filter(Boolean).map((t) => t.toLowerCase())
3731
- );
3732
- tokens.add("noopener");
3733
- tokens.add("noreferrer");
3734
- el.setAttribute("rel", Array.from(tokens).join(" "));
3735
- }
3736
- }
3737
- function sanitizeHtml(html) {
3738
- if (!html || typeof html !== "string") return "";
3739
- const doc = new DOMParser().parseFromString(html, "text/html");
3740
- for (const child of Array.from(doc.body.children)) {
3741
- sanitizeElement(child);
3742
- }
3743
- return doc.body.innerHTML;
3744
- }
3745
- var BLOCK_TAGS = /* @__PURE__ */ new Set([
3746
- "p",
3747
- "h1",
3748
- "h2",
3749
- "h3",
3750
- "h4",
3751
- "h5",
3752
- "h6",
3753
- "div",
3754
- "ul",
3755
- "ol",
3756
- "li",
3757
- "table",
3758
- "blockquote",
3759
- "pre",
3760
- "hr"
3761
- ]);
3762
- function normalizeToBlockHtml(html) {
3763
- if (!html) return "";
3764
- const doc = new DOMParser().parseFromString(html, "text/html");
3765
- const body = doc.body;
3766
- const childNodes = Array.from(body.childNodes);
3767
- const needsWrap = childNodes.some((node) => {
3768
- if (node.nodeType === Node.TEXT_NODE) return !!node.nodeValue?.trim();
3769
- if (node.nodeType === Node.ELEMENT_NODE)
3770
- return !BLOCK_TAGS.has(node.tagName.toLowerCase());
3771
- return false;
3772
- });
3773
- if (!needsWrap) return html;
3774
- while (body.firstChild) body.removeChild(body.firstChild);
3775
- let pendingP = null;
3776
- for (const node of childNodes) {
3777
- const isBlock = node.nodeType === Node.ELEMENT_NODE && BLOCK_TAGS.has(node.tagName.toLowerCase());
3778
- const isWhitespace = node.nodeType === Node.TEXT_NODE && !node.nodeValue?.trim();
3779
- if (isBlock) {
3780
- if (pendingP) {
3781
- body.appendChild(pendingP);
3782
- pendingP = null;
3783
- }
3784
- body.appendChild(node);
3785
- } else if (!isWhitespace) {
3786
- if (!pendingP) pendingP = doc.createElement("p");
3787
- pendingP.appendChild(node);
3761
+ dataTransfer.setData("text/plain", "_");
3762
+ dataTransfer.setDragImage(img2, 0, 0);
3763
+ dataTransfer.setData(
3764
+ "application/x-lexical-drag",
3765
+ JSON.stringify({
3766
+ data: {
3767
+ altText: node.__altText,
3768
+ caption: node.__caption,
3769
+ height: node.__height,
3770
+ key: node.getKey(),
3771
+ showCaption: node.__showCaption,
3772
+ src: node.__src,
3773
+ width: node.__width
3774
+ },
3775
+ type: "image"
3776
+ })
3777
+ );
3778
+ return true;
3779
+ }
3780
+ var $onDragover2 = (event) => {
3781
+ const node = $getImageNodeInSelection2();
3782
+ if (!node) return false;
3783
+ if (!canDropImage2(event)) {
3784
+ event.preventDefault();
3785
+ }
3786
+ return true;
3787
+ };
3788
+ var $onDrop2 = (event, editor) => {
3789
+ const node = $getImageNodeInSelection2();
3790
+ if (!node) return false;
3791
+ const data = getDragImageData2(event);
3792
+ if (!data) return false;
3793
+ event.preventDefault();
3794
+ if (canDropImage2(event)) {
3795
+ const range = getDragSelection2(event);
3796
+ node.remove();
3797
+ const rangeSelection = lexical.$createRangeSelection();
3798
+ if (range !== null && range !== void 0) {
3799
+ rangeSelection.applyDOMRange(range);
3788
3800
  }
3801
+ lexical.$setSelection(rangeSelection);
3802
+ editor.dispatchCommand(INSERT_INLINE_IMAGE_COMMAND, data);
3789
3803
  }
3790
- if (pendingP) body.appendChild(pendingP);
3791
- return body.innerHTML;
3804
+ return true;
3805
+ };
3806
+ var $getImageNodeInSelection2 = () => {
3807
+ const selection = lexical.$getSelection();
3808
+ if (!lexical.$isNodeSelection(selection)) return null;
3809
+ const nodes = selection.getNodes();
3810
+ const node = nodes[0];
3811
+ return $isInlineImageNode(node) ? node : null;
3812
+ };
3813
+ var getDragImageData2 = (event) => {
3814
+ const dragData = event.dataTransfer?.getData("application/x-lexical-drag");
3815
+ if (!dragData) return null;
3816
+ const { type, data } = JSON.parse(dragData);
3817
+ if (type !== "image") return null;
3818
+ return data;
3819
+ };
3820
+ var canDropImage2 = (event) => {
3821
+ const target = event.target;
3822
+ return !!(lexical.isHTMLElement(target) && !target.closest("code, span.editor-image") && lexical.isHTMLElement(target.parentElement) && target.parentElement.closest("div.ContentEditable__root"));
3823
+ };
3824
+ var getDragSelection2 = (event) => {
3825
+ let range;
3826
+ const domSelection = lexical.getDOMSelectionFromTarget(event.target);
3827
+ if (document.caretRangeFromPoint) {
3828
+ range = document.caretRangeFromPoint(event.clientX, event.clientY);
3829
+ } else if (event.rangeParent && domSelection !== null) {
3830
+ domSelection.collapse(event.rangeParent, event.rangeOffset || 0);
3831
+ range = domSelection.getRangeAt(0);
3832
+ } else {
3833
+ throw Error("Cannot get the selection when dragging");
3834
+ }
3835
+ return range;
3836
+ };
3837
+ var INSERT_PAGE_BREAK = lexical.createCommand();
3838
+ function PageBreakPlugin() {
3839
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
3840
+ React9.useEffect(() => {
3841
+ if (!editor.hasNodes([PageBreakNode])) {
3842
+ throw new Error(
3843
+ "PageBreakPlugin: PageBreakNode is not registered on editor"
3844
+ );
3845
+ }
3846
+ return utils.mergeRegister(
3847
+ editor.registerCommand(
3848
+ INSERT_PAGE_BREAK,
3849
+ () => {
3850
+ const selection = lexical.$getSelection();
3851
+ if (!lexical.$isRangeSelection(selection)) {
3852
+ return false;
3853
+ }
3854
+ const focusNode = selection.focus.getNode();
3855
+ if (focusNode !== null) {
3856
+ const pgBreak = $createPageBreakNode();
3857
+ utils.$insertNodeToNearestRoot(pgBreak);
3858
+ }
3859
+ return true;
3860
+ },
3861
+ lexical.COMMAND_PRIORITY_EDITOR
3862
+ )
3863
+ );
3864
+ }, [editor]);
3865
+ return null;
3792
3866
  }
3793
-
3794
- // src/Utils/Helper.ts
3795
3867
  function findBlockByKind(kind) {
3796
3868
  const root = lexical.$getRoot();
3797
3869
  for (const child of root.getChildren()) {
@@ -3863,7 +3935,7 @@ function RefApiPlugin({
3863
3935
  editor.getEditorState().read(() => {
3864
3936
  html$1 = html.$generateHtmlFromNodes(editor, null);
3865
3937
  });
3866
- return html$1;
3938
+ return postProcessOutput(html$1);
3867
3939
  },
3868
3940
  clear: () => {
3869
3941
  editor.update(() => {
@@ -5012,7 +5084,8 @@ function getToolbarGroupsByLevel(level) {
5012
5084
  ["FontFamily", "|"],
5013
5085
  ["FontSize", "|"],
5014
5086
  ["Decorators", "|"],
5015
- ["Align"]
5087
+ ["Align", "|"],
5088
+ ["PageSetup"]
5016
5089
  ];
5017
5090
  case "pro" /* Pro */:
5018
5091
  default:
@@ -5027,7 +5100,8 @@ function getToolbarGroupsByLevel(level) {
5027
5100
  ["FontFamily", "|"],
5028
5101
  ["FontSize", "|"],
5029
5102
  ["Decorators", "|"],
5030
- ["Align"]
5103
+ ["Align", "|"],
5104
+ ["PageSetup"]
5031
5105
  ];
5032
5106
  }
5033
5107
  }
@@ -5065,6 +5139,7 @@ var normalizeHex = (v) => {
5065
5139
  if (hex.length === 4 || hex.length === 7) return hex.toLowerCase();
5066
5140
  return "#000000";
5067
5141
  };
5142
+ var isCompleteHex = (v) => /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test((v ?? "").trim());
5068
5143
  var hexToRgb = (hex) => {
5069
5144
  const h = normalizeHex(hex).replace("#", "");
5070
5145
  if (h.length === 3) {
@@ -5112,40 +5187,6 @@ var hsvToRgb = (h, s, v) => {
5112
5187
  b: Math.round((bb + m) * 255)
5113
5188
  };
5114
5189
  };
5115
- function useDrag(onMove, onEnd, interactingRef) {
5116
- const draggingRef = React9__namespace.useRef(false);
5117
- const start = React9__namespace.useCallback(
5118
- (e) => {
5119
- draggingRef.current = true;
5120
- if (interactingRef) interactingRef.current = true;
5121
- onMove(e.clientX, e.clientY);
5122
- const move = (ev) => {
5123
- if (!draggingRef.current) return;
5124
- onMove(ev.clientX, ev.clientY);
5125
- };
5126
- const up = () => {
5127
- draggingRef.current = false;
5128
- window.removeEventListener("mousemove", move);
5129
- window.removeEventListener("mouseup", up);
5130
- if (interactingRef) {
5131
- const clearFlag = () => {
5132
- interactingRef.current = false;
5133
- };
5134
- window.addEventListener("click", clearFlag, { once: true });
5135
- setTimeout(() => {
5136
- window.removeEventListener("click", clearFlag);
5137
- interactingRef.current = false;
5138
- }, 0);
5139
- }
5140
- onEnd?.();
5141
- };
5142
- window.addEventListener("mousemove", move);
5143
- window.addEventListener("mouseup", up);
5144
- },
5145
- [onMove, onEnd, interactingRef]
5146
- );
5147
- return start;
5148
- }
5149
5190
  var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange }) => {
5150
5191
  const [open, setOpen] = React9__namespace.useState(false);
5151
5192
  const btnRef = React9__namespace.useRef(null);
@@ -5156,13 +5197,9 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5156
5197
  },
5157
5198
  [onOpenChange]
5158
5199
  );
5159
- const interactingRef = React9__namespace.useRef(false);
5160
5200
  const handleDismiss = React9__namespace.useCallback(() => setOpenAndNotify(false), [setOpenAndNotify]);
5161
5201
  const preventDismissOnEvent = React9__namespace.useCallback(
5162
- (ev) => {
5163
- if (interactingRef.current) return true;
5164
- return ev.type !== "click";
5165
- },
5202
+ (ev) => ev.type !== "click",
5166
5203
  []
5167
5204
  );
5168
5205
  const [, forceReposition] = React9__namespace.useState(0);
@@ -5184,100 +5221,119 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5184
5221
  window.removeEventListener("resize", reposition);
5185
5222
  };
5186
5223
  }, [open]);
5187
- const [hex, setHex] = React9__namespace.useState(normalizeHex(value || "#000000"));
5224
+ const appliedHex = React9__namespace.useMemo(() => normalizeHex(value || "#000000"), [value]);
5225
+ const [hex, setHexState] = React9__namespace.useState(appliedHex);
5226
+ const [hexText, setHexText] = React9__namespace.useState(appliedHex);
5188
5227
  const { r, g, b } = React9__namespace.useMemo(() => hexToRgb(hex), [hex]);
5189
5228
  const hsv = React9__namespace.useMemo(() => rgbToHsv(r, g, b), [r, g, b]);
5190
5229
  const [h, setH] = React9__namespace.useState(hsv.h);
5191
5230
  const [s, setS] = React9__namespace.useState(hsv.s);
5192
5231
  const [v, setV] = React9__namespace.useState(hsv.v);
5232
+ const commit = React9__namespace.useCallback(
5233
+ (nextHex) => {
5234
+ const rgb = hexToRgb(nextHex);
5235
+ const next = rgbToHsv(rgb.r, rgb.g, rgb.b);
5236
+ setHexState(nextHex);
5237
+ setHexText(nextHex);
5238
+ setH(next.h);
5239
+ setS(next.s);
5240
+ setV(next.v);
5241
+ onChange(nextHex);
5242
+ },
5243
+ [onChange]
5244
+ );
5245
+ const commitFromHsv = React9__namespace.useCallback(
5246
+ (hh, ss, vv) => {
5247
+ const rgb = hsvToRgb(hh, ss, vv);
5248
+ const nextHex = rgbToHex(rgb.r, rgb.g, rgb.b);
5249
+ setHexState(nextHex);
5250
+ setHexText(nextHex);
5251
+ setH(hh);
5252
+ setS(ss);
5253
+ setV(vv);
5254
+ onChange(nextHex);
5255
+ },
5256
+ [onChange]
5257
+ );
5193
5258
  const wasOpenRef = React9__namespace.useRef(open);
5194
- console.log("[AO-ColorPicker]", title, "render with incoming value prop:", JSON.stringify(value));
5195
5259
  React9__namespace.useEffect(() => {
5196
5260
  const justOpened = open && !wasOpenRef.current;
5197
5261
  wasOpenRef.current = open;
5198
- console.log("[AO-ColorPicker]", title, "open-seed effect", {
5199
- open,
5200
- justOpened,
5201
- incomingValue: value
5202
- });
5203
5262
  if (!justOpened) return;
5204
- const n = normalizeHex(value || "#000000");
5205
- console.log("[AO-ColorPicker]", title, "seeding local state from value on open", {
5206
- incomingValue: value,
5207
- normalized: n
5208
- });
5209
- setHex(n);
5210
- const rgb = hexToRgb(n);
5263
+ setHexState(appliedHex);
5264
+ setHexText(appliedHex);
5265
+ const rgb = hexToRgb(appliedHex);
5211
5266
  const next = rgbToHsv(rgb.r, rgb.g, rgb.b);
5212
5267
  setH(next.h);
5213
5268
  setS(next.s);
5214
5269
  setV(next.v);
5215
- }, [value, open]);
5216
- const updateHexFromHsv = React9__namespace.useCallback((hh, ss, vv) => {
5217
- const rgb = hsvToRgb(hh, ss, vv);
5218
- const nextHex = rgbToHex(rgb.r, rgb.g, rgb.b);
5219
- setHex(nextHex);
5220
- return nextHex;
5270
+ }, [appliedHex, open]);
5271
+ const svRef = React9__namespace.useRef(null);
5272
+ const svPointFromEvent = React9__namespace.useCallback((clientX, clientY) => {
5273
+ if (!svRef.current) return null;
5274
+ const rect = svRef.current.getBoundingClientRect();
5275
+ const x = clamp3(clientX - rect.left, 0, rect.width);
5276
+ const y = clamp3(clientY - rect.top, 0, rect.height);
5277
+ const ss = rect.width === 0 ? 0 : x / rect.width;
5278
+ const vv = rect.height === 0 ? 0 : 1 - y / rect.height;
5279
+ return { ss, vv };
5221
5280
  }, []);
5222
- const commitHsv = React9__namespace.useCallback(
5223
- (hh, ss, vv, close) => {
5224
- const nextHex = updateHexFromHsv(hh, ss, vv);
5225
- console.log("[AO-ColorPicker]", title, "commitHsv -> onChange", { nextHex, close: !!close });
5226
- onChange(nextHex);
5227
- if (close) setOpenAndNotify(false);
5281
+ const hRef = React9__namespace.useRef(h);
5282
+ hRef.current = h;
5283
+ const handleSVPointerDown = React9__namespace.useCallback(
5284
+ (e) => {
5285
+ e.currentTarget.setPointerCapture(e.pointerId);
5286
+ const pt = svPointFromEvent(e.clientX, e.clientY);
5287
+ if (pt) commitFromHsv(hRef.current, pt.ss, pt.vv);
5228
5288
  },
5229
- [onChange, title, setOpenAndNotify, updateHexFromHsv]
5289
+ [svPointFromEvent, commitFromHsv]
5230
5290
  );
5231
- const hRef = React9__namespace.useRef(h);
5291
+ const handleSVPointerMove = React9__namespace.useCallback(
5292
+ (e) => {
5293
+ if (e.buttons !== 1) return;
5294
+ const pt = svPointFromEvent(e.clientX, e.clientY);
5295
+ if (pt) commitFromHsv(hRef.current, pt.ss, pt.vv);
5296
+ },
5297
+ [svPointFromEvent, commitFromHsv]
5298
+ );
5299
+ const hueRef = React9__namespace.useRef(null);
5232
5300
  const sRef = React9__namespace.useRef(s);
5301
+ sRef.current = s;
5233
5302
  const vRef = React9__namespace.useRef(v);
5234
- React9__namespace.useEffect(() => {
5235
- hRef.current = h;
5236
- }, [h]);
5237
- React9__namespace.useEffect(() => {
5238
- sRef.current = s;
5239
- }, [s]);
5240
- React9__namespace.useEffect(() => {
5241
- vRef.current = v;
5242
- }, [v]);
5243
- const svRef = React9__namespace.useRef(null);
5244
- const onSVMove = React9__namespace.useCallback(
5245
- (clientX, clientY) => {
5246
- if (!svRef.current) return;
5247
- const rect = svRef.current.getBoundingClientRect();
5248
- const x = clamp3(clientX - rect.left, 0, rect.width);
5249
- const y = clamp3(clientY - rect.top, 0, rect.height);
5250
- const ss = rect.width === 0 ? 0 : x / rect.width;
5251
- const vv = rect.height === 0 ? 0 : 1 - y / rect.height;
5252
- setS(ss);
5253
- setV(vv);
5254
- sRef.current = ss;
5255
- vRef.current = vv;
5256
- updateHexFromHsv(hRef.current, ss, vv);
5303
+ vRef.current = v;
5304
+ const huePointFromEvent = React9__namespace.useCallback((clientX) => {
5305
+ if (!hueRef.current) return null;
5306
+ const rect = hueRef.current.getBoundingClientRect();
5307
+ const x = clamp3(clientX - rect.left, 0, rect.width);
5308
+ return rect.width === 0 ? 0 : x / rect.width * 360;
5309
+ }, []);
5310
+ const handleHuePointerDown = React9__namespace.useCallback(
5311
+ (e) => {
5312
+ e.currentTarget.setPointerCapture(e.pointerId);
5313
+ const hh = huePointFromEvent(e.clientX);
5314
+ if (hh != null) commitFromHsv(hh, sRef.current, vRef.current);
5257
5315
  },
5258
- [updateHexFromHsv]
5316
+ [huePointFromEvent, commitFromHsv]
5259
5317
  );
5260
- const commitSV = React9__namespace.useCallback(() => {
5261
- commitHsv(hRef.current, sRef.current, vRef.current);
5262
- }, [commitHsv]);
5263
- const startSV = useDrag(onSVMove, commitSV, interactingRef);
5264
- const hueRef = React9__namespace.useRef(null);
5265
- const onHueMove = React9__namespace.useCallback(
5266
- (clientX) => {
5267
- if (!hueRef.current) return;
5268
- const rect = hueRef.current.getBoundingClientRect();
5269
- const x = clamp3(clientX - rect.left, 0, rect.width);
5270
- const hh = rect.width === 0 ? 0 : x / rect.width * 360;
5271
- setH(hh);
5272
- hRef.current = hh;
5273
- updateHexFromHsv(hh, sRef.current, vRef.current);
5318
+ const handleHuePointerMove = React9__namespace.useCallback(
5319
+ (e) => {
5320
+ if (e.buttons !== 1) return;
5321
+ const hh = huePointFromEvent(e.clientX);
5322
+ if (hh != null) commitFromHsv(hh, sRef.current, vRef.current);
5323
+ },
5324
+ [huePointFromEvent, commitFromHsv]
5325
+ );
5326
+ const handleHexChange = React9__namespace.useCallback(
5327
+ (_, val) => {
5328
+ const next = val ?? "";
5329
+ setHexText(next);
5330
+ if (isCompleteHex(next.trim())) commit(normalizeHex(next));
5274
5331
  },
5275
- [updateHexFromHsv]
5332
+ [commit]
5276
5333
  );
5277
- const commitHue = React9__namespace.useCallback(() => {
5278
- commitHsv(hRef.current, sRef.current, vRef.current);
5279
- }, [commitHsv]);
5280
- const startHue = useDrag((x) => onHueMove(x), commitHue, interactingRef);
5334
+ const handleHexBlur = React9__namespace.useCallback(() => {
5335
+ commit(normalizeHex(hexText));
5336
+ }, [hexText, commit]);
5281
5337
  const svThumb = React9__namespace.useMemo(() => ({ left: `${s * 100}%`, top: `${(1 - v) * 100}%` }), [s, v]);
5282
5338
  const hueThumb = React9__namespace.useMemo(() => ({ left: `${h / 360 * 100}%` }), [h]);
5283
5339
  const hueColor = React9__namespace.useMemo(() => {
@@ -5302,10 +5358,6 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5302
5358
  },
5303
5359
  onClick: () => {
5304
5360
  if (disabled) return;
5305
- console.log("[AO-ColorPicker]", title, "trigger button clicked", {
5306
- wasOpen: open,
5307
- activeElementBeforeToggle: document.activeElement?.tagName
5308
- });
5309
5361
  setOpenAndNotify(!open);
5310
5362
  }
5311
5363
  }
@@ -5320,7 +5372,7 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5320
5372
  transform: "translateX(-50%)",
5321
5373
  width: 14,
5322
5374
  height: 3,
5323
- background: hex,
5375
+ background: appliedHex,
5324
5376
  borderRadius: 1,
5325
5377
  border: "0.5px solid rgba(0,0,0,0.18)",
5326
5378
  pointerEvents: "none"
@@ -5336,72 +5388,192 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5336
5388
  directionalHint: 4,
5337
5389
  className: "aoColorCallout",
5338
5390
  preventDismissOnEvent,
5339
- children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 10 }, styles: { root: { padding: 12, width: 320 } }, children: [
5340
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aoLexRow", children: [
5341
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexSwatch", style: { background: hex } }),
5342
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexTitle", children: title })
5343
- ] }),
5344
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aoLexRow", children: [
5345
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexLabel", children: "Hex" }),
5391
+ children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 14 }, styles: { root: { padding: "14px 16px 16px", width: 288 } }, children: [
5392
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
5393
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, fontWeight: 600, color: "#242424", letterSpacing: 0.1 }, children: title }),
5346
5394
  /* @__PURE__ */ jsxRuntime.jsx(
5347
- react.TextField,
5395
+ "button",
5348
5396
  {
5349
- value: hex,
5350
- onChange: (_, val) => setHex(normalizeHex(val || "")),
5351
- onBlur: () => {
5352
- const n = normalizeHex(hex);
5353
- setHex(n);
5354
- const rgb = hexToRgb(n);
5355
- const next = rgbToHsv(rgb.r, rgb.g, rgb.b);
5356
- setH(next.h);
5357
- setS(next.s);
5358
- setV(next.v);
5359
- console.log("[AO-ColorPicker]", title, "hex field blur -> onChange", { raw: hex, normalized: n });
5360
- onChange(n);
5361
- }
5397
+ type: "button",
5398
+ "aria-label": "Close",
5399
+ onClick: () => setOpenAndNotify(false),
5400
+ style: {
5401
+ display: "flex",
5402
+ alignItems: "center",
5403
+ justifyContent: "center",
5404
+ width: 24,
5405
+ height: 24,
5406
+ padding: 0,
5407
+ border: "none",
5408
+ borderRadius: 4,
5409
+ background: "transparent",
5410
+ color: "#616161",
5411
+ cursor: "pointer"
5412
+ },
5413
+ onMouseEnter: (e) => e.currentTarget.style.background = "#f0f0f0",
5414
+ onMouseLeave: (e) => e.currentTarget.style.background = "transparent",
5415
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.Dismiss16Regular, {})
5362
5416
  }
5363
5417
  )
5364
5418
  ] }),
5365
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexSwatches", children: PRESET.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
5366
- "button",
5419
+ /* @__PURE__ */ jsxRuntime.jsxs(
5420
+ "div",
5367
5421
  {
5368
- type: "button",
5369
- className: "aoLexSwatchBtn",
5370
- style: { background: c },
5371
- onClick: () => {
5372
- setHex(c);
5373
- const rgb = hexToRgb(c);
5374
- const next = rgbToHsv(rgb.r, rgb.g, rgb.b);
5375
- setH(next.h);
5376
- setS(next.s);
5377
- setV(next.v);
5378
- console.log("[AO-ColorPicker]", title, "preset swatch click -> onChange", { color: c });
5379
- onChange(c);
5422
+ ref: svRef,
5423
+ onPointerDown: handleSVPointerDown,
5424
+ onPointerMove: handleSVPointerMove,
5425
+ style: {
5426
+ position: "relative",
5427
+ width: "100%",
5428
+ height: 150,
5429
+ borderRadius: 8,
5430
+ overflow: "hidden",
5431
+ cursor: "crosshair",
5432
+ touchAction: "none",
5433
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.08)"
5380
5434
  },
5381
- title: c
5382
- },
5383
- c
5384
- )) }),
5385
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aoLexSV", ref: svRef, onMouseDown: startSV, children: [
5386
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexSVHue", style: { background: hueColor } }),
5387
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexSVWhite" }),
5388
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexSVBlack" }),
5389
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexSVThumb", style: svThumb })
5390
- ] }),
5391
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexHue", ref: hueRef, onMouseDown: startHue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexHueThumb", style: hueThumb }) }),
5392
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aoLexPreview", style: { background: hex } }),
5393
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aoLexActions", children: [
5435
+ children: [
5436
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, background: hueColor } }),
5437
+ /* @__PURE__ */ jsxRuntime.jsx(
5438
+ "div",
5439
+ {
5440
+ style: {
5441
+ position: "absolute",
5442
+ inset: 0,
5443
+ background: "linear-gradient(to right, #fff, rgba(255,255,255,0))"
5444
+ }
5445
+ }
5446
+ ),
5447
+ /* @__PURE__ */ jsxRuntime.jsx(
5448
+ "div",
5449
+ {
5450
+ style: {
5451
+ position: "absolute",
5452
+ inset: 0,
5453
+ background: "linear-gradient(to top, #000, rgba(0,0,0,0))"
5454
+ }
5455
+ }
5456
+ ),
5457
+ /* @__PURE__ */ jsxRuntime.jsx(
5458
+ "div",
5459
+ {
5460
+ style: {
5461
+ position: "absolute",
5462
+ width: 16,
5463
+ height: 16,
5464
+ borderRadius: "50%",
5465
+ border: "2px solid #fff",
5466
+ boxShadow: "0 0 0 1px rgba(0,0,0,0.35), 0 1px 3px rgba(0,0,0,0.4)",
5467
+ transform: "translate(-50%, -50%)",
5468
+ pointerEvents: "none",
5469
+ ...svThumb
5470
+ }
5471
+ }
5472
+ )
5473
+ ]
5474
+ }
5475
+ ),
5476
+ /* @__PURE__ */ jsxRuntime.jsx(
5477
+ "div",
5478
+ {
5479
+ ref: hueRef,
5480
+ onPointerDown: handleHuePointerDown,
5481
+ onPointerMove: handleHuePointerMove,
5482
+ style: {
5483
+ position: "relative",
5484
+ width: "100%",
5485
+ height: 12,
5486
+ borderRadius: 999,
5487
+ cursor: "pointer",
5488
+ touchAction: "none",
5489
+ background: "linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)",
5490
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.08)"
5491
+ },
5492
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5493
+ "div",
5494
+ {
5495
+ style: {
5496
+ position: "absolute",
5497
+ top: "50%",
5498
+ width: 16,
5499
+ height: 16,
5500
+ borderRadius: "50%",
5501
+ background: hueColor,
5502
+ border: "2px solid #fff",
5503
+ boxShadow: "0 0 0 1px rgba(0,0,0,0.35), 0 1px 3px rgba(0,0,0,0.4)",
5504
+ transform: "translate(-50%, -50%)",
5505
+ pointerEvents: "none",
5506
+ ...hueThumb
5507
+ }
5508
+ }
5509
+ )
5510
+ }
5511
+ ),
5512
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5394
5513
  /* @__PURE__ */ jsxRuntime.jsx(
5395
- react.DefaultButton,
5514
+ "div",
5396
5515
  {
5397
- type: "button",
5398
- text: "Apply",
5399
- onClick: () => {
5400
- commitHsv(h, s, v, true);
5516
+ style: {
5517
+ width: 32,
5518
+ height: 32,
5519
+ borderRadius: 6,
5520
+ flexShrink: 0,
5521
+ background: hex,
5522
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.12)"
5401
5523
  }
5402
5524
  }
5403
5525
  ),
5404
- /* @__PURE__ */ jsxRuntime.jsx(react.DefaultButton, { type: "button", text: "Close", onClick: () => setOpenAndNotify(false) })
5526
+ /* @__PURE__ */ jsxRuntime.jsx(
5527
+ react.TextField,
5528
+ {
5529
+ value: hexText,
5530
+ onChange: handleHexChange,
5531
+ onBlur: handleHexBlur,
5532
+ onKeyDown: (e) => {
5533
+ if (e.key === "Enter") commit(normalizeHex(hexText));
5534
+ },
5535
+ styles: { root: { flex: 1 }, fieldGroup: { borderRadius: 6 } }
5536
+ }
5537
+ )
5538
+ ] }),
5539
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5540
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 600, color: "#8a8a8a", marginBottom: 6, letterSpacing: 0.3 }, children: "STANDARD COLORS" }),
5541
+ /* @__PURE__ */ jsxRuntime.jsx(
5542
+ "div",
5543
+ {
5544
+ style: {
5545
+ display: "grid",
5546
+ gridTemplateColumns: "repeat(9, 1fr)",
5547
+ gap: 6
5548
+ },
5549
+ children: PRESET.map((c) => {
5550
+ const isSelected = c.toLowerCase() === hex.toLowerCase();
5551
+ return /* @__PURE__ */ jsxRuntime.jsx(
5552
+ "button",
5553
+ {
5554
+ type: "button",
5555
+ onClick: () => commit(c),
5556
+ title: c,
5557
+ "aria-label": c,
5558
+ style: {
5559
+ width: 22,
5560
+ height: 22,
5561
+ padding: 0,
5562
+ borderRadius: 5,
5563
+ background: c,
5564
+ cursor: "pointer",
5565
+ boxShadow: isSelected ? "0 0 0 2px #fff, 0 0 0 3px #4a86e8" : "inset 0 0 0 1px rgba(0,0,0,0.15)",
5566
+ border: "none",
5567
+ transition: "transform 80ms ease"
5568
+ },
5569
+ onMouseEnter: (e) => e.currentTarget.style.transform = "scale(1.12)",
5570
+ onMouseLeave: (e) => e.currentTarget.style.transform = "scale(1)"
5571
+ },
5572
+ c
5573
+ );
5574
+ })
5575
+ }
5576
+ )
5405
5577
  ] })
5406
5578
  ] })
5407
5579
  }
@@ -5414,20 +5586,10 @@ var ColorPickerPlugin = ({ disabled }) => {
5414
5586
  const lastRangeSelectionRef = React9__namespace.default.useRef(null);
5415
5587
  const updateToolbar = () => {
5416
5588
  const selection$1 = lexical.$getSelection();
5417
- const isRange = lexical.$isRangeSelection(selection$1);
5418
- console.log("[AO-ColorPicker] updateToolbar", {
5419
- isRangeSelection: isRange,
5420
- isCollapsed: isRange ? selection$1.isCollapsed() : null
5421
- });
5422
- if (isRange) {
5589
+ if (lexical.$isRangeSelection(selection$1)) {
5423
5590
  lastRangeSelectionRef.current = selection$1.clone();
5424
5591
  const c = selection.$getSelectionStyleValueForProperty(selection$1, "color", "#000000");
5425
- const bg = selection.$getSelectionStyleValueForProperty(
5426
- selection$1,
5427
- "background-color",
5428
- "#ffffff"
5429
- );
5430
- console.log("[AO-ColorPicker] updateToolbar readback", { color: c, bgColor: bg });
5592
+ const bg = selection.$getSelectionStyleValueForProperty(selection$1, "background-color", "#ffffff");
5431
5593
  setColors({ color: c, bgColor: bg });
5432
5594
  }
5433
5595
  };
@@ -5457,15 +5619,6 @@ var ColorPickerPlugin = ({ disabled }) => {
5457
5619
  };
5458
5620
  const applyStyle = (args) => {
5459
5621
  if (disabled) return;
5460
- console.log("[AO-ColorPicker] applyStyle called", {
5461
- property: args.property,
5462
- color: args.color,
5463
- hasSavedSelection: !!lastRangeSelectionRef.current,
5464
- savedSelectionIsCollapsed: lastRangeSelectionRef.current?.isCollapsed() ?? null,
5465
- wasEditorActiveAtOpen: wasEditorActiveRef.current,
5466
- activeElementTag: document.activeElement?.tagName,
5467
- activeElementClass: document.activeElement?.className
5468
- });
5469
5622
  editor.update(
5470
5623
  () => {
5471
5624
  const saved = lastRangeSelectionRef.current;
@@ -5473,39 +5626,17 @@ var ColorPickerPlugin = ({ disabled }) => {
5473
5626
  lexical.$setSelection(saved.clone());
5474
5627
  }
5475
5628
  const selection$1 = lexical.$getSelection();
5476
- const isRange = lexical.$isRangeSelection(selection$1);
5477
- console.log("[AO-ColorPicker] applyStyle inside editor.update", {
5478
- hadSavedSelection: !!saved,
5479
- selectionAfterRestoreIsRange: isRange,
5480
- selectionAfterRestoreIsCollapsed: isRange ? selection$1.isCollapsed() : null
5481
- });
5482
- if (isRange) {
5629
+ if (lexical.$isRangeSelection(selection$1)) {
5483
5630
  selection.$patchStyleText(selection$1, { [args.property]: args.color });
5484
- const verify = selection.$getSelectionStyleValueForProperty(
5485
- selection$1,
5486
- args.property,
5487
- "<none>"
5488
- );
5489
- console.log("[AO-ColorPicker] applyStyle after patchStyleText, readback in same update", {
5490
- property: args.property,
5491
- appliedColor: args.color,
5492
- readBack: verify
5493
- });
5494
- } else {
5495
- console.warn(
5496
- "[AO-ColorPicker] applyStyle: no range selection available \u2014 style was NOT applied",
5497
- { property: args.property, color: args.color }
5498
- );
5499
5631
  }
5500
5632
  },
5501
5633
  // Without this tag, Lexical's reconciler force-focuses the editor root
5502
5634
  // whenever this update's selection diff finds the root isn't already
5503
- // focused (see Lexical.dev.mjs ~8112) — which it never is while the
5504
- // color picker's Callout legitimately holds focus. That forced focus
5505
- // then fights Fluent's Callout for focus on every single drag-driven
5506
- // commit, repeatedly bouncing focus (and, via FocusEventsPlugin,
5507
- // nulling and restoring the selection) between the editor and the
5508
- // popover until the drag's tracked color desynced from the cursor.
5635
+ // focused — which it isn't while the color picker's Callout holds
5636
+ // focus. The picker now only calls applyStyle once, on Apply, so this
5637
+ // is no longer fighting for focus on every drag pixel, but there's no
5638
+ // reason to force a focus change here at all — handleOpenChange above
5639
+ // already does that deliberately, once, on close.
5509
5640
  { tag: lexical.SKIP_SELECTION_FOCUS_TAG }
5510
5641
  );
5511
5642
  };
@@ -5874,6 +6005,60 @@ var InsertLinkPlugin = ({ disabled }) => {
5874
6005
  }
5875
6006
  );
5876
6007
  };
6008
+ function PageSetupPlugin({ disabled, value, onChange }) {
6009
+ const sizeLabel = value.size === "pageless" ? "Pageless" : PAGE_SIZE_OPTIONS.find((o) => o.key === value.size)?.label ?? "Pageless";
6010
+ const isPaged = value.size !== "pageless";
6011
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6012
+ reactComponents.Menu,
6013
+ {
6014
+ checkedValues: {
6015
+ size: [value.size],
6016
+ orientation: [value.orientation],
6017
+ margin: [value.margin]
6018
+ },
6019
+ onCheckedValueChange: (_, data) => {
6020
+ const selected = data.checkedItems[0];
6021
+ if (!selected) return;
6022
+ if (data.name === "size") onChange({ ...value, size: selected });
6023
+ else if (data.name === "orientation")
6024
+ onChange({ ...value, orientation: selected });
6025
+ else if (data.name === "margin") onChange({ ...value, margin: selected });
6026
+ },
6027
+ children: [
6028
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
6029
+ reactComponents.Button,
6030
+ {
6031
+ appearance: "subtle",
6032
+ size: "small",
6033
+ disabled,
6034
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.DocumentRegular, {}),
6035
+ title: "Page setup",
6036
+ style: { minWidth: "auto" },
6037
+ children: sizeLabel
6038
+ }
6039
+ ) }),
6040
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuPopover, { style: { minWidth: 220 }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.MenuList, { children: [
6041
+ /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.MenuGroup, { children: [
6042
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuGroupHeader, { children: "Page size" }),
6043
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuItemRadio, { name: "size", value: "pageless", children: "Pageless" }),
6044
+ PAGE_SIZE_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuItemRadio, { name: "size", value: opt.key, children: opt.label }, opt.key))
6045
+ ] }),
6046
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuDivider, {}),
6047
+ /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.MenuGroup, { children: [
6048
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuGroupHeader, { children: "Orientation" }),
6049
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuItemRadio, { name: "orientation", value: "portrait", disabled: !isPaged, children: "Portrait" }),
6050
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuItemRadio, { name: "orientation", value: "landscape", disabled: !isPaged, children: "Landscape" })
6051
+ ] }),
6052
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuDivider, {}),
6053
+ /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.MenuGroup, { children: [
6054
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuGroupHeader, { children: "Margins" }),
6055
+ MARGIN_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuItemRadio, { name: "margin", value: opt.key, disabled: !isPaged, children: opt.label }, opt.key))
6056
+ ] })
6057
+ ] }) })
6058
+ ]
6059
+ }
6060
+ );
6061
+ }
5877
6062
  var TableItemPlugin = ({ disabled }) => {
5878
6063
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
5879
6064
  const [columns, setColumns] = React9.useState("");
@@ -6091,7 +6276,8 @@ var ALLOWED_TOKENS = {
6091
6276
  FontSize: true,
6092
6277
  Decorators: true,
6093
6278
  CodeBlock: true,
6094
- Align: true
6279
+ Align: true,
6280
+ PageSetup: true
6095
6281
  };
6096
6282
  function sanitizePluginGroups(groups) {
6097
6283
  if (!groups || groups.length === 0) return [];
@@ -6630,6 +6816,16 @@ var ToolBarPlugins = (props) => {
6630
6816
  key
6631
6817
  );
6632
6818
  }
6819
+ case "PageSetup":
6820
+ return /* @__PURE__ */ jsxRuntime.jsx(
6821
+ PageSetupPlugin,
6822
+ {
6823
+ disabled: !isEditable || props.readOnly,
6824
+ value: props.pageSetup,
6825
+ onChange: props.onPageSetupChange
6826
+ },
6827
+ key
6828
+ );
6633
6829
  default:
6634
6830
  return null;
6635
6831
  }
@@ -6851,12 +7047,6 @@ function FocusEventsPlugin({
6851
7047
  const next = e.relatedTarget;
6852
7048
  const container = containerRef.current;
6853
7049
  const stillInside = !!next && (container ? container.contains(next) : root.contains(next));
6854
- console.log("[AO-ColorPicker] FocusEventsPlugin focusout", {
6855
- relatedTargetTag: next ? next.tagName : null,
6856
- relatedTargetClass: next ? next.className : null,
6857
- stillInside,
6858
- willClearSelection: !stillInside
6859
- });
6860
7050
  if (stillInside) return;
6861
7051
  editor.update(() => {
6862
7052
  lexical.$setSelection(null);
@@ -6987,6 +7177,8 @@ var ContentEditorComponent = React9.forwardRef(
6987
7177
  const [charCount, setCharCount] = React9.useState(0);
6988
7178
  const handleCharCount = React9.useCallback((count) => setCharCount(count), []);
6989
7179
  const [refErrors, setRefErrors] = React9.useState([]);
7180
+ const [pageSetup, setPageSetup] = React9.useState(DEFAULT_PAGE_SETUP);
7181
+ const pageCanvas = resolvePageCanvasMetrics(pageSetup);
6990
7182
  const contentEditableDomRef = React9.useRef(null);
6991
7183
  const previousOverLimitRef = React9.useRef(false);
6992
7184
  const focusedRef = React9.useRef(false);
@@ -7028,8 +7220,8 @@ var ContentEditorComponent = React9.forwardRef(
7028
7220
  color: "var(--colorNeutralForeground3, grey)",
7029
7221
  position: "absolute",
7030
7222
  top: props.level !== "none" /* None */ ? "17px" : "27px",
7031
- left: 20,
7032
- right: 20,
7223
+ left: pageCanvas.paddingPx,
7224
+ right: pageCanvas.paddingPx,
7033
7225
  fontSize: "14px",
7034
7226
  pointerEvents: "none",
7035
7227
  userSelect: "none"
@@ -7040,8 +7232,6 @@ var ContentEditorComponent = React9.forwardRef(
7040
7232
  outline: "none",
7041
7233
  overflow: "auto",
7042
7234
  marginTop: "0px",
7043
- paddingLeft: "20px",
7044
- paddingRight: "20px",
7045
7235
  position: "relative",
7046
7236
  background: "var(--colorNeutralBackground1, #ffffff)",
7047
7237
  justifyContent: "center",
@@ -7141,7 +7331,9 @@ var ContentEditorComponent = React9.forwardRef(
7141
7331
  ToolBarPlugins,
7142
7332
  {
7143
7333
  level: props.level ?? "basic" /* Basic */,
7144
- readOnly: props.readOnly
7334
+ readOnly: props.readOnly,
7335
+ pageSetup,
7336
+ onPageSetupChange: setPageSetup
7145
7337
  }
7146
7338
  )
7147
7339
  }
@@ -7155,7 +7347,8 @@ var ContentEditorComponent = React9.forwardRef(
7155
7347
  padding: "15px 0px",
7156
7348
  overflowY: "scroll",
7157
7349
  overflowX: "auto",
7158
- minWidth: 0
7350
+ minWidth: 0,
7351
+ background: pageCanvas.widthPx !== void 0 ? "#eef0f2" : void 0
7159
7352
  },
7160
7353
  onClickCapture: handleReadOnlyClickCapture,
7161
7354
  children: [
@@ -7174,7 +7367,15 @@ var ContentEditorComponent = React9.forwardRef(
7174
7367
  {
7175
7368
  ref: contentEditableDomRef,
7176
7369
  className: react.css(EditorStyles.contentEditor),
7177
- style: { paddingTop: props.level !== "none" /* None */ ? 0 : 10 },
7370
+ style: {
7371
+ paddingTop: props.level !== "none" /* None */ ? 0 : 10,
7372
+ paddingLeft: pageCanvas.paddingPx,
7373
+ paddingRight: pageCanvas.paddingPx,
7374
+ maxWidth: pageCanvas.widthPx,
7375
+ marginLeft: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
7376
+ marginRight: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
7377
+ boxShadow: pageCanvas.widthPx !== void 0 ? "0 0 0 1px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.08)" : void 0
7378
+ },
7178
7379
  spellCheck: !resolvedSpellCheck,
7179
7380
  autoCorrect: resolvedSpellCheck ? "off" : void 0,
7180
7381
  autoCapitalize: resolvedSpellCheck ? "off" : void 0