feat: unify creation entry templates

This commit is contained in:
2026-06-03 10:24:03 +08:00
parent b0865cfa19
commit 3f742fbaca
25 changed files with 820 additions and 346 deletions

View File

@@ -43,9 +43,7 @@ describe('UnifiedCreationPage', () => {
]);
expect(fields[2]?.getAttribute('data-field-kind')).toBe('audio');
expect(fields[3]?.getAttribute('data-required')).toBe('true');
expect(screen.getByTestId('unified-creation-play-badge').textContent).toBe(
'wooden-fish',
);
expect(screen.queryByTestId('unified-creation-play-badge')).toBeNull();
fireEvent.click(screen.getByRole('button', { name: '返回' }));
expect(onBack).toHaveBeenCalledTimes(1);
expect(screen.queryByLabelText('创作字段')).toBeNull();

View File

@@ -26,7 +26,7 @@ export function UnifiedCreationPage({
data-result-stage={spec.resultStage}
>
<header className="unified-creation-page__header shrink-0 pb-3">
<div className="mb-2 flex items-center justify-between gap-3">
<div className="mb-2 flex items-center gap-3">
{onBack ? (
<button
type="button"
@@ -42,12 +42,6 @@ export function UnifiedCreationPage({
) : (
<span aria-hidden="true" className="min-h-8 w-0 shrink-0" />
)}
<span
className="unified-creation-page__play-badge shrink-0 rounded-full border border-[var(--platform-subpanel-border)] bg-white/80 px-3 py-1 text-[11px] font-black text-[var(--platform-text-soft)]"
data-testid="unified-creation-play-badge"
>
{spec.playId}
</span>
</div>
<div className="flex items-center justify-between gap-3">
<h1 className="m-0 min-w-0 truncate text-[1.35rem] font-black leading-tight tracking-normal text-[var(--platform-text-strong)] sm:text-[1.65rem]">

View File

@@ -1,11 +1,11 @@
import type { CustomWorldGenerationProgress } from '../../../packages/shared/src/contracts/runtime';
import type { CustomWorldStructuredAnchorEntry } from '../../services/customWorldAgentGenerationProgress';
import { CustomWorldGenerationView } from '../CustomWorldGenerationView';
import type { UnifiedCreationPlayId } from './unifiedCreationSpecs';
import { getUnifiedGenerationCopy } from './unifiedGenerationCopy';
import type { UnifiedGenerationPlayId } from './unifiedGenerationCopy';
type UnifiedGenerationPageProps = {
playId: UnifiedCreationPlayId;
playId: UnifiedGenerationPlayId;
settingText: string;
anchorEntries?: CustomWorldStructuredAnchorEntry[];
progress: CustomWorldGenerationProgress | null;

View File

@@ -6,9 +6,21 @@ import {
} from './unifiedCreationSpecs';
describe('unified creation specs', () => {
test('统一壳当前覆盖拼图、抓大鹅、跳一跳和敲木鱼', () => {
test('统一壳覆盖所有已有创作模板工作台', () => {
expect(listUnifiedCreationSpecs().map((spec) => spec.playId).sort()).toEqual(
['jump-hop', 'match3d', 'puzzle', 'wooden-fish'],
[
'baby-object-match',
'bark-battle',
'big-fish',
'creative-agent',
'jump-hop',
'match3d',
'puzzle',
'rpg',
'square-hole',
'visual-novel',
'wooden-fish',
],
);
});
@@ -22,7 +34,12 @@ describe('unified creation specs', () => {
expect([...fieldKinds].sort()).toEqual(['audio', 'image', 'select', 'text']);
});
test('四条链路都映射到统一创作、生成、结果阶段', () => {
test('主要链路都映射到统一创作、生成、结果阶段', () => {
expect(getUnifiedCreationSpec('rpg')).toMatchObject({
workspaceStage: 'agent-workspace',
generationStage: 'custom-world-generating',
resultStage: 'custom-world-result',
});
expect(getUnifiedCreationSpec('puzzle')).toMatchObject({
workspaceStage: 'puzzle-agent-workspace',
generationStage: 'puzzle-generating',
@@ -43,5 +60,20 @@ describe('unified creation specs', () => {
generationStage: 'wooden-fish-generating',
resultStage: 'wooden-fish-result',
});
expect(getUnifiedCreationSpec('bark-battle')).toMatchObject({
workspaceStage: 'bark-battle-workspace',
generationStage: 'bark-battle-generating',
resultStage: 'bark-battle-result',
});
expect(getUnifiedCreationSpec('visual-novel')).toMatchObject({
workspaceStage: 'visual-novel-agent-workspace',
generationStage: 'visual-novel-generating',
resultStage: 'visual-novel-result',
});
expect(getUnifiedCreationSpec('baby-object-match')).toMatchObject({
workspaceStage: 'baby-object-match-workspace',
generationStage: 'baby-object-match-generating',
resultStage: 'baby-object-match-result',
});
});
});

View File

@@ -3,13 +3,58 @@ import type {
UnifiedCreationSpec,
} from '../../services/creationEntryConfigService';
export type UnifiedCreationPlayId = UnifiedCreationSpec['playId'];
export const UNIFIED_CREATION_PLAY_IDS = [
'rpg',
'big-fish',
'puzzle',
'match3d',
'jump-hop',
'wooden-fish',
'square-hole',
'bark-battle',
'visual-novel',
'baby-object-match',
'creative-agent',
] as const;
export type UnifiedCreationPlayId =
(typeof UNIFIED_CREATION_PLAY_IDS)[number];
export type { UnifiedCreationSpec };
const FALLBACK_UNIFIED_CREATION_SPECS: Record<
UnifiedCreationPlayId,
UnifiedCreationSpec
> = {
rpg: {
playId: 'rpg',
title: '想做个什么玩法?',
workspaceStage: 'agent-workspace',
generationStage: 'custom-world-generating',
resultStage: 'custom-world-result',
fields: [
{
id: 'message',
kind: 'text',
label: '创作想法',
required: true,
},
],
},
'big-fish': {
playId: 'big-fish',
title: '想做个什么玩法?',
workspaceStage: 'big-fish-agent-workspace',
generationStage: 'big-fish-generating',
resultStage: 'big-fish-result',
fields: [
{
id: 'message',
kind: 'text',
label: '玩法想法',
required: true,
},
],
},
puzzle: {
playId: 'puzzle',
title: '想做个什么玩法?',
@@ -148,12 +193,135 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record<
},
],
},
'square-hole': {
playId: 'square-hole',
title: '想做个什么玩法?',
workspaceStage: 'square-hole-agent-workspace',
generationStage: 'square-hole-generating',
resultStage: 'square-hole-result',
fields: [
{
id: 'message',
kind: 'text',
label: '玩法想法',
required: true,
},
],
},
'bark-battle': {
playId: 'bark-battle',
title: '想做个什么玩法?',
workspaceStage: 'bark-battle-workspace',
generationStage: 'bark-battle-generating',
resultStage: 'bark-battle-result',
fields: [
{
id: 'title',
kind: 'text',
label: '作品标题',
required: true,
},
{
id: 'themeDescription',
kind: 'text',
label: '主题/场景描述',
required: true,
},
{
id: 'playerImageDescription',
kind: 'text',
label: '玩家形象描述',
required: true,
},
{
id: 'opponentImageDescription',
kind: 'text',
label: '对手形象描述',
required: true,
},
{
id: 'onomatopoeia',
kind: 'text',
label: '拟声词',
required: false,
},
{
id: 'difficultyPreset',
kind: 'select',
label: '难度',
required: true,
},
],
},
'visual-novel': {
playId: 'visual-novel',
title: '想做个什么玩法?',
workspaceStage: 'visual-novel-agent-workspace',
generationStage: 'visual-novel-generating',
resultStage: 'visual-novel-result',
fields: [
{
id: 'ideaText',
kind: 'text',
label: '一句话创作',
required: true,
},
{
id: 'visualStyleId',
kind: 'select',
label: '视觉画风',
required: true,
},
],
},
'baby-object-match': {
playId: 'baby-object-match',
title: '想做个什么玩法?',
workspaceStage: 'baby-object-match-workspace',
generationStage: 'baby-object-match-generating',
resultStage: 'baby-object-match-result',
fields: [
{
id: 'itemAName',
kind: 'text',
label: '物品 A',
required: true,
},
{
id: 'itemBName',
kind: 'text',
label: '物品 B',
required: true,
},
],
},
'creative-agent': {
playId: 'creative-agent',
title: '想做个什么玩法?',
workspaceStage: 'creative-agent-workspace',
generationStage: 'puzzle-generating',
resultStage: 'puzzle-result',
fields: [
{
id: 'message',
kind: 'text',
label: '创作想法',
required: true,
},
{
id: 'referenceImage',
kind: 'image',
label: '参考图',
required: false,
},
],
},
};
export function getUnifiedCreationSpec(
playId: UnifiedCreationPlayId,
configType?: CreationEntryTypeConfig | null,
) {
): UnifiedCreationSpec {
return (
configType?.unifiedCreationSpec ?? FALLBACK_UNIFIED_CREATION_SPECS[playId]
);

View File

@@ -1,5 +1,10 @@
import type { UnifiedCreationPlayId } from './unifiedCreationSpecs';
export type UnifiedGenerationPlayId = Extract<
UnifiedCreationPlayId,
'puzzle' | 'match3d' | 'jump-hop' | 'wooden-fish'
>;
const UNIFIED_GENERATION_COPY = {
puzzle: {
retryLabel: '重新生成图片',
@@ -26,7 +31,7 @@ const UNIFIED_GENERATION_COPY = {
activeBadgeLabel: '素材生成中',
},
} as const satisfies Record<
UnifiedCreationPlayId,
UnifiedGenerationPlayId,
{
retryLabel: string;
settingTitle: string;
@@ -35,6 +40,6 @@ const UNIFIED_GENERATION_COPY = {
}
>;
export function getUnifiedGenerationCopy(playId: UnifiedCreationPlayId) {
export function getUnifiedGenerationCopy(playId: UnifiedGenerationPlayId) {
return UNIFIED_GENERATION_COPY[playId];
}