This commit is contained in:
2026-04-18 13:05:29 +08:00
parent 09d4c0c31b
commit 5032701c38
77 changed files with 8538 additions and 2413 deletions

View File

@@ -10,6 +10,7 @@ import {
} from 'react';
import type {
CustomWorldAgentMessage,
CustomWorldAgentActionRequest,
CustomWorldAgentOperationRecord,
CustomWorldAgentSessionSnapshot,
@@ -27,10 +28,11 @@ import {
executeCustomWorldAgentAction,
getCustomWorldAgentOperation,
getCustomWorldAgentSession,
sendCustomWorldAgentMessage,
streamCustomWorldAgentMessage,
} from '../../services/aiService';
import { buildCustomWorldProfileFromAgentDraft } from '../../services/customWorldAgentDraftResult';
import {
buildAgentDraftFoundationAnchorEntries,
buildAgentDraftFoundationGenerationProgress,
buildAgentDraftFoundationSettingText,
isDraftFoundationOperation,
@@ -55,6 +57,7 @@ import {
} from '../../services/platformBrowseHistory';
import {
clearProfileBrowseHistory,
deleteCustomWorldProfile,
getCustomWorldGalleryDetail,
getProfileDashboard,
listCustomWorldGallery,
@@ -66,10 +69,7 @@ import {
upsertCustomWorldProfile,
upsertProfileBrowseHistory,
} from '../../services/storageService';
import {
type CustomWorldProfile,
type GameState,
} from '../../types';
import { type CustomWorldProfile, type GameState } from '../../types';
import { useAuthUi } from '../auth/AuthUiContext';
import { PlatformCreationTypeModal } from './PlatformCreationTypeModal';
import { type PlatformHomeTab, PlatformHomeView } from './PlatformHomeView';
@@ -141,6 +141,16 @@ function createFailedAgentOperation(params: {
};
}
function buildOptimisticAgentMessage(
payload: Pick<CustomWorldAgentMessage, 'id' | 'role' | 'kind' | 'text'>,
): CustomWorldAgentMessage {
return {
...payload,
createdAt: new Date().toISOString(),
relatedOperationId: null,
};
}
function buildAgentSeedTextFromProfile(profile: CustomWorldProfile) {
return (
buildCustomWorldCreatorIntentGenerationText(profile.creatorIntent).trim() ||
@@ -215,6 +225,8 @@ export function PreGameSelectionFlow({
useState<CustomWorldAgentSessionSnapshot | null>(null);
const [agentOperation, setAgentOperation] =
useState<CustomWorldAgentOperationRecord | null>(null);
const [streamingAgentReplyText, setStreamingAgentReplyText] = useState('');
const [isStreamingAgentReply, setIsStreamingAgentReply] = useState(false);
const [isLoadingAgentSession, setIsLoadingAgentSession] = useState(false);
const [customWorldError, setCustomWorldError] = useState<string | null>(null);
const [platformError, setPlatformError] = useState<string | null>(null);
@@ -457,6 +469,8 @@ export function PreGameSelectionFlow({
if (!activeAgentSessionId) {
setAgentSession(null);
setIsLoadingAgentSession(false);
setStreamingAgentReplyText('');
setIsStreamingAgentReply(false);
return;
}
@@ -479,6 +493,8 @@ export function PreGameSelectionFlow({
);
setAgentSession(null);
setAgentOperation(null);
setStreamingAgentReplyText('');
setIsStreamingAgentReply(false);
persistAgentUiState(null, null);
setPlatformTab('create');
setSelectionStage('platform');
@@ -636,6 +652,10 @@ export function PreGameSelectionFlow({
() => buildAgentDraftFoundationSettingText(agentSession),
[agentSession],
);
const agentDraftAnchorPreviewEntries = useMemo(
() => buildAgentDraftFoundationAnchorEntries(agentSession),
[agentSession],
);
const agentDraftResultProfile = useMemo(
() => buildCustomWorldProfileFromAgentDraft(agentSession),
[agentSession],
@@ -794,23 +814,63 @@ export function PreGameSelectionFlow({
return;
}
const optimisticUserMessage = buildOptimisticAgentMessage({
id: payload.clientMessageId,
role: 'user',
kind: 'chat',
text: payload.text.trim(),
});
setAgentOperation(null);
persistAgentUiState(activeAgentSessionId, null);
setStreamingAgentReplyText('');
setIsStreamingAgentReply(true);
setAgentSession((current) =>
current
? {
...current,
messages: [...current.messages, optimisticUserMessage],
updatedAt: optimisticUserMessage.createdAt,
}
: current,
);
try {
const { operation } = await sendCustomWorldAgentMessage(
const nextSession = await streamCustomWorldAgentMessage(
activeAgentSessionId,
payload,
{
onUpdate: (text) => {
setStreamingAgentReplyText(text);
},
},
);
setAgentOperation(operation);
persistAgentUiState(activeAgentSessionId, operation.operationId);
setAgentSession(nextSession);
setAgentOperation(null);
setStreamingAgentReplyText('');
} catch (error) {
const errorMessage = resolveErrorMessage(error, '发送共创消息失败。');
setAgentOperation(
createFailedAgentOperation({
type: 'process_message',
phaseLabel: '发送消息失败',
error: errorMessage,
}),
setAgentSession((current) =>
current
? {
...current,
messages: [
...current.messages,
buildOptimisticAgentMessage({
id: `message-error-${Date.now()}`,
role: 'assistant',
kind: 'warning',
text: errorMessage,
}),
],
updatedAt: new Date().toISOString(),
}
: current,
);
setStreamingAgentReplyText('');
persistAgentUiState(activeAgentSessionId, null);
} finally {
setIsStreamingAgentReply(false);
}
};
@@ -858,6 +918,8 @@ export function PreGameSelectionFlow({
const leaveAgentWorkspace = () => {
setPlatformTab('create');
setAgentOperation(null);
setStreamingAgentReplyText('');
setIsStreamingAgentReply(false);
setGeneratedCustomWorldProfile(null);
setCustomWorldAutoSaveError(null);
setCustomWorldAutoSaveState('idle');
@@ -1058,11 +1120,7 @@ export function PreGameSelectionFlow({
customWorldAutoSaveTimeoutRef.current = null;
}
};
}, [
generatedCustomWorldProfile,
saveGeneratedCustomWorld,
selectionStage,
]);
}, [generatedCustomWorldProfile, saveGeneratedCustomWorld, selectionStage]);
const openSavedCustomWorldEditor = (
entry: CustomWorldLibraryEntry<CustomWorldProfile>,
@@ -1070,7 +1128,8 @@ export function PreGameSelectionFlow({
setSelectedDetailEntry(entry);
const normalizedProfile = normalizeAgentBackedProfile(entry.profile);
setGeneratedCustomWorldProfile(normalizedProfile);
lastAutoSavedProfileSignatureRef.current = JSON.stringify(normalizedProfile);
lastAutoSavedProfileSignatureRef.current =
JSON.stringify(normalizedProfile);
setCustomWorldAutoSaveState('saved');
setCustomWorldAutoSaveError(null);
setCustomWorldError(null);
@@ -1129,6 +1188,36 @@ export function PreGameSelectionFlow({
}
};
const handleDeleteSelectedWorld = async () => {
if (!selectedDetailEntry || isMutatingDetail) {
return;
}
const confirmed = window.confirm(
`确认删除作品《${selectedDetailEntry.worldName}》吗?删除后会从你的作品列表和公开广场中移除。`,
);
if (!confirmed) {
return;
}
setIsMutatingDetail(true);
setDetailError(null);
try {
const entries = await deleteCustomWorldProfile(
selectedDetailEntry.profileId,
);
setSavedCustomWorldEntries(entries);
setSelectedDetailEntry(null);
setPlatformTab('create');
setSelectionStage('platform');
setPublishedGalleryEntries(await listCustomWorldGallery());
} catch (error) {
setDetailError(resolveErrorMessage(error, '删除自定义世界失败。'));
} finally {
setIsMutatingDetail(false);
}
};
const isSelectedWorldOwned = Boolean(
selectedDetailEntry &&
savedCustomWorldEntries.some(
@@ -1228,6 +1317,9 @@ export function PreGameSelectionFlow({
? handleUnpublishSelectedWorld
: null
}
onDelete={
isSelectedWorldOwned ? handleDeleteSelectedWorld : null
}
/>
)}
</motion.div>
@@ -1250,13 +1342,9 @@ export function PreGameSelectionFlow({
<CustomWorldAgentWorkspace
session={agentSession}
activeOperation={agentOperation}
streamingReplyText={streamingAgentReplyText}
isStreamingReply={isStreamingAgentReply}
onBack={leaveAgentWorkspace}
onRefresh={() => {
if (!activeAgentSessionId) {
return;
}
void syncAgentSessionSnapshot(activeAgentSessionId);
}}
onSubmitMessage={(payload) => {
void submitAgentMessage(payload);
}}
@@ -1290,6 +1378,7 @@ export function PreGameSelectionFlow({
>
<CustomWorldGenerationView
settingText={activeGenerationSettingText}
anchorEntries={agentDraftAnchorPreviewEntries}
progress={activeGenerationProgress}
isGenerating={isActiveGenerationRunning}
error={activeGenerationError}
@@ -1300,10 +1389,10 @@ export function PreGameSelectionFlow({
backLabel="返回工作区"
settingActionLabel="回到工作区"
retryLabel="重新生成草稿"
settingTitle="当前共创设定"
settingTitle="当前锚点信息"
settingDescription={
isAgentDraftGenerationView
? '这批锚点会被整理成第一版世界底稿与草稿卡。'
? '将按当前八锚点结构编译第一版世界底稿与草稿卡。'
: undefined
}
progressTitle={
@@ -1385,7 +1474,6 @@ export function PreGameSelectionFlow({
void openRpgAgentWorkspace();
}}
/>
</>
);
}