719 lines
27 KiB
TypeScript
719 lines
27 KiB
TypeScript
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 (
|
|
<div className="flex h-full min-h-0 items-center justify-center">
|
|
<div className="platform-subpanel rounded-2xl px-5 py-4 text-sm text-[var(--platform-text-base)]">
|
|
{label}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function RpgEntryFlowShellImpl({
|
|
selectionStage,
|
|
setSelectionStage,
|
|
hasSavedGame,
|
|
savedSnapshot,
|
|
handleContinueGame,
|
|
handleStartNewGame,
|
|
handleCustomWorldSelect,
|
|
}: RpgEntryFlowShellProps) {
|
|
const authUi = useAuthUi();
|
|
const [showCreationTypeModal, setShowCreationTypeModal] = useState(false);
|
|
const [selectedDetailEntry, setSelectedDetailEntry] = useState<
|
|
CustomWorldLibraryEntry<CustomWorldProfile> | 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 = (
|
|
<CustomWorldCreationHub
|
|
items={creationHubItems}
|
|
loading={platformBootstrap.isLoadingPlatform}
|
|
error={
|
|
platformBootstrap.isLoadingPlatform
|
|
? null
|
|
: platformBootstrap.platformError ?? sessionController.creationTypeError
|
|
}
|
|
onBack={() => {
|
|
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 (
|
|
<>
|
|
<AnimatePresence mode="wait">
|
|
{selectionStage === 'platform' && (
|
|
<motion.div
|
|
key="platform-home"
|
|
initial={{ opacity: 0, y: 12 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -12 }}
|
|
className="flex h-full min-h-0 flex-col"
|
|
>
|
|
<RpgEntryHomeView
|
|
activeTab={platformBootstrap.platformTab}
|
|
onTabChange={platformBootstrap.setPlatformTab}
|
|
hasSavedGame={hasSavedGame}
|
|
savedSnapshot={savedSnapshot}
|
|
saveEntries={platformBootstrap.saveEntries}
|
|
saveError={platformBootstrap.saveError}
|
|
featuredEntries={featuredGalleryEntries}
|
|
latestEntries={platformBootstrap.publishedGalleryEntries}
|
|
myEntries={platformBootstrap.savedCustomWorldEntries}
|
|
historyEntries={platformBootstrap.historyEntries}
|
|
profileDashboard={platformBootstrap.profileDashboard}
|
|
isLoadingPlatform={platformBootstrap.isLoadingPlatform}
|
|
isLoadingDashboard={platformBootstrap.isLoadingDashboard}
|
|
isResumingSaveWorldKey={platformBootstrap.isResumingSaveWorldKey}
|
|
platformError={
|
|
platformBootstrap.isLoadingPlatform
|
|
? null
|
|
: platformBootstrap.platformError ??
|
|
sessionController.creationTypeError
|
|
}
|
|
dashboardError={
|
|
platformBootstrap.isLoadingDashboard
|
|
? null
|
|
: platformBootstrap.dashboardError
|
|
}
|
|
createTabContent={creationHubContent}
|
|
onContinueGame={handleContinueGame}
|
|
onResumeSave={(entry) => {
|
|
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();
|
|
}
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
)}
|
|
|
|
{selectionStage === 'detail' && (
|
|
<motion.div
|
|
key="platform-detail"
|
|
initial={{ opacity: 0, y: 12 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -12 }}
|
|
className="flex h-full min-h-0 flex-col"
|
|
>
|
|
{detailNavigation.isDetailLoading || !selectedDetailEntry ? (
|
|
<div className="flex h-full items-center justify-center">
|
|
<div className="platform-subpanel rounded-2xl px-5 py-4 text-sm text-[var(--platform-text-base)]">
|
|
{detailNavigation.detailError || '正在读取作品详情...'}
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<RpgEntryWorldDetailView
|
|
entry={selectedDetailEntry}
|
|
isMutating={detailNavigation.isMutatingDetail}
|
|
error={detailNavigation.detailError}
|
|
onBack={() => {
|
|
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
|
|
}
|
|
/>
|
|
)}
|
|
</motion.div>
|
|
)}
|
|
|
|
{selectionStage === 'agent-workspace' && (
|
|
<motion.div
|
|
key="agent-workspace"
|
|
initial={{ opacity: 0, y: 12 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -12 }}
|
|
className="flex h-full min-h-0 flex-col"
|
|
>
|
|
<Suspense
|
|
fallback={
|
|
<LazyPanelFallback label="正在加载 Agent 共创工作区..." />
|
|
}
|
|
>
|
|
{sessionController.agentSession ? (
|
|
<CustomWorldAgentWorkspace
|
|
session={sessionController.agentSession}
|
|
activeOperation={sessionController.agentOperation}
|
|
streamingReplyText={sessionController.streamingAgentReplyText}
|
|
isStreamingReply={sessionController.isStreamingAgentReply}
|
|
onBack={leaveAgentWorkspace}
|
|
onSubmitMessage={(payload) => {
|
|
void sessionController.submitAgentMessage(payload);
|
|
}}
|
|
onExecuteAction={(payload) => {
|
|
void sessionController.executeAgentAction(payload);
|
|
}}
|
|
/>
|
|
) : (
|
|
<div className="flex h-full items-center justify-center">
|
|
<div className="platform-subpanel rounded-2xl px-5 py-4 text-sm text-[var(--platform-text-base)]">
|
|
{sessionController.isLoadingAgentSession
|
|
? '正在准备 Agent 共创工作区...'
|
|
: sessionController.creationTypeError || '正在恢复创作工作区...'}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Suspense>
|
|
</motion.div>
|
|
)}
|
|
|
|
{selectionStage === 'custom-world-generating' && (
|
|
<motion.div
|
|
key="custom-world-generating"
|
|
initial={{ opacity: 0, y: 12 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -12 }}
|
|
className="flex h-full min-h-0 flex-col"
|
|
>
|
|
<Suspense
|
|
fallback={<LazyPanelFallback label="正在加载世界生成面板..." />}
|
|
>
|
|
<CustomWorldGenerationView
|
|
settingText={sessionController.agentDraftSettingPreview}
|
|
anchorEntries={sessionController.agentDraftAnchorPreviewEntries}
|
|
progress={sessionController.agentDraftGenerationProgress}
|
|
isGenerating={sessionController.isActiveGenerationRunning}
|
|
error={sessionController.activeGenerationError}
|
|
onBack={leaveAgentDraftGeneration}
|
|
onEditSetting={leaveAgentDraftGeneration}
|
|
onRetry={() => {
|
|
void sessionController.executeAgentAction({
|
|
action: 'draft_foundation',
|
|
});
|
|
}}
|
|
onInterrupt={undefined}
|
|
backLabel="返回工作区"
|
|
settingActionLabel={null}
|
|
retryLabel="重新生成草稿"
|
|
settingTitle="当前世界信息"
|
|
settingDescription={null}
|
|
progressTitle="世界草稿生成进度"
|
|
activeBadgeLabel="草稿编译中"
|
|
pausedBadgeLabel="草稿生成已暂停"
|
|
idleBadgeLabel="等待返回工作区"
|
|
/>
|
|
</Suspense>
|
|
</motion.div>
|
|
)}
|
|
|
|
{selectionStage === 'custom-world-result' &&
|
|
sessionController.generatedCustomWorldProfile && (
|
|
<motion.div
|
|
key="custom-world-result"
|
|
initial={{ opacity: 0, y: 12 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -12 }}
|
|
className="flex h-full min-h-0 flex-col"
|
|
>
|
|
<Suspense
|
|
fallback={<LazyPanelFallback label="正在加载世界编辑器..." />}
|
|
>
|
|
<RpgCreationResultView
|
|
profile={sessionController.generatedCustomWorldProfile}
|
|
previewCharacters={previewCustomWorldCharacters}
|
|
isGenerating={false}
|
|
progress={0}
|
|
progressLabel=""
|
|
error={resultViewError}
|
|
onProfileChange={(profile) => {
|
|
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}
|
|
/>
|
|
</Suspense>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
|
|
<RpgEntryCreationTypeModal
|
|
isOpen={showCreationTypeModal}
|
|
isBusy={sessionController.isCreatingAgentSession}
|
|
error={sessionController.creationTypeError}
|
|
onClose={() => {
|
|
if (sessionController.isCreatingAgentSession) {
|
|
return;
|
|
}
|
|
setShowCreationTypeModal(false);
|
|
}}
|
|
onSelectRpg={() => {
|
|
runProtectedAction(() => {
|
|
void sessionController.openRpgAgentWorkspace();
|
|
});
|
|
}}
|
|
/>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export const RpgCreationShellImpl = RpgEntryFlowShellImpl;
|
|
|
|
export default RpgEntryFlowShellImpl;
|