Documentation
¶
Overview ¶
Package tree provides a searchable, expand/collapse hierarchical view inside a bordered pane. It accepts any data shape that satisfies the minimal Node interface (Label + Children) and renders a flat list of visible rows with depth-aware indentation, a "▸/▾" expand glyph, and a cursor.
Use it for file trees, JSON browsers, nested categories — anything where the user wants to drill into a hierarchy. The body is a pkg/pane.Pane (so pgup/pgdn/arrows/mouse-wheel and horizontal scroll work out of the box) plus an optional pkg/filter.Model for the "/-to-search" overlay.
Features:
- Cursor + scroll across the flat visible-row slice
- Space toggles the cursor's expand state, descending into children when expanded; enter selects; E expands every node in the tree, C collapses everything back to root. Arrow keys and hjkl follow the library-wide scroll convention (rule 23) and are reserved for vertical/horizontal scroll — they do not expand/collapse.
- "/" focuses an embedded filter (case-insensitive substring against Label()); matches highlight inline with MatchStyle
- "\" toggles filter mode: when on, only matching nodes (and their ancestors, so the path stays readable) are shown; n/N step through matches
- InitialDepth controls how deep the tree starts pre-expanded
Items are caller-owned via the Node interface — copy from your own data shape without forcing tuilib's structs into your domain model.
Index ¶
- type Keys
- type Model
- func (m Model) Cursor() int
- func (m Model) FilterMode() bool
- func (m Model) Help() []key.Binding
- func (m Model) Init() tea.Cmd
- func (m Model) Loading() bool
- func (m Model) Query() string
- func (m Model) Searching() bool
- func (m Model) Selected() (Node, bool)
- func (m *Model) SetActiveColor(c lipgloss.TerminalColor)
- func (m *Model) SetCurrentLineStyle(s lipgloss.Style)
- func (m *Model) SetCursor(n int)
- func (m *Model) SetDimensions(w, h int)
- func (m *Model) SetFilterMode(b bool)
- func (m *Model) SetFocused(b bool)
- func (m *Model) SetInactiveColor(c lipgloss.TerminalColor)
- func (m *Model) SetLoading(b bool) tea.Cmd
- func (m *Model) SetLoadingLabel(s string)
- func (m *Model) SetMatchStyle(s lipgloss.Style)
- func (m *Model) SetQuery(s string)
- func (m *Model) SetRoot(n Node)
- func (m *Model) SetSpinnerStyle(s lipgloss.Style)
- func (m *Model) SetTitle(s string)
- func (m Model) Update(msg tea.Msg) (Model, tea.Cmd)
- func (m Model) View() string
- type Node
- type Options
- type SelectedChangedMsg
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Keys ¶
type Keys struct {
Up, Down key.Binding
Top, Bottom key.Binding
Toggle, Enter key.Binding
ExpandAll, CollapseAll key.Binding
NextSibling, PrevSibling key.Binding
NextLeaf, PrevLeaf key.Binding
Search, NextMatch, PrevMatch key.Binding
Filter key.Binding
Pane pane.Keys
}
Keys is the tree's keymap. Each binding carries both its dispatch keys (WithKeys) and its help label (WithHelp). Update and Help() read from the same struct, so custom bindings propagate to the hint strip automatically. The embedded pane.Keys covers horizontal scroll.
type Model ¶
type Model struct {
// contains filtered or unexported fields
}
Model is the tree widget. Embed by value; mutate via the setters.
func (Model) FilterMode ¶
FilterMode reports whether non-matching subtrees are currently hidden.
func (Model) Help ¶
Help returns the keys this tree responds to. Each entry comes from m.keys (the same bindings Update dispatches against), so custom keymaps propagate to the hint strip automatically. While the embedded filter is focused, returns the filter's keys.
func (Model) Selected ¶
Selected returns the node under the cursor. ok is false when the visible row set is empty (e.g. filter mode with no matches).
func (*Model) SetActiveColor ¶
func (m *Model) SetActiveColor(c lipgloss.TerminalColor)
SetActiveColor updates the body pane's active border color.
func (*Model) SetCurrentLineStyle ¶
SetCurrentLineStyle updates the row style applied under the cursor.
func (*Model) SetCursor ¶
SetCursor moves the cursor (clamped to the visible range) and scrolls to keep it on screen.
func (*Model) SetDimensions ¶
SetDimensions resizes the tree in place.
func (*Model) SetFilterMode ¶
SetFilterMode turns filter-only rendering on or off.
func (*Model) SetFocused ¶
SetFocused flips the body pane's focus state.
func (*Model) SetInactiveColor ¶
func (m *Model) SetInactiveColor(c lipgloss.TerminalColor)
SetInactiveColor updates the body pane's inactive border color.
func (*Model) SetLoading ¶
SetLoading toggles the loading state. When entering, returns the spinner's initial Tick command — propagate it back from your screen's Update so the spinner animates.
func (*Model) SetLoadingLabel ¶
SetLoadingLabel updates the text rendered next to the spinner while loading.
func (*Model) SetMatchStyle ¶
SetMatchStyle updates the highlight style applied to matched substrings.
func (*Model) SetRoot ¶
SetRoot swaps the underlying tree in place, preserving as much user state as possible so periodic polling doesn't clobber the view under the user. Behavior:
- Expand/collapse state is keyed on label-based paths (see childPath). Any surviving path stays expanded; new nodes appear collapsed (unless a prior generation of the map had them expanded); paths that disappeared are pruned from the expanded map.
- Cursor pins to the same node (by path) when it survives. When the previous node is gone, cursor walks up its ancestors until a surviving path is found; failing that, cursor snaps to 0.
- Nil root clears expand state and cursor.
This is the auto-refresh primitive: fetch new tree, call SetRoot, done.
func (*Model) SetSpinnerStyle ¶
SetSpinnerStyle updates the lipgloss style applied to the spinner glyph.
type Node ¶
Node is the minimal contract a caller's data must satisfy. Label is the row's display text; Children returns the immediate descendants (nil or empty for leaves). Tree never mutates the returned slice.
type Options ¶
type Options struct {
Width, Height int
// Title sits on the pane's top-left border slot. Defaults to "tree".
Title string
// Root is the top-level node. Its own label is rendered as the first
// row; pass a synthetic root if your data has multiple top-level items.
Root Node
// Searchable embeds a filter.Model above the body pane (three rows). If
// false, "/" is ignored and the full height is used for the tree.
Searchable bool
// InitialDepth pre-expands every node whose depth is < InitialDepth.
// 0 (default) shows only the root collapsed, 1 expands the root,
// 2 expands the root and its direct children, etc.
InitialDepth int
// MatchStyle is the lipgloss style applied to matched substrings while
// a query is active. Pass via theme.Tree() for a sensible default.
MatchStyle lipgloss.Style
// CurrentLineStyle is applied to the entire row holding the cursor,
// padded out to the pane's inner width. theme.Tree() seeds a subtle
// background.
CurrentLineStyle lipgloss.Style
// Pane pass-throughs. See pkg/pane.Options for defaults.
ActiveColor lipgloss.TerminalColor
InactiveColor lipgloss.TerminalColor
ActiveBorder lipgloss.Border
InactiveBorder lipgloss.Border
SlotBrackets pane.SlotBracketStyle
HScrollbar bool
// SpinnerStyle is applied to the spinner glyph rendered while the
// tree is in its loading state (see SetLoading). Pass via theme.Tree()
// for a sensible default.
SpinnerStyle lipgloss.Style
// LoadingLabel is rendered next to the spinner while loading.
LoadingLabel string
// Filter configures the embedded filter. Ignored when Searchable=false.
Filter filter.Options
// Keys overrides the default keymap. Fields left zero fall back to
// DefaultKeys(). theme.Tree() pre-populates this; mutate Keys.X
// in-place to override individual actions.
Keys Keys
}
Options configures a new tree. Zero-value fields fall back to defaults.
type SelectedChangedMsg ¶ added in v0.17.0
type SelectedChangedMsg struct {
// Path is the label-based path from root to the focused node. Nil
// when Empty is true. Uses the same path scheme as SetRoot's
// expand-state preservation — sibling labels with a duplicate-
// suffix ("Pod", "Pod:2") are included in the array.
Path []string
// Label is the focused node's own label — Path[len(Path)-1] for
// convenience. Empty when Empty is true.
Label string
// Depth is 0 for the root, 1 for its children, etc. Zero when Empty.
Depth int
// Empty is true when no node is focused. Path / Label / Depth are
// zero-valued.
Empty bool
}
SelectedChangedMsg is emitted by the tree when the cursor lands on a different node than the last emit — after cursor movement, SetRoot swaps that change the focused node, expand/collapse ops that shift the cursor, or on the initial view. Parents subscribe to it to drive "detail on hover" patterns: refetch a parameterized detail source keyed on the focused node's path. Dedup is on the path (label-based, same identity scheme SetRoot uses to preserve expand state); an identical-structure SetRoot doesn't re-emit. Empty=true fires only as a transition (had focus → no focus) — an initially-empty tree never emits.