@@ -1,4 +1,5 @@
import { AnimatePresence , motion } from 'motion/ react' ;
import { Loader2 } from 'lucide- react' ;
import { AnimatePresence , motion } from 'motion/react' ;
import {
lazy ,
Suspense ,
@@ -9,6 +10,7 @@ import {
useState ,
} from 'react' ;
import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth' ;
import type {
BigFishRuntimeSnapshotResponse ,
BigFishSessionSnapshotResponse ,
@@ -31,7 +33,6 @@ import type {
CustomWorldGalleryCard ,
CustomWorldLibraryEntry ,
} from '../../../packages/shared/src/contracts/runtime' ;
import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth' ;
import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets' ;
import {
getPublicAuthUserByCode ,
@@ -43,15 +44,22 @@ import {
getBigFishCreationSession ,
streamBigFishCreationMessage ,
} from '../../services/big-fish-creation' ;
import {
deleteBigFishWork ,
listBigFishWorks ,
} from '../../services/big-fish-works' ;
import {
startBigFishRuntimeRun ,
submitBigFishRuntimeInput ,
} from '../../services/big-fish-runtime' ;
import {
deleteBigFishWork ,
listBigFishWorks ,
} from '../../services/big-fish-works' ;
import { readCustomWorldAgentUiState } from '../../services/customWorldAgentUiState' ;
import {
buildBigFishGenerationAnchorEntries ,
buildMiniGameDraftGenerationProgress ,
buildPuzzleGenerationAnchorEntries ,
createMiniGameDraftGenerationState ,
type MiniGameDraftGenerationState ,
} from '../../services/miniGameDraftGenerationProgress' ;
import { getPlatformProfileDashboard } from '../../services/platform-entry' ;
import {
createPuzzleAgentSession ,
@@ -60,17 +68,17 @@ import {
streamPuzzleAgentMessage ,
} from '../../services/puzzle-agent' ;
import { getPuzzleGalleryDetail , listPuzzleGallery } from '../../services/puzzle-gallery' ;
import { advanceLocalPuzzleNextLevel } from '../../services/puzzle-runtime' ;
import {
advanceLocalPuzzleLevel ,
dragLocalPuzzlePiece ,
startLocalPuzzleRun ,
swapLocalPuzzlePieces ,
} from '../../services/puzzle-runtime/puzzleLocalRuntime' ;
import { deletePuzzleWork , listPuzzleWorks } from '../../services/puzzle-works' ;
import { deleteRpgEntryWorldProfile } from '../../services/rpg-entry' ;
import { getRpgEntryWorldGalleryDetailByCode } from '../../services/rpg-entry/rpgEntryLibraryClient' ;
import { deleteRpgCreationAgentSession } from '../../services/rpg-creation' ;
import { rpgCreationPreviewAdapter } from '../../services/rpg-creation/rpgCreationPreviewAdapter' ;
import { deleteRpgEntryWorldProfile } from '../../services/rpg-entry' ;
import { getRpgEntryWorldGalleryDetailByCode } from '../../services/rpg-entry/rpgEntryLibraryClient' ;
import type { CustomWorldProfile } from '../../types' ;
import { useAuthUi } from '../auth/AuthUiContext' ;
import { CustomWorldCreationHub } from '../custom-world-home/CustomWorldCreationHub' ;
@@ -88,13 +96,13 @@ import {
type PlatformPublicGalleryCard ,
} from '../rpg-entry/rpgEntryWorldPresentation' ;
import { PlatformEntryCreationTypeModal } from './PlatformEntryCreationTypeModal' ;
import type { PlatformCreationTypeId } from './platformEntryCreationTypes' ;
import { PlatformEntryHomeView } from './PlatformEntryHomeView' ;
import {
buildCreationHubFallbackItems ,
normalizeAgentBackedProfile ,
resolveRpgCreationErrorMessage ,
} from './platformEntryShared' ;
import type { PlatformCreationTypeId } from './platformEntryCreationTypes' ;
import type { PlatformEntryFlowShellProps } from './platformEntryTypes' ;
import { PlatformEntryWorldDetailView } from './PlatformEntryWorldDetailView' ;
import { usePlatformEntryBootstrap } from './usePlatformEntryBootstrap' ;
@@ -341,6 +349,8 @@ export function PlatformEntryFlowShellImpl({
const [ bigFishRun , setBigFishRun ] =
useState < BigFishRuntimeSnapshotResponse | null > ( null ) ;
const [ isBigFishLoadingLibrary , setIsBigFishLoadingLibrary ] = useState ( false ) ;
const [ bigFishGenerationState , setBigFishGenerationState ] =
useState < MiniGameDraftGenerationState | null > ( null ) ;
const bigFishInputInFlightRef = useRef ( false ) ;
const [ puzzleOperation , setPuzzleOperation ] =
useState < PuzzleAgentOperationRecord | null > ( null ) ;
@@ -352,6 +362,10 @@ export function PlatformEntryFlowShellImpl({
useState < PuzzleWorkSummary | null > ( null ) ;
const [ puzzleRun , setPuzzleRun ] = useState < PuzzleRunSnapshot | null > ( null ) ;
const [ isPuzzleLoadingLibrary , setIsPuzzleLoadingLibrary ] = useState ( false ) ;
const [ puzzleGenerationState , setPuzzleGenerationState ] =
useState < MiniGameDraftGenerationState | null > ( null ) ;
const [ isPuzzleNextLevelGenerating , setIsPuzzleNextLevelGenerating ] =
useState ( false ) ;
const [ isSearchingPublicCode , setIsSearchingPublicCode ] = useState ( false ) ;
const [ publicSearchError , setPublicSearchError ] = useState < string | null > ( null ) ;
const [ searchedPublicUser , setSearchedPublicUser ] =
@@ -773,8 +787,44 @@ export function PlatformEntryFlowShellImpl({
onSessionOpened : ( ) = > {
setShowCreationTypeModal ( false ) ;
} ,
onActionComplete : ( { response , setSession } ) = > {
onActionComplete : ( { payload , response, setSession } ) = > {
setSession ( response . session ) ;
if ( payload . action !== 'big_fish_compile_draft' ) {
return ;
}
setBigFishGenerationState ( ( current ) = >
current
? {
. . . current ,
phase : 'ready' ,
completedAssetCount : response.session.assetSlots.filter (
( slot ) = > slot . status === 'ready' ,
) . length ,
totalAssetCount : response.session.assetSlots.length ,
}
: current ,
) ;
} ,
beforeExecuteAction : ( { payload } ) = > {
if ( payload . action !== 'big_fish_compile_draft' ) {
return ;
}
setSelectionStage ( 'big-fish-generating' ) ;
setBigFishGenerationState ( createMiniGameDraftGenerationState ( 'big-fish' ) ) ;
} ,
onActionError : ( { payload , errorMessage } ) = > {
if ( payload . action !== 'big_fish_compile_draft' ) {
return ;
}
setBigFishGenerationState ( ( current ) = >
current
? {
. . . current ,
phase : 'failed' ,
error : errorMessage ,
}
: current ,
) ;
} ,
} ) ;
@@ -784,7 +834,7 @@ export function PlatformEntryFlowShellImpl({
{ session : PuzzleAgentSessionSnapshot } ,
SendPuzzleAgentMessageRequest ,
PuzzleAgentActionRequest ,
{ operation : PuzzleAgentOperationRecord }
{ operation : PuzzleAgentOperationRecord ; session : PuzzleAgentSessionSnapshot }
> ( {
client : {
createSession : createPuzzleAgentSession ,
@@ -811,8 +861,9 @@ export function PlatformEntryFlowShellImpl({
onSessionOpened : ( ) = > {
setShowCreationTypeModal ( false ) ;
} ,
onActionComplete : async ( { payload , response , session , setSession } ) = > {
onActionComplete : async ( { payload , response , setSession } ) = > {
setPuzzleOperation ( response . operation ) ;
setSession ( response . session ) ;
if ( payload . action === 'publish_puzzle_work' ) {
await Promise . allSettled ( [
@@ -821,21 +872,51 @@ export function PlatformEntryFlowShellImpl({
] ) ;
}
const latestResponse = await getPuzzleAgentSession ( session . sessionId ) ;
const latestSession = latestResponse . session ;
setSession ( latestSession ) ;
if ( payload . action === 'compile_puzzle_draft' ) {
setPuzzleGenerationState ( ( current ) = >
current
? {
. . . current ,
phase : 'ready' ,
completedAssetCount : 1 ,
totalAssetCount : 1 ,
}
: current ,
) ;
}
if (
payload . action === 'publish_puzzle_work' &&
latestS ession. publishedProfileId
response . s ession. publishedProfileId
) {
const galleryDetail = await getPuzzleGalleryDetail (
latestS ession. publishedProfileId ,
response . s ession. publishedProfileId ,
) ;
setSelectedPuzzleDetail ( galleryDetail . item ) ;
setSelectionStage ( 'puzzle-gallery-detail' ) ;
}
} ,
beforeExecuteAction : ( { payload } ) = > {
if ( payload . action !== 'compile_puzzle_draft' ) {
return ;
}
setSelectionStage ( 'puzzle-generating' ) ;
setPuzzleGenerationState ( createMiniGameDraftGenerationState ( 'puzzle' ) ) ;
} ,
onActionError : ( { payload , errorMessage } ) = > {
if ( payload . action !== 'compile_puzzle_draft' ) {
return ;
}
setPuzzleGenerationState ( ( current ) = >
current
? {
. . . current ,
phase : 'failed' ,
error : errorMessage ,
}
: current ,
) ;
} ,
} ) ;
const bigFishSession = bigFishFlow . session ;
@@ -906,12 +987,15 @@ export function PlatformEntryFlowShellImpl({
const leaveBigFishFlow = useCallback ( ( ) = > {
setBigFishRun ( null ) ;
setBigFishGenerationState ( null ) ;
bigFishFlow . leaveFlow ( ) ;
} , [ bigFishFlow ] ) ;
const leavePuzzleFlow = useCallback ( ( ) = > {
setPuzzleOperation ( null ) ;
setPuzzleRun ( null ) ;
setPuzzleGenerationState ( null ) ;
setIsPuzzleNextLevelGenerating ( false ) ;
puzzleFlow . leaveFlow ( ) ;
} , [ puzzleFlow ] ) ;
@@ -1039,9 +1123,35 @@ export function PlatformEntryFlowShellImpl({
return ;
}
const currentLevel = puzzleRun . currentLevel ;
if ( ! currentLevel || currentLevel . status !== 'cleared' ) {
return ;
}
setIsPuzzleBusy ( true ) ;
setIsPuzzleNextLevelGenerating ( true ) ;
setPuzzleError ( null ) ;
setPuzzleRun ( advanceLocalPuzzleLevel ( puzzleRun ) ) ;
} , [ isPuzzleBusy , puzzleRun ] ) ;
try {
const { run } = await advanceLocalPuzzleNextLevel ( {
run : puzzleRun ,
sourceSessionId :
selectedPuzzleDetail?.sourceSessionId ? ? puzzleSession ? . sessionId ? ? null ,
} ) ;
setPuzzleRun ( run ) ;
} catch ( error ) {
setPuzzleError ( resolvePuzzleErrorMessage ( error , '准备下一关失败。' ) ) ;
} finally {
setIsPuzzleNextLevelGenerating ( false ) ;
setIsPuzzleBusy ( false ) ;
}
} , [
isPuzzleBusy ,
puzzleRun ,
puzzleSession ,
resolvePuzzleErrorMessage ,
selectedPuzzleDetail ,
] ) ;
const leaveAgentWorkspace = useCallback ( ( ) = > {
enterCreateTab ( ) ;
@@ -1713,6 +1823,49 @@ export function PlatformEntryFlowShellImpl({
< / motion.div >
) }
{ selectionStage === 'big-fish-generating' && (
< motion.div
key = "big-fish-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 = {
bigFishSession ? . lastAssistantReply ? ? '正在整理当前玩法草稿。'
}
anchorEntries = { buildBigFishGenerationAnchorEntries ( bigFishSession ) }
progress = { buildMiniGameDraftGenerationProgress (
bigFishGenerationState ,
) }
isGenerating = { isBigFishBusy }
error = { bigFishError }
onBack = { leaveBigFishFlow }
onEditSetting = { ( ) = > {
setSelectionStage ( 'big-fish-agent-workspace' ) ;
} }
onRetry = { ( ) = > {
void executeBigFishAction ( { action : 'big_fish_compile_draft' } ) ;
} }
onInterrupt = { undefined }
backLabel = "返回创作中心"
settingActionLabel = { null }
retryLabel = "重新生成草稿"
settingTitle = "当前玩法信息"
settingDescription = { null }
progressTitle = "大鱼吃小鱼草稿生成进度"
activeBadgeLabel = "草稿生成中"
pausedBadgeLabel = "草稿生成已暂停"
idleBadgeLabel = "等待返回工作区"
/ >
< / Suspense >
< / motion.div >
) }
{ selectionStage === 'big-fish-result' && bigFishSession ? . draft && (
< motion.div
key = "big-fish-result"
@@ -1796,6 +1949,49 @@ export function PlatformEntryFlowShellImpl({
< / motion.div >
) }
{ selectionStage === 'puzzle-generating' && (
< motion.div
key = "puzzle-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 = {
puzzleSession ? . lastAssistantReply ? ? '正在整理当前拼图草稿。'
}
anchorEntries = { buildPuzzleGenerationAnchorEntries ( puzzleSession ) }
progress = { buildMiniGameDraftGenerationProgress (
puzzleGenerationState ,
) }
isGenerating = { isPuzzleBusy }
error = { puzzleError }
onBack = { leavePuzzleFlow }
onEditSetting = { ( ) = > {
setSelectionStage ( 'puzzle-agent-workspace' ) ;
} }
onRetry = { ( ) = > {
void executePuzzleAction ( { action : 'compile_puzzle_draft' } ) ;
} }
onInterrupt = { undefined }
backLabel = "返回创作中心"
settingActionLabel = { null }
retryLabel = "重新生成草稿"
settingTitle = "当前拼图信息"
settingDescription = { null }
progressTitle = "拼图草稿生成进度"
activeBadgeLabel = "草稿生成中"
pausedBadgeLabel = "草稿生成已暂停"
idleBadgeLabel = "等待返回工作区"
/ >
< / Suspense >
< / motion.div >
) }
{ selectionStage === 'puzzle-result' && puzzleSession ? . draft && (
< motion.div
key = "puzzle-result"
@@ -1852,7 +2048,7 @@ export function PlatformEntryFlowShellImpl({
>
< PuzzleRuntimeShell
run = { puzzleRun }
isBusy = { isPuzzleBusy }
isBusy = { isPuzzleBusy || isPuzzleNextLevelGenerating }
error = { puzzleError }
onBack = { ( ) = > {
setSelectionStage ( 'puzzle-gallery-detail' ) ;
@@ -1867,6 +2063,17 @@ export function PlatformEntryFlowShellImpl({
void advancePuzzleLevel ( ) ;
} }
/ >
{ isPuzzleNextLevelGenerating ? (
< div className = "fixed inset-0 z-[120] flex items-center justify-center bg-slate-950/62 px-5 backdrop-blur-sm" >
< div className = "flex max-w-[18rem] flex-col items-center gap-3 rounded-[1.5rem] border border-white/12 bg-slate-950/92 px-6 py-5 text-center text-white shadow-[0_28px_80px_rgba(0,0,0,0.35)]" >
< Loader2 className = "h-6 w-6 animate-spin text-amber-200" / >
< div className = "text-sm font-bold" > 正 在 准 备 下 一 关 < / div >
< div className = "text-xs leading-5 text-white/68" >
广 场 暂 无 可 接 续 作 品 , 正 在 生 成 新 的 候 选 图 。
< / div >
< / div >
< / div >
) : null }
< / motion.div >
) }