feat: 完善图像工作区与创作工具体验
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-08-16 14:08:02 +08:00
parent bfcb88cfef
commit 26b52d76e3
92 changed files with 16678 additions and 2975 deletions

View File

@@ -10,6 +10,7 @@ import {
confirmImageWorkspaceGeneration,
createImageWorkspaceConversation,
createImageWorkspaceProject,
deleteImageWorkspaceProject,
fetchImageWorkspace,
fetchImageWorkspaceConversation,
ImageWorkspaceApiError,
@@ -17,6 +18,7 @@ import {
resolveImageWorkspaceAssetUrl,
saveImageWorkspaceAsset,
sendImageWorkspaceMessage,
updateImageWorkspaceGenerationQuote,
uploadImageWorkspaceAsset,
} from '@/lib/image-workspace';
@@ -83,6 +85,18 @@ describe('AI design renderer API boundary', () => {
});
});
it('deletes a Workspace through its encoded Main-owned route', async () => {
const result = { workspaceId: 'workspace/one', deleted: true as const };
hostApiFetchMock.mockResolvedValueOnce({ success: true, status: 200, data: result });
await expect(deleteImageWorkspaceProject('workspace/one')).resolves.toEqual(result);
expect(hostApiFetchMock).toHaveBeenCalledWith(
'/api/works/image-workspace/workspaces/workspace%2Fone',
{ method: 'DELETE' },
);
});
it('creates and reads Conversations through encoded Main-owned routes', async () => {
await createImageWorkspaceConversation(
'workspace/one',
@@ -190,6 +204,49 @@ describe('AI design renderer API boundary', () => {
});
});
it('re-quotes a Quote through the Main-owned PATCH route without adding defaults', async () => {
const quote = {
quoteId: 'quote-one',
status: 'active',
medium: 'video',
briefVersion: 3,
briefSummary: '校园海报转为动态视觉',
finalPrompt: '用户最新编辑后的完整提示词',
promptMode: 'guided',
generationParameters: {
model: 'wan2.7-i2v-2026-04-25',
resolution: '720P',
aspectRatio: '16:9',
durationSeconds: 6,
},
generationOptions: { models: [], resolutions: [], durations: [], aspectRatios: [] },
pricing: { schema: 'design-pricing-v2', amount: 9, rounding: 'ceil' },
quotedDesignPoints: 9,
expiresAt: '2026-08-15T10:30:00Z',
} as const;
hostApiFetchMock.mockResolvedValueOnce({ success: true, status: 200, data: quote });
await expect(updateImageWorkspaceGenerationQuote(
'workspace-one',
'quote-one',
quote.finalPrompt,
quote.generationParameters,
)).resolves.toEqual(quote);
const [path, init] = hostApiFetchMock.mock.calls[0];
expect(path).toBe(
'/api/works/image-workspace/workspaces/workspace-one/generation-quotes/quote-one',
);
expect(init).toMatchObject({ method: 'PATCH' });
expect(JSON.parse(String(init?.body))).toEqual({
finalPrompt: quote.finalPrompt,
model: quote.generationParameters.model,
resolution: quote.generationParameters.resolution,
aspectRatio: quote.generationParameters.aspectRatio,
durationSeconds: quote.generationParameters.durationSeconds,
});
});
it('preserves structured Main error codes for revision recovery', async () => {
hostApiFetchMock.mockRejectedValueOnce(new AppError(
'UNKNOWN',
@@ -241,4 +298,34 @@ describe('AI design renderer API boundary', () => {
);
expect(JSON.stringify(hostApiFetchMock.mock.calls[0])).not.toContain('private/content');
});
it('uses a video extension when Main saves a generated video asset', async () => {
hostApiFetchMock.mockResolvedValueOnce({
success: true,
status: 200,
data: { status: 'saved' },
});
await saveImageWorkspaceAsset({
assetId: 'video/one',
workspaceId: 'workspace/one',
mediaType: 'video',
mimeType: 'video/mp4',
width: 1920,
height: 1080,
durationMilliseconds: 4_000,
createdAt: '2026-08-05T12:00:00Z',
contentPath: '/api/works/image-workspace/private/video',
});
expect(hostApiFetchMock).toHaveBeenCalledWith(
'/api/works/image-workspace/workspaces/workspace%2Fone/assets/video%2Fone/download',
{
method: 'POST',
body: JSON.stringify({
defaultFileName: 'Makelore-AI-Design-video-one.mp4',
}),
},
);
});
});