import { AnimatePresence, motion } from 'motion/react'; import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState, } from 'react'; import type { CustomWorldAgentActionRequest, CustomWorldAgentMessage, CustomWorldAgentOperationRecord, CustomWorldAgentSessionSnapshot, SendCustomWorldAgentMessageRequest, } from '../../../packages/shared/src/contracts/customWorldAgent'; import type { CustomWorldGalleryCard, CustomWorldLibraryEntry, ProfileDashboardSummary, ProfileSaveArchiveSummary, } from '../../../packages/shared/src/contracts/runtime'; import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets'; import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes'; import { createCustomWorldAgentSession, executeCustomWorldAgentAction, getCustomWorldAgentOperation, getCustomWorldAgentSession, streamCustomWorldAgentMessage, } from '../../services/aiService'; import { buildCustomWorldProfileFromAgentDraft } from '../../services/customWorldAgentDraftResult'; import { buildAgentDraftFoundationAnchorEntries, buildAgentDraftFoundationGenerationProgress, buildAgentDraftFoundationSettingText, isDraftFoundationOperation, isDraftFoundationOperationRunning, } from '../../services/customWorldAgentGenerationProgress'; import { readCustomWorldAgentUiState, writeCustomWorldAgentUiState, } from '../../services/customWorldAgentUiState'; import { buildCustomWorldCreatorIntentFoundationText } from '../../services/customWorldCreatorIntent'; import { hasPendingPlatformBrowseHistoryMigration, markPlatformBrowseHistoryMigrated, type PlatformBrowseHistoryEntry, type PlatformBrowseHistoryWriteEntry, readPlatformBrowseHistory, writePlatformBrowseHistory, } from '../../services/platformBrowseHistory'; import { deleteCustomWorldProfile, getCustomWorldGalleryDetail, getProfileDashboard, listCustomWorldGallery, listCustomWorldLibrary, listProfileBrowseHistory, listProfileSaveArchives, publishCustomWorldProfile, resumeProfileSaveArchive, syncProfileBrowseHistory, unpublishCustomWorldProfile, upsertCustomWorldProfile, upsertProfileBrowseHistory, } from '../../services/storageService'; import { type CustomWorldProfile, type GameState } from '../../types'; import { useAuthUi } from '../auth/AuthUiContext'; import { PlatformCreationTypeModal } from './PlatformCreationTypeModal'; import { type PlatformHomeTab, PlatformHomeView } from './PlatformHomeView'; import { PlatformWorldDetailView } from './PlatformWorldDetailView'; const CustomWorldGenerationView = lazy(async () => { const module = await import('../CustomWorldGenerationView'); return { default: module.CustomWorldGenerationView, }; }); const CustomWorldResultView = lazy(async () => { const module = await import('../CustomWorldResultView'); return { default: module.CustomWorldResultView, }; }); const CustomWorldAgentWorkspace = lazy(async () => { const module = await import( '../custom-world-agent/CustomWorldAgentWorkspace' ); return { default: module.CustomWorldAgentWorkspace, }; }); export type SelectionStage = | 'platform' | 'detail' | 'agent-workspace' | 'custom-world-generating' | 'custom-world-result'; type CustomWorldGenerationViewSource = 'agent-draft-foundation' | null; type CustomWorldResultViewSource = 'saved-profile' | 'agent-draft' | null; type CustomWorldAutoSaveState = 'idle' | 'saving' | 'saved' | 'error'; type PreGameSelectionFlowProps = { selectionStage: SelectionStage; setSelectionStage: (stage: SelectionStage) => void; gameState: GameState; hasSavedGame: boolean; savedSnapshot: HydratedSavedGameSnapshot | null; handleContinueGame: (snapshot?: HydratedSavedGameSnapshot | null) => void; handleStartNewGame: () => void; handleCustomWorldSelect: (customWorldProfile: CustomWorldProfile) => void; }; function resolveErrorMessage(error: unknown, fallback: string) { return error instanceof Error ? error.message : fallback; } function createFailedAgentOperation(params: { type: CustomWorldAgentOperationRecord['type']; phaseLabel: string; error: string; }): CustomWorldAgentOperationRecord { return { operationId: `local-failed-${Date.now()}`, type: params.type, status: 'failed', phaseLabel: params.phaseLabel, phaseDetail: params.error, progress: 100, error: params.error, }; } function buildOptimisticAgentMessage( payload: Pick, ): CustomWorldAgentMessage { return { ...payload, createdAt: new Date().toISOString(), relatedOperationId: null, }; } function normalizeAgentBackedProfile(profile: CustomWorldProfile) { const foundationText = buildCustomWorldCreatorIntentFoundationText( profile.creatorIntent, ).trim(); if (!foundationText || foundationText === profile.settingText.trim()) { return profile; } return { ...profile, settingText: foundationText, } satisfies CustomWorldProfile; } function LazyPanelFallback({ label }: { label: string }) { return (
{label}
); } export function PreGameSelectionFlow({ selectionStage, setSelectionStage, hasSavedGame, savedSnapshot, handleContinueGame, handleStartNewGame, handleCustomWorldSelect, }: PreGameSelectionFlowProps) { const authUi = useAuthUi(); const initialAgentUiStateRef = useRef(readCustomWorldAgentUiState()); const hasAppliedInitialAgentWorkspaceRef = useRef(false); const hasRequestedInitialAgentWorkspaceAuthRef = useRef(false); const [generatedCustomWorldProfile, setGeneratedCustomWorldProfile] = useState(null); const [savedCustomWorldEntries, setSavedCustomWorldEntries] = useState< CustomWorldLibraryEntry[] >([]); const [publishedGalleryEntries, setPublishedGalleryEntries] = useState< CustomWorldGalleryCard[] >([]); const [historyEntries, setHistoryEntries] = useState< PlatformBrowseHistoryEntry[] >([]); const [saveEntries, setSaveEntries] = useState( [], ); const [platformTab, setPlatformTab] = useState('home'); const [selectedDetailEntry, setSelectedDetailEntry] = useState | null>(null); const [showCreationTypeModal, setShowCreationTypeModal] = useState(false); const [creationTypeError, setCreationTypeError] = useState( null, ); const [isCreatingAgentSession, setIsCreatingAgentSession] = useState(false); const [activeAgentSessionId, setActiveAgentSessionId] = useState< string | null >(() => initialAgentUiStateRef.current.activeSessionId ?? null); const [activeAgentOperationId, setActiveAgentOperationId] = useState< string | null >(() => initialAgentUiStateRef.current.activeOperationId ?? null); const [agentSession, setAgentSession] = useState(null); const [agentOperation, setAgentOperation] = useState(null); const [streamingAgentReplyText, setStreamingAgentReplyText] = useState(''); const [isStreamingAgentReply, setIsStreamingAgentReply] = useState(false); const [isLoadingAgentSession, setIsLoadingAgentSession] = useState(false); const [customWorldError, setCustomWorldError] = useState(null); const [platformError, setPlatformError] = useState(null); const [profileDashboard, setProfileDashboard] = useState(null); const [dashboardError, setDashboardError] = useState(null); const [_historyError, setHistoryError] = useState(null); const [saveError, setSaveError] = useState(null); const [detailError, setDetailError] = useState(null); const [isLoadingPlatform, setIsLoadingPlatform] = useState(false); const [isLoadingDashboard, setIsLoadingDashboard] = useState(false); const [isResumingSaveWorldKey, setIsResumingSaveWorldKey] = useState< string | null >(null); const [isDetailLoading, setIsDetailLoading] = useState(false); const [isMutatingDetail, setIsMutatingDetail] = useState(false); const [customWorldAutoSaveState, setCustomWorldAutoSaveState] = useState('idle'); const [customWorldAutoSaveError, setCustomWorldAutoSaveError] = useState< string | null >(null); const [customWorldGenerationViewSource, setCustomWorldGenerationViewSource] = useState(null); const [customWorldResultViewSource, setCustomWorldResultViewSource] = useState(null); const [agentDraftGenerationStartedAt, setAgentDraftGenerationStartedAt] = useState(null); const customWorldAutoSaveTimeoutRef = useRef(null); const lastAutoSavedProfileSignatureRef = useRef(null); const latestAutoSaveRequestIdRef = useRef(0); const platformTabBootstrapUserIdRef = useRef( undefined, ); const previewCustomWorldCharacters = useMemo( () => generatedCustomWorldProfile ? buildCustomWorldPlayableCharacters(generatedCustomWorldProfile) : [], [generatedCustomWorldProfile], ); const featuredGalleryEntries = useMemo( () => publishedGalleryEntries.slice(0, 6), [publishedGalleryEntries], ); const isAuthenticated = Boolean(authUi?.user); const runProtectedAction = useCallback( (action: () => void) => { if (!authUi?.requireAuth) { action(); return; } authUi.requireAuth(action); }, [authUi], ); const persistAgentUiState = useCallback( (nextSessionId: string | null, nextOperationId: string | null) => { setActiveAgentSessionId(nextSessionId); setActiveAgentOperationId(nextOperationId); writeCustomWorldAgentUiState({ activeSessionId: nextSessionId, activeOperationId: nextOperationId, }); }, [], ); const syncAgentSessionSnapshot = useCallback(async (sessionId: string) => { const nextSession = await getCustomWorldAgentSession(sessionId); setAgentSession(nextSession); return nextSession; }, []); const refreshProfileDashboard = useCallback(async () => { if (!authUi?.user) { setProfileDashboard(null); setDashboardError(null); setIsLoadingDashboard(false); return; } setIsLoadingDashboard(true); setDashboardError(null); try { setProfileDashboard(await getProfileDashboard()); } catch (error) { setDashboardError(resolveErrorMessage(error, '读取个人数据看板失败。')); } finally { setIsLoadingDashboard(false); } }, [authUi?.user]); const appendBrowseHistoryEntry = useCallback( async (entry: PlatformBrowseHistoryWriteEntry) => { const nextEntries = writePlatformBrowseHistory(authUi?.user, entry); setHistoryEntries(nextEntries); setHistoryError(null); if (!authUi?.user) { return; } try { const syncedEntries = await upsertProfileBrowseHistory(entry); setHistoryEntries(syncedEntries); markPlatformBrowseHistoryMigrated(authUi?.user); } catch (error) { setHistoryError(resolveErrorMessage(error, '写入浏览历史失败。')); } }, [authUi?.user], ); useEffect(() => { const initialAgentSessionId = initialAgentUiStateRef.current.activeSessionId; if (!initialAgentSessionId || hasAppliedInitialAgentWorkspaceRef.current) { return; } setPlatformTab('create'); // URL 或 sessionStorage 中残留的共创工作区属于受保护入口, // 未登录时只允许先唤起登录弹窗,不能直接恢复会话请求。 if (!authUi?.user) { if (!hasRequestedInitialAgentWorkspaceAuthRef.current) { hasRequestedInitialAgentWorkspaceAuthRef.current = true; authUi?.openLoginModal?.(() => { setSelectionStage('agent-workspace'); }); } return; } hasAppliedInitialAgentWorkspaceRef.current = true; setSelectionStage('agent-workspace'); }, [authUi?.openLoginModal, authUi?.user, setSelectionStage]); useEffect(() => { if (!selectedDetailEntry) { return; } const nextOwnedEntry = savedCustomWorldEntries.find( (entry) => entry.ownerUserId === selectedDetailEntry.ownerUserId && entry.profileId === selectedDetailEntry.profileId, ); if (nextOwnedEntry && nextOwnedEntry !== selectedDetailEntry) { setSelectedDetailEntry(nextOwnedEntry); } }, [savedCustomWorldEntries, selectedDetailEntry]); useEffect(() => { let isActive = true; void (async () => { const localHistoryEntries = readPlatformBrowseHistory(authUi?.user); setHistoryEntries(localHistoryEntries); setHistoryError(null); setSaveError(null); setIsLoadingPlatform(true); setPlatformError(null); setIsLoadingDashboard(isAuthenticated); setDashboardError(null); if (!isAuthenticated) { setSavedCustomWorldEntries([]); setSaveEntries([]); setProfileDashboard(null); } try { const [ libraryEntriesResult, galleryEntriesResult, dashboardResult, historyResult, saveArchivesResult, ] = await Promise.allSettled([ isAuthenticated ? listCustomWorldLibrary() : Promise.resolve([]), listCustomWorldGallery(), isAuthenticated ? getProfileDashboard() : Promise.resolve(null), isAuthenticated ? (async () => { let nextEntries = await listProfileBrowseHistory(); if ( hasPendingPlatformBrowseHistoryMigration(authUi?.user) && localHistoryEntries.length > 0 ) { nextEntries = await syncProfileBrowseHistory(localHistoryEntries); markPlatformBrowseHistoryMigrated(authUi?.user); } return nextEntries; })() : Promise.resolve(localHistoryEntries), isAuthenticated ? listProfileSaveArchives() : Promise.resolve([]), ]); if (!isActive) { return; } if (libraryEntriesResult.status === 'fulfilled') { setSavedCustomWorldEntries(libraryEntriesResult.value); } else { setSavedCustomWorldEntries([]); } if (galleryEntriesResult.status === 'fulfilled') { setPublishedGalleryEntries(galleryEntriesResult.value); } else { setPublishedGalleryEntries([]); } if ( (isAuthenticated && libraryEntriesResult.status === 'rejected') || galleryEntriesResult.status === 'rejected' ) { const platformFailure = libraryEntriesResult.status === 'rejected' ? libraryEntriesResult.reason : galleryEntriesResult.status === 'rejected' ? galleryEntriesResult.reason : null; setPlatformError( resolveErrorMessage(platformFailure, '读取平台数据失败。'), ); } if (dashboardResult.status === 'fulfilled') { setProfileDashboard(dashboardResult.value); } else if (isAuthenticated) { setProfileDashboard(null); setDashboardError( resolveErrorMessage( dashboardResult.reason, '读取个人数据看板失败。', ), ); } if (historyResult.status === 'fulfilled') { setHistoryEntries(historyResult.value); } else if (isAuthenticated) { setHistoryError( resolveErrorMessage(historyResult.reason, '读取浏览历史失败。'), ); } if (saveArchivesResult.status === 'fulfilled') { setSaveEntries(saveArchivesResult.value); } else if (isAuthenticated) { setSaveEntries([]); setSaveError( resolveErrorMessage( saveArchivesResult.reason, '读取存档列表失败。', ), ); } const nextPlatformBootstrapUserId = authUi?.user?.id ?? null; if ( platformTabBootstrapUserIdRef.current !== nextPlatformBootstrapUserId ) { platformTabBootstrapUserIdRef.current = nextPlatformBootstrapUserId; if (!initialAgentUiStateRef.current.activeSessionId) { setPlatformTab( isAuthenticated && saveArchivesResult.status === 'fulfilled' && saveArchivesResult.value.length > 0 ? 'saves' : 'home', ); } } } finally { if (isActive) { setIsLoadingPlatform(false); setIsLoadingDashboard(false); } } })(); return () => { isActive = false; }; }, [authUi?.user, isAuthenticated]); useEffect(() => { if ( selectionStage === 'custom-world-result' && !generatedCustomWorldProfile ) { setSelectionStage(selectedDetailEntry ? 'detail' : 'platform'); } }, [ generatedCustomWorldProfile, selectedDetailEntry, selectionStage, setSelectionStage, ]); useEffect( () => () => { if (customWorldAutoSaveTimeoutRef.current !== null) { window.clearTimeout(customWorldAutoSaveTimeoutRef.current); } }, [], ); useEffect(() => { if (!activeAgentSessionId) { setAgentSession(null); setAgentOperation(null); setIsLoadingAgentSession(false); setStreamingAgentReplyText(''); setIsStreamingAgentReply(false); return; } if (!authUi?.user) { setAgentSession(null); setAgentOperation(null); setIsLoadingAgentSession(false); setStreamingAgentReplyText(''); setIsStreamingAgentReply(false); return; } let cancelled = false; setIsLoadingAgentSession(true); void syncAgentSessionSnapshot(activeAgentSessionId) .then(() => { if (!cancelled) { setCreationTypeError(null); } }) .catch((error) => { if (cancelled) { return; } setCreationTypeError( resolveErrorMessage(error, '读取 Agent 共创工作区失败。'), ); setAgentSession(null); setAgentOperation(null); setStreamingAgentReplyText(''); setIsStreamingAgentReply(false); persistAgentUiState(null, null); setPlatformTab('create'); setSelectionStage('platform'); }) .finally(() => { if (!cancelled) { setIsLoadingAgentSession(false); } }); return () => { cancelled = true; }; }, [ activeAgentSessionId, authUi?.user, persistAgentUiState, setSelectionStage, syncAgentSessionSnapshot, ]); useEffect(() => { if (!activeAgentSessionId || !activeAgentOperationId || !authUi?.user) { return; } let cancelled = false; const pollOperation = async () => { try { const nextOperation = await getCustomWorldAgentOperation( activeAgentSessionId, activeAgentOperationId, ); if (cancelled) { return; } setAgentOperation(nextOperation); if ( nextOperation.status === 'completed' || nextOperation.status === 'failed' ) { persistAgentUiState(activeAgentSessionId, null); await syncAgentSessionSnapshot(activeAgentSessionId).catch( () => null, ); } } catch (error) { if (cancelled) { return; } const errorMessage = resolveErrorMessage( error, '读取共创操作状态失败。', ); setAgentOperation( createFailedAgentOperation({ type: 'process_message', phaseLabel: '读取操作状态失败', error: errorMessage, }), ); persistAgentUiState(activeAgentSessionId, null); } }; void pollOperation(); const intervalId = window.setInterval(() => { void pollOperation(); }, 1200); return () => { cancelled = true; window.clearInterval(intervalId); }; }, [ activeAgentOperationId, activeAgentSessionId, authUi?.user, persistAgentUiState, syncAgentSessionSnapshot, ]); useEffect(() => { if ( !isDraftFoundationOperationRunning(agentOperation) || agentDraftGenerationStartedAt ) { return; } setAgentDraftGenerationStartedAt(Date.now()); }, [agentDraftGenerationStartedAt, agentOperation]); useEffect(() => { if ( selectionStage !== 'custom-world-generating' || customWorldGenerationViewSource !== 'agent-draft-foundation' || !isDraftFoundationOperation(agentOperation) || agentOperation.status !== 'completed' ) { return; } let cancelled = false; const timeoutId = window.setTimeout(() => { void (async () => { const latestSession = activeAgentSessionId ? await syncAgentSessionSnapshot(activeAgentSessionId).catch( () => null, ) : agentSession; if (cancelled) { return; } const draftResultProfile = buildCustomWorldProfileFromAgentDraft( latestSession ?? agentSession, ); if (!draftResultProfile) { setAgentDraftGenerationStartedAt(null); setCustomWorldGenerationViewSource(null); setSelectionStage('agent-workspace'); return; } setGeneratedCustomWorldProfile( normalizeAgentBackedProfile(draftResultProfile), ); setAgentDraftGenerationStartedAt(null); setCustomWorldGenerationViewSource(null); setCustomWorldResultViewSource('agent-draft'); setSelectionStage('custom-world-result'); })(); }, 900); return () => { cancelled = true; window.clearTimeout(timeoutId); }; }, [ activeAgentSessionId, agentOperation, customWorldGenerationViewSource, agentSession, selectionStage, setSelectionStage, syncAgentSessionSnapshot, ]); const agentDraftSettingPreview = useMemo( () => buildAgentDraftFoundationSettingText(agentSession), [agentSession], ); const agentDraftAnchorPreviewEntries = useMemo( () => buildAgentDraftFoundationAnchorEntries(agentSession), [agentSession], ); const agentDraftResultProfile = useMemo( () => buildCustomWorldProfileFromAgentDraft(agentSession), [agentSession], ); const shouldAutoOpenAgentDraftResult = useMemo( () => Boolean( agentDraftResultProfile && agentSession && (agentSession.stage === 'object_refining' || agentSession.stage === 'visual_refining' || agentSession.stage === 'long_tail_review' || agentSession.stage === 'ready_to_publish' || agentSession.stage === 'published') && agentSession.draftCards.length > 0, ), [agentDraftResultProfile, agentSession], ); useEffect(() => { if (!shouldAutoOpenAgentDraftResult || !agentDraftResultProfile) { return; } if (selectionStage === 'agent-workspace') { setGeneratedCustomWorldProfile(agentDraftResultProfile); setCustomWorldResultViewSource('agent-draft'); setSelectionStage('custom-world-result'); return; } if ( selectionStage === 'custom-world-result' && !generatedCustomWorldProfile ) { setGeneratedCustomWorldProfile(agentDraftResultProfile); setCustomWorldResultViewSource('agent-draft'); } }, [ agentDraftResultProfile, generatedCustomWorldProfile, selectionStage, setSelectionStage, shouldAutoOpenAgentDraftResult, ]); const agentDraftGenerationProgress = useMemo( () => buildAgentDraftFoundationGenerationProgress( agentOperation, agentDraftGenerationStartedAt, ), [agentDraftGenerationStartedAt, agentOperation], ); const isAgentDraftGenerationView = customWorldGenerationViewSource === 'agent-draft-foundation'; const isAgentDraftResultView = customWorldResultViewSource === 'agent-draft'; const activeGenerationSettingText = agentDraftSettingPreview; const activeGenerationProgress = agentDraftGenerationProgress; const isActiveGenerationRunning = isDraftFoundationOperationRunning(agentOperation); const activeGenerationError = isDraftFoundationOperation(agentOperation) && agentOperation.status === 'failed' ? agentOperation.error || agentOperation.phaseDetail : null; const leaveCustomWorldResult = () => { setGeneratedCustomWorldProfile(null); setCustomWorldError(null); setCustomWorldAutoSaveError(null); setCustomWorldAutoSaveState('idle'); setCustomWorldGenerationViewSource(null); setCustomWorldResultViewSource(null); setSelectionStage( isAgentDraftResultView ? 'agent-workspace' : selectedDetailEntry ? 'detail' : 'platform', ); }; const openCreationTypePicker = () => { if (isCreatingAgentSession) { return; } if (!hasSavedGame) { handleStartNewGame(); } setCreationTypeError(null); setShowCreationTypeModal(true); }; const openRpgAgentWorkspace = async (seedText = '') => { if (isCreatingAgentSession) { return; } setIsCreatingAgentSession(true); setCreationTypeError(null); try { const { session } = await createCustomWorldAgentSession( seedText ? { seedText } : {}, ); setAgentSession(session); setAgentOperation(null); setGeneratedCustomWorldProfile(null); setCustomWorldAutoSaveError(null); setCustomWorldAutoSaveState('idle'); setAgentDraftGenerationStartedAt(null); setCustomWorldGenerationViewSource(null); setCustomWorldResultViewSource(null); persistAgentUiState(session.sessionId, null); setShowCreationTypeModal(false); setPlatformTab('create'); setSelectionStage('agent-workspace'); } catch (error) { setCreationTypeError(resolveErrorMessage(error, '开启共创工作台失败。')); } finally { setIsCreatingAgentSession(false); } }; const submitAgentMessage = async ( payload: SendCustomWorldAgentMessageRequest, ) => { if (!activeAgentSessionId) { return; } const optimisticUserMessage = buildOptimisticAgentMessage({ id: payload.clientMessageId, role: 'user', kind: 'chat', text: payload.text.trim(), }); setAgentOperation(null); persistAgentUiState(activeAgentSessionId, null); setStreamingAgentReplyText(''); setIsStreamingAgentReply(true); setAgentSession((current) => current ? { ...current, messages: [...current.messages, optimisticUserMessage], updatedAt: optimisticUserMessage.createdAt, } : current, ); try { const nextSession = await streamCustomWorldAgentMessage( activeAgentSessionId, payload, { onUpdate: (text) => { setStreamingAgentReplyText(text); }, }, ); setAgentSession(nextSession); setAgentOperation(null); setStreamingAgentReplyText(''); } catch (error) { const errorMessage = resolveErrorMessage(error, '发送共创消息失败。'); setAgentSession((current) => current ? { ...current, messages: [ ...current.messages, buildOptimisticAgentMessage({ id: `message-error-${Date.now()}`, role: 'assistant', kind: 'warning', text: errorMessage, }), ], updatedAt: new Date().toISOString(), } : current, ); setStreamingAgentReplyText(''); persistAgentUiState(activeAgentSessionId, null); } finally { setIsStreamingAgentReply(false); } }; const executeAgentAction = async (payload: CustomWorldAgentActionRequest) => { if (!activeAgentSessionId) { return; } const isDraftFoundationAction = payload.action === 'draft_foundation'; if (isDraftFoundationAction) { setGeneratedCustomWorldProfile(null); setCustomWorldError(null); setCustomWorldAutoSaveError(null); setCustomWorldAutoSaveState('idle'); setCustomWorldGenerationViewSource('agent-draft-foundation'); setCustomWorldResultViewSource(null); setAgentDraftGenerationStartedAt(Date.now()); setSelectionStage('custom-world-generating'); } try { const { operation } = await executeCustomWorldAgentAction( activeAgentSessionId, payload, ); setAgentOperation(operation); persistAgentUiState(activeAgentSessionId, operation.operationId); } catch (error) { const errorMessage = resolveErrorMessage(error, '执行共创操作失败。'); setAgentOperation( createFailedAgentOperation({ type: payload.action === 'draft_foundation' ? 'draft_foundation' : payload.action, phaseLabel: '执行操作失败', error: errorMessage, }), ); persistAgentUiState(activeAgentSessionId, null); } }; const leaveAgentWorkspace = () => { setPlatformTab('create'); setAgentOperation(null); setStreamingAgentReplyText(''); setIsStreamingAgentReply(false); setGeneratedCustomWorldProfile(null); setCustomWorldAutoSaveError(null); setCustomWorldAutoSaveState('idle'); setAgentDraftGenerationStartedAt(null); setCustomWorldGenerationViewSource(null); setCustomWorldResultViewSource(null); persistAgentUiState(activeAgentSessionId, null); setSelectionStage('platform'); }; const leaveAgentDraftGeneration = () => { if (isDraftFoundationOperationRunning(agentOperation)) { return; } setAgentDraftGenerationStartedAt(null); setCustomWorldGenerationViewSource(null); setSelectionStage('agent-workspace'); }; const leaveAgentDraftResult = () => { setGeneratedCustomWorldProfile(null); setCustomWorldError(null); setCustomWorldAutoSaveError(null); setCustomWorldAutoSaveState('idle'); setCustomWorldGenerationViewSource(null); setCustomWorldResultViewSource(null); setPlatformTab('create'); setSelectionStage('platform'); }; const retryAgentDraftGeneration = () => { void executeAgentAction({ action: 'draft_foundation', }); }; const openCustomWorldCreator = () => { openCreationTypePicker(); }; const openLibraryDetail = ( entry: CustomWorldLibraryEntry, ) => { if (entry.visibility === 'published') { void appendBrowseHistoryEntry({ ownerUserId: entry.ownerUserId, profileId: entry.profileId, worldName: entry.worldName, subtitle: entry.subtitle, summaryText: entry.summaryText, coverImageSrc: entry.coverImageSrc, themeMode: entry.themeMode, authorDisplayName: entry.authorDisplayName, }); } setSelectedDetailEntry(entry); setDetailError(null); setSelectionStage('detail'); }; const openGalleryDetail = async (entry: CustomWorldGalleryCard) => { setSelectionStage('detail'); setIsDetailLoading(true); setDetailError(null); try { const detailEntry = await getCustomWorldGalleryDetail( entry.ownerUserId, entry.profileId, ); setSelectedDetailEntry(detailEntry); void appendBrowseHistoryEntry({ ownerUserId: detailEntry.ownerUserId, profileId: detailEntry.profileId, worldName: detailEntry.worldName, subtitle: detailEntry.subtitle, summaryText: detailEntry.summaryText, coverImageSrc: detailEntry.coverImageSrc, themeMode: detailEntry.themeMode, authorDisplayName: detailEntry.authorDisplayName, }); } catch (error) { setSelectedDetailEntry(null); setDetailError(resolveErrorMessage(error, '读取作品详情失败。')); } finally { setIsDetailLoading(false); } }; const handleResumeSaveEntry = useCallback( async (entry: ProfileSaveArchiveSummary) => { if (!authUi?.user || isResumingSaveWorldKey) { return; } setIsResumingSaveWorldKey(entry.worldKey); setSaveError(null); try { const resumedArchive = await resumeProfileSaveArchive(entry.worldKey); setSaveEntries((currentEntries) => currentEntries.map((currentEntry) => currentEntry.worldKey === resumedArchive.entry.worldKey ? resumedArchive.entry : currentEntry, ), ); handleContinueGame(resumedArchive.snapshot); } catch (error) { setSaveError(resolveErrorMessage(error, '恢复存档失败。')); } finally { setIsResumingSaveWorldKey(null); } }, [authUi?.user, handleContinueGame, isResumingSaveWorldKey], ); const saveGeneratedCustomWorld = useCallback( async (profile = generatedCustomWorldProfile) => { if (!profile) { return null; } const normalizedProfile = normalizeAgentBackedProfile(profile); const profileSignature = JSON.stringify(normalizedProfile); const requestId = latestAutoSaveRequestIdRef.current + 1; latestAutoSaveRequestIdRef.current = requestId; setCustomWorldAutoSaveState('saving'); setCustomWorldAutoSaveError(null); try { const mutation = await upsertCustomWorldProfile(normalizedProfile); if (latestAutoSaveRequestIdRef.current !== requestId) { return mutation; } lastAutoSavedProfileSignatureRef.current = profileSignature; setSavedCustomWorldEntries(mutation.entries); setSelectedDetailEntry((current) => { if (!current || current.profileId === mutation.entry.profileId) { return mutation.entry; } return current; }); setCustomWorldAutoSaveState('saved'); setCustomWorldAutoSaveError(null); return mutation; } catch (error) { if (latestAutoSaveRequestIdRef.current !== requestId) { return null; } setCustomWorldAutoSaveState('error'); setCustomWorldAutoSaveError( resolveErrorMessage(error, '保存自定义世界失败。'), ); return null; } }, [generatedCustomWorldProfile], ); useEffect(() => { if (!generatedCustomWorldProfile) { setCustomWorldAutoSaveState('idle'); setCustomWorldAutoSaveError(null); lastAutoSavedProfileSignatureRef.current = null; if (customWorldAutoSaveTimeoutRef.current !== null) { window.clearTimeout(customWorldAutoSaveTimeoutRef.current); customWorldAutoSaveTimeoutRef.current = null; } return; } if (selectionStage !== 'custom-world-result') { return; } const nextSignature = JSON.stringify(generatedCustomWorldProfile); if (nextSignature === lastAutoSavedProfileSignatureRef.current) { return; } setCustomWorldAutoSaveState('saving'); if (customWorldAutoSaveTimeoutRef.current !== null) { window.clearTimeout(customWorldAutoSaveTimeoutRef.current); } const profileToSave = generatedCustomWorldProfile; customWorldAutoSaveTimeoutRef.current = window.setTimeout(() => { void saveGeneratedCustomWorld(profileToSave); customWorldAutoSaveTimeoutRef.current = null; }, 600); return () => { if (customWorldAutoSaveTimeoutRef.current !== null) { window.clearTimeout(customWorldAutoSaveTimeoutRef.current); customWorldAutoSaveTimeoutRef.current = null; } }; }, [generatedCustomWorldProfile, saveGeneratedCustomWorld, selectionStage]); const openSavedCustomWorldEditor = ( entry: CustomWorldLibraryEntry, ) => { setSelectedDetailEntry(entry); const normalizedProfile = normalizeAgentBackedProfile(entry.profile); setGeneratedCustomWorldProfile(normalizedProfile); lastAutoSavedProfileSignatureRef.current = JSON.stringify(normalizedProfile); setCustomWorldAutoSaveState('saved'); setCustomWorldAutoSaveError(null); setCustomWorldError(null); setCustomWorldGenerationViewSource(null); setCustomWorldResultViewSource('saved-profile'); setSelectionStage('custom-world-result'); }; const handleStartSelectedWorld = () => { if (!selectedDetailEntry) { return; } runProtectedAction(() => { handleCustomWorldSelect(selectedDetailEntry.profile); }); }; const handlePublishSelectedWorld = async () => { if (!selectedDetailEntry || isMutatingDetail) { return; } setIsMutatingDetail(true); setDetailError(null); try { const mutation = await publishCustomWorldProfile( selectedDetailEntry.profileId, ); setSavedCustomWorldEntries(mutation.entries); setSelectedDetailEntry(mutation.entry); setPublishedGalleryEntries(await listCustomWorldGallery()); } catch (error) { setDetailError(resolveErrorMessage(error, '发布自定义世界失败。')); } finally { setIsMutatingDetail(false); } }; const handleUnpublishSelectedWorld = async () => { if (!selectedDetailEntry || isMutatingDetail) { return; } setIsMutatingDetail(true); setDetailError(null); try { const mutation = await unpublishCustomWorldProfile( selectedDetailEntry.profileId, ); setSavedCustomWorldEntries(mutation.entries); setSelectedDetailEntry(mutation.entry); setPublishedGalleryEntries(await listCustomWorldGallery()); } catch (error) { setDetailError(resolveErrorMessage(error, '下架自定义世界失败。')); } finally { setIsMutatingDetail(false); } }; const handleDeleteSelectedWorld = async () => { if (!selectedDetailEntry || isMutatingDetail) { return; } const confirmed = window.confirm( `确认删除作品《${selectedDetailEntry.worldName}》吗?删除后会从你的作品列表和公开广场中移除。`, ); if (!confirmed) { return; } setIsMutatingDetail(true); setDetailError(null); try { const entries = await deleteCustomWorldProfile( selectedDetailEntry.profileId, ); setSavedCustomWorldEntries(entries); setSelectedDetailEntry(null); setPlatformTab('create'); setSelectionStage('platform'); setPublishedGalleryEntries(await listCustomWorldGallery()); } catch (error) { setDetailError(resolveErrorMessage(error, '删除自定义世界失败。')); } finally { setIsMutatingDetail(false); } }; const isSelectedWorldOwned = Boolean( selectedDetailEntry && savedCustomWorldEntries.some( (entry) => entry.ownerUserId === selectedDetailEntry.ownerUserId && entry.profileId === selectedDetailEntry.profileId, ), ); const resultViewError = customWorldAutoSaveError ?? customWorldError; return ( <> {selectionStage === 'platform' && ( { void handleResumeSaveEntry(entry); }} onOpenCreateWorld={openCustomWorldCreator} onOpenCreateTypePicker={openCreationTypePicker} onOpenGalleryDetail={(entry) => { runProtectedAction(() => { void openGalleryDetail(entry); }); }} onOpenLibraryDetail={(entry) => { runProtectedAction(() => { openLibraryDetail(entry); }); }} onOpenProfileDashboardCard={() => { if (dashboardError) { void refreshProfileDashboard(); } }} /> )} {selectionStage === 'detail' && ( {isDetailLoading || !selectedDetailEntry ? (
{detailError || '正在读取作品详情...'}
) : ( { setDetailError(null); setSelectionStage('platform'); }} onStartGame={handleStartSelectedWorld} onContinueEdit={ isSelectedWorldOwned ? () => { runProtectedAction(() => { openSavedCustomWorldEditor(selectedDetailEntry); }); } : null } onPublish={ selectedDetailEntry.visibility === 'draft' && isSelectedWorldOwned ? () => { runProtectedAction(() => { void handlePublishSelectedWorld(); }); } : null } onUnpublish={ selectedDetailEntry.visibility === 'published' && isSelectedWorldOwned ? () => { runProtectedAction(() => { void handleUnpublishSelectedWorld(); }); } : null } onDelete={ isSelectedWorldOwned ? () => { runProtectedAction(() => { void handleDeleteSelectedWorld(); }); } : null } /> )}
)} {selectionStage === 'agent-workspace' && ( } > {agentSession ? ( { void submitAgentMessage(payload); }} onExecuteAction={(payload) => { void executeAgentAction(payload); }} /> ) : (
{isLoadingAgentSession ? '正在准备 Agent 共创工作区...' : creationTypeError || '正在恢复创作工作区...'}
)}
)} {selectionStage === 'custom-world-generating' && ( } > )} {selectionStage === 'custom-world-result' && generatedCustomWorldProfile && ( } > { setGeneratedCustomWorldProfile( normalizeAgentBackedProfile(profile), ); }} onBack={ isAgentDraftResultView ? leaveAgentDraftResult : leaveCustomWorldResult } onEditSetting={undefined} onRegenerate={undefined} onContinueExpand={undefined} onEnterWorld={() => { runProtectedAction(() => { handleCustomWorldSelect(generatedCustomWorldProfile); }); }} readOnly={false} backLabel={isAgentDraftResultView ? '返回创作' : undefined} editActionLabel="去Agent调整设定" enterWorldActionLabel="进入世界" autoSaveState={customWorldAutoSaveState} /> )}
{ if (isCreatingAgentSession) { return; } setShowCreationTypeModal(false); }} onSelectRpg={() => { runProtectedAction(() => { void openRpgAgentWorkspace(); }); }} /> ); }