同类问题:另外两个宿主的引用 provider 也按内容签名记忆

- PlanningChatView 的 chatProjectAssets 由工作台壳每次渲染重新 .filter() 派生、DirectProjectComposer 的 assets 由 manifest 订阅每次渲染重新派生,两处的 [assets] 依赖同样从不命中缓存
- 两处改用 assetsSignature(...) 做依赖,provider 身份不再随宿主渲染抖动(流式回复时每帧都换),输入区不再每帧重注册 Lexical 更新监听
This commit is contained in:
2026-09-22 17:12:02 +08:00
parent fbfc7e5e62
commit aa973ea34d
2 changed files with 13 additions and 2 deletions
@@ -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?.();
@@ -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) {