Documentation
¶
Index ¶
- func ConfigDir() string
- func ConfigPath(app, file string) string
- func EnvOr(key, fallback string) string
- func ExpandHome(path string) string
- func HomeDir() string
- func PadLines(s string, width int) string
- func PadToHeight(s string, lines int) string
- func WrapText(s string, width int) string
- func WriteJSON(v any) int
- type Action
- type Binding
- type Config
- type Footer
- type HelpModal
- type HelpSection
- type IPCArgs
- type KeyRegistry
- func (r *KeyRegistry) Actions() []Action
- func (r *KeyRegistry) Bindings() []key.Binding
- func (r *KeyRegistry) CustomCount() int
- func (r *KeyRegistry) IsCustom(id string) bool
- func (r *KeyRegistry) Load() error
- func (r *KeyRegistry) Register(a Action) *KeyRegistry
- func (r *KeyRegistry) RegisterMany(actions ...Action) *KeyRegistry
- func (r *KeyRegistry) Reload() (bool, error)
- func (r *KeyRegistry) Reset(id string) error
- func (r *KeyRegistry) ResetAll() (int, error)
- func (r *KeyRegistry) Resolve(id string) key.Binding
- func (r *KeyRegistry) Save() error
- func (r *KeyRegistry) Set(id string, keys ...string) error
- type SettingsModal
- type Theme
- func (t Theme) Dim() lipgloss.Style
- func (t Theme) Error() lipgloss.Style
- func (t Theme) Footer(width int) lipgloss.Style
- func (t Theme) Header(width int) lipgloss.Style
- func (t Theme) Info() lipgloss.Style
- func (t Theme) Modal() lipgloss.Style
- func (t Theme) Muted() lipgloss.Style
- func (t Theme) Panel(focused bool) lipgloss.Style
- func (t Theme) Success() lipgloss.Style
- func (t Theme) Title() lipgloss.Style
- func (t Theme) Warning() lipgloss.Style
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ConfigDir ¶
func ConfigDir() string
ConfigDir is the user config base (os.UserConfigDir()), falling back to ~/.config — the parent of every project's own config directory.
func ConfigPath ¶
ConfigPath is ~/.config/<app>/<file>, the canonical location of an app's own config files (config.toml, keybindings.json). It honors XDG_CONFIG_HOME through ConfigDir, so callers never need to resolve the base themselves.
func ExpandHome ¶
ExpandHome expands a leading "~" or "~/" into the user's home directory, leaving any other path untouched.
func HomeDir ¶
func HomeDir() string
HomeDir is the current user's home directory, the base most config/data paths are anchored to. Falls back to $HOME when UserHomeDir fails.
func PadLines ¶
PadLines pads or truncates every line of s so its ANSI-aware visible width is exactly `width` — a border box only ends up sized (and positioned) correctly if every line it wraps is uniform. The truncating side uses an ANSI-aware truncate (ansi.Truncate, not go-runewidth's): lines here can carry nested SGR codes (table header/selected-row colors, highlighted cards), and a truncate that counts escape-sequence bytes as visible width cuts real content far too early, garbling it.
func PadToHeight ¶
PadToHeight pads s with blank lines until it has exactly `lines` lines (or truncates extra ones) — used for panels whose natural content is shorter than their computed box height, so a fixed-height neighbor panel's border still lines up with this one's bottom border.
Types ¶
type Action ¶
Action is one keybinding in a KeyRegistry: a stable ID (used as the key in the persisted config file and in Resolve calls), the help description shown in the footer/help modal, the default keys, and an optional display label (defaults to the keys joined with "/"). Label can be a compact alias — e.g. Keys {"j","k","up","down"} with Label "j/k".
type Binding ¶
Binding is the keybinding type the help modal and footer consume. It's the bubbles/key Binding, aliased so apps only need to import tabelhatuiui (the bubbles/key types stay available if an app wants to do its own matching).
type Config ¶
type Config[T any] struct { // contains filtered or unexported fields }
Config holds an app's settings, merged from compiled-in defaults and an optional TOML file. It mirrors KeyRegistry: the file on disk is the source of truth, and Reload() picks up external edits without restarting.
T must be a struct of plain values. Pointer, map and slice fields are shared with the defaults value until the file overrides them, so callers must not mutate what Get() returns.
func NewConfig ¶
NewConfig builds a Config for path, using defaults for every key the file leaves out. It does not touch the filesystem; call Load first.
func (*Config[T]) Get ¶
func (c *Config[T]) Get() T
Get returns the current effective config: defaults with the file's overrides applied.
func (*Config[T]) Load ¶
Load reads the config file once. A missing file is not an error — the app runs on pure defaults. See Reload for the config-file-first flow.
func (*Config[T]) Reload ¶
Reload re-reads the config file, making the on-disk config.toml the source of truth again — call it from the app's reload key so an external edit is picked up without restarting. It reports whether the effective config changed (a deleted file counts as a change back to pure defaults).
A malformed file returns an error and leaves the previous config in place, so a typo mid-edit never drops a running app back to defaults.
type Footer ¶
type Footer struct {
// contains filtered or unexported fields
}
Footer builds the bottom status/help bar: a context string on the left (a message, a counter, "perfil: dev") and the keybinding hints on the right, generated from a slice of bubbles/key bindings so the hints can never drift out of sync with what Update() actually matches.
The layout follows what every ianptkcs TUI hand-rolled before this type existed: left and right sides separated by a run of spaces, with token-aware truncation (hints are dropped at " · " boundaries from the right, then the left side is truncated) when the line overflows.
func NewFooter ¶
NewFooter starts a footer whose right side is generated from the given keybindings. Disabled bindings are skipped.
func (*Footer) Line ¶
Line returns the footer's unstyled content, sized to fit a Footer(width) render — that is, trimmed to width-4 columns (the Footer style's horizontal padding + border consume the other 4). The line never wraps: whatever doesn't fit gets dropped or truncated.
type HelpModal ¶
type HelpModal struct {
// contains filtered or unexported fields
}
HelpModal is a centered overlay that lists every keybinding in the app, grouped by section — the "?" view every vim/tmux-ish TUI has. Build it once with the app's sections, bind "?" to Toggle(), forward Update() results, and render it last in View (on top of the app).
Scrolls with j/k (or arrows), closes with q/esc. Sizes itself from the latest SetSize call and centers itself over the whole screen.
func NewHelpModal ¶
func NewHelpModal(sections ...HelpSection) *HelpModal
NewHelpModal creates a help modal from the given sections. Bindings that are disabled (or have no keys) are skipped at render time.
func (*HelpModal) SetSize ¶
SetSize records the current viewport so the modal can size/center itself. Call it from the app's WindowSizeMsg handler.
func (*HelpModal) Toggle ¶
func (m *HelpModal) Toggle()
Toggle opens the modal if it's closed and closes it if it's open.
func (*HelpModal) Update ¶
Update handles keys while the modal is open and reports whether the message was consumed. When visible, every message is consumed — the app must not process its own keys while the modal is up.
type HelpSection ¶
HelpSection is one titled group of keybindings in the help modal — for example "Navegação" with the panel-switching keys, "Ações" with the per-app commands. Bindings are static; BindingsFn (if set) supplies them live at render time — pass one backed by a KeyRegistry so the help modal reflects rebinds instantly.
type IPCArgs ¶
IPCArgs is the parsed form of a `bin ipc <método> [key=value...] --json` invocation — the scriptable-data-source convention dcal/djobs/tabelharadar all share, so a shell script or an LLM can ask a TUI's data without going through the interface itself.
func ParseIPCArgs ¶
ParseIPCArgs parses the args after `ipc`. It returns an error for an unknown arg (anything that's neither --json nor key=value) or a missing --json flag; the caller is expected to print its own usage line with it.
type KeyRegistry ¶
type KeyRegistry struct {
// contains filtered or unexported fields
}
KeyRegistry is the single source of truth for an app's keybindings: defaults registered in code, optional per-action overrides persisted to a JSON file, and live resolution so dispatch/footer/help/settings always see the same effective binding. Create one, Register every action, Load(), then Resolve in Update and feed Bindings() to Footer/HelpModal.
The config file is the primary customization flow (config-file-first): the user edits keybindings.json and the app calls Reload() on a reload key (or per View) to pick the edit up without restarting. The persisted format is a flat map of action ID to the custom keys; the file is written only when at least one override exists (all-resets delete it), and is removed when the last override is cleared.
func NewKeyRegistry ¶
func NewKeyRegistry(path string) *KeyRegistry
NewKeyRegistry creates an empty registry that persists overrides to path.
func (*KeyRegistry) Actions ¶
func (r *KeyRegistry) Actions() []Action
Actions returns every registered action with its CURRENT (effective) keys, in registration order, for rendering the settings list.
func (*KeyRegistry) Bindings ¶
func (r *KeyRegistry) Bindings() []key.Binding
Bindings returns the effective binding for every registered action, in registration order — feed this to Footer/HelpModal.
func (*KeyRegistry) CustomCount ¶
func (r *KeyRegistry) CustomCount() int
CustomCount is the number of actions with a custom override.
func (*KeyRegistry) IsCustom ¶
func (r *KeyRegistry) IsCustom(id string) bool
IsCustom reports whether an action currently has a custom override.
func (*KeyRegistry) Load ¶
func (r *KeyRegistry) Load() error
Load reads custom overrides from the config file once. A missing file is not an error (no overrides yet). See Reload for the config-file-first flow.
func (*KeyRegistry) Register ¶
func (r *KeyRegistry) Register(a Action) *KeyRegistry
Register adds (or replaces) an action with its default keys and help. Registration order is kept and used by Bindings()/Actions().
func (*KeyRegistry) RegisterMany ¶
func (r *KeyRegistry) RegisterMany(actions ...Action) *KeyRegistry
RegisterMany is a convenience for registering a slice of actions in order.
func (*KeyRegistry) Reload ¶
func (r *KeyRegistry) Reload() (bool, error)
Reload re-reads the config file, making the on-disk keybindings.json the source of truth again — call it from the app's reload key (or once per View) so an external edit to the file is picked up without restarting. It reports whether the overrides changed on disk since the last load/save (a deleted file counts as a change back to pure defaults).
func (*KeyRegistry) Reset ¶
func (r *KeyRegistry) Reset(id string) error
Reset restores an action to its default keys and persists the change.
func (*KeyRegistry) ResetAll ¶
func (r *KeyRegistry) ResetAll() (int, error)
ResetAll restores every action to its defaults (deleting the config file) and returns the number of overrides that were cleared.
func (*KeyRegistry) Resolve ¶
func (r *KeyRegistry) Resolve(id string) key.Binding
Resolve returns the effective binding for an action — custom keys when an override exists, otherwise the defaults. Unknown IDs resolve to a disabled empty binding.
func (*KeyRegistry) Save ¶
func (r *KeyRegistry) Save() error
Save persists the current overrides. With nothing overridden it deletes the config file so the app reads as pure defaults.
type SettingsModal ¶
type SettingsModal struct {
// contains filtered or unexported fields
}
SettingsModal is the keybinding editor: a centered overlay listing every registered action with its current keys, letting the user rebind (enter → "press a key"), reset an action (r) or everything (R). Overrides go straight to the KeyRegistry, which persists them.
Built on top of a KeyRegistry, so the footer, the help modal and the key dispatch all pick up changes immediately. Bind a key (e.g. "," or "s") to Toggle(), forward Update() results, and render it last in View.
func NewSettingsModal ¶
func NewSettingsModal(r *KeyRegistry) *SettingsModal
NewSettingsModal creates a settings modal editing the given registry.
func (*SettingsModal) Close ¶
func (m *SettingsModal) Close()
func (*SettingsModal) Open ¶
func (m *SettingsModal) Open()
Open and Close explicitly control visibility.
func (*SettingsModal) SetSize ¶
func (m *SettingsModal) SetSize(width, height int)
SetSize records the current viewport so the modal can size/center itself.
func (*SettingsModal) Toggle ¶
func (m *SettingsModal) Toggle()
Toggle opens the modal if closed and closes it if open.
func (*SettingsModal) Update ¶
func (m *SettingsModal) Update(msg tea.Msg) bool
Update handles keys while the modal is open and reports whether the message was consumed. While visible, every message is consumed — the app must not process its own keys.
func (*SettingsModal) View ¶
func (m *SettingsModal) View(theme Theme) string
View renders the settings overlay centered on the screen, or "" when closed. Render it last in the app's View.
func (*SettingsModal) Visible ¶
func (m *SettingsModal) Visible() bool
Visible reports whether the modal is currently open.
type Theme ¶
type Theme struct {
Base lipgloss.Color
Mantle lipgloss.Color
Surface0 lipgloss.Color
Surface1 lipgloss.Color
Overlay0 lipgloss.Color
Overlay1 lipgloss.Color
Text lipgloss.Color
Subtext0 lipgloss.Color
// Primary mirrors the installed DankMaterialShell's own configured
// accent (falling back to a manually chosen Catppuccin accent) — see
// resolvePrimaryHex. Consumers read the same DMS settings.json djobs and
// tabelharadar do, so every tool's chrome matches whatever accent DMS is
// set to.
Primary lipgloss.Color
Red lipgloss.Color
Green lipgloss.Color
Yellow lipgloss.Color
Blue lipgloss.Color
Pink lipgloss.Color
Lavender lipgloss.Color
}
Theme is the resolved Catppuccin Mocha palette plus the DMS accent used as Primary — the shared chrome every ianptkcs TUI renders with. Colors follow the official semantic guide: https://github.com/catppuccin/catppuccin/blob/main/docs/style-guide.md
func NewThemeFromEnv ¶
NewThemeFromEnv builds the theme like ResolveTheme, but sources the DMS settings path and the fallback accent from environment variables derived from appPrefix — the same lookup every ianptkcs TUI's theme.go hand-rolled:
<PREFIX>_DMS_SETTINGS (default ~/.config/DankMaterialShell/settings.json) <PREFIX>_ACCENT (default "mauve")
func ResolveTheme ¶
ResolveTheme builds the theme: Primary comes from the DMS settings.json at settingsPath (empty string means "not resolvable", falling back to the Catppuccin accent fallbackAccent).
func (Theme) Footer ¶
Footer renders the bottom help/status bar: mantle background, subtext text, full width, flush against the terminal edges.
func (Theme) Header ¶
Header renders the top status bar: primary accent background, base text, full width, flush against the terminal edges. Horizontal padding gives the title an inset from the edges.
func (Theme) Modal ¶
Modal is a centered overlay box with an accent (Primary) border — used by djobs for its form/confirm dialogs, generic enough to live in the lib.
func (Theme) Muted ¶
Muted is the "no longer relevant" fallback — same as Dim but one step more visible (Overlay1), used for stale/secondary content.
func (Theme) Panel ¶
Panel intentionally has no Width(): calling Width() on a style makes lipgloss re-wrap its content, and that wrap logic miscounts lines that already carry their own nested ANSI (a table's selected-row highlight, a highlighted card), breaking alignment. Content is pre-padded to a uniform width with PadLines instead, so the border ends up sized correctly on its own. focused switches the border to Primary (also used for headers/ titles), so the currently-navigable panel is obvious.
func (Theme) Success ¶
Semantic status styles follow the Catppuccin style guide's meanings: Success for active/ok, Warning for paused/delayed, Error for a hard problem, Info for a neutral "done" state. Background is set to Base so the colored text reads correctly wherever it's dropped in (a table cell, a description line, a modal) — djobs' colorizeStatusColumn needs this.