# M6: move, copy, swap Branch: `m6-move-copy-swap` → `main` ## What changed **Pads can be moved, copied and swapped**, across banks: - **Dragging** a filled pad onto an empty pad moves it; onto a filled pad swaps the two; holding **Alt** copies (onto a filled pad: copy and replace). The target cell shows a dashed outline for move/swap and a solid one for copy. - **Dragging over a bank letter** switches the grid to that bank, so a pad can be dropped on another bank's pads. - **Move to pad / Copy to pad** buttons open a picker (bank and pad, with each pad's sample shown): the keyboard route to the same three actions. Move onto a filled pad becomes Swap, as with dragging. - Dragging pads inside the window doesn't interfere with dropping WAV files from Explorer: the Wails runtime only handles drags that carry files. **Recorded-notes warning** (build plan M6, format doc section 3). Notes point at a slot, not a sample, so they stay where they are. Before any move, swap or copy that changes what a pad with notes will play, a dialog lists each such pad and what its notes will play afterwards. Pads without notes get no dialog. Copying onto a filled pad also asks first. Pads with notes show a `notes` badge in the grid and a note in the settings panel. **Load (brought forward from M8).** M6's done-when needs a project with notes, and only the device makes those, so the app needs Load now. Load opens a `.xpj` (the name comes from the file, samples from its `_[ProjectData]` folder) and reports firmware or version warnings. New and Load ask before discarding unsaved changes. M8 keeps missing-file badges, the window-close prompt and the other guard rails. **Go service**: `MovePad`, `CopyPad`, `SwapPads`, `Load` (dialog) and `LoadPath`. The `Dialogs` interface gains `OpenProject`. **Fix after the first laptop test:** the picker's Move/Copy button did nothing (step 7). The click handler closed the dialog before reading the dialog's choice. Closing clears `ui.picker`, and Svelte 5 props are live, so the following `picker.from` threw inside an async handler, which nothing reported. The handler now reads the choice first. `transfer` also shows any unexpected error in the banner instead of failing silently. ## Testing On the server: `go vet` and `go test ./...` pass. New service tests cover move/copy/swap across banks (tuning carried, sample shared, move onto a filled pad, out-of-range slot), and Load: cancelled dialog, DemoFile (name, 6 samples, notes on A01–A04 only, preview URL, 2 sequences), and a bad file keeping the current project. bajdev's laptop run (2026-11-11): steps 1–6 and 8 passed; step 7 failed and is fixed above. Re-run step 7. The frontend is untested here (no Node). **On the laptop** (create `C:\temp\m6` first): 1. `npm run check` in `frontend`: 0 errors, 0 warnings. 2. `wails3 dev`, **Load** `testdata\device\DemoFile.xpj`. A01–A04 show `notes`; B01 and B02 don't. Pads play when clicked. 3. **Move across banks, with a warning:** drag A01 over the **C** bank letter (the grid switches to bank C) and drop on C01. A dialog says A01 has notes that will play nothing. Cancel: nothing changes. Do it again and confirm: C01 has the kick, A01 is empty. 4. **No warning without notes:** **Alt**-drag B01 to bank D, pad D05. It copies with no dialog; B01 is unchanged. 5. **Swap:** drag A02 onto B02. A dialog lists A02's notes; confirm. A02 and B02 have swapped. 6. Rename to `UiMoves`, **Export to folder** `C:\temp\m6`. Then: ``` go run ./cmd/mpckit-cli -load testdata\device\DemoFile.xpj -name CliMoves -out C:\temp\m6 -move "A1:C1" -copy "B1:D5" -swap "A2:B2" go run ./tools/xpjdiff C:\temp\m6\UiMoves.xpj C:\temp\m6\CliMoves.xpj ``` prints `identical`. **(M6 done-when: all three across banks.)** 7. **Buttons:** select C01, **Copy to pad** → bank A, pad A03 (shows the hat): the button reads "Copy and replace". The warning lists A03's notes. **Move to pad** onto an empty pad moves it. 8. **Unsaved changes:** after any edit, **New** asks to discard; Cancel keeps the project.