tuibase

package module
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 2026 License: MIT Imports: 7 Imported by: 0

README

tui-base

A structured, production-minded foundation for building large Charm v2 terminal applications in Go.

This project is designed for engineers who want more than a demo app: predictable architecture, consistent theme behavior, keyboard and mouse interaction patterns, and debug tooling that helps you understand message flow while you build.

Coverage Status

Sibling repos

  • jarvisfriends/snap — "Jarvis Friends Snap": the reusable components (keys, geometry, date/time pickers, and more on the way — navigation, table, pickers). tui-base imports them back; snap is the single source of truth. First packages moved 2026-07-09.
  • jarvisfriends/inspector — the runtime debugger, being split out so any Charm-based app can use it (see ROADMAP SP-12).

Why This Exists

Building a large TUI gets hard when app state, routing, styling, and diagnostics are scattered. tui-base provides an opinionated structure that keeps those concerns coherent:

  • Router-first composition for multi-page apps.
  • Shared live theme pointer propagated across components.
  • Status and notification primitives for global UX.
  • Inspector workflows for observing runtime behavior.
  • Test- and benchmark-friendly package boundaries.

Current Capabilities

The following milestone capabilities are already implemented:

  • Shared style propagation via one mutable app style pointer.
  • Window title + terminal canvas foreground/background managed at router and page level.
  • Keyboard + mouse navigation via Sidebar and Tabs.
  • Global shortcuts for settings, navigation visibility, detailed help, and status visibility.
  • Runtime navigation style switching and settings persistence.
  • Settings UX with overview rows + overlay editor forms.
  • Live theme preview while editing settings.
  • Runtime logging configuration and level updates.
  • Inspector message log with deduplication and runtime log streaming.
  • Notification manager with severity, TTL, persistence, keyed action items, and history panel.
  • Status bar integrations for settings and notification controls, including pending-action counts.
  • Compositor-based overlays for toast/history rendering plus router-registered action prompts.
  • Modern Go cleanup workflow using modernize -fix.

Project Layout

  • tuibase.go: root consumer package — tuibase.Run(tuibase.Options{...}) bootstraps a full app.
  • cmd/tui-base/: runnable reference application.
  • router/: root model and message routing.
  • pages/: page models (home, settings); the Ctrl+D inspector now lives in inspector and is imported back.
  • theme/: compatibility alias shim over snap/styles (kept until downstream apps migrate their imports).
  • logging/: runtime logger + subscriber fanout.
  • the reusable components (navigation, status bar, notifications, table, styles, pickers, key bindings, geometry, date/time pickers, gates, winterm) live in snap and are imported back.
  • common/: shared public interfaces/types.

Quick Start

For a practical walkthrough, see docs/getting-started.md.

Demos

The snap components that need a router+pages host — navigation, status bar, notifications — are demoed here, in the reference app (their tapes can't live in a synthetic snap example). Regenerate every gif with go -C tools/rendertapes run . (Docker or Podman; the tool cross-compiles the demo binaries, runs each *.tape in the official vhs container, and drops the gifs next to their tapes).

Reference app tour

reference app tour

Pages, the settings overlay editor, and the Ctrl+D inspector.

Navigation

navigation demo

The multipage example cycling pages with Tab, focusing the sidebar with Ctrl+B for live arrow-key switching, and hiding/showing the nav chrome.

Notifications + status bar

notifications demo

Info/warning/error toasts fired from the inspector's test keys, TTL expiry, the status bar's notification count, full help, and hiding/showing the bar.

Architectural Notes

For durable design rationale and decisions, see docs/architecture-decisions.md.

Roadmap

The roadmap now tracks only open work. See .github/ROADMAP.md.

Local Development

Prerequisites

Install these once to match the full CI gate locally:

# Go (1.26.4+; 1.26.3 has a known CVE) - https://go.dev/dl/

# golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin

# shellcheck
brew install shellcheck          # macOS/Linux via Homebrew
# or: apt-get install shellcheck

# markdownlint-cli2
npm install -g markdownlint-cli2

# actionlint
brew install actionlint          # macOS/Linux
# or: go install github.com/rhysd/actionlint/cmd/actionlint@latest

# govulncheck
go install golang.org/x/vuln/cmd/govulncheck@latest
  • Build baseline:
    • go build ./...
  • Run tests:
    • go test ./... -v
  • Run race tests:
    • go test -race ./... -v
  • Run lint:
    • GOOS=windows GOARCH=amd64 golangci-lint run ./...
    • GOOS=linux GOARCH=amd64 golangci-lint run ./...
  • Run full local verification (hook-equivalent):
    • bash tools/local_verify.sh

Documentation

Overview

Package tuibase is the consumer-facing entry point for building multi-page terminal applications on the tui-base framework.

It wraps the router package (the root model that owns navigation, theming, the status bar, notifications, and the Ctrl+D inspector) so a full app needs only one import:

err := tuibase.Run(tuibase.Options{
    AppName: "My App",
    ExtraPages: []tuibase.RegisteredPage{
        {Title: "Dashboard", Model: dashboard.New()},
    },
})

Apps that need to customize the Bubble Tea program (extra options, custom signal handling) can build the pieces themselves via NewWithOptions and the router package; Run is the batteries-included path.

The runnable reference application lives in cmd/tui-base.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func EnsureWindowsTerminal

func EnsureWindowsTerminal()

EnsureWindowsTerminal relaunches the process inside Windows Terminal when it was started under the legacy Windows console (conhost) and, having done so, exits the original process. Call it as the very first statement in main — before any other setup — so apps that bootstrap their own state before tuibase.Run still get the modern terminal without doing that work twice:

func main() {
    tuibase.EnsureWindowsTerminal()
    // ... app setup, then tuibase.Run(...) ...
}

It does nothing (and returns) on non-Windows platforms, when already in a modern terminal, in a non-interactive session, when wt.exe is missing, or when opted out via TUI_BASE_NO_WT_RELAUNCH. tuibase.Run/RunContext already perform this check, so calling it here is only needed for apps that run setup before Run and want it moved as early as possible. See router.MaybeRelaunchInWindowsTerminal for the full policy.

func InstallWindowsTerminalProfile

func InstallWindowsTerminalProfile(p WindowsTerminalProfile) (fragmentFile string, err error)

InstallWindowsTerminalProfile re-exports router.InstallWindowsTerminalProfile: it registers the app as a Windows Terminal profile (branded name + icon in the new-tab dropdown). Call it from an installer or a setup flag, not on every launch.

func Run

func Run(opts Options, options ...Option) error

Run builds the router for opts, wraps it in a Bubble Tea program with tui-base's standard options (including the app-derived color-profile env var), and blocks until the program exits. Functional options, when given, apply on top of the struct (see Option).

Example

ExampleRun shows the minimal bootstrap for an application built on tui-base: one call wires the router, theming, status bar, notifications, and the Ctrl+D inspector around your pages.

It has no Output comment on purpose: the example is compile-checked but not executed, because Run blocks on a live terminal.

package main

import (
	tuibase "github.com/jarvisfriends/tui-base"
)

func main() {
	err := tuibase.Run(tuibase.Options{
		AppName:    "My App",
		ExtraPages: []tuibase.RegisteredPage{
			// {Title: "Dashboard", Model: dashboard.New()},
		},
	})
	if err != nil {
		panic(err)
	}
}

func RunContext

func RunContext(ctx context.Context, opts Options, options ...Option) error

RunContext is Run bound to ctx: cancel it (e.g. from signal.NotifyContext on SIGINT/SIGTERM) and the program shuts down cleanly with the terminal restored — the graceful-shutdown path for services and wrappers embedding a tui-base app.

func UninstallWindowsTerminalProfile

func UninstallWindowsTerminalProfile(appName string) error

UninstallWindowsTerminalProfile re-exports router.UninstallWindowsTerminalProfile: it removes the fragment previously installed for appName.

Types

type Option

type Option func(*Options)

Option is a functional option for building an app, Bubble Tea-style (SP-11, shape per Q-21): the struct Options and With* options coexist — pass a struct, a list of options, or both. All options are applied first, then defaults fill anything left unset, exactly like tea.NewProgram:

err := tuibase.Run(tuibase.Options{},
    tuibase.WithAppName("My App"),
    tuibase.WithPages(tuibase.RegisteredPage{Title: "Dashboard", Model: dash}),
    tuibase.WithDebugOverlay(myInspector),
)

func WithAppName

func WithAppName(name string) Option

WithAppName sets the display name used in the terminal window title, the info modal, and the derived env-var prefix.

func WithAppVersion

func WithAppVersion(version string) Option

WithAppVersion overrides the version string shown in the info modal.

func WithConfigDir

func WithConfigDir(dir string) Option

WithConfigDir overrides the settings configuration directory outright.

func WithConfigDirName

func WithConfigDirName(name string) Option

WithConfigDirName sets the subdirectory name under os.UserConfigDir() used for persistent state.

func WithDebugOverlay

func WithDebugOverlay(m tea.Model) Option

WithDebugOverlay stores a model that replaces the built-in inspector as the Ctrl+D debug pop-up (Q-22): while the model is non-nil, tui-base owns the Ctrl+D toggle and presents this model in the inspector's overlay box — pairing with the standalone jarvisfriends/inspector, which delivers itself as a plain tea.Model. The model receives the overlay's inner size as a WindowSizeMsg, all keys while visible (Ctrl+D/Esc close), and mouse events when its View sets OnMouse.

func WithDefaultPage

func WithDefaultPage(title string) Option

WithDefaultPage selects the page (by Title) shown on startup.

func WithGates

func WithGates(g *gate.GateRegistry) Option

WithGates supplies the feature-gate registry (see docs/feature-gates.md).

func WithInitialLogLevel

func WithInitialLogLevel(level string) Option

WithInitialLogLevel sets the log level applied at startup.

func WithKeyMap

func WithKeyMap(km *keys.AppKeyMap) Option

WithKeyMap replaces the default key map.

func WithPages

func WithPages(pages ...RegisteredPage) Option

WithPages appends application pages (order preserved).

func WithSettingsSections

func WithSettingsSections(sections ...config.Section[string]) Option

WithSettingsSections appends app-defined sections to the settings page.

func WithWatchSettingsFile

func WithWatchSettingsFile() Option

WithWatchSettingsFile enables live reload of tui_settings.json (FW-1).

func WithoutTerminalRelaunch

func WithoutTerminalRelaunch() Option

WithoutTerminalRelaunch disables the automatic relaunch into Windows Terminal when started under the legacy console.

type Options

type Options = router.Options

Options re-exports router.Options: startup configuration for the app (name, version, config dir, pages, settings sections).

type RegisteredPage

type RegisteredPage = router.RegisteredPage

RegisteredPage re-exports router.RegisteredPage: one application page (title + model) to add to the router.

type RouterModel

type RouterModel = router.RouterModel

RouterModel re-exports router.RouterModel, the root tea.Model.

func New

func New(options ...Option) *RouterModel

New returns a router built from the given functional options (SP-11); with none it has the built-in pages only (Home and Settings, with the inspector available as the Ctrl+D overlay):

m := tuibase.New(tuibase.WithAppName("My App"), tuibase.WithPages(pages...))

func NewWithOptions

func NewWithOptions(opts Options, options ...Option) *RouterModel

NewWithOptions returns a router configured for the embedding application. Functional options, when given, apply on top of the struct.

type WindowsTerminalProfile

type WindowsTerminalProfile = router.WindowsTerminalProfile

WindowsTerminalProfile re-exports router.WindowsTerminalProfile: the description of a Windows Terminal profile fragment (branded new-tab entry).

Directories

Path Synopsis
cmd
tui-base command
Command tui-base runs the reference application showcasing the framework: multi-page routing, theming, notifications, and the Ctrl+D inspector.
Command tui-base runs the reference application showcasing the framework: multi-page routing, theming, notifications, and the Ctrl+D inspector.
Package common holds small shared building blocks with no UI dependencies: the Component interface contract for pages, build/version metadata baked in via -ldflags (AppVersion, dependency info for the info modal), and WriteFileAtomic for crash-safe config persistence.
Package common holds small shared building blocks with no UI dependencies: the Component interface contract for pages, build/version metadata baked in via -ldflags (AppVersion, dependency info for the info modal), and WriteFileAtomic for crash-safe config persistence.
Package config defines the types that allow any Model to contribute configurable fields to the application's Settings page.
Package config defines the types that allow any Model to contribute configurable fields to the application's Settings page.
Package envpath shortens absolute paths for display by collapsing well-known environment-variable prefixes (e.g.
Package envpath shortens absolute paths for display by collapsing well-known environment-variable prefixes (e.g.
examples
dashboard command
Command dashboard shows a single custom page with themed widgets: a bubbles table styled via theme.TableStyles, live status bar segments, and a notification fired from a key press.
Command dashboard shows a single custom page with themed widgets: a bubbles table styled via theme.TableStyles, live status bar segments, and a notification fired from a key press.
minimal command
Command minimal is the smallest possible tui-base app: the built-in Home and Settings pages, theming, notifications, and the Ctrl+D inspector — one import, one call.
Command minimal is the smallest possible tui-base app: the built-in Home and Settings pages, theming, notifications, and the Ctrl+D inspector — one import, one call.
multipage command
Command multipage shows several app pages with custom settings sections and graceful SIGINT/SIGTERM shutdown via RunContext.
Command multipage shows several app pages with custom settings sections and graceful SIGINT/SIGTERM shutdown via RunContext.
Package filewatch bridges fsnotify file-system events into Bubble Tea messages so views can live-reload when files change on disk (FW-1).
Package filewatch bridges fsnotify file-system events into Bubble Tea messages so views can live-reload when files change on disk (FW-1).
Package logging is tui-base's runtime logger: leveled Debugf/Infof/Warnf/ Errorf functions writing to a rotating file (or the system temp directory), with a subscriber fan-out that feeds the in-app inspector's Log tab.
Package logging is tui-base's runtime logger: leveled Debugf/Infof/Warnf/ Errorf functions writing to a rotating file (or the system temp directory), with a subscriber fan-out that feeds the in-app inspector's Log tab.
Package overlay provides shared overlay primitives used by the router's built-in overlays and by page models (settings, dashboard) that manage their own centered form dialogs.
Package overlay provides shared overlay primitives used by the router's built-in overlays and by page models (settings, dashboard) that manage their own centered form dialogs.
pages
home
Package home is the default landing page shown when an application supplies no pages of its own — a minimal scrollable example of the page contract (page.Base embedding, theme-driven rendering, wheel/key scrolling).
Package home is the default landing page shown when an application supplies no pages of its own — a minimal scrollable example of the page contract (page.Base embedding, theme-driven rendering, wheel/key scrolling).
settings
Package settings is the built-in three-pane settings page: a compact overview of every setting with an edit overlay per item (huh forms for selects/text/file pickers; custom overlays for directories, multi-file lists, durations, dates, and key recording).
Package settings is the built-in three-pane settings page: a compact overview of every setting with an edit overlay per item (huh forms for selects/text/file pickers; custom overlays for directories, multi-file lists, durations, dates, and key recording).
Package router provides the root tea.Model for tui-base applications: it owns navigation, active-page selection, the status bar, the shared theme pointer, the notification manager, and the Z-ordered overlay stack (toast, notification history, Ctrl+D inspector, info modal).
Package router provides the root tea.Model for tui-base applications: it owns navigation, active-page selection, the status bar, the shared theme pointer, the notification manager, and the Z-ordered overlay stack (toast, notification history, Ctrl+D inspector, info modal).
Package testutil holds tui-base's house-rule test helpers: architecture layering (CheckNoImports) and descriptive type naming (CheckDescriptiveStructNames).
Package testutil holds tui-base's house-rule test helpers: architecture layering (CheckNoImports) and descriptive type naming (CheckDescriptiveStructNames).
Package theme is tui-base's compatibility surface over the shared style contract, which moved to github.com/jarvisfriends/snap/styles in the wholesale component wave (ROADMAP SP-2 follow-up, 2026-07-10).
Package theme is tui-base's compatibility surface over the shared style contract, which moved to github.com/jarvisfriends/snap/styles in the wholesale component wave (ROADMAP SP-2 follow-up, 2026-07-10).

Jump to

Keyboard shortcuts

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