新增图片生成像素艺术后处理
新增确定性像素网格规整模块并保留第三方 MIT 许可证。 为普通图片、角色和图标图集接入像素艺术选项、并发预算和失败降级。 同步前端交互、请求契约、OpenAPI、项目文档与测试。
This commit is contained in:
@@ -836,6 +836,10 @@ describe('ImageCanvasEditorView generation integration', () => {
|
||||
name: '生成图片模型 nanobanana2',
|
||||
}).className,
|
||||
).toContain('platform-inline-option-button');
|
||||
const pixelArtToggle = within(generateDialog).getByRole('checkbox', {
|
||||
name: '像素艺术',
|
||||
}) as HTMLInputElement;
|
||||
expect(pixelArtToggle.checked).toBe(false);
|
||||
expect(
|
||||
within(generateDialog).getByRole('button', { name: '生成' }).className,
|
||||
).toContain('platform-button');
|
||||
@@ -847,6 +851,7 @@ describe('ImageCanvasEditorView generation integration', () => {
|
||||
fireEvent.change(screen.getByLabelText('生成提示词'), {
|
||||
target: { value: '一张明亮的拼图主视觉' },
|
||||
});
|
||||
fireEvent.click(pixelArtToggle);
|
||||
fireEvent.click(
|
||||
within(generateDialog).getByRole('button', { name: '生成' }),
|
||||
);
|
||||
@@ -857,6 +862,7 @@ describe('ImageCanvasEditorView generation integration', () => {
|
||||
expect.objectContaining({
|
||||
prompt: '一张明亮的拼图主视觉',
|
||||
model: 'gemini-3.1-flash-image-preview',
|
||||
style: 'pixelArt',
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '1K',
|
||||
projectId: 'editor-project-default',
|
||||
@@ -865,6 +871,11 @@ describe('ImageCanvasEditorView generation integration', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
const submittedGenerationInputs =
|
||||
generateEditorImageMock.mock.calls[0]?.[0]?.generationInputs;
|
||||
expect(JSON.stringify(submittedGenerationInputs)).not.toContain(
|
||||
'pixelArt',
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByAltText(/画布图片:生成图片/)).toBeTruthy();
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
createLayerFromAsset,
|
||||
DEFAULT_CANVAS_BACKGROUND_COLOR,
|
||||
formatCanvasDisplayScalePercent,
|
||||
hydrateCanvasGenerationDialog,
|
||||
hydrateLayer,
|
||||
normalizeAssetLibrary,
|
||||
normalizeCanvasBackgroundHex,
|
||||
@@ -631,6 +632,7 @@ describe('ImageCanvasEditorModel', () => {
|
||||
composerOpen: false,
|
||||
generatedLayerId: 'layer-generated',
|
||||
imageModel: 'gpt-image-2',
|
||||
style: 'pixelArt',
|
||||
generationStartedAt: 1_771_400_000_000,
|
||||
generationFinishedAt: 1_771_400_004_000,
|
||||
placeholder: {
|
||||
@@ -688,6 +690,7 @@ describe('ImageCanvasEditorModel', () => {
|
||||
status: 'generating',
|
||||
generatedLayerId: 'layer-generated',
|
||||
imageModel: 'gpt-image-2',
|
||||
style: 'pixelArt',
|
||||
generationStartedAt: 1_771_400_000_000,
|
||||
generationFinishedAt: 1_771_400_004_000,
|
||||
placeholder: {
|
||||
@@ -704,6 +707,35 @@ describe('ImageCanvasEditorModel', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('defaults restored supported image styles to none and drops them from other modes', () => {
|
||||
expect(
|
||||
hydrateCanvasGenerationDialog({
|
||||
id: 'generation-dialog-legacy',
|
||||
mode: 'generate',
|
||||
prompt: '旧任务',
|
||||
status: 'idle',
|
||||
})?.style,
|
||||
).toBe('none');
|
||||
expect(
|
||||
hydrateCanvasGenerationDialog({
|
||||
id: 'generation-dialog-unknown-style',
|
||||
mode: 'character',
|
||||
prompt: '未知风格',
|
||||
status: 'idle',
|
||||
style: 'futureStyle',
|
||||
})?.style,
|
||||
).toBe('none');
|
||||
expect(
|
||||
hydrateCanvasGenerationDialog({
|
||||
id: 'generation-dialog-spec',
|
||||
mode: 'spec',
|
||||
prompt: '规范任务',
|
||||
status: 'idle',
|
||||
style: 'pixelArt',
|
||||
})?.style,
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('drops restored generator references owned by another user', () => {
|
||||
const dialog: CanvasGenerationDialogState = {
|
||||
id: 'generation-dialog-owner',
|
||||
|
||||
@@ -537,6 +537,14 @@ export function hydrateCanvasGenerationDialog(
|
||||
if (!id || !isCanvasGenerationDialogMode(snapshot.mode)) {
|
||||
return null;
|
||||
}
|
||||
const style =
|
||||
snapshot.mode === 'generate' ||
|
||||
snapshot.mode === 'character' ||
|
||||
snapshot.mode === 'icon'
|
||||
? snapshot.style === 'pixelArt'
|
||||
? 'pixelArt'
|
||||
: 'none'
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
id,
|
||||
@@ -601,6 +609,7 @@ export function hydrateCanvasGenerationDialog(
|
||||
currentUserId,
|
||||
),
|
||||
imageModel: stringOrUndefined(snapshot.imageModel),
|
||||
style,
|
||||
videoModel:
|
||||
typeof snapshot.videoModel === 'string'
|
||||
? snapshot.videoModel
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
EditorCharacterAnimationGenerationResult,
|
||||
EditorCharacterAnimationRatio,
|
||||
EditorCharacterAnimationResolution,
|
||||
EditorImageGenerationStyle,
|
||||
EditorVideoAspectRatio,
|
||||
EditorVideoModel,
|
||||
EditorVideoResolution,
|
||||
@@ -215,6 +216,7 @@ export type GenerateDialogState = {
|
||||
publicationReferences?: CharacterReferenceImage[];
|
||||
uiDesignSpecReference?: CharacterReferenceImage | null;
|
||||
imageModel?: string;
|
||||
style?: EditorImageGenerationStyle;
|
||||
videoModel?: EditorVideoModel;
|
||||
videoAspectRatio?: EditorVideoAspectRatio;
|
||||
videoResolution?: EditorVideoResolution;
|
||||
|
||||
@@ -69,6 +69,7 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
mode: 'generate',
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
style: 'none',
|
||||
placeholder: {
|
||||
x: -312,
|
||||
y: -332,
|
||||
@@ -156,6 +157,7 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
}),
|
||||
).toMatchObject({
|
||||
mode: 'character',
|
||||
style: 'none',
|
||||
imageModel: 'gpt-image-2',
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '1K',
|
||||
@@ -176,6 +178,7 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
}),
|
||||
).toMatchObject({
|
||||
mode: 'icon',
|
||||
style: 'none',
|
||||
imageModel: 'unknown-model',
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '1K',
|
||||
@@ -609,6 +612,7 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
imageModel: 'gpt-image-2',
|
||||
aspectRatio: '2:3',
|
||||
imageSize: '2K',
|
||||
style: 'pixelArt',
|
||||
},
|
||||
mode: 'redraw',
|
||||
}),
|
||||
@@ -619,6 +623,7 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
imageModel: 'gpt-image-2',
|
||||
aspectRatio: '2:3',
|
||||
imageSize: '2K',
|
||||
style: 'none',
|
||||
placeholder: {
|
||||
x: 472,
|
||||
y: 140,
|
||||
|
||||
@@ -196,6 +196,7 @@ export function createGenerateDialogDraft({
|
||||
prompt: '',
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
style: 'none',
|
||||
imageModel: DEFAULT_IMAGE_MODEL,
|
||||
aspectRatio: dimensionDefaults.aspectRatio,
|
||||
imageSize: dimensionDefaults.imageSize,
|
||||
@@ -269,6 +270,7 @@ export function createCharacterGenerationDialogDraft({
|
||||
prompt: '',
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
style: 'none',
|
||||
characterSpecReference: null,
|
||||
characterReferences: [],
|
||||
imageModel: normalizedImageModel,
|
||||
@@ -307,6 +309,7 @@ export function createIconGenerationDialogDraft({
|
||||
prompt: '',
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
style: 'none',
|
||||
iconSpecReference: null,
|
||||
generationReferences: [],
|
||||
iconDescriptions: [],
|
||||
|
||||
@@ -47,6 +47,7 @@ function ImageOptionsHarness({
|
||||
<output aria-label="当前模型">{dialog.imageModel}</output>
|
||||
<output aria-label="当前比例">{dialog.aspectRatio}</output>
|
||||
<output aria-label="当前尺寸">{dialog.imageSize}</output>
|
||||
<output aria-label="当前风格">{dialog.style ?? '-'}</output>
|
||||
<output aria-label="当前状态">{dialog.status}</output>
|
||||
<output aria-label="当前错误">{dialog.errorMessage ?? '-'}</output>
|
||||
<output aria-label="当前占位">
|
||||
@@ -66,6 +67,67 @@ function ImageOptionsHarness({
|
||||
}
|
||||
|
||||
describe('ImageCanvasGenerationImageOptionsView', () => {
|
||||
it.each(['generate', 'character', 'icon'] as const)(
|
||||
'shows and updates the pixel art style for %s generation',
|
||||
(mode) => {
|
||||
render(
|
||||
<ImageOptionsHarness
|
||||
initialDialog={{
|
||||
mode,
|
||||
prompt: '',
|
||||
status: 'failed',
|
||||
errorMessage: '旧错误',
|
||||
style: 'none',
|
||||
imageModel: IMAGE_MODEL_NANOBANANA2,
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '1K',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
const toggle = screen.getByRole('checkbox', {
|
||||
name: '像素艺术',
|
||||
}) as HTMLInputElement;
|
||||
const modelButton = screen.getByRole('button', {
|
||||
name: '生成图片模型 nanobanana2',
|
||||
});
|
||||
expect(toggle.checked).toBe(false);
|
||||
expect(
|
||||
toggle.compareDocumentPosition(modelButton) &
|
||||
Node.DOCUMENT_POSITION_FOLLOWING,
|
||||
).not.toBe(0);
|
||||
|
||||
fireEvent.click(toggle);
|
||||
|
||||
expect(toggle.checked).toBe(true);
|
||||
expect(screen.getByLabelText('当前风格').textContent).toBe('pixelArt');
|
||||
expect(screen.getByLabelText('当前状态').textContent).toBe('idle');
|
||||
expect(screen.getByLabelText('当前错误').textContent).toBe('-');
|
||||
},
|
||||
);
|
||||
|
||||
it.each(['spec', 'quick-edit', 'ui-design', 'publication'] as const)(
|
||||
'does not show the pixel art style for %s generation',
|
||||
(mode) => {
|
||||
render(
|
||||
<ImageOptionsHarness
|
||||
initialDialog={{
|
||||
mode,
|
||||
prompt: '',
|
||||
status: 'idle',
|
||||
imageModel: IMAGE_MODEL_NANOBANANA2,
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '1K',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.queryByRole('checkbox', { name: '像素艺术' }),
|
||||
).toBeNull();
|
||||
},
|
||||
);
|
||||
|
||||
it('updates dimensions from a menu, keeps the menu open and marks the selection', () => {
|
||||
render(
|
||||
<ImageOptionsHarness
|
||||
|
||||
@@ -137,6 +137,10 @@ export function ImageCanvasGenerationImageOptionsView({
|
||||
const dimensionsButtonRef = useRef<HTMLButtonElement | null>(null);
|
||||
const modelButtonRef = useRef<HTMLButtonElement | null>(null);
|
||||
const isQuickEdit = dialog.mode === 'quick-edit';
|
||||
const supportsImageStyle =
|
||||
dialog.mode === 'generate' ||
|
||||
dialog.mode === 'character' ||
|
||||
dialog.mode === 'icon';
|
||||
const normalizedLockedModel = lockedModel
|
||||
? normalizeEditorImageModel(lockedModel)
|
||||
: null;
|
||||
@@ -306,6 +310,21 @@ export function ImageCanvasGenerationImageOptionsView({
|
||||
: null}
|
||||
</div>
|
||||
) : null}
|
||||
{supportsImageStyle ? (
|
||||
<label className="image-canvas-editor__image-style-toggle">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={dialog.style === 'pixelArt'}
|
||||
disabled={isGenerating}
|
||||
onChange={(event) =>
|
||||
updateDialog({
|
||||
style: event.target.checked ? 'pixelArt' : 'none',
|
||||
})
|
||||
}
|
||||
/>
|
||||
<span>像素艺术</span>
|
||||
</label>
|
||||
) : null}
|
||||
{includeModel ? (
|
||||
<div
|
||||
className={[
|
||||
|
||||
@@ -47,6 +47,7 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
|
||||
input: {
|
||||
prompt: '一张发光主视觉',
|
||||
model: 'gemini-3.1-flash-image-preview',
|
||||
style: 'none',
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '1K',
|
||||
},
|
||||
@@ -193,6 +194,24 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('does not send image style from an incomplete quick-edit snapshot', () => {
|
||||
const plan = buildImageGenerationSubmissionPlan({
|
||||
dialog: {
|
||||
mode: 'quick-edit',
|
||||
prompt: '把当前图改成雨天',
|
||||
status: 'idle',
|
||||
style: 'pixelArt',
|
||||
},
|
||||
layers: [],
|
||||
nextGeneratedIndex: 4,
|
||||
});
|
||||
|
||||
expect(plan.kind).toBe('image');
|
||||
expect(plan.kind === 'image' ? plan.input : null).not.toHaveProperty(
|
||||
'style',
|
||||
);
|
||||
});
|
||||
|
||||
it('builds spec generation plans with reference prompt semantics', () => {
|
||||
const plan = buildImageGenerationSubmissionPlan({
|
||||
dialog: {
|
||||
@@ -294,6 +313,7 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
|
||||
prompt: ' 白发骑士 ',
|
||||
status: 'idle',
|
||||
imageModel: 'gpt-image-2',
|
||||
style: 'pixelArt',
|
||||
aspectRatio: '2:3',
|
||||
imageSize: '2K',
|
||||
characterSpecReference: {
|
||||
@@ -324,6 +344,7 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
|
||||
model: 'gpt-image-2',
|
||||
screenColor: 'auto',
|
||||
segModel: 'birefnet',
|
||||
style: 'pixelArt',
|
||||
aspectRatio: '2:3',
|
||||
imageSize: '2K',
|
||||
referenceImageSrcs: [
|
||||
@@ -432,6 +453,7 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
|
||||
mode: 'publication',
|
||||
prompt: '',
|
||||
status: 'idle',
|
||||
style: 'pixelArt',
|
||||
publicationGameInfo: {
|
||||
gameName: ' 重庆洪崖洞火锅 ',
|
||||
gameCategories: '抓大鹅、休闲、治愈、手绘风',
|
||||
@@ -665,6 +687,7 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
|
||||
prompt: ' 返回按钮 \n\n设置按钮',
|
||||
status: 'idle',
|
||||
imageModel: 'gpt-image-2',
|
||||
style: 'pixelArt',
|
||||
aspectRatio: '3:2',
|
||||
imageSize: '2K',
|
||||
assetLabel: ' 冒险游戏图标 ',
|
||||
@@ -695,6 +718,7 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
|
||||
model: 'gpt-image-2',
|
||||
screenColor: 'auto',
|
||||
segModel: 'birefnet',
|
||||
style: 'pixelArt',
|
||||
aspectRatio: '3:2',
|
||||
imageSize: '2K',
|
||||
},
|
||||
|
||||
@@ -354,6 +354,7 @@ export function buildImageGenerationSubmissionPlan({
|
||||
model: imageModel,
|
||||
screenColor,
|
||||
segModel,
|
||||
style: dialog.style === 'pixelArt' ? 'pixelArt' : 'none',
|
||||
aspectRatio: dialog.aspectRatio ?? '1:1',
|
||||
imageSize: dialog.imageSize ?? '1K',
|
||||
...(referenceImageSrcs.length ? { referenceImageSrcs } : {}),
|
||||
@@ -564,6 +565,9 @@ export function buildImageGenerationSubmissionPlan({
|
||||
input: {
|
||||
prompt: normalizedPrompt,
|
||||
model: imageModel,
|
||||
...(dialog.mode === 'generate'
|
||||
? { style: dialog.style === 'pixelArt' ? 'pixelArt' : 'none' }
|
||||
: {}),
|
||||
aspectRatio: dialog.aspectRatio ?? '1:1',
|
||||
imageSize: dialog.imageSize ?? '1K',
|
||||
...(dialog.generationReferences?.length
|
||||
@@ -639,6 +643,7 @@ export function buildIconSpritesheetGenerationSubmissionPlan(
|
||||
model: rememberImageModel,
|
||||
screenColor,
|
||||
segModel,
|
||||
style: dialog.style === 'pixelArt' ? 'pixelArt' : 'none',
|
||||
aspectRatio: dialog.aspectRatio ?? '1:1',
|
||||
imageSize: dialog.imageSize ?? '1K',
|
||||
},
|
||||
|
||||
@@ -7079,6 +7079,33 @@ button.image-canvas-editor__reference-chip:disabled {
|
||||
width: 8.5rem;
|
||||
}
|
||||
|
||||
.image-canvas-editor__image-style-toggle {
|
||||
grid-column: 3;
|
||||
display: inline-flex;
|
||||
min-height: 2.25rem;
|
||||
align-items: center;
|
||||
justify-self: end;
|
||||
gap: 0.38rem;
|
||||
padding: 0 0.5rem;
|
||||
color: #475569;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 760;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image-canvas-editor__image-style-toggle input {
|
||||
width: 0.95rem;
|
||||
height: 0.95rem;
|
||||
margin: 0;
|
||||
accent-color: var(--image-canvas-brand-fill);
|
||||
}
|
||||
|
||||
.image-canvas-editor__image-style-toggle:has(input:disabled) {
|
||||
color: #94a3b8;
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.image-canvas-editor__model-trigger-label {
|
||||
display: inline-flex;
|
||||
min-width: 0;
|
||||
@@ -8904,6 +8931,7 @@ button.image-canvas-editor__reference-chip:disabled {
|
||||
|
||||
.image-canvas-editor__option-cluster--dimensions,
|
||||
.image-canvas-editor__option-cluster--model,
|
||||
.image-canvas-editor__image-style-toggle,
|
||||
.image-canvas-editor__readonly-generation-option,
|
||||
.image-canvas-editor__generation-submit,
|
||||
.image-canvas-editor__character-animation-submit {
|
||||
@@ -8913,6 +8941,10 @@ button.image-canvas-editor__reference-chip:disabled {
|
||||
justify-self: stretch;
|
||||
}
|
||||
|
||||
.image-canvas-editor__image-style-toggle {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.image-canvas-editor__generation-submit {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -838,6 +838,7 @@ describe('editorProjectClient', () => {
|
||||
model: 'gpt-image-2',
|
||||
screenColor: '#FFD6C2',
|
||||
segModel: 'anime-seg',
|
||||
style: 'pixelArt',
|
||||
aspectRatio: '2:3',
|
||||
imageSize: '1K',
|
||||
projectId: 'editor-project-1',
|
||||
@@ -862,6 +863,7 @@ describe('editorProjectClient', () => {
|
||||
model: 'gpt-image-2',
|
||||
screenColor: '#FFD6C2',
|
||||
segModel: 'anime-seg',
|
||||
style: 'pixelArt',
|
||||
aspectRatio: '2:3',
|
||||
imageSize: '1K',
|
||||
projectId: 'editor-project-1',
|
||||
@@ -1015,6 +1017,7 @@ describe('editorProjectClient', () => {
|
||||
model: 'gpt-image-2',
|
||||
screenColor: '#E6D8FF',
|
||||
segModel: 'anime-seg',
|
||||
style: 'none',
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '2K',
|
||||
projectId: 'editor-project-1',
|
||||
@@ -1036,6 +1039,7 @@ describe('editorProjectClient', () => {
|
||||
model: 'gpt-image-2',
|
||||
screenColor: '#E6D8FF',
|
||||
segModel: 'anime-seg',
|
||||
style: 'none',
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '2K',
|
||||
projectId: 'editor-project-1',
|
||||
|
||||
@@ -184,6 +184,8 @@ export type EditorAssetLibrarySnapshot = {
|
||||
assets: EditorAssetSnapshot[];
|
||||
};
|
||||
|
||||
export type EditorImageGenerationStyle = 'none' | 'pixelArt';
|
||||
|
||||
export type EditorImageGenerationInput = {
|
||||
prompt: string;
|
||||
size?: string;
|
||||
@@ -196,6 +198,7 @@ export type EditorImageGenerationInput = {
|
||||
model?: string;
|
||||
screenColor?: string;
|
||||
segModel?: string;
|
||||
style?: EditorImageGenerationStyle;
|
||||
aspectRatio?: string;
|
||||
imageSize?: string;
|
||||
referenceImageSrcs?: string[];
|
||||
@@ -228,6 +231,7 @@ export type EditorIconSpritesheetGenerationInput = {
|
||||
model?: string;
|
||||
screenColor?: string;
|
||||
segModel?: string;
|
||||
style?: EditorImageGenerationStyle;
|
||||
aspectRatio?: string;
|
||||
imageSize?: string;
|
||||
projectId?: string | null;
|
||||
@@ -920,6 +924,7 @@ export async function generateEditorImage(input: EditorImageGenerationInput) {
|
||||
...(input.model ? { model: input.model } : {}),
|
||||
...(input.screenColor ? { screenColor: input.screenColor } : {}),
|
||||
...(input.segModel ? { segModel: input.segModel } : {}),
|
||||
...(input.style ? { style: input.style } : {}),
|
||||
...(input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}),
|
||||
...(input.imageSize ? { imageSize: input.imageSize } : {}),
|
||||
...(input.referenceImageSrcs?.length
|
||||
@@ -966,6 +971,7 @@ export async function generateEditorIconSpritesheet(
|
||||
model: input.model?.trim() || EDITOR_IMAGE_MODEL_NANOBANANA2,
|
||||
...(input.screenColor ? { screenColor: input.screenColor } : {}),
|
||||
...(input.segModel ? { segModel: input.segModel } : {}),
|
||||
...(input.style ? { style: input.style } : {}),
|
||||
...(input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}),
|
||||
...(input.imageSize ? { imageSize: input.imageSize } : {}),
|
||||
...(input.projectId ? { projectId: input.projectId } : {}),
|
||||
|
||||
Reference in New Issue
Block a user