Improve right-click menu paste (#15286)

* 🎨 Improve right-click menu paste

* 🎨 Improve right-click menu paste
This commit is contained in:
Jeffrey Chen 2025-07-17 16:49:46 +08:00 committed by GitHub
parent 62d560e7a8
commit 280f97ee81
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 51 additions and 6 deletions

View file

@ -2,6 +2,22 @@ import {focusByRange} from "./selection";
import {fetchPost} from "../../util/fetch";
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;
};
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;
};
export const openByMobile = (uri: string) => {
if (!uri) {
return;
@ -58,14 +74,27 @@ export const readClipboard = async () => {
const text: {
textHTML?: string,
textPlain?: string,
siyuanHTML?: string,
files?: File[],
} = {textPlain: "", textHTML: ""};
} = {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);
}
}
}
if (item.types.includes("text/plain")) {
const blob = await item.getType("text/plain");

View file

@ -248,6 +248,7 @@ const readLocalFile = async (protyle: IProtyle, localFiles: string[]) => {
export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEvent | {
textHTML?: string,
textPlain?: string,
siyuanHTML?: string,
files?: File[],
}) & {
target: HTMLElement
@ -275,6 +276,7 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
} else {
textHTML = event.textHTML;
textPlain = event.textPlain;
siyuanHTML = event.siyuanHTML;
files = event.files;
}

View file

@ -66,7 +66,7 @@ import {openGlobalSearch} from "../../search/util";
import {popSearch} from "../../mobile/menu/search";
/// #endif
import {BlockPanel} from "../../block/Panel";
import {copyPlainText, isInIOS, isMac, isOnlyMeta, readClipboard} from "../util/compatibility";
import {copyPlainText, isInIOS, isMac, isOnlyMeta, readClipboard, encodeBase64} from "../util/compatibility";
import {MenuItem} from "../../menus/Menu";
import {fetchPost} from "../../util/fetch";
import {onGet} from "../util/onGet";
@ -442,10 +442,17 @@ 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);
event.clipboardData.setData("text/html", selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html));
// 获取 text/siyuan 数据
enableLuteMarkdownSyntax(protyle);
event.clipboardData.setData("text/siyuan", selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html);
const siyuanData = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
event.clipboardData.setData("text/siyuan", siyuanData);
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);
});
this.element.addEventListener("mousedown", (event: MouseEvent) => {
@ -1940,10 +1947,17 @@ 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);
event.clipboardData.setData("text/html", selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html));
// 获取 text/siyuan 数据
enableLuteMarkdownSyntax(protyle);
event.clipboardData.setData("text/siyuan", selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html);
const siyuanData = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
event.clipboardData.setData("text/siyuan", siyuanData);
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);
});
let beforeContextmenuRange: Range;