文档导航
文档 / TUI Frontend

TUI Frontend

OpenSquilla 终端聊天通过两种 renderer 暴露同一套公共 UI 策略:

Backend or targetStatusHow to useRequirements
autoDefault policyopensquilla chat or --ui autoInstalled host when available; startup-only plain fallback
tuiStrict full-screen TUIopensquilla chat --ui tuiSource override today; future same-version companion
plainMinimal rescue surfaceopensquilla chat --ui plainPython package only
live-opentuiManual harness targetReal-terminal harness onlytmux, OpenTUI deps, and live provider config

live-opentui 不是一个 OPENSQUILLA_TUI_BACKEND 值。它是一个受保护的测试目标, 通过真实 CLI 启动 OpenTUI 路径。

TUI contracts 与 renderer 无关,并围绕两个独立的平面构建:

  • Streaming plane: 在写入终端之前对 token deltas 进行批处理,因此长答案不会 为每个 token 重绘整个界面。
  • Structured UI plane: 将归一化的 TUI domain events 发送给 plugins。Plugin snapshots 可由有能力的 TUI backends 以及未来的 renderers 渲染。

核心 wheel 仍然是平台中立的,当前 release 不发布 TUI companion。源码检出通过下面 的显式开发 override 运行该 host。仓库中还包含一个 builder,可以生成一个自包含的 opensquilla-tui-host 制品用于验证,但正式的 release 工作流和安装程序既不发布也不 安装它。因此 release 安装使用 plain 回退。

Plugin Slots

Plugins 消费与 renderer 无关的 events,并通过命名的 slots 发布小型 snapshots。 当前的 slots 包括:

SlotPurpose
router_hudActive-turn model-routing decision.
statusCompact status or queue notices.
tool_activityTool cards and tool summary history.
usageToken, cache, and cost summary.
inspectorOptional detail panel state for selected items.

第一个 plugin 是 RouterHudPlugin。它监听 router_decision events 并更新底部 工具栏,而不改变 router 的选择行为。

Router HUD

当路由 metadata 可用时,有能力的 TUI backends 可以渲染一个 Router HUD。在当前 实现中,OpenTUI footer 是该 HUD 的主要终端显示。该 HUD 仅用于显示:它消费 turn metadata,并不改变 model 选择。

该 HUD 可以显示:

  • selected tier and model;
  • baseline model;
  • route source;
  • confidence;
  • estimated savings;
  • fallback state;
  • thinking mode;
  • prompt policy;
  • whether routing was applied;
  • rollout phase.

routing_applied=true 加上完整 rollout 显示为一条 active route。 routing_applied=false 或一个 observe rollout 显示为 observe-only。Fallback routes 使用警告样式。一条真实的正常 decision 仍然在紧凑的 footer 中可见(例如 router c0 60%);诸如 gateway 之类的传输 bootstrap 占位符不会作为 decision 呈现。decision 状态会在每个 turn 开始时被清除,因此一个被绕过的 turn 不会继承 先前的 route。

Responsive Transcript and Context

OpenTUI 保持一条线性的、可滚动的 transcript。一旦 context.update 到达,一个 固定的单行 identity header 会呈现产品、任务、规范 Agent、共享 surface 以及 Gateway 状态。在保留的和新的 turn cards 上都使用相同的 Agent 标签,包括在一次 session context 刷新之后。

在 132 终端列或更宽时,一条 30–36 列的 context rail 占据整个终端高度。transcript 和 composer 都按 rail 宽度内缩;该 rail 不会引入一个独立滚动的消息窗格。在更窄 的宽度下,它折叠为一条按优先级适配的单条 footer strip。布局和裁剪使用终端显示 单元格,因此 CJK 和 emoji 标签不会破坏边框或使固定 header 换行。

附加式的 context.update frame 可以携带 Agent identity、任务、surface、Gateway 状态、model、权限、workspace、队列以及 context 信息。不发送该 frame 的较旧父级 会保留先前的几何布局和仅 router 的 footer 行为。

一个空的规范历史会挂载一个 transcript 原生的欢迎视图,其中包含 OpenSquilla wordmark、定位、已解析的运行时 context 以及首个操作快捷键。显示排版会根据 transcript 的实际宽度和终端高度选择六行 block、两行 tiny 或纯文本模式。历史 替换是权威的:恢复的内容会移除欢迎视图,而一个空的 /new/reset snapshot 会重新挂载它。

OpenTUI 的公共 renderer resizefocus events 负责正常的 viewport 恢复。 原始的 WriteStream resize 和 SIGWINCH 只是合并的回退,仅在 renderer 错过其 resize event 时使用。一次 resize、remount、主题变更或历史替换会在一个 pre-paint 事务中重建 transcript、rail、welcome、footer 和 caret,并暴露一个完整 frame。 健康的 focus/resize 路径不会重写 alternate-screen 或鼠标模式;更强的模式重新 断言保留给显式恢复以及一次已知 blur 之后的首次滚轮。这避免了重复的屏幕交换, 并使 caret 保持在 composer 内。

在 Codex、VS Code 或其他终端中没有自动的周期性重绘。仅用于诊断,maintainers 可以通过一个正的 OPENSQUILLA_TUI_REPAINT_WATCHDOG_MS 值(钳制为至少 250ms)来 选择启用。real-terminal gate 必须以默认的事件驱动值零通过。

Complete Process Detail

turn.begin 也会立即打开一个稳定的 reasoning 活动块。它首先渲染 Waiting for model output…;真实的 provider reasoning deltas 会追加到同一个块 并增量渲染。live peek 根据终端高度从三行增长到最多八个可视行,强调最新的一行。 不会生成合成的 reasoning:一个亚秒级的空块会消失,一个较长的空块可能会稳定为 Worked for Ns,而一个包含 provider reasoning 的块会稳定为 Thought for Ns

Thinking、reasoning 和 tool renderers 累积 host protocol 传递的每一个 delta。 Tool detail 包括完整的参数、进程更新、结果和错误。已完成的 reasoning 保留其最新 的至多八个可视行,因此短的 traces 仍然完全可见,而长的 traces 保留其最近的 context,且在终端 resize 时不改变高度。其他已完成的 detail 保持紧凑。折叠内容 显示隐藏可视行的数量;这是一种呈现选择,而非丢弃数据。在 block.end 之后收到的 迟到 deltas 会作为同一个块的一部分被保留。

当一个 ensemble 实际执行时,provider 生命周期 events 会创建一个原地的 Ensemble · n/m complete 块。Ctrl+O 会披露公共的成员 model、provider、状态、 耗时、tokens、成本和错误 metadata。已完成的收据和 fallback 原因在历史 hydration 后仍然保留。候选答案正文和私有 reasoning 从不会被复制到这个块中。仅有配置本身不会 被视为 Ensemble 已执行的证据。footer 单独显示由 Gateway 拥有的 direct | router | ensemble 策略。/strategy 是主要的选择器,而 /router/ensemble 仍然作为兼容性控制;这三者都通过 models.routing.set 更新该规范 状态。在一个活动的 Turn 期间,footer 将其标记为下一个 Turn 的策略,同时该 Turn 继续渲染其捕获的 Router decision 或 Ensemble 生命周期。

composer 在 streaming 期间保持交互。本地 UI 命令会立即执行。在 Gateway 模式下, 忙碌时的 Enter 请求原生的 turn steering,而忙碌时的 Tab 显式地将后续排入 队列;一次迟到/不可用的 steer 会明显地回退到有界队列。standalone turns 保持其 进程内的 tool-boundary 注入契约。历史 hydration 和未解析的附件仍可能显式地禁用 或阻止提交。

Ctrl+O 展开或折叠所有保留的 process detail,而不会从 composer 上夺走焦点。 展开使用经过净化的终端文本和 transcript 当前的内容宽度,包括宽 rail 的内缩。 这一前端保证从 host-protocol 边界开始:上游的 tool-result 压缩或 provider 截断 仍由单独的 tool-compression.md 契约管理。

UI Selection

公共选择器是 --ui auto|tui|plain,省略 --ui 意味着 autoauto 只能在 alternate-screen 启动之前回退。显式的 tui 在 host 缺失或不兼容时清晰地失败。 启动后的 host 崩溃会恢复终端并退出;它不会在一个 turn 期间切换 renderers。

OPENSQUILLA_TUI_BACKEND 是公共选择器与运行时适配器之间的内部交接。裸的 opensquilla chat 会忽略任何预先存在的值,因此陈旧的 profile 或 workspace dotenv 无法禁用 plain 救援路径。新用户和源码开发说明必须使用 --ui

bun install --frozen-lockfile --cwd=src/opensquilla/cli/tui/opentui/package
OPENSQUILLA_TUI_DEV_SOURCE_HOST=1 uv run opensquilla chat --ui tui

源 backend 仅在显式的开发者 override 下加载。resolver 可以为未来的 rollout 验证 一个同版本的已安装 companion,但当前仅核心的 release 安装并不提供这样的 companion。

在没有新的产品方向以及 replay 加真实终端证据的情况下,不要添加并行的终端/前端 实现。

OpenTUI compatibility contract

源 host 和开发 companion 锁定一个精确的 @opentui/core 版本(当前为 0.4.3) 和一个精确的 Bun 工具链。产品布局遵循 OpenTUI 记录的 renderer and resize contractabsolute renderable, mouse, and z-index APIs 以及公共的 ScrollBox contract。 它使用 onMouseScrollscrollAcceleration 以及记录的 cursor 和 lifecycle cleanup API。 应用代码不得覆盖受保护的 renderable 方法。

OpenTUI 0.4.x 文档不暴露 pre-paint 布局回调或公共的 full-frame 失效方法。带类型 的 setFrameCallback / calculateLayout 桥接被隔离在 opentuiCompat.mjs 中, 而唯一的私有 full-repaint 标志被隔离在 viewportRecovery.mjs 中;任何产品组件都 不得深入其中任何一个接缝。每次 OpenTUI 升级都会重新审计这两个适配器,并在有记录 的上游替代方案出现时立即移除。

审慎地跟进新的 OpenTUI release,而不是让依赖浮动:

  1. 一起更新精确的 package 和 native-artifact 锁;
  2. 运行完整的 Node 和 Bun renderer 套件;
  3. 在 80×24、120×30 和 160×40 运行样式化 framebuffer 视觉矩阵;
  4. 在 macOS 和 Linux 上通过真实终端的 streaming、滚轮、resize、focus/remount、 alternate-screen、cursor 和 teardown gates;
  5. 仅在一个单独的 rollout 中接线 release 资产,且需在打包的 companion 在原生 runners 上重复相同的 gates 之后。

一个 OpenTUI 主版本有资格及时被采用,但从不仅仅因为它存在就被采用:公共 API 审查 以及视觉/终端 gates 才是兼容性决定。

组件测试使用 OpenTUI 官方的 @opentui/core/testing renderer 进行精确的字符/跨度/cursor 断言。PTY/tmux harness 仍然是用于真实 alternate-screen 字节和终端模式恢复的单独集成 oracle。

Replay Benchmarks

replay harness 在没有实时 provider 的情况下测量 OpenTUI 渲染路径:

uv run python scripts/bench_tui_replay.py --renderer opentui --fixture long-stream --summary-json .artifacts/tui/opentui-long-stream.json
uv run python scripts/bench_tui_replay.py --renderer opentui --fixture dense-history --summary-json .artifacts/tui/opentui-dense-history.json

Summary 字段包括 rendererfixtureavailableskip_reasonevent_counttext_charstool_countrouter_decision_countwall_msflush_countmax_buffer_charscoalescing_ratiotranscript_itemsvisible_itemsexpanded_toolsprojection_wall_msrendered_text_matchesplugin_error_counterrors

将 OpenTUI 结果用作 renderer 回归证据。它们验证开发 surface;它们并不意味着一个 companion 已被发布。

关于终端级别的启动与渲染证据,请使用 real-terminal TUI harness

产品所有权和 legacy-freeze 规则定义在 tui-product-contract.md 中。

在 GitHub 上编辑此页(英文原稿) OpenSquilla 文档 · 中文社区翻译