远程
文档编写中
部分内容可能尚不完整。
ThinkTerm 里的远程工作区,目标是用起来和本地一样。会话由多路复用服务端持有,而不是由你面前的窗口持有,因此合上笔记本、换一台机器、或者掉线,都不会终止正在进行的工作。同一个多路复用服务端也可以持有你的本地终端,所以下面讲的多数内容并不只适用于通过网络访问的机器。

SSH 主机簿
ThinkTerm 维护自己的主机列表,与你的系统 SSH 配置分开存放。在它旁边,~/.ssh/config 里定义的主机以只读视图呈现 —— ThinkTerm 会读取那个文件,但从不写入。
ThinkTerm 自己的列表里,每台主机可以记录用户名、身份文件、任意 SSH 选项、默认工作区,以及一个保存的密码。操作系统探测默认开启,连上的主机可以报告自己运行的是哪个发行版。
远程主机页面
这份列表在 远程主机 页面里,可以从侧栏底部的按钮打开,也可以用 Remote Hosts... 命令打开;Space 菜单里的 添加远程主机… 会直接打开一张空白的主机表单。搜索框为空时,最上面有一张卡片介绍 ThinkTerm Connect,带一个 添加服务器 按钮。你自己的主机排在前面,归在 主机 下。接着是 Tailscale 一组,列出 tailnet 里的机器(见内置 tailnet),再往后是来自 ~/.ssh/config 的主机,归在 来自你的 SSH 配置(n) 一组里 —— 这一组可以折叠,搜索时会自动展开。搜索框匹配主机的名称、地址和用户。
每张卡片在名称下方写着它连到哪里。ThinkTerm Connect 主机画成蓝色的服务器,只写地址;其他卡片还会写明连接方式,SSH 或 Mosh,并带一个通向 Connect 的按钮:你自己的主机上是 换成 Connect,当场改过去;来自 ~/.ssh/config 的主机上是 用 Connect 添加,会按它填好一张新主机表单。
选中一台你自己的主机,它会在列表旁的编辑区打开。拖动编辑区的左边缘可以调宽窄,ThinkTerm 会记住宽度。各分组从上到下是:
| 分组 | 内容 |
|---|---|
连接 | 名称、网络、主机、端口(新主机为 22)、其他地址、用户 |
连接方式 | 三张卡片选一张:ThinkTerm Connect、SSH、Mosh |
身份验证 | 密码、私钥文件 |
会话 | 工作区、检测操作系统、文件面板自动连接、只对普通 SSH 提供的 识别远端程序,以及选中 Mosh 时出现的 Mosh 服务端命令 |
高级 | 其他任意 SSH 选项,用 + 添加选项 逐条添加 |
新主机默认选 ThinkTerm Connect(标着 推荐),检测操作系统、文件面板自动连接 和 识别远端程序 都是开着的。文件面板自动连接 对在有这一项之前保存的主机是关闭的;打开后,只要这台主机的终端出现在旁边,文件面板就会连上去,不必等你点 连接 —— 见通过 SFTP 访问远程文件。
网络 是 系统 或 内置 Tailnet,见内置 tailnet。其他地址 列出这台主机还能在哪里应答 —— 局域网里的名字、Tailscale 名字、做了端口转发的公网地址 —— 用逗号或空格分隔,每个写成 主机 或 主机:端口;不写端口就用 端口。连接时先试 主机,再依次试其他地址,每个隔 0.3 秒,前一个失败则立刻试下一个。最先应答的那个承载连接,ThinkTerm 只在它上面登录一次。无论哪个地址应答,主机密钥都按 主机 来核对;某个地址上的服务端给出的密钥与记录不符,就当作另一台机器跳过。成功连上的那个地址,之后一小时内下次连接会先试它。
对已保存的主机,网络、连接方式 的选择,以及三个开关(检测操作系统、文件面板自动连接、识别远端程序)在点下的那一刻就保存了。你输入的内容要等 保存,取消 也不会撤销已经做出的选择。已保存的主机在编辑区顶部有 连接;新主机则有 保存 和 保存并打开(保存后立即连接)。一台主机由它的网络、地址、用户和端口来标识,所以这四项完全相同的第二台主机会被拒绝,并提示你去编辑已有的那一台;把主机切到另一个网络时,如果那里已经存了同一台,也是如此。
卡片菜单里有 连接、编辑主机…、创建副本 和 删除“名称”。创建副本 会打开一张按原主机填好的新主机表单 —— 密码和选项都在 —— 只有地址不复制,因为它必须不同;名称是原名后面加上 copy。删除需要再点一次 永久删除,因为它会连同这台主机的 Thread,以及从它镜像来的 Space 一起删掉。这些 Space 只是从这台设备上移除:服务端仍保留它们,把主机加回来,它们也就回来了。来自 ~/.ssh/config 的主机没有属于 ThinkTerm 的东西可编辑或删除,所以它的菜单里只有 连接。
给 ThinkTerm Connect 主机改名,或把它换到另一个网络,不会丢掉它的 Space:Space 跟着主机用上新名字。旧连接如果还连着,Space 就留在旧名字那边,直到它结束。
三种连接方式
一台主机可以用以下任意一种方式打开,在编辑区的 连接方式 里选。
普通 SSH —— 直连会话。连接断开时会话随之结束。
Mosh —— 一种漫游的 UDP 会话,能容忍网络切换和睡眠。ThinkTerm 默认用 mosh-server new -s -l LANG=en_US.UTF-8 启动远端;该命令可以按主机单独修改,当远程机器上的 mosh-server 位置不寻常时就需要这么做。
ThinkTerm Connect —— 常驻的多路复用,也是新主机的默认选择。只有这一种能给你完整的工作区:服务端持有会话树,因此多个客户端可以同时接入并看到同一套结构,某个客户端离开也不影响其余一切继续运行。
主机需要什么
普通 SSH 只需要一个 SSH 服务端。
Mosh 需要两端都装有 Mosh,而 ThinkTerm 两端都不自带。它先通过 SSH 登录,用这台主机的 Mosh 服务端命令 在主机上启动 mosh-server,再在本机按服务端的应答运行 mosh-client。当它自己做不到时 —— 没有保存的密码、需要确认主机密钥、命令执行失败 —— 它会退而在终端里运行 mosh 命令,让你自己回答它提出的问题。这条退路只带上主机的端口和私钥文件,不带其他 SSH 选项,也不带保存的密码。无论哪种,主机上都要装有 mosh-server,本机要装有 Mosh 的客户端。
ThinkTerm Connect 需要主机上装有 ThinkTerm。服务器版就够了,而且不需要 root:
curl -fsSL https://raw.githubusercontent.com/RoversX/thinkterm/main/install.sh | sh -s -- --server不需要配置任何服务。连接时会在主机上运行 thinkterm cli --prefer-mux proxy —— 优先用 ~/.local/bin 里的那份,其次是 PATH 上的 thinkterm —— 而它会在还没有 server 运行时启动 thinkterm-mux-server --daemonize。你也可以在有人连接之前,用同样的命令自己把它启动起来。
主机上还没有 ThinkTerm 也不是死路。连接时会先找 ~/.local/bin/thinkterm,再找 PATH 上的 thinkterm 或 wezterm;都没有的话,连接窗口会说明情况并问 Install it? [y/N](这段提示不翻译,保持英文),同时给出要运行的命令:用这个版本的安装器,从 GitHub 取下来,以 ssh 用户的身份在主机上运行,装到 ~/.local 下,不需要 root。回答 y 就会安装,然后连接。其他回答会把命令打印出来,让你自己去运行;如果你是从远程主机页面发起的连接,这一次还会改用普通 SSH 打开这台主机。它仍然是 Connect 主机,下次连接会再问一次。装不了的时候,它不会问,而是直接说明原因:这个应用是开发版,GitHub 上还没有它那个版本的发布,或者主机装不了发布版 —— 不是 x86-64 或 ARM64 的 Linux CPU、musl libc,或者 glibc 比发布版要求的旧。
在 Linux 主机上,退出最后一个 SSH 会话可能会把 server 一起带走,这取决于系统如何处置会话留下的进程。安装器建议的做法是 loginctl enable-linger $USER,以让它继续运行。
两端必须运行同一个版本。版本不一致时,ThinkTerm 会提出从这一端修复,见 更新远端 server。服务器版的更多细节见 安装。
内置 tailnet
ThinkTerm 可以不装 Tailscale 应用,自己加入 Tailscale 网络。加入之后,tailnet 里的机器会列在远程主机页面里,把 网络 设为 内置 Tailnet 的主机就经由它连接。它在 macOS 和 Linux 上可用;其他系统上不显示 Tailscale 一组,网络 里也会说这个系统上不支持内置 Tailnet。
开启。 关着的时候,Tailscale 一组里有一张卡片,写着 连 tailnet 里的机器,带一个 开启 按钮。点它会打开 Tailscale 面板,顶替主机编辑区;这一组标题末尾的齿轮随时可以再打开它,在设置里搜索 Tailscale 会找到的 Tailscale → 远程主机 也行。标题上写着它现在的状态:未开启、正在连接…、需要登录、已连接、没连上 或 别的进程在用。
登录。 第一次加入的电脑会显示 需要登录;点 登录 会在浏览器里打开批准链接,把这台电脑加进 tailnet。面板的 登录 一组里还有 打开登录链接 和 复制链接,用来在别的设备上批准;也可以用 tailnet 里生成的密钥,填在 Auth key(不开浏览器时用)。
| 面板 | 内容 |
|---|---|
| 顶部 | 开启、关闭 tailnet 的开关 |
状态 | 已连接 · 名称 · 地址,或者没连上的原因 |
登录 | 等待批准的链接,以及 Auth key(不开浏览器时用) |
连接 | 控制服务器(留空用 Tailscale 自己的,Headscale 则填它的 HTTPS 地址)和 本机名字(留空时是 thinkterm- 加这台机器密钥的四个字符) |
退出登录 | 退出 tailnet,并忘掉这台电脑的身份和 auth key。只在有东西可忘时显示 |
每个字段在你离开它时保存。换了控制服务器或名字会重启节点,经由 tailnet 的连接随之结束。关掉开关只是退出 tailnet,电脑的身份还留着,所以再打开仍是同一台机器;退出登录 之后再加入就是一台新机器,旧的会一直列在 tailnet 里,直到你在那边把它删掉。
机器。 在线的排前面,其余按名称排。卡片上有设备图标 —— 厂商标志,Linux 则是发行版的标志 —— 还有名称,以及它是什么设备、是否在线,比如 Mac · 在线。点一台已经存成主机的机器,会像点其他卡片一样选中那台主机并打开它的编辑区,双击则连接。点一台没存过的机器,会打开一张新主机表单,名称和地址已填好,网络 是 内置 Tailnet,光标落在 用户 上;保存 后它就和其他主机一样。一台已保存的主机,如果它的地址(或 其他地址 里的某一个)是这台机器的名称、在 tailnet 里的全名或 tailnet 地址,它就代表这台机器。
把主机放到 tailnet 上。 在 网络 里选 内置 Tailnet。Tailscale 没开之前它是灰的,并附有说明;已经在 tailnet 上的主机仍会显示这个选择。选中后,主机的每个地址 —— 主机 和 其他地址 —— 都经由 tailnet 拨号,即使失败也不会改走系统网络。名字只在 tailnet 的机器列表里查,不走 DNS,所以请用机器的名称、全名或 tailnet 地址。Mosh 使用系统网络,因此 Mosh 主机不能放到 tailnet 上,tailnet 上的主机也不能改成 Mosh;高级 里设了 ProxyCommand 或 BindAddress 的主机同样不能用。同一个地址在两个网络上算两台主机。
在哪里运行。 节点是一个独立的程序 thinkterm-tailnet,tailnet 开着时由本机的会话 server 启动,停了就重新启动。server 结束,或者你关掉 tailnet,它也就结束。开启时如果没有会话 server 在运行,ThinkTerm 会专门为此启动一个,里面没有终端,与 本地终端放在后台会话 server 里 的设置无关;停止会话 server 也会让节点一起停下。同一时刻只有一个进程持有节点,本机所有 ThinkTerm 进程都通过它连到 tailnet。别的进程在用 的意思是这台电脑上另一个 ThinkTerm 进程持有着它。
没有人坐在前面的机器
thinkterm tailnet 可以在 shell 里做同样的事,要以持有节点的会话 server 所属的用户身份运行,不能用 sudo。
| 命令 | 作用 |
|---|---|
thinkterm tailnet up | 加入并等到连上;需要批准时打印链接。选项:--serve-ssh、--control-url URL、--hostname NAME,以及从标准输入读取 auth key、代替在浏览器里批准的 --auth-key-stdin |
thinkterm tailnet down | 退出 tailnet,身份留给下一次 up |
thinkterm tailnet logout | 退出,并忘掉身份和 auth key |
thinkterm tailnet status | 显示设置、节点的状态和它看到的机器 |
--serve-ssh 让 tailnet 里的机器可以 SSH 到这台机器:节点把它 tailnet 地址上的 22 端口交给本机自己的 sshd,并且不接受 tailnet 来的任何其他连接。sshd 看到这些连接来自 127.0.0.1 或 ::1,所以你为本地连接定的规则也适用于它们。只有 up --serve-ssh 会打开它;不带这个选项的 up 会把它关掉,down 和 logout 也会,面板里的开关则从不打开它。开着的时候,面板会写 tailnet 里的机器可以 SSH 到这台电脑(thinkterm tailnet up)。
它保存了什么
设置在主机簿旁边的 tailnet.json 里。其中的 auth key 和节点密钥与保存的密码用同一个 secret.key 加密,所以同样的限制也适用于它们。本机的其他 ThinkTerm 进程通过设置旁边的 socket tailnet.sock 连到节点,只有你的账户能打开它。加入时会与 Tailscale 的控制服务器通信,或者与你在 控制服务器 里设的那一个通信。
保存的密码
主机簿里保存的密码以 AES-256-GCM 加密存储。密钥单独存放在一个文件 secret.key 中,与主机簿在同一个目录里。
在 macOS 和 Linux 上,该密钥文件以 0600 权限创建,因此这台机器上的其他账户读不到它。
但要清楚它挡不住什么。这个权限挡的是其他用户,挡不住任何以你自己身份运行的东西。文件同步、备份任务,或者你启动的任何程序,读一个 0600 文件毫无障碍 —— 而 secret.key 与加密后的主机簿在同一个目录里,所以凡是复制走这个目录的,两半都拿到了,可以从容解密。把密钥放在磁盘上而不是系统钥匙串里是一个有意的取舍,这就是它的代价。
Windows 上没有与之对应的权限位
在 Windows 上,密钥和主机簿一起放在 %LOCALAPPDATA%\ThinkTerm\ssh\,与其余工作区数据分开 —— 这是刻意挪出漫游配置文件的,这样域账户注销时不会把你的凭据带到配置文件服务器上。旧安装里的那两个文件会在 ThinkTerm 第一次需要时成对迁移过去,不会只搬一半。
文件本身在那里依然没有属于自己的权限设置:它继承所在目录的 ACL。如果这台机器由多个账户共用,就应当把保存的密码视为"任何能访问该文件夹的人都可读"。
如果你不想保存密码,改用身份文件即可 —— 它在同一个编辑界面里,也是按主机设置的字段。
断开不等于删除
把一个远程 Space 从侧栏移走,和把它删除,是两个不同的动作,ThinkTerm 有意把它们分开。远程 Space 的菜单里最多有三项,归在它的名字下面:
| 菜单项 | 作用 |
|---|---|
断开连接(服务器继续运行) | 丢掉这台设备上的这份 Space。服务端继续运行,里面的每个会话都保持存活;这台设备下次连接时,Space 会回来 |
在服务器上删除(所有设备) | 在服务端删除这个 Space,于是它从每一台设备上消失 |
删除并结束远程会话 | 在服务端删除它,并结束其中正在运行的会话。只在服务端处于连接状态时提供 |
合上笔记本时三项都用不着:服务端照常运行,下次连接时从你离开的地方继续。
网络意外中断后,ThinkTerm 会自行重连并恢复会话。在重试期间,它会显示为"正在连接",而不是报为失败。
但自动重试是有上限的。约两分钟后,ThinkTerm 会停止尝试,等你按下"重新连接"。这个计时器量的是工作区处于不可用状态有多久,而不是网络断了多久 —— 一条反复连上、但恢复的会话每次都被拒绝的链路,同样会走到这个上限;只有真正恢复的会话才会把它清零。一个确实恢复过、正常用过的会话 —— 或者仅仅是撑过了那两分钟的会话 —— 下次断开时会从头计时,而不是接着旧的时钟走。放弃时不会拆掉任何东西 —— 域仍然处于接入状态,每个窗口和 pane 都还在 —— 但超过这个时长的中断,需要你手动操作一步才能恢复。本地会话 server 是例外:对它永远不会放弃。
挺过一次升级
以前更新 server 意味着停掉它,而停掉 server 会结束它持有的所有会话。现在不必这样了。
运行中的 server 可以把自己的 pane 直接交给新的二进制:新进程接管伪终端、其上运行的进程、终端内容,以及窗口和标签结构,然后在同一个 socket 上开始应答。客户端只会看到一次重连并按 id 重新绑定,pane 里面的东西不会重启。
有两类 pane 不会被带过去:进程已经退出的,以及本身就是通往别处的通道的——比如作为独立域打开的 ssh 或 tmux pane。它们会被剪除而不是移交。
这就是 更新远端 server 时保留会话 背后的机制,完整流程见 更新。
本地会话放进会话 server
多路复用服务端并不只服务于远程主机。在 设置 › 通用 › 后台会话 下:
本地终端放在后台会话 server 里 —— 本地终端跑在本机的 mux server 中,ThinkTerm 退出、崩溃或更新后会话仍在。下次启动 ThinkTerm 时生效。
开启后,退出 ThinkTerm 不再是结束本地工作的那个动作,Thread 布局也会被沿用而不是重建。新安装默认开启;在它成为默认值之前就已经有设置的安装会保留原来的设置,在你手动打开之前保持关闭。切换后在下次启动时生效,不是立即生效。
Windows 上同样可用,但那里承诺的略少一些:本地终端能挺过 ThinkTerm 的退出和崩溃,而安装更新会停掉 server,里面的东西也就结束了。上面讲的把 pane 交给新二进制那套是 Unix 的机制,尚未移植。
它也是本地终端能被浏览器够到的前提 —— 浏览器客户端由 mux server 提供服务,而不是 GUI 自带的多路复用器。见 网页访问。浏览器里的页面还可以被允许经由这台服务端打开其他机器,由服务端用 SSH 登录它们;见连接其他机器。
经过 server 运行的代价比想象中小,因为 server 知道它的客户端是不是在同一台机器上。在 macOS 和 Linux 上,它能认出 socket 另一端的进程,并把发给这个客户端的数据以不压缩的形式、经由更大的缓冲区送过去 —— 正因如此,本地 pane 里的一张大图或一段快速动画,不会让两个进程付出远超绘制本身的代价。
主动结束它
因为这个 server 活得比应用久,所以有一套专门结束它的方式:thinkterm cli stop-server;Quit and Stop Session Server 命令 —— 在 macOS 的 ThinkTerm 菜单里紧挨着 Quit,命令面板里也有,这个名称在界面上不翻译;以及 设置 › 通用 › 后台会话 下的 停止会话服务器,这一行只在 server 运行时出现,要按两次才会执行。三条路做的是同一件事 —— server 里的每个终端都会结束、它的窗口会关闭,而你下次打开的终端会启动一个全新的 server。
在多台设备之间移动
多个客户端可以同时连到同一个 ThinkTerm Connect server。同一时刻由一台设备驱动前端,而这个权限——交接租约——属于设备而不是进程:一块由主机和用户标识的屏幕。
正是这个区分让重启的代价变得很低。原本在驱动的那台设备断开后再回来(GUI 重启了、笔记本的 SSH 重新建立了),它不会被当成又一块来排队的屏幕,而是在第一帧画面上就把租约收回,不需要点击。
其他设备仍然必须显式接管 —— 但主动发起连接本身就是那个动作。按下"连接",或者切进一个服务端尚未接入的 Space,租约会作为打开过程的一部分被拿到,于是从第一帧起你就是在往那个终端里打字。后台自动重连不会这样做,一直连着的渲染端也不会自动继承租约。一旦某台设备认领过,之前的持有者回来也不会把它抢回去。如果你更希望手动认领,remote_mux_auto_claim_on_open 可以关掉自动的那一半。
另一台设备正在驱动的终端不会对你隐藏。它画的就是那台设备看到的画面,角上写着是哪台机器握着它、点一下或滚一下就能拿过来。在你这么做之前,按键、拖拽和拖动分隔条都不会生效,而不是落到你看不见的地方。
浏览器在这里也算一台设备,按它进来时用的那条链接的名字来标识 —— 同一条链接开的两个标签页共享同一次轮值,thinkterm cli list-clients 里也以那个名字显示。
以上说的都是 server 的默认模式 B · Handoff (exclusive)。终端右键菜单里的 Frontend access 可以把它切换到 A · Shared (tmux-like),那里没有轮值:每一台接入的设备都可以同时往每一个终端里打字,就像 tmux 一样。这个模式属于 server,而不属于某个终端或某台设备,所以切换它会影响所有接入的设备。桌面端的这个菜单不翻译,按英文显示。
远端正在运行什么
tab 可以显示一个图标,表明 pane 里正在运行的程序。在 ThinkTerm Connect 主机上,服务端会观察自己的 pane,并告诉每个接入的客户端各自在运行什么,所以远程 tab 的标注方式和本地的一样。
走普通 SSH 时,ThinkTerm 看不到远端的任何进程 —— 本地唯一在运行的只是 ssh —— 所以除非远端 shell 自己报告在运行什么,tab 能说的也就只有这些。ThinkTerm 自己列表里的每台主机都有一个 识别远端程序 开关,用来做这件事。新主机默认打开,在有这个开关之前保存的主机则是关闭的,而且只对普通 SSH 提供:ThinkTerm Connect 用不着它,Mosh 则不会把这些报告传过来。
打开它,在下一次连接之前什么都不会变。下一次连接时,ThinkTerm 会通过 SFTP 把它的 shell 集成复制到主机上的 ~/.config/thinkterm/,并在登录 shell 读取的启动文件里加上一行带标记的内容 —— .zshrc(ZDOTDIR 指到哪里就是哪里),或者 .bashrc 加上 bash 读取的登录文件。主机上的其他东西一概不动。改动任何文件之前,会先确认每个文件都可写;装到一半的会被撤回。关掉开关,下一次连接时会用同样的方式把那一行删掉。
只支持 bash 和 zsh。登录 shell 是别的(比如 fish)的主机,会在开关下方如实说明,并且什么都不留下。失败也显示在那里,指出哪个文件写不进去,下一次连接会再试。每次连接做成了什么都会记在这台主机上,下一次连接不会重复去做。
装好之后,shell 会在每条命令开始时报告它的命令行,并在回到提示符时清空。之后新开的 shell 才会报告,已经开着的不会。sudo 后面的命令或前面带 VAR=value 的命令,报出的是实际运行的那个程序;在本地 pane 里直接输入 ssh 时,只要另一端的 shell 加载了这个集成,同样会读取这份报告。
接入的服务端能描述什么
你接入的服务端只描述属于它自己的 Space。它给出的 Space id 如果已经属于你别处的东西(本地默认 Space,或者另一个域),那份数据会被丢弃,而不是被允许顶替记在这个 id 下的 project 与布局;而一个 project 如果指向服务端并没有发来的 Space,它会被丢弃,而不是被悄悄塞进你的默认 Space。不会因为某个服务端认领了一个已被占用的 id,你手上的东西就变了样。
为了让远程用起来像本地做了什么
以下行为适用于 ThinkTerm Connect 域上的会话:
- 在等待会被察觉时,打字不等待往返。 预测性本地回显让按键不等服务端就显示出来,但只有当实测往返时延达到阈值(默认 100 毫秒)时才启用。低于这个值,服务端自己的回显已经够快,预测保持关闭。本地会话则完全不预测。
- 分屏不是本地专属的奢侈品。 分屏树、二级标签栏、拖拽分屏、拖动分隔条调整大小,在远程会话中同样可用,行为与本地一致。
- 尺寸不会抖动。 拖动分隔条不会导致回弹、挤压或重同步风暴。
- 分隔条按链路允许的速度调整。
设置 › 终端 › 远程分屏缩放决定拖动分隔条时尺寸何时送到服务端。默认的自动在连接较快时持续发送,在连接较慢时只发送最终尺寸;实时总是持续发送,松手后只在你松开时发送。 - 滚轮是准确的。 在
vim、less这类鼠标模式程序里,原始的滚动格数被保留,因此滚动既不会太快也不会太慢。 - 客户端状态归你所有。 焦点和选区以本地为准,不会被服务端回显打乱。每个客户端持有自己的配色,在一台设备上换主题不会改变另一台。
- 图片能传到远程 pane。 图片和 kitty 协议的动图在远程 pane 里的显示与本地一致;正在流式传输的图片会留在屏幕上,而不是每一帧都丢掉。
