diff --git a/examples/fullscreen/main.go b/examples/fullscreen/main.go index e8486d8..0a7e293 100644 --- a/examples/fullscreen/main.go +++ b/examples/fullscreen/main.go @@ -16,7 +16,7 @@ type model int type tickMsg time.Time func main() { - p := tea.NewProgram(initialize, update, view) + p := tea.NewProgram(model(5)) p.EnterAltScreen() err := p.Start() @@ -27,13 +27,11 @@ func main() { } } -func initialize() (tea.Model, tea.Cmd) { - return model(5), tick() +func (m model) Init() tea.Cmd { + return tick() } -func update(message tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { - m, _ := mdl.(model) - +func (m model) Update(message tea.Msg) (tea.Model, tea.Cmd) { switch msg := message.(type) { case tea.KeyMsg: @@ -58,8 +56,7 @@ func update(message tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { return m, nil } -func view(mdl tea.Model) string { - m, _ := mdl.(model) +func (m model) View() string { return fmt.Sprintf("\n\n Hi. This program will exit in %d seconds...", m) } diff --git a/examples/http/main.go b/examples/http/main.go index c79a76a..a02f4db 100644 --- a/examples/http/main.go +++ b/examples/http/main.go @@ -3,7 +3,6 @@ package main // A simple program that makes a GET request and prints the response status. import ( - "errors" "fmt" "log" "net/http" @@ -14,31 +13,29 @@ import ( const url = "https://charm.sh/" -type Model struct { +type model struct { status int err error } type statusMsg int -type errMsg error + +type errMsg struct{ error } + +func (e errMsg) Error() string { return e.Error() } func main() { - p := tea.NewProgram(initialize, update, view) + p := tea.NewProgram(model{}) if err := p.Start(); err != nil { log.Fatal(err) } } -func initialize() (tea.Model, tea.Cmd) { - return Model{0, nil}, checkServer +func (m model) Init() tea.Cmd { + return checkServer } -func update(msg tea.Msg, model tea.Model) (tea.Model, tea.Cmd) { - m, ok := model.(Model) - if !ok { - return Model{err: errors.New("could not perform assertion on model during update")}, nil - } - +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: @@ -66,8 +63,7 @@ func update(msg tea.Msg, model tea.Model) (tea.Model, tea.Cmd) { } } -func view(model tea.Model) string { - m, _ := model.(Model) +func (m model) View() string { s := fmt.Sprintf("Checking %s...", url) if m.err != nil { s += fmt.Sprintf("something went wrong: %s", m.err) @@ -83,7 +79,7 @@ func checkServer() tea.Msg { } res, err := c.Get(url) if err != nil { - return errMsg(err) + return errMsg{err} } return statusMsg(res.StatusCode) } diff --git a/examples/pager/main.go b/examples/pager/main.go index abf4e29..9cf5454 100644 --- a/examples/pager/main.go +++ b/examples/pager/main.go @@ -53,7 +53,7 @@ func main() { defer f.Close() } - p := tea.NewProgram(initialize(string(content)), update, view) + p := tea.NewProgram(model{content: string(content)}) // Use the full size of the terminal in its "alternate screen buffer" p.EnterAltScreen() @@ -75,19 +75,11 @@ type model struct { viewport viewport.Model } -func initialize(content string) func() (tea.Model, tea.Cmd) { - return func() (tea.Model, tea.Cmd) { - return model{ - // Store content in the model so we can hand it off to the viewport - // later. - content: content, - }, nil - } +func (m model) Init() tea.Cmd { + return nil } -func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { - m, _ := mdl.(model) - +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var ( cmd tea.Cmd cmds []tea.Cmd @@ -142,9 +134,7 @@ func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { return m, tea.Batch(cmds...) } -func view(mdl tea.Model) string { - m, _ := mdl.(model) - +func (m model) View() string { if !m.ready { return "\n Initalizing..." } diff --git a/examples/result/main.go b/examples/result/main.go index 9f74593..8dd55b1 100644 --- a/examples/result/main.go +++ b/examples/result/main.go @@ -29,7 +29,8 @@ func main() { // Pass the channel to the initialize function so our Bubble Tea program // can send the final choice along when the time comes. - if err := tea.NewProgram(initialize(result), update, view).Start(); err != nil { + p := tea.NewProgram(model{cursor: 0, choice: result}) + if err := p.Start(); err != nil { fmt.Println("Oh no:", err) os.Exit(1) } @@ -43,15 +44,15 @@ func main() { // Pass a channel to the model to listen to the result value. This is a // function that returns the initialize function and is typically how you would // pass arguments to a tea.Init function. -func initialize(choice chan string) func() (tea.Model, tea.Cmd) { - return func() (tea.Model, tea.Cmd) { - return model{cursor: 0, choice: choice}, nil - } +func initialModel(choice chan string) model { + return model{cursor: 0, choice: choice} } -func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { - m, _ := mdl.(model) +func (m model) Init() tea.Cmd { + return nil +} +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { @@ -83,9 +84,7 @@ func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { return m, nil } -func view(mdl tea.Model) string { - m, _ := mdl.(model) - +func (m model) View() string { s := strings.Builder{} s.WriteString("What kind of Bubble Tea would you like to order?\n\n") diff --git a/examples/simple/main.go b/examples/simple/main.go index bd0bd57..1756a92 100644 --- a/examples/simple/main.go +++ b/examples/simple/main.go @@ -27,10 +27,6 @@ func main() { } } -// Messages are events that we respond to in our Update function. This -// particular one indicates that the timer has ticked. -type tickMsg time.Time - // A model can be more or less any type of data. It holds all the data for a // program, so often it's a struct. For this simple example, however, all // we'll need is a simple integer. @@ -66,6 +62,10 @@ func (m model) View() string { return fmt.Sprintf("Hi. This program will exit in %d seconds. To quit sooner press any key.\n", m) } +// Messages are events that we respond to in our Update function. This +// particular one indicates that the timer has ticked. +type tickMsg time.Time + func tick() tea.Msg { time.Sleep(time.Second) return tickMsg{} diff --git a/examples/spinner/main.go b/examples/spinner/main.go index e54356c..3fe3ef9 100644 --- a/examples/spinner/main.go +++ b/examples/spinner/main.go @@ -12,41 +12,35 @@ import ( "github.com/muesli/termenv" ) -var ( - color = termenv.ColorProfile() -) +var term = termenv.ColorProfile() -type Model struct { +type errMsg error + +type model struct { spinner spinner.Model quitting bool err error } -type errMsg error - func main() { - p := tea.NewProgram(initialize, update, view) + p := tea.NewProgram(initialModel()) if err := p.Start(); err != nil { fmt.Println(err) os.Exit(1) } } -func initialize() (tea.Model, tea.Cmd) { +func initialModel() model { s := spinner.NewModel() s.Frames = spinner.Dot - - return Model{ - spinner: s, - }, spinner.Tick(s) + return model{spinner: s} } -func update(msg tea.Msg, model tea.Model) (tea.Model, tea.Cmd) { - m, ok := model.(Model) - if !ok { - return model, nil - } +func (m model) Init() tea.Cmd { + return spinner.Tick(m.spinner) +} +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: @@ -74,17 +68,13 @@ func update(msg tea.Msg, model tea.Model) (tea.Model, tea.Cmd) { } -func view(model tea.Model) string { - m, ok := model.(Model) - if !ok { - return "could not perform assertion on model in view\n" - } +func (m model) View() string { if m.err != nil { return m.err.Error() } s := termenv. String(spinner.View(m.spinner)). - Foreground(color.Color("205")). + Foreground(term.Color("205")). String() str := fmt.Sprintf("\n\n %s Loading forever...press q to quit\n\n", s) if m.quitting { diff --git a/examples/textinput/main.go b/examples/textinput/main.go index be6c5ab..528dd66 100644 --- a/examples/textinput/main.go +++ b/examples/textinput/main.go @@ -4,7 +4,6 @@ package main // component library. import ( - "errors" "fmt" "log" @@ -12,48 +11,39 @@ import ( tea "github.com/charmbracelet/bubbletea" ) -type Model struct { - textInput input.Model - err error -} - -type tickMsg struct{} -type errMsg error - func main() { - p := tea.NewProgram( - initialize, - update, - view, - ) + p := tea.NewProgram(initialModel()) if err := p.Start(); err != nil { log.Fatal(err) } } -func initialize() (tea.Model, tea.Cmd) { +type tickMsg struct{} +type errMsg error + +type model struct { + textInput input.Model + err error +} + +func initialModel() model { inputModel := input.NewModel() inputModel.Placeholder = "Pikachu" inputModel.Focus() - return Model{ + return model{ textInput: inputModel, err: nil, - }, input.Blink(inputModel) + } } -func update(msg tea.Msg, model tea.Model) (tea.Model, tea.Cmd) { +func (m model) Init() tea.Cmd { + return input.Blink(m.textInput) +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd - m, ok := model.(Model) - if !ok { - // When we encounter errors in Update we simply add the error to the - // model so we can handle it in the view. We could also return a command - // that does something else with the error, like logs it via IO. - return Model{ - err: errors.New("could not perform assertion on model in update"), - }, nil - } switch msg := msg.(type) { case tea.KeyMsg: @@ -76,13 +66,7 @@ func update(msg tea.Msg, model tea.Model) (tea.Model, tea.Cmd) { return m, cmd } -func view(model tea.Model) string { - m, ok := model.(Model) - if !ok { - return "Oh no: could not perform assertion on model." - } else if m.err != nil { - return fmt.Sprintf("Uh oh: %s", m.err) - } +func (m model) View() string { return fmt.Sprintf( "What’s your favorite Pokémon?\n\n%s\n\n%s", input.View(m.textInput), diff --git a/examples/textinputs/main.go b/examples/textinputs/main.go index 40ae18f..15b06a9 100644 --- a/examples/textinputs/main.go +++ b/examples/textinputs/main.go @@ -12,9 +12,7 @@ import ( te "github.com/muesli/termenv" ) -const ( - focusedTextColor = "205" -) +const focusedTextColor = "205" var ( color = te.ColorProfile().Color @@ -25,11 +23,7 @@ var ( ) func main() { - if err := tea.NewProgram( - initialize, - update, - view, - ).Start(); err != nil { + if err := tea.NewProgram(initialModel()).Start(); err != nil { fmt.Printf("could not start program: %s\n", err) os.Exit(1) } @@ -43,7 +37,7 @@ type model struct { submitButton string } -func initialize() (tea.Model, tea.Cmd) { +func initialModel() model { name := input.NewModel() name.Placeholder = "Name" name.Focus() @@ -58,25 +52,21 @@ func initialize() (tea.Model, tea.Cmd) { email.Placeholder = "Email" email.Prompt = blurredPrompt - return model{0, name, nickName, email, blurredSubmitButton}, - tea.Batch( - input.Blink(name), - input.Blink(nickName), - input.Blink(email), - ) + return model{0, name, nickName, email, blurredSubmitButton} } +func (m model) Init() tea.Cmd { + return tea.Batch( + input.Blink(m.nameInput), + input.Blink(m.nickNameInput), + input.Blink(m.emailInput), + ) +} -func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { - m, ok := mdl.(model) - if !ok { - panic("could not perform assertion on model") - } - +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { - case tea.KeyMsg: switch msg.String() { @@ -167,12 +157,7 @@ func updateInputs(msg tea.Msg, m model) (model, tea.Cmd) { return m, tea.Batch(cmds...) } -func view(mdl tea.Model) string { - m, ok := mdl.(model) - if !ok { - return "could not perform assertion on model" - } - +func (m model) View() string { s := "\n" inputs := []string{ @@ -189,6 +174,5 @@ func view(mdl tea.Model) string { } s += "\n\n" + m.submitButton + "\n" - return s }