Single source of truth for all screen rectangles.
Layout.compute/1 takes editor state and produces a struct of named
rectangles for every UI element. The renderer never computes its own
coordinates; it receives pre-computed rectangles and draws into them.
Rectangle format
All rectangles are {row, col, width, height} tuples matching the
existing WindowTree.rect() type. The origin is the top-left corner
of the allocated area.
Regions vs Overlays
Regions are non-overlapping areas that tile the screen: file tree, editor area, agent panel, modeline, minibuffer. They participate in the non-overlap invariant.
Overlays float over regions: picker, which-key popup, completion menu. They have positioning rects but don't participate in the tiling constraint.
Summary
Types
A horizontal separator between split panes: {row, col, width, filename}.
A screen rectangle: {row, col, width, height}.
Complete layout for one frame.
Layout for a single editor window, with sub-rects for each chrome element.
Functions
Returns the content height (visible rows) for the active window.
Returns the content width for the active window.
Returns the window layout for the active window, or nil.
Splits a window layout's content rect to carve out a sidebar.
Computes the complete layout for the current frame.
Computes window layouts and horizontal separator positions simultaneously.
Returns the cached layout from state, or computes it fresh.
Invalidates the cached layout. Call when layout-affecting state changes (viewport resize, file tree toggle, agent panel toggle, window split/close).
Computes the layout and stores it in state for reuse within the same frame.
Types
@type horizontal_separator() :: {non_neg_integer(), non_neg_integer(), pos_integer(), String.t()}
A horizontal separator between split panes: {row, col, width, filename}.
@type rect() :: {non_neg_integer(), non_neg_integer(), pos_integer(), pos_integer()}
A screen rectangle: {row, col, width, height}.
@type t() :: %MingaEditor.Layout{ agent_panel: rect() | nil, editor_area: rect(), file_tree: rect() | nil, horizontal_separators: [horizontal_separator()], minibuffer: rect(), status_bar: rect() | nil, tab_bar: rect() | nil, terminal: rect(), window_layouts: %{required(MingaEditor.Window.id()) => window_layout()} }
Complete layout for one frame.
@type window_layout() :: %{ total: rect(), content: rect(), modeline: {non_neg_integer(), non_neg_integer(), pos_integer(), 0}, sidebar: rect() | nil }
Layout for a single editor window, with sub-rects for each chrome element.
total— the full window rect (from WindowTree.layout)content— the text area within the window (full window; no per-window modeline)modeline— always zero-height; kept for backward compatibility. The global status bar atLayout.t().status_barreplaces per-window modelines.sidebar— optional info panel (agent chat dashboard)
Functions
@spec active_content_height(t(), MingaEditor.State.t()) :: pos_integer()
Returns the content height (visible rows) for the active window.
@spec active_content_width(t(), MingaEditor.State.t()) :: pos_integer()
Returns the content width for the active window.
This is useful for computing gutter width and wrap maps before rendering.
@spec active_window_layout(t(), MingaEditor.State.t()) :: window_layout() | nil
Returns the window layout for the active window, or nil.
@spec add_sidebar(window_layout()) :: window_layout()
Splits a window layout's content rect to carve out a sidebar.
Returns the layout with content narrowed and sidebar set to
the right-hand info panel rect. Only applied when the content width
exceeds @sidebar_threshold.
@spec compute(MingaEditor.State.t() | map()) :: t()
Computes the complete layout for the current frame.
Delegates to the shell's compute_layout callback, which dispatches
to the appropriate TUI or GUI layout module.
@spec compute_window_layouts_with_separators( MingaEditor.WindowTree.t(), rect(), map() ) :: {%{required(MingaEditor.Window.id()) => window_layout()}, [horizontal_separator()]}
Computes window layouts and horizontal separator positions simultaneously.
Horizontal splits steal 1 row from the top window for a separator bar. The separator shows the lower window's buffer filename.
Returns {window_layouts_map, horizontal_separators_list}.
@spec get(MingaEditor.State.t() | map()) :: t()
Returns the cached layout from state, or computes it fresh.
Prefer this over compute/1 when you have a state that might already
have a cached layout. The cache is invalidated on resize, file tree
toggle, and agent panel toggle.
@spec invalidate(MingaEditor.State.t()) :: MingaEditor.State.t()
Invalidates the cached layout. Call when layout-affecting state changes (viewport resize, file tree toggle, agent panel toggle, window split/close).
@spec put(MingaEditor.State.t() | map()) :: map()
Computes the layout and stores it in state for reuse within the same frame.
Call this once at the start of a render cycle or event handler, then
read state.layout downstream.