TUI
文档编写中
部分内容可能尚不完整。
同一个工作区,不带 GUI:
thinkterm tui [DOMAIN ...]它是 thinkterm 可执行文件的一个子命令,而不是第二个程序,所以不运行的时候不占用任何成本。
它是平级客户端,不是镜像
TUI 不是 GUI 的只读视图。它是同一个权威服务端的另一个客户端,并且遵守同样的规则:
- Space、Project 和 Thread 数据只接受来自服务端的快照
- 每一次改动都要等服务端确认之后,显示才会更新
因此同时开着 GUI 和 TUI,不可能出现两边对"存在什么"各执一词的情况。离线的服务端在 TUI 里根本无法修改:不会有任何改动被排队等着以后再发。
它能做什么
终端里的日常工作它都覆盖:新建标签、分屏、调整大小、放大、关闭 pane 和标签、带回滚搜索的复制模式,并且完整支持鼠标。
在侧边栏上,它可以新建和改名 Space、Project 与 Thread,置顶和取消置顶 Thread,并在每一层执行删除 —— 每次都会确认,因为删除可能结束里面的会话。拖动可以在 Project 内部重排 Thread、在 Space 内部重排 Project;Space 本身的顺序不变。它还能从下文介绍的连接面板接入和重试服务端,以及跳到下一个需要你关注的 Thread。
它没有什么
TUI 比桌面应用窄。它没有归档、没有 Thread 引用、没有文件、笔记、片段或 Agents 面板、没有插件、没有实时总览,也没有标签图标。它新建的 Project 不绑定你挑选的文件夹:名字由你定,起点是你的主目录。用 TUI 不会让这些东西丢失 —— 它们保持桌面端留下的样子 —— 只是要用桌面应用才能碰到它们。
终端由谁驱动
数据是共享的,但一个终端同一时刻只有一个驱动者。默认是交接模式 —— 客户端在你与之交互时把终端拿过来 —— 而 TUI 可以把服务端切换成类似 tmux 的共享模式,让所有接入的客户端看到同一张网格。这个模式属于服务端,并且在它重启后依然保留,所以它是对那个服务端做的决定,而不是对当前这个客户端。在共享模式下,最后一个与某个标签交互的客户端决定它的尺寸。交接的那一面见 远程。
切换开关在标签栏的 ⋮ 菜单里,即 A · Shared (tmux-like) 和 B · Handoff (exclusive),当前模式旁边带勾。底部状态栏打开时会写明状态:A SHARED;这个 TUI 握着终端时是 B ACTIVE;另一台设备握着时是 B VIEW · example-host。在共享模式下,尺寸由另一个客户端决定的标签会改显示 A SHARED · VIEW 120×30,带上这个标签被保持的尺寸。
被另一台设备握着的终端,上面会盖一条提示,说明它正在哪里使用,以及 Click or scroll to continue。在 TUI 里只有点击或滚动能接管它,键盘永远不会:没法分辨一次按键是想接管,还是要打给下面那个终端的,所以提示还在时敲的键不会去任何地方。
退出不会结束任何东西
退出 TUI 只是从服务端脱离。里面的每个会话都继续运行,与 GUI 客户端断开时完全一样。
这正是那个显而易见的用法成立的原因:SSH 到一台机器,跑 thinkterm tui,做点事,然后断开。或者在一台你本来就不想要 GUI 的机器上彻底跳过它。
用 Ctrl-b d 或 Ctrl-b q、⋮ 菜单里的 Detach,或状态栏上的 Detach 按钮脱离。
它不会在自己的服务端里打开
如果 TUI 运行在它要接入的那个服务端的某个 pane 里,它就是在那个服务端的一个终端里画那个服务端自己的终端。ThinkTerm 会拒绝:它脱离所有已接入的服务端,带着错误退出,你的终端保持原样。错误信息会提示你从另一个终端应用里运行 thinkterm tui,或者换一个域。
是不是同一个,看的是服务端的身份,而不是域的名字:每个 ThinkTerm pane 都记录着承载它的服务端,报告同一身份的目标会被拒绝。旧到没有这项记录的 pane 也会被拒绝,因为从它无法证明目标是另一个服务端。
界面
侧边栏是一棵树:每个已接入的服务端、它的 Space、Space 里的 Project,以及其中的 Thread。▾ 和 ▸ 用来折叠一层。每一行都带一个 ⋯ 按钮打开它的菜单,Space 行和 Project 行还带一个 +,分别用来添加 Project 和 Thread。树的上方是 ⊕ New Thread,在你所在的 Project 里添加一个 Thread;还有一个 WORKSPACES 标题,它的 + 在你所在的 Space 里添加 Project。⚙ Settings 位于底部。顶部的 ≡ 用来显示和隐藏侧边栏。
Thread 行以一个标记开头:
| 标记 | 含义 |
|---|---|
* | 里面有东西在运行 |
! | 它需要关注,或者被标记为未读 |
+ | 它的工作在你没看的时候完成了 |
★ | 它已置顶 |
打开一个 Thread 会在服务端清除它的未读标记,对所有客户端生效。Ctrl-b o 按树的顺序跳到下一个带 ! 或 + 的 Thread,到底后从头再来。TUI 启动时如果没有记住的 Thread,就打开最需要关注的那一个。
标签栏放着所选 Thread 的标签,每个标签上有一个 ✕ 用来关闭,+ 新建标签,⋮ 是主菜单。标签放不下时,两端的 < 和 > 会标出藏起来了几个。在标签栏上滚动滚轮可以切换标签。
pane 导航栏是每个 pane 顶部的一条。左边是堆叠在这个 pane 位置上的各个 pane —— 几个终端共用同一块矩形,各自以标签的形式切换 —— 每个都带一个 ✕。右边是四个按钮:⤢ 放大(放大后变成 ⤡)、◫ 向右分屏、⊟ 向下分屏,以及 + 在同一位置上堆叠一个新终端。pane 窄到放不下四个按钮时,先去掉分屏按钮,保留放大。
状态栏在底部,默认关闭。它显示当前模式的按键、消息、访问状态、需要关注的 Thread 数 ! N —— 点一下跳到下一个 —— 以及 Detach。关闭时,消息和模式提示以浮层显示,永远不会从终端那里占走一行。
按键
除了前缀 Ctrl-b 之后的按键,其余一切都交给终端。按下 Ctrl-b,松开,再按命令键。Ctrl-b Ctrl-b 会把一个字面的 Ctrl-b 发给 pane 里的程序。Ctrl-b ? 显示按键汇总,Esc 或 ? 关闭它。
前缀之后
| 按键 | 作用 |
|---|---|
? | 帮助 |
d 或 q | 脱离 |
b 或 s | 显示或隐藏侧边栏 |
g 或 w | 导航模式 |
C | 连接面板 |
, | 设置 |
c | 在所选 Thread 里新建标签 |
n / p | 下一个 / 上一个标签 |
1 … 9 | 第 1 到第 9 个标签 |
↑ / ↓ | 上一个 / 下一个 Thread |
o | 下一个需要关注的 Thread |
Tab | 下一个 pane |
h j k l、← → | 聚焦左、下、上、右侧的 pane |
v | 向右分屏 |
- | 向下分屏 |
x | 确认后关闭 pane |
X | 确认后关闭标签 |
z | 放大 pane,或取消放大 |
r | 调整大小模式 |
[ | 复制模式 |
前缀之后按其他键,除了提示一句什么也不做。
导航模式
打开侧边栏并把键盘留在那里,这样不用前缀就能在树上走动。
| 按键 | 作用 |
|---|---|
j / k、↓ / ↑ | 下一个 / 上一个 Thread |
h / l、← / → | 聚焦左 / 右侧的 pane |
n / p | 下一个 / 上一个标签 |
? | 帮助 |
Enter 或 Esc | 离开导航模式 |
调整大小模式
| 按键 | 作用 |
|---|---|
h j k l 或方向键 | 把当前 pane 的边朝那个方向移动一格 |
Enter 或 Esc | 结束 |
复制模式
复制模式在当前 pane 里放一个光标,它在回滚内容中移动,与 pane 里运行的程序无关。
| 按键 | 作用 |
|---|---|
h j k l 或方向键 | 移动光标 |
PgUp / PgDn | 滚动十行 |
End | 回到底部 |
Space | 开始选择;再按一次结束 |
y、Y 或 Enter | 复制所选内容 |
/ | 向前搜索 |
? | 向后搜索 |
n / N | 下一个 / 上一个匹配 |
Esc | 离开复制模式 |
搜索覆盖整个回滚内容,不区分大小写。向前搜索跳到第一个匹配,向后搜索跳到最后一个,并把光标移过去;没有匹配时显示 No matches。
菜单、输入框和确认
| 场合 | 按键 |
|---|---|
| 菜单 | j / k 或方向键移动,Enter 选择,Esc 关闭 |
| 名称输入框 | 直接输入,Backspace 删除,Enter 确定,Esc 取消 |
| 确认对话框 | Enter 或 y 确认,Esc 或 n 取消 |
输入框留空时什么也不做。
鼠标
Application mouse 打开时(默认如此),鼠标能做按键能做的大部分事:
- 点击 Thread 打开它,点击标签显示它,点击 pane 聚焦它。在终端上点击或滚动滚轮,也会把它从另一台设备那里接管过来。
- 在 pane 里拖动选择文本。靠近上下边缘时,选择会带着 pane 滚动。沿同一行拖动要超过三格才算数,所以手指滑开一两格的轻点仍然是点击。
- 右键任何东西打开它的菜单。侧边栏行上的
⋯打开的是同一个菜单。 - 拖动分隔线调整两侧 pane 的大小,拖动侧边栏右边缘调整侧边栏宽度,拖动 pane 的滚动条在回滚内容中移动。侧边栏宽度会被保存。
- 把一个 Thread 拖到同一 Project 的另一个 Thread 上,或把一个 Project 拖到同一 Space 的另一个 Project 上,就把它移到对方前面。
pane 里的程序要求接管鼠标时 —— vim、htop、开了鼠标支持的 less —— 点击和拖动会交给那个程序。按住 Shift 可以自己选择文本。Shift 加右键,以及中键,总是交给程序。
在 pane 上滚动滚轮有三种结果。程序要求了鼠标,滚轮交给它。pane 处于备用屏幕 —— 一个没有要求鼠标的全屏程序 —— 时,每一格都变成方向键,正因如此 less 和 git log 在触摸屏上也能滚动。其余情况滚动回滚内容。按住 Shift,或者已经往回滚过,总是滚动回滚内容。滚轮每一格是 scroll_lines 行。
右键菜单
| 对象 | 提供 |
|---|---|
| 服务端 | New Space、Retry connection、Disconnect |
| Space | New Project、Rename Space、Delete from server、End sessions and delete |
| Project | New Thread、Rename Project、End sessions and remove |
| Thread | Open、New Thread、Rename Thread、Pin 或 Unpin、End session and delete |
| 标签 | Open、New Tab、Rename Tab、Close Tab |
| pane | Copy、Paste、Split Right、Split Down、Zoom、Close Pane |
标签栏上的 ⋮ | New Tab、Split Right、Split Down、Copy Mode、Paste、Connections、A · Shared (tmux-like)、B · Handoff (exclusive)、TUI Settings、Toggle Sidebar、Help、Detach |
只有当那个 pane 里有一段已完成的选择时,Copy 才可用。
新建和删除做了什么
New Space 询问名称,然后建立这个 Space,里面带一个位于主目录的 Project Home 和一个 Thread main。New Project 询问名称,在主目录建立这个 Project,并带一个 main Thread。New Thread 询问名称。每一项都会打开它刚建好的东西。
删除之前一定会询问;会结束 pane 的,对话框会写明结束几个:
Delete from server为所有客户端从服务端移除一个 Space,里面的会话继续运行。End sessions and delete移除这个 Space,并结束其中每一个 pane。End sessions and remove移除一个 Project,并结束它的 pane。End session and delete移除一个 Thread,并结束它的 pane。Close Pane和Close Tab在服务端结束那些 pane,对所有客户端生效。
服务端上的 Disconnect 不是删除:TUI 只是放开它,它的会话继续运行。
复制与粘贴
Copy on select 打开时(默认如此),拖动结束即复制,复制模式里用 y 复制。关掉它,完成的选择会留在屏幕上;这时 Ctrl-C 会复制它,而不是中断程序。复制之后选择就被丢弃,所以下一次 Ctrl-C 又会送到程序那里。
TUI 通过系统自己的工具写入剪贴板:
| 系统 | 使用 |
|---|---|
| macOS | pbcopy 和 pbpaste |
| Linux | Wayland 下用 wl-copy 和 wl-paste,其次 xclip,再次 xsel |
| Windows | Windows 剪贴板 |
这些都写不进去时 —— 典型情况是 SSH 到一台没有显示器的 Linux 机器 —— TUI 改为把文本以 OSC 52 序列发给它所运行的那个终端,支持 OSC 52 的终端会把它放进你面前那台机器的剪贴板。行不行取决于那个终端,而不是 ThinkTerm。
菜单里的 Paste 通过同样的工具读取剪贴板,所以在 SSH 上它读的是远程机器的剪贴板,或者干脆失败。你所用终端自己的粘贴快捷键总是有效的:TUI 会把收到的内容交给当前 pane。
复制和粘贴每次最多 8 MiB。
窄屏与手机
TUI 会随着所在终端的宽度改变形态。
- 宽屏 —— 侧边栏以你设定的宽度待在终端旁边。
- 紧凑 —— 比
narrow_width宽不到 24 列时,侧边栏固定为 18 列。 - 窄屏 —— 在
narrow_width列(默认 64)或更窄时,树不再与终端共享屏幕。打开时它盖住终端,你选中一个 Thread 后它就让开;≡再把它叫回来。窄屏下的 TUI 总是从终端开始,在那里打开或关闭树也不会被保存,所以共用同一个tui.toml的手机和电脑不会互相覆盖对方的选择。
手机报告的列数差别很大,所以 narrow_width 是唯一值得手动调整的数字。
Touch targets 按指尖来设定按钮大小:按钮更宽,顶部是两行高的栏。保持 Auto 时它跟随窄屏布局,多数时候是对的,但手机横过来报告的列数多到像一台电脑时就不对了 —— 所以才有这个设置。
在手机上,pane 导航栏是手指唯一能碰到放大、分屏和新终端的地方,因为软键盘上几乎没有像样的 Ctrl-b。即使是很矮的 pane,也会保留它的导航栏。
设置
Ctrl-b ,、侧边栏底部的 ⚙ Settings,或 ⋮ 菜单里的 TUI Settings 都能打开设置界面。↑ ↓ 或 j k 在行间移动;← →、h l、Enter 或 Space 改变取值;Esc 关闭。点击某一行也会改变它。每次改动立即生效并保存。
界面上有十行。另有两个设置只存在于文件中:sidebar_width(拖动侧边栏边缘也会设置它)和 narrow_width。
tui.toml 中的键 | 界面上 | 默认值 | 取值 |
|---|---|---|---|
theme | Theme | monochrome-dark | 下面十个主题之一 |
sidebar_visible | Sidebar | true | true、false |
pane_nav_bar | Pane nav bar | true | true、false |
pane_borders | Pane borders | true | true、false |
pane_scrollbars | Pane scrollbars | false | true、false |
show_status_bar | Bottom status bar | false | true、false |
mouse | Application mouse | true | true、false |
copy_on_select | Copy on select | true | true、false |
touch_targets | Touch targets | 不设置(Auto) | 不设置、true、false |
scroll_lines | Scroll lines | 3 | 1 到 20 |
sidebar_width | — | 26 | 18 到 36 列 |
narrow_width | — | 64 | 20 到 200 列 |
超出范围的值会被拉回范围内。边框需要富余的空间,滚动条要占一列,所以两者只在足够大的 pane 上画出来;滚动条默认关闭,因为它占用的那一列是服务端不知道的。
关掉 Application mouse,普通的点击、拖动和滚轮就不再以鼠标事件的形式送到程序:点击和拖动始终用来选择,滚轮用来滚动,在备用屏幕上则发送方向键。
一个最小的 tui.toml:
theme = "tokyo-night"
narrow_width = 80
touch_targets = true主题
theme | 界面上 |
|---|---|
monochrome-dark | Monochrome Dark |
monochrome-light | Monochrome Light |
terminal | Terminal Default |
catppuccin | Catppuccin |
tokyo-night | Tokyo Night |
dracula | Dracula |
nord | Nord |
gruvbox | Gruvbox |
solarized-dark | Solarized Dark |
solarized-light | Solarized Light |
主题给 TUI 自己的界面上色,不给终端上色:终端保持服务端给它们的颜色。深色主题画在你的终端原有的背景上,所以侧边栏和 pane 在背景色上永远不会不一致。两个浅色主题自带背景色,因为深色文字放在深色终端上没法看。Terminal Default 连文字也用终端自己的颜色。
NO_COLOR 不会把这些有名字的主题变成灰色。想要不带颜色,请选两个 Monochrome 主题之一。
连接面板
Ctrl-b C,或 ⋮ 菜单里的 Connections,列出 TUI 能接入的每个服务端:
- 本机,显示为
Local—— 有正在运行的 ThinkTerm 桌面应用就是它,否则是本地会话 server - 你配置里的多路复用域
远程主机里使用 ThinkTerm Connect 的主机,以它们的标签显示
无法接入的主机也会列出,但呈灰色并附上原因:普通 SSH 主机提示 Enable ThinkTerm Connect in Remote Hosts,Mosh 主机则说明它的会话不是 mux 服务端。
| 标记 | 状态 |
|---|---|
● | 已接入 |
… | 正在连接 |
! | 失败 |
○ | 未接入 |
- | 无法接入 |
| 按键 | 作用 |
|---|---|
j / k、↓ / ↑ | 移动 |
Enter 或 r | 接入,或重试 |
d | 断开 —— 服务端保留它的会话 |
q | 脱离并退出 TUI |
Esc | 关闭面板 |
点击某一行也会接入它。重试 Local 时会重新寻找桌面应用,所以桌面应用退出之后,它连到的就是会话 server。
选项
| 选项 | 效果 |
|---|---|
[DOMAIN ...] | 要接入哪些服务端,按域名指定。不指定时,TUI 只接入本机;其他服务端可以之后从连接面板接入 |
--prefer-mux | 连接到后台 mux 服务端,而不是正在运行的 ThinkTerm GUI。默认优先连 GUI |
--class <NAME> | 用于发现运行中 ThinkTerm GUI 套接字的窗口类名 |
--tui-config <PATH> | 使用另一个 TUI 设置文件。TUI 的设置独立于 GUI |
它的设置文件在哪里
默认文件是 ~/.config/thinkterm/tui.toml,TOML 格式,遵循 $XDG_CONFIG_HOME;不想用命令行标志时,$THINKTERM_TUI_CONFIG 也能指定另一个。这个文件可以在 TUI 内部编辑,并且只有与默认值不同的项才会被写回 —— 默认值仍然是默认值,不会被固定成今天的取值。
另外,~/.local/state/thinkterm/tui.json(遵循 $XDG_STATE_HOME)记住你上次停在哪个域、哪个 Thread 和哪个标签。
相关
- 远程 —— TUI 接入的那些服务端
- Space、Project 与 Thread —— 以及桌面应用为它们添加的东西
- 快捷键与命令 —— 桌面应用的快捷键
- 命令行 ——
thinkterm可执行文件的其余部分
