La manière amusante, fonctionnelle et avec état de créer des applications de terminal. Un framework Go basé sur The Elm Architecture. Bubble Tea est bien adapté aux applications de terminaux simples et complexes, qu'elles soient en ligne, en fenêtre complète ou un mélange des deux.
Bubble Tea est utilisé en production et inclut un certain nombre de fonctionnalités et d'optimisations de performances que nous avons ajoutées en cours de route. Parmi ceux-ci se trouve un moteur de rendu standard basé sur le framerate, un moteur de rendu pour les régions défilables hautes performances qui fonctionne parallèlement au moteur de rendu principal et la prise en charge de la souris.
Pour commencer, consultez le didacticiel ci-dessous, les exemples, la documentation, les didacticiels vidéo et quelques ressources courantes.
N'oubliez pas de consulter Bubbles, une bibliothèque de composants d'interface utilisateur courants pour Bubble Tea.
Bubble Tea est basé sur les paradigmes de conception fonctionnelle de The Elm Architecture, qui fonctionne bien avec Go. C'est une manière agréable de créer des applications.
Ce didacticiel suppose que vous possédez une connaissance pratique de Go.
D'ailleurs, le code source non annoté de ce programme est disponible sur GitHub.
Pour ce tutoriel, nous établissons une liste de courses.
Pour commencer, nous allons définir notre package et importer quelques bibliothèques. Notre seule importation externe sera la bibliothèque Bubble Tea, que nous appellerons tea
en abrégé.
package main
import (
"fmt"
"os"
tea "github.com/charmbracelet/bubbletea"
)
Les programmes Bubble Tea sont composés d'un modèle qui décrit l'état de l'application et de trois méthodes simples sur ce modèle :
Commençons donc par définir notre modèle qui stockera l'état de notre application. Il peut s'agir de n'importe quel type, mais une struct
est généralement la plus logique.
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
}
Ensuite, nous définirons l'état initial de notre application. Dans ce cas, nous définissons une fonction pour renvoyer notre modèle initial, mais nous pourrions tout aussi bien définir le modèle initial en tant que variable ailleurs.
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 {}),
}
}
Ensuite, nous définissons la méthode Init
. Init
peut renvoyer un Cmd
qui pourrait effectuer des E/S initiales. Pour l’instant, nous n’avons pas besoin d’effectuer d’E/S, donc pour la commande, nous renverrons simplement nil
, ce qui se traduit par « aucune commande ».
func ( m model ) Init () tea. Cmd {
// Just return `nil`, which means "no I/O right now, please."
return nil
}
La prochaine étape est la méthode de mise à jour. La fonction de mise à jour est appelée lorsque « des choses se produisent ». Son travail consiste à examiner ce qui s'est passé et à renvoyer un modèle mis à jour en réponse. Il peut également renvoyer un Cmd
pour faire bouger davantage de choses, mais pour l'instant, ne vous inquiétez pas de cette partie.
Dans notre cas, lorsqu'un utilisateur appuie sur la flèche vers le bas, le travail de Update
consiste à remarquer que la flèche vers le bas a été enfoncée et à déplacer le curseur en conséquence (ou non).
Le « quelque chose s'est produit » se présente sous la forme d'un Msg
, qui peut être de n'importe quel type. Les messages sont le résultat de certaines E/S qui ont eu lieu, comme une pression sur une touche, un tick de minuterie ou une réponse d'un serveur.
Nous déterminons généralement le type de Msg
que nous avons reçu avec un commutateur de type, mais vous pouvez également utiliser une assertion de type.
Pour l'instant, nous nous concentrerons uniquement sur les messages tea.KeyMsg
, qui sont automatiquement envoyés à la fonction de mise à jour lorsque les touches sont enfoncées.
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
}
Vous avez peut-être remarqué que ctrl+c et q ci-dessus renvoient une commande tea.Quit
avec le modèle. Il s'agit d'une commande spéciale qui demande au runtime Bubble Tea de se fermer, quittant ainsi le programme.
Enfin, il est temps de rendre notre interface utilisateur. De toutes les méthodes, la vue est la plus simple. Nous examinons le modèle dans son état actuel et l'utilisons pour renvoyer une string
. Cette chaîne est notre interface utilisateur !
Étant donné que la vue décrit l'intégralité de l'interface utilisateur de votre application, vous n'avez pas à vous soucier de redessiner la logique et ce genre de choses. Bubble Tea s'en charge pour vous.
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
}
La dernière étape consiste simplement à exécuter notre programme. Nous passons notre modèle initial à tea.NewProgram
et le laissons déchirer :
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 )
}
}
Ce didacticiel couvre les bases de la création d'une interface utilisateur de terminal interactif, mais dans le monde réel, vous devrez également effectuer des E/S. Pour en savoir plus, consultez le didacticiel de commande. C'est assez simple.
Il existe également plusieurs exemples de Bubble Tea et, bien sûr, il existe Go Docs.
Étant donné que les applications Bubble Tea prennent le contrôle de stdin et stdout, vous devrez exécuter Delve en mode sans tête, puis vous y connecter :
# 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
Si vous ne fournissez pas explicitement l'indicateur --listen
, le port utilisé variera selon l'exécution, donc le transmettre rend le débogueur plus facile à utiliser à partir d'un script ou de l'EDI de votre choix.
De plus, nous transmettons --api-version=2
car Delve utilise par défaut la version 1 pour des raisons de compatibilité ascendante. Cependant, Delve recommande d'utiliser la version 2 pour tout nouveau développement et certains clients peuvent ne plus fonctionner avec la version 1. Pour plus d'informations, consultez la documentation Delve.
Vous ne pouvez pas vraiment vous connecter à la sortie standard avec Bubble Tea car votre TUI est occupé à l'occuper ! Vous pouvez cependant vous connecter à un fichier en incluant quelque chose comme ce qui suit avant de démarrer votre programme 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 ()
}
Pour voir ce qui est enregistré en temps réel, exécutez tail -f debug.log
pendant que vous exécutez votre programme dans une autre fenêtre.
Il existe plus de 8 000 applications créées avec Bubble Tea ! En voici une poignée.
Pour plus d'applications créées avec Bubble Tea, voir Charm & Friends. Y a-t-il quelque chose de cool que vous avez préparé avec Bubble Tea et que vous souhaitez partager ? Les PR sont les bienvenus !
Voir contribuer.
Nous aimerions connaître votre avis sur ce projet. N'hésitez pas à nous laisser un message !
Bubble Tea est basé sur les paradigmes de The Elm Architecture d'Evan Czaplicki et alia et de l'excellent go-tea de TJ Holowaychuk. Il s'inspire des nombreux grands Zeichenorientierte Benutzerschnittstellen des jours passés.
MIT
Une partie du charme.
Charm爱开源 • Charm aime l'open source • نحنُ نحب المصادر المفتوحة