ui

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 4, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package ui — banner.go draws the startup logo.

The banner is a 7x8 pixel-font rendering of "FUTILS" with a two-pass colour gradient (light yellow-green → dark forest green) and a drop shadow. The design mirrors frefresh-go's banner for visual family resemblance — same pixel metrics, same shadow offset — but swaps the orange-to-yellow palette for green so the two tools are distinguishable at a glance when the user has both in muscle memory.

Index

Constants

This section is empty.

Variables

View Source
var AccentColor = lipgloss.Color("#22c55e")

AccentColor is the brand green used for cursors, highlights and focus states throughout the UI. This is the futils-specific palette — frefresh-go uses orange (#e8712a); futils uses green (Tailwind green-500) to keep the two tools visually distinct when muscle memory would otherwise confuse them.

View Source
var BuildInfo string

BuildInfo is an optional compact freshness line shown under the version in the banner — set by main for dev builds, empty (and hidden) for releases.

View Source
var DimColor = lipgloss.Color("8")

DimColor is the muted gray used for secondary text (hints, labels, deselected rows). Terminal-theme-agnostic — ANSI 8 reads as grey on both light and dark backgrounds.

View Source
var ErrGoBack = errors.New("go back")

ErrGoBack is returned when the user presses Esc/b to go back one step. Callers handle it non-fatally (re-show parent menu) so TUI navigation feels like browser back rather than an error state.

View Source
var ErrQuit = errors.New("quit")

ErrQuit is returned when the user presses Ctrl+C or q to quit outright.

View Source
var Version = "dev"

Version is set by main at startup.

Functions

func Banner() string

Banner returns the full startup banner: pixel-art logo, centred version string, and centred keymap hint. Intended to be printed once at launch.

func Confirm

func Confirm(message string) (bool, error)

Confirm shows a yes/no prompt. Returns true for yes. Themed to the accent colour for visual consistency with the rest of the UI.

func DefaultFilterRowRenderer

func DefaultFilterRowRenderer(opt FilterOption, selected bool) string

DefaultFilterRowRenderer renders the Label only, with the cursor row highlighted in the accent color. Used when callers don't need custom per-row styling.

func FilterMenu

func FilterMenu(title string, options []FilterOption, render FilterRowRenderer) (string, error)

FilterMenu shows a searchable single-select list. Typing filters the visible rows by case-insensitive substring match on Label; arrow keys navigate the filtered subset; Enter selects.

`render` controls per-row appearance and is responsible for honoring the `selected` flag — a custom renderer that ignores selection state will produce an invisible cursor. Pass DefaultFilterRowRenderer when no custom styling is needed.

Returns the chosen option's Value, or ErrGoBack on esc, ErrQuit on ctrl+c.

func FitWidth

func FitWidth(s string, width int) string

FitWidth sizes s to exactly width display columns: padded with trailing spaces when shorter, or truncated with a trailing … when longer. Rune-aware (counts runes, not bytes) so accented names still line up in a column.

func ItemTypeColor

func ItemTypeColor(itemType string) lipgloss.Color

ItemTypeColor returns the lipgloss color used to render a Fabric item type label in the move picker. Notebooks are accent green (matching the brand), Reports are orange (matching the sibling tool's accent), Semantic Models stay at the default terminal foreground. Unknown types fall back to the default fg.

func MultiSelect

func MultiSelect(title string, options []string, initial []string) ([]string, error)

MultiSelect shows an interactive checkbox list. Items in `initial` are pre-checked — useful for "edit existing favourites" flows where you want the user to see and toggle current state.

Returns the checked items in the order they appear in `options` (not selection order), so favourites look the same regardless of whether the user clicked top-to-bottom or bottom-to-top.

Navigation:

↑/↓ k/j        single row
alt+↑/↓ pgup/pgdown / alt+k/j   jump 5 rows
home/g • end/G jump to first / last
space          toggle cursor row
a              select all (or clear if already full)
enter          confirm • esc/b go back • ctrl+c/q quit

Returns ErrGoBack on esc, ErrQuit on ctrl+c/q.

func NumberMenu

func NumberMenu(message string, options []MenuOption) (string, error)

NumberMenu shows a single-select menu with arrow-key navigation and digit shortcuts. Returns ErrGoBack on esc/b and ErrQuit on ctrl+c/q so callers can handle cancellation cleanly.

Options with IsHeader=true render as non-selectable section labels — the cursor lands on the first non-header row and skips headers when arrow keys move through the list.

func ParameterForm

func ParameterForm(params []fabric.Parameter) ([]fabric.JobInput, error)

ParameterForm prompts the user to override each discovered notebook parameter. The form renders:

  • Text / Int / Float → free-text input, empty means "keep notebook default"
  • Bool → Yes/No confirm, pre-set to the notebook's default

The return value contains ONLY genuine overrides. Fields the user didn't change are omitted so Fabric falls back to the notebook's own Python default — and because Fabric rejects empty-string Text values with a 400, we must NOT send them.

Returns ErrGoBack if the user presses esc, ErrQuit on ctrl+c.

Types

type Categorizer

type Categorizer func(tableName string) string

Categorizer maps a table name to a group label used by TableCheckbox's cascade picker. Callers supply this so internal/ui stays free of any customer-specific naming conventions (e.g. Norwegian "Fakta", German "Faktentabelle"). A nil categorizer means "no grouping" — every table lives under a single bucket.

type FilterOption

type FilterOption struct {
	Label string
	Value string
	Meta  any
}

FilterOption is one row in a FilterMenu. Label is what the user sees and what the filter matches. Value is returned on selection. Meta is arbitrary per-row data the caller's renderer can use (e.g. the Fabric item type for color-coding).

type FilterRowRenderer

type FilterRowRenderer func(opt FilterOption, selected bool) string

FilterRowRenderer turns a FilterOption + selection state into a rendered string. Selection state takes precedence: a renderer MUST return a uniformly-highlighted row when selected, regardless of any per-row coloring it would otherwise apply.

type MenuOption struct {
	Label    string
	Value    string
	IsHeader bool
}

MenuOption is one row in a NumberMenu. Label is what the user sees, Value is what gets returned when they select it — decoupled so display names can differ from internal identifiers (e.g. notebook display name vs. item ID).

IsHeader marks a non-selectable section label. Header rows render in dim style with no number/pointer, are skipped by cursor navigation, and don't consume a digit shortcut — selectable items are numbered independently so "1, 2, 3" stays sane even when headers sit between them.

func MenuOptionsFromStrings(values []string) []MenuOption

MenuOptionsFromStrings is a shortcut for menus where the user sees the same text they'd pass programmatically (e.g. customer names).

type Spinner

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

Spinner shows a non-blocking animated spinner on stdout. Suitable for wrapping long API calls so the terminal doesn't look frozen.

func NewSpinner

func NewSpinner(message string) *Spinner

func (*Spinner) Start

func (s *Spinner) Start()

Start begins the animation in a goroutine. Call Stop to end it.

func (*Spinner) Stop

func (s *Spinner) Stop()

Stop halts the animation and blocks until the goroutine has exited, so the caller's next stdout write doesn't race with a final frame.

type TableSelection

type TableSelection struct {
	FullRefresh bool
	Tables      []string
	Summary     string
}

TableSelection is what TableCheckbox returns. FullRefresh=true means "no objects in the refresh body" (i.e. refresh the entire model). Otherwise Tables is the explicit list passed to the Enhanced Refresh API.

func TableCheckbox

func TableCheckbox(message string, tables []string, categorizer Categorizer) (TableSelection, error)

TableCheckbox shows the refresh-specific table picker. Returns a TableSelection describing what to refresh, or ErrGoBack/ErrQuit if the user backed out.

categorizer may be nil — in that case every table goes into a single "Tables" bucket and the cascade only has the global All toggle. Pass a customer-specific categorizer (e.g. Dim/Fakta/Log) from the cmd layer to get domain-aware grouping without coupling internal/ui to any one customer's naming convention.

Jump to

Keyboard shortcuts

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