Interface de usuário do terminal: BubbleTea (Go) vs Ratatui (Rust)

Visão rápida de frameworks TUI no estilo Elm (Go) versus modo imediato (Rust)

Conteúdo da página

Duas opções fortes para construir interfaces de usuário para terminal hoje são BubbleTea (Go) e Ratatui (Rust). Uma oferece um framework opinado, estilo Elm; a outra, uma biblioteca flexível, em modo imediato.

Este post resume o que cada uma é, mostra um exemplo mínimo para ambas e sugere quando escolher uma ou outra. E sim, também fornece alguns links úteis.

Crush UI - implementado usando o framework BubbleTea

Crush UI (screenshot acima) é implementado usando o framework BubbleTea.

O que é o BubbleTea?

BubbleTea é um framework Go para TUIs baseado em The Elm Architecture. Você descreve seu app com um modelo (estado) e três partes: Init (comando inicial), Update (trata mensagens, retorna o novo modelo e um comando opcional), e View (renderiza a interface como uma string). O framework executa o loop de eventos, transforma teclas e I/O em mensagens e redessenha quando o modelo muda. Então: o que é o BubbleTea? Em resumo, é a maneira divertida e com estado de construir aplicações para terminal em Go, com uma única fonte de verdade e atualizações previsíveis.

O BubbleTea está pronto para produção (v1.x), tem dezenas de milhares de estrelas no GitHub e milhares de importadores conhecidos, e funciona em linha, tela cheia ou misto. Normalmente você o combina com Bubbles para componentes (inputs, viewports, spinners) e Lip Gloss para estilização. Se você estiver construindo um TUI em Go, um primeiro passo sólido é seguir a estrutura comum de projetos Go para que seu cmd/ e pacotes fiquem claros conforme o app cresce.

Um programa BubbleTea mínimo tem esta aparência: um modelo, Init, Update (tratando mensagens de tecla), e View retornando uma string. O runtime faz o resto.

package main

import (
    "fmt"
    "os"
    tea "github.com/charmbracelet/bubbletea"
)

type model struct {
    choices  []string
    cursor   int
    selected map[int]struct{}
}

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.KeyMsg:
        switch msg.String() {
        case "ctrl+c", "q":
            return m, tea.Quit
        case "up", "k":
            if m.cursor > 0 { m.cursor-- }
        case "down", "j":
            if m.cursor < len(m.choices)-1 { m.cursor++ }
        case "enter", " ":
            if _, ok := m.selected[m.cursor]; ok {
                delete(m.selected, m.cursor)
            } else {
                m.selected[m.cursor] = struct{}{}
            }
        }
    }
    return m, nil
}

func (m model) View() string {
    s := "What should we buy?\n\n"
    for i, choice := range m.choices {
        cursor := " "
        if m.cursor == i { cursor = ">" }
        checked := " "
        if _, ok := m.selected[i]; ok { checked = "x" }
        s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
    }
    return s + "\nPress q to quit.\n"
}

func main() {
    if _, err := tea.NewProgram(model{
        choices:  []string{"Buy carrots", "Buy celery", "Buy kohlrabi"},
        selected: make(map[int]struct{}),
    }).Run(); err != nil {
        fmt.Printf("error: %v", err)
        os.Exit(1)
    }
}

Aplicações notáveis construídas com BubbleTea incluem Crush (agente de código AI baseado em TUI do Charm), Glow, Huh, e muitas ferramentas do ecossistema de projetos Go mais populares no GitHub. Para aplicações Go mais complexas, você pode adicionar injeção de dependência e testes sólidos unitários; as mesmas ideias se aplicam aos modelos e comandos do BubbleTea.

O que é o Ratatui?

Ratatui é uma biblioteca Rust para TUIs que usa renderização em modo imediato: cada quadro você descreve a interface inteira (widgets e layout), e o Ratatui desenha. O que é o Ratatui? É uma ferramenta leve e sem opinião — não impõe um modelo estilo Elm ou uma estrutura específica de app. Você mantém seu próprio estado, roda seu próprio loop de eventos (normalmente com crossterm, termion ou termwiz), e chama terminal.draw(|f| { ... }) para renderizar. Então a diferença entre estilo Elm e modo imediato é exatamente esta: no estilo Elm o framework possui o loop e você reage via Update/View; no modo imediato você possui o loop e redessenha a interface inteira a partir do estado atual a cada quadro.

O Ratatui é usado por mais de 2.100 crates e confiado por empresas como Netflix (ex: bpftop), OpenAI, AWS (ex: amazon-q-developer-cli) e Vercel. A versão 0.30.x é atual, com documentação forte e backends opcionais. É uma boa escolha quando você quer controle total sobre entrada e renderização, ou quando já está no ecossistema Rust.

Um app Ratatui mínimo: você inicia o terminal, roda um loop que desenha e depois lê eventos, e restaura o terminal ao sair.

use crossterm::event::{self, Event, KeyCode};
use ratatui::{prelude::*, widgets::Paragraph};
use std::time::Duration;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut terminal = ratatui::init();
    loop {
        terminal.draw(|frame| {
            let area = frame.area();
            frame.render_widget(
                Paragraph::new("Hello, Ratatui! Press q to quit.").alignment(Alignment::Center),
                area,
            );
        })?;
        if event::poll(Duration::from_millis(250))? {
            if let Event::Key(key) = event::read()? {
                if key.code == KeyCode::Char('q') {
                    break;
                }
            }
        }
    }
    ratatui::restore();
    Ok(())
}

Então: quando devo escolher BubbleTea vs Ratatui? Escolha BubbleTea quando quiser o caminho mais rápido para uma TUI polida em Go, com um único modelo e Update/View claros, e quando sua equipe ou ecossistema for Go (ex: você já está usando ORMs Go ou Ollama em Go). Escolha Ratatui quando precisar de controle máximo, estiver em Rust, ou estiver construindo TUIs sensíveis a desempenho ou com restrições de recursos; seu design em modo imediato e backends opcionais oferecem essa flexibilidade.

Resumo

Aspecto BubbleTea (Go) Ratatui (Rust)
Estilo Arquitetura Elm (modelo, Init/Update/View) Modo imediato (você possui o loop, desenha cada quadro)
Ecosistema Bubbles, Lip Gloss, 10k+ apps, Crush 2,100+ crates, Netflix/OpenAI/AWS/Vercel
Melhor para Iteração rápida, equipes Go, CLIs Controle, desempenho, código bases em Rust

Ambos são excelentes escolhas; a linguagem preferida e o quanto de estrutura queremos do framework devem guiar a decisão.

Recursos Externos