@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.
- package/api-reference.json +1 -1
- package/dist/index.cjs +140 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +140 -111
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -25680,67 +25680,7 @@ var SlashCommand = Extension.create({
|
|
|
25680
25680
|
import { Extension as Extension2 } from "@tiptap/core";
|
|
25681
25681
|
import { Plugin as Plugin2 } from "@tiptap/pm/state";
|
|
25682
25682
|
|
|
25683
|
-
// src/components/UEditor/
|
|
25684
|
-
var LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
|
|
25685
|
-
var IMAGE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
25686
|
-
function normalizeUrlInput(raw) {
|
|
25687
|
-
return raw.trim().replace(/[\u0000-\u001F\u007F\s]+/g, "");
|
|
25688
|
-
}
|
|
25689
|
-
function isProtocolRelativeUrl(value) {
|
|
25690
|
-
return value.startsWith("//");
|
|
25691
|
-
}
|
|
25692
|
-
function isRelativeUrl(value) {
|
|
25693
|
-
return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
|
|
25694
|
-
}
|
|
25695
|
-
function isDataImageUrl(value) {
|
|
25696
|
-
return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
|
|
25697
|
-
}
|
|
25698
|
-
function isSafeUEditorUrl(raw, kind) {
|
|
25699
|
-
const value = normalizeUrlInput(raw);
|
|
25700
|
-
if (!value) return false;
|
|
25701
|
-
if (kind === "image" && isDataImageUrl(value)) return true;
|
|
25702
|
-
if (isRelativeUrl(value)) return true;
|
|
25703
|
-
if (isProtocolRelativeUrl(value)) return false;
|
|
25704
|
-
try {
|
|
25705
|
-
const parsed = new URL(value);
|
|
25706
|
-
return kind === "image" ? IMAGE_PROTOCOLS.has(parsed.protocol) : LINK_PROTOCOLS.has(parsed.protocol);
|
|
25707
|
-
} catch {
|
|
25708
|
-
return false;
|
|
25709
|
-
}
|
|
25710
|
-
}
|
|
25711
|
-
function sanitizeUEditorUrl(raw, kind) {
|
|
25712
|
-
const value = raw.trim();
|
|
25713
|
-
if (!value) return "";
|
|
25714
|
-
if (isSafeUEditorUrl(value, kind)) return normalizeUrlInput(value);
|
|
25715
|
-
if (kind === "link" && !isProtocolRelativeUrl(value) && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
|
|
25716
|
-
const withProtocol = `https://${value}`;
|
|
25717
|
-
return isSafeUEditorUrl(withProtocol, kind) ? withProtocol : "";
|
|
25718
|
-
}
|
|
25719
|
-
return "";
|
|
25720
|
-
}
|
|
25721
|
-
|
|
25722
|
-
// src/components/UEditor/clipboard-images.ts
|
|
25723
|
-
var DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
25724
|
-
var DEFAULT_UEDITOR_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif", "image/svg+xml"];
|
|
25725
|
-
function getImageFiles(dataTransfer) {
|
|
25726
|
-
if (!dataTransfer) return [];
|
|
25727
|
-
const itemFiles = [];
|
|
25728
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
25729
|
-
for (const item of Array.from(dataTransfer.items ?? [])) {
|
|
25730
|
-
if (item.kind !== "file") continue;
|
|
25731
|
-
if (!item.type.startsWith("image/")) continue;
|
|
25732
|
-
const file = item.getAsFile();
|
|
25733
|
-
if (!file) continue;
|
|
25734
|
-
byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
|
|
25735
|
-
}
|
|
25736
|
-
itemFiles.push(...Array.from(byKey.values()));
|
|
25737
|
-
if (itemFiles.length > 0) return itemFiles;
|
|
25738
|
-
for (const file of Array.from(dataTransfer.files ?? [])) {
|
|
25739
|
-
if (!file.type.startsWith("image/")) continue;
|
|
25740
|
-
byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
|
|
25741
|
-
}
|
|
25742
|
-
return Array.from(byKey.values());
|
|
25743
|
-
}
|
|
25683
|
+
// src/components/UEditor/clipboard-tables.ts
|
|
25744
25684
|
function getClipboardData(dataTransfer, type) {
|
|
25745
25685
|
try {
|
|
25746
25686
|
return dataTransfer.getData(type) ?? "";
|
|
@@ -25758,42 +25698,77 @@ function extractClipboardHtmlFragment(html) {
|
|
|
25758
25698
|
}
|
|
25759
25699
|
return html;
|
|
25760
25700
|
}
|
|
25761
|
-
|
|
25762
|
-
|
|
25763
|
-
var TABLE_BLOCK_SELECTOR = "p,ul,ol,blockquote,pre,h1,h2,h3,h4,h5,h6,hr,table";
|
|
25764
|
-
function hasMeaningfulTableCellText(value) {
|
|
25765
|
-
return Boolean(value?.replace(/\u00a0/g, " ").trim());
|
|
25701
|
+
function normalizeClipboardCellText(value) {
|
|
25702
|
+
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();
|
|
25766
25703
|
}
|
|
25767
|
-
function
|
|
25768
|
-
|
|
25769
|
-
|
|
25770
|
-
const hasMediaContent = Boolean(cell.querySelector(TABLE_MEDIA_SELECTOR));
|
|
25771
|
-
if (!hasText && !hasBlockContent && !hasMediaContent) {
|
|
25772
|
-
cell.innerHTML = EMPTY_TABLE_CELL_HTML;
|
|
25704
|
+
function getClipboardCellText(node) {
|
|
25705
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
25706
|
+
return node.textContent ?? "";
|
|
25773
25707
|
}
|
|
25774
|
-
|
|
25775
|
-
|
|
25776
|
-
|
|
25777
|
-
|
|
25778
|
-
|
|
25708
|
+
if (!(node instanceof HTMLElement)) {
|
|
25709
|
+
return "";
|
|
25710
|
+
}
|
|
25711
|
+
if (node.tagName === "BR") {
|
|
25712
|
+
return "\n";
|
|
25713
|
+
}
|
|
25714
|
+
const childText = Array.from(node.childNodes).map(getClipboardCellText).join("");
|
|
25715
|
+
if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && childText && !childText.endsWith("\n")) {
|
|
25716
|
+
return `${childText}
|
|
25717
|
+
`;
|
|
25779
25718
|
}
|
|
25719
|
+
return childText;
|
|
25720
|
+
}
|
|
25721
|
+
function getHtmlTableRows(table) {
|
|
25722
|
+
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
|
+
}))
|
|
25727
|
+
);
|
|
25728
|
+
return rows.filter((row) => row.length > 0);
|
|
25729
|
+
}
|
|
25730
|
+
function createParagraphContent(text) {
|
|
25731
|
+
return text ? {
|
|
25732
|
+
type: "paragraph",
|
|
25733
|
+
content: [{ type: "text", text }]
|
|
25734
|
+
} : { type: "paragraph" };
|
|
25780
25735
|
}
|
|
25781
|
-
function
|
|
25736
|
+
function createTableCellContent(cell) {
|
|
25737
|
+
const lines = cell.text.split("\n");
|
|
25738
|
+
const paragraphs = (lines.length > 0 ? lines : [""]).map(createParagraphContent);
|
|
25739
|
+
return {
|
|
25740
|
+
type: cell.isHeader ? "tableHeader" : "tableCell",
|
|
25741
|
+
content: paragraphs.length > 0 ? paragraphs : [{ type: "paragraph" }]
|
|
25742
|
+
};
|
|
25743
|
+
}
|
|
25744
|
+
function createTableContent(rows, minColumnCount = 1) {
|
|
25745
|
+
const tableRows = rows.filter((row) => row.length > 0);
|
|
25746
|
+
if (tableRows.length === 0) return null;
|
|
25747
|
+
const columnCount = tableRows.reduce((max, row) => Math.max(max, row.length), 0);
|
|
25748
|
+
if (columnCount < minColumnCount) return null;
|
|
25749
|
+
return {
|
|
25750
|
+
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
|
+
})
|
|
25761
|
+
};
|
|
25762
|
+
}
|
|
25763
|
+
function getClipboardTableContent(dataTransfer) {
|
|
25782
25764
|
const html = getClipboardData(dataTransfer, "text/html");
|
|
25783
|
-
if (!/<table(?:\s|>)/i.test(html)) return
|
|
25765
|
+
if (!/<table(?:\s|>)/i.test(html)) return null;
|
|
25766
|
+
if (typeof DOMParser === "undefined") return null;
|
|
25784
25767
|
const fragment = extractClipboardHtmlFragment(html);
|
|
25785
|
-
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
|
|
25789
|
-
normalizeClipboardTable(table);
|
|
25790
|
-
return table.outerHTML;
|
|
25791
|
-
}
|
|
25792
|
-
}
|
|
25793
|
-
return fragment;
|
|
25794
|
-
}
|
|
25795
|
-
function escapeHtml(value) {
|
|
25796
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
25768
|
+
const doc = new DOMParser().parseFromString(fragment, "text/html");
|
|
25769
|
+
const table = doc.querySelector("table");
|
|
25770
|
+
if (!(table instanceof HTMLTableElement)) return null;
|
|
25771
|
+
return createTableContent(getHtmlTableRows(table));
|
|
25797
25772
|
}
|
|
25798
25773
|
function parseClipboardTsvRows(text) {
|
|
25799
25774
|
const rows = [];
|
|
@@ -25845,22 +25820,76 @@ function parseClipboardTsvRows(text) {
|
|
|
25845
25820
|
}
|
|
25846
25821
|
return rows;
|
|
25847
25822
|
}
|
|
25848
|
-
function
|
|
25849
|
-
const lines = value.split("\n");
|
|
25850
|
-
const paragraphs = lines.length > 0 ? lines : [""];
|
|
25851
|
-
return paragraphs.map((line) => `<p>${escapeHtml(line)}</p>`).join("");
|
|
25852
|
-
}
|
|
25853
|
-
function getClipboardTsvTableHtml(dataTransfer) {
|
|
25823
|
+
function getClipboardTsvTableContent(dataTransfer) {
|
|
25854
25824
|
const text = getClipboardData(dataTransfer, "text/plain").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
25855
|
-
if (!text.includes(" ")) return
|
|
25825
|
+
if (!text.includes(" ")) return null;
|
|
25856
25826
|
const rows = parseClipboardTsvRows(text);
|
|
25857
|
-
|
|
25858
|
-
|
|
25859
|
-
|
|
25860
|
-
|
|
25861
|
-
|
|
25862
|
-
|
|
25863
|
-
|
|
25827
|
+
return createTableContent(
|
|
25828
|
+
rows.map((row) => row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))),
|
|
25829
|
+
2
|
|
25830
|
+
);
|
|
25831
|
+
}
|
|
25832
|
+
|
|
25833
|
+
// src/components/UEditor/url-safety.ts
|
|
25834
|
+
var LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
|
|
25835
|
+
var IMAGE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
25836
|
+
function normalizeUrlInput(raw) {
|
|
25837
|
+
return raw.trim().replace(/[\u0000-\u001F\u007F\s]+/g, "");
|
|
25838
|
+
}
|
|
25839
|
+
function isProtocolRelativeUrl(value) {
|
|
25840
|
+
return value.startsWith("//");
|
|
25841
|
+
}
|
|
25842
|
+
function isRelativeUrl(value) {
|
|
25843
|
+
return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
|
|
25844
|
+
}
|
|
25845
|
+
function isDataImageUrl(value) {
|
|
25846
|
+
return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
|
|
25847
|
+
}
|
|
25848
|
+
function isSafeUEditorUrl(raw, kind) {
|
|
25849
|
+
const value = normalizeUrlInput(raw);
|
|
25850
|
+
if (!value) return false;
|
|
25851
|
+
if (kind === "image" && isDataImageUrl(value)) return true;
|
|
25852
|
+
if (isRelativeUrl(value)) return true;
|
|
25853
|
+
if (isProtocolRelativeUrl(value)) return false;
|
|
25854
|
+
try {
|
|
25855
|
+
const parsed = new URL(value);
|
|
25856
|
+
return kind === "image" ? IMAGE_PROTOCOLS.has(parsed.protocol) : LINK_PROTOCOLS.has(parsed.protocol);
|
|
25857
|
+
} catch {
|
|
25858
|
+
return false;
|
|
25859
|
+
}
|
|
25860
|
+
}
|
|
25861
|
+
function sanitizeUEditorUrl(raw, kind) {
|
|
25862
|
+
const value = raw.trim();
|
|
25863
|
+
if (!value) return "";
|
|
25864
|
+
if (isSafeUEditorUrl(value, kind)) return normalizeUrlInput(value);
|
|
25865
|
+
if (kind === "link" && !isProtocolRelativeUrl(value) && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
|
|
25866
|
+
const withProtocol = `https://${value}`;
|
|
25867
|
+
return isSafeUEditorUrl(withProtocol, kind) ? withProtocol : "";
|
|
25868
|
+
}
|
|
25869
|
+
return "";
|
|
25870
|
+
}
|
|
25871
|
+
|
|
25872
|
+
// src/components/UEditor/clipboard-images.ts
|
|
25873
|
+
var DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
25874
|
+
var DEFAULT_UEDITOR_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif", "image/svg+xml"];
|
|
25875
|
+
function getImageFiles(dataTransfer) {
|
|
25876
|
+
if (!dataTransfer) return [];
|
|
25877
|
+
const itemFiles = [];
|
|
25878
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
25879
|
+
for (const item of Array.from(dataTransfer.items ?? [])) {
|
|
25880
|
+
if (item.kind !== "file") continue;
|
|
25881
|
+
if (!item.type.startsWith("image/")) continue;
|
|
25882
|
+
const file = item.getAsFile();
|
|
25883
|
+
if (!file) continue;
|
|
25884
|
+
byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
|
|
25885
|
+
}
|
|
25886
|
+
itemFiles.push(...Array.from(byKey.values()));
|
|
25887
|
+
if (itemFiles.length > 0) return itemFiles;
|
|
25888
|
+
for (const file of Array.from(dataTransfer.files ?? [])) {
|
|
25889
|
+
if (!file.type.startsWith("image/")) continue;
|
|
25890
|
+
byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
|
|
25891
|
+
}
|
|
25892
|
+
return Array.from(byKey.values());
|
|
25864
25893
|
}
|
|
25865
25894
|
function fileToDataUrl(file) {
|
|
25866
25895
|
return new Promise((resolve, reject) => {
|
|
@@ -25916,16 +25945,16 @@ var ClipboardImages = Extension2.create({
|
|
|
25916
25945
|
props: {
|
|
25917
25946
|
handlePaste: (_view, event) => {
|
|
25918
25947
|
if (!event || !event.clipboardData) return false;
|
|
25919
|
-
const
|
|
25920
|
-
if (
|
|
25948
|
+
const tableContent = getClipboardTableContent(event.clipboardData);
|
|
25949
|
+
if (tableContent) {
|
|
25921
25950
|
event.preventDefault();
|
|
25922
|
-
editor.chain().focus().insertContent(
|
|
25951
|
+
editor.chain().focus().insertContent(tableContent).run();
|
|
25923
25952
|
return true;
|
|
25924
25953
|
}
|
|
25925
|
-
const
|
|
25926
|
-
if (
|
|
25954
|
+
const tsvTableContent = getClipboardTsvTableContent(event.clipboardData);
|
|
25955
|
+
if (tsvTableContent) {
|
|
25927
25956
|
event.preventDefault();
|
|
25928
|
-
editor.chain().focus().insertContent(
|
|
25957
|
+
editor.chain().focus().insertContent(tsvTableContent).run();
|
|
25929
25958
|
return true;
|
|
25930
25959
|
}
|
|
25931
25960
|
const files = getImageFiles(event.clipboardData);
|