ui

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package ui holds what every hoist screen shares: one Styles palette built from a light/dark flag, the frame and pane chrome every screen is drawn in (frame.go), the dialog compositor (dialog.go), relative-time wording (time.go) and the status-bar line helper. It imports Lip Gloss and x/ansi (cell width and ANSI stripping) — no Bubbles components, no screen state — so any screen package can depend on it without a cycle.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Ago

func Ago(now, t time.Time) string

Ago is Span for a past instant: "34 days ago", "just now", or "never" for a zero time.

func BodyHeight

func BodyHeight(height, sections int) int

BodyHeight is how many content rows a Frame with n sections can hold on a terminal height rows tall: the height minus the footer, the two edges and the n-1 rules between sections. A screen sizes its scrolling section to this less the fixed rows of its other sections.

func Box

func Box(st Styles, title string, sections []string, width int) string

Box draws a titled, rounded box exactly width cells wide around sections, with a rule between each pair. Every line of every section is truncated to the inner width, never wrapped: a version string that wraps mid-token is the defect this exists to end (#85).

func Columns

func Columns(st Styles, left, right string, leftWidth int) string

Columns lays left and right side by side with a vertical rule between them, left padded or truncated to leftWidth, both aligned to the top. Replaces the string-padding joinPanes the plan screen carried (AGENTS.md §4.8 codifies this as the two-pane shape).

func Dialog

func Dialog(st Styles, under, title, body string, width, height int) string

Dialog draws body in a titled box centred over under, for a width×height terminal — the shape every huh.Confirm takes from M10 on. The screen underneath stays visible (dimmed, its own colours stripped) so the decision keeps its context: the operator confirming a direct commit is still looking at the commits it ships. Built on lipgloss's compositor (Canvas and Layer), which is the one place the TUI uses it: a dialog is the case where covering the parent is the point, and everything else composes with JoinVertical (docs/tui/README.md).

The dialog is at least wide enough for its longest line plus the box, capped at the terminal; body lines wider than that are truncated. A terminal too small for the box gets the dialog alone, uncentred, so the question is never lost behind the chrome.

func Span

func Span(d time.Duration) string

Span words a duration: "just now" under a minute, then "12m", "3h 48m", "3 days", "5 weeks", "3 months", "2 years". Coarse on purpose — the reader is comparing builds, not timing them.

func StatusBar

func StatusBar(width int, left, right string) string

StatusBar renders one line exactly width cells wide: left text, then right text flush against the right edge. When both do not fit, left is truncated with an ellipsis so the key hints on the right stay visible; when even right alone does not fit, right is cut. Both arguments may carry ANSI styling; widths are measured on the visible text.

func Until

func Until(now, t time.Time) string

Until is Span for a future instant: "in 3h 48m", "now" when it has arrived, "overdue by 12m" when it has passed, or "" for a zero time (no deadline).

Types

type Frame

type Frame struct {
	Title    string
	Sections []string
	// Panes are full-width blocks (already rendered, a Box each) stacked under the main box
	// and above the footer — the matrix's in-flight pane. Empty strings are skipped.
	Panes  []string
	Footer string
}

Frame is the shape every hoist screen is drawn in (AGENTS.md §4.8, M10): a titled, rounded box whose Sections are separated by rules, and a Footer — the status bar — that is always the terminal's last line no matter how tall the box is. The box is tight to its content; a screen that wants it to fill the terminal sizes its own section (a viewport, a table) to Frame.BodyHeight. Nothing here draws a box character by hand: the edges, the junctions and the title line all come from lipgloss's Border, so the widths are right by construction rather than by counting.

A screen's View is `ui.Frame{...}.Render(styles, width, height)`; a sub-pane inside a section (the in-flight panel under the matrix) is Box, the same thing without a footer.

func (Frame) Render

func (f Frame) Render(st Styles, width, height int) string

Render draws the frame for a width×height terminal: the box, then blank rows, then the footer on the last line. Content wider than the box is truncated with "…"; content taller than the space above the footer is cut from the bottom (the screen is expected to have sized its scrolling section, see BodyHeight). A width or height too small to hold a box (under 4 columns or 3 rows) renders only what fits.

type Styles

type Styles struct {
	// Dark records which background the palette was built for.
	Dark bool
	// Header styles a table header cell, Cell an ordinary cell, Selected the cursor row.
	Header, Cell, Selected lipgloss.Style
	// Status styles the status-bar summary, Notice a transient message shown in its place,
	// Hint the key hints on the right of the bar.
	Status, Notice, Hint lipgloss.Style
	// Help styles the expanded help line toggled by ?.
	Help lipgloss.Style

	// Chrome (M10). Border colours every frame and pane edge; Title the name in a frame's
	// top edge; Rule a section divider's own text, when it carries one.
	Border, Title, Rule lipgloss.Style
	// Semantic colours, reinforcing a word rather than replacing it (the state is always
	// spelled out; colour is the second channel). Good: pinned, green CI, "in staging".
	// Warn: drifted, split, a migration, a blocked step. Bad: a failed step, an error line.
	// Dim: external, an unreached step, "…10 more". Accent: the cursor, an id, a command
	// the operator should type. Production: the amber temperature a production target
	// gives a header or a mode chip.
	Good, Warn, Bad, Dim, Accent, Production lipgloss.Style
	// Add and Del colour the + and - lines of a diff.
	Add, Del lipgloss.Style
}

Styles is the palette for one terminal background. The root model builds it once from tea.BackgroundColorMsg and pushes it into every screen; screens never pick colours themselves, so a theme change is one call.

func NewStyles

func NewStyles(dark bool) Styles

NewStyles returns the palette for a dark or light background.

Directories

Path Synopsis
Package uitest is the one harness every screen's tests render through (AGENTS.md §4.8, M10): a golden comparison at a stated terminal size that also asserts the shape a terminal would actually show — exactly height lines, none wider than width — and a way to drive a screen with real keypresses, so a test can never pass by setting the field a key would have set.
Package uitest is the one harness every screen's tests render through (AGENTS.md §4.8, M10): a golden comparison at a stated terminal size that also asserts the shape a terminal would actually show — exactly height lines, none wider than width — and a way to drive a screen with real keypresses, so a test can never pass by setting the field a key would have set.

Jump to

Keyboard shortcuts

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