# pi-open-tui

[![omp 状态](https://omppackages.com/badge/pi-open-tui.svg)](https://omppackages.com/packages/pi-open-tui)

[English](./README.md) | **简体中文**

一个为 [Pi](https://pi.dev) 编程代理打造的终端界面扩展。

![pi-open-tui 预览](https://raw.githubusercontent.com/OldSuns/pi-open-tui/main/assets/preview_dashboard_1.png)

Pi 1.4.0 的真实终端截图。

## 功能亮点

- **Pi 顶栏**：显示模型、思考等级、当前目录和常用斜杠命令提示
- **自适应底栏**：集中展示 Git 状态、运行环境、上下文用量、Token、费用和扩展状态
- **带边框的编辑器**：支持块状、竖线和下划线三种光标样式；保留其它扩展已安装的兼容编辑器
- **项目环境感知**：识别 50 多种运行环境，并展示 ahead/behind、已暂存、已修改、未跟踪、stash 和 detached HEAD 等 Git 状态
- **单轮遥测**：展示 TPS、首 Token 延迟（TTFT）、耗时、停顿、Token 数量和模型标价速率
- **思考预览**：模型工作时，在 Pi 隐藏思考块的 `Thinking...` 位置显示实时字幕，展示推理内容的末尾片段
- **交互式设置**：通过 `/open-tui` 配置，并支持英文和简体中文界面

## 环境要求

- Pi 1.2.0 或更高版本
- Node.js 22.19.0 或更高版本（Pi 的最低要求）
- 支持 UTF-8 和彩色输出的终端
- 使用完整图标集时需要 [Nerd Font](https://www.nerdfonts.com/font-downloads)（可选；内置可移植 Unicode 图标）

## 安装

安装扩展：

```bash
pi install npm:pi-open-tui
```

也可以只在当前会话中试用：

```bash
pi -e npm:pi-open-tui
```

## 字体与图标

可从 [Nerd Fonts 官方下载页](https://www.nerdfonts.com/font-downloads)或 [GitHub 最新版本](https://github.com/ryanoasis/nerd-fonts/releases/latest)下载任意已修补字体。安装后，请在终端配置中选择该字体，并重启终端。

默认的 `auto` 模式检查终端环境，而不是已安装的字体文件。在支持 UTF-8 的交互式 TTY 中，它会使用 Nerd Font 图标，即使 Pi 运行在 runner 或子 shell 中。如果图标显示为方框、乱码或错误符号，请打开 `/open-tui`，在**外观**页选择合适的模式：

- `nerd`：终端已配置 Nerd Font 时，强制使用 Nerd Font 图标
- `unicode`：可移植 Unicode 图标（文件夹、分支、笔记本电脑、灯泡、插头、沙漏等），无需修补字体；emoji 字形通过终端的 emoji 回退渲染
- `ascii`：使用纯文本图标，无需安装修补字体
- `auto`：在支持 UTF-8 的交互式 TTY 中使用 Nerd Font 图标；SSH 会话使用可移植 Unicode 图标（字体由客户端终端决定，通常没有 Nerd Font）；非交互式输出、`TERM=dumb` 或明确配置为非 UTF-8 的 locale 使用 ASCII

如果已经安装字体，但 `auto` 仍选择 ASCII，请手动切换为 `nerd`。使用 VS Code、Windows Terminal 等应用时，只在操作系统中安装字体还不够，还需要在对应的终端配置中选中该字体。如果 SSH 客户端终端确实安装了 Nerd Font 并希望在 SSH 下使用完整图标，请将模式显式设为 `nerd`。

如果较宽的 Nerd Font 图标占用了图标与文字之间的间隔（例如在 Kitty 中），可开启 `footerSegments.nerdIconSpacing`（`/open-tui` 中的 **底栏 → Nerd 图标后额外留空**）。开启后底栏会在 Nerd Font 图标后额外预留一格空白，包括运行环境图标，常规底栏和内嵌底栏的宽度计算都包含这格留白；Unicode/ASCII 图标与紧凑的 Git 状态标记不受影响。Kitty 用户也可以在 `kitty.conf` 中设置 `narrow_symbols U+E000-U+F8FF 1`。

## 配置

运行 `/open-tui` 打开设置窗口，其中包含**常规**、**外观**、**底栏**和**遥测**四个页面，并会适应终端尺寸。设置保存在 Pi 的 agent 目录内的 `open-tui.json` 中；默认目录为 `~/.pi/agent`，可通过 `PI_CODING_AGENT_DIR` 覆盖。保存失败时会提示：修改仅在当前会话生效。

配置示例：

```json
{
  "enabled": true,
  "header": true,
  "inlineFooter": false,
  "copyButtons": true,
  "settingsLanguage": "zh",
  "cursorStyle": "block",
  "icons": {
    "mode": "auto"
  },
  "footerSegments": {
    "cwd": true,
    "hostname": false,
    "sessionName": false,
    "gitBranch": true,
    "gitStatus": true,
    "gitCommit": false,
    "runtime": true,
    "context": true,
    "tokens": true,
    "cost": true,
    "liveTps": true,
    "extensionStatuses": true,
    "capitalizeProviderName": true,
    "nerdIconSpacing": false
  },
  "telemetry": {
    "enabled": true,
    "tps": true,
    "ttft": true,
    "duration": true,
    "tokens": true,
    "stalls": true,
    "cost": true
  },
  "thinkingPeek": {
    "lines": 1
  },
  "toolView": {
    "mode": "native"
  }
}
```

主要选项：

| 选项 | 可选值 | 说明 |
| --- | --- | --- |
| `settingsLanguage` | `en`、`zh` | 切换 `/open-tui` 设置界面的语言 |
| `header` | `true`、`false` | 决定是否用 open-tui 顶部信息栏覆盖 Pi 的 Header；设为 `false` 时把顶部栏位置留给 Pi 或其他扩展 |
| `inlineFooter` | `true`、`false` | 将两条主要 Footer 信息行移入编辑器上下边框以节省垂直空间，默认关闭；扩展状态行仍显示在编辑器外 |
| `copyButtons` | `true`、`false` | 全屏模式下给代码块加圆角边框，并在代码块和用户消息上显示可点击的 `[Copy]` 标签，默认开启；见[复制按钮](#复制按钮) |
| `cursorStyle` | `block`、`bar`、`underline` | `bar` 和 `underline` 需要终端支持光标形状转义序列 |
| `icons.mode` | `auto`、`nerd`、`unicode`、`ascii` | 控制底栏和遥测通知使用的图标 |
| `footerSegments` | 布尔开关 | 分别控制底栏中的各项数据 |
| `footerSegments.capitalizeProviderName` | 布尔开关 | 将底栏中提供商名称的首字母大写；设为 `false` 时保留原始大小写 |
| `footerSegments.nerdIconSpacing` | 布尔开关 | 在底栏 Nerd Font 图标后额外留一格空白；默认 `false` |
| `telemetry` | 布尔开关 | 控制遥测总开关和各项指标 |
| `thinkingPeek.lines` | `0`、`1`、`2` | 关闭、单行或双行思考预览 |
| `toolView.mode` | `native`、`compact` | Pi 原生卡片（默认），或带编辑/写入成功预览的紧凑调用与摘要行。只对切换后新建的行生效 |

`sessionName` 仅在会话有名称时显示；`hostname` 会显示主机名的短名称（主机名的第一个标签，例如从 `mba.example.com` 显示为 `mba`），并使用服务器图标；处于分离 HEAD（detached HEAD）状态时，`gitBranch` 会显示 `HEAD` 和短提交哈希，`gitCommit` 会在其后追加指向该提交的标签，且仅在开启 `gitBranch` 时生效；关闭 `extensionStatuses` 会隐藏整行扩展状态，其中也包括 MCP 状态。每条状态会保留其扩展通过 `ctx.ui.theme.fg()` 设置的颜色；未设置颜色的状态使用 muted 主题色显示。

底栏的 Token 和费用总计包含助手回复、工具报告的用量、压缩与分支摘要以及缓存预热；缓存命中百分比表示最近一条助手回复，而非整个会话的累计比例。

底栏第一行宽度不足时，先隐藏运行环境和上下文条，然后依次隐藏 Git、会话名称、主机名和计时器；当前目录保留到最后，并缩短为文件夹名。

开启 `inlineFooter` 后，两条常规 Footer 信息行会移入编辑器边框，从而节省垂直空间。顶部边框左侧显示 Git 分支，右侧信息组以当前目录开头；开启 `sessionName` 时，会话标题也会显示在左侧。Header 和扩展状态行保持独立显示；终端较窄时优先截断低优先级 Footer 数据，保留右侧统计信息和边框角。

open-tui 会给保留 Pi 原生编辑器布局及光标、鼠标、内边距接口的编辑器加边框，包括基于 `CustomEditor` 的扩展。装饰直接作用于同一实例，因此灰字建议、按键处理、补全和提交回调能够保留。通过当前 `ctx.ui.setEditorComponent()` 接口注册的编辑器扩展可以排在 `pi-open-tui` 前面或后面，运行时替换编辑器也会自动加框，包括在持有新 UI 上下文的快捷键回调中替换。替换后的实例在 Pi 挂载时直接装饰，不会再次调用扩展的创建函数；临时对话框保持原样。装饰前，open-tui 会按终端宽度渲染一次编辑器，检查是否存在可识别的原生上下边界。每次加框也会检查本次实际输出；没有这些边界时按完整请求宽度重新渲染，不应用边框的鼠标坐标偏移，原生边界恢复后再继续加框。缺少这些能力或边界、或在布局检测时抛出异常的编辑器保持原样，即使开启 `inlineFooter`，底栏也会独立显示。关闭 open-tui 会撤销装饰、恢复注册接口，并保留最新的编辑器工厂，包括后注册的扩展包装。

顺序无关的保证针对编辑器注册，不包含顶栏或底栏的替换。如果扩展调用预先保存的旧注册函数，或直接替换注册接口本身，则不在此保证范围内。

## 单轮遥测

每次 Agent 完整运行结束后，pi-open-tui 会显示一条临时结果，并将其中的多个工具调用轮次合并统计：

```text
> TPS 42.5 tok/s | ~ TTFT 1.2s | + 29.7s | ↑ 567 | ↓ 1.2k | ! stall 1x / 4.3s | $ $3.60/M
```

TPS 的计算方式是：将本次运行中服务商报告的全部 Assistant 输出 Token，除以各个生成轮次的总耗时。计时范围从 `turn_start` 到 Assistant 的 `message_end`，包含 TTFT、隐藏推理、缓冲和停顿，但不包含轮次之间的工具执行时间。没有输出 Token 或无法测得生成时间时，会显示 `TPS —`。

回复流式输出时，工作计时器还会显示当前的估算速度，例如 `working 12.3s · ~54 tok/s`。`~` 表示估算值：它是最近 2 秒内流式输出的正文、思考内容和工具调用参数的速率，按拉丁文本和代码约 4 个字符 1 个 Token、中日韩字符每字 1 个 Token 估算。停顿时数值会逐渐降向零；工具执行期间和消息之间以暗色保留上一个值。由**底栏 → 实时 TPS**（`footerSegments.liveTps`）控制，与遥测通知的开关互不影响。运行结束后不再显示，以上面的精确 TPS 为准。

`$ / M` 表示根据 `usage.cost.total` 得到的模型标价速率，不是底栏中的会话累计费用。所有遥测字段都可以在**遥测**页单独开关。

## 思考预览

开启 Pi 的 **Hide thinking** 后，任务运行期间 pi-open-tui 会在原生隐藏思考块的 `Thinking...` 位置显示紧凑的实时字幕。`/open-tui` 中提供**关闭、单行、双行**三种模式：

- 模型推理时，思考内容的末尾片段会随 spinner 滚动（`~ think ⠋ …`）；
- 开始输出正文时定格为对勾（`~ think ✓`）；
- 双行模式中，上一条和最新一条思考内容使用相同的文字缩进；如果最新一行超出宽度，则两行随新 Token 持续显示它的最新末尾片段，不再保留上一条；
- 任务结束后恢复原生 `Thinking...` 标签。

```text
~ think ⠋ 上一条思考
          最新一条思考
```

该字幕仅在模型真正输出推理内容后出现，非推理模型不会显示。可见性由 Pi 自身的 Hide thinking 开关控制，切换后立即生效。每一行都按*显示宽度*截断（全角字符计 2 列），因此包含中文的思考文本也不会溢出终端。可在 `/open-tui` 的**常规 → 思考预览**中切换，或直接修改 `open-tui.json` 中的 `thinkingPeek.lines`。

## 工具视图

默认仍为**原生**模式。将**常规 → 工具视图**设为**紧凑**（`toolView.mode: "compact"`）后，Pi 内置工具（`read`、`bash`、`powershell`、`edit`、`write`、`grep`、`find`、`ls`）显示为一行调用加一行摘要，编辑和写入成功时附带预览：

```text
● Read(extensions/open-tui/footer.ts:1-120)
  └  120 lines
● Edit(extensions/open-tui/footer.ts)
  └  +2 −1
● Bash(npm test)
  └  Done · 131 lines · 6.4s
```

- 成功后调用与摘要文字变暗，圆点为绿色；等待时圆点变暗，运行时圆点每秒在主题强调色与暗色之间切换。错误继续显示红色圆点与摘要。Shell 失败摘要保留最后一行输出；运行中显示最新 3 行输出和已用时间。
- `edit` 成功预览解析 Pi 的真实 diff，分别对旧代码和新代码做语法高亮。删除/新增行使用红/绿整行背景色带，在终端右边缘前留出两列。仅相邻且配对、共享非空白内容至少占 30% 的删除/新增行使用更强的变更词背景；超出匹配预算时回退到整行高亮。没有有效 diff 时保留摘要。
- `write` 成功预览使用正常、中性的终端背景，显示行号和语法高亮，不带 `+` 号。新建与覆盖写入显示相同，不对比旧文件。部分结果和错误不显示成功预览。
- 预览正文最多显示 16 行；超过时显示前 16 行，加上隐藏行数与 `Ctrl+O` 提示。编辑的上下文行和省略行也计入行数。仅紧凑模式下，长行以 `…` 横向截断，不自动换行。
- 点击某行只展开这一行为 Pi 原生卡片，`Ctrl+O` 展开全部行；原生展开视图的完整输出、diff 和截断提示保持不变。各次调用独立显示，不分组，也不增加配置项。
- 只改变显示：工具、参数、结果以及模型看到的内容都不受影响。仅接管 Pi 确认来源为内置的工具；第三方和 MCP 工具（包括替换内置同名工具的扩展）保持各自的渲染。
- 紧凑摘要会标记输出截断，包括 grep 的长行截断；方括号正文和失败信息会保留，不会被误当成工具通知删除。
- 模式在 Pi 创建某行时确定，因此切换只影响新建的行；执行 `/reload` 或新开会话后整个记录都会应用。

## 复制按钮

全屏模式（Pi 的 `tuiMode: "fullscreen"`）下，每个代码块都会画在圆角边框里，顶边显示语言和始终可见的 `[Copy]` 标签；每条用户消息也有 `[Copy]` 标签。点击即可把原始文本放入剪贴板，Pi 会短暂提示 `Copied!`。

```text
╭─ sh ─────────────── [Copy] ╮
│ pi install npm:pi-open-tui │
╰────────────────────────────╯
```

- 代码块：边框替代了上下两行围栏，代码块高度不变；过长的行在边框内换行。标签复制不含围栏的代码，保留缩进和制表符。用户消息和助手回复里的代码块都适用。
- 用户消息：标签位于气泡最后一行，复制你输入的原文。
- 整条助手回复没有标签，请使用 Pi 的 `/copy`。
- 回复输出过程中代码块只有边框、没有标签，避免复制到不完整的代码；边框太窄时也不显示标签；思考块保留 Pi 原生的围栏。
- 普通模式不捕获鼠标，代码块保留 Pi 原生的围栏。
- 拖动选择经过边框时会包含边框字符和 `[Copy]` 文字。
- 如果同时安装了 pi-pretty-tui，则保留它自己的代码块边框和复制按钮，不再绘制这些内容。

可在 `/open-tui` 的**常规 → 复制按钮**中切换，或直接修改 `open-tui.json` 中的 `copyButtons`。

## 本地开发

```bash
npm install
npm test
npm run typecheck
pi -e .
```

CI 在 Linux、Windows、macOS 上，使用 Node.js 22.19.0 和 24，检查 Pi 1.2.0 及最新版本。核对兼容性时应让各个 Pi 包保持同一版本。自动化测试不能替代通过 `pi -e .` 检查终端字体、鼠标交互和光标支持。

## 致谢

本项目基于多个 Pi 社区包的工作：

- **[pi-haiku](https://github.com/nnocte/pi-haiku)** — 双行底栏结构和工作计时器
- **[pi-claude-code-tui](https://github.com/Phoobobo/pi-claude-code-tui)** — Pi Logo 图案与圆角编辑器边框技术
- **[pi-zentui](https://github.com/lmilojevicc/pi-zentui)** — Starship 风格底栏、运行环境检测、会话生命周期和设置界面模式
- **[pi-tps](https://github.com/monotykamary/pi-tps)** — 单轮计时、停顿检测和保守的 TPS 计算方式
- **[pi-polish](https://github.com/Pringled/pi-extensions/blob/main/packages/pi-polish)** — 紧凑的工具调用行和带语法高亮的编辑 diff

Logo 图案源自 Pi 官方安装脚本（`pi.dev/install.sh`）。运行环境检测和 Git porcelain 解析借鉴了 `pi-zentui` 的结构。

特别感谢 **[LINUX DO](https://linux.do)** 社区的支持。

## 许可证

[MIT](./LICENSE)
