@underverse-ui/underverse 1.0.149 → 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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@underverse-ui/underverse",
3
- "version": "1.0.149",
3
+ "version": "1.0.150",
4
4
  "sourceEntry": "src/index.ts",
5
5
  "totalExports": 232,
6
6
  "exports": [
package/dist/index.cjs CHANGED
@@ -25836,6 +25836,8 @@ var import_core5 = require("@tiptap/core");
25836
25836
  var import_state2 = require("@tiptap/pm/state");
25837
25837
 
25838
25838
  // src/components/UEditor/clipboard-tables.ts
25839
+ var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
25840
+ var DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
25839
25841
  function getClipboardData(dataTransfer, type) {
25840
25842
  try {
25841
25843
  return dataTransfer.getData(type) ?? "";
@@ -25856,6 +25858,411 @@ function extractClipboardHtmlFragment(html) {
25856
25858
  function normalizeClipboardCellText(value) {
25857
25859
  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
25860
  }
25861
+ function parseStyleDeclarations(styleText) {
25862
+ const declarations = /* @__PURE__ */ new Map();
25863
+ if (!styleText) return declarations;
25864
+ for (const declaration of styleText.split(";")) {
25865
+ const separatorIndex = declaration.indexOf(":");
25866
+ if (separatorIndex <= 0) continue;
25867
+ const property = declaration.slice(0, separatorIndex).trim().toLowerCase();
25868
+ const value = cleanStyleValue(declaration.slice(separatorIndex + 1));
25869
+ if (!property || !value) continue;
25870
+ declarations.set(property, value);
25871
+ }
25872
+ return declarations;
25873
+ }
25874
+ function mergeStyleDeclarations(...sources) {
25875
+ const declarations = /* @__PURE__ */ new Map();
25876
+ for (const source of sources) {
25877
+ if (!source) continue;
25878
+ for (const [property, value] of source.entries()) {
25879
+ declarations.set(property, value);
25880
+ }
25881
+ }
25882
+ return declarations;
25883
+ }
25884
+ function extractCssClassNames(selectorText) {
25885
+ const classNames = /* @__PURE__ */ new Set();
25886
+ const classNamePattern = /\.([_a-zA-Z-][\w-]*)/g;
25887
+ let match;
25888
+ while ((match = classNamePattern.exec(selectorText)) !== null) {
25889
+ classNames.add(match[1]);
25890
+ }
25891
+ return classNames;
25892
+ }
25893
+ function parseClipboardCssClassStyles(doc) {
25894
+ const styleMap = /* @__PURE__ */ new Map();
25895
+ for (const styleElement of Array.from(doc.querySelectorAll("style"))) {
25896
+ const cssText = (styleElement.textContent ?? "").replace(/<!--|-->/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
25897
+ const rulePattern = /([^{}]+)\{([^{}]*)\}/g;
25898
+ let match;
25899
+ while ((match = rulePattern.exec(cssText)) !== null) {
25900
+ const classNames = extractCssClassNames(match[1]);
25901
+ if (classNames.size === 0) continue;
25902
+ const declarations = parseStyleDeclarations(match[2]);
25903
+ if (declarations.size === 0) continue;
25904
+ for (const className of classNames) {
25905
+ styleMap.set(className, mergeStyleDeclarations(styleMap.get(className), declarations));
25906
+ }
25907
+ }
25908
+ }
25909
+ return styleMap;
25910
+ }
25911
+ function getElementStyleDeclarations(element, styleMap) {
25912
+ const classDeclarations = Array.from(element.classList).map((className) => styleMap.get(className));
25913
+ const inlineDeclarations = parseStyleDeclarations(element.getAttribute("style"));
25914
+ return mergeStyleDeclarations(...classDeclarations, inlineDeclarations);
25915
+ }
25916
+ function cleanStyleValue(value) {
25917
+ const normalized = value?.trim();
25918
+ if (!normalized) return null;
25919
+ if (/[\0<>;{}]/.test(normalized)) return null;
25920
+ if (/\b(?:expression|url|(?:repeating-)?(?:linear|radial|conic)-gradient)\s*\(/i.test(normalized)) return null;
25921
+ return normalized;
25922
+ }
25923
+ function normalizeColorValue(value) {
25924
+ const normalized = cleanStyleValue(value);
25925
+ if (!normalized) return null;
25926
+ if (/^(?:auto|inherit|initial|none|transparent|unset)$/i.test(normalized)) return null;
25927
+ return normalized;
25928
+ }
25929
+ function normalizeTextColorValue(value) {
25930
+ const normalized = normalizeColorValue(value);
25931
+ if (!normalized) return null;
25932
+ if (/^(?:automatic|windowtext|black|#000|#000000|rgb\(\s*0\s*,\s*0\s*,\s*0\s*\))$/i.test(normalized)) {
25933
+ return DEFAULT_HTML_TABLE_TEXT_COLOR;
25934
+ }
25935
+ return normalized;
25936
+ }
25937
+ function isWhiteColor(value) {
25938
+ if (!value) return false;
25939
+ return /^(?:white|#fff|#ffffff|rgb\(\s*255\s*,\s*255\s*,\s*255\s*\))$/i.test(value.trim());
25940
+ }
25941
+ function parseCssColorRgb(value) {
25942
+ const normalized = normalizeColorValue(value);
25943
+ if (!normalized) return null;
25944
+ const lowerColor = normalized.toLowerCase();
25945
+ if (lowerColor === "white") return { r: 255, g: 255, b: 255 };
25946
+ if (lowerColor === "black") return { r: 0, g: 0, b: 0 };
25947
+ const hexMatch = lowerColor.match(/^#([\da-f]{3}|[\da-f]{6})$/i);
25948
+ if (hexMatch) {
25949
+ const hex = hexMatch[1];
25950
+ const fullHex = hex.length === 3 ? hex.split("").map((part) => part + part).join("") : hex;
25951
+ return {
25952
+ r: Number.parseInt(fullHex.slice(0, 2), 16),
25953
+ g: Number.parseInt(fullHex.slice(2, 4), 16),
25954
+ b: Number.parseInt(fullHex.slice(4, 6), 16)
25955
+ };
25956
+ }
25957
+ const rgbMatch = lowerColor.match(/^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)/);
25958
+ if (rgbMatch) {
25959
+ return {
25960
+ r: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[1]))),
25961
+ g: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[2]))),
25962
+ b: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[3])))
25963
+ };
25964
+ }
25965
+ return null;
25966
+ }
25967
+ function getRelativeLuminance(value) {
25968
+ const rgb = parseCssColorRgb(value);
25969
+ if (!rgb) return null;
25970
+ const toLinear = (channel) => {
25971
+ const normalized = channel / 255;
25972
+ return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
25973
+ };
25974
+ return 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);
25975
+ }
25976
+ function isLightTextColor(value) {
25977
+ const luminance = getRelativeLuminance(value);
25978
+ return luminance !== null && luminance >= 0.72;
25979
+ }
25980
+ function isDarkReadableBackground(value) {
25981
+ const luminance = getRelativeLuminance(value);
25982
+ return luminance !== null && luminance <= 0.45;
25983
+ }
25984
+ function splitCssTokens(value) {
25985
+ const tokens = [];
25986
+ let current = "";
25987
+ let depth = 0;
25988
+ for (const char of value) {
25989
+ if (char === "(") depth += 1;
25990
+ if (char === ")") depth = Math.max(0, depth - 1);
25991
+ if (/\s/.test(char) && depth === 0) {
25992
+ if (current) {
25993
+ tokens.push(current);
25994
+ current = "";
25995
+ }
25996
+ continue;
25997
+ }
25998
+ current += char;
25999
+ }
26000
+ if (current) tokens.push(current);
26001
+ return tokens;
26002
+ }
26003
+ function extractColorFromShorthand(value) {
26004
+ const normalized = cleanStyleValue(value);
26005
+ if (!normalized) return null;
26006
+ const explicitColor = normalized.match(/#[\da-f]{3,8}\b|rgba?\([^)]+\)|hsla?\([^)]+\)/i);
26007
+ if (explicitColor) return explicitColor[0];
26008
+ const ignoredKeywords = /* @__PURE__ */ new Set([
26009
+ "border-box",
26010
+ "center",
26011
+ "contain",
26012
+ "content-box",
26013
+ "cover",
26014
+ "fixed",
26015
+ "inherit",
26016
+ "initial",
26017
+ "left",
26018
+ "local",
26019
+ "none",
26020
+ "no-repeat",
26021
+ "padding-box",
26022
+ "repeat",
26023
+ "repeat-x",
26024
+ "repeat-y",
26025
+ "right",
26026
+ "scroll",
26027
+ "top",
26028
+ "transparent",
26029
+ "unset"
26030
+ ]);
26031
+ return splitCssTokens(normalized).find((token) => !ignoredKeywords.has(token.toLowerCase())) ?? null;
26032
+ }
26033
+ function getBackgroundColor(styles) {
26034
+ return normalizeColorValue(styles.get("background-color")) ?? normalizeColorValue(extractColorFromShorthand(styles.get("background")));
26035
+ }
26036
+ var BORDER_STYLES = /* @__PURE__ */ new Set([
26037
+ "dashed",
26038
+ "dotted",
26039
+ "double",
26040
+ "groove",
26041
+ "hidden",
26042
+ "inset",
26043
+ "none",
26044
+ "outset",
26045
+ "ridge",
26046
+ "solid"
26047
+ ]);
26048
+ var BORDER_WIDTH_KEYWORDS = /* @__PURE__ */ new Set(["medium", "thick", "thin"]);
26049
+ function normalizeBorderStyle(value) {
26050
+ const normalized = cleanStyleValue(value);
26051
+ if (!normalized) return null;
26052
+ const styles = splitCssTokens(normalized).filter((token) => BORDER_STYLES.has(token.toLowerCase()));
26053
+ const usefulStyles = styles.filter((style) => !/^(?:hidden|none)$/i.test(style));
26054
+ return usefulStyles.length > 0 ? usefulStyles.join(" ") : null;
26055
+ }
26056
+ function normalizeBorderWidth(value) {
26057
+ const normalized = cleanStyleValue(value);
26058
+ if (!normalized) return null;
26059
+ const widths = splitCssTokens(normalized).filter((token) => {
26060
+ const lowerToken = token.toLowerCase();
26061
+ return BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token);
26062
+ });
26063
+ return widths.length > 0 ? widths.join(" ") : null;
26064
+ }
26065
+ function parseBorderShorthand(value) {
26066
+ const normalized = cleanStyleValue(value);
26067
+ if (!normalized) return null;
26068
+ const tokens = splitCssTokens(normalized);
26069
+ let borderStyle = null;
26070
+ let borderWidth = null;
26071
+ const colorTokens = [];
26072
+ for (const token of tokens) {
26073
+ const lowerToken = token.toLowerCase();
26074
+ if (!borderStyle && BORDER_STYLES.has(lowerToken)) {
26075
+ borderStyle = lowerToken;
26076
+ continue;
26077
+ }
26078
+ if (!borderWidth && (BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token))) {
26079
+ borderWidth = token;
26080
+ continue;
26081
+ }
26082
+ colorTokens.push(token);
26083
+ }
26084
+ if (borderStyle && /^(?:hidden|none)$/i.test(borderStyle)) return null;
26085
+ return {
26086
+ borderColor: normalizeColorValue(colorTokens.join(" ")),
26087
+ borderStyle,
26088
+ borderWidth
26089
+ };
26090
+ }
26091
+ function getFirstParsedBorder(styles) {
26092
+ for (const property of ["border", "border-top", "border-right", "border-bottom", "border-left"]) {
26093
+ const border = parseBorderShorthand(styles.get(property));
26094
+ if (border) return border;
26095
+ }
26096
+ return null;
26097
+ }
26098
+ function getBorderAttrs(styles) {
26099
+ const parsedBorder = getFirstParsedBorder(styles);
26100
+ return {
26101
+ borderColor: normalizeColorValue(styles.get("border-color")) ?? parsedBorder?.borderColor ?? void 0,
26102
+ borderStyle: normalizeBorderStyle(styles.get("border-style")) ?? parsedBorder?.borderStyle ?? void 0,
26103
+ borderWidth: normalizeBorderWidth(styles.get("border-width")) ?? parsedBorder?.borderWidth ?? void 0
26104
+ };
26105
+ }
26106
+ function parsePositiveInteger(value, max = 100) {
26107
+ if (!value) return null;
26108
+ const parsed = Number.parseInt(value, 10);
26109
+ if (!Number.isFinite(parsed) || parsed < 1) return null;
26110
+ return Math.min(parsed, max);
26111
+ }
26112
+ function parseCssSize(value) {
26113
+ const normalized = cleanStyleValue(value);
26114
+ if (!normalized) return null;
26115
+ const match = normalized.match(/^(\d+(?:\.\d+)?)(px|pt)?$/i);
26116
+ if (!match) return null;
26117
+ const amount = Number.parseFloat(match[1]);
26118
+ if (!Number.isFinite(amount) || amount <= 0) return null;
26119
+ return Math.round(match[2]?.toLowerCase() === "pt" ? amount * (4 / 3) : amount);
26120
+ }
26121
+ function getCellWidth(cell, styles, colspan) {
26122
+ if (colspan !== 1) return null;
26123
+ const width = parseCssSize(cell.getAttribute("data-colwidth") ?? cell.getAttribute("width") ?? styles.get("width"));
26124
+ return width ? [width] : null;
26125
+ }
26126
+ function getTableRowAttrs(row, styles) {
26127
+ const rowHeight = parseCssSize(
26128
+ row.getAttribute("data-row-height") ?? row.getAttribute("height") ?? styles.get("height")
26129
+ );
26130
+ return rowHeight ? { rowHeight } : void 0;
26131
+ }
26132
+ function getTableCellAttrs(cell, styles, defaultBackgroundColor) {
26133
+ const colspan = parsePositiveInteger(cell.getAttribute("colspan")) ?? 1;
26134
+ const rowspan = parsePositiveInteger(cell.getAttribute("rowspan")) ?? 1;
26135
+ const backgroundColor = getBackgroundColor(styles) ?? normalizeColorValue(cell.getAttribute("data-background-color")) ?? normalizeColorValue(cell.getAttribute("bgcolor")) ?? defaultBackgroundColor;
26136
+ const borderAttrs = getBorderAttrs(styles);
26137
+ const colwidth = getCellWidth(cell, styles, colspan);
26138
+ const attrs = {};
26139
+ if (backgroundColor) attrs.backgroundColor = backgroundColor;
26140
+ if (borderAttrs.borderColor) attrs.borderColor = borderAttrs.borderColor;
26141
+ if (borderAttrs.borderStyle) attrs.borderStyle = borderAttrs.borderStyle;
26142
+ if (borderAttrs.borderWidth) attrs.borderWidth = borderAttrs.borderWidth;
26143
+ if (colspan > 1) attrs.colspan = colspan;
26144
+ if (rowspan > 1) attrs.rowspan = rowspan;
26145
+ if (colwidth) attrs.colwidth = colwidth;
26146
+ return Object.keys(attrs).length > 0 ? attrs : void 0;
26147
+ }
26148
+ function marksEqual(left, right) {
26149
+ return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
26150
+ }
26151
+ function mergeMarks(base2, additions) {
26152
+ const next = [...base2 ?? []];
26153
+ for (const addition of additions ?? []) {
26154
+ const existingIndex = next.findIndex((mark) => mark.type === addition.type);
26155
+ if (existingIndex >= 0) {
26156
+ const existingMark = next[existingIndex];
26157
+ next[existingIndex] = {
26158
+ ...existingMark,
26159
+ attrs: {
26160
+ ...existingMark.attrs ?? {},
26161
+ ...addition.attrs ?? {}
26162
+ }
26163
+ };
26164
+ continue;
26165
+ }
26166
+ next.push(addition);
26167
+ }
26168
+ return next.length > 0 ? next : void 0;
26169
+ }
26170
+ function getMarkColor(marks, markType) {
26171
+ const mark = marks?.find((candidate) => candidate.type === markType);
26172
+ const color = mark?.attrs?.color;
26173
+ return typeof color === "string" ? color : null;
26174
+ }
26175
+ function replaceTextStyleColor(marks, color) {
26176
+ let replaced = false;
26177
+ const next = (marks ?? []).map((mark) => {
26178
+ if (mark.type !== "textStyle") return mark;
26179
+ replaced = true;
26180
+ return {
26181
+ ...mark,
26182
+ attrs: {
26183
+ ...mark.attrs ?? {},
26184
+ color
26185
+ }
26186
+ };
26187
+ });
26188
+ if (!replaced) {
26189
+ next.unshift({ type: "textStyle", attrs: { color } });
26190
+ }
26191
+ return next;
26192
+ }
26193
+ function ensureReadableSpreadsheetSegments(segments, cellBackgroundColor) {
26194
+ return segments.map((segment) => {
26195
+ const textColor = getMarkColor(segment.marks, "textStyle");
26196
+ if (!isLightTextColor(textColor)) return segment;
26197
+ const inlineBackgroundColor = getMarkColor(segment.marks, "highlight");
26198
+ if (isDarkReadableBackground(inlineBackgroundColor) || isDarkReadableBackground(cellBackgroundColor)) {
26199
+ return segment;
26200
+ }
26201
+ return {
26202
+ ...segment,
26203
+ marks: replaceTextStyleColor(segment.marks, DEFAULT_HTML_TABLE_TEXT_COLOR)
26204
+ };
26205
+ });
26206
+ }
26207
+ function getElementInlineMarks(element, styles) {
26208
+ const marks = [];
26209
+ const tagName = element.tagName;
26210
+ const color = normalizeTextColorValue(styles.get("color") ?? element.getAttribute("color"));
26211
+ const backgroundColor = getBackgroundColor(styles);
26212
+ const fontWeight = styles.get("font-weight")?.toLowerCase();
26213
+ const fontStyle = styles.get("font-style")?.toLowerCase();
26214
+ const textDecoration = styles.get("text-decoration")?.toLowerCase();
26215
+ if (color) {
26216
+ marks.push({ type: "textStyle", attrs: { color } });
26217
+ }
26218
+ if (backgroundColor && !isWhiteColor(backgroundColor)) {
26219
+ marks.push({ type: "highlight", attrs: { color: backgroundColor } });
26220
+ }
26221
+ if (tagName === "B" || tagName === "STRONG" || fontWeight === "bold" || /^\d+$/.test(fontWeight ?? "") && Number(fontWeight) >= 600) {
26222
+ marks.push({ type: "bold" });
26223
+ }
26224
+ if (tagName === "I" || tagName === "EM" || fontStyle === "italic") {
26225
+ marks.push({ type: "italic" });
26226
+ }
26227
+ if (tagName === "U" || textDecoration?.includes("underline")) {
26228
+ marks.push({ type: "underline" });
26229
+ }
26230
+ return marks.length > 0 ? marks : void 0;
26231
+ }
26232
+ function appendTextSegment(segments, segment) {
26233
+ if (!segment.text) return;
26234
+ const lastSegment = segments[segments.length - 1];
26235
+ if (lastSegment && marksEqual(lastSegment.marks, segment.marks)) {
26236
+ lastSegment.text += segment.text;
26237
+ return;
26238
+ }
26239
+ segments.push(segment);
26240
+ }
26241
+ function segmentsEndWithNewline(segments) {
26242
+ return segments.length > 0 && segments[segments.length - 1].text.endsWith("\n");
26243
+ }
26244
+ function normalizeClipboardTextSegments(segments) {
26245
+ const normalizedSegments = [];
26246
+ for (const segment of segments) {
26247
+ appendTextSegment(normalizedSegments, {
26248
+ text: segment.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " "),
26249
+ marks: segment.marks
26250
+ });
26251
+ }
26252
+ while (normalizedSegments.length > 0) {
26253
+ const firstSegment = normalizedSegments[0];
26254
+ firstSegment.text = firstSegment.text.replace(/^\s+/, "");
26255
+ if (firstSegment.text) break;
26256
+ normalizedSegments.shift();
26257
+ }
26258
+ while (normalizedSegments.length > 0) {
26259
+ const lastSegment = normalizedSegments[normalizedSegments.length - 1];
26260
+ lastSegment.text = lastSegment.text.replace(/\s+$/, "");
26261
+ if (lastSegment.text) break;
26262
+ normalizedSegments.pop();
26263
+ }
26264
+ return normalizedSegments;
26265
+ }
25859
26266
  function getClipboardCellText(node) {
25860
26267
  if (node.nodeType === Node.TEXT_NODE) {
25861
26268
  return node.textContent ?? "";
@@ -25873,57 +26280,205 @@ function getClipboardCellText(node) {
25873
26280
  }
25874
26281
  return childText;
25875
26282
  }
25876
- function getHtmlTableRows(table) {
26283
+ function getClipboardCellSegments(node, styleMap, inheritedMarks) {
26284
+ if (node.nodeType === Node.TEXT_NODE) {
26285
+ return [{ text: node.textContent ?? "", marks: inheritedMarks }];
26286
+ }
26287
+ if (!(node instanceof HTMLElement)) {
26288
+ return [];
26289
+ }
26290
+ if (node.tagName === "BR") {
26291
+ return [{ text: "\n", marks: inheritedMarks }];
26292
+ }
26293
+ const styles = getElementStyleDeclarations(node, styleMap);
26294
+ const marks = mergeMarks(inheritedMarks, getElementInlineMarks(node, styles));
26295
+ const segments = [];
26296
+ for (const childNode of Array.from(node.childNodes)) {
26297
+ for (const segment of getClipboardCellSegments(childNode, styleMap, marks)) {
26298
+ appendTextSegment(segments, segment);
26299
+ }
26300
+ }
26301
+ if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && segments.length > 0 && !segmentsEndWithNewline(segments)) {
26302
+ appendTextSegment(segments, { text: "\n" });
26303
+ }
26304
+ return segments;
26305
+ }
26306
+ function getClipboardCellChildSegments(cell, styleMap, inheritedMarks) {
26307
+ const segments = [];
26308
+ for (const childNode of Array.from(cell.childNodes)) {
26309
+ for (const segment of getClipboardCellSegments(childNode, styleMap, inheritedMarks)) {
26310
+ appendTextSegment(segments, segment);
26311
+ }
26312
+ }
26313
+ return normalizeClipboardTextSegments(segments);
26314
+ }
26315
+ function getHtmlTableRows(table, styleMap) {
25877
26316
  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
- }))
26317
+ (row) => ({
26318
+ attrs: getTableRowAttrs(row, getElementStyleDeclarations(row, styleMap)),
26319
+ cells: Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement).map((cell) => {
26320
+ const styles = getElementStyleDeclarations(cell, styleMap);
26321
+ const textColor = normalizeTextColorValue(styles.get("color")) ?? DEFAULT_HTML_TABLE_TEXT_COLOR;
26322
+ const inheritedMarks = [{ type: "textStyle", attrs: { color: textColor } }];
26323
+ const attrs = getTableCellAttrs(cell, styles, DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR);
26324
+ const segments = ensureReadableSpreadsheetSegments(
26325
+ getClipboardCellChildSegments(cell, styleMap, inheritedMarks),
26326
+ attrs?.backgroundColor
26327
+ );
26328
+ return {
26329
+ text: normalizeClipboardCellText(getClipboardCellText(cell)),
26330
+ isHeader: cell.tagName === "TH",
26331
+ attrs,
26332
+ segments: segments.length > 0 ? segments : void 0,
26333
+ textColor
26334
+ };
26335
+ })
26336
+ })
25882
26337
  );
25883
- return rows.filter((row) => row.length > 0);
26338
+ return rows.filter((row) => row.cells.length > 0);
26339
+ }
26340
+ function createTextMarks(cell) {
26341
+ return cell.textColor ? [{ type: "textStyle", attrs: { color: cell.textColor } }] : void 0;
25884
26342
  }
25885
- function createParagraphContent(text) {
26343
+ function createParagraphContent(text, marks) {
25886
26344
  return text ? {
25887
26345
  type: "paragraph",
25888
- content: [{ type: "text", text }]
26346
+ content: [{ type: "text", text, ...marks ? { marks } : {} }]
25889
26347
  } : { type: "paragraph" };
25890
26348
  }
26349
+ function createParagraphContentFromSegments(segments) {
26350
+ const paragraphs = [[]];
26351
+ for (const segment of segments) {
26352
+ const parts = segment.text.split("\n");
26353
+ parts.forEach((part, index) => {
26354
+ if (part) {
26355
+ paragraphs[paragraphs.length - 1].push({ text: part, marks: segment.marks });
26356
+ }
26357
+ if (index < parts.length - 1) {
26358
+ paragraphs.push([]);
26359
+ }
26360
+ });
26361
+ }
26362
+ return paragraphs.map((paragraphSegments) => {
26363
+ const content = paragraphSegments.map((segment) => ({
26364
+ type: "text",
26365
+ text: segment.text,
26366
+ ...segment.marks ? { marks: segment.marks } : {}
26367
+ }));
26368
+ return content.length > 0 ? { type: "paragraph", content } : { type: "paragraph" };
26369
+ });
26370
+ }
25891
26371
  function createTableCellContent(cell) {
25892
26372
  const lines = cell.text.split("\n");
25893
- const paragraphs = (lines.length > 0 ? lines : [""]).map(createParagraphContent);
26373
+ const marks = createTextMarks(cell);
26374
+ const paragraphs = cell.segments && cell.segments.length > 0 ? createParagraphContentFromSegments(cell.segments) : (lines.length > 0 ? lines : [""]).map((line) => createParagraphContent(line, marks));
25894
26375
  return {
25895
26376
  type: cell.isHeader ? "tableHeader" : "tableCell",
26377
+ ...cell.attrs ? { attrs: cell.attrs } : {},
25896
26378
  content: paragraphs.length > 0 ? paragraphs : [{ type: "paragraph" }]
25897
26379
  };
25898
26380
  }
25899
- function createTableContent(rows, minColumnCount = 1) {
25900
- const tableRows = rows.filter((row) => row.length > 0);
26381
+ function getRowspanLimitedCell(cell, remainingRowCount) {
26382
+ const attrs = cell.attrs;
26383
+ if (!attrs?.rowspan || attrs.rowspan <= remainingRowCount) return cell;
26384
+ if (remainingRowCount <= 1) {
26385
+ const { rowspan: _rowspan, ...nextAttrs } = attrs;
26386
+ return {
26387
+ ...cell,
26388
+ attrs: Object.keys(nextAttrs).length > 0 ? nextAttrs : void 0
26389
+ };
26390
+ }
26391
+ return {
26392
+ ...cell,
26393
+ attrs: {
26394
+ ...attrs,
26395
+ rowspan: remainingRowCount
26396
+ }
26397
+ };
26398
+ }
26399
+ function normalizeTableRows(rows) {
26400
+ const positionedRows = [];
26401
+ let rowspans = [];
26402
+ let columnCount = 0;
26403
+ rows.forEach((row, rowIndex) => {
26404
+ const coveredColumns = rowspans.map((span) => span > 0);
26405
+ const nextRowspans = rowspans.map((span) => Math.max(0, span - 1));
26406
+ const positionedCells = [];
26407
+ let columnIndex = 0;
26408
+ for (const rawCell of row.cells) {
26409
+ while (coveredColumns[columnIndex]) columnIndex += 1;
26410
+ const remainingRowCount = rows.length - rowIndex;
26411
+ const cell = getRowspanLimitedCell(rawCell, remainingRowCount);
26412
+ const colspan = Math.max(1, cell.attrs?.colspan ?? 1);
26413
+ const rowspan = Math.max(1, cell.attrs?.rowspan ?? 1);
26414
+ positionedCells.push({ startColumn: columnIndex, colspan, cell });
26415
+ if (rowspan > 1) {
26416
+ for (let offset = 0; offset < colspan; offset += 1) {
26417
+ const spannedColumn = columnIndex + offset;
26418
+ nextRowspans[spannedColumn] = Math.max(nextRowspans[spannedColumn] ?? 0, rowspan - 1);
26419
+ }
26420
+ }
26421
+ columnIndex += colspan;
26422
+ }
26423
+ const lastCoveredColumn = coveredColumns.reduce((lastIndex, covered, index) => covered ? index : lastIndex, -1);
26424
+ const lastFutureRowspanColumn = nextRowspans.reduce((lastIndex, span, index) => span > 0 ? index : lastIndex, -1);
26425
+ columnCount = Math.max(columnCount, columnIndex, lastCoveredColumn + 1, lastFutureRowspanColumn + 1);
26426
+ positionedRows.push({
26427
+ attrs: row.attrs,
26428
+ cells: positionedCells,
26429
+ coveredColumns
26430
+ });
26431
+ rowspans = nextRowspans;
26432
+ });
26433
+ return { positionedRows, columnCount };
26434
+ }
26435
+ function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
26436
+ const content = [];
26437
+ const cellByStartColumn = new Map(row.cells.map((cell) => [cell.startColumn, cell]));
26438
+ let columnIndex = 0;
26439
+ while (columnIndex < columnCount) {
26440
+ if (row.coveredColumns[columnIndex]) {
26441
+ columnIndex += 1;
26442
+ continue;
26443
+ }
26444
+ const positionedCell = cellByStartColumn.get(columnIndex);
26445
+ if (positionedCell) {
26446
+ content.push(createTableCellContent(positionedCell.cell));
26447
+ columnIndex += positionedCell.colspan;
26448
+ continue;
26449
+ }
26450
+ content.push(createTableCellContent({ text: "", isHeader: false, attrs: fillerCellAttrs }));
26451
+ columnIndex += 1;
26452
+ }
26453
+ return content;
26454
+ }
26455
+ function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
26456
+ const tableRows = rows.filter((row) => row.cells.length > 0);
25901
26457
  if (tableRows.length === 0) return null;
25902
- const columnCount = tableRows.reduce((max, row) => Math.max(max, row.length), 0);
26458
+ const { positionedRows, columnCount } = normalizeTableRows(tableRows);
25903
26459
  if (columnCount < minColumnCount) return null;
25904
26460
  return {
25905
26461
  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
- })
26462
+ content: positionedRows.map((row) => ({
26463
+ type: "tableRow",
26464
+ ...row.attrs ? { attrs: row.attrs } : {},
26465
+ content: createNormalizedRowContent(row, columnCount, fillerCellAttrs)
26466
+ }))
25916
26467
  };
25917
26468
  }
25918
26469
  function getClipboardTableContent(dataTransfer) {
25919
26470
  const html = getClipboardData(dataTransfer, "text/html");
25920
26471
  if (!/<table(?:\s|>)/i.test(html)) return null;
25921
26472
  if (typeof DOMParser === "undefined") return null;
26473
+ const doc = new DOMParser().parseFromString(html, "text/html");
25922
26474
  const fragment = extractClipboardHtmlFragment(html);
25923
- const doc = new DOMParser().parseFromString(fragment, "text/html");
25924
- const table = doc.querySelector("table");
26475
+ const fragmentDoc = new DOMParser().parseFromString(fragment, "text/html");
26476
+ const styleMap = parseClipboardCssClassStyles(doc);
26477
+ const table = fragmentDoc.querySelector("table") ?? doc.querySelector("table");
25925
26478
  if (!(table instanceof HTMLTableElement)) return null;
25926
- return createTableContent(getHtmlTableRows(table));
26479
+ return createTableContent(getHtmlTableRows(table, styleMap), 1, {
26480
+ backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
26481
+ });
25927
26482
  }
25928
26483
  function parseClipboardTsvRows(text) {
25929
26484
  const rows = [];
@@ -25980,7 +26535,9 @@ function getClipboardTsvTableContent(dataTransfer) {
25980
26535
  if (!text.includes(" ")) return null;
25981
26536
  const rows = parseClipboardTsvRows(text);
25982
26537
  return createTableContent(
25983
- rows.map((row) => row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))),
26538
+ rows.map((row) => ({
26539
+ cells: row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))
26540
+ })),
25984
26541
  2
25985
26542
  );
25986
26543
  }