@underverse-ui/underverse 2.0.29 → 2.0.30

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.
Files changed (35) hide show
  1. package/api-reference.json +1 -1
  2. package/dist/{EmojiPicker-NV3GQYWI.js → EmojiPicker-ISJ6RXYG.js} +4 -4
  3. package/dist/{chunk-LK6YWBI6.js → chunk-2P7TSXEE.js} +82 -25
  4. package/dist/chunk-2P7TSXEE.js.map +1 -0
  5. package/dist/{chunk-5QWYO527.js → chunk-37MHVJMV.js} +846 -90
  6. package/dist/chunk-37MHVJMV.js.map +1 -0
  7. package/dist/{chunk-XTZTUDRE.js → chunk-57BKIC6J.js} +711 -800
  8. package/dist/chunk-57BKIC6J.js.map +1 -0
  9. package/dist/{chunk-LRIRY4DG.js → chunk-5W63IHIN.js} +11 -3
  10. package/dist/chunk-5W63IHIN.js.map +1 -0
  11. package/dist/{chunk-F7MX3XXK.js → chunk-S4UHFRL3.js} +3 -3
  12. package/dist/{chunk-A4GUPKHM.js → chunk-ZP7F4ZD7.js} +63 -43
  13. package/dist/chunk-ZP7F4ZD7.js.map +1 -0
  14. package/dist/index.cjs +2225 -1311
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +55 -0
  17. package/dist/index.d.ts +55 -0
  18. package/dist/index.js +23 -10
  19. package/dist/index.js.map +1 -1
  20. package/dist/{menu-bar-AHG64PEB.js → menu-bar-DPCNNUX5.js} +193 -47
  21. package/dist/menu-bar-DPCNNUX5.js.map +1 -0
  22. package/dist/ueditor.cjs +9407 -8506
  23. package/dist/ueditor.cjs.map +1 -1
  24. package/dist/ueditor.d.cts +10 -0
  25. package/dist/ueditor.d.ts +10 -0
  26. package/dist/ueditor.js +4 -4
  27. package/package.json +2 -1
  28. package/dist/chunk-5QWYO527.js.map +0 -1
  29. package/dist/chunk-A4GUPKHM.js.map +0 -1
  30. package/dist/chunk-LK6YWBI6.js.map +0 -1
  31. package/dist/chunk-LRIRY4DG.js.map +0 -1
  32. package/dist/chunk-XTZTUDRE.js.map +0 -1
  33. package/dist/menu-bar-AHG64PEB.js.map +0 -1
  34. /package/dist/{EmojiPicker-NV3GQYWI.js.map → EmojiPicker-ISJ6RXYG.js.map} +0 -0
  35. /package/dist/{chunk-F7MX3XXK.js.map → chunk-S4UHFRL3.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  EmojiGridButton,
3
3
  formatEmojiCountLabel
4
- } from "./chunk-LRIRY4DG.js";
4
+ } from "./chunk-5W63IHIN.js";
5
5
  import {
6
6
  COLUMN_RESIZE_LINE_THICKNESS,
7
7
  CellBgColorIcon,
@@ -28,6 +28,7 @@ import {
28
28
  applyImageLayout,
29
29
  applyImageWidthPreset,
30
30
  applyTableAlignment,
31
+ buildTableFormulaDependencyGraph,
31
32
  clampResponsiveTableWidthBp,
32
33
  clearTableColumnAt,
33
34
  clearTableRowAt,
@@ -36,10 +37,14 @@ import {
36
37
  dispatchTableLayoutChange,
37
38
  duplicateTableColumnAt,
38
39
  duplicateTableRowAt,
40
+ evaluateBasicTableFormula,
39
41
  expandTableFromCell,
40
42
  findTableNodeInfoFromState,
41
43
  findTableRowNodeInfo,
42
44
  formatBasisPointsAsPercentage,
45
+ formatFormulaError,
46
+ formatTableFormulaDisplayValue,
47
+ getAffectedTableFormulaLabels,
43
48
  getDefaultFontSizes,
44
49
  getDefaultLineHeights,
45
50
  getLegacyTableColumnWeights,
@@ -50,7 +55,11 @@ import {
50
55
  getResponsiveTableOffsetBp,
51
56
  getResponsiveTableWidthBp,
52
57
  getSelectionTableCell,
58
+ getTableCellRangeLabels,
53
59
  getTableColumnRatios,
60
+ getTableFormulaRecalculationOrder,
61
+ getTableFormulaReferences,
62
+ indexToColumnName,
54
63
  insertResponsiveColumnLayout,
55
64
  isCrossRealmElement,
56
65
  isCrossRealmHTMLElement,
@@ -58,6 +67,7 @@ import {
58
67
  isCrossRealmTable,
59
68
  isCrossRealmTableCell,
60
69
  isCrossRealmTableRow,
70
+ isDraftTableFormula,
61
71
  isPointOverRenderedText,
62
72
  isRowResizeHotspot,
63
73
  isRowResizingGlobal,
@@ -66,19 +76,24 @@ import {
66
76
  moveResponsiveColumnRatio,
67
77
  normalizeColumnRatios,
68
78
  normalizeStyleValue,
79
+ normalizeTableFormula,
69
80
  normalizeTableWidthMode,
70
81
  parseColumnRatios,
71
82
  parsePercentageToBasisPoints,
83
+ parseTableCellRange,
72
84
  resetImageSize,
73
85
  resolveColumnResizeTarget,
74
86
  resolveEventElement,
75
87
  resolveResponsiveTableOffsetBp,
76
88
  resolveRowResizeTarget,
89
+ rewriteTableNodeFormulaReferencesForDeletion,
90
+ rewriteTableNodeFormulaReferencesForInsertion,
91
+ rewriteTableNodeFormulaReferencesForMove,
77
92
  runTableCommandAtCellPos,
78
93
  sanitizeUEditorUrl,
79
94
  useEditorColors,
80
95
  useSharedEditorUiRenderState
81
- } from "./chunk-5QWYO527.js";
96
+ } from "./chunk-37MHVJMV.js";
82
97
  import {
83
98
  Tooltip,
84
99
  UEditorPortalProvider,
@@ -88,7 +103,7 @@ import {
88
103
  resolveUEditorPortalContainer,
89
104
  useSmartTranslations,
90
105
  useUEditorPortalContainerGetter
91
- } from "./chunk-A4GUPKHM.js";
106
+ } from "./chunk-ZP7F4ZD7.js";
92
107
 
93
108
  // src/components/UEditor/prepare-content-for-save.ts
94
109
  var MIME_EXTENSION_MAP = {
@@ -141,16 +156,16 @@ function createFileFromDataImageUrl(dataUrl, index) {
141
156
  const name = `ueditor-image-${index + 1}.${extension}`;
142
157
  return new File([bytes], name, { type: parsed.mime });
143
158
  }
144
- function normalizeUploadResult(result) {
159
+ function normalizeUploadResult(result, kind) {
145
160
  if (typeof result === "string") {
146
- const url2 = sanitizeUEditorUrl(result, "image");
161
+ const url2 = sanitizeUEditorUrl(result, kind);
147
162
  if (!url2) throw new Error("Upload handler returned an empty URL.");
148
163
  return { url: url2 };
149
164
  }
150
165
  if (!result || typeof result !== "object") {
151
166
  throw new Error("Upload handler returned invalid result.");
152
167
  }
153
- const url = typeof result.url === "string" ? sanitizeUEditorUrl(result.url, "image") : "";
168
+ const url = typeof result.url === "string" ? sanitizeUEditorUrl(result.url, kind) : "";
154
169
  if (!url) throw new Error("Upload handler object result is missing `url`.");
155
170
  const { url: _ignoredUrl, ...rest } = result;
156
171
  const meta = Object.keys(rest).length > 0 ? rest : void 0;
@@ -205,11 +220,13 @@ function normalizeImageUrl(url) {
205
220
  }
206
221
  function replaceSrcInTag(match, nextSrc) {
207
222
  if (!match.srcAttr) return match.tag;
208
- const { start, end, quote } = match.srcAttr;
209
- const escaped = quote === '"' ? nextSrc.replace(/"/g, """) : quote === "'" ? nextSrc.replace(/'/g, "'") : nextSrc;
210
- const srcAttr = quote ? `src=${quote}${escaped}${quote}` : `src=${escaped}`;
223
+ const { start, end } = match.srcAttr;
224
+ const srcAttr = `src="${escapeHtmlAttribute(nextSrc)}"`;
211
225
  return `${match.tag.slice(0, start)}${srcAttr}${match.tag.slice(end)}`;
212
226
  }
227
+ function escapeHtmlAttribute(value) {
228
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
229
+ }
213
230
  function collectImgTagMatches(html) {
214
231
  const matches = [];
215
232
  const imgRegex = /<img\b[^>]*>/gi;
@@ -327,13 +344,14 @@ function collectFileCardMatches(html) {
327
344
  }
328
345
  function replaceFileCardAttrs(tag, nextUrl) {
329
346
  let result = tag;
347
+ const escapedUrl = escapeHtmlAttribute(nextUrl);
330
348
  const hrefRegex = /(\bhref\s*=\s*)(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+)/i;
331
349
  const dataSrcRegex = /(\bdata-src\s*=\s*)(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+)/i;
332
350
  if (hrefRegex.test(result)) {
333
- result = result.replace(hrefRegex, `$1"${nextUrl}"`);
351
+ result = result.replace(hrefRegex, `$1"${escapedUrl}"`);
334
352
  }
335
353
  if (dataSrcRegex.test(result)) {
336
- result = result.replace(dataSrcRegex, `$1"${nextUrl}"`);
354
+ result = result.replace(dataSrcRegex, `$1"${escapedUrl}"`);
337
355
  }
338
356
  return result;
339
357
  }
@@ -403,7 +421,7 @@ async function prepareUEditorContentForSave({
403
421
  try {
404
422
  const file = createFileFromDataImageUrl(candidate.src, candidate.index);
405
423
  const uploadResult = await uploadImageForSave(file);
406
- const normalized = normalizeUploadResult(uploadResult);
424
+ const normalized = normalizeUploadResult(uploadResult, "image");
407
425
  return { candidate, file, ...normalized };
408
426
  } catch (error) {
409
427
  return { candidate, error: getErrorReason(error) };
@@ -450,7 +468,7 @@ async function prepareUEditorContentForSave({
450
468
  try {
451
469
  const file = createFileFromDataUrl(candidate.src, candidate.fileName);
452
470
  const uploadResult = await uploadFn(file);
453
- const normalized = normalizeUploadResult(uploadResult);
471
+ const normalized = normalizeUploadResult(uploadResult, "file");
454
472
  return { candidate, file, ...normalized };
455
473
  } catch (error) {
456
474
  return { candidate, error: getErrorReason(error) };
@@ -503,7 +521,7 @@ async function prepareUEditorContentForSave({
503
521
  }
504
522
 
505
523
  // src/components/UEditor/UEditor.tsx
506
- import React15, { useEffect as useEffect10, useImperativeHandle as useImperativeHandle4, useMemo as useMemo2, useRef as useRef10 } from "react";
524
+ import React17, { useEffect as useEffect10, useImperativeHandle as useImperativeHandle4, useMemo as useMemo2, useRef as useRef10 } from "react";
507
525
  import { useEditor, EditorContent } from "@tiptap/react";
508
526
 
509
527
  // src/components/UEditor/extensions.ts
@@ -538,7 +556,7 @@ import { NodeViewWrapper, NodeViewContent } from "@tiptap/react";
538
556
  import React from "react";
539
557
  import { jsx } from "react/jsx-runtime";
540
558
  var LazyBaseEmojiPicker = React.lazy(async () => {
541
- const { EmojiPicker: EmojiPicker2 } = await import("./EmojiPicker-NV3GQYWI.js");
559
+ const { EmojiPicker: EmojiPicker2 } = await import("./EmojiPicker-ISJ6RXYG.js");
542
560
  return { default: EmojiPicker2 };
543
561
  });
544
562
  var EmojiPicker = ({ onSelect }) => {
@@ -575,7 +593,8 @@ var CalloutView = ({ node, updateAttributes, selected }) => {
575
593
  "button",
576
594
  {
577
595
  type: "button",
578
- className: "flex items-center justify-center text-2xl h-9 w-9 hover:bg-muted-foreground/10 active:scale-95 rounded-lg transition-all focus:outline-none",
596
+ "aria-label": t("callout.selectEmoji"),
597
+ className: "flex items-center justify-center text-2xl h-9 w-9 hover:bg-muted-foreground/10 active:scale-95 rounded-lg transition-[background-color,transform] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
579
598
  children: currentEmoji
580
599
  }
581
600
  ),
@@ -589,9 +608,11 @@ var CalloutView = ({ node, updateAttributes, selected }) => {
589
608
  {
590
609
  type: "button",
591
610
  title: col.name,
611
+ "aria-label": `${t("callout.backgroundColor")}: ${col.name}`,
612
+ "aria-pressed": currentBgColor === col.color,
592
613
  onClick: () => updateAttributes({ backgroundColor: col.color }),
593
614
  className: cn(
594
- "h-6 w-6 rounded-full border transition-all active:scale-90 hover:scale-105",
615
+ "h-6 w-6 rounded-full border transition-[transform,border-color,box-shadow] active:scale-90 hover:scale-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
595
616
  currentBgColor === col.color ? "border-primary ring-2 ring-primary/30" : "border-border/60"
596
617
  ),
597
618
  style: { backgroundColor: col.color }
@@ -1079,12 +1100,13 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
1079
1100
  e.preventDefault();
1080
1101
  e.stopPropagation();
1081
1102
  if (!safeSrc) return;
1082
- const link = document.createElement("a");
1103
+ const ownerDocument = editor.view.dom.ownerDocument;
1104
+ const link = ownerDocument.createElement("a");
1083
1105
  link.href = safeSrc;
1084
1106
  link.download = fileName || "download";
1085
- document.body.appendChild(link);
1107
+ ownerDocument.body.appendChild(link);
1086
1108
  link.click();
1087
- document.body.removeChild(link);
1109
+ ownerDocument.body.removeChild(link);
1088
1110
  };
1089
1111
  const hasInlineUpload = safeSrc.startsWith("data:") && Boolean(uploadFn);
1090
1112
  const handleRetryUpload = (event) => {
@@ -1098,18 +1120,19 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
1098
1120
  NodeViewWrapper3,
1099
1121
  {
1100
1122
  className: cn(
1101
- "my-4 flex items-center justify-between rounded-xl border border-border/60 bg-card p-4 hover:bg-muted/15 transition-all duration-200 cursor-pointer select-none",
1123
+ "my-4 flex items-center justify-between rounded-xl border border-border/60 bg-card p-4 hover:bg-muted/15 transition-[background-color,border-color,box-shadow] duration-200 select-none",
1102
1124
  selected && "ring-2 ring-primary/45 border-primary/20 shadow-sm"
1103
1125
  ),
1104
- onClick: handleDownload,
1126
+ role: "group",
1127
+ "aria-label": fileName || t("fileCard.unnamed"),
1105
1128
  children: [
1106
1129
  /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3.5 min-w-0", children: [
1107
- /* @__PURE__ */ jsx4("div", { className: "flex h-11 w-11 items-center justify-center rounded-lg bg-primary/10 text-primary flex-shrink-0", children: /* @__PURE__ */ jsx4(FileIcon, { className: "h-6 w-6" }) }),
1130
+ /* @__PURE__ */ jsx4("div", { className: "flex h-11 w-11 items-center justify-center rounded-lg bg-primary/10 text-primary flex-shrink-0", children: /* @__PURE__ */ jsx4(FileIcon, { "aria-hidden": "true", className: "h-6 w-6" }) }),
1108
1131
  /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
1109
1132
  /* @__PURE__ */ jsx4("div", { className: "text-sm font-semibold text-foreground truncate break-all", children: fileName || t("fileCard.unnamed") }),
1110
1133
  /* @__PURE__ */ jsxs3("div", { className: "text-xs text-muted-foreground flex items-center gap-2 mt-0.5", children: [
1111
1134
  fileSize ? /* @__PURE__ */ jsx4("span", { children: formatBytes(fileSize) }) : null,
1112
- hasInlineUpload && /* @__PURE__ */ jsx4("span", { className: cn(
1135
+ hasInlineUpload && /* @__PURE__ */ jsx4("span", { "aria-live": "polite", className: cn(
1113
1136
  "font-medium truncate max-w-50",
1114
1137
  uploadError ? "text-destructive" : "text-primary animate-pulse"
1115
1138
  ), children: uploadError ? `${t("fileCard.uploadFailed") || "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"}: ${uploadError}` : t("fileCard.uploading") }),
@@ -1120,7 +1143,7 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
1120
1143
  onClick: handleRetryUpload,
1121
1144
  className: "inline-flex items-center gap-1 rounded-md border border-destructive/30 px-1.5 py-0.5 text-[11px] font-medium text-destructive hover:bg-destructive/10",
1122
1145
  children: [
1123
- /* @__PURE__ */ jsx4(RefreshCw2, { className: "h-3 w-3" }),
1146
+ /* @__PURE__ */ jsx4(RefreshCw2, { "aria-hidden": "true", className: "h-3 w-3" }),
1124
1147
  t("fileCard.retryUpload")
1125
1148
  ]
1126
1149
  }
@@ -1133,8 +1156,9 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
1133
1156
  {
1134
1157
  type: "button",
1135
1158
  onClick: handleDownload,
1136
- className: "flex h-8 w-8 items-center justify-center rounded-lg border border-border bg-background text-muted-foreground hover:text-foreground hover:bg-muted active:scale-95 transition-all focus:outline-none",
1137
- children: /* @__PURE__ */ jsx4(Download, { className: "h-4 w-4" })
1159
+ "aria-label": t("fileCard.download").replace("{name}", fileName || t("fileCard.unnamed")),
1160
+ className: "flex h-8 w-8 items-center justify-center rounded-lg border border-border bg-background text-muted-foreground hover:text-foreground hover:bg-muted active:scale-95 transition-[color,background-color,transform] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1161
+ children: /* @__PURE__ */ jsx4(Download, { "aria-hidden": "true", className: "h-4 w-4" })
1138
1162
  }
1139
1163
  )
1140
1164
  ]
@@ -1392,6 +1416,7 @@ import { Node as Node5, mergeAttributes as mergeAttributes5 } from "@tiptap/core
1392
1416
  import { ReactNodeViewRenderer as ReactNodeViewRenderer5 } from "@tiptap/react";
1393
1417
 
1394
1418
  // src/components/UEditor/FormRadioView.tsx
1419
+ import { useId } from "react";
1395
1420
  import { NodeViewWrapper as NodeViewWrapper5 } from "@tiptap/react";
1396
1421
  import { Settings } from "lucide-react";
1397
1422
  import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
@@ -1400,6 +1425,8 @@ var FormRadioView = ({ node, updateAttributes, editor, selected }) => {
1400
1425
  const isChecked = !!node.attrs.checked;
1401
1426
  const isEditable = editor.isEditable;
1402
1427
  const groupName = node.attrs.name || "radio-group";
1428
+ const groupInputId = useId();
1429
+ const radioLabel = `${t("menubar.formRadio")} \u2014 ${node.attrs.value || groupName}`;
1403
1430
  const handleSelect = (e) => {
1404
1431
  e.preventDefault();
1405
1432
  e.stopPropagation();
@@ -1425,6 +1452,7 @@ var FormRadioView = ({ node, updateAttributes, editor, selected }) => {
1425
1452
  {
1426
1453
  role: "radio",
1427
1454
  "aria-checked": isChecked,
1455
+ "aria-label": radioLabel,
1428
1456
  tabIndex: isEditable ? 0 : -1,
1429
1457
  onClick: handleSelect,
1430
1458
  onKeyDown: handleKeyDown2,
@@ -1453,17 +1481,21 @@ var FormRadioView = ({ node, updateAttributes, editor, selected }) => {
1453
1481
  "button",
1454
1482
  {
1455
1483
  type: "button",
1484
+ "aria-label": t("radio.editGroup") || "Edit Group",
1456
1485
  className: "p-0.5 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors outline-none cursor-pointer",
1457
1486
  title: t("radio.editGroup") || "Edit Group",
1458
1487
  children: /* @__PURE__ */ jsx6(Settings, { className: "w-3.5 h-3.5" })
1459
1488
  }
1460
1489
  ),
1461
1490
  children: /* @__PURE__ */ jsxs4("div", { className: "p-3 w-52 flex flex-col gap-2 bg-card border rounded-xl shadow-lg text-card-foreground", children: [
1462
- /* @__PURE__ */ jsx6("label", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: t("radio.groupName") || "Group Name" }),
1491
+ /* @__PURE__ */ jsx6("label", { htmlFor: groupInputId, className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: t("radio.groupName") || "Group Name" }),
1463
1492
  /* @__PURE__ */ jsx6(
1464
1493
  "input",
1465
1494
  {
1466
1495
  type: "text",
1496
+ id: groupInputId,
1497
+ name: "radio-group-name",
1498
+ autoComplete: "off",
1467
1499
  value: groupName,
1468
1500
  onChange: (e) => updateAttributes({ name: e.target.value }),
1469
1501
  className: `w-full rounded-lg bg-background px-2 py-1.5 text-sm text-foreground ${formControlOutlineClass}`
@@ -1478,6 +1510,12 @@ var FormRadioView = ({ node, updateAttributes, editor, selected }) => {
1478
1510
  };
1479
1511
 
1480
1512
  // src/components/UEditor/form-radio.ts
1513
+ var formRadioIdSequence = 0;
1514
+ function createFormRadioId() {
1515
+ formRadioIdSequence += 1;
1516
+ const randomId = globalThis.crypto?.randomUUID?.();
1517
+ return randomId ? `rad-${randomId}` : `rad-${Date.now().toString(36)}-${formRadioIdSequence.toString(36)}`;
1518
+ }
1481
1519
  var FormRadio = Node5.create({
1482
1520
  name: "formRadio",
1483
1521
  group: "inline",
@@ -1510,7 +1548,7 @@ var FormRadio = Node5.create({
1510
1548
  },
1511
1549
  id: {
1512
1550
  default: null,
1513
- parseHTML: (element) => element.getAttribute("data-id") || element.getAttribute("id") || `rad-${Math.random().toString(36).substring(2, 9)}`,
1551
+ parseHTML: (element) => element.getAttribute("data-id") || element.getAttribute("id") || createFormRadioId(),
1514
1552
  renderHTML: (attributes) => {
1515
1553
  if (!attributes.id) return {};
1516
1554
  return {
@@ -1569,10 +1607,15 @@ var FormRadio = Node5.create({
1569
1607
  setFormRadio: (attributes) => ({ commands }) => {
1570
1608
  return commands.insertContent({
1571
1609
  type: this.name,
1572
- attrs: attributes
1610
+ attrs: {
1611
+ ...attributes,
1612
+ id: attributes?.id || createFormRadioId(),
1613
+ name: attributes?.name || "radio-group"
1614
+ }
1573
1615
  });
1574
1616
  },
1575
1617
  checkFormRadio: (id) => ({ tr, dispatch }) => {
1618
+ if (!id) return false;
1576
1619
  if (!dispatch) return true;
1577
1620
  let targetGroupName = "";
1578
1621
  let targetPos = -1;
@@ -1624,7 +1667,7 @@ import { Gapcursor } from "@tiptap/extension-gapcursor";
1624
1667
  import { Extension } from "@tiptap/core";
1625
1668
  import { Suggestion } from "@tiptap/suggestion";
1626
1669
  import { ReactRenderer } from "@tiptap/react";
1627
- import React4, { forwardRef, useEffect as useEffect3, useImperativeHandle, useRef as useRef2 } from "react";
1670
+ import React5, { forwardRef, useEffect as useEffect3, useImperativeHandle, useRef as useRef2 } from "react";
1628
1671
  import {
1629
1672
  FileCode,
1630
1673
  Heading1,
@@ -1709,9 +1752,9 @@ var DEFAULT_MESSAGES = {
1709
1752
  formRadioDesc: "Insert an interactive radio button field"
1710
1753
  };
1711
1754
  function useResettingIndex(resetToken) {
1712
- const [state, setState] = React4.useState({ resetToken, index: 0 });
1755
+ const [state, setState] = React5.useState({ resetToken, index: 0 });
1713
1756
  const selectedIndex = Object.is(state.resetToken, resetToken) ? state.index : 0;
1714
- const setSelectedIndex = React4.useCallback((nextIndex) => {
1757
+ const setSelectedIndex = React5.useCallback((nextIndex) => {
1715
1758
  setState((prev) => {
1716
1759
  const prevIndex = Object.is(prev.resetToken, resetToken) ? prev.index : 0;
1717
1760
  return {
@@ -1927,7 +1970,7 @@ function buildSlashCommandItems({
1927
1970
  title: messages.formRadio,
1928
1971
  description: messages.formRadioDesc,
1929
1972
  command: run(({ editor, range }) => {
1930
- getCommandChain(editor, range).setFormRadio({ name: `radio-group-${Math.random().toString(36).substring(2, 6)}` }).run();
1973
+ getCommandChain(editor, range).setFormRadio().run();
1931
1974
  })
1932
1975
  }
1933
1976
  ].filter((item) => {
@@ -1939,6 +1982,7 @@ function buildSlashCommandItems({
1939
1982
  var SlashCommandList = forwardRef((props, ref) => {
1940
1983
  const [selectedIndex, setSelectedIndex] = useResettingIndex(props.items);
1941
1984
  const listRef = useRef2(null);
1985
+ const listId = React5.useId();
1942
1986
  useEffect3(() => {
1943
1987
  const selectedElement = listRef.current?.querySelector(`[data-index="${selectedIndex}"]`);
1944
1988
  selectedElement?.scrollIntoView({ block: "nearest" });
@@ -1969,41 +2013,56 @@ var SlashCommandList = forwardRef((props, ref) => {
1969
2013
  }
1970
2014
  }));
1971
2015
  if (props.items.length === 0) {
1972
- return /* @__PURE__ */ jsx7("div", { className: "w-72 p-4 text-center text-sm text-muted-foreground", children: props.messages.noResults });
2016
+ return /* @__PURE__ */ jsx7("div", { role: "status", className: "w-72 p-4 text-center text-sm text-muted-foreground", children: props.messages.noResults });
1973
2017
  }
1974
- return /* @__PURE__ */ jsxs5("div", { ref: listRef, className: "w-72 max-h-80 overflow-y-auto bg-card border border-border/50 rounded-2xl shadow-lg", children: [
1975
- /* @__PURE__ */ jsx7("div", { className: "px-3 py-2 border-b", children: /* @__PURE__ */ jsx7("span", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: props.messages.basicBlocks }) }),
1976
- /* @__PURE__ */ jsx7("div", { className: "p-1", children: props.items.map((item, index) => /* @__PURE__ */ jsxs5(
1977
- "button",
1978
- {
1979
- type: "button",
1980
- "data-index": index,
1981
- onMouseDown: (event) => event.preventDefault(),
1982
- onClick: () => props.command(item),
1983
- className: cn(
1984
- "flex items-center w-full px-3 py-2.5 rounded-lg transition-colors group",
1985
- selectedIndex === index ? "bg-accent" : "hover:bg-accent/50"
1986
- ),
1987
- children: [
1988
- /* @__PURE__ */ jsx7(
1989
- "div",
1990
- {
1991
- className: cn(
1992
- "flex items-center justify-center w-10 h-10 rounded-lg mr-3 transition-colors",
1993
- selectedIndex === index ? "bg-primary/10" : "bg-muted/50 group-hover:bg-muted"
2018
+ return /* @__PURE__ */ jsxs5(
2019
+ "div",
2020
+ {
2021
+ ref: listRef,
2022
+ id: listId,
2023
+ role: "listbox",
2024
+ "aria-label": props.messages.basicBlocks,
2025
+ "aria-activedescendant": `${listId}-option-${selectedIndex}`,
2026
+ className: "w-72 max-h-80 overflow-y-auto bg-card border border-border/50 rounded-2xl shadow-lg",
2027
+ children: [
2028
+ /* @__PURE__ */ jsx7("div", { className: "px-3 py-2 border-b", children: /* @__PURE__ */ jsx7("span", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: props.messages.basicBlocks }) }),
2029
+ /* @__PURE__ */ jsx7("div", { className: "p-1", children: props.items.map((item, index) => /* @__PURE__ */ jsxs5(
2030
+ "button",
2031
+ {
2032
+ type: "button",
2033
+ id: `${listId}-option-${index}`,
2034
+ role: "option",
2035
+ "aria-selected": selectedIndex === index,
2036
+ tabIndex: -1,
2037
+ "data-index": index,
2038
+ onMouseDown: (event) => event.preventDefault(),
2039
+ onClick: () => props.command(item),
2040
+ className: cn(
2041
+ "flex items-center w-full px-3 py-2.5 rounded-lg transition-colors group",
2042
+ selectedIndex === index ? "bg-accent" : "hover:bg-accent/50"
2043
+ ),
2044
+ children: [
2045
+ /* @__PURE__ */ jsx7(
2046
+ "div",
2047
+ {
2048
+ className: cn(
2049
+ "flex items-center justify-center w-10 h-10 rounded-lg mr-3 transition-colors",
2050
+ selectedIndex === index ? "bg-primary/10" : "bg-muted/50 group-hover:bg-muted"
2051
+ ),
2052
+ children: /* @__PURE__ */ jsx7(item.icon, { "aria-hidden": "true", className: cn("w-5 h-5", selectedIndex === index ? "text-primary" : "text-muted-foreground") })
2053
+ }
1994
2054
  ),
1995
- children: /* @__PURE__ */ jsx7(item.icon, { className: cn("w-5 h-5", selectedIndex === index ? "text-primary" : "text-muted-foreground") })
1996
- }
1997
- ),
1998
- /* @__PURE__ */ jsxs5("div", { className: "text-left", children: [
1999
- /* @__PURE__ */ jsx7("div", { className: cn("text-sm font-medium", selectedIndex === index && "text-primary"), children: item.title }),
2000
- /* @__PURE__ */ jsx7("div", { className: "text-xs text-muted-foreground", children: item.description })
2001
- ] })
2002
- ]
2003
- },
2004
- item.title
2005
- )) })
2006
- ] });
2055
+ /* @__PURE__ */ jsxs5("div", { className: "text-left", children: [
2056
+ /* @__PURE__ */ jsx7("div", { className: cn("text-sm font-medium", selectedIndex === index && "text-primary"), children: item.title }),
2057
+ /* @__PURE__ */ jsx7("div", { className: "text-xs text-muted-foreground", children: item.description })
2058
+ ] })
2059
+ ]
2060
+ },
2061
+ item.title
2062
+ )) })
2063
+ ]
2064
+ }
2065
+ );
2007
2066
  });
2008
2067
  SlashCommandList.displayName = "SlashCommandList";
2009
2068
  var SlashCommand = Extension.create({
@@ -2087,13 +2146,13 @@ import { Extension as Extension2 } from "@tiptap/core";
2087
2146
  import { Suggestion as Suggestion2 } from "@tiptap/suggestion";
2088
2147
  import { ReactRenderer as ReactRenderer2 } from "@tiptap/react";
2089
2148
  import { PluginKey as PluginKey2 } from "@tiptap/pm/state";
2090
- import React5, { forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2 } from "react";
2149
+ import React6, { forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2 } from "react";
2091
2150
  import { Smile } from "lucide-react";
2092
2151
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
2093
2152
  function useResettingIndex2(resetToken) {
2094
- const [state, setState] = React5.useState({ resetToken, index: 0 });
2153
+ const [state, setState] = React6.useState({ resetToken, index: 0 });
2095
2154
  const selectedIndex = Object.is(state.resetToken, resetToken) ? state.index : 0;
2096
- const setSelectedIndex = React5.useCallback((nextIndex) => {
2155
+ const setSelectedIndex = React6.useCallback((nextIndex) => {
2097
2156
  setState((prev) => {
2098
2157
  const prevIndex = Object.is(prev.resetToken, resetToken) ? prev.index : 0;
2099
2158
  return {
@@ -2107,6 +2166,7 @@ function useResettingIndex2(resetToken) {
2107
2166
  var EmojiList = forwardRef2((props, ref) => {
2108
2167
  const t = useSmartTranslations("UEditor");
2109
2168
  const [selectedIndex, setSelectedIndex] = useResettingIndex2(props.items);
2169
+ const listId = React6.useId();
2110
2170
  const showingCountLabel = formatEmojiCountLabel(t("emojiSuggestion.showingCount"), 64, props.items.length);
2111
2171
  useImperativeHandle2(ref, () => ({
2112
2172
  onKeyDown: (keyProps) => {
@@ -2139,28 +2199,42 @@ var EmojiList = forwardRef2((props, ref) => {
2139
2199
  }
2140
2200
  }));
2141
2201
  if (props.items.length === 0) {
2142
- return /* @__PURE__ */ jsx8("div", { className: "w-80 p-4 text-center text-sm text-muted-foreground bg-card border border-border/50 rounded-2xl shadow-lg", children: t("emojiSuggestion.noResults") });
2143
- }
2144
- return /* @__PURE__ */ jsxs6("div", { className: "w-80 max-h-80 overflow-y-auto bg-card border border-border/50 rounded-2xl shadow-lg", children: [
2145
- /* @__PURE__ */ jsx8("div", { className: "px-3 py-2 border-b bg-muted/30", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
2146
- /* @__PURE__ */ jsx8(Smile, { className: "w-4 h-4 text-primary" }),
2147
- /* @__PURE__ */ jsx8("span", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: t("emojiSuggestion.title") })
2148
- ] }) }),
2149
- /* @__PURE__ */ jsxs6("div", { className: "p-3", children: [
2150
- /* @__PURE__ */ jsx8("div", { className: "grid grid-cols-8 gap-1", children: props.items.slice(0, 64).map((item, index) => /* @__PURE__ */ jsx8(
2151
- EmojiGridButton,
2152
- {
2153
- emoji: item.emoji,
2154
- name: item.name,
2155
- onClick: () => props.command(item),
2156
- active: selectedIndex === index,
2157
- className: "text-xl"
2158
- },
2159
- item.name
2160
- )) }),
2161
- props.items.length > 64 && /* @__PURE__ */ jsx8("div", { className: "mt-2 text-xs text-center text-muted-foreground", children: showingCountLabel })
2162
- ] })
2163
- ] });
2202
+ return /* @__PURE__ */ jsx8("div", { role: "status", className: "w-80 p-4 text-center text-sm text-muted-foreground bg-card border border-border/50 rounded-2xl shadow-lg", children: t("emojiSuggestion.noResults") });
2203
+ }
2204
+ return /* @__PURE__ */ jsxs6(
2205
+ "div",
2206
+ {
2207
+ id: listId,
2208
+ role: "listbox",
2209
+ "aria-label": t("emojiSuggestion.title"),
2210
+ "aria-activedescendant": `${listId}-option-${selectedIndex}`,
2211
+ className: "w-80 max-h-80 overflow-y-auto bg-card border border-border/50 rounded-2xl shadow-lg",
2212
+ children: [
2213
+ /* @__PURE__ */ jsx8("div", { className: "px-3 py-2 border-b bg-muted/30", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
2214
+ /* @__PURE__ */ jsx8(Smile, { className: "w-4 h-4 text-primary" }),
2215
+ /* @__PURE__ */ jsx8("span", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: t("emojiSuggestion.title") })
2216
+ ] }) }),
2217
+ /* @__PURE__ */ jsxs6("div", { className: "p-3", children: [
2218
+ /* @__PURE__ */ jsx8("div", { className: "grid grid-cols-8 gap-1", children: props.items.slice(0, 64).map((item, index) => /* @__PURE__ */ jsx8(
2219
+ EmojiGridButton,
2220
+ {
2221
+ id: `${listId}-option-${index}`,
2222
+ role: "option",
2223
+ "aria-selected": selectedIndex === index,
2224
+ tabIndex: -1,
2225
+ emoji: item.emoji,
2226
+ name: item.name,
2227
+ onClick: () => props.command(item),
2228
+ active: selectedIndex === index,
2229
+ className: "text-xl"
2230
+ },
2231
+ item.name
2232
+ )) }),
2233
+ props.items.length > 64 && /* @__PURE__ */ jsx8("div", { className: "mt-2 text-xs text-center text-muted-foreground", children: showingCountLabel })
2234
+ ] })
2235
+ ]
2236
+ }
2237
+ );
2164
2238
  });
2165
2239
  EmojiList.displayName = "EmojiList";
2166
2240
  var allEmojiItemsPromise = null;
@@ -2250,7 +2324,7 @@ import { Extension as Extension3 } from "@tiptap/core";
2250
2324
  import { Suggestion as Suggestion3 } from "@tiptap/suggestion";
2251
2325
  import { ReactRenderer as ReactRenderer3 } from "@tiptap/react";
2252
2326
  import { PluginKey as PluginKey3 } from "@tiptap/pm/state";
2253
- import React6, { forwardRef as forwardRef3, useImperativeHandle as useImperativeHandle3 } from "react";
2327
+ import React7, { forwardRef as forwardRef3, useImperativeHandle as useImperativeHandle3 } from "react";
2254
2328
  import { Sigma } from "lucide-react";
2255
2329
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2256
2330
  var FORMULA_FUNCTIONS = [
@@ -2305,8 +2379,9 @@ function isFormulaFunctionSuggestionQuery(query) {
2305
2379
  }
2306
2380
  var FormulaSuggestionList = forwardRef3((props, ref) => {
2307
2381
  const t = useSmartTranslations("UEditor");
2308
- const [selectedIndex, setSelectedIndex] = React6.useState(0);
2309
- React6.useEffect(() => {
2382
+ const [selectedIndex, setSelectedIndex] = React7.useState(0);
2383
+ const listId = React7.useId();
2384
+ React7.useEffect(() => {
2310
2385
  setSelectedIndex(0);
2311
2386
  }, [props.items]);
2312
2387
  useImperativeHandle3(ref, () => ({
@@ -2331,32 +2406,46 @@ var FormulaSuggestionList = forwardRef3((props, ref) => {
2331
2406
  }
2332
2407
  }));
2333
2408
  if (props.items.length === 0) {
2334
- return /* @__PURE__ */ jsx9("div", { className: "w-72 p-3 text-sm text-muted-foreground bg-card border border-border/50 rounded-lg shadow-lg", children: t("formulaSuggestion.noResults") });
2335
- }
2336
- return /* @__PURE__ */ jsxs7("div", { className: "w-72 max-h-80 overflow-y-auto bg-card border border-border/50 rounded-lg shadow-lg", children: [
2337
- /* @__PURE__ */ jsx9("div", { className: "px-3 py-2 border-b bg-muted/30", children: /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
2338
- /* @__PURE__ */ jsx9(Sigma, { className: "w-4 h-4 text-primary" }),
2339
- /* @__PURE__ */ jsx9("span", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: t("formulaSuggestion.title") })
2340
- ] }) }),
2341
- /* @__PURE__ */ jsx9("div", { className: "p-1.5", children: props.items.map((item, index) => /* @__PURE__ */ jsxs7(
2342
- "button",
2343
- {
2344
- type: "button",
2345
- className: `w-full rounded-md px-2.5 py-2 text-left transition-colors ${selectedIndex === index ? "bg-primary/10 text-primary" : "text-foreground hover:bg-muted"}`,
2346
- onMouseEnter: () => setSelectedIndex(index),
2347
- onMouseDown: (event) => event.preventDefault(),
2348
- onClick: () => props.command(item),
2349
- children: [
2350
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-3", children: [
2351
- /* @__PURE__ */ jsx9("span", { className: "font-mono text-sm font-semibold", children: item.name }),
2352
- /* @__PURE__ */ jsx9("span", { className: "font-mono text-xs text-muted-foreground", children: item.signature })
2353
- ] }),
2354
- /* @__PURE__ */ jsx9("div", { className: "mt-0.5 text-xs text-muted-foreground", children: t(item.descriptionKey) })
2355
- ]
2356
- },
2357
- item.name
2358
- )) })
2359
- ] });
2409
+ return /* @__PURE__ */ jsx9("div", { role: "status", className: "w-72 p-3 text-sm text-muted-foreground bg-card border border-border/50 rounded-lg shadow-lg", children: t("formulaSuggestion.noResults") });
2410
+ }
2411
+ return /* @__PURE__ */ jsxs7(
2412
+ "div",
2413
+ {
2414
+ id: listId,
2415
+ role: "listbox",
2416
+ "aria-label": t("formulaSuggestion.title"),
2417
+ "aria-activedescendant": `${listId}-option-${selectedIndex}`,
2418
+ className: "w-72 max-h-80 overflow-y-auto bg-card border border-border/50 rounded-lg shadow-lg",
2419
+ children: [
2420
+ /* @__PURE__ */ jsx9("div", { className: "px-3 py-2 border-b bg-muted/30", children: /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
2421
+ /* @__PURE__ */ jsx9(Sigma, { className: "w-4 h-4 text-primary" }),
2422
+ /* @__PURE__ */ jsx9("span", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: t("formulaSuggestion.title") })
2423
+ ] }) }),
2424
+ /* @__PURE__ */ jsx9("div", { className: "p-1.5", children: props.items.map((item, index) => /* @__PURE__ */ jsxs7(
2425
+ "button",
2426
+ {
2427
+ type: "button",
2428
+ id: `${listId}-option-${index}`,
2429
+ role: "option",
2430
+ "aria-selected": selectedIndex === index,
2431
+ tabIndex: -1,
2432
+ className: `w-full rounded-md px-2.5 py-2 text-left transition-colors ${selectedIndex === index ? "bg-primary/10 text-primary" : "text-foreground hover:bg-muted"}`,
2433
+ onMouseEnter: () => setSelectedIndex(index),
2434
+ onMouseDown: (event) => event.preventDefault(),
2435
+ onClick: () => props.command(item),
2436
+ children: [
2437
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-3", children: [
2438
+ /* @__PURE__ */ jsx9("span", { className: "font-mono text-sm font-semibold", children: item.name }),
2439
+ /* @__PURE__ */ jsx9("span", { className: "font-mono text-xs text-muted-foreground", children: item.signature })
2440
+ ] }),
2441
+ /* @__PURE__ */ jsx9("div", { className: "mt-0.5 text-xs text-muted-foreground", children: t(item.descriptionKey) })
2442
+ ]
2443
+ },
2444
+ item.name
2445
+ )) })
2446
+ ]
2447
+ }
2448
+ );
2360
2449
  });
2361
2450
  FormulaSuggestionList.displayName = "FormulaSuggestionList";
2362
2451
  function insertFormulaFunction(editor, range, item) {
@@ -3360,7 +3449,10 @@ import { Plugin as Plugin4, PluginKey as PluginKey5, TextSelection } from "@tipt
3360
3449
  import {
3361
3450
  addColumnAfter as addColumnAfterCommand,
3362
3451
  addColumnBefore as addColumnBeforeCommand,
3452
+ addRowAfter as addRowAfterCommand,
3453
+ addRowBefore as addRowBeforeCommand,
3363
3454
  deleteColumn as deleteColumnCommand,
3455
+ deleteRow as deleteRowCommand,
3364
3456
  selectedRect,
3365
3457
  tableEditing,
3366
3458
  tableNodeTypes as tableNodeTypes2
@@ -3938,19 +4030,25 @@ function createUEditorColGroup(node, widthMode) {
3938
4030
  tableWidth: totalWidth > 0 ? `${totalWidth}px` : ""
3939
4031
  };
3940
4032
  }
3941
- function runResponsiveColumnStructureCommand(state, dispatch, command, updateLayout) {
4033
+ function runResponsiveColumnStructureCommand(state, dispatch, command, updateLayout, rewriteFormulas) {
3942
4034
  const tableInfo = findTableNodeInfoFromState(state);
3943
- if (!tableInfo || normalizeTableWidthMode(tableInfo.node.attrs.widthMode) !== "responsive") {
4035
+ if (!tableInfo) {
3944
4036
  return command(state, dispatch);
3945
4037
  }
3946
- const rect = selectedRect(state);
3947
- const nextLayout = updateLayout({
4038
+ let rect;
4039
+ try {
4040
+ rect = selectedRect(state);
4041
+ } catch {
4042
+ return false;
4043
+ }
4044
+ const responsive = normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive";
4045
+ const nextLayout = responsive ? updateLayout({
3948
4046
  widthBp: getResponsiveTableWidthBp(tableInfo.node),
3949
4047
  columnRatios: getTableColumnRatios(tableInfo.node)
3950
- }, rect);
4048
+ }, rect) : null;
3951
4049
  return command(state, dispatch ? (transaction) => {
3952
- const nextTable = transaction.doc.nodeAt(tableInfo.pos);
3953
- if (nextTable?.type.name === "table" && nextLayout.columnRatios.length > 0) {
4050
+ let nextTable = transaction.doc.nodeAt(tableInfo.pos);
4051
+ if (nextTable?.type.name === "table" && nextLayout && nextLayout.columnRatios.length > 0) {
3954
4052
  const widthBp = clampResponsiveTableWidthBp(nextLayout.widthBp);
3955
4053
  const tableAlign = normalizeTableAlign(nextTable.attrs.textAlign);
3956
4054
  const offsetBp = resolveResponsiveTableOffsetBp(
@@ -3968,6 +4066,33 @@ function runResponsiveColumnStructureCommand(state, dispatch, command, updateLay
3968
4066
  getLogicalTableColumnCount(nextTable)
3969
4067
  )
3970
4068
  });
4069
+ nextTable = transaction.doc.nodeAt(tableInfo.pos);
4070
+ }
4071
+ if (nextTable?.type.name === "table") {
4072
+ const rewrittenTable = rewriteFormulas(nextTable, rect);
4073
+ if (rewrittenTable !== nextTable) {
4074
+ transaction.replaceWith(tableInfo.pos, tableInfo.pos + nextTable.nodeSize, rewrittenTable);
4075
+ }
4076
+ }
4077
+ dispatch(transaction);
4078
+ } : void 0);
4079
+ }
4080
+ function runRowStructureCommand(state, dispatch, command, rewriteFormulas) {
4081
+ const tableInfo = findTableNodeInfoFromState(state);
4082
+ if (!tableInfo) return command(state, dispatch);
4083
+ let rect;
4084
+ try {
4085
+ rect = selectedRect(state);
4086
+ } catch {
4087
+ return false;
4088
+ }
4089
+ return command(state, dispatch ? (transaction) => {
4090
+ const nextTable = transaction.doc.nodeAt(tableInfo.pos);
4091
+ if (nextTable?.type.name === "table") {
4092
+ const rewrittenTable = rewriteFormulas(nextTable, rect);
4093
+ if (rewrittenTable !== nextTable) {
4094
+ transaction.replaceWith(tableInfo.pos, tableInfo.pos + nextTable.nodeSize, rewrittenTable);
4095
+ }
3971
4096
  }
3972
4097
  dispatch(transaction);
3973
4098
  } : void 0);
@@ -4091,7 +4216,8 @@ var UEditorTable = Table2.extend({
4091
4216
  layout.columnRatios,
4092
4217
  rect.left,
4093
4218
  rect.left
4094
- )
4219
+ ),
4220
+ (tableNode, rect) => rewriteTableNodeFormulaReferencesForInsertion(tableNode, "column", rect.left)
4095
4221
  );
4096
4222
  },
4097
4223
  addColumnAfter: () => ({ state, dispatch }) => {
@@ -4104,7 +4230,8 @@ var UEditorTable = Table2.extend({
4104
4230
  layout.columnRatios,
4105
4231
  rect.right,
4106
4232
  rect.right - 1
4107
- )
4233
+ ),
4234
+ (tableNode, rect) => rewriteTableNodeFormulaReferencesForInsertion(tableNode, "column", rect.right)
4108
4235
  );
4109
4236
  },
4110
4237
  deleteColumn: () => ({ state, dispatch }) => {
@@ -4122,9 +4249,38 @@ var UEditorTable = Table2.extend({
4122
4249
  );
4123
4250
  }
4124
4251
  return nextLayout;
4125
- }
4252
+ },
4253
+ (tableNode, rect) => rewriteTableNodeFormulaReferencesForDeletion(
4254
+ tableNode,
4255
+ "column",
4256
+ rect.left,
4257
+ rect.right - rect.left
4258
+ )
4126
4259
  );
4127
4260
  },
4261
+ addRowBefore: () => ({ state, dispatch }) => runRowStructureCommand(
4262
+ state,
4263
+ dispatch,
4264
+ addRowBeforeCommand,
4265
+ (tableNode, rect) => rewriteTableNodeFormulaReferencesForInsertion(tableNode, "row", rect.top)
4266
+ ),
4267
+ addRowAfter: () => ({ state, dispatch }) => runRowStructureCommand(
4268
+ state,
4269
+ dispatch,
4270
+ addRowAfterCommand,
4271
+ (tableNode, rect) => rewriteTableNodeFormulaReferencesForInsertion(tableNode, "row", rect.bottom)
4272
+ ),
4273
+ deleteRow: () => ({ state, dispatch }) => runRowStructureCommand(
4274
+ state,
4275
+ dispatch,
4276
+ deleteRowCommand,
4277
+ (tableNode, rect) => rewriteTableNodeFormulaReferencesForDeletion(
4278
+ tableNode,
4279
+ "row",
4280
+ rect.top,
4281
+ rect.bottom - rect.top
4282
+ )
4283
+ ),
4128
4284
  setTableAlign: (tableAlign) => ({ state, dispatch }) => {
4129
4285
  const tableInfo = findTableNodeInfoFromState(state);
4130
4286
  if (!tableInfo) return false;
@@ -4275,7 +4431,7 @@ var UEditorTable = Table2.extend({
4275
4431
  var table_align_default = UEditorTable;
4276
4432
 
4277
4433
  // src/components/UEditor/CodeBlockView.tsx
4278
- import React8, { useState as useState4 } from "react";
4434
+ import React9, { useState as useState4 } from "react";
4279
4435
  import { NodeViewWrapper as NodeViewWrapper7, NodeViewContent as NodeViewContent2 } from "@tiptap/react";
4280
4436
  import { Check as Check2, Copy, ChevronDown } from "lucide-react";
4281
4437
  import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -4302,10 +4458,11 @@ var CodeBlockView = ({
4302
4458
  }) => {
4303
4459
  const [copied, setCopied] = useState4(false);
4304
4460
  const [isDropdownOpen, setIsDropdownOpen] = useState4(false);
4305
- const timeoutRef = React8.useRef(null);
4461
+ const timeoutRef = React9.useRef(null);
4462
+ const t = useSmartTranslations("UEditor");
4306
4463
  const currentLangValue = node.attrs.language || "text";
4307
4464
  const currentLangLabel = LANGUAGES.find((lang) => lang.value === currentLangValue)?.label || "Plain Text";
4308
- React8.useEffect(() => {
4465
+ React9.useEffect(() => {
4309
4466
  return () => {
4310
4467
  if (timeoutRef.current) {
4311
4468
  clearTimeout(timeoutRef.current);
@@ -4339,10 +4496,12 @@ var CodeBlockView = ({
4339
4496
  {
4340
4497
  type: "button",
4341
4498
  onClick: () => setIsDropdownOpen(!isDropdownOpen),
4342
- className: "flex items-center gap-1.5 rounded-lg border border-border bg-background px-2.5 py-1 text-xs text-muted-foreground hover:text-foreground hover:bg-muted active:scale-95 transition-all focus:outline-none",
4499
+ "aria-haspopup": "listbox",
4500
+ "aria-expanded": isDropdownOpen,
4501
+ className: "flex items-center gap-1.5 rounded-lg border border-border bg-background px-2.5 py-1 text-xs text-muted-foreground hover:text-foreground hover:bg-muted active:scale-95 transition-[color,background-color,transform] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
4343
4502
  children: [
4344
4503
  currentLangLabel,
4345
- /* @__PURE__ */ jsx11(ChevronDown, { className: "h-3 w-3" })
4504
+ /* @__PURE__ */ jsx11(ChevronDown, { "aria-hidden": "true", className: "h-3 w-3" })
4346
4505
  ]
4347
4506
  }
4348
4507
  ),
@@ -4354,10 +4513,12 @@ var CodeBlockView = ({
4354
4513
  onClick: () => setIsDropdownOpen(false)
4355
4514
  }
4356
4515
  ),
4357
- /* @__PURE__ */ jsx11("div", { className: "absolute right-0 mt-1 max-h-56 w-36 overflow-y-auto rounded-lg border border-border/50 bg-popover p-1 shadow-lg z-30 [scrollbar-width:thin] animate-in fade-in-0 zoom-in-95", children: LANGUAGES.map((lang) => /* @__PURE__ */ jsx11(
4516
+ /* @__PURE__ */ jsx11("div", { role: "listbox", "aria-label": currentLangLabel, className: "absolute right-0 mt-1 max-h-56 w-36 overflow-y-auto rounded-lg border border-border/50 bg-popover p-1 shadow-lg z-30 [scrollbar-width:thin] animate-in fade-in-0 zoom-in-95 motion-reduce:animate-none", children: LANGUAGES.map((lang) => /* @__PURE__ */ jsx11(
4358
4517
  "button",
4359
4518
  {
4360
4519
  type: "button",
4520
+ role: "option",
4521
+ "aria-selected": currentLangValue === lang.value,
4361
4522
  onClick: () => {
4362
4523
  updateAttributes({ language: lang.value });
4363
4524
  setIsDropdownOpen(false);
@@ -4377,9 +4538,10 @@ var CodeBlockView = ({
4377
4538
  {
4378
4539
  type: "button",
4379
4540
  onClick: handleCopy,
4380
- className: "flex h-7 w-7 items-center justify-center rounded-lg border border-border bg-background text-muted-foreground hover:text-foreground hover:bg-muted active:scale-95 transition-all focus:outline-none",
4381
- title: "Copy code",
4382
- children: copied ? /* @__PURE__ */ jsx11(Check2, { className: "h-3.5 w-3.5 text-success animate-in fade-in-0 zoom-in-75" }) : /* @__PURE__ */ jsx11(Copy, { className: "h-3.5 w-3.5" })
4541
+ "aria-label": t("menubar.copy"),
4542
+ className: "flex h-7 w-7 items-center justify-center rounded-lg border border-border bg-background text-muted-foreground hover:text-foreground hover:bg-muted active:scale-95 transition-[color,background-color,transform] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
4543
+ title: t("menubar.copy"),
4544
+ children: copied ? /* @__PURE__ */ jsx11(Check2, { "aria-hidden": "true", className: "h-3.5 w-3.5 text-success animate-in fade-in-0 zoom-in-75 motion-reduce:animate-none" }) : /* @__PURE__ */ jsx11(Copy, { "aria-hidden": "true", className: "h-3.5 w-3.5" })
4383
4545
  }
4384
4546
  )
4385
4547
  ]
@@ -4584,527 +4746,6 @@ import { Plugin as Plugin6 } from "@tiptap/pm/state";
4584
4746
  // src/components/UEditor/table-formula-commands.ts
4585
4747
  import { TextSelection as TextSelection2 } from "@tiptap/pm/state";
4586
4748
  import { selectedRect as selectedRect2, setCellAttr, TableMap as TableMap2 } from "@tiptap/pm/tables";
4587
-
4588
- // src/components/UEditor/table-formula.ts
4589
- var CELL_ADDRESS_RE = /^([A-Z]+)([1-9]\d*)$/i;
4590
- var CELL_RANGE_RE = /^([A-Z]+[1-9]\d*):([A-Z]+[1-9]\d*)$/i;
4591
- var SUPPORTED_FUNCTIONS = /* @__PURE__ */ new Set(["SUM", "AVG", "MIN", "MAX", "COUNT"]);
4592
- function columnNameToIndex(columnName) {
4593
- const normalized = columnName.trim().toUpperCase();
4594
- if (!/^[A-Z]+$/.test(normalized)) {
4595
- return -1;
4596
- }
4597
- let index = 0;
4598
- for (const char of normalized) {
4599
- index = index * 26 + char.charCodeAt(0) - 64;
4600
- }
4601
- return index - 1;
4602
- }
4603
- function indexToColumnName(index) {
4604
- if (!Number.isInteger(index) || index < 0) {
4605
- return "";
4606
- }
4607
- let value = index + 1;
4608
- let name = "";
4609
- while (value > 0) {
4610
- const remainder = (value - 1) % 26;
4611
- name = String.fromCharCode(65 + remainder) + name;
4612
- value = Math.floor((value - 1) / 26);
4613
- }
4614
- return name;
4615
- }
4616
- function parseTableCellAddress(input) {
4617
- const match = input.trim().match(CELL_ADDRESS_RE);
4618
- if (!match) {
4619
- return null;
4620
- }
4621
- const column = columnNameToIndex(match[1] ?? "");
4622
- const row = Number.parseInt(match[2] ?? "", 10) - 1;
4623
- if (column < 0 || row < 0) {
4624
- return null;
4625
- }
4626
- return {
4627
- column,
4628
- row,
4629
- label: `${indexToColumnName(column)}${row + 1}`
4630
- };
4631
- }
4632
- function parseTableCellRange(input) {
4633
- const match = input.trim().match(CELL_RANGE_RE);
4634
- if (!match) {
4635
- return null;
4636
- }
4637
- const from = parseTableCellAddress(match[1] ?? "");
4638
- const to = parseTableCellAddress(match[2] ?? "");
4639
- if (!from || !to) {
4640
- return null;
4641
- }
4642
- return { from, to };
4643
- }
4644
- function getTableCellRangeLabels(range) {
4645
- const startColumn = Math.min(range.from.column, range.to.column);
4646
- const endColumn = Math.max(range.from.column, range.to.column);
4647
- const startRow = Math.min(range.from.row, range.to.row);
4648
- const endRow = Math.max(range.from.row, range.to.row);
4649
- const labels = [];
4650
- for (let row = startRow; row <= endRow; row += 1) {
4651
- for (let column = startColumn; column <= endColumn; column += 1) {
4652
- labels.push(`${indexToColumnName(column)}${row + 1}`);
4653
- }
4654
- }
4655
- return labels;
4656
- }
4657
- function normalizeTableFormula(formula) {
4658
- return formula.trim().replace(/^=/, "").trim();
4659
- }
4660
- function isDraftTableFormula(formula) {
4661
- const normalized = normalizeTableFormula(formula);
4662
- if (!normalized) return true;
4663
- let depth = 0;
4664
- for (const char of normalized) {
4665
- if (char === "(") depth += 1;
4666
- if (char === ")") depth -= 1;
4667
- if (depth < 0) return false;
4668
- }
4669
- if (depth > 0) return true;
4670
- if (/^[A-Z]+\(\s*\)$/i.test(normalized)) return true;
4671
- if (/[+\-*/,(]\s*$/.test(normalized)) return true;
4672
- return false;
4673
- }
4674
- function formatFormulaError(error) {
4675
- return `#${error.toUpperCase()}`;
4676
- }
4677
- function normalizeTableNumberFormat(format) {
4678
- return format === "text" || format === "number" || format === "currency" || format === "percent" || format === "date" ? format : "text";
4679
- }
4680
- function formatTableFormulaDisplayValue(value, numberFormat) {
4681
- const stringValue = String(value);
4682
- if (stringValue.startsWith("#")) {
4683
- return stringValue;
4684
- }
4685
- const normalizedFormat = normalizeTableNumberFormat(numberFormat);
4686
- if (normalizedFormat === "text") {
4687
- return stringValue;
4688
- }
4689
- const numericValue = typeof value === "number" ? value : Number.parseFloat(stringValue.replace(/,/g, ""));
4690
- if (!Number.isFinite(numericValue)) {
4691
- return stringValue;
4692
- }
4693
- if (normalizedFormat === "number") {
4694
- return new Intl.NumberFormat("en-US", { maximumFractionDigits: 6 }).format(numericValue);
4695
- }
4696
- if (normalizedFormat === "currency") {
4697
- return new Intl.NumberFormat("en-US", {
4698
- style: "currency",
4699
- currency: "USD",
4700
- maximumFractionDigits: 2
4701
- }).format(numericValue);
4702
- }
4703
- if (normalizedFormat === "percent") {
4704
- return new Intl.NumberFormat("en-US", {
4705
- style: "percent",
4706
- maximumFractionDigits: 2
4707
- }).format(numericValue);
4708
- }
4709
- const excelEpochMs = Date.UTC(1899, 11, 30);
4710
- const date = new Date(excelEpochMs + numericValue * 24 * 60 * 60 * 1e3);
4711
- if (Number.isNaN(date.getTime())) {
4712
- return stringValue;
4713
- }
4714
- return new Intl.DateTimeFormat("en-US", {
4715
- year: "numeric",
4716
- month: "2-digit",
4717
- day: "2-digit",
4718
- timeZone: "UTC"
4719
- }).format(date);
4720
- }
4721
- function getTableFormulaReferences(formula) {
4722
- const normalized = normalizeTableFormula(formula);
4723
- if (!normalized) return [];
4724
- const tokens = tokenizeFormula(normalized);
4725
- if (!tokens) return [];
4726
- const references = /* @__PURE__ */ new Set();
4727
- for (const token of tokens) {
4728
- if (token.type === "cell") {
4729
- references.add(token.value);
4730
- } else if (token.type === "range") {
4731
- const range = parseTableCellRange(token.value);
4732
- if (!range) continue;
4733
- for (const label of getTableCellRangeLabels(range)) {
4734
- references.add(label);
4735
- }
4736
- }
4737
- }
4738
- return Array.from(references);
4739
- }
4740
- function buildTableFormulaDependencyGraph(cells) {
4741
- const dependencies = /* @__PURE__ */ new Map();
4742
- const dependents = /* @__PURE__ */ new Map();
4743
- const formulas = /* @__PURE__ */ new Map();
4744
- for (const cell of cells) {
4745
- const label = cell.label.toUpperCase();
4746
- formulas.set(label, cell.formula);
4747
- dependencies.set(label, new Set(getTableFormulaReferences(cell.formula)));
4748
- if (!dependents.has(label)) {
4749
- dependents.set(label, /* @__PURE__ */ new Set());
4750
- }
4751
- }
4752
- for (const [label, refs] of dependencies) {
4753
- for (const ref of refs) {
4754
- if (!dependents.has(ref)) {
4755
- dependents.set(ref, /* @__PURE__ */ new Set());
4756
- }
4757
- dependents.get(ref)?.add(label);
4758
- }
4759
- }
4760
- return { dependencies, dependents, formulas };
4761
- }
4762
- function getTableFormulaCircularReferences(graph) {
4763
- const circular = /* @__PURE__ */ new Set();
4764
- const visited = /* @__PURE__ */ new Set();
4765
- const finishOrder = [];
4766
- for (const startLabel of graph.formulas.keys()) {
4767
- if (visited.has(startLabel)) continue;
4768
- visited.add(startLabel);
4769
- const stack = [
4770
- {
4771
- label: startLabel,
4772
- references: Array.from(graph.dependencies.get(startLabel) ?? []).filter((ref) => graph.formulas.has(ref)),
4773
- nextIndex: 0
4774
- }
4775
- ];
4776
- while (stack.length > 0) {
4777
- const frame = stack[stack.length - 1];
4778
- if (!frame) break;
4779
- const reference = frame.references[frame.nextIndex];
4780
- if (reference) {
4781
- frame.nextIndex += 1;
4782
- if (!visited.has(reference)) {
4783
- visited.add(reference);
4784
- stack.push({
4785
- label: reference,
4786
- references: Array.from(graph.dependencies.get(reference) ?? []).filter((ref) => graph.formulas.has(ref)),
4787
- nextIndex: 0
4788
- });
4789
- }
4790
- continue;
4791
- }
4792
- stack.pop();
4793
- finishOrder.push(frame.label);
4794
- }
4795
- }
4796
- const assigned = /* @__PURE__ */ new Set();
4797
- for (let index = finishOrder.length - 1; index >= 0; index -= 1) {
4798
- const startLabel = finishOrder[index];
4799
- if (!startLabel || assigned.has(startLabel)) continue;
4800
- const component = [];
4801
- const stack = [startLabel];
4802
- assigned.add(startLabel);
4803
- while (stack.length > 0) {
4804
- const label = stack.pop();
4805
- if (!label) continue;
4806
- component.push(label);
4807
- for (const dependent of graph.dependents.get(label) ?? []) {
4808
- if (!graph.formulas.has(dependent) || assigned.has(dependent)) continue;
4809
- assigned.add(dependent);
4810
- stack.push(dependent);
4811
- }
4812
- }
4813
- if (component.length > 1) {
4814
- for (const label of component) circular.add(label);
4815
- } else {
4816
- const label = component[0];
4817
- if (label && graph.dependencies.get(label)?.has(label)) circular.add(label);
4818
- }
4819
- }
4820
- return circular;
4821
- }
4822
- function getTableFormulaRecalculationOrder(graph) {
4823
- const circular = getTableFormulaCircularReferences(graph);
4824
- const visiting = /* @__PURE__ */ new Set();
4825
- const visited = /* @__PURE__ */ new Set();
4826
- const order = [];
4827
- for (const startLabel of graph.formulas.keys()) {
4828
- if (visited.has(startLabel) || circular.has(startLabel)) continue;
4829
- const stack = [];
4830
- const push = (label) => {
4831
- visiting.add(label);
4832
- stack.push({
4833
- label,
4834
- references: Array.from(graph.dependencies.get(label) ?? []).filter((ref) => graph.formulas.has(ref) && !circular.has(ref)),
4835
- nextIndex: 0
4836
- });
4837
- };
4838
- push(startLabel);
4839
- while (stack.length > 0) {
4840
- const frame = stack[stack.length - 1];
4841
- if (!frame) break;
4842
- const reference = frame.references[frame.nextIndex];
4843
- if (reference) {
4844
- frame.nextIndex += 1;
4845
- if (!visited.has(reference) && !visiting.has(reference)) {
4846
- push(reference);
4847
- }
4848
- continue;
4849
- }
4850
- stack.pop();
4851
- visiting.delete(frame.label);
4852
- visited.add(frame.label);
4853
- order.push(frame.label);
4854
- }
4855
- }
4856
- return { order, circular };
4857
- }
4858
- function getAffectedTableFormulaLabels(graph, changedLabels) {
4859
- const affected = /* @__PURE__ */ new Set();
4860
- const queue = Array.from(changedLabels, (label) => label.toUpperCase());
4861
- for (const label of queue) {
4862
- if (graph.formulas.has(label)) {
4863
- affected.add(label);
4864
- }
4865
- }
4866
- for (let index = 0; index < queue.length; index += 1) {
4867
- const label = queue[index];
4868
- if (!label) continue;
4869
- for (const dependent of graph.dependents.get(label) ?? []) {
4870
- if (affected.has(dependent)) continue;
4871
- affected.add(dependent);
4872
- queue.push(dependent);
4873
- }
4874
- }
4875
- return affected;
4876
- }
4877
- function evaluateBasicTableFormula(formula, getCellValue) {
4878
- const normalized = normalizeTableFormula(formula);
4879
- if (!normalized) {
4880
- return { value: null, error: "empty" };
4881
- }
4882
- const tokens = tokenizeFormula(normalized);
4883
- if (!tokens) {
4884
- return { value: null, error: "invalid-formula" };
4885
- }
4886
- const parser = new FormulaParser(tokens, getCellValue);
4887
- const result = parser.parseExpression();
4888
- if (result.error) {
4889
- return result;
4890
- }
4891
- if (!parser.isComplete()) {
4892
- return { value: null, error: "invalid-formula" };
4893
- }
4894
- return result;
4895
- }
4896
- function tokenizeFormula(formula) {
4897
- const tokens = [];
4898
- let index = 0;
4899
- while (index < formula.length) {
4900
- const char = formula[index];
4901
- if (!char) break;
4902
- if (/\s/.test(char)) {
4903
- index += 1;
4904
- continue;
4905
- }
4906
- if (char === "," || char === "+" || char === "-" || char === "*" || char === "/" || char === "(" || char === ")") {
4907
- if (char === ",") tokens.push({ type: "comma", value: char });
4908
- else if (char === "(" || char === ")") tokens.push({ type: "paren", value: char });
4909
- else tokens.push({ type: "operator", value: char });
4910
- index += 1;
4911
- continue;
4912
- }
4913
- const numberMatch = formula.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)/);
4914
- if (numberMatch?.[0]) {
4915
- const value = Number.parseFloat(numberMatch[0]);
4916
- if (!Number.isFinite(value)) return null;
4917
- tokens.push({ type: "number", value });
4918
- index += numberMatch[0].length;
4919
- continue;
4920
- }
4921
- const identifierMatch = formula.slice(index).match(/^[A-Z]+[1-9]\d*(?::[A-Z]+[1-9]\d*)?|^[A-Z]+/i);
4922
- if (identifierMatch?.[0]) {
4923
- const value = identifierMatch[0].toUpperCase();
4924
- if (CELL_RANGE_RE.test(value)) tokens.push({ type: "range", value });
4925
- else if (parseTableCellAddress(value)) tokens.push({ type: "cell", value });
4926
- else if (SUPPORTED_FUNCTIONS.has(value)) tokens.push({ type: "function", value });
4927
- else return null;
4928
- index += identifierMatch[0].length;
4929
- continue;
4930
- }
4931
- return null;
4932
- }
4933
- return tokens;
4934
- }
4935
- function toFiniteFormulaResult(value) {
4936
- return Number.isFinite(value) ? { value, error: null } : { value: null, error: "invalid-formula" };
4937
- }
4938
- function parseTableCellNumericValue(value) {
4939
- if (typeof value === "number") {
4940
- return Number.isFinite(value) ? value : null;
4941
- }
4942
- let normalized = String(value ?? "").trim();
4943
- if (!normalized || normalized.startsWith("#")) return null;
4944
- const isPercent = normalized.endsWith("%");
4945
- if (isPercent) normalized = normalized.slice(0, -1).trim();
4946
- const currencyMatch = normalized.match(/^([+-]?)\$(.+)$/);
4947
- if (currencyMatch) {
4948
- normalized = `${currencyMatch[1] ?? ""}${currencyMatch[2] ?? ""}`.trim();
4949
- }
4950
- const groupedNumber = /^[+-]?\d{1,3}(?:,\d{3})+(?:\.\d*)?(?:[eE][+-]?\d+)?$/;
4951
- const plainNumber = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
4952
- if (!groupedNumber.test(normalized) && !plainNumber.test(normalized)) return null;
4953
- const parsed = Number(normalized.replace(/,/g, ""));
4954
- if (!Number.isFinite(parsed)) return null;
4955
- return isPercent ? parsed / 100 : parsed;
4956
- }
4957
- var FormulaParser = class {
4958
- constructor(tokens, getCellValue) {
4959
- this.tokens = tokens;
4960
- this.getCellValue = getCellValue;
4961
- this.index = 0;
4962
- }
4963
- isComplete() {
4964
- return this.index >= this.tokens.length;
4965
- }
4966
- parseExpression() {
4967
- let left = this.parseTerm();
4968
- while (!left.error) {
4969
- const operator = this.peekOperator(["+", "-"]);
4970
- if (!operator) break;
4971
- this.index += 1;
4972
- const right = this.parseTerm();
4973
- if (right.error) return right;
4974
- left = toFiniteFormulaResult(operator.value === "+" ? left.value + right.value : left.value - right.value);
4975
- }
4976
- return left;
4977
- }
4978
- parseTerm() {
4979
- let left = this.parseFactor();
4980
- while (!left.error) {
4981
- const operator = this.peekOperator(["*", "/"]);
4982
- if (!operator) break;
4983
- this.index += 1;
4984
- const right = this.parseFactor();
4985
- if (right.error) return right;
4986
- if (operator.value === "/" && right.value === 0) {
4987
- return { value: null, error: "division-by-zero" };
4988
- }
4989
- left = toFiniteFormulaResult(operator.value === "*" ? left.value * right.value : left.value / right.value);
4990
- }
4991
- return left;
4992
- }
4993
- parseFactor() {
4994
- const token = this.tokens[this.index];
4995
- if (!token) {
4996
- return { value: null, error: "invalid-formula" };
4997
- }
4998
- if (token.type === "operator" && (token.value === "-" || token.value === "+")) {
4999
- this.index += 1;
5000
- const value = this.parseFactor();
5001
- if (value.error) return value;
5002
- return toFiniteFormulaResult(token.value === "-" ? -value.value : value.value);
5003
- }
5004
- if (token.type === "number") {
5005
- this.index += 1;
5006
- return { value: token.value, error: null };
5007
- }
5008
- if (token.type === "cell") {
5009
- this.index += 1;
5010
- return this.readCellNumber(token.value);
5011
- }
5012
- if (token.type === "function") {
5013
- return this.parseFunction(token.value);
5014
- }
5015
- if (token.type === "paren" && token.value === "(") {
5016
- this.index += 1;
5017
- const value = this.parseExpression();
5018
- if (value.error) return value;
5019
- if (!this.consumeParen(")")) {
5020
- return { value: null, error: "invalid-formula" };
5021
- }
5022
- return value;
5023
- }
5024
- return { value: null, error: "invalid-formula" };
5025
- }
5026
- parseFunction(name) {
5027
- this.index += 1;
5028
- if (!this.consumeParen("(")) {
5029
- return { value: null, error: "invalid-formula" };
5030
- }
5031
- const values = [];
5032
- while (true) {
5033
- const token = this.tokens[this.index];
5034
- if (!token) {
5035
- return { value: null, error: "invalid-formula" };
5036
- }
5037
- if (token.type === "range") {
5038
- this.index += 1;
5039
- const range = parseTableCellRange(token.value);
5040
- if (!range) return { value: null, error: "invalid-reference" };
5041
- for (const label of getTableCellRangeLabels(range)) {
5042
- if (name === "COUNT") {
5043
- const cellValue2 = this.readOptionalCellNumber(label);
5044
- if (cellValue2 != null) values.push(cellValue2);
5045
- continue;
5046
- }
5047
- const cellValue = this.readOptionalCellNumber(label);
5048
- if (cellValue != null) values.push(cellValue);
5049
- }
5050
- } else if (token.type === "cell") {
5051
- this.index += 1;
5052
- const cellValue = this.readOptionalCellNumber(token.value);
5053
- if (cellValue != null) values.push(cellValue);
5054
- } else {
5055
- const value = this.parseExpression();
5056
- if (value.error) return value;
5057
- values.push(value.value);
5058
- }
5059
- if (this.consumeComma()) {
5060
- continue;
5061
- }
5062
- if (this.consumeParen(")")) {
5063
- break;
5064
- }
5065
- return { value: null, error: "invalid-formula" };
5066
- }
5067
- if (name === "SUM") return toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0));
5068
- if (name === "AVG") {
5069
- return values.length > 0 ? toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0) / values.length) : { value: null, error: "division-by-zero" };
5070
- }
5071
- if (name === "MIN") return toFiniteFormulaResult(values.length > 0 ? Math.min(...values) : 0);
5072
- if (name === "MAX") return toFiniteFormulaResult(values.length > 0 ? Math.max(...values) : 0);
5073
- if (name === "COUNT") return { value: values.length, error: null };
5074
- return { value: null, error: "invalid-formula" };
5075
- }
5076
- readCellNumber(label) {
5077
- const parsed = parseTableCellNumericValue(this.getCellValue(label));
5078
- if (parsed == null) {
5079
- return { value: null, error: "invalid-reference" };
5080
- }
5081
- return { value: parsed, error: null };
5082
- }
5083
- readOptionalCellNumber(label) {
5084
- return parseTableCellNumericValue(this.getCellValue(label));
5085
- }
5086
- peekOperator(operators) {
5087
- const token = this.tokens[this.index];
5088
- return token?.type === "operator" && operators.includes(token.value) ? token : null;
5089
- }
5090
- consumeComma() {
5091
- if (this.tokens[this.index]?.type !== "comma") {
5092
- return false;
5093
- }
5094
- this.index += 1;
5095
- return true;
5096
- }
5097
- consumeParen(value) {
5098
- const token = this.tokens[this.index];
5099
- if (token?.type !== "paren" || token.value !== value) {
5100
- return false;
5101
- }
5102
- this.index += 1;
5103
- return true;
5104
- }
5105
- };
5106
-
5107
- // src/components/UEditor/table-formula-commands.ts
5108
4749
  var UEDITOR_TABLE_FORMULA_RECALCULATE_META = "ueditorTableFormulaRecalculate";
5109
4750
  var TABLE_FORMULA_PLAN_CACHE_LIMIT = 50;
5110
4751
  var tableFormulaPlanCache = /* @__PURE__ */ new Map();
@@ -5984,6 +5625,7 @@ function buildUEditorExtensions({
5984
5625
  maxImageFileSize,
5985
5626
  allowedImageMimeTypes,
5986
5627
  fallbackToDataUrl,
5628
+ onImageUploadError,
5987
5629
  editable = true,
5988
5630
  fetchMetadata
5989
5631
  }) {
@@ -6051,7 +5693,8 @@ function buildUEditorExtensions({
6051
5693
  insertMode: imageInsertMode,
6052
5694
  ...maxImageFileSize !== void 0 ? { maxFileSize: maxImageFileSize } : {},
6053
5695
  ...allowedImageMimeTypes ? { allowedMimeTypes: allowedImageMimeTypes } : {},
6054
- ...fallbackToDataUrl !== void 0 ? { fallbackToDataUrl } : {}
5696
+ ...fallbackToDataUrl !== void 0 ? { fallbackToDataUrl } : {},
5697
+ onError: onImageUploadError
6055
5698
  }),
6056
5699
  TextStyle,
6057
5700
  font_family_default,
@@ -7494,6 +7137,8 @@ var CustomBubbleMenu = ({
7494
7137
  editor.on("transaction", schedulePositionUpdate);
7495
7138
  editor.on("focus", schedulePositionUpdate);
7496
7139
  editor.on("blur", handleBlur);
7140
+ editorWindow?.addEventListener("resize", schedulePositionUpdate);
7141
+ editorWindow?.addEventListener("scroll", schedulePositionUpdate, true);
7497
7142
  updatePositionRef.current = updatePosition;
7498
7143
  schedulePositionUpdate();
7499
7144
  return () => {
@@ -7504,6 +7149,8 @@ var CustomBubbleMenu = ({
7504
7149
  editor.off("transaction", schedulePositionUpdate);
7505
7150
  editor.off("focus", schedulePositionUpdate);
7506
7151
  editor.off("blur", handleBlur);
7152
+ editorWindow?.removeEventListener("resize", schedulePositionUpdate);
7153
+ editorWindow?.removeEventListener("scroll", schedulePositionUpdate, true);
7507
7154
  };
7508
7155
  }, [editor, editorDocument, editorWindow]);
7509
7156
  useEffect5(() => {
@@ -7637,12 +7284,16 @@ var CustomFloatingMenu = ({ editor }) => {
7637
7284
  editor.on("transaction", schedulePositionUpdate);
7638
7285
  editor.on("focus", schedulePositionUpdate);
7639
7286
  editor.on("blur", handleBlur);
7287
+ editorWindow?.addEventListener("resize", schedulePositionUpdate);
7288
+ editorWindow?.addEventListener("scroll", schedulePositionUpdate, true);
7640
7289
  schedulePositionUpdate();
7641
7290
  return () => {
7642
7291
  if (animationFrameId !== null) editorWindow?.cancelAnimationFrame(animationFrameId);
7643
7292
  editor.off("transaction", schedulePositionUpdate);
7644
7293
  editor.off("focus", schedulePositionUpdate);
7645
7294
  editor.off("blur", handleBlur);
7295
+ editorWindow?.removeEventListener("resize", schedulePositionUpdate);
7296
+ editorWindow?.removeEventListener("scroll", schedulePositionUpdate, true);
7646
7297
  };
7647
7298
  }, [editor, editorWindow]);
7648
7299
  if (!isVisible || !portalContainer) return null;
@@ -7701,7 +7352,7 @@ var CharacterCountDisplay = ({ editor, maxCharacters }) => {
7701
7352
  };
7702
7353
 
7703
7354
  // src/components/UEditor/table-controls.tsx
7704
- import React10 from "react";
7355
+ import React11 from "react";
7705
7356
 
7706
7357
  // node_modules/prosemirror-model/dist/index.js
7707
7358
  function findDiffStart(a, b, pos) {
@@ -12357,6 +12008,8 @@ function TableAddRails({
12357
12008
  layout,
12358
12009
  onStartAddColumn,
12359
12010
  onStartAddRow,
12011
+ onAddColumn,
12012
+ onAddRow,
12360
12013
  quickAddColumnLabel,
12361
12014
  quickAddRowLabel
12362
12015
  }) {
@@ -12379,17 +12032,16 @@ function TableAddRails({
12379
12032
  type: "button",
12380
12033
  "data-table-control": "add-column",
12381
12034
  "aria-label": quickAddColumnLabel,
12382
- onMouseDown: (event) => {
12383
- event.preventDefault();
12384
- event.stopPropagation();
12385
- if (!canExpandTable) return;
12386
- onStartAddColumn();
12035
+ tabIndex: showColumnRail ? 0 : -1,
12036
+ onPointerDown: onStartAddColumn,
12037
+ onClick: (event) => {
12038
+ if (event.detail === 0 && canExpandTable) onAddColumn();
12387
12039
  },
12388
12040
  disabled: !canExpandTable,
12389
12041
  className: cn(
12390
12042
  "absolute z-30 inline-flex items-center justify-center rounded-md",
12391
12043
  "border border-border/70 bg-muted/40 text-muted-foreground shadow-sm backdrop-blur",
12392
- "transition-[opacity,transform,colors] duration-200 delay-100 ease-out cursor-pointer hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
12044
+ "transition-[opacity,transform,color,border-color,background-color] duration-200 delay-100 ease-out motion-reduce:transition-none cursor-pointer hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
12393
12045
  ),
12394
12046
  style: {
12395
12047
  top: columnRailTop,
@@ -12416,17 +12068,16 @@ function TableAddRails({
12416
12068
  type: "button",
12417
12069
  "data-table-control": "add-row",
12418
12070
  "aria-label": quickAddRowLabel,
12419
- onMouseDown: (event) => {
12420
- event.preventDefault();
12421
- event.stopPropagation();
12422
- if (!canExpandTable) return;
12423
- onStartAddRow();
12071
+ tabIndex: showRowRail ? 0 : -1,
12072
+ onPointerDown: onStartAddRow,
12073
+ onClick: (event) => {
12074
+ if (event.detail === 0 && canExpandTable) onAddRow();
12424
12075
  },
12425
12076
  disabled: !canExpandTable,
12426
12077
  className: cn(
12427
12078
  "absolute z-30 inline-flex items-center justify-center rounded-md",
12428
12079
  "border border-border/70 bg-muted/40 text-muted-foreground shadow-sm backdrop-blur",
12429
- "transition-[opacity,transform,colors] duration-200 delay-100 ease-out cursor-pointer hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
12080
+ "transition-[opacity,transform,color,border-color,background-color] duration-200 delay-100 ease-out motion-reduce:transition-none cursor-pointer hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
12430
12081
  ),
12431
12082
  style: {
12432
12083
  top: rowRailTop,
@@ -12519,6 +12170,7 @@ function TableRowHandles({
12519
12170
  getMenuItems,
12520
12171
  hoverRowHandleIndex,
12521
12172
  onOpenMenuChange,
12173
+ onMove,
12522
12174
  onStartDrag,
12523
12175
  openMenuKey,
12524
12176
  rowDragLabel,
@@ -12559,13 +12211,16 @@ function TableRowHandles({
12559
12211
  {
12560
12212
  type: "button",
12561
12213
  "aria-label": `${rowDragLabel} ${rowHandle.index + 1}`,
12562
- onMouseDown: (event) => {
12214
+ tabIndex: visible ? 0 : -1,
12215
+ onPointerDown: (event) => onStartDrag(event, rowHandle),
12216
+ onKeyDown: (event) => {
12217
+ if (!event.altKey || event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
12563
12218
  event.preventDefault();
12564
12219
  event.stopPropagation();
12565
- onStartDrag(rowHandle);
12220
+ onMove(rowHandle, event.key === "ArrowUp" ? -1 : 1);
12566
12221
  },
12567
12222
  className: cn(
12568
- "inline-flex h-6 w-6 items-center justify-center rounded-full transition-[opacity,transform,colors,border,background-color] duration-200 delay-100 ease-out",
12223
+ "inline-flex h-6 w-6 items-center justify-center rounded-full transition-[opacity,transform,color,border-color,background-color] duration-200 delay-100 ease-out motion-reduce:transition-none",
12569
12224
  visible ? "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur hover:bg-accent hover:text-foreground cursor-grab active:cursor-grabbing" : "border-transparent bg-transparent cursor-pointer"
12570
12225
  ),
12571
12226
  style: {
@@ -12576,7 +12231,7 @@ function TableRowHandles({
12576
12231
  // selection near the first character of a table cell.
12577
12232
  pointerEvents: visible ? "auto" : "none"
12578
12233
  },
12579
- children: visible ? /* @__PURE__ */ jsx16(GripVertical, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx16("div", { className: "h-3 w-1 rounded-full bg-muted-foreground/50 hover:bg-muted-foreground" })
12234
+ children: visible ? /* @__PURE__ */ jsx16(GripVertical, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx16("div", { className: "h-3 w-1 rounded-full bg-muted-foreground/50 hover:bg-muted-foreground" })
12580
12235
  }
12581
12236
  )
12582
12237
  }
@@ -12597,6 +12252,7 @@ function TableColumnHandles({
12597
12252
  getMenuItems,
12598
12253
  hoverColumnHandleIndex,
12599
12254
  onOpenMenuChange,
12255
+ onMove,
12600
12256
  onStartDrag,
12601
12257
  openMenuKey
12602
12258
  }) {
@@ -12634,13 +12290,16 @@ function TableColumnHandles({
12634
12290
  {
12635
12291
  type: "button",
12636
12292
  "aria-label": `${columnDragLabel} ${columnHandle.index + 1}`,
12637
- onMouseDown: (event) => {
12293
+ tabIndex: visible ? 0 : -1,
12294
+ onPointerDown: (event) => onStartDrag(event, columnHandle),
12295
+ onKeyDown: (event) => {
12296
+ if (!event.altKey || event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
12638
12297
  event.preventDefault();
12639
12298
  event.stopPropagation();
12640
- onStartDrag(columnHandle);
12299
+ onMove(columnHandle, event.key === "ArrowLeft" ? -1 : 1);
12641
12300
  },
12642
12301
  className: cn(
12643
- "inline-flex h-6 w-6 items-center justify-center rounded-full transition-[opacity,transform,colors,border,background-color] duration-200 delay-100 ease-out",
12302
+ "inline-flex h-6 w-6 items-center justify-center rounded-full transition-[opacity,transform,color,border-color,background-color] duration-200 delay-100 ease-out motion-reduce:transition-none",
12644
12303
  visible ? "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur hover:bg-accent hover:text-foreground cursor-grab active:cursor-grabbing" : "border-transparent bg-transparent cursor-pointer"
12645
12304
  ),
12646
12305
  style: {
@@ -12648,7 +12307,7 @@ function TableColumnHandles({
12648
12307
  transform: isShown ? "scale(1)" : `scale(${IDLE_HANDLE_SCALE})`,
12649
12308
  pointerEvents: visible ? "auto" : "none"
12650
12309
  },
12651
- children: visible ? /* @__PURE__ */ jsx16(GripHorizontal, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx16("div", { className: "h-1 w-3 rounded-full bg-muted-foreground/50 hover:bg-muted-foreground" })
12310
+ children: visible ? /* @__PURE__ */ jsx16(GripHorizontal, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx16("div", { className: "h-1 w-3 rounded-full bg-muted-foreground/50 hover:bg-muted-foreground" })
12652
12311
  }
12653
12312
  )
12654
12313
  }
@@ -12676,12 +12335,25 @@ var EDGE_HANDLE_CLASS = cn(
12676
12335
  "border-transparent transition-colors duration-150 hover:border-primary",
12677
12336
  "focus-visible:border-primary focus-visible:outline-none"
12678
12337
  );
12338
+ function formatTableResizeDimensionLabel({
12339
+ width,
12340
+ height,
12341
+ widthBp,
12342
+ widthMode,
12343
+ viewportWidth
12344
+ }) {
12345
+ const widthLabel = widthMode === "responsive" ? formatBasisPointsAsPercentage(
12346
+ widthBp ?? Math.max(1, width) / Math.max(1, viewportWidth) * TABLE_WIDTH_BASIS_POINTS
12347
+ ) : `${Math.round(width)}px`;
12348
+ return `${widthLabel} \xD7 ${Math.round(height)}px`;
12349
+ }
12679
12350
  function TableResizeHandles({
12680
12351
  active,
12681
12352
  ctrlHint,
12682
12353
  frameRef,
12683
12354
  layout,
12684
12355
  onFitWidth,
12356
+ onKeyboardResize,
12685
12357
  onStartResize,
12686
12358
  resizeBothLabel
12687
12359
  }) {
@@ -12714,7 +12386,8 @@ function TableResizeHandles({
12714
12386
  EDGE_HANDLE_CLASS,
12715
12387
  "left-0 border-l-2"
12716
12388
  ),
12717
- onPointerDown: (event) => onStartResize(event, "left")
12389
+ onPointerDown: (event) => onStartResize(event, "left"),
12390
+ onKeyDown: (event) => onKeyboardResize(event, "left")
12718
12391
  }
12719
12392
  ),
12720
12393
  /* @__PURE__ */ jsx17(
@@ -12728,7 +12401,8 @@ function TableResizeHandles({
12728
12401
  EDGE_HANDLE_CLASS,
12729
12402
  "right-0 border-r-2"
12730
12403
  ),
12731
- onPointerDown: (event) => onStartResize(event, "right")
12404
+ onPointerDown: (event) => onStartResize(event, "right"),
12405
+ onKeyDown: (event) => onKeyboardResize(event, "right")
12732
12406
  }
12733
12407
  )
12734
12408
  ] }) : null,
@@ -12745,7 +12419,11 @@ function TableResizeHandles({
12745
12419
  active && "bg-primary text-primary-foreground shadow-md"
12746
12420
  ),
12747
12421
  onDoubleClick: onFitWidth,
12422
+ onClick: (event) => {
12423
+ if (event.detail === 0) onFitWidth(event);
12424
+ },
12748
12425
  onPointerDown: (event) => onStartResize(event, "both"),
12426
+ onKeyDown: (event) => onKeyboardResize(event, "both"),
12749
12427
  children: /* @__PURE__ */ jsx17(MoveDiagonal2, { "aria-hidden": "true", className: "h-3.5 w-3.5", strokeWidth: 2.25 })
12750
12428
  }
12751
12429
  ),
@@ -12756,7 +12434,12 @@ function TableResizeHandles({
12756
12434
  "aria-live": "polite",
12757
12435
  "data-table-resize-dimensions": "",
12758
12436
  className: "absolute bottom-2 right-2 rounded bg-foreground/85 px-1.5 py-0.5 text-[10px] font-medium text-background shadow-sm",
12759
- children: `${Math.round(layout.tableWidth)} \xD7 ${Math.round(layout.tableHeight)}`
12437
+ children: formatTableResizeDimensionLabel({
12438
+ width: layout.tableWidth,
12439
+ height: layout.tableHeight,
12440
+ widthMode: layout.widthMode,
12441
+ viewportWidth: layout.viewportWidth
12442
+ })
12760
12443
  }
12761
12444
  ) : null
12762
12445
  ]
@@ -12997,6 +12680,9 @@ function applyTableSize(editor, snapshot, dimensions, options = {}) {
12997
12680
  import { Fragment as Fragment7, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
12998
12681
  var TABLE_MENU_TOP_OFFSET = 10;
12999
12682
  var AXIS_HANDLE_RADIUS = 12;
12683
+ function clamp2(value, min, max) {
12684
+ return Math.min(Math.max(value, min), max);
12685
+ }
13000
12686
  function nearestIndex(centers, position) {
13001
12687
  let bestIndex = 0;
13002
12688
  let bestDistance = Number.POSITIVE_INFINITY;
@@ -13027,21 +12713,21 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13027
12713
  const t = useSmartTranslations("UEditor");
13028
12714
  const editorDocument = editor.view.dom.ownerDocument;
13029
12715
  const editorWindow = editorDocument.defaultView;
13030
- const [layout, setLayout] = React10.useState(null);
13031
- const [dragPreview, setDragPreview] = React10.useState(null);
13032
- const [tableResizeActive, setTableResizeActive] = React10.useState(false);
13033
- const [hoverState, setHoverState] = React10.useState(DEFAULT_TABLE_HOVER_STATE);
13034
- const [openMenuKey, setOpenMenuKey] = React10.useState(null);
13035
- const layoutRef = React10.useRef(null);
13036
- const dragStateRef = React10.useRef(null);
13037
- const syncFrameRef = React10.useRef(null);
13038
- const layoutRefreshFrameRef = React10.useRef(null);
13039
- const tableResizeFrameRef = React10.useRef(null);
13040
- const tableResizePreviewFrameRef = React10.useRef(null);
13041
- React10.useEffect(() => {
12716
+ const [layout, setLayout] = React11.useState(null);
12717
+ const [dragPreview, setDragPreview] = React11.useState(null);
12718
+ const [tableResizeActive, setTableResizeActive] = React11.useState(false);
12719
+ const [hoverState, setHoverState] = React11.useState(DEFAULT_TABLE_HOVER_STATE);
12720
+ const [openMenuKey, setOpenMenuKey] = React11.useState(null);
12721
+ const layoutRef = React11.useRef(null);
12722
+ const dragStateRef = React11.useRef(null);
12723
+ const syncFrameRef = React11.useRef(null);
12724
+ const layoutRefreshFrameRef = React11.useRef(null);
12725
+ const tableResizeFrameRef = React11.useRef(null);
12726
+ const tableResizePreviewFrameRef = React11.useRef(null);
12727
+ React11.useEffect(() => {
13042
12728
  layoutRef.current = layout;
13043
12729
  }, [layout]);
13044
- const syncFromCell = React10.useCallback((cell) => {
12730
+ const syncFromCell = React11.useCallback((cell) => {
13045
12731
  const surface = containerRef.current;
13046
12732
  if (!surface || !cell) {
13047
12733
  layoutRef.current = null;
@@ -13052,10 +12738,10 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13052
12738
  layoutRef.current = nextLayout;
13053
12739
  setLayout(nextLayout);
13054
12740
  }, [containerRef, editor]);
13055
- const syncFromSelection = React10.useCallback(() => {
12741
+ const syncFromSelection = React11.useCallback(() => {
13056
12742
  syncFromCell(getSelectedCell(editor));
13057
12743
  }, [editor, syncFromCell]);
13058
- const scheduleSyncFromSelection = React10.useCallback(() => {
12744
+ const scheduleSyncFromSelection = React11.useCallback(() => {
13059
12745
  if (syncFrameRef.current !== null) return;
13060
12746
  if (!editorWindow) return;
13061
12747
  syncFrameRef.current = editorWindow.requestAnimationFrame(() => {
@@ -13063,13 +12749,13 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13063
12749
  syncFromSelection();
13064
12750
  });
13065
12751
  }, [editorWindow, syncFromSelection]);
13066
- React10.useEffect(() => () => {
12752
+ React11.useEffect(() => () => {
13067
12753
  if (syncFrameRef.current !== null) {
13068
12754
  editorWindow?.cancelAnimationFrame(syncFrameRef.current);
13069
12755
  syncFrameRef.current = null;
13070
12756
  }
13071
12757
  }, [editorWindow]);
13072
- const refreshCurrentLayout = React10.useCallback(() => {
12758
+ const refreshCurrentLayout = React11.useCallback(() => {
13073
12759
  const currentLayout = layoutRef.current;
13074
12760
  if (!currentLayout) return;
13075
12761
  const surface = containerRef.current;
@@ -13084,7 +12770,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13084
12770
  layoutRef.current = nextLayout;
13085
12771
  setLayout(nextLayout);
13086
12772
  }, [containerRef, editor]);
13087
- const scheduleCurrentLayoutRefresh = React10.useCallback(() => {
12773
+ const scheduleCurrentLayoutRefresh = React11.useCallback(() => {
13088
12774
  if (!layoutRef.current || layoutRefreshFrameRef.current !== null) return;
13089
12775
  if (!editorWindow) return;
13090
12776
  layoutRefreshFrameRef.current = editorWindow.requestAnimationFrame(() => {
@@ -13092,13 +12778,13 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13092
12778
  refreshCurrentLayout();
13093
12779
  });
13094
12780
  }, [editorWindow, refreshCurrentLayout]);
13095
- React10.useEffect(() => () => {
12781
+ React11.useEffect(() => () => {
13096
12782
  if (layoutRefreshFrameRef.current !== null) {
13097
12783
  editorWindow?.cancelAnimationFrame(layoutRefreshFrameRef.current);
13098
12784
  layoutRefreshFrameRef.current = null;
13099
12785
  }
13100
12786
  }, [editorWindow]);
13101
- const updateTableResizePreview = React10.useCallback((dimensions) => {
12787
+ const updateTableResizePreview = React11.useCallback((dimensions) => {
13102
12788
  const frame = tableResizeFrameRef.current;
13103
12789
  if (!frame) return;
13104
12790
  frame.style.width = `${dimensions.width}px`;
@@ -13108,11 +12794,17 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13108
12794
  frame.style.left = `${activeLayout.tableLeft + (dimensions.leftDelta ?? 0)}px`;
13109
12795
  }
13110
12796
  const dimensionsLabel = frame.querySelector("[data-table-resize-dimensions]");
13111
- if (dimensionsLabel) {
13112
- dimensionsLabel.textContent = `${dimensions.width} \xD7 ${dimensions.height}`;
12797
+ if (dimensionsLabel && activeLayout) {
12798
+ dimensionsLabel.textContent = formatTableResizeDimensionLabel({
12799
+ width: dimensions.width,
12800
+ height: dimensions.height,
12801
+ widthBp: dimensions.widthBp,
12802
+ widthMode: activeLayout.widthMode,
12803
+ viewportWidth: activeLayout.viewportWidth
12804
+ });
13113
12805
  }
13114
12806
  }, []);
13115
- const resetTableResizePreview = React10.useCallback(() => {
12807
+ const resetTableResizePreview = React11.useCallback(() => {
13116
12808
  const activeLayout = layoutRef.current;
13117
12809
  if (!activeLayout) return;
13118
12810
  updateTableResizePreview({
@@ -13121,7 +12813,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13121
12813
  leftDelta: 0
13122
12814
  });
13123
12815
  }, [updateTableResizePreview]);
13124
- const clearDrag = React10.useCallback(() => {
12816
+ const clearDrag = React11.useCallback(() => {
13125
12817
  const dragState = dragStateRef.current;
13126
12818
  if (dragState?.kind === "resize-table") {
13127
12819
  try {
@@ -13141,7 +12833,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13141
12833
  resetTableResizePreview();
13142
12834
  setDocumentCursor(editorDocument, "");
13143
12835
  }, [editorDocument, editorWindow, resetTableResizePreview]);
13144
- const updateHoverState = React10.useCallback((event) => {
12836
+ const updateHoverState = React11.useCallback((event) => {
13145
12837
  const activeLayout = layoutRef.current;
13146
12838
  const surface = containerRef.current;
13147
12839
  if (!activeLayout || !surface || dragStateRef.current || event.buttons !== 0) {
@@ -13157,7 +12849,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13157
12849
  return areTableHoverStatesEqual(prev, nextState) ? prev : nextState;
13158
12850
  });
13159
12851
  }, [containerRef]);
13160
- React10.useEffect(() => {
12852
+ React11.useEffect(() => {
13161
12853
  const proseMirror = editor.view.dom;
13162
12854
  const surface = containerRef.current;
13163
12855
  if (!surface) return void 0;
@@ -13244,37 +12936,37 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13244
12936
  editor.off("update", scheduleCurrentLayoutRefresh);
13245
12937
  };
13246
12938
  }, [clearDrag, containerRef, editor, editorWindow, scheduleCurrentLayoutRefresh, scheduleSyncFromSelection, syncFromCell, syncFromSelection, updateHoverState]);
13247
- const runAtCellPos = React10.useCallback((cellPos, command, options) => {
12939
+ const runAtCellPos = React11.useCallback((cellPos, command, options) => {
13248
12940
  const result = runTableCommandAtCellPos(editor, cellPos, command);
13249
12941
  if (options?.sync !== false) {
13250
12942
  scheduleSyncFromSelection();
13251
12943
  }
13252
12944
  return result;
13253
12945
  }, [editor, scheduleSyncFromSelection]);
13254
- const runAtActiveCell = React10.useCallback((command, options) => {
12946
+ const runAtActiveCell = React11.useCallback((command, options) => {
13255
12947
  return runAtCellPos(layoutRef.current?.cellPos ?? null, command, options);
13256
12948
  }, [runAtCellPos]);
13257
- const duplicateRowAt = React10.useCallback((rowIndex, cellPos) => {
12949
+ const duplicateRowAt = React11.useCallback((rowIndex, cellPos) => {
13258
12950
  const result = duplicateTableRowAt(editor, rowIndex, cellPos);
13259
12951
  scheduleSyncFromSelection();
13260
12952
  return result;
13261
12953
  }, [editor, scheduleSyncFromSelection]);
13262
- const clearRowAt = React10.useCallback((rowIndex, cellPos) => {
12954
+ const clearRowAt = React11.useCallback((rowIndex, cellPos) => {
13263
12955
  const result = clearTableRowAt(editor, rowIndex, cellPos);
13264
12956
  scheduleSyncFromSelection();
13265
12957
  return result;
13266
12958
  }, [editor, scheduleSyncFromSelection]);
13267
- const duplicateColumnAt = React10.useCallback((columnIndex, cellPos) => {
12959
+ const duplicateColumnAt = React11.useCallback((columnIndex, cellPos) => {
13268
12960
  const result = duplicateTableColumnAt(editor, columnIndex, cellPos);
13269
12961
  scheduleSyncFromSelection();
13270
12962
  return result;
13271
12963
  }, [editor, scheduleSyncFromSelection]);
13272
- const clearColumnAt = React10.useCallback((columnIndex, cellPos) => {
12964
+ const clearColumnAt = React11.useCallback((columnIndex, cellPos) => {
13273
12965
  const result = clearTableColumnAt(editor, columnIndex, cellPos);
13274
12966
  scheduleSyncFromSelection();
13275
12967
  return result;
13276
12968
  }, [editor, scheduleSyncFromSelection]);
13277
- const expandTableBy = React10.useCallback((rows, cols) => {
12969
+ const expandTableBy = React11.useCallback((rows, cols) => {
13278
12970
  const activeCellPos = layoutRef.current?.cellPos ?? editor.state.selection.from;
13279
12971
  const result = expandTableFromCell(editor, activeCellPos, rows, cols);
13280
12972
  scheduleSyncFromSelection();
@@ -13283,7 +12975,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13283
12975
  const canExpandTable = Boolean(layout);
13284
12976
  const controlsVisible = false;
13285
12977
  const tableMenuOpen = openMenuKey === "table";
13286
- const startTableResize = React10.useCallback((event, edge) => {
12978
+ const startTableResize = React11.useCallback((event, edge) => {
13287
12979
  if (event.button !== 0 || dragStateRef.current) return;
13288
12980
  const activeLayout = layoutRef.current;
13289
12981
  if (!activeLayout) return;
@@ -13321,7 +13013,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13321
13013
  updateTableResizePreview(pendingDimensions);
13322
13014
  setDocumentCursor(editorDocument, edge === "both" ? "nwse-resize" : "ew-resize");
13323
13015
  }, [editor, editorDocument, updateTableResizePreview]);
13324
- const fitTableToEditorWidth = React10.useCallback((event) => {
13016
+ const fitTableToEditorWidth = React11.useCallback((event) => {
13325
13017
  if (event.button !== 0 || dragStateRef.current) return;
13326
13018
  const activeLayout = layoutRef.current;
13327
13019
  if (!activeLayout) return;
@@ -13346,19 +13038,57 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13346
13038
  scheduleSyncFromSelection();
13347
13039
  }
13348
13040
  }, [editor, scheduleSyncFromSelection]);
13349
- const startAddColumnDrag = React10.useCallback(() => {
13041
+ const resizeTableWithKeyboard = React11.useCallback((event, edge) => {
13042
+ const horizontal = event.key === "ArrowLeft" || event.key === "ArrowRight";
13043
+ const vertical = event.key === "ArrowUp" || event.key === "ArrowDown";
13044
+ if (!horizontal && !vertical || edge !== "both" && !horizontal) return;
13045
+ const activeLayout = layoutRef.current;
13046
+ if (!activeLayout) return;
13047
+ const snapshot = createTableSizeSnapshot(
13048
+ editor,
13049
+ activeLayout.cellPos,
13050
+ activeLayout.tableWidth,
13051
+ activeLayout.tableHeight,
13052
+ activeLayout.viewportWidth
13053
+ );
13054
+ if (!snapshot) return;
13055
+ event.preventDefault();
13056
+ event.stopPropagation();
13057
+ const step = event.shiftKey ? 20 : 5;
13058
+ const dimensions = resolveTableResizeDimensions({
13059
+ deltaX: event.key === "ArrowLeft" ? -step : event.key === "ArrowRight" ? step : 0,
13060
+ deltaY: event.key === "ArrowUp" ? -step : event.key === "ArrowDown" ? step : 0,
13061
+ edge,
13062
+ lockAxis: false,
13063
+ preserveRatio: false,
13064
+ snapshot
13065
+ });
13066
+ if (applyTableSize(editor, snapshot, dimensions, { widthMode: snapshot.widthMode })) {
13067
+ scheduleSyncFromSelection();
13068
+ }
13069
+ }, [editor, scheduleSyncFromSelection]);
13070
+ const startAddColumnDrag = React11.useCallback((event) => {
13071
+ if (event.button !== 0 || dragStateRef.current) return;
13072
+ event.preventDefault();
13073
+ event.stopPropagation();
13350
13074
  setOpenMenuKey(null);
13351
13075
  dragStateRef.current = { kind: "add-column", previewCols: 1 };
13352
13076
  setDragPreview({ kind: "add-column", previewCols: 1 });
13353
13077
  setDocumentCursor(editorDocument, "ew-resize");
13354
13078
  }, [editorDocument]);
13355
- const startAddRowDrag = React10.useCallback(() => {
13079
+ const startAddRowDrag = React11.useCallback((event) => {
13080
+ if (event.button !== 0 || dragStateRef.current) return;
13081
+ event.preventDefault();
13082
+ event.stopPropagation();
13356
13083
  setOpenMenuKey(null);
13357
13084
  dragStateRef.current = { kind: "add-row", previewRows: 1 };
13358
13085
  setDragPreview({ kind: "add-row", previewRows: 1 });
13359
13086
  setDocumentCursor(editorDocument, "ns-resize");
13360
13087
  }, [editorDocument]);
13361
- const startRowDrag = React10.useCallback((rowHandle) => {
13088
+ const startRowDrag = React11.useCallback((event, rowHandle) => {
13089
+ if (event.button !== 0 || dragStateRef.current) return;
13090
+ event.preventDefault();
13091
+ event.stopPropagation();
13362
13092
  setOpenMenuKey(null);
13363
13093
  dragStateRef.current = {
13364
13094
  kind: "row",
@@ -13375,7 +13105,10 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13375
13105
  });
13376
13106
  setDocumentCursor(editorDocument, "grabbing");
13377
13107
  }, [editorDocument]);
13378
- const startColumnDrag = React10.useCallback((columnHandle) => {
13108
+ const startColumnDrag = React11.useCallback((event, columnHandle) => {
13109
+ if (event.button !== 0 || dragStateRef.current) return;
13110
+ event.preventDefault();
13111
+ event.stopPropagation();
13379
13112
  setOpenMenuKey(null);
13380
13113
  dragStateRef.current = {
13381
13114
  kind: "column",
@@ -13392,9 +13125,62 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13392
13125
  });
13393
13126
  setDocumentCursor(editorDocument, "grabbing");
13394
13127
  }, [editorDocument]);
13395
- React10.useEffect(() => {
13128
+ const moveRowWithKeyboard = React11.useCallback((rowHandle, direction) => {
13129
+ const targetIndex = clamp2(rowHandle.index + direction, 0, (layoutRef.current?.rowHandles.length ?? 1) - 1);
13130
+ if (targetIndex === rowHandle.index) return;
13131
+ const tableInfo = findTableInfo(editor, rowHandle.cellPos);
13132
+ moveTableRow({
13133
+ from: rowHandle.index,
13134
+ to: targetIndex,
13135
+ pos: rowHandle.cellPos,
13136
+ select: true
13137
+ })(editor.state, (transaction) => {
13138
+ const nextTable = tableInfo ? transaction.doc.nodeAt(tableInfo.pos) : null;
13139
+ if (tableInfo && nextTable?.type.name === "table") {
13140
+ const rewrittenTable = rewriteTableNodeFormulaReferencesForMove(nextTable, "row", rowHandle.index, targetIndex);
13141
+ if (rewrittenTable !== nextTable) {
13142
+ transaction.replaceWith(tableInfo.pos, tableInfo.pos + nextTable.nodeSize, rewrittenTable);
13143
+ }
13144
+ }
13145
+ editor.view.dispatch(transaction);
13146
+ });
13147
+ scheduleSyncFromSelection();
13148
+ }, [editor, scheduleSyncFromSelection]);
13149
+ const moveColumnWithKeyboard = React11.useCallback((columnHandle, direction) => {
13150
+ const targetIndex = clamp2(columnHandle.index + direction, 0, (layoutRef.current?.columnHandles.length ?? 1) - 1);
13151
+ if (targetIndex === columnHandle.index) return;
13152
+ const tableInfo = findTableInfo(editor, columnHandle.cellPos);
13153
+ const responsiveRatios = tableInfo && normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive" ? moveResponsiveColumnRatio(getTableColumnRatios(tableInfo.node), columnHandle.index, targetIndex) : null;
13154
+ moveTableColumn({
13155
+ from: columnHandle.index,
13156
+ to: targetIndex,
13157
+ pos: columnHandle.cellPos,
13158
+ select: true
13159
+ })(editor.state, (transaction) => {
13160
+ if (tableInfo && responsiveRatios) {
13161
+ const nextTable2 = transaction.doc.nodeAt(tableInfo.pos);
13162
+ if (nextTable2?.type.name === "table") {
13163
+ transaction.setNodeMarkup(tableInfo.pos, void 0, {
13164
+ ...nextTable2.attrs,
13165
+ widthMode: "responsive",
13166
+ columnRatios: responsiveRatios
13167
+ });
13168
+ }
13169
+ }
13170
+ const nextTable = tableInfo ? transaction.doc.nodeAt(tableInfo.pos) : null;
13171
+ if (tableInfo && nextTable?.type.name === "table") {
13172
+ const rewrittenTable = rewriteTableNodeFormulaReferencesForMove(nextTable, "column", columnHandle.index, targetIndex);
13173
+ if (rewrittenTable !== nextTable) {
13174
+ transaction.replaceWith(tableInfo.pos, tableInfo.pos + nextTable.nodeSize, rewrittenTable);
13175
+ }
13176
+ }
13177
+ editor.view.dispatch(transaction);
13178
+ });
13179
+ scheduleSyncFromSelection();
13180
+ }, [editor, scheduleSyncFromSelection]);
13181
+ React11.useEffect(() => {
13396
13182
  if (!editorWindow) return;
13397
- const handleMouseMove2 = (event) => {
13183
+ const handlePointerMove = (event) => {
13398
13184
  const dragState = dragStateRef.current;
13399
13185
  const activeLayout = layoutRef.current;
13400
13186
  const surface = containerRef.current;
@@ -13447,16 +13233,31 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13447
13233
  setDragPreview({ kind: "add-row", previewRows });
13448
13234
  setDocumentCursor(editorDocument, "ns-resize");
13449
13235
  };
13450
- const handleMouseUp = () => {
13236
+ const handlePointerUp = () => {
13451
13237
  const dragState = dragStateRef.current;
13452
13238
  if (!dragState || dragState.kind === "resize-table") return;
13453
13239
  if (dragState.kind === "row" && dragState.originIndex !== dragState.targetIndex) {
13240
+ const tableInfo = findTableInfo(editor, dragState.anchorPos);
13454
13241
  moveTableRow({
13455
13242
  from: dragState.originIndex,
13456
13243
  to: dragState.targetIndex,
13457
13244
  pos: dragState.anchorPos,
13458
13245
  select: true
13459
- })(editor.state, editor.view.dispatch);
13246
+ })(editor.state, (transaction) => {
13247
+ const nextTable = tableInfo ? transaction.doc.nodeAt(tableInfo.pos) : null;
13248
+ if (tableInfo && nextTable?.type.name === "table") {
13249
+ const rewrittenTable = rewriteTableNodeFormulaReferencesForMove(
13250
+ nextTable,
13251
+ "row",
13252
+ dragState.originIndex,
13253
+ dragState.targetIndex
13254
+ );
13255
+ if (rewrittenTable !== nextTable) {
13256
+ transaction.replaceWith(tableInfo.pos, tableInfo.pos + nextTable.nodeSize, rewrittenTable);
13257
+ }
13258
+ }
13259
+ editor.view.dispatch(transaction);
13260
+ });
13460
13261
  scheduleSyncFromSelection();
13461
13262
  }
13462
13263
  if (dragState.kind === "column" && dragState.originIndex !== dragState.targetIndex) {
@@ -13473,15 +13274,27 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13473
13274
  select: true
13474
13275
  })(editor.state, (transaction) => {
13475
13276
  if (tableInfo && responsiveRatios) {
13476
- const nextTable = transaction.doc.nodeAt(tableInfo.pos);
13477
- if (nextTable?.type.name === "table") {
13277
+ const nextTable2 = transaction.doc.nodeAt(tableInfo.pos);
13278
+ if (nextTable2?.type.name === "table") {
13478
13279
  transaction.setNodeMarkup(tableInfo.pos, void 0, {
13479
- ...nextTable.attrs,
13280
+ ...nextTable2.attrs,
13480
13281
  widthMode: "responsive",
13481
13282
  columnRatios: responsiveRatios
13482
13283
  });
13483
13284
  }
13484
13285
  }
13286
+ const nextTable = tableInfo ? transaction.doc.nodeAt(tableInfo.pos) : null;
13287
+ if (tableInfo && nextTable?.type.name === "table") {
13288
+ const rewrittenTable = rewriteTableNodeFormulaReferencesForMove(
13289
+ nextTable,
13290
+ "column",
13291
+ dragState.originIndex,
13292
+ dragState.targetIndex
13293
+ );
13294
+ if (rewrittenTable !== nextTable) {
13295
+ transaction.replaceWith(tableInfo.pos, tableInfo.pos + nextTable.nodeSize, rewrittenTable);
13296
+ }
13297
+ }
13485
13298
  editor.view.dispatch(transaction);
13486
13299
  });
13487
13300
  scheduleSyncFromSelection();
@@ -13494,20 +13307,23 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13494
13307
  }
13495
13308
  clearDrag();
13496
13309
  };
13497
- const unsubscribeMouseMove = subscribeSharedGlobalEvent(
13310
+ const unsubscribePointerMove = subscribeSharedGlobalEvent(
13498
13311
  editorWindow,
13499
- "mousemove",
13500
- handleMouseMove2
13312
+ "pointermove",
13313
+ handlePointerMove,
13314
+ { passive: false }
13501
13315
  );
13502
- const unsubscribeMouseUp = subscribeSharedGlobalEvent(editorWindow, "mouseup", handleMouseUp);
13316
+ const unsubscribePointerUp = subscribeSharedGlobalEvent(editorWindow, "pointerup", handlePointerUp);
13317
+ const unsubscribePointerCancel = subscribeSharedGlobalEvent(editorWindow, "pointercancel", clearDrag);
13503
13318
  const unsubscribeBlur = subscribeSharedGlobalEvent(editorWindow, "blur", clearDrag);
13504
13319
  return () => {
13505
- unsubscribeMouseMove();
13506
- unsubscribeMouseUp();
13320
+ unsubscribePointerMove();
13321
+ unsubscribePointerUp();
13322
+ unsubscribePointerCancel();
13507
13323
  unsubscribeBlur();
13508
13324
  };
13509
13325
  }, [clearDrag, containerRef, editor, editorDocument, editorWindow, expandTableBy, scheduleSyncFromSelection]);
13510
- React10.useEffect(() => {
13326
+ React11.useEffect(() => {
13511
13327
  if (!editorWindow) return;
13512
13328
  const handlePointerMove = (event) => {
13513
13329
  const dragState = dragStateRef.current;
@@ -13578,7 +13394,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13578
13394
  }
13579
13395
  };
13580
13396
  }, [clearDrag, editor, editorDocument, editorWindow, scheduleSyncFromSelection, updateTableResizePreview]);
13581
- const openCellInspector = React10.useCallback(() => {
13397
+ const openCellInspector = React11.useCallback(() => {
13582
13398
  const cellPos = layoutRef.current?.cellPos;
13583
13399
  if (!showCellInspector || cellPos == null) return;
13584
13400
  const didSelect = editor.commands.setCellSelection({
@@ -13590,7 +13406,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13590
13406
  if (!editor.isDestroyed) editor.view.focus();
13591
13407
  });
13592
13408
  }, [editor, editorWindow, showCellInspector]);
13593
- const menuItems = React10.useMemo(() => {
13409
+ const menuItems = React11.useMemo(() => {
13594
13410
  if (!layout) return [];
13595
13411
  return [
13596
13412
  ...showCellInspector ? [{
@@ -13663,7 +13479,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13663
13479
  }
13664
13480
  ];
13665
13481
  }, [editor, layout, openCellInspector, runAtActiveCell, showCellInspector, t]);
13666
- const getRowHandleMenuItems = React10.useCallback((rowHandle) => [
13482
+ const getRowHandleMenuItems = React11.useCallback((rowHandle) => [
13667
13483
  {
13668
13484
  label: t("tableMenu.addRowBefore"),
13669
13485
  icon: ArrowUp,
@@ -13691,7 +13507,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13691
13507
  destructive: true
13692
13508
  }
13693
13509
  ], [clearRowAt, duplicateRowAt, runAtCellPos, t]);
13694
- const getColumnHandleMenuItems = React10.useCallback((columnHandle) => [
13510
+ const getColumnHandleMenuItems = React11.useCallback((columnHandle) => [
13695
13511
  {
13696
13512
  label: t("tableMenu.addColumnBefore"),
13697
13513
  icon: ArrowLeft,
@@ -13737,6 +13553,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13737
13553
  onOpenMenuChange: (menuKey, open) => {
13738
13554
  setOpenMenuKey((prev) => open ? menuKey : prev === menuKey ? null : prev);
13739
13555
  },
13556
+ onMove: moveRowWithKeyboard,
13740
13557
  onStartDrag: startRowDrag,
13741
13558
  openMenuKey,
13742
13559
  rowDragLabel: t("tableMenu.dragRow"),
@@ -13757,6 +13574,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13757
13574
  onOpenMenuChange: (menuKey, open) => {
13758
13575
  setOpenMenuKey((prev) => open ? menuKey : prev === menuKey ? null : prev);
13759
13576
  },
13577
+ onMove: moveColumnWithKeyboard,
13760
13578
  onStartDrag: startColumnDrag,
13761
13579
  openMenuKey
13762
13580
  }
@@ -13784,6 +13602,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13784
13602
  frameRef: tableResizeFrameRef,
13785
13603
  layout,
13786
13604
  onFitWidth: fitTableToEditorWidth,
13605
+ onKeyboardResize: resizeTableWithKeyboard,
13787
13606
  onStartResize: startTableResize,
13788
13607
  resizeBothLabel: t("tableMenu.resizeBoth")
13789
13608
  }
@@ -13798,6 +13617,8 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13798
13617
  layout,
13799
13618
  onStartAddColumn: startAddColumnDrag,
13800
13619
  onStartAddRow: startAddRowDrag,
13620
+ onAddColumn: () => expandTableBy(0, 1),
13621
+ onAddRow: () => expandTableBy(1, 0),
13801
13622
  quickAddColumnLabel: t("tableMenu.quickAddColumnAfter"),
13802
13623
  quickAddRowLabel: t("tableMenu.quickAddRowAfter")
13803
13624
  }
@@ -13815,10 +13636,10 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13815
13636
  }
13816
13637
 
13817
13638
  // src/components/UEditor/use-table-interactions.ts
13818
- import React12, { useEffect as useEffect6, useRef as useRef6 } from "react";
13639
+ import React13, { useEffect as useEffect6, useRef as useRef6 } from "react";
13819
13640
 
13820
13641
  // src/components/UEditor/use-table-row-resize.ts
13821
- import React11, { useRef as useRef5 } from "react";
13642
+ import React12, { useRef as useRef5 } from "react";
13822
13643
  function setRowResizeBodyState(ownerDocument, active) {
13823
13644
  ownerDocument.body.style.userSelect = active ? "none" : "";
13824
13645
  ownerDocument.body.style.cursor = active ? "row-resize" : "";
@@ -13832,15 +13653,15 @@ function useTableRowResize({
13832
13653
  scheduleTableLayoutSync
13833
13654
  }) {
13834
13655
  const stateRef = useRef5(null);
13835
- const syncActiveGuide = React11.useCallback(() => {
13656
+ const syncActiveGuide = React12.useCallback(() => {
13836
13657
  const state = stateRef.current;
13837
13658
  if (!state) return false;
13838
13659
  setHoveredTableCell(state.cellElement);
13839
13660
  showRowGuide(state.tableElement, state.rowElement, state.cellElement, state.pendingHeight);
13840
13661
  return true;
13841
13662
  }, [setHoveredTableCell, showRowGuide]);
13842
- const isResizing = React11.useCallback(() => stateRef.current !== null, []);
13843
- const beginResize = React11.useCallback((event, table, row, cell) => {
13663
+ const isResizing = React12.useCallback(() => stateRef.current !== null, []);
13664
+ const beginResize = React12.useCallback((event, table, row, cell) => {
13844
13665
  if (!editor || !isRowResizeHotspot(cell, event.clientX, event.clientY)) {
13845
13666
  return false;
13846
13667
  }
@@ -13869,7 +13690,7 @@ function useTableRowResize({
13869
13690
  event.stopPropagation();
13870
13691
  return true;
13871
13692
  }, [editor, setHoveredTableCell, showRowGuide]);
13872
- const handlePointerMove = React11.useCallback((event) => {
13693
+ const handlePointerMove = React12.useCallback((event) => {
13873
13694
  const state = stateRef.current;
13874
13695
  if (!state) return;
13875
13696
  const nextHeight = Math.max(
@@ -13886,7 +13707,7 @@ function useTableRowResize({
13886
13707
  setRowResizeBodyState(state.rowElement.ownerDocument, true);
13887
13708
  showRowGuide(state.tableElement, state.rowElement, state.cellElement, nextHeight);
13888
13709
  }, [showRowGuide]);
13889
- const handlePointerUp = React11.useCallback((event) => {
13710
+ const handlePointerUp = React12.useCallback((event) => {
13890
13711
  if (!editor) return;
13891
13712
  const state = stateRef.current;
13892
13713
  if (!state) return;
@@ -13911,7 +13732,7 @@ function useTableRowResize({
13911
13732
  clearAllTableResizeHover();
13912
13733
  scheduleTableLayoutSync();
13913
13734
  }, [clearAllTableResizeHover, clearHoveredTableCell, editor, scheduleTableLayoutSync]);
13914
- const cancelResize = React11.useCallback(() => {
13735
+ const cancelResize = React12.useCallback(() => {
13915
13736
  const state = stateRef.current;
13916
13737
  if (!state) return;
13917
13738
  stateRef.current = null;
@@ -13921,7 +13742,7 @@ function useTableRowResize({
13921
13742
  clearAllTableResizeHover();
13922
13743
  scheduleTableLayoutSync();
13923
13744
  }, [clearAllTableResizeHover, clearHoveredTableCell, scheduleTableLayoutSync]);
13924
- const cleanup = React11.useCallback(() => {
13745
+ const cleanup = React12.useCallback(() => {
13925
13746
  const editorDocument = stateRef.current?.rowElement.ownerDocument ?? editor?.view.dom.ownerDocument;
13926
13747
  stateRef.current = null;
13927
13748
  isRowResizingGlobal.active = false;
@@ -13950,16 +13771,16 @@ function useUEditorTableInteractions(editor, editable = true) {
13950
13771
  const activeTableCellRef = useRef6(null);
13951
13772
  const suppressActiveCellHighlightRef = useRef6(false);
13952
13773
  const tableLayoutSyncFrameRef = useRef6(null);
13953
- const getProseMirrorElement = React12.useCallback(() => {
13774
+ const getProseMirrorElement = React13.useCallback(() => {
13954
13775
  return editorContentRef.current?.querySelector(".ProseMirror");
13955
13776
  }, []);
13956
- const setEditorResizeCursor = React12.useCallback((cursor) => {
13777
+ const setEditorResizeCursor = React13.useCallback((cursor) => {
13957
13778
  const proseMirror = getProseMirrorElement();
13958
13779
  if (proseMirror) {
13959
13780
  proseMirror.style.cursor = cursor;
13960
13781
  }
13961
13782
  }, [getProseMirrorElement]);
13962
- const hideColumnGuide = React12.useCallback(() => {
13783
+ const hideColumnGuide = React13.useCallback(() => {
13963
13784
  editorContentRef.current?.classList.remove("resize-cursor");
13964
13785
  getProseMirrorElement()?.classList.remove("resize-cursor");
13965
13786
  const guide = tableColumnGuideRef.current;
@@ -13967,7 +13788,7 @@ function useUEditorTableInteractions(editor, editable = true) {
13967
13788
  guide.style.opacity = "0";
13968
13789
  }
13969
13790
  }, [getProseMirrorElement]);
13970
- const hideRowGuide = React12.useCallback(() => {
13791
+ const hideRowGuide = React13.useCallback(() => {
13971
13792
  editorContentRef.current?.classList.remove("resize-row-cursor");
13972
13793
  getProseMirrorElement()?.classList.remove("resize-row-cursor");
13973
13794
  const guide = tableRowGuideRef.current;
@@ -13977,21 +13798,21 @@ function useUEditorTableInteractions(editor, editable = true) {
13977
13798
  }, [getProseMirrorElement]);
13978
13799
  const hotspotTimerRef = useRef6(null);
13979
13800
  const activeHotspotKeyRef = useRef6(null);
13980
- const clearHotspotTimer = React12.useCallback(() => {
13801
+ const clearHotspotTimer = React13.useCallback(() => {
13981
13802
  if (hotspotTimerRef.current !== null) {
13982
13803
  const realm = editorContentRef.current?.ownerDocument.defaultView;
13983
13804
  realm?.clearTimeout(hotspotTimerRef.current);
13984
13805
  hotspotTimerRef.current = null;
13985
13806
  }
13986
13807
  }, []);
13987
- const clearAllTableResizeHover = React12.useCallback(() => {
13808
+ const clearAllTableResizeHover = React13.useCallback(() => {
13988
13809
  clearHotspotTimer();
13989
13810
  activeHotspotKeyRef.current = null;
13990
13811
  setEditorResizeCursor("");
13991
13812
  hideColumnGuide();
13992
13813
  hideRowGuide();
13993
13814
  }, [clearHotspotTimer, hideColumnGuide, hideRowGuide, setEditorResizeCursor]);
13994
- const updateActiveCellHighlight = React12.useCallback((cell) => {
13815
+ const updateActiveCellHighlight = React13.useCallback((cell) => {
13995
13816
  const surface = editorContentRef.current;
13996
13817
  const highlight = activeTableCellHighlightRef.current;
13997
13818
  if (!highlight) return;
@@ -14006,7 +13827,7 @@ function useUEditorTableInteractions(editor, editable = true) {
14006
13827
  highlight.style.width = `${metrics.width}px`;
14007
13828
  highlight.style.height = `${metrics.height}px`;
14008
13829
  }, []);
14009
- const scheduleTableLayoutSync = React12.useCallback(() => {
13830
+ const scheduleTableLayoutSync = React13.useCallback(() => {
14010
13831
  if (!activeTableCellRef.current || tableLayoutSyncFrameRef.current !== null) return;
14011
13832
  const realm = editorContentRef.current?.ownerDocument.defaultView;
14012
13833
  if (!realm) return;
@@ -14018,7 +13839,7 @@ function useUEditorTableInteractions(editor, editable = true) {
14018
13839
  editorContentRef.current?.dispatchEvent(new realm.CustomEvent(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT));
14019
13840
  });
14020
13841
  }, [updateActiveCellHighlight]);
14021
- const setActiveTableCell = React12.useCallback((cell) => {
13842
+ const setActiveTableCell = React13.useCallback((cell) => {
14022
13843
  if (activeTableCellRef.current === cell) {
14023
13844
  updateActiveCellHighlight(cell);
14024
13845
  return;
@@ -14026,17 +13847,17 @@ function useUEditorTableInteractions(editor, editable = true) {
14026
13847
  activeTableCellRef.current = cell;
14027
13848
  updateActiveCellHighlight(activeTableCellRef.current);
14028
13849
  }, [updateActiveCellHighlight]);
14029
- const clearActiveTableCell = React12.useCallback(() => {
13850
+ const clearActiveTableCell = React13.useCallback(() => {
14030
13851
  activeTableCellRef.current = null;
14031
13852
  updateActiveCellHighlight(null);
14032
13853
  }, [updateActiveCellHighlight]);
14033
- const setHoveredTableCell = React12.useCallback((cell) => {
13854
+ const setHoveredTableCell = React13.useCallback((cell) => {
14034
13855
  hoveredTableCellRef.current = cell;
14035
13856
  }, []);
14036
- const clearHoveredTableCell = React12.useCallback(() => {
13857
+ const clearHoveredTableCell = React13.useCallback(() => {
14037
13858
  hoveredTableCellRef.current = null;
14038
13859
  }, []);
14039
- const showColumnGuide = React12.useCallback((table, row, cell) => {
13860
+ const showColumnGuide = React13.useCallback((table, row, cell) => {
14040
13861
  const surface = editorContentRef.current;
14041
13862
  const guide = tableColumnGuideRef.current;
14042
13863
  if (!surface || !guide) return;
@@ -14050,7 +13871,7 @@ function useUEditorTableInteractions(editor, editable = true) {
14050
13871
  getProseMirrorElement()?.classList.add("resize-cursor");
14051
13872
  setEditorResizeCursor("col-resize");
14052
13873
  }, [getProseMirrorElement, setEditorResizeCursor]);
14053
- const showRowGuide = React12.useCallback((table, row, cell, previewHeight) => {
13874
+ const showRowGuide = React13.useCallback((table, row, cell, previewHeight) => {
14054
13875
  const surface = editorContentRef.current;
14055
13876
  const guide = tableRowGuideRef.current;
14056
13877
  if (!surface || !guide) return;
@@ -14082,7 +13903,7 @@ function useUEditorTableInteractions(editor, editable = true) {
14082
13903
  clearAllTableResizeHover,
14083
13904
  scheduleTableLayoutSync
14084
13905
  });
14085
- const syncActiveTableCellFromSelection = React12.useCallback(() => {
13906
+ const syncActiveTableCellFromSelection = React13.useCallback(() => {
14086
13907
  if (!editor) return;
14087
13908
  if (!editor.isFocused) {
14088
13909
  clearActiveTableCell();
@@ -14423,7 +14244,7 @@ function cancelFormulaEditing(view) {
14423
14244
  function getReferenceInsertionPrefix(view, cellContentStart, insertionPos) {
14424
14245
  const textBeforeCursor = view.state.doc.textBetween(cellContentStart, insertionPos, "", "");
14425
14246
  const isInsideFunctionArguments = /[A-Z]+\([^)]*$/i.test(textBeforeCursor);
14426
- const followsReference = /[A-Z]+[1-9]\d*(?::[A-Z]+[1-9]\d*)?\s*$/i.test(textBeforeCursor);
14247
+ const followsReference = /\$?[A-Z]+\$?[1-9]\d*(?::\$?[A-Z]+\$?[1-9]\d*)?\s*$/i.test(textBeforeCursor);
14427
14248
  return isInsideFunctionArguments && followsReference ? "," : "";
14428
14249
  }
14429
14250
  function findTableForPos(view, pos) {
@@ -14974,7 +14795,7 @@ function TableFormulaBar({ editor }) {
14974
14795
  }
14975
14796
 
14976
14797
  // src/components/UEditor/use-editor-output.ts
14977
- import * as React14 from "react";
14798
+ import * as React15 from "react";
14978
14799
  function normalizeDebounceMs(value) {
14979
14800
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
14980
14801
  }
@@ -14984,18 +14805,18 @@ function useUEditorOutput({
14984
14805
  onJsonChange,
14985
14806
  outputDebounceMs
14986
14807
  }) {
14987
- const callbacksRef = React14.useRef({
14808
+ const callbacksRef = React15.useRef({
14988
14809
  onChange,
14989
14810
  onHtmlChange,
14990
14811
  onJsonChange
14991
14812
  });
14992
- const pendingEditorRef = React14.useRef(null);
14993
- const timeoutRef = React14.useRef(null);
14813
+ const pendingEditorRef = React15.useRef(null);
14814
+ const timeoutRef = React15.useRef(null);
14994
14815
  const debounceMs = normalizeDebounceMs(outputDebounceMs);
14995
- React14.useLayoutEffect(() => {
14816
+ React15.useLayoutEffect(() => {
14996
14817
  callbacksRef.current = { onChange, onHtmlChange, onJsonChange };
14997
14818
  }, [onChange, onHtmlChange, onJsonChange]);
14998
- const flushOutputUpdates = React14.useCallback(() => {
14819
+ const flushOutputUpdates = React15.useCallback(() => {
14999
14820
  if (timeoutRef.current !== null) {
15000
14821
  clearTimeout(timeoutRef.current);
15001
14822
  timeoutRef.current = null;
@@ -15013,7 +14834,7 @@ function useUEditorOutput({
15013
14834
  callbacks.onJsonChange(editor.getJSON());
15014
14835
  }
15015
14836
  }, []);
15016
- const scheduleOutputUpdate = React14.useCallback((editor) => {
14837
+ const scheduleOutputUpdate = React15.useCallback((editor) => {
15017
14838
  const callbacks = callbacksRef.current;
15018
14839
  if (!callbacks.onChange && !callbacks.onHtmlChange && !callbacks.onJsonChange) return;
15019
14840
  pendingEditorRef.current = editor;
@@ -15027,32 +14848,98 @@ function useUEditorOutput({
15027
14848
  }
15028
14849
  timeoutRef.current = setTimeout(flushOutputUpdates, debounceMs);
15029
14850
  }, [debounceMs, flushOutputUpdates]);
15030
- React14.useEffect(() => {
14851
+ React15.useEffect(() => {
15031
14852
  if (!pendingEditorRef.current || timeoutRef.current === null) return;
15032
14853
  clearTimeout(timeoutRef.current);
15033
14854
  timeoutRef.current = debounceMs === 0 ? null : setTimeout(flushOutputUpdates, debounceMs);
15034
14855
  if (debounceMs === 0) flushOutputUpdates();
15035
14856
  }, [debounceMs, flushOutputUpdates]);
15036
- React14.useEffect(() => () => {
14857
+ React15.useEffect(() => () => {
14858
+ flushOutputUpdates();
15037
14859
  if (timeoutRef.current !== null) {
15038
14860
  clearTimeout(timeoutRef.current);
15039
14861
  timeoutRef.current = null;
15040
14862
  }
15041
14863
  pendingEditorRef.current = null;
15042
- }, []);
14864
+ }, [flushOutputUpdates]);
15043
14865
  return {
15044
14866
  flushOutputUpdates,
15045
14867
  scheduleOutputUpdate
15046
14868
  };
15047
14869
  }
15048
14870
 
14871
+ // src/components/UEditor/use-wide-table-layout.ts
14872
+ import React16 from "react";
14873
+ function useWideTableLayout(editor, enabled, widthBoundaryRef, editorShellRef, editorSurfaceRef) {
14874
+ React16.useLayoutEffect(() => {
14875
+ const boundary = widthBoundaryRef.current;
14876
+ const shell = editorShellRef.current;
14877
+ const surface = editorSurfaceRef.current;
14878
+ if (!editor || !boundary || !shell || !surface) return;
14879
+ const editorDocument = editor.view.dom.ownerDocument;
14880
+ const editorWindow = editorDocument.defaultView;
14881
+ if (!editorWindow) return;
14882
+ const editorDom = editor.view.dom;
14883
+ let frame = 0;
14884
+ const resetWidths = () => {
14885
+ shell.style.removeProperty("width");
14886
+ editorDom.style.removeProperty("width");
14887
+ };
14888
+ const sync = () => {
14889
+ frame = 0;
14890
+ resetWidths();
14891
+ if (!enabled || editor.isDestroyed) return;
14892
+ const boundaryWidth = boundary.getBoundingClientRect().width || boundary.clientWidth;
14893
+ const surfaceWidth = surface.getBoundingClientRect().width || surface.clientWidth;
14894
+ if (boundaryWidth <= 0 || surfaceWidth <= 0) return;
14895
+ editorDom.style.width = `${surfaceWidth}px`;
14896
+ const shellRect = shell.getBoundingClientRect();
14897
+ let requiredWidth = boundaryWidth;
14898
+ editorDom.querySelectorAll("table").forEach((table) => {
14899
+ const tableRect = table.getBoundingClientRect();
14900
+ if (tableRect.width > 0) {
14901
+ requiredWidth = Math.max(requiredWidth, tableRect.right - shellRect.left + 32);
14902
+ return;
14903
+ }
14904
+ const widthBp = Number(table.getAttribute("data-table-width-bp"));
14905
+ if (Number.isFinite(widthBp) && widthBp > 1e4) {
14906
+ requiredWidth = Math.max(requiredWidth, surfaceWidth * widthBp / 1e4 + 32);
14907
+ }
14908
+ });
14909
+ if (requiredWidth <= boundaryWidth + 1) {
14910
+ editorDom.style.removeProperty("width");
14911
+ return;
14912
+ }
14913
+ shell.style.width = `${Math.ceil(requiredWidth)}px`;
14914
+ };
14915
+ const schedule = () => {
14916
+ if (frame) editorWindow.cancelAnimationFrame(frame);
14917
+ frame = editorWindow.requestAnimationFrame(sync);
14918
+ };
14919
+ const resizeObserver = typeof editorWindow.ResizeObserver === "function" ? new editorWindow.ResizeObserver(schedule) : null;
14920
+ resizeObserver?.observe(boundary);
14921
+ editor.on("transaction", schedule);
14922
+ surface.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, schedule);
14923
+ editorWindow.addEventListener("resize", schedule);
14924
+ schedule();
14925
+ return () => {
14926
+ if (frame) editorWindow.cancelAnimationFrame(frame);
14927
+ resizeObserver?.disconnect();
14928
+ editor.off("transaction", schedule);
14929
+ surface.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, schedule);
14930
+ editorWindow.removeEventListener("resize", schedule);
14931
+ resetWidths();
14932
+ };
14933
+ }, [editor, editorShellRef, editorSurfaceRef, enabled, widthBoundaryRef]);
14934
+ }
14935
+
15049
14936
  // src/components/UEditor/UEditor.tsx
15050
14937
  import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
15051
- var LazyMenuBar = React15.lazy(async () => {
15052
- const { MenuBar } = await import("./menu-bar-AHG64PEB.js");
14938
+ var LazyMenuBar = React17.lazy(async () => {
14939
+ const { MenuBar } = await import("./menu-bar-DPCNNUX5.js");
15053
14940
  return { default: MenuBar };
15054
14941
  });
15055
- var UEditor = React15.forwardRef(({
14942
+ var UEditor = React17.forwardRef(({
15056
14943
  getPortalContainer,
15057
14944
  content = "",
15058
14945
  onChange,
@@ -15066,7 +14953,11 @@ var UEditor = React15.forwardRef(({
15066
14953
  maxImageFileSize,
15067
14954
  allowedImageMimeTypes,
15068
14955
  fallbackToDataUrl,
14956
+ onImageUploadError,
15069
14957
  placeholder,
14958
+ ariaLabel,
14959
+ ariaLabelledBy,
14960
+ ariaDescribedBy,
15070
14961
  className,
15071
14962
  editable = true,
15072
14963
  autofocus = false,
@@ -15078,6 +14969,7 @@ var UEditor = React15.forwardRef(({
15078
14969
  maxCharacters,
15079
14970
  minHeight = "200px",
15080
14971
  maxHeight = "auto",
14972
+ expandWideTables = true,
15081
14973
  variant = "default",
15082
14974
  rounded = true,
15083
14975
  fontFamilies,
@@ -15105,8 +14997,10 @@ var UEditor = React15.forwardRef(({
15105
14997
  const editorInstanceRef = useRef10(null);
15106
14998
  const formulaRangePickRef = useRef10(null);
15107
14999
  const formulaRangeSurfaceRef = useRef10(null);
15108
- const [formulaRangeHighlight, setFormulaRangeHighlight] = React15.useState(null);
15109
- const [isMenuBarVisible, setIsMenuBarVisible] = React15.useState(showMenuBar);
15000
+ const [formulaRangeHighlight, setFormulaRangeHighlight] = React17.useState(null);
15001
+ const [isMenuBarVisible, setIsMenuBarVisible] = React17.useState(showMenuBar);
15002
+ const widthBoundaryRef = useRef10(null);
15003
+ const editorShellRef = useRef10(null);
15110
15004
  useEffect10(() => {
15111
15005
  setIsMenuBarVisible(showMenuBar);
15112
15006
  }, [showMenuBar]);
@@ -15116,7 +15010,7 @@ var UEditor = React15.forwardRef(({
15116
15010
  onJsonChange,
15117
15011
  outputDebounceMs
15118
15012
  });
15119
- const scheduleFormulaRecalculate = React15.useCallback((editor2, options) => {
15013
+ const scheduleFormulaRecalculate = React17.useCallback((editor2, options) => {
15120
15014
  if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
15121
15015
  if (!options?.force && isEditingTableFormulaText(editor2)) return;
15122
15016
  scheduledFormulaRecalculateRef.current = true;
@@ -15150,14 +15044,15 @@ var UEditor = React15.forwardRef(({
15150
15044
  maxImageFileSize,
15151
15045
  allowedImageMimeTypes,
15152
15046
  fallbackToDataUrl,
15047
+ onImageUploadError,
15153
15048
  editable,
15154
15049
  fetchMetadata
15155
15050
  }),
15156
15051
  ...extraExtensions ?? []
15157
15052
  ],
15158
- [getPortalContainer, effectivePlaceholder, t, maxCharacters, uploadImage, resolvedUploadFile, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable, fetchMetadata, extraExtensions]
15053
+ [getPortalContainer, effectivePlaceholder, t, maxCharacters, uploadImage, resolvedUploadFile, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, onImageUploadError, editable, fetchMetadata, extraExtensions]
15159
15054
  );
15160
- const syncFormulaRangeHighlight = React15.useCallback((pickState) => {
15055
+ const syncFormulaRangeHighlight = React17.useCallback((pickState) => {
15161
15056
  const container = formulaRangeSurfaceRef.current;
15162
15057
  setFormulaRangeHighlight(container && pickState ? getFormulaRangePickHighlight(container, pickState) : null);
15163
15058
  }, []);
@@ -15229,9 +15124,6 @@ var UEditor = React15.forwardRef(({
15229
15124
  clearSelectedTableCellFormula(activeEditor);
15230
15125
  return true;
15231
15126
  }
15232
- if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
15233
- event.stopPropagation();
15234
- }
15235
15127
  return false;
15236
15128
  },
15237
15129
  click: (view, event) => {
@@ -15251,7 +15143,11 @@ var UEditor = React15.forwardRef(({
15251
15143
  }
15252
15144
  },
15253
15145
  attributes: {
15254
- class: UEDITOR_PROSEMIRROR_CLASS_NAME
15146
+ class: UEDITOR_PROSEMIRROR_CLASS_NAME,
15147
+ role: editable ? "textbox" : "document",
15148
+ ...editable ? { "aria-multiline": "true" } : { "aria-readonly": "true" },
15149
+ ...ariaLabelledBy ? { "aria-labelledby": ariaLabelledBy } : { "aria-label": ariaLabel ?? t("accessibility.editorLabel") },
15150
+ ...ariaDescribedBy ? { "aria-describedby": ariaDescribedBy } : null
15255
15151
  }
15256
15152
  },
15257
15153
  onUpdate: ({ editor: editor2, transaction, appendedTransactions }) => {
@@ -15297,23 +15193,28 @@ var UEditor = React15.forwardRef(({
15297
15193
  apply: t("tableMenu.apply"),
15298
15194
  cancel: t("imageInput.cancelBtn")
15299
15195
  });
15196
+ useWideTableLayout(editor, expandWideTables, widthBoundaryRef, editorShellRef, editorContentRef);
15300
15197
  useImperativeHandle4(
15301
15198
  ref,
15302
15199
  () => ({
15303
15200
  editor,
15304
15201
  prepareContentForSave: async ({ throwOnError = false } = {}) => {
15305
- if (!inFlightPrepareRef.current) {
15306
- const htmlSnapshot = editor?.getHTML() ?? content ?? "";
15307
- inFlightPrepareRef.current = prepareUEditorContentForSave({
15202
+ const htmlSnapshot = editor?.getHTML() ?? content ?? "";
15203
+ if (!inFlightPrepareRef.current || inFlightPrepareRef.current.html !== htmlSnapshot) {
15204
+ let trackedPromise;
15205
+ trackedPromise = prepareUEditorContentForSave({
15308
15206
  html: htmlSnapshot,
15309
15207
  uploadImageForSave,
15310
15208
  uploadFileForSave,
15311
15209
  uploadConcurrency: uploadImageConcurrency
15312
15210
  }).finally(() => {
15313
- inFlightPrepareRef.current = null;
15211
+ if (inFlightPrepareRef.current?.promise === trackedPromise) {
15212
+ inFlightPrepareRef.current = null;
15213
+ }
15314
15214
  });
15215
+ inFlightPrepareRef.current = { html: htmlSnapshot, promise: trackedPromise };
15315
15216
  }
15316
- const result = await inFlightPrepareRef.current;
15217
+ const result = await inFlightPrepareRef.current.promise;
15317
15218
  if (throwOnError && result.errors.length > 0) {
15318
15219
  throw new UEditorPrepareContentForSaveError(result);
15319
15220
  }
@@ -15352,15 +15253,18 @@ var UEditor = React15.forwardRef(({
15352
15253
  return /* @__PURE__ */ jsx20(UEditorPortalProvider, { getPortalContainer, children: /* @__PURE__ */ jsx20(
15353
15254
  "div",
15354
15255
  {
15256
+ role: "status",
15257
+ "aria-live": "polite",
15355
15258
  className: cn("w-full border bg-background flex items-center justify-center text-muted-foreground", className),
15356
15259
  style: { minHeight },
15357
15260
  children: t("loading")
15358
15261
  }
15359
15262
  ) });
15360
15263
  }
15361
- return /* @__PURE__ */ jsx20(UEditorPortalProvider, { getPortalContainer, children: /* @__PURE__ */ jsxs17(
15264
+ return /* @__PURE__ */ jsx20(UEditorPortalProvider, { getPortalContainer, children: /* @__PURE__ */ jsx20("div", { ref: widthBoundaryRef, "data-ueditor-width-boundary": "", className: "w-full", children: /* @__PURE__ */ jsxs17(
15362
15265
  "div",
15363
15266
  {
15267
+ ref: editorShellRef,
15364
15268
  className: cn(
15365
15269
  "group relative flex flex-col text-card-foreground",
15366
15270
  editable ? [
@@ -15375,7 +15279,7 @@ var UEditor = React15.forwardRef(({
15375
15279
  ),
15376
15280
  children: [
15377
15281
  editable && (showMenuBar || showToolbar || showBubbleMenu) ? /* @__PURE__ */ jsxs17(EditorUiRenderStateProvider, { editor, children: [
15378
- isMenuBarVisible && /* @__PURE__ */ jsx20(React15.Suspense, { fallback: null, children: /* @__PURE__ */ jsx20(
15282
+ isMenuBarVisible && /* @__PURE__ */ jsx20(React17.Suspense, { fallback: null, children: /* @__PURE__ */ jsx20(
15379
15283
  LazyMenuBar,
15380
15284
  {
15381
15285
  editor,
@@ -15384,6 +15288,8 @@ var UEditor = React15.forwardRef(({
15384
15288
  imageInsertMode,
15385
15289
  maxImageFileSize,
15386
15290
  allowedImageMimeTypes,
15291
+ onImageUploadError,
15292
+ fallbackToDataUrl,
15387
15293
  onSave,
15388
15294
  onExport,
15389
15295
  onSourceCode,
@@ -15402,6 +15308,8 @@ var UEditor = React15.forwardRef(({
15402
15308
  imageInsertMode,
15403
15309
  maxImageFileSize,
15404
15310
  allowedImageMimeTypes,
15311
+ onImageUploadError,
15312
+ fallbackToDataUrl,
15405
15313
  fontFamilies,
15406
15314
  defaultFontFamily,
15407
15315
  fontSizes,
@@ -15427,7 +15335,10 @@ var UEditor = React15.forwardRef(({
15427
15335
  editorContentRef.current = node;
15428
15336
  formulaRangeSurfaceRef.current = node;
15429
15337
  },
15430
- className: "relative flex-1 overflow-y-auto",
15338
+ className: cn(
15339
+ "relative flex-1 overflow-y-auto",
15340
+ expandWideTables && "[&_.tableWrapper]:!overflow-visible [&_.tableWrapper]:!pb-0"
15341
+ ),
15431
15342
  style: {
15432
15343
  fontFamily: defaultFontFamily,
15433
15344
  minHeight: editable ? minHeight : void 0,
@@ -15506,7 +15417,7 @@ var UEditor = React15.forwardRef(({
15506
15417
  showFooter && showCharacterCount && /* @__PURE__ */ jsx20(CharacterCountDisplay, { editor, maxCharacters })
15507
15418
  ]
15508
15419
  }
15509
- ) });
15420
+ ) }) });
15510
15421
  });
15511
15422
  UEditor.displayName = "UEditor";
15512
15423
  var UEditor_default = UEditor;
@@ -15518,4 +15429,4 @@ export {
15518
15429
  prepareUEditorContentForSave,
15519
15430
  UEditor_default
15520
15431
  };
15521
- //# sourceMappingURL=chunk-XTZTUDRE.js.map
15432
+ //# sourceMappingURL=chunk-57BKIC6J.js.map