tally-react the undo message reads from the label, such as Undid Paste 2 cells.
The Mutation object
createMutation({ label, changes?, inserted?, deleted? }) fills in id and at. applyMutation(rows, mutation, schema, direction?) applies one forward or backward, and invertMutation swaps every change and turns inserts into deletes and back.
Editing through the grid
edits.ts
setCells parses every value with the column type, so '$5k' becomes 5000 in a currency column. An empty value becomes null. Values that do not parse and values that do not change are skipped, and a call that changes nothing commits nothing. tally-react uses the verbs Paste, Fill and Clear.
grid.newRow() creates a blank row with a fresh UUID key. Pass newRow to the grid when the key is a function or needs another shape.
Optimistic updates
A commit applies to the grid’s rows at once, then goes to the source’smutate. When the source answers accepted: false or throws, the mutation is reverted and removed from history, and the result carries the errors. A source without mutate keeps edits in the grid only.
Undo and redo
grid.undo() reverts the last mutation in the grid and sends its inverse to the source. grid.redo() applies it again and sends it. When the source rejects either one, the grid steps back.
The row store at grid.rows keeps the history:
A new commit after an undo drops the mutations that were undone.
Live patches
Changes from outside the grid arrive as patches rather than mutations. A patch upserts and deletes rows by key, and it is not part of the undo history. Local mutations made since the last load are replayed on top of the patched rows.patch.ts
grid.rows.get().changed, and the is:changed flag filters to their rows. grid.rows.clearChanged() resets them.