tui

package
v0.19.0 Latest Latest
Warning

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

Go to latest
Published: Jul 2, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FilterScope added in v0.18.0

func FilterScope(allTicks []tick.Tick, scope Scope, owner string) []tick.Tick

FilterScope filters allTicks to the set a content view should render for the given scope (§4–§5). Smart views are cross-cutting queries; a tree node scopes to that node's subtree (the node plus its descendants).

func PinColorProfile added in v0.18.0

func PinColorProfile(p termenv.Profile)

PinColorProfile fixes the lipgloss/termenv color profile process-wide. It replaces the old `COLORTERM=truecolor` init() hack in model.go with an explicit, test-pinnable seam (§11 determinism pins): production pins TrueColor so terminals that misreport (e.g. TERM=screen under tmux) still get full color; tests pin a fixed profile so ANSI output does not vary by environment.

tui.PinColorProfile(termenv.TrueColor) // production
tui.PinColorProfile(termenv.Ascii)     // golden tests (no ANSI noise)

func RenderRoadmap added in v0.13.0

func RenderRoadmap(allTicks []tick.Tick, width int) string

RenderRoadmap renders the roadmap view for the left pane. It takes the full tick slice (to call ComputeRoadmap) and the available width for the pane. The function is pure (no model state) so it is trivially unit-testable.

Visual layout per epic line:

<glyph> <id>  [closed/total] <title> [gate-badge] [← blocked by: id1 id2] [← after: id1 id2]

Each wave is preceded by a "Wave N" header (1-indexed).

func RenderRoadmapWithSelection added in v0.16.0

func RenderRoadmapWithSelection(allTicks []tick.Tick, width int, selectedID string) (string, int)

RenderRoadmapWithSelection renders the roadmap like RenderRoadmap, but the epic whose ID matches selectedID gets a "> " cursor and the tree's selection style; all other lines keep their per-segment styles (status glyph, blocked and after annotations) untouched. It also returns the 0-based content line index of the selected epic line (-1 when nothing is selected) so callers can scroll it into view.

func SaveState added in v0.18.0

func SaveState(storePath string, s PersistedState) error

SaveState writes the current App state to .tick/<stateFileName> in the store path. The file is created (or truncated) with mode 0o644. Errors are returned to the caller; the .tick dir must already exist.

Types

type App added in v0.18.0

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

App is the root model (§2): it owns global focus, terminal size, the active scope (selected sidebar node), the active content view, and the view registry. It routes Update/View to the focused child and picks the adaptive 3/2/1-pane layout by terminal width (§3, layout.go).

func NewApp added in v0.18.0

func NewApp(ticks []tick.Tick, storePath, owner string) App

NewApp builds the root model from the filtered tick set, the store path, and the current user. It wires the sidebar, the view registry (List registered), and the read-only detail stub.

func (*App) ApplyState added in v0.18.0

func (a *App) ApplyState(s PersistedState)

ApplyState restores the PersistedState into the App fields. Call this after NewApp but before tea.NewProgram.

func (*App) Close added in v0.18.0

func (a *App) Close()

Close releases the filesystem watcher.

func (*App) ExtractState added in v0.18.0

func (a *App) ExtractState() PersistedState

ExtractState snapshots the App fields that belong in PersistedState. Call this just before tea.NewProgram exits (i.e. after Run() returns).

func (App) Init added in v0.18.0

func (a App) Init() tea.Cmd

Init satisfies tea.Model; it starts the filesystem watcher if available.

func (App) Update added in v0.18.0

func (a App) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update routes messages to the focused child and handles global keys (§2).

func (App) View added in v0.18.0

func (a App) View() string

View renders the adaptive shell (§3): nav │ main │ (detail), with the pane count chosen by terminal width.

type PersistedState added in v0.18.0

type PersistedState struct {
	// ActiveViewIndex is App.activeIx — which content view is shown (0=List …).
	ActiveViewIndex int `json:"activeViewIndex"`

	// Scope is App.scope — the selected sidebar row (smart view or tree node).
	Scope Scope `json:"scope"`

	// CollapsedNodes is Sidebar.collapsed — set of tick IDs whose tree rows are
	// collapsed. The zero value (nil map) means nothing is collapsed.
	CollapsedNodes map[string]bool `json:"collapsedNodes,omitempty"`

	// Focus is App.focus — which pane (sidebar/main/detail) holds keyboard focus.
	Focus focusZone `json:"focus"`

	// DetailVisible is App.detailVisible — whether the detail pane is shown in
	// narrow (2/1-pane) layouts.
	DetailVisible bool `json:"detailVisible"`
}

PersistedState is the subset of App state that survives across sessions. It is serialised to JSON as .tick/.tui-state.json on quit and loaded before the first tea.NewProgram call.

Fields map directly to App internals — no re-definition: the types (Scope, focusZone, etc.) are the same ones App uses.

func DefaultState added in v0.18.0

func DefaultState() PersistedState

DefaultState returns a PersistedState with sensible defaults matching NewApp's initial values (focusSidebar, first smart-view, all expanded, etc.).

func LoadState added in v0.18.0

func LoadState(storePath string) (PersistedState, error)

LoadState reads .tick/<stateFileName> from the given store path and returns the decoded state. If the file does not exist the defaults are returned with no error. Any other I/O or JSON error is returned to the caller.

type Scope added in v0.18.0

type Scope struct {
	Kind  scopeKind
	Smart smartViewKind // valid when Kind == scopeSmart
	Node  string        // tick ID, valid when Kind == scopeNode
}

Scope is the App's active selection: either one of the smart views or a node in the project tree. Selecting a sidebar row sets the App's Scope, which the content views read to decide what set of ticks to show (§4).

type ScopeAware added in v0.18.0

type ScopeAware interface {
	SetScope(scope Scope, allTicks []tick.Tick)
}

ScopeAware is implemented by views that react to the active sidebar scope and the underlying tick set. The App calls SetScope when the selection in the sidebar changes or the ticks reload.

type Selector added in v0.18.0

type Selector interface {
	SelectedTickID() string
}

Selector is implemented by views that track a "current" tick so the detail pane can mirror the highlighted row. Returns "" when nothing is selected.

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

Sidebar is the left navigation pane: two stacked lists (smart views + the collapsible project tree). It owns its own selection cursor and collapse state; selecting a row yields a Scope via SelectedScope (§4).

func NewSidebar added in v0.18.0

func NewSidebar(allTicks []tick.Tick, owner string) Sidebar

NewSidebar builds the sidebar from the full tick set and the current user.

func (*Sidebar) SelectNode added in v0.18.0

func (s *Sidebar) SelectNode(id string)

SelectNode moves the sidebar cursor to the tree node for the given tick ID. This is used by the App's jumpToEpicMsg handler to point the sidebar at the jumped-to epic. If the ID is not present in the current row list (e.g. the sidebar has no project tree for that ID) the selection is left unchanged.

func (*Sidebar) SelectedScope added in v0.18.0

func (s *Sidebar) SelectedScope() Scope

SelectedScope returns the Scope for the currently highlighted row. When the selection rests on a non-selectable row (heading) or there are no rows, it falls back to the Awaiting smart view.

func (*Sidebar) SetSize added in v0.18.0

func (s *Sidebar) SetSize(width, height int)

SetSize records the available pane dimensions.

func (*Sidebar) SetTicks added in v0.18.0

func (s *Sidebar) SetTicks(allTicks []tick.Tick)

SetTicks updates the underlying ticks and rebuilds the row list, preserving the current selection by identity where possible.

func (Sidebar) Update added in v0.18.0

func (s Sidebar) Update(msg tea.Msg) (Sidebar, bool)

Update handles navigation and collapse/expand for the sidebar (§4). It moves the selection cursor (j/k or arrows), and toggles tree-node collapse on space/enter. It returns the updated sidebar and whether the selected scope changed (so the App can re-scope the content views).

func (Sidebar) View added in v0.18.0

func (s Sidebar) View(focused bool) string

View renders the sidebar body (§4). focused controls the selection highlight.

type Sizable added in v0.18.0

type Sizable interface {
	SetSize(width, height int)
}

Sizable is implemented by views that need to know their content dimensions. The App calls SetSize whenever the layout changes. Views that render purely from data may ignore size and omit this.

type View added in v0.18.0

type View interface {
	// Update handles a message and returns the (possibly mutated) view plus an
	// optional command. Views are value types: return the updated copy.
	Update(tea.Msg) (View, tea.Cmd)
	// View renders the view body to a string sized to the most recent
	// SetSize call.
	View() string
	// Title is the human-readable view name shown in the content header.
	Title() string
	// Tab is the short label shown in the view-tab strip (e.g. "List").
	Tab() string
}

View is a swappable content view-model for the main pane (§2, §5). Each view renders the App's current scope over the shared tick set. Views are small Update/View pairs, testable in isolation.

The interface is deliberately minimal so downstream view ticks (Board, Roadmap, Timeline) can implement it cheaply and register with one line. It is the load-bearing seam every later view tick depends on — change it only with care.

Jump to

Keyboard shortcuts

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