Pane System Overview
The pane workspace system is the largest single module in FrankenTUI —
roughly 9,000 lines in crates/ftui-layout/src/pane.rs. It is the
substrate for any IDE-style or tmux-style UI where the user interactively
splits, resizes, moves, and closes panes.
This page is a map. Every box links to its own page for the detail.
Why a whole subsystem
A naive implementation looks easy: store a binary tree of splits, let pointer drags move the dividers. In practice, the requirements stack up:
- Undo and redo the entire layout history.
- Serialize the tree to disk and load it back, byte-identical.
- Replay a user’s input trace against a canonical reference and emit diff artifacts for conformance testing.
- Terminal and web parity — the same gesture behavior on a TTY and in a browser.
- Momentum when a drag releases (inertial throw) with snap-to-grid.
- Validation of malformed trees with repair suggestions instead of panics.
- Magnetic docking previews while dragging a pane onto another.
- Intelligence modes (Focus / Compare / Monitor / Compact) that change resize heuristics.
Each of those requirements earns its own module inside pane.rs.
The five-layer mental model
┌─────────────────────────────────────────────────────────────┐
│ Layer 5: Host adapters │
│ PaneTerminalAdapter │ PanePointerCaptureAdapter (web) │
│ (both live in ftui-runtime) │
├─────────────────────────────────────────────────────────────┤
│ Layer 4: Interaction machines │
│ PaneDragResizeMachine │ PaneInertialThrow │
│ PanePressureSnapProfile │ intelligence modes │
├─────────────────────────────────────────────────────────────┤
│ Layer 3: Semantic input │
│ PaneSemanticInputEvent { PointerDown, PointerMove, │
│ PointerUp, WheelNudge, KeyboardResize, Cancel, Blur } │
├─────────────────────────────────────────────────────────────┤
│ Layer 2: Operations + timeline │
│ PaneOperation { SplitLeaf, CloseNode, MoveSubtree, │
│ SwapNodes, SetSplitRatio, NormalizeRatios } │
│ PaneInteractionTimeline (undo / redo / replay) │
├─────────────────────────────────────────────────────────────┤
│ Layer 1: The tree itself │
│ PaneTree, PaneTreeSnapshot, PaneLeaf, PaneSplit, │
│ PaneConstraints, invariant validation + repair │
└─────────────────────────────────────────────────────────────┘Each layer depends only on the layer below it. You can use Layer 1 and
Layer 2 without touching any input machinery — which is exactly what
happens inside the program-simulator test harness.
The concept ordering
Read the pages in this order if you’re new:
1. The tree — pane-tree
PaneTree, HSplit / VSplit / Leaf, PaneTreeSnapshot, and
invariant validation. The raw data model.
2. Mutations and history — operations-and-timeline
Every change to the tree is a PaneOperation. Every applied operation
lands in a PaneInteractionTimeline that supports undo, redo, checkpoints,
and deterministic replay.
3. Semantic input — semantic-input
PaneSemanticInputEvent is the host-agnostic input vocabulary. Terminal
and web backends both emit it.
4. Drag/resize machine — drag-resize-machine
The three-state lifecycle (Idle → Armed → Dragging) that converts pointer events into resize operations. Includes debouncing and hysteresis.
5. Inertial throw + pressure snap — inertial-throw
Momentum physics after a drag releases. Snap profile tuned by gesture speed and direction consistency.
6. Intelligence modes — intelligence-modes
Focus / Compare / Monitor / Compact. Preset heuristics that bias
the layout planner.
7. Magnetic docking — magnetic-docking
PaneDockZone, PaneDockPreview, ghost rectangles, and how the magnetic
field radius is tuned by wheel.
8. Terminal/web parity — terminal-web-parity
PaneTerminalAdapter (in ftui-runtime) and the browser pointer-capture
path. Same semantic events, same machine, same operations.
Design principles (they all hold everywhere)
- Stable IDs.
PaneId(u64), with0reserved. IDs survive round-tripping through snapshots so replay maps cleanly. - Canonical snapshots.
PaneTreeSnapshot::canonicalize()produces a deterministic serialization. The same tree, the same bytes. - Extension bags.
BTreeMap<String, String>on every record. New fields land here without bumping the schema version. - Strict validation up front.
invariant_report()enumerates duplicate IDs, orphans, cycles, bad ratios — before you ever apply an operation. - Repairable errors where possible.
PaneRepairActiondescribes what a safe automatic repair would do; the caller chooses whether to run it. - State hashing.
state_hash()(FNV-1a) lets you detect any change cheaply — used by operation outcomes, replay diffs, and telemetry.
A 30-second tour of the types
PaneId(u64) // stable node handle, 0 reserved
PaneSplitRatio // reduced form (gcd'd)
PaneConstraints // min/max/collapsible + margin/padding
PaneLeaf // { surface_key: String, extensions }
PaneSplit // { axis, ratio, first, second }
PaneNodeKind = Leaf | Split
PaneNodeRecord // { id, parent?, constraints, kind, ext }
PaneTreeSnapshot // serializable form
PaneTree // in-memory working formThe serialized form (PaneTreeSnapshot) and the working form (PaneTree)
are deliberately distinct. Snapshots are flat (Vec<PaneNodeRecord>) and
cheap to hash; the runtime tree indexes them in a BTreeMap for O(log n)
operations.
Where to start reading code
If you want to open one file and learn the shape:
- pane.rs — 9,297 lines
The file layout inside pane.rs is section-commented; searching for
// ─── banners walks you through the major sections in the same order
as this page.
Where to go next
HSplit / VSplit / Leaf, snapshots, and invariant validation.
Pane treePaneOperation and replay-safe journaling.
The three-state lifecycle and its debouncing.
Drag/resize machineHost-agnostic event vocabulary.
Semantic inputThe lower-level gesture layer that feeds PaneSemanticInputEvent.