# Agent Note: Dim-gray pulse for the running prompt glyph

Status: implemented

[English](2026-07-27-tui-running-glyph-smooth-fade.md) | 中文

## Problem

回合运行时，TUI 会把 `>` 提示符替换为阶段字形（`◍`/`✻`/`●`/`⚙`）。此前的迭代用强调蓝为其亮度做动画（先是离散 SGR 波，后是 truecolor 呼吸）——一个持续脉动的彩色指示器。期望的效果保留持续脉动以示正在工作，但改为安静的暗灰而非颜色，并在两端做平滑的淡入淡出。

## Decision

运行字形是一种暗灰色，在回合开始时淡入，运行期间持续脉动，回合结束后淡出，随后恢复为普通的 `>` 光标。它从不使用强调色。

亮度是淡入淡出包络乘以运行脉冲。包络控制出现/消失，随渲染时钟在 `STATUS_FADE_MS = 300` 内线性变化：淡入为 `(now − startedAt)/FADE` 并做钳制，淡出为 `1 − (now − endedAt)/FADE`。`pulseLevel` 是在 `STATUS_PULSE_FLOOR`（0）与 1 之间、周期为 `STATUS_PULSE_PERIOD_MS = 1400` 的余弦，因此每次呼吸都从完全不可见涨到满亮再回落。交给 `fadeGlyph` 的 truecolor 不透明度为 `envelope × pulse`。

`fadeGlyph` 以该不透明度渲染。在 truecolor 下，低于 `STATUS_FADE_MIN_OPACITY`（0.12）时字形被完全隐藏——留出空白列——因此脉冲谷值消失，而非停留为接近背景的灰；在其之上，字形在 `STATUS_FADE_GRAY.trough` 与 `.settled`（与空闲光标相同的暗灰）之间插值出 24 位灰色，发出 `\x1b[38;2;r;g;bm`，因此淡入与脉冲都表现为亮度。没有 truecolor 时不存在逐帧灰度，因此用一个单独的 `visible` 标志——只由包络驱动，而非脉动的不透明度——以调色板 muted 角色显示字形或留出空白列；脉冲从不使回退闪烁。完全关闭颜色时，可见字形以裸字符呈现，在单色终端上保住光标列。

运行提示符以 `STATUS_ANIMATION_INTERVAL_MS = 50`（约 20 fps）刷新，使脉动逐帧移动；同一次 tick 也让 0.1 s 精度的耗时文本保持最新，因此不需要单独的计时器。

淡出会延续到回合之后：在运行 → 非运行的边沿，`beginFadeOut` 把最后渲染的字形交给一个 `FadingStatus`，其自有计时器持续重绘，直到渐变窗口结束，然后调用 `clearStatus` 并恢复 `>`。拆解路径（dispose、agent-disposed、启动失败）直接调用 `clearStatus`，一次性停止运行与淡出两个计时器——不会有残留的渐变。交给淡出的字形是最后一次的实时阶段字形（`runningStatus.lastGlyph`），而非收尾回合的步骤结束后阶段推导返回的 ttft 兜底字形。

字形字符及其单元格从不改变——只有灰色亮度变化——所以光标列在各帧之间以及光标↔字形的切换之间都保持固定。

## Alternatives considered

**保留强调色。** 需要脉冲，但要用与空闲光标一致的安静灰色，而非彩色指示器；移除强调色，保留脉动。

**运行时保持稳定（不脉动）。** 曾试过稳定的暗色字形并被否决：持续脉动更能表明代理正在积极工作。脉动以灰色回归。

**用非零下限让谷值保持微弱可见。** 逐次下限（0.45 → 0.15 → 0.02）都让最暗点太可见，读不出真正的安静；即便 0.02 也停在灰度约 45，仅比背景高一档。改用下限 0 加显式可见阈值（`STATUS_FADE_MIN_OPACITY`），在每次呼吸的底部完全隐藏字形，使谷值真正缺席。由于涨落是平滑余弦，消失读作柔和的淡出，而非低而非零的灰度开关会带来的硬性开/关闪烁。

**让非 truecolor 回退也脉动。** SGR 只暴露三个强度档位，做平滑脉动太粗糙，而按脉冲开关字形会使其闪烁。回退改为由包络控制的稳定 muted 字形；只有 truecolor 终端获得脉动。

## Consequences

代价是运行或淡出期间渲染 tick 更快（50 ms），换来的是运行字形整段回合读作一种从无涨到暗记号再回落的安静灰色呼吸，与空闲光标的色调一致；差分终端只重发变化的单元格，因此额外帧开销很低。淡出意味着指示器在回合结束后残留约 300 ms。快照以非 truecolor、冻结时钟运行，因此只钉住由包络控制的稳定 muted 字形，而非脉动；truecolor 的不可见谷值、稳定峰值、上升中间帧、淡出、以及非 truecolor 的出现/消失均由 `tui.spec.ts` 的单元测试钉住。
