A maneira divertida, funcional e com estado de criar aplicativos de terminal. Uma estrutura Go baseada na arquitetura Elm. Bubble Tea é adequado para aplicações de terminal simples e complexas, seja em linha, em janela inteira ou uma mistura de ambos.
Bubble Tea está em uso na produção e inclui vários recursos e otimizações de desempenho que adicionamos ao longo do caminho. Entre eles está um renderizador padrão baseado em taxa de quadros, um renderizador para regiões roláveis de alto desempenho que funciona junto com o renderizador principal e suporte para mouse.
Para começar, veja o tutorial abaixo, os exemplos, a documentação, os tutoriais em vídeo e alguns recursos comuns.
Não deixe de conferir Bubbles, uma biblioteca de componentes de UI comuns para Bubble Tea.
Bubble Tea é baseado nos paradigmas de design funcional da The Elm Architecture, que funciona muito bem com Go. É uma maneira deliciosa de criar aplicativos.
Este tutorial pressupõe que você tenha conhecimento prático de Go.
A propósito, o código-fonte não anotado deste programa está disponível no GitHub.
Para este tutorial, estamos fazendo uma lista de compras.
Para começar vamos definir nosso pacote e importar algumas bibliotecas. Nossa única importação externa será a biblioteca Bubble Tea, que chamaremos de tea
.
package main
import (
"fmt"
"os"
tea "github.com/charmbracelet/bubbletea"
)
Os programas Bubble Tea são compostos por um modelo que descreve o estado do aplicativo e três métodos simples nesse modelo:
Então, vamos começar definindo nosso modelo que armazenará o estado de nossa aplicação. Pode ser de qualquer tipo, mas uma struct
geralmente faz mais sentido.
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
}
A seguir, definiremos o estado inicial da nossa aplicação. Neste caso, estamos definindo uma função para retornar nosso modelo inicial; no entanto, poderíamos facilmente definir o modelo inicial como uma variável em outro lugar também.
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 {}),
}
}
A seguir, definimos o método Init
. Init
pode retornar um Cmd
que pode realizar alguma E/S inicial. Por enquanto, não precisamos fazer nenhuma E/S, portanto, para o comando, retornaremos apenas nil
, que se traduz em "nenhum comando".
func ( m model ) Init () tea. Cmd {
// Just return `nil`, which means "no I/O right now, please."
return nil
}
O próximo é o método de atualização. A função de atualização é chamada quando “coisas acontecem”. Sua função é observar o que aconteceu e retornar um modelo atualizado em resposta. Ele também pode retornar um Cmd
para fazer mais coisas acontecerem, mas por enquanto não se preocupe com essa parte.
No nosso caso, quando um usuário pressiona a seta para baixo, o trabalho de Update
é perceber que a seta para baixo foi pressionada e mover o cursor de acordo (ou não).
O “algo aconteceu” vem na forma de uma Msg
, que pode ser de qualquer tipo. As mensagens são o resultado de alguma E/S que ocorreu, como um pressionamento de tecla, um tique do cronômetro ou uma resposta de um servidor.
Geralmente descobrimos que tipo de Msg
recebemos com uma opção de tipo, mas você também pode usar uma asserção de tipo.
Por enquanto, lidaremos apenas com mensagens tea.KeyMsg
, que são enviadas automaticamente para a função de atualização quando as teclas são pressionadas.
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
}
Você deve ter notado que ctrl+c eq acima retornam um comando tea.Quit
com o modelo. Esse é um comando especial que instrui o tempo de execução do Bubble Tea a encerrar, saindo do programa.
Finalmente, é hora de renderizar nossa UI. De todos os métodos, a visualização é o mais simples. Observamos o modelo em seu estado atual e o usamos para retornar uma string
. Essa string é nossa UI!
Como a visualização descreve toda a UI do seu aplicativo, você não precisa se preocupar em redesenhar a lógica e coisas assim. Bubble Tea cuida disso para você.
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
}
A última etapa é simplesmente executar nosso programa. Passamos nosso modelo inicial para tea.NewProgram
e deixamos ele funcionar:
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 )
}
}
Este tutorial cobre os fundamentos da construção de uma UI de terminal interativo, mas no mundo real você também precisará realizar E/S. Para saber mais sobre isso, dê uma olhada no Tutorial de Comandos. É muito simples.
Existem também vários exemplos de Bubble Tea disponíveis e, claro, Go Docs.
Como os aplicativos Bubble Tea assumem o controle de stdin e stdout, você precisará executar o delve no modo headless e conectar-se a ele:
# 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
Se você não fornecer explicitamente o sinalizador --listen
, a porta usada variará por execução, portanto, passá-la torna o depurador mais fácil de usar a partir de um script ou do IDE de sua escolha.
Além disso, passamos --api-version=2
porque o padrão do delve é a versão 1 por motivos de compatibilidade com versões anteriores. No entanto, o delve recomenda usar a versão 2 para todos os novos desenvolvimentos e alguns clientes podem não funcionar mais com a versão 1. Para obter mais informações, consulte a documentação do Delve.
Você realmente não pode fazer logon no stdout com Bubble Tea porque sua TUI está ocupada ocupando isso! Você pode, no entanto, fazer login em um arquivo incluindo algo como o seguinte antes de iniciar seu programa 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 ()
}
Para ver o que está sendo registrado em tempo real, execute tail -f debug.log
enquanto executa seu programa em outra janela.
Existem mais de 8 mil aplicativos desenvolvidos com Bubble Tea! Aqui estão alguns deles.
Para mais aplicativos criados com Bubble Tea, consulte Charm & Friends. Há algo legal que você fez com Bubble Tea e deseja compartilhar? PRs são bem-vindos!
Veja contribuindo.
Adoraríamos ouvir sua opinião sobre este projeto. Sinta-se à vontade para nos deixar uma mensagem!
Bubble Tea é baseado nos paradigmas de The Elm Architecture de Evan Czaplicki e outros e no excelente go-tea de TJ Holowaychuk. É inspirado nos muitos grandes Zeichenorientierte Benutzerschnittstellen do passado.
MIT
Parte do charme.
Charm热爱开源 • Charm adora código aberto • نحنُ نحب المصادر المفتوحة