Die unterhaltsame, funktionale und zustandsbehaftete Art, Terminal-Apps zu erstellen. Ein Go-Framework basierend auf The Elm Architecture. Bubble Tea eignet sich gut für einfache und komplexe Terminalanwendungen, entweder Inline, Vollfenster oder eine Mischung aus beidem.
Bubble Tea wird in der Produktion verwendet und umfasst eine Reihe von Funktionen und Leistungsoptimierungen, die wir im Laufe der Zeit hinzugefügt haben. Dazu gehören ein Standard-Framerate-basierter Renderer, ein Renderer für scrollbare Hochleistungsbereiche, der neben dem Hauptrenderer arbeitet, und Mausunterstützung.
Sehen Sie sich zum Einstieg das Tutorial unten, die Beispiele, die Dokumentation, die Video-Tutorials und einige allgemeine Ressourcen an.
Schauen Sie sich unbedingt Bubbles an, eine Bibliothek mit gängigen UI-Komponenten für Bubble Tea.
Bubble Tea basiert auf den funktionalen Designparadigmen von The Elm Architecture, die übrigens gut mit Go funktionieren. Es ist eine wunderbare Möglichkeit, Anwendungen zu erstellen.
Für dieses Tutorial wird davon ausgegangen, dass Sie über praktische Kenntnisse in Go verfügen.
Der nichtkommentierte Quellcode für dieses Programm ist übrigens auf GitHub verfügbar.
Für dieses Tutorial erstellen wir eine Einkaufsliste.
Zunächst definieren wir unser Paket und importieren einige Bibliotheken. Unser einziger externer Import wird die Bubble Tea-Bibliothek sein, die wir kurz tea
nennen.
package main
import (
"fmt"
"os"
tea "github.com/charmbracelet/bubbletea"
)
Bubble Tea-Programme bestehen aus einem Modell , das den Anwendungsstatus beschreibt, und drei einfachen Methoden für dieses Modell:
Beginnen wir also mit der Definition unseres Modells, das den Status unserer Anwendung speichert. Es kann jeder Typ sein, aber eine struct
ist normalerweise am sinnvollsten.
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
}
Als Nächstes definieren wir den Anfangszustand unserer Anwendung. In diesem Fall definieren wir eine Funktion, die unser Ausgangsmodell zurückgibt. Wir könnten das Ausgangsmodell jedoch genauso gut auch anderswo als Variable definieren.
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 {}),
}
}
Als nächstes definieren wir die Init
-Methode. Init
kann einen Cmd
zurückgeben, der einige anfängliche E/A ausführen könnte. Im Moment müssen wir keine E/A durchführen, daher geben wir für den Befehl einfach nil
zurück, was übersetzt „kein Befehl“ bedeutet.
func ( m model ) Init () tea. Cmd {
// Just return `nil`, which means "no I/O right now, please."
return nil
}
Als nächstes kommt die Update-Methode. Die Update-Funktion wird aufgerufen, wenn „etwas passiert“. Seine Aufgabe besteht darin, zu untersuchen, was passiert ist, und als Antwort ein aktualisiertes Modell zurückzugeben. Es kann auch einen Cmd
zurückgeben, um weitere Dinge zu bewirken, aber machen Sie sich über diesen Teil vorerst keine Sorgen.
Wenn in unserem Fall ein Benutzer den Abwärtspfeil drückt, besteht die Aufgabe von Update
darin, zu bemerken, dass der Abwärtspfeil gedrückt wurde, und den Cursor entsprechend zu bewegen (oder auch nicht).
Die Meldung „Etwas ist passiert“ erfolgt in Form einer Msg
, die beliebiger Art sein kann. Nachrichten sind das Ergebnis einiger E/A-Vorgänge, die stattgefunden haben, z. B. eines Tastendrucks, eines Timer-Ticks oder einer Antwort eines Servers.
Normalerweise ermitteln wir mit einem Typwechsel, welche Art von Msg
wir erhalten haben, Sie können aber auch eine Typzusicherung verwenden.
Im Moment beschäftigen wir uns nur mit tea.KeyMsg
-Nachrichten, die beim Drücken von Tasten automatisch an die Update-Funktion gesendet werden.
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
}
Möglicherweise ist Ihnen aufgefallen, dass Strg+C und Q oben einen Befehl tea.Quit
mit dem Modell zurückgeben. Dabei handelt es sich um einen speziellen Befehl, der die Bubble Tea-Laufzeitumgebung anweist, das Programm zu beenden.
Endlich ist es Zeit, unsere Benutzeroberfläche zu rendern. Von allen Methoden ist die Ansicht die einfachste. Wir betrachten das Modell in seinem aktuellen Zustand und verwenden es, um einen string
zurückzugeben. Diese Zeichenfolge ist unsere Benutzeroberfläche!
Da die Ansicht die gesamte Benutzeroberfläche Ihrer Anwendung beschreibt, müssen Sie sich keine Gedanken über das Neuzeichnen der Logik und ähnliches machen. Bubble Tea übernimmt das für Sie.
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
}
Der letzte Schritt besteht darin, einfach unser Programm auszuführen. Wir übergeben unser ursprüngliches Modell an tea.NewProgram
und lassen es los:
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 )
}
}
Dieses Tutorial behandelt die Grundlagen zum Erstellen einer interaktiven Terminal-Benutzeroberfläche, aber in der realen Welt müssen Sie auch E/A durchführen. Um mehr darüber zu erfahren, schauen Sie sich das Command-Tutorial an. Es ist ziemlich einfach.
Es gibt auch mehrere Bubble Tea-Beispiele und natürlich Go Docs.
Da Bubble Tea-Apps die Kontrolle über stdin und stdout übernehmen, müssen Sie delve im Headless-Modus ausführen und dann eine Verbindung herstellen:
# 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
Wenn Sie das Flag --listen
nicht explizit angeben, variiert der verwendete Port je nach Ausführung. Wenn Sie dies also übergeben, ist die Verwendung des Debuggers über ein Skript oder die IDE Ihrer Wahl einfacher.
Darüber hinaus übergeben wir --api-version=2
, da delve aus Gründen der Abwärtskompatibilität standardmäßig Version 1 verwendet. Delve empfiehlt jedoch die Verwendung von Version 2 für alle Neuentwicklungen und einige Clients funktionieren möglicherweise nicht mehr mit Version 1. Weitere Informationen finden Sie in der Delve-Dokumentation.
Sie können sich mit Bubble Tea nicht wirklich auf Standard anmelden, weil Ihre TUI damit beschäftigt ist, das zu belegen! Sie können sich jedoch in einer Datei anmelden, indem Sie vor dem Starten Ihres Bubble Tea-Programms Folgendes einfügen:
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 ()
}
Um in Echtzeit zu sehen, was protokolliert wird, führen Sie tail -f debug.log
aus, während Sie Ihr Programm in einem anderen Fenster ausführen.
Es gibt über 8.000 Anwendungen, die mit Bubble Tea erstellt wurden! Hier sind eine Handvoll davon.
Weitere mit Bubble Tea erstellte Anwendungen finden Sie unter Charm & Friends. Gibt es etwas Cooles, das Sie mit Bubble Tea zubereitet haben und das Sie mit anderen teilen möchten? PRs sind willkommen!
Siehe Mitwirken.
Wir würden gerne Ihre Meinung zu diesem Projekt hören. Schreiben Sie uns gerne eine Nachricht!
Bubble Tea basiert auf den Paradigmen von The Elm Architecture von Evan Czaplicki und anderen und dem hervorragenden Go-Tea von TJ Holowaychuk. Es ist von den vielen großartigen zeichenorientierten Benutzerschnittstellen vergangener Tage inspiriert.
MIT
Ein Teil von Charme.
Charm-Inhalte • Charm liebt Open Source • Mehr Informationen