Primitives / foldkit-primitives/state

history

function · packages/primitives/src/state/history.ts:159

function history<const Name extends string, Value>(config: {
  readonly name: Name
  readonly value: Schema.Codec<Value, unknown>
  readonly capacity?: number | undefined
}): Bundle.Bundle<
  Name,
  { readonly initial: Value },
  HistoryModel<Value>,
  | {
      readonly _tag: 'Push'
      readonly value: Value
      readonly group?: string | undefined
    }
  | { readonly _tag: 'Undo' }
  | { readonly _tag: 'Redo' }
  | { readonly _tag: 'GoTo'; readonly step: number }
  | { readonly _tag: 'Clear' },
  never,
  never,
  never,
  void,
  {},
  {}
> & {
  Message: MessageUnion<{
    readonly Push: {
      readonly value: Schema.Codec<Value, unknown, never, never>
      readonly group: Schema.optional<Schema.String>
    }
    readonly Undo: {}
    readonly Redo: {}
    readonly GoTo: { readonly step: Schema.Number }
    readonly Clear: {}
  }>
}

Undo/redo as a bundle factory: the past, present, and future of one value live in the Model, so every step replays. Pushing records, even an unchanged value; skipping duplicates is the application's policy, not the stack's.

A push may name a group. Consecutive pushes of the same group are one step: the present moves and nothing new is recorded, so typing a word undoes as a whole. Grouping needs no clock; the application names what belongs together.

The steps are also pure functions (History.push, undo, redo, goTo, clear), for a parent that records an edit in the same transition that makes it rather than dispatching a Message to a placed bundle.