feat(runtime): add M1 scene persistence, undo/redo and hotkeys

- load .forge.ron through bevy_forge_core into the live ECS (3D components)
- Ctrl+S syncs and saves the document; custom components are preserved
- transform undo/redo for inspector sliders and gizmo drags (Ctrl+Z/Y)
- F focus, Delete subtree, Ctrl+D duplicate; camera subtrees are guarded
- 66 runtime tests; workspace 412 passed; garden demo docs/screenshots
This commit is contained in:
Emil Shanaty
2026-10-27 23:03:33 +03:00
parent 59212c952f
commit 3eea9c020d
14 changed files with 2369 additions and 16 deletions
+106
View File
@@ -0,0 +1,106 @@
# Bevy Forge v2 — M1 Core
Status: **M1 Core complete and independently verified (`pass_with_risks`)**. M0 is committed at
`59212c9`; M1 turns the M0 spike into a scene-editing slice that reads and writes the existing
`bevy_forge_core` `.forge.ron` format.
![Bevy Forge M1 Core — garden scene](m1-core-1440x900.png)
_1000×640 variant: [m1-core-1000x640.png](m1-core-1000x640.png)._
## What M1 adds
- **Real scene load**: `FORGE_PROJECT` + `FORGE_SCENE` load a `.forge.ron` scene through
`bevy_forge_core` into the live ECS. The garden demo is the reference scene.
- **Save**: Ctrl+S syncs edited transforms/names/visibility back into the scene document and writes
a `.forge.ron` file (`FORGE_SAVE_PATH`, default `scenes/edited.forge.ron`).
- **Transform undo/redo**: Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y for inspector-slider and gizmo-drag
edits.
- **Hotkeys**: Ctrl+S save, F focus camera, Delete delete the selected subtree, Ctrl+D duplicate
the selected subtree.
- Focused tests: **66 runtime tests** (scene 6, shortcuts 6, history 8, inspector 13, gizmo 10,
plus the M0 suite), all green.
- Independent verification: **V11 pass_with_risks** (`.swarm/m1/reviews/V11.md`), including a
post-review delta after the camera guard. Runtime **66 tests**, workspace **412 passed, 2 ignored**.
- Interactive checks done: load 9/7, slider edit → Ctrl+Z → Ctrl+Shift+Z, Ctrl+S with a valid saved
RON (garden source untouched), Ctrl+D duplicate, Delete, F focus, camera delete/duplicate refusal,
no panic.
## Run
```bash
cd bevy-forge
FORGE_PROJECT=examples/demo-project \
FORGE_SCENE=scenes/garden.forge.ron \
cargo run -p bevy_forge_runtime --example editor_live
```
| Variable | Default | Meaning |
|---|---|---|
| `FORGE_PROJECT` | `examples/demo-project` | project root with a `Cargo.toml` containing bevy |
| `FORGE_SCENE` | `scenes/garden.forge.ron` | scene to load, relative to the project root |
| `FORGE_SAVE_PATH` | `scenes/edited.forge.ron` | Ctrl+S output, relative to the project root |
The scene plugin loads when at least one of `FORGE_PROJECT`/`FORGE_SCENE` is set; the command above
sets both. The garden scene has 9 entities / 7 roots (Camera, Sun, Ground, Shrub, Flower Stem, Flower Bloom,
Orbit Crystal, Lantern Post, Lantern Glow). The custom `forge_demo::Orbit` component is skipped in
the live ECS with a warning; all 3D meshes, lights, camera, names, transforms and visibility load.
## Controls
| Input | Action |
|---|---|
| F1 / **Editor (F1)** | Toggle the editor UI |
| Click hierarchy row | Select entity |
| Drag inspector Translation X/Y/Z | Move selected entity |
| Drag gizmo axis | Move selected entity |
| Right/middle drag, wheel | Orbit / pan / zoom camera |
| **Ctrl+S** | Save `.forge.ron` |
| **Ctrl+Z** | Undo transform edit |
| **Ctrl+Shift+Z / Ctrl+Y** | Redo transform edit |
| **F** | Focus camera on selected entity |
| **Delete** | Delete selected subtree |
| **Ctrl+D** | Duplicate selected subtree |
Delete/duplicate are structural edits and are **not undoable in M1**; undo/redo is Transform-only.
## Architecture
```
crates/bevy_forge_runtime/
├── src/scene.rs .forge.ron load, ECS spawn, save/sync, delete/duplicate helpers
├── src/shortcuts.rs Ctrl+S / F / Delete / Ctrl+D
├── src/history.rs transform command stack + Ctrl+Z/Y
├── src/inspector.rs M0 reflection inspector + history capture
├── src/gizmo.rs M0 camera/gizmo + history capture
├── src/hierarchy.rs M0 live tree
└── examples/editor_live.rs
```
The runtime now depends on `bevy_forge_core` (Bevy-free model/persistence). Core `Transform` uses
XYZ Euler degrees; the bridge converts to/from Bevy quaternions with
`bevy_forge_core::math::{quat_from_euler_deg, quat_to_euler_deg}`.
## Limits (M1)
- 3D only in the live bridge; Sprite/Text2d/Camera2d components are skipped (M2).
- Custom components are skipped in ECS but preserved in the document on save.
- Delete/duplicate are not undoable; multi-select/docking/terminal/play mode are out of scope.
- Deleting or duplicating a `Camera3d` subtree is refused with a warning in M1: the editor camera is
adopted from the scene, and a second active camera would break the viewport.
- V11 residual: a synthetic portal drag can be shorter than one Bevy frame, so the live gizmo drag
→ Ctrl+Z path and the inspector click/snap finalizer remain unit/helper-tested rather than
scripted end-to-end. Slider-drag undo is interactively verified.
- The garden `Camera3d` is adopted by the editor camera; do not add a second active camera.
- Saving must be explicit (Ctrl+S); there is no autosave loop yet.
- Hover styling and screenshot determinism: park the pointer outside the window for byte-stable
screenshots (M0 caveat).
- A synthetic portal drag can be shorter than one Bevy frame, so the live gizmo-drag → Ctrl+Z path
is covered by transform helper/unit tests and the unchanged M0 drag code, not by a scripted
mouse hold; the slider undo path is interactively verified.
## Verification
M0 acceptance is in [`m0-spike.md`](m0-spike.md). M1 was independently verified by V11:
`.swarm/m1/reviews/V11.md` (initial and post-fix delta). The exact commands are in
`.swarm/m1/plan.md`; the final report with actual spend is in the local M1 coordination dir.