Веселый, функциональный и сохраняющий состояние способ создания терминальных приложений. Фреймворк Go, основанный на архитектуре Elm. Bubble Tea хорошо подходит для простых и сложных терминальных приложений: встроенных, полнооконных или их комбинации.
Bubble Tea используется в производстве и включает в себя ряд функций и оптимизаций производительности, которые мы добавили в ходе разработки. Среди них — стандартный рендерер на основе частоты кадров, рендерер для высокопроизводительных прокручиваемых областей, который работает вместе с основным рендерером, и поддержка мыши.
Чтобы начать, ознакомьтесь с руководством ниже, примерами, документацией, видеоуроками и некоторыми общими ресурсами.
Обязательно ознакомьтесь с Bubbles, библиотекой общих компонентов пользовательского интерфейса для Bubble Tea.
Bubble Tea основан на парадигмах функционального дизайна The Elm Architecture, которые прекрасно работают с Go. Это восхитительный способ создания приложений.
В этом руководстве предполагается, что у вас есть практические знания Go.
Кстати, неаннотированный исходный код этой программы доступен на GitHub.
В этом уроке мы составляем список покупок.
Для начала мы определим наш пакет и импортируем несколько библиотек. Нашим единственным внешним импортом будет библиотека Bubble Tea, которую мы для краткости будем называть tea
.
package main
import (
"fmt"
"os"
tea "github.com/charmbracelet/bubbletea"
)
Программы Bubble Tea состоят из модели , описывающей состояние приложения, и трех простых методов этой модели:
Итак, давайте начнем с определения нашей модели, которая будет хранить состояние нашего приложения. Это может быть любой тип, но struct
обычно имеет наибольший смысл.
type model struct {
choices [] string // items on the to-do list
cursor int // which to-do list item our cursor is pointing at
selected map [ int ] struct {} // which to-do items are selected
}
Далее мы определим исходное состояние нашего приложения. В этом случае мы определяем функцию, возвращающую нашу исходную модель, однако мы могли бы с тем же успехом определить исходную модель как переменную и в другом месте.
func initialModel () model {
return model {
// Our to-do list is a grocery list
choices : [] string { "Buy carrots" , "Buy celery" , "Buy kohlrabi" },
// A map which indicates which choices are selected. We're using
// the map like a mathematical set. The keys refer to the indexes
// of the `choices` slice, above.
selected : make ( map [ int ] struct {}),
}
}
Далее мы определяем метод Init
. Init
может вернуть Cmd
, который может выполнить некоторый начальный ввод-вывод. На данный момент нам не нужно выполнять какие-либо операции ввода-вывода, поэтому для команды мы просто вернем nil
, что переводится как «нет команды».
func ( m model ) Init () tea. Cmd {
// Just return `nil`, which means "no I/O right now, please."
return nil
}
Далее идет метод обновления. Функция обновления вызывается, когда «что-то происходит». Его задача — посмотреть, что произошло, и вернуть в ответ обновленную модель. Он также может вернуть Cmd
, чтобы сделать больше вещей, но пока не беспокойтесь об этой части.
В нашем случае, когда пользователь нажимает стрелку вниз, задача Update
состоит в том, чтобы заметить, что стрелка вниз была нажата, и соответствующим образом переместить курсор (или нет).
«Что-то произошло» приходит в форме Msg
, которое может быть любого типа. Сообщения являются результатом некоторых операций ввода-вывода, таких как нажатие клавиши, тик таймера или ответ сервера.
Обычно мы определяем, какой тип Msg
мы получили, с помощью переключателя типа, но вы также можете использовать утверждение типа.
Сейчас мы будем иметь дело только с сообщениями tea.KeyMsg
, которые автоматически отправляются в функцию обновления при нажатии клавиш.
func ( m model ) Update ( msg tea. Msg ) (tea. Model , tea. Cmd ) {
switch msg := msg .( type ) {
// Is it a key press?
case tea. KeyMsg :
// Cool, what was the actual key pressed?
switch msg . String () {
// These keys should exit the program.
case "ctrl+c" , "q" :
return m , tea . Quit
// The "up" and "k" keys move the cursor up
case "up" , "k" :
if m . cursor > 0 {
m . cursor --
}
// The "down" and "j" keys move the cursor down
case "down" , "j" :
if m . cursor < len ( m . choices ) - 1 {
m . cursor ++
}
// The "enter" key and the spacebar (a literal space) toggle
// the selected state for the item that the cursor is pointing at.
case "enter" , " " :
_ , ok := m . selected [ m . cursor ]
if ok {
delete ( m . selected , m . cursor )
} else {
m . selected [ m . cursor ] = struct {}{}
}
}
}
// Return the updated model to the Bubble Tea runtime for processing.
// Note that we're not returning a command.
return m , nil
}
Возможно, вы заметили, что ctrl+c и q выше возвращают команду tea.Quit
с моделью. Это специальная команда, которая дает команду среде выполнения Bubble Tea выйти из программы.
Наконец, пришло время визуализировать наш пользовательский интерфейс. Из всех способов вид является самым простым. Мы смотрим на модель в ее текущем состоянии и используем ее для возврата string
. Эта строка — наш пользовательский интерфейс!
Поскольку представление описывает весь пользовательский интерфейс вашего приложения, вам не нужно беспокоиться о перерисовке логики и тому подобном. Bubble Tea позаботится об этом за вас.
func ( m model ) View () string {
// The header
s := "What should we buy at the market? n n "
// Iterate over our choices
for i , choice := range m . choices {
// Is the cursor pointing at this choice?
cursor := " " // no cursor
if m . cursor == i {
cursor = ">" // cursor!
}
// Is this choice selected?
checked := " " // not selected
if _ , ok := m . selected [ i ]; ok {
checked = "x" // selected!
}
// Render the row
s += fmt . Sprintf ( "%s [%s] %s n " , cursor , checked , choice )
}
// The footer
s += " n Press q to quit. n "
// Send the UI for rendering
return s
}
Последний шаг — просто запустить нашу программу. Мы передаем нашу первоначальную модель в tea.NewProgram
и позволяем ей копироваться:
func main () {
p := tea . NewProgram ( initialModel ())
if _ , err := p . Run (); err != nil {
fmt . Printf ( "Alas, there's been an error: %v" , err )
os . Exit ( 1 )
}
}
В этом руководстве рассматриваются основы создания пользовательского интерфейса интерактивного терминала, но в реальном мире вам также потребуется выполнять ввод-вывод. Чтобы узнать об этом, посмотрите Учебное пособие по командам. Это довольно просто.
Также доступно несколько примеров Bubble Tea и, конечно же, Go Docs.
Поскольку приложения Bubble Tea берут на себя управление стандартным вводом и выводом, вам необходимо запустить delve в безголовом режиме, а затем подключиться к нему:
# Start the debugger
$ dlv debug --headless --api-version=2 --listen=127.0.0.1:43000 .
API server listening at: 127.0.0.1:43000
# Connect to it from another terminal
$ dlv connect 127.0.0.1:43000
Если вы не укажете явно флаг --listen
, используемый порт будет меняться в зависимости от запуска, поэтому его передача упрощает использование отладчика из сценария или выбранной вами IDE.
Кроме того, мы передаем --api-version=2
, поскольку по умолчанию delve использует версию 1 из соображений обратной совместимости. Однако delve рекомендует использовать версию 2 для всех новых разработок, а некоторые клиенты могут больше не работать с версией 1. Дополнительные сведения см. в документации Delve.
Вы не можете войти в стандартный вывод с помощью Bubble Tea, потому что ваш TUI занят этим! Однако вы можете войти в файл, включив перед запуском программы Bubble Tea что-то вроде следующего:
if len ( os . Getenv ( "DEBUG" )) > 0 {
f , err := tea . LogToFile ( "debug.log" , "debug" )
if err != nil {
fmt . Println ( "fatal:" , err )
os . Exit ( 1 )
}
defer f . Close ()
}
Чтобы увидеть, что регистрируется в реальном времени, запустите tail -f debug.log
одновременно запуская программу в другом окне.
С помощью Bubble Tea создано более 8 тысяч приложений! Вот несколько из них.
Дополнительные приложения, созданные с помощью Bubble Tea, см. в разделе «Очарование и друзья». Есть ли что-нибудь классное, что вы приготовили из Bubble Tea и чем бы хотели поделиться? Пиар приветствуется!
См. вклад.
Мы хотели бы услышать ваши мысли об этом проекте. Не стесняйтесь, напишите нам!
Bubble Tea основан на парадигмах «Архитектуры вяза» Эвана Чаплицкого и других и превосходного чая от Ти Джей Холовайчука. Он вдохновлен многими великими Zeichenorientierte Benutzerschnittstellen прошлых дней.
Массачусетский технологический институт
Часть очарования.
Charm爱开源 • Charm любит открытый исходный код • نحنُ المصادر المفتوحة