@tarviks/lexical-rich-editor 1.2.1 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9,7 +9,6 @@ var useLexicalNodeSelection = require('@lexical/react/useLexicalNodeSelection');
9
9
  var lexical = require('lexical');
10
10
  var react = require('@fluentui/react');
11
11
  var reactComponents = require('@fluentui/react-components');
12
- var reactIcons = require('@fluentui/react-icons');
13
12
  var code = require('@lexical/code');
14
13
  var link = require('@lexical/link');
15
14
  var list = require('@lexical/list');
@@ -25,8 +24,8 @@ var LexicalRichTextPlugin = require('@lexical/react/LexicalRichTextPlugin');
25
24
  var LexicalTablePlugin = require('@lexical/react/LexicalTablePlugin');
26
25
  var richText = require('@lexical/rich-text');
27
26
  var table = require('@lexical/table');
28
- var LexicalBlockWithAlignableContents = require('@lexical/react/LexicalBlockWithAlignableContents');
29
27
  var LexicalDecoratorBlockNode = require('@lexical/react/LexicalDecoratorBlockNode');
28
+ var reactIcons = require('@fluentui/react-icons');
30
29
  var selection = require('@lexical/selection');
31
30
  var reactDom = require('react-dom');
32
31
  var html = require('@lexical/html');
@@ -544,17 +543,6 @@ var init_ImageComponent = __esm({
544
543
  onClick,
545
544
  lexical.COMMAND_PRIORITY_LOW
546
545
  ),
547
- editor.registerCommand(
548
- lexical.DRAGSTART_COMMAND,
549
- (event) => {
550
- if (event.target === imageRef.current) {
551
- event.preventDefault();
552
- return true;
553
- }
554
- return false;
555
- },
556
- lexical.COMMAND_PRIORITY_LOW
557
- ),
558
546
  editor.registerCommand(
559
547
  lexical.KEY_DELETE_COMMAND,
560
548
  $onDelete,
@@ -612,36 +600,44 @@ var init_ImageComponent = __esm({
612
600
  const onResizeStart = () => {
613
601
  setIsResizing(true);
614
602
  };
615
- const draggable = isSelected && lexical.$isNodeSelection(selection) && !isResizing;
603
+ const draggable = !isResizing;
616
604
  const isFocused = (isSelected || isResizing) && isEditable;
617
- return /* @__PURE__ */ jsxRuntime.jsx(React9.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
618
- /* @__PURE__ */ jsxRuntime.jsx("div", { draggable, children: isLoadError ? /* @__PURE__ */ jsxRuntime.jsx(BrokenImage, {}) : /* @__PURE__ */ jsxRuntime.jsx(
619
- LazyImage,
620
- {
621
- className: isFocused ? `focused ${lexical.$isNodeSelection(selection) ? "draggable" : ""}` : null,
622
- src,
623
- altText,
624
- imageRef,
625
- width,
626
- height,
627
- maxWidth,
628
- onError: () => setIsLoadError(true)
629
- }
630
- ) }),
631
- resizable && lexical.$isNodeSelection(selection) && isFocused && /* @__PURE__ */ jsxRuntime.jsx(
632
- ImageResizer_default,
633
- {
634
- showCaption,
635
- setShowCaption,
636
- editor,
637
- buttonRef,
638
- imageRef,
639
- onResizeStart,
640
- onResizeEnd,
641
- captionsEnabled: !isLoadError && captionsEnabled
642
- }
643
- )
644
- ] }) });
605
+ return /* @__PURE__ */ jsxRuntime.jsx(React9.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsxs(
606
+ "div",
607
+ {
608
+ draggable,
609
+ style: { position: "relative", display: "inline-block" },
610
+ children: [
611
+ isLoadError ? /* @__PURE__ */ jsxRuntime.jsx(BrokenImage, {}) : /* @__PURE__ */ jsxRuntime.jsx(
612
+ LazyImage,
613
+ {
614
+ className: isFocused ? `focused ${lexical.$isNodeSelection(selection) ? "draggable" : ""}` : null,
615
+ src,
616
+ altText,
617
+ imageRef,
618
+ width,
619
+ height,
620
+ maxWidth,
621
+ onError: () => setIsLoadError(true)
622
+ }
623
+ ),
624
+ resizable && lexical.$isNodeSelection(selection) && isFocused && /* @__PURE__ */ jsxRuntime.jsx(
625
+ ImageResizer_default,
626
+ {
627
+ showCaption,
628
+ setShowCaption,
629
+ editor,
630
+ buttonRef,
631
+ imageRef,
632
+ maxWidth,
633
+ onResizeStart,
634
+ onResizeEnd,
635
+ captionsEnabled: !isLoadError && captionsEnabled
636
+ }
637
+ )
638
+ ]
639
+ }
640
+ ) });
645
641
  };
646
642
  ImageComponent_default = ImageComponent;
647
643
  }
@@ -786,9 +782,6 @@ var init_ImageNode = __esm({
786
782
  const writable = this.getWritable();
787
783
  writable.__width = width;
788
784
  writable.__height = height;
789
- if (typeof width === "number" && width > writable.__maxWidth) {
790
- writable.__maxWidth = width;
791
- }
792
785
  }
793
786
  setShowCaption(showCaption) {
794
787
  const writable = this.getWritable();
@@ -915,35 +908,13 @@ var init_InlineImageComponent = __esm({
915
908
  };
916
909
  InlineImageComponent = ({ src, altText, nodeKey, width, height, showCaption, caption, position }) => {
917
910
  const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection.useLexicalNodeSelection(nodeKey);
911
+ const [isResizing, setIsResizing] = React9.useState(false);
918
912
  const [selection, setSelection] = React9.useState(null);
919
913
  const activeEditorRef = React9.useRef(null);
920
914
  const imageRef = React9.useRef(null);
921
915
  const buttonRef = React9.useRef(null);
922
916
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
923
917
  const isEditable = useLexicalEditable.useLexicalEditable();
924
- const [isResizing, setIsResizing] = React9.useState(false);
925
- const onResizeEnd = (nextWidth, nextHeight) => {
926
- setTimeout(() => {
927
- setIsResizing(false);
928
- }, 200);
929
- editor.update(() => {
930
- const node = lexical.$getNodeByKey(nodeKey);
931
- if ($isInlineImageNode(node)) {
932
- node.setWidthAndHeight(nextWidth, nextHeight);
933
- }
934
- });
935
- };
936
- const onResizeStart = () => {
937
- setIsResizing(true);
938
- };
939
- const setShowCaption = (show) => {
940
- editor.update(() => {
941
- const node = lexical.$getNodeByKey(nodeKey);
942
- if ($isInlineImageNode(node)) {
943
- node.setShowCaption(show);
944
- }
945
- });
946
- };
947
918
  const $onDelete = React9.useCallback(
948
919
  (payload) => {
949
920
  const deleteSelection = lexical.$getSelection();
@@ -999,6 +970,25 @@ var init_InlineImageComponent = __esm({
999
970
  },
1000
971
  [caption, editor, setSelected]
1001
972
  );
973
+ const onClick = React9.useCallback(
974
+ (payload) => {
975
+ const event = payload;
976
+ if (isResizing) {
977
+ return true;
978
+ }
979
+ if (event.target === imageRef.current) {
980
+ if (event.shiftKey) {
981
+ setSelected(!isSelected);
982
+ } else {
983
+ clearSelection();
984
+ setSelected(true);
985
+ }
986
+ return true;
987
+ }
988
+ return false;
989
+ },
990
+ [isResizing, isSelected, setSelected, clearSelection]
991
+ );
1002
992
  React9.useEffect(() => {
1003
993
  let isMounted = true;
1004
994
  const unregister = utils.mergeRegister(
@@ -1017,51 +1007,13 @@ var init_InlineImageComponent = __esm({
1017
1007
  ),
1018
1008
  editor.registerCommand(
1019
1009
  lexical.CLICK_COMMAND,
1020
- (payload) => {
1021
- const event = payload;
1022
- if (isResizing) {
1023
- return true;
1024
- }
1025
- if (event.target === imageRef.current) {
1026
- if (event.shiftKey) {
1027
- setSelected(!isSelected);
1028
- } else {
1029
- clearSelection();
1030
- setSelected(true);
1031
- }
1032
- return true;
1033
- }
1034
- return false;
1035
- },
1036
- lexical.COMMAND_PRIORITY_LOW
1037
- ),
1038
- editor.registerCommand(
1039
- lexical.DRAGSTART_COMMAND,
1040
- (event) => {
1041
- if (event.target === imageRef.current) {
1042
- event.preventDefault();
1043
- return true;
1044
- }
1045
- return false;
1046
- },
1047
- lexical.COMMAND_PRIORITY_LOW
1048
- ),
1049
- editor.registerCommand(
1050
- lexical.KEY_DELETE_COMMAND,
1051
- $onDelete,
1052
- lexical.COMMAND_PRIORITY_LOW
1053
- ),
1054
- editor.registerCommand(
1055
- lexical.KEY_BACKSPACE_COMMAND,
1056
- $onDelete,
1010
+ onClick,
1057
1011
  lexical.COMMAND_PRIORITY_LOW
1058
1012
  ),
1013
+ editor.registerCommand(lexical.KEY_DELETE_COMMAND, $onDelete, lexical.COMMAND_PRIORITY_LOW),
1014
+ editor.registerCommand(lexical.KEY_BACKSPACE_COMMAND, $onDelete, lexical.COMMAND_PRIORITY_LOW),
1059
1015
  editor.registerCommand(lexical.KEY_ENTER_COMMAND, $onEnter, lexical.COMMAND_PRIORITY_LOW),
1060
- editor.registerCommand(
1061
- lexical.KEY_ESCAPE_COMMAND,
1062
- $onEscape,
1063
- lexical.COMMAND_PRIORITY_LOW
1064
- )
1016
+ editor.registerCommand(lexical.KEY_ESCAPE_COMMAND, $onEscape, lexical.COMMAND_PRIORITY_LOW)
1065
1017
  );
1066
1018
  return () => {
1067
1019
  isMounted = false;
@@ -1076,12 +1028,27 @@ var init_InlineImageComponent = __esm({
1076
1028
  $onDelete,
1077
1029
  $onEnter,
1078
1030
  $onEscape,
1031
+ onClick,
1079
1032
  setSelected
1080
1033
  ]);
1081
- const draggable = isSelected && lexical.$isNodeSelection(selection) && !isResizing;
1034
+ const onResizeStart = () => {
1035
+ setIsResizing(true);
1036
+ };
1037
+ const onResizeEnd = (nextWidth, nextHeight) => {
1038
+ setTimeout(() => {
1039
+ setIsResizing(false);
1040
+ }, 200);
1041
+ editor.update(() => {
1042
+ const node = lexical.$getNodeByKey(nodeKey);
1043
+ if ($isInlineImageNode(node)) {
1044
+ node.setWidthAndHeight(nextWidth, nextHeight);
1045
+ }
1046
+ });
1047
+ };
1048
+ const draggable = !isResizing;
1082
1049
  const isFocused = (isSelected || isResizing) && isEditable;
1083
- return /* @__PURE__ */ jsxRuntime.jsx(React9.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1084
- /* @__PURE__ */ jsxRuntime.jsx("span", { draggable, children: /* @__PURE__ */ jsxRuntime.jsx(
1050
+ return /* @__PURE__ */ jsxRuntime.jsx(React9.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { draggable, style: { position: "relative", display: "inline-block" }, children: [
1051
+ /* @__PURE__ */ jsxRuntime.jsx(
1085
1052
  LazyImage2,
1086
1053
  {
1087
1054
  className: isFocused ? `focused ${lexical.$isNodeSelection(selection) ? "draggable" : ""}` : null,
@@ -1092,18 +1059,19 @@ var init_InlineImageComponent = __esm({
1092
1059
  height,
1093
1060
  position
1094
1061
  }
1095
- ) }),
1096
- isFocused && lexical.$isNodeSelection(selection) && /* @__PURE__ */ jsxRuntime.jsx(
1062
+ ),
1063
+ lexical.$isNodeSelection(selection) && isFocused && /* @__PURE__ */ jsxRuntime.jsx(
1097
1064
  ImageResizer_default,
1098
1065
  {
1099
- showCaption,
1100
- setShowCaption,
1066
+ showCaption: false,
1067
+ setShowCaption: () => {
1068
+ },
1069
+ captionsEnabled: false,
1101
1070
  editor,
1102
1071
  buttonRef,
1103
1072
  imageRef,
1104
1073
  onResizeStart,
1105
- onResizeEnd,
1106
- captionsEnabled: false
1074
+ onResizeEnd
1107
1075
  }
1108
1076
  )
1109
1077
  ] }) });
@@ -1294,6 +1262,19 @@ var ContentEditorLevel = /* @__PURE__ */ ((ContentEditorLevel2) => {
1294
1262
  ContentEditorLevel2["Pro"] = "pro";
1295
1263
  return ContentEditorLevel2;
1296
1264
  })(ContentEditorLevel || {});
1265
+ var DEFAULT_VALIDATION_MESSAGES = {
1266
+ required: "This field is required.",
1267
+ minWords: (current, min) => `Minimum ${min} word${min === 1 ? "" : "s"} required (${current} entered).`,
1268
+ maxWords: (current, max) => `Word limit exceeded: ${current} / ${max} words.`,
1269
+ minChars: (current, min) => `Minimum ${min} character${min === 1 ? "" : "s"} required (${current} entered).`,
1270
+ maxChars: (current, max) => `Character limit exceeded: ${current} / ${max} characters.`,
1271
+ noImages: "Images are not allowed in this field.",
1272
+ maxImages: (current, max) => `Too many images: ${current} / ${max} allowed.`,
1273
+ noLinks: "Hyperlinks are not allowed in this field.",
1274
+ maxLinks: (current, max) => `Too many links: ${current} / ${max} allowed.`,
1275
+ noTables: "Tables are not allowed in this field.",
1276
+ imageTooLarge: (fileMB, maxMB) => `Image size (${fileMB.toFixed(1)} MB) exceeds the ${maxMB} MB limit.`
1277
+ };
1297
1278
 
1298
1279
  // src/Types/PageSetup.ts
1299
1280
  var DEFAULT_PAGE_SETUP = {
@@ -1334,6 +1315,106 @@ function resolvePageCanvasMetrics(value) {
1334
1315
  paddingPx: Math.round(margin.valueIn * CSS_PX_PER_INCH)
1335
1316
  };
1336
1317
  }
1318
+ lexical.createCommand(
1319
+ "INSERT_ALPHA_LIST_COMMAND"
1320
+ );
1321
+ var AlphaListNode = class _AlphaListNode extends list.ListNode {
1322
+ static getType() {
1323
+ return "alpha-list";
1324
+ }
1325
+ static clone(node) {
1326
+ return new _AlphaListNode(node.getStart(), node.__key);
1327
+ }
1328
+ constructor(start = 1, key) {
1329
+ super("number", start, key);
1330
+ this.__listType = "lower-alpha";
1331
+ }
1332
+ createDOM(config, _editor) {
1333
+ const element = document.createElement("ol");
1334
+ element.setAttribute("type", "a");
1335
+ if (this.getStart() !== 1) element.setAttribute("start", String(this.getStart()));
1336
+ element.style.listStyleType = "lower-alpha";
1337
+ const olClass = config.theme?.list?.ol;
1338
+ if (olClass) element.className = olClass;
1339
+ return element;
1340
+ }
1341
+ updateDOM() {
1342
+ return false;
1343
+ }
1344
+ exportDOM(_editor) {
1345
+ const element = document.createElement("ol");
1346
+ element.setAttribute("type", "a");
1347
+ if (this.getStart() !== 1) element.setAttribute("start", String(this.getStart()));
1348
+ element.style.listStyleType = "lower-alpha";
1349
+ return { element };
1350
+ }
1351
+ static importDOM() {
1352
+ return {
1353
+ ol: (domNode) => {
1354
+ if (domNode instanceof HTMLOListElement && (domNode.getAttribute("type") === "a" || domNode.style.listStyleType === "lower-alpha")) {
1355
+ return { conversion: convertAlphaOL, priority: 1 };
1356
+ }
1357
+ return null;
1358
+ }
1359
+ };
1360
+ }
1361
+ exportJSON() {
1362
+ const base = super.exportJSON();
1363
+ return {
1364
+ ...base,
1365
+ type: "alpha-list",
1366
+ listType: "lower-alpha",
1367
+ tag: "ol",
1368
+ version: 1,
1369
+ start: this.getStart()
1370
+ };
1371
+ }
1372
+ static importJSON(serialized) {
1373
+ return new _AlphaListNode(serialized.start ?? 1);
1374
+ }
1375
+ };
1376
+ function convertAlphaOL(domNode) {
1377
+ const start = domNode instanceof HTMLOListElement ? parseInt(domNode.getAttribute("start") || "1", 10) || 1 : 1;
1378
+ return { node: $createAlphaListNode(start) };
1379
+ }
1380
+ function $createAlphaListNode(start = 1) {
1381
+ return new AlphaListNode(start);
1382
+ }
1383
+ function $isAlphaListNode(node) {
1384
+ return node instanceof AlphaListNode;
1385
+ }
1386
+ function $toggleAlphaList() {
1387
+ const selection = lexical.$getSelection();
1388
+ if (!lexical.$isRangeSelection(selection)) return;
1389
+ const anchor = selection.anchor.getNode();
1390
+ const currentList = utils.$findMatchingParent(anchor, list.$isListNode);
1391
+ if ($isAlphaListNode(currentList)) {
1392
+ list.$removeList();
1393
+ return;
1394
+ }
1395
+ list.$insertList("number");
1396
+ const sel2 = lexical.$getSelection();
1397
+ if (!lexical.$isRangeSelection(sel2)) return;
1398
+ const newAnchor = sel2.anchor.getNode();
1399
+ const newList = utils.$findMatchingParent(newAnchor, (n) => list.$isListNode(n) && !$isAlphaListNode(n));
1400
+ if (!newList || !list.$isListNode(newList)) return;
1401
+ const start = newList.getStart();
1402
+ const formatType = typeof newList.getFormatType === "function" ? newList.getFormatType() : void 0;
1403
+ const alpha = $createAlphaListNode(start);
1404
+ for (const child of newList.getChildren()) {
1405
+ alpha.append(child);
1406
+ }
1407
+ newList.replace(alpha);
1408
+ if (formatType && typeof alpha.setFormat === "function") {
1409
+ alpha.setFormat(formatType);
1410
+ }
1411
+ const firstItem = alpha.getFirstChild();
1412
+ if (list.$isListItemNode(firstItem)) {
1413
+ firstItem.selectStart();
1414
+ } else {
1415
+ alpha.select();
1416
+ }
1417
+ }
1337
1418
  var AutocompleteNode = class _AutocompleteNode extends lexical.TextNode {
1338
1419
  static getType() {
1339
1420
  return "autocomplete";
@@ -1344,6 +1425,13 @@ var AutocompleteNode = class _AutocompleteNode extends lexical.TextNode {
1344
1425
  static importJSON(serializedNode) {
1345
1426
  return new _AutocompleteNode(serializedNode.text, serializedNode.uuid);
1346
1427
  }
1428
+ // AutocompleteNode is excluded from copy via excludeFromCopy(), so it is
1429
+ // never placed on the clipboard and never needs to be reconstructed from
1430
+ // HTML. Returning null here satisfies Lexical's exportDOM/importDOM
1431
+ // contract check and silences the console warning.
1432
+ static importDOM() {
1433
+ return null;
1434
+ }
1347
1435
  exportJSON() {
1348
1436
  return { ...super.exportJSON(), uuid: this.__uuid };
1349
1437
  }
@@ -1658,13 +1746,20 @@ function VideoResizer({
1658
1746
  const dy = ev.clientY - rs.startY;
1659
1747
  let newW = rs.startW;
1660
1748
  let newH = rs.startH;
1661
- if (dir === "se") {
1749
+ if (dir === "se" || dir === "ne") {
1662
1750
  newW = Math.max(MIN_WIDTH, rs.startW + dx);
1663
1751
  newH = newW / rs.ratio;
1752
+ } else if (dir === "nw" || dir === "sw") {
1753
+ newW = Math.max(MIN_WIDTH, rs.startW - dx);
1754
+ newH = newW / rs.ratio;
1664
1755
  } else if (dir === "e") {
1665
1756
  newW = Math.max(MIN_WIDTH, rs.startW + dx);
1757
+ } else if (dir === "w") {
1758
+ newW = Math.max(MIN_WIDTH, rs.startW - dx);
1666
1759
  } else if (dir === "s") {
1667
1760
  newH = Math.max(MIN_HEIGHT, rs.startH + dy);
1761
+ } else if (dir === "n") {
1762
+ newH = Math.max(MIN_HEIGHT, rs.startH - dy);
1668
1763
  }
1669
1764
  container.style.width = `${newW}px`;
1670
1765
  container.style.height = `${newH}px`;
@@ -1684,13 +1779,23 @@ function VideoResizer({
1684
1779
  /* @__PURE__ */ jsxRuntime.jsx(
1685
1780
  "div",
1686
1781
  {
1687
- style: {
1688
- ...handleBase,
1689
- top: "50%",
1690
- right: -5,
1691
- transform: "translateY(-50%)",
1692
- cursor: "ew-resize"
1693
- },
1782
+ style: { ...handleBase, top: -5, left: "50%", transform: "translateX(-50%)", cursor: "n-resize" },
1783
+ onPointerDown: (e) => startResize(e, "n"),
1784
+ title: "Resize height"
1785
+ }
1786
+ ),
1787
+ /* @__PURE__ */ jsxRuntime.jsx(
1788
+ "div",
1789
+ {
1790
+ style: { ...handleBase, top: -5, right: -5, cursor: "ne-resize" },
1791
+ onPointerDown: (e) => startResize(e, "ne"),
1792
+ title: "Resize (proportional)"
1793
+ }
1794
+ ),
1795
+ /* @__PURE__ */ jsxRuntime.jsx(
1796
+ "div",
1797
+ {
1798
+ style: { ...handleBase, top: "50%", right: -5, transform: "translateY(-50%)", cursor: "ew-resize" },
1694
1799
  onPointerDown: (e) => startResize(e, "e"),
1695
1800
  title: "Resize width"
1696
1801
  }
@@ -1698,13 +1803,15 @@ function VideoResizer({
1698
1803
  /* @__PURE__ */ jsxRuntime.jsx(
1699
1804
  "div",
1700
1805
  {
1701
- style: {
1702
- ...handleBase,
1703
- bottom: -5,
1704
- left: "50%",
1705
- transform: "translateX(-50%)",
1706
- cursor: "ns-resize"
1707
- },
1806
+ style: { ...handleBase, bottom: -5, right: -5, cursor: "se-resize" },
1807
+ onPointerDown: (e) => startResize(e, "se"),
1808
+ title: "Resize (proportional)"
1809
+ }
1810
+ ),
1811
+ /* @__PURE__ */ jsxRuntime.jsx(
1812
+ "div",
1813
+ {
1814
+ style: { ...handleBase, bottom: -5, left: "50%", transform: "translateX(-50%)", cursor: "s-resize" },
1708
1815
  onPointerDown: (e) => startResize(e, "s"),
1709
1816
  title: "Resize height"
1710
1817
  }
@@ -1712,15 +1819,30 @@ function VideoResizer({
1712
1819
  /* @__PURE__ */ jsxRuntime.jsx(
1713
1820
  "div",
1714
1821
  {
1715
- style: { ...handleBase, bottom: -5, right: -5, cursor: "se-resize" },
1716
- onPointerDown: (e) => startResize(e, "se"),
1822
+ style: { ...handleBase, bottom: -5, left: -5, cursor: "sw-resize" },
1823
+ onPointerDown: (e) => startResize(e, "sw"),
1824
+ title: "Resize (proportional)"
1825
+ }
1826
+ ),
1827
+ /* @__PURE__ */ jsxRuntime.jsx(
1828
+ "div",
1829
+ {
1830
+ style: { ...handleBase, top: "50%", left: -5, transform: "translateY(-50%)", cursor: "ew-resize" },
1831
+ onPointerDown: (e) => startResize(e, "w"),
1832
+ title: "Resize width"
1833
+ }
1834
+ ),
1835
+ /* @__PURE__ */ jsxRuntime.jsx(
1836
+ "div",
1837
+ {
1838
+ style: { ...handleBase, top: -5, left: -5, cursor: "nw-resize" },
1839
+ onPointerDown: (e) => startResize(e, "nw"),
1717
1840
  title: "Resize (proportional)"
1718
1841
  }
1719
1842
  )
1720
1843
  ] });
1721
1844
  }
1722
1845
  function YouTubeComponent({
1723
- className,
1724
1846
  format,
1725
1847
  nodeKey,
1726
1848
  videoID,
@@ -1728,9 +1850,48 @@ function YouTubeComponent({
1728
1850
  height,
1729
1851
  editor
1730
1852
  }) {
1853
+ const wrapperRef = React9__namespace.useRef(null);
1731
1854
  const containerRef = React9__namespace.useRef(null);
1855
+ const iframeRef = React9__namespace.useRef(null);
1856
+ const isResizingRef = React9__namespace.useRef(false);
1857
+ const [isNodeSelected, setNodeSelected, clearNodeSelection] = useLexicalNodeSelection.useLexicalNodeSelection(nodeKey);
1732
1858
  const [isHovered, setIsHovered] = React9__namespace.useState(false);
1733
1859
  const [isResizing, setIsResizing] = React9__namespace.useState(false);
1860
+ const [isPlaying, setIsPlaying] = React9__namespace.useState(false);
1861
+ React9__namespace.useEffect(() => {
1862
+ return utils.mergeRegister(
1863
+ editor.registerCommand(
1864
+ lexical.FORMAT_ELEMENT_COMMAND,
1865
+ (formatType) => {
1866
+ if (isNodeSelected) {
1867
+ editor.update(() => {
1868
+ const node = lexical.$getNodeByKey(nodeKey);
1869
+ if ($isYouTubeNode(node)) node.setFormat(formatType);
1870
+ });
1871
+ return true;
1872
+ }
1873
+ return false;
1874
+ },
1875
+ lexical.COMMAND_PRIORITY_LOW
1876
+ ),
1877
+ // Select this node on click. When the thumbnail is shown the <img> is
1878
+ // in the parent DOM so CLICK_COMMAND fires naturally. When the iframe is
1879
+ // live, clicks land in the iframe's browsing context — the node stays
1880
+ // selected via wrapperRef boundary detection on the wrapper div.
1881
+ editor.registerCommand(
1882
+ lexical.CLICK_COMMAND,
1883
+ (event) => {
1884
+ if (wrapperRef.current?.contains(event.target)) {
1885
+ if (!event.shiftKey) clearNodeSelection();
1886
+ setNodeSelected(true);
1887
+ return true;
1888
+ }
1889
+ return false;
1890
+ },
1891
+ lexical.COMMAND_PRIORITY_LOW
1892
+ )
1893
+ );
1894
+ }, [editor, isNodeSelected, nodeKey, clearNodeSelection, setNodeSelected]);
1734
1895
  const handleDelete = (e) => {
1735
1896
  e.preventDefault();
1736
1897
  e.stopPropagation();
@@ -1739,72 +1900,173 @@ function YouTubeComponent({
1739
1900
  node?.remove();
1740
1901
  });
1741
1902
  };
1903
+ const handleResizeStart = () => {
1904
+ isResizingRef.current = true;
1905
+ setIsResizing(true);
1906
+ if (iframeRef.current) iframeRef.current.style.pointerEvents = "none";
1907
+ };
1742
1908
  const handleResizeEnd = (w, h) => {
1909
+ isResizingRef.current = false;
1743
1910
  setIsResizing(false);
1911
+ if (iframeRef.current) iframeRef.current.style.pointerEvents = "";
1912
+ setIsHovered(true);
1744
1913
  editor.update(() => {
1745
1914
  const node = lexical.$getNodeByKey(nodeKey);
1746
- if ($isYouTubeNode(node)) {
1747
- node.setSize(Math.round(w), Math.round(h));
1748
- }
1915
+ if ($isYouTubeNode(node)) node.setSize(Math.round(w), Math.round(h));
1749
1916
  });
1750
1917
  };
1751
- return /* @__PURE__ */ jsxRuntime.jsx(LexicalBlockWithAlignableContents.BlockWithAlignableContents, { className, format, nodeKey, children: /* @__PURE__ */ jsxRuntime.jsxs(
1918
+ const actionBtnStyle = (side) => ({
1919
+ position: "absolute",
1920
+ top: 8,
1921
+ [side]: 8,
1922
+ width: 28,
1923
+ height: 28,
1924
+ borderRadius: "50%",
1925
+ background: "rgba(0,0,0,0.65)",
1926
+ color: "#fff",
1927
+ border: "none",
1928
+ cursor: "pointer",
1929
+ fontSize: side === "right" ? 18 : 14,
1930
+ lineHeight: 1,
1931
+ padding: 0,
1932
+ zIndex: 10,
1933
+ display: "flex",
1934
+ alignItems: "center",
1935
+ justifyContent: "center"
1936
+ });
1937
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: wrapperRef, style: { display: "block", textAlign: format || void 0 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1752
1938
  "div",
1753
1939
  {
1754
1940
  ref: containerRef,
1755
- style: { position: "relative", display: "inline-block", width, height, lineHeight: 0 },
1941
+ style: {
1942
+ position: "relative",
1943
+ display: "inline-block",
1944
+ width,
1945
+ height,
1946
+ lineHeight: 0,
1947
+ outline: isNodeSelected ? "2px solid #0078d4" : void 0,
1948
+ outlineOffset: 2
1949
+ },
1756
1950
  onMouseEnter: () => setIsHovered(true),
1757
1951
  onMouseLeave: () => {
1758
- if (!isResizing) setIsHovered(false);
1952
+ if (!isResizingRef.current) setIsHovered(false);
1759
1953
  },
1760
1954
  children: [
1761
- /* @__PURE__ */ jsxRuntime.jsx(
1762
- "iframe",
1763
- {
1764
- width: "100%",
1765
- height: "100%",
1766
- src: `https://www.youtube.com/embed/${videoID}`,
1767
- allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
1768
- allowFullScreen: true,
1769
- title: "YouTube video",
1770
- style: { display: "block", border: "none", pointerEvents: isResizing ? "none" : "auto" }
1771
- }
1772
- ),
1773
- isHovered && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1774
- /* @__PURE__ */ jsxRuntime.jsx(
1775
- "button",
1776
- {
1777
- type: "button",
1778
- onClick: handleDelete,
1779
- title: "Remove video",
1780
- style: {
1781
- position: "absolute",
1782
- top: 8,
1783
- right: 8,
1784
- width: 28,
1785
- height: 28,
1786
- borderRadius: "50%",
1787
- background: "rgba(0,0,0,0.65)",
1788
- color: "#fff",
1789
- border: "none",
1790
- cursor: "pointer",
1791
- fontSize: 18,
1792
- lineHeight: 1,
1793
- padding: 0,
1794
- zIndex: 10,
1795
- display: "flex",
1796
- alignItems: "center",
1797
- justifyContent: "center"
1798
- },
1799
- children: "\xD7"
1800
- }
1801
- ),
1955
+ isPlaying ? (
1956
+ /* ── Playing state ─────────────────────────────────────────────────
1957
+ * Show the real YouTube iframe in-place at the same dimensions.
1958
+ * autoplay=1 starts playback immediately.
1959
+ * pointer-events are set to 'none' during resize (handleResizeStart)
1960
+ * so drag events are not lost to the iframe's browsing context. */
1802
1961
  /* @__PURE__ */ jsxRuntime.jsx(
1803
- VideoResizer,
1962
+ "iframe",
1804
1963
  {
1805
- containerRef,
1806
- onResizeStart: () => setIsResizing(true),
1807
- onResizeEnd: handleResizeEnd
1964
+ ref: iframeRef,
1965
+ width: "100%",
1966
+ height: "100%",
1967
+ src: `https://www.youtube.com/embed/${videoID}?autoplay=1`,
1968
+ sandbox: "allow-same-origin allow-scripts allow-popups allow-presentation",
1969
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
1970
+ allowFullScreen: true,
1971
+ title: "YouTube video",
1972
+ style: { display: "block", border: "none" }
1973
+ }
1974
+ )
1975
+ ) : (
1976
+ /* ── Thumbnail state ───────────────────────────────────────────────
1977
+ * Static <img> keeps all clicks in the parent DOM so Lexical's
1978
+ * CLICK_COMMAND fires correctly and the node can be selected.
1979
+ * Clicking the red ▶ badge switches to playing state. */
1980
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1981
+ /* @__PURE__ */ jsxRuntime.jsx(
1982
+ "img",
1983
+ {
1984
+ src: `https://img.youtube.com/vi/${videoID}/hqdefault.jpg`,
1985
+ alt: "YouTube video",
1986
+ draggable: false,
1987
+ style: {
1988
+ width: "100%",
1989
+ height: "100%",
1990
+ objectFit: "cover",
1991
+ display: "block",
1992
+ userSelect: "none",
1993
+ cursor: "pointer"
1994
+ }
1995
+ }
1996
+ ),
1997
+ /* @__PURE__ */ jsxRuntime.jsx(
1998
+ "div",
1999
+ {
2000
+ role: "button",
2001
+ "aria-label": "Play video",
2002
+ onClick: () => setIsPlaying(true),
2003
+ style: {
2004
+ position: "absolute",
2005
+ top: "50%",
2006
+ left: "50%",
2007
+ transform: "translate(-50%, -50%)",
2008
+ width: 56,
2009
+ height: 56,
2010
+ background: "rgba(255, 0, 0, 0.85)",
2011
+ borderRadius: "50%",
2012
+ display: "flex",
2013
+ alignItems: "center",
2014
+ justifyContent: "center",
2015
+ cursor: "pointer"
2016
+ },
2017
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#fff", fontSize: 20, lineHeight: 1, marginLeft: 5 }, children: "\u25B6" })
2018
+ }
2019
+ )
2020
+ ] })
2021
+ ),
2022
+ (isHovered || isResizing) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2023
+ /* @__PURE__ */ jsxRuntime.jsx(
2024
+ "button",
2025
+ {
2026
+ type: "button",
2027
+ onClick: handleDelete,
2028
+ title: "Remove video",
2029
+ style: actionBtnStyle("right"),
2030
+ children: "\xD7"
2031
+ }
2032
+ ),
2033
+ isPlaying && /* @__PURE__ */ jsxRuntime.jsx(
2034
+ "button",
2035
+ {
2036
+ type: "button",
2037
+ onClick: (e) => {
2038
+ e.stopPropagation();
2039
+ setIsPlaying(false);
2040
+ },
2041
+ title: "Stop video",
2042
+ style: actionBtnStyle("left"),
2043
+ children: "\u23F9"
2044
+ }
2045
+ ),
2046
+ /* @__PURE__ */ jsxRuntime.jsx(
2047
+ "button",
2048
+ {
2049
+ type: "button",
2050
+ onClick: (e) => {
2051
+ e.stopPropagation();
2052
+ window.open(`https://www.youtube.com/watch?v=${videoID}`, "_blank", "noopener,noreferrer");
2053
+ },
2054
+ title: "Open in browser",
2055
+ style: {
2056
+ ...actionBtnStyle("left"),
2057
+ top: isPlaying ? 44 : 8,
2058
+ // stack below stop button when playing
2059
+ fontSize: 13
2060
+ },
2061
+ children: "\u2197"
2062
+ }
2063
+ ),
2064
+ /* @__PURE__ */ jsxRuntime.jsx(
2065
+ VideoResizer,
2066
+ {
2067
+ containerRef,
2068
+ onResizeStart: handleResizeStart,
2069
+ onResizeEnd: handleResizeEnd
1808
2070
  }
1809
2071
  )
1810
2072
  ] })
@@ -1864,6 +2126,7 @@ var YouTubeNode = class _YouTubeNode extends LexicalDecoratorBlockNode.Decorator
1864
2126
  iframe.setAttribute("width", String(this.__width));
1865
2127
  iframe.setAttribute("height", String(this.__height));
1866
2128
  iframe.style.border = "none";
2129
+ iframe.setAttribute("sandbox", "allow-same-origin allow-scripts allow-popups allow-presentation");
1867
2130
  iframe.setAttribute(
1868
2131
  "allow",
1869
2132
  "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
@@ -1891,16 +2154,10 @@ var YouTubeNode = class _YouTubeNode extends LexicalDecoratorBlockNode.Decorator
1891
2154
  writable.__width = width;
1892
2155
  writable.__height = height;
1893
2156
  }
1894
- decorate(_editor, config) {
1895
- const embedBlockTheme = config.theme.embedBlock || {};
1896
- const className = {
1897
- base: embedBlockTheme.base || "",
1898
- focus: embedBlockTheme.focus || ""
1899
- };
2157
+ decorate(_editor, _config) {
1900
2158
  return /* @__PURE__ */ jsxRuntime.jsx(
1901
2159
  YouTubeComponent,
1902
2160
  {
1903
- className,
1904
2161
  format: this.__format,
1905
2162
  nodeKey: this.getKey(),
1906
2163
  videoID: this.__id,
@@ -2266,12 +2523,12 @@ function useFloatingPortalContainer(editor) {
2266
2523
  }, [editor]);
2267
2524
  return container;
2268
2525
  }
2269
- function setPopupPositionFixed(popupEl, rect) {
2526
+ function setPopupPositionFixed(popupEl, rect, topBoundary) {
2270
2527
  const GAP = 8;
2271
2528
  const MARGIN = 8;
2272
2529
  let top = rect.top - popupEl.offsetHeight - GAP;
2273
- let left = rect.left + rect.width - popupEl.offsetWidth;
2274
- if (top < MARGIN) top = rect.bottom + GAP;
2530
+ let left = rect.left;
2531
+ if (top < topBoundary) top = rect.bottom + GAP;
2275
2532
  left = clamp2(left, MARGIN, window.innerWidth - popupEl.offsetWidth - MARGIN);
2276
2533
  popupEl.style.top = `${top}px`;
2277
2534
  popupEl.style.left = `${left}px`;
@@ -2313,10 +2570,22 @@ function FloatingCharacterStylesEditor({
2313
2570
  popupEl.classList.remove("is-open");
2314
2571
  return;
2315
2572
  }
2316
- const range = sel.getRangeAt(0);
2317
- const rect = range.getBoundingClientRect();
2573
+ let rect;
2574
+ try {
2575
+ const focusRange = document.createRange();
2576
+ focusRange.setStart(sel.focusNode, sel.focusOffset);
2577
+ focusRange.setEnd(sel.focusNode, sel.focusOffset);
2578
+ rect = focusRange.getBoundingClientRect();
2579
+ if (rect.width === 0 && rect.height === 0 && rect.top === 0 && rect.left === 0) {
2580
+ throw new Error("empty focus rect");
2581
+ }
2582
+ } catch {
2583
+ rect = sel.getRangeAt(0).getBoundingClientRect();
2584
+ }
2585
+ const toolbarEl = root?.closest(".lexical-rich-editor-root")?.querySelector(".editor-toolbar-root");
2586
+ const topBoundary = toolbarEl ? toolbarEl.getBoundingClientRect().bottom + 8 : 8;
2318
2587
  if (!mouseDownRef.current) {
2319
- setPopupPositionFixed(popupEl, rect);
2588
+ setPopupPositionFixed(popupEl, rect, topBoundary);
2320
2589
  }
2321
2590
  popupEl.classList.add("is-open");
2322
2591
  }, [editor]);
@@ -2524,6 +2793,11 @@ function useCharacterStylesPopup(editor, opts) {
2524
2793
  setIsText(false);
2525
2794
  return;
2526
2795
  }
2796
+ const activeElement = document.activeElement;
2797
+ if (activeElement && activeElement !== document.body && rootElement && !rootElement.contains(activeElement)) {
2798
+ setIsText(false);
2799
+ return;
2800
+ }
2527
2801
  if (!lexical.$isRangeSelection(selection)) return;
2528
2802
  const node = getSelectedNode(selection);
2529
2803
  setIsBold(selection.hasFormat("bold"));
@@ -2544,7 +2818,11 @@ function useCharacterStylesPopup(editor, opts) {
2544
2818
  }, [editor]);
2545
2819
  React9.useEffect(() => {
2546
2820
  document.addEventListener("selectionchange", updatePopupState);
2547
- return () => document.removeEventListener("selectionchange", updatePopupState);
2821
+ document.addEventListener("focusin", updatePopupState);
2822
+ return () => {
2823
+ document.removeEventListener("selectionchange", updatePopupState);
2824
+ document.removeEventListener("focusin", updatePopupState);
2825
+ };
2548
2826
  }, [updatePopupState]);
2549
2827
  React9.useEffect(() => editor.registerUpdateListener(updatePopupState), [editor, updatePopupState]);
2550
2828
  if (!portalContainer || !isText || isLink) return null;
@@ -2802,34 +3080,57 @@ function normalizeToBlockHtml(html) {
2802
3080
  if (pendingP) body.appendChild(pendingP);
2803
3081
  return body.innerHTML;
2804
3082
  }
3083
+ function splitHeadingsAtBrSequences(html) {
3084
+ const doc = new DOMParser().parseFromString(html, "text/html");
3085
+ const headings = Array.from(doc.querySelectorAll("h1, h2, h3, h4, h5, h6"));
3086
+ headings.forEach((el) => {
3087
+ const inner = el.innerHTML;
3088
+ const SEP = /<br\s*\/?>\s*(?:<br\s*\/?>)+/gi;
3089
+ if (!SEP.test(inner)) return;
3090
+ SEP.lastIndex = 0;
3091
+ const parts = inner.split(SEP).map((p) => p.trim()).filter(Boolean);
3092
+ if (parts.length <= 1) return;
3093
+ const parent = el.parentNode;
3094
+ if (!parent) return;
3095
+ const tagName = el.tagName.toLowerCase();
3096
+ const attrs = Array.from(el.attributes);
3097
+ const fragment = doc.createDocumentFragment();
3098
+ parts.forEach((part) => {
3099
+ const newEl = doc.createElement(tagName);
3100
+ attrs.forEach((a) => newEl.setAttribute(a.name, a.value));
3101
+ newEl.innerHTML = part;
3102
+ fragment.appendChild(newEl);
3103
+ });
3104
+ parent.replaceChild(fragment, el);
3105
+ });
3106
+ return doc.body.innerHTML;
3107
+ }
2805
3108
  var CustomOnChangePlugin = ({ value, onChange }) => {
2806
3109
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
2807
3110
  const initializedRef = React9.useRef(false);
2808
- const onChangeRef = React9.useRef(onChange);
2809
- onChangeRef.current = onChange;
2810
3111
  React9.useEffect(() => {
2811
3112
  if (!value || initializedRef.current) return;
2812
3113
  initializedRef.current = true;
2813
3114
  editor.update(() => {
2814
3115
  const root = lexical.$getRoot();
2815
3116
  root.clear();
3117
+ const safe = sanitizeHtml(value);
3118
+ const cleaned = normalizeToBlockHtml(splitHeadingsAtBrSequences(safe));
2816
3119
  const parser = new DOMParser();
2817
- const dom = parser.parseFromString(value, "text/html");
3120
+ const dom = parser.parseFromString(cleaned, "text/html");
2818
3121
  const nodes = html.$generateNodesFromDOM(editor, dom);
2819
3122
  root.append(...nodes);
2820
- lexical.$setSelection(null);
2821
3123
  });
2822
3124
  }, [editor, value]);
2823
- const handleChange = React9.useCallback((editorState) => {
2824
- editorState.read(() => {
2825
- onChangeRef.current(postProcessOutput(html.$generateHtmlFromNodes(editor)));
2826
- });
2827
- }, [editor]);
2828
3125
  return /* @__PURE__ */ jsxRuntime.jsx(
2829
3126
  LexicalOnChangePlugin.OnChangePlugin,
2830
3127
  {
2831
- onChange: handleChange,
2832
- ignoreSelectionChange: true
3128
+ onChange: (editorState) => {
3129
+ editorState.read(() => {
3130
+ const raw = html.$generateHtmlFromNodes(editor);
3131
+ onChange(postProcessOutput(splitHeadingsAtBrSequences(raw)));
3132
+ });
3133
+ }
2833
3134
  }
2834
3135
  );
2835
3136
  };
@@ -2930,7 +3231,7 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
2930
3231
  setFloatingElemPositionForLinkEditor(domRect, editorElem, anchorElem);
2931
3232
  }
2932
3233
  setLastSelection(selection);
2933
- } else if (!activeElement || activeElement.className !== "link-input") {
3234
+ } else if (!activeElement || activeElement.className !== "aoLinkInput") {
2934
3235
  if (rootElement !== null) {
2935
3236
  setFloatingElemPositionForLinkEditor(null, editorElem, anchorElem);
2936
3237
  }
@@ -2939,7 +3240,7 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
2939
3240
  setLinkUrl("");
2940
3241
  }
2941
3242
  return true;
2942
- }, [anchorElem, editor, isLink, setIsLinkEditMode, isLinkEditMode, linkUrl]);
3243
+ }, [anchorElem, editor, setIsLinkEditMode, isLinkEditMode, isLink, linkUrl]);
2943
3244
  React9.useEffect(() => {
2944
3245
  const scrollerElem = anchorElem.parentElement;
2945
3246
  const update = () => {
@@ -3031,13 +3332,12 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
3031
3332
  setIsLinkEditMode(false);
3032
3333
  }
3033
3334
  };
3034
- return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: editorRef, className: "link-editor", children: !isLink ? null : isLinkEditMode ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3335
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: editorRef, className: "aoLinkEditor", children: !isLink ? null : isLinkEditMode ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3035
3336
  /* @__PURE__ */ jsxRuntime.jsx(
3036
3337
  "input",
3037
3338
  {
3038
3339
  ref: inputRef,
3039
- className: "link-input",
3040
- placeholder: "https://",
3340
+ className: "aoLinkInput",
3041
3341
  value: editedLinkUrl,
3042
3342
  onChange: (event) => {
3043
3343
  setEditedLinkUrl(event.target.value);
@@ -3047,35 +3347,37 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
3047
3347
  }
3048
3348
  }
3049
3349
  ),
3050
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "link-actions", children: [
3350
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aoLinkInputActions", children: [
3051
3351
  /* @__PURE__ */ jsxRuntime.jsx(
3052
- "button",
3352
+ "div",
3053
3353
  {
3054
- type: "button",
3055
- className: "link-icon-btn link-cancel",
3354
+ className: "aoLinkCancel",
3355
+ role: "button",
3356
+ tabIndex: 0,
3056
3357
  title: "Cancel",
3057
3358
  "aria-label": "Cancel",
3058
3359
  onMouseDown: preventDefault,
3059
3360
  onClick: () => {
3060
3361
  setIsLinkEditMode(false);
3061
3362
  },
3062
- children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.Dismiss16Regular, {})
3363
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.DismissRegular, { fontSize: 16 })
3063
3364
  }
3064
3365
  ),
3065
3366
  /* @__PURE__ */ jsxRuntime.jsx(
3066
- "button",
3367
+ "div",
3067
3368
  {
3068
- type: "button",
3069
- className: "link-icon-btn link-confirm",
3369
+ className: "aoLinkConfirm",
3370
+ role: "button",
3371
+ tabIndex: 0,
3070
3372
  title: "Confirm",
3071
3373
  "aria-label": "Confirm",
3072
3374
  onMouseDown: preventDefault,
3073
3375
  onClick: handleLinkSubmission,
3074
- children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.Checkmark16Regular, {})
3376
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.CheckmarkRegular, { fontSize: 16 })
3075
3377
  }
3076
3378
  )
3077
3379
  ] })
3078
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "link-view", children: [
3380
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aoLinkView", children: [
3079
3381
  /* @__PURE__ */ jsxRuntime.jsx(
3080
3382
  "a",
3081
3383
  {
@@ -3085,38 +3387,38 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
3085
3387
  children: linkUrl
3086
3388
  }
3087
3389
  ),
3088
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "link-actions", children: [
3089
- /* @__PURE__ */ jsxRuntime.jsx(
3090
- "button",
3091
- {
3092
- type: "button",
3093
- className: "link-icon-btn link-edit",
3094
- title: "Edit link",
3095
- "aria-label": "Edit link",
3096
- onMouseDown: preventDefault,
3097
- onClick: (event) => {
3098
- event.preventDefault();
3099
- setEditedLinkUrl(linkUrl);
3100
- setIsLinkEditMode(true);
3101
- },
3102
- children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.Edit16Regular, {})
3103
- }
3104
- ),
3105
- /* @__PURE__ */ jsxRuntime.jsx(
3106
- "button",
3107
- {
3108
- type: "button",
3109
- className: "link-icon-btn link-trash",
3110
- title: "Remove link",
3111
- "aria-label": "Remove link",
3112
- onMouseDown: preventDefault,
3113
- onClick: () => {
3114
- editor.dispatchCommand(link.TOGGLE_LINK_COMMAND, null);
3115
- },
3116
- children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.Delete16Regular, {})
3117
- }
3118
- )
3119
- ] })
3390
+ /* @__PURE__ */ jsxRuntime.jsx(
3391
+ "div",
3392
+ {
3393
+ className: "aoLinkEdit",
3394
+ role: "button",
3395
+ tabIndex: 0,
3396
+ title: "Edit link",
3397
+ "aria-label": "Edit link",
3398
+ onMouseDown: preventDefault,
3399
+ onClick: (event) => {
3400
+ event.preventDefault();
3401
+ setEditedLinkUrl(linkUrl);
3402
+ setIsLinkEditMode(true);
3403
+ },
3404
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.EditRegular, { fontSize: 16 })
3405
+ }
3406
+ ),
3407
+ /* @__PURE__ */ jsxRuntime.jsx(
3408
+ "div",
3409
+ {
3410
+ className: "aoLinkTrash",
3411
+ role: "button",
3412
+ tabIndex: 0,
3413
+ title: "Remove link",
3414
+ "aria-label": "Remove link",
3415
+ onMouseDown: preventDefault,
3416
+ onClick: () => {
3417
+ editor.dispatchCommand(link.TOGGLE_LINK_COMMAND, null);
3418
+ },
3419
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.DeleteRegular, { fontSize: 16 })
3420
+ }
3421
+ )
3120
3422
  ] }) });
3121
3423
  };
3122
3424
  var useFloatingLinkEditorToolbar = (editor, anchorElem, isLinkEditMode, setIsLinkEditMode) => {
@@ -3197,8 +3499,6 @@ var FloatingLinkEditorPlugin = ({ anchorElem, isLinkEditMode, setIsLinkEditMode
3197
3499
  setIsLinkEditMode
3198
3500
  );
3199
3501
  };
3200
-
3201
- // src/Plugins/ImagePlugin.tsx
3202
3502
  init_ImageNode();
3203
3503
  var INSERT_IMAGE_COMMAND = lexical.createCommand("INSERT_IMAGE_COMMAND");
3204
3504
  var readClipboardImageAsDataURL = async (event) => {
@@ -3217,28 +3517,77 @@ var readClipboardImageAsDataURL = async (event) => {
3217
3517
  }
3218
3518
  return null;
3219
3519
  };
3520
+ var InsertImageByURL = ({
3521
+ setIsOpen,
3522
+ onClick,
3523
+ disabled
3524
+ }) => {
3525
+ const [altText, setAltText] = React9.useState("");
3526
+ const [src, setSrc] = React9.useState("");
3527
+ const isDisabled = disabled || src === "";
3528
+ return /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 6, padding: "10px 0px 0px 0px" }, children: [
3529
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Enter URL", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3530
+ reactComponents.Input,
3531
+ {
3532
+ autoFocus: !disabled,
3533
+ appearance: "underline",
3534
+ placeholder: "Add URL",
3535
+ disabled,
3536
+ onChange: (_, v) => setSrc(v.value),
3537
+ value: src
3538
+ }
3539
+ ) }),
3540
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Alt Text", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3541
+ reactComponents.Input,
3542
+ {
3543
+ placeholder: "Alt text",
3544
+ disabled,
3545
+ onChange: (_, v) => setAltText(v.value),
3546
+ value: altText
3547
+ },
3548
+ "alt-text-url"
3549
+ ) }),
3550
+ /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
3551
+ /* @__PURE__ */ jsxRuntime.jsx(
3552
+ reactComponents.Button,
3553
+ {
3554
+ style: { width: "150px" },
3555
+ onClick: () => !disabled && onClick({ altText, src }),
3556
+ disabled: isDisabled,
3557
+ size: "small",
3558
+ children: "Confirm"
3559
+ },
3560
+ "url-confirm-btn"
3561
+ ),
3562
+ /* @__PURE__ */ jsxRuntime.jsx(
3563
+ reactComponents.Button,
3564
+ {
3565
+ style: { width: "150px" },
3566
+ onClick: () => setIsOpen(false),
3567
+ disabled,
3568
+ size: "small",
3569
+ children: "Cancel"
3570
+ },
3571
+ "file-url-cancel"
3572
+ )
3573
+ ] })
3574
+ ] });
3575
+ };
3220
3576
  var InsertImageDialog = ({
3221
3577
  activeEditor,
3222
3578
  disabled,
3223
- open: externalOpen,
3224
- onClose
3579
+ maxImageSizeMB,
3580
+ validationMessages
3225
3581
  }) => {
3226
3582
  const [src, setSrc] = React9.useState("");
3227
3583
  const [altText, setAltText] = React9.useState("");
3228
- const [internalOpen, setInternalOpen] = React9.useState(false);
3584
+ const [isOpen, setIsOpen] = React9.useState(false);
3585
+ const [selectedValue, setSelectedValue] = React9.useState("Upload");
3229
3586
  const [fileName, setFileName] = React9.useState("");
3587
+ const [fileSizeError, setFileSizeError] = React9.useState(null);
3230
3588
  const hasModifier = React9.useRef(false);
3231
3589
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
3232
- const isControlled = externalOpen !== void 0;
3233
- const isOpen = isControlled ? !!externalOpen && !disabled : internalOpen && !disabled;
3234
- const isAddDisabled = disabled || src === "";
3235
- const handleClose = () => {
3236
- setSrc("");
3237
- setAltText("");
3238
- setFileName("");
3239
- if (isControlled) onClose?.();
3240
- else setInternalOpen(false);
3241
- };
3590
+ const isDisabled = disabled || src === "" || !!fileSizeError;
3242
3591
  React9.useEffect(() => {
3243
3592
  hasModifier.current = false;
3244
3593
  const handler = (e) => {
@@ -3250,57 +3599,75 @@ var InsertImageDialog = ({
3250
3599
  const onClick = (payload) => {
3251
3600
  if (disabled) return;
3252
3601
  activeEditor.dispatchCommand(INSERT_IMAGE_COMMAND, payload);
3253
- handleClose();
3602
+ setIsOpen(false);
3603
+ setAltText("");
3604
+ setSrc("");
3605
+ setFileName("");
3606
+ setFileSizeError(null);
3254
3607
  };
3255
3608
  const loadImage = (event) => {
3256
3609
  if (disabled) return;
3257
3610
  const files = event.target.files;
3258
3611
  if (!files || files.length === 0) return;
3612
+ const file = files[0];
3613
+ if (maxImageSizeMB !== void 0) {
3614
+ const fileMB = file.size / (1024 * 1024);
3615
+ if (fileMB > maxImageSizeMB) {
3616
+ const override = validationMessages?.imageTooLarge;
3617
+ const msg = override !== void 0 ? typeof override === "function" ? override(fileMB, maxImageSizeMB) : override : DEFAULT_VALIDATION_MESSAGES.imageTooLarge(fileMB, maxImageSizeMB);
3618
+ setFileSizeError(msg);
3619
+ setSrc("");
3620
+ setFileName("");
3621
+ event.target.value = "";
3622
+ return;
3623
+ }
3624
+ }
3625
+ setFileSizeError(null);
3259
3626
  const reader = new FileReader();
3260
3627
  reader.onload = () => {
3261
3628
  if (typeof reader.result === "string") {
3262
3629
  setSrc(reader.result);
3263
- setFileName(files[0].name);
3630
+ setFileName(file.name);
3264
3631
  }
3265
3632
  };
3266
- reader.readAsDataURL(files[0]);
3633
+ reader.readAsDataURL(file);
3267
3634
  };
3268
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3269
- !isControlled && /* @__PURE__ */ jsxRuntime.jsx(
3270
- reactComponents.Button,
3271
- {
3272
- size: "small",
3273
- title: "Add Image",
3274
- disabled,
3275
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ImageAddRegular, { style: { color: iconColor } }),
3276
- style: {
3277
- background: isOpen && !disabled ? "#ebebeb" : "none",
3278
- border: "none",
3279
- margin: 2,
3280
- opacity: disabled ? 0.55 : 1,
3281
- cursor: disabled ? "not-allowed" : "pointer"
3282
- },
3283
- onClick: () => {
3284
- if (disabled) return;
3285
- setSrc("");
3286
- setAltText("");
3287
- setFileName("");
3288
- setInternalOpen(true);
3289
- }
3635
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3636
+ reactComponents.Dialog,
3637
+ {
3638
+ open: disabled ? false : isOpen,
3639
+ onOpenChange: (_, data) => {
3640
+ if (!disabled) setIsOpen(data.open);
3290
3641
  },
3291
- "upload-image"
3292
- ),
3293
- /* @__PURE__ */ jsxRuntime.jsx(
3294
- reactComponents.Dialog,
3295
- {
3296
- open: isOpen,
3297
- onOpenChange: (_, data) => {
3298
- if (!data.open) handleClose();
3299
- },
3300
- children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogSurface, { style: { maxWidth: "400px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogBody, { children: [
3301
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTitle, { children: "Insert Image" }),
3302
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 10 }, style: { paddingTop: 8 }, children: [
3303
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Upload", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs(
3642
+ children: [
3643
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3644
+ reactComponents.Button,
3645
+ {
3646
+ size: "small",
3647
+ title: "Add Image",
3648
+ disabled,
3649
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ImageAddRegular, { style: { color: iconColor } }),
3650
+ style: {
3651
+ background: isOpen && !disabled ? "#ebebeb" : "none",
3652
+ border: "none",
3653
+ margin: 2,
3654
+ opacity: disabled ? 0.55 : 1,
3655
+ cursor: disabled ? "not-allowed" : "pointer"
3656
+ },
3657
+ onClick: () => {
3658
+ if (disabled) return;
3659
+ setIsOpen((prev) => !prev);
3660
+ setSrc("");
3661
+ setAltText("");
3662
+ setFileName("");
3663
+ }
3664
+ },
3665
+ "upload-image"
3666
+ ) }),
3667
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogSurface, { style: { maxWidth: 340 }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogBody, { children: [
3668
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTitle, { children: "Insert image" }),
3669
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 8 }, children: [
3670
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Upload", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs(
3304
3671
  "label",
3305
3672
  {
3306
3673
  style: {
@@ -3339,7 +3706,8 @@ var InsertImageDialog = ({
3339
3706
  ]
3340
3707
  }
3341
3708
  ) }),
3342
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Alt Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3709
+ fileSizeError && /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MessageBar, { intent: "error", style: { marginTop: 4 }, children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MessageBarBody, { children: fileSizeError }) }),
3710
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Alt Text", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3343
3711
  reactComponents.Input,
3344
3712
  {
3345
3713
  placeholder: "Alt text",
@@ -3348,25 +3716,24 @@ var InsertImageDialog = ({
3348
3716
  onChange: (_, d) => setAltText(d.value),
3349
3717
  value: altText
3350
3718
  }
3351
- ) })
3352
- ] }) }),
3353
- /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogActions, { children: [
3354
- /* @__PURE__ */ jsxRuntime.jsx(
3355
- reactComponents.Button,
3719
+ ) }),
3720
+ selectedValue === "URL" && /* @__PURE__ */ jsxRuntime.jsx(
3721
+ InsertImageByURL,
3356
3722
  {
3357
- appearance: "primary",
3358
- size: "small",
3359
- disabled: isAddDisabled,
3360
- onClick: () => onClick({ altText, src }),
3361
- children: "Add"
3723
+ disabled,
3724
+ setIsOpen: (open) => setIsOpen(open),
3725
+ onClick: (payload) => onClick(payload)
3362
3726
  }
3363
- ),
3364
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: handleClose, children: "Cancel" })
3727
+ )
3728
+ ] }) }),
3729
+ /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogActions, { children: [
3730
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled: isDisabled, onClick: () => onClick({ altText, src }), children: "Add" }),
3731
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
3365
3732
  ] })
3366
3733
  ] }) })
3367
- }
3368
- )
3369
- ] });
3734
+ ]
3735
+ }
3736
+ );
3370
3737
  };
3371
3738
  var ImagesPlugin = ({ captionsEnabled }) => {
3372
3739
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
@@ -3379,9 +3746,21 @@ var ImagesPlugin = ({ captionsEnabled }) => {
3379
3746
  INSERT_IMAGE_COMMAND,
3380
3747
  (payload) => {
3381
3748
  const imageNode = $createImageNode(payload);
3382
- lexical.$insertNodes([imageNode]);
3383
- if (lexical.$isRootOrShadowRoot(imageNode.getParentOrThrow())) {
3384
- utils.$wrapNodeInElement(imageNode, lexical.$createParagraphNode).selectEnd();
3749
+ const selection = lexical.$getSelection();
3750
+ if (lexical.$isRangeSelection(selection)) {
3751
+ const anchorNode = selection.anchor.getNode();
3752
+ const topLevel = anchorNode.getTopLevelElementOrThrow();
3753
+ const imageParagraph = lexical.$createParagraphNode();
3754
+ imageParagraph.append(imageNode);
3755
+ topLevel.insertAfter(imageParagraph);
3756
+ const tail = lexical.$createParagraphNode();
3757
+ imageParagraph.insertAfter(tail);
3758
+ tail.select();
3759
+ } else {
3760
+ lexical.$insertNodes([imageNode]);
3761
+ if (lexical.$isRootOrShadowRoot(imageNode.getParentOrThrow())) {
3762
+ utils.$wrapNodeInElement(imageNode, lexical.$createParagraphNode).selectEnd();
3763
+ }
3385
3764
  }
3386
3765
  return true;
3387
3766
  },
@@ -3432,12 +3811,25 @@ var TRANSPARENT_IMAGE = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAA
3432
3811
  var img = document.createElement("img");
3433
3812
  img.src = TRANSPARENT_IMAGE;
3434
3813
  var $onDragStart = (event) => {
3435
- const node = $getImageNodeInSelection();
3814
+ let node = $getImageNodeInSelection();
3815
+ if (!node) {
3816
+ const target = event.target;
3817
+ if (!target) return false;
3818
+ const lexicalNode = lexical.$getNearestNodeFromDOMNode(target);
3819
+ if ($isImageNode(lexicalNode)) {
3820
+ node = lexicalNode;
3821
+ }
3822
+ }
3436
3823
  if (!node) return false;
3437
3824
  const dataTransfer = event.dataTransfer;
3438
3825
  if (!dataTransfer) return false;
3826
+ const imgEl = event.target?.closest?.("span.editor-image")?.querySelector?.("img") ?? event.target;
3827
+ if (imgEl instanceof HTMLElement) {
3828
+ dataTransfer.setDragImage(imgEl, 20, 20);
3829
+ } else {
3830
+ dataTransfer.setDragImage(img, 0, 0);
3831
+ }
3439
3832
  dataTransfer.setData("text/plain", "_");
3440
- dataTransfer.setDragImage(img, 0, 0);
3441
3833
  dataTransfer.setData(
3442
3834
  "application/x-lexical-drag",
3443
3835
  JSON.stringify({
@@ -3465,20 +3857,23 @@ var $onDragover = (event) => {
3465
3857
  return true;
3466
3858
  };
3467
3859
  var $onDrop = (event, editor) => {
3468
- const node = $getImageNodeInSelection();
3469
- if (!node) return false;
3470
3860
  const data = getDragImageData(event);
3471
3861
  if (!data) return false;
3472
3862
  event.preventDefault();
3473
3863
  if (canDropImage(event)) {
3864
+ const sourceKey = data.key;
3865
+ if (sourceKey) {
3866
+ const sourceNode = lexical.$getNodeByKey(sourceKey);
3867
+ if (sourceNode) sourceNode.remove();
3868
+ }
3474
3869
  const range = getDragSelection(event);
3475
- node.remove();
3476
3870
  const rangeSelection = lexical.$createRangeSelection();
3477
3871
  if (range !== null && range !== void 0) {
3478
3872
  rangeSelection.applyDOMRange(range);
3479
3873
  }
3480
3874
  lexical.$setSelection(rangeSelection);
3481
- editor.dispatchCommand(INSERT_IMAGE_COMMAND, data);
3875
+ const { key: _key, ...insertPayload } = data;
3876
+ editor.dispatchCommand(INSERT_IMAGE_COMMAND, insertPayload);
3482
3877
  }
3483
3878
  return true;
3484
3879
  };
@@ -3498,7 +3893,7 @@ var getDragImageData = (event) => {
3498
3893
  };
3499
3894
  var canDropImage = (event) => {
3500
3895
  const target = event.target;
3501
- return !!(lexical.isHTMLElement(target) && !target.closest("code, span.editor-image") && lexical.isHTMLElement(target.parentElement) && target.parentElement.closest("div.ContentEditable__root"));
3896
+ return !!(lexical.isHTMLElement(target) && !target.closest("code, span.editor-image") && target.closest('[contenteditable="true"]'));
3502
3897
  };
3503
3898
  var getDragSelection = (event) => {
3504
3899
  let range;
@@ -3513,8 +3908,6 @@ var getDragSelection = (event) => {
3513
3908
  }
3514
3909
  return range;
3515
3910
  };
3516
-
3517
- // src/Plugins/InlineImage.tsx
3518
3911
  init_InlineImage();
3519
3912
  init_InlineImageNode();
3520
3913
  var INSERT_INLINE_IMAGE_COMMAND = lexical.createCommand(
@@ -3533,39 +3926,45 @@ var useStyles = reactComponents.makeStyles({
3533
3926
  var InsertInlineImageDialog = ({
3534
3927
  disabled,
3535
3928
  activeEditor,
3536
- open: externalOpen,
3537
- onClose
3929
+ maxImageSizeMB,
3930
+ validationMessages
3538
3931
  }) => {
3539
3932
  const hasModifier = React9.useRef(false);
3540
3933
  const [src, setSrc] = React9.useState("");
3541
- const [internalOpen, setInternalOpen] = React9.useState(false);
3934
+ const [isOpen, setIsOpen] = React9.useState(false);
3542
3935
  const [altText, setAltText] = React9.useState("");
3543
3936
  const [fileName, setFileName] = React9.useState("");
3544
3937
  const [position, setPosition] = React9.useState("left");
3938
+ const [fileSizeError, setFileSizeError] = React9.useState(null);
3545
3939
  const styles = useStyles();
3546
3940
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
3547
- const isControlled = externalOpen !== void 0;
3548
- const isOpen = isControlled ? !!externalOpen && !disabled : internalOpen && !disabled;
3549
- const isAddDisabled = disabled || src === "";
3550
- const handleClose = () => {
3551
- setSrc("");
3552
- setAltText("");
3553
- setFileName("");
3554
- if (isControlled) onClose?.();
3555
- else setInternalOpen(false);
3556
- };
3941
+ const isDisabled = disabled || src === "" || !!fileSizeError;
3557
3942
  const loadImage = (event) => {
3558
3943
  if (disabled) return;
3559
3944
  const files = event.target.files;
3560
3945
  if (!files || files.length === 0) return;
3946
+ const file = files[0];
3947
+ if (maxImageSizeMB !== void 0) {
3948
+ const fileMB = file.size / (1024 * 1024);
3949
+ if (fileMB > maxImageSizeMB) {
3950
+ const override = validationMessages?.imageTooLarge;
3951
+ const msg = override !== void 0 ? typeof override === "function" ? override(fileMB, maxImageSizeMB) : override : DEFAULT_VALIDATION_MESSAGES.imageTooLarge(fileMB, maxImageSizeMB);
3952
+ setFileSizeError(msg);
3953
+ setSrc("");
3954
+ setFileName("");
3955
+ event.target.value = "";
3956
+ return;
3957
+ }
3958
+ }
3959
+ setFileSizeError(null);
3561
3960
  const reader = new FileReader();
3562
3961
  reader.onload = () => {
3563
3962
  if (typeof reader.result === "string") {
3564
3963
  setSrc(reader.result);
3565
- setFileName(files[0].name);
3964
+ setFileName(file.name);
3566
3965
  }
3567
3966
  };
3568
- reader.readAsDataURL(files[0]);
3967
+ reader.readAsDataURL(file);
3569
3968
  };
3570
3969
  React9.useEffect(() => {
3571
3970
  const handler = (e) => {
@@ -3578,44 +3977,48 @@ var InsertInlineImageDialog = ({
3578
3977
  if (disabled) return;
3579
3978
  const payload = { altText, position, src };
3580
3979
  activeEditor.dispatchCommand(INSERT_INLINE_IMAGE_COMMAND, payload);
3581
- handleClose();
3980
+ setIsOpen(false);
3981
+ setAltText("");
3982
+ setSrc("");
3983
+ setFileName("");
3984
+ setFileSizeError(null);
3582
3985
  };
3583
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3584
- !isControlled && /* @__PURE__ */ jsxRuntime.jsx(
3585
- reactComponents.Button,
3586
- {
3587
- size: "small",
3588
- title: "Add Inline Image",
3589
- disabled,
3590
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ImageEditRegular, { style: { color: iconColor } }),
3591
- style: {
3592
- background: isOpen && !disabled ? "#ebebeb" : "none",
3593
- border: "none",
3594
- margin: 2,
3595
- opacity: disabled ? 0.55 : 1,
3596
- cursor: disabled ? "not-allowed" : "pointer"
3597
- },
3598
- onClick: () => {
3599
- if (disabled) return;
3600
- setSrc("");
3601
- setAltText("");
3602
- setFileName("");
3603
- setInternalOpen(true);
3604
- }
3986
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3987
+ reactComponents.Dialog,
3988
+ {
3989
+ open: disabled ? false : isOpen,
3990
+ onOpenChange: (_, data) => {
3991
+ if (!disabled) setIsOpen(data.open);
3605
3992
  },
3606
- "upload-inline-image"
3607
- ),
3608
- /* @__PURE__ */ jsxRuntime.jsx(
3609
- reactComponents.Dialog,
3610
- {
3611
- open: isOpen,
3612
- onOpenChange: (_, data) => {
3613
- if (!data.open) handleClose();
3614
- },
3615
- children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogSurface, { style: { maxWidth: "440px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogBody, { children: [
3616
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTitle, { children: "Insert Inline Image" }),
3617
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 10 }, style: { paddingTop: 8 }, children: [
3618
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Upload", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs(
3993
+ children: [
3994
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3995
+ reactComponents.Button,
3996
+ {
3997
+ size: "small",
3998
+ title: "Add Inline Image",
3999
+ disabled,
4000
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ImageEditRegular, { style: { color: iconColor } }),
4001
+ style: {
4002
+ background: isOpen && !disabled ? "#ebebeb" : "none",
4003
+ border: "none",
4004
+ margin: 2,
4005
+ opacity: disabled ? 0.55 : 1,
4006
+ cursor: disabled ? "not-allowed" : "pointer"
4007
+ },
4008
+ onClick: () => {
4009
+ if (disabled) return;
4010
+ setIsOpen((prev) => !prev);
4011
+ setAltText("");
4012
+ setSrc("");
4013
+ setFileName("");
4014
+ }
4015
+ },
4016
+ "upload-inline-image"
4017
+ ) }),
4018
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogSurface, { style: { maxWidth: 360 }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogBody, { children: [
4019
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTitle, { children: "Insert inline image" }),
4020
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 8 }, children: [
4021
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Upload", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs(
3619
4022
  "label",
3620
4023
  {
3621
4024
  style: {
@@ -3654,22 +4057,25 @@ var InsertInlineImageDialog = ({
3654
4057
  ]
3655
4058
  }
3656
4059
  ) }),
3657
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Position", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs(
4060
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Position", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs(
3658
4061
  reactComponents.Dropdown,
3659
4062
  {
3660
- placeholder: "Left Align",
3661
4063
  className: styles.alignDropdown,
3662
4064
  disabled,
4065
+ value: position === "full" ? "Full" : position === "right" ? "Right" : "Left",
4066
+ selectedOptions: [position ?? "left"],
3663
4067
  listbox: { style: { width: "120px" } },
3664
4068
  root: { style: { borderBottom: "1px solid black" } },
4069
+ onOptionSelect: (_, data) => setPosition(data.optionValue),
3665
4070
  children: [
3666
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { text: "full", onClick: () => setPosition("full"), children: "Full" }, "full"),
3667
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { text: "left", onClick: () => setPosition("left"), children: "Left" }, "left"),
3668
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { text: "right", onClick: () => setPosition("right"), children: "Right" }, "right")
4071
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { value: "left", children: "Left" }, "left"),
4072
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { value: "right", children: "Right" }, "right"),
4073
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { value: "full", children: "Full" }, "full")
3669
4074
  ]
3670
4075
  }
3671
4076
  ) }),
3672
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Alt Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
4077
+ fileSizeError && /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MessageBar, { intent: "error", style: { marginTop: 4 }, children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MessageBarBody, { children: fileSizeError }) }),
4078
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Alt Text", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3673
4079
  reactComponents.Input,
3674
4080
  {
3675
4081
  placeholder: "Alt text",
@@ -3684,9 +4090,8 @@ var InsertInlineImageDialog = ({
3684
4090
  /* @__PURE__ */ jsxRuntime.jsx(
3685
4091
  reactComponents.Button,
3686
4092
  {
3687
- appearance: "primary",
3688
4093
  size: "small",
3689
- disabled: isAddDisabled,
4094
+ disabled: isDisabled,
3690
4095
  onClick: handleOnClick,
3691
4096
  children: "Add"
3692
4097
  },
@@ -3697,16 +4102,16 @@ var InsertInlineImageDialog = ({
3697
4102
  {
3698
4103
  size: "small",
3699
4104
  disabled,
3700
- onClick: handleClose,
4105
+ onClick: () => setIsOpen(false),
3701
4106
  children: "Cancel"
3702
4107
  },
3703
4108
  "file-inline-upload-cancel"
3704
4109
  )
3705
4110
  ] })
3706
4111
  ] }) })
3707
- }
3708
- )
3709
- ] });
4112
+ ]
4113
+ }
4114
+ );
3710
4115
  };
3711
4116
  var InlineImagePlugin = () => {
3712
4117
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
@@ -3723,6 +4128,11 @@ var InlineImagePlugin = () => {
3723
4128
  if (lexical.$isRootOrShadowRoot(imageNode.getParentOrThrow())) {
3724
4129
  utils.$wrapNodeInElement(imageNode, lexical.$createParagraphNode).selectEnd();
3725
4130
  }
4131
+ const parent = imageNode.getParent();
4132
+ if (parent && typeof parent.setFormat === "function") {
4133
+ const fmt = payload.position === "right" ? "right" : payload.position === "full" ? "center" : "left";
4134
+ parent.setFormat(fmt);
4135
+ }
3726
4136
  return true;
3727
4137
  },
3728
4138
  lexical.COMMAND_PRIORITY_EDITOR
@@ -3751,12 +4161,23 @@ var TRANSPARENT_IMAGE2 = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEA
3751
4161
  var img2 = document.createElement("img");
3752
4162
  img2.src = TRANSPARENT_IMAGE2;
3753
4163
  function $onDragStart2(event) {
3754
- const node = $getImageNodeInSelection2();
4164
+ let node = $getImageNodeInSelection2();
4165
+ if (!node) {
4166
+ const target = event.target;
4167
+ if (!target) return false;
4168
+ const lexicalNode = lexical.$getNearestNodeFromDOMNode(target);
4169
+ if ($isInlineImageNode(lexicalNode)) node = lexicalNode;
4170
+ }
3755
4171
  if (!node) return false;
3756
4172
  const dataTransfer = event.dataTransfer;
3757
4173
  if (!dataTransfer) return false;
4174
+ const imgEl = event.target?.closest?.(".inline-editor-image")?.querySelector?.("img") ?? event.target;
4175
+ if (imgEl instanceof HTMLElement) {
4176
+ dataTransfer.setDragImage(imgEl, 20, 20);
4177
+ } else {
4178
+ dataTransfer.setDragImage(img2, 0, 0);
4179
+ }
3758
4180
  dataTransfer.setData("text/plain", "_");
3759
- dataTransfer.setDragImage(img2, 0, 0);
3760
4181
  dataTransfer.setData(
3761
4182
  "application/x-lexical-drag",
3762
4183
  JSON.stringify({
@@ -3775,28 +4196,31 @@ function $onDragStart2(event) {
3775
4196
  return true;
3776
4197
  }
3777
4198
  var $onDragover2 = (event) => {
3778
- const node = $getImageNodeInSelection2();
3779
- if (!node) return false;
4199
+ const hasDragData = !!event.dataTransfer?.types.includes("application/x-lexical-drag");
4200
+ if (!hasDragData) return false;
3780
4201
  if (!canDropImage2(event)) {
3781
4202
  event.preventDefault();
3782
4203
  }
3783
4204
  return true;
3784
4205
  };
3785
4206
  var $onDrop2 = (event, editor) => {
3786
- const node = $getImageNodeInSelection2();
3787
- if (!node) return false;
3788
4207
  const data = getDragImageData2(event);
3789
4208
  if (!data) return false;
3790
4209
  event.preventDefault();
3791
4210
  if (canDropImage2(event)) {
4211
+ const sourceKey = data.key;
4212
+ if (sourceKey) {
4213
+ const sourceNode = lexical.$getNodeByKey(sourceKey);
4214
+ if (sourceNode) sourceNode.remove();
4215
+ }
3792
4216
  const range = getDragSelection2(event);
3793
- node.remove();
3794
4217
  const rangeSelection = lexical.$createRangeSelection();
3795
4218
  if (range !== null && range !== void 0) {
3796
4219
  rangeSelection.applyDOMRange(range);
3797
4220
  }
3798
4221
  lexical.$setSelection(rangeSelection);
3799
- editor.dispatchCommand(INSERT_INLINE_IMAGE_COMMAND, data);
4222
+ const { key: _key, ...insertPayload } = data;
4223
+ editor.dispatchCommand(INSERT_INLINE_IMAGE_COMMAND, insertPayload);
3800
4224
  }
3801
4225
  return true;
3802
4226
  };
@@ -3816,7 +4240,7 @@ var getDragImageData2 = (event) => {
3816
4240
  };
3817
4241
  var canDropImage2 = (event) => {
3818
4242
  const target = event.target;
3819
- return !!(lexical.isHTMLElement(target) && !target.closest("code, span.editor-image") && lexical.isHTMLElement(target.parentElement) && target.parentElement.closest("div.ContentEditable__root"));
4243
+ return !!(lexical.isHTMLElement(target) && !target.closest("code, span.editor-image, .inline-editor-image") && target.closest('[contenteditable="true"]'));
3820
4244
  };
3821
4245
  var getDragSelection2 = (event) => {
3822
4246
  let range;
@@ -3907,20 +4331,79 @@ function hasBlock(editor, kind) {
3907
4331
  }
3908
4332
 
3909
4333
  // src/Plugins/RefApiPlugin.tsx
4334
+ function getUserContentSignature(editor) {
4335
+ const parts = [];
4336
+ editor.getEditorState().read(() => {
4337
+ const root = lexical.$getRoot();
4338
+ const collectNode = (node) => {
4339
+ if ($isHtmlBlockNode(node)) return;
4340
+ if (lexical.$isDecoratorNode(node)) {
4341
+ parts.push(`D:${node.getKey()}`);
4342
+ return;
4343
+ }
4344
+ if (lexical.$isLineBreakNode(node)) {
4345
+ parts.push("BR");
4346
+ return;
4347
+ }
4348
+ if (lexical.$isTextNode(node)) {
4349
+ parts.push(`T${node.getFormat()}:${node.getStyle()}:${node.getTextContent()}`);
4350
+ return;
4351
+ }
4352
+ if (lexical.$isElementNode(node)) {
4353
+ const type = node.getType();
4354
+ const tag = typeof node.getTag === "function" ? node.getTag() : "";
4355
+ const list = typeof node.getListType === "function" ? node.getListType() : "";
4356
+ const fmt = typeof node.getFormat === "function" ? node.getFormat() : "";
4357
+ parts.push(`[${type}:${tag}:${list}:${fmt}]`);
4358
+ for (const child of node.getChildren()) {
4359
+ collectNode(child);
4360
+ }
4361
+ parts.push("[/]");
4362
+ }
4363
+ };
4364
+ for (const child of root.getChildren()) {
4365
+ if ($isHtmlBlockNode(child)) continue;
4366
+ collectNode(child);
4367
+ }
4368
+ });
4369
+ return JSON.stringify(parts);
4370
+ }
3910
4371
  function RefApiPlugin({
3911
4372
  forwardedRef,
3912
4373
  contentEditableDomRef,
3913
- focusedRef,
3914
- setRefErrors
4374
+ focusedRef
3915
4375
  }) {
3916
4376
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
4377
+ const cleanBaselineRef = React9__namespace.default.useRef(null);
4378
+ React9__namespace.default.useEffect(() => {
4379
+ const capture = () => {
4380
+ if (cleanBaselineRef.current === null) {
4381
+ cleanBaselineRef.current = getUserContentSignature(editor);
4382
+ }
4383
+ };
4384
+ const unregister = editor.registerUpdateListener(({ dirtyElements, dirtyLeaves }) => {
4385
+ if (cleanBaselineRef.current !== null) {
4386
+ unregister();
4387
+ return;
4388
+ }
4389
+ if (dirtyElements.size === 0 && dirtyLeaves.size === 0) return;
4390
+ capture();
4391
+ unregister();
4392
+ });
4393
+ const timerId = setTimeout(capture, 0);
4394
+ return () => {
4395
+ clearTimeout(timerId);
4396
+ unregister();
4397
+ };
4398
+ }, [editor]);
3917
4399
  React9.useImperativeHandle(
3918
4400
  forwardedRef,
3919
4401
  () => ({
3920
4402
  setValue: (html$1) => {
3921
4403
  editor.update(() => {
4404
+ const safe = normalizeToBlockHtml(sanitizeHtml(html$1 || ""));
3922
4405
  const parser = new DOMParser();
3923
- const dom = parser.parseFromString(html$1 || "<p></p>", "text/html");
4406
+ const dom = parser.parseFromString(safe || "<p></p>", "text/html");
3924
4407
  const nodes = html.$generateNodesFromDOM(editor, dom);
3925
4408
  const root = lexical.$getRoot();
3926
4409
  root.clear();
@@ -3930,9 +4413,9 @@ function RefApiPlugin({
3930
4413
  getValue: () => {
3931
4414
  let html$1 = "";
3932
4415
  editor.getEditorState().read(() => {
3933
- html$1 = html.$generateHtmlFromNodes(editor, null);
4416
+ html$1 = postProcessOutput(splitHeadingsAtBrSequences(html.$generateHtmlFromNodes(editor, null)));
3934
4417
  });
3935
- return postProcessOutput(html$1);
4418
+ return html$1;
3936
4419
  },
3937
4420
  clear: () => {
3938
4421
  editor.update(() => {
@@ -3950,14 +4433,20 @@ function RefApiPlugin({
3950
4433
  },
3951
4434
  isFocused: () => focusedRef.current,
3952
4435
  getEditor: () => editor,
3953
- setErrors: (messages) => setRefErrors(messages),
3954
- clearErrors: () => setRefErrors([]),
3955
4436
  // Generic blocks (signature, footer, banner, etc.)
3956
4437
  upsertBlock: (spec) => upsertBlock(editor, spec),
3957
4438
  removeBlock: (kind) => removeBlock(editor, kind),
3958
- hasBlock: (kind) => hasBlock(editor, kind)
4439
+ hasBlock: (kind) => hasBlock(editor, kind),
4440
+ checkDirty: () => {
4441
+ if (cleanBaselineRef.current === null) return false;
4442
+ const current = getUserContentSignature(editor);
4443
+ return current !== cleanBaselineRef.current;
4444
+ },
4445
+ markClean: () => {
4446
+ cleanBaselineRef.current = getUserContentSignature(editor);
4447
+ }
3959
4448
  }),
3960
- [editor, contentEditableDomRef, focusedRef, setRefErrors]
4449
+ [editor, contentEditableDomRef, focusedRef]
3961
4450
  );
3962
4451
  return null;
3963
4452
  }
@@ -4598,8 +5087,22 @@ function TableActionMenuPlugin({ disabled = false }) {
4598
5087
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
4599
5088
  const [isInTable, setIsInTable] = React9__namespace.useState(false);
4600
5089
  const [anchorRect, setAnchorRect] = React9__namespace.useState(null);
5090
+ const [contentRight, setContentRight] = React9__namespace.useState(null);
4601
5091
  const [open, setOpen] = React9__namespace.useState(false);
5092
+ const openRef = React9__namespace.useRef(false);
5093
+ const savedAnchorRef = React9__namespace.useRef(null);
5094
+ const measureContentRight = React9__namespace.useCallback((cellDom) => {
5095
+ try {
5096
+ const range = document.createRange();
5097
+ range.selectNodeContents(cellDom);
5098
+ const cr = range.getBoundingClientRect();
5099
+ return cr.width > 2 ? cr.right : null;
5100
+ } catch {
5101
+ return null;
5102
+ }
5103
+ }, []);
4602
5104
  const updateFromSelection = React9__namespace.useCallback(() => {
5105
+ if (openRef.current) return;
4603
5106
  const root = editor.getRootElement();
4604
5107
  if (!root) return;
4605
5108
  editor.getEditorState().read(() => {
@@ -4611,6 +5114,7 @@ function TableActionMenuPlugin({ disabled = false }) {
4611
5114
  if (dom) {
4612
5115
  setIsInTable(true);
4613
5116
  setAnchorRect(dom.getBoundingClientRect());
5117
+ setContentRight(null);
4614
5118
  return;
4615
5119
  }
4616
5120
  }
@@ -4618,6 +5122,7 @@ function TableActionMenuPlugin({ disabled = false }) {
4618
5122
  if (!lexical.$isRangeSelection(selection)) {
4619
5123
  setIsInTable(false);
4620
5124
  setAnchorRect(null);
5125
+ setContentRight(null);
4621
5126
  return;
4622
5127
  }
4623
5128
  const anchorNode = selection.anchor.getNode();
@@ -4625,18 +5130,28 @@ function TableActionMenuPlugin({ disabled = false }) {
4625
5130
  if (!cellNode || !table.$isTableCellNode(cellNode)) {
4626
5131
  setIsInTable(false);
4627
5132
  setAnchorRect(null);
5133
+ setContentRight(null);
4628
5134
  return;
4629
5135
  }
4630
5136
  const cellDom = editor.getElementByKey(cellNode.getKey());
4631
5137
  if (!cellDom) {
4632
5138
  setIsInTable(false);
4633
5139
  setAnchorRect(null);
5140
+ setContentRight(null);
4634
5141
  return;
4635
5142
  }
4636
5143
  setIsInTable(true);
4637
5144
  setAnchorRect(cellDom.getBoundingClientRect());
5145
+ setContentRight(measureContentRight(cellDom));
5146
+ if (lexical.$isRangeSelection(selection)) {
5147
+ savedAnchorRef.current = {
5148
+ key: selection.anchor.key,
5149
+ offset: selection.anchor.offset,
5150
+ type: selection.anchor.type
5151
+ };
5152
+ }
4638
5153
  });
4639
- }, [editor]);
5154
+ }, [editor, measureContentRight]);
4640
5155
  React9__namespace.useEffect(() => {
4641
5156
  return utils.mergeRegister(
4642
5157
  editor.registerCommand(
@@ -4693,30 +5208,37 @@ function TableActionMenuPlugin({ disabled = false }) {
4693
5208
  lexical.COMMAND_PRIORITY_HIGH
4694
5209
  );
4695
5210
  }, [editor, disabled]);
4696
- React9__namespace.useEffect(() => {
4697
- if (!isInTable && open) setOpen(false);
4698
- }, [isInTable, open]);
4699
5211
  const canShow = isInTable && !!anchorRect && !disabled;
4700
5212
  const handleStyle = React9__namespace.useMemo(() => {
4701
5213
  if (!anchorRect) return void 0;
4702
5214
  const top = Math.max(8, anchorRect.top + 6);
4703
- const left = Math.max(8, anchorRect.right - 34);
5215
+ const clampedCellRight = Math.min(anchorRect.right, window.innerWidth - 8);
5216
+ const left = contentRight !== null ? Math.max(anchorRect.left + 4, Math.min(contentRight + 4, clampedCellRight - 32)) : Math.max(8, anchorRect.left + 8);
4704
5217
  return {
4705
5218
  position: "fixed",
4706
5219
  top,
4707
5220
  left,
4708
5221
  zIndex: 9999
4709
5222
  };
4710
- }, [anchorRect]);
5223
+ }, [anchorRect, contentRight]);
4711
5224
  const dangerStyle = {
4712
5225
  color: "var(--colorPaletteRedForeground1)"
4713
5226
  };
4714
5227
  const run = React9__namespace.useCallback(
4715
5228
  (fn) => {
4716
5229
  if (disabled) return;
4717
- editor.focus();
4718
- editor.update(() => fn());
5230
+ openRef.current = false;
4719
5231
  setOpen(false);
5232
+ editor.update(() => {
5233
+ const saved = savedAnchorRef.current;
5234
+ if (saved && lexical.$getNodeByKey(saved.key)) {
5235
+ const sel = lexical.$createRangeSelection();
5236
+ sel.anchor.set(saved.key, saved.offset, saved.type);
5237
+ sel.focus.set(saved.key, saved.offset, saved.type);
5238
+ lexical.$setSelection(sel);
5239
+ }
5240
+ fn();
5241
+ });
4720
5242
  },
4721
5243
  [disabled, editor]
4722
5244
  );
@@ -4726,32 +5248,31 @@ function TableActionMenuPlugin({ disabled = false }) {
4726
5248
  const insertColLeft = () => run(() => table.$insertTableColumnAtSelection(false));
4727
5249
  const deleteRow = () => run(() => table.$deleteTableRowAtSelection());
4728
5250
  const deleteCol = () => run(() => table.$deleteTableColumnAtSelection());
4729
- const deleteTable = () => run(() => {
4730
- const selection = lexical.$getSelection();
4731
- if (table.$isTableSelection(selection)) {
4732
- const tableNode = selection.getNodes().find((n) => table.$isTableNode(n));
5251
+ const deleteTable = () => {
5252
+ if (disabled) return;
5253
+ openRef.current = false;
5254
+ setOpen(false);
5255
+ editor.update(() => {
5256
+ const saved = savedAnchorRef.current;
5257
+ if (!saved) return;
5258
+ const anchorNode = lexical.$getNodeByKey(saved.key);
5259
+ if (!anchorNode) return;
5260
+ const tableNode = lexical.$findMatchingParent(anchorNode, (n) => table.$isTableNode(n));
4733
5261
  if (tableNode) tableNode.remove();
4734
- return;
4735
- }
4736
- if (!lexical.$isRangeSelection(selection)) return;
4737
- const node = selection.anchor.getNode();
4738
- const cell = lexical.$findMatchingParent(node, (n) => table.$isTableCellNode(n)) ?? (table.$isTableCellNode(node) ? node : null);
4739
- if (!cell) return;
4740
- const table$1 = table.$getTableNodeFromLexicalNodeOrThrow(cell);
4741
- table$1.remove();
4742
- });
5262
+ });
5263
+ };
4743
5264
  if (!canShow || !handleStyle) return null;
4744
5265
  return reactDom.createPortal(
4745
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: handleStyle, className: "aoTableActionHandleRoot", children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Menu, { open, onOpenChange: (_, data) => setOpen(data.open), children: [
5266
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: handleStyle, className: "aoTableActionHandleRoot", "data-lexical-editor-portal": "true", children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Menu, { open, onOpenChange: (_, data) => {
5267
+ openRef.current = data.open;
5268
+ setOpen(data.open);
5269
+ }, children: [
4746
5270
  /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
4747
5271
  "button",
4748
5272
  {
4749
5273
  type: "button",
4750
5274
  className: "aoTableActionHandleBtn",
4751
5275
  "aria-label": "Table options",
4752
- onMouseDown: (e) => {
4753
- e.preventDefault();
4754
- },
4755
5276
  children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ChevronDown12Regular, {})
4756
5277
  }
4757
5278
  ) }),
@@ -5075,8 +5596,8 @@ function getToolbarGroupsByLevel(level) {
5075
5596
  // ['undo', 'redo', '|'],
5076
5597
  ["Bold", "Italic", "Underline", "|"],
5077
5598
  ["ColorPicker", "|"],
5078
- ["Link", "|"],
5079
- ["Insert", "|"],
5599
+ ["Link", "Table", "|"],
5600
+ // ['Image', 'Youtube', 'InlineImage', '|'],
5080
5601
  ["Heading", "|"],
5081
5602
  ["FontFamily", "|"],
5082
5603
  ["FontSize", "|"],
@@ -5090,8 +5611,9 @@ function getToolbarGroupsByLevel(level) {
5090
5611
  // ['undo', 'redo', '|'],
5091
5612
  ["Bold", "Italic", "Underline", "|"],
5092
5613
  ["ColorPicker", "|"],
5093
- ["Link", "|"],
5094
- ["Insert", "|"],
5614
+ ["Link", "Table", "|"],
5615
+ // ['CodeBlock', '|'],
5616
+ ["Image", "Youtube", "InlineImage", "|"],
5095
5617
  ["Heading", "|"],
5096
5618
  ["FontFamily", "|"],
5097
5619
  ["FontSize", "|"],
@@ -5101,6 +5623,85 @@ function getToolbarGroupsByLevel(level) {
5101
5623
  ];
5102
5624
  }
5103
5625
  }
5626
+ var DEFAULT_FONT_SIZE = 15;
5627
+ function $splitBlocksAtLineBreaks(selection) {
5628
+ const blocksToSplit = /* @__PURE__ */ new Set();
5629
+ for (const node of selection.getNodes()) {
5630
+ const block = node.getTopLevelElement();
5631
+ if (block && lexical.$isElementNode(block)) {
5632
+ const children = block.getChildren();
5633
+ if (children.some(lexical.$isLineBreakNode)) {
5634
+ blocksToSplit.add(block);
5635
+ }
5636
+ }
5637
+ }
5638
+ for (const block of blocksToSplit) {
5639
+ const children = [...block.getChildren()];
5640
+ const groups = [[]];
5641
+ for (const child of children) {
5642
+ if (lexical.$isLineBreakNode(child)) {
5643
+ groups.push([]);
5644
+ } else {
5645
+ groups[groups.length - 1].push(child);
5646
+ }
5647
+ }
5648
+ const nonEmptyCount = groups.filter((g) => g.length > 0).length;
5649
+ if (nonEmptyCount <= 1) continue;
5650
+ for (let i = groups.length - 1; i >= 1; i--) {
5651
+ const group = groups[i];
5652
+ const newBlock = group.length === 0 ? lexical.$createParagraphNode() : richText.$isHeadingNode(block) ? richText.$createHeadingNode(block.getTag()) : lexical.$createParagraphNode();
5653
+ group.forEach((child) => newBlock.append(child));
5654
+ block.insertAfter(newBlock);
5655
+ }
5656
+ [...block.getChildren()].filter(lexical.$isLineBreakNode).forEach((br) => br.remove());
5657
+ }
5658
+ }
5659
+ function $splitBlockAtPartialSelection(selection) {
5660
+ if (selection.isCollapsed()) return false;
5661
+ const anchorBlock = selection.anchor.getNode().getTopLevelElement();
5662
+ const focusBlock = selection.focus.getNode().getTopLevelElement();
5663
+ if (!anchorBlock || !focusBlock || !anchorBlock.is(focusBlock)) return false;
5664
+ if (!lexical.$isElementNode(anchorBlock)) return false;
5665
+ const block = anchorBlock;
5666
+ const extractedNodes = selection.extract();
5667
+ if (!extractedNodes.length) return false;
5668
+ const allDirect = extractedNodes.every((n) => {
5669
+ const parent = n.getParent();
5670
+ return parent !== null && parent.is(block);
5671
+ });
5672
+ if (!allDirect) return false;
5673
+ const allChildren = [...block.getChildren()];
5674
+ const firstSelected = extractedNodes[0];
5675
+ const lastSelected = extractedNodes[extractedNodes.length - 1];
5676
+ const firstIdx = allChildren.findIndex((n) => n.is(firstSelected));
5677
+ const lastIdx = allChildren.findIndex((n) => n.is(lastSelected));
5678
+ if (firstIdx === -1 || lastIdx === -1) return false;
5679
+ if (firstIdx === 0 && lastIdx === allChildren.length - 1) return false;
5680
+ const selectedNodes = allChildren.slice(firstIdx, lastIdx + 1);
5681
+ const afterNodes = allChildren.slice(lastIdx + 1);
5682
+ if (afterNodes.length > 0) {
5683
+ const afterBlock = richText.$isHeadingNode(block) ? richText.$createHeadingNode(block.getTag()) : lexical.$createParagraphNode();
5684
+ afterNodes.forEach((n) => afterBlock.append(n));
5685
+ block.insertAfter(afterBlock);
5686
+ }
5687
+ const selectedBlock = lexical.$createParagraphNode();
5688
+ selectedNodes.forEach((n) => selectedBlock.append(n));
5689
+ block.insertAfter(selectedBlock);
5690
+ if (block.getChildrenSize() === 0) {
5691
+ block.remove();
5692
+ }
5693
+ selectedBlock.select();
5694
+ return true;
5695
+ }
5696
+ var formatParagraph = (editor) => {
5697
+ editor.update(() => {
5698
+ const selection$1 = lexical.$getSelection();
5699
+ if (lexical.$isRangeSelection(selection$1)) {
5700
+ $splitBlocksAtLineBreaks(selection$1);
5701
+ }
5702
+ selection.$setBlocksType(lexical.$getSelection(), () => lexical.$createParagraphNode());
5703
+ });
5704
+ };
5104
5705
  var PRESET = [
5105
5706
  "#000000",
5106
5707
  "#434343",
@@ -5384,7 +5985,7 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5384
5985
  directionalHint: 4,
5385
5986
  className: "aoColorCallout",
5386
5987
  preventDismissOnEvent,
5387
- children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 14 }, styles: { root: { padding: "14px 16px 16px", width: 288 } }, children: [
5988
+ children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 14 }, styles: { root: { padding: "14px 16px 16px", width: 250 } }, children: [
5388
5989
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
5389
5990
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, fontWeight: 600, color: "#242424", letterSpacing: 0.1 }, children: title }),
5390
5991
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5397,8 +5998,8 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5397
5998
  display: "flex",
5398
5999
  alignItems: "center",
5399
6000
  justifyContent: "center",
5400
- width: 24,
5401
- height: 24,
6001
+ width: 18,
6002
+ height: 18,
5402
6003
  padding: 0,
5403
6004
  border: "none",
5404
6005
  borderRadius: 4,
@@ -5421,7 +6022,7 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5421
6022
  style: {
5422
6023
  position: "relative",
5423
6024
  width: "100%",
5424
- height: 150,
6025
+ height: 125,
5425
6026
  borderRadius: 8,
5426
6027
  overflow: "hidden",
5427
6028
  cursor: "crosshair",
@@ -5510,8 +6111,8 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5510
6111
  "div",
5511
6112
  {
5512
6113
  style: {
5513
- width: 32,
5514
- height: 32,
6114
+ width: 20,
6115
+ height: 20,
5515
6116
  borderRadius: 6,
5516
6117
  flexShrink: 0,
5517
6118
  background: hex,
@@ -5552,10 +6153,10 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5552
6153
  title: c,
5553
6154
  "aria-label": c,
5554
6155
  style: {
5555
- width: 22,
5556
- height: 22,
6156
+ width: 18,
6157
+ height: 18,
5557
6158
  padding: 0,
5558
- borderRadius: 5,
6159
+ borderRadius: 4,
5559
6160
  background: c,
5560
6161
  cursor: "pointer",
5561
6162
  boxShadow: isSelected ? "0 0 0 2px #fff, 0 0 0 3px #4a86e8" : "inset 0 0 0 1px rgba(0,0,0,0.15)",
@@ -5765,7 +6366,6 @@ var FontFamilyPlugin = ({ disabled = false }) => {
5765
6366
  "font-family"
5766
6367
  );
5767
6368
  };
5768
- var DEFAULT_FONT_SIZE = 15;
5769
6369
  var FONT_SIZE_OPTIONS = [
5770
6370
  "8",
5771
6371
  "9",
@@ -5901,135 +6501,43 @@ var FontSizePlugin = ({ disabled }) => {
5901
6501
  "fontsize"
5902
6502
  ) });
5903
6503
  };
5904
- var VERBATIM_LINK_RE = /^https?:\/\/|^mailto:|^tel:|^#|^\//i;
5905
- function getLinkValidationMessage(raw) {
5906
- const trimmed = raw.trim();
5907
- if (!trimmed) return void 0;
5908
- if (/\s/.test(trimmed)) return "URL cannot contain spaces";
5909
- if (!VERBATIM_LINK_RE.test(trimmed) && !trimmed.includes(".")) {
5910
- return "Enter a valid URL (e.g. example.com)";
5911
- }
5912
- return void 0;
5913
- }
5914
6504
  var InsertLinkPlugin = ({
5915
6505
  disabled,
5916
- open: externalOpen,
5917
- onClose
6506
+ setIsLinkEditMode
5918
6507
  }) => {
5919
6508
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
5920
- const [internalOpen, setInternalOpen] = React9.useState(false);
5921
- const [text, setText] = React9.useState("");
5922
- const [link$1, setLink] = React9.useState("");
5923
6509
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
5924
- const isControlled = externalOpen !== void 0;
5925
- const isOpen = isControlled ? !!externalOpen && !disabled : internalOpen && !disabled;
5926
- const linkError = getLinkValidationMessage(link$1);
5927
- const handleClose = () => {
5928
- setText("");
5929
- setLink("");
5930
- if (isControlled) onClose?.();
5931
- else setInternalOpen(false);
5932
- };
5933
- const insertLink = (text2, link2) => {
6510
+ const insertLink = () => {
5934
6511
  if (disabled) return;
5935
- if (getLinkValidationMessage(link2)) return;
5936
- const trimmedLink = link2.trim();
5937
- const href = VERBATIM_LINK_RE.test(trimmedLink) ? trimmedLink : `https://${trimmedLink}`;
5938
6512
  editor.update(() => {
5939
6513
  const selection = lexical.$getSelection();
5940
- if (lexical.$isRangeSelection(selection)) {
5941
- const textNode = new lexical.TextNode(text2);
5942
- const linkNode = link.$createLinkNode(href);
5943
- linkNode.append(textNode);
5944
- selection.insertNodes([linkNode]);
6514
+ if (!lexical.$isRangeSelection(selection)) return;
6515
+ const node = getSelectedNode2(selection);
6516
+ const linkParent = utils.$findMatchingParent(node, link.$isLinkNode);
6517
+ if (!linkParent && !link.$isLinkNode(node)) {
6518
+ editor.dispatchCommand(link.TOGGLE_LINK_COMMAND, "https://");
5945
6519
  }
5946
6520
  });
5947
- handleClose();
6521
+ setIsLinkEditMode(true);
5948
6522
  };
5949
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5950
- !isControlled && /* @__PURE__ */ jsxRuntime.jsx(
5951
- reactComponents.Button,
5952
- {
5953
- size: "small",
5954
- title: "Add link",
5955
- disabled,
5956
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.LinkAddRegular, { style: { color: iconColor } }),
5957
- style: {
5958
- background: isOpen && !disabled ? "#ebebeb" : "none",
5959
- border: "none",
5960
- margin: 2,
5961
- opacity: disabled ? 0.55 : 1,
5962
- cursor: disabled ? "not-allowed" : "pointer"
5963
- },
5964
- onClick: () => {
5965
- if (disabled) return;
5966
- setInternalOpen((prev) => !prev);
5967
- }
6523
+ return /* @__PURE__ */ jsxRuntime.jsx(
6524
+ reactComponents.Button,
6525
+ {
6526
+ size: "small",
6527
+ title: "Add link",
6528
+ disabled,
6529
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.LinkAddRegular, { style: { color: iconColor } }),
6530
+ style: {
6531
+ background: "none",
6532
+ border: "none",
6533
+ margin: 2,
6534
+ opacity: disabled ? 0.55 : 1,
6535
+ cursor: disabled ? "not-allowed" : "pointer"
5968
6536
  },
5969
- "upload-link"
5970
- ),
5971
- /* @__PURE__ */ jsxRuntime.jsx(
5972
- reactComponents.Dialog,
5973
- {
5974
- open: isOpen,
5975
- onOpenChange: (_, data) => {
5976
- if (!data.open) handleClose();
5977
- },
5978
- children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogSurface, { style: { maxWidth: "380px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogBody, { children: [
5979
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTitle, { children: "Insert Link" }),
5980
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 10 }, style: { paddingTop: 8 }, children: [
5981
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
5982
- reactComponents.Input,
5983
- {
5984
- autoFocus: !disabled,
5985
- value: text,
5986
- appearance: "underline",
5987
- placeholder: "Text",
5988
- disabled,
5989
- onChange: (_, v) => setText(v.value)
5990
- }
5991
- ) }),
5992
- /* @__PURE__ */ jsxRuntime.jsx(
5993
- reactComponents.Field,
5994
- {
5995
- label: "Link",
5996
- orientation: "horizontal",
5997
- size: "small",
5998
- validationState: linkError ? "error" : "none",
5999
- validationMessage: linkError,
6000
- children: /* @__PURE__ */ jsxRuntime.jsx(
6001
- reactComponents.Input,
6002
- {
6003
- value: link$1,
6004
- appearance: "underline",
6005
- placeholder: "Link",
6006
- disabled,
6007
- onChange: (_, v) => setLink(v.value),
6008
- onKeyDown: (e) => {
6009
- if (e.key === "Enter" && text && link$1 && !linkError) insertLink(text, link$1);
6010
- }
6011
- }
6012
- )
6013
- }
6014
- )
6015
- ] }) }),
6016
- /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogActions, { children: [
6017
- /* @__PURE__ */ jsxRuntime.jsx(
6018
- reactComponents.Button,
6019
- {
6020
- appearance: "primary",
6021
- size: "small",
6022
- disabled: disabled || !text || !link$1 || !!linkError,
6023
- onClick: () => insertLink(text, link$1),
6024
- children: "Add"
6025
- }
6026
- ),
6027
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: handleClose, children: "Cancel" })
6028
- ] })
6029
- ] }) })
6030
- }
6031
- )
6032
- ] });
6537
+ onClick: insertLink
6538
+ },
6539
+ "insert-link"
6540
+ );
6033
6541
  };
6034
6542
  function PageSetupPlugin({ disabled, value, onChange }) {
6035
6543
  const sizeLabel = value.size === "pageless" ? "Pageless" : PAGE_SIZE_OPTIONS.find((o) => o.key === value.size)?.label ?? "Pageless";
@@ -6085,281 +6593,177 @@ function PageSetupPlugin({ disabled, value, onChange }) {
6085
6593
  }
6086
6594
  );
6087
6595
  }
6088
- var MAX_ROWS = 50;
6089
- var MAX_COLS = 50;
6090
- var TableItemPlugin = ({
6091
- disabled,
6092
- open: externalOpen,
6093
- onClose
6094
- }) => {
6596
+ var TableItemPlugin = ({ disabled }) => {
6095
6597
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
6096
6598
  const [columns, setColumns] = React9.useState("");
6097
6599
  const [rows, setRows] = React9.useState("");
6098
- const [internalOpen, setInternalOpen] = React9.useState(false);
6099
- const [rowError, setRowError] = React9.useState("");
6100
- const [colError, setColError] = React9.useState("");
6101
- const isControlled = externalOpen !== void 0;
6102
- const isOpen = isControlled ? !!externalOpen && !disabled : internalOpen && !disabled;
6600
+ const [isOpen, setIsOpen] = React9.useState(false);
6103
6601
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
6104
- const handleClose = () => {
6105
- setRows("");
6106
- setColumns("");
6107
- setRowError("");
6108
- setColError("");
6109
- if (isControlled) onClose?.();
6110
- else setInternalOpen(false);
6111
- };
6112
- const onRowsChange = (val) => {
6113
- const clean = val.replace(/\D/g, "");
6114
- setRows(clean);
6115
- const n = Number(clean);
6116
- if (clean && n > MAX_ROWS) setRowError(`Maximum ${MAX_ROWS} rows allowed`);
6117
- else setRowError("");
6118
- };
6119
- const onColsChange = (val) => {
6120
- const clean = val.replace(/\D/g, "");
6121
- setColumns(clean);
6122
- const n = Number(clean);
6123
- if (clean && n > MAX_COLS) setColError(`Maximum ${MAX_COLS} columns allowed`);
6124
- else setColError("");
6125
- };
6126
6602
  const onAddTable = () => {
6127
6603
  if (disabled) return;
6128
6604
  const row = Number(rows);
6129
6605
  const col = Number(columns);
6130
6606
  if (!row || !col) return;
6131
- if (row > MAX_ROWS || col > MAX_COLS) return;
6132
6607
  editor.update(() => {
6133
6608
  const tableNode = table.$createTableNodeWithDimensions(row, col, true);
6134
6609
  utils.$insertNodeToNearestRoot(tableNode);
6135
6610
  });
6136
- handleClose();
6611
+ setRows("");
6612
+ setColumns("");
6613
+ setIsOpen(false);
6137
6614
  };
6138
- const isAddDisabled = disabled || !rows || !columns || !!rowError || !!colError;
6139
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6140
- !isControlled && /* @__PURE__ */ jsxRuntime.jsx(
6141
- reactComponents.Button,
6142
- {
6143
- size: "small",
6144
- title: "Add table",
6145
- disabled,
6146
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TableAddRegular, { style: { color: iconColor } }),
6147
- style: {
6148
- background: isOpen && !disabled ? "#ebebeb" : "none",
6149
- border: "none",
6150
- margin: 2,
6151
- opacity: disabled ? 0.55 : 1,
6152
- cursor: disabled ? "not-allowed" : "pointer"
6153
- },
6154
- onClick: () => {
6155
- if (disabled) return;
6156
- setRows("");
6157
- setColumns("");
6158
- setRowError("");
6159
- setColError("");
6160
- setInternalOpen(true);
6161
- }
6615
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6616
+ reactComponents.Dialog,
6617
+ {
6618
+ open: disabled ? false : isOpen,
6619
+ onOpenChange: (_, data) => {
6620
+ if (!disabled) setIsOpen(data.open);
6162
6621
  },
6163
- "insert-table-nodes"
6164
- ),
6165
- /* @__PURE__ */ jsxRuntime.jsx(
6166
- reactComponents.Dialog,
6167
- {
6168
- open: isOpen,
6169
- onOpenChange: (_, data) => {
6170
- if (!data.open) handleClose();
6171
- },
6172
- children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogSurface, { style: { maxWidth: "380px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogBody, { children: [
6173
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTitle, { children: "Insert Table" }),
6174
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 10 }, style: { paddingTop: 8 }, children: [
6175
- /* @__PURE__ */ jsxRuntime.jsx(
6176
- reactComponents.Field,
6622
+ children: [
6623
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
6624
+ reactComponents.Button,
6625
+ {
6626
+ size: "small",
6627
+ title: "Add table",
6628
+ disabled,
6629
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TableAddRegular, { style: { color: iconColor } }),
6630
+ style: {
6631
+ background: isOpen && !disabled ? "#ebebeb" : "none",
6632
+ border: "none",
6633
+ margin: 2,
6634
+ opacity: disabled ? 0.55 : 1,
6635
+ cursor: disabled ? "not-allowed" : "pointer"
6636
+ },
6637
+ onClick: () => {
6638
+ if (disabled) return;
6639
+ setIsOpen((prev) => !prev);
6640
+ setRows("");
6641
+ setColumns("");
6642
+ }
6643
+ },
6644
+ "insert-table-nodes"
6645
+ ) }),
6646
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogSurface, { style: { maxWidth: 300 }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogBody, { children: [
6647
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTitle, { children: "Insert table" }),
6648
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 8 }, children: [
6649
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Rows", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
6650
+ reactComponents.Input,
6177
6651
  {
6178
- label: "Rows",
6179
- orientation: "horizontal",
6180
- size: "small",
6181
- validationMessage: rowError || void 0,
6182
- validationState: rowError ? "error" : "none",
6183
- children: /* @__PURE__ */ jsxRuntime.jsx(
6184
- reactComponents.Input,
6185
- {
6186
- autoFocus: !disabled,
6187
- type: "number",
6188
- min: 1,
6189
- max: MAX_ROWS,
6190
- value: rows,
6191
- placeholder: "Rows",
6192
- appearance: "underline",
6193
- disabled,
6194
- input: { style: { textAlign: "left" } },
6195
- onChange: (_, v) => onRowsChange(v.value)
6196
- }
6197
- )
6652
+ autoFocus: !disabled,
6653
+ value: rows,
6654
+ placeholder: "Rows",
6655
+ appearance: "underline",
6656
+ disabled,
6657
+ onChange: (_, v) => setRows(v.value)
6198
6658
  }
6199
- ),
6200
- /* @__PURE__ */ jsxRuntime.jsx(
6201
- reactComponents.Field,
6659
+ ) }),
6660
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Columns", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
6661
+ reactComponents.Input,
6202
6662
  {
6203
- label: "Columns",
6204
- orientation: "horizontal",
6205
- size: "small",
6206
- validationMessage: colError || void 0,
6207
- validationState: colError ? "error" : "none",
6208
- children: /* @__PURE__ */ jsxRuntime.jsx(
6209
- reactComponents.Input,
6210
- {
6211
- type: "number",
6212
- min: 1,
6213
- max: MAX_COLS,
6214
- value: columns,
6215
- placeholder: "Columns",
6216
- appearance: "underline",
6217
- disabled,
6218
- input: { style: { textAlign: "left" } },
6219
- onChange: (_, v) => onColsChange(v.value)
6220
- }
6221
- )
6663
+ value: columns,
6664
+ placeholder: "Columns",
6665
+ appearance: "underline",
6666
+ disabled,
6667
+ onChange: (_, v) => setColumns(v.value)
6222
6668
  }
6223
- )
6669
+ ) })
6224
6670
  ] }) }),
6225
6671
  /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogActions, { children: [
6226
6672
  /* @__PURE__ */ jsxRuntime.jsx(
6227
6673
  reactComponents.Button,
6228
6674
  {
6229
- appearance: "primary",
6230
6675
  size: "small",
6231
- disabled: isAddDisabled,
6676
+ appearance: "primary",
6677
+ disabled: disabled || !rows || !columns,
6232
6678
  onClick: onAddTable,
6233
6679
  children: "Add"
6234
6680
  }
6235
6681
  ),
6236
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: handleClose, children: "Cancel" })
6682
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
6237
6683
  ] })
6238
6684
  ] }) })
6239
- }
6240
- )
6241
- ] });
6685
+ ]
6686
+ }
6687
+ );
6242
6688
  };
6243
- function extractYouTubeId(url) {
6244
- const trimmed = url.trim();
6245
- if (/^[\w-]{11}$/.test(trimmed)) return trimmed;
6246
- const match = /(?:youtu\.be\/|youtube\.com\/(?:watch\?v=|embed\/|v\/|shorts\/|live\/|u\/\w\/))([^#&?]{11})/.exec(trimmed);
6247
- return match ? match[1] : null;
6248
- }
6249
- var YoutubeUploadPlugin = ({
6250
- disabled,
6251
- open: externalOpen,
6252
- onClose
6253
- }) => {
6689
+ var YoutubeUploadPlugin = ({ disabled }) => {
6254
6690
  const [url, setURL] = React9.useState("");
6255
- const [urlError, setUrlError] = React9.useState("");
6256
- const [internalOpen, setInternalOpen] = React9.useState(false);
6691
+ const [isOpen, setIsOpen] = React9.useState(false);
6257
6692
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
6258
6693
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#424242";
6259
- const isControlled = externalOpen !== void 0;
6260
- const isOpen = isControlled ? !!externalOpen && !disabled : internalOpen && !disabled;
6261
- const handleClose = () => {
6262
- setURL("");
6263
- setUrlError("");
6264
- if (isControlled) onClose?.();
6265
- else setInternalOpen(false);
6266
- };
6267
6694
  const onHandleEmbeded = () => {
6268
6695
  if (disabled) return;
6269
6696
  if (!url) return;
6270
- const id = extractYouTubeId(url);
6271
- if (!id) {
6272
- setUrlError("Invalid YouTube URL. Supported: watch?v=, youtu.be/, /shorts/, /live/");
6273
- return;
6274
- }
6275
- setUrlError("");
6697
+ const match = /^.*(youtu\.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/.exec(url);
6698
+ const id = match && match[2]?.length === 11 ? match[2] : null;
6699
+ if (!id) return;
6276
6700
  editor.update(() => {
6277
6701
  const node = $createYouTubeNode(id);
6278
6702
  lexical.$insertNodes([node]);
6279
6703
  });
6280
- handleClose();
6704
+ setURL("");
6705
+ setIsOpen(false);
6281
6706
  };
6282
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6283
- !isControlled && /* @__PURE__ */ jsxRuntime.jsx(
6284
- reactComponents.Button,
6285
- {
6286
- title: "Add youtube URL",
6287
- size: "small",
6288
- disabled,
6289
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.VideoClipRegular, { style: { color: iconColor } }),
6290
- style: {
6291
- background: isOpen && !disabled ? "#ebebeb" : "none",
6292
- border: "none",
6293
- margin: 2,
6294
- opacity: disabled ? 0.55 : 1,
6295
- cursor: disabled ? "not-allowed" : "pointer"
6296
- },
6297
- onClick: () => {
6298
- if (disabled) return;
6299
- setURL("");
6300
- setUrlError("");
6301
- setInternalOpen(true);
6302
- }
6707
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6708
+ reactComponents.Dialog,
6709
+ {
6710
+ open: disabled ? false : isOpen,
6711
+ onOpenChange: (_, data) => {
6712
+ if (!disabled) setIsOpen(data.open);
6303
6713
  },
6304
- "upload-video"
6305
- ),
6306
- /* @__PURE__ */ jsxRuntime.jsx(
6307
- reactComponents.Dialog,
6308
- {
6309
- open: isOpen,
6310
- onOpenChange: (_, data) => {
6311
- if (!data.open) handleClose();
6312
- },
6313
- children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogSurface, { style: { maxWidth: "420px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogBody, { children: [
6314
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTitle, { children: "Insert YouTube Video" }),
6315
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 10 }, style: { paddingTop: 8 }, children: [
6316
- /* @__PURE__ */ jsxRuntime.jsx(
6317
- reactComponents.Field,
6318
- {
6319
- label: "URL",
6320
- orientation: "horizontal",
6321
- size: "small",
6322
- validationState: urlError ? "error" : "none",
6323
- validationMessage: urlError || void 0,
6324
- children: /* @__PURE__ */ jsxRuntime.jsx(
6325
- reactComponents.Input,
6326
- {
6327
- autoFocus: !disabled,
6328
- disabled,
6329
- value: url,
6330
- appearance: "underline",
6331
- placeholder: "Paste YouTube URL or video ID\u2026",
6332
- onChange: (_, v) => {
6333
- setURL(v.value);
6334
- if (urlError) setUrlError("");
6335
- },
6336
- onKeyDown: (e) => {
6337
- if (e.key === "Enter") onHandleEmbeded();
6338
- }
6339
- }
6340
- )
6341
- }
6342
- ),
6343
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "#94a3b8", lineHeight: 1.5 }, children: "Supports: youtube.com/watch?v=\u2026, youtu.be/\u2026, /shorts/\u2026, /live/\u2026" })
6344
- ] }) }),
6714
+ children: [
6715
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
6716
+ reactComponents.Button,
6717
+ {
6718
+ title: "Add youtube URL",
6719
+ size: "small",
6720
+ disabled,
6721
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.VideoClipRegular, { style: { color: iconColor } }),
6722
+ style: {
6723
+ background: isOpen && !disabled ? "#ebebeb" : "none",
6724
+ border: "none",
6725
+ margin: 2,
6726
+ opacity: disabled ? 0.55 : 1,
6727
+ cursor: disabled ? "not-allowed" : "pointer"
6728
+ },
6729
+ onClick: () => {
6730
+ if (disabled) return;
6731
+ setIsOpen((prev) => !prev);
6732
+ setURL("");
6733
+ }
6734
+ },
6735
+ "upload-video"
6736
+ ) }),
6737
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogSurface, { style: { maxWidth: 320 }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogBody, { children: [
6738
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTitle, { children: "Embed YouTube video" }),
6739
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogContent, { children: /* @__PURE__ */ jsxRuntime.jsx(react.Stack, { tokens: { childrenGap: 8 }, children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "URL", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
6740
+ reactComponents.Input,
6741
+ {
6742
+ autoFocus: !disabled,
6743
+ disabled,
6744
+ value: url,
6745
+ appearance: "underline",
6746
+ placeholder: "Add Youtube video URL",
6747
+ onChange: (_, v) => setURL(v.value)
6748
+ }
6749
+ ) }) }) }),
6345
6750
  /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogActions, { children: [
6346
- /* @__PURE__ */ jsxRuntime.jsx(
6347
- reactComponents.Button,
6348
- {
6349
- appearance: "primary",
6350
- size: "small",
6351
- disabled: disabled || !url,
6352
- onClick: onHandleEmbeded,
6353
- children: "Add"
6354
- }
6355
- ),
6356
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: handleClose, children: "Cancel" })
6751
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled: disabled || !url, onClick: onHandleEmbeded, children: "Add" }),
6752
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
6357
6753
  ] })
6358
6754
  ] }) })
6359
- }
6360
- )
6361
- ] });
6755
+ ]
6756
+ }
6757
+ );
6362
6758
  };
6759
+ var TextAlphaListLtrFilled = ({ style }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "1em", height: "1em", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", style, children: [
6760
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8.75 4a.75.75 0 1 0 0 1.5h7.5a.75.75 0 0 0 0-1.5h-7.5Z" }),
6761
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8.75 9a.75.75 0 1 0 0 1.5h7.5a.75.75 0 0 0 0-1.5h-7.5Z" }),
6762
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 14.75c0-.41.34-.75.75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5a.75.75 0 0 1-.75-.75Z" }),
6763
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: "3.5", y: "6", fontSize: "5.5", fontWeight: "bold", textAnchor: "middle", children: "a" }),
6764
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: "3.5", y: "11", fontSize: "5.5", fontWeight: "bold", textAnchor: "middle", children: "b" }),
6765
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: "3.5", y: "16", fontSize: "5.5", fontWeight: "bold", textAnchor: "middle", children: "c" })
6766
+ ] });
6363
6767
  var useStyles4 = reactComponents.makeStyles({
6364
6768
  dropdown: {
6365
6769
  minInlineSize: "90px",
@@ -6377,25 +6781,42 @@ var ALLOWED_TOKENS = {
6377
6781
  Bold: true,
6378
6782
  Italic: true,
6379
6783
  Underline: true,
6784
+ Strikethrough: true,
6785
+ Subscript: true,
6786
+ Superscript: true,
6787
+ Highlight: true,
6788
+ Uppercase: true,
6789
+ Lowercase: true,
6790
+ Capitalize: true,
6791
+ BulletList: true,
6792
+ NumberList: true,
6793
+ AlphabeticalList: true,
6794
+ Quote: true,
6795
+ PageBreak: true,
6796
+ H1: true,
6797
+ H2: true,
6798
+ H3: true,
6799
+ H4: true,
6800
+ H5: true,
6801
+ H6: true,
6380
6802
  ColorPicker: true,
6381
6803
  Link: true,
6382
6804
  Table: true,
6383
6805
  Image: true,
6384
6806
  InlineImage: true,
6385
6807
  Youtube: true,
6386
- Insert: true,
6387
- Heading: true,
6388
6808
  FontFamily: true,
6389
6809
  FontSize: true,
6810
+ Align: true,
6811
+ Heading: true,
6390
6812
  Decorators: true,
6391
6813
  CodeBlock: true,
6392
- Align: true,
6393
6814
  PageSetup: true
6394
6815
  };
6395
6816
  function sanitizePluginGroups(groups) {
6396
- if (!groups || groups.length === 0) return [];
6817
+ if (!Array.isArray(groups) || groups.length === 0) return [];
6397
6818
  return groups.map(
6398
- (g) => (g || []).map((t) => t.trim()).filter((t) => ALLOWED_TOKENS[t] === true)
6819
+ (g) => (Array.isArray(g) ? g : []).map((t) => typeof t === "string" ? t.trim() : "").filter((t) => ALLOWED_TOKENS[t] === true)
6399
6820
  ).filter((g) => g.length > 0);
6400
6821
  }
6401
6822
  var ToolBarPlugins = (props) => {
@@ -6415,9 +6836,9 @@ var ToolBarPlugins = (props) => {
6415
6836
  const [isLowercase, setIsLowercase] = React9.useState(false);
6416
6837
  const [isCapitalize, setIsCapitalize] = React9.useState(false);
6417
6838
  const [alignment, setAlignment] = React9.useState("left");
6418
- const [activeInsertDialog, setActiveInsertDialog] = React9.useState(null);
6419
- const lastSelectionRef = React9__namespace.default.useRef(null);
6420
- const presetGroups = getToolbarGroupsByLevel(props.level);
6839
+ const [decoratorOpen, setDecoratorOpen] = React9.useState(false);
6840
+ const decoratorSelectingRef = React9__namespace.default.useRef(false);
6841
+ const presetGroups = props.customToolbar ?? getToolbarGroupsByLevel(props.level);
6421
6842
  const pluginGroups = React9.useMemo(() => sanitizePluginGroups(presetGroups), [presetGroups]);
6422
6843
  const updateToolbarPlugins = () => {
6423
6844
  const selection = lexical.$getSelection();
@@ -6459,6 +6880,10 @@ var ToolBarPlugins = (props) => {
6459
6880
  setSelectNodeType("paragraph");
6460
6881
  return;
6461
6882
  }
6883
+ if ($isAlphaListNode(element)) {
6884
+ setSelectNodeType("alpha");
6885
+ return;
6886
+ }
6462
6887
  if (list.$isListNode(element)) {
6463
6888
  const parentList = utils.$getNearestNodeOfType(anchorNode, list.ListNode);
6464
6889
  const type2 = parentList ? parentList.getTag() : element.getTag();
@@ -6470,23 +6895,15 @@ var ToolBarPlugins = (props) => {
6470
6895
  ["paragraph", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "quote", "code"].includes(type) ? type : "paragraph"
6471
6896
  );
6472
6897
  };
6473
- const applyToBlock = React9__namespace.default.useCallback(
6474
- (fn) => {
6475
- editor.update(() => {
6476
- const saved = lastSelectionRef.current;
6477
- if (saved) lexical.$setSelection(saved.clone());
6478
- const sel = lexical.$getSelection();
6479
- if (lexical.$isRangeSelection(sel)) fn(sel);
6480
- });
6481
- },
6482
- [editor]
6483
- );
6484
6898
  const formatQuote = () => {
6485
- applyToBlock((selection$1) => {
6899
+ editor.update(() => {
6900
+ const selection$1 = lexical.$getSelection();
6901
+ if (!lexical.$isRangeSelection(selection$1)) return;
6486
6902
  if (selectNodeType === "quote") {
6487
- selection.$setBlocksType(selection$1, () => lexical.$createParagraphNode());
6903
+ formatParagraph(editor);
6488
6904
  } else {
6489
- selection.$setBlocksType(selection$1, () => richText.$createQuoteNode());
6905
+ $splitBlocksAtLineBreaks(selection$1);
6906
+ selection.$setBlocksType(lexical.$getSelection(), () => richText.$createQuoteNode());
6490
6907
  }
6491
6908
  });
6492
6909
  };
@@ -6503,8 +6920,6 @@ var ToolBarPlugins = (props) => {
6503
6920
  editor.registerCommand(
6504
6921
  lexical.SELECTION_CHANGE_COMMAND,
6505
6922
  () => {
6506
- const sel = lexical.$getSelection();
6507
- if (lexical.$isRangeSelection(sel)) lastSelectionRef.current = sel.clone();
6508
6923
  updateToolbarPlugins();
6509
6924
  return false;
6510
6925
  },
@@ -6536,52 +6951,16 @@ var ToolBarPlugins = (props) => {
6536
6951
  editor.dispatchCommand(lexical.FORMAT_TEXT_COMMAND, "highlight");
6537
6952
  break;
6538
6953
  case "leftAlign" /* LeftAlign */:
6539
- applyToBlock((sel) => {
6540
- const seen = /* @__PURE__ */ new Set();
6541
- sel.getNodes().forEach((n) => {
6542
- const t = n.getTopLevelElementOrThrow();
6543
- if (!seen.has(t.getKey())) {
6544
- seen.add(t.getKey());
6545
- t.setFormat("left");
6546
- }
6547
- });
6548
- });
6954
+ applyAlignmentWithSplit("left");
6549
6955
  break;
6550
6956
  case "rightAlign" /* RightAlign */:
6551
- applyToBlock((sel) => {
6552
- const seen = /* @__PURE__ */ new Set();
6553
- sel.getNodes().forEach((n) => {
6554
- const t = n.getTopLevelElementOrThrow();
6555
- if (!seen.has(t.getKey())) {
6556
- seen.add(t.getKey());
6557
- t.setFormat("right");
6558
- }
6559
- });
6560
- });
6957
+ applyAlignmentWithSplit("right");
6561
6958
  break;
6562
6959
  case "centerAlign" /* CenterAlign */:
6563
- applyToBlock((sel) => {
6564
- const seen = /* @__PURE__ */ new Set();
6565
- sel.getNodes().forEach((n) => {
6566
- const t = n.getTopLevelElementOrThrow();
6567
- if (!seen.has(t.getKey())) {
6568
- seen.add(t.getKey());
6569
- t.setFormat("center");
6570
- }
6571
- });
6572
- });
6960
+ applyAlignmentWithSplit("center");
6573
6961
  break;
6574
6962
  case "justifyAlign" /* JustifyAlign */:
6575
- applyToBlock((sel) => {
6576
- const seen = /* @__PURE__ */ new Set();
6577
- sel.getNodes().forEach((n) => {
6578
- const t = n.getTopLevelElementOrThrow();
6579
- if (!seen.has(t.getKey())) {
6580
- seen.add(t.getKey());
6581
- t.setFormat("justify");
6582
- }
6583
- });
6584
- });
6963
+ applyAlignmentWithSplit("justify");
6585
6964
  break;
6586
6965
  case "undo" /* Undo */:
6587
6966
  editor.dispatchCommand(lexical.UNDO_COMMAND, void 0);
@@ -6600,9 +6979,32 @@ var ToolBarPlugins = (props) => {
6600
6979
  break;
6601
6980
  }
6602
6981
  };
6982
+ const applyAlignmentWithSplit = (formatType) => {
6983
+ editor.update(() => {
6984
+ const selection = lexical.$getSelection();
6985
+ if (!lexical.$isRangeSelection(selection)) return;
6986
+ $splitBlocksAtLineBreaks(selection);
6987
+ const sel = lexical.$getSelection();
6988
+ if (lexical.$isRangeSelection(sel)) {
6989
+ $splitBlockAtPartialSelection(sel);
6990
+ }
6991
+ });
6992
+ editor.dispatchCommand(lexical.FORMAT_ELEMENT_COMMAND, formatType);
6993
+ };
6603
6994
  const updateHeading = (heading) => {
6604
- applyToBlock((selection$1) => {
6605
- selection.$setBlocksType(selection$1, () => richText.$createHeadingNode(heading));
6995
+ editor.update(() => {
6996
+ const selection$1 = lexical.$getSelection();
6997
+ if (lexical.$isRangeSelection(selection$1)) {
6998
+ $splitBlocksAtLineBreaks(selection$1);
6999
+ const sel = lexical.$getSelection();
7000
+ if (lexical.$isRangeSelection(sel)) {
7001
+ $splitBlockAtPartialSelection(sel);
7002
+ }
7003
+ selection.$setBlocksType(lexical.$getSelection(), () => richText.$createHeadingNode(heading));
7004
+ }
7005
+ });
7006
+ editor.getEditorState().read(() => {
7007
+ updateToolbarPlugins();
6606
7008
  });
6607
7009
  };
6608
7010
  const renderToken = (token, groupIndex, tokenIndex) => {
@@ -6614,7 +7016,6 @@ var ToolBarPlugins = (props) => {
6614
7016
  const brand = palette.themePrimary;
6615
7017
  const brandHover = palette.themeDarkAlt ?? brand;
6616
7018
  const brandPressed = palette.themeDark ?? brand;
6617
- palette.white;
6618
7019
  const bgHover = palette.neutralLighter;
6619
7020
  const bgPressed = palette.neutralLight;
6620
7021
  const bgActive = palette.neutralLighterAlt ?? palette.neutralLighter;
@@ -6661,13 +7062,14 @@ var ToolBarPlugins = (props) => {
6661
7062
  marginRight: 8,
6662
7063
  verticalAlign: "middle"
6663
7064
  };
6664
- const isDisabled = !isEditable || !!props.readOnly;
6665
7065
  switch (token) {
6666
7066
  case "Bold":
6667
7067
  return /* @__PURE__ */ jsxRuntime.jsx(
6668
7068
  reactComponents.Button,
6669
7069
  {
6670
7070
  size: "small",
7071
+ "aria-label": "Bold",
7072
+ "aria-pressed": isBold,
6671
7073
  disabled: !isEditable || props.readOnly,
6672
7074
  icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextBold24Regular, { style: { color: getIconColor(isBold) } }),
6673
7075
  style: getButtonStyle(isBold),
@@ -6680,6 +7082,8 @@ var ToolBarPlugins = (props) => {
6680
7082
  reactComponents.Button,
6681
7083
  {
6682
7084
  size: "small",
7085
+ "aria-label": "Italic",
7086
+ "aria-pressed": isItalic,
6683
7087
  disabled: !isEditable || props.readOnly,
6684
7088
  icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextItalicFilled, { style: { color: getIconColor(isItalic) } }),
6685
7089
  style: getButtonStyle(isItalic),
@@ -6692,6 +7096,8 @@ var ToolBarPlugins = (props) => {
6692
7096
  reactComponents.Button,
6693
7097
  {
6694
7098
  size: "small",
7099
+ "aria-label": "Underline",
7100
+ "aria-pressed": isUnderline,
6695
7101
  disabled: !isEditable || props.readOnly,
6696
7102
  icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextUnderlineFilled, { style: { color: getIconColor(isUnderline) } }),
6697
7103
  style: getButtonStyle(isUnderline),
@@ -6702,7 +7108,14 @@ var ToolBarPlugins = (props) => {
6702
7108
  case "ColorPicker":
6703
7109
  return /* @__PURE__ */ jsxRuntime.jsx(ColorPickerPlugin, { disabled: !isEditable || props.readOnly }, key);
6704
7110
  case "Link":
6705
- return /* @__PURE__ */ jsxRuntime.jsx(InsertLinkPlugin, { disabled: !isEditable || props.readOnly }, key);
7111
+ return /* @__PURE__ */ jsxRuntime.jsx(
7112
+ InsertLinkPlugin,
7113
+ {
7114
+ disabled: !isEditable || props.readOnly,
7115
+ setIsLinkEditMode: props.setIsLinkEditMode
7116
+ },
7117
+ key
7118
+ );
6706
7119
  case "Table":
6707
7120
  return /* @__PURE__ */ jsxRuntime.jsx(TableItemPlugin, { disabled: !isEditable || props.readOnly }, key);
6708
7121
  case "Image":
@@ -6710,7 +7123,9 @@ var ToolBarPlugins = (props) => {
6710
7123
  InsertImageDialog,
6711
7124
  {
6712
7125
  activeEditor: editor,
6713
- disabled: !isEditable || props.readOnly
7126
+ disabled: !isEditable || props.readOnly,
7127
+ maxImageSizeMB: props.maxImageSizeMB,
7128
+ validationMessages: props.validationMessages
6714
7129
  },
6715
7130
  key
6716
7131
  );
@@ -6719,139 +7134,405 @@ var ToolBarPlugins = (props) => {
6719
7134
  InsertInlineImageDialog,
6720
7135
  {
6721
7136
  activeEditor: editor,
6722
- disabled: !isEditable || props.readOnly
7137
+ disabled: !isEditable || props.readOnly,
7138
+ maxImageSizeMB: props.maxImageSizeMB,
7139
+ validationMessages: props.validationMessages
6723
7140
  },
6724
7141
  key
6725
7142
  );
6726
7143
  case "Youtube":
6727
7144
  return /* @__PURE__ */ jsxRuntime.jsx(YoutubeUploadPlugin, { disabled: !isEditable || props.readOnly }, key);
6728
- case "Insert": {
6729
- const menuIconColor = isDisabled ? fgDisabled : fg;
6730
- return /* @__PURE__ */ jsxRuntime.jsxs(React9__namespace.default.Fragment, { children: [
6731
- /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Menu, { children: [
6732
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
6733
- reactComponents.Button,
6734
- {
6735
- size: "small",
6736
- disabled: isDisabled,
6737
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.AddRegular, { style: { color: menuIconColor } }),
6738
- style: {
6739
- ...getButtonStyle(false),
6740
- gap: 4,
6741
- paddingInline: 8
6742
- },
6743
- children: "Insert"
6744
- }
6745
- ) }),
6746
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuPopover, { children: /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.MenuList, { children: [
6747
- /* @__PURE__ */ jsxRuntime.jsx(
6748
- reactComponents.MenuItem,
6749
- {
6750
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TableAddRegular, { style: { color: menuIconColor } }),
6751
- onClick: () => !isDisabled && setActiveInsertDialog("table"),
6752
- children: "Table"
6753
- }
6754
- ),
6755
- /* @__PURE__ */ jsxRuntime.jsx(
6756
- reactComponents.MenuItem,
6757
- {
6758
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ImageAddRegular, { style: { color: menuIconColor } }),
6759
- onClick: () => !isDisabled && setActiveInsertDialog("image"),
6760
- children: "Image"
6761
- }
6762
- ),
6763
- /* @__PURE__ */ jsxRuntime.jsx(
6764
- reactComponents.MenuItem,
6765
- {
6766
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ImageEditRegular, { style: { color: menuIconColor } }),
6767
- onClick: () => !isDisabled && setActiveInsertDialog("inlineImage"),
6768
- children: "Inline Image"
6769
- }
6770
- ),
6771
- /* @__PURE__ */ jsxRuntime.jsx(
6772
- reactComponents.MenuItem,
6773
- {
6774
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.VideoClipRegular, { style: { color: menuIconColor } }),
6775
- onClick: () => !isDisabled && setActiveInsertDialog("youtube"),
6776
- children: "YouTube"
6777
- }
6778
- )
6779
- ] }) })
6780
- ] }),
6781
- /* @__PURE__ */ jsxRuntime.jsx(
6782
- TableItemPlugin,
6783
- {
6784
- disabled: isDisabled,
6785
- open: activeInsertDialog === "table",
6786
- onClose: () => setActiveInsertDialog(null)
6787
- }
6788
- ),
6789
- /* @__PURE__ */ jsxRuntime.jsx(
6790
- InsertImageDialog,
6791
- {
6792
- activeEditor: editor,
6793
- disabled: isDisabled,
6794
- open: activeInsertDialog === "image",
6795
- onClose: () => setActiveInsertDialog(null)
6796
- }
6797
- ),
6798
- /* @__PURE__ */ jsxRuntime.jsx(
6799
- InsertInlineImageDialog,
6800
- {
6801
- activeEditor: editor,
6802
- disabled: isDisabled,
6803
- open: activeInsertDialog === "inlineImage",
6804
- onClose: () => setActiveInsertDialog(null)
6805
- }
6806
- ),
6807
- /* @__PURE__ */ jsxRuntime.jsx(
6808
- YoutubeUploadPlugin,
6809
- {
6810
- disabled: isDisabled,
6811
- open: activeInsertDialog === "youtube",
6812
- onClose: () => setActiveInsertDialog(null)
6813
- }
6814
- )
6815
- ] }, key);
6816
- }
6817
7145
  case "Heading": {
6818
7146
  const headingLabel = selectNodeType === "paragraph" ? "Normal" : HEADING_OPTION.find((h) => h.key === selectNodeType)?.text ?? selectNodeType;
6819
7147
  return /* @__PURE__ */ jsxRuntime.jsxs(
6820
7148
  reactComponents.Dropdown,
6821
7149
  {
6822
- title: "Add heading",
6823
- id: `heading-option-${groupIndex}`,
6824
- placeholder: "Format",
6825
- disabled: !isEditable,
6826
- style: { minWidth: "100px" },
6827
- className: styles.dropdown,
6828
- value: headingLabel,
6829
- selectedOptions: [selectNodeType],
6830
- onOptionSelect: (_, data) => {
6831
- const val = data.optionValue;
6832
- if (!val) return;
6833
- if (val === "paragraph") {
6834
- applyToBlock((sel) => selection.$setBlocksType(sel, () => lexical.$createParagraphNode()));
7150
+ title: "Add heading",
7151
+ id: `heading-option-${groupIndex}`,
7152
+ placeholder: "Format",
7153
+ disabled: !isEditable,
7154
+ style: { minWidth: "100px" },
7155
+ className: styles.dropdown,
7156
+ value: headingLabel,
7157
+ selectedOptions: [selectNodeType],
7158
+ onOptionSelect: (_, data) => {
7159
+ const val = data.optionValue;
7160
+ if (!val) return;
7161
+ if (val === "paragraph") {
7162
+ formatParagraph(editor);
7163
+ setSelectNodeType("paragraph");
7164
+ } else {
7165
+ updateHeading(val);
7166
+ setSelectNodeType(val);
7167
+ }
7168
+ },
7169
+ children: [
7170
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { value: "paragraph", children: "Normal" }),
7171
+ HEADING_OPTION.map((option, idx) => /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { value: option.key, children: option.text }, `${option.key}-${idx}`))
7172
+ ]
7173
+ },
7174
+ key
7175
+ );
7176
+ }
7177
+ case "FontFamily":
7178
+ return /* @__PURE__ */ jsxRuntime.jsx(FontFamilyPlugin, { disabled: !isEditable || props.readOnly }, key);
7179
+ case "FontSize":
7180
+ return /* @__PURE__ */ jsxRuntime.jsx(FontSizePlugin, { disabled: !isEditable || props.readOnly }, key);
7181
+ case "|":
7182
+ return /* @__PURE__ */ jsxRuntime.jsx(reactComponents.ToolbarDivider, {}, key);
7183
+ // ── Standalone text-format toggle buttons ─────────────────────────────
7184
+ // These were previously only accessible inside the 'Decorators' dropdown.
7185
+ // Use them directly in customToolbar to show individual buttons instead.
7186
+ case "Strikethrough":
7187
+ return /* @__PURE__ */ jsxRuntime.jsx(
7188
+ reactComponents.Button,
7189
+ {
7190
+ size: "small",
7191
+ "aria-label": "Strikethrough",
7192
+ "aria-pressed": isStrikethrough,
7193
+ disabled: !isEditable || props.readOnly,
7194
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextStrikethroughFilled, { style: { color: getIconColor(isStrikethrough) } }),
7195
+ style: getButtonStyle(isStrikethrough),
7196
+ onClick: () => onHandleSelectOption("strike" /* Strikethrough */)
7197
+ },
7198
+ key
7199
+ );
7200
+ case "Subscript":
7201
+ return /* @__PURE__ */ jsxRuntime.jsx(
7202
+ reactComponents.Button,
7203
+ {
7204
+ size: "small",
7205
+ "aria-label": "Subscript",
7206
+ "aria-pressed": isSubscript,
7207
+ disabled: !isEditable || props.readOnly,
7208
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextSubscriptFilled, { style: { color: getIconColor(isSubscript) } }),
7209
+ style: getButtonStyle(isSubscript),
7210
+ onClick: () => onHandleSelectOption("subscript" /* Subscript */)
7211
+ },
7212
+ key
7213
+ );
7214
+ case "Superscript":
7215
+ return /* @__PURE__ */ jsxRuntime.jsx(
7216
+ reactComponents.Button,
7217
+ {
7218
+ size: "small",
7219
+ "aria-label": "Superscript",
7220
+ "aria-pressed": isSuperscript,
7221
+ disabled: !isEditable || props.readOnly,
7222
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextSuperscriptFilled, { style: { color: getIconColor(isSuperscript) } }),
7223
+ style: getButtonStyle(isSuperscript),
7224
+ onClick: () => onHandleSelectOption("superscript" /* Superscript */)
7225
+ },
7226
+ key
7227
+ );
7228
+ case "Highlight":
7229
+ return /* @__PURE__ */ jsxRuntime.jsx(
7230
+ reactComponents.Button,
7231
+ {
7232
+ size: "small",
7233
+ "aria-label": "Highlight",
7234
+ "aria-pressed": isHighlight,
7235
+ disabled: !isEditable || props.readOnly,
7236
+ icon: /* @__PURE__ */ jsxRuntime.jsx(
7237
+ reactIcons.HighlightAccentFilled,
7238
+ {
7239
+ style: { color: isEditable ? isHighlight ? brand : fg : fgDisabled }
7240
+ }
7241
+ ),
7242
+ style: getButtonStyle(isHighlight),
7243
+ onClick: () => onHandleSelectOption("highlight" /* Highlight */)
7244
+ },
7245
+ key
7246
+ );
7247
+ case "Uppercase":
7248
+ return /* @__PURE__ */ jsxRuntime.jsx(
7249
+ reactComponents.Button,
7250
+ {
7251
+ size: "small",
7252
+ "aria-label": "Uppercase",
7253
+ "aria-pressed": isUppercase,
7254
+ disabled: !isEditable || props.readOnly,
7255
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextCaseUppercaseFilled, { style: { color: getIconColor(isUppercase) } }),
7256
+ style: getButtonStyle(isUppercase),
7257
+ onClick: () => editor.dispatchCommand(lexical.FORMAT_TEXT_COMMAND, "uppercase")
7258
+ },
7259
+ key
7260
+ );
7261
+ case "Lowercase":
7262
+ return /* @__PURE__ */ jsxRuntime.jsx(
7263
+ reactComponents.Button,
7264
+ {
7265
+ size: "small",
7266
+ "aria-label": "Lowercase",
7267
+ "aria-pressed": isLowercase,
7268
+ disabled: !isEditable || props.readOnly,
7269
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextCaseLowercaseFilled, { style: { color: getIconColor(isLowercase) } }),
7270
+ style: getButtonStyle(isLowercase),
7271
+ onClick: () => onHandleSelectOption("lowercase" /* Lowercase */)
7272
+ },
7273
+ key
7274
+ );
7275
+ case "Capitalize":
7276
+ return /* @__PURE__ */ jsxRuntime.jsx(
7277
+ reactComponents.Button,
7278
+ {
7279
+ size: "small",
7280
+ "aria-label": "Capitalize",
7281
+ "aria-pressed": isCapitalize,
7282
+ disabled: !isEditable || props.readOnly,
7283
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextCaseTitleFilled, { style: { color: getIconColor(isCapitalize) } }),
7284
+ style: getButtonStyle(isCapitalize),
7285
+ onClick: () => onHandleSelectOption("capitalize" /* Capitalize */)
7286
+ },
7287
+ key
7288
+ );
7289
+ // ── Standalone list toggle buttons ────────────────────────────────────
7290
+ case "BulletList":
7291
+ return /* @__PURE__ */ jsxRuntime.jsx(
7292
+ reactComponents.Button,
7293
+ {
7294
+ size: "small",
7295
+ "aria-label": "Bullet list",
7296
+ "aria-pressed": selectNodeType === "ul",
7297
+ disabled: !isEditable || props.readOnly,
7298
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextBulletListLtrFilled, { style: { color: getIconColor(selectNodeType === "ul") } }),
7299
+ style: getButtonStyle(selectNodeType === "ul"),
7300
+ onClick: () => editor.dispatchCommand(
7301
+ selectNodeType === "ul" ? list.REMOVE_LIST_COMMAND : list.INSERT_UNORDERED_LIST_COMMAND,
7302
+ void 0
7303
+ )
7304
+ },
7305
+ key
7306
+ );
7307
+ case "NumberList":
7308
+ return /* @__PURE__ */ jsxRuntime.jsx(
7309
+ reactComponents.Button,
7310
+ {
7311
+ size: "small",
7312
+ "aria-label": "Number list",
7313
+ "aria-pressed": selectNodeType === "ol",
7314
+ disabled: !isEditable || props.readOnly,
7315
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextNumberListLtrFilled, { style: { color: getIconColor(selectNodeType === "ol") } }),
7316
+ style: getButtonStyle(selectNodeType === "ol"),
7317
+ onClick: () => editor.dispatchCommand(
7318
+ selectNodeType === "ol" ? list.REMOVE_LIST_COMMAND : list.INSERT_ORDERED_LIST_COMMAND,
7319
+ void 0
7320
+ )
7321
+ },
7322
+ key
7323
+ );
7324
+ case "AlphabeticalList":
7325
+ return /* @__PURE__ */ jsxRuntime.jsx(
7326
+ reactComponents.Button,
7327
+ {
7328
+ size: "small",
7329
+ "aria-label": "Alphabetical list",
7330
+ "aria-pressed": selectNodeType === "alpha",
7331
+ disabled: !isEditable || props.readOnly,
7332
+ icon: /* @__PURE__ */ jsxRuntime.jsx(
7333
+ TextAlphaListLtrFilled,
7334
+ {
7335
+ style: { color: getIconColor(selectNodeType === "alpha") }
7336
+ }
7337
+ ),
7338
+ style: getButtonStyle(selectNodeType === "alpha"),
7339
+ onClick: () => editor.update(() => $toggleAlphaList())
7340
+ },
7341
+ key
7342
+ );
7343
+ // ── Standalone block buttons ──────────────────────────────────────────
7344
+ case "Quote":
7345
+ return /* @__PURE__ */ jsxRuntime.jsx(
7346
+ reactComponents.Button,
7347
+ {
7348
+ size: "small",
7349
+ "aria-label": "Quote",
7350
+ "aria-pressed": selectNodeType === "quote",
7351
+ disabled: !isEditable || props.readOnly,
7352
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.CommentQuoteRegular, { style: { color: getIconColor(selectNodeType === "quote") } }),
7353
+ style: getButtonStyle(selectNodeType === "quote"),
7354
+ onClick: () => formatQuote()
7355
+ },
7356
+ key
7357
+ );
7358
+ case "PageBreak":
7359
+ return /* @__PURE__ */ jsxRuntime.jsx(
7360
+ reactComponents.Button,
7361
+ {
7362
+ size: "small",
7363
+ "aria-label": "Page break",
7364
+ disabled: !isEditable || props.readOnly,
7365
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.DocumentPageBreakRegular, { style: { color: getIconColor() } }),
7366
+ style: getButtonStyle(),
7367
+ onClick: () => editor.dispatchCommand(INSERT_PAGE_BREAK, void 0)
7368
+ },
7369
+ key
7370
+ );
7371
+ // ── Standalone heading-level toggle buttons ───────────────────────────
7372
+ // Each button sets (or toggles off) that specific heading level.
7373
+ // Clicking an active heading reverts to normal paragraph.
7374
+ case "H1":
7375
+ return /* @__PURE__ */ jsxRuntime.jsx(
7376
+ reactComponents.Button,
7377
+ {
7378
+ size: "small",
7379
+ "aria-label": "Heading 1",
7380
+ "aria-pressed": selectNodeType === "h1",
7381
+ disabled: !isEditable || props.readOnly,
7382
+ style: {
7383
+ ...getButtonStyle(selectNodeType === "h1"),
7384
+ color: getIconColor(selectNodeType === "h1"),
7385
+ fontSize: 12,
7386
+ fontWeight: 600
7387
+ },
7388
+ onClick: () => {
7389
+ if (selectNodeType === "h1") {
7390
+ formatParagraph(editor);
7391
+ setSelectNodeType("paragraph");
7392
+ } else {
7393
+ updateHeading("h1");
7394
+ setSelectNodeType("h1");
7395
+ }
7396
+ },
7397
+ children: "H1"
7398
+ },
7399
+ key
7400
+ );
7401
+ case "H2":
7402
+ return /* @__PURE__ */ jsxRuntime.jsx(
7403
+ reactComponents.Button,
7404
+ {
7405
+ size: "small",
7406
+ "aria-label": "Heading 2",
7407
+ "aria-pressed": selectNodeType === "h2",
7408
+ disabled: !isEditable || props.readOnly,
7409
+ style: {
7410
+ ...getButtonStyle(selectNodeType === "h2"),
7411
+ color: getIconColor(selectNodeType === "h2"),
7412
+ fontSize: 12,
7413
+ fontWeight: 600
7414
+ },
7415
+ onClick: () => {
7416
+ if (selectNodeType === "h2") {
7417
+ formatParagraph(editor);
7418
+ setSelectNodeType("paragraph");
7419
+ } else {
7420
+ updateHeading("h2");
7421
+ setSelectNodeType("h2");
7422
+ }
7423
+ },
7424
+ children: "H2"
7425
+ },
7426
+ key
7427
+ );
7428
+ case "H3":
7429
+ return /* @__PURE__ */ jsxRuntime.jsx(
7430
+ reactComponents.Button,
7431
+ {
7432
+ size: "small",
7433
+ "aria-label": "Heading 3",
7434
+ "aria-pressed": selectNodeType === "h3",
7435
+ disabled: !isEditable || props.readOnly,
7436
+ style: {
7437
+ ...getButtonStyle(selectNodeType === "h3"),
7438
+ color: getIconColor(selectNodeType === "h3"),
7439
+ fontSize: 12,
7440
+ fontWeight: 600
7441
+ },
7442
+ onClick: () => {
7443
+ if (selectNodeType === "h3") {
7444
+ formatParagraph(editor);
7445
+ setSelectNodeType("paragraph");
7446
+ } else {
7447
+ updateHeading("h3");
7448
+ setSelectNodeType("h3");
7449
+ }
7450
+ },
7451
+ children: "H3"
7452
+ },
7453
+ key
7454
+ );
7455
+ case "H4":
7456
+ return /* @__PURE__ */ jsxRuntime.jsx(
7457
+ reactComponents.Button,
7458
+ {
7459
+ size: "small",
7460
+ "aria-label": "Heading 4",
7461
+ "aria-pressed": selectNodeType === "h4",
7462
+ disabled: !isEditable || props.readOnly,
7463
+ style: {
7464
+ ...getButtonStyle(selectNodeType === "h4"),
7465
+ color: getIconColor(selectNodeType === "h4"),
7466
+ fontSize: 12,
7467
+ fontWeight: 600
7468
+ },
7469
+ onClick: () => {
7470
+ if (selectNodeType === "h4") {
7471
+ formatParagraph(editor);
7472
+ setSelectNodeType("paragraph");
7473
+ } else {
7474
+ updateHeading("h4");
7475
+ setSelectNodeType("h4");
7476
+ }
7477
+ },
7478
+ children: "H4"
7479
+ },
7480
+ key
7481
+ );
7482
+ case "H5":
7483
+ return /* @__PURE__ */ jsxRuntime.jsx(
7484
+ reactComponents.Button,
7485
+ {
7486
+ size: "small",
7487
+ "aria-label": "Heading 5",
7488
+ "aria-pressed": selectNodeType === "h5",
7489
+ disabled: !isEditable || props.readOnly,
7490
+ style: {
7491
+ ...getButtonStyle(selectNodeType === "h5"),
7492
+ color: getIconColor(selectNodeType === "h5"),
7493
+ fontSize: 12,
7494
+ fontWeight: 600
7495
+ },
7496
+ onClick: () => {
7497
+ if (selectNodeType === "h5") {
7498
+ formatParagraph(editor);
6835
7499
  setSelectNodeType("paragraph");
6836
7500
  } else {
6837
- updateHeading(val);
6838
- setSelectNodeType(val);
7501
+ updateHeading("h5");
7502
+ setSelectNodeType("h5");
6839
7503
  }
6840
7504
  },
6841
- children: [
6842
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { value: "paragraph", children: "Normal" }),
6843
- HEADING_OPTION.map((option, idx) => /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { value: option.key, children: option.text }, `${option.key}-${idx}`))
6844
- ]
7505
+ children: "H5"
7506
+ },
7507
+ key
7508
+ );
7509
+ case "H6":
7510
+ return /* @__PURE__ */ jsxRuntime.jsx(
7511
+ reactComponents.Button,
7512
+ {
7513
+ size: "small",
7514
+ "aria-label": "Heading 6",
7515
+ "aria-pressed": selectNodeType === "h6",
7516
+ disabled: !isEditable || props.readOnly,
7517
+ style: {
7518
+ ...getButtonStyle(selectNodeType === "h6"),
7519
+ color: getIconColor(selectNodeType === "h6"),
7520
+ fontSize: 12,
7521
+ fontWeight: 600
7522
+ },
7523
+ onClick: () => {
7524
+ if (selectNodeType === "h6") {
7525
+ formatParagraph(editor);
7526
+ setSelectNodeType("paragraph");
7527
+ } else {
7528
+ updateHeading("h6");
7529
+ setSelectNodeType("h6");
7530
+ }
7531
+ },
7532
+ children: "H6"
6845
7533
  },
6846
7534
  key
6847
7535
  );
6848
- }
6849
- case "FontFamily":
6850
- return /* @__PURE__ */ jsxRuntime.jsx(FontFamilyPlugin, { disabled: !isEditable || props.readOnly }, key);
6851
- case "FontSize":
6852
- return /* @__PURE__ */ jsxRuntime.jsx(FontSizePlugin, { disabled: !isEditable || props.readOnly }, key);
6853
- case "|":
6854
- return /* @__PURE__ */ jsxRuntime.jsx(reactComponents.ToolbarDivider, {}, key);
6855
7536
  case "Decorators": {
6856
7537
  const activeDecorators = [
6857
7538
  ...isUppercase ? ["uppercase"] : [],
@@ -6863,6 +7544,7 @@ var ToolBarPlugins = (props) => {
6863
7544
  ...isHighlight ? ["highlight"] : [],
6864
7545
  ...selectNodeType === "ul" ? ["ul-list"] : [],
6865
7546
  ...selectNodeType === "ol" ? ["ol-list"] : [],
7547
+ ...selectNodeType === "alpha" ? ["al-list"] : [],
6866
7548
  ...selectNodeType === "quote" ? ["quote"] : []
6867
7549
  ];
6868
7550
  const DECORATOR_LABEL = {
@@ -6875,6 +7557,7 @@ var ToolBarPlugins = (props) => {
6875
7557
  highlight: "Highlight",
6876
7558
  "ul-list": "Bullet list",
6877
7559
  "ol-list": "Number list",
7560
+ "al-list": "Alphabetical list",
6878
7561
  quote: "Quote"
6879
7562
  };
6880
7563
  const decoratorValue = activeDecorators.length === 0 ? "" : activeDecorators.length === 1 ? DECORATOR_LABEL[activeDecorators[0]] : `${DECORATOR_LABEL[activeDecorators[0]]} +${activeDecorators.length - 1}`;
@@ -6891,7 +7574,16 @@ var ToolBarPlugins = (props) => {
6891
7574
  button: { style: dropdownButtonStyle },
6892
7575
  expandIcon: { style: dropdownExpandIconStyle },
6893
7576
  listbox: { style: { minInlineSize: "180px" } },
7577
+ open: decoratorOpen,
7578
+ onOpenChange: (_, data) => {
7579
+ if (decoratorSelectingRef.current) {
7580
+ decoratorSelectingRef.current = false;
7581
+ return;
7582
+ }
7583
+ setDecoratorOpen(data.open);
7584
+ },
6894
7585
  onOptionSelect: (_, data) => {
7586
+ decoratorSelectingRef.current = true;
6895
7587
  switch (data.optionValue) {
6896
7588
  case "uppercase":
6897
7589
  editor.dispatchCommand(lexical.FORMAT_TEXT_COMMAND, "uppercase");
@@ -6915,10 +7607,19 @@ var ToolBarPlugins = (props) => {
6915
7607
  onHandleSelectOption("highlight" /* Highlight */);
6916
7608
  break;
6917
7609
  case "ul-list":
6918
- editor.dispatchCommand(selectNodeType === "ul" ? list.REMOVE_LIST_COMMAND : list.INSERT_UNORDERED_LIST_COMMAND, void 0);
7610
+ editor.dispatchCommand(
7611
+ selectNodeType === "ul" ? list.REMOVE_LIST_COMMAND : list.INSERT_UNORDERED_LIST_COMMAND,
7612
+ void 0
7613
+ );
6919
7614
  break;
6920
7615
  case "ol-list":
6921
- editor.dispatchCommand(selectNodeType === "ol" ? list.REMOVE_LIST_COMMAND : list.INSERT_ORDERED_LIST_COMMAND, void 0);
7616
+ editor.dispatchCommand(
7617
+ selectNodeType === "ol" ? list.REMOVE_LIST_COMMAND : list.INSERT_ORDERED_LIST_COMMAND,
7618
+ void 0
7619
+ );
7620
+ break;
7621
+ case "al-list":
7622
+ editor.update(() => $toggleAlphaList());
6922
7623
  break;
6923
7624
  case "page-break":
6924
7625
  editor.dispatchCommand(INSERT_PAGE_BREAK, void 0);
@@ -6954,7 +7655,12 @@ var ToolBarPlugins = (props) => {
6954
7655
  "Superscript"
6955
7656
  ] }),
6956
7657
  /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Option, { value: "highlight", text: "Highlight", children: [
6957
- /* @__PURE__ */ jsxRuntime.jsx(reactIcons.HighlightAccentFilled, { style: { ...optionIconStyle, color: isEditable ? brand : fgDisabled } }),
7658
+ /* @__PURE__ */ jsxRuntime.jsx(
7659
+ reactIcons.HighlightAccentFilled,
7660
+ {
7661
+ style: { ...optionIconStyle, color: isEditable ? brand : fgDisabled }
7662
+ }
7663
+ ),
6958
7664
  "Highlight"
6959
7665
  ] }),
6960
7666
  /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Option, { value: "ul-list", text: "Bullet list", children: [
@@ -6965,6 +7671,10 @@ var ToolBarPlugins = (props) => {
6965
7671
  /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextNumberListLtrFilled, { style: optionIconStyle }),
6966
7672
  "Number list"
6967
7673
  ] }),
7674
+ /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Option, { value: "al-list", text: "Alphabetical list", children: [
7675
+ /* @__PURE__ */ jsxRuntime.jsx(TextAlphaListLtrFilled, { style: optionIconStyle }),
7676
+ "Alphabetical list"
7677
+ ] }),
6968
7678
  /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Option, { value: "page-break", text: "Page Break", children: [
6969
7679
  /* @__PURE__ */ jsxRuntime.jsx(reactIcons.DocumentPageBreakRegular, { style: optionIconStyle }),
6970
7680
  "Page break"
@@ -6990,10 +7700,30 @@ var ToolBarPlugins = (props) => {
6990
7700
  // );
6991
7701
  case "Align": {
6992
7702
  const ALIGN_OPTIONS = [
6993
- { value: "left", label: "Left Align", icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignLeftFilled, { style: optionIconStyle }), action: "leftAlign" /* LeftAlign */ },
6994
- { value: "center", label: "Center Align", icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignCenterFilled, { style: optionIconStyle }), action: "centerAlign" /* CenterAlign */ },
6995
- { value: "right", label: "Right Align", icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignRightFilled, { style: optionIconStyle }), action: "rightAlign" /* RightAlign */ },
6996
- { value: "justify", label: "Justify Align", icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignJustifyFilled, { style: optionIconStyle }), action: "justifyAlign" /* JustifyAlign */ }
7703
+ {
7704
+ value: "left",
7705
+ label: "Left Align",
7706
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignLeftFilled, { style: optionIconStyle }),
7707
+ action: "leftAlign" /* LeftAlign */
7708
+ },
7709
+ {
7710
+ value: "center",
7711
+ label: "Center Align",
7712
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignCenterFilled, { style: optionIconStyle }),
7713
+ action: "centerAlign" /* CenterAlign */
7714
+ },
7715
+ {
7716
+ value: "right",
7717
+ label: "Right Align",
7718
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignRightFilled, { style: optionIconStyle }),
7719
+ action: "rightAlign" /* RightAlign */
7720
+ },
7721
+ {
7722
+ value: "justify",
7723
+ label: "Justify Align",
7724
+ icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignJustifyFilled, { style: optionIconStyle }),
7725
+ action: "justifyAlign" /* JustifyAlign */
7726
+ }
6997
7727
  ];
6998
7728
  const alignLabel = ALIGN_OPTIONS.find((o) => o.value === alignment)?.label ?? "Left Align";
6999
7729
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -7209,32 +7939,31 @@ function BrowserSpellCheckPlugin({ enabled }) {
7209
7939
  }, [editor, enabled]);
7210
7940
  return null;
7211
7941
  }
7212
- function WordCountPlugin({
7213
- onCountChange
7214
- }) {
7215
- const [editor] = LexicalComposerContext.useLexicalComposerContext();
7216
- React9.useEffect(() => {
7217
- return editor.registerUpdateListener(({ editorState }) => {
7218
- editorState.read(() => {
7219
- const text = lexical.$getRoot().getTextContent();
7220
- const words = text.trim() === "" ? 0 : text.trim().split(/\s+/).length;
7221
- onCountChange(words);
7222
- });
7223
- });
7224
- }, [editor, onCountChange]);
7225
- return null;
7226
- }
7227
- function CharCountPlugin({
7228
- onCountChange
7942
+ function ContentMetricsPlugin({
7943
+ onMetricsChange
7229
7944
  }) {
7230
7945
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
7946
+ const prevRef = React9.useRef({ words: 0, chars: 0, images: 0, links: 0, tables: 0 });
7231
7947
  React9.useEffect(() => {
7232
- return editor.registerUpdateListener(({ editorState }) => {
7233
- editorState.read(() => {
7234
- onCountChange(lexical.$getRoot().getTextContent().length);
7235
- });
7948
+ return editor.registerUpdateListener(({ dirtyElements, dirtyLeaves }) => {
7949
+ if (dirtyElements.size === 0 && dirtyLeaves.size === 0) return;
7950
+ const root = editor.getRootElement();
7951
+ if (!root) return;
7952
+ const text = root.innerText ?? "";
7953
+ const trimmed = text.trim();
7954
+ const words = trimmed === "" ? 0 : trimmed.split(/\s+/).length;
7955
+ const chars = trimmed.length;
7956
+ const images = root.querySelectorAll("img").length;
7957
+ const links = root.querySelectorAll("a[href]").length;
7958
+ const tables = root.querySelectorAll("table").length;
7959
+ const prev = prevRef.current;
7960
+ if (prev.words !== words || prev.chars !== chars || prev.images !== images || prev.links !== links || prev.tables !== tables) {
7961
+ const next = { words, chars, images, links, tables };
7962
+ prevRef.current = next;
7963
+ onMetricsChange(next);
7964
+ }
7236
7965
  });
7237
- }, [editor, onCountChange]);
7966
+ }, [editor, onMetricsChange]);
7238
7967
  return null;
7239
7968
  }
7240
7969
  function FocusEventsPlugin({
@@ -7255,7 +7984,8 @@ function FocusEventsPlugin({
7255
7984
  const next = e.relatedTarget;
7256
7985
  const container = containerRef.current;
7257
7986
  const stillInside = !!next && (container ? container.contains(next) : root.contains(next));
7258
- if (stillInside) return;
7987
+ const isEditorPortal = !!next?.closest?.("[data-lexical-editor-portal]");
7988
+ if (stillInside || isEditorPortal) return;
7259
7989
  editor.update(() => {
7260
7990
  lexical.$setSelection(null);
7261
7991
  });
@@ -7310,10 +8040,7 @@ function _adaptRawSpell(data, text) {
7310
8040
  }
7311
8041
  const rawGrammar = data.grammar_correction ?? data.improved_text;
7312
8042
  const grammarCorrection = rawGrammar && rawGrammar.trim() !== text.trim() ? rawGrammar.trim() : void 0;
7313
- return {
7314
- issues: issues.sort((a, b) => a.offset - b.offset),
7315
- grammarCorrection
7316
- };
8043
+ return { issues: issues.sort((a, b) => a.offset - b.offset), grammarCorrection };
7317
8044
  }
7318
8045
  function _adaptRawSuggest(data) {
7319
8046
  if (!data) return null;
@@ -7368,387 +8095,460 @@ function _makeQueryFn(fn) {
7368
8095
  };
7369
8096
  };
7370
8097
  }
7371
- var ContentEditorComponent = React9.forwardRef((props, ref) => {
7372
- const isReadOnly = !!props.readOnly;
7373
- const resolvedSpellCheck = React9__namespace.default.useMemo(
7374
- () => props.spellCheckFn ? _makeSpellCheckFn(props.spellCheckFn) : props.useSpellCheck,
7375
- // eslint-disable-next-line react-hooks/exhaustive-deps
7376
- [props.spellCheckFn, props.useSpellCheck]
7377
- );
7378
- const resolvedQuery = React9__namespace.default.useMemo(
7379
- () => props.suggestFn ? _makeQueryFn(props.suggestFn) : props.useQuery,
7380
- // eslint-disable-next-line react-hooks/exhaustive-deps
7381
- [props.suggestFn, props.useQuery]
7382
- );
7383
- const [floatingAnchorElem, setFloatingAnchorElem] = React9.useState(null);
7384
- const [isLinkEditMode, setIsLinkEditMode] = React9.useState(false);
7385
- const [wordCount, setWordCount] = React9.useState(0);
7386
- const handleWordCount = React9.useCallback(
7387
- (count) => setWordCount(count),
7388
- []
7389
- );
7390
- const [charCount, setCharCount] = React9.useState(0);
7391
- const handleCharCount = React9.useCallback(
7392
- (count) => setCharCount(count),
7393
- []
7394
- );
7395
- const [refErrors, setRefErrors] = React9.useState([]);
7396
- const [pageSetup, setPageSetup] = React9.useState(DEFAULT_PAGE_SETUP);
7397
- const pageCanvas = resolvePageCanvasMetrics(pageSetup);
7398
- const contentEditableDomRef = React9.useRef(null);
7399
- const previousOverLimitRef = React9.useRef(false);
7400
- const focusedRef = React9.useRef(false);
7401
- const setFocused = (focused) => {
7402
- focusedRef.current = focused;
7403
- };
7404
- const containerRef = React9.useRef(null);
7405
- const onAnchorRef = (elem) => {
7406
- if (elem) setFloatingAnchorElem(elem);
7407
- };
7408
- const initialConfig = {
7409
- namespace: props.namespace,
7410
- theme,
7411
- onError: () => {
7412
- },
7413
- nodes: [
7414
- richText.HeadingNode,
7415
- richText.QuoteNode,
7416
- code.CodeHighlightNode,
7417
- code.CodeNode,
7418
- list.ListNode,
7419
- list.ListItemNode,
7420
- link.LinkNode,
7421
- link.AutoLinkNode,
7422
- table.TableNode,
7423
- table.TableRowNode,
7424
- table.TableCellNode,
7425
- ImageNode,
7426
- InlineImageNode,
7427
- YouTubeNode,
7428
- PageBreakNode,
7429
- AutocompleteNode,
7430
- SpellErrorNode,
7431
- HtmlBlockNode
7432
- ]
7433
- };
7434
- const EditorStyles = react.mergeStyleSets({
7435
- editorPlaceholder: {
7436
- color: "var(--colorNeutralForeground3, grey)",
7437
- position: "absolute",
7438
- top: props.level !== "none" /* None */ ? "17px" : "27px",
7439
- left: pageCanvas.paddingPx,
7440
- right: pageCanvas.paddingPx,
7441
- fontSize: "14px",
7442
- pointerEvents: "none",
7443
- userSelect: "none"
7444
- },
7445
- contentEditor: {
7446
- zIndex: 0,
7447
- flex: "auto",
7448
- outline: "none",
7449
- overflow: "auto",
7450
- marginTop: "0px",
7451
- position: "relative",
7452
- background: "var(--colorNeutralBackground1, #ffffff)",
7453
- justifyContent: "center",
7454
- height: props.contentHeight ?? "100%",
7455
- ...isReadOnly && {
7456
- cursor: "not-allowed",
7457
- opacity: 0.75,
7458
- userSelect: "text"
7459
- }
7460
- }
7461
- });
7462
- const urlRegExp = new RegExp(
7463
- /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)/
7464
- );
7465
- const validateUrl = (url) => url === "https://" || urlRegExp.test(url);
7466
- const handleReadOnlyClickCapture = (e) => {
7467
- if (!isReadOnly) return;
7468
- const target = e.target;
7469
- const anchor = target?.closest?.("a");
7470
- if (anchor) {
7471
- e.preventDefault();
7472
- e.stopPropagation();
7473
- }
7474
- };
7475
- const [touched, setTouched] = React9.useState(false);
7476
- const isOverLimit = props.wordLimit !== void 0 && wordCount > props.wordLimit;
7477
- const internalErrors = [];
7478
- if (isOverLimit) {
7479
- const m = props.errorMessages?.wordLimitExceeded;
7480
- internalErrors.push(
7481
- typeof m === "function" ? m(wordCount, props.wordLimit) : m ?? `Word limit exceeded (${wordCount} / ${props.wordLimit} words used)`
7482
- );
7483
- }
7484
- if (props.required && touched && wordCount === 0) {
7485
- internalErrors.push(
7486
- props.errorMessages?.required ?? "This field is required"
7487
- );
7488
- }
7489
- if (props.minWords !== void 0 && touched && wordCount < props.minWords) {
7490
- const m = props.errorMessages?.minWords;
7491
- internalErrors.push(
7492
- typeof m === "function" ? m(wordCount, props.minWords) : m ?? `Minimum ${props.minWords} words required (${wordCount} entered)`
7493
- );
7494
- }
7495
- if (props.maxChars !== void 0 && charCount > props.maxChars) {
7496
- const m = props.errorMessages?.maxCharsExceeded;
7497
- internalErrors.push(
7498
- typeof m === "function" ? m(charCount, props.maxChars) : m ?? `Character limit exceeded (${charCount} / ${props.maxChars} characters used)`
8098
+ function EditorReadyPlugin({ onReady }) {
8099
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
8100
+ const calledRef = React9.useRef(false);
8101
+ React9.useEffect(() => {
8102
+ if (!onReady || calledRef.current) return;
8103
+ calledRef.current = true;
8104
+ onReady(editor);
8105
+ }, [editor, onReady]);
8106
+ return null;
8107
+ }
8108
+ var ContentEditorComponent = React9.forwardRef(
8109
+ (props, ref) => {
8110
+ const isReadOnly = !!props.readOnly;
8111
+ const resolvedSpellCheck = React9__namespace.default.useMemo(
8112
+ () => props.spellCheckFn ? _makeSpellCheckFn(props.spellCheckFn) : props.useSpellCheck,
8113
+ // eslint-disable-next-line react-hooks/exhaustive-deps
8114
+ [props.spellCheckFn, props.useSpellCheck]
7499
8115
  );
7500
- }
7501
- if (props.minChars !== void 0 && touched && charCount < props.minChars && charCount > 0) {
7502
- const m = props.errorMessages?.minCharsRequired;
7503
- internalErrors.push(
7504
- typeof m === "function" ? m(charCount, props.minChars) : m ?? `Minimum ${props.minChars} characters required (${charCount} entered)`
8116
+ const resolvedQuery = React9__namespace.default.useMemo(
8117
+ () => props.suggestFn ? _makeQueryFn(props.suggestFn) : props.useQuery,
8118
+ // eslint-disable-next-line react-hooks/exhaustive-deps
8119
+ [props.suggestFn, props.useQuery]
7505
8120
  );
7506
- }
7507
- const allErrors = [
7508
- ...internalErrors,
7509
- ...props.errors ?? [],
7510
- ...refErrors
7511
- ];
7512
- const hasErrors = allErrors.length > 0;
7513
- const hasRedBorder = hasErrors;
7514
- React9.useEffect(() => {
7515
- if (props.wordLimit === void 0 || !props.onWordLimitExceeded) return;
7516
- const wasOverLimit = previousOverLimitRef.current;
7517
- if (isOverLimit !== wasOverLimit) {
7518
- props.onWordLimitExceeded({
7519
- wordCount,
7520
- wordLimit: props.wordLimit,
7521
- exceeded: isOverLimit
7522
- });
7523
- previousOverLimitRef.current = isOverLimit;
7524
- }
7525
- }, [isOverLimit, wordCount, props.wordLimit, props.onWordLimitExceeded]);
7526
- return /* @__PURE__ */ jsxRuntime.jsx(reactComponents.FluentProvider, { theme: reactComponents.webLightTheme, style: { height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(LexicalComposer.LexicalComposer, { initialConfig, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, style: { height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
7527
- react.Stack,
7528
- {
7529
- style: {
7530
- zIndex: 1e3,
7531
- background: "#fff",
7532
- borderRadius: "2px",
7533
- width: props.width ?? "100%",
7534
- height: props.height ?? "100%",
7535
- margin: props.margin ?? "5px auto",
7536
- border: `1px solid ${hasRedBorder ? "#c4272c" : "var(--colorNeutralStroke1, #ccced1)"}`,
7537
- transition: "border-color 0.2s",
7538
- display: "flex",
7539
- flexDirection: "column"
8121
+ const [floatingAnchorElem, setFloatingAnchorElem] = React9.useState(null);
8122
+ const [isLinkEditMode, setIsLinkEditMode] = React9.useState(false);
8123
+ const [metrics, setMetrics] = React9.useState({
8124
+ words: 0,
8125
+ chars: 0,
8126
+ images: 0,
8127
+ links: 0,
8128
+ tables: 0
8129
+ });
8130
+ const handleMetrics = React9.useCallback((m) => setMetrics(m), []);
8131
+ const [pageSetup, setPageSetup] = React9.useState(DEFAULT_PAGE_SETUP);
8132
+ const pageCanvas = resolvePageCanvasMetrics(pageSetup);
8133
+ const wordCount = metrics.words;
8134
+ const contentEditableDomRef = React9.useRef(null);
8135
+ const previousOverLimitRef = React9.useRef(false);
8136
+ const focusedRef = React9.useRef(false);
8137
+ const setFocused = (focused) => {
8138
+ focusedRef.current = focused;
8139
+ };
8140
+ const containerRef = React9.useRef(null);
8141
+ const onAnchorRef = (elem) => {
8142
+ if (elem) setFloatingAnchorElem(elem);
8143
+ };
8144
+ const initialConfig = React9__namespace.default.useMemo(() => {
8145
+ const config = {
8146
+ namespace: props.namespace ?? "",
8147
+ theme,
8148
+ onError: () => {
8149
+ },
8150
+ nodes: [
8151
+ richText.HeadingNode,
8152
+ richText.QuoteNode,
8153
+ code.CodeHighlightNode,
8154
+ code.CodeNode,
8155
+ list.ListNode,
8156
+ list.ListItemNode,
8157
+ AlphaListNode,
8158
+ link.LinkNode,
8159
+ link.AutoLinkNode,
8160
+ table.TableNode,
8161
+ table.TableRowNode,
8162
+ table.TableCellNode,
8163
+ ImageNode,
8164
+ InlineImageNode,
8165
+ YouTubeNode,
8166
+ PageBreakNode,
8167
+ AutocompleteNode,
8168
+ SpellErrorNode,
8169
+ HtmlBlockNode
8170
+ ]
8171
+ };
8172
+ props.onBeforeInitialize?.(config);
8173
+ return config;
8174
+ }, []);
8175
+ const EditorStyles = react.mergeStyleSets({
8176
+ editorPlaceholder: {
8177
+ color: "var(--colorNeutralForeground3, grey)",
8178
+ position: "absolute",
8179
+ top: props.level !== "none" /* None */ ? "17px" : "27px",
8180
+ left: pageCanvas.paddingPx,
8181
+ right: pageCanvas.paddingPx,
8182
+ fontSize: "14px",
8183
+ pointerEvents: "none",
8184
+ userSelect: "none"
7540
8185
  },
7541
- children: [
7542
- /* @__PURE__ */ jsxRuntime.jsx(
7543
- "div",
7544
- {
7545
- style: {
7546
- pointerEvents: isReadOnly ? "none" : "auto",
7547
- position: "sticky",
7548
- opacity: isReadOnly ? 0.85 : 1
7549
- },
7550
- children: /* @__PURE__ */ jsxRuntime.jsx(
7551
- ToolBarPlugins,
8186
+ contentEditor: {
8187
+ zIndex: 0,
8188
+ flex: "auto",
8189
+ outline: "none",
8190
+ overflow: "auto",
8191
+ marginTop: "0px",
8192
+ position: "relative",
8193
+ background: "var(--colorNeutralBackground1, #ffffff)",
8194
+ justifyContent: "center",
8195
+ height: props.contentHeight ?? "100%",
8196
+ ...isReadOnly && {
8197
+ cursor: "not-allowed",
8198
+ opacity: 0.75,
8199
+ userSelect: "text"
8200
+ }
8201
+ }
8202
+ });
8203
+ const urlRegExp = new RegExp(
8204
+ /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)/
8205
+ );
8206
+ const validateUrl = (url) => url === "https://" || urlRegExp.test(url);
8207
+ const handleReadOnlyClickCapture = (e) => {
8208
+ if (!isReadOnly) return;
8209
+ const target = e.target;
8210
+ const anchor = target?.closest?.("a");
8211
+ if (anchor) {
8212
+ e.preventDefault();
8213
+ e.stopPropagation();
8214
+ }
8215
+ };
8216
+ const isOverLimit = props.wordLimit !== void 0 && wordCount > props.wordLimit;
8217
+ React9.useEffect(() => {
8218
+ if (props.wordLimit === void 0 || !props.onWordLimitExceeded) return;
8219
+ const wasOverLimit = previousOverLimitRef.current;
8220
+ if (isOverLimit !== wasOverLimit) {
8221
+ props.onWordLimitExceeded({
8222
+ wordCount,
8223
+ wordLimit: props.wordLimit,
8224
+ exceeded: isOverLimit
8225
+ });
8226
+ previousOverLimitRef.current = isOverLimit;
8227
+ }
8228
+ }, [isOverLimit, wordCount, props.wordLimit, props.onWordLimitExceeded]);
8229
+ const validationErrors = React9__namespace.default.useMemo(() => {
8230
+ const errors = [];
8231
+ const custom = props.validationMessages ?? {};
8232
+ const { words, chars, images, links, tables } = metrics;
8233
+ const msg = (key, ...args) => {
8234
+ const override = custom[key];
8235
+ if (override !== void 0) {
8236
+ return typeof override === "function" ? override(...args) : override;
8237
+ }
8238
+ const def = DEFAULT_VALIDATION_MESSAGES[key];
8239
+ return typeof def === "function" ? def(...args) : def;
8240
+ };
8241
+ const requiredFired = props.required && words === 0;
8242
+ if (requiredFired) {
8243
+ errors.push({ type: "required", message: msg("required") });
8244
+ } else if (props.minWords !== void 0 && words < props.minWords) {
8245
+ errors.push({ type: "minWords", message: msg("minWords", words, props.minWords) });
8246
+ }
8247
+ const effectiveMaxWords = props.maxWords ?? props.wordLimit;
8248
+ if (effectiveMaxWords !== void 0 && words > effectiveMaxWords) {
8249
+ errors.push({ type: "maxWords", message: msg("maxWords", words, effectiveMaxWords) });
8250
+ }
8251
+ if (!requiredFired && props.minChars !== void 0 && chars < props.minChars) {
8252
+ errors.push({ type: "minChars", message: msg("minChars", chars, props.minChars) });
8253
+ }
8254
+ if (props.maxChars !== void 0 && chars > props.maxChars) {
8255
+ errors.push({ type: "maxChars", message: msg("maxChars", chars, props.maxChars) });
8256
+ }
8257
+ if (props.noImages && images > 0) {
8258
+ errors.push({ type: "noImages", message: msg("noImages") });
8259
+ } else if (props.maxImages !== void 0 && images > props.maxImages) {
8260
+ errors.push({ type: "maxImages", message: msg("maxImages", images, props.maxImages) });
8261
+ }
8262
+ if (props.noLinks && links > 0) {
8263
+ errors.push({ type: "noLinks", message: msg("noLinks") });
8264
+ } else if (props.maxLinks !== void 0 && links > props.maxLinks) {
8265
+ errors.push({ type: "maxLinks", message: msg("maxLinks", links, props.maxLinks) });
8266
+ }
8267
+ if (props.noTables && tables > 0) {
8268
+ errors.push({ type: "noTables", message: msg("noTables") });
8269
+ }
8270
+ return errors;
8271
+ }, [
8272
+ metrics,
8273
+ props.required,
8274
+ props.minWords,
8275
+ props.maxWords,
8276
+ props.wordLimit,
8277
+ props.minChars,
8278
+ props.maxChars,
8279
+ props.noImages,
8280
+ props.maxImages,
8281
+ props.noLinks,
8282
+ props.maxLinks,
8283
+ props.noTables,
8284
+ props.validationMessages
8285
+ ]);
8286
+ const previousErrorTypesRef = React9.useRef("");
8287
+ React9.useEffect(() => {
8288
+ if (!props.onValidationChange) return;
8289
+ const key = validationErrors.map((e) => e.type).join(",");
8290
+ if (key !== previousErrorTypesRef.current) {
8291
+ previousErrorTypesRef.current = key;
8292
+ props.onValidationChange(validationErrors);
8293
+ }
8294
+ }, [validationErrors, props.onValidationChange]);
8295
+ const trimmedErrorMessage = props.errorMessage?.trim() || "";
8296
+ const hasValidationError = validationErrors.length > 0 || !!trimmedErrorMessage;
8297
+ return /* @__PURE__ */ jsxRuntime.jsx(reactComponents.FluentProvider, { theme: reactComponents.webLightTheme, style: { height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsxs(LexicalComposer.LexicalComposer, { initialConfig, children: [
8298
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "lexical-rich-editor-root", style: { height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
8299
+ react.Stack,
8300
+ {
8301
+ style: {
8302
+ zIndex: 1e3,
8303
+ background: "#fff",
8304
+ borderRadius: "2px",
8305
+ width: props.width ?? "100%",
8306
+ height: props.height ?? "100%",
8307
+ margin: props.margin ?? "5px auto",
8308
+ border: `1px solid ${isOverLimit || hasValidationError ? "#c4272c" : "var(--colorNeutralStroke1, #ccced1)"}`,
8309
+ transition: "border-color 0.2s",
8310
+ display: "flex",
8311
+ flexDirection: "column"
8312
+ },
8313
+ children: [
8314
+ /* @__PURE__ */ jsxRuntime.jsx(
8315
+ "div",
7552
8316
  {
7553
- level: props.level ?? "basic" /* Basic */,
7554
- readOnly: props.readOnly,
7555
- pageSetup,
7556
- onPageSetupChange: setPageSetup
8317
+ className: "editor-toolbar-root",
8318
+ style: {
8319
+ pointerEvents: isReadOnly ? "none" : "auto",
8320
+ position: "sticky",
8321
+ opacity: isReadOnly ? 0.85 : 1
8322
+ },
8323
+ children: /* @__PURE__ */ jsxRuntime.jsx(
8324
+ ToolBarPlugins,
8325
+ {
8326
+ level: props.level ?? "basic" /* Basic */,
8327
+ customToolbar: props.customToolbar,
8328
+ readOnly: props.readOnly,
8329
+ pageSetup,
8330
+ onPageSetupChange: setPageSetup,
8331
+ maxImageSizeMB: props.maxImageSizeMB,
8332
+ validationMessages: props.validationMessages,
8333
+ setIsLinkEditMode
8334
+ }
8335
+ )
7557
8336
  }
7558
- )
7559
- }
7560
- ),
7561
- /* @__PURE__ */ jsxRuntime.jsxs(
7562
- "div",
7563
- {
7564
- style: {
7565
- position: "relative",
7566
- flexGrow: 1,
7567
- padding: "15px 0px",
7568
- overflowY: "scroll",
7569
- overflowX: "auto",
7570
- minWidth: 0,
7571
- background: pageCanvas.widthPx !== void 0 ? "#eef0f2" : void 0
7572
- },
7573
- onClickCapture: handleReadOnlyClickCapture,
7574
- children: [
7575
- /* @__PURE__ */ jsxRuntime.jsx(
7576
- LexicalRichTextPlugin.RichTextPlugin,
7577
- {
7578
- ErrorBoundary: LexicalErrorBoundary.LexicalErrorBoundary,
7579
- contentEditable: /* @__PURE__ */ jsxRuntime.jsx(
7580
- "div",
8337
+ ),
8338
+ /* @__PURE__ */ jsxRuntime.jsxs(
8339
+ "div",
8340
+ {
8341
+ style: {
8342
+ position: "relative",
8343
+ flexGrow: 1,
8344
+ padding: "15px 0px",
8345
+ overflowY: "scroll",
8346
+ overflowX: "auto",
8347
+ minWidth: 0,
8348
+ background: pageCanvas.widthPx !== void 0 ? "#eef0f2" : void 0
8349
+ },
8350
+ onClickCapture: handleReadOnlyClickCapture,
8351
+ children: [
8352
+ /* @__PURE__ */ jsxRuntime.jsx(
8353
+ LexicalRichTextPlugin.RichTextPlugin,
7581
8354
  {
7582
- className: "editor",
7583
- style: { height: "100%", position: "relative" },
7584
- ref: onAnchorRef,
7585
- children: /* @__PURE__ */ jsxRuntime.jsx(
7586
- LexicalContentEditable.ContentEditable,
8355
+ ErrorBoundary: LexicalErrorBoundary.LexicalErrorBoundary,
8356
+ contentEditable: /* @__PURE__ */ jsxRuntime.jsx(
8357
+ "div",
7587
8358
  {
7588
- ref: contentEditableDomRef,
7589
- className: react.css(EditorStyles.contentEditor),
7590
- style: {
7591
- paddingTop: props.level !== "none" /* None */ ? 0 : 10,
7592
- paddingLeft: pageCanvas.paddingPx,
7593
- paddingRight: pageCanvas.paddingPx,
7594
- maxWidth: pageCanvas.widthPx,
7595
- marginLeft: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
7596
- marginRight: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
7597
- 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
7598
- },
7599
- spellCheck: !resolvedSpellCheck,
7600
- autoCorrect: resolvedSpellCheck ? "off" : void 0,
7601
- autoCapitalize: resolvedSpellCheck ? "off" : void 0
8359
+ className: "editor",
8360
+ style: { height: "100%", position: "relative" },
8361
+ ref: onAnchorRef,
8362
+ children: /* @__PURE__ */ jsxRuntime.jsx(
8363
+ LexicalContentEditable.ContentEditable,
8364
+ {
8365
+ ref: contentEditableDomRef,
8366
+ className: react.css(EditorStyles.contentEditor),
8367
+ style: {
8368
+ paddingTop: props.level !== "none" /* None */ ? 0 : 10,
8369
+ paddingLeft: pageCanvas.paddingPx,
8370
+ paddingRight: pageCanvas.paddingPx,
8371
+ maxWidth: pageCanvas.widthPx,
8372
+ marginLeft: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
8373
+ marginRight: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
8374
+ 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
8375
+ },
8376
+ spellCheck: !resolvedSpellCheck,
8377
+ autoCorrect: resolvedSpellCheck ? "off" : void 0,
8378
+ autoCapitalize: resolvedSpellCheck ? "off" : void 0
8379
+ }
8380
+ )
7602
8381
  }
7603
- )
8382
+ ),
8383
+ placeholder: /* @__PURE__ */ jsxRuntime.jsx(react.Stack, { className: react.css(EditorStyles.editorPlaceholder), children: props.placeholder })
7604
8384
  }
7605
8385
  ),
7606
- placeholder: /* @__PURE__ */ jsxRuntime.jsx(react.Stack, { className: react.css(EditorStyles.editorPlaceholder), children: props.placeholder })
7607
- }
7608
- ),
7609
- props.wordLimit !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
7610
- "div",
7611
- {
7612
- style: {
7613
- position: "sticky",
7614
- bottom: 0,
7615
- display: "flex",
7616
- justifyContent: "flex-end",
7617
- paddingRight: 14,
7618
- pointerEvents: "none",
7619
- userSelect: "none"
7620
- },
7621
- children: /* @__PURE__ */ jsxRuntime.jsxs(
7622
- "span",
8386
+ (props.wordLimit !== void 0 || props.maxWords !== void 0 || props.minWords !== void 0 || props.maxChars !== void 0 || props.minChars !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs(
8387
+ "div",
7623
8388
  {
7624
8389
  style: {
7625
- fontSize: "11px",
7626
- color: isOverLimit ? "#c4272c" : "var(--colorNeutralForeground3, #aaa)",
7627
- fontWeight: isOverLimit ? 600 : 400,
7628
- transition: "color 0.2s, font-weight 0.2s"
8390
+ position: "sticky",
8391
+ bottom: 0,
8392
+ display: "flex",
8393
+ justifyContent: "flex-end",
8394
+ gap: 10,
8395
+ paddingRight: 14,
8396
+ pointerEvents: "none",
8397
+ userSelect: "none"
7629
8398
  },
7630
8399
  children: [
7631
- wordCount,
7632
- " / ",
7633
- props.wordLimit,
7634
- " words"
8400
+ (props.wordLimit !== void 0 || props.maxWords !== void 0 || props.minWords !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs(
8401
+ "span",
8402
+ {
8403
+ style: {
8404
+ fontSize: "11px",
8405
+ color: hasValidationError ? "#c4272c" : "var(--colorNeutralForeground3, #aaa)",
8406
+ fontWeight: hasValidationError ? 600 : 400,
8407
+ transition: "color 0.2s, font-weight 0.2s"
8408
+ },
8409
+ children: [
8410
+ wordCount,
8411
+ (props.maxWords ?? props.wordLimit) !== void 0 && ` / ${props.maxWords ?? props.wordLimit}`,
8412
+ props.minWords !== void 0 && props.maxWords === void 0 && props.wordLimit === void 0 && ` (min ${props.minWords})`,
8413
+ " words"
8414
+ ]
8415
+ }
8416
+ ),
8417
+ (props.maxChars !== void 0 || props.minChars !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs(
8418
+ "span",
8419
+ {
8420
+ style: {
8421
+ fontSize: "11px",
8422
+ color: hasValidationError ? "#c4272c" : "var(--colorNeutralForeground3, #aaa)",
8423
+ fontWeight: hasValidationError ? 600 : 400,
8424
+ transition: "color 0.2s, font-weight 0.2s"
8425
+ },
8426
+ children: [
8427
+ metrics.chars,
8428
+ props.maxChars !== void 0 && ` / ${props.maxChars}`,
8429
+ props.minChars !== void 0 && props.maxChars === void 0 && ` (min ${props.minChars})`,
8430
+ " chars"
8431
+ ]
8432
+ }
8433
+ )
7635
8434
  ]
7636
8435
  }
7637
8436
  )
8437
+ ]
8438
+ }
8439
+ ),
8440
+ /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyPlugin, { readonly: isReadOnly }),
8441
+ /* @__PURE__ */ jsxRuntime.jsx(BrowserSpellCheckPlugin, { enabled: !resolvedSpellCheck }),
8442
+ /* @__PURE__ */ jsxRuntime.jsx(
8443
+ FocusEventsPlugin,
8444
+ {
8445
+ onFocus: props.onFocus,
8446
+ onBlur: props.onBlur,
8447
+ setFocused,
8448
+ containerRef
8449
+ }
8450
+ ),
8451
+ props.autoFocus && !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(LexicalAutoFocusPlugin.AutoFocusPlugin, {}),
8452
+ /* @__PURE__ */ jsxRuntime.jsx(LexicalHistoryPlugin.HistoryPlugin, {}),
8453
+ /* @__PURE__ */ jsxRuntime.jsx(LexicalListPlugin.ListPlugin, {}),
8454
+ /* @__PURE__ */ jsxRuntime.jsx(LexicalLinkPlugin.LinkPlugin, { validateUrl }),
8455
+ /* @__PURE__ */ jsxRuntime.jsx(LexicalAutoLinkPlugin.AutoLinkPlugin, { matchers: MATCHERS }),
8456
+ /* @__PURE__ */ jsxRuntime.jsx(LexicalTablePlugin.TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true }),
8457
+ !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(YoutubeDeletePlugin, {}),
8458
+ !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsxRuntime.jsx(TableActionMenuPlugin, {}),
8459
+ !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsxRuntime.jsx(TableCellResizerPlugin, { anchorElem: floatingAnchorElem }),
8460
+ !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
8461
+ FloatingLinkEditorPlugin,
8462
+ {
8463
+ anchorElem: floatingAnchorElem,
8464
+ isLinkEditMode,
8465
+ setIsLinkEditMode
8466
+ }
8467
+ ),
8468
+ !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(ImagePlugin_default, {}),
8469
+ !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(InlineImage_default, {}),
8470
+ !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(PageBreakPlugin, {}),
8471
+ !!resolvedQuery && !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
8472
+ AutocompletePlugin,
8473
+ {
8474
+ useQuery: resolvedQuery,
8475
+ isReadOnly,
8476
+ onSuggestionShown: props.onSuggestionShown,
8477
+ onSuggestionAccept: props.onSuggestionAccept,
8478
+ idleMs: props.suggestIdleMs ?? 300,
8479
+ minWords: 4,
8480
+ prefixWindow: 300
8481
+ }
8482
+ ),
8483
+ !!resolvedSpellCheck && !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
8484
+ SpellCheckPlugin,
8485
+ {
8486
+ useSpellCheck: resolvedSpellCheck,
8487
+ onSpellCheckAccept: props.onSpellCheckAccept,
8488
+ idleMs: props.spellCheckIdleMs ?? 1200,
8489
+ enabled: props.spellCheckEnabled !== false
8490
+ }
8491
+ ),
8492
+ !isReadOnly && props.showFloatingToolbar && /* @__PURE__ */ jsxRuntime.jsx(CharacterStylesPopupPlugin, {}),
8493
+ /* @__PURE__ */ jsxRuntime.jsx(CustomOnChangePlugin, { value: props.value, onChange: props.onChange }),
8494
+ (props.wordLimit !== void 0 || props.required || props.minWords !== void 0 || props.maxWords !== void 0 || props.minChars !== void 0 || props.maxChars !== void 0 || props.noImages || props.maxImages !== void 0 || props.noLinks || props.maxLinks !== void 0 || props.noTables) && /* @__PURE__ */ jsxRuntime.jsx(ContentMetricsPlugin, { onMetricsChange: handleMetrics }),
8495
+ /* @__PURE__ */ jsxRuntime.jsx(
8496
+ RefApiPlugin,
8497
+ {
8498
+ forwardedRef: ref,
8499
+ contentEditableDomRef,
8500
+ focusedRef
8501
+ }
8502
+ ),
8503
+ (validationErrors.length > 0 || trimmedErrorMessage) && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flexShrink: 0, padding: "6px 20px 8px" }, children: [
8504
+ validationErrors.map((err) => /* @__PURE__ */ jsxRuntime.jsxs(
8505
+ "div",
8506
+ {
8507
+ style: {
8508
+ display: "flex",
8509
+ alignItems: "flex-start",
8510
+ gap: 6,
8511
+ marginTop: 2,
8512
+ color: "#c4272c",
8513
+ fontSize: 12,
8514
+ lineHeight: "18px"
8515
+ },
8516
+ children: [
8517
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, flexShrink: 0 }, children: "\u26A0" }),
8518
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: err.message })
8519
+ ]
8520
+ },
8521
+ err.type
8522
+ )),
8523
+ trimmedErrorMessage && /* @__PURE__ */ jsxRuntime.jsxs(
8524
+ "div",
8525
+ {
8526
+ style: {
8527
+ display: "flex",
8528
+ alignItems: "flex-start",
8529
+ gap: 6,
8530
+ marginTop: 2,
8531
+ color: "#c4272c",
8532
+ fontSize: 12,
8533
+ lineHeight: "18px"
8534
+ },
8535
+ children: [
8536
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, flexShrink: 0 }, children: "\u26A0" }),
8537
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: trimmedErrorMessage })
8538
+ ]
7638
8539
  }
7639
8540
  )
7640
- ]
7641
- }
7642
- ),
7643
- hasErrors && /* @__PURE__ */ jsxRuntime.jsx(
7644
- "div",
7645
- {
7646
- style: {
7647
- borderTop: "1px solid #fbd5d5",
7648
- background: "#fff8f8",
7649
- padding: "6px 12px 8px",
7650
- display: "flex",
7651
- flexDirection: "column",
7652
- gap: 4
7653
- },
7654
- children: allErrors.map((err, i) => /* @__PURE__ */ jsxRuntime.jsxs(
7655
- "div",
7656
- {
7657
- style: { display: "flex", alignItems: "center", gap: 6 },
7658
- children: [
7659
- /* @__PURE__ */ jsxRuntime.jsx(
7660
- reactIcons.ErrorCircleRegular,
7661
- {
7662
- style: { fontSize: 14, color: "#c4272c", flexShrink: 0 }
7663
- }
7664
- ),
7665
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#c4272c" }, children: err })
7666
- ]
7667
- },
7668
- i
7669
- ))
7670
- }
7671
- ),
7672
- /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyPlugin, { readonly: isReadOnly }),
7673
- /* @__PURE__ */ jsxRuntime.jsx(BrowserSpellCheckPlugin, { enabled: !resolvedSpellCheck }),
7674
- /* @__PURE__ */ jsxRuntime.jsx(
7675
- FocusEventsPlugin,
7676
- {
7677
- onFocus: props.onFocus,
7678
- onBlur: () => {
7679
- setTouched(true);
7680
- props.onBlur?.();
7681
- },
7682
- setFocused,
7683
- containerRef
7684
- }
7685
- ),
7686
- props.autoFocus && !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(LexicalAutoFocusPlugin.AutoFocusPlugin, {}),
7687
- /* @__PURE__ */ jsxRuntime.jsx(LexicalHistoryPlugin.HistoryPlugin, {}),
7688
- /* @__PURE__ */ jsxRuntime.jsx(LexicalListPlugin.ListPlugin, {}),
7689
- /* @__PURE__ */ jsxRuntime.jsx(LexicalLinkPlugin.LinkPlugin, { validateUrl }),
7690
- /* @__PURE__ */ jsxRuntime.jsx(LexicalAutoLinkPlugin.AutoLinkPlugin, { matchers: MATCHERS }),
7691
- /* @__PURE__ */ jsxRuntime.jsx(LexicalTablePlugin.TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true }),
7692
- !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(YoutubeDeletePlugin, {}),
7693
- !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsxRuntime.jsx(TableActionMenuPlugin, {}),
7694
- !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsxRuntime.jsx(TableCellResizerPlugin, { anchorElem: floatingAnchorElem }),
7695
- !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
7696
- FloatingLinkEditorPlugin,
7697
- {
7698
- anchorElem: floatingAnchorElem,
7699
- isLinkEditMode,
7700
- setIsLinkEditMode
7701
- }
7702
- ),
7703
- !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(ImagePlugin_default, {}),
7704
- !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(InlineImage_default, {}),
7705
- !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(PageBreakPlugin, {}),
7706
- !!resolvedQuery && !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
7707
- AutocompletePlugin,
7708
- {
7709
- useQuery: resolvedQuery,
7710
- isReadOnly,
7711
- onSuggestionShown: props.onSuggestionShown,
7712
- onSuggestionAccept: props.onSuggestionAccept,
7713
- idleMs: props.suggestIdleMs ?? 300,
7714
- minWords: 4,
7715
- prefixWindow: 300
7716
- }
7717
- ),
7718
- !!resolvedSpellCheck && !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
7719
- SpellCheckPlugin,
7720
- {
7721
- useSpellCheck: resolvedSpellCheck,
7722
- onSpellCheckAccept: props.onSpellCheckAccept,
7723
- idleMs: props.spellCheckIdleMs ?? 1200,
7724
- enabled: props.spellCheckEnabled !== false
7725
- }
7726
- ),
7727
- !isReadOnly && props.showFloatingToolbar && /* @__PURE__ */ jsxRuntime.jsx(CharacterStylesPopupPlugin, {}),
7728
- /* @__PURE__ */ jsxRuntime.jsx(
7729
- CustomOnChangePlugin,
7730
- {
7731
- value: props.value,
7732
- onChange: props.onChange
7733
- }
7734
- ),
7735
- (props.wordLimit !== void 0 || props.required || props.minWords !== void 0) && /* @__PURE__ */ jsxRuntime.jsx(WordCountPlugin, { onCountChange: handleWordCount }),
7736
- (props.maxChars !== void 0 || props.minChars !== void 0) && /* @__PURE__ */ jsxRuntime.jsx(CharCountPlugin, { onCountChange: handleCharCount }),
7737
- /* @__PURE__ */ jsxRuntime.jsx(
7738
- RefApiPlugin,
7739
- {
7740
- forwardedRef: ref,
7741
- contentEditableDomRef,
7742
- focusedRef,
7743
- setRefErrors
7744
- }
7745
- )
7746
- ]
7747
- }
7748
- ) }) }) });
7749
- });
8541
+ ] })
8542
+ ]
8543
+ }
8544
+ ) }),
8545
+ /* @__PURE__ */ jsxRuntime.jsx(EditorReadyPlugin, { onReady: props.onReady })
8546
+ ] }) });
8547
+ }
8548
+ );
7750
8549
 
7751
8550
  exports.ContentEditorComponent = ContentEditorComponent;
7752
8551
  exports.ContentEditorLevel = ContentEditorLevel;
8552
+ exports.DEFAULT_VALIDATION_MESSAGES = DEFAULT_VALIDATION_MESSAGES;
7753
8553
  //# sourceMappingURL=index.js.map
7754
8554
  //# sourceMappingURL=index.js.map