{"owner":"charmbracelet","repo":"bubbletea","hasSkills":true,"totalSkillsCount":1,"totalTokensCount":1812,"categories":["command-prompt"],"hasMcp":false,"mcpConfig":null,"found":["tutorials/commands/README.md"],"skills":{"tutorials/commands/README.md":"# Commands in Bubble Tea\n\nThis is the second tutorial for Bubble Tea covering commands, which deal with\nI/O. The tutorial assumes you have a working knowledge of Go and a decent\nunderstanding of [the first tutorial][basics].\n\nYou can find the non-annotated version of this program [on GitHub][source].\n\n[basics]: https://github.com/charmbracelet/bubbletea/tree/main/tutorials/basics\n[source]: https://github.com/charmbracelet/bubbletea/blob/master/tutorials/commands/main.go\n\n## Let’s Go!\n\nFor this tutorial we’re building a very simple program that makes an HTTP\nrequest to a server and reports the status code of the response.\n\nWe’ll import a few necessary packages and put the URL we’re going to check in\na `const`.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n\n    tea \"charm.land/bubbletea/v2\"\n)\n\nconst url = \"https://charm.sh/\"\n```\n\n## The Model\n\nNext we’ll define our model. The only things we need to store are the status\ncode of the HTTP response and a possible error.\n\n```go\ntype model struct {\n    status int\n    err    error\n}\n```\n\n## Commands and Messages\n\n`Cmd`s are functions that perform some I/O and then return a `Msg`. Checking the\ntime, ticking a timer, reading from the disk, and network stuff are all I/O and\nshould be run through commands. That might sound harsh, but it will keep your\nBubble Tea program straightforward and simple.\n\nAnyway, let’s write a `Cmd` that makes a request to a server and returns the\nresult as a `Msg`.\n\n```go\nfunc checkServer() tea.Msg {\n\n    // Create an HTTP client and make a GET request.\n    c := &http.Client{Timeout: 10 * time.Second}\n    res, err := c.Get(url)\n\n    if err != nil {\n        // There was an error making our request. Wrap the error we received\n        // in a message and return it.\n        return errMsg{err}\n    }\n    // We received a response from the server. Return the HTTP status code\n    // as a message.\n    return statusMsg(res.StatusCode)\n}\n\ntype statusMsg int\n\ntype errMsg struct{ err error }\n\n// For messages that contain errors it's often handy to also implement the\n// error interface on the message.\nfunc (e errMsg) Error() string { return e.err.Error() }\n```\n\nAnd notice that we’ve defined two new `Msg` types. They can be any type, even\nan empty struct. We’ll come back to them later in our update function.\nFirst, let’s write our initialization function.\n\n## The Initialization Method\n\nThe initialization method is very simple: we return the `Cmd` we made earlier.\nNote that we don’t call the function; the Bubble Tea runtime will do that when\nthe time is right.\n\n```go\nfunc (m model) Init() tea.Cmd {\n    return checkServer\n}\n```\n\n## The Update Method\n\nInternally, `Cmd`s run asynchronously in a goroutine. The `Msg` they return is\ncollected and sent to our update function for handling. Remember those message\ntypes we made earlier when we were making the `checkServer` command? We handle\nthem here. This makes dealing with many asynchronous operations very easy.\n\n```go\nfunc (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {\n    switch msg := msg.(type) {\n\n    case statusMsg:\n        // The server returned a status message. Save it to our model. Also\n        // tell the Bubble Tea runtime we want to exit because we have nothing\n        // else to do. We'll still be able to render a final view with our\n        // status message.\n        m.status = int(msg)\n        return m, tea.Quit\n\n    case errMsg:\n        // There was an error. Note it in the model. And tell the runtime\n        // we're done and want to quit.\n        m.err = msg\n        return m, tea.Quit\n\n    case tea.KeyPressMsg:\n        // Ctrl+c exits. Even with short running programs it's good to have\n        // a quit key, just in case your logic is off. Users will be very\n        // annoyed if they can't exit.\n\t\tif msg.Mod == tea.ModCtrl && msg.Code == 'c' {\n\t\t\treturn m, tea.Quit\n\t\t}\n    }\n\n    // If we happen to get any other messages, don't do anything.\n    return m, nil\n}\n```\n\n## The View Function\n\nOur view is very straightforward. We look at the current model and build a\n`tea.View` accordingly:\n\n```go\nfunc (m model) View() tea.View {\n    // If there's an error, print it out and don't do anything else.\n    if m.err != nil {\n        return tea.NewView(fmt.Sprintf(\"\\nWe had some trouble: %v\\n\\n\", m.err))\n    }\n\n    // Tell the user we're doing something.\n    s := fmt.Sprintf(\"Checking %s ... \", url)\n\n    // When the server responds with a status, add it to the current line.\n    if m.status > 0 {\n        s += fmt.Sprintf(\"%d %s!\", m.status, http.StatusText(m.status))\n    }\n\n    // Send off whatever we came up with above for rendering.\n    return tea.NewView(\"\\n\" + s + \"\\n\\n\")\n}\n```\n\n## Run the program\n\nThe only thing left to do is run the program, so let’s do that! Our initial\nmodel doesn’t need any data at all in this case, we just initialize it with\na `model` struct with default values.\n\n```go\nfunc main() {\n    if _, err := tea.NewProgram(model{}).Run(); err != nil {\n        fmt.Printf(\"Uh oh, there was an error: %v\\n\", err)\n        os.Exit(1)\n    }\n}\n```\n\nAnd that’s that. There’s one more thing that is helpful to know about\n`Cmd`s, though.\n\n## One More Thing About Commands\n\n`Cmd`s are defined in Bubble Tea as `type Cmd func() Msg`. So they’re just\nfunctions that don’t take any arguments and return a `Msg`, which can be\nany type. If you need to pass arguments to a command, you just make a function\nthat returns a command. For example:\n\n```go\nfunc cmdWithArg(id int) tea.Cmd {\n    return func() tea.Msg {\n        return someMsg{id: id}\n    }\n}\n```\n\nA more real-world example looks like:\n\n```go\nfunc checkSomeUrl(url string) tea.Cmd {\n    return func() tea.Msg {\n        c := &http.Client{Timeout: 10 * time.Second}\n        res, err := c.Get(url)\n        if err != nil {\n            return errMsg{err}\n        }\n        return statusMsg(res.StatusCode)\n    }\n}\n```\n\nAnyway, just make sure you do as much stuff as you can in the innermost\nfunction, because that’s the one that runs asynchronously.\n\n## Now What?\n\nAfter doing this tutorial and [the previous one][basics] you should be ready to\nbuild a Bubble Tea program of your own. We also recommend that you look at the\nBubble Tea [example programs][examples] as well as [Bubbles][bubbles],\na component library for Bubble Tea.\n\nAnd, of course, check out the [Go Docs][docs].\n\n[bubbles]: https://github.com/charmbracelet/bubbles\n[docs]: https://pkg.go.dev/charm.land/bubbletea/v2?tab=doc\n[examples]: https://github.com/charmbracelet/bubbletea/tree/main/examples\n\n## Additional Resources\n\n- [Libraries we use with Bubble Tea](https://github.com/charmbracelet/bubbletea/#libraries-we-use-with-bubble-tea)\n- [Bubble Tea in the Wild](https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild)\n\n### Feedback\n\nWe’d love to hear your thoughts on this tutorial. Feel free to drop us a note!\n\n- [Twitter](https://twitter.com/charmcli)\n- [The Fediverse](https://mastodon.social/@charmcli)\n- [Discord](https://charm.sh/chat)\n\n---\n\nPart of [Charm](https://charm.sh).\n\n<a href=\"https://charm.sh/\"><img alt=\"The Charm logo\" src=\"https://stuff.charm.sh/charm-badge.jpg\" width=\"400\"></a>\n\nCharm热爱开源 • Charm loves open source\n"},"files":{"tutorials/commands/README.md":"# Commands in Bubble Tea\n\nThis is the second tutorial for Bubble Tea covering commands, which deal with\nI/O. The tutorial assumes you have a working knowledge of Go and a decent\nunderstanding of [the first tutorial][basics].\n\nYou can find the non-annotated version of this program [on GitHub][source].\n\n[basics]: https://github.com/charmbracelet/bubbletea/tree/main/tutorials/basics\n[source]: https://github.com/charmbracelet/bubbletea/blob/master/tutorials/commands/main.go\n\n## Let’s Go!\n\nFor this tutorial we’re building a very simple program that makes an HTTP\nrequest to a server and reports the status code of the response.\n\nWe’ll import a few necessary packages and put the URL we’re going to check in\na `const`.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n\n    tea \"charm.land/bubbletea/v2\"\n)\n\nconst url = \"https://charm.sh/\"\n```\n\n## The Model\n\nNext we’ll define our model. The only things we need to store are the status\ncode of the HTTP response and a possible error.\n\n```go\ntype model struct {\n    status int\n    err    error\n}\n```\n\n## Commands and Messages\n\n`Cmd`s are functions that perform some I/O and then return a `Msg`. Checking the\ntime, ticking a timer, reading from the disk, and network stuff are all I/O and\nshould be run through commands. That might sound harsh, but it will keep your\nBubble Tea program straightforward and simple.\n\nAnyway, let’s write a `Cmd` that makes a request to a server and returns the\nresult as a `Msg`.\n\n```go\nfunc checkServer() tea.Msg {\n\n    // Create an HTTP client and make a GET request.\n    c := &http.Client{Timeout: 10 * time.Second}\n    res, err := c.Get(url)\n\n    if err != nil {\n        // There was an error making our request. Wrap the error we received\n        // in a message and return it.\n        return errMsg{err}\n    }\n    // We received a response from the server. Return the HTTP status code\n    // as a message.\n    return statusMsg(res.StatusCode)\n}\n\ntype statusMsg int\n\ntype errMsg struct{ err error }\n\n// For messages that contain errors it's often handy to also implement the\n// error interface on the message.\nfunc (e errMsg) Error() string { return e.err.Error() }\n```\n\nAnd notice that we’ve defined two new `Msg` types. They can be any type, even\nan empty struct. We’ll come back to them later in our update function.\nFirst, let’s write our initialization function.\n\n## The Initialization Method\n\nThe initialization method is very simple: we return the `Cmd` we made earlier.\nNote that we don’t call the function; the Bubble Tea runtime will do that when\nthe time is right.\n\n```go\nfunc (m model) Init() tea.Cmd {\n    return checkServer\n}\n```\n\n## The Update Method\n\nInternally, `Cmd`s run asynchronously in a goroutine. The `Msg` they return is\ncollected and sent to our update function for handling. Remember those message\ntypes we made earlier when we were making the `checkServer` command? We handle\nthem here. This makes dealing with many asynchronous operations very easy.\n\n```go\nfunc (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {\n    switch msg := msg.(type) {\n\n    case statusMsg:\n        // The server returned a status message. Save it to our model. Also\n        // tell the Bubble Tea runtime we want to exit because we have nothing\n        // else to do. We'll still be able to render a final view with our\n        // status message.\n        m.status = int(msg)\n        return m, tea.Quit\n\n    case errMsg:\n        // There was an error. Note it in the model. And tell the runtime\n        // we're done and want to quit.\n        m.err = msg\n        return m, tea.Quit\n\n    case tea.KeyPressMsg:\n        // Ctrl+c exits. Even with short running programs it's good to have\n        // a quit key, just in case your logic is off. Users will be very\n        // annoyed if they can't exit.\n\t\tif msg.Mod == tea.ModCtrl && msg.Code == 'c' {\n\t\t\treturn m, tea.Quit\n\t\t}\n    }\n\n    // If we happen to get any other messages, don't do anything.\n    return m, nil\n}\n```\n\n## The View Function\n\nOur view is very straightforward. We look at the current model and build a\n`tea.View` accordingly:\n\n```go\nfunc (m model) View() tea.View {\n    // If there's an error, print it out and don't do anything else.\n    if m.err != nil {\n        return tea.NewView(fmt.Sprintf(\"\\nWe had some trouble: %v\\n\\n\", m.err))\n    }\n\n    // Tell the user we're doing something.\n    s := fmt.Sprintf(\"Checking %s ... \", url)\n\n    // When the server responds with a status, add it to the current line.\n    if m.status > 0 {\n        s += fmt.Sprintf(\"%d %s!\", m.status, http.StatusText(m.status))\n    }\n\n    // Send off whatever we came up with above for rendering.\n    return tea.NewView(\"\\n\" + s + \"\\n\\n\")\n}\n```\n\n## Run the program\n\nThe only thing left to do is run the program, so let’s do that! Our initial\nmodel doesn’t need any data at all in this case, we just initialize it with\na `model` struct with default values.\n\n```go\nfunc main() {\n    if _, err := tea.NewProgram(model{}).Run(); err != nil {\n        fmt.Printf(\"Uh oh, there was an error: %v\\n\", err)\n        os.Exit(1)\n    }\n}\n```\n\nAnd that’s that. There’s one more thing that is helpful to know about\n`Cmd`s, though.\n\n## One More Thing About Commands\n\n`Cmd`s are defined in Bubble Tea as `type Cmd func() Msg`. So they’re just\nfunctions that don’t take any arguments and return a `Msg`, which can be\nany type. If you need to pass arguments to a command, you just make a function\nthat returns a command. For example:\n\n```go\nfunc cmdWithArg(id int) tea.Cmd {\n    return func() tea.Msg {\n        return someMsg{id: id}\n    }\n}\n```\n\nA more real-world example looks like:\n\n```go\nfunc checkSomeUrl(url string) tea.Cmd {\n    return func() tea.Msg {\n        c := &http.Client{Timeout: 10 * time.Second}\n        res, err := c.Get(url)\n        if err != nil {\n            return errMsg{err}\n        }\n        return statusMsg(res.StatusCode)\n    }\n}\n```\n\nAnyway, just make sure you do as much stuff as you can in the innermost\nfunction, because that’s the one that runs asynchronously.\n\n## Now What?\n\nAfter doing this tutorial and [the previous one][basics] you should be ready to\nbuild a Bubble Tea program of your own. We also recommend that you look at the\nBubble Tea [example programs][examples] as well as [Bubbles][bubbles],\na component library for Bubble Tea.\n\nAnd, of course, check out the [Go Docs][docs].\n\n[bubbles]: https://github.com/charmbracelet/bubbles\n[docs]: https://pkg.go.dev/charm.land/bubbletea/v2?tab=doc\n[examples]: https://github.com/charmbracelet/bubbletea/tree/main/examples\n\n## Additional Resources\n\n- [Libraries we use with Bubble Tea](https://github.com/charmbracelet/bubbletea/#libraries-we-use-with-bubble-tea)\n- [Bubble Tea in the Wild](https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild)\n\n### Feedback\n\nWe’d love to hear your thoughts on this tutorial. Feel free to drop us a note!\n\n- [Twitter](https://twitter.com/charmcli)\n- [The Fediverse](https://mastodon.social/@charmcli)\n- [Discord](https://charm.sh/chat)\n\n---\n\nPart of [Charm](https://charm.sh).\n\n<a href=\"https://charm.sh/\"><img alt=\"The Charm logo\" src=\"https://stuff.charm.sh/charm-badge.jpg\" width=\"400\"></a>\n\nCharm热爱开源 • Charm loves open source\n"},"items":[{"name":"README.md","path":"tutorials/commands/README.md","rawUrl":"https://raw.githubusercontent.com/charmbracelet/bubbletea/HEAD/tutorials/commands/README.md","title":"Command: /readme","category":"command-prompt","format":"markdown","content":"# Commands in Bubble Tea\n\nThis is the second tutorial for Bubble Tea covering commands, which deal with\nI/O. The tutorial assumes you have a working knowledge of Go and a decent\nunderstanding of [the first tutorial][basics].\n\nYou can find the non-annotated version of this program [on GitHub][source].\n\n[basics]: https://github.com/charmbracelet/bubbletea/tree/main/tutorials/basics\n[source]: https://github.com/charmbracelet/bubbletea/blob/master/tutorials/commands/main.go\n\n## Let’s Go!\n\nFor this tutorial we’re building a very simple program that makes an HTTP\nrequest to a server and reports the status code of the response.\n\nWe’ll import a few necessary packages and put the URL we’re going to check in\na `const`.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n\n    tea \"charm.land/bubbletea/v2\"\n)\n\nconst url = \"https://charm.sh/\"\n```\n\n## The Model\n\nNext we’ll define our model. The only things we need to store are the status\ncode of the HTTP response and a possible error.\n\n```go\ntype model struct {\n    status int\n    err    error\n}\n```\n\n## Commands and Messages\n\n`Cmd`s are functions that perform some I/O and then return a `Msg`. Checking the\ntime, ticking a timer, reading from the disk, and network stuff are all I/O and\nshould be run through commands. That might sound harsh, but it will keep your\nBubble Tea program straightforward and simple.\n\nAnyway, let’s write a `Cmd` that makes a request to a server and returns the\nresult as a `Msg`.\n\n```go\nfunc checkServer() tea.Msg {\n\n    // Create an HTTP client and make a GET request.\n    c := &http.Client{Timeout: 10 * time.Second}\n    res, err := c.Get(url)\n\n    if err != nil {\n        // There was an error making our request. Wrap the error we received\n        // in a message and return it.\n        return errMsg{err}\n    }\n    // We received a response from the server. Return the HTTP status code\n    // as a message.\n    return statusMsg(res.StatusCode)\n}\n\ntype statusMsg int\n\ntype errMsg struct{ err error }\n\n// For messages that contain errors it's often handy to also implement the\n// error interface on the message.\nfunc (e errMsg) Error() string { return e.err.Error() }\n```\n\nAnd notice that we’ve defined two new `Msg` types. They can be any type, even\nan empty struct. We’ll come back to them later in our update function.\nFirst, let’s write our initialization function.\n\n## The Initialization Method\n\nThe initialization method is very simple: we return the `Cmd` we made earlier.\nNote that we don’t call the function; the Bubble Tea runtime will do that when\nthe time is right.\n\n```go\nfunc (m model) Init() tea.Cmd {\n    return checkServer\n}\n```\n\n## The Update Method\n\nInternally, `Cmd`s run asynchronously in a goroutine. The `Msg` they return is\ncollected and sent to our update function for handling. Remember those message\ntypes we made earlier when we were making the `checkServer` command? We handle\nthem here. This makes dealing with many asynchronous operations very easy.\n\n```go\nfunc (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {\n    switch msg := msg.(type) {\n\n    case statusMsg:\n        // The server returned a status message. Save it to our model. Also\n        // tell the Bubble Tea runtime we want to exit because we have nothing\n        // else to do. We'll still be able to render a final view with our\n        // status message.\n        m.status = int(msg)\n        return m, tea.Quit\n\n    case errMsg:\n        // There was an error. Note it in the model. And tell the runtime\n        // we're done and want to quit.\n        m.err = msg\n        return m, tea.Quit\n\n    case tea.KeyPressMsg:\n        // Ctrl+c exits. Even with short running programs it's good to have\n        // a quit key, just in case your logic is off. Users will be very\n        // annoyed if they can't exit.\n\t\tif msg.Mod == tea.ModCtrl && msg.Code == 'c' {\n\t\t\treturn m, tea.Quit\n\t\t}\n    }\n\n    // If we happen to get any other messages, don't do anything.\n    return m, nil\n}\n```\n\n## The View Function\n\nOur view is very straightforward. We look at the current model and build a\n`tea.View` accordingly:\n\n```go\nfunc (m model) View() tea.View {\n    // If there's an error, print it out and don't do anything else.\n    if m.err != nil {\n        return tea.NewView(fmt.Sprintf(\"\\nWe had some trouble: %v\\n\\n\", m.err))\n    }\n\n    // Tell the user we're doing something.\n    s := fmt.Sprintf(\"Checking %s ... \", url)\n\n    // When the server responds with a status, add it to the current line.\n    if m.status > 0 {\n        s += fmt.Sprintf(\"%d %s!\", m.status, http.StatusText(m.status))\n    }\n\n    // Send off whatever we came up with above for rendering.\n    return tea.NewView(\"\\n\" + s + \"\\n\\n\")\n}\n```\n\n## Run the program\n\nThe only thing left to do is run the program, so let’s do that! Our initial\nmodel doesn’t need any data at all in this case, we just initialize it with\na `model` struct with default values.\n\n```go\nfunc main() {\n    if _, err := tea.NewProgram(model{}).Run(); err != nil {\n        fmt.Printf(\"Uh oh, there was an error: %v\\n\", err)\n        os.Exit(1)\n    }\n}\n```\n\nAnd that’s that. There’s one more thing that is helpful to know about\n`Cmd`s, though.\n\n## One More Thing About Commands\n\n`Cmd`s are defined in Bubble Tea as `type Cmd func() Msg`. So they’re just\nfunctions that don’t take any arguments and return a `Msg`, which can be\nany type. If you need to pass arguments to a command, you just make a function\nthat returns a command. For example:\n\n```go\nfunc cmdWithArg(id int) tea.Cmd {\n    return func() tea.Msg {\n        return someMsg{id: id}\n    }\n}\n```\n\nA more real-world example looks like:\n\n```go\nfunc checkSomeUrl(url string) tea.Cmd {\n    return func() tea.Msg {\n        c := &http.Client{Timeout: 10 * time.Second}\n        res, err := c.Get(url)\n        if err != nil {\n            return errMsg{err}\n        }\n        return statusMsg(res.StatusCode)\n    }\n}\n```\n\nAnyway, just make sure you do as much stuff as you can in the innermost\nfunction, because that’s the one that runs asynchronously.\n\n## Now What?\n\nAfter doing this tutorial and [the previous one][basics] you should be ready to\nbuild a Bubble Tea program of your own. We also recommend that you look at the\nBubble Tea [example programs][examples] as well as [Bubbles][bubbles],\na component library for Bubble Tea.\n\nAnd, of course, check out the [Go Docs][docs].\n\n[bubbles]: https://github.com/charmbracelet/bubbles\n[docs]: https://pkg.go.dev/charm.land/bubbletea/v2?tab=doc\n[examples]: https://github.com/charmbracelet/bubbletea/tree/main/examples\n\n## Additional Resources\n\n- [Libraries we use with Bubble Tea](https://github.com/charmbracelet/bubbletea/#libraries-we-use-with-bubble-tea)\n- [Bubble Tea in the Wild](https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild)\n\n### Feedback\n\nWe’d love to hear your thoughts on this tutorial. Feel free to drop us a note!\n\n- [Twitter](https://twitter.com/charmcli)\n- [The Fediverse](https://mastodon.social/@charmcli)\n- [Discord](https://charm.sh/chat)\n\n---\n\nPart of [Charm](https://charm.sh).\n\n<a href=\"https://charm.sh/\"><img alt=\"The Charm logo\" src=\"https://stuff.charm.sh/charm-badge.jpg\" width=\"400\"></a>\n\nCharm热爱开源 • Charm loves open source\n","isInternal":false,"tokens":1812,"sizeBytes":7246}],"systemPromptSnippet":"<yakaai_skills repo=\"charmbracelet/bubbletea\">\n<!-- File: tutorials/commands/README.md (Tokens: ~1812 | Category: command-prompt) -->\n# Commands in Bubble Tea\n\nThis is the second tutorial for Bubble Tea covering commands, which deal with\nI/O. The tutorial assumes you have a working knowledge of Go and a decent\nunderstanding of [the first tutorial][basics].\n\nYou can find the non-annotated version of this program [on GitHub][source].\n\n[basics]: https://github.com/charmbracelet/bubbletea/tree/main/tutorials/basics\n[source]: https://github.com/charmbracelet/bubbletea/blob/master/tutorials/commands/main.go\n\n## Let’s Go!\n\nFor this tutorial we’re building a very simple program that makes an HTTP\nrequest to a server and reports the status code of the response.\n\nWe’ll import a few necessary packages and put the URL we’re going to check in\na `const`.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n\n    tea \"charm.land/bubbletea/v2\"\n)\n\nconst url = \"https://charm.sh/\"\n```\n\n## The Model\n\nNext we’ll define our model. The only things we need to store are the status\ncode of the HTTP response and a possible error.\n\n```go\ntype model struct {\n    status int\n    err    error\n}\n```\n\n## Commands and Messages\n\n`Cmd`s are functions that perform some I/O and then return a `Msg`. Checking the\ntime, ticking a timer, reading from the disk, and network stuff are all I/O and\nshould be run through commands. That might sound harsh, but it will keep your\nBubble Tea program straightforward and simple.\n\nAnyway, let’s write a `Cmd` that makes a request to a server and returns the\nresult as a `Msg`.\n\n```go\nfunc checkServer() tea.Msg {\n\n    // Create an HTTP client and make a GET request.\n    c := &http.Client{Timeout: 10 * time.Second}\n    res, err := c.Get(url)\n\n    if err != nil {\n        // There was an error making our request. Wrap the error we received\n        // in a message and return it.\n        return errMsg{err}\n    }\n    // We received a response from the server. Return the HTTP status code\n    // as a message.\n    return statusMsg(res.StatusCode)\n}\n\ntype statusMsg int\n\ntype errMsg struct{ err error }\n\n// For messages that contain errors it's often handy to also implement the\n// error interface on the message.\nfunc (e errMsg) Error() string { return e.err.Error() }\n```\n\nAnd notice that we’ve defined two new `Msg` types. They can be any type, even\nan empty struct. We’ll come back to them later in our update function.\nFirst, let’s write our initialization function.\n\n## The Initialization Method\n\nThe initialization method is very simple: we return the `Cmd` we made earlier.\nNote that we don’t call the function; the Bubble Tea runtime will do that when\nthe time is right.\n\n```go\nfunc (m model) Init() tea.Cmd {\n    return checkServer\n}\n```\n\n## The Update Method\n\nInternally, `Cmd`s run asynchronously in a goroutine. The `Msg` they return is\ncollected and sent to our update function for handling. Remember those message\ntypes we made earlier when we were making the `checkServer` command? We handle\nthem here. This makes dealing with many asynchronous operations very easy.\n\n```go\nfunc (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {\n    switch msg := msg.(type) {\n\n    case statusMsg:\n        // The server returned a status message. Save it to our model. Also\n        // tell the Bubble Tea runtime we want to exit because we have nothing\n        // else to do. We'll still be able to render a final view with our\n        // status message.\n        m.status = int(msg)\n        return m, tea.Quit\n\n    case errMsg:\n        // There was an error. Note it in the model. And tell the runtime\n        // we're done and want to quit.\n        m.err = msg\n        return m, tea.Quit\n\n    case tea.KeyPressMsg:\n        // Ctrl+c exits. Even with short running programs it's good to have\n        // a quit key, just in case your logic is off. Users will be very\n        // annoyed if they can't exit.\n\t\tif msg.Mod == tea.ModCtrl && msg.Code == 'c' {\n\t\t\treturn m, tea.Quit\n\t\t}\n    }\n\n    // If we happen to get any other messages, don't do anything.\n    return m, nil\n}\n```\n\n## The View Function\n\nOur view is very straightforward. We look at the current model and build a\n`tea.View` accordingly:\n\n```go\nfunc (m model) View() tea.View {\n    // If there's an error, print it out and don't do anything else.\n    if m.err != nil {\n        return tea.NewView(fmt.Sprintf(\"\\nWe had some trouble: %v\\n\\n\", m.err))\n    }\n\n    // Tell the user we're doing something.\n    s := fmt.Sprintf(\"Checking %s ... \", url)\n\n    // When the server responds with a status, add it to the current line.\n    if m.status > 0 {\n        s += fmt.Sprintf(\"%d %s!\", m.status, http.StatusText(m.status))\n    }\n\n    // Send off whatever we came up with above for rendering.\n    return tea.NewView(\"\\n\" + s + \"\\n\\n\")\n}\n```\n\n## Run the program\n\nThe only thing left to do is run the program, so let’s do that! Our initial\nmodel doesn’t need any data at all in this case, we just initialize it with\na `model` struct with default values.\n\n```go\nfunc main() {\n    if _, err := tea.NewProgram(model{}).Run(); err != nil {\n        fmt.Printf(\"Uh oh, there was an error: %v\\n\", err)\n        os.Exit(1)\n    }\n}\n```\n\nAnd that’s that. There’s one more thing that is helpful to know about\n`Cmd`s, though.\n\n## One More Thing About Commands\n\n`Cmd`s are defined in Bubble Tea as `type Cmd func() Msg`. So they’re just\nfunctions that don’t take any arguments and return a `Msg`, which can be\nany type. If you need to pass arguments to a command, you just make a function\nthat returns a command. For example:\n\n```go\nfunc cmdWithArg(id int) tea.Cmd {\n    return func() tea.Msg {\n        return someMsg{id: id}\n    }\n}\n```\n\nA more real-world example looks like:\n\n```go\nfunc checkSomeUrl(url string) tea.Cmd {\n    return func() tea.Msg {\n        c := &http.Client{Timeout: 10 * time.Second}\n        res, err := c.Get(url)\n        if err != nil {\n            return errMsg{err}\n        }\n        return statusMsg(res.StatusCode)\n    }\n}\n```\n\nAnyway, just make sure you do as much stuff as you can in the innermost\nfunction, because that’s the one that runs asynchronously.\n\n## Now What?\n\nAfter doing this tutorial and [the previous one][basics] you should be ready to\nbuild a Bubble Tea program of your own. We also recommend that you look at the\nBubble Tea [example programs][examples] as well as [Bubbles][bubbles],\na component library for Bubble Tea.\n\nAnd, of course, check out the [Go Docs][docs].\n\n[bubbles]: https://github.com/charmbracelet/bubbles\n[docs]: https://pkg.go.dev/charm.land/bubbletea/v2?tab=doc\n[examples]: https://github.com/charmbracelet/bubbletea/tree/main/examples\n\n## Additional Resources\n\n- [Libraries we use with Bubble Tea](https://github.com/charmbracelet/bubbletea/#libraries-we-use-with-bubble-tea)\n- [Bubble Tea in the Wild](https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild)\n\n### Feedback\n\nWe’d love to hear your thoughts on this tutorial. Feel free to drop us a note!\n\n- [Twitter](https://twitter.com/charmcli)\n- [The Fediverse](https://mastodon.social/@charmcli)\n- [Discord](https://charm.sh/chat)\n\n---\n\nPart of [Charm](https://charm.sh).\n\n<a href=\"https://charm.sh/\"><img alt=\"The Charm logo\" src=\"https://stuff.charm.sh/charm-badge.jpg\" width=\"400\"></a>\n\nCharm热爱开源 • Charm loves open source\n</yakaai_skills>"}