1
This commit is contained in:
@@ -17,7 +17,7 @@ const baseDraftItem: CustomWorldWorkSummary = {
|
||||
updatedAt: new Date('2026-04-14T10:00:00.000Z').toISOString(),
|
||||
publishedAt: null,
|
||||
stage: 'object_refining',
|
||||
stageLabel: '精修对象',
|
||||
stageLabel: '待完善草稿',
|
||||
playableNpcCount: 3,
|
||||
landmarkCount: 4,
|
||||
sessionId: 'session-1',
|
||||
@@ -35,7 +35,7 @@ test('creation hub reflects updated draft title summary and counts after rerende
|
||||
onBack={() => {}}
|
||||
onRetry={() => {}}
|
||||
onCreateNew={() => {}}
|
||||
onResumeDraft={() => {}}
|
||||
onOpenDraft={() => {}}
|
||||
onEnterPublished={() => {}}
|
||||
/>,
|
||||
);
|
||||
@@ -62,7 +62,7 @@ test('creation hub reflects updated draft title summary and counts after rerende
|
||||
onBack={() => {}}
|
||||
onRetry={() => {}}
|
||||
onCreateNew={() => {}}
|
||||
onResumeDraft={() => {}}
|
||||
onOpenDraft={() => {}}
|
||||
onEnterPublished={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ test('creation hub draft card renders compiled work summary fields', () => {
|
||||
onBack={() => {}}
|
||||
onRetry={() => {}}
|
||||
onCreateNew={() => {}}
|
||||
onResumeDraft={() => {}}
|
||||
onOpenDraft={() => {}}
|
||||
onEnterPublished={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
@@ -15,7 +15,7 @@ type CustomWorldCreationHubProps = {
|
||||
onBack: () => void;
|
||||
onRetry: () => void;
|
||||
onCreateNew: () => void;
|
||||
onResumeDraft: (sessionId: string) => void;
|
||||
onOpenDraft: (item: CustomWorldWorkSummary) => void;
|
||||
onEnterPublished: (profileId: string) => void;
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ export function CustomWorldCreationHub({
|
||||
onBack,
|
||||
onRetry,
|
||||
onCreateNew,
|
||||
onResumeDraft,
|
||||
onOpenDraft,
|
||||
onEnterPublished,
|
||||
}: CustomWorldCreationHubProps) {
|
||||
const [activeFilter, setActiveFilter] =
|
||||
@@ -129,7 +129,7 @@ export function CustomWorldCreationHub({
|
||||
item={item}
|
||||
onClick={() => {
|
||||
if (item.sourceType === 'agent_session' && item.sessionId) {
|
||||
onResumeDraft(item.sessionId);
|
||||
onOpenDraft(item);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,11 @@ export function CustomWorldWorkCard({
|
||||
const isDraft = item.status === 'draft';
|
||||
const hasFoundationDraft =
|
||||
item.playableNpcCount > 0 || item.landmarkCount > 0;
|
||||
const actionLabel = isDraft
|
||||
? hasFoundationDraft
|
||||
? '继续完善'
|
||||
: '继续创作'
|
||||
: '进入世界';
|
||||
const roleCountLabel = isDraft ? '角色' : '可扮演角色';
|
||||
|
||||
return (
|
||||
@@ -104,7 +109,7 @@ export function CustomWorldWorkCard({
|
||||
onClick={onClick}
|
||||
className="platform-button platform-button--primary min-h-0 shrink-0 rounded-full px-4 py-2 text-sm"
|
||||
>
|
||||
{isDraft ? (hasFoundationDraft ? '继续精修' : '继续创作') : '进入世界'}
|
||||
{actionLabel}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,13 +30,13 @@ import {
|
||||
import type {
|
||||
CustomWorldGalleryCard,
|
||||
CustomWorldLibraryEntry,
|
||||
PlatformBrowseHistoryEntry,
|
||||
ProfileDashboardCardKey,
|
||||
ProfileDashboardSummary,
|
||||
ProfileSaveArchiveSummary,
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes';
|
||||
import type { AuthUser } from '../../services/authService';
|
||||
import type { PlatformBrowseHistoryEntry } from '../../services/platformBrowseHistory';
|
||||
import type { CustomWorldProfile } from '../../types';
|
||||
import { useAuthUi } from '../auth/AuthUiContext';
|
||||
import { PlatformBrandLogo } from './PlatformBrandLogo';
|
||||
|
||||
@@ -223,6 +223,97 @@ const mockAuthUser: AuthUser = {
|
||||
wechatBound: false,
|
||||
};
|
||||
|
||||
const compiledAgentDraftSession: CustomWorldAgentSessionSnapshot = {
|
||||
...mockSession,
|
||||
stage: 'object_refining',
|
||||
creatorIntent: {
|
||||
sourceMode: 'card',
|
||||
worldHook: '被海雾吞没的旧航路群岛',
|
||||
playerPremise: '玩家回到群岛调查沉船真相。',
|
||||
themeKeywords: ['海雾', '旧航路'],
|
||||
toneDirectives: ['压抑', '悬疑'],
|
||||
openingSituation: '首夜就有陌生船只闯入禁航区。',
|
||||
coreConflicts: ['航运公会与守灯会争夺航路控制权'],
|
||||
keyFactions: [],
|
||||
keyCharacters: [],
|
||||
keyLandmarks: [],
|
||||
iconicElements: ['会移动的海雾'],
|
||||
forbiddenDirectives: [],
|
||||
rawSettingText: '',
|
||||
},
|
||||
draftProfile: {
|
||||
name: '潮雾列岛',
|
||||
subtitle: '旧灯塔与失控航路',
|
||||
summary: '第一版世界底稿已经整理完成。',
|
||||
tone: '压抑、潮湿、悬疑',
|
||||
playerGoal: '查清沉船与禁航区异动的真相。',
|
||||
majorFactions: ['守灯会', '航运公会'],
|
||||
coreConflicts: ['守灯会与航运公会争夺旧航路控制权'],
|
||||
playableNpcs: [
|
||||
{
|
||||
id: 'playable-1',
|
||||
name: '沈砺',
|
||||
title: '旧航路引路人',
|
||||
role: '关键同行者',
|
||||
publicIdentity: '最熟悉旧航路的人。',
|
||||
publicMask: '看上去像可靠旧友。',
|
||||
currentPressure: '他必须在两股势力间站队。',
|
||||
hiddenHook: '暗中替沉船商盟引路。',
|
||||
relationToPlayer: '旧友兼潜在背叛者',
|
||||
threadIds: ['thread-1'],
|
||||
summary: '他像旧友,但也像一把始终没收回鞘的刀。',
|
||||
},
|
||||
],
|
||||
storyNpcs: [
|
||||
{
|
||||
id: 'story-1',
|
||||
name: '顾潮音',
|
||||
title: '守灯会值夜人',
|
||||
role: '场景关键角色',
|
||||
publicIdentity: '负责夜间巡灯与封锁。',
|
||||
publicMask: '对外一直冷静克制。',
|
||||
currentPressure: '她知道更多禁航区真相。',
|
||||
hiddenHook: '曾亲眼见过失控海雾吞船。',
|
||||
relationToPlayer: '最早愿意交换线索的人',
|
||||
threadIds: ['thread-1'],
|
||||
summary: '她总像比所有人更早知道海雾会往哪一侧压下来。',
|
||||
},
|
||||
],
|
||||
landmarks: [
|
||||
{
|
||||
id: 'landmark-1',
|
||||
name: '回潮旧灯塔',
|
||||
purpose: '观察雾潮与往来船只',
|
||||
mood: '潮湿、压抑、风声不止',
|
||||
importance: '开局核心场景',
|
||||
characterIds: ['story-1'],
|
||||
threadIds: ['thread-1'],
|
||||
summary: '旧灯塔是整片群岛最先看见异动的地方。',
|
||||
},
|
||||
],
|
||||
factions: [],
|
||||
threads: [],
|
||||
chapters: [],
|
||||
worldHook: '被海雾吞没的旧航路群岛',
|
||||
playerPremise: '玩家回到群岛调查沉船真相。',
|
||||
openingSituation: '首夜就有陌生船只闯入禁航区。',
|
||||
iconicElements: ['会移动的海雾'],
|
||||
sourceAnchorSummary: '海雾、旧灯塔、失控航路。',
|
||||
},
|
||||
draftCards: [
|
||||
{
|
||||
id: 'world-foundation',
|
||||
kind: 'world',
|
||||
title: '潮雾列岛',
|
||||
subtitle: '旧灯塔与失控航路',
|
||||
summary: '第一版世界底稿已经整理完成。',
|
||||
status: 'warning',
|
||||
linkedIds: ['playable-1', 'story-1', 'landmark-1'],
|
||||
warningCount: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
type TestAuthValue = {
|
||||
user: AuthUser | null;
|
||||
openLoginModal: (postLoginAction?: (() => void) | null) => void;
|
||||
@@ -416,7 +507,7 @@ test('create tab opens game type modal, keeps AIRP and visual novel locked, and
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test('create tab uses unified creation hub and can resume an agent draft', async () => {
|
||||
test('create tab opens compiled agent draft in result refinement page', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
vi.mocked(listCustomWorldWorks).mockResolvedValue([
|
||||
@@ -425,7 +516,7 @@ test('create tab uses unified creation hub and can resume an agent draft', async
|
||||
sourceType: 'agent_session',
|
||||
status: 'draft',
|
||||
title: '潮雾列岛',
|
||||
subtitle: '精修对象',
|
||||
subtitle: '待完善草稿',
|
||||
summary: '玩家是失职返乡的守灯人。',
|
||||
coverImageSrc: null,
|
||||
coverRenderMode: 'image',
|
||||
@@ -433,7 +524,7 @@ test('create tab uses unified creation hub and can resume an agent draft', async
|
||||
updatedAt: '2026-04-20T10:00:00.000Z',
|
||||
publishedAt: null,
|
||||
stage: 'object_refining',
|
||||
stageLabel: '精修对象',
|
||||
stageLabel: '待完善草稿',
|
||||
playableNpcCount: 3,
|
||||
landmarkCount: 4,
|
||||
roleVisualReadyCount: 1,
|
||||
@@ -445,21 +536,70 @@ test('create tab uses unified creation hub and can resume an agent draft', async
|
||||
canEnterWorld: false,
|
||||
},
|
||||
]);
|
||||
vi.mocked(getCustomWorldAgentSession).mockResolvedValue(
|
||||
compiledAgentDraftSession,
|
||||
);
|
||||
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreationHub(user);
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: /继续精修/u }),
|
||||
).toBeTruthy();
|
||||
expect(screen.getByRole('button', { name: /继续精修/u })).toBeTruthy();
|
||||
expect(screen.getByRole('button', { name: /继续完善/u })).toBeTruthy();
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /继续精修/u }));
|
||||
await user.click(screen.getByRole('button', { name: /继续完善/u }));
|
||||
|
||||
await waitFor(
|
||||
async () => {
|
||||
expect(await screen.findByText('世界档案')).toBeTruthy();
|
||||
expect(screen.queryByText('Agent工作区:custom-world-agent-session-1')).toBeNull();
|
||||
expect(screen.getByRole('button', { name: /返回创作/u })).toBeTruthy();
|
||||
},
|
||||
{ timeout: 2500 },
|
||||
);
|
||||
});
|
||||
|
||||
test('create tab resumes agent workspace when draft has no compiled result yet', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
vi.mocked(listCustomWorldWorks).mockResolvedValue([
|
||||
{
|
||||
workId: 'draft:custom-world-agent-session-1',
|
||||
sourceType: 'agent_session',
|
||||
status: 'draft',
|
||||
title: '潮雾列岛',
|
||||
subtitle: '补齐关键锚点',
|
||||
summary: '玩家是失职返乡的守灯人。',
|
||||
coverImageSrc: null,
|
||||
coverRenderMode: 'image',
|
||||
coverCharacterImageSrcs: [],
|
||||
updatedAt: '2026-04-20T10:00:00.000Z',
|
||||
publishedAt: null,
|
||||
stage: 'clarifying',
|
||||
stageLabel: '补齐关键锚点',
|
||||
playableNpcCount: 0,
|
||||
landmarkCount: 0,
|
||||
roleVisualReadyCount: 0,
|
||||
roleAnimationReadyCount: 0,
|
||||
roleAssetSummaryLabel: null,
|
||||
sessionId: 'custom-world-agent-session-1',
|
||||
profileId: null,
|
||||
canResume: true,
|
||||
canEnterWorld: false,
|
||||
},
|
||||
]);
|
||||
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreationHub(user);
|
||||
|
||||
expect(screen.getByRole('button', { name: /继续创作/u })).toBeTruthy();
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /继续创作/u }));
|
||||
|
||||
expect(
|
||||
await screen.findByText('Agent工作区:custom-world-agent-session-1'),
|
||||
).toBeTruthy();
|
||||
expect(screen.queryByText('世界档案')).toBeNull();
|
||||
});
|
||||
|
||||
test('clicking a public work while logged out routes through requireAuth', async () => {
|
||||
@@ -581,7 +721,7 @@ test('starting draft generation leaves the agent workspace and shows the generat
|
||||
expect(screen.queryByText('先告诉我你想做一个怎样的 RPG 世界。')).toBeNull();
|
||||
});
|
||||
|
||||
test('existing draft sessions enter the agent preview layout without opening legacy editor', async () => {
|
||||
test('existing draft sessions open result page refinement instead of agent dialog', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
vi.mocked(getCustomWorldAgentOperation).mockResolvedValue({
|
||||
@@ -593,96 +733,9 @@ test('existing draft sessions enter the agent preview layout without opening leg
|
||||
progress: 100,
|
||||
error: null,
|
||||
});
|
||||
vi.mocked(getCustomWorldAgentSession).mockResolvedValue({
|
||||
...mockSession,
|
||||
stage: 'object_refining',
|
||||
creatorIntent: {
|
||||
sourceMode: 'card',
|
||||
worldHook: '被海雾吞没的旧航路群岛',
|
||||
playerPremise: '玩家回到群岛调查沉船真相。',
|
||||
themeKeywords: ['海雾', '旧航路'],
|
||||
toneDirectives: ['压抑', '悬疑'],
|
||||
openingSituation: '首夜就有陌生船只闯入禁航区。',
|
||||
coreConflicts: ['航运公会与守灯会争夺航路控制权'],
|
||||
keyFactions: [],
|
||||
keyCharacters: [],
|
||||
keyLandmarks: [],
|
||||
iconicElements: ['会移动的海雾'],
|
||||
forbiddenDirectives: [],
|
||||
rawSettingText: '',
|
||||
},
|
||||
draftProfile: {
|
||||
name: '潮雾列岛',
|
||||
subtitle: '旧灯塔与失控航路',
|
||||
summary: '第一版世界底稿已经整理完成。',
|
||||
tone: '压抑、潮湿、悬疑',
|
||||
playerGoal: '查清沉船与禁航区异动的真相。',
|
||||
majorFactions: ['守灯会', '航运公会'],
|
||||
coreConflicts: ['守灯会与航运公会争夺旧航路控制权'],
|
||||
playableNpcs: [
|
||||
{
|
||||
id: 'playable-1',
|
||||
name: '沈砺',
|
||||
title: '旧航路引路人',
|
||||
role: '关键同行者',
|
||||
publicIdentity: '最熟悉旧航路的人。',
|
||||
publicMask: '看上去像可靠旧友。',
|
||||
currentPressure: '他必须在两股势力间站队。',
|
||||
hiddenHook: '暗中替沉船商盟引路。',
|
||||
relationToPlayer: '旧友兼潜在背叛者',
|
||||
threadIds: ['thread-1'],
|
||||
summary: '他像旧友,但也像一把始终没收回鞘的刀。',
|
||||
},
|
||||
],
|
||||
storyNpcs: [
|
||||
{
|
||||
id: 'story-1',
|
||||
name: '顾潮音',
|
||||
title: '守灯会值夜人',
|
||||
role: '场景关键角色',
|
||||
publicIdentity: '负责夜间巡灯与封锁。',
|
||||
publicMask: '对外一直冷静克制。',
|
||||
currentPressure: '她知道更多禁航区真相。',
|
||||
hiddenHook: '曾亲眼见过失控海雾吞船。',
|
||||
relationToPlayer: '最早愿意交换线索的人',
|
||||
threadIds: ['thread-1'],
|
||||
summary: '她总像比所有人更早知道海雾会往哪一侧压下来。',
|
||||
},
|
||||
],
|
||||
landmarks: [
|
||||
{
|
||||
id: 'landmark-1',
|
||||
name: '回潮旧灯塔',
|
||||
purpose: '观察雾潮与往来船只',
|
||||
mood: '潮湿、压抑、风声不止',
|
||||
importance: '开局核心场景',
|
||||
characterIds: ['story-1'],
|
||||
threadIds: ['thread-1'],
|
||||
summary: '旧灯塔是整片群岛最先看见异动的地方。',
|
||||
},
|
||||
],
|
||||
factions: [],
|
||||
threads: [],
|
||||
chapters: [],
|
||||
worldHook: '被海雾吞没的旧航路群岛',
|
||||
playerPremise: '玩家回到群岛调查沉船真相。',
|
||||
openingSituation: '首夜就有陌生船只闯入禁航区。',
|
||||
iconicElements: ['会移动的海雾'],
|
||||
sourceAnchorSummary: '海雾、旧灯塔、失控航路。',
|
||||
},
|
||||
draftCards: [
|
||||
{
|
||||
id: 'world-foundation',
|
||||
kind: 'world',
|
||||
title: '潮雾列岛',
|
||||
subtitle: '旧灯塔与失控航路',
|
||||
summary: '第一版世界底稿已经整理完成。',
|
||||
status: 'warning',
|
||||
linkedIds: ['playable-1', 'story-1', 'landmark-1'],
|
||||
warningCount: 0,
|
||||
},
|
||||
],
|
||||
});
|
||||
vi.mocked(getCustomWorldAgentSession).mockResolvedValue(
|
||||
compiledAgentDraftSession,
|
||||
);
|
||||
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
@@ -704,12 +757,12 @@ test('existing draft sessions enter the agent preview layout without opening leg
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /场景角色/u }));
|
||||
expect(screen.getByRole('button', { name: /顾潮音/u })).toBeTruthy();
|
||||
expect(screen.queryByText(/编辑场景角色:顾潮音/u)).toBeNull();
|
||||
expect(screen.queryByRole('button', { name: /AI生成/u })).toBeNull();
|
||||
expect(screen.queryByText('技能')).toBeNull();
|
||||
await user.click(screen.getByRole('button', { name: /顾潮音/u }));
|
||||
expect(await screen.findByText(/编辑场景角色:顾潮音/u)).toBeTruthy();
|
||||
expect(screen.getByRole('button', { name: /AI生成/u })).toBeTruthy();
|
||||
});
|
||||
|
||||
test('agent draft result back button returns to workspace without redundant sync when session is already latest', async () => {
|
||||
test('agent draft result back button returns to creation hub without redundant sync when session is already latest', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
vi.mocked(executeCustomWorldAgentAction).mockResolvedValue({
|
||||
@@ -843,7 +896,7 @@ test('agent draft result back button returns to workspace without redundant sync
|
||||
} satisfies CustomWorldAgentSessionSnapshot;
|
||||
vi.mocked(getCustomWorldAgentSession).mockResolvedValue(resultSession);
|
||||
|
||||
render(<TestWrapper />);
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openNewRpgCreation(user);
|
||||
|
||||
@@ -858,9 +911,7 @@ test('agent draft result back button returns to workspace without redundant sync
|
||||
await user.click(screen.getByRole('button', { name: /返回创作/u }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText('Agent工作区:custom-world-agent-session-1'),
|
||||
).toBeTruthy();
|
||||
expect(screen.getByText('创作中心')).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(
|
||||
@@ -968,7 +1019,7 @@ test('agent draft result auto-save persists the latest profile rebuilt from sync
|
||||
} satisfies CustomWorldAgentSessionSnapshot;
|
||||
vi.mocked(getCustomWorldAgentSession).mockResolvedValue(syncedSession);
|
||||
|
||||
render(<TestWrapper />);
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openNewRpgCreation(user);
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import type {
|
||||
import type {
|
||||
CustomWorldGalleryCard,
|
||||
CustomWorldLibraryEntry,
|
||||
PlatformBrowseHistoryEntry,
|
||||
PlatformBrowseHistoryWriteEntry,
|
||||
ProfileDashboardSummary,
|
||||
ProfileSaveArchiveSummary,
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
@@ -46,14 +48,6 @@ import {
|
||||
writeCustomWorldAgentUiState,
|
||||
} from '../../services/customWorldAgentUiState';
|
||||
import { buildCustomWorldCreatorIntentFoundationText } from '../../services/customWorldCreatorIntent';
|
||||
import {
|
||||
hasPendingPlatformBrowseHistoryMigration,
|
||||
markPlatformBrowseHistoryMigrated,
|
||||
type PlatformBrowseHistoryEntry,
|
||||
type PlatformBrowseHistoryWriteEntry,
|
||||
readPlatformBrowseHistory,
|
||||
writePlatformBrowseHistory,
|
||||
} from '../../services/platformBrowseHistory';
|
||||
import {
|
||||
deleteCustomWorldProfile,
|
||||
getCustomWorldGalleryDetail,
|
||||
@@ -64,7 +58,6 @@ import {
|
||||
listProfileSaveArchives,
|
||||
publishCustomWorldProfile,
|
||||
resumeProfileSaveArchive,
|
||||
syncProfileBrowseHistory,
|
||||
unpublishCustomWorldProfile,
|
||||
upsertCustomWorldProfile,
|
||||
upsertProfileBrowseHistory,
|
||||
@@ -381,18 +374,11 @@ export function PreGameSelectionFlow({
|
||||
|
||||
const appendBrowseHistoryEntry = useCallback(
|
||||
async (entry: PlatformBrowseHistoryWriteEntry) => {
|
||||
const nextEntries = writePlatformBrowseHistory(authUi?.user, entry);
|
||||
setHistoryEntries(nextEntries);
|
||||
setHistoryError(null);
|
||||
|
||||
if (!authUi?.user) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const syncedEntries = await upsertProfileBrowseHistory(entry);
|
||||
setHistoryEntries(syncedEntries);
|
||||
markPlatformBrowseHistoryMigrated(authUi?.user);
|
||||
} catch (error) {
|
||||
setHistoryError(resolveErrorMessage(error, '写入浏览历史失败。'));
|
||||
}
|
||||
@@ -444,8 +430,7 @@ export function PreGameSelectionFlow({
|
||||
let isActive = true;
|
||||
|
||||
void (async () => {
|
||||
const localHistoryEntries = readPlatformBrowseHistory(authUi?.user);
|
||||
setHistoryEntries(localHistoryEntries);
|
||||
setHistoryEntries([]);
|
||||
setHistoryError(null);
|
||||
setSaveError(null);
|
||||
setIsLoadingPlatform(true);
|
||||
@@ -472,22 +457,7 @@ export function PreGameSelectionFlow({
|
||||
isAuthenticated ? listCustomWorldWorks() : Promise.resolve([]),
|
||||
listCustomWorldGallery(),
|
||||
isAuthenticated ? getProfileDashboard() : Promise.resolve(null),
|
||||
isAuthenticated
|
||||
? (async () => {
|
||||
let nextEntries = await listProfileBrowseHistory();
|
||||
|
||||
if (
|
||||
hasPendingPlatformBrowseHistoryMigration(authUi?.user) &&
|
||||
localHistoryEntries.length > 0
|
||||
) {
|
||||
nextEntries =
|
||||
await syncProfileBrowseHistory(localHistoryEntries);
|
||||
markPlatformBrowseHistoryMigrated(authUi?.user);
|
||||
}
|
||||
|
||||
return nextEntries;
|
||||
})()
|
||||
: Promise.resolve(localHistoryEntries),
|
||||
isAuthenticated ? listProfileBrowseHistory() : Promise.resolve([]),
|
||||
isAuthenticated ? listProfileSaveArchives() : Promise.resolve([]),
|
||||
]);
|
||||
if (!isActive) {
|
||||
@@ -881,8 +851,6 @@ export function PreGameSelectionFlow({
|
||||
const isAgentDraftGenerationView =
|
||||
customWorldGenerationViewSource === 'agent-draft-foundation';
|
||||
const isAgentDraftResultView = customWorldResultViewSource === 'agent-draft';
|
||||
const isAgentDraftResultEditingFrozen =
|
||||
customWorldResultViewSource === 'agent-draft';
|
||||
const activeGenerationSettingText = agentDraftSettingPreview;
|
||||
const activeGenerationProgress = agentDraftGenerationProgress;
|
||||
const isActiveGenerationRunning =
|
||||
@@ -1096,7 +1064,8 @@ export function PreGameSelectionFlow({
|
||||
setCustomWorldAutoSaveState('idle');
|
||||
setCustomWorldGenerationViewSource(null);
|
||||
setCustomWorldResultViewSource(null);
|
||||
setSelectionStage('agent-workspace');
|
||||
setPlatformTab('create');
|
||||
setSelectionStage('platform');
|
||||
};
|
||||
|
||||
const retryAgentDraftGeneration = () => {
|
||||
@@ -1133,17 +1102,39 @@ export function PreGameSelectionFlow({
|
||||
const handleOpenCreationWork = useCallback(
|
||||
async (work: CustomWorldWorkSummary) => {
|
||||
if (work.status === 'draft' && work.sessionId) {
|
||||
// 阶段二要求草稿优先回到 Agent 工作区,而不是再次自动顶回结果页。
|
||||
isAgentDraftResultAutoOpenSuppressedRef.current = true;
|
||||
persistAgentUiState(work.sessionId, null);
|
||||
setGeneratedCustomWorldProfile(null);
|
||||
setCustomWorldError(null);
|
||||
setCustomWorldAutoSaveError(null);
|
||||
setCustomWorldAutoSaveState('idle');
|
||||
setCustomWorldGenerationViewSource(null);
|
||||
setCustomWorldResultViewSource(null);
|
||||
|
||||
const shouldOpenAgentWorkspace =
|
||||
work.playableNpcCount <= 0 && work.landmarkCount <= 0;
|
||||
|
||||
if (shouldOpenAgentWorkspace) {
|
||||
// 仅八锚点未整理成底稿时才恢复 Agent 对话工作区。
|
||||
isAgentDraftResultAutoOpenSuppressedRef.current = true;
|
||||
setGeneratedCustomWorldProfile(null);
|
||||
setCustomWorldResultViewSource(null);
|
||||
setPlatformTab('create');
|
||||
setSelectionStage('agent-workspace');
|
||||
return;
|
||||
}
|
||||
|
||||
isAgentDraftResultAutoOpenSuppressedRef.current = false;
|
||||
const latestSession = await syncAgentSessionSnapshot(work.sessionId);
|
||||
const nextProfile = buildCustomWorldProfileFromAgentDraft(latestSession);
|
||||
if (!nextProfile) {
|
||||
setPlatformError('当前草稿还没有可编辑的结果页数据,请先继续补齐锚点。');
|
||||
setPlatformTab('create');
|
||||
setSelectionStage('agent-workspace');
|
||||
return;
|
||||
}
|
||||
|
||||
setGeneratedCustomWorldProfile(normalizeAgentBackedProfile(nextProfile));
|
||||
setCustomWorldResultViewSource('agent-draft');
|
||||
setPlatformTab('create');
|
||||
setSelectionStage('agent-workspace');
|
||||
setSelectionStage('custom-world-result');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1179,6 +1170,7 @@ export function PreGameSelectionFlow({
|
||||
openLibraryDetail,
|
||||
persistAgentUiState,
|
||||
savedCustomWorldEntries,
|
||||
syncAgentSessionSnapshot,
|
||||
setSelectionStage,
|
||||
],
|
||||
);
|
||||
@@ -1583,32 +1575,9 @@ export function PreGameSelectionFlow({
|
||||
});
|
||||
}}
|
||||
onCreateNew={openCreationTypePicker}
|
||||
onResumeDraft={(sessionId) => {
|
||||
onOpenDraft={(item) => {
|
||||
runProtectedAction(() => {
|
||||
void handleOpenCreationWork({
|
||||
workId: `draft:${sessionId}`,
|
||||
sourceType: 'agent_session',
|
||||
status: 'draft',
|
||||
title: '',
|
||||
subtitle: '',
|
||||
summary: '',
|
||||
coverImageSrc: null,
|
||||
coverRenderMode: 'image',
|
||||
coverCharacterImageSrcs: [],
|
||||
updatedAt: new Date().toISOString(),
|
||||
publishedAt: null,
|
||||
stage: null,
|
||||
stageLabel: '',
|
||||
playableNpcCount: 0,
|
||||
landmarkCount: 0,
|
||||
roleVisualReadyCount: 0,
|
||||
roleAnimationReadyCount: 0,
|
||||
roleAssetSummaryLabel: null,
|
||||
sessionId,
|
||||
profileId: null,
|
||||
canResume: true,
|
||||
canEnterWorld: false,
|
||||
});
|
||||
void handleOpenCreationWork(item);
|
||||
});
|
||||
}}
|
||||
onEnterPublished={(profileId) => {
|
||||
@@ -1918,10 +1887,10 @@ export function PreGameSelectionFlow({
|
||||
});
|
||||
});
|
||||
}}
|
||||
readOnly={isAgentDraftResultEditingFrozen}
|
||||
readOnly={false}
|
||||
compactAgentResultMode={isAgentDraftResultView}
|
||||
backLabel={isAgentDraftResultView ? '返回创作' : undefined}
|
||||
editActionLabel="去Agent调整设定"
|
||||
editActionLabel="继续调整设定"
|
||||
enterWorldActionLabel="进入世界"
|
||||
autoSaveState={customWorldAutoSaveState}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user