Refine creation tab UX, generation flow, and bindings

Large changes across frontend, backend and docs to align creation-tab and generation-page behavior with new product UI/UX and Spacetime bindings. Updated hermes decision-log and pitfalls with concrete rules (banner carousel, font sizing, unread-dot tokens, template-card layout, direct card->entry routing, separation of account balance vs prize pools, removal of global page card shell, generation progress milestones and unified circular progress, and background video handling). Added GenerationProgressHero component and media assets, plus generation-related UI/tests updates (CustomWorldGenerationView, BarkBattleGeneratingView, creation hub/cards, platform entry routing, index tests). Backend and contract updates include new category fields in admin API types and admin UI form/list, spacetime-client/module/migration changes and generated bindings script. Misc: many tests adjusted, new docs and plan files added, and several server-rs crate changes to support the updated creation/ generation workflows.
This commit is contained in:
2026-05-25 00:41:30 +08:00
parent 2ba4691bc0
commit 50a0d6f982
75 changed files with 5533 additions and 1101 deletions

View File

@@ -18,6 +18,14 @@ const testEntryConfig = {
title: '选择创作类型',
description: '先选玩法类型,再进入对应创作工作台。',
},
eventBanner: {
title: '泥点挑战',
description: '创作活动测试横幅。',
coverImageSrc: '/creation-type-references/puzzle.webp',
prizePoolMudPoints: 1000,
startsAtText: '2026-05-01',
endsAtText: '2026-05-31',
},
creationTypes: [
{
id: 'rpg',
@@ -28,6 +36,9 @@ const testEntryConfig = {
visible: true,
open: true,
sortOrder: 10,
categoryId: 'recent',
categoryLabel: '最近创作',
categorySortOrder: 10,
updatedAtMicros: 1,
},
{
@@ -39,17 +50,23 @@ const testEntryConfig = {
visible: true,
open: true,
sortOrder: 30,
categoryId: 'recent',
categoryLabel: '最近创作',
categorySortOrder: 10,
updatedAtMicros: 1,
},
{
id: 'match3d',
title: '抓大鹅',
subtitle: '3D 消除关卡',
badge: '可创',
badge: '可创',
imageSrc: '/creation-type-references/match3d.webp',
visible: true,
open: true,
sortOrder: 40,
categoryId: 'recent',
categoryLabel: '最近创作',
categorySortOrder: 10,
updatedAtMicros: 1,
},
{
@@ -61,6 +78,9 @@ const testEntryConfig = {
visible: false,
open: true,
sortOrder: 50,
categoryId: 'recent',
categoryLabel: '最近创作',
categorySortOrder: 10,
updatedAtMicros: 1,
},
{
@@ -72,6 +92,9 @@ const testEntryConfig = {
visible: false,
open: false,
sortOrder: 60,
categoryId: 'recent',
categoryLabel: '最近创作',
categorySortOrder: 10,
updatedAtMicros: 1,
},
{
@@ -83,6 +106,9 @@ const testEntryConfig = {
visible: true,
open: false,
sortOrder: 70,
categoryId: 'recent',
categoryLabel: '最近创作',
categorySortOrder: 10,
updatedAtMicros: 1,
},
],
@@ -140,6 +166,96 @@ test('creation hub draft card renders compiled work summary fields', () => {
expect(html).not.toContain('大鱼吃小鱼');
});
test('creation start card renders reference-aligned banner and template metadata', () => {
const html = renderToStaticMarkup(
<CustomWorldCreationHub
items={[]}
loading={false}
error={null}
onRetry={() => {}}
onCreateType={noopCreateType}
onOpenDraft={() => {}}
onEnterPublished={() => {}}
entryConfig={testEntryConfig}
creationTypes={testCreationTypes}
mode="start-only"
/>,
);
expect(html).toContain('creation-event-banner');
expect(html).toContain('creation-event-banner__track');
expect(html).toContain('creation-event-banner__slide');
expect(html).toContain('creation-event-banner__timebar');
expect(html).toContain('拼图主题创作赛');
expect(html).toContain('抓大鹅主题创作赛');
expect(html).toContain('1,000');
expect(html).toContain('泥点数');
expect(html).not.toContain('泥点挑战');
expect(html).toMatch(
/creation-event-banner__timebar[\s\S]*creation-event-banner__pager[\s\S]*creation-template-card/u,
);
expect(html).toContain('creation-template-card__body');
expect(html).toContain('creation-template-card__cost-badge');
expect(html).toContain('拼图关卡创作');
expect(html).toContain('10-20泥点数');
expect(html).toContain('即将开放');
expect(html).not.toContain('可创建');
expect(html).not.toContain('可创作');
expect(html).not.toContain('creation-event-banner__counter');
expect(html).not.toContain('预计消耗 10-20 泥点');
expect(html).not.toContain('platform-creation-reference-card');
});
test('creation start card keeps typography in compact UI scale', () => {
const html = renderToStaticMarkup(
<CustomWorldCreationHub
items={[]}
loading={false}
error={null}
onRetry={() => {}}
onCreateType={noopCreateType}
onOpenDraft={() => {}}
onEnterPublished={() => {}}
entryConfig={testEntryConfig}
creationTypes={testCreationTypes}
mode="start-only"
/>,
);
expect(html).toMatch(/creation-template-card__title[^"]*\btext-sm\b/u);
expect(html).toMatch(/creation-template-card__subtitle[^"]*\btext-xs\b/u);
expect(html).toMatch(
/creation-template-card__cost-badge[^"]*\btext-\[11px\](?:\s|")/u,
);
expect(html).not.toMatch(
/\b(text-lg|text-xl|sm:text-base|sm:text-lg|sm:text-xl|text-\[1\.08rem\])\b/u,
);
});
test('creation start card removes the outer template list frame and tightens card grid', () => {
const html = renderToStaticMarkup(
<CustomWorldCreationHub
items={[]}
loading={false}
error={null}
onRetry={() => {}}
onCreateType={noopCreateType}
onOpenDraft={() => {}}
onEnterPublished={() => {}}
entryConfig={testEntryConfig}
creationTypes={testCreationTypes}
mode="start-only"
/>,
);
expect(html).toContain('creation-template-list');
expect(html).toMatch(/creation-template-list__grid[^"]*\bgap-2\b/u);
expect(html).toMatch(/creation-template-card[^"]*\bmin-h-\[12\.5rem\]/u);
expect(html).not.toMatch(
/creation-template-list[^"]*\bborder\b[^"]*\bborder-\[#f0dfd6\]/u,
);
});
test('creation hub renders puzzle works in the same unified list with puzzle tag', () => {
const html = renderToStaticMarkup(
<CustomWorldCreationHub
@@ -514,3 +630,64 @@ test('creation hub published card keeps publish info without fixed action text',
expect(html).not.toContain('creation-work-card__action');
expect(html).not.toContain('>查看详情<');
});
test('creation hub root keeps the remap theme hook without the page card shell', () => {
const html = renderToStaticMarkup(
<CustomWorldCreationHub
items={[]}
loading={false}
error={null}
onRetry={() => {}}
onCreateType={noopCreateType}
onOpenDraft={() => {}}
onEnterPublished={() => {}}
entryConfig={testEntryConfig}
creationTypes={testCreationTypes}
mode="works-only"
/>,
);
expect(html).toContain('platform-remap-surface');
expect(html).not.toContain('platform-page-stage');
});
test('creation hub draft tabs use discover-style channel labels', () => {
const html = renderToStaticMarkup(
<CustomWorldCreationHub
mode="works-only"
items={[]}
loading={false}
error={null}
onRetry={() => {}}
onCreateType={noopCreateType}
onOpenDraft={() => {}}
onEnterPublished={() => {}}
entryConfig={testEntryConfig}
creationTypes={testCreationTypes}
puzzleItems={[
{
workId: 'puzzle:works-tab',
profileId: 'puzzle-profile-works-tab',
ownerUserId: 'user-1',
authorDisplayName: '测试作者',
levelName: '测试草稿',
summary: '测试草稿',
themeTags: [],
coverImageSrc: null,
publicationStatus: 'draft',
updatedAt: '2026-05-07T00:00:00.000Z',
publishedAt: null,
playCount: 0,
remixCount: 0,
likeCount: 0,
publishReady: false,
},
]}
onOpenPuzzleDetail={() => {}}
/>,
);
expect(html).toContain('platform-mobile-home-channel');
expect(html).toContain('platform-mobile-home-channel--active');
expect(html).not.toContain('platform-tab--active');
});