From bfe8b84d6b506a4df04714543656212331cc3503 Mon Sep 17 00:00:00 2001 From: Trae User Date: Wed, 9 Sep 2026 16:15:48 +0800 Subject: [PATCH 1/4] feat: redesign TUI AskUserQuestion interaction --- AGENTS.md | 12 + CMakeLists.txt | 6 +- docs/help/configuration.html | 10 +- .../.openspec.yaml | 2 + .../redesign-tui-ask-user-question/design.md | 143 ++ .../proposal.md | 36 + .../specs/tui-ask-user-question/spec.md | 129 ++ .../redesign-tui-ask-user-question/tasks.md | 38 + src/config/config.cpp | 32 + src/config/config.hpp | 4 + src/main.cpp | 2010 ++++++++--------- src/tool/ask_overlay_input.cpp | 194 -- src/tool/ask_overlay_input.hpp | 52 - src/tool/ask_user_question_tool.cpp | 83 +- src/tool/ask_user_question_tool.hpp | 35 +- src/tool/ask_user_question_types.hpp | 37 + src/tui/ask_question_controller.cpp | 516 +++++ src/tui/ask_question_controller.hpp | 207 ++ src/tui/ask_question_editor.cpp | 259 +++ src/tui/ask_question_editor.hpp | 54 + src/tui/ask_question_layout.cpp | 438 ++++ src/tui/ask_question_layout.hpp | 116 + src/tui/ask_question_overlay.cpp | 470 ---- src/tui/ask_question_overlay.hpp | 88 - src/tui/ask_question_session.cpp | 104 + src/tui/ask_question_session.hpp | 62 + src/tui/ask_question_text.cpp | 108 + src/tui/ask_question_text.hpp | 14 + src/tui/tui_ask_channel.cpp | 115 +- src/tui_state.hpp | 24 + tests/config/config_tui_test.cpp | 46 + tests/tool/ask_overlay_input_test.cpp | 252 --- tests/tool/ask_user_question_tool_test.cpp | 122 +- tests/tui/ask_question_controller_test.cpp | 294 +++ tests/tui/ask_question_editor_test.cpp | 49 + tests/tui/ask_question_layout_test.cpp | 89 + tests/tui/ask_question_overlay_test.cpp | 389 ---- tests/tui/ask_question_session_test.cpp | 72 + tests/tui/ask_question_text_test.cpp | 52 + 39 files changed, 4133 insertions(+), 2630 deletions(-) create mode 100644 openspec/changes/redesign-tui-ask-user-question/.openspec.yaml create mode 100644 openspec/changes/redesign-tui-ask-user-question/design.md create mode 100644 openspec/changes/redesign-tui-ask-user-question/proposal.md create mode 100644 openspec/changes/redesign-tui-ask-user-question/specs/tui-ask-user-question/spec.md create mode 100644 openspec/changes/redesign-tui-ask-user-question/tasks.md delete mode 100644 src/tool/ask_overlay_input.cpp delete mode 100644 src/tool/ask_overlay_input.hpp create mode 100644 src/tool/ask_user_question_types.hpp create mode 100644 src/tui/ask_question_controller.cpp create mode 100644 src/tui/ask_question_controller.hpp create mode 100644 src/tui/ask_question_editor.cpp create mode 100644 src/tui/ask_question_editor.hpp create mode 100644 src/tui/ask_question_layout.cpp create mode 100644 src/tui/ask_question_layout.hpp delete mode 100644 src/tui/ask_question_overlay.cpp delete mode 100644 src/tui/ask_question_overlay.hpp create mode 100644 src/tui/ask_question_session.cpp create mode 100644 src/tui/ask_question_session.hpp create mode 100644 src/tui/ask_question_text.cpp create mode 100644 src/tui/ask_question_text.hpp delete mode 100644 tests/tool/ask_overlay_input_test.cpp create mode 100644 tests/tui/ask_question_controller_test.cpp create mode 100644 tests/tui/ask_question_editor_test.cpp create mode 100644 tests/tui/ask_question_layout_test.cpp delete mode 100644 tests/tui/ask_question_overlay_test.cpp create mode 100644 tests/tui/ask_question_session_test.cpp create mode 100644 tests/tui/ask_question_text_test.cpp diff --git a/AGENTS.md b/AGENTS.md index d007ca31..d6e04a17 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,6 +51,18 @@ Use `testing::TempDir()` or `std::filesystem::temp_directory_path()` for file I/ If CMake test discovery/build integration is unavailable in the editor, still keep changes compatible with the documented `cmake --build` and `ctest` commands. For web-only changes, at minimum run `pnpm test` and `pnpm build` from [web/](web). +## 研发实施中的通用注意事项 + +- 重构或迁移功能时,先明确新旧路径的责任边界,再逐步切换调用入口;如果新旧状态、事件处理或兼容分支同时生效,同一个输入可能被重复处理,问题通常只在特定交互顺序下暴露。 +- 事件驱动程序需要明确每类事件的唯一所有者。键盘、鼠标、定时器、重绘和后台回调应经过统一适配层进入业务状态机,不能只迁移最常见的事件而遗漏边缘输入路径。 +- 业务状态、传输格式和展示文本应分层维护。不要用格式化后的字符串推断状态;空字符串、缺失字段和显式的空值可能代表不同语义,跨线程或跨进程传输时应保留必要的结构化信息。 +- 不要把布局、分页或超时等动态行为写成固定常量。可视区域、终端尺寸、配置值和运行时状态变化后,固定步长或固定边界容易产生越界、跳过内容或无法操作的问题。 +- 将时间、外部 IO、线程调度和平台资源封装在边界上,核心逻辑尽量使用可注入的时钟、输入和依赖。这样既能避免测试永久等待,也能稳定覆盖超时、取消和竞态场景。 +- Windows 增量构建前确认没有残留进程占用输出文件,并加载正确的编译器开发环境;链接错误有时来自文件锁或环境变量缺失,而不是源代码错误。 +- 多步骤任务应采用“小范围修改 → 定向编译/测试 → 再扩大范围”的节奏。遇到失败先判断是代码错误、环境问题、并发进程、缓存还是测试基线问题,不要在未定位原因前反复重试。 +- 修改前后都要检查工作区范围。不要使用会清理或覆盖无关用户文件的命令;提交时精确选择相关文件,并通过 `git diff --check`、差异审查和测试结果确认改动没有夹带无关内容。 +- 非平凡行为变更应同步更新设计文档、任务清单和测试;不要等全部代码完成后才补记录,否则容易遗漏已验证的约束和未完成事项。 + ## Commit & Pull Request Guidelines Recent history uses short imperative commits, sometimes with `feat:` prefixes, for example `feat: Implement AskUserQuestion tool` or `Add unit tests for session serialization`. Keep commits focused and mention tests when relevant. diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d0f1791..729498ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,7 +256,11 @@ set(ACECODE_TESTABLE_TUI_SOURCES ${CMAKE_SOURCE_DIR}/src/markdown/mermaid_renderer.cpp ${CMAKE_SOURCE_DIR}/src/markdown/link_safety.cpp ${CMAKE_SOURCE_DIR}/src/markdown/syntax_highlight.cpp - ${CMAKE_SOURCE_DIR}/src/tui/ask_question_overlay.cpp + ${CMAKE_SOURCE_DIR}/src/tui/ask_question_controller.cpp + ${CMAKE_SOURCE_DIR}/src/tui/ask_question_editor.cpp + ${CMAKE_SOURCE_DIR}/src/tui/ask_question_layout.cpp + ${CMAKE_SOURCE_DIR}/src/tui/ask_question_session.cpp + ${CMAKE_SOURCE_DIR}/src/tui/ask_question_text.cpp ${CMAKE_SOURCE_DIR}/src/tui/chat_file_link.cpp ${CMAKE_SOURCE_DIR}/src/tui/text_truncation.cpp ${CMAKE_SOURCE_DIR}/src/tui/paste_handler.cpp diff --git a/docs/help/configuration.html b/docs/help/configuration.html index 4b0f49a0..9434d5ea 100644 --- a/docs/help/configuration.html +++ b/docs/help/configuration.html @@ -35,18 +35,24 @@

配置文件与生效范围

优先通过界面修改对应设置;需要手动编辑时,先确认数据目录、字段范围和保存方式。

-
本页内容
+
本页内容

配置保存在哪里

个人安装的主配置是 ~/.acecode/config.json,Windows 对应 %USERPROFILE%\.acecode\config.json。模型预设、默认模型以及网络、技能、MCP 等全局选项保存在这套用户配置中。

Windows 服务模式使用 %PROGRAMDATA%\acecode,与个人安装的数据目录分开。连接远端后台时,配置属于远端用户或服务身份;编辑本机文件不会自动修改远端配置。

配置层适合保存的内容
全局配置服务商连接、默认值、扩展连接与运行选项。
工作目录覆盖例如 TUI 用 /model --cwd 保存的项目模型选择。
项目文件项目规则、项目技能和项目 Hooks。
当前任务任务选择的模型、权限与对话上下文。

具体位置见本地配置和数据。复制配置到其他计算机前,检查其中的绝对路径、可执行文件位置和认证信息。

修改与生效

模型页通过保存模型或保存修改提交;个性化文本和 MCP JSON 等控件会在离开编辑区时保存,并显示保存状态。TUI 设置中心的 General、Appearance 开关通常即时保存,配置与模型表单按底部提示使用 Ctrl+S

不同配置有不同生效边界。任务模型和权限使用专门的切换入口;后台连接与运行服务以界面的应用结果为准。出现“重启 daemon 后生效”时,保存正在进行的工作后重启。手动改动任意 JSON 文件,并不等于所有运行中的模块都已经重新加载。

+

TUI 问答配置

TUI 中的 AskUserQuestion 会在选项较多或说明较长时使用可滚动内容区。以下字段位于配置文件的 tui 对象中:

字段默认值有效范围说明
question_min_visible_rows42–12AskUserQuestion 内容区的最小可见行数。内容超出视口后,可使用鼠标滚轮或滚动条查看。
question_selection_feedback_ms2000–1000预设选项提交后保留选中视觉反馈的时长,单位为毫秒。设置为 0 可关闭反馈延迟。

例如:

{
+  "tui": {
+    "question_min_visible_rows": 4,
+    "question_selection_feedback_ms": 200
+  }
+}

这两个字段会在读取配置时限制在有效范围内。超出范围的整数会自动限制到边界,并记录警告;非整数值会被忽略并继续使用默认值。省略字段时使用默认值,配置保存采用稀疏写入,不会强制写出默认值。

手动编辑与错误恢复

  1. 先备份当前有效配置,使用支持 UTF-8 的编辑器打开。
  2. 只修改目标字段,保持正确的 JSON 类型,不加入注释或尾随逗号。
  3. 重新加载相关功能,或按该功能要求重启。
  4. 检查界面实际值和一次小操作,确认修改已生效。

当前版本会保存有效配置快照。配置损坏且存在有效快照时,会备份错误文件并尝试自动恢复;Web/Desktop 会显示一次配置已自动回滚提示。没有可用快照时仍会报告配置错误。按提示查看备份位置并修复目标字段,备份可能含密钥,不要直接公开。

- +
diff --git a/openspec/changes/redesign-tui-ask-user-question/.openspec.yaml b/openspec/changes/redesign-tui-ask-user-question/.openspec.yaml new file mode 100644 index 00000000..2e24cfa4 --- /dev/null +++ b/openspec/changes/redesign-tui-ask-user-question/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-07 diff --git a/openspec/changes/redesign-tui-ask-user-question/design.md b/openspec/changes/redesign-tui-ask-user-question/design.md new file mode 100644 index 00000000..d5687c63 --- /dev/null +++ b/openspec/changes/redesign-tui-ask-user-question/design.md @@ -0,0 +1,143 @@ +# Design: redesign-tui-ask-user-question + +## Context + +当前 TUI 已通过 `ToolContext::ask_user_questions` 接入共享 `AskUserQuestion` 工具;`src/tui/tui_ask_channel.cpp` 负责把 JSON payload 写入 `TuiState`、等待结果并返回 JSON。overlay 的排版和滚动数学集中在 `src/tui/ask_question_overlay.*`,而题目选择、Other 输入、导航、鼠标和提交事件仍散落在 `TuiState` 与 `main.cpp`。现有实现虽有多题、滚动、鼠标和 timeout 基础能力,但模型不统一,且 `Other...` 复用普通 prompt 输入状态。 + +此变更只替换 TUI 适配路径。公开工具参数、共享异步 channel 形状、daemon/Web/Desktop UI 和持久化会话消息不改变。 + +## Goals / Non-Goals + +**Goals:** + +- 将 TUI 问答交互从 `main.cpp` / `TuiState` 中提炼为纯 C++、可单测的深模块。 +- 保持单题快速完成,并为 2–4 题请求提供自动前进、回看和只读汇总提交。 +- 使自定义答案、键盘、鼠标、滚动、超时和选中反馈的优先级明确、非阻塞且可测试。 +- 保持既有 FIFO 请求队列和 `ToolContext::ask_user_questions` 异步边界。 +- 不因 TUI 视觉重设计改变模型可见的成功/取消基本语义。 + +**Non-Goals:** + +- 不修改 `AskUserQuestion` 公开 schema、题目/选项数限制或 `multiSelect` 语义。 +- 不重做 daemon、Web 或 Desktop 问答界面,也不引入跨端新协议。 +- 不把 AskUserQuestion 扩展为纯文本题或通用表单。 +- 不保留旧、新两套 TUI 问答路径的运行时开关。 + +## Architecture + +### D1. 纯模型、布局、FTXUI 适配三层 + +新增集中在 `src/tui/` 的聚焦类型: + +1. **问答控制器**:拥有题目页/汇总页、每题答案变体、焦点、编辑状态、逻辑滚动偏移与语义状态转换;不依赖 FTXUI、时钟、剪贴板或终端坐标。 +2. **UTF-8 行内编辑器**:纯 C++ 核心,拥有文本、字节安全的 codepoint 边界、光标、选区、逐行移动、删除和替换;控制器组合该类型,而不复用普通 composer 状态。 +3. **布局与命中计算**:以只读控制器快照和终端字符单元格尺寸生成可绘制行、双列宽度、折行、视口、滚动条和命中矩形;不改变控制器状态。 +4. **FTXUI 适配器**:将 FTXUI 键盘/鼠标/滚轮/resize/tick 规范化为领域事件,调用控制器,执行语义效果(channel 回调、剪贴板、重绘、toast 和 deadline 调度),并把布局快照绘制为 FTXUI 元素。 + +`main.cpp` 仅保留 active overlay 的路由、FTXUI event loop 接线和渲染挂接,不再拥有问答业务状态机。 + +### D2. 问答会话对象与 channel 适配 + +每个 active 请求由一个 TUI 问答会话对象承载: + +- 不可变请求:解析后的问题、子任务来源展示文本、已校验配置; +- 控制器; +- 可选固定 timeout deadline; +- 可选预设项选中反馈 deadline; +- 双击适配器状态(同一命中区域和 500ms 窗口); +- 临时 toast 状态。 + +`ask_via_tui_overlay` 继续是唯一阻塞适配器:它入队并等待会话产生结构化完成效果,再按既有 JSON response 契约返回 `{cancelled, timed_out, answers}`。FIFO 仍由 TUI overlay 占用协调保证,同一时间只激活一个会话。会话完成、取消或超时后销毁,随后激活下一个请求。 + +控制器不直接调用 channel 或系统服务。它消费领域事件并产生效果,例如 `RequestRedraw`、`SubmitCurrent`、`Complete`、`Cancel`、`CopyText`、`ShowToast`、`StartSelectionFeedback`。适配器执行效果并在必要时向控制器反馈,如剪贴板成功/失败或 deadline 到期。 + +### D3. 显式答案状态与结果映射 + +每题使用明确的答案变体而非“索引 + 若干布尔值”推断: + +- `NotAnswered`; +- 预设选择集合(保持显示顺序); +- 自定义草稿及其是否激活; +- timeout 自动选择标记。 + +单选题的有效答案始终只有一个:激活自定义答案会清除预设;选择预设会取消自定义激活但保留草稿。多选题的预设集合与激活的非空自定义补充可共存。提交时按显示顺序输出预设 label,最后追加有效自定义文本;没有有效值则映射为 `Not answered`。 + +会话完成后,TUI 适配器构造现有 response JSON。控制器返回的“用户取消”不伪装为空答案,适配器继续映射到既有失败路径及明确取消文案。普通、未作答和 timeout 自动选择的结构化信息只在 TUI 本地结果 metadata/转录适配中使用,由适配器生成紧凑 Q/A 卡片;控制器不格式化转录或写持久化。 + +### D4. 事件优先级、编辑与输入 + +适配层将原始事件转换为:导航、选择、提交、字符输入、编辑命令、复制、滚动、点击、双击、拖选、resize、feedback-deadline、timeout-deadline 等领域事件。 + +控制器按当前模式处理: + +- **预设项焦点**:单选/多选的 Space、Enter 与数字遵循需求文档;预设项上的普通字符进入自定义编辑。`j/k` 导航,`y` 请求复制 `