termchrome

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 28, 2026 License: MIT Imports: 2 Imported by: 0

README

termchrome

Shared, opinionated TUI chrome widgets for sibling terminal apps (passage, ssherpa, …): rounded box geometry, a canonical key-hint footer, aligned key/value rows, a locale-aware glyph set (spinner + progress bar), and a countdown urgency ramp.

go get github.com/0xbenc/termchrome

Requires Go 1.26+. It renders strings only over a termtheme.Theme and depends on termtheme alone — no Bubble Tea, no os/net — so drawing a box, footer, or countdown on a non-list screen never drags a navigation or runtime dependency in. List windowing lives in termnav.

What's here

  • Box geometryEdge/Top/Bottom/Divider/Line draw a rounded bordered shell. Fill dashes are always border-styled (the canonical choice). The per-app overflow policy (Strip vs Sanitize) is injected via the Truncator seam, never baked in — so the trusted-chrome path and a raw-transcript path can each keep their own policy while sharing the geometry.
  • Footer / KeyHint / FooterSep — render key hints in one grammar ("key label / key label") with progressive +N overflow instead of a silent ~ truncation.
  • KVRow — an aligned label value row with one gutter, one grammar.
  • GlyphSetUnicodeGlyphs/ASCIIGlyphs/ResolveGlyphs(env) pick a spinner + progress-bar cell set by locale (braille on UTF-8, ASCII otherwise), decoupled from color. Frame(n) cycles the spinner; Bar(remaining,total,w) draws a progress bar.
  • UrgencyRole(remaining,total) — ramps a countdown success → warning → danger as it drains (danger in the last sixth, warning in the last half).

Design

termchrome is the next extraction after termtheme (the must-agree theme interchange core) and termnav (the navigation/list-windowing engine): the opinionated widgets that compose over a theme. Apps keep their own shell composition (which rows, which footer content, the wizard step rail) and their own overflow Truncator; only the genuinely shared primitives live here.

Documentation

Overview

Package termchrome owns the shared, opinionated TUI chrome widgets that sibling terminal apps (passage, ssherpa, …) render through: rounded box geometry (Edge/Top/Bottom/Divider/Line), the canonical key-hint Footer, the aligned KVRow, plus the locale-aware GlyphSet (spinner + progress Bar) and the countdown UrgencyRole.

It renders STRINGS ONLY over a termtheme.Theme. It depends on termtheme alone — no Bubble Tea, no os/net — so a box, footer, or countdown on a non-list screen never drags a navigation/runtime dependency in. List windowing lives in termnav; the per-app overflow policy (Strip vs Sanitize) stays in each app and is injected via the Truncator seam, never baked in here.

Index

Constants

View Source
const FooterSep = " / "

FooterSep is the one canonical key-hint separator. Screens historically drifted between " / " and " / "; this is the single source of truth.

Variables

This section is empty.

Functions

func Bottom

func Bottom(theme termtheme.Theme, width int) string

Bottom draws the rounded bottom border.

func Divider

func Divider(theme termtheme.Theme, width int) string

Divider draws a mid-box horizontal rule.

func Edge

func Edge(theme termtheme.Theme, left, right, label string, width int, tr Truncator) string

Edge draws a top/divider/bottom border row. The fill dashes are always border-styled — the canonical choice that resolves the historical divergence between a styled picker border and a default-colored overlay border. The label is styled by the caller and truncated by tr; an empty label yields a plain rule.

func Footer(hints []KeyHint, width int) string

Footer renders key hints in the canonical grammar ("key label / key label"). When the hints exceed width it drops trailing ones and appends a "+N" marker (progressive disclosure) rather than letting the shell silently truncate with "~". width <= 0 means no overflow handling.

func KVRow

func KVRow(theme termtheme.Theme, label, value string, gutter int) string

KVRow renders an aligned "label value" row: the label is muted and padded to gutter cells so values line up in a column, the value is foreground-styled. One gutter, one grammar — replacing the per-screen 7/8/9/13/14 drift.

func Line

func Line(theme termtheme.Theme, content string, width int, tr Truncator) string

Line wraps content as a box body row ("│ … │"), truncating with tr and padding to the inner width.

func Top

func Top(theme termtheme.Theme, label string, width int, tr Truncator) string

Top draws the rounded top border with an optional label.

func UrgencyRole

func UrgencyRole(remaining, total int) termtheme.Role

UrgencyRole ramps a countdown's color from success through warning to danger as it drains: danger in roughly the last sixth, warning in the last half, success otherwise. For a 30s TOTP that lands danger at <=5s and warning at <=15s, matching the at-a-glance "is this code about to expire" read.

Types

type GlyphSet

type GlyphSet struct {
	Name     string
	ASCII    bool
	Spinner  []string // animation frames, cycled per tick
	BarFull  string   // filled progress-bar cell
	BarEmpty string   // empty progress-bar cell
}

GlyphSet is the set of decorative runes the UI animates with — spinner frames and progress-bar cells. It exists so motion never renders as mojibake on a terminal without UTF-8: every field has an ASCII fallback, chosen by locale, independently of color (a monochrome UTF-8 xterm still gets the pretty glyphs; a C/POSIX-locale terminal gets ASCII even in full color).

func ASCIIGlyphs

func ASCIIGlyphs() GlyphSet

ASCIIGlyphs is the 7-bit fallback for terminals without UTF-8. Every rune is <= 0x7e so it is safe on legacy codepages.

func DefaultGlyphs

func DefaultGlyphs() GlyphSet

DefaultGlyphs is used when a caller does not resolve a set from the environment. Most terminals are UTF-8, so the rich set is the default; the resolved set from ResolveGlyphs should be preferred where the env is known.

func ResolveGlyphs

func ResolveGlyphs(env []string) GlyphSet

ResolveGlyphs picks a glyph set from the environment: Unicode when the active locale advertises UTF-8, ASCII otherwise. Glyph choice is deliberately decoupled from NoColor — capability, not color, decides. A nil env reads the current process environment (via termtheme.EnvMap).

func UnicodeGlyphs

func UnicodeGlyphs() GlyphSet

UnicodeGlyphs is the default rich set: a braille spinner and block bar cells.

func (GlyphSet) Bar

func (g GlyphSet) Bar(remaining, total, width int) string

Bar renders a width-cell progress bar filled to remaining/total using the glyph set's full/empty cells (e.g. "▰▰▰▱▱" or ASCII "###--"). It is plain (unstyled); apply UrgencyRole via a theme to color it. Out-of-range inputs are clamped; width<1 yields an empty string.

func (GlyphSet) Frame

func (g GlyphSet) Frame(n int) string

Frame returns the spinner frame for tick count n, cycling safely.

type KeyHint

type KeyHint struct {
	Key   string
	Label string
}

KeyHint is one footer affordance: a key (or chord) and what it does.

type Truncator

type Truncator func(value string, width int) string

Truncator shortens a (possibly styled) string to width cells. Callers pass their own so the trusted-chrome policy (Sanitize on overflow) and the raw transcript-body policy (Strip on overflow, preserve on fit) are each preserved while the box geometry is shared.

Jump to

Keyboard shortcuts

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