@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.js CHANGED
@@ -869,12 +869,15 @@ var en_default = {
869
869
  clickToOpenNewTab: "Click to open this link in a new tab.",
870
870
  clickToOpenLink: "Click to open link.",
871
871
  noDescription: "No description available.",
872
- failedToLoad: "Failed to load link preview. Click to open link."
872
+ failedToLoad: "Failed to load link preview. Click to open link.",
873
+ previewFailed: "Preview failed to load",
874
+ retryPreview: "Retry preview"
873
875
  },
874
876
  fileCard: {
875
877
  unnamed: "Unnamed file",
876
878
  uploading: "(Uploading...)",
877
- uploadFailed: "Upload failed"
879
+ uploadFailed: "Upload failed",
880
+ retryUpload: "Retry upload"
878
881
  },
879
882
  menubar: {
880
883
  file: "File",
@@ -1261,12 +1264,15 @@ var vi_default = {
1261
1264
  clickToOpenNewTab: "B\u1EA5m \u0111\u1EC3 m\u1EDF li\xEAn k\u1EBFt n\xE0y trong tab m\u1EDBi.",
1262
1265
  clickToOpenLink: "B\u1EA5m \u0111\u1EC3 m\u1EDF li\xEAn k\u1EBFt.",
1263
1266
  noDescription: "Kh\xF4ng c\xF3 m\xF4 t\u1EA3.",
1264
- failedToLoad: "T\u1EA3i b\u1EA3n xem tr\u01B0\u1EDBc th\u1EA5t b\u1EA1i. B\u1EA5m \u0111\u1EC3 m\u1EDF li\xEAn k\u1EBFt."
1267
+ failedToLoad: "T\u1EA3i b\u1EA3n xem tr\u01B0\u1EDBc th\u1EA5t b\u1EA1i. B\u1EA5m \u0111\u1EC3 m\u1EDF li\xEAn k\u1EBFt.",
1268
+ previewFailed: "Kh\xF4ng t\u1EA3i \u0111\u01B0\u1EE3c b\u1EA3n xem tr\u01B0\u1EDBc",
1269
+ retryPreview: "Th\u1EED l\u1EA1i"
1265
1270
  },
1266
1271
  fileCard: {
1267
1272
  unnamed: "T\u1EC7p kh\xF4ng t\xEAn",
1268
1273
  uploading: "(\u0110ang t\u1EA3i l\xEAn...)",
1269
- uploadFailed: "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"
1274
+ uploadFailed: "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i",
1275
+ retryUpload: "T\u1EA3i l\u1EA1i"
1270
1276
  },
1271
1277
  menubar: {
1272
1278
  file: "T\u1EADp tin",
@@ -1653,12 +1659,15 @@ var ko_default = {
1653
1659
  clickToOpenNewTab: "\uD074\uB9AD\uD558\uC5EC \uC774 \uB9C1\uD06C\uB97C \uC0C8 \uD0ED\uC5D0\uC11C \uC5FD\uB2C8\uB2E4.",
1654
1660
  clickToOpenLink: "\uD074\uB9AD\uD558\uC5EC \uB9C1\uD06C\uB97C \uC5FD\uB2C8\uB2E4.",
1655
1661
  noDescription: "\uC124\uBA85\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
1656
- 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."
1662
+ 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.",
1663
+ previewFailed: "\uBBF8\uB9AC\uBCF4\uAE30\uB97C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4",
1664
+ retryPreview: "\uB2E4\uC2DC \uC2DC\uB3C4"
1657
1665
  },
1658
1666
  fileCard: {
1659
1667
  unnamed: "\uC774\uB984 \uC5C6\uB294 \uD30C\uC77C",
1660
1668
  uploading: "(\uC5C5\uB85C\uB4DC \uC911...)",
1661
- uploadFailed: "\uC5C5\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."
1669
+ uploadFailed: "\uC5C5\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.",
1670
+ retryUpload: "\uB2E4\uC2DC \uC5C5\uB85C\uB4DC"
1662
1671
  },
1663
1672
  menubar: {
1664
1673
  file: "\uD30C\uC77C",
@@ -2045,12 +2054,15 @@ var ja_default = {
2045
2054
  clickToOpenNewTab: "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u3053\u306E\u30EA\u30F3\u30AF\u3092\u65B0\u3057\u3044\u30BF\u30D6\u3067\u958B\u304F\u3002",
2046
2055
  clickToOpenLink: "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u30EA\u30F3\u30AF\u3092\u958B\u304F\u3002",
2047
2056
  noDescription: "\u8AAC\u660E\u306F\u3042\u308A\u307E\u305B\u3093\u3002",
2048
- 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"
2057
+ 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",
2058
+ previewFailed: "\u30D7\u30EC\u30D3\u30E5\u30FC\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F",
2059
+ retryPreview: "\u518D\u8A66\u884C"
2049
2060
  },
2050
2061
  fileCard: {
2051
2062
  unnamed: "\u540D\u524D\u306A\u3057\u30D5\u30A1\u30A4\u30EB",
2052
2063
  uploading: "(\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u4E2D...)",
2053
- uploadFailed: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u5931\u6557"
2064
+ uploadFailed: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u5931\u6557",
2065
+ retryUpload: "\u518D\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9"
2054
2066
  },
2055
2067
  menubar: {
2056
2068
  file: "\u30D5\u30A1\u30A4\u30EB",
@@ -24855,21 +24867,27 @@ import { Node as Node3, mergeAttributes as mergeAttributes2 } from "@tiptap/core
24855
24867
  import { ReactNodeViewRenderer as ReactNodeViewRenderer2 } from "@tiptap/react";
24856
24868
 
24857
24869
  // src/components/UEditor/BookmarkView.tsx
24858
- import { useEffect as useEffect31, useState as useState42 } from "react";
24870
+ import React68, { useEffect as useEffect31, useState as useState42 } from "react";
24859
24871
  import { NodeViewWrapper as NodeViewWrapper2 } from "@tiptap/react";
24860
- import { ExternalLink as ExternalLink2, Globe as Globe2 } from "lucide-react";
24872
+ import { AlertCircle as AlertCircle4, ExternalLink as ExternalLink2, Globe as Globe2, RefreshCw } from "lucide-react";
24861
24873
  import { jsx as jsx77, jsxs as jsxs63 } from "react/jsx-runtime";
24862
24874
  var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
24863
24875
  const t = useSmartTranslations("UEditor");
24864
24876
  const { url, title, description, image, publisher } = node.attrs;
24865
24877
  const [loading2, setLoading] = useState42(false);
24866
24878
  const [error, setError] = useState42(false);
24879
+ const [retryToken, setRetryToken] = useState42(0);
24880
+ const fetchedRetryTokenRef = React68.useRef(-1);
24881
+ const fetchRequestIdRef = React68.useRef(0);
24867
24882
  useEffect31(() => {
24868
- if (title) return;
24869
- let active = true;
24883
+ if (title && retryToken === 0) return;
24884
+ if (fetchedRetryTokenRef.current === retryToken) return;
24885
+ fetchedRetryTokenRef.current = retryToken;
24886
+ const requestId = fetchRequestIdRef.current + 1;
24887
+ fetchRequestIdRef.current = requestId;
24870
24888
  const safeUpdateAttributes = (attrs) => {
24871
24889
  Promise.resolve().then(() => {
24872
- if (active && editor && !editor.isDestroyed) {
24890
+ if (fetchRequestIdRef.current === requestId && editor && !editor.isDestroyed) {
24873
24891
  updateAttributes(attrs);
24874
24892
  }
24875
24893
  });
@@ -24896,7 +24914,7 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
24896
24914
  setLoading(true);
24897
24915
  setError(false);
24898
24916
  fetchMetadata(url).then((meta) => {
24899
- if (!active) return;
24917
+ if (fetchRequestIdRef.current !== requestId) return;
24900
24918
  safeUpdateAttributes({
24901
24919
  title: meta.title || new URL(url).hostname || url,
24902
24920
  description: meta.description || t("bookmark.noDescription"),
@@ -24905,33 +24923,22 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
24905
24923
  });
24906
24924
  setLoading(false);
24907
24925
  }).catch(() => {
24908
- if (!active) return;
24926
+ if (fetchRequestIdRef.current !== requestId) return;
24909
24927
  setError(true);
24910
24928
  setLoading(false);
24911
- try {
24912
- const hostname = new URL(url).hostname;
24913
- safeUpdateAttributes({
24914
- title: hostname || url,
24915
- description: t("bookmark.failedToLoad"),
24916
- publisher: hostname
24917
- });
24918
- } catch {
24919
- safeUpdateAttributes({
24920
- title: url,
24921
- description: t("bookmark.clickToOpenLink"),
24922
- publisher: ""
24923
- });
24924
- }
24925
24929
  });
24926
- return () => {
24927
- active = false;
24928
- };
24929
- }, [url, title, editor, updateAttributes, t]);
24930
+ return void 0;
24931
+ }, [url, title, editor, updateAttributes, t, retryToken]);
24930
24932
  const handleOpenLink = (e) => {
24931
24933
  e.preventDefault();
24932
24934
  e.stopPropagation();
24933
24935
  window.open(url, "_blank", "noopener,noreferrer");
24934
24936
  };
24937
+ const handleRetryPreview = (event) => {
24938
+ event.preventDefault();
24939
+ event.stopPropagation();
24940
+ setRetryToken((value) => value + 1);
24941
+ };
24935
24942
  const displayPublisher = publisher || (() => {
24936
24943
  try {
24937
24944
  return new URL(url).hostname;
@@ -24967,6 +24974,24 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
24967
24974
  /* @__PURE__ */ jsxs63("span", { className: "text-[11px] text-primary/80 font-mono mt-2 truncate flex items-center gap-1", children: [
24968
24975
  url,
24969
24976
  /* @__PURE__ */ jsx77(ExternalLink2, { className: "h-3 w-3 flex-shrink-0" })
24977
+ ] }),
24978
+ error && /* @__PURE__ */ jsxs63("div", { className: "mt-2 flex flex-wrap items-center gap-2 text-xs text-destructive", children: [
24979
+ /* @__PURE__ */ jsxs63("span", { className: "inline-flex items-center gap-1", children: [
24980
+ /* @__PURE__ */ jsx77(AlertCircle4, { className: "h-3.5 w-3.5" }),
24981
+ t("bookmark.previewFailed")
24982
+ ] }),
24983
+ /* @__PURE__ */ jsxs63(
24984
+ "button",
24985
+ {
24986
+ type: "button",
24987
+ onClick: handleRetryPreview,
24988
+ className: "inline-flex items-center gap-1 rounded-md border border-destructive/30 px-2 py-0.5 font-medium hover:bg-destructive/10",
24989
+ children: [
24990
+ /* @__PURE__ */ jsx77(RefreshCw, { className: "h-3 w-3" }),
24991
+ t("bookmark.retryPreview")
24992
+ ]
24993
+ }
24994
+ )
24970
24995
  ] })
24971
24996
  ] }),
24972
24997
  image && /* @__PURE__ */ jsx77("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__ */ jsx77(
@@ -25104,7 +25129,7 @@ import { ReactNodeViewRenderer as ReactNodeViewRenderer3 } from "@tiptap/react";
25104
25129
  // src/components/UEditor/FileCardView.tsx
25105
25130
  import { useEffect as useEffect32, useState as useState43, useRef as useRef27 } from "react";
25106
25131
  import { NodeViewWrapper as NodeViewWrapper3 } from "@tiptap/react";
25107
- import { Download as Download2, File as LucideFile, FileText as FileText2, Image as Image3, Music, Video } from "lucide-react";
25132
+ import { Download as Download2, File as LucideFile, FileText as FileText2, Image as Image3, Music, RefreshCw as RefreshCw2, Video } from "lucide-react";
25108
25133
  import { jsx as jsx78, jsxs as jsxs64 } from "react/jsx-runtime";
25109
25134
  function formatBytes(bytes, decimals = 1) {
25110
25135
  if (!bytes || bytes <= 0) return "0 Bytes";
@@ -25151,6 +25176,7 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
25151
25176
  const { src, fileName, fileSize, fileType } = node.attrs;
25152
25177
  const FileIcon = getFileIcon2(fileType);
25153
25178
  const [uploadError, setUploadError] = useState43(null);
25179
+ const [retryToken, setRetryToken] = useState43(0);
25154
25180
  const uploadAttemptedRef = useRef27(false);
25155
25181
  useEffect32(() => {
25156
25182
  if (!src || !src.startsWith("data:")) {
@@ -25182,7 +25208,7 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
25182
25208
  }
25183
25209
  };
25184
25210
  void runUpload();
25185
- }, [src, fileName, fileType, editor, updateAttributes]);
25211
+ }, [src, fileName, fileType, editor, updateAttributes, retryToken]);
25186
25212
  const handleDownload = (e) => {
25187
25213
  e.preventDefault();
25188
25214
  e.stopPropagation();
@@ -25195,6 +25221,13 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
25195
25221
  document.body.removeChild(link);
25196
25222
  };
25197
25223
  const isUploading = (src || "").startsWith("data:");
25224
+ const handleRetryUpload = (event) => {
25225
+ event.preventDefault();
25226
+ event.stopPropagation();
25227
+ uploadAttemptedRef.current = false;
25228
+ setUploadError(null);
25229
+ setRetryToken((value) => value + 1);
25230
+ };
25198
25231
  return /* @__PURE__ */ jsxs64(
25199
25232
  NodeViewWrapper3,
25200
25233
  {
@@ -25213,7 +25246,19 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
25213
25246
  isUploading && /* @__PURE__ */ jsx78("span", { className: cn(
25214
25247
  "font-medium truncate max-w-[200px]",
25215
25248
  uploadError ? "text-destructive" : "text-primary animate-pulse"
25216
- ), children: uploadError ? `${t("fileCard.uploadFailed") || "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"}: ${uploadError}` : t("fileCard.uploading") })
25249
+ ), children: uploadError ? `${t("fileCard.uploadFailed") || "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"}: ${uploadError}` : t("fileCard.uploading") }),
25250
+ isUploading && uploadError && /* @__PURE__ */ jsxs64(
25251
+ "button",
25252
+ {
25253
+ type: "button",
25254
+ onClick: handleRetryUpload,
25255
+ 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",
25256
+ children: [
25257
+ /* @__PURE__ */ jsx78(RefreshCw2, { className: "h-3 w-3" }),
25258
+ t("fileCard.retryUpload")
25259
+ ]
25260
+ }
25261
+ )
25217
25262
  ] })
25218
25263
  ] })
25219
25264
  ] }),
@@ -28816,6 +28861,20 @@ function createEmptyCellNode(cellNode) {
28816
28861
  function createCellCopyForColumnDuplicate(cellNode) {
28817
28862
  return cellNode.type.create(cellNode.attrs, cellNode.content);
28818
28863
  }
28864
+ function createCellWithDuplicatedLogicalColumn(cellNode, widthIndex) {
28865
+ const colspan = Math.max(1, Number(cellNode.attrs.colspan) || 1);
28866
+ let nextColwidth = null;
28867
+ if (Array.isArray(cellNode.attrs.colwidth)) {
28868
+ nextColwidth = [...cellNode.attrs.colwidth];
28869
+ const duplicateWidth = nextColwidth[widthIndex];
28870
+ nextColwidth.splice(widthIndex + 1, 0, typeof duplicateWidth === "number" ? duplicateWidth : 0);
28871
+ }
28872
+ return cellNode.type.create({
28873
+ ...cellNode.attrs,
28874
+ colspan: colspan + 1,
28875
+ ...nextColwidth ? { colwidth: nextColwidth } : null
28876
+ }, cellNode.content);
28877
+ }
28819
28878
  function getTableRows(tableNode) {
28820
28879
  const rows = [];
28821
28880
  tableNode.forEach((rowNode, rowOffset) => {
@@ -28991,6 +29050,11 @@ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
28991
29050
  return rect && rect.top === rowIndex && rect.left <= columnIndex && columnIndex < rect.right;
28992
29051
  });
28993
29052
  if (!sourceCell) return rowInfo.node;
29053
+ const sourceRect = safeFindCell(map, sourceCell.relativePos);
29054
+ if (sourceRect && (sourceRect.left < columnIndex || sourceRect.right > columnIndex + 1)) {
29055
+ cells[sourceCell.index] = createCellWithDuplicatedLogicalColumn(sourceCell.node, columnIndex - sourceRect.left);
29056
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
29057
+ }
28994
29058
  cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
28995
29059
  return rowInfo.node.type.create(rowInfo.node.attrs, cells);
28996
29060
  });
@@ -30312,6 +30376,25 @@ function getTableFormulaRecalculationOrder(graph) {
30312
30376
  }
30313
30377
  return { order, circular };
30314
30378
  }
30379
+ function getAffectedTableFormulaLabels(graph, changedLabels) {
30380
+ const affected = /* @__PURE__ */ new Set();
30381
+ const queue = Array.from(changedLabels, (label) => label.toUpperCase());
30382
+ for (const label of queue) {
30383
+ if (graph.formulas.has(label)) {
30384
+ affected.add(label);
30385
+ }
30386
+ }
30387
+ for (let index = 0; index < queue.length; index += 1) {
30388
+ const label = queue[index];
30389
+ if (!label) continue;
30390
+ for (const dependent of graph.dependents.get(label) ?? []) {
30391
+ if (affected.has(dependent)) continue;
30392
+ affected.add(dependent);
30393
+ queue.push(dependent);
30394
+ }
30395
+ }
30396
+ return affected;
30397
+ }
30315
30398
  function evaluateBasicTableFormula(formula, getCellValue) {
30316
30399
  const normalized = normalizeTableFormula(formula);
30317
30400
  if (!normalized) {
@@ -30459,10 +30542,19 @@ var FormulaParser = class {
30459
30542
  const range = parseTableCellRange(token.value);
30460
30543
  if (!range) return { value: null, error: "invalid-reference" };
30461
30544
  for (const label of getTableCellRangeLabels(range)) {
30545
+ if (name === "COUNT") {
30546
+ const cellValue2 = this.readOptionalCellNumber(label);
30547
+ if (cellValue2 != null) values.push(cellValue2);
30548
+ continue;
30549
+ }
30462
30550
  const cellValue = this.readCellNumber(label);
30463
30551
  if (cellValue.error) return cellValue;
30464
30552
  values.push(cellValue.value);
30465
30553
  }
30554
+ } else if (name === "COUNT" && token.type === "cell") {
30555
+ this.index += 1;
30556
+ const cellValue = this.readOptionalCellNumber(token.value);
30557
+ if (cellValue != null) values.push(cellValue);
30466
30558
  } else {
30467
30559
  const value = this.parseExpression();
30468
30560
  if (value.error) return value;
@@ -30476,7 +30568,7 @@ var FormulaParser = class {
30476
30568
  }
30477
30569
  return { value: null, error: "invalid-formula" };
30478
30570
  }
30479
- if (values.length === 0) {
30571
+ if (values.length === 0 && name !== "COUNT") {
30480
30572
  return { value: null, error: "invalid-formula" };
30481
30573
  }
30482
30574
  if (name === "SUM") return { value: values.reduce((sum, value) => sum + value, 0), error: null };
@@ -30494,6 +30586,11 @@ var FormulaParser = class {
30494
30586
  }
30495
30587
  return { value: parsed, error: null };
30496
30588
  }
30589
+ readOptionalCellNumber(label) {
30590
+ const value = this.getCellValue(label);
30591
+ const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
30592
+ return Number.isFinite(parsed) ? parsed : null;
30593
+ }
30497
30594
  peekOperator(operators) {
30498
30595
  const token = this.tokens[this.index];
30499
30596
  return token?.type === "operator" && operators.includes(token.value) ? token : null;
@@ -30557,6 +30654,41 @@ function getSelectionTableCellNode(editor) {
30557
30654
  }
30558
30655
  return null;
30559
30656
  }
30657
+ function getSelectionTableInfo(editor) {
30658
+ const { $from } = editor.state.selection;
30659
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
30660
+ const node = $from.node(depth);
30661
+ if (node.type.name === "table") {
30662
+ return {
30663
+ node,
30664
+ pos: $from.before(depth)
30665
+ };
30666
+ }
30667
+ }
30668
+ return null;
30669
+ }
30670
+ function getSelectionTableCellLabel(editor) {
30671
+ const { $from } = editor.state.selection;
30672
+ let cellDepth = -1;
30673
+ let tableDepth = -1;
30674
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
30675
+ const node = $from.node(depth);
30676
+ if (cellDepth < 0 && (node.type.name === "tableCell" || node.type.name === "tableHeader")) {
30677
+ cellDepth = depth;
30678
+ }
30679
+ if (node.type.name === "table") {
30680
+ tableDepth = depth;
30681
+ break;
30682
+ }
30683
+ }
30684
+ if (cellDepth < 0 || tableDepth < 0) return null;
30685
+ const tableNode = $from.node(tableDepth);
30686
+ const tableStart = $from.start(tableDepth);
30687
+ const relativeCellPos = $from.before(cellDepth) - tableStart;
30688
+ const rect = safeFindCell2(TableMap2.get(tableNode), relativeCellPos);
30689
+ if (!rect) return null;
30690
+ return `${indexToColumnName(rect.left)}${rect.top + 1}`;
30691
+ }
30560
30692
  function isEditingTableFormulaText(editor) {
30561
30693
  const cellNode = getSelectionTableCellNode(editor);
30562
30694
  return Boolean(cellNode && getCellText(cellNode).startsWith("="));
@@ -30568,20 +30700,6 @@ function createCellDisplayContent(cellNode, displayValue) {
30568
30700
  }
30569
30701
  return [paragraphType.create(null, cellNode.type.schema.text(displayValue))];
30570
30702
  }
30571
- function buildTableValueGetter(tableNode) {
30572
- const map = TableMap2.get(tableNode);
30573
- const values = /* @__PURE__ */ new Map();
30574
- for (const rowInfo of getTableRows2(tableNode)) {
30575
- for (const entry of rowInfo.cells) {
30576
- const rect = safeFindCell2(map, entry.relativePos);
30577
- if (!rect) continue;
30578
- const label = `${indexToColumnName(rect.left)}${rect.top + 1}`;
30579
- const computedValue = entry.node.attrs.computedValue;
30580
- values.set(label, typeof computedValue === "string" && computedValue.trim() ? computedValue : getCellText(entry.node));
30581
- }
30582
- }
30583
- return (label) => values.get(label.toUpperCase());
30584
- }
30585
30703
  function buildTableValueMap(tableNode) {
30586
30704
  const map = TableMap2.get(tableNode);
30587
30705
  const values = /* @__PURE__ */ new Map();
@@ -30596,10 +30714,6 @@ function buildTableValueMap(tableNode) {
30596
30714
  }
30597
30715
  return values;
30598
30716
  }
30599
- function getFormulaComputedValue(formula, tableNode) {
30600
- const result = evaluateBasicTableFormula(formula, buildTableValueGetter(tableNode));
30601
- return result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30602
- }
30603
30717
  function normalizeFormulaInput(formula) {
30604
30718
  const trimmed = formula.trim();
30605
30719
  if (!trimmed) return "";
@@ -30610,19 +30724,19 @@ function setSelectedTableCellFormula(editor, formula) {
30610
30724
  const { state, view } = editor;
30611
30725
  if (!normalized) {
30612
30726
  const clearedFormula = setCellAttr("formula", null)(state, view.dispatch.bind(view));
30613
- const clearedValue = setCellAttr("computedValue", null)(editor.state, view.dispatch.bind(view));
30614
- if (clearedFormula || clearedValue) {
30727
+ const clearedValue2 = setCellAttr("computedValue", null)(editor.state, view.dispatch.bind(view));
30728
+ if (clearedFormula || clearedValue2) {
30729
+ recalculateActiveTableFormulas(editor);
30615
30730
  view.focus();
30616
30731
  dispatchTableLayoutChange(editor);
30617
30732
  return true;
30618
30733
  }
30619
30734
  return false;
30620
30735
  }
30621
- const rect = selectedRect2(state);
30622
- const computedValue = getFormulaComputedValue(normalized, rect.table);
30623
30736
  const appliedFormula = setCellAttr("formula", normalized)(state, view.dispatch.bind(view));
30624
- const appliedValue = setCellAttr("computedValue", computedValue)(editor.state, view.dispatch.bind(view));
30625
- if (appliedFormula || appliedValue) {
30737
+ const clearedValue = setCellAttr("computedValue", null)(editor.state, view.dispatch.bind(view));
30738
+ if (appliedFormula || clearedValue) {
30739
+ recalculateActiveTableFormulas(editor);
30626
30740
  view.focus();
30627
30741
  dispatchTableLayoutChange(editor);
30628
30742
  return true;
@@ -30704,7 +30818,7 @@ function promoteFormulaTextInTableNode(tableNode) {
30704
30818
  changed
30705
30819
  };
30706
30820
  }
30707
- function recalculateTableNode(tableNode) {
30821
+ function recalculateTableNode(tableNode, options) {
30708
30822
  const promoted = promoteFormulaTextInTableNode(tableNode);
30709
30823
  tableNode = promoted.tableNode;
30710
30824
  const map = TableMap2.get(tableNode);
@@ -30734,13 +30848,16 @@ function recalculateTableNode(tableNode) {
30734
30848
  }))
30735
30849
  );
30736
30850
  const { order, circular } = getTableFormulaRecalculationOrder(graph);
30851
+ const affectedLabels = options?.changedLabels ? getAffectedTableFormulaLabels(graph, options.changedLabels) : null;
30737
30852
  const computedValues = /* @__PURE__ */ new Map();
30738
30853
  const getCellValue = (label) => values.get(label.toUpperCase());
30739
30854
  for (const label of circular) {
30855
+ if (affectedLabels && !affectedLabels.has(label)) continue;
30740
30856
  computedValues.set(label, formatFormulaError("circular-reference"));
30741
30857
  values.set(label, formatFormulaError("circular-reference"));
30742
30858
  }
30743
30859
  for (const label of order) {
30860
+ if (affectedLabels && !affectedLabels.has(label)) continue;
30744
30861
  const entry = formulaEntries.get(label);
30745
30862
  if (!entry) continue;
30746
30863
  const result = evaluateBasicTableFormula(entry.formula, getCellValue);
@@ -30785,6 +30902,22 @@ function recalculateSelectedTable(editor) {
30785
30902
  dispatchTableLayoutChange(editor);
30786
30903
  return true;
30787
30904
  }
30905
+ function recalculateActiveTableFormulas(editor) {
30906
+ const tableInfo = getSelectionTableInfo(editor);
30907
+ if (!tableInfo) {
30908
+ return recalculateAllTableFormulas(editor);
30909
+ }
30910
+ const activeCellLabel = getSelectionTableCellLabel(editor);
30911
+ const nextTable = recalculateTableNode(tableInfo.node, {
30912
+ changedLabels: activeCellLabel ? [activeCellLabel] : null
30913
+ });
30914
+ if (!nextTable) return false;
30915
+ editor.view.dispatch(
30916
+ editor.state.tr.replaceWith(tableInfo.pos, tableInfo.pos + tableInfo.node.nodeSize, nextTable).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true)
30917
+ );
30918
+ dispatchTableLayoutChange(editor);
30919
+ return true;
30920
+ }
30788
30921
  function recalculateAllTableFormulas(editor) {
30789
30922
  const replacements = [];
30790
30923
  editor.state.doc.descendants((node, pos) => {
@@ -38820,6 +38953,146 @@ var MenuBar = ({
38820
38953
  ] });
38821
38954
  };
38822
38955
 
38956
+ // src/components/UEditor/table-formula-range-picker.ts
38957
+ import { TextSelection as TextSelection4 } from "@tiptap/pm/state";
38958
+ import { TableMap as TableMap4 } from "@tiptap/pm/tables";
38959
+ function getCellText2(cellNode) {
38960
+ return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
38961
+ }
38962
+ function findSelectionFormulaCell(view) {
38963
+ const { $from } = view.state.selection;
38964
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
38965
+ const node = $from.node(depth);
38966
+ if (node.type.name !== "tableCell" && node.type.name !== "tableHeader") continue;
38967
+ if (!getCellText2(node).startsWith("=")) return null;
38968
+ const cellPos = $from.before(depth);
38969
+ const cellDom = view.nodeDOM(cellPos);
38970
+ const tableDepth = depth - 2;
38971
+ const tableNode = tableDepth > 0 ? $from.node(tableDepth) : null;
38972
+ if (!tableNode || tableNode.type.name !== "table") return null;
38973
+ return {
38974
+ cellDom: cellDom instanceof HTMLTableCellElement ? cellDom : null,
38975
+ tablePos: $from.before(tableDepth)
38976
+ };
38977
+ }
38978
+ return null;
38979
+ }
38980
+ function findTableForPos(view, pos) {
38981
+ const $pos = view.state.doc.resolve(pos);
38982
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
38983
+ const node = $pos.node(depth);
38984
+ if (node.type.name === "table") {
38985
+ return {
38986
+ node,
38987
+ pos: $pos.before(depth),
38988
+ start: $pos.start(depth)
38989
+ };
38990
+ }
38991
+ }
38992
+ return null;
38993
+ }
38994
+ function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
38995
+ const relativeDomPos = domPos - tableStart;
38996
+ const seen = /* @__PURE__ */ new Set();
38997
+ for (const relativeCellPos of map.map) {
38998
+ if (seen.has(relativeCellPos)) continue;
38999
+ seen.add(relativeCellPos);
39000
+ if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
39001
+ return relativeCellPos;
39002
+ }
39003
+ }
39004
+ return null;
39005
+ }
39006
+ function getPickedCellLabel(view, target, tablePos) {
39007
+ const element = resolveEventElement(target);
39008
+ const cell = element?.closest?.("th,td");
39009
+ if (!(cell instanceof HTMLTableCellElement)) return null;
39010
+ const domPos = view.posAtDOM(cell, 0);
39011
+ const tableInfo = findTableForPos(view, domPos);
39012
+ if (!tableInfo || tableInfo.pos !== tablePos) return null;
39013
+ const map = TableMap4.get(tableInfo.node);
39014
+ const relativeCellPos = getCellRelativePosFromDomPos2(map, tableInfo.start, domPos);
39015
+ if (relativeCellPos == null) return null;
39016
+ const rect = map.findCell(relativeCellPos);
39017
+ return {
39018
+ cell,
39019
+ label: `${indexToColumnName(rect.left)}${rect.top + 1}`
39020
+ };
39021
+ }
39022
+ function normalizeFormulaRangeLabel(fromLabel, toLabel) {
39023
+ return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
39024
+ }
39025
+ function replacePickedLabel(view, pickState, nextLabel, currentCell) {
39026
+ if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell) return pickState;
39027
+ if (nextLabel === pickState.currentLabel) {
39028
+ return {
39029
+ ...pickState,
39030
+ currentCell
39031
+ };
39032
+ }
39033
+ let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
39034
+ const nextTo = pickState.insertedFrom + nextLabel.length;
39035
+ tr = tr.setSelection(TextSelection4.create(tr.doc, nextTo));
39036
+ view.dispatch(tr);
39037
+ return {
39038
+ ...pickState,
39039
+ insertedTo: nextTo,
39040
+ currentLabel: nextLabel,
39041
+ currentCell
39042
+ };
39043
+ }
39044
+ function beginFormulaRangePick(view, event) {
39045
+ if (event.button !== 0) return null;
39046
+ const formulaCell = findSelectionFormulaCell(view);
39047
+ if (!formulaCell) return null;
39048
+ const picked = getPickedCellLabel(view, event.target, formulaCell.tablePos);
39049
+ if (!picked || picked.cell === formulaCell.cellDom) return null;
39050
+ const { from, to } = view.state.selection;
39051
+ let tr = view.state.tr.insertText(picked.label, from, to);
39052
+ tr = tr.setSelection(TextSelection4.create(tr.doc, from + picked.label.length));
39053
+ view.dispatch(tr);
39054
+ view.focus();
39055
+ event.preventDefault();
39056
+ event.stopPropagation();
39057
+ return {
39058
+ anchorLabel: picked.label,
39059
+ anchorCell: picked.cell,
39060
+ tablePos: formulaCell.tablePos,
39061
+ insertedFrom: from,
39062
+ insertedTo: from + picked.label.length,
39063
+ currentLabel: picked.label,
39064
+ currentCell: picked.cell
39065
+ };
39066
+ }
39067
+ function updateFormulaRangePick(view, pickState, event) {
39068
+ const picked = getPickedCellLabel(view, event.target, pickState.tablePos);
39069
+ if (!picked) return pickState;
39070
+ event.preventDefault();
39071
+ event.stopPropagation();
39072
+ return replacePickedLabel(
39073
+ view,
39074
+ pickState,
39075
+ normalizeFormulaRangeLabel(pickState.anchorLabel, picked.label),
39076
+ picked.cell
39077
+ );
39078
+ }
39079
+ function getFormulaRangePickHighlight(container, pickState) {
39080
+ if (!container.contains(pickState.anchorCell) || !container.contains(pickState.currentCell)) return null;
39081
+ const containerRect = container.getBoundingClientRect();
39082
+ const anchorRect = pickState.anchorCell.getBoundingClientRect();
39083
+ const currentRect = pickState.currentCell.getBoundingClientRect();
39084
+ const left = Math.min(anchorRect.left, currentRect.left) - containerRect.left + container.scrollLeft;
39085
+ const top = Math.min(anchorRect.top, currentRect.top) - containerRect.top + container.scrollTop;
39086
+ const right = Math.max(anchorRect.right, currentRect.right) - containerRect.left + container.scrollLeft;
39087
+ const bottom = Math.max(anchorRect.bottom, currentRect.bottom) - containerRect.top + container.scrollTop;
39088
+ return {
39089
+ left,
39090
+ top,
39091
+ width: Math.max(0, right - left),
39092
+ height: Math.max(0, bottom - top)
39093
+ };
39094
+ }
39095
+
38823
39096
  // src/components/UEditor/UEditor.tsx
38824
39097
  import { jsx as jsx94, jsxs as jsxs79 } from "react/jsx-runtime";
38825
39098
  var UEditor = React83.forwardRef(({
@@ -38852,7 +39125,6 @@ var UEditor = React83.forwardRef(({
38852
39125
  fetchMetadata,
38853
39126
  uploadFile,
38854
39127
  uploadFileForSave,
38855
- collaborationOptions,
38856
39128
  extraExtensions,
38857
39129
  showMenuBar = false,
38858
39130
  onSave,
@@ -38865,6 +39137,9 @@ var UEditor = React83.forwardRef(({
38865
39137
  const inFlightPrepareRef = useRef37(null);
38866
39138
  const lastAppliedContentRef = useRef37(content ?? "");
38867
39139
  const scheduledFormulaRecalculateRef = useRef37(false);
39140
+ const formulaRangePickRef = useRef37(null);
39141
+ const formulaRangeSurfaceRef = useRef37(null);
39142
+ const [formulaRangeHighlight, setFormulaRangeHighlight] = React83.useState(null);
38868
39143
  const scheduleFormulaRecalculate = React83.useCallback((editor2, options) => {
38869
39144
  if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
38870
39145
  if (!options?.force && isEditingTableFormulaText(editor2)) return;
@@ -38872,7 +39147,7 @@ var UEditor = React83.forwardRef(({
38872
39147
  queueMicrotask(() => {
38873
39148
  scheduledFormulaRecalculateRef.current = false;
38874
39149
  if (!editor2.isDestroyed && (options?.force || !isEditingTableFormulaText(editor2))) {
38875
- recalculateAllTableFormulas(editor2);
39150
+ recalculateActiveTableFormulas(editor2);
38876
39151
  }
38877
39152
  });
38878
39153
  }, []);
@@ -38905,6 +39180,10 @@ var UEditor = React83.forwardRef(({
38905
39180
  ],
38906
39181
  [effectivePlaceholder, t, maxCharacters, uploadImage, resolvedUploadFile, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable, fetchMetadata, extraExtensions]
38907
39182
  );
39183
+ const syncFormulaRangeHighlight = React83.useCallback((pickState) => {
39184
+ const container = formulaRangeSurfaceRef.current;
39185
+ setFormulaRangeHighlight(container && pickState ? getFormulaRangePickHighlight(container, pickState) : null);
39186
+ }, []);
38908
39187
  const editor = useEditor({
38909
39188
  immediatelyRender: false,
38910
39189
  extensions,
@@ -38913,6 +39192,37 @@ var UEditor = React83.forwardRef(({
38913
39192
  autofocus,
38914
39193
  editorProps: {
38915
39194
  handleDOMEvents: {
39195
+ mousedown: (view, event) => {
39196
+ if (!(event instanceof MouseEvent)) return false;
39197
+ const pickState = beginFormulaRangePick(view, event);
39198
+ if (!pickState) return false;
39199
+ formulaRangePickRef.current = pickState;
39200
+ syncFormulaRangeHighlight(pickState);
39201
+ return true;
39202
+ },
39203
+ mousemove: (view, event) => {
39204
+ if (!(event instanceof MouseEvent)) return false;
39205
+ const pickState = formulaRangePickRef.current;
39206
+ if (!pickState) return false;
39207
+ if (event.buttons === 0) {
39208
+ formulaRangePickRef.current = null;
39209
+ syncFormulaRangeHighlight(null);
39210
+ return false;
39211
+ }
39212
+ const nextPickState = updateFormulaRangePick(view, pickState, event);
39213
+ formulaRangePickRef.current = nextPickState;
39214
+ syncFormulaRangeHighlight(nextPickState);
39215
+ return true;
39216
+ },
39217
+ mouseup: (_view, event) => {
39218
+ if (!(event instanceof MouseEvent)) return false;
39219
+ if (!formulaRangePickRef.current) return false;
39220
+ formulaRangePickRef.current = null;
39221
+ syncFormulaRangeHighlight(null);
39222
+ event.preventDefault();
39223
+ event.stopPropagation();
39224
+ return true;
39225
+ },
38916
39226
  keydown: (_view, event) => {
38917
39227
  if (!(event instanceof KeyboardEvent)) return false;
38918
39228
  if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
@@ -39078,7 +39388,10 @@ var UEditor = React83.forwardRef(({
39078
39388
  /* @__PURE__ */ jsxs79(
39079
39389
  "div",
39080
39390
  {
39081
- ref: editorContentRef,
39391
+ ref: (node) => {
39392
+ editorContentRef.current = node;
39393
+ formulaRangeSurfaceRef.current = node;
39394
+ },
39082
39395
  className: "relative flex-1 overflow-y-auto",
39083
39396
  style: {
39084
39397
  minHeight: editable ? minHeight : void 0,
@@ -39110,6 +39423,20 @@ var UEditor = React83.forwardRef(({
39110
39423
  className: "pointer-events-none hidden absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10"
39111
39424
  }
39112
39425
  ),
39426
+ formulaRangeHighlight && /* @__PURE__ */ jsx94(
39427
+ "span",
39428
+ {
39429
+ "aria-hidden": "true",
39430
+ "data-ueditor-formula-range-highlight": "",
39431
+ className: "pointer-events-none absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10",
39432
+ style: {
39433
+ left: formulaRangeHighlight.left,
39434
+ top: formulaRangeHighlight.top,
39435
+ width: formulaRangeHighlight.width,
39436
+ height: formulaRangeHighlight.height
39437
+ }
39438
+ }
39439
+ ),
39113
39440
  editable && /* @__PURE__ */ jsx94(TableControls, { editor, containerRef: editorContentRef }),
39114
39441
  /* @__PURE__ */ jsx94(
39115
39442
  EditorContent,