refactor: update "俺想想" to "让俺想想" across various components and documentation for consistency

This commit is contained in:
hy committed 2026-09-09 15:27:53 +08:00
1 parent 11e7508db9
commit 851590b845
10 files changed
+46 -38

No files matched your search

+7 -6
View File
@@ -242,15 +242,16 @@
#### WB-SESSION-011 清空消息记录
- 系统行为:一次 AI run 正常完成后,Composer 上方出现「清空消息记录」胶囊(与消息操作胶囊同壳,内含橡皮/擦除图标,不用火花或垃圾桶)。整颗主操作 hover 提示:`清空「{伙伴名}」的全部消息记录,无法恢复。`。点击后先弹出确认框(标题「清空消息记录?」,正文说明将删除该伙伴全部对话消息且无法恢复、伙伴本身保留;主按钮「清空」为危险态)。确认后物理删除该伙伴 `chat_session_message_log` 全部行及对应 `ai_run`;**不删除** `ai_file_change`、伙伴本身、Composer 草稿、右侧标签与浏览器标签。清空后主区消息列表为空。伙伴仍在运行时不允许清空。
- 系统行为:当前伙伴存在消息记录、且不在 running / waiting_approval 时,Composer 上方显示「清空消息记录」胶囊(与消息操作胶囊同壳,内含橡皮/擦除图标,不用火花或垃圾桶)。整颗主操作 hover 提示:`清空「{伙伴名}」的全部消息记录,无法恢复。`。点击后先弹出确认框(标题「清空消息记录?」,正文说明将删除该伙伴全部对话消息且无法恢复、伙伴本身保留;主按钮「清空」为危险态)。确认后物理删除该伙伴 `chat_session_message_log` 全部行及对应 `ai_run`;**不删除** `ai_file_change`、伙伴本身、Composer 草稿、右侧标签与浏览器标签。清空后主区消息列表为空,胶囊随之消失。伙伴仍在运行或等待审批时不显示清空入口。
- 验收:
- `WB-SESSION-011-AC1` 确认清空后该伙伴消息列表为空,不再保留旧气泡。
- `WB-SESSION-011-AC2` 清空后仍停留在同一伙伴;Composer 草稿与右侧标签保持不变。
- `WB-SESSION-011-AC3` 清空后下一次发送不再把旧对话作为模型上下文。
- `WB-SESSION-011-AC4` 当前伙伴存在 running / waiting_approval 时,清空不可用。
- `WB-SESSION-011-AC4` 当前伙伴存在 running / waiting_approval 时,清空入口不显示。
- `WB-SESSION-011-AC5` 胶囊文案为「清空消息记录」;hover tooltip 含伙伴名且说明无法恢复。
- `WB-SESSION-011-AC6` 点击胶囊先出现确认框;取消确认不删除任何消息。
- `WB-SESSION-011-AC7` 清空不删除该伙伴的 `ai_file_change` 文件回退记录。
- `WB-SESSION-011-AC8` 伙伴有消息且空闲时显示清空入口;切换伙伴或重启后,只要仍有消息且空闲仍应显示。
#### WB-SESSION-012 编辑伙伴
@@ -293,9 +294,9 @@
- 验收:
- `WB-COMPOSER-003-AC1` 智能模式发送后,主区先可见用户消息与 assistant 占位消息。
- `WB-COMPOSER-003-AC2` AI 运行成功启动后,assistant 过程流通过事件逐步追加更新,而不是一次性静态替换。
- `WB-COMPOSER-003-AC3` assistant 在 Brain 思考阶段于过程流最下方展示“俺想想”并带有微光流动视觉反馈。当思考请求发起时在消息最下方展示;当收到输出、思考结束、出现“正在重试”过程组、已有可见流式 Thought / 最终回答、或 run 已结束(完成 / 失败 / 取消)时,必须隐藏“俺想想”。
- `WB-COMPOSER-003-AC3` assistant 在 Brain 思考阶段于过程流最下方展示“让俺想想”并带有微光流动视觉反馈。运行未结束且尚无最终回答时预留同高占位,避免文案显隐导致消息区上挤抖动。当思考请求发起时展示文案;当收到输出、思考结束、出现“正在重试”过程组、已有可见流式 Thought / 最终回答、或 run 已结束(完成 / 失败 / 取消)时,必须隐藏“让俺想想”。
- `WB-COMPOSER-003-AC4` assistant turn 中 brain 最终输出始终位于过程流下方,并按 markdown 渲染;最终回答在内容分片到达后直接流式渲染,不展示独立收尾等待层;内部文件链接打开右侧文件预览,网页链接打开右侧内置浏览器;若当前伙伴已有浏览器 Tab 正停留在同一 URL,则激活该 Tab,不新开。
- `WB-COMPOSER-003-AC5` 决策层(Thought)只展示面向用户的短摘要,不得展示模型原始长篇推理;决策层出现时,当前“俺想想”必须立即隐藏;已落地的 Thought 使用浅灰色(#787774)文本呈现思考状态;未超出单行时不展示折叠图标,超出单行时默认以单行省略号截断展示且不常驻图标,鼠标悬浮时显示展开图标,点击展开后常驻浅灰卡片背景并展示收起图标,展开内容受最大高度限制(超出纵向滚动),流式阶段平铺展开。
- `WB-COMPOSER-003-AC5` 决策层(Thought)只展示面向用户的短摘要,不得展示模型原始长篇推理;决策层出现时,当前“让俺想想”必须立即隐藏;已落地的 Thought 使用浅灰色(#787774)文本呈现思考状态;未超出单行时不展示折叠图标,超出单行时默认以单行省略号截断展示且不常驻图标,鼠标悬浮时显示展开图标,点击展开后常驻浅灰卡片背景并展示收起图标,展开内容受最大高度限制(超出纵向滚动),流式阶段平铺展开。
- `WB-COMPOSER-003-AC6` 过程流使用用户可理解的文案,不直接暴露内部 tool 名或底层事件名;两次 Thought 之间的工具调用(含 `learn_skill`)合并为一个 Activity 面板,展开后按时间平铺自然语言说明;压缩与重试单独成行。Activity header 汇总规则见 `WB-COMPOSER-009-AC5`;等待审批时的 header 与审后工具行图标见 `WB-BLOCKER-001-AC6` / `WB-BLOCKER-001-AC7`。
- `WB-COMPOSER-003-AC7` AI 输出中切到 Settings 或触发界面重新挂载后,回到 Workbench 能通过伙伴消息记录投影恢复过程流、partial answer、最终回答或等待审批状态。
- `WB-COMPOSER-003-AC8` brain 在最终交付 markdown 中使用标准 Markdown 链接引用资料(名称 + 绝对路径或 https URL);最终交付生成后、回传界面前,后台**仅对本地文件**链接地址并行去重检查并写入交付 `linkAccess` sidecar(不改写正文)。可访问的本地文件链接保留文件预览交互;不可访问或无法识别的本地文件链接不得删除,必须使用强调色降低透明度的灰色弱化文本,悬浮提示「链接无法访问」,且不可点击。`http(s)` 网页链接不做可达性检查、不写入可达性 sidecar,交付前后均默认可点并打开内置浏览器。界面不把裸 URL / `www.…` 自动识别为链接,也不对其套用 `linkAccess` 弱化态;流式阶段尚无 sidecar 时本地文件链接可先保持可点,交付落库后按 sidecar 更新。界面渲染和用户点击时均不再次检查可访问性。渲染侧匹配 sidecar 前须对 href 做 percent-decoding / 路径规范化,避免中文等非 ASCII 路径因编码差异误判不可达。
@@ -352,7 +353,7 @@
#### WB-COMPOSER-009 消息流布局
- 系统行为:workbench 消息区不展示用户与 assistant 头像;assistant 回复以左对齐内容流展示,过程流没有独立标题或左侧竖线,且与最终 brain 输出共用消息 rail;最终 markdown 输出直接位于内容流中;用户消息使用右对齐紧凑气泡。无消息时输入区位于主工作区中部;存在消息后输入区固定在主区底部,只允许消息区独立滚动,右侧面板展开或折叠不得改变输入区定位。过程流按真实时间展示决策层与过程层:思考层“俺想想”是 Brain 请求阶段的临时等待状态,位于过程区最下方展示,请求时出现、结束时隐藏;决策层(Thought)使用浅灰色文本,未超出单行时不显示折叠图标,超出单行时默认单行省略且鼠标 hover 显示展开图标,展开后常驻浅灰卡片背景并支持内部滚动;两次 Thought 之间的全部工具调用(含 `learn_skill`)合并为一个 Activity 面板(默认折叠,hover 显示展开箭头),header 与展开行规则见 `WB-COMPOSER-009-AC5` / `WB-COMPOSER-009-AC6`;压缩层、重试层和决策层累计保留,压缩与重试单独成行、不并入 Activity。
- 系统行为:workbench 消息区不展示用户与 assistant 头像;assistant 回复以左对齐内容流展示,过程流没有独立标题或左侧竖线,且与最终 brain 输出共用消息 rail;最终 markdown 输出直接位于内容流中;用户消息使用右对齐紧凑气泡。无消息时输入区位于主工作区中部;存在消息后输入区固定在主区底部,只允许消息区独立滚动,右侧面板展开或折叠不得改变输入区定位。过程流按真实时间展示决策层与过程层:思考层“让俺想想”是 Brain 请求阶段的临时等待状态,位于过程区最下方展示,请求时出现、结束时隐藏;决策层(Thought)使用浅灰色文本,未超出单行时不显示折叠图标,超出单行时默认单行省略且鼠标 hover 显示展开图标,展开后常驻浅灰卡片背景并支持内部滚动;两次 Thought 之间的全部工具调用(含 `learn_skill`)合并为一个 Activity 面板(默认折叠,hover 显示展开箭头),header 与展开行规则见 `WB-COMPOSER-009-AC5` / `WB-COMPOSER-009-AC6`;压缩层、重试层和决策层累计保留,压缩与重试单独成行、不并入 Activity。
- 验收:
- `WB-COMPOSER-009-AC1` 用户与 assistant 消息不展示头像或头像占位。
- `WB-COMPOSER-009-AC2` assistant 过程流和最终输出左边缘与底部 composer 内容 rail 对齐。
@@ -363,7 +364,7 @@
- `WB-COMPOSER-009-AC7` 文件名、query、命令、URL 和技能名不使用粗体或冒号强调,保持正常字重;可以通过文字颜色表达层级。
- `WB-COMPOSER-009-AC8` `learn_skill` 作为普通工具行展示,使用技能通用名称,例如 `docx 阅读器`、`docx 编辑器`、`资料查阅器`、`命令执行器`;运行中展示“正在学习 xx”,完成后展示“已学会 xx”,不再作为独立学习层。
- `WB-COMPOSER-009-AC9` Activity header、压缩层和重试层展示对应图标;浏览器各动作(打开网页使用地球图标、查看页面使用眼睛图标、操作页面使用指针图标、提取内容使用清单图标、截取页面使用相机图标、滚动页面使用上下箭头图标、列出标签使用窗口图标)、网页抓取使用下载图标、网页搜索使用放大镜图标、读取图片与生成图片使用照片相框图标(截取页面并读取图片的视觉成对动作统一使用照片图标,不退化为闪电)、知识库、文件、终端和学习使用独立语义图标;其他混合动作或混合工具时使用工作流闪电图标。展开后的每条工具句子不展示图标,也不渲染前置小圆点或“AI 思路”类图标。
- `WB-COMPOSER-009-AC10` 决策层、Activity、压缩层、重试层按事件时间顺序累计展示;决策层采用浅灰色文本,未超出单行时不显示折叠图标,超出单行时单行截断省略,折叠状态展开图标在 hover 时显示且垂直居中于单行文本,展开时常驻浅灰卡片背景、折叠按钮居顶对齐第一行文本并在最大高度内纵向滚动,Activity 默认折叠;不存在独立学习层或最终收尾等待层;Brain 请求期间在消息流最下方展示“俺想想”。
- `WB-COMPOSER-009-AC10` 决策层、Activity、压缩层、重试层按事件时间顺序累计展示;决策层采用浅灰色文本,未超出单行时不显示折叠图标,超出单行时单行截断省略,折叠状态展开图标在 hover 时显示且垂直居中于单行文本,展开时常驻浅灰卡片背景、折叠按钮居顶对齐第一行文本并在最大高度内纵向滚动,Activity 默认折叠;不存在独立学习层或最终收尾等待层;Brain 请求期间在消息流最下方展示“让俺想想”。
- `WB-COMPOSER-009-AC11` 无消息时输入区保持居中;存在消息后消息区为唯一纵向滚动容器,输入区固定在主区底部且不被消息内容向下推出窗口。
- `WB-COMPOSER-009-AC12` “正在 / 已检索知识库”只来自 AI 实际调用知识库工具;新建干净智能对话、仅切换伙伴时不得凭空出现该过程文案。
+4 -4
View File
@@ -282,10 +282,10 @@ tests/case/workbench/
#### `context-clear-capsule`
- 测试类型:`e2e`
- What:run 完成后出现「清空消息记录」胶囊;hover tooltip 含伙伴名与无法恢复说明;点击先确认,取消不删;确认后消息列表为空,仍停留在同一伙伴,草稿不变。
- Why:用户需要可理解的「清空对话」动作:确认后消息列表为空,下次发送不再带旧上下文。
- What:伙伴有消息且空闲时出现「清空消息记录」胶囊;hover tooltip 含伙伴名与无法恢复说明;点击先确认,取消不删;确认后消息列表为空,仍停留在同一伙伴,草稿不变。
- Why:用户需要可理解的「清空对话」动作:确认后消息列表为空,下次发送不再带旧上下文;切换伙伴后只要仍有消息也应能清空。
- How:创建伙伴并完成一次真实 AI run;检查胶囊文案与 tooltip;点击胶囊后取消确认并断言消息仍在;再点胶囊并确认清空;断言消息为空、仍在同一伙伴、输入草稿不变。运行中不得出现清空入口。
- Oracle:确认后列表为空对齐 `WB-SESSION-011-AC1`;不换伙伴 / 草稿对齐 `WB-SESSION-011-AC2`;下次发送无旧上下文对齐 `WB-SESSION-011-AC3`(由清空后 message log 为空保证);运行中不可用对齐 `WB-SESSION-011-AC4`(本 case Not,沿用既有运行态约束);文案与 tooltip 对齐 `WB-SESSION-011-AC5`;确认框取消/确认对齐 `WB-SESSION-011-AC6`。
- Oracle:有消息且空闲时可见对齐 `WB-SESSION-011-AC8`;确认后列表为空对齐 `WB-SESSION-011-AC1`;不换伙伴 / 草稿对齐 `WB-SESSION-011-AC2`;下次发送无旧上下文对齐 `WB-SESSION-011-AC3`(由清空后 message log 为空保证);运行中不可用对齐 `WB-SESSION-011-AC4`;文案与 tooltip 对齐 `WB-SESSION-011-AC5`;确认框取消/确认对齐 `WB-SESSION-011-AC6`。
- Not:不验证 compaction;不在本 case 制造文件回退记录(`WB-SESSION-011-AC7` 由 unit `clear-partner-context-deletes-messages-and-runs-keeps-file-changes` 覆盖)。
### 5.2 `run`
@@ -296,7 +296,7 @@ tests/case/workbench/
- What:智能模式真实发送后,验证 Thought / Activity 过程层渲染与最终 markdown 收口(过程流主 case)。
- Why:重构后过程层是用户扫进度的主界面;需覆盖 Activity 折叠 header、展开平铺、学习/工具统一渲染,以及最终回答,而不是只断言“有过程块”。
- How:真实文件地址内准备带唯一事实的文件;先创建伙伴;用户通过 UI 发送自然语言总结请求;等待真实 AI run 完成;展开全部 Activity 面板并采集 header / body 文案。
- Oracle:用户消息与 assistant 过程流逐步出现,对齐 `WB-COMPOSER-003-AC1`、`WB-COMPOSER-003-AC2`;用户与 assistant 消息不展示头像,用户消息右对齐气泡,assistant 过程流/最终输出与 composer 内容 rail 对齐,最终 markdown 无独立背景卡片,对齐 `WB-COMPOSER-009-AC1`、`WB-COMPOSER-009-AC2`、`WB-COMPOSER-009-AC3`、`WB-COMPOSER-009-AC4`。运行中过程流可见,且“正在重试”与“俺想想”不同时出现;“俺想想”在 Brain 请求期间于过程区最下方展示,run 完成 / 失败 / 取消或产出正文后过程区不得仍挂着“俺想想”,对齐 `WB-COMPOSER-003-AC3`。决策层(Thought)短摘要可见且不暴露原始长推理,以浅灰文本展示,未超出单行时不展示折叠图标,超出单行时单行省略,hover 时展示展开图标且垂直居中于单行文本,展开后常驻浅灰卡片背景、折叠按钮居顶对齐第一行并在内部滚动,对齐 `WB-COMPOSER-003-AC5`、`WB-COMPOSER-009-AC10`。若本轮确有可见工具调用:至少一块 Activity(`details` + summary + 展开行);summary 为工具独立维护的 header 短句汇总(可独立成句,不携带命令参数/路径/query,不得出现展开行半截连词如「查看页面并」),根据单一动作或混合工具精准呈现对应专属语义图标(浏览器打开/查看/操作/提取/截图/滚动/标签、网页抓取/搜索、图片读取/生成或工作流闪电图标;截取页面并读取图片的成对视觉动作统一呈现为照片图标,不退化为闪电;浏览器操作不产生审批卡片与展开行审核绿勾);展开行为带对象的工具原句且不含冒号强调;两次 Thought 之间的工具合并为 Activity,对齐 `WB-COMPOSER-003-AC6`、`WB-COMPOSER-009-AC5`、`WB-COMPOSER-009-AC6`、`WB-COMPOSER-014-AC5`、`WB-BLOCKER-001-AC8`。Header:按工具 UI 类型归并;同类多次为「短句 N 次」;归并后 ≤3 类用「并/但」拼接;>3 类 summary 为「已完成 n 项」。若事件含 `learn_skill`:展开行文案含“正在学习 / 已学会”,且不作为独立学习层;若含 docx 工具:展开行文案含“Word 文档”;对齐 `WB-COMPOSER-009-AC8`、`WB-COMPOSER-009-AC9`、`WB-COMPOSER-009-AC10`。文件名、query、命令、URL 和技能名不使用粗体或冒号强调,对齐 `WB-COMPOSER-009-AC7`。过程区不出现最终收尾等待文案(如“让俺收尾”),最终回答直接流式收口,对齐 `WB-COMPOSER-003-AC4`、`WB-COMPOSER-014-AC1`、`WB-COMPOSER-014-AC2`。证据引用与 terminal 状态规则仍由同域其他 case / AI suite 覆盖,本 case 保持 `WB-COMPOSER-003-AC8`、`WB-COMPOSER-003-AC9` 的过程流兼容边界(不因过程层重构破坏)。自动重试与截断提示、计划进度环等由相关运行态 case 覆盖并继续对齐 `WB-COMPOSER-014-AC3`、`WB-COMPOSER-014-AC4`。最终 markdown 围绕工作区事实(Alice/Bob)且不裸露 chain-of-thought,对齐 `WB-CORE-005`、`WB-CORE-006`。仅切换伙伴或新建干净对话时不得凭空出现“正在 / 已检索知识库”,对齐 `WB-COMPOSER-009-AC12`。
- Oracle:用户消息与 assistant 过程流逐步出现,对齐 `WB-COMPOSER-003-AC1`、`WB-COMPOSER-003-AC2`;用户与 assistant 消息不展示头像,用户消息右对齐气泡,assistant 过程流/最终输出与 composer 内容 rail 对齐,最终 markdown 无独立背景卡片,对齐 `WB-COMPOSER-009-AC1`、`WB-COMPOSER-009-AC2`、`WB-COMPOSER-009-AC3`、`WB-COMPOSER-009-AC4`。运行中过程流可见,且“正在重试”与“让俺想想”不同时出现;“让俺想想”在 Brain 请求期间于过程区最下方展示,run 完成 / 失败 / 取消或产出正文后过程区不得仍挂着“让俺想想”,对齐 `WB-COMPOSER-003-AC3`。决策层(Thought)短摘要可见且不暴露原始长推理,以浅灰文本展示,未超出单行时不展示折叠图标,超出单行时单行省略,hover 时展示展开图标且垂直居中于单行文本,展开后常驻浅灰卡片背景、折叠按钮居顶对齐第一行并在内部滚动,对齐 `WB-COMPOSER-003-AC5`、`WB-COMPOSER-009-AC10`。若本轮确有可见工具调用:至少一块 Activity(`details` + summary + 展开行);summary 为工具独立维护的 header 短句汇总(可独立成句,不携带命令参数/路径/query,不得出现展开行半截连词如「查看页面并」),根据单一动作或混合工具精准呈现对应专属语义图标(浏览器打开/查看/操作/提取/截图/滚动/标签、网页抓取/搜索、图片读取/生成或工作流闪电图标;截取页面并读取图片的成对视觉动作统一呈现为照片图标,不退化为闪电;浏览器操作不产生审批卡片与展开行审核绿勾);展开行为带对象的工具原句且不含冒号强调;两次 Thought 之间的工具合并为 Activity,对齐 `WB-COMPOSER-003-AC6`、`WB-COMPOSER-009-AC5`、`WB-COMPOSER-009-AC6`、`WB-COMPOSER-014-AC5`、`WB-BLOCKER-001-AC8`。Header:按工具 UI 类型归并;同类多次为「短句 N 次」;归并后 ≤3 类用「并/但」拼接;>3 类 summary 为「已完成 n 项」。若事件含 `learn_skill`:展开行文案含“正在学习 / 已学会”,且不作为独立学习层;若含 docx 工具:展开行文案含“Word 文档”;对齐 `WB-COMPOSER-009-AC8`、`WB-COMPOSER-009-AC9`、`WB-COMPOSER-009-AC10`。文件名、query、命令、URL 和技能名不使用粗体或冒号强调,对齐 `WB-COMPOSER-009-AC7`。过程区不出现最终收尾等待文案(如“让俺收尾”),最终回答直接流式收口,对齐 `WB-COMPOSER-003-AC4`、`WB-COMPOSER-014-AC1`、`WB-COMPOSER-014-AC2`。证据引用与 terminal 状态规则仍由同域其他 case / AI suite 覆盖,本 case 保持 `WB-COMPOSER-003-AC8`、`WB-COMPOSER-003-AC9` 的过程流兼容边界(不因过程层重构破坏)。自动重试与截断提示、计划进度环等由相关运行态 case 覆盖并继续对齐 `WB-COMPOSER-014-AC3`、`WB-COMPOSER-014-AC4`。最终 markdown 围绕工作区事实(Alice/Bob)且不裸露 chain-of-thought,对齐 `WB-CORE-005`、`WB-CORE-006`。仅切换伙伴或新建干净对话时不得凭空出现“正在 / 已检索知识库”,对齐 `WB-COMPOSER-009-AC12`。
- Not:不对最终文案逐字断言;不把真实模型是否调用某工具误归因为 UI bug(工具相关断言均以本轮实际事件为条件)。
#### `terminal-background-service-flow`
+1 -1
View File
@@ -49,4 +49,4 @@ Windows 与 macOS 使用不同的安全留白比例。Windows ICO 包含 16、20
这版 logo 的核心是“憨厚可靠的工作伙伴”。
黑色圆形是稳定底座,代表工具的可靠性。帽檐和思考点轻微跳出圆形,表达产品里“俺想想”的气质:不是冷冰冰地正在处理,而是像一个会帮用户琢磨办法的伙伴。
黑色圆形是稳定底座,代表工具的可靠性。帽檐和思考点轻微跳出圆形,表达产品里“让俺想想”的气质:不是冷冰冰地正在处理,而是像一个会帮用户琢磨办法的伙伴。
@@ -135,7 +135,7 @@ export const createBrainNode = (input: {
const classifyStreamAsThought = (): void => {
if (streamClassifiedAsThought) return
streamClassifiedAsThought = true
// Process-layer "俺想想" closes as soon as brain produces visible output.
// Process-layer "让俺想想" closes as soon as brain produces visible output.
// Log annotation thinking_completed stays until the round finishes (thought projection).
completeThinking()
input.ctx.sendEvent({
@@ -557,6 +557,10 @@ const AssistantTurnCard: React.FC<AssistantTurnCardProps> = ({
runFinished: Boolean(turn?.runFinished),
hasFinalAnswer
})
/** 运行未结束且尚无最终回答时预留固定行高,避免「让俺想想」显隐把页面顶抖 */
const reserveThinkingSlot =
showThinkingIndicator ||
(!Boolean(turn?.runFinished) && !Boolean(turn?.waitingApproval) && !hasFinalAnswer)
const processGroups = useMemo(
() => (turn?.processGroups || []).filter((group) => group.kind !== 'thinking'),
[turn?.processGroups]
@@ -586,7 +590,7 @@ const AssistantTurnCard: React.FC<AssistantTurnCardProps> = ({
return (
<div className="w-full" data-testid={`workbench-assistant-turn-${messageId}`}>
<div className="space-y-4">
{timeline.length > 0 || hasStreamingThought || showThinkingIndicator ? (
{timeline.length > 0 || hasStreamingThought || reserveThinkingSlot ? (
<div
className={`transition-opacity duration-200 ${
turn.status === 'completed' && hasFinalAnswer ? 'opacity-75' : ''
@@ -624,14 +628,17 @@ const AssistantTurnCard: React.FC<AssistantTurnCardProps> = ({
<MarkdownRenderer content={streamingMarkdown} variant="thought" />
</div>
) : null}
{showThinkingIndicator ? (
{reserveThinkingSlot ? (
<div
className="flex items-center gap-1.5 text-[13px] leading-6 text-[#787774]"
className="flex h-6 items-center gap-1.5 text-[13px] leading-6 text-[#787774]"
data-testid={`${workbenchSelectors.assistantProcessItemPrefix}-${messageId}-thinking`}
aria-hidden={!showThinkingIndicator}
>
<span className="workbench-thinking-shimmer font-medium text-[#37352F]">
俺想想
</span>
{showThinkingIndicator ? (
<span className="workbench-thinking-shimmer font-medium text-[#37352F]">
让俺想想
</span>
) : null}
</div>
) : null}
</div>
@@ -425,7 +425,7 @@ const getDisplayGroupText = (
status: AssistantProcessStatus
): AssistantRichTextSegment[] => {
if (display.kind === 'thinking') {
if (status === 'running') return sentence(text('俺想想'))
if (status === 'running') return sentence(text('让俺想想'))
return []
}
if (display.kind === 'retry') {
@@ -639,7 +639,7 @@ export const createAssistantThinkingGroup = (messageId: string): AssistantProces
skillKey: 'generic',
iconKind: 'default',
status: 'running',
runningText: sentence(text('俺想想')),
runningText: sentence(text('让俺想想')),
completedText: [],
failedText: [],
partialFailedText: [],
+9 -9
View File
@@ -375,7 +375,7 @@ const reduceAssistantTurnByRunEvent = (
}
if (event.type === 'run_warning') {
// 告警不停跑:仅结束「俺想想」,不把 turn 标 failed。
// 告警不停跑:仅结束「让俺想想」,不把 turn 标 failed。
next = { ...next, isBrainThinking: false }
}
@@ -771,7 +771,6 @@ const ChatPage: React.FC = () => {
const [createPartnerShape, setCreatePartnerShape] = useState<PartnerAvatarShape>('circle')
const [createPartnerColor, setCreatePartnerColor] = useState<PartnerAvatarColor>('blue')
const [createPartnerSubmitting, setCreatePartnerSubmitting] = useState(false)
const [clearContextVisible, setClearContextVisible] = useState(false)
const [busyCancelTask, setBusyCancelTask] = useState<ScheduledTask | null>(null)
const [activeTimelineDayStart, setActiveTimelineDayStart] = useState<number | null>(null)
const [composerAiPreference, setComposerAiPreference] = useState<WorkbenchAiPreference>('default')
@@ -1282,6 +1281,14 @@ const ChatPage: React.FC = () => {
() => [...currentMessages].reverse().find((item) => item.role === 'user')?.id || null,
[currentMessages]
)
const clearContextVisible = useMemo(
() =>
Boolean(currentPartnerId) &&
currentMessages.length > 0 &&
!aiRunId &&
!pendingApprovalBatch,
[aiRunId, currentMessages.length, currentPartnerId, pendingApprovalBatch]
)
const userMessageTimeIds = useMemo(
() => collectUserMessageTimeIds(currentMessages),
[currentMessages]
@@ -1878,7 +1885,6 @@ const ChatPage: React.FC = () => {
const opened = await window.api.partnerOpen({ partnerId, limit: 50 })
if (partnerSwitchRequestRef.current !== requestId) return
applyPartnerUiState(partnerId, opened.uiState || null)
setClearContextVisible(false)
setActiveTimelineDayStart(null)
pendingTimelineJumpDayRef.current = null
const restoredActiveTab = opened.uiState?.panel.tabs.find(
@@ -2776,7 +2782,6 @@ const ChatPage: React.FC = () => {
setAssistantTurns({})
setAiRunId(null)
setLoading(false)
setClearContextVisible(false)
void window.api.workbenchUiStateSave({ activeViewKey: null })
}
}
@@ -3241,9 +3246,6 @@ const ChatPage: React.FC = () => {
})
}
void refreshContextUsage(eventPartnerId || currentPartnerIdRef.current)
if (eventPartnerId === currentPartnerIdRef.current) {
setClearContextVisible(true)
}
}
})
return () => {
@@ -3394,7 +3396,6 @@ const ChatPage: React.FC = () => {
setCancellingRun(false)
setPendingApprovalBatch(null)
setApprovalSubmitting(false)
setClearContextVisible(false)
lastSubmittedInputRef.current = userQuery
setPartners((items) =>
items.map((item) =>
@@ -3506,7 +3507,6 @@ const ChatPage: React.FC = () => {
}
setCurrentMessages([])
setHasMoreMessagesBefore(false)
setClearContextVisible(false)
setActiveTimelineDayStart(null)
void loadPartners()
} catch (error) {
+1 -1
View File
@@ -22,7 +22,7 @@ export type AssistantThinkingIndicatorInput = {
}
/**
* “俺想想”展示判断规则:
* “让俺想想”展示判断规则:
* - 请求时展示(isBrainThinking 为 true);
* - 结束时隐藏(收到输出、出错、run 结束、等待审批或已产出最终交付时隐藏)。
*/
@@ -314,8 +314,8 @@ describe('workbench conversation e2e', () => {
result.runningEditButtons === 0 &&
result.messageAttachmentTexts.some((text) => text.includes('message-note.md')) &&
result.messageAttachmentTexts.some((text) => text.includes('MD')) &&
result.assistantProcessTextAfterSend.includes('俺想想') &&
(result.assistantProcessTextAfterWait.includes('俺想想') ||
result.assistantProcessTextAfterSend.includes('让俺想想') &&
(result.assistantProcessTextAfterWait.includes('让俺想想') ||
result.assistantProcessTextAfterWait.includes('处理中')) &&
result.interruptedRunCancelled &&
result.attachmentsAboveBubble,
@@ -136,10 +136,10 @@ const sampleThinkingWaitEarly = async (
): Promise<{ sawThinkingWait: boolean; processTextDuringRun: string }> => {
const deadline = Date.now() + timeoutMs
let processTextDuringRun = ((await processLocator.textContent()) || '').trim()
let sawThinkingWait = /俺想想/u.test(processTextDuringRun)
let sawThinkingWait = /让俺想想/u.test(processTextDuringRun)
while (Date.now() < deadline && !sawThinkingWait) {
processTextDuringRun = ((await processLocator.textContent()) || '').trim()
if (/俺想想/u.test(processTextDuringRun)) {
if (/让俺想想/u.test(processTextDuringRun)) {
sawThinkingWait = true
break
}
@@ -149,7 +149,7 @@ const sampleThinkingWaitEarly = async (
}
if (!sawThinkingWait) {
processTextDuringRun = ((await processLocator.textContent()) || '').trim()
sawThinkingWait = /俺想想/u.test(processTextDuringRun)
sawThinkingWait = /让俺想想/u.test(processTextDuringRun)
}
return { sawThinkingWait, processTextDuringRun }
}
@@ -179,7 +179,7 @@ describe('workbench run e2e', () => {
const earlySample = await sampleThinkingWaitEarly(page, processLocator)
const retryAndThinkingCoexist =
/正在重试/u.test(earlySample.processTextDuringRun) &&
/俺想想/u.test(earlySample.processTextDuringRun)
/让俺想想/u.test(earlySample.processTextDuringRun)
const evidence = await waitForWorkbenchAiRunCompletion(
runtime,
startedAtMs,
@@ -195,7 +195,7 @@ describe('workbench run e2e', () => {
sawThinkingWait: earlySample.sawThinkingWait,
processTextDuringRun: earlySample.processTextDuringRun,
retryAndThinkingCoexist,
thinkingStuckAfterComplete: /俺想想/u.test(processRender.processRootText),
thinkingStuckAfterComplete: /让俺想想/u.test(processRender.processRootText),
processRender,
visibleToolNames,
finalAnswerText,
@@ -260,7 +260,7 @@ describe('workbench run e2e', () => {
{
name: 'workbench-ai-process-activity-render-real',
message:
'真实 AI run 应渲染 Thought/Activity 过程层(header 同类归并/并但/>3 类已完成 n 项、展开平铺、无收尾等待层、俺想想不卡在完成后),并产出围绕工作区事实的最终 markdown,对齐 WB-COMPOSER-003、WB-COMPOSER-009、WB-COMPOSER-014、WB-CORE-005、WB-CORE-006。',
'真实 AI run 应渲染 Thought/Activity 过程层(header 同类归并/并但/>3 类已完成 n 项、展开平铺、无收尾等待层、让俺想想不卡在完成后),并产出围绕工作区事实的最终 markdown,对齐 WB-COMPOSER-003、WB-COMPOSER-009、WB-COMPOSER-014、WB-CORE-005、WB-CORE-006。',
data: result
}
)