From 761d32cebfeeb043cb9b71c8cf57c85f00bf1e6c 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 10:43:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=86=E5=88=86=E8=87=AA=E5=8A=A8=E5=88=86?= =?UTF-8?q?=E7=A6=BB=E6=A8=A1=E5=9E=8B=E5=B9=B6=E5=A2=9E=E5=8A=A0=E6=96=87?= =?UTF-8?q?=E5=AD=97=E9=81=AE=E7=BD=A9=E5=8C=BA=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 separation model 拆为 note、node、binding、result 模块 为 SeparationNode 增加 text_mask_areas 字段并同步 TypeScript 类型 --- .../src/ui_editor/commands/separation/mod.rs | 6 + .../ui_editor/commands/separation/model.rs | 138 ------------------ .../commands/separation/model/binding.rs | 50 +++++++ .../commands/separation/model/mod.rs | 13 ++ .../commands/separation/model/node.rs | 44 ++++++ .../commands/separation/model/note.rs | 23 +++ .../commands/separation/model/result.rs | 43 ++++++ .../ui-editor/types/SeparationNode.ts | 3 +- .../features/ui-editor/types/TextMaskArea.ts | 3 + 9 files changed, 184 insertions(+), 139 deletions(-) delete mode 100644 apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model.rs create mode 100644 apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/binding.rs create mode 100644 apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/mod.rs create mode 100644 apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/node.rs create mode 100644 apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/note.rs create mode 100644 apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/result.rs create mode 100644 apps/ai-game-creator-shell/src/features/ui-editor/types/TextMaskArea.ts diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/mod.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/mod.rs index bf51348a7..91dc38009 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/mod.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/mod.rs @@ -118,6 +118,7 @@ mod tests { description: "image".to_string(), rework_notes: Vec::new(), }, + text_mask_areas: vec![], children: vec![], rework_count: 0, }; @@ -204,6 +205,7 @@ mod tests { width_px: 1, height_px: 1, note: SeparationNote::default(), + text_mask_areas: vec![], children: vec![], rework_count: 0, }; @@ -263,6 +265,7 @@ mod tests { width_px: 10, height_px: 10, note: SeparationNote::default(), + text_mask_areas: vec![], children: vec![], rework_count: 0, }; @@ -273,6 +276,7 @@ mod tests { width_px: 10, height_px: 10, note: SeparationNote::default(), + text_mask_areas: vec![], children: vec![], rework_count: 0, }; @@ -283,6 +287,7 @@ mod tests { width_px: 5, height_px: 5, note: SeparationNote::default(), + text_mask_areas: vec![], children: vec![], rework_count: 0, }; @@ -295,6 +300,7 @@ mod tests { width_px: 100, height_px: 100, note: SeparationNote::default(), + text_mask_areas: vec![], children: vec![a, b, c], rework_count: 0, }, diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model.rs deleted file mode 100644 index 12fca349c..000000000 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model.rs +++ /dev/null @@ -1,138 +0,0 @@ -use crate::ui_editor::utils::{NodeId, UIDesignImageId}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use ts_rs::TS; - -pub const SEPARATION_STATE_SCHEMA_VERSION: &str = "ui-editor-separation-state.v1"; -pub const MAX_REWORK_COUNT: u32 = 3; -pub const MAX_REWORK_NOTE_CHARS: usize = 512; - -#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize, TS)] -#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] -pub struct SeparationNote { - pub description: String, - pub rework_notes: Vec, -} -impl SeparationNote { - pub fn as_prompt(&self) -> String { - let mut prompt = format!("desc: {}", self.description); - if !self.rework_notes.is_empty() { - prompt.push_str("\nprevious rework notes:"); - for note in &self.rework_notes { - prompt.push_str("\n- "); - prompt.push_str(note); - } - } - prompt - } -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] -#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] -pub struct SeparationNode { - pub id: NodeId, - pub global_pos_x_px: u32, - pub global_pos_y_px: u32, - pub width_px: u32, - pub height_px: u32, - pub note: SeparationNote, - pub children: Vec, - pub rework_count: u32, -} -impl SeparationNode { - pub fn as_prompt(&self) -> String { - format!( - "node_id={} note: {}", - self.id.as_str(), - self.note.as_prompt() - ) - } -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] -#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] -pub struct SeparationTree { - pub src_ui_design: UIDesignImageId, - pub root: SeparationNode, - pub root_extractable: bool, -} -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] -#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] -pub struct BoundNode { - pub node_id: NodeId, - pub cut_image_path: String, -} -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] -#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] -pub struct ProblematicNode { - pub node_id: NodeId, - pub problem_description: String, - pub rework_count: u32, -} -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] -#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] -pub struct SeparationState { - pub schema_version: String, - pub trees: Vec, - pub bound: Vec, - pub problematic_nodes: Vec, -} -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] -#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] -pub struct SeparationDTO { - pub bound_nodes: Vec, - pub problematic_nodes: Vec, -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] -#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] -pub struct SeparationRecoveryDTO { - pub exists: bool, - pub bound_node_count: usize, - pub problematic_node_count: usize, - pub has_pending_tree: bool, -} - -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] -#[schemars(deny_unknown_fields)] -pub struct BindingArea { - pub global_pos_x_px: u32, - pub global_pos_y_px: u32, - pub width_px: u32, - pub height_px: u32, -} -impl BindingArea { - pub fn validate_in(&self, w: u32, h: u32) -> Result<(), String> { - if self.width_px == 0 || self.height_px == 0 { - return Err("BindingArea 宽度和高度必须大于 0".into()); - } - if self - .global_pos_x_px - .checked_add(self.width_px) - .is_none_or(|v| v > w) - || self - .global_pos_y_px - .checked_add(self.height_px) - .is_none_or(|v| v > h) - { - return Err("BindingArea 超出处理图边界".into()); - } - Ok(()) - } -} -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] -#[schemars(deny_unknown_fields)] -pub enum BindingDecision { - Ok { - extracted_area: BindingArea, - to_node: NodeId, - }, - NeedRework { - problem_description: String, - to_node: NodeId, - }, -} -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] -pub struct BindingResp { - pub decisions: Vec, -} diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/binding.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/binding.rs new file mode 100644 index 000000000..d552e8b12 --- /dev/null +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/binding.rs @@ -0,0 +1,50 @@ +use crate::ui_editor::utils::NodeId; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[schemars(deny_unknown_fields)] +pub struct BindingArea { + pub global_pos_x_px: u32, + pub global_pos_y_px: u32, + pub width_px: u32, + pub height_px: u32, +} + +impl BindingArea { + pub fn validate_in(&self, w: u32, h: u32) -> Result<(), String> { + if self.width_px == 0 || self.height_px == 0 { + return Err("BindingArea 宽度和高度必须大于 0".into()); + } + if self + .global_pos_x_px + .checked_add(self.width_px) + .is_none_or(|v| v > w) + || self + .global_pos_y_px + .checked_add(self.height_px) + .is_none_or(|v| v > h) + { + return Err("BindingArea 超出处理图边界".into()); + } + Ok(()) + } +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[schemars(deny_unknown_fields)] +pub enum BindingDecision { + Ok { + extracted_area: BindingArea, + to_node: NodeId, + }, + NeedRework { + problem_description: String, + to_node: NodeId, + }, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +pub struct BindingResp { + pub decisions: Vec, +} diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/mod.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/mod.rs new file mode 100644 index 000000000..7e1032bac --- /dev/null +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/mod.rs @@ -0,0 +1,13 @@ +mod binding; +mod node; +mod note; +mod result; + +pub use binding::*; +pub use node::*; +pub use note::*; +pub use result::*; + +pub const SEPARATION_STATE_SCHEMA_VERSION: &str = "ui-editor-separation-state.v1"; +pub const MAX_REWORK_COUNT: u32 = 3; +pub const MAX_REWORK_NOTE_CHARS: usize = 512; diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/node.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/node.rs new file mode 100644 index 000000000..e0ab3c8fb --- /dev/null +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/node.rs @@ -0,0 +1,44 @@ +use crate::ui_editor::utils::{NodeId, UIDesignImageId}; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, TS)] +#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] +pub struct TextMaskArea { + pub global_pos_x_px: u32, + pub global_pos_y_px: u32, + pub width_px: u32, + pub height_px: u32, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] +#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] +pub struct SeparationNode { + pub id: NodeId, + pub global_pos_x_px: u32, + pub global_pos_y_px: u32, + pub width_px: u32, + pub height_px: u32, + pub note: super::SeparationNote, + pub text_mask_areas: Vec, + pub children: Vec, + pub rework_count: u32, +} + +impl SeparationNode { + pub fn as_prompt(&self) -> String { + format!( + "node_id={} note: {}", + self.id.as_str(), + self.note.as_prompt() + ) + } +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] +#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] +pub struct SeparationTree { + pub src_ui_design: UIDesignImageId, + pub root: SeparationNode, + pub root_extractable: bool, +} diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/note.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/note.rs new file mode 100644 index 000000000..15c500b59 --- /dev/null +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/note.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize, TS)] +#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] +pub struct SeparationNote { + pub description: String, + pub rework_notes: Vec, +} + +impl SeparationNote { + pub fn as_prompt(&self) -> String { + let mut prompt = format!("desc: {}", self.description); + if !self.rework_notes.is_empty() { + prompt.push_str("\nprevious rework notes:"); + for note in &self.rework_notes { + prompt.push_str("\n- "); + prompt.push_str(note); + } + } + prompt + } +} diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/result.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/result.rs new file mode 100644 index 000000000..82b34bdcd --- /dev/null +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/model/result.rs @@ -0,0 +1,43 @@ +use crate::ui_editor::utils::NodeId; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] +#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] +pub struct BoundNode { + pub node_id: NodeId, + pub cut_image_path: String, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] +#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] +pub struct ProblematicNode { + pub node_id: NodeId, + pub problem_description: String, + pub rework_count: u32, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] +#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] +pub struct SeparationState { + pub schema_version: String, + pub trees: Vec, + pub bound: Vec, + pub problematic_nodes: Vec, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] +#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] +pub struct SeparationDTO { + pub bound_nodes: Vec, + pub problematic_nodes: Vec, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)] +#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/ui-editor/types/"))] +pub struct SeparationRecoveryDTO { + pub exists: bool, + pub bound_node_count: usize, + pub problematic_node_count: usize, + pub has_pending_tree: bool, +} diff --git a/apps/ai-game-creator-shell/src/features/ui-editor/types/SeparationNode.ts b/apps/ai-game-creator-shell/src/features/ui-editor/types/SeparationNode.ts index 7aaa94d6f..09a0d8a53 100644 --- a/apps/ai-game-creator-shell/src/features/ui-editor/types/SeparationNode.ts +++ b/apps/ai-game-creator-shell/src/features/ui-editor/types/SeparationNode.ts @@ -1,5 +1,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { NodeId } from "./NodeId"; import type { SeparationNote } from "./SeparationNote"; +import type { TextMaskArea } from "./TextMaskArea"; -export type SeparationNode = { id: NodeId, global_pos_x_px: number, global_pos_y_px: number, width_px: number, height_px: number, note: SeparationNote, children: Array, rework_count: number, }; +export type SeparationNode = { id: NodeId, global_pos_x_px: number, global_pos_y_px: number, width_px: number, height_px: number, note: SeparationNote, text_mask_areas: Array, children: Array, rework_count: number, }; diff --git a/apps/ai-game-creator-shell/src/features/ui-editor/types/TextMaskArea.ts b/apps/ai-game-creator-shell/src/features/ui-editor/types/TextMaskArea.ts new file mode 100644 index 000000000..5e450d070 --- /dev/null +++ b/apps/ai-game-creator-shell/src/features/ui-editor/types/TextMaskArea.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TextMaskArea = { global_pos_x_px: number, global_pos_y_px: number, width_px: number, height_px: number, };