bloom

package module
v0.0.0-...-56513da Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2026 License: MIT Imports: 23 Imported by: 0

README ΒΆ

banner

πŸ”— Table of Contents

✨ Features

bloom is a layout, compositing, and animation engine for Bubble Tea programs. Describe your view as a tree of rows, columns, and layers, built from your model every frame, and bloom sizes it to the terminal, draws dialogs and toasts on top, animates what changes, and tells you what the mouse is over.

[!WARNING] bloom is still pre-1.0. Its API may change drastically until the v1.0.0 release, so expect breaking changes between versions, and pin the version you use.

  • βœ” Layout without the math: Row, Column, Grid, and Wrap, with Fixed, Fill, Grow, Percent, and natural-size slots, Min/Max, gaps, alignment, and justification. No more measuring rendered strings and subtracting borders on every resize.
  • βœ” Responsive by priority: panels marked Collapsible drop out when the terminal is too small, lowest priority first, and components can build a different layout for the space they're given.
  • βœ” Real layers: Overlay draws dialogs, dropdowns, popovers, and toasts over the screen, anchored to a corner or to another node, with backdrops, drop shadows, and modal input.
  • βœ” Mouse handlers: clicks, double clicks, the wheel, hover, and drags go to handlers on the nodes they belong to, like bloom.Text(name).On(on.Click(m.open)). Hover styles and drag capture are built in.
  • βœ” Focus: tab order from the layout, focused styles, the real terminal cursor, and focus scopes that keep tab inside a dialog until it closes.
  • βœ” Animation kept in your model: eased and spring values, presences for showing and hiding (with ShowFor for toasts that hide themselves), sequences of steps, and automatic layout animation with Animate().
  • βœ” 25+ cell effects: Fade, Slide, Iris, Unfold, Decode, Glitch, and more as transitions; Dim, Desaturate, Tint, and Spotlight as backdrops; Shake, Shimmer, Roll, and Burst for feedback; Chase, Pulse, Comet, and March for looping borders; and combinators to stagger, delay, reverse, and ease them. Or write your own.
  • βœ” Borders that do more: put titles, clickable tabs, icon buttons, and counters on a box's border with Edge, plus draggable scrollbars that only show up when there's more to scroll.
  • βœ” Loading placeholders: Skeleton(8, 0, 5, 11) draws shimmering bars in place of content that's still loading, a line for each line of the content, so nothing jumps when it arrives.
  • βœ” Works with existing components: anything with a View() string method, including Bubbles components, is a leaf in the tree.
  • βœ” Fast: strings are parsed once, Lip Gloss renders are cached, and unchanged frames aren't composited again. A 200Γ—60 dashboard animating a dialog over a dimmed backdrop takes about 0.45ms a frame.

βš™ Usage

Take a look at the official documentation for tutorials, guides, and reference, starting with building your first layout. The documentation is also available for LLMs and agents as llms.txt and llms-full.txt, and every page has a markdown version.

func (m *model) View() tea.View {
	v := m.ui.View(m.width, m.height, bloom.Overlay(
		bloom.Column(
			bloom.Fill(bloom.Row(
				bloom.Fixed(24, m.sidebar).Collapsible(1),
				bloom.Fill(m.reader).Min(40),
			)),
			m.statusbar,
		),
		bloom.Layer(m.dialog).
			Modal().
			Backdrop(fx.Dim{Amount: 0.5}).
			Presence(m.dialogAnim, fx.Fade{}, fx.Slide{From: bloom.Bottom}),
		bloom.Layer(m.toast).
			Anchor(bloom.End, bloom.End).
			Presence(m.toastAnim, fx.Slide{From: bloom.Right}),
	))
	v.AltScreen = true
	v.MouseMode = tea.MouseModeCellMotion
	return v
}

Runnable programs, from a basic layout to a 120 FPS dashboard, are in _examples. Run one with go run ./forms from that directory.

go get -u github.com/lrstanley/bloom@latest
  • bubbletint: hundreds of terminal color themes for Bubble Tea and Lip Gloss, which work directly in bloom's styles and effects.
  • steep: a test harness that runs Bubble Tea programs in an emulated terminal, used for bloom's own end-to-end tests.

πŸ™‹β™‚ Support & Assistance

  • ❀ Please review the Code of Conduct for guidelines on ensuring everyone has the best experience interacting with the community.
  • πŸ™‹β™‚ Take a look at the support document on guidelines for tips on how to ask the right questions.
  • 🐞 For all features/bugs/issues/questions/etc, head over here.

🀝 Contributing

  • ❀ Please review the Code of Conduct for guidelines on ensuring everyone has the best experience interacting with the community.
  • πŸ“‹ Please review the contributing doc for submitting issues/a guide on submitting pull requests and helping out.
  • πŸ— For anything security related, please review this repositories security policy.

βš– License

MIT License

Copyright (c) 2026 Liam Stanley <liam@liam.sh>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Also located here

Documentation ΒΆ

Overview ΒΆ

Package bloom lays out, composites, and routes input for Bubble Tea views.

A view is described each frame as a tree of nodes built from model state: Row and Column share space between children, Box draws a Lip Gloss frame around one, Overlay puts [Layer]s such as dialogs over a base, and any Bubble Tea component with a View() string method is a leaf.

bloom.Column(
	bloom.Row(
		bloom.Fixed(20, m.sidebar).ID(paneSidebar).Collapsible(1),
		bloom.Fill(m.reader).ID(paneReader).Min(40),
	),
	m.statusbar,
)

Who sizes what ΒΆ

Constraints go down, sizes come up, and parents place. A parent decides each child's rectangle, whether it is collapsed, and how it is clipped. How a child takes part in sharing space is set where it is placed, by wrapping it in a slot such as Fixed or Fill, not by the child itself. A child only knows the size it was given: a component that wants it can implement Layouter, and one that knows its natural size can implement Measurer.

Rendering ΒΆ

Root turns a tree into a tea.View each frame. Leaf strings are parsed into cells once and cached, nodes are composited in tree order with every node clipped to its parent, and fx effects run on a node's cells or on the frame beneath a layer. A frame identical to the previous one is not composited again. Mouse events run the handlers of the nodes under the pointer, given with On and made by package on, using the geometry of the frame on screen.

Layout is a pure function of the tree, so Solve can compute the same rectangles in Update, for components that need their size there.

Documentation ΒΆ

See the documentation for guides and examples. It is also available for LLMs and agents as llms.txt.

Index ΒΆ

Constants ΒΆ

View Source
const (
	Bottom      = fx.Bottom
	Top         = fx.Top
	Left        = fx.Left
	Right       = fx.Right
	TopLeft     = fx.TopLeft
	TopRight    = fx.TopRight
	BottomLeft  = fx.BottomLeft
	BottomRight = fx.BottomRight
)

The sides and corners of a node or of the screen.

Variables ΒΆ

This section is empty.

Functions ΒΆ

func Debug ΒΆ

func Debug(c Child, w, h int) string

Debug lays out c in a wΓ—h frame, as Solve does, and returns its tree of nodes as text, one node per line: what it is, how it is sized, its ID, where it was laid out as x,y and width x height, and anything else that affects it. A node its container collapsed, or a layer whose anchor is not on screen, is marked hidden; one partly on screen is marked with the part that is, and one not on screen at all is marked offscreen. Components are shown by their type, and those that build their own layout, such as a Layouter, with the tree they built.

Debug is meant for finding out why a layout looks the way it does, such as by writing it to a log from View. Its format may change between versions.

Column 0,0 30x5
β”œβ”€ Row 0,0 30x3
β”‚  β”œβ”€ Fixed(10) id="sidebar" 0,0 10x3 collapsible=1
β”‚  β”‚  └─ Box 0,0 10x3
β”‚  β”‚     └─ Text "Inbox" 1,1 8x1
β”‚  └─ Fill id="reader" 10,0 20x3 min=12
β”‚     └─ main.reader 10,0 20x3
└─ Text "ready" 0,3 30x1 truncate

func Render ΒΆ

func Render(c Child, w, h int) string

Render lays out and draws c in a wΓ—h frame and returns it as a string, for using a layout where a string is wanted. A w or h of zero or less is c's natural size on that axis. A model's View should use Root instead, which keeps more between frames. Render is stateless and draws animation targets immediately.

func ThrottleMouse ΒΆ

func ThrottleMouse(every time.Duration) func(tea.Model, tea.Msg) tea.Msg

ThrottleMouse returns a tea.WithFilter filter that drops wheel and motion events that come within every of the last of the same kind let through, which spares a frame for each one when the terminal sends them faster than they can be drawn. A dropped event is not made up for: the wheel scrolls less, and when the pointer stops right after one, hover waits until it moves again.

Types ΒΆ

type Align ΒΆ

type Align uint8

Align is where a child goes on the cross axis of a Row or Column, or on either axis of a Layer.

const (
	// Stretch makes a child as big as the space it is in. It is the default
	// for children of a row or column.
	Stretch Align = iota
	// Start puts a child at the top or left.
	Start
	// Center puts a child in the middle.
	Center
	// End puts a child at the bottom or right.
	End
)

type BoxNode ΒΆ

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

BoxNode draws a Lip Gloss style's frame, its margins, border, padding, and background, around a child. Build one with Box.

func Box ΒΆ

func Box(style lipgloss.Style, child Child) BoxNode

Box draws style's frame around child, which gets the space inside it. A box given less space than its frame takes is not drawn, rather than drawn broken.

func (BoxNode) BorderFx ΒΆ

func (n BoxNode) BorderFx(t float64, effects ...fx.Effect) BoxNode

BorderFx applies effects to the box's frame for progress t, its border, padding, and background, but not what is inside it or on its edges. The effects of package fx made for borders, such as fx.Chase, change only the border's cells. Effects that move cells do not move the frame.

func (BoxNode) Edge ΒΆ

func (n BoxNode) Edge(side Direction, align Align, child Child) BoxNode

Edge draws child over side of the box's border, such as a title or a counter on the top border, or a Scrollbar on the right one, without taking space from what is inside. side is Top, Bottom, Left, or Right; a corner shows nothing. On the top and bottom, children are one row tall and kept a cell from the corners; on the left and right, they are one column wide, and a Text runs down the side, a character to a row. Children at Start and End are placed from that end inward, in the order given, and children at Center share the middle of what is left. A Stretch child takes all of what is left. Children are given no more room than is left, one cell apart, and one with no room is not shown. A side without a border shows none.

func (BoxNode) HoverStyle ΒΆ

func (n BoxNode) HoverStyle(style lipgloss.Style) BoxNode

HoverStyle draws the box's frame with style instead of its own while the pointer is over it. It should take the same space as the box's style, changing only how it looks. See on.Hover for when the pointer is followed.

func (BoxNode) ID ΒΆ

func (n BoxNode) ID[K comparable](id K) BoxNode

ID sets the ID that Frame.Rect refers to the box by.

func (BoxNode) On ΒΆ

func (n BoxNode) On(handlers ...on.Handler) BoxNode

On adds mouse handlers to the box, made by package on. The box takes events on its frame as well as inside it, and the positions handlers are given are relative to its outer corner.

func (BoxNode) View ΒΆ

func (n BoxNode) View() string

View implements Child, drawing the node at its natural size.

type Child ΒΆ

type Child interface {
	View() string
}

Child is anything that can be placed in a layout: every node of this package, and any Bubble Tea component, which is drawn from its View string. Use Text for a plain string.

func Centered ΒΆ

func Centered(child Child) Child

Centered places child at its natural size in the middle of the space it is given.

func Drawable ΒΆ

func Drawable(d uv.Drawable) Child

Drawable returns a node that draws d directly onto the cells it is given, clipped to them. Its natural size is zero unless d implements Measurer.

func Present ΒΆ

func Present(p anim.Presence, child Child, effects ...fx.Effect) Child

Present draws child with p's progress while visible. When hidden, child is absent, taking neither space nor a gap in its container.

func Transition ΒΆ

func Transition(t float64, child Child, effects ...fx.Effect) Child

Transition draws child with effects applied to its cells for progress t, from 0, as it starts to enter, to 1, when it is fully shown. See fx.Effect.

type Context ΒΆ

type Context struct {
	// Width and Height are the space available. Once layout is done they
	// are the exact size the component was given.
	Width, Height int
}

Context is what a Layouter is given to build its layout for.

type Direction ΒΆ

type Direction = fx.Direction

Direction is a side or a corner of a node or of the screen, such as where a layer is anchored to a node, or where an effect starts from.

type FlexNode ΒΆ

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

FlexNode lays children out in a row or column. Build one with Row or Column.

func Column ΒΆ

func Column(children ...Child) FlexNode

Column lays children out top to bottom.

func Row ΒΆ

func Row(children ...Child) FlexNode

Row lays children out left to right.

func (FlexNode) Align ΒΆ

func (n FlexNode) Align(a Align) FlexNode

Align sets where children go on the cross axis. The default is Stretch.

func (FlexNode) Gap ΒΆ

func (n FlexNode) Gap(cells int) FlexNode

Gap puts n cells between children.

func (FlexNode) ID ΒΆ

func (n FlexNode) ID[K comparable](id K) FlexNode

ID sets the ID that Frame.Rect refers to the node by.

func (FlexNode) Justify ΒΆ

func (n FlexNode) Justify(j flex.Justify) FlexNode

Justify sets where children go on the main axis when none of them grow.

func (FlexNode) On ΒΆ

func (n FlexNode) On(handlers ...on.Handler) FlexNode

On adds mouse handlers to the node, made by package on. They take events on the gaps between children too.

func (FlexNode) View ΒΆ

func (n FlexNode) View() string

View implements Child, drawing the node at its natural size.

type Focus ΒΆ

type Focus[K comparable] struct {
	// contains filtered or unexported fields
}

Focus is which node has the keyboard focus, by its ID, of type K. Keep it in the model: the layout only says which nodes can be focused, with Focusable, and where focus scopes are.

Focus moves between focusable nodes whose IDs are Ks, and passes over those with IDs of other types, so give everything that takes focus the same ID type:

type control int

const (
	fieldName control = iota
	fieldEmail
	buttonSave
)

type model struct {
	focus bloom.Focus[control]
}

For nodes of different kinds, K can be an interface that their ID types implement, and a Focus[any] moves between every focusable node.

Focus.Push confines focus to a scope, such as a modal Layer, until Focus.Pop puts it back where it was. A scope's ID can be of any type:

case openConfirmMsg:
	m.focus.Push(dialogConfirm)
	m.focus.Set(buttonOK)
case closeConfirmMsg:
	m.focus.Pop()
case tea.KeyPressMsg:
	if msg.String() == "tab" {
		m.focus.Next(m.ui.Frame())
	}

func (*Focus[K]) Clear ΒΆ

func (f *Focus[K]) Clear()

Clear removes the focus.

func (*Focus[K]) Current ΒΆ

func (f *Focus[K]) Current() (K, bool)

Current returns the ID of the focused node, and whether a node is focused.

func (*Focus[K]) Is ΒΆ

func (f *Focus[K]) Is(id K) bool

Is reports whether the node with ID id is focused.

func (*Focus[K]) Next ΒΆ

func (f *Focus[K]) Next(frame *Frame) bool

Next focuses the next node that can be focused, in the order of frame, inside the innermost scope pushed, wrapping around. It reports whether there was one.

func (*Focus[K]) Pop ΒΆ

func (f *Focus[K]) Pop()

Pop leaves the innermost scope, focusing what was focused when it was pushed.

func (*Focus[K]) Prev ΒΆ

func (f *Focus[K]) Prev(frame *Frame) bool

Prev focuses the previous node that can be focused, as Focus.Next does.

func (*Focus[K]) Push ΒΆ

func (f *Focus[K]) Push[S comparable](scope S)

Push confines focus to the scope with ID scope, clearing it until a node there is focused, and remembers the focus to go back to on Focus.Pop.

func (*Focus[K]) Scope ΒΆ

func (f *Focus[K]) Scope() any

Scope returns the ID of the innermost scope pushed, or nil.

func (*Focus[K]) Set ΒΆ

func (f *Focus[K]) Set(id K)

Set focuses the node with ID id.

func (*Focus[K]) Update ΒΆ

func (f *Focus[K]) Update(msg tea.Msg, frame *Frame) (tea.Cmd, bool)

Update handles tab and shift+tab, which move the focus, and enter and space, which run the on.Press handlers of the focused node in frame. It returns their commands, and reports whether it handled msg: tab keys are handled even when frame has no focusable nodes, and enter and space only when the focused node has a Press handler. Clicks move the focus with WithFocus.

func (*Focus[K]) Within ΒΆ

func (f *Focus[K]) Within[S comparable](frame *Frame, scope S) bool

Within reports whether the focused node is inside the scope with ID scope in frame.

type Frame ΒΆ

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

Frame is the geometry of a laid-out frame: where each node with an ID went, which nodes can be focused, and what the mouse can hit, with the mouse handlers nodes were given. It does not change once made, so it is safe to keep.

func Solve ΒΆ

func Solve(c Child, w, h int) *Frame

Solve lays out c in a wΓ—h frame without drawing it, and returns its geometry at animation targets. Solve is stateless; retained layout animations may draw intermediate rectangles. Components that need their target size in Update can use Solve there. Effects are not run, so a node a transition is moving, such as with fx.Slide, is where it was laid out:

case tea.WindowSizeMsg:
	m.width, m.height = msg.Width, msg.Height
	if r, ok := bloom.Solve(m.layout(), m.width, m.height).Rect(paneReader); ok {
		m.reader.SetWidth(r.Dx())
		m.reader.SetHeight(r.Dy())
	}

func (*Frame) Hit ΒΆ

func (f *Frame) Hit(x, y int) []any

Hit returns the IDs of the nodes at x, y on screen that mouse events there reach, outermost first, so the last is the topmost node hit. It is nil when nothing with an ID is there, or when a modal layer takes the point although it is outside the layer. It is for handling a tea.MouseMsg in Update rather than with handlers.

func (*Frame) Rect ΒΆ

func (f *Frame) Rect[K comparable](id K) (image.Rectangle, bool)

Rect returns where the node with ID id was laid out, which may be partly off screen, and whether it is in the frame. Retained layout animations report the visual rectangle drawn this frame, except for a node moving with effects, such as with SlotNode.MoveWith, which is laid out where it goes from the start.

type GridNode ΒΆ

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

GridNode lays children out in equal-width columns. Build one with Grid.

func Grid ΒΆ

func Grid(cols int, children ...Child) GridNode

Grid fills cols columns row by row; cols below one means one column. A short last row keeps the column widths. Each row is as tall as its tallest cell; rows share extra height equally, and give up height in proportion to their own when short of it. A slot child sizes its content horizontally within its cell.

func (GridNode) Gap ΒΆ

func (n GridNode) Gap(x, y int) GridNode

Gap sets the horizontal and vertical space between cells.

func (GridNode) ID ΒΆ

func (n GridNode) ID[K comparable](id K) GridNode

ID sets the ID that Frame.Rect refers to the grid by.

func (GridNode) On ΒΆ

func (n GridNode) On(handlers ...on.Handler) GridNode

On adds mouse handlers to the grid, made by package on.

func (GridNode) View ΒΆ

func (n GridNode) View() string

View implements Child, drawing the grid at its natural size.

type LayerNode ΒΆ

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

LayerNode is a child of an Overlay, such as a dialog, placed over the base. Build one with Layer.

func Layer ΒΆ

func Layer(child Child) LayerNode

Layer returns a layer showing child at its natural size, in the middle of its overlay.

func (LayerNode) Anchor ΒΆ

func (n LayerNode) Anchor(x, y Align) LayerNode

Anchor sets where the layer goes in its overlay on each axis.

func (LayerNode) AnchorTo ΒΆ

func (n LayerNode) AnchorTo[K comparable](id K, side Direction) LayerNode

AnchorTo places the layer against side of the node with ID id in the overlay's base, such as a dropdown below its input, and aligned with its start, shifted to stay on screen. A corner places the layer diagonally off that corner of the node, such as below it and to its right for BottomRight. The zero value is Bottom. The layer is hidden while no such node is on screen.

func (LayerNode) Animate ΒΆ

func (n LayerNode) Animate(opts ...anim.Option) LayerNode

Animate smoothly moves and resizes the layer when layout changes. It needs an ID and a retained Root; without either it does nothing. Options default to anim.NewValue and are captured when the ID first appears. Root controls the clock and FPS. A hidden anchor or removed layer disappears immediately.

func (LayerNode) Backdrop ΒΆ

func (n LayerNode) Backdrop(effects ...fx.Effect) LayerNode

Backdrop applies effects to everything drawn beneath the layer, with the layer's transition progress as their strength.

func (LayerNode) ID ΒΆ

func (n LayerNode) ID[K comparable](id K) LayerNode

ID sets the ID that focus, layout animation, anchoring, and Frame.Rect refer to the layer by.

func (LayerNode) Margin ΒΆ

func (n LayerNode) Margin(cells int) LayerNode

Margin keeps the layer at least cells away from the overlay's edges, shrinking it if needed.

func (LayerNode) Modal ΒΆ

func (n LayerNode) Modal() LayerNode

Modal makes the layer take every mouse event from what is beneath it. Clicks outside it run its on.ClickOutside handlers, such as to close a dialog when its backdrop is clicked. It is also a focus scope, for Focus.Push.

func (LayerNode) MoveWith ΒΆ

func (n LayerNode) MoveWith(effects ...fx.Effect) LayerNode

MoveWith has LayerNode.Animate move the layer with effects rather than by gliding, as SlotNode.MoveWith does a slot: it snaps to its new place, and effects draw it on its way there from where it was, over the layers before it. It does nothing without Animate.

func (LayerNode) Offset ΒΆ

func (n LayerNode) Offset(x, y int) LayerNode

Offset moves the layer from where it is anchored.

func (LayerNode) On ΒΆ

func (n LayerNode) On(handlers ...on.Handler) LayerNode

On adds mouse handlers to the layer, made by package on.

func (LayerNode) Presence ΒΆ

func (n LayerNode) Presence(p anim.Presence, effects ...fx.Effect) LayerNode

Presence sets the transition from p, omitting the entire layer, including its backdrop, shadow, and input entries, while p is not visible.

func (LayerNode) Shadow ΒΆ

func (n LayerNode) Shadow(x, y int, effects ...fx.Effect) LayerNode

Shadow applies effects to the frame beneath the layer, offset by x, y, the way a drop shadow darkens what is under it.

func (LayerNode) Size ΒΆ

func (n LayerNode) Size(w, h int) LayerNode

Size sets the layer's size; zero for either is the child's natural size. The size is clamped to the overlay, less the margin.

func (LayerNode) Transition ΒΆ

func (n LayerNode) Transition(t float64, effects ...fx.Effect) LayerNode

Transition draws the layer with effects applied to its cells for progress t, as with Transition, and sets the strength of its backdrop and shadow.

func (LayerNode) View ΒΆ

func (n LayerNode) View() string

View implements Child, drawing the layer's child at its natural size.

type Layouter ΒΆ

type Layouter interface {
	Child
	Layout(ctx Context) Child
}

Layouter is a component that builds its own layout for the space it is given, which lets it change with its size. Layout must not change the component: it is called while measuring as well as for the final layout.

type MarqueeNode ΒΆ

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

MarqueeNode is a line of text that scrolls when it does not fit. Build one with Marquee.

func Marquee ΒΆ

func Marquee(s string, phase float64) MarqueeNode

Marquee returns a single line showing s, a string that may hold ANSI styles and whose newlines are shown as spaces. Text that fits its width is shown as it is, at its natural width, so the node shrinks in a Fill slot. Text that does not fit is cut off with "…" while phase is 0, and otherwise scrolls: phase from 0 to 1 moves it one full loop, the text and the gap after it, wrapping around seamlessly. A phase outside that range wraps.

Show a marquee at phase 0 until something, such as focus, gives it a phase to scroll with. A looping Sequence from package anim can drive it:

anim.NewSequence(0, anim.Jump(0), anim.To(1, anim.Duration(8*time.Second), anim.Ease(anim.Linear)))

func (MarqueeNode) Gap ΒΆ

func (n MarqueeNode) Gap(s string) MarqueeNode

Gap sets what is shown between the end of the text and its start again while scrolling, three spaces by default.

func (MarqueeNode) Layout ΒΆ

func (n MarqueeNode) Layout(ctx Context) Child

Layout implements Layouter.

func (MarqueeNode) Style ΒΆ

func (n MarqueeNode) Style(style lipgloss.Style) MarqueeNode

Style renders the text with style, at the size the node is given as TextNode.Style does, so a background fills it. The style's padding and borders count against the width the text has to scroll in.

func (MarqueeNode) View ΒΆ

func (n MarqueeNode) View() string

View implements Child, drawing the marquee at its natural size.

type Measurer ΒΆ

type Measurer interface {
	Measure(avail Size) Size
}

Measurer is a component that knows its natural size, so its View is not measured. Measure returns the size the component wants within avail; the result is clamped to avail. Return what the component needs, not avail itself: when a layout is drawn at its natural size, avail is unbounded.

type Option ΒΆ

type Option func(*config)

Option configures a Root.

func WithClock ΒΆ

func WithClock(now func() time.Time) Option

WithClock sets the clock for layout animations and for telling double clicks apart, for tests. It must not be nil.

func WithColors ΒΆ

func WithColors(fg, bg color.Color) Option

WithColors sets the terminal's default colors, for effects that blend colors, until the terminal reports its own (see Root.Init).

func WithFPS ΒΆ

func WithFPS(n int) Option

WithFPS sets the layout animation tick rate, defaulting to 60. Values below one use one frame per second.

func WithFocus ΒΆ

func WithFocus[K comparable](f *Focus[K]) Option

WithFocus lets clicks move the model's focus: a click focuses the innermost node under the pointer that f can focus, as Focus.Next would, before any handler for the click runs, so a handler can move the focus on from there. f must stay where it is, as a field of a model used by pointer does.

func WithUnfocusedFPS ΒΆ

func WithUnfocusedFPS(n int) Option

WithUnfocusedFPS sets the layout animation tick rate while the terminal is not focused, defaulting to 20, and never above WithFPS. Terminals report focus only while tea.View ReportFocus is set, as on the view from Root.View; until they do, the root counts the terminal as focused.

func WithWidthMethod ΒΆ

func WithWidthMethod(method ansi.Method) Option

WithWidthMethod sets how the width of graphemes is measured. It must match the terminal's, which Bubble Tea measures with ansi.GraphemeWidth when the terminal supports it. The default is ansi.GraphemeWidth, as Lip Gloss uses.

type OverlayNode ΒΆ

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

OverlayNode puts layers over a base. Build one with Overlay.

func Overlay ΒΆ

func Overlay(base Child, layers ...LayerNode) OverlayNode

Overlay draws layers, in order, over base, which gets all the overlay's space. Layers do not take space from the base.

func (OverlayNode) View ΒΆ

func (n OverlayNode) View() string

View implements Child, drawing the node at its natural size.

type Root ΒΆ

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

Root renders a layout as a model's tea.View, keeping what it can between frames. Keep one in the root model, by pointer, and pass it the model's layout from View. Pass every message to Root.Update and return its command.

func New ΒΆ

func New(opts ...Option) *Root

New returns a Root.

func (*Root) Click ΒΆ

func (r *Root) Click[K comparable](id K) (tea.Cmd, bool)

Click runs the handlers for a click of the left button in the middle of the part of the node with ID id that is on screen, as Root.ClickAt does, and reports whether the node is on screen.

func (*Root) ClickAt ΒΆ

func (r *Root) ClickAt(x, y int) tea.Cmd

ClickAt runs the handlers for a click of the left button at x, y, as a press and a release with nothing drawn between them, and returns their commands. It is for tests, which can also pass any tea.MouseMsg to Root.Update.

func (*Root) Frame ΒΆ

func (r *Root) Frame() *Frame

Frame returns the geometry of the last frame rendered, for moving focus with Focus and for finding where nodes are on screen.

func (*Root) Init ΒΆ

func (r *Root) Init() tea.Cmd

Init asks the terminal for its default colors, which effects that blend colors use. Return it from the model's Init. Pass every message to Root.Update and return its command from the model's Update.

func (*Root) Render ΒΆ

func (r *Root) Render(w, h int, c Child) string

Render lays out and draws c in a wΓ—h frame and returns it as a string. A height of zero or less is c's natural height, for programs that do not use the whole screen. Nothing is drawn while the width is zero or less, as it is before Bubble Tea reports the window's size.

func (*Root) Update ΒΆ

func (r *Root) Update(msg tea.Msg) tea.Cmd

Update notes what the terminal reports about itself: its default colors, color profile, and focus. Pass every message to Update and return its command: it schedules frames while layout animations run, fewer while the terminal is not focused (see WithUnfocusedFPS), and returns the commands of mouse handlers it runs. It runs them for mouse events that the OnMouse handler of Root.View did not see, such as when the view is built some other way, and runs on.Hover handlers as nodes move under the pointer or the window loses focus.

func (*Root) View ΒΆ

func (r *Root) View(w, h int, c Child) tea.View

View lays out and draws c in a wΓ—h frame, and returns a view showing it, with the cursor of the TextNode that placed one, and an OnMouse handler that runs the mouse handlers of the nodes under the pointer in this frame, which is the one on screen when Bubble Tea calls it. It runs them on the event loop, before Update gets the event, so they can change the model, and returns their commands to run in the background.

type ScrollNode ΒΆ

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

ScrollNode shows part of a child bigger than the space it is given. Build one with Scroll.

func Scroll ΒΆ

func Scroll(child Child, offset int) ScrollNode

Scroll gives child as much height as it wants and shows it from row offset, clipped to the space the scroll is given. The child is measured and drawn whole, so content far larger than the screen, such as a long log, is best windowed by the component itself.

func (ScrollNode) View ΒΆ

func (n ScrollNode) View() string

View implements Child, drawing the node at its natural size.

func (ScrollNode) X ΒΆ

func (n ScrollNode) X(offset int) ScrollNode

X scrolls the child sideways too: it gets as much width as it wants, such as for lines that should not wrap, and is shown from column offset. Without it, the child gets the scroll's width.

type ScrollbarNode ΒΆ

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

ScrollbarNode is a scrollbar, one column wide, or one row tall when horizontal, that shows which part of some content is in view. Build one with Scrollbar or ScrollbarOf.

It is drawn only while the content is bigger than its view, so given to BoxNode.Edge on a border, it shows over the border while there is more to scroll to, and takes no space. Its natural length is zero, so place it with Stretch there, or in a Row, or a Column when horizontal, which stretches it across.

func Scrollbar ΒΆ

func Scrollbar(total, visible, offset int) ScrollbarNode

Scrollbar returns a scrollbar for content of total rows, visible of them in view from row offset, as a Bubbles viewport reports with TotalLineCount, VisibleLineCount, and YOffset. When horizontal, they are columns.

func ScrollbarOf ΒΆ

func ScrollbarOf[K comparable](id K) ScrollbarNode

ScrollbarOf returns a scrollbar for the Scroll that the node with ID id is in, which follows the scroll from its layout, with no state of its own. It is not drawn while there is no such node on screen.

func (ScrollbarNode) Horizontal ΒΆ

func (n ScrollbarNode) Horizontal() ScrollbarNode

Horizontal makes the scrollbar one row tall, for content scrolled sideways, such as with ScrollNode.X, and for a border's top or bottom.

func (ScrollbarNode) OnScroll ΒΆ

func (n ScrollbarNode) OnScroll(fn func(offset int) tea.Cmd) ScrollbarNode

OnScroll runs fn with the offset to scroll to when the scrollbar is used with the mouse, so it needs no state in the model beyond the offset itself. Pressing on the thumb and moving drags it; pressing on the rest of the scrollbar jumps so the thumb is centered on the pointer, and holding the button down drags it from there.

func (ScrollbarNode) Thumb ΒΆ

func (n ScrollbarNode) Thumb(glyph string, style lipgloss.Style) ScrollbarNode

Thumb sets the glyph and style of the part of the scrollbar for what is in view. The default glyph is "┃", or "━" when horizontal, with no style.

func (ScrollbarNode) Track ΒΆ

func (n ScrollbarNode) Track(glyph string, style lipgloss.Style) ScrollbarNode

Track sets the glyph and style of the rest of the scrollbar. The default is no glyph, which draws nothing there, leaving a border beneath it shown.

func (ScrollbarNode) View ΒΆ

func (n ScrollbarNode) View() string

View implements Child, drawing the node at its natural size.

type Size ΒΆ

type Size struct {
	W, H int
}

Size is a width and height in cells.

type SkeletonNode ΒΆ

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

SkeletonNode is a placeholder for content that is still loading. Build one with Skeleton.

func Skeleton ΒΆ

func Skeleton(lines ...int) SkeletonNode

Skeleton returns a placeholder for content that is still loading: a bar of blocks for each of lines, that many cells wide, with 0 for a blank line. Give it a line for each line of the content it stands in for, so that nothing moves when the content arrives:

if !m.loaded {
	return bloom.Box(card, bloom.Skeleton(8, 0, 5, 11).Shimmer(m.shimmer.Float()))
}
return bloom.Box(card, bloom.Column(title, bloom.Fixed(1, nil), value, unit))

Bars are cut to the width the skeleton is given. They are drawn in the terminal's foreground, faded most of the way into its background, unless given a SkeletonNode.Style.

func (SkeletonNode) Layout ΒΆ

func (n SkeletonNode) Layout(ctx Context) Child

Layout implements Layouter.

func (SkeletonNode) Shimmer ΒΆ

func (n SkeletonNode) Shimmer(phase float64) SkeletonNode

Shimmer sweeps a band of light across the skeleton, as fx.Shimmer does, at phase from 0 to 1. A looping sequence from package anim can drive it, started in Init and stopped once the content arrives:

shimmer: anim.NewLoop(1400 * time.Millisecond),

For a band of another color or width, draw the skeleton in a Transition with an fx.Shimmer instead.

func (SkeletonNode) Style ΒΆ

func (n SkeletonNode) Style(style lipgloss.Style) SkeletonNode

Style draws the bars with style, whose foreground colors them.

func (SkeletonNode) View ΒΆ

func (n SkeletonNode) View() string

View implements Child, drawing the skeleton at its natural size.

type SlotNode ΒΆ

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

SlotNode places a child in a Row or Column with a sizing rule. Build one with Fixed, Fill, Grow, Percent, or Auto. A child given to a row or column directly is placed as with Auto.

func Auto ΒΆ

func Auto(child Child) SlotNode

Auto places child at its natural size. It can shrink when space runs short, down to its Min, which is zero unless set.

func Fill ΒΆ

func Fill(child Child) SlotNode

Fill places child in an equal share of the space left by its siblings.

func Fixed ΒΆ

func Fixed(n int, child Child) SlotNode

Fixed places child at exactly n cells on the main axis. A nil child is empty space, such as Fixed(1, nil) for a blank line in a column; Space is empty space that grows to fill instead.

func Grow ΒΆ

func Grow(weight int, child Child) SlotNode

Grow places child in a share of the space left by its siblings, weighted against the other growing children's weights.

func Percent ΒΆ

func Percent(p int, child Child) SlotNode

Percent places child at p percent of its container's main axis. It can shrink when space runs short.

func Space ΒΆ

func Space() SlotNode

Space is an empty slot that fills the space left by its siblings.

func (SlotNode) Align ΒΆ

func (n SlotNode) Align(a Align) SlotNode

Align overrides the container's cross-axis alignment for this slot.

func (SlotNode) Animate ΒΆ

func (n SlotNode) Animate(opts ...anim.Option) SlotNode

Animate smoothly moves and resizes the slot when layout changes, reflowing its child at each intermediate size. It needs an ID and a retained Root; without either it does nothing. Zero-size slots animate closed; collapsed slots and removed nodes disappear immediately. Options default to anim.NewValue and are captured when the ID first appears. Root controls the clock and FPS.

func (SlotNode) Collapsible ΒΆ

func (n SlotNode) Collapsible(priority int) SlotNode

Collapsible lets the slot be dropped, with the gap beside it, when its container cannot fit the minimum sizes of all its children. Slots with the lowest priority are dropped first.

func (SlotNode) Focusable ΒΆ

func (n SlotNode) Focusable() SlotNode

Focusable makes the slot one that Focus can move to.

func (SlotNode) ID ΒΆ

func (n SlotNode) ID[K comparable](id K) SlotNode

ID sets the ID that focus, layout animation, and Frame.Rect refer to the slot by.

func (SlotNode) Max ΒΆ

func (n SlotNode) Max(cells int) SlotNode

Max sets the largest size the slot grows to on the main axis.

func (SlotNode) Min ΒΆ

func (n SlotNode) Min(cells int) SlotNode

Min sets the smallest size the slot shrinks to on the main axis.

func (SlotNode) MoveWith ΒΆ

func (n SlotNode) MoveWith(effects ...fx.Effect) SlotNode

MoveWith has SlotNode.Animate move the slot with effects rather than by gliding: when layout changes where it goes, it snaps there, and effects are applied to its cells as its animation runs from 0 to 1, with canvas.Surface.From where it was. Its cells may be drawn anywhere between the two, over the rest of its container, such as by fx.Fly, which flies them over. A slot still gliding, such as one that was a dragged layer with the same ID, finishes its glide first. It does nothing without Animate.

func (SlotNode) On ΒΆ

func (n SlotNode) On(handlers ...on.Handler) SlotNode

On adds mouse handlers to the slot, made by package on.

func (SlotNode) Scope ΒΆ

func (n SlotNode) Scope() SlotNode

Scope makes the slot a focus scope: Focus.Within reports whether focus is inside it, and Focus.Push confines focus to it. A slot needs an ID to be a scope.

func (SlotNode) View ΒΆ

func (n SlotNode) View() string

View implements Child, drawing the slot at its natural size.

type TextNode ΒΆ

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

TextNode is a string, optionally styled. Build one with Text.

func Text ΒΆ

func Text(s string) TextNode

Text returns a node showing s, a string that may hold ANSI styles.

func (TextNode) Cursor ΒΆ

func (n TextNode) Cursor(c *tea.Cursor) TextNode

Cursor shows the terminal cursor at c, relative to the node's top-left cell, while that cell is on screen and not covered by a later layer.

func (TextNode) Focusable ΒΆ

func (n TextNode) Focusable() TextNode

Focusable makes the node one that Focus can move to.

func (TextNode) HoverStyle ΒΆ

func (n TextNode) HoverStyle(style lipgloss.Style) TextNode

HoverStyle renders the text with style instead of its own while the pointer is over it, which needs no state in the model. It should take the same space as the node's style, changing only how it looks. See on.Hover for when the pointer is followed.

func (TextNode) ID ΒΆ

func (n TextNode) ID[K comparable](id K) TextNode

ID sets the ID that focus and Frame.Rect refer to the node by.

func (TextNode) On ΒΆ

func (n TextNode) On(handlers ...on.Handler) TextNode

On adds mouse handlers to the node, made by package on.

func (TextNode) Style ΒΆ

func (n TextNode) Style(style lipgloss.Style) TextNode

Style renders the text with style at the size the node is given, so a background fills the node and long lines wrap to its width. A width or height set on style itself is kept.

func (TextNode) Truncate ΒΆ

func (n TextNode) Truncate() TextNode

Truncate shows one line, replacing newlines with spaces and cutting text that does not fit with "…". Padding and borders count against its width.

func (TextNode) View ΒΆ

func (n TextNode) View() string

View implements Child, drawing the node at its natural size.

type WrapNode ΒΆ

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

WrapNode flows children at their natural sizes onto successive lines. Build one with Wrap.

func Wrap ΒΆ

func Wrap(children ...Child) WrapNode

Wrap places children left to right, starting a new line when the next child does not fit. Each line is as tall as its tallest child. An overwide child gets a line of its own, clipped to the container. Slot sizing rules do not apply: every child keeps its natural size.

func (WrapNode) Gap ΒΆ

func (n WrapNode) Gap(x, y int) WrapNode

Gap sets the horizontal space between children and vertical space between lines.

func (WrapNode) ID ΒΆ

func (n WrapNode) ID[K comparable](id K) WrapNode

ID sets the ID that Frame.Rect refers to the flow by.

func (WrapNode) On ΒΆ

func (n WrapNode) On(handlers ...on.Handler) WrapNode

On adds mouse handlers to the flow, made by package on.

func (WrapNode) View ΒΆ

func (n WrapNode) View() string

View implements Child, drawing the flow at its natural size.

Directories ΒΆ

Path Synopsis
Package anim animates numbers in a Bubble Tea model: a Value eases or springs toward a target, and a Presence is a value from 0 to 1 for showing and hiding something, such as a dialog.
Package anim animates numbers in a Bubble Tea model: a Value eases or springs toward a target, and a Presence is a value from 0 to 1 for showing and hiding something, such as a dialog.
Package canvas holds the cell grids the layout engine composites: a Canvas of cells, parsed once from styled strings and cached by Cache, copied onto a frame with Canvas.Blit, and handed to effects as a Surface.
Package canvas holds the cell grids the layout engine composites: a Canvas of cells, parsed once from styled strings and cached by Cache, copied onto a frame with Canvas.Blit, and handed to effects as a Surface.
Package flex shares a fixed number of terminal cells between tracks, the rows or columns of a container, using integer arithmetic only.
Package flex shares a fixed number of terminal cells between tracks, the rows or columns of a container, using integer arithmetic only.
Package fx has cell effects: functions that transform a node's rendered cells, or the frame beneath a dialog, for a progress value.
Package fx has cell effects: functions that transform a node's rendered cells, or the frame beneath a dialog, for a progress value.
internal
motion
Package motion tracks the layout animations of boxes with IDs from one frame to the next: boxes gliding to where layout puts them, and boxes moving there with effects.
Package motion tracks the layout animations of boxes with IDs from one frame to the next: boxes gliding to where layout puts them, and boxes moving there with effects.
Package on makes mouse handlers for bloom nodes, attached with their On method:
Package on makes mouse handlers for bloom nodes, attached with their On method:

Jump to

Keyboard shortcuts

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