Skip to Content
ftui-layoutPanesOverview

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), with 0 reserved. 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. PaneRepairAction describes 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 form

The 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