插件
文档编写中
部分内容可能尚不完整。
插件是一个独立的程序,它在右侧栏里加一个面板。ThinkTerm 启动它、和它通信,并把它发来的内容画出来 —— 桌面窗口里和浏览器里都一样 —— 但它的代码一行也不在 ThinkTerm 里面运行。一个插件崩溃,带走的只是它自己的面板,别的都不受影响。
文件与笔记 里讲的片段也是一个插件。它是内置的,用的接口和你安装的插件完全一样。
插件出现在哪里
只在右侧栏里。带面板的插件会以它自己的名字出现在侧栏的切换器里,和文件、笔记、片段并列,同时列在 设置 › 侧栏与插件 里。它不会添加自己的命令、菜单项或通知,命令面板里也不会有它的东西。和插件有关的键只有一个:一个可以绑定的动作,把键盘交给它的面板 —— 见在面板里输入和快捷键与命令。

要显示的东西多于侧栏放得下的面板,可以在侧栏左边打开一个更宽的视图,宽度由你拖动决定。没有地方放它时 —— 窗口太窄、手机上 —— 插件会把原本要放在那里的内容收进面板本身。
面板在浏览器里和在桌面端看起来一样,因为两边用同一份代码、按 ThinkTerm 自己的配色来画它。浏览器用的是为它提供页面的那个 server 所在机器上的插件,而不是浏览器所在设备上的 —— 所以一台无界面的主机显示的是它自己的插件,server 安装包也正因此附带了插件 server。见 网页访问。
在面板里输入
面板可以带输入框:单行的、多行的,或者显示成圆点的机密输入框。ThinkTerm 自己用自己的字体和配色来画它们,也自己处理编辑,所以输入法、选择和剪贴板在其中的表现和窗口里别处一样。机密输入框不能复制或剪切,里面的文字只有在你提交时才会交给插件。
键盘归终端,直到你把它交给某个面板。在输入框里点一下,键盘就交到那里;点面板里的其他地方不会,所以点击只是点击。在桌面端,你也可以用 FocusPluginPanel 给某个面板绑一个键。它会把右侧栏切到那个面板,并把键盘放进它的第一个输入框;再按一次这个键,键盘就还回去:
config.keys = {
{ key = 'S', mods = 'CMD|SHIFT', action = wezterm.action.FocusPluginPanel 'stocks' },
}浏览器里没有这样的键:那里的面板通过输入框拿到键盘。
面板拿着键盘时,插件只会听到它要求的那几个键(比如方向键)和输入框里的内容。按住 Ctrl、Alt 或 Command 的键永远到不了它。按 Escape,或点一下终端,键盘就还回去;Tab 在面板的输入框之间切换。如果面板丢了键盘却不是你拿走的 —— 比如它的插件停了 —— 你接着打的字哪里也不去,直到你按 Escape 或点一下,也不会落到终端里。
安装与移除
一个插件就是一个目录,里面放着 plugin.toml 和它要运行的程序。安装一个插件,就是把这个目录放进插件目录:
| 系统 | 插件目录 |
|---|---|
| macOS | ~/Library/Application Support/ThinkTerm/plugins |
| Linux | ~/.local/share/ThinkTerm/plugins |
| Windows | %APPDATA%\ThinkTerm\plugins |
没有安装步骤,也不用重启。ThinkTerm 下一次去看的时候就会发现新插件:打开 设置 › 侧栏与插件 时,或者运行 thinkterm plugin list 时。删掉目录就移除了它。
没见过的插件是 new,在你允许之前,它的任何东西都不会运行。设置 › 侧栏与插件 会把它显示为 新插件:允许之前不会运行,旁边有 允许 按钮,桌面端和浏览器里都一样;在 shell 里用 thinkterm plugin enable <id> 允许它,thinkterm plugin list 会把它的状态显示为 new。在那之前,它的面板不会出现,对它的调用会被拒绝,它的程序也不会启动 —— 即使设成了 保活 也一样。
ThinkTerm 记住你允许这个插件时它所在的目录。当它被移动、一个链接指向了另一份副本、另一个插件用了它的 id,或它的清单给了它另一个 id 时,它会重新变回 new,正在运行的那份会被停掉。原地更新插件,或者通过符号链接重新构建你正在开发的插件,都不会再问一次。在插件目录旁边的 plugins.json 里,等待中的插件被记为关闭,所以共用这个文件的、比这更旧的 ThinkTerm 也不会运行它。只是因为换了位置而关闭的,回到你允许它的位置后会重新打开;你自己关掉的,则一直关着。
没有任何东西会替你下载或安装插件,应用里也没有可供挑选的插件目录。机器上的每一个插件,都是有人亲手复制过去的。分享插件的人把它们放在 GitHub 上,加上话题 thinkterm-plugin,并把 plugin.toml 放在仓库根目录,这样把仓库克隆进插件目录就算装好了。插件是以你的身份运行的程序:不管它是怎么到你手里的,只复制你信任的。
命令行
thinkterm plugin 在 shell 里查看和操作插件:
| 命令 | 作用 |
|---|---|
thinkterm plugin list | 每个插件、它的状态(等待允许时是 new)和版本 |
thinkterm plugin enable <id> | 打开一个插件;对 new 的插件,同时也允许它运行 |
thinkterm plugin disable <id> | 关闭一个插件 |
thinkterm plugin reload [<id>] | 停下一个插件并重新读取它的清单;不给 id 时针对每一个已安装的插件 |
thinkterm plugin call <id> <json> | 向一个插件发出一次调用,并打印它的回答 |
thinkterm plugin dir | 打印插件目录 |
reload 还会清除插件的崩溃或失败状态,让它可以再试一次。同样的开关、一个 重新加载 按钮,以及打开插件文件夹的入口,都在 设置 › 侧栏与插件 里。
插件怎样运行
插件不是由窗口来运行的。它们跑在 thinkterm-plugin-server 之下,这是一个独立于 GUI、也独立于会话 server 的小进程,桌面端、浏览器和命令行都和它通信。每台机器上每个用户只有一个。第一次有东西需要插件时它启动,最后一个使用者离开 30 秒后退出,并把所有插件一起停掉。
每个你已允许的已安装插件又各是一个独立进程,在第一次被用到时启动 —— 一个面板出现,或者命令行发来一次调用。意外退出的插件会显示为已停止,下次被用到时再启动;一分钟内崩溃三次的插件会被标记为失败,在你重新加载它、或它的文件发生变化之前不再碰它。它的程序在磁盘上变了,ThinkTerm 会在下次使用前重启它。
插件闲着时运行多久
插件的某个面板正在任何地方显示时 —— 窗口里或浏览器里 —— 或者有一次对它的调用正在等回答时,它就算在使用中。不再使用之后,它的程序还会跑多久,取决于它的后台模式:
| 设置 | 何时启动 | 何时停止 |
|---|---|---|
保活 | 随这台机器上的 ThinkTerm 启动 | 这台机器上的 ThinkTerm 不在了 |
按需(默认) | 用到时 | 最后一次使用 2 分钟后 |
用完就停 | 用到时 | 最后一次使用 10 秒后 |
插件的清单写明它需要哪一种。设置 › 侧栏与插件 里每个插件旁的菜单可以逐个插件覆盖它,桌面端和浏览器里都可以改;菜单会把清单要求的那一项标为 插件默认。
这里的取舍,是在「打开面板时它已经就绪」和「你不看的时候它没在跑」之间。保活 适合替你盯着某样东西的插件;这也是 ThinkTerm 不经要求就启动插件的唯一一种模式,而且也只针对你已允许的插件。保活的插件自己停掉时,2 秒后会被再次启动,直到它被标记为失败。
「这台机器上的 ThinkTerm」指桌面应用或会话 server,哪个在跑算哪个。在一台没有桌面端的主机上就是会话 server,所以在那里保活的插件,会和那个 server 跑得一样久。每台机器各自记住自己的选择:同一个插件,可以在服务器上保活,在笔记本上按需运行。
面板旁边的终端
插件会被告知如何用 thinkterm cli 够到它面板旁边的终端,它运行的程序能在 THINKTERM_CLI 里找到 ThinkTerm 自带的那个 thinkterm。有了它们,插件可以读出那个终端显示的内容,列出各个 pane 里在运行什么,并跟踪那里发生的事 —— 程序和命令结束、标题、状态上报 —— 用的就是命令行里讲的那些命令。另一台机器上的终端,经由显示它的那个 ThinkTerm 够到,所问的事由它转达。这个终端就是获得焦点的 pane,你一切换过去,插件就会被告知新的终端;它的目录以及是否在另一台机器上,要等那个 pane 报告了自己的位置之后才跟上。
另一台机器上的终端
面板属于它旁边的那个终端 —— 所在窗口里获得焦点的那个 pane —— 插件会被告知那个终端的目录。当那个终端跑在通过 SSH 连上的另一台机器上时,插件本身仍然跑在这里,跑在本机上。它想在那边做的事,要请 ThinkTerm 代劳:运行一个程序、读一个文件,或者看看某个路径上是什么。ThinkTerm 用文件面板所用的同一条连接、以你的身份去做。
这些请求有几条值得知道的限制:
- 它们只能够到插件面板旁边那个终端所在的机器,而且只在那个终端还在那里时有效。为一台终端已经离开的机器发出的请求会失败。
- ThinkTerm 只在你允许之后才会连接一台机器 —— 在文件面板里,或者用面板在内容位置显示的那个按钮。在那之前,每个请求都会失败。在文件面板里允许过,对插件同样有效。某台主机若打开了
文件面板自动连接,当文件面板显示在它的终端旁边时就会自动允许;只是插件的面板显示着,并不会这样。 - 在那边运行的程序,20 秒后会被停掉。
这是桌面端的功能。浏览器里的面板不会用这种方式够到其他机器。
插件能做什么
插件是你放到机器上的程序,它以你的身份、带着你的权限运行。在你允许之前它什么都不会运行,所以没经你手出现在插件目录里的插件,只会被列出来。允许之后,ThinkTerm 不会在侧栏以外显示插件的任何东西,但这拦不住程序本身去读你的文件、访问网络或运行别的程序:只有沙箱才拦得住,而这里没有沙箱。请像安装任何其他程序那样安装插件。
远程请求把这一点延伸到了你通过 SSH 连上的机器:你在这里信任的插件,也就是你在每一台「终端挨着它面板」的主机上信任的插件。上面那些限制收窄的是它何时能够到那些机器,而不是够到之后能做什么。同样,插件能读出它面板旁边的终端显示的一切,就像任何以你的身份运行 thinkterm cli 的人一样。
ThinkTerm 源码里的插件
ThinkTerm 的源码在 plugins/ 下带了三个插件。它们不在下载的安装包里:每一个都要用 Cargo 构建,再把它的 plugin.toml 和程序复制到插件目录下它自己的目录里来安装。
- Diff(
diff)以 GitHub Desktop 的方式,显示旁边终端所在 git 仓库自上次提交以来的改动:面板里是改动过的文件,更宽的视图里是选中文件的各行,每一处改动都有标记。文件上方的输入框把文件缩小到路径里含有该文字的那些(不分大小写);面板拿着键盘时,上下键选前一个或后一个文件,在输入框里按 Return 则选中剩下的第一个。面板显示期间它每两秒运行一次git,在 git 处理得慢的仓库里会放慢一些,并且不运行仓库配置可以为它指定的那些程序 —— fsmonitor、外部 diff、文本转换器。挨着另一台机器上的终端时,它通过 ThinkTerm 在那边运行git。 - 股票(
stocks)是一份来自 Yahoo Finance 的自选行情,每个代码当天的走势画成一条线,外加选中那只的图表。添加代码的办法是在面板顶部的输入框里键入并按 Return;面板拿着键盘时,上下键选前一只或后一只。Yahoo 的图表 API 不是官方接口,随时可能拒绝或变化。 - 文本工具(
text-tools)在plugins/example里,是写插件的起点。它从命令行回答两种调用,decode和uuid,还有一个面板,能解码你在里面输入的内容 —— JWT、Unix 时间、URL 编码或 Base64 —— 或者在点Decode from the terminal后,解码旁边终端里最靠下、能解码的那一行,并把结果折行显示,能放几行就放几行。
编写插件
插件的程序通过标准输入和标准输出与 ThinkTerm 通信,每次一行 JSON。面板是手画的:插件发来一组矩形、文字、线条和填充区域,以及点击能落到它身上的位置,由 ThinkTerm 画出来。文字可以要求折行:这时它会被拆成盒子的高度所能容纳的行数,桌面端和浏览器拆得一样,放不下的部分以省略号结尾。没有控件。
Rust crate thinkterm-plugin-sdk 实现了这套协议,上面那些插件用的都是它。清单、消息、面板元素和各项限制,在 ThinkTerm 仓库的 docs/thinkterm/plugins.md 里有完整说明。开发时,把你的源码目录链接进插件目录,然后重新构建即可;程序写到标准错误的内容会进入插件 server 的日志。
