Skip to main content

Layout historyEnterprise

Dockview can keep a bounded undo/redo stack of layout mutations, giving you Ctrl+Z for your layout. Closing a panel by accident, dragging a group to the wrong split, floating something, maximizing or a fat-fingered resize can all be reverted with api.undo() and re-applied with api.redo().

The feature is off by default and records nothing until you opt in.

Enabling

Turn history on by setting the layoutHistory option with { enabled: true }. The option is honoured live, so you can toggle it at runtime via updateOptions.

clearOnFromJSON
Clear the stacks when the whole layout is replaced via fromJSON / clear. Default true.
clearOnFromJSON?: boolean
coalesceMs
Debounce window (ms) for coalescing a continuous resize drag into one undo entry. Default 400.
coalesceMs?: number
depth
Max undo depth (bounded ring). Default 25.
depth?: number
enabled
Record mutations. Default false (module is registered but inert).
enabled?: boolean
recordResize
Record sash-resize as undoable steps, coalescing a continuous drag into a single entry. Default true.
recordResize?: boolean
undoableProgrammaticMutations
Also record mutations originating from DockviewApi calls (the app's own programmatic changes). Default false; only user gestures.
undoableProgrammaticMutations?: boolean

What gets recorded

Each undoable step is a full snapshot of the layout captured before and after the mutation, so an undo restores the whole layout, including a closed panel's params and title.

The following structural mutations each record one undo step:

  • add / remove: adding and closing panels or groups
  • move: dragging or moving a panel or group to a new location
  • float: floating a group
  • popout: opening a group in a popout window
  • maximize: maximizing / restoring a group
  • tab-group: tab-group changes (such as recolouring)

Resize (dragging a sash) has no discrete mutation boundary, so it is caught separately and a continuous drag is coalesced into a single entry. It is recorded by default; set recordResize: false to ignore resizes, and tune the debounce window with coalesceMs (default 400ms).

A panel's state is only restorable on undo if it is serialized into the panel's params. State held imperatively outside params (for example a chart's zoom kept in a closure) is not captured by the snapshot; this is the same constraint that fromJSON already imposes.

History depth

The stack is a bounded ring. depth (default 25) caps how many undo steps are retained; pushing past the limit drops the oldest entry.

User vs programmatic mutations

By default only user gestures (drag-dock, tab close button, sash resize) enter the stack. A programmatic DockviewApi call such as api.addPanel(...) is treated as the app's own state management and is not something the end user expects Ctrl+Z to revert. Set undoableProgrammaticMutations: true to record those too.

Clearing on a full restore

Loading a whole new layout via fromJSON (or calling api.clear()) clears the history by default, so undo can't resurrect a layout from a different session. Set clearOnFromJSON: false to keep the stacks across a full restore.

Driving undo / redo

Dockview binds no keyboard shortcuts itself; wiring Ctrl+Z / Ctrl+Shift+Z (or toolbar buttons) to api.undo() / api.redo() is the host app's call. Use canUndo / canRedo to drive the disabled state of your controls, and listen to onDidChangeHistory to keep them in sync as the stacks change.

canRedo
Whether redo would do something.
canRedo: boolean
canUndo
Whether undo would do something. Reactive via onDidChangeHistory.
canUndo: boolean
onDidChangeHistory
Fires whenever the undo/redo stacks change.
onDidChangeHistory: Event<LayoutHistoryChangeEvent>
clearHistory
Drop both undo and redo stacks (e.g. on document switch).
clearHistory(): void
redo
Re-apply the next layout mutation undone via undo.
redo(): void
undo
Undo the previous recorded layout mutation. No-op when there is nothing to undo, when layoutHistory.enabled is not set, or when the LayoutHistory module is absent.
undo(): void

onDidChangeHistory fires whenever the undo/redo stacks change (on every push, undo, redo and clear) and carries the reactive state:

canRedo
readonly canRedo: boolean
canUndo
readonly canUndo: boolean
lastEntry
readonly lastEntry?: {
kind: LayoutHistoryKind,
origin: DockviewOrigin
}
redoCount
readonly redoCount: number
undoCount
readonly undoCount: number

Cross-window support

Undo and redo restore the whole layout, including floating groups and popout windows. A mutation made inside a popout window is still a mutation of the same dock and records a single step on the shared stack.

Floating groups restore synchronously, but popout windows re-open asynchronously. If an undo or redo re-opens a closed popout, await api.popoutRestorationPromise to know the window is ready.

Browsers only allow window.open from within a user gesture. An undo re-opening a popout must originate from a trusted user event (a keyboard shortcut or a button click); otherwise the popout falls back to an inline group.

See also