Skip to content

Reliev 🩹

Reliev is a small store library on top of observ that adds undo/redo to reactive state. You subclass Store, mark the methods that change state with @mutation, and every call records a history entry that can be undone and redone. Patches are recorded with patchdiff, so a mutation costs what it changes, not the size of your state.

from reliev import Store, mutation

class TodoStore(Store):
    @mutation(context=lambda self, item: f"Add {item}")
    def add_item(self, item):
        self.state["items"].append(item)

store = TodoStore({"items": []})
store.add_item("apple")

assert store.state["items"] == ["apple"]
assert store.undo_context == "Add apple"  # e.g. menu item "Undo Add apple"

store.undo()
assert store.state["items"] == []

store.redo()
assert store.state["items"] == ["apple"]

Because the state is an observ reactive proxy, everything composes with observ's watch and computed: watchers see exactly the keys a mutation (or an undo) touched, and the @computed decorator exposes derived state as store properties.

Where to start

  • observ: the reactivity system reliev's stores are built on.
  • patchdiff: records the bidirectional patches that make undo/redo possible.