replace onEditorAgentGenerationResult with onEditorAgentCanvasRefreshRequested and update related logic

This commit is contained in:
2026-07-10 11:25:14 +08:00
parent 365ada84e3
commit 74361af6bd
3 changed files with 117 additions and 106 deletions
@@ -34,7 +34,7 @@ type EditorAgentGetConversation =
EditorAgentConversationClient['getConversation'];
type EditorAgentDeleteConversation =
EditorAgentConversationClient['deleteConversation'];
type EditorAgentStreamMessage = EditorAgentConversationClient['streamMessage'];
type EditorAgentStreamMessage = EditorAgentConversationClient['sendMessage'];
type EditorAgentConversationSummary = Awaited<
ReturnType<EditorAgentListConversations>
>[number];
@@ -68,8 +68,6 @@ const getEditorAgentConversationMock = vi.hoisted(() =>
>(async () => ({
conversationId: 'editor-agent-conv-test',
projectId: 'editor-project-default',
title: '画布 Agent',
createdAt: '2026-07-03T00:00:00.000Z',
updatedAt: '2026-07-03T00:00:00.000Z',
messages: [],
})),
@@ -86,12 +84,17 @@ const deleteEditorAgentConversationMock = vi.hoisted(() =>
updatedAt: '2026-07-03T00:00:00.000Z',
})),
);
const streamEditorAgentMessageMock = vi.hoisted(() =>
const sendEditorAgentMessageMock = vi.hoisted(() =>
vi.fn<
Parameters<EditorAgentStreamMessage>,
ReturnType<EditorAgentStreamMessage>
>(async () => undefined),
>(async () => ({
deltaMessages: [],
errorMessage: null,
})),
);
const confirmEditorAgentToolCallMock = vi.hoisted(() => vi.fn());
const cancelEditorAgentToolCallMock = vi.hoisted(() => vi.fn());
const generateEditorImageMock = vi.hoisted(() => vi.fn());
const generateEditorIconSpritesheetMock = vi.hoisted(() => vi.fn());
const generateEditorCharacterAnimationMock = vi.hoisted(() => vi.fn());
@@ -145,11 +148,13 @@ vi.mock('../../services/frontendRuntimeConfigService', () => ({
}));
vi.mock('../../services/image-editor/editorAgentClient', () => ({
cancelEditorAgentToolCall: cancelEditorAgentToolCallMock,
confirmEditorAgentToolCall: confirmEditorAgentToolCallMock,
createEditorAgentConversation: createEditorAgentConversationMock,
deleteEditorAgentConversation: deleteEditorAgentConversationMock,
getEditorAgentConversation: getEditorAgentConversationMock,
listEditorAgentConversations: listEditorAgentConversationsMock,
streamEditorAgentMessage: streamEditorAgentMessageMock,
sendEditorAgentMessage: sendEditorAgentMessageMock,
}));
function createEditorAgentConversationSummary(
@@ -158,8 +163,6 @@ function createEditorAgentConversationSummary(
return {
conversationId: 'editor-agent-conv-test',
projectId: 'editor-project-default',
title: '画布 Agent',
createdAt: '2026-07-03T00:00:00.000Z',
updatedAt: '2026-07-03T00:00:00.000Z',
...overrides,
};
@@ -171,33 +174,30 @@ function createEditorAgentDetailWithGeneration(
const summary = createEditorAgentConversationSummary();
return {
...summary,
title: '画布 Agent',
createdAt: '2026-07-03T00:00:00.000Z',
messages: [
{
id: 'agent-message-result',
role: 'assistant',
kind: 'chat',
text: '生成完成',
id: 1,
role: 'system',
text: 'internal completed tool output',
attachments: [],
generations: [
{
toolCallId: 'agent-tool-call-1',
toolName: 'generate_image',
taskId: 'task-agent-1',
status: 'completed',
model: 'gpt-image-2',
images: [
{
resourceId,
imageSrc: '/agent-result.png',
thumbnailSrc: '/agent-result-thumb.png',
width: 512,
height: 512,
},
],
error: null,
},
],
status: 'completed',
toolCall: {
toolName: 'generate_image',
summary: '',
status: 'completed',
args: {},
images: [
{
resourceId,
imageSrc: '/agent-result.png',
thumbnailSrc: '/agent-result-thumb.png',
width: 512,
height: 512,
},
],
error: null,
},
createdAt: '2026-07-03T00:00:01.000Z',
},
],
@@ -260,16 +260,25 @@ describe('ImageCanvasEditorView', () => {
...createEditorAgentConversationSummary({
conversationId: 'editor-agent-conv-created',
}),
title: '画布 Agent',
createdAt: '2026-07-03T00:00:00.000Z',
messages: [],
});
getEditorAgentConversationMock.mockResolvedValue({
...createEditorAgentConversationSummary(),
title: '画布 Agent',
createdAt: '2026-07-03T00:00:00.000Z',
messages: [],
});
deleteEditorAgentConversationMock.mockResolvedValue(
createEditorAgentConversationSummary(),
);
streamEditorAgentMessageMock.mockResolvedValue(undefined);
sendEditorAgentMessageMock.mockResolvedValue({
deltaMessages: [],
errorMessage: null,
});
confirmEditorAgentToolCallMock.mockReset();
cancelEditorAgentToolCallMock.mockReset();
getPlatformProfileDashboardMock.mockResolvedValue({
walletBalance: 1234,
totalPlayTimeMs: 0,
@@ -283,7 +292,9 @@ describe('ImageCanvasEditorView', () => {
createEditorAgentConversationMock.mockReset();
getEditorAgentConversationMock.mockReset();
deleteEditorAgentConversationMock.mockReset();
streamEditorAgentMessageMock.mockReset();
sendEditorAgentMessageMock.mockReset();
confirmEditorAgentToolCallMock.mockReset();
cancelEditorAgentToolCallMock.mockReset();
getPlatformProfileDashboardMock.mockReset();
loadFrontendRuntimeConfigMock.mockReset();
});
@@ -1436,9 +1447,7 @@ describe('ImageCanvasEditorView', () => {
render(<ImageCanvasEditorView />);
expect(screen.queryByRole('button', { name: '画布 Agent' })).toBeNull();
expect(
screen.queryByRole('button', { name: '打开画布 Agent' }),
).toBeNull();
expect(screen.queryByRole('button', { name: '打开画布 Agent' })).toBeNull();
expect(screen.queryByLabelText('发送给画布 Agent')).toBeNull();
expect(listEditorAgentConversationsMock).not.toHaveBeenCalled();
});
@@ -1827,46 +1836,55 @@ describe('ImageCanvasEditorView', () => {
]);
getEditorAgentConversationMock.mockResolvedValueOnce({
...createEditorAgentConversationSummary(),
messages: [],
title: '画布 Agent',
createdAt: '2026-07-03T00:00:00.000Z',
messages: [
{
id: 0,
role: 'system',
text: 'internal pending tool prompt',
attachments: [],
toolCall: {
toolName: 'edit-image',
summary: '',
status: 'pending_confirmation',
args: {
object_image_id: 'source-image',
prompt: '把图片换成像素风',
},
images: [],
error: null,
},
createdAt: '2026-07-03T00:00:00.000Z',
},
],
});
confirmEditorAgentToolCallMock.mockImplementation(async () => {
showGeneratedAsset = true;
return {
id: 0,
role: 'system',
text: 'internal completed tool output',
attachments: [],
toolCall: {
toolName: 'edit-image',
summary: '',
status: 'completed',
args: {},
images: [
{
resourceId: null,
imageSrc: '/agent-stream.png',
thumbnailSrc: null,
width: 512,
height: 512,
},
],
error: null,
},
createdAt: '2026-07-03T00:00:00.000Z',
};
});
streamEditorAgentMessageMock.mockImplementation(
async (conversationId, _payload, options) => {
options.onEvent?.({
event: 'message_delta',
data: {
conversationId,
messageId: 'assistant-agent-stream',
role: 'assistant',
kind: 'chat',
textDelta: '我来生成图片。',
},
});
showGeneratedAsset = true;
options.onEvent?.({
event: 'generation_result',
data: {
conversationId,
messageId: 'assistant-agent-stream',
toolCallId: 'tool-call-agent-stream',
toolName: 'generate_image',
model: 'gpt-image-2',
images: [
{
resourceId: 'resource-agent-stream',
imageSrc: '/agent-stream.png',
thumbnailSrc: null,
width: 512,
height: 512,
},
],
},
});
options.onEvent?.({
event: 'done',
data: { conversationId, title: null },
});
},
);
loadEditorProjectMock.mockResolvedValueOnce({
projectId: 'editor-project-default',
title: '默认项目',
@@ -1915,12 +1933,13 @@ describe('ImageCanvasEditorView', () => {
'editor-agent-conv-test',
);
});
fireEvent.change(await screen.findByLabelText('发送给画布 Agent'), {
target: { value: '生成一张图' },
});
fireEvent.click(screen.getByRole('button', { name: '发送' }));
fireEvent.click(await screen.findByRole('button', { name: '确认' }));
await waitFor(() => {
expect(confirmEditorAgentToolCallMock).toHaveBeenCalledWith(
'editor-agent-conv-test',
0,
);
expect(loadEditorProjectMock).toHaveBeenCalledWith(
'editor-project-default',
);
@@ -10,16 +10,15 @@ import {
} from 'react';
import type { ExternalGenerationTaskRecord } from '../../../packages/shared/src/contracts/externalGeneration';
import type { EditorAgentGenerationResultEvent } from '../../../packages/shared/src/contracts/editorAgent';
import { loadFrontendRuntimeConfig } from '../../services/frontendRuntimeConfigService';
import {
createEditorAsset,
createEditorProjectResource,
type EditorAssetSnapshot,
type EditorProjectSnapshot,
loadEditorProject,
loadEditorGenerationPricing,
loadEditorProject,
} from '../../services/image-editor/editorProjectClient';
import { loadFrontendRuntimeConfig } from '../../services/frontendRuntimeConfigService';
import { shouldShowRechargeEntry } from '../../services/payment/paymentPlatform';
import { getPlatformProfileDashboard } from '../../services/platform-entry/platformProfileClient';
import { useAuthUi } from '../auth/AuthUiContext';
@@ -78,11 +77,11 @@ import {
} from './useImageCanvasAssetCanvasBridge';
import { useImageCanvasAssetExportWorkflow } from './useImageCanvasAssetExportWorkflow';
import { useImageCanvasAssetLibrary } from './useImageCanvasAssetLibrary';
import { useImageCanvasContextStore } from './useImageCanvasContextStore.ts';
import { useImageCanvasEditorChrome } from './useImageCanvasEditorChrome';
import { useImageCanvasGenerationSurface } from './useImageCanvasGenerationSurface';
import { useImageCanvasKeyboardShortcuts } from './useImageCanvasKeyboardShortcuts';
import { useImageCanvasLayerCommands } from './useImageCanvasLayerCommands';
import { useImageCanvasContextStore } from './useImageCanvasContextStore.ts';
import { useImageCanvasProjectPersistence } from './useImageCanvasProjectPersistence';
import { useImageCanvasStageController } from './useImageCanvasStageController';
import { useImageCanvasStageInteractions } from './useImageCanvasStageInteractions';
@@ -1096,20 +1095,14 @@ export function ImageCanvasEditorView({
},
[applyGeneratedProjectSnapshot, projectId, refreshEditorWalletBalance],
);
const handleEditorAgentGenerationResult = useCallback(
(event: EditorAgentGenerationResultEvent) => {
const hasGeneratedResource = event.images.some((image) =>
image.resourceId?.trim(),
);
if (!projectId || !hasGeneratedResource) {
return;
}
void loadEditorProject(projectId)
.then(applyGeneratedProjectSnapshot)
.catch(() => undefined);
},
[applyGeneratedProjectSnapshot, projectId],
);
const handleEditorAgentCanvasRefreshRequested = useCallback(() => {
if (!projectId) {
return;
}
void loadEditorProject(projectId)
.then(applyGeneratedProjectSnapshot)
.catch(() => undefined);
}, [applyGeneratedProjectSnapshot, projectId]);
const persistUpdatedLayerResource = useCallback(
(layer: CanvasLayer) => {
if (!projectId) {
@@ -2078,7 +2071,8 @@ export function ImageCanvasEditorView({
onActivateGenerationDialog: activateCanvasGenerationDialog,
onFocusExternalTask: focusExternalGenerationTask,
onExternalTasksCompleted: handleExternalGenerationTasksCompleted,
onEditorAgentGenerationResult: handleEditorAgentGenerationResult,
onEditorAgentCanvasRefreshRequested:
handleEditorAgentCanvasRefreshRequested,
onToggleTaskSidebar: toggleTaskSidebar,
onToggleAgentConversation: toggleAgentConversation,
onCropExpandHandlePointerDown: generationSurface.startCropExpandFrameResize,
@@ -7,9 +7,9 @@ import type {
RefObject,
} from 'react';
import type { ExternalGenerationTaskRecord } from '../../../packages/shared/src/contracts/externalGeneration';
import type { EditorAgentGenerationResultEvent } from '../../../packages/shared/src/contracts/editorAgent';
import { EditorAgentConversationPanelView } from '@/src/components/image-editor/EditorAgentConversation/EditorAgentConversationPanelView.tsx';
import type { ExternalGenerationTaskRecord } from '../../../packages/shared/src/contracts/externalGeneration';
import { ImageCanvasBottomToolbarView } from './ImageCanvasBottomToolbarView';
import { ImageCanvasContextMenusView } from './ImageCanvasContextMenusView';
import type {
@@ -126,9 +126,7 @@ export type ImageCanvasStageViewProps = {
onActivateGenerationDialog: (dialog: CanvasGenerationDialogState) => void;
onFocusExternalTask: (task: ExternalGenerationTaskRecord) => void;
onExternalTasksCompleted?: (tasks: ExternalGenerationTaskRecord[]) => void;
onEditorAgentGenerationResult?: (
event: EditorAgentGenerationResultEvent,
) => void;
onEditorAgentCanvasRefreshRequested?: () => void;
onToggleTaskSidebar: () => void;
onToggleAgentConversation: () => void;
onCropExpandHandlePointerDown: (
@@ -259,7 +257,7 @@ export function ImageCanvasStageView({
onActivateGenerationDialog,
onFocusExternalTask,
onExternalTasksCompleted,
onEditorAgentGenerationResult,
onEditorAgentCanvasRefreshRequested,
onToggleTaskSidebar,
onToggleAgentConversation,
onCropExpandHandlePointerDown,
@@ -476,7 +474,7 @@ export function ImageCanvasStageView({
onToggleOpen={onToggleAgentConversation}
layers={layers}
assets={editorAgentAssets}
onGenerationResult={onEditorAgentGenerationResult}
onCanvasRefreshRequested={onEditorAgentCanvasRefreshRequested}
/>
) : null}