@underverse-ui/underverse 1.0.146 → 1.0.148

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.146",
3
+ "version": "1.0.148",
4
4
  "sourceEntry": "src/index.ts",
5
5
  "totalExports": 232,
6
6
  "exports": [
package/dist/index.cjs CHANGED
@@ -25835,67 +25835,7 @@ var SlashCommand = import_core4.Extension.create({
25835
25835
  var import_core5 = require("@tiptap/core");
25836
25836
  var import_state2 = require("@tiptap/pm/state");
25837
25837
 
25838
- // src/components/UEditor/url-safety.ts
25839
- var LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
25840
- var IMAGE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
25841
- function normalizeUrlInput(raw) {
25842
- return raw.trim().replace(/[\u0000-\u001F\u007F\s]+/g, "");
25843
- }
25844
- function isProtocolRelativeUrl(value) {
25845
- return value.startsWith("//");
25846
- }
25847
- function isRelativeUrl(value) {
25848
- return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
25849
- }
25850
- function isDataImageUrl(value) {
25851
- return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
25852
- }
25853
- function isSafeUEditorUrl(raw, kind) {
25854
- const value = normalizeUrlInput(raw);
25855
- if (!value) return false;
25856
- if (kind === "image" && isDataImageUrl(value)) return true;
25857
- if (isRelativeUrl(value)) return true;
25858
- if (isProtocolRelativeUrl(value)) return false;
25859
- try {
25860
- const parsed = new URL(value);
25861
- return kind === "image" ? IMAGE_PROTOCOLS.has(parsed.protocol) : LINK_PROTOCOLS.has(parsed.protocol);
25862
- } catch {
25863
- return false;
25864
- }
25865
- }
25866
- function sanitizeUEditorUrl(raw, kind) {
25867
- const value = raw.trim();
25868
- if (!value) return "";
25869
- if (isSafeUEditorUrl(value, kind)) return normalizeUrlInput(value);
25870
- if (kind === "link" && !isProtocolRelativeUrl(value) && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
25871
- const withProtocol = `https://${value}`;
25872
- return isSafeUEditorUrl(withProtocol, kind) ? withProtocol : "";
25873
- }
25874
- return "";
25875
- }
25876
-
25877
- // src/components/UEditor/clipboard-images.ts
25878
- var DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE = 10 * 1024 * 1024;
25879
- var DEFAULT_UEDITOR_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif", "image/svg+xml"];
25880
- function getImageFiles(dataTransfer) {
25881
- if (!dataTransfer) return [];
25882
- const itemFiles = [];
25883
- const byKey = /* @__PURE__ */ new Map();
25884
- for (const item of Array.from(dataTransfer.items ?? [])) {
25885
- if (item.kind !== "file") continue;
25886
- if (!item.type.startsWith("image/")) continue;
25887
- const file = item.getAsFile();
25888
- if (!file) continue;
25889
- byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
25890
- }
25891
- itemFiles.push(...Array.from(byKey.values()));
25892
- if (itemFiles.length > 0) return itemFiles;
25893
- for (const file of Array.from(dataTransfer.files ?? [])) {
25894
- if (!file.type.startsWith("image/")) continue;
25895
- byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
25896
- }
25897
- return Array.from(byKey.values());
25898
- }
25838
+ // src/components/UEditor/clipboard-tables.ts
25899
25839
  function getClipboardData(dataTransfer, type) {
25900
25840
  try {
25901
25841
  return dataTransfer.getData(type) ?? "";
@@ -25913,42 +25853,77 @@ function extractClipboardHtmlFragment(html) {
25913
25853
  }
25914
25854
  return html;
25915
25855
  }
25916
- var EMPTY_TABLE_CELL_HTML = "<p></p>";
25917
- var TABLE_MEDIA_SELECTOR = "img,video,audio,iframe,svg,canvas,embed,object";
25918
- var TABLE_BLOCK_SELECTOR = "p,ul,ol,blockquote,pre,h1,h2,h3,h4,h5,h6,hr,table";
25919
- function hasMeaningfulTableCellText(value) {
25920
- return Boolean(value?.replace(/\u00a0/g, " ").trim());
25856
+ function normalizeClipboardCellText(value) {
25857
+ 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();
25921
25858
  }
25922
- function normalizeClipboardTableCell(cell) {
25923
- const hasText = hasMeaningfulTableCellText(cell.textContent);
25924
- const hasBlockContent = Boolean(cell.querySelector(TABLE_BLOCK_SELECTOR));
25925
- const hasMediaContent = Boolean(cell.querySelector(TABLE_MEDIA_SELECTOR));
25926
- if (!hasText && !hasBlockContent && !hasMediaContent) {
25927
- cell.innerHTML = EMPTY_TABLE_CELL_HTML;
25859
+ function getClipboardCellText(node) {
25860
+ if (node.nodeType === Node.TEXT_NODE) {
25861
+ return node.textContent ?? "";
25928
25862
  }
25929
- }
25930
- function normalizeClipboardTable(table) {
25931
- for (const cell of Array.from(table.querySelectorAll("td,th"))) {
25932
- if (!(cell instanceof HTMLTableCellElement)) continue;
25933
- normalizeClipboardTableCell(cell);
25863
+ if (!(node instanceof HTMLElement)) {
25864
+ return "";
25865
+ }
25866
+ if (node.tagName === "BR") {
25867
+ return "\n";
25868
+ }
25869
+ const childText = Array.from(node.childNodes).map(getClipboardCellText).join("");
25870
+ if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && childText && !childText.endsWith("\n")) {
25871
+ return `${childText}
25872
+ `;
25934
25873
  }
25874
+ return childText;
25875
+ }
25876
+ function getHtmlTableRows(table) {
25877
+ 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
+ }))
25882
+ );
25883
+ return rows.filter((row) => row.length > 0);
25884
+ }
25885
+ function createParagraphContent(text) {
25886
+ return text ? {
25887
+ type: "paragraph",
25888
+ content: [{ type: "text", text }]
25889
+ } : { type: "paragraph" };
25935
25890
  }
25936
- function getClipboardTableHtml(dataTransfer) {
25891
+ function createTableCellContent(cell) {
25892
+ const lines = cell.text.split("\n");
25893
+ const paragraphs = (lines.length > 0 ? lines : [""]).map(createParagraphContent);
25894
+ return {
25895
+ type: cell.isHeader ? "tableHeader" : "tableCell",
25896
+ content: paragraphs.length > 0 ? paragraphs : [{ type: "paragraph" }]
25897
+ };
25898
+ }
25899
+ function createTableContent(rows, minColumnCount = 1) {
25900
+ const tableRows = rows.filter((row) => row.length > 0);
25901
+ if (tableRows.length === 0) return null;
25902
+ const columnCount = tableRows.reduce((max, row) => Math.max(max, row.length), 0);
25903
+ if (columnCount < minColumnCount) return null;
25904
+ return {
25905
+ 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
+ })
25916
+ };
25917
+ }
25918
+ function getClipboardTableContent(dataTransfer) {
25937
25919
  const html = getClipboardData(dataTransfer, "text/html");
25938
- if (!/<table(?:\s|>)/i.test(html)) return "";
25920
+ if (!/<table(?:\s|>)/i.test(html)) return null;
25921
+ if (typeof DOMParser === "undefined") return null;
25939
25922
  const fragment = extractClipboardHtmlFragment(html);
25940
- if (typeof DOMParser !== "undefined") {
25941
- const doc = new DOMParser().parseFromString(fragment, "text/html");
25942
- const table = doc.querySelector("table");
25943
- if (table instanceof HTMLTableElement) {
25944
- normalizeClipboardTable(table);
25945
- return table.outerHTML;
25946
- }
25947
- }
25948
- return fragment;
25949
- }
25950
- function escapeHtml(value) {
25951
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
25923
+ const doc = new DOMParser().parseFromString(fragment, "text/html");
25924
+ const table = doc.querySelector("table");
25925
+ if (!(table instanceof HTMLTableElement)) return null;
25926
+ return createTableContent(getHtmlTableRows(table));
25952
25927
  }
25953
25928
  function parseClipboardTsvRows(text) {
25954
25929
  const rows = [];
@@ -26000,22 +25975,76 @@ function parseClipboardTsvRows(text) {
26000
25975
  }
26001
25976
  return rows;
26002
25977
  }
26003
- function renderClipboardTableCellHtml(value) {
26004
- const lines = value.split("\n");
26005
- const paragraphs = lines.length > 0 ? lines : [""];
26006
- return paragraphs.map((line) => `<p>${escapeHtml(line)}</p>`).join("");
26007
- }
26008
- function getClipboardTsvTableHtml(dataTransfer) {
25978
+ function getClipboardTsvTableContent(dataTransfer) {
26009
25979
  const text = getClipboardData(dataTransfer, "text/plain").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
26010
- if (!text.includes(" ")) return "";
25980
+ if (!text.includes(" ")) return null;
26011
25981
  const rows = parseClipboardTsvRows(text);
26012
- if (rows.length === 0 || rows.every((row) => row.length < 2)) return "";
26013
- const columnCount = rows.reduce((max, row) => Math.max(max, row.length), 0);
26014
- const body = rows.map((row) => {
26015
- const normalizedRow = Array.from({ length: columnCount }, (_, index) => row[index] ?? "");
26016
- return `<tr>${normalizedRow.map((cell) => `<td>${renderClipboardTableCellHtml(cell)}</td>`).join("")}</tr>`;
26017
- }).join("");
26018
- return `<table><tbody>${body}</tbody></table>`;
25982
+ return createTableContent(
25983
+ rows.map((row) => row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))),
25984
+ 2
25985
+ );
25986
+ }
25987
+
25988
+ // src/components/UEditor/url-safety.ts
25989
+ var LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
25990
+ var IMAGE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
25991
+ function normalizeUrlInput(raw) {
25992
+ return raw.trim().replace(/[\u0000-\u001F\u007F\s]+/g, "");
25993
+ }
25994
+ function isProtocolRelativeUrl(value) {
25995
+ return value.startsWith("//");
25996
+ }
25997
+ function isRelativeUrl(value) {
25998
+ return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
25999
+ }
26000
+ function isDataImageUrl(value) {
26001
+ return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
26002
+ }
26003
+ function isSafeUEditorUrl(raw, kind) {
26004
+ const value = normalizeUrlInput(raw);
26005
+ if (!value) return false;
26006
+ if (kind === "image" && isDataImageUrl(value)) return true;
26007
+ if (isRelativeUrl(value)) return true;
26008
+ if (isProtocolRelativeUrl(value)) return false;
26009
+ try {
26010
+ const parsed = new URL(value);
26011
+ return kind === "image" ? IMAGE_PROTOCOLS.has(parsed.protocol) : LINK_PROTOCOLS.has(parsed.protocol);
26012
+ } catch {
26013
+ return false;
26014
+ }
26015
+ }
26016
+ function sanitizeUEditorUrl(raw, kind) {
26017
+ const value = raw.trim();
26018
+ if (!value) return "";
26019
+ if (isSafeUEditorUrl(value, kind)) return normalizeUrlInput(value);
26020
+ if (kind === "link" && !isProtocolRelativeUrl(value) && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
26021
+ const withProtocol = `https://${value}`;
26022
+ return isSafeUEditorUrl(withProtocol, kind) ? withProtocol : "";
26023
+ }
26024
+ return "";
26025
+ }
26026
+
26027
+ // src/components/UEditor/clipboard-images.ts
26028
+ var DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE = 10 * 1024 * 1024;
26029
+ var DEFAULT_UEDITOR_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif", "image/svg+xml"];
26030
+ function getImageFiles(dataTransfer) {
26031
+ if (!dataTransfer) return [];
26032
+ const itemFiles = [];
26033
+ const byKey = /* @__PURE__ */ new Map();
26034
+ for (const item of Array.from(dataTransfer.items ?? [])) {
26035
+ if (item.kind !== "file") continue;
26036
+ if (!item.type.startsWith("image/")) continue;
26037
+ const file = item.getAsFile();
26038
+ if (!file) continue;
26039
+ byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
26040
+ }
26041
+ itemFiles.push(...Array.from(byKey.values()));
26042
+ if (itemFiles.length > 0) return itemFiles;
26043
+ for (const file of Array.from(dataTransfer.files ?? [])) {
26044
+ if (!file.type.startsWith("image/")) continue;
26045
+ byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
26046
+ }
26047
+ return Array.from(byKey.values());
26019
26048
  }
26020
26049
  function fileToDataUrl(file) {
26021
26050
  return new Promise((resolve, reject) => {
@@ -26071,16 +26100,16 @@ var ClipboardImages = import_core5.Extension.create({
26071
26100
  props: {
26072
26101
  handlePaste: (_view, event) => {
26073
26102
  if (!event || !event.clipboardData) return false;
26074
- const tableHtml = getClipboardTableHtml(event.clipboardData);
26075
- if (tableHtml) {
26103
+ const tableContent = getClipboardTableContent(event.clipboardData);
26104
+ if (tableContent) {
26076
26105
  event.preventDefault();
26077
- editor.chain().focus().insertContent(tableHtml).run();
26106
+ editor.chain().focus().insertContent(tableContent).run();
26078
26107
  return true;
26079
26108
  }
26080
- const tsvTableHtml = getClipboardTsvTableHtml(event.clipboardData);
26081
- if (tsvTableHtml) {
26109
+ const tsvTableContent = getClipboardTsvTableContent(event.clipboardData);
26110
+ if (tsvTableContent) {
26082
26111
  event.preventDefault();
26083
- editor.chain().focus().insertContent(tsvTableHtml).run();
26112
+ editor.chain().focus().insertContent(tsvTableContent).run();
26084
26113
  return true;
26085
26114
  }
26086
26115
  const files = getImageFiles(event.clipboardData);