@trafica/editor 1.0.32 → 1.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { forwardRef, useRef, useEffect, useCallback, useMemo, useImperativeHandle, useState, useLayoutEffect, useSyncExternalStore } from 'react';
2
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
1
+ import { useSyncExternalStore, useRef, useCallback, useState, useEffect, useLayoutEffect, useMemo } from 'react';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import { createPortal } from 'react-dom';
4
4
 
5
5
  // src/editor/core/DocumentModel.ts
@@ -1633,38 +1633,47 @@ var htmlSerializer = {
1633
1633
  return parseHTMLBody(parsed.body);
1634
1634
  }
1635
1635
  };
1636
+ function alignAttrs(align, extraStyles = "") {
1637
+ const dataAlign = align ? ` data-align="${align}"` : "";
1638
+ const textAlign = align && align !== "left" ? `text-align:${align};` : "";
1639
+ const combined = textAlign + extraStyles;
1640
+ const style = combined ? ` style="${combined}"` : "";
1641
+ return dataAlign + style;
1642
+ }
1636
1643
  function serializeBlock(node, idCounts = /* @__PURE__ */ new Map(), inCell = false) {
1637
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A;
1644
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B;
1638
1645
  switch (node.type) {
1639
1646
  case "paragraph": {
1640
- const align = ((_a = node.attrs) == null ? void 0 : _a.align) ? ` data-align="${node.attrs.align}"` : "";
1641
- const style = inCell ? ` style="margin:0;min-height:1.2em;"` : "";
1642
- return `<p${align}${style}>${serializeChildren(node.children)}</p>`;
1647
+ const align = (_a = node.attrs) == null ? void 0 : _a.align;
1648
+ const extra = inCell ? "margin:0;min-height:1.2em;" : "";
1649
+ return `<p${alignAttrs(align, extra)}>${serializeChildren(node.children)}</p>`;
1643
1650
  }
1644
1651
  case "heading": {
1645
1652
  const l = (_c = (_b = node.attrs) == null ? void 0 : _b.level) != null ? _c : 1;
1646
- const align = ((_d = node.attrs) == null ? void 0 : _d.align) ? ` data-align="${node.attrs.align}"` : "";
1653
+ const align = (_d = node.attrs) == null ? void 0 : _d.align;
1647
1654
  const rawText = node.children.filter((c) => "text" in c).map((c) => c.text).join("");
1648
1655
  const base = slugify(rawText) || `heading-${l}`;
1649
1656
  const count = (_e = idCounts.get(base)) != null ? _e : 0;
1650
1657
  idCounts.set(base, count + 1);
1651
1658
  const id = count === 0 ? base : `${base}-${count}`;
1652
- return `<h${l} id="${id}"${align}>${serializeChildren(node.children)}</h${l}>`;
1659
+ return `<h${l} id="${id}"${alignAttrs(align)}>${serializeChildren(node.children)}</h${l}>`;
1653
1660
  }
1654
1661
  case "blockquote": {
1655
- const align = ((_f = node.attrs) == null ? void 0 : _f.align) ? ` data-align="${node.attrs.align}"` : "";
1656
- return `<blockquote${align}>${serializeChildren(node.children)}</blockquote>`;
1662
+ const align = (_f = node.attrs) == null ? void 0 : _f.align;
1663
+ return `<blockquote${alignAttrs(align)}>${serializeChildren(node.children)}</blockquote>`;
1657
1664
  }
1658
1665
  case "bullet_list":
1659
1666
  return `<ul>${serializeChildren(node.children)}</ul>`;
1660
1667
  case "ordered_list":
1661
1668
  return `<ol>${serializeChildren(node.children)}</ol>`;
1662
- case "list_item":
1663
- return `<li>${serializeChildren(node.children)}</li>`;
1669
+ case "list_item": {
1670
+ const align = (_g = node.attrs) == null ? void 0 : _g.align;
1671
+ return `<li${alignAttrs(align)}>${serializeChildren(node.children)}</li>`;
1672
+ }
1664
1673
  case "check_list":
1665
1674
  return `<ul class="todo-list" data-type="checklist">${serializeChildren(node.children)}</ul>`;
1666
1675
  case "check_list_item": {
1667
- const checked = !!((_g = node.attrs) == null ? void 0 : _g.checked);
1676
+ const checked = !!((_h = node.attrs) == null ? void 0 : _h.checked);
1668
1677
  const dataChecked = checked ? ' data-checked="true"' : "";
1669
1678
  const itemClass = `todo-list__item${checked ? " todo-list__item_checked" : ""}`;
1670
1679
  const checkedAttr = checked ? ' checked="checked"' : "";
@@ -1672,16 +1681,16 @@ function serializeBlock(node, idCounts = /* @__PURE__ */ new Map(), inCell = fal
1672
1681
  return `<li class="${itemClass}"${dataChecked}><label class="todo-list__label"><input type="checkbox" disabled="disabled"${checkedAttr}><span class="todo-list__label__description">${innerContent}</span></label></li>`;
1673
1682
  }
1674
1683
  case "code_block": {
1675
- const lang = ((_h = node.attrs) == null ? void 0 : _h.language) ? ` class="language-${node.attrs.language}"` : "";
1684
+ const lang = ((_i = node.attrs) == null ? void 0 : _i.language) ? ` class="language-${node.attrs.language}"` : "";
1676
1685
  const raw = node.children.map((c) => isTextNode(c) ? escapeHTML(c.text) : "").join("");
1677
1686
  return `<pre><code${lang}>${raw}</code></pre>`;
1678
1687
  }
1679
1688
  case "image": {
1680
- const src = escapeAttr((_j = (_i = node.attrs) == null ? void 0 : _i.src) != null ? _j : "");
1681
- const alt = escapeAttr((_l = (_k = node.attrs) == null ? void 0 : _k.alt) != null ? _l : "");
1682
- const width = ((_m = node.attrs) == null ? void 0 : _m.width) ? ` width="${node.attrs.width}"` : "";
1683
- const align = ((_n = node.attrs) == null ? void 0 : _n.align) ? ` data-align="${node.attrs.align}"` : "";
1684
- const caption = ((_o = node.attrs) == null ? void 0 : _o.caption) ? escapeHTML(node.attrs.caption) : "";
1689
+ const src = escapeAttr((_k = (_j = node.attrs) == null ? void 0 : _j.src) != null ? _k : "");
1690
+ const alt = escapeAttr((_m = (_l = node.attrs) == null ? void 0 : _l.alt) != null ? _m : "");
1691
+ const width = ((_n = node.attrs) == null ? void 0 : _n.width) ? ` width="${node.attrs.width}"` : "";
1692
+ const align = ((_o = node.attrs) == null ? void 0 : _o.align) ? ` data-align="${node.attrs.align}"` : "";
1693
+ const caption = ((_p = node.attrs) == null ? void 0 : _p.caption) ? escapeHTML(node.attrs.caption) : "";
1685
1694
  if (caption) {
1686
1695
  return `<figure${align}><img src="${src}" alt="${alt}"${width} /><figcaption>${caption}</figcaption></figure>`;
1687
1696
  }
@@ -1691,7 +1700,7 @@ function serializeBlock(node, idCounts = /* @__PURE__ */ new Map(), inCell = fal
1691
1700
  return "<hr />";
1692
1701
  case "table": {
1693
1702
  const rows = node.children.map((c) => serializeBlock(c)).join("");
1694
- const colWidths = (_q = (_p = node.attrs) == null ? void 0 : _p.colWidths) != null ? _q : [];
1703
+ const colWidths = (_r = (_q = node.attrs) == null ? void 0 : _q.colWidths) != null ? _r : [];
1695
1704
  let colgroup = "";
1696
1705
  if (colWidths.length > 0) {
1697
1706
  const total = colWidths.reduce((s, w) => s + w, 0) || colWidths.length * 120;
@@ -1704,15 +1713,15 @@ function serializeBlock(node, idCounts = /* @__PURE__ */ new Map(), inCell = fal
1704
1713
  return `<tr>${cells}</tr>`;
1705
1714
  }
1706
1715
  case "table_cell": {
1707
- if ((_r = node.attrs) == null ? void 0 : _r.covered) return "";
1708
- const cs = ((_s = node.attrs) == null ? void 0 : _s.colspan) > 1 ? ` colspan="${(_t = node.attrs) == null ? void 0 : _t.colspan}"` : "";
1709
- const rs = ((_u = node.attrs) == null ? void 0 : _u.rowspan) > 1 ? ` rowspan="${(_v = node.attrs) == null ? void 0 : _v.rowspan}"` : "";
1716
+ if ((_s = node.attrs) == null ? void 0 : _s.covered) return "";
1717
+ const cs = ((_t = node.attrs) == null ? void 0 : _t.colspan) > 1 ? ` colspan="${(_u = node.attrs) == null ? void 0 : _u.colspan}"` : "";
1718
+ const rs = ((_v = node.attrs) == null ? void 0 : _v.rowspan) > 1 ? ` rowspan="${(_w = node.attrs) == null ? void 0 : _w.rowspan}"` : "";
1710
1719
  return `<td${cs}${rs} style="border:1px solid #d1d5db;padding:0.5em 0.75em;vertical-align:top;min-width:40px;word-break:break-word;">${node.children.map((c) => serializeBlock(c, /* @__PURE__ */ new Map(), true)).join("")}</td>`;
1711
1720
  }
1712
1721
  case "table_header": {
1713
- if ((_w = node.attrs) == null ? void 0 : _w.covered) return "";
1714
- const cs = ((_x = node.attrs) == null ? void 0 : _x.colspan) > 1 ? ` colspan="${(_y = node.attrs) == null ? void 0 : _y.colspan}"` : "";
1715
- const rs = ((_z = node.attrs) == null ? void 0 : _z.rowspan) > 1 ? ` rowspan="${(_A = node.attrs) == null ? void 0 : _A.rowspan}"` : "";
1722
+ if ((_x = node.attrs) == null ? void 0 : _x.covered) return "";
1723
+ const cs = ((_y = node.attrs) == null ? void 0 : _y.colspan) > 1 ? ` colspan="${(_z = node.attrs) == null ? void 0 : _z.colspan}"` : "";
1724
+ const rs = ((_A = node.attrs) == null ? void 0 : _A.rowspan) > 1 ? ` rowspan="${(_B = node.attrs) == null ? void 0 : _B.rowspan}"` : "";
1716
1725
  return `<th${cs}${rs} style="border:1px solid #d1d5db;padding:0.5em 0.75em;vertical-align:top;min-width:40px;word-break:break-word;background:#f9fafb;font-weight:600;text-align:left;">${node.children.map((c) => serializeBlock(c, /* @__PURE__ */ new Map(), true)).join("")}</th>`;
1717
1726
  }
1718
1727
  default:
@@ -7831,7 +7840,7 @@ var jsonSerializer = {
7831
7840
  }
7832
7841
  }
7833
7842
  };
7834
- function EditorCore({
7843
+ function Editor({
7835
7844
  engine,
7836
7845
  placeholder = "Start writing...",
7837
7846
  className = "",
@@ -7839,14 +7848,11 @@ function EditorCore({
7839
7848
  onHTMLChange,
7840
7849
  onJSONChange,
7841
7850
  onOpenLinkPopup,
7842
- onUploadImage: _onUploadImage,
7843
- onFocus: onFocusProp,
7844
- onBlur: onBlurProp,
7845
- hideToolbar = false,
7846
- toolbarConfig
7851
+ onUploadImage: _onUploadImage
7847
7852
  }) {
7848
7853
  const containerRef = useRef(null);
7849
7854
  const isRenderingRef = useRef(false);
7855
+ const skipRestoreSelectionRef = useRef(false);
7850
7856
  const scrollCaretIntoView = useCallback(() => {
7851
7857
  var _a, _b;
7852
7858
  const sel = window.getSelection();
@@ -7959,8 +7965,14 @@ function EditorCore({
7959
7965
  useLayoutEffect(() => {
7960
7966
  const container = containerRef.current;
7961
7967
  if (!container || !state.selection) return;
7968
+ if (skipRestoreSelectionRef.current) {
7969
+ skipRestoreSelectionRef.current = false;
7970
+ return;
7971
+ }
7972
+ isRenderingRef.current = true;
7962
7973
  restoreSelection(container, state.selection);
7963
7974
  scrollCaretIntoView();
7975
+ isRenderingRef.current = false;
7964
7976
  }, [state.selection]);
7965
7977
  useEffect(() => {
7966
7978
  const container = containerRef.current;
@@ -8025,6 +8037,7 @@ function EditorCore({
8025
8037
  if (currentSelection && JSON.stringify(currentSelection.anchor) === JSON.stringify(captured.anchor) && JSON.stringify(currentSelection.focus) === JSON.stringify(captured.focus)) {
8026
8038
  return;
8027
8039
  }
8040
+ skipRestoreSelectionRef.current = true;
8028
8041
  const tr = createTransaction();
8029
8042
  tr.steps.push(tr_setSelection(captured));
8030
8043
  engine.dispatch(tr);
@@ -8051,11 +8064,7 @@ function EditorCore({
8051
8064
  );
8052
8065
  engine.dispatch(tr);
8053
8066
  }
8054
- onFocusProp == null ? void 0 : onFocusProp();
8055
- }, [engine, onFocusProp]);
8056
- const handleBlur = useCallback(() => {
8057
- onBlurProp == null ? void 0 : onBlurProp();
8058
- }, [onBlurProp]);
8067
+ }, [engine]);
8059
8068
  const handleKeyDown = useCallback(
8060
8069
  (e) => {
8061
8070
  var _a, _b, _c;
@@ -8407,6 +8416,7 @@ function EditorCore({
8407
8416
  if (!container) return;
8408
8417
  const selection = captureSelection(container);
8409
8418
  if (!selection) return;
8419
+ skipRestoreSelectionRef.current = true;
8410
8420
  const tr = createTransaction();
8411
8421
  tr.steps.push(
8412
8422
  tr_setSelection(selection)
@@ -8489,7 +8499,7 @@ function EditorCore({
8489
8499
  className: `editor-root relative flex flex-col ${className}`,
8490
8500
  style: editorHeight ? { minHeight: editorHeight } : void 0,
8491
8501
  children: [
8492
- !readOnly && !hideToolbar && /* @__PURE__ */ jsx(
8502
+ !readOnly && /* @__PURE__ */ jsx(
8493
8503
  Toolbar,
8494
8504
  {
8495
8505
  engine,
@@ -8500,8 +8510,7 @@ function EditorCore({
8500
8510
  linkPopupOpen,
8501
8511
  onLinkPopupClose: () => setLinkPopupOpen(false),
8502
8512
  isSourceMode,
8503
- onToggleSource: handleToggleSource,
8504
- toolbarConfig
8513
+ onToggleSource: handleToggleSource
8505
8514
  }
8506
8515
  ),
8507
8516
  /* @__PURE__ */ jsx("div", { className: "relative", children: isSourceMode ? /* @__PURE__ */ jsx(
@@ -8528,7 +8537,7 @@ function EditorCore({
8528
8537
  isVisualEmpty && /* @__PURE__ */ jsx(
8529
8538
  "div",
8530
8539
  {
8531
- className: "\r\n absolute\r\n top-6\r\n left-6\r\n pointer-events-none\r\n select-none\r\n text-base\r\n text-gray-400\r\n \r\n ",
8540
+ className: "\r\n absolute\r\n top-6\r\n left-6\r\n pointer-events-none\r\n select-none\r\n text-base\r\n text-gray-400\r\n dark:text-gray-500\r\n ",
8532
8541
  "aria-hidden": "true",
8533
8542
  children: placeholder
8534
8543
  }
@@ -8546,7 +8555,6 @@ function EditorCore({
8546
8555
  onMouseDown: handleMouseDown,
8547
8556
  onContextMenu: handleContextMenu,
8548
8557
  onFocus: handleFocus,
8549
- onBlur: handleBlur,
8550
8558
  onClick: handleClick,
8551
8559
  onKeyDown: handleKeyDown,
8552
8560
  className: [
@@ -8558,7 +8566,7 @@ function EditorCore({
8558
8566
  "text-base",
8559
8567
  "leading-relaxed",
8560
8568
  "text-gray-900",
8561
- "",
8569
+ "dark:text-gray-100",
8562
8570
  readOnly ? "cursor-default" : "cursor-text"
8563
8571
  ].join(" ")
8564
8572
  }
@@ -8568,7 +8576,7 @@ function EditorCore({
8568
8576
  {
8569
8577
  role: "tooltip",
8570
8578
  style: { left: linkTooltip.x, top: linkTooltip.y },
8571
- className: "absolute z-50 pointer-events-none flex items-center gap-1.5 bg-gray-900 text-white text-xs px-2.5 py-1.5 rounded shadow-lg max-w-xs",
8579
+ className: "absolute z-50 pointer-events-none flex items-center gap-1.5 bg-gray-900 dark:bg-gray-700 text-white text-xs px-2.5 py-1.5 rounded shadow-lg max-w-xs",
8572
8580
  children: [
8573
8581
  /* @__PURE__ */ jsxs("svg", { width: "11", height: "11", viewBox: "0 0 16 16", fill: "none", className: "shrink-0 opacity-70", "aria-hidden": "true", children: [
8574
8582
  /* @__PURE__ */ jsx("path", { d: "M6.5 3.5H4A2.5 2.5 0 0 0 4 8.5h2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
@@ -8683,168 +8691,1020 @@ function prettyPrintHTML(html) {
8683
8691
  }
8684
8692
  return result.join("\n");
8685
8693
  }
8686
-
8687
- // src/editor/table/TablePlugin.ts
8688
- function getCellDOMElement(cellPathJson) {
8689
- return document.querySelector(`[data-block-path='${cellPathJson}']`);
8690
- }
8691
- function isCaretAtContainerStart(container) {
8692
- const sel = window.getSelection();
8693
- if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return false;
8694
- const range = sel.getRangeAt(0);
8695
- if (range.startOffset !== 0) return false;
8696
- let node = range.startContainer;
8697
- while (node && node !== container) {
8698
- if (node.previousSibling) return false;
8699
- node = node.parentNode;
8700
- }
8701
- return node === container;
8702
- }
8703
- function isCaretAtContainerEnd(container) {
8704
- var _a, _b;
8705
- const sel = window.getSelection();
8706
- if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return false;
8707
- const range = sel.getRangeAt(0);
8708
- const endNode = range.endContainer;
8709
- const endOffset = range.endOffset;
8710
- const len = endNode.nodeType === Node.TEXT_NODE ? (_b = (_a = endNode.textContent) == null ? void 0 : _a.length) != null ? _b : 0 : endNode.childNodes.length;
8711
- if (endOffset !== len) return false;
8712
- let node = endNode;
8713
- while (node && node !== container) {
8714
- if (node.nextSibling) return false;
8715
- node = node.parentNode;
8716
- }
8717
- return node === container;
8718
- }
8719
- function isCaretOnEdgeLine(container, direction) {
8720
- const sel = window.getSelection();
8721
- if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return false;
8722
- const caretRange = sel.getRangeAt(0).cloneRange();
8723
- caretRange.collapse(true);
8724
- const caretRects = caretRange.getClientRects();
8725
- if (!caretRects.length) return true;
8726
- const caretRect = caretRects[0];
8727
- const containerRect = container.getBoundingClientRect();
8728
- const lineHeight = parseFloat(getComputedStyle(container).lineHeight) || 20;
8729
- if (direction === "up") {
8730
- return caretRect.top < containerRect.top + lineHeight;
8731
- } else {
8732
- return caretRect.bottom > containerRect.bottom - lineHeight;
8733
- }
8734
- }
8735
- var TablePlugin = {
8736
- name: "table",
8737
- keyBindings: {
8738
- Tab: (engine) => {
8739
- var _a;
8740
- const state = engine.getState();
8741
- const sel = state.selection;
8742
- if (!sel) return false;
8743
- const cellPos = findCellPosition(state.doc, sel.anchor.path);
8744
- if (!cellPos) return false;
8745
- const { tablePath, row, col } = cellPos;
8746
- const table = getNodeAtPath(state.doc, tablePath);
8747
- const { rows, cols } = getTableDimensions(table);
8748
- let nextRow = row;
8749
- let nextCol = col + 1;
8750
- while (nextRow < rows) {
8751
- if (nextCol >= cols) {
8752
- nextCol = 0;
8753
- nextRow++;
8754
- continue;
8755
- }
8756
- const c = getNodeAtPath(state.doc, [...tablePath, nextRow, nextCol]);
8757
- if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
8758
- nextCol++;
8759
- }
8760
- if (nextRow >= rows) {
8761
- const newTable = insertTableRowAfter(table, rows - 1);
8762
- const newChildren = [...state.doc.children];
8763
- newChildren[tablePath[0]] = newTable;
8764
- const tempDoc = { type: "doc", children: newChildren };
8765
- const tr2 = createTransaction();
8766
- tr2.steps.push({ type: "delete_node", path: tablePath });
8767
- tr2.steps.push({
8768
- type: "insert_node",
8769
- parentPath: tablePath.length > 1 ? tablePath.slice(0, -1) : [],
8770
- index: tablePath[tablePath.length - 1],
8771
- node: newTable
8772
- });
8773
- const pos2 = getCellFirstPosition(tempDoc, tablePath, rows, 0);
8774
- if (pos2) tr2.steps.push(tr_setSelection(makeCollapsedSelection(pos2)));
8775
- engine.dispatch(tr2);
8776
- return true;
8694
+ function EditorCore({
8695
+ engine,
8696
+ placeholder = "Start writing...",
8697
+ className = "",
8698
+ readOnly = false,
8699
+ onHTMLChange,
8700
+ onJSONChange,
8701
+ onOpenLinkPopup,
8702
+ onUploadImage: _onUploadImage,
8703
+ onFocus: onFocusProp,
8704
+ onBlur: onBlurProp,
8705
+ hideToolbar = false,
8706
+ toolbarConfig
8707
+ }) {
8708
+ const containerRef = useRef(null);
8709
+ const isRenderingRef = useRef(false);
8710
+ const scrollCaretIntoView = useCallback(() => {
8711
+ var _a, _b;
8712
+ const sel = window.getSelection();
8713
+ if (!sel || sel.rangeCount === 0) return;
8714
+ const range = sel.getRangeAt(0).cloneRange();
8715
+ range.collapse(true);
8716
+ const caretRect = range.getBoundingClientRect();
8717
+ let rect = caretRect;
8718
+ if (!rect || rect.top === 0 && rect.bottom === 0 && rect.left === 0) {
8719
+ const node = range.startContainer;
8720
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
8721
+ if (el) rect = el.getBoundingClientRect();
8722
+ }
8723
+ if (!rect || rect.bottom === 0) return;
8724
+ let scrollEl = (_b = (_a = containerRef.current) == null ? void 0 : _a.parentElement) != null ? _b : null;
8725
+ while (scrollEl) {
8726
+ const style = window.getComputedStyle(scrollEl);
8727
+ const overflow = style.overflow + style.overflowY;
8728
+ if (/auto|scroll/.test(overflow)) break;
8729
+ scrollEl = scrollEl.parentElement;
8730
+ }
8731
+ const PADDING = 24;
8732
+ if (scrollEl) {
8733
+ const containerRect = scrollEl.getBoundingClientRect();
8734
+ if (rect.bottom > containerRect.bottom - PADDING) {
8735
+ scrollEl.scrollBy({ top: rect.bottom - containerRect.bottom + PADDING, behavior: "smooth" });
8736
+ } else if (rect.top < containerRect.top + PADDING) {
8737
+ scrollEl.scrollBy({ top: rect.top - containerRect.top - PADDING, behavior: "smooth" });
8777
8738
  }
8778
- const pos = getCellFirstPosition(state.doc, tablePath, nextRow, nextCol);
8779
- if (!pos) return false;
8780
- const tr = createTransaction();
8781
- tr.steps.push(tr_setSelection(makeCollapsedSelection(pos)));
8782
- engine.dispatch(tr);
8783
- return true;
8784
- },
8785
- "ArrowRight": (engine) => {
8786
- var _a;
8787
- const state = engine.getState();
8788
- const sel = state.selection;
8789
- if (!sel) return false;
8790
- const cellPos = findCellPosition(state.doc, sel.anchor.path);
8791
- if (!cellPos) return false;
8792
- const { tablePath, row, col } = cellPos;
8793
- const cellPath = [...tablePath, row, col];
8794
- const cellEl = getCellDOMElement(JSON.stringify(cellPath));
8795
- if (!cellEl || !isCaretAtContainerEnd(cellEl)) return false;
8796
- const table = getNodeAtPath(state.doc, tablePath);
8797
- const { rows, cols } = getTableDimensions(table);
8798
- let nextRow = row;
8799
- let nextCol = col + 1;
8800
- while (nextRow < rows) {
8801
- if (nextCol >= cols) {
8802
- nextCol = 0;
8803
- nextRow++;
8804
- continue;
8805
- }
8806
- const c = getNodeAtPath(state.doc, [...tablePath, nextRow, nextCol]);
8807
- if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
8808
- nextCol++;
8739
+ } else {
8740
+ if (rect.bottom > window.innerHeight - PADDING) {
8741
+ window.scrollBy({ top: rect.bottom - window.innerHeight + PADDING, behavior: "smooth" });
8742
+ } else if (rect.top < PADDING) {
8743
+ window.scrollBy({ top: rect.top - PADDING, behavior: "smooth" });
8809
8744
  }
8810
- if (nextRow >= rows) return true;
8811
- const pos = getCellFirstPosition(state.doc, tablePath, nextRow, nextCol);
8812
- if (!pos) return false;
8745
+ }
8746
+ }, []);
8747
+ const isComposingRef = useRef(false);
8748
+ const stateRef = useRef(engine.getState());
8749
+ const [selectedImagePath, setSelectedImagePath] = useState(null);
8750
+ const [findReplaceOpen, setFindReplaceOpen] = useState(false);
8751
+ const [findReplaceMode, setFindReplaceMode] = useState("find");
8752
+ const [linkPopupOpen, setLinkPopupOpen] = useState(false);
8753
+ const [isSourceMode, setIsSourceMode] = useState(false);
8754
+ const [sourceHTML, setSourceHTML] = useState("");
8755
+ const [editorHeight, setEditorHeight] = useState(null);
8756
+ const editorRootRef = useRef(null);
8757
+ const resizeDragRef = useRef(null);
8758
+ const [linkTooltip, setLinkTooltip] = useState(null);
8759
+ const linkTooltipTimerRef = useRef(null);
8760
+ const handleToggleSource = useCallback(() => {
8761
+ if (!isSourceMode) {
8762
+ const html = htmlSerializer.serialize(engine.getState().doc);
8763
+ setSourceHTML(prettyPrintHTML2(html));
8764
+ setIsSourceMode(true);
8765
+ } else {
8766
+ const newDoc = htmlSerializer.deserialize(sourceHTML);
8813
8767
  const tr = createTransaction();
8814
- tr.steps.push(tr_setSelection(makeCollapsedSelection(pos)));
8768
+ tr.steps.push(tr_replaceDoc(newDoc));
8815
8769
  engine.dispatch(tr);
8816
- return true;
8817
- },
8818
- "ArrowLeft": (engine) => {
8819
- var _a;
8820
- const state = engine.getState();
8821
- const sel = state.selection;
8822
- if (!sel) return false;
8823
- const cellPos = findCellPosition(state.doc, sel.anchor.path);
8824
- if (!cellPos) return false;
8825
- const { tablePath, row, col } = cellPos;
8826
- const cellPath = [...tablePath, row, col];
8827
- const cellEl = getCellDOMElement(JSON.stringify(cellPath));
8828
- if (!cellEl || !isCaretAtContainerStart(cellEl)) return false;
8829
- const table = getNodeAtPath(state.doc, tablePath);
8830
- const { cols } = getTableDimensions(table);
8831
- let prevRow = row;
8832
- let prevCol = col - 1;
8833
- while (prevRow >= 0) {
8834
- if (prevCol < 0) {
8835
- prevCol = cols - 1;
8836
- prevRow--;
8837
- continue;
8838
- }
8839
- const c = getNodeAtPath(state.doc, [...tablePath, prevRow, prevCol]);
8840
- if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
8841
- prevCol--;
8842
- }
8843
- if (prevRow < 0) return true;
8844
- const pos = getCellLastPosition(state.doc, tablePath, prevRow, prevCol);
8845
- if (!pos) return false;
8846
- const tr = createTransaction();
8847
- tr.steps.push(tr_setSelection(makeCollapsedSelection(pos)));
8770
+ setIsSourceMode(false);
8771
+ }
8772
+ }, [isSourceMode, sourceHTML, engine]);
8773
+ useEffect(() => {
8774
+ const onMouseMove = (e) => {
8775
+ const drag = resizeDragRef.current;
8776
+ if (!drag) return;
8777
+ const newHeight = Math.max(120, drag.startHeight + (e.clientY - drag.startY));
8778
+ setEditorHeight(newHeight);
8779
+ };
8780
+ const onMouseUp = () => {
8781
+ resizeDragRef.current = null;
8782
+ };
8783
+ document.addEventListener("mousemove", onMouseMove);
8784
+ document.addEventListener("mouseup", onMouseUp);
8785
+ return () => {
8786
+ document.removeEventListener("mousemove", onMouseMove);
8787
+ document.removeEventListener("mouseup", onMouseUp);
8788
+ };
8789
+ }, []);
8790
+ const [tableContextMenu, setTableContextMenu] = useState(null);
8791
+ const [tableSelection, setTableSelection] = useState(null);
8792
+ const cellDragRef = useRef(null);
8793
+ const colResizeRef = useRef(null);
8794
+ const state = useEditorState(engine);
8795
+ const inTableCellPos = state.selection ? findCellPosition(state.doc, state.selection.anchor.path) : null;
8796
+ useLayoutEffect(() => {
8797
+ stateRef.current = state;
8798
+ }, [state]);
8799
+ useLayoutEffect(() => {
8800
+ const container = containerRef.current;
8801
+ if (!container) return;
8802
+ isRenderingRef.current = true;
8803
+ renderDocument(state.doc, container);
8804
+ if (!container.contains(document.activeElement)) {
8805
+ container.focus({ preventScroll: true });
8806
+ }
8807
+ if (state.selection) {
8808
+ restoreSelection(container, state.selection);
8809
+ scrollCaretIntoView();
8810
+ }
8811
+ isRenderingRef.current = false;
8812
+ if (onHTMLChange) {
8813
+ onHTMLChange(htmlSerializer.serialize(state.doc));
8814
+ }
8815
+ if (onJSONChange) {
8816
+ onJSONChange(jsonSerializer.serialize(state.doc));
8817
+ }
8818
+ }, [state.doc]);
8819
+ useLayoutEffect(() => {
8820
+ const container = containerRef.current;
8821
+ if (!container || !state.selection) return;
8822
+ restoreSelection(container, state.selection);
8823
+ scrollCaretIntoView();
8824
+ }, [state.selection]);
8825
+ useEffect(() => {
8826
+ const container = containerRef.current;
8827
+ if (!container || readOnly) return;
8828
+ return attachClipboardHandlers(container, engine);
8829
+ }, [engine, readOnly]);
8830
+ useEffect(() => {
8831
+ const container = containerRef.current;
8832
+ if (!container) return;
8833
+ container.querySelectorAll(".editor-cell-selected").forEach((el) => {
8834
+ el.classList.remove("editor-cell-selected");
8835
+ });
8836
+ if (!tableSelection) return;
8837
+ const { tablePath, anchorCell, focusCell } = tableSelection;
8838
+ const minRow = Math.min(anchorCell[0], focusCell[0]);
8839
+ const maxRow = Math.max(anchorCell[0], focusCell[0]);
8840
+ const minCol = Math.min(anchorCell[1], focusCell[1]);
8841
+ const maxCol = Math.max(anchorCell[1], focusCell[1]);
8842
+ for (let r = minRow; r <= maxRow; r++) {
8843
+ for (let c = minCol; c <= maxCol; c++) {
8844
+ const cellPath = [...tablePath, r, c];
8845
+ const td = container.querySelector(
8846
+ `[data-block-path="${JSON.stringify(cellPath)}"]`
8847
+ );
8848
+ if (td) td.classList.add("editor-cell-selected");
8849
+ }
8850
+ }
8851
+ }, [tableSelection, state.doc]);
8852
+ useEffect(() => {
8853
+ const container = containerRef.current;
8854
+ if (!container) return;
8855
+ container.querySelectorAll(".editor-cell-active").forEach((el) => {
8856
+ el.classList.remove("editor-cell-active");
8857
+ });
8858
+ if (!inTableCellPos) {
8859
+ setTableSelection(null);
8860
+ return;
8861
+ }
8862
+ if (tableSelection) {
8863
+ const { anchorCell, focusCell, tablePath } = tableSelection;
8864
+ const sameTable = JSON.stringify(tablePath) === JSON.stringify(inTableCellPos.tablePath);
8865
+ const singleCell = anchorCell[0] === focusCell[0] && anchorCell[1] === focusCell[1];
8866
+ if (!sameTable || singleCell) {
8867
+ setTableSelection(null);
8868
+ }
8869
+ }
8870
+ const cellPath = [...inTableCellPos.tablePath, inTableCellPos.row, inTableCellPos.col];
8871
+ const td = container.querySelector(
8872
+ `[data-block-path="${JSON.stringify(cellPath)}"]`
8873
+ );
8874
+ if (td) td.classList.add("editor-cell-active");
8875
+ }, [inTableCellPos, state.doc]);
8876
+ useEffect(() => {
8877
+ const onSelectionChange = () => {
8878
+ if (isRenderingRef.current) return;
8879
+ const container = containerRef.current;
8880
+ if (!container) return;
8881
+ if (!isSelectionInContainer(container)) return;
8882
+ const captured = captureSelection(container);
8883
+ if (!captured) return;
8884
+ const currentSelection = engine.getState().selection;
8885
+ if (currentSelection && JSON.stringify(currentSelection.anchor) === JSON.stringify(captured.anchor) && JSON.stringify(currentSelection.focus) === JSON.stringify(captured.focus)) {
8886
+ return;
8887
+ }
8888
+ const tr = createTransaction();
8889
+ tr.steps.push(tr_setSelection(captured));
8890
+ engine.dispatch(tr);
8891
+ };
8892
+ document.addEventListener(
8893
+ "selectionchange",
8894
+ onSelectionChange
8895
+ );
8896
+ return () => {
8897
+ document.removeEventListener(
8898
+ "selectionchange",
8899
+ onSelectionChange
8900
+ );
8901
+ };
8902
+ }, [engine]);
8903
+ const handleFocus = useCallback(() => {
8904
+ const currentState = engine.getState();
8905
+ if (!currentState.selection) {
8906
+ const tr = createTransaction();
8907
+ tr.steps.push(
8908
+ tr_setSelection(
8909
+ makeCollapsedSelection({ path: [0], offset: 0 })
8910
+ )
8911
+ );
8912
+ engine.dispatch(tr);
8913
+ }
8914
+ onFocusProp == null ? void 0 : onFocusProp();
8915
+ }, [engine, onFocusProp]);
8916
+ const handleBlur = useCallback(() => {
8917
+ onBlurProp == null ? void 0 : onBlurProp();
8918
+ }, [onBlurProp]);
8919
+ const handleKeyDown = useCallback(
8920
+ (e) => {
8921
+ var _a, _b, _c;
8922
+ if (readOnly) return;
8923
+ if ((e.ctrlKey || e.metaKey) && e.key === "f") {
8924
+ e.preventDefault();
8925
+ setFindReplaceMode("find");
8926
+ setFindReplaceOpen(true);
8927
+ return;
8928
+ }
8929
+ if ((e.ctrlKey || e.metaKey) && e.key === "h") {
8930
+ e.preventDefault();
8931
+ setFindReplaceMode("replace");
8932
+ setFindReplaceOpen(true);
8933
+ return;
8934
+ }
8935
+ if ((e.ctrlKey || e.metaKey) && e.key === "k") {
8936
+ e.preventDefault();
8937
+ setLinkPopupOpen(true);
8938
+ onOpenLinkPopup == null ? void 0 : onOpenLinkPopup();
8939
+ return;
8940
+ }
8941
+ if ((e.ctrlKey || e.metaKey) && (e.key === "a" || e.key === "A")) {
8942
+ e.preventDefault();
8943
+ const container = containerRef.current;
8944
+ if (container) {
8945
+ const spans = container.querySelectorAll("[data-path]");
8946
+ const first = spans[0];
8947
+ const last = spans[spans.length - 1];
8948
+ if (first && last) {
8949
+ const sel = window.getSelection();
8950
+ const range = document.createRange();
8951
+ range.setStart((_a = leafTextNode2(first, "first")) != null ? _a : first, 0);
8952
+ const lastLeaf = leafTextNode2(last, "last");
8953
+ range.setEnd(lastLeaf != null ? lastLeaf : last, (_c = (_b = lastLeaf == null ? void 0 : lastLeaf.textContent) == null ? void 0 : _b.length) != null ? _c : 0);
8954
+ sel == null ? void 0 : sel.removeAllRanges();
8955
+ sel == null ? void 0 : sel.addRange(range);
8956
+ }
8957
+ }
8958
+ return;
8959
+ }
8960
+ if (selectedImagePath && (e.key === "Delete" || e.key === "Backspace")) {
8961
+ e.preventDefault();
8962
+ deleteImageAtPath(selectedImagePath)(engine);
8963
+ setSelectedImagePath(null);
8964
+ return;
8965
+ }
8966
+ if (selectedImagePath) setSelectedImagePath(null);
8967
+ if (e.key === "Tab") {
8968
+ const { doc, selection: sel } = engine.getState();
8969
+ if (sel) {
8970
+ const bp = findContentBlockPath(doc, sel.anchor.path);
8971
+ const block = bp ? getNodeAtPath(doc, bp) : null;
8972
+ if ((block == null ? void 0 : block.type) === "code_block") {
8973
+ e.preventDefault();
8974
+ insertText(" ")(engine);
8975
+ return;
8976
+ }
8977
+ if ((block == null ? void 0 : block.type) === "list_item") {
8978
+ e.preventDefault();
8979
+ if (e.shiftKey) {
8980
+ outdentListItem(engine);
8981
+ } else {
8982
+ indentListItem(engine);
8983
+ }
8984
+ return;
8985
+ }
8986
+ if (findCellPosition(doc, sel.anchor.path)) {
8987
+ e.preventDefault();
8988
+ }
8989
+ }
8990
+ }
8991
+ const handled = engine.handleKeyDown(
8992
+ e.nativeEvent
8993
+ );
8994
+ if (handled) return;
8995
+ if (e.key === "Enter" && !e.shiftKey) {
8996
+ e.preventDefault();
8997
+ handleEnter(engine);
8998
+ return;
8999
+ }
9000
+ if (e.key === "Backspace") {
9001
+ e.preventDefault();
9002
+ handleBackspace(engine);
9003
+ return;
9004
+ }
9005
+ if (e.key === "Delete") {
9006
+ e.preventDefault();
9007
+ handleDelete(engine);
9008
+ return;
9009
+ }
9010
+ if (e.key === "Enter" && e.shiftKey) {
9011
+ e.preventDefault();
9012
+ insertText("\n")(engine);
9013
+ return;
9014
+ }
9015
+ },
9016
+ [engine, readOnly, onOpenLinkPopup, selectedImagePath]
9017
+ );
9018
+ useEffect(() => {
9019
+ const container = containerRef.current;
9020
+ if (!container || readOnly) return;
9021
+ const onBeforeInput = (e) => {
9022
+ if (e.isComposing || isComposingRef.current) return;
9023
+ switch (e.inputType) {
9024
+ case "insertText":
9025
+ case "insertReplacementText": {
9026
+ if (!e.data) return;
9027
+ e.preventDefault();
9028
+ insertText(e.data)(engine);
9029
+ return;
9030
+ }
9031
+ case "insertParagraph": {
9032
+ e.preventDefault();
9033
+ handleEnter(engine);
9034
+ return;
9035
+ }
9036
+ case "insertLineBreak": {
9037
+ e.preventDefault();
9038
+ insertText("\n")(engine);
9039
+ return;
9040
+ }
9041
+ case "deleteContentBackward":
9042
+ case "deleteWordBackward":
9043
+ case "deleteSoftLineBackward": {
9044
+ e.preventDefault();
9045
+ handleBackspace(engine);
9046
+ return;
9047
+ }
9048
+ case "insertFromPaste":
9049
+ case "insertFromDrop":
9050
+ return;
9051
+ default:
9052
+ e.preventDefault();
9053
+ }
9054
+ };
9055
+ container.addEventListener("beforeinput", onBeforeInput);
9056
+ return () => container.removeEventListener("beforeinput", onBeforeInput);
9057
+ }, [engine, readOnly]);
9058
+ useEffect(() => {
9059
+ const container = containerRef.current;
9060
+ if (!container || readOnly) return;
9061
+ const onCompositionStart = () => {
9062
+ isComposingRef.current = true;
9063
+ };
9064
+ const onCompositionEnd = (e) => {
9065
+ isComposingRef.current = false;
9066
+ if (e.data) insertText(e.data)(engine);
9067
+ };
9068
+ container.addEventListener("compositionstart", onCompositionStart);
9069
+ container.addEventListener("compositionend", onCompositionEnd);
9070
+ return () => {
9071
+ container.removeEventListener("compositionstart", onCompositionStart);
9072
+ container.removeEventListener("compositionend", onCompositionEnd);
9073
+ };
9074
+ }, [engine, readOnly]);
9075
+ useEffect(() => {
9076
+ const container = containerRef.current;
9077
+ if (!container) return;
9078
+ const onMouseOver = (e) => {
9079
+ var _a;
9080
+ const anchor = e.target.closest("a.editor-link");
9081
+ if (!anchor) return;
9082
+ const href = (_a = anchor.getAttribute("href")) != null ? _a : "";
9083
+ if (!href) return;
9084
+ if (linkTooltipTimerRef.current) clearTimeout(linkTooltipTimerRef.current);
9085
+ linkTooltipTimerRef.current = setTimeout(() => {
9086
+ const rect = anchor.getBoundingClientRect();
9087
+ const containerRect = container.getBoundingClientRect();
9088
+ setLinkTooltip({
9089
+ href,
9090
+ x: rect.left - containerRect.left,
9091
+ y: rect.bottom - containerRect.top + 6
9092
+ });
9093
+ }, 200);
9094
+ };
9095
+ const onMouseOut = (e) => {
9096
+ const anchor = e.target.closest("a.editor-link");
9097
+ if (!anchor) return;
9098
+ if (linkTooltipTimerRef.current) clearTimeout(linkTooltipTimerRef.current);
9099
+ setLinkTooltip(null);
9100
+ };
9101
+ const onClick = (e) => {
9102
+ if (!(e.ctrlKey || e.metaKey)) return;
9103
+ const anchor = e.target.closest("a.editor-link");
9104
+ if (!anchor) return;
9105
+ e.preventDefault();
9106
+ const href = anchor.getAttribute("href");
9107
+ if (href) window.open(href, "_blank", "noopener,noreferrer");
9108
+ };
9109
+ container.addEventListener("mouseover", onMouseOver);
9110
+ container.addEventListener("mouseout", onMouseOut);
9111
+ container.addEventListener("click", onClick);
9112
+ return () => {
9113
+ container.removeEventListener("mouseover", onMouseOver);
9114
+ container.removeEventListener("mouseout", onMouseOut);
9115
+ container.removeEventListener("click", onClick);
9116
+ if (linkTooltipTimerRef.current) clearTimeout(linkTooltipTimerRef.current);
9117
+ };
9118
+ }, []);
9119
+ useEffect(() => {
9120
+ const onMouseMove = (e) => {
9121
+ const drag = colResizeRef.current;
9122
+ if (!drag) return;
9123
+ const delta = e.clientX - drag.startX;
9124
+ const newWidth = Math.max(40, drag.startWidth + delta);
9125
+ const container = containerRef.current;
9126
+ if (container) {
9127
+ const colEl = container.querySelector(
9128
+ `col[data-col-index="${drag.colIndex}"]`
9129
+ );
9130
+ if (colEl) colEl.style.width = `${newWidth}px`;
9131
+ }
9132
+ };
9133
+ const onMouseUp = (e) => {
9134
+ const drag = colResizeRef.current;
9135
+ if (!drag) return;
9136
+ const delta = e.clientX - drag.startX;
9137
+ const newWidth = Math.max(40, drag.startWidth + delta);
9138
+ colResizeRef.current = null;
9139
+ setColumnWidth(drag.tablePath, drag.colIndex, newWidth)(engine);
9140
+ };
9141
+ document.addEventListener("mousemove", onMouseMove);
9142
+ document.addEventListener("mouseup", onMouseUp);
9143
+ return () => {
9144
+ document.removeEventListener("mousemove", onMouseMove);
9145
+ document.removeEventListener("mouseup", onMouseUp);
9146
+ };
9147
+ }, [engine]);
9148
+ useEffect(() => {
9149
+ const onMouseMove = (e) => {
9150
+ var _a, _b;
9151
+ const drag = cellDragRef.current;
9152
+ if (!drag) return;
9153
+ const td = e.target.closest("[data-cell-row]");
9154
+ if (!td) return;
9155
+ const row = parseInt((_a = td.dataset.cellRow) != null ? _a : "0");
9156
+ const col = parseInt((_b = td.dataset.cellCol) != null ? _b : "0");
9157
+ const tdTablePath = td.dataset.cellTablePath ? JSON.parse(td.dataset.cellTablePath) : null;
9158
+ if (!tdTablePath || JSON.stringify(tdTablePath) !== JSON.stringify(drag.tablePath)) return;
9159
+ setTableSelection({
9160
+ tablePath: drag.tablePath,
9161
+ anchorCell: [drag.startRow, drag.startCol],
9162
+ focusCell: [row, col]
9163
+ });
9164
+ };
9165
+ const onMouseUp = () => {
9166
+ cellDragRef.current = null;
9167
+ };
9168
+ document.addEventListener("mousemove", onMouseMove);
9169
+ document.addEventListener("mouseup", onMouseUp);
9170
+ return () => {
9171
+ document.removeEventListener("mousemove", onMouseMove);
9172
+ document.removeEventListener("mouseup", onMouseUp);
9173
+ };
9174
+ }, []);
9175
+ const handleContextMenu = useCallback(
9176
+ (e) => {
9177
+ var _a, _b, _c, _d, _e, _f;
9178
+ const td = e.target.closest("[data-cell-row]");
9179
+ if (!td) return;
9180
+ e.preventDefault();
9181
+ const row = parseInt((_a = td.dataset.cellRow) != null ? _a : "0");
9182
+ const col = parseInt((_b = td.dataset.cellCol) != null ? _b : "0");
9183
+ const tablePath = td.dataset.cellTablePath ? JSON.parse(td.dataset.cellTablePath) : null;
9184
+ if (!tablePath) return;
9185
+ const cellNode = getNodeAtPath(
9186
+ engine.getState().doc,
9187
+ [...tablePath, row, col]
9188
+ );
9189
+ const isMerged = !!cellNode && (((_d = (_c = cellNode.attrs) == null ? void 0 : _c.colspan) != null ? _d : 1) > 1 || ((_f = (_e = cellNode.attrs) == null ? void 0 : _e.rowspan) != null ? _f : 1) > 1);
9190
+ setTableContextMenu({ x: e.clientX, y: e.clientY, tablePath, row, col, isMerged });
9191
+ },
9192
+ [engine]
9193
+ );
9194
+ const handleMouseDown = useCallback(
9195
+ (e) => {
9196
+ var _a, _b, _c, _d;
9197
+ if (readOnly) return;
9198
+ const target = e.target;
9199
+ if (target.dataset.resizeImagePath) {
9200
+ e.preventDefault();
9201
+ const imagePath = JSON.parse(target.dataset.resizeImagePath);
9202
+ const corner = (_a = target.dataset.resizeImagePos) != null ? _a : "se";
9203
+ const container = containerRef.current;
9204
+ let startWidth = 300;
9205
+ if (container) {
9206
+ const fig2 = container.querySelector(`[data-image-path="${JSON.stringify(imagePath)}"]`);
9207
+ const img = fig2 == null ? void 0 : fig2.querySelector("img");
9208
+ if (img) startWidth = img.getBoundingClientRect().width || 300;
9209
+ }
9210
+ imageResizeRef.current = {
9211
+ imagePath,
9212
+ corner,
9213
+ startX: e.clientX,
9214
+ startY: e.clientY,
9215
+ startWidth,
9216
+ startHeight: 0
9217
+ };
9218
+ return;
9219
+ }
9220
+ const fig = target.closest("[data-image-path]");
9221
+ if (fig == null ? void 0 : fig.dataset.imagePath) {
9222
+ e.preventDefault();
9223
+ setSelectedImagePath(JSON.parse(fig.dataset.imagePath));
9224
+ return;
9225
+ }
9226
+ setSelectedImagePath(null);
9227
+ if (target.dataset.resizeTable) {
9228
+ e.preventDefault();
9229
+ const tablePath = JSON.parse(target.dataset.resizeTable);
9230
+ const colIndex = parseInt((_b = target.dataset.resizeCol) != null ? _b : "0");
9231
+ const container = containerRef.current;
9232
+ let startWidth = 120;
9233
+ if (container) {
9234
+ const colEl = container.querySelector(
9235
+ `col[data-col-index="${colIndex}"]`
9236
+ );
9237
+ if (colEl) startWidth = colEl.getBoundingClientRect().width || 120;
9238
+ }
9239
+ colResizeRef.current = { tablePath, colIndex, startX: e.clientX, startWidth };
9240
+ return;
9241
+ }
9242
+ const td = target.closest("[data-cell-row]");
9243
+ if (!td) setTableSelection(null);
9244
+ if (td && !readOnly) {
9245
+ const row = parseInt((_c = td.dataset.cellRow) != null ? _c : "0");
9246
+ const col = parseInt((_d = td.dataset.cellCol) != null ? _d : "0");
9247
+ const tdTablePath = td.dataset.cellTablePath ? JSON.parse(td.dataset.cellTablePath) : null;
9248
+ if (tdTablePath) {
9249
+ cellDragRef.current = { tablePath: tdTablePath, startRow: row, startCol: col };
9250
+ setTableSelection({
9251
+ tablePath: tdTablePath,
9252
+ anchorCell: [row, col],
9253
+ focusCell: [row, col]
9254
+ });
9255
+ }
9256
+ }
9257
+ const checkTarget = target.dataset.checkPath ? target : target.closest("[data-check-path]");
9258
+ if (checkTarget == null ? void 0 : checkTarget.dataset.checkPath) {
9259
+ e.preventDefault();
9260
+ toggleCheckItemAt(JSON.parse(checkTarget.dataset.checkPath))(engine);
9261
+ }
9262
+ },
9263
+ [engine, readOnly]
9264
+ );
9265
+ const handleClick = useCallback(() => {
9266
+ const container = containerRef.current;
9267
+ if (!container) return;
9268
+ const selection = captureSelection(container);
9269
+ if (!selection) return;
9270
+ const tr = createTransaction();
9271
+ tr.steps.push(
9272
+ tr_setSelection(selection)
9273
+ );
9274
+ engine.dispatch(tr);
9275
+ }, [engine]);
9276
+ const imageResizeRef = useRef(null);
9277
+ useEffect(() => {
9278
+ const onMouseMove = (e) => {
9279
+ const drag = imageResizeRef.current;
9280
+ if (!drag) return;
9281
+ const deltaX = e.clientX - drag.startX;
9282
+ const isRight = drag.corner === "ne" || drag.corner === "se";
9283
+ const newWidth = Math.max(60, drag.startWidth + (isRight ? deltaX : -deltaX));
9284
+ const container = containerRef.current;
9285
+ if (container) {
9286
+ const fig = container.querySelector(
9287
+ `[data-image-path="${JSON.stringify(drag.imagePath)}"]`
9288
+ );
9289
+ const img = fig == null ? void 0 : fig.querySelector("img");
9290
+ if (img) img.style.width = `${newWidth}px`;
9291
+ }
9292
+ };
9293
+ const onMouseUp = (e) => {
9294
+ const drag = imageResizeRef.current;
9295
+ if (!drag) return;
9296
+ const deltaX = e.clientX - drag.startX;
9297
+ const isRight = drag.corner === "ne" || drag.corner === "se";
9298
+ const newWidth = Math.max(60, drag.startWidth + (isRight ? deltaX : -deltaX));
9299
+ imageResizeRef.current = null;
9300
+ setImageAttr(drag.imagePath, { width: newWidth })(engine);
9301
+ };
9302
+ document.addEventListener("mousemove", onMouseMove);
9303
+ document.addEventListener("mouseup", onMouseUp);
9304
+ return () => {
9305
+ document.removeEventListener("mousemove", onMouseMove);
9306
+ document.removeEventListener("mouseup", onMouseUp);
9307
+ };
9308
+ }, [engine]);
9309
+ useEffect(() => {
9310
+ const container = containerRef.current;
9311
+ if (!container) return;
9312
+ container.querySelectorAll("[data-image-path]").forEach((el) => {
9313
+ el.removeAttribute("data-selected");
9314
+ });
9315
+ if (selectedImagePath) {
9316
+ const fig = container.querySelector(
9317
+ `[data-image-path="${JSON.stringify(selectedImagePath)}"]`
9318
+ );
9319
+ if (fig) fig.setAttribute("data-selected", "true");
9320
+ }
9321
+ }, [selectedImagePath, state.doc]);
9322
+ useEffect(() => {
9323
+ const container = containerRef.current;
9324
+ if (!container || readOnly) return;
9325
+ const onDragOver = (e) => {
9326
+ var _a;
9327
+ if ((_a = e.dataTransfer) == null ? void 0 : _a.types.includes("Files")) e.preventDefault();
9328
+ };
9329
+ const onDrop = (e) => {
9330
+ var _a;
9331
+ e.preventDefault();
9332
+ const file = (_a = e.dataTransfer) == null ? void 0 : _a.files[0];
9333
+ if (!file || !file.type.startsWith("image/")) return;
9334
+ const blobUrl = URL.createObjectURL(file);
9335
+ insertImage(blobUrl, file.name.replace(/\.[^.]+$/, ""))(engine);
9336
+ };
9337
+ container.addEventListener("dragover", onDragOver);
9338
+ container.addEventListener("drop", onDrop);
9339
+ return () => {
9340
+ container.removeEventListener("dragover", onDragOver);
9341
+ container.removeEventListener("drop", onDrop);
9342
+ };
9343
+ }, [engine, readOnly]);
9344
+ const isVisualEmpty = state.doc.children.length === 1 && state.doc.children[0].type === "paragraph" && state.doc.children[0].children.length === 0;
9345
+ return /* @__PURE__ */ jsxs(
9346
+ "div",
9347
+ {
9348
+ ref: editorRootRef,
9349
+ className: `editor-root relative flex flex-col ${className}`,
9350
+ style: editorHeight ? { minHeight: editorHeight } : void 0,
9351
+ children: [
9352
+ !readOnly && !hideToolbar && /* @__PURE__ */ jsx(
9353
+ Toolbar,
9354
+ {
9355
+ engine,
9356
+ onFindReplace: (mode) => {
9357
+ setFindReplaceMode(mode);
9358
+ setFindReplaceOpen(true);
9359
+ },
9360
+ linkPopupOpen,
9361
+ onLinkPopupClose: () => setLinkPopupOpen(false),
9362
+ isSourceMode,
9363
+ onToggleSource: handleToggleSource,
9364
+ toolbarConfig
9365
+ }
9366
+ ),
9367
+ /* @__PURE__ */ jsx("div", { className: "relative", children: isSourceMode ? /* @__PURE__ */ jsx(
9368
+ "textarea",
9369
+ {
9370
+ "aria-label": "HTML source editor",
9371
+ value: sourceHTML,
9372
+ onChange: (e) => {
9373
+ setSourceHTML(e.target.value);
9374
+ const t = e.target;
9375
+ t.style.height = "auto";
9376
+ t.style.height = t.scrollHeight + "px";
9377
+ },
9378
+ ref: (el) => {
9379
+ if (el) {
9380
+ el.style.height = "auto";
9381
+ el.style.height = el.scrollHeight + "px";
9382
+ }
9383
+ },
9384
+ spellCheck: false,
9385
+ className: "w-full block px-6 py-4 font-mono text-sm leading-relaxed bg-gray-950 text-green-300 outline-none resize-none border-0 overflow-hidden"
9386
+ }
9387
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
9388
+ isVisualEmpty && /* @__PURE__ */ jsx(
9389
+ "div",
9390
+ {
9391
+ className: "\r\n absolute\r\n top-6\r\n left-6\r\n pointer-events-none\r\n select-none\r\n text-base\r\n text-gray-400\r\n \r\n ",
9392
+ "aria-hidden": "true",
9393
+ children: placeholder
9394
+ }
9395
+ ),
9396
+ /* @__PURE__ */ jsx(
9397
+ "div",
9398
+ {
9399
+ ref: containerRef,
9400
+ contentEditable: !readOnly,
9401
+ suppressContentEditableWarning: true,
9402
+ role: "textbox",
9403
+ "aria-multiline": "true",
9404
+ "aria-label": "Rich text editor",
9405
+ spellCheck: true,
9406
+ onMouseDown: handleMouseDown,
9407
+ onContextMenu: handleContextMenu,
9408
+ onFocus: handleFocus,
9409
+ onBlur: handleBlur,
9410
+ onClick: handleClick,
9411
+ onKeyDown: handleKeyDown,
9412
+ className: [
9413
+ "editor-canvas",
9414
+ "min-h-[300px]",
9415
+ "px-6",
9416
+ "py-4",
9417
+ "outline-none",
9418
+ "text-base",
9419
+ "leading-relaxed",
9420
+ "text-gray-900",
9421
+ "",
9422
+ readOnly ? "cursor-default" : "cursor-text"
9423
+ ].join(" ")
9424
+ }
9425
+ ),
9426
+ linkTooltip && /* @__PURE__ */ jsxs(
9427
+ "div",
9428
+ {
9429
+ role: "tooltip",
9430
+ style: { left: linkTooltip.x, top: linkTooltip.y },
9431
+ className: "absolute z-50 pointer-events-none flex items-center gap-1.5 bg-gray-900 text-white text-xs px-2.5 py-1.5 rounded shadow-lg max-w-xs",
9432
+ children: [
9433
+ /* @__PURE__ */ jsxs("svg", { width: "11", height: "11", viewBox: "0 0 16 16", fill: "none", className: "shrink-0 opacity-70", "aria-hidden": "true", children: [
9434
+ /* @__PURE__ */ jsx("path", { d: "M6.5 3.5H4A2.5 2.5 0 0 0 4 8.5h2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
9435
+ /* @__PURE__ */ jsx("path", { d: "M9.5 3.5H12A2.5 2.5 0 0 1 12 8.5h-2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
9436
+ /* @__PURE__ */ jsx("path", { d: "M5.5 6h5", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })
9437
+ ] }),
9438
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: linkTooltip.href }),
9439
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 opacity-50 ml-1", children: "Ctrl+click to open" })
9440
+ ]
9441
+ }
9442
+ )
9443
+ ] }) }),
9444
+ inTableCellPos && !readOnly && /* @__PURE__ */ jsx(
9445
+ TableToolbar,
9446
+ {
9447
+ engine,
9448
+ tablePath: inTableCellPos.tablePath,
9449
+ cellPos: { row: inTableCellPos.row, col: inTableCellPos.col },
9450
+ tableSelection,
9451
+ editorContainer: containerRef
9452
+ }
9453
+ ),
9454
+ tableContextMenu && /* @__PURE__ */ jsx(
9455
+ TableContextMenu,
9456
+ {
9457
+ x: tableContextMenu.x,
9458
+ y: tableContextMenu.y,
9459
+ tablePath: tableContextMenu.tablePath,
9460
+ row: tableContextMenu.row,
9461
+ col: tableContextMenu.col,
9462
+ isMerged: tableContextMenu.isMerged,
9463
+ tableSelection,
9464
+ engine,
9465
+ onClose: () => setTableContextMenu(null)
9466
+ }
9467
+ ),
9468
+ findReplaceOpen && /* @__PURE__ */ jsx(
9469
+ FindReplaceModal,
9470
+ {
9471
+ engine,
9472
+ editorContainer: containerRef,
9473
+ initialMode: findReplaceMode,
9474
+ onClose: () => setFindReplaceOpen(false)
9475
+ }
9476
+ ),
9477
+ selectedImagePath && !readOnly && /* @__PURE__ */ jsx(
9478
+ ImageToolbar,
9479
+ {
9480
+ engine,
9481
+ imagePath: selectedImagePath,
9482
+ editorContainer: containerRef,
9483
+ onClose: () => setSelectedImagePath(null)
9484
+ }
9485
+ ),
9486
+ !readOnly && /* @__PURE__ */ jsx(
9487
+ "div",
9488
+ {
9489
+ title: "Drag to resize",
9490
+ onMouseDown: (e) => {
9491
+ e.preventDefault();
9492
+ const rootEl = editorRootRef.current;
9493
+ if (!rootEl) return;
9494
+ resizeDragRef.current = {
9495
+ startY: e.clientY,
9496
+ startHeight: rootEl.getBoundingClientRect().height
9497
+ };
9498
+ },
9499
+ className: "absolute bottom-0 right-0 w-4 h-4 cursor-s-resize flex items-end justify-end pr-0.5 pb-0.5 select-none",
9500
+ "aria-hidden": "true",
9501
+ children: /* @__PURE__ */ jsx("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M7 1L1 7M7 4L4 7M7 7L7 7", stroke: "#9ca3af", strokeWidth: "1.2", strokeLinecap: "round" }) })
9502
+ }
9503
+ )
9504
+ ]
9505
+ }
9506
+ );
9507
+ }
9508
+ var BLOCK_TAGS2 = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
9509
+ var _OPEN_ONLY_RE2 = new RegExp(`^<(${BLOCK_TAGS2})(?:\\s[^>]*)?>$`, "i");
9510
+ var _CLOSE_ONLY_RE2 = new RegExp(`^<\\/(${BLOCK_TAGS2})>$`, "i");
9511
+ var _HR_RE2 = /^<hr(\s[^>]*)?\/?>$/i;
9512
+ function leafTextNode2(el, end) {
9513
+ let node = el;
9514
+ while (node.hasChildNodes()) {
9515
+ node = end === "first" ? node.firstChild : node.lastChild;
9516
+ }
9517
+ return node.nodeType === Node.TEXT_NODE ? node : null;
9518
+ }
9519
+ function prettyPrintHTML2(html) {
9520
+ const blockOpen = new RegExp(`(<(?:${BLOCK_TAGS2})(?:\\s[^>]*)?>)`, "gi");
9521
+ const blockClose = new RegExp(`(<\\/(?:${BLOCK_TAGS2})>)`, "gi");
9522
+ const spread = html.replace(blockOpen, "\n$1").replace(blockClose, "$1\n").replace(/<hr(\s[^>]*)?\/?>(\s*)/gi, "\n<hr />\n");
9523
+ let depth = 0;
9524
+ const result = [];
9525
+ for (const raw of spread.split("\n")) {
9526
+ const line = raw.trim();
9527
+ if (!line) continue;
9528
+ if (_HR_RE2.test(line)) {
9529
+ result.push(" ".repeat(depth) + line);
9530
+ continue;
9531
+ }
9532
+ if (_CLOSE_ONLY_RE2.test(line)) {
9533
+ depth = Math.max(0, depth - 1);
9534
+ result.push(" ".repeat(depth) + line);
9535
+ continue;
9536
+ }
9537
+ if (_OPEN_ONLY_RE2.test(line)) {
9538
+ result.push(" ".repeat(depth) + line);
9539
+ depth++;
9540
+ continue;
9541
+ }
9542
+ result.push(" ".repeat(depth) + line);
9543
+ }
9544
+ return result.join("\n");
9545
+ }
9546
+
9547
+ // src/editor/table/TablePlugin.ts
9548
+ function getCellDOMElement(cellPathJson) {
9549
+ return document.querySelector(`[data-block-path='${cellPathJson}']`);
9550
+ }
9551
+ function isCaretAtContainerStart(container) {
9552
+ const sel = window.getSelection();
9553
+ if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return false;
9554
+ const range = sel.getRangeAt(0);
9555
+ if (range.startOffset !== 0) return false;
9556
+ let node = range.startContainer;
9557
+ while (node && node !== container) {
9558
+ if (node.previousSibling) return false;
9559
+ node = node.parentNode;
9560
+ }
9561
+ return node === container;
9562
+ }
9563
+ function isCaretAtContainerEnd(container) {
9564
+ var _a, _b;
9565
+ const sel = window.getSelection();
9566
+ if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return false;
9567
+ const range = sel.getRangeAt(0);
9568
+ const endNode = range.endContainer;
9569
+ const endOffset = range.endOffset;
9570
+ const len = endNode.nodeType === Node.TEXT_NODE ? (_b = (_a = endNode.textContent) == null ? void 0 : _a.length) != null ? _b : 0 : endNode.childNodes.length;
9571
+ if (endOffset !== len) return false;
9572
+ let node = endNode;
9573
+ while (node && node !== container) {
9574
+ if (node.nextSibling) return false;
9575
+ node = node.parentNode;
9576
+ }
9577
+ return node === container;
9578
+ }
9579
+ function isCaretOnEdgeLine(container, direction) {
9580
+ const sel = window.getSelection();
9581
+ if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return false;
9582
+ const caretRange = sel.getRangeAt(0).cloneRange();
9583
+ caretRange.collapse(true);
9584
+ const caretRects = caretRange.getClientRects();
9585
+ if (!caretRects.length) return true;
9586
+ const caretRect = caretRects[0];
9587
+ const containerRect = container.getBoundingClientRect();
9588
+ const lineHeight = parseFloat(getComputedStyle(container).lineHeight) || 20;
9589
+ if (direction === "up") {
9590
+ return caretRect.top < containerRect.top + lineHeight;
9591
+ } else {
9592
+ return caretRect.bottom > containerRect.bottom - lineHeight;
9593
+ }
9594
+ }
9595
+ var TablePlugin = {
9596
+ name: "table",
9597
+ keyBindings: {
9598
+ Tab: (engine) => {
9599
+ var _a;
9600
+ const state = engine.getState();
9601
+ const sel = state.selection;
9602
+ if (!sel) return false;
9603
+ const cellPos = findCellPosition(state.doc, sel.anchor.path);
9604
+ if (!cellPos) return false;
9605
+ const { tablePath, row, col } = cellPos;
9606
+ const table = getNodeAtPath(state.doc, tablePath);
9607
+ const { rows, cols } = getTableDimensions(table);
9608
+ let nextRow = row;
9609
+ let nextCol = col + 1;
9610
+ while (nextRow < rows) {
9611
+ if (nextCol >= cols) {
9612
+ nextCol = 0;
9613
+ nextRow++;
9614
+ continue;
9615
+ }
9616
+ const c = getNodeAtPath(state.doc, [...tablePath, nextRow, nextCol]);
9617
+ if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
9618
+ nextCol++;
9619
+ }
9620
+ if (nextRow >= rows) {
9621
+ const newTable = insertTableRowAfter(table, rows - 1);
9622
+ const newChildren = [...state.doc.children];
9623
+ newChildren[tablePath[0]] = newTable;
9624
+ const tempDoc = { type: "doc", children: newChildren };
9625
+ const tr2 = createTransaction();
9626
+ tr2.steps.push({ type: "delete_node", path: tablePath });
9627
+ tr2.steps.push({
9628
+ type: "insert_node",
9629
+ parentPath: tablePath.length > 1 ? tablePath.slice(0, -1) : [],
9630
+ index: tablePath[tablePath.length - 1],
9631
+ node: newTable
9632
+ });
9633
+ const pos2 = getCellFirstPosition(tempDoc, tablePath, rows, 0);
9634
+ if (pos2) tr2.steps.push(tr_setSelection(makeCollapsedSelection(pos2)));
9635
+ engine.dispatch(tr2);
9636
+ return true;
9637
+ }
9638
+ const pos = getCellFirstPosition(state.doc, tablePath, nextRow, nextCol);
9639
+ if (!pos) return false;
9640
+ const tr = createTransaction();
9641
+ tr.steps.push(tr_setSelection(makeCollapsedSelection(pos)));
9642
+ engine.dispatch(tr);
9643
+ return true;
9644
+ },
9645
+ "ArrowRight": (engine) => {
9646
+ var _a;
9647
+ const state = engine.getState();
9648
+ const sel = state.selection;
9649
+ if (!sel) return false;
9650
+ const cellPos = findCellPosition(state.doc, sel.anchor.path);
9651
+ if (!cellPos) return false;
9652
+ const { tablePath, row, col } = cellPos;
9653
+ const cellPath = [...tablePath, row, col];
9654
+ const cellEl = getCellDOMElement(JSON.stringify(cellPath));
9655
+ if (!cellEl || !isCaretAtContainerEnd(cellEl)) return false;
9656
+ const table = getNodeAtPath(state.doc, tablePath);
9657
+ const { rows, cols } = getTableDimensions(table);
9658
+ let nextRow = row;
9659
+ let nextCol = col + 1;
9660
+ while (nextRow < rows) {
9661
+ if (nextCol >= cols) {
9662
+ nextCol = 0;
9663
+ nextRow++;
9664
+ continue;
9665
+ }
9666
+ const c = getNodeAtPath(state.doc, [...tablePath, nextRow, nextCol]);
9667
+ if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
9668
+ nextCol++;
9669
+ }
9670
+ if (nextRow >= rows) return true;
9671
+ const pos = getCellFirstPosition(state.doc, tablePath, nextRow, nextCol);
9672
+ if (!pos) return false;
9673
+ const tr = createTransaction();
9674
+ tr.steps.push(tr_setSelection(makeCollapsedSelection(pos)));
9675
+ engine.dispatch(tr);
9676
+ return true;
9677
+ },
9678
+ "ArrowLeft": (engine) => {
9679
+ var _a;
9680
+ const state = engine.getState();
9681
+ const sel = state.selection;
9682
+ if (!sel) return false;
9683
+ const cellPos = findCellPosition(state.doc, sel.anchor.path);
9684
+ if (!cellPos) return false;
9685
+ const { tablePath, row, col } = cellPos;
9686
+ const cellPath = [...tablePath, row, col];
9687
+ const cellEl = getCellDOMElement(JSON.stringify(cellPath));
9688
+ if (!cellEl || !isCaretAtContainerStart(cellEl)) return false;
9689
+ const table = getNodeAtPath(state.doc, tablePath);
9690
+ const { cols } = getTableDimensions(table);
9691
+ let prevRow = row;
9692
+ let prevCol = col - 1;
9693
+ while (prevRow >= 0) {
9694
+ if (prevCol < 0) {
9695
+ prevCol = cols - 1;
9696
+ prevRow--;
9697
+ continue;
9698
+ }
9699
+ const c = getNodeAtPath(state.doc, [...tablePath, prevRow, prevCol]);
9700
+ if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
9701
+ prevCol--;
9702
+ }
9703
+ if (prevRow < 0) return true;
9704
+ const pos = getCellLastPosition(state.doc, tablePath, prevRow, prevCol);
9705
+ if (!pos) return false;
9706
+ const tr = createTransaction();
9707
+ tr.steps.push(tr_setSelection(makeCollapsedSelection(pos)));
8848
9708
  engine.dispatch(tr);
8849
9709
  return true;
8850
9710
  },
@@ -9146,151 +10006,6 @@ function execCommand(engine, command, ...args) {
9146
10006
  function registerCommand(name, fn) {
9147
10007
  REGISTRY[name] = fn;
9148
10008
  }
9149
- var Editor = forwardRef(function Editor2(props, ref) {
9150
- var _a;
9151
- const {
9152
- value,
9153
- defaultValue,
9154
- onChange,
9155
- placeholder,
9156
- readOnly = false,
9157
- toolbar = DEFAULT_TOOLBAR,
9158
- theme = "light",
9159
- plugins,
9160
- onFocus,
9161
- onBlur,
9162
- onReady,
9163
- onUploadImage,
9164
- className,
9165
- style,
9166
- minHeight,
9167
- maxHeight
9168
- } = props;
9169
- const engine = useEditorEngine();
9170
- const pluginsRegisteredRef = useRef(false);
9171
- if (!pluginsRegisteredRef.current && (plugins == null ? void 0 : plugins.length)) {
9172
- pluginsRegisteredRef.current = true;
9173
- for (const p of plugins) {
9174
- engine.registerPlugin(p);
9175
- }
9176
- }
9177
- const initializedRef = useRef(false);
9178
- useEffect(() => {
9179
- if (initializedRef.current) return;
9180
- initializedRef.current = true;
9181
- const initHTML = value != null ? value : defaultValue;
9182
- if (!initHTML) return;
9183
- const doc = htmlSerializer.deserialize(initHTML);
9184
- const tr = createTransaction();
9185
- tr.steps.push(tr_replaceDoc(doc));
9186
- engine.dispatch(tr);
9187
- }, []);
9188
- const lastEmittedRef = useRef("");
9189
- useEffect(() => {
9190
- if (value === void 0) return;
9191
- if (value === lastEmittedRef.current) return;
9192
- const currentHTML = htmlSerializer.serialize(engine.getState().doc);
9193
- if (value === currentHTML) return;
9194
- const doc = htmlSerializer.deserialize(value);
9195
- const tr = createTransaction();
9196
- tr.steps.push(tr_replaceDoc(doc));
9197
- engine.dispatch(tr);
9198
- }, [value, engine]);
9199
- const handleHTMLChange = useCallback((html) => {
9200
- lastEmittedRef.current = html;
9201
- onChange == null ? void 0 : onChange(html);
9202
- }, [onChange]);
9203
- const rootRef = useRef(null);
9204
- const api = useMemo(() => ({
9205
- getHTML: () => htmlSerializer.serialize(engine.getState().doc),
9206
- setHTML: (html) => {
9207
- const doc = htmlSerializer.deserialize(html);
9208
- const tr = createTransaction();
9209
- tr.steps.push(tr_replaceDoc(doc));
9210
- engine.dispatch(tr);
9211
- },
9212
- getJSON: () => engine.getState().doc,
9213
- getMarkdown: () => markdownSerializer.serialize(engine.getState().doc),
9214
- focus: () => {
9215
- var _a2;
9216
- const ce = (_a2 = rootRef.current) == null ? void 0 : _a2.querySelector("[contenteditable]");
9217
- ce == null ? void 0 : ce.focus();
9218
- },
9219
- blur: () => {
9220
- var _a2;
9221
- const ce = (_a2 = rootRef.current) == null ? void 0 : _a2.querySelector("[contenteditable]");
9222
- ce == null ? void 0 : ce.blur();
9223
- },
9224
- clear: () => {
9225
- const tr = createTransaction();
9226
- tr.steps.push(tr_replaceDoc(createEmptyDocument()));
9227
- engine.dispatch(tr);
9228
- },
9229
- undo: () => {
9230
- engine.handleKeyDown(
9231
- new KeyboardEvent("keydown", { key: "z", ctrlKey: true, bubbles: true })
9232
- );
9233
- },
9234
- redo: () => {
9235
- engine.handleKeyDown(
9236
- new KeyboardEvent("keydown", { key: "y", ctrlKey: true, bubbles: true })
9237
- );
9238
- },
9239
- execCommand: (command, ...args) => {
9240
- return execCommand(engine, command, ...args);
9241
- },
9242
- registerCommand: (name, fn) => {
9243
- registerCommand(name, fn);
9244
- },
9245
- getEngine: () => engine
9246
- }), [engine]);
9247
- useImperativeHandle(ref, () => api, [api]);
9248
- useEffect(() => {
9249
- onReady == null ? void 0 : onReady(api);
9250
- }, []);
9251
- const themeMode = typeof theme === "string" ? theme : (_a = theme.mode) != null ? _a : "light";
9252
- const themeTokenOverrides = typeof theme === "object" && theme.tokens ? theme.tokens : void 0;
9253
- const rootStyle = useMemo(() => {
9254
- const base = { ...style };
9255
- if (minHeight !== void 0)
9256
- base.minHeight = typeof minHeight === "number" ? `${minHeight}px` : minHeight;
9257
- if (maxHeight !== void 0) {
9258
- base.maxHeight = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
9259
- base.overflow = "auto";
9260
- }
9261
- if (themeTokenOverrides) {
9262
- for (const [key, val] of Object.entries(themeTokenOverrides)) {
9263
- const cssVar = `--editor-${key.replace(/([A-Z])/g, (m) => `-${m.toLowerCase()}`)}`;
9264
- base[cssVar] = val;
9265
- }
9266
- }
9267
- return base;
9268
- }, [style, minHeight, maxHeight, themeTokenOverrides]);
9269
- return /* @__PURE__ */ jsx(
9270
- "div",
9271
- {
9272
- ref: rootRef,
9273
- "data-editor-theme": themeMode,
9274
- "data-traffica-editor": "",
9275
- className,
9276
- style: rootStyle,
9277
- children: /* @__PURE__ */ jsx(
9278
- EditorCore,
9279
- {
9280
- engine,
9281
- placeholder,
9282
- readOnly,
9283
- hideToolbar: toolbar === false,
9284
- toolbarConfig: toolbar !== false ? toolbar : void 0,
9285
- onHTMLChange: handleHTMLChange,
9286
- onFocus,
9287
- onBlur,
9288
- onUploadImage
9289
- }
9290
- )
9291
- }
9292
- );
9293
- });
9294
10009
 
9295
10010
  export { BASIC_TOOLBAR, DEFAULT_TOOLBAR, Editor, EditorCore, EditorEngine, MINIMAL_TOOLBAR, TablePlugin, Toolbar, addColumnLeft, addColumnRight, addMarkToNode, addRowAbove, addRowBelow, applyMarkToRange, applySearchHighlights, applyTransaction, attachClipboardHandlers, attachPasteHandler, captureSelection, clearSearchHighlights, collectText, comparePaths, comparePositions, createEmptyDocument, createHistoryManager, createHistoryPlugin, createParagraph, createPastePlugin, createTableCell, createTableNode, createTransaction, deleteColumn, deleteImageAtPath, deleteRange, deleteRow, deleteTable, deleteTableColumn, deleteTableRow, deleteTextAtPath, execCommand, findCellPosition, findContentBlockPath, findMatches, findTablePath, getActiveAlignment, getActiveBlockType, getActiveFontFamily, getActiveFontSize, getActiveHighlightColor, getActiveLinkHref, getActiveLinkRange, getActiveMarks, getActiveTextColor, getCellFirstPosition, getCellLastPosition, getDocumentLength, getDocumentMarkAttrValues, getNodeAtPath, getTableDimensions, getTextNodesBetween, htmlSerializer, insertImage, insertLink, insertTable, insertTableColumnAfter, insertTableColumnBefore, insertTableRowAfter, insertTableRowBefore, insertText, insertTextAtPath, isBlockNode, isContainerBlock, isSelectionInContainer, isTextNode, joinBlocks, jsonSerializer, makeCollapsedSelection, makePosition, markdownSerializer, marksEqual, mergeAdjacentTextNodesInDoc, mergeCells, mergeTableCells, normalizeRange, redo, registerCommand, removeMarkFromNode, removeMarkFromRange, renderDocument, replaceAllMatches, replaceMatch, restoreSelection, scrollMatchIntoView, setAlignment, setBlockType2 as setBlockType, setCodeBlockLanguage, setColumnWidth, setFontFamily, setFontSize, setHighlightColor, setImageAttr, setMarkOnRange, setTextColor, splitBlock, splitCell, splitTableCell, toggleCheckItemAt, toggleHeaderRow, toggleMark, undo, updateColumnWidth, useEditorEngine, useEditorState, walkDocument };
9296
10011
  //# sourceMappingURL=index.mjs.map