# Agent Note: 统一的 TUI 呈现与导航

Status: implemented
Archived: 2026-08-04

[English](2026-07-28-consolidated-tui-presentation.md) | 中文

## Problem

终端 UI 逐步积累了多套彼此干扰的呈现规则：调色板角色互为别名，或在浅色终端中颠倒强调层级；工具卡片的框架、输出和退出标记重复或争夺注意力；注入上下文被当作 XML 解析，无法可靠折叠；`/resume` 即使能通过启动器访问其他工作区，也会排除不属于当前工作区的会话。每个症状看似局部，但持久决策只有一个终端阅读模型：精简且可检查的调色板、以状态为首且正文内收的卡片、与内容无关的记录折叠，以及感知工作区的导航。

## Decision

### 调色板

`paletteSpec(scheme)` 是 SGR 开始码、结束码和用途的唯一表。`createPalette` 从该表派生所有包装器，`/palette` 在运行中的终端打印同一张表。除固定的启动品牌渐变外，组件不自行发出 SGR 序列。每个结束码都重置对应开始码设置的所有 SGR 组。

重复角色被合并：`muted` 并入 `dim`，`added` 并入 `success`，`removed` 并入 `error`，未使用的第二强调色被移除。`dim` 在两种配色方案中都使用 `2;39`，并以 `22;39` 结束，使内收文本相对于终端前景色变暗，而不会在浅色背景上变成固定的深灰色。TypeScript 分别标记颜色和属性，允许属性与颜色组合，同时拒绝会因重置而丢失外层颜色的嵌套颜色。

### 工具卡片

工具卡片由一行带颜色的 `Tool / <name>` 状态标题和一块统一的 dim 正文组成。呈现器标题、终端命令及 cwd 行、输出、XML 文本和折叠标记都使用正文色调。差异颜色继续保留，因为红绿承载语义；信号标记也继续作为错误显示。

`renderUnknownXml` 对未知工具结果显式接收正文样式器。终端呈现器在返回 `TerminalResultView.output` 前解析并移除面向模型的末尾退出或信号标记；TUI 只把结构化状态呈现一次。截断、超时和沙箱信息继续留在正文中，因为状态标记不表达这些事实。

### 注入上下文与折叠

注入上下文由 `ContextCardComponent` 按普通文本呈现，不经过 XML 树渲染器。仅移除精确配对的外层 `<system-reminder>` 行；不匹配、单边或正文内类似标签的文本都原样保留。面向模型的内容不变。折叠在正文组装完成后使用共享 `preview` 辅助函数，因此只取决于行数，不依赖解析是否成功或载荷包含哪些字符。

`Ctrl+O` 在折叠、展开和隐藏之间循环。隐藏状态会连同卡片自有的前导间距一起移除工具卡片。上下文卡片参与折叠和展开状态，但工具隐藏时回到折叠状态，因为注入指令不是可丢弃的工具流量。隐藏阶段还会把每个轮次的 assistant 步骤折叠为一条消息；该规则由[隐藏模式 assistant 折叠 Agent Note](../feature/2026-07-29-tui-hidden-mode-assistant-fold.md)负责。

### 跨工作区恢复

恢复选择器汇总所有记录，并维护可用 Tab 切换的当前工作区/所有工作区范围。默认范围是当前工作区；只有更宽范围才显示工作区标签。没有 cwd 的记录会被拒绝，因为没有可进入的目录。

`TuiResumeHost.handoff` 接收选中的 `SessionId` 和预检时重新读取的 cwd。CLI 在释放当前应用前切换目录，因此无法访问的目录会在终端仍可恢复时失败；随后 `execve` 继承所选工作区。退出提示也由启动器提供，而不是让 TUI 反推启动器命令语法。

## Alternatives considered

**为每个视觉症状保留独立 Agent Note 和局部修复。** 否决：这些决策共享同一阅读层级，而且彼此多次取代。由一份记录统一拥有最终的调色板、卡片、上下文和导航规则，读者无需重建变更顺序。

**保留别名，并依靠约定执行呈现规则。** 否决：别名暗示并不存在的差异；嵌套颜色重置或不完整的 SGR 结束会静默失败。单一表格加类型约束使契约可检查且可机械验证。

**保留工具卡片内部的框架/输出颜色分层。** 否决：真实卡片会混用默认前景、青色命令、dim cwd、无样式 XML 和 dim 输出。状态标题已经提供扫描锚点；统一内收正文能消除噪声。差异颜色是狭窄的语义例外。

**把注入上下文继续解析或修复成 XML。** 否决：提醒框架只是包裹任意普通文本的提示约定，其中会包含原始 `&`、比较表达式和尖括号占位符。修复或转义要么猜测结构，要么改变模型可见文本。

**随工具卡片一起隐藏上下文卡片。** 否决：上下文承载注入指令，不是可恢复的执行细节。因此隐藏阶段只移除工具流量。

**把恢复限制在一个工作区，或在启动后推断 cwd。** 否决：前者迫使用户手动重启；后者恢复的会话头 cwd 并不控制文件系统和 shell 的路径解析。目标目录必须在进程替换前跨过主机接口。

**移除 TUI 退出状态标记，或移除面向模型的退出标记。** 否决：前者是便于扫描的 UI 状态，后者是模型的状态信号。呈现器在构造结构化视图时消费文本标记，使两类受众各看到一种表示。

## Consequences

记录现在表现为带颜色的状态标题和内收细节；上下文对任意普通文本都稳定呈现；一个快捷键控制记录密度。公共 `TuiTheme.muted` 角色被移除，扩展改用 `dim`。调色板和 `renderUnknownXml` 契约更严格，以少量编译期摩擦换取对静默样式丢失的防护。

跨工作区恢复会把所有依赖路径解析的工具移动到另一个目录。cwd 缺失或不可访问时不能交接。更宽的选择范围也使共享会话存储的并发访问更容易触达；跨进程会话锁仍是独立后续工作。

终端呈现器仍会把与退出标记语法完全一致的最后一行输出视为结构化状态，因此命令有意打印这种行时，卡片正文可能丢失该行。`dsh-tool-bash` 已记录这一残余限制。

## Testing

TUI 单元测试和无密钥终端快照覆盖调色板枚举、浅色/深色角色、合法与非法样式组合、统一 dim 卡片正文、保留语义的差异颜色、仅有一个退出状态且正文无标记、普通文本上下文框架、与内容无关的折叠、Ctrl+O 三态循环、模型过滤和两种恢复范围。CLI 交接测试覆盖传递重新读取的 cwd，并在释放前拒绝目录切换失败。tool-bash 测试把结果标记的生成、解析和移除固定为同一轮往返契约。
