layout

package
v0.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 23, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Overview

Package layout implements the layout engine for grut's TUI. It manages a binary split tree where each leaf holds a panel name, and computes absolute sizes from terminal dimensions.

Index

Constants

View Source
const (

	// PanelPadH is the horizontal padding (in characters) added inside each
	// panel's left and right edges to keep content away from the border.
	PanelPadH = 1
)
View Source
const SingleTabMode = true

SingleTabMode controls whether multi-tab features are active. v1: true (single-tab, tab bar hidden, tab keybindings disabled). Set to false to re-enable multi-tab in v2.

Variables

This section is empty.

Functions

func FindSplitAtBorder

func FindSplitAtBorder(root Node, x, y int, area Rect, hitZone int) (*SplitNode, Direction, Rect)

FindSplitAtBorder walks the layout tree and returns the SplitNode whose border is at or near the given (x, y) coordinates within the specified hit zone tolerance. Coordinates are relative to the panel area (not terminal). Inner (deeper) splits take precedence over outer ones. Returns the split, its direction, and the total area the split occupies.

func FirstPanelOf

func FirstPanelOf(node Node) string

FirstPanelOf returns the name of the first (top-left-most) panel in the subtree rooted at node.

func Presets

func Presets() map[string]Preset

Presets returns all built-in layout presets keyed by name.

func RegisterDefaults

func RegisterDefaults(r *Registry, cfg *config.Config, gc git.GitClient, th *theme.Theme)

RegisterDefaults registers the built-in panels. Panels with real implementations use their concrete constructors; those still under development use placeholders. The cfg parameter provides the already-loaded configuration, avoiding redundant disk reads on every panel creation. The gc parameter provides the git client for git-aware panels; if nil, git panels are not registered. The th parameter provides the theme for styled panels; if nil, panels use fallback colors.

func RenderTabBar

func RenderTabBar(tabs []Tab, activeIdx, width int) string

RenderTabBar renders a one-line tab bar showing open tabs with their preset number, plus hints for unopened presets. The active tab is shown in uppercase. The result is padded or truncated to fit the given width.

v1: hidden — returns "" so the tab bar takes no vertical space. The full rendering logic is preserved below for v2 multi-tab.

func Resolve

func Resolve(node Node, area Rect) map[string]Rect

Resolve walks the layout tree and computes the absolute Rect for each leaf panel given the available area. Returns a map of panel name → Rect.

func SplitRect

func SplitRect(area Rect, dir Direction, ratio float64) (Rect, Rect)

SplitRect divides a Rect into two sub-rects based on direction and ratio. One column (horizontal) or row (vertical) is reserved between the two sub-rects for the separator line that visually divides panels.

Types

type Direction

type Direction int

Direction represents the orientation of a split.

const (
	// Horizontal splits the space into left and right children.
	Horizontal Direction = iota
	// Vertical splits the space into top and bottom children.
	Vertical
)

func (Direction) String

func (d Direction) String() string

String returns the human-readable direction name.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine manages the layout state: the active layout tree, instantiated panels, focus tracking, and zoom state.

func NewEngine

func NewEngine(reg *Registry, preset Preset) (*Engine, error)

NewEngine creates a layout engine with the given registry and initial preset.

func (*Engine) AddTab

func (e *Engine) AddTab(preset Preset) (tea.Cmd, error)

--------------------------------------------------------------------------- Tab operations --------------------------------------------------------------------------- AddTab creates a new tab from a preset, instantiating any panels not already present. Returns a tea.Cmd from initializing new panels.

func (*Engine) BorderSize

func (e *Engine) BorderSize() int

BorderSize returns the width of one side of the outer border.

func (*Engine) CloseActiveTab

func (e *Engine) CloseActiveTab() error

CloseActiveTab closes the currently active tab. Returns an error if it is the last remaining tab.

func (*Engine) CloseFocusedPanel

func (e *Engine) CloseFocusedPanel() error

CloseFocusedPanel removes the focused panel from the current tab's tree. The sibling takes the parent split's place. Returns an error if it is the last panel in the tab.

func (*Engine) CurrentPreviewPosition

func (e *Engine) CurrentPreviewPosition() PreviewPosition

CurrentPreviewPosition returns the current preview position of the active tab's layout. It checks whether "preview" is a direct child of the root split and infers position from the split direction and child order. If preview is not found at the root level, returns PreviewRight as default.

func (*Engine) FocusByName

func (e *Engine) FocusByName(name string) bool

FocusByName focuses the panel with the given name. Returns false if not found.

func (*Engine) FocusNext

func (e *Engine) FocusNext()

FocusNext cycles focus to the next panel in order.

func (*Engine) FocusPrev

func (e *Engine) FocusPrev()

FocusPrev cycles focus to the previous panel in order.

func (*Engine) FocusedName

func (e *Engine) FocusedName() string

FocusedName returns the name of the currently focused panel.

func (*Engine) FocusedPanel

func (e *Engine) FocusedPanel() panels.Panel

FocusedPanel returns the currently focused panel, or nil if none.

func (*Engine) Height

func (e *Engine) Height() int

Height returns the current total height.

func (*Engine) Init

func (e *Engine) Init(ctx context.Context) tea.Cmd

Init initializes all panels. Must be called after NewEngine.

func (*Engine) InnerArea

func (e *Engine) InnerArea() Rect

InnerArea returns the inner content area dimensions (after subtracting the single outer border) used to resolve panel rects.

func (*Engine) IsDragging

func (e *Engine) IsDragging() bool

IsDragging returns whether a mouse drag resize is in progress.

func (*Engine) IsZoomed

func (e *Engine) IsZoomed() bool

IsZoomed returns whether the focused panel is in zoom mode.

func (*Engine) MoveTabLeft

func (e *Engine) MoveTabLeft()

MoveTabLeft swaps the active tab with its left neighbor.

func (*Engine) MoveTabRight

func (e *Engine) MoveTabRight()

MoveTabRight swaps the active tab with its right neighbor.

func (*Engine) NextTab

func (e *Engine) NextTab()

NextTab cycles to the next tab.

func (*Engine) PanelOrder

func (e *Engine) PanelOrder() []string

PanelOrder returns the ordered list of panel names.

func (*Engine) PanelRects

func (e *Engine) PanelRects() map[string]Rect

PanelRects returns the resolved rectangles for each panel in the current layout, excluding the status bar and tab bar area.

func (*Engine) Panels

func (e *Engine) Panels() map[string]panels.Panel

Panels returns the map of panel name to panel instance.

func (*Engine) PrevTab

func (e *Engine) PrevTab()

PrevTab cycles to the previous tab.

func (*Engine) RenameActiveTab

func (e *Engine) RenameActiveTab(name string) error

RenameActiveTab renames the currently active tab.

func (*Engine) ResizeGrow

func (e *Engine) ResizeGrow()

ResizeGrow increases the space allocated to the focused panel by adjusting the nearest split ratio.

func (*Engine) ResizeShrink

func (e *Engine) ResizeShrink()

ResizeShrink decreases the space allocated to the focused panel by adjusting the nearest split ratio.

func (*Engine) RotatePreviewPosition

func (e *Engine) RotatePreviewPosition()

RotatePreviewPosition cycles the preview panel position in the active tab through right → bottom → left → top → right.

func (*Engine) SetPreviewPosition

func (e *Engine) SetPreviewPosition(pos PreviewPosition)

SetPreviewPosition sets the preview panel position across all tabs whose tree is a simple two-leaf split of "filetree" and "preview". This ensures that switching tabs preserves the chosen position. If the active tab's position already matches, this is a no-op.

func (*Engine) SetSize

func (e *Engine) SetSize(width, height int)

SetSize updates the terminal dimensions and recalculates panel sizes.

func (*Engine) SplitFocusedHorizontal

func (e *Engine) SplitFocusedHorizontal(newPanelType string) (tea.Cmd, error)

--------------------------------------------------------------------------- Split / panel operations --------------------------------------------------------------------------- SplitFocusedHorizontal splits the focused panel with a horizontal divider, placing a new panel of the given type below.

func (*Engine) SplitFocusedVertical

func (e *Engine) SplitFocusedVertical(newPanelType string) (tea.Cmd, error)

SplitFocusedVertical splits the focused panel with a vertical divider, placing a new panel of the given type to the right.

func (*Engine) StatusBarHeight

func (e *Engine) StatusBarHeight() int

StatusBarHeight returns the height reserved for the status bar.

func (*Engine) SwitchTab

func (e *Engine) SwitchTab(idx int) error

SwitchTab activates the tab at the given index.

func (*Engine) TabBarHeight

func (e *Engine) TabBarHeight() int

TabBarHeight returns the exported tab bar height for rendering.

func (*Engine) TabManager

func (e *Engine) TabManager() *TabManager

TabManager returns the underlying tab manager.

func (*Engine) ToggleZoom

func (e *Engine) ToggleZoom()

ToggleZoom toggles the zoom state of the focused panel.

func (*Engine) Update

func (e *Engine) Update(msg tea.Msg) tea.Cmd

Update routes the given message to the appropriate panel(s). Key and mouse events go only to the focused panel (we don't want unfocused panels reacting to keyboard input or mouse clicks). All other messages (async results, cross-panel notifications, etc.) are broadcast to ALL panels in the active tab, following the standard Bubble Tea v2 composite model pattern where all sub-models see all non-input messages.

func (*Engine) Width

func (e *Engine) Width() int

Width returns the current total width.

type LeafNode

type LeafNode struct {
	Panel string
}

LeafNode is a terminal node in the layout tree, holding a panel name.

func (*LeafNode) Clone

func (l *LeafNode) Clone() Node

Clone implements Node.

func (*LeafNode) PanelNames

func (l *LeafNode) PanelNames() []string

PanelNames implements Node.

type Node

type Node interface {
	// PanelNames returns all panel names contained in this subtree.
	PanelNames() []string
	// Clone returns a deep copy of the node.
	Clone() Node
	// contains filtered or unexported methods
}

Node is the interface for layout tree nodes. A node is either a SplitNode (with two children) or a LeafNode (holding a panel name).

func RemoveLeaf

func RemoveLeaf(root Node, targetPanel string) (Node, bool)

RemoveLeaf removes the leaf named targetPanel from the tree, collapsing its parent SplitNode so the sibling takes the parent's place. Returns the new root and whether the panel was found.

newTree, ok := RemoveLeaf(tab.Tree, "terminal")

func SplitLeaf

func SplitLeaf(root Node, targetPanel string, dir Direction, newPanel string) Node

SplitLeaf replaces the leaf named targetPanel with a new SplitNode containing the original leaf as the first child and a new leaf (newPanel) as the second child, using the given direction and a 0.5 ratio. Returns the (possibly new) root node. The caller must assign the result back to their tree reference:

tab.Tree = SplitLeaf(tab.Tree, "preview", Vertical, "terminal")

type PanelFactory

type PanelFactory func() panels.Panel

PanelFactory is a constructor function for creating panels by name.

type Preset

type Preset struct {
	Name   string
	Tree   Node
	Panels []string
}

Preset is a named layout configuration consisting of a layout tree and the list of panel names it requires.

func AgentPreset

func AgentPreset() Preset

AgentPreset returns the "agent" layout: filetree (20%) | terminal (40%) | agents (40%)

func ExplorerPreset

func ExplorerPreset() Preset

ExplorerPreset returns the default "explorer" layout: Left column (30%): filetree (35%) on top, gitinfo (25%), github (20%), commits (20%) on bottom. Right column (70%): preview (100%).

func FullPreset

func FullPreset() Preset

FullPreset returns the "full" layout: filetree (15%) | gitstatus (25%) | preview (35%) | terminal (25%)

func GitPreset

func GitPreset() Preset

GitPreset returns the "git" layout: filetree (30%) | preview (70%) The filetree already shows git status indicators on each file, so a separate gitstatus panel is not needed.

func ReviewPreset

func ReviewPreset() Preset

ReviewPreset returns the "review" layout: filetree (20%) | review (50%) | context (30%)

type PreviewPosition

type PreviewPosition int

--------------------------------------------------------------------------- Preview position cycling --------------------------------------------------------------------------- PreviewPosition represents where the preview panel sits relative to the filetree.

const (
	PreviewRight  PreviewPosition = iota // default: filetree | preview
	PreviewBottom                        // filetree on top, preview on bottom
	PreviewLeft                          // preview | filetree
	PreviewTop                           // preview on top, filetree on bottom
)

func PreviewPositionFromString

func PreviewPositionFromString(s string) PreviewPosition

PreviewPositionFromString converts a string to a PreviewPosition. Unrecognised values default to PreviewRight.

func (PreviewPosition) String

func (p PreviewPosition) String() string

String returns the string representation of a PreviewPosition.

type Rect

type Rect struct {
	X      int
	Y      int
	Width  int
	Height int
}

Rect represents a rectangular area with position and size.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry maps panel names to their factory functions, enabling dynamic panel creation by the layout engine.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates a new empty panel registry.

func (*Registry) Create

func (r *Registry) Create(name string) (panels.Panel, error)

Create instantiates a panel by name using its registered factory. Returns an error if the name is not registered.

func (*Registry) Has

func (r *Registry) Has(name string) bool

Has returns true if a factory is registered for the given name.

func (*Registry) Names

func (r *Registry) Names() []string

Names returns all registered panel names in no particular order.

func (*Registry) Register

func (r *Registry) Register(name string, factory PanelFactory)

Register adds a panel factory under the given name. If a factory with that name already exists, it is replaced.

type SplitNode

type SplitNode struct {
	First     Node // left or top child
	Second    Node // right or bottom child
	Direction Direction
	Ratio     float64
}

SplitNode divides space between two children according to a direction and ratio. Ratio is the fraction of space allocated to the first (left/top) child, in the range (0, 1).

func FindSplitContaining

func FindSplitContaining(root Node, panelName string) (*SplitNode, string)

FindSplitContaining finds the innermost SplitNode whose First or Second child directly contains the given panel name. Returns the split node and which child ("first" or "second") contains it, or nil if not found. For nested trees, this returns the deepest split that directly parents the leaf — the one whose ratio directly affects the panel's size.

func (*SplitNode) Clone

func (s *SplitNode) Clone() Node

Clone implements Node.

func (*SplitNode) PanelNames

func (s *SplitNode) PanelNames() []string

PanelNames implements Node.

type Tab

type Tab struct {
	Tree Node
	Name string
}

Tab represents a single tab with its own layout tree.

type TabManager

type TabManager struct {
	// contains filtered or unexported fields
}

TabManager manages a set of tabs, each with its own layout tree. Only one tab is active at a time.

func NewTabManager

func NewTabManager(name string, tree Node) *TabManager

NewTabManager creates a tab manager with a single initial tab.

func (*TabManager) ActiveIndex

func (tm *TabManager) ActiveIndex() int

ActiveIndex returns the index of the active tab.

func (*TabManager) ActiveTab

func (tm *TabManager) ActiveTab() *Tab

ActiveTab returns the currently active tab.

func (*TabManager) Add

func (tm *TabManager) Add(name string, tree Node)

Add creates a new tab with the given name and layout tree, appended after the current active tab. The new tab becomes active.

func (*TabManager) Close

func (tm *TabManager) Close(idx int) error

Close removes the tab at the given index. Returns an error if it's the last tab or the index is out of range.

func (*TabManager) Count

func (tm *TabManager) Count() int

Count returns the number of tabs.

func (*TabManager) MoveLeft

func (tm *TabManager) MoveLeft()

MoveLeft swaps the active tab with its left neighbor. No-op if already at the leftmost position.

func (*TabManager) MoveRight

func (tm *TabManager) MoveRight()

MoveRight swaps the active tab with its right neighbor. No-op if already at the rightmost position.

func (*TabManager) NextTab

func (tm *TabManager) NextTab()

NextTab cycles to the next tab, wrapping around.

func (*TabManager) PrevTab

func (tm *TabManager) PrevTab()

PrevTab cycles to the previous tab, wrapping around.

func (*TabManager) Rename

func (tm *TabManager) Rename(idx int, name string) error

Rename changes the name of the tab at the given index.

func (*TabManager) Select

func (tm *TabManager) Select(idx int) error

Select sets the active tab to the given index. Returns an error if the index is out of range.

func (*TabManager) Tabs

func (tm *TabManager) Tabs() []Tab

Tabs returns a copy of all tabs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL