Merge remote-tracking branch 'origin/master' into codex/tiaoyitiao
# Conflicts: # server-rs/crates/api-server/src/jump_hop.rs # server-rs/crates/api-server/src/modules/jump_hop.rs
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,10 @@ export type SelectionStage =
|
||||
| 'jump-hop-result'
|
||||
| 'jump-hop-runtime'
|
||||
| 'jump-hop-gallery-detail'
|
||||
| 'puzzle-clear-workspace'
|
||||
| 'puzzle-clear-generating'
|
||||
| 'puzzle-clear-result'
|
||||
| 'puzzle-clear-runtime'
|
||||
| 'bark-battle-workspace'
|
||||
| 'bark-battle-generating'
|
||||
| 'bark-battle-result'
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import { createMiniGameDraftGenerationState } from '../../services/miniGameDraftGenerationProgress';
|
||||
import { shouldTickPlatformGenerationProgressClock } from './platformGenerationProgressClock';
|
||||
|
||||
describe('platformGenerationProgressClock', () => {
|
||||
test('ticks while puzzle clear generation is still running', () => {
|
||||
expect(
|
||||
shouldTickPlatformGenerationProgressClock({
|
||||
selectionStage: 'puzzle-clear-generating',
|
||||
generationState: createMiniGameDraftGenerationState('puzzle-clear'),
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('stops ticking after puzzle clear generation is ready or failed', () => {
|
||||
const runningState = createMiniGameDraftGenerationState('puzzle-clear');
|
||||
|
||||
expect(
|
||||
shouldTickPlatformGenerationProgressClock({
|
||||
selectionStage: 'puzzle-clear-generating',
|
||||
generationState: { ...runningState, phase: 'ready' },
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldTickPlatformGenerationProgressClock({
|
||||
selectionStage: 'puzzle-clear-generating',
|
||||
generationState: { ...runningState, phase: 'failed' },
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('ticks for other shared mini game generation stages', () => {
|
||||
expect(
|
||||
shouldTickPlatformGenerationProgressClock({
|
||||
selectionStage: 'jump-hop-generating',
|
||||
generationState: createMiniGameDraftGenerationState('jump-hop'),
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldTickPlatformGenerationProgressClock({
|
||||
selectionStage: 'wooden-fish-generating',
|
||||
generationState: createMiniGameDraftGenerationState('wooden-fish'),
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('ticks visual novel generation from its phase source', () => {
|
||||
expect(
|
||||
shouldTickPlatformGenerationProgressClock({
|
||||
selectionStage: 'visual-novel-generating',
|
||||
visualNovelGenerationStartedAtMs: 1000,
|
||||
visualNovelGenerationPhase: 'generating',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldTickPlatformGenerationProgressClock({
|
||||
selectionStage: 'visual-novel-generating',
|
||||
visualNovelGenerationStartedAtMs: 1000,
|
||||
visualNovelGenerationPhase: 'ready',
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('does not tick when no generating stage is active', () => {
|
||||
expect(
|
||||
shouldTickPlatformGenerationProgressClock({
|
||||
selectionStage: 'platform',
|
||||
generationState: createMiniGameDraftGenerationState('puzzle-clear'),
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { MiniGameDraftGenerationState } from '../../services/miniGameDraftGenerationProgress';
|
||||
import type { SelectionStage } from './platformEntryTypes';
|
||||
|
||||
type VisualNovelEntryGenerationPhase = 'generating' | 'ready' | 'failed';
|
||||
|
||||
type PlatformGenerationProgressClockInput = {
|
||||
selectionStage: SelectionStage;
|
||||
generationState?: MiniGameDraftGenerationState | null;
|
||||
visualNovelGenerationStartedAtMs?: number | null;
|
||||
visualNovelGenerationPhase?: VisualNovelEntryGenerationPhase;
|
||||
};
|
||||
|
||||
export function shouldTickPlatformGenerationProgressClock({
|
||||
selectionStage,
|
||||
generationState,
|
||||
visualNovelGenerationStartedAtMs,
|
||||
visualNovelGenerationPhase,
|
||||
}: PlatformGenerationProgressClockInput) {
|
||||
if (selectionStage === 'visual-novel-generating') {
|
||||
return (
|
||||
visualNovelGenerationStartedAtMs != null &&
|
||||
visualNovelGenerationPhase !== 'ready' &&
|
||||
visualNovelGenerationPhase !== 'failed'
|
||||
);
|
||||
}
|
||||
|
||||
if (!selectionStage.endsWith('-generating')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Boolean(
|
||||
generationState &&
|
||||
generationState.phase !== 'ready' &&
|
||||
generationState.phase !== 'failed',
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { isPuzzleCompileActionReady } from './puzzleDraftGenerationState';
|
||||
import type { PuzzleAgentSessionSnapshot } from '../../../packages/shared/src/contracts/puzzleAgentSession';
|
||||
|
||||
describe('isPuzzleCompileActionReady', () => {
|
||||
it('keeps compile action generating until the draft has a cover image', () => {
|
||||
const session = {
|
||||
sessionId: 'puzzle-session-1',
|
||||
draft: {
|
||||
coverImageSrc: null,
|
||||
levels: [
|
||||
{
|
||||
generationStatus: 'generating',
|
||||
coverImageSrc: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
} as PuzzleAgentSessionSnapshot;
|
||||
|
||||
expect(isPuzzleCompileActionReady(session)).toBe(false);
|
||||
});
|
||||
|
||||
it('treats compile action as ready after the selected cover exists', () => {
|
||||
const session = {
|
||||
sessionId: 'puzzle-session-1',
|
||||
draft: {
|
||||
coverImageSrc: '/generated-puzzle-assets/session/cover.png',
|
||||
levels: [
|
||||
{
|
||||
generationStatus: 'ready',
|
||||
coverImageSrc: '/generated-puzzle-assets/session/cover.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as PuzzleAgentSessionSnapshot;
|
||||
|
||||
expect(isPuzzleCompileActionReady(session)).toBe(true);
|
||||
});
|
||||
});
|
||||
20
src/components/platform-entry/puzzleDraftGenerationState.ts
Normal file
20
src/components/platform-entry/puzzleDraftGenerationState.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { PuzzleAgentSessionSnapshot } from '../../../packages/shared/src/contracts/puzzleAgentSession';
|
||||
|
||||
function hasText(value: string | null | undefined) {
|
||||
return typeof value === 'string' && value.trim().length > 0;
|
||||
}
|
||||
|
||||
export function isPuzzleCompileActionReady(
|
||||
session: PuzzleAgentSessionSnapshot,
|
||||
) {
|
||||
const draft = session.draft;
|
||||
if (!draft) {
|
||||
return false;
|
||||
}
|
||||
if (hasText(draft.coverImageSrc)) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
draft.levels?.some((level) => hasText(level.coverImageSrc)) === true
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user