新增画布完美像素功能
在图片工具栏接入完美像素交互、占位流程与历史保护 新增严格像素网格检测和登录态同步处理接口 补齐素材类型校验、持久化边界、并发限制及技术文档 修复 Windows rlib 门禁长文件名解析
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
||||
removeEditorImageBackground,
|
||||
renameEditorProject,
|
||||
saveEditorProjectLayout,
|
||||
snapEditorImageToPixelArt,
|
||||
splitEditorIconSpritesheet,
|
||||
submitEditorAssetShowcase,
|
||||
toggleEditorShowcaseAssetLike,
|
||||
@@ -1741,4 +1742,103 @@ describe('editorProjectClient', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('submits a stable perfect-pixel source without unsafe POST retries', async () => {
|
||||
requestJsonMock.mockResolvedValueOnce({
|
||||
imageSrc: '/generated-images/editor/perfect-pixel.png',
|
||||
objectKey: 'generated-images/editor/perfect-pixel.png',
|
||||
assetObjectId: 'asset-object-perfect-pixel',
|
||||
width: 1024,
|
||||
height: 768,
|
||||
sourceType: 'generated',
|
||||
taskId: 'perfect-pixel-1',
|
||||
elapsedMs: 321,
|
||||
provider: 'Genarrative',
|
||||
resource: { resourceId: 'resource-perfect-pixel' },
|
||||
asset: { assetId: 'asset-perfect-pixel' },
|
||||
project: null,
|
||||
});
|
||||
|
||||
const result = await snapEditorImageToPixelArt({
|
||||
sourceImageSrc: 'generated-images/editor/source.png',
|
||||
projectId: 'editor-project-1',
|
||||
sourceResourceId: 'resource-source',
|
||||
assetKind: 'character',
|
||||
generationInputs: {
|
||||
fields: [{ title: '角色设定', value: '红发骑士' }],
|
||||
references: [],
|
||||
},
|
||||
assetFolderId: 'project',
|
||||
assetLabel: '源图 · 完美像素',
|
||||
canvasCompletion: {
|
||||
dialogId: 'generation-dialog-perfect-pixel',
|
||||
title: '源图 · 完美像素',
|
||||
placeholder: {
|
||||
x: 472,
|
||||
y: 140,
|
||||
width: 320,
|
||||
height: 240,
|
||||
originalWidth: 320,
|
||||
originalHeight: 240,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.taskId).toBe('perfect-pixel-1');
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
'/api/editor/images/pixel-art-snaps',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sourceImageSrc: 'generated-images/editor/source.png',
|
||||
projectId: 'editor-project-1',
|
||||
sourceResourceId: 'resource-source',
|
||||
assetKind: 'character',
|
||||
generationInputs: {
|
||||
fields: [{ title: '角色设定', value: '红发骑士' }],
|
||||
references: [],
|
||||
},
|
||||
assetFolderId: 'project',
|
||||
assetLabel: '源图 · 完美像素',
|
||||
canvasCompletion: {
|
||||
dialogId: 'generation-dialog-perfect-pixel',
|
||||
title: '源图 · 完美像素',
|
||||
placeholder: {
|
||||
x: 472,
|
||||
y: 140,
|
||||
width: 320,
|
||||
height: 240,
|
||||
originalWidth: 320,
|
||||
originalHeight: 240,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
'完美像素处理失败',
|
||||
{ timeoutMs: 120_000 },
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects inline perfect-pixel media before sending the request', async () => {
|
||||
await expect(
|
||||
snapEditorImageToPixelArt({
|
||||
sourceImageSrc: 'data:image/png;base64,source',
|
||||
projectId: 'editor-project-1',
|
||||
canvasCompletion: {
|
||||
dialogId: 'generation-dialog-perfect-pixel',
|
||||
title: '源图 · 完美像素',
|
||||
placeholder: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 320,
|
||||
height: 240,
|
||||
originalWidth: 320,
|
||||
originalHeight: 240,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow('待完美像素处理图片必须先上传 OSS');
|
||||
expect(requestJsonMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ const EDITOR_PROJECT_RESOURCE_API_BASE = '/api/editor/project-resources';
|
||||
const EDITOR_IMAGE_GENERATION_API = '/api/editor/images/generations';
|
||||
const EDITOR_IMAGE_EDIT_API = '/api/editor/images/edits';
|
||||
const EDITOR_BACKGROUND_REMOVAL_API = '/api/editor/images/background-removals';
|
||||
const EDITOR_PIXEL_ART_SNAP_API = '/api/editor/images/pixel-art-snaps';
|
||||
const EDITOR_ICON_SPRITESHEET_GENERATION_API =
|
||||
'/api/editor/icon-spritesheets/generations';
|
||||
const EDITOR_ICON_SPRITESHEET_SLICE_API =
|
||||
@@ -294,6 +295,19 @@ export type EditorBackgroundRemovalInput = {
|
||||
canvasCompletion?: EditorCanvasGenerationCompletionInput | null;
|
||||
};
|
||||
|
||||
export type EditorPixelArtSnapInput = {
|
||||
sourceImageSrc: string;
|
||||
projectId: string;
|
||||
sourceResourceId?: string | null;
|
||||
assetKind?: string | null;
|
||||
generationInputs?: EditorAssetGenerationInputs | null;
|
||||
assetFolderId?: string | null;
|
||||
assetLabel?: string | null;
|
||||
canvasCompletion: EditorCanvasGenerationCompletionInput & {
|
||||
dialogId: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type EditorImageGenerationResult = {
|
||||
imageSrc: string;
|
||||
objectKey?: string | null;
|
||||
@@ -317,6 +331,21 @@ export type EditorBackgroundRemovalResult = {
|
||||
queueState: ExternalGenerationJobStatusRecord;
|
||||
};
|
||||
|
||||
export type EditorPixelArtSnapResult = {
|
||||
imageSrc: string;
|
||||
objectKey: string;
|
||||
assetObjectId: string;
|
||||
width: number;
|
||||
height: number;
|
||||
sourceType: 'generated';
|
||||
taskId: string;
|
||||
elapsedMs: number;
|
||||
provider: 'Genarrative';
|
||||
resource: EditorProjectResourceSnapshot;
|
||||
asset: EditorAssetSnapshot;
|
||||
project: EditorProjectSnapshot | null;
|
||||
};
|
||||
|
||||
export type EditorIconSpritesheetIconResult = {
|
||||
name: string;
|
||||
imageSrc: string;
|
||||
@@ -659,6 +688,7 @@ type EditorShowcaseAssetResponse = {
|
||||
|
||||
type EditorImageGenerationResponse = EditorImageGenerationResult;
|
||||
type EditorBackgroundRemovalResponse = EditorBackgroundRemovalResult;
|
||||
type EditorPixelArtSnapResponse = EditorPixelArtSnapResult;
|
||||
type EditorIconSpritesheetGenerationResponse =
|
||||
EditorIconSpritesheetGenerationResult;
|
||||
type EditorVideoGenerationResponse = EditorVideoGenerationResult;
|
||||
@@ -1114,6 +1144,33 @@ export async function removeEditorImageBackground(
|
||||
);
|
||||
}
|
||||
|
||||
export async function snapEditorImageToPixelArt(
|
||||
input: EditorPixelArtSnapInput,
|
||||
) {
|
||||
assertStableEditorMediaReference(input.sourceImageSrc, '待完美像素处理图片');
|
||||
return requestJson<EditorPixelArtSnapResponse>(
|
||||
EDITOR_PIXEL_ART_SNAP_API,
|
||||
jsonRequest('POST', {
|
||||
sourceImageSrc: input.sourceImageSrc,
|
||||
projectId: input.projectId,
|
||||
...(input.sourceResourceId
|
||||
? { sourceResourceId: input.sourceResourceId }
|
||||
: {}),
|
||||
...(input.assetKind ? { assetKind: input.assetKind } : {}),
|
||||
...(input.generationInputs
|
||||
? { generationInputs: input.generationInputs }
|
||||
: {}),
|
||||
...(input.assetFolderId ? { assetFolderId: input.assetFolderId } : {}),
|
||||
...(input.assetLabel ? { assetLabel: input.assetLabel } : {}),
|
||||
canvasCompletion: input.canvasCompletion,
|
||||
}),
|
||||
'完美像素处理失败',
|
||||
{
|
||||
timeoutMs: 120_000,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateEditorCharacterAnimation(
|
||||
input: EditorCharacterAnimationGenerationInput,
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user