import { AnimatePresence, motion } from 'motion/react'; import { lazy, Suspense, useCallback, useEffect, useMemo, useState } from 'react'; import type { CustomWorldLibraryEntry } from '../../../packages/shared/src/contracts/runtime'; import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets'; import { readCustomWorldAgentUiState } from '../../services/customWorldAgentUiState'; import { getRpgProfileDashboard } from '../../services/rpg-entry'; import { rpgCreationPreviewAdapter } from '../../services/rpg-creation/rpgCreationPreviewAdapter'; import type { CustomWorldProfile } from '../../types'; import { useAuthUi } from '../auth/AuthUiContext'; import { CustomWorldCreationHub } from '../custom-world-home/CustomWorldCreationHub'; import { RpgEntryCreationTypeModal } from './RpgEntryCreationTypeModal'; import { RpgEntryHomeView } from './RpgEntryHomeView'; import { buildCreationHubFallbackItems, normalizeAgentBackedProfile, resolveRpgCreationErrorMessage, } from './rpgEntryShared'; import type { RpgEntryFlowShellProps } from './rpgEntryTypes'; import { RpgEntryWorldDetailView } from './RpgEntryWorldDetailView'; import { useRpgCreationAgentOperationPolling } from './useRpgCreationAgentOperationPolling'; import { useRpgCreationEnterWorld } from './useRpgCreationEnterWorld'; import { useRpgCreationResultAutosave } from './useRpgCreationResultAutosave'; import { useRpgCreationSessionController } from './useRpgCreationSessionController'; import { useRpgEntryBootstrap } from './useRpgEntryBootstrap'; import { useRpgEntryLibraryDetail } from './useRpgEntryLibraryDetail'; import { useRpgEntryNavigation } from './useRpgEntryNavigation'; const CustomWorldGenerationView = lazy(async () => { const module = await import('../CustomWorldGenerationView'); return { default: module.CustomWorldGenerationView, }; }); const RpgCreationResultView = lazy(async () => { const module = await import('../rpg-creation-result/RpgCreationResultView'); return { default: module.RpgCreationResultView, }; }); const CustomWorldAgentWorkspace = lazy(async () => { const module = await import( '../custom-world-agent/CustomWorldAgentWorkspace' ); return { default: module.CustomWorldAgentWorkspace, }; }); function LazyPanelFallback({ label }: { label: string }) { return (
{label}
); } export function RpgEntryFlowShellImpl({ selectionStage, setSelectionStage, hasSavedGame, savedSnapshot, handleContinueGame, handleStartNewGame, handleCustomWorldSelect, }: RpgEntryFlowShellProps) { const authUi = useAuthUi(); const [showCreationTypeModal, setShowCreationTypeModal] = useState(false); const [selectedDetailEntry, setSelectedDetailEntry] = useState< CustomWorldLibraryEntry | null >(null); const hasInitialAgentSession = Boolean( readCustomWorldAgentUiState().activeSessionId, ); const platformBootstrap = useRpgEntryBootstrap({ user: authUi?.user, getProfileDashboard: getRpgProfileDashboard, handleContinueGame, hasInitialAgentSession, }); const entryNavigation = useRpgEntryNavigation({ setSelectionStage, setSelectedDetailEntry, }); const enterCreateTab = useCallback(() => { platformBootstrap.setPlatformTab('create'); }, [platformBootstrap]); const sessionController = useRpgCreationSessionController({ userId: authUi?.user?.id, openLoginModal: authUi?.openLoginModal, selectionStage, setSelectionStage, enterCreateTab, onSessionOpened: () => { setShowCreationTypeModal(false); }, }); useRpgCreationAgentOperationPolling({ activeAgentSessionId: sessionController.activeAgentSessionId, activeAgentOperationId: sessionController.activeAgentOperationId, userId: authUi?.user?.id, setAgentOperation: sessionController.setAgentOperation, persistAgentUiState: sessionController.persistAgentUiState, syncAgentSessionSnapshot: sessionController.syncAgentSessionSnapshot, }); const autosaveCoordinator = useRpgCreationResultAutosave({ selectionStage, activeAgentSessionId: sessionController.activeAgentSessionId, agentSession: sessionController.agentSession, generatedCustomWorldProfile: sessionController.generatedCustomWorldProfile, isAgentDraftResultView: sessionController.isAgentDraftResultView, userId: authUi?.user?.id, setGeneratedCustomWorldProfile: sessionController.setGeneratedCustomWorldProfile, setAgentOperation: sessionController.setAgentOperation, setSavedCustomWorldEntries: platformBootstrap.setSavedCustomWorldEntries, setSelectedDetailEntry, refreshCustomWorldWorks: platformBootstrap.refreshCustomWorldWorks, persistAgentUiState: sessionController.persistAgentUiState, syncAgentSessionSnapshot: sessionController.syncAgentSessionSnapshot, buildDraftResultProfile: (session) => rpgCreationPreviewAdapter.buildPreviewFromSession(session), }); const detailNavigation = useRpgEntryLibraryDetail({ userId: authUi?.user?.id, selectedDetailEntry, setSelectedDetailEntry, savedCustomWorldEntries: platformBootstrap.savedCustomWorldEntries, setSavedCustomWorldEntries: platformBootstrap.setSavedCustomWorldEntries, setGeneratedCustomWorldProfile: sessionController.setGeneratedCustomWorldProfile, setCustomWorldError: sessionController.setCustomWorldError, setCustomWorldAutoSaveError: autosaveCoordinator.setCustomWorldAutoSaveError, setCustomWorldAutoSaveState: autosaveCoordinator.setCustomWorldAutoSaveState, setCustomWorldGenerationViewSource: sessionController.setCustomWorldGenerationViewSource, setCustomWorldResultViewSource: sessionController.setCustomWorldResultViewSource, setSelectionStage, setPlatformTabToCreate: enterCreateTab, setPlatformError: platformBootstrap.setPlatformError, appendBrowseHistoryEntry: platformBootstrap.appendBrowseHistoryEntry, refreshCustomWorldWorks: platformBootstrap.refreshCustomWorldWorks, refreshPublishedGallery: platformBootstrap.refreshPublishedGallery, persistAgentUiState: sessionController.persistAgentUiState, syncAgentSessionSnapshot: sessionController.syncAgentSessionSnapshot, buildDraftResultProfile: (session) => rpgCreationPreviewAdapter.buildPreviewFromSession(session), suppressAgentDraftResultAutoOpen: sessionController.suppressAgentDraftResultAutoOpen, releaseAgentDraftResultAutoOpenSuppression: sessionController.releaseAgentDraftResultAutoOpenSuppression, resetAutoSaveTrackingToIdle: autosaveCoordinator.resetAutoSaveTrackingToIdle, markAutoSavedProfile: autosaveCoordinator.markAutoSavedProfile, }); const enterWorldCoordinator = useRpgCreationEnterWorld({ isAgentDraftResultView: sessionController.isAgentDraftResultView, activeAgentSessionId: sessionController.activeAgentSessionId, generatedCustomWorldProfile: sessionController.generatedCustomWorldProfile, agentSessionProfile: sessionController.agentDraftResultProfile, agentSession: sessionController.agentSession, handleCustomWorldSelect, executePublishWorld: () => autosaveCoordinator.executeAgentActionAndWait({ action: 'publish_world', }), syncAgentDraftResultProfile: autosaveCoordinator.syncAgentDraftResultProfile, setGeneratedCustomWorldProfile: sessionController.setGeneratedCustomWorldProfile, }); const previewCustomWorldCharacters = useMemo( () => sessionController.generatedCustomWorldProfile ? buildCustomWorldPlayableCharacters( sessionController.generatedCustomWorldProfile, ) : [], [sessionController.generatedCustomWorldProfile], ); const agentResultPreview = sessionController.agentSession?.resultPreview ?? null; const agentResultPreviewBlockers = useMemo( () => agentResultPreview?.blockers?.map((entry) => entry.message) ?? [], [agentResultPreview], ); const agentResultPreviewQualityFindings = useMemo( () => agentResultPreview?.qualityFindings ?? [], [agentResultPreview], ); const agentResultPreviewSourceLabel = useMemo(() => { if (!agentResultPreview?.source) { return null; } if (agentResultPreview.source === 'published_profile') { return '已发布世界'; } if (agentResultPreview.source === 'session_preview') { return '会话预览'; } return '服务端预览'; }, [agentResultPreview]); const featuredGalleryEntries = useMemo( () => platformBootstrap.publishedGalleryEntries.slice(0, 6), [platformBootstrap.publishedGalleryEntries], ); const creationHubItems = platformBootstrap.customWorldWorkEntries.length > 0 ? platformBootstrap.customWorldWorkEntries : buildCreationHubFallbackItems(platformBootstrap.savedCustomWorldEntries); const resultViewError = autosaveCoordinator.customWorldAutoSaveError ?? sessionController.customWorldError; useEffect(() => { if ( selectionStage === 'custom-world-result' && !sessionController.generatedCustomWorldProfile ) { setSelectionStage(selectedDetailEntry ? 'detail' : 'platform'); } }, [ selectedDetailEntry, selectionStage, sessionController.generatedCustomWorldProfile, setSelectionStage, ]); const runProtectedAction = useCallback( (action: () => void) => { if (!authUi?.requireAuth) { action(); return; } authUi.requireAuth(action); }, [authUi], ); const openCreationTypePicker = useCallback(() => { if (sessionController.isCreatingAgentSession) { return; } if (!hasSavedGame) { handleStartNewGame(); } sessionController.setCreationTypeError(null); setShowCreationTypeModal(true); }, [ handleStartNewGame, hasSavedGame, sessionController, ]); const leaveAgentWorkspace = useCallback(() => { enterCreateTab(); sessionController.resetSessionViewState(); sessionController.setGeneratedCustomWorldProfile(null); autosaveCoordinator.resetAutoSaveTrackingToIdle(); sessionController.persistAgentUiState( sessionController.activeAgentSessionId, null, ); setSelectionStage('platform'); }, [ autosaveCoordinator, enterCreateTab, sessionController, setSelectionStage, ]); const leaveAgentDraftGeneration = useCallback(() => { if (sessionController.isActiveGenerationRunning) { return; } sessionController.setAgentDraftGenerationStartedAt(null); sessionController.setCustomWorldGenerationViewSource(null); setSelectionStage('agent-workspace'); }, [sessionController, setSelectionStage]); const leaveAgentDraftResult = useCallback(() => { sessionController.suppressAgentDraftResultAutoOpen(); sessionController.setGeneratedCustomWorldProfile(null); sessionController.setCustomWorldError(null); autosaveCoordinator.resetAutoSaveTrackingToIdle(); sessionController.setCustomWorldGenerationViewSource(null); sessionController.setCustomWorldResultViewSource(null); enterCreateTab(); setSelectionStage('platform'); }, [ autosaveCoordinator, enterCreateTab, sessionController, setSelectionStage, ]); const leaveCustomWorldResult = useCallback(() => { sessionController.setGeneratedCustomWorldProfile(null); sessionController.setCustomWorldError(null); autosaveCoordinator.resetAutoSaveTrackingToIdle(); sessionController.setCustomWorldGenerationViewSource(null); sessionController.setCustomWorldResultViewSource(null); setSelectionStage(selectedDetailEntry ? 'detail' : 'platform'); }, [ autosaveCoordinator, selectedDetailEntry, sessionController, setSelectionStage, ]); const handleStartSelectedWorld = useCallback(() => { if (!selectedDetailEntry) { return; } runProtectedAction(() => { handleCustomWorldSelect(selectedDetailEntry.profile); }); }, [handleCustomWorldSelect, runProtectedAction, selectedDetailEntry]); const creationHubContent = ( { platformBootstrap.setPlatformTab('home'); }} onRetry={() => { platformBootstrap.setPlatformError(null); void platformBootstrap.refreshCustomWorldWorks().catch((error) => { platformBootstrap.setPlatformError( resolveRpgCreationErrorMessage(error, '读取创作作品列表失败。'), ); }); }} onCreateNew={openCreationTypePicker} onOpenDraft={(item) => { runProtectedAction(() => { void detailNavigation.handleOpenCreationWork(item); }); }} onEnterPublished={(profileId) => { runProtectedAction(() => { const matchedWork = creationHubItems.find( (entry) => entry.profileId === profileId, ); if (!matchedWork) { return; } void detailNavigation.handleOpenCreationWork(matchedWork); }); }} /> ); return ( <> {selectionStage === 'platform' && ( { void platformBootstrap.handleResumeSaveEntry(entry); }} onOpenCreateWorld={openCreationTypePicker} onOpenCreateTypePicker={openCreationTypePicker} onOpenGalleryDetail={(entry) => { runProtectedAction(() => { void detailNavigation.openGalleryDetail(entry); }); }} onOpenLibraryDetail={(entry) => { runProtectedAction(() => { detailNavigation.openLibraryDetail(entry); }); }} onOpenProfileDashboardCard={() => { if (platformBootstrap.dashboardError) { void platformBootstrap.refreshProfileDashboard(); } }} /> )} {selectionStage === 'detail' && ( {detailNavigation.isDetailLoading || !selectedDetailEntry ? (
{detailNavigation.detailError || '正在读取作品详情...'}
) : ( { detailNavigation.setDetailError(null); entryNavigation.backToPlatformHome(); }} onStartGame={handleStartSelectedWorld} onContinueEdit={ detailNavigation.isSelectedWorldOwned ? () => { runProtectedAction(() => { detailNavigation.openSavedCustomWorldEditor( selectedDetailEntry, ); }); } : null } onPublish={ selectedDetailEntry.visibility === 'draft' && detailNavigation.isSelectedWorldOwned ? () => { runProtectedAction(() => { void detailNavigation.handlePublishSelectedWorld(); }); } : null } onUnpublish={ selectedDetailEntry.visibility === 'published' && detailNavigation.isSelectedWorldOwned ? () => { runProtectedAction(() => { void detailNavigation.handleUnpublishSelectedWorld(); }); } : null } onDelete={ detailNavigation.isSelectedWorldOwned ? () => { runProtectedAction(() => { void detailNavigation.handleDeleteSelectedWorld(); }); } : null } /> )}
)} {selectionStage === 'agent-workspace' && ( } > {sessionController.agentSession ? ( { void sessionController.submitAgentMessage(payload); }} onExecuteAction={(payload) => { void sessionController.executeAgentAction(payload); }} /> ) : (
{sessionController.isLoadingAgentSession ? '正在准备 Agent 共创工作区...' : sessionController.creationTypeError || '正在恢复创作工作区...'}
)}
)} {selectionStage === 'custom-world-generating' && ( } > { void sessionController.executeAgentAction({ action: 'draft_foundation', }); }} onInterrupt={undefined} backLabel="返回工作区" settingActionLabel={null} retryLabel="重新生成草稿" settingTitle="当前世界信息" settingDescription={null} progressTitle="世界草稿生成进度" activeBadgeLabel="草稿编译中" pausedBadgeLabel="草稿生成已暂停" idleBadgeLabel="等待返回工作区" /> )} {selectionStage === 'custom-world-result' && sessionController.generatedCustomWorldProfile && ( } > { sessionController.setGeneratedCustomWorldProfile( normalizeAgentBackedProfile(profile), ); }} onBack={ sessionController.isAgentDraftResultView ? () => { void (async () => { const currentProfile = sessionController.generatedCustomWorldProfile; if (!currentProfile) { leaveAgentDraftResult(); return; } await autosaveCoordinator.syncAgentDraftResultProfile( currentProfile, ); leaveAgentDraftResult(); })().catch((error) => { sessionController.setCustomWorldError( resolveRpgCreationErrorMessage( error, '返回创作前同步草稿失败。', ), ); }); } : leaveCustomWorldResult } onEditSetting={undefined} onRegenerate={undefined} onContinueExpand={undefined} onEnterWorld={() => { runProtectedAction(() => { void enterWorldCoordinator .enterWorldFromCurrentResult() .catch((error) => { sessionController.setCustomWorldError( resolveRpgCreationErrorMessage( error, '发布并进入世界失败。', ), ); }); }); }} readOnly={false} compactAgentResultMode={sessionController.isAgentDraftResultView} backLabel={ sessionController.isAgentDraftResultView ? '返回创作' : undefined } editActionLabel="继续调整设定" enterWorldActionLabel={ sessionController.isAgentDraftResultView && sessionController.agentSession?.stage !== 'published' ? '发布并进入世界' : '进入世界' } publishReady={ sessionController.isAgentDraftResultView ? Boolean(agentResultPreview?.publishReady) : true } publishBlockers={ sessionController.isAgentDraftResultView ? agentResultPreviewBlockers : [] } qualityFindings={ sessionController.isAgentDraftResultView ? agentResultPreviewQualityFindings : [] } previewSourceLabel={ sessionController.isAgentDraftResultView ? agentResultPreviewSourceLabel : null } autoSaveState={autosaveCoordinator.customWorldAutoSaveState} /> )}
{ if (sessionController.isCreatingAgentSession) { return; } setShowCreationTypeModal(false); }} onSelectRpg={() => { runProtectedAction(() => { void sessionController.openRpgAgentWorkspace(); }); }} /> ); } export const RpgCreationShellImpl = RpgEntryFlowShellImpl; export default RpgEntryFlowShellImpl;