我知道在阅读模式下复制,可以不带MD格式,直接复制内容,但是,我希望能复制的时候保留空行,因为我写东西习惯于段落之间保留一个空行,我的笔记也是用这种方式进行排版格式化的,但是在复制到其他地方时,这些空行空格缩进都会丢失,导致我不得不使用第三方工具再排版一次,非常麻烦。
还有有序列表,复制的时候,怎样才能保留前面的数字?
我知道在阅读模式下复制,可以不带MD格式,直接复制内容,但是,我希望能复制的时候保留空行,因为我写东西习惯于段落之间保留一个空行,我的笔记也是用这种方式进行排版格式化的,但是在复制到其他地方时,这些空行空格缩进都会丢失,导致我不得不使用第三方工具再排版一次,非常麻烦。
还有有序列表,复制的时候,怎样才能保留前面的数字?
直接复制应该不会有空行,似乎 Obsidian 在阅读模式下的渲染逻辑和编辑模式不一样,很多要素都不一样(吐槽已久的阅读模式与编辑模式视觉效果不一致
所以建议看看“导出”相关的插件,比如导出为 Word 或者其他可以复制的格式,算是曲线救国了……
编辑模式下也复制不到空行,还会带一堆MD格式……这个真的很难受啊
这问题我也理解的不太好,
目前认为, 本质是在 html 视角下, 一些可见字符其实更像是 “渲染样式”, 不算 “内容”
最典型就是有序列表的 1. 2. 3.
但是在复制到其他地方时,这些空行空格缩进都会丢失
首先在阅读视图里是要损失掉一些信息的, 不能认为跟编辑视图下完全一致:
%%...%%
在阅读视图里不会显示另一方面, 这除了跟 Ob 阅读视图转 html 的信息损失有关, 也跟 “粘贴到的程序能否完整识别信息” 有关
选中文本复制时是做了不少工作的, 它会存 html 格式 + plain text 格式各一份, 但粘贴时要看对面应用程序能支持啥格式: 例子就是 copy 同一份富文本 (来自网页, 来自 Ob 阅读视图都行) 粘贴到记事本以及粘贴到 Office 系列, 会看到它输出格式不一样, Office 就能保持有序列表的编号, 记事本显然不行
Ob 里可以控制台贴以下代码, 然后阅读视图随便复制点内容, 点击按钮就能看 “到底复制到了什么” – 是 html + plain text 各存一遍
// 在控制台执行以下代码创建一个临时按钮
const btn = document.createElement('button');
btn.textContent = '读取剪贴板';
btn.style.position = 'fixed';
btn.style.top = '100px';
btn.style.right = '10px';
btn.style.zIndex = '9999';
btn.style.padding = '10px';
btn.style.background = '#007bff';
btn.style.color = 'white';
btn.style.border = 'none';
btn.style.borderRadius = '4px';
btn.onclick = async () => {
try {
const clipboardItems = await navigator.clipboard.read();
for (const clipboardItem of clipboardItems) {
// 读取纯文本
try {
const plainText = await clipboardItem.getType('text/plain');
console.log('纯文本内容:', await plainText.text());
} catch (e) {
console.log('无纯文本内容');
}
// 读取HTML
try {
const html = await clipboardItem.getType('text/html');
console.log('HTML内容:', await html.text());
} catch (e) {
console.log('无HTML内容');
}
}
} catch (err) {
console.error('读取剪贴板失败:', err);
}
};
document.body.appendChild(btn);
new Notice('已添加剪贴板读取按钮到页面右上角');
这些空行空格缩进都会丢失 有序列表复制的时候,怎样才能保留前面的数字
说实话, 如果链接和加粗之类的较少, 有序序号和段间空行较多, 还不如复制 markdown 格式, 然后删掉加粗来的快
其余办法我能想到的是
require('html-to-text')
库似乎是专干这个事的最后实在不行了, 还有个手动办法是定制一个支持空行和序号的剪切版修复脚本, 即手动实现一点点的 html-to-text 核心功能
原型就是类似下面这种的, 复制后点击按钮, 会读一遍剪切版里的 html 元素, 给 orderlist 序号打补丁并输出纯文本
(Ob 控制台贴以下代码, 注意一定在次要仓库里测试好了再用)
// 创建第二个按钮 - 转换HTML为格式化文本
const convertBtn = document.createElement('button');
convertBtn.textContent = '转换HTML为格式化文本';
convertBtn.style.position = 'fixed';
convertBtn.style.top = '150px'; // 放在第一个按钮下方
convertBtn.style.right = '10px';
convertBtn.style.zIndex = '9999';
convertBtn.style.padding = '10px';
convertBtn.style.background = '#28a745';
convertBtn.style.color = 'white';
convertBtn.style.border = 'none';
convertBtn.style.borderRadius = '4px';
convertBtn.onclick = async () => {
try {
// 获取剪贴板中的HTML内容
const clipboardItems = await navigator.clipboard.read();
let htmlContent = '';
for (const clipboardItem of clipboardItems) {
try {
const htmlBlob = await clipboardItem.getType('text/html');
htmlContent = await htmlBlob.text();
break; // 找到HTML内容就停止
} catch (e) {
continue;
}
}
if (!htmlContent) {
alert('剪贴板中没有找到HTML内容');
return;
}
// 转换HTML为格式化文本
const formattedText = htmlToTextWithFormatting(htmlContent);
// 显示结果
console.log('格式化后的文本输出:');
console.log('-------------------');
console.log(formattedText);
console.log('-------------------');
// 复制到剪贴板
await navigator.clipboard.writeText(formattedText);
new Notice(`已转换并复制到剪贴板!\n结果输出到控制台且复制到剪贴板\n ${formattedText}`);
} catch (err) {
console.error('转换失败:', err);
alert('转换失败: ' + err.message);
}
};
// 添加到页面
document.body.appendChild(convertBtn);
// HTML转换函数
function htmlToTextWithFormatting(html) {
const temp = document.createElement('div');
temp.innerHTML = html;
// 处理有序列表
const olElements = temp.querySelectorAll('ol');
olElements.forEach(ol => {
let listCounter = 1; // 重置计数器
// 计算当前ol的嵌套深度
const getIndentLevel = (element) => {
let level = 0;
let parent = element.parentElement;
while (parent && parent !== temp) {
if (parent.tagName === 'OL' || parent.tagName === 'UL') {
level++;
}
parent = parent.parentElement;
}
return level;
};
const currentLevel = getIndentLevel(ol);
// 处理当前ol下的直接li子元素
const topLevelLis = Array.from(ol.children).filter(child => child.tagName === 'LI');
topLevelLis.forEach(li => {
// 计算缩进 - 每层缩进4个空格
const indent = ' '.repeat(currentLevel);
// 处理子列表(会在递归中处理)
const subLists = li.querySelectorAll('ol');
//subLists.forEach(subOl => {
// const subLis = subOl.querySelectorAll('li');
// subLis.forEach((subLi, subIndex) => {
// const subIndent = ' '.repeat(getIndentLevel(subOl));
// subLi.textContent = `${subIndent}${subIndex + 1}. ${subLi.textContent.trim()}\n`;
// });
//});
subLists.forEach(subOl => {
const subLis = subOl.querySelectorAll('li');
subLis.forEach((subLi, subIndex) => {
const subIndent = ' '.repeat(getIndentLevel(subOl));
const prefix = subIndex === 0 ? '\n' : ''; // 在第一个subLi元素前加换行
subLi.textContent = `${prefix}${subIndent}${subIndex + 1}. ${subLi.textContent.trim()}\n`;
});
});
// 处理当前项
li.textContent = `${indent}${listCounter++}. ${li.textContent.trim()}\n`;
});
});
// 处理无序列表(转换为*)
const ulElements = temp.querySelectorAll('ul');
ulElements.forEach(ul => {
const lis = ul.querySelectorAll('li');
lis.forEach(li => {
li.textContent = `* ${li.textContent}\n`;
});
});
// 保留段落换行
const pElements = temp.querySelectorAll('p');
pElements.forEach(p => {
p.innerHTML = p.innerHTML + '\n\n';
});
// 处理换行符
const brElements = temp.querySelectorAll('br');
brElements.forEach(br => {
br.replaceWith('\n');
});
// 处理div换行
const divElements = temp.querySelectorAll('div');
divElements.forEach(div => {
if (!div.querySelector('p') && !div.querySelector('br')) {
div.innerHTML = div.innerHTML + '\n';
}
});
// 移除多余空白
return temp.textContent
.replace(/\n{3,}/g, '\n\n') // 多个空行合并为两个
.trim();
}
new Notice('已添加"转换HTML为格式化文本"按钮到页面右上角');
感谢这么详细的分析,眼下看来没什么好的解决方案,这也算是用黑曜石写作然后分享到其他地方时的一个问题了