From 74f726e65d8cdca2bc600fcccf33579439ebc0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 10 Sep 2026 18:43:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E8=87=AA=E5=8A=A8=E5=88=87?= =?UTF-8?q?=E5=88=86=E7=B4=A0=E6=9D=90=E6=A3=80=E6=9F=A5=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除自动切分对预先导入独立素材的错误要求。 统一自动切分前置与结果检查,并补齐图片和字体引用校验。 在新建和重新开始自动切分时执行前置检查,恢复流程继续尽力完成。 --- .../src/features/ui-editor/requisites.ts | 79 +++++++------------ .../ui-editor/components/WorkflowChecks.ts | 15 ++-- .../src/view/ui-editor/useUiEditorPage.ts | 26 ++++++ ...案】UI编辑器自动切分素材工作流-2026-09-08.md | 1 + 4 files changed, 62 insertions(+), 59 deletions(-) diff --git a/apps/ai-game-creator-shell/src/features/ui-editor/requisites.ts b/apps/ai-game-creator-shell/src/features/ui-editor/requisites.ts index 1fcd649c8..759792923 100644 --- a/apps/ai-game-creator-shell/src/features/ui-editor/requisites.ts +++ b/apps/ai-game-creator-shell/src/features/ui-editor/requisites.ts @@ -72,7 +72,7 @@ export function validateComponentRecognitionPrerequisites( return issues; } -export function validateAssetRecognitionPrerequisites( +export function validateAssetSeparationPrerequisites( state: State, ): UiEditorPrerequisiteIssue[] { const issues = validateComponentRecognitionPrerequisites(state); @@ -91,58 +91,9 @@ export function validateAssetRecognitionPrerequisites( }); } } - if (Object.keys(state.sprite_assets).length === 0) { - issues.push({ - code: 'missing-sprite-assets', - message: '请先导入独立素材', - }); - } return issues; } -export function validateLayoutGenerationPrerequisites( - state: State, -): UiEditorPrerequisiteIssue[] { - const issues = validateAssetRecognitionPrerequisites(state); - const visit = (nodes: State['ui_trees'][number]['root']['children']) => { - for (const node of nodes) { - const component = node.component; - if (component) { - if ( - 'Image' in component && - component.Image.target_graphic !== null && - !(component.Image.target_graphic in state.sprite_assets) - ) { - issues.push({ - code: 'missing-target-graphic', - message: '图片组件引用的独立素材不存在', - resourceId: component.Image.target_graphic, - }); - } - if ('Text' in component) { - const font = component.Text.font; - if (typeof font !== 'string' && !(font.Bound in state.font_assets)) { - issues.push({ - code: 'missing-font', - message: '文本组件引用的字体不存在', - resourceId: font.Bound, - }); - } - } - } - visit(node.children); - } - }; - for (const tree of state.ui_trees) visit([tree.root]); - return issues; -} - -export function validateLayoutReviewPrerequisites( - state: State, -): UiEditorPrerequisiteIssue[] { - return validateLayoutGenerationPrerequisites(state); -} - function layoutStatusIssue( status: StageStatus, ): UiEditorPrerequisiteIssue | null { @@ -214,7 +165,7 @@ export function validateStructureRecognitionResult( return issues; } -export function validateVisualBindingResult( +export function validateAssetSeparationResult( state: State, ): UiEditorPrerequisiteIssue[] { const issues: UiEditorPrerequisiteIssue[] = []; @@ -225,6 +176,32 @@ export function validateVisualBindingResult( } const issue = componentStatusIssue(node.metadata.component_status); if (issue) issues.push(issue); + const component = node.component; + if ('Image' in component) { + const targetGraphic = component.Image.target_graphic; + if (targetGraphic === null) { + issues.push({ + code: 'missing-separated-image', + message: '图片组件尚未完成素材切分', + }); + } else if (!(targetGraphic in state.sprite_assets)) { + issues.push({ + code: 'missing-target-graphic', + message: '图片组件引用的切分素材不存在', + resourceId: targetGraphic, + }); + } + } + if ('Text' in component) { + const font = component.Text.font; + if (typeof font !== 'string' && !(font.Bound in state.font_assets)) { + issues.push({ + code: 'missing-font', + message: '文本组件引用的字体不存在', + resourceId: font.Bound, + }); + } + } }); } return issues; diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/components/WorkflowChecks.ts b/apps/ai-game-creator-shell/src/view/ui-editor/components/WorkflowChecks.ts index 1f0b8f943..fbf4d1582 100644 --- a/apps/ai-game-creator-shell/src/view/ui-editor/components/WorkflowChecks.ts +++ b/apps/ai-game-creator-shell/src/view/ui-editor/components/WorkflowChecks.ts @@ -1,11 +1,10 @@ import { type UiEditorPrerequisiteIssue, - validateAssetRecognitionPrerequisites, + validateAssetSeparationPrerequisites, + validateAssetSeparationResult, validateComponentRecognitionPrerequisites, - validateLayoutReviewPrerequisites, validateReferenceAnalysisResult, validateStructureRecognitionResult, - validateVisualBindingResult, } from '../../../features/ui-editor/requisites'; import type { State } from '../../../features/ui-editor/types/State'; import type { UiEditorStepId } from '../model'; @@ -22,7 +21,7 @@ export function prerequisiteIssuesForStep( case 'structure-recognition': return validateComponentRecognitionPrerequisites(state); case 'asset-separation': - return validateAssetRecognitionPrerequisites(state); + return validateAssetSeparationPrerequisites(state); } } @@ -36,7 +35,7 @@ export function postCheckIssuesForStep( case 'structure-recognition': return validateStructureRecognitionResult(state); case 'asset-separation': - return validateVisualBindingResult(state); + return validateAssetSeparationResult(state); } } @@ -46,7 +45,7 @@ export function postCheckIssuesForSave( return [ ...validateReferenceAnalysisResult(state), ...validateStructureRecognitionResult(state), - ...validateVisualBindingResult(state), + ...validateAssetSeparationResult(state), ]; } @@ -58,8 +57,8 @@ export function activeStepPrerequisiteIssues( case 'reference-analysis': return validateComponentRecognitionPrerequisites(state); case 'structure-recognition': - return validateAssetRecognitionPrerequisites(state); + return validateComponentRecognitionPrerequisites(state); case 'asset-separation': - return validateLayoutReviewPrerequisites(state); + return validateAssetSeparationPrerequisites(state); } } diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/useUiEditorPage.ts b/apps/ai-game-creator-shell/src/view/ui-editor/useUiEditorPage.ts index c449a56e7..735a02af7 100644 --- a/apps/ai-game-creator-shell/src/view/ui-editor/useUiEditorPage.ts +++ b/apps/ai-game-creator-shell/src/view/ui-editor/useUiEditorPage.ts @@ -1241,6 +1241,19 @@ export function useUiEditorSession( setSeparationRecovery(recovery); return; } + const prerequisiteIssues = prerequisiteIssuesForStep( + editor.state, + 'asset-separation', + ); + if (prerequisiteIssues.length > 0) { + reportWorkflowCompletion( + 'asset-separation', + 'failure', + prerequisiteIssues.map((issue) => issue.message).join(';'), + setSeparationStatus, + ); + return; + } await runSeparationWorkflow(); } catch (cause) { setSeparationStatus( @@ -1257,6 +1270,19 @@ export function useUiEditorSession( async function restartSeparation() { if (!resourceId) return; setSeparationRecovery(null); + const prerequisiteIssues = prerequisiteIssuesForStep( + editor.state, + 'asset-separation', + ); + if (prerequisiteIssues.length > 0) { + reportWorkflowCompletion( + 'asset-separation', + 'failure', + prerequisiteIssues.map((issue) => issue.message).join(';'), + setSeparationStatus, + ); + return; + } try { await invoke('discard_separation_recovery', { projectPath, diff --git a/docs/technical/【技术方案】UI编辑器自动切分素材工作流-2026-09-08.md b/docs/technical/【技术方案】UI编辑器自动切分素材工作流-2026-09-08.md index d06c1ef77..053da070b 100644 --- a/docs/technical/【技术方案】UI编辑器自动切分素材工作流-2026-09-08.md +++ b/docs/technical/【技术方案】UI编辑器自动切分素材工作流-2026-09-08.md @@ -85,6 +85,7 @@ Rust 在接收并校验工具结果后,才把这两个枚举变体映射为正 ## 前端正式接入 - UI 编辑器点击“自动切分素材”时,前端只调用一次 `separate_ui`,消费完整 `SeparationDTO`;separation 内部 batch 不向前端暴露,也不在 UI 中显示 batch 进度。 +- 自动切分的前置检查只要求界面图及对应 UI tree 有效,不要求用户预先导入 SpriteAsset;结果检查负责报告未回填的 Image、丢失的切分素材引用、丢失的字体引用及组件审阅状态。新建和重新开始切分时执行前置检查,继续已有 sidecar 时直接按恢复状态尽力完成。 - 如果 sidecar 已存在未完成的 `state.json`,点击入口时先打开独立弹窗,由用户选择“继续上次自动切分素材”或“开始新的自动切分素材”。继续复用 sidecar 的 pending tree;重新开始只替换当前 `state.json`,不删除 sidecar 图片。 - `BoundNode.cut_image_path` 必须是项目根相对路径。前端使用现有 `import_local_project_image_assets` 登记 cut 图片;由于该通用命令单次最多 100 个路径,前端可以在资源登记阶段按 100 条分组调用,但这不属于 separation batch,也不向用户展示。 - 现有本地资源导入按清洗后的文件名 stem 与内容摘要生成目标路径;相同目标路径直接复用已有 manifest asset ID,内容不同则拒绝覆盖或生成不同摘要路径。前端不自行猜测 SpriteAsset 是否存在,也不从 NodeId 派生 SpriteAssetId。