Image editor: hide raw Prompt, use Resolution

Remove backend-assembled raw Prompt and copy action from image info; render a lightweight generationInputs snapshot (user panel inputs + reference thumbnails) stored on canvas layers and shown in the image info dialog. Unify canvas display and info to use originalWidth/originalHeight (Resolution) instead of saved Size and hydrate legacy layout width/height only as fallback. Add model/aspectRatio/imageSize options for character/icon generation (frontend state, tests, and client payloads). Increase Axum JSON body limit for character animation endpoint to 12MB for compatibility and prefer submitting persisted objectKey over large Data URLs. Update tests, docs, and related server/frontend code to reflect these behaviors and validations.
This commit is contained in:
2026-06-16 17:06:21 +08:00
parent 7eeff10c67
commit 3a3cc89280
14 changed files with 1041 additions and 135 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+41 -6
View File
@@ -5205,18 +5205,53 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
font-weight: 760;
}
.image-canvas-editor__metadata-prompt {
.image-canvas-editor__metadata-inputs {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.42rem;
}
.image-canvas-editor__metadata-copy {
cursor: pointer;
border: 1px solid rgba(148, 163, 184, 0.28);
background: #ffffff;
color: #334155;
.image-canvas-editor__metadata-input-field {
display: grid;
gap: 0.16rem;
}
.image-canvas-editor__metadata-input-title {
color: #64748b;
font-size: 0.7rem;
font-weight: 820;
}
.image-canvas-editor__metadata-reference-list {
display: grid;
width: 100%;
gap: 0.42rem;
}
.image-canvas-editor__metadata-reference-card {
display: grid;
grid-template-columns: 2.4rem minmax(0, 1fr);
align-items: center;
gap: 0.5rem;
width: 100%;
border: 1px solid rgba(148, 163, 184, 0.26);
border-radius: 0.65rem;
background: rgba(248, 250, 252, 0.92);
padding: 0.38rem;
}
.image-canvas-editor__metadata-reference-card img {
width: 2.4rem;
height: 2.4rem;
border-radius: 0.48rem;
object-fit: cover;
}
.image-canvas-editor__metadata-reference-copy {
display: grid;
min-width: 0;
gap: 0.12rem;
}
@media (max-width: 760px) {
@@ -567,6 +567,48 @@ describe('editorProjectClient', () => {
);
});
it('passes image model options to editor image generation', async () => {
requestJsonMock.mockResolvedValueOnce({
imageSrc: 'data:image/png;base64,character',
width: 1024,
height: 1536,
sourceType: 'generated',
prompt: '角色设定',
actualPrompt: '角色设定',
model: 'gpt-image-2',
provider: 'VectorEngine',
taskId: 'vector-character-1',
});
await generateEditorImage({
prompt: '角色设定',
kind: 'character',
model: 'gpt-image-2',
aspectRatio: '2:3',
imageSize: '1K',
});
expect(requestJsonMock).toHaveBeenCalledWith(
'/api/editor/images/generations',
expect.objectContaining({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
prompt: '角色设定',
kind: 'character',
model: 'gpt-image-2',
aspectRatio: '2:3',
imageSize: '1K',
}),
}),
'生成图片失败',
expect.objectContaining({
timeoutMs: 1_200_000,
retry: { maxRetries: 0 },
}),
);
});
it('generates icon spritesheets through the dedicated backend BFF', async () => {
requestJsonMock.mockResolvedValueOnce({
spritesheetImageSrc: 'data:image/png;base64,sheet',
@@ -614,6 +656,48 @@ describe('editorProjectClient', () => {
);
});
it('passes image model options to icon spritesheet generation', async () => {
requestJsonMock.mockResolvedValueOnce({
spritesheetImageSrc: 'data:image/png;base64,sheet',
spritesheetWidth: 1024,
spritesheetHeight: 1024,
iconImageSrcs: [],
prompt: '图标素材 prompt',
actualPrompt: '图标素材 prompt',
model: 'gpt-image-2',
provider: 'VectorEngine',
taskId: 'icon-spritesheet-task-2',
});
await generateEditorIconSpritesheet({
referenceImageSrc: 'data:image/png;base64,spec',
iconDescriptions: ['返回按钮'],
model: 'gpt-image-2',
aspectRatio: '1:1',
imageSize: '2K',
});
expect(requestJsonMock).toHaveBeenCalledWith(
'/api/editor/icon-spritesheets/generations',
expect.objectContaining({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
referenceImageSrc: 'data:image/png;base64,spec',
iconDescriptions: ['返回按钮'],
model: 'gpt-image-2',
aspectRatio: '1:1',
imageSize: '2K',
}),
}),
'生成图标素材失败',
expect.objectContaining({
timeoutMs: 1_200_000,
retry: { maxRetries: 0 },
}),
);
});
it('passes spec generation size and kind to the backend BFF', async () => {
requestJsonMock.mockResolvedValueOnce({
imageSrc: 'data:image/png;base64,spec',
@@ -8,7 +8,7 @@ const EDITOR_ICON_SPRITESHEET_GENERATION_API =
'/api/editor/icon-spritesheets/generations';
const EDITOR_CHARACTER_ANIMATION_GENERATION_API =
'/api/editor/character-animations/generations';
const EDITOR_ICON_SPRITESHEET_MODEL = 'gemini-3.1-flash-image-preview';
const EDITOR_IMAGE_MODEL_NANOBANANA2 = 'gemini-3.1-flash-image-preview';
const DEFAULT_PROJECT_TITLE = '未命名画布';
const EDITOR_PROJECT_REQUEST_OPTIONS = {
clearAuthOnUnauthorized: false,
@@ -89,6 +89,8 @@ export type EditorImageGenerationInput = {
size?: string;
kind?: 'spec' | 'character' | 'quick-edit';
model?: string;
aspectRatio?: string;
imageSize?: string;
referenceImageSrcs?: string[];
};
@@ -96,6 +98,8 @@ export type EditorIconSpritesheetGenerationInput = {
referenceImageSrc: string;
iconDescriptions: string[];
model?: string;
aspectRatio?: string;
imageSize?: string;
};
export type EditorImageEditInput = {
@@ -477,6 +481,8 @@ export async function generateEditorImage(input: EditorImageGenerationInput) {
...(input.size ? { size: input.size } : {}),
...(input.kind ? { kind: input.kind } : {}),
...(input.model ? { model: input.model } : {}),
...(input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}),
...(input.imageSize ? { imageSize: input.imageSize } : {}),
...(input.referenceImageSrcs?.length
? { referenceImageSrcs: input.referenceImageSrcs }
: {}),
@@ -500,7 +506,9 @@ export async function generateEditorIconSpritesheet(
jsonRequest('POST', {
referenceImageSrc: input.referenceImageSrc,
iconDescriptions: input.iconDescriptions,
model: input.model?.trim() || EDITOR_ICON_SPRITESHEET_MODEL,
model: input.model?.trim() || EDITOR_IMAGE_MODEL_NANOBANANA2,
...(input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}),
...(input.imageSize ? { imageSize: input.imageSize } : {}),
}),
'生成图标素材失败',
{