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