Standard undo/redo systems

Farzad Yousefzadeh

Software engineer @Stately.ai

https://stately.ai

Two types of applications

Data driven

Interaction driven

Real world examples

Why do we need undo/redo?

Forgiving to mistakes

Allow exploration

JS Implementation

Memento pattern

Storage limits

Memory limits

Serialization concerns

UndoRedoSystem {
  private undoStack: State[];
  private redoStack: State[];
  public state: State;
}

Command pattern

Stores change descriptor (less storage)

Applies update to build prev/next state (less memory)

interface State {
  count: number;
}
interface Transaction {
  patches: Patch[];
  inversePatches: Patch[];
}
/**
Why not a function as the descriptor? Serialization and persistence!
* Patch {
*  op: 'replace' | 'remove' | 'add'
*  path: string[];
*  value: any
* }
*/

UndoRedoSystem {
  private undoStack: Transaction[];
  private redoStack: Transaction[];
  public state: State;
}

Distributed updates and batching (1)

Updates can happen in multiple places

Updates can happen in different depths

To the user, this is one operation so when they undo, the shouldn't get partial inverse patches

function reparentShape() {
  // multiple separate updates at once
  updateShape();
  updateSiblingShapes();
  updateParent();
}
// Update in multiple depths:
function updateParent() {
  // do some stuff
  updateSiblingShapes();
}
function reparentShape() {
  updateShape();
  updateParent();
}

Distributed updates and batching (2)

Sequential external updates with no time limits in between (append to the last update)

To the user, this is one operation so when they undo, the shouldn't get partial inverse patches

function renameShape(newName: string) {}

renameShape('new1')
renameShape('new2')
renameShape('newLastTimeIPromise')

Transactions to the rescue

Usability perspective

  • Keyboard integration
  • Accessible menus
  • Handling screen readers and voice over
  • Confirmation for non-visual clients

Further challenges

  • Multiple Undo/redo systems in sync or being adaptive
  • Bidirectional undo/redo systems like live collaborations
  • Size and capacity