# Technical Documentation: charmbracelet/bubbletea

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/charmbracelet/bubbletea)

### Overview (`README.md`)

# Bubble Tea

<p>
    <img src="https://github.com/user-attachments/assets/ad408275-8799-488f-9303-441e7f869535" width="350"><br>
    <a href="https://github.com/charmbracelet/bubbletea/releases"><img src="https://img.shields.io/github/release/charmbracelet/bubbletea.svg" alt="Latest Release"></a>
    <a href="https://pkg.go.dev/charm.land/bubbletea/v2?tab=doc"><img src="https://godoc.org/charm.land/bubbletea/v2?status.svg" alt="GoDoc"></a>
    <a href="https://github.com/charmbracelet/bubbletea/actions"><img src="https://github.com/charmbracelet/bubbletea/actions/workflows/build.yml/badge.svg?branch=main" alt="Build Status"></a>
</p>

The fun, functional and stateful way to build terminal apps. A Go framework
based on [The Elm Architecture][elm]. Bubble Tea is well-suited for simple and
complex terminal applications, either inline, full-window, or a mix of both.

<p>
    <img src="https://stuff.charm.sh/bubbletea/bubbletea-example.gif" width="100%" alt="Bubble Tea Example">
</p>

Bubble Tea is in use in production and includes a number of features and
performance optimizations we’ve added along the way. Among those is
a high-performance cell-based renderer, built-in color downsampling,
declarative views, high-fidelity keyboard and mouse handling, native clipboard
support, and more.

To get started, see the tutorial below, the [examples][examples], the
[docs][docs], and some common [resources](#libraries-we-use-with-bubble-tea).

> [!TIP]
>
> Upgrading from v1? Check out the [upgrade guide](./UPGRADE_GUIDE_V2.md), or
> point your LLM at it and let it go to town.

## By the way

Be sure to check out [Bubbles][bubbles], a library of common UI components for Bubble Tea.

<p>
    <a href="https://github.com/charmbracelet/bubbles"><img src="https://github.com/user-attachments/assets/b6dc4638-b67a-4bfa-88d0-a8e8833c3ac9" width="172" alt="Bubbles Badge"></a>&nbsp;&nbsp;
    <a href="https://github.com/charmbracelet/bubbles"><img src="https://stuff.charm.sh/bubbles-examples/textinput.gif" width="400" alt="Text Input Example from Bubbles"></a>
</p>

---

## Tutorial

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
to build applications.

This tutorial assumes you have a working knowledge of Go.

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/main/tutorials/basics

### Enough! Let's get to it.

For this tutorial, we're making a shopping list.

To start we'll define our package and import some libraries. Our only external
import will be the Bubble Tea library, which we'll call `tea` for short.

```go
package main

// These imports will be used later in the tutorial. If you save the file
// now, Go might complain they are unused, but that's fine.
// You may also need to run `go mod tidy` to download bubbletea and its
// dependencies.
import (
    "fmt"
    "os"

    tea "charm.land/bubbletea/v2"
)
```

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.

### The Model

So let's start by defining our model which will store our application's state.
It can be any type, but a `struct` usually makes the most sense.

```go
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
}
```

## Initialization

Next, we’ll define our application’s initial state. `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{
		// 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{}),
	}
}
```

After that, 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 (m model) Init() tea.Cmd {
    // Just return `nil`, which means "no I/O right now, please."
    return nil
}
```

### The Update Method

Next up is the update method. The update function is called when “things
happen.” Its job is to look at what has happened and return an updated model in
response. It can also return a `Cmd` to make more things happen, but for now
don't worry about that part.

In our case, when a user presses the down arrow, `Update`’s job is to notice
that the down arrow was pressed and move the cursor accordingly (or not).

The “something happened” comes in the form of a `Msg`, which can be any type.
Messages are the result of some I/O that took place, such as a keypress, timer
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.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.KeyPressMsg:

        // 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 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)
            } 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
}
```

You may have noticed that <kbd>ctrl+c</kbd> and <kbd>q</kbd> above return
a `tea.Quit` command with the model. That’s a special command which instructs
the Bubble Tea runtime to quit, exiting the program.

### The View Method

At last, it’s time to render our UI. Of all the methods, the view is the
simplest. We look at the model in its current state and use it to build a
`tea.View`. The view declares our UI content and, optionally, terminal features
like alt screen mode, mouse tracking, cursor position, and more.

Because the view describes the entire UI of your application, you don’t have to
worry about redrawing logic and stuff like that. Bubble Tea takes care of it
for you.

```go
func (m model) View() tea.View {
    // 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 += "\nPress q to quit.\n"

    // Send the UI for rendering
    return tea.NewView(s)
}
```

### All Together Now

The last step is to simply run our program. We pass our initial model to
`tea.NewProgram` and let it rip:

```go
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)
    }
}
```

## What’s Next?

This tutorial covers the basics of building an interactive terminal UI, but
in the real world you'll also need to perform I/O. To learn about that have a
look at the [Command Tutorial][cmd]. It's pretty simple.

There are also several [Bubble Tea examples][examples] available and, of course,
there are [Go Docs][docs].

[cmd]: https://github.com/charmbracelet/bubbletea/tree/main/tutorials/commands/
[examples]: https://github.com/charmbracelet/bubbletea/tree/main/examples
[docs]: https://pkg.go.dev/charm.land/bubbletea/v2?tab=doc

## Debugging

### Debugging with Delve

Since Bubble Tea apps assume control of stdin and stdout, you’ll need to run
delve in headless mode and then connect to it:

```bash
# 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
```

If you do not explicitly supply the `--listen` flag, the port used will vary
per run, so passing this in makes the debugger easier to use from a script
or your IDE of choice.

Additionally, we pass in `--api-version=2` because delve defaults to version 1
for backwards compatibility reasons. However, delve recommends using version 2
for all new development and some clients may no longer work with version 1.
For more information, see the [Delve documentation](https://github.com/go-delve/delve/tree/master/Documentation/api).

### Logging Stuff

You can’t really log to stdout with Bubble Tea because your TUI is busy
occupying that! You can, however, log to a file by including something like
the following prior to starting your Bubble Tea program:

```go
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()
}
```

To see what’s being logged in real time, run `tail -f debug.log` while you run
your program in another window.

## Libraries we use with Bubble Tea

- [Bubbles][bubbles]: Common Bubble Tea components such as text inputs, viewports, spinners and so on
- [Lip Gloss][lipgloss]: Style, format and layout tools for terminal applications
- [Harmonica][harmonica]: A spring animation library for smooth, natural motion
- [BubbleZone][bubblezone]: Easy mouse event tracking for Bubble Tea components
- [ntcharts][ntcharts]: A terminal charting library built for Bubble Tea and [Lip Gloss][lipgloss]

[bubbles]: https://github.com/charmbracelet/bubbles
[lipgloss]: https://github.com/charmbracelet/lipgloss
[harmonica]: https://github.com/charmbracelet/harmonica
[bubblezone]: https://github.com/lrstanley/bubblezone
[ntcharts]: https://github.com/NimbleMarkets/ntcharts

## Bubble Tea in the Wild

There are over [18,000 applications](https://github.com/charmbracelet/bubbletea/network/dependents) built with Bubble Tea! Here are a handful of ’em.

### Staff favourites

- [chezmoi](https://github.com/twpayne/chezmoi): securely manage your dotfiles across multiple machines
- [circumflex](https://github.com/bensadeh/circumflex): read Hacker News in the terminal
- [gh-dash](https://www.github.com/dlvhdr/gh-dash): a GitHub CLI extension for PRs and issues
- [Tetrigo](https://github.com/Broderick-Westrope/tetrigo): Tetris in the terminal
- [Signls](https://github.com/emprcl/signls): a generative midi sequencer designed for composition and live performance
- [Superfile](https://github.com/yorukot/superfile): a super file manager

### In Industry

- Microsoft Azure – [Aztify](https://github.com/Azure/aztfy): bring Microsoft Azure resources under Terraform
- Daytona – [Daytona](https://github.com/daytonaio/daytona): an AI infrastructure platform
- Cockroach Labs – [CockroachDB](https://github.com/cockroachdb/cockroach): a cloud-native, high-availability distributed SQL database
- Truffle Security Co. – [Trufflehog](https://github.com/trufflesecurity/trufflehog): find leaked credentials
- NVIDIA – [container-canary](https://github.com/NVIDIA/container-canary): a container validator
- AWS – [eks-node-viewer](https://github.com/awslabs/eks-node-viewer): a tool for visualizing dynamic node usage within an EKS cluster
- MinIO – [mc](https://github.com/minio/mc): the official [MinIO](https://min.io) client
- Ubuntu – [Authd](https://github.com/ubuntu/authd): an authentication daemon for cloud-based identity providers

### Charm stuff

- [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser, and online markdown stash
- [Huh?](https://github.com/charmbracelet/huh): an interactive prompt and form toolkit
- [Mods](https://github.com/charmbracelet/mods): AI on the CLI, built for pipelines
- [Wishlist](https://github.com/charmbracelet/wishlist): an SSH directory (and bastion!)

### There’s so much more where that came from

For more applications built with Bubble Tea see [Charm & Friends][community].
Is there something cool you made with Bubble Tea you want to share? [PRs][community] are
welcome!

## Contributing

See [contributing][contribute].

[contribute]: https://github.com/charmbracelet/bubbletea/contribute

## Feedback

We’d love to hear your thoughts on this project. Feel free to drop us a note!

- [Twitter](https://twitter.com/charmcli)
- [The Fediverse](https://mastodon.social/@charmcli)
- [Discord](https://charm.sh/chat)

## Acknowledgments

Bubble Tea is based on the paradigms of [The Elm Architecture][elm] by Evan
Czaplicki et alia and the excellent [go-tea][gotea] by TJ Holowaychuk. It’s
inspired by the many great [_Zeichenorientierte Benutzerschnittstellen_][zb]
of days past.

[elm]: https://guide.elm-lang.org/architecture/
[gotea]: https://github.com/tj/go-tea
[zb]: https://de.wikipedia.org/wiki/Zeichenorientierte_Benutzerschnittstelle
[community]: https://github.com/charm-and-friends/charm-in-the-wild

## License

[MIT](https://github.com/charmbracelet/bubbletea/raw/main/LICENSE)

---

Part of [Charm](https://charm.sh).

<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-banner-softy.jpg" width="400"></a>

Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة

---

### Overview (`examples/README.md`)

# Examples

### Alt Screen Toggle

The `altscreen-toggle` example shows how to transition between the alternative
screen buffer and the normal screen buffer using Bubble Tea.

<a href="./altscreen-toggle/main.go">
  <img width="750" src="./altscreen-toggle/altscreen-toggle.gif" />
</a>

### Chat

The `chat` examples shows a basic chat application with a multi-line `textarea`
input.

<a href="./chat/main.go">
  <img width="750" src="./chat/chat.gif" />
</a>

### Composable Views

The `composable-views` example shows how to compose two bubble models (spinner
and timer) together in a single application and switch between them.

<a href="./composable-views/main.go">
  <img width="750" src="./composable-views/composable-views.gif" />
</a>

### ISBN Book Form

The `isbn-form` example demonstrates how to build a multi-step form with
`textinput` bubbles and validation on the inputs.

<a href="./isbn-form/main.go">
  <img width="750" src="./isbn-form/isbn-form.gif" />
</a>

### Debounce

The `debounce` example shows how to throttle key presses to avoid overloading
your Bubble Tea application.

<a href="./debounce/main.go">
  <img width="750" src="./debounce/debounce.gif" />
</a>

### Exec

The `exec` example shows how to execute a running command during the execution
of a Bubble Tea application such as launching an `EDITOR`.
 
<a href="./exec/main.go">
  <img width="750" src="./exec/exec.gif" />
</a>

### Full Screen

The `fullscreen` example shows how to make a Bubble Tea application fullscreen.

<a href="./fullscreen/main.go">
  <img width="750" src="./fullscreen/fullscreen.gif" />
</a>

### Glamour

The `glamour` example shows how to use [Glamour](https://github.com/charmbracelet/glamour) inside a viewport bubble.

<a href="./glamour/main.go">
  <img width="750" src="./glamour/glamour.gif" />
</a>

### Help

The `help` example shows how to use the `help` bubble to display help to the
user of your application.

<a href="./help/main.go">
  <img width="750" src="./help/help.gif" />
</a>

### Http

The `http` example shows how to make an `http` call within your Bubble Tea
application.

<a href="./http/main.go">
  <img width="750" src="./http/http.gif" />
</a>

### Default List

The `list-default` example shows how to use the list bubble.

<a href="./list-default/main.go">
  <img width="750" src="./list-default/list-default.gif" />
</a>

### Fancy List

The `list-fancy` example shows how to use the list bubble with extra customizations.

<a href="./list-fancy/main.go">
  <img width="750" src="./list-fancy/list-fancy.gif" />
</a>

### Simple List

The `list-simple` example shows how to use the list and customize it to have a simpler, more compact, appearance.

<a href="./list-simple/main.go">
  <img width="750" src="./list-simple/list-simple.gif" />
</a>

### Mouse

The `mouse` example shows how to receive mouse events in a Bubble Tea
application.

<a href="./mouse/main.go">
  Code
</a>

### Package Manager

The `package-manager` example shows how to build an interface for a package
manager using the `tea.Println` feature.

<a href="./package-manager/main.go">
  <img width="750" src="./package-manager/package-manager.gif" />
</a>

### Pager

The `pager` example shows how to build a simple pager application similar to
`less`.

<a href="./pager/main.go">
  <img width="750" src="./pager/pager.gif" />
</a>

### Paginator

The `paginator` example shows how to build a simple paginated list.

<a href="./paginator/main.go">
  <img width="750" src="./paginator/paginator.gif" />
</a>

### Pipe

The `pipe` example demonstrates using shell pipes to communicate with Bubble
Tea applications.

<a href="./pipe/main.go">
  <img width="750" src="./pipe/pipe.gif" />
</a>

### Animated Progress

The `progress-animated` example shows how to build a progress bar with an
animated progression.

<a href="./progress-animated/main.go">
  <img width="750" src="./progress-animated/progress-animated.gif" />
</a>

### Download Progress

The `progress-download` example demonstrates how to download a file while
indicating download progress through Bubble Tea.

<a href="./progress-download/main.go">
  Code
</a>

### Static Progress

The `progress-static` example shows a progress bar with static incrementation
of progress.

<a href="./progress-static/main.go">
  <img width="750" src="./progress-static/progress-static.gif" />
</a>

### Real Time

The `realtime` example demonstrates the use of go channels to perform realtime
communication with a Bubble Tea application.

<a href="./realtime/main.go">
  <img width="750" src="./realtime/realtime.gif" />
</a>

### Result

The `result` example shows a choice menu with the ability to select an option.

<a href="./result/main.go">
  <img width="750" src="./result/result.gif" />
</a>

### Send Msg

The `send-msg` example demonstrates the usage of custom `tea.Msg`s.

<a href="./send-msg/main.go">
  <img width="750" src="./send-msg/send-msg.gif" />
</a>

### Sequence

The `sequence` example demonstrates the `tea.Sequence` command.

<a href="./sequence/main.go">
  <img width="750" src="./sequence/sequence.gif" />
</a>

### Simple

The `simple` example shows a very simple Bubble Tea application.

<a href="./simple/main.go">
  <img width="750" src="./simple/simple.gif" />
</a>

### Spinner

The `spinner` example demonstrates a spinner bubble being used to indicate loading.

<a href="./spinner/main.go">
  <img width="750" src="./spinner/spinner.gif" />
</a>

### Spinners

The `spinner` example shows various spinner types that are available.

<a href="./spinners/main.go">
  <img width="750" src="./spinners/spinners.gif" />
</a>

### Split Editors

The `split-editors` example shows multiple `textarea`s being used in a single
application and being able to switch focus between them.

<a href="./split-editors/main.go">
  <img width="750" src="./split-editors/split-editors.gif" />
</a>

### Stop Watch

The `stopwatch` example shows a sample stop watch built with Bubble Tea.

<a href="./stopwatch/main.go">
  <img width="750" src="./stopwatch/stopwatch.gif" />
</a>

### Table

The `table` example demonstrates the table bubble being used to display tabular
data.

<a href="./table/main.go">
  <img width="750" src="./table/table.gif" />
</a>

### Tabs

The `tabs` example demonstrates tabbed navigation styled with [Lip Gloss](https://github.com/charmbracelet/lipgloss).

<a href="./tabs/main.go">
  <img width="750" src="./tabs/tabs.gif" />
</a>

### Text Area

The `textarea` example demonstrates a simple Bubble Tea application using a
`textarea` bubble.

<a href="./textarea/main.go">
  <img width="750" src="./textarea/textarea.gif" />
</a>

### Text Input

The `textinput` example demonstrates a simple Bubble Tea application using a `textinput` bubble.

<a href="./textinput/main.go">
  <img width="750" src="./textinput/textinput.gif" />
</a>

### Multiple Text Inputs

The `textinputs` example shows multiple `textinputs` and being able to switch
focus between them as well as changing the cursor mode.

<a href="./textinputs/main.go">
  <img width="750" src="./textinputs/textinputs.gif" />
</a>

### Timer

The `timer` example shows a simple timer built with Bubble Tea.

<a href="./timer/main.go">
  <img width="750" src="./timer/timer.gif" />
</a>

### TUI Daemon

The `tui-daemon-combo` demonstrates building a text-user interface along with a
daemon mode using Bubble Tea.

<a href="./tui-daemon-combo/main.go">
  <img width="750" src="./tui-daemon-combo/tui-daemon-combo.gif" />
</a>

### Views

The `views` example demonstrates how to build a Bubble Tea application with
multiple views and switch between them.

<a href="./views/main.go">
  <img width="750" src="./views/views.gif" />
</a>

---

### Overview (`examples/altscreen-toggle/README.md`)

# Alt Screen Toggle

<img width="800" src="./altscreen-toggle.gif" />

---

### Overview (`examples/composable-views/README.md`)

# Composable Views

<img width="800" src="./composable-views.gif" />

---

### Overview (`examples/debounce/README.md`)

# Debounce

<img width="800" src="./debounce.gif" />

---

### Overview (`examples/fullscreen/README.md`)

# Full Screen

<img width="800" src="./fullscreen.gif" />

---

### Overview (`examples/glamour/README.md`)

# Glamour

<img width="800" src="./glamour.gif" />

---

### Overview (`examples/list-default/README.md`)

# Default List

<img width="800" src="./list-default.gif" />

---

### Overview (`examples/list-fancy/README.md`)

# Fancy List

<img width="800" src="./list-fancy.gif" />

---

### Overview (`examples/list-simple/README.md`)

# Simple List

<img width="800" src="./list-simple.gif" />

---

### Overview (`examples/package-manager/README.md`)

# Package Manager

<img width="800" src="./package-manager.gif" />

---

### Overview (`examples/paginator/README.md`)

# Paginator

<img width="800" src="./paginator.gif" />

---

### Overview (`examples/progress-animated/README.md`)

# Animated Progress

<img width="800" src="./progress-animated.gif" />

---

### Overview (`examples/progress-download/README.md`)

# Download Progress

This example demonstrates how to download a file from a URL and show its
progress with a [Progress Bubble][progress].

In this case we're getting download progress with an [`io.TeeReader`][tee] and
sending progress `Msg`s to the `Program` with `Program.Send()`.

## How to Run

Build the application with `go build .`, then run with a `--url` argument
specifying the URL of the file to download. For example:

```
./progress-download --url="https://download.blender.org/demo/color_vortex.blend"
```

Note that in this example a TUI will not be shown for URLs that do not respond
with a ContentLength header.

* * *

This example originally came from [this discussion][discussion].

* * *

<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>

Charm热爱开源 • Charm loves open source


[progress]: https://github.com/charmbracelet/bubbles/
[tee]: https://pkg.go.dev/io#TeeReader
[discussion]: https://github.com/charmbracelet/bubbles/discussions/127

---

### Overview (`examples/progress-static/README.md`)

# Static Progress

<img width="800" src="./progress-static.gif" />

---

### Overview (`examples/realtime/README.md`)

# Real Time

<img width="800" src="./realtime.gif" />

---

### Overview (`examples/send-msg/README.md`)

# Send Msg

<img width="800" src="./send-msg.gif" />

---

### Overview (`examples/sequence/README.md`)

# Sequence

<img width="800" src="./sequence.gif" />

---

### Overview (`examples/spinner/README.md`)

# Spinner

<img width="800" src="./spinner.gif" />

---

### Overview (`examples/spinners/README.md`)

# Spinners

<img width="800" src="./spinners.gif" />

---

### Overview (`examples/split-editors/README.md`)

# Split Editors

<img width="800" src="./split-editors.gif" />

---

### Overview (`examples/stopwatch/README.md`)

# Stopwatch

<img width="800" src="./stopwatch.gif" />

---

### Overview (`examples/textarea/README.md`)

# Text Area

<img width="800" src="./textarea.gif" />

---

### Overview (`examples/textinput/README.md`)

# Text Input

<img width="800" src="./textinput.gif" />

---

### Overview (`examples/textinputs/README.md`)

# Text Inputs

<img width="800" src="./textinputs.gif" />

---

### Overview (`examples/tui-daemon-combo/README.md`)

# TUI Daemon

<img width="800" src="./tui-daemon-combo.gif" />

---

### Overview (`tutorials/basics/README.md`)

# 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
to build applications.

This tutorial assumes you have a working knowledge of Go.

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/main/tutorials/basics

## Enough! Let’s get to it.

For this tutorial, we’re making a shopping list.

To start we’ll define our package and import some libraries. Our only external
import will be the Bubble Tea library, which we’ll call `tea` for short.

```go
package main

import (
    "fmt"
    "os"

    tea "charm.land/bubbletea/v2"
)
```

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.

## The Model

So let’s start by defining our model which will store our application’s state.
It can be any type, but a `struct` usually makes the most sense.

```go
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
}
```

## 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.

```go
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{}),
	}
}
```

After that, we’ll 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 {
    return nil
}
```

## The Update Method

Next up is the update method. The update function is called when “things
happen.” Its job is to look at what has happened and return an updated model in
response. It can also return a `Cmd` to make more things happen, but for now
don’t worry about that part.

In our case, when a user presses the down arrow, `Update`’s job is to notice
that the down arrow was pressed and move the cursor accordingly (or not).

The “something happened” comes in the form of a `Msg`, which can be any type.
Messages are the result of some I/O that took place, such as a keypress, timer
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.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.KeyPressMsg:

        // 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 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)
            } 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
}
```

You may have noticed that <kbd>ctrl+c</kbd> and <kbd>q</kbd> above return
a `tea.Quit` command with the model. That’s a special command which instructs
the Bubble Tea runtime to quit, exiting the program.

## The View Method

At last, it’s time to render our UI. Of all the methods, the view is the
simplest. We look at the model in its current state and use it to return
a `tea.View`. The view declares our UI content and, optionally, terminal
features like full-window mode (aka, altscreen mode), mouse tracking, cursor
position, and more.

Because the view describes the entire UI of your application, you don’t have to
worry about redrawing logic and stuff like that. Bubble Tea takes care of it
for you.

```go
func (m model) View() tea.View {
    // 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 += "\nPress q to quit.\n"

    // Send the UI for rendering
    return tea.NewView(s)
}
```

## All Together Now

The last step is to simply run our program. We pass our initial model to
`tea.NewProgram` and let it rip:

```go
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)
    }
}
```

## What’s Next?

This tutorial covers the basics of building an interactive terminal UI, but
in the real world you’ll also need to perform I/O. To learn about that have a
look at the [Command Tutorial][cmd]. It’s pretty simple.

There are also several [Bubble Tea examples][examples] available and, of course,
there are [Go Docs][docs].

[cmd]: https://github.com/charmbracelet/bubbletea/tree/main/tutorials/commands/
[examples]: https://github.com/charmbracelet/bubbletea/tree/main/examples
[docs]: https://pkg.go.dev/charm.land/bubbletea/v2?tab=doc

## 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)

### 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)

---

Part of [Charm](https://charm.sh).

<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>

Charm热爱开源 • Charm loves open source

---

### Overview (`tutorials/commands/README.md`)

# 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
understanding of [the first tutorial][basics].

You can find the non-annotated version of this program [on GitHub][source].

[basics]: https://github.com/charmbracelet/bubbletea/tree/main/tutorials/basics
[source]: https://github.com/charmbracelet/bubbletea/blob/master/tutorials/commands/main.go

## Let’s Go!

For this tutorial we’re building a very simple program that makes an HTTP
request to a server and reports the status code of the response.

We’ll import a few necessary packages and put the URL we’re going to check in
a `const`.

```go
package main

import (
    "fmt"
    "net/http"
    "os"
    "time"

    tea "charm.land/bubbletea/v2"
)

const url = "https://charm.sh/"
```

## The Model

Next we’ll define our model. The only things we need to store are the status
code of the HTTP response and a possible error.

```go
type model struct {
    status int
    err    error
}
```

## Commands and Messages

`Cmd`s are functions that perform some I/O and then return a `Msg`. Checking the
time, ticking a timer, reading from the disk, and network stuff are all I/O and
should be run through commands. That might sound harsh, but it will keep your
Bubble Tea program straightforward and simple.

Anyway, let’s write a `Cmd` that makes a request to a server and returns the
result as a `Msg`.

```go
func checkServer() tea.Msg {

    // Create an HTTP client and make a GET request.
    c := &http.Client{Timeout: 10 * time.Second}
    res, err := c.Get(url)

    if err != nil {
        // There was an error making our request. Wrap the error we received
        // in a message and return it.
        return errMsg{err}
    }
    // We received a response from the server. Return the HTTP status code
    // as a message.
    return statusMsg(res.StatusCode)
}

type statusMsg int

type errMsg struct{ err error }

// For messages that contain errors it's often handy to also implement the
// error interface on the message.
func (e errMsg) Error() string { return e.err.Error() }
```

And notice that we’ve defined two new `Msg` types. They can be any type, even
an empty struct. We’ll come back to them later in our update function.
First, let’s write our initialization function.

## The Initialization Method

The initialization method is very simple: we return the `Cmd` we made earlier.
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
}
```

## The Update Method

Internally, `Cmd`s run asynchronously in a goroutine. The `Msg` they return is
collected and sent to our update function for handling. Remember those message
types we made earlier when we were making the `checkServer` command? We handle
them here. This makes dealing with many asynchronous operations very easy.

```go
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {

    case statusMsg:
        // The server returned a status message. Save it to our model. Also
        // tell the Bubble Tea runtime we want to exit because we have nothing
        // else to do. We'll still be able to render a final view with our
        // status message.
        m.status = int(msg)
        return m, tea.Quit

    case errMsg:
        // There was an error. Note it in the model. And tell the runtime
        // we're done and want to quit.
        m.err = msg
        return m, tea.Quit

    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.Mod == tea.ModCtrl && msg.Code == 'c' {
			return m, tea.Quit
		}
    }

    // If we happen to get any other messages, don't do anything.
    return m, nil
}
```

## The View Function

Our view is very straightforward. We look at the current model and build a
`tea.View` accordingly:

```go
func (m model) View() tea.View {
    // If there's an error, print it out and don't do anything else.
    if m.err != nil {
        return tea.NewView(fmt.Sprintf("\nWe had some trouble: %v\n\n", m.err))
    }

    // Tell the user we're doing something.
    s := fmt.Sprintf("Checking %s ... ", url)

    // When the server responds with a status, add it to the current line.
    if m.status > 0 {
        s += fmt.Sprintf("%d %s!", m.status, http.StatusText(m.status))
    }

    // Send off whatever we came up with above for rendering.
    return tea.NewView("\n" + s + "\n\n")
}
```

## Run the program

The only thing left to do is run the program, so let’s do that! Our initial
model doesn’t need any data at all in this case, we just initialize it with
a `model` struct with default values.

```go
func main() {
    if _, err := tea.NewProgram(model{}).Run(); err != nil {
        fmt.Printf("Uh oh, there was an error: %v\n", err)
        os.Exit(1)
    }
}
```

And that’s that. There’s one more thing that is helpful to know about
`Cmd`s, though.

## One More Thing About Commands

`Cmd`s are defined in Bubble Tea as `type Cmd func() Msg`. So they’re just
functions that don’t take any arguments and return a `Msg`, which can be
any type. If you need to pass arguments to a command, you just make a function
that returns a command. For example:

```go
func cmdWithArg(id int) tea.Cmd {
    return func() tea.Msg {
        return someMsg{id: id}
    }
}
```

A more real-world example looks like:

```go
func checkSomeUrl(url string) tea.Cmd {
    return func() tea.Msg {
        c := &http.Client{Timeout: 10 * time.Second}
        res, err := c.Get(url)
        if err != nil {
            return errMsg{err}
        }
        return statusMsg(res.StatusCode)
    }
}
```

Anyway, just make sure you do as much stuff as you can in the innermost
function, because that’s the one that runs asynchronously.

## Now What?

After doing this tutorial and [the previous one][basics] you should be ready to
build a Bubble Tea program of your own. We also recommend that you look at the
Bubble Tea [example programs][examples] as well as [Bubbles][bubbles],
a component library for Bubble Tea.

And, of course, check out the [Go Docs][docs].

[bubbles]: https://github.com/charmbracelet/bubbles
[docs]: https://pkg.go.dev/charm.land/bubbletea/v2?tab=doc
[examples]: https://github.com/charmbracelet/bubbletea/tree/main/examples

## 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)

### 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)

---

Part of [Charm](https://charm.sh).

<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>

Charm热爱开源 • Charm loves open source

---

--- METRICS ---
- Files Extracted: 28
- Estimated Token Budget: ~10260 tokens
- Canonical Reference: https://codewiki.google/github.com/charmbracelet/bubbletea
