From 2dd87bc05e961f61e783cce11b1e9fd6dc92a006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Tue, 18 Aug 2026 16:33:11 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=B4=E6=98=8E=20UI=20State=20=E6=81=A2?= =?UTF-8?q?=E5=A4=8D=E5=89=AF=E6=9C=AC=E7=9A=84=E6=9C=80=E4=BD=B3=E5=8A=AA?= =?UTF-8?q?=E5=8A=9B=E8=AF=AD=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补充持久化代码注释,区分可解析候选与严格恢复校验 同步技术方案文档中的 previous 恢复边界和并发行为 --- .../src-tauri/src/ui_editor/persistence.rs | 5 +++-- .../【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) 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 81951dbb3..5757fe02f 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 @@ -300,8 +300,9 @@ fn write_ui_design_document( ) -> Result<(), String> { let primary_path = resolve_local_project_path(root, relative_path)?; let backup_path = agent_runtime_json_sidecar_backup_path(&primary_path); - // Only a valid primary becomes a recovery copy. In particular, recovery - // must not replace the last known-good copy with a corrupt primary. + // Best-effort recovery keeps the last parseable, canonical envelope as a + // candidate. Semantic identity, revision and State validation is deferred + // to read_valid_ui_design_document_path before a recovery candidate is installed. if let Ok(previous) = read_ui_design_document_path(&primary_path) { write_ui_design_raw_file( &backup_path, diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 2ea7e06c3..e90aa6ef8 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -1123,7 +1123,7 @@ game-project/ - UI 编辑器复用现有 manifest `kind: "UI"`、`mediaType: "application/json"` 资源,不增加平行 asset kind。资源文件固定为严格 `game-creator-ui-design-state.v1` JSON envelope:`projectId`、`assetId`、每资源 `revision` 和 Rust 唯一源 `State`;旧空对象、未知字段、身份错配、超限、无效内部引用和不安全相对路径均失败关闭。内部引用校验同时覆盖 `Image.target_graphic -> sprite_assets` 与 `Text.font -> font_assets`,可选引用非空时必须命中同一 State 内已登记资源。 - Tauri 专用 load/save command 只接受项目路径、期望项目 ID、manifest asset ID 和(保存时)资源 revision;Rust 按 manifest 解析受控本地路径并在项目写锁内做 CAS。相同 `State` 返回 unchanged 且不推进 project revision;不同内容安装并回读一致后才推进 revision,后续推进失败返回 `reconciliation-required`,不伪装为完整保存。 -- UI State 原子安装保留最近一个有效 `.previous` 恢复副本。主文件损坏时仅在恢复副本通过同一严格校验后恢复;恢复安装与保存共用项目写锁,并在持锁后重新读取主文件,已有并发保存的有效新版本时直接返回而不安装旧副本。任一候选均不可信则停在加载错误,前端禁编辑和保存。新建 UI 资源先登记并安装合法 envelope,任一步失败补偿 manifest/文件,避免把空 JSON 留给资源卡。 +- UI State 原子安装保留最近一个可解析、canonical 的 `.previous` 恢复候选,作为最佳努力恢复来源;写入主文件前不把完整 State 语义校验重复执行一遍。主文件损坏时,恢复候选仍必须通过同一严格 schema、project/asset identity、revision、引用和 State 校验后才能安装;恢复安装与保存共用项目写锁,并在持锁后重新读取主文件,已有并发保存的有效新版本时直接返回而不安装旧副本。任一候选均不可信则停在加载错误,前端禁编辑和保存。新建 UI 资源先登记并安装合法 envelope,任一步失败补偿 manifest/文件,避免把空 JSON 留给资源卡。 - 图片路径只需是安全项目相对路径,不要求外部图片仍存在或已登记为 manifest asset;缺失媒体只导致 preview 占位。`imageOrder`、当前选择、缩放、面板开关和 preview URL 不写入 State,加载后由 State 派生。保存冻结提交快照,保存期间的新编辑继续保持 dirty;AI state lock 和加载期间禁保存。 ## 2026-08-18 UI Editor 新建节点组件状态