Documentation
¶
Overview ¶
Package theme defines capability-aware theming primitives for Nabat and its extensions.
A Theme carries one or more Palette entries, a default variant, and cross-variant defaults. Theme.Resolve picks a variant from Capabilities and returns an immutable ResolvedTheme queried by Token or accessor.
The package does not depend on the nabat root package or IOStreams; extensions can import it to read styles without pulling in command or IO machinery.
Index ¶
- Constants
- Variables
- func All() map[string]Theme
- func ChromaFromTokens(name string, tokens map[Token]lipgloss.Style) *chroma.Style
- func ChromaPreset(v Variant) string
- func GlamourFromTokens(tokens map[Token]lipgloss.Style, base *ansi.StyleConfig) *ansi.StyleConfig
- func GlamourPreset(v Variant, c Capabilities) string
- func HuhFromTokens(tokens map[Token]lipgloss.Style) huh.Theme
- func Names() []string
- func Schema() []byte
- type Capabilities
- type Metadata
- type Override
- type Palette
- type Prompt
- type PromptKnobs
- type Requirement
- type ResolvedTheme
- func (r ResolvedTheme) CheckRequirements(reqs []Requirement) error
- func (r ResolvedTheme) Chroma() *chroma.Style
- func (r ResolvedTheme) Glamour() *ansi.StyleConfig
- func (r ResolvedTheme) HasToken(t Token) bool
- func (r ResolvedTheme) Huh() huh.Theme
- func (r ResolvedTheme) ListEnumerator() list.Enumerator
- func (r ResolvedTheme) MissingTokens(req Requirement) []Token
- func (r ResolvedTheme) Name() string
- func (r ResolvedTheme) Style(t Token) lipgloss.Style
- func (r ResolvedTheme) TableBorder() lipgloss.Border
- func (r ResolvedTheme) Tokens() []Token
- func (r ResolvedTheme) Variant() Variant
- type Resolver
- type Theme
- type Token
- type UnicodeLevel
- type Variant
Examples ¶
Constants ¶
const ( // Default is the theme installed when the caller does not pass // nabat.WithTheme. Capability-aware: defers to the terminal's // detected color profile and background luminance. Default = "default" // Minimal is a low-color theme that relies on bold instead of // foreground colors. It declares variant=notty so the framework // disables chroma syntax highlighting and forces glamour into // plain-text mode, matching pipe-friendly defaults. Minimal = "minimal" // Charm is the higher-contrast palette aligned with Charm.land // defaults, suitable for dark terminals. Charm = "charm" // Dracula ships Dracula Classic (dark) and Alucard Classic (light) // per draculatheme.com/spec; pickVariant selects by terminal // luminance. Dark uses chroma/glamour/huh "dracula"; light uses // chroma "github", glamour "light", and token-derived prompts. Dracula = "dracula" // Gruvbox is the retro groove palette from morhetz/gruvbox; dark and // light variants switch via pickVariant. Pairs with chroma's "gruvbox" // and "gruvbox-light" styles. Gruvbox = "gruvbox" // CatppuccinLatte is the Catppuccin Latte palette for light // backgrounds. Pairs with chroma's "catppuccin-latte" style. CatppuccinLatte = "catppuccin-latte" // CatppuccinFrappe is the Catppuccin Frappé palette for dark // backgrounds. Pairs with chroma's "catppuccin-frappe" style. CatppuccinFrappe = "catppuccin-frappe" // CatppuccinMacchiato is the Catppuccin Macchiato palette for dark // backgrounds. Pairs with chroma's "catppuccin-macchiato" style. CatppuccinMacchiato = "catppuccin-macchiato" // CatppuccinMocha is the Catppuccin Mocha palette for dark // backgrounds. Pairs with chroma's "catppuccin-mocha" style. CatppuccinMocha = "catppuccin-mocha" // Nabat is the brand palette: warm Persian rock-candy tones // (saffron, pistachio, pomegranate, turquoise). It ships with a // matching framework-owned chroma style and huh adapter, both // referenced by name from the manifest. Nabat = "nabat" // Nord is the Nord palette (Polar Night, Snow Storm, Frost, // Aurora) for dark backgrounds. Pairs with chroma's "nord" style. Nord = "nord" // Solarized is the canonical Solarized palette (base tones plus eight // accents). Dark/light variants follow https://ethanschoonover.com/solarized/; // pickVariant selects by terminal luminance. Pairs with chroma's // "solarized-dark" / "solarized-light" styles. Solarized = "solarized" )
Untyped string constants for built-in theme names. Pass to [nabat.WithTheme]. Untyped so they mix with env/config strings. Each name matches an embedded data/<name>.json file.
Example:
nabat.New("myctl", nabat.WithTheme(theme.Dracula))
Variables ¶
var DefaultAliases = map[Token]Token{ ListEnumerator: TextMuted, TreeEnumerator: ListEnumerator, TableBorder: TextMuted, ListItem: TextPrimary, TreeItem: TextPrimary, TableCell: TextPrimary, TableHeader: TextTitle, SpinnerActive: StatusInfo, StatusActive: StatusInfo, }
DefaultAliases is the framework fall-through map used when a Token has no direct entry in the resolved Palette. ResolvedTheme.Style walks the chain until it hits a style or bottoms out at the zero lipgloss.Style.
Palette.Aliases overrides entries here; an empty target disables the default for that key. Cycles stop at Style time (zero style) and are reported at Theme.Resolve.
Functions ¶
func All ¶
All returns a defensive copy of the registry, parsing every manifest. Callers may mutate the map without affecting the registry.
Parse errors panic. For per-theme error handling, iterate Names and call Get instead.
func ChromaFromTokens ¶
ChromaFromTokens derives a chroma style from semantic tokens.
func ChromaPreset ¶
ChromaPreset picks the upstream chroma style name for v. Theme.Resolve uses it when chroma slots are empty.
- VariantDark -> "monokai"
- VariantLight -> "github"
- VariantNoTTY / unset -> "" (chroma default at render time)
func GlamourFromTokens ¶
func GlamourFromTokens(tokens map[Token]lipgloss.Style, base *ansi.StyleConfig) *ansi.StyleConfig
GlamourFromTokens derives a glamour style from semantic tokens. The base style is cloned so callers can pass one of glamour's built-in dark/light/notty presets and keep untouched slots intact.
func GlamourPreset ¶
func GlamourPreset(v Variant, c Capabilities) string
GlamourPreset picks the upstream glamour style name for v and c. Theme.Resolve uses it when glamour slots are empty.
- VariantNoTTY or non-interactive -> "notty"
- Capabilities.Dark -> "dark"
- otherwise -> "light"
func HuhFromTokens ¶
HuhFromTokens derives a huh.Theme from a per-token style map via PromptFromTokens. Prefer PromptFromTokens when the Nabat-native Prompt value is useful; this stays for callers that want huh.Theme directly.
func Names ¶
func Names() []string
Names returns the registered theme names in lexical order.
Example ¶
ExampleNames demonstrates that Names returns themes in lexical order, which is what callers typically want for completion lists.
package main
import (
"fmt"
"nabat.dev/theme"
)
func main() {
names := theme.Names()
for i := 1; i < len(names); i++ {
if names[i] < names[i-1] {
fmt.Println("unsorted")
return
}
}
fmt.Println("sorted")
}
Output: sorted
Types ¶
type Capabilities ¶
type Capabilities struct {
// Dark reports whether the terminal background is dark.
Dark bool
// BackgroundHex is the exact terminal background color when the
// detector could read it. Empty when detection could not run.
BackgroundHex string
// Profile is the active [colorprofile.Profile] for the primary
// output stream.
Profile colorprofile.Profile
// Interactive reports whether primary output is a TTY and input
// allows prompting.
Interactive bool
// Width is the terminal width in cells, or 0 when unmeasured.
Width int
// Hyperlinks reports OSC 8 hyperlink support.
Hyperlinks bool
// Unicode is the terminal's Unicode capability tier.
Unicode UnicodeLevel
// ReducedMotion reports whether animations should be suppressed
// (NO_MOTION, REDUCE_MOTION, and similar flags).
ReducedMotion bool
}
Capabilities describes the rendering surface a Theme resolves against. Themes branch on these fields for colors, plain-text fallbacks, and glamour presets.
Capabilities is a plain constructible struct: the theme package has no IOStreams dependency, so tests build values directly. The nabat root package owns detection. When detection is uncertain, the framework reports the safer (less-feature) value.
type Metadata ¶
type Metadata struct {
// Name is the manifest "name" field and [Get] registry key.
Name string
// Description is the manifest "description" field, or empty.
Description string
// Default is the fallback variant for [Theme.Resolve]. Empty for
// single-variant themes.
Default string
// Variants is the sorted list of declared variant keys. Always
// non-empty (schema requires at least one).
Variants []string
// TokenNames is the sorted, deduplicated set of token paths any
// variant declares.
TokenNames []string
}
Metadata describes a built-in manifest without invoking Theme.Resolve. Manifest returns a fresh value each call; mutating slices in place is safe and does not affect the registry.
type Override ¶
type Override interface {
// contains filtered or unexported methods
}
Override is a per-Palette mutation from the Set* helpers. Applied to every variant of the underlying Theme, so a single token tweak affects whichever variant Theme.Resolve picks.
Prefer the Set* helpers (or nabat.WithThemeOverride) over implementing Override directly.
func SetAlias ¶
SetAlias returns an Override that sets tok's fall-through target in Palette.Aliases. An empty target disables the framework default for that key.
func SetChroma ¶
SetChroma returns an Override that sets an owned *chroma.Style and clears Palette.ChromaName so the cascade uses the pointer alone.
func SetChromaName ¶
SetChromaName returns an Override that sets the upstream chroma style name and clears Palette.Chroma.
func SetGlamour ¶
func SetGlamour(s *ansi.StyleConfig) Override
SetGlamour returns an Override that sets an owned *ansi.StyleConfig and clears Palette.GlamourName and Palette.GlamourFor.
func SetGlamourName ¶
SetGlamourName returns an Override that sets the upstream glamour preset name and clears Palette.Glamour and Palette.GlamourFor.
func SetHuh ¶
SetHuh returns an Override that sets the huh.Theme for interactive prompts. Nil reverts to the PromptFromTokens fallback at Theme.Resolve time.
type Palette ¶
type Palette struct {
// Tokens is the per-token style map. Missing tokens fall through
// the alias chain ([Aliases] over [DefaultAliases]) before the
// zero [lipgloss.Style].
Tokens map[Token]lipgloss.Style
// Aliases overrides [DefaultAliases] for this palette. A non-empty
// mapping replaces the default; Aliases[X] = "" disables the
// default for that key without substituting another.
Aliases map[Token]Token
// Chroma is an owned [*chroma.Style]. Non-nil wins over
// [Palette.ChromaName]; when both are zero, [ChromaFromTokens]
// supplies the default.
Chroma *chroma.Style
// ChromaName is the upstream chroma style name used when
// [Palette.Chroma] is nil. Unknown names fall through to chroma's
// own default at render time.
ChromaName string
// Glamour is an owned [*ansi.StyleConfig]. Non-nil wins over
// [Palette.GlamourName] and [Palette.GlamourFor].
Glamour *ansi.StyleConfig
// GlamourName is the upstream glamour preset name used when
// Glamour and GlamourFor are nil. Unknown names fall through to
// glamour's own default.
GlamourName string
// GlamourFor is a capability-aware factory called by [Theme.Resolve]
// when Glamour and GlamourName are zero. Errors surface from
// [Theme.ResolveErr] (and are discarded by [Theme.Resolve]).
GlamourFor func(Capabilities) (*ansi.StyleConfig, error)
// Prompt is the Nabat-native prompt style block, converted to a
// [huh.Theme] at resolve time. Zero falls back to
// [PromptFromTokens]. [Palette.Huh] wins over Prompt when set.
Prompt Prompt
// Huh is the [huh.Theme] for interactive prompts. Non-nil wins
// over [Prompt] and [PromptFromTokens]. Use it when the closed
// [Prompt] surface is not enough.
Huh huh.Theme
}
Palette is the per-variant style data a Theme carries. Theme.Resolve picks one palette and fills nil or empty cascade slots with framework defaults.
Chroma and glamour use a three-way cascade (owned value, registered name, capability default) folded into one value at resolve time.
type Prompt ¶
type Prompt struct {
// Title styles the group / section title above prompts.
Title lipgloss.Style
// Description styles explanatory text under each prompt.
Description lipgloss.Style
// Cursor styles the text-input cursor.
Cursor lipgloss.Style
// Placeholder styles the text-input placeholder.
Placeholder lipgloss.Style
// SelectedOption styles the currently selected list item.
SelectedOption lipgloss.Style
// UnselectedOption styles items that are not selected.
UnselectedOption lipgloss.Style
// SelectedPrefix styles the marker next to the selected item.
SelectedPrefix lipgloss.Style
// UnselectedPrefix styles the marker next to non-selected items.
UnselectedPrefix lipgloss.Style
// Error styles error indicators and messages.
Error lipgloss.Style
// Help styles the keybind footer text.
Help lipgloss.Style
// Selector styles the active-row indicator and navigation arrows.
Selector lipgloss.Style
// ButtonFocused styles the focused submit / next button.
ButtonFocused lipgloss.Style
// ButtonBlurred styles the inactive button.
ButtonBlurred lipgloss.Style
// Border is the form / card border. The zero [lipgloss.Border]
// leaves huh's base border untouched.
Border lipgloss.Border
// BorderColor is the focused field left-border foreground. The
// zero value leaves huh's base border color untouched.
BorderColor color.Color
}
Prompt is the closed Nabat-native style block for interactive prompts. Each field is a lipgloss.Style for one slot; the zero style means inherit huh's base styling for that slot.
For huh's full surface, set Palette.Huh instead; a non-nil Huh wins over Prompt. Prefix markers use lipgloss.Style.SetString for their literal text.
func PromptFromTokens ¶
PromptFromTokens derives a Prompt from a per-token style map. It is the fallback when a Palette declares neither Palette.Prompt nor Palette.Huh.
func (Prompt) Huh ¶
Huh returns a huh.Theme derived from this Prompt, starting from huh.ThemeBase and overlaying set fields. Zero-style fields inherit the base.
Blurred styling reuses Focused for a stable cross-state look; authors who need separate blurred styles set Palette.Huh directly. Border and BorderColor apply to Focused.Base and Focused.Card when set; Blurred fields always use a hidden border so the focused left border acts as the focus indicator.
func (Prompt) IsZero ¶
IsZero reports whether every field on p is unset. The framework uses it to fall back to PromptFromTokens when a palette omits Prompt.
A Prompt with only [Border] or [BorderColor] set is non-zero. The check inspects fields individually because lipgloss.Style is not comparable.
type PromptKnobs ¶
type PromptKnobs struct {
// SelectedPrefix is the literal prefix before selected options.
SelectedPrefix string
// UnselectedPrefix is the literal prefix before unselected options.
UnselectedPrefix string
// Border is the form/card border. The zero [lipgloss.Border]
// leaves the prompt border unchanged.
Border lipgloss.Border
// BorderColor is the focused field left-border foreground. The
// zero value leaves the prompt border color unchanged.
BorderColor color.Color
}
PromptKnobs carries theme-wide prompt settings shared across variants.
func (PromptKnobs) Apply ¶
func (k PromptKnobs) Apply(p Prompt) Prompt
Apply overlays non-zero knobs from k onto p and returns the result.
func (PromptKnobs) IsZero ¶
func (k PromptKnobs) IsZero() bool
IsZero reports whether k leaves every prompt knob unset.
type Requirement ¶
Requirement declares the Token set a consumer reads from a ResolvedTheme. Consumer identifies the requester in diagnostics (for example "logging extension"). Plain data; pass by value; treat Tokens as read-only.
func CoreRequirements ¶
func CoreRequirements() []Requirement
CoreRequirements returns the Requirement entries the nabat root package reads from a ResolvedTheme. The framework includes this list automatically; extensions declare only what they add.
func Require ¶
func Require(consumer string, tokens ...Token) Requirement
Require returns a Requirement for consumer and the tokens it reads.
Example:
return theme.Require("logging extension",
theme.StatusInfo, theme.StatusWarning, theme.StatusError,
)
type ResolvedTheme ¶
type ResolvedTheme struct {
// contains filtered or unexported fields
}
ResolvedTheme is the immutable result of Theme.Resolve, queried by Token or accessor. Safe for concurrent use after construction. Slice results are defensive copies; shared style pointers are read-only. The zero value yields empty styles and nil chroma/glamour.
func (ResolvedTheme) CheckRequirements ¶
func (r ResolvedTheme) CheckRequirements(reqs []Requirement) error
CheckRequirements returns an error listing consumers whose tokens this ResolvedTheme does not fully cover. nil means all satisfied.
Diagnostic format:
theme "minimal" is missing tokens required by: - logging extension: status.info, status.warning - core help renderer: text.title
func (ResolvedTheme) Chroma ¶
func (r ResolvedTheme) Chroma() *chroma.Style
Chroma returns the resolved *chroma.Style for syntax highlighting. nil means no highlighting; the upstream chroma fallback applies at render time.
func (ResolvedTheme) Glamour ¶
func (r ResolvedTheme) Glamour() *ansi.StyleConfig
Glamour returns the resolved *ansi.StyleConfig for markdown rendering. nil means use glamour's own default at render time.
func (ResolvedTheme) HasToken ¶
func (r ResolvedTheme) HasToken(t Token) bool
HasToken reports whether token t is covered on this ResolvedTheme, either directly or via the alias chain.
A token explicitly set to the zero lipgloss.Style counts as covered (the author opted in). Consumers that need the style still call ResolvedTheme.Style.
func (ResolvedTheme) Huh ¶
func (r ResolvedTheme) Huh() huh.Theme
Huh returns the huh.Theme from Palette.Huh, or the PromptFromTokens fallback when that field was nil.
func (ResolvedTheme) ListEnumerator ¶
func (r ResolvedTheme) ListEnumerator() list.Enumerator
ListEnumerator returns the default list enumerator. The zero ResolvedTheme returns nil (list.Bullet at the call site); Theme.Resolve always produces a non-nil value.
func (ResolvedTheme) MissingTokens ¶
func (r ResolvedTheme) MissingTokens(req Requirement) []Token
MissingTokens returns tokens from req that this ResolvedTheme does not cover. The result is sorted lexically; empty means fully satisfied.
func (ResolvedTheme) Name ¶
func (r ResolvedTheme) Name() string
Name returns the theme name from Theme.Name. Empty means a nameless theme, typically the zero ResolvedTheme fallback.
func (ResolvedTheme) Style ¶
func (r ResolvedTheme) Style(t Token) lipgloss.Style
Style returns the lipgloss.Style for token t, walking the alias chain when t has no direct entry. The chain is the merge of DefaultAliases and Palette.Aliases folded at Theme.Resolve time.
When the chain bottoms out, Style returns the zero lipgloss.Style (terminal default). A cyclic chain returns the zero style via a per-call seen set; Theme.Resolve also reports cycles at construction.
func (ResolvedTheme) TableBorder ¶
func (r ResolvedTheme) TableBorder() lipgloss.Border
TableBorder returns the default table border. The zero ResolvedTheme returns the zero lipgloss.Border; Theme.Resolve always produces a usable border (lipgloss.NormalBorder when none was set).
func (ResolvedTheme) Tokens ¶
func (r ResolvedTheme) Tokens() []Token
Tokens returns the tokens this theme set explicitly. The slice is freshly allocated; callers may modify it without affecting the theme.
func (ResolvedTheme) Variant ¶
func (r ResolvedTheme) Variant() Variant
Variant returns the variant Theme.Resolve picked. VariantUnset means no declaration; treat it as compatible with every Capabilities snapshot. Use it for diagnostics, not to skip applying the theme (Resolve already picked a compatible palette).
type Resolver ¶
type Resolver interface {
Resolve(Capabilities) ResolvedTheme
}
Resolver resolves a theme against runtime Capabilities. Most themes declare one Palette per Variant and use Theme.Resolve. Implement Resolver directly only when palette choice cannot be expressed that way.
Theme implements Resolver.
Example ¶
ExampleResolver shows the escape hatch for themes that need to pick a palette based on runtime Capabilities in a way one Palette per Variant cannot express. Most themes never need this — the built-in catalog and every straight programmatic theme satisfies Resolver via the Theme.Resolve method that comes with the struct.
package main
import (
"fmt"
"charm.land/lipgloss/v2"
"nabat.dev/theme"
)
func main() {
r := capabilityAwareResolver{}
rt := r.Resolve(theme.Capabilities{Dark: false, Interactive: true})
fmt.Println(rt.Style(theme.StatusError).GetBold())
}
// capabilityAwareResolver picks a palette by examining capabilities at
// resolve time, branching on the Profile field in a way one Palette
// per Variant cannot express directly.
type capabilityAwareResolver struct{}
func (capabilityAwareResolver) Resolve(c theme.Capabilities) theme.ResolvedTheme {
fg := lipgloss.Color("#E05454")
if !c.Dark {
fg = lipgloss.Color("#A03030")
}
t := theme.Theme{
Name: "capability-aware",
Variants: map[theme.Variant]theme.Palette{
theme.VariantUnset: {
Tokens: map[theme.Token]lipgloss.Style{
theme.StatusError: lipgloss.NewStyle().Foreground(fg).Bold(true),
},
},
},
}
return t.Resolve(c)
}
Output: true
type Theme ¶
type Theme struct {
// Name identifies the theme in errors, the catalog key, and
// [ResolvedTheme.Name]. Built-ins require it; programmatic themes
// may leave it empty.
Name string
// Variants maps each declared [Variant] to its [Palette].
// An empty map yields a zero [ResolvedTheme] from [Theme.Resolve]
// (every Style call returns the zero [lipgloss.Style]).
Variants map[Variant]Palette
// Default is the variant [Theme.Resolve] picks when [Capabilities]
// do not match a declared variant. For a single-variant theme the
// lone key wins even when Default is [VariantUnset].
Default Variant
// ListEnum is the default enumerator for list output. Nil resolves
// to [list.Bullet].
ListEnum list.Enumerator
// TableBorder is the default table border. The zero
// [lipgloss.Border] resolves to [lipgloss.NormalBorder].
TableBorder lipgloss.Border
// PromptKnobs are theme-wide prompt settings applied to
// token-derived and palette prompt styles.
PromptKnobs PromptKnobs
}
Theme is declarative CLI styling: Palette entries per Variant, a default variant, and cross-variant defaults. Theme.Resolve returns an immutable ResolvedTheme. Safe to copy by value, but the copy shares the Variants map; use Theme.Clone or [nabat.WithThemeOverride] to tweak. Implements Resolver.
Example ¶
ExampleTheme demonstrates the canonical declarative shape: a Theme is data — one Palette per declared variant, plus cross-variant defaults. Resolution picks a variant based on Capabilities and applies framework defaults for any nil/empty cascade slot.
package main
import (
"fmt"
"charm.land/lipgloss/v2"
"nabat.dev/theme"
)
func main() {
t := theme.Theme{
Name: "plain",
Default: theme.VariantDark,
Variants: map[theme.Variant]theme.Palette{
theme.VariantDark: {
Tokens: map[theme.Token]lipgloss.Style{
theme.StatusSuccess: lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#7EC87E")),
},
},
},
}
r := t.Resolve(theme.Capabilities{Dark: true, Interactive: true})
fmt.Println(r.Name())
fmt.Println(r.Style(theme.StatusSuccess).GetBold())
}
Output: plain true
func Get ¶
Get returns the Theme registered under name. On a miss the error lists every available name:
t, err := theme.Get("draculaa") // typo
// err: nabat/theme: no theme named "draculaa"; available: [...]
The first call parses and caches the embedded manifest; later calls return a cloned cached Theme. Parse errors surface on first access.
Example ¶
ExampleGet shows the typical lookup path: resolve a built-in theme by name, then call Theme.Resolve against a Capabilities snapshot to read the resulting ResolvedTheme. Most consumers reach this through nabat.WithTheme rather than calling Get directly.
package main
import (
"fmt"
"nabat.dev/theme"
)
func main() {
t, err := theme.Get(theme.Default)
if err != nil {
fmt.Println("error:", err)
return
}
r := t.Resolve(theme.Capabilities{Dark: true, Interactive: true})
fmt.Println(r.Name())
}
Output: default
Example (Unknown) ¶
ExampleGet_unknown shows the actionable error returned when a theme name does not exist in the catalog. The error lists every available name so users can fix typos without consulting docs.
package main
import (
"fmt"
"nabat.dev/theme"
)
func main() {
_, err := theme.Get("draculaa")
fmt.Println(err != nil)
}
Output: true
func (Theme) Clone ¶
Clone returns a shallow copy of t with an independent Variants map. Palette values inside the map are shared; assigning to Clone().Variants[k] does not affect the original.
func (Theme) Resolve ¶
func (t Theme) Resolve(c Capabilities) ResolvedTheme
Resolve picks a Variant for Capabilities, fills zero Palette fields with framework defaults, and returns an immutable ResolvedTheme safe to share across goroutines.
One declared variant wins (Theme.Default ignored); otherwise Default picks. A zero default with multiple variants yields the zero ResolvedTheme. Resolve discards callback and alias-cycle errors; use Theme.ResolveErr for those. On Palette.GlamourFor failure the glamour slot stays empty.
func (Theme) ResolveErr ¶
func (t Theme) ResolveErr(c Capabilities) (ResolvedTheme, error)
ResolveErr behaves like Theme.Resolve but also returns errors from per-Palette callbacks (today Palette.GlamourFor) and alias-cycle validation.
On failure ResolveErr still returns a usable ResolvedTheme (failed glamour or alias slots stay empty) plus a non-nil error. Callers must check the error for diagnostics and may still apply the returned theme.
func (Theme) Validate ¶
Validate reports structural problems with the theme. It fails when Theme.Default names a missing variant, or Default is set with an empty Theme.Variants. Empty themes without Default are valid.
func (Theme) With ¶
With returns a derived Theme with overrides applied to every variant. The receiver is not modified; the returned theme owns fresh per-variant maps. Overrides apply left to right; the right-most override of a token wins. With panics if any override is nil.
Example:
dracula, _ := theme.Get(theme.Dracula)
mine := dracula.With(
theme.SetToken(theme.StatusError, magenta),
theme.SetAlias(theme.ListItem, theme.TextSecondary),
)
type Token ¶
type Token string
Token names a semantic style slot in a ResolvedTheme. Tokens are dotted lowercase strings (for example "status.success") that identify role, not appearance. The constants below are Nabat's well-known set; token names are an open set, not an enum.
const ( // StatusSuccess marks affirmative output (Context.Success, ok badges). StatusSuccess Token = "status.success" // StatusWarning marks warnings (Context.Warn, degraded operation). StatusWarning Token = "status.warning" // StatusError marks failure output (Context.Error, rejected commands). StatusError Token = "status.error" // StatusInfo marks neutral status narrative (Context.Info, retries). StatusInfo Token = "status.info" // TextPrimary styles primary body text and table/list/tree item text. TextPrimary Token = "text.primary" // TextSecondary styles descriptive text and prose. TextSecondary Token = "text.secondary" // TextTitle styles help titles, table headers, and section titles. TextTitle Token = "text.title" // TextLink styles hyperlinks. TextLink Token = "text.link" // AccentPrimary styles labels and key chrome accents. AccentPrimary Token = "accent.primary" // TextMuted styles de-emphasized chrome (borders, enumerators). TextMuted Token = "text.muted" // CodeSurface styles code block backgrounds. CodeSurface Token = "code.surface" // TableBorder styles characters drawn between table cells. TableBorder Token = "table.border" // TableHeader styles cells in a table header row. TableHeader Token = "table.header" // TableCell styles cells in a table data row. TableCell Token = "table.cell" // ListItem styles list item text. ListItem Token = "list.item" // ListEnumerator styles list enumerator markers. ListEnumerator Token = "list.enumerator" // TreeItem styles tree item text. TreeItem Token = "tree.item" // TreeEnumerator styles tree enumerator markers. TreeEnumerator Token = "tree.enumerator" // SpinnerActive styles the live [Spinner] icon. Unset themes fall // through to [StatusInfo] via [DefaultAliases]. SpinnerActive Token = "spinner.active" // StatusActive styles the spinner icon on active [Status] rows. // Unset themes fall through to [StatusInfo] via [DefaultAliases]. StatusActive Token = "status.active" )
Well-known semantic tokens used by Nabat core consumers.
type UnicodeLevel ¶
type UnicodeLevel uint8
UnicodeLevel is how much Unicode the terminal can render. Tiers are monotonic: higher values include the lower ones, so consumers can compare for "at least N".
const ( UnicodeASCII UnicodeLevel = iota UnicodeWide UnicodeEmoji )
UnicodeLevel constants:
- UnicodeASCII: 7-bit ASCII only; use "+", "-", "|" for boxes.
- UnicodeWide: box-drawing, bullets, and arrows (framework default).
- UnicodeEmoji: emoji and multi-codepoint sequences.
type Variant ¶
type Variant string
Variant is a theme's intended luminance or TTY context (dark, light, or notty). Runtime diagnostics and a future "--theme-variant" override read this slot.
The zero value (VariantUnset) means the theme did not declare a target; consumers should treat it as compatible with every Capabilities snapshot.
const ( // VariantUnset is the zero value: no target variant declared. VariantUnset Variant = "" // VariantDark indicates a palette designed for dark terminals. VariantDark Variant = "dark" // VariantLight indicates a palette designed for light terminals. VariantLight Variant = "light" // VariantNoTTY indicates a palette designed for non-TTY output. VariantNoTTY Variant = "notty" )
Variant constants match the manifest "variant" field and Theme.Default / Theme.Variants keys.