改造时隐藏系统提示词
改造和重绘面板只从用户可见输入快照恢复提示词。 没有用户输入快照的旧资源打开改造时保持输入框为空。 补充图片、音频、视频、规范和宣发素材改造提示词恢复测试。 更新图片画布编辑器文档中的系统提示词隐藏规则。
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -22,6 +22,7 @@ import {
|
||||
createIconGenerationDialogDraft,
|
||||
createPublicationGenerationDialogDraft,
|
||||
createQuickEditPanelDraft,
|
||||
createRedrawPanelDraft,
|
||||
createSoundEffectGenerationDialogDraft,
|
||||
createSpecDialogDraft,
|
||||
createUiDesignGenerationDialogDraft,
|
||||
@@ -263,7 +264,7 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
title: '游戏音效',
|
||||
mediaType: 'audio',
|
||||
assetKind: 'sound-effect',
|
||||
prompt: '金币收集音',
|
||||
prompt: '【系统内置】完整音效提示词:金币收集音,自动混音,内部参数。',
|
||||
generationInputs: {
|
||||
fields: [
|
||||
{ title: 'prompt', value: '金币收集音' },
|
||||
@@ -315,20 +316,19 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
expect(createAudioRedrawGenerationDialogDraft(createLayer())).toBeNull();
|
||||
});
|
||||
|
||||
it('uses audio prompt templates when remodeling old audio layers without prompts', () => {
|
||||
it('keeps audio remodel prompts blank when no user input snapshot exists', () => {
|
||||
expect(
|
||||
createAudioRedrawGenerationDialogDraft(
|
||||
createLayer({
|
||||
mediaType: 'audio',
|
||||
assetKind: 'sound-effect',
|
||||
prompt: '',
|
||||
prompt: '【系统内置】完整音效提示词',
|
||||
generationInputs: { fields: [], references: [] },
|
||||
}),
|
||||
),
|
||||
).toMatchObject({
|
||||
mode: 'audio-sound-effect',
|
||||
prompt:
|
||||
'短促清脆的游戏交互音效,带轻微闪光质感,干净收尾,适合按钮确认或收集奖励。',
|
||||
prompt: '',
|
||||
});
|
||||
expect(
|
||||
createAudioRedrawGenerationDialogDraft(
|
||||
@@ -336,26 +336,33 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
mediaType: 'audio',
|
||||
assetKind: 'background-music',
|
||||
title: '游戏背景音乐',
|
||||
prompt: '',
|
||||
generationInputs: { fields: [], references: [] },
|
||||
prompt: '【系统内置】完整背景音乐提示词',
|
||||
generationInputs: {
|
||||
fields: [{ title: 'duration', value: '10秒' }],
|
||||
references: [],
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toMatchObject({
|
||||
mode: 'audio-background-music',
|
||||
prompt: '适合轻松冒险游戏的循环背景音乐,温暖明亮,旋律简洁,不包含人声。',
|
||||
prompt: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('creates edit, quick-edit, and character animation panel drafts', () => {
|
||||
const sourceLayer = createLayer({
|
||||
prompt: '原图提示',
|
||||
prompt: '【系统内置】完整拼接提示词:原图提示,自动画质优化。',
|
||||
model: 'gpt-image-2',
|
||||
assetKind: 'character',
|
||||
generationInputs: {
|
||||
fields: [{ title: '生成提示词', value: '原图提示' }],
|
||||
references: [],
|
||||
},
|
||||
});
|
||||
|
||||
expect(createEditDialogDraft(sourceLayer)).toEqual({
|
||||
mode: 'edit',
|
||||
prompt: '原图提示,在保持主体结构的基础上优化画面细节',
|
||||
prompt: '原图提示',
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
sourceLayerId: 'layer-source',
|
||||
@@ -371,6 +378,7 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
quickEditReferences: [],
|
||||
status: 'idle',
|
||||
});
|
||||
expect(createRedrawPanelDraft(sourceLayer).prompt).toBe('原图提示');
|
||||
expect(createCharacterAnimationPanelDraft(sourceLayer)).toEqual({
|
||||
sourceLayerId: 'layer-source',
|
||||
promptText: '',
|
||||
@@ -383,6 +391,86 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
expect(createCharacterAnimationPanelDraft(createLayer())).toBeNull();
|
||||
});
|
||||
|
||||
it('restores remodel prompts from user-visible generation input snapshots only', () => {
|
||||
const cases: Array<{
|
||||
fields: NonNullable<CanvasLayer['generationInputs']>['fields'];
|
||||
expectedPrompt: string;
|
||||
}> = [
|
||||
{
|
||||
fields: [{ title: '视频描述', value: '用户的视频描述' }],
|
||||
expectedPrompt: '用户的视频描述',
|
||||
},
|
||||
{
|
||||
fields: [{ title: '角色设定', value: '用户的角色设定' }],
|
||||
expectedPrompt: '用户的角色设定',
|
||||
},
|
||||
{
|
||||
fields: [{ title: '用户输入', value: '用户的 UI 需求' }],
|
||||
expectedPrompt: '用户的 UI 需求',
|
||||
},
|
||||
{
|
||||
fields: [{ title: '素材描述', value: '返回按钮\n设置按钮' }],
|
||||
expectedPrompt: '返回按钮\n设置按钮',
|
||||
},
|
||||
{
|
||||
fields: [{ title: '自定义规范提示词', value: '用户自定义规范' }],
|
||||
expectedPrompt: '用户自定义规范',
|
||||
},
|
||||
{
|
||||
fields: [
|
||||
{ title: '玩法设定', value: '平台跳跃' },
|
||||
{ title: '美术风格', value: '像素风' },
|
||||
],
|
||||
expectedPrompt: '玩法设定:平台跳跃\n美术风格:像素风',
|
||||
},
|
||||
{
|
||||
fields: [
|
||||
{ title: '游戏名', value: '星海远征' },
|
||||
{ title: '游戏分类', value: '冒险' },
|
||||
{ title: '一句话描述游戏', value: '驾驶飞船探索星云' },
|
||||
],
|
||||
expectedPrompt:
|
||||
'游戏名:星海远征\n游戏分类:冒险\n一句话描述游戏:驾驶飞船探索星云',
|
||||
},
|
||||
{
|
||||
fields: [{ title: '动作描述', value: '待机动作' }],
|
||||
expectedPrompt: '待机动作',
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of cases) {
|
||||
expect(
|
||||
createRedrawPanelDraft(
|
||||
createLayer({
|
||||
prompt: '【系统内置】完整拼接提示词,包含默认规则和模型约束。',
|
||||
generationInputs: {
|
||||
fields: testCase.fields,
|
||||
references: [],
|
||||
},
|
||||
}),
|
||||
).prompt,
|
||||
).toBe(testCase.expectedPrompt);
|
||||
}
|
||||
|
||||
expect(
|
||||
createRedrawPanelDraft(
|
||||
createLayer({
|
||||
prompt: '【系统内置】固定素材提取提示词',
|
||||
generationInputs: {
|
||||
fields: [
|
||||
{
|
||||
title: '提取提示词',
|
||||
value: '仅提取被红色框框选的素材并整理成spritesheet',
|
||||
},
|
||||
{ title: 'model', value: 'gpt-image-2' },
|
||||
],
|
||||
references: [],
|
||||
},
|
||||
}),
|
||||
).prompt,
|
||||
).toBe('');
|
||||
});
|
||||
|
||||
it('normalizes legacy nanobanana model aliases in quick edit drafts', () => {
|
||||
expect(
|
||||
createQuickEditPanelDraft(
|
||||
|
||||
@@ -416,37 +416,62 @@ function findGenerationInputFieldValue(
|
||||
);
|
||||
}
|
||||
|
||||
function resolveDefaultAudioPrompt(
|
||||
mode: Extract<
|
||||
GenerateDialogState['mode'],
|
||||
'audio-sound-effect' | 'audio-background-music'
|
||||
>,
|
||||
) {
|
||||
return mode === 'audio-background-music'
|
||||
? DEFAULT_BACKGROUND_MUSIC_PROMPT
|
||||
: DEFAULT_SOUND_EFFECT_PROMPT;
|
||||
const USER_PROMPT_INPUT_TITLES = new Set(
|
||||
[
|
||||
'生成提示词',
|
||||
'视频描述',
|
||||
'prompt',
|
||||
'sound',
|
||||
'gpt_description_prompt',
|
||||
'音效提示词',
|
||||
'背景音乐提示词',
|
||||
'角色设定',
|
||||
'用户输入',
|
||||
'素材描述',
|
||||
'自定义规范提示词',
|
||||
'修改要求',
|
||||
'快速编辑提示词',
|
||||
'重绘提示词',
|
||||
'动作描述',
|
||||
].map((title) => title.toLowerCase()),
|
||||
);
|
||||
|
||||
const STRUCTURED_USER_INPUT_TITLES = new Set(
|
||||
[
|
||||
'玩法设定',
|
||||
'美术风格',
|
||||
'头身比',
|
||||
'角色视角',
|
||||
'游戏名',
|
||||
'游戏分类',
|
||||
'一句话描述游戏',
|
||||
].map((title) => title.toLowerCase()),
|
||||
);
|
||||
|
||||
function resolveUserGenerationPromptSnapshot(sourceLayer: CanvasLayer) {
|
||||
const fields = sourceLayer.generationInputs?.fields ?? [];
|
||||
const promptField = fields.find((field) =>
|
||||
USER_PROMPT_INPUT_TITLES.has(field.title.trim().toLowerCase()),
|
||||
);
|
||||
const promptValue = promptField?.value.trim();
|
||||
if (promptValue) {
|
||||
return promptValue;
|
||||
}
|
||||
|
||||
const structuredLines = fields.flatMap((field) => {
|
||||
if (!STRUCTURED_USER_INPUT_TITLES.has(field.title.trim().toLowerCase())) {
|
||||
return [];
|
||||
}
|
||||
const value = field.value.trim();
|
||||
return value ? [`${field.title.trim()}:${value}`] : [];
|
||||
});
|
||||
return structuredLines.join('\n');
|
||||
}
|
||||
|
||||
function resolveAudioRedrawPrompt(
|
||||
sourceLayer: CanvasLayer,
|
||||
mode: Extract<
|
||||
GenerateDialogState['mode'],
|
||||
'audio-sound-effect' | 'audio-background-music'
|
||||
>,
|
||||
) {
|
||||
return (
|
||||
sourceLayer.prompt?.trim() ||
|
||||
findGenerationInputFieldValue(sourceLayer, [
|
||||
'prompt',
|
||||
'sound',
|
||||
'gpt_description_prompt',
|
||||
'生成提示词',
|
||||
'音效提示词',
|
||||
'背景音乐提示词',
|
||||
])?.trim() ||
|
||||
sourceLayer.generationInputs?.fields[0]?.value?.trim() ||
|
||||
resolveDefaultAudioPrompt(mode)
|
||||
);
|
||||
return resolveUserGenerationPromptSnapshot(sourceLayer);
|
||||
}
|
||||
|
||||
function resolveAudioRedrawMode(sourceLayer: CanvasLayer) {
|
||||
@@ -493,7 +518,7 @@ export function createAudioRedrawGenerationDialogDraft(
|
||||
const baseDraft = {
|
||||
mode,
|
||||
sourceLayerId: sourceLayer.id,
|
||||
prompt: resolveAudioRedrawPrompt(sourceLayer, mode),
|
||||
prompt: resolveAudioRedrawPrompt(sourceLayer),
|
||||
status: 'idle' as const,
|
||||
composerOpen: true,
|
||||
placeholder: {
|
||||
@@ -562,9 +587,7 @@ export function createEditDialogDraft(
|
||||
): GenerateDialogState {
|
||||
return {
|
||||
mode: 'edit',
|
||||
prompt: sourceLayer.prompt
|
||||
? `${sourceLayer.prompt},在保持主体结构的基础上优化画面细节`
|
||||
: '',
|
||||
prompt: resolveUserGenerationPromptSnapshot(sourceLayer),
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
sourceLayerId: sourceLayer.id,
|
||||
@@ -646,10 +669,7 @@ export function createRedrawPanelDraft(
|
||||
return {
|
||||
...createQuickEditPanelDraft(sourceLayer),
|
||||
mode: 'redraw',
|
||||
prompt:
|
||||
sourceLayer.prompt?.trim() ||
|
||||
sourceLayer.generationInputs?.fields[0]?.value?.trim() ||
|
||||
'',
|
||||
prompt: resolveUserGenerationPromptSnapshot(sourceLayer),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,7 @@ function GenerationWorkflowHarness({
|
||||
? `${activeDialog.mode}:${activeDialog.status}:${activeDialog.composerOpen !== false ? 'open' : 'closed'}:${activeDialog.generatedLayerId ?? '-'}:${activeDialog.placeholder ? 'placeholder' : '-'}`
|
||||
: '-'}
|
||||
</span>
|
||||
<span data-testid="dialog-prompt">{activeDialog?.prompt || '-'}</span>
|
||||
<span data-testid="generation-dialogs">
|
||||
{dialogs.canvasGenerationDialogs
|
||||
.map(
|
||||
@@ -256,7 +257,7 @@ function GenerationWorkflowHarness({
|
||||
height: 120,
|
||||
originalWidth: 420,
|
||||
originalHeight: 120,
|
||||
prompt: '金币跳出音',
|
||||
prompt: '【系统内置】完整音效提示词:金币跳出音,内部混音参数。',
|
||||
generationInputs: {
|
||||
fields: [
|
||||
{ title: 'prompt', value: '金币跳出音' },
|
||||
@@ -296,6 +297,23 @@ function GenerationWorkflowHarness({
|
||||
>
|
||||
打开图片改造
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
workflow.openRedrawPanel(
|
||||
createLayer({
|
||||
id: 'layer-internal-prompt',
|
||||
prompt: '【系统内置】完整拼接提示词,带默认画质和模型约束。',
|
||||
generationInputs: {
|
||||
fields: [{ title: '视频描述', value: '用户的视频描述' }],
|
||||
references: [],
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
>
|
||||
打开用户快照改造
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => workflow.openCropExpandPanel(layers[0]!)}
|
||||
@@ -678,11 +696,22 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
expect(screen.getByTestId('dialog').textContent).toBe(
|
||||
'audio-sound-effect:idle:open:-:placeholder',
|
||||
);
|
||||
expect(screen.getByTestId('dialog-prompt').textContent).toBe('金币跳出音');
|
||||
expect(screen.getByTestId('placeholder').textContent).toBe(
|
||||
'572:140:420:120',
|
||||
);
|
||||
});
|
||||
|
||||
it('opens generated layer remodel with user input snapshot instead of internal prompt', () => {
|
||||
render(<GenerationWorkflowHarness />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开用户快照改造' }));
|
||||
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'layer-internal-prompt:idle:用户的视频描述',
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps the active canvas generator when opening quick edit for another layer', () => {
|
||||
render(<GenerationWorkflowHarness />);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user