import { ArrowRight, Loader2, Sparkles } from 'lucide-react'; import { AnimatePresence, motion } from 'motion/react'; import { type Dispatch, lazy, type SetStateAction, Suspense, useCallback, useEffect, useMemo, useRef, useState, } from 'react'; import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth'; import type { BigFishRuntimeSnapshotResponse, BigFishSessionSnapshotResponse, ExecuteBigFishActionRequest, SendBigFishMessageRequest, SubmitBigFishInputRequest, } from '../../../packages/shared/src/contracts/bigFish'; import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary'; import type { CreativeAgentSessionSnapshot, CreativeAgentSseEvent, CreativeDraftEditResult, StreamCreativeAgentMessageRequest, } from '../../../packages/shared/src/contracts/creativeAgent'; import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldWorkSummary'; import type { CreateMatch3DSessionRequest, ExecuteMatch3DActionRequest, Match3DActionResponse, Match3DAgentSessionSnapshot, Match3DSessionResponse, SendMatch3DMessageRequest, } from '../../../packages/shared/src/contracts/match3dAgent'; import type { Match3DRunSnapshot } from '../../../packages/shared/src/contracts/match3dRuntime'; import type { Match3DWorkProfile, Match3DWorkSummary, } from '../../../packages/shared/src/contracts/match3dWorks'; import type { PuzzleAgentActionRequest, PuzzleAgentOperationRecord, } from '../../../packages/shared/src/contracts/puzzleAgentActions'; import type { PuzzleDraftLevel, PuzzleResultDraft, } from '../../../packages/shared/src/contracts/puzzleAgentDraft'; import type { CreatePuzzleAgentSessionRequest, PuzzleAgentSessionSnapshot, SendPuzzleAgentMessageRequest, } from '../../../packages/shared/src/contracts/puzzleAgentSession'; import type { PuzzleCreativeTemplateSelection } from '../../../packages/shared/src/contracts/puzzleCreativeTemplate'; import type { PuzzleRunSnapshot, PuzzleRuntimePropKind, SubmitPuzzleLeaderboardRequest, } from '../../../packages/shared/src/contracts/puzzleRuntimeSession'; import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; import type { CustomWorldGalleryCard, CustomWorldLibraryEntry, ProfilePlayedWorkSummary, ProfilePlayStatsResponse, ProfileSaveArchiveResumeResponse, ProfileSaveArchiveSummary, } from '../../../packages/shared/src/contracts/runtime'; import type { CreateSquareHoleSessionRequest, ExecuteSquareHoleActionRequest, SendSquareHoleMessageRequest, SquareHoleActionResponse, SquareHoleSessionResponse, SquareHoleSessionSnapshot, } from '../../../packages/shared/src/contracts/squareHoleAgent'; import type { SquareHoleRunSnapshot } from '../../../packages/shared/src/contracts/squareHoleRuntime'; import type { SquareHoleWorkProfile, SquareHoleWorkSummary, } from '../../../packages/shared/src/contracts/squareHoleWorks'; import type { CreateVisualNovelSessionRequest, ExecuteVisualNovelAgentActionRequest, SendVisualNovelMessageRequest, VisualNovelAgentSessionSnapshot, VisualNovelResultDraft, VisualNovelRunSnapshot, VisualNovelRuntimeActionRequest, VisualNovelSessionResponse, VisualNovelWorkDetail, VisualNovelWorkSummary, } from '../../../packages/shared/src/contracts/visualNovel'; import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets'; import { buildPublicWorkStagePath, pushAppHistoryPath, } from '../../routing/appPageRoutes'; import { resolveRuntimeNotFoundRecoveryAction } from '../../routing/runtimeNotFoundRecovery'; import { ApiClientError, BACKGROUND_AUTH_REQUEST_OPTIONS, } from '../../services/apiClient'; import { getPublicAuthUserByCode, getPublicAuthUserById, } from '../../services/authService'; import { createBigFishCreationSession, executeBigFishCreationAction, getBigFishCreationSession, streamBigFishCreationMessage, } from '../../services/big-fish-creation'; import { likeBigFishGalleryWork, listBigFishGallery, remixBigFishGalleryWork, } from '../../services/big-fish-gallery'; import { recordBigFishPlay, startBigFishRun as startBigFishRuntimeRun, submitBigFishInput as submitBigFishRuntimeInput, } from '../../services/big-fish-runtime'; import { deleteBigFishWork, listBigFishWorks, } from '../../services/big-fish-works'; import { type CreationEntryConfig, fetchCreationEntryConfig, } from '../../services/creationEntryConfigService'; import { cancelCreativeAgentSession, confirmCreativePuzzleTemplate, createCreativeAgentSession, streamCreativeAgentMessage, streamCreativeDraftEdit, } from '../../services/creative-agent'; import { readCustomWorldAgentUiState, shouldRestoreCustomWorldAgentUiState, } from '../../services/customWorldAgentUiState'; import { match3dCreationClient } from '../../services/match3d-creation'; import { createServerMatch3DRuntimeAdapter } from '../../services/match3d-runtime'; import { deleteMatch3DWork, getMatch3DWorkDetail, listMatch3DGallery, listMatch3DWorks, } from '../../services/match3d-works'; import { buildBigFishGenerationAnchorEntries, buildMatch3DGenerationAnchorEntries, buildMiniGameDraftGenerationProgress, buildPuzzleGenerationAnchorEntries, buildSquareHoleGenerationAnchorEntries, createMiniGameDraftGenerationState, type MiniGameDraftGenerationState, } from '../../services/miniGameDraftGenerationProgress'; import { getPlatformProfileDashboard } from '../../services/platform-entry/platformProfileClient'; import { buildBigFishPublicWorkCode, buildMatch3DPublicWorkCode, buildPuzzlePublicWorkCode, buildSquareHolePublicWorkCode, buildVisualNovelPublicWorkCode, isSameBigFishPublicWorkCode, isSameMatch3DPublicWorkCode, isSamePuzzlePublicWorkCode, isSameSquareHolePublicWorkCode, isSameVisualNovelPublicWorkCode, } from '../../services/publicWorkCode'; import { createPuzzleAgentSession, executePuzzleAgentAction, getPuzzleAgentSession, streamPuzzleAgentMessage, } from '../../services/puzzle-agent'; import { getPuzzleGalleryDetail, likePuzzleGalleryWork, listPuzzleGallery, remixPuzzleGalleryWork, } from '../../services/puzzle-gallery'; import { generatePuzzleOnboardingWork, savePuzzleOnboardingWork, } from '../../services/puzzle-onboarding'; import { advancePuzzleNextLevel, startPuzzleRun, submitPuzzleLeaderboard, } from '../../services/puzzle-runtime'; import { advanceLocalPuzzleLevel, applyLocalPuzzleFreezeTime, dragLocalPuzzlePiece, extendLocalPuzzleTime, isLocalPuzzleRun, refreshLocalPuzzleTimer, resolvePuzzleRestartLevelId, restartLocalPuzzleLevel, setLocalPuzzlePaused, startLocalPuzzleRun, submitLocalPuzzleLeaderboard, swapLocalPuzzlePieces, } from '../../services/puzzle-runtime/puzzleLocalRuntime'; import { claimPuzzleWorkPointIncentive, deletePuzzleWork, listPuzzleWorks, updatePuzzleWork, } from '../../services/puzzle-works'; import { deleteRpgCreationAgentSession } from '../../services/rpg-creation'; import { rpgCreationPreviewAdapter } from '../../services/rpg-creation/rpgCreationPreviewAdapter'; import { deleteRpgEntryWorldProfile, getRpgEntryWorldGalleryDetailByCode, likeRpgEntryWorldGallery, recordRpgEntryWorldGalleryPlay, remixRpgEntryWorldGallery, } from '../../services/rpg-entry/rpgEntryLibraryClient'; import { getRpgProfilePlayStats, submitRpgProfileFeedback, } from '../../services/rpg-entry/rpgProfileClient'; import { requestRpgRuntimeJson } from '../../services/rpg-runtime/rpgRuntimeRequest'; import { squareHoleCreationClient } from '../../services/square-hole-creation'; import { dropSquareHoleShape, finishSquareHoleTimeUp, restartSquareHoleRun, startSquareHoleRun, stopSquareHoleRun, } from '../../services/square-hole-runtime'; import { deleteSquareHoleWork, getSquareHoleWorkDetail, listSquareHoleGallery, listSquareHoleWorks, } from '../../services/square-hole-works'; import { compileVisualNovelWorkProfile, createVisualNovelSession, executeVisualNovelAction, getVisualNovelSession, streamVisualNovelMessage, } from '../../services/visual-novel-creation'; import { listVisualNovelGallery, startVisualNovelRun, streamVisualNovelRuntimeAction, } from '../../services/visual-novel-runtime'; import { deleteVisualNovelWork, getVisualNovelWorkDetail, listVisualNovelWorks, publishVisualNovelWork, updateVisualNovelWork, } from '../../services/visual-novel-works'; import type { CustomWorldProfile } from '../../types'; import { useAuthUi } from '../auth/AuthUiContext'; import { PublishShareModal } from '../common/PublishShareModal'; import type { PublishShareModalPayload } from '../common/publishShareModalModel'; import { UnifiedModal } from '../common/UnifiedModal'; import { resolveCreativeAgentTargetSelectionStage } from '../creative-agent/creativeAgentViewModel'; import { isBigFishGalleryEntry, isMatch3DGalleryEntry, isPuzzleGalleryEntry, isSquareHoleGalleryEntry, isVisualNovelGalleryEntry, mapBigFishWorkToPlatformGalleryCard, mapMatch3DWorkToPlatformGalleryCard, mapPuzzleWorkToPlatformGalleryCard, mapSquareHoleWorkToPlatformGalleryCard, mapVisualNovelWorkToPlatformGalleryCard, type PlatformPublicGalleryCard, } from '../rpg-entry/rpgEntryWorldPresentation'; import { useRpgCreationAgentOperationPolling } from '../rpg-entry/useRpgCreationAgentOperationPolling'; import { useRpgCreationEnterWorld } from '../rpg-entry/useRpgCreationEnterWorld'; import { useRpgCreationResultAutosave } from '../rpg-entry/useRpgCreationResultAutosave'; import { useRpgCreationSessionController } from '../rpg-entry/useRpgCreationSessionController'; import { buildVisualNovelEntryGenerationAnchorEntries, buildVisualNovelEntryGenerationProgress, type VisualNovelEntryFormPayload, } from '../visual-novel-creation/VisualNovelAgentWorkspace'; import { createMockVisualNovelRunFromDraft } from '../visual-novel-runtime/visualNovelMockData'; import { canExposePublicWork, EDUTAINMENT_HIDDEN_MESSAGE, filterGeneralPublicWorks, } from './platformEdutainmentVisibility'; import { PlatformEntryCreationTypeModal } from './PlatformEntryCreationTypeModal'; import type { PlatformCreationTypeId } from './platformEntryCreationTypes'; import { derivePlatformCreationTypes, getVisiblePlatformCreationTypes, isPlatformCreationTypeVisible, } from './platformEntryCreationTypes'; import { PlatformEntryHomeView, type PlatformHomeTab, } from './PlatformEntryHomeView'; import { buildCreationHubFallbackItems, resolveRpgCreationErrorMessage, } from './platformEntryShared'; import type { PlatformEntryFlowShellProps } from './platformEntryTypes'; import { PlatformEntryWorldDetailView } from './PlatformEntryWorldDetailView'; import { PlatformFeedbackView } from './PlatformFeedbackView'; import { PlatformWorkDetailView } from './PlatformWorkDetailView'; import { usePlatformCreationAgentFlowController } from './usePlatformCreationAgentFlowController'; import { usePlatformEntryBootstrap } from './usePlatformEntryBootstrap'; import { usePlatformEntryLibraryDetail } from './usePlatformEntryLibraryDetail'; import { usePlatformEntryNavigation } from './usePlatformEntryNavigation'; type AgentResultPublishGateView = { blockers: string[]; publishReady: boolean; }; type PuzzleDetailReturnTarget = { tab: PlatformHomeTab; }; type PuzzleRuntimeReturnStage = | 'puzzle-result' | 'puzzle-gallery-detail' | 'work-detail' | 'platform'; type PuzzleRuntimeAuthMode = 'default' | 'isolated'; type PuzzleOnboardingPhase = 'input' | 'generating' | 'generated'; type PuzzleOnboardingDraft = { promptText: string; item: PuzzleWorkSummary; }; type BigFishRuntimeReturnStage = 'big-fish-result' | 'work-detail' | 'platform'; type BigFishRuntimeSessionSource = 'draft' | 'work' | null; type RecommendRuntimeKind = | 'big-fish' | 'match3d' | 'puzzle' | 'square-hole' | 'visual-novel' | 'rpg'; type SquareHoleRuntimeReturnStage = | 'square-hole-result' | 'work-detail' | 'platform'; type VisualNovelRuntimeReturnStage = | 'visual-novel-result' | 'visual-novel-gallery-detail' | 'work-detail' | 'platform'; type VisualNovelEntryGenerationPhase = 'generating' | 'ready' | 'failed'; type PuzzleSaveArchiveState = { runtimeKind?: unknown; entryProfileId?: unknown; currentProfileId?: unknown; currentLevelId?: unknown; }; type DeleteCreationWorkConfirmation = { id: string; title: string; detail: string; run: () => void; }; async function resumePuzzleProfileSaveArchiveRaw(worldKey: string) { return requestRpgRuntimeJson< ProfileSaveArchiveResumeResponse >( `/profile/save-archives/${encodeURIComponent(worldKey)}`, { method: 'POST' }, '恢复拼图存档失败', ); } type AgentResultBlockerView = { code?: string; message: string; }; const AGENT_RESULT_STRUCTURAL_BLOCKER_CODES = new Set([ 'publish_missing_world_hook', 'publish_missing_player_premise', 'publish_missing_core_conflict', 'publish_missing_main_chapter', 'publish_missing_first_act', ]); const RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS = BACKGROUND_AUTH_REQUEST_OPTIONS; const PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS = RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS; function getPlatformPublicGalleryEntryTime(entry: PlatformPublicGalleryCard) { const rawTime = entry.publishedAt ?? entry.updatedAt; const timestamp = new Date(rawTime).getTime(); return Number.isNaN(timestamp) ? 0 : timestamp; } function getPlatformPublicGalleryEntryKey(entry: PlatformPublicGalleryCard) { const kind = isBigFishGalleryEntry(entry) ? 'big-fish' : isPuzzleGalleryEntry(entry) ? 'puzzle' : isMatch3DGalleryEntry(entry) ? 'match3d' : isSquareHoleGalleryEntry(entry) ? 'square-hole' : isVisualNovelGalleryEntry(entry) ? 'visual-novel' : 'rpg'; return `${kind}:${entry.ownerUserId}:${entry.profileId}`; } function getPlatformRecommendRuntimeKind( entry: PlatformPublicGalleryCard, ): RecommendRuntimeKind { if (isBigFishGalleryEntry(entry)) { return 'big-fish'; } if (isPuzzleGalleryEntry(entry)) { return 'puzzle'; } if (isMatch3DGalleryEntry(entry)) { return 'match3d'; } if (isSquareHoleGalleryEntry(entry)) { return 'square-hole'; } if (isVisualNovelGalleryEntry(entry)) { return 'visual-novel'; } return 'rpg'; } function isSamePlatformPublicGalleryEntry( left: PlatformPublicGalleryCard, right: PlatformPublicGalleryCard, ) { return ( getPlatformPublicGalleryEntryKey(left) === getPlatformPublicGalleryEntryKey(right) ); } function mergePlatformPublicGalleryEntries( rpgEntries: CustomWorldGalleryCard[], puzzleEntries: PlatformPublicGalleryCard[], ) { const entryMap = new Map(); [...rpgEntries, ...puzzleEntries].forEach((entry) => { entryMap.set(getPlatformPublicGalleryEntryKey(entry), entry); }); return Array.from(entryMap.values()).sort( (left, right) => getPlatformPublicGalleryEntryTime(right) - getPlatformPublicGalleryEntryTime(left), ); } function mapRpgGalleryCardToPublicWorkDetail( entry: CustomWorldGalleryCard, ): PlatformPublicGalleryCard { return entry; } function mapPuzzleWorkToPublicWorkDetail( item: PuzzleWorkSummary, ): PlatformPublicGalleryCard { return mapPuzzleWorkToPlatformGalleryCard(item); } function resolveVisiblePuzzleDetailCoverCount( entry: PlatformPublicGalleryCard | null, run: PuzzleRunSnapshot | null, ) { if (!entry || !isPuzzleGalleryEntry(entry)) { return 1; } if (run?.entryProfileId !== entry.profileId) { return 1; } // 中文注释:封面首图永远公开,后续封面跟随当前玩家本次 run 的通关进度即时解锁。 return Math.max(1, run.clearedLevelCount + 1); } function mapMatch3DWorkToPublicWorkDetail( item: Match3DWorkSummary, ): PlatformPublicGalleryCard { return mapMatch3DWorkToPlatformGalleryCard(item); } function mapSquareHoleWorkToPublicWorkDetail( item: SquareHoleWorkSummary, ): PlatformPublicGalleryCard { return mapSquareHoleWorkToPlatformGalleryCard(item); } function mapBigFishWorkToPublicWorkDetail( item: BigFishWorkSummary, ): PlatformPublicGalleryCard { return mapBigFishWorkToPlatformGalleryCard(item); } function mapVisualNovelWorkToPublicWorkDetail( item: VisualNovelWorkSummary, ): PlatformPublicGalleryCard { return mapVisualNovelWorkToPlatformGalleryCard(item); } function mapVisualNovelWorkDetailToSession( work: VisualNovelWorkDetail, ): VisualNovelAgentSessionSnapshot { return { sessionId: work.sourceSessionId?.trim() || work.workId, ownerUserId: work.summary.ownerUserId, sourceMode: work.draft.sourceMode, status: 'ready', messages: [], draft: work.draft, pendingAction: null, createdAt: work.createdAt, updatedAt: work.summary.updatedAt, }; } function mapPublicWorkDetailToMatch3DWork( entry: PlatformPublicGalleryCard, ): Match3DWorkSummary | null { if (!isMatch3DGalleryEntry(entry)) { return null; } return { workId: entry.workId, profileId: entry.profileId, ownerUserId: entry.ownerUserId, sourceSessionId: 'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string' ? entry.sourceSessionId : null, gameName: entry.worldName, themeText: entry.themeTags[0] ?? '经典消除', summary: entry.summaryText, tags: entry.themeTags, coverImageSrc: entry.coverImageSrc, referenceImageSrc: null, clearCount: 12, difficulty: 4, publicationStatus: 'published', playCount: entry.playCount ?? 0, updatedAt: entry.updatedAt, publishedAt: entry.publishedAt, publishReady: true, }; } function buildMatch3DProfileFromSession( session: Match3DAgentSessionSnapshot | null, ): Match3DWorkProfile | null { const draft = session?.draft; if (!session || !draft?.profileId) { return null; } const now = session.updatedAt || new Date().toISOString(); return { workId: draft.profileId, profileId: draft.profileId, ownerUserId: 'current-user', sourceSessionId: session.sessionId, gameName: draft.gameName, themeText: draft.themeText, summary: draft.summary ?? draft.summaryText ?? '', tags: draft.tags, coverImageSrc: draft.coverImageSrc ?? draft.referenceImageSrc ?? null, referenceImageSrc: draft.referenceImageSrc ?? null, clearCount: draft.clearCount, difficulty: draft.difficulty, publicationStatus: 'draft', playCount: 0, updatedAt: now, publishedAt: null, publishReady: Boolean(draft.publishReady), generatedItemAssets: draft.generatedItemAssets, }; } function mapPublicWorkDetailToPuzzleWork( entry: PlatformPublicGalleryCard, ): PuzzleWorkSummary | null { if (!isPuzzleGalleryEntry(entry)) { return null; } return { workId: entry.workId, profileId: entry.profileId, ownerUserId: entry.ownerUserId, sourceSessionId: 'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string' ? entry.sourceSessionId : null, authorDisplayName: entry.authorDisplayName, levelName: entry.worldName, summary: entry.summaryText, themeTags: entry.themeTags, coverImageSrc: entry.coverImageSrc, publicationStatus: 'published', updatedAt: entry.updatedAt, publishedAt: entry.publishedAt, playCount: entry.playCount ?? 0, remixCount: entry.remixCount ?? 0, likeCount: entry.likeCount ?? 0, pointIncentiveTotalHalfPoints: 0, pointIncentiveClaimedPoints: 0, pointIncentiveTotalPoints: 0, pointIncentiveClaimablePoints: 0, publishReady: true, levels: entry.coverSlides?.map((slide, index) => ({ levelId: slide.id || `puzzle-level-${index + 1}`, levelName: slide.label, pictureDescription: entry.summaryText, candidates: [], selectedCandidateId: null, coverImageSrc: slide.imageSrc, coverAssetId: null, generationStatus: 'ready' as const, })) ?? [], }; } function mapPublicWorkDetailToBigFishWork( entry: PlatformPublicGalleryCard, ): BigFishWorkSummary | null { if (!isBigFishGalleryEntry(entry)) { return null; } const levelCount = Number.parseInt( entry.themeTags.find((tag) => /^\d+级$/u.test(tag))?.replace('级', '') ?? '0', 10, ); return { workId: entry.workId, sourceSessionId: entry.profileId, ownerUserId: entry.ownerUserId, authorDisplayName: entry.authorDisplayName, title: entry.worldName, subtitle: entry.subtitle, summary: entry.summaryText, coverImageSrc: entry.coverImageSrc, status: 'published', updatedAt: entry.updatedAt, publishedAt: entry.publishedAt, publishReady: true, levelCount: Number.isNaN(levelCount) ? 0 : levelCount, levelMainImageReadyCount: 0, levelMotionReadyCount: 0, backgroundReady: Boolean(entry.coverImageSrc), playCount: entry.playCount ?? 0, remixCount: entry.remixCount ?? 0, likeCount: entry.likeCount ?? 0, }; } function mapPublicWorkDetailToSquareHoleWork( entry: PlatformPublicGalleryCard, ): SquareHoleWorkSummary | null { if (!isSquareHoleGalleryEntry(entry)) { return null; } return { workId: entry.workId, profileId: entry.profileId, ownerUserId: entry.ownerUserId, sourceSessionId: 'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string' ? entry.sourceSessionId : null, gameName: entry.worldName, themeText: entry.themeTags[0] ?? '方洞挑战', twistRule: entry.subtitle, summary: entry.summaryText, tags: entry.themeTags, coverImageSrc: entry.coverImageSrc, backgroundPrompt: entry.backgroundPrompt ?? '方洞挑战运行背景', backgroundImageSrc: entry.backgroundImageSrc ?? null, shapeOptions: entry.shapeOptions ?? [], holeOptions: entry.holeOptions ?? [], shapeCount: entry.shapeCount ?? 8, difficulty: entry.difficulty ?? 4, publicationStatus: 'published', playCount: entry.playCount ?? 0, updatedAt: entry.updatedAt, publishedAt: entry.publishedAt, publishReady: true, }; } function buildSquareHoleProfileFromSession( session: SquareHoleSessionSnapshot | null, ): SquareHoleWorkProfile | null { const draft = session?.draft; if (!session || !draft?.profileId) { return null; } const now = session.updatedAt || new Date().toISOString(); return { workId: draft.profileId, profileId: draft.profileId, ownerUserId: 'current-user', sourceSessionId: session.sessionId, gameName: draft.gameName, themeText: draft.themeText, twistRule: draft.twistRule, summary: draft.summary, tags: draft.tags, coverImageSrc: draft.coverImageSrc ?? null, backgroundPrompt: draft.backgroundPrompt, backgroundImageSrc: draft.backgroundImageSrc ?? null, shapeOptions: draft.shapeOptions, holeOptions: draft.holeOptions, shapeCount: draft.shapeCount, difficulty: draft.difficulty, publicationStatus: 'draft', playCount: 0, updatedAt: now, publishedAt: null, publishReady: Boolean(draft.publishReady), }; } function mergePuzzleWorkSummary( current: PuzzleWorkSummary, updated: PuzzleWorkSummary, ): PuzzleWorkSummary { return current.profileId === updated.profileId ? updated : current; } const PUZZLE_ONBOARDING_FIRST_VISIT_STORAGE_KEY = 'genarrative.puzzle-onboarding.first-visit.v1'; const PUZZLE_ONBOARDING_COPY = '待定待定待定'; const PUZZLE_ONBOARDING_CLEAR_COPY = '只差一步,就可以永久保留你的梦'; const PUZZLE_ONBOARDING_GENERATED_DELAY_MS = 700; function escapePuzzleOnboardingSvgText(value: string) { return value .replace(/&/gu, '&') .replace(//gu, '>') .replace(/"/gu, '"'); } function buildPuzzleOnboardingFallbackImage(promptText: string) { const trimmedPrompt = promptText.trim(); const displayPrompt = escapePuzzleOnboardingSvgText( trimmedPrompt.slice(0, 12) || '百梦拼图', ); return ( 'data:image/svg+xml;utf8,' + encodeURIComponent(` ${displayPrompt} `) ); } function buildPuzzleOnboardingFallbackWork( promptText: string, ): PuzzleWorkSummary { const now = new Date().toISOString(); const seed = Date.now(); const coverImageSrc = buildPuzzleOnboardingFallbackImage(promptText); const level: PuzzleDraftLevel = { levelId: 'onboarding-local-level-1', levelName: '梦境拼图', pictureDescription: promptText, pictureReference: null, candidates: [ { candidateId: 'onboarding-local-candidate-1', imageSrc: coverImageSrc, assetId: 'onboarding-local-asset-1', prompt: promptText, actualPrompt: promptText, sourceType: 'generated', selected: true, }, ], selectedCandidateId: 'onboarding-local-candidate-1', coverImageSrc, coverAssetId: 'onboarding-local-asset-1', generationStatus: 'ready', }; return { workId: `onboarding-local-work-${seed}`, profileId: `onboarding-local-profile-${seed}`, ownerUserId: 'onboarding-guest', sourceSessionId: null, authorDisplayName: '百梦主', workTitle: '梦境拼图', workDescription: promptText, levelName: level.levelName, summary: promptText, themeTags: ['新手引导', '拼图'], coverImageSrc, coverAssetId: level.coverAssetId, publicationStatus: 'draft', updatedAt: now, publishedAt: null, playCount: 0, remixCount: 0, likeCount: 0, publishReady: true, levels: [level], }; } function shouldUseLocalPuzzleOnboardingFallback(error: unknown) { return ( error instanceof ApiClientError && error.status === 404 && (error.code === 'NOT_FOUND' || error.message.includes('资源不存在')) ); } function isMissingPuzzleWorkError(error: unknown) { return ( (error instanceof ApiClientError && error.status === 404 && (error.code === 'NOT_FOUND' || error.message.includes('资源不存在') || error.message.includes('未找到'))) || (error instanceof Error && (error.message.includes('资源不存在') || error.message.includes('未找到拼图作品'))) ); } function maybeAlertRuntimeNotFoundAndReturnHome() { if (typeof window === 'undefined') { return false; } const recoveryAction = resolveRuntimeNotFoundRecoveryAction( window.location.pathname, ); if (!recoveryAction) { return false; } // 中文注释:直接 runtime 深链找不到作品时,弹窗确认后立刻回首页,避免保留空白运行态。 window.alert('作品不存在或已下架,将返回首页。'); pushAppHistoryPath(recoveryAction.nextPath); return true; } function hasSeenPuzzleOnboarding() { if (typeof window === 'undefined') { return true; } try { return ( window.localStorage.getItem(PUZZLE_ONBOARDING_FIRST_VISIT_STORAGE_KEY) === '1' ); } catch { return false; } } function markPuzzleOnboardingSeen() { if (typeof window === 'undefined') { return; } try { window.localStorage.setItem(PUZZLE_ONBOARDING_FIRST_VISIT_STORAGE_KEY, '1'); } catch { // 中文注释:localStorage 不可写时只降级为本次会话展示,不影响主流程。 } } function PuzzleOnboardingView({ prompt, phase, error, onPromptChange, onSubmit, onSkip, }: { prompt: string; phase: PuzzleOnboardingPhase; error: string | null; onPromptChange: (value: string) => void; onSubmit: () => void; onSkip: () => void; }) { const isGenerating = phase === 'generating'; const isGenerated = phase === 'generated'; const canSubmit = Boolean(prompt.trim()) && !isGenerating && !isGenerated; return (
{isGenerating ? ( ) : ( )}

{PUZZLE_ONBOARDING_COPY}

{ event.preventDefault(); onSubmit(); }} >