r215 VSIL Editor Multi-Select + Drag-to-Reorder — Implementation Report

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

TL;DR

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).

What's in r215

1. Multi-select (Shift+click)

- { replace: true } (default) — replace the set with the given ids

- { toggle: true } — toggle the given ids in the set (Shift+click)

- 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"

2. Drag-to-reorder

3. Snapshot model extension

4. LayersPanel history-applied listener

r214.1 invariants preserved

| 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) |

Test results

| 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 ✅ |

r215 unit test sections (25 assertions)

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)

r215 Playwright smoke (18 assertions, 14 steps)

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)

Files (8893a63)

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.

How to verify

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 "

What's next

Immediate

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)

Short-term

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?

Standing (FVS main, parked)

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)

Open questions for the operator

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)?