bubbleup

package module
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 31, 2026 License: MIT Imports: 11 Imported by: 3

README

Welcome to BubbleUp

Float your alerts to the top of your TUI like a bubble in a soda. Integrates with BubbleTea applications seamlessly to render your status updates in style.

Go Version

Example showcasing BubbleUp

Requirements

Getting Started

Run the following to download the module:

go get go.dalton.dog/bubbleup

Then import it into your project with the following:

import (
    "go.dalton.dog/bubbleup"
    tea "github.com/charmbracelet/bubbletea"
)

This is assuming you already have BubbleTea installed and available in your project. Go check out their repo here for more information!

From there, it's as simple as creating a new bubbleup.AlertModel by calling bubbleup.NewAlertModel(width, useNerdFont, duration), and embedding the returned model inside of your main model.

Parameters:

  • width (int): Maximum width for alerts in characters
  • useNerdFont (bool): Whether to use NerdFont symbols* or ASCII prefixes**
  • duration (time.Duration): How long alerts display before auto-dismissing (in seconds)

* NerdFont must be installed on your app user's computer.
** Unicode prefixes also an alternate to NerdFont symbols or ASCII prefixes using the WithUnicodePrefix() method.

Basic Example:

// Create alert model: max width=50, use NerdFont, 10 second duration
alertModel := bubbleup.NewAlertModel(50, true, 10*time.Second)

m := myModel{
    alert: alertModel,
}

See the Configuration Options section below for advanced features like positioning, dynamic width, and Unicode fonts.

Configuration Options

BubbleUp supports method chaining for configuration. All With*() methods return the modified AlertModel, allowing you to chain multiple configurations:

m.alert = bubbleup.NewAlertModel(50, false, 10*time.Second).
    WithMinWidth(15).                              // Enable dynamic width (15-50 chars)
    WithPosition(bubbleup.TopRightPosition).       // Position at top-right
    WithUnicodePrefix().                           // Use Unicode symbols
    WithAllowEscToClose()                          // Allow ESC to dismiss alerts
Position Support

Control where alerts appear on screen with WithPosition():

Available Positions:

  • TopLeftPosition - Top-left corner (default)
  • TopCenterPosition - Top center
  • TopRightPosition - Top-right corner
  • BottomLeftPosition - Bottom-left corner
  • BottomCenterPosition - Bottom center
  • BottomRightPosition - Bottom-right corner

Example:

// Show error at top-right
m.alert = m.alert.WithPosition(bubbleup.TopRightPosition)
alertCmd = m.alert.NewAlertCmd(bubbleup.ErrorKey, "Connection failed")

// Show success at bottom-center
m.alert = m.alert.WithPosition(bubbleup.BottomCenterPosition)
alertCmd = m.alert.NewAlertCmd(bubbleup.InfoKey, "File saved")

Note: Position can be changed dynamically - different alerts can appear at different positions.

Dynamic Width Alerts

By default, alerts have a fixed width set by the width parameter passed to NewAlertModel(). You enable dynamic width alerts by setting a minimum alert with by calling the WithMinWidth() method. This will change BubbleUp to automatically size alarts dynamically based on message length bracketed within minWidth and (max) width:

Fixed Width Mode (default):

  • minWidth == 0 or WithMinWidth() not called
  • Alert is always width characters wide
  • Messages wrap if longer than width

Dynamic Width Mode:

  • Call WithMinWidth(min) with a minimum width
  • Alert width varies between min and (max) width based on message length
  • Short messages = narrow alerts, long messages = wider alerts (up to max)
  • Too short messages are right-padded with spaces to the current minWidth.

Example:

// Create with max width 50, enable dynamic sizing with min 15
m.alert = bubbleup.NewAlertModel(50, true, 10*time.Second).WithMinWidth(15)

// Short message: alert will be ~15-20 chars wide
alertCmd = m.alert.NewAlertCmd(bubbleup.InfoKey, "Done")

// Long message: alert will be ~45-50 chars wide
alertCmd = m.alert.NewAlertCmd(bubbleup.ErrorKey, "This is a longer error message")

// Very long message: alert will be 50 chars wide and wrapped
alertCmd = m.alert.NewAlertCmd(bubbleup.ErrorKey, "This is a super, extra longer error message that will wrap")

When to Use:

  • Fixed width: When you want consistent alert sizing
  • Dynamic width: When you have varying message lengths and want compact alerts
Font Options

BubbleUp supports three font/symbol options for alert prefixes:

NerdFonts (requires installation on app user's system):

  • Beautiful icons from NerdFonts
  • Users get NerdFonts at: https://nerdfonts.com
  • To enable: NewAlertModel(width, true, duration)

Unicode (works ~everywhere):

  • Standard Unicode symbols that work in most terminals
  • No special font required
  • Best default choice for portability
  • To enable: NewAlertModel(width, false, duration).WithUnicodePrefix()

ASCII (fallback):

  • Simple ASCII character prefixes: (i), (!), [!!], (?)
  • Works everywhere, even in restricted environments
  • To enable: NewAlertModel(width, false, duration) (default when useNerdFont=false)

When to Use:

  • NerdFonts: You want the nicest looking UI (assumes you control the systems running the app.)
    • Also when your users are either technical enough and motivated enough to install NerdFonts.
  • Unicode: When you just want your app to work reliably without extra fuss.
  • ASCII: When app reliability is your top-most concern.
    • Or if you want no fuss but dislike the available Unicode characters.

Example:

// Unicode fonts (recommended for portability)
m.alert = bubbleup.NewAlertModel(50, false, 10*time.Second).WithUnicodePrefix()

// NerdFont (requires NerdFont installation on user's system)
m.alert = bubbleup.NewAlertModel(50, true, 10*time.Second)

// ASCII (maximum compatibility)
m.alert = bubbleup.NewAlertModel(50, false, 10*time.Second)
Keyboard Interaction

Enable Esc key to dismiss alerts before their timeout:

m.alert = bubbleup.NewAlertModel(50, true, 10*time.Second).WithAllowEscToClose()

Handling Esc key in Your Update() Method:

When Esc can close alerts, you need to check if an alert is active before handling Esc in your other BubbleTea models (like for quitting your app):

func (m myModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    var alertCmd tea.Cmd

    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "esc":
            // Only quit if no alert is active
            if !m.alert.HasActiveAlert() {
                return m, tea.Quit
            }
            // Alert is active - let alert.Update() handle ESC
        case "q":
            return m, tea.Quit
        case "s":
            alertCmd = m.alert.NewAlertCmd(bubbleup.InfoKey, "File saved")
        }
    }

    // Pass messages to alert model
    outAlert, outCmd := m.alert.Update(msg)
    m.alert = outAlert.(bubbleup.AlertModel)

    return m, tea.Batch(alertCmd, outCmd)
}

Methods:

  • WithAllowEscToClose() - Enable Esc to close alerts
  • HasActiveAlert() - Returns true if an alert is currently displayed

Integrating Into Your BubbleTea App

In your Init() Method

Be sure to return the result of the alert models' Init().

If you need to also return one or more commands, be sure to use tea.Batch() to bundle them together.

In your Update() Method

This is where you'll actually spawn the alerts, which is as easy as calling NewAlertCmd() with a key and a message. The formatting and stylings are handled by what is provided in the stored AlertDefinition types (more info below).

Example with the included Info alert type:

Be sure to pass any received messages to the alert model, and appropriately use the return values.

Reassign your stored alert with the updated alert, and return the given command either alone, or via tea.Batch().

alertCmd = m.alert.NewAlertCmd(bubbleup.InfoKey, "New info alert.") // Get the command to initiate the desired alert

outAlert, outCmd := m.alert.Update(msg)  // Pass any messages to the alert model, such as alert or tick messages
m.alert = outAlert.(bubbleup.AlertModel) // Reassign the returned alert model to the main model

return m, tea.Batch(alertCmd, outCmd)

Note: If you enabled WithAllowEscToClose(), see the Keyboard Interaction section for handling Esc key properly.

In your View() Method

You want to do all of your normal view code to render your output, and then pass that into your alert model's Render() function. This will overlay the alert onto the provided content. We recommend you have this be the last thing you do in your View() function.

NOTE: The AlertModel's View() function is empty and is not intended to be called.

Creating Your Own Alert Types

You can create your own alert types by creating an instance of an AlertDefinition struct, and passing it into your model's RegisterNewAlertType() function. The AlertDefinition consists of the following parts:

  • Key: (Required) Unique identifier for your alert type. What is passed into NewAlertCmd to get rendering information.
  • ForeColor: (Required) A hex color string that you want to use as the foreground color of your alert type, for example: "#00FF00".
  • Style: (Optional) A lipgloss.Style struct that will override the default one, but it's up to you to make sure your override meshes well.
  • Prefix: (Optional) The symbol or strings used to prefix your message contents. Can be left empty
Example

You would declare and register your new alert type like this:

    myCustomAlert := AlertDefinition{
        Key: "CoolAlert",
        ForeColor: "#123456",
        Prefix: ":)"
    }

    m.alertModel.RegisterNewAlertType(myCustomAlert)

NOTE: We did not pass a style so BubbleUp will use the default style.

Then call it later by using the following code:

outAlertCmd := m.alert.NewAlertCmd("CoolAlert", "My really cool alert message")

Then interact with outAlertCmd as described in the Update section above.

Complete Example

See example for a complete working example demonstrating all features:

  • examples/example_main.go

Documentation

Full API documentation is available at pkg.go.dev/go.dalton.dog/bubbleup

Credits

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

Submit bug reports via GitHub Issues.

License

This project is licensed under the MIT License.

See the LICENSE file for details.

Documentation

Index

Constants

View Source
const (
	InfoKey  = "Info"
	WarnKey  = "Warn"
	ErrorKey = "Error"
	DebugKey = "Debug"
)

Alert keys for the included alert types.

View Source
const (
	InfoNerdSymbol  = " "
	WarnNerdSymbol  = "󱈸 "
	ErrorNerdSymbol = "󰬅 "
	DebugNerdSymbol = "󰃤 "

	InfoASCIIPrefix    = "(i)"
	WarningASCIIPrefix = "(!)"
	ErrorASCIIPrefix   = "[!!]"
	DebugASCIIPrefix   = "(?)"

	InfoUnicodePrefix    = "\u24D8 " // Trailing space is intentional
	WarningUnicodePrefix = "\u26A0"
	ErrorUnicodePrefix   = "\u2718"
	DebugUnicodePrefix   = "\u003F"

	// Deprecated: use InfoASCIIPrefix instead.
	InfoUniPrefix = InfoASCIIPrefix

	// Deprecated: use WarningASCIIPrefix instead.
	WarningUniPrefix = WarningASCIIPrefix

	// Deprecated: use ErrorASCIIPrefix instead.
	ErrorUniPrefix = ErrorASCIIPrefix

	// Deprecated: use DebugASCIIPrefix instead.
	DebugUniPrefix = DebugASCIIPrefix
)

Symbols used by the included alert types. To use the NerdFont symbols, you must be using a NerdFont, which can be obtained from https://www.nerdfonts.com/. If you want to use the default non-NerdFont symbols, pass false into the useNerdFont parameter when creating your alert model.

View Source
const (
	InfoColor  = "#00FF00"
	WarnColor  = "#FFFF00"
	ErrorColor = "#FF0000"
	DebugColor = "#FF00FF"
	BackColor  = "#000000"
)

Colors used by the included alert types.

View Source
const (
	DefaultLerpIncrement = 0.18
)

Defaults used by the notification rendering.

Variables

This section is empty.

Functions

This section is empty.

Types

type AlertDefinition

type AlertDefinition struct {
	// (Req) Unique key used to refer to an alert type
	Key string

	// (Req) Hex code of the color you want your alert to be
	ForeColor string

	// (Opt) lipgloss.Style used to render the alert
	Style lipgloss.Style

	// (Opt) String used to prefix the alert message
	Prefix string
}

AlertDefinition is all the information needed to register a new alert type.

type AlertModel

type AlertModel struct {
	// contains filtered or unexported fields
}

AlertModel maintains a list of alert types, and facilitates the display and clearing of alerts, as well as registering custom alerts and overrides to defaults Note: duration is measured in seconds Note: width behavior depends on minWidth:

  • minWidth == 0 (default): width is fixed width
  • minWidth > 0: width is max width, minWidth is minimum, actual width varies with message length

func NewAlertModel

func NewAlertModel(width int, useNerdFont bool, duration time.Duration) *AlertModel

NewAlertModel creates and returns a new AlertModel, initialized with default alert types

func (AlertModel) HasActiveAlert added in v1.2.0

func (m AlertModel) HasActiveAlert() bool

HasActiveAlert allows other models to tell if there is an active already and avoid processing an esc key used to clear an alert

func (AlertModel) Init

func (m AlertModel) Init() tea.Cmd

Init required as part of BubbleTea Model interface

func (AlertModel) NewAlertCmd

func (m AlertModel) NewAlertCmd(alertType, message string) tea.Cmd

NewAlertCmd will construct and return the tea.Cmd needed to trigger an alert. This should be called in your Update() function, and the returned tea.Cmd should be batched into your return.

func (AlertModel) RegisterNewAlertType

func (m AlertModel) RegisterNewAlertType(definition AlertDefinition)

RegisterNewAlertType will registery a new alert type based on the provided AlertDefintion. This can also be used to overwrite the provided defaults by providing an AlertDefintion with one of the default keys.

func (AlertModel) Render

func (m AlertModel) Render(content string) string

Render takes in the main view content and overlays the model's active alert. This function expects you build the entirety of your view's content before calling this function. It's recommended for this to be the final call of your model's View(). Returns a string representation of the content with overlayed alert.

func (AlertModel) Update

func (m AlertModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update takes in a message and returns an associated command to drive model functionality. First alertMsg starts the ticking command that causes alert refreshing Implemented as part of BubbleTea Model interface

func (AlertModel) View

func (m AlertModel) View() string

View doesn't do anything, and it should never be called directly Implemented as part of BubbleTea Model interface

func (AlertModel) WithAllowEscToClose added in v1.2.0

func (m AlertModel) WithAllowEscToClose() AlertModel

func (AlertModel) WithMinWidth added in v1.2.0

func (m AlertModel) WithMinWidth(min int) AlertModel

WithMinWidth returns a new AlertModel with dynamic width enabled. When minWidth > 0, the notification width will vary between minWidth and width (max) based on the actual message length. This is an immutable operation. If min > width, it will be clamped to width.

func (AlertModel) WithPosition added in v1.2.0

func (m AlertModel) WithPosition(pos Position) AlertModel

WithPosition returns a new AlertModel with the specified position. This is an immutable operation that returns a copy with the updated position.

func (AlertModel) WithUnicodePrefix added in v1.2.0

func (m AlertModel) WithUnicodePrefix() AlertModel

WithUnicodePrefix switches the AlertModule to use Unicode fonts

type Position added in v1.2.0

type Position string
const (
	TopLeftPosition      Position = "TL"
	TopCenterPosition    Position = "TC"
	TopRightPosition     Position = "TR"
	BottomLeftPosition   Position = "BL"
	BottomCenterPosition Position = "BC"
	BottomRightPosition  Position = "BR"
	UnspecifiedPosition  Position = ""
)

func (Position) IsValid added in v1.2.0

func (p Position) IsValid() bool

func (Position) Label added in v1.2.0

func (p Position) Label() string

func (Position) String added in v1.2.0

func (p Position) String() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL