Skip to content

子任务节点动画系统 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 引言
  2. 项目结构定位
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与可访问性
  8. 故障排查指南
  9. 结论

引言 ​

本文件聚焦“子任务节点动画系统”,即 Chrome 扩展侧边栏中,子任务执行时如何展示“当前正在执行的节点”、如何区分成功/失败/部分完成状态,以及如何在取消或完成后让动效正确停止。该系统横跨三个层面:

  • 后台执行层:负责子任务计划、节点执行、进度回写与超时控制。
  • 界面渲染层:负责把子任务清单、节点列表、当前执行节点、统计按钮等渲染到 DOM。
  • 样式与动效层:通过 CSS 变量 Token 体系实现呼吸底、左侧光条、旋转环、图标脉冲等动效,并保证在取消/完成后自动静止。

该系统的目标是让用户在几十上百个节点中,一眼看出“到底哪一条在跑”,同时避免“取消后还在转圈”“已完成节点误以为仍在运行”等常见交互问题。

项目结构定位 ​

子任务节点动画系统主要位于以下路径:

层级文件职责
后台执行src/background/react-loop.js生成子任务计划、执行子任务、记录部分完成进度、计算超时预算
界面逻辑src/side_panel/chat-manager.js解析子任务清单、渲染待执行行/活动行/节点列表、处理过滤点击、分类最终结果
流式清理src/side_panel/chat-streaming.js取消流时的 UI 收尾,包括摘掉活动态、还原静态状态位
样式与动效src/side_panel/styles.css定义子任务卡片、节点行、当前节点强调、部分完成、reduced-motion 降级
回归测试test/unit/subtask-current-node-anim.unit.test.js验证当前节点动效作用域、取消链路、主题 Token 使用
语义修复测试test/unit/subtask-partial-outcome.unit.test.js验证“部分完成”三态与失败上报摘要
超时预算测试test/unit/subtask-timeout-budget.unit.test.js验证子任务超时放开、中断原因口径、已完成节点摘要回灌
graph TB
RL["后台执行<br/>react-loop.js"] --> CM["界面渲染<br/>chat-manager.js"]
CM --> CS["流式清理<br/>chat-streaming.js"]
CM --> STY["样式与动效<br/>styles.css"]
TEST1["动效回归测试<br/>subtask-current-node-anim.unit.test.js"] --> CM
TEST1 --> STY
TEST2["部分完成语义测试<br/>subtask-partial-outcome.unit.test.js"] --> CM
TEST3["超时预算测试<br/>subtask-timeout-budget.unit.test.js"] --> RL

图表来源

章节来源

核心组件 ​

子任务节点动画系统由四个关键能力组成:

  1. 子任务计划解析与权威清单
    从后台下发的执行日志中提取 nodeType='subtask_plan' 的权威子任务清单,用于界面展示“计划了哪些、还没执行到哪些”。

  2. 节点过滤与统计
    提供“✓N / ✗N / ⊘N”三类统计按钮,支持切换过滤状态;同一状态再点一次取消过滤,点击另一状态切换过滤。

  3. 当前执行节点动效
    当子任务处于活动态时,当前执行节点的状态位从静态“○”变为旋转环,整行出现呼吸底和左侧光条,节点图标有脉冲效果,文字加粗提亮。

  4. 取消与完成后的动效收敛
    取消流时移除 .subtask-progress-active,并把旋转环替换为静态“○”;完成或失败时也移除活动态,避免已完成节点继续动效。

章节来源

架构总览 ​

子任务节点动画系统的数据与控制流如下:

sequenceDiagram
participant User as "用户"
participant Panel as "侧边栏界面<br/>chat-manager.js"
participant Stream as "流式处理<br/>chat-streaming.js"
participant Background as "后台执行<br/>react-loop.js"
participant Styles as "样式系统<br/>styles.css"
User->>Panel : 查看子任务卡片
Panel->>Background : 读取执行日志/子任务计划
Background-->>Panel : subtask_plan + 节点状态更新
Panel->>Panel : 渲染待执行行/活动行/节点列表
Panel->>Styles : 添加 .subtask-progress-active
Styles-->>Panel : 显示旋转环/呼吸底/左侧光条
User->>Stream : 点击停止/取消
Stream->>Panel : finalizeCancelledStream
Stream->>Styles : 移除 .subtask-progress-active
Stream->>Panel : 状态位还原为静态 ○

图表来源

详细组件分析 ​

1. 子任务计划与节点渲染 ​

后台在执行主任务时,会生成权威子任务清单,并通过执行日志下发给侧边栏。界面侧通过解析 subtask_plan 消息,构建子任务条目数组,并按 index 升序插入容器,保证视觉顺序等于计划顺序。

关键点:

  • 权威清单来自后台,不是前端临时拼凑。
  • 待执行行和活动行可能以不同顺序到达,因此必须按 data-subtask-index 排序插入。
  • 未命中 plan 的消息或旧历史会自动降级为无清单模式。
flowchart TD
Start(["收到执行日志"]) --> Extract["提取 subtask_plan 清单"]
Extract --> HasPlan{"是否命中权威清单?"}
HasPlan --> |是| BuildItems["构建子任务条目数组"]
HasPlan --> |否| Fallback["降级为无清单模式"]
BuildItems --> InsertOrder["按 index 升序插入容器"]
InsertOrder --> RenderPending["渲染待执行行"]
RenderPending --> RenderActive["渲染活动行/节点列表"]
Fallback --> End(["结束"])
RenderActive --> End

图表来源

章节来源

2. 节点过滤与统计 ​

节点过滤由两个纯函数驱动:

函数输入输出行为
toggleNodeFilter当前过滤状态、点击的目标状态新过滤状态同一状态再点取消,点击另一状态切换
applyNodeFilter节点数组、过滤状态过滤后的节点数组success/failed 精确匹配,other 保留既非成功也非失败的节点

UI 上,统计按钮带有 data-node-filter,点击后:

  • 切换 _nodeFilter
  • 若激活过滤,自动展开子任务卡片
  • 调用 _renderNodes 重新渲染节点列表
flowchart TD
Click["点击 ✓N/✗N/⊘N"] --> Toggle["toggleNodeFilter(current, clicked)"]
Toggle --> FilterState{"是否激活过滤?"}
FilterState --> |是| Expand["添加 subtask-expanded"]
FilterState --> |否| Collapse["保持或收起"]
Expand --> Render["_renderNodes()"]
Collapse --> Render
Render --> Apply["applyNodeFilter(nodes, filter)"]
Apply --> UpdateDOM["更新节点显隐"]

图表来源

章节来源

3. 当前执行节点动效 ​

当前执行节点动效的核心约束是:动效只应在子任务卡片处于活动态时生效。如果基础规则自带 animation,会导致已完成子任务中的未完成节点一直转圈;如果取消链路不摘掉活动态,用户点了停止后界面仍会假装在跑。

动效包含四层:

动效触发条件表现
状态位旋转环子任务活动态 + 节点 processing用 <span class="subtask-node-spinner"> 替代静态 “○”
整行呼吸底子任务活动态 + 节点 processing背景色与左侧光条渐变
节点图标脉冲子任务活动态 + 节点 processing图标透明度与缩放变化
文字加粗提亮子任务活动态 + 节点 processing当前节点名称更醒目

CSS 中所有颜色均使用主题 Token(如 var(--brand-violet-rgb)),不使用硬编码十六进制色值,从而兼容亮色、暗色和跟随系统主题。

classDiagram
class SubtaskCard {
+string status
+boolean isRunning
+Array nodes
}
class NodeItem {
+string status
+string name
+boolean isProcessing
}
class Spinner {
+boolean isActive
+animationDuration
}
class BreatheEffect {
+backgroundGradient
+leftGlow
}
class IconPulse {
+opacityAnimation
+scaleAnimation
}
SubtaskCard --> NodeItem : "包含多个节点"
SubtaskCard --> Spinner : "活动态时启用"
SubtaskCard --> BreatheEffect : "processing 节点高亮"
SubtaskCard --> IconPulse : "processing 节点图标脉冲"

图表来源

章节来源

4. 取消链路与动效收敛 ​

取消流程由 finalizeCancelledStream 负责。它需要完成三件事:

  1. 移除所有 .subtask-progress-active,使呼吸底和旋转环停止。
  2. 把所有 .subtask-node-spinner 替换为静态 “○”,避免“取消后还在转圈”。
  3. 移除其他 streaming 相关动画元素,并标记消息为已取消。
flowchart TD
Cancel["用户点击停止"] --> Finalize["finalizeCancelledStream(element)"]
Finalize --> RemoveActive["移除 .subtask-progress-active"]
RemoveActive --> ReplaceSpinner["替换 .subtask-node-spinner 为 ○"]
ReplaceSpinner --> CleanupOther["移除其他 spinner 与 streaming 类"]
CleanupOther --> Done(["取消完成"])

图表来源

章节来源

5. 部分完成语义与失败上报 ​

“部分完成”是一个重要语义修复:当内层 ReAct 循环抛异常但多数节点已成功时,不应简单显示红色“失败”,而应显示橙色“部分完成”,并附带真实中断原因。同时,失败上报给父模型的 Result 中附加已完成节点的产出摘要,避免父模型“带着失败继续”却完全不知道已完成的工作。

关键实现点:

  • classifySubtaskOutcome 根据整体状态、成功节点数、总节点数判断是否为部分完成。
  • 阈值默认 0.8,即成功率 ≥80% 且整体失败时,UI 显示“部分完成”。
  • 后台通过 recordSubtaskPartialProgress 滚动写入已完成节点摘要,随父 checkpoint 落盘。
flowchart TD
Input["子任务结果<br/>status + succeedNodes + totalNodes"] --> Classify["classifySubtaskOutcome"]
Classify --> Success{"success ?"}
Success --> |是| MarkDone["标记为成功"]
Success --> |否| CheckRate{"succeedNodes / totalNodes >= 0.8 ?"}
CheckRate --> |是| Partial["标记为部分完成"]
CheckRate --> |否| Fail["标记为失败"]
Partial --> Summary["buildSubtaskPartialSummary"]
Fail --> Summary
Summary --> ParentReport["上报父模型 Result"]

图表来源

章节来源

6. 子任务超时预算与中断原因 ​

早期版本中,子任务曾被硬编码的 15 分钟上限封顶,导致设置页“整体循环超时”对子任务无效。修复后:

  • 子任务超时上限只由配置值决定。
  • 父任务可通过 apiParams.subtaskLoopTimeoutMs 注入更紧预算。
  • 中断文案需包含实际耗时与完整豁免项(澄清等待、敏感操作确认等待)。
  • 子任务不存自己的 checkpoint,中断即整体失败由父任务重试;因此已完成节点摘要必须回灌,否则重跑等于从零重做。
flowchart TD
Config["设置页 loopTimeout"] --> Compute["computeSubtaskLoopTimeoutMs"]
ParentBudget["父任务剩余时间"] --> MinCheck{"是否注入更紧预算?"}
MinCheck --> |是| UseParent["取 min(loopTimeout, subtaskLoopTimeoutMs)"]
MinCheck --> |否| UseConfig["使用配置值"]
UseParent --> Timeout["子任务超时判定"]
UseConfig --> Timeout
Timeout --> Reason["生成中断原因文案"]
Reason --> Replay["已完成节点摘要回灌"]

图表来源

章节来源

依赖关系分析 ​

子任务节点动画系统的依赖关系可以概括为:

  • chat-manager.js 依赖后台下发的执行日志,负责解析、渲染、交互。
  • chat-streaming.js 依赖 chat-manager 生成的 DOM,负责取消时的清理。
  • styles.css 被 chat-manager 和 chat-streaming 共同消费,提供视觉与动效。
  • react-loop.js 是数据源,负责子任务计划、执行、进度、超时。
  • 单元测试 通过读取源码片段断言行为,属于“源码级回归测试”,而非运行时集成测试。
graph LR
RL["react-loop.js"] --> CM["chat-manager.js"]
CM --> CS["chat-streaming.js"]
CM --> STY["styles.css"]
TEST1["subtask-current-node-anim.unit.test.js"] --> CM
TEST1 --> STY
TEST2["subtask-partial-outcome.unit.test.js"] --> CM
TEST3["subtask-timeout-budget.unit.test.js"] --> RL

图表来源

章节来源

性能与可访问性 ​

性能考虑 ​

  • 动效全部基于 CSS,不依赖 JavaScript 动画循环,减少 JS 主线程压力。
  • 旋转环、呼吸底、图标脉冲仅在 .subtask-progress-active 下生效,避免已完成节点持续消耗 GPU。
  • reduced-motion 媒体查询下关闭呼吸底和图标脉冲,仅保留常亮高亮,降低对低性能设备的负担。

可访问性考虑 ​

  • 使用 prefers-reduced-motion: reduce 提供降级方案,确保对动画敏感的用户仍能感知“当前节点”。
  • 颜色全部走主题 Token,避免硬编码色值导致暗色模式下对比度不足。
  • 统计按钮具备 hover 与 active 反馈,提升可点击性。

章节来源

故障排查指南 ​

现象可能原因排查位置修复建议
取消后节点仍旋转未移除 .subtask-progress-active 或未替换 .subtask-node-spinnerchat-streaming.js 的 finalizeCancelledStream确保移除活动态并将旋转环替换为静态 “○”
已完成节点一直呼吸基础规则自带 animation,未限定在 .subtask-progress-activestyles.css 中 .subtask-node-spinner 与 .subtask-node.processing将 animation 移到 .subtask-progress-active 作用域
无法区分当前节点缺少旋转环/呼吸底/左侧光条styles.css 当前节点强调块检查 keyframes 与作用域是否正确
部分完成显示为失败classifySubtaskOutcome 阈值或状态判断错误chat-manager.js核对 succeedNodes/totalNodes 与阈值 0.8
子任务 15 分钟被杀仍受硬编码超时限制react-loop.js 子任务超时计算确认放开硬编码上限,改用配置值
重跑子任务从零开始未完成节点摘要未回灌react-loop.js 的 recordSubtaskPartialProgress确保随父 checkpoint 落盘

章节来源

结论 ​

子任务节点动画系统通过“后台执行 + 界面渲染 + 样式动效 + 回归测试”四层协作,解决了用户在大量子任务节点中难以识别“当前执行节点”的问题。其设计重点在于:

  1. 作用域隔离:动效只在 .subtask-progress-active 下生效,避免已完成节点误动。
  2. 取消收敛:取消链路明确摘掉活动态并还原静态状态位。
  3. 语义清晰:部分完成与失败分离,失败上报附带已完成节点摘要。
  4. 主题合规:所有颜色使用 Token,兼容亮/暗/跟随系统主题。
  5. 测试保障:通过源码级单测锁定关键行为,防止动效回归。

这套机制不仅提升了用户体验,也为后续扩展更多子任务可视化能力提供了稳定基础。