AGC 画布素材卡「引用」点了没反应:插入事件只挂在策划输入盒,DirectProject 路径下句柄恒为空(拖拽批量引用同样失效) #602
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
现象
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 → 0agc-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(批量)。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为真时提前 returnDirectProjectChatView,其后的PlanningChatView(:1844-1846)整段不渲染 →chatComposerRef.current永远是null。.../project-development/chat/DirectProjectChatView.tsx内部渲染DirectProjectComposer;后者在DirectProjectComposer.tsx:111自己useRef出ResourceReferenceInputHandle,props 里没有 ref 出口,也没有订阅这两个事件;DirectProjectChatView同样没有任何事件订阅。chatComposerRef.current?.insertReferences(...)的可选链把整次调用静默吞掉:不报错、不提示、不插入——用户看到的就是「点了没反应」。4. 回归来源(以前是通的)
b7a659cdf引入的。当时 App 有 3 个聊天面都挂chatComposerRef(SupervisorChatOnlyView/ProjectSupervisorView/ 策划面),还没有directProjectMode这条提前 return。composerRef={chatComposerRef}只剩策划面 1 处、directProjectMode被引入 → 普通项目这条路径上的引用入口从那时起失效。19e1883f7(第二父提交含RESOURCE_REFERENCE_INSERT_MANY_EVENT3 处,合并结果为 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同样只断言批量事件被派发。影响面
@引用 = 事件无消费者,同为死入口。planningStartMode/designAgentActive=true)下 App 渲染PlanningChatView,composerRef确实挂上了 —— 这解释了为什么不是所有项目都能复现,也让问题更难被发现。window自定义事件 + 一个模块外的 ref 约定,消费者是否存在只靠上下文保证,缺任何一环都是静默失败。改进方法
推荐:单一订阅点 + 「活跃聊天输入区」注册表
apps/ai-game-creator-shell/src/features/project-workspace/activeChatComposer.ts:模块级只保存当前挂载的那一个输入区句柄({ insertReferences(refs), focus() }),提供registerActiveChatComposer(handle)(返回注销函数,注销时校验身份、避免旧句柄清掉新句柄)与insertChatReferences(refs): boolean(空批次 / 无句柄返回false)。DirectProjectComposer用useImperativeHandle把内部composerRef暴露成DirectProjectComposerHandle;DirectProjectChatView挂载期间注册、卸载注销(策划面同理,两条链路互斥渲染,同一时刻只有一个句柄)。App.tsx的监听收敛为一处,两个事件都走insertChatReferences(把 09-21 丢掉的批量分支补回来);chatComposerRef只留给策划输入盒自己的提交逻辑(getDraft/clear),不再承担跨面板插入职责。false时不再静默:dev 下console.warn,需要的话由宿主给可见提示(可选加固)。备选:只给 DirectProjectComposer 加 ref 出口 + App 分流
App 多持一个
directProjectComposerRef,监听里按directProjectMode决定用哪个 ref。改动更小,但把「哪个 ref 此刻是活的」这个隐式前提留在检测点上,将来出现第三个聊天面时容易再犯同样的错。演进方向(本次不必做)
根治形态是不要用
window事件跨树传这个意图:由ProjectDevelopmentView(画布与chat的共同宿主)用 React context 下发insertChatReferences,画布直接调用。当前注册表方案与它语义一致,将来换实现不需要再动画布。必做项
App/DirectProjectChatView,不要再传<div>项目总控</div>桩)+ 资源画布,点工具条「引用」后断言form.project-chat-composer的草稿里出现[data-resource-reference-id="<assetId>"];PlanningChatView)行为不回归。docs/【功能说明】AGC聊天素材引用-2026-09-08.md的「当前已完成」第 8 条(工具条「引用」入口)与「拖拽引用(2026-09-21)」一节现在描述的是失效链路,修复后按现状复核。验收判据
@素材名芯片,光标落在插入之后。manifestAssetId)不出现「引用」按钮(现状保持);拖拽落点仍给「选中的素材都还没登记为项目资源,暂时不能 @ 引用」提示。PlanningChatView)的引用插入行为不回归。npm run test -- apps/ai-game-creator-shell/tests、npm run agc:typecheck;共享输入区相关用例npm run test -- src/components/image-editor。问题原因
现象:AGC 资源画布选中一张已登记素材,点选中工具条的「引用」毫无反应 —— 聊天输入框不出
@素材名芯片、也没有任何提示;把素材卡拖到对话栏的批量引用同样没反应。根因:这条链路只有一个消费者,而它挂在一个「只在策划链路才会被赋值」的 ref 上:
dispatchResourceReferenceInsert(单条)与dispatchResourceReferenceInsertMany(拖拽批量)。App.tsx,做的是chatComposerRef.current?.insertReferences(...);而chatComposerRef只赋给PlanningChatView。directProjectMode后,普通项目在App.tsx提前 returnDirectProjectChatView,后面的PlanningChatView整段不渲染 →chatComposerRef.current恒为null,可选链把整次调用静默吞掉。RESOURCE_REFERENCE_INSERT_MANY_EVENT监听整段丢了:批量引用连监听者都没有。实现思路(采用正文里的注册表方案)
features/project-workspace/activeChatComposer.ts:模块级只保存当前挂载的那一个输入区句柄{ insertReferences(refs), focus() };registerActiveChatComposer返回注销函数并在注销时校验身份,重复注册时留 dev 告警;insertChatReferences(refs)在空批次 / 无输入区 / 句柄报落空三种情况返回false。DirectProjectComposer用useImperativeHandle暴露DirectProjectComposerHandle(按 ref 转发,并由它回答插入是否真的递到输入区)。DirectProjectChatView与PlanningChatView各自注册按 ref 转发的句柄(注册时不读输入区是否就位,不依赖父子 effect 顺序;两条链路互斥渲染)。App.tsx的监听收敛为一处,单条 + 批量两个事件都走insertChatReferences(补回丢失的批量分支);空批次直接返回,失败时 dev 下console.warn,不再静默。chatComposerRef只保留给策划输入盒自己的getDraft/clear。落地情况
master,headfix/agc-canvas-reference-insert)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。[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-routesOK;check:encoding/git diff --check/ eslint--max-warnings 0通过。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)。insertChatReferences)语义一致,本次不做。未做真机/客户端冒烟(未重启正在运行的客户端),结论来自 jsdom + 真实聊天面渲染 + 反向证伪。