# 3D 编辑器侧边栏信息架构 ## 目标布局 ```text ┌ 场景大纲与资产中心 ┐ ┌──────── 3D Viewport ────────┐ ┌ 右侧工作区 ──┐ │ 场景大纲(常驻) │ │ │ │ 检查器/控制/数据│ │ ├─ Robot │ │ │ │ 上下文属性 │ │ │ ├─ Body │ │ │ │ │ │ │ └─ Joint │ │ │ │ │ │ └─ Maps │ │ │ │ │ │ └─ Object │ │ │ │ │ ├────────────────────┤ │ │ │ │ │ 资产库 | 工程文件 │ │ │ │ │ └────────────────────┘ └──────────────────────────────┘ └──────────────┘ ``` - 场景大纲常驻,资产库与工程文件共享下半区,放置地图时无需在“结构/资产”之间切换。 - 右侧使用“检查器 / 控制台 / 数据录制”三个顶层视图;检查器内部根据统一选择自动路由到 Body、Joint、地图实例或地图对象。 - Python/ONNX 控制器、全局 Actuator/Joint 控制和数据录制不是对象属性,放在右侧独立视图中,不使用模态窗口,因此操作时始终能看到并交互仿真画面。 - 控制与训练等重型组件仅在首次进入对应右侧视图时懒加载。 - 控制台分区默认全部折叠,按“执行器控制 → 关节调试 → 外力交互 → Python 控制 → ONNX 推理 → 强化学习训练”排列,并在标题右侧显示“控制 / 调试 / 交互 / 推理 / 训练”功能标记。 ## 状态与依赖边界 `EditorSelection` 是大纲高亮和检查器路由的唯一选择语义,只保存稳定 ID: ```ts type EditorSelection = | { kind: 'body'; bodyId: number } | { kind: 'joint'; jointId: number; bodyId: number } | { kind: 'map'; mapAssetId: string } | { kind: 'map-object'; mapAssetId: string; objectId: string }; ``` Viewer 的射线命中详情仍保留为临时 `ViewerSelection`,仅用于展示 geom 和世界位置,不再决定检查器类型。 组件职责: - `ProjectSidebar`:只编排常驻 `SceneOutliner`、`MapAssetLibrary` 和 `ProjectTree`。 - `SceneOutliner`:把 simulation 与 map 数据投影成一棵可选择的场景树;不修改业务状态。 - `MapAssetLibrary`:只负责资产发现、点击添加和拖放,不再保存已放置实例树。 - `ModelControlsSidebar`:持有右侧栏壳层、顶层视图切换,并在检查器视图中按 `EditorSelection.kind` 路由。 - `RobotInspector`:承载模型摘要、Body 和 Joint 的领域 UI。 - `PhysicalMapPanel`:承载地图实例参数与地图对象精确属性,不再持有视口工具状态。 - `MapViewportToolbar`:在视口内统一承载 Transform、网格吸附与贴地检测,并与顶部 `ViewerToolDock` 共享交互状态。 - `MapDraftStatusOverlay`:常驻视口底部,统一显示改动计数以及场景级提交/丢弃入口。 - `WorkspaceToolsPanel`:懒加载并在右侧栏内承载控制器、全局仿真控制和数据录制。 所有选择入口遵循同一事件流: ```text 场景树点击 / Viewer 拾取 ↓ App 更新 EditorSelection(稳定 ID) ↓ SceneOutliner 高亮 + Inspector 自动路由 ↓ 领域面板发出 command callback ↓ App / MapEditSession / SimulationSession 执行修改 ``` 该实现只使用 React hooks、受控属性和 Tailwind class,兼容 React 18;当前工程使用 React 19 时无需兼容层。