@underverse-ui/underverse 2.0.28 → 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-Y4X6CAZY.js → chunk-2P7TSXEE.js} +82 -25
  4. package/dist/chunk-2P7TSXEE.js.map +1 -0
  5. package/dist/{chunk-KTOXCD6J.js → chunk-37MHVJMV.js} +1078 -90
  6. package/dist/chunk-37MHVJMV.js.map +1 -0
  7. package/dist/{chunk-QYFXYDBK.js → chunk-57BKIC6J.js} +1306 -888
  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 +3275 -1628
  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-CFIWMBAA.js → menu-bar-DPCNNUX5.js} +227 -54
  21. package/dist/menu-bar-DPCNNUX5.js.map +1 -0
  22. package/dist/ueditor.cjs +9803 -8169
  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-A4GUPKHM.js.map +0 -1
  29. package/dist/chunk-KTOXCD6J.js.map +0 -1
  30. package/dist/chunk-LRIRY4DG.js.map +0 -1
  31. package/dist/chunk-QYFXYDBK.js.map +0 -1
  32. package/dist/chunk-Y4X6CAZY.js.map +0 -1
  33. package/dist/menu-bar-CFIWMBAA.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,12 +1,13 @@
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,
8
8
  CellBorderIcon,
9
9
  ClipboardImages,
10
+ DEFAULT_RESPONSIVE_TABLE_WIDTH_BP,
10
11
  DEFAULT_TABLE_ROW_HEIGHT,
11
12
  DropdownMenu,
12
13
  DropdownMenuItem,
@@ -18,6 +19,7 @@ import {
18
19
  MIN_TABLE_ROW_HEIGHT,
19
20
  Popover,
20
21
  ROW_RESIZE_LINE_THICKNESS,
22
+ TABLE_WIDTH_BASIS_POINTS,
21
23
  TextColorIcon,
22
24
  ToolbarButton,
23
25
  UEDITOR_PROSEMIRROR_CLASS_NAME,
@@ -26,42 +28,72 @@ import {
26
28
  applyImageLayout,
27
29
  applyImageWidthPreset,
28
30
  applyTableAlignment,
31
+ buildTableFormulaDependencyGraph,
32
+ clampResponsiveTableWidthBp,
29
33
  clearTableColumnAt,
30
34
  clearTableRowAt,
35
+ deleteResponsiveColumnLayout,
31
36
  deleteSelectedImage,
32
37
  dispatchTableLayoutChange,
33
38
  duplicateTableColumnAt,
34
39
  duplicateTableRowAt,
40
+ evaluateBasicTableFormula,
35
41
  expandTableFromCell,
36
42
  findTableNodeInfoFromState,
37
43
  findTableRowNodeInfo,
44
+ formatBasisPointsAsPercentage,
45
+ formatFormulaError,
46
+ formatTableFormulaDisplayValue,
47
+ getAffectedTableFormulaLabels,
38
48
  getDefaultFontSizes,
39
49
  getDefaultLineHeights,
50
+ getLegacyTableColumnWeights,
51
+ getLogicalTableColumnCount,
40
52
  getRelativeBoundaryMetrics,
41
53
  getRelativeCellMetrics,
42
54
  getRelativeSelectedCellsMetrics,
55
+ getResponsiveTableOffsetBp,
56
+ getResponsiveTableWidthBp,
43
57
  getSelectionTableCell,
58
+ getTableCellRangeLabels,
59
+ getTableColumnRatios,
60
+ getTableFormulaRecalculationOrder,
61
+ getTableFormulaReferences,
62
+ indexToColumnName,
63
+ insertResponsiveColumnLayout,
44
64
  isCrossRealmElement,
45
65
  isCrossRealmHTMLElement,
46
66
  isCrossRealmNode,
47
67
  isCrossRealmTable,
48
68
  isCrossRealmTableCell,
49
69
  isCrossRealmTableRow,
70
+ isDraftTableFormula,
50
71
  isPointOverRenderedText,
51
72
  isRowResizeHotspot,
52
73
  isRowResizingGlobal,
53
74
  isSafeUEditorUrl,
54
75
  mergeTableCellsPreservingColumnWidths,
76
+ moveResponsiveColumnRatio,
77
+ normalizeColumnRatios,
55
78
  normalizeStyleValue,
79
+ normalizeTableFormula,
80
+ normalizeTableWidthMode,
81
+ parseColumnRatios,
82
+ parsePercentageToBasisPoints,
83
+ parseTableCellRange,
56
84
  resetImageSize,
57
85
  resolveColumnResizeTarget,
58
86
  resolveEventElement,
87
+ resolveResponsiveTableOffsetBp,
59
88
  resolveRowResizeTarget,
89
+ rewriteTableNodeFormulaReferencesForDeletion,
90
+ rewriteTableNodeFormulaReferencesForInsertion,
91
+ rewriteTableNodeFormulaReferencesForMove,
60
92
  runTableCommandAtCellPos,
61
93
  sanitizeUEditorUrl,
62
94
  useEditorColors,
63
95
  useSharedEditorUiRenderState
64
- } from "./chunk-KTOXCD6J.js";
96
+ } from "./chunk-37MHVJMV.js";
65
97
  import {
66
98
  Tooltip,
67
99
  UEditorPortalProvider,
@@ -71,7 +103,7 @@ import {
71
103
  resolveUEditorPortalContainer,
72
104
  useSmartTranslations,
73
105
  useUEditorPortalContainerGetter
74
- } from "./chunk-A4GUPKHM.js";
106
+ } from "./chunk-ZP7F4ZD7.js";
75
107
 
76
108
  // src/components/UEditor/prepare-content-for-save.ts
77
109
  var MIME_EXTENSION_MAP = {
@@ -124,16 +156,16 @@ function createFileFromDataImageUrl(dataUrl, index) {
124
156
  const name = `ueditor-image-${index + 1}.${extension}`;
125
157
  return new File([bytes], name, { type: parsed.mime });
126
158
  }
127
- function normalizeUploadResult(result) {
159
+ function normalizeUploadResult(result, kind) {
128
160
  if (typeof result === "string") {
129
- const url2 = sanitizeUEditorUrl(result, "image");
161
+ const url2 = sanitizeUEditorUrl(result, kind);
130
162
  if (!url2) throw new Error("Upload handler returned an empty URL.");
131
163
  return { url: url2 };
132
164
  }
133
165
  if (!result || typeof result !== "object") {
134
166
  throw new Error("Upload handler returned invalid result.");
135
167
  }
136
- const url = typeof result.url === "string" ? sanitizeUEditorUrl(result.url, "image") : "";
168
+ const url = typeof result.url === "string" ? sanitizeUEditorUrl(result.url, kind) : "";
137
169
  if (!url) throw new Error("Upload handler object result is missing `url`.");
138
170
  const { url: _ignoredUrl, ...rest } = result;
139
171
  const meta = Object.keys(rest).length > 0 ? rest : void 0;
@@ -188,11 +220,13 @@ function normalizeImageUrl(url) {
188
220
  }
189
221
  function replaceSrcInTag(match, nextSrc) {
190
222
  if (!match.srcAttr) return match.tag;
191
- const { start, end, quote } = match.srcAttr;
192
- const escaped = quote === '"' ? nextSrc.replace(/"/g, """) : quote === "'" ? nextSrc.replace(/'/g, "'") : nextSrc;
193
- const srcAttr = quote ? `src=${quote}${escaped}${quote}` : `src=${escaped}`;
223
+ const { start, end } = match.srcAttr;
224
+ const srcAttr = `src="${escapeHtmlAttribute(nextSrc)}"`;
194
225
  return `${match.tag.slice(0, start)}${srcAttr}${match.tag.slice(end)}`;
195
226
  }
227
+ function escapeHtmlAttribute(value) {
228
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
229
+ }
196
230
  function collectImgTagMatches(html) {
197
231
  const matches = [];
198
232
  const imgRegex = /<img\b[^>]*>/gi;
@@ -310,13 +344,14 @@ function collectFileCardMatches(html) {
310
344
  }
311
345
  function replaceFileCardAttrs(tag, nextUrl) {
312
346
  let result = tag;
347
+ const escapedUrl = escapeHtmlAttribute(nextUrl);
313
348
  const hrefRegex = /(\bhref\s*=\s*)(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+)/i;
314
349
  const dataSrcRegex = /(\bdata-src\s*=\s*)(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+)/i;
315
350
  if (hrefRegex.test(result)) {
316
- result = result.replace(hrefRegex, `$1"${nextUrl}"`);
351
+ result = result.replace(hrefRegex, `$1"${escapedUrl}"`);
317
352
  }
318
353
  if (dataSrcRegex.test(result)) {
319
- result = result.replace(dataSrcRegex, `$1"${nextUrl}"`);
354
+ result = result.replace(dataSrcRegex, `$1"${escapedUrl}"`);
320
355
  }
321
356
  return result;
322
357
  }
@@ -386,7 +421,7 @@ async function prepareUEditorContentForSave({
386
421
  try {
387
422
  const file = createFileFromDataImageUrl(candidate.src, candidate.index);
388
423
  const uploadResult = await uploadImageForSave(file);
389
- const normalized = normalizeUploadResult(uploadResult);
424
+ const normalized = normalizeUploadResult(uploadResult, "image");
390
425
  return { candidate, file, ...normalized };
391
426
  } catch (error) {
392
427
  return { candidate, error: getErrorReason(error) };
@@ -433,7 +468,7 @@ async function prepareUEditorContentForSave({
433
468
  try {
434
469
  const file = createFileFromDataUrl(candidate.src, candidate.fileName);
435
470
  const uploadResult = await uploadFn(file);
436
- const normalized = normalizeUploadResult(uploadResult);
471
+ const normalized = normalizeUploadResult(uploadResult, "file");
437
472
  return { candidate, file, ...normalized };
438
473
  } catch (error) {
439
474
  return { candidate, error: getErrorReason(error) };
@@ -486,7 +521,7 @@ async function prepareUEditorContentForSave({
486
521
  }
487
522
 
488
523
  // src/components/UEditor/UEditor.tsx
489
- 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";
490
525
  import { useEditor, EditorContent } from "@tiptap/react";
491
526
 
492
527
  // src/components/UEditor/extensions.ts
@@ -521,7 +556,7 @@ import { NodeViewWrapper, NodeViewContent } from "@tiptap/react";
521
556
  import React from "react";
522
557
  import { jsx } from "react/jsx-runtime";
523
558
  var LazyBaseEmojiPicker = React.lazy(async () => {
524
- const { EmojiPicker: EmojiPicker2 } = await import("./EmojiPicker-NV3GQYWI.js");
559
+ const { EmojiPicker: EmojiPicker2 } = await import("./EmojiPicker-ISJ6RXYG.js");
525
560
  return { default: EmojiPicker2 };
526
561
  });
527
562
  var EmojiPicker = ({ onSelect }) => {
@@ -558,7 +593,8 @@ var CalloutView = ({ node, updateAttributes, selected }) => {
558
593
  "button",
559
594
  {
560
595
  type: "button",
561
- 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",
562
598
  children: currentEmoji
563
599
  }
564
600
  ),
@@ -572,9 +608,11 @@ var CalloutView = ({ node, updateAttributes, selected }) => {
572
608
  {
573
609
  type: "button",
574
610
  title: col.name,
611
+ "aria-label": `${t("callout.backgroundColor")}: ${col.name}`,
612
+ "aria-pressed": currentBgColor === col.color,
575
613
  onClick: () => updateAttributes({ backgroundColor: col.color }),
576
614
  className: cn(
577
- "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",
578
616
  currentBgColor === col.color ? "border-primary ring-2 ring-primary/30" : "border-border/60"
579
617
  ),
580
618
  style: { backgroundColor: col.color }
@@ -1062,12 +1100,13 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
1062
1100
  e.preventDefault();
1063
1101
  e.stopPropagation();
1064
1102
  if (!safeSrc) return;
1065
- const link = document.createElement("a");
1103
+ const ownerDocument = editor.view.dom.ownerDocument;
1104
+ const link = ownerDocument.createElement("a");
1066
1105
  link.href = safeSrc;
1067
1106
  link.download = fileName || "download";
1068
- document.body.appendChild(link);
1107
+ ownerDocument.body.appendChild(link);
1069
1108
  link.click();
1070
- document.body.removeChild(link);
1109
+ ownerDocument.body.removeChild(link);
1071
1110
  };
1072
1111
  const hasInlineUpload = safeSrc.startsWith("data:") && Boolean(uploadFn);
1073
1112
  const handleRetryUpload = (event) => {
@@ -1081,18 +1120,19 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
1081
1120
  NodeViewWrapper3,
1082
1121
  {
1083
1122
  className: cn(
1084
- "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",
1085
1124
  selected && "ring-2 ring-primary/45 border-primary/20 shadow-sm"
1086
1125
  ),
1087
- onClick: handleDownload,
1126
+ role: "group",
1127
+ "aria-label": fileName || t("fileCard.unnamed"),
1088
1128
  children: [
1089
1129
  /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3.5 min-w-0", children: [
1090
- /* @__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" }) }),
1091
1131
  /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
1092
1132
  /* @__PURE__ */ jsx4("div", { className: "text-sm font-semibold text-foreground truncate break-all", children: fileName || t("fileCard.unnamed") }),
1093
1133
  /* @__PURE__ */ jsxs3("div", { className: "text-xs text-muted-foreground flex items-center gap-2 mt-0.5", children: [
1094
1134
  fileSize ? /* @__PURE__ */ jsx4("span", { children: formatBytes(fileSize) }) : null,
1095
- hasInlineUpload && /* @__PURE__ */ jsx4("span", { className: cn(
1135
+ hasInlineUpload && /* @__PURE__ */ jsx4("span", { "aria-live": "polite", className: cn(
1096
1136
  "font-medium truncate max-w-50",
1097
1137
  uploadError ? "text-destructive" : "text-primary animate-pulse"
1098
1138
  ), children: uploadError ? `${t("fileCard.uploadFailed") || "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"}: ${uploadError}` : t("fileCard.uploading") }),
@@ -1103,7 +1143,7 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
1103
1143
  onClick: handleRetryUpload,
1104
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",
1105
1145
  children: [
1106
- /* @__PURE__ */ jsx4(RefreshCw2, { className: "h-3 w-3" }),
1146
+ /* @__PURE__ */ jsx4(RefreshCw2, { "aria-hidden": "true", className: "h-3 w-3" }),
1107
1147
  t("fileCard.retryUpload")
1108
1148
  ]
1109
1149
  }
@@ -1116,8 +1156,9 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
1116
1156
  {
1117
1157
  type: "button",
1118
1158
  onClick: handleDownload,
1119
- 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",
1120
- 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" })
1121
1162
  }
1122
1163
  )
1123
1164
  ]
@@ -1375,6 +1416,7 @@ import { Node as Node5, mergeAttributes as mergeAttributes5 } from "@tiptap/core
1375
1416
  import { ReactNodeViewRenderer as ReactNodeViewRenderer5 } from "@tiptap/react";
1376
1417
 
1377
1418
  // src/components/UEditor/FormRadioView.tsx
1419
+ import { useId } from "react";
1378
1420
  import { NodeViewWrapper as NodeViewWrapper5 } from "@tiptap/react";
1379
1421
  import { Settings } from "lucide-react";
1380
1422
  import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
@@ -1383,6 +1425,8 @@ var FormRadioView = ({ node, updateAttributes, editor, selected }) => {
1383
1425
  const isChecked = !!node.attrs.checked;
1384
1426
  const isEditable = editor.isEditable;
1385
1427
  const groupName = node.attrs.name || "radio-group";
1428
+ const groupInputId = useId();
1429
+ const radioLabel = `${t("menubar.formRadio")} \u2014 ${node.attrs.value || groupName}`;
1386
1430
  const handleSelect = (e) => {
1387
1431
  e.preventDefault();
1388
1432
  e.stopPropagation();
@@ -1408,6 +1452,7 @@ var FormRadioView = ({ node, updateAttributes, editor, selected }) => {
1408
1452
  {
1409
1453
  role: "radio",
1410
1454
  "aria-checked": isChecked,
1455
+ "aria-label": radioLabel,
1411
1456
  tabIndex: isEditable ? 0 : -1,
1412
1457
  onClick: handleSelect,
1413
1458
  onKeyDown: handleKeyDown2,
@@ -1436,17 +1481,21 @@ var FormRadioView = ({ node, updateAttributes, editor, selected }) => {
1436
1481
  "button",
1437
1482
  {
1438
1483
  type: "button",
1484
+ "aria-label": t("radio.editGroup") || "Edit Group",
1439
1485
  className: "p-0.5 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors outline-none cursor-pointer",
1440
1486
  title: t("radio.editGroup") || "Edit Group",
1441
1487
  children: /* @__PURE__ */ jsx6(Settings, { className: "w-3.5 h-3.5" })
1442
1488
  }
1443
1489
  ),
1444
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: [
1445
- /* @__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" }),
1446
1492
  /* @__PURE__ */ jsx6(
1447
1493
  "input",
1448
1494
  {
1449
1495
  type: "text",
1496
+ id: groupInputId,
1497
+ name: "radio-group-name",
1498
+ autoComplete: "off",
1450
1499
  value: groupName,
1451
1500
  onChange: (e) => updateAttributes({ name: e.target.value }),
1452
1501
  className: `w-full rounded-lg bg-background px-2 py-1.5 text-sm text-foreground ${formControlOutlineClass}`
@@ -1461,6 +1510,12 @@ var FormRadioView = ({ node, updateAttributes, editor, selected }) => {
1461
1510
  };
1462
1511
 
1463
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
+ }
1464
1519
  var FormRadio = Node5.create({
1465
1520
  name: "formRadio",
1466
1521
  group: "inline",
@@ -1493,7 +1548,7 @@ var FormRadio = Node5.create({
1493
1548
  },
1494
1549
  id: {
1495
1550
  default: null,
1496
- 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(),
1497
1552
  renderHTML: (attributes) => {
1498
1553
  if (!attributes.id) return {};
1499
1554
  return {
@@ -1552,10 +1607,15 @@ var FormRadio = Node5.create({
1552
1607
  setFormRadio: (attributes) => ({ commands }) => {
1553
1608
  return commands.insertContent({
1554
1609
  type: this.name,
1555
- attrs: attributes
1610
+ attrs: {
1611
+ ...attributes,
1612
+ id: attributes?.id || createFormRadioId(),
1613
+ name: attributes?.name || "radio-group"
1614
+ }
1556
1615
  });
1557
1616
  },
1558
1617
  checkFormRadio: (id) => ({ tr, dispatch }) => {
1618
+ if (!id) return false;
1559
1619
  if (!dispatch) return true;
1560
1620
  let targetGroupName = "";
1561
1621
  let targetPos = -1;
@@ -1607,7 +1667,7 @@ import { Gapcursor } from "@tiptap/extension-gapcursor";
1607
1667
  import { Extension } from "@tiptap/core";
1608
1668
  import { Suggestion } from "@tiptap/suggestion";
1609
1669
  import { ReactRenderer } from "@tiptap/react";
1610
- 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";
1611
1671
  import {
1612
1672
  FileCode,
1613
1673
  Heading1,
@@ -1692,9 +1752,9 @@ var DEFAULT_MESSAGES = {
1692
1752
  formRadioDesc: "Insert an interactive radio button field"
1693
1753
  };
1694
1754
  function useResettingIndex(resetToken) {
1695
- const [state, setState] = React4.useState({ resetToken, index: 0 });
1755
+ const [state, setState] = React5.useState({ resetToken, index: 0 });
1696
1756
  const selectedIndex = Object.is(state.resetToken, resetToken) ? state.index : 0;
1697
- const setSelectedIndex = React4.useCallback((nextIndex) => {
1757
+ const setSelectedIndex = React5.useCallback((nextIndex) => {
1698
1758
  setState((prev) => {
1699
1759
  const prevIndex = Object.is(prev.resetToken, resetToken) ? prev.index : 0;
1700
1760
  return {
@@ -1910,7 +1970,7 @@ function buildSlashCommandItems({
1910
1970
  title: messages.formRadio,
1911
1971
  description: messages.formRadioDesc,
1912
1972
  command: run(({ editor, range }) => {
1913
- getCommandChain(editor, range).setFormRadio({ name: `radio-group-${Math.random().toString(36).substring(2, 6)}` }).run();
1973
+ getCommandChain(editor, range).setFormRadio().run();
1914
1974
  })
1915
1975
  }
1916
1976
  ].filter((item) => {
@@ -1922,6 +1982,7 @@ function buildSlashCommandItems({
1922
1982
  var SlashCommandList = forwardRef((props, ref) => {
1923
1983
  const [selectedIndex, setSelectedIndex] = useResettingIndex(props.items);
1924
1984
  const listRef = useRef2(null);
1985
+ const listId = React5.useId();
1925
1986
  useEffect3(() => {
1926
1987
  const selectedElement = listRef.current?.querySelector(`[data-index="${selectedIndex}"]`);
1927
1988
  selectedElement?.scrollIntoView({ block: "nearest" });
@@ -1952,41 +2013,56 @@ var SlashCommandList = forwardRef((props, ref) => {
1952
2013
  }
1953
2014
  }));
1954
2015
  if (props.items.length === 0) {
1955
- 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 });
1956
2017
  }
1957
- 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: [
1958
- /* @__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 }) }),
1959
- /* @__PURE__ */ jsx7("div", { className: "p-1", children: props.items.map((item, index) => /* @__PURE__ */ jsxs5(
1960
- "button",
1961
- {
1962
- type: "button",
1963
- "data-index": index,
1964
- onMouseDown: (event) => event.preventDefault(),
1965
- onClick: () => props.command(item),
1966
- className: cn(
1967
- "flex items-center w-full px-3 py-2.5 rounded-lg transition-colors group",
1968
- selectedIndex === index ? "bg-accent" : "hover:bg-accent/50"
1969
- ),
1970
- children: [
1971
- /* @__PURE__ */ jsx7(
1972
- "div",
1973
- {
1974
- className: cn(
1975
- "flex items-center justify-center w-10 h-10 rounded-lg mr-3 transition-colors",
1976
- 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
+ }
1977
2054
  ),
1978
- children: /* @__PURE__ */ jsx7(item.icon, { className: cn("w-5 h-5", selectedIndex === index ? "text-primary" : "text-muted-foreground") })
1979
- }
1980
- ),
1981
- /* @__PURE__ */ jsxs5("div", { className: "text-left", children: [
1982
- /* @__PURE__ */ jsx7("div", { className: cn("text-sm font-medium", selectedIndex === index && "text-primary"), children: item.title }),
1983
- /* @__PURE__ */ jsx7("div", { className: "text-xs text-muted-foreground", children: item.description })
1984
- ] })
1985
- ]
1986
- },
1987
- item.title
1988
- )) })
1989
- ] });
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
+ );
1990
2066
  });
1991
2067
  SlashCommandList.displayName = "SlashCommandList";
1992
2068
  var SlashCommand = Extension.create({
@@ -2070,13 +2146,13 @@ import { Extension as Extension2 } from "@tiptap/core";
2070
2146
  import { Suggestion as Suggestion2 } from "@tiptap/suggestion";
2071
2147
  import { ReactRenderer as ReactRenderer2 } from "@tiptap/react";
2072
2148
  import { PluginKey as PluginKey2 } from "@tiptap/pm/state";
2073
- import React5, { forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2 } from "react";
2149
+ import React6, { forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2 } from "react";
2074
2150
  import { Smile } from "lucide-react";
2075
2151
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
2076
2152
  function useResettingIndex2(resetToken) {
2077
- const [state, setState] = React5.useState({ resetToken, index: 0 });
2153
+ const [state, setState] = React6.useState({ resetToken, index: 0 });
2078
2154
  const selectedIndex = Object.is(state.resetToken, resetToken) ? state.index : 0;
2079
- const setSelectedIndex = React5.useCallback((nextIndex) => {
2155
+ const setSelectedIndex = React6.useCallback((nextIndex) => {
2080
2156
  setState((prev) => {
2081
2157
  const prevIndex = Object.is(prev.resetToken, resetToken) ? prev.index : 0;
2082
2158
  return {
@@ -2090,6 +2166,7 @@ function useResettingIndex2(resetToken) {
2090
2166
  var EmojiList = forwardRef2((props, ref) => {
2091
2167
  const t = useSmartTranslations("UEditor");
2092
2168
  const [selectedIndex, setSelectedIndex] = useResettingIndex2(props.items);
2169
+ const listId = React6.useId();
2093
2170
  const showingCountLabel = formatEmojiCountLabel(t("emojiSuggestion.showingCount"), 64, props.items.length);
2094
2171
  useImperativeHandle2(ref, () => ({
2095
2172
  onKeyDown: (keyProps) => {
@@ -2122,28 +2199,42 @@ var EmojiList = forwardRef2((props, ref) => {
2122
2199
  }
2123
2200
  }));
2124
2201
  if (props.items.length === 0) {
2125
- 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") });
2126
- }
2127
- return /* @__PURE__ */ jsxs6("div", { className: "w-80 max-h-80 overflow-y-auto bg-card border border-border/50 rounded-2xl shadow-lg", children: [
2128
- /* @__PURE__ */ jsx8("div", { className: "px-3 py-2 border-b bg-muted/30", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
2129
- /* @__PURE__ */ jsx8(Smile, { className: "w-4 h-4 text-primary" }),
2130
- /* @__PURE__ */ jsx8("span", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: t("emojiSuggestion.title") })
2131
- ] }) }),
2132
- /* @__PURE__ */ jsxs6("div", { className: "p-3", children: [
2133
- /* @__PURE__ */ jsx8("div", { className: "grid grid-cols-8 gap-1", children: props.items.slice(0, 64).map((item, index) => /* @__PURE__ */ jsx8(
2134
- EmojiGridButton,
2135
- {
2136
- emoji: item.emoji,
2137
- name: item.name,
2138
- onClick: () => props.command(item),
2139
- active: selectedIndex === index,
2140
- className: "text-xl"
2141
- },
2142
- item.name
2143
- )) }),
2144
- props.items.length > 64 && /* @__PURE__ */ jsx8("div", { className: "mt-2 text-xs text-center text-muted-foreground", children: showingCountLabel })
2145
- ] })
2146
- ] });
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
+ );
2147
2238
  });
2148
2239
  EmojiList.displayName = "EmojiList";
2149
2240
  var allEmojiItemsPromise = null;
@@ -2233,7 +2324,7 @@ import { Extension as Extension3 } from "@tiptap/core";
2233
2324
  import { Suggestion as Suggestion3 } from "@tiptap/suggestion";
2234
2325
  import { ReactRenderer as ReactRenderer3 } from "@tiptap/react";
2235
2326
  import { PluginKey as PluginKey3 } from "@tiptap/pm/state";
2236
- import React6, { forwardRef as forwardRef3, useImperativeHandle as useImperativeHandle3 } from "react";
2327
+ import React7, { forwardRef as forwardRef3, useImperativeHandle as useImperativeHandle3 } from "react";
2237
2328
  import { Sigma } from "lucide-react";
2238
2329
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2239
2330
  var FORMULA_FUNCTIONS = [
@@ -2288,8 +2379,9 @@ function isFormulaFunctionSuggestionQuery(query) {
2288
2379
  }
2289
2380
  var FormulaSuggestionList = forwardRef3((props, ref) => {
2290
2381
  const t = useSmartTranslations("UEditor");
2291
- const [selectedIndex, setSelectedIndex] = React6.useState(0);
2292
- React6.useEffect(() => {
2382
+ const [selectedIndex, setSelectedIndex] = React7.useState(0);
2383
+ const listId = React7.useId();
2384
+ React7.useEffect(() => {
2293
2385
  setSelectedIndex(0);
2294
2386
  }, [props.items]);
2295
2387
  useImperativeHandle3(ref, () => ({
@@ -2314,32 +2406,46 @@ var FormulaSuggestionList = forwardRef3((props, ref) => {
2314
2406
  }
2315
2407
  }));
2316
2408
  if (props.items.length === 0) {
2317
- 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") });
2318
- }
2319
- return /* @__PURE__ */ jsxs7("div", { className: "w-72 max-h-80 overflow-y-auto bg-card border border-border/50 rounded-lg shadow-lg", children: [
2320
- /* @__PURE__ */ jsx9("div", { className: "px-3 py-2 border-b bg-muted/30", children: /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
2321
- /* @__PURE__ */ jsx9(Sigma, { className: "w-4 h-4 text-primary" }),
2322
- /* @__PURE__ */ jsx9("span", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: t("formulaSuggestion.title") })
2323
- ] }) }),
2324
- /* @__PURE__ */ jsx9("div", { className: "p-1.5", children: props.items.map((item, index) => /* @__PURE__ */ jsxs7(
2325
- "button",
2326
- {
2327
- type: "button",
2328
- 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"}`,
2329
- onMouseEnter: () => setSelectedIndex(index),
2330
- onMouseDown: (event) => event.preventDefault(),
2331
- onClick: () => props.command(item),
2332
- children: [
2333
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-3", children: [
2334
- /* @__PURE__ */ jsx9("span", { className: "font-mono text-sm font-semibold", children: item.name }),
2335
- /* @__PURE__ */ jsx9("span", { className: "font-mono text-xs text-muted-foreground", children: item.signature })
2336
- ] }),
2337
- /* @__PURE__ */ jsx9("div", { className: "mt-0.5 text-xs text-muted-foreground", children: t(item.descriptionKey) })
2338
- ]
2339
- },
2340
- item.name
2341
- )) })
2342
- ] });
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
+ );
2343
2449
  });
2344
2450
  FormulaSuggestionList.displayName = "FormulaSuggestionList";
2345
2451
  function insertFormulaFunction(editor, range, item) {
@@ -3339,8 +3445,18 @@ var indent_default = Indent;
3339
3445
  // src/components/UEditor/table-align.ts
3340
3446
  import { mergeAttributes as mergeAttributes7 } from "@tiptap/core";
3341
3447
  import { Table as Table2 } from "@tiptap/extension-table";
3342
- import { Plugin as Plugin4, PluginKey as PluginKey5 } from "@tiptap/pm/state";
3343
- import { tableEditing } from "@tiptap/pm/tables";
3448
+ import { Plugin as Plugin4, PluginKey as PluginKey5, TextSelection } from "@tiptap/pm/state";
3449
+ import {
3450
+ addColumnAfter as addColumnAfterCommand,
3451
+ addColumnBefore as addColumnBeforeCommand,
3452
+ addRowAfter as addRowAfterCommand,
3453
+ addRowBefore as addRowBeforeCommand,
3454
+ deleteColumn as deleteColumnCommand,
3455
+ deleteRow as deleteRowCommand,
3456
+ selectedRect,
3457
+ tableEditing,
3458
+ tableNodeTypes as tableNodeTypes2
3459
+ } from "@tiptap/pm/tables";
3344
3460
 
3345
3461
  // src/components/UEditor/table-column-resize.ts
3346
3462
  import { Plugin as Plugin3 } from "@tiptap/pm/state";
@@ -3361,14 +3477,10 @@ function getColumnResizeMinWidth(configuredMinWidth) {
3361
3477
  const normalizedMinWidth = Number.isFinite(configuredMinWidth) && configuredMinWidth > 0 ? Math.round(configuredMinWidth) : MIN_RESIZED_TABLE_COLUMN_WIDTH;
3362
3478
  return Math.max(MIN_RESIZED_TABLE_COLUMN_WIDTH, normalizedMinWidth);
3363
3479
  }
3364
- function setColumnStyle(column, width) {
3365
- if (width == null) {
3366
- column.style.width = `${DEFAULT_TABLE_COLUMN_WIDTH}px`;
3367
- column.style.minWidth = `${DEFAULT_TABLE_COLUMN_WIDTH}px`;
3368
- return;
3369
- }
3370
- column.style.width = `${Math.max(width, MIN_RESIZED_TABLE_COLUMN_WIDTH)}px`;
3371
- column.style.minWidth = "";
3480
+ function setColumnStyle(column, width, ratio, explicit, responsive) {
3481
+ column.style.width = responsive ? formatBasisPointsAsPercentage(ratio) : `${width}px`;
3482
+ column.style.minWidth = responsive || explicit ? "" : `${width}px`;
3483
+ column.setAttribute("width", String(width));
3372
3484
  }
3373
3485
  function isTableColumnElement(node) {
3374
3486
  return isCrossRealmElement(node) && String(node.tagName).toUpperCase() === "COL";
@@ -3377,6 +3489,7 @@ function updateDynamicColumns(node, colgroup, table, ownerDocument, overrideCol,
3377
3489
  let totalWidth = 0;
3378
3490
  let nextDOM = colgroup.firstChild;
3379
3491
  const row = node.firstChild;
3492
+ const columns = [];
3380
3493
  if (row) {
3381
3494
  for (let rowCellIndex = 0, col = 0; rowCellIndex < row.childCount; rowCellIndex += 1) {
3382
3495
  const { colspan, colwidth } = row.child(rowCellIndex).attrs;
@@ -3385,7 +3498,11 @@ function updateDynamicColumns(node, colgroup, table, ownerDocument, overrideCol,
3385
3498
  const width = rawWidth ? Math.max(rawWidth, MIN_RESIZED_TABLE_COLUMN_WIDTH) : null;
3386
3499
  totalWidth += width ?? DEFAULT_TABLE_COLUMN_WIDTH;
3387
3500
  const colElement = isTableColumnElement(nextDOM) ? nextDOM : colgroup.appendChild(ownerDocument.createElement("col"));
3388
- setColumnStyle(colElement, width);
3501
+ columns.push({
3502
+ element: colElement,
3503
+ explicit: width !== null,
3504
+ width: width ?? DEFAULT_TABLE_COLUMN_WIDTH
3505
+ });
3389
3506
  nextDOM = colElement.nextSibling;
3390
3507
  }
3391
3508
  }
@@ -3395,13 +3512,39 @@ function updateDynamicColumns(node, colgroup, table, ownerDocument, overrideCol,
3395
3512
  nextDOM.parentNode?.removeChild(nextDOM);
3396
3513
  nextDOM = after;
3397
3514
  }
3398
- const hasUserWidth = typeof node.attrs.style === "string" && /\bwidth\s*:/i.test(node.attrs.style);
3399
- if (!hasUserWidth) {
3515
+ const responsive = normalizeTableWidthMode(node.attrs.widthMode) === "responsive";
3516
+ const columnRatios = getTableColumnRatios(node);
3517
+ columns.forEach(({ element, explicit, width }, index) => {
3518
+ setColumnStyle(element, width, columnRatios[index] ?? 1, explicit, responsive);
3519
+ });
3520
+ if (responsive) {
3521
+ const widthBp = getResponsiveTableWidthBp(node);
3522
+ const offsetBp = getResponsiveTableOffsetBp(node);
3523
+ table.setAttribute("data-table-width-mode", "responsive");
3524
+ table.setAttribute("data-table-width", formatBasisPointsAsPercentage(widthBp));
3525
+ table.setAttribute("data-table-width-bp", String(widthBp));
3526
+ table.setAttribute("data-table-offset", formatBasisPointsAsPercentage(offsetBp));
3527
+ table.setAttribute("data-table-offset-bp", String(offsetBp));
3528
+ table.setAttribute("data-table-column-ratios", columnRatios.join(","));
3529
+ table.style.width = formatBasisPointsAsPercentage(widthBp);
3530
+ table.style.marginLeft = formatBasisPointsAsPercentage(offsetBp);
3531
+ table.style.marginRight = "auto";
3532
+ table.style.minWidth = `${Math.max(1, colgroup.childElementCount) * MIN_RESIZED_TABLE_COLUMN_WIDTH}px`;
3533
+ } else {
3534
+ table.removeAttribute("data-table-width-mode");
3535
+ table.removeAttribute("data-table-width");
3536
+ table.removeAttribute("data-table-width-bp");
3537
+ table.removeAttribute("data-table-offset");
3538
+ table.removeAttribute("data-table-offset-bp");
3539
+ table.removeAttribute("data-table-column-ratios");
3400
3540
  table.style.width = `${totalWidth}px`;
3401
3541
  table.style.minWidth = "";
3402
- } else {
3403
- table.style.minWidth = `${totalWidth}px`;
3542
+ const tableAlign = node.attrs.textAlign;
3543
+ table.style.marginLeft = tableAlign === "center" || tableAlign === "right" ? "auto" : "0px";
3544
+ table.style.marginRight = tableAlign === "center" ? "auto" : tableAlign === "right" ? "0px" : "auto";
3404
3545
  }
3546
+ if (node.attrs.textAlign) table.setAttribute("data-table-align", String(node.attrs.textAlign));
3547
+ else table.removeAttribute("data-table-align");
3405
3548
  }
3406
3549
  var UEditorTableView = class {
3407
3550
  constructor(node, _defaultColumnWidth, maybeView) {
@@ -3437,7 +3580,40 @@ var UEditorTableView = class {
3437
3580
  };
3438
3581
  function getDraggedWidth(dragging, event) {
3439
3582
  const offset = event.clientX - dragging.startX;
3440
- return Math.max(dragging.minWidth, Math.round(dragging.startWidth + offset));
3583
+ const maximum = dragging.neighborStartWidth === void 0 ? Number.POSITIVE_INFINITY : dragging.startWidth + dragging.neighborStartWidth - dragging.minWidth;
3584
+ return Math.min(maximum, Math.max(dragging.minWidth, Math.round(dragging.startWidth + offset)));
3585
+ }
3586
+ function normalizeColumnWidthsToTotal(values, total, minimum) {
3587
+ const safeTotal = Math.max(values.length * minimum, Math.round(total));
3588
+ const weights = values.map((value) => Number.isFinite(value) && value > 0 ? value : DEFAULT_TABLE_COLUMN_WIDTH);
3589
+ const weightSum = weights.reduce((sum, value) => sum + value, 0);
3590
+ const widths = weights.map((value) => Math.max(minimum, Math.round(value / weightSum * safeTotal)));
3591
+ let difference = safeTotal - widths.reduce((sum, value) => sum + value, 0);
3592
+ while (difference !== 0) {
3593
+ let changed = false;
3594
+ for (let index = widths.length - 1; index >= 0 && difference !== 0; index -= 1) {
3595
+ if (difference < 0 && widths[index] <= minimum) continue;
3596
+ widths[index] += difference > 0 ? 1 : -1;
3597
+ difference += difference > 0 ? -1 : 1;
3598
+ changed = true;
3599
+ }
3600
+ if (!changed) break;
3601
+ }
3602
+ return widths;
3603
+ }
3604
+ function getResizeColumnInfo(state, cell) {
3605
+ const $cell = state.doc.resolve(cell);
3606
+ const table = $cell.node(-1);
3607
+ const map = TableMap.get(table);
3608
+ const start = $cell.start(-1);
3609
+ const nodeAfter = $cell.nodeAfter;
3610
+ if (!nodeAfter) return null;
3611
+ return {
3612
+ col: map.colCount($cell.pos - start) + nodeAfter.attrs.colspan - 1,
3613
+ map,
3614
+ start,
3615
+ table
3616
+ };
3441
3617
  }
3442
3618
  function getCurrentColWidth(view, cellPos, { colspan, colwidth }) {
3443
3619
  const width = colwidth?.[colwidth.length - 1];
@@ -3506,6 +3682,12 @@ function handleMouseMove(view, event, handleWidth, lastColumnResizable) {
3506
3682
  if (event.clientX - left <= handleWidth) cell = edgeCell(view, event, "left", handleWidth);
3507
3683
  else if (right - event.clientX <= handleWidth) cell = edgeCell(view, event, "right", handleWidth);
3508
3684
  }
3685
+ if (cell !== -1) {
3686
+ const info = getResizeColumnInfo(view.state, cell);
3687
+ if (info && normalizeTableWidthMode(info.table.attrs.widthMode) === "responsive" && info.col === info.map.width - 1) {
3688
+ cell = -1;
3689
+ }
3690
+ }
3509
3691
  if (cell === pluginState.activeHandle) {
3510
3692
  clearHandleHoverTimer();
3511
3693
  return;
@@ -3552,33 +3734,47 @@ function handleMouseLeave(view) {
3552
3734
  updateHandle(view, -1);
3553
3735
  }
3554
3736
  }
3555
- function updateColumnWidth(view, cell, width) {
3556
- const $cell = view.state.doc.resolve(cell);
3557
- const table = $cell.node(-1);
3558
- const map = TableMap.get(table);
3559
- const start = $cell.start(-1);
3560
- const nodeAfter = $cell.nodeAfter;
3561
- if (!nodeAfter) return;
3562
- const col = map.colCount($cell.pos - start) + nodeAfter.attrs.colspan - 1;
3737
+ function updateColumnWidths(view, cell, widthsByColumn, columnRatios) {
3738
+ const info = getResizeColumnInfo(view.state, cell);
3739
+ if (!info) return;
3740
+ const { map, start, table } = info;
3563
3741
  const tr = view.state.tr;
3564
- for (let row = 0; row < map.height; row += 1) {
3565
- const mapIndex = row * map.width + col;
3566
- if (row && map.map[mapIndex] === map.map[mapIndex - map.width]) continue;
3567
- const pos = map.map[mapIndex];
3742
+ const seenCellPositions = /* @__PURE__ */ new Set();
3743
+ for (const pos of map.map) {
3744
+ if (seenCellPositions.has(pos)) continue;
3745
+ seenCellPositions.add(pos);
3568
3746
  const cellNode = table.nodeAt(pos);
3569
3747
  if (!cellNode) continue;
3570
3748
  const attrs = cellNode.attrs;
3571
- const index = attrs.colspan === 1 ? 0 : col - map.colCount(pos);
3572
- if (attrs.colwidth?.[index] === width) continue;
3573
3749
  const colwidth = attrs.colwidth ? attrs.colwidth.slice() : Array.from({ length: attrs.colspan }, () => 0);
3574
- colwidth[index] = width;
3750
+ const cellStartColumn = map.colCount(pos);
3751
+ let changed = false;
3752
+ for (let index = 0; index < attrs.colspan; index += 1) {
3753
+ const width = widthsByColumn.get(cellStartColumn + index);
3754
+ if (width === void 0 || colwidth[index] === width) continue;
3755
+ colwidth[index] = width;
3756
+ changed = true;
3757
+ }
3758
+ if (!changed) continue;
3575
3759
  tr.setNodeMarkup(start + pos, null, {
3576
3760
  ...attrs,
3577
3761
  colwidth
3578
3762
  });
3579
3763
  }
3764
+ if (columnRatios) {
3765
+ tr.setNodeMarkup(start - 1, void 0, {
3766
+ ...table.attrs,
3767
+ widthMode: "responsive",
3768
+ columnRatios: normalizeColumnRatios(columnRatios, map.width)
3769
+ });
3770
+ }
3580
3771
  if (tr.docChanged) view.dispatch(tr);
3581
3772
  }
3773
+ function updateColumnWidth(view, cell, width) {
3774
+ const info = getResizeColumnInfo(view.state, cell);
3775
+ if (!info) return;
3776
+ updateColumnWidths(view, cell, /* @__PURE__ */ new Map([[info.col, width]]));
3777
+ }
3582
3778
  function getActiveDragging(state) {
3583
3779
  const dragging = columnResizingPluginKey.getState(state)?.dragging;
3584
3780
  return dragging ? dragging : null;
@@ -3630,16 +3826,36 @@ function handleMouseDown(view, event, cellMinWidth) {
3630
3826
  if (!pluginState || pluginState.activeHandle === -1 || pluginState.dragging) return false;
3631
3827
  const cell = view.state.doc.nodeAt(pluginState.activeHandle);
3632
3828
  if (!cell) return false;
3829
+ const resizeInfo = getResizeColumnInfo(view.state, pluginState.activeHandle);
3830
+ if (!resizeInfo) return false;
3633
3831
  const attrs = cell.attrs;
3634
- const width = getCurrentColWidth(view, pluginState.activeHandle, {
3832
+ let width = getCurrentColWidth(view, pluginState.activeHandle, {
3635
3833
  colspan: attrs.colspan ?? 1,
3636
3834
  colwidth: attrs.colwidth
3637
3835
  });
3638
3836
  const minWidth = getColumnResizeMinWidth(cellMinWidth);
3837
+ let responsiveColumnWidths;
3838
+ let neighborStartWidth;
3839
+ if (normalizeTableWidthMode(resizeInfo.table.attrs.widthMode) === "responsive") {
3840
+ if (resizeInfo.col >= resizeInfo.map.width - 1) return false;
3841
+ const storedRatios = getTableColumnRatios(resizeInfo.table);
3842
+ const legacyWidths = getLegacyTableColumnWeights(resizeInfo.table, DEFAULT_TABLE_COLUMN_WIDTH);
3843
+ const storedTotal = legacyWidths.reduce((sum, value) => sum + value, 0);
3844
+ const tableElement = getTableElementAtCell(view, pluginState.activeHandle);
3845
+ const renderedWidth = tableElement?.getBoundingClientRect().width || storedTotal;
3846
+ responsiveColumnWidths = normalizeColumnWidthsToTotal(storedRatios, renderedWidth, minWidth);
3847
+ width = responsiveColumnWidths[resizeInfo.col];
3848
+ neighborStartWidth = responsiveColumnWidths[resizeInfo.col + 1];
3849
+ }
3639
3850
  const dragging = {
3640
3851
  startX: event.clientX,
3641
3852
  startWidth: width,
3642
- minWidth
3853
+ minWidth,
3854
+ ...responsiveColumnWidths ? {
3855
+ columnIndex: resizeInfo.col,
3856
+ responsiveColumnWidths,
3857
+ neighborStartWidth
3858
+ } : null
3643
3859
  };
3644
3860
  view.dispatch(view.state.tr.setMeta(columnResizingPluginKey, { setDragging: dragging }));
3645
3861
  function finish(nextEvent) {
@@ -3648,7 +3864,20 @@ function handleMouseDown(view, event, cellMinWidth) {
3648
3864
  const activeDragging = getActiveDragging(view.state);
3649
3865
  const activeHandle = columnResizingPluginKey.getState(view.state)?.activeHandle ?? -1;
3650
3866
  if (activeDragging && activeHandle > -1) {
3651
- updateColumnWidth(view, activeHandle, getDraggedWidth(activeDragging, nextEvent));
3867
+ const nextWidth = getDraggedWidth(activeDragging, nextEvent);
3868
+ if (activeDragging.responsiveColumnWidths && activeDragging.columnIndex !== void 0 && activeDragging.neighborStartWidth !== void 0) {
3869
+ const nextColumnWidths = activeDragging.responsiveColumnWidths.slice();
3870
+ nextColumnWidths[activeDragging.columnIndex] = nextWidth;
3871
+ nextColumnWidths[activeDragging.columnIndex + 1] = activeDragging.startWidth + activeDragging.neighborStartWidth - nextWidth;
3872
+ updateColumnWidths(
3873
+ view,
3874
+ activeHandle,
3875
+ new Map(nextColumnWidths.map((columnWidth, index) => [index, columnWidth])),
3876
+ normalizeColumnRatios(nextColumnWidths, nextColumnWidths.length)
3877
+ );
3878
+ } else {
3879
+ updateColumnWidth(view, activeHandle, nextWidth);
3880
+ }
3652
3881
  view.dispatch(view.state.tr.setMeta(columnResizingPluginKey, { setDragging: null }));
3653
3882
  }
3654
3883
  hideColumnResizeGhost(view);
@@ -3678,6 +3907,9 @@ function handleDecorations(state, cell, ownerDocument) {
3678
3907
  const nodeAfter = $cell.nodeAfter;
3679
3908
  if (!nodeAfter) return DecorationSet2.empty;
3680
3909
  const col = map.colCount($cell.pos - start) + nodeAfter.attrs.colspan - 1;
3910
+ if (normalizeTableWidthMode(table.attrs.widthMode) === "responsive" && col === map.width - 1) {
3911
+ return DecorationSet2.empty;
3912
+ }
3681
3913
  for (let row = 0; row < map.height; row += 1) {
3682
3914
  const index = col + row * map.width;
3683
3915
  if ((col === map.width - 1 || map.map[index] !== map.map[index + 1]) && (row === 0 || map.map[index] !== map.map[index - map.width])) {
@@ -3765,7 +3997,7 @@ function parseTableAlign(element) {
3765
3997
  if ((marginLeft === "0px" || marginLeft === "0") && marginRight === "auto") return "left";
3766
3998
  return null;
3767
3999
  }
3768
- function renderTableAlignStyle(tableAlign) {
4000
+ function renderFixedTableAlignStyle(tableAlign) {
3769
4001
  switch (tableAlign) {
3770
4002
  case "center":
3771
4003
  return "table-layout: fixed; margin-left: auto; margin-right: auto;";
@@ -3777,28 +4009,94 @@ function renderTableAlignStyle(tableAlign) {
3777
4009
  return "";
3778
4010
  }
3779
4011
  }
3780
- function createUEditorColGroup(node) {
3781
- const columns = [];
3782
- let totalWidth = 0;
3783
- const firstRow = node.firstChild;
3784
- if (firstRow) {
3785
- for (let cellIndex = 0; cellIndex < firstRow.childCount; cellIndex += 1) {
3786
- const cell = firstRow.child(cellIndex);
3787
- const colspan = Math.max(1, Number(cell.attrs.colspan) || 1);
3788
- const colwidth = Array.isArray(cell.attrs.colwidth) ? cell.attrs.colwidth : [];
3789
- for (let spanIndex = 0; spanIndex < colspan; spanIndex += 1) {
3790
- const storedWidth = Number(colwidth[spanIndex]);
3791
- const width = Number.isFinite(storedWidth) && storedWidth > 0 ? Math.max(MIN_RESIZED_TABLE_COLUMN_WIDTH, Math.round(storedWidth)) : DEFAULT_TABLE_COLUMN_WIDTH;
3792
- totalWidth += width;
3793
- columns.push(["col", { style: `width: ${width}px; min-width: ${width}px;`, width: String(width) }]);
3794
- }
3795
- }
4012
+ function parseTableWidthMode(element) {
4013
+ const storedMode = element.getAttribute("data-table-width-mode");
4014
+ if (storedMode === "responsive" || storedMode === "full" || parsePercentageToBasisPoints(element.style.width) !== null) {
4015
+ return "responsive";
3796
4016
  }
4017
+ return "fixed";
4018
+ }
4019
+ function createUEditorColGroup(node, widthMode) {
4020
+ const columnWidths = getLegacyTableColumnWeights(node, DEFAULT_TABLE_COLUMN_WIDTH).map((width) => Math.max(MIN_RESIZED_TABLE_COLUMN_WIDTH, Math.round(width)));
4021
+ const columnRatios = getTableColumnRatios(node);
4022
+ const totalWidth = columnWidths.reduce((sum, width) => sum + width, 0);
4023
+ const columns = columnWidths.map((width, index) => ["col", {
4024
+ style: widthMode === "responsive" ? `width: ${formatBasisPointsAsPercentage(columnRatios[index])};` : `width: ${width}px; min-width: ${width}px;`,
4025
+ width: String(width)
4026
+ }]);
3797
4027
  return {
3798
4028
  colgroup: ["colgroup", {}, ...columns],
4029
+ columnCount: columns.length,
3799
4030
  tableWidth: totalWidth > 0 ? `${totalWidth}px` : ""
3800
4031
  };
3801
4032
  }
4033
+ function runResponsiveColumnStructureCommand(state, dispatch, command, updateLayout, rewriteFormulas) {
4034
+ const tableInfo = findTableNodeInfoFromState(state);
4035
+ if (!tableInfo) {
4036
+ return command(state, dispatch);
4037
+ }
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({
4046
+ widthBp: getResponsiveTableWidthBp(tableInfo.node),
4047
+ columnRatios: getTableColumnRatios(tableInfo.node)
4048
+ }, rect) : null;
4049
+ return command(state, dispatch ? (transaction) => {
4050
+ let nextTable = transaction.doc.nodeAt(tableInfo.pos);
4051
+ if (nextTable?.type.name === "table" && nextLayout && nextLayout.columnRatios.length > 0) {
4052
+ const widthBp = clampResponsiveTableWidthBp(nextLayout.widthBp);
4053
+ const tableAlign = normalizeTableAlign(nextTable.attrs.textAlign);
4054
+ const offsetBp = resolveResponsiveTableOffsetBp(
4055
+ widthBp,
4056
+ tableAlign,
4057
+ getResponsiveTableOffsetBp(tableInfo.node)
4058
+ );
4059
+ transaction.setNodeMarkup(tableInfo.pos, void 0, {
4060
+ ...nextTable.attrs,
4061
+ widthMode: "responsive",
4062
+ widthBp,
4063
+ offsetBp,
4064
+ columnRatios: normalizeColumnRatios(
4065
+ nextLayout.columnRatios,
4066
+ getLogicalTableColumnCount(nextTable)
4067
+ )
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
+ }
4096
+ }
4097
+ dispatch(transaction);
4098
+ } : void 0);
4099
+ }
3802
4100
  var UEditorTable = Table2.extend({
3803
4101
  addGlobalAttributes() {
3804
4102
  return [
@@ -3816,10 +4114,64 @@ var UEditorTable = Table2.extend({
3816
4114
  const tableAlign = normalizeTableAlign(attributes.textAlign);
3817
4115
  if (!tableAlign) return {};
3818
4116
  return {
3819
- "data-table-align": tableAlign,
3820
- style: renderTableAlignStyle(tableAlign)
4117
+ "data-table-align": tableAlign
3821
4118
  };
3822
4119
  }
4120
+ },
4121
+ widthMode: {
4122
+ default: "fixed",
4123
+ parseHTML: (element) => {
4124
+ if (!(element instanceof HTMLElement)) return "fixed";
4125
+ return parseTableWidthMode(element);
4126
+ },
4127
+ renderHTML: (attributes) => {
4128
+ if (normalizeTableWidthMode(attributes.widthMode) !== "responsive") return {};
4129
+ return {
4130
+ "data-table-width-mode": "responsive"
4131
+ };
4132
+ }
4133
+ },
4134
+ widthBp: {
4135
+ default: null,
4136
+ parseHTML: (element) => {
4137
+ if (!(element instanceof HTMLElement) || parseTableWidthMode(element) !== "responsive") return null;
4138
+ const stored = Number(element.getAttribute("data-table-width-bp"));
4139
+ if (Number.isFinite(stored) && stored > 0) return clampResponsiveTableWidthBp(stored);
4140
+ return parsePercentageToBasisPoints(element.getAttribute("data-table-width") ?? element.style.width) ?? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP;
4141
+ },
4142
+ renderHTML: () => ({})
4143
+ },
4144
+ offsetBp: {
4145
+ default: null,
4146
+ parseHTML: (element) => {
4147
+ if (!(element instanceof HTMLElement) || parseTableWidthMode(element) !== "responsive") return null;
4148
+ const storedValue = element.getAttribute("data-table-offset-bp");
4149
+ const stored = Number(storedValue);
4150
+ if (storedValue !== null && Number.isFinite(stored) && stored >= 0) return Math.round(stored);
4151
+ const explicitOffset = parsePercentageToBasisPoints(
4152
+ element.getAttribute("data-table-offset") ?? element.style.marginLeft
4153
+ );
4154
+ if (explicitOffset !== null) return explicitOffset;
4155
+ const widthBp = parsePercentageToBasisPoints(
4156
+ element.getAttribute("data-table-width") ?? element.style.width
4157
+ ) ?? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP;
4158
+ const tableAlign = parseTableAlign(element);
4159
+ return resolveResponsiveTableOffsetBp(widthBp, tableAlign);
4160
+ },
4161
+ renderHTML: () => ({})
4162
+ },
4163
+ columnRatios: {
4164
+ default: null,
4165
+ parseHTML: (element) => {
4166
+ if (!(element instanceof HTMLElement)) return null;
4167
+ const storedRatios = parseColumnRatios(
4168
+ element.getAttribute("data-table-column-ratios") ?? element.getAttribute("data-column-ratios")
4169
+ );
4170
+ if (storedRatios) return storedRatios;
4171
+ const columnPercentages = Array.from(element.querySelectorAll("colgroup > col")).map((column) => parsePercentageToBasisPoints(column.style.width));
4172
+ return columnPercentages.length > 0 && columnPercentages.every((ratio) => ratio !== null) ? normalizeColumnRatios(columnPercentages, columnPercentages.length) : null;
4173
+ },
4174
+ renderHTML: () => ({})
3823
4175
  }
3824
4176
  }
3825
4177
  }
@@ -3828,13 +4180,117 @@ var UEditorTable = Table2.extend({
3828
4180
  addCommands() {
3829
4181
  return {
3830
4182
  ...this.parent?.(),
4183
+ insertTable: ({ rows = 3, cols = 3, withHeaderRow = true } = {}) => ({ tr, dispatch, editor }) => {
4184
+ const types = tableNodeTypes2(editor.schema);
4185
+ const rowCount = Math.max(1, Math.floor(rows));
4186
+ const columnCount = Math.max(1, Math.floor(cols));
4187
+ const createCells = (type) => Array.from({ length: columnCount }, () => type.createAndFill()).filter((cell) => cell !== null);
4188
+ const bodyCells = createCells(types.cell);
4189
+ const headerCells = withHeaderRow ? createCells(types.header_cell) : bodyCells;
4190
+ const tableRows = Array.from({ length: rowCount }, (_, index) => types.row.createChecked(
4191
+ null,
4192
+ withHeaderRow && index === 0 ? headerCells : bodyCells
4193
+ ));
4194
+ const table = types.table.createChecked(
4195
+ {
4196
+ widthMode: "responsive",
4197
+ widthBp: DEFAULT_RESPONSIVE_TABLE_WIDTH_BP,
4198
+ offsetBp: 0,
4199
+ columnRatios: normalizeColumnRatios(null, columnCount)
4200
+ },
4201
+ tableRows
4202
+ );
4203
+ if (dispatch) {
4204
+ const offset = tr.selection.from + 1;
4205
+ tr.replaceSelectionWith(table).scrollIntoView().setSelection(TextSelection.near(tr.doc.resolve(offset)));
4206
+ }
4207
+ return true;
4208
+ },
4209
+ addColumnBefore: () => ({ state, dispatch }) => {
4210
+ return runResponsiveColumnStructureCommand(
4211
+ state,
4212
+ dispatch,
4213
+ addColumnBeforeCommand,
4214
+ (layout, rect) => insertResponsiveColumnLayout(
4215
+ layout.widthBp,
4216
+ layout.columnRatios,
4217
+ rect.left,
4218
+ rect.left
4219
+ ),
4220
+ (tableNode, rect) => rewriteTableNodeFormulaReferencesForInsertion(tableNode, "column", rect.left)
4221
+ );
4222
+ },
4223
+ addColumnAfter: () => ({ state, dispatch }) => {
4224
+ return runResponsiveColumnStructureCommand(
4225
+ state,
4226
+ dispatch,
4227
+ addColumnAfterCommand,
4228
+ (layout, rect) => insertResponsiveColumnLayout(
4229
+ layout.widthBp,
4230
+ layout.columnRatios,
4231
+ rect.right,
4232
+ rect.right - 1
4233
+ ),
4234
+ (tableNode, rect) => rewriteTableNodeFormulaReferencesForInsertion(tableNode, "column", rect.right)
4235
+ );
4236
+ },
4237
+ deleteColumn: () => ({ state, dispatch }) => {
4238
+ return runResponsiveColumnStructureCommand(
4239
+ state,
4240
+ dispatch,
4241
+ deleteColumnCommand,
4242
+ (layout, rect) => {
4243
+ let nextLayout = layout;
4244
+ for (let index = rect.right - 1; index >= rect.left; index -= 1) {
4245
+ nextLayout = deleteResponsiveColumnLayout(
4246
+ nextLayout.widthBp,
4247
+ nextLayout.columnRatios,
4248
+ index
4249
+ );
4250
+ }
4251
+ return nextLayout;
4252
+ },
4253
+ (tableNode, rect) => rewriteTableNodeFormulaReferencesForDeletion(
4254
+ tableNode,
4255
+ "column",
4256
+ rect.left,
4257
+ rect.right - rect.left
4258
+ )
4259
+ );
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
+ ),
3831
4284
  setTableAlign: (tableAlign) => ({ state, dispatch }) => {
3832
4285
  const tableInfo = findTableNodeInfoFromState(state);
3833
4286
  if (!tableInfo) return false;
4287
+ const widthBp = getResponsiveTableWidthBp(tableInfo.node);
4288
+ const offsetBp = resolveResponsiveTableOffsetBp(widthBp, tableAlign);
3834
4289
  dispatch?.(
3835
4290
  state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
3836
4291
  ...tableInfo.node.attrs,
3837
- textAlign: tableAlign
4292
+ textAlign: tableAlign,
4293
+ ...normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive" ? { offsetBp } : null
3838
4294
  })
3839
4295
  );
3840
4296
  return true;
@@ -3853,11 +4309,31 @@ var UEditorTable = Table2.extend({
3853
4309
  };
3854
4310
  },
3855
4311
  renderHTML({ node, HTMLAttributes }) {
3856
- const { colgroup, tableWidth } = createUEditorColGroup(node);
4312
+ const widthMode = normalizeTableWidthMode(node.attrs.widthMode);
4313
+ const { colgroup, columnCount, tableWidth } = createUEditorColGroup(node, widthMode);
4314
+ const tableAlign = normalizeTableAlign(node.attrs.textAlign);
4315
+ const widthBp = getResponsiveTableWidthBp(node);
4316
+ const offsetBp = getResponsiveTableOffsetBp(node);
4317
+ const columnRatios = getTableColumnRatios(node);
4318
+ const tableStyle = widthMode === "responsive" ? [
4319
+ `width: ${formatBasisPointsAsPercentage(widthBp)}`,
4320
+ `margin-left: ${formatBasisPointsAsPercentage(offsetBp)}`,
4321
+ "margin-right: auto",
4322
+ `min-width: ${Math.max(1, columnCount) * MIN_RESIZED_TABLE_COLUMN_WIDTH}px`,
4323
+ "table-layout: fixed"
4324
+ ].join("; ") + ";" : tableWidth ? `width: ${tableWidth}; ${renderFixedTableAlignStyle(tableAlign)}` : `table-layout: fixed; ${renderFixedTableAlignStyle(tableAlign)}`;
3857
4325
  const table = [
3858
4326
  "table",
3859
4327
  mergeAttributes7(this.options.HTMLAttributes, HTMLAttributes, {
3860
- style: tableWidth ? `width: ${tableWidth};` : "table-layout: fixed;"
4328
+ ...widthMode === "responsive" ? {
4329
+ "data-table-width-mode": "responsive",
4330
+ "data-table-width": formatBasisPointsAsPercentage(widthBp),
4331
+ "data-table-width-bp": String(widthBp),
4332
+ "data-table-offset": formatBasisPointsAsPercentage(offsetBp),
4333
+ "data-table-offset-bp": String(offsetBp),
4334
+ "data-table-column-ratios": columnRatios.join(",")
4335
+ } : null,
4336
+ style: tableStyle
3861
4337
  }),
3862
4338
  colgroup,
3863
4339
  ["tbody", 0]
@@ -3910,6 +4386,31 @@ var UEditorTable = Table2.extend({
3910
4386
  tableEditing({
3911
4387
  allowTableNodeSelection: this.options.allowTableNodeSelection
3912
4388
  }),
4389
+ new Plugin4({
4390
+ key: new PluginKey5("responsiveTableLayoutNormalizer"),
4391
+ appendTransaction(transactions, _oldState, newState) {
4392
+ if (!transactions.some((transaction) => transaction.docChanged)) return null;
4393
+ const tableInfo = findTableNodeInfoFromState(newState);
4394
+ const node = tableInfo?.node;
4395
+ if (!tableInfo || !node || normalizeTableWidthMode(node.attrs.widthMode) !== "responsive") return null;
4396
+ const columnCount = getLogicalTableColumnCount(node);
4397
+ const columnRatios = getTableColumnRatios(node);
4398
+ const widthBp = getResponsiveTableWidthBp(node);
4399
+ const offsetBp = getResponsiveTableOffsetBp(node);
4400
+ const storedRatios = parseColumnRatios(node.attrs.columnRatios);
4401
+ const ratiosMatch = storedRatios?.length === columnCount && storedRatios.every((ratio, index) => Math.round(ratio) === columnRatios[index]);
4402
+ if (ratiosMatch && node.attrs.widthBp === widthBp && node.attrs.offsetBp === offsetBp && node.attrs.widthMode === "responsive") {
4403
+ return null;
4404
+ }
4405
+ return newState.tr.setNodeMarkup(tableInfo.pos, void 0, {
4406
+ ...node.attrs,
4407
+ widthMode: "responsive",
4408
+ widthBp,
4409
+ offsetBp,
4410
+ columnRatios
4411
+ });
4412
+ }
4413
+ }),
3913
4414
  new Plugin4({
3914
4415
  appendTransaction(_transactions, _oldState, newState) {
3915
4416
  const { doc, schema } = newState;
@@ -3930,7 +4431,7 @@ var UEditorTable = Table2.extend({
3930
4431
  var table_align_default = UEditorTable;
3931
4432
 
3932
4433
  // src/components/UEditor/CodeBlockView.tsx
3933
- import React8, { useState as useState4 } from "react";
4434
+ import React9, { useState as useState4 } from "react";
3934
4435
  import { NodeViewWrapper as NodeViewWrapper7, NodeViewContent as NodeViewContent2 } from "@tiptap/react";
3935
4436
  import { Check as Check2, Copy, ChevronDown } from "lucide-react";
3936
4437
  import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -3957,10 +4458,11 @@ var CodeBlockView = ({
3957
4458
  }) => {
3958
4459
  const [copied, setCopied] = useState4(false);
3959
4460
  const [isDropdownOpen, setIsDropdownOpen] = useState4(false);
3960
- const timeoutRef = React8.useRef(null);
4461
+ const timeoutRef = React9.useRef(null);
4462
+ const t = useSmartTranslations("UEditor");
3961
4463
  const currentLangValue = node.attrs.language || "text";
3962
4464
  const currentLangLabel = LANGUAGES.find((lang) => lang.value === currentLangValue)?.label || "Plain Text";
3963
- React8.useEffect(() => {
4465
+ React9.useEffect(() => {
3964
4466
  return () => {
3965
4467
  if (timeoutRef.current) {
3966
4468
  clearTimeout(timeoutRef.current);
@@ -3994,10 +4496,12 @@ var CodeBlockView = ({
3994
4496
  {
3995
4497
  type: "button",
3996
4498
  onClick: () => setIsDropdownOpen(!isDropdownOpen),
3997
- 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",
3998
4502
  children: [
3999
4503
  currentLangLabel,
4000
- /* @__PURE__ */ jsx11(ChevronDown, { className: "h-3 w-3" })
4504
+ /* @__PURE__ */ jsx11(ChevronDown, { "aria-hidden": "true", className: "h-3 w-3" })
4001
4505
  ]
4002
4506
  }
4003
4507
  ),
@@ -4009,10 +4513,12 @@ var CodeBlockView = ({
4009
4513
  onClick: () => setIsDropdownOpen(false)
4010
4514
  }
4011
4515
  ),
4012
- /* @__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(
4013
4517
  "button",
4014
4518
  {
4015
4519
  type: "button",
4520
+ role: "option",
4521
+ "aria-selected": currentLangValue === lang.value,
4016
4522
  onClick: () => {
4017
4523
  updateAttributes({ language: lang.value });
4018
4524
  setIsDropdownOpen(false);
@@ -4032,9 +4538,10 @@ var CodeBlockView = ({
4032
4538
  {
4033
4539
  type: "button",
4034
4540
  onClick: handleCopy,
4035
- 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",
4036
- title: "Copy code",
4037
- 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" })
4038
4545
  }
4039
4546
  )
4040
4547
  ]
@@ -4237,529 +4744,8 @@ import { Extension as Extension10 } from "@tiptap/core";
4237
4744
  import { Plugin as Plugin6 } from "@tiptap/pm/state";
4238
4745
 
4239
4746
  // src/components/UEditor/table-formula-commands.ts
4240
- import { TextSelection } from "@tiptap/pm/state";
4241
- import { selectedRect, setCellAttr, TableMap as TableMap2 } from "@tiptap/pm/tables";
4242
-
4243
- // src/components/UEditor/table-formula.ts
4244
- var CELL_ADDRESS_RE = /^([A-Z]+)([1-9]\d*)$/i;
4245
- var CELL_RANGE_RE = /^([A-Z]+[1-9]\d*):([A-Z]+[1-9]\d*)$/i;
4246
- var SUPPORTED_FUNCTIONS = /* @__PURE__ */ new Set(["SUM", "AVG", "MIN", "MAX", "COUNT"]);
4247
- function columnNameToIndex(columnName) {
4248
- const normalized = columnName.trim().toUpperCase();
4249
- if (!/^[A-Z]+$/.test(normalized)) {
4250
- return -1;
4251
- }
4252
- let index = 0;
4253
- for (const char of normalized) {
4254
- index = index * 26 + char.charCodeAt(0) - 64;
4255
- }
4256
- return index - 1;
4257
- }
4258
- function indexToColumnName(index) {
4259
- if (!Number.isInteger(index) || index < 0) {
4260
- return "";
4261
- }
4262
- let value = index + 1;
4263
- let name = "";
4264
- while (value > 0) {
4265
- const remainder = (value - 1) % 26;
4266
- name = String.fromCharCode(65 + remainder) + name;
4267
- value = Math.floor((value - 1) / 26);
4268
- }
4269
- return name;
4270
- }
4271
- function parseTableCellAddress(input) {
4272
- const match = input.trim().match(CELL_ADDRESS_RE);
4273
- if (!match) {
4274
- return null;
4275
- }
4276
- const column = columnNameToIndex(match[1] ?? "");
4277
- const row = Number.parseInt(match[2] ?? "", 10) - 1;
4278
- if (column < 0 || row < 0) {
4279
- return null;
4280
- }
4281
- return {
4282
- column,
4283
- row,
4284
- label: `${indexToColumnName(column)}${row + 1}`
4285
- };
4286
- }
4287
- function parseTableCellRange(input) {
4288
- const match = input.trim().match(CELL_RANGE_RE);
4289
- if (!match) {
4290
- return null;
4291
- }
4292
- const from = parseTableCellAddress(match[1] ?? "");
4293
- const to = parseTableCellAddress(match[2] ?? "");
4294
- if (!from || !to) {
4295
- return null;
4296
- }
4297
- return { from, to };
4298
- }
4299
- function getTableCellRangeLabels(range) {
4300
- const startColumn = Math.min(range.from.column, range.to.column);
4301
- const endColumn = Math.max(range.from.column, range.to.column);
4302
- const startRow = Math.min(range.from.row, range.to.row);
4303
- const endRow = Math.max(range.from.row, range.to.row);
4304
- const labels = [];
4305
- for (let row = startRow; row <= endRow; row += 1) {
4306
- for (let column = startColumn; column <= endColumn; column += 1) {
4307
- labels.push(`${indexToColumnName(column)}${row + 1}`);
4308
- }
4309
- }
4310
- return labels;
4311
- }
4312
- function normalizeTableFormula(formula) {
4313
- return formula.trim().replace(/^=/, "").trim();
4314
- }
4315
- function isDraftTableFormula(formula) {
4316
- const normalized = normalizeTableFormula(formula);
4317
- if (!normalized) return true;
4318
- let depth = 0;
4319
- for (const char of normalized) {
4320
- if (char === "(") depth += 1;
4321
- if (char === ")") depth -= 1;
4322
- if (depth < 0) return false;
4323
- }
4324
- if (depth > 0) return true;
4325
- if (/^[A-Z]+\(\s*\)$/i.test(normalized)) return true;
4326
- if (/[+\-*/,(]\s*$/.test(normalized)) return true;
4327
- return false;
4328
- }
4329
- function formatFormulaError(error) {
4330
- return `#${error.toUpperCase()}`;
4331
- }
4332
- function normalizeTableNumberFormat(format) {
4333
- return format === "text" || format === "number" || format === "currency" || format === "percent" || format === "date" ? format : "text";
4334
- }
4335
- function formatTableFormulaDisplayValue(value, numberFormat) {
4336
- const stringValue = String(value);
4337
- if (stringValue.startsWith("#")) {
4338
- return stringValue;
4339
- }
4340
- const normalizedFormat = normalizeTableNumberFormat(numberFormat);
4341
- if (normalizedFormat === "text") {
4342
- return stringValue;
4343
- }
4344
- const numericValue = typeof value === "number" ? value : Number.parseFloat(stringValue.replace(/,/g, ""));
4345
- if (!Number.isFinite(numericValue)) {
4346
- return stringValue;
4347
- }
4348
- if (normalizedFormat === "number") {
4349
- return new Intl.NumberFormat("en-US", { maximumFractionDigits: 6 }).format(numericValue);
4350
- }
4351
- if (normalizedFormat === "currency") {
4352
- return new Intl.NumberFormat("en-US", {
4353
- style: "currency",
4354
- currency: "USD",
4355
- maximumFractionDigits: 2
4356
- }).format(numericValue);
4357
- }
4358
- if (normalizedFormat === "percent") {
4359
- return new Intl.NumberFormat("en-US", {
4360
- style: "percent",
4361
- maximumFractionDigits: 2
4362
- }).format(numericValue);
4363
- }
4364
- const excelEpochMs = Date.UTC(1899, 11, 30);
4365
- const date = new Date(excelEpochMs + numericValue * 24 * 60 * 60 * 1e3);
4366
- if (Number.isNaN(date.getTime())) {
4367
- return stringValue;
4368
- }
4369
- return new Intl.DateTimeFormat("en-US", {
4370
- year: "numeric",
4371
- month: "2-digit",
4372
- day: "2-digit",
4373
- timeZone: "UTC"
4374
- }).format(date);
4375
- }
4376
- function getTableFormulaReferences(formula) {
4377
- const normalized = normalizeTableFormula(formula);
4378
- if (!normalized) return [];
4379
- const tokens = tokenizeFormula(normalized);
4380
- if (!tokens) return [];
4381
- const references = /* @__PURE__ */ new Set();
4382
- for (const token of tokens) {
4383
- if (token.type === "cell") {
4384
- references.add(token.value);
4385
- } else if (token.type === "range") {
4386
- const range = parseTableCellRange(token.value);
4387
- if (!range) continue;
4388
- for (const label of getTableCellRangeLabels(range)) {
4389
- references.add(label);
4390
- }
4391
- }
4392
- }
4393
- return Array.from(references);
4394
- }
4395
- function buildTableFormulaDependencyGraph(cells) {
4396
- const dependencies = /* @__PURE__ */ new Map();
4397
- const dependents = /* @__PURE__ */ new Map();
4398
- const formulas = /* @__PURE__ */ new Map();
4399
- for (const cell of cells) {
4400
- const label = cell.label.toUpperCase();
4401
- formulas.set(label, cell.formula);
4402
- dependencies.set(label, new Set(getTableFormulaReferences(cell.formula)));
4403
- if (!dependents.has(label)) {
4404
- dependents.set(label, /* @__PURE__ */ new Set());
4405
- }
4406
- }
4407
- for (const [label, refs] of dependencies) {
4408
- for (const ref of refs) {
4409
- if (!dependents.has(ref)) {
4410
- dependents.set(ref, /* @__PURE__ */ new Set());
4411
- }
4412
- dependents.get(ref)?.add(label);
4413
- }
4414
- }
4415
- return { dependencies, dependents, formulas };
4416
- }
4417
- function getTableFormulaCircularReferences(graph) {
4418
- const circular = /* @__PURE__ */ new Set();
4419
- const visited = /* @__PURE__ */ new Set();
4420
- const finishOrder = [];
4421
- for (const startLabel of graph.formulas.keys()) {
4422
- if (visited.has(startLabel)) continue;
4423
- visited.add(startLabel);
4424
- const stack = [
4425
- {
4426
- label: startLabel,
4427
- references: Array.from(graph.dependencies.get(startLabel) ?? []).filter((ref) => graph.formulas.has(ref)),
4428
- nextIndex: 0
4429
- }
4430
- ];
4431
- while (stack.length > 0) {
4432
- const frame = stack[stack.length - 1];
4433
- if (!frame) break;
4434
- const reference = frame.references[frame.nextIndex];
4435
- if (reference) {
4436
- frame.nextIndex += 1;
4437
- if (!visited.has(reference)) {
4438
- visited.add(reference);
4439
- stack.push({
4440
- label: reference,
4441
- references: Array.from(graph.dependencies.get(reference) ?? []).filter((ref) => graph.formulas.has(ref)),
4442
- nextIndex: 0
4443
- });
4444
- }
4445
- continue;
4446
- }
4447
- stack.pop();
4448
- finishOrder.push(frame.label);
4449
- }
4450
- }
4451
- const assigned = /* @__PURE__ */ new Set();
4452
- for (let index = finishOrder.length - 1; index >= 0; index -= 1) {
4453
- const startLabel = finishOrder[index];
4454
- if (!startLabel || assigned.has(startLabel)) continue;
4455
- const component = [];
4456
- const stack = [startLabel];
4457
- assigned.add(startLabel);
4458
- while (stack.length > 0) {
4459
- const label = stack.pop();
4460
- if (!label) continue;
4461
- component.push(label);
4462
- for (const dependent of graph.dependents.get(label) ?? []) {
4463
- if (!graph.formulas.has(dependent) || assigned.has(dependent)) continue;
4464
- assigned.add(dependent);
4465
- stack.push(dependent);
4466
- }
4467
- }
4468
- if (component.length > 1) {
4469
- for (const label of component) circular.add(label);
4470
- } else {
4471
- const label = component[0];
4472
- if (label && graph.dependencies.get(label)?.has(label)) circular.add(label);
4473
- }
4474
- }
4475
- return circular;
4476
- }
4477
- function getTableFormulaRecalculationOrder(graph) {
4478
- const circular = getTableFormulaCircularReferences(graph);
4479
- const visiting = /* @__PURE__ */ new Set();
4480
- const visited = /* @__PURE__ */ new Set();
4481
- const order = [];
4482
- for (const startLabel of graph.formulas.keys()) {
4483
- if (visited.has(startLabel) || circular.has(startLabel)) continue;
4484
- const stack = [];
4485
- const push = (label) => {
4486
- visiting.add(label);
4487
- stack.push({
4488
- label,
4489
- references: Array.from(graph.dependencies.get(label) ?? []).filter((ref) => graph.formulas.has(ref) && !circular.has(ref)),
4490
- nextIndex: 0
4491
- });
4492
- };
4493
- push(startLabel);
4494
- while (stack.length > 0) {
4495
- const frame = stack[stack.length - 1];
4496
- if (!frame) break;
4497
- const reference = frame.references[frame.nextIndex];
4498
- if (reference) {
4499
- frame.nextIndex += 1;
4500
- if (!visited.has(reference) && !visiting.has(reference)) {
4501
- push(reference);
4502
- }
4503
- continue;
4504
- }
4505
- stack.pop();
4506
- visiting.delete(frame.label);
4507
- visited.add(frame.label);
4508
- order.push(frame.label);
4509
- }
4510
- }
4511
- return { order, circular };
4512
- }
4513
- function getAffectedTableFormulaLabels(graph, changedLabels) {
4514
- const affected = /* @__PURE__ */ new Set();
4515
- const queue = Array.from(changedLabels, (label) => label.toUpperCase());
4516
- for (const label of queue) {
4517
- if (graph.formulas.has(label)) {
4518
- affected.add(label);
4519
- }
4520
- }
4521
- for (let index = 0; index < queue.length; index += 1) {
4522
- const label = queue[index];
4523
- if (!label) continue;
4524
- for (const dependent of graph.dependents.get(label) ?? []) {
4525
- if (affected.has(dependent)) continue;
4526
- affected.add(dependent);
4527
- queue.push(dependent);
4528
- }
4529
- }
4530
- return affected;
4531
- }
4532
- function evaluateBasicTableFormula(formula, getCellValue) {
4533
- const normalized = normalizeTableFormula(formula);
4534
- if (!normalized) {
4535
- return { value: null, error: "empty" };
4536
- }
4537
- const tokens = tokenizeFormula(normalized);
4538
- if (!tokens) {
4539
- return { value: null, error: "invalid-formula" };
4540
- }
4541
- const parser = new FormulaParser(tokens, getCellValue);
4542
- const result = parser.parseExpression();
4543
- if (result.error) {
4544
- return result;
4545
- }
4546
- if (!parser.isComplete()) {
4547
- return { value: null, error: "invalid-formula" };
4548
- }
4549
- return result;
4550
- }
4551
- function tokenizeFormula(formula) {
4552
- const tokens = [];
4553
- let index = 0;
4554
- while (index < formula.length) {
4555
- const char = formula[index];
4556
- if (!char) break;
4557
- if (/\s/.test(char)) {
4558
- index += 1;
4559
- continue;
4560
- }
4561
- if (char === "," || char === "+" || char === "-" || char === "*" || char === "/" || char === "(" || char === ")") {
4562
- if (char === ",") tokens.push({ type: "comma", value: char });
4563
- else if (char === "(" || char === ")") tokens.push({ type: "paren", value: char });
4564
- else tokens.push({ type: "operator", value: char });
4565
- index += 1;
4566
- continue;
4567
- }
4568
- const numberMatch = formula.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)/);
4569
- if (numberMatch?.[0]) {
4570
- const value = Number.parseFloat(numberMatch[0]);
4571
- if (!Number.isFinite(value)) return null;
4572
- tokens.push({ type: "number", value });
4573
- index += numberMatch[0].length;
4574
- continue;
4575
- }
4576
- const identifierMatch = formula.slice(index).match(/^[A-Z]+[1-9]\d*(?::[A-Z]+[1-9]\d*)?|^[A-Z]+/i);
4577
- if (identifierMatch?.[0]) {
4578
- const value = identifierMatch[0].toUpperCase();
4579
- if (CELL_RANGE_RE.test(value)) tokens.push({ type: "range", value });
4580
- else if (parseTableCellAddress(value)) tokens.push({ type: "cell", value });
4581
- else if (SUPPORTED_FUNCTIONS.has(value)) tokens.push({ type: "function", value });
4582
- else return null;
4583
- index += identifierMatch[0].length;
4584
- continue;
4585
- }
4586
- return null;
4587
- }
4588
- return tokens;
4589
- }
4590
- function toFiniteFormulaResult(value) {
4591
- return Number.isFinite(value) ? { value, error: null } : { value: null, error: "invalid-formula" };
4592
- }
4593
- function parseTableCellNumericValue(value) {
4594
- if (typeof value === "number") {
4595
- return Number.isFinite(value) ? value : null;
4596
- }
4597
- let normalized = String(value ?? "").trim();
4598
- if (!normalized || normalized.startsWith("#")) return null;
4599
- const isPercent = normalized.endsWith("%");
4600
- if (isPercent) normalized = normalized.slice(0, -1).trim();
4601
- const currencyMatch = normalized.match(/^([+-]?)\$(.+)$/);
4602
- if (currencyMatch) {
4603
- normalized = `${currencyMatch[1] ?? ""}${currencyMatch[2] ?? ""}`.trim();
4604
- }
4605
- const groupedNumber = /^[+-]?\d{1,3}(?:,\d{3})+(?:\.\d*)?(?:[eE][+-]?\d+)?$/;
4606
- const plainNumber = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
4607
- if (!groupedNumber.test(normalized) && !plainNumber.test(normalized)) return null;
4608
- const parsed = Number(normalized.replace(/,/g, ""));
4609
- if (!Number.isFinite(parsed)) return null;
4610
- return isPercent ? parsed / 100 : parsed;
4611
- }
4612
- var FormulaParser = class {
4613
- constructor(tokens, getCellValue) {
4614
- this.tokens = tokens;
4615
- this.getCellValue = getCellValue;
4616
- this.index = 0;
4617
- }
4618
- isComplete() {
4619
- return this.index >= this.tokens.length;
4620
- }
4621
- parseExpression() {
4622
- let left = this.parseTerm();
4623
- while (!left.error) {
4624
- const operator = this.peekOperator(["+", "-"]);
4625
- if (!operator) break;
4626
- this.index += 1;
4627
- const right = this.parseTerm();
4628
- if (right.error) return right;
4629
- left = toFiniteFormulaResult(operator.value === "+" ? left.value + right.value : left.value - right.value);
4630
- }
4631
- return left;
4632
- }
4633
- parseTerm() {
4634
- let left = this.parseFactor();
4635
- while (!left.error) {
4636
- const operator = this.peekOperator(["*", "/"]);
4637
- if (!operator) break;
4638
- this.index += 1;
4639
- const right = this.parseFactor();
4640
- if (right.error) return right;
4641
- if (operator.value === "/" && right.value === 0) {
4642
- return { value: null, error: "division-by-zero" };
4643
- }
4644
- left = toFiniteFormulaResult(operator.value === "*" ? left.value * right.value : left.value / right.value);
4645
- }
4646
- return left;
4647
- }
4648
- parseFactor() {
4649
- const token = this.tokens[this.index];
4650
- if (!token) {
4651
- return { value: null, error: "invalid-formula" };
4652
- }
4653
- if (token.type === "operator" && (token.value === "-" || token.value === "+")) {
4654
- this.index += 1;
4655
- const value = this.parseFactor();
4656
- if (value.error) return value;
4657
- return toFiniteFormulaResult(token.value === "-" ? -value.value : value.value);
4658
- }
4659
- if (token.type === "number") {
4660
- this.index += 1;
4661
- return { value: token.value, error: null };
4662
- }
4663
- if (token.type === "cell") {
4664
- this.index += 1;
4665
- return this.readCellNumber(token.value);
4666
- }
4667
- if (token.type === "function") {
4668
- return this.parseFunction(token.value);
4669
- }
4670
- if (token.type === "paren" && token.value === "(") {
4671
- this.index += 1;
4672
- const value = this.parseExpression();
4673
- if (value.error) return value;
4674
- if (!this.consumeParen(")")) {
4675
- return { value: null, error: "invalid-formula" };
4676
- }
4677
- return value;
4678
- }
4679
- return { value: null, error: "invalid-formula" };
4680
- }
4681
- parseFunction(name) {
4682
- this.index += 1;
4683
- if (!this.consumeParen("(")) {
4684
- return { value: null, error: "invalid-formula" };
4685
- }
4686
- const values = [];
4687
- while (true) {
4688
- const token = this.tokens[this.index];
4689
- if (!token) {
4690
- return { value: null, error: "invalid-formula" };
4691
- }
4692
- if (token.type === "range") {
4693
- this.index += 1;
4694
- const range = parseTableCellRange(token.value);
4695
- if (!range) return { value: null, error: "invalid-reference" };
4696
- for (const label of getTableCellRangeLabels(range)) {
4697
- if (name === "COUNT") {
4698
- const cellValue2 = this.readOptionalCellNumber(label);
4699
- if (cellValue2 != null) values.push(cellValue2);
4700
- continue;
4701
- }
4702
- const cellValue = this.readOptionalCellNumber(label);
4703
- if (cellValue != null) values.push(cellValue);
4704
- }
4705
- } else if (token.type === "cell") {
4706
- this.index += 1;
4707
- const cellValue = this.readOptionalCellNumber(token.value);
4708
- if (cellValue != null) values.push(cellValue);
4709
- } else {
4710
- const value = this.parseExpression();
4711
- if (value.error) return value;
4712
- values.push(value.value);
4713
- }
4714
- if (this.consumeComma()) {
4715
- continue;
4716
- }
4717
- if (this.consumeParen(")")) {
4718
- break;
4719
- }
4720
- return { value: null, error: "invalid-formula" };
4721
- }
4722
- if (name === "SUM") return toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0));
4723
- if (name === "AVG") {
4724
- return values.length > 0 ? toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0) / values.length) : { value: null, error: "division-by-zero" };
4725
- }
4726
- if (name === "MIN") return toFiniteFormulaResult(values.length > 0 ? Math.min(...values) : 0);
4727
- if (name === "MAX") return toFiniteFormulaResult(values.length > 0 ? Math.max(...values) : 0);
4728
- if (name === "COUNT") return { value: values.length, error: null };
4729
- return { value: null, error: "invalid-formula" };
4730
- }
4731
- readCellNumber(label) {
4732
- const parsed = parseTableCellNumericValue(this.getCellValue(label));
4733
- if (parsed == null) {
4734
- return { value: null, error: "invalid-reference" };
4735
- }
4736
- return { value: parsed, error: null };
4737
- }
4738
- readOptionalCellNumber(label) {
4739
- return parseTableCellNumericValue(this.getCellValue(label));
4740
- }
4741
- peekOperator(operators) {
4742
- const token = this.tokens[this.index];
4743
- return token?.type === "operator" && operators.includes(token.value) ? token : null;
4744
- }
4745
- consumeComma() {
4746
- if (this.tokens[this.index]?.type !== "comma") {
4747
- return false;
4748
- }
4749
- this.index += 1;
4750
- return true;
4751
- }
4752
- consumeParen(value) {
4753
- const token = this.tokens[this.index];
4754
- if (token?.type !== "paren" || token.value !== value) {
4755
- return false;
4756
- }
4757
- this.index += 1;
4758
- return true;
4759
- }
4760
- };
4761
-
4762
- // src/components/UEditor/table-formula-commands.ts
4747
+ import { TextSelection as TextSelection2 } from "@tiptap/pm/state";
4748
+ import { selectedRect as selectedRect2, setCellAttr, TableMap as TableMap2 } from "@tiptap/pm/tables";
4763
4749
  var UEDITOR_TABLE_FORMULA_RECALCULATE_META = "ueditorTableFormulaRecalculate";
4764
4750
  var TABLE_FORMULA_PLAN_CACHE_LIMIT = 50;
4765
4751
  var tableFormulaPlanCache = /* @__PURE__ */ new Map();
@@ -4891,7 +4877,7 @@ function restoreSelectionAtTableCell(editor, anchor) {
4891
4877
  if (!cellNode || cellNode.type.name !== "tableCell" && cellNode.type.name !== "tableHeader") return false;
4892
4878
  const selectionPos = Math.min(cellPos + 2, editor.state.doc.content.size);
4893
4879
  editor.view.dispatch(
4894
- editor.state.tr.setSelection(TextSelection.near(editor.state.doc.resolve(selectionPos))).setMeta("addToHistory", false)
4880
+ editor.state.tr.setSelection(TextSelection2.near(editor.state.doc.resolve(selectionPos))).setMeta("addToHistory", false)
4895
4881
  );
4896
4882
  return true;
4897
4883
  }
@@ -5021,7 +5007,7 @@ function removeSelectedTableCellFormula(editor, clearContent) {
5021
5007
  );
5022
5008
  let transaction = editor.state.tr.replaceWith(selectedCell.cellPos, selectedCell.cellPos + selectedCell.node.nodeSize, nextCell2).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true);
5023
5009
  const selectionPos = Math.min(selectedCell.cellPos + 2, transaction.doc.content.size);
5024
- transaction = transaction.setSelection(TextSelection.near(transaction.doc.resolve(selectionPos)));
5010
+ transaction = transaction.setSelection(TextSelection2.near(transaction.doc.resolve(selectionPos)));
5025
5011
  editor.view.dispatch(transaction);
5026
5012
  recalculateActiveTableFormulas(editor);
5027
5013
  restoreSelectionAtTableCell(editor, selectedCell);
@@ -5221,7 +5207,7 @@ function createActiveTableFormulaRecalculationTransaction(state, options) {
5221
5207
  return applyTableCellReplacements(state.tr, replacements).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true);
5222
5208
  }
5223
5209
  function recalculateSelectedTable(editor) {
5224
- const rect = selectedRect(editor.state);
5210
+ const rect = selectedRect2(editor.state);
5225
5211
  const tableNode = rect.table;
5226
5212
  return dispatchTableCellReplacements(
5227
5213
  editor,
@@ -5639,6 +5625,7 @@ function buildUEditorExtensions({
5639
5625
  maxImageFileSize,
5640
5626
  allowedImageMimeTypes,
5641
5627
  fallbackToDataUrl,
5628
+ onImageUploadError,
5642
5629
  editable = true,
5643
5630
  fetchMetadata
5644
5631
  }) {
@@ -5706,7 +5693,8 @@ function buildUEditorExtensions({
5706
5693
  insertMode: imageInsertMode,
5707
5694
  ...maxImageFileSize !== void 0 ? { maxFileSize: maxImageFileSize } : {},
5708
5695
  ...allowedImageMimeTypes ? { allowedMimeTypes: allowedImageMimeTypes } : {},
5709
- ...fallbackToDataUrl !== void 0 ? { fallbackToDataUrl } : {}
5696
+ ...fallbackToDataUrl !== void 0 ? { fallbackToDataUrl } : {},
5697
+ onError: onImageUploadError
5710
5698
  }),
5711
5699
  TextStyle,
5712
5700
  font_family_default,
@@ -5819,7 +5807,7 @@ import {
5819
5807
  } from "lucide-react";
5820
5808
 
5821
5809
  // src/components/UEditor/table-border-commands.ts
5822
- import { selectedRect as selectedRect2 } from "@tiptap/pm/tables";
5810
+ import { selectedRect as selectedRect3 } from "@tiptap/pm/tables";
5823
5811
  var SIDES = ["top", "right", "bottom", "left"];
5824
5812
  var OPPOSITE_SIDE = {
5825
5813
  top: "bottom",
@@ -5890,7 +5878,7 @@ function getAppliedSides(position, cell, selection) {
5890
5878
  }
5891
5879
  function applyTableCellBorders(editor, position, format, options = {}) {
5892
5880
  const { state, view } = editor;
5893
- const selection = selectedRect2(state);
5881
+ const selection = selectedRect3(state);
5894
5882
  const transaction = state.tr;
5895
5883
  const cellPositions = new Set(selection.map.cellsInRect(selection));
5896
5884
  const sidesByCell = /* @__PURE__ */ new Map();
@@ -7149,6 +7137,8 @@ var CustomBubbleMenu = ({
7149
7137
  editor.on("transaction", schedulePositionUpdate);
7150
7138
  editor.on("focus", schedulePositionUpdate);
7151
7139
  editor.on("blur", handleBlur);
7140
+ editorWindow?.addEventListener("resize", schedulePositionUpdate);
7141
+ editorWindow?.addEventListener("scroll", schedulePositionUpdate, true);
7152
7142
  updatePositionRef.current = updatePosition;
7153
7143
  schedulePositionUpdate();
7154
7144
  return () => {
@@ -7159,6 +7149,8 @@ var CustomBubbleMenu = ({
7159
7149
  editor.off("transaction", schedulePositionUpdate);
7160
7150
  editor.off("focus", schedulePositionUpdate);
7161
7151
  editor.off("blur", handleBlur);
7152
+ editorWindow?.removeEventListener("resize", schedulePositionUpdate);
7153
+ editorWindow?.removeEventListener("scroll", schedulePositionUpdate, true);
7162
7154
  };
7163
7155
  }, [editor, editorDocument, editorWindow]);
7164
7156
  useEffect5(() => {
@@ -7292,12 +7284,16 @@ var CustomFloatingMenu = ({ editor }) => {
7292
7284
  editor.on("transaction", schedulePositionUpdate);
7293
7285
  editor.on("focus", schedulePositionUpdate);
7294
7286
  editor.on("blur", handleBlur);
7287
+ editorWindow?.addEventListener("resize", schedulePositionUpdate);
7288
+ editorWindow?.addEventListener("scroll", schedulePositionUpdate, true);
7295
7289
  schedulePositionUpdate();
7296
7290
  return () => {
7297
7291
  if (animationFrameId !== null) editorWindow?.cancelAnimationFrame(animationFrameId);
7298
7292
  editor.off("transaction", schedulePositionUpdate);
7299
7293
  editor.off("focus", schedulePositionUpdate);
7300
7294
  editor.off("blur", handleBlur);
7295
+ editorWindow?.removeEventListener("resize", schedulePositionUpdate);
7296
+ editorWindow?.removeEventListener("scroll", schedulePositionUpdate, true);
7301
7297
  };
7302
7298
  }, [editor, editorWindow]);
7303
7299
  if (!isVisible || !portalContainer) return null;
@@ -7356,7 +7352,7 @@ var CharacterCountDisplay = ({ editor, maxCharacters }) => {
7356
7352
  };
7357
7353
 
7358
7354
  // src/components/UEditor/table-controls.tsx
7359
- import React10 from "react";
7355
+ import React11 from "react";
7360
7356
 
7361
7357
  // node_modules/prosemirror-model/dist/index.js
7362
7358
  function findDiffStart(a, b, pos) {
@@ -10008,7 +10004,7 @@ var Selection = class {
10008
10004
  found.
10009
10005
  */
10010
10006
  static findFrom($pos, dir, textOnly = false) {
10011
- let inner = $pos.parent.inlineContent ? new TextSelection2($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
10007
+ let inner = $pos.parent.inlineContent ? new TextSelection3($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
10012
10008
  if (inner)
10013
10009
  return inner;
10014
10010
  for (let depth = $pos.depth - 1; depth >= 0; depth--) {
@@ -10077,7 +10073,7 @@ var Selection = class {
10077
10073
  returns the bookmark for that.
10078
10074
  */
10079
10075
  getBookmark() {
10080
- return TextSelection2.between(this.$anchor, this.$head).getBookmark();
10076
+ return TextSelection3.between(this.$anchor, this.$head).getBookmark();
10081
10077
  }
10082
10078
  };
10083
10079
  Selection.prototype.visible = true;
@@ -10097,7 +10093,7 @@ function checkTextSelection($pos) {
10097
10093
  console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
10098
10094
  }
10099
10095
  }
10100
- var TextSelection2 = class _TextSelection extends Selection {
10096
+ var TextSelection3 = class _TextSelection extends Selection {
10101
10097
  /**
10102
10098
  Construct a text selection between the given points.
10103
10099
  */
@@ -10183,7 +10179,7 @@ var TextSelection2 = class _TextSelection extends Selection {
10183
10179
  return new _TextSelection($anchor, $head);
10184
10180
  }
10185
10181
  };
10186
- Selection.jsonID("text", TextSelection2);
10182
+ Selection.jsonID("text", TextSelection3);
10187
10183
  var TextBookmark = class _TextBookmark {
10188
10184
  constructor(anchor, head) {
10189
10185
  this.anchor = anchor;
@@ -10193,7 +10189,7 @@ var TextBookmark = class _TextBookmark {
10193
10189
  return new _TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
10194
10190
  }
10195
10191
  resolve(doc) {
10196
- return TextSelection2.between(doc.resolve(this.anchor), doc.resolve(this.head));
10192
+ return TextSelection3.between(doc.resolve(this.anchor), doc.resolve(this.head));
10197
10193
  }
10198
10194
  };
10199
10195
  var NodeSelection = class _NodeSelection extends Selection {
@@ -10312,7 +10308,7 @@ var AllBookmark = {
10312
10308
  };
10313
10309
  function findSelectionIn(doc, node, pos, index, dir, text = false) {
10314
10310
  if (node.inlineContent)
10315
- return TextSelection2.create(doc, pos);
10311
+ return TextSelection3.create(doc, pos);
10316
10312
  for (let i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
10317
10313
  let child = node.child(i);
10318
10314
  if (!child.isAtom) {
@@ -10812,7 +10808,7 @@ function freshColWidth(attrs) {
10812
10808
  for (let i = 0; i < attrs.colspan; i++) result.push(0);
10813
10809
  return result;
10814
10810
  }
10815
- function tableNodeTypes2(schema) {
10811
+ function tableNodeTypes3(schema) {
10816
10812
  let result = schema.cached.tableNodeTypes;
10817
10813
  if (!result) {
10818
10814
  result = schema.cached.tableNodeTypes = {};
@@ -10904,7 +10900,7 @@ var CellSelection2 = class CellSelection3 extends Selection {
10904
10900
  else if (tableChanged && this.isColSelection()) return CellSelection3.colSelection($anchorCell, $headCell);
10905
10901
  else return new CellSelection3($anchorCell, $headCell);
10906
10902
  }
10907
- return TextSelection2.between($anchorCell, $headCell);
10903
+ return TextSelection3.between($anchorCell, $headCell);
10908
10904
  }
10909
10905
  content() {
10910
10906
  const table = this.$anchorCell.node(-1);
@@ -11329,7 +11325,7 @@ function moveTableRow$1(table, indexesOrigin, indexesTarget, direction) {
11329
11325
  rows = moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, direction);
11330
11326
  return convertArrayOfRowsToTableNode(table, rows);
11331
11327
  }
11332
- function selectedRect3(state) {
11328
+ function selectedRect4(state) {
11333
11329
  const sel = state.selection;
11334
11330
  const $pos = selectionCell(state);
11335
11331
  const table = $pos.node(-1);
@@ -11346,8 +11342,8 @@ function deprecated_toggleHeader(type) {
11346
11342
  return function(state, dispatch) {
11347
11343
  if (!isInTable2(state)) return false;
11348
11344
  if (dispatch) {
11349
- const types = tableNodeTypes2(state.schema);
11350
- const rect = selectedRect3(state), tr = state.tr;
11345
+ const types = tableNodeTypes3(state.schema);
11346
+ const rect = selectedRect4(state), tr = state.tr;
11351
11347
  const cells = rect.map.cellsInRect(type == "column" ? {
11352
11348
  left: rect.left,
11353
11349
  top: 0,
@@ -11386,8 +11382,8 @@ function toggleHeader(type, options) {
11386
11382
  return function(state, dispatch) {
11387
11383
  if (!isInTable2(state)) return false;
11388
11384
  if (dispatch) {
11389
- const types = tableNodeTypes2(state.schema);
11390
- const rect = selectedRect3(state), tr = state.tr;
11385
+ const types = tableNodeTypes3(state.schema);
11386
+ const rect = selectedRect4(state), tr = state.tr;
11391
11387
  const isHeaderRowEnabled = isHeaderEnabledByType("row", rect, types);
11392
11388
  const isHeaderColumnEnabled = isHeaderEnabledByType("column", rect, types);
11393
11389
  const selectionStartsAt = (type === "column" ? isHeaderRowEnabled : type === "row" ? isHeaderColumnEnabled : false) ? 1 : 0;
@@ -11421,7 +11417,7 @@ function deleteCellSelection(state, dispatch) {
11421
11417
  if (!(sel instanceof CellSelection2)) return false;
11422
11418
  if (dispatch) {
11423
11419
  const tr = state.tr;
11424
- const baseContent = tableNodeTypes2(state.schema).cell.createAndFill().content;
11420
+ const baseContent = tableNodeTypes3(state.schema).cell.createAndFill().content;
11425
11421
  sel.forEachCell((cell, pos) => {
11426
11422
  if (!cell.content.eq(baseContent)) tr.replace(tr.mapping.map(pos + 1), tr.mapping.map(pos + cell.nodeSize - 1), new Slice(baseContent, 0, 0));
11427
11423
  });
@@ -11519,7 +11515,7 @@ function shiftArrow(axis, dir) {
11519
11515
  };
11520
11516
  }
11521
11517
  function atEndOfCell(view, axis, dir) {
11522
- if (!(view.state.selection instanceof TextSelection2)) return null;
11518
+ if (!(view.state.selection instanceof TextSelection3)) return null;
11523
11519
  const { $head } = view.state.selection;
11524
11520
  for (let d = $head.depth - 1; d >= 0; d--) {
11525
11521
  const parent = $head.node(d);
@@ -11836,7 +11832,8 @@ function buildTableControlLayout(editor, surface, cell) {
11836
11832
  avgRowHeight,
11837
11833
  avgColumnWidth,
11838
11834
  rowHandles,
11839
- columnHandles
11835
+ columnHandles,
11836
+ widthMode: normalizeTableWidthMode(tableInfo.node.attrs.widthMode)
11840
11837
  };
11841
11838
  }
11842
11839
 
@@ -12011,6 +12008,8 @@ function TableAddRails({
12011
12008
  layout,
12012
12009
  onStartAddColumn,
12013
12010
  onStartAddRow,
12011
+ onAddColumn,
12012
+ onAddRow,
12014
12013
  quickAddColumnLabel,
12015
12014
  quickAddRowLabel
12016
12015
  }) {
@@ -12033,17 +12032,16 @@ function TableAddRails({
12033
12032
  type: "button",
12034
12033
  "data-table-control": "add-column",
12035
12034
  "aria-label": quickAddColumnLabel,
12036
- onMouseDown: (event) => {
12037
- event.preventDefault();
12038
- event.stopPropagation();
12039
- if (!canExpandTable) return;
12040
- onStartAddColumn();
12035
+ tabIndex: showColumnRail ? 0 : -1,
12036
+ onPointerDown: onStartAddColumn,
12037
+ onClick: (event) => {
12038
+ if (event.detail === 0 && canExpandTable) onAddColumn();
12041
12039
  },
12042
12040
  disabled: !canExpandTable,
12043
12041
  className: cn(
12044
12042
  "absolute z-30 inline-flex items-center justify-center rounded-md",
12045
12043
  "border border-border/70 bg-muted/40 text-muted-foreground shadow-sm backdrop-blur",
12046
- "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"
12047
12045
  ),
12048
12046
  style: {
12049
12047
  top: columnRailTop,
@@ -12070,17 +12068,16 @@ function TableAddRails({
12070
12068
  type: "button",
12071
12069
  "data-table-control": "add-row",
12072
12070
  "aria-label": quickAddRowLabel,
12073
- onMouseDown: (event) => {
12074
- event.preventDefault();
12075
- event.stopPropagation();
12076
- if (!canExpandTable) return;
12077
- onStartAddRow();
12071
+ tabIndex: showRowRail ? 0 : -1,
12072
+ onPointerDown: onStartAddRow,
12073
+ onClick: (event) => {
12074
+ if (event.detail === 0 && canExpandTable) onAddRow();
12078
12075
  },
12079
12076
  disabled: !canExpandTable,
12080
12077
  className: cn(
12081
12078
  "absolute z-30 inline-flex items-center justify-center rounded-md",
12082
12079
  "border border-border/70 bg-muted/40 text-muted-foreground shadow-sm backdrop-blur",
12083
- "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"
12084
12081
  ),
12085
12082
  style: {
12086
12083
  top: rowRailTop,
@@ -12173,6 +12170,7 @@ function TableRowHandles({
12173
12170
  getMenuItems,
12174
12171
  hoverRowHandleIndex,
12175
12172
  onOpenMenuChange,
12173
+ onMove,
12176
12174
  onStartDrag,
12177
12175
  openMenuKey,
12178
12176
  rowDragLabel,
@@ -12213,13 +12211,16 @@ function TableRowHandles({
12213
12211
  {
12214
12212
  type: "button",
12215
12213
  "aria-label": `${rowDragLabel} ${rowHandle.index + 1}`,
12216
- 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;
12217
12218
  event.preventDefault();
12218
12219
  event.stopPropagation();
12219
- onStartDrag(rowHandle);
12220
+ onMove(rowHandle, event.key === "ArrowUp" ? -1 : 1);
12220
12221
  },
12221
12222
  className: cn(
12222
- "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",
12223
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"
12224
12225
  ),
12225
12226
  style: {
@@ -12230,7 +12231,7 @@ function TableRowHandles({
12230
12231
  // selection near the first character of a table cell.
12231
12232
  pointerEvents: visible ? "auto" : "none"
12232
12233
  },
12233
- 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" })
12234
12235
  }
12235
12236
  )
12236
12237
  }
@@ -12251,6 +12252,7 @@ function TableColumnHandles({
12251
12252
  getMenuItems,
12252
12253
  hoverColumnHandleIndex,
12253
12254
  onOpenMenuChange,
12255
+ onMove,
12254
12256
  onStartDrag,
12255
12257
  openMenuKey
12256
12258
  }) {
@@ -12288,13 +12290,16 @@ function TableColumnHandles({
12288
12290
  {
12289
12291
  type: "button",
12290
12292
  "aria-label": `${columnDragLabel} ${columnHandle.index + 1}`,
12291
- 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;
12292
12297
  event.preventDefault();
12293
12298
  event.stopPropagation();
12294
- onStartDrag(columnHandle);
12299
+ onMove(columnHandle, event.key === "ArrowLeft" ? -1 : 1);
12295
12300
  },
12296
12301
  className: cn(
12297
- "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",
12298
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"
12299
12304
  ),
12300
12305
  style: {
@@ -12302,7 +12307,7 @@ function TableColumnHandles({
12302
12307
  transform: isShown ? "scale(1)" : `scale(${IDLE_HANDLE_SCALE})`,
12303
12308
  pointerEvents: visible ? "auto" : "none"
12304
12309
  },
12305
- 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" })
12306
12311
  }
12307
12312
  )
12308
12313
  }
@@ -12317,7 +12322,7 @@ function TableColumnHandles({
12317
12322
 
12318
12323
  // src/components/UEditor/table-resize-handles.tsx
12319
12324
  import { MoveDiagonal2 } from "lucide-react";
12320
- import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
12325
+ import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
12321
12326
  var HANDLE_BASE_CLASS = cn(
12322
12327
  "pointer-events-auto absolute z-10 inline-flex touch-none items-center justify-center rounded-md",
12323
12328
  "bg-background/95 text-primary shadow-sm backdrop-blur-sm",
@@ -12325,11 +12330,30 @@ var HANDLE_BASE_CLASS = cn(
12325
12330
  "hover:bg-primary hover:text-primary-foreground hover:shadow-md active:scale-95",
12326
12331
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
12327
12332
  );
12333
+ var EDGE_HANDLE_CLASS = cn(
12334
+ "pointer-events-auto absolute top-0 z-10 h-full w-2 touch-none cursor-col-resize bg-transparent p-0",
12335
+ "border-transparent transition-colors duration-150 hover:border-primary",
12336
+ "focus-visible:border-primary focus-visible:outline-none"
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
+ }
12328
12350
  function TableResizeHandles({
12329
12351
  active,
12330
12352
  ctrlHint,
12331
12353
  frameRef,
12332
12354
  layout,
12355
+ onFitWidth,
12356
+ onKeyboardResize,
12333
12357
  onStartResize,
12334
12358
  resizeBothLabel
12335
12359
  }) {
@@ -12350,6 +12374,38 @@ function TableResizeHandles({
12350
12374
  height: layout.tableHeight
12351
12375
  },
12352
12376
  children: [
12377
+ layout.widthMode === "responsive" ? /* @__PURE__ */ jsxs14(Fragment6, { children: [
12378
+ /* @__PURE__ */ jsx17(
12379
+ "button",
12380
+ {
12381
+ type: "button",
12382
+ "aria-label": `${resizeBothLabel} \u2014 \u2190`,
12383
+ title: resizeBothLabel,
12384
+ "data-table-resize-handle": "left",
12385
+ className: cn(
12386
+ EDGE_HANDLE_CLASS,
12387
+ "left-0 border-l-2"
12388
+ ),
12389
+ onPointerDown: (event) => onStartResize(event, "left"),
12390
+ onKeyDown: (event) => onKeyboardResize(event, "left")
12391
+ }
12392
+ ),
12393
+ /* @__PURE__ */ jsx17(
12394
+ "button",
12395
+ {
12396
+ type: "button",
12397
+ "aria-label": `${resizeBothLabel} \u2014 \u2192`,
12398
+ title: resizeBothLabel,
12399
+ "data-table-resize-handle": "right",
12400
+ className: cn(
12401
+ EDGE_HANDLE_CLASS,
12402
+ "right-0 border-r-2"
12403
+ ),
12404
+ onPointerDown: (event) => onStartResize(event, "right"),
12405
+ onKeyDown: (event) => onKeyboardResize(event, "right")
12406
+ }
12407
+ )
12408
+ ] }) : null,
12353
12409
  /* @__PURE__ */ jsx17(
12354
12410
  "button",
12355
12411
  {
@@ -12362,7 +12418,12 @@ function TableResizeHandles({
12362
12418
  "bottom-[-28px] right-[-30px] h-6 w-6 cursor-nwse-resize",
12363
12419
  active && "bg-primary text-primary-foreground shadow-md"
12364
12420
  ),
12365
- onPointerDown: onStartResize,
12421
+ onDoubleClick: onFitWidth,
12422
+ onClick: (event) => {
12423
+ if (event.detail === 0) onFitWidth(event);
12424
+ },
12425
+ onPointerDown: (event) => onStartResize(event, "both"),
12426
+ onKeyDown: (event) => onKeyboardResize(event, "both"),
12366
12427
  children: /* @__PURE__ */ jsx17(MoveDiagonal2, { "aria-hidden": "true", className: "h-3.5 w-3.5", strokeWidth: 2.25 })
12367
12428
  }
12368
12429
  ),
@@ -12373,7 +12434,12 @@ function TableResizeHandles({
12373
12434
  "aria-live": "polite",
12374
12435
  "data-table-resize-dimensions": "",
12375
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",
12376
- 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
+ })
12377
12443
  }
12378
12444
  ) : null
12379
12445
  ]
@@ -12424,7 +12490,7 @@ function getLogicalRowHeights(table, fallback) {
12424
12490
  });
12425
12491
  return heights;
12426
12492
  }
12427
- function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight) {
12493
+ function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight, containerWidth = startWidth) {
12428
12494
  const tableInfo = findTableInfo(editor, anchorPos);
12429
12495
  if (!tableInfo) return null;
12430
12496
  const tableMap = TableMap3.get(tableInfo.node);
@@ -12433,13 +12499,16 @@ function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight) {
12433
12499
  const safeHeight = Math.max(tableMap.height * MIN_TABLE_ROW_HEIGHT, Math.round(startHeight));
12434
12500
  const fallbackColumnWidth = safeWidth / tableMap.width;
12435
12501
  const fallbackRowHeight = safeHeight / tableMap.height;
12502
+ const widthMode = normalizeTableWidthMode(tableInfo.node.attrs.widthMode);
12503
+ const safeContainerWidth = Math.max(1, Math.round(containerWidth));
12504
+ const columnWeights = widthMode === "responsive" ? getTableColumnRatios(tableInfo.node) : getLogicalColumnWidths(tableInfo.node, tableMap, fallbackColumnWidth);
12436
12505
  return {
12437
12506
  anchorPos,
12438
12507
  tablePos: tableInfo.pos,
12439
12508
  startWidth: safeWidth,
12440
12509
  startHeight: safeHeight,
12441
12510
  columnWidths: normalizeWeightsToTotal(
12442
- getLogicalColumnWidths(tableInfo.node, tableMap, fallbackColumnWidth),
12511
+ columnWeights,
12443
12512
  safeWidth,
12444
12513
  MIN_RESIZED_TABLE_COLUMN_WIDTH
12445
12514
  ),
@@ -12449,7 +12518,12 @@ function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight) {
12449
12518
  MIN_TABLE_ROW_HEIGHT
12450
12519
  ),
12451
12520
  minWidth: tableMap.width * MIN_RESIZED_TABLE_COLUMN_WIDTH,
12452
- minHeight: tableMap.height * MIN_TABLE_ROW_HEIGHT
12521
+ minHeight: tableMap.height * MIN_TABLE_ROW_HEIGHT,
12522
+ containerWidth: safeContainerWidth,
12523
+ widthMode,
12524
+ widthBp: getResponsiveTableWidthBp(tableInfo.node),
12525
+ offsetBp: getResponsiveTableOffsetBp(tableInfo.node),
12526
+ columnRatios: getTableColumnRatios(tableInfo.node)
12453
12527
  };
12454
12528
  }
12455
12529
  function resolveTableResizeDimensions({
@@ -12457,12 +12531,14 @@ function resolveTableResizeDimensions({
12457
12531
  deltaY,
12458
12532
  lockAxis,
12459
12533
  preserveRatio,
12534
+ edge = "both",
12460
12535
  snapshot
12461
12536
  }) {
12462
- let width = snapshot.startWidth + deltaX;
12463
- let height = snapshot.startHeight + deltaY;
12537
+ const horizontalDelta = edge === "left" ? -deltaX : deltaX;
12538
+ let width = snapshot.startWidth + horizontalDelta;
12539
+ let height = edge === "both" ? snapshot.startHeight + deltaY : snapshot.startHeight;
12464
12540
  if (preserveRatio) {
12465
- const horizontalDrag = Math.abs(deltaX) >= Math.abs(deltaY);
12541
+ const horizontalDrag = edge !== "both" || Math.abs(deltaX) >= Math.abs(deltaY);
12466
12542
  const scale = horizontalDrag ? width / snapshot.startWidth : height / snapshot.startHeight;
12467
12543
  const minScale = Math.max(
12468
12544
  snapshot.minWidth / snapshot.startWidth,
@@ -12475,22 +12551,56 @@ function resolveTableResizeDimensions({
12475
12551
  const safeScale = Math.min(Math.max(scale, minScale), maxScale);
12476
12552
  width = snapshot.startWidth * safeScale;
12477
12553
  height = snapshot.startHeight * safeScale;
12478
- } else if (lockAxis) {
12554
+ } else if (lockAxis && edge === "both") {
12479
12555
  if (Math.abs(deltaX) >= Math.abs(deltaY)) {
12480
12556
  height = snapshot.startHeight;
12481
12557
  } else {
12482
12558
  width = snapshot.startWidth;
12483
12559
  }
12484
12560
  }
12561
+ let nextWidth = Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minWidth, Math.round(width)));
12562
+ const nextHeight = Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minHeight, Math.round(height)));
12563
+ if (snapshot.widthMode !== "responsive") {
12564
+ return { width: nextWidth, height: nextHeight };
12565
+ }
12566
+ const containerWidth = Math.max(1, snapshot.containerWidth);
12567
+ const startOffset = snapshot.offsetBp / TABLE_WIDTH_BASIS_POINTS * containerWidth;
12568
+ let nextOffset = startOffset;
12569
+ if (edge === "left") {
12570
+ const fixedRightEdge = startOffset + snapshot.startWidth;
12571
+ nextOffset = Math.min(
12572
+ fixedRightEdge - snapshot.minWidth,
12573
+ Math.max(0, startOffset + deltaX)
12574
+ );
12575
+ nextWidth = fixedRightEdge - nextOffset;
12576
+ }
12577
+ const widthBp = clampResponsiveTableWidthBp(
12578
+ nextWidth / containerWidth * TABLE_WIDTH_BASIS_POINTS
12579
+ );
12580
+ const offsetBp = resolveResponsiveTableOffsetBp(
12581
+ widthBp,
12582
+ null,
12583
+ nextOffset / containerWidth * TABLE_WIDTH_BASIS_POINTS
12584
+ );
12485
12585
  return {
12486
- width: Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minWidth, Math.round(width))),
12487
- height: Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minHeight, Math.round(height)))
12586
+ width: Math.round(widthBp / TABLE_WIDTH_BASIS_POINTS * containerWidth),
12587
+ height: nextHeight,
12588
+ widthBp,
12589
+ offsetBp,
12590
+ leftDelta: nextOffset - startOffset
12488
12591
  };
12489
12592
  }
12490
12593
  function arraysEqual(left, right) {
12491
12594
  return Array.isArray(left) && left.length === right.length && left.every((value, index) => value === right[index]);
12492
12595
  }
12493
- function applyTableSize(editor, snapshot, dimensions) {
12596
+ function getResponsiveResizeAlignment(widthBp, offsetBp) {
12597
+ const gap = Math.max(0, TABLE_WIDTH_BASIS_POINTS - widthBp);
12598
+ if (offsetBp === 0) return "left";
12599
+ if (offsetBp === gap) return "right";
12600
+ if (Math.abs(offsetBp * 2 - gap) <= 1) return "center";
12601
+ return null;
12602
+ }
12603
+ function applyTableSize(editor, snapshot, dimensions, options = {}) {
12494
12604
  const table = editor.state.doc.nodeAt(snapshot.tablePos);
12495
12605
  if (!table || table.type.name !== "table") return false;
12496
12606
  const tableMap = TableMap3.get(table);
@@ -12499,9 +12609,32 @@ function applyTableSize(editor, snapshot, dimensions) {
12499
12609
  }
12500
12610
  const resizeWidth = dimensions.width !== snapshot.startWidth;
12501
12611
  const resizeHeight = dimensions.height !== snapshot.startHeight;
12502
- if (!resizeWidth && !resizeHeight) return false;
12612
+ const widthMode = options.widthMode ?? snapshot.widthMode;
12613
+ const widthModeChanged = normalizeTableWidthMode(table.attrs.widthMode) !== widthMode || widthMode === "responsive" && table.attrs.widthMode !== "responsive";
12614
+ const widthBp = dimensions.widthBp ?? clampResponsiveTableWidthBp(
12615
+ dimensions.width / Math.max(1, snapshot.containerWidth) * TABLE_WIDTH_BASIS_POINTS
12616
+ );
12617
+ const offsetBp = resolveResponsiveTableOffsetBp(
12618
+ widthBp,
12619
+ null,
12620
+ dimensions.offsetBp ?? snapshot.offsetBp
12621
+ );
12622
+ const responsiveLayoutChanged = widthMode === "responsive" && (table.attrs.widthBp !== widthBp || table.attrs.offsetBp !== offsetBp);
12623
+ if (!resizeWidth && !resizeHeight && !widthModeChanged && !responsiveLayoutChanged) return false;
12503
12624
  const tableStart = snapshot.tablePos + 1;
12504
12625
  const transaction = editor.state.tr;
12626
+ if (widthModeChanged || responsiveLayoutChanged) {
12627
+ transaction.setNodeMarkup(snapshot.tablePos, void 0, {
12628
+ ...table.attrs,
12629
+ widthMode,
12630
+ ...widthMode === "responsive" ? {
12631
+ widthBp,
12632
+ offsetBp,
12633
+ columnRatios: normalizeColumnRatios(snapshot.columnRatios, tableMap.width),
12634
+ textAlign: getResponsiveResizeAlignment(widthBp, offsetBp)
12635
+ } : null
12636
+ });
12637
+ }
12505
12638
  if (resizeWidth) {
12506
12639
  const nextColumnWidths = normalizeWeightsToTotal(
12507
12640
  snapshot.columnWidths,
@@ -12544,9 +12677,12 @@ function applyTableSize(editor, snapshot, dimensions) {
12544
12677
  }
12545
12678
 
12546
12679
  // src/components/UEditor/table-controls.tsx
12547
- import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
12680
+ import { Fragment as Fragment7, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
12548
12681
  var TABLE_MENU_TOP_OFFSET = 10;
12549
12682
  var AXIS_HANDLE_RADIUS = 12;
12683
+ function clamp2(value, min, max) {
12684
+ return Math.min(Math.max(value, min), max);
12685
+ }
12550
12686
  function nearestIndex(centers, position) {
12551
12687
  let bestIndex = 0;
12552
12688
  let bestDistance = Number.POSITIVE_INFINITY;
@@ -12577,21 +12713,21 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12577
12713
  const t = useSmartTranslations("UEditor");
12578
12714
  const editorDocument = editor.view.dom.ownerDocument;
12579
12715
  const editorWindow = editorDocument.defaultView;
12580
- const [layout, setLayout] = React10.useState(null);
12581
- const [dragPreview, setDragPreview] = React10.useState(null);
12582
- const [tableResizeActive, setTableResizeActive] = React10.useState(false);
12583
- const [hoverState, setHoverState] = React10.useState(DEFAULT_TABLE_HOVER_STATE);
12584
- const [openMenuKey, setOpenMenuKey] = React10.useState(null);
12585
- const layoutRef = React10.useRef(null);
12586
- const dragStateRef = React10.useRef(null);
12587
- const syncFrameRef = React10.useRef(null);
12588
- const layoutRefreshFrameRef = React10.useRef(null);
12589
- const tableResizeFrameRef = React10.useRef(null);
12590
- const tableResizePreviewFrameRef = React10.useRef(null);
12591
- 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(() => {
12592
12728
  layoutRef.current = layout;
12593
12729
  }, [layout]);
12594
- const syncFromCell = React10.useCallback((cell) => {
12730
+ const syncFromCell = React11.useCallback((cell) => {
12595
12731
  const surface = containerRef.current;
12596
12732
  if (!surface || !cell) {
12597
12733
  layoutRef.current = null;
@@ -12602,10 +12738,10 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12602
12738
  layoutRef.current = nextLayout;
12603
12739
  setLayout(nextLayout);
12604
12740
  }, [containerRef, editor]);
12605
- const syncFromSelection = React10.useCallback(() => {
12741
+ const syncFromSelection = React11.useCallback(() => {
12606
12742
  syncFromCell(getSelectedCell(editor));
12607
12743
  }, [editor, syncFromCell]);
12608
- const scheduleSyncFromSelection = React10.useCallback(() => {
12744
+ const scheduleSyncFromSelection = React11.useCallback(() => {
12609
12745
  if (syncFrameRef.current !== null) return;
12610
12746
  if (!editorWindow) return;
12611
12747
  syncFrameRef.current = editorWindow.requestAnimationFrame(() => {
@@ -12613,13 +12749,13 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12613
12749
  syncFromSelection();
12614
12750
  });
12615
12751
  }, [editorWindow, syncFromSelection]);
12616
- React10.useEffect(() => () => {
12752
+ React11.useEffect(() => () => {
12617
12753
  if (syncFrameRef.current !== null) {
12618
12754
  editorWindow?.cancelAnimationFrame(syncFrameRef.current);
12619
12755
  syncFrameRef.current = null;
12620
12756
  }
12621
12757
  }, [editorWindow]);
12622
- const refreshCurrentLayout = React10.useCallback(() => {
12758
+ const refreshCurrentLayout = React11.useCallback(() => {
12623
12759
  const currentLayout = layoutRef.current;
12624
12760
  if (!currentLayout) return;
12625
12761
  const surface = containerRef.current;
@@ -12634,7 +12770,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12634
12770
  layoutRef.current = nextLayout;
12635
12771
  setLayout(nextLayout);
12636
12772
  }, [containerRef, editor]);
12637
- const scheduleCurrentLayoutRefresh = React10.useCallback(() => {
12773
+ const scheduleCurrentLayoutRefresh = React11.useCallback(() => {
12638
12774
  if (!layoutRef.current || layoutRefreshFrameRef.current !== null) return;
12639
12775
  if (!editorWindow) return;
12640
12776
  layoutRefreshFrameRef.current = editorWindow.requestAnimationFrame(() => {
@@ -12642,31 +12778,42 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12642
12778
  refreshCurrentLayout();
12643
12779
  });
12644
12780
  }, [editorWindow, refreshCurrentLayout]);
12645
- React10.useEffect(() => () => {
12781
+ React11.useEffect(() => () => {
12646
12782
  if (layoutRefreshFrameRef.current !== null) {
12647
12783
  editorWindow?.cancelAnimationFrame(layoutRefreshFrameRef.current);
12648
12784
  layoutRefreshFrameRef.current = null;
12649
12785
  }
12650
12786
  }, [editorWindow]);
12651
- const updateTableResizePreview = React10.useCallback((dimensions) => {
12787
+ const updateTableResizePreview = React11.useCallback((dimensions) => {
12652
12788
  const frame = tableResizeFrameRef.current;
12653
12789
  if (!frame) return;
12654
12790
  frame.style.width = `${dimensions.width}px`;
12655
12791
  frame.style.height = `${dimensions.height}px`;
12792
+ const activeLayout = layoutRef.current;
12793
+ if (activeLayout) {
12794
+ frame.style.left = `${activeLayout.tableLeft + (dimensions.leftDelta ?? 0)}px`;
12795
+ }
12656
12796
  const dimensionsLabel = frame.querySelector("[data-table-resize-dimensions]");
12657
- if (dimensionsLabel) {
12658
- 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
+ });
12659
12805
  }
12660
12806
  }, []);
12661
- const resetTableResizePreview = React10.useCallback(() => {
12807
+ const resetTableResizePreview = React11.useCallback(() => {
12662
12808
  const activeLayout = layoutRef.current;
12663
12809
  if (!activeLayout) return;
12664
12810
  updateTableResizePreview({
12665
12811
  width: Math.round(activeLayout.tableWidth),
12666
- height: Math.round(activeLayout.tableHeight)
12812
+ height: Math.round(activeLayout.tableHeight),
12813
+ leftDelta: 0
12667
12814
  });
12668
12815
  }, [updateTableResizePreview]);
12669
- const clearDrag = React10.useCallback(() => {
12816
+ const clearDrag = React11.useCallback(() => {
12670
12817
  const dragState = dragStateRef.current;
12671
12818
  if (dragState?.kind === "resize-table") {
12672
12819
  try {
@@ -12686,7 +12833,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12686
12833
  resetTableResizePreview();
12687
12834
  setDocumentCursor(editorDocument, "");
12688
12835
  }, [editorDocument, editorWindow, resetTableResizePreview]);
12689
- const updateHoverState = React10.useCallback((event) => {
12836
+ const updateHoverState = React11.useCallback((event) => {
12690
12837
  const activeLayout = layoutRef.current;
12691
12838
  const surface = containerRef.current;
12692
12839
  if (!activeLayout || !surface || dragStateRef.current || event.buttons !== 0) {
@@ -12702,7 +12849,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12702
12849
  return areTableHoverStatesEqual(prev, nextState) ? prev : nextState;
12703
12850
  });
12704
12851
  }, [containerRef]);
12705
- React10.useEffect(() => {
12852
+ React11.useEffect(() => {
12706
12853
  const proseMirror = editor.view.dom;
12707
12854
  const surface = containerRef.current;
12708
12855
  if (!surface) return void 0;
@@ -12789,37 +12936,37 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12789
12936
  editor.off("update", scheduleCurrentLayoutRefresh);
12790
12937
  };
12791
12938
  }, [clearDrag, containerRef, editor, editorWindow, scheduleCurrentLayoutRefresh, scheduleSyncFromSelection, syncFromCell, syncFromSelection, updateHoverState]);
12792
- const runAtCellPos = React10.useCallback((cellPos, command, options) => {
12939
+ const runAtCellPos = React11.useCallback((cellPos, command, options) => {
12793
12940
  const result = runTableCommandAtCellPos(editor, cellPos, command);
12794
12941
  if (options?.sync !== false) {
12795
12942
  scheduleSyncFromSelection();
12796
12943
  }
12797
12944
  return result;
12798
12945
  }, [editor, scheduleSyncFromSelection]);
12799
- const runAtActiveCell = React10.useCallback((command, options) => {
12946
+ const runAtActiveCell = React11.useCallback((command, options) => {
12800
12947
  return runAtCellPos(layoutRef.current?.cellPos ?? null, command, options);
12801
12948
  }, [runAtCellPos]);
12802
- const duplicateRowAt = React10.useCallback((rowIndex, cellPos) => {
12949
+ const duplicateRowAt = React11.useCallback((rowIndex, cellPos) => {
12803
12950
  const result = duplicateTableRowAt(editor, rowIndex, cellPos);
12804
12951
  scheduleSyncFromSelection();
12805
12952
  return result;
12806
12953
  }, [editor, scheduleSyncFromSelection]);
12807
- const clearRowAt = React10.useCallback((rowIndex, cellPos) => {
12954
+ const clearRowAt = React11.useCallback((rowIndex, cellPos) => {
12808
12955
  const result = clearTableRowAt(editor, rowIndex, cellPos);
12809
12956
  scheduleSyncFromSelection();
12810
12957
  return result;
12811
12958
  }, [editor, scheduleSyncFromSelection]);
12812
- const duplicateColumnAt = React10.useCallback((columnIndex, cellPos) => {
12959
+ const duplicateColumnAt = React11.useCallback((columnIndex, cellPos) => {
12813
12960
  const result = duplicateTableColumnAt(editor, columnIndex, cellPos);
12814
12961
  scheduleSyncFromSelection();
12815
12962
  return result;
12816
12963
  }, [editor, scheduleSyncFromSelection]);
12817
- const clearColumnAt = React10.useCallback((columnIndex, cellPos) => {
12964
+ const clearColumnAt = React11.useCallback((columnIndex, cellPos) => {
12818
12965
  const result = clearTableColumnAt(editor, columnIndex, cellPos);
12819
12966
  scheduleSyncFromSelection();
12820
12967
  return result;
12821
12968
  }, [editor, scheduleSyncFromSelection]);
12822
- const expandTableBy = React10.useCallback((rows, cols) => {
12969
+ const expandTableBy = React11.useCallback((rows, cols) => {
12823
12970
  const activeCellPos = layoutRef.current?.cellPos ?? editor.state.selection.from;
12824
12971
  const result = expandTableFromCell(editor, activeCellPos, rows, cols);
12825
12972
  scheduleSyncFromSelection();
@@ -12828,7 +12975,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12828
12975
  const canExpandTable = Boolean(layout);
12829
12976
  const controlsVisible = false;
12830
12977
  const tableMenuOpen = openMenuKey === "table";
12831
- const startTableResize = React10.useCallback((event) => {
12978
+ const startTableResize = React11.useCallback((event, edge) => {
12832
12979
  if (event.button !== 0 || dragStateRef.current) return;
12833
12980
  const activeLayout = layoutRef.current;
12834
12981
  if (!activeLayout) return;
@@ -12836,7 +12983,8 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12836
12983
  editor,
12837
12984
  activeLayout.cellPos,
12838
12985
  activeLayout.tableWidth,
12839
- activeLayout.tableHeight
12986
+ activeLayout.tableHeight,
12987
+ activeLayout.viewportWidth
12840
12988
  );
12841
12989
  if (!snapshot) return;
12842
12990
  event.preventDefault();
@@ -12858,25 +13006,89 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12858
13006
  pointerTarget: event.currentTarget,
12859
13007
  startX: event.clientX,
12860
13008
  startY: event.clientY,
13009
+ edge,
12861
13010
  snapshot,
12862
13011
  pendingDimensions
12863
13012
  };
12864
13013
  updateTableResizePreview(pendingDimensions);
12865
- setDocumentCursor(editorDocument, "nwse-resize");
13014
+ setDocumentCursor(editorDocument, edge === "both" ? "nwse-resize" : "ew-resize");
12866
13015
  }, [editor, editorDocument, updateTableResizePreview]);
12867
- const startAddColumnDrag = React10.useCallback(() => {
13016
+ const fitTableToEditorWidth = React11.useCallback((event) => {
13017
+ if (event.button !== 0 || dragStateRef.current) return;
13018
+ const activeLayout = layoutRef.current;
13019
+ if (!activeLayout) return;
13020
+ const snapshot = createTableSizeSnapshot(
13021
+ editor,
13022
+ activeLayout.cellPos,
13023
+ activeLayout.tableWidth,
13024
+ activeLayout.tableHeight,
13025
+ activeLayout.viewportWidth
13026
+ );
13027
+ if (!snapshot) return;
13028
+ event.preventDefault();
13029
+ event.stopPropagation();
13030
+ const didResize = applyTableSize(editor, snapshot, {
13031
+ width: Math.round(activeLayout.viewportWidth),
13032
+ height: snapshot.startHeight,
13033
+ widthBp: TABLE_WIDTH_BASIS_POINTS,
13034
+ offsetBp: 0,
13035
+ leftDelta: -(snapshot.offsetBp / TABLE_WIDTH_BASIS_POINTS * snapshot.containerWidth)
13036
+ }, { widthMode: "responsive" });
13037
+ if (didResize) {
13038
+ scheduleSyncFromSelection();
13039
+ }
13040
+ }, [editor, scheduleSyncFromSelection]);
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();
12868
13074
  setOpenMenuKey(null);
12869
13075
  dragStateRef.current = { kind: "add-column", previewCols: 1 };
12870
13076
  setDragPreview({ kind: "add-column", previewCols: 1 });
12871
13077
  setDocumentCursor(editorDocument, "ew-resize");
12872
13078
  }, [editorDocument]);
12873
- const startAddRowDrag = React10.useCallback(() => {
13079
+ const startAddRowDrag = React11.useCallback((event) => {
13080
+ if (event.button !== 0 || dragStateRef.current) return;
13081
+ event.preventDefault();
13082
+ event.stopPropagation();
12874
13083
  setOpenMenuKey(null);
12875
13084
  dragStateRef.current = { kind: "add-row", previewRows: 1 };
12876
13085
  setDragPreview({ kind: "add-row", previewRows: 1 });
12877
13086
  setDocumentCursor(editorDocument, "ns-resize");
12878
13087
  }, [editorDocument]);
12879
- 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();
12880
13092
  setOpenMenuKey(null);
12881
13093
  dragStateRef.current = {
12882
13094
  kind: "row",
@@ -12893,7 +13105,10 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12893
13105
  });
12894
13106
  setDocumentCursor(editorDocument, "grabbing");
12895
13107
  }, [editorDocument]);
12896
- 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();
12897
13112
  setOpenMenuKey(null);
12898
13113
  dragStateRef.current = {
12899
13114
  kind: "column",
@@ -12910,9 +13125,62 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12910
13125
  });
12911
13126
  setDocumentCursor(editorDocument, "grabbing");
12912
13127
  }, [editorDocument]);
12913
- 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(() => {
12914
13182
  if (!editorWindow) return;
12915
- const handleMouseMove2 = (event) => {
13183
+ const handlePointerMove = (event) => {
12916
13184
  const dragState = dragStateRef.current;
12917
13185
  const activeLayout = layoutRef.current;
12918
13186
  const surface = containerRef.current;
@@ -12965,25 +13233,70 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12965
13233
  setDragPreview({ kind: "add-row", previewRows });
12966
13234
  setDocumentCursor(editorDocument, "ns-resize");
12967
13235
  };
12968
- const handleMouseUp = () => {
13236
+ const handlePointerUp = () => {
12969
13237
  const dragState = dragStateRef.current;
12970
13238
  if (!dragState || dragState.kind === "resize-table") return;
12971
13239
  if (dragState.kind === "row" && dragState.originIndex !== dragState.targetIndex) {
13240
+ const tableInfo = findTableInfo(editor, dragState.anchorPos);
12972
13241
  moveTableRow({
12973
13242
  from: dragState.originIndex,
12974
13243
  to: dragState.targetIndex,
12975
13244
  pos: dragState.anchorPos,
12976
13245
  select: true
12977
- })(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
+ });
12978
13261
  scheduleSyncFromSelection();
12979
13262
  }
12980
13263
  if (dragState.kind === "column" && dragState.originIndex !== dragState.targetIndex) {
13264
+ const tableInfo = findTableInfo(editor, dragState.anchorPos);
13265
+ const responsiveRatios = tableInfo && normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive" ? moveResponsiveColumnRatio(
13266
+ getTableColumnRatios(tableInfo.node),
13267
+ dragState.originIndex,
13268
+ dragState.targetIndex
13269
+ ) : null;
12981
13270
  moveTableColumn({
12982
13271
  from: dragState.originIndex,
12983
13272
  to: dragState.targetIndex,
12984
13273
  pos: dragState.anchorPos,
12985
13274
  select: true
12986
- })(editor.state, editor.view.dispatch);
13275
+ })(editor.state, (transaction) => {
13276
+ if (tableInfo && responsiveRatios) {
13277
+ const nextTable2 = transaction.doc.nodeAt(tableInfo.pos);
13278
+ if (nextTable2?.type.name === "table") {
13279
+ transaction.setNodeMarkup(tableInfo.pos, void 0, {
13280
+ ...nextTable2.attrs,
13281
+ widthMode: "responsive",
13282
+ columnRatios: responsiveRatios
13283
+ });
13284
+ }
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
+ }
13298
+ editor.view.dispatch(transaction);
13299
+ });
12987
13300
  scheduleSyncFromSelection();
12988
13301
  }
12989
13302
  if (dragState.kind === "add-row") {
@@ -12994,20 +13307,23 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
12994
13307
  }
12995
13308
  clearDrag();
12996
13309
  };
12997
- const unsubscribeMouseMove = subscribeSharedGlobalEvent(
13310
+ const unsubscribePointerMove = subscribeSharedGlobalEvent(
12998
13311
  editorWindow,
12999
- "mousemove",
13000
- handleMouseMove2
13312
+ "pointermove",
13313
+ handlePointerMove,
13314
+ { passive: false }
13001
13315
  );
13002
- const unsubscribeMouseUp = subscribeSharedGlobalEvent(editorWindow, "mouseup", handleMouseUp);
13316
+ const unsubscribePointerUp = subscribeSharedGlobalEvent(editorWindow, "pointerup", handlePointerUp);
13317
+ const unsubscribePointerCancel = subscribeSharedGlobalEvent(editorWindow, "pointercancel", clearDrag);
13003
13318
  const unsubscribeBlur = subscribeSharedGlobalEvent(editorWindow, "blur", clearDrag);
13004
13319
  return () => {
13005
- unsubscribeMouseMove();
13006
- unsubscribeMouseUp();
13320
+ unsubscribePointerMove();
13321
+ unsubscribePointerUp();
13322
+ unsubscribePointerCancel();
13007
13323
  unsubscribeBlur();
13008
13324
  };
13009
13325
  }, [clearDrag, containerRef, editor, editorDocument, editorWindow, expandTableBy, scheduleSyncFromSelection]);
13010
- React10.useEffect(() => {
13326
+ React11.useEffect(() => {
13011
13327
  if (!editorWindow) return;
13012
13328
  const handlePointerMove = (event) => {
13013
13329
  const dragState = dragStateRef.current;
@@ -13017,6 +13333,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13017
13333
  deltaY: event.clientY - dragState.startY,
13018
13334
  lockAxis: event.ctrlKey && !event.shiftKey,
13019
13335
  preserveRatio: event.ctrlKey && event.shiftKey,
13336
+ edge: dragState.edge,
13020
13337
  snapshot: dragState.snapshot
13021
13338
  });
13022
13339
  if (tableResizePreviewFrameRef.current === null) {
@@ -13028,7 +13345,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13028
13345
  updateTableResizePreview(currentDragState.pendingDimensions);
13029
13346
  });
13030
13347
  }
13031
- setDocumentCursor(editorDocument, "nwse-resize");
13348
+ setDocumentCursor(editorDocument, dragState.edge === "both" ? "nwse-resize" : "ew-resize");
13032
13349
  if (event.cancelable) event.preventDefault();
13033
13350
  };
13034
13351
  const finishTableResize = (event, commit) => {
@@ -13039,7 +13356,12 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13039
13356
  tableResizePreviewFrameRef.current = null;
13040
13357
  }
13041
13358
  updateTableResizePreview(dragState.pendingDimensions);
13042
- if (commit && applyTableSize(editor, dragState.snapshot, dragState.pendingDimensions)) {
13359
+ if (commit && applyTableSize(
13360
+ editor,
13361
+ dragState.snapshot,
13362
+ dragState.pendingDimensions,
13363
+ { widthMode: dragState.snapshot.widthMode }
13364
+ )) {
13043
13365
  scheduleSyncFromSelection();
13044
13366
  }
13045
13367
  clearDrag();
@@ -13072,7 +13394,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13072
13394
  }
13073
13395
  };
13074
13396
  }, [clearDrag, editor, editorDocument, editorWindow, scheduleSyncFromSelection, updateTableResizePreview]);
13075
- const openCellInspector = React10.useCallback(() => {
13397
+ const openCellInspector = React11.useCallback(() => {
13076
13398
  const cellPos = layoutRef.current?.cellPos;
13077
13399
  if (!showCellInspector || cellPos == null) return;
13078
13400
  const didSelect = editor.commands.setCellSelection({
@@ -13084,7 +13406,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13084
13406
  if (!editor.isDestroyed) editor.view.focus();
13085
13407
  });
13086
13408
  }, [editor, editorWindow, showCellInspector]);
13087
- const menuItems = React10.useMemo(() => {
13409
+ const menuItems = React11.useMemo(() => {
13088
13410
  if (!layout) return [];
13089
13411
  return [
13090
13412
  ...showCellInspector ? [{
@@ -13157,7 +13479,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13157
13479
  }
13158
13480
  ];
13159
13481
  }, [editor, layout, openCellInspector, runAtActiveCell, showCellInspector, t]);
13160
- const getRowHandleMenuItems = React10.useCallback((rowHandle) => [
13482
+ const getRowHandleMenuItems = React11.useCallback((rowHandle) => [
13161
13483
  {
13162
13484
  label: t("tableMenu.addRowBefore"),
13163
13485
  icon: ArrowUp,
@@ -13185,7 +13507,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13185
13507
  destructive: true
13186
13508
  }
13187
13509
  ], [clearRowAt, duplicateRowAt, runAtCellPos, t]);
13188
- const getColumnHandleMenuItems = React10.useCallback((columnHandle) => [
13510
+ const getColumnHandleMenuItems = React11.useCallback((columnHandle) => [
13189
13511
  {
13190
13512
  label: t("tableMenu.addColumnBefore"),
13191
13513
  icon: ArrowLeft,
@@ -13220,7 +13542,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13220
13542
  const menuLeft = Math.max(8, layout.tableLeft);
13221
13543
  const rowHandleLeft = layout.tableLeft - AXIS_HANDLE_RADIUS;
13222
13544
  const columnHandleTop = layout.tableTop - AXIS_HANDLE_RADIUS;
13223
- return /* @__PURE__ */ jsxs15(Fragment6, { children: [
13545
+ return /* @__PURE__ */ jsxs15(Fragment7, { children: [
13224
13546
  /* @__PURE__ */ jsx18(
13225
13547
  TableRowHandles,
13226
13548
  {
@@ -13231,6 +13553,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13231
13553
  onOpenMenuChange: (menuKey, open) => {
13232
13554
  setOpenMenuKey((prev) => open ? menuKey : prev === menuKey ? null : prev);
13233
13555
  },
13556
+ onMove: moveRowWithKeyboard,
13234
13557
  onStartDrag: startRowDrag,
13235
13558
  openMenuKey,
13236
13559
  rowDragLabel: t("tableMenu.dragRow"),
@@ -13251,6 +13574,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13251
13574
  onOpenMenuChange: (menuKey, open) => {
13252
13575
  setOpenMenuKey((prev) => open ? menuKey : prev === menuKey ? null : prev);
13253
13576
  },
13577
+ onMove: moveColumnWithKeyboard,
13254
13578
  onStartDrag: startColumnDrag,
13255
13579
  openMenuKey
13256
13580
  }
@@ -13277,6 +13601,8 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13277
13601
  ctrlHint: t("tableMenu.resizeCtrlHint"),
13278
13602
  frameRef: tableResizeFrameRef,
13279
13603
  layout,
13604
+ onFitWidth: fitTableToEditorWidth,
13605
+ onKeyboardResize: resizeTableWithKeyboard,
13280
13606
  onStartResize: startTableResize,
13281
13607
  resizeBothLabel: t("tableMenu.resizeBoth")
13282
13608
  }
@@ -13291,6 +13617,8 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13291
13617
  layout,
13292
13618
  onStartAddColumn: startAddColumnDrag,
13293
13619
  onStartAddRow: startAddRowDrag,
13620
+ onAddColumn: () => expandTableBy(0, 1),
13621
+ onAddRow: () => expandTableBy(1, 0),
13294
13622
  quickAddColumnLabel: t("tableMenu.quickAddColumnAfter"),
13295
13623
  quickAddRowLabel: t("tableMenu.quickAddRowAfter")
13296
13624
  }
@@ -13308,10 +13636,10 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
13308
13636
  }
13309
13637
 
13310
13638
  // src/components/UEditor/use-table-interactions.ts
13311
- import React12, { useEffect as useEffect6, useRef as useRef6 } from "react";
13639
+ import React13, { useEffect as useEffect6, useRef as useRef6 } from "react";
13312
13640
 
13313
13641
  // src/components/UEditor/use-table-row-resize.ts
13314
- import React11, { useRef as useRef5 } from "react";
13642
+ import React12, { useRef as useRef5 } from "react";
13315
13643
  function setRowResizeBodyState(ownerDocument, active) {
13316
13644
  ownerDocument.body.style.userSelect = active ? "none" : "";
13317
13645
  ownerDocument.body.style.cursor = active ? "row-resize" : "";
@@ -13325,15 +13653,15 @@ function useTableRowResize({
13325
13653
  scheduleTableLayoutSync
13326
13654
  }) {
13327
13655
  const stateRef = useRef5(null);
13328
- const syncActiveGuide = React11.useCallback(() => {
13656
+ const syncActiveGuide = React12.useCallback(() => {
13329
13657
  const state = stateRef.current;
13330
13658
  if (!state) return false;
13331
13659
  setHoveredTableCell(state.cellElement);
13332
13660
  showRowGuide(state.tableElement, state.rowElement, state.cellElement, state.pendingHeight);
13333
13661
  return true;
13334
13662
  }, [setHoveredTableCell, showRowGuide]);
13335
- const isResizing = React11.useCallback(() => stateRef.current !== null, []);
13336
- 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) => {
13337
13665
  if (!editor || !isRowResizeHotspot(cell, event.clientX, event.clientY)) {
13338
13666
  return false;
13339
13667
  }
@@ -13362,7 +13690,7 @@ function useTableRowResize({
13362
13690
  event.stopPropagation();
13363
13691
  return true;
13364
13692
  }, [editor, setHoveredTableCell, showRowGuide]);
13365
- const handlePointerMove = React11.useCallback((event) => {
13693
+ const handlePointerMove = React12.useCallback((event) => {
13366
13694
  const state = stateRef.current;
13367
13695
  if (!state) return;
13368
13696
  const nextHeight = Math.max(
@@ -13379,7 +13707,7 @@ function useTableRowResize({
13379
13707
  setRowResizeBodyState(state.rowElement.ownerDocument, true);
13380
13708
  showRowGuide(state.tableElement, state.rowElement, state.cellElement, nextHeight);
13381
13709
  }, [showRowGuide]);
13382
- const handlePointerUp = React11.useCallback((event) => {
13710
+ const handlePointerUp = React12.useCallback((event) => {
13383
13711
  if (!editor) return;
13384
13712
  const state = stateRef.current;
13385
13713
  if (!state) return;
@@ -13404,7 +13732,7 @@ function useTableRowResize({
13404
13732
  clearAllTableResizeHover();
13405
13733
  scheduleTableLayoutSync();
13406
13734
  }, [clearAllTableResizeHover, clearHoveredTableCell, editor, scheduleTableLayoutSync]);
13407
- const cancelResize = React11.useCallback(() => {
13735
+ const cancelResize = React12.useCallback(() => {
13408
13736
  const state = stateRef.current;
13409
13737
  if (!state) return;
13410
13738
  stateRef.current = null;
@@ -13414,7 +13742,7 @@ function useTableRowResize({
13414
13742
  clearAllTableResizeHover();
13415
13743
  scheduleTableLayoutSync();
13416
13744
  }, [clearAllTableResizeHover, clearHoveredTableCell, scheduleTableLayoutSync]);
13417
- const cleanup = React11.useCallback(() => {
13745
+ const cleanup = React12.useCallback(() => {
13418
13746
  const editorDocument = stateRef.current?.rowElement.ownerDocument ?? editor?.view.dom.ownerDocument;
13419
13747
  stateRef.current = null;
13420
13748
  isRowResizingGlobal.active = false;
@@ -13443,16 +13771,16 @@ function useUEditorTableInteractions(editor, editable = true) {
13443
13771
  const activeTableCellRef = useRef6(null);
13444
13772
  const suppressActiveCellHighlightRef = useRef6(false);
13445
13773
  const tableLayoutSyncFrameRef = useRef6(null);
13446
- const getProseMirrorElement = React12.useCallback(() => {
13774
+ const getProseMirrorElement = React13.useCallback(() => {
13447
13775
  return editorContentRef.current?.querySelector(".ProseMirror");
13448
13776
  }, []);
13449
- const setEditorResizeCursor = React12.useCallback((cursor) => {
13777
+ const setEditorResizeCursor = React13.useCallback((cursor) => {
13450
13778
  const proseMirror = getProseMirrorElement();
13451
13779
  if (proseMirror) {
13452
13780
  proseMirror.style.cursor = cursor;
13453
13781
  }
13454
13782
  }, [getProseMirrorElement]);
13455
- const hideColumnGuide = React12.useCallback(() => {
13783
+ const hideColumnGuide = React13.useCallback(() => {
13456
13784
  editorContentRef.current?.classList.remove("resize-cursor");
13457
13785
  getProseMirrorElement()?.classList.remove("resize-cursor");
13458
13786
  const guide = tableColumnGuideRef.current;
@@ -13460,7 +13788,7 @@ function useUEditorTableInteractions(editor, editable = true) {
13460
13788
  guide.style.opacity = "0";
13461
13789
  }
13462
13790
  }, [getProseMirrorElement]);
13463
- const hideRowGuide = React12.useCallback(() => {
13791
+ const hideRowGuide = React13.useCallback(() => {
13464
13792
  editorContentRef.current?.classList.remove("resize-row-cursor");
13465
13793
  getProseMirrorElement()?.classList.remove("resize-row-cursor");
13466
13794
  const guide = tableRowGuideRef.current;
@@ -13470,21 +13798,21 @@ function useUEditorTableInteractions(editor, editable = true) {
13470
13798
  }, [getProseMirrorElement]);
13471
13799
  const hotspotTimerRef = useRef6(null);
13472
13800
  const activeHotspotKeyRef = useRef6(null);
13473
- const clearHotspotTimer = React12.useCallback(() => {
13801
+ const clearHotspotTimer = React13.useCallback(() => {
13474
13802
  if (hotspotTimerRef.current !== null) {
13475
13803
  const realm = editorContentRef.current?.ownerDocument.defaultView;
13476
13804
  realm?.clearTimeout(hotspotTimerRef.current);
13477
13805
  hotspotTimerRef.current = null;
13478
13806
  }
13479
13807
  }, []);
13480
- const clearAllTableResizeHover = React12.useCallback(() => {
13808
+ const clearAllTableResizeHover = React13.useCallback(() => {
13481
13809
  clearHotspotTimer();
13482
13810
  activeHotspotKeyRef.current = null;
13483
13811
  setEditorResizeCursor("");
13484
13812
  hideColumnGuide();
13485
13813
  hideRowGuide();
13486
13814
  }, [clearHotspotTimer, hideColumnGuide, hideRowGuide, setEditorResizeCursor]);
13487
- const updateActiveCellHighlight = React12.useCallback((cell) => {
13815
+ const updateActiveCellHighlight = React13.useCallback((cell) => {
13488
13816
  const surface = editorContentRef.current;
13489
13817
  const highlight = activeTableCellHighlightRef.current;
13490
13818
  if (!highlight) return;
@@ -13499,7 +13827,7 @@ function useUEditorTableInteractions(editor, editable = true) {
13499
13827
  highlight.style.width = `${metrics.width}px`;
13500
13828
  highlight.style.height = `${metrics.height}px`;
13501
13829
  }, []);
13502
- const scheduleTableLayoutSync = React12.useCallback(() => {
13830
+ const scheduleTableLayoutSync = React13.useCallback(() => {
13503
13831
  if (!activeTableCellRef.current || tableLayoutSyncFrameRef.current !== null) return;
13504
13832
  const realm = editorContentRef.current?.ownerDocument.defaultView;
13505
13833
  if (!realm) return;
@@ -13511,7 +13839,7 @@ function useUEditorTableInteractions(editor, editable = true) {
13511
13839
  editorContentRef.current?.dispatchEvent(new realm.CustomEvent(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT));
13512
13840
  });
13513
13841
  }, [updateActiveCellHighlight]);
13514
- const setActiveTableCell = React12.useCallback((cell) => {
13842
+ const setActiveTableCell = React13.useCallback((cell) => {
13515
13843
  if (activeTableCellRef.current === cell) {
13516
13844
  updateActiveCellHighlight(cell);
13517
13845
  return;
@@ -13519,17 +13847,17 @@ function useUEditorTableInteractions(editor, editable = true) {
13519
13847
  activeTableCellRef.current = cell;
13520
13848
  updateActiveCellHighlight(activeTableCellRef.current);
13521
13849
  }, [updateActiveCellHighlight]);
13522
- const clearActiveTableCell = React12.useCallback(() => {
13850
+ const clearActiveTableCell = React13.useCallback(() => {
13523
13851
  activeTableCellRef.current = null;
13524
13852
  updateActiveCellHighlight(null);
13525
13853
  }, [updateActiveCellHighlight]);
13526
- const setHoveredTableCell = React12.useCallback((cell) => {
13854
+ const setHoveredTableCell = React13.useCallback((cell) => {
13527
13855
  hoveredTableCellRef.current = cell;
13528
13856
  }, []);
13529
- const clearHoveredTableCell = React12.useCallback(() => {
13857
+ const clearHoveredTableCell = React13.useCallback(() => {
13530
13858
  hoveredTableCellRef.current = null;
13531
13859
  }, []);
13532
- const showColumnGuide = React12.useCallback((table, row, cell) => {
13860
+ const showColumnGuide = React13.useCallback((table, row, cell) => {
13533
13861
  const surface = editorContentRef.current;
13534
13862
  const guide = tableColumnGuideRef.current;
13535
13863
  if (!surface || !guide) return;
@@ -13543,7 +13871,7 @@ function useUEditorTableInteractions(editor, editable = true) {
13543
13871
  getProseMirrorElement()?.classList.add("resize-cursor");
13544
13872
  setEditorResizeCursor("col-resize");
13545
13873
  }, [getProseMirrorElement, setEditorResizeCursor]);
13546
- const showRowGuide = React12.useCallback((table, row, cell, previewHeight) => {
13874
+ const showRowGuide = React13.useCallback((table, row, cell, previewHeight) => {
13547
13875
  const surface = editorContentRef.current;
13548
13876
  const guide = tableRowGuideRef.current;
13549
13877
  if (!surface || !guide) return;
@@ -13575,7 +13903,7 @@ function useUEditorTableInteractions(editor, editable = true) {
13575
13903
  clearAllTableResizeHover,
13576
13904
  scheduleTableLayoutSync
13577
13905
  });
13578
- const syncActiveTableCellFromSelection = React12.useCallback(() => {
13906
+ const syncActiveTableCellFromSelection = React13.useCallback(() => {
13579
13907
  if (!editor) return;
13580
13908
  if (!editor.isFocused) {
13581
13909
  clearActiveTableCell();
@@ -13830,7 +14158,7 @@ import { useEffect as useEffect7, useRef as useRef7 } from "react";
13830
14158
  import { TableMap as TableMap5 } from "@tiptap/pm/tables";
13831
14159
 
13832
14160
  // src/components/UEditor/table-formula-range-picker.ts
13833
- import { TextSelection as TextSelection3 } from "@tiptap/pm/state";
14161
+ import { TextSelection as TextSelection4 } from "@tiptap/pm/state";
13834
14162
  import { TableMap as TableMap4 } from "@tiptap/pm/tables";
13835
14163
  function getCellText2(cellNode) {
13836
14164
  return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
@@ -13908,7 +14236,7 @@ function cancelFormulaEditing(view) {
13908
14236
  if (!context) return false;
13909
14237
  let tr = view.state.tr.delete(context.cellContentStart, context.cellContentEnd);
13910
14238
  const selectionPos = Math.min(context.cellContentStart, tr.doc.content.size);
13911
- tr = tr.setSelection(TextSelection3.near(tr.doc.resolve(selectionPos)));
14239
+ tr = tr.setSelection(TextSelection4.near(tr.doc.resolve(selectionPos)));
13912
14240
  view.dispatch(tr);
13913
14241
  view.focus();
13914
14242
  return true;
@@ -13916,7 +14244,7 @@ function cancelFormulaEditing(view) {
13916
14244
  function getReferenceInsertionPrefix(view, cellContentStart, insertionPos) {
13917
14245
  const textBeforeCursor = view.state.doc.textBetween(cellContentStart, insertionPos, "", "");
13918
14246
  const isInsideFunctionArguments = /[A-Z]+\([^)]*$/i.test(textBeforeCursor);
13919
- 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);
13920
14248
  return isInsideFunctionArguments && followsReference ? "," : "";
13921
14249
  }
13922
14250
  function findTableForPos(view, pos) {
@@ -13985,7 +14313,7 @@ function replacePickedLabel(view, pickState, nextLabel, currentCell) {
13985
14313
  }
13986
14314
  let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
13987
14315
  const nextTo = pickState.insertedFrom + nextLabel.length;
13988
- tr = tr.setSelection(TextSelection3.create(tr.doc, nextTo));
14316
+ tr = tr.setSelection(TextSelection4.create(tr.doc, nextTo));
13989
14317
  view.dispatch(tr);
13990
14318
  return {
13991
14319
  ...pickState,
@@ -14022,7 +14350,7 @@ function beginFormulaRangePick(view, event) {
14022
14350
  const insertedText = `${prefix}${picked.label}`;
14023
14351
  const insertedFrom = from + prefix.length;
14024
14352
  let tr = view.state.tr.insertText(insertedText, from, to);
14025
- tr = tr.setSelection(TextSelection3.create(tr.doc, from + insertedText.length));
14353
+ tr = tr.setSelection(TextSelection4.create(tr.doc, from + insertedText.length));
14026
14354
  view.dispatch(tr);
14027
14355
  view.focus();
14028
14356
  event.preventDefault();
@@ -14467,7 +14795,7 @@ function TableFormulaBar({ editor }) {
14467
14795
  }
14468
14796
 
14469
14797
  // src/components/UEditor/use-editor-output.ts
14470
- import * as React14 from "react";
14798
+ import * as React15 from "react";
14471
14799
  function normalizeDebounceMs(value) {
14472
14800
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
14473
14801
  }
@@ -14477,18 +14805,18 @@ function useUEditorOutput({
14477
14805
  onJsonChange,
14478
14806
  outputDebounceMs
14479
14807
  }) {
14480
- const callbacksRef = React14.useRef({
14808
+ const callbacksRef = React15.useRef({
14481
14809
  onChange,
14482
14810
  onHtmlChange,
14483
14811
  onJsonChange
14484
14812
  });
14485
- const pendingEditorRef = React14.useRef(null);
14486
- const timeoutRef = React14.useRef(null);
14813
+ const pendingEditorRef = React15.useRef(null);
14814
+ const timeoutRef = React15.useRef(null);
14487
14815
  const debounceMs = normalizeDebounceMs(outputDebounceMs);
14488
- React14.useLayoutEffect(() => {
14816
+ React15.useLayoutEffect(() => {
14489
14817
  callbacksRef.current = { onChange, onHtmlChange, onJsonChange };
14490
14818
  }, [onChange, onHtmlChange, onJsonChange]);
14491
- const flushOutputUpdates = React14.useCallback(() => {
14819
+ const flushOutputUpdates = React15.useCallback(() => {
14492
14820
  if (timeoutRef.current !== null) {
14493
14821
  clearTimeout(timeoutRef.current);
14494
14822
  timeoutRef.current = null;
@@ -14506,7 +14834,7 @@ function useUEditorOutput({
14506
14834
  callbacks.onJsonChange(editor.getJSON());
14507
14835
  }
14508
14836
  }, []);
14509
- const scheduleOutputUpdate = React14.useCallback((editor) => {
14837
+ const scheduleOutputUpdate = React15.useCallback((editor) => {
14510
14838
  const callbacks = callbacksRef.current;
14511
14839
  if (!callbacks.onChange && !callbacks.onHtmlChange && !callbacks.onJsonChange) return;
14512
14840
  pendingEditorRef.current = editor;
@@ -14520,32 +14848,98 @@ function useUEditorOutput({
14520
14848
  }
14521
14849
  timeoutRef.current = setTimeout(flushOutputUpdates, debounceMs);
14522
14850
  }, [debounceMs, flushOutputUpdates]);
14523
- React14.useEffect(() => {
14851
+ React15.useEffect(() => {
14524
14852
  if (!pendingEditorRef.current || timeoutRef.current === null) return;
14525
14853
  clearTimeout(timeoutRef.current);
14526
14854
  timeoutRef.current = debounceMs === 0 ? null : setTimeout(flushOutputUpdates, debounceMs);
14527
14855
  if (debounceMs === 0) flushOutputUpdates();
14528
14856
  }, [debounceMs, flushOutputUpdates]);
14529
- React14.useEffect(() => () => {
14857
+ React15.useEffect(() => () => {
14858
+ flushOutputUpdates();
14530
14859
  if (timeoutRef.current !== null) {
14531
14860
  clearTimeout(timeoutRef.current);
14532
14861
  timeoutRef.current = null;
14533
14862
  }
14534
14863
  pendingEditorRef.current = null;
14535
- }, []);
14864
+ }, [flushOutputUpdates]);
14536
14865
  return {
14537
14866
  flushOutputUpdates,
14538
14867
  scheduleOutputUpdate
14539
14868
  };
14540
14869
  }
14541
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
+
14542
14936
  // src/components/UEditor/UEditor.tsx
14543
14937
  import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
14544
- var LazyMenuBar = React15.lazy(async () => {
14545
- const { MenuBar } = await import("./menu-bar-CFIWMBAA.js");
14938
+ var LazyMenuBar = React17.lazy(async () => {
14939
+ const { MenuBar } = await import("./menu-bar-DPCNNUX5.js");
14546
14940
  return { default: MenuBar };
14547
14941
  });
14548
- var UEditor = React15.forwardRef(({
14942
+ var UEditor = React17.forwardRef(({
14549
14943
  getPortalContainer,
14550
14944
  content = "",
14551
14945
  onChange,
@@ -14559,7 +14953,11 @@ var UEditor = React15.forwardRef(({
14559
14953
  maxImageFileSize,
14560
14954
  allowedImageMimeTypes,
14561
14955
  fallbackToDataUrl,
14956
+ onImageUploadError,
14562
14957
  placeholder,
14958
+ ariaLabel,
14959
+ ariaLabelledBy,
14960
+ ariaDescribedBy,
14563
14961
  className,
14564
14962
  editable = true,
14565
14963
  autofocus = false,
@@ -14571,6 +14969,7 @@ var UEditor = React15.forwardRef(({
14571
14969
  maxCharacters,
14572
14970
  minHeight = "200px",
14573
14971
  maxHeight = "auto",
14972
+ expandWideTables = true,
14574
14973
  variant = "default",
14575
14974
  rounded = true,
14576
14975
  fontFamilies,
@@ -14598,8 +14997,10 @@ var UEditor = React15.forwardRef(({
14598
14997
  const editorInstanceRef = useRef10(null);
14599
14998
  const formulaRangePickRef = useRef10(null);
14600
14999
  const formulaRangeSurfaceRef = useRef10(null);
14601
- const [formulaRangeHighlight, setFormulaRangeHighlight] = React15.useState(null);
14602
- 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);
14603
15004
  useEffect10(() => {
14604
15005
  setIsMenuBarVisible(showMenuBar);
14605
15006
  }, [showMenuBar]);
@@ -14609,7 +15010,7 @@ var UEditor = React15.forwardRef(({
14609
15010
  onJsonChange,
14610
15011
  outputDebounceMs
14611
15012
  });
14612
- const scheduleFormulaRecalculate = React15.useCallback((editor2, options) => {
15013
+ const scheduleFormulaRecalculate = React17.useCallback((editor2, options) => {
14613
15014
  if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
14614
15015
  if (!options?.force && isEditingTableFormulaText(editor2)) return;
14615
15016
  scheduledFormulaRecalculateRef.current = true;
@@ -14643,14 +15044,15 @@ var UEditor = React15.forwardRef(({
14643
15044
  maxImageFileSize,
14644
15045
  allowedImageMimeTypes,
14645
15046
  fallbackToDataUrl,
15047
+ onImageUploadError,
14646
15048
  editable,
14647
15049
  fetchMetadata
14648
15050
  }),
14649
15051
  ...extraExtensions ?? []
14650
15052
  ],
14651
- [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]
14652
15054
  );
14653
- const syncFormulaRangeHighlight = React15.useCallback((pickState) => {
15055
+ const syncFormulaRangeHighlight = React17.useCallback((pickState) => {
14654
15056
  const container = formulaRangeSurfaceRef.current;
14655
15057
  setFormulaRangeHighlight(container && pickState ? getFormulaRangePickHighlight(container, pickState) : null);
14656
15058
  }, []);
@@ -14722,9 +15124,6 @@ var UEditor = React15.forwardRef(({
14722
15124
  clearSelectedTableCellFormula(activeEditor);
14723
15125
  return true;
14724
15126
  }
14725
- if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
14726
- event.stopPropagation();
14727
- }
14728
15127
  return false;
14729
15128
  },
14730
15129
  click: (view, event) => {
@@ -14744,7 +15143,11 @@ var UEditor = React15.forwardRef(({
14744
15143
  }
14745
15144
  },
14746
15145
  attributes: {
14747
- 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
14748
15151
  }
14749
15152
  },
14750
15153
  onUpdate: ({ editor: editor2, transaction, appendedTransactions }) => {
@@ -14790,23 +15193,28 @@ var UEditor = React15.forwardRef(({
14790
15193
  apply: t("tableMenu.apply"),
14791
15194
  cancel: t("imageInput.cancelBtn")
14792
15195
  });
15196
+ useWideTableLayout(editor, expandWideTables, widthBoundaryRef, editorShellRef, editorContentRef);
14793
15197
  useImperativeHandle4(
14794
15198
  ref,
14795
15199
  () => ({
14796
15200
  editor,
14797
15201
  prepareContentForSave: async ({ throwOnError = false } = {}) => {
14798
- if (!inFlightPrepareRef.current) {
14799
- const htmlSnapshot = editor?.getHTML() ?? content ?? "";
14800
- inFlightPrepareRef.current = prepareUEditorContentForSave({
15202
+ const htmlSnapshot = editor?.getHTML() ?? content ?? "";
15203
+ if (!inFlightPrepareRef.current || inFlightPrepareRef.current.html !== htmlSnapshot) {
15204
+ let trackedPromise;
15205
+ trackedPromise = prepareUEditorContentForSave({
14801
15206
  html: htmlSnapshot,
14802
15207
  uploadImageForSave,
14803
15208
  uploadFileForSave,
14804
15209
  uploadConcurrency: uploadImageConcurrency
14805
15210
  }).finally(() => {
14806
- inFlightPrepareRef.current = null;
15211
+ if (inFlightPrepareRef.current?.promise === trackedPromise) {
15212
+ inFlightPrepareRef.current = null;
15213
+ }
14807
15214
  });
15215
+ inFlightPrepareRef.current = { html: htmlSnapshot, promise: trackedPromise };
14808
15216
  }
14809
- const result = await inFlightPrepareRef.current;
15217
+ const result = await inFlightPrepareRef.current.promise;
14810
15218
  if (throwOnError && result.errors.length > 0) {
14811
15219
  throw new UEditorPrepareContentForSaveError(result);
14812
15220
  }
@@ -14845,15 +15253,18 @@ var UEditor = React15.forwardRef(({
14845
15253
  return /* @__PURE__ */ jsx20(UEditorPortalProvider, { getPortalContainer, children: /* @__PURE__ */ jsx20(
14846
15254
  "div",
14847
15255
  {
15256
+ role: "status",
15257
+ "aria-live": "polite",
14848
15258
  className: cn("w-full border bg-background flex items-center justify-center text-muted-foreground", className),
14849
15259
  style: { minHeight },
14850
15260
  children: t("loading")
14851
15261
  }
14852
15262
  ) });
14853
15263
  }
14854
- 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(
14855
15265
  "div",
14856
15266
  {
15267
+ ref: editorShellRef,
14857
15268
  className: cn(
14858
15269
  "group relative flex flex-col text-card-foreground",
14859
15270
  editable ? [
@@ -14868,7 +15279,7 @@ var UEditor = React15.forwardRef(({
14868
15279
  ),
14869
15280
  children: [
14870
15281
  editable && (showMenuBar || showToolbar || showBubbleMenu) ? /* @__PURE__ */ jsxs17(EditorUiRenderStateProvider, { editor, children: [
14871
- isMenuBarVisible && /* @__PURE__ */ jsx20(React15.Suspense, { fallback: null, children: /* @__PURE__ */ jsx20(
15282
+ isMenuBarVisible && /* @__PURE__ */ jsx20(React17.Suspense, { fallback: null, children: /* @__PURE__ */ jsx20(
14872
15283
  LazyMenuBar,
14873
15284
  {
14874
15285
  editor,
@@ -14877,6 +15288,8 @@ var UEditor = React15.forwardRef(({
14877
15288
  imageInsertMode,
14878
15289
  maxImageFileSize,
14879
15290
  allowedImageMimeTypes,
15291
+ onImageUploadError,
15292
+ fallbackToDataUrl,
14880
15293
  onSave,
14881
15294
  onExport,
14882
15295
  onSourceCode,
@@ -14895,6 +15308,8 @@ var UEditor = React15.forwardRef(({
14895
15308
  imageInsertMode,
14896
15309
  maxImageFileSize,
14897
15310
  allowedImageMimeTypes,
15311
+ onImageUploadError,
15312
+ fallbackToDataUrl,
14898
15313
  fontFamilies,
14899
15314
  defaultFontFamily,
14900
15315
  fontSizes,
@@ -14920,7 +15335,10 @@ var UEditor = React15.forwardRef(({
14920
15335
  editorContentRef.current = node;
14921
15336
  formulaRangeSurfaceRef.current = node;
14922
15337
  },
14923
- 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
+ ),
14924
15342
  style: {
14925
15343
  fontFamily: defaultFontFamily,
14926
15344
  minHeight: editable ? minHeight : void 0,
@@ -14999,7 +15417,7 @@ var UEditor = React15.forwardRef(({
14999
15417
  showFooter && showCharacterCount && /* @__PURE__ */ jsx20(CharacterCountDisplay, { editor, maxCharacters })
15000
15418
  ]
15001
15419
  }
15002
- ) });
15420
+ ) }) });
15003
15421
  });
15004
15422
  UEditor.displayName = "UEditor";
15005
15423
  var UEditor_default = UEditor;
@@ -15011,4 +15429,4 @@ export {
15011
15429
  prepareUEditorContentForSave,
15012
15430
  UEditor_default
15013
15431
  };
15014
- //# sourceMappingURL=chunk-QYFXYDBK.js.map
15432
+ //# sourceMappingURL=chunk-57BKIC6J.js.map