From cfd129a0bf0da21378b2b682e3258631bc00e07d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Fri, 31 Jul 2026 13:34:29 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=94=BB=E5=B8=83?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E5=AE=9A=E4=BD=8D=E7=8A=B6=E6=80=81=E8=AF=AF?= =?UTF-8?q?=E6=8A=A5=20=E8=AE=A9=20focusResource=20=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E5=8C=B9=E9=85=8D=E7=BB=93=E6=9E=9C=20?= =?UTF-8?q?=E6=9C=AA=E6=89=BE=E5=88=B0=E7=94=BB=E5=B8=83=E5=9B=BE=E5=B1=82?= =?UTF-8?q?=E6=97=B6=E5=90=91=E5=8F=B3=E9=94=AE=E8=8F=9C=E5=8D=95=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E5=A4=B1=E8=B4=A5=E7=8A=B6=E6=80=81=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E8=B5=84=E6=BA=90=E5=AE=9A=E4=BD=8D=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E4=B8=8E=E5=A4=B1=E8=B4=A5=E7=9A=84=E5=9B=9E=E5=BD=92=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EditorAgentConversation/MessageBubble.test.tsx | 8 ++++++++ .../EditorAgentConversation/useRightClickMenu.ts | 8 ++++---- .../image-editor/ImageCanvasActionsContext.test.tsx | 6 ++++-- src/components/image-editor/ImageCanvasActionsContext.ts | 2 +- src/components/image-editor/ImageCanvasEditorView.tsx | 5 +++-- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/components/image-editor/EditorAgentConversation/MessageBubble.test.tsx b/src/components/image-editor/EditorAgentConversation/MessageBubble.test.tsx index cc5bfbe51..9c5371d5d 100644 --- a/src/components/image-editor/EditorAgentConversation/MessageBubble.test.tsx +++ b/src/components/image-editor/EditorAgentConversation/MessageBubble.test.tsx @@ -51,6 +51,7 @@ function renderMessage( describe('MessageBubble', () => { beforeEach(() => { vi.clearAllMocks(); + focusResourceMock.mockReturnValue({ found: true }); vi.mocked(copyTextToClipboard).mockResolvedValue(true); }); @@ -448,6 +449,13 @@ describe('MessageBubble', () => { ); } expect(focusResourceMock).toHaveBeenCalledTimes(3); + + focusResourceMock.mockReturnValueOnce({ found: false }); + fireEvent.contextMenu(imageCard!, { clientX: 30, clientY: 40 }); + fireEvent.click(screen.getByRole('menuitem', { name: '在画布中定位' })); + + expect(focusResourceMock).toHaveBeenLastCalledWith('resource-image'); + expect(screen.getByRole('menuitem', { name: '定位失败' })).toBeTruthy(); }); it('exports right-click images through the native HostBridge', async () => { diff --git a/src/components/image-editor/EditorAgentConversation/useRightClickMenu.ts b/src/components/image-editor/EditorAgentConversation/useRightClickMenu.ts index 294e1e168..e26c667dd 100644 --- a/src/components/image-editor/EditorAgentConversation/useRightClickMenu.ts +++ b/src/components/image-editor/EditorAgentConversation/useRightClickMenu.ts @@ -4,6 +4,7 @@ import { useState, } from 'react'; +import type { ImageCanvasActions } from '@/src/components/image-editor/ImageCanvasActionsContext.ts'; import { readAssetBytes } from '@/src/services/assetReadUrlService.ts'; import { copyTextToClipboard } from '@/src/services/clipboard.ts'; import { @@ -234,7 +235,7 @@ async function downloadAsset(asset: EditorAgentContextAsset) { function focusAssetOnCanvas( asset: EditorAgentContextAsset, - onFocusResource?: (resourceId: string) => void, + onFocusResource?: ImageCanvasActions['focusResource'], ) { if (asset.kind !== 'generated_media' || !onFocusResource) { return false; @@ -243,8 +244,7 @@ function focusAssetOnCanvas( if (!resourceId) { return false; } - onFocusResource(resourceId); - return true; + return onFocusResource(resourceId).found; } export function useRightClickMenu({ @@ -252,7 +252,7 @@ export function useRightClickMenu({ onFocusResource, }: { onReferenceImage?: (asset: EditorAgentContextAsset) => boolean; - onFocusResource?: (resourceId: string) => void; + onFocusResource?: ImageCanvasActions['focusResource']; } = {}) { const [rightClickMenu, setRightClickMenu] = useState(null); diff --git a/src/components/image-editor/ImageCanvasActionsContext.test.tsx b/src/components/image-editor/ImageCanvasActionsContext.test.tsx index 7269b16ef..a383b68dc 100644 --- a/src/components/image-editor/ImageCanvasActionsContext.test.tsx +++ b/src/components/image-editor/ImageCanvasActionsContext.test.tsx @@ -11,7 +11,7 @@ import { ImageCanvasActionsProvider } from './ImageCanvasActionsProvider'; describe('ImageCanvasActionsContext', () => { it('exposes the editor-scoped canvas actions', () => { - const focusResource = vi.fn(); + const focusResource = vi.fn(() => ({ found: true })); const refreshCanvas = vi.fn(); const { result } = renderHook(useImageCanvasActions, { wrapper: ({ children }: { children: ReactNode }) => ( @@ -24,11 +24,13 @@ describe('ImageCanvasActionsContext', () => { ), }); + let focusResult: { found: boolean } | undefined; act(() => { - result.current.focusResource('resource-a'); + focusResult = result.current.focusResource('resource-a'); result.current.refreshCanvas(); }); + expect(focusResult).toEqual({ found: true }); expect(focusResource).toHaveBeenCalledWith('resource-a'); expect(refreshCanvas).toHaveBeenCalledTimes(1); }); diff --git a/src/components/image-editor/ImageCanvasActionsContext.ts b/src/components/image-editor/ImageCanvasActionsContext.ts index 963632896..b989b66e9 100644 --- a/src/components/image-editor/ImageCanvasActionsContext.ts +++ b/src/components/image-editor/ImageCanvasActionsContext.ts @@ -1,7 +1,7 @@ import { createContext, useContext } from 'react'; export type ImageCanvasActions = { - focusResource: (resourceId: string) => void; + focusResource: (resourceId: string) => { found: boolean }; refreshCanvas: () => void; }; diff --git a/src/components/image-editor/ImageCanvasEditorView.tsx b/src/components/image-editor/ImageCanvasEditorView.tsx index 277b529da..4f5f39d17 100644 --- a/src/components/image-editor/ImageCanvasEditorView.tsx +++ b/src/components/image-editor/ImageCanvasEditorView.tsx @@ -904,15 +904,16 @@ export function ImageCanvasEditorView({ (resourceId: string) => { const normalizedResourceId = resourceId.trim(); if (!normalizedResourceId) { - return; + return { found: false }; } const targetLayer = layersRef.current.find( (layer) => layer.resourceId === normalizedResourceId, ); if (!targetLayer) { - return; + return { found: false }; } focusCanvasBounds(targetLayer, { animate: true }); + return { found: true }; }, [focusCanvasBounds], );