From 995db645e2f63dd11ad1d6a24a69d92324840d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 19 Aug 2026 15:24:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E6=95=9B=20UI=20Editor=20=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 统一 StageStatus 为 NoProblem、NeedReview 和 Blocked 移除 Pending 与 Passed 的前后端、LLM 草稿及持久化夹具用法 补充旧状态拒绝测试并更新 UI Editor 技术方案 --- .../src/ui_editor/commands/binding.rs | 14 +++---- .../src-tauri/src/ui_editor/commands/merge.rs | 10 ++--- .../src/ui_editor/commands/recognition.rs | 10 ++--- .../src-tauri/src/ui_editor/layout/node.rs | 15 +++++++- .../src-tauri/src/ui_editor/persistence.rs | 16 ++++---- .../src/features/ui-editor/requisites.ts | 38 +++++++------------ .../features/ui-editor/stageStatusOverview.ts | 6 +-- .../features/ui-editor/types/StageStatus.ts | 2 +- .../features/ui-editor/useUiEditorState.ts | 8 ++-- .../ui-editor/components/InputSidebar.tsx | 4 +- .../components/Inspector/InspectorSidebar.tsx | 5 +-- .../components/RecognitionOverview.tsx | 2 +- .../tests/bindingOverview.test.ts | 6 +-- .../tests/stageStatusOverview.test.ts | 10 ++--- .../tests/uiEditorPage.test.ts | 2 +- .../tests/uiEditorPreview.test.tsx | 4 +- .../tests/uiEditorState.test.ts | 20 +++++----- .../useNodeTransformInteraction.test.tsx | 4 +- ...案】AI游戏创作智能体App实施计划-2026-06-24.md | 4 +- 19 files changed, 90 insertions(+), 90 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/binding.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/binding.rs index ee243dbb8..971a02798 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/binding.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/binding.rs @@ -23,7 +23,7 @@ const SYSTEM_PROMPT: &str = r#" * 每个 change 的 components 是该节点完整的新渲染栈,空数组表示明确清空。数组顺序从底到顶渲染。 * 对每个 Component,直接完整返回其全部参数. * 有任何困难或者不确定把状态设为 NeedReview,说明中文原因。 -* 纯结构节点可以返回空数组并标为 Passed。 +* 纯结构节点可以返回空数组并标为 NoProblem。 * 面向用户的 reason 使用中文。 "#; @@ -31,7 +31,7 @@ const SYSTEM_PROMPT: &str = r#" #[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] enum DraftStatus { - Passed, + NoProblem, NeedReview(String), } @@ -134,7 +134,7 @@ fn validate_and_materialize( } let components = change.components; let components_status = match change.components_status { - DraftStatus::Passed => StageStatus::Passed, + DraftStatus::NoProblem => StageStatus::NoProblem, DraftStatus::NeedReview(reason) if reason.trim().is_empty() => { return Err("组件待审状态必须包含原因".to_string()) } @@ -286,7 +286,7 @@ mod tests { let unapproved = BindingChangeDraft { node_id: id("other"), components: Vec::new(), - components_status: DraftStatus::Passed, + components_status: DraftStatus::NoProblem, }; assert!(validate_and_materialize(vec![unapproved], &editable, &sprites).is_err()); @@ -300,7 +300,7 @@ mod tests { }, }, )], - components_status: DraftStatus::Passed, + components_status: DraftStatus::NoProblem, }; assert!(validate_and_materialize(vec![foreign], &editable, &sprites).is_err()); } @@ -312,7 +312,7 @@ mod tests { vec![BindingChangeDraft { node_id: id("editable"), components: Vec::new(), - components_status: DraftStatus::Passed, + components_status: DraftStatus::NoProblem, }], &editable, &HashSet::new(), @@ -320,7 +320,7 @@ mod tests { .expect("valid changed-only clear"); assert_eq!(result.changes.len(), 1); assert!(result.changes[0].components.is_empty()); - assert_eq!(result.changes[0].components_status, StageStatus::Passed); + assert_eq!(result.changes[0].components_status, StageStatus::NoProblem); } #[test] diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/merge.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/merge.rs index affbe7074..919e6308f 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/merge.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/merge.rs @@ -287,8 +287,8 @@ mod materialize { metadata: NodeMetadata { name: container_name, description: container_description, - layout_status: StageStatus::Passed, - components_status: StageStatus::Passed, + layout_status: StageStatus::NoProblem, + components_status: StageStatus::NoProblem, allow_llm_edit_layout: true, allow_llm_edit_component: true, source: NodeSource::Llm, @@ -415,8 +415,8 @@ mod tests { metadata: NodeMetadata { name: id.to_string(), description: String::new(), - layout_status: StageStatus::Passed, - components_status: StageStatus::Passed, + layout_status: StageStatus::NoProblem, + components_status: StageStatus::NoProblem, allow_llm_edit_layout: true, allow_llm_edit_component: true, source: NodeSource::Human, @@ -459,7 +459,7 @@ mod tests { result.root.children_display_mode, ChildrenDisplayMode::Exclusive ); - assert_eq!(result.root.metadata.components_status, StageStatus::Passed); + assert_eq!(result.root.metadata.components_status, StageStatus::NoProblem); assert_eq!(result.root.children.len(), 2); } } diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/recognition.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/recognition.rs index c054020b7..27d1f65a2 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/recognition.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/recognition.rs @@ -258,7 +258,7 @@ fn convert_node( transform.set_resolved_rect(&parent_rect, target_rect); let status = if blocked_reasons.is_empty() { match &source.confidence { - Confidence::Confident => StageStatus::Passed, + Confidence::Confident => StageStatus::NoProblem, Confidence::UnSure(reason) => StageStatus::NeedReview(reason.clone()), } } else { @@ -276,7 +276,7 @@ fn convert_node( name: source.name.clone(), description: source.description.clone(), layout_status: status, - components_status: StageStatus::Passed, + components_status: StageStatus::NoProblem, allow_llm_edit_layout: true, allow_llm_edit_component: true, source: NodeSource::Llm, @@ -440,7 +440,7 @@ mod tests { converted.layout.transform.resolve(&root_rect), UIRect::new(Point2::new(50.0, 25.0), Vector2::new(100.0, 50.0)), ); - assert_eq!(converted.metadata.components_status, StageStatus::Passed); + assert_eq!(converted.metadata.components_status, StageStatus::NoProblem); } #[test] @@ -677,8 +677,8 @@ pub(crate) async fn recognize_ui_impl( metadata: NodeMetadata { name: "页面根节点".to_string(), description: String::new(), - layout_status: StageStatus::Passed, - components_status: StageStatus::Passed, + layout_status: StageStatus::NoProblem, + components_status: StageStatus::NoProblem, allow_llm_edit_layout: true, allow_llm_edit_component: true, source: NodeSource::System, diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/layout/node.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/layout/node.rs index ad6a87984..bd9fc0443 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/layout/node.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/layout/node.rs @@ -27,12 +27,23 @@ pub enum NodeSource { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] #[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] pub enum StageStatus { - Pending, - Passed, + NoProblem, NeedReview(String), // reason inside Blocked(String), // reason inside } +#[cfg(test)] +mod tests { + use super::StageStatus; + + #[test] + fn rejects_retired_status_values() { + for value in ["\"Pending\"", "\"Passed\""] { + assert!(serde_json::from_str::(value).is_err()); + } + } +} + #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] #[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] pub struct NodeMetadata { diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/persistence.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/persistence.rs index 76c9428f4..80c584609 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/persistence.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/persistence.rs @@ -757,8 +757,8 @@ mod tests { "metadata": { "name": "页面根节点", "description": "", - "layout_status": "Passed", - "components_status": "Passed", + "layout_status": "NoProblem", + "components_status": "NoProblem", "allow_llm_edit_layout": true, "allow_llm_edit_component": true, "source": "System" @@ -783,8 +783,8 @@ mod tests { "metadata": { "name": "拖拽节点", "description": "", - "layout_status": "Passed", - "components_status": "Passed", + "layout_status": "NoProblem", + "components_status": "NoProblem", "allow_llm_edit_layout": true, "allow_llm_edit_component": true, "source": "Human" @@ -934,8 +934,8 @@ mod tests { "metadata": { "name": "根节点", "description": "", - "layout_status": "Pending", - "components_status": "Pending", + "layout_status": "NoProblem", + "components_status": "NoProblem", "allow_llm_edit_layout": true, "allow_llm_edit_component": true, "source": "System" @@ -1128,8 +1128,8 @@ mod tests { "metadata": { "name": "根节点", "description": "", - "layout_status": "Pending", - "components_status": "Pending", + "layout_status": "NoProblem", + "components_status": "NoProblem", "allow_llm_edit_layout": false, "allow_llm_edit_component": false, "source": "System" 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 98bb8538e..af3e0c929 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 @@ -145,37 +145,27 @@ export function validateLayoutReviewPrerequisites( function layoutStatusIssue( status: StageStatus, ): UiEditorPrerequisiteIssue | null { - switch (status) { - case 'Pending': - return { code: 'layout-pending', message: '存在尚未识别的节点布局' }; - case 'Blocked': - return { code: 'layout-blocked', message: '存在被阻塞的节点结果' }; - case 'Passed': - return null; - default: - return { - code: 'layout-needs-review', - message: status.NeedReview || '存在需要人工审阅的节点结果', - }; + if (status === 'NoProblem') return null; + if ('Blocked' in status) { + return { code: 'layout-blocked', message: '存在被阻塞的节点结果' }; } + return { + code: 'layout-needs-review', + message: status.NeedReview || '存在需要人工审阅的节点结果', + }; } function componentStatusIssue( status: StageStatus, ): UiEditorPrerequisiteIssue | null { - switch (status) { - case 'Pending': - return { code: 'components-pending', message: '存在尚未绑定的节点组件' }; - case 'Blocked': - return { code: 'components-blocked', message: '存在被阻塞的节点结果' }; - case 'Passed': - return null; - default: - return { - code: 'components-needs-review', - message: status.NeedReview || '存在需要人工审阅的节点结果', - }; + if (status === 'NoProblem') return null; + if ('Blocked' in status) { + return { code: 'components-blocked', message: '存在被阻塞的节点结果' }; } + return { + code: 'components-needs-review', + message: status.NeedReview || '存在需要人工审阅的节点结果', + }; } function visitNodes( diff --git a/apps/ai-game-creator-shell/src/features/ui-editor/stageStatusOverview.ts b/apps/ai-game-creator-shell/src/features/ui-editor/stageStatusOverview.ts index 938432b78..c2f9e9c00 100644 --- a/apps/ai-game-creator-shell/src/features/ui-editor/stageStatusOverview.ts +++ b/apps/ai-game-creator-shell/src/features/ui-editor/stageStatusOverview.ts @@ -17,7 +17,7 @@ export type UiTreeNodeTarget = { export type StageStatusOverview = { total: number; needsAttention: number; - passed: number; + noProblem: number; blocked: number; }; @@ -50,14 +50,14 @@ export function getStageStatusOverview( const overview: StageStatusOverview = { total: 0, needsAttention: 0, - passed: 0, + noProblem: 0, blocked: 0, }; for (const { node } of collectUiTreeNodeTargets(uiTrees)) { const status = node.metadata[field]; overview.total += 1; - if (status === 'Passed') overview.passed += 1; + if (status === 'NoProblem') overview.noProblem += 1; if (isBlocked(status)) overview.blocked += 1; if (isBlocked(status) || isNeedReview(status)) { overview.needsAttention += 1; diff --git a/apps/ai-game-creator-shell/src/features/ui-editor/types/StageStatus.ts b/apps/ai-game-creator-shell/src/features/ui-editor/types/StageStatus.ts index b8cd73741..9a12ba476 100644 --- a/apps/ai-game-creator-shell/src/features/ui-editor/types/StageStatus.ts +++ b/apps/ai-game-creator-shell/src/features/ui-editor/types/StageStatus.ts @@ -1,4 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export type StageStatus = - 'Pending' | 'Passed' | { NeedReview: string } | { Blocked: string }; + 'NoProblem' | { NeedReview: string } | { Blocked: string }; diff --git a/apps/ai-game-creator-shell/src/features/ui-editor/useUiEditorState.ts b/apps/ai-game-creator-shell/src/features/ui-editor/useUiEditorState.ts index 98edb788e..5bfe37adf 100644 --- a/apps/ai-game-creator-shell/src/features/ui-editor/useUiEditorState.ts +++ b/apps/ai-game-creator-shell/src/features/ui-editor/useUiEditorState.ts @@ -132,8 +132,8 @@ function createPageRoot(state: State): Node { metadata: { name: '页面根节点', description: '', - layout_status: 'Passed', - components_status: 'Passed', + layout_status: 'NoProblem', + components_status: 'NoProblem', allow_llm_edit_layout: true, allow_llm_edit_component: true, source: 'System', @@ -163,8 +163,8 @@ function createHumanNode(state: State): Node { metadata: { name: '新节点', description: '', - layout_status: 'Passed', - components_status: 'Passed', + layout_status: 'NoProblem', + components_status: 'NoProblem', allow_llm_edit_layout: true, allow_llm_edit_component: true, source: 'Human', diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/components/InputSidebar.tsx b/apps/ai-game-creator-shell/src/view/ui-editor/components/InputSidebar.tsx index 5d2b134f1..fe363f319 100644 --- a/apps/ai-game-creator-shell/src/view/ui-editor/components/InputSidebar.tsx +++ b/apps/ai-game-creator-shell/src/view/ui-editor/components/InputSidebar.tsx @@ -63,8 +63,8 @@ export function InputSidebar({ input }: { input: UiEditorInputProjection }) { metadata: { name: 'UI Trees', description: '', - layout_status: 'Passed', - components_status: 'Passed', + layout_status: 'NoProblem', + components_status: 'NoProblem', allow_llm_edit_layout: false, allow_llm_edit_component: false, source: 'System', diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/InspectorSidebar.tsx b/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/InspectorSidebar.tsx index 7d7a811d5..c6fb65ee5 100644 --- a/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/InspectorSidebar.tsx +++ b/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/InspectorSidebar.tsx @@ -839,11 +839,10 @@ function NodeStageSelect({ if (readOnly) return; const next = event.target.value; if (next === 'NeedReview' || next === 'Blocked') return; - onChange(next === 'Pending' ? 'Pending' : 'Passed'); + onChange('NoProblem'); }} > - - + {kind === 'NeedReview' ? ( ) : null} diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/components/RecognitionOverview.tsx b/apps/ai-game-creator-shell/src/view/ui-editor/components/RecognitionOverview.tsx index 6ce579c7f..69a262e0a 100644 --- a/apps/ai-game-creator-shell/src/view/ui-editor/components/RecognitionOverview.tsx +++ b/apps/ai-game-creator-shell/src/view/ui-editor/components/RecognitionOverview.tsx @@ -53,7 +53,7 @@ export function RecognitionOverview({ disabled={overview.needsAttention === 0} onClick={attentionCycle.focusNext} /> - + { expect(getStageStatusOverview(trees, 'layout_status')).toEqual({ total: 5, needsAttention: 2, - passed: 2, + noProblem: 3, blocked: 1, }); }); @@ -73,7 +73,7 @@ describe('stageStatusOverview', () => { 'review-a', 'blocked-a', 'root-b', - 'passed-b', + 'no-problem-b', ]); }); diff --git a/apps/ai-game-creator-shell/tests/uiEditorPage.test.ts b/apps/ai-game-creator-shell/tests/uiEditorPage.test.ts index 04cbfc84e..115df60ec 100644 --- a/apps/ai-game-creator-shell/tests/uiEditorPage.test.ts +++ b/apps/ai-game-creator-shell/tests/uiEditorPage.test.ts @@ -336,7 +336,7 @@ describe('UiEditorPage', () => { act(() => { result.current.highlightStatusField('components_status'); - result.current.setNodeMetadata({ components_status: 'Passed' }); + result.current.setNodeMetadata({ components_status: 'NoProblem' }); }); expect(result.current.highlightedStatusField).toBeNull(); }); diff --git a/apps/ai-game-creator-shell/tests/uiEditorPreview.test.tsx b/apps/ai-game-creator-shell/tests/uiEditorPreview.test.tsx index a687bdb30..f31090c98 100644 --- a/apps/ai-game-creator-shell/tests/uiEditorPreview.test.tsx +++ b/apps/ai-game-creator-shell/tests/uiEditorPreview.test.tsx @@ -28,8 +28,8 @@ function node(id: string, children: UiNode[] = []): UiNode { metadata: { name: id, description: '', - layout_status: 'Passed', - components_status: 'Passed', + layout_status: 'NoProblem', + components_status: 'NoProblem', allow_llm_edit_layout: true, allow_llm_edit_component: true, source: 'System', diff --git a/apps/ai-game-creator-shell/tests/uiEditorState.test.ts b/apps/ai-game-creator-shell/tests/uiEditorState.test.ts index 8b8c1b059..43c9dac4d 100644 --- a/apps/ai-game-creator-shell/tests/uiEditorState.test.ts +++ b/apps/ai-game-creator-shell/tests/uiEditorState.test.ts @@ -69,8 +69,8 @@ function nodeWithSprite(id: string): Node { metadata: { name: 'Image', description: '', - layout_status: 'Passed', - components_status: 'Pending', + layout_status: 'NoProblem', + components_status: 'NoProblem', allow_llm_edit_layout: true, allow_llm_edit_component: true, source: 'Llm', @@ -106,8 +106,8 @@ function pageRoot(id: string, children: Node[] = []): Node { metadata: { name: id, description: '', - layout_status: 'Passed', - components_status: 'Pending', + layout_status: 'NoProblem', + components_status: 'NoProblem', allow_llm_edit_layout: true, allow_llm_edit_component: true, source: 'System', @@ -250,8 +250,8 @@ describe('useUiEditorState', () => { expect.objectContaining({ root: expect.objectContaining({ metadata: expect.objectContaining({ - layout_status: 'Passed', - components_status: 'Passed', + layout_status: 'NoProblem', + components_status: 'NoProblem', }), }), }), @@ -279,8 +279,8 @@ describe('useUiEditorState', () => { expect( result.current.state.ui_trees[0]!.root.children[0]?.metadata, ).toMatchObject({ - layout_status: 'Passed', - components_status: 'Passed', + layout_status: 'NoProblem', + components_status: 'NoProblem', }); }); @@ -427,8 +427,8 @@ describe('useUiEditorState', () => { metadata: { name: '页面根节点', description: '', - layout_status: 'Passed', - components_status: 'Pending', + layout_status: 'NoProblem', + components_status: 'NoProblem', allow_llm_edit_layout: true, allow_llm_edit_component: true, source: 'System', diff --git a/apps/ai-game-creator-shell/tests/useNodeTransformInteraction.test.tsx b/apps/ai-game-creator-shell/tests/useNodeTransformInteraction.test.tsx index 4dc730e0e..f2a246549 100644 --- a/apps/ai-game-creator-shell/tests/useNodeTransformInteraction.test.tsx +++ b/apps/ai-game-creator-shell/tests/useNodeTransformInteraction.test.tsx @@ -27,8 +27,8 @@ function node(id: string, children: UiNode[] = []): UiNode { metadata: { name: id, description: '', - layout_status: 'Passed', - components_status: 'Passed', + layout_status: 'NoProblem', + components_status: 'NoProblem', allow_llm_edit_layout: true, allow_llm_edit_component: true, source: 'System', diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 1fa2d8785..fd468169d 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -32,7 +32,7 @@ UI Editor Inspector 的全局只读状态唯一来源是 `controller.editor.isLo ## 2026-08-18 UI Editor 结构识别、合并与增量导入边界 -UI Editor 当前把“识别界面结构”定义为结构草稿阶段,而不是完整视觉还原阶段。识别 DTO 只负责输出节点层级、几何、名称、描述和置信度;节点组件暂为空,由后续“绑定视觉素材”阶段补齐 Image / Text 组件。`applyRecognitionResult` 可以整体替换当前 `ui_trees`,但该替换只代表结构结果,不能宣称已经保留截图中的视觉内容;组件状态继续为 `Pending`,前置检查负责阻止跳过绑定阶段。 +UI Editor 当前把“识别界面结构”定义为结构草稿阶段,而不是完整视觉还原阶段。识别 DTO 只负责输出节点层级、几何、名称、描述和置信度;节点组件暂为空,由后续“绑定视觉素材”阶段补齐 Image / Text 组件。`applyRecognitionResult` 可以整体替换当前 `ui_trees`,但该替换只代表结构结果,不能宣称已经保留截图中的视觉内容;组件状态使用 `NoProblem`,前置检查仍会根据空组件和素材绑定情况阻止跳过绑定阶段。 多图合并使用 LLM 返回的语义投影树。`Simple` 和 `Merged` 只列出希望进入结果树的原始节点;未被计划引用的源节点表示本次合并判定为冗余或不属于目标公共结构,允许被丢弃,不要求 `used_original_ids` 覆盖全部输入节点。重复 ID 和未知 ID 仍然是错误;省略不是隐式复制或随机删除,而是合并计划的正式语义。 @@ -1142,7 +1142,7 @@ game-project/ ## 2026-08-18 UI Editor 新建节点组件状态 -- 所有非组件绑定流程创建的 UI Editor 节点,`components_status` 与 `layout_status` 一致初始化为 `Passed`,覆盖前端新建页面根 / 人工节点,以及 Rust 结构识别与合并产生的节点。组件绑定命令仍只对 LLM 显式返回的节点写入其 `Passed` 或 `NeedReview` 结果;未返回的节点状态不变。 +- 所有非组件绑定流程创建的 UI Editor 节点,`components_status` 与 `layout_status` 一致初始化为 `NoProblem`,覆盖前端新建页面根 / 人工节点,以及 Rust 结构识别与合并产生的节点。组件绑定命令仍只对 LLM 显式返回的节点写入其 `NoProblem` 或 `NeedReview` 结果;未返回的节点状态不变。 ## 2026-08-18 UI Editor 节点预览可见性