Vanessa 2025-07-17 17:26:58 +08:00
parent 280f97ee81
commit 0d3dec71f3
4 changed files with 38 additions and 49 deletions

View file

@ -5,17 +5,22 @@ import {Constants} from "../../constants";
export const encodeBase64 = (text: string): string => {
const encoder = new TextEncoder();
const bytes = encoder.encode(text);
const binaryString = String.fromCharCode(...bytes);
const base64 = btoa(binaryString);
return base64;
return btoa(String.fromCharCode(...bytes));
};
export const decodeBase64 = (base64: string): string => {
const decoder = new TextDecoder();
const binaryString = atob(base64);
const bytes = Uint8Array.from(binaryString, char => char.charCodeAt(0));
const text = decoder.decode(bytes);
return text;
const getSiyuanHTML = (text: IClipboardData) => {
const siyuanMatch = text.textHTML.match(/<!--siyuan-data:([^>]+)-->/);
if (siyuanMatch) {
try {
const decoder = new TextDecoder();
const bytes = Uint8Array.from(atob(siyuanMatch[1]), char => char.charCodeAt(0));
text.siyuanHTML = decoder.decode(bytes);
// 移除注释节点,保持原有的 text/html 内容
text.textHTML = text.textHTML.replace(/<!--siyuan-data:[^>]+-->/, "");
} catch (e) {
console.log("Failed to decode siyuan data from HTML comment:", e);
}
}
};
export const openByMobile = (uri: string) => {
@ -71,30 +76,14 @@ export const readText = () => {
};
export const readClipboard = async () => {
const text: {
textHTML?: string,
textPlain?: string,
siyuanHTML?: string,
files?: File[],
} = {textPlain: "", textHTML: "", siyuanHTML: ""};
const text: IClipboardData = {textPlain: "", textHTML: "", siyuanHTML: ""};
try {
const clipboardContents = await navigator.clipboard.read();
for (const item of clipboardContents) {
if (item.types.includes("text/html")) {
const blob = await item.getType("text/html");
text.textHTML = await blob.text();
// 从 text/html 中的注释节点提取 text/siyuan 数据
const siyuanMatch = text.textHTML.match(/<!--siyuan-data:([^>]+)-->/);
if (siyuanMatch) {
try {
text.siyuanHTML = decodeBase64(siyuanMatch[1]);
// 移除注释节点,保持原有的 text/html 内容
text.textHTML = text.textHTML.replace(/<!--siyuan-data:[^>]+-->/, "");
} catch (e) {
console.log("Failed to decode siyuan data from HTML comment:", e);
}
}
getSiyuanHTML(text);
}
if (item.types.includes("text/plain")) {
const blob = await item.getType("text/plain");
@ -110,9 +99,11 @@ export const readClipboard = async () => {
if (isInAndroid()) {
text.textPlain = window.JSAndroid.readClipboard();
text.textHTML = window.JSAndroid.readHTMLClipboard();
getSiyuanHTML(text);
} else if (isInHarmony()) {
text.textPlain = window.JSHarmony.readClipboard();
text.textHTML = window.JSHarmony.readHTMLClipboard();
getSiyuanHTML(text);
}
return text;
}

View file

@ -245,12 +245,7 @@ const readLocalFile = async (protyle: IProtyle, localFiles: string[]) => {
uploadLocalFiles(localFiles, protyle, true);
};
export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEvent | {
textHTML?: string,
textPlain?: string,
siyuanHTML?: string,
files?: File[],
}) & {
export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEvent | IClipboardData) & {
target: HTMLElement
}) => {
if ("clipboardData" in event || "dataTransfer" in event) {

View file

@ -442,17 +442,15 @@ export class WYSIWYG {
// Remove ZWSP when copying inline elements https://github.com/siyuan-note/siyuan/issues/13882
.replace(new RegExp(Constants.ZWSP, "g"), "");
event.clipboardData.setData("text/plain", textPlain);
// 获取 text/siyuan 数据
// 设置 text/siyuan 数据
enableLuteMarkdownSyntax(protyle);
const siyuanData = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
event.clipboardData.setData("text/siyuan", siyuanData);
const siyuanHTML = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
event.clipboardData.setData("text/siyuan", siyuanHTML);
restoreLuteMarkdownSyntax(protyle);
// 在 text/html 中插入注释节点,用于右键菜单粘贴时获取 text/siyuan 数据
const textHTML = selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html);
const siyuanComment = `<!--siyuan-data:${encodeBase64(siyuanData)}-->`;
event.clipboardData.setData("text/html", siyuanComment + textHTML);
event.clipboardData.setData("text/html", `<!--siyuan-data:${encodeBase64(siyuanHTML)}-->` + (selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html)));
});
this.element.addEventListener("mousedown", (event: MouseEvent) => {
@ -1947,17 +1945,15 @@ export class WYSIWYG {
}
textPlain = textPlain.replace(/\u00A0/g, " "); // Replace non-breaking spaces with normal spaces when copying https://github.com/siyuan-note/siyuan/issues/9382
event.clipboardData.setData("text/plain", textPlain);
// 获取 text/siyuan 数据
// 设置 text/siyuan 数据
enableLuteMarkdownSyntax(protyle);
const siyuanData = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
event.clipboardData.setData("text/siyuan", siyuanData);
const siyuanHTML = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
event.clipboardData.setData("text/siyuan", siyuanHTML);
restoreLuteMarkdownSyntax(protyle);
// 在 text/html 中插入注释节点,用于右键菜单粘贴时获取 text/siyuan 数据
const textHTML = selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html);
const siyuanComment = `<!--siyuan-data:${encodeBase64(siyuanData)}-->`;
event.clipboardData.setData("text/html", siyuanComment + textHTML);
event.clipboardData.setData("text/html", `<!--siyuan-data:${encodeBase64(siyuanHTML)}-->` + (selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html)));
});
let beforeContextmenuRange: Range;

View file

@ -260,6 +260,13 @@ interface Window {
destroyTheme(): Promise<void>;
}
interface IClipboardData {
textHTML?: string,
textPlain?: string,
siyuanHTML?: string,
files?: File[],
}
interface IRefDefs {
refID: string,
defIDs?: string[]