From ec9a4fcbac2ca1b7607414c470e69a18fa79542d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 24 Sep 2026 17:24:47 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=99=E8=8A=82=E7=82=B9=20offset=20?= =?UTF-8?q?=E8=A1=A5=E4=B8=8A=E6=9C=89=E9=99=90=E6=95=B0=E5=80=BC=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src-tauri/src/ui_editor/persistence.rs 的 validate_node 现在校验 node.offset.min/max 必须是有限数值,与相邻的 Transform/custom_minimum_size 校验一致;此前只有 Transform 的 anchor/offset 被校验,NaN 或 Infinity 的树级偏移可以存盘,预览按 offset.min 定位时整棵树的渲染会一起失效 同文件新增 rejects_non_finite_node_offset,覆盖「offset 全有限可保存、min 变成 Infinity 后校验失败」 --- .../src-tauri/src/ui_editor/persistence.rs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) 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 a95d17803..c03c63e70 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 @@ -710,6 +710,11 @@ fn validate_node( { return Err("UI 节点 Transform 必须是有限数值".to_string()); } + if !node.offset.min.iter().all(|value| value.is_finite()) + || !node.offset.max.iter().all(|value| value.is_finite()) + { + return Err("UI 节点 offset 必须是有限数值".to_string()); + } if node .layout .transform @@ -1321,6 +1326,62 @@ mod tests { ); } + #[test] + fn rejects_non_finite_node_offset() { + let state: State = serde_json::from_value(serde_json::json!({ + "ui_trees": [{ + "src_ui_design": "page", + "root": { + "id": "root", + "layout": { + "transform": { + "anchor_min": [0.0, 0.0], + "anchor_max": [1.0, 1.0], + "offset_min": [0.0, 0.0], + "offset_max": [0.0, 0.0] + }, + "custom_minimum_size": [0.0, 0.0], + "size_flags_horizontal": 1, + "size_flags_vertical": 1, + "size_flags_stretch_ratio": 1.0, + "container": "None" + }, + "metadata": { + "name": "根节点", + "description": "", + "layout_status": "NoProblem", + "component_status": "NoProblem", + "allow_llm_edit_layout": true, + "allow_llm_edit_component": true, + "source": "System" + }, + "component": null, + "children_display_mode": "Stack", + "children": [], + "offset": { "min": [0.0, 0.0], "max": [1280.0, 720.0] } + } + }], + "ui_design_images": { + "page": { + "path": "missing.png", + "pixel_size": [1280.0, 720.0], + "pixels_per_unit": 1.0 + } + }, + "sprite_assets": {}, + "font_assets": {} + })) + .expect("deserialize state with ordered offset"); + assert_eq!(validate_state(&state), Ok(())); + + let mut invalid = state; + invalid.ui_trees[0].root.offset.min[1] = f32::INFINITY; + assert_eq!( + validate_state(&invalid), + Err("UI 节点 offset 必须是有限数值".to_string()) + ); + } + #[test] fn recovers_last_valid_state_when_primary_is_corrupt() { let (directory, asset_id) = fixture();