Go TUI Framework
glyph
Batteries-included terminal UI framework for Go.
Declarative composition. Your data, rendered directly. Keyboard-first. Fast.
$
go get github.com/kungfusheep/glyph@latest
copied
01
package main
import . "github.com/kungfusheep/glyph"
func main() {
app, _ := NewApp()
count := 0
app.SetView(
VBox(
Text(&count),
Text("↑/↓ to count, q to quit"),
),
)
app.Handle("up", func() { count++ })
app.Handle("down", func() { count-- })
app.Handle("q", app.Stop)
app.Run()
}
A glyph app is a tree of plain functions. VBox stacks its children, Text renders a value. Pass a pointer; glyph reads the current value every update. Keyboard handlers take plain function callbacks. Run starts the event loop and blocks until the app exits.
Follow the full guide →
Once
Build
When you call
SetView(), the declarative tree is compiled into
a flat array of operations. All reflection, type switches, and allocation
happen here. The composable API surface,
VBox.Gap(2).Border(...), is purely a build-time convenience.
Each update
Execute
Rendering walks the compiled ops and dereferences pointers to read current
state. Pointer reads into a cell buffer, then a diff-based flush to the terminal.
The execute path is zero-alloc by design.
02
File browser
Split pane with list navigation and live preview.
OnSelect loads the file, TextView renders it.
HBox(
VBox.Grow(1).Border(BorderRounded)(
List(&files).BindVimNav().OnSelect(func(f *string) {
data, _ := os.ReadFile(*f)
preview = string(data)
}),
),
VBox.Grow(2).Border(BorderRounded)(
TextView(&preview).Grow(1),
),
)
Process monitor
Progress bars for system metrics, sortable table
for processes. AutoTable infers columns from struct fields.
VBox(
HBox.Gap(4)(
Text("CPU"), Progress(&cpuPct).Width(30),
Text("Mem"), Progress(&memPct).Width(30),
),
AutoTable(&procs).Sortable().Scrollable(20).BindVimNav(),
)
Deploy log
Spinner, status text, and progress in a single row.
Log streams output live. Result appears conditionally when done.
VBox.Border(BorderRounded).Title("deploy")(
HBox.Gap(2)(
Spinner(&frame).FG(Cyan),
Text(&status).Bold(),
Progress(&pct).Width(20),
),
Log(output).Grow(1).MaxLines(500),
If(&done).Then(Text(&result).Bold().FG(Green)),
)
Fuzzy finder
FilterList handles the search input and matching.
Custom Render for each row, border and title for framing.
FilterList(&packages, func(p *Pkg) string { return p.Name }).
Render(func(p *Pkg) any {
return HBox.Gap(2)(
Text(&p.Name).Bold(),
Text(&p.Desc).FG(BrightBlack),
)
}).MaxVisible(15).Border(BorderRounded).Title("packages")
Live dashboard
Two sparkline panels side by side with a scrolling
event log below. Update the slices; glyph reads the new values on the next update.
VBox(
HBox.Gap(1)(
VBox.Grow(1).Border(BorderRounded).Title("requests/s")(
Sparkline(&reqData).FG(Green),
Text(&reqRate).FG(BrightBlack),
),
VBox.Grow(1).Border(BorderRounded).Title("p99 latency")(
Sparkline(&latData).FG(Yellow),
Text(&p99).FG(BrightBlack),
),
),
Log(events).Grow(1).MaxLines(200),
)
Registration form
Form auto-aligns labels, manages focus, and wires
validation. Errors surface on blur or submit, as configured.
Form.LabelBold().OnSubmit(register)(
Field("Name", Input(&name).Validate(VRequired, VOnBlur)),
Field("Email", Input(&email).Validate(VEmail, VOnBlur)),
Field("Role", Radio(&role, "Admin", "User", "Guest")),
Field("Terms", Checkbox(&agree, "I accept").Validate(VTrue, VOnSubmit)),
)
03
Input & Forms
Text & Streaming
Navigation
Styling
Control Flow
Rendering
04
$
go get github.com/kungfusheep/glyph@latest
copied