Back

Construindo Interfaces de Terminal com Charm

Construindo Interfaces de Terminal com Charm

Se você já usou lazygit, k9s ou htop, você experimentou como uma interface de terminal bem construída pode ser — responsiva, estruturada e surpreendentemente elegante. Construir algo assim costumava significar lutar com ncurses ou escrever códigos de escape brutos. O ecossistema Charm muda isso completamente.

Este artigo explica como construir interfaces de terminal com Charm usando Go, focando em Bubble Tea e suas bibliotecas complementares. As versões principais atuais das bibliotecas Charm usam caminhos de módulos Go atualizados, que você verá refletidos nos exemplos abaixo. Se você pensa em componentes e estado, o modelo mental parecerá imediatamente familiar.

Principais Conclusões

  • Bubble Tea segue a arquitetura Elm (Model, Update, View), tornando o gerenciamento de estado previsível e componível.
  • Lip Gloss fornece estilização declarativa para saída de terminal, enquanto Bubbles oferece componentes de UI prontos como campos de texto, spinners e viewports.
  • As bibliotecas do ecossistema Charm se organizam de forma limpa umas sobre as outras, permitindo que você construa TUIs refinadas em Go sem códigos de escape brutos ou ncurses.
  • Huh e Wish estendem a stack para formulários e TUIs hospedadas via SSH, respectivamente.

O Que É uma TUI e Por Que Construir Uma?

Uma Interface de Usuário de Terminal (TUI) é uma aplicação interativa e visualmente estruturada que roda dentro de um terminal. Diferente de uma CLI simples que aceita um comando e encerra, uma TUI mantém estado, responde a entrada de teclado em tempo real e renderiza layouts dinâmicos.

TUIs valem a pena construir quando você quer:

  • Uma ferramenta de desenvolvedor que funciona via SSH sem um navegador
  • Menor sobrecarga de recursos do que uma aplicação Electron
  • Algo scriptável que ainda pareça refinado

O Ecossistema Charm em Resumo

Charm mantém várias bibliotecas que funcionam bem juntas:

BibliotecaFunção
Bubble TeaFramework de aplicação (loop de eventos, estado)
Lip GlossEstilização e layout
BubblesComponentes de UI pré-construídos
HuhPrimitivas de formulário e entrada
WishServidor SSH para hospedar TUIs remotamente

Bubble Tea é o núcleo. Todo o resto se organiza em cima dele.

Como Funciona a Arquitetura do Bubble Tea (Model, Update, View)

Bubble Tea segue a arquitetura Elm, um padrão que desenvolvedores frontend reconhecerão do Redux ou do useReducer do React.

Toda aplicação Bubble Tea define três coisas:

  • Model — o estado da sua aplicação
  • Update — uma função que trata mensagens e retorna um novo modelo
  • View — uma função que renderiza o modelo como uma visualização de terminal
package main

import (
    "fmt"
    "log"

    tea "charm.land/bubbletea/v2"
)

type model struct {
    count int
}

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

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyPressMsg:
        switch msg.String() {
        case "up":
            m.count++
        case "q", "ctrl+c":
            return m, tea.Quit
        }
    }
    return m, nil
}

func (m model) View() tea.View {
    return tea.NewView(fmt.Sprintf("Count: %d\n(press up to increment, q to quit)", m.count))
}

func main() {
    p := tea.NewProgram(model{})
    if _, err := p.Run(); err != nil {
        log.Fatal(err)
    }
}

Note o caminho de importação charm.land/bubbletea/v2. As versões atuais das bibliotecas Charm usam caminhos de módulo charm.land em vez das importações antigas github.com/charmbracelet/* que aparecem em muitos tutoriais mais antigos.

Adicionando Layouts e Estilização com Lip Gloss

Lip Gloss cuida da estilização. Você define estilos como valores e os aplica a strings antes de renderizar.

import "charm.land/lipgloss/v2"

var titleStyle = lipgloss.NewStyle().
    Bold(true).
    Foreground(lipgloss.Color("#FF79C6")).
    Padding(0, 1)

func (m model) View() tea.View {
    return tea.NewView(titleStyle.Render("My TUI App"))
}

Nota: Versões recentes do Lip Gloss mudaram como o comportamento de cor adaptativa funciona. Detecção de fundo e adaptação de estilo agora são tratadas mais explicitamente no código da aplicação em vez de automaticamente pela biblioteca.

Usando Componentes Pré-construídos do Bubbles

Bubbles fornece componentes prontos — campos de texto, spinners, barras de progresso, viewports e mais. Cada componente segue o mesmo contrato Model/Update/View, então você pode incorporá-los diretamente no seu próprio modelo.

import "charm.land/bubbles/v2/textinput"

type model struct {
    input textinput.Model
}

Essa componibilidade é o que faz o Bubble Tea escalar de forma limpa. Você constrói modelos pequenos e focados e os compõe em modelos maiores, exatamente como compor componentes em um framework frontend.

Quando Usar Huh ou Wish

Se sua TUI precisa de formulários — entradas de múltiplos campos, confirmações, menus de seleção — Huh cuida disso sem você precisar construir do zero. Para hospedar uma TUI via SSH para que usuários possam acessá-la sem instalar nada localmente, Wish envolve seu programa Bubble Tea em um servidor SSH.

Começando

go mod init mytui
go get charm.land/bubbletea/v2
go get charm.land/lipgloss/v2
go get charm.land/bubbles/v2

Execute seu programa com:

func main() {
    p := tea.NewProgram(model{})
    if _, err := p.Run(); err != nil {
        log.Fatal(err)
    }
}

Conclusão

A stack Charm Bubble Tea oferece uma maneira estruturada e componível de construir aplicações de terminal em Go. O padrão Model/Update/View mantém o estado previsível, Lip Gloss cuida da estilização de forma declarativa, e Bubbles fornece componentes que você gastaria dias escrevendo. Se você está confortável com pensamento baseado em componentes de frontend, construir interfaces de terminal com Charm parecerá menos como aprender um novo paradigma e mais como aplicar um que você já conhece — só que em um terminal.

Perguntas Frequentes

Bubble Tea é uma biblioteca Go, então você precisa de pelo menos um entendimento básico de Go para usá-la. Dito isso, a arquitetura inspirada em Elm é direta. Se você está familiarizado com conceitos como estado, mensagens e funções de renderização de qualquer linguagem, pode aprender a sintaxe específica de Go relativamente rápido.

As versões atuais usam caminhos de módulo charm.land como charm.land/bubbletea/v2 em vez das importações antigas github.com/charmbracelet encontradas em muitos tutoriais mais antigos. A função Init retorna apenas um comando, e a função View retorna uma visualização de terminal em vez de uma string bruta.

Sim. Como Update é uma função pura que recebe uma mensagem e retorna um novo modelo e comando, você pode testar unitariamente sua lógica de aplicação chamando Update diretamente com mensagens específicas e fazendo asserções sobre o estado do modelo retornado. A função View também pode ser testada verificando sua saída renderizada.

Sim. A biblioteca Wish permite envolver qualquer programa Bubble Tea em um servidor SSH. Usuários se conectam via SSH e interagem com sua TUI diretamente no terminal deles sem instalar nada localmente. Isso é útil para ferramentas de desenvolvedor compartilhadas, dashboards ou demos interativas.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay