diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/index.tsx b/apps/ai-game-creator-shell/src/view/ui-editor/index.tsx
index 8476fbe08..de1f21527 100644
--- a/apps/ai-game-creator-shell/src/view/ui-editor/index.tsx
+++ b/apps/ai-game-creator-shell/src/view/ui-editor/index.tsx
@@ -16,6 +16,7 @@ import { PreviewWorkspace } from './components/preview/PreviewWorkspace';
import { RecognitionOverview } from './components/RecognitionOverview';
import { ToolNavigation } from './components/ToolNavigation';
import { WorkflowActionCard } from './components/WorkflowActionCard';
+import { WorkflowCompletionModal } from './components/WorkflowCompletionModal';
import { UI_EDITOR_STEPS, type UiEditorStepId } from './model';
import {
type UiEditorWorkflowProjection,
@@ -329,6 +330,10 @@ export default function UiEditorPage({
+
setReturnConfirmOpen(false)}
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 d6f10938a..6a7eefab2 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
@@ -10,7 +10,10 @@ import {
} from '../../features/ui-editor/importAdapter';
import { applyMergeResult } from '../../features/ui-editor/merge';
import { applyRecognitionResult } from '../../features/ui-editor/recognition';
-import type { StageStatusField } from '../../features/ui-editor/stageStatusOverview';
+import {
+ getStageStatusOverview,
+ type StageStatusField,
+} from '../../features/ui-editor/stageStatusOverview';
import { collectUiNodeIds } from '../../features/ui-editor/treeUtils';
import type { BindingDTO } from '../../features/ui-editor/types/BindingDTO';
import type { ChildrenDisplayMode } from '../../features/ui-editor/types/ChildrenDisplayMode';
@@ -54,6 +57,10 @@ import {
prerequisiteIssuesForStep,
type UiEditorPrerequisiteIssue,
} from './components/WorkflowChecks';
+import {
+ appendWorkflowCheckPrompt,
+ type WorkflowCompletionNotice,
+} from './components/workflowCompletionNotice';
import {
type PendingResourceRemoval,
removalHasDownstreamReferences,
@@ -248,6 +255,8 @@ export function useUiEditorSession(
const [hasSuggested, setHasSuggested] = useState(false);
const [hasRecognized, setHasRecognized] = useState(false);
const [hasBound, setHasBound] = useState(false);
+ const [completionNotice, setCompletionNotice] =
+ useState(null);
useEffect(() => {
setActiveStep(initialStep);
@@ -954,6 +963,7 @@ export function useUiEditorSession(
async function suggestUiDesignSemantics() {
if (isSuggesting || isWorkflowBusy) return;
+ setCompletionNotice(null);
setSuggestionStatus(null);
setIsSuggesting(true);
try {
@@ -964,12 +974,26 @@ export function useUiEditorSession(
);
editor.replaceState(applyUiDesignSuggestions(snapshot, suggestions));
setHasSuggested(true);
- setSuggestionStatus(`已应用 ${suggestions.length} 条参考图语义建议。`);
+ const message = appendWorkflowCheckPrompt(
+ `参考图分析完成:已应用 ${suggestions.length} 条参考图语义建议`,
+ );
+ setSuggestionStatus(message);
+ setCompletionNotice({
+ step: 'reference-analysis',
+ outcome: 'success',
+ message,
+ });
});
} catch (cause) {
- setSuggestionStatus(
+ const message = appendWorkflowCheckPrompt(
cause instanceof Error ? cause.message : String(cause),
);
+ setSuggestionStatus(message);
+ setCompletionNotice({
+ step: 'reference-analysis',
+ outcome: 'failure',
+ message,
+ });
} finally {
setIsSuggesting(false);
}
@@ -977,6 +1001,7 @@ export function useUiEditorSession(
async function recognizeUi() {
if (isRecognizing || isWorkflowBusy) return;
+ setCompletionNotice(null);
setRecognitionStatus(null);
setIsRecognizing(true);
try {
@@ -985,15 +1010,34 @@ export function useUiEditorSession(
projectPath,
state: snapshot,
});
- editor.replaceState(applyRecognitionResult(snapshot, result));
+ const nextState = applyRecognitionResult(snapshot, result);
+ editor.replaceState(nextState);
setHasRecognized(true);
setSelectedNodeId(null);
- setRecognitionStatus(`已替换 ${result.ui_trees.length} 棵界面树。`);
+ const overview = getStageStatusOverview(
+ nextState.ui_trees,
+ 'layout_status',
+ );
+ const message = appendWorkflowCheckPrompt(
+ `界面结构识别完成:已替换 ${result.ui_trees.length} 棵界面树,待检查 ${overview.needsAttention} 项(必须修复 ${overview.blocked} 项)`,
+ );
+ setRecognitionStatus(message);
+ setCompletionNotice({
+ step: 'structure-recognition',
+ outcome: 'success',
+ message,
+ });
});
} catch (cause) {
- setRecognitionStatus(
+ const message = appendWorkflowCheckPrompt(
cause instanceof Error ? cause.message : String(cause),
);
+ setRecognitionStatus(message);
+ setCompletionNotice({
+ step: 'structure-recognition',
+ outcome: 'failure',
+ message,
+ });
} finally {
setIsRecognizing(false);
}
@@ -1020,6 +1064,7 @@ export function useUiEditorSession(
async function bindComponents() {
if (isBinding || isWorkflowBusy) return;
+ setCompletionNotice(null);
setBindingStatus(null);
setIsBinding(true);
try {
@@ -1047,13 +1092,27 @@ export function useUiEditorSession(
history: index < batches.length - 1 ? 'skip' : 'record',
});
}
- setBindingStatus(
- `组件绑定完成(${batches.length}/${batches.length})。`,
+ const message = appendWorkflowCheckPrompt(
+ `视觉素材绑定完成:已处理 ${batches.length}/${batches.length} 个批次`,
);
+ setBindingStatus(message);
setHasBound(true);
+ setCompletionNotice({
+ step: 'visual-binding',
+ outcome: 'success',
+ message,
+ });
});
} catch (cause) {
- setBindingStatus(cause instanceof Error ? cause.message : String(cause));
+ const message = appendWorkflowCheckPrompt(
+ cause instanceof Error ? cause.message : String(cause),
+ );
+ setBindingStatus(message);
+ setCompletionNotice({
+ step: 'visual-binding',
+ outcome: 'failure',
+ message,
+ });
} finally {
setIsBinding(false);
}
@@ -1318,6 +1377,7 @@ export function useUiEditorSession(
isBinding,
hasBound,
bindingStatus,
+ completionNotice,
bindComponents,
requestStepChange,
continueToNextStep: () => {
@@ -1325,6 +1385,7 @@ export function useUiEditorSession(
},
confirmStepChange,
cancelStepChange: () => setPendingWorkflowStepChange(null),
+ dismissCompletionNotice: () => setCompletionNotice(null),
},
dialogs: {
projectPath,