# Agent Note: TUI 提示符主题组合可变的插件值

Status: implemented

[English](2026-07-24-configurable-tui-prompt-theme.md) | 中文

## 问题

终端提示符行与编辑器前缀原先在 TUI 内部由一组固定字段拼装而成，涵盖工作区、模型、用量、缓存、上下文与计时。部署方可以全局更改颜色，却无法调整字段顺序、替换输入前缀、加入插件状态，也无法构建 Powerline 风格的提示符。

## 决策

TUI 主题把 `color`、`truecolor`、`leftPrompt`、`rightPrompt`、`inputPrompt` 以及运行状态下的静态 `inputPlaceholder` 归为一组。三个提示符字符串通过插值引用 `${name}`；未知或不可用的值连同相邻的横向分隔空白一起消失。左右模板共用一行，重叠时保留右侧，宽度计算使用可识别 ANSI 的可见宽度。输入模板控制编辑器首行前缀与续行缩进。

`ctx.tuiPrompt` 是由 `@deepseek-ai/dsh-tui/prompt` 提供的上下文全局注册表。`register(name, initialValue)` 返回带 `set(value)` 与 `dispose()` 的句柄。存储的值是字符串而非回调：更新必须显式发起，未变化的字符串会被忽略，而一次注册、变更或 dispose 会安排一次合并后的通知。渲染器用 `get(name)` 读取当前值，并用 `subscribe(listener)` 订阅何时重绘。该订阅是服务内部的直接回调，而非 Cordis 事件，因此一个自行变化的值仍能重绘，而不需要一个其他消费方永远不会观察的总线条目。`subscribe` 与每个注册都由调用方的 Cordis effect 拥有，因此在订阅方或贡献方的 fiber dispose 时一并移除。每次 `subscribe` 都是一个按记录身份区分的独立订阅，因此两个 fiber 可以传入同一个回调，而 dispose 其中一个不会影响另一个。合并通知会容错每个观察者——同步抛出、返回被拒 promise，甚至一个对字符串渲染也会抛异常的错误（日志走不抛异常的 `errorChain`）——因此一个损坏的观察者不会饿死其余观察者；并且在派发过程中会重新校验每个订阅的存活性，因此同一批次中同步取消了另一个订阅的监听器会立即使其静默。注册遵循 Cordis 的 effect 所有权模型，拒绝重复名称，并在插件 dispose（资源释放）时移除对应的值。

注册的片段被视为可信的、允许携带 ANSI 的呈现输出。模板中的字面文本与普通外部内容仍会被清洗，但提供提示符值的插件可以输出终端控制序列。复合值自行负责协调背景色过渡与分隔符，因此一个 `${powerline}` 值就能渲染完整的 Powerline 段，而无需与相邻的原子提供方耦合。

内置的 `cwd`、`git/worktree`、`token_meter/cache_hit_rate`、`model`、`context`、`queued`、带样式的 `symbol` 标签与 `indicator` 光标符值使用同一个注册表。会话与 agent（智能体）事件更新各自的句柄，运行计时器每一拍更新 `queued`——转向队列徽标，仅在运行中的一轮有排队消息时才可用——与带动画的 `indicator`。随附的输入模板为 `${symbol} ${indicator}`，保留了原有的 `dsh > ` 前缀。

## 曾考虑的替代方案

**每次渲染时求值同步的提供方回调。** 不予采纳：在渲染期执行插件代码会引入一个本可避免的故障边界；存储字符串能让渲染过程不涉及插件求值。

**把变更通知发布为 Cordis 事件。** 已否决：该通知只有一个消费方（当前会话的 TUI 渲染器），因此全局类型事件会增加一个总线条目、scope 分发面以及无人观察的跨插件扇出。服务内部包裹的直接 `subscribe` 回调以更小的面积承载同样的合并重绘。

**暴露语义化的样式角色而非 ANSI。** 不予采纳：语义角色无法表达任意的 Powerline 背景色过渡，除非为每种呈现技巧扩展共享的样式协议。

**把提示符字段放在 TUI 配置顶层。** 不予采纳：模板与颜色选择共同定义终端呈现，应归属于同一个 `theme` 对象之下。

## 后果

提示符值的贡献插件依赖 TUI 专属的注册表，加载顺序位于该服务之后、TUI 消费方之前。命名空间对整个 Cordis 上下文全局生效，与 TUI 当前的单会话 transcript（文本记录）所有权一致。允许任意 ANSI 是有意的信任决策：不受支持的、影响光标的序列可能破坏布局，只有 pi-tui 可见宽度工具能理解的序列才能保证对齐可靠。

通过注册值更改 `inputPrompt` 时，编辑器文本、光标、历史、自动补全与焦点均得以保留，因为 pi-tui 支持原地替换等宽的首行与续行前缀。静态的 `inputPlaceholder` 会被清洗，且仅在 agent 运行且编辑器为空时显示。

## 测试

注册表测试固定校验、重名拒绝、更新、不可用值、合并通知的容错、取消订阅、dispose、插值、尾随字面保留、空白清理与 ANSI 保留等行为。TUI 测试固定嵌套主题默认值、自定义模板、带外值重绘、可变重绘、支持 Powerline 的片段、动态输入前缀宽度以及运行状态下的静态占位文本。组装后的 TUI 演示测试固定服务加载顺序与配置转发。
