Skip to content

Releases: charmbracelet/bubbles

v2.0.0-alpha.2

12 Nov 22:09
v2.0.0-alpha.2
0719711
Compare
Choose a tag to compare
v2.0.0-alpha.2 Pre-release
Pre-release

Smells like Bubbles v2 Alpha 2!

Thanks for trying out Bubbles v2.0.0-alpha.2! This release was designed to work with Bubble Tea and Lip Gloss v2 alphas with the same tag, so make sure you catch ’em all:

go get github.com/charmbracelet/bubbletea/[email protected]
go get github.com/charmbracelet/bubbles/[email protected]
go get github.com/charmbracelet/lipgloss/[email protected]

There are a lot of small API changes in this release, around two general ideas:

  • Consistency across Bubbles
  • Manual light/dark background management for Lip Gloss v2 (see below)

We've found upgrading pretty easy, especially with a linter, but let us know how it goes for you. Read on for the breakdown.

Note

When in doubt, check the examples for reference: they've all been updated for v2.

A Note on Light and Dark Styles

Some Bubbles, like help, offer defaults for light and dark background colors. Selecting one or the other now a manual process, and you have two options.

🎩 The Best Way

Ideally, you have Bubble Tea query the background color for you. This means that you'll be properly querying the correct input and outputs with your program, and the query will happen in lockstep with the application.

// Query for the background color.
func (m model) Init() (tea.Model, tea.Cmd) {
	return m, tea.RequestBackgroundColor
}

// Listen for the response and initialize your styles accordigly.
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.BackgroundColorMsg:
		// Initialize your styles now that you know the background color.
		m.help = help.DefaultStyles(msg.IsDark())
		return m, nil
	}
}

If you're using Wish you must do it this way in to get the background color of the client.

🀠 The Quick Way

The quick way is to use detect the background color via the compat package in Lip Gloss. It's less recommended because it contains blocking I/O that operates independently of Bubble Tea and, when used with Wish it will not return the background color of the client (because it's running locally on the server).

import "github.com/charmbracelet/lipgloss/v2/compat"

var hasDarkBG = compat.HasDarkBackground()

func main() {
    var m model

    h := help.New()
    h.Styles = help.DefaultStyles(hasDarkBG)

    // And so on...
    m.help = h
}

For details on the compat package see the Lip Gloss v2.0.0-alpha.2 release notes.

πŸ‘€ Also Note

You can also just apply defaults manually.

h.Styles = help.DefaultLightStyles() // light mode!
h.Styles = help.DefaultDarkStyles()  // jk dark mode

What’s Changed: the Laundry List

Filepicker

  • Removed: DefaultStylesWithRenderer(). Lip Gloss is pure now, so just use DefaultStyles().
  • Model.Height has been broken into a getter and setter; use Model.SetHeight(int) and Model.Height() int instead

Help

help now defaults to using colors for dark backgrounds. You can manually change them with DefaultLightStyles() and DefaultDarkStyles():

h := help.New()
h.Styles = help.DefaultDarkStyles()  // dark mode
h.Styles = help.DefaultLightStyles() // light mode

Or, just detect the background color and apply the appropriate set of styles accordingly:

func (m Model) Init() (tea.Model, tea.Cmd) {
	// Ask for the background color.
	return m, tea.RequestBackgroundColor
}

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.BackgroundColorMsg:
		// We know the background color, so let’s update our styling.
		m.help.Styles = help.DefaultStyles(msg.IsDark())
	}
}

List

  • DefaultStyles() now takes a boolean to determine whether it should be rendered with light or dark styles: DefaultStyles(isDark bool)
  • DefaultItemStyles() now takes a boolean to determine whether it should be rendered with light or dark styles: DefaultItemStyles(isDark bool)

Paginator

  • The global variable DefaultKeyMap is now a function: func DefaultKeyMap() KeyMap

Progress

  • Model.Width has been broken into a getter and setter; use Model.SetWidth(int) and Model.Width() int instead
p := progress.New()

// Before
p.Width = 25
fmt.Printf("%w is a good width", p.Width)

// After
p.SetWidth(25)
fmt.Printf("%w is a good width", p.Width())

Stopwatch

  • NewWithInterval(time.Duration) has been removed. Pass an Option to New() instead: New(WithInterval(time.Duration))

Table

  • Model.Width has been broken into a getter and setter; use Model.SetWidth(int) and Model.Width() int instead
  • Model.Height has been broken into a getter and setter; use Model.SetHeight(int) and Model.Height() int instead

Textarea

  • The global variable DefaultKeyMap is now a function: func DefaultKeyMap() KeyMap
  • Model.FocusedStyle and Model.BlurredStyle have been replaced by Model.Styles.Focused and Model.Styles.Blurred
  • DefaultStyles() (blurred, focused Style) is now DefaultStyles(isDark bool) Styles. See help above for an example on how to work with this.

Textinput

  • The global variable DefaultKeyMap is now a function: func DefaultKeyMap() KeyMap
  • Model.Width has been broken into a getter and setter; use Model.SetWidth(int) and Model.Width() int instead

Timer

  • NewWithInterval(time.Duration) has been removed. Pass an Option to New() instead: New(time.Duration, WithInterval(time.Duration))

Viewport

  • Model.Width and Model.Height have been replaced with getters and setters:
m := v.New()

// Before
vp.Width = 40
vp.Height = 80
fmt.Println("%d is my favorite width", vp.Width)

// After
vp.SetWidth(40)
vp.SetHeight(80)
fmt.Println("%d is my favorite width", vp.Width())
  • New() doesn’t have deafult args anymore: New(width, height int) is now New(...Option). To set an initial width and height do one of the following:
// Use functional arguments:
vp := viewport.New(viewport.WithWidth(40), viewport.WithHeight(80)

// Or just:
vp := viewport.New()
vp.SetWidth(40)
vp.SetHeight(80)

Changelog

New Contributors

Full Changelog: v2.0.0-alpha.1...v2.0.0-alpha.2

πŸ’ That’s a wrap!

Feel free to reach out, ask questions, give feedback, and let us know how it's going. We’d love to know what you think.


Part of Charm.

<img alt="The Charm logo" src="https://stuff....

Read more

v2.0.0-alpha.1

18 Sep 18:57
v2.0.0-alpha.1
f81fd52
Compare
Choose a tag to compare
v2.0.0-alpha.1 Pre-release
Pre-release

Changelog

New Features

Bug fixes

Dependency updates

Other work


The Charm logo

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

v0.20.0

06 Sep 16:18
d3bd075
Compare
Choose a tag to compare

Focus. Breathe.

This features support for Bubble Tea's new focus-blur feature as well as a quality-of-life update to paginator. Enjoy!

Focus

You heard that right. Focus-blur window events are now enabled for textinput and textarea which were recently added to Bubble Tea v1.1.0. As long as WithReportFocus is enabled in your Program you'll automatically get nicer inputs.

To enable focus reporting:

p := tea.NewProgram(model{}, tea.WithReportFocus())

Remember to stay focused and hydrated!

Paginator opts

Speaking of functional arguments, paginator also received some some new quality-of-life startup options, courtesy @nervo.

p := paginator.New(
	paginator.WithPerPage(42),
	paginator.WithTotalPages(42),
)

Of course, you can still set the values on the model directly too:

p := paginator.New()
p.PerPage = 42
p.TotalPages = 24

Happy paging!

Changelog

New!

Deps


The Charm logo

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

v0.19.0

20 Aug 20:13
Compare
Choose a tag to compare

Bugs? Squashed (along with a few nice lil’ features).

Community-Driven Development?! Yep, the majority of the changes in this release were done by the community. Thank you all for your contributions that made this release possible.

Progress: custom chars

You can now customize the filled and empty characters of the progress bar.

p := progress.New(progress.WithFillCharacters('>', '.'))

progress bar example

Table improvements

Help is on the way

Table now includes a short and full help view so it's easier than ever to tell your users how to interact with the table.

// Render a table with its help.
t := table.New()
view := t.View() + "\n" + t.HelpView()

Accessing columns

You can also now get the table's columns (this already existed for rows).

package table

// Columns returns the current columns.
func (m Model) Columns() []Column

List: page navigation is fixed!

Previously, list.NextPage() and list.PrevPage() didn't work because the methods did not have pointer receivers. We've fixed this…by making them pointer receivers!

⚠️ Note that this is a minor API change and you might need to update your app to pass a pointer receiver to your model rather than a copy. Details in #458.

package progress

// NextPage moves to the next page, if available.
func (m *Model) NextPage()

// PrevPage moves to the previous page, if available.
func (m *Model) PrevPage()

What’s Changed

Changed

  • Textarea: Improve setting width by @mikelorant in #496
  • Textinput: fix out of range panic if no matched suggestions by @rdnt in #473
  • List: Fix no-op list pagination functions by @nekopy in #458
  • Table: Clarify position constant in JoinHorizontal by @aditipatelpro in #577
  • Progress: make full/empty fill characters configurable by @rwinkhart in #409
  • Dependencies: switch to x/ansi for text manipulation by @aymanbagabas in #505

Added

Fixed

  • Table: Render Row Tests by @maaslalani in #487
  • Table: Only render columns with a positive width by @fabio42 in #465
  • Table: Fix inheritence of SelectedStyle in StyleFunc by @gabrielfu in #539
  • Table: Don't include header height in the total table size by @prgres in #434
  • Table: Fix premature viewport scroll by @dzeleniak in #429
  • Textarea: Fix end of buffer character by @mikelorant in #491
  • Textarea: Set textarea default EndOfBufferCharacter to ' ' by @blvrd in #510
  • Textarea: End of Buffer alignment by @maaslalani in #486
  • Textinput: don't block input on validation by @GabrielNagy in #185
  • Viewport: Fix division by zero in scrollpercentage by @zMoooooritz in #494
  • Help: Fix centering by @gabe565 in #516
  • Progress: Stop spring defaults from overriding WithStringOptions by @nervo in #540
  • Cursor: Make SetMode method in cursor library handle invalid mode values correctly by @anirudhaCodes in #477

Test coverage βœ…

New Contributors

Full Changelog: v0.18.0...v0.19.0


The Charm logo

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

v0.18.0

01 Feb 18:54
364eac9
Compare
Choose a tag to compare

Textarea, but faster

This release features several fixes and big performance improvements for the textarea bubble.

What's Changed

New

Improved

Fixed

  • fix(textarea): correctly trim incoming paste by @muesli in #469
  • fix(textinput): Placeholder No Longer Changes Width + Paste Calculation by @hopefulTex in #451
  • fix(viewport): pad width to contentWidth by @ivanvc in #388

New Contributors

Full Changelog: v0.17.1...v0.18.0


The Charm logo

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

v0.17.1

13 Dec 18:41
Compare
Choose a tag to compare

Bumping Bubble Tea

This is just a little update to update to the latest version of Bubble Tea.

What's Changed

Full Changelog: v0.17.0...v0.17.1


The Charm logo

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

v0.17.0

13 Dec 16:28
v0.17.0
167e906
Compare
Choose a tag to compare

Text input autocompletions and various improvements

Autocompletion in Text Input

So @toadle wanted textinputs to support autocompletion in a ghost-text kind of a way. Rather than wait for us to do it he did what any dedicated open source developer would: he sent a PR! And now we can all benefit from his hard work.

Autocompletion is super easy to use:

ti := textinput.New()
ti.SetSuggestions([]string{"meow", "purr"})

By default you can press ctrl+n and ctrl+p to cycle through suggestions, but those keybindings can be changed as you, the application developer, see fit. For details check out textinput.SetSuggestions and the corresponding KeyMap in the docs.

Is the progress bar done yet?

@yrashk acutely noticed that to nicely transition from one state to another after an animated progress bar fills up it's helpful to know when the animated has finished animating before transitioning. To solve for this he added an IsAnimating method to the progress model. Thanks, @yrashk!

Changelog

New!

  • Support suggestions and autocompletion in textinput by @toadle in #407
  • Add method for determining if progress bar is animating by @yrashk in #386

Improved

Fixed

Full Changelog: v0.16.1...v0.17.0

New Contributors


The Charm logo

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

v0.16.1

31 May 18:42
v0.16.1
afd7868
Compare
Choose a tag to compare

File Picker Bubble πŸ“ 🫧

This release introduces a brand new filepicker bubble, new features, and a tonne of bugfixes.
Let us know what you think, ask questions, or just say hello in our Discord.

File picker example

For a quick start on how to use this bubble, take a look at the Example code.

Getting Started

Create a new file picker and add it to your Bubble Tea model.

picker := filepicker.New()
picker.CurrentDirectory, err = os.UserHomeDir()
if err != nil {
    // ...
}

m := model{
    picker: picker,
    // ...
}

Initialize the file picker in your Model's Init function.

func (m model) Init() tea.Cmd {
    return tea.Batch(
        m.picker.Init(),
        // ...
    )
}

Update the filepicker as any other bubble in the Update function.
After the picker.Update, use the DidSelectFile(msg tea.Msg) function to perform an action when the user selects a valid file.
You may allow only certain file types to be selected with the AllowedTypes property and allow directories to be selected with the DirAllowed property. To see the currently selected file/directory use the Path property.

var cmd tea.Cmd
m.picker, cmd = m.picker.Update(msg)

// Did the user select a file?
if didSelect, path := m.picker.DidSelectFile(msg); didSelect {
	// Get the path of the selected file.
	return m, tea.Println("You selected: " + selectedPath)
}

return m, cmd

For the full example on how to use this bubble, take a look at the Example code.

New

Fixed

New Contributors

Full Changelog: v0.15.0...v0.16.0


The Charm logo

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

v0.15.0

20 Jan 16:27
4a71cee
Compare
Choose a tag to compare

Ceci, Cela

This is primarily a housekeeping release with lots and lots of bugfixes and improvements from the community. Thanks, everyone, for all your support! πŸ€—

Please do feel free to say hello, ask questions, and tell us what else you want to see in our Discord. πŸ’¬

New

  • textinput + textarea: support for forthcoming bracketed paste (see also charmbracelet/bubbletea#397) and avoid ctrl chars in clipboard input by @knz in #214
  • textinput: support key bindings textarea by @knz in #270
  • paginator: make paginator keybindings customizable by @knz in #272
  • viewport: Expose TotalLineCount() and VisibleLineCount() methods within viewport by @adaam2 in #283
  • table: make UpdateViewport() have constant runtime by @pja237 in #284
  • table: add SetColumns method to set columns by @maaslalani in #260
  • table: expose rows by @marcantoineg in #287

Fixed

New Contributors

Full Changelog: v0.14.0...v0.15.0


The Charm logo

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

v0.14.0

06 Sep 16:34
Compare
Choose a tag to compare

Table Bubble

This feature release of Bubbles includes a brand new table bubble that you can use to show and select tabular data! Huge thanks to @wesleimp for contributing this change ❀️.

Table Bubble showing countries and their populations

See the example code for an example of how to use the table in your Bubble Tea applications.

Getting Started

Create a new table:

t := table.New(
	table.WithColumns(columns),
	table.WithRows(rows),
	table.WithFocused(true),
	table.WithHeight(7),
)

Alternatively,

t := table.New(table.WithColumns(columns))
t.SetRows(rows)
t.Focus()
t.SetHeight(7)

Style the table how you want:

s := table.DefaultStyles()
s.Header = s.Header.
	BorderStyle(lipgloss.NormalBorder()).
	BorderForeground(lipgloss.Color("240")).
	BorderBottom(true).
	Bold(false)
s.Selected = s.Selected.
	Foreground(lipgloss.Color("229")).
	Background(lipgloss.Color("57")).
	Bold(false)
t.SetStyles(s)

And then Update and Render (View) the table:

type model struct {
	table table.Model
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmd tea.Cmd
	m.table, cmd = m.table.Update(msg)
	return m, cmd
}

func (m model) View() string {
	return m.table.View()
}

Other Changes

  • Ctrl+H backspaces text by @maaslalani in #199
  • LineCount() + Include New Line characters in Length() by @maaslalani in #204
  • fix(viewport): honor width and height settings by @meowgorithm in #216
  • feat(textarea): new bindings for "go to begin" / "go to end" by @knz in #226
  • feat(textarea): PromptFunc, support dynamic prompts by @knz in #211
  • fix(viewport): properly truncate to size by @knz in #228

New Contributors

Full Changelog: v0.13.0...v0.14.0

Read more about it in our examples and docs:


The Charm logo

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