在 Obsidian Bridge 的基础上解决 MN 与 OB 的联动痛点

原先 Obsidian-Bridge 搭配 Marginnote-Companion(Obsidian 插件)有两大痛点

  • 随着版本更新,自动插入模式不再有效
  • Marginnote-Companion 不能读取指向学习集的笔记链接(目前是指向文档),所以传送非常不便

不过我发现Obsidian-Bridge 开启后,在 MN中点击笔记后会获得包含笔记的所有字符串,因此在此基础上,在 AI 的协助下写了一个 Templater 脚本
,Marginnote-Companion 可以关掉了

  1. 在 Obsidian 中 安装 Templater 插件
  2. 新建一个模板文件
  3. 复制代码
<%*
// --- 配置区域 ---
const POLL_INTERVAL = 1000; // 检查频率:1000毫秒 = 1秒
const MN_SIGNATURE = "noteId"; // 简单的特征识别,防止误粘贴其他东西

// 定义全局变量来控制开关 (依附在 window 对象上)
if (!window.mn_auto_listener) {
    window.mn_auto_listener = {
        running: false,
        intervalId: null,
        lastClip: ""
    };
}

const listener = window.mn_auto_listener;

// --- 开关逻辑 ---
if (listener.running) {
    // 如果正在运行,则关闭
    clearInterval(listener.intervalId);
    listener.running = false;
    listener.intervalId = null;
    new Notice("🛑 MN 自动监听已停止");
} else {
    // 如果没运行,则启动
    listener.running = true;
    listener.lastClip = await tp.system.clipboard(); // 记住当前的,防止一开始就重复粘贴
    new Notice("🟢 MN 自动监听已启动!请去 MN 尽情复制吧");

    // 启动定时循环
    listener.intervalId = window.setInterval(async () => {
        try {
            // 1. 获取当前剪贴板
            // 注意:这里我们直接用 electron 的 api 获取,这比 tp.system 快一点且不卡顿
            const electron = require("electron");
            const clip = electron.clipboard.readText();

            // 2. 只有当剪贴板变化了,且包含 MN 特征时才处理
            if (clip !== listener.lastClip && clip.includes(MN_SIGNATURE) && clip.includes("{")) {
                
                // 更新最后状态
                listener.lastClip = clip;

                // --- 3. 调用 V5.1 核心处理逻辑 (直接内嵌) ---
                let output = "";
                try {
                    const start = clip.indexOf('{');
                    const end = clip.lastIndexOf('}');
                    if (start !== -1 && end !== -1) {
                        let jsonStr = clip.substring(start, end + 1);
                        jsonStr = jsonStr.replace(/[\u0000-\u0009\u000B-\u001F\u007F]/g, "");
                        const data = JSON.parse(jsonStr);

                        let noteData = data.data || data.last?.data;
                        if (!noteData && data.rows) noteData = data.rows[0];
                        
                        if (noteData) {
                            // 提取字段
                            let noteId = noteData.noteId;
                            let docMd5 = noteData.docMd5;
                            let title = (noteData.noteTitle || "").trim();
                            let excerpt = (noteData.highlight_text || noteData.excerptText || "").trim();
                            let notesText = (noteData.notesText || "").trim();

                            // 内容构建
                            let textParts = [];
                            if (title) textParts.push(title);
                            if (notesText && excerpt && notesText.includes(excerpt)) {
                                textParts.push(notesText);
                            } else {
                                if (excerpt) textParts.push(excerpt);
                                if (notesText) textParts.push(notesText);
                            }
                            textParts = [...new Set(textParts)];

                            // 链接构建
                            let link = "";
                            if (noteId) {
                                let topicId = "";
                                if (data.bookMap && docMd5 && data.bookMap[docMd5]) {
                                    topicId = data.bookMap[docMd5].currentTopicId;
                                }
                                link = `marginnote4app://note/${noteId}`;
                                if (topicId) link += `/${topicId}`;
                            }

                            // 链接前置
                            if (link && textParts.length > 0) {
                                textParts[0] = `${textParts[0]} [MN](${link})`;
                            }

                            // 最终组合
                            output = textParts.join("\n\n");
                            if (textParts.length === 0 && link) output = `[MN](${link})`;
                        }
                    }
                } catch (err) {
                    console.log("MN 解析忽略非标准数据");
                }

                // --- 4. 写入当前文件 ---
                if (output) {
                    // 获取当前活跃的编辑器
                    const view = app.workspace.activeLeaf.view;
                    if (view.editor) {
                        const editor = view.editor;
                        // 在光标处或者文件末尾插入
                        // 这里我们选择:插入到当前光标位置,并换行
                        const cursor = editor.getCursor();
                        // 插入内容 + 两个换行
                        editor.replaceRange(output + "\n\n", cursor);
                        
                        // 移动光标到最后,方便下一条
                        const newCursor = { 
                            line: cursor.line + output.split("\n").length + 2, 
                            ch: 0 
                        };
                        editor.setCursor(newCursor);
                        
                        new Notice("✅ 已捕获一条笔记");
                    }
                }
            }
        } catch (e) {
            console.error(e);
            // 出错不停止,继续监听
        }
    }, POLL_INTERVAL);
}
%>
  1. 调用这个模板:会看到右上角弹出绿色提示::green_circle: MN 自动监听已启动!”
  2. 随后在MarginNote 里阅读,点击卡片,Obsidian 会在后台默默地把你复制的内容处理好,追加到那个文件的末尾。
  3. 阅读完成后再次调用模板,你会看到红色提示::stop_sign: MN 自动监听已停止”

还有很多优化的空间

2 个赞

期待进一步优化Obsidian Bridge 的同步兼容性、稳定性 :heart:

上次试了一下同步,目前还是没法很好的解决图片同步的问题,,也没时间折腾了,备考太烦了