AGC 画布素材卡「引用」点了没反应:插入事件只挂在策划输入盒,DirectProject 路径下句柄恒为空(拖拽批量引用同样失效) #602

Open
opened 2026-10-03 18:05:54 +08:00 by suzmii · 1 comment
Member

现象

AGC 资源画布上选中一张已登记素材,选中工具条弹出后点「引用」(图标 @、可见文案「引用」,title="引用")没有任何反应:聊天输入框里不出现 @素材名 芯片,也没有任何提示。

复现与实测(客户端 attach,2026-10-03)

  • 客户端 http://127.0.0.1:3080(CDP 9222 attach)。挂载的对话面只有 DirectProject:section.project-chat-surface.is-direct-codex,输入区 form.project-chat-composer.is-direct-codex 与 [contenteditable][aria-label="陶泥儿对话内容"] 都在位(没有策划面 project-chat-surface:not(.is-direct-codex))。
  • 在页面里派发按钮实际使用的那两种事件(负载与 dispatchResourceReferenceInsert / dispatchResourceReferenceInsertMany 逐字同形):
    • agc-resource-reference-insert → 芯片数 0 → 0
    • agc-resource-reference-insert-many → 芯片数 0 → 0
  • 结论:画布侧派发是真的、输入区也真的挂着,但没有任何消费者把它落进草稿。

根因

引用插入事件只有一个消费者,而它绑在一个「只在策划链路里才会被挂上」的 ref 上。

1. 派发侧(资源画布)

  • 选中工具条「引用」按钮:apps/ai-game-creator-shell/src/view/project-development/index.tsx:10080-10108,点击只调 dispatchResourceReferenceInsert({ type: 'resource', source: 'resource-card', ... })(:10086-10087)。
  • 拖到对话栏批量引用:同文件 :6180 调 dispatchResourceReferenceInsertMany(references)。
  • 事件定义/派发:apps/ai-game-creator-shell/src/features/project-workspace/resourceReferences.ts:83,89(单条)、:105,112(批量)。
  • 按钮本身没有别的落点:onClick 里除派发外没有任何状态写入或提示。

2. 消费侧(App)

  • apps/ai-game-creator-shell/src/App.tsx:1275-1292 是全仓库唯一的 RESOURCE_REFERENCE_INSERT_EVENT 监听,它只做 chatComposerRef.current?.insertReferences([detail.reference])(:1280-1281)。
  • 它只监听单条事件:RESOURCE_REFERENCE_INSERT_MANY_EVENT 在 apps/ai-game-creator-shell/src/** 里没有任何监听者(grep 只命中 resourceReferences.ts 的定义与 tests/)。
  • chatComposerRef 声明在 App.tsx:322;全仓库唯一赋值处是 App.tsx:1846 的 composerRef={chatComposerRef},挂在 PlanningChatView 上。

3. 普通项目永远走不到那条赋值

  • App.tsx:209:const directProjectMode = !designAgentActive;
  • App.tsx:1805-1822:directProjectMode 为真时提前 return DirectProjectChatView,其后的 PlanningChatView(:1844-1846)整段不渲染 → chatComposerRef.current 永远是 null。
  • DirectProject 的输入区是自持的:.../project-development/chat/DirectProjectChatView.tsx 内部渲染 DirectProjectComposer;后者在 DirectProjectComposer.tsx:111 自己 useRef 出 ResourceReferenceInputHandle,props 里没有 ref 出口,也没有订阅这两个事件;DirectProjectChatView 同样没有任何事件订阅。
  • 于是 chatComposerRef.current?.insertReferences(...) 的可选链把整次调用静默吞掉:不报错、不提示、不插入——用户看到的就是「点了没反应」。

4. 回归来源(以前是通的)

  • 监听是 2026-09-08 b7a659cdf 引入的。当时 App 有 3 个聊天面都挂 chatComposerRef(SupervisorChatOnlyView / ProjectSupervisorView / 策划面),还没有 directProjectMode 这条提前 return。
  • 2026-09-22 DirectProject 拆分落地后,composerRef={chatComposerRef} 只剩策划面 1 处、directProjectMode 被引入 → 普通项目这条路径上的引用入口从那时起失效。
  • 同一批 2026-09-22 的合并冲突收口还把 09-21 新加的批量监听整段丢了:19e1883f7(第二父提交含 RESOURCE_REFERENCE_INSERT_MANY_EVENT 3 处,合并结果为 0 处)、0a8debef5(第一父提交含 3 处,合并结果 0 处);git blame App.tsx:1275-1292 至今仍指向 09-08 版本,说明合并保留了旧的那一段。拖拽批量引用现在连监听者都没有,是同一根因的另一半。

5. 为什么现有测试没拦住

  • apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts:3599「工具条里的「引用」用键盘也能插进聊天输入框」只断言 RESOURCE_REFERENCE_INSERT_EVENT 被派发;:3664 传给画布的 chat 是 <div>项目总控</div> 桩,根本不接事件,也没有断言"芯片真的进了草稿"。
  • tests/resourceCanvasChatReferenceDrop.test.tsx:176-184 同样只断言批量事件被派发。
  • 两条用例钉的是「按钮长得对 / 事件发出去了」,而链路真正断在「谁把它落进草稿」这一段,所以一直是绿的。

影响面

  • 普通项目(DirectProject,默认路径):画布选中工具条「引用」= 死按钮(可见、可点、零反馈)。
  • 所有路径:资源卡拖到对话栏的批量 @ 引用 = 事件无消费者,同为死入口。
  • 立项策划链路(planningStartMode / designAgentActive=true)下 App 渲染 PlanningChatView,composerRef 确实挂上了 —— 这解释了为什么不是所有项目都能复现,也让问题更难被发现。
  • 同一类隐患:现在「画布 → 聊天输入区」是 window 自定义事件 + 一个模块外的 ref 约定,消费者是否存在只靠上下文保证,缺任何一环都是静默失败。

改进方法

推荐:单一订阅点 + 「活跃聊天输入区」注册表

  1. 新增 apps/ai-game-creator-shell/src/features/project-workspace/activeChatComposer.ts:模块级只保存当前挂载的那一个输入区句柄({ insertReferences(refs), focus() }),提供 registerActiveChatComposer(handle)(返回注销函数,注销时校验身份、避免旧句柄清掉新句柄)与 insertChatReferences(refs): boolean(空批次 / 无句柄返回 false)。
  2. DirectProjectComposer 用 useImperativeHandle 把内部 composerRef 暴露成 DirectProjectComposerHandle;DirectProjectChatView 挂载期间注册、卸载注销(策划面同理,两条链路互斥渲染,同一时刻只有一个句柄)。
  3. App.tsx 的监听收敛为一处,两个事件都走 insertChatReferences(把 09-21 丢掉的批量分支补回来);chatComposerRef 只留给策划输入盒自己的提交逻辑(getDraft / clear),不再承担跨面板插入职责。
  4. 返回 false 时不再静默:dev 下 console.warn,需要的话由宿主给可见提示(可选加固)。

备选:只给 DirectProjectComposer 加 ref 出口 + App 分流

App 多持一个 directProjectComposerRef,监听里按 directProjectMode 决定用哪个 ref。改动更小,但把「哪个 ref 此刻是活的」这个隐式前提留在检测点上,将来出现第三个聊天面时容易再犯同样的错。

演进方向(本次不必做)

根治形态是不要用 window 事件跨树传这个意图:由 ProjectDevelopmentView(画布与 chat 的共同宿主)用 React context 下发 insertChatReferences,画布直接调用。当前注册表方案与它语义一致,将来换实现不需要再动画布。

必做项

  • 两个事件都要有消费者(单条 + 批量),并把批量监听补回来。
  • 端到端回归用例(现有两条只断言派发,必须升级为"派发 → 真的落到草稿"):
    • 渲染真实 DirectProject 聊天(App / DirectProjectChatView,不要再传 <div>项目总控</div> 桩)+ 资源画布,点工具条「引用」后断言 form.project-chat-composer 的草稿里出现 [data-resource-reference-id="<assetId>"];
    • 拖到对话栏松手后断言整批芯片一次插入、顺序 = 拖动集合顺序,且零坐标写入(现有 4 条用例保留);
    • 策划链路(PlanningChatView)行为不回归。
  • 文档复核:docs/【功能说明】AGC聊天素材引用-2026-09-08.md 的「当前已完成」第 8 条(工具条「引用」入口)与「拖拽引用(2026-09-21)」一节现在描述的是失效链路,修复后按现状复核。

验收判据

  1. 普通项目:画布选中一张已登记素材 → 点「引用」→ 聊天输入框出现 @素材名 芯片,光标落在插入之后。
  2. 多选后拖任意一张到对话栏松手 → 整批素材一次插入同一批芯片,顺序 = 拖动集合顺序,不写布局坐标。
  3. 未登记素材(无 manifestAssetId)不出现「引用」按钮(现状保持);拖拽落点仍给「选中的素材都还没登记为项目资源,暂时不能 @ 引用」提示。
  4. 立项策划链路(PlanningChatView)的引用插入行为不回归。
  5. 定向验证:npm run test -- apps/ai-game-creator-shell/tests、npm run agc:typecheck;共享输入区相关用例 npm run test -- src/components/image-editor。
## 现象 AGC 资源画布上选中一张已登记素材,选中工具条弹出后点「引用」(图标 `@`、可见文案「引用」,`title="引用"`)没有任何反应:聊天输入框里不出现 `@素材名` 芯片,也没有任何提示。 ## 复现与实测(客户端 attach,2026-10-03) - 客户端 `http://127.0.0.1:3080`(CDP 9222 attach)。挂载的对话面只有 DirectProject:`section.project-chat-surface.is-direct-codex`,输入区 `form.project-chat-composer.is-direct-codex` 与 `[contenteditable][aria-label="陶泥儿对话内容"]` 都在位(没有策划面 `project-chat-surface:not(.is-direct-codex)`)。 - 在页面里派发按钮实际使用的那两种事件(负载与 `dispatchResourceReferenceInsert` / `dispatchResourceReferenceInsertMany` 逐字同形): - `agc-resource-reference-insert` → 芯片数 0 → 0 - `agc-resource-reference-insert-many` → 芯片数 0 → 0 - 结论:画布侧派发是真的、输入区也真的挂着,但**没有任何消费者把它落进草稿**。 ## 根因 引用插入事件只有**一个**消费者,而它绑在一个「只在策划链路里才会被挂上」的 ref 上。 ### 1. 派发侧(资源画布) - 选中工具条「引用」按钮:`apps/ai-game-creator-shell/src/view/project-development/index.tsx:10080-10108`,点击只调 `dispatchResourceReferenceInsert({ type: 'resource', source: 'resource-card', ... })`(`:10086-10087`)。 - 拖到对话栏批量引用:同文件 `:6180` 调 `dispatchResourceReferenceInsertMany(references)`。 - 事件定义/派发:`apps/ai-game-creator-shell/src/features/project-workspace/resourceReferences.ts:83,89`(单条)、`:105,112`(批量)。 - 按钮本身没有别的落点:onClick 里除派发外没有任何状态写入或提示。 ### 2. 消费侧(App) - `apps/ai-game-creator-shell/src/App.tsx:1275-1292` 是全仓库**唯一**的 `RESOURCE_REFERENCE_INSERT_EVENT` 监听,它只做 `chatComposerRef.current?.insertReferences([detail.reference])`(`:1280-1281`)。 - 它**只监听单条事件**:`RESOURCE_REFERENCE_INSERT_MANY_EVENT` 在 `apps/ai-game-creator-shell/src/**` 里没有任何监听者(`grep` 只命中 `resourceReferences.ts` 的定义与 `tests/`)。 - `chatComposerRef` 声明在 `App.tsx:322`;全仓库唯一赋值处是 `App.tsx:1846` 的 `composerRef={chatComposerRef}`,挂在 `PlanningChatView` 上。 ### 3. 普通项目永远走不到那条赋值 - `App.tsx:209`:`const directProjectMode = !designAgentActive;` - `App.tsx:1805-1822`:`directProjectMode` 为真时**提前 return** `DirectProjectChatView`,其后的 `PlanningChatView`(`:1844-1846`)整段不渲染 → `chatComposerRef.current` 永远是 `null`。 - DirectProject 的输入区是自持的:`.../project-development/chat/DirectProjectChatView.tsx` 内部渲染 `DirectProjectComposer`;后者在 `DirectProjectComposer.tsx:111` 自己 `useRef` 出 `ResourceReferenceInputHandle`,props 里**没有 ref 出口**,也没有订阅这两个事件;`DirectProjectChatView` 同样没有任何事件订阅。 - 于是 `chatComposerRef.current?.insertReferences(...)` 的可选链把整次调用静默吞掉:不报错、不提示、不插入——用户看到的就是「点了没反应」。 ### 4. 回归来源(以前是通的) - 监听是 2026-09-08 `b7a659cdf` 引入的。当时 App 有 3 个聊天面都挂 `chatComposerRef`(`SupervisorChatOnlyView` / `ProjectSupervisorView` / 策划面),还没有 `directProjectMode` 这条提前 return。 - 2026-09-22 DirectProject 拆分落地后,`composerRef={chatComposerRef}` 只剩策划面 1 处、`directProjectMode` 被引入 → 普通项目这条路径上的引用入口从那时起失效。 - 同一批 2026-09-22 的合并冲突收口还把 09-21 新加的批量监听整段丢了:`19e1883f7`(第二父提交含 `RESOURCE_REFERENCE_INSERT_MANY_EVENT` 3 处,合并结果为 0 处)、`0a8debef5`(第一父提交含 3 处,合并结果 0 处);`git blame App.tsx:1275-1292` 至今仍指向 09-08 版本,说明合并保留了旧的那一段。**拖拽批量引用现在连监听者都没有**,是同一根因的另一半。 ### 5. 为什么现有测试没拦住 - `apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts:3599`「工具条里的「引用」用键盘也能插进聊天输入框」只断言 `RESOURCE_REFERENCE_INSERT_EVENT` 被**派发**;`:3664` 传给画布的 chat 是 `<div>项目总控</div>` 桩,根本不接事件,也没有断言"芯片真的进了草稿"。 - `tests/resourceCanvasChatReferenceDrop.test.tsx:176-184` 同样只断言批量事件被派发。 - 两条用例钉的是「按钮长得对 / 事件发出去了」,而链路真正断在「谁把它落进草稿」这一段,所以一直是绿的。 ## 影响面 - 普通项目(DirectProject,默认路径):画布选中工具条「引用」= 死按钮(可见、可点、零反馈)。 - 所有路径:资源卡拖到对话栏的批量 `@` 引用 = 事件无消费者,同为死入口。 - 立项策划链路(`planningStartMode` / `designAgentActive=true`)下 App 渲染 `PlanningChatView`,`composerRef` 确实挂上了 —— 这解释了为什么不是所有项目都能复现,也让问题更难被发现。 - 同一类隐患:现在「画布 → 聊天输入区」是 `window` 自定义事件 + 一个模块外的 ref 约定,消费者是否存在只靠上下文保证,缺任何一环都是静默失败。 ## 改进方法 ### 推荐:单一订阅点 + 「活跃聊天输入区」注册表 1. 新增 `apps/ai-game-creator-shell/src/features/project-workspace/activeChatComposer.ts`:模块级只保存**当前挂载**的那一个输入区句柄(`{ insertReferences(refs), focus() }`),提供 `registerActiveChatComposer(handle)`(返回注销函数,注销时校验身份、避免旧句柄清掉新句柄)与 `insertChatReferences(refs): boolean`(空批次 / 无句柄返回 `false`)。 2. `DirectProjectComposer` 用 `useImperativeHandle` 把内部 `composerRef` 暴露成 `DirectProjectComposerHandle`;`DirectProjectChatView` 挂载期间注册、卸载注销(策划面同理,两条链路互斥渲染,同一时刻只有一个句柄)。 3. `App.tsx` 的监听收敛为一处,**两个事件都走 `insertChatReferences`**(把 09-21 丢掉的批量分支补回来);`chatComposerRef` 只留给策划输入盒自己的提交逻辑(`getDraft` / `clear`),不再承担跨面板插入职责。 4. 返回 `false` 时不再静默:dev 下 `console.warn`,需要的话由宿主给可见提示(可选加固)。 ### 备选:只给 DirectProjectComposer 加 ref 出口 + App 分流 App 多持一个 `directProjectComposerRef`,监听里按 `directProjectMode` 决定用哪个 ref。改动更小,但把「哪个 ref 此刻是活的」这个隐式前提留在检测点上,将来出现第三个聊天面时容易再犯同样的错。 ### 演进方向(本次不必做) 根治形态是不要用 `window` 事件跨树传这个意图:由 `ProjectDevelopmentView`(画布与 `chat` 的共同宿主)用 React context 下发 `insertChatReferences`,画布直接调用。当前注册表方案与它语义一致,将来换实现不需要再动画布。 ### 必做项 - 两个事件都要有消费者(单条 + 批量),并把批量监听补回来。 - 端到端回归用例(现有两条只断言派发,必须升级为"派发 → 真的落到草稿"): - 渲染**真实** DirectProject 聊天(`App` / `DirectProjectChatView`,不要再传 `<div>项目总控</div>` 桩)+ 资源画布,点工具条「引用」后断言 `form.project-chat-composer` 的草稿里出现 `[data-resource-reference-id="<assetId>"]`; - 拖到对话栏松手后断言整批芯片一次插入、顺序 = 拖动集合顺序,且零坐标写入(现有 4 条用例保留); - 策划链路(`PlanningChatView`)行为不回归。 - 文档复核:`docs/【功能说明】AGC聊天素材引用-2026-09-08.md` 的「当前已完成」第 8 条(工具条「引用」入口)与「拖拽引用(2026-09-21)」一节现在描述的是失效链路,修复后按现状复核。 ## 验收判据 1. 普通项目:画布选中一张已登记素材 → 点「引用」→ 聊天输入框出现 `@素材名` 芯片,光标落在插入之后。 2. 多选后拖任意一张到对话栏松手 → 整批素材一次插入同一批芯片,顺序 = 拖动集合顺序,不写布局坐标。 3. 未登记素材(无 `manifestAssetId`)不出现「引用」按钮(现状保持);拖拽落点仍给「选中的素材都还没登记为项目资源,暂时不能 @ 引用」提示。 4. 立项策划链路(`PlanningChatView`)的引用插入行为不回归。 5. 定向验证:`npm run test -- apps/ai-game-creator-shell/tests`、`npm run agc:typecheck`;共享输入区相关用例 `npm run test -- src/components/image-editor`。
suzmii added the Kind/Bug
Priority
High
2
labels 2026-10-03 18:05:54 +08:00
suzmii self-assigned this 2026-10-03 18:46:18 +08:00
Author
Member

问题原因

现象:AGC 资源画布选中一张已登记素材,点选中工具条的「引用」毫无反应 —— 聊天输入框不出 @素材名 芯片、也没有任何提示;把素材卡拖到对话栏的批量引用同样没反应。

根因:这条链路只有一个消费者,而它挂在一个「只在策划链路才会被赋值」的 ref 上:

  • 画布侧只负责派发 dispatchResourceReferenceInsert(单条)与 dispatchResourceReferenceInsertMany(拖拽批量)。
  • 全仓库唯一的监听在 App.tsx,做的是 chatComposerRef.current?.insertReferences(...);而 chatComposerRef 只赋给 PlanningChatView。
  • 2026-09-22 DirectProject 拆分引入 directProjectMode 后,普通项目在 App.tsx 提前 return DirectProjectChatView,后面的 PlanningChatView 整段不渲染 → chatComposerRef.current 恒为 null,可选链把整次调用静默吞掉。
  • 同一批合并冲突还把 2026-09-21 新加的 RESOURCE_REFERENCE_INSERT_MANY_EVENT 监听整段丢了:批量引用连监听者都没有。

实现思路(采用正文里的注册表方案)

  1. 新增 features/project-workspace/activeChatComposer.ts:模块级只保存当前挂载的那一个输入区句柄 { insertReferences(refs), focus() };registerActiveChatComposer 返回注销函数并在注销时校验身份,重复注册时留 dev 告警;insertChatReferences(refs) 在空批次 / 无输入区 / 句柄报落空三种情况返回 false。
  2. DirectProjectComposer 用 useImperativeHandle 暴露 DirectProjectComposerHandle(按 ref 转发,并由它回答插入是否真的递到输入区)。
  3. DirectProjectChatView 与 PlanningChatView 各自注册按 ref 转发的句柄(注册时不读输入区是否就位,不依赖父子 effect 顺序;两条链路互斥渲染)。
  4. App.tsx 的监听收敛为一处,单条 + 批量两个事件都走 insertChatReferences(补回丢失的批量分支);空批次直接返回,失败时 dev 下 console.warn,不再静默。
  5. chatComposerRef 只保留给策划输入盒自己的 getDraft / clear。

落地情况

  • PR:#605(base master,head fix/agc-canvas-reference-insert)
  • 最终 head:4fc13bcfe34533f2221830c89c6dbcad48c86165(已并入 master #601 的测试替身/类型门禁与 fix/ci-master-red #609 的共享红修复,满足「PR head 含最新 base 提交」门禁)
  • 改动:新增 activeChatComposer.ts;App.tsx、DirectProjectChatView.tsx、DirectProjectComposer.tsx、PlanningChatView.tsx;测试 tests/activeChatComposer.test.ts(新增,钉注册表合同)、tests/resourceCanvasChatReferenceDrop.test.tsx、tests/appSurface/{project-development,design-agent}.suite.ts;文档 docs/【功能说明】AGC聊天素材引用-2026-09-08.md 与 shared-memory 的 pitfalls / decision-log。
  • 原有用例只断言「事件被派发」,已升级为端到端:渲染真实 DirectProject 聊天面,点工具条「引用」后断言草稿里出现 [data-resource-reference-id="<assetId>"];拖拽批量断言整批一次插入、顺序 = 拖动集合顺序、零坐标写入;另加策划链路不回归、未登记素材只给原因、空批次不误报三条。反向证伪:去掉注册调用 / 空批次短路 / 重复注册告警后,对应用例逐一变红。
  • 验证(最终实跑):npx vitest run tests/activeChatComposer.test.ts tests/resourceCanvasChatReferenceDrop.test.tsx → 2 files / 12 passed;appSurface.test.ts -t 引用 → 4 passed;整包 apps/ai-game-creator-shell/tests → 197 passed | 1 skipped(198 文件)、1918 passed | 17 skipped(1935 用例);npm run agc:typecheck(含 check:tests:types)exit 0;check:nginx-spa-routes OK;check:encoding / git diff --check / eslint --max-warnings 0 通过。
  • CI:4fc13bcfe 的 8/8 context 全绿(Rust crates 2.9min、Rust lane 1/2 5.3min、lane 2/2 4.4min、web tests 3.6min、Frontend 3.5min、Backend 6.5min、Native shell 7.2min、Repository checks 6.3min)。
  • 边界:仍沿用「window 事件 + 注册表」形态;根治形态(画布与聊天的共同宿主用 context 下发 insertChatReferences)语义一致,本次不做。未做真机/客户端冒烟(未重启正在运行的客户端),结论来自 jsdom + 真实聊天面渲染 + 反向证伪。
## 问题原因 **现象**:AGC 资源画布选中一张已登记素材,点选中工具条的「引用」毫无反应 —— 聊天输入框不出 `@素材名` 芯片、也没有任何提示;把素材卡拖到对话栏的批量引用同样没反应。 根因:这条链路只有**一个**消费者,而它挂在一个「只在策划链路才会被赋值」的 ref 上: - 画布侧只负责派发 `dispatchResourceReferenceInsert`(单条)与 `dispatchResourceReferenceInsertMany`(拖拽批量)。 - 全仓库唯一的监听在 `App.tsx`,做的是 `chatComposerRef.current?.insertReferences(...)`;而 `chatComposerRef` 只赋给 `PlanningChatView`。 - 2026-09-22 DirectProject 拆分引入 `directProjectMode` 后,普通项目在 `App.tsx` 提前 return `DirectProjectChatView`,后面的 `PlanningChatView` 整段不渲染 → `chatComposerRef.current` 恒为 `null`,可选链把整次调用静默吞掉。 - 同一批合并冲突还把 2026-09-21 新加的 `RESOURCE_REFERENCE_INSERT_MANY_EVENT` 监听整段丢了:批量引用连监听者都没有。 ## 实现思路(采用正文里的注册表方案) 1. 新增 `features/project-workspace/activeChatComposer.ts`:模块级只保存**当前挂载的那一个**输入区句柄 `{ insertReferences(refs), focus() }`;`registerActiveChatComposer` 返回注销函数并在注销时校验身份,重复注册时留 dev 告警;`insertChatReferences(refs)` 在空批次 / 无输入区 / 句柄报落空三种情况返回 `false`。 2. `DirectProjectComposer` 用 `useImperativeHandle` 暴露 `DirectProjectComposerHandle`(按 ref 转发,并由它回答插入是否真的递到输入区)。 3. `DirectProjectChatView` 与 `PlanningChatView` 各自注册**按 ref 转发**的句柄(注册时不读输入区是否就位,不依赖父子 effect 顺序;两条链路互斥渲染)。 4. `App.tsx` 的监听收敛为一处,**单条 + 批量两个事件都走 `insertChatReferences`**(补回丢失的批量分支);空批次直接返回,失败时 dev 下 `console.warn`,不再静默。 5. `chatComposerRef` 只保留给策划输入盒自己的 `getDraft` / `clear`。 ## 落地情况 - PR:https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/605(base `master`,head `fix/agc-canvas-reference-insert`) - 最终 head:`4fc13bcfe34533f2221830c89c6dbcad48c86165`(已并入 `master` #601 的测试替身/类型门禁与 `fix/ci-master-red` #609 的共享红修复,满足「PR head 含最新 base 提交」门禁) - 改动:新增 `activeChatComposer.ts`;`App.tsx`、`DirectProjectChatView.tsx`、`DirectProjectComposer.tsx`、`PlanningChatView.tsx`;测试 `tests/activeChatComposer.test.ts`(新增,钉注册表合同)、`tests/resourceCanvasChatReferenceDrop.test.tsx`、`tests/appSurface/{project-development,design-agent}.suite.ts`;文档 `docs/【功能说明】AGC聊天素材引用-2026-09-08.md` 与 shared-memory 的 pitfalls / decision-log。 - 原有用例只断言「事件被派发」,已升级为端到端:渲染**真实** DirectProject 聊天面,点工具条「引用」后断言草稿里出现 `[data-resource-reference-id="<assetId>"]`;拖拽批量断言整批一次插入、顺序 = 拖动集合顺序、零坐标写入;另加策划链路不回归、未登记素材只给原因、空批次不误报三条。反向证伪:去掉注册调用 / 空批次短路 / 重复注册告警后,对应用例逐一变红。 - 验证(最终实跑):`npx vitest run tests/activeChatComposer.test.ts tests/resourceCanvasChatReferenceDrop.test.tsx` → 2 files / 12 passed;`appSurface.test.ts -t 引用` → 4 passed;整包 `apps/ai-game-creator-shell/tests` → 197 passed | 1 skipped(198 文件)、1918 passed | 17 skipped(1935 用例);`npm run agc:typecheck`(含 `check:tests:types`)exit 0;`check:nginx-spa-routes` OK;`check:encoding` / `git diff --check` / eslint `--max-warnings 0` 通过。 - CI:`4fc13bcfe` 的 8/8 context 全绿(Rust crates 2.9min、Rust lane 1/2 5.3min、lane 2/2 4.4min、web tests 3.6min、Frontend 3.5min、Backend 6.5min、Native shell 7.2min、Repository checks 6.3min)。 - 边界:仍沿用「window 事件 + 注册表」形态;根治形态(画布与聊天的共同宿主用 context 下发 `insertChatReferences`)语义一致,本次不做。未做真机/客户端冒烟(未重启正在运行的客户端),结论来自 jsdom + 真实聊天面渲染 + 反向证伪。
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: GenarrativeAI/Genarrative#602