diff --git a/README.md b/README.md index 50c4c6e89c..3811bc135c 100644 --- a/README.md +++ b/README.md @@ -89,34 +89,27 @@ type model struct { } ``` -### Initialization +## Initialization -Next, we’ll define our application’s initial state. In this case, we’re defining -a function to return our initial model, however, we could just as easily define -the initial model as a variable elsewhere, too. +Next, we’ll define our application’s initial state in the `Init` method. `Init` +can return a `Cmd` that could perform some initial I/O. For now, we don't need +to do any I/O, so for the command, we'll just return `nil`, which translates to +"no command." ```go -func initialModel() model { - return model{ +func (m model) Init() (tea.Model, tea.Cmd) { + m = { // 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 + // the map like a mathematical set. The keys refer to the indexes // of the `choices` slice, above. selected: make(map[int]struct{}), } -} -``` - -Next, we define the `Init` method. `Init` can return a `Cmd` that could perform -some initial I/O. For now, we don't need to do any I/O, so for the command, -we'll just return `nil`, which translates to "no command." -```go -func (m model) Init() tea.Cmd { // Just return `nil`, which means "no I/O right now, please." - return nil + return m, nil } ``` @@ -137,15 +130,15 @@ tick, or a response from a server. We usually figure out which type of `Msg` we received with a type switch, but you could also use a type assertion. -For now, we'll just deal with `tea.KeyMsg` messages, which are automatically -sent to the update function when keys are pressed. +For now, we'll just deal with `tea.KeyPressMsg` messages, which are +automatically sent to the update function when keys are pressed. ```go func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { // Is it a key press? - case tea.KeyMsg: + case tea.KeyPressMsg: // Cool, what was the actual key pressed? switch msg.String() { @@ -166,9 +159,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 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", " ": + // The "enter" key and the space bar (a literal space) toggle the + // selected state for the item that the cursor is pointing at. + case "enter", "space": _, ok := m.selected[m.cursor] if ok { delete(m.selected, m.cursor) diff --git a/tutorials/basics/README.md b/tutorials/basics/README.md index 2d6ed6b693..66272e4668 100644 --- a/tutorials/basics/README.md +++ b/tutorials/basics/README.md @@ -1,5 +1,4 @@ -Bubble Tea Basics -================= +# Bubble Tea Basics Bubble Tea is based on the functional design paradigms of [The Elm Architecture][elm], which happens to work nicely with Go. It's a delightful way @@ -11,7 +10,7 @@ By the way, the non-annotated source code for this program is available [on GitHub][tut-source]. [elm]: https://guide.elm-lang.org/architecture/ -[tut-source]:https://github.com/charmbracelet/bubbletea/tree/master/tutorials/basics +[tut-source]: https://github.com/charmbracelet/bubbletea/tree/master/tutorials/basics ## Enough! Let's get to it. @@ -34,9 +33,9 @@ import ( Bubble Tea programs are comprised of a **model** that describes the application state and three simple methods on that model: -* **Init**, a function that returns an initial command for the application to run. -* **Update**, a function that handles incoming events and updates the model accordingly. -* **View**, a function that renders the UI based on the data in the model. +- **Init**, a function that returns an initial command for the application to run. +- **Update**, a function that handles incoming events and updates the model accordingly. +- **View**, a function that renders the UI based on the data in the model. ## The Model @@ -53,13 +52,14 @@ type model struct { ## Initialization -Next, we’ll define our application’s initial state. In this case, we’re defining -a function to return our initial model, however, we could just as easily define -the initial model as a variable elsewhere, too. +Next, we’ll define our application’s initial state in the `Init` method. `Init` +can return a `Cmd` that could perform some initial I/O. For now, we don't need +to do any I/O, so for the command, we'll just return `nil`, which translates to +"no command." ```go -func initialModel() model { - return model{ +func (m model) Init() (tea.Model, tea.Cmd) { + m = { // Our to-do list is a grocery list choices: []string{"Buy carrots", "Buy celery", "Buy kohlrabi"}, @@ -68,17 +68,9 @@ func initialModel() model { // of the `choices` slice, above. selected: make(map[int]struct{}), } -} -``` - -Next, we define the `Init` method. `Init` can return a `Cmd` that could perform -some initial I/O. For now, we don't need to do any I/O, so for the command, -we'll just return `nil`, which translates to "no command." -```go -func (m model) Init() (tea.Model, tea.Cmd) { // Just return `nil`, which means "no I/O right now, please." - return nil + return m, nil } ``` @@ -99,15 +91,15 @@ tick, or a response from a server. We usually figure out which type of `Msg` we received with a type switch, but you could also use a type assertion. -For now, we'll just deal with `tea.KeyMsg` messages, which are automatically -sent to the update function when keys are pressed. +For now, we'll just deal with `tea.KeyPressMsg` messages, which are +automatically sent to the update function when keys are pressed. ```go func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { // Is it a key press? - case tea.KeyMsg: + case tea.KeyPressMsg: // Cool, what was the actual key pressed? switch msg.String() { @@ -128,9 +120,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 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", " ": + // The enter key and the space bar toggle the selected state for the + // item that the cursor is pointing at. + case "enter", "space": _, ok := m.selected[m.cursor] if ok { delete(m.selected, m.cursor) @@ -194,12 +186,12 @@ func (m model) View() string { ## All Together Now -The last step is to simply run our program. We pass our initial model to +The last step is to simply run our program. We pass and empty model `tea.NewProgram` and let it rip: ```go func main() { - p := tea.NewProgram(initialModel()) + p := tea.NewProgram(model{}) if _, err := p.Run(); err != nil { fmt.Printf("Alas, there's been an error: %v", err) os.Exit(1) @@ -222,18 +214,18 @@ there are [Go Docs][docs]. ## Additional Resources -* [Libraries we use with Bubble Tea](https://github.com/charmbracelet/bubbletea/#libraries-we-use-with-bubble-tea) -* [Bubble Tea in the Wild](https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild) +- [Libraries we use with Bubble Tea](https://github.com/charmbracelet/bubbletea/#libraries-we-use-with-bubble-tea) +- [Bubble Tea in the Wild](https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild) ### Feedback We'd love to hear your thoughts on this tutorial. Feel free to drop us a note! -* [Twitter](https://twitter.com/charmcli) -* [The Fediverse](https://mastodon.social/@charmcli) -* [Discord](https://charm.sh/chat) +- [Twitter](https://twitter.com/charmcli) +- [The Fediverse](https://mastodon.social/@charmcli) +- [Discord](https://charm.sh/chat) -*** +--- Part of [Charm](https://charm.sh). diff --git a/tutorials/basics/main.go b/tutorials/basics/main.go index 6417b2e8e6..babe20d013 100644 --- a/tutorials/basics/main.go +++ b/tutorials/basics/main.go @@ -13,8 +13,8 @@ type model struct { selected map[int]struct{} } -func initialModel() model { - return model{ +func (m model) Init() (tea.Model, tea.Cmd) { + m = model{ choices: []string{"Buy carrots", "Buy celery", "Buy kohlrabi"}, // A map which indicates which choices are selected. We're using @@ -22,15 +22,12 @@ func initialModel() model { // of the `choices` slice, above. selected: make(map[int]struct{}), } -} - -func (m model) Init() (tea.Model, tea.Cmd) { return m, tea.SetWindowTitle("Grocery List") } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { - case tea.KeyMsg: + case tea.KeyPressMsg: switch msg.String() { case "ctrl+c", "q": return m, tea.Quit @@ -42,7 +39,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.cursor < len(m.choices)-1 { m.cursor++ } - case "enter", " ": + case "enter", "space": _, ok := m.selected[m.cursor] if ok { delete(m.selected, m.cursor) @@ -78,7 +75,7 @@ func (m model) View() string { } func main() { - p := tea.NewProgram(initialModel()) + p := tea.NewProgram(model{}) if _, err := p.Run(); err != nil { fmt.Printf("Alas, there's been an error: %v", err) os.Exit(1) diff --git a/tutorials/commands/README.md b/tutorials/commands/README.md index 1d2d700ad6..f6441f9f12 100644 --- a/tutorials/commands/README.md +++ b/tutorials/commands/README.md @@ -1,5 +1,4 @@ -Commands in Bubble Tea -====================== +# Commands in Bubble Tea This is the second tutorial for Bubble Tea covering commands, which deal with I/O. The tutorial assumes you have a working knowledge of Go and a decent @@ -92,8 +91,8 @@ Note that we don't call the function; the Bubble Tea runtime will do that when the time is right. ```go -func (m model) Init() (tea.Cmd) { - return checkServer +func (m model) Init() (tea.Model, tea.Cmd) { + return m, checkServer } ``` @@ -122,13 +121,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.err = msg return m, tea.Quit - case tea.KeyMsg: + case tea.KeyPressMsg: // Ctrl+c exits. Even with short running programs it's good to have // a quit key, just in case your logic is off. Users will be very // annoyed if they can't exit. - if msg.Type == tea.KeyCtrlC { - return m, tea.Quit - } + if msg.Mod == tea.ModCtrl && msg.Code == 'c' { + return m, tea.Quit + } } // If we happen to get any other messages, don't do anything. @@ -227,18 +226,18 @@ And, of course, check out the [Go Docs][docs]. ## Additional Resources -* [Libraries we use with Bubble Tea](https://github.com/charmbracelet/bubbletea/#libraries-we-use-with-bubble-tea) -* [Bubble Tea in the Wild](https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild) +- [Libraries we use with Bubble Tea](https://github.com/charmbracelet/bubbletea/#libraries-we-use-with-bubble-tea) +- [Bubble Tea in the Wild](https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild) ### Feedback We'd love to hear your thoughts on this tutorial. Feel free to drop us a note! -* [Twitter](https://twitter.com/charmcli) -* [The Fediverse](https://mastodon.social/@charmcli) -* [Discord](https://charm.sh/chat) +- [Twitter](https://twitter.com/charmcli) +- [The Fediverse](https://mastodon.social/@charmcli) +- [Discord](https://charm.sh/chat) -*** +--- Part of [Charm](https://charm.sh). diff --git a/tutorials/commands/main.go b/tutorials/commands/main.go index e1f5a52a11..bd54bfaac9 100644 --- a/tutorials/commands/main.go +++ b/tutorials/commands/main.go @@ -49,8 +49,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.err = msg return m, tea.Quit - case tea.KeyMsg: - if msg.String() == "ctrl+c" { + case tea.KeyPressMsg: + if msg.Mod == tea.ModCtrl && msg.Code == 'c' { return m, tea.Quit } }