@tarviks/lexical-rich-editor 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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,37 +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
- maxWidth,
640
- onResizeStart,
641
- onResizeEnd,
642
- captionsEnabled: !isLoadError && captionsEnabled
643
- }
644
- )
645
- ] }) });
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
+ ) });
646
641
  };
647
642
  ImageComponent_default = ImageComponent;
648
643
  }
@@ -876,6 +871,7 @@ __export(InlineImageComponent_exports, {
876
871
  var imageCache2, useSuspenseImage2, LazyImage2, InlineImageComponent, InlineImageComponent_default;
877
872
  var init_InlineImageComponent = __esm({
878
873
  "src/Nodes/InlineImageComponent.tsx"() {
874
+ init_ImageResizer();
879
875
  init_InlineImage();
880
876
  init_InlineImageNode();
881
877
  imageCache2 = /* @__PURE__ */ new Set();
@@ -912,6 +908,7 @@ var init_InlineImageComponent = __esm({
912
908
  };
913
909
  InlineImageComponent = ({ src, altText, nodeKey, width, height, showCaption, caption, position }) => {
914
910
  const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection.useLexicalNodeSelection(nodeKey);
911
+ const [isResizing, setIsResizing] = React9.useState(false);
915
912
  const [selection, setSelection] = React9.useState(null);
916
913
  const activeEditorRef = React9.useRef(null);
917
914
  const imageRef = React9.useRef(null);
@@ -973,6 +970,25 @@ var init_InlineImageComponent = __esm({
973
970
  },
974
971
  [caption, editor, setSelected]
975
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
+ );
976
992
  React9.useEffect(() => {
977
993
  let isMounted = true;
978
994
  const unregister = utils.mergeRegister(
@@ -991,48 +1007,13 @@ var init_InlineImageComponent = __esm({
991
1007
  ),
992
1008
  editor.registerCommand(
993
1009
  lexical.CLICK_COMMAND,
994
- (payload) => {
995
- const event = payload;
996
- if (event.target === imageRef.current) {
997
- if (event.shiftKey) {
998
- setSelected(!isSelected);
999
- } else {
1000
- clearSelection();
1001
- setSelected(true);
1002
- }
1003
- return true;
1004
- }
1005
- return false;
1006
- },
1007
- lexical.COMMAND_PRIORITY_LOW
1008
- ),
1009
- editor.registerCommand(
1010
- lexical.DRAGSTART_COMMAND,
1011
- (event) => {
1012
- if (event.target === imageRef.current) {
1013
- event.preventDefault();
1014
- return true;
1015
- }
1016
- return false;
1017
- },
1018
- lexical.COMMAND_PRIORITY_LOW
1019
- ),
1020
- editor.registerCommand(
1021
- lexical.KEY_DELETE_COMMAND,
1022
- $onDelete,
1023
- lexical.COMMAND_PRIORITY_LOW
1024
- ),
1025
- editor.registerCommand(
1026
- lexical.KEY_BACKSPACE_COMMAND,
1027
- $onDelete,
1010
+ onClick,
1028
1011
  lexical.COMMAND_PRIORITY_LOW
1029
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),
1030
1015
  editor.registerCommand(lexical.KEY_ENTER_COMMAND, $onEnter, lexical.COMMAND_PRIORITY_LOW),
1031
- editor.registerCommand(
1032
- lexical.KEY_ESCAPE_COMMAND,
1033
- $onEscape,
1034
- lexical.COMMAND_PRIORITY_LOW
1035
- )
1016
+ editor.registerCommand(lexical.KEY_ESCAPE_COMMAND, $onEscape, lexical.COMMAND_PRIORITY_LOW)
1036
1017
  );
1037
1018
  return () => {
1038
1019
  isMounted = false;
@@ -1041,27 +1022,59 @@ var init_InlineImageComponent = __esm({
1041
1022
  }, [
1042
1023
  clearSelection,
1043
1024
  editor,
1025
+ isResizing,
1044
1026
  isSelected,
1045
1027
  nodeKey,
1046
1028
  $onDelete,
1047
1029
  $onEnter,
1048
1030
  $onEscape,
1031
+ onClick,
1049
1032
  setSelected
1050
1033
  ]);
1051
- const draggable = isSelected && lexical.$isNodeSelection(selection);
1052
- const isFocused = isSelected && isEditable;
1053
- return /* @__PURE__ */ jsxRuntime.jsx(React9.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx("span", { draggable, children: /* @__PURE__ */ jsxRuntime.jsx(
1054
- LazyImage2,
1055
- {
1056
- className: isFocused ? `focused ${lexical.$isNodeSelection(selection) ? "draggable" : ""}` : null,
1057
- src,
1058
- altText,
1059
- imageRef,
1060
- width,
1061
- height,
1062
- position
1063
- }
1064
- ) }) });
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;
1049
+ const isFocused = (isSelected || isResizing) && isEditable;
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(
1052
+ LazyImage2,
1053
+ {
1054
+ className: isFocused ? `focused ${lexical.$isNodeSelection(selection) ? "draggable" : ""}` : null,
1055
+ src,
1056
+ altText,
1057
+ imageRef,
1058
+ width,
1059
+ height,
1060
+ position
1061
+ }
1062
+ ),
1063
+ lexical.$isNodeSelection(selection) && isFocused && /* @__PURE__ */ jsxRuntime.jsx(
1064
+ ImageResizer_default,
1065
+ {
1066
+ showCaption: false,
1067
+ setShowCaption: () => {
1068
+ },
1069
+ captionsEnabled: false,
1070
+ editor,
1071
+ buttonRef,
1072
+ imageRef,
1073
+ onResizeStart,
1074
+ onResizeEnd
1075
+ }
1076
+ )
1077
+ ] }) });
1065
1078
  };
1066
1079
  InlineImageComponent_default = InlineImageComponent;
1067
1080
  }
@@ -1249,6 +1262,19 @@ var ContentEditorLevel = /* @__PURE__ */ ((ContentEditorLevel2) => {
1249
1262
  ContentEditorLevel2["Pro"] = "pro";
1250
1263
  return ContentEditorLevel2;
1251
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
+ };
1252
1278
 
1253
1279
  // src/Types/PageSetup.ts
1254
1280
  var DEFAULT_PAGE_SETUP = {
@@ -1289,6 +1315,106 @@ function resolvePageCanvasMetrics(value) {
1289
1315
  paddingPx: Math.round(margin.valueIn * CSS_PX_PER_INCH)
1290
1316
  };
1291
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
+ }
1292
1418
  var AutocompleteNode = class _AutocompleteNode extends lexical.TextNode {
1293
1419
  static getType() {
1294
1420
  return "autocomplete";
@@ -1299,6 +1425,13 @@ var AutocompleteNode = class _AutocompleteNode extends lexical.TextNode {
1299
1425
  static importJSON(serializedNode) {
1300
1426
  return new _AutocompleteNode(serializedNode.text, serializedNode.uuid);
1301
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
+ }
1302
1435
  exportJSON() {
1303
1436
  return { ...super.exportJSON(), uuid: this.__uuid };
1304
1437
  }
@@ -1613,13 +1746,20 @@ function VideoResizer({
1613
1746
  const dy = ev.clientY - rs.startY;
1614
1747
  let newW = rs.startW;
1615
1748
  let newH = rs.startH;
1616
- if (dir === "se") {
1749
+ if (dir === "se" || dir === "ne") {
1617
1750
  newW = Math.max(MIN_WIDTH, rs.startW + dx);
1618
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;
1619
1755
  } else if (dir === "e") {
1620
1756
  newW = Math.max(MIN_WIDTH, rs.startW + dx);
1757
+ } else if (dir === "w") {
1758
+ newW = Math.max(MIN_WIDTH, rs.startW - dx);
1621
1759
  } else if (dir === "s") {
1622
1760
  newH = Math.max(MIN_HEIGHT, rs.startH + dy);
1761
+ } else if (dir === "n") {
1762
+ newH = Math.max(MIN_HEIGHT, rs.startH - dy);
1623
1763
  }
1624
1764
  container.style.width = `${newW}px`;
1625
1765
  container.style.height = `${newH}px`;
@@ -1639,13 +1779,23 @@ function VideoResizer({
1639
1779
  /* @__PURE__ */ jsxRuntime.jsx(
1640
1780
  "div",
1641
1781
  {
1642
- style: {
1643
- ...handleBase,
1644
- top: "50%",
1645
- right: -5,
1646
- transform: "translateY(-50%)",
1647
- cursor: "ew-resize"
1648
- },
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" },
1649
1799
  onPointerDown: (e) => startResize(e, "e"),
1650
1800
  title: "Resize width"
1651
1801
  }
@@ -1653,13 +1803,15 @@ function VideoResizer({
1653
1803
  /* @__PURE__ */ jsxRuntime.jsx(
1654
1804
  "div",
1655
1805
  {
1656
- style: {
1657
- ...handleBase,
1658
- bottom: -5,
1659
- left: "50%",
1660
- transform: "translateX(-50%)",
1661
- cursor: "ns-resize"
1662
- },
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" },
1663
1815
  onPointerDown: (e) => startResize(e, "s"),
1664
1816
  title: "Resize height"
1665
1817
  }
@@ -1667,15 +1819,30 @@ function VideoResizer({
1667
1819
  /* @__PURE__ */ jsxRuntime.jsx(
1668
1820
  "div",
1669
1821
  {
1670
- style: { ...handleBase, bottom: -5, right: -5, cursor: "se-resize" },
1671
- 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"),
1672
1840
  title: "Resize (proportional)"
1673
1841
  }
1674
1842
  )
1675
1843
  ] });
1676
1844
  }
1677
1845
  function YouTubeComponent({
1678
- className,
1679
1846
  format,
1680
1847
  nodeKey,
1681
1848
  videoID,
@@ -1683,9 +1850,48 @@ function YouTubeComponent({
1683
1850
  height,
1684
1851
  editor
1685
1852
  }) {
1853
+ const wrapperRef = React9__namespace.useRef(null);
1686
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);
1687
1858
  const [isHovered, setIsHovered] = React9__namespace.useState(false);
1688
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]);
1689
1895
  const handleDelete = (e) => {
1690
1896
  e.preventDefault();
1691
1897
  e.stopPropagation();
@@ -1694,71 +1900,172 @@ function YouTubeComponent({
1694
1900
  node?.remove();
1695
1901
  });
1696
1902
  };
1903
+ const handleResizeStart = () => {
1904
+ isResizingRef.current = true;
1905
+ setIsResizing(true);
1906
+ if (iframeRef.current) iframeRef.current.style.pointerEvents = "none";
1907
+ };
1697
1908
  const handleResizeEnd = (w, h) => {
1909
+ isResizingRef.current = false;
1698
1910
  setIsResizing(false);
1911
+ if (iframeRef.current) iframeRef.current.style.pointerEvents = "";
1912
+ setIsHovered(true);
1699
1913
  editor.update(() => {
1700
1914
  const node = lexical.$getNodeByKey(nodeKey);
1701
- if ($isYouTubeNode(node)) {
1702
- node.setSize(Math.round(w), Math.round(h));
1703
- }
1915
+ if ($isYouTubeNode(node)) node.setSize(Math.round(w), Math.round(h));
1704
1916
  });
1705
1917
  };
1706
- 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(
1707
1938
  "div",
1708
1939
  {
1709
1940
  ref: containerRef,
1710
- 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
+ },
1711
1950
  onMouseEnter: () => setIsHovered(true),
1712
1951
  onMouseLeave: () => {
1713
- if (!isResizing) setIsHovered(false);
1952
+ if (!isResizingRef.current) setIsHovered(false);
1714
1953
  },
1715
1954
  children: [
1716
- /* @__PURE__ */ jsxRuntime.jsx(
1717
- "iframe",
1718
- {
1719
- width: "100%",
1720
- height: "100%",
1721
- src: `https://www.youtube.com/embed/${videoID}`,
1722
- allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
1723
- allowFullScreen: true,
1724
- title: "YouTube video",
1725
- style: { display: "block", border: "none", pointerEvents: isResizing ? "none" : "auto" }
1726
- }
1727
- ),
1728
- isHovered && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
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. */
1729
1961
  /* @__PURE__ */ jsxRuntime.jsx(
1730
- "button",
1962
+ "iframe",
1731
1963
  {
1732
- type: "button",
1733
- onClick: handleDelete,
1734
- title: "Remove video",
1735
- style: {
1736
- position: "absolute",
1737
- top: 8,
1738
- right: 8,
1739
- width: 28,
1740
- height: 28,
1741
- borderRadius: "50%",
1742
- background: "rgba(0,0,0,0.65)",
1743
- color: "#fff",
1744
- border: "none",
1745
- cursor: "pointer",
1746
- fontSize: 18,
1747
- lineHeight: 1,
1748
- padding: 0,
1749
- zIndex: 10,
1750
- display: "flex",
1751
- alignItems: "center",
1752
- justifyContent: "center"
1753
- },
1754
- children: "\xD7"
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" }
1755
1973
  }
1756
- ),
1757
- /* @__PURE__ */ jsxRuntime.jsx(
1758
- VideoResizer,
1759
- {
1760
- containerRef,
1761
- onResizeStart: () => setIsResizing(true),
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,
1762
2069
  onResizeEnd: handleResizeEnd
1763
2070
  }
1764
2071
  )
@@ -1819,6 +2126,7 @@ var YouTubeNode = class _YouTubeNode extends LexicalDecoratorBlockNode.Decorator
1819
2126
  iframe.setAttribute("width", String(this.__width));
1820
2127
  iframe.setAttribute("height", String(this.__height));
1821
2128
  iframe.style.border = "none";
2129
+ iframe.setAttribute("sandbox", "allow-same-origin allow-scripts allow-popups allow-presentation");
1822
2130
  iframe.setAttribute(
1823
2131
  "allow",
1824
2132
  "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
@@ -1846,16 +2154,10 @@ var YouTubeNode = class _YouTubeNode extends LexicalDecoratorBlockNode.Decorator
1846
2154
  writable.__width = width;
1847
2155
  writable.__height = height;
1848
2156
  }
1849
- decorate(_editor, config) {
1850
- const embedBlockTheme = config.theme.embedBlock || {};
1851
- const className = {
1852
- base: embedBlockTheme.base || "",
1853
- focus: embedBlockTheme.focus || ""
1854
- };
2157
+ decorate(_editor, _config) {
1855
2158
  return /* @__PURE__ */ jsxRuntime.jsx(
1856
2159
  YouTubeComponent,
1857
2160
  {
1858
- className,
1859
2161
  format: this.__format,
1860
2162
  nodeKey: this.getKey(),
1861
2163
  videoID: this.__id,
@@ -2221,12 +2523,12 @@ function useFloatingPortalContainer(editor) {
2221
2523
  }, [editor]);
2222
2524
  return container;
2223
2525
  }
2224
- function setPopupPositionFixed(popupEl, rect) {
2526
+ function setPopupPositionFixed(popupEl, rect, topBoundary) {
2225
2527
  const GAP = 8;
2226
2528
  const MARGIN = 8;
2227
2529
  let top = rect.top - popupEl.offsetHeight - GAP;
2228
- let left = rect.left + rect.width - popupEl.offsetWidth;
2229
- if (top < MARGIN) top = rect.bottom + GAP;
2530
+ let left = rect.left;
2531
+ if (top < topBoundary) top = rect.bottom + GAP;
2230
2532
  left = clamp2(left, MARGIN, window.innerWidth - popupEl.offsetWidth - MARGIN);
2231
2533
  popupEl.style.top = `${top}px`;
2232
2534
  popupEl.style.left = `${left}px`;
@@ -2268,10 +2570,22 @@ function FloatingCharacterStylesEditor({
2268
2570
  popupEl.classList.remove("is-open");
2269
2571
  return;
2270
2572
  }
2271
- const range = sel.getRangeAt(0);
2272
- 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;
2273
2587
  if (!mouseDownRef.current) {
2274
- setPopupPositionFixed(popupEl, rect);
2588
+ setPopupPositionFixed(popupEl, rect, topBoundary);
2275
2589
  }
2276
2590
  popupEl.classList.add("is-open");
2277
2591
  }, [editor]);
@@ -2479,6 +2793,11 @@ function useCharacterStylesPopup(editor, opts) {
2479
2793
  setIsText(false);
2480
2794
  return;
2481
2795
  }
2796
+ const activeElement = document.activeElement;
2797
+ if (activeElement && activeElement !== document.body && rootElement && !rootElement.contains(activeElement)) {
2798
+ setIsText(false);
2799
+ return;
2800
+ }
2482
2801
  if (!lexical.$isRangeSelection(selection)) return;
2483
2802
  const node = getSelectedNode(selection);
2484
2803
  setIsBold(selection.hasFormat("bold"));
@@ -2499,7 +2818,11 @@ function useCharacterStylesPopup(editor, opts) {
2499
2818
  }, [editor]);
2500
2819
  React9.useEffect(() => {
2501
2820
  document.addEventListener("selectionchange", updatePopupState);
2502
- return () => document.removeEventListener("selectionchange", updatePopupState);
2821
+ document.addEventListener("focusin", updatePopupState);
2822
+ return () => {
2823
+ document.removeEventListener("selectionchange", updatePopupState);
2824
+ document.removeEventListener("focusin", updatePopupState);
2825
+ };
2503
2826
  }, [updatePopupState]);
2504
2827
  React9.useEffect(() => editor.registerUpdateListener(updatePopupState), [editor, updatePopupState]);
2505
2828
  if (!portalContainer || !isText || isLink) return null;
@@ -2757,34 +3080,57 @@ function normalizeToBlockHtml(html) {
2757
3080
  if (pendingP) body.appendChild(pendingP);
2758
3081
  return body.innerHTML;
2759
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
+ }
2760
3108
  var CustomOnChangePlugin = ({ value, onChange }) => {
2761
3109
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
2762
3110
  const initializedRef = React9.useRef(false);
2763
- const onChangeRef = React9.useRef(onChange);
2764
- onChangeRef.current = onChange;
2765
3111
  React9.useEffect(() => {
2766
3112
  if (!value || initializedRef.current) return;
2767
3113
  initializedRef.current = true;
2768
3114
  editor.update(() => {
2769
3115
  const root = lexical.$getRoot();
2770
3116
  root.clear();
3117
+ const safe = sanitizeHtml(value);
3118
+ const cleaned = normalizeToBlockHtml(splitHeadingsAtBrSequences(safe));
2771
3119
  const parser = new DOMParser();
2772
- const dom = parser.parseFromString(value, "text/html");
3120
+ const dom = parser.parseFromString(cleaned, "text/html");
2773
3121
  const nodes = html.$generateNodesFromDOM(editor, dom);
2774
3122
  root.append(...nodes);
2775
- lexical.$setSelection(null);
2776
3123
  });
2777
3124
  }, [editor, value]);
2778
- const handleChange = React9.useCallback((editorState) => {
2779
- editorState.read(() => {
2780
- onChangeRef.current(postProcessOutput(html.$generateHtmlFromNodes(editor)));
2781
- });
2782
- }, [editor]);
2783
3125
  return /* @__PURE__ */ jsxRuntime.jsx(
2784
3126
  LexicalOnChangePlugin.OnChangePlugin,
2785
3127
  {
2786
- onChange: handleChange,
2787
- ignoreSelectionChange: true
3128
+ onChange: (editorState) => {
3129
+ editorState.read(() => {
3130
+ const raw = html.$generateHtmlFromNodes(editor);
3131
+ onChange(postProcessOutput(splitHeadingsAtBrSequences(raw)));
3132
+ });
3133
+ }
2788
3134
  }
2789
3135
  );
2790
3136
  };
@@ -2878,7 +3224,7 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
2878
3224
  return;
2879
3225
  }
2880
3226
  const rootElement = editor.getRootElement();
2881
- if (selection !== null && nativeSelection !== null && rootElement !== null && rootElement.contains(nativeSelection.anchorNode) && editor.isEditable()) {
3227
+ if (isLink && selection !== null && nativeSelection !== null && rootElement !== null && rootElement.contains(nativeSelection.anchorNode) && editor.isEditable()) {
2882
3228
  const domRect = nativeSelection.focusNode?.parentElement?.getBoundingClientRect();
2883
3229
  if (domRect) {
2884
3230
  domRect.y += 40;
@@ -2894,7 +3240,7 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
2894
3240
  setLinkUrl("");
2895
3241
  }
2896
3242
  return true;
2897
- }, [anchorElem, editor, setIsLinkEditMode, isLinkEditMode, linkUrl]);
3243
+ }, [anchorElem, editor, setIsLinkEditMode, isLinkEditMode, isLink, linkUrl]);
2898
3244
  React9.useEffect(() => {
2899
3245
  const scrollerElem = anchorElem.parentElement;
2900
3246
  const update = () => {
@@ -3001,17 +3347,20 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
3001
3347
  }
3002
3348
  }
3003
3349
  ),
3004
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3350
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "link-input-actions", children: [
3005
3351
  /* @__PURE__ */ jsxRuntime.jsx(
3006
3352
  "div",
3007
3353
  {
3008
3354
  className: "link-cancel",
3009
3355
  role: "button",
3010
3356
  tabIndex: 0,
3357
+ title: "Cancel",
3358
+ "aria-label": "Cancel",
3011
3359
  onMouseDown: preventDefault,
3012
3360
  onClick: () => {
3013
3361
  setIsLinkEditMode(false);
3014
- }
3362
+ },
3363
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.DismissRegular, { fontSize: 16 })
3015
3364
  }
3016
3365
  ),
3017
3366
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -3020,8 +3369,11 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
3020
3369
  className: "link-confirm",
3021
3370
  role: "button",
3022
3371
  tabIndex: 0,
3372
+ title: "Confirm",
3373
+ "aria-label": "Confirm",
3023
3374
  onMouseDown: preventDefault,
3024
- onClick: handleLinkSubmission
3375
+ onClick: handleLinkSubmission,
3376
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.CheckmarkRegular, { fontSize: 16 })
3025
3377
  }
3026
3378
  )
3027
3379
  ] })
@@ -3041,12 +3393,15 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
3041
3393
  className: "link-edit",
3042
3394
  role: "button",
3043
3395
  tabIndex: 0,
3396
+ title: "Edit link",
3397
+ "aria-label": "Edit link",
3044
3398
  onMouseDown: preventDefault,
3045
3399
  onClick: (event) => {
3046
3400
  event.preventDefault();
3047
3401
  setEditedLinkUrl(linkUrl);
3048
3402
  setIsLinkEditMode(true);
3049
- }
3403
+ },
3404
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.EditRegular, { fontSize: 16 })
3050
3405
  }
3051
3406
  ),
3052
3407
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -3055,10 +3410,13 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
3055
3410
  className: "link-trash",
3056
3411
  role: "button",
3057
3412
  tabIndex: 0,
3413
+ title: "Remove link",
3414
+ "aria-label": "Remove link",
3058
3415
  onMouseDown: preventDefault,
3059
3416
  onClick: () => {
3060
3417
  editor.dispatchCommand(link.TOGGLE_LINK_COMMAND, null);
3061
- }
3418
+ },
3419
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.DeleteRegular, { fontSize: 16 })
3062
3420
  }
3063
3421
  )
3064
3422
  ] }) });
@@ -3141,8 +3499,6 @@ var FloatingLinkEditorPlugin = ({ anchorElem, isLinkEditMode, setIsLinkEditMode
3141
3499
  setIsLinkEditMode
3142
3500
  );
3143
3501
  };
3144
-
3145
- // src/Plugins/ImagePlugin.tsx
3146
3502
  init_ImageNode();
3147
3503
  var INSERT_IMAGE_COMMAND = lexical.createCommand("INSERT_IMAGE_COMMAND");
3148
3504
  var readClipboardImageAsDataURL = async (event) => {
@@ -3170,7 +3526,7 @@ var InsertImageByURL = ({
3170
3526
  const [src, setSrc] = React9.useState("");
3171
3527
  const isDisabled = disabled || src === "";
3172
3528
  return /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 6, padding: "10px 0px 0px 0px" }, children: [
3173
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Enter URL", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3529
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Enter URL", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3174
3530
  reactComponents.Input,
3175
3531
  {
3176
3532
  autoFocus: !disabled,
@@ -3181,7 +3537,7 @@ var InsertImageByURL = ({
3181
3537
  value: src
3182
3538
  }
3183
3539
  ) }),
3184
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Alt Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3540
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Alt Text", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3185
3541
  reactComponents.Input,
3186
3542
  {
3187
3543
  placeholder: "Alt text",
@@ -3219,16 +3575,19 @@ var InsertImageByURL = ({
3219
3575
  };
3220
3576
  var InsertImageDialog = ({
3221
3577
  activeEditor,
3222
- disabled
3578
+ disabled,
3579
+ maxImageSizeMB,
3580
+ validationMessages
3223
3581
  }) => {
3224
3582
  const [src, setSrc] = React9.useState("");
3225
3583
  const [altText, setAltText] = React9.useState("");
3226
3584
  const [isOpen, setIsOpen] = React9.useState(false);
3227
3585
  const [selectedValue, setSelectedValue] = React9.useState("Upload");
3228
3586
  const [fileName, setFileName] = React9.useState("");
3587
+ const [fileSizeError, setFileSizeError] = React9.useState(null);
3229
3588
  const hasModifier = React9.useRef(false);
3230
3589
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
3231
- const isDisabled = disabled || src === "";
3590
+ const isDisabled = disabled || src === "" || !!fileSizeError;
3232
3591
  React9.useEffect(() => {
3233
3592
  hasModifier.current = false;
3234
3593
  const handler = (e) => {
@@ -3244,31 +3603,44 @@ var InsertImageDialog = ({
3244
3603
  setAltText("");
3245
3604
  setSrc("");
3246
3605
  setFileName("");
3606
+ setFileSizeError(null);
3247
3607
  };
3248
3608
  const loadImage = (event) => {
3249
3609
  if (disabled) return;
3250
3610
  const files = event.target.files;
3251
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);
3252
3626
  const reader = new FileReader();
3253
3627
  reader.onload = () => {
3254
3628
  if (typeof reader.result === "string") {
3255
3629
  setSrc(reader.result);
3256
- setFileName(files[0].name);
3630
+ setFileName(file.name);
3257
3631
  }
3258
3632
  };
3259
- reader.readAsDataURL(files[0]);
3633
+ reader.readAsDataURL(file);
3260
3634
  };
3261
3635
  return /* @__PURE__ */ jsxRuntime.jsxs(
3262
- reactComponents.Popover,
3636
+ reactComponents.Dialog,
3263
3637
  {
3264
- trapFocus: true,
3265
- withArrow: true,
3266
3638
  open: disabled ? false : isOpen,
3267
3639
  onOpenChange: (_, data) => {
3268
3640
  if (!disabled) setIsOpen(data.open);
3269
3641
  },
3270
3642
  children: [
3271
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.PopoverTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3643
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3272
3644
  reactComponents.Button,
3273
3645
  {
3274
3646
  size: "small",
@@ -3292,81 +3664,73 @@ var InsertImageDialog = ({
3292
3664
  },
3293
3665
  "upload-image"
3294
3666
  ) }),
3295
- /* @__PURE__ */ jsxRuntime.jsxs(
3296
- reactComponents.PopoverSurface,
3297
- {
3298
- style: {
3299
- width: "320px",
3300
- opacity: disabled ? 0.6 : 1,
3301
- pointerEvents: disabled ? "none" : "auto"
3302
- },
3303
- children: [
3304
- /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 6 }, children: [
3305
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Upload", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs(
3306
- "label",
3307
- {
3308
- style: {
3309
- cursor: disabled ? "not-allowed" : "pointer",
3310
- display: "flex",
3311
- alignItems: "center",
3312
- gap: 8,
3313
- opacity: disabled ? 0.75 : 1
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(
3671
+ "label",
3672
+ {
3673
+ style: {
3674
+ cursor: disabled ? "not-allowed" : "pointer",
3675
+ display: "flex",
3676
+ alignItems: "center",
3677
+ gap: 8,
3678
+ opacity: disabled ? 0.75 : 1
3679
+ },
3680
+ children: [
3681
+ /* @__PURE__ */ jsxRuntime.jsx(
3682
+ "input",
3683
+ {
3684
+ type: "file",
3685
+ accept: "image/*",
3686
+ style: { display: "none" },
3687
+ disabled,
3688
+ onChange: loadImage
3314
3689
  },
3315
- children: [
3316
- /* @__PURE__ */ jsxRuntime.jsx(
3317
- "input",
3318
- {
3319
- type: "file",
3320
- accept: "image/*",
3321
- style: { display: "none" },
3322
- disabled,
3323
- onChange: loadImage
3324
- },
3325
- "inline-image-upload"
3326
- ),
3327
- /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { horizontal: true, children: [
3328
- /* @__PURE__ */ jsxRuntime.jsx(
3329
- reactIcons.AttachFilled,
3330
- {
3331
- style: {
3332
- fontSize: "16px",
3333
- color: disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#808080",
3334
- marginTop: 2
3335
- }
3336
- }
3337
- ),
3338
- !fileName && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#808080" }, children: "Upload File" })
3339
- ] }),
3340
- fileName && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#808080" }, children: fileName })
3341
- ]
3342
- }
3343
- ) }),
3344
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Alt Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3345
- reactComponents.Input,
3346
- {
3347
- placeholder: "Alt text",
3348
- appearance: "underline",
3349
- disabled,
3350
- onChange: (_, d) => setAltText(d.value),
3351
- value: altText
3352
- }
3353
- ) }),
3354
- /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
3355
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled: isDisabled, onClick: () => onClick({ altText, src }), children: "Add" }),
3356
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
3357
- ] })
3358
- ] }),
3359
- selectedValue === "URL" && /* @__PURE__ */ jsxRuntime.jsx(
3360
- InsertImageByURL,
3361
- {
3362
- disabled,
3363
- setIsOpen: (open) => setIsOpen(open),
3364
- onClick: (payload) => onClick(payload)
3365
- }
3366
- )
3367
- ]
3368
- }
3369
- )
3690
+ "inline-image-upload"
3691
+ ),
3692
+ /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { horizontal: true, children: [
3693
+ /* @__PURE__ */ jsxRuntime.jsx(
3694
+ reactIcons.AttachFilled,
3695
+ {
3696
+ style: {
3697
+ fontSize: "16px",
3698
+ color: disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#808080",
3699
+ marginTop: 2
3700
+ }
3701
+ }
3702
+ ),
3703
+ !fileName && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#808080" }, children: "Upload File" })
3704
+ ] }),
3705
+ fileName && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#808080" }, children: fileName })
3706
+ ]
3707
+ }
3708
+ ) }),
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(
3711
+ reactComponents.Input,
3712
+ {
3713
+ placeholder: "Alt text",
3714
+ appearance: "underline",
3715
+ disabled,
3716
+ onChange: (_, d) => setAltText(d.value),
3717
+ value: altText
3718
+ }
3719
+ ) }),
3720
+ selectedValue === "URL" && /* @__PURE__ */ jsxRuntime.jsx(
3721
+ InsertImageByURL,
3722
+ {
3723
+ disabled,
3724
+ setIsOpen: (open) => setIsOpen(open),
3725
+ onClick: (payload) => onClick(payload)
3726
+ }
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" })
3732
+ ] })
3733
+ ] }) })
3370
3734
  ]
3371
3735
  }
3372
3736
  );
@@ -3382,9 +3746,21 @@ var ImagesPlugin = ({ captionsEnabled }) => {
3382
3746
  INSERT_IMAGE_COMMAND,
3383
3747
  (payload) => {
3384
3748
  const imageNode = $createImageNode(payload);
3385
- lexical.$insertNodes([imageNode]);
3386
- if (lexical.$isRootOrShadowRoot(imageNode.getParentOrThrow())) {
3387
- 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
+ }
3388
3764
  }
3389
3765
  return true;
3390
3766
  },
@@ -3435,12 +3811,25 @@ var TRANSPARENT_IMAGE = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAA
3435
3811
  var img = document.createElement("img");
3436
3812
  img.src = TRANSPARENT_IMAGE;
3437
3813
  var $onDragStart = (event) => {
3438
- 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
+ }
3439
3823
  if (!node) return false;
3440
3824
  const dataTransfer = event.dataTransfer;
3441
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
+ }
3442
3832
  dataTransfer.setData("text/plain", "_");
3443
- dataTransfer.setDragImage(img, 0, 0);
3444
3833
  dataTransfer.setData(
3445
3834
  "application/x-lexical-drag",
3446
3835
  JSON.stringify({
@@ -3468,20 +3857,23 @@ var $onDragover = (event) => {
3468
3857
  return true;
3469
3858
  };
3470
3859
  var $onDrop = (event, editor) => {
3471
- const node = $getImageNodeInSelection();
3472
- if (!node) return false;
3473
3860
  const data = getDragImageData(event);
3474
3861
  if (!data) return false;
3475
3862
  event.preventDefault();
3476
3863
  if (canDropImage(event)) {
3864
+ const sourceKey = data.key;
3865
+ if (sourceKey) {
3866
+ const sourceNode = lexical.$getNodeByKey(sourceKey);
3867
+ if (sourceNode) sourceNode.remove();
3868
+ }
3477
3869
  const range = getDragSelection(event);
3478
- node.remove();
3479
3870
  const rangeSelection = lexical.$createRangeSelection();
3480
3871
  if (range !== null && range !== void 0) {
3481
3872
  rangeSelection.applyDOMRange(range);
3482
3873
  }
3483
3874
  lexical.$setSelection(rangeSelection);
3484
- editor.dispatchCommand(INSERT_IMAGE_COMMAND, data);
3875
+ const { key: _key, ...insertPayload } = data;
3876
+ editor.dispatchCommand(INSERT_IMAGE_COMMAND, insertPayload);
3485
3877
  }
3486
3878
  return true;
3487
3879
  };
@@ -3501,7 +3893,7 @@ var getDragImageData = (event) => {
3501
3893
  };
3502
3894
  var canDropImage = (event) => {
3503
3895
  const target = event.target;
3504
- 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"]'));
3505
3897
  };
3506
3898
  var getDragSelection = (event) => {
3507
3899
  let range;
@@ -3516,8 +3908,6 @@ var getDragSelection = (event) => {
3516
3908
  }
3517
3909
  return range;
3518
3910
  };
3519
-
3520
- // src/Plugins/InlineImage.tsx
3521
3911
  init_InlineImage();
3522
3912
  init_InlineImageNode();
3523
3913
  var INSERT_INLINE_IMAGE_COMMAND = lexical.createCommand(
@@ -3535,7 +3925,9 @@ var useStyles = reactComponents.makeStyles({
3535
3925
  });
3536
3926
  var InsertInlineImageDialog = ({
3537
3927
  disabled,
3538
- activeEditor
3928
+ activeEditor,
3929
+ maxImageSizeMB,
3930
+ validationMessages
3539
3931
  }) => {
3540
3932
  const hasModifier = React9.useRef(false);
3541
3933
  const [src, setSrc] = React9.useState("");
@@ -3543,21 +3935,36 @@ var InsertInlineImageDialog = ({
3543
3935
  const [altText, setAltText] = React9.useState("");
3544
3936
  const [fileName, setFileName] = React9.useState("");
3545
3937
  const [position, setPosition] = React9.useState("left");
3938
+ const [fileSizeError, setFileSizeError] = React9.useState(null);
3546
3939
  const styles = useStyles();
3547
3940
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
3548
- const isDisabled = disabled || src === "";
3941
+ const isDisabled = disabled || src === "" || !!fileSizeError;
3549
3942
  const loadImage = (event) => {
3550
3943
  if (disabled) return;
3551
3944
  const files = event.target.files;
3552
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);
3553
3960
  const reader = new FileReader();
3554
3961
  reader.onload = () => {
3555
3962
  if (typeof reader.result === "string") {
3556
3963
  setSrc(reader.result);
3557
- setFileName(files[0].name);
3964
+ setFileName(file.name);
3558
3965
  }
3559
3966
  };
3560
- reader.readAsDataURL(files[0]);
3967
+ reader.readAsDataURL(file);
3561
3968
  };
3562
3969
  React9.useEffect(() => {
3563
3970
  const handler = (e) => {
@@ -3574,18 +3981,17 @@ var InsertInlineImageDialog = ({
3574
3981
  setAltText("");
3575
3982
  setSrc("");
3576
3983
  setFileName("");
3984
+ setFileSizeError(null);
3577
3985
  };
3578
3986
  return /* @__PURE__ */ jsxRuntime.jsxs(
3579
- reactComponents.Popover,
3987
+ reactComponents.Dialog,
3580
3988
  {
3581
- trapFocus: true,
3582
- withArrow: true,
3583
3989
  open: disabled ? false : isOpen,
3584
3990
  onOpenChange: (_, data) => {
3585
3991
  if (!disabled) setIsOpen(data.open);
3586
3992
  },
3587
3993
  children: [
3588
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.PopoverTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3994
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3589
3995
  reactComponents.Button,
3590
3996
  {
3591
3997
  size: "small",
@@ -3609,104 +4015,100 @@ var InsertInlineImageDialog = ({
3609
4015
  },
3610
4016
  "upload-inline-image"
3611
4017
  ) }),
3612
- /* @__PURE__ */ jsxRuntime.jsx(
3613
- reactComponents.PopoverSurface,
3614
- {
3615
- style: {
3616
- width: "400px",
3617
- opacity: disabled ? 0.6 : 1,
3618
- pointerEvents: disabled ? "none" : "auto"
3619
- },
3620
- children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 6, padding: "10px 0px 0px 0px" }, children: [
3621
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Upload", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs(
3622
- "label",
3623
- {
3624
- style: {
3625
- cursor: disabled ? "not-allowed" : "pointer",
3626
- display: "flex",
3627
- alignItems: "center",
3628
- gap: 8,
3629
- opacity: disabled ? 0.75 : 1
3630
- },
3631
- children: [
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(
4022
+ "label",
4023
+ {
4024
+ style: {
4025
+ cursor: disabled ? "not-allowed" : "pointer",
4026
+ display: "flex",
4027
+ alignItems: "center",
4028
+ gap: 8,
4029
+ opacity: disabled ? 0.75 : 1
4030
+ },
4031
+ children: [
4032
+ /* @__PURE__ */ jsxRuntime.jsx(
4033
+ "input",
4034
+ {
4035
+ type: "file",
4036
+ accept: "image/*",
4037
+ style: { display: "none" },
4038
+ disabled,
4039
+ onChange: loadImage
4040
+ },
4041
+ "inline-image-upload"
4042
+ ),
4043
+ /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { horizontal: true, children: [
3632
4044
  /* @__PURE__ */ jsxRuntime.jsx(
3633
- "input",
4045
+ reactIcons.AttachFilled,
3634
4046
  {
3635
- type: "file",
3636
- accept: "image/*",
3637
- style: { display: "none" },
3638
- disabled,
3639
- onChange: loadImage
3640
- },
3641
- "inline-image-upload"
3642
- ),
3643
- /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { horizontal: true, children: [
3644
- /* @__PURE__ */ jsxRuntime.jsx(
3645
- reactIcons.AttachFilled,
3646
- {
3647
- style: {
3648
- fontSize: "16px",
3649
- color: disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#808080",
3650
- marginTop: 2
3651
- }
4047
+ style: {
4048
+ fontSize: "16px",
4049
+ color: disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#808080",
4050
+ marginTop: 2
3652
4051
  }
3653
- ),
3654
- !fileName && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#808080" }, children: "Upload File" })
3655
- ] }),
3656
- fileName && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#808080" }, children: fileName })
3657
- ]
3658
- }
3659
- ) }),
3660
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Position", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs(
3661
- reactComponents.Dropdown,
3662
- {
3663
- placeholder: "Left Align",
3664
- className: styles.alignDropdown,
3665
- disabled,
3666
- listbox: { style: { width: "120px" } },
3667
- root: { style: { borderBottom: "1px solid black" } },
3668
- children: [
3669
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { text: "full", onClick: () => setPosition("full"), children: "Full" }, "full"),
3670
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { text: "left", onClick: () => setPosition("left"), children: "Left" }, "left"),
3671
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { text: "right", onClick: () => setPosition("right"), children: "Right" }, "right")
3672
- ]
3673
- }
3674
- ) }),
3675
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Alt Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
3676
- reactComponents.Input,
3677
- {
3678
- placeholder: "Alt text",
3679
- appearance: "underline",
3680
- disabled,
3681
- value: altText,
3682
- onChange: (_, d) => setAltText(d.value)
3683
- }
3684
- ) }),
3685
- /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
3686
- /* @__PURE__ */ jsxRuntime.jsx(
3687
- reactComponents.Button,
3688
- {
3689
- size: "small",
3690
- disabled: isDisabled,
3691
- onClick: handleOnClick,
3692
- children: "Add"
3693
- },
3694
- "file-inline-upload-btn"
3695
- ),
3696
- /* @__PURE__ */ jsxRuntime.jsx(
3697
- reactComponents.Button,
3698
- {
3699
- size: "small",
3700
- disabled,
3701
- onClick: () => setIsOpen(false),
3702
- children: "Cancel"
3703
- },
3704
- "file-inline-upload-cancel"
3705
- )
3706
- ] })
3707
- ] })
3708
- }
3709
- )
4052
+ }
4053
+ ),
4054
+ !fileName && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#808080" }, children: "Upload File" })
4055
+ ] }),
4056
+ fileName && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#808080" }, children: fileName })
4057
+ ]
4058
+ }
4059
+ ) }),
4060
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Position", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs(
4061
+ reactComponents.Dropdown,
4062
+ {
4063
+ className: styles.alignDropdown,
4064
+ disabled,
4065
+ value: position === "full" ? "Full" : position === "right" ? "Right" : "Left",
4066
+ selectedOptions: [position ?? "left"],
4067
+ listbox: { style: { width: "120px" } },
4068
+ root: { style: { borderBottom: "1px solid black" } },
4069
+ onOptionSelect: (_, data) => setPosition(data.optionValue),
4070
+ children: [
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")
4074
+ ]
4075
+ }
4076
+ ) }),
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(
4079
+ reactComponents.Input,
4080
+ {
4081
+ placeholder: "Alt text",
4082
+ appearance: "underline",
4083
+ disabled,
4084
+ value: altText,
4085
+ onChange: (_, d) => setAltText(d.value)
4086
+ }
4087
+ ) })
4088
+ ] }) }),
4089
+ /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogActions, { children: [
4090
+ /* @__PURE__ */ jsxRuntime.jsx(
4091
+ reactComponents.Button,
4092
+ {
4093
+ size: "small",
4094
+ disabled: isDisabled,
4095
+ onClick: handleOnClick,
4096
+ children: "Add"
4097
+ },
4098
+ "file-inline-upload-btn"
4099
+ ),
4100
+ /* @__PURE__ */ jsxRuntime.jsx(
4101
+ reactComponents.Button,
4102
+ {
4103
+ size: "small",
4104
+ disabled,
4105
+ onClick: () => setIsOpen(false),
4106
+ children: "Cancel"
4107
+ },
4108
+ "file-inline-upload-cancel"
4109
+ )
4110
+ ] })
4111
+ ] }) })
3710
4112
  ]
3711
4113
  }
3712
4114
  );
@@ -3726,6 +4128,11 @@ var InlineImagePlugin = () => {
3726
4128
  if (lexical.$isRootOrShadowRoot(imageNode.getParentOrThrow())) {
3727
4129
  utils.$wrapNodeInElement(imageNode, lexical.$createParagraphNode).selectEnd();
3728
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
+ }
3729
4136
  return true;
3730
4137
  },
3731
4138
  lexical.COMMAND_PRIORITY_EDITOR
@@ -3754,12 +4161,23 @@ var TRANSPARENT_IMAGE2 = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEA
3754
4161
  var img2 = document.createElement("img");
3755
4162
  img2.src = TRANSPARENT_IMAGE2;
3756
4163
  function $onDragStart2(event) {
3757
- 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
+ }
3758
4171
  if (!node) return false;
3759
4172
  const dataTransfer = event.dataTransfer;
3760
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
+ }
3761
4180
  dataTransfer.setData("text/plain", "_");
3762
- dataTransfer.setDragImage(img2, 0, 0);
3763
4181
  dataTransfer.setData(
3764
4182
  "application/x-lexical-drag",
3765
4183
  JSON.stringify({
@@ -3778,28 +4196,31 @@ function $onDragStart2(event) {
3778
4196
  return true;
3779
4197
  }
3780
4198
  var $onDragover2 = (event) => {
3781
- const node = $getImageNodeInSelection2();
3782
- if (!node) return false;
4199
+ const hasDragData = !!event.dataTransfer?.types.includes("application/x-lexical-drag");
4200
+ if (!hasDragData) return false;
3783
4201
  if (!canDropImage2(event)) {
3784
4202
  event.preventDefault();
3785
4203
  }
3786
4204
  return true;
3787
4205
  };
3788
4206
  var $onDrop2 = (event, editor) => {
3789
- const node = $getImageNodeInSelection2();
3790
- if (!node) return false;
3791
4207
  const data = getDragImageData2(event);
3792
4208
  if (!data) return false;
3793
4209
  event.preventDefault();
3794
4210
  if (canDropImage2(event)) {
4211
+ const sourceKey = data.key;
4212
+ if (sourceKey) {
4213
+ const sourceNode = lexical.$getNodeByKey(sourceKey);
4214
+ if (sourceNode) sourceNode.remove();
4215
+ }
3795
4216
  const range = getDragSelection2(event);
3796
- node.remove();
3797
4217
  const rangeSelection = lexical.$createRangeSelection();
3798
4218
  if (range !== null && range !== void 0) {
3799
4219
  rangeSelection.applyDOMRange(range);
3800
4220
  }
3801
4221
  lexical.$setSelection(rangeSelection);
3802
- editor.dispatchCommand(INSERT_INLINE_IMAGE_COMMAND, data);
4222
+ const { key: _key, ...insertPayload } = data;
4223
+ editor.dispatchCommand(INSERT_INLINE_IMAGE_COMMAND, insertPayload);
3803
4224
  }
3804
4225
  return true;
3805
4226
  };
@@ -3819,7 +4240,7 @@ var getDragImageData2 = (event) => {
3819
4240
  };
3820
4241
  var canDropImage2 = (event) => {
3821
4242
  const target = event.target;
3822
- return !!(lexical.isHTMLElement(target) && !target.closest("code, span.editor-image") && lexical.isHTMLElement(target.parentElement) && target.parentElement.closest("div.ContentEditable__root"));
4243
+ return !!(lexical.isHTMLElement(target) && !target.closest("code, span.editor-image, .inline-editor-image") && target.closest('[contenteditable="true"]'));
3823
4244
  };
3824
4245
  var getDragSelection2 = (event) => {
3825
4246
  let range;
@@ -3910,20 +4331,79 @@ function hasBlock(editor, kind) {
3910
4331
  }
3911
4332
 
3912
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
+ }
3913
4371
  function RefApiPlugin({
3914
4372
  forwardedRef,
3915
4373
  contentEditableDomRef,
3916
- focusedRef,
3917
- setRefErrors
4374
+ focusedRef
3918
4375
  }) {
3919
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]);
3920
4399
  React9.useImperativeHandle(
3921
4400
  forwardedRef,
3922
4401
  () => ({
3923
4402
  setValue: (html$1) => {
3924
4403
  editor.update(() => {
4404
+ const safe = normalizeToBlockHtml(sanitizeHtml(html$1 || ""));
3925
4405
  const parser = new DOMParser();
3926
- const dom = parser.parseFromString(html$1 || "<p></p>", "text/html");
4406
+ const dom = parser.parseFromString(safe || "<p></p>", "text/html");
3927
4407
  const nodes = html.$generateNodesFromDOM(editor, dom);
3928
4408
  const root = lexical.$getRoot();
3929
4409
  root.clear();
@@ -3933,9 +4413,9 @@ function RefApiPlugin({
3933
4413
  getValue: () => {
3934
4414
  let html$1 = "";
3935
4415
  editor.getEditorState().read(() => {
3936
- html$1 = html.$generateHtmlFromNodes(editor, null);
4416
+ html$1 = postProcessOutput(splitHeadingsAtBrSequences(html.$generateHtmlFromNodes(editor, null)));
3937
4417
  });
3938
- return postProcessOutput(html$1);
4418
+ return html$1;
3939
4419
  },
3940
4420
  clear: () => {
3941
4421
  editor.update(() => {
@@ -3953,14 +4433,20 @@ function RefApiPlugin({
3953
4433
  },
3954
4434
  isFocused: () => focusedRef.current,
3955
4435
  getEditor: () => editor,
3956
- setErrors: (messages) => setRefErrors(messages),
3957
- clearErrors: () => setRefErrors([]),
3958
4436
  // Generic blocks (signature, footer, banner, etc.)
3959
4437
  upsertBlock: (spec) => upsertBlock(editor, spec),
3960
4438
  removeBlock: (kind) => removeBlock(editor, kind),
3961
- 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
+ }
3962
4448
  }),
3963
- [editor, contentEditableDomRef, focusedRef, setRefErrors]
4449
+ [editor, contentEditableDomRef, focusedRef]
3964
4450
  );
3965
4451
  return null;
3966
4452
  }
@@ -4601,8 +5087,22 @@ function TableActionMenuPlugin({ disabled = false }) {
4601
5087
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
4602
5088
  const [isInTable, setIsInTable] = React9__namespace.useState(false);
4603
5089
  const [anchorRect, setAnchorRect] = React9__namespace.useState(null);
5090
+ const [contentRight, setContentRight] = React9__namespace.useState(null);
4604
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
+ }, []);
4605
5104
  const updateFromSelection = React9__namespace.useCallback(() => {
5105
+ if (openRef.current) return;
4606
5106
  const root = editor.getRootElement();
4607
5107
  if (!root) return;
4608
5108
  editor.getEditorState().read(() => {
@@ -4614,6 +5114,7 @@ function TableActionMenuPlugin({ disabled = false }) {
4614
5114
  if (dom) {
4615
5115
  setIsInTable(true);
4616
5116
  setAnchorRect(dom.getBoundingClientRect());
5117
+ setContentRight(null);
4617
5118
  return;
4618
5119
  }
4619
5120
  }
@@ -4621,6 +5122,7 @@ function TableActionMenuPlugin({ disabled = false }) {
4621
5122
  if (!lexical.$isRangeSelection(selection)) {
4622
5123
  setIsInTable(false);
4623
5124
  setAnchorRect(null);
5125
+ setContentRight(null);
4624
5126
  return;
4625
5127
  }
4626
5128
  const anchorNode = selection.anchor.getNode();
@@ -4628,18 +5130,28 @@ function TableActionMenuPlugin({ disabled = false }) {
4628
5130
  if (!cellNode || !table.$isTableCellNode(cellNode)) {
4629
5131
  setIsInTable(false);
4630
5132
  setAnchorRect(null);
5133
+ setContentRight(null);
4631
5134
  return;
4632
5135
  }
4633
5136
  const cellDom = editor.getElementByKey(cellNode.getKey());
4634
5137
  if (!cellDom) {
4635
5138
  setIsInTable(false);
4636
5139
  setAnchorRect(null);
5140
+ setContentRight(null);
4637
5141
  return;
4638
5142
  }
4639
5143
  setIsInTable(true);
4640
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
+ }
4641
5153
  });
4642
- }, [editor]);
5154
+ }, [editor, measureContentRight]);
4643
5155
  React9__namespace.useEffect(() => {
4644
5156
  return utils.mergeRegister(
4645
5157
  editor.registerCommand(
@@ -4696,30 +5208,37 @@ function TableActionMenuPlugin({ disabled = false }) {
4696
5208
  lexical.COMMAND_PRIORITY_HIGH
4697
5209
  );
4698
5210
  }, [editor, disabled]);
4699
- React9__namespace.useEffect(() => {
4700
- if (!isInTable && open) setOpen(false);
4701
- }, [isInTable, open]);
4702
5211
  const canShow = isInTable && !!anchorRect && !disabled;
4703
5212
  const handleStyle = React9__namespace.useMemo(() => {
4704
5213
  if (!anchorRect) return void 0;
4705
5214
  const top = Math.max(8, anchorRect.top + 6);
4706
- 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);
4707
5217
  return {
4708
5218
  position: "fixed",
4709
5219
  top,
4710
5220
  left,
4711
5221
  zIndex: 9999
4712
5222
  };
4713
- }, [anchorRect]);
5223
+ }, [anchorRect, contentRight]);
4714
5224
  const dangerStyle = {
4715
5225
  color: "var(--colorPaletteRedForeground1)"
4716
5226
  };
4717
5227
  const run = React9__namespace.useCallback(
4718
5228
  (fn) => {
4719
5229
  if (disabled) return;
4720
- editor.focus();
4721
- editor.update(() => fn());
5230
+ openRef.current = false;
4722
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
+ });
4723
5242
  },
4724
5243
  [disabled, editor]
4725
5244
  );
@@ -4729,32 +5248,31 @@ function TableActionMenuPlugin({ disabled = false }) {
4729
5248
  const insertColLeft = () => run(() => table.$insertTableColumnAtSelection(false));
4730
5249
  const deleteRow = () => run(() => table.$deleteTableRowAtSelection());
4731
5250
  const deleteCol = () => run(() => table.$deleteTableColumnAtSelection());
4732
- const deleteTable = () => run(() => {
4733
- const selection = lexical.$getSelection();
4734
- if (table.$isTableSelection(selection)) {
4735
- 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));
4736
5261
  if (tableNode) tableNode.remove();
4737
- return;
4738
- }
4739
- if (!lexical.$isRangeSelection(selection)) return;
4740
- const node = selection.anchor.getNode();
4741
- const cell = lexical.$findMatchingParent(node, (n) => table.$isTableCellNode(n)) ?? (table.$isTableCellNode(node) ? node : null);
4742
- if (!cell) return;
4743
- const table$1 = table.$getTableNodeFromLexicalNodeOrThrow(cell);
4744
- table$1.remove();
4745
- });
5262
+ });
5263
+ };
4746
5264
  if (!canShow || !handleStyle) return null;
4747
5265
  return reactDom.createPortal(
4748
- /* @__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: [
4749
5270
  /* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
4750
5271
  "button",
4751
5272
  {
4752
5273
  type: "button",
4753
5274
  className: "aoTableActionHandleBtn",
4754
5275
  "aria-label": "Table options",
4755
- onMouseDown: (e) => {
4756
- e.preventDefault();
4757
- },
4758
5276
  children: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ChevronDown12Regular, {})
4759
5277
  }
4760
5278
  ) }),
@@ -5105,6 +5623,85 @@ function getToolbarGroupsByLevel(level) {
5105
5623
  ];
5106
5624
  }
5107
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
+ };
5108
5705
  var PRESET = [
5109
5706
  "#000000",
5110
5707
  "#434343",
@@ -5388,7 +5985,7 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5388
5985
  directionalHint: 4,
5389
5986
  className: "aoColorCallout",
5390
5987
  preventDismissOnEvent,
5391
- 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: [
5392
5989
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
5393
5990
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, fontWeight: 600, color: "#242424", letterSpacing: 0.1 }, children: title }),
5394
5991
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5401,8 +5998,8 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5401
5998
  display: "flex",
5402
5999
  alignItems: "center",
5403
6000
  justifyContent: "center",
5404
- width: 24,
5405
- height: 24,
6001
+ width: 18,
6002
+ height: 18,
5406
6003
  padding: 0,
5407
6004
  border: "none",
5408
6005
  borderRadius: 4,
@@ -5425,7 +6022,7 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5425
6022
  style: {
5426
6023
  position: "relative",
5427
6024
  width: "100%",
5428
- height: 150,
6025
+ height: 125,
5429
6026
  borderRadius: 8,
5430
6027
  overflow: "hidden",
5431
6028
  cursor: "crosshair",
@@ -5514,8 +6111,8 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5514
6111
  "div",
5515
6112
  {
5516
6113
  style: {
5517
- width: 32,
5518
- height: 32,
6114
+ width: 20,
6115
+ height: 20,
5519
6116
  borderRadius: 6,
5520
6117
  flexShrink: 0,
5521
6118
  background: hex,
@@ -5556,10 +6153,10 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5556
6153
  title: c,
5557
6154
  "aria-label": c,
5558
6155
  style: {
5559
- width: 22,
5560
- height: 22,
6156
+ width: 18,
6157
+ height: 18,
5561
6158
  padding: 0,
5562
- borderRadius: 5,
6159
+ borderRadius: 4,
5563
6160
  background: c,
5564
6161
  cursor: "pointer",
5565
6162
  boxShadow: isSelected ? "0 0 0 2px #fff, 0 0 0 3px #4a86e8" : "inset 0 0 0 1px rgba(0,0,0,0.15)",
@@ -5769,7 +6366,6 @@ var FontFamilyPlugin = ({ disabled = false }) => {
5769
6366
  "font-family"
5770
6367
  );
5771
6368
  };
5772
- var DEFAULT_FONT_SIZE = 15;
5773
6369
  var FONT_SIZE_OPTIONS = [
5774
6370
  "8",
5775
6371
  "9",
@@ -5905,104 +6501,42 @@ var FontSizePlugin = ({ disabled }) => {
5905
6501
  "fontsize"
5906
6502
  ) });
5907
6503
  };
5908
- var InsertLinkPlugin = ({ disabled }) => {
6504
+ var InsertLinkPlugin = ({
6505
+ disabled,
6506
+ setIsLinkEditMode
6507
+ }) => {
5909
6508
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
5910
- const [isOpen, setIsOpen] = React9.useState(false);
5911
- const [text, setText] = React9.useState("");
5912
- const [link$1, setLink] = React9.useState("");
5913
6509
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
5914
- const insertLink = (text2, link2) => {
6510
+ const insertLink = () => {
5915
6511
  if (disabled) return;
5916
6512
  editor.update(() => {
5917
- setText("");
5918
- setLink("");
5919
6513
  const selection = lexical.$getSelection();
5920
- if (lexical.$isRangeSelection(selection)) {
5921
- const textNode = new lexical.TextNode(text2);
5922
- const linkNode = link.$createLinkNode(link2.startsWith("http") ? link2 : `https://${link2}`);
5923
- linkNode.append(textNode);
5924
- 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://");
5925
6519
  }
5926
- setIsOpen(false);
5927
6520
  });
6521
+ setIsLinkEditMode(true);
5928
6522
  };
5929
- return /* @__PURE__ */ jsxRuntime.jsxs(
5930
- reactComponents.Popover,
6523
+ return /* @__PURE__ */ jsxRuntime.jsx(
6524
+ reactComponents.Button,
5931
6525
  {
5932
- trapFocus: true,
5933
- withArrow: true,
5934
- open: disabled ? false : isOpen,
5935
- onOpenChange: (_, data) => {
5936
- if (!disabled) setIsOpen(data.open);
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"
5937
6536
  },
5938
- children: [
5939
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.PopoverTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
5940
- reactComponents.Button,
5941
- {
5942
- size: "small",
5943
- title: "Add link",
5944
- disabled,
5945
- icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.LinkAddRegular, { style: { color: iconColor } }),
5946
- style: {
5947
- background: isOpen && !disabled ? "#ebebeb" : "none",
5948
- border: "none",
5949
- margin: 2,
5950
- opacity: disabled ? 0.55 : 1,
5951
- cursor: disabled ? "not-allowed" : "pointer"
5952
- },
5953
- onClick: () => {
5954
- if (!disabled) setIsOpen((prev) => !prev);
5955
- }
5956
- },
5957
- "upload-link"
5958
- ) }),
5959
- /* @__PURE__ */ jsxRuntime.jsx(
5960
- reactComponents.PopoverSurface,
5961
- {
5962
- style: {
5963
- width: "270px",
5964
- opacity: disabled ? 0.6 : 1,
5965
- pointerEvents: disabled ? "none" : "auto"
5966
- },
5967
- children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 10 }, children: [
5968
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
5969
- reactComponents.Input,
5970
- {
5971
- autoFocus: !disabled,
5972
- value: text,
5973
- appearance: "underline",
5974
- placeholder: "Text",
5975
- disabled,
5976
- onChange: (_, v) => setText(v.value)
5977
- }
5978
- ) }),
5979
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Link", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
5980
- reactComponents.Input,
5981
- {
5982
- value: link$1,
5983
- appearance: "underline",
5984
- placeholder: "Link",
5985
- disabled,
5986
- onChange: (_, v) => setLink(v.value)
5987
- }
5988
- ) }),
5989
- /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
5990
- /* @__PURE__ */ jsxRuntime.jsx(
5991
- reactComponents.Button,
5992
- {
5993
- size: "small",
5994
- disabled: disabled || !text || !link$1,
5995
- onClick: () => insertLink(text, link$1),
5996
- children: "Add"
5997
- }
5998
- ),
5999
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
6000
- ] })
6001
- ] })
6002
- }
6003
- )
6004
- ]
6005
- }
6537
+ onClick: insertLink
6538
+ },
6539
+ "insert-link"
6006
6540
  );
6007
6541
  };
6008
6542
  function PageSetupPlugin({ disabled, value, onChange }) {
@@ -6079,16 +6613,14 @@ var TableItemPlugin = ({ disabled }) => {
6079
6613
  setIsOpen(false);
6080
6614
  };
6081
6615
  return /* @__PURE__ */ jsxRuntime.jsxs(
6082
- reactComponents.Popover,
6616
+ reactComponents.Dialog,
6083
6617
  {
6084
- trapFocus: true,
6085
- withArrow: true,
6086
6618
  open: disabled ? false : isOpen,
6087
6619
  onOpenChange: (_, data) => {
6088
6620
  if (!disabled) setIsOpen(data.open);
6089
6621
  },
6090
6622
  children: [
6091
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.PopoverTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
6623
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
6092
6624
  reactComponents.Button,
6093
6625
  {
6094
6626
  size: "small",
@@ -6111,58 +6643,45 @@ var TableItemPlugin = ({ disabled }) => {
6111
6643
  },
6112
6644
  "insert-table-nodes"
6113
6645
  ) }),
6114
- /* @__PURE__ */ jsxRuntime.jsx(
6115
- reactComponents.PopoverSurface,
6116
- {
6117
- style: {
6118
- width: "270px",
6119
- opacity: disabled ? 0.6 : 1,
6120
- pointerEvents: disabled ? "none" : "auto"
6121
- },
6122
- children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 10 }, children: [
6123
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Rows", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
6124
- reactComponents.Input,
6125
- {
6126
- autoFocus: !disabled,
6127
- type: "number",
6128
- min: 1,
6129
- value: rows,
6130
- placeholder: "Rows",
6131
- appearance: "underline",
6132
- disabled,
6133
- input: { style: { textAlign: "left" } },
6134
- onChange: (_, v) => setRows(v.value.replace(/\D/g, ""))
6135
- }
6136
- ) }),
6137
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Columns", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
6138
- reactComponents.Input,
6139
- {
6140
- type: "number",
6141
- min: 1,
6142
- value: columns,
6143
- placeholder: "Columns",
6144
- appearance: "underline",
6145
- disabled,
6146
- input: { style: { textAlign: "left" } },
6147
- onChange: (_, v) => setColumns(v.value.replace(/\D/g, ""))
6148
- }
6149
- ) }),
6150
- /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
6151
- /* @__PURE__ */ jsxRuntime.jsx(
6152
- reactComponents.Button,
6153
- {
6154
- size: "small",
6155
- appearance: "primary",
6156
- disabled: disabled || !rows || !columns,
6157
- onClick: onAddTable,
6158
- children: "Add"
6159
- }
6160
- ),
6161
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
6162
- ] })
6163
- ] })
6164
- }
6165
- )
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,
6651
+ {
6652
+ autoFocus: !disabled,
6653
+ value: rows,
6654
+ placeholder: "Rows",
6655
+ appearance: "underline",
6656
+ disabled,
6657
+ onChange: (_, v) => setRows(v.value)
6658
+ }
6659
+ ) }),
6660
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "Columns", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
6661
+ reactComponents.Input,
6662
+ {
6663
+ value: columns,
6664
+ placeholder: "Columns",
6665
+ appearance: "underline",
6666
+ disabled,
6667
+ onChange: (_, v) => setColumns(v.value)
6668
+ }
6669
+ ) })
6670
+ ] }) }),
6671
+ /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogActions, { children: [
6672
+ /* @__PURE__ */ jsxRuntime.jsx(
6673
+ reactComponents.Button,
6674
+ {
6675
+ size: "small",
6676
+ appearance: "primary",
6677
+ disabled: disabled || !rows || !columns,
6678
+ onClick: onAddTable,
6679
+ children: "Add"
6680
+ }
6681
+ ),
6682
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
6683
+ ] })
6684
+ ] }) })
6166
6685
  ]
6167
6686
  }
6168
6687
  );
@@ -6186,16 +6705,14 @@ var YoutubeUploadPlugin = ({ disabled }) => {
6186
6705
  setIsOpen(false);
6187
6706
  };
6188
6707
  return /* @__PURE__ */ jsxRuntime.jsxs(
6189
- reactComponents.Popover,
6708
+ reactComponents.Dialog,
6190
6709
  {
6191
- trapFocus: true,
6192
- withArrow: true,
6193
6710
  open: disabled ? false : isOpen,
6194
6711
  onOpenChange: (_, data) => {
6195
6712
  if (!disabled) setIsOpen(data.open);
6196
6713
  },
6197
6714
  children: [
6198
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.PopoverTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
6715
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
6199
6716
  reactComponents.Button,
6200
6717
  {
6201
6718
  title: "Add youtube URL",
@@ -6217,37 +6734,36 @@ var YoutubeUploadPlugin = ({ disabled }) => {
6217
6734
  },
6218
6735
  "upload-video"
6219
6736
  ) }),
6220
- /* @__PURE__ */ jsxRuntime.jsx(
6221
- reactComponents.PopoverSurface,
6222
- {
6223
- style: {
6224
- width: "270px",
6225
- opacity: disabled ? 0.6 : 1,
6226
- pointerEvents: disabled ? "none" : "auto"
6227
- },
6228
- children: /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { tokens: { childrenGap: 10 }, children: [
6229
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Field, { label: "URL", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(
6230
- reactComponents.Input,
6231
- {
6232
- autoFocus: !disabled,
6233
- disabled,
6234
- value: url,
6235
- appearance: "underline",
6236
- placeholder: "Add Youtube video URL",
6237
- onChange: (_, v) => setURL(v.value)
6238
- }
6239
- ) }),
6240
- /* @__PURE__ */ jsxRuntime.jsxs(react.Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
6241
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled: disabled || !url, onClick: onHandleEmbeded, children: "Add" }),
6242
- /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
6243
- ] })
6244
- ] })
6245
- }
6246
- )
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
+ ) }) }) }),
6750
+ /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.DialogActions, { children: [
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" })
6753
+ ] })
6754
+ ] }) })
6247
6755
  ]
6248
6756
  }
6249
6757
  );
6250
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
+ ] });
6251
6767
  var useStyles4 = reactComponents.makeStyles({
6252
6768
  dropdown: {
6253
6769
  minInlineSize: "90px",
@@ -6265,24 +6781,42 @@ var ALLOWED_TOKENS = {
6265
6781
  Bold: true,
6266
6782
  Italic: true,
6267
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,
6268
6802
  ColorPicker: true,
6269
6803
  Link: true,
6270
6804
  Table: true,
6271
6805
  Image: true,
6272
6806
  InlineImage: true,
6273
6807
  Youtube: true,
6274
- Heading: true,
6275
6808
  FontFamily: true,
6276
6809
  FontSize: true,
6810
+ Align: true,
6811
+ Heading: true,
6277
6812
  Decorators: true,
6278
6813
  CodeBlock: true,
6279
- Align: true,
6280
6814
  PageSetup: true
6281
6815
  };
6282
6816
  function sanitizePluginGroups(groups) {
6283
- if (!groups || groups.length === 0) return [];
6817
+ if (!Array.isArray(groups) || groups.length === 0) return [];
6284
6818
  return groups.map(
6285
- (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)
6286
6820
  ).filter((g) => g.length > 0);
6287
6821
  }
6288
6822
  var ToolBarPlugins = (props) => {
@@ -6302,8 +6836,9 @@ var ToolBarPlugins = (props) => {
6302
6836
  const [isLowercase, setIsLowercase] = React9.useState(false);
6303
6837
  const [isCapitalize, setIsCapitalize] = React9.useState(false);
6304
6838
  const [alignment, setAlignment] = React9.useState("left");
6305
- const lastSelectionRef = React9__namespace.default.useRef(null);
6306
- 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);
6307
6842
  const pluginGroups = React9.useMemo(() => sanitizePluginGroups(presetGroups), [presetGroups]);
6308
6843
  const updateToolbarPlugins = () => {
6309
6844
  const selection = lexical.$getSelection();
@@ -6345,6 +6880,10 @@ var ToolBarPlugins = (props) => {
6345
6880
  setSelectNodeType("paragraph");
6346
6881
  return;
6347
6882
  }
6883
+ if ($isAlphaListNode(element)) {
6884
+ setSelectNodeType("alpha");
6885
+ return;
6886
+ }
6348
6887
  if (list.$isListNode(element)) {
6349
6888
  const parentList = utils.$getNearestNodeOfType(anchorNode, list.ListNode);
6350
6889
  const type2 = parentList ? parentList.getTag() : element.getTag();
@@ -6356,23 +6895,15 @@ var ToolBarPlugins = (props) => {
6356
6895
  ["paragraph", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "quote", "code"].includes(type) ? type : "paragraph"
6357
6896
  );
6358
6897
  };
6359
- const applyToBlock = React9__namespace.default.useCallback(
6360
- (fn) => {
6361
- editor.update(() => {
6362
- const saved = lastSelectionRef.current;
6363
- if (saved) lexical.$setSelection(saved.clone());
6364
- const sel = lexical.$getSelection();
6365
- if (lexical.$isRangeSelection(sel)) fn(sel);
6366
- });
6367
- },
6368
- [editor]
6369
- );
6370
6898
  const formatQuote = () => {
6371
- applyToBlock((selection$1) => {
6899
+ editor.update(() => {
6900
+ const selection$1 = lexical.$getSelection();
6901
+ if (!lexical.$isRangeSelection(selection$1)) return;
6372
6902
  if (selectNodeType === "quote") {
6373
- selection.$setBlocksType(selection$1, () => lexical.$createParagraphNode());
6903
+ formatParagraph(editor);
6374
6904
  } else {
6375
- selection.$setBlocksType(selection$1, () => richText.$createQuoteNode());
6905
+ $splitBlocksAtLineBreaks(selection$1);
6906
+ selection.$setBlocksType(lexical.$getSelection(), () => richText.$createQuoteNode());
6376
6907
  }
6377
6908
  });
6378
6909
  };
@@ -6389,8 +6920,6 @@ var ToolBarPlugins = (props) => {
6389
6920
  editor.registerCommand(
6390
6921
  lexical.SELECTION_CHANGE_COMMAND,
6391
6922
  () => {
6392
- const sel = lexical.$getSelection();
6393
- if (lexical.$isRangeSelection(sel)) lastSelectionRef.current = sel.clone();
6394
6923
  updateToolbarPlugins();
6395
6924
  return false;
6396
6925
  },
@@ -6422,52 +6951,16 @@ var ToolBarPlugins = (props) => {
6422
6951
  editor.dispatchCommand(lexical.FORMAT_TEXT_COMMAND, "highlight");
6423
6952
  break;
6424
6953
  case "leftAlign" /* LeftAlign */:
6425
- applyToBlock((sel) => {
6426
- const seen = /* @__PURE__ */ new Set();
6427
- sel.getNodes().forEach((n) => {
6428
- const t = n.getTopLevelElementOrThrow();
6429
- if (!seen.has(t.getKey())) {
6430
- seen.add(t.getKey());
6431
- t.setFormat("left");
6432
- }
6433
- });
6434
- });
6954
+ applyAlignmentWithSplit("left");
6435
6955
  break;
6436
6956
  case "rightAlign" /* RightAlign */:
6437
- applyToBlock((sel) => {
6438
- const seen = /* @__PURE__ */ new Set();
6439
- sel.getNodes().forEach((n) => {
6440
- const t = n.getTopLevelElementOrThrow();
6441
- if (!seen.has(t.getKey())) {
6442
- seen.add(t.getKey());
6443
- t.setFormat("right");
6444
- }
6445
- });
6446
- });
6957
+ applyAlignmentWithSplit("right");
6447
6958
  break;
6448
6959
  case "centerAlign" /* CenterAlign */:
6449
- applyToBlock((sel) => {
6450
- const seen = /* @__PURE__ */ new Set();
6451
- sel.getNodes().forEach((n) => {
6452
- const t = n.getTopLevelElementOrThrow();
6453
- if (!seen.has(t.getKey())) {
6454
- seen.add(t.getKey());
6455
- t.setFormat("center");
6456
- }
6457
- });
6458
- });
6960
+ applyAlignmentWithSplit("center");
6459
6961
  break;
6460
6962
  case "justifyAlign" /* JustifyAlign */:
6461
- applyToBlock((sel) => {
6462
- const seen = /* @__PURE__ */ new Set();
6463
- sel.getNodes().forEach((n) => {
6464
- const t = n.getTopLevelElementOrThrow();
6465
- if (!seen.has(t.getKey())) {
6466
- seen.add(t.getKey());
6467
- t.setFormat("justify");
6468
- }
6469
- });
6470
- });
6963
+ applyAlignmentWithSplit("justify");
6471
6964
  break;
6472
6965
  case "undo" /* Undo */:
6473
6966
  editor.dispatchCommand(lexical.UNDO_COMMAND, void 0);
@@ -6486,9 +6979,32 @@ var ToolBarPlugins = (props) => {
6486
6979
  break;
6487
6980
  }
6488
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
+ };
6489
6994
  const updateHeading = (heading) => {
6490
- applyToBlock((selection$1) => {
6491
- 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();
6492
7008
  });
6493
7009
  };
6494
7010
  const renderToken = (token, groupIndex, tokenIndex) => {
@@ -6500,7 +7016,6 @@ var ToolBarPlugins = (props) => {
6500
7016
  const brand = palette.themePrimary;
6501
7017
  const brandHover = palette.themeDarkAlt ?? brand;
6502
7018
  const brandPressed = palette.themeDark ?? brand;
6503
- palette.white;
6504
7019
  const bgHover = palette.neutralLighter;
6505
7020
  const bgPressed = palette.neutralLight;
6506
7021
  const bgActive = palette.neutralLighterAlt ?? palette.neutralLighter;
@@ -6553,6 +7068,8 @@ var ToolBarPlugins = (props) => {
6553
7068
  reactComponents.Button,
6554
7069
  {
6555
7070
  size: "small",
7071
+ "aria-label": "Bold",
7072
+ "aria-pressed": isBold,
6556
7073
  disabled: !isEditable || props.readOnly,
6557
7074
  icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextBold24Regular, { style: { color: getIconColor(isBold) } }),
6558
7075
  style: getButtonStyle(isBold),
@@ -6565,6 +7082,8 @@ var ToolBarPlugins = (props) => {
6565
7082
  reactComponents.Button,
6566
7083
  {
6567
7084
  size: "small",
7085
+ "aria-label": "Italic",
7086
+ "aria-pressed": isItalic,
6568
7087
  disabled: !isEditable || props.readOnly,
6569
7088
  icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextItalicFilled, { style: { color: getIconColor(isItalic) } }),
6570
7089
  style: getButtonStyle(isItalic),
@@ -6577,6 +7096,8 @@ var ToolBarPlugins = (props) => {
6577
7096
  reactComponents.Button,
6578
7097
  {
6579
7098
  size: "small",
7099
+ "aria-label": "Underline",
7100
+ "aria-pressed": isUnderline,
6580
7101
  disabled: !isEditable || props.readOnly,
6581
7102
  icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextUnderlineFilled, { style: { color: getIconColor(isUnderline) } }),
6582
7103
  style: getButtonStyle(isUnderline),
@@ -6587,7 +7108,14 @@ var ToolBarPlugins = (props) => {
6587
7108
  case "ColorPicker":
6588
7109
  return /* @__PURE__ */ jsxRuntime.jsx(ColorPickerPlugin, { disabled: !isEditable || props.readOnly }, key);
6589
7110
  case "Link":
6590
- 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
+ );
6591
7119
  case "Table":
6592
7120
  return /* @__PURE__ */ jsxRuntime.jsx(TableItemPlugin, { disabled: !isEditable || props.readOnly }, key);
6593
7121
  case "Image":
@@ -6595,7 +7123,9 @@ var ToolBarPlugins = (props) => {
6595
7123
  InsertImageDialog,
6596
7124
  {
6597
7125
  activeEditor: editor,
6598
- disabled: !isEditable || props.readOnly
7126
+ disabled: !isEditable || props.readOnly,
7127
+ maxImageSizeMB: props.maxImageSizeMB,
7128
+ validationMessages: props.validationMessages
6599
7129
  },
6600
7130
  key
6601
7131
  );
@@ -6604,7 +7134,9 @@ var ToolBarPlugins = (props) => {
6604
7134
  InsertInlineImageDialog,
6605
7135
  {
6606
7136
  activeEditor: editor,
6607
- disabled: !isEditable || props.readOnly
7137
+ disabled: !isEditable || props.readOnly,
7138
+ maxImageSizeMB: props.maxImageSizeMB,
7139
+ validationMessages: props.validationMessages
6608
7140
  },
6609
7141
  key
6610
7142
  );
@@ -6627,7 +7159,7 @@ var ToolBarPlugins = (props) => {
6627
7159
  const val = data.optionValue;
6628
7160
  if (!val) return;
6629
7161
  if (val === "paragraph") {
6630
- applyToBlock((sel) => selection.$setBlocksType(sel, () => lexical.$createParagraphNode()));
7162
+ formatParagraph(editor);
6631
7163
  setSelectNodeType("paragraph");
6632
7164
  } else {
6633
7165
  updateHeading(val);
@@ -6648,83 +7180,456 @@ var ToolBarPlugins = (props) => {
6648
7180
  return /* @__PURE__ */ jsxRuntime.jsx(FontSizePlugin, { disabled: !isEditable || props.readOnly }, key);
6649
7181
  case "|":
6650
7182
  return /* @__PURE__ */ jsxRuntime.jsx(reactComponents.ToolbarDivider, {}, key);
6651
- case "Decorators": {
6652
- const activeDecorators = [
6653
- ...isUppercase ? ["uppercase"] : [],
6654
- ...isLowercase ? ["lowercase"] : [],
6655
- ...isCapitalize ? ["capitalize"] : [],
6656
- ...isStrikethrough ? ["strike"] : [],
6657
- ...isSubscript ? ["subscript"] : [],
6658
- ...isSuperscript ? ["superscript"] : [],
6659
- ...isHighlight ? ["highlight"] : [],
6660
- ...selectNodeType === "ul" ? ["ul-list"] : [],
6661
- ...selectNodeType === "ol" ? ["ol-list"] : [],
6662
- ...selectNodeType === "quote" ? ["quote"] : []
6663
- ];
6664
- const DECORATOR_LABEL = {
6665
- uppercase: "Uppercase",
6666
- lowercase: "Lowercase",
6667
- capitalize: "Capitalize",
6668
- strike: "Strikethrough",
6669
- subscript: "Subscript",
6670
- superscript: "Superscript",
6671
- highlight: "Highlight",
6672
- "ul-list": "Bullet list",
6673
- "ol-list": "Number list",
6674
- quote: "Quote"
6675
- };
6676
- const decoratorValue = activeDecorators.length === 0 ? "" : activeDecorators.length === 1 ? DECORATOR_LABEL[activeDecorators[0]] : `${DECORATOR_LABEL[activeDecorators[0]]} +${activeDecorators.length - 1}`;
6677
- return /* @__PURE__ */ jsxRuntime.jsxs(
6678
- reactComponents.Dropdown,
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,
6679
7189
  {
6680
- multiselect: true,
6681
- id: `${groupIndex}-set-decorators`,
6682
- placeholder: "Text",
6683
- value: decoratorValue,
6684
- selectedOptions: activeDecorators,
6685
- disabled: !isEditable,
6686
- className: styles.alignDropdown,
6687
- button: { style: dropdownButtonStyle },
6688
- expandIcon: { style: dropdownExpandIconStyle },
6689
- listbox: { style: { minInlineSize: "180px" } },
6690
- onOptionSelect: (_, data) => {
6691
- switch (data.optionValue) {
6692
- case "uppercase":
6693
- editor.dispatchCommand(lexical.FORMAT_TEXT_COMMAND, "uppercase");
6694
- break;
6695
- case "lowercase":
6696
- onHandleSelectOption("lowercase" /* Lowercase */);
6697
- break;
6698
- case "capitalize":
6699
- onHandleSelectOption("capitalize" /* Capitalize */);
6700
- break;
6701
- case "strike":
6702
- onHandleSelectOption("strike" /* Strikethrough */);
6703
- break;
6704
- case "subscript":
6705
- onHandleSelectOption("subscript" /* Subscript */);
6706
- break;
6707
- case "superscript":
6708
- onHandleSelectOption("superscript" /* Superscript */);
6709
- break;
6710
- case "highlight":
6711
- onHandleSelectOption("highlight" /* Highlight */);
6712
- break;
6713
- case "ul-list":
6714
- editor.dispatchCommand(selectNodeType === "ul" ? list.REMOVE_LIST_COMMAND : list.INSERT_UNORDERED_LIST_COMMAND, void 0);
6715
- break;
6716
- case "ol-list":
6717
- editor.dispatchCommand(selectNodeType === "ol" ? list.REMOVE_LIST_COMMAND : list.INSERT_ORDERED_LIST_COMMAND, void 0);
6718
- break;
6719
- case "page-break":
6720
- editor.dispatchCommand(INSERT_PAGE_BREAK, void 0);
6721
- break;
6722
- case "quote":
6723
- formatQuote();
6724
- break;
6725
- }
6726
- },
6727
- children: [
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);
7499
+ setSelectNodeType("paragraph");
7500
+ } else {
7501
+ updateHeading("h5");
7502
+ setSelectNodeType("h5");
7503
+ }
7504
+ },
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"
7533
+ },
7534
+ key
7535
+ );
7536
+ case "Decorators": {
7537
+ const activeDecorators = [
7538
+ ...isUppercase ? ["uppercase"] : [],
7539
+ ...isLowercase ? ["lowercase"] : [],
7540
+ ...isCapitalize ? ["capitalize"] : [],
7541
+ ...isStrikethrough ? ["strike"] : [],
7542
+ ...isSubscript ? ["subscript"] : [],
7543
+ ...isSuperscript ? ["superscript"] : [],
7544
+ ...isHighlight ? ["highlight"] : [],
7545
+ ...selectNodeType === "ul" ? ["ul-list"] : [],
7546
+ ...selectNodeType === "ol" ? ["ol-list"] : [],
7547
+ ...selectNodeType === "alpha" ? ["al-list"] : [],
7548
+ ...selectNodeType === "quote" ? ["quote"] : []
7549
+ ];
7550
+ const DECORATOR_LABEL = {
7551
+ uppercase: "Uppercase",
7552
+ lowercase: "Lowercase",
7553
+ capitalize: "Capitalize",
7554
+ strike: "Strikethrough",
7555
+ subscript: "Subscript",
7556
+ superscript: "Superscript",
7557
+ highlight: "Highlight",
7558
+ "ul-list": "Bullet list",
7559
+ "ol-list": "Number list",
7560
+ "al-list": "Alphabetical list",
7561
+ quote: "Quote"
7562
+ };
7563
+ const decoratorValue = activeDecorators.length === 0 ? "" : activeDecorators.length === 1 ? DECORATOR_LABEL[activeDecorators[0]] : `${DECORATOR_LABEL[activeDecorators[0]]} +${activeDecorators.length - 1}`;
7564
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7565
+ reactComponents.Dropdown,
7566
+ {
7567
+ multiselect: true,
7568
+ id: `${groupIndex}-set-decorators`,
7569
+ placeholder: "Text",
7570
+ value: decoratorValue,
7571
+ selectedOptions: activeDecorators,
7572
+ disabled: !isEditable,
7573
+ className: styles.alignDropdown,
7574
+ button: { style: dropdownButtonStyle },
7575
+ expandIcon: { style: dropdownExpandIconStyle },
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
+ },
7585
+ onOptionSelect: (_, data) => {
7586
+ decoratorSelectingRef.current = true;
7587
+ switch (data.optionValue) {
7588
+ case "uppercase":
7589
+ editor.dispatchCommand(lexical.FORMAT_TEXT_COMMAND, "uppercase");
7590
+ break;
7591
+ case "lowercase":
7592
+ onHandleSelectOption("lowercase" /* Lowercase */);
7593
+ break;
7594
+ case "capitalize":
7595
+ onHandleSelectOption("capitalize" /* Capitalize */);
7596
+ break;
7597
+ case "strike":
7598
+ onHandleSelectOption("strike" /* Strikethrough */);
7599
+ break;
7600
+ case "subscript":
7601
+ onHandleSelectOption("subscript" /* Subscript */);
7602
+ break;
7603
+ case "superscript":
7604
+ onHandleSelectOption("superscript" /* Superscript */);
7605
+ break;
7606
+ case "highlight":
7607
+ onHandleSelectOption("highlight" /* Highlight */);
7608
+ break;
7609
+ case "ul-list":
7610
+ editor.dispatchCommand(
7611
+ selectNodeType === "ul" ? list.REMOVE_LIST_COMMAND : list.INSERT_UNORDERED_LIST_COMMAND,
7612
+ void 0
7613
+ );
7614
+ break;
7615
+ case "ol-list":
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());
7623
+ break;
7624
+ case "page-break":
7625
+ editor.dispatchCommand(INSERT_PAGE_BREAK, void 0);
7626
+ break;
7627
+ case "quote":
7628
+ formatQuote();
7629
+ break;
7630
+ }
7631
+ },
7632
+ children: [
6728
7633
  /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Option, { value: "uppercase", text: "Uppercase", children: [
6729
7634
  /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextCaseUppercaseFilled, { style: optionIconStyle }),
6730
7635
  "Uppercase"
@@ -6750,7 +7655,12 @@ var ToolBarPlugins = (props) => {
6750
7655
  "Superscript"
6751
7656
  ] }),
6752
7657
  /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Option, { value: "highlight", text: "Highlight", children: [
6753
- /* @__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
+ ),
6754
7664
  "Highlight"
6755
7665
  ] }),
6756
7666
  /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Option, { value: "ul-list", text: "Bullet list", children: [
@@ -6761,6 +7671,10 @@ var ToolBarPlugins = (props) => {
6761
7671
  /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextNumberListLtrFilled, { style: optionIconStyle }),
6762
7672
  "Number list"
6763
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
+ ] }),
6764
7678
  /* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Option, { value: "page-break", text: "Page Break", children: [
6765
7679
  /* @__PURE__ */ jsxRuntime.jsx(reactIcons.DocumentPageBreakRegular, { style: optionIconStyle }),
6766
7680
  "Page break"
@@ -6786,10 +7700,30 @@ var ToolBarPlugins = (props) => {
6786
7700
  // );
6787
7701
  case "Align": {
6788
7702
  const ALIGN_OPTIONS = [
6789
- { value: "left", label: "Left Align", icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignLeftFilled, { style: optionIconStyle }), action: "leftAlign" /* LeftAlign */ },
6790
- { value: "center", label: "Center Align", icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignCenterFilled, { style: optionIconStyle }), action: "centerAlign" /* CenterAlign */ },
6791
- { value: "right", label: "Right Align", icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.TextAlignRightFilled, { style: optionIconStyle }), action: "rightAlign" /* RightAlign */ },
6792
- { 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
+ }
6793
7727
  ];
6794
7728
  const alignLabel = ALIGN_OPTIONS.find((o) => o.value === alignment)?.label ?? "Left Align";
6795
7729
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -7005,28 +7939,31 @@ function BrowserSpellCheckPlugin({ enabled }) {
7005
7939
  }, [editor, enabled]);
7006
7940
  return null;
7007
7941
  }
7008
- function WordCountPlugin({ onCountChange }) {
7009
- const [editor] = LexicalComposerContext.useLexicalComposerContext();
7010
- React9.useEffect(() => {
7011
- return editor.registerUpdateListener(({ editorState }) => {
7012
- editorState.read(() => {
7013
- const text = lexical.$getRoot().getTextContent();
7014
- const words = text.trim() === "" ? 0 : text.trim().split(/\s+/).length;
7015
- onCountChange(words);
7016
- });
7017
- });
7018
- }, [editor, onCountChange]);
7019
- return null;
7020
- }
7021
- function CharCountPlugin({ onCountChange }) {
7942
+ function ContentMetricsPlugin({
7943
+ onMetricsChange
7944
+ }) {
7022
7945
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
7946
+ const prevRef = React9.useRef({ words: 0, chars: 0, images: 0, links: 0, tables: 0 });
7023
7947
  React9.useEffect(() => {
7024
- return editor.registerUpdateListener(({ editorState }) => {
7025
- editorState.read(() => {
7026
- onCountChange(lexical.$getRoot().getTextContent().length);
7027
- });
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
+ }
7028
7965
  });
7029
- }, [editor, onCountChange]);
7966
+ }, [editor, onMetricsChange]);
7030
7967
  return null;
7031
7968
  }
7032
7969
  function FocusEventsPlugin({
@@ -7047,7 +7984,8 @@ function FocusEventsPlugin({
7047
7984
  const next = e.relatedTarget;
7048
7985
  const container = containerRef.current;
7049
7986
  const stillInside = !!next && (container ? container.contains(next) : root.contains(next));
7050
- if (stillInside) return;
7987
+ const isEditorPortal = !!next?.closest?.("[data-lexical-editor-portal]");
7988
+ if (stillInside || isEditorPortal) return;
7051
7989
  editor.update(() => {
7052
7990
  lexical.$setSelection(null);
7053
7991
  });
@@ -7157,6 +8095,16 @@ function _makeQueryFn(fn) {
7157
8095
  };
7158
8096
  };
7159
8097
  }
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
+ }
7160
8108
  var ContentEditorComponent = React9.forwardRef(
7161
8109
  (props, ref) => {
7162
8110
  const isReadOnly = !!props.readOnly;
@@ -7172,13 +8120,17 @@ var ContentEditorComponent = React9.forwardRef(
7172
8120
  );
7173
8121
  const [floatingAnchorElem, setFloatingAnchorElem] = React9.useState(null);
7174
8122
  const [isLinkEditMode, setIsLinkEditMode] = React9.useState(false);
7175
- const [wordCount, setWordCount] = React9.useState(0);
7176
- const handleWordCount = React9.useCallback((count) => setWordCount(count), []);
7177
- const [charCount, setCharCount] = React9.useState(0);
7178
- const handleCharCount = React9.useCallback((count) => setCharCount(count), []);
7179
- const [refErrors, setRefErrors] = React9.useState([]);
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), []);
7180
8131
  const [pageSetup, setPageSetup] = React9.useState(DEFAULT_PAGE_SETUP);
7181
8132
  const pageCanvas = resolvePageCanvasMetrics(pageSetup);
8133
+ const wordCount = metrics.words;
7182
8134
  const contentEditableDomRef = React9.useRef(null);
7183
8135
  const previousOverLimitRef = React9.useRef(false);
7184
8136
  const focusedRef = React9.useRef(false);
@@ -7189,32 +8141,37 @@ var ContentEditorComponent = React9.forwardRef(
7189
8141
  const onAnchorRef = (elem) => {
7190
8142
  if (elem) setFloatingAnchorElem(elem);
7191
8143
  };
7192
- const initialConfig = {
7193
- namespace: props.namespace,
7194
- theme,
7195
- onError: () => {
7196
- },
7197
- nodes: [
7198
- richText.HeadingNode,
7199
- richText.QuoteNode,
7200
- code.CodeHighlightNode,
7201
- code.CodeNode,
7202
- list.ListNode,
7203
- list.ListItemNode,
7204
- link.LinkNode,
7205
- link.AutoLinkNode,
7206
- table.TableNode,
7207
- table.TableRowNode,
7208
- table.TableCellNode,
7209
- ImageNode,
7210
- InlineImageNode,
7211
- YouTubeNode,
7212
- PageBreakNode,
7213
- AutocompleteNode,
7214
- SpellErrorNode,
7215
- HtmlBlockNode
7216
- ]
7217
- };
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
+ }, []);
7218
8175
  const EditorStyles = react.mergeStyleSets({
7219
8176
  editorPlaceholder: {
7220
8177
  color: "var(--colorNeutralForeground3, grey)",
@@ -7256,41 +8213,7 @@ var ContentEditorComponent = React9.forwardRef(
7256
8213
  e.stopPropagation();
7257
8214
  }
7258
8215
  };
7259
- const [touched, setTouched] = React9.useState(false);
7260
8216
  const isOverLimit = props.wordLimit !== void 0 && wordCount > props.wordLimit;
7261
- const internalErrors = [];
7262
- if (isOverLimit) {
7263
- const m = props.errorMessages?.wordLimitExceeded;
7264
- internalErrors.push(
7265
- typeof m === "function" ? m(wordCount, props.wordLimit) : m ?? `Word limit exceeded (${wordCount} / ${props.wordLimit} words used)`
7266
- );
7267
- }
7268
- if (props.required && touched && wordCount === 0) {
7269
- internalErrors.push(
7270
- props.errorMessages?.required ?? "This field is required"
7271
- );
7272
- }
7273
- if (props.minWords !== void 0 && touched && wordCount < props.minWords) {
7274
- const m = props.errorMessages?.minWords;
7275
- internalErrors.push(
7276
- typeof m === "function" ? m(wordCount, props.minWords) : m ?? `Minimum ${props.minWords} words required (${wordCount} entered)`
7277
- );
7278
- }
7279
- if (props.maxChars !== void 0 && charCount > props.maxChars) {
7280
- const m = props.errorMessages?.maxCharsExceeded;
7281
- internalErrors.push(
7282
- typeof m === "function" ? m(charCount, props.maxChars) : m ?? `Character limit exceeded (${charCount} / ${props.maxChars} characters used)`
7283
- );
7284
- }
7285
- if (props.minChars !== void 0 && touched && charCount < props.minChars && charCount > 0) {
7286
- const m = props.errorMessages?.minCharsRequired;
7287
- internalErrors.push(
7288
- typeof m === "function" ? m(charCount, props.minChars) : m ?? `Minimum ${props.minChars} characters required (${charCount} entered)`
7289
- );
7290
- }
7291
- const allErrors = [...internalErrors, ...props.errors ?? [], ...refErrors];
7292
- const hasErrors = allErrors.length > 0;
7293
- const hasRedBorder = hasErrors;
7294
8217
  React9.useEffect(() => {
7295
8218
  if (props.wordLimit === void 0 || !props.onWordLimitExceeded) return;
7296
8219
  const wasOverLimit = previousOverLimitRef.current;
@@ -7303,215 +8226,329 @@ var ContentEditorComponent = React9.forwardRef(
7303
8226
  previousOverLimitRef.current = isOverLimit;
7304
8227
  }
7305
8228
  }, [isOverLimit, wordCount, props.wordLimit, props.onWordLimitExceeded]);
7306
- 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(
7307
- react.Stack,
7308
- {
7309
- style: {
7310
- zIndex: 1e3,
7311
- background: "#fff",
7312
- borderRadius: "2px",
7313
- width: props.width ?? "100%",
7314
- height: props.height ?? "100%",
7315
- margin: props.margin ?? "5px auto",
7316
- border: `1px solid ${hasRedBorder ? "#c4272c" : "var(--colorNeutralStroke1, #ccced1)"}`,
7317
- transition: "border-color 0.2s",
7318
- display: "flex",
7319
- flexDirection: "column"
7320
- },
7321
- children: [
7322
- /* @__PURE__ */ jsxRuntime.jsx(
7323
- "div",
7324
- {
7325
- style: {
7326
- pointerEvents: isReadOnly ? "none" : "auto",
7327
- position: "sticky",
7328
- opacity: isReadOnly ? 0.85 : 1
7329
- },
7330
- children: /* @__PURE__ */ jsxRuntime.jsx(
7331
- ToolBarPlugins,
7332
- {
7333
- level: props.level ?? "basic" /* Basic */,
7334
- readOnly: props.readOnly,
7335
- pageSetup,
7336
- onPageSetupChange: setPageSetup
7337
- }
7338
- )
7339
- }
7340
- ),
7341
- /* @__PURE__ */ jsxRuntime.jsxs(
7342
- "div",
7343
- {
7344
- style: {
7345
- position: "relative",
7346
- flexGrow: 1,
7347
- padding: "15px 0px",
7348
- overflowY: "scroll",
7349
- overflowX: "auto",
7350
- minWidth: 0,
7351
- background: pageCanvas.widthPx !== void 0 ? "#eef0f2" : void 0
7352
- },
7353
- onClickCapture: handleReadOnlyClickCapture,
7354
- children: [
7355
- /* @__PURE__ */ jsxRuntime.jsx(
7356
- LexicalRichTextPlugin.RichTextPlugin,
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",
8316
+ {
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,
7357
8325
  {
7358
- ErrorBoundary: LexicalErrorBoundary.LexicalErrorBoundary,
7359
- contentEditable: /* @__PURE__ */ jsxRuntime.jsx(
7360
- "div",
7361
- {
7362
- className: "editor",
7363
- style: { height: "100%", position: "relative" },
7364
- ref: onAnchorRef,
7365
- children: /* @__PURE__ */ jsxRuntime.jsx(
7366
- LexicalContentEditable.ContentEditable,
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
+ )
8336
+ }
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,
8354
+ {
8355
+ ErrorBoundary: LexicalErrorBoundary.LexicalErrorBoundary,
8356
+ contentEditable: /* @__PURE__ */ jsxRuntime.jsx(
8357
+ "div",
8358
+ {
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
+ )
8381
+ }
8382
+ ),
8383
+ placeholder: /* @__PURE__ */ jsxRuntime.jsx(react.Stack, { className: react.css(EditorStyles.editorPlaceholder), children: props.placeholder })
8384
+ }
8385
+ ),
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",
8388
+ {
8389
+ style: {
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"
8398
+ },
8399
+ children: [
8400
+ (props.wordLimit !== void 0 || props.maxWords !== void 0 || props.minWords !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs(
8401
+ "span",
7367
8402
  {
7368
- ref: contentEditableDomRef,
7369
- className: react.css(EditorStyles.contentEditor),
7370
8403
  style: {
7371
- paddingTop: props.level !== "none" /* None */ ? 0 : 10,
7372
- paddingLeft: pageCanvas.paddingPx,
7373
- paddingRight: pageCanvas.paddingPx,
7374
- maxWidth: pageCanvas.widthPx,
7375
- marginLeft: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
7376
- marginRight: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
7377
- boxShadow: pageCanvas.widthPx !== void 0 ? "0 0 0 1px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.08)" : void 0
8404
+ fontSize: "11px",
8405
+ color: hasValidationError ? "#c4272c" : "var(--colorNeutralForeground3, #aaa)",
8406
+ fontWeight: hasValidationError ? 600 : 400,
8407
+ transition: "color 0.2s, font-weight 0.2s"
7378
8408
  },
7379
- spellCheck: !resolvedSpellCheck,
7380
- autoCorrect: resolvedSpellCheck ? "off" : void 0,
7381
- autoCapitalize: resolvedSpellCheck ? "off" : void 0
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
+ ]
7382
8432
  }
7383
8433
  )
7384
- }
7385
- ),
7386
- placeholder: /* @__PURE__ */ jsxRuntime.jsx(react.Stack, { className: react.css(EditorStyles.editorPlaceholder), children: props.placeholder })
7387
- }
7388
- ),
7389
- props.wordLimit !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
7390
- "div",
7391
- {
7392
- style: {
7393
- position: "sticky",
7394
- bottom: 0,
7395
- display: "flex",
7396
- justifyContent: "flex-end",
7397
- paddingRight: 14,
7398
- pointerEvents: "none",
7399
- userSelect: "none"
7400
- },
7401
- children: /* @__PURE__ */ jsxRuntime.jsxs(
7402
- "span",
7403
- {
7404
- style: {
7405
- fontSize: "11px",
7406
- color: isOverLimit ? "#c4272c" : "var(--colorNeutralForeground3, #aaa)",
7407
- fontWeight: isOverLimit ? 600 : 400,
7408
- transition: "color 0.2s, font-weight 0.2s"
7409
- },
7410
- children: [
7411
- wordCount,
7412
- " / ",
7413
- props.wordLimit,
7414
- " words"
7415
- ]
7416
- }
7417
- )
7418
- }
7419
- )
7420
- ]
7421
- }
7422
- ),
7423
- hasErrors && /* @__PURE__ */ jsxRuntime.jsx(
7424
- "div",
7425
- {
7426
- style: {
7427
- borderTop: "1px solid #fbd5d5",
7428
- background: "#fff8f8",
7429
- padding: "6px 12px 8px",
7430
- display: "flex",
7431
- flexDirection: "column",
7432
- gap: 4
7433
- },
7434
- children: allErrors.map((err, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
7435
- /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ErrorCircleRegular, { style: { fontSize: 14, color: "#c4272c", flexShrink: 0 } }),
7436
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#c4272c" }, children: err })
7437
- ] }, i))
7438
- }
7439
- ),
7440
- /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyPlugin, { readonly: isReadOnly }),
7441
- /* @__PURE__ */ jsxRuntime.jsx(BrowserSpellCheckPlugin, { enabled: !resolvedSpellCheck }),
7442
- /* @__PURE__ */ jsxRuntime.jsx(
7443
- FocusEventsPlugin,
7444
- {
7445
- onFocus: props.onFocus,
7446
- onBlur: () => {
7447
- setTouched(true);
7448
- props.onBlur?.();
7449
- },
7450
- setFocused,
7451
- containerRef
7452
- }
7453
- ),
7454
- props.autoFocus && !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(LexicalAutoFocusPlugin.AutoFocusPlugin, {}),
7455
- /* @__PURE__ */ jsxRuntime.jsx(LexicalHistoryPlugin.HistoryPlugin, {}),
7456
- /* @__PURE__ */ jsxRuntime.jsx(LexicalListPlugin.ListPlugin, {}),
7457
- /* @__PURE__ */ jsxRuntime.jsx(LexicalLinkPlugin.LinkPlugin, { validateUrl }),
7458
- /* @__PURE__ */ jsxRuntime.jsx(LexicalAutoLinkPlugin.AutoLinkPlugin, { matchers: MATCHERS }),
7459
- /* @__PURE__ */ jsxRuntime.jsx(LexicalTablePlugin.TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true }),
7460
- !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(YoutubeDeletePlugin, {}),
7461
- !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsxRuntime.jsx(TableActionMenuPlugin, {}),
7462
- !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsxRuntime.jsx(TableCellResizerPlugin, { anchorElem: floatingAnchorElem }),
7463
- !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
7464
- FloatingLinkEditorPlugin,
7465
- {
7466
- anchorElem: floatingAnchorElem,
7467
- isLinkEditMode,
7468
- setIsLinkEditMode
7469
- }
7470
- ),
7471
- !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(ImagePlugin_default, {}),
7472
- !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(InlineImage_default, {}),
7473
- !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(PageBreakPlugin, {}),
7474
- !!resolvedQuery && !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
7475
- AutocompletePlugin,
7476
- {
7477
- useQuery: resolvedQuery,
7478
- isReadOnly,
7479
- onSuggestionShown: props.onSuggestionShown,
7480
- onSuggestionAccept: props.onSuggestionAccept,
7481
- idleMs: props.suggestIdleMs ?? 300,
7482
- minWords: 4,
7483
- prefixWindow: 300
7484
- }
7485
- ),
7486
- !!resolvedSpellCheck && !isReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
7487
- SpellCheckPlugin,
7488
- {
7489
- useSpellCheck: resolvedSpellCheck,
7490
- onSpellCheckAccept: props.onSpellCheckAccept,
7491
- idleMs: props.spellCheckIdleMs ?? 1200,
7492
- enabled: props.spellCheckEnabled !== false
7493
- }
7494
- ),
7495
- !isReadOnly && props.showFloatingToolbar && /* @__PURE__ */ jsxRuntime.jsx(CharacterStylesPopupPlugin, {}),
7496
- /* @__PURE__ */ jsxRuntime.jsx(CustomOnChangePlugin, { value: props.value, onChange: props.onChange }),
7497
- (props.wordLimit !== void 0 || props.required || props.minWords !== void 0) && /* @__PURE__ */ jsxRuntime.jsx(WordCountPlugin, { onCountChange: handleWordCount }),
7498
- (props.maxChars !== void 0 || props.minChars !== void 0) && /* @__PURE__ */ jsxRuntime.jsx(CharCountPlugin, { onCountChange: handleCharCount }),
7499
- /* @__PURE__ */ jsxRuntime.jsx(
7500
- RefApiPlugin,
7501
- {
7502
- forwardedRef: ref,
7503
- contentEditableDomRef,
7504
- focusedRef,
7505
- setRefErrors
7506
- }
7507
- )
7508
- ]
7509
- }
7510
- ) }) }) });
8434
+ ]
8435
+ }
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
+ ]
8539
+ }
8540
+ )
8541
+ ] })
8542
+ ]
8543
+ }
8544
+ ) }),
8545
+ /* @__PURE__ */ jsxRuntime.jsx(EditorReadyPlugin, { onReady: props.onReady })
8546
+ ] }) });
7511
8547
  }
7512
8548
  );
7513
8549
 
7514
8550
  exports.ContentEditorComponent = ContentEditorComponent;
7515
8551
  exports.ContentEditorLevel = ContentEditorLevel;
8552
+ exports.DEFAULT_VALIDATION_MESSAGES = DEFAULT_VALIDATION_MESSAGES;
7516
8553
  //# sourceMappingURL=index.js.map
7517
8554
  //# sourceMappingURL=index.js.map