Puzzle: support history images & partial generation
Allow history-generated image paths to be submitted where Data URLs were previously required and avoid treating partial/result-page generations as blocking the whole draft. Backend: resolve history /generated-* references via resolve_puzzle_reference_image_as_data_url and convert to PuzzleDownloadedImage; add PuzzleDownloadedImage::from_resolved_reference_image; extend draft handling to apply generated level metadata (auto-naming) and normalize generation_status to treat levels with images as ready. API: add shouldAutoNameLevel to action contracts and use it to request/refine generated level names. Spacetime/module and mappers: normalize completed level statuses when saving/reading so result-page background or per-level generation doesn't mask completed drafts. Frontend: expose resolver helpers, only mark a work as generating when no usable cover or ready level exists, keep level controls enabled during UI-background regeneration, and add tests covering history-image submission, auto-naming, and UI-background/partial-generation behaviors.
This commit is contained in:
@@ -535,6 +535,60 @@ test('puzzle workspace hides prompt and cost when AI redraw is off', async () =>
|
||||
});
|
||||
});
|
||||
|
||||
test('puzzle workspace submits history image when AI redraw is off', async () => {
|
||||
const onCreateFromForm = vi.fn();
|
||||
vi.mocked(puzzleAssetClient.listHistoryAssets).mockResolvedValue([
|
||||
{
|
||||
assetObjectId: 'asset-history-1',
|
||||
assetKind: 'puzzle_cover_image',
|
||||
imageSrc: '/generated-puzzle-assets/history/image.png',
|
||||
ownerUserId: 'user-1',
|
||||
ownerLabel: '账号 user-1',
|
||||
profileId: null,
|
||||
entityId: 'puzzle-session-1',
|
||||
createdAt: '1713686400.000000Z',
|
||||
updatedAt: '1713686400.000000Z',
|
||||
},
|
||||
]);
|
||||
|
||||
render(
|
||||
<PuzzleAgentWorkspace
|
||||
session={null}
|
||||
onBack={() => {}}
|
||||
onSubmitMessage={() => {}}
|
||||
onExecuteAction={() => {}}
|
||||
onCreateFromForm={onCreateFromForm}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '选择历史图片' }));
|
||||
const picker = await screen.findByRole('dialog', {
|
||||
name: '选择历史图片',
|
||||
});
|
||||
fireEvent.click(
|
||||
await within(picker).findByRole('button', { name: /image\.png/u }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('dialog', { name: '选择历史图片' })).toBeNull();
|
||||
});
|
||||
|
||||
const aiRedrawSwitch = screen.getByRole('switch', { name: 'AI重绘' });
|
||||
fireEvent.click(aiRedrawSwitch);
|
||||
expect(screen.queryByLabelText('画面AI重绘要求(提示词)')).toBeNull();
|
||||
expect(screen.queryByText('消耗2泥点')).toBeNull();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /生成拼图游戏草稿/u }));
|
||||
|
||||
expect(onCreateFromForm).toHaveBeenCalledWith({
|
||||
seedText: '历史素材 · image.png',
|
||||
pictureDescription: '历史素材 · image.png',
|
||||
referenceImageSrc: '/generated-puzzle-assets/history/image.png',
|
||||
referenceImageSrcs: [],
|
||||
imageModel: 'gpt-image-2',
|
||||
aiRedraw: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('puzzle workspace submits uploaded reference image when AI redraw is on', async () => {
|
||||
const onCreateFromForm = vi.fn();
|
||||
const uploadedDataUrl = 'data:image/png;base64,uploaded-square';
|
||||
|
||||
Reference in New Issue
Block a user