@underverse-ui/underverse 1.0.155 → 1.0.157

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1055,12 +1055,15 @@ var en_default = {
1055
1055
  clickToOpenNewTab: "Click to open this link in a new tab.",
1056
1056
  clickToOpenLink: "Click to open link.",
1057
1057
  noDescription: "No description available.",
1058
- failedToLoad: "Failed to load link preview. Click to open link."
1058
+ failedToLoad: "Failed to load link preview. Click to open link.",
1059
+ previewFailed: "Preview failed to load",
1060
+ retryPreview: "Retry preview"
1059
1061
  },
1060
1062
  fileCard: {
1061
1063
  unnamed: "Unnamed file",
1062
1064
  uploading: "(Uploading...)",
1063
- uploadFailed: "Upload failed"
1065
+ uploadFailed: "Upload failed",
1066
+ retryUpload: "Retry upload"
1064
1067
  },
1065
1068
  menubar: {
1066
1069
  file: "File",
@@ -1447,12 +1450,15 @@ var vi_default = {
1447
1450
  clickToOpenNewTab: "B\u1EA5m \u0111\u1EC3 m\u1EDF li\xEAn k\u1EBFt n\xE0y trong tab m\u1EDBi.",
1448
1451
  clickToOpenLink: "B\u1EA5m \u0111\u1EC3 m\u1EDF li\xEAn k\u1EBFt.",
1449
1452
  noDescription: "Kh\xF4ng c\xF3 m\xF4 t\u1EA3.",
1450
- failedToLoad: "T\u1EA3i b\u1EA3n xem tr\u01B0\u1EDBc th\u1EA5t b\u1EA1i. B\u1EA5m \u0111\u1EC3 m\u1EDF li\xEAn k\u1EBFt."
1453
+ failedToLoad: "T\u1EA3i b\u1EA3n xem tr\u01B0\u1EDBc th\u1EA5t b\u1EA1i. B\u1EA5m \u0111\u1EC3 m\u1EDF li\xEAn k\u1EBFt.",
1454
+ previewFailed: "Kh\xF4ng t\u1EA3i \u0111\u01B0\u1EE3c b\u1EA3n xem tr\u01B0\u1EDBc",
1455
+ retryPreview: "Th\u1EED l\u1EA1i"
1451
1456
  },
1452
1457
  fileCard: {
1453
1458
  unnamed: "T\u1EC7p kh\xF4ng t\xEAn",
1454
1459
  uploading: "(\u0110ang t\u1EA3i l\xEAn...)",
1455
- uploadFailed: "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"
1460
+ uploadFailed: "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i",
1461
+ retryUpload: "T\u1EA3i l\u1EA1i"
1456
1462
  },
1457
1463
  menubar: {
1458
1464
  file: "T\u1EADp tin",
@@ -1839,12 +1845,15 @@ var ko_default = {
1839
1845
  clickToOpenNewTab: "\uD074\uB9AD\uD558\uC5EC \uC774 \uB9C1\uD06C\uB97C \uC0C8 \uD0ED\uC5D0\uC11C \uC5FD\uB2C8\uB2E4.",
1840
1846
  clickToOpenLink: "\uD074\uB9AD\uD558\uC5EC \uB9C1\uD06C\uB97C \uC5FD\uB2C8\uB2E4.",
1841
1847
  noDescription: "\uC124\uBA85\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
1842
- failedToLoad: "\uB9C1\uD06C \uBBF8\uB9AC\uBCF4\uAE30\uB97C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uD074\uB9AD\uD558\uC5EC \uB9C1\uD06C\uB97C \uC5FD\uB2C8\uB2E4."
1848
+ failedToLoad: "\uB9C1\uD06C \uBBF8\uB9AC\uBCF4\uAE30\uB97C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uD074\uB9AD\uD558\uC5EC \uB9C1\uD06C\uB97C \uC5FD\uB2C8\uB2E4.",
1849
+ previewFailed: "\uBBF8\uB9AC\uBCF4\uAE30\uB97C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4",
1850
+ retryPreview: "\uB2E4\uC2DC \uC2DC\uB3C4"
1843
1851
  },
1844
1852
  fileCard: {
1845
1853
  unnamed: "\uC774\uB984 \uC5C6\uB294 \uD30C\uC77C",
1846
1854
  uploading: "(\uC5C5\uB85C\uB4DC \uC911...)",
1847
- uploadFailed: "\uC5C5\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."
1855
+ uploadFailed: "\uC5C5\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.",
1856
+ retryUpload: "\uB2E4\uC2DC \uC5C5\uB85C\uB4DC"
1848
1857
  },
1849
1858
  menubar: {
1850
1859
  file: "\uD30C\uC77C",
@@ -2231,12 +2240,15 @@ var ja_default = {
2231
2240
  clickToOpenNewTab: "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u3053\u306E\u30EA\u30F3\u30AF\u3092\u65B0\u3057\u3044\u30BF\u30D6\u3067\u958B\u304F\u3002",
2232
2241
  clickToOpenLink: "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u30EA\u30F3\u30AF\u3092\u958B\u304F\u3002",
2233
2242
  noDescription: "\u8AAC\u660E\u306F\u3042\u308A\u307E\u305B\u3093\u3002",
2234
- failedToLoad: "\u30D7\u30EC\u30D3\u30E5\u30FC\u306E\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u30EA\u30F3\u30AF\u3092\u958B\u304F\u3002"
2243
+ failedToLoad: "\u30D7\u30EC\u30D3\u30E5\u30FC\u306E\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u30EA\u30F3\u30AF\u3092\u958B\u304F\u3002",
2244
+ previewFailed: "\u30D7\u30EC\u30D3\u30E5\u30FC\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F",
2245
+ retryPreview: "\u518D\u8A66\u884C"
2235
2246
  },
2236
2247
  fileCard: {
2237
2248
  unnamed: "\u540D\u524D\u306A\u3057\u30D5\u30A1\u30A4\u30EB",
2238
2249
  uploading: "(\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u4E2D...)",
2239
- uploadFailed: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u5931\u6557"
2250
+ uploadFailed: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u5931\u6557",
2251
+ retryUpload: "\u518D\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9"
2240
2252
  },
2241
2253
  menubar: {
2242
2254
  file: "\u30D5\u30A1\u30A4\u30EB",
@@ -25025,7 +25037,7 @@ var import_core2 = require("@tiptap/core");
25025
25037
  var import_react45 = require("@tiptap/react");
25026
25038
 
25027
25039
  // src/components/UEditor/BookmarkView.tsx
25028
- var import_react43 = require("react");
25040
+ var import_react43 = __toESM(require("react"), 1);
25029
25041
  var import_react44 = require("@tiptap/react");
25030
25042
  var import_lucide_react41 = require("lucide-react");
25031
25043
  var import_jsx_runtime77 = require("react/jsx-runtime");
@@ -25034,12 +25046,18 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
25034
25046
  const { url, title, description, image, publisher } = node.attrs;
25035
25047
  const [loading2, setLoading] = (0, import_react43.useState)(false);
25036
25048
  const [error, setError] = (0, import_react43.useState)(false);
25049
+ const [retryToken, setRetryToken] = (0, import_react43.useState)(0);
25050
+ const fetchedRetryTokenRef = import_react43.default.useRef(-1);
25051
+ const fetchRequestIdRef = import_react43.default.useRef(0);
25037
25052
  (0, import_react43.useEffect)(() => {
25038
- if (title) return;
25039
- let active = true;
25053
+ if (title && retryToken === 0) return;
25054
+ if (fetchedRetryTokenRef.current === retryToken) return;
25055
+ fetchedRetryTokenRef.current = retryToken;
25056
+ const requestId = fetchRequestIdRef.current + 1;
25057
+ fetchRequestIdRef.current = requestId;
25040
25058
  const safeUpdateAttributes = (attrs) => {
25041
25059
  Promise.resolve().then(() => {
25042
- if (active && editor && !editor.isDestroyed) {
25060
+ if (fetchRequestIdRef.current === requestId && editor && !editor.isDestroyed) {
25043
25061
  updateAttributes(attrs);
25044
25062
  }
25045
25063
  });
@@ -25066,7 +25084,7 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
25066
25084
  setLoading(true);
25067
25085
  setError(false);
25068
25086
  fetchMetadata(url).then((meta) => {
25069
- if (!active) return;
25087
+ if (fetchRequestIdRef.current !== requestId) return;
25070
25088
  safeUpdateAttributes({
25071
25089
  title: meta.title || new URL(url).hostname || url,
25072
25090
  description: meta.description || t("bookmark.noDescription"),
@@ -25075,33 +25093,22 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
25075
25093
  });
25076
25094
  setLoading(false);
25077
25095
  }).catch(() => {
25078
- if (!active) return;
25096
+ if (fetchRequestIdRef.current !== requestId) return;
25079
25097
  setError(true);
25080
25098
  setLoading(false);
25081
- try {
25082
- const hostname = new URL(url).hostname;
25083
- safeUpdateAttributes({
25084
- title: hostname || url,
25085
- description: t("bookmark.failedToLoad"),
25086
- publisher: hostname
25087
- });
25088
- } catch {
25089
- safeUpdateAttributes({
25090
- title: url,
25091
- description: t("bookmark.clickToOpenLink"),
25092
- publisher: ""
25093
- });
25094
- }
25095
25099
  });
25096
- return () => {
25097
- active = false;
25098
- };
25099
- }, [url, title, editor, updateAttributes, t]);
25100
+ return void 0;
25101
+ }, [url, title, editor, updateAttributes, t, retryToken]);
25100
25102
  const handleOpenLink = (e) => {
25101
25103
  e.preventDefault();
25102
25104
  e.stopPropagation();
25103
25105
  window.open(url, "_blank", "noopener,noreferrer");
25104
25106
  };
25107
+ const handleRetryPreview = (event) => {
25108
+ event.preventDefault();
25109
+ event.stopPropagation();
25110
+ setRetryToken((value) => value + 1);
25111
+ };
25105
25112
  const displayPublisher = publisher || (() => {
25106
25113
  try {
25107
25114
  return new URL(url).hostname;
@@ -25137,6 +25144,24 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
25137
25144
  /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("span", { className: "text-[11px] text-primary/80 font-mono mt-2 truncate flex items-center gap-1", children: [
25138
25145
  url,
25139
25146
  /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(import_lucide_react41.ExternalLink, { className: "h-3 w-3 flex-shrink-0" })
25147
+ ] }),
25148
+ error && /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "mt-2 flex flex-wrap items-center gap-2 text-xs text-destructive", children: [
25149
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
25150
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(import_lucide_react41.AlertCircle, { className: "h-3.5 w-3.5" }),
25151
+ t("bookmark.previewFailed")
25152
+ ] }),
25153
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)(
25154
+ "button",
25155
+ {
25156
+ type: "button",
25157
+ onClick: handleRetryPreview,
25158
+ className: "inline-flex items-center gap-1 rounded-md border border-destructive/30 px-2 py-0.5 font-medium hover:bg-destructive/10",
25159
+ children: [
25160
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(import_lucide_react41.RefreshCw, { className: "h-3 w-3" }),
25161
+ t("bookmark.retryPreview")
25162
+ ]
25163
+ }
25164
+ )
25140
25165
  ] })
25141
25166
  ] }),
25142
25167
  image && /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "relative w-full sm:w-44 h-32 sm:h-auto flex-shrink-0 bg-muted overflow-hidden border-t sm:border-t-0 sm:border-l border-border/50", children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
@@ -25321,6 +25346,7 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
25321
25346
  const { src, fileName, fileSize, fileType } = node.attrs;
25322
25347
  const FileIcon = getFileIcon2(fileType);
25323
25348
  const [uploadError, setUploadError] = (0, import_react46.useState)(null);
25349
+ const [retryToken, setRetryToken] = (0, import_react46.useState)(0);
25324
25350
  const uploadAttemptedRef = (0, import_react46.useRef)(false);
25325
25351
  (0, import_react46.useEffect)(() => {
25326
25352
  if (!src || !src.startsWith("data:")) {
@@ -25352,7 +25378,7 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
25352
25378
  }
25353
25379
  };
25354
25380
  void runUpload();
25355
- }, [src, fileName, fileType, editor, updateAttributes]);
25381
+ }, [src, fileName, fileType, editor, updateAttributes, retryToken]);
25356
25382
  const handleDownload = (e) => {
25357
25383
  e.preventDefault();
25358
25384
  e.stopPropagation();
@@ -25365,6 +25391,13 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
25365
25391
  document.body.removeChild(link);
25366
25392
  };
25367
25393
  const isUploading = (src || "").startsWith("data:");
25394
+ const handleRetryUpload = (event) => {
25395
+ event.preventDefault();
25396
+ event.stopPropagation();
25397
+ uploadAttemptedRef.current = false;
25398
+ setUploadError(null);
25399
+ setRetryToken((value) => value + 1);
25400
+ };
25368
25401
  return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(
25369
25402
  import_react47.NodeViewWrapper,
25370
25403
  {
@@ -25383,7 +25416,19 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
25383
25416
  isUploading && /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: cn(
25384
25417
  "font-medium truncate max-w-[200px]",
25385
25418
  uploadError ? "text-destructive" : "text-primary animate-pulse"
25386
- ), children: uploadError ? `${t("fileCard.uploadFailed") || "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"}: ${uploadError}` : t("fileCard.uploading") })
25419
+ ), children: uploadError ? `${t("fileCard.uploadFailed") || "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"}: ${uploadError}` : t("fileCard.uploading") }),
25420
+ isUploading && uploadError && /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(
25421
+ "button",
25422
+ {
25423
+ type: "button",
25424
+ onClick: handleRetryUpload,
25425
+ 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",
25426
+ children: [
25427
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react42.RefreshCw, { className: "h-3 w-3" }),
25428
+ t("fileCard.retryUpload")
25429
+ ]
25430
+ }
25431
+ )
25387
25432
  ] })
25388
25433
  ] })
25389
25434
  ] }),
@@ -28933,6 +28978,20 @@ function createEmptyCellNode(cellNode) {
28933
28978
  function createCellCopyForColumnDuplicate(cellNode) {
28934
28979
  return cellNode.type.create(cellNode.attrs, cellNode.content);
28935
28980
  }
28981
+ function createCellWithDuplicatedLogicalColumn(cellNode, widthIndex) {
28982
+ const colspan = Math.max(1, Number(cellNode.attrs.colspan) || 1);
28983
+ let nextColwidth = null;
28984
+ if (Array.isArray(cellNode.attrs.colwidth)) {
28985
+ nextColwidth = [...cellNode.attrs.colwidth];
28986
+ const duplicateWidth = nextColwidth[widthIndex];
28987
+ nextColwidth.splice(widthIndex + 1, 0, typeof duplicateWidth === "number" ? duplicateWidth : 0);
28988
+ }
28989
+ return cellNode.type.create({
28990
+ ...cellNode.attrs,
28991
+ colspan: colspan + 1,
28992
+ ...nextColwidth ? { colwidth: nextColwidth } : null
28993
+ }, cellNode.content);
28994
+ }
28936
28995
  function getTableRows(tableNode) {
28937
28996
  const rows = [];
28938
28997
  tableNode.forEach((rowNode, rowOffset) => {
@@ -29108,6 +29167,11 @@ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
29108
29167
  return rect && rect.top === rowIndex && rect.left <= columnIndex && columnIndex < rect.right;
29109
29168
  });
29110
29169
  if (!sourceCell) return rowInfo.node;
29170
+ const sourceRect = safeFindCell(map, sourceCell.relativePos);
29171
+ if (sourceRect && (sourceRect.left < columnIndex || sourceRect.right > columnIndex + 1)) {
29172
+ cells[sourceCell.index] = createCellWithDuplicatedLogicalColumn(sourceCell.node, columnIndex - sourceRect.left);
29173
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
29174
+ }
29111
29175
  cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
29112
29176
  return rowInfo.node.type.create(rowInfo.node.attrs, cells);
29113
29177
  });
@@ -30407,6 +30471,25 @@ function getTableFormulaRecalculationOrder(graph) {
30407
30471
  }
30408
30472
  return { order, circular };
30409
30473
  }
30474
+ function getAffectedTableFormulaLabels(graph, changedLabels) {
30475
+ const affected = /* @__PURE__ */ new Set();
30476
+ const queue = Array.from(changedLabels, (label) => label.toUpperCase());
30477
+ for (const label of queue) {
30478
+ if (graph.formulas.has(label)) {
30479
+ affected.add(label);
30480
+ }
30481
+ }
30482
+ for (let index = 0; index < queue.length; index += 1) {
30483
+ const label = queue[index];
30484
+ if (!label) continue;
30485
+ for (const dependent of graph.dependents.get(label) ?? []) {
30486
+ if (affected.has(dependent)) continue;
30487
+ affected.add(dependent);
30488
+ queue.push(dependent);
30489
+ }
30490
+ }
30491
+ return affected;
30492
+ }
30410
30493
  function evaluateBasicTableFormula(formula, getCellValue) {
30411
30494
  const normalized = normalizeTableFormula(formula);
30412
30495
  if (!normalized) {
@@ -30554,10 +30637,19 @@ var FormulaParser = class {
30554
30637
  const range = parseTableCellRange(token.value);
30555
30638
  if (!range) return { value: null, error: "invalid-reference" };
30556
30639
  for (const label of getTableCellRangeLabels(range)) {
30640
+ if (name === "COUNT") {
30641
+ const cellValue2 = this.readOptionalCellNumber(label);
30642
+ if (cellValue2 != null) values.push(cellValue2);
30643
+ continue;
30644
+ }
30557
30645
  const cellValue = this.readCellNumber(label);
30558
30646
  if (cellValue.error) return cellValue;
30559
30647
  values.push(cellValue.value);
30560
30648
  }
30649
+ } else if (name === "COUNT" && token.type === "cell") {
30650
+ this.index += 1;
30651
+ const cellValue = this.readOptionalCellNumber(token.value);
30652
+ if (cellValue != null) values.push(cellValue);
30561
30653
  } else {
30562
30654
  const value = this.parseExpression();
30563
30655
  if (value.error) return value;
@@ -30571,7 +30663,7 @@ var FormulaParser = class {
30571
30663
  }
30572
30664
  return { value: null, error: "invalid-formula" };
30573
30665
  }
30574
- if (values.length === 0) {
30666
+ if (values.length === 0 && name !== "COUNT") {
30575
30667
  return { value: null, error: "invalid-formula" };
30576
30668
  }
30577
30669
  if (name === "SUM") return { value: values.reduce((sum, value) => sum + value, 0), error: null };
@@ -30589,6 +30681,11 @@ var FormulaParser = class {
30589
30681
  }
30590
30682
  return { value: parsed, error: null };
30591
30683
  }
30684
+ readOptionalCellNumber(label) {
30685
+ const value = this.getCellValue(label);
30686
+ const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
30687
+ return Number.isFinite(parsed) ? parsed : null;
30688
+ }
30592
30689
  peekOperator(operators) {
30593
30690
  const token = this.tokens[this.index];
30594
30691
  return token?.type === "operator" && operators.includes(token.value) ? token : null;
@@ -30652,6 +30749,41 @@ function getSelectionTableCellNode(editor) {
30652
30749
  }
30653
30750
  return null;
30654
30751
  }
30752
+ function getSelectionTableInfo(editor) {
30753
+ const { $from } = editor.state.selection;
30754
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
30755
+ const node = $from.node(depth);
30756
+ if (node.type.name === "table") {
30757
+ return {
30758
+ node,
30759
+ pos: $from.before(depth)
30760
+ };
30761
+ }
30762
+ }
30763
+ return null;
30764
+ }
30765
+ function getSelectionTableCellLabel(editor) {
30766
+ const { $from } = editor.state.selection;
30767
+ let cellDepth = -1;
30768
+ let tableDepth = -1;
30769
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
30770
+ const node = $from.node(depth);
30771
+ if (cellDepth < 0 && (node.type.name === "tableCell" || node.type.name === "tableHeader")) {
30772
+ cellDepth = depth;
30773
+ }
30774
+ if (node.type.name === "table") {
30775
+ tableDepth = depth;
30776
+ break;
30777
+ }
30778
+ }
30779
+ if (cellDepth < 0 || tableDepth < 0) return null;
30780
+ const tableNode = $from.node(tableDepth);
30781
+ const tableStart = $from.start(tableDepth);
30782
+ const relativeCellPos = $from.before(cellDepth) - tableStart;
30783
+ const rect = safeFindCell2(import_tables2.TableMap.get(tableNode), relativeCellPos);
30784
+ if (!rect) return null;
30785
+ return `${indexToColumnName(rect.left)}${rect.top + 1}`;
30786
+ }
30655
30787
  function isEditingTableFormulaText(editor) {
30656
30788
  const cellNode = getSelectionTableCellNode(editor);
30657
30789
  return Boolean(cellNode && getCellText(cellNode).startsWith("="));
@@ -30663,20 +30795,6 @@ function createCellDisplayContent(cellNode, displayValue) {
30663
30795
  }
30664
30796
  return [paragraphType.create(null, cellNode.type.schema.text(displayValue))];
30665
30797
  }
30666
- function buildTableValueGetter(tableNode) {
30667
- const map = import_tables2.TableMap.get(tableNode);
30668
- const values = /* @__PURE__ */ new Map();
30669
- for (const rowInfo of getTableRows2(tableNode)) {
30670
- for (const entry of rowInfo.cells) {
30671
- const rect = safeFindCell2(map, entry.relativePos);
30672
- if (!rect) continue;
30673
- const label = `${indexToColumnName(rect.left)}${rect.top + 1}`;
30674
- const computedValue = entry.node.attrs.computedValue;
30675
- values.set(label, typeof computedValue === "string" && computedValue.trim() ? computedValue : getCellText(entry.node));
30676
- }
30677
- }
30678
- return (label) => values.get(label.toUpperCase());
30679
- }
30680
30798
  function buildTableValueMap(tableNode) {
30681
30799
  const map = import_tables2.TableMap.get(tableNode);
30682
30800
  const values = /* @__PURE__ */ new Map();
@@ -30691,10 +30809,6 @@ function buildTableValueMap(tableNode) {
30691
30809
  }
30692
30810
  return values;
30693
30811
  }
30694
- function getFormulaComputedValue(formula, tableNode) {
30695
- const result = evaluateBasicTableFormula(formula, buildTableValueGetter(tableNode));
30696
- return result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30697
- }
30698
30812
  function normalizeFormulaInput(formula) {
30699
30813
  const trimmed = formula.trim();
30700
30814
  if (!trimmed) return "";
@@ -30705,19 +30819,19 @@ function setSelectedTableCellFormula(editor, formula) {
30705
30819
  const { state, view } = editor;
30706
30820
  if (!normalized) {
30707
30821
  const clearedFormula = (0, import_tables2.setCellAttr)("formula", null)(state, view.dispatch.bind(view));
30708
- const clearedValue = (0, import_tables2.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
30709
- if (clearedFormula || clearedValue) {
30822
+ const clearedValue2 = (0, import_tables2.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
30823
+ if (clearedFormula || clearedValue2) {
30824
+ recalculateActiveTableFormulas(editor);
30710
30825
  view.focus();
30711
30826
  dispatchTableLayoutChange(editor);
30712
30827
  return true;
30713
30828
  }
30714
30829
  return false;
30715
30830
  }
30716
- const rect = (0, import_tables2.selectedRect)(state);
30717
- const computedValue = getFormulaComputedValue(normalized, rect.table);
30718
30831
  const appliedFormula = (0, import_tables2.setCellAttr)("formula", normalized)(state, view.dispatch.bind(view));
30719
- const appliedValue = (0, import_tables2.setCellAttr)("computedValue", computedValue)(editor.state, view.dispatch.bind(view));
30720
- if (appliedFormula || appliedValue) {
30832
+ const clearedValue = (0, import_tables2.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
30833
+ if (appliedFormula || clearedValue) {
30834
+ recalculateActiveTableFormulas(editor);
30721
30835
  view.focus();
30722
30836
  dispatchTableLayoutChange(editor);
30723
30837
  return true;
@@ -30799,7 +30913,7 @@ function promoteFormulaTextInTableNode(tableNode) {
30799
30913
  changed
30800
30914
  };
30801
30915
  }
30802
- function recalculateTableNode(tableNode) {
30916
+ function recalculateTableNode(tableNode, options) {
30803
30917
  const promoted = promoteFormulaTextInTableNode(tableNode);
30804
30918
  tableNode = promoted.tableNode;
30805
30919
  const map = import_tables2.TableMap.get(tableNode);
@@ -30829,13 +30943,16 @@ function recalculateTableNode(tableNode) {
30829
30943
  }))
30830
30944
  );
30831
30945
  const { order, circular } = getTableFormulaRecalculationOrder(graph);
30946
+ const affectedLabels = options?.changedLabels ? getAffectedTableFormulaLabels(graph, options.changedLabels) : null;
30832
30947
  const computedValues = /* @__PURE__ */ new Map();
30833
30948
  const getCellValue = (label) => values.get(label.toUpperCase());
30834
30949
  for (const label of circular) {
30950
+ if (affectedLabels && !affectedLabels.has(label)) continue;
30835
30951
  computedValues.set(label, formatFormulaError("circular-reference"));
30836
30952
  values.set(label, formatFormulaError("circular-reference"));
30837
30953
  }
30838
30954
  for (const label of order) {
30955
+ if (affectedLabels && !affectedLabels.has(label)) continue;
30839
30956
  const entry = formulaEntries.get(label);
30840
30957
  if (!entry) continue;
30841
30958
  const result = evaluateBasicTableFormula(entry.formula, getCellValue);
@@ -30880,6 +30997,22 @@ function recalculateSelectedTable(editor) {
30880
30997
  dispatchTableLayoutChange(editor);
30881
30998
  return true;
30882
30999
  }
31000
+ function recalculateActiveTableFormulas(editor) {
31001
+ const tableInfo = getSelectionTableInfo(editor);
31002
+ if (!tableInfo) {
31003
+ return recalculateAllTableFormulas(editor);
31004
+ }
31005
+ const activeCellLabel = getSelectionTableCellLabel(editor);
31006
+ const nextTable = recalculateTableNode(tableInfo.node, {
31007
+ changedLabels: activeCellLabel ? [activeCellLabel] : null
31008
+ });
31009
+ if (!nextTable) return false;
31010
+ editor.view.dispatch(
31011
+ editor.state.tr.replaceWith(tableInfo.pos, tableInfo.pos + tableInfo.node.nodeSize, nextTable).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true)
31012
+ );
31013
+ dispatchTableLayoutChange(editor);
31014
+ return true;
31015
+ }
30883
31016
  function recalculateAllTableFormulas(editor) {
30884
31017
  const replacements = [];
30885
31018
  editor.state.doc.descendants((node, pos) => {
@@ -38876,6 +39009,146 @@ var MenuBar = ({
38876
39009
  ] });
38877
39010
  };
38878
39011
 
39012
+ // src/components/UEditor/table-formula-range-picker.ts
39013
+ var import_state9 = require("@tiptap/pm/state");
39014
+ var import_tables4 = require("@tiptap/pm/tables");
39015
+ function getCellText2(cellNode) {
39016
+ return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
39017
+ }
39018
+ function findSelectionFormulaCell(view) {
39019
+ const { $from } = view.state.selection;
39020
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
39021
+ const node = $from.node(depth);
39022
+ if (node.type.name !== "tableCell" && node.type.name !== "tableHeader") continue;
39023
+ if (!getCellText2(node).startsWith("=")) return null;
39024
+ const cellPos = $from.before(depth);
39025
+ const cellDom = view.nodeDOM(cellPos);
39026
+ const tableDepth = depth - 2;
39027
+ const tableNode = tableDepth > 0 ? $from.node(tableDepth) : null;
39028
+ if (!tableNode || tableNode.type.name !== "table") return null;
39029
+ return {
39030
+ cellDom: cellDom instanceof HTMLTableCellElement ? cellDom : null,
39031
+ tablePos: $from.before(tableDepth)
39032
+ };
39033
+ }
39034
+ return null;
39035
+ }
39036
+ function findTableForPos(view, pos) {
39037
+ const $pos = view.state.doc.resolve(pos);
39038
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
39039
+ const node = $pos.node(depth);
39040
+ if (node.type.name === "table") {
39041
+ return {
39042
+ node,
39043
+ pos: $pos.before(depth),
39044
+ start: $pos.start(depth)
39045
+ };
39046
+ }
39047
+ }
39048
+ return null;
39049
+ }
39050
+ function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
39051
+ const relativeDomPos = domPos - tableStart;
39052
+ const seen = /* @__PURE__ */ new Set();
39053
+ for (const relativeCellPos of map.map) {
39054
+ if (seen.has(relativeCellPos)) continue;
39055
+ seen.add(relativeCellPos);
39056
+ if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
39057
+ return relativeCellPos;
39058
+ }
39059
+ }
39060
+ return null;
39061
+ }
39062
+ function getPickedCellLabel(view, target, tablePos) {
39063
+ const element = resolveEventElement(target);
39064
+ const cell = element?.closest?.("th,td");
39065
+ if (!(cell instanceof HTMLTableCellElement)) return null;
39066
+ const domPos = view.posAtDOM(cell, 0);
39067
+ const tableInfo = findTableForPos(view, domPos);
39068
+ if (!tableInfo || tableInfo.pos !== tablePos) return null;
39069
+ const map = import_tables4.TableMap.get(tableInfo.node);
39070
+ const relativeCellPos = getCellRelativePosFromDomPos2(map, tableInfo.start, domPos);
39071
+ if (relativeCellPos == null) return null;
39072
+ const rect = map.findCell(relativeCellPos);
39073
+ return {
39074
+ cell,
39075
+ label: `${indexToColumnName(rect.left)}${rect.top + 1}`
39076
+ };
39077
+ }
39078
+ function normalizeFormulaRangeLabel(fromLabel, toLabel) {
39079
+ return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
39080
+ }
39081
+ function replacePickedLabel(view, pickState, nextLabel, currentCell) {
39082
+ if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell) return pickState;
39083
+ if (nextLabel === pickState.currentLabel) {
39084
+ return {
39085
+ ...pickState,
39086
+ currentCell
39087
+ };
39088
+ }
39089
+ let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
39090
+ const nextTo = pickState.insertedFrom + nextLabel.length;
39091
+ tr = tr.setSelection(import_state9.TextSelection.create(tr.doc, nextTo));
39092
+ view.dispatch(tr);
39093
+ return {
39094
+ ...pickState,
39095
+ insertedTo: nextTo,
39096
+ currentLabel: nextLabel,
39097
+ currentCell
39098
+ };
39099
+ }
39100
+ function beginFormulaRangePick(view, event) {
39101
+ if (event.button !== 0) return null;
39102
+ const formulaCell = findSelectionFormulaCell(view);
39103
+ if (!formulaCell) return null;
39104
+ const picked = getPickedCellLabel(view, event.target, formulaCell.tablePos);
39105
+ if (!picked || picked.cell === formulaCell.cellDom) return null;
39106
+ const { from, to } = view.state.selection;
39107
+ let tr = view.state.tr.insertText(picked.label, from, to);
39108
+ tr = tr.setSelection(import_state9.TextSelection.create(tr.doc, from + picked.label.length));
39109
+ view.dispatch(tr);
39110
+ view.focus();
39111
+ event.preventDefault();
39112
+ event.stopPropagation();
39113
+ return {
39114
+ anchorLabel: picked.label,
39115
+ anchorCell: picked.cell,
39116
+ tablePos: formulaCell.tablePos,
39117
+ insertedFrom: from,
39118
+ insertedTo: from + picked.label.length,
39119
+ currentLabel: picked.label,
39120
+ currentCell: picked.cell
39121
+ };
39122
+ }
39123
+ function updateFormulaRangePick(view, pickState, event) {
39124
+ const picked = getPickedCellLabel(view, event.target, pickState.tablePos);
39125
+ if (!picked) return pickState;
39126
+ event.preventDefault();
39127
+ event.stopPropagation();
39128
+ return replacePickedLabel(
39129
+ view,
39130
+ pickState,
39131
+ normalizeFormulaRangeLabel(pickState.anchorLabel, picked.label),
39132
+ picked.cell
39133
+ );
39134
+ }
39135
+ function getFormulaRangePickHighlight(container, pickState) {
39136
+ if (!container.contains(pickState.anchorCell) || !container.contains(pickState.currentCell)) return null;
39137
+ const containerRect = container.getBoundingClientRect();
39138
+ const anchorRect = pickState.anchorCell.getBoundingClientRect();
39139
+ const currentRect = pickState.currentCell.getBoundingClientRect();
39140
+ const left = Math.min(anchorRect.left, currentRect.left) - containerRect.left + container.scrollLeft;
39141
+ const top = Math.min(anchorRect.top, currentRect.top) - containerRect.top + container.scrollTop;
39142
+ const right = Math.max(anchorRect.right, currentRect.right) - containerRect.left + container.scrollLeft;
39143
+ const bottom = Math.max(anchorRect.bottom, currentRect.bottom) - containerRect.top + container.scrollTop;
39144
+ return {
39145
+ left,
39146
+ top,
39147
+ width: Math.max(0, right - left),
39148
+ height: Math.max(0, bottom - top)
39149
+ };
39150
+ }
39151
+
38879
39152
  // src/components/UEditor/UEditor.tsx
38880
39153
  var import_jsx_runtime95 = require("react/jsx-runtime");
38881
39154
  var UEditor = import_react71.default.forwardRef(({
@@ -38908,7 +39181,6 @@ var UEditor = import_react71.default.forwardRef(({
38908
39181
  fetchMetadata,
38909
39182
  uploadFile,
38910
39183
  uploadFileForSave,
38911
- collaborationOptions,
38912
39184
  extraExtensions,
38913
39185
  showMenuBar = false,
38914
39186
  onSave,
@@ -38921,6 +39193,9 @@ var UEditor = import_react71.default.forwardRef(({
38921
39193
  const inFlightPrepareRef = (0, import_react71.useRef)(null);
38922
39194
  const lastAppliedContentRef = (0, import_react71.useRef)(content ?? "");
38923
39195
  const scheduledFormulaRecalculateRef = (0, import_react71.useRef)(false);
39196
+ const formulaRangePickRef = (0, import_react71.useRef)(null);
39197
+ const formulaRangeSurfaceRef = (0, import_react71.useRef)(null);
39198
+ const [formulaRangeHighlight, setFormulaRangeHighlight] = import_react71.default.useState(null);
38924
39199
  const scheduleFormulaRecalculate = import_react71.default.useCallback((editor2, options) => {
38925
39200
  if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
38926
39201
  if (!options?.force && isEditingTableFormulaText(editor2)) return;
@@ -38928,7 +39203,7 @@ var UEditor = import_react71.default.forwardRef(({
38928
39203
  queueMicrotask(() => {
38929
39204
  scheduledFormulaRecalculateRef.current = false;
38930
39205
  if (!editor2.isDestroyed && (options?.force || !isEditingTableFormulaText(editor2))) {
38931
- recalculateAllTableFormulas(editor2);
39206
+ recalculateActiveTableFormulas(editor2);
38932
39207
  }
38933
39208
  });
38934
39209
  }, []);
@@ -38961,6 +39236,10 @@ var UEditor = import_react71.default.forwardRef(({
38961
39236
  ],
38962
39237
  [effectivePlaceholder, t, maxCharacters, uploadImage, resolvedUploadFile, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable, fetchMetadata, extraExtensions]
38963
39238
  );
39239
+ const syncFormulaRangeHighlight = import_react71.default.useCallback((pickState) => {
39240
+ const container = formulaRangeSurfaceRef.current;
39241
+ setFormulaRangeHighlight(container && pickState ? getFormulaRangePickHighlight(container, pickState) : null);
39242
+ }, []);
38964
39243
  const editor = (0, import_react72.useEditor)({
38965
39244
  immediatelyRender: false,
38966
39245
  extensions,
@@ -38969,6 +39248,37 @@ var UEditor = import_react71.default.forwardRef(({
38969
39248
  autofocus,
38970
39249
  editorProps: {
38971
39250
  handleDOMEvents: {
39251
+ mousedown: (view, event) => {
39252
+ if (!(event instanceof MouseEvent)) return false;
39253
+ const pickState = beginFormulaRangePick(view, event);
39254
+ if (!pickState) return false;
39255
+ formulaRangePickRef.current = pickState;
39256
+ syncFormulaRangeHighlight(pickState);
39257
+ return true;
39258
+ },
39259
+ mousemove: (view, event) => {
39260
+ if (!(event instanceof MouseEvent)) return false;
39261
+ const pickState = formulaRangePickRef.current;
39262
+ if (!pickState) return false;
39263
+ if (event.buttons === 0) {
39264
+ formulaRangePickRef.current = null;
39265
+ syncFormulaRangeHighlight(null);
39266
+ return false;
39267
+ }
39268
+ const nextPickState = updateFormulaRangePick(view, pickState, event);
39269
+ formulaRangePickRef.current = nextPickState;
39270
+ syncFormulaRangeHighlight(nextPickState);
39271
+ return true;
39272
+ },
39273
+ mouseup: (_view, event) => {
39274
+ if (!(event instanceof MouseEvent)) return false;
39275
+ if (!formulaRangePickRef.current) return false;
39276
+ formulaRangePickRef.current = null;
39277
+ syncFormulaRangeHighlight(null);
39278
+ event.preventDefault();
39279
+ event.stopPropagation();
39280
+ return true;
39281
+ },
38972
39282
  keydown: (_view, event) => {
38973
39283
  if (!(event instanceof KeyboardEvent)) return false;
38974
39284
  if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
@@ -39134,7 +39444,10 @@ var UEditor = import_react71.default.forwardRef(({
39134
39444
  /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)(
39135
39445
  "div",
39136
39446
  {
39137
- ref: editorContentRef,
39447
+ ref: (node) => {
39448
+ editorContentRef.current = node;
39449
+ formulaRangeSurfaceRef.current = node;
39450
+ },
39138
39451
  className: "relative flex-1 overflow-y-auto",
39139
39452
  style: {
39140
39453
  minHeight: editable ? minHeight : void 0,
@@ -39166,6 +39479,20 @@ var UEditor = import_react71.default.forwardRef(({
39166
39479
  className: "pointer-events-none hidden absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10"
39167
39480
  }
39168
39481
  ),
39482
+ formulaRangeHighlight && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
39483
+ "span",
39484
+ {
39485
+ "aria-hidden": "true",
39486
+ "data-ueditor-formula-range-highlight": "",
39487
+ className: "pointer-events-none absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10",
39488
+ style: {
39489
+ left: formulaRangeHighlight.left,
39490
+ top: formulaRangeHighlight.top,
39491
+ width: formulaRangeHighlight.width,
39492
+ height: formulaRangeHighlight.height
39493
+ }
39494
+ }
39495
+ ),
39169
39496
  editable && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(TableControls, { editor, containerRef: editorContentRef }),
39170
39497
  /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
39171
39498
  import_react72.EditorContent,