Interface utilisateur terminal : BubbleTea (Go) vs Ratatui (Rust)

Aperçu rapide des frameworks TUI au style Elm (Go) vs mode immédiat (Rust)

Sommaire

Deux options solides pour construire des interfaces utilisateur en terminal aujourd’hui sont BubbleTea (Go) et Ratatui (Rust). L’une vous offre un cadre opinionné, du style Elm ; l’autre, une bibliothèque flexible en mode immédiat.

Cet article résume ce que chacun est, montre un exemple minimal pour les deux, et suggère quand choisir l’un ou l’autre. Et oui, il fournit également quelques liens utiles.

Crush UI - implémenté à l’aide du framework BubbleTea

Crush UI (capture d’écran ci-dessus) est implémenté à l’aide du framework BubbleTea.

Qu’est-ce que BubbleTea ?

BubbleTea est un framework Go pour les interfaces utilisateur en terminal basé sur The Elm Architecture. Vous décrivez votre application avec un modèle (état) et trois éléments : Init (commande initiale), Update (gérer les messages, retourner un nouveau modèle et une commande facultative), et View (rendre l’interface utilisateur sous forme de chaîne). Le framework exécute la boucle d’événements, transforme les touches et les entrées/sorties en messages, et redessine lorsqu’il y a un changement de modèle. Donc : qu’est-ce que BubbleTea ? En bref, c’est une manière amusante et étatique de construire des applications en terminal en Go, avec une seule source de vérité et des mises à jour prévisibles.

BubbleTea est prêt pour la production (v1.x), a des dizaines de milliers d’étoiles sur GitHub et des milliers d’utilisateurs connus, et fonctionne en mode inline, plein écran ou mixte. Vous le combinez généralement avec Bubbles pour les composants (entrées, viewport, curseurs) et Lip Gloss pour le style. Si vous construisez une interface utilisateur en terminal en Go, une première étape solide est de suivre les structures de projet courantes Go project structure afin que vos cmd/ et vos packages restent clairs à mesure que l’application grandit.

Un programme BubbleTea minimal ressemble à ceci : un modèle, Init, Update (gérant les messages clés), et View retournant une chaîne. Le runtime fait le reste.

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)
    }
}

Des applications notables construites avec BubbleTea incluent Crush (l’agent d’IA de Charm basé sur une interface utilisateur en terminal), Glow, Huh, et de nombreux outils de l’écosystème des projets Go les plus populaires sur GitHub. Pour des applications Go plus complexes, vous pourriez ajouter l’injection de dépendances et des tests unitaires solides unit tests; les mêmes idées s’appliquent aux modèles et aux commandes de BubbleTea.

Qu’est-ce que Ratatui ?

Ratatui est une bibliothèque Rust pour les interfaces utilisateur en terminal qui utilise le rendu en mode immédiat : chaque frame, vous décrivez l’ensemble de l’interface utilisateur (widgets et mise en page), et Ratatui la dessine. Qu’est-ce que Ratatui ? C’est un outil léger et non opinionné – il n’impose pas un modèle Elm-style ou une structure d’application spécifique. Vous gérez votre propre état, exécutez votre propre boucle d’événements (généralement avec crossterm, termion ou termwiz), et appelez terminal.draw(|f| { ... }) pour le rendu. Donc, la différence entre le mode Elm-style et le mode immédiat est exactement celle-ci : dans le mode Elm-style, le framework gère la boucle et vous réagissez via Update/View ; dans le mode immédiat, vous gérez la boucle et redessinez l’ensemble de l’interface utilisateur à partir de l’état actuel chaque frame.

Ratatui est utilisé par plus de 2 100 crates et est approuvé par des entreprises comme Netflix (par exemple, bpftop), OpenAI, AWS (par exemple, amazon-q-developer-cli) et Vercel. La version 0.30.x est actuelle, avec des documents solides et des backends optionnels. C’est une bonne option lorsque vous souhaitez un contrôle total sur l’entrée et le rendu, ou lorsque vous êtes déjà dans l’écosystème Rust.

Une application Ratatui minimale : vous initialisez le terminal, exécutez une boucle qui dessine puis lit les événements, et restaurez le terminal à la sortie.

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(())
}

Donc : quand devrais-je choisir BubbleTea vs Ratatui ? Choisissez BubbleTea lorsque vous souhaitez la plus rapide des chemins vers une interface utilisateur en terminal polie en Go, avec un seul modèle et un Update/View clair, et lorsque votre équipe ou votre écosystème est en Go (par exemple, vous utilisez déjà Go ORMs ou Ollama en Go). Choisissez Ratatui lorsque vous avez besoin d’un contrôle maximum, que vous êtes en Rust, ou que vous construisez des interfaces utilisateur en terminal sensibles à la performance ou à la contrainte de ressources ; sa conception en mode immédiat et ses backends optionnels offrent cette flexibilité.

Résumé

Aspect BubbleTea (Go) Ratatui (Rust)
Style Architecture Elm (modèle, Init/Update/View) Mode immédiat (vous gérez la boucle, dessinez chaque frame)
Écosystème Bubbles, Lip Gloss, 10k+ applications, Crush 2 100+ crates, Netflix/OpenAI/AWS/Vercel
Meilleur pour Itération rapide, équipes Go, CLI Contrôle, performance, codebases Rust

Les deux sont des choix excellents ; la langue préférée et le niveau de structure souhaité par le framework devraient guider la décision.

Liens utiles

Ressources externes