@underverse-ui/underverse 1.0.148 → 1.0.150

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -25681,6 +25681,8 @@ import { Extension as Extension2 } from "@tiptap/core";
25681
25681
  import { Plugin as Plugin2 } from "@tiptap/pm/state";
25682
25682
 
25683
25683
  // src/components/UEditor/clipboard-tables.ts
25684
+ var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
25685
+ var DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
25684
25686
  function getClipboardData(dataTransfer, type) {
25685
25687
  try {
25686
25688
  return dataTransfer.getData(type) ?? "";
@@ -25701,6 +25703,411 @@ function extractClipboardHtmlFragment(html) {
25701
25703
  function normalizeClipboardCellText(value) {
25702
25704
  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();
25703
25705
  }
25706
+ function parseStyleDeclarations(styleText) {
25707
+ const declarations = /* @__PURE__ */ new Map();
25708
+ if (!styleText) return declarations;
25709
+ for (const declaration of styleText.split(";")) {
25710
+ const separatorIndex = declaration.indexOf(":");
25711
+ if (separatorIndex <= 0) continue;
25712
+ const property = declaration.slice(0, separatorIndex).trim().toLowerCase();
25713
+ const value = cleanStyleValue(declaration.slice(separatorIndex + 1));
25714
+ if (!property || !value) continue;
25715
+ declarations.set(property, value);
25716
+ }
25717
+ return declarations;
25718
+ }
25719
+ function mergeStyleDeclarations(...sources) {
25720
+ const declarations = /* @__PURE__ */ new Map();
25721
+ for (const source of sources) {
25722
+ if (!source) continue;
25723
+ for (const [property, value] of source.entries()) {
25724
+ declarations.set(property, value);
25725
+ }
25726
+ }
25727
+ return declarations;
25728
+ }
25729
+ function extractCssClassNames(selectorText) {
25730
+ const classNames = /* @__PURE__ */ new Set();
25731
+ const classNamePattern = /\.([_a-zA-Z-][\w-]*)/g;
25732
+ let match;
25733
+ while ((match = classNamePattern.exec(selectorText)) !== null) {
25734
+ classNames.add(match[1]);
25735
+ }
25736
+ return classNames;
25737
+ }
25738
+ function parseClipboardCssClassStyles(doc) {
25739
+ const styleMap = /* @__PURE__ */ new Map();
25740
+ for (const styleElement of Array.from(doc.querySelectorAll("style"))) {
25741
+ const cssText = (styleElement.textContent ?? "").replace(/<!--|-->/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
25742
+ const rulePattern = /([^{}]+)\{([^{}]*)\}/g;
25743
+ let match;
25744
+ while ((match = rulePattern.exec(cssText)) !== null) {
25745
+ const classNames = extractCssClassNames(match[1]);
25746
+ if (classNames.size === 0) continue;
25747
+ const declarations = parseStyleDeclarations(match[2]);
25748
+ if (declarations.size === 0) continue;
25749
+ for (const className of classNames) {
25750
+ styleMap.set(className, mergeStyleDeclarations(styleMap.get(className), declarations));
25751
+ }
25752
+ }
25753
+ }
25754
+ return styleMap;
25755
+ }
25756
+ function getElementStyleDeclarations(element, styleMap) {
25757
+ const classDeclarations = Array.from(element.classList).map((className) => styleMap.get(className));
25758
+ const inlineDeclarations = parseStyleDeclarations(element.getAttribute("style"));
25759
+ return mergeStyleDeclarations(...classDeclarations, inlineDeclarations);
25760
+ }
25761
+ function cleanStyleValue(value) {
25762
+ const normalized = value?.trim();
25763
+ if (!normalized) return null;
25764
+ if (/[\0<>;{}]/.test(normalized)) return null;
25765
+ if (/\b(?:expression|url|(?:repeating-)?(?:linear|radial|conic)-gradient)\s*\(/i.test(normalized)) return null;
25766
+ return normalized;
25767
+ }
25768
+ function normalizeColorValue(value) {
25769
+ const normalized = cleanStyleValue(value);
25770
+ if (!normalized) return null;
25771
+ if (/^(?:auto|inherit|initial|none|transparent|unset)$/i.test(normalized)) return null;
25772
+ return normalized;
25773
+ }
25774
+ function normalizeTextColorValue(value) {
25775
+ const normalized = normalizeColorValue(value);
25776
+ if (!normalized) return null;
25777
+ if (/^(?:automatic|windowtext|black|#000|#000000|rgb\(\s*0\s*,\s*0\s*,\s*0\s*\))$/i.test(normalized)) {
25778
+ return DEFAULT_HTML_TABLE_TEXT_COLOR;
25779
+ }
25780
+ return normalized;
25781
+ }
25782
+ function isWhiteColor(value) {
25783
+ if (!value) return false;
25784
+ return /^(?:white|#fff|#ffffff|rgb\(\s*255\s*,\s*255\s*,\s*255\s*\))$/i.test(value.trim());
25785
+ }
25786
+ function parseCssColorRgb(value) {
25787
+ const normalized = normalizeColorValue(value);
25788
+ if (!normalized) return null;
25789
+ const lowerColor = normalized.toLowerCase();
25790
+ if (lowerColor === "white") return { r: 255, g: 255, b: 255 };
25791
+ if (lowerColor === "black") return { r: 0, g: 0, b: 0 };
25792
+ const hexMatch = lowerColor.match(/^#([\da-f]{3}|[\da-f]{6})$/i);
25793
+ if (hexMatch) {
25794
+ const hex = hexMatch[1];
25795
+ const fullHex = hex.length === 3 ? hex.split("").map((part) => part + part).join("") : hex;
25796
+ return {
25797
+ r: Number.parseInt(fullHex.slice(0, 2), 16),
25798
+ g: Number.parseInt(fullHex.slice(2, 4), 16),
25799
+ b: Number.parseInt(fullHex.slice(4, 6), 16)
25800
+ };
25801
+ }
25802
+ const rgbMatch = lowerColor.match(/^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)/);
25803
+ if (rgbMatch) {
25804
+ return {
25805
+ r: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[1]))),
25806
+ g: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[2]))),
25807
+ b: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[3])))
25808
+ };
25809
+ }
25810
+ return null;
25811
+ }
25812
+ function getRelativeLuminance(value) {
25813
+ const rgb = parseCssColorRgb(value);
25814
+ if (!rgb) return null;
25815
+ const toLinear = (channel) => {
25816
+ const normalized = channel / 255;
25817
+ return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
25818
+ };
25819
+ return 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);
25820
+ }
25821
+ function isLightTextColor(value) {
25822
+ const luminance = getRelativeLuminance(value);
25823
+ return luminance !== null && luminance >= 0.72;
25824
+ }
25825
+ function isDarkReadableBackground(value) {
25826
+ const luminance = getRelativeLuminance(value);
25827
+ return luminance !== null && luminance <= 0.45;
25828
+ }
25829
+ function splitCssTokens(value) {
25830
+ const tokens = [];
25831
+ let current = "";
25832
+ let depth = 0;
25833
+ for (const char of value) {
25834
+ if (char === "(") depth += 1;
25835
+ if (char === ")") depth = Math.max(0, depth - 1);
25836
+ if (/\s/.test(char) && depth === 0) {
25837
+ if (current) {
25838
+ tokens.push(current);
25839
+ current = "";
25840
+ }
25841
+ continue;
25842
+ }
25843
+ current += char;
25844
+ }
25845
+ if (current) tokens.push(current);
25846
+ return tokens;
25847
+ }
25848
+ function extractColorFromShorthand(value) {
25849
+ const normalized = cleanStyleValue(value);
25850
+ if (!normalized) return null;
25851
+ const explicitColor = normalized.match(/#[\da-f]{3,8}\b|rgba?\([^)]+\)|hsla?\([^)]+\)/i);
25852
+ if (explicitColor) return explicitColor[0];
25853
+ const ignoredKeywords = /* @__PURE__ */ new Set([
25854
+ "border-box",
25855
+ "center",
25856
+ "contain",
25857
+ "content-box",
25858
+ "cover",
25859
+ "fixed",
25860
+ "inherit",
25861
+ "initial",
25862
+ "left",
25863
+ "local",
25864
+ "none",
25865
+ "no-repeat",
25866
+ "padding-box",
25867
+ "repeat",
25868
+ "repeat-x",
25869
+ "repeat-y",
25870
+ "right",
25871
+ "scroll",
25872
+ "top",
25873
+ "transparent",
25874
+ "unset"
25875
+ ]);
25876
+ return splitCssTokens(normalized).find((token) => !ignoredKeywords.has(token.toLowerCase())) ?? null;
25877
+ }
25878
+ function getBackgroundColor(styles) {
25879
+ return normalizeColorValue(styles.get("background-color")) ?? normalizeColorValue(extractColorFromShorthand(styles.get("background")));
25880
+ }
25881
+ var BORDER_STYLES = /* @__PURE__ */ new Set([
25882
+ "dashed",
25883
+ "dotted",
25884
+ "double",
25885
+ "groove",
25886
+ "hidden",
25887
+ "inset",
25888
+ "none",
25889
+ "outset",
25890
+ "ridge",
25891
+ "solid"
25892
+ ]);
25893
+ var BORDER_WIDTH_KEYWORDS = /* @__PURE__ */ new Set(["medium", "thick", "thin"]);
25894
+ function normalizeBorderStyle(value) {
25895
+ const normalized = cleanStyleValue(value);
25896
+ if (!normalized) return null;
25897
+ const styles = splitCssTokens(normalized).filter((token) => BORDER_STYLES.has(token.toLowerCase()));
25898
+ const usefulStyles = styles.filter((style) => !/^(?:hidden|none)$/i.test(style));
25899
+ return usefulStyles.length > 0 ? usefulStyles.join(" ") : null;
25900
+ }
25901
+ function normalizeBorderWidth(value) {
25902
+ const normalized = cleanStyleValue(value);
25903
+ if (!normalized) return null;
25904
+ const widths = splitCssTokens(normalized).filter((token) => {
25905
+ const lowerToken = token.toLowerCase();
25906
+ return BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token);
25907
+ });
25908
+ return widths.length > 0 ? widths.join(" ") : null;
25909
+ }
25910
+ function parseBorderShorthand(value) {
25911
+ const normalized = cleanStyleValue(value);
25912
+ if (!normalized) return null;
25913
+ const tokens = splitCssTokens(normalized);
25914
+ let borderStyle = null;
25915
+ let borderWidth = null;
25916
+ const colorTokens = [];
25917
+ for (const token of tokens) {
25918
+ const lowerToken = token.toLowerCase();
25919
+ if (!borderStyle && BORDER_STYLES.has(lowerToken)) {
25920
+ borderStyle = lowerToken;
25921
+ continue;
25922
+ }
25923
+ if (!borderWidth && (BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token))) {
25924
+ borderWidth = token;
25925
+ continue;
25926
+ }
25927
+ colorTokens.push(token);
25928
+ }
25929
+ if (borderStyle && /^(?:hidden|none)$/i.test(borderStyle)) return null;
25930
+ return {
25931
+ borderColor: normalizeColorValue(colorTokens.join(" ")),
25932
+ borderStyle,
25933
+ borderWidth
25934
+ };
25935
+ }
25936
+ function getFirstParsedBorder(styles) {
25937
+ for (const property of ["border", "border-top", "border-right", "border-bottom", "border-left"]) {
25938
+ const border = parseBorderShorthand(styles.get(property));
25939
+ if (border) return border;
25940
+ }
25941
+ return null;
25942
+ }
25943
+ function getBorderAttrs(styles) {
25944
+ const parsedBorder = getFirstParsedBorder(styles);
25945
+ return {
25946
+ borderColor: normalizeColorValue(styles.get("border-color")) ?? parsedBorder?.borderColor ?? void 0,
25947
+ borderStyle: normalizeBorderStyle(styles.get("border-style")) ?? parsedBorder?.borderStyle ?? void 0,
25948
+ borderWidth: normalizeBorderWidth(styles.get("border-width")) ?? parsedBorder?.borderWidth ?? void 0
25949
+ };
25950
+ }
25951
+ function parsePositiveInteger(value, max = 100) {
25952
+ if (!value) return null;
25953
+ const parsed = Number.parseInt(value, 10);
25954
+ if (!Number.isFinite(parsed) || parsed < 1) return null;
25955
+ return Math.min(parsed, max);
25956
+ }
25957
+ function parseCssSize(value) {
25958
+ const normalized = cleanStyleValue(value);
25959
+ if (!normalized) return null;
25960
+ const match = normalized.match(/^(\d+(?:\.\d+)?)(px|pt)?$/i);
25961
+ if (!match) return null;
25962
+ const amount = Number.parseFloat(match[1]);
25963
+ if (!Number.isFinite(amount) || amount <= 0) return null;
25964
+ return Math.round(match[2]?.toLowerCase() === "pt" ? amount * (4 / 3) : amount);
25965
+ }
25966
+ function getCellWidth(cell, styles, colspan) {
25967
+ if (colspan !== 1) return null;
25968
+ const width = parseCssSize(cell.getAttribute("data-colwidth") ?? cell.getAttribute("width") ?? styles.get("width"));
25969
+ return width ? [width] : null;
25970
+ }
25971
+ function getTableRowAttrs(row, styles) {
25972
+ const rowHeight = parseCssSize(
25973
+ row.getAttribute("data-row-height") ?? row.getAttribute("height") ?? styles.get("height")
25974
+ );
25975
+ return rowHeight ? { rowHeight } : void 0;
25976
+ }
25977
+ function getTableCellAttrs(cell, styles, defaultBackgroundColor) {
25978
+ const colspan = parsePositiveInteger(cell.getAttribute("colspan")) ?? 1;
25979
+ const rowspan = parsePositiveInteger(cell.getAttribute("rowspan")) ?? 1;
25980
+ const backgroundColor = getBackgroundColor(styles) ?? normalizeColorValue(cell.getAttribute("data-background-color")) ?? normalizeColorValue(cell.getAttribute("bgcolor")) ?? defaultBackgroundColor;
25981
+ const borderAttrs = getBorderAttrs(styles);
25982
+ const colwidth = getCellWidth(cell, styles, colspan);
25983
+ const attrs = {};
25984
+ if (backgroundColor) attrs.backgroundColor = backgroundColor;
25985
+ if (borderAttrs.borderColor) attrs.borderColor = borderAttrs.borderColor;
25986
+ if (borderAttrs.borderStyle) attrs.borderStyle = borderAttrs.borderStyle;
25987
+ if (borderAttrs.borderWidth) attrs.borderWidth = borderAttrs.borderWidth;
25988
+ if (colspan > 1) attrs.colspan = colspan;
25989
+ if (rowspan > 1) attrs.rowspan = rowspan;
25990
+ if (colwidth) attrs.colwidth = colwidth;
25991
+ return Object.keys(attrs).length > 0 ? attrs : void 0;
25992
+ }
25993
+ function marksEqual(left, right) {
25994
+ return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
25995
+ }
25996
+ function mergeMarks(base2, additions) {
25997
+ const next = [...base2 ?? []];
25998
+ for (const addition of additions ?? []) {
25999
+ const existingIndex = next.findIndex((mark) => mark.type === addition.type);
26000
+ if (existingIndex >= 0) {
26001
+ const existingMark = next[existingIndex];
26002
+ next[existingIndex] = {
26003
+ ...existingMark,
26004
+ attrs: {
26005
+ ...existingMark.attrs ?? {},
26006
+ ...addition.attrs ?? {}
26007
+ }
26008
+ };
26009
+ continue;
26010
+ }
26011
+ next.push(addition);
26012
+ }
26013
+ return next.length > 0 ? next : void 0;
26014
+ }
26015
+ function getMarkColor(marks, markType) {
26016
+ const mark = marks?.find((candidate) => candidate.type === markType);
26017
+ const color = mark?.attrs?.color;
26018
+ return typeof color === "string" ? color : null;
26019
+ }
26020
+ function replaceTextStyleColor(marks, color) {
26021
+ let replaced = false;
26022
+ const next = (marks ?? []).map((mark) => {
26023
+ if (mark.type !== "textStyle") return mark;
26024
+ replaced = true;
26025
+ return {
26026
+ ...mark,
26027
+ attrs: {
26028
+ ...mark.attrs ?? {},
26029
+ color
26030
+ }
26031
+ };
26032
+ });
26033
+ if (!replaced) {
26034
+ next.unshift({ type: "textStyle", attrs: { color } });
26035
+ }
26036
+ return next;
26037
+ }
26038
+ function ensureReadableSpreadsheetSegments(segments, cellBackgroundColor) {
26039
+ return segments.map((segment) => {
26040
+ const textColor = getMarkColor(segment.marks, "textStyle");
26041
+ if (!isLightTextColor(textColor)) return segment;
26042
+ const inlineBackgroundColor = getMarkColor(segment.marks, "highlight");
26043
+ if (isDarkReadableBackground(inlineBackgroundColor) || isDarkReadableBackground(cellBackgroundColor)) {
26044
+ return segment;
26045
+ }
26046
+ return {
26047
+ ...segment,
26048
+ marks: replaceTextStyleColor(segment.marks, DEFAULT_HTML_TABLE_TEXT_COLOR)
26049
+ };
26050
+ });
26051
+ }
26052
+ function getElementInlineMarks(element, styles) {
26053
+ const marks = [];
26054
+ const tagName = element.tagName;
26055
+ const color = normalizeTextColorValue(styles.get("color") ?? element.getAttribute("color"));
26056
+ const backgroundColor = getBackgroundColor(styles);
26057
+ const fontWeight = styles.get("font-weight")?.toLowerCase();
26058
+ const fontStyle = styles.get("font-style")?.toLowerCase();
26059
+ const textDecoration = styles.get("text-decoration")?.toLowerCase();
26060
+ if (color) {
26061
+ marks.push({ type: "textStyle", attrs: { color } });
26062
+ }
26063
+ if (backgroundColor && !isWhiteColor(backgroundColor)) {
26064
+ marks.push({ type: "highlight", attrs: { color: backgroundColor } });
26065
+ }
26066
+ if (tagName === "B" || tagName === "STRONG" || fontWeight === "bold" || /^\d+$/.test(fontWeight ?? "") && Number(fontWeight) >= 600) {
26067
+ marks.push({ type: "bold" });
26068
+ }
26069
+ if (tagName === "I" || tagName === "EM" || fontStyle === "italic") {
26070
+ marks.push({ type: "italic" });
26071
+ }
26072
+ if (tagName === "U" || textDecoration?.includes("underline")) {
26073
+ marks.push({ type: "underline" });
26074
+ }
26075
+ return marks.length > 0 ? marks : void 0;
26076
+ }
26077
+ function appendTextSegment(segments, segment) {
26078
+ if (!segment.text) return;
26079
+ const lastSegment = segments[segments.length - 1];
26080
+ if (lastSegment && marksEqual(lastSegment.marks, segment.marks)) {
26081
+ lastSegment.text += segment.text;
26082
+ return;
26083
+ }
26084
+ segments.push(segment);
26085
+ }
26086
+ function segmentsEndWithNewline(segments) {
26087
+ return segments.length > 0 && segments[segments.length - 1].text.endsWith("\n");
26088
+ }
26089
+ function normalizeClipboardTextSegments(segments) {
26090
+ const normalizedSegments = [];
26091
+ for (const segment of segments) {
26092
+ appendTextSegment(normalizedSegments, {
26093
+ text: segment.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " "),
26094
+ marks: segment.marks
26095
+ });
26096
+ }
26097
+ while (normalizedSegments.length > 0) {
26098
+ const firstSegment = normalizedSegments[0];
26099
+ firstSegment.text = firstSegment.text.replace(/^\s+/, "");
26100
+ if (firstSegment.text) break;
26101
+ normalizedSegments.shift();
26102
+ }
26103
+ while (normalizedSegments.length > 0) {
26104
+ const lastSegment = normalizedSegments[normalizedSegments.length - 1];
26105
+ lastSegment.text = lastSegment.text.replace(/\s+$/, "");
26106
+ if (lastSegment.text) break;
26107
+ normalizedSegments.pop();
26108
+ }
26109
+ return normalizedSegments;
26110
+ }
25704
26111
  function getClipboardCellText(node) {
25705
26112
  if (node.nodeType === Node.TEXT_NODE) {
25706
26113
  return node.textContent ?? "";
@@ -25718,57 +26125,205 @@ function getClipboardCellText(node) {
25718
26125
  }
25719
26126
  return childText;
25720
26127
  }
25721
- function getHtmlTableRows(table) {
26128
+ function getClipboardCellSegments(node, styleMap, inheritedMarks) {
26129
+ if (node.nodeType === Node.TEXT_NODE) {
26130
+ return [{ text: node.textContent ?? "", marks: inheritedMarks }];
26131
+ }
26132
+ if (!(node instanceof HTMLElement)) {
26133
+ return [];
26134
+ }
26135
+ if (node.tagName === "BR") {
26136
+ return [{ text: "\n", marks: inheritedMarks }];
26137
+ }
26138
+ const styles = getElementStyleDeclarations(node, styleMap);
26139
+ const marks = mergeMarks(inheritedMarks, getElementInlineMarks(node, styles));
26140
+ const segments = [];
26141
+ for (const childNode of Array.from(node.childNodes)) {
26142
+ for (const segment of getClipboardCellSegments(childNode, styleMap, marks)) {
26143
+ appendTextSegment(segments, segment);
26144
+ }
26145
+ }
26146
+ if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && segments.length > 0 && !segmentsEndWithNewline(segments)) {
26147
+ appendTextSegment(segments, { text: "\n" });
26148
+ }
26149
+ return segments;
26150
+ }
26151
+ function getClipboardCellChildSegments(cell, styleMap, inheritedMarks) {
26152
+ const segments = [];
26153
+ for (const childNode of Array.from(cell.childNodes)) {
26154
+ for (const segment of getClipboardCellSegments(childNode, styleMap, inheritedMarks)) {
26155
+ appendTextSegment(segments, segment);
26156
+ }
26157
+ }
26158
+ return normalizeClipboardTextSegments(segments);
26159
+ }
26160
+ function getHtmlTableRows(table, styleMap) {
25722
26161
  const rows = Array.from(table.querySelectorAll("tr")).map(
25723
- (row) => Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement).map((cell) => ({
25724
- text: normalizeClipboardCellText(getClipboardCellText(cell)),
25725
- isHeader: cell.tagName === "TH"
25726
- }))
26162
+ (row) => ({
26163
+ attrs: getTableRowAttrs(row, getElementStyleDeclarations(row, styleMap)),
26164
+ cells: Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement).map((cell) => {
26165
+ const styles = getElementStyleDeclarations(cell, styleMap);
26166
+ const textColor = normalizeTextColorValue(styles.get("color")) ?? DEFAULT_HTML_TABLE_TEXT_COLOR;
26167
+ const inheritedMarks = [{ type: "textStyle", attrs: { color: textColor } }];
26168
+ const attrs = getTableCellAttrs(cell, styles, DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR);
26169
+ const segments = ensureReadableSpreadsheetSegments(
26170
+ getClipboardCellChildSegments(cell, styleMap, inheritedMarks),
26171
+ attrs?.backgroundColor
26172
+ );
26173
+ return {
26174
+ text: normalizeClipboardCellText(getClipboardCellText(cell)),
26175
+ isHeader: cell.tagName === "TH",
26176
+ attrs,
26177
+ segments: segments.length > 0 ? segments : void 0,
26178
+ textColor
26179
+ };
26180
+ })
26181
+ })
25727
26182
  );
25728
- return rows.filter((row) => row.length > 0);
26183
+ return rows.filter((row) => row.cells.length > 0);
26184
+ }
26185
+ function createTextMarks(cell) {
26186
+ return cell.textColor ? [{ type: "textStyle", attrs: { color: cell.textColor } }] : void 0;
25729
26187
  }
25730
- function createParagraphContent(text) {
26188
+ function createParagraphContent(text, marks) {
25731
26189
  return text ? {
25732
26190
  type: "paragraph",
25733
- content: [{ type: "text", text }]
26191
+ content: [{ type: "text", text, ...marks ? { marks } : {} }]
25734
26192
  } : { type: "paragraph" };
25735
26193
  }
26194
+ function createParagraphContentFromSegments(segments) {
26195
+ const paragraphs = [[]];
26196
+ for (const segment of segments) {
26197
+ const parts = segment.text.split("\n");
26198
+ parts.forEach((part, index) => {
26199
+ if (part) {
26200
+ paragraphs[paragraphs.length - 1].push({ text: part, marks: segment.marks });
26201
+ }
26202
+ if (index < parts.length - 1) {
26203
+ paragraphs.push([]);
26204
+ }
26205
+ });
26206
+ }
26207
+ return paragraphs.map((paragraphSegments) => {
26208
+ const content = paragraphSegments.map((segment) => ({
26209
+ type: "text",
26210
+ text: segment.text,
26211
+ ...segment.marks ? { marks: segment.marks } : {}
26212
+ }));
26213
+ return content.length > 0 ? { type: "paragraph", content } : { type: "paragraph" };
26214
+ });
26215
+ }
25736
26216
  function createTableCellContent(cell) {
25737
26217
  const lines = cell.text.split("\n");
25738
- const paragraphs = (lines.length > 0 ? lines : [""]).map(createParagraphContent);
26218
+ const marks = createTextMarks(cell);
26219
+ const paragraphs = cell.segments && cell.segments.length > 0 ? createParagraphContentFromSegments(cell.segments) : (lines.length > 0 ? lines : [""]).map((line) => createParagraphContent(line, marks));
25739
26220
  return {
25740
26221
  type: cell.isHeader ? "tableHeader" : "tableCell",
26222
+ ...cell.attrs ? { attrs: cell.attrs } : {},
25741
26223
  content: paragraphs.length > 0 ? paragraphs : [{ type: "paragraph" }]
25742
26224
  };
25743
26225
  }
25744
- function createTableContent(rows, minColumnCount = 1) {
25745
- const tableRows = rows.filter((row) => row.length > 0);
26226
+ function getRowspanLimitedCell(cell, remainingRowCount) {
26227
+ const attrs = cell.attrs;
26228
+ if (!attrs?.rowspan || attrs.rowspan <= remainingRowCount) return cell;
26229
+ if (remainingRowCount <= 1) {
26230
+ const { rowspan: _rowspan, ...nextAttrs } = attrs;
26231
+ return {
26232
+ ...cell,
26233
+ attrs: Object.keys(nextAttrs).length > 0 ? nextAttrs : void 0
26234
+ };
26235
+ }
26236
+ return {
26237
+ ...cell,
26238
+ attrs: {
26239
+ ...attrs,
26240
+ rowspan: remainingRowCount
26241
+ }
26242
+ };
26243
+ }
26244
+ function normalizeTableRows(rows) {
26245
+ const positionedRows = [];
26246
+ let rowspans = [];
26247
+ let columnCount = 0;
26248
+ rows.forEach((row, rowIndex) => {
26249
+ const coveredColumns = rowspans.map((span) => span > 0);
26250
+ const nextRowspans = rowspans.map((span) => Math.max(0, span - 1));
26251
+ const positionedCells = [];
26252
+ let columnIndex = 0;
26253
+ for (const rawCell of row.cells) {
26254
+ while (coveredColumns[columnIndex]) columnIndex += 1;
26255
+ const remainingRowCount = rows.length - rowIndex;
26256
+ const cell = getRowspanLimitedCell(rawCell, remainingRowCount);
26257
+ const colspan = Math.max(1, cell.attrs?.colspan ?? 1);
26258
+ const rowspan = Math.max(1, cell.attrs?.rowspan ?? 1);
26259
+ positionedCells.push({ startColumn: columnIndex, colspan, cell });
26260
+ if (rowspan > 1) {
26261
+ for (let offset = 0; offset < colspan; offset += 1) {
26262
+ const spannedColumn = columnIndex + offset;
26263
+ nextRowspans[spannedColumn] = Math.max(nextRowspans[spannedColumn] ?? 0, rowspan - 1);
26264
+ }
26265
+ }
26266
+ columnIndex += colspan;
26267
+ }
26268
+ const lastCoveredColumn = coveredColumns.reduce((lastIndex, covered, index) => covered ? index : lastIndex, -1);
26269
+ const lastFutureRowspanColumn = nextRowspans.reduce((lastIndex, span, index) => span > 0 ? index : lastIndex, -1);
26270
+ columnCount = Math.max(columnCount, columnIndex, lastCoveredColumn + 1, lastFutureRowspanColumn + 1);
26271
+ positionedRows.push({
26272
+ attrs: row.attrs,
26273
+ cells: positionedCells,
26274
+ coveredColumns
26275
+ });
26276
+ rowspans = nextRowspans;
26277
+ });
26278
+ return { positionedRows, columnCount };
26279
+ }
26280
+ function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
26281
+ const content = [];
26282
+ const cellByStartColumn = new Map(row.cells.map((cell) => [cell.startColumn, cell]));
26283
+ let columnIndex = 0;
26284
+ while (columnIndex < columnCount) {
26285
+ if (row.coveredColumns[columnIndex]) {
26286
+ columnIndex += 1;
26287
+ continue;
26288
+ }
26289
+ const positionedCell = cellByStartColumn.get(columnIndex);
26290
+ if (positionedCell) {
26291
+ content.push(createTableCellContent(positionedCell.cell));
26292
+ columnIndex += positionedCell.colspan;
26293
+ continue;
26294
+ }
26295
+ content.push(createTableCellContent({ text: "", isHeader: false, attrs: fillerCellAttrs }));
26296
+ columnIndex += 1;
26297
+ }
26298
+ return content;
26299
+ }
26300
+ function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
26301
+ const tableRows = rows.filter((row) => row.cells.length > 0);
25746
26302
  if (tableRows.length === 0) return null;
25747
- const columnCount = tableRows.reduce((max, row) => Math.max(max, row.length), 0);
26303
+ const { positionedRows, columnCount } = normalizeTableRows(tableRows);
25748
26304
  if (columnCount < minColumnCount) return null;
25749
26305
  return {
25750
26306
  type: "table",
25751
- content: tableRows.map((row) => {
25752
- const normalizedRow = Array.from(
25753
- { length: columnCount },
25754
- (_, index) => row[index] ?? { text: "", isHeader: false }
25755
- );
25756
- return {
25757
- type: "tableRow",
25758
- content: normalizedRow.map(createTableCellContent)
25759
- };
25760
- })
26307
+ content: positionedRows.map((row) => ({
26308
+ type: "tableRow",
26309
+ ...row.attrs ? { attrs: row.attrs } : {},
26310
+ content: createNormalizedRowContent(row, columnCount, fillerCellAttrs)
26311
+ }))
25761
26312
  };
25762
26313
  }
25763
26314
  function getClipboardTableContent(dataTransfer) {
25764
26315
  const html = getClipboardData(dataTransfer, "text/html");
25765
26316
  if (!/<table(?:\s|>)/i.test(html)) return null;
25766
26317
  if (typeof DOMParser === "undefined") return null;
26318
+ const doc = new DOMParser().parseFromString(html, "text/html");
25767
26319
  const fragment = extractClipboardHtmlFragment(html);
25768
- const doc = new DOMParser().parseFromString(fragment, "text/html");
25769
- const table = doc.querySelector("table");
26320
+ const fragmentDoc = new DOMParser().parseFromString(fragment, "text/html");
26321
+ const styleMap = parseClipboardCssClassStyles(doc);
26322
+ const table = fragmentDoc.querySelector("table") ?? doc.querySelector("table");
25770
26323
  if (!(table instanceof HTMLTableElement)) return null;
25771
- return createTableContent(getHtmlTableRows(table));
26324
+ return createTableContent(getHtmlTableRows(table, styleMap), 1, {
26325
+ backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
26326
+ });
25772
26327
  }
25773
26328
  function parseClipboardTsvRows(text) {
25774
26329
  const rows = [];
@@ -25825,7 +26380,9 @@ function getClipboardTsvTableContent(dataTransfer) {
25825
26380
  if (!text.includes(" ")) return null;
25826
26381
  const rows = parseClipboardTsvRows(text);
25827
26382
  return createTableContent(
25828
- rows.map((row) => row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))),
26383
+ rows.map((row) => ({
26384
+ cells: row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))
26385
+ })),
25829
26386
  2
25830
26387
  );
25831
26388
  }
@@ -36277,8 +36834,7 @@ import {
36277
36834
  Trash2 as Trash25,
36278
36835
  Underline as UnderlineIcon3,
36279
36836
  Undo as UndoIcon2,
36280
- Upload as Upload4,
36281
- X as X19
36837
+ Upload as Upload4
36282
36838
  } from "lucide-react";
36283
36839
  import { Fragment as Fragment35, jsx as jsx92, jsxs as jsxs77 } from "react/jsx-runtime";
36284
36840
  function MenuTableInsertGrid({
@@ -36776,14 +37332,6 @@ var MenuBar = ({
36776
37332
  }
36777
37333
  openPreviewDialog();
36778
37334
  };
36779
- React81.useEffect(() => {
36780
- if (!showPreviewDialog) return void 0;
36781
- const previousOverflow = document.body.style.overflow;
36782
- document.body.style.overflow = "hidden";
36783
- return () => {
36784
- document.body.style.overflow = previousOverflow;
36785
- };
36786
- }, [showPreviewDialog]);
36787
37335
  const applySourceHtml = () => {
36788
37336
  editor.chain().focus().setContent(sourceHtml).run();
36789
37337
  setShowSourceDialog(false);
@@ -37001,38 +37549,33 @@ var MenuBar = ({
37001
37549
  ] })
37002
37550
  }
37003
37551
  ),
37004
- showPreviewDialog && /* @__PURE__ */ jsxs77("div", { className: "fixed inset-0 z-9999 flex flex-col bg-background text-foreground", children: [
37005
- /* @__PURE__ */ jsxs77("div", { className: "flex shrink-0 items-center justify-between border-b border-border bg-card px-4 py-3", children: [
37006
- /* @__PURE__ */ jsx92("h2", { className: "text-base font-semibold", children: t("menubar.preview") }),
37007
- /* @__PURE__ */ jsx92(
37008
- "button",
37552
+ /* @__PURE__ */ jsx92(
37553
+ Modal_default,
37554
+ {
37555
+ isOpen: showPreviewDialog,
37556
+ onClose: () => setShowPreviewDialog(false),
37557
+ title: t("menubar.preview"),
37558
+ size: "full",
37559
+ width: "min(1180px, calc(100vw - 2rem))",
37560
+ height: "min(860px, calc(100vh - 2rem))",
37561
+ className: "flex flex-col overflow-hidden rounded-xl md:rounded-2xl",
37562
+ contentClassName: "flex min-h-0 flex-1 overflow-hidden p-0",
37563
+ children: /* @__PURE__ */ jsx92(
37564
+ "div",
37009
37565
  {
37010
- type: "button",
37011
- onClick: () => setShowPreviewDialog(false),
37012
- "aria-label": t("menubar.closeDialog"),
37013
- className: cn(
37014
- "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors",
37015
- "hover:bg-accent hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1"
37016
- ),
37017
- children: /* @__PURE__ */ jsx92(X19, { className: "h-4 w-4", "aria-hidden": "true" })
37566
+ "data-testid": "preview-content",
37567
+ className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-4 md:px-8",
37568
+ children: editor.isEmpty ? /* @__PURE__ */ jsx92("p", { className: "text-muted-foreground text-sm", children: t("menubar.previewEmpty") }) : /* @__PURE__ */ jsx92(
37569
+ "div",
37570
+ {
37571
+ className: UEDITOR_PROSEMIRROR_CLASS_NAME,
37572
+ dangerouslySetInnerHTML: { __html: editor.getHTML() }
37573
+ }
37574
+ )
37018
37575
  }
37019
37576
  )
37020
- ] }),
37021
- /* @__PURE__ */ jsx92(
37022
- "div",
37023
- {
37024
- "data-testid": "preview-content",
37025
- className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-4 md:px-8",
37026
- children: editor.isEmpty ? /* @__PURE__ */ jsx92("p", { className: "text-muted-foreground text-sm", children: t("menubar.previewEmpty") }) : /* @__PURE__ */ jsx92(
37027
- "div",
37028
- {
37029
- className: UEDITOR_PROSEMIRROR_CLASS_NAME,
37030
- dangerouslySetInnerHTML: { __html: editor.getHTML() }
37031
- }
37032
- )
37033
- }
37034
- )
37035
- ] })
37577
+ }
37578
+ )
37036
37579
  ] });
37037
37580
  };
37038
37581