Skip to content

Conversation

@renovate
Copy link
Contributor

@renovate renovate bot commented Mar 24, 2025

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
github.com/charmbracelet/bubbletea v1.2.5-0.20241205214244-9306010a31ee -> v2.0.0-beta.1 age adoption passing confidence

Release Notes

charmbracelet/bubbletea (github.com/charmbracelet/bubbletea)

v2.0.0-beta.1

Compare Source

It’s time for Bubble Tea v2 Beta

We're very excited to announce the first beta release of Bubble Tea v2! This release includes a number of improvements and features over the last alpha 2 release.

🍔 New to v2? Looking to upgrade? Check out the full Bubble Tea v2 guide.

Changes Since Alpha 2

Since the last alpha 2 release, we've made a few changes and added some new features. Including restoring old Init behavior, actual cursor support, and some minor API name changes.

Init and Model Changes

We restored the old tea.Model interface and Init method changes. Now, Init returns a tea.Cmd as what it was before. The thought here is that it's more ergonomic and returning a tea.Model doesn't make much sense because during Init you already have a newly created model.

type Model struct
	// My model fields
}

func newModel() Model {
	return Model{
		// Initialize your model here
	}
}

// Before in alpha 2
func (m Model) Init() (tea.Model, tea.Cmd) {
	// But we already have a model, so why return a new one?
	return m, nil
}

// After in beta 1
func (m Model) Init() tea.Cmd {
	// Same as original Bubble Tea
	return nil
}
Cursor Support and View Interfaces

Yes, finally, you can control the cursor! We've added a new tea.CursorView interface that you can implement to control the cursor position and style. This is a long-awaited feature, and we're excited to see what you do with it.

Originally, tea.Model implemented a View method that returned a string. We changed this to be in its own tea.ViewModel interface and took it out of tea.Model. Now tea.Model only has Update and Init methods. Not implementing a View method is equivalent to disabling rendering. Both new interfaces are optional. The difference is that tea.CursorModel returns a tea.Cursor along with a string to control the cursor.

// Before
func (m Model) View() string {
	// How can I move the cursor?
	return "My awesome program! █" // <- fake cursor 😛
}

// After
func (m Model) View() (string, *tea.Cursor) {
	var c *tea.Cursor
	// Nil cursor means a hidden cursor
	if m.showCursor {
		// Move the cursor to the 21nd column right after the text
		c := tea.NewCursor(20, 0) // X: 20, Y: 0
		c.Style = tea.CursorBlock // 😎
		c.Blink = false
	}
	return "My awesome program!", c
}
Enhanced Keyboard Support

Terminologies matter. Previously, we introduced tea.EnableKeyboardEnhancements to request different keyboard enhancement features. We've renamed this to tea.RequestKeyboardEnhancement to signal that the program is requesting keyboard enhancements from the terminal and the terminal may or may not support them.

// Before in alpha 2
func (m Model) Init() (tea.Model, tea.Cmd) {
	return m, tea.EnableKeyboardEnhancements(
		tea.WithKeyReleases,
		tea.WithUniformKeyLayout,
	)
}

// After in beta 1
func (m Model) Init() tea.Cmd {
	return tea.RequestKeyboardEnhancement(
		tea.WithKeyReleases,
		tea.WithUniformKeyLayout,
	)
}

Like details?

Here’s the full changelog since v2.0.0-alpha.2


🌈 Feedback

Have thoughts on Bubble Tea v2 Alpha? We’d love to hear about it. Let us know on…


Part of Charm.

The Charm logo

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

v2.0.0-alpha.2

Compare Source

Are you excited about another alpha release?

We know we are! This release is chock full of features and improvements.

If you’re new to Bubble Tea v2 we recommending taking a look at the v2.0.0-alpha.1 release notes as well.

If you're already using v2.0.0-alpha.1 there's nothing new to do for v2.0.0-alpha.2. You can blissfully ignore everything below and gleefully reap the benefits…but don't you want to know what’s new?

Let's dive in!

🚚 Packages

This release was designed to work with Bubble Tea, Bubbles, and Lip Gloss v2 alphas with the same tag, so make sure you catch ’em all:

go get github.com/charmbracelet/bubbletea/v2@&#8203;v2.0.0-alpha.2
go get github.com/charmbracelet/bubbles/v2@&#8203;v2.0.0-alpha.2
go get github.com/charmbracelet/lipgloss/v2@&#8203;v2.0.0-alpha.2

🎩 Better Windows Input Support

We take Windows support seriously, and with this release, we've revamped the
Windows input handling. Now, Bubble Tea works even better on Windows.

Bubble Tea now takes advantage of the Windows Console API to
get the terminal's size, focus events, and advanced keyboard and mouse
handling. This won't interfere with the existing escape sequence handling, so
you can still use the same code across different platforms.

🪄 Program-level Color Profiles and Styles

Bubble Tea now automatically downsamples ANSI, when necessary, for the appropriate output. For example, if you're setting 24-bit (TrueColor) colors in your application, and the user is using Apple Terminal, the colors will be automatically downsampled to the nearest colors in the 256 color spectrum. Or, if output's being directed to a file, colors will be stripped entirely.

Even better, this works with colors and styling generated by anything, not just Lip Gloss.
That means you can flow in ANSI styling from any tool or library and rest assured that colors
will just work with the user's terminal. This is all thanks to our new
magical colorprofile
library.

Detecting the color profile

Need to use the detected color profile in your app? Listen to tea.ColorProfileMsg in Update:

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.ColorProfileMsg:
        m.colorProfile = msg.Profile // gottem!
    }
    return m, nil
}
Manually applying a color profile

What's that you say? You want to manually set a color profile for testing? Now you can, on the program level.

import (
    "github.com/charmbraclelet/bubbletea/v2"
    "github.com/charmbraclelet/colorprofile"
)

p := colorprofile.TrueColor // i love colors. lets' use 16,777,216 of 'em
p = colorprofile.ANSI256    // jk, 256 colors are plenty
p = colorprofile.ANSI       // actually let's juse use 16 colors
p = colorprofile.Ascii      // nm, no colors, but keep things like bold, italics, etc.
p = colorprofile.NoTTY      // lol actually strip all ANSI sequences

prog := tea.Program(tea.WithColorProfile(p))

Want to hard detect the color profile in wish? We bet you do.

func main() {
    var s ssh.Session
    pty, _, _ := s.Pty()

    // Get the environment...
    envs := append(s.Environ(), "TERM="+pty.Term)

    // ...and give it to Bubble Tea so it can detect the color profile.
    opt := tea.WithEnvironment(envs)

    p := tea.NewProgram(model,
        tea.WithInput(pty.Slave),
        tea.WithOutput(pty.Slave),
        opt, // wow
    )
}

Going deep

Hungry for more? This is for the more hard core terminal devs, like you.

😮 An Acual Cursor

Another nice feature is the ability to use a real terminal
cursor. If you enable the cursor visibility, you can control the cursor
position and shape in your program.

func (m model) Init() (tea.Model, tea.Cmd) {
    return m, tea.Batch(
        tea.ShowCursor,
        tea.SetCursorStyle(tea.CursorBlock, false),     // non-blinking block cursor
        tea.SetCursorStyle(tea.CursorUnderline, true),  // blinking underline cursor
        tea.SetCursorStyle(tea.CursorBar, true),        // blinking vertical bar cursor
    )
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "q":
            return m, tea.Quit
        }
    }
    // move the cursor to the second row and first column of the program screen.
    return m, tea.SetCursorPosition(0, 1)
}

func (m model) View() string {
    return "Hey there!\n <- I'm a cursor!"
}

You can also request the cursor position by using the tea.RequestCursorPosition command.

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyPressMsg:
        switch msg.String() {
        case "enter":
            return m, tea.RequestCursorPosition
        case "q":
            return m, tea.Quit
        }
    case tea.CursorPositionMsg:
        m.cursorPosition.X, m.cursorPosition.Y = msg.X, msg.Y
    }
    return m, nil
}

We’re still in the process of integrating true cursor support into textarea and textinput in Bubbles, however a true cursor is now at your disposal.

🗝️ Uniform Key Handling

One of the biggest changes coming to Bubble Tea v2 is enhanced keyboard
handling. This includes support for more key events, and complex key
combinations, as well as key releases.

When targeting users on different platforms and keyboard layouts, it's
important to have a consistent and reliable way to handle key events. With this
release, you can now use tea.WithUniformKeyLayout keyboard enhancement to
ensure that your program works as expected across different keyboard layouts.

For example, on a multi-layout QWERTY keyboard, ctrl+a should always
trigger a ctrl+a event, regardless of the layout language. Similarly, shift+h should send a shift+h event with the letter H as its
printable value. But what happens when you press shift+h in a
different QWERTY layout?

Let's take the PC-101 QWERTY Arabic layout as an example. In this layout, shift+h corresponds to the Arabic letter أ. If you're building a
game or an application that relies on key events where you don't really care
about the actual printable value of the key press, you'd want to ensure that shift+h always sends a shift+h event despite the layout language.

This will also respect the keyboard layout and send the correct printable
value for the key press. For example, on a US QWERTY keyboard, ctrl+a
corresponds to the first letter on the second row of the keyboard. On a French
AZERTY keyboard, the same key combinations correspond to ctrl+q.

To achieve this, you can use the tea.WithUniformKeyLayout option and let Bubble
Tea handle the rest.

p := tea.NewProgram(model, tea.WithEnhancedKeyboard(
        tea.WithKeyReleases, // Do we want to listen to key releases?
        tea.WithUniformKeyLayout,
    ))

// Later in your program's Update function.
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyPressMsg:
        switch msg.String() {
        case "ctrl+a":
            // Handle ctrl+a event regardless of the keyboard layout.
        case "shift+h":
            // Handle shift+h event regardless of the keyboard layout and get the correct printable value.
        }
    }
    return m, nil
}
💬 Requesting Terminal Properties

With this release, we made it explicit how to request terminal properties. Use
the tea.Request... commands to get the terminal's size, background color,
foreground color, etc.

// Before
func (m model) Init() (tea.Model, tea.Cmd) {
    return m, tea.BackgroundColor
}

// After
func (m model) Init() (tea.Model, tea.Cmd) {
    return m, tea.RequestBackgroundColor
}

The following Cmds are now at your disposal:

  • tea.RequestBackgroundColor (sends a tea.BackgroundColorMsg)
  • tea.RequestForegroundColor (sends a tea.ForgroundColorMsg)
  • tea.RequestCursorColor (sends a tea.CursorColorMsg)
  • tea.RequestWindowSize (sends a tea.WindowSizeMsg)
  • tea.RequestCursorPosition (sends a tea.CursorPostionMsg)
🍇 Grapheme Clustering

Grapheme what? In short, grapheme clustering is a way to handle Unicode text
segmentation and boundaries. It's useful when you want to determine the terminal
cell width a string or a grapheme cluster occupies. The algorithm is defined
in the Unicode Text Segmentation UAX #​29.

For example, '🧑‍🌾' is a single grapheme cluster, but it's made up of 3
UTF-8 codepoints. Terminals that support grapheme clustering will treat this as
a 2 cell wide character, while those that don't might treat it as 2, 4, 5, or
even 6 cells wide. Our friend Mitchell Hashimoto has a great blog post on this
topic: Grapheme Clusters and Terminal Emulators.

We've added grapheme clustering and mode 2027 support in the previous alpha.1
release, and it was on by default. We've noticed that some terminals, like
Apple Terminal, don't play well with this feature. Specifically, with the
DECRQM control sequence. Not cool.

Now we're making the feature opt-in and off by default. You can still enable it by
using the tea.WithGraphemeClustering option.

p := tea.NewProgram(model, tea.WithGraphemeClustering())

// Or in your program's Init function.
func (m model) Init() (tea.Model, tea.Cmd) {
    return m, tea.EnableGraphemeClustering
}

Want to learn more about grapheme clusters? Mitchell Hashimoto has a great post about it.

Changelog

New Contributors

Full Changelog: charmbracelet/bubbletea@v2.0.0-alpha.1...v2.0.0-alpha.2


🌈 Feedback

Have thoughts on Bubble Tea v2 Alpha? We’d love to hear about it. Let us know on…


Part of Charm.

The Charm logo

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

v2.0.0-alpha.1

Compare Source

Who’s ready for Bubble Tea v2 Alpha?

We’re so excited for you to try Bubble Tea v2! Keep in mind that this is an alpha release and things may change.

[!NOTE]
We don't take API changes lightly and strive to make the upgrade process as simple as possible. We believe the changes bring necessary improvements as well as pave the way for the future. If something feels way off, let us know.

Here are the the big things to look out for in v2:

Key handling is way better now

We added support for way, way, way better key handling in newer terminals. For example, now you can map to things like shift+enter and super+space, as long as the terminal supports progressive keyboard enhancement. You can also detect key releases too (we're looking at you, terminal game developers).

Init looks more like Update

We changed Init()’s signature to match Update() to make programs easier to follow and to make swapping models easier.

Upgrading

Upgrading to Bubble Tea v2 is easy. Just update your imports and follow the
instructions below.

go get github.com/charmbracelet/bubbletea/v2@&#8203;v2.0.0-alpha.1

### If you're using Bubbles you'll also want to update that.
### Note that Huh isn't supported in v2 yet.
go get github.com/charmbracelet/bubbles/v2@&#8203;v2.0.0-alpha.1
Init() signature

Change your Model's Init() signature to return a tea.Model and a tea.Cmd:

// Before:
func (m Model) Init() Cmd
    // do your thing
    return cmd
}

// After:
func (m Model) Init() (Model, Cmd)
    // oooh, I can return a new model now
    return m, cmd
Why the change?

Now you can use Init to initialize the Model with some state. By following this pattern Bubble Tea programs become easier to follow. Of course, you can still initialize the model before passing it to Program if you want, too.

It also becomes more natural to switch models in Update, which is a very useful
way to manage state. Consider the following:

func (m ListModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg.(type) {
    case SwitchToEditorMsg:
        // The user wants to edit an item. Switch to the editor model.
        return EditorModel{}.Init()
    }

    // ...
}

While the change to Init() may seem big, in practice we've found that it doesn't take much to update existing programs.

Keyboard enhancements (optional)

With Bubble Tea v2, you can get more out of your terminal. Progressive keyboard enhancements, allow you to use more key combinations. Just use the tea.WithKeyboardEnhancements option when creating a new program to get all the keys, in supported terminals only.

You can enable enhanced keyboard support by passing the tea.WithKeyboardEnhancements option to tea.NewProgram or by using the tea.EnableKeyboardEnhancements command.

p := tea.NewProgram(model, tea.WithKeyboardEnhancements())

// Or in your `Init` function:
func (m Model) Init() (tea.Model, tea.Cmd) {
    return m, tea.EnableKeyboardEnhancements()
}

By default, release events aren't included, but you can opt-into them with the tea.WithKeyReleases flag:

tea.WithKeyboardEnhancements(tea.WithKeyReleases)   // ProgramOption
tea.EnableKeyboardEnhancements(tea.WithKeyReleases) // Cmd

You can detect if a terminal supports keyboard enhancements by listening for tea.KeyboardEnhancementsMsg after enabling progressive enhancements.

func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyboardEnhancementsMsg:
        // More keys, please!
    }
}

[!NOTE]
This feature is enabled by default on Windows due to the fact that we use the Windows Console API to support other Windows features.

Which terminals support progressive enhancement?
Key messages

Key messages are now split into tea.KeyPressMsg and tea.KeyReleaseMsg. Use
tea.KeyMsg to match against both.

// Before:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        // I'm a key press message
    }
}

// After:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        // I'm a key press or release message
        switch key := msg.(type) {
        case tea.KeyPressMsg:
            // I'm a key press message
            return m, tea.Printf("You pressed %s", key)
        case tea.KeyReleaseMsg:
            // I'm a key release message ;)
            return m, tea.Printf("Key released: %s", key)
        }
    }
}

We no longer have key.Type and key.Runes fields. These have been replaced
with key.Code and key.Text respectively. A key code is just a rune that
represents the key message. It can be a special key like tea.KeyEnter,
tea.KeyTab, tea.KeyEscape, or a printable rune.

// Before:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.Type {
        case tea.KeyEnter:
            // Enter key
        case tea.KeyRune:
            // A printable rune
            switch msg.Runes[0] {
            case 'a':
                // The letter 'a'
            }
        }
    }
}

// After:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.Code {
        case tea.KeyEnter:
            // Enter key
        case 'a':
            // The letter 'a'
        }
    }
}

The new key.Text field signifies a printable key event. If the key event has
a non-empty Text field, it means the key event is a printable key event. In
that case, key.Code is always going to be the first rune of key.Text.

// Before:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        if msg.Type == tea.KeyRune {
            // A printable rune
            switch string(msg.Runes) {
            case "😃":
                // Smiley face
            }
        }
    }
}

// After:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        // A printable rune
        switch msg.Text {
        case "😃":
            // Smiley face
        }
    }
}

Instead of matching against msg.Type == tea.KeyCtrl... keys, key modifiers
are now part of the key event itself as key.Mod. Shifted keys now have their
own key code in key.ShiftedCode. Typing shift+b will produce
key.Code == 'b', key.ShiftedCode == 'B', key.Text == "B", and key.Mod == tea.ModShift.

// Before:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.Type {
        case tea.KeyCtrlC:
            // ctrl+c
        case tea.KeyCtrlA:
            // ctrl+a
        default: // 🙄
            // can i catch all ctrl+<key> combinations?
            if msg.Alt {
                // but i want to catch ctrl+alt+<key> combinations too 🤔
                return m, tea.Printf("idk what to do with '%s'", msg)
            }
            switch msg.Runes[0] {
            case 'B': // shift+a
                // ugh, i forgot caps lock was on
                return m, tea.Printf("You typed '%s'", msg.Runes)
            }
        }
    }
}

// After:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.Mod {
        case tea.ModCtrl: // We're only interested in ctrl+<key>
            switch msg.Code {
            case 'c':
                // ctrl+c
            case 'a':
                // ctrl+a
            default:
                return m, tea.Printf("That's an interesting key combo! %s", msg)
            }
        default:
            if msg.Mod.Contains(tea.ModCtrl|tea.ModAlt) {
                return m, tea.Printf("I'm a '%s' 😎!", msg)
            }
            if len(msg.Text) > 0 {
                switch msg.String() {
                case "shift+b":
                    // It doesn't matter if caps lock is on or off, we got your back!
                    return m, tea.Printf("You typed '%s'", msg.Text) // "B"
                }
            }
        }
    }
}

The easiest way to match against key events is to use msg.String():

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "space":
            // Space bar returns "space" now :D
            return m, tea.Println("You pressed the space bar!")
        case "ctrl+c":
            // Copy to the clipboard.
            return m, tea.SetClipboard("Howdy")
        case "ctrl+v":
            // Read the clipboard (not supported by all terminals).
            return m, tea.ReadClipboard()
        case "alt+enter":
            // Fullscreen mode, anyone?
        case "shift+x":
            // Just an upper case 'x'
            return m, tea.Println("You typed %q", msg.Text) // "X"
        case "shift+enter":
            // Awesome, right?
        case "ctrl+alt+super+enter":
            // Yes, you can do that now!
        }
    }
}

Oh, and we finally changed space bar to return "space" instead of " ". In
this case, key.Code == ' ' and key.Text == " ".

Paste messages

Bracketed-paste has its own message type now. Use tea.PasteMsg to match
against paste events.

// Before:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        if msg.Paste {
            // That's weird, I'm a paste message
            m.text += string(msg.Runes)
        }
    }
}

// After:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        // I'm a key message
    case tea.PasteMsg:
        // Here comes a paste!
        m.text += string(msg)
    }
}
Mouse messages

We've also improved the mouse API, which is a breaking change. Use
tea.MouseMsg to match against different types of mouse events. Mouse messages
are split into tea.MouseClickMsg, tea.MouseReleaseMsg, tea.MouseWheelMsg,
and tea.MouseMotionMsg.

// Before:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.MouseMsg:
        switch msg.Button {
        case tea.MouseButtonWheelUp:
            // Whee!
        case tea.MouseButtonLeft:
            // Clickety click
        }
    }
}

// After:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.MouseMsg:
        mouse := msg.Mouse()
        switch msg := msg.(type) {
        case tea.MouseClickMsg:
            switch msg.Button {
            case tea.MouseLeft:
                // Clickety click
            }
        case tea.MouseReleaseMsg:
            // Release the mouse!
        case tea.MouseWheelMsg:
            // Scroll, scroll, scrollllll
        case tea.MouseMotionMsg:
            // Where did the mouse go?
        }

        return m, tea.Printf("%T: (X: %d, Y: %d) %s", msg, mouse.X, mouse.Y, mouse)
    }
}

Other new things

Native clipboard support

Bubble Tea now supports native clipboard operations, also known as OSC52.
This means you can even copy and paste over SSH!

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "ctrl+c":
            // Copy to the clipboard.
            return m, tea.SetClipboard("Howdy")

        case "ctrl+v":
            // Read the clipboard (not supported by all terminals).
            return m, tea.ReadClipboard()
        }

    case tea.ClipboardMsg:
        // Here's the system clipboard contents.

    case tea.PrimaryClipboardMsg:
        // Only on X11 and Wayland ;)
    }
}

X11 and Wayland users can also use tea.SetPrimaryClipboard to set the primary
clipboard. Note that this is a very niche sort of thing and may or may not
work on macOS, Windows, and other platforms without the notion of more than one
clipboard.

Bracketed-paste messages

Bubble Tea v1 supports bracketed-paste mode by default. It sends paste events
as a tea.KeyMsg with msg.Paste flag set to true, which to be honest, was
pretty confusing.

In Bubble Tea v2, paste events are sent as their own tea.PasteMsg message. If
you don't care about the content and want to listen to paste start/end events,
you can use tea.PasteStartMsg and tea.PasteEndMsg.

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.PasteStartMsg:
        // The user started pasting.

    case tea.PasteEndMsg:
        // The user stopped pasting.

    case tea.PasteMsg:
        // Here comes a paste!
        m.text += string(msg)
    }
}
Detecting terminal colors

You can now read and set the terminal's foreground, background, and cursor
colors. This is useful for creating advanced terminal applications.

To change the terminal's colors, use tea.SetForegroundColor, tea.SetBackgroundColor,
and tea.SetCursorColor.

// Read the terminal's colors when the program starts.
func (m Model) Init() (tea.Model, tea.Cmd) {
    return m, tea.Batch(
        tea.ForegroundColor,
        tea.BackgroundColor,
        tea.CursorColor,
    )
}

// Change the terminal's colors when the user presses enter and reset on exit.
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.BackgroundColorMsg:
        return m, tea.Printf("Background color: %s\n", msg)
    case tea.ForegroundColorMsg:
        return m, tea.Printf("Foreground color: %s\n", msg)
    case tea.CursorColorMsg:
        return m, tea.Printf("Cursor color: %s\n", msg)
    case tea.KeyPressMsg:
        switch msg.String() {
        case "enter":
            return m, tea.Batch(
                tea.SetForegroundColor(ansi.Red),
                tea.SetBackgroundColor(ansi.Green),
                tea.SetCursorColor(ansi.Blue),
            )
        case "esc":
            return m, tea.Quit
        }
    }

    return m, nil
}
Mode 2027 (grapheme clustering)

Most terminals use wcwidth to calculate the width of characters displayed on
the screen. This becomes a problem when you have characters that are composed
of multiple codepoints, like emojis or characters with diacritics. Mode 2027
tries to solve this issue by informing the terminal that it should use grapheme
clusters instead of codepoints to calculate the width of characters.

In Bubble Tea v2, this feature is enabled by default. It's a no-op on terminals
that don't support it. You can disable it by passing the
WithoutGraphemeClustering option.

Terminal version and name

Don't know what terminal you're running in? $TERM is too vague? Bubble Tea
now has a tea.TerminalVersion command that queries the terminal for its name
and version using XTVERSION control sequence.

[!NOTE]
This feature is not supported by all terminals.

func (m Model) Init() (tea.Model, tea.Cmd) {
    return m, tea.TerminalVersion
}

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.TerminalVersionMsg:
        fmt.Printf("Terminal: %s\n", string(msg))
    }
}
Terminfo and Termcap capabilities

Sometimes you need to know what capabilities the terminal has. Bubble Tea now
has a tea.RequestCapability command that queries the terminal for a specific
terminfo/termcap capability.

[!NOTE]
This feature is not supported by all terminals.

func (m Model) Init() (tea.Model, tea.Cmd) {
    return m, tea.RequestCapability("Ms") // Ms is the terminfo capability for clipboard support
}

Changelog

New Features
  • 7cb34becafd6eb1716a8eb4251da44fded6376ea: (v2) feat: combine keyboard enhancements into a nicer API (#​1152) (@​aymanbagabas)
  • 5cf8c29adcb2b524d79e90e96424f2d960b5136c: feat(example): add example to query the terminal (@​aymanbagabas)
  • cdc250332b587f456eaf7f20511cdc03719513e7: feat(example): add example to set-terminal-color's (@​aymanbagabas)
  • e16559d1b7c26d7b103b2332587bb853a34f9830: feat(examples): add print-key example (@​aymanbagabas)
  • eacd93085e4b3f652e94136cc3407f4d57da37c8: feat(examples): add request-capability example (@​aymanbagabas)
  • 3e87bf8e629122629866c7f6f57d271ae3b05ce8: feat: add Enable/Disable focus report commands (@​caarlos0)
  • fa03b597f4374c1c69ab168dd67d97557848a971: feat: add custom renderer option (@​aymanbagabas)
  • 8e406ff9e7baecb58499d3d2b91de290709593aa: feat: add kitty keyboard options and settings (#​1083) (@​aymanbagabas)
  • 0126cafd1d21f7d93a0f15b6f035c436c117422e: feat: add kitty keyboard options and settings (@​aymanbagabas)
  • b2e983a1f12f62e268ba9ff22174a4b8b632134a: feat: add mode 2027 grapheme clustering stubs (#​1105) (@​aymanbagabas)
  • c385aba97b9057c29e03e5c2398b686eb2207b7a: feat: add renderer Resize and InsertAbove (@​aymanbagabas)
  • ed7dc3d175db6b480e02ef570242a255dbf5f078: feat: add support for win32-input-mode (@​aymanbagabas)
  • 53d72b46241cc74e4b693af3ec4634e0611c548c: feat: add the ability to change the renderer output (@​aymanbagabas)
  • 76fc19dcb2c6efab763eee624bb385b213779fda: feat: detect key input grapheme runes (@​aymanbagabas)
  • 70853cb8d020178bc26f6771d65c8456d18be6fe: feat: drop erikgeiser/coninput in favor of x/windows (@​aymanbagabas)
  • 05f8399a93346e03f9c0ece6ceda29d4b1ad6820: feat: dry setting and retrieving terminal modes (@​aymanbagabas)
  • 05e8ecbec359c549be3796df0055c046717cd786: feat: enable focus reporting (#​1081) (@​aymanbagabas)
  • c629efbf424d805e210b775086940954a83c21e6: feat: enable focus reporting (@​caarlos0)
  • 033071be4d8e12d32e582f1879e9eb285bda6566: feat: export kitty/xterm/win32 input commands and messages (@​aymanbagabas)
  • 0825f6113d094029bdfad36f400eca847206f3f0: feat: export nil renderer (@​aymanbagabas)
  • 1f5a28f82d67c788c68336b9780b834aea3104a8: feat: expose the renderer interface (#​1094) (@​aymanbagabas)
  • 6f7f9e64924f0ff5ac9b63c50251a421c3f374a2: feat: expose the renderer interface (@​aymanbagabas)
  • ea5460529abf017cb04a11f9b77482a21d7735a3: feat: introduce ExecuteSequence to send the terminal arbitrary sequence (@​aymanbagabas)
  • 03df14ca89936f39489054c744712a80e6e06dfa: feat: kitty: support associated text keyboard enhancement (@​aymanbagabas)
  • 485e52eedd4c2a85bd6307819443bc5742ffe864: feat: query and set terminal background, foreground, and cursor colors (#​1085) (@​aymanbagabas)
  • 44d4973291755341228eae204041dfccafade339: feat: query and set terminal background, foreground, and cursor colors (@​aymanbagabas)
  • ad68fc1a72af5e05ac63961d3d0c2888f8c5800f: feat: query modify other keys (@​aymanbagabas)
  • c657bba74b48c6f4055aeaf1f4d4422a7779f71a: feat: query the terminal version string and primary device attrs (#​1088) (@​aymanbagabas)
  • 26bc2cb52ec23285c9c56294b8125da1bc973a13: feat: query the terminal version string and primary device attrs (@​aymanbagabas)
  • 73773e861ab18e89817d9a5888f2d74d55833be0: feat: recognize nested sequence msgs (@​aymanbagabas)
  • 6062461b06a97737b42e4700c26e56982a0f8c1f: feat: support setting and querying the terminal clipboard using OSC52 (#​1086) (@​aymanbagabas)
  • 04f843d2f9748b354b17de3413d4d7556421aec3: feat: support setting and querying the terminal clipboard using OSC52 (@​aymanbagabas)
  • 828ff70fefa26e7784c451278a0fa21cd2268ada: feat: support xterm modifyOtherKeys keyboard protocol (#​1084) (@​aymanbagabas)
  • 1678d85ce2bb1609f5982687e95d7ff5d4b0d126: feat: use bubbles/v2 and bubbletea/v2 in examples and tutorials (@​aymanbagabas)
  • 3faa9b30193ab9cd82801b586c8cdcb657fd7edd: feat: use kitty report alternate keys with enhanced keyboard (@​aymanbagabas)
  • 0fe006d28f90c23931626ad4d8c37676f73e1505: feat: use the new renderer interface (@​aymanbagabas)
Bug fixes
  • 448eb829daa1d9d6ba14ecfffb3a1d9b011c5b8f: fix(ci): skip CI for examples/tutorials running go1.18 (@​aymanbagabas)
  • 5df8f28dced4592193ae0973579169c3cd7ee9c9: fix(examples): keys shouldn't be routed to viewport in chat example (@​meowgorithm)
  • 356c649672fc57ca0a478249e5afe5aa71b33171: fix(examples): update testdata (@​aymanbagabas)
  • 9d02251b66f8a79a87e8a70f8393dd9e6cc15c0c: fix(key): use the key text value when keycode is multirune (@​aymanbagabas)
  • 0c8967d8a973cd9cb70bfe5ff03ed0f6cf61acc0: fix(kitty): only use printables (@​aymanbagabas)
  • 3f5fb9a3f9d6b5f84330f78cd7879d0e40d5bbe9: fix(lint): reorder key struct to fix fieldalignment (@​aymanbagabas)
  • 508be2e53c54680a167a4bfc849e3db036e0183b: fix: force query the terminal bg before running any programs (@​aymanbagabas)
  • 7df684aaaa89d3c9adbff865831d26b87da0d202: fix: handle Kitty keyboard protocol extensions to legacy sequences (@​aymanbagabas)
  • 8635fb30c3619ee5e8de5f0da459676be7c6146b: fix: hide cursor if needed after restore (@​aymanbagabas)
  • d1827e4cd166338e94a2d0587d5dd808f50a813e: fix: ignore invalid XTGETTCAP responses (@​aymanbagabas)
  • 1bd66e6fb2aa6746632a773e93642831055899d7: fix: ignore nil terminal colors (@​aymanbagabas)
  • a6725ba1b454b493e14fc6339123c244cfb02523: fix: implement RequestCapability and unexport and remove internal messages and commands (@​aymanbagabas)
  • a26ecc5e91bf27cfff779983a1c2ba3bc53dda13: fix: implement String() method for MouseButton type (@​aymanbagabas)
  • 0dd62108ee8a28efa08d05328f6070cca7683474: fix: initialize the terminal before the renderer (@​aymanbagabas)
  • 9660d7d1a3afe4e2bcf1c92bcc64a9ec9fa34dbe: fix: keep track of terminal capabilities and gracefully turn them off (@​aymanbagabas)
  • 4d2072bfecd6cc3c97c4895210ffa58b6e942659: fix: kitty: request protocol flags and rename flag methods (@​aymanbagabas)
  • bdb32370f6a11f6d3cea67dc11763a7bca9b42a1: fix: kitty: request protocol flags and rename flag methods (@​aymanbagabas)
  • c0f9975e1f2a57731380dd9d1becff9d92602066: fix: lint errors (@​aymanbagabas)
  • fa69e03c9e73704edab63868262a5a7c3125499d: fix: lint issues (#​1109) (@​aymanbagabas)
  • 4e118e9b7ec01d8c13bf432ce301de80a6dd1fca: fix: lint issues (@​aymanbagabas)
  • 7567352f1ad55193b0b8be989a0eb1068da4a379: fix: mouse sequence enable/disable order (@​aymanbagabas)
  • 45222dfde877e96d636add74f3dec47cf1fc6bad: fix: only reset terminal colors if they're changed (@​aymanbagabas)
  • 695fbf3d4e29bcfdd762af0a405e7ac352453e02: fix: only shutdown the program once (@​aymanbagabas)
  • 88a5cd2192a3b2cd9efa0c4407be337a999875bc: fix: parse invalid utf8 sequences (@​aymanbagabas)
  • cb37f8847e86f7493c9d6dab3ab26531b65091c8: fix: rename events to messages (@​aymanbagabas)
  • 614aa93d2c19e321530d824dd1af722a26ed2ece: fix: renderer: don't return a Cmd on update (@​aymanbagabas)
  • 516b7cd4dcd0550479d85975e80b4502d171c2c6: fix: renderer: nilRenderer doesn't need to be exported (@​aymanbagabas)
  • 3650670bdb938ae61a46dcfd9f8b753708b06212: fix: reset terminal colors on exit using osc 110/111/112 (@​aymanbagabas)
  • ceaed4d6b23f5bc77a498a839d832f51a946e88f: fix: restore terminal colors (@​aymanbagabas)
  • c9f2a56893a069ef01fb358a9214eec4e28dbc08: fix: screen test (@​aymanbagabas)
  • fe88dd538b7d0b9b73b36ad68df89cd399c41029: fix: show the cursor on exit (@​aymanbagabas)
  • 544a715e237541286cb12261e10de764fb164329: fix: simplify instantiating a new standard renderer (@​aymanbagabas)
  • c630d5e94e5b1c7dd31ddf6a441e034ae533b4bd: fix: simplify kitty keyboard msg flags (@​aymanbagabas)
  • c2c195c51afa9cc25a10564482117149aa33b8bf: fix: special case modified f3 key and cursor pos report (@​aymanbagabas)
  • 4fa1f0608cca7ef22d8eb97fe57793a975c86baa: fix: terminal colors tests (@​aymanbagabas)
  • 62e46fe66fc5d826e2e1456aeb1586e4145fb8dc: fix: unexport kitty, modifyOtherKeys, and windowsInputMode options (@​aymanbagabas)
  • 4967f6b3e6d99d632e5d036a9db5f42bf1a7f1bd: fix: unexport standardRenderer (@​aymanbagabas)
  • f2bdd36fb2feb874216cd4ee03b0fa9011258035: fix: use KeyRunes to indicate text input (@​aymanbagabas)
  • e206b363d1e6071e325fab5d8cdbcdbd0b64e461: fix: use explicit names for kitty keyboard option (@​aymanbagabas)
  • 96364131070c6fab3ce7e9264d566ff8b6e76c5a: fix: use safeWriter to guard writing to output (@​aymanbagabas)
  • 2f6637bdbd54467cfbd755977bcdf4c99a32b22b: fix: windows driver build (@​aymanbagabas)
  • da83499c21d243e4b8a682b08f72085fdda5578f: fix: windows: correctly parse upper/lower key (@​aymanbagabas)
Documentation updates
  • fe54df7a74004584a9ff7883634cdb6b64241fdc: docs(examples): add help view to table example (@​meowgorithm)
  • 3274e41ecb94a7a3ad620e1accff35ca2a0b41f0: docs(tutorials): upgrade tuts to v2 (#​1155) (@​meowgorithm)
  • 2f14548625a1b492196996a1700f7566d08a2483: docs: add godoc examples (@​aymanbagabas)
  • 210358daec801b5c65a80859f18dd9c84c24f2ab: docs: add v2 todo code reminders (@​aymanbagabas)
  • de4788dc763d5a6ce7ca555c5ee6fce3179dedc4: docs: update readme badge images (@​aymanbagabas)
Other work
  • 85c5adc127b3bf4fffb4bd776f1692b0f1d6858c: (v2) Export different input mode commands and messages (#​1119) (@​aymanbagabas)
  • 8a75439c1bad4969b4da9526a7cbc68b7633265a: (v2) Use KeyMsg/MouseMsg interfaces (#​1111) (@​aymanbagabas)
  • 3ef72f278e9f0825904462544203e170c0f9718a: Fix Windows API & Add support for win32-input-mode (#​1087) (@​aymanbagabas)
  • ad68c4205f7d96afa4a4c72087dec81fa3ff2931: feat!: make Init return the model (#​1112) (@​aymanbagabas)
  • e8903bbd92e3f939e810cddefc001b97704ec1bc: feat!: use KeyExtended to signify more than one rune (@​aymanbagabas)
  • 14cb6b5de1d2d7e11234ccad4031bd651bd48ade: feat!: v2: update module path to github.com/charmbracelet/bubbletea/v2 (@​aymanbagabas)
  • 6e060ca1bd4c3c676a785ce31a422476c9022c35: refactor!: remove backwards compatibility (@​aymanbagabas)
  • 7b87642c8759c34f599989502961103a5b651305: refactor!: use key/mouse msg interfaces (@​aymanbagabas)
  • 3075646dfdf4c2ef54aca19934c115f925dbfc24: refactor: bracketed-paste active state (@​aymanbagabas)
  • ffe0133d436a675918b2f8960a08e0c9006d3fa2: refactor: change kitty keyboard flag name for clarity (@​aymanbagabas)
  • ae0c2735752b598b540d61801bc6b2823af85e2c: refactor: check the initial size during Run (@​aymanbagabas)
  • 7a49b33ec5b3d40ee00118a86f58e1a9cc7a9fae: refactor: define renderer execute to write queries to the terminal (@​aymanbagabas)
  • 16f706ac13a18de9ef561c95cc0bb8b802bdea13: refactor: expose key codes and define key/mouse interfaces (@​aymanbagabas)
  • 4247aac6d7eb6d8c74653311897e427272b5c0de: refactor: flatten csi sequence parsing (@​aymanbagabas)
  • 0c1a6a4b476df6af3c728e031e439c86710867cc: refactor: reimplement the event loop with a sequence parser (#​1080) (@​aymanbagabas)
  • 13ffcad06c58001658d5c0d2ef58990da39183e0: refactor: reimplement the event loop with a sequence parser (@​aymanbagabas)
  • 370d24867c0d58ae0b0c729c584ed2ded3cc5542: refactor: remove kitty protocol flags (@​aymanbagabas)
  • 3981b80fce82bf800906767e302fadef7d455821: refactor: remove tea.ExecuteSequence (@​aymanbagabas)
  • eb2eee4d987964dbfd3fd6df07e962aeef0567fd: refactor: rename KeySym to KeyType and make KeyRunes the default (@​aymanbagabas)
  • ec5b3626a454315750b94ecc29b34ad07d65956e: refactor: rename query kitty keyboard command (@​aymanbagabas)
  • e0865cfed36c594d6be4ea783548d03d8f6efeca: refactor: simplify key modifier matching (@​aymanbagabas)
  • 1f88b9ed6e9cdec8f990ba8ac33b0546badca790: refactor: simplify renderer interface (@​aymanbagabas)
  • 39ea34c830d1788384982a60125aa505492057d9: refactor: unexport kitty keyboard settings and options (@​aymanbagabas)
  • 5f7700e13268e9003fb7cff5bd1e7e2f9f3b18cc: refactor: unexport modify other keys (@​aymanbagabas)
  • c47c2b9c8fc1802fda68b468ccb2f6f44bac992a: refactor: unexport win32 input mode (@​aymanbagabas)
  • f31a5f352add858415632bee9911d33a5e6120a6: refactor: we don't care about renderer render errors (@​aymanbagabas)

Feedback

Have thoughts on Bubble Tea v2 Alpha? We’d love to hear about it. Let us know on…


Part of Charm.

The Charm logo

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

v1.3.4

Compare Source

This release fixes an issue on Windows where the mouse is always enabled even if it wasn't requested. Now, using mouse options such as tea.WithAllMouseMotion() and commands such as tea.EnableMouseAllMotion and tea.DisableMouse turns the mouse on/off as expected.

Changelog

New Features
Bug fixes
Other work

The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or on Discord.

v1.3.3

Compare Source

This release restore the program options that were deprecated in the previous releases.

Changelog

Full Changelog: charmbracelet/bubbletea@v1.3.2...v1.3.3


The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or on Discord.

v1.3.2

Compare Source

Fix canceling terminal input reads on Windows.

Changelog

Bug fixes

The Charm logo

Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse


Configuration

📅 Schedule: Branch creation - "before 6am on monday" (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@ilo-nanpa
Copy link

ilo-nanpa bot commented Mar 24, 2025

it seems like you haven't added any nanpa changeset files to this PR.

if this pull request includes changes to code, make sure to add a changeset, by writing a file to .nanpa/<unique-name>.kdl:

minor type="added" "Introduce frobnication algorithm"

refer to the manpage for more information.

@renovate renovate bot force-pushed the renovate/major-go-deps branch from 4074ab6 to 37a6dac Compare March 26, 2025 19:12
@renovate renovate bot changed the title fix(deps): update module github.com/charmbracelet/bubbletea to v2 fix(deps): update module github.com/charmbracelet/bubbletea to v2 - autoclosed Mar 29, 2025
@renovate renovate bot closed this Mar 29, 2025
@renovate renovate bot deleted the renovate/major-go-deps branch March 29, 2025 00:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants