From b3989b0d5c38190157a2de13e723683f6ca49346 Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Tue, 8 Sep 2026 19:22:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20AGC=20=E7=B4=A0?= =?UTF-8?q?=E6=9D=90=E9=9D=A2=E6=9D=BF=E9=A2=84=E8=A7=88=E5=92=8C=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E9=87=8D=E5=8F=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 每个素材缩略图使用独立预览 scope,避免开发模式取消后无法恢复 - 调整模型选择、@ 按钮和发送按钮的底部布局 - 聊天引用候选改为使用完整已登记素材列表 - 补充图片缩略图和 StrictMode 回归测试 --- apps/ai-game-creator-shell/src/App.tsx | 8 +++- .../ProjectSupervisorView.tsx | 6 +-- .../ProjectWorkspaceChatPane.tsx | 4 +- .../ResourceReferenceInput.tsx | 24 +++++----- .../SupervisorChatOnlyView.tsx | 6 +-- apps/ai-game-creator-shell/src/styles.css | 6 +-- .../tests/resourceReferenceInput.test.tsx | 44 +++++++++++++++++++ 7 files changed, 73 insertions(+), 25 deletions(-) diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index 587238530..d69655a0a 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -10691,6 +10691,9 @@ export function App({ (asset) => asset.localPath && !asset.localPath.startsWith('.agent/'), ) .slice(0, 8); + const chatProjectAssets = manifest.assets.filter( + (asset) => asset.localPath && !asset.localPath.startsWith('.agent/'), + ); const visibleMainProjectCheckpoints = projectCheckpoints.slice(0, 5); const currentProjectTitle = localProject ? manifest.name.trim() || projectNameFromPath(localProject.projectPath) @@ -10940,6 +10943,7 @@ export function App({ chatInput={chatInput} chatReferences={chatReferences} composerRef={chatComposerRef} + chatProjectAssets={chatProjectAssets} messagesRef={supervisorChatMessagesRef} onCancelConfirmation={cancelUiCommandConfirmation} onChatInputChange={handleChatComposerChange} @@ -10968,7 +10972,6 @@ export function App({ hiddenConversationCount={hiddenConversationCount} needsUserInput={projectSupervisorNeedsUserInput} visibleMessages={visibleMessages} - visibleMainProjectAssets={visibleMainProjectAssets} workspaceStatus={workspaceStatus} expectedRunId={projectSupervisorExpectedRunId} /> @@ -10981,6 +10984,7 @@ export function App({ chatInput={chatInput} chatReferences={chatReferences} composerRef={chatComposerRef} + chatProjectAssets={chatProjectAssets} directCodex={directCodexProductRuntime} directStatus={directCodexProductRuntime ? directCodexStatus : null} directProcessDetail={ @@ -11011,7 +11015,6 @@ export function App({ : projectSupervisorTransientReply } visibleMessages={visibleMessages} - visibleMainProjectAssets={visibleMainProjectAssets} visibleProfessionalAgentCards={visibleProfessionalAgentCards} showProfessionalCollaboration={ !directCodexProductRuntime && orchestrationMode === 'professional-dag' @@ -11062,6 +11065,7 @@ export function App({ chatInput={chatInput} chatReferences={chatReferences} composerRef={chatComposerRef} + chatProjectAssets={chatProjectAssets} chatInputRef={chatInputRef} confirmProjectCreateInNonEmptyFolder={ confirmProjectCreateInNonEmptyFolder diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx index ce067fc1b..aff12a648 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx @@ -69,6 +69,7 @@ function directStatusTitle(status: string | null | undefined) { type ProjectSupervisorViewProps = RuntimePanelProps & { chatInput: string; chatReferences: ChatReference[]; + chatProjectAssets: import('../../../../../packages/shared/src/contracts/gameCreationApp').GameCreationAppAssetManifestEntry[]; composerRef?: Ref; directCodex?: boolean; directStatus?: GameCreatorDirectTurnUpdateStatus | null; @@ -103,12 +104,12 @@ type ProjectSupervisorViewProps = RuntimePanelProps & { comment: string | null, ) => Promise; onMakeGameFromApprovedGdd?: () => Promise; - visibleMainProjectAssets?: import('../../../../../packages/shared/src/contracts/gameCreationApp').GameCreationAppAssetManifestEntry[]; }; export function ProjectSupervisorView({ chatInput, chatReferences, + chatProjectAssets, composerRef, directCodex = false, directStatus = null, @@ -140,7 +141,6 @@ export function ProjectSupervisorView({ onPlanGddRefresh, onPlanGddDecision, onMakeGameFromApprovedGdd, - visibleMainProjectAssets = [], ...runtimePanelProps }: ProjectSupervisorViewProps) { const [expandedProcessKey, setExpandedProcessKey] = useState( @@ -363,7 +363,7 @@ export function ProjectSupervisorView({ ; chatInputRef: RefObject; confirmProjectCreateInNonEmptyFolder: () => void; @@ -211,6 +212,7 @@ export function ProjectWorkspaceChatPane({ chatAgentBusy, chatInput, chatReferences, + chatProjectAssets, composerRef, chatInputRef, confirmProjectCreateInNonEmptyFolder, @@ -945,7 +947,7 @@ export function ProjectWorkspaceChatPane({ ref={composerRef} inputRef={chatInputRef} ariaLabel="创作想法" - assets={visibleMainProjectAssets} + assets={chatProjectAssets} projectPath={projectPath} disabled={chatAgentBusy || projectSupervisorNeedsUserInput} multiline={false} diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx index c4720aa77..96f1998f9 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx @@ -206,7 +206,6 @@ function ResourceReferenceEditor({ width: number; } | null>(null); const rootRef = useRef(null); - const previewScopeIdRef = useRef(createProjectResourcePreviewScopeId()); const assetReferences = useMemo( () => @@ -297,13 +296,6 @@ function ResourceReferenceEditor({ lastEmittedDraftRef.current = nextDraft; }, [editor, references, value]); - useEffect( - () => () => { - cancelLocalProjectResourcePreviewScope(previewScopeIdRef.current); - }, - [], - ); - useEffect( () => editor.registerCommand( @@ -578,7 +570,6 @@ function ResourceReferenceEditor({ (asset) => asset.id === reference.resourceId, )} projectPath={projectPath} - scopeId={previewScopeIdRef.current} /> {reference.label} @@ -637,17 +628,24 @@ function ResourceReferenceEditor({ function ResourcePickerThumbnail({ asset, projectPath, - scopeId, }: { asset?: GameCreationAppAssetManifestEntry; projectPath: string; - scopeId: string; }) { + const previewScopeIdRef = useRef(createProjectResourcePreviewScopeId()); const [previewUrl, setPreviewUrl] = useState(null); const [failed, setFailed] = useState(false); const mediaType = asset?.mediaType.toLowerCase() ?? ''; const kind = asset?.kind.toLowerCase() ?? ''; + useEffect( + () => () => { + cancelLocalProjectResourcePreviewScope(previewScopeIdRef.current); + previewScopeIdRef.current = createProjectResourcePreviewScopeId(); + }, + [], + ); + useEffect(() => { setPreviewUrl(null); setFailed(false); @@ -660,7 +658,7 @@ function ResourcePickerThumbnail({ void invoke<{ dataUrl: string }>('read_local_project_image_preview', { projectPath, relativePath: asset.localPath, - scopeId, + scopeId: previewScopeIdRef.current, requestId: createProjectResourcePreviewRequestId(), }) .then((result) => { @@ -672,7 +670,7 @@ function ResourcePickerThumbnail({ return () => { cancelled = true; }; - }, [asset, mediaType, projectPath, scopeId]); + }, [asset, mediaType, projectPath]); if (previewUrl) { return ; diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx index f57ea820a..c53d4f302 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx @@ -45,6 +45,7 @@ type SupervisorChatOnlyViewProps = { chatAgentBusy: boolean; chatInput: string; chatReferences: ChatReference[]; + chatProjectAssets: import('../../../../../packages/shared/src/contracts/gameCreationApp').GameCreationAppAssetManifestEntry[]; composerRef?: Ref; directCodex?: boolean; messagesRef: RefObject; @@ -73,13 +74,13 @@ type SupervisorChatOnlyViewProps = { visibleMessages: ChatMessage[]; workspaceStatus: string; expectedRunId?: string | null; - visibleMainProjectAssets?: import('../../../../../packages/shared/src/contracts/gameCreationApp').GameCreationAppAssetManifestEntry[]; }; export function SupervisorChatOnlyView({ chatAgentBusy, chatInput, chatReferences, + chatProjectAssets, composerRef, directCodex = false, messagesRef, @@ -107,7 +108,6 @@ export function SupervisorChatOnlyView({ needsUserInput, visibleMessages, workspaceStatus, - visibleMainProjectAssets = [], }: SupervisorChatOnlyViewProps) { const shouldFollowLatestRef = useRef(true); const running = Boolean( @@ -312,7 +312,7 @@ export function SupervisorChatOnlyView({ { ).not.toBeNull(); }); + test('loads image thumbnails through the controlled native preview command', async () => { + const user = userEvent.setup(); + const invoke = vi.fn().mockResolvedValue({ + dataUrl: 'data:image/png;base64,iVBORw0KGgo=', + }); + ( + window as unknown as { + __TAURI__?: { core?: { invoke?: typeof invoke } }; + } + ).__TAURI__ = { core: { invoke } }; + render( + + + , + ); + + await user.click(screen.getByRole('button', { name: '插入素材引用' })); + await waitFor(() => { + expect( + document.querySelector('.resource-reference-picker-list img'), + ).not.toBeNull(); + }); + expect(invoke).toHaveBeenCalledWith( + 'read_local_project_image_preview', + expect.objectContaining({ + projectPath: 'C:/project', + relativePath: 'assets/hero.png', + }), + ); + delete ( + window as unknown as { + __TAURI__?: { core?: { invoke?: typeof invoke } }; + } + ).__TAURI__; + }); + test('filters candidate references by name, id, kind, and media category', () => { const hero = resourceReferenceFromAsset(assets[0]!, 'asset-picker'); const theme = resourceReferenceFromAsset(assets[2]!, 'asset-picker');