diff --git a/src/components/creation-home/creationShowcaseModel.test.ts b/src/components/creation-home/creationShowcaseModel.test.ts index bb6c348f4..56438c42f 100644 --- a/src/components/creation-home/creationShowcaseModel.test.ts +++ b/src/components/creation-home/creationShowcaseModel.test.ts @@ -260,6 +260,62 @@ describe('creationShowcaseModel', () => { expect(items[1]?.cost).toBe('12泥点'); }); + it('prefers user generation inputs over internal assembled prompts', () => { + const items = buildCreationShowcaseItems({ + activeTab: 'all', + projectResources: [ + createResource({ + resourceId: 'character-1', + label: '角色主图', + assetKind: 'character', + prompt: + '【系统内置】完整拼接提示词:用户的角色设定,自动添加视角、构图和抠图背景规则。', + actualPrompt: + '【系统内置】真正发送给模型的完整提示词,包含更多模型约束。', + generationInputs: { + fields: [ + { title: '角色设定', value: '用户的角色设定' }, + { title: '抠图背景色', value: '暖浅桃色 #FFD6C2' }, + { title: 'model', value: 'gpt-image-2' }, + ], + references: [], + }, + }), + ], + }); + + expect(items).toHaveLength(1); + expect(items[0]?.prompt).toBe('用户的角色设定'); + }); + + it('does not expose built-in extraction prompts as showcase descriptions', () => { + const items = buildCreationShowcaseItems({ + activeTab: 'all', + projectResources: [ + createResource({ + resourceId: 'icon-sheet-1', + label: '图标素材图集', + assetKind: 'icon-spritesheet', + prompt: '【系统内置】固定素材提取提示词', + generationInputs: { + fields: [ + { + title: '提取提示词', + value: + '仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。', + }, + { title: 'model', value: 'gpt-image-2' }, + ], + references: [], + }, + }), + ], + }); + + expect(items).toHaveLength(1); + expect(items[0]?.prompt).toBe('-'); + }); + it('keeps unset category visible in all tab and out of concrete category tabs', () => { const items = buildCreationShowcaseItems({ activeTab: 'all', diff --git a/src/components/creation-home/creationShowcaseModel.ts b/src/components/creation-home/creationShowcaseModel.ts index 73947dbec..65257e528 100644 --- a/src/components/creation-home/creationShowcaseModel.ts +++ b/src/components/creation-home/creationShowcaseModel.ts @@ -65,6 +65,38 @@ const SHOWCASE_AUTHOR_KEYS = [ 'authorPublicUserCode', ] as const; +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()), +); + export const SHOWCASE_TABS: Array<{ id: ShowcaseTabId; label: string }> = [ { id: 'all', label: '全部' }, { id: 'characters', label: '角色' }, @@ -89,6 +121,22 @@ function normalizeText(value: unknown) { return typeof value === 'string' && value.trim() ? value.trim() : ''; } +function normalizeGenerationInputTitle(value: string) { + return value.trim().toLowerCase(); +} + +function isBuiltInPromptText(value: string) { + const trimmedValue = value.trim(); + return ( + trimmedValue.startsWith('【系统内置】') || + trimmedValue.startsWith('【宣发素材类型】') || + trimmedValue.startsWith('参考图1的图标素材规范') || + trimmedValue.startsWith('生成玩法UI原型图') || + trimmedValue.includes('仅提取被红色框框选的素材') || + trimmedValue.includes('图集背景必须使用所选单一纯色抠图背景') + ); +} + function normalizeNumber(value: unknown) { if (typeof value === 'number' && Number.isFinite(value)) { return value; @@ -597,18 +645,80 @@ function getGroupsForTab(assets: EditorAssetSnapshot[], tab: ShowcaseTabId) { return []; } -function resolveAssetPrompt(asset: EditorAssetSnapshot) { - const directPrompt = asset.prompt?.trim() || asset.actualPrompt?.trim(); - if (directPrompt) { - return directPrompt; +function resolveUserGenerationPrompt(asset: EditorAssetSnapshot) { + const fields = asset.generationInputs?.fields ?? []; + const promptField = fields.find((field) => { + const value = field.value.trim(); + return ( + value && + USER_PROMPT_INPUT_TITLES.has( + normalizeGenerationInputTitle(field.title), + ) && + !isBuiltInPromptText(value) + ); + }); + const promptValue = promptField?.value.trim(); + if (promptValue) { + return promptValue; } - const generationFields = asset.generationInputs?.fields ?? []; - const firstPromptField = - generationFields.find((field) => - /提示词|描述|设定|prompt/u.test(field.title), - ) ?? generationFields[0]; - return firstPromptField?.value?.trim() || UNKNOWN_META_VALUE; + const structuredLines = fields.flatMap((field) => { + if ( + !STRUCTURED_USER_INPUT_TITLES.has( + normalizeGenerationInputTitle(field.title), + ) + ) { + return []; + } + const value = field.value.trim(); + return value && !isBuiltInPromptText(value) + ? [`${field.title.trim()}:${value}`] + : []; + }); + return structuredLines.join('\n'); +} + +function extractUserPromptFromAssembledPrompt(value: string) { + const trimmedValue = value.trim(); + const sectionMatch = + trimmedValue.match(/【用户输入】\s*([\s\S]*?)(?=\n+【|$)/u) ?? + trimmedValue.match(/【游戏输入】\s*([\s\S]*?)(?=\n+【|$)/u); + const sectionValue = sectionMatch?.[1]?.trim(); + if (sectionValue && !isBuiltInPromptText(sectionValue)) { + return sectionValue; + } + + const roleSettingMatch = trimmedValue.match( + /(?:^|\n)角色设定[::]\s*([^\n]+)/u, + ); + const roleSettingValue = roleSettingMatch?.[1]?.trim(); + return roleSettingValue && !isBuiltInPromptText(roleSettingValue) + ? roleSettingValue + : ''; +} + +function resolveAssetPrompt(asset: EditorAssetSnapshot) { + const userPrompt = resolveUserGenerationPrompt(asset); + if (userPrompt) { + return userPrompt; + } + + for (const value of [asset.prompt, asset.actualPrompt]) { + const directPrompt = value?.trim() ?? ''; + if (!directPrompt) { + continue; + } + + const extractedPrompt = extractUserPromptFromAssembledPrompt(directPrompt); + if (extractedPrompt) { + return extractedPrompt; + } + if (!isBuiltInPromptText(directPrompt)) { + return directPrompt; + } + } + + return UNKNOWN_META_VALUE; } function resolveGroupPrompt(assets: EditorAssetSnapshot[]) {