在 LaTeX Suite 插件中,如何通过 Javascript,并借助历史剪贴板,快速实现创建 [text](link) 形式的链接?

预期的效果

三个步骤:

  • 复制链接 link
  • 复制文本 text
  • 回到 Obsidian 中敲击 vvv 触发得到 [🔗via: text](link)

背景

相关信息

  • Windows 11;已开启 Win + V 系统级剪贴板管理;
  • Obsidian 1.8.9

我个人在写周刊等内容的时候会使用 LaTeX Suite 插件触发一个 $\longleftarrow$ [🔗via: text](link) 格式来表示信息的出处。格式如下方举例的最后两行:

最初代码如下:

{trigger: "vvv", replacement: "[🔗via: $1]($2)$3", options: "tA"},

每一次我都需要手动将 linktext 手动复制一遍,然后每一次都需要手动将二者粘贴一遍。

前面的复制操作不可避免,但是我想后面的粘贴操作可以尽量避免。

成功尝试

代码改进如下:

{trigger: "vvv", replacement: () => {
    const { clipboard } = require('electron');
    let text = clipboard.readText(); // 得到剪贴板的第一个内容
    // let link = ???;
    // let out_link = `\[🔗via: ${text}\]\(${link}\)$1`;
    let out_link = `\[🔗via: ${text}\]\($1\)$2`;
    return out_link;
}, options: "tA"}, 

对于上面这段代码,步骤如下:

  1. 复制帖子链接 link,复制链接文本 text
  2. 回到 Obsidian 中,敲击 vvv
  3. 触发得到 [🔗via: text](),此时光标在 () 内部;
  4. Win + V 打开历史剪贴板,选中第二条内容(即链接),粘贴;
  5. 敲击 TAB ,光标跳转到第二个占位符,即退出链接。

相比之下上一段代码,步骤上就仅仅是少了一个 粘贴 text 的步骤。

回到注释,这段代码中我可以得到剪贴板的第一个内容 text,但是我不知道怎么得到剪贴板的第二个内容 link

这也核心问题就是:如何通过 Javascript 中如何得到历史剪贴板的第二个内容?

失败尝试

我不是很了解 javascript,问了 DeepSeek 之后迷惑更大了。

AI 提供了一个方案(引用格式)

基于你的需求,以下是极简实现方案:通过记录最后两次剪贴操作,直接获取第二次内容。代码完全集中于「保存两次剪贴内容」的原子性功能实现:

const { clipboard } = require('electron');
// 核心逻辑:保存最近两次剪贴内容(当前内容为clipboardHistory[0],上一步为[1])
let clipboardHistory = [];
setInterval(() => {
  const text = clipboard.readText().trim();
  // 仅当内容变化时更新数组 (核心去重判断)
  if (text && clipboardHistory[0] !== text) {
    clipboardHistory.unshift(text); // 插入数组头部
    clipboardHistory = clipboardHistory.slice(0, 2); // 只保留前两项
  }
}, 300); // 每0.3秒检查一次剪贴板
// 触发器直接使用数组索引获取历史
{
  trigger: "vvv",
  replacement: "[🔗via: " + (clipboardHistory[1] || "文本未复制") + "](" + clipboardHistory[0] + ")",
  options: "tA"
}

但是不行:

  • 抛开上面代码不谈,LaTeX Suite 插件支持的 .js snippets 文件本质上是一个大列表,每一个元素都是 {trigger...} 的形式,上述代码中的 clipboardHistorysetInterval 部分并不属于 {trigger...} 范围内。
  • 就算是把全局的变量全部放入 {trigger...} 范围内,结果也运行不对。
{trigger: "vvv", replacement: () => {
    const { clipboard } = require('electron');
    let clipboardHistory = [];
    setInterval(() => {
      const text = clipboard.readText().trim();
      // 仅当内容变化时更新数组 (核心去重判断)
      if (text && clipboardHistory[0] !== text) {
        clipboardHistory.unshift(text); // 插入数组头部
        clipboardHistory = clipboardHistory.slice(0, 2); // 只保留前两项
      }
    }, 300); // 每0.3秒检查一次剪贴板
    let out_link = `[🔗via: ${clipboardHistory[1]}](${clipboardHistory[0]})`;
    return out_link;
}, options: "tA"}, 

改写成如上,得到的就是 [🔗via: undefined](undefined)

同时 AI 也提到:

常规的剪贴板管理中,通常只能访问最新的内容。无论是浏览器环境还是Electron这样的桌面应用,通常的API如clipboard.readText()只能获得最新的剪贴板内容。因为大多数系统的剪贴板历史并不是默认公开的,尤其是对于更早的条目。

所以,或许没有办法实现我预期的效果吗?

换个思路,别的软件可以怎么实现呢?

把 clipboardHistory 存到全局对象上面。也就是把

let clipboardHistory = []

改成

if(!window.clipboardHistory){
    window.clipboardHistory = [];
    // 设置定时器 的代码
    setInterval .....
}

1 个赞
    {trigger: "vvv", replacement: () => {
        const { clipboard } = require('electron');
        if(!window.clipboardHistory) {
            window.ClipboardItem
            window.clipboardHistory = [];
            // 设置定时器 的代码
            setInterval(() => {
                const text = clipboard.readText().trim();
                // 仅当内容变化时更新数组 (核心去重判断)
                if (text && clipboardHistory[0] !== text) {
                    clipboardHistory.unshift(text); // 插入数组头部
                    clipboardHistory = clipboardHistory.slice(0, 2); // 只保留前两项
                }
            }, 300); // 每0.3秒检查一次剪贴板
        }
        let out_link = `[🔗via: ${clipboardHistory[0]}](${clipboardHistory[1]})`;
        return out_link;
    }, options: "tA"}, 

OMG,真的可以,感谢!