Status: ✅ All green · 24/24 r214 unit tests · 23/23 r214 Playwright smoke · 0 regressions
Date: 2026-08-30
Branch: feat/id9-r214-editor-polish @ cc216ec (pushed to origin)
Base: feat/id9-r213-vsil-editor @ 3d51f99
Repo: avidtech6/vibecoder-standalone
Wires the 3 selection surfaces (Canvas, Layers, Inspector) through a shared singleton history (with bus-driven sync), adds 12+ keyboard shortcuts, drag-to-zoom/pan in Canvas, drag-scrub + click+drag keyframes in Timeline, and real VSIL scene binding via ctx.inspector.vsil.asset.bind(...). The Editor now behaves like a real creative tool: selections propagate, undo/redo works across components, and presets render real scenes.
useEditorSelection hook in src/host/editor/selection.tsactiveRegionId / activeAssetIdregion:selected, vsil:asset-bound) sync across componentsctx.inspector.region.transformsetCurrentLayers(layers) from LayersPanel embeds the current layer state in every push, so undo/redo can roll back layer statequeueMicrotask(notify) defers subscriber updates to avoid "Cannot update a component while rendering a different component" warningsclearHistory() on Lock → Edit toggle transition (read-only mode resets history)| Key | Action |
|-----|--------|
| Cmd/Ctrl + Z | Undo |
| Cmd/Ctrl + Shift + Z, Cmd/Ctrl + Y | Redo |
| Space | Toggle play/pause (Timeline) |
| V / M | Move tool |
| B | Brush tool |
| T | Text tool |
| S | Shape tool |
| C | Crop tool |
| X | Transform tool |
| + / = | Zoom in |
| - / _ | Zoom out |
| Cmd/Ctrl + 0 | Fit to viewport |
| [ / ] | Layer up / down |
Listener attached to the editor's outer div (not document). Ignores keydowns in input/textarea/select so typing in the Inspector's transform fields is unaffected.
onWheel adjusts zoom in 9 discrete steps (25, 50, 75, 100, 125, 150, 200, 300, 400)onMouseDown starts a pan drag (unless clicking a layer box, which selects instead)onMouseMove updates pan in real-timeonMouseUp ends the dragonPointerDown on the SVG starts either a playhead drag or a keyframe drag (the keyframe's own onPointerDown sets dragKeyframeRef and stops propagation)onPointerMove updates the playhead time OR the dragged keyframe's timeonPointerUp clears both drag refssetPointerCapture is wrapped in try/catch (jsdom doesn't support it)| Suite | Result |
|------|--------|
| r207 unit tests | 24/24 ✅ |
| r208 unit tests | 27/27 ✅ |
| r210 unit tests | 27/27 ✅ |
| r212 unit tests | 27/27 ✅ |
| r213 unit tests | 36/36 ✅ |
| r214 unit tests | 24/24 ✅ |
| r212 Playwright smoke | 44/44 ✅ |
| r214 Playwright smoke | 23/23 ✅ |
| Total | 232/232 ✅ |
| Invariant | Status |
|-----------|--------|
| No iframe | ✅ (0 in r214 source, 0 in DOM after Playwright) |
| No postMessage | ✅ (0 in r214 source) |
| Window-level event bus | ✅ (region:selected, vsil:asset-bound still fire) |
| a788 READ-ONLY invariant | ✅ (Inspector.readOnly bound to VSILEditor.readOnly; defaults to true) |
| R207 mounts functional | ✅ (PreviewHostVSIL/InspectorHost/CompositionGraphHost still in mount-hosts) |
| R208 EditBar functional | ✅ (still mounts for non-timeline tiles) |
| All r206-r213 invariants | ✅ (no regressions in any unit test) |
src/host/editor/selection.ts | 285 (NEW, singleton + hook)
src/host/editor/shortcuts.ts | 145 (NEW, keydown listener)
src/host/editor/__tests__/editor-r214.test.tsx | 540 (NEW, 24 assertions)
scripts/smoke/r214-smoke.spec.cjs | 460 (NEW, 23 assertions)
scripts/smoke/screenshots-r214/ | 5 PNGs (NEW)
src/host/editor/Canvas.tsx | 217 (M, drag-to-zoom + pan + layer boxes)
src/host/editor/Inspector.tsx | 81 (M, applies to selection + Apply button)
src/host/editor/LayersPanel.tsx | 127 (M, bus-driven selection + setCurrentLayers)
src/host/editor/PresetBrowser.tsx | 41 (M, calls asset.bind + setActiveAssetId)
src/host/editor/Timeline.tsx | 102 (M, drag-scrub + keyframe drag)
src/host/editor/VSILEditor.tsx | 116 (M, useShortcuts + Canvas zoom wiring)
Net: +1,810 / -145 lines. 5 new files, 6 modified files.
cd /workspace/vibecoder-standalone-r214
# Unit tests
npx tsx src/host/editor/__tests__/editor-r214.test.tsx # 24/24
npx tsx src/host/editor/__tests__/editor.test.tsx # 36/36 (r213 regression)
# Playwright smoke (Vite dev server on :5298)
npx vite --port 5298 --host 0.0.0.0 &
R214_BASE=http://localhost:5298 node scripts/smoke/r214-smoke.spec.cjs # 23/23
# Source check
grep -l "
1. Operator review of the r214 polish in the dev server (port 5298)
2. Operator decision on merge (r213 + r214 = the full ID9 chrome + Editor + polish)
3. Inspector ASTMode (Phase 4.3 Step 2) — currently just shows transforms; could integrate with VibeChat for AI-powered region manipulation
4. Layer drag-to-reorder in Layers panel (currently uses up/down buttons)
5. Multi-select layers (Shift+click to add to selection)
6. Context menu on layer boxes (right-click for delete/duplicate/etc)
7. Phase 4.2 dev-server, Phase 4.4.4 Step 3, Phase 4.5 VibeScope sync, Phase 4.6 vault code — operator picks still pending
8. a788 InspectorCard cross-repo migration (still pending)
1. Merge r214 to main now, or wait for r215 (multi-select, drag-to-reorder)?
2. Production deploy — npm run build + scp to 185.249.73.178?
3. Inspector → AI integration (Phase 4.3 Step 2) — should we expose the active layer to VibeChat for AI manipulation?
4. Layer drag-to-reorder — is this a high-priority polish, or can it wait for r215?