@underverse-ui/underverse 1.0.149 → 1.0.151

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
@@ -1029,6 +1029,10 @@ var en_default = {
1029
1029
  borderWidth: "Border Width",
1030
1030
  borderColor: "Border Color",
1031
1031
  clearBorder: "Clear Border",
1032
+ formula: "Formula",
1033
+ apply: "Apply",
1034
+ clear: "Clear",
1035
+ recalculate: "Recalculate",
1032
1036
  done: "Done"
1033
1037
  },
1034
1038
  callout: {
@@ -1405,6 +1409,10 @@ var vi_default = {
1405
1409
  borderWidth: "\u0110\u1ED9 d\xE0y vi\u1EC1n",
1406
1410
  borderColor: "M\xE0u vi\u1EC1n",
1407
1411
  clearBorder: "X\xF3a vi\u1EC1n",
1412
+ formula: "C\xF4ng th\u1EE9c",
1413
+ apply: "\xC1p d\u1EE5ng",
1414
+ clear: "X\xF3a",
1415
+ recalculate: "T\xEDnh l\u1EA1i",
1408
1416
  done: "Xong"
1409
1417
  },
1410
1418
  callout: {
@@ -25836,6 +25844,8 @@ var import_core5 = require("@tiptap/core");
25836
25844
  var import_state2 = require("@tiptap/pm/state");
25837
25845
 
25838
25846
  // src/components/UEditor/clipboard-tables.ts
25847
+ var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
25848
+ var DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
25839
25849
  function getClipboardData(dataTransfer, type) {
25840
25850
  try {
25841
25851
  return dataTransfer.getData(type) ?? "";
@@ -25856,6 +25866,411 @@ function extractClipboardHtmlFragment(html) {
25856
25866
  function normalizeClipboardCellText(value) {
25857
25867
  return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " ").replace(/[ \t]+\n/g, "\n").replace(/\n[ \t]+/g, "\n").replace(/\n+$/g, "").replace(/^\n+/g, "").trim();
25858
25868
  }
25869
+ function parseStyleDeclarations(styleText) {
25870
+ const declarations = /* @__PURE__ */ new Map();
25871
+ if (!styleText) return declarations;
25872
+ for (const declaration of styleText.split(";")) {
25873
+ const separatorIndex = declaration.indexOf(":");
25874
+ if (separatorIndex <= 0) continue;
25875
+ const property = declaration.slice(0, separatorIndex).trim().toLowerCase();
25876
+ const value = cleanStyleValue(declaration.slice(separatorIndex + 1));
25877
+ if (!property || !value) continue;
25878
+ declarations.set(property, value);
25879
+ }
25880
+ return declarations;
25881
+ }
25882
+ function mergeStyleDeclarations(...sources) {
25883
+ const declarations = /* @__PURE__ */ new Map();
25884
+ for (const source of sources) {
25885
+ if (!source) continue;
25886
+ for (const [property, value] of source.entries()) {
25887
+ declarations.set(property, value);
25888
+ }
25889
+ }
25890
+ return declarations;
25891
+ }
25892
+ function extractCssClassNames(selectorText) {
25893
+ const classNames = /* @__PURE__ */ new Set();
25894
+ const classNamePattern = /\.([_a-zA-Z-][\w-]*)/g;
25895
+ let match;
25896
+ while ((match = classNamePattern.exec(selectorText)) !== null) {
25897
+ classNames.add(match[1]);
25898
+ }
25899
+ return classNames;
25900
+ }
25901
+ function parseClipboardCssClassStyles(doc) {
25902
+ const styleMap = /* @__PURE__ */ new Map();
25903
+ for (const styleElement of Array.from(doc.querySelectorAll("style"))) {
25904
+ const cssText = (styleElement.textContent ?? "").replace(/<!--|-->/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
25905
+ const rulePattern = /([^{}]+)\{([^{}]*)\}/g;
25906
+ let match;
25907
+ while ((match = rulePattern.exec(cssText)) !== null) {
25908
+ const classNames = extractCssClassNames(match[1]);
25909
+ if (classNames.size === 0) continue;
25910
+ const declarations = parseStyleDeclarations(match[2]);
25911
+ if (declarations.size === 0) continue;
25912
+ for (const className of classNames) {
25913
+ styleMap.set(className, mergeStyleDeclarations(styleMap.get(className), declarations));
25914
+ }
25915
+ }
25916
+ }
25917
+ return styleMap;
25918
+ }
25919
+ function getElementStyleDeclarations(element, styleMap) {
25920
+ const classDeclarations = Array.from(element.classList).map((className) => styleMap.get(className));
25921
+ const inlineDeclarations = parseStyleDeclarations(element.getAttribute("style"));
25922
+ return mergeStyleDeclarations(...classDeclarations, inlineDeclarations);
25923
+ }
25924
+ function cleanStyleValue(value) {
25925
+ const normalized = value?.trim();
25926
+ if (!normalized) return null;
25927
+ if (/[\0<>;{}]/.test(normalized)) return null;
25928
+ if (/\b(?:expression|url|(?:repeating-)?(?:linear|radial|conic)-gradient)\s*\(/i.test(normalized)) return null;
25929
+ return normalized;
25930
+ }
25931
+ function normalizeColorValue(value) {
25932
+ const normalized = cleanStyleValue(value);
25933
+ if (!normalized) return null;
25934
+ if (/^(?:auto|inherit|initial|none|transparent|unset)$/i.test(normalized)) return null;
25935
+ return normalized;
25936
+ }
25937
+ function normalizeTextColorValue(value) {
25938
+ const normalized = normalizeColorValue(value);
25939
+ if (!normalized) return null;
25940
+ if (/^(?:automatic|windowtext|black|#000|#000000|rgb\(\s*0\s*,\s*0\s*,\s*0\s*\))$/i.test(normalized)) {
25941
+ return DEFAULT_HTML_TABLE_TEXT_COLOR;
25942
+ }
25943
+ return normalized;
25944
+ }
25945
+ function isWhiteColor(value) {
25946
+ if (!value) return false;
25947
+ return /^(?:white|#fff|#ffffff|rgb\(\s*255\s*,\s*255\s*,\s*255\s*\))$/i.test(value.trim());
25948
+ }
25949
+ function parseCssColorRgb(value) {
25950
+ const normalized = normalizeColorValue(value);
25951
+ if (!normalized) return null;
25952
+ const lowerColor = normalized.toLowerCase();
25953
+ if (lowerColor === "white") return { r: 255, g: 255, b: 255 };
25954
+ if (lowerColor === "black") return { r: 0, g: 0, b: 0 };
25955
+ const hexMatch = lowerColor.match(/^#([\da-f]{3}|[\da-f]{6})$/i);
25956
+ if (hexMatch) {
25957
+ const hex = hexMatch[1];
25958
+ const fullHex = hex.length === 3 ? hex.split("").map((part) => part + part).join("") : hex;
25959
+ return {
25960
+ r: Number.parseInt(fullHex.slice(0, 2), 16),
25961
+ g: Number.parseInt(fullHex.slice(2, 4), 16),
25962
+ b: Number.parseInt(fullHex.slice(4, 6), 16)
25963
+ };
25964
+ }
25965
+ const rgbMatch = lowerColor.match(/^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)/);
25966
+ if (rgbMatch) {
25967
+ return {
25968
+ r: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[1]))),
25969
+ g: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[2]))),
25970
+ b: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[3])))
25971
+ };
25972
+ }
25973
+ return null;
25974
+ }
25975
+ function getRelativeLuminance(value) {
25976
+ const rgb = parseCssColorRgb(value);
25977
+ if (!rgb) return null;
25978
+ const toLinear = (channel) => {
25979
+ const normalized = channel / 255;
25980
+ return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
25981
+ };
25982
+ return 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);
25983
+ }
25984
+ function isLightTextColor(value) {
25985
+ const luminance = getRelativeLuminance(value);
25986
+ return luminance !== null && luminance >= 0.72;
25987
+ }
25988
+ function isDarkReadableBackground(value) {
25989
+ const luminance = getRelativeLuminance(value);
25990
+ return luminance !== null && luminance <= 0.45;
25991
+ }
25992
+ function splitCssTokens(value) {
25993
+ const tokens = [];
25994
+ let current = "";
25995
+ let depth = 0;
25996
+ for (const char of value) {
25997
+ if (char === "(") depth += 1;
25998
+ if (char === ")") depth = Math.max(0, depth - 1);
25999
+ if (/\s/.test(char) && depth === 0) {
26000
+ if (current) {
26001
+ tokens.push(current);
26002
+ current = "";
26003
+ }
26004
+ continue;
26005
+ }
26006
+ current += char;
26007
+ }
26008
+ if (current) tokens.push(current);
26009
+ return tokens;
26010
+ }
26011
+ function extractColorFromShorthand(value) {
26012
+ const normalized = cleanStyleValue(value);
26013
+ if (!normalized) return null;
26014
+ const explicitColor = normalized.match(/#[\da-f]{3,8}\b|rgba?\([^)]+\)|hsla?\([^)]+\)/i);
26015
+ if (explicitColor) return explicitColor[0];
26016
+ const ignoredKeywords = /* @__PURE__ */ new Set([
26017
+ "border-box",
26018
+ "center",
26019
+ "contain",
26020
+ "content-box",
26021
+ "cover",
26022
+ "fixed",
26023
+ "inherit",
26024
+ "initial",
26025
+ "left",
26026
+ "local",
26027
+ "none",
26028
+ "no-repeat",
26029
+ "padding-box",
26030
+ "repeat",
26031
+ "repeat-x",
26032
+ "repeat-y",
26033
+ "right",
26034
+ "scroll",
26035
+ "top",
26036
+ "transparent",
26037
+ "unset"
26038
+ ]);
26039
+ return splitCssTokens(normalized).find((token) => !ignoredKeywords.has(token.toLowerCase())) ?? null;
26040
+ }
26041
+ function getBackgroundColor(styles) {
26042
+ return normalizeColorValue(styles.get("background-color")) ?? normalizeColorValue(extractColorFromShorthand(styles.get("background")));
26043
+ }
26044
+ var BORDER_STYLES = /* @__PURE__ */ new Set([
26045
+ "dashed",
26046
+ "dotted",
26047
+ "double",
26048
+ "groove",
26049
+ "hidden",
26050
+ "inset",
26051
+ "none",
26052
+ "outset",
26053
+ "ridge",
26054
+ "solid"
26055
+ ]);
26056
+ var BORDER_WIDTH_KEYWORDS = /* @__PURE__ */ new Set(["medium", "thick", "thin"]);
26057
+ function normalizeBorderStyle(value) {
26058
+ const normalized = cleanStyleValue(value);
26059
+ if (!normalized) return null;
26060
+ const styles = splitCssTokens(normalized).filter((token) => BORDER_STYLES.has(token.toLowerCase()));
26061
+ const usefulStyles = styles.filter((style) => !/^(?:hidden|none)$/i.test(style));
26062
+ return usefulStyles.length > 0 ? usefulStyles.join(" ") : null;
26063
+ }
26064
+ function normalizeBorderWidth(value) {
26065
+ const normalized = cleanStyleValue(value);
26066
+ if (!normalized) return null;
26067
+ const widths = splitCssTokens(normalized).filter((token) => {
26068
+ const lowerToken = token.toLowerCase();
26069
+ return BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token);
26070
+ });
26071
+ return widths.length > 0 ? widths.join(" ") : null;
26072
+ }
26073
+ function parseBorderShorthand(value) {
26074
+ const normalized = cleanStyleValue(value);
26075
+ if (!normalized) return null;
26076
+ const tokens = splitCssTokens(normalized);
26077
+ let borderStyle = null;
26078
+ let borderWidth = null;
26079
+ const colorTokens = [];
26080
+ for (const token of tokens) {
26081
+ const lowerToken = token.toLowerCase();
26082
+ if (!borderStyle && BORDER_STYLES.has(lowerToken)) {
26083
+ borderStyle = lowerToken;
26084
+ continue;
26085
+ }
26086
+ if (!borderWidth && (BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token))) {
26087
+ borderWidth = token;
26088
+ continue;
26089
+ }
26090
+ colorTokens.push(token);
26091
+ }
26092
+ if (borderStyle && /^(?:hidden|none)$/i.test(borderStyle)) return null;
26093
+ return {
26094
+ borderColor: normalizeColorValue(colorTokens.join(" ")),
26095
+ borderStyle,
26096
+ borderWidth
26097
+ };
26098
+ }
26099
+ function getFirstParsedBorder(styles) {
26100
+ for (const property of ["border", "border-top", "border-right", "border-bottom", "border-left"]) {
26101
+ const border = parseBorderShorthand(styles.get(property));
26102
+ if (border) return border;
26103
+ }
26104
+ return null;
26105
+ }
26106
+ function getBorderAttrs(styles) {
26107
+ const parsedBorder = getFirstParsedBorder(styles);
26108
+ return {
26109
+ borderColor: normalizeColorValue(styles.get("border-color")) ?? parsedBorder?.borderColor ?? void 0,
26110
+ borderStyle: normalizeBorderStyle(styles.get("border-style")) ?? parsedBorder?.borderStyle ?? void 0,
26111
+ borderWidth: normalizeBorderWidth(styles.get("border-width")) ?? parsedBorder?.borderWidth ?? void 0
26112
+ };
26113
+ }
26114
+ function parsePositiveInteger(value, max = 100) {
26115
+ if (!value) return null;
26116
+ const parsed = Number.parseInt(value, 10);
26117
+ if (!Number.isFinite(parsed) || parsed < 1) return null;
26118
+ return Math.min(parsed, max);
26119
+ }
26120
+ function parseCssSize(value) {
26121
+ const normalized = cleanStyleValue(value);
26122
+ if (!normalized) return null;
26123
+ const match = normalized.match(/^(\d+(?:\.\d+)?)(px|pt)?$/i);
26124
+ if (!match) return null;
26125
+ const amount = Number.parseFloat(match[1]);
26126
+ if (!Number.isFinite(amount) || amount <= 0) return null;
26127
+ return Math.round(match[2]?.toLowerCase() === "pt" ? amount * (4 / 3) : amount);
26128
+ }
26129
+ function getCellWidth(cell, styles, colspan) {
26130
+ if (colspan !== 1) return null;
26131
+ const width = parseCssSize(cell.getAttribute("data-colwidth") ?? cell.getAttribute("width") ?? styles.get("width"));
26132
+ return width ? [width] : null;
26133
+ }
26134
+ function getTableRowAttrs(row, styles) {
26135
+ const rowHeight = parseCssSize(
26136
+ row.getAttribute("data-row-height") ?? row.getAttribute("height") ?? styles.get("height")
26137
+ );
26138
+ return rowHeight ? { rowHeight } : void 0;
26139
+ }
26140
+ function getTableCellAttrs(cell, styles, defaultBackgroundColor) {
26141
+ const colspan = parsePositiveInteger(cell.getAttribute("colspan")) ?? 1;
26142
+ const rowspan = parsePositiveInteger(cell.getAttribute("rowspan")) ?? 1;
26143
+ const backgroundColor = getBackgroundColor(styles) ?? normalizeColorValue(cell.getAttribute("data-background-color")) ?? normalizeColorValue(cell.getAttribute("bgcolor")) ?? defaultBackgroundColor;
26144
+ const borderAttrs = getBorderAttrs(styles);
26145
+ const colwidth = getCellWidth(cell, styles, colspan);
26146
+ const attrs = {};
26147
+ if (backgroundColor) attrs.backgroundColor = backgroundColor;
26148
+ if (borderAttrs.borderColor) attrs.borderColor = borderAttrs.borderColor;
26149
+ if (borderAttrs.borderStyle) attrs.borderStyle = borderAttrs.borderStyle;
26150
+ if (borderAttrs.borderWidth) attrs.borderWidth = borderAttrs.borderWidth;
26151
+ if (colspan > 1) attrs.colspan = colspan;
26152
+ if (rowspan > 1) attrs.rowspan = rowspan;
26153
+ if (colwidth) attrs.colwidth = colwidth;
26154
+ return Object.keys(attrs).length > 0 ? attrs : void 0;
26155
+ }
26156
+ function marksEqual(left, right) {
26157
+ return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
26158
+ }
26159
+ function mergeMarks(base2, additions) {
26160
+ const next = [...base2 ?? []];
26161
+ for (const addition of additions ?? []) {
26162
+ const existingIndex = next.findIndex((mark) => mark.type === addition.type);
26163
+ if (existingIndex >= 0) {
26164
+ const existingMark = next[existingIndex];
26165
+ next[existingIndex] = {
26166
+ ...existingMark,
26167
+ attrs: {
26168
+ ...existingMark.attrs ?? {},
26169
+ ...addition.attrs ?? {}
26170
+ }
26171
+ };
26172
+ continue;
26173
+ }
26174
+ next.push(addition);
26175
+ }
26176
+ return next.length > 0 ? next : void 0;
26177
+ }
26178
+ function getMarkColor(marks, markType) {
26179
+ const mark = marks?.find((candidate) => candidate.type === markType);
26180
+ const color = mark?.attrs?.color;
26181
+ return typeof color === "string" ? color : null;
26182
+ }
26183
+ function replaceTextStyleColor(marks, color) {
26184
+ let replaced = false;
26185
+ const next = (marks ?? []).map((mark) => {
26186
+ if (mark.type !== "textStyle") return mark;
26187
+ replaced = true;
26188
+ return {
26189
+ ...mark,
26190
+ attrs: {
26191
+ ...mark.attrs ?? {},
26192
+ color
26193
+ }
26194
+ };
26195
+ });
26196
+ if (!replaced) {
26197
+ next.unshift({ type: "textStyle", attrs: { color } });
26198
+ }
26199
+ return next;
26200
+ }
26201
+ function ensureReadableSpreadsheetSegments(segments, cellBackgroundColor) {
26202
+ return segments.map((segment) => {
26203
+ const textColor = getMarkColor(segment.marks, "textStyle");
26204
+ if (!isLightTextColor(textColor)) return segment;
26205
+ const inlineBackgroundColor = getMarkColor(segment.marks, "highlight");
26206
+ if (isDarkReadableBackground(inlineBackgroundColor) || isDarkReadableBackground(cellBackgroundColor)) {
26207
+ return segment;
26208
+ }
26209
+ return {
26210
+ ...segment,
26211
+ marks: replaceTextStyleColor(segment.marks, DEFAULT_HTML_TABLE_TEXT_COLOR)
26212
+ };
26213
+ });
26214
+ }
26215
+ function getElementInlineMarks(element, styles) {
26216
+ const marks = [];
26217
+ const tagName = element.tagName;
26218
+ const color = normalizeTextColorValue(styles.get("color") ?? element.getAttribute("color"));
26219
+ const backgroundColor = getBackgroundColor(styles);
26220
+ const fontWeight = styles.get("font-weight")?.toLowerCase();
26221
+ const fontStyle = styles.get("font-style")?.toLowerCase();
26222
+ const textDecoration = styles.get("text-decoration")?.toLowerCase();
26223
+ if (color) {
26224
+ marks.push({ type: "textStyle", attrs: { color } });
26225
+ }
26226
+ if (backgroundColor && !isWhiteColor(backgroundColor)) {
26227
+ marks.push({ type: "highlight", attrs: { color: backgroundColor } });
26228
+ }
26229
+ if (tagName === "B" || tagName === "STRONG" || fontWeight === "bold" || /^\d+$/.test(fontWeight ?? "") && Number(fontWeight) >= 600) {
26230
+ marks.push({ type: "bold" });
26231
+ }
26232
+ if (tagName === "I" || tagName === "EM" || fontStyle === "italic") {
26233
+ marks.push({ type: "italic" });
26234
+ }
26235
+ if (tagName === "U" || textDecoration?.includes("underline")) {
26236
+ marks.push({ type: "underline" });
26237
+ }
26238
+ return marks.length > 0 ? marks : void 0;
26239
+ }
26240
+ function appendTextSegment(segments, segment) {
26241
+ if (!segment.text) return;
26242
+ const lastSegment = segments[segments.length - 1];
26243
+ if (lastSegment && marksEqual(lastSegment.marks, segment.marks)) {
26244
+ lastSegment.text += segment.text;
26245
+ return;
26246
+ }
26247
+ segments.push(segment);
26248
+ }
26249
+ function segmentsEndWithNewline(segments) {
26250
+ return segments.length > 0 && segments[segments.length - 1].text.endsWith("\n");
26251
+ }
26252
+ function normalizeClipboardTextSegments(segments) {
26253
+ const normalizedSegments = [];
26254
+ for (const segment of segments) {
26255
+ appendTextSegment(normalizedSegments, {
26256
+ text: segment.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " "),
26257
+ marks: segment.marks
26258
+ });
26259
+ }
26260
+ while (normalizedSegments.length > 0) {
26261
+ const firstSegment = normalizedSegments[0];
26262
+ firstSegment.text = firstSegment.text.replace(/^\s+/, "");
26263
+ if (firstSegment.text) break;
26264
+ normalizedSegments.shift();
26265
+ }
26266
+ while (normalizedSegments.length > 0) {
26267
+ const lastSegment = normalizedSegments[normalizedSegments.length - 1];
26268
+ lastSegment.text = lastSegment.text.replace(/\s+$/, "");
26269
+ if (lastSegment.text) break;
26270
+ normalizedSegments.pop();
26271
+ }
26272
+ return normalizedSegments;
26273
+ }
25859
26274
  function getClipboardCellText(node) {
25860
26275
  if (node.nodeType === Node.TEXT_NODE) {
25861
26276
  return node.textContent ?? "";
@@ -25873,57 +26288,205 @@ function getClipboardCellText(node) {
25873
26288
  }
25874
26289
  return childText;
25875
26290
  }
25876
- function getHtmlTableRows(table) {
26291
+ function getClipboardCellSegments(node, styleMap, inheritedMarks) {
26292
+ if (node.nodeType === Node.TEXT_NODE) {
26293
+ return [{ text: node.textContent ?? "", marks: inheritedMarks }];
26294
+ }
26295
+ if (!(node instanceof HTMLElement)) {
26296
+ return [];
26297
+ }
26298
+ if (node.tagName === "BR") {
26299
+ return [{ text: "\n", marks: inheritedMarks }];
26300
+ }
26301
+ const styles = getElementStyleDeclarations(node, styleMap);
26302
+ const marks = mergeMarks(inheritedMarks, getElementInlineMarks(node, styles));
26303
+ const segments = [];
26304
+ for (const childNode of Array.from(node.childNodes)) {
26305
+ for (const segment of getClipboardCellSegments(childNode, styleMap, marks)) {
26306
+ appendTextSegment(segments, segment);
26307
+ }
26308
+ }
26309
+ if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && segments.length > 0 && !segmentsEndWithNewline(segments)) {
26310
+ appendTextSegment(segments, { text: "\n" });
26311
+ }
26312
+ return segments;
26313
+ }
26314
+ function getClipboardCellChildSegments(cell, styleMap, inheritedMarks) {
26315
+ const segments = [];
26316
+ for (const childNode of Array.from(cell.childNodes)) {
26317
+ for (const segment of getClipboardCellSegments(childNode, styleMap, inheritedMarks)) {
26318
+ appendTextSegment(segments, segment);
26319
+ }
26320
+ }
26321
+ return normalizeClipboardTextSegments(segments);
26322
+ }
26323
+ function getHtmlTableRows(table, styleMap) {
25877
26324
  const rows = Array.from(table.querySelectorAll("tr")).map(
25878
- (row) => Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement).map((cell) => ({
25879
- text: normalizeClipboardCellText(getClipboardCellText(cell)),
25880
- isHeader: cell.tagName === "TH"
25881
- }))
26325
+ (row) => ({
26326
+ attrs: getTableRowAttrs(row, getElementStyleDeclarations(row, styleMap)),
26327
+ cells: Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement).map((cell) => {
26328
+ const styles = getElementStyleDeclarations(cell, styleMap);
26329
+ const textColor = normalizeTextColorValue(styles.get("color")) ?? DEFAULT_HTML_TABLE_TEXT_COLOR;
26330
+ const inheritedMarks = [{ type: "textStyle", attrs: { color: textColor } }];
26331
+ const attrs = getTableCellAttrs(cell, styles, DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR);
26332
+ const segments = ensureReadableSpreadsheetSegments(
26333
+ getClipboardCellChildSegments(cell, styleMap, inheritedMarks),
26334
+ attrs?.backgroundColor
26335
+ );
26336
+ return {
26337
+ text: normalizeClipboardCellText(getClipboardCellText(cell)),
26338
+ isHeader: cell.tagName === "TH",
26339
+ attrs,
26340
+ segments: segments.length > 0 ? segments : void 0,
26341
+ textColor
26342
+ };
26343
+ })
26344
+ })
25882
26345
  );
25883
- return rows.filter((row) => row.length > 0);
26346
+ return rows.filter((row) => row.cells.length > 0);
26347
+ }
26348
+ function createTextMarks(cell) {
26349
+ return cell.textColor ? [{ type: "textStyle", attrs: { color: cell.textColor } }] : void 0;
25884
26350
  }
25885
- function createParagraphContent(text) {
26351
+ function createParagraphContent(text, marks) {
25886
26352
  return text ? {
25887
26353
  type: "paragraph",
25888
- content: [{ type: "text", text }]
26354
+ content: [{ type: "text", text, ...marks ? { marks } : {} }]
25889
26355
  } : { type: "paragraph" };
25890
26356
  }
26357
+ function createParagraphContentFromSegments(segments) {
26358
+ const paragraphs = [[]];
26359
+ for (const segment of segments) {
26360
+ const parts = segment.text.split("\n");
26361
+ parts.forEach((part, index) => {
26362
+ if (part) {
26363
+ paragraphs[paragraphs.length - 1].push({ text: part, marks: segment.marks });
26364
+ }
26365
+ if (index < parts.length - 1) {
26366
+ paragraphs.push([]);
26367
+ }
26368
+ });
26369
+ }
26370
+ return paragraphs.map((paragraphSegments) => {
26371
+ const content = paragraphSegments.map((segment) => ({
26372
+ type: "text",
26373
+ text: segment.text,
26374
+ ...segment.marks ? { marks: segment.marks } : {}
26375
+ }));
26376
+ return content.length > 0 ? { type: "paragraph", content } : { type: "paragraph" };
26377
+ });
26378
+ }
25891
26379
  function createTableCellContent(cell) {
25892
26380
  const lines = cell.text.split("\n");
25893
- const paragraphs = (lines.length > 0 ? lines : [""]).map(createParagraphContent);
26381
+ const marks = createTextMarks(cell);
26382
+ const paragraphs = cell.segments && cell.segments.length > 0 ? createParagraphContentFromSegments(cell.segments) : (lines.length > 0 ? lines : [""]).map((line) => createParagraphContent(line, marks));
25894
26383
  return {
25895
26384
  type: cell.isHeader ? "tableHeader" : "tableCell",
26385
+ ...cell.attrs ? { attrs: cell.attrs } : {},
25896
26386
  content: paragraphs.length > 0 ? paragraphs : [{ type: "paragraph" }]
25897
26387
  };
25898
26388
  }
25899
- function createTableContent(rows, minColumnCount = 1) {
25900
- const tableRows = rows.filter((row) => row.length > 0);
26389
+ function getRowspanLimitedCell(cell, remainingRowCount) {
26390
+ const attrs = cell.attrs;
26391
+ if (!attrs?.rowspan || attrs.rowspan <= remainingRowCount) return cell;
26392
+ if (remainingRowCount <= 1) {
26393
+ const { rowspan: _rowspan, ...nextAttrs } = attrs;
26394
+ return {
26395
+ ...cell,
26396
+ attrs: Object.keys(nextAttrs).length > 0 ? nextAttrs : void 0
26397
+ };
26398
+ }
26399
+ return {
26400
+ ...cell,
26401
+ attrs: {
26402
+ ...attrs,
26403
+ rowspan: remainingRowCount
26404
+ }
26405
+ };
26406
+ }
26407
+ function normalizeTableRows(rows) {
26408
+ const positionedRows = [];
26409
+ let rowspans = [];
26410
+ let columnCount = 0;
26411
+ rows.forEach((row, rowIndex) => {
26412
+ const coveredColumns = rowspans.map((span) => span > 0);
26413
+ const nextRowspans = rowspans.map((span) => Math.max(0, span - 1));
26414
+ const positionedCells = [];
26415
+ let columnIndex = 0;
26416
+ for (const rawCell of row.cells) {
26417
+ while (coveredColumns[columnIndex]) columnIndex += 1;
26418
+ const remainingRowCount = rows.length - rowIndex;
26419
+ const cell = getRowspanLimitedCell(rawCell, remainingRowCount);
26420
+ const colspan = Math.max(1, cell.attrs?.colspan ?? 1);
26421
+ const rowspan = Math.max(1, cell.attrs?.rowspan ?? 1);
26422
+ positionedCells.push({ startColumn: columnIndex, colspan, cell });
26423
+ if (rowspan > 1) {
26424
+ for (let offset = 0; offset < colspan; offset += 1) {
26425
+ const spannedColumn = columnIndex + offset;
26426
+ nextRowspans[spannedColumn] = Math.max(nextRowspans[spannedColumn] ?? 0, rowspan - 1);
26427
+ }
26428
+ }
26429
+ columnIndex += colspan;
26430
+ }
26431
+ const lastCoveredColumn = coveredColumns.reduce((lastIndex, covered, index) => covered ? index : lastIndex, -1);
26432
+ const lastFutureRowspanColumn = nextRowspans.reduce((lastIndex, span, index) => span > 0 ? index : lastIndex, -1);
26433
+ columnCount = Math.max(columnCount, columnIndex, lastCoveredColumn + 1, lastFutureRowspanColumn + 1);
26434
+ positionedRows.push({
26435
+ attrs: row.attrs,
26436
+ cells: positionedCells,
26437
+ coveredColumns
26438
+ });
26439
+ rowspans = nextRowspans;
26440
+ });
26441
+ return { positionedRows, columnCount };
26442
+ }
26443
+ function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
26444
+ const content = [];
26445
+ const cellByStartColumn = new Map(row.cells.map((cell) => [cell.startColumn, cell]));
26446
+ let columnIndex = 0;
26447
+ while (columnIndex < columnCount) {
26448
+ if (row.coveredColumns[columnIndex]) {
26449
+ columnIndex += 1;
26450
+ continue;
26451
+ }
26452
+ const positionedCell = cellByStartColumn.get(columnIndex);
26453
+ if (positionedCell) {
26454
+ content.push(createTableCellContent(positionedCell.cell));
26455
+ columnIndex += positionedCell.colspan;
26456
+ continue;
26457
+ }
26458
+ content.push(createTableCellContent({ text: "", isHeader: false, attrs: fillerCellAttrs }));
26459
+ columnIndex += 1;
26460
+ }
26461
+ return content;
26462
+ }
26463
+ function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
26464
+ const tableRows = rows.filter((row) => row.cells.length > 0);
25901
26465
  if (tableRows.length === 0) return null;
25902
- const columnCount = tableRows.reduce((max, row) => Math.max(max, row.length), 0);
26466
+ const { positionedRows, columnCount } = normalizeTableRows(tableRows);
25903
26467
  if (columnCount < minColumnCount) return null;
25904
26468
  return {
25905
26469
  type: "table",
25906
- content: tableRows.map((row) => {
25907
- const normalizedRow = Array.from(
25908
- { length: columnCount },
25909
- (_, index) => row[index] ?? { text: "", isHeader: false }
25910
- );
25911
- return {
25912
- type: "tableRow",
25913
- content: normalizedRow.map(createTableCellContent)
25914
- };
25915
- })
26470
+ content: positionedRows.map((row) => ({
26471
+ type: "tableRow",
26472
+ ...row.attrs ? { attrs: row.attrs } : {},
26473
+ content: createNormalizedRowContent(row, columnCount, fillerCellAttrs)
26474
+ }))
25916
26475
  };
25917
26476
  }
25918
26477
  function getClipboardTableContent(dataTransfer) {
25919
26478
  const html = getClipboardData(dataTransfer, "text/html");
25920
26479
  if (!/<table(?:\s|>)/i.test(html)) return null;
25921
26480
  if (typeof DOMParser === "undefined") return null;
26481
+ const doc = new DOMParser().parseFromString(html, "text/html");
25922
26482
  const fragment = extractClipboardHtmlFragment(html);
25923
- const doc = new DOMParser().parseFromString(fragment, "text/html");
25924
- const table = doc.querySelector("table");
26483
+ const fragmentDoc = new DOMParser().parseFromString(fragment, "text/html");
26484
+ const styleMap = parseClipboardCssClassStyles(doc);
26485
+ const table = fragmentDoc.querySelector("table") ?? doc.querySelector("table");
25925
26486
  if (!(table instanceof HTMLTableElement)) return null;
25926
- return createTableContent(getHtmlTableRows(table));
26487
+ return createTableContent(getHtmlTableRows(table, styleMap), 1, {
26488
+ backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
26489
+ });
25927
26490
  }
25928
26491
  function parseClipboardTsvRows(text) {
25929
26492
  const rows = [];
@@ -25980,7 +26543,9 @@ function getClipboardTsvTableContent(dataTransfer) {
25980
26543
  if (!text.includes(" ")) return null;
25981
26544
  const rows = parseClipboardTsvRows(text);
25982
26545
  return createTableContent(
25983
- rows.map((row) => row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))),
26546
+ rows.map((row) => ({
26547
+ cells: row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))
26548
+ })),
25984
26549
  2
25985
26550
  );
25986
26551
  }
@@ -27255,6 +27820,46 @@ var CustomTableCell = import_extension_table_cell.default.extend({
27255
27820
  "data-border-width": attributes.borderWidth
27256
27821
  };
27257
27822
  }
27823
+ },
27824
+ cellId: {
27825
+ default: null,
27826
+ parseHTML: (element) => element.getAttribute("data-cell-id") || null,
27827
+ renderHTML: (attributes) => {
27828
+ if (!attributes.cellId) return {};
27829
+ return {
27830
+ "data-cell-id": attributes.cellId
27831
+ };
27832
+ }
27833
+ },
27834
+ numberFormat: {
27835
+ default: null,
27836
+ parseHTML: (element) => element.getAttribute("data-number-format") || null,
27837
+ renderHTML: (attributes) => {
27838
+ if (!attributes.numberFormat) return {};
27839
+ return {
27840
+ "data-number-format": attributes.numberFormat
27841
+ };
27842
+ }
27843
+ },
27844
+ formula: {
27845
+ default: null,
27846
+ parseHTML: (element) => element.getAttribute("data-formula") || null,
27847
+ renderHTML: (attributes) => {
27848
+ if (!attributes.formula) return {};
27849
+ return {
27850
+ "data-formula": attributes.formula
27851
+ };
27852
+ }
27853
+ },
27854
+ computedValue: {
27855
+ default: null,
27856
+ parseHTML: (element) => element.getAttribute("data-computed-value") || null,
27857
+ renderHTML: (attributes) => {
27858
+ if (!attributes.computedValue) return {};
27859
+ return {
27860
+ "data-computed-value": attributes.computedValue
27861
+ };
27862
+ }
27258
27863
  }
27259
27864
  };
27260
27865
  },
@@ -27322,6 +27927,46 @@ var CustomTableHeader = import_extension_table_header.default.extend({
27322
27927
  "data-border-width": attributes.borderWidth
27323
27928
  };
27324
27929
  }
27930
+ },
27931
+ cellId: {
27932
+ default: null,
27933
+ parseHTML: (element) => element.getAttribute("data-cell-id") || null,
27934
+ renderHTML: (attributes) => {
27935
+ if (!attributes.cellId) return {};
27936
+ return {
27937
+ "data-cell-id": attributes.cellId
27938
+ };
27939
+ }
27940
+ },
27941
+ numberFormat: {
27942
+ default: null,
27943
+ parseHTML: (element) => element.getAttribute("data-number-format") || null,
27944
+ renderHTML: (attributes) => {
27945
+ if (!attributes.numberFormat) return {};
27946
+ return {
27947
+ "data-number-format": attributes.numberFormat
27948
+ };
27949
+ }
27950
+ },
27951
+ formula: {
27952
+ default: null,
27953
+ parseHTML: (element) => element.getAttribute("data-formula") || null,
27954
+ renderHTML: (attributes) => {
27955
+ if (!attributes.formula) return {};
27956
+ return {
27957
+ "data-formula": attributes.formula
27958
+ };
27959
+ }
27960
+ },
27961
+ computedValue: {
27962
+ default: null,
27963
+ parseHTML: (element) => element.getAttribute("data-computed-value") || null,
27964
+ renderHTML: (attributes) => {
27965
+ if (!attributes.computedValue) return {};
27966
+ return {
27967
+ "data-computed-value": attributes.computedValue
27968
+ };
27969
+ }
27325
27970
  }
27326
27971
  };
27327
27972
  },
@@ -28034,6 +28679,34 @@ function collectChildren(node) {
28034
28679
  function createEmptyCellNode(cellNode) {
28035
28680
  return cellNode.type.createAndFill(cellNode.attrs) ?? cellNode;
28036
28681
  }
28682
+ function createCellCopyForColumnDuplicate(cellNode) {
28683
+ return cellNode.type.create(cellNode.attrs, cellNode.content);
28684
+ }
28685
+ function getTableRows(tableNode) {
28686
+ const rows = [];
28687
+ tableNode.forEach((rowNode, rowOffset) => {
28688
+ const cells = [];
28689
+ rowNode.forEach((cellNode, cellOffset, index) => {
28690
+ cells.push({
28691
+ index,
28692
+ node: cellNode,
28693
+ relativePos: rowOffset + 1 + cellOffset
28694
+ });
28695
+ });
28696
+ rows.push({
28697
+ node: rowNode,
28698
+ cells
28699
+ });
28700
+ });
28701
+ return rows;
28702
+ }
28703
+ function safeFindCell(map, relativePos) {
28704
+ try {
28705
+ return map.findCell(relativePos);
28706
+ } catch {
28707
+ return null;
28708
+ }
28709
+ }
28037
28710
  function getSelectedTableRect(editor) {
28038
28711
  const cellSelection = getCellSelectionPositions(editor.state.selection);
28039
28712
  if (cellSelection) {
@@ -28159,34 +28832,49 @@ function duplicateTableRowAt(editor, rowIndex, cellPos) {
28159
28832
  }
28160
28833
  function clearTableRowAt(editor, rowIndex, cellPos) {
28161
28834
  return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
28162
- const rows = collectChildren(tableNode);
28163
- const rowNode = rows[rowIndex];
28164
- if (!rowNode) return null;
28165
- const cells = collectChildren(rowNode).map((cellNode) => createEmptyCellNode(cellNode));
28166
- rows[rowIndex] = rowNode.type.create(rowNode.attrs, cells);
28835
+ const map = import_tables.TableMap.get(tableNode);
28836
+ if (rowIndex < 0 || rowIndex >= map.height) return null;
28837
+ const rows = getTableRows(tableNode).map((rowInfo) => {
28838
+ const cells = collectChildren(rowInfo.node);
28839
+ for (const entry of rowInfo.cells) {
28840
+ const rect = safeFindCell(map, entry.relativePos);
28841
+ if (!rect || rect.top > rowIndex || rowIndex >= rect.bottom) continue;
28842
+ cells[entry.index] = createEmptyCellNode(entry.node);
28843
+ }
28844
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
28845
+ });
28167
28846
  return tableNode.type.create(tableNode.attrs, rows);
28168
28847
  });
28169
28848
  }
28170
28849
  function duplicateTableColumnAt(editor, columnIndex, cellPos) {
28171
28850
  return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
28172
- const rows = collectChildren(tableNode).map((rowNode) => {
28173
- const cells = collectChildren(rowNode);
28174
- const cellNode = cells[columnIndex];
28175
- if (!cellNode) return rowNode;
28176
- cells.splice(columnIndex + 1, 0, cellNode.copy(cellNode.content));
28177
- return rowNode.type.create(rowNode.attrs, cells);
28851
+ const map = import_tables.TableMap.get(tableNode);
28852
+ if (columnIndex < 0 || columnIndex >= map.width) return null;
28853
+ const rows = getTableRows(tableNode).map((rowInfo, rowIndex) => {
28854
+ const cells = collectChildren(rowInfo.node);
28855
+ const sourceCell = rowInfo.cells.find((entry) => {
28856
+ const rect = safeFindCell(map, entry.relativePos);
28857
+ return rect && rect.top === rowIndex && rect.left <= columnIndex && columnIndex < rect.right;
28858
+ });
28859
+ if (!sourceCell) return rowInfo.node;
28860
+ cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
28861
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
28178
28862
  });
28179
28863
  return tableNode.type.create(tableNode.attrs, rows);
28180
28864
  });
28181
28865
  }
28182
28866
  function clearTableColumnAt(editor, columnIndex, cellPos) {
28183
28867
  return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
28184
- const rows = collectChildren(tableNode).map((rowNode) => {
28185
- const cells = collectChildren(rowNode);
28186
- const cellNode = cells[columnIndex];
28187
- if (!cellNode) return rowNode;
28188
- cells[columnIndex] = createEmptyCellNode(cellNode);
28189
- return rowNode.type.create(rowNode.attrs, cells);
28868
+ const map = import_tables.TableMap.get(tableNode);
28869
+ if (columnIndex < 0 || columnIndex >= map.width) return null;
28870
+ const rows = getTableRows(tableNode).map((rowInfo) => {
28871
+ const cells = collectChildren(rowInfo.node);
28872
+ for (const entry of rowInfo.cells) {
28873
+ const rect = safeFindCell(map, entry.relativePos);
28874
+ if (!rect || rect.left > columnIndex || columnIndex >= rect.right) continue;
28875
+ cells[entry.index] = createEmptyCellNode(entry.node);
28876
+ }
28877
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
28190
28878
  });
28191
28879
  return tableNode.type.create(tableNode.attrs, rows);
28192
28880
  });
@@ -29238,14 +29926,410 @@ var EditorToolbar = ({
29238
29926
  // src/components/UEditor/menus.tsx
29239
29927
  var import_react62 = require("react");
29240
29928
  var import_react63 = require("@tiptap/react");
29241
- var import_tables2 = require("@tiptap/pm/tables");
29929
+ var import_tables3 = require("@tiptap/pm/tables");
29242
29930
  var import_react_dom8 = require("react-dom");
29243
29931
  var import_lucide_react49 = require("lucide-react");
29932
+
29933
+ // src/components/UEditor/table-formula-commands.ts
29934
+ var import_tables2 = require("@tiptap/pm/tables");
29935
+
29936
+ // src/components/UEditor/table-formula.ts
29937
+ var CELL_ADDRESS_RE = /^([A-Z]+)([1-9]\d*)$/i;
29938
+ var CELL_RANGE_RE = /^([A-Z]+[1-9]\d*):([A-Z]+[1-9]\d*)$/i;
29939
+ var SUPPORTED_FUNCTIONS = /* @__PURE__ */ new Set(["SUM", "AVG", "MIN", "MAX", "COUNT"]);
29940
+ function columnNameToIndex(columnName) {
29941
+ const normalized = columnName.trim().toUpperCase();
29942
+ if (!/^[A-Z]+$/.test(normalized)) {
29943
+ return -1;
29944
+ }
29945
+ let index = 0;
29946
+ for (const char of normalized) {
29947
+ index = index * 26 + char.charCodeAt(0) - 64;
29948
+ }
29949
+ return index - 1;
29950
+ }
29951
+ function indexToColumnName(index) {
29952
+ if (!Number.isInteger(index) || index < 0) {
29953
+ return "";
29954
+ }
29955
+ let value = index + 1;
29956
+ let name = "";
29957
+ while (value > 0) {
29958
+ const remainder = (value - 1) % 26;
29959
+ name = String.fromCharCode(65 + remainder) + name;
29960
+ value = Math.floor((value - 1) / 26);
29961
+ }
29962
+ return name;
29963
+ }
29964
+ function parseTableCellAddress(input) {
29965
+ const match = input.trim().match(CELL_ADDRESS_RE);
29966
+ if (!match) {
29967
+ return null;
29968
+ }
29969
+ const column = columnNameToIndex(match[1] ?? "");
29970
+ const row = Number.parseInt(match[2] ?? "", 10) - 1;
29971
+ if (column < 0 || row < 0) {
29972
+ return null;
29973
+ }
29974
+ return {
29975
+ column,
29976
+ row,
29977
+ label: `${indexToColumnName(column)}${row + 1}`
29978
+ };
29979
+ }
29980
+ function parseTableCellRange(input) {
29981
+ const match = input.trim().match(CELL_RANGE_RE);
29982
+ if (!match) {
29983
+ return null;
29984
+ }
29985
+ const from = parseTableCellAddress(match[1] ?? "");
29986
+ const to = parseTableCellAddress(match[2] ?? "");
29987
+ if (!from || !to) {
29988
+ return null;
29989
+ }
29990
+ return { from, to };
29991
+ }
29992
+ function getTableCellRangeLabels(range) {
29993
+ const startColumn = Math.min(range.from.column, range.to.column);
29994
+ const endColumn = Math.max(range.from.column, range.to.column);
29995
+ const startRow = Math.min(range.from.row, range.to.row);
29996
+ const endRow = Math.max(range.from.row, range.to.row);
29997
+ const labels = [];
29998
+ for (let row = startRow; row <= endRow; row += 1) {
29999
+ for (let column = startColumn; column <= endColumn; column += 1) {
30000
+ labels.push(`${indexToColumnName(column)}${row + 1}`);
30001
+ }
30002
+ }
30003
+ return labels;
30004
+ }
30005
+ function normalizeTableFormula(formula) {
30006
+ return formula.trim().replace(/^=/, "").trim();
30007
+ }
30008
+ function evaluateBasicTableFormula(formula, getCellValue) {
30009
+ const normalized = normalizeTableFormula(formula);
30010
+ if (!normalized) {
30011
+ return { value: null, error: "empty" };
30012
+ }
30013
+ const tokens = tokenizeFormula(normalized);
30014
+ if (!tokens) {
30015
+ return { value: null, error: "invalid-formula" };
30016
+ }
30017
+ const parser = new FormulaParser(tokens, getCellValue);
30018
+ const result = parser.parseExpression();
30019
+ if (result.error) {
30020
+ return result;
30021
+ }
30022
+ if (!parser.isComplete()) {
30023
+ return { value: null, error: "invalid-formula" };
30024
+ }
30025
+ return result;
30026
+ }
30027
+ function tokenizeFormula(formula) {
30028
+ const tokens = [];
30029
+ let index = 0;
30030
+ while (index < formula.length) {
30031
+ const char = formula[index];
30032
+ if (!char) break;
30033
+ if (/\s/.test(char)) {
30034
+ index += 1;
30035
+ continue;
30036
+ }
30037
+ if (char === "," || char === "+" || char === "-" || char === "*" || char === "/" || char === "(" || char === ")") {
30038
+ if (char === ",") tokens.push({ type: "comma", value: char });
30039
+ else if (char === "(" || char === ")") tokens.push({ type: "paren", value: char });
30040
+ else tokens.push({ type: "operator", value: char });
30041
+ index += 1;
30042
+ continue;
30043
+ }
30044
+ const numberMatch = formula.slice(index).match(/^\d+(?:\.\d+)?/);
30045
+ if (numberMatch?.[0]) {
30046
+ tokens.push({ type: "number", value: Number.parseFloat(numberMatch[0]) });
30047
+ index += numberMatch[0].length;
30048
+ continue;
30049
+ }
30050
+ const identifierMatch = formula.slice(index).match(/^[A-Z]+[1-9]\d*(?::[A-Z]+[1-9]\d*)?|^[A-Z]+/i);
30051
+ if (identifierMatch?.[0]) {
30052
+ const value = identifierMatch[0].toUpperCase();
30053
+ if (CELL_RANGE_RE.test(value)) tokens.push({ type: "range", value });
30054
+ else if (parseTableCellAddress(value)) tokens.push({ type: "cell", value });
30055
+ else if (SUPPORTED_FUNCTIONS.has(value)) tokens.push({ type: "function", value });
30056
+ else return null;
30057
+ index += identifierMatch[0].length;
30058
+ continue;
30059
+ }
30060
+ return null;
30061
+ }
30062
+ return tokens;
30063
+ }
30064
+ var FormulaParser = class {
30065
+ constructor(tokens, getCellValue) {
30066
+ this.tokens = tokens;
30067
+ this.getCellValue = getCellValue;
30068
+ this.index = 0;
30069
+ }
30070
+ isComplete() {
30071
+ return this.index >= this.tokens.length;
30072
+ }
30073
+ parseExpression() {
30074
+ let left = this.parseTerm();
30075
+ while (!left.error) {
30076
+ const operator = this.peekOperator(["+", "-"]);
30077
+ if (!operator) break;
30078
+ this.index += 1;
30079
+ const right = this.parseTerm();
30080
+ if (right.error) return right;
30081
+ left = {
30082
+ value: operator.value === "+" ? left.value + right.value : left.value - right.value,
30083
+ error: null
30084
+ };
30085
+ }
30086
+ return left;
30087
+ }
30088
+ parseTerm() {
30089
+ let left = this.parseFactor();
30090
+ while (!left.error) {
30091
+ const operator = this.peekOperator(["*", "/"]);
30092
+ if (!operator) break;
30093
+ this.index += 1;
30094
+ const right = this.parseFactor();
30095
+ if (right.error) return right;
30096
+ if (operator.value === "/" && right.value === 0) {
30097
+ return { value: null, error: "division-by-zero" };
30098
+ }
30099
+ left = {
30100
+ value: operator.value === "*" ? left.value * right.value : left.value / right.value,
30101
+ error: null
30102
+ };
30103
+ }
30104
+ return left;
30105
+ }
30106
+ parseFactor() {
30107
+ const token = this.tokens[this.index];
30108
+ if (!token) {
30109
+ return { value: null, error: "invalid-formula" };
30110
+ }
30111
+ if (token.type === "operator" && token.value === "-") {
30112
+ this.index += 1;
30113
+ const value = this.parseFactor();
30114
+ if (value.error) return value;
30115
+ return { value: -value.value, error: null };
30116
+ }
30117
+ if (token.type === "number") {
30118
+ this.index += 1;
30119
+ return { value: token.value, error: null };
30120
+ }
30121
+ if (token.type === "cell") {
30122
+ this.index += 1;
30123
+ return this.readCellNumber(token.value);
30124
+ }
30125
+ if (token.type === "function") {
30126
+ return this.parseFunction(token.value);
30127
+ }
30128
+ if (token.type === "paren" && token.value === "(") {
30129
+ this.index += 1;
30130
+ const value = this.parseExpression();
30131
+ if (value.error) return value;
30132
+ if (!this.consumeParen(")")) {
30133
+ return { value: null, error: "invalid-formula" };
30134
+ }
30135
+ return value;
30136
+ }
30137
+ return { value: null, error: "invalid-formula" };
30138
+ }
30139
+ parseFunction(name) {
30140
+ this.index += 1;
30141
+ if (!this.consumeParen("(")) {
30142
+ return { value: null, error: "invalid-formula" };
30143
+ }
30144
+ const values = [];
30145
+ while (true) {
30146
+ const token = this.tokens[this.index];
30147
+ if (!token) {
30148
+ return { value: null, error: "invalid-formula" };
30149
+ }
30150
+ if (token.type === "range") {
30151
+ this.index += 1;
30152
+ const range = parseTableCellRange(token.value);
30153
+ if (!range) return { value: null, error: "invalid-reference" };
30154
+ for (const label of getTableCellRangeLabels(range)) {
30155
+ const cellValue = this.readCellNumber(label);
30156
+ if (cellValue.error) return cellValue;
30157
+ values.push(cellValue.value);
30158
+ }
30159
+ } else {
30160
+ const value = this.parseExpression();
30161
+ if (value.error) return value;
30162
+ values.push(value.value);
30163
+ }
30164
+ if (this.consumeComma()) {
30165
+ continue;
30166
+ }
30167
+ if (this.consumeParen(")")) {
30168
+ break;
30169
+ }
30170
+ return { value: null, error: "invalid-formula" };
30171
+ }
30172
+ if (values.length === 0) {
30173
+ return { value: null, error: "invalid-formula" };
30174
+ }
30175
+ if (name === "SUM") return { value: values.reduce((sum, value) => sum + value, 0), error: null };
30176
+ if (name === "AVG") return { value: values.reduce((sum, value) => sum + value, 0) / values.length, error: null };
30177
+ if (name === "MIN") return { value: Math.min(...values), error: null };
30178
+ if (name === "MAX") return { value: Math.max(...values), error: null };
30179
+ if (name === "COUNT") return { value: values.length, error: null };
30180
+ return { value: null, error: "invalid-formula" };
30181
+ }
30182
+ readCellNumber(label) {
30183
+ const value = this.getCellValue(label);
30184
+ const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
30185
+ if (!Number.isFinite(parsed)) {
30186
+ return { value: null, error: "invalid-reference" };
30187
+ }
30188
+ return { value: parsed, error: null };
30189
+ }
30190
+ peekOperator(operators) {
30191
+ const token = this.tokens[this.index];
30192
+ return token?.type === "operator" && operators.includes(token.value) ? token : null;
30193
+ }
30194
+ consumeComma() {
30195
+ if (this.tokens[this.index]?.type !== "comma") {
30196
+ return false;
30197
+ }
30198
+ this.index += 1;
30199
+ return true;
30200
+ }
30201
+ consumeParen(value) {
30202
+ const token = this.tokens[this.index];
30203
+ if (token?.type !== "paren" || token.value !== value) {
30204
+ return false;
30205
+ }
30206
+ this.index += 1;
30207
+ return true;
30208
+ }
30209
+ };
30210
+
30211
+ // src/components/UEditor/table-formula-commands.ts
30212
+ function collectChildren2(node) {
30213
+ const children = [];
30214
+ node.forEach((child) => children.push(child));
30215
+ return children;
30216
+ }
30217
+ function getTableRows2(tableNode) {
30218
+ const rows = [];
30219
+ tableNode.forEach((rowNode, rowOffset) => {
30220
+ const cells = [];
30221
+ rowNode.forEach((cellNode, cellOffset, index) => {
30222
+ cells.push({
30223
+ index,
30224
+ node: cellNode,
30225
+ relativePos: rowOffset + 1 + cellOffset
30226
+ });
30227
+ });
30228
+ rows.push({ node: rowNode, cells });
30229
+ });
30230
+ return rows;
30231
+ }
30232
+ function safeFindCell2(map, relativePos) {
30233
+ try {
30234
+ return map.findCell(relativePos);
30235
+ } catch {
30236
+ return null;
30237
+ }
30238
+ }
30239
+ function getCellText(cellNode) {
30240
+ return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
30241
+ }
30242
+ function buildTableValueGetter(tableNode) {
30243
+ const map = import_tables2.TableMap.get(tableNode);
30244
+ const values = /* @__PURE__ */ new Map();
30245
+ for (const rowInfo of getTableRows2(tableNode)) {
30246
+ for (const entry of rowInfo.cells) {
30247
+ const rect = safeFindCell2(map, entry.relativePos);
30248
+ if (!rect) continue;
30249
+ const label = `${indexToColumnName(rect.left)}${rect.top + 1}`;
30250
+ const computedValue = entry.node.attrs.computedValue;
30251
+ values.set(label, typeof computedValue === "string" && computedValue.trim() ? computedValue : getCellText(entry.node));
30252
+ }
30253
+ }
30254
+ return (label) => values.get(label.toUpperCase());
30255
+ }
30256
+ function getFormulaComputedValue(formula, tableNode) {
30257
+ const result = evaluateBasicTableFormula(formula, buildTableValueGetter(tableNode));
30258
+ return result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30259
+ }
30260
+ function normalizeFormulaInput(formula) {
30261
+ const trimmed = formula.trim();
30262
+ if (!trimmed) return "";
30263
+ return trimmed.startsWith("=") ? trimmed : `=${trimmed}`;
30264
+ }
30265
+ function setSelectedTableCellFormula(editor, formula) {
30266
+ const normalized = normalizeFormulaInput(formula);
30267
+ const { state, view } = editor;
30268
+ if (!normalized) {
30269
+ const clearedFormula = (0, import_tables2.setCellAttr)("formula", null)(state, view.dispatch.bind(view));
30270
+ const clearedValue = (0, import_tables2.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
30271
+ if (clearedFormula || clearedValue) {
30272
+ view.focus();
30273
+ dispatchTableLayoutChange(editor);
30274
+ return true;
30275
+ }
30276
+ return false;
30277
+ }
30278
+ const rect = (0, import_tables2.selectedRect)(state);
30279
+ const computedValue = getFormulaComputedValue(normalized, rect.table);
30280
+ const appliedFormula = (0, import_tables2.setCellAttr)("formula", normalized)(state, view.dispatch.bind(view));
30281
+ const appliedValue = (0, import_tables2.setCellAttr)("computedValue", computedValue)(editor.state, view.dispatch.bind(view));
30282
+ if (appliedFormula || appliedValue) {
30283
+ view.focus();
30284
+ dispatchTableLayoutChange(editor);
30285
+ return true;
30286
+ }
30287
+ return false;
30288
+ }
30289
+ function clearSelectedTableCellFormula(editor) {
30290
+ return setSelectedTableCellFormula(editor, "");
30291
+ }
30292
+ function recalculateSelectedTable(editor) {
30293
+ const rect = (0, import_tables2.selectedRect)(editor.state);
30294
+ const tableNode = rect.table;
30295
+ const map = import_tables2.TableMap.get(tableNode);
30296
+ const getCellValue = buildTableValueGetter(tableNode);
30297
+ let changed = false;
30298
+ const rows = getTableRows2(tableNode).map((rowInfo) => {
30299
+ const cells = collectChildren2(rowInfo.node);
30300
+ for (const entry of rowInfo.cells) {
30301
+ const formula = typeof entry.node.attrs.formula === "string" ? entry.node.attrs.formula.trim() : "";
30302
+ if (!formula) continue;
30303
+ const rectForCell = safeFindCell2(map, entry.relativePos);
30304
+ if (!rectForCell) continue;
30305
+ const result = evaluateBasicTableFormula(formula, getCellValue);
30306
+ const computedValue = result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30307
+ if (entry.node.attrs.computedValue === computedValue) continue;
30308
+ cells[entry.index] = entry.node.type.create(
30309
+ {
30310
+ ...entry.node.attrs,
30311
+ computedValue
30312
+ },
30313
+ entry.node.content,
30314
+ entry.node.marks
30315
+ );
30316
+ changed = true;
30317
+ }
30318
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
30319
+ });
30320
+ if (!changed) return false;
30321
+ const nextTable = tableNode.type.create(tableNode.attrs, rows);
30322
+ editor.view.dispatch(editor.state.tr.replaceWith(rect.tableStart - 1, rect.tableStart - 1 + tableNode.nodeSize, nextTable));
30323
+ dispatchTableLayoutChange(editor);
30324
+ return true;
30325
+ }
30326
+
30327
+ // src/components/UEditor/menus.tsx
29244
30328
  var import_jsx_runtime86 = require("react/jsx-runtime");
29245
30329
  function applyTableCellBackground(editor, color) {
29246
30330
  const value = color || null;
29247
30331
  const { state, view } = editor;
29248
- const applied = (0, import_tables2.setCellAttr)("backgroundColor", value)(state, view.dispatch.bind(view));
30332
+ const applied = (0, import_tables3.setCellAttr)("backgroundColor", value)(state, view.dispatch.bind(view));
29249
30333
  if (applied) {
29250
30334
  view.focus();
29251
30335
  return;
@@ -29271,6 +30355,8 @@ var BubbleMenuContent = ({
29271
30355
  setShowLinkInput(initialShowLinkInput);
29272
30356
  }, [initialShowLinkInput]);
29273
30357
  const [showTypographyPanel, setShowTypographyPanel] = (0, import_react62.useState)(false);
30358
+ const [showFormulaPanel, setShowFormulaPanel] = (0, import_react62.useState)(false);
30359
+ const [formulaDraft, setFormulaDraft] = (0, import_react62.useState)("");
29274
30360
  const [showFontSizeOptions, setShowFontSizeOptions] = (0, import_react62.useState)(false);
29275
30361
  const [fontSizeDraft, setFontSizeDraft] = (0, import_react62.useState)("");
29276
30362
  const isImageSelected = editor.isActive("image");
@@ -29281,7 +30367,8 @@ var BubbleMenuContent = ({
29281
30367
  const currentTextColor = normalizeStyleValue(textStyleAttrs.color) || "inherit";
29282
30368
  const currentHighlightColor = normalizeStyleValue(editor.getAttributes("highlight").color) || "";
29283
30369
  const currentCellBgColor = normalizeStyleValue(editor.getAttributes("tableCell").backgroundColor || editor.getAttributes("tableHeader").backgroundColor) || "";
29284
- const isInTable2 = (0, import_tables2.isInTable)(editor.state);
30370
+ const currentCellFormula = normalizeStyleValue(editor.getAttributes("tableCell").formula || editor.getAttributes("tableHeader").formula) || "";
30371
+ const isInTable2 = (0, import_tables3.isInTable)(editor.state);
29285
30372
  const canMergeCells = isInTable2 && editor.can().mergeCells();
29286
30373
  const canSplitCell = isInTable2 && editor.can().splitCell();
29287
30374
  const currentFontSize = normalizeStyleValue(textStyleAttrs.fontSize);
@@ -29297,6 +30384,9 @@ var BubbleMenuContent = ({
29297
30384
  (0, import_react62.useEffect)(() => {
29298
30385
  setFontSizeDraft(currentFontSize.replace(/px$/i, ""));
29299
30386
  }, [currentFontSize]);
30387
+ (0, import_react62.useEffect)(() => {
30388
+ setFormulaDraft(currentCellFormula);
30389
+ }, [currentCellFormula]);
29300
30390
  const applyFontSizeDraft = () => {
29301
30391
  const normalized = fontSizeDraft.trim();
29302
30392
  if (!normalized) {
@@ -29499,6 +30589,82 @@ var BubbleMenuContent = ({
29499
30589
  )
29500
30590
  ] });
29501
30591
  }
30592
+ if (showFormulaPanel && isInTable2) {
30593
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "w-72 p-2", children: [
30594
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
30595
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "px-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: t("tableMenu.formula") || "Formula" }),
30596
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30597
+ "button",
30598
+ {
30599
+ type: "button",
30600
+ onClick: () => {
30601
+ setShowFormulaPanel(false);
30602
+ onKeepOpenChange?.(false);
30603
+ },
30604
+ className: "rounded-md px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
30605
+ children: t("colors.done")
30606
+ }
30607
+ )
30608
+ ] }),
30609
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "flex h-9 items-center overflow-hidden rounded-md border border-border/60 bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30610
+ "input",
30611
+ {
30612
+ value: formulaDraft,
30613
+ onChange: (event) => setFormulaDraft(event.target.value),
30614
+ onMouseDown: (event) => event.stopPropagation(),
30615
+ onClick: (event) => event.stopPropagation(),
30616
+ onKeyDown: (event) => {
30617
+ event.stopPropagation();
30618
+ if (event.key === "Enter") {
30619
+ event.preventDefault();
30620
+ setSelectedTableCellFormula(editor, formulaDraft);
30621
+ setShowFormulaPanel(false);
30622
+ onKeepOpenChange?.(false);
30623
+ }
30624
+ },
30625
+ "aria-label": t("tableMenu.formula") || "Formula",
30626
+ placeholder: "=SUM(A1:A3)",
30627
+ className: "h-full min-w-0 flex-1 bg-transparent px-2 text-sm font-medium text-foreground outline-none"
30628
+ }
30629
+ ) }),
30630
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "mt-2 grid grid-cols-3 gap-1", children: [
30631
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30632
+ "button",
30633
+ {
30634
+ type: "button",
30635
+ onClick: () => {
30636
+ setSelectedTableCellFormula(editor, formulaDraft);
30637
+ setShowFormulaPanel(false);
30638
+ onKeepOpenChange?.(false);
30639
+ },
30640
+ className: "h-8 rounded-md bg-primary/10 text-xs font-semibold text-primary transition-colors hover:bg-primary/15",
30641
+ children: t("tableMenu.apply") || "Apply"
30642
+ }
30643
+ ),
30644
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30645
+ "button",
30646
+ {
30647
+ type: "button",
30648
+ onClick: () => {
30649
+ clearSelectedTableCellFormula(editor);
30650
+ setFormulaDraft("");
30651
+ },
30652
+ className: "h-8 rounded-md bg-muted/40 text-xs font-semibold text-foreground transition-colors hover:bg-muted",
30653
+ children: t("tableMenu.clear") || "Clear"
30654
+ }
30655
+ ),
30656
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30657
+ "button",
30658
+ {
30659
+ type: "button",
30660
+ onClick: () => recalculateSelectedTable(editor),
30661
+ className: "h-8 rounded-md bg-muted/40 text-xs font-semibold text-foreground transition-colors hover:bg-muted",
30662
+ children: t("tableMenu.recalculate") || "Recalc"
30663
+ }
30664
+ )
30665
+ ] })
30666
+ ] });
30667
+ }
29502
30668
  if (showTypographyPanel) {
29503
30669
  return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "w-72 p-2", children: [
29504
30670
  /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
@@ -29712,6 +30878,21 @@ var BubbleMenuContent = ({
29712
30878
  )
29713
30879
  }
29714
30880
  ),
30881
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30882
+ ToolbarButton,
30883
+ {
30884
+ onMouseDown: () => {
30885
+ onKeepOpenChange?.(true);
30886
+ },
30887
+ onClick: () => {
30888
+ setFormulaDraft(currentCellFormula);
30889
+ setShowFormulaPanel(true);
30890
+ },
30891
+ active: Boolean(currentCellFormula),
30892
+ title: t("tableMenu.formula") || "Formula",
30893
+ children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_lucide_react49.Sigma, { className: "w-4 h-4" })
30894
+ }
30895
+ ),
29715
30896
  /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
29716
30897
  ToolbarButton,
29717
30898
  {
@@ -33553,7 +34734,7 @@ if (typeof WeakMap != "undefined") {
33553
34734
  return cache[cachePos++] = value;
33554
34735
  };
33555
34736
  }
33556
- var TableMap2 = class {
34737
+ var TableMap3 = class {
33557
34738
  constructor(width, height, map, problems) {
33558
34739
  this.width = width;
33559
34740
  this.height = height;
@@ -33690,7 +34871,7 @@ function computeMap(table) {
33690
34871
  pos++;
33691
34872
  }
33692
34873
  if (width === 0 || height === 0) (problems || (problems = [])).push({ type: "zero_sized" });
33693
- const tableMap = new TableMap2(width, height, map, problems);
34874
+ const tableMap = new TableMap3(width, height, map, problems);
33694
34875
  let badWidths = false;
33695
34876
  for (let i = 0; !badWidths && i < colWidths.length; i += 2) if (colWidths[i] != null && colWidths[i + 1] < height) badWidths = true;
33696
34877
  if (badWidths) findBadColWidths(tableMap, colWidths, table);
@@ -33794,7 +34975,7 @@ function inSameTable($cellA, $cellB) {
33794
34975
  }
33795
34976
  function nextCell($pos, axis, dir) {
33796
34977
  const table = $pos.node(-1);
33797
- const map = TableMap2.get(table);
34978
+ const map = TableMap3.get(table);
33798
34979
  const tableStart = $pos.start(-1);
33799
34980
  const moved = map.nextCell($pos.pos - tableStart, axis, dir);
33800
34981
  return moved == null ? null : $pos.node(0).resolve(tableStart + moved);
@@ -33814,7 +34995,7 @@ function removeColSpan(attrs, pos, n = 1) {
33814
34995
  var CellSelection = class CellSelection2 extends Selection {
33815
34996
  constructor($anchorCell, $headCell = $anchorCell) {
33816
34997
  const table = $anchorCell.node(-1);
33817
- const map = TableMap2.get(table);
34998
+ const map = TableMap3.get(table);
33818
34999
  const tableStart = $anchorCell.start(-1);
33819
35000
  const rect = map.rectBetween($anchorCell.pos - tableStart, $headCell.pos - tableStart);
33820
35001
  const doc = $anchorCell.node(0);
@@ -33843,7 +35024,7 @@ var CellSelection = class CellSelection2 extends Selection {
33843
35024
  }
33844
35025
  content() {
33845
35026
  const table = this.$anchorCell.node(-1);
33846
- const map = TableMap2.get(table);
35027
+ const map = TableMap3.get(table);
33847
35028
  const tableStart = this.$anchorCell.start(-1);
33848
35029
  const rect = map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart);
33849
35030
  const seen = {};
@@ -33897,7 +35078,7 @@ var CellSelection = class CellSelection2 extends Selection {
33897
35078
  }
33898
35079
  forEachCell(f) {
33899
35080
  const table = this.$anchorCell.node(-1);
33900
- const map = TableMap2.get(table);
35081
+ const map = TableMap3.get(table);
33901
35082
  const tableStart = this.$anchorCell.start(-1);
33902
35083
  const cells = map.cellsInRect(map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart));
33903
35084
  for (let i = 0; i < cells.length; i++) f(table.nodeAt(cells[i]), tableStart + cells[i]);
@@ -33912,7 +35093,7 @@ var CellSelection = class CellSelection2 extends Selection {
33912
35093
  }
33913
35094
  static colSelection($anchorCell, $headCell = $anchorCell) {
33914
35095
  const table = $anchorCell.node(-1);
33915
- const map = TableMap2.get(table);
35096
+ const map = TableMap3.get(table);
33916
35097
  const tableStart = $anchorCell.start(-1);
33917
35098
  const anchorRect = map.findCell($anchorCell.pos - tableStart);
33918
35099
  const headRect = map.findCell($headCell.pos - tableStart);
@@ -33928,7 +35109,7 @@ var CellSelection = class CellSelection2 extends Selection {
33928
35109
  }
33929
35110
  isRowSelection() {
33930
35111
  const table = this.$anchorCell.node(-1);
33931
- const map = TableMap2.get(table);
35112
+ const map = TableMap3.get(table);
33932
35113
  const tableStart = this.$anchorCell.start(-1);
33933
35114
  const anchorLeft = map.colCount(this.$anchorCell.pos - tableStart);
33934
35115
  const headLeft = map.colCount(this.$headCell.pos - tableStart);
@@ -33942,7 +35123,7 @@ var CellSelection = class CellSelection2 extends Selection {
33942
35123
  }
33943
35124
  static rowSelection($anchorCell, $headCell = $anchorCell) {
33944
35125
  const table = $anchorCell.node(-1);
33945
- const map = TableMap2.get(table);
35126
+ const map = TableMap3.get(table);
33946
35127
  const tableStart = $anchorCell.start(-1);
33947
35128
  const anchorRect = map.findCell($anchorCell.pos - tableStart);
33948
35129
  const headRect = map.findCell($headCell.pos - tableStart);
@@ -33991,7 +35172,7 @@ var CellBookmark = class CellBookmark2 {
33991
35172
  };
33992
35173
  var fixTablesKey = new PluginKey4("fix-tables");
33993
35174
  function convertTableNodeToArrayOfRows(tableNode) {
33994
- const map = TableMap2.get(tableNode);
35175
+ const map = TableMap3.get(tableNode);
33995
35176
  const rows = [];
33996
35177
  const rowCount = map.height;
33997
35178
  const colCount$1 = map.width;
@@ -34022,7 +35203,7 @@ function convertTableNodeToArrayOfRows(tableNode) {
34022
35203
  }
34023
35204
  function convertArrayOfRowsToTableNode(tableNode, arrayOfNodes) {
34024
35205
  const newRows = [];
34025
- const map = TableMap2.get(tableNode);
35206
+ const map = TableMap3.get(tableNode);
34026
35207
  const rowCount = map.height;
34027
35208
  const colCount$1 = map.width;
34028
35209
  for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
@@ -34071,7 +35252,7 @@ function findParentNode(predicate, $pos) {
34071
35252
  function getCellsInColumn(columnIndex, selection) {
34072
35253
  const table = findTable(selection.$from);
34073
35254
  if (!table) return;
34074
- const map = TableMap2.get(table.node);
35255
+ const map = TableMap3.get(table.node);
34075
35256
  if (columnIndex < 0 || columnIndex > map.width - 1) return;
34076
35257
  return map.cellsInRect({
34077
35258
  left: columnIndex,
@@ -34092,7 +35273,7 @@ function getCellsInColumn(columnIndex, selection) {
34092
35273
  function getCellsInRow(rowIndex, selection) {
34093
35274
  const table = findTable(selection.$from);
34094
35275
  if (!table) return;
34095
- const map = TableMap2.get(table.node);
35276
+ const map = TableMap3.get(table.node);
34096
35277
  if (rowIndex < 0 || rowIndex > map.height - 1) return;
34097
35278
  return map.cellsInRect({
34098
35279
  left: 0,
@@ -34221,7 +35402,7 @@ function moveColumn(moveColParams) {
34221
35402
  const newTable = moveTableColumn$1(table.node, indexesOriginColumn, indexesTargetColumn, 0);
34222
35403
  tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
34223
35404
  if (!select) return true;
34224
- const map = TableMap2.get(newTable);
35405
+ const map = TableMap3.get(newTable);
34225
35406
  const start = table.start;
34226
35407
  const index = targetIndex;
34227
35408
  const lastCell = map.positionAt(map.height - 1, index, newTable);
@@ -34249,7 +35430,7 @@ function moveRow(moveRowParams) {
34249
35430
  const newTable = moveTableRow$1(table.node, indexesOriginRow, indexesTargetRow, 0);
34250
35431
  tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
34251
35432
  if (!select) return true;
34252
- const map = TableMap2.get(newTable);
35433
+ const map = TableMap3.get(newTable);
34253
35434
  const start = table.start;
34254
35435
  const index = targetIndex;
34255
35436
  const lastCell = map.positionAt(index, map.width - 1, newTable);
@@ -34264,12 +35445,12 @@ function moveTableRow$1(table, indexesOrigin, indexesTarget, direction) {
34264
35445
  rows = moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, direction);
34265
35446
  return convertArrayOfRowsToTableNode(table, rows);
34266
35447
  }
34267
- function selectedRect2(state) {
35448
+ function selectedRect3(state) {
34268
35449
  const sel = state.selection;
34269
35450
  const $pos = selectionCell(state);
34270
35451
  const table = $pos.node(-1);
34271
35452
  const tableStart = $pos.start(-1);
34272
- const map = TableMap2.get(table);
35453
+ const map = TableMap3.get(table);
34273
35454
  return {
34274
35455
  ...sel instanceof CellSelection ? map.rectBetween(sel.$anchorCell.pos - tableStart, sel.$headCell.pos - tableStart) : map.findCell($pos.pos - tableStart),
34275
35456
  tableStart,
@@ -34282,7 +35463,7 @@ function deprecated_toggleHeader(type) {
34282
35463
  if (!isInTable(state)) return false;
34283
35464
  if (dispatch) {
34284
35465
  const types = tableNodeTypes(state.schema);
34285
- const rect = selectedRect2(state), tr = state.tr;
35466
+ const rect = selectedRect3(state), tr = state.tr;
34286
35467
  const cells = rect.map.cellsInRect(type == "column" ? {
34287
35468
  left: rect.left,
34288
35469
  top: 0,
@@ -34322,7 +35503,7 @@ function toggleHeader(type, options) {
34322
35503
  if (!isInTable(state)) return false;
34323
35504
  if (dispatch) {
34324
35505
  const types = tableNodeTypes(state.schema);
34325
- const rect = selectedRect2(state), tr = state.tr;
35506
+ const rect = selectedRect3(state), tr = state.tr;
34326
35507
  const isHeaderRowEnabled = isHeaderEnabledByType("row", rect, types);
34327
35508
  const isHeaderColumnEnabled = isHeaderEnabledByType("column", rect, types);
34328
35509
  const selectionStartsAt = (type === "column" ? isHeaderRowEnabled : type === "row" ? isHeaderColumnEnabled : false) ? 1 : 0;
@@ -34472,6 +35653,262 @@ var columnResizingPluginKey = new PluginKey4("tableColumnResizing");
34472
35653
  // src/components/UEditor/table-controls.tsx
34473
35654
  var import_lucide_react52 = require("lucide-react");
34474
35655
 
35656
+ // src/components/UEditor/table-layout-model.ts
35657
+ var FALLBACK_TABLE_ROW_HEIGHT = 44;
35658
+ var FALLBACK_TABLE_COLUMN_WIDTH = 160;
35659
+ function getVisibleTableBounds(layout) {
35660
+ const left = Math.max(layout.tableLeft, layout.wrapperLeft);
35661
+ const top = Math.max(layout.tableTop, layout.wrapperTop);
35662
+ const right = Math.min(layout.tableLeft + layout.tableWidth, layout.wrapperLeft + layout.viewportWidth);
35663
+ const bottom = Math.min(layout.tableTop + layout.tableHeight, layout.wrapperTop + layout.viewportHeight);
35664
+ return {
35665
+ left,
35666
+ top,
35667
+ right,
35668
+ bottom,
35669
+ width: Math.max(0, right - left),
35670
+ height: Math.max(0, bottom - top)
35671
+ };
35672
+ }
35673
+ function metricOrFallback(value, fallback) {
35674
+ return Number.isFinite(value) && value > 0 ? value : fallback;
35675
+ }
35676
+ function parsePixelMetric(value) {
35677
+ if (!value) return null;
35678
+ const parsed = Number.parseFloat(value);
35679
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
35680
+ }
35681
+ function getPrimaryCell(table) {
35682
+ const cell = table.querySelector("th,td");
35683
+ return cell instanceof HTMLTableCellElement ? cell : null;
35684
+ }
35685
+ function getLastCell(table) {
35686
+ const lastRow = table.rows.item(table.rows.length - 1);
35687
+ if (!(lastRow instanceof HTMLTableRowElement)) return null;
35688
+ const cell = lastRow.cells.item(lastRow.cells.length - 1);
35689
+ return cell instanceof HTMLTableCellElement ? cell : null;
35690
+ }
35691
+ function getCellFromTarget(target) {
35692
+ const element = resolveEventElement(target);
35693
+ if (!element) return null;
35694
+ const directCell = element.closest("th,td");
35695
+ if (directCell instanceof HTMLTableCellElement) {
35696
+ return directCell;
35697
+ }
35698
+ const table = element.closest("table");
35699
+ if (table instanceof HTMLTableElement) {
35700
+ return getPrimaryCell(table);
35701
+ }
35702
+ return null;
35703
+ }
35704
+ function findTableInfo(editor, pos) {
35705
+ const $pos = editor.state.doc.resolve(pos);
35706
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
35707
+ const node = $pos.node(depth);
35708
+ if (node.type.name === "table") {
35709
+ return {
35710
+ node,
35711
+ pos: $pos.before(depth),
35712
+ start: $pos.start(depth)
35713
+ };
35714
+ }
35715
+ }
35716
+ return null;
35717
+ }
35718
+ function getCellRelativePosFromDomPos(map, tableStart, domPos) {
35719
+ const relativeDomPos = domPos - tableStart;
35720
+ const seen = /* @__PURE__ */ new Set();
35721
+ for (const relativeCellPos of map.map) {
35722
+ if (seen.has(relativeCellPos)) continue;
35723
+ seen.add(relativeCellPos);
35724
+ if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
35725
+ return relativeCellPos;
35726
+ }
35727
+ }
35728
+ return null;
35729
+ }
35730
+ function buildLogicalColumnMetrics({
35731
+ editor,
35732
+ surface,
35733
+ surfaceRect,
35734
+ tableElement,
35735
+ tableInfo,
35736
+ tableLeft,
35737
+ tableWidth
35738
+ }) {
35739
+ const map = TableMap3.get(tableInfo.node);
35740
+ const fallbackWidth = metricOrFallback(tableWidth / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35741
+ const firstRow = tableElement.rows.item(0);
35742
+ const visualColumns = [];
35743
+ if (firstRow) {
35744
+ for (const tableCell of Array.from(firstRow.cells)) {
35745
+ if (!(tableCell instanceof HTMLTableCellElement)) continue;
35746
+ const cellPos = editor.view.posAtDOM(tableCell, 0);
35747
+ const relativeCellPos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35748
+ if (relativeCellPos == null) continue;
35749
+ const cellMapRect = map.findCell(relativeCellPos);
35750
+ const cellRect = tableCell.getBoundingClientRect();
35751
+ const cellStart = cellRect.width > 0 ? cellRect.left - surfaceRect.left + surface.scrollLeft : tableLeft + cellMapRect.left * fallbackWidth;
35752
+ const size = metricOrFallback(cellRect.width, fallbackWidth * Math.max(1, cellMapRect.right - cellMapRect.left));
35753
+ visualColumns.push({
35754
+ index: cellMapRect.left,
35755
+ cellPos: tableInfo.start + relativeCellPos,
35756
+ start: cellStart,
35757
+ size,
35758
+ center: cellStart + size / 2
35759
+ });
35760
+ }
35761
+ }
35762
+ if (visualColumns.length > 0) {
35763
+ return visualColumns.sort((a, b) => a.index - b.index);
35764
+ }
35765
+ const cols = Array.from(tableElement.querySelectorAll("colgroup > col"));
35766
+ const parsedWidths = cols.slice(0, map.width).map((col) => parsePixelMetric(col.style.width) ?? parsePixelMetric(col.getAttribute("width")));
35767
+ const hasCompleteColWidths = parsedWidths.length >= map.width && parsedWidths.every((width) => typeof width === "number");
35768
+ let cursor = tableLeft;
35769
+ return Array.from({ length: map.width }, (_, index) => {
35770
+ const size = hasCompleteColWidths ? parsedWidths[index] : fallbackWidth;
35771
+ const start = hasCompleteColWidths ? cursor : tableLeft + index * fallbackWidth;
35772
+ cursor += size;
35773
+ return {
35774
+ index,
35775
+ cellPos: tableInfo.start + map.positionAt(0, index, tableInfo.node),
35776
+ start,
35777
+ size,
35778
+ center: start + size / 2
35779
+ };
35780
+ });
35781
+ }
35782
+ function buildLogicalRowMetrics({
35783
+ editor,
35784
+ surface,
35785
+ surfaceRect,
35786
+ tableInfo,
35787
+ rows,
35788
+ tableTop,
35789
+ tableHeight,
35790
+ cornerCell
35791
+ }) {
35792
+ const map = TableMap3.get(tableInfo.node);
35793
+ const fallbackHeight = metricOrFallback(tableHeight / map.height, FALLBACK_TABLE_ROW_HEIGHT);
35794
+ const visualRows = [];
35795
+ const seenCellPositions = /* @__PURE__ */ new Set();
35796
+ for (let rowIndex = 0; rowIndex < map.height; rowIndex += 1) {
35797
+ const relativeCellPos = map.map[rowIndex * map.width];
35798
+ if (seenCellPositions.has(relativeCellPos)) continue;
35799
+ seenCellPositions.add(relativeCellPos);
35800
+ const cellMapRect = map.findCell(relativeCellPos);
35801
+ const cellDom = editor.view.nodeDOM(tableInfo.start + relativeCellPos);
35802
+ const tableCell = cellDom instanceof HTMLTableCellElement ? cellDom : null;
35803
+ if (tableCell) {
35804
+ const cellRect = tableCell.getBoundingClientRect();
35805
+ const start = cellRect.height > 0 ? cellRect.top - surfaceRect.top + surface.scrollTop : tableTop + cellMapRect.top * fallbackHeight;
35806
+ const size = metricOrFallback(cellRect.height, fallbackHeight * Math.max(1, cellMapRect.bottom - cellMapRect.top));
35807
+ visualRows.push({
35808
+ index: cellMapRect.top,
35809
+ cellPos: tableInfo.start + relativeCellPos,
35810
+ start,
35811
+ size,
35812
+ center: start + size / 2
35813
+ });
35814
+ }
35815
+ }
35816
+ if (visualRows.length > 0) {
35817
+ return visualRows.sort((a, b) => a.index - b.index);
35818
+ }
35819
+ return rows.map((tableRow, index) => {
35820
+ const rowRect = tableRow.getBoundingClientRect();
35821
+ const anchorCell = tableRow.cells.item(0) ?? cornerCell;
35822
+ const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top + surface.scrollTop : tableTop + index * fallbackHeight;
35823
+ const size = metricOrFallback(rowRect.height, fallbackHeight);
35824
+ return {
35825
+ index,
35826
+ cellPos: editor.view.posAtDOM(anchorCell, 0),
35827
+ start,
35828
+ size,
35829
+ center: start + size / 2
35830
+ };
35831
+ });
35832
+ }
35833
+ function buildTableControlLayout(editor, surface, cell) {
35834
+ const row = cell.closest("tr");
35835
+ const table = cell.closest("table");
35836
+ if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) {
35837
+ return null;
35838
+ }
35839
+ const rows = Array.from(table.rows).filter((item) => item instanceof HTMLTableRowElement);
35840
+ const cornerCell = getLastCell(table);
35841
+ const cellPos = editor.view.posAtDOM(cell, 0);
35842
+ const tableInfo = findTableInfo(editor, cellPos);
35843
+ if (rows.length === 0 || !tableInfo || !(cornerCell instanceof HTMLTableCellElement)) {
35844
+ return null;
35845
+ }
35846
+ const map = TableMap3.get(tableInfo.node);
35847
+ const surfaceRect = surface.getBoundingClientRect();
35848
+ const tableRect = table.getBoundingClientRect();
35849
+ const wrapperElement = table.closest(".tableWrapper");
35850
+ const wrapper = wrapperElement instanceof HTMLElement ? wrapperElement : null;
35851
+ const wrapperRect = wrapper?.getBoundingClientRect() ?? tableRect;
35852
+ const tableLeft = tableRect.left - surfaceRect.left + surface.scrollLeft;
35853
+ const tableTop = tableRect.top - surfaceRect.top + surface.scrollTop;
35854
+ const avgRowHeight = metricOrFallback(tableRect.height / rows.length, FALLBACK_TABLE_ROW_HEIGHT);
35855
+ const avgColumnWidth = metricOrFallback(tableRect.width / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35856
+ const tableWidth = metricOrFallback(tableRect.width, avgColumnWidth * map.width);
35857
+ const tableHeight = metricOrFallback(tableRect.height, avgRowHeight * rows.length);
35858
+ const wrapperLeft = wrapperRect.left - surfaceRect.left + surface.scrollLeft;
35859
+ const wrapperTop = wrapperRect.top - surfaceRect.top + surface.scrollTop;
35860
+ const wrapperWidth = metricOrFallback(wrapperRect.width, tableWidth);
35861
+ const wrapperHeight = metricOrFallback(wrapperRect.height, tableHeight);
35862
+ const viewportWidth = metricOrFallback(wrapper?.clientWidth ?? wrapperRect.width, tableWidth);
35863
+ const viewportHeight = metricOrFallback(wrapper?.clientHeight ?? wrapperRect.height, tableHeight);
35864
+ const verticalScrollbarWidth = Math.max(0, Math.round(wrapperWidth - viewportWidth));
35865
+ const horizontalScrollbarHeight = Math.max(0, Math.round(wrapperHeight - viewportHeight));
35866
+ const rowHandles = buildLogicalRowMetrics({
35867
+ editor,
35868
+ surface,
35869
+ surfaceRect,
35870
+ tableInfo,
35871
+ rows,
35872
+ tableTop,
35873
+ tableHeight,
35874
+ cornerCell
35875
+ });
35876
+ const columnHandles = buildLogicalColumnMetrics({
35877
+ editor,
35878
+ surface,
35879
+ surfaceRect,
35880
+ tableElement: table,
35881
+ tableInfo,
35882
+ tableLeft,
35883
+ tableWidth
35884
+ });
35885
+ const activeCellRelativePos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35886
+ const activeCellRect = activeCellRelativePos != null ? map.findCell(activeCellRelativePos) : { left: cell.cellIndex, top: row.rowIndex };
35887
+ const normalizedCellPos = activeCellRelativePos != null ? tableInfo.start + activeCellRelativePos : cellPos;
35888
+ return {
35889
+ cellPos: normalizedCellPos,
35890
+ cornerCellPos: tableInfo.start + map.positionAt(map.height - 1, map.width - 1, tableInfo.node),
35891
+ activeRowIndex: activeCellRect.top,
35892
+ activeColumnIndex: activeCellRect.left,
35893
+ tableLeft,
35894
+ tableTop,
35895
+ tableWidth,
35896
+ tableHeight,
35897
+ wrapperLeft,
35898
+ wrapperTop,
35899
+ wrapperWidth,
35900
+ wrapperHeight,
35901
+ viewportWidth,
35902
+ viewportHeight,
35903
+ horizontalScrollbarHeight,
35904
+ verticalScrollbarWidth,
35905
+ avgRowHeight,
35906
+ avgColumnWidth,
35907
+ rowHandles,
35908
+ columnHandles
35909
+ };
35910
+ }
35911
+
34475
35912
  // src/components/UEditor/table-hover-state.ts
34476
35913
  var MENU_HOVER_PADDING = 18;
34477
35914
  var ROW_HANDLE_HOVER_WIDTH = 28;
@@ -34505,18 +35942,18 @@ function buildTableHoverState({
34505
35942
  const directAddRow = targetElement?.closest?.("[data-table-control='add-row']");
34506
35943
  const directRowHandleIndex = directRowHandle instanceof HTMLElement ? Number.parseInt(directRowHandle.dataset.rowHandleIndex ?? "", 10) : Number.NaN;
34507
35944
  const directColumnHandleIndex = directColumnHandle instanceof HTMLElement ? Number.parseInt(directColumnHandle.dataset.columnHandleIndex ?? "", 10) : Number.NaN;
34508
- const visibleTableWidth = Math.min(layout.tableWidth, layout.viewportWidth);
34509
- const visibleTableHeight = Math.min(layout.tableHeight, layout.viewportHeight);
35945
+ const visibleBounds = getVisibleTableBounds(layout);
35946
+ const rowRailTop = layout.wrapperTop + layout.wrapperHeight;
34510
35947
  const isMouseInTable = relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + layout.tableWidth && relativeY >= layout.tableTop && relativeY <= layout.tableTop + layout.tableHeight;
34511
35948
  const rowHandleIndex = Number.isFinite(directRowHandleIndex) ? directRowHandleIndex : layout.rowHandles.find((rowHandle) => relativeX >= layout.tableLeft - ROW_HANDLE_HOVER_WIDTH && relativeX <= layout.tableLeft && Math.abs(relativeY - rowHandle.center) <= HANDLE_HOVER_RADIUS)?.index ?? null;
34512
35949
  const columnHandleIndex = Number.isFinite(directColumnHandleIndex) ? directColumnHandleIndex : layout.columnHandles.find((columnHandle) => relativeY >= layout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= layout.tableTop && Math.abs(relativeX - columnHandle.center) <= HANDLE_HOVER_RADIUS)?.index ?? null;
34513
35950
  const menuVisible = Boolean(directTableMenu) || isMouseInTable || relativeX >= layout.tableLeft - MENU_HOVER_PADDING && relativeX <= layout.tableLeft + 42 && relativeY >= layout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= layout.tableTop + MENU_HOVER_PADDING;
34514
35951
  const lastRow = layout.rowHandles[layout.rowHandles.length - 1];
34515
35952
  const lastCol = layout.columnHandles[layout.columnHandles.length - 1];
34516
- const isMouseInLastColumn = lastCol ? relativeX >= lastCol.start && relativeX <= lastCol.start + lastCol.size && relativeY >= layout.tableTop && relativeY <= layout.tableTop + visibleTableHeight : false;
34517
- const addColumnVisible = Boolean(directAddColumn) || relativeX >= layout.tableLeft + visibleTableWidth && relativeX <= layout.tableLeft + visibleTableWidth + ADD_COLUMN_HOVER_WIDTH && relativeY >= layout.tableTop && relativeY <= layout.tableTop + visibleTableHeight || isMouseInLastColumn;
34518
- const isMouseInLastRow = lastRow ? relativeY >= lastRow.start && relativeY <= lastRow.start + lastRow.size && relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + visibleTableWidth : false;
34519
- const addRowVisible = Boolean(directAddRow) || relativeY >= layout.tableTop + visibleTableHeight && relativeY <= layout.tableTop + visibleTableHeight + ADD_ROW_HOVER_HEIGHT && relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + visibleTableWidth || isMouseInLastRow;
35953
+ const isMouseInLastColumn = lastCol ? relativeX >= lastCol.start && relativeX <= lastCol.start + lastCol.size && relativeY >= visibleBounds.top && relativeY <= visibleBounds.bottom : false;
35954
+ const addColumnVisible = Boolean(directAddColumn) || relativeX >= visibleBounds.right && relativeX <= visibleBounds.right + ADD_COLUMN_HOVER_WIDTH && relativeY >= visibleBounds.top && relativeY <= visibleBounds.bottom || isMouseInLastColumn;
35955
+ const isMouseInLastRow = lastRow ? relativeY >= lastRow.start && relativeY <= lastRow.start + lastRow.size && relativeX >= visibleBounds.left && relativeX <= visibleBounds.right : false;
35956
+ const addRowVisible = Boolean(directAddRow) || relativeY >= rowRailTop && relativeY <= rowRailTop + ADD_ROW_HOVER_HEIGHT && relativeX >= visibleBounds.left && relativeX <= visibleBounds.right || isMouseInLastRow;
34520
35957
  return {
34521
35958
  menuVisible,
34522
35959
  addColumnVisible,
@@ -34638,12 +36075,11 @@ function TableAddRails({
34638
36075
  quickAddColumnLabel,
34639
36076
  quickAddRowLabel
34640
36077
  }) {
34641
- const visibleTableWidth = Math.min(layout.tableWidth, layout.viewportWidth);
34642
- const visibleTableHeight = Math.min(layout.tableHeight, layout.viewportHeight);
34643
- const columnRailTop = layout.tableTop;
34644
- const columnRailLeft = layout.tableLeft + visibleTableWidth + ADD_COLUMN_RAIL_GAP;
36078
+ const visibleBounds = getVisibleTableBounds(layout);
36079
+ const columnRailTop = visibleBounds.top;
36080
+ const columnRailLeft = visibleBounds.right + ADD_COLUMN_RAIL_GAP;
34645
36081
  const rowRailTop = layout.wrapperTop + layout.wrapperHeight + ADD_ROW_RAIL_GAP;
34646
- const rowRailLeft = Math.max(layout.tableLeft, layout.wrapperLeft);
36082
+ const rowRailLeft = visibleBounds.left;
34647
36083
  const showColumnRail = controlsVisible || addColumnVisible;
34648
36084
  const showRowRail = controlsVisible || addRowVisible;
34649
36085
  return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(import_jsx_runtime89.Fragment, { children: [
@@ -34671,10 +36107,10 @@ function TableAddRails({
34671
36107
  "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
34672
36108
  ),
34673
36109
  style: {
34674
- top: showColumnRail ? columnRailTop : columnRailTop + Math.max(0, visibleTableHeight / 2 - 24),
36110
+ top: showColumnRail ? columnRailTop : columnRailTop + Math.max(0, visibleBounds.height / 2 - 24),
34675
36111
  left: columnRailLeft,
34676
36112
  width: showColumnRail ? 18 : 12,
34677
- height: showColumnRail ? visibleTableHeight : 48,
36113
+ height: showColumnRail ? visibleBounds.height : 48,
34678
36114
  opacity: showColumnRail ? 1 : 0,
34679
36115
  transform: showColumnRail ? "scale(1)" : "scale(0.92)",
34680
36116
  pointerEvents: showColumnRail ? "auto" : "none"
@@ -34709,8 +36145,8 @@ function TableAddRails({
34709
36145
  ),
34710
36146
  style: {
34711
36147
  top: rowRailTop,
34712
- left: showRowRail ? rowRailLeft : rowRailLeft + Math.max(0, visibleTableWidth / 2 - 24),
34713
- width: showRowRail ? visibleTableWidth : 48,
36148
+ left: showRowRail ? rowRailLeft : rowRailLeft + Math.max(0, visibleBounds.width / 2 - 24),
36149
+ width: showRowRail ? visibleBounds.width : 48,
34714
36150
  height: showRowRail ? 16 : 12,
34715
36151
  opacity: showRowRail ? 1 : 0,
34716
36152
  transform: showRowRail ? "scale(1)" : "scale(0.92)",
@@ -34935,248 +36371,6 @@ function TableColumnHandles({
34935
36371
  }) });
34936
36372
  }
34937
36373
 
34938
- // src/components/UEditor/table-layout-model.ts
34939
- var FALLBACK_TABLE_ROW_HEIGHT = 44;
34940
- var FALLBACK_TABLE_COLUMN_WIDTH = 160;
34941
- function metricOrFallback(value, fallback) {
34942
- return Number.isFinite(value) && value > 0 ? value : fallback;
34943
- }
34944
- function parsePixelMetric(value) {
34945
- if (!value) return null;
34946
- const parsed = Number.parseFloat(value);
34947
- return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
34948
- }
34949
- function getPrimaryCell(table) {
34950
- const cell = table.querySelector("th,td");
34951
- return cell instanceof HTMLTableCellElement ? cell : null;
34952
- }
34953
- function getLastCell(table) {
34954
- const lastRow = table.rows.item(table.rows.length - 1);
34955
- if (!(lastRow instanceof HTMLTableRowElement)) return null;
34956
- const cell = lastRow.cells.item(lastRow.cells.length - 1);
34957
- return cell instanceof HTMLTableCellElement ? cell : null;
34958
- }
34959
- function getCellFromTarget(target) {
34960
- const element = resolveEventElement(target);
34961
- if (!element) return null;
34962
- const directCell = element.closest("th,td");
34963
- if (directCell instanceof HTMLTableCellElement) {
34964
- return directCell;
34965
- }
34966
- const table = element.closest("table");
34967
- if (table instanceof HTMLTableElement) {
34968
- return getPrimaryCell(table);
34969
- }
34970
- return null;
34971
- }
34972
- function findTableInfo(editor, pos) {
34973
- const $pos = editor.state.doc.resolve(pos);
34974
- for (let depth = $pos.depth; depth > 0; depth -= 1) {
34975
- const node = $pos.node(depth);
34976
- if (node.type.name === "table") {
34977
- return {
34978
- node,
34979
- pos: $pos.before(depth),
34980
- start: $pos.start(depth)
34981
- };
34982
- }
34983
- }
34984
- return null;
34985
- }
34986
- function getCellRelativePosFromDomPos(map, tableStart, domPos) {
34987
- const relativeDomPos = domPos - tableStart;
34988
- const seen = /* @__PURE__ */ new Set();
34989
- for (const relativeCellPos of map.map) {
34990
- if (seen.has(relativeCellPos)) continue;
34991
- seen.add(relativeCellPos);
34992
- if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
34993
- return relativeCellPos;
34994
- }
34995
- }
34996
- return null;
34997
- }
34998
- function buildLogicalColumnMetrics({
34999
- editor,
35000
- surface,
35001
- surfaceRect,
35002
- tableElement,
35003
- tableInfo,
35004
- tableLeft,
35005
- tableWidth
35006
- }) {
35007
- const map = TableMap2.get(tableInfo.node);
35008
- const fallbackWidth = metricOrFallback(tableWidth / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35009
- const firstRow = tableElement.rows.item(0);
35010
- const visualColumns = [];
35011
- if (firstRow) {
35012
- for (const tableCell of Array.from(firstRow.cells)) {
35013
- if (!(tableCell instanceof HTMLTableCellElement)) continue;
35014
- const cellPos = editor.view.posAtDOM(tableCell, 0);
35015
- const relativeCellPos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35016
- if (relativeCellPos == null) continue;
35017
- const cellMapRect = map.findCell(relativeCellPos);
35018
- const cellRect = tableCell.getBoundingClientRect();
35019
- const cellStart = cellRect.width > 0 ? cellRect.left - surfaceRect.left + surface.scrollLeft : tableLeft + cellMapRect.left * fallbackWidth;
35020
- const size = metricOrFallback(cellRect.width, fallbackWidth * Math.max(1, cellMapRect.right - cellMapRect.left));
35021
- visualColumns.push({
35022
- index: cellMapRect.left,
35023
- cellPos: tableInfo.start + relativeCellPos,
35024
- start: cellStart,
35025
- size,
35026
- center: cellStart + size / 2
35027
- });
35028
- }
35029
- }
35030
- if (visualColumns.length > 0) {
35031
- return visualColumns.sort((a, b) => a.index - b.index);
35032
- }
35033
- const cols = Array.from(tableElement.querySelectorAll("colgroup > col"));
35034
- const parsedWidths = cols.slice(0, map.width).map((col) => parsePixelMetric(col.style.width) ?? parsePixelMetric(col.getAttribute("width")));
35035
- const hasCompleteColWidths = parsedWidths.length >= map.width && parsedWidths.every((width) => typeof width === "number");
35036
- let cursor = tableLeft;
35037
- return Array.from({ length: map.width }, (_, index) => {
35038
- const size = hasCompleteColWidths ? parsedWidths[index] : fallbackWidth;
35039
- const start = hasCompleteColWidths ? cursor : tableLeft + index * fallbackWidth;
35040
- cursor += size;
35041
- return {
35042
- index,
35043
- cellPos: tableInfo.start + map.positionAt(0, index, tableInfo.node),
35044
- start,
35045
- size,
35046
- center: start + size / 2
35047
- };
35048
- });
35049
- }
35050
- function buildLogicalRowMetrics({
35051
- editor,
35052
- surface,
35053
- surfaceRect,
35054
- tableInfo,
35055
- rows,
35056
- tableTop,
35057
- tableHeight,
35058
- cornerCell
35059
- }) {
35060
- const map = TableMap2.get(tableInfo.node);
35061
- const fallbackHeight = metricOrFallback(tableHeight / map.height, FALLBACK_TABLE_ROW_HEIGHT);
35062
- const visualRows = [];
35063
- const seenCellPositions = /* @__PURE__ */ new Set();
35064
- for (let rowIndex = 0; rowIndex < map.height; rowIndex += 1) {
35065
- const relativeCellPos = map.map[rowIndex * map.width];
35066
- if (seenCellPositions.has(relativeCellPos)) continue;
35067
- seenCellPositions.add(relativeCellPos);
35068
- const cellMapRect = map.findCell(relativeCellPos);
35069
- const cellDom = editor.view.nodeDOM(tableInfo.start + relativeCellPos);
35070
- const tableCell = cellDom instanceof HTMLTableCellElement ? cellDom : null;
35071
- if (tableCell) {
35072
- const cellRect = tableCell.getBoundingClientRect();
35073
- const start = cellRect.height > 0 ? cellRect.top - surfaceRect.top + surface.scrollTop : tableTop + cellMapRect.top * fallbackHeight;
35074
- const size = metricOrFallback(cellRect.height, fallbackHeight * Math.max(1, cellMapRect.bottom - cellMapRect.top));
35075
- visualRows.push({
35076
- index: cellMapRect.top,
35077
- cellPos: tableInfo.start + relativeCellPos,
35078
- start,
35079
- size,
35080
- center: start + size / 2
35081
- });
35082
- }
35083
- }
35084
- if (visualRows.length > 0) {
35085
- return visualRows.sort((a, b) => a.index - b.index);
35086
- }
35087
- return rows.map((tableRow, index) => {
35088
- const rowRect = tableRow.getBoundingClientRect();
35089
- const anchorCell = tableRow.cells.item(0) ?? cornerCell;
35090
- const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top + surface.scrollTop : tableTop + index * fallbackHeight;
35091
- const size = metricOrFallback(rowRect.height, fallbackHeight);
35092
- return {
35093
- index,
35094
- cellPos: editor.view.posAtDOM(anchorCell, 0),
35095
- start,
35096
- size,
35097
- center: start + size / 2
35098
- };
35099
- });
35100
- }
35101
- function buildTableControlLayout(editor, surface, cell) {
35102
- const row = cell.closest("tr");
35103
- const table = cell.closest("table");
35104
- if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) {
35105
- return null;
35106
- }
35107
- const rows = Array.from(table.rows).filter((item) => item instanceof HTMLTableRowElement);
35108
- const cornerCell = getLastCell(table);
35109
- const cellPos = editor.view.posAtDOM(cell, 0);
35110
- const tableInfo = findTableInfo(editor, cellPos);
35111
- if (rows.length === 0 || !tableInfo || !(cornerCell instanceof HTMLTableCellElement)) {
35112
- return null;
35113
- }
35114
- const map = TableMap2.get(tableInfo.node);
35115
- const surfaceRect = surface.getBoundingClientRect();
35116
- const tableRect = table.getBoundingClientRect();
35117
- const wrapperElement = table.closest(".tableWrapper");
35118
- const wrapper = wrapperElement instanceof HTMLElement ? wrapperElement : null;
35119
- const wrapperRect = wrapper?.getBoundingClientRect() ?? tableRect;
35120
- const tableLeft = tableRect.left - surfaceRect.left + surface.scrollLeft;
35121
- const tableTop = tableRect.top - surfaceRect.top + surface.scrollTop;
35122
- const avgRowHeight = metricOrFallback(tableRect.height / rows.length, FALLBACK_TABLE_ROW_HEIGHT);
35123
- const avgColumnWidth = metricOrFallback(tableRect.width / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35124
- const tableWidth = metricOrFallback(tableRect.width, avgColumnWidth * map.width);
35125
- const tableHeight = metricOrFallback(tableRect.height, avgRowHeight * rows.length);
35126
- const wrapperLeft = wrapperRect.left - surfaceRect.left + surface.scrollLeft;
35127
- const wrapperTop = wrapperRect.top - surfaceRect.top + surface.scrollTop;
35128
- const wrapperWidth = metricOrFallback(wrapperRect.width, tableWidth);
35129
- const wrapperHeight = metricOrFallback(wrapperRect.height, tableHeight);
35130
- const viewportWidth = metricOrFallback(wrapper?.clientWidth ?? wrapperRect.width, tableWidth);
35131
- const viewportHeight = metricOrFallback(wrapper?.clientHeight ?? wrapperRect.height, tableHeight);
35132
- const verticalScrollbarWidth = Math.max(0, Math.round(wrapperWidth - viewportWidth));
35133
- const horizontalScrollbarHeight = Math.max(0, Math.round(wrapperHeight - viewportHeight));
35134
- const rowHandles = buildLogicalRowMetrics({
35135
- editor,
35136
- surface,
35137
- surfaceRect,
35138
- tableInfo,
35139
- rows,
35140
- tableTop,
35141
- tableHeight,
35142
- cornerCell
35143
- });
35144
- const columnHandles = buildLogicalColumnMetrics({
35145
- editor,
35146
- surface,
35147
- surfaceRect,
35148
- tableElement: table,
35149
- tableInfo,
35150
- tableLeft,
35151
- tableWidth
35152
- });
35153
- const activeCellRelativePos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35154
- const activeCellRect = activeCellRelativePos != null ? map.findCell(activeCellRelativePos) : { left: cell.cellIndex, top: row.rowIndex };
35155
- const normalizedCellPos = activeCellRelativePos != null ? tableInfo.start + activeCellRelativePos : cellPos;
35156
- return {
35157
- cellPos: normalizedCellPos,
35158
- cornerCellPos: tableInfo.start + map.positionAt(map.height - 1, map.width - 1, tableInfo.node),
35159
- activeRowIndex: activeCellRect.top,
35160
- activeColumnIndex: activeCellRect.left,
35161
- tableLeft,
35162
- tableTop,
35163
- tableWidth,
35164
- tableHeight,
35165
- wrapperLeft,
35166
- wrapperTop,
35167
- wrapperWidth,
35168
- wrapperHeight,
35169
- viewportWidth,
35170
- viewportHeight,
35171
- horizontalScrollbarHeight,
35172
- verticalScrollbarWidth,
35173
- avgRowHeight,
35174
- avgColumnWidth,
35175
- rowHandles,
35176
- columnHandles
35177
- };
35178
- }
35179
-
35180
36374
  // src/components/UEditor/table-controls.tsx
35181
36375
  var import_jsx_runtime92 = require("react/jsx-runtime");
35182
36376
  var TABLE_MENU_TOP_OFFSET = 10;