refactor: 收口 Match3D 生成资产进度
This commit is contained in:
@@ -1423,6 +1423,7 @@
|
|||||||
|
|
||||||
- 背景:`PlatformEntryFlowShellImpl.tsx` 内联维护小游戏生成状态恢复、失败 / 完成收尾、展示 rebase、拼图后端进度合并和 ready / generating 判定,壳层同时承担 API / background task 副作用和 `MiniGameDraftGenerationState` 生命周期细节。
|
- 背景:`PlatformEntryFlowShellImpl.tsx` 内联维护小游戏生成状态恢复、失败 / 完成收尾、展示 rebase、拼图后端进度合并和 ready / generating 判定,壳层同时承担 API / background task 副作用和 `MiniGameDraftGenerationState` 生命周期细节。
|
||||||
- 决策:新增 `src/components/platform-entry/platformMiniGameDraftGenerationStateModel.ts`,收口恢复态、失败态、完成态、展示 rebase、拼图 progress phase 阈值和进度 metadata 合并。壳层继续负责 API、后台任务、React state 写入、作品架刷新、URL 和 stage 切换。
|
- 决策:新增 `src/components/platform-entry/platformMiniGameDraftGenerationStateModel.ts`,收口恢复态、失败态、完成态、展示 rebase、拼图 progress phase 阈值和进度 metadata 合并。壳层继续负责 API、后台任务、React state 写入、作品架刷新、URL 和 stage 切换。
|
||||||
|
- 追加决策:抓大鹅轮询作品素材时的旁路进度合并也归入该 Module,由 `mergeMatch3DGeneratedAssetsIntoGenerationState(state, assets)` 统一统计可用图片素材、至少 5 个总素材计数、`match3d-generate-views` phase 推进和首个素材错误传播;壳层只负责轮询 session / work detail 与写入 state。
|
||||||
- 影响范围:拼图 / 抓大鹅 / 大鱼吃小鱼 / 方洞 / 跳一跳 / 敲木鱼 / 宝贝识物生成状态恢复、完成失败收尾、生成页返回展示和拼图轮询进度合并。
|
- 影响范围:拼图 / 抓大鹅 / 大鱼吃小鱼 / 方洞 / 跳一跳 / 敲木鱼 / 宝贝识物生成状态恢复、完成失败收尾、生成页返回展示和拼图轮询进度合并。
|
||||||
- 验证方式:`npm run test -- src/components/platform-entry/platformMiniGameDraftGenerationStateModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
- 验证方式:`npm run test -- src/components/platform-entry/platformMiniGameDraftGenerationStateModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
||||||
- 关联文档:`docs/technical/【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
- 关联文档:`docs/technical/【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_
|
|||||||
|
|
||||||
平台壳的拼图 runtime 恢复 work、跳一跳 pending session、敲木鱼 detail 恢复 session 和敲木鱼 pending session DTO 映射收口到 `src/components/platform-entry/platformMiniGameSessionMappingModel.ts`,壳层只保留网络、状态、URL 与 stage 副作用,规则见 [【前端架构】PlatformMiniGameSessionMappingModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameSessionMappingModel收口计划-2026-06-04.md)。
|
平台壳的拼图 runtime 恢复 work、跳一跳 pending session、敲木鱼 detail 恢复 session 和敲木鱼 pending session DTO 映射收口到 `src/components/platform-entry/platformMiniGameSessionMappingModel.ts`,壳层只保留网络、状态、URL 与 stage 副作用,规则见 [【前端架构】PlatformMiniGameSessionMappingModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameSessionMappingModel收口计划-2026-06-04.md)。
|
||||||
|
|
||||||
平台小游戏生成状态的恢复、失败 / 完成收尾、展示 rebase、拼图后端进度合并和 ready / generating 判定收口到 `src/components/platform-entry/platformMiniGameDraftGenerationStateModel.ts`,壳层只保留 API、后台任务、React state、URL 与 stage 副作用,规则见 [【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md)。
|
平台小游戏生成状态的恢复、失败 / 完成收尾、展示 rebase、拼图后端进度合并、抓大鹅生成资产旁路进度合并和 ready / generating 判定收口到 `src/components/platform-entry/platformMiniGameDraftGenerationStateModel.ts`,壳层只保留 API、后台任务、React state、URL 与 stage 副作用,规则见 [【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md)。
|
||||||
|
|
||||||
平台小游戏草稿恢复和提交所需的拼图 / 抓大鹅表单 payload、拼图编译 action、pending metadata 与拼图 form-only 草稿判定收口到 `src/components/platform-entry/platformMiniGameDraftPayloadModel.ts`,壳层只保留 API、Action 执行、background task 与状态副作用,规则见 [【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md)。
|
平台小游戏草稿恢复和提交所需的拼图 / 抓大鹅表单 payload、拼图编译 action、pending metadata 与拼图 form-only 草稿判定收口到 `src/components/platform-entry/platformMiniGameDraftPayloadModel.ts`,壳层只保留 API、Action 执行、background task 与状态副作用,规则见 [【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md)。
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 背景
|
## 背景
|
||||||
|
|
||||||
`PlatformEntryFlowShellImpl.tsx` 曾内联维护小游戏生成状态的恢复、失败/完成收尾、展示 rebase、拼图后端进度合并和生成中 / ready 判定。壳层因此既要处理 API 回包、React state、后台任务、URL 和 stage,又要记住 `MiniGameDraftGenerationState` 的生命周期细节。
|
`PlatformEntryFlowShellImpl.tsx` 曾内联维护小游戏生成状态的恢复、失败/完成收尾、展示 rebase、拼图后端进度合并、抓大鹅生成资产旁路进度合并和生成中 / ready 判定。壳层因此既要处理 API 回包、React state、后台任务、URL 和 stage,又要记住 `MiniGameDraftGenerationState` 的生命周期细节。
|
||||||
|
|
||||||
这些状态变换不读取 DOM,不请求网络,也不写 React state;它们属于平台层小游戏草稿生成状态 **Module**。壳层只应决定何时调用、把返回值写入对应 state。
|
这些状态变换不读取 DOM,不请求网络,也不写 React state;它们属于平台层小游戏草稿生成状态 **Module**。壳层只应决定何时调用、把返回值写入对应 state。
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
- `createFailedMiniGameDraftGenerationStateForRestoredDraft(kind, updatedAt, error, metadata?)`:恢复失败草稿时按后端 `updatedAt` 建立失败态。
|
- `createFailedMiniGameDraftGenerationStateForRestoredDraft(kind, updatedAt, error, metadata?)`:恢复失败草稿时按后端 `updatedAt` 建立失败态。
|
||||||
- `rebaseMiniGameDraftGenerationStateForDisplay(state)` 与 `rebaseMiniGameDraftBackgroundCompileTaskForDisplay(task)`:清理展示用 `finishedAtMs`,避免返回生成页后沿用结束态计时。
|
- `rebaseMiniGameDraftGenerationStateForDisplay(state)` 与 `rebaseMiniGameDraftBackgroundCompileTaskForDisplay(task)`:清理展示用 `finishedAtMs`,避免返回生成页后沿用结束态计时。
|
||||||
- `createPuzzleDraftGenerationStateFromPayload(payload, session?)`、`resolvePuzzlePhaseFromSessionProgress(state, session)`、`mergePuzzleSessionProgressIntoGenerationState(state, session)`:集中处理拼图生成的 aiRedraw、后端进度百分比和 phase 推进。
|
- `createPuzzleDraftGenerationStateFromPayload(payload, session?)`、`resolvePuzzlePhaseFromSessionProgress(state, session)`、`mergePuzzleSessionProgressIntoGenerationState(state, session)`:集中处理拼图生成的 aiRedraw、后端进度百分比和 phase 推进。
|
||||||
|
- `mergeMatch3DGeneratedAssetsIntoGenerationState(state, assets)`:抓大鹅轮询到作品素材后,按可用图片数量推进生成页资产计数,并把首个素材错误传播到生成态。
|
||||||
- `resolveFinishedMiniGameDraftGenerationState(state, phase, options?)`:统一完成 / 失败收尾的 `finishedAtMs`、错误与资产计数合并。
|
- `resolveFinishedMiniGameDraftGenerationState(state, phase, options?)`:统一完成 / 失败收尾的 `finishedAtMs`、错误与资产计数合并。
|
||||||
- `isMiniGameDraftReady(state)` 与 `isMiniGameDraftGenerating(state)`:统一生成态轻量判定。
|
- `isMiniGameDraftReady(state)` 与 `isMiniGameDraftGenerating(state)`:统一生成态轻量判定。
|
||||||
|
|
||||||
@@ -27,12 +28,15 @@
|
|||||||
- 拼图 session 只有在 `draft` 存在且不是 `formDraft` 时才视为后端编译生成中 session,才写入 `puzzleProgressPercent` 并推进 phase。
|
- 拼图 session 只有在 `draft` 存在且不是 `formDraft` 时才视为后端编译生成中 session,才写入 `puzzleProgressPercent` 并推进 phase。
|
||||||
- 拼图进度阈值保持旧值:`>=96` 到 `puzzle-select-image`,`>=94` 到 `puzzle-ui-assets`,`>=88` 时按 `puzzleAiRedraw=false` 进入 `puzzle-level-scene`,否则进入 `puzzle-cover-image`。
|
- 拼图进度阈值保持旧值:`>=96` 到 `puzzle-select-image`,`>=94` 到 `puzzle-ui-assets`,`>=88` 时按 `puzzleAiRedraw=false` 进入 `puzzle-level-scene`,否则进入 `puzzle-cover-image`。
|
||||||
- phase 变化时 `puzzleActiveStepStartedAtMs` 使用 session `updatedAt` 解析值;phase 不变时保留旧值。
|
- phase 变化时 `puzzleActiveStepStartedAtMs` 使用 session `updatedAt` 解析值;phase 不变时保留旧值。
|
||||||
|
- 抓大鹅资产旁路进度不得覆盖 `ready` 或 `failed` 终态;非终态下只统计有 `imageViews[].imageObjectKey` / `imageViews[].imageSrc`、顶层 `imageObjectKey` 或顶层 `imageSrc` 的素材。
|
||||||
|
- 抓大鹅资产旁路进度的 `totalAssetCount` 至少为 `5`,保留当前五物品首批生成节奏;已有素材数量超过 `5` 时按真实素材数量展示。
|
||||||
|
- 抓大鹅已有可用素材时 phase 推进到 `match3d-generate-views`;无可用素材时保留原 phase;首个素材错误写入 `error`,无素材错误时保留原错误。
|
||||||
- 展示 rebase 只清理 `finishedAtMs`,不得修改 phase、error、资产计数或 metadata。
|
- 展示 rebase 只清理 `finishedAtMs`,不得修改 phase、error、资产计数或 metadata。
|
||||||
|
|
||||||
## Depth / Leverage / Locality
|
## Depth / Leverage / Locality
|
||||||
|
|
||||||
- **Depth**:壳层以状态变换函数表达意图;生成态字段、拼图阈值、时间解析与计数合并藏入 Module Implementation。
|
- **Depth**:壳层以状态变换函数表达意图;生成态字段、拼图阈值、抓大鹅素材计数、时间解析与计数合并藏入 Module Implementation。
|
||||||
- **Leverage**:后续新增小游戏生成恢复或调整拼图后端进度阈值时,先改 Module 与单测,再让壳层 Adapter 保持调用点不变。
|
- **Leverage**:后续新增小游戏生成恢复、调整拼图后端进度阈值或改变抓大鹅素材批次展示时,先改 Module 与单测,再让壳层 Adapter 保持调用点不变。
|
||||||
- **Locality**:小游戏生成状态规则集中到一个纯测试面,避免在大型壳层的 API callback、background task 和恢复流程中重复推理 `MiniGameDraftGenerationState`。
|
- **Locality**:小游戏生成状态规则集中到一个纯测试面,避免在大型壳层的 API callback、background task 和恢复流程中重复推理 `MiniGameDraftGenerationState`。
|
||||||
|
|
||||||
## 验收
|
## 验收
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ import type {
|
|||||||
} from '../../../packages/shared/src/contracts/match3dAgent';
|
} from '../../../packages/shared/src/contracts/match3dAgent';
|
||||||
import type { Match3DRunSnapshot } from '../../../packages/shared/src/contracts/match3dRuntime';
|
import type { Match3DRunSnapshot } from '../../../packages/shared/src/contracts/match3dRuntime';
|
||||||
import type {
|
import type {
|
||||||
Match3DGeneratedItemAsset,
|
|
||||||
Match3DWorkProfile,
|
Match3DWorkProfile,
|
||||||
Match3DWorkSummary,
|
Match3DWorkSummary,
|
||||||
} from '../../../packages/shared/src/contracts/match3dWorks';
|
} from '../../../packages/shared/src/contracts/match3dWorks';
|
||||||
@@ -516,6 +515,7 @@ import {
|
|||||||
createPuzzleDraftGenerationStateFromPayload,
|
createPuzzleDraftGenerationStateFromPayload,
|
||||||
isMiniGameDraftGenerating,
|
isMiniGameDraftGenerating,
|
||||||
isMiniGameDraftReady,
|
isMiniGameDraftReady,
|
||||||
|
mergeMatch3DGeneratedAssetsIntoGenerationState,
|
||||||
mergePuzzleSessionProgressIntoGenerationState,
|
mergePuzzleSessionProgressIntoGenerationState,
|
||||||
rebaseMiniGameDraftBackgroundCompileTaskForDisplay,
|
rebaseMiniGameDraftBackgroundCompileTaskForDisplay,
|
||||||
rebaseMiniGameDraftGenerationStateForDisplay,
|
rebaseMiniGameDraftGenerationStateForDisplay,
|
||||||
@@ -751,35 +751,6 @@ function mapVisualNovelWorkDetailToSession(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveMatch3DGenerationStateFromAssets(
|
|
||||||
current: MiniGameDraftGenerationState | null,
|
|
||||||
assets: readonly Match3DGeneratedItemAsset[] | null | undefined,
|
|
||||||
): MiniGameDraftGenerationState | null {
|
|
||||||
if (!current || current.phase === 'ready' || current.phase === 'failed') {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
const assetList = assets ?? [];
|
|
||||||
const imageReadyCount = assetList.filter(
|
|
||||||
(asset) =>
|
|
||||||
asset.imageViews?.some(
|
|
||||||
(view) => view.imageObjectKey?.trim() || view.imageSrc?.trim(),
|
|
||||||
) ||
|
|
||||||
asset.imageObjectKey?.trim() ||
|
|
||||||
asset.imageSrc?.trim(),
|
|
||||||
).length;
|
|
||||||
const totalAssetCount = Math.max(5, assetList.length);
|
|
||||||
const failedAsset = assetList.find((asset) => asset.error?.trim());
|
|
||||||
|
|
||||||
return {
|
|
||||||
...current,
|
|
||||||
phase: imageReadyCount > 0 ? 'match3d-generate-views' : current.phase,
|
|
||||||
completedAssetCount: imageReadyCount,
|
|
||||||
totalAssetCount,
|
|
||||||
error: failedAsset?.error?.trim() || current.error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildSquareHoleProfileFromSession(
|
function buildSquareHoleProfileFromSession(
|
||||||
session: SquareHoleSessionSnapshot | null,
|
session: SquareHoleSessionSnapshot | null,
|
||||||
): SquareHoleWorkProfile | null {
|
): SquareHoleWorkProfile | null {
|
||||||
@@ -4807,7 +4778,7 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
const normalizedItem = normalizeMatch3DWorkForRuntimeUi(item);
|
const normalizedItem = normalizeMatch3DWorkForRuntimeUi(item);
|
||||||
setMatch3DProfile(normalizedItem);
|
setMatch3DProfile(normalizedItem);
|
||||||
setMatch3DGenerationState((current) =>
|
setMatch3DGenerationState((current) =>
|
||||||
resolveMatch3DGenerationStateFromAssets(
|
mergeMatch3DGeneratedAssetsIntoGenerationState(
|
||||||
current,
|
current,
|
||||||
normalizedItem.generatedItemAssets,
|
normalizedItem.generatedItemAssets,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||||
|
|
||||||
|
import type { Match3DGeneratedItemAsset } from '../../../packages/shared/src/contracts/match3dWorks';
|
||||||
import type { PuzzleAnchorPack } from '../../../packages/shared/src/contracts/puzzleAgentDraft';
|
import type { PuzzleAnchorPack } from '../../../packages/shared/src/contracts/puzzleAgentDraft';
|
||||||
import type {
|
import type {
|
||||||
CreatePuzzleAgentSessionRequest,
|
CreatePuzzleAgentSessionRequest,
|
||||||
@@ -12,6 +13,7 @@ import {
|
|||||||
createPuzzleDraftGenerationStateFromPayload,
|
createPuzzleDraftGenerationStateFromPayload,
|
||||||
isMiniGameDraftGenerating,
|
isMiniGameDraftGenerating,
|
||||||
isMiniGameDraftReady,
|
isMiniGameDraftReady,
|
||||||
|
mergeMatch3DGeneratedAssetsIntoGenerationState,
|
||||||
mergePuzzleSessionProgressIntoGenerationState,
|
mergePuzzleSessionProgressIntoGenerationState,
|
||||||
rebaseMiniGameDraftBackgroundCompileTaskForDisplay,
|
rebaseMiniGameDraftBackgroundCompileTaskForDisplay,
|
||||||
rebaseMiniGameDraftGenerationStateForDisplay,
|
rebaseMiniGameDraftGenerationStateForDisplay,
|
||||||
@@ -96,6 +98,17 @@ function buildState(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildMatch3DAsset(
|
||||||
|
overrides: Partial<Match3DGeneratedItemAsset> = {},
|
||||||
|
): Match3DGeneratedItemAsset {
|
||||||
|
return {
|
||||||
|
itemId: 'item-1',
|
||||||
|
itemName: '红宝石',
|
||||||
|
status: 'pending',
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
vi.setSystemTime(NOW);
|
vi.setSystemTime(NOW);
|
||||||
@@ -277,6 +290,74 @@ describe('platformMiniGameDraftGenerationStateModel', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('merges match3d generated assets into active generation state', () => {
|
||||||
|
const state = buildState({
|
||||||
|
kind: 'match3d',
|
||||||
|
phase: 'match3d-material-sheet',
|
||||||
|
completedAssetCount: 0,
|
||||||
|
totalAssetCount: 0,
|
||||||
|
error: '旧错误',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mergeMatch3DGeneratedAssetsIntoGenerationState(state, [
|
||||||
|
buildMatch3DAsset({
|
||||||
|
itemId: 'item-with-view',
|
||||||
|
imageViews: [
|
||||||
|
{
|
||||||
|
viewId: 'front',
|
||||||
|
viewIndex: 0,
|
||||||
|
imageObjectKey: 'objects/front.png',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
buildMatch3DAsset({
|
||||||
|
itemId: 'item-with-src',
|
||||||
|
imageSrc: '/generated/item.png',
|
||||||
|
}),
|
||||||
|
buildMatch3DAsset({
|
||||||
|
itemId: 'item-with-error',
|
||||||
|
error: '切图失败',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
).toMatchObject({
|
||||||
|
phase: 'match3d-generate-views',
|
||||||
|
completedAssetCount: 2,
|
||||||
|
totalAssetCount: 5,
|
||||||
|
error: '切图失败',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('keeps match3d generated asset merge away from finished states', () => {
|
||||||
|
const readyState = buildState({
|
||||||
|
kind: 'match3d',
|
||||||
|
phase: 'ready',
|
||||||
|
completedAssetCount: 5,
|
||||||
|
totalAssetCount: 5,
|
||||||
|
});
|
||||||
|
const failedState = buildState({
|
||||||
|
kind: 'match3d',
|
||||||
|
phase: 'failed',
|
||||||
|
error: '已失败',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mergeMatch3DGeneratedAssetsIntoGenerationState(readyState, [
|
||||||
|
buildMatch3DAsset({ imageSrc: '/generated/new.png' }),
|
||||||
|
]),
|
||||||
|
).toBe(readyState);
|
||||||
|
expect(
|
||||||
|
mergeMatch3DGeneratedAssetsIntoGenerationState(failedState, [
|
||||||
|
buildMatch3DAsset({ imageSrc: '/generated/new.png' }),
|
||||||
|
]),
|
||||||
|
).toBe(failedState);
|
||||||
|
expect(
|
||||||
|
mergeMatch3DGeneratedAssetsIntoGenerationState(null, [
|
||||||
|
buildMatch3DAsset({ imageSrc: '/generated/new.png' }),
|
||||||
|
]),
|
||||||
|
).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
test('finishes generation state and resolves ready/generating flags', () => {
|
test('finishes generation state and resolves ready/generating flags', () => {
|
||||||
const failedState = resolveFinishedMiniGameDraftGenerationState(
|
const failedState = resolveFinishedMiniGameDraftGenerationState(
|
||||||
buildState({ error: '旧错误' }),
|
buildState({ error: '旧错误' }),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { Match3DGeneratedItemAsset } from '../../../packages/shared/src/contracts/match3dWorks';
|
||||||
import type {
|
import type {
|
||||||
CreatePuzzleAgentSessionRequest,
|
CreatePuzzleAgentSessionRequest,
|
||||||
PuzzleAgentSessionSnapshot,
|
PuzzleAgentSessionSnapshot,
|
||||||
@@ -134,6 +135,35 @@ export function mergePuzzleSessionProgressIntoGenerationState(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mergeMatch3DGeneratedAssetsIntoGenerationState(
|
||||||
|
current: MiniGameDraftGenerationState | null,
|
||||||
|
assets: readonly Match3DGeneratedItemAsset[] | null | undefined,
|
||||||
|
): MiniGameDraftGenerationState | null {
|
||||||
|
if (!current || current.phase === 'ready' || current.phase === 'failed') {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
const assetList = assets ?? [];
|
||||||
|
const imageReadyCount = assetList.filter(
|
||||||
|
(asset) =>
|
||||||
|
asset.imageViews?.some(
|
||||||
|
(view) => view.imageObjectKey?.trim() || view.imageSrc?.trim(),
|
||||||
|
) ||
|
||||||
|
asset.imageObjectKey?.trim() ||
|
||||||
|
asset.imageSrc?.trim(),
|
||||||
|
).length;
|
||||||
|
const totalAssetCount = Math.max(5, assetList.length);
|
||||||
|
const failedAsset = assetList.find((asset) => asset.error?.trim());
|
||||||
|
|
||||||
|
return {
|
||||||
|
...current,
|
||||||
|
phase: imageReadyCount > 0 ? 'match3d-generate-views' : current.phase,
|
||||||
|
completedAssetCount: imageReadyCount,
|
||||||
|
totalAssetCount,
|
||||||
|
error: failedAsset?.error?.trim() || current.error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveFinishedMiniGameDraftGenerationState(
|
export function resolveFinishedMiniGameDraftGenerationState(
|
||||||
state: MiniGameDraftGenerationState,
|
state: MiniGameDraftGenerationState,
|
||||||
phase: 'ready' | 'failed',
|
phase: 'ready' | 'failed',
|
||||||
|
|||||||
Reference in New Issue
Block a user