Save useful sounds on the patch shelf and bring them into later musical ideas.
Pin a patch in one song, drop it into another. Design spec: Keep and reuse patches. Workbench-only — the shelf is an authoring aid; nothing about it reaches the language, the IR, or the engine.
1. Using it#
| gesture | where |
|---|---|
| pin / unpin | ◇ / ◆ in a bench decl-cell head bar (patch cells only); More… → Remove from saved patches in the browser also unpins |
| browse | Sounds → Patches, in the resizable sound browser |
| insert | select a patch, then Add patch, or use the shelf section of the bench + palette |
| jump to it in this song | Show in song (when the open song already declares it) |
| clipboard | Copy — the block and its captured dependencies |
| inspect | Source and dependencies expands the captured source |
The browser's search box matches on patch name and origin song.
2. What a pin captures#
The patch's own block plus every local top-level binding it needs, transitively — the macro it reads, the helper that macro reads, the patch it extends with with. Tracks and songs are never captured (following a play would pull in the whole file). Caps: 16 dependencies, 24 kB per entry, 200 entries (oldest dropped).
Dependencies are found by scanning identifiers in code position: string literals and -- comments are skipped, ${…} interpolation holes inside a pattern literal are not (they really do reference bindings).
3. What an insert does#
Dependencies land first, then the patch. Against the target song:
- a dependency whose name is free is inserted as-is;
- a dependency whose name is taken by identical text is reused, not duplicated — a shared helper stays one binding;
- a dependency whose name is taken by different text is renamed (
warm→warm2) and every reference in the inserted block follows; - the patch itself is always inserted, renamed on any collision (
pluck→pluck2) — clickingaddmeans add.
A renamed macro carries its macro "key" string literal along, so two knobs never share a key. Renames never touch pattern atoms: !"kick*4" survives a kick binding being renamed.
The assembled result is compiled before it lands. If it doesn't, nothing is applied and the toast says so — the same contract as the bench's dup / delete / reorder ops.
4. Identity#
An entry is keyed by patch name within its origin song, so lush from marquee and lush from nightswim are two entries and each pins independently. The origin song is the file stem; shells with no file path (the browser) fall back to the buffer's own song declaration name, then untitled.
Re-pinning the same patch refreshes the captured text in place — that is how you update a shelf entry after improving the patch.
5. Where it is stored#
| shell | store |
|---|---|
volta-studio (desktop) | ~/.volta/shelf.json (atomic write) |
| browser workbench | localStorage["volta-studio-shelf"] |
| anything else | in memory, for the session |
One document per user, not per song — unlike takes, which live beside the song in .studio-takes/<song>/. A malformed store is ignored rather than erroring, and the UI will not write a shelf back until it has successfully read one (or given up trying), so an empty shelf can never overwrite a real one.
The bridge seam is EngineBridge::load_shelf() -> Option<String> / save_shelf(&str); the document is {"version": 1, "entries": [...]} with each entry {name, source, deps: [{name, kind, source}], song}.
6. Code map#
| file | role |
|---|---|
ui/src/shelf.rs | entry model, capture, code-position lexer, insert assembly, JSON, panel |
ui/src/bench.rs | head-bar pin chip, palette shelf section (both reported via BenchEvents) |
ui/src/app/shelf.rs | pin toggle, capture, verified insert, the panel host |
ui/src/app/buffers.rs (song_stem) | song identity (file stem, then song name, then untitled) |
ui/src/app/shell.rs | store load (with retries) and debounced save |
app-native/src/shelfstore.rs | ~/.volta/shelf.json (shared by volta-studio + volta-shot) |
ui-wasm/src/web.rs, runtime/public/studio-host.js | the localStorage half |
ui/tests/shelf.rs | the behavior suite (pin → store → insert into another song) |