diff --git a/.hermes/shared-memory/pitfalls.md b/.hermes/shared-memory/pitfalls.md index cb6a5f3e..bb648e65 100644 --- a/.hermes/shared-memory/pitfalls.md +++ b/.hermes/shared-memory/pitfalls.md @@ -128,6 +128,14 @@ - 验证:`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "persisted generating puzzle draft"`,并确认恢复生成中草稿后 `getPuzzleAgentSession` 不会因为进度刷新继续连发。 - 关联:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/platform-entry/usePlatformCreationAgentFlowController.ts`、`src/components/platform-entry/usePlatformCreationAgentFlowController.test.tsx`。 +## 小游戏恢复生成页不要只用请求 busy 判定是否生成中 + +- 现象:敲木鱼作品架里的生成中草稿点击进入生成页后,页面会显示“重新生成草稿”按钮,而不是继续显示素材生成中的等待态。 +- 原因:平台壳恢复 `generationStatus=generating` 草稿时会把 `isBusy` 置回 false,只保留 `MiniGameDraftGenerationState` 作为生成事实;生成页如果只把请求 busy 传给 `isGenerating`,共用生成页会误判为空闲态并展示重试按钮。 +- 处理:小游戏生成页的 `isGenerating` 必须由 `isBusy || isMiniGameDraftGenerating(generationState)` 推导;跳一跳、拼消消、敲木鱼等从作品架恢复的生成页都要使用同一口径。 +- 验证:`npm run test -- src/components/platform-entry/PlatformEntryFlowShellImpl.test.ts` 应覆盖 `busy=false` 但敲木鱼 generation state 仍在生成中时继续隐藏重试入口。 +- 关联:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/unified-creation/UnifiedGenerationPage.tsx`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。 + ## 拼图试玩恢复 query 必须先切到运行态路径再写 - 现象:拼图试玩或正式运行态打开后,刷新会停在“正在进入拼图关卡”,或地址栏只有 `runtimeProfileId`,缺少草稿 `runtimeSessionId`。 diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.test.ts b/src/components/platform-entry/PlatformEntryFlowShellImpl.test.ts index 23b75efa..5b1b7549 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.test.ts +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.test.ts @@ -1,9 +1,11 @@ import { describe, expect, test } from 'vitest'; import { + resolveMiniGameGenerationViewBusy, resolveMiniGameGenerationProgressTickState, } from './PlatformEntryFlowShellImpl'; import { createMiniGameDraftGenerationState } from '../../services/miniGameDraftGenerationProgress'; +import { resolveFinishedMiniGameDraftGenerationState } from './platformMiniGameDraftGenerationStateModel'; describe('resolveMiniGameGenerationProgressTickState', () => { test('returns jump hop and wooden fish generation states for progress ticking', () => { @@ -30,3 +32,28 @@ describe('resolveMiniGameGenerationProgressTickState', () => { ).toBeNull(); }); }); + +describe('resolveMiniGameGenerationViewBusy', () => { + test('敲木鱼恢复生成中草稿时继续隐藏重新生成按钮', () => { + const woodenFishGeneratingState = + createMiniGameDraftGenerationState('wooden-fish'); + + expect( + resolveMiniGameGenerationViewBusy(false, woodenFishGeneratingState), + ).toBe(true); + }); + + test('生成态结束后只保留真实 busy', () => { + const woodenFishReadyState = resolveFinishedMiniGameDraftGenerationState( + createMiniGameDraftGenerationState('wooden-fish'), + 'ready', + ); + + expect(resolveMiniGameGenerationViewBusy(false, woodenFishReadyState)).toBe( + false, + ); + expect(resolveMiniGameGenerationViewBusy(true, woodenFishReadyState)).toBe( + true, + ); + }); +}); diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index 3c4a5c63..90871704 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -696,6 +696,13 @@ export function resolveMiniGameGenerationProgressTickState( return kind ? (states[kind] ?? null) : null; } +export function resolveMiniGameGenerationViewBusy( + isBusy: boolean, + state: MiniGameDraftGenerationState | null | undefined, +) { + return isBusy || isMiniGameDraftGenerating(state ?? null); +} + type PuzzleDetailReturnTarget = { tab: PlatformHomeTab; }; @@ -4454,11 +4461,10 @@ export function PlatformEntryFlowShellImpl({ activeMatch3DBackgroundCompileTask?.payload ?? match3dFormDraftPayload; const match3dGenerationViewError = activeMatch3DBackgroundCompileTask?.error ?? match3dError; - const isMatch3DGenerationViewBusy = - isMatch3DBusy || - isMiniGameDraftGenerating( - activeMatch3DBackgroundCompileTask?.generationState ?? null, - ); + const isMatch3DGenerationViewBusy = resolveMiniGameGenerationViewBusy( + isMatch3DBusy, + match3dGenerationViewState, + ); const activePuzzleGenerationSessionId = selectionStage === 'puzzle-generating' ? (activePuzzleGenerationSessionIdRef.current ?? @@ -4488,11 +4494,22 @@ export function PlatformEntryFlowShellImpl({ }, [puzzleFlow.setSession]); const puzzleGenerationViewError = activePuzzleBackgroundCompileTask?.error ?? puzzleError; - const isPuzzleGenerationViewBusy = - isPuzzleBusy || - isMiniGameDraftGenerating( - activePuzzleBackgroundCompileTask?.generationState ?? null, - ); + const isPuzzleGenerationViewBusy = resolveMiniGameGenerationViewBusy( + isPuzzleBusy, + puzzleGenerationViewState, + ); + const isJumpHopGenerationViewBusy = resolveMiniGameGenerationViewBusy( + isJumpHopBusy, + jumpHopGenerationState, + ); + const isPuzzleClearGenerationViewBusy = resolveMiniGameGenerationViewBusy( + isPuzzleClearBusy, + puzzleClearGenerationState, + ); + const isWoodenFishGenerationViewBusy = resolveMiniGameGenerationViewBusy( + isWoodenFishBusy, + woodenFishGenerationState, + ); const platformBootstrapErrorForDisplay = isCreationEntryDisabledErrorMessage( platformBootstrap.platformError, ) @@ -15592,7 +15609,7 @@ export function PlatformEntryFlowShellImpl({ jumpHopGenerationState, miniGameGenerationProgressNowMs, )} - isGenerating={isJumpHopBusy} + isGenerating={isJumpHopGenerationViewBusy} error={jumpHopError} onBack={leaveJumpHopFlow} onEditSetting={() => { @@ -15728,7 +15745,7 @@ export function PlatformEntryFlowShellImpl({ puzzleClearGenerationState, miniGameGenerationProgressNowMs, )} - isGenerating={isPuzzleClearBusy} + isGenerating={isPuzzleClearGenerationViewBusy} error={puzzleClearError} onBack={leavePuzzleClearFlow} onEditSetting={() => { @@ -15867,7 +15884,7 @@ export function PlatformEntryFlowShellImpl({ woodenFishGenerationState, miniGameGenerationProgressNowMs, )} - isGenerating={isWoodenFishBusy} + isGenerating={isWoodenFishGenerationViewBusy} error={woodenFishError} onBack={leaveWoodenFishFlow} onEditSetting={() => {