第六章:交互式 TUI 深入
第三章我们快速过了一遍 TUI 的日常操作。那只是热身。本章把所有交互式 TUI 的细节——从界面每一寸区域的精确含义,到编辑器隐藏的高级功能,到消息队列的分发策略、键盘快捷键的完全自定义、会话树的深层操作、上下文压缩机制以及主题定制——全部掰开了讲清楚。读完这一章,你对 Pi 的 TUI 就再也没有”盲区”了。
6.1 TUI 界面布局全览
当你启动 pi(交互模式),终端会进入全屏 TUI。整个界面垂直分为四个功能明确的区域。下面从上到下逐一解剖。
6.1.1 启动头部(Startup Header)
启动头部在会话开始时显示(此后向上滚动被消息区替代)。它是你进入新会话时看到的第一个信息面板,告诉你 Pi 当前加载了什么”配置资产”:
| 显示项 | 含义 |
|---|---|
| 快捷键提示 | 快速提醒常用键位(如 Ctrl+L 切换模型、Shift+Tab 切换思考等级、Ctrl+G 外部编辑器等),帮助新用户快速上手 |
| Context Files 加载列表 | 列出已发现并注入的 AGENTS.md / CLAUDE.md 文件及其来源路径。每一行对应一个目录层级——从全局(~/.pi/agent/)到当前工作目录。如果有多个文件,Pi 会按加载顺序逐条列出 |
| Prompt Templates | 列出当前已发现的 .md 模板文件路径,标注模板名称(即文件名,不含 .md 扩展名)。你可以在编辑器中用 /name 展开模板 |
| Skills | 列出当前所有已加载的技能名称和一句话摘要。这些技能遵循 Agent Skills 标准,保存在 ~/.pi/agent/skills/ 或项目 .agents/skills/ 中 |
| Extensions | 列出所有已激活的 TypeScript 扩展名称和简短描述。如果扩展注册了新的 / 命令或工具,这里也会一目了然 |
启动头部只在新会话的最上方渲染一次。如果你用 /new 开启了新会话,它会再次出现。如果你用 pi -c 继续旧会话,启动头部不会重复显示——因为旧会话已经加载过了。
6.1.2 消息区域(Messages)
消息区域占据终端的绝大部分空间,是 Ai 与你交流的主舞台。渲染时没有任何隐藏——每一比特的信息都在这里:
| 消息类型 | 外观特征 | 内容 |
|---|---|---|
| 用户消息 | 以 你: 或你的名字开头 |
你在编辑器中输入并发送的所有文字、图片、文件引用 |
| 助手消息 | 以模型名或 Pi: 开头 |
模型的文本回复——流式渲染,逐 token 推送,你能看到它”正在想”的过程 |
| 思考块(Thinking blocks) | 可折叠的灰色/半透明文本块 | 模型的推理过程(thinking traces)。默认折叠,按 Ctrl+T 展开/折叠。展开后能看到模型”内心独白”——它对问题的分析、对工具选择的推演、对结果的判断 |
| 工具调用 | 以 ●⏺ 或类似图标开头,紧跟工具名和参数摘要 |
模型决定调用某个工具时的声明。例如 ●⏺ read src/app.ts (offset=0, limit=100) |
| 工具结果 | 缩进显示,通常包含命令执行输出、文件内容、错误信息 | 工具执行的返回内容。bash 命令的 stdout/stderr、read 返回的文件片段、edit 的替换结果等。默认可能被折叠/截断,按 Ctrl+O 展开/折叠 |
| 系统通知 | 浅色文本,通常以 ⚠ 或 ℹ 开头 |
上下文压缩通知、token 预算告警、模型切换提示、会话操作结果等 |
| 错误消息 | 红色文本 | API 调用失败、工具执行错误、网络超时、认证失败等异常 |
| 扩展 UI | 嵌入在消息流中的自定义组件 | 扩展通过 Pi TUI API 渲染的对话框、进度条、确认提示等 |
流式渲染(Streaming): Pi 使用差分渲染(differential rendering)引擎。模型每收到一段增量文本,Pi 只刷新变化的部分而不是重绘整个屏幕。这意味着你看到的打字机效果几乎无闪烁——在新一代终端模拟器(Ghostty、iTerm2、Kitty)上,借助同步输出(synchronized output)escape 序列,体验接近原生 GUI。
工具输出的折叠控制: 工具调用返回的内容有时候极长(比如 cat 了一个千行文件)。Pi 默认把这些输出折叠,只显示前几行概要。按 Ctrl+O 可以展开/折叠当前工具的输出。这个行为在 settings.json 中可以通过 collapseToolOutputs 配置项控制——设为 false 则始终展开。
思考块的折叠控制: 类似地,Ctrl+T 折叠/展开模型的思考过程。如果你不需要窥探模型的推理细节,可以把它们全部折叠以保持消息区清爽。
6.1.3 编辑器(Editor)
编辑器是 Pi TUI 的核心交互组件——你在这里输入 prompt、使用文件引用、粘贴图片、执行 shell 命令、调用斜杠命令。它不是一个简单的 <input> 框,而是一个集成了模糊搜索、路径补全、多行编辑、消息队列等功能的终端原生编辑器组件。
编辑器边框颜色的含义(思考级别指示):
编辑器的边框颜色不是装饰——它实时反映当前的思考级别(thinking level):
| 边框颜色 | 思考级别 | 含义 |
|---|---|---|
| 默认(通常是绿色或主题色) | off |
无思考——模型直接输出,不产生推理痕迹 |
| 蓝色 | minimal / low |
低思考——模型做轻量推理 |
| 黄色 | medium |
中思考——平衡推理深度与速度 |
| 橙色 | high |
高思考——模型投入较多算力做深度推理 |
| 红色 | xhigh |
极高思考——最大推理预算,适合数学证明、复杂架构设计 |
你可以在编辑器中按 Shift+Tab 循环切换思考级别,边框颜色随之实时变化。你也可以通过 /settings 面板或 --thinking CLI 参数设置。思考级别的选择直接影响模型的推理 token 消耗和输出质量——越高级别越耗 token、越慢、但也越深入。对于简单任务(如”给这个函数加 JSDoc”),off 或 low 完全足够;对于复杂任务(如”重构整个模块的架构”),high 或 xhigh 更靠谱。
6.1.4 底部栏(Footer)
底部栏是一条固定在终端最底行的状态条,实时反映当前会话的关键指标——它在你打字、模型推理时持续更新:
| 字段 | 图标/简写 | 含义 |
|---|---|---|
| 工作目录 | 路径字符串 | 当前 Pi 会话的工作目录(当前项目根目录)。所有相对路径解析以此为基准 |
| 会话名称 | 用户命名的字符串或自动生成 | 通过 /name 设置的会话别名,未命名时显示为自动生成的短 ID |
| 输入 Token(↑) | ↑ 12.3k |
累计已发送给模型的输入 token 数(包括系统提示词、上下文文件、用户消息、工具结果等) |
| 输出 Token(↓) | ↓ 4.2k |
模型累计生成的输出 token 数 |
| 缓存读取(R) | R 5.1k |
本次会话中从 provider 缓存读取的 token 数(命中 prompt caching / Anthropic 的 cache API 等)。缓存命中意味着这部分 token 不收费或收费极低 |
| 缓存写入(W) | W 8.0k |
写入 provider 缓存的 token 数。下次重复使用相同的前缀就能命中这些缓存 |
| 缓存命中率(CH) | CH 42% |
缓存读取 / 总输入 token 的比例。百分比越高,花的钱越少。精确公式:R / (↑ - W + R) |
| 费用 | $0.042 |
本次会话的估算费用(USD)。基于各模型的定价表实时计算输入和输出 token 的成本,减去缓存折扣后的净值。注意:这是估算值——不同供应商的定价结构(如批量折扣、动态定价)可能不完全匹配 |
| 上下文使用率 | ctx 23% |
当前已使用上下文 / 模型上下文窗口上限的比例。例如 ctx 72% 意味着你已经用了模型 72% 的上下文窗口。超过 ~85% 时可能触发自动压缩(compaction);超过 100% 会截断最早的消息 |
| 当前模型 | 模型名 | 当前正在使用的模型标识符 |
典型的一条底部栏长这样:
~/projects/my-app │ review-refactor │ ↑12.3k ↓4.2k R5.1k W8.0k CH42% │ $0.042 │ ctx 23% │ claude-sonnet-4-20250514:high
这个状态栏让你一目了然地知道三件事:会话在哪、花了多少钱、还剩多少空间。在长时间编码会话中,你不需要切出去看账单——底部栏就是你的实时预算面板。
6.2 编辑器详解
Pi 的编辑器远不止是一个文本输入框。它是整个 TUI 的核心交互界面,集成了模糊搜索、路径补全、多行编辑、多模态输入、内联 shell 执行、外部编辑器回调、Vim 模式绑定等功能。下面逐一详述。
6.2.1 文件引用:@ 触发模糊搜索
在编辑器中输入 @ 字符,Pi 会立刻弹出一个文件选择器浮层——覆盖整个屏幕的模糊搜索列表。你可以边打字边筛选,不需要输入完整路径:
@code.ts → 匹配 src/utils/code.ts、lib/helpers/code.ts、tests/code.test.ts 等
@uti → 实时过滤,只显示路径中包含 "uti" 的文件
选择器支持以下交互:
| 操作 | 按键 |
|---|---|
| 筛选文件 | 直接打字(文件名或路径的连续片段即可) |
| 上下移动光标 | ↑ / ↓ |
| 翻页 | PgUp / PgDn |
| 选中文件 | Enter |
| 取消选择 | Esc |
选中后,Pi 把文件的相对路径插入到编辑器光标位置。你可以在同一条消息中引用多个文件:
@code.ts @test.ts "审查这两个文件的差异"
此时模型会看到消息中附带了两个文件的路径引用。Pi 在后台把引用的文件内容作为附加的多模态/文本内容发送给模型。你不需要手动 cat 这些文件——Pi 自动在后台读取并把内容放入消息载荷。
@ 的文件预处理原理: Pi 的 pi-agent-core 在处理消息时,检测到 @ 引用的文件路径后会调用传输层(Transport)读取文件内容。对于文本文件(.ts、.js、.md 等),内容作为文本附加;对于图片文件(.png、.jpg、.gif、.webp),内容作为 Base64 编码的多模态图片块附加。支持的最大文件大小和可处理的图片格式取决于目标模型的视觉能力。
@ + 目录: 如果你用 @src/ 选择一个目录,Pi 会列出该目录的结构(文件树)而不是把所有文件内容都塞进去。这在你需要模型了解项目结构但没有具体读取需求时很有用。
6.2.2 路径补全:Tab 键
在编辑器中,当光标位于文件路径上时,按 Tab 触发路径补全:
你: 帮我重构 src/uti|
按 Tab,Pi 弹出补全列表:
你: 帮我重构 src/utils/format.ts
补全逻辑基于当前工作目录下的实际文件结构。它支持:
- 相对路径补全:
src/开始 - 绝对路径补全:
/home/user/project/开始 .和..补全:./和../开始- 部分匹配:不需要输入完整前缀,
src/ut也可以匹配src/utils/
如果你安装了 @earendil-works/pi-fuzzy-path 等第三方扩展,补全行为还可以进一步强化(如子串匹配、最近使用文件排序等)。
6.2.3 多行输入
编辑器默认是单行模式——按 Enter 直接发送消息。当你需要输入多行 prompt 时(比如粘贴一段代码、写一个结构化的分步骤指令),有两种方式:
| 平台 | 多行输入键 | 说明 |
|---|---|---|
| Linux / macOS | Shift+Enter |
在当前位置插入一个换行符,不发送消息 |
| Windows(Windows Terminal、PowerShell 等) | Ctrl+Enter |
Shift+Enter 在部分 Windows 终端模拟器上会被拦截,因此 Windows 上的默认多行键是 Ctrl+Enter |
在多行模式下,编辑器会向上扩展以容纳更多行。你的 prompt 可以写成:
你: 请帮我做以下三件事:
你: 1. 审查 src/utils/format.ts 的安全问题
你: 2. 重写 src/utils/validate.ts 的错误处理
你: 3. 更新对应的测试文件
你: [按 Enter 发送]
多行模式与消息队列协同工作时尤为有用——你可以在模型运行期间从容编写下一条多行消息,排进队列等待交付。
6.2.4 图片粘贴与拖拽
Pi 原生支持多模态模型(Claude Sonnet/Opus、GPT-4o、Gemini 等),可以在编辑器中直接置入图片。有两种方式:
方式一:粘贴
| 平台 | 快捷键 |
|---|---|
| Linux / macOS | Ctrl+V |
| Windows | Alt+V |
确保你的剪贴板中有一张图片(截图后 Ctrl+C 复制),然后在编辑器中按对应快捷键,图片即作为 Base64 编码的多模态块附加到当前消息中。你可以继续在图片前后添加文字说明。
方式二:拖拽
直接从操作系统的文件管理器(Windows 资源管理器、macOS Finder、Linux 文件管理器)中拖拽图片文件到终端窗口。支持现代终端模拟器(Ghostty、iTerm2、Kitty、Windows Terminal 等)。拖放成功后,你会看到图片文件的路径出现在编辑器中,Pi 在发送前会在后台读取并编码。
支持的图片格式: PNG、JPEG、GIF(静态帧)、WebP。注意;GIF 动图的多帧动画在发送给模型时只会取第一帧。如果模型不支持视觉输入(如 deepseek-chat 纯文本版本),图片附件会被忽略或报错。
Print 模式也支持图片:
pi -p @screenshot.png "这个截图里有什么错误信息?"
终端预览(可选): 部分终端(如 iTerm2、Ghostty、Kitty)支持在终端内嵌显示图片。如果安装了 @gotgenes/pi-image-preview 等扩展,粘贴的图片会在消息区渲染一个小缩略图预览,方便你确认贴了正确的图片。
6.2.5 Shell 命令内联:! 和 !!
这是 Pi 在交互模式下最高频的实用功能之一——不需要离开编辑器,就能把 Shell 命令的输出注入到当前的 prompt 上下文中。
!command:输出发送给模型
在编辑器中输入以单个 !(英文感叹号)开头的行,Pi 会把它视为一条 Shell 命令来执行,并把标准输出(stdout)追加到你的消息中一起发给模型:
你: 我的测试有什么问题?先看看当前的失败列表:
你: !npm test 2>&1 | tail -20
此时模型接收到的消息类似于:
我的测试有什么问题?先看看当前的失败列表:
[shell output of: npm test 2>&1 | tail -20]
FAIL src/utils/format.test.ts
● formatDate › should handle invalid input
expect(formatDate(null)).toThrow() → received undefined
...
模型同时看到你的问题和你提供的上下文,可以直接给出针对性回答,避免了”你先告诉我错误是什么”这种多轮浪费。
更多典型用法:
# 查看最近 5 次提交
你: 我最近的工作摘要是什么?
你: !git log --oneline -5 --author="me"
# 检查文件状态
你: 有哪些文件被我改过了?
你: !git status --short
# 运行 lint 并分析
你: 我的代码有哪些风格和潜在错误?
你: !npx eslint src/ --format stylish 2>&1
# 查看依赖树
你: 我的项目有哪些包依赖了 lodash?
你: !npm ls lodash --all 2>&1
! 命令由 Pi 的本地 Shell 执行(使用当前工作目录),执行时间有超时限制(默认 30 秒,可通过 bashTimeout 配置)。如果命令执行时间超过限制,会被强制终止,stdout/stderr 截至超时那一刻的内容仍会发送给模型。
!!command:静默执行
双感叹号 !!command 执行命令,但不把输出发给模型。这适用于你只想做环境准备,不希望噪声输出污染模型上下文:
你: !!npm install
你: 依赖装好了,帮我看看 package.json 里有没有可以安全更新的版本
模型看到的消息里没有 npm install 的海量输出,只有你的第二行。这在需要静默完成预处理步骤时极其有用。
! 命令的组合技巧
你可以在一段 prompt 中混用 ! 和普通文本:
你: 帮我分析一下,为什么这个 PR 会导致 CI 失败?
你: !git log --oneline -1
你: !git diff HEAD~1
你: !cat ci-failure.log
你: 给出修复方案
模型一次收到:你的分析请求 + 最近的 commit 信息 + 这个 commit 的 diff + CI 失败日志。一步到位,不需要多轮交互。
6.2.6 外部编辑器:Ctrl+G
当你的 prompt 太复杂、或者你想用自己熟悉的编辑器来组织思路时,在 Pi 编辑器中按 Ctrl+G:
- Pi 把编辑器中的当前文本内容写入一个临时文件
- Pi 启动外部编辑器(按以下优先级选择)打开该文件:
settings.json中的externalEditor配置项- 环境变量
$VISUAL - 环境变量
$EDITOR - Windows 上:
notepad.exe(记事本) - 其他系统:
nano
- 你在外部编辑器中自由编辑文本
- 保存文件并关闭编辑器
- Pi 检测到编辑器关闭,自动读回文件内容并回填到编辑器中
这个功能让你可以用 VSCode、Vim、Emacs、Sublime Text——你最高效的编辑器——来编写复杂 prompt。它对以下场景特别有用:
- 写一份需要格式化和分段的结构化任务列表
- 粘贴超长代码片段到 prompt 中
- 同时参考多个窗口的信息来组织问题
配置外部编辑器: 在 ~/.pi/agent/settings.json 中:
{
"externalEditor": "code --wait"
}
--wait 参数是关键——没有它,VSCode 启动后立即返回,Pi 在你还未编辑完时就尝试读回文件。同理,vim 用户应设为 vim(vim 默认阻塞,所以不需要特别参数),Sublime Text 用户应设为 subl --wait。
6.2.7 Vim 模式绑定
Pi 的编辑器支持 Vim 风格的模态编辑。在 ~/.pi/agent/settings.json 中设置:
{
"editorMode": "vim"
}
启用后,编辑器行为改为 Vim 模式:
| Vim 模式 | 行为 | 进入方式 |
|---|---|---|
| Normal 模式 | 导航、复制、粘贴、删除等操作模式 | 按 Esc 从 Insert 模式返回 |
| Insert 模式 | 正常打字输入(默认启动状态) | 在 Normal 模式下按 i / a / o / O 等进入 |
| Visual 模式 | 文本选择 | 在 Normal 模式下按 v |
支持的标准 Vim 键位包括但不限于:
| 按键 | 功能 |
|---|---|
h / j / k / l |
左 / 下 / 上 / 右移动光标 |
w / b |
前进 / 后退一个词 |
0 / $ |
跳到行首 / 行尾 |
gg / G |
跳到文件开头 / 末尾 |
dd |
删除当前行 |
yy |
复制当前行 |
p / P |
在光标后 / 前粘贴 |
u |
撤销 |
Ctrl+R |
重做 |
/pattern |
向前搜索 |
?pattern |
向后搜索 |
n / N |
下一个 / 上一个搜索结果 |
ciw |
改变光标所在词 |
ci" |
改变引号内文本 |
:%s/old/new/g |
全局替换 |
重要提醒: 启用 Vim 模式后,Pi 的一些默认按键映射会发生冲突。例如 Esc 在普通模式下是”返回 Normal 模式”而不是”中断操作”;i 不再是普通的文本输入。你需要重新调整自己的肌肉记忆。如果发现自己频繁误操作,可以在 keybindings.json 中微调冲突按键(详见 6.5 节),或切换回默认模式("editorMode": "default")。
Pi 的 Vim 模式实现基于 @earendil-works/pi-tui 中内置的 Vim 仿真器,它支持最常见的 Vim 动作,但不是完整的 Vim 引擎——一些高级功能(如宏录制 q、标记 m、:g 全局命令等)暂不支持。
6.3 斜杠命令大全
在编辑器中输入 /,Pi 会弹出一个命令补全菜单,列出所有可用的斜杠命令。每条命令都有简短描述,你可以打字筛选,按 Enter 执行。
下表是 Pi 内置的所有斜杠命令及其功能详解:
| 命令 | 功能 | 使用场景 |
|---|---|---|
/login |
启动 OAuth 流程,登录到一个支持 OAuth 的供应商(如 Anthropic Console、GitHub Copilot) | 配置订阅制账号时使用。执行后会在浏览器中打开认证页面,完成授权后 Pi 保存 token |
/logout |
登出当前 OAuth 会话,清除本地保存的 token | 切换账号或撤销授权时使用 |
/model |
打开模型选择器(等效于 Ctrl+L) |
交互式浏览所有可用模型,按供应商分组,支持搜索过滤 |
/scoped-models |
管理 Ctrl+P / Shift+Ctrl+P 循环切换的模型范围 |
限定你的”快速模型切换”范围,避免意外切到不想要的模型 |
/settings |
打开交互式设置面板 | 可视化修改思考等级、传输方式、消息交付策略、主题、上下文压缩策略等,无需手写 JSON |
/resume |
打开历史会话选择器 | 从当前项目的所有历史会话中选择一个恢复。支持搜索、排序、过滤 |
/new |
开始一个全新的会话 | 当前会话会被保存并关闭,新建一个空白会话文件,启动头部重新显示 |
/name <name> |
设置当前会话的显示名称 | 为会话起一个有意义的名字(如”重构format模块”),方便在 /resume 列表中识别 |
/session |
查看当前会话的详细信息 | 显示:会话文件路径、会话 ID、消息数量、输入/输出 token 统计、费用、创建时间等 |
/tree |
打开会话树导航 | 可视化浏览当前会话的完整消息树结构,可在任意历史节点跳转并创建分支 |
/fork |
从活跃分支的任意用户消息创建新会话 | 打开消息选择器,选取一条用户消息,将该消息及其前置历史复制到新会话文件 |
/clone |
将当前活跃分支完整复制到新会话文件 | 存档当前进展,在新文件中继续探索——原始会话保持不变 |
/compact [prompt] |
手动触发上下文压缩 | 将当前会话中的历史消息压缩为一条摘要,释放上下文窗口空间。可附带自定义摘要指令 |
/copy |
将上一条助手消息复制到系统剪贴板 | 快速复制模型的代码块或回复内容 |
/export [file] |
导出当前会话为 .html 或 .jsonl 文件 | 保存完整会话记录供审计、分享或备份。不指定路径时自动保存到 ~/.pi/agent/exports/ |
/import <file> |
从 JSONL 文件导入并恢复一个会话 | 将之前导出的会话重新加载到 Pi 中,恢复为可继续对话的完整状态 |
/share |
将当前会话上传为私有 GitHub Gist | 生成一个可分享的 HTML 链接,接收者可以在浏览器中查看格式化的对话记录 |
/trust |
保存当前目录的项目信任决策 | 写入 ~/.pi/agent/trust.json,下次进入同目录不再弹出信任提示。保存后需重启 Pi 才生效 |
/reload |
重新加载配置文件 | 重载 keybindings、Extensions、Skills、Prompt Templates 和上下文文件。注意:主题文件自动热加载,不需要 /reload |
/hotkeys |
显示当前所有键盘快捷键 | 打印一份完整的键位映射表,包括你自定义的 keybindings |
/changelog |
显示 Pi 的版本更新记录 | 展示当前版本和最近几个版本的 changelog 摘要 |
/quit 或 /exit |
退出 Pi | 保存当前会话并关闭 TUI。下次用 pi -c 可继续 |
命令补全菜单的交互:
| 操作 | 按键 |
|---|---|
| 打开菜单 | 输入 / |
| 筛选命令 | 继续输入命令名(模糊匹配) |
| 上下移动 | ↑ / ↓ |
| 选中执行 | Enter |
| 关闭菜单 | Esc |
扩展注册的斜杠命令: 如果你安装了第三方扩展,扩展可以注册自己的斜杠命令。这些自定义命令会出现在 / 补全菜单中,与内置命令混合排序。例如 @gotgenes/pi-git 扩展可能注册 /git-log、/git-diff、/git-branch 等命令。
6.4 消息队列系统
这是 Pi 交互体验中最精妙的设计之一。在多数 Agent 工具中,当模型正在执行工具调用(比如跑一个长达 30 秒的 npm install),你只能干瞪眼等着——任何键盘输入都被忽略。Pi 打破了这种”阻塞式”交互。
6.4.1 队列机制
当模型正在工作(思考中或执行工具调用)时,编辑器的边框会显示不同的颜色或动画(通常是脉冲闪烁或颜色变化),提示你”模型忙碌中”。此时你仍然可以打字。当你按下发送键时,消息不会立即发送,而是进入一个消息队列,等待合适的时机交付。
队列中的消息根据发送方式分为两种类型:
Steering(转向消息)——按 Enter 发送
“转向消息”在当前模型工具回合一结束就立即交付给模型。它的设计目的是让你”中途介入”——例如你看到模型正在用 find 搜索文件,但你意识到它搜错了目录,你就可以打一条转向消息:”应该在 src/ 目录下搜索,不是根目录”,然后按 Enter。等当前工具执行完返回结果后,这条转向消息会立刻送达模型,影响它的下一步决策。
交付时机: 等当前正在执行的工具调用返回结果后,立刻把 steering 消息交给模型处理。模型会在收到工具结果的同时收到你的转向指示。
典型场景:
- 你看到模型用错了命令参数,想纠正它
- 你发现模型的搜索方向偏了,想引导回来
- 你突然想起一个重要信息需要补充
Follow-up(跟进消息)——按 Alt+Enter 发送
“跟进消息”在模型完成当前全部工作后才交付(即模型停止调用工具、准备输出回复文本时)。它的设计目的是”排队下一个任务”——模型当前的任务还在进行中,但你已经在想下一步了。
交付时机: 等模型完成当前全部工作流(所有工具调用结束、文本回复输出完毕),模型处于”等待下一条用户消息”状态时,才交付 follow-up 消息。
典型场景:
- 模型正在重构一个模块,你已经在想下一个要重构的模块
- 模型正在调试一个问题,你已经在想”修好后顺便更新一下文档”
- 你想在模型当前任务完成后自动执行另一个任务
两者的对比
| 特性 | Steering(Enter) |
Follow-up(Alt+Enter) |
|---|---|---|
| 交付时机 | 当前工具回合一结束 | 模型完成全部工作后 |
| 典型用途 | 中途纠偏、补充信息 | 排队下一个任务 |
| 对当前任务的影响 | 可能改变当前任务的方向 | 不影响当前任务,纯排队 |
| 队列语义 | “等等,还有一件事……” | “做完这个之后,再做……” |
6.4.2 队列管理操作
| 按键 | 功能 |
|---|---|
Alt+Up 或 Ctrl+Up |
把队列中最新的那条消息取回编辑器,你可以重新编辑后再次排队或直接发送 |
Esc |
中止当前模型的执行,并恢复队列中的所有消息到编辑器(模型会收到中断信号,尚未执行完的工具调用被取消) |
6.4.3 交付策略配置
你可以在 settings.json 中通过 steeringMode 和 followUpMode 两个配置项精细控制消息交付策略:
{
"steeringMode": "one-at-a-time",
"followUpMode": "all"
}
| 配置值 | 行为 |
|---|---|
"one-at-a-time"(默认) |
每次只交付一条排队消息,等模型回复后再交付下一条。这模拟了”一对一对话”的感觉 |
"all" |
一次性交付所有排队消息。模型会依次处理它们。适合你想批量提交指令的场景 |
两个字段独立配置——你可以让 steering 消息逐条交付(以免互相干扰),而让 follow-up 消息一次性全部交付(因为它们是独立的任务)。
6.4.4 消息队列的视觉反馈
当队列中有消息时,底部栏会显示队列计数(如 [2] 表示有 2 条消息在队列中)。编辑器的边框可能变为闪烁模式,提醒你”还有话没说完”。这些视觉提示帮助你追踪队列状态,避免遗忘已排队的消息。
6.5 键盘快捷键
Pi 的键盘快捷键系统高度可定制。它分为三层:内置默认快捷键、keybindings.json 自定义覆盖、以及按键风格(Emacs / Vim)的整体切换。
6.5.1 默认快捷键一览
| 按键 | 功能 | 作用域 |
|---|---|---|
Enter |
发送消息(模型空闲时);发送 Steering 消息(模型忙碌时) | 编辑器 |
Alt+Enter |
发送 Follow-up 消息(模型忙碌时);多行输入换行(Windows) | 编辑器 |
Shift+Enter |
多行输入换行(Linux/macOS) | 编辑器 |
Ctrl+Enter |
多行输入换行(Windows 备用) | 编辑器 |
Esc |
取消/中断当前操作(模型工作时终止执行);关闭弹窗/补全菜单 | 全局 |
Esc × 2 |
打开 /tree 会话树导航 |
编辑器 |
Ctrl+C |
清空编辑器内容 | 编辑器 |
Ctrl+C × 2 |
退出 Pi(第二次按下时确认退出) | 全局 |
Ctrl+L |
打开模型选择器(等同 /model) |
编辑器 |
Ctrl+P |
在限定的模型范围内正向循环切换(下一个模型) | 编辑器 |
Shift+Ctrl+P |
在限定的模型范围内反向循环切换(上一个模型) | 编辑器 |
Shift+Tab |
循环切换思考级别(off → minimal → low → medium → high → xhigh → off …) |
编辑器 |
Ctrl+O |
折叠/展开当前工具输出 | 消息区域 |
Ctrl+T |
折叠/展开思考块(thinking blocks) | 消息区域 |
Ctrl+G |
打开外部编辑器编辑当前 prompt | 编辑器 |
Ctrl+V |
从剪贴板粘贴图片(Linux/macOS) | 编辑器 |
Alt+V |
从剪贴板粘贴图片(Windows) | 编辑器 |
Ctrl+Z |
撤销编辑器中的文本修改 | 编辑器 |
Ctrl+Y 或 Ctrl+Shift+Z |
重做编辑器中被撤销的文本修改 | 编辑器 |
Ctrl+A |
跳转到行首 | 编辑器 |
Ctrl+E |
跳转到行尾 | 编辑器 |
Ctrl+K |
删除从光标到行尾的所有内容 | 编辑器 |
Ctrl+W |
删除光标前一个词 | 编辑器 |
Alt+← / Alt+→ |
按词向前/向后移动光标 | 编辑器 |
Ctrl+↑ / Ctrl+↓ |
在 /tree 中上下翻页 |
/tree |
Ctrl+← / Ctrl+→ 或 Alt+← / Alt+→ |
在 /tree 中折叠/展开分支或在分支间跳转 |
/tree |
Shift+L |
在 /tree 中为当前节点打标签(书签) |
/tree |
Shift+T |
在 /tree 中切换标签时间戳显示 |
/tree |
Ctrl+O |
在 /tree 中切换过滤模式 |
/tree |
Alt+Up |
从消息队列中取回最近的排队消息 | 编辑器(模型忙碌时) |
@ |
触发模糊文件搜索 | 编辑器 |
Tab |
路径补全(光标在路径上时) | 编辑器 |
/ |
打开斜杠命令补全菜单 | 编辑器 |
6.5.2 自定义快捷键:keybindings.json
所有默认快捷键都可以通过 ~/.pi/agent/keybindings.json 覆盖。文件格式为 JSON,每个条目定义了一个按键到命令的映射:
[
{
"keys": ["Ctrl+Shift+L"],
"command": "pi:model",
"title": "打开模型选择器"
},
{
"keys": ["Ctrl+Shift+N"],
"command": "pi:new",
"title": "新建会话"
},
{
"keys": ["F5"],
"command": "pi:send",
"title": "发送消息"
}
]
每条绑定的结构说明:
| 字段 | 类型 | 说明 |
|---|---|---|
keys |
string[] |
触发键的组合。修饰键:Ctrl、Shift、Alt(macOS 上对应 Cmd、Shift、Option)。普通键:字母、数字、功能键(F1-F12)、Enter、Esc 等 |
command |
string |
命令 ID。内置命令的前缀为 pi:。扩展注册的命令使用扩展自定义的 ID |
title |
string |
人类可读的名称,显示在 /hotkeys 输出中 |
可用的内置命令 ID(部分):
| 命令 ID | 对应功能 |
|---|---|
pi:send |
发送消息 |
pi:send-steering |
发送 Steering 消息 |
pi:send-follow-up |
发送 Follow-up 消息 |
pi:interrupt |
中断模型执行(Esc) |
pi:new-line |
插入换行 |
pi:clear |
清空编辑器 |
pi:external-editor |
打开外部编辑器 |
pi:file-search |
触发 @ 文件搜索 |
pi:path-complete |
触发路径补全 |
pi:command-palette |
打开斜杠命令菜单 |
pi:model |
打开模型选择器 |
pi:model-cycle-next |
下一个模型 |
pi:model-cycle-prev |
上一个模型 |
pi:thinking-cycle |
循环切换思考级别 |
pi:collapse-tool-output |
折叠/展开工具输出 |
pi:collapse-thinking |
折叠/展开思考块 |
pi:quit |
退出 Pi |
pi:tree |
打开会话树 |
pi:settings |
打开设置面板 |
pi:new-session |
新建会话 |
pi:resume |
恢复会话 |
pi:name |
命名会话 |
pi:compact |
触发上下文压缩 |
pi:reload |
重新加载配置 |
pi:hotkeys |
显示快捷键 |
pi:paste-image |
粘贴图片 |
应用自定义快捷键: 修改 keybindings.json 后,运行 /reload 即可生效。你不需要重启 Pi。如果修改了不支持的按键或命令 ID,Pi 会在启动头部显示一条警告但不会崩溃——不正确的绑定被忽略,默认绑定仍然有效。
冲突处理: 如果你定义的快捷键与默认快捷键冲突,你的自定义绑定覆盖默认绑定。如果你想同时保留两者,可以给不同的键分配相同的命令 ID。
6.5.3 Emacs 风格按键绑定
Pi 编辑器在默认模式下就内置了大量 Emacs 风格的只读快捷键(导航和编辑),包括:
Ctrl+A— 行首Ctrl+E— 行尾Ctrl+B— 光标后退一个字符Ctrl+F— 光标前进一个字符Ctrl+N— 下一行(多行模式下)Ctrl+P— 上一行(多行模式下)Ctrl+K— 删除到行尾Ctrl+W— 删除前一个词Ctrl+D— 删除光标处字符Alt+B— 后退一个词Alt+F— 前进一个词Alt+D— 删除后一个词
这些快捷键不需要任何配置——它们在默认模式下自动生效。如果你更习惯纯 Emacs 操作风格,默认模式已经基本满足需求。如果某些键位与你的终端快捷键冲突(例如 Ctrl+W 在部分终端中是”关闭标签页”),你可以在 keybindings.json 中重新映射。
6.5.4 Vim 风格按键绑定
在 ~/.pi/agent/settings.json 中设置:
{
"editorMode": "vim"
}
详见 6.2.7 节。
6.6 会话树与导航
Pi 的会话不是线性聊天记录,而是一棵消息树。这是 Pi 设计中最具特色的架构决策之一——它让你在同一段对话中自由探索多个方向,而不会丢失任何历史。
6.6.1 会话树的数据结构
每个会话文件(JSONL 格式)由一系列”条目”(entry)组成。每条目有自己的唯一 id,以及一个 parentId 字段指向它的父条目:
{"id":"1","type":"user","content":"帮我审查 src/utils/ 下所有文件"}
{"id":"2","type":"assistant","parentId":"1","content":"好的,我先用 find 列出该目录的文件……"}
{"id":"3","type":"tool_call","parentId":"2","toolName":"bash","params":{...}}
{"id":"4","type":"tool_result","parentId":"3","output":"..."}
{"id":"5","type":"assistant","parentId":"4","content":"找到 5 个文件。让我逐个审查……"}
{"id":"6","type":"user","parentId":"5","content":"其实我们只关心 format.ts 和 validate.ts"}
{"id":"7","type":"assistant","parentId":"6","content":"好的,我专注审查这两个文件……"}
这个结构就是一棵树。parentId 决定了消息的父子关系。一个父节点可以有多个子节点(即多个分支):
├─ user (id:1): "帮我审查 src/utils/"
│ └─ assistant (id:2): "好的……"
│ └─ tool_call (id:3): bash find …
│ └─ tool_result (id:4): 找到 5 个文件
│ └─ assistant (id:5): "让我逐个审查……"
│ ├─ user (id:6): "只关心 format.ts 和 validate.ts"
│ │ └─ assistant (id:7): "好的,专注这两个……" ← 当前活跃分支
│ └─ user (id:8): "等等,我想看看全部 5 个" ← 备选分支
│ └─ assistant (id:9): "好,全部审查……"
在这个例子中,id:5 有两个子节点:id:6 和 id:8。它们是两个不同的分支——你可以在两个方向之间自由跳转,每个方向的对话都完整保留。
6.6.2 /tree:在会话树中导航
/tree(或按 Esc × 2)打开可视化会话树导航界面。这个界面显示当前会话的完整消息树结构,让你浏览、跳转和创建分支。
导航操作:
| 操作 | 按键 |
|---|---|
| 上下移动光标 | ↑ / ↓ |
| 翻页(一次翻半屏) | ← / → |
| 折叠/展开当前节点的子节点 | Ctrl+← / Ctrl+→ 或 Alt+← / Alt+→ |
| 为当前节点打标签(书签) | Shift+L |
| 切换标签旁边的时间戳显示 | Shift+T |
| 切换过滤模式(循环:默认 → 隐藏工具调用 → 仅用户消息 → 仅标签 → 全部) | Ctrl+O |
| 确认跳转到所选节点 | Enter |
| 取消并返回编辑器 | Esc / Ctrl+C |
过滤模式详解:
| 过滤模式 | 显示内容 |
|---|---|
| 默认 | 显示用户消息、助手消息和自定义消息,工具调用和工具结果折叠为一行摘要 |
| 隐藏工具调用 | 完全隐藏所有工具调用和工具结果,只显示用户和助手的对话 |
| 仅用户消息 | 只显示用户发送的消息节点——适合快速找到你之前的某条提问 |
| 仅带标签的消息 | 只显示你用 Shift+L 打过书签的节点——适合追踪关键决策点 |
| 全部显示 | 显示所有节点,包括工具调用、工具结果、compaction 记录等一切 |
默认过滤模式可通过 treeFilterMode 在 settings.json 中配置。
跳转行为:
-
你选择了某条”用户消息”或”自定义消息”节点 → Pi 把当前会话的活跃叶子节点移动到你选中节点的父节点处(即从该节点重新开始),并把选中节点的文本回填到编辑器。你可以修改后重新发送——这就创建了一个新分支。
-
你选择了某条”助手消息”、”工具调用结果”、”compaction 记录”或其他非用户条目 → Pi 把活跃叶子节点移动到该节点,编辑器留空。这意味着你从该点直接继续对话,而不是重新编辑那条消息。
6.6.3 /fork:从历史消息创建独立新会话
/fork 和 /tree 的核心区别在于:/tree 在同一个会话文件内操作,/fork 创建全新的独立会话文件。
当你输入 /fork 时:
- Pi 打开一个”用户消息选择器”,列出当前活跃分支上所有的用户消息
- 你选择一条用户消息(比如三天前你问的”这个模块应该怎么重构?”)
- Pi 把该消息及其全部前置历史(从会话开始到你选中的消息)复制到一个新的会话文件
- 选中的用户消息文本回填到编辑器中,你可以修改后重新发送
- 从此刻起,你在两个独立的会话文件中各自探索——原始会话保持不变
典型使用场景:
- 你对一周前的一个提问有了新的想法,想基于当时的状态重新开始,但不想破坏原来的会话
- 你想把原会话中的某条不同方向作为”种子”开始一个独立项目
- 你想和同事分享某个特定分支的完整历史,而不用分享整个巨型会话
6.6.4 /clone:当前分支的完整复制
/clone 把当前活跃分支的完整路径(从根到当前叶子)复制到一个新会话文件,停在当前叶子节点,编辑器为空。它比 /fork 更直接——不需要选择,直接复制。
典型使用场景:
- 存档点:当前的探索到了一个”不错但不够完美”的状态,你想保存这个状态,然后在新文件中继续激进地修改而不担心毁掉现有成果
- A/B 测试:你已经在当前分支上打好了基础(项目环境就绪、上下文充分),你想从这个相同起点出发尝试两种不同的实现方案
6.6.5 会话树操作对比
| 特性 | /tree |
/fork |
/clone |
|---|---|---|---|
| 产物 | 同一会话文件内的新分支 | 独立的新会话文件 | 独立的新会话文件 |
| 选择方式 | 全树导航,选择任意节点 | 从当前活跃分支的用户消息中选一条 | 无需选择,直接复制当前分支 |
| 编辑器状态 | 选择用户消息时回填文本;选择非用户节点时留空 | 回填选中的用户消息文本 | 留空 |
| 历史保留 | 全部保留在同一文件中 | 前置历史复制到新文件,后续各自独立 | 当前分支全路径复制到新文件,后续各自独立 |
| 典型场景 | 同一项目内探索不同方案 | 从历史问题重新开始独立探索 | 存档并继续探索 |
6.6.6 会话导出
Pi 支持两种导出格式,用于审计、分享和备份:
导出为 HTML
/export report.html
生成一个自包含的 HTML 文件,支持在浏览器中查看。HTML 导出包含:
- 完整的消息流(用户消息、模型回复、工具调用及结果、思考块)
- 消息树结构(通过 JavaScript 渲染的可折叠树导航)
- CSS 样式美化(接近 Pi TUI 的视觉风格)
- 所有文本和代码块的语法高亮
导出的 HTML 可以分享给没有安装 Pi 的同事、上传到静态网站、或作为项目文档的一部分。
导出为 JSONL
/export session-backup.jsonl
生成原始的 JSONL 文件,包含会话的完整数据结构,可以用 /import 重新加载到 Pi 中。JSONL 导出用于:
- 会话备份(迁移到新机器)
- 程序化处理(用脚本分析会话数据)
- 训练数据准备(会话记录作为 trajectory 数据)
如果不指定文件路径,Pi 会自动保存到 ~/.pi/agent/exports/ 目录,文件名基于当前会话名和时间戳自动生成。
6.7 上下文压缩(Compaction)
模型上下文窗口是有限的——Claude Sonnet 200K、GPT-4o 128K、Gemini 1M 等。当你和 Pi 聊了数百条消息后,上下文使用率(底部栏 ctx 数值)会逼近上限。Pi 提供了一套自动和手动的上下文压缩机制,在保持对话连贯性的前提下释放空间。
6.7.1 自动压缩机制
当上下文使用率超过阈值时(默认 85%,可在 settings.json 中通过 autoCompactThreshold 配置),Pi 自动触发一次压缩:
- Pi 截取当前会话的前 N 条历史消息(不包括最近的对话,以保留即时上下文)
- 调用模型生成一段摘要文本(summary),概括被压缩部分的关键信息:完成的任务、做出的决策、发现的 bug、遗留的 TODO
- 用一条“compaction”类型的消息条目替换所有被压缩的历史消息,消息内容为摘要文本
- 底部栏的
ctx数值下降,上下文窗口空间被释放
压缩高度对用户透明——你会在消息区看到一条系统通知(如 ℹ Context compacted: 45 messages summarized into 320 tokens),被压缩的消息仍然保留在会话文件中(JSONL 结构不变),只是不再发送给模型。你仍然可以通过 /tree 查看完整历史。
自动压缩的智能触发: Pi 不会在模型正在执行工具调用的过程中触发压缩——那会打断工作流。压缩在对话的自然间隙发生(例如你刚发送了一条新消息、模型准备开始回复前)。如果上下文使用率飙升至极端值(>95%),Pi 会强制触发压缩并可能丢弃最早的消息。
6.7.2 /compact 手动压缩
有时候你不需要等待自动压缩——你想主动释放空间、或者你对摘要的内容有特定要求。用 /compact 手动触发:
/compact
Pi 对当前会话执行一次压缩(阈值检查被跳过,强制执行)。你还可以附加自定义摘要指令:
/compact 请重点关注 API 设计的决策和遗留的安全问题
这个自定义 prompt 会作为压缩摘要的”焦点指令”(focus instruction)传递给压缩模型。压缩模型根据这个指令调整摘要内容的侧重点——在上面的例子中,压缩会特别强调 API 设计变更和安全隐患,对琐碎的代码格式修改则一笔带过。
6.7.3 压缩策略配置
在 settings.json 中,你可以精细控制压缩行为:
{
"autoCompactThreshold": 0.85,
"compactModel": "claude-haiku-4-20250514",
"compactPrompt": "Summarize the conversation above, capturing key decisions made, files modified, and unresolved issues.",
"compactRetainRecent": 10,
"compactMaxTokens": 4000
}
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
autoCompactThreshold |
number |
0.85 |
自动压缩触发阈值。0.85 = 上下文使用率达到 85% 时触发。设为 0 禁用自动压缩 |
compactModel |
string |
当前模型 | 用于生成摘要的模型。通常推荐一个廉价快速的模型(如 Claude Haiku、GPT-4o-mini),因为摘要不需要深度推理 |
compactPrompt |
string |
内置默认提示 | 压缩时使用的系统提示。你可以指定摘要应包含哪些重点 |
compactRetainRecent |
number |
10 |
保留最近的 N 条消息不被压缩(保持即时上下文)。值越大,压缩释放的空间越少 |
compactMaxTokens |
number |
4000 |
压缩摘要的最大 token 数。压缩摘要本身也占用上下文,所以不宜过大 |
压缩模型的独立选择: 压缩模型独立于主对话模型——你可以在 compactModel 中指定一个完全不同(通常更便宜)的模型。这很聪明:摘要不需要 Opus 级别的推理能力,Haiku 或同等廉价模型足以应付。这也意味着压缩操作几乎不增加费用——用最便宜的模型处理”减负”任务。
压缩与 /tree 的关系: 压缩不影响会话树的数据完整性。被压缩的消息仍然以原始条目形式存储在 JSONL 文件中,只是被标记为”已压缩”(compacted),不发送给模型。如果你通过 /tree 跳回到压缩前的某一节点,Pi 会自动”解压”这部分历史——因为新的活跃路径从被压缩前开始了,模型需要看到完整历史。
6.8 主题定制
Pi 的 TUI 支持完整的主题定制——从内置的深色/浅色主题,到自定义配色方案,再到热重载,Pi 让你可以把终端界面调整到完全符合自己的审美。
6.8.1 内置主题
Pi 内置了两个主题:
| 主题名 | 说明 |
|---|---|
dark |
深色主题(默认)。深灰背景、白色/浅灰文本、高对比度的语法高亮 |
light |
浅色主题。白/浅灰背景、深色文本、柔和的语法高亮 |
切换主题的方式:
在 /settings 面板中: 进入 Settings → Theme → 选择 dark 或 light。
在 settings.json 中:
{
"theme": "light"
}
通过 CLI 参数(仅本次会话):
pi --theme light
6.8.2 自定义主题
你还可以创建完全自定义的主题,保存在 JSON 文件中。主题文件的默认搜索路径:
~/.pi/agent/themes/(全局主题).pi/themes/(项目级主题)
通过 settings.json 指定自定义主题:
{
"theme": "my-custom-theme"
}
Pi 会在上述路径中查找 my-custom-theme.json 文件(优先全局,然后项目级)。如果找不到,回退到 dark 默认主题。
6.8.3 主题文件结构
一个完整的自定义主题文件定义了 Pi TUI 中每一种 UI 元素的颜色配置。以下是一个完整的示例:
{
"name": "my-custom-theme",
"colors": {
"background": "#1a1b26",
"foreground": "#a9b1d6",
"cursor": "#c0caf5",
"selection": "#364a82",
"border": "#565f89",
"black": "#32344a",
"red": "#f7768e",
"green": "#9ece6a",
"yellow": "#e0af68",
"blue": "#7aa2f7",
"magenta": "#ad8ee6",
"cyan": "#449dab",
"white": "#787c99",
"brightBlack": "#444b6a",
"brightRed": "#ff7a93",
"brightGreen": "#b9f27c",
"brightYellow": "#ff9e64",
"brightBlue": "#7da6ff",
"brightMagenta": "#bb9af7",
"brightCyan": "#0db9d7",
"brightWhite": "#acb0d0",
"startupHeader": "#7aa2f7",
"editorBorder": {
"default": "#9ece6a",
"active": "#7aa2f7",
"busy": "#e0af68",
"error": "#f7768e"
},
"editorCursor": "#c0caf5",
"editorSelection": "#364a82",
"editorText": "#a9b1d6",
"editorPlaceholder": "#565f89",
"userMessage": "#7aa2f7",
"assistantMessage": "#a9b1d6",
"toolCall": "#e0af68",
"toolResult": "#787c99",
"thinkingBlock": "#444b6a",
"thinkingBlockText": "#787c99",
"error": "#f7768e",
"warning": "#e0af68",
"info": "#449dab",
"success": "#9ece6a",
"footerBackground": "#1a1b26",
"footerForeground": "#565f89",
"footerHighlight": "#7aa2f7",
"footerWarning": "#e0af68",
"treeNode": "#a9b1d6",
"treeBranch": "#565f89",
"treeActive": "#7aa2f7",
"treeTag": "#e0af68",
"commandPalette": {
"background": "#24283b",
"foreground": "#a9b1d6",
"selected": "#364a82",
"description": "#565f89"
},
"fileSearchPopup": {
"background": "#24283b",
"foreground": "#a9b1d6",
"selected": "#364a82",
"pathMuted": "#565f89"
}
}
}
颜色值支持以下格式:
- 十六进制(Hex):
#1a1b26、#7aa2f7(短格式#abc不支持,必须用 6 位) - 命名颜色:
"red"、"blue"、"green"、"cyan"、"magenta"、"yellow"、"white"、"black" - 终端颜色索引:
"color16"(使用终端调色板中的第 16 号颜色)
字段语义详解:
| 颜色字段 | 影响的 UI 元素 |
|---|---|
background / foreground |
全局背景和前景(文本)色 |
cursor |
编辑器光标颜色 |
selection |
选中文本的背景色 |
border |
编辑器边框、弹窗边框等 |
black ~ brightWhite |
标准的 16 色 ANSI 调色板,影响 Markdown 代码块的语法高亮、终端内嵌输出的颜色 |
startupHeader |
启动头部的文字颜色 |
editorBorder.default |
编辑器边框默认色(通常是绿色或主题色) |
editorBorder.active |
编辑器边框活跃色(光标在编辑器中时) |
editorBorder.busy |
编辑器边框颜色(模型忙碌时——通常是脉冲动画) |
editorBorder.error |
编辑器边框错误色(消息发送失败等) |
editorCursor |
编辑器光标颜色 |
editorSelection |
编辑器选中文本的背景色 |
editorText |
编辑器中的文本颜色 |
editorPlaceholder |
编辑器占位符文本颜色(如”输入 / 查看命令”) |
userMessage |
消息区中用户消息的文本颜色 |
assistantMessage |
消息区中助手消息的文本颜色 |
toolCall |
工具调用声明的颜色 |
toolResult |
工具返回结果的颜色 |
thinkingBlock |
思考块的背景色 |
thinkingBlockText |
思考块内的文本颜色 |
error / warning / info / success |
错误、警告、信息、成功的颜色 |
footerBackground / footerForeground |
底部栏的背景和文字色 |
footerHighlight |
底部栏高亮文本色(如模型名、费用等关键数字) |
footerWarning |
底部栏警告文本色(如上下文使用率接近上限) |
treeNode / treeBranch / treeActive / treeTag |
/tree 视图中的节点、分支线、当前活跃节点、书签的颜色 |
commandPalette.* |
/ 命令补全菜单的颜色 |
fileSearchPopup.* |
@ 模糊文件搜索弹窗的颜色 |
6.8.4 主题热重载
Pi 的主题系统支持热重载(hot reload)——你修改主题 JSON 文件并保存后,Pi 的 TUI 界面立即更新,不需要 /reload,更不需要重启 Pi。
热重载基于文件系统的 watch 机制:Pi 监视已加载的主题文件路径,一旦检测到文件修改,自动重新解析 JSON 并更新所有 TUI 组件的颜色——整个过程在一秒内完成。
这对于微调配色方案极其方便:你在另一个终端窗口中打开主题 JSON 文件,一边调色一边看 Pi 的实时变化,直到满意为止。
6.8.5 社区主题
Pi 的包生态中有大量社区贡献的主题包。例如:
pi install npm:@catppuccin/pi-theme
pi install npm:@tokyonight/pi-theme
pi install npm:@dracula/pi-theme
pi install npm:@nord/pi-theme
安装后,通过 settings.json 指定主题名即可。这些社区主题通常只维护 colors 对象,与 Pi 的主题格式完全兼容。
6.9 本章小结
本章对 Pi 交互式 TUI 进行了地毯式扫描。你应当已经掌握了以下内容:
- TUI 四区域布局: 启动头部(加载资源清单)→ 消息区(流式对话 + 工具调用 + 思考块)→ 编辑器(思考级别颜色边框 + 多功能输入)→ 底部栏(实时 token / 缓存 / 费用 / 上下文使用率 / 模型名)
- 编辑器高级功能:
@模糊文件搜索、Tab路径补全、Shift+Enter/Ctrl+Enter多行输入、Ctrl+V/Alt+V图片粘贴及拖拽、!command/!!command内联 Shell 执行、Ctrl+G外部编辑器调用、Vim 模态编辑 - 20+ 条斜杠命令: 从
/login、/model、/settings到/tree、/fork、/clone、/compact、/export、/share、/hotkeys等,覆盖认证、模型管理、设置、会话操作、导出分享和配置重载 - 消息队列系统: Steering(
Enter)中途纠偏、Follow-up(Alt+Enter)排队任务、Alt+Up取回消息、Esc中断并恢复;steeringMode/followUpMode交付策略配置 - 键盘快捷键系统: 30+ 个默认快捷键(全局 + 编辑器 +
/tree)、keybindings.json自定义覆盖、Emacs 风格内置支持、Vim 模式整体切换 - 会话树与导航: JSONL 树形结构(
id/parentId)、/tree可视化导航(五种过滤模式 + 书签)、/fork从历史消息独立分支、/clone复制当前分支存档;HTML / JSONL 双格式导出 - 上下文压缩: 自动压缩触发机制(
autoCompactThreshold)、/compact手动压缩(可选焦点指令)、压缩配置策略(compactModel/compactPrompt/compactRetainRecent/compactMaxTokens) - 主题定制:
dark/light内置主题、自定义 JSON 主题文件(40+ 颜色字段)、热重载(修改即生效)、社区主题安装
Pi 的 TUI 就是它最强的交互界面。掌握本章的所有内容,意味着你对 Pi 的操作进入了”肌肉记忆”阶段——不再需要看键盘、不再需要猜测某个功能在哪里——你的全部注意力可以放在真正重要的事情上:和 Pi 一起写好代码。
下一章:第七章:工具系统内置工具与权限控制 将转向 Pi 的工具系统:四个核心工具(read、write、edit、bash)+ 三个可选只读工具(grep、find、ls)的精确语义,以及如何通过 CLI 参数和配置精确控制模型的工具范围。