diff --git a/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectComposer/DirectProjectComposer.tsx b/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectComposer/DirectProjectComposer.tsx index ea336a19d..c92cd74d1 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectComposer/DirectProjectComposer.tsx +++ b/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectComposer/DirectProjectComposer.tsx @@ -21,6 +21,7 @@ import { ResourceReferencePicker, type ResourceReferencePickerHandle, } from '../../../../../features/project-workspace/ResourceReferencePicker'; +import { assetsSignature } from '../../../../../features/project-workspace/resourceReferences'; import type { DirectCodexTurnAttachment } from '../../conversation/directCodexTurnAttachments'; import type { DirectCodexUserContentPart } from '../../generated/DirectCodexUserContentPart'; import type { QueuedChatTurn } from './chatComposerQueue'; @@ -107,6 +108,7 @@ export function DirectProjectComposer({ (Rust `direct_codex_user_item_to_codex_turn_input`),所以只有这里注入 Skill provider。 */ const skillProvider = useSkillReferenceProvider(); + const assetsContentSignature = assetsSignature(assets); const referenceProviders = useMemo( () => [ createResourceReferenceProvider({ assets }), @@ -114,7 +116,11 @@ export function DirectProjectComposer({ attachmentReferenceProvider, runtimeRegionReferenceProvider, ], - [assets, skillProvider], + // `assets` 由 manifest 订阅每次渲染重新 `.filter()` 派生,依赖它等于从不命中缓存: + // provider 身份一变,输入区就重新注册 Lexical 更新监听(流式回复时每帧都换)。 + // 按内容签名取依赖才真的能跳过重建。 + // eslint-disable-next-line react-hooks/exhaustive-deps + [assetsContentSignature, skillProvider], ); const openPicker = useCallback(() => { onReferencePickerOpen?.(); diff --git a/apps/ai-game-creator-shell/src/view/project-development/planning/PlanningChatView.tsx b/apps/ai-game-creator-shell/src/view/project-development/planning/PlanningChatView.tsx index 00f769090..cca6c5918 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/planning/PlanningChatView.tsx +++ b/apps/ai-game-creator-shell/src/view/project-development/planning/PlanningChatView.tsx @@ -23,6 +23,7 @@ import { ResourceReferenceInput, type ResourceReferenceInputHandle, } from '../../../features/project-workspace/ResourceReferenceInput'; +import { assetsSignature } from '../../../features/project-workspace/resourceReferences'; import { AgentReasoning } from '../chat/components/AgentReasoning/AgentReasoning'; import { ComposerReasoningEffortSelect } from '../chat/components/DirectProjectComposer/ComposerControls'; import { formatClockTime } from '../chat/components/ToolCallGroup/toolCallGroupPresentation'; @@ -135,13 +136,17 @@ export function PlanningChatView({ `$` Skill 候选只有 DirectProject 回合那条路径会把 `agc_skill_reference` 解析成真 Skill, 这里注入 Skill provider 只会得到一个退化成本文的误导入口,所以不注入。 */ + const chatProjectAssetsSignature = assetsSignature(chatProjectAssets); const referenceProviders = useMemo( () => [ createResourceReferenceProvider({ assets: chatProjectAssets }), attachmentReferenceProvider, runtimeRegionReferenceProvider, ], - [chatProjectAssets], + // `chatProjectAssets` 由工作台壳每次渲染重新 `.filter()` 派生,依赖它等于从不命中缓存: + // provider 身份一变,输入区就重新注册 Lexical 更新监听。按内容签名取依赖才真的能跳过重建。 + // eslint-disable-next-line react-hooks/exhaustive-deps + [chatProjectAssetsSignature], ); let submitLabel = '发送'; if (needsUserInput) {