ui editor 页面优化 #337
Reference in New Issue
Block a user
Delete Branch "feat/ui-editor-v3"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
style: 把各个工具栏做在画布之上
d4796a90abto706563378b706563378bto6dbc4a39cb15c633986dto8a0d5600b3UI Editor review 复核结果
说明:
[x]表示已自动修复并单独提交;[ ]表示保留给用户处理的高风险/产品取舍项。每项均按“当前实现 → 问题 → 建议”说明。此文件是临时复核清单,不提交。apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/merge.rs:306(提交6b8a10641):当前合并测试 helper 构造Node时没有offset;新增字段后测试无法编译。已补NodeOffset::default()。若未来统一提供Node构造器,可再减少测试样板。.../commands/merge.rs:490-493(提交d37064886):当前直接用required_tool_arguments,工具调用缺失和参数 JSON 错误共用parse_arguments。已先解析工具调用并记录parse_tool_call/missing_tool_call,再单独解析参数;建议继续保持这两个阶段分离。.../commands/separation/mod.rs(提交4f4fb0873、365e530b5):当前切分测试 helper 同样漏填offset;已补默认值并修正从公开layout::offset模块导入类型。建议保留显式默认值,避免测试依赖结构体字段顺序。.../components/InputSidebar.tsx:93-94(提交08c3b89b6):当前跨树节点点击只更新节点选择,不切换 active design image;Delete 等操作可能命中旧树。已在真实跨节点选择分支先调用selectDesignImage再选择节点。建议保留同节点通知去重逻辑。.../UiEditorCopyPathButton.tsx:22-23(提交abd95a623):当前复制失败已设置failed状态,却又从void copyPath()抛出异常,造成未处理 Promise rejection。已移除重复抛出;用户可见错误仍由状态提示。.../features/ui-editor/stateTransition.ts:120(提交ce309a3b2):当前 spread 会把类型允许但值为undefined的补丁写回 metadata。已过滤undefined后再 clone/merge,恢复“未提供字段不覆盖”的语义。.../features/ui-editor/stageStatusOverview.ts:91-99(提交bec225436):当前上一项索引使用嵌套三元表达式。已改为显式if/else,行为不变且便于区分字符串游标与树游标。.../features/ui-editor/stateInvariants.ts:5-9(提交f455d3edb):当前missing-image只在 union 中声明,校验器从不发出;实际缺失树图片使用missing-tree-image。已移除死类型,建议以后新增 issue code 时同步发出路径或不加入 union。.../commands/recognition.rs:773-778(提交0988c4907):当前识别流程把缺失工具调用当成参数解析失败。已区分parse_tool_call/missing_tool_call与parse_arguments,并恢复专门的“未返回工具调用”提示。.../commands/ui_design_suggestion.rs:217-219(提交899a591cd):当前语义建议流程同样混淆缺失工具调用和坏 JSON。已拆分工具查找与参数解析,并记录独立诊断阶段。.../components/RecognitionOverview.tsx:47(提交91f534ed6):当前固定窄侧栏使用四列,标签宽度不足会换行拥挤。已改为两列;建议后续若面板宽度可变再加响应式断点。.../components/SeparationOverview.tsx:50(提交2d8b51885):当前同样在窄侧栏使用四列。已改为两列,长标签可读性更好。.../components/Inspector/InspectorSidebar.tsx:844-849(提交f4e9414d6):当前getAnimations()会同时拿到 transition 和多个 pseudo-element animation。已只重启名称以ui-editor-status-attention开头的动画,避免干扰无关过渡。.../components/UiEditorSaveResultModal.tsx:118-129(提交d46cdfb28):当前成功、生成、失败通知分别复制 footer 按钮 markup。已抽取NoticeFooter,统一关闭/重试布局和样式;不改变 retry handler 行为。.../layout/offset.rs:7-8(提交3ce2977e3、绑定同步提交54b9bd435):当前 ts-rs 默认把[f32; 2]导出为Array<number>,而前端 NodeOffset 使用 tuple。已为 min/max 加[f32; 2]覆盖并同步生成绑定,避免类型漂移。.../view/ui-editor/index.tsx:147-152(提交1e7a3f17f):当前返回路径保存失败时确认弹窗仍打开,同时叠加保存结果弹窗。已对afterReturn失败直接沿用确认弹窗中的 save error,不再打开第二个 focus trap。.../useUiEditorPage.ts:1559-1562(提交31c084fce):当前 catch 丢弃底层异常,固定显示“保存失败”。已保留Error.message/字符串异常并写入 save error 和返回结果,便于诊断和重试。.../view/ui-editor/index.tsx:92-93(提交c921ae3b1):当前 overview 渲染是嵌套三元。已改为显式分支,保持三个步骤映射不变。.../operationLifecycle.ts:6-10(提交a6c3db60f):当前 hook 对外暴露setRunning、reset,但现有调用方只使用 running/status/begin/finish/setStatus。已收窄公开返回面并删除无调用的 reset 实现;若未来需要外部重置,应按明确生命周期重新接入。.../useUiEditorPage.ts:1551(提交6d02a0c29):当前generateError只写不读,页面已通过返回结果 notice 展示生成失败。已删除该状态、写入和 projection 字段,避免形成误导性的死状态。.../components/preview/PreviewWorkspace.tsx:124-126(提交1ab7fa999):当前pixels_per_unit为 0/非有限值时直接相除,可产生 Infinity 并污染 bounds/viewport。已在相除前校验有限且大于 0,无效图片按 0 尺寸处理。.../components/preview/PreviewWorkspace.tsx:33(提交0a102ea1c):当前同一模块分两次导入Node和别名UiNode。已合并为单一类型导入并复用Node。.../components/preview/PreviewWorkspace.tsx:132(提交e72637789):当前offset.min是必填数组,Boolean(array)永远为真,过滤无效。已改为检查两个坐标均为有限数。.../components/preview/PreviewWorkspace.tsx:427-428(提交70c65c37d):当前 pointerup 从 React state 快照读偏移,可能落后一帧。已从 drag 起点、释放坐标和当前 viewport scale 直接计算最终偏移再提交。.../components/preview/PreviewWorkspace.tsx:211-212(提交8fdf39f37):当前 effect 读取canvasSizeRef却依赖canvasSize状态,依赖项本身惰性且初始适配由 ref/一次性 guard 控制。已移除无效依赖;没有擅自改变 resize 适配时机,若要修正零尺寸首次 fit 需另做交互验证。.../components/InputSidebar.tsx:93-94(提交337133734):当前移除切图调用后treeId参数变成未使用参数,会触发 ESLint。已先将其改为_treeId,再在第 5 项恢复实际使用。.../components/UiEditorSaveResultModal.tsx:33-39(提交2142027f8):当前 notice 内容使用嵌套三元链。已抽取renderNoticeContent,用早返回映射三种 notice。- 重命名 src-tauri/src/ui_editor/design_doc/{checkpoint,creation,mod,run_workflow,test_support}.rs 与 steps/** 到 ui_editor/agent_tools/ - 更新 main.rs、ui_editor/mod.rs、agent/runtime_tools/ui_design_doc.rs 中的模块路径引用 - 更新 steps/recognize.rs 与 steps/separate/mod.rs 内部对 agent_tools 夹具的引用 - 更新 tests/project.rs 注释中的 creation.rs 与 next_ui_design_path 模块路径 - 更新 agent_tools/test_support.rs 模块注释为 agent_tools 单测共用夹具 - 更新技术方案《UI编辑器代码地图与模块职责》《UI编辑器Agent工具化重写》的模块树与落地顺序路径 - 更新 shared-memory decision-log 中该次重写的测试过滤器为 agent_toolsWIP: Feat/ui editor v3to WIP: ui editor v3 页面优化WIP: ui editor v3 页面优化to WIP: ui editor 页面优化9694faa62] 现状:Node.offset是普通 serde 字段。问题:字段级缺省会漏;但评审说“旧文档因此读不了”只对了一半——真正拒绝整份旧文档的是parse_ui_design_document的 canonical 往返比对(缺offset会导致值 != canonical),字段级#[serde(default)]只救缺字段的子结构(如检查点里的 State 快照)。建议:已加#[serde(default)];若要让缺offset的整份旧文档可读,需放开 canonical 检查或写迁移,属 breaking,留给你。ec9a4fcba] 现状:validate_node只校验 Transform 的 anchor/offset,node.offset完全不校验。问题:NaN/Infinity 的树级偏移能存盘,而预览按offset.min定位,会把整棵树渲染坏。建议:加有限数值校验(已做);min <= max我没加,因为offset.max目前没有任何渲染或后端消费者(前端只读offset.min),硬性排序校验会引入无依据的拒绝。d082c5ab1] 现状:required_tool_arguments是pub(crate)组合封装,recognition.rs直接调两个底层函数。问题:死代码且会触发dead_code告警。建议:删除(已做)。1c7d90a95] 现状:acceptance_evidence_tool_may_advance_project_revision在退役ui.workflow.run时只删未补,而ui-design-doc.run-workflow仍是验收证据工具且会推进项目 revision。问题:validate_acceptance_evidence_revision会以「evidence project revision 不匹配」拒掉真实证据。建 议:把ui-design-doc.run-workflow加入该白名单,并补契约测试(已做)。c844f857e] 现状:删ui.workflow.run的pages[].applicationPath分支时,没给ui-design-doc.from-images的images[].path补校验。问题:交接阶段的绝对路径逃逸早筛丢失,只剩后置的resolve_local_project_path。建议:按images/{index}/path用collect_native_string_field收集(已做)。补充:ledger.rs的 native 函数映射与normalize_runtime_tool_input_paths同样没有ui-design-doc.*,但那是 native 函数名维度,不是本次回归。940d7b579] 现状:用RunUiDesignDocWorkflowOutput.revision(文档级)跟revision_before(项目级 sidecar)比较。问题:两个计数器不同源,项目 revision 一旦超过文档 revision 条件恒假,emit_game_creator_manifest_invalidated永不触发,前端 manifest 一直过期。建议:比较 workflow 前后读到的项目 revision(已做)。project_gates.rs的is_agent_runtime_project_mutation_observation、agent_runtime_observation_advances_project_revision、验证资格分支只认旧的ui.workflow.run,新工具ui-design-doc.from-images|run-workflow会推进项目 revision 却不被当成 mutation。问题:任务可能 不要求验证就判完成、pending revision 少算导致pending_project_revision_drift误报、tool_policy_snapshot与门禁口径不一致。建议:按工具名 +status == "ok"纳入,或在 observation detail 里带revisionAdvanceCount再消费;这会改完成/验证判定语义,需要你定口径。observe_agent_runtime_ui_design_doc_from_images/..._into_js是同步函数,直接在 async 执行器线程上做image::open解码、manifest/文件 IO 与 SHA-256 + 写文件。问题:阻塞 executor,而仓库别处统一用spawn_blocking。建议:改成 async +spawn_blocking包住阻塞核心;属签名与调用链调整。ui.workflow.run的 safe-detail 脱敏分支删除后,三个ui-design-doc.*都没补,agent_runtime_action_receipt_safe_detail_with_owner落到 fallback 返回None;agent_runtime_public_action_input_summary同理。问题:作为验收证据的工具,其 receipt 里assetId/relativePath/backfillErrors未脱敏。建议:照旧分支补assetId/relativePath校验、长度与控制字符处理;留给你是因为要定脱敏字段口径。create_ui_design_doc_from_images文档注释明确“每次调用都新建”。问题:双击或关掉重开会重复登记文档与 manifest 条目。建议:加在途 ref 或先查已关联文档。历史:648ead215有意取消了「原型 → 已有文档」的幂等查找,所 以可能是设计选择,需要你确认。file.take(..)是方法调用自动重借用,不会 move,file.set_len/seek照常可用(我用最小复现验证过),所以“无法编译”的 critical 结论不成立。真问题:该文件以 append 模式打开,set_len只改大小、不移动写指针,半行截断路径下保留的是旧文件头而不是刚读出的尾部,会截错位置。建议:改用OpenOptions::write(true).truncate(false)+ 显式seek后再截断;属写盘语义 breaking,留给你。ac870fd41] 现状:文档登记失败时只删文档文件,prepare_design_images已登记的图片留在 manifest 里无人引用。问题:孤儿 manifest 条目。建议:失败分支调用drop_manifest_assets(root, &[], ®istered_image_ids),并保留 reconciliation-required 文案(已做,含用例)。already_written_back恢复路径里resume_progress → rebuild_target_state已跑过register_cut_image_sprites/apply_separation,主流程又跑一遍。问题:重复 manifest IO,且失败描述会重复追加进backfill_errors。建议:该分支跳过这些步骤;会改变副作用与错误 列表,属行为变更。588768bff] 现状(评审判断错):rejected不只用于输入解析失败,三个观察都把“项目上下文缺失”的错误传进来,而这些错误嵌了宿主绝对路径。问题:摘要原样交给 provider,与注释里“无需脱敏”的说法矛盾。建议:rejected(root, tool, …)统一走redact_agent_runtime_project_paths(已做)。0e15d7604] 现状:texts/ui-design-doc.json里写好的参数描述没人引用,from-images的images没description,one_string_input_schema只产出裸type: string。问题:模型看不到这些文案,目录条目等于死配置。建议:用one_string_input_schema_with_description/prompt_text!把描述接进 schema(已做)。treeSize直接throw,且不校验pixel_size;它由deriveTreeOffset → createTree → synchronizeDesignImageTrees在replaceState里对持久化 State 直接调用。问题:脏文档在加载期抛异常,绕过{ok:false, reason:'invalid'}和不变量文案。建议:返回null交给调用方 ;注意评审给的补丁只改了函数体,null必须向上传播到deriveTreeOffset/createTree,不是一行改动。pixel_size / pixels_per_unit加有限性校验在四处重复(nodeTransformGeometry.ts、stateTransition.ts、useUiEditorState.ts、stateInvariants.ts),且treeSize已经漂移。建议:抽一个共享 helper,和 16 一起做收益最大。0f9e2f173] 现状:UiDesignDocImageReference两个字段都可选,{}或都给要等 Rust 侧才报错。问题:契约没进类型系统。建议:改成{assetId: string; path?: never} | {assetId?: never; path: string}(已做,tsc通过)。LayoutEditor的 JSX 被注释掉,但函数体(约 270 行)仍在文件里,靠eslint-disable-next-line压未用告警,onLayoutChange仍挂在 props 与调用处。问题:大块死代码 + 无消费者 prop。建议:删除注释 JSX、LayoutEditor函数与onLayoutChange。注意评审说SizeFlagOptions/InspectorSelect失去唯一调用点是错的,两者仍在活跃使用。我没动是因为“被注释”看起来像还打算恢复。prepare_design_image先做 manifest 副作用register_local_asset_at,再校验格式/尺寸与查重;批次中后面某张失败时,前面已登记的图片留在 manifest 里,rollback_created_document跑在更晚阶段救不回来。建议:先全量校验再登记,或把已登记 id 提到跨函数状态统一回收;属跨 函数状态改造。a871684bd] 现状:round.at是秒级时间戳,却被写进“第 N 轮”的漂移文案(评审正文和代码块自相矛盾)。建议:改文案说明它是检查点时间戳,不改u64字段类型(已做)。064c26d7c] 现状:右键 pointerdown 把指针捕获在视口上,pointerup 被重定向到捕获元素,openNodeContextMenuAt拿到的event.target是视口,resolveRightClickMenuTarget找不到[data-node-id]。问题:节点右键菜单永不出现。建议:pointerdown 时记下命中的event.target(新增rightPanTargetRef),抬起时用它解析(已做,含反向验证)。134046f80] 现状:onPointerCancel={handleTreePointerUp}把浏览器发起的取消当成成功释放。问题:会提交非预期的树偏移。建议:新增handleTreePointerCancel,清treeDragRef并释放捕获,不提交偏移(已做)。d7f3790fc] 现状:节点拖动守卫只判!treeId,不判!tree || !logicalSize,与缩放路径不一致。问题:树在拖动中被删除会留下悬空捕获手势。建议:补齐与 resize 相同的守卫(已做)。12e8287f9] 现状:step * viewportScale溢出成Infinity时,while (screenSpacing >= MAX)里Infinity / 2仍是Infinity。问题:死循环冻住画布。建议:进入循环前挡掉非有限screenSpacing(已做)。76dfd74de] 现状:onWindowBlur清了右键平移状态,却漏了treeDragRef。问题:失焦时若没有pointercancel,残留引用会被后续 move/up 复用并提交偏移。建议:blur 里一并清treeDragRef与预览偏移(已做,含反向验证)。95fec72ef] 现状:会话投影里仍暴露独立generateCode,全仓无调用方,失败静默返回null。建议:删除该包装(saveAndGenerateCode与stateStore.generateCode保留)。c8852ac2a] 现状:overview在每个分支只赋值一次。建议:改成const+ 三元表达不可变意图(已做)。1c7d90a95] 现状:tool_policy_snapshot把ui-design-doc.run-workflow列为验收证据,但acceptance_evidence_tool_may_advance_project_revision不含它。问题:证据工具与 revision 推进识别不一致。建议:加入该白名单,两处口径统一(已做)。ef4601c55] 现状:节点增删改已按treeForSelectedNode找树,但deleteNode仍默认treeId = activeImageId,而selectNode可选中任意渲染树里的节点。问题:Inspector 删除按钮与 Delete/Backspace 会去删激活图那棵树,删别棵树的节点静默失败。建议:deleteNode默认落到选中节点所在树; 键盘删除改调deleteNode(selectedNodeId)(顺带去掉activeImageId入参),PreviewWorkspace显式传contextMenu.treeId语义不变(已做,含页面级用例)。e2e924341] 现状:set-tree-offset只校验每个元素有限,[]空真通过、[5]也能过。问题:command.min[1]是undefined,offset.max变成 NaN。建议:加command.min.length !== 2判定(已做)。9e49db419] 现状:不变量 seam 直接取image.path.trim()与image.pixel_size.every(...)。问题:旧/残缺文档抛TypeError,被 save 的 catch 吞成通用的「保存失败,请稍后重试。」。建议:取字段前先判类型(typeof image.path === 'string'、Array.isArray(image.pixel_size)) (已做)。src/features/ui-editor/uiDesignResourceBridge.ts 的 UiDesignDocImageReference 改为判别联合:原先两个字段都可选,`{}` 或两个都给要等到 Rust 运行时才被判错