Merge remote-tracking branch 'gitea/master'

This commit is contained in:
xiaohan.yuan committed 2026-10-21 18:53:39 +08:00
commit 36b6ceae45
14 files changed
+1226 -66

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
# B05 / YC-009:TodoWrite 交付记录 01
日期:2026-10-21。状态:开发实现与离线验证完成,待验收;不预填验收通过。
- 需求来源:`9069c604e03505b91b5b9731d11d47727b168635`,`B05-需求.md`。
- 开发基线:`8d10743fb2643b8e0aaf4495b38eb84f71b1e7f7`。
- 完整代码提交:`d423d59b08395360cd7263709d4560ab436d7c0c`。
- 本批不发布安装包,不调整模型桥、权限策略、项目数据库或其他计划卡设计。
## 实现与关键决定
1. 新增 `TodoWriteView.tsx`:只读规范化 args / `_rawInput`,兼容既有 todos / steps 及历史工具别名;保留 task ID、content、activeForm 和状态。完成数和总数按规范化列表计算。
2. 使用现代 `cr-tool-plan-task*` 结构与专用头部,去掉重复标题。merge 显示“进度 completed/total”;空与错误仅显示相应头部,运行中空列表附接收中状态。
3. 复用既有 `selectPlanTasks` 纯窗口算法,默认最多五项。优先第一个进行中任务,否则最后一个已完成任务,再否则从首项开始;不新增分页或更多按钮。
4. 完成项删除线并弱化,进行中使用 activeForm 和加粗,状态使用 SVG / 圆点,不再使用 OK / > / - / x 占位。样式按照需求提供的 REA 静态契约实现。
5. `SourceToolExpandable` 增加仅 Todo 使用的 retained expand-signal 开关和独立 disclosure key。真实 isLastContent 边沿控制展开;同值重挂载保留手动选择;session / tool / part 身份隔离。其他工具默认行为不变。
6. 不向普通列表追加 SDK 原始成功文本;不改写持久化结果、不合并历史工具快照。
7. 增加专用可选 `onTodoTaskClick`,字段为 toolCallId / toolName / taskId,支持点击、Enter、Space。当前没有证实的应用宿主目标,因此默认不传回调、不显示按钮角色或动作箭头,不复用计划预览冒充待办操作。
## 变更文件
- `src/renderer/src/workbuddy/conversation/TodoWriteView.tsx`(新增)
- `src/renderer/src/workbuddy/conversation/todo-write.css`(新增)
- `src/renderer/src/workbuddy/conversation/ToolCallRenderer.tsx`
- `src/renderer/src/workbuddy/conversation/SourceToolHeader.tsx`
- `src/renderer/src/workbuddy/conversation/source-labels.ts`
- `scripts/todo-write-regression.tsx`(新增)
- `scripts/todo-write-dom-regression.tsx`(新增)
- `scripts/conversation-renderer-regression.tsx`
- `scripts/run-alignment-regression.mjs`
## 验证
以下命令在 Linux 开发环境执行,没有启动真实 Claude SDK、外部模型服务或用户电脑,没有加载用户发布会话。
| 命令 | 结果 | 证据 |
|---|---|---|
| `npm run build` | exit 0,类型检查与生产构建通过 | `evidence/b05-01/build.log` |
| `npm run test:conversation` | exit 0 | `evidence/b05-01/conversation.log` |
| `npm run test:alignment` | exit 0,12 个离线套件;此单独运行明确跳过 DOM | `evidence/b05-01/alignment.log` |
| `YCODE_TEST_DOM_MODULE=/tmp/ycode-b01-dom/node_modules/jsdom npm run test:alignment:dom` | exit 0,12 个离线套件与 10 个 DOM 套件 | `evidence/b05-01/alignment-dom.log` |
新增聚焦测试已接入上述 alignment 入口,共 29 项投影/SSR 检查与 42 项 DOM 断言:投影/SSR 覆盖窗口夹具、状态、标题、空/失败、别名、原始输入与非突变;DOM 覆盖真实 TodoWriteView 和 CompletedFold 组合、位置边沿、手动选择、相同信号重挂载、会话/工具隔离、合法历史快照和可选回调键盘事件。使用合成数据,不修改真实会话。
测试依赖按项目既有外置 jsdom 约定提供;上述绝对路径是本次环境路径,其他环境需替换为其已安装的 jsdom 模块路径。严格 DOM 入口缺少依赖会失败,不将跳过当通过。
## 剩余风险与验收边界
- AC9.8:组件可选回调已验证,但实际应用宿主任务点击目标未实现,不能宣称完整业务交互通过。默认非交互是刻意避免伪导航。
- AC9.9:真实 Electron 浅色、深色、窄屏、长文本及七项任务截图未完成;DOM 不测真实像素、文本溢出和动画过程。当前浏览器执行环境受限,不绕过安全限制;由具备环境的验收端补测。
- 复用了现有 recovered CheckIcon / LoaderIcon / FailedIcon。没有独立确认名为 TaskCheckIcon 的精确原版资产;不能声称图标逐像素一致。
- 新 CSS 来自需求中已留存的 REA 静态数值,不代表当前原版 CSS 激活分支与实时动效已复核。仍需补原版 REA 运行证据。
- 交付没有修改验收记录;验收端应固定完整代码 SHA 后记录实际结果。
@@ -0,0 +1,87 @@
npm warn Unknown env config "http-proxy". This will stop working in the next major version of npm.
> ycode@0.0.10 test:alignment:dom
> node scripts/run-alignment-regression.mjs --require-dom
PASS model alignment: fresh/legacy migration, new/edit/default/delete/reload, valid selection (offline host fixtures)
PASS model settings: deferred owner switch, rename mapping, addition selection, empty deletion, immutable history
PASS session factory: first-send preference, mode preservation, invalid permission rejection (isolated ports)
{"historyMessages":10000,"bytes":747781,"parseMs":2.923016000000004,"storeHydrationMs":0.14862899999999968,"note":"Node parsing/store benchmark only, not browser rendering or Windows IPC performance"}
session-composer-alignment: loading/cache/retry/races/model/permissions/modes/SSR passed
alignment-conversation: local groups, original expanded order, question projection and truthful status passed
A01 native MessageBubble identity aliases, immutable inputs and ordinary raw output passed
A01 imported native card, linked dedup and unknown-position compatibility passed
question-answer-shell: structural and recovered CSS contracts passed
question-answer-shell: DOM hierarchy, controls linkage, click, Enter, Space and inert contracts passed
Runtime progress, session-grant scope and approval lifecycle regressions passed
Permission intercept static rendering passed; interactive keyboard/focus remains UI acceptance
PASS window six second active
PASS window seven fifth active
PASS window seven first active
PASS window seven last active
PASS window seven all pending
PASS window seven all completed
PASS window only fourth completed
PASS window multiple active uses first
PASS window empty
PASS window one
PASS window five
PASS window long middle
PASS alias TodoWrite
PASS alias todo_write
PASS alias todoWrite
PASS alias todo-write
PASS ordinary tools do not enter dedicated view
PASS args and raw input false
PASS args and raw input true
PASS args and raw input json
PASS historical steps retain compatible task aliases
PASS single dedicated title, modern status rows and output projection
PASS merge progress title computed from complete list not five-row window
PASS empty running is header only with receiving cue
PASS empty finished is header only without receiving cue
PASS failure is dedicated error header only
PASS without host callback rows are not fake buttons
PASS malformed raw input falls back without modifying original data
PASS long and escaped content stays plain text with full title
todo-write: 29 projection and SSR checks passed; real Electron visual acceptance not covered
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"permission-approval"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"permission-approval"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"permission-approval"}
[claude-permission] {"permissionMode":"full-access","agentMode":"ask","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"mode-or-inactive-deny"}
[claude-permission] {"permissionMode":"full-access","agentMode":"plan","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"mode-or-inactive-deny"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":true,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"subcommandResults","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"safetyCheck","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":true,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":true,"branch":"rule-approval"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"permission-approval"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"error-deny"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"permission-approval"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"85aafbf52a5144b5","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"business-question"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"85aafbf52a5144b5","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"business-question"}
Claude host approval policy unit checks passed (no SDK process/application/matching verification)
[claude-permission] {"permissionMode":"full-access","agentMode":"plan","sdkMode":"plan","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"hook","toolCall":"unknown","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":"unknown","branch":"mode-or-inactive-deny"}
Mock SDK replay lifecycle passed: fail closed, retry, session isolation, independent mode guard
B03 offline host policy: 276 checks passed; no real SDK/CLI/model execution.
task popconfirm DOM regression: 34 checks passed (jsdom, not pixel validation)
question-interaction: 24 DOM behavioral checks passed
question-identity: 30 A01 DOM checks passed (not Electron/pixel/SDK acceptance)
Question composer DOM: 21 checks passed (not pixel/SDK verification)
completed-fold-nested: real question/tool/thinking/local-summary remount, event, keyboard and identity contracts passed
completed-fold-native-identity: real reducer done correction, native IDs, insertion/reorder, repeated outer toggles, message isolation and legacy fallback passed
Permission intercept DOM: 49 checks passed (not pixel/SDK verification)
{"result":"PASS","suite":"reasoning-follow-dom","assertions":63,"evidence":"Actual ReactDOM with synthetic geometry/RAF/resize; not native scroll or pixel evidence"}
todo-write-dom: 42 actual TodoWrite/CompletedFold checks passed; no Electron/pixel claim
{"checks":37,"passed":true,"scope":"Isolated React DOM and CSS contracts; no native keyboard/layout/pixel acceptance"}
{"result":"PASS","phase":"alignment-offline","suites":12}
{"result":"PASS","phase":"alignment-dom","suites":10}
npm notice
npm notice New major version of npm available! 11.9.0 -> 12.2.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v12.2.0
npm notice To update run: npm install -g npm@12.2.0
npm notice
+72
View File
@@ -0,0 +1,72 @@
npm warn Unknown env config "http-proxy". This will stop working in the next major version of npm.
> ycode@0.0.10 test:alignment
> node scripts/run-alignment-regression.mjs
PASS model alignment: fresh/legacy migration, new/edit/default/delete/reload, valid selection (offline host fixtures)
PASS model settings: deferred owner switch, rename mapping, addition selection, empty deletion, immutable history
PASS session factory: first-send preference, mode preservation, invalid permission rejection (isolated ports)
{"historyMessages":10000,"bytes":747781,"parseMs":2.8995029999999957,"storeHydrationMs":0.1266159999999985,"note":"Node parsing/store benchmark only, not browser rendering or Windows IPC performance"}
session-composer-alignment: loading/cache/retry/races/model/permissions/modes/SSR passed
alignment-conversation: local groups, original expanded order, question projection and truthful status passed
A01 native MessageBubble identity aliases, immutable inputs and ordinary raw output passed
A01 imported native card, linked dedup and unknown-position compatibility passed
question-answer-shell: structural and recovered CSS contracts passed
question-answer-shell: DOM checks NOT RUN (set YCODE_TEST_DOM_MODULE to an available jsdom module)
Runtime progress, session-grant scope and approval lifecycle regressions passed
Permission intercept static rendering passed; interactive keyboard/focus remains UI acceptance
PASS window six second active
PASS window seven fifth active
PASS window seven first active
PASS window seven last active
PASS window seven all pending
PASS window seven all completed
PASS window only fourth completed
PASS window multiple active uses first
PASS window empty
PASS window one
PASS window five
PASS window long middle
PASS alias TodoWrite
PASS alias todo_write
PASS alias todoWrite
PASS alias todo-write
PASS ordinary tools do not enter dedicated view
PASS args and raw input false
PASS args and raw input true
PASS args and raw input json
PASS historical steps retain compatible task aliases
PASS single dedicated title, modern status rows and output projection
PASS merge progress title computed from complete list not five-row window
PASS empty running is header only with receiving cue
PASS empty finished is header only without receiving cue
PASS failure is dedicated error header only
PASS without host callback rows are not fake buttons
PASS malformed raw input falls back without modifying original data
PASS long and escaped content stays plain text with full title
todo-write: 29 projection and SSR checks passed; real Electron visual acceptance not covered
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"permission-approval"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"permission-approval"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"permission-approval"}
[claude-permission] {"permissionMode":"full-access","agentMode":"ask","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"mode-or-inactive-deny"}
[claude-permission] {"permissionMode":"full-access","agentMode":"plan","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"mode-or-inactive-deny"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":true,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"subcommandResults","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"safetyCheck","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":true,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"full-access","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"full-access-preauthorized"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":true,"branch":"rule-approval"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"permission-approval"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"error-deny"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"e3b98a4da31a127d","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"permission-approval"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"85aafbf52a5144b5","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"business-question"}
[claude-permission] {"permissionMode":"default-sandbox","agentMode":"craft","sdkMode":"unknown","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"callback","toolCall":"85aafbf52a5144b5","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":false,"branch":"business-question"}
Claude host approval policy unit checks passed (no SDK process/application/matching verification)
[claude-permission] {"permissionMode":"full-access","agentMode":"plan","sdkMode":"plan","sdkModeSource":"host-query-or-acknowledged-control","policyTransition":false,"source":"hook","toolCall":"unknown","decisionCategory":"unknown","decisionCode":"unknown","decisionCodePresent":false,"matchedAskRule":"unknown","branch":"mode-or-inactive-deny"}
Mock SDK replay lifecycle passed: fail closed, retry, session isolation, independent mode guard
B03 offline host policy: 276 checks passed; no real SDK/CLI/model execution.
{"result":"PASS","phase":"alignment-offline","suites":12}
{"result":"SKIP","phase":"alignment-dom","suites":10,"reason":"YCODE_TEST_DOM_MODULE is not set; DOM coverage was not run. Use --require-dom to forbid this skip."}
+670
View File
@@ -0,0 +1,670 @@
npm warn Unknown env config "http-proxy". This will stop working in the next major version of npm.
> ycode@0.0.10 build
> npm run typecheck && electron-vite build
npm warn Unknown env config "http-proxy". This will stop working in the next major version of npm.
> ycode@0.0.10 typecheck
> tsc --noEmit -p tsconfig.node.json && tsc --noEmit -p tsconfig.web.json
vite v8.3.4 building ssr environment for production...
transforming...
✓ 454 modules transformed.
rendering chunks...
out/main/index.mjs 1,656.15 kB
✓ built in 504ms
vite v8.3.4 building ssr environment for production...
transforming...
✓ 2 modules transformed.
rendering chunks...
out/preload/index.cjs 16.06 kB
✓ built in 10ms
vite v8.3.4 building client environment for production...
transforming...
✓ 3046 modules transformed.
rendering chunks...
out/renderer/settings.html 1.55 kB
out/renderer/index.html 1.73 kB
out/renderer/assets/KaTeX_Size3-Regular-CTq5MqoE.woff 4.42 kB
out/renderer/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 4.92 kB
out/renderer/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 5.20 kB
out/renderer/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 5.46 kB
out/renderer/assets/KaTeX_Size4-Regular-BF-4gkZK.woff 5.98 kB
out/renderer/assets/KaTeX_Size2-Regular-oD1tc_U0.woff 6.18 kB
out/renderer/assets/KaTeX_Size1-Regular-C195tn64.woff 6.49 kB
out/renderer/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 6.90 kB
out/renderer/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 6.91 kB
out/renderer/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf 7.58 kB
out/renderer/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff 7.65 kB
out/renderer/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff 7.71 kB
out/renderer/assets/archify-owner-avatar-BdE-HOjX.jpg 9.46 kB
out/renderer/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 9.64 kB
out/renderer/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 10.34 kB
out/renderer/assets/KaTeX_Size4-Regular-DWFBv043.ttf 10.36 kB
out/renderer/assets/KaTeX_Script-Regular-D5yQViql.woff 10.58 kB
out/renderer/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 11.31 kB
out/renderer/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 11.34 kB
out/renderer/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf 11.50 kB
out/renderer/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 12.02 kB
out/renderer/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 12.21 kB
out/renderer/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf 12.22 kB
out/renderer/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff 12.31 kB
out/renderer/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf 12.34 kB
out/renderer/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf 12.36 kB
out/renderer/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff 13.20 kB
out/renderer/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff 13.29 kB
out/renderer/assets/workbuddy-buddy-thumbnail-2713ed5b-CUhW0EYr.png 13.42 kB
out/renderer/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 13.56 kB
out/renderer/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff 14.11 kB
out/renderer/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff 14.40 kB
out/renderer/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff 16.02 kB
out/renderer/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 16.40 kB
out/renderer/assets/KaTeX_Math-Italic-t53AETM-.woff2 16.44 kB
out/renderer/assets/KaTeX_Script-Regular-C5JkGWo-.ttf 16.64 kB
out/renderer/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 16.78 kB
out/renderer/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 16.98 kB
out/renderer/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff 18.66 kB
out/renderer/assets/KaTeX_Math-Italic-DA0__PXp.woff 18.74 kB
out/renderer/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff 19.41 kB
out/renderer/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf 19.43 kB
out/renderer/assets/KaTeX_Fraktur-Regular-CB_wures.ttf 19.57 kB
out/renderer/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf 19.58 kB
out/renderer/assets/KaTeX_Main-Italic-BMLOBm91.woff 19.67 kB
out/renderer/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf 22.36 kB
out/renderer/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf 24.50 kB
out/renderer/assets/KaTeX_Main-Bold-Cx986IdX.woff2 25.32 kB
out/renderer/assets/KaTeX_Main-Regular-B22Nviop.woff2 26.27 kB
out/renderer/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf 27.55 kB
out/renderer/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 28.07 kB
out/renderer/assets/KaTeX_Main-Bold-Jm3AIy58.woff 29.91 kB
out/renderer/assets/KaTeX_Main-Regular-Dr94JaBh.woff 30.77 kB
out/renderer/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf 31.19 kB
out/renderer/assets/KaTeX_Math-Italic-flOr_0UB.ttf 31.30 kB
out/renderer/assets/design-case-3-DaIvrslH.png 32.76 kB
out/renderer/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf 32.96 kB
out/renderer/assets/KaTeX_AMS-Regular-DMm9YOAa.woff 33.51 kB
out/renderer/assets/KaTeX_Main-Italic-3WenGoN9.ttf 33.58 kB
out/renderer/assets/design-case-4-0hVdAJQR.png 45.16 kB
out/renderer/assets/design-case-2-BH_iQ5r3.png 45.56 kB
out/renderer/assets/KaTeX_Main-Bold-waoOVXN0.ttf 51.33 kB
out/renderer/assets/design-case-1-DOYiafVz.png 52.97 kB
out/renderer/assets/KaTeX_Main-Regular-ypZvNtVU.ttf 53.58 kB
out/renderer/assets/KaTeX_AMS-Regular-DRggAlZN.ttf 63.63 kB
out/renderer/assets/workbuddy-appearance-dark-CXQX3TZX.png 109.34 kB
out/renderer/assets/workbuddy-appearance-light-BbpPCukh.png 113.02 kB
out/renderer/assets/codicon-ngg6Pgfi-ngg6Pgfi.ttf 121.97 kB
out/renderer/assets/kimi-DiLyRSiJ.svg 129.22 kB
out/renderer/assets/json.worker-DEWrzUkU-DlQJfCLu.js 438.15 kB
out/renderer/assets/worker-portable-D1lwTqsk-D1lwTqsk.js 442.77 kB
out/renderer/assets/landing-hero-B6659kdy.png 629.28 kB
out/renderer/assets/html.worker-BC4B-9NY-C21OBAfO.js 756.13 kB
out/renderer/assets/css.worker-D7wyZb8r-DjOdNjkq.js 1,086.84 kB
out/renderer/assets/ts.worker-BsXRl7a--ByKYw2y-.js 8,119.57 kB
out/renderer/assets/ArtifactPierreDiff-Pvasiehm.css 1.15 kB
out/renderer/assets/GeneralSettingsPanel-BMcnXzoL.css 1.22 kB
out/renderer/assets/PrivateRuntimesSettingsPanel-EdeAJ36I.css 2.27 kB
out/renderer/assets/AgentsSettingsPanel-D7jOqSpF.css 2.48 kB
out/renderer/assets/AboutHelpSettingsPanel-CvF9RRxe.css 5.68 kB
out/renderer/assets/updates-ui-DMMyH1QT.css 6.55 kB
out/renderer/assets/AppearanceSettingsPanel-5CpGnhJ2.css 8.01 kB
out/renderer/assets/ShortcutsSettingsPanel-DwQcnFCC.css 17.84 kB
out/renderer/assets/MemorySettingsPanel-BborA2GL.css 32.21 kB
out/renderer/assets/ModelSettingsPanel-CFoDDMfH.css 88.96 kB
out/renderer/assets/PersonalizationSettingsPanel-D6czCIoD.css 98.51 kB
out/renderer/assets/app-BWjvCN9X.css 256.94 kB
out/renderer/assets/index-BQNslUuB.css 2,672.71 kB
out/renderer/assets/pie-WAS4IAKB-DKyretgv.js 0.11 kB
out/renderer/assets/info-A6RAGUB7-BbuYkEQc.js 0.11 kB
out/renderer/assets/radar-RG4KPBEZ-DxOoeCNQ.js 0.11 kB
out/renderer/assets/packet-AYTQ26CC-5rw3f48a.js 0.11 kB
out/renderer/assets/cynefin-OW5HDTMX-DcWHdUdD.js 0.11 kB
out/renderer/assets/treemap-WGGIJYW6-Db5Fv2Px.js 0.11 kB
out/renderer/assets/wardley-WFR3VGLG-Cw51bcpw.js 0.11 kB
out/renderer/assets/gitGraph-4MIJSDKK-CTA_mL7Y.js 0.12 kB
out/renderer/assets/railroad-74A4TZTK-DXj7b3sH.js 0.12 kB
out/renderer/assets/treeView-Q6P3EWNA-CwSIBnHm.js 0.12 kB
out/renderer/assets/railroad-peg-WCYAUIDC-DquA166b.js 0.12 kB
out/renderer/assets/architecture-7GRP2DOG-Bqw4dsRW.js 0.12 kB
out/renderer/assets/railroad-abnf-HS5TGJTU-BRdGQKor.js 0.12 kB
out/renderer/assets/railroad-ebnf-LZEXJU2U-DZkQfJQF.js 0.12 kB
out/renderer/assets/eventmodeling-NTZA5JFV-Cy7WZVhw.js 0.12 kB
out/renderer/assets/array-75Ih5Qg4.js 0.16 kB
out/renderer/assets/channel-BWQK_ajg.js 0.25 kB
out/renderer/assets/init-DHdG6DHP.js 0.27 kB
out/renderer/assets/chunk-Y2CYZVJY-CrOJDJJy.js 0.38 kB
out/renderer/assets/chunk-JWPE2WC7-BWmaSNkB.js 0.42 kB
out/renderer/assets/chunk-2Q5K7J3B-BBOGhvIe.js 0.44 kB
out/renderer/assets/prism-tsx-BJWEHjlk.js 0.48 kB
out/renderer/assets/chunk-5VM5RSS4-D50WUAUX.js 0.52 kB
out/renderer/assets/chunk-XXDRQBXY-BsO966Nz.js 0.58 kB
out/renderer/assets/flowDiagram-HODETNUW-BTgZ3hZ4.js 0.59 kB
out/renderer/assets/prism-json-DxLGXN1o.js 0.62 kB
out/renderer/assets/codeowners-C7Y3CTAA-DLFDeFSM.js 0.75 kB
out/renderer/assets/prism-clike-BcH_eOfE.js 0.89 kB
out/renderer/assets/tsv-DVa1_Ovr-Dcnz2zsv.js 0.94 kB
out/renderer/assets/shellsession-C0HwRpr0-DSJrX8zi.js 0.96 kB
out/renderer/assets/javascript-DGySzfUB-CQaVVFGk.js 0.99 kB
out/renderer/assets/settings-BqWrzhuc.js 1.01 kB
out/renderer/assets/infoDiagram-27XIBGKW-BpX7PJd3.js 1.09 kB
out/renderer/assets/swimlanesDiagram-VR7AAH4N-DftsxObZ.js 1.09 kB
out/renderer/assets/html-derivative-D3Jld805-DnSa01yF.js 1.10 kB
out/renderer/assets/prism-go-Cw8Jg9YT.js 1.11 kB
out/renderer/assets/classDiagram-ZZMXUADV-DfaHi3gl.js 1.16 kB
out/renderer/assets/classDiagram-v2-VYDZK3BY-CuhvyBCV.js 1.16 kB
out/renderer/assets/stateDiagram-v2-MP3YSRHH-BEU-HhX7.js 1.16 kB
out/renderer/assets/qmldir-DIq06gp2-Dk3xkylH.js 1.20 kB
out/renderer/assets/git-rebase-CgwCWTNR-Dy4BOb63.js 1.23 kB
out/renderer/assets/chunk-POPQ4Y6H-m-QFhyU7.js 1.23 kB
out/renderer/assets/azcli-Bsxmaz-H-Dgh56k5-.js 1.26 kB
out/renderer/assets/personalization-prefs-DASR3vwn.js 1.28 kB
out/renderer/assets/csv-BRhERoQG-g8SHvOgt.js 1.36 kB
out/renderer/assets/prism-latex-CHSuA_Lo.js 1.38 kB
out/renderer/assets/jsx-runtime-C48wkPcG.js 1.41 kB
out/renderer/assets/Switch-lqp_PN8l.js 1.46 kB
out/renderer/assets/git-commit-DGtBy1vY-D-8ck59F.js 1.47 kB
out/renderer/assets/AgentsSettingsPanel-COZMCaB6.js 1.56 kB
out/renderer/assets/xsl-DV28r3Jw-BVd5CgjJ.js 1.59 kB
out/renderer/assets/dotenv-DXc2fSbR-CJy04BVY.js 1.62 kB
out/renderer/assets/general-settings-icons-DOmPIusw.js 1.64 kB
out/renderer/assets/prism-scala-CRTYzCpy.js 1.65 kB
out/renderer/assets/ini-D4-1Iswn-DaNDgFS8.js 1.65 kB
out/renderer/assets/prism-typescript-D-7ZUUQ-.js 1.65 kB
out/renderer/assets/sizeCapture-INFHLROL-CCEMv_MJ.js 1.68 kB
out/renderer/assets/csp-Bsbijh2H-033SDyRW.js 1.69 kB
out/renderer/assets/sparql-Mj-YIaDH-BykJ7CZ3.js 1.71 kB
out/renderer/assets/prism-css-Cmyw76Xa.js 1.73 kB
out/renderer/assets/ini-DMSv7OY2-BXeXsWON.js 1.74 kB
out/renderer/assets/rolldown-runtime-BJCASTsb-DKQFAHUF.js 1.78 kB
out/renderer/assets/line-XXWW1d8G.js 1.89 kB
out/renderer/assets/fortran-fixed-form-D91DiZLE-CdOEPerJ.js 1.91 kB
out/renderer/assets/docker-B7J3eHK7-QmwgNdK9.js 1.96 kB
out/renderer/assets/hxml-938TheZ7-RCVJURv9.js 1.97 kB
out/renderer/assets/desktop-BwI-o01e-CG4hU2BG.js 2.03 kB
out/renderer/assets/ordinal-CmpyCcm_.js 2.22 kB
out/renderer/assets/prism-c-B7tsC1jE.js 2.23 kB
out/renderer/assets/rolldown-runtime-EITvVbkG.js 2.32 kB
out/renderer/assets/erb-BgMB3Uiw-DolD8jZl.js 2.40 kB
out/renderer/assets/prism-kotlin-Bc2NQYRR.js 2.43 kB
out/renderer/assets/jssm-CIisorE2-D_RzJT49.js 2.44 kB
out/renderer/assets/prism-python-CTkfasgb.js 2.48 kB
out/renderer/assets/reg-mzCtpvUf-CI4m8Oi6.js 2.54 kB
out/renderer/assets/bat-2PwuskcT-BbFmQdFU.js 2.55 kB
out/renderer/assets/prism-mermaid-DSoNsh6-.js 2.56 kB
out/renderer/assets/pla-HAHcX3CH-DHyEyFew.js 2.57 kB
out/renderer/assets/sb-O6mPt74--_nMcDiF8.js 2.60 kB
out/renderer/assets/chunk-GVQU2GXP-ofjj9lSj.js 2.61 kB
out/renderer/assets/edge-DcEd3r9D-CBXc5XSz.js 2.62 kB
out/renderer/assets/scheme-C-yCXfo--BHhgh59u.js 2.66 kB
out/renderer/assets/flow9-z3HgHIql-qOic_Sv2.js 2.72 kB
out/renderer/assets/prism-yaml-cmjQ_1eD.js 2.77 kB
out/renderer/assets/gleam-DEEAoY6G-BpQSljGj.js 2.78 kB
out/renderer/assets/railroadDiagram-O6MQD6OU-Bf48G_eC.js 2.78 kB
out/renderer/assets/diff-lAxbRYdS-DM8zs7pT.js 2.79 kB
out/renderer/assets/hy-BKymZdC0-CoHxmd7P.js 2.86 kB
out/renderer/assets/dockerfile-CnkhFuxG-CfXHMZr-.js 3.01 kB
out/renderer/assets/openscad-BRTETpP_-hMqJPCYB.js 3.03 kB
out/renderer/assets/pascaligo-DI1dd1Zd-D23AoFdQ.js 3.03 kB
out/renderer/assets/log-Cn1ZE6kb-B2Eb-ljU.js 3.05 kB
out/renderer/assets/objective-c-gNYZndaF-4hkcNMcE.js 3.06 kB
out/renderer/assets/sparql-DxWbGozt-B7qTmycl.js 3.08 kB
out/renderer/assets/prism-markup-templating-D4pSJIpH.js 3.11 kB
out/renderer/assets/prism-cpp-CEgmwlDT.js 3.11 kB
out/renderer/assets/cameligo-B6LDhzzR-DSNRk43W.js 3.12 kB
out/renderer/assets/wenyan-BVgiL1tD-BbCpcZMk.js 3.12 kB
out/renderer/assets/cairo-D_LzMZ4I-vxqtZBLC.js 3.17 kB
out/renderer/assets/prism-rust-Du0Qa8GC.js 3.18 kB
out/renderer/assets/lua-CJt09SGy-3O0VAMVx.js 3.19 kB
out/renderer/assets/graphql-C0cKWTiE-BQOKksLE.js 3.20 kB
out/renderer/assets/berry-DTcSbIs7-Bajq3gC1.js 3.20 kB
out/renderer/assets/jsonl-Cn_fzNA8-BaVvDATF.js 3.21 kB
out/renderer/assets/json-CKKhFIOr-s_zdeuXX.js 3.29 kB
out/renderer/assets/mips-Df5bwPtM-DJn7b529.js 3.29 kB
out/renderer/assets/jsonc-Bf3Xcgeq-dLcyekG4.js 3.31 kB
out/renderer/assets/logo-BxqPaaID-DryN2ftp.js 3.33 kB
out/renderer/assets/abnfDiagram-VCTEODGH-DScZ7qeo.js 3.36 kB
out/renderer/assets/go-BejFoqvR-BnGoIlsY.js 3.42 kB
out/renderer/assets/po-DI6EG-tz-B5Fqkzer.js 3.44 kB
out/renderer/assets/lexon-5NyrCfQ6-B1bdOCGH.js 3.45 kB
out/renderer/assets/prism-sql-BggiY1Ht.js 3.45 kB
out/renderer/assets/json5-BlijqrwT-RbfFZ0Ca.js 3.45 kB
out/renderer/assets/mipsasm-U3SQfJCQ-Bpofb6PH.js 3.46 kB
out/renderer/assets/sophia-foSZ3xPI-DGYIHURw.js 3.47 kB
out/renderer/assets/tasl-C74Cvwdf-odmyCWOH.js 3.49 kB
out/renderer/assets/genie-CjS3m_gh-DHg6VJ2c.js 3.56 kB
out/renderer/assets/prism-swift-xplGsQ1n.js 3.56 kB
out/renderer/assets/vala-DMgevQbf-D3glwiU9.js 3.57 kB
out/renderer/assets/pegDiagram-XKGWAZYB-XBo3nAy7.js 3.59 kB
out/renderer/assets/rel-D6dNeDZv-BuMJ00SL.js 3.60 kB
out/renderer/assets/prism-java-6uFEGwWz.js 3.63 kB
out/renderer/assets/fsharp-Ba-cvOiG-BoxQhS9v.js 3.64 kB
out/renderer/assets/chunk-P2QGCYS3-CxdLISr-.js 3.65 kB
out/renderer/assets/ebnfDiagram-PWID7BFC-zPt9Bbyk.js 3.66 kB
out/renderer/assets/qsharp-78iLGMHs-Dbh3PRIx.js 3.68 kB
out/renderer/assets/m3-Bu81_nWr-4t51Qx0i.js 3.69 kB
out/renderer/assets/chunk-F27PBJKO-CrA32sFu.js 3.70 kB
out/renderer/assets/pascal-DyPgZbsX-Ba58YM7a.js 3.71 kB
out/renderer/assets/path-CSWwviF2.js 3.79 kB
out/renderer/assets/fluent-BSSEgVQa-DxeZwjFt.js 3.81 kB
out/renderer/assets/splunk-BdqSfgpd-DI-JRPbG.js 3.81 kB
out/renderer/assets/jsonnet-Bn71vRY8-D3pjpEpa.js 3.82 kB
out/renderer/assets/ssh-config-BO51LXox-COpWXG2j.js 3.82 kB
out/renderer/assets/bicep-DujF3y-4-DHww3ZLF.js 3.87 kB
out/renderer/assets/narrat-DdgkgBr0-BdvjtlQ4.js 3.87 kB
out/renderer/assets/redis-Bo427DT4-DBDF4ZWx.js 3.88 kB
out/renderer/assets/turtle-DAmmiOda-BDuf0mmV.js 3.92 kB
out/renderer/assets/dist-CE9GMTNb.js 3.97 kB
out/renderer/assets/prism-jsx-BO0yI1A3.js 3.97 kB
out/renderer/assets/xml-BBFzlEmT-CqLnJtYa.js 3.99 kB
out/renderer/assets/java-Z7anYNQ6-DdC7WVZS.js 4.01 kB
out/renderer/assets/r-Xh-gXEK4-DKEPC8dA.js 4.02 kB
out/renderer/assets/ron-CdysqNkW-Biq2zrex.js 4.11 kB
out/renderer/assets/zenscript-XbBI4c9W-BIIQCk-W.js 4.12 kB
out/renderer/assets/glsl-CZF-bvQV-Fa2h6RFS.js 4.14 kB
out/renderer/assets/shell-DGp7WuK3-X00pamiI.js 4.15 kB
out/renderer/assets/gn-DJxfXStl-LnblnCw8.js 4.20 kB
out/renderer/assets/kotlin-C61BvWgT-B7MkJpdU.js 4.20 kB
out/renderer/assets/typespec-B704GmX3-f1l-QmOw.js 4.26 kB
out/renderer/assets/kdl-YTd72GVl-bT5ugZen.js 4.27 kB
out/renderer/assets/AppearanceSettingsPanel-CBK8fzZz.js 4.28 kB
out/renderer/assets/hurl-CDXCvJNd-B5COsIcP.js 4.28 kB
out/renderer/assets/cypher-CjfQI0Y9-9gjOyi6P.js 4.33 kB
out/renderer/assets/pascal-CLPA94yO-Ct0GT-Y8.js 4.35 kB
out/renderer/assets/powershell-CaPzBwIT-C9KgMPOP.js 4.50 kB
out/renderer/assets/tcl-D-NiFh9X-A6B_HfKB.js 4.63 kB
out/renderer/assets/apex-BE-040ts-wdfiR7V6.js 4.65 kB
out/renderer/assets/tcl-DowxIvU4-Bbhzqrsw.js 4.65 kB
out/renderer/assets/rosmsg-DDyuMVTo-DYKxOLYP.js 4.72 kB
out/renderer/assets/nextflow-DVTlkzu9-BuUjFRik.js 4.75 kB
out/renderer/assets/http-DhxVjYFh-CnuCwIDo.js 4.76 kB
out/renderer/assets/hcl-CbjaVS9i-DCS43mBd.js 4.79 kB
out/renderer/assets/rust-Ij2gfGoT-DLzaBWYs.js 4.83 kB
out/renderer/assets/polar-CwTxzzWE-FAjAWxw3.js 4.87 kB
out/renderer/assets/prism-ruby-PgHh3fQm.js 4.91 kB
out/renderer/assets/fennel-COJTUo2O-hFG5qwbk.js 4.97 kB
out/renderer/assets/bibtex-yEKRWsaj-Bi_6UN3n.js 5.00 kB
out/renderer/assets/coffee-CcgZJF1B-DkN4xoBX.js 5.04 kB
out/renderer/assets/llvm-DiFwU398-1mzrPGsR.js 5.25 kB
out/renderer/assets/less-CXux59mP-BM7SkPLH.js 5.27 kB
out/renderer/assets/msdax-DEyWhkWP-CwdIr_-P.js 5.29 kB
out/renderer/assets/prism-javascript-B6zosBCV.js 5.31 kB
out/renderer/assets/wgsl-Ds7VVNHP-DP7CKawS.js 5.34 kB
out/renderer/assets/markdown-CBag5COD-B_1rbk64.js 5.36 kB
out/renderer/assets/dart-DK8_VUW5-Bnb_Tn57.js 5.39 kB
out/renderer/assets/zig-BBWfbOQ5-FARukIbS.js 5.54 kB
out/renderer/assets/restructuredtext-D2p7PcFG-DDHAm_JI.js 5.55 kB
out/renderer/assets/dax-CmrIxjmL-Dts6UfZw.js 5.56 kB
out/renderer/assets/gdresource-4lq4eh4q-DVqedupl.js 5.56 kB
out/renderer/assets/qml-D6gd64uC-DRYar41A.js 5.57 kB
out/renderer/assets/bicep-wTwvD1CI-f8YJLLcJ.js 5.58 kB
out/renderer/assets/cssMode-Z-o80aSY-ghntTnpV.js 5.59 kB
out/renderer/assets/awk-DTjcVAOD-CzIppyvU.js 5.66 kB
out/renderer/assets/liquid-Dv-zz5BA-D9ukCDuL.js 5.71 kB
out/renderer/assets/csharp-DpS7Vfy9-CbQzv1ql.js 5.76 kB
out/renderer/assets/coq-BFytDJOI-ef_Ur-QP.js 5.81 kB
out/renderer/assets/swift-C3A42nmX-BBVToPkN.js 5.83 kB
out/renderer/assets/xml-bIkPj5aH-CwOW5xDz.js 5.89 kB
out/renderer/assets/ecl-CYzojQ6a-C8m0_osY.js 5.91 kB
out/renderer/assets/jinja-Dl-Ji_JA-TQEv_6fW.js 5.92 kB
out/renderer/assets/lean-Cu10prRO-DeNAbMo1.js 6.03 kB
out/renderer/assets/yaml-GEMDST6R-D-0AGe4F.js 6.09 kB
out/renderer/assets/moonbit-B8-g2u5v-CVTe6nNw.js 6.10 kB
out/renderer/assets/powerquery-tKWDTKOo-CH98Al2Y.js 6.11 kB
out/renderer/assets/SourceCodePreview-C1ztCBwf.js 6.11 kB
out/renderer/assets/python-CHafhQyI-DmJIfgbV.js 6.14 kB
out/renderer/assets/shaderlab-Df7nOKvI-BmJ1KXMD.js 6.16 kB
out/renderer/assets/css-sG8ozMHf-pZGexMoj.js 6.17 kB
out/renderer/assets/cpp-CipCPBaT-7uQRIwsC.js 6.17 kB
out/renderer/assets/vb-BKV3Sgm5-Dt7Ll8D7.js 6.29 kB
out/renderer/assets/htmlMode-BDZbYlMj-siLdeIso.js 6.40 kB
out/renderer/assets/red-CYaKK_PD-CzqFe3Tm.js 6.45 kB
out/renderer/assets/min-dark-DGPfDRqJ-HwLk1pky.js 6.48 kB
out/renderer/assets/pug-EKWpNZSi-oQ708bsr.js 6.51 kB
out/renderer/assets/prisma-CdZyz3OC-DHqg13Q1.js 6.53 kB
out/renderer/assets/gdshader-ryZhEIxg-DTUiAD-H.js 6.55 kB
out/renderer/assets/ara-_lrJyEFe-BMlXck3g.js 6.56 kB
out/renderer/assets/clojure-WSq30mUj-J-mHf2za.js 6.61 kB
out/renderer/assets/toml-DPP887_3-DwEWbgmd.js 6.62 kB
out/renderer/assets/postcss-CcIp2ENG-CYsMQTxy.js 6.64 kB
out/renderer/assets/verilog-bragEGMk-B0_YfS3E.js 6.64 kB
out/renderer/assets/PrivateRuntimesSettingsPanel-B4qZX77D.js 6.67 kB
out/renderer/assets/solarized-light-BIMszn7B-BHWshuHE.js 6.68 kB
out/renderer/assets/cypher-COYrq2Yy-BK3oZ1E6.js 6.75 kB
out/renderer/assets/arc-LpCwTDer.js 6.75 kB
out/renderer/assets/proto-CQIfhs_g-CtoFRVaP.js 6.77 kB
out/renderer/assets/smalltalk-BLGdLDJX-BnBPALl8.js 6.79 kB
out/renderer/assets/vb-D1Cw85Ss-BSz-V9M4.js 6.86 kB
out/renderer/assets/talonscript-DA9pg7Tj-RnGeNC0u.js 6.96 kB
out/renderer/assets/r-Bb2Mu7Fo-CdtUEa4x.js 7.00 kB
out/renderer/assets/solarized-dark-D_I-4EGa-dEDCUT49.js 7.04 kB
out/renderer/assets/diagram-Z3DM3KII-CDqXKLeu.js 7.08 kB
out/renderer/assets/riscv-CQHqXr1Z-Cpm-2Pe9.js 7.11 kB
out/renderer/assets/soy-DybHqX1A-DwQA3zJN.js 7.15 kB
out/renderer/assets/min-light-BF0EjVgr-CorKJXFA.js 7.16 kB
out/renderer/assets/scheme-Dn0tgEWN-Dx4kPKzi.js 7.37 kB
out/renderer/assets/prism-bash-B2ETaHUm.js 7.43 kB
out/renderer/assets/typescript-sfPIqb8J-DtIl1kXb.js 7.44 kB
out/renderer/assets/hlsl-CAmjcFpk-18jAR-hO.js 7.48 kB
out/renderer/assets/mdx-Bdu4uO-C-BrzgNjCw.js 7.58 kB
out/renderer/assets/qss-DkzwWM0J-zjH4M815.js 7.67 kB
out/renderer/assets/html-DKbmsqz6-Cnkf6ew3.js 7.69 kB
out/renderer/assets/systemd-CS6mVasZ-D_yvUREH.js 8.07 kB
out/renderer/assets/monokai-CYqILLdj-DvnA5NkK.js 8.08 kB
out/renderer/assets/twig-r8vgPZrm-D2l24YwI.js 8.18 kB
out/renderer/assets/updates-ui-BqpdtOLV.js 8.22 kB
out/renderer/assets/wgsl-D_5Qxf6T-D508aVGZ.js 8.27 kB
out/renderer/assets/sdbl-BXfz6d3_-CYe5PkP_.js 8.42 kB
out/renderer/assets/regexp-Dv0OUts_-CPlEBsRg.js 8.46 kB
out/renderer/assets/prism-php-dzcOOH50.js 8.49 kB
out/renderer/assets/systemverilog-BreNGkRs-RIXr4hNf.js 8.65 kB
out/renderer/assets/plsql-C-xpsZfV-D-CnN_to.js 8.71 kB
out/renderer/assets/vue-html-DvuHo2mm-0SkYmZrV.js 8.71 kB
out/renderer/assets/haml-CIYwkycC-Df8c_gw1.js 8.81 kB
out/renderer/assets/scss-CLoW-2dr-BBC_GO_6.js 8.86 kB
out/renderer/assets/dart-CBkkDIWs-pnVQUz7O.js 8.94 kB
out/renderer/assets/horizon-CELVL8Ba-C2I1M4mp.js 8.97 kB
out/renderer/assets/horizon-bright-DBDyMeUQ-Co9G8FSM.js 8.98 kB
out/renderer/assets/julia-DvNkK6LK-Bi3Rlfah.js 9.04 kB
out/renderer/assets/clojure-886L7m2N-BhGJsUM7.js 9.14 kB
out/renderer/assets/make-CsAfL-jc-motAe4hn.js 9.18 kB
out/renderer/assets/andromeeda--j5Bq-48-DCMNZFq_.js 9.21 kB
out/renderer/assets/dark-plus-FAPM6M6c-Bt70pA2P.js 9.29 kB
out/renderer/assets/sas-BQIEUTPQ-BIyx7TAp.js 9.31 kB
out/renderer/assets/slack-dark-nDxLkSCC-y2Ay7FmO.js 9.31 kB
out/renderer/assets/perl-Bec3eWyr-CDmSu3_s.js 9.32 kB
out/renderer/assets/plastic-DY_h9Vzh-CD0KLW0z.js 9.49 kB
out/renderer/assets/sass-Ysv8HTNa-BGj6JvvR.js 9.49 kB
out/renderer/assets/st-BrgXZ-Ag-BUs-RRgp.js 9.58 kB
out/renderer/assets/typst-Co5rZla4-OQ5Z92wn.js 9.61 kB
out/renderer/assets/slack-ochin-7zJWY-K--Dysf61-O.js 9.63 kB
out/renderer/assets/defaultLocale-QNqK0iCN.js 9.84 kB
out/renderer/assets/handlebars-CIJAyfa9-6toBlNYk.js 9.94 kB
out/renderer/assets/jison-Cbw0l5It-CxZxtz6O.js 9.95 kB
out/renderer/assets/sql-DviNlDJO-CwV6bDIP.js 9.97 kB
out/renderer/assets/kotlin-DIw6ua6F-TFNQT90V.js 10.01 kB
out/renderer/assets/cmake-CyNDuJo8-BZldhEGe.js 10.07 kB
out/renderer/assets/light-plus-DkScKIB5-7ug4iLR4.js 10.14 kB
out/renderer/assets/hcl-DoJdlYjN-CYB-uxe0.js 10.25 kB
out/renderer/assets/scala-Dj45wWzI-CAMCqIXS.js 10.40 kB
out/renderer/assets/postiats-SaHD9Qcv-FPvOr1pn.js 10.45 kB
out/renderer/assets/beancount-CyZf3Huc-DE4CAoDi.js 10.58 kB
out/renderer/assets/tex-gcnXexI3-DkpGyyGn.js 10.60 kB
out/renderer/assets/php-Bn8AF0uj-CBbpr2vs.js 10.62 kB
out/renderer/assets/nextflow-groovy-D4ZyOBoZ-B2EMVDGy.js 10.63 kB
out/renderer/assets/dream-maker-5oXa1Z51-D1LnccFA.js 10.67 kB
out/renderer/assets/ts-tags-xeTAUNHG-Bn9JKLph.js 10.70 kB
out/renderer/assets/raku-Bfr41qeN-D4VhQ2rr.js 10.72 kB
out/renderer/assets/mysql-CZJtil0v-CP4gK9vT.js 10.73 kB
out/renderer/assets/task-menu-icons-BRdNqJF0.js 10.78 kB
out/renderer/assets/prism-csharp-D9xOwbIN.js 10.91 kB
out/renderer/assets/yaml-CsitfczK-Dei6m9-K.js 10.97 kB
out/renderer/assets/diagram-UQ7AKVKN-BNly1nWm.js 11.05 kB
out/renderer/assets/elm-Dl1XrIiD-eMpXTcNa.js 11.18 kB
out/renderer/assets/just-He7qscmV-DoPzJKPC.js 11.19 kB
out/renderer/assets/redshift-Djb4PK3z-9Ar4PFWD.js 11.35 kB
out/renderer/assets/github-light-B_J7N1HZ-DMuZw_Uo.js 11.38 kB
out/renderer/assets/ruby-CxOCB7_P-BzsjPJ6Z.js 11.48 kB
out/renderer/assets/prolog-DAqMMM08-BuMDGWJy.js 11.56 kB
out/renderer/assets/terraform-BW8B2WXV-DHhZfkB5.js 11.59 kB
out/renderer/assets/github-dark-BgqEY2WG-Dnnmf1fd.js 11.60 kB
out/renderer/assets/puppet-CaR8DEdW-Bk1_hyzj.js 11.65 kB
out/renderer/assets/pkl-DrXR3Dxy-Bk_Dwe9f.js 11.67 kB
out/renderer/assets/laserwave-Clkc4Pj8-CKWIlHTc.js 11.69 kB
out/renderer/assets/pieDiagram-E7YTZNPT-BNLfmN4s.js 11.95 kB
out/renderer/assets/rst-BJFdabDk-D3rnvzr7.js 12.04 kB
out/renderer/assets/linear-WS155L0r.js 12.11 kB
out/renderer/assets/wasm-DYwNZ-r4-DxxDPMxJ.js 12.21 kB
out/renderer/assets/hjson-CmvCiVhY-C4ioDjCi.js 12.25 kB
out/renderer/assets/razor-D5kquflS-CWY5AfCS.js 12.39 kB
out/renderer/assets/abap-BJyKdrbp-hWIyERPx.js 12.52 kB
out/renderer/assets/handlebars-B1z64YSt-Dv-6oFzD.js 12.52 kB
out/renderer/assets/apache-BUbWdjM2-Drf7fe4P.js 12.66 kB
out/renderer/assets/vesper-B5lCMuIY-BEIZAJku.js 12.88 kB
out/renderer/assets/pgsql-BYm17UA1-Cvj1smQI.js 12.93 kB
out/renderer/assets/protobuf-BOGxqb2m-yTWn-oM_.js 12.98 kB
out/renderer/assets/bat-D2y8Uewi-Dag26b2Z.js 13.09 kB
out/renderer/assets/fish-CaZR_ofm-B5raXkjs.js 13.24 kB
out/renderer/assets/elixir-Lipcswxh-DnZCznfr.js 13.27 kB
out/renderer/assets/v-wnWoBHkK-Day6RbRE.js 13.41 kB
out/renderer/assets/AboutHelpSettingsPanel-BY1_9D5h.js 13.48 kB
out/renderer/assets/vitesse-light-DQXFiRFx-ClS7zZun.js 13.81 kB
out/renderer/assets/aurora-x-9vUYYjHV-Ctpf2PFJ.js 13.85 kB
out/renderer/assets/vitesse-black-cwK5rWgD-xb5LTMEn.js 13.87 kB
out/renderer/assets/vitesse-dark-CsfgW0oH-DRstyp0V.js 13.96 kB
out/renderer/assets/ShortcutsSettingsPanel-C0YAbSVE.js 14.15 kB
out/renderer/assets/pug-D0UckVoV-DTpPZPyJ.js 14.16 kB
out/renderer/assets/synthwave-84-LX1B8mRG-p2n15IVZ.js 14.23 kB
out/renderer/assets/github-light-default-BRmIzqWc-BssP3fWL.js 14.36 kB
out/renderer/assets/clarity-AxkJPY5c-DxQsyzW4.js 14.48 kB
out/renderer/assets/github-light-high-contrast-BUKIZOxn-ddD8WB0C.js 14.49 kB
out/renderer/assets/github-dark-dimmed-DoIdqTey-DVepSQBv.js 14.64 kB
out/renderer/assets/github-dark-default-CwWjzI40-RrXCLjwC.js 14.64 kB
out/renderer/assets/chunk-PWAF6VOD-B_xWZeTJ.js 14.76 kB
out/renderer/assets/github-dark-high-contrast-bFLNMLvK-BXKopV-S.js 14.80 kB
out/renderer/assets/diagram-S7CK7UJ4-HxWCw845.js 14.98 kB
out/renderer/assets/gnuplot-Dw3FRha5-D3M-xWAW.js 14.98 kB
out/renderer/assets/rust-C0Ih6nmc-NSyRjuKn.js 15.27 kB
out/renderer/assets/actionscript-3-O8jnLcvn-D3y3sBt8.js 15.41 kB
out/renderer/assets/luau-BzxGbjcn-DRqn7wkf.js 15.64 kB
out/renderer/assets/nix-zl191KBb-BbUoemNK.js 15.85 kB
out/renderer/assets/lua-BwTLfNCK-CwVt-Bud.js 16.04 kB
out/renderer/assets/abap-FvPUHv8B-BHnmFpIs.js 16.05 kB
out/renderer/assets/solidity-96fhzG-Q-BeFLe5_3.js 16.28 kB
out/renderer/assets/matlab-8ZQqHKQX-BhcRPlgD.js 16.29 kB
out/renderer/assets/cue-eHSfcyUV-DT6a09xa.js 16.40 kB
out/renderer/assets/kusto-D8vBBISG-MUlttHhz.js 16.45 kB
out/renderer/assets/powerquery-B7BjLMcF-BkVSWPtX.js 16.49 kB
out/renderer/assets/elixir-B1NaQxrO-C2KIFym7.js 16.50 kB
out/renderer/assets/react-DrereneL.js 16.50 kB
out/renderer/assets/odin-B-YqhW9I-B4nLtb__.js 16.71 kB
out/renderer/assets/solidity-Dpt2BLAA-BD_YSA0r.js 16.98 kB
out/renderer/assets/bird2-BMkLvc4r-DJUqkWBZ.js 17.17 kB
out/renderer/assets/kanagawa-wave-CM_o6oKV-BiCJ69Qw.js 17.32 kB
out/renderer/assets/kanagawa-lotus-Cj3XDpoG-DyyQ5zzx.js 17.32 kB
out/renderer/assets/kanagawa-dragon-D_NPnIy_-DsTAhavE.js 17.33 kB
out/renderer/assets/cynefinDiagram-5FMLGOSQ-Mcy-XBmQ.js 17.61 kB
out/renderer/assets/stateDiagram-D77RDMKH-Ce6yHAkh.js 18.11 kB
out/renderer/assets/chunk-L3NEJ4N5-BTj7ZXs5.js 18.16 kB
out/renderer/assets/liquid-D_7zu7XR-CXZ_m_sQ.js 18.46 kB
out/renderer/assets/svelte-DUGqPubT-Cg5Umebm.js 18.60 kB
out/renderer/assets/graphql-ObxcfEHH-CE1BS9vi.js 18.65 kB
out/renderer/assets/jsonMode-DdQpkp6D-ryNuzreF.js 18.79 kB
out/renderer/assets/material-theme-q28j4V_0-D4sopXzV.js 18.81 kB
out/renderer/assets/material-theme-ocean-DAvlEnSm-BNlRm3bO.js 18.83 kB
out/renderer/assets/material-theme-darker-CHEuMVX9-_HyRAmHW.js 18.83 kB
out/renderer/assets/material-theme-lighter-DY4T1_JG-CCMtFHoU.js 18.84 kB
out/renderer/assets/material-theme-palenight-CqDFvQ-v-BCUQCRA9.js 18.84 kB
out/renderer/assets/gherkin-BcjDDslM-Vee2W7jW.js 19.20 kB
out/renderer/assets/gdscript-CekKc1U_-DVYWisvp.js 19.21 kB
out/renderer/assets/groovy-DH3arekX-Ck1fPmLU.js 19.38 kB
out/renderer/assets/mdc-D-U85KZE-Da7cgSf8.js 19.85 kB
out/renderer/assets/diagram-VSXAHHWV-CYmzzVvl.js 20.01 kB
out/renderer/assets/move-CmExnPjU-CnmvqY5Z.js 20.22 kB
out/renderer/assets/ayu-dark-BeByMiYw-DMYpUV0j.js 20.27 kB
out/renderer/assets/ayu-mirage-C1jJ6Jfs-BNm6kYch.js 20.28 kB
out/renderer/assets/ayu-light-DmTZeN54-D_6LeGBp.js 20.35 kB
out/renderer/assets/glimmer-js-D9ZnrcNN-C_sv3tNP.js 20.44 kB
out/renderer/assets/glimmer-ts-DuhFj6qR-3vTIoInh.js 20.44 kB
out/renderer/assets/viml-DPVJ_KYe-7wnrr3JE.js 20.56 kB
out/renderer/assets/nushell-Cd8Dwa1M-BCLkjKh-.js 20.62 kB
out/renderer/assets/snazzy-light-B8znOqT6-B6ELzxzT.js 20.97 kB
out/renderer/assets/dagre-GXQ25YYZ-Dv6RL6rv.js 21.01 kB
out/renderer/assets/dracula-DeMffiYO-APAP0ZMF.js 21.26 kB
out/renderer/assets/dracula-soft-BnfvpcDv-DVKzJw-a.js 21.27 kB
out/renderer/assets/twig-7zjsCqYg-DxthtzQl.js 21.44 kB
out/renderer/assets/freemarker2-enkimFXA-0bWMkoTL.js 21.73 kB
out/renderer/assets/PersonalizationSettingsPanel-Cu4FSwRk.js 21.87 kB
out/renderer/assets/rose-pine-CEVQiaW0-B9wdQBUK.js 21.94 kB
out/renderer/assets/rose-pine-moon-B9bum7Ik-DV9wvzoF.js 21.96 kB
out/renderer/assets/rose-pine-dawn-2Q5fMoJp-9IwweoZR.js 21.96 kB
out/renderer/assets/powershell-ycAdP1r9-zBR5oucw.js 22.11 kB
out/renderer/assets/common-lisp-DcFHcTKh-ChYmZcml.js 22.81 kB
out/renderer/assets/gruvbox-dark-hard-gOzMLqif-1ffvJLYJ.js 22.83 kB
out/renderer/assets/gruvbox-dark-soft-CKvURktN-CTfPcZGR.js 22.83 kB
out/renderer/assets/gruvbox-light-hard-CMa_c48J-CiF1HZj7.js 22.84 kB
out/renderer/assets/gruvbox-light-soft-yfAGJYsX-DOBtCxv4.js 22.84 kB
out/renderer/assets/gruvbox-dark-medium-BQSVGwWb-XqSAmK9Z.js 22.84 kB
out/renderer/assets/gruvbox-light-medium-B9S5UNr1-DHFc3pZf.js 22.84 kB
out/renderer/assets/nim-CD961Jl3-GNJlFYEB.js 22.91 kB
out/renderer/assets/cadence-C2JmJjDr-CNsAMskX.js 23.87 kB
out/renderer/assets/sql-C_oMiPb0-eqfeiYFN.js 23.87 kB
out/renderer/assets/wit-CXpQol8Q-CgnX2mEY.js 24.11 kB
out/renderer/assets/templ-gFcHJbF7-CtGorYnD.js 24.38 kB
out/renderer/assets/astro-kV3HjkbP-DWkULF6m.js 24.46 kB
out/renderer/assets/vhdl-Ci4REirV-79GIduW4.js 24.46 kB
out/renderer/assets/purescript-DCsDAdnX-BHSmjuaf.js 24.97 kB
out/renderer/assets/vue-Cuk7Vimg-QGoH4Fqf.js 25.25 kB
out/renderer/assets/surrealql-GFjNBdcm-R--ilK2-.js 25.34 kB
out/renderer/assets/one-light-CMfGrl_P-BVh_cQxI.js 25.49 kB
out/renderer/assets/night-owl-light-CAEa42tO-C8k75joB.js 26.10 kB
out/renderer/assets/system-verilog-D_bVVpLE-D0CLrP2v.js 26.41 kB
out/renderer/assets/angular-html-BrLPO2Fv-DN2wP65A.js 26.48 kB
out/renderer/assets/typespec-D5WYs8C4-6KJNyQgC.js 26.72 kB
out/renderer/assets/nord-DlCAGdjr-Dq7-29jJ.js 26.91 kB
out/renderer/assets/codeql-DidIlXer-BXtoeXUa.js 27.08 kB
out/renderer/assets/scss-BhK8Ovpn-Bro8Wnj5.js 27.52 kB
out/renderer/assets/coffee-DrMWXxy6-DqnaejKc.js 27.68 kB
out/renderer/assets/java-DaupjRFz-BmH8F4Xu.js 27.69 kB
out/renderer/assets/razor-B2hRAVmz-DFoDL3Rc.js 27.72 kB
out/renderer/assets/apl-C42PBett-BV6hrP71.js 27.87 kB
out/renderer/assets/GeneralSettingsPanel-Dhp6WB35.js 27.94 kB
out/renderer/assets/chunk-SVP7TREG-Csis0frL.js 28.38 kB
out/renderer/assets/fsharp-BHFgIxsY-DiAA2GQQ.js 28.48 kB
out/renderer/assets/marko-DXKHPFnH-COCjppDO.js 28.74 kB
out/renderer/assets/night-owl-DegwdXaM-CAHni64B.js 29.11 kB
out/renderer/assets/c3-BOs4FlGA-BpND_1Oc.js 29.17 kB
out/renderer/assets/MemorySettingsPanel-Bxb72epL.js 29.75 kB
out/renderer/assets/crystal-DlzoxO28-jPE7UMPv.js 29.84 kB
out/renderer/assets/applescript-ij24h7OO-CfuGzwAx.js 29.87 kB
out/renderer/assets/tsMode-SiG-Sp2b-uJP6tDKM.js 30.55 kB
out/renderer/assets/pierre-dark-Daa2n9wr-jVOIM079.js 30.74 kB
out/renderer/assets/pierre-light-D_hZXvUj-B-I8Fy7S.js 30.75 kB
out/renderer/assets/pierre-dark-soft-BhNM3pgh-C21QEiBh.js 30.76 kB
out/renderer/assets/pierre-light-soft-BVrSv6ZG-B45EorIo.js 30.76 kB
out/renderer/assets/ishikawaDiagram-5VMMS53U-DTo9OLUj.js 30.78 kB
out/renderer/assets/pierre-dark-tritanopia-CGKuoAZi-BJEQ1Wx0.js 30.78 kB
out/renderer/assets/pierre-light-tritanopia-BiDEaWaH-CKL7emT7.js 30.78 kB
out/renderer/assets/pierre-dark-protanopia-deuteranopia-Cs1lz9lE-CdeAx_-I.js 30.82 kB
out/renderer/assets/pierre-light-protanopia-deuteranopia-BM0RLBB_-BCikKBoA.js 30.82 kB
out/renderer/assets/stylus-ZCryC73G-BNz0cuP0.js 31.29 kB
out/renderer/assets/diagram-VX7I27RA-BUBTTGEV.js 31.44 kB
out/renderer/assets/scala-BBsC3Oz3-BEVUQysb.js 31.48 kB
out/renderer/assets/mermaid-D6CQfUGU-BIokhzAy.js 33.37 kB
out/renderer/assets/poimandres-EIt_jotH-C68Hghjr.js 33.69 kB
out/renderer/assets/kanban-definition-UXKFOSKX---wf-Z-L.js 33.91 kB
out/renderer/assets/one-dark-pro-DR9QpZM3-DWXkK0aA.js 33.98 kB
out/renderer/assets/mermaid-parser.core-D9uZC-Qx.js 34.66 kB
out/renderer/assets/haxe-BWT5wSyZ-Bc0s339E.js 35.38 kB
out/renderer/assets/nginx-DhtViCEk-D_dlMkM2.js 35.58 kB
out/renderer/assets/houston-BdS5A_yD-B7tBMzJ_.js 35.61 kB
out/renderer/assets/tokyo-night-BS1_DFu7-DM9rDy1i.js 35.86 kB
out/renderer/assets/erlang-Dd62d0xZ-Dn5zmcXH.js 37.71 kB
out/renderer/assets/rough.esm-BX5QqLti.js 38.33 kB
out/renderer/assets/mindmap-definition-YA3MSWOX-CUizKTTG.js 38.59 kB
out/renderer/assets/cobol-D7RZfOK--CiunptQS.js 39.37 kB
out/renderer/assets/journeyDiagram-3NMN7TZE-_ouxsy-Y.js 40.13 kB
out/renderer/assets/julia-ZzkUXm0W-B05POQ1y.js 40.32 kB
out/renderer/assets/lspLanguageFeatures-DK9zifhE-DeEqLmGO.js 40.76 kB
out/renderer/assets/asm-DYVIzeEk-BdsmPhUV.js 40.91 kB
out/renderer/assets/haskell-Ba6HbLj8-C6aYe-tt.js 41.78 kB
out/renderer/assets/sankeyDiagram-P5KCCOFB-CBzEcjmt.js 41.83 kB
out/renderer/assets/shellscript-B69gKcss-BCpQfG2V.js 41.97 kB
out/renderer/assets/wardleyDiagram-VM6X3IG4-BHD45b_a.js 42.40 kB
out/renderer/assets/perl-Dv6Cq_FS-Qpttd9ok.js 43.55 kB
out/renderer/assets/pierre-dark-vibrant-BMsyYliQ-CUZmeWj8.js 44.96 kB
out/renderer/assets/pierre-light-vibrant-CWzM4Pa_-CEqHPLvH.js 44.97 kB
out/renderer/assets/ruby-DWcdJvcx-BvnxDDlL.js 46.50 kB
out/renderer/assets/go-BOpYl7fu-B0liyMKV.js 47.02 kB
out/renderer/assets/apex-B48ZHHzN-D3dPjLM0.js 47.18 kB
out/renderer/assets/catppuccin-mocha-cPsH7Lup-B1aKBIop.js 47.46 kB
out/renderer/assets/catppuccin-latte-DcEecI7n-DvwHc2S5.js 47.46 kB
out/renderer/assets/catppuccin-frappe-BdOW2tdI-DECg27lJ.js 47.46 kB
out/renderer/assets/catppuccin-macchiato-Bvz_yAZY-jtZXG4Wb.js 47.47 kB
out/renderer/assets/ada-BT0j8ine-D-uzx6Mf.js 48.28 kB
out/renderer/assets/d-B1HbH29p-m8z4Mb03.js 49.10 kB
out/renderer/assets/css-NFwnoVns-CFobPrFm.js 49.49 kB
out/renderer/assets/imba-C-oGt11l-BHSoATU3.js 50.13 kB
out/renderer/assets/requirementDiagram-BXWQKSXE-DF7sZnvW.js 51.39 kB
out/renderer/assets/erDiagram-RLTQ6QDP-B5kVdOsu.js 52.41 kB
out/renderer/assets/browserify-C9uP_nIr.js 52.80 kB
out/renderer/assets/gitGraphDiagram-WWUBYQGX-CKeLSGy2.js 53.82 kB
out/renderer/assets/everforest-dark-gCM8XhEI-BkULplJM.js 53.94 kB
out/renderer/assets/everforest-light-DhK35DkL-BWsjYbyt.js 53.95 kB
out/renderer/assets/timeline-definition-24CTP7MA-B52UdTo1.js 54.31 kB
out/renderer/assets/wikitext-CQNrXTmv-DmewJRQe.js 56.09 kB
out/renderer/assets/stata-BUONSdsT-BMio-HiN.js 57.23 kB
out/renderer/assets/quadrantDiagram-AXDQQJYC-XlPTPqNY.js 58.21 kB
out/renderer/assets/html-Dq1ixqYb-ikBZyjXh.js 58.25 kB
out/renderer/assets/chunk-OSK3NFVY-nxsF_2l9.js 58.56 kB
out/renderer/assets/ocaml-CUdGm1iq-CKVIVKNi.js 62.65 kB
out/renderer/assets/ballerina-CpYcPBJI-DUjr1anJ.js 64.89 kB
out/renderer/assets/markdown-BSrOIEaF-DTmUkziu.js 65.52 kB
out/renderer/assets/model-settings-source-Beswzs9F.js 65.56 kB
out/renderer/assets/chunk-IMKFNOWR-DMjCUg_X.js 67.22 kB
out/renderer/assets/mojo-BNaitZ-7-CNyrqsBu.js 70.00 kB
out/renderer/assets/python-Cld0Iwtn-Dc00FHqu.js 70.17 kB
out/renderer/assets/blockDiagram-I7D4REHJ-Dt5Nk8hT.js 71.01 kB
out/renderer/assets/bsl-CtP8370o-2oWsl7ro.js 71.07 kB
out/renderer/assets/c-B4augMcT-BnnD0pzw.js 72.57 kB
out/renderer/assets/chunk-75Z2AOVW-g5kYAQXn.js 72.67 kB
out/renderer/assets/latex-g2Pus_l2-jmHDzxNT.js 72.85 kB
out/renderer/assets/vyper-C1nscAY5-D__MF1ly.js 74.85 kB
out/renderer/assets/ModelSettingsPanel-gTozZxuT.js 76.73 kB
out/renderer/assets/xychartDiagram-S5SC5T6Z-C21HkgFK.js 76.79 kB
out/renderer/assets/chunk-GMAD6QVW-Dnp68ZTh.js 77.76 kB
out/renderer/assets/hack-B1ag3l4X-tyEt6EwS.js 80.58 kB
out/renderer/assets/src-DsgKm1PY.js 83.16 kB
out/renderer/assets/chunk-TICWLB2K-ySi6rtZ2.js 83.88 kB
out/renderer/assets/fortran-free-form-CsgtTAg3-DgulwIAi.js 89.20 kB
out/renderer/assets/vennDiagram-4TSXK5OY-BPjUg06I.js 89.26 kB
out/renderer/assets/csharp-YOPVljXo-BWs4xY0W.js 89.91 kB
out/renderer/assets/racket--xYIV9-n-BvSlC08n.js 92.62 kB
out/renderer/assets/swift-CgzYrGBX-pR4OWhlc.js 94.14 kB
out/renderer/assets/less-BGhX8xko-fBMPrmVZ.js 99.00 kB
out/renderer/assets/chunk-SHT3W25Y-Ck-aB5Uu.js 104.10 kB
out/renderer/assets/objective-c-CmcWiAft-BnWIdCJE.js 105.62 kB
out/renderer/assets/blade-Bq8WPf0E-BvQh51Wk.js 105.98 kB
out/renderer/assets/c4Diagram-7LVT6UL2-DaNs-p8D.js 107.96 kB
out/renderer/assets/php-X15vsN4N-BP3XzobV.js 111.49 kB
out/renderer/assets/dagre-CzI-C3sx.js 122.98 kB
out/renderer/assets/ganttDiagram-EL5Y4UJY-DRVfLdn1.js 123.77 kB
out/renderer/assets/graphlib-CoAzpIqe.js 127.44 kB
out/renderer/assets/asciidoc-DJWalVNM-CUrf9IhF.js 146.30 kB
out/renderer/assets/mdx-C_Ato9sK-Dw7GE6T0.js 146.49 kB
out/renderer/assets/editor.main-POO0Vbhx-Ckm40Z4F.js 161.32 kB
out/renderer/assets/cose-bilkent-JH36ORCC-CA60tZpq.js 165.63 kB
out/renderer/assets/objective-cpp-BggTCHV8-C8vqT1kg.js 172.18 kB
out/renderer/assets/javascript-AfZCJ9Uc-CBeTSWR3.js 175.31 kB
out/renderer/assets/tsx-BOlKGefW-DmB6MG_T.js 176.00 kB
out/renderer/assets/jsx-CPd9p36x-irxTBUcS.js 178.26 kB
out/renderer/assets/sequenceDiagram-WJ2MYXX4-DaqVHWoY.js 190.05 kB
out/renderer/assets/typescript-4sRsPriL-tBSOa5KM.js 191.16 kB
out/renderer/assets/mermaid.core-DHHVRzzC.js 191.57 kB
out/renderer/assets/angular-ts-YduEkmVa-D3Aj057l.js 193.82 kB
out/renderer/assets/vue-vine-DXhYlaTf-F_V-T9AA.js 201.27 kB
out/renderer/assets/SourceSelect-5sFypDRe.js 203.20 kB
out/renderer/assets/chunk-4HAMMTFA-DK0AoKOa.js 210.32 kB
out/renderer/assets/swimlanes-42K2YHIH-DjvqEQqr.js 265.51 kB
out/renderer/assets/wolfram-BVoTES0L-CQ8sM4xY.js 267.14 kB
out/renderer/assets/chunk-DU6HZSFF-CaRG2Ier.js 271.07 kB
out/renderer/assets/architectureDiagram-5GKGNRK7-DZMUubDN.js 318.23 kB
out/renderer/assets/katex-Y9ra-ykP.js 497.35 kB
out/renderer/assets/texmath-Bkeg3ZLn.js 594.25 kB
out/renderer/assets/wasm-CVjZL0jK-Dt2swvi2.js 622.62 kB
out/renderer/assets/cpp-9jHBA231-CBf5U9Ag.js 626.73 kB
out/renderer/assets/app-FL-TA-H2.js 677.26 kB
out/renderer/assets/emacs-lisp-DjtOxg4j-nrVZCSuO.js 780.06 kB
out/renderer/assets/cytoscape.esm-bVGYt-iW.js 869.69 kB
out/renderer/assets/ArtifactPierreDiff-fqHylWj_.js 1,119.58 kB
out/renderer/assets/chunk-FOHPRMQF-BCllzKJD.js 1,154.18 kB
out/renderer/assets/index-_D-I1Mwx.js 3,780.38 kB
out/renderer/assets/editor.api2-dPwET-7w-BYIQ9bT3.js 4,889.23 kB
✓ built in 2.45s
npm notice
npm notice New major version of npm available! 11.9.0 -> 12.2.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v12.2.0
npm notice To update run: npm install -g npm@12.2.0
npm notice
@@ -0,0 +1,9 @@
npm warn Unknown env config "http-proxy". This will stop working in the next major version of npm.
> ycode@0.0.10 test:conversation
> esbuild scripts/conversation-renderer-regression.tsx scripts/composer-regression.tsx scripts/completed-fold-regression.tsx scripts/streaming-footer-regression.tsx --bundle --platform=node --format=cjs --jsx=automatic --loader:.css=text --out-extension:.js=.cjs --outdir=.tmp/conversation-tests --log-level=error && node .tmp/conversation-tests/conversation-renderer-regression.cjs && node .tmp/conversation-tests/composer-regression.cjs && node .tmp/conversation-tests/streaming-footer-regression.cjs && node .tmp/conversation-tests/completed-fold-regression.cjs
Conversation specialized renderer SSR regression checks passed
Composer/message action regression cases passed
streaming-footer-regression: persistent stream status, delayed/paused rotation, brand/locale mapping, source CSS, and completed model footer passed
Completed-fold model and SSR regression checks passed
+5 -5
View File
@@ -557,18 +557,18 @@ const todoWriteRenderer = renderToStaticMarkup(
}} }}
/> />
) )
assert.match(todoWriteRenderer, /进度更新 1\/3/) assert.match(todoWriteRenderer, /进度 1\/3/)
assert.match(todoWriteRenderer, /Completed task/) assert.match(todoWriteRenderer, /Completed task/)
assert.match(todoWriteRenderer, /Writing the active task/) assert.match(todoWriteRenderer, /Writing the active task/)
assert.match(todoWriteRenderer, /Pending task/) assert.match(todoWriteRenderer, /Pending task/)
assert.match(todoWriteRenderer, /cb-plan-task--completed/) assert.match(todoWriteRenderer, /cr-tool-plan-task__text--done/)
assert.match(todoWriteRenderer, /cb-plan-task--in_progress/) assert.match(todoWriteRenderer, /cr-tool-plan-task__text--running/)
assert.match(todoWriteRenderer, /cb-plan-task--pending/) assert.match(todoWriteRenderer, /data-task-status="pending"/)
const todoStepsRenderer = renderToStaticMarkup( const todoStepsRenderer = renderToStaticMarkup(
<ToolCallRenderer tool={{ id: 'todo-write-steps', name: 'TodoWrite', status: 'running', args: { steps: [{ title: 'Compatibility step', status: 'doing' }] } }} defaultExpanded /> <ToolCallRenderer tool={{ id: 'todo-write-steps', name: 'TodoWrite', status: 'running', args: { steps: [{ title: 'Compatibility step', status: 'doing' }] } }} defaultExpanded />
) )
assert.match(todoStepsRenderer, /Compatibility step/) assert.match(todoStepsRenderer, /Compatibility step/)
assert.match(todoStepsRenderer, /cb-plan-task--in_progress/) assert.match(todoStepsRenderer, /cr-tool-plan-task__text--running/)
const fileListRenderer = renderToStaticMarkup( const fileListRenderer = renderToStaticMarkup(
<ToolCallRenderer tool={{ id: 'files-1', name: 'Glob', status: 'success', args: { pattern: 'src/**/*.ts' }, result: { result: { results: [{ path: 'src/main.ts' }, { path: 'src/util.ts' }] } } }} defaultExpanded /> <ToolCallRenderer tool={{ id: 'files-1', name: 'Glob', status: 'success', args: { pattern: 'src/**/*.ts' }, result: { result: { results: [{ path: 'src/main.ts' }, { path: 'src/util.ts' }] } } }} defaultExpanded />
) )
+4 -4
View File
@@ -22,7 +22,7 @@ if (domModule) {
const tests = [ const tests = [
'model-selection-alignment-regression.ts', 'model-settings-alignment-regression.ts', 'session-factory-alignment-regression.ts', 'model-selection-alignment-regression.ts', 'model-settings-alignment-regression.ts', 'session-factory-alignment-regression.ts',
'session-composer-alignment-regression.tsx', 'alignment-conversation-regression.tsx', 'question-answer-shell-regression.tsx', 'session-composer-alignment-regression.tsx', 'alignment-conversation-regression.tsx', 'question-answer-shell-regression.tsx',
'runtime-authorization-regression.ts', 'permission-intercept-regression.tsx' 'runtime-authorization-regression.ts', 'permission-intercept-regression.tsx', 'todo-write-regression.tsx'
] ]
for (const file of tests) { for (const file of tests) {
const format = file.endsWith('.tsx') ? 'cjs' : 'esm' const format = file.endsWith('.tsx') ? 'cjs' : 'esm'
@@ -42,7 +42,7 @@ for (const file of ['scripts/build-runtime-regressions.mjs', '.tmp/claude-approv
// Optional DOM tests use separately installed jsdom, not Electron/CLI or an upstream model. // Optional DOM tests use separately installed jsdom, not Electron/CLI or an upstream model.
if (domModule) { if (domModule) {
for (const file of ['task-popconfirm-regression', 'question-interaction-regression', 'question-identity-regression', 'question-composer-dom-regression', 'completed-fold-nested-regression', 'completed-fold-native-identity-regression', 'permission-intercept-dom-regression', 'reasoning-follow-dom-regression']) { for (const file of ['task-popconfirm-regression', 'question-interaction-regression', 'question-identity-regression', 'question-composer-dom-regression', 'completed-fold-nested-regression', 'completed-fold-native-identity-regression', 'permission-intercept-dom-regression', 'reasoning-follow-dom-regression', 'todo-write-dom-regression']) {
const outfile = `.tmp/alignment-tests/${file}.cjs` const outfile = `.tmp/alignment-tests/${file}.cjs`
await build({ entryPoints: [`scripts/${file}.tsx`], outfile, bundle: true, platform: 'node', format: 'cjs', jsx: 'automatic', loader: { '.css': 'empty' }, logLevel: 'warning' }) await build({ entryPoints: [`scripts/${file}.tsx`], outfile, bundle: true, platform: 'node', format: 'cjs', jsx: 'automatic', loader: { '.css': 'empty' }, logLevel: 'warning' })
const result = spawnSync(process.execPath, [outfile], { stdio: 'inherit', timeout: 120000 }) const result = spawnSync(process.execPath, [outfile], { stdio: 'inherit', timeout: 120000 })
@@ -56,5 +56,5 @@ if (domModule) {
console.log(JSON.stringify({ result: 'PASS', phase: 'alignment-offline', suites: tests.length + 3 })) console.log(JSON.stringify({ result: 'PASS', phase: 'alignment-offline', suites: tests.length + 3 }))
console.log(JSON.stringify(domModule console.log(JSON.stringify(domModule
? { result: 'PASS', phase: 'alignment-dom', suites: 9 } ? { result: 'PASS', phase: 'alignment-dom', suites: 10 }
: { result: 'SKIP', phase: 'alignment-dom', suites: 9, reason: 'YCODE_TEST_DOM_MODULE is not set; DOM coverage was not run. Use --require-dom to forbid this skip.' })) : { result: 'SKIP', phase: 'alignment-dom', suites: 10, reason: 'YCODE_TEST_DOM_MODULE is not set; DOM coverage was not run. Use --require-dom to forbid this skip.' }))
+97
View File
@@ -0,0 +1,97 @@
/** B05 real React TodoWrite + CompletedFold DOM tests. Synthetic data only, no native UI claim. */
import assert from 'node:assert/strict'
import { createRequire } from 'node:module'
import type { ConversationToolCall } from '../src/renderer/src/workbuddy/conversation/types'
const { JSDOM } = createRequire(process.cwd() + '/package.json')(process.env.YCODE_TEST_DOM_MODULE || 'jsdom')
const dom = new JSDOM('<html><body><main id="root"></main></body></html>', { pretendToBeVisual:true, url:'http://localhost/' })
for (const key of ['window','document','Node','Element','HTMLElement','MouseEvent','KeyboardEvent','Event','CustomEvent']) Object.defineProperty(globalThis,key,{value:dom.window[key],configurable:true})
Object.defineProperty(globalThis,'navigator',{value:dom.window.navigator,configurable:true})
Object.assign(globalThis,{IS_REACT_ACT_ENVIRONMENT:true,requestAnimationFrame:dom.window.requestAnimationFrame.bind(dom.window),cancelAnimationFrame:dom.window.cancelAnimationFrame.bind(dom.window)})
const { act } = require('react') as typeof import('react')
const { createRoot } = require('react-dom/client') as typeof import('react-dom/client')
const { CompletedFold } = require('../src/renderer/src/workbuddy/conversation/CompletedFold') as typeof import('../src/renderer/src/workbuddy/conversation/CompletedFold')
const { ToolCallRenderer } = require('../src/renderer/src/workbuddy/conversation/ToolCallRenderer') as typeof import('../src/renderer/src/workbuddy/conversation/ToolCallRenderer')
const { ThinkingBlock } = require('../src/renderer/src/workbuddy/conversation/SpecializedBlocks') as typeof import('../src/renderer/src/workbuddy/conversation/SpecializedBlocks')
const root = createRoot(document.getElementById('root')!)
let checks = 0
function equal(actual:unknown, expected:unknown, label:string) { assert.deepEqual(actual,expected,label);checks++ }
const tasks = (count=7, active=4) => Array.from({length:count},(_,i)=>({id:String(i+1),content:`Task ${i+1}`,activeForm:`Working ${i+1}`,status:i===active?'in_progress':i<active?'completed':'pending'}))
const makeTool = (id:string,last:boolean,args:Record<string,unknown>={todos:tasks()}): ConversationToolCall => ({id,name:'TodoWrite',status:'executed',isLastContent:last,args,result:'Todos have been modified successfully...',output:'Todos have been modified successfully...'})
const calls: Array<{toolCallId:string;toolName:string;taskId:string|number}> = []
interface Options { session?:string; finished?:boolean; last?:boolean; remount?:number; second?:boolean; callback?:boolean; active?:number; count?:number }
function Fixture({session='session-a',finished=true,last=true,remount=0,second=false,callback=false,active=4,count=7}: Options) {
const parts = [{type:'tool',toolId:'todo-1'},{type:'reasoning',id:'thought',text:'Saved thought'},{type:'tool',toolId:'ordinary'},{type:'tool',toolId:'question'},...(second?[{type:'tool',toolId:'todo-2'}]:[]),{type:'text',text:'Answer'}]
return <CompletedFold foldId={`${session}:message`} parts={parts} turnFinished={finished}
classifyToolCall={part=>part.toolId==='question'?{role:'inline-question'}:undefined}
renderUnit={part=>part.type==='reasoning'?<ThinkingBlock text="Saved thought" isFinished />:part.type==='tool'?<div data-fixture-tool={part.toolId}>
<ToolCallRenderer key={`${part.toolId}:${remount}`} sessionId={session}
tool={part.toolId==='todo-1'?makeTool('todo-1',last,{todos:tasks(count,active)}):part.toolId==='todo-2'?makeTool('todo-2',true,{todos:tasks(6,1)}):part.toolId==='question'?{id:'question',name:'AskUserQuestion',status:'executed',metaData:{questionAnswer:{questions:[{question:'Choose?',answers:['Yes']}]}}}:{id:'ordinary',name:'UnknownOrdinaryTool',status:'executed',args:{value:1},result:'Ordinary result'}}
onTodoTaskClick={callback?request=>calls.push(request):undefined} />
</div>:<p>Answer</p>} />
}
const find=(selector:string)=>{const el=document.querySelector(selector);assert.ok(el,selector);return el}
const todo='[data-fixture-tool="todo-1"] .cr-tool-head__main'
const secondTodo='[data-fixture-tool="todo-2"] .cr-tool-head__main'
const ordinary='[data-fixture-tool="ordinary"] .cr-tool-head__main'
const question='[data-fixture-tool="question"] .cr-tool-head__main'
const thought='.cr-reasoning > .cr-collapse__header'
const outer='.cr-collapse--completed > .cr-collapse__header'
const expanded=(selector:string)=>find(selector).getAttribute('aria-expanded')
const rows=()=>Array.from(document.querySelectorAll('[data-fixture-tool="todo-1"] .cr-tool-plan-task__row')).map(el=>el.getAttribute('data-task-id'))
async function render(options:Options={}) { await act(async()=>root.render(<Fixture {...options} />)) }
async function click(selector:string) { await act(async()=>find(selector).dispatchEvent(new MouseEvent('click',{bubbles:true}))) }
async function key(selector:string,value:string) { await act(async()=>find(selector).dispatchEvent(new KeyboardEvent('keydown',{key:value,bubbles:true,cancelable:true}))) }
async function main() {
await render()
equal(expanded(outer),'false','finished outer starts closed')
await click(outer)
equal(expanded(todo),'true','last Todo initially open')
equal(rows(),['3','4','5','6','7'],'real component displays five centered rows')
equal(document.querySelector('[data-fixture-tool="todo-1"]')!.textContent!.includes('Todos have been modified successfully'),false,'SDK success hidden')
for(const selector of [ordinary,question,thought]) {equal(expanded(selector),'false',`${selector} starts independent`); await click(selector)}
await click(todo); equal(expanded(todo),'false','manual Todo close')
await render(); equal(expanded(todo),'false','same isLastContent preserves manual choice')
await click(outer); await click(outer)
equal(expanded(todo),'false','outer collapse/reopen preserves manual close')
for(const selector of [ordinary,question,thought]) equal(expanded(selector),'true','Todo state does not alter other disclosure')
await render({remount:1}); equal(expanded(todo),'false','same-value inner remount preserves manual choice')
await render({last:false,remount:1}); equal(expanded(todo),'false','true to false closes')
await render({last:true,remount:1}); equal(expanded(todo),'true','false to true opens')
await render({last:false,remount:2}); equal(expanded(todo),'false','real true-false edge concurrent with remount closes')
await click(todo); equal(expanded(todo),'true','manual open while not last')
await render({last:false,remount:3}); equal(expanded(todo),'true','same false remount does not overwrite manual open')
await click(todo); equal(expanded(todo),'false','manual close before next real edge')
await render({last:true,remount:4}); equal(expanded(todo),'true','false-true remount opens despite retained manual close')
await render({session:'session-b',last:false}); equal(expanded(outer),'false','session identity resets completed outer')
await click(outer); equal(expanded(todo),'false','same tool id in other session does not inherit')
for(const selector of [ordinary,question,thought]) equal(expanded(selector),'false','other disclosures isolated per session')
await render({session:'live-session',finished:false,last:true})
equal(expanded(todo),'true','live Todo open')
await render({session:'live-session',finished:true,last:false})
equal(expanded(todo),'false','live-to-completed reparent plus actual edge closes')
await click(outer); await click(todo)
await render({session:'live-session',finished:true,last:false,second:true})
equal(expanded(todo),'true','older manually opened snapshot stays open after later call')
equal(expanded(secondTodo),'true','later call has own disclosure')
equal(document.querySelectorAll('.cr-tool-head--todo-write').length,2,'distinct historical calls are not merged')
equal(document.querySelectorAll('[data-fixture-tool="todo-2"] .cr-tool-plan-task__row').length,5,'second snapshot independently projected')
await click(secondTodo); equal(expanded(todo),'true','closing later Todo cannot close older snapshot')
await render({session:'live-session',finished:true,last:false,second:true,active:6})
equal(rows(),['3','4','5','6','7'],'same call update refreshes window without data merge')
assert.match(find('[data-fixture-tool="todo-1"]').textContent!,/Working 7/); checks++
await render({session:'callbacks',callback:true})
await click(outer)
const row='[data-fixture-tool="todo-1"] [data-task-id="3"]'
equal(find(row).getAttribute('role'),'button','host callback exposes button semantics')
equal(find(row).getAttribute('tabindex'),'0','host callback exposes Tab target')
await click(row); await key(row,'Enter'); await key(row,' ')
equal(calls,[0,1,2].map(()=>({toolCallId:'todo-1',toolName:'TodoWrite',taskId:'3'})),'click and keyboard preserve exact event identity')
equal(expanded(todo),'true','row activation does not toggle parent')
await render({session:'callbacks',callback:false})
equal(find(row).getAttribute('role'),null,'no fake role without actual host target')
equal(find(row).getAttribute('tabindex'),null,'no fake keyboard navigation without target')
await click(row); await key(row,'Enter'); equal(calls.length,3,'callback removed disables interaction')
await act(async()=>root.unmount());dom.window.close()
console.log(`todo-write-dom: ${checks} actual TodoWrite/CompletedFold checks passed; no Electron/pixel claim`)
}
main().then(()=>process.exit(0)).catch(error=>{console.error(error);dom.window.close();process.exit(1)})
+98
View File
@@ -0,0 +1,98 @@
/** B05 projection and SSR checks; no SDK, network, or pixel claims. */
import assert from 'node:assert/strict'
import { renderToStaticMarkup } from 'react-dom/server'
import { ToolCallRenderer, getCapturedToolKind } from '../src/renderer/src/workbuddy/conversation/ToolCallRenderer'
import { normalizeTodoWrite, windowTodoTasks } from '../src/renderer/src/workbuddy/conversation/TodoWriteView'
import type { ConversationToolCall } from '../src/renderer/src/workbuddy/conversation/types'
let checks = 0
function check(name: string, run: () => void) { run(); checks++; console.log(`PASS ${name}`) }
const tasks = (count: number, active = -1, completed: number[] = []) => Array.from({ length: count }, (_, i) => ({ id: String(i + 1), content: `Task ${i + 1}`, activeForm: `Working ${i + 1}`, status: i === active ? 'in_progress' : completed.includes(i) ? 'completed' : 'pending' }))
const tool = (patch: Partial<ConversationToolCall> = {}): ConversationToolCall => ({ id: 'todo-1', name: 'TodoWrite', status: 'executed', isLastContent: true, args: { todos: tasks(7, 4) }, result: 'Todos have been modified successfully...', output: 'Todos have been modified successfully...', ...patch })
const render = (value: ConversationToolCall) => renderToStaticMarkup(<ToolCallRenderer tool={value} sessionId="synthetic" />)
const ids = (items: ReturnType<typeof tasks>) => windowTodoTasks(items).map(item => String(item.id))
const fixtures: [string, ReturnType<typeof tasks>, string[]][] = [
['six second active', tasks(6, 1), ['1','2','3','4','5']],
['seven fifth active', tasks(7, 4), ['3','4','5','6','7']],
['seven first active', tasks(7, 0), ['1','2','3','4','5']],
['seven last active', tasks(7, 6), ['3','4','5','6','7']],
['seven all pending', tasks(7), ['1','2','3','4','5']],
['seven all completed', tasks(7, -1, [0,1,2,3,4,5,6]), ['3','4','5','6','7']],
['only fourth completed', tasks(7, -1, [3]), ['2','3','4','5','6']],
['multiple active uses first', tasks(7, 1).map((x,i) => i === 5 ? {...x,status:'in_progress'} : x), ['1','2','3','4','5']],
['empty', tasks(0), []], ['one', tasks(1), ['1']], ['five', tasks(5), ['1','2','3','4','5']],
['long middle', tasks(100, 49), ['48','49','50','51','52']],
]
for (const [name,input,expected] of fixtures) check(`window ${name}`, () => {
const before = structuredClone(input)
assert.deepEqual(ids(input), expected); assert.deepEqual(input,before)
const html = render(tool({args:{todos:input}}))
assert.equal((html.match(/data-task-id=/g) || []).length,expected.length)
assert.doesNotMatch(html, /更多|下一页|上一页/)
})
for (const alias of ['TodoWrite','todo_write','todoWrite','todo-write']) check(`alias ${alias}`, () => {
assert.equal(getCapturedToolKind(alias),'todo-write')
assert.match(render(tool({name:alias})), /cr-tool-head--todo-write/)
})
check('ordinary tools do not enter dedicated view', () => {
assert.equal(getCapturedToolKind('NotTodoWrite'),undefined)
assert.doesNotMatch(render(tool({name:'NotTodoWrite'})), /cr-tool-head--todo-write/)
})
for (const raw of [false,true,'json'] as const) check(`args and raw input ${raw}`, () => {
const input = { merge:true, todos:[{id:0, content:'Done', activeForm:'Doing',status:'completed'},{id:'a',content:'Active',activeForm:'Active now',status:'in_progress'}] }
const value = tool({args: raw ? {_rawInput:raw === 'json' ? JSON.stringify(input) : input} : input})
const before = structuredClone(value), projection = normalizeTodoWrite(value)
assert.equal(projection.merge,true); assert.equal(projection.completedCount,1); assert.equal(projection.totalCount,2)
assert.equal(projection.tasks[0].id,0); assert.equal(projection.tasks[1].activeForm,'Active now')
render(value); assert.deepEqual(value,before)
})
check('historical steps retain compatible task aliases', () => {
const value = tool({args:{steps:[{title:'Legacy task',status:'doing'}]}})
assert.match(render(value),/Legacy task/); assert.match(render(value),/data-task-status="in_progress"/)
})
check('single dedicated title, modern status rows and output projection', () => {
const value = tool({args:{todos:[...tasks(3,1,[0]),{id:'failed',content:'Failed task',status:'failed'}]}})
const before = structuredClone(value), html = render(value)
assert.equal((html.match(/任务列表/g)||[]).length,1)
assert.match(html,/cr-tool-plan-task__text--done/)
assert.match(html,/cr-tool-plan-task__text--running/)
assert.match(html,/Working 2/); assert.doesNotMatch(html,/>Task 2</)
for(const status of ['completed','in_progress','pending','failed']) assert.match(html,new RegExp(`data-task-status="${status}"`))
assert.doesNotMatch(html,/cb-plan-task|Todos have been modified successfully|cr-captured-tool__output/)
assert.deepEqual(value,before)
})
check('merge progress title computed from complete list not five-row window', () => {
const html = render(tool({args:{merge:true,todos:tasks(7,6,[0,1,2,3,4,5])}}))
assert.match(html,/进度 6\/7/); assert.doesNotMatch(html,/任务列表|进度更新/)
})
check('empty running is header only with receiving cue', () => {
const html = render(tool({status:'running',args:{todos:[]}}))
assert.match(html,/接收中/); assert.equal((html.match(/任务列表/g)||[]).length,1)
assert.doesNotMatch(html,/cr-tool-plan-task__row|cr-tool-exp__content-shell/)
})
check('empty finished is header only without receiving cue', () => {
const html = render(tool({args:{todos:[]}}))
assert.match(html,/任务列表/); assert.doesNotMatch(html,/接收中|cr-tool-exp__content-shell/)
})
check('failure is dedicated error header only', () => {
const html = render(tool({status:'failed',result:'error',output:'SDK ERROR RAW'}))
assert.match(html,/生成失败/); assert.match(html,/cr-tool-head__icon--error/)
assert.doesNotMatch(html,/cr-tool-plan-task__row|cr-tool-exp__content-shell|SDK ERROR RAW/)
})
check('without host callback rows are not fake buttons', () => {
const html = render(tool())
assert.doesNotMatch(html,/cr-tool-plan-task__row[^>]*role="button"|cr-tool-plan-task__action/)
})
check('malformed raw input falls back without modifying original data', () => {
const value = tool({args:{_rawInput:'{bad JSON',todos:[null, 7, {}, {content:' '}, {id:0,content:'Safe task',activeForm:' ',status:'in_progress'}]}})
const before = structuredClone(value), projection = normalizeTodoWrite(value)
assert.equal(projection.tasks.length,1); assert.equal(projection.tasks[0].id,0)
assert.equal(projection.tasks[0].activeForm,undefined)
const html = render(value); assert.match(html,/Safe task/); assert.deepEqual(value,before)
})
check('long and escaped content stays plain text with full title', () => {
const content = 'Long task ' + '<script>not executable</script> '.repeat(20)
const html = render(tool({args:{todos:[{id:'long',content,status:'pending'}]}}))
assert.match(html,/title="Long task/); assert.match(html,/&lt;script&gt;/)
assert.doesNotMatch(html,/<script>/)
})
console.log(`todo-write: ${checks} projection and SSR checks passed; real Electron visual acceptance not covered`)
@@ -117,6 +117,9 @@ export interface SourceToolExpandableProps extends Omit<SourceToolHeaderProps, '
canExpand?: boolean canExpand?: boolean
defaultExpanded?: boolean defaultExpanded?: boolean
expand?: boolean expand?: boolean
/** Todo-only opt-in: retain actual position edges through presentation remounts. */
retainExpandEdges?: boolean
disclosureKey?: string
autoScroll?: boolean autoScroll?: boolean
disableMotion?: boolean disableMotion?: boolean
children?: ReactNode children?: ReactNode
@@ -127,6 +130,8 @@ export function SourceToolExpandable({
canExpand = true, canExpand = true,
defaultExpanded = false, defaultExpanded = false,
expand, expand,
retainExpandEdges = false,
disclosureKey,
autoScroll = false, autoScroll = false,
disableMotion = false, disableMotion = false,
children, children,
@@ -134,7 +139,9 @@ export function SourceToolExpandable({
className, className,
...headerProps ...headerProps
}: SourceToolExpandableProps) { }: SourceToolExpandableProps) {
const [expanded, setExpanded] = useDisclosureState('tool-expandable', () => defaultExpanded || expand === true) const slot = disclosureKey ? `tool-expandable:${disclosureKey}` : 'tool-expandable'
const [expanded, setExpanded] = useDisclosureState(slot, () => defaultExpanded || expand === true)
const [retainedExpand, setRetainedExpand] = useDisclosureState(`${slot}:expand-signal`, expand === true)
const priorExpand = useRef<boolean | undefined>(undefined) const priorExpand = useRef<boolean | undefined>(undefined)
const expandable = canExpand && children != null const expandable = canExpand && children != null
const open = expandable && expanded const open = expandable && expanded
@@ -143,11 +150,12 @@ export function SourceToolExpandable({
// Same transition semantics as source ToolExpandable [988614,989631). // Same transition semantics as source ToolExpandable [988614,989631).
useEffect(() => { useEffect(() => {
if (expand === undefined) return if (expand === undefined) return
const previous = priorExpand.current const previous = retainExpandEdges ? retainedExpand : priorExpand.current
priorExpand.current = expand priorExpand.current = expand
if (retainExpandEdges) setRetainedExpand(expand)
if (previous === true && !expand) setExpanded(false) if (previous === true && !expand) setExpanded(false)
else if (previous === false && expand) setExpanded(true) else if (previous === false && expand) setExpanded(true)
}, [expand]) }, [expand, retainExpandEdges, retainedExpand, setRetainedExpand, setExpanded])
// Electron 37 supports source interpolate-size CSS; disabled motion uses source setStaticState. // Electron 37 supports source interpolate-size CSS; disabled motion uses source setStaticState.
// The older-renderer JS animation-library fallback is not recovered here. // The older-renderer JS animation-library fallback is not recovered here.
useLayoutEffect(() => { useLayoutEffect(() => {
@@ -0,0 +1,96 @@
import type { ConversationToolCall } from './types'
import { SourceToolExpandable, SourceToolHeader } from './SourceToolHeader'
import { CheckIcon, FailedIcon, LoaderIcon, PlanIcon } from './RecoveredBlockIcons'
import { getToolCallPhase } from './ToolCall'
import { sourceConversationLabel as t } from './source-labels'
import { getPlanTodoStatus, selectPlanTasks, type PlanTodoItem } from './SpecializedBlocks'
import './todo-write.css'
/** Payload for the tool.todo.task.click integration. No project task is implied. */
export interface TodoTaskClickRequest {
toolCallId: string
toolName: string
taskId: string | number
}
function record(value: unknown): Record<string, unknown> {
if (typeof value === 'string') {
try { return record(JSON.parse(value)) } catch { return {} }
}
return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}
}
function text(...values: unknown[]): string {
return values.find(value => typeof value === 'string' && value.trim()) as string || ''
}
/** Read-only presentation projection; never edits tool args, results or history. */
export function normalizeTodoWrite(tool: ConversationToolCall) {
const args = tool.args ?? {}
const raw = record(args._rawInput)
const data = Object.keys(raw).length ? raw : args
const candidates = [data.todos, data.steps, args.todos, args.steps]
const rawTasks = candidates.find(Array.isArray) as unknown[] | undefined ?? []
const tasks: PlanTodoItem[] = rawTasks.flatMap((value, index) => {
const item = record(value)
const content = text(item.content, item.title, item.text, item.step)
if (!content) return []
return [{
id: typeof item.id === 'string' || typeof item.id === 'number' ? item.id : String(index + 1),
content,
activeForm: text(item.activeForm) || undefined,
status: text(item.status)
}]
})
return {
tasks,
completedCount: tasks.filter(task => getPlanTodoStatus(task.status) === 'completed').length,
totalCount: tasks.length,
merge: (data.merge ?? args.merge) === true
}
}
export function windowTodoTasks(tasks: PlanTodoItem[], maxDisplay = 5): PlanTodoItem[] {
const limit = Number.isFinite(maxDisplay) ? Math.max(1, Math.floor(maxDisplay)) : 5
return selectPlanTasks(tasks, limit).visible
}
function StatusGlyph({ status }: { status: ReturnType<typeof getPlanTodoStatus> }) {
if (status === 'completed') return <CheckIcon />
if (status === 'in_progress') return <LoaderIcon className="cr-tool-plan-task__loader" />
if (status === 'failed') return <FailedIcon />
return <span className="cr-tool-plan-task__pending" />
}
export function TodoWriteView({ tool, sessionId, defaultExpanded = false, onTaskClick }: {
tool: ConversationToolCall
sessionId?: string
defaultExpanded?: boolean
onTaskClick?: (request: TodoTaskClickRequest) => void
}) {
const { tasks, completedCount, totalCount, merge } = normalizeTodoWrite(tool)
const phase = getToolCallPhase(tool.status)
const title = merge ? t('tool.todo.progress', { completed: completedCount, total: totalCount }) : t('tool.todo.list')
if (phase === 'error') return <SourceToolHeader className="cr-tool-head--todo-write" error statusText={t('tool.todo.generateError')} />
if (!tasks.length) return <SourceToolHeader className="cr-tool-head--todo-write" icon={<PlanIcon />} statusText={title}
secondaryInfo={phase === 'running' ? t('tool.todo.receiving') : undefined} />
return <SourceToolExpandable className="cr-tool-head--todo-write" icon={<PlanIcon />} statusText={title}
defaultExpanded={defaultExpanded} expand={tool.isLastContent === true} retainExpandEdges
disclosureKey={`todo:${JSON.stringify([sessionId ?? '', tool.id])}`}>
<div className="cr-tool-plan-task">
{windowTodoTasks(tasks).map((task, index) => {
const status = getPlanTodoStatus(task.status)
const label = status === 'in_progress' ? task.activeForm || task.content : task.content
const activate = onTaskClick ? () => onTaskClick({ toolCallId: tool.id, toolName: tool.name, taskId: task.id }) : undefined
return <div key={`${typeof task.id}:${task.id}:${index}`} className={`cr-tool-plan-task__row${activate ? ' cr-tool-plan-task__row--clickable' : ''}`}
data-task-id={task.id} data-task-status={status} role={activate ? 'button' : undefined} tabIndex={activate ? 0 : undefined}
onClick={activate} onKeyDown={activate ? event => {
if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); event.stopPropagation(); activate() }
} : undefined}>
<span className={`cr-tool-plan-task__glyph cr-tool-plan-task__glyph--${status}`} aria-hidden="true"><StatusGlyph status={status} /></span>
<span className={`cr-tool-plan-task__text${status === 'completed' ? ' cr-tool-plan-task__text--done' : ''}${status === 'in_progress' ? ' cr-tool-plan-task__text--running' : ''}`} title={label}>{label}</span>
{activate && <span className="cr-tool-plan-task__action" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="m6 4 4 4-4 4" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" /></svg></span>}
</div>
})}
</div>
</SourceToolExpandable>
}
@@ -5,6 +5,7 @@ import { useEffect, useState, type KeyboardEvent, type ReactNode } from 'react'
import { AskUserQuestion } from './AskUserQuestion' import { AskUserQuestion } from './AskUserQuestion'
import { ToolCallCard, getToolCallPhase, getToolResultText } from './ToolCall' import { ToolCallCard, getToolCallPhase, getToolResultText } from './ToolCall'
import { SourceToolExpandable, SourceToolHeader } from './SourceToolHeader' import { SourceToolExpandable, SourceToolHeader } from './SourceToolHeader'
import { TodoWriteView, type TodoTaskClickRequest } from './TodoWriteView'
import { resolveSourceToolIcon } from './SourceToolIcons' import { resolveSourceToolIcon } from './SourceToolIcons'
import { SourceWriteFileView } from './SourceWriteFileView' import { SourceWriteFileView } from './SourceWriteFileView'
import { SourcePresentFilesView } from './SourcePresentFilesView' import { SourcePresentFilesView } from './SourcePresentFilesView'
@@ -12,7 +13,7 @@ import { SourceToolApprovalActions } from './SourceToolActions'
import { hasSourcePermissionRequest, type ToolApprovalControl } from './tool-approval' import { hasSourcePermissionRequest, type ToolApprovalControl } from './tool-approval'
import { sourceConversationLabel as t } from './source-labels' import { sourceConversationLabel as t } from './source-labels'
import { CheckIcon, FailedIcon } from './RecoveredBlockIcons' import { CheckIcon, FailedIcon } from './RecoveredBlockIcons'
import { getPlanTodoStatus, PlanCreateBlock, PlanUpdateBlock, TaskBlock, type PlanTodoItem, type TaskToolData } from './SpecializedBlocks' import { PlanCreateBlock, PlanUpdateBlock, TaskBlock, type PlanTodoItem, type TaskToolData } from './SpecializedBlocks'
import type { ConversationToolCall } from './types' import type { ConversationToolCall } from './types'
import type { PreviewArtifact } from '@shared/artifacts' import type { PreviewArtifact } from '@shared/artifacts'
@@ -38,6 +39,7 @@ export interface ToolCallRendererProps {
onOpenLink?: (url: string, toolCallId: string, toolName: string) => void onOpenLink?: (url: string, toolCallId: string, toolName: string) => void
onTaskApprove?: (index: number, toolCallId: string, data?: TaskToolData) => void | Promise<void> onTaskApprove?: (index: number, toolCallId: string, data?: TaskToolData) => void | Promise<void>
onTaskReject?: (index: number, toolCallId: string, data?: TaskToolData) => void | Promise<void> onTaskReject?: (index: number, toolCallId: string, data?: TaskToolData) => void | Promise<void>
onTodoTaskClick?: (request: TodoTaskClickRequest) => void
onPlanTaskClick?: (request: { toolCallId: string; toolName: string; planId: string; taskId: string | number }) => void onPlanTaskClick?: (request: { toolCallId: string; toolName: string; planId: string; taskId: string | number }) => void
onPlanView?: (request: { toolCallId: string; toolName: string; planId: string; planName: string }) => void onPlanView?: (request: { toolCallId: string; toolName: string; planId: string; planName: string }) => void
onPlanDownload?: (request: { toolCallId: string; toolName: string; planId: string; planName: string }) => void onPlanDownload?: (request: { toolCallId: string; toolName: string; planId: string; planName: string }) => void
@@ -156,6 +158,7 @@ export function ToolCallRenderer({
onOpenLink, onOpenLink,
onTaskApprove, onTaskApprove,
onTaskReject, onTaskReject,
onTodoTaskClick,
onPlanTaskClick, onPlanTaskClick,
onPlanView, onPlanView,
onPlanDownload, onPlanDownload,
@@ -188,7 +191,7 @@ export function ToolCallRenderer({
return withToolOutput(tool, view) return withToolOutput(tool, view)
} }
if (kind === 'todo-write') { if (kind === 'todo-write') {
return <TodoWriteView tool={tool} defaultExpanded={defaultExpanded} /> return <TodoWriteView key={`${sessionId ?? ''}:${tool.id}`} tool={tool} sessionId={sessionId} defaultExpanded={defaultExpanded} onTaskClick={onTodoTaskClick} />
} }
if (kind === 'exit-plan') { if (kind === 'exit-plan') {
const plan = normalizeExitPlan(tool) const plan = normalizeExitPlan(tool)
@@ -251,57 +254,6 @@ function ToolOutput({ tool }: { tool: ConversationToolCall }) {
return <pre className="cr-captured-tool__output">{tool.output}</pre> return <pre className="cr-captured-tool__output">{tool.output}</pre>
} }
function TodoWriteView({ tool, defaultExpanded }: { tool: ConversationToolCall; defaultExpanded: boolean }) {
const rawInput = parseRecord(tool.args?._rawInput)
const data = Object.keys(rawInput).length > 0 ? rawInput : tool.args ?? {}
const rawTasks = Array.isArray(data.todos) ? data.todos
: Array.isArray(data.steps) ? data.steps
: Array.isArray(tool.args?.todos) ? tool.args.todos
: Array.isArray(tool.args?.steps) ? tool.args.steps : []
const tasks = rawTasks.flatMap((value, index): PlanTodoItem[] => {
const task = asRecord(value)
const content = firstString(task.content, task.title, task.text, task.step)
if (!content) return []
return [{
id: typeof task.id === 'string' || typeof task.id === 'number' ? task.id : String(index + 1),
content,
activeForm: firstString(task.activeForm) || undefined,
status: firstString(task.status)
}]
})
const completedCount = tasks.filter(task => getPlanTodoStatus(task.status) === 'completed').length
const totalCount = typeof data.totalCount === 'number' ? data.totalCount : tasks.length
const isRunning = getToolCallPhase(tool.status) === 'running'
const isError = getToolCallPhase(tool.status) === 'error'
return <CapturedToolCard tool={tool} title={t('tool.todo.list')} defaultExpanded={defaultExpanded || tool.isLastContent === true}>
{isError
? <ToolSummary primary={t('tool.todo.generateError')} />
: tasks.length === 0
? <ToolSummary primary={t('tool.todo.list')} secondary={isRunning ? t('tool.todo.receiving') : undefined} />
: <div className="cr-captured-tool__details">
<ToolSummary primary={data.merge === true ? t('tool.todo.progress', { completed: completedCount, total: totalCount }) : t('tool.todo.list')} />
<div className="cb-plan-todolist"><div className="cb-plan-todolist-wrapper">
{tasks.map(task => {
const status = getPlanTodoStatus(task.status)
return <div className={`cb-plan-task cb-plan-task--${status}`} key={task.id}>
<span className="cb-plan-task-status" aria-hidden="true">{todoStatusMark(status)}</span>
<span className="cb-plan-task-text">{status === 'in_progress' ? task.activeForm || task.content : task.content}</span>
</div>
})}
</div></div>
</div>}
<ToolOutput tool={tool} />
</CapturedToolCard>
}
function todoStatusMark(status: ReturnType<typeof getPlanTodoStatus>): string {
if (status === 'completed') return 'OK'
if (status === 'in_progress') return '>'
if (status === 'failed') return 'x'
return '-'
}
function ToolSummary({ primary, secondary }: { primary: string; secondary?: string }) { function ToolSummary({ primary, secondary }: { primary: string; secondary?: string }) {
return <div className="cr-captured-tool__summary"><strong>{primary}</strong>{secondary && <span>{secondary}</span>}</div> return <div className="cr-captured-tool__summary"><strong>{primary}</strong>{secondary && <span>{secondary}</span>}</div>
} }
@@ -1344,7 +1344,7 @@ const zhCN = {
"tool.todo.generateError": "生成失败", "tool.todo.generateError": "生成失败",
"tool.todo.list": "任务列表", "tool.todo.list": "任务列表",
"tool.todo.receiving": "接收中...", "tool.todo.receiving": "接收中...",
"tool.todo.progress": "进度更新 {completed}/{total}", "tool.todo.progress": "进度 {completed}/{total}",
"tool.question.waitingAnswer": "等待你的回答...", "tool.question.waitingAnswer": "等待你的回答...",
"tool.question.generating": "问题生成中...", "tool.question.generating": "问题生成中...",
"tool.question.cancelled": "已取消提问", "tool.question.cancelled": "已取消提问",
@@ -0,0 +1,18 @@
/* Modern TodoWrite PlanTask contract from REA ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94.
See docs/协作/TodoWrite样式-REA核对-2026-10-21.md. Not the older cb-plan-task branch. */
.cr-tool-plan-task { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cr-tool-plan-task__row { display: flex; align-items: flex-start; gap: 8px; min-width: 0; border-radius: 8px; padding: 1px 4px 2px; }
.cr-tool-plan-task__glyph { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 16px; width: 16px; min-height: 25px; color: var(--cr-text-tertiary); }
.cr-tool-plan-task__glyph svg { width: 16px; height: 16px; }
.cr-tool-plan-task__pending { display: block; width: 12px; height: 12px; box-sizing: border-box; border: 1px solid currentColor; border-radius: 50%; }
.cr-tool-plan-task__text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; line-height: 1.75; color: var(--cr-text-primary); }
.cr-tool-plan-task__text--done { color: var(--cr-text-tertiary); text-decoration: line-through; }
.cr-tool-plan-task__text--running { font-weight: 600; }
.cr-tool-plan-task__glyph--failed { color: var(--cr-status-error); }
.cr-tool-plan-task__loader { animation: cr-tool-plan-task-spin 2s linear infinite; }
.cr-tool-plan-task__row--clickable { cursor: pointer; }
.cr-tool-plan-task__row--clickable:hover { background: var(--cr-bg-hover); }
.cr-tool-plan-task__row--clickable:focus-visible { outline: 2px solid var(--cr-text-primary); outline-offset: 1px; }
.cr-tool-plan-task__action { display: inline-flex; align-items: center; flex: 0 0 16px; min-height: 25px; color: var(--cr-text-tertiary); }
@keyframes cr-tool-plan-task-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .cr-tool-plan-task__loader { animation: none; } }