umma.dev

Charm CLI

Here I give a brief overview of what Charm CLI is and how to get started with the basics.

What is Charm CLI?

Charm CLI a framework Go lang CLI applications. Charm offers a variety of different libraries and apps you can use to integrate into your application.

Charm CLI Libs/Apps

Libraries

Bubble Tea

Build terminal user interfaces.

Lip Gloss

Style your terminal.

Charm

Back-end for your commandline applications.

Wish

Create SSH apps.

Bubbles

The Bubble Tea component toolkit.

Glamour

Stylesheet drivern markdown renderer.

Harmonica

Physics-based animation library.

Apps

VHS

Script GIFs of the terminal with code.

  • Edit code to change GIFs
  • Run locally or from server
  • Works with CI

Soft Serve

Self-hostable Git server which runs its own SSH service and SSH-accessible terminal user interface.

  • Configure git
  • Create repos with git push
  • Browse repos via ssh-powered TUI

Glow

Render markdown on the commpand line.

Skate

Personal key value store via command line.

Log

Colourful logging library

Getting Started

Example: building a shopping list for command line via Bubble Tea

mkdir [folder-name]

cd [folder-name]

go mode init [folder-name]

touch main.go
// main.go

package main

import (
  "fmt"
  "os"

  tea "github.com/charmbracelet/bubbletea"
)

// neeed methods init (reutrn initial command for application run), update (handle incokming events and update model accordingly) and view (render UI based on data in model)

// init
type mode struct {
  choices []string // list of items
  cursor int // which item cursor is pointing at
  selected map[int]struct{} // item selected
}

func initialMode() model {
  return model{
    choices: []string{"Buy apples", "Buy pears", "Buy bananas"},
    selected: make(map[int]struct{})
  }
}

func (m model) Init() tea.Cmd {
  retur nil
}

// update
func (m model) Update(msg tea.Msg) (tea.Model, tea.cmd) {
  switch msg := msg.(type) {
    case tea.KeyMsg:
      switch msg.String() {
        case "ctrl+c", "q":
          return m, tea.Quit // m is the model
        case "up", "k":
          if m.cursor > 0 {
            m.cursor --
          }
        case "down", "j":
          if m.cursor < len(m.choices)-1 {
            m.cursor++
          }
        case "enter", " ":
          _, ok := m.selected[m.cursor]
          if ok {
            delete(m.selected, m.cursor)
          } else {
            m.selected[m.cursor] = struct{}{}
          }
      }
  }
  return m, nil
}

// view - this is where lipgloss would be used
func (m model) View() string {
  s := "What should we buy at the market? \n\n"

  for i, choice := range m.choices {
    // is cursor pointing at this choice
    cursor := " " // no cursor - empty current index
    if m.cursor == i {
      cursor = ">"
    }

    // is this choice selected?
    checked := " " // not selected
    if _, ok := m.selected[i]; ok{
      checked = "x"
    }

    // render the row
    s+= fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
  }
  s += "\nPress q to quit \n"
  return s
}

func main() {
  p := tea.NewProgram(iniialModel())
  if err := p.Start(); err != nil {
    fmt.Printf("Error: %v", err)
    os.Exit(1)
  }
}
go run .

Find Out More…

Charm CLI docs