From 2a031bcd895ae17ddeb69998d0a4070c25343bd4 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Tue, 26 May 2026 14:43:49 +0800 Subject: [PATCH 1/2] fix: return draft results to shelf --- .hermes/shared-memory/pitfalls.md | 8 + ...玩法创作】平å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md | 3 +- .../PlatformEntryFlowShellImpl.tsx | 163 +++++++++++++----- ...gEntryFlowShell.agent.interaction.test.tsx | 39 ++++- 4 files changed, 164 insertions(+), 49 deletions(-) diff --git a/.hermes/shared-memory/pitfalls.md b/.hermes/shared-memory/pitfalls.md index eac362e4..55c3369a 100644 --- a/.hermes/shared-memory/pitfalls.md +++ b/.hermes/shared-memory/pitfalls.md @@ -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 秒一次的正常刷新。 diff --git a/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md b/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md index 5044d3fe..e3b799cf 100644 --- a/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md +++ b/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md @@ -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 上方完整滚动露出,ä¸å¾—è¢«å›ºå®šåº•éƒ¨å¯¼èˆªé®æŒ¡ã€‚ diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index c6359f6b..ef7d73b8 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -453,6 +453,7 @@ type PendingDraftShelfMap = Partial< Record > >; +type CreationFlowReturnTarget = 'create' | 'draft-shelf'; type Match3DBackgroundCompileTask = { session: Match3DAgentSessionSnapshot; payload: CreateMatch3DSessionRequest; @@ -3300,6 +3301,8 @@ export function PlatformEntryFlowShellImpl({ ); const handledInitialPublicWorkCodeRef = useRef(null); const selectionStageRef = useRef(selectionStage); + const creationFlowReturnTargetRef = + useRef('create'); const activeMatch3DGenerationSessionIdRef = useRef(null); const activePuzzleGenerationSessionIdRef = useRef(null); const [draftGenerationNotices, setDraftGenerationNotices] = @@ -3545,11 +3548,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' && @@ -4717,9 +4743,15 @@ export function PlatformEntryFlowShellImpl({ handleStartNewGame(); } + markCreationFlowReturnToCreate(); sessionController.setCreationTypeError(null); return true; - }, [handleStartNewGame, hasSavedGame, sessionController]); + }, [ + handleStartNewGame, + hasSavedGame, + markCreationFlowReturnToCreate, + sessionController, + ]); const openCreationTypePicker = useCallback(() => { if (!prepareCreationLaunch()) { @@ -5905,11 +5937,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); @@ -5927,9 +5961,11 @@ export function PlatformEntryFlowShellImpl({ setSquareHoleSession, setStreamingSquareHoleReplyText, squareHoleFlow, + markCreationFlowReturnToCreate, ]); const openMatch3DWorkspace = useCallback(() => { + markCreationFlowReturnToCreate(); setMatch3DRun(null); setMatch3DProfile(null); setMatch3DRuntimeProfile(null); @@ -5946,9 +5982,11 @@ export function PlatformEntryFlowShellImpl({ setMatch3DError, setSelectionStage, setStreamingMatch3DReplyText, + markCreationFlowReturnToCreate, ]); const openJumpHopWorkspace = useCallback(() => { + markCreationFlowReturnToCreate(); setJumpHopError(null); setJumpHopSession(null); setJumpHopWork(null); @@ -5957,9 +5995,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); @@ -5968,9 +6007,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); @@ -5981,9 +6021,11 @@ export function PlatformEntryFlowShellImpl({ setPuzzleCreationError, setPuzzleError, setSelectionStage, + markCreationFlowReturnToCreate, ]); const openBarkBattleWorkspace = useCallback(() => { + markCreationFlowReturnToCreate(); setBarkBattleDraftConfig(null); setBarkBattlePublishedConfig(null); setBarkBattleRuntimeMode('draft'); @@ -5995,15 +6037,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, ]); @@ -6014,6 +6058,7 @@ export function PlatformEntryFlowShellImpl({ return; } + markCreationFlowReturnToCreate(); enterCreateTab(); setShowCreationTypeModal(false); setBabyObjectMatchError(null); @@ -6021,6 +6066,7 @@ export function PlatformEntryFlowShellImpl({ }, [ enterCreateTab, isBabyObjectMatchVisible, + markCreationFlowReturnToCreate, sessionController, setBabyObjectMatchError, setSelectionStage, @@ -6039,9 +6085,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) { @@ -6049,6 +6101,7 @@ export function PlatformEntryFlowShellImpl({ } setPuzzleRun(null); + markCreationFlowReturnToCreate(); setPuzzleRuntimeAuthMode('default'); setPuzzleOperation(null); setPuzzleGenerationState(null); @@ -6077,6 +6130,7 @@ export function PlatformEntryFlowShellImpl({ enterCreateTab, isCreativeAgentBusy, isCreativeAgentStreaming, + markCreationFlowReturnToCreate, resolvePuzzleErrorMessage, setSelectionStage, ]); @@ -6922,9 +6976,9 @@ export function PlatformEntryFlowShellImpl({ setBigFishRuntimeStartedAt(null); setBigFishRuntimeReturnStage('platform'); setBigFishGenerationState(null); - clearCreationUrlState(); bigFishFlow.leaveFlow(); - }, [bigFishFlow]); + returnToCreationFlowSource(); + }, [bigFishFlow, returnToCreationFlowSource]); const leaveMatch3DFlow = useCallback(() => { setMatch3DRun(null); @@ -6932,17 +6986,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); @@ -6951,9 +7005,8 @@ export function PlatformEntryFlowShellImpl({ setJumpHopGenerationState(null); setJumpHopSession(null); setJumpHopError(null); - clearCreationUrlState(); - setSelectionStage('platform'); - }, [setSelectionStage]); + returnToCreationFlowSource(); + }, [returnToCreationFlowSource]); const leaveWoodenFishFlow = useCallback(() => { setWoodenFishRun(null); @@ -6962,9 +7015,8 @@ export function PlatformEntryFlowShellImpl({ setWoodenFishGenerationState(null); setWoodenFishSession(null); setWoodenFishError(null); - clearCreationUrlState(); - setSelectionStage('platform'); - }, [setSelectionStage]); + returnToCreationFlowSource(); + }, [returnToCreationFlowSource]); const createReadyJumpHopGenerationState = useCallback( (state: MiniGameDraftGenerationState) => @@ -6992,10 +7044,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) => { @@ -7276,10 +7326,10 @@ export function PlatformEntryFlowShellImpl({ setActiveCreativeAgentSessionId(null); setCreativeDraftEditError(null); resetRecommendRuntimeSelection(); - clearCreationUrlState(); clearPuzzleRuntimeUrlState(); puzzleFlow.leaveFlow(); - }, [puzzleFlow, resetRecommendRuntimeSelection]); + returnToCreationFlowSource(); + }, [puzzleFlow, resetRecommendRuntimeSelection, returnToCreationFlowSource]); const leaveVisualNovelFlow = useCallback(() => { setVisualNovelWork(null); @@ -7288,9 +7338,9 @@ export function PlatformEntryFlowShellImpl({ setVisualNovelFormDraftPayload(null); setVisualNovelGenerationStartedAtMs(null); setVisualNovelGenerationPhase('generating'); - clearCreationUrlState(); visualNovelFlow.leaveFlow(); - }, [visualNovelFlow]); + returnToCreationFlowSource(); + }, [returnToCreationFlowSource, visualNovelFlow]); const leaveBabyObjectMatchFlow = useCallback(() => { setBabyObjectMatchDraft(null); @@ -7298,11 +7348,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) => { @@ -10079,7 +10126,6 @@ export function PlatformEntryFlowShellImpl({ }, [activeRecommendRuntimeKind, setPuzzleError]); const leaveAgentWorkspace = useCallback(() => { - enterCreateTab(); sessionController.resetSessionViewState(); sessionController.setGeneratedCustomWorldProfile(null); autosaveCoordinator.resetAutoSaveTrackingToIdle(); @@ -10087,12 +10133,11 @@ export function PlatformEntryFlowShellImpl({ sessionController.activeAgentSessionId, null, ); - setSelectionStage('platform'); + returnToCreationFlowSource(); }, [ autosaveCoordinator, - enterCreateTab, + returnToCreationFlowSource, sessionController, - setSelectionStage, ]); const leaveAgentDraftGeneration = useCallback(() => { @@ -10112,13 +10157,11 @@ export function PlatformEntryFlowShellImpl({ autosaveCoordinator.resetAutoSaveTrackingToIdle(); sessionController.setCustomWorldGenerationViewSource(null); sessionController.setCustomWorldResultViewSource(null); - enterCreateTab(); - setSelectionStage('platform'); + returnToCreationFlowSource(); }, [ autosaveCoordinator, - enterCreateTab, + returnToCreationFlowSource, sessionController, - setSelectionStage, ]); const leaveCustomWorldResult = useCallback(() => { @@ -10127,12 +10170,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(() => { @@ -14135,6 +14184,7 @@ export function PlatformEntryFlowShellImpl({ }} onOpenDraft={(item) => { runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); void detailNavigation.handleOpenCreationWork(item); }); }} @@ -14146,6 +14196,7 @@ export function PlatformEntryFlowShellImpl({ if (!matchedWork) { return; } + markCreationFlowReturnToDraftShelf(); void detailNavigation.handleOpenCreationWork(matchedWork); }); }} @@ -14160,6 +14211,7 @@ export function PlatformEntryFlowShellImpl({ isBigFishCreationVisible ? (item) => { runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); void openBigFishDraft(item); }); } @@ -14169,6 +14221,7 @@ export function PlatformEntryFlowShellImpl({ isJumpHopCreationVisible ? (item) => { runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); void openJumpHopDraft(item); }); } @@ -14185,6 +14238,7 @@ export function PlatformEntryFlowShellImpl({ match3dItems={match3dShelfItems} onOpenMatch3DDetail={(item) => { runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); void openMatch3DDraft(item); }); }} @@ -14198,6 +14252,7 @@ export function PlatformEntryFlowShellImpl({ isSquareHoleCreationVisible ? (item) => { runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); void openSquareHoleDraft(item); }); } @@ -14213,6 +14268,7 @@ export function PlatformEntryFlowShellImpl({ puzzleItems={puzzleShelfItems} onOpenPuzzleDetail={(item) => { runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); void openPuzzleDraft(item); }); }} @@ -14228,6 +14284,7 @@ export function PlatformEntryFlowShellImpl({ } onOpenBabyObjectMatchDetail={(item) => { runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); openBabyObjectMatchDraft(item); }); }} @@ -14237,12 +14294,14 @@ export function PlatformEntryFlowShellImpl({ barkBattleItems={barkBattleShelfItems} onOpenBarkBattleDetail={(item) => { runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); openBarkBattleDraft(item); }); }} visualNovelItems={visualNovelShelfItems} onOpenVisualNovelDetail={(item) => { runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); void openVisualNovelDraft(item); }); }} @@ -14691,6 +14750,10 @@ export function PlatformEntryFlowShellImpl({ isBusy={isBigFishBusy} error={bigFishError} onBack={() => { + if (shouldReturnToDraftShelf()) { + leaveBigFishFlow(); + return; + } setSelectionStage('big-fish-agent-workspace'); }} onDismissError={() => { @@ -14840,6 +14903,10 @@ export function PlatformEntryFlowShellImpl({ isBusy={isMatch3DBusy} error={match3dError} onBack={() => { + if (shouldReturnToDraftShelf()) { + leaveMatch3DFlow(); + return; + } returnToCreationCenterFromGeneration(); }} onSaved={(profile) => { @@ -15081,6 +15148,10 @@ export function PlatformEntryFlowShellImpl({ isBusy={isBabyObjectMatchBusy} error={babyObjectMatchError} onBack={() => { + if (shouldReturnToDraftShelf()) { + leaveBabyObjectMatchFlow(); + return; + } setSelectionStage('baby-object-match-workspace'); }} onSaveDraft={(draft) => { @@ -15266,6 +15337,10 @@ export function PlatformEntryFlowShellImpl({ isBusy={isSquareHoleBusy} error={squareHoleError} onBack={() => { + if (shouldReturnToDraftShelf()) { + leaveSquareHoleFlow(); + return; + } setSelectionStage('square-hole-agent-workspace'); }} onSaved={(profile) => { @@ -15903,6 +15978,10 @@ export function PlatformEntryFlowShellImpl({ isBusy={isVisualNovelBusy} error={visualNovelError} onBack={() => { + if (shouldReturnToDraftShelf()) { + leaveVisualNovelFlow(); + return; + } setSelectionStage('visual-novel-agent-workspace'); }} onSaveDraft={(draft) => { @@ -16181,6 +16260,10 @@ export function PlatformEntryFlowShellImpl({ : barkBattleError } onBack={() => { + if (shouldReturnToDraftShelf()) { + leaveBarkBattleFlow(); + return; + } enterCreateTab(); selectionStageRef.current = 'platform'; setSelectionStage('platform'); diff --git a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx index baf9807c..6ad68ccb 100644 --- a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx +++ b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx @@ -324,6 +324,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: '新建作å“', @@ -6938,7 +6945,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({ @@ -6979,10 +6986,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 screen.findByText('拼图工作区:missing-session')).toBeTruthy(); + expect(within(draftPanel).getByText('雨夜猫塔')).toBeTruthy(); + expect(getPlatformTabPanel('create').getAttribute('aria-hidden')).toBe('true'); + expect(screen.queryByText('拼图工作区:missing-session')).toBeNull(); expect( screen.queryByText('雨夜里有一åªä¼šå‘光的猫站在é—迹å°é˜¶ä¸Šã€‚'), ).toBeNull(); @@ -8996,7 +9009,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 = { @@ -9153,22 +9166,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(); }); From 564fc01b370e696c352559ab37e3da2413d4e0e0 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Tue, 26 May 2026 16:52:22 +0800 Subject: [PATCH 2/2] test: align puzzle next-level expectation --- .../rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx index 324bdfe1..ecc4c5f0 100644 --- a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx +++ b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx @@ -7748,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( (