1
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user