新抠图算法(轻量版)上线 (#73)

新抠图算法(轻量版)上线,生图背景色同步改为多颜色+自动决策

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/73
Reviewed-by: kdletters <kdletters@qq.com>
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
This commit was merged in pull request #73.
This commit is contained in:
2026-07-07 14:57:10 +08:00
committed by kdletters
parent 0c14e51419
commit 308d440816
30 changed files with 1650 additions and 387 deletions
File diff suppressed because it is too large Load Diff
@@ -481,7 +481,7 @@ describe('ImageCanvasGenerationDialogModel', () => {
{
title: '提取提示词',
value:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
},
{ title: 'model', value: 'gpt-image-2' },
],
@@ -496,13 +496,17 @@ describe('ImageCanvasGenerationDialogModel', () => {
const canvasSize = { width: 960, height: 720 };
const viewport = { x: 0, y: 0, scale: 1 };
const characterDraft = createSameSourceGenerationDialogDraft({
sourceLayer: createLayer({
assetKind: 'character',
generationInputs: {
fields: [{ title: '角色设定', value: '红发骑士' }],
references: [],
},
}),
sourceLayer: createLayer({
assetKind: 'character',
generationInputs: {
fields: [
{ title: '角色设定', value: '红发骑士' },
{ title: '抠图背景色', value: '暖浅桃色 #FFD6C2' },
{ title: '抠图模型', value: '动漫风格 anime-seg' },
],
references: [],
},
}),
canvasSize,
viewport,
mode: 'redraw',
@@ -738,6 +738,7 @@ function restoreSharedImageOptions(
sourceLayer: CanvasLayer,
sourceDialog?: CanvasGenerationDialogState | null,
): Omit<CanvasGenerationDialogState, 'id'> {
const fields = getGenerationInputFieldValues(sourceLayer);
return {
...draft,
imageModel: sourceDialog?.imageModel ?? draft.imageModel,
@@ -1,4 +1,4 @@
/* @vitest-environment jsdom */
/* @vitest-environment jsdom */
import { fireEvent, render, screen, within } from '@testing-library/react';
import { useState } from 'react';
@@ -15,11 +15,13 @@ import {
function ImageOptionsHarness({
initialDialog,
includeDimensions = true,
onRememberImageModel = vi.fn(),
lockedModel,
}: {
initialDialog: GenerateDialogState;
includeDimensions?: boolean;
onRememberImageModel?: (model: string) => void;
lockedModel?: string;
}) {
@@ -250,7 +250,9 @@ describe('ImageCanvasGenerationModel', () => {
],
),
).toEqual({
fields: [{ title: '角色设定', value: '主角骑士' }],
fields: [
{ title: '角色设定', value: '主角骑士' },
],
references: [
{
title: '角色规范',
@@ -313,7 +315,7 @@ describe('ImageCanvasGenerationModel', () => {
{
title: '提取提示词',
value:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
},
],
references: [
@@ -97,8 +97,10 @@ export const DEFAULT_ICON_DESCRIPTIONS = [
'原图按钮',
'冻结按钮',
];
export const DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR = 'auto';
export const DEFAULT_EDITOR_BGFILTER_SEG_MODEL = 'birefnet';
export const UI_DESIGN_ASSET_EXTRACTION_PROMPT =
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。';
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。';
export const QUICK_EDIT_SIZE_PRESETS = [
'1024x1024',
'1536x1024',
@@ -1203,10 +1205,12 @@ export function buildUiDesignAssetExtractionGenerationInputs(
references?: CharacterReferenceImage[],
): CanvasGenerationInputs {
return {
fields: createGenerationInputField(
'提取提示词',
UI_DESIGN_ASSET_EXTRACTION_PROMPT,
),
fields: [
...createGenerationInputField(
'提取提示词',
UI_DESIGN_ASSET_EXTRACTION_PROMPT,
),
],
references: [
...createLayerGenerationInputReference('UI设计图', sourceLayer),
...(references ?? []).flatMap((reference, index) =>
@@ -301,6 +301,8 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
prompt: '白发骑士',
kind: 'character',
model: 'gpt-image-2',
screenColor: 'auto',
segModel: 'birefnet',
aspectRatio: '2:3',
imageSize: '2K',
referenceImageSrcs: [
@@ -316,7 +318,9 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
});
expect(plan.kind === 'image' ? plan.result.generationInputs : null).toEqual(
{
fields: [{ title: '角色设定', value: '白发骑士' }],
fields: [
{ title: '角色设定', value: '白发骑士' },
],
references: [
{
title: '角色规范',
@@ -665,6 +669,8 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
referenceImageSrcs: ['data:image/png;base64,ref'],
iconDescriptions: ['返回按钮', '设置按钮'],
model: 'gpt-image-2',
screenColor: 'auto',
segModel: 'birefnet',
aspectRatio: '3:2',
imageSize: '2K',
},
@@ -29,7 +29,8 @@ import {
buildUiDesignGenerationInputs,
buildVideoGenerationInputs,
CHARACTER_ANIMATION_MODEL,
DEFAULT_ICON_DESCRIPTIONS,
DEFAULT_EDITOR_BGFILTER_SEG_MODEL,
DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR,
DEFAULT_SOUND_EFFECT_DURATION_SECONDS,
DEFAULT_SOUND_EFFECT_MODEL,
DEFAULT_SPEC_FORM_VALUES,
@@ -347,6 +348,8 @@ export function buildImageGenerationSubmissionPlan({
),
].filter((src): src is string => Boolean(src));
const imageModel = normalizeEditorImageModel(dialog.imageModel);
const screenColor = DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR;
const segModel = DEFAULT_EDITOR_BGFILTER_SEG_MODEL;
return {
kind: 'image',
normalizedPrompt,
@@ -354,6 +357,8 @@ export function buildImageGenerationSubmissionPlan({
prompt: normalizedPrompt,
kind: 'character',
model: imageModel,
screenColor,
segModel,
aspectRatio: dialog.aspectRatio ?? '1:1',
imageSize: dialog.imageSize ?? '1K',
...(referenceImageSrcs.length ? { referenceImageSrcs } : {}),
@@ -569,11 +574,14 @@ export function buildImageGenerationSubmissionPlan({
export function buildIconSpritesheetGenerationSubmissionPlan(
dialog: GenerateDialogState,
): IconSpritesheetGenerationSubmissionPlan {
const iconDescriptions = (
dialog.prompt.trim()
? dialog.prompt.split(/[\r\n,,、;;/|]+/u)
: (dialog.iconDescriptions ?? DEFAULT_ICON_DESCRIPTIONS)
)
const iconDescriptionSource = dialog.prompt.trim()
? dialog.prompt.split(/[\r\n,,、;;/|]+/u)
: (dialog.iconDescriptions ?? []).some((description) =>
description.trim(),
)
? (dialog.iconDescriptions ?? [])
: [];
const iconDescriptions = iconDescriptionSource
.map((description) => description.trim())
.filter(Boolean)
.slice(0, ICON_DESCRIPTION_LIMIT);
@@ -593,6 +601,8 @@ export function buildIconSpritesheetGenerationSubmissionPlan(
}
const rememberImageModel = normalizeEditorImageModel(dialog.imageModel);
const screenColor = DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR;
const segModel = DEFAULT_EDITOR_BGFILTER_SEG_MODEL;
return {
ok: true,
iconDescriptions,
@@ -609,6 +619,8 @@ export function buildIconSpritesheetGenerationSubmissionPlan(
: {}),
iconDescriptions,
model: rememberImageModel,
screenColor,
segModel,
aspectRatio: dialog.aspectRatio ?? '1:1',
imageSize: dialog.imageSize ?? '1K',
},
@@ -48,7 +48,10 @@ export type UiAssetExtractionImageSize =
export function createUiAssetExtractionState(
sourceLayerId: string,
options: { initialTool?: UiAssetExtractionTool | null; model: string },
options: {
initialTool?: UiAssetExtractionTool | null;
model: string;
},
): UiAssetExtractionState {
return {
sourceLayerId,
@@ -172,6 +172,12 @@ describe('ImageCanvasUiAssetExtractionOverlayView', () => {
expect(
within(panel).getByRole('button', { name: '提取素材模型 nanobanana2' }),
).toBeTruthy();
expect(
within(panel).queryByRole('button', { name: /提取素材背景色/u }),
).toBeNull();
expect(
within(panel).queryByRole('button', { name: /提取素材抠图模型/u }),
).toBeNull();
expect(within(panel).getByLabelText('参考UI.png')).toBeTruthy();
fireEvent.click(
within(panel).getByRole('button', { name: '上传UI素材参考图' }),
@@ -1945,9 +1945,9 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
spritesheetWidth: 512,
spritesheetHeight: 512,
prompt:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
actualPrompt:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
model: 'gpt-image-2',
provider: 'VectorEngine',
taskId: 'task-ui-assets',
@@ -1973,6 +1973,8 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
expect.objectContaining({
sourceImageSrc: 'generated/ui-design.png',
model: 'gemini-3.1-flash-image-preview',
screenColor: 'auto',
segModel: 'birefnet',
}),
);
});
@@ -1999,9 +2001,9 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
spritesheetWidth: 512,
spritesheetHeight: 512,
prompt:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
actualPrompt:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
model: 'gpt-image-2',
provider: 'VectorEngine',
taskId: 'task-ui-assets',
@@ -2039,9 +2041,9 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
spritesheetWidth: 512,
spritesheetHeight: 512,
prompt:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
actualPrompt:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
model: 'gpt-image-2',
provider: 'VectorEngine',
taskId: 'task-ui-assets',
@@ -2087,6 +2089,8 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
expect.objectContaining({
sourceImageSrc: 'data:image/png;base64,marked-ui-design',
model: 'gemini-3.1-flash-image-preview',
screenColor: 'auto',
segModel: 'birefnet',
aspectRatio: '1:1',
imageSize: '2K',
generationInputs: expect.objectContaining({
@@ -2094,7 +2098,7 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
expect.objectContaining({
title: '提取提示词',
value:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
}),
]),
}),
@@ -2109,9 +2113,9 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
spritesheetWidth: 512,
spritesheetHeight: 512,
prompt:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
actualPrompt:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕。绿幕背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板、绿色投影或绿色反光。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。',
model: 'gpt-image-2',
provider: 'VectorEngine',
taskId: 'task-ui-assets',
@@ -2135,6 +2139,8 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
await waitFor(() => {
expect(extractEditorUiDesignAssetsMock).toHaveBeenCalledWith(
expect.objectContaining({
screenColor: 'auto',
segModel: 'birefnet',
referenceImageSrcs: ['generated/ref-style.png'],
generationInputs: expect.objectContaining({
references: expect.arrayContaining([
@@ -58,6 +58,8 @@ import {
buildUiDesignAssetExtractionGenerationInputs,
buildVideoGenerationInputs,
CHARACTER_ANIMATION_MODEL,
DEFAULT_EDITOR_BGFILTER_SEG_MODEL,
DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR,
DEFAULT_IMAGE_MODEL,
DEFAULT_VIDEO_MODEL,
ICON_FRAME_DISPLAY_SIZE,
@@ -749,6 +751,8 @@ export function useImageCanvasGenerationSubmissionWorkflow({
marks: options.marks,
});
}
const screenColor = DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR;
const segModel = DEFAULT_EDITOR_BGFILTER_SEG_MODEL;
const generationInputs = buildUiDesignAssetExtractionGenerationInputs(
sourceLayer,
options.references,
@@ -773,6 +777,8 @@ export function useImageCanvasGenerationSubmissionWorkflow({
model: normalizeEditorImageModel(
options.model ?? DEFAULT_IMAGE_MODEL,
),
screenColor,
segModel,
...(referenceImageSrcs.length ? { referenceImageSrcs } : {}),
projectId,
generationInputs,
+53 -18
View File
@@ -7086,7 +7086,7 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
border: 1px solid rgba(15, 23, 42, 0.09);
border-radius: 1.45rem;
background: rgba(255, 255, 255, 0.97);
padding: 0.72rem 0.82rem 0.82rem;
padding: 0.72rem 0.82rem 0.58rem;
color: #1f2937;
font-size: 0.82rem;
box-shadow:
@@ -7178,14 +7178,20 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
.image-canvas-editor__generation-composer-footer {
grid-column: 1 / -1;
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto auto;
grid-template-columns: max-content minmax(0, 1fr) max-content max-content max-content;
align-items: center;
gap: 0.46rem;
gap: 0.10rem;
justify-content: start;
position: relative;
border-top: 1px solid rgba(15, 23, 42, 0.06);
padding-top: 0.44rem;
}
.image-canvas-editor__generation-composer
.image-canvas-editor__generation-composer-footer {
padding-top: 1.1rem;
}
.image-canvas-editor__generation-close {
display: inline-flex;
align-items: center;
@@ -7428,6 +7434,7 @@ button.image-canvas-editor__reference-chip:disabled {
min-height: 2.25rem;
height: 2.25rem;
width: 100%;
min-width: 0;
border: 1px solid transparent;
border-radius: 999px;
background: transparent;
@@ -7436,6 +7443,14 @@ button.image-canvas-editor__reference-chip:disabled {
box-shadow: none;
}
.image-canvas-editor__option-cluster .platform-inline-option-button__label {
display: inline-flex;
min-width: 0;
max-width: 100%;
align-items: center;
overflow: hidden;
}
.image-canvas-editor__option-cluster:hover:not(:disabled),
.image-canvas-editor__option-cluster[aria-expanded='true'] {
border-color: rgba(15, 23, 42, 0.08);
@@ -7463,7 +7478,7 @@ button.image-canvas-editor__reference-chip:disabled {
}
.image-canvas-editor__option-cluster--model {
max-width: min(15.5rem, 32vw);
max-width: 8.5rem;
justify-self: end;
overflow: hidden;
text-overflow: ellipsis;
@@ -7478,11 +7493,13 @@ button.image-canvas-editor__reference-chip:disabled {
.image-canvas-editor__option-popover-anchor--dimensions {
grid-column: 1;
justify-self: start;
width: max-content;
}
.image-canvas-editor__option-popover-anchor--model {
grid-column: 3;
grid-column: 4;
justify-self: end;
width: 8.5rem;
}
.image-canvas-editor__model-trigger-label {
@@ -7862,7 +7879,7 @@ button.image-canvas-editor__reference-chip:disabled {
}
.image-canvas-editor__generation-submit {
grid-column: 4;
grid-column: 5;
min-width: 5.45rem;
height: 2.25rem;
gap: 0.4rem;
@@ -7902,14 +7919,14 @@ button.image-canvas-editor__reference-chip:disabled {
z-index: 13;
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto minmax(5.8rem, auto) auto auto;
width: min(34rem, calc(100% - 1.5rem));
min-height: 11.2rem;
grid-template-rows: auto minmax(5.1rem, auto) auto auto;
width: min(42rem, calc(100% - 1.5rem));
min-height: 10.4rem;
gap: 0.5rem;
border: 1px solid #cfd6df;
border-radius: 1.35rem;
background: #ffffff;
padding: 0.62rem 0.7rem 0.68rem;
padding: 0.62rem 0.7rem 0.44rem;
color: #1f2937;
box-shadow: 0 18px 46px rgba(15, 23, 42, 0.16);
transform: translateX(-50%);
@@ -7917,7 +7934,7 @@ button.image-canvas-editor__reference-chip:disabled {
.image-canvas-editor__character-composer
.image-canvas-editor__generation-prompt {
min-height: 5.8rem;
min-height: 5.1rem;
padding-top: 0.42rem;
}
@@ -7989,24 +8006,31 @@ button.image-canvas-editor__reference-chip:disabled {
z-index: 13;
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto minmax(5.8rem, auto) auto auto;
width: min(34rem, calc(100% - 1.5rem));
min-height: 11.2rem;
grid-template-rows: auto minmax(5.1rem, auto) auto auto;
width: min(42rem, calc(100% - 1.5rem));
min-height: 10.4rem;
gap: 0.5rem;
border: 1px solid #cfd6df;
border-radius: 1.35rem;
background: #ffffff;
padding: 0.62rem 0.7rem 0.68rem;
padding: 0.62rem 0.7rem 0.44rem;
color: #1f2937;
box-shadow: 0 18px 46px rgba(15, 23, 42, 0.16);
transform: translateX(-50%);
}
.image-canvas-editor__icon-composer .image-canvas-editor__generation-prompt {
min-height: 5.8rem;
min-height: 5.1rem;
padding-top: 0.42rem;
}
.image-canvas-editor__character-composer
.image-canvas-editor__generation-composer-footer,
.image-canvas-editor__icon-composer
.image-canvas-editor__generation-composer-footer {
padding-top: 0.96rem;
}
.image-canvas-editor__icon-spec-card:disabled {
cursor: wait;
opacity: 0.68;
@@ -8491,6 +8515,7 @@ button.image-canvas-editor__reference-chip:disabled {
.image-canvas-editor__quick-edit-panel.image-canvas-editor__generation-composer {
width: min(42rem, calc(100vw - 1.5rem));
gap: 0.18rem 0.72rem;
padding-bottom: 0.48rem;
}
.image-canvas-editor__generation-composer--quick-edit {
@@ -8501,6 +8526,11 @@ button.image-canvas-editor__reference-chip:disabled {
grid-template-columns: auto minmax(0, 1fr) auto auto;
}
.image-canvas-editor__quick-edit-panel
.image-canvas-editor__generation-composer-footer {
padding-top: 1rem;
}
.image-canvas-editor__quick-edit-footer
.image-canvas-editor__option-popover-anchor--model {
grid-column: 3;
@@ -8684,7 +8714,7 @@ button.image-canvas-editor__reference-chip:disabled {
}
.image-canvas-editor__ui-extraction-model {
grid-column: 2;
grid-column: 4;
justify-self: end;
width: min(15.5rem, 42vw);
}
@@ -9305,10 +9335,15 @@ button.image-canvas-editor__reference-chip:disabled {
width: auto;
max-height: min(72vh, 31rem);
overflow: auto;
padding: 0.58rem;
padding: 0.58rem 0.58rem 0.44rem;
transform: none;
}
.image-canvas-editor__character-composer
.image-canvas-editor__generation-composer-footer {
padding-top: 0.86rem;
}
.image-canvas-editor__character-reference-row {
padding-right: 2rem;
}
@@ -780,6 +780,8 @@ describe('editorProjectClient', () => {
prompt: '角色设定',
kind: 'character',
model: 'gpt-image-2',
screenColor: '#FFD6C2',
segModel: 'anime-seg',
aspectRatio: '2:3',
imageSize: '1K',
projectId: 'editor-project-1',
@@ -802,6 +804,8 @@ describe('editorProjectClient', () => {
prompt: '角色设定',
kind: 'character',
model: 'gpt-image-2',
screenColor: '#FFD6C2',
segModel: 'anime-seg',
aspectRatio: '2:3',
imageSize: '1K',
projectId: 'editor-project-1',
@@ -943,6 +947,8 @@ describe('editorProjectClient', () => {
referenceImageSrc: 'data:image/png;base64,spec',
iconDescriptions: ['返回按钮'],
model: 'gpt-image-2',
screenColor: '#E6D8FF',
segModel: 'anime-seg',
aspectRatio: '1:1',
imageSize: '2K',
projectId: 'editor-project-1',
@@ -962,6 +968,8 @@ describe('editorProjectClient', () => {
referenceImageSrc: 'data:image/png;base64,spec',
iconDescriptions: ['返回按钮'],
model: 'gpt-image-2',
screenColor: '#E6D8FF',
segModel: 'anime-seg',
aspectRatio: '1:1',
imageSize: '2K',
projectId: 'editor-project-1',
@@ -994,9 +1002,9 @@ describe('editorProjectClient', () => {
},
],
prompt:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板或绿色阴影。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯色背景 中度天蓝 #7FB3FF / RGB(127,179,255),方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板或阴影。',
actualPrompt:
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕,方便后续扣除背景;素材自身不要出现绿色描边、绿色底板或绿色阴影。',
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用单一纯色背景 中度天蓝 #7FB3FF / RGB(127,179,255),方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板或阴影。',
model: 'gpt-image-2',
provider: 'VectorEngine',
taskId: 'ui-asset-extraction-1',
@@ -1005,6 +1013,8 @@ describe('editorProjectClient', () => {
const result = await extractEditorUiDesignAssets({
sourceImageSrc: 'data:image/png;base64,ui-design',
model: 'gpt-image-2',
screenColor: '#7FB3FF',
segModel: 'anime-seg',
referenceImageSrcs: ['data:image/png;base64,ui-ref'],
projectId: 'editor-project-1',
assetFolderId: 'project',
@@ -1026,6 +1036,8 @@ describe('editorProjectClient', () => {
body: JSON.stringify({
sourceImageSrc: 'data:image/png;base64,ui-design',
model: 'gpt-image-2',
screenColor: '#7FB3FF',
segModel: 'anime-seg',
referenceImageSrcs: ['data:image/png;base64,ui-ref'],
projectId: 'editor-project-1',
generationInputs: {
@@ -162,6 +162,8 @@ export type EditorImageGenerationInput = {
| 'ui-design'
| 'publication-material';
model?: string;
screenColor?: string;
segModel?: string;
aspectRatio?: string;
imageSize?: string;
referenceImageSrcs?: string[];
@@ -192,6 +194,8 @@ export type EditorIconSpritesheetGenerationInput = {
referenceImageSrcs?: string[];
iconDescriptions: string[];
model?: string;
screenColor?: string;
segModel?: string;
aspectRatio?: string;
imageSize?: string;
projectId?: string | null;
@@ -203,6 +207,8 @@ export type EditorIconSpritesheetGenerationInput = {
export type EditorUiDesignAssetExtractionInput = {
sourceImageSrc: string;
model?: string;
screenColor?: string;
segModel?: string;
referenceImageSrcs?: string[];
aspectRatio?: string;
imageSize?: string;
@@ -856,6 +862,8 @@ export async function generateEditorImage(input: EditorImageGenerationInput) {
...(input.size ? { size: input.size } : {}),
...(input.kind ? { kind: input.kind } : {}),
...(input.model ? { model: input.model } : {}),
...(input.screenColor ? { screenColor: input.screenColor } : {}),
...(input.segModel ? { segModel: input.segModel } : {}),
...(input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}),
...(input.imageSize ? { imageSize: input.imageSize } : {}),
...(input.referenceImageSrcs?.length
@@ -895,6 +903,8 @@ export async function generateEditorIconSpritesheet(
: {}),
iconDescriptions: input.iconDescriptions,
model: input.model?.trim() || EDITOR_IMAGE_MODEL_NANOBANANA2,
...(input.screenColor ? { screenColor: input.screenColor } : {}),
...(input.segModel ? { segModel: input.segModel } : {}),
...(input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}),
...(input.imageSize ? { imageSize: input.imageSize } : {}),
...(input.projectId ? { projectId: input.projectId } : {}),
@@ -922,6 +932,8 @@ export async function extractEditorUiDesignAssets(
jsonRequest('POST', {
sourceImageSrc: input.sourceImageSrc,
...(input.model ? { model: input.model } : {}),
...(input.screenColor ? { screenColor: input.screenColor } : {}),
...(input.segModel ? { segModel: input.segModel } : {}),
...(input.referenceImageSrcs?.length
? { referenceImageSrcs: input.referenceImageSrcs }
: {}),