网页访问
文档编写中
部分内容可能尚不完整。
ThinkTerm 的多路复用服务端可以把它的终端交给浏览器。一部手机,或者一台没装 ThinkTerm 的机器,打开的是装着 ThinkTerm 的那台机器上已经在跑的会话 —— 同一套 Space、Project 和 Thread,不是副本。
在你主动打开之前,什么都不会监听。
打开它
三种方式,面向的都是多路复用服务端,而不是 GUI 自带的那个进程内多路复用器:
设置 › 网页—— 一个开关、正在接受连接的地址,以及你已经发出去的链接列表。thinkterm cli web-server on—— shell 里的同一件事。--bind-address选择监听地址,status报告在哪里接受连接,off关闭端口并断开上面的浏览器。- 配置文件里的
web_servers条目 —— 在服务端启动时读取,于是它每次运行端口都是开着的。
前两种改的是运行中的服务端,效力与它同在。配置条目才是持久的那一种;重新加载配置不会打开或关闭网页端口。
本地终端需要跑在后台会话 server 里,才有东西可供服务 —— 那个设置在 远程 里有说明,新安装时它默认是开着的。没有它时,设置 › 网页 会直接说明情况,而不是给你一个按下去没有用的开关。如果当前接入的只有一个远程域,那一节讲的是那台机器的端口,不是本机的。
链接就是凭据
浏览器靠一个令牌进入,而令牌是以 URL 的形式交到你手上的:
thinkterm cli web-token mint --label phone --ttl 12h打开这条 URL 的人,在那台机器上拥有的东西和你一样:一个以你身份运行的 shell,以及每个 pane 的回滚内容。请按对待 ssh 私钥的方式对待它 —— 尤其是,不要把它贴到任何你不会贴私钥的地方。
--ttl接受30m、12h、7d,或者一个秒数。不给这个参数,令牌就一直有效到被吊销。从设置里复制的链接默认 8 小时后过期,除非你另选,最长可到直到手动吊销。web-token list列出服务端知道的每个令牌,以及各自连着几个浏览器。web-token revoke <id>,或者--all,立刻切断它们,包括正在连着的浏览器。--url-only只打印 URL,便于脚本使用。
已连接的浏览器会出现在 thinkterm cli list-clients 里,用的就是签发令牌时给的名字。
令牌放在 URL 里 # 之后的那一段,浏览器从不把这一段发给服务端,也不会放进 Referer。页面把它移到这个浏览器标签页自己的会话存储里,并把地址栏改写成不带令牌的样子,免得它被意外收藏、分享或截进截图。同一个标签页里刷新照常可用;新开的标签页,或者只输入地址,都需要再用一次链接,页面会这样提示。
谁能够到这个端口
默认监听 127.0.0.1:8088 —— 只有它自己运行的那台机器够得到,别的都不行。
其他设备也能访问 会把它改绑到这台机器的每一个地址上,所以只在你信任的网络里打开它。浏览器只对安全上下文开放 WebGPU,而 http://localhost 算一个、指向其他主机的普通 http:// 不算,所以非环回地址上的监听器需要 TLS:在你没有提供自己的证书时,ThinkTerm 会生成一张自签证书,写上这台机器的主机名和各个地址。浏览器会为这张证书警告一次,然后就正常画页面了。接受警告之前,请把浏览器里这张证书的 SHA-256 指纹,与 设置 › 网页 里显示的、或者 web-token mint 完整输出里打印的那个对一下 —— --url-only 不会打印它 —— 不一致就停下。默认开启的 require_tls_off_loopback 正是那个拒绝以明文服务非环回地址的开关 —— 那样服务出去的页面本来也没有 WebGPU,什么都显示不出来。
同一局域网或 tailnet 里的手机,用 设置 › 网页 里的二维码把链接带过去最省事。跨公网时,用 ssh -L 把环回端口转发出去,而不是把它暴露出去。
在反向代理之后
监听器只接受来自它自己地址的页面。通过其他名字访问到的页面 —— 比如经由反向代理的 https://terminal.example.com —— 必须列在 allowed_origins 里,否则连接会被拒绝。只要列出任何一个 origin,就会取代上述默认,web-token mint 打印的链接也随之指向列出的 origin。
设置 › 网页
| 行 | 作用 |
|---|---|
允许浏览器访问 | 在会话 server 上打开或关闭端口 |
其他设备也能访问 | 带着自己的证书监听每一个地址,而不只是环回地址 |
访问链接 › 复制链接 | 签发一条链接并复制 |
链接有效期 | 下一条复制的链接能活多久:1 小时、8 小时(默认)、1 天、7 天或 直到手动吊销 |
用手机扫码 › 显示二维码 | 签发一条链接并以二维码显示 |
已发出的链接 | 每一条仍然有效的链接,各带一个 吊销 按钮 |
所有链接 › 全部吊销 | 一次性切断所有浏览器 |
每条发出去的链接,以签发时给的名字命名;没有名字就用最后一次使用它的设备;还没人用过就显示 尚未使用。下面写着它何时过期、有几个浏览器通过它连着。吊销 只结束这一条链接并断开正在用它的浏览器,其余链接不受影响。
web_servers 条目
web_servers 里的每个条目就是一个端口,在服务端启动时读取:
config.web_servers = {
{
bind_address = '127.0.0.1:8088',
token_file = wezterm.home_dir .. '/.local/share/thinkterm/web-tokens.json',
},
}| 字段 | 默认值 | 含义 |
|---|---|---|
bind_address | 127.0.0.1:8088 | 监听的地址和端口 |
pem_private_key | 不设置 | PEM 私钥。与 pem_cert 一起设置后,端口提供 https 和 wss |
pem_cert | 不设置 | PEM 证书 |
pem_ca | 不设置 | PEM CA 证书链 |
static_dir | 已安装程序的旁边 | 页面文件所在的位置 |
token_file | 不设置 | 已签发令牌的存放位置,以摘要形式保存 |
allowed_origins | 监听器自己的地址 | 允许建立连接的 origin |
require_tls_off_loopback | true | 在非环回地址上没有 TLS 就拒绝启动 |
token_file 比看上去重要。 不设置时,令牌只存在服务端的内存里:服务端一旦重启 —— 机器重启之后,或者一次无法把会话交接过去的更新之后 —— 每一条链接都会失效,必须重新签发。设置之后,链接能撑过重启。文件里存的是摘要,从来不是令牌本身,所以拿到它的副本也进不来。
static_dir 用来运行另一份页面,而不是已安装的那一份,比如开发构建;环境变量 THINKTERM_WEB_STATIC_DIR 不改配置也能做到同样的事,两者都设置时以 static_dir 为准。
网页端能做什么
浏览器画的是同一个工作区:Space / Project / Thread 树、标签栏、分屏、搜索面板,以及右侧面板。Thread 和 pane 都可以在上面新建和关闭。
右侧面板提供片段、Agents,以及已安装插件的面板。片段和插件来自提供这个页面的那台机器,而不是浏览器所在的那台:指向某台服务端的浏览器,看到的是那台服务端的片段和插件。「运行」或「粘贴」会落到按下那一刻聚焦的 pane 里。Agents 面板默认关闭,用标签栏最右端的按钮打开。
用 Kitty 图形协议发来的图片会在网页里画出来,包括动画。通过 iTerm2 协议发来的 PNG 或 JPEG 图片目前还不会。
侧边栏
侧边栏和桌面端一样,一次显示一个 Space:顶部是它的名字,然后是 新建会话,已置顶 下面是置顶的 Thread,再往下 工作区 下面是各个 Project 及其 Thread。Space 名字旁边的 … 列出其他 Space,并提供 新建空间,以及重命名或删除当前显示的那个 —— 最后一个 Space 永远删不掉。
在页面上你可以:
- 新建、重命名、切换和删除 Space
- 在
添加工作区…下输入服务端上的一个目录~/dir或/dir来添加 Project,它会带一个mainThread - 单击 Project 折叠它,双击名字重命名,归档、恢复、移除它
- 新建、置顶、取消置顶和删除 Thread,双击名字重命名,以及标记为未读
- 在 Project 内拖动 Thread、在 Space 内拖动 Project 来重排
已归档的 Project 收在 Space 底部的 已归档(N) 下,点一下就会展开。Thread 行上的删除按钮和标签上的关闭按钮都会再问一次,显示 删除? 或 关闭?,因为它们会结束里面的程序。服务端上没有被任何 Thread 认领的窗口列在 其他窗口 下,所以没有什么是够不着的。
侧边栏的宽度在它的边缘拖动,并由浏览器记住。标签栏开头的按钮可以把它收起;打开 悬停时显示侧栏 后,把指针停在左边缘它就会出现,指针离开后收回。
菜单
右键提供桌面端的菜单,只是去掉了服务端没有对应操作的那些:
| 对象 | 提供 |
|---|---|
| pane | 复制、粘贴、向右分屏、向左分屏、向下分屏、向上分屏、前端访问模式 › A · 共享(类似 tmux) / B · 交接(独占) |
| 标签 | 关闭左侧标签页、关闭右侧标签页、关闭其他标签页、在右侧新建终端标签页、放大窗格 |
| Thread | 置顶会话 或 取消置顶会话、重命名会话…、删除会话、标记为未读 |
| Project | 重命名项目…、新建会话、折叠 / 展开会话、归档项目…、移除项目 |
| 已归档的 Project | 取消归档、永久删除… › 删除项目及会话 |
Project 里有 pane 在运行时,归档项目… 会先说明要关闭几个,再由 归档并关闭面板 执行。重命名标签、在窗口之间移动标签以及重置终端,是桌面端独有的。标签仍然可以沿标签栏拖动来重排,把一个 pane 拖到另一个 pane 的边缘,它就会移到那里。
每个 pane 都带一条栏,左边是它的标签,右边是在这个 pane 里新建标签、向下分屏、向右分屏和放大。
搜索面板
Mac 上按 ⌘K,其他系统按 Ctrl+K,打开搜索面板;快捷键可以在网页自己的设置里更换。它按分组查找:
查找线程—— 所有 Space 里的 Thread标签页和窗格—— 当前显示的窗口里的那些切换空间—— 各个 Space命令——新建会话、新建标签页、向右分屏、向下分屏、放大窗格、关闭窗格、接管终端、跟随桌面端的焦点、显示或隐藏侧栏、设置、放大字号、缩小字号、重置字号
什么都没输入时,最近选过的排在前面。方向键移动,Enter 执行,Esc 关闭。侧边栏底部的放大镜打开的是只含 Thread 的搜索。
终端由谁驱动
终端怎样共享由服务端决定:pane 菜单里的 前端访问模式 子菜单用来切换,当前模式旁边带勾。这个选择由服务端保存,重启后也还在,所以它适用于接入这台服务端的每一台设备,而不只是这个页面。
B · 交接(独占) 是默认模式,一个终端同一时刻只有一个驱动者。打开桌面端正握着的终端时,上面会盖一张卡片 —— 终端正在另一台设备上使用、点击或滚动即可继续 —— 终端在下面仍然看得见。在里面点一下、滚动一下或按一个键,就把它接了过来。第一次按键不会丢,交接完成后照样打进去。点击标签栏、pane 栏和侧边栏永远不会接管它。Ctrl+Shift+T,或搜索面板里的 接管终端,会明确地接管它。这与 远程 里讲的交接是同一套机制,浏览器在其中算作一台由它的链接标识的设备 —— 同一条链接开的两个标签页共享同一次轮值。
A · 共享(类似 tmux) 没有驱动者可交接:所有接入的客户端看到同样的终端,都能往里打字,最后一个与某个标签交互的客户端决定它的尺寸。
页面握着一个标签时,这个标签会被调整成浏览器窗口的形状。桌面端握着它时,页面保持桌面端的尺寸和形状,标签比窗口大时会被裁掉。Ctrl+Shift+F 可以随时把标签调整成这个窗口的形状。
跟随桌面端的焦点 在页面打开时就是开着的,它让两边保持同一个焦点:桌面端切到另一个 pane,页面跟着切;在页面里点一个 pane,桌面端也跟着动。再执行一次这个命令即停止跟随;它不会跨页面加载保留。
剪贴板
用鼠标选择文本,松开的那一刻就复制了。双击选择一个词,三击选择一行。程序要求了鼠标时,按住 Shift 可以自己选择文本。pane 菜单里也有 复制 和 粘贴。
程序可以用 OSC 52 设置剪贴板,但只限页面正在显示的 pane:没人在看的 pane 无权写这台设备的剪贴板。
键盘快捷键
| 按键 | 作用 |
|---|---|
Cmd+C 或 Ctrl+Shift+C | 复制所选内容 |
Cmd+V 或 Ctrl+Shift+V | 粘贴 |
Ctrl+Shift+Enter | 向右分屏 |
Ctrl+Shift+\ | 向下分屏 |
Ctrl+Shift+Z | 放大 pane,或取消放大 |
Ctrl+Shift+F | 把标签调整成这个窗口的形状 |
Ctrl+Shift+T | 接管终端 |
Ctrl+Shift 加方向键 | 聚焦相邻的 pane,不带动桌面端的焦点 |
Cmd+=、Cmd+-、Cmd+0 | 当前 pane 的字号:放大、缩小、重置 |
⌘K / Ctrl+K | 搜索面板,除非你换了别的快捷键 |
其余所有 Cmd 组合都留给浏览器 —— 刷新、地址栏、标签页 —— 永远不会到达终端。其他一切按键,包括 Ctrl 组合键,都交给当前 pane;所以在 Mac 以外的系统上,默认的搜索快捷键意味着 Ctrl+K 到不了终端,如果某个程序需要它,请在设置里改用 Ctrl+Shift+P。输入法可以正常使用:输入法正在组字的按键留在输入法那里,候选窗口跟随终端光标。
在手机上
指针是手指,或者窗口窄于 720 像素时,页面切换到手机布局。请先按上文所述通过 https 连到服务端。
- 终端占满整个屏幕。侧边栏和 Agents 面板变成盖在上面的抽屉;从左边缘向内滑动拉出侧边栏,向左滑回去收起。在抽屉里选中一个 Thread 后它会自动收起。
- 底部的按键栏提供
Esc、Tab、Ctrl和Alt、四个方向键、Home、End、PgUp、PgDn,以及-/|~。Ctrl和Alt是粘滞的:点一下,它就作用于下一个按键,无论来自按键栏还是软键盘。按住方向键会连续重复。 - 软键盘只在你用按键栏上的键盘按钮要求时才出现,所以点一下终端不会让半个屏幕被挡住。
- 手指拖动用来滚动,快速一划会继续滚下去。两根手指也能滚动;双指捏合改变手指下那个 pane 的字号。
- 轻点就是点击。长按半秒打开 pane 的菜单。
- 只有一个 pane 的标签不显示 pane 栏;有了分屏、需要区分时才出现。
触屏上无法选择文本:在手机上拖动永远是滚动。
网页自己的设置
侧边栏底部的齿轮打开网页自己的设置。它们属于这个浏览器,不属于服务端,也不属于你的账号。
| 分区 | 设置 | 选项 |
|---|---|---|
| 通用 | 语言 | 跟随系统(即浏览器的语言),或 ThinkTerm 支持的某种语言 |
| 通用 | 滚动方式 | 平滑(默认),按像素跟随手指或触控板;或 逐行,每次一行 |
| 通用 | 搜索快捷键 | ⌘K(默认)、⌘⇧P、Ctrl+Shift+P。在 Mac 以外的系统上,⌘ 指 Ctrl |
| 外观 | 配色方案 | 跟随桌面端(默认),即使用服务端配置的配色方案;或任意一个内置方案 |
| 外观 | 主题 | 深色(默认)、浅色 或 跟随系统,作用于终端周围的页面 |
| 外观 | 字号 | 跟随桌面端,让这里的一个单元格与那边的一样大;或 固定字号,6 到 72,以半磅为步长 |
| 侧栏与插件 | 悬停时显示侧栏 | 默认开启 |
| 侧栏与插件 | 重置侧栏宽度 | 把侧边栏恢复到默认宽度 |
| 侧栏与插件 | Agent 面板、片段 | 右侧面板是否提供它们 |
| 侧栏与插件 | 重新加载插件,以及每个插件 | 一个开关,以及闲置时保持运行多久 |
| 关于 | — | 正在使用的语言、页面来自的地址,以及构建 |
配色方案 会打开一个可搜索的列表,每个方案旁边附有它的颜色;指针或方向键停在哪一行,终端就预览哪一个,Enter 保留,Esc 恢复原来的选择。设置顶部的搜索框能在任何分区里找到某一行。
存在哪里
存在浏览器的本地存储里,一起存的还有页面上次显示的 Space、搜索面板最近的选择、侧边栏宽度,以及选中的配色方案连同它的颜色,这样下次加载时一开始就用它来画。换一个浏览器,或者用隐私窗口,都从默认值开始。这些都不会发给服务端。禁止存储的浏览器只在本次加载中保留这些设置。
URL 覆盖
URL 可以为一次加载覆盖部分设置,而不改变已存储的内容:
| 参数 | 效果 |
|---|---|
?lang=de-DE | 页面语言 |
?theme=light | light、dark 或 system |
?font=14 | 固定字号,6 到 72 |
?glyphfont= | 页面自带字体缺字时使用的 CSS 字体列表,它也决定汉字采用哪个地区的字形 |
把它们放在签发出的链接的 # 之前:https://example-host:8088/?theme=light#token=…。
连接断开时
页面会自己重连,回到同一个标签:先立即试一次,然后等一秒,每次等待时间翻倍,最长十五秒。右下角会有一条提示,说连接已断开、正在重连。连续失败六次之后,它会补充说链接可能已过期,或者服务端可能没在运行 —— 浏览器不会被告知连接为什么被拒绝,所以从页面上看,被吊销的链接、过期的链接和停掉的服务端是一样的。
断开期间屏幕上的内容保持不动,恢复之后标签会按服务端重新排布。页面只在没有东西可回去时才放弃:服务端没有任何 pane,或者说的是另一个协议版本。它会说明是哪一种。
网页端不能做的事
- 文件和笔记 —— 目前是桌面端独有的,标签图标也是。
- 实时总览和远程主机 —— 浏览器里没有对应的东西,也没有它们的按钮。
- Thread 引用 —— 它们属于桌面端,不会显示。SSH 主机上的 Project 也不会显示:服务端打不开它们。
- 通知和提示音 —— 铃铛里什么都没有,也不会发出声音。
- 链接 —— 输出里的链接不能点击。
- 回滚搜索、复制模式和快速选择 —— 搜索面板搜的是工作区,不是 pane 里的文字。
- 触屏选择 —— 在手机上拖动是滚动。
- iTerm2 图片 —— 通过 iTerm2 协议发来的 PNG 和 JPEG 图片目前还不会画出来。
- 从右到左的文字 —— 显示为方框,因为页面不会重排双向文字,而顺序错了的正确字母更难被发现。
- 重命名标签、在窗口之间移动标签、重置终端 —— 服务端没有对应的操作。
页面自带的两种字体缺少的字符 —— 中日韩文字、韩文字母、emoji —— 用浏览器所在设备上安装的字体来画。哪里都没有的字符显示为方框。没有 WebGPU 时,页面只会显示一条消息,让你通过 https 或 http://localhost、在启用了 WebGPU 的浏览器里打开它。
