Loading...
Loading...
Low-level Go terminal primitives - cell-based rendering, input handling, screen management. Use when building custom Go terminal renderers, ultraviolet, cell buffers, or performance-critical TUI work below Bubble Tea's abstraction level.
npx skill4agent add alxxpersonal/forge charm-ultravioletuvgithub.com/charmbracelet/ultravioletscreen/layout/type Cell struct {
Content string // single grapheme cluster
Style Style // fg, bg, attrs (bold, italic, etc.)
Link Link // OSC 8 hyperlink
Width int // columns occupied (1 for normal, 2 for wide chars like CJK)
}EmptyCell" "Width == 0Lines []LineLine []Cellbuf := uv.NewBuffer(80, 24) // width, height
buf.SetCell(x, y, &cell) // write a cell
cell := buf.CellAt(x, y) // read a cell (nil if out of bounds)
buf.Resize(newW, newH) // resize, preserving content
buf.Clear() // fill with EmptyCell
buf.Fill(&cell) // fill with custom cell
buf.FillArea(&cell, area) // fill rectangular region
clone := buf.Clone() // deep copyDrawablebuf.Draw(screen, area)rbuf := uv.NewRenderBuffer(80, 24)
rbuf.SetCell(x, y, &cell) // auto-marks line as touched
rbuf.TouchLine(x, y, n) // manually mark region dirty
rbuf.TouchedLines() // count of dirty linestype Screen interface {
Bounds() Rectangle
CellAt(x, y int) *Cell
SetCell(x, y int, c *Cell)
WidthMethod() WidthMethod
}BufferScreenBufferWindowTerminalScreentype Drawable interface {
Draw(scr Screen, area Rectangle)
}BufferWindowStyledString// Root window (owns its buffer)
root := uv.NewScreen(80, 24)
// Child window with own buffer
child := root.NewWindow(x, y, width, height)
// View into parent buffer (shared memory)
view := root.NewView(x, y, width, height)MoveToMoveByResizeClonet := uv.DefaultTerminal()
// or: t := uv.NewTerminal(console, opts)
t.Start() // enter raw mode, start event loop
defer t.Stop() // restore terminal, clean up
scr := t.Screen() // returns *TerminalScreen
for ev := range t.Events() {
switch ev := ev.(type) {
case uv.WindowSizeEvent:
scr.Resize(ev.Width, ev.Height)
case uv.KeyPressEvent:
if ev.MatchString("ctrl+c") { return }
}
}scr := t.Screen()
// Screen modes
scr.EnterAltScreen() // alternate screen buffer
scr.ExitAltScreen()
// Rendering cycle
scr.SetCell(x, y, &cell)
scr.Render() // diff current vs previous state
scr.Flush() // write changes to terminal
// Or use Display for Drawable components
scr.Display(myDrawable) // clear + draw + render + flush
// Terminal features
scr.ShowCursor()
scr.SetCursorPosition(x, y)
scr.SetMouseMode(uv.MouseModeClick)
scr.SetBackgroundColor(color)
scr.SetWindowTitle("My App")
scr.SetSynchronizedUpdates(true) // mode 2026
scr.SetKeyboardEnhancements(enh) // kitty protocol
// Inline mode helper
scr.InsertAbove(content) // insert text above without disrupting screenss := uv.NewStyledString("Hello \x1b[1mWorld\x1b[0m")
ss.Draw(screen, area)Screenimport "github.com/charmbracelet/ultraviolet/screen"
screen.Clear(scr) // clear entire screen
screen.ClearArea(scr, area) // clear region
screen.Fill(scr, &cell) // fill screen
screen.FillArea(scr, &cell, area) // fill region
screen.Clone(scr) // deep copy to Buffer
screen.CloneArea(scr, area) // deep copy region
// Drawing context with stateful style
ctx := screen.NewContext(scr)
ctx.SetForeground(ansi.Red)
ctx.SetBold(true)
ctx.DrawString("hello", x, y)
ctx.Printf("count: %d", n) // implements io.Writerimport "github.com/charmbracelet/ultraviolet/layout"
// Split area vertically into 3 parts
chunks := layout.New().
Direction(layout.Vertical).
Constraints(
layout.Len(3), // fixed 3 rows
layout.Fill(1), // fill remaining
layout.Len(1), // fixed 1 row
).
Split(area)LenRatioPercentFillMinMaxt.Events()| Event | Description |
|---|---|
| Terminal resized (width, height in cells) |
| Terminal resized (width, height in pixels) |
| Key pressed. Use |
| Key released (requires kitty keyboard protocol) |
| Mouse click with position and button |
| Mouse moved (requires mouse mode enabled) |
| Bracketed paste content |
"ctrl+a""shift+enter""alt+tab""f1""space"image.Pointimage.Rectanglepos := uv.Pos(x, y) // == image.Point{X: x, Y: y}
rect := uv.Rect(x, y, width, height) // origin + size (NOT min/max)uv.Rect(x, y, w, h)image.Rect(x0, y0, x1, y1)style := uv.Style{
Fg: ansi.Red,
Bg: ansi.Black,
UnderlineColor: ansi.Blue,
Underline: uv.UnderlineCurly,
Attrs: uv.AttrBold | uv.AttrItalic,
}AttrBoldAttrFaintAttrItalicAttrBlinkAttrReverseAttrConcealAttrStrikethroughUnderlineNoneUnderlineSingleUnderlineDoubleUnderlineCurlyUnderlineDottedUnderlineDashedSetCellRender()Flush()package main
import (
"log"
uv "github.com/charmbracelet/ultraviolet"
"github.com/charmbracelet/ultraviolet/screen"
)
func main() {
t := uv.DefaultTerminal()
scr := t.Screen()
scr.EnterAltScreen()
if err := t.Start(); err != nil {
log.Fatal(err)
}
defer t.Stop()
ctx := screen.NewContext(scr)
for ev := range t.Events() {
switch ev := ev.(type) {
case uv.WindowSizeEvent:
scr.Resize(ev.Width, ev.Height)
case uv.KeyPressEvent:
if ev.MatchString("q", "ctrl+c") {
return
}
}
screen.Clear(scr)
ctx.DrawString("Hello, World!", 0, 0)
scr.Render()
scr.Flush()
}
}ultraviolet (primitives)
|
+-- Lip Gloss v2 (styling, composition)
|
+-- Bubble Tea v2 (framework: Elm architecture, state management, commands)
|
+-- Bubbles (components: text input, list, table, etc.)ProgramModelUpdateViewStyleexamples/