This commit is contained in:
2026-05-10 22:20:54 +08:00
parent d6219f1a0c
commit 192accd796
92 changed files with 7045 additions and 1559 deletions

View File

@@ -154,6 +154,7 @@ import {
} from '../../services/match3d-works';
import {
buildBigFishGenerationAnchorEntries,
buildMatch3DGenerationAnchorEntries,
buildMiniGameDraftGenerationProgress,
buildPuzzleGenerationAnchorEntries,
buildSquareHoleGenerationAnchorEntries,
@@ -284,9 +285,13 @@ import { useRpgCreationAgentOperationPolling } from '../rpg-entry/useRpgCreation
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 { PlatformEntryCreationTypeModal } from './PlatformEntryCreationTypeModal';
import { PlatformFeedbackView } from './PlatformFeedbackView';
import type { PlatformCreationTypeId } from './platformEntryCreationTypes';
import {
getVisiblePlatformCreationTypes,
@@ -302,6 +307,7 @@ import {
} 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';
@@ -349,6 +355,7 @@ type VisualNovelRuntimeReturnStage =
| 'visual-novel-gallery-detail'
| 'work-detail'
| 'platform';
type VisualNovelEntryGenerationPhase = 'generating' | 'ready' | 'failed';
type PuzzleSaveArchiveState = {
runtimeKind?: unknown;
@@ -591,6 +598,7 @@ function buildMatch3DProfileFromSession(
updatedAt: now,
publishedAt: null,
publishReady: Boolean(draft.publishReady),
generatedItemAssets: draft.generatedItemAssets,
};
}
@@ -1513,6 +1521,10 @@ export function PlatformEntryFlowShellImpl({
const [match3dRuntimeReturnStage, setMatch3DRuntimeReturnStage] = useState<
'match3d-result' | 'work-detail'
>('match3d-result');
const [match3dFormDraftPayload, setMatch3DFormDraftPayload] =
useState<CreateMatch3DSessionRequest | null>(null);
const [match3dGenerationState, setMatch3DGenerationState] =
useState<MiniGameDraftGenerationState | null>(null);
const [isMatch3DLoadingLibrary, setIsMatch3DLoadingLibrary] = useState(false);
const [squareHoleWorks, setSquareHoleWorks] = useState<
SquareHoleWorkSummary[]
@@ -1597,10 +1609,12 @@ export function PlatformEntryFlowShellImpl({
);
const [puzzleGenerationState, setPuzzleGenerationState] =
useState<MiniGameDraftGenerationState | null>(null);
const [puzzleGenerationProgressNowMs, setPuzzleGenerationProgressNowMs] =
const [miniGameGenerationProgressNowMs, setMiniGameGenerationProgressNowMs] =
useState(() => Date.now());
const [puzzleFormDraftPayload, setPuzzleFormDraftPayload] =
useState<CreatePuzzleAgentSessionRequest | null>(null);
const [activeCreationFormType, setActiveCreationFormType] =
useState<PlatformCreationTypeId>('puzzle');
const [puzzleOnboardingPrompt, setPuzzleOnboardingPrompt] = useState('');
const [puzzleOnboardingPhase, setPuzzleOnboardingPhase] =
useState<PuzzleOnboardingPhase>('input');
@@ -1640,6 +1654,12 @@ export function PlatformEntryFlowShellImpl({
useState<VisualNovelRunSnapshot | null>(null);
const [visualNovelRuntimeReturnStage, setVisualNovelRuntimeReturnStage] =
useState<VisualNovelRuntimeReturnStage>('visual-novel-result');
const [visualNovelFormDraftPayload, setVisualNovelFormDraftPayload] =
useState<VisualNovelEntryFormPayload | null>(null);
const [visualNovelGenerationStartedAtMs, setVisualNovelGenerationStartedAtMs] =
useState<number | null>(null);
const [visualNovelGenerationPhase, setVisualNovelGenerationPhase] =
useState<VisualNovelEntryGenerationPhase>('generating');
const [isVisualNovelLoadingLibrary, setIsVisualNovelLoadingLibrary] =
useState(false);
const [isPuzzleNextLevelGenerating, setIsPuzzleNextLevelGenerating] =
@@ -2191,23 +2211,38 @@ export function PlatformEntryFlowShellImpl({
]);
useEffect(() => {
const shouldTickPuzzleProgress =
selectionStage === 'puzzle-generating' &&
puzzleGenerationState != null &&
puzzleGenerationState.phase !== 'ready' &&
puzzleGenerationState.phase !== 'failed';
const activeGenerationState =
selectionStage === 'puzzle-generating'
? puzzleGenerationState
: selectionStage === 'match3d-generating'
? match3dGenerationState
: null;
const shouldTickProgress =
selectionStage === 'visual-novel-generating'
? visualNovelGenerationStartedAtMs != null &&
visualNovelGenerationPhase !== 'ready' &&
visualNovelGenerationPhase !== 'failed'
: activeGenerationState != null &&
activeGenerationState.phase !== 'ready' &&
activeGenerationState.phase !== 'failed';
if (!shouldTickPuzzleProgress) {
if (!shouldTickProgress) {
return undefined;
}
setPuzzleGenerationProgressNowMs(Date.now());
setMiniGameGenerationProgressNowMs(Date.now());
const timerId = window.setInterval(() => {
setPuzzleGenerationProgressNowMs(Date.now());
setMiniGameGenerationProgressNowMs(Date.now());
}, 500);
return () => window.clearInterval(timerId);
}, [puzzleGenerationState, selectionStage]);
}, [
match3dGenerationState,
puzzleGenerationState,
selectionStage,
visualNovelGenerationPhase,
visualNovelGenerationStartedAtMs,
]);
const runProtectedAction = useCallback(
(action: () => void) => {
@@ -2514,6 +2549,7 @@ export function PlatformEntryFlowShellImpl({
enterCreateTab,
setSelectionStage,
onSessionOpened: () => {
setActiveCreationFormType('match3d');
setShowCreationTypeModal(false);
},
onActionComplete: async ({ payload, response, setSession }) => {
@@ -2521,6 +2557,18 @@ export function PlatformEntryFlowShellImpl({
if (payload.action !== 'match3d_compile_draft') {
return;
}
setMatch3DGenerationState((current) =>
current
? {
...current,
phase: 'ready',
completedAssetCount:
response.session.draft?.generatedItemAssets?.length ?? 3,
totalAssetCount:
response.session.draft?.generatedItemAssets?.length ?? 3,
}
: current,
);
const profileId = response.session.draft?.profileId;
if (!profileId) {
@@ -2530,12 +2578,38 @@ export function PlatformEntryFlowShellImpl({
try {
const { item } = await getMatch3DWorkDetail(profileId);
setMatch3DProfile(item);
setMatch3DProfile({
...item,
generatedItemAssets:
response.session.draft?.generatedItemAssets ??
item.generatedItemAssets,
});
await refreshMatch3DShelf().catch(() => undefined);
} catch {
setMatch3DProfile(buildMatch3DProfileFromSession(response.session));
}
},
beforeExecuteAction: ({ payload }) => {
if (payload.action !== 'match3d_compile_draft') {
return;
}
setSelectionStage('match3d-generating');
setMatch3DGenerationState(createMiniGameDraftGenerationState('match3d'));
},
onActionError: ({ payload, errorMessage }) => {
if (payload.action !== 'match3d_compile_draft') {
return;
}
setMatch3DGenerationState((current) =>
current
? {
...current,
phase: 'failed',
error: errorMessage,
}
: current,
);
},
});
const squareHoleFlow = usePlatformCreationAgentFlowController<
@@ -2723,6 +2797,7 @@ export function PlatformEntryFlowShellImpl({
enterCreateTab,
setSelectionStage,
onSessionOpened: () => {
setActiveCreationFormType('puzzle');
sessionController.setCreationTypeError(null);
setPuzzleCreationError(null);
setShowCreationTypeModal(false);
@@ -2873,7 +2948,6 @@ export function PlatformEntryFlowShellImpl({
const setMatch3DError = match3dFlow.setError;
match3DErrorSetterRef.current = setMatch3DError;
const isMatch3DBusy = match3dFlow.isBusy;
const streamingMatch3DReplyText = match3dFlow.streamingReplyText;
const setStreamingMatch3DReplyText = match3dFlow.setStreamingReplyText;
const isStreamingMatch3DReply = match3dFlow.isStreamingReply;
const setIsStreamingMatch3DReply = match3dFlow.setIsStreamingReply;
@@ -2903,7 +2977,6 @@ export function PlatformEntryFlowShellImpl({
visualNovelErrorSetterRef.current = setVisualNovelError;
const isVisualNovelBusy = visualNovelFlow.isBusy;
const setIsVisualNovelBusy = visualNovelFlow.setIsBusy;
const visualNovelStreamingReplyText = visualNovelFlow.streamingReplyText;
const isVisualNovelStreamingReply = visualNovelFlow.isStreamingReply;
const resetRpgSessionViewState = sessionController.resetSessionViewState;
const setRpgGeneratedCustomWorldProfile =
@@ -2922,24 +2995,6 @@ export function PlatformEntryFlowShellImpl({
await bigFishFlow.openWorkspace();
}, [bigFishFlow]);
const openMatch3DAgentWorkspace = useCallback(async () => {
setMatch3DSession(null);
setMatch3DProfile(null);
setMatch3DRun(null);
setMatch3DError(null);
setStreamingMatch3DReplyText('');
setIsStreamingMatch3DReply(false);
await match3dFlow.openWorkspace();
}, [
match3dFlow,
setIsStreamingMatch3DReply,
setMatch3DError,
setMatch3DProfile,
setMatch3DRun,
setMatch3DSession,
setStreamingMatch3DReplyText,
]);
const openSquareHoleAgentWorkspace = useCallback(async () => {
setSquareHoleSession(null);
setSquareHoleProfile(null);
@@ -2960,30 +3015,6 @@ export function PlatformEntryFlowShellImpl({
squareHoleFlow,
]);
const openPuzzleAgentWorkspace = useCallback(async () => {
setPuzzleRun(null);
setPuzzleRuntimeAuthMode('default');
setPuzzleOperation(null);
setPuzzleGenerationState(null);
setPuzzleFormDraftPayload(null);
sessionController.setCreationTypeError(null);
setPuzzleCreationError(null);
const nextSession = await puzzleFlow.openWorkspace({});
if (nextSession) {
void refreshPuzzleShelf();
}
}, [puzzleFlow, refreshPuzzleShelf, sessionController]);
const openVisualNovelAgentWorkspace = useCallback(() => {
setVisualNovelWork(null);
setVisualNovelRun(null);
setVisualNovelRuntimeReturnStage('visual-novel-result');
visualNovelFlow.resetTransientState();
enterCreateTab();
setShowCreationTypeModal(false);
setSelectionStage('visual-novel-agent-workspace');
}, [enterCreateTab, setSelectionStage, visualNovelFlow]);
const leaveCreativeAgentWorkspace = useCallback(() => {
const sessionId = creativeAgentSession?.sessionId?.trim();
if (sessionId && creativeAgentSession?.stage !== 'target_ready') {
@@ -3058,6 +3089,85 @@ export function PlatformEntryFlowShellImpl({
[puzzleFlow],
);
const createMatch3DDraftFromForm = useCallback(
async (payload: CreateMatch3DSessionRequest) => {
setMatch3DFormDraftPayload(payload);
setMatch3DGenerationState(null);
setMatch3DSession(null);
setMatch3DProfile(null);
setMatch3DRun(null);
setMatch3DError(null);
setStreamingMatch3DReplyText('');
setIsStreamingMatch3DReply(false);
const nextSession = await match3dFlow.openWorkspace(payload);
if (!nextSession) {
return;
}
await match3dFlow.executeAction(
{ action: 'match3d_compile_draft' },
nextSession,
);
},
[
match3dFlow,
setIsStreamingMatch3DReply,
setMatch3DError,
setMatch3DProfile,
setMatch3DRun,
setMatch3DSession,
setStreamingMatch3DReplyText,
],
);
const createVisualNovelDraftFromForm = useCallback(
async (payload: VisualNovelEntryFormPayload) => {
setVisualNovelFormDraftPayload(payload);
setVisualNovelGenerationStartedAtMs(Date.now());
setVisualNovelGenerationPhase('generating');
setVisualNovelWork(null);
setVisualNovelRun(null);
setVisualNovelRuntimeReturnStage('visual-novel-result');
setVisualNovelError(null);
setIsVisualNovelBusy(true);
setSelectionStage('visual-novel-generating');
try {
const createResponse = await createVisualNovelSession({
sourceMode: payload.sourceMode,
seedText: payload.seedText,
sourceAssetIds: payload.sourceAssetIds,
});
setVisualNovelSession(createResponse.session);
const nextSession = await streamVisualNovelMessage(
createResponse.session.sessionId,
{
clientMessageId: `visual-novel-entry-${Date.now().toString(36)}`,
text: payload.seedText,
},
);
setVisualNovelSession(nextSession);
setVisualNovelGenerationPhase('ready');
setSelectionStage('visual-novel-result');
} catch (error) {
setVisualNovelGenerationPhase('failed');
setVisualNovelError(
resolvePuzzleErrorMessage(error, '生成视觉小说草稿失败。'),
);
} finally {
setIsVisualNovelBusy(false);
}
},
[
resolvePuzzleErrorMessage,
setIsVisualNovelBusy,
setSelectionStage,
setVisualNovelError,
setVisualNovelSession,
],
);
const savePuzzleFormDraft = useCallback(
async (payload: CreatePuzzleAgentSessionRequest) => {
const session = puzzleFlow.session;
@@ -3118,6 +3228,8 @@ export function PlatformEntryFlowShellImpl({
setBigFishError(null);
setMatch3DSession(null);
setMatch3DProfile(null);
setMatch3DFormDraftPayload(null);
setActiveCreationFormType('puzzle');
setMatch3DWorks([]);
setMatch3DGalleryEntries([]);
setMatch3DRun(null);
@@ -3160,6 +3272,9 @@ export function PlatformEntryFlowShellImpl({
setVisualNovelGalleryEntries([]);
setVisualNovelRun(null);
setVisualNovelRuntimeReturnStage('visual-novel-result');
setVisualNovelFormDraftPayload(null);
setVisualNovelGenerationStartedAtMs(null);
setVisualNovelGenerationPhase('generating');
setVisualNovelError(null);
setDeletingCreationWorkId(null);
setClaimingPuzzlePointIncentiveProfileId(null);
@@ -3189,6 +3304,7 @@ export function PlatformEntryFlowShellImpl({
resetAutoSaveTrackingToIdle,
resetRpgSessionViewState,
selectionStage,
setActiveCreationFormType,
setBigFishError,
setIsStreamingMatch3DReply,
setIsStreamingSquareHoleReply,
@@ -3231,9 +3347,10 @@ export function PlatformEntryFlowShellImpl({
}
if (type === 'match3d') {
runProtectedAction(() => {
void openMatch3DAgentWorkspace();
});
enterCreateTab();
setShowCreationTypeModal(false);
setActiveCreationFormType('match3d');
setMatch3DError(null);
return;
}
@@ -3245,28 +3362,34 @@ export function PlatformEntryFlowShellImpl({
}
if (type === 'puzzle') {
runProtectedAction(() => {
void openPuzzleAgentWorkspace();
});
enterCreateTab();
setShowCreationTypeModal(false);
setActiveCreationFormType('puzzle');
setPuzzleCreationError(null);
setPuzzleError(null);
return;
}
if (type === 'visual-novel') {
runProtectedAction(() => {
openVisualNovelAgentWorkspace();
});
enterCreateTab();
setShowCreationTypeModal(false);
setActiveCreationFormType('visual-novel');
setVisualNovelError(null);
return;
}
},
[
openBigFishAgentWorkspace,
openMatch3DAgentWorkspace,
openPuzzleAgentWorkspace,
openSquareHoleAgentWorkspace,
openVisualNovelAgentWorkspace,
enterCreateTab,
openSquareHoleAgentWorkspace,
prepareCreationLaunch,
runProtectedAction,
sessionController,
setActiveCreationFormType,
setMatch3DError,
setPuzzleCreationError,
setPuzzleError,
setVisualNovelError,
],
);
@@ -3281,9 +3404,11 @@ export function PlatformEntryFlowShellImpl({
const leaveMatch3DFlow = useCallback(() => {
setMatch3DRun(null);
setMatch3DFormDraftPayload(null);
setMatch3DGenerationState(null);
setMatch3DRuntimeReturnStage('match3d-result');
match3dFlow.leaveFlow();
}, [match3dFlow]);
}, [match3dFlow, setMatch3DFormDraftPayload]);
const leaveSquareHoleFlow = useCallback(() => {
setSquareHoleRun(null);
@@ -3307,20 +3432,12 @@ export function PlatformEntryFlowShellImpl({
setVisualNovelWork(null);
setVisualNovelRun(null);
setVisualNovelRuntimeReturnStage('visual-novel-result');
setVisualNovelFormDraftPayload(null);
setVisualNovelGenerationStartedAtMs(null);
setVisualNovelGenerationPhase('generating');
visualNovelFlow.leaveFlow();
}, [visualNovelFlow]);
const openVisualNovelResult = useCallback(
(session: VisualNovelAgentSessionSnapshot) => {
setVisualNovelSession(session);
setVisualNovelWork(null);
setVisualNovelRun(null);
setVisualNovelRuntimeReturnStage('visual-novel-result');
setSelectionStage('visual-novel-result');
},
[setSelectionStage, setVisualNovelSession],
);
const saveVisualNovelDraft = useCallback(
async (draft: VisualNovelResultDraft) => {
const currentSession = visualNovelSession;
@@ -3716,8 +3833,6 @@ export function PlatformEntryFlowShellImpl({
const submitBigFishMessage = bigFishFlow.submitMessage;
const submitMatch3DMessage = match3dFlow.submitMessage;
const submitSquareHoleMessage = squareHoleFlow.submitMessage;
const submitPuzzleMessage = puzzleFlow.submitMessage;
@@ -3728,6 +3843,21 @@ export function PlatformEntryFlowShellImpl({
const executeSquareHoleAction = squareHoleFlow.executeAction;
const retryMatch3DDraftGeneration = useCallback(() => {
if (match3dFormDraftPayload) {
void createMatch3DDraftFromForm(match3dFormDraftPayload);
return;
}
void executeMatch3DAction({
action: 'match3d_compile_draft',
});
}, [
createMatch3DDraftFromForm,
executeMatch3DAction,
match3dFormDraftPayload,
]);
const retrySquareHoleAssetGeneration = useCallback(() => {
const session = squareHoleSession;
if (!session?.draft?.profileId) {
@@ -3744,6 +3874,35 @@ export function PlatformEntryFlowShellImpl({
const executePuzzleAction = puzzleFlow.executeAction;
const executePuzzleBackgroundAction = useCallback(
async (payload: PuzzleAgentActionRequest) => {
const targetSession = puzzleFlow.session;
if (!targetSession) {
return;
}
const formPayload = buildPuzzleFormPayloadFromAction(payload);
if (formPayload) {
setPuzzleFormDraftPayload(formPayload);
}
setPuzzleError(null);
try {
const response = await executePuzzleAgentAction(
targetSession.sessionId,
payload,
);
setPuzzleOperation(response.operation);
puzzleFlow.setSession(response.session);
} catch (error) {
setPuzzleError(
resolvePuzzleErrorMessage(error, '执行拼图操作失败。'),
);
}
},
[puzzleFlow, resolvePuzzleErrorMessage, setPuzzleError],
);
const retryPuzzleDraftGeneration = useCallback(() => {
if (puzzleFormDraftPayload) {
void createPuzzleDraftFromForm(puzzleFormDraftPayload);
@@ -3755,6 +3914,19 @@ export function PlatformEntryFlowShellImpl({
);
}, [createPuzzleDraftFromForm, executePuzzleAction, puzzleFormDraftPayload]);
const retryVisualNovelDraftGeneration = useCallback(() => {
if (!visualNovelFormDraftPayload) {
setSelectionStage('visual-novel-agent-workspace');
return;
}
void createVisualNovelDraftFromForm(visualNovelFormDraftPayload);
}, [
createVisualNovelDraftFromForm,
setSelectionStage,
visualNovelFormDraftPayload,
]);
const executePuzzleWorkspaceAction = useCallback(
(payload: PuzzleAgentActionRequest) => {
if (
@@ -3768,9 +3940,19 @@ export function PlatformEntryFlowShellImpl({
}
}
if (payload.action === 'generate_puzzle_images') {
void executePuzzleBackgroundAction(payload);
return;
}
void executePuzzleAction(payload);
},
[createPuzzleDraftFromForm, executePuzzleAction, puzzleFlow.session],
[
createPuzzleDraftFromForm,
executePuzzleAction,
executePuzzleBackgroundAction,
puzzleFlow.session,
],
);
const openCreativeAgentTarget = useCallback(async () => {
@@ -5098,6 +5280,7 @@ export function PlatformEntryFlowShellImpl({
: '删除后会从你的作品列表中移除。',
run: () => {
setDeletingCreationWorkId(work.workId);
setMatch3DFormDraftPayload(null);
setMatch3DError(null);
void deleteMatch3DWork(work.profileId)
@@ -5121,6 +5304,7 @@ export function PlatformEntryFlowShellImpl({
refreshMatch3DGallery,
requestDeleteCreationWork,
resolveMatch3DErrorMessage,
setMatch3DFormDraftPayload,
setMatch3DError,
],
);
@@ -5805,6 +5989,8 @@ export function PlatformEntryFlowShellImpl({
return;
}
setMatch3DFormDraftPayload(null);
try {
const { item: profile } = await getMatch3DWorkDetail(item.profileId);
setMatch3DProfile(profile);
@@ -5820,6 +6006,7 @@ export function PlatformEntryFlowShellImpl({
openPublicWorkDetail,
refreshMatch3DShelf,
resolveMatch3DErrorMessage,
setMatch3DFormDraftPayload,
setMatch3DError,
],
);
@@ -7424,7 +7611,7 @@ export function PlatformEntryFlowShellImpl({
aria-label="选择模板"
>
{getVisiblePlatformCreationTypes().map((item) => {
const selected = item.id === 'puzzle';
const selected = item.id === activeCreationFormType;
const disabled =
item.locked ||
sessionController.isCreatingAgentSession ||
@@ -7446,7 +7633,7 @@ export function PlatformEntryFlowShellImpl({
aria-selected={selected}
disabled={disabled}
onClick={() => {
if (item.id === 'puzzle') {
if (item.id === activeCreationFormType) {
return;
}
handleCreationHubCreateType(item.id);
@@ -7496,31 +7683,76 @@ export function PlatformEntryFlowShellImpl({
</div>
<div className="mt-3 min-h-0 flex-1 overflow-hidden">
<Suspense fallback={<LazyPanelFallback label="正在加载拼图创作..." />}>
<PuzzleAgentWorkspace
session={puzzleSession}
isBusy={isPuzzleBusy || isStreamingPuzzleReply}
error={puzzleError}
onBack={leavePuzzleFlow}
onSubmitMessage={(payload) => {
void submitPuzzleMessage(payload);
}}
onExecuteAction={(payload) => {
executePuzzleWorkspaceAction(payload);
}}
initialFormPayload={puzzleFormDraftPayload}
onCreateFromForm={(payload) => {
runProtectedAction(() => {
void createPuzzleDraftFromForm(payload);
});
}}
onAutoSaveForm={(payload) => {
void savePuzzleFormDraft(payload);
}}
showBackButton={false}
title={null}
/>
</Suspense>
{activeCreationFormType === 'match3d' ? (
<Suspense
fallback={
<LazyPanelFallback label="正在加载抓大鹅创作..." />
}
>
<Match3DAgentWorkspace
session={match3dSession}
isBusy={isMatch3DBusy || isStreamingMatch3DReply}
error={match3dError}
onBack={leaveMatch3DFlow}
onExecuteAction={(payload) => {
void executeMatch3DAction(payload);
}}
initialFormPayload={match3dFormDraftPayload}
onCreateFromForm={(payload) => {
runProtectedAction(() => {
void createMatch3DDraftFromForm(payload);
});
}}
showBackButton={false}
title={null}
/>
</Suspense>
) : activeCreationFormType === 'visual-novel' ? (
<Suspense
fallback={<LazyPanelFallback label="正在加载视觉小说创作..." />}
>
<VisualNovelAgentWorkspace
session={null}
isBusy={isVisualNovelBusy || isVisualNovelStreamingReply}
error={visualNovelError}
onBack={leaveVisualNovelFlow}
initialFormPayload={visualNovelFormDraftPayload}
onCreateFromForm={(payload) => {
runProtectedAction(() => {
void createVisualNovelDraftFromForm(payload);
});
}}
showBackButton={false}
title={null}
/>
</Suspense>
) : (
<Suspense fallback={<LazyPanelFallback label="正在加载拼图创作..." />}>
<PuzzleAgentWorkspace
session={puzzleSession}
isBusy={isPuzzleBusy || isStreamingPuzzleReply}
error={puzzleError}
onBack={leavePuzzleFlow}
onSubmitMessage={(payload) => {
void submitPuzzleMessage(payload);
}}
onExecuteAction={(payload) => {
executePuzzleWorkspaceAction(payload);
}}
initialFormPayload={puzzleFormDraftPayload}
onCreateFromForm={(payload) => {
runProtectedAction(() => {
void createPuzzleDraftFromForm(payload);
});
}}
onAutoSaveForm={(payload) => {
void savePuzzleFormDraft(payload);
}}
showBackButton={false}
title={null}
/>
</Suspense>
)}
</div>
</div>
</div>
@@ -7601,6 +7833,12 @@ export function PlatformEntryFlowShellImpl({
onSelectPreviousRecommendEntry={() =>
selectAdjacentRecommendRuntimeEntry(-1)
}
onLikeRecommendEntry={(entry) => {
likePublicWork(entry);
}}
onRemixRecommendEntry={(entry) => {
remixPublicWork(entry);
}}
onOpenLibraryDetail={(entry) => {
runProtectedAction(() => {
void detailNavigation.openLibraryDetail(entry);
@@ -7984,7 +8222,7 @@ export function PlatformEntryFlowShellImpl({
</motion.div>
)}
{selectionStage === 'match3d-agent-workspace' && (
{selectionStage === 'match3d-agent-workspace' && match3dSession && (
<motion.div
key="match3d-agent-workspace"
initial={{ opacity: 0, y: 12 }}
@@ -7999,14 +8237,9 @@ export function PlatformEntryFlowShellImpl({
>
<Match3DAgentWorkspace
session={match3dSession}
streamingReplyText={streamingMatch3DReplyText}
isStreamingReply={isStreamingMatch3DReply}
isBusy={isMatch3DBusy || isStreamingMatch3DReply}
error={match3dError}
onBack={leaveMatch3DFlow}
onSubmitMessage={(payload) => {
void submitMatch3DMessage(payload);
}}
onExecuteAction={(payload) => {
void executeMatch3DAction(payload);
}}
@@ -8015,6 +8248,52 @@ export function PlatformEntryFlowShellImpl({
</motion.div>
)}
{selectionStage === 'match3d-generating' && (
<motion.div
key="match3d-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={
match3dSession?.lastAssistantReply ??
'正在生成本局抓大鹅物品素材。'
}
anchorEntries={buildMatch3DGenerationAnchorEntries(
match3dSession,
match3dFormDraftPayload,
)}
progress={buildMiniGameDraftGenerationProgress(
match3dGenerationState,
miniGameGenerationProgressNowMs,
)}
isGenerating={isMatch3DBusy}
error={match3dError}
onBack={leaveMatch3DFlow}
onEditSetting={() => {
setSelectionStage('match3d-agent-workspace');
}}
onRetry={retryMatch3DDraftGeneration}
onInterrupt={undefined}
backLabel="返回创作中心"
settingActionLabel={null}
retryLabel="重新生成草稿"
settingTitle="当前抓大鹅信息"
settingDescription={null}
progressTitle="抓大鹅草稿生成进度"
activeBadgeLabel="素材生成中"
pausedBadgeLabel="素材生成已暂停"
idleBadgeLabel="等待返回工作区"
/>
</Suspense>
</motion.div>
)}
{selectionStage === 'match3d-result' && match3dSession?.draft && (
<motion.div
key="match3d-result"
@@ -8467,7 +8746,7 @@ export function PlatformEntryFlowShellImpl({
)}
progress={buildMiniGameDraftGenerationProgress(
puzzleGenerationState,
puzzleGenerationProgressNowMs,
miniGameGenerationProgressNowMs,
)}
isGenerating={isPuzzleBusy}
error={puzzleError}
@@ -8514,7 +8793,7 @@ export function PlatformEntryFlowShellImpl({
error={puzzleError}
onBack={leavePuzzleFlow}
onExecuteAction={(payload) => {
void executePuzzleAction(payload);
executePuzzleWorkspaceAction(payload);
}}
onStartTestRun={startPuzzleTestRunFromDraft}
creativeDraftEdit={
@@ -8546,25 +8825,67 @@ export function PlatformEntryFlowShellImpl({
session={visualNovelSession}
isBusy={isVisualNovelBusy || isVisualNovelStreamingReply}
error={visualNovelError}
streamingReplyText={visualNovelStreamingReplyText}
onBack={leaveVisualNovelFlow}
onCreateSession={(payload) => {
void visualNovelFlow.openWorkspace(payload);
initialFormPayload={visualNovelFormDraftPayload}
onCreateFromForm={(payload) => {
void createVisualNovelDraftFromForm(payload);
}}
onSubmitMessage={(payload) => {
void visualNovelFlow.submitMessage(payload);
/>
</Suspense>
</motion.div>
)}
{selectionStage === 'visual-novel-generating' && (
<motion.div
key="visual-novel-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={
visualNovelFormDraftPayload?.seedText ??
visualNovelSession?.messages.find(
(message) => message.role === 'user',
)?.text ??
'正在整理当前视觉小说草稿。'
}
anchorEntries={buildVisualNovelEntryGenerationAnchorEntries(
visualNovelFormDraftPayload,
)}
progress={buildVisualNovelEntryGenerationProgress(
visualNovelGenerationStartedAtMs,
visualNovelGenerationPhase,
miniGameGenerationProgressNowMs,
)}
isGenerating={isVisualNovelBusy || isVisualNovelStreamingReply}
error={visualNovelError}
onBack={leaveVisualNovelFlow}
onEditSetting={() => {
setSelectionStage('visual-novel-agent-workspace');
}}
onExecuteAction={(payload) => {
void visualNovelFlow.executeAction(payload);
}}
onOpenResult={openVisualNovelResult}
onRetry={retryVisualNovelDraftGeneration}
onInterrupt={undefined}
backLabel="返回创作中心"
settingActionLabel={null}
retryLabel="重新生成草稿"
settingTitle="当前视觉小说信息"
settingDescription={null}
progressTitle="视觉小说草稿生成进度"
activeBadgeLabel="草稿生成中"
pausedBadgeLabel="草稿生成已暂停"
idleBadgeLabel="等待返回工作区"
/>
</Suspense>
</motion.div>
)}
{selectionStage === 'visual-novel-result' &&
visualNovelSession?.draft && (
(visualNovelSession?.draft || visualNovelWork?.draft) && (
<motion.div
key="visual-novel-result"
initial={{ opacity: 0, y: 12 }}
@@ -8576,7 +8897,7 @@ export function PlatformEntryFlowShellImpl({
fallback={<LazyPanelFallback label="正在加载视觉小说结果..." />}
>
<VisualNovelResultView
draft={visualNovelWork?.draft ?? visualNovelSession.draft}
draft={visualNovelWork?.draft ?? visualNovelSession?.draft}
isBusy={isVisualNovelBusy}
error={visualNovelError}
onBack={() => {
@@ -9049,9 +9370,7 @@ export function PlatformEntryFlowShellImpl({
});
}}
onSelectMatch3D={() => {
runProtectedAction(() => {
void openMatch3DAgentWorkspace();
});
handleCreationHubCreateType('match3d');
}}
onSelectSquareHole={() => {
runProtectedAction(() => {
@@ -9059,9 +9378,7 @@ export function PlatformEntryFlowShellImpl({
});
}}
onSelectPuzzle={() => {
runProtectedAction(() => {
void openPuzzleAgentWorkspace();
});
handleCreationHubCreateType('puzzle');
}}
onSelectCreativeAgent={() => {
runProtectedAction(() => {
@@ -9069,9 +9386,7 @@ export function PlatformEntryFlowShellImpl({
});
}}
onSelectVisualNovel={() => {
runProtectedAction(() => {
openVisualNovelAgentWorkspace();
});
handleCreationHubCreateType('visual-novel');
}}
/>
<PublishShareModal