Merge pull request 'fix: return draft results to shelf' (#37) from codex/fix-draft-result-back-target into master

Reviewed-on: #37
This commit was merged in pull request #37.
This commit is contained in:
2026-05-26 16:54:39 +08:00
4 changed files with 184 additions and 56 deletions

View File

@@ -31,6 +31,14 @@
- 验证:`npm run test -- src/services/creationUrlState.test.ts src/routing/appPageRoutes.test.ts src/components/platform-entry/usePlatformCreationAgentFlowController.test.tsx`;手测生成页 / 结果页刷新仍恢复同一草稿,打开公开作品详情 URL 不带私有恢复参数。
- 关联:`src/services/creationUrlState.ts``src/routing/appPageRoutes.ts``src/components/platform-entry/PlatformEntryFlowShellImpl.tsx``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
## 草稿作品架打开结果页返回必须回草稿 Tab
- 现象:从草稿 Tab 作品架点击已有草稿进入结果页后,点结果页返回会跳回创作 Tab 模板入口,用户需要重新切回草稿页才能继续找原草稿。
- 原因:平台壳层只按结果页类型硬编码返回创作入口,没有记录本次创作流是从草稿作品架打开;如果来源标记没有在新建入口时重置,还可能污染下一条创作链路。
- 处理:从作品架打开任一玩法草稿时标记返回目标为 `draft-shelf`;从创作 Tab 新建、打开模板或退出非草稿来源工作区时重置为 `create`;结果页返回和工作区退出统一消费这个返回目标,并在消费后复位。
- 验证:`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "puzzle draft result back button returns to draft hub when opened from shelf|agent draft result back button returns to draft hub without syncing result profile"`
- 关联:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx``src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
## 拼图生成页轮询不要绑展示 phase 或不稳定 setter
- 现象:拼图创作进入生成中页后,`/api/runtime/puzzle/agent/sessions/{sessionId}` 会在 0.3 到 0.5 秒内被反复 GET看起来像轮询风暴而不是 3 秒一次的正常刷新。

View File

@@ -42,7 +42,8 @@
4. 生成中作品在整卡上加等待遮罩,但不移除作品基础信息。
5. 生成中状态不能只存在前端内存 notice。后端作品摘要必须下发可恢复的 `generationStatus`;前端刷新或退出产品后,作品架优先用摘要状态恢复等待遮罩,本轮内存 notice 只作为即时反馈。
6. 点击 `generationStatus=generating` 的草稿卡必须恢复对应玩法的生成进度页,不能进入空白结果页或普通工作区;恢复生成页的 `startedAtMs` 使用进入生成页的当前时间,作品摘要 `updatedAt` 只用于排序和摘要展示,不参与假进度起算。
7. 私有 generated 图片必须通过 `ResolvedAssetImage` / `/api/assets/read-url` 换签读取
7. 从草稿 Tab 作品架打开草稿工作区、生成页或结果页时,返回按钮必须回到草稿 Tab 的同一作品架语境;从创作 Tab 新建或直接进入创作链路时才回到创作 Tab。平台壳层需要显式记录本次创作流的返回来源不能让结果页返回动作固定跳到创作入口
8. 私有 generated 图片必须通过 `ResolvedAssetImage` / `/api/assets/read-url` 换签读取。
发现 Tab、创作 Tab 与草稿 Tab 的页面根内容区不再套 `platform-page-stage` 外层全局卡片壳,让列表、筛选和玩法卡获得更宽的横向空间;推荐页和我的页仍按各自页面设计保留原有全局卡片口径。移动端“我的”页仍按顶部头像 / 昵称 / 陶泥号、会员横幅、三张统计卡、每日任务、五项常用功能宫格、设置入口、次级入口带和法律信息组织,但字号必须维持平台普通 UI 档位,不能因为窄屏把卡片标题、功能 label 或法律信息撑成展示级字号;最后一屏内容必须能在底部 dock 上方完整滚动露出,不得被固定底部导航遮挡。

View File

@@ -462,6 +462,7 @@ type PendingDraftShelfMap = Partial<
Record<string, PendingDraftShelfState>
>
>;
type CreationFlowReturnTarget = 'create' | 'draft-shelf';
type Match3DBackgroundCompileTask = {
session: Match3DAgentSessionSnapshot;
payload: CreateMatch3DSessionRequest;
@@ -3313,6 +3314,8 @@ export function PlatformEntryFlowShellImpl({
);
const handledInitialPublicWorkCodeRef = useRef<string | null>(null);
const selectionStageRef = useRef(selectionStage);
const creationFlowReturnTargetRef =
useRef<CreationFlowReturnTarget>('create');
const activeMatch3DGenerationSessionIdRef = useRef<string | null>(null);
const activePuzzleGenerationSessionIdRef = useRef<string | null>(null);
const [draftGenerationNotices, setDraftGenerationNotices] =
@@ -3565,11 +3568,34 @@ export function PlatformEntryFlowShellImpl({
const enterDraftTab = useCallback(() => {
setPlatformTab('saves');
}, [setPlatformTab]);
const markCreationFlowReturnToCreate = useCallback(() => {
creationFlowReturnTargetRef.current = 'create';
}, []);
const markCreationFlowReturnToDraftShelf = useCallback(() => {
creationFlowReturnTargetRef.current = 'draft-shelf';
}, []);
const returnToCreationFlowSource = useCallback(() => {
const returnTarget = creationFlowReturnTargetRef.current;
creationFlowReturnTargetRef.current = 'create';
clearCreationUrlState();
if (returnTarget === 'draft-shelf') {
enterDraftTab();
} else {
enterCreateTab();
}
selectionStageRef.current = 'platform';
setSelectionStage('platform');
}, [enterCreateTab, enterDraftTab, setSelectionStage]);
const shouldReturnToDraftShelf = useCallback(
() => creationFlowReturnTargetRef.current === 'draft-shelf',
[],
);
const returnToCreationCenterFromGeneration = useCallback(() => {
markCreationFlowReturnToCreate();
enterCreateTab();
selectionStageRef.current = 'platform';
setSelectionStage('platform');
}, [enterCreateTab, setSelectionStage]);
}, [enterCreateTab, markCreationFlowReturnToCreate, setSelectionStage]);
const isViewingMatch3DGeneration = useCallback((sessionId: string) => {
return (
selectionStageRef.current === 'match3d-generating' &&
@@ -4745,9 +4771,15 @@ export function PlatformEntryFlowShellImpl({
handleStartNewGame();
}
markCreationFlowReturnToCreate();
sessionController.setCreationTypeError(null);
return true;
}, [handleStartNewGame, hasSavedGame, sessionController]);
}, [
handleStartNewGame,
hasSavedGame,
markCreationFlowReturnToCreate,
sessionController,
]);
const openCreationTypePicker = useCallback(() => {
if (!prepareCreationLaunch()) {
@@ -5948,11 +5980,13 @@ export function PlatformEntryFlowShellImpl({
]);
const openBigFishAgentWorkspace = useCallback(async () => {
markCreationFlowReturnToCreate();
setBigFishRun(null);
await bigFishFlow.openWorkspace();
}, [bigFishFlow]);
}, [bigFishFlow, markCreationFlowReturnToCreate]);
const openSquareHoleAgentWorkspace = useCallback(async () => {
markCreationFlowReturnToCreate();
setSquareHoleSession(null);
setSquareHoleProfile(null);
setSquareHoleRun(null);
@@ -5970,9 +6004,11 @@ export function PlatformEntryFlowShellImpl({
setSquareHoleSession,
setStreamingSquareHoleReplyText,
squareHoleFlow,
markCreationFlowReturnToCreate,
]);
const openMatch3DWorkspace = useCallback(() => {
markCreationFlowReturnToCreate();
setMatch3DRun(null);
setMatch3DProfile(null);
setMatch3DRuntimeProfile(null);
@@ -5989,9 +6025,11 @@ export function PlatformEntryFlowShellImpl({
setMatch3DError,
setSelectionStage,
setStreamingMatch3DReplyText,
markCreationFlowReturnToCreate,
]);
const openJumpHopWorkspace = useCallback(() => {
markCreationFlowReturnToCreate();
setJumpHopError(null);
setJumpHopSession(null);
setJumpHopWork(null);
@@ -6000,9 +6038,10 @@ export function PlatformEntryFlowShellImpl({
enterCreateTab();
setShowCreationTypeModal(false);
setSelectionStage('jump-hop-workspace');
}, [enterCreateTab, setSelectionStage]);
}, [enterCreateTab, markCreationFlowReturnToCreate, setSelectionStage]);
const openWoodenFishWorkspace = useCallback(() => {
markCreationFlowReturnToCreate();
setWoodenFishError(null);
setWoodenFishSession(null);
setWoodenFishWork(null);
@@ -6011,9 +6050,10 @@ export function PlatformEntryFlowShellImpl({
enterCreateTab();
setShowCreationTypeModal(false);
setSelectionStage('wooden-fish-workspace');
}, [enterCreateTab, setSelectionStage]);
}, [enterCreateTab, markCreationFlowReturnToCreate, setSelectionStage]);
const openPuzzleWorkspace = useCallback(() => {
markCreationFlowReturnToCreate();
enterCreateTab();
setShowCreationTypeModal(false);
setPuzzleCreationError(null);
@@ -6024,9 +6064,11 @@ export function PlatformEntryFlowShellImpl({
setPuzzleCreationError,
setPuzzleError,
setSelectionStage,
markCreationFlowReturnToCreate,
]);
const openBarkBattleWorkspace = useCallback(() => {
markCreationFlowReturnToCreate();
setBarkBattleDraftConfig(null);
setBarkBattlePublishedConfig(null);
setBarkBattleRuntimeMode('draft');
@@ -6038,15 +6080,17 @@ export function PlatformEntryFlowShellImpl({
setShowCreationTypeModal(false);
selectionStageRef.current = 'bark-battle-workspace';
setSelectionStage('bark-battle-workspace');
}, [enterCreateTab, setSelectionStage]);
}, [enterCreateTab, markCreationFlowReturnToCreate, setSelectionStage]);
const openVisualNovelWorkspace = useCallback(() => {
markCreationFlowReturnToCreate();
enterCreateTab();
setShowCreationTypeModal(false);
setVisualNovelError(null);
setSelectionStage('visual-novel-agent-workspace');
}, [
enterCreateTab,
markCreationFlowReturnToCreate,
setSelectionStage,
setVisualNovelError,
]);
@@ -6057,6 +6101,7 @@ export function PlatformEntryFlowShellImpl({
return;
}
markCreationFlowReturnToCreate();
enterCreateTab();
setShowCreationTypeModal(false);
setBabyObjectMatchError(null);
@@ -6064,6 +6109,7 @@ export function PlatformEntryFlowShellImpl({
}, [
enterCreateTab,
isBabyObjectMatchVisible,
markCreationFlowReturnToCreate,
sessionController,
setBabyObjectMatchError,
setSelectionStage,
@@ -6082,9 +6128,15 @@ export function PlatformEntryFlowShellImpl({
setIsCreativeAgentStreaming(false);
setCreativeDraftEditError(null);
setIsCreativeDraftEditBusy(false);
markCreationFlowReturnToCreate();
enterCreateTab();
setSelectionStage('platform');
}, [creativeAgentSession, enterCreateTab, setSelectionStage]);
}, [
creativeAgentSession,
enterCreateTab,
markCreationFlowReturnToCreate,
setSelectionStage,
]);
const openCreativeAgentWorkspace = useCallback(async () => {
if (isCreativeAgentBusy || isCreativeAgentStreaming) {
@@ -6092,6 +6144,7 @@ export function PlatformEntryFlowShellImpl({
}
setPuzzleRun(null);
markCreationFlowReturnToCreate();
setPuzzleRuntimeAuthMode('default');
setPuzzleOperation(null);
setPuzzleGenerationState(null);
@@ -6120,6 +6173,7 @@ export function PlatformEntryFlowShellImpl({
enterCreateTab,
isCreativeAgentBusy,
isCreativeAgentStreaming,
markCreationFlowReturnToCreate,
resolvePuzzleErrorMessage,
setSelectionStage,
]);
@@ -6965,9 +7019,9 @@ export function PlatformEntryFlowShellImpl({
setBigFishRuntimeStartedAt(null);
setBigFishRuntimeReturnStage('platform');
setBigFishGenerationState(null);
clearCreationUrlState();
bigFishFlow.leaveFlow();
}, [bigFishFlow]);
returnToCreationFlowSource();
}, [bigFishFlow, returnToCreationFlowSource]);
const leaveMatch3DFlow = useCallback(() => {
setMatch3DRun(null);
@@ -6975,17 +7029,17 @@ export function PlatformEntryFlowShellImpl({
setMatch3DFormDraftPayload(null);
setMatch3DGenerationState(null);
setMatch3DRuntimeReturnStage('match3d-result');
clearCreationUrlState();
match3dFlow.leaveFlow();
}, [match3dFlow, setMatch3DFormDraftPayload]);
returnToCreationFlowSource();
}, [match3dFlow, returnToCreationFlowSource, setMatch3DFormDraftPayload]);
const leaveSquareHoleFlow = useCallback(() => {
setSquareHoleRun(null);
setSquareHoleRuntimeReturnStage('square-hole-result');
setSquareHoleGenerationState(null);
clearCreationUrlState();
squareHoleFlow.leaveFlow();
}, [squareHoleFlow]);
returnToCreationFlowSource();
}, [returnToCreationFlowSource, squareHoleFlow]);
const leaveJumpHopFlow = useCallback(() => {
setJumpHopRun(null);
@@ -6994,9 +7048,8 @@ export function PlatformEntryFlowShellImpl({
setJumpHopGenerationState(null);
setJumpHopSession(null);
setJumpHopError(null);
clearCreationUrlState();
setSelectionStage('platform');
}, [setSelectionStage]);
returnToCreationFlowSource();
}, [returnToCreationFlowSource]);
const leaveWoodenFishFlow = useCallback(() => {
setWoodenFishRun(null);
@@ -7005,9 +7058,8 @@ export function PlatformEntryFlowShellImpl({
setWoodenFishGenerationState(null);
setWoodenFishSession(null);
setWoodenFishError(null);
clearCreationUrlState();
setSelectionStage('platform');
}, [setSelectionStage]);
returnToCreationFlowSource();
}, [returnToCreationFlowSource]);
const createReadyJumpHopGenerationState = useCallback(
(state: MiniGameDraftGenerationState) =>
@@ -7035,10 +7087,8 @@ export function PlatformEntryFlowShellImpl({
setBarkBattleError(null);
setBarkBattleGenerationPartialFailed(false);
setIsBarkBattleBusy(false);
clearCreationUrlState();
selectionStageRef.current = 'platform';
setSelectionStage('platform');
}, [setSelectionStage]);
returnToCreationFlowSource();
}, [returnToCreationFlowSource]);
const createBarkBattleGeneratingDraft = useCallback(
async (payload: BarkBattleConfigEditorPayload) => {
@@ -7319,10 +7369,10 @@ export function PlatformEntryFlowShellImpl({
setActiveCreativeAgentSessionId(null);
setCreativeDraftEditError(null);
resetRecommendRuntimeSelection();
clearCreationUrlState();
clearPuzzleRuntimeUrlState();
puzzleFlow.leaveFlow();
}, [puzzleFlow, resetRecommendRuntimeSelection]);
returnToCreationFlowSource();
}, [puzzleFlow, resetRecommendRuntimeSelection, returnToCreationFlowSource]);
const leaveVisualNovelFlow = useCallback(() => {
setVisualNovelWork(null);
@@ -7331,9 +7381,9 @@ export function PlatformEntryFlowShellImpl({
setVisualNovelFormDraftPayload(null);
setVisualNovelGenerationStartedAtMs(null);
setVisualNovelGenerationPhase('generating');
clearCreationUrlState();
visualNovelFlow.leaveFlow();
}, [visualNovelFlow]);
returnToCreationFlowSource();
}, [returnToCreationFlowSource, visualNovelFlow]);
const leaveBabyObjectMatchFlow = useCallback(() => {
setBabyObjectMatchDraft(null);
@@ -7341,11 +7391,8 @@ export function PlatformEntryFlowShellImpl({
setBabyObjectMatchGenerationState(null);
setBabyObjectMatchGenerationPhase('generating');
setBabyObjectMatchError(null);
clearCreationUrlState();
enterCreateTab();
selectionStageRef.current = 'platform';
setSelectionStage('platform');
}, [enterCreateTab, setSelectionStage]);
returnToCreationFlowSource();
}, [returnToCreationFlowSource]);
const saveBabyObjectMatchResultDraft = useCallback(
async (draft: BabyObjectMatchDraft) => {
@@ -10172,7 +10219,6 @@ export function PlatformEntryFlowShellImpl({
}, [activeRecommendRuntimeKind, setPuzzleError]);
const leaveAgentWorkspace = useCallback(() => {
enterCreateTab();
sessionController.resetSessionViewState();
sessionController.setGeneratedCustomWorldProfile(null);
autosaveCoordinator.resetAutoSaveTrackingToIdle();
@@ -10180,12 +10226,11 @@ export function PlatformEntryFlowShellImpl({
sessionController.activeAgentSessionId,
null,
);
setSelectionStage('platform');
returnToCreationFlowSource();
}, [
autosaveCoordinator,
enterCreateTab,
returnToCreationFlowSource,
sessionController,
setSelectionStage,
]);
const leaveAgentDraftGeneration = useCallback(() => {
@@ -10205,13 +10250,11 @@ export function PlatformEntryFlowShellImpl({
autosaveCoordinator.resetAutoSaveTrackingToIdle();
sessionController.setCustomWorldGenerationViewSource(null);
sessionController.setCustomWorldResultViewSource(null);
enterCreateTab();
setSelectionStage('platform');
returnToCreationFlowSource();
}, [
autosaveCoordinator,
enterCreateTab,
returnToCreationFlowSource,
sessionController,
setSelectionStage,
]);
const leaveCustomWorldResult = useCallback(() => {
@@ -10220,12 +10263,18 @@ export function PlatformEntryFlowShellImpl({
autosaveCoordinator.resetAutoSaveTrackingToIdle();
sessionController.setCustomWorldGenerationViewSource(null);
sessionController.setCustomWorldResultViewSource(null);
if (shouldReturnToDraftShelf()) {
returnToCreationFlowSource();
return;
}
setSelectionStage(selectedDetailEntry ? 'detail' : 'platform');
}, [
autosaveCoordinator,
returnToCreationFlowSource,
selectedDetailEntry,
sessionController,
setSelectionStage,
shouldReturnToDraftShelf,
]);
const handleStartSelectedWorld = useCallback(() => {
@@ -14242,6 +14291,7 @@ export function PlatformEntryFlowShellImpl({
}}
onOpenDraft={(item) => {
runProtectedAction(() => {
markCreationFlowReturnToDraftShelf();
void detailNavigation.handleOpenCreationWork(item);
});
}}
@@ -14253,6 +14303,7 @@ export function PlatformEntryFlowShellImpl({
if (!matchedWork) {
return;
}
markCreationFlowReturnToDraftShelf();
void detailNavigation.handleOpenCreationWork(matchedWork);
});
}}
@@ -14267,6 +14318,7 @@ export function PlatformEntryFlowShellImpl({
isBigFishCreationVisible
? (item) => {
runProtectedAction(() => {
markCreationFlowReturnToDraftShelf();
void openBigFishDraft(item);
});
}
@@ -14276,6 +14328,7 @@ export function PlatformEntryFlowShellImpl({
isJumpHopCreationVisible
? (item) => {
runProtectedAction(() => {
markCreationFlowReturnToDraftShelf();
void openJumpHopDraft(item);
});
}
@@ -14292,6 +14345,7 @@ export function PlatformEntryFlowShellImpl({
match3dItems={match3dShelfItems}
onOpenMatch3DDetail={(item) => {
runProtectedAction(() => {
markCreationFlowReturnToDraftShelf();
void openMatch3DDraft(item);
});
}}
@@ -14305,6 +14359,7 @@ export function PlatformEntryFlowShellImpl({
isSquareHoleCreationVisible
? (item) => {
runProtectedAction(() => {
markCreationFlowReturnToDraftShelf();
void openSquareHoleDraft(item);
});
}
@@ -14320,6 +14375,7 @@ export function PlatformEntryFlowShellImpl({
puzzleItems={puzzleShelfItems}
onOpenPuzzleDetail={(item) => {
runProtectedAction(() => {
markCreationFlowReturnToDraftShelf();
void openPuzzleDraft(item);
});
}}
@@ -14335,6 +14391,7 @@ export function PlatformEntryFlowShellImpl({
}
onOpenBabyObjectMatchDetail={(item) => {
runProtectedAction(() => {
markCreationFlowReturnToDraftShelf();
openBabyObjectMatchDraft(item);
});
}}
@@ -14344,12 +14401,14 @@ export function PlatformEntryFlowShellImpl({
barkBattleItems={barkBattleShelfItems}
onOpenBarkBattleDetail={(item) => {
runProtectedAction(() => {
markCreationFlowReturnToDraftShelf();
openBarkBattleDraft(item);
});
}}
visualNovelItems={visualNovelShelfItems}
onOpenVisualNovelDetail={(item) => {
runProtectedAction(() => {
markCreationFlowReturnToDraftShelf();
void openVisualNovelDraft(item);
});
}}
@@ -14799,6 +14858,10 @@ export function PlatformEntryFlowShellImpl({
isBusy={isBigFishBusy}
error={bigFishError}
onBack={() => {
if (shouldReturnToDraftShelf()) {
leaveBigFishFlow();
return;
}
setSelectionStage('big-fish-agent-workspace');
}}
onDismissError={() => {
@@ -14948,6 +15011,10 @@ export function PlatformEntryFlowShellImpl({
isBusy={isMatch3DBusy}
error={match3dError}
onBack={() => {
if (shouldReturnToDraftShelf()) {
leaveMatch3DFlow();
return;
}
returnToCreationCenterFromGeneration();
}}
onSaved={(profile) => {
@@ -15197,6 +15264,10 @@ export function PlatformEntryFlowShellImpl({
isBusy={isBabyObjectMatchBusy}
error={babyObjectMatchError}
onBack={() => {
if (shouldReturnToDraftShelf()) {
leaveBabyObjectMatchFlow();
return;
}
setSelectionStage('baby-object-match-workspace');
}}
onSaveDraft={(draft) => {
@@ -15382,6 +15453,10 @@ export function PlatformEntryFlowShellImpl({
isBusy={isSquareHoleBusy}
error={squareHoleError}
onBack={() => {
if (shouldReturnToDraftShelf()) {
leaveSquareHoleFlow();
return;
}
setSelectionStage('square-hole-agent-workspace');
}}
onSaved={(profile) => {
@@ -16019,6 +16094,10 @@ export function PlatformEntryFlowShellImpl({
isBusy={isVisualNovelBusy}
error={visualNovelError}
onBack={() => {
if (shouldReturnToDraftShelf()) {
leaveVisualNovelFlow();
return;
}
setSelectionStage('visual-novel-agent-workspace');
}}
onSaveDraft={(draft) => {
@@ -16297,6 +16376,10 @@ export function PlatformEntryFlowShellImpl({
: barkBattleError
}
onBack={() => {
if (shouldReturnToDraftShelf()) {
leaveBarkBattleFlow();
return;
}
enterCreateTab();
selectionStageRef.current = 'platform';
setSelectionStage('platform');

View File

@@ -336,6 +336,13 @@ function getPlatformTabPanel(tab: string) {
return panel;
}
async function findPlatformTabPanel(tab: string) {
await waitFor(() => {
expect(document.getElementById(`platform-tab-panel-${tab}`)).toBeTruthy();
});
return getPlatformTabPanel(tab);
}
const testCreationEntryConfig = {
startCard: {
title: '新建作品',
@@ -4688,7 +4695,7 @@ test('match3d result trial passes generated 2D image views into first runtime mo
});
});
test('match3d result back returns to platform creation page', async () => {
test('match3d result back returns to draft hub when opened from shelf', async () => {
const user = userEvent.setup();
const match3dDraftWork: Match3DWorkSummary = {
workId: 'match3d-work-back-1',
@@ -4742,9 +4749,15 @@ test('match3d result back returns to platform creation page', async () => {
expect(await screen.findByText('抓大鹅结果页')).toBeTruthy();
await user.click(screen.getByRole('button', { name: '返回' }));
const draftPanel = await findPlatformTabPanel('saves');
await waitFor(() => {
expect(draftPanel.getAttribute('aria-hidden')).toBe('false');
});
expect(
await screen.findByRole('tablist', { name: '玩法模板分类' }),
within(draftPanel).getByRole('tablist', { name: '作品筛选' }),
).toBeTruthy();
expect(within(draftPanel).getByText('自动试玩抓大鹅')).toBeTruthy();
expect(getPlatformTabPanel('create').getAttribute('aria-hidden')).toBe('true');
expect(screen.queryByText('抓大鹅结果页')).toBeNull();
});
@@ -5153,8 +5166,10 @@ test('completed baby object match draft viewed immediately does not keep unread
await waitFor(() => {
expect(screen.queryByText('宝贝识物结果页')).toBeNull();
});
await user.click(await screen.findByRole('button', { name: '返回' }));
await openDraftHub(user);
const reopenedDraftPanel = await findPlatformTabPanel('saves');
await waitFor(() => {
expect(reopenedDraftPanel.getAttribute('aria-hidden')).toBe('false');
});
expect(screen.queryByLabelText('新生成完成')).toBeNull();
});
@@ -5205,8 +5220,10 @@ test('completed baby object match draft shows unread marker after leaving genera
await waitFor(() => {
expect(screen.queryByText('宝贝识物结果页')).toBeNull();
});
await user.click(await screen.findByRole('button', { name: '返回' }));
await openDraftHub(user);
const draftPanel = await findPlatformTabPanel('saves');
await waitFor(() => {
expect(draftPanel.getAttribute('aria-hidden')).toBe('false');
});
expect(screen.queryByLabelText('新生成完成')).toBeNull();
});
@@ -7301,7 +7318,7 @@ test('match3d creation tab stays usable even when public galleries fail', async
expect(match3dCreationClient.createSession).not.toHaveBeenCalled();
});
test('puzzle draft result back button returns to creation hub', async () => {
test('puzzle draft result back button returns to draft hub when opened from shelf', async () => {
const user = userEvent.setup();
vi.mocked(listPuzzleWorks).mockResolvedValue({
@@ -7342,10 +7359,16 @@ test('puzzle draft result back button returns to creation hub', async () => {
await user.click(screen.getByRole('button', { name: '返回' }));
const draftPanel = await findPlatformTabPanel('saves');
await waitFor(() => {
expect(draftPanel.getAttribute('aria-hidden')).toBe('false');
});
expect(
await screen.findByRole('tablist', { name: '玩法模板分类' }),
within(draftPanel).getByRole('tablist', { name: '作品筛选' }),
).toBeTruthy();
expect(await findCreationTypeButton('拼图')).toBeTruthy();
expect(within(draftPanel).getByText('雨夜猫塔')).toBeTruthy();
expect(getPlatformTabPanel('create').getAttribute('aria-hidden')).toBe('true');
expect(screen.queryByText('拼图工作区missing-session')).toBeNull();
expect(
screen.queryByText('雨夜里有一只会发光的猫站在遗迹台阶上。'),
).toBeNull();
@@ -7725,7 +7748,10 @@ test('formal puzzle runtime uses frontend move merge logic and backend leaderboa
await user.click(within(dialog).getByRole('button', { name: '下一关' }));
await waitFor(() => {
expect(advancePuzzleNextLevel).toHaveBeenCalledWith(clearedFirstLevel.runId);
expect(advancePuzzleNextLevel).toHaveBeenCalledWith(
clearedFirstLevel.runId,
{},
);
});
expect(
(
@@ -9406,7 +9432,7 @@ test('agent result view does not keep legacy publish blockers when preview uses
expect((actionButton as HTMLButtonElement).disabled).toBe(false);
});
test('agent draft result back button returns to creation hub without syncing result profile', async () => {
test('agent draft result back button returns to draft hub without syncing result profile', async () => {
const user = userEvent.setup();
const resultSession = {
@@ -9563,22 +9589,32 @@ test('agent draft result back button returns to creation hub without syncing res
},
{ timeout: 2500 },
);
const syncCallsBeforeBack = vi
.mocked(executeRpgCreationAction)
.mock.calls.filter(
([sessionId, payload]) =>
sessionId === 'custom-world-agent-session-1' &&
payload?.action === 'sync_result_profile',
).length;
await user.click(screen.getByRole('button', { name: /返回创作/u }));
const draftPanel = await findPlatformTabPanel('saves');
await waitFor(() => {
expect(screen.getByRole('tablist', { name: '玩法模板分类' })).toBeTruthy();
expect(draftPanel.getAttribute('aria-hidden')).toBe('false');
});
expect(within(draftPanel).getByRole('tablist', { name: '作品筛选' })).toBeTruthy();
expect(getPlatformTabPanel('create').getAttribute('aria-hidden')).toBe('true');
expect(
vi
.mocked(executeRpgCreationAction)
.mock.calls.some(
.mock.calls.filter(
([sessionId, payload]) =>
sessionId === 'custom-world-agent-session-1' &&
payload?.action === 'sync_result_profile',
),
).toBe(false);
).length,
).toBe(syncCallsBeforeBack);
expect(screen.queryByText('世界档案')).toBeNull();
});