1
This commit is contained in:
@@ -59,7 +59,7 @@ import {
|
||||
getPuzzleAgentSession,
|
||||
streamPuzzleAgentMessage,
|
||||
} from '../../services/puzzle-agent';
|
||||
import { getPuzzleGalleryDetail } from '../../services/puzzle-gallery';
|
||||
import { getPuzzleGalleryDetail, listPuzzleGallery } from '../../services/puzzle-gallery';
|
||||
import {
|
||||
advanceLocalPuzzleLevel,
|
||||
dragLocalPuzzlePiece,
|
||||
@@ -82,6 +82,11 @@ 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 {
|
||||
isPuzzleGalleryEntry,
|
||||
mapPuzzleWorkToPlatformGalleryCard,
|
||||
type PlatformPublicGalleryCard,
|
||||
} from '../rpg-entry/rpgEntryWorldPresentation';
|
||||
import { PlatformEntryCreationTypeModal } from './PlatformEntryCreationTypeModal';
|
||||
import { PlatformEntryHomeView } from './PlatformEntryHomeView';
|
||||
import {
|
||||
@@ -93,6 +98,7 @@ import type { PlatformCreationTypeId } from './platformEntryCreationTypes';
|
||||
import type { PlatformEntryFlowShellProps } from './platformEntryTypes';
|
||||
import { PlatformEntryWorldDetailView } from './PlatformEntryWorldDetailView';
|
||||
import { usePlatformEntryBootstrap } from './usePlatformEntryBootstrap';
|
||||
import { usePlatformCreationAgentFlowController } from './usePlatformCreationAgentFlowController';
|
||||
import { usePlatformEntryLibraryDetail } from './usePlatformEntryLibraryDetail';
|
||||
import { usePlatformEntryNavigation } from './usePlatformEntryNavigation';
|
||||
|
||||
@@ -114,6 +120,33 @@ const AGENT_RESULT_STRUCTURAL_BLOCKER_CODES = new Set([
|
||||
'publish_missing_first_act',
|
||||
]);
|
||||
|
||||
function getPlatformPublicGalleryEntryTime(entry: PlatformPublicGalleryCard) {
|
||||
const rawTime = entry.publishedAt ?? entry.updatedAt;
|
||||
const timestamp = new Date(rawTime).getTime();
|
||||
return Number.isNaN(timestamp) ? 0 : timestamp;
|
||||
}
|
||||
|
||||
function getPlatformPublicGalleryEntryKey(entry: PlatformPublicGalleryCard) {
|
||||
return `${isPuzzleGalleryEntry(entry) ? 'puzzle' : 'rpg'}:${entry.ownerUserId}:${entry.profileId}`;
|
||||
}
|
||||
|
||||
function mergePlatformPublicGalleryEntries(
|
||||
rpgEntries: CustomWorldGalleryCard[],
|
||||
puzzleEntries: PlatformPublicGalleryCard[],
|
||||
) {
|
||||
const entryMap = new Map<string, PlatformPublicGalleryCard>();
|
||||
|
||||
[...rpgEntries, ...puzzleEntries].forEach((entry) => {
|
||||
entryMap.set(getPlatformPublicGalleryEntryKey(entry), entry);
|
||||
});
|
||||
|
||||
return Array.from(entryMap.values()).sort(
|
||||
(left, right) =>
|
||||
getPlatformPublicGalleryEntryTime(right) -
|
||||
getPlatformPublicGalleryEntryTime(left),
|
||||
);
|
||||
}
|
||||
|
||||
function readProfileTextField(
|
||||
profile: CustomWorldProfile | null,
|
||||
paths: string[],
|
||||
@@ -304,28 +337,20 @@ export function PlatformEntryFlowShellImpl({
|
||||
const [showCreationTypeModal, setShowCreationTypeModal] = useState(false);
|
||||
const [selectedDetailEntry, setSelectedDetailEntry] =
|
||||
useState<CustomWorldLibraryEntry<CustomWorldProfile> | null>(null);
|
||||
const [bigFishSession, setBigFishSession] =
|
||||
useState<BigFishSessionSnapshotResponse | null>(null);
|
||||
const [bigFishWorks, setBigFishWorks] = useState<BigFishWorkSummary[]>([]);
|
||||
const [bigFishRun, setBigFishRun] =
|
||||
useState<BigFishRuntimeSnapshotResponse | null>(null);
|
||||
const [bigFishError, setBigFishError] = useState<string | null>(null);
|
||||
const [isBigFishBusy, setIsBigFishBusy] = useState(false);
|
||||
const [isBigFishLoadingLibrary, setIsBigFishLoadingLibrary] = useState(false);
|
||||
const [streamingBigFishReplyText, setStreamingBigFishReplyText] =
|
||||
useState('');
|
||||
const [isStreamingBigFishReply, setIsStreamingBigFishReply] = useState(false);
|
||||
const bigFishInputInFlightRef = useRef(false);
|
||||
const [puzzleSession, setPuzzleSession] =
|
||||
useState<PuzzleAgentSessionSnapshot | null>(null);
|
||||
const [puzzleOperation, setPuzzleOperation] =
|
||||
useState<PuzzleAgentOperationRecord | null>(null);
|
||||
const [puzzleWorks, setPuzzleWorks] = useState<PuzzleWorkSummary[]>([]);
|
||||
const [puzzleGalleryEntries, setPuzzleGalleryEntries] = useState<
|
||||
PuzzleWorkSummary[]
|
||||
>([]);
|
||||
const [selectedPuzzleDetail, setSelectedPuzzleDetail] =
|
||||
useState<PuzzleWorkSummary | null>(null);
|
||||
const [puzzleRun, setPuzzleRun] = useState<PuzzleRunSnapshot | null>(null);
|
||||
const [puzzleError, setPuzzleError] = useState<string | null>(null);
|
||||
const [isPuzzleBusy, setIsPuzzleBusy] = useState(false);
|
||||
const [isPuzzleLoadingLibrary, setIsPuzzleLoadingLibrary] = useState(false);
|
||||
const [isSearchingPublicCode, setIsSearchingPublicCode] = useState(false);
|
||||
const [publicSearchError, setPublicSearchError] = useState<string | null>(null);
|
||||
@@ -334,8 +359,6 @@ export function PlatformEntryFlowShellImpl({
|
||||
const [deletingCreationWorkId, setDeletingCreationWorkId] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [streamingPuzzleReplyText, setStreamingPuzzleReplyText] = useState('');
|
||||
const [isStreamingPuzzleReply, setIsStreamingPuzzleReply] = useState(false);
|
||||
const hasInitialAgentSession = Boolean(
|
||||
readCustomWorldAgentUiState().activeSessionId,
|
||||
);
|
||||
@@ -359,6 +382,17 @@ export function PlatformEntryFlowShellImpl({
|
||||
setPlatformTab('create');
|
||||
}, [setPlatformTab]);
|
||||
|
||||
const resolveBigFishErrorMessage = useCallback(
|
||||
(error: unknown, fallback: string) =>
|
||||
resolveRpgCreationErrorMessage(error, fallback),
|
||||
[],
|
||||
);
|
||||
const resolvePuzzleErrorMessage = useCallback(
|
||||
(error: unknown, fallback: string) =>
|
||||
resolveRpgCreationErrorMessage(error, fallback),
|
||||
[],
|
||||
);
|
||||
|
||||
const sessionController = useRpgCreationSessionController({
|
||||
userId: authUi?.user?.id,
|
||||
openLoginModal: authUi?.openLoginModal,
|
||||
@@ -441,10 +475,18 @@ export function PlatformEntryFlowShellImpl({
|
||||
agentSessionProfile: sessionController.agentDraftResultProfile,
|
||||
agentSession: sessionController.agentSession,
|
||||
handleCustomWorldSelect,
|
||||
executePublishWorld: () =>
|
||||
autosaveCoordinator.executeAgentActionAndWait({
|
||||
executePublishWorld: async () => {
|
||||
const latestSession = await autosaveCoordinator.executeAgentActionAndWait({
|
||||
action: 'publish_world',
|
||||
}),
|
||||
});
|
||||
// 发布动作会在后端同步 gallery 投影;前端发布完成后立即刷新首页/分类页共用的公开作品列表。
|
||||
await Promise.allSettled([
|
||||
platformBootstrap.refreshPublishedGallery(),
|
||||
platformBootstrap.refreshCustomWorldWorks(),
|
||||
refreshPuzzleGallery(),
|
||||
]);
|
||||
return latestSession;
|
||||
},
|
||||
setGeneratedCustomWorldProfile:
|
||||
sessionController.setGeneratedCustomWorldProfile,
|
||||
});
|
||||
@@ -495,8 +537,24 @@ export function PlatformEntryFlowShellImpl({
|
||||
}, [agentResultPreview]);
|
||||
|
||||
const featuredGalleryEntries = useMemo(
|
||||
() => platformBootstrap.publishedGalleryEntries.slice(0, 6),
|
||||
[platformBootstrap.publishedGalleryEntries],
|
||||
() => {
|
||||
const puzzlePublicEntries = puzzleGalleryEntries.map(
|
||||
mapPuzzleWorkToPlatformGalleryCard,
|
||||
);
|
||||
return mergePlatformPublicGalleryEntries(
|
||||
platformBootstrap.publishedGalleryEntries,
|
||||
puzzlePublicEntries,
|
||||
).slice(0, 6);
|
||||
},
|
||||
[platformBootstrap.publishedGalleryEntries, puzzleGalleryEntries],
|
||||
);
|
||||
const latestGalleryEntries = useMemo(
|
||||
() =>
|
||||
mergePlatformPublicGalleryEntries(
|
||||
platformBootstrap.publishedGalleryEntries,
|
||||
puzzleGalleryEntries.map(mapPuzzleWorkToPlatformGalleryCard),
|
||||
),
|
||||
[platformBootstrap.publishedGalleryEntries, puzzleGalleryEntries],
|
||||
);
|
||||
|
||||
const creationHubItems =
|
||||
@@ -523,17 +581,6 @@ export function PlatformEntryFlowShellImpl({
|
||||
setSelectionStage,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectionStage === 'big-fish-result' && !bigFishSession?.draft) {
|
||||
setSelectionStage(
|
||||
bigFishSession ? 'big-fish-agent-workspace' : 'platform',
|
||||
);
|
||||
}
|
||||
if (selectionStage === 'big-fish-runtime' && !bigFishRun) {
|
||||
setSelectionStage(bigFishSession?.draft ? 'big-fish-result' : 'platform');
|
||||
}
|
||||
}, [bigFishRun, bigFishSession, selectionStage, setSelectionStage]);
|
||||
|
||||
const runProtectedAction = useCallback(
|
||||
(action: () => void) => {
|
||||
if (!authUi?.requireAuth) {
|
||||
@@ -647,17 +694,6 @@ export function PlatformEntryFlowShellImpl({
|
||||
setShowCreationTypeModal(true);
|
||||
}, [prepareCreationLaunch]);
|
||||
|
||||
const resolveBigFishErrorMessage = useCallback(
|
||||
(error: unknown, fallback: string) =>
|
||||
resolveRpgCreationErrorMessage(error, fallback),
|
||||
[],
|
||||
);
|
||||
const resolvePuzzleErrorMessage = useCallback(
|
||||
(error: unknown, fallback: string) =>
|
||||
resolveRpgCreationErrorMessage(error, fallback),
|
||||
[],
|
||||
);
|
||||
|
||||
const refreshBigFishShelf = useCallback(async () => {
|
||||
setIsBigFishLoadingLibrary(true);
|
||||
|
||||
@@ -690,64 +726,144 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
}, [resolvePuzzleErrorMessage]);
|
||||
|
||||
const openBigFishAgentWorkspace = useCallback(async () => {
|
||||
if (isBigFishBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsBigFishBusy(true);
|
||||
setBigFishError(null);
|
||||
setBigFishRun(null);
|
||||
|
||||
const refreshPuzzleGallery = useCallback(async () => {
|
||||
try {
|
||||
const { session } = await createBigFishCreationSession({});
|
||||
setBigFishSession(session);
|
||||
enterCreateTab();
|
||||
setShowCreationTypeModal(false);
|
||||
setSelectionStage('big-fish-agent-workspace');
|
||||
const galleryResponse = await listPuzzleGallery();
|
||||
setPuzzleGalleryEntries(galleryResponse.items);
|
||||
return galleryResponse.items;
|
||||
} catch (error) {
|
||||
setBigFishError(
|
||||
resolveBigFishErrorMessage(error, '开启大鱼吃小鱼共创工作台失败。'),
|
||||
setPuzzleGalleryEntries([]);
|
||||
setPuzzleError(
|
||||
resolvePuzzleErrorMessage(error, '读取拼图广场失败。'),
|
||||
);
|
||||
} finally {
|
||||
setIsBigFishBusy(false);
|
||||
return [];
|
||||
}
|
||||
}, [
|
||||
}, [resolvePuzzleErrorMessage]);
|
||||
|
||||
const bigFishFlow = usePlatformCreationAgentFlowController<
|
||||
BigFishSessionSnapshotResponse,
|
||||
Record<string, never>,
|
||||
{ session: BigFishSessionSnapshotResponse },
|
||||
SendBigFishMessageRequest,
|
||||
ExecuteBigFishActionRequest,
|
||||
{ session: BigFishSessionSnapshotResponse }
|
||||
>({
|
||||
client: {
|
||||
createSession: createBigFishCreationSession,
|
||||
getSession: getBigFishCreationSession,
|
||||
streamMessage: streamBigFishCreationMessage,
|
||||
executeAction: executeBigFishCreationAction,
|
||||
selectSession: (response) => response.session,
|
||||
},
|
||||
createPayload: {},
|
||||
workspaceStage: 'big-fish-agent-workspace',
|
||||
resultStage: 'big-fish-result',
|
||||
platformStage: 'platform',
|
||||
isCompileAction: (payload) => payload.action === 'big_fish_compile_draft',
|
||||
resolveErrorMessage: resolveBigFishErrorMessage,
|
||||
errorMessages: {
|
||||
open: '开启大鱼吃小鱼共创工作台失败。',
|
||||
restoreMissingSession: '这份大鱼吃小鱼草稿缺少会话信息,请重新开始创作。',
|
||||
restore: '读取大鱼吃小鱼创作草稿失败。',
|
||||
submit: '发送大鱼吃小鱼共创消息失败。',
|
||||
execute: '执行大鱼吃小鱼操作失败。',
|
||||
},
|
||||
enterCreateTab,
|
||||
isBigFishBusy,
|
||||
resolveBigFishErrorMessage,
|
||||
setSelectionStage,
|
||||
]);
|
||||
onSessionOpened: () => {
|
||||
setShowCreationTypeModal(false);
|
||||
},
|
||||
onActionComplete: ({ response, setSession }) => {
|
||||
setSession(response.session);
|
||||
},
|
||||
});
|
||||
|
||||
const puzzleFlow = usePlatformCreationAgentFlowController<
|
||||
PuzzleAgentSessionSnapshot,
|
||||
Record<string, never>,
|
||||
{ session: PuzzleAgentSessionSnapshot },
|
||||
SendPuzzleAgentMessageRequest,
|
||||
PuzzleAgentActionRequest,
|
||||
{ operation: PuzzleAgentOperationRecord }
|
||||
>({
|
||||
client: {
|
||||
createSession: createPuzzleAgentSession,
|
||||
getSession: getPuzzleAgentSession,
|
||||
streamMessage: streamPuzzleAgentMessage,
|
||||
executeAction: executePuzzleAgentAction,
|
||||
selectSession: (response) => response.session,
|
||||
},
|
||||
createPayload: {},
|
||||
workspaceStage: 'puzzle-agent-workspace',
|
||||
resultStage: 'puzzle-result',
|
||||
platformStage: 'platform',
|
||||
isCompileAction: (payload) => payload.action === 'compile_puzzle_draft',
|
||||
resolveErrorMessage: resolvePuzzleErrorMessage,
|
||||
errorMessages: {
|
||||
open: '开启拼图共创工作台失败。',
|
||||
restoreMissingSession: '这份拼图草稿缺少会话信息,请重新开始创作。',
|
||||
restore: '读取拼图创作草稿失败。',
|
||||
submit: '发送拼图共创消息失败。',
|
||||
execute: '执行拼图操作失败。',
|
||||
},
|
||||
enterCreateTab,
|
||||
setSelectionStage,
|
||||
onSessionOpened: () => {
|
||||
setShowCreationTypeModal(false);
|
||||
},
|
||||
onActionComplete: async ({ payload, response, session, setSession }) => {
|
||||
setPuzzleOperation(response.operation);
|
||||
|
||||
if (payload.action === 'publish_puzzle_work') {
|
||||
await Promise.allSettled([
|
||||
refreshPuzzleShelf(),
|
||||
refreshPuzzleGallery(),
|
||||
]);
|
||||
}
|
||||
|
||||
const latestResponse = await getPuzzleAgentSession(session.sessionId);
|
||||
const latestSession = latestResponse.session;
|
||||
setSession(latestSession);
|
||||
|
||||
if (
|
||||
payload.action === 'publish_puzzle_work' &&
|
||||
latestSession.publishedProfileId
|
||||
) {
|
||||
const galleryDetail = await getPuzzleGalleryDetail(
|
||||
latestSession.publishedProfileId,
|
||||
);
|
||||
setSelectedPuzzleDetail(galleryDetail.item);
|
||||
setSelectionStage('puzzle-gallery-detail');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const bigFishSession = bigFishFlow.session;
|
||||
const bigFishError = bigFishFlow.error;
|
||||
const setBigFishError = bigFishFlow.setError;
|
||||
const isBigFishBusy = bigFishFlow.isBusy;
|
||||
const setIsBigFishBusy = bigFishFlow.setIsBusy;
|
||||
const streamingBigFishReplyText = bigFishFlow.streamingReplyText;
|
||||
const isStreamingBigFishReply = bigFishFlow.isStreamingReply;
|
||||
|
||||
const puzzleSession = puzzleFlow.session;
|
||||
const puzzleError = puzzleFlow.error;
|
||||
const setPuzzleError = puzzleFlow.setError;
|
||||
const isPuzzleBusy = puzzleFlow.isBusy;
|
||||
const setIsPuzzleBusy = puzzleFlow.setIsBusy;
|
||||
const streamingPuzzleReplyText = puzzleFlow.streamingReplyText;
|
||||
const isStreamingPuzzleReply = puzzleFlow.isStreamingReply;
|
||||
|
||||
const openBigFishAgentWorkspace = useCallback(async () => {
|
||||
setBigFishRun(null);
|
||||
await bigFishFlow.openWorkspace();
|
||||
}, [bigFishFlow]);
|
||||
|
||||
const openPuzzleAgentWorkspace = useCallback(async () => {
|
||||
if (isPuzzleBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsPuzzleBusy(true);
|
||||
setPuzzleError(null);
|
||||
setPuzzleRun(null);
|
||||
setPuzzleOperation(null);
|
||||
|
||||
try {
|
||||
const { session } = await createPuzzleAgentSession({});
|
||||
setPuzzleSession(session);
|
||||
enterCreateTab();
|
||||
setShowCreationTypeModal(false);
|
||||
setSelectionStage('puzzle-agent-workspace');
|
||||
} catch (error) {
|
||||
setPuzzleError(
|
||||
resolvePuzzleErrorMessage(error, '开启拼图共创工作台失败。'),
|
||||
);
|
||||
} finally {
|
||||
setIsPuzzleBusy(false);
|
||||
}
|
||||
}, [
|
||||
enterCreateTab,
|
||||
isPuzzleBusy,
|
||||
resolvePuzzleErrorMessage,
|
||||
setSelectionStage,
|
||||
]);
|
||||
await puzzleFlow.openWorkspace();
|
||||
}, [puzzleFlow]);
|
||||
|
||||
const handleCreationHubCreateType = useCallback(
|
||||
(type: PlatformCreationTypeId) => {
|
||||
@@ -789,206 +905,34 @@ export function PlatformEntryFlowShellImpl({
|
||||
);
|
||||
|
||||
const leaveBigFishFlow = useCallback(() => {
|
||||
setBigFishError(null);
|
||||
setBigFishRun(null);
|
||||
setStreamingBigFishReplyText('');
|
||||
setIsStreamingBigFishReply(false);
|
||||
enterCreateTab();
|
||||
setSelectionStage('platform');
|
||||
}, [enterCreateTab, setSelectionStage]);
|
||||
bigFishFlow.leaveFlow();
|
||||
}, [bigFishFlow]);
|
||||
|
||||
const leavePuzzleFlow = useCallback(() => {
|
||||
setPuzzleError(null);
|
||||
setPuzzleOperation(null);
|
||||
setPuzzleRun(null);
|
||||
setStreamingPuzzleReplyText('');
|
||||
setIsStreamingPuzzleReply(false);
|
||||
enterCreateTab();
|
||||
setSelectionStage('platform');
|
||||
}, [enterCreateTab, setSelectionStage]);
|
||||
puzzleFlow.leaveFlow();
|
||||
}, [puzzleFlow]);
|
||||
|
||||
const submitBigFishMessage = useCallback(
|
||||
async (payload: SendBigFishMessageRequest) => {
|
||||
if (!bigFishSession || isStreamingBigFishReply) {
|
||||
return;
|
||||
}
|
||||
const submitBigFishMessage = bigFishFlow.submitMessage;
|
||||
|
||||
const optimisticMessage = {
|
||||
id: payload.clientMessageId,
|
||||
role: 'user',
|
||||
kind: 'chat',
|
||||
text: payload.text.trim(),
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
const submitPuzzleMessage = puzzleFlow.submitMessage;
|
||||
|
||||
setBigFishError(null);
|
||||
setStreamingBigFishReplyText('');
|
||||
setIsStreamingBigFishReply(true);
|
||||
setBigFishSession((current) =>
|
||||
current
|
||||
? {
|
||||
...current,
|
||||
messages: [...current.messages, optimisticMessage],
|
||||
updatedAt: optimisticMessage.createdAt,
|
||||
}
|
||||
: current,
|
||||
const executeBigFishAction = bigFishFlow.executeAction;
|
||||
|
||||
const executePuzzleAction = puzzleFlow.executeAction;
|
||||
|
||||
useEffect(() => {
|
||||
if (selectionStage === 'big-fish-result' && !bigFishSession?.draft) {
|
||||
setSelectionStage(
|
||||
bigFishSession ? 'big-fish-agent-workspace' : 'platform',
|
||||
);
|
||||
|
||||
try {
|
||||
const nextSession = await streamBigFishCreationMessage(
|
||||
bigFishSession.sessionId,
|
||||
payload,
|
||||
{
|
||||
onUpdate: setStreamingBigFishReplyText,
|
||||
},
|
||||
);
|
||||
setBigFishSession(nextSession);
|
||||
setStreamingBigFishReplyText('');
|
||||
} catch (error) {
|
||||
setBigFishError(
|
||||
resolveBigFishErrorMessage(error, '发送大鱼吃小鱼共创消息失败。'),
|
||||
);
|
||||
} finally {
|
||||
setIsStreamingBigFishReply(false);
|
||||
}
|
||||
},
|
||||
[bigFishSession, isStreamingBigFishReply, resolveBigFishErrorMessage],
|
||||
);
|
||||
|
||||
const submitPuzzleMessage = useCallback(
|
||||
async (payload: SendPuzzleAgentMessageRequest) => {
|
||||
if (!puzzleSession || isStreamingPuzzleReply) {
|
||||
return;
|
||||
}
|
||||
|
||||
const optimisticMessage = {
|
||||
id: payload.clientMessageId,
|
||||
role: 'user',
|
||||
kind: 'chat',
|
||||
text: payload.text.trim(),
|
||||
createdAt: new Date().toISOString(),
|
||||
} satisfies PuzzleAgentSessionSnapshot['messages'][number];
|
||||
|
||||
setPuzzleError(null);
|
||||
setStreamingPuzzleReplyText('');
|
||||
setIsStreamingPuzzleReply(true);
|
||||
setPuzzleSession((current) =>
|
||||
current
|
||||
? {
|
||||
...current,
|
||||
messages: [...current.messages, optimisticMessage],
|
||||
updatedAt: optimisticMessage.createdAt,
|
||||
}
|
||||
: current,
|
||||
);
|
||||
|
||||
try {
|
||||
const nextSession = await streamPuzzleAgentMessage(
|
||||
puzzleSession.sessionId,
|
||||
payload,
|
||||
{
|
||||
onUpdate: setStreamingPuzzleReplyText,
|
||||
},
|
||||
);
|
||||
setPuzzleSession(nextSession);
|
||||
setStreamingPuzzleReplyText('');
|
||||
} catch (error) {
|
||||
setPuzzleError(
|
||||
resolvePuzzleErrorMessage(error, '发送拼图共创消息失败。'),
|
||||
);
|
||||
} finally {
|
||||
setIsStreamingPuzzleReply(false);
|
||||
}
|
||||
},
|
||||
[isStreamingPuzzleReply, puzzleSession, resolvePuzzleErrorMessage],
|
||||
);
|
||||
|
||||
const executeBigFishAction = useCallback(
|
||||
async (payload: ExecuteBigFishActionRequest) => {
|
||||
if (!bigFishSession || isBigFishBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsBigFishBusy(true);
|
||||
setBigFishError(null);
|
||||
|
||||
try {
|
||||
const { session } = await executeBigFishCreationAction(
|
||||
bigFishSession.sessionId,
|
||||
payload,
|
||||
);
|
||||
setBigFishSession(session);
|
||||
if (payload.action === 'big_fish_compile_draft') {
|
||||
setSelectionStage('big-fish-result');
|
||||
}
|
||||
} catch (error) {
|
||||
setBigFishError(
|
||||
resolveBigFishErrorMessage(error, '执行大鱼吃小鱼操作失败。'),
|
||||
);
|
||||
} finally {
|
||||
setIsBigFishBusy(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
bigFishSession,
|
||||
isBigFishBusy,
|
||||
resolveBigFishErrorMessage,
|
||||
setSelectionStage,
|
||||
],
|
||||
);
|
||||
|
||||
const executePuzzleAction = useCallback(
|
||||
async (payload: PuzzleAgentActionRequest) => {
|
||||
if (!puzzleSession || isPuzzleBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsPuzzleBusy(true);
|
||||
setPuzzleError(null);
|
||||
|
||||
try {
|
||||
const { operation } = await executePuzzleAgentAction(
|
||||
puzzleSession.sessionId,
|
||||
payload,
|
||||
);
|
||||
setPuzzleOperation(operation);
|
||||
|
||||
if (payload.action === 'publish_puzzle_work') {
|
||||
await refreshPuzzleShelf();
|
||||
}
|
||||
|
||||
const { session } = await getPuzzleAgentSession(
|
||||
puzzleSession.sessionId,
|
||||
);
|
||||
setPuzzleSession(session);
|
||||
|
||||
if (payload.action === 'compile_puzzle_draft') {
|
||||
setSelectionStage('puzzle-result');
|
||||
}
|
||||
if (
|
||||
payload.action === 'publish_puzzle_work' &&
|
||||
session.publishedProfileId
|
||||
) {
|
||||
const galleryDetail = await getPuzzleGalleryDetail(
|
||||
session.publishedProfileId,
|
||||
);
|
||||
setSelectedPuzzleDetail(galleryDetail.item);
|
||||
setSelectionStage('puzzle-gallery-detail');
|
||||
}
|
||||
} catch (error) {
|
||||
setPuzzleError(resolvePuzzleErrorMessage(error, '执行拼图操作失败。'));
|
||||
} finally {
|
||||
setIsPuzzleBusy(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
isPuzzleBusy,
|
||||
puzzleSession,
|
||||
refreshPuzzleShelf,
|
||||
resolvePuzzleErrorMessage,
|
||||
setSelectionStage,
|
||||
],
|
||||
);
|
||||
}
|
||||
if (selectionStage === 'big-fish-runtime' && !bigFishRun) {
|
||||
setSelectionStage(bigFishSession?.draft ? 'big-fish-result' : 'platform');
|
||||
}
|
||||
}, [bigFishRun, bigFishSession, selectionStage, setSelectionStage]);
|
||||
|
||||
const startBigFishRun = useCallback(async () => {
|
||||
if (!bigFishSession || isBigFishBusy) {
|
||||
@@ -1327,6 +1271,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
void deletePuzzleWork(work.profileId)
|
||||
.then((response) => {
|
||||
setPuzzleWorks(response.items);
|
||||
void refreshPuzzleGallery();
|
||||
})
|
||||
.catch((error) => {
|
||||
setPuzzleError(resolvePuzzleErrorMessage(error, '删除拼图作品失败。'));
|
||||
@@ -1336,7 +1281,12 @@ export function PlatformEntryFlowShellImpl({
|
||||
});
|
||||
});
|
||||
},
|
||||
[deletingCreationWorkId, resolvePuzzleErrorMessage, runProtectedAction],
|
||||
[
|
||||
deletingCreationWorkId,
|
||||
refreshPuzzleGallery,
|
||||
resolvePuzzleErrorMessage,
|
||||
runProtectedAction,
|
||||
],
|
||||
);
|
||||
|
||||
const openPuzzleDetail = useCallback(
|
||||
@@ -1360,80 +1310,28 @@ export function PlatformEntryFlowShellImpl({
|
||||
|
||||
const openPuzzleDraft = useCallback(
|
||||
async (item: PuzzleWorkSummary) => {
|
||||
const sessionId = item.sourceSessionId?.trim();
|
||||
if (!sessionId) {
|
||||
setPuzzleError('这份拼图草稿缺少会话信息,请重新开始创作。');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsPuzzleBusy(true);
|
||||
setPuzzleError(null);
|
||||
setPuzzleOperation(null);
|
||||
setPuzzleRun(null);
|
||||
setSelectedPuzzleDetail(null);
|
||||
setStreamingPuzzleReplyText('');
|
||||
setIsStreamingPuzzleReply(false);
|
||||
|
||||
try {
|
||||
const { session } = await getPuzzleAgentSession(sessionId);
|
||||
setPuzzleSession(session);
|
||||
enterCreateTab();
|
||||
setSelectionStage(session.draft ? 'puzzle-result' : 'puzzle-agent-workspace');
|
||||
} catch (error) {
|
||||
const restoredSession = await puzzleFlow.restoreDraft(item.sourceSessionId);
|
||||
if (!restoredSession) {
|
||||
await refreshPuzzleShelf().catch(() => undefined);
|
||||
setPuzzleError(resolvePuzzleErrorMessage(error, '读取拼图创作草稿失败。'));
|
||||
enterCreateTab();
|
||||
setSelectionStage('platform');
|
||||
} finally {
|
||||
setIsPuzzleBusy(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
enterCreateTab,
|
||||
refreshPuzzleShelf,
|
||||
resolvePuzzleErrorMessage,
|
||||
setSelectionStage,
|
||||
],
|
||||
[puzzleFlow, refreshPuzzleShelf],
|
||||
);
|
||||
|
||||
const openBigFishDraft = useCallback(
|
||||
async (item: BigFishWorkSummary) => {
|
||||
const sessionId = item.sourceSessionId?.trim();
|
||||
if (!sessionId) {
|
||||
setBigFishError('这份大鱼吃小鱼草稿缺少会话信息,请重新开始创作。');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsBigFishBusy(true);
|
||||
setBigFishError(null);
|
||||
setBigFishRun(null);
|
||||
setStreamingBigFishReplyText('');
|
||||
setIsStreamingBigFishReply(false);
|
||||
|
||||
try {
|
||||
const { session } = await getBigFishCreationSession(sessionId);
|
||||
setBigFishSession(session);
|
||||
enterCreateTab();
|
||||
setSelectionStage(
|
||||
session.draft ? 'big-fish-result' : 'big-fish-agent-workspace',
|
||||
);
|
||||
} catch (error) {
|
||||
const restoredSession = await bigFishFlow.restoreDraft(
|
||||
item.sourceSessionId,
|
||||
);
|
||||
if (!restoredSession) {
|
||||
await refreshBigFishShelf().catch(() => undefined);
|
||||
setBigFishError(
|
||||
resolveBigFishErrorMessage(error, '读取大鱼吃小鱼创作草稿失败。'),
|
||||
);
|
||||
enterCreateTab();
|
||||
setSelectionStage('platform');
|
||||
} finally {
|
||||
setIsBigFishBusy(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
enterCreateTab,
|
||||
refreshBigFishShelf,
|
||||
resolveBigFishErrorMessage,
|
||||
setSelectionStage,
|
||||
],
|
||||
[bigFishFlow, refreshBigFishShelf],
|
||||
);
|
||||
|
||||
const startBigFishRunFromWork = useCallback(
|
||||
@@ -1450,7 +1348,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
try {
|
||||
const { session } = await getBigFishCreationSession(sessionId);
|
||||
const { run } = await startBigFishRuntimeRun(sessionId);
|
||||
setBigFishSession(session);
|
||||
bigFishFlow.setSession(session);
|
||||
setBigFishRun(run);
|
||||
setSelectionStage('big-fish-runtime');
|
||||
} catch (error) {
|
||||
@@ -1461,9 +1359,15 @@ export function PlatformEntryFlowShellImpl({
|
||||
setIsBigFishBusy(false);
|
||||
}
|
||||
},
|
||||
[resolveBigFishErrorMessage, setSelectionStage],
|
||||
[bigFishFlow, resolveBigFishErrorMessage, setSelectionStage],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectionStage === 'platform') {
|
||||
void refreshPuzzleGallery();
|
||||
}
|
||||
}, [refreshPuzzleGallery, selectionStage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
(platformBootstrap.platformTab === 'create' ||
|
||||
@@ -1610,7 +1514,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
saveEntries={platformBootstrap.saveEntries}
|
||||
saveError={platformBootstrap.saveError}
|
||||
featuredEntries={featuredGalleryEntries}
|
||||
latestEntries={platformBootstrap.publishedGalleryEntries}
|
||||
latestEntries={latestGalleryEntries}
|
||||
myEntries={platformBootstrap.savedCustomWorldEntries}
|
||||
historyEntries={platformBootstrap.historyEntries}
|
||||
profileDashboard={platformBootstrap.profileDashboard}
|
||||
@@ -1636,6 +1540,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
onOpenCreateWorld={openCreationTypePicker}
|
||||
onOpenCreateTypePicker={openCreationTypePicker}
|
||||
onOpenGalleryDetail={(entry) => {
|
||||
if (isPuzzleGalleryEntry(entry)) {
|
||||
void openPuzzleDetail(entry.profileId);
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
void detailNavigation.openGalleryDetail(entry);
|
||||
});
|
||||
@@ -1658,6 +1567,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
void platformBootstrap.refreshProfileDashboard();
|
||||
}
|
||||
}}
|
||||
onRechargeSuccess={platformBootstrap.refreshProfileDashboard}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -1788,6 +1698,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
<BigFishAgentWorkspace
|
||||
session={bigFishSession}
|
||||
streamingReplyText={streamingBigFishReplyText}
|
||||
isStreamingReply={isStreamingBigFishReply}
|
||||
isBusy={isBigFishBusy || isStreamingBigFishReply}
|
||||
error={bigFishError}
|
||||
onBack={leaveBigFishFlow}
|
||||
@@ -1871,6 +1782,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
session={puzzleSession}
|
||||
activeOperation={puzzleOperation}
|
||||
streamingReplyText={streamingPuzzleReplyText}
|
||||
isStreamingReply={isStreamingPuzzleReply}
|
||||
isBusy={isPuzzleBusy || isStreamingPuzzleReply}
|
||||
error={puzzleError}
|
||||
onBack={leavePuzzleFlow}
|
||||
@@ -1985,7 +1897,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
onInterrupt={undefined}
|
||||
backLabel="返回工作区"
|
||||
settingActionLabel={null}
|
||||
retryLabel="重新生成草稿"
|
||||
retryLabel="继续生成草稿"
|
||||
settingTitle="当前世界信息"
|
||||
settingDescription={null}
|
||||
progressTitle="世界草稿生成进度"
|
||||
@@ -2024,26 +1936,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
onBack={
|
||||
sessionController.isAgentDraftResultView
|
||||
? () => {
|
||||
void (async () => {
|
||||
const currentProfile =
|
||||
sessionController.generatedCustomWorldProfile;
|
||||
if (!currentProfile) {
|
||||
leaveAgentDraftResult();
|
||||
return;
|
||||
}
|
||||
|
||||
await autosaveCoordinator.syncAgentDraftResultProfile(
|
||||
currentProfile,
|
||||
);
|
||||
leaveAgentDraftResult();
|
||||
})().catch((error) => {
|
||||
sessionController.setCustomWorldError(
|
||||
resolveRpgCreationErrorMessage(
|
||||
error,
|
||||
'返回创作前同步草稿失败。',
|
||||
),
|
||||
);
|
||||
});
|
||||
leaveAgentDraftResult();
|
||||
}
|
||||
: leaveCustomWorldResult
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user