@tarviks/lexical-rich-editor 1.1.0 → 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.mjs CHANGED
@@ -6,9 +6,9 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
6
6
  import { useLexicalEditable } from '@lexical/react/useLexicalEditable';
7
7
  import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
8
8
  import { createCommand, DecoratorNode, COMMAND_PRIORITY_HIGH, TextNode, ElementNode, $setSelection, $getSelection, $isRangeSelection, SELECTION_CHANGE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, $findMatchingParent, COMMAND_PRIORITY_LOW, KEY_DOWN_COMMAND, $insertNodes, $isRootOrShadowRoot, $createParagraphNode, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, DRAGSTART_COMMAND, DRAGOVER_COMMAND, DROP_COMMAND, $getNodeByKey, $getRoot, $createTextNode, $isTextNode, $createRangeSelection, $isElementNode, $isNodeSelection, CLICK_COMMAND, $applyNodeReplacement, FORMAT_TEXT_COMMAND, $isDecoratorNode, $getNearestNodeFromDOMNode, COMMAND_PRIORITY_CRITICAL, REDO_COMMAND, UNDO_COMMAND, getDOMSelection, KEY_ESCAPE_COMMAND, isHTMLElement, getDOMSelectionFromTarget, SKIP_SELECTION_FOCUS_TAG, createEditor, KEY_ENTER_COMMAND } from 'lexical';
9
- import { mergeStyleSets, Stack, css, useTheme, Callout, TextField, DefaultButton } from '@fluentui/react';
10
- import { makeStyles, FluentProvider, webLightTheme, Menu, MenuTrigger, MenuPopover, MenuList, MenuGroup, MenuGroupHeader, MenuItem, MenuDivider, Dropdown, Option, ToolbarDivider, Button, Popover, PopoverTrigger, PopoverSurface, Field, Input } from '@fluentui/react-components';
11
- import { ErrorCircleRegular, ChevronDown12Regular, ArrowUpRegular, RowTripleRegular, ArrowDownRegular, ArrowLeftRegular, ColumnTripleRegular, ArrowRightRegular, DeleteRegular, TextCaseUppercaseFilled, TextCaseLowercaseFilled, TextCaseTitleFilled, TextStrikethroughFilled, TextSubscriptFilled, TextSuperscriptFilled, HighlightAccentFilled, TextBulletListLtrFilled, TextNumberListLtrFilled, DocumentPageBreakRegular, CommentQuoteRegular, TextUnderlineFilled, TextItalicFilled, TextBold24Regular, TextAlignLeftFilled, TextAlignCenterFilled, TextAlignRightFilled, TextAlignJustifyFilled, VideoClipRegular, ImageEditRegular, AttachFilled, ImageAddRegular, TableAddRegular, LinkAddRegular, TextColorRegular, PaintBucket16Filled, CodeFilled, LinkFilled } from '@fluentui/react-icons';
9
+ import { mergeStyleSets, Stack, css, useTheme, Callout, TextField } from '@fluentui/react';
10
+ import { makeStyles, FluentProvider, webLightTheme, Menu, MenuTrigger, MenuPopover, MenuList, MenuGroup, MenuGroupHeader, MenuItem, MenuDivider, Dropdown, Option, ToolbarDivider, Button, MenuItemRadio, Popover, PopoverTrigger, PopoverSurface, Field, Input } from '@fluentui/react-components';
11
+ import { ErrorCircleRegular, ChevronDown12Regular, ArrowUpRegular, RowTripleRegular, ArrowDownRegular, ArrowLeftRegular, ColumnTripleRegular, ArrowRightRegular, DeleteRegular, TextCaseUppercaseFilled, TextCaseLowercaseFilled, TextCaseTitleFilled, TextStrikethroughFilled, TextSubscriptFilled, TextSuperscriptFilled, HighlightAccentFilled, TextBulletListLtrFilled, TextNumberListLtrFilled, DocumentPageBreakRegular, CommentQuoteRegular, TextUnderlineFilled, TextItalicFilled, TextBold24Regular, DocumentRegular, TextAlignLeftFilled, TextAlignCenterFilled, TextAlignRightFilled, TextAlignJustifyFilled, VideoClipRegular, ImageEditRegular, AttachFilled, ImageAddRegular, TableAddRegular, LinkAddRegular, TextColorRegular, PaintBucket16Filled, CodeFilled, LinkFilled, Dismiss16Regular } from '@fluentui/react-icons';
12
12
  import { CodeHighlightNode, CodeNode, $isCodeHighlightNode } from '@lexical/code';
13
13
  import { LinkNode, AutoLinkNode, $isLinkNode, $isAutoLinkNode, TOGGLE_LINK_COMMAND, $createLinkNode } from '@lexical/link';
14
14
  import { ListNode, ListItemNode, $isListNode, REMOVE_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND } from '@lexical/list';
@@ -1228,6 +1228,46 @@ var ContentEditorLevel = /* @__PURE__ */ ((ContentEditorLevel2) => {
1228
1228
  ContentEditorLevel2["Pro"] = "pro";
1229
1229
  return ContentEditorLevel2;
1230
1230
  })(ContentEditorLevel || {});
1231
+
1232
+ // src/Types/PageSetup.ts
1233
+ var DEFAULT_PAGE_SETUP = {
1234
+ size: "pageless",
1235
+ orientation: "portrait",
1236
+ margin: "normal"
1237
+ };
1238
+ var PAGE_SIZE_OPTIONS = [
1239
+ { key: "a4", label: 'A4 (8.27" x 11.69")', widthIn: 8.27, heightIn: 11.69 },
1240
+ { key: "letter", label: 'Letter (8.5" x 11")', widthIn: 8.5, heightIn: 11 },
1241
+ { key: "legal", label: 'Legal (8.5" x 14")', widthIn: 8.5, heightIn: 14 },
1242
+ { key: "tabloid", label: 'Tabloid (11" x 17")', widthIn: 11, heightIn: 17 },
1243
+ { key: "a3", label: 'A3 (11.69" x 16.54")', widthIn: 11.69, heightIn: 16.54 },
1244
+ { key: "a5", label: 'A5 (5.83" x 8.27")', widthIn: 5.83, heightIn: 8.27 },
1245
+ { key: "b4", label: 'B4 (9.84" x 13.90")', widthIn: 9.84, heightIn: 13.9 },
1246
+ { key: "b5", label: 'B5 (6.93" x 9.84")', widthIn: 6.93, heightIn: 9.84 },
1247
+ { key: "statement", label: 'Statement (5.5" x 8.5")', widthIn: 5.5, heightIn: 8.5 },
1248
+ { key: "executive", label: 'Executive (7.25" x 10.5")', widthIn: 7.25, heightIn: 10.5 },
1249
+ { key: "folio", label: 'Folio (8.5" x 13")', widthIn: 8.5, heightIn: 13 }
1250
+ ];
1251
+ var MARGIN_OPTIONS = [
1252
+ { key: "narrow", label: 'Narrow (0.25")', valueIn: 0.25 },
1253
+ { key: "normal", label: 'Normal (0.4")', valueIn: 0.4 },
1254
+ { key: "moderate", label: 'Moderate (0.75")', valueIn: 0.75 },
1255
+ { key: "wide", label: 'Wide (1")', valueIn: 1 }
1256
+ ];
1257
+ var CSS_PX_PER_INCH = 96;
1258
+ function resolvePageCanvasMetrics(value) {
1259
+ const margin = MARGIN_OPTIONS.find((o) => o.key === value.margin) ?? MARGIN_OPTIONS[1];
1260
+ if (value.size === "pageless") {
1261
+ return { widthPx: void 0, paddingPx: 20 };
1262
+ }
1263
+ const size = PAGE_SIZE_OPTIONS.find((o) => o.key === value.size);
1264
+ if (!size) return { widthPx: void 0, paddingPx: 20 };
1265
+ const widthIn = value.orientation === "landscape" ? size.heightIn : size.widthIn;
1266
+ return {
1267
+ widthPx: Math.round(widthIn * CSS_PX_PER_INCH),
1268
+ paddingPx: Math.round(margin.valueIn * CSS_PX_PER_INCH)
1269
+ };
1270
+ }
1231
1271
  var AutocompleteNode = class _AutocompleteNode extends TextNode {
1232
1272
  static getType() {
1233
1273
  return "autocomplete";
@@ -2466,6 +2506,236 @@ function CharacterStylesPopupPlugin(props) {
2466
2506
  const [editor] = useLexicalComposerContext();
2467
2507
  return useCharacterStylesPopup(editor, props);
2468
2508
  }
2509
+
2510
+ // src/Utils/Sanitize.ts
2511
+ var DROP_ENTIRELY = /* @__PURE__ */ new Set([
2512
+ "script",
2513
+ "noscript",
2514
+ "style",
2515
+ "object",
2516
+ "embed",
2517
+ "form",
2518
+ "input",
2519
+ "button",
2520
+ "select",
2521
+ "textarea",
2522
+ "meta",
2523
+ "link",
2524
+ "base"
2525
+ ]);
2526
+ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
2527
+ // Block
2528
+ "p",
2529
+ "h1",
2530
+ "h2",
2531
+ "h3",
2532
+ "h4",
2533
+ "h5",
2534
+ "h6",
2535
+ "ul",
2536
+ "ol",
2537
+ "li",
2538
+ "blockquote",
2539
+ "pre",
2540
+ "div",
2541
+ "table",
2542
+ "thead",
2543
+ "tbody",
2544
+ "tfoot",
2545
+ "tr",
2546
+ "td",
2547
+ "th",
2548
+ // Inline
2549
+ "span",
2550
+ "a",
2551
+ "strong",
2552
+ "b",
2553
+ "em",
2554
+ "i",
2555
+ "u",
2556
+ "s",
2557
+ "del",
2558
+ "strike",
2559
+ "sub",
2560
+ "sup",
2561
+ "mark",
2562
+ "code",
2563
+ "br",
2564
+ "hr",
2565
+ // Media / embeds
2566
+ "img",
2567
+ "iframe"
2568
+ ]);
2569
+ var ALLOWED_ATTRS = /* @__PURE__ */ new Set([
2570
+ // Presentation
2571
+ "class",
2572
+ "style",
2573
+ "dir",
2574
+ "lang",
2575
+ // Anchors
2576
+ "href",
2577
+ "target",
2578
+ "rel",
2579
+ // Images
2580
+ "src",
2581
+ "alt",
2582
+ "width",
2583
+ "height",
2584
+ // Tables
2585
+ "colspan",
2586
+ "rowspan",
2587
+ // Lists — <ol type="a"> and <ol start="2">
2588
+ "start",
2589
+ "type",
2590
+ // Lexical-internal data markers
2591
+ "data-lex-block",
2592
+ "data-kind",
2593
+ "data-lexical-decorator",
2594
+ // Misc
2595
+ "title",
2596
+ "allowfullscreen"
2597
+ ]);
2598
+ var DANGEROUS_URL = /^\s*(javascript|data\s*:|vbscript)/i;
2599
+ function sanitizeElement(el) {
2600
+ for (const child of Array.from(el.children)) {
2601
+ sanitizeElement(child);
2602
+ }
2603
+ const tag = el.tagName.toLowerCase();
2604
+ if (DROP_ENTIRELY.has(tag)) {
2605
+ el.parentNode?.removeChild(el);
2606
+ return;
2607
+ }
2608
+ if (tag === "iframe") {
2609
+ const src = el.getAttribute("src") ?? "";
2610
+ const isYouTube = /^\s*https:\/\/(www\.)?(youtube\.com|youtube-nocookie\.com)\/embed\/[^?&/]+/i.test(src);
2611
+ if (!isYouTube) {
2612
+ el.parentNode?.removeChild(el);
2613
+ return;
2614
+ }
2615
+ }
2616
+ if (!ALLOWED_TAGS.has(tag)) {
2617
+ while (el.firstChild) {
2618
+ el.parentNode?.insertBefore(el.firstChild, el);
2619
+ }
2620
+ el.parentNode?.removeChild(el);
2621
+ return;
2622
+ }
2623
+ for (const { name, value } of Array.from(el.attributes)) {
2624
+ const lname = name.toLowerCase();
2625
+ if (!ALLOWED_ATTRS.has(lname)) {
2626
+ el.removeAttribute(name);
2627
+ continue;
2628
+ }
2629
+ if (lname === "href" || lname === "src") {
2630
+ if (DANGEROUS_URL.test(value)) {
2631
+ el.removeAttribute(name);
2632
+ }
2633
+ }
2634
+ if (lname === "style") {
2635
+ const safe = value.replace(/expression\s*\(/gi, "(").replace(/url\s*\(\s*['"]?\s*javascript:/gi, "url(");
2636
+ el.setAttribute("style", safe);
2637
+ }
2638
+ }
2639
+ if (tag === "a" && (el.getAttribute("target") || "").toLowerCase() === "_blank") {
2640
+ const rel = (el.getAttribute("rel") || "").trim();
2641
+ const tokens = new Set(
2642
+ rel.split(/\s+/).filter(Boolean).map((t) => t.toLowerCase())
2643
+ );
2644
+ tokens.add("noopener");
2645
+ tokens.add("noreferrer");
2646
+ el.setAttribute("rel", Array.from(tokens).join(" "));
2647
+ }
2648
+ }
2649
+ function sanitizeHtml(html) {
2650
+ if (!html || typeof html !== "string") return "";
2651
+ const doc = new DOMParser().parseFromString(html, "text/html");
2652
+ for (const child of Array.from(doc.body.children)) {
2653
+ sanitizeElement(child);
2654
+ }
2655
+ return doc.body.innerHTML;
2656
+ }
2657
+ function postProcessOutput(html) {
2658
+ if (!html) return html;
2659
+ const doc = new DOMParser().parseFromString(html, "text/html");
2660
+ for (const inner of Array.from(doc.querySelectorAll("b > strong"))) {
2661
+ const outer = inner.parentElement;
2662
+ _mergeAttributes(inner, outer);
2663
+ while (inner.firstChild) outer.insertBefore(inner.firstChild, inner);
2664
+ outer.removeChild(inner);
2665
+ }
2666
+ for (const inner of Array.from(doc.querySelectorAll("i > em"))) {
2667
+ const outer = inner.parentElement;
2668
+ _mergeAttributes(inner, outer);
2669
+ while (inner.firstChild) outer.insertBefore(inner.firstChild, inner);
2670
+ outer.removeChild(inner);
2671
+ }
2672
+ for (const p of Array.from(doc.querySelectorAll("p"))) {
2673
+ const style = p.style;
2674
+ const hasMargin = !!(style.margin || style.marginTop || style.marginRight || style.marginBottom || style.marginLeft);
2675
+ if (!hasMargin) style.margin = "0";
2676
+ if (!style.lineHeight) style.lineHeight = "1.25";
2677
+ }
2678
+ return doc.body.innerHTML;
2679
+ }
2680
+ function _mergeAttributes(src, dst) {
2681
+ for (const { name, value } of Array.from(src.attributes)) {
2682
+ if (name === "style") {
2683
+ const prev = dst.getAttribute("style") || "";
2684
+ dst.setAttribute("style", prev ? `${prev}; ${value}` : value);
2685
+ } else if (name === "class") {
2686
+ const prev = dst.getAttribute("class") || "";
2687
+ dst.setAttribute("class", prev ? `${prev} ${value}` : value);
2688
+ }
2689
+ }
2690
+ }
2691
+ var BLOCK_TAGS = /* @__PURE__ */ new Set([
2692
+ "p",
2693
+ "h1",
2694
+ "h2",
2695
+ "h3",
2696
+ "h4",
2697
+ "h5",
2698
+ "h6",
2699
+ "div",
2700
+ "ul",
2701
+ "ol",
2702
+ "li",
2703
+ "table",
2704
+ "blockquote",
2705
+ "pre",
2706
+ "hr"
2707
+ ]);
2708
+ function normalizeToBlockHtml(html) {
2709
+ if (!html) return "";
2710
+ const doc = new DOMParser().parseFromString(html, "text/html");
2711
+ const body = doc.body;
2712
+ const childNodes = Array.from(body.childNodes);
2713
+ const needsWrap = childNodes.some((node) => {
2714
+ if (node.nodeType === Node.TEXT_NODE) return !!node.nodeValue?.trim();
2715
+ if (node.nodeType === Node.ELEMENT_NODE)
2716
+ return !BLOCK_TAGS.has(node.tagName.toLowerCase());
2717
+ return false;
2718
+ });
2719
+ if (!needsWrap) return html;
2720
+ while (body.firstChild) body.removeChild(body.firstChild);
2721
+ let pendingP = null;
2722
+ for (const node of childNodes) {
2723
+ const isBlock = node.nodeType === Node.ELEMENT_NODE && BLOCK_TAGS.has(node.tagName.toLowerCase());
2724
+ const isWhitespace = node.nodeType === Node.TEXT_NODE && !node.nodeValue?.trim();
2725
+ if (isBlock) {
2726
+ if (pendingP) {
2727
+ body.appendChild(pendingP);
2728
+ pendingP = null;
2729
+ }
2730
+ body.appendChild(node);
2731
+ } else if (!isWhitespace) {
2732
+ if (!pendingP) pendingP = doc.createElement("p");
2733
+ pendingP.appendChild(node);
2734
+ }
2735
+ }
2736
+ if (pendingP) body.appendChild(pendingP);
2737
+ return body.innerHTML;
2738
+ }
2469
2739
  var CustomOnChangePlugin = ({ value, onChange }) => {
2470
2740
  const [editor] = useLexicalComposerContext();
2471
2741
  const initializedRef = useRef(false);
@@ -2486,7 +2756,7 @@ var CustomOnChangePlugin = ({ value, onChange }) => {
2486
2756
  }, [editor, value]);
2487
2757
  const handleChange = useCallback((editorState) => {
2488
2758
  editorState.read(() => {
2489
- onChangeRef.current($generateHtmlFromNodes(editor));
2759
+ onChangeRef.current(postProcessOutput($generateHtmlFromNodes(editor)));
2490
2760
  });
2491
2761
  }, [editor]);
2492
2762
  return /* @__PURE__ */ jsx(
@@ -3498,279 +3768,81 @@ var $onDrop2 = (event, editor) => {
3498
3768
  const node = $getImageNodeInSelection2();
3499
3769
  if (!node) return false;
3500
3770
  const data = getDragImageData2(event);
3501
- if (!data) return false;
3502
- event.preventDefault();
3503
- if (canDropImage2(event)) {
3504
- const range = getDragSelection2(event);
3505
- node.remove();
3506
- const rangeSelection = $createRangeSelection();
3507
- if (range !== null && range !== void 0) {
3508
- rangeSelection.applyDOMRange(range);
3509
- }
3510
- $setSelection(rangeSelection);
3511
- editor.dispatchCommand(INSERT_INLINE_IMAGE_COMMAND, data);
3512
- }
3513
- return true;
3514
- };
3515
- var $getImageNodeInSelection2 = () => {
3516
- const selection = $getSelection();
3517
- if (!$isNodeSelection(selection)) return null;
3518
- const nodes = selection.getNodes();
3519
- const node = nodes[0];
3520
- return $isInlineImageNode(node) ? node : null;
3521
- };
3522
- var getDragImageData2 = (event) => {
3523
- const dragData = event.dataTransfer?.getData("application/x-lexical-drag");
3524
- if (!dragData) return null;
3525
- const { type, data } = JSON.parse(dragData);
3526
- if (type !== "image") return null;
3527
- return data;
3528
- };
3529
- var canDropImage2 = (event) => {
3530
- const target = event.target;
3531
- return !!(isHTMLElement(target) && !target.closest("code, span.editor-image") && isHTMLElement(target.parentElement) && target.parentElement.closest("div.ContentEditable__root"));
3532
- };
3533
- var getDragSelection2 = (event) => {
3534
- let range;
3535
- const domSelection = getDOMSelectionFromTarget(event.target);
3536
- if (document.caretRangeFromPoint) {
3537
- range = document.caretRangeFromPoint(event.clientX, event.clientY);
3538
- } else if (event.rangeParent && domSelection !== null) {
3539
- domSelection.collapse(event.rangeParent, event.rangeOffset || 0);
3540
- range = domSelection.getRangeAt(0);
3541
- } else {
3542
- throw Error("Cannot get the selection when dragging");
3543
- }
3544
- return range;
3545
- };
3546
- var INSERT_PAGE_BREAK = createCommand();
3547
- function PageBreakPlugin() {
3548
- const [editor] = useLexicalComposerContext();
3549
- useEffect(() => {
3550
- if (!editor.hasNodes([PageBreakNode])) {
3551
- throw new Error(
3552
- "PageBreakPlugin: PageBreakNode is not registered on editor"
3553
- );
3554
- }
3555
- return mergeRegister(
3556
- editor.registerCommand(
3557
- INSERT_PAGE_BREAK,
3558
- () => {
3559
- const selection = $getSelection();
3560
- if (!$isRangeSelection(selection)) {
3561
- return false;
3562
- }
3563
- const focusNode = selection.focus.getNode();
3564
- if (focusNode !== null) {
3565
- const pgBreak = $createPageBreakNode();
3566
- $insertNodeToNearestRoot(pgBreak);
3567
- }
3568
- return true;
3569
- },
3570
- COMMAND_PRIORITY_EDITOR
3571
- )
3572
- );
3573
- }, [editor]);
3574
- return null;
3575
- }
3576
-
3577
- // src/Utils/Sanitize.ts
3578
- var DROP_ENTIRELY = /* @__PURE__ */ new Set([
3579
- "script",
3580
- "noscript",
3581
- "style",
3582
- "object",
3583
- "embed",
3584
- "form",
3585
- "input",
3586
- "button",
3587
- "select",
3588
- "textarea",
3589
- "meta",
3590
- "link",
3591
- "base"
3592
- ]);
3593
- var ALLOWED_TAGS = /* @__PURE__ */ new Set([
3594
- // Block
3595
- "p",
3596
- "h1",
3597
- "h2",
3598
- "h3",
3599
- "h4",
3600
- "h5",
3601
- "h6",
3602
- "ul",
3603
- "ol",
3604
- "li",
3605
- "blockquote",
3606
- "pre",
3607
- "div",
3608
- "table",
3609
- "thead",
3610
- "tbody",
3611
- "tfoot",
3612
- "tr",
3613
- "td",
3614
- "th",
3615
- // Inline
3616
- "span",
3617
- "a",
3618
- "strong",
3619
- "b",
3620
- "em",
3621
- "i",
3622
- "u",
3623
- "s",
3624
- "del",
3625
- "strike",
3626
- "sub",
3627
- "sup",
3628
- "mark",
3629
- "code",
3630
- "br",
3631
- "hr",
3632
- // Media / embeds
3633
- "img",
3634
- "iframe"
3635
- ]);
3636
- var ALLOWED_ATTRS = /* @__PURE__ */ new Set([
3637
- // Presentation
3638
- "class",
3639
- "style",
3640
- "dir",
3641
- "lang",
3642
- // Anchors
3643
- "href",
3644
- "target",
3645
- "rel",
3646
- // Images
3647
- "src",
3648
- "alt",
3649
- "width",
3650
- "height",
3651
- // Tables
3652
- "colspan",
3653
- "rowspan",
3654
- // Lists — <ol type="a"> and <ol start="2">
3655
- "start",
3656
- "type",
3657
- // Lexical-internal data markers
3658
- "data-lex-block",
3659
- "data-kind",
3660
- "data-lexical-decorator",
3661
- // Misc
3662
- "title",
3663
- "allowfullscreen"
3664
- ]);
3665
- var DANGEROUS_URL = /^\s*(javascript|data\s*:|vbscript)/i;
3666
- function sanitizeElement(el) {
3667
- for (const child of Array.from(el.children)) {
3668
- sanitizeElement(child);
3669
- }
3670
- const tag = el.tagName.toLowerCase();
3671
- if (DROP_ENTIRELY.has(tag)) {
3672
- el.parentNode?.removeChild(el);
3673
- return;
3674
- }
3675
- if (tag === "iframe") {
3676
- const src = el.getAttribute("src") ?? "";
3677
- const isYouTube = /^\s*https:\/\/(www\.)?(youtube\.com|youtube-nocookie\.com)\/embed\/[^?&/]+/i.test(src);
3678
- if (!isYouTube) {
3679
- el.parentNode?.removeChild(el);
3680
- return;
3681
- }
3682
- }
3683
- if (!ALLOWED_TAGS.has(tag)) {
3684
- while (el.firstChild) {
3685
- el.parentNode?.insertBefore(el.firstChild, el);
3686
- }
3687
- el.parentNode?.removeChild(el);
3688
- return;
3689
- }
3690
- for (const { name, value } of Array.from(el.attributes)) {
3691
- const lname = name.toLowerCase();
3692
- if (!ALLOWED_ATTRS.has(lname)) {
3693
- el.removeAttribute(name);
3694
- continue;
3695
- }
3696
- if (lname === "href" || lname === "src") {
3697
- if (DANGEROUS_URL.test(value)) {
3698
- el.removeAttribute(name);
3699
- }
3700
- }
3701
- if (lname === "style") {
3702
- const safe = value.replace(/expression\s*\(/gi, "(").replace(/url\s*\(\s*['"]?\s*javascript:/gi, "url(");
3703
- el.setAttribute("style", safe);
3771
+ if (!data) return false;
3772
+ event.preventDefault();
3773
+ if (canDropImage2(event)) {
3774
+ const range = getDragSelection2(event);
3775
+ node.remove();
3776
+ const rangeSelection = $createRangeSelection();
3777
+ if (range !== null && range !== void 0) {
3778
+ rangeSelection.applyDOMRange(range);
3704
3779
  }
3780
+ $setSelection(rangeSelection);
3781
+ editor.dispatchCommand(INSERT_INLINE_IMAGE_COMMAND, data);
3705
3782
  }
3706
- if (tag === "a" && (el.getAttribute("target") || "").toLowerCase() === "_blank") {
3707
- const rel = (el.getAttribute("rel") || "").trim();
3708
- const tokens = new Set(
3709
- rel.split(/\s+/).filter(Boolean).map((t) => t.toLowerCase())
3710
- );
3711
- tokens.add("noopener");
3712
- tokens.add("noreferrer");
3713
- el.setAttribute("rel", Array.from(tokens).join(" "));
3714
- }
3715
- }
3716
- function sanitizeHtml(html) {
3717
- if (!html || typeof html !== "string") return "";
3718
- const doc = new DOMParser().parseFromString(html, "text/html");
3719
- for (const child of Array.from(doc.body.children)) {
3720
- sanitizeElement(child);
3783
+ return true;
3784
+ };
3785
+ var $getImageNodeInSelection2 = () => {
3786
+ const selection = $getSelection();
3787
+ if (!$isNodeSelection(selection)) return null;
3788
+ const nodes = selection.getNodes();
3789
+ const node = nodes[0];
3790
+ return $isInlineImageNode(node) ? node : null;
3791
+ };
3792
+ var getDragImageData2 = (event) => {
3793
+ const dragData = event.dataTransfer?.getData("application/x-lexical-drag");
3794
+ if (!dragData) return null;
3795
+ const { type, data } = JSON.parse(dragData);
3796
+ if (type !== "image") return null;
3797
+ return data;
3798
+ };
3799
+ var canDropImage2 = (event) => {
3800
+ const target = event.target;
3801
+ return !!(isHTMLElement(target) && !target.closest("code, span.editor-image") && isHTMLElement(target.parentElement) && target.parentElement.closest("div.ContentEditable__root"));
3802
+ };
3803
+ var getDragSelection2 = (event) => {
3804
+ let range;
3805
+ const domSelection = getDOMSelectionFromTarget(event.target);
3806
+ if (document.caretRangeFromPoint) {
3807
+ range = document.caretRangeFromPoint(event.clientX, event.clientY);
3808
+ } else if (event.rangeParent && domSelection !== null) {
3809
+ domSelection.collapse(event.rangeParent, event.rangeOffset || 0);
3810
+ range = domSelection.getRangeAt(0);
3811
+ } else {
3812
+ throw Error("Cannot get the selection when dragging");
3721
3813
  }
3722
- return doc.body.innerHTML;
3723
- }
3724
- var BLOCK_TAGS = /* @__PURE__ */ new Set([
3725
- "p",
3726
- "h1",
3727
- "h2",
3728
- "h3",
3729
- "h4",
3730
- "h5",
3731
- "h6",
3732
- "div",
3733
- "ul",
3734
- "ol",
3735
- "li",
3736
- "table",
3737
- "blockquote",
3738
- "pre",
3739
- "hr"
3740
- ]);
3741
- function normalizeToBlockHtml(html) {
3742
- if (!html) return "";
3743
- const doc = new DOMParser().parseFromString(html, "text/html");
3744
- const body = doc.body;
3745
- const childNodes = Array.from(body.childNodes);
3746
- const needsWrap = childNodes.some((node) => {
3747
- if (node.nodeType === Node.TEXT_NODE) return !!node.nodeValue?.trim();
3748
- if (node.nodeType === Node.ELEMENT_NODE)
3749
- return !BLOCK_TAGS.has(node.tagName.toLowerCase());
3750
- return false;
3751
- });
3752
- if (!needsWrap) return html;
3753
- while (body.firstChild) body.removeChild(body.firstChild);
3754
- let pendingP = null;
3755
- for (const node of childNodes) {
3756
- const isBlock = node.nodeType === Node.ELEMENT_NODE && BLOCK_TAGS.has(node.tagName.toLowerCase());
3757
- const isWhitespace = node.nodeType === Node.TEXT_NODE && !node.nodeValue?.trim();
3758
- if (isBlock) {
3759
- if (pendingP) {
3760
- body.appendChild(pendingP);
3761
- pendingP = null;
3762
- }
3763
- body.appendChild(node);
3764
- } else if (!isWhitespace) {
3765
- if (!pendingP) pendingP = doc.createElement("p");
3766
- pendingP.appendChild(node);
3814
+ return range;
3815
+ };
3816
+ var INSERT_PAGE_BREAK = createCommand();
3817
+ function PageBreakPlugin() {
3818
+ const [editor] = useLexicalComposerContext();
3819
+ useEffect(() => {
3820
+ if (!editor.hasNodes([PageBreakNode])) {
3821
+ throw new Error(
3822
+ "PageBreakPlugin: PageBreakNode is not registered on editor"
3823
+ );
3767
3824
  }
3768
- }
3769
- if (pendingP) body.appendChild(pendingP);
3770
- return body.innerHTML;
3825
+ return mergeRegister(
3826
+ editor.registerCommand(
3827
+ INSERT_PAGE_BREAK,
3828
+ () => {
3829
+ const selection = $getSelection();
3830
+ if (!$isRangeSelection(selection)) {
3831
+ return false;
3832
+ }
3833
+ const focusNode = selection.focus.getNode();
3834
+ if (focusNode !== null) {
3835
+ const pgBreak = $createPageBreakNode();
3836
+ $insertNodeToNearestRoot(pgBreak);
3837
+ }
3838
+ return true;
3839
+ },
3840
+ COMMAND_PRIORITY_EDITOR
3841
+ )
3842
+ );
3843
+ }, [editor]);
3844
+ return null;
3771
3845
  }
3772
-
3773
- // src/Utils/Helper.ts
3774
3846
  function findBlockByKind(kind) {
3775
3847
  const root = $getRoot();
3776
3848
  for (const child of root.getChildren()) {
@@ -3842,7 +3914,7 @@ function RefApiPlugin({
3842
3914
  editor.getEditorState().read(() => {
3843
3915
  html = $generateHtmlFromNodes(editor, null);
3844
3916
  });
3845
- return html;
3917
+ return postProcessOutput(html);
3846
3918
  },
3847
3919
  clear: () => {
3848
3920
  editor.update(() => {
@@ -4991,7 +5063,8 @@ function getToolbarGroupsByLevel(level) {
4991
5063
  ["FontFamily", "|"],
4992
5064
  ["FontSize", "|"],
4993
5065
  ["Decorators", "|"],
4994
- ["Align"]
5066
+ ["Align", "|"],
5067
+ ["PageSetup"]
4995
5068
  ];
4996
5069
  case "pro" /* Pro */:
4997
5070
  default:
@@ -5006,7 +5079,8 @@ function getToolbarGroupsByLevel(level) {
5006
5079
  ["FontFamily", "|"],
5007
5080
  ["FontSize", "|"],
5008
5081
  ["Decorators", "|"],
5009
- ["Align"]
5082
+ ["Align", "|"],
5083
+ ["PageSetup"]
5010
5084
  ];
5011
5085
  }
5012
5086
  }
@@ -5044,6 +5118,7 @@ var normalizeHex = (v) => {
5044
5118
  if (hex.length === 4 || hex.length === 7) return hex.toLowerCase();
5045
5119
  return "#000000";
5046
5120
  };
5121
+ var isCompleteHex = (v) => /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test((v ?? "").trim());
5047
5122
  var hexToRgb = (hex) => {
5048
5123
  const h = normalizeHex(hex).replace("#", "");
5049
5124
  if (h.length === 3) {
@@ -5126,62 +5201,118 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5126
5201
  };
5127
5202
  }, [open]);
5128
5203
  const appliedHex = React9.useMemo(() => normalizeHex(value || "#000000"), [value]);
5129
- const [hex, setHex] = React9.useState(appliedHex);
5204
+ const [hex, setHexState] = React9.useState(appliedHex);
5205
+ const [hexText, setHexText] = React9.useState(appliedHex);
5130
5206
  const { r, g, b } = React9.useMemo(() => hexToRgb(hex), [hex]);
5131
5207
  const hsv = React9.useMemo(() => rgbToHsv(r, g, b), [r, g, b]);
5132
5208
  const [h, setH] = React9.useState(hsv.h);
5133
5209
  const [s, setS] = React9.useState(hsv.s);
5134
5210
  const [v, setV] = React9.useState(hsv.v);
5135
- const setDraft = React9.useCallback((nextHex) => {
5136
- setHex(nextHex);
5137
- const rgb = hexToRgb(nextHex);
5138
- const next = rgbToHsv(rgb.r, rgb.g, rgb.b);
5139
- setH(next.h);
5140
- setS(next.s);
5141
- setV(next.v);
5142
- }, []);
5143
- const setDraftFromHsv = React9.useCallback((hh, ss, vv) => {
5144
- const rgb = hsvToRgb(hh, ss, vv);
5145
- setHex(rgbToHex(rgb.r, rgb.g, rgb.b));
5146
- setH(hh);
5147
- setS(ss);
5148
- setV(vv);
5149
- }, []);
5211
+ const commit = React9.useCallback(
5212
+ (nextHex) => {
5213
+ const rgb = hexToRgb(nextHex);
5214
+ const next = rgbToHsv(rgb.r, rgb.g, rgb.b);
5215
+ setHexState(nextHex);
5216
+ setHexText(nextHex);
5217
+ setH(next.h);
5218
+ setS(next.s);
5219
+ setV(next.v);
5220
+ onChange(nextHex);
5221
+ },
5222
+ [onChange]
5223
+ );
5224
+ const commitFromHsv = React9.useCallback(
5225
+ (hh, ss, vv) => {
5226
+ const rgb = hsvToRgb(hh, ss, vv);
5227
+ const nextHex = rgbToHex(rgb.r, rgb.g, rgb.b);
5228
+ setHexState(nextHex);
5229
+ setHexText(nextHex);
5230
+ setH(hh);
5231
+ setS(ss);
5232
+ setV(vv);
5233
+ onChange(nextHex);
5234
+ },
5235
+ [onChange]
5236
+ );
5150
5237
  const wasOpenRef = React9.useRef(open);
5151
5238
  React9.useEffect(() => {
5152
5239
  const justOpened = open && !wasOpenRef.current;
5153
5240
  wasOpenRef.current = open;
5154
5241
  if (!justOpened) return;
5155
- setDraft(appliedHex);
5156
- }, [appliedHex, open, setDraft]);
5242
+ setHexState(appliedHex);
5243
+ setHexText(appliedHex);
5244
+ const rgb = hexToRgb(appliedHex);
5245
+ const next = rgbToHsv(rgb.r, rgb.g, rgb.b);
5246
+ setH(next.h);
5247
+ setS(next.s);
5248
+ setV(next.v);
5249
+ }, [appliedHex, open]);
5157
5250
  const svRef = React9.useRef(null);
5158
- const handleSVClick = React9.useCallback(
5251
+ const svPointFromEvent = React9.useCallback((clientX, clientY) => {
5252
+ if (!svRef.current) return null;
5253
+ const rect = svRef.current.getBoundingClientRect();
5254
+ const x = clamp3(clientX - rect.left, 0, rect.width);
5255
+ const y = clamp3(clientY - rect.top, 0, rect.height);
5256
+ const ss = rect.width === 0 ? 0 : x / rect.width;
5257
+ const vv = rect.height === 0 ? 0 : 1 - y / rect.height;
5258
+ return { ss, vv };
5259
+ }, []);
5260
+ const hRef = React9.useRef(h);
5261
+ hRef.current = h;
5262
+ const handleSVPointerDown = React9.useCallback(
5263
+ (e) => {
5264
+ e.currentTarget.setPointerCapture(e.pointerId);
5265
+ const pt = svPointFromEvent(e.clientX, e.clientY);
5266
+ if (pt) commitFromHsv(hRef.current, pt.ss, pt.vv);
5267
+ },
5268
+ [svPointFromEvent, commitFromHsv]
5269
+ );
5270
+ const handleSVPointerMove = React9.useCallback(
5159
5271
  (e) => {
5160
- if (!svRef.current) return;
5161
- const rect = svRef.current.getBoundingClientRect();
5162
- const x = clamp3(e.clientX - rect.left, 0, rect.width);
5163
- const y = clamp3(e.clientY - rect.top, 0, rect.height);
5164
- const ss = rect.width === 0 ? 0 : x / rect.width;
5165
- const vv = rect.height === 0 ? 0 : 1 - y / rect.height;
5166
- setDraftFromHsv(h, ss, vv);
5272
+ if (e.buttons !== 1) return;
5273
+ const pt = svPointFromEvent(e.clientX, e.clientY);
5274
+ if (pt) commitFromHsv(hRef.current, pt.ss, pt.vv);
5167
5275
  },
5168
- [h, setDraftFromHsv]
5276
+ [svPointFromEvent, commitFromHsv]
5169
5277
  );
5170
5278
  const hueRef = React9.useRef(null);
5171
- const handleHueClick = React9.useCallback(
5279
+ const sRef = React9.useRef(s);
5280
+ sRef.current = s;
5281
+ const vRef = React9.useRef(v);
5282
+ vRef.current = v;
5283
+ const huePointFromEvent = React9.useCallback((clientX) => {
5284
+ if (!hueRef.current) return null;
5285
+ const rect = hueRef.current.getBoundingClientRect();
5286
+ const x = clamp3(clientX - rect.left, 0, rect.width);
5287
+ return rect.width === 0 ? 0 : x / rect.width * 360;
5288
+ }, []);
5289
+ const handleHuePointerDown = React9.useCallback(
5290
+ (e) => {
5291
+ e.currentTarget.setPointerCapture(e.pointerId);
5292
+ const hh = huePointFromEvent(e.clientX);
5293
+ if (hh != null) commitFromHsv(hh, sRef.current, vRef.current);
5294
+ },
5295
+ [huePointFromEvent, commitFromHsv]
5296
+ );
5297
+ const handleHuePointerMove = React9.useCallback(
5172
5298
  (e) => {
5173
- if (!hueRef.current) return;
5174
- const rect = hueRef.current.getBoundingClientRect();
5175
- const x = clamp3(e.clientX - rect.left, 0, rect.width);
5176
- const hh = rect.width === 0 ? 0 : x / rect.width * 360;
5177
- setDraftFromHsv(hh, s, v);
5299
+ if (e.buttons !== 1) return;
5300
+ const hh = huePointFromEvent(e.clientX);
5301
+ if (hh != null) commitFromHsv(hh, sRef.current, vRef.current);
5302
+ },
5303
+ [huePointFromEvent, commitFromHsv]
5304
+ );
5305
+ const handleHexChange = React9.useCallback(
5306
+ (_, val) => {
5307
+ const next = val ?? "";
5308
+ setHexText(next);
5309
+ if (isCompleteHex(next.trim())) commit(normalizeHex(next));
5178
5310
  },
5179
- [s, v, setDraftFromHsv]
5311
+ [commit]
5180
5312
  );
5181
- const handleApply = React9.useCallback(() => {
5182
- onChange(hex);
5183
- setOpenAndNotify(false);
5184
- }, [onChange, hex, setOpenAndNotify]);
5313
+ const handleHexBlur = React9.useCallback(() => {
5314
+ commit(normalizeHex(hexText));
5315
+ }, [hexText, commit]);
5185
5316
  const svThumb = React9.useMemo(() => ({ left: `${s * 100}%`, top: `${(1 - v) * 100}%` }), [s, v]);
5186
5317
  const hueThumb = React9.useMemo(() => ({ left: `${h / 360 * 100}%` }), [h]);
5187
5318
  const hueColor = React9.useMemo(() => {
@@ -5236,44 +5367,192 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5236
5367
  directionalHint: 4,
5237
5368
  className: "aoColorCallout",
5238
5369
  preventDismissOnEvent,
5239
- children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 10 }, styles: { root: { padding: 12, width: 320 } }, children: [
5240
- /* @__PURE__ */ jsxs("div", { className: "aoLexRow", children: [
5241
- /* @__PURE__ */ jsx("div", { className: "aoLexSwatch", style: { background: hex } }),
5242
- /* @__PURE__ */ jsx("div", { className: "aoLexTitle", children: title })
5243
- ] }),
5244
- /* @__PURE__ */ jsxs("div", { className: "aoLexRow", children: [
5245
- /* @__PURE__ */ jsx("div", { className: "aoLexLabel", children: "Hex" }),
5370
+ children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 14 }, styles: { root: { padding: "14px 16px 16px", width: 288 } }, children: [
5371
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
5372
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13, fontWeight: 600, color: "#242424", letterSpacing: 0.1 }, children: title }),
5246
5373
  /* @__PURE__ */ jsx(
5247
- TextField,
5374
+ "button",
5248
5375
  {
5249
- value: hex,
5250
- onChange: (_, val) => setHex(normalizeHex(val || "")),
5251
- onBlur: () => setDraft(normalizeHex(hex))
5376
+ type: "button",
5377
+ "aria-label": "Close",
5378
+ onClick: () => setOpenAndNotify(false),
5379
+ style: {
5380
+ display: "flex",
5381
+ alignItems: "center",
5382
+ justifyContent: "center",
5383
+ width: 24,
5384
+ height: 24,
5385
+ padding: 0,
5386
+ border: "none",
5387
+ borderRadius: 4,
5388
+ background: "transparent",
5389
+ color: "#616161",
5390
+ cursor: "pointer"
5391
+ },
5392
+ onMouseEnter: (e) => e.currentTarget.style.background = "#f0f0f0",
5393
+ onMouseLeave: (e) => e.currentTarget.style.background = "transparent",
5394
+ children: /* @__PURE__ */ jsx(Dismiss16Regular, {})
5252
5395
  }
5253
5396
  )
5254
5397
  ] }),
5255
- /* @__PURE__ */ jsx("div", { className: "aoLexSwatches", children: PRESET.map((c) => /* @__PURE__ */ jsx(
5256
- "button",
5398
+ /* @__PURE__ */ jsxs(
5399
+ "div",
5257
5400
  {
5258
- type: "button",
5259
- className: "aoLexSwatchBtn",
5260
- style: { background: c },
5261
- onClick: () => setDraft(c),
5262
- title: c
5263
- },
5264
- c
5265
- )) }),
5266
- /* @__PURE__ */ jsxs("div", { className: "aoLexSV", ref: svRef, onClick: handleSVClick, children: [
5267
- /* @__PURE__ */ jsx("div", { className: "aoLexSVHue", style: { background: hueColor } }),
5268
- /* @__PURE__ */ jsx("div", { className: "aoLexSVWhite" }),
5269
- /* @__PURE__ */ jsx("div", { className: "aoLexSVBlack" }),
5270
- /* @__PURE__ */ jsx("div", { className: "aoLexSVThumb", style: svThumb })
5401
+ ref: svRef,
5402
+ onPointerDown: handleSVPointerDown,
5403
+ onPointerMove: handleSVPointerMove,
5404
+ style: {
5405
+ position: "relative",
5406
+ width: "100%",
5407
+ height: 150,
5408
+ borderRadius: 8,
5409
+ overflow: "hidden",
5410
+ cursor: "crosshair",
5411
+ touchAction: "none",
5412
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.08)"
5413
+ },
5414
+ children: [
5415
+ /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: hueColor } }),
5416
+ /* @__PURE__ */ jsx(
5417
+ "div",
5418
+ {
5419
+ style: {
5420
+ position: "absolute",
5421
+ inset: 0,
5422
+ background: "linear-gradient(to right, #fff, rgba(255,255,255,0))"
5423
+ }
5424
+ }
5425
+ ),
5426
+ /* @__PURE__ */ jsx(
5427
+ "div",
5428
+ {
5429
+ style: {
5430
+ position: "absolute",
5431
+ inset: 0,
5432
+ background: "linear-gradient(to top, #000, rgba(0,0,0,0))"
5433
+ }
5434
+ }
5435
+ ),
5436
+ /* @__PURE__ */ jsx(
5437
+ "div",
5438
+ {
5439
+ style: {
5440
+ position: "absolute",
5441
+ width: 16,
5442
+ height: 16,
5443
+ borderRadius: "50%",
5444
+ border: "2px solid #fff",
5445
+ boxShadow: "0 0 0 1px rgba(0,0,0,0.35), 0 1px 3px rgba(0,0,0,0.4)",
5446
+ transform: "translate(-50%, -50%)",
5447
+ pointerEvents: "none",
5448
+ ...svThumb
5449
+ }
5450
+ }
5451
+ )
5452
+ ]
5453
+ }
5454
+ ),
5455
+ /* @__PURE__ */ jsx(
5456
+ "div",
5457
+ {
5458
+ ref: hueRef,
5459
+ onPointerDown: handleHuePointerDown,
5460
+ onPointerMove: handleHuePointerMove,
5461
+ style: {
5462
+ position: "relative",
5463
+ width: "100%",
5464
+ height: 12,
5465
+ borderRadius: 999,
5466
+ cursor: "pointer",
5467
+ touchAction: "none",
5468
+ background: "linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)",
5469
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.08)"
5470
+ },
5471
+ children: /* @__PURE__ */ jsx(
5472
+ "div",
5473
+ {
5474
+ style: {
5475
+ position: "absolute",
5476
+ top: "50%",
5477
+ width: 16,
5478
+ height: 16,
5479
+ borderRadius: "50%",
5480
+ background: hueColor,
5481
+ border: "2px solid #fff",
5482
+ boxShadow: "0 0 0 1px rgba(0,0,0,0.35), 0 1px 3px rgba(0,0,0,0.4)",
5483
+ transform: "translate(-50%, -50%)",
5484
+ pointerEvents: "none",
5485
+ ...hueThumb
5486
+ }
5487
+ }
5488
+ )
5489
+ }
5490
+ ),
5491
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5492
+ /* @__PURE__ */ jsx(
5493
+ "div",
5494
+ {
5495
+ style: {
5496
+ width: 32,
5497
+ height: 32,
5498
+ borderRadius: 6,
5499
+ flexShrink: 0,
5500
+ background: hex,
5501
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.12)"
5502
+ }
5503
+ }
5504
+ ),
5505
+ /* @__PURE__ */ jsx(
5506
+ TextField,
5507
+ {
5508
+ value: hexText,
5509
+ onChange: handleHexChange,
5510
+ onBlur: handleHexBlur,
5511
+ onKeyDown: (e) => {
5512
+ if (e.key === "Enter") commit(normalizeHex(hexText));
5513
+ },
5514
+ styles: { root: { flex: 1 }, fieldGroup: { borderRadius: 6 } }
5515
+ }
5516
+ )
5271
5517
  ] }),
5272
- /* @__PURE__ */ jsx("div", { className: "aoLexHue", ref: hueRef, onClick: handleHueClick, children: /* @__PURE__ */ jsx("div", { className: "aoLexHueThumb", style: hueThumb }) }),
5273
- /* @__PURE__ */ jsx("div", { className: "aoLexPreview", style: { background: hex } }),
5274
- /* @__PURE__ */ jsxs("div", { className: "aoLexActions", children: [
5275
- /* @__PURE__ */ jsx(DefaultButton, { type: "button", text: "Apply", onClick: handleApply }),
5276
- /* @__PURE__ */ jsx(DefaultButton, { type: "button", text: "Close", onClick: () => setOpenAndNotify(false) })
5518
+ /* @__PURE__ */ jsxs("div", { children: [
5519
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 600, color: "#8a8a8a", marginBottom: 6, letterSpacing: 0.3 }, children: "STANDARD COLORS" }),
5520
+ /* @__PURE__ */ jsx(
5521
+ "div",
5522
+ {
5523
+ style: {
5524
+ display: "grid",
5525
+ gridTemplateColumns: "repeat(9, 1fr)",
5526
+ gap: 6
5527
+ },
5528
+ children: PRESET.map((c) => {
5529
+ const isSelected = c.toLowerCase() === hex.toLowerCase();
5530
+ return /* @__PURE__ */ jsx(
5531
+ "button",
5532
+ {
5533
+ type: "button",
5534
+ onClick: () => commit(c),
5535
+ title: c,
5536
+ "aria-label": c,
5537
+ style: {
5538
+ width: 22,
5539
+ height: 22,
5540
+ padding: 0,
5541
+ borderRadius: 5,
5542
+ background: c,
5543
+ cursor: "pointer",
5544
+ boxShadow: isSelected ? "0 0 0 2px #fff, 0 0 0 3px #4a86e8" : "inset 0 0 0 1px rgba(0,0,0,0.15)",
5545
+ border: "none",
5546
+ transition: "transform 80ms ease"
5547
+ },
5548
+ onMouseEnter: (e) => e.currentTarget.style.transform = "scale(1.12)",
5549
+ onMouseLeave: (e) => e.currentTarget.style.transform = "scale(1)"
5550
+ },
5551
+ c
5552
+ );
5553
+ })
5554
+ }
5555
+ )
5277
5556
  ] })
5278
5557
  ] })
5279
5558
  }
@@ -5705,6 +5984,60 @@ var InsertLinkPlugin = ({ disabled }) => {
5705
5984
  }
5706
5985
  );
5707
5986
  };
5987
+ function PageSetupPlugin({ disabled, value, onChange }) {
5988
+ const sizeLabel = value.size === "pageless" ? "Pageless" : PAGE_SIZE_OPTIONS.find((o) => o.key === value.size)?.label ?? "Pageless";
5989
+ const isPaged = value.size !== "pageless";
5990
+ return /* @__PURE__ */ jsxs(
5991
+ Menu,
5992
+ {
5993
+ checkedValues: {
5994
+ size: [value.size],
5995
+ orientation: [value.orientation],
5996
+ margin: [value.margin]
5997
+ },
5998
+ onCheckedValueChange: (_, data) => {
5999
+ const selected = data.checkedItems[0];
6000
+ if (!selected) return;
6001
+ if (data.name === "size") onChange({ ...value, size: selected });
6002
+ else if (data.name === "orientation")
6003
+ onChange({ ...value, orientation: selected });
6004
+ else if (data.name === "margin") onChange({ ...value, margin: selected });
6005
+ },
6006
+ children: [
6007
+ /* @__PURE__ */ jsx(MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
6008
+ Button,
6009
+ {
6010
+ appearance: "subtle",
6011
+ size: "small",
6012
+ disabled,
6013
+ icon: /* @__PURE__ */ jsx(DocumentRegular, {}),
6014
+ title: "Page setup",
6015
+ style: { minWidth: "auto" },
6016
+ children: sizeLabel
6017
+ }
6018
+ ) }),
6019
+ /* @__PURE__ */ jsx(MenuPopover, { style: { minWidth: 220 }, children: /* @__PURE__ */ jsxs(MenuList, { children: [
6020
+ /* @__PURE__ */ jsxs(MenuGroup, { children: [
6021
+ /* @__PURE__ */ jsx(MenuGroupHeader, { children: "Page size" }),
6022
+ /* @__PURE__ */ jsx(MenuItemRadio, { name: "size", value: "pageless", children: "Pageless" }),
6023
+ PAGE_SIZE_OPTIONS.map((opt) => /* @__PURE__ */ jsx(MenuItemRadio, { name: "size", value: opt.key, children: opt.label }, opt.key))
6024
+ ] }),
6025
+ /* @__PURE__ */ jsx(MenuDivider, {}),
6026
+ /* @__PURE__ */ jsxs(MenuGroup, { children: [
6027
+ /* @__PURE__ */ jsx(MenuGroupHeader, { children: "Orientation" }),
6028
+ /* @__PURE__ */ jsx(MenuItemRadio, { name: "orientation", value: "portrait", disabled: !isPaged, children: "Portrait" }),
6029
+ /* @__PURE__ */ jsx(MenuItemRadio, { name: "orientation", value: "landscape", disabled: !isPaged, children: "Landscape" })
6030
+ ] }),
6031
+ /* @__PURE__ */ jsx(MenuDivider, {}),
6032
+ /* @__PURE__ */ jsxs(MenuGroup, { children: [
6033
+ /* @__PURE__ */ jsx(MenuGroupHeader, { children: "Margins" }),
6034
+ MARGIN_OPTIONS.map((opt) => /* @__PURE__ */ jsx(MenuItemRadio, { name: "margin", value: opt.key, disabled: !isPaged, children: opt.label }, opt.key))
6035
+ ] })
6036
+ ] }) })
6037
+ ]
6038
+ }
6039
+ );
6040
+ }
5708
6041
  var TableItemPlugin = ({ disabled }) => {
5709
6042
  const [editor] = useLexicalComposerContext();
5710
6043
  const [columns, setColumns] = useState("");
@@ -5922,7 +6255,8 @@ var ALLOWED_TOKENS = {
5922
6255
  FontSize: true,
5923
6256
  Decorators: true,
5924
6257
  CodeBlock: true,
5925
- Align: true
6258
+ Align: true,
6259
+ PageSetup: true
5926
6260
  };
5927
6261
  function sanitizePluginGroups(groups) {
5928
6262
  if (!groups || groups.length === 0) return [];
@@ -6461,6 +6795,16 @@ var ToolBarPlugins = (props) => {
6461
6795
  key
6462
6796
  );
6463
6797
  }
6798
+ case "PageSetup":
6799
+ return /* @__PURE__ */ jsx(
6800
+ PageSetupPlugin,
6801
+ {
6802
+ disabled: !isEditable || props.readOnly,
6803
+ value: props.pageSetup,
6804
+ onChange: props.onPageSetupChange
6805
+ },
6806
+ key
6807
+ );
6464
6808
  default:
6465
6809
  return null;
6466
6810
  }
@@ -6812,6 +7156,8 @@ var ContentEditorComponent = forwardRef(
6812
7156
  const [charCount, setCharCount] = useState(0);
6813
7157
  const handleCharCount = useCallback((count) => setCharCount(count), []);
6814
7158
  const [refErrors, setRefErrors] = useState([]);
7159
+ const [pageSetup, setPageSetup] = useState(DEFAULT_PAGE_SETUP);
7160
+ const pageCanvas = resolvePageCanvasMetrics(pageSetup);
6815
7161
  const contentEditableDomRef = useRef(null);
6816
7162
  const previousOverLimitRef = useRef(false);
6817
7163
  const focusedRef = useRef(false);
@@ -6853,8 +7199,8 @@ var ContentEditorComponent = forwardRef(
6853
7199
  color: "var(--colorNeutralForeground3, grey)",
6854
7200
  position: "absolute",
6855
7201
  top: props.level !== "none" /* None */ ? "17px" : "27px",
6856
- left: 20,
6857
- right: 20,
7202
+ left: pageCanvas.paddingPx,
7203
+ right: pageCanvas.paddingPx,
6858
7204
  fontSize: "14px",
6859
7205
  pointerEvents: "none",
6860
7206
  userSelect: "none"
@@ -6865,8 +7211,6 @@ var ContentEditorComponent = forwardRef(
6865
7211
  outline: "none",
6866
7212
  overflow: "auto",
6867
7213
  marginTop: "0px",
6868
- paddingLeft: "20px",
6869
- paddingRight: "20px",
6870
7214
  position: "relative",
6871
7215
  background: "var(--colorNeutralBackground1, #ffffff)",
6872
7216
  justifyContent: "center",
@@ -6966,7 +7310,9 @@ var ContentEditorComponent = forwardRef(
6966
7310
  ToolBarPlugins,
6967
7311
  {
6968
7312
  level: props.level ?? "basic" /* Basic */,
6969
- readOnly: props.readOnly
7313
+ readOnly: props.readOnly,
7314
+ pageSetup,
7315
+ onPageSetupChange: setPageSetup
6970
7316
  }
6971
7317
  )
6972
7318
  }
@@ -6980,7 +7326,8 @@ var ContentEditorComponent = forwardRef(
6980
7326
  padding: "15px 0px",
6981
7327
  overflowY: "scroll",
6982
7328
  overflowX: "auto",
6983
- minWidth: 0
7329
+ minWidth: 0,
7330
+ background: pageCanvas.widthPx !== void 0 ? "#eef0f2" : void 0
6984
7331
  },
6985
7332
  onClickCapture: handleReadOnlyClickCapture,
6986
7333
  children: [
@@ -6999,7 +7346,15 @@ var ContentEditorComponent = forwardRef(
6999
7346
  {
7000
7347
  ref: contentEditableDomRef,
7001
7348
  className: css(EditorStyles.contentEditor),
7002
- style: { paddingTop: props.level !== "none" /* None */ ? 0 : 10 },
7349
+ style: {
7350
+ paddingTop: props.level !== "none" /* None */ ? 0 : 10,
7351
+ paddingLeft: pageCanvas.paddingPx,
7352
+ paddingRight: pageCanvas.paddingPx,
7353
+ maxWidth: pageCanvas.widthPx,
7354
+ marginLeft: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
7355
+ marginRight: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
7356
+ 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
7357
+ },
7003
7358
  spellCheck: !resolvedSpellCheck,
7004
7359
  autoCorrect: resolvedSpellCheck ? "off" : void 0,
7005
7360
  autoCapitalize: resolvedSpellCheck ? "off" : void 0