clihelpers

package module
v0.0.0-...-1f7baba Latest Latest
Warning

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

Go to latest
Published: Jun 18, 2026 License: Apache-2.0 Imports: 15 Imported by: 5

README

CLI Helpers

Patterns codified as to reduce yak-shaving.

Install

go get -u go.nwlabs.dev/cli-helpers/v2

Long help text for Cobra

var rootCmd = &cobra.Command{
  Use:   "command",
  Short: "Does a thing!",

  // This. ↓↓↓
  Long: clihelpers.LongHelpText(`
  Does an even longer thing!

  Longer description that is indented the same as the code.`),
  Run: func(cmd *cobra.Command, args []string) {
    // ...
  },
}

Version screen for Cobra

var versionCmd = clihelpers.VersionScreen()

func init() {
  rootCmd.AddCommand(versionCmd)
}

Default table styles for Bubbletea

t := table.New(
  table.WithColumns(columns),
  table.WithRows(rows),
  table.WithFocused(true),
  table.WithHeight(height),
)

t.SetStyles(
  clihelpers.DefaultTableStyles(), // <-- This.
)

Default keymap for Bubbletea

model struct {
  help     help.Model
  keys     clihelpers.KeyMap // <-- This.
  // ...
}

Then…

var keys = clihelpers.KeyBindings // <-- This.

Then handle the key-presses in your model.Update() method.

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  var cmd tea.Cmd

  switch msg := msg.(type) {
  case tea.WindowSizeMsg:
    // If we set a width on the help menu it can gracefully truncate
    // its view as needed.
    m.help.Width = msg.Width

  case tea.KeyMsg:
    switch {
    case key.Matches(msg, m.keys.Up):
      m.lastKey = "↑"
    case key.Matches(msg, m.keys.Down):
      m.lastKey = "↓"
    case key.Matches(msg, m.keys.Help):
      m.help.ShowAll = !m.help.ShowAll
    case key.Matches(msg, m.keys.Quit):
      m.quitting = true

      return m, tea.Quit
    }
    // ...
  }
  // ...

  return m, cmd
}

Base outer table style for Bubbletea

func (m model) View() string {
  if m.quitting {
    return ""
  }

  helpView := m.help.View(m.keys)
  baseStyle := clihelpers.BaseOuterTableStyle() // <-- Get the standard style.

  return baseStyle.Render(m.table.View()) + "\n" + helpView
}

Spinners for Bubbletea

No library code for this. Just an example of how to spin on an async process, then pass the result back to the outer scope.

import "charm.land/huh/v2/spinner"

var (
  apiToken string // <-- (1) Defined at this level of the scope.

  logger = log.NewWithOptions(os.Stderr, log.Options{
    ReportTimestamp: true,
    TimeFormat:      time.Kitchen,
    Prefix:          "app-name",
  })

  // ...

  rootCmd = &cobra.Command{
    Use:   "...",
    Short: "...",
    Long:  "...",
    Run: func(cmd *cobra.Command, args []string) {

      err := spinner.New().
        Title("Fetching OAuth Bearer Token...").
        Type(spinner.Dots).
        Action(func(apiToken *string) func() { // <-- (3) Receive the variable with *.
          return func() {
            // Do async stuff...
            token := myAsyncProcess()

            *apiToken = token // <-- (4) Assign the result to the variable we passed into the closure.
          }
        }(&apiToken)). // <-- (2) Pass the variable from the outer scope into the closure with &.
        Run()
      if err != nil {
        logger.Fatal(err)
      }

      // ...
    }

    apiToken // <-- (5) Now contains the value from the async process.
  }
)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// Understand background colors
	ClrHasDarkBG = lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
	ClrLightDark = lipgloss.LightDark(ClrHasDarkBG)

	// Base colors
	ClrBlack  = lipgloss.Color("#171e21")
	ClrBlue   = lipgloss.Color("#0087ff")
	ClrGreen  = lipgloss.Color("#009900")
	ClrOrange = lipgloss.Color("#ffa500")
	ClrPurple = lipgloss.Color("#8700ff")
	ClrRed    = lipgloss.Color("#cc0000")
	ClrWhite  = lipgloss.Color("#ffffff")
	ClrYellow = lipgloss.Color("#ffff00")

	// Meanings (logging)
	ClrFatal = ClrRed
	ClrError = ClrRed
	ClrWarn  = ClrOrange
	ClrInfo  = ClrBlue
	ClrDebug = ClrPurple

	// Meanings (success)
	ClrFailure = ClrRed
	ClrSuccess = ClrGreen

	// Inline styles change based on background
	StyleInlineHighlight = lipgloss.NewStyle().
							Foreground(ClrLightDark(ClrBlue, ClrYellow))

	// Base styles for backgrounds
	StyleFailure = lipgloss.NewStyle().
					Foreground(ClrWhite).
					Background(ClrFailure)
	StyleSuccess = lipgloss.NewStyle().
					Foreground(ClrWhite).
					Background(ClrSuccess)

	StyleDebug = lipgloss.NewStyle().
				Foreground(ClrWhite).
				Background(ClrDebug)
	StyleInfo = lipgloss.NewStyle().
				Foreground(ClrWhite).
				Background(ClrInfo)
	StyleWarn = lipgloss.NewStyle().
				Foreground(ClrBlack).
				Background(ClrWarn)
	StyleError = lipgloss.NewStyle().
				Foreground(ClrWhite).
				Background(ClrError)
)
View Source
var KeyBindings = KeyMap{
	Up: key.NewBinding(
		key.WithKeys("up", "k"),
		key.WithHelp("↑/k", "move up"),
	),
	Down: key.NewBinding(
		key.WithKeys("down", "j"),
		key.WithHelp("↓/j", "move down"),
	),
	Help: key.NewBinding(
		key.WithKeys("?"),
		key.WithHelp("?", "toggle help"),
	),
	Enter: key.NewBinding(
		key.WithKeys("enter"),
		key.WithHelp("enter", "make selection"),
	),
	Quit: key.NewBinding(
		key.WithKeys("q", "esc", "ctrl+c"),
		key.WithHelp("q/esc", "quit"),
	),
}

KeyBindings are the standard/default keybindings for the application.

Functions

func BaseOuterTableStyle

func BaseOuterTableStyle() lipgloss.Style

BaseOuterTableStyle returns a lipgloss style for the outer border of a table.

func DefaultTableStyles

func DefaultTableStyles() table.Styles

DefaultTableStyles sets the default styles for a Bubbletea table. Set the values to the table with `t.SetStyles(s)`.

func ExampleText

func ExampleText(text string) string

ExampleText returns formatting that is appropriate for CLI examples.

func GetLoggerStyles

func GetLoggerStyles() *log.Styles

func LongHelpText

func LongHelpText(text string) string

LongHelpText returns a styled string with a rounded border and padding. It also allows you to ignore indentation. This is the standardized style for Northwood Labs CLI help text.

func VCS

func VCS(key, fallback string) string

VCS reads data from the Go build info and returns the value of a specific key. Useful for pre-setting things like the VCS URL, build date, etc.

func VersionScreen

func VersionScreen() *cobra.Command

Types

type KeyMap

type KeyMap struct {
	Up    key.Binding
	Down  key.Binding
	Help  key.Binding
	Enter key.Binding
	Quit  key.Binding
}

keyMap defines a set of keybindings. To work for help it must satisfy key.Map. It could also very easily be a map[string]key.Binding.

func (KeyMap) FullHelp

func (k KeyMap) FullHelp() [][]key.Binding

FullHelp returns keybindings for the expanded help view. It's part of the key.Map interface.

func (KeyMap) ShortHelp

func (k KeyMap) ShortHelp() []key.Binding

ShortHelp returns keybindings to be shown in the mini help view. It's part of the key.Map interface.

Jump to

Keyboard shortcuts

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