Status: ✅ All green · 25/25 r215 unit tests · 18/18 r215 Playwright smoke · 0 regressions in r207-r214.1
Date: 2026-08-30
Branch: feat/id9-r214-editor-polish @ 8893a63 (pushed to origin, on top of bc3c338)
Base: feat/id9-r214-editor-polish @ bc3c338 (r214.1)
Repo: avidtech6/vibecoder-standalone
r215 adds two refinement features to the r214.1 VSIL Editor:
1. Multi-select for LayersPanel — Shift+click toggles the row in the selection set; plain click replaces the set with the single row. Selection propagates to Canvas (multi-highlight) and Inspector (first selected).
2. Drag-to-reorder for LayersPanel — Mouse drag to reorder layers vertically, OR use the up/down buttons. Both push a snapshot to the singleton history. Undo/redo restores the order.
Both features integrate with the r214.1 singleton history so undo/redo works across all 6 surfaces (activeRegionId + activeAssetId + layers + zoom + tool + selectedRegionIds).
currentSelected: RegionId[] to the singleton. SelectionSnapshot gained selectedRegionIds: RegionId[]. The hook exposes selectedRegionIds (the canonical list) and setSelectedRegionIds(ids, opts?) with opts: - { replace: true } (default) — replace the set with the given ids
- { toggle: true } — toggle the given ids in the set (Shift+click)
event.shiftKey: - Plain click → setSelectedRegionIds([id], { replace: true })
- Shift+click → setSelectedRegionIds([id], { toggle: true })
- Each row's data-active reflects whether the id is in the selected set
- Footer shows "N selected"
selectedRegionIds. Multi-selected boxes all show data-active=1 with the highlight color.onPointerDown starts a drag (only on the row body, not on action buttons). onPointerMove tracks the drop target via document.elementFromPoint. onPointerUp swaps the dragged row with the drop target and pushes a snapshot.onClick={(e) => e.stopPropagation()} AND onPointerDown={(e) => e.stopPropagation()} so they don't trigger the row drag.SelectionSnapshot gains selectedRegionIds: RegionId[].[] (empty array).applySnapshot restores the full set in one commit (synchronous via fireHistoryApplied).pushSnap includes selectedRegionIds in the sig so identical snaps dedupe.prev and vice versa (no additions/removals), use the snap's order. Otherwise fall back to updating fields and preserving order.| Invariant | Status |
|-----------|--------|
| Synchronous history-applied event | ✅ |
| Snapshot includes zoom + tool + layers + selection (now + selectedRegionIds) | ✅ |
| Editor refocus on mouseDown | ✅ |
| Controlled zoom propagation to Canvas | ✅ |
| Label has pointer-events: none | ✅ (r214.1) |
| No iframe in r215 source | ✅ (0 hits) |
| No postMessage in r215 source | ✅ (0 hits) |
| Static React imports (Vite dual-React trap avoided) | ✅ |
| All r207-r214.1 invariants | ✅ (208 prior unit tests pass) |
| 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 ✅ |
| r214.1 unit tests | 18/18 ✅ |
| r215 unit tests (NEW) | 25/25 ✅ |
| r212 Playwright smoke | 44/44 ✅ |
| r213 Playwright smoke | 31/31 ✅ |
| r214 Playwright smoke | 23/23 ✅ |
| r214.1 Playwright smoke | 21/21 ✅ |
| r215 Playwright smoke (NEW) | 18/18 ✅ |
| Total | 345/345 ✅ |
1. Multi-select: plain click replaces the set (3 assertions)
2. Multi-select: Shift+click adds the row to the set (4 assertions)
3. Multi-select: Shift+click on already-selected removes (3 assertions)
4. Multi-select propagates to Canvas (1 assertion)
5. Drag-to-reorder swaps layers via pointer events (1 assertion)
6. Drag-to-reorder direct pointer events do not crash (1 assertion)
7. Undo restores previous order via singleton (4 assertions, including redo)
8. Snapshot includes selectedRegionIds + layers (1 assertion)
9. Label has pointer-events: none (1 assertion, r214.1 invariant)
10. Source-level: 0 iframes, 0 postMessage in r215 source (2 assertions)
11. Hook: setSelectedRegionIds with replace + toggle (3 assertions)
12. Inspector shows first selected layer name + id (1 assertion)
1. Page loads + Editor mounts via SideChrome → Edit (1)
2. data-r215-wired on LayersPanel (1)
3. Plain click on row-bg → only bg active (3)
4. Shift+click on row-scene → [bg, scene] active (2)
5. Footer shows "2 selected" (1)
6. Canvas: layer-scene box shows data-active=1 (1)
7. Canvas: layer-bg box shows data-active=1 (1)
8. Shift+click on row-scene again → toggle off (2)
9. Footer shows "1 selected" (1)
10. moveUp on row-scene via ↑ button → order [scene, bg, fx, ui] (1)
11. Cmd+Z undo → order restored to [bg, scene, fx, ui] (1)
12. Redo via Cmd+Shift+Z → order back (1)
13. Drag-to-reorder via pointer events (1)
14. 0 iframes in DOM (1)
src/host/editor/selection.ts | +120 (M, multi-select API)
src/host/editor/LayersPanel.tsx | +180 (M, multi-select + drag)
src/host/editor/Canvas.tsx | +5 (M, multi-highlight)
src/host/editor/Inspector.tsx | +20 (M, first-selected name)
src/host/editor/__tests__/editor-r215.test.tsx | +467 (NEW, 25 assertions)
scripts/smoke/r215-smoke.spec.cjs | +360 (NEW, 18 assertions)
scripts/smoke/screenshots-r215/ | 6 PNGs (NEW)
Net: +1091 lines (most are tests). 3 new files, 4 modified files.
cd /workspace/vibecoder-standalone-r214
# Unit tests
npx tsx src/host/editor/__tests__/editor-r215.test.tsx # 25/25
npx tsx src/host/editor/__tests__/editor-r214-1.test.tsx # 18/18
npx tsx src/host/editor/__tests__/editor-r214.test.tsx # 24/24
npx tsx src/host/editor/__tests__/editor.test.tsx # 36/36
npx tsx src/host/chrome/__tests__/SideChrome.test.tsx # 27/27
# All 208 unit tests
# Playwright smoke (port 5301 = r215 vite)
R215_BASE=http://localhost:5301 node scripts/smoke/r215-smoke.spec.cjs # 18/18
R2141_BASE=http://localhost:5301 node scripts/smoke/r214-1-smoke.spec.cjs # 21/21
R214_BASE=http://localhost:5301 node scripts/smoke/r214-smoke.spec.cjs # 23/23
R213_BASE=http://localhost:5301 node scripts/smoke/r213-smoke.spec.cjs # 31/31
R212_BASE=http://localhost:5302 node scripts/smoke/r212-smoke.spec.cjs # 44/44
# Source check
grep -l "
1. Operator review of the r215 multi-select + drag in the dev server (port 5301)
2. Operator decision on merge (r213 + r214 + r214.1 + r215 = the full ID9 chrome + Editor + polish + stability + multi-select + drag)
3. Multi-transform apply — Inspector's "Apply" should affect all selected layers (currently applies to the first)
4. Context menu on layer boxes (right-click for delete/duplicate/etc)
5. Production deploy — npm run build + scp to 185.249.73.178?
6. 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
7. a788 InspectorCard cross-repo migration (still pending)
8. Inspector → AI integration (Phase 4.3 Step 2)
1. Merge r215 (8893a63) to main now, or wait for multi-transform apply (r216)?
2. Production deploy — npm run build + scp to 185.249.73.178?
3. Context menu on layer boxes — high priority, or wait?
4. Inspector → AI integration (Phase 4.3 Step 2)?