Интерфейс терминала: BubbleTea (Go) vs Ratatui (Rust)

Быстрый обзор фреймворков TUI в стиле Elm (Go) и immediate-mode (Rust)

Содержимое страницы

Два мощных варианта для создания терминальных пользовательских интерфейсов сегодня — это BubbleTea (Go) и Ratatui (Rust). Один предлагает вам opinionated-фреймворк в стиле Elm; другой — гибкую библиотеку immediate-mode.

Эта статья подводит итог тому, что такое каждый из них, показывает минимальные примеры для обоих и предлагает, когда выбирать тот или иной. И да, также дает несколько полезных ссылок.

Crush UI - реализован с использованием фреймворка BubbleTea

Crush UI (скриншот выше) реализован с использованием фреймворка BubbleTea.

Что такое BubbleTea?

BubbleTea — это фреймворк для Go, предназначенный для TUIs на основе The Elm Architecture. Вы описываете свое приложение с помощью модели (состояния) и трех компонентов: Init (начальная команда), Update (обработка сообщений, возврат новой модели и опциональной команды), и View (рендеринг интерфейса в виде строки). Фреймворк запускает цикл событий, преобразует нажатия клавиш и I/O в сообщения и перерисовывает интерфейс при изменении модели. Итак: что такое BubbleTea? Короче говоря, это веселый, состоятельный способ создания терминальных приложений на Go, с единственным источником истины и предсказуемыми обновлениями.

BubbleTea готова к производству (v1.x), имеет десятки тысяч звезд на GitHub и тысячи известных импортеров, и работает встроенной, полноэкранной или смешанной. Обычно его сочетают с Bubbles для компонентов (входы, viewport, спиннеры) и Lip Gloss для стилизации. Если вы создаете Go TUI, хороший первый шаг — следовать распространенной структуре Go-проектов, чтобы ваши cmd/ и пакеты оставались понятными по мере роста приложения.

Минимальная программа на BubbleTea выглядит так: модель, Init, Update (обработка сообщений клавиш) и View, возвращающая строку. Остальное делает рантайм.

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 := "Что нам купить?\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 + "\nНажмите q для выхода.\n"
}

func main() {
    if _, err := tea.NewProgram(model{
        choices:  []string{"Купить морковь", "Купить сельдерей", "Купить кольраби"},
        selected: make(map[int]struct{}),
    }).Run(); err != nil {
        fmt.Printf("ошибка: %v", err)
        os.Exit(1)
    }
}

Известные приложения, созданные с использованием BubbleTea, включают Crush (TUI-основанный AI-кодинговый агент Charm), Glow, Huh и многие инструменты из экосистемы самых популярных Go-проектов. Для более сложных Go-приложений вы можете добавить внедрение зависимостей и надежные модульные тесты; те же идеи применимы к моделям и командам BubbleTea.

Что такое Ratatui?

Ratatui — это библиотека для Rust, предназначенная для TUIs, использующая рендеринг в immediate-mode: каждый кадр вы описываете весь интерфейс (виджеты и макет), и Ratatui его рисует. Что такое Ratatui? Это легковесный, неопределяющий инструментарий — он не навязывает модель в стиле Elm или определенную структуру приложения. Вы сохраняете свое состояние, запускаете свой цикл событий (обычно с crossterm, termion, или termwiz), и вызываете terminal.draw(|f| { ... }) для рендеринга. Таким образом, разница между стилем Elm и immediate mode заключается именно в этом: в стиле Elm фреймворк владеет циклом, и вы реагируете через Update/View; в immediate mode вы владеете циклом и перерисовываете весь интерфейс из текущего состояния каждый кадр.

Ratatui используется в 2,100+ кратах и доверяется компаниями, такими как Netflix (например, bpftop), OpenAI, AWS (например, amazon-q-developer-cli), и Vercel. Версия 0.30.x является текущей, с сильной документацией и опциональными бэкендами. Это хороший выбор, когда вы хотите полный контроль над вводом и рендерингом, или когда вы уже в экосистеме Rust.

Минимальное приложение Ratatui: вы инициализируете терминал, запускаете цикл, который рисует и затем считывает события, и восстанавливаете терминал при выходе.

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("Привет, Ratatui! Нажмите q для выхода.").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(())
}

Итак: когда мне выбирать BubbleTea или Ratatui? Выбирайте BubbleTea, когда вы хотите самый быстрый путь к отполированному TUI на Go, с одной моделью и четким Update/View, и когда ваша команда или экосистема — это Go (например, вы уже используете Go ORMs или Ollama в Go). Выбирайте Ratatui, когда вам нужен максимальный контроль, вы работаете в Rust, или создаете TUIs с высокими требованиями к производительности или ограниченными ресурсами; его дизайн immediate-mode и опциональные бэкенды обеспечивают эту гибкость.

Сводка

Аспект BubbleTea (Go) Ratatui (Rust)
Стиль Архитектура Elm (модель, Init/Update/View) Immediate mode (вы владеете циклом, рисуете каждый кадр)
Экосистема Bubbles, Lip Gloss, 10k+ приложений, Crush 2,100+ кратов, Netflix/OpenAI/AWS/Vercel
Лучше всего для Быстрая итерация, команды Go, CLI Контроль, производительность, кодовые базы Rust

Оба варианта отличные; предпочтительный язык и сколько структуры мы хотим от фреймворка должны определять решение.

Полезные ссылки

Внешние ресурсы