【技巧】超越快捷键!实现 Obsidian 内的「文本指令」

「文本指令」,即输入特定的文本之后执行指令
在 Obsidian 内,可以用插件 Latex Suite [^1] 实现这个功能。

实现的效果:
240627_实现OB内的文本指令-img-240630_014745

在上图中,只要在 OB 内输入 lnk 并空格,就会调用 Quicker 执行动作[^2],从当前浏览器获取到标签页并粘贴进来。

之前写文章介绍过如何用连续按键作为快捷键来执行命令,这次相当于更进一步:用文本来执行外部程序的功能
如此,我们便更进一步拓宽了「快捷键」的限制——再也不用担心快捷键太多记不住了,现在完全可以通过输入几个字,来执行我们想要的操作!

[!NOTE] 文本替换插件
Latex Suite 其实是个文本替换插件,类似的还有 Easy Typing,两者都可以在输入特定的文本之后将它转换成其他文本。
但代码执行功能是 LatexSuite 插件独有的,也是它最大的亮点。

定义文本指令

在 LatexSuite 的设置中,可以用 json 来定义替换规则。

我们先用一个最简单的替换规则来入门:

{trigger: ">>", replacement:"> 🦊", options: "tA"},

这样的规则含义如下:

  • trigger: ">>": 在输入 >> 的时候触发替换
  • replacement:"> 🦊": 替换的内容为:> 🦊
  • options: "tA"t 代表在文本模式下有效,A 代表自动执行(否则需要按 Tab 或空格才会执行)

就是这么简单,把这行规则粘贴进插件设置的 Snippets 区域内试一下吧!


除此以外,我们还可以定义粘贴之后指针的位置:

// 代码粘贴
{trigger: "`js ", replacement: "```js\n$0\n```", options: "tA"},

这里的 $0 就代表了粘贴后输入指针的位置,\n 则是换行,所以它会在转换后将输入光标放进代码块的中间。

240627_实现OB内的文本指令-img-240630_032452


除了直接填写替换的文本,还可以填写 Javascript,以添加动态的文本。
例如:

{trigger: "td ", replacement: () => ("# " + moment().format('YYYY.MM.DD') ), options: "tA"},

这段 JS 代码在你输入 td 并空格的时候,就会转成今天的日期大标题。

240627_实现OB内的文本指令-img-240630_021218

:fox_face: 我原先其实都是用的 Easy Typing 做文本替换,直到有一天想要插入这种日期格式发现实现不了,所以才找到了 Latex Suite 这款插件。

更多介绍可以看插件的说明:Function snippets

执行命令

如上文所说,Latex 不光可以替换「文本」,还可以在你输入特定文字的时候运行 JS 脚本。
这就给了我们很大的操作空间

我们在 Obsidian 内,可以用如下命令来运行特定的命令(Command):

app.commands.executeCommandById('command-id');

比如,我们想在输入「打开设置」后打开设置,可以写:

{trigger: "打开设置 ", replacement: () => {
    app.commands.executeCommandById("app:open-settings");
    return "";
}, options: "tA"},

获取命令 ID

至于「如何获取 command id」——
我们可以按 Ctrl+Shfit+I 打开开发者工具:
切换到 Console(控制台),输入 app.commands.commands[",然后会出现所有命令 ID,再输入几个字进行补全:
240627_实现OB内的文本指令-img-240627_194029

选中命令的 ID 并回车,就能看到这个命令的详细信息:
240627_实现OB内的文本指令-img-240627_194149

这里的 id 即命令 ID。

调用外部程序

如果想要更进一步,调用外部程序——例如 Quicker,那么可以配合 Shell Commands[^3] 插件来执行!

ShellCommands 是一款专门用来运行外部程序的 OB 插件。
在插件的设置界面,可以填写需要运行的命令:

这里填写的内容和你在系统「运行」中执行的命令等同,所以也可以按 Win+R 来测试命令(在命令行执行也一样)
240627_实现OB内的文本指令-img-240630_022056

然后以 Quicker 举例,在 Quicker 的文档[^4] 中我们可以知道,
通过命令行运行 "C:\Program Files\Quicker\QuickerStarter.exe" -c "runaction:动作标识?参数内容" 就可以调用 QK 执行指定的动作。

所以我们只需要按照这个格式填写好动作 ID 和需要的参数,就完成了在 OB 中调用 QK 动作 的配置。

实例:OB 粘贴

正如前面所提到的,我在 OB 里会经常调用 Quicker 来把浏览器的内容粘贴进笔记,作为参考链接。

就以这个应用为例,我会填写参数:
"C:\Program Files\Quicker\QuickerStarter.exe" "runaction:c86c0994-77ae-46ae-b61e-44eac5f88e59?{{selection}}"

这里面 c86c0994-77ae-46ae-b61e-44eac5f88e59 是 QK 动作 OB 粘贴 的 ID,{{selection}} 是 ShellCommands 插件提供的参数,代表选中的文本。

我在 SC 命令的 Variables 中将 selection 变量的默认值设置为 NO-SELECTION

这样 Quicker 动作就可以根据传入的参数,来决定粘贴的方式:

  • 如果有选中文本,就把链接粘贴进当前文本
  • 否则,就粘贴完整的 Markdown 格式链接

240627_实现OB内的文本指令-img-240630_023941

为了方便演示,上图用的是快捷键 Alt+V ,展示了在选中文本与未选中的情况下,运行 QK 动作的差异。
QK 本身的处理逻辑可以直接编辑动作查看。

Quicker 的文本指令功能

其实,QK 自己也有文本指令功能,所以其实一些普通的动作直接用 QK 自带的文本指令就完全够用。

本文之所以饶了这么一大圈,用 LatexSuite + ShellCommands 重新拼装出了「文本指令」的功能,一方面是作为实践「OB+外部联动」的尝试,另一方面,也是因为这样才能更好地获取Obsidian 内的数据(例如当前选中的文本、当前笔记的标题……这些数据从外部难以获取,而在 OB 内则可以轻易通过 API 来获得)

总结

本文从文本替换的基础应用出发,介绍了 Latex Suite 插件的配置规则和基础用法。随后拓展到如何从 Ob 内调用外部程序,以及更进一步的——如何将 OB 里的数据传递给 Quicker,并根据场景执行不同的功能。

绝大多数时候,因为 OB 自身的插件和脚本非常强大,所以很多的需求都可以直接自己内部完成。但是偶尔——像是需要和浏览器或者播放器进行交互——就会需要 QK 这样的外部工具进行协助。

而此前两者的「互通」就是最让我困扰的步骤,现在则基本可以双向打通了。

从 OB 到 QK,可以通过 ShellCommands 实现调用和传参;
而反过来,从 QK 到 OB,则可以通过 AdvancedURI[^5] 来打开文档或是运行命令。

至于后者,又是可以拓展成一篇文章的内容量了,以后有缘再聊。

小彩蛋:更多的 OBxQK 联动动作 :tada:

基于上面所说的流程,我还做了几个非常实用的 Quicker 动作!

粘压图片
压缩剪贴板内的图片,并粘贴进 Obsidian
默认是压缩成最大宽度1200的 jpg 格式图片,可以自行调节。

这个动作可以有效避免「粘贴了超大的 png 进笔记然后占用几十 Mb 的空间」,我分配给了快捷键 Ctrl+Num2

动作网址

也有 Obsidian 插件可以在粘贴图片的时候进行压缩,但是我嫌那个可以配置的参数太少,遂自己造轮子。

获取标签页(获取视频封面)
动作网址
其实是动作自带的功能,我默认关闭了。

启用之后,如果当前网页是 Youtube 或者 Bilibili 的视频页面,会自动获取视频的封面并粘贴进来。配合上面的粘压图片动作可以避免被超大体积的封面图背刺。

顺便,这个动作本来是为了方便在 QQ 上给人分享网址的,所以还支持了不少别的功能,包括但不限于:

  1. 通过「先粘贴文本,再粘贴图片」的方式,支持聊天框里的粘贴
  2. 多选标签页的时候运行,可以复制多个标签页的链接
  3. 右键可以选择复制成纯文本、MD 格式或者是仅网址的模式

截取视频画面/插入空降时间
动作网址
针对正在播放视频的网页,运行这个动作可以自动截取视频画面并粘贴进来。同样可以配合粘压图片动作,避免截图文件过大。
同时也提供了时间码&空降时间链接的功能,可以复制当前时间(或者可以直接跳转到当前时间的空降链接)并粘贴。

我给这个动作设置了快捷键 Ctrl+Num3

[!info] Media Extended 插件
类似的功能用 ME 插件也可以做,但我之前试用的时候好像需要遵循特定的笔记规则,所以还是为了自己的习惯重新造轮子了 lol

:fox_face: 这一套感觉也可以专门出一个「如何用 OB 做视频笔记」的工作流说明了 hhhh


脚注:

[^1]: LatexSuite 仓库:obsidian-latex-suite
[^2]: 我自己写的用来在 OB 里快速粘贴的动作:Ob粘贴 - by Moy - 动作信息 - Quicker ,以及获取标签页的动作:获取标签页 - by Moy - 动作信息 - Quicker
[^3]: Taitava/obsidian-shellcommands
[^4]: 从外部启动Quicker动作 - Quicker
[^5]: Vinzent03/obsidian-advanced-uri

7 个赞

大佬,我卡在了这一步没跟上你的步骤,是单独针对这个添加的shell命令进行设定吗?


前面的2个Quicker动作已添加好,都能正常使用。
shell命令这里没搞定 :smiling_face_with_tear:

从截图上看……你是漏了最前面的英文引号 :rofl:
(selection 设置全局默认值也OK的,这个没关系

谢谢~
我把前后引号都漏了 :face_with_spiral_eyes:
shell命令没用过,看见3个自带的都没有双引号就跟着来了

区别主要在于路径中的空格哈,如果是上面这样 C:\Program Files\ 这样的路径,你不加双引号的话它只会识别到空格前面那部分 C:\Program 然后就会提示找不到文件。
用双引号包裹住才能让 Shell 知道这是完整的路径

1 个赞

大佬,再次请教,最近几天我自己尝试了很多次,都没有制作出自己的文本命令(非Obsidian外)

比如想要输入222后,自动粘贴剪贴版
实际效果只是把222给删掉了,设定内容:

{trigger: "222", replacement: () => {
    app.commands.executeCommandById["editing-toolbar:editor-paste"];
    return "";
}, options: "tA"},

比如我想输入999,就把本行内容挪至上一行
实际上,位置倒是换了,但本行多了99,上一行少了末尾2个字,设定内容:

{trigger: "999", replacement: () => {
    app.commands.executeCommandById("editing-toolbar:editor:swap-line-up");
    return "";

第一个:方框输错了

    {trigger: "999 ", replacement: () => {
        app.commands.executeCommandById('editing-toolbar:editor-paste');
        return "";
    }, options: "tA"},

函数要用 () 包裹


第二个我猜测是时序问题,你运行这个 Command 的时候没来得及完成替换。

试试这个:


    {trigger: "888 ", replacement: () => {
        function myFunction() {
            app.commands.executeCommandById("editing-toolbar:editor:swap-line-up");
        }
        
        // 等待 500 毫秒后执行函数
        setTimeout(myFunction, 300);

        return "";
    }, options: "tA"},

会先等待 300 毫秒再执行命令,这样应该能稳定成功。

谢谢 都成功啦,终于可以自由输入命令了


还有个进一步需求不知道能不能实现:
重复执行上一次执行的命令(延续其他软件的使用习惯)

目前我使用的命令面板是Better Command Palette,用快捷键打开它带的命令面板后,按Enter键,就能执行它置顶的命令(上一次执行过的命令)

image

有现成的插件

请问该插件是不是有bug啊? 我安装完启用后, 所有快捷键都不好使了, 把它关闭就正常了. 麻烦测试下你那边也这样么, 谢谢.

我这边一直开着这个插件,没问题

感谢, 看来是插件有冲突, 我去沙箱库试了一下确实没问题, 还是头一回见到这么严重的插件冲突, 我得慢慢排查了…