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], );