How an edit flows
Every user edit is one Tauri command and one undo step. Taking “move a region” as an example:
sequenceDiagram
participant UI as React component
participant API as api.ts act()
participant C as commands/regions.rs move_region
participant S as Session
participant E as Engine (audio thread)
UI->>API: act("move_region", { id, start, track })
API->>C: invoke (args camelCase in JS, snake_case in Rust)
C->>S: edit(): checkpoint("Move Region")
C->>S: move_region(id, start, track)
S->>S: update Project, regions_dsp(track)
S->>E: Cmd::Regions(track, new RegionDsp list)
C->>S: frozen_guard()
C-->>API: Snapshot (project.for_ui(), undo label, ...)
API->>API: setState({ snap }) and toast if refused
E-->>S: Garbage::Regions(old) (dropped in idle)
The pieces:
-
act(cmd, args)insrc/api.tsinvokes a command that returns aSnapshot(or aMade, which wraps one) and stores it in the global store. Errors become a toast.actMadereturns the ids an edit created, so the caller can select them.callis the plain form for commands that return other data. -
Argument names: JavaScript passes camelCase keys (
fadeIn), which Tauri maps to the Rust parameterfade_in. Fields inside a struct argument (aTrackPatch, aRegion) keep the serde names, which are snake_case. -
Command wrappers in
src-tauri/src/commands.rs:Wrapper Undo step Returns snap(f)no Snapshottry_snap(f)no Result<Snapshot>edit(label, key, plugins, f)yes, then frozen_guardSnapshotwithrefusedset when the edit touched a frozen tracktry_edit(label, key, plugins, f)yes, then frozen_guardResult<Snapshot>edit_made(label, plugins, f)yes, then frozen_guardResult<Made>(snapshot plus new ids)keycoalesces a burst of edits into one step (see Undo);pluginsmakes the checkpoint capture plug-in states, for edits that remove plug-ins. -
Not undoable on purpose: mute, solo, track on/off, record enable and input monitoring (performance switches, as in Logic;
update_trackgives them no label), transport, cycle and Autopunch range changes, track selection, the metronome and count-in switches. -
The
Snapshot(commands.rs) carries the project without plug-in state blobs (Project::for_ui), the selected track, project path, saved/dirty flags, audio status, MIDI inputs, open plug-in editors, the MIDI target name, undo/redo labels and the PDC report.Session::idlealso emits a fresh snapshot as theprojectevent when editors open or close, automation was written, or a plug-in’s latency changed. -
Ticks are separate and much lighter (see The frontend).