Add generationStatus and match3d/runtime fixes
Introduce persistent generationStatus to work summaries (puzzle & match3d) and propagate generation recovery rules across docs and frontend/backends so "generating" is restored from server-side work summary rather than ephemeral front-end notices. Update API server image/asset handling (improve match3d material sheet green/alpha decontamination and promote generatedItemAssets background fields) and add runtime improvements: alpha-based hotspot hit-testing, tray insertion/three-match animation behavior, and session re-read on client-side VectorEngine timeouts/lock-screen interruptions. Many docs, tests and related frontend modules updated/added to reflect these contract and behavior changes.
This commit is contained in:
@@ -567,6 +567,96 @@ describe('PuzzleResultView', () => {
|
||||
).toHaveProperty('disabled', true);
|
||||
});
|
||||
|
||||
test('keeps the current level dialog open when another level generation completes', () => {
|
||||
const base = createSession();
|
||||
const firstLevel = base.draft!.levels![0]!;
|
||||
const generatingSecondLevel = {
|
||||
...firstLevel,
|
||||
levelId: 'puzzle-level-2',
|
||||
levelName: '第二关',
|
||||
pictureDescription: '第二关画面正在生成。',
|
||||
candidates: [],
|
||||
selectedCandidateId: null,
|
||||
coverImageSrc: null,
|
||||
coverAssetId: null,
|
||||
generationStatus: 'generating' as const,
|
||||
};
|
||||
const localThirdLevel = {
|
||||
...firstLevel,
|
||||
levelId: 'puzzle-level-3',
|
||||
levelName: '第三关',
|
||||
pictureDescription: '第三关初稿。',
|
||||
candidates: [],
|
||||
selectedCandidateId: null,
|
||||
coverImageSrc: null,
|
||||
coverAssetId: null,
|
||||
generationStatus: 'idle' as const,
|
||||
};
|
||||
const completedSecondLevel = {
|
||||
...generatingSecondLevel,
|
||||
candidates: [
|
||||
{
|
||||
candidateId: 'candidate-level-2',
|
||||
imageSrc: '/puzzle/level-2.png',
|
||||
assetId: 'asset-level-2',
|
||||
prompt: '第二关画面',
|
||||
actualPrompt: null,
|
||||
sourceType: 'generated' as const,
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
selectedCandidateId: 'candidate-level-2',
|
||||
coverImageSrc: '/puzzle/level-2.png',
|
||||
coverAssetId: 'asset-level-2',
|
||||
generationStatus: 'ready' as const,
|
||||
};
|
||||
|
||||
const { rerender } = render(
|
||||
<PuzzleResultView
|
||||
session={createSession({
|
||||
draft: {
|
||||
...base.draft!,
|
||||
levels: [firstLevel, generatingSecondLevel, localThirdLevel],
|
||||
},
|
||||
})}
|
||||
onBack={() => {}}
|
||||
onExecuteAction={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
openPuzzleLevelsTab();
|
||||
fireEvent.click(screen.getByText('第三关'));
|
||||
const dialog = screen.getByRole('dialog', { name: '关卡详情' });
|
||||
fireEvent.change(within(dialog).getByLabelText('画面描述'), {
|
||||
target: { value: '正在编辑第三关的信息。' },
|
||||
});
|
||||
|
||||
rerender(
|
||||
<PuzzleResultView
|
||||
session={createSession({
|
||||
draft: {
|
||||
...base.draft!,
|
||||
levels: [firstLevel, completedSecondLevel],
|
||||
},
|
||||
updatedAt: '2026-05-14T10:00:00.000Z',
|
||||
})}
|
||||
onBack={() => {}}
|
||||
onExecuteAction={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
const currentDialog = screen.getByRole('dialog', { name: '关卡详情' });
|
||||
expect(within(currentDialog).getByLabelText('关卡名称')).toHaveProperty(
|
||||
'value',
|
||||
'第三关',
|
||||
);
|
||||
expect(within(currentDialog).getByLabelText('画面描述')).toHaveProperty(
|
||||
'value',
|
||||
'正在编辑第三关的信息。',
|
||||
);
|
||||
expect(within(currentDialog).queryByDisplayValue('第二关')).toBeNull();
|
||||
});
|
||||
|
||||
test('publishes with work info and serialized levels', () => {
|
||||
const onExecuteAction = vi.fn();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user