DSH / Atlas
2026-07-27implementedsimplificationarchived 2026-08-04

Copyable TUI transcript without gutter bars

无 gutter bar 的可复制 TUI transcript

The TUI grouped user prompts and tool cards behind a colored left-gutter bar (`▌ `) prepended to every body line, and indented assistant and system blocks by one column. Both are per-line prefixes: a terminal mouse drag-select over the scrollback captures the leading `▌ ` or the leading space on each line, so copy-paste of a message, a tool's output, or a code block pulls in decoration the user must strip by hand. Th

English

Problem

The TUI grouped user prompts and tool cards behind a colored left-gutter bar () prepended to every body line, and indented assistant and system blocks by one column. Both are per-line prefixes: a terminal mouse drag-select over the scrollback captures the leading or the leading space on each line, so copy-paste of a message, a tool's output, or a code block pulls in decoration the user must strip by hand. The bar was the transcript's only per-message separator, so it could not simply be dropped without another way to tell messages apart.

Decision

The scrollback carries no per-line prefix. Messages are separated only by a bold, underlined role header in the role color and blank-line spacing, both of which the terminal already inserts around each block. The underline gives each role a distinct visual band without a background fill, so it reads on any terminal theme and never enters the clipboard:

  • User and steering prompts (UserMessageComponent) are a plain Container: a bold, underlined accent You / Steering header line (via the shared messageHeader helper), then the prompt body at column 0.
  • Assistant blocks render a bold, underlined Assistant header, then reasoning and text at column 0, with the timing line at the end of the block (the former paddingX = 1 indent is gone).
  • Tool cards drop the GutterBox wrapper. The card status (pending / error / success) colors the whole title line — the status glyph ( / / ) plus the title text share one color, bold and underlined to match the role headers — instead of a colored bar beside an uncolored title. The body renders unprefixed; body lines still pass through Text at the terminal width so overlong raw tool output wraps rather than overflowing.
  • The GutterBox class is deleted; nothing else used it.

A drag-select over any of these regions now copies exactly the message text.

Alternatives considered

  • Keep the bar only on user messages, drop it on tool cards — leaves tool output, the most-copied region, still polluted. Rejected: the goal is a wholly copyable transcript.
  • A single top rule or bar on the header line only — the body copies clean, but selecting the header still captures a glyph, and it reintroduces a decoration character for no distinguishing gain over the underlined role header.
  • Indent grouped bodies instead of a bar — leading spaces still enter the clipboard, so it does not solve the copy problem; explicitly ruled out.
  • A filled background band on the header (reverse video, or a 256-color muted background) — gives each role a strong color block, but the saturated ANSI fill reads as too heavy and the 256-color shades are fixed rather than theme-remapped. The underline gives per-role distinction with a far lighter footprint.

Consequences

  • Copy-paste from the scrollback is clean with no user post-processing. This was the motivating win.
  • The transcript is flatter than the gutter-bar layout, but each role's bold, underlined header in the role color plus blank-line spacing keeps message boundaries clear without any left-edge fill. Tool-card status stays legible through the colored, underlined glyph and title.
  • Box-drawing borders () on transient overlays — status panel, model selector, resume list — are untouched. They are not scrollback message content and are rarely copied.
  • The affected keyless TUI *.expected.txt snapshots were re-recorded by fixture replay (no API key needed; the recorded LLM sessions are unchanged, only the render differs). Interactive boot and a round-trip prompt were verified in tmux.

中文

Problem

TUI 此前把用户提示词和工具卡片分组在一条彩色左侧 gutter bar()之后,该竖条被逐行加在每一行正文前面,并把 assistant 与系统块整体缩进一列。两者都是逐行前缀:在 transcript 上用鼠标框选时,每一行开头的 或前导空格都会被一并选中,因此复制一条消息、一段工具输出或一个代码块时都会带上装饰字符,用户必须手动清理。该竖条又是 transcript 中唯一的逐条消息分隔标记,所以不能在没有其他区分方式的情况下直接删掉。

Decision

transcript 不再带任何逐行前缀。消息仅通过以角色色渲染的粗体带下划线角色标题和空行分隔,而这两者本就由终端在每个块前后自动插入。下划线让每个角色获得清晰的视觉分带,且无需背景填充,因此在任何终端配色下都可读,也绝不会进入剪贴板:

  • 用户提示词与 steering 提示词(UserMessageComponent)改为普通 Container:一行粗体带下划线的强调色 You / Steering 标题(经共享的 messageHeader 辅助函数生成),随后是位于第 0 列的提示词正文。
  • Assistant 块渲染一行粗体带下划线的 Assistant 标题,随后 reasoning 与文本均在第 0 列渲染,timing 行位于块末尾(原先的 paddingX = 1 缩进已移除)。
  • 工具卡片去掉 GutterBox 包装层。卡片状态(进行中 / 错误 / 成功)对整行标题着色——状态字形( / / )与标题文本共用一种颜色,并同角色标题一样加粗且带下划线——而不再是未着色标题旁的一条彩色竖条。正文无前缀渲染;正文行仍按终端宽度经 Text 处理,使过长的原始工具输出换行而非溢出。
  • GutterBox 类被删除;没有其他地方使用它。

现在对上述任一区域框选,复制得到的正是消息文本本身。

Alternatives considered

  • 仅在用户消息上保留竖条、在工具卡片上去掉 —— 会让最常被复制的工具输出仍然带有污染。已否决:目标是让整个 transcript 都可复制。
  • 仅在标题行上加一条顶部横线或竖条 —— 正文复制干净,但选中标题时仍会带上一个字形,且相比带下划线的角色标题并未带来额外的区分收益,却重新引入了装饰字符。
  • 用缩进代替竖条对分组正文缩进 —— 前导空格仍会进入剪贴板,无法解决复制问题;已明确排除。
  • 在标题上使用填充背景带(反色,或 256 色柔和背景)—— 能给每个角色一块强烈的色块,但饱和的 ANSI 填充观感过重,且 256 色是固定色而非随主题重映射。下划线以远更轻的方式提供了同样的逐角色区分。

Consequences

  • 从 transcript 复制粘贴无需用户做任何后处理。这正是本次改动的核心收益。
  • transcript 比 gutter bar 布局更扁平,但每个角色以角色色渲染的粗体带下划线标题加空行分隔,无需任何左缘填充即可让消息边界保持清晰。工具卡片状态仍通过彩色带下划线的字形与标题保持可读。
  • 临时浮层(状态面板、模型选择器、恢复列表)上的制表符边框()保持不变。它们不属于 transcript 消息内容,且很少被复制。
  • 受影响的 keyless TUI *.expected.txt 快照均通过 fixture 回放重新记录(无需 API 密钥;所记录的 LLM 会话未变,仅渲染不同)。交互式启动与一次往返提示已在 tmux 中验证。