An open-source project by Truffle

glyph. Components for the terminal.

Beautifully designed components for terminal UIs. Yours to copy, paste, own. Install the CLI, run glyph add chat-thread, and a chat surface drops into your repo as plain Go source you own. No glyph runtime dependency. No version pinning. No magic.

go install github.com/truffle-dev/glyph/cmd/glyph@latest
cd path/to/your/project
glyph init
glyph add chat-thread

v0.1 is Bubble Tea. Adapters for ratatui, Textual, and Ink follow.

glyph · reel
A thirty-second TUI reel showing glyph's chat thread, command palette, log stream, sidebar with toast, progress bar, and diff view, all composed from copy-paste components.
Six surfaces in thirty seconds. Every frame is real Bubble Tea output, recorded from examples/reel. The whole binary fits in one file.

v0.1 · sixteen components

Ten opinionated surfaces, one design language.

Each card shows the smallest hello-world that compiles after a glyph add. Click through for the full source, the JSON manifest, and the install command.

Theme

theme
Terminal preview of the theme component.

Token palette every component reads from. Edit one file to retheme an entire app.

import "github.com/truffle-dev/glyph/components/theme"

t := theme.Default     // dark
t := theme.Light       // warm paper
fmt.Println(t.Primary) // lipgloss.Color
glyph add theme source ↗

Chat bubble

chat-bubble
Terminal preview of the chat-bubble component.

Role-aware speech bubble with width-aware wrapping. user / assistant / system / tool.

b := chatbubble.New(theme.Default).
  WithRole(chatbubble.RoleAssistant).
  WithLabel("glyph").
  WithText("Welcome.").
  WithWidth(72)
fmt.Println(b.View())
glyph add chat-bubble source ↗

Chat input

chat-input
Terminal preview of the chat-input component.

Single-line chat prompt with placeholder, cursor, focus state, submit and cancel bindings.

i := chatinput.New(theme.Default).
  WithPlaceholder("Type a message…").
  WithPrompt("you › ").
  WithWidth(72).
  Focus()
glyph add chat-input source ↗

Chat thread

chat-thread
Terminal preview of the chat-thread component.

Vertically scrolling conversation surface. Composes chat-bubble. Arrow keys, PgUp/PgDn, Home/End.

t := chatthread.New(theme.Default).WithSize(72, 12)
t = t.Append(chatthread.Message{
  Role: chatbubble.RoleAssistant,
  Label: "glyph", Text: "Welcome.",
})
glyph add chat-thread source ↗

Command palette

command-palette
Terminal preview of the command-palette component.

Filterable modal command picker. Substring matcher by default; swap in your own.

p := commandpalette.New(theme.Default).
  WithCommands([]commandpalette.Command{
    {ID: "save", Title: "Save file", Keybinding: "ctrl+s"},
  }).
  WithSize(72, 14)
glyph add command-palette source ↗

Markdown viewer

markdown-viewer
Terminal preview of the markdown-viewer component.

Scrollable terminal markdown. Headings, paragraphs, bullets, blockquotes, code, links.

md := markdownviewer.New(theme.Default).
  WithSize(80, 18).
  WithSource(source)
glyph add markdown-viewer source ↗

Log stream

log-stream
Terminal preview of the log-stream component.

Bounded color-coded log view that tails like tail -f. Level filter, capacity ring.

s := logstream.New(theme.Default).
  WithSize(96, 16).
  WithMinLevel(logstream.LevelInfo)
s = s.Append(logstream.Entry{
  Level: logstream.LevelWarn, Source: "auth",
  Message: "deprecated token format",
})
glyph add log-stream source ↗

Diff view

diff-view
Terminal preview of the diff-view component.

Unified-diff renderer with line numbers, color-coded additions and removals.

lines := diffview.ParseUnified(rawDiff)
d := diffview.New(theme.Default).
  WithSize(96, 18).
  WithLines(lines)
glyph add diff-view source ↗

Notification toast

notification-toast
Terminal preview of the notification-toast component.

Stacked dismissible notifications with level-aware coloring and per-toast TTLs.

tray := notificationtoast.New(theme.Default).
  WithWidth(48).WithMaxItems(3)
tray = tray.Push(notificationtoast.Toast{
  ID: "build-1", Level: notificationtoast.LevelSuccess,
  Title: "Success", Message: "Build complete.",
  ExpiresAt: time.Now().Add(6 * time.Second),
})
glyph add notification-toast source ↗

Status bar

status-bar
Terminal preview of the status-bar component.

Single-line three-segment status bar. Left fills from left, right anchors right, truncates left first under pressure.

bar := statusbar.New(theme.Default).
  WithWidth(80).
  WithLeft(statusbar.Item{Text: "glyph"}).
  WithCenter(statusbar.Item{Text: "main"}).
  WithRight(statusbar.Item{Text: "OK",
    Style: statusbar.StyleSuccess})
glyph add status-bar source ↗

Spinner

spinner
Terminal preview of the spinner component.

Animated single-glyph indicator with an optional label. Five styles: dots, line, arc, pulse, bounce. TickMsg carries an ID so a parent can multiplex several spinners.

s := spinner.New(theme.Default).
  WithStyle(spinner.StyleDots).
  WithLabel("Working")
glyph add spinner source ↗

Tabs

tabs
Terminal preview of the tabs component.

Horizontal labeled tab row primitive. Arrow keys or Tab cycle with wrap. Parent owns the panels rendered below.

t := tabs.New(theme.Default).
  WithTabs([]string{"chat", "logs", "diff"}).
  WithActive(0)
glyph add tabs source ↗

Panel

panel
Terminal preview of the panel component.

Bordered container with optional title and footer. The workhorse layout primitive: wrap any view in one. Two border weights, configurable padding.

p := panel.New(theme.Default).
  WithTitle("Logs").
  WithFooter("3 entries").
  WithContent(logs.View())
glyph add panel source ↗

List

list
Terminal preview of the list component.

Vertical selectable list with cursor highlight, optional hints, disabled items, and internal scrolling. The navigation primitive most agent UIs reach for after tabs.

l := list.New(theme.Default).
  WithHeight(8).
  WithItems([]list.Item{
    {Label: "Inbox", Hint: "12 unread"},
    {Label: "Drafts"},
  })
glyph add list source ↗

Progress bar

progress-bar
Terminal preview of the progress-bar component.

Determinate progress indicator with an optional label and percentage readout. Color- and glyph-tunable. Pair with spinner for indeterminate work.

bar := progressbar.New(theme.Default).
  WithPercent(0.42).
  WithLabel("uploading").
  WithWidth(40)
glyph add progress-bar source ↗

Key hints

key-hints
Terminal preview of the key-hints component.

Compact footer of key-and-description pairs. The bottom-row cheatsheet every TUI grows into. Width-clamped so it never wraps mid-binding.

hints := keyhints.New(theme.Default).
  WithHints([]keyhints.Hint{
    {Key: "Tab", Desc: "next pane"},
    {Key: "q",   Desc: "quit"},
  })
glyph add key-hints source ↗

Design principles

Four sentences that shape every component.

  1. 01

    Copy, don't depend.

    Every component is downloadable as source. No glyph runtime dependency. Delete glyph after install and your app still works.

  2. 02

    One framework at a time.

    v0.1 is Bubble Tea. Adapters for ratatui, Textual, and Ink follow. The launch will not dilute itself across three frameworks.

  3. 03

    Tokens, not hardcoded colors.

    Every component references theme.Default. Theming a whole app is one file change.

  4. 04

    Stories are tests are screenshots.

    A component without a story file doesn't ship. Stories drive the screenshot pipeline and the demo equally.

See it move

One binary. Every component, on stage.

The glyph repo ships an examples/showcase binary: five tabs, a status bar at the bottom, a toast tray overlaying every tab. The seven main component surfaces composed into one TUI. The fastest way to feel the library. The remaining nine components each ship a runnable story under components/<name>/story/.

git clone https://github.com/truffle-dev/glyph
cd glyph
go run ./examples/showcase

Tab cycles tabs forward, Shift-Tab cycles back. On any non-chat tab, t fires a toast and l appends a log entry.

Credits

The shape of glyph is borrowed from shadcn/ui, which solved this distribution problem for React. The terminal needed the same answer. Built on Bubble Tea by Charm. Open source under MIT.

The repo lives at github.com/truffle-dev/glyph. The fastest first contribution is a new component: copy components/chat-bubble/ as a template, replace the body, add a story file, open a PR.