teapane

package module
v0.0.0-...-13ae084 Latest Latest
Warning

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

Go to latest
Published: Apr 24, 2026 License: MIT Imports: 4 Imported by: 0

README

teapane

⚠️ This README was generated with the assistance of AI (Claude by Anthropic) based on the current source code and design discussions with the author.


teapane is a Go library for building terminal UI layouts using a flexbox-inspired model. It is designed to work alongside Bubble Tea and Lip Gloss, giving you a familiar CSS-like mental model for arranging panes in a terminal window.

Status: Beta — The core rendering pipeline is working. The full flexbox feature set (grow/shrink, justify-content, align-items, wrapping, and gap) is actively being developed.


Why teapane?

Building complex terminal layouts with raw Lip Gloss requires a lot of manual size math. teapane abstracts that away by letting you describe how panes should relate to each other — much like CSS flexbox — rather than calculating exact pixel (cell) positions yourself. If you know how to write a flexbox layout in CSS, the concepts here will feel immediately familiar.


Core Concepts

Pane

A Pane is the basic building block. It holds a string of content to display, a resolved width and height, and a PaneStyle that controls how it looks and how it behaves inside a container.

pane := teapane.NewPane(40, 10, true) // width, height, hasBorder
pane.DisplayString = "Hello, terminal!"
PaneContainer

A PaneContainer holds a slice of Panes and a ContainerStyle that controls how they are laid out — the flex direction, wrapping, alignment, and so on.

container := teapane.NewContainer(
    teapane.NewContainerStyle(),
    paneA,
    paneB,
    paneC,
)
Rendering

To render a container at a given size, call RenderContainer. To render a standalone pane, call RenderPane or use the View() method on a Pane directly.

output := teapane.RenderContainer(container, totalWidth, totalHeight)
fmt.Print(output)

Sizing System

teapane uses a Size struct with three unit modes, mirroring CSS concepts:

Fixed(n) gives the pane an exact cell count regardless of the container size. Percent(n) gives the pane a percentage of the available space. Auto() means the pane will share whatever space is left over equally with other auto-sized panes.

// This pane always takes up exactly 20 columns.
pane.Style.Basis = teapane.Fixed(20)

// This pane takes up 50% of the available width.
pane.Style.Basis = teapane.Percent(50)

// This pane takes up an equal share of whatever is left.
pane.Style.Basis = teapane.Auto()

You can also set MinBasis and MaxBasis on a pane to clamp its resolved size, similar to min-width and max-width in CSS.


Borders

Borders are configured through PaneBorder and are included in the pane's total size — the content area shrinks accordingly, just like box-sizing: border-box in CSS. A default border style is provided:

// Uses the built-in rounded box-drawing border in amber.
pane.Style.Border = teapane.DefaultBorder

You can customise the corner and edge characters and the Lip Gloss color to create any border style you like.


Flex Direction

The container's FlexDirection controls whether panes flow left-to-right (DirectionRow) or top-to-bottom (DirectionColumn).

style := teapane.NewContainerStyle()
style.FlexDirection = teapane.DirectionColumn

Ordering

Panes are sorted by their Style.Order field before rendering, just like the CSS order property. Lower values appear first. This lets you reorder panes in the layout without changing their position in the slice.


Styles Reference

PaneStyle fields (current)

Grow and Shrink are defined but not yet active in the layout engine — they will control how panes expand or contract to fill leftover space. Basis and CrossBasis control main-axis and cross-axis sizing. MinBasis, MaxBasis, MinCrossBasis, and MaxCrossBasis clamp those sizes. Order controls render order. WrapText enables soft word-wrapping of the pane's content. AlignSelf will override the container's AlignItems for this specific pane. Border holds the pane's border configuration.

ContainerStyle fields (current)

FlexDirection sets the flow axis. FlexWrap, Justify, AlignItems, AlignContent, GapRow, and GapColumn are defined and will be fully active once the layout engine is extended (see Roadmap below).


Roadmap

The following features are defined in the type system but not yet implemented in the layout engine. They are planned for the next development phase, following the CSS flexbox algorithm closely.

flex-grow / flex-shrink will distribute leftover space (or absorb overflow) proportionally among panes that opt in, using a weighted shrink algorithm identical to how browsers handle it.

justify-content will control spacing along the main axis — centering items, pushing them to the ends, or distributing space between and around them.

align-items / align-self will position panes along the cross axis within their flex line, with per-pane overrides via AlignSelf.

flex-wrap will allow panes to break onto multiple lines when they overflow the container, with WrapReverse for reverse line stacking.

gap (row and column) will insert fixed space between panes without affecting justify-content free-space calculations.

Nested containers are a longer-term goal. The plan is to introduce a FlexItem interface and a separate FlexItemStyle struct so that a PaneContainer can itself be a child of another container, holding both a ContainerStyle (for its children) and a FlexItemStyle (for its own parent).


Dependencies

teapane depends on Lip Gloss for color rendering and for JoinHorizontal / JoinVertical composition. It is intended to be used with Bubble Tea, though it has no hard dependency on the Bubble Tea runtime itself — you can call RenderContainer from any context.


License

teapane is released under the MIT License. You are free to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software, provided the original copyright notice and this permission notice are included in all copies or substantial portions of the software.

Documentation

Index

Constants

View Source
const (
	DirectionRow     FlexDirection = "row"
	DirectionRowR    FlexDirection = "row-reverse"
	DirectionColumn  FlexDirection = "column"
	DirectionColumnR FlexDirection = "column-reverse"

	WrapNone    FlexWrap = "nowrap"
	WrapWrap    FlexWrap = "wrap"
	WrapReverse FlexWrap = "wrap-reverse"

	JustifyStart   Justify = "flex-start"
	JustifyEnd     Justify = "flex-end"
	JustifyCenter  Justify = "center"
	JustifyBetween Justify = "space-between"
	JustifyAround  Justify = "space-around"
	JustifyEvenly  Justify = "space-evenly"

	AlignItemStrech   AlignItems = "stretch"
	AlignItemStart    AlignItems = "flex-start"
	AlignItemEnd      AlignItems = "flex-end"
	AlignItemCenter   AlignItems = "center"
	AlignItemBaseline AlignItems = "baseline"

	AlignContentStrech  AlignContent = "stretch"
	AlignContentStart   AlignContent = "flex-start"
	AlignContentEnd     AlignContent = "flex-end"
	AlignContentBetween AlignContent = "space-between"
	AlignContentAround  AlignContent = "space-around"
	AlignContentEvenly  AlignContent = "space-evenly"
	AlignContentCenter  AlignContent = "center"

	AlignSelfAuto     AlignSelf = "auto"
	AlignSelfCenter   AlignSelf = "center"
	AlignSlefStart    AlignSelf = "flex-start"
	AlignSelfEnd      AlignSelf = "flex-end"
	AlignSelfStretch  AlignSelf = "stretch"
	AlignSelfBaseline AlignSelf = "baseline"
)

Variables

View Source
var (
	DefaultBorder = PaneBorder{Enabled: true, TopLeft: "┌", TopRight: "┐", BottomLeft: "└", BottomRight: "┘", Vertical: "│", Horizontal: "─", Color: lipgloss.Color("#FFFFFF")}
)

Functions

func RenderContainer

func RenderContainer(c PaneContainer, main, cross int) string

func RenderPane

func RenderPane(p Pane, width, height int) string

func ResolveSize

func ResolveSize(s Size, total int) int

Types

type AlignContent

type AlignContent string

type AlignItems

type AlignItems string

type AlignSelf

type AlignSelf string

type ContainerStyle

type ContainerStyle struct {
	FlexDirection FlexDirection
	FlexWrap      FlexWrap
	Justify       Justify
	AlignItems    AlignItems
	AlignContent  AlignContent
	GapRow        int
	GapColumn     int
}

func NewContainerStyle

func NewContainerStyle() ContainerStyle

type FlexDirection

type FlexDirection string

type FlexWrap

type FlexWrap string

type Justify

type Justify string

type Pane

type Pane struct {
	Height        int
	Width         int
	DisplayHeight int
	DisplayWidth  int
	DisplayString string
	Style         PaneStyle
}

func NewPane

func NewPane(width, height int, haveBorder bool) Pane

func ResolvePaneSizes

func ResolvePaneSizes(panes []Pane, main, cross int) []Pane

func (Pane) View

func (p Pane) View() string

type PaneBorder

type PaneBorder struct {
	Enabled     bool
	TopLeft     string
	TopRight    string
	BottomLeft  string
	BottomRight string
	Horizontal  string
	Vertical    string
	Color       lipgloss.Color
}

type PaneContainer

type PaneContainer struct {
	Panes []Pane
	Style ContainerStyle
}

func NewContainer

func NewContainer(style ContainerStyle, panes ...Pane) PaneContainer

type PaneStyle

type PaneStyle struct {
	Grow          int
	Shrink        int
	Basis         Size
	CrossBasis    Size
	MinBasis      int
	MaxBasis      int
	MinCrossBasis int
	MaxCrossBasis int
	Order         int
	WrapText      bool
	AlignSelf     AlignSelf
	Border        PaneBorder
}

type Size

type Size struct {
	Value int
	Unit  SizeUnit
}

func Auto

func Auto() Size

func Fixed

func Fixed(n int) Size

func Percent

func Percent(n int) Size

type SizeUnit

type SizeUnit int
const (
	UnitAuto SizeUnit = iota
	UnitFixed
	UnitPercent
)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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