如果官方考虑能给个选项就更好了 😄 ,我这个实现还是有点拉的
效果图
修改前是这个样子的:
同时不影响原有块复制实现(块内直接按 Ctrl+C)(注意到控制台中输出了 Not Edit Block
):
实现基本思路
前端不太懂,俺结合论坛内其他大佬的实现、还有 Deepseek 的帮助下,搓出来的。一并表示感谢。
观察到复制出的文本包含两种类型,一种是 text/plain
,即纯文本结果;另一种是 text/html
,即包含格式的富文本结果。所以总思路是将 text/html
转换为 Markdown。
具体实现方式如下:
- 判断是否是选中部分文本。考虑到对整个块进行复制时是可以正常复制出 Markdown 文本的,为了不干扰选中整个块的情况,需判断是否选中部分文本。实现相对鸡贼,是通过判断 Toolbar 是否冒出来判断的;
- 从原始复制结果中读出来
text/html
内容,并借助Protyle
提供的助手函数将html
变回Markdown
; - 将 Markdown 结果写入剪切板。为保证在编辑器内复制效果不受影响,先读取了原本的
text/html
,然后将读上来的text/html
和 Markdown 结果一起写入剪切板。
已知问题
受限于“判断是否选中部分文本”的实现方式,在选中文本后,需直接按下Ctrl+C进行复制,不可进行其他点击操作;这意味着通过右键菜单进行复制时,本脚本无法生效。
脚本内容
!(function () {
// 获取Protyle对象,以使用其中的BlockDOM2StdMd助手函数
function getProtyle() {
try {
if (document.getElementById("sidebar")) return siyuan.mobile.editor.protyle;
const currDoc = siyuan?.layout?.centerLayout?.children.map(item => item.children.find(item => item.headElement?.classList.contains('item--focus') && (item.panelElement.closest('.layout__wnd--active') || item.panelElement.closest('[data-type="wnd"]')))).find(item => item);
return currDoc?.model.editor.protyle;
} catch (e) {
console.error(e);
return null;
}
};
// 判断是否选中部分文本
function isNotEditBlock() {
const p = getProtyle(); // 甜菜,看toolbar是否出来,判断是否选中部分文本
if(p.toolbar.element.className.includes('fn__none')) return true;
return false;
};
let toolbar_show = false;
document.addEventListener(
"mouseup", async (event) =>{
setTimeout(async () => {
if(isNotEditBlock()){
toolbar_show = false;
}else{
toolbar_show = true;
}
}, 20)
});
document.addEventListener(
"copy",
async (event) => {
setTimeout(async () => {
try {
// 判断是不是选中部分文本,如果是的话才处理
if(toolbar_show == false){
console.info("Not Edit ablock");
}else{
let mddata = '';
let clipboardItems = await navigator.clipboard.read();
for (const clipboardItem of clipboardItems) {
for (const type of clipboardItem.types) {
if(type == "text/html"){ // 读取剪切板中的text/html数据,然后借助助手函数把html转换为md
const blob = await clipboardItem.getType(type);
// 创建FileReader对象,以做到用文本方式读取blob对象
const reader = new FileReader();
reader.readAsText(blob, 'utf-8');
reader.onload = function(e) {
const htmlString = reader.result; // 读取后的结果
// console.log('htmlString: ' + htmlString);
// 因为读出来的 text/html 可能包含多个并列的 p 标签,需要遍历处理
const parser = new DOMParser();
const doc = parser.parseFromString(htmlString, 'text/html');
const pTags = doc.querySelectorAll('p'); // 获取所有 p 标签
const p = getProtyle();
pTags.forEach((pTag, index) => {
// console.log(`pTag[${index}].innerHTML: ` + pTag.innerHTML);
// 如果需要在每个处理结果之间加上换行符,可以在后面拼接 "\n"
mddata += p.lute.HTML2Md(pTag.innerHTML).trimEnd() + "\n";
});
};
}
}
}
// 仅修改text/plain,text/html不动
clipboardItems = await navigator.clipboard.read();
const newItems = [];
for (const item of clipboardItems) {
const types = item.types;
const newData = {};
for (const type of types) {
// 其他类型直接保留原数据
newData[type] = await item.getType(type);
}
newData['text/plain'] = new Blob([mddata], { type: 'text/plain' });
newItems.push(new ClipboardItem(newData));
}
// 将修改后的内容写回剪贴板
await navigator.clipboard.write(newItems);
}
} catch (err) {
console.error("读取剪贴板内容失败:", err);
}
}, 100);
},
{ passive: false, capture: true }
);
})();
参考了如下工作,感谢各位大佬的分享:
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于