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:
2026-05-19 10:02:13 +08:00
parent 5e03b3d2f2
commit 7b37271f17
16 changed files with 653 additions and 73 deletions

View File

@@ -221,6 +221,81 @@ test('creation hub marks generating and newly completed drafts', () => {
expect(html).toContain('creation-work-card__spinner');
});
test('creation hub does not mask completed puzzle drafts while a later level image is generating', () => {
const html = renderToStaticMarkup(
<CustomWorldCreationHub
items={[]}
puzzleItems={[
{
workId: 'puzzle-work-session-1',
profileId: 'puzzle-profile-session-1',
ownerUserId: 'user-1',
authorDisplayName: '测试作者',
workTitle: '潮雾拼图草稿',
workDescription: '已经有可查看的首关结果。',
levelName: '潮雾拼图',
summary: '已经有可查看的首关结果。',
themeTags: ['潮雾'],
coverImageSrc: '/generated-puzzle-assets/session/cover.png',
publicationStatus: 'draft',
updatedAt: new Date('2026-04-22T10:00:00.000Z').toISOString(),
publishedAt: null,
publishReady: false,
sourceSessionId: 'puzzle-session-1',
generationStatus: 'generating',
levels: [
{
levelId: 'puzzle-level-1',
levelName: '潮雾拼图',
pictureDescription: '潮雾港口。',
pictureReference: null,
candidates: [
{
candidateId: 'candidate-1',
imageSrc: '/generated-puzzle-assets/session/cover.png',
assetId: 'asset-1',
prompt: '潮雾港口',
actualPrompt: null,
sourceType: 'generated',
selected: true,
},
],
selectedCandidateId: 'candidate-1',
coverImageSrc: '/generated-puzzle-assets/session/cover.png',
coverAssetId: 'asset-1',
generationStatus: 'ready',
},
{
levelId: 'puzzle-level-2',
levelName: '灯塔',
pictureDescription: '灯塔新关卡。',
pictureReference: null,
candidates: [],
selectedCandidateId: null,
coverImageSrc: null,
coverAssetId: null,
generationStatus: 'generating',
},
],
},
]}
loading={false}
error={null}
onRetry={() => {}}
onCreateType={noopCreateType}
onOpenDraft={() => {}}
onEnterPublished={() => {}}
entryConfig={testEntryConfig}
creationTypes={testCreationTypes}
onOpenPuzzleDetail={() => {}}
/>,
);
expect(html).not.toContain('生成中...');
expect(html).not.toContain('creation-work-card__spinner');
expect(html).toContain('继续创作《潮雾拼图草稿》');
});
test('creation hub published work uses unified list card layout', () => {
const html = renderToStaticMarkup(
<CustomWorldCreationHub