import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary'; import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldAgent'; import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; import type { CustomWorldLibraryEntry } from '../../../packages/shared/src/contracts/runtime'; import { buildPuzzlePublicWorkCode } from '../../services/publicWorkCode'; import type { CustomWorldProfile } from '../../types'; export type CreationWorkShelfKind = 'rpg' | 'big-fish' | 'puzzle'; export type CreationWorkShelfStatus = 'draft' | 'published'; export type CreationWorkShelfBadgeTone = 'warm' | 'success' | 'neutral'; export type CreationWorkShelfBadge = { id: string; label: string; tone: CreationWorkShelfBadgeTone; }; export type CreationWorkShelfMetric = { id: string; label: string; tone?: CreationWorkShelfBadgeTone; }; export type CreationWorkShelfSource = | { kind: 'rpg'; item: CustomWorldWorkSummary; } | { kind: 'big-fish'; item: BigFishWorkSummary; } | { kind: 'puzzle'; item: PuzzleWorkSummary; }; export type CreationWorkShelfItem = { id: string; kind: CreationWorkShelfKind; status: CreationWorkShelfStatus; title: string; subtitle: string; summary: string; updatedAt: string; coverImageSrc: string | null; coverRenderMode: 'image' | 'scene_with_roles'; coverCharacterImageSrcs: string[]; publicWorkCode: string | null; typeLabel: string; openActionLabel: string; canExperience: boolean; canDelete: boolean; badges: CreationWorkShelfBadge[]; metrics: CreationWorkShelfMetric[]; source: CreationWorkShelfSource; }; export function buildCreationWorkShelfItems(params: { rpgItems: CustomWorldWorkSummary[]; rpgLibraryEntries?: CustomWorldLibraryEntry[]; bigFishItems: BigFishWorkSummary[]; puzzleItems: PuzzleWorkSummary[]; canDeleteRpg?: boolean; canDeleteBigFish?: boolean; canDeletePuzzle?: boolean; }) { const { rpgItems, rpgLibraryEntries = [], bigFishItems, puzzleItems, canDeleteRpg = false, canDeleteBigFish = false, canDeletePuzzle = false, } = params; return [ ...rpgItems.map((item) => mapRpgWorkToShelfItem(item, canDeleteRpg, rpgLibraryEntries), ), ...bigFishItems.map((item) => mapBigFishWorkToShelfItem(item, canDeleteBigFish), ), ...puzzleItems.map((item) => mapPuzzleWorkToShelfItem(item, canDeletePuzzle), ), ].sort( (left, right) => getShelfItemTime(right.updatedAt) - getShelfItemTime(left.updatedAt), ); } function mapRpgWorkToShelfItem( item: CustomWorldWorkSummary, canDelete: boolean, libraryEntries: CustomWorldLibraryEntry[], ): CreationWorkShelfItem { const isDraft = item.status === 'draft'; const libraryEntry = item.profileId ? libraryEntries.find((entry) => entry.profileId === item.profileId) : null; const badges: CreationWorkShelfBadge[] = [ buildStatusBadge(item.status), { id: 'type', label: 'RPG', tone: 'neutral' }, ]; if (item.stageLabel) { badges.push({ id: 'stage', label: item.stageLabel, tone: 'neutral' }); } const metrics: CreationWorkShelfMetric[] = [ { id: 'playable-npc-count', label: `${isDraft ? '角色' : '可扮演角色'} ${item.playableNpcCount}`, }, { id: 'landmark-count', label: `地点 ${item.landmarkCount}` }, ]; if (item.roleVisualReadyCount) { metrics.push({ id: 'role-visual-ready-count', label: `主图 ${item.roleVisualReadyCount}`, tone: 'warm', }); } if (item.roleAnimationReadyCount) { metrics.push({ id: 'role-animation-ready-count', label: `动作 ${item.roleAnimationReadyCount}`, tone: 'success', }); } if (item.roleAssetSummaryLabel) { metrics.push({ id: 'role-asset-summary', label: item.roleAssetSummaryLabel, }); } return { id: item.workId, kind: 'rpg', status: item.status, title: item.title, subtitle: item.subtitle, summary: item.summary, updatedAt: item.updatedAt, coverImageSrc: item.coverImageSrc ?? null, coverRenderMode: item.coverRenderMode ?? 'image', coverCharacterImageSrcs: item.coverCharacterImageSrcs ?? [], publicWorkCode: item.status === 'published' ? (libraryEntry?.publicWorkCode ?? null) : null, typeLabel: 'RPG', openActionLabel: isDraft ? item.playableNpcCount > 0 || item.landmarkCount > 0 ? '继续完善' : '继续创作' : '查看详情', canExperience: item.status === 'published' && item.canEnterWorld, canDelete, badges, metrics, source: { kind: 'rpg', item }, }; } function mapBigFishWorkToShelfItem( item: BigFishWorkSummary, canDelete: boolean, ): CreationWorkShelfItem { return { id: item.workId, kind: 'big-fish', status: item.status, title: item.title, subtitle: item.subtitle, summary: item.summary, updatedAt: item.updatedAt, coverImageSrc: item.coverImageSrc ?? null, coverRenderMode: 'image', coverCharacterImageSrcs: [], publicWorkCode: null, typeLabel: '大鱼', openActionLabel: item.status === 'draft' ? '继续创作' : '查看详情', canExperience: item.status === 'published', canDelete, badges: [ buildStatusBadge(item.status), { id: 'type', label: '大鱼', tone: 'neutral' }, ], metrics: [ { id: 'level-count', label: `关卡 ${item.levelCount}` }, { id: 'level-main-image-ready-count', label: `主图 ${item.levelMainImageReadyCount}`, }, { id: 'level-motion-ready-count', label: `动作 ${item.levelMotionReadyCount}`, }, ...(item.backgroundReady ? [ { id: 'background-ready', label: '背景已就绪', tone: 'success' as const, }, ] : []), ], source: { kind: 'big-fish', item }, }; } function mapPuzzleWorkToShelfItem( item: PuzzleWorkSummary, canDelete: boolean, ): CreationWorkShelfItem { const status = item.publicationStatus; return { id: item.workId, kind: 'puzzle', status, title: item.levelName, subtitle: item.authorDisplayName, summary: item.summary, updatedAt: item.updatedAt, coverImageSrc: item.coverImageSrc ?? null, coverRenderMode: 'image', coverCharacterImageSrcs: [], publicWorkCode: status === 'published' ? buildPuzzlePublicWorkCode(item.profileId) : null, typeLabel: '拼图', openActionLabel: status === 'published' && !item.sourceSessionId ? '查看详情' : '继续创作', canExperience: status === 'published', canDelete, badges: [ buildStatusBadge(status), { id: 'type', label: '拼图', tone: 'neutral' }, ...item.themeTags.slice(0, 2).map((tag) => ({ id: `tag:${tag}`, label: tag, tone: 'neutral' as const, })), ], metrics: [ { id: 'author', label: `作者 ${item.authorDisplayName}` }, { id: 'play-count', label: `游玩 ${item.playCount}` }, ], source: { kind: 'puzzle', item }, }; } function buildStatusBadge( status: CreationWorkShelfStatus, ): CreationWorkShelfBadge { return { id: 'status', label: status === 'draft' ? '草稿' : '已发布', tone: status === 'draft' ? 'warm' : 'success', }; } function getShelfItemTime(value: string) { const timestamp = new Date(value).getTime(); return Number.isNaN(timestamp) ? 0 : timestamp; }