gitstack

grindlemire/go-tui code browser

8.4 KB markdown 212 lines 2026-06-12 · 896358a raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
<p align="center">
  <picture>
    <source media="(prefers-color-scheme: light)" srcset="docs/public/go-tui-logo-light-bg.svg">
    <source media="(prefers-color-scheme: dark)" srcset="docs/public/go-tui-logo.svg">
    <img alt="go-tui" src="docs/public/go-tui-logo.svg" width="310">
  </picture>
</p>

<p align="center">
  <strong>Reactive Terminal UIs in Go</strong>
</p>

<p align="center">
  <a href="https://pkg.go.dev/github.com/grindlemire/go-tui"><img src="https://pkg.go.dev/badge/github.com/grindlemire/go-tui.svg" alt="Go Reference"></a>
  <a href="https://github.com/grindlemire/go-tui/actions/workflows/ci.yml"><img src="https://github.com/grindlemire/go-tui/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://github.com/grindlemire/go-tui/blob/main/LICENSE"><img src="https://img.shields.io/github/license/grindlemire/go-tui" alt="License"></a>
  <a href="https://goreportcard.com/report/github.com/grindlemire/go-tui"><img src="https://goreportcard.com/badge/github.com/grindlemire/go-tui" alt="Go Report Card"></a>
  <a href="https://codecov.io/gh/grindlemire/go-tui"><img src="https://codecov.io/gh/grindlemire/go-tui/branch/main/graph/badge.svg" alt="Coverage"></a>
</p>

<p align="center">
  Define terminal interfaces in <code>.gsx</code> templates with HTML-like syntax and Tailwind-style classes. <br>
  The compiler generates type-safe Go. The runtime handles flexbox layout, reactive state, and rendering.
</p>

<p align="center">
  <a href="https://go-tui.dev">Guides & API Reference</a> &middot;
  <a href="#examples">Examples</a> &middot;
  <a href="#editor-support">Editor Support</a>
</p>

<p align="center">
  <strong>Pre-1.0</strong>: go-tui is under active development. Some APIs may evolve as the project matures.
</p>

---

## Install

```bash
go get github.com/grindlemire/go-tui
go install github.com/grindlemire/go-tui/cmd/tui@latest
```

## Quick look

**counter.gsx**

```go
package main

import (
    "fmt"
    tui "github.com/grindlemire/go-tui"
)

type counter struct {
    count *tui.State[int]
}

func Counter() *counter {
    return &counter{count: tui.NewState(0)}
}

func (c *counter) KeyMap() tui.KeyMap {
    return tui.KeyMap{
        tui.On(tui.Rune('+'), func(ke tui.KeyEvent) { c.count.Update(func(v int) int { return v + 1 }) }),
        tui.On(tui.Rune('-'), func(ke tui.KeyEvent) { c.count.Update(func(v int) int { return v - 1 }) }),
        tui.On(tui.Rune('q'), func(ke tui.KeyEvent) { ke.App().Stop() }),
    }
}

templ (c *counter) Render() {
    <div class="flex-col items-center justify-center h-full gap-1">
        <span class="font-bold text-cyan">{fmt.Sprintf("Count: %d", c.count.Get())}</span>
        <span class="font-dim">+/- to change, q to quit</span>
    </div>
}
```

**main.go**

```go
package main

import (
    "fmt"
    "os"
    tui "github.com/grindlemire/go-tui"
)

func main() {
    app, err := tui.NewApp(tui.WithRootComponent(Counter()))
    if err != nil {
        fmt.Fprintf(os.Stderr, "%v\n", err)
        os.Exit(1)
    }
    defer app.Close()
    if err := app.Run(); err != nil {
        fmt.Fprintf(os.Stderr, "%v\n", err)
        os.Exit(1)
    }
}
```

```bash
tui generate counter.gsx
go run .
```

The `generate` command compiles `.gsx` files into plain Go source (`*_gsx.go`) that you can read, debug, and commit.

## What's in the box

- `.gsx` templates with HTML-like elements and Tailwind-style utility classes, compiled to type-safe Go
- Pure Go flexbox layout without CGO: row, column, justify, align, gap, padding, margin, percentage widths, min/max constraints
- Generic `State[T]` with automatic re-rendering, batched updates, and bindings
- Struct components with keyboard/mouse handlers, watchers for timers and channels, refs, and a `{children...}` slot
- Modal dialogs with backdrop, focus trapping, and preemptive key handling
- Language server, formatter, and tree-sitter grammar for VS Code
- Only depends on `golang.org/x/{sys,tools}`, pure Go from terminal to layout

## How it works

```
.gsx files
    │  tui generate

Go source (*_gsx.go)
    │  go build

Widget tree + flexbox layout engine


Double-buffered character grid
    │  diff-based updates

Terminal (ANSI escape sequences)
```

The `.gsx` compiler runs at build time and produces regular Go files. At runtime, the program builds a tree of `*tui.Element` nodes. The layout engine positions them with flexbox, and a double-buffered renderer diffs the output to minimize terminal writes.

## Examples

The [`examples/`](examples/) directory has runnable programs for each feature area. Examples 01 through 23 accompany the [guides](https://go-tui.dev).

| Example | What it covers |
|---------|----------------|
| [01-getting-started](examples/01-getting-started) | Minimal component, gradient text, quit handling |
| [02-gsx-syntax](examples/02-gsx-syntax) | GSX file structure, templ syntax, control flow |
| [03-styling](examples/03-styling) | Colors, text styles, borders, conditional styling |
| [04-layout](examples/04-layout) | Flexbox row/column, justify, align, reusable layouts |
| [05-elements](examples/05-elements) | Built-in elements, disabled state, progress bars |
| [06-state](examples/06-state) | `State[T]`, `if`/`for`/`:=` bindings, reactive children, modal dialog |
| [07-components](examples/07-components) | Component composition, tabs, `{children...}` slot |
| [08-events](examples/08-events) | Keyboard event handling, `KeyMap`, `OnKey`/`OnRune` |
| [09-refs-and-clicks](examples/09-refs-and-clicks) | Refs, click handling, mouse + keyboard, modal with onActivate |
| [10-scrolling](examples/10-scrolling) | Scrollable containers, keyboard navigation |
| [11-focus](examples/11-focus) | Focus management, tab cycling |
| [12-watchers](examples/12-watchers) | Interval timers, channel watchers, live data |
| [13-testing](examples/13-testing) | Unit testing components |
| [14-multi-component](examples/14-multi-component) | Multi-file components, shared state |
| [15-inline-mode](examples/15-inline-mode) | Inline terminal rendering mode |
| [16-streaming](examples/16-streaming) | Auto-scroll, stick-to-bottom, streaming data |
| [17-inline-streaming](examples/17-inline-streaming) | Inline mode with streaming content |
| [18-dashboard](examples/18-dashboard) | Metrics, sparklines, scrollable event log |
| [19-print](examples/19-print) | One-shot rendering, print and exit |
| [20-animation](examples/20-animation) | Frame-cycling spinners, eased progress, color wave, pulsing border |
| [21-directory-tree](examples/21-directory-tree) | Foldable directory tree, lazy loading, scroll-to-cursor, path highlighting |
| [22-event-loop](examples/22-event-loop) | Custom event loops via `Open`, `Step`, `Events`, `Dispatch`, `Render` |
| [23-event-dump](examples/23-event-dump) | Diagnostic event log for verifying key, mouse, and resize handling |

See also [`ai-chat`](examples/ai-chat) and [`docs-example`](examples/docs-example).

```bash
cd examples/01-getting-started && go run .
```

## CLI

```
tui generate [path...]       Compile .gsx files to Go source
tui check [path...]          Validate without generating
tui fmt [path...]            Format .gsx files in place
tui fmt --check [path...]    Check formatting without modifying
tui lsp                      Start the language server (stdio)
tui version                  Print version
```

`tui generate ./...` compiles all `.gsx` files under the current directory.

## Editor support

**VS Code**: Install the extension from [`editor/vscode/`](editor/vscode/). Syntax highlighting, completion, hover, go-to-definition, diagnostics, and formatting.

**Neovim**: Install the plugin from [`editor/nvim/`](editor/nvim/). Syntax highlighting via tree-sitter, LSP integration, and filetype detection. See the [Neovim README](editor/nvim/README.md) for setup instructions.

**Helix**: Tree-sitter grammar at [`editor/tree-sitter-gsx/`](editor/tree-sitter-gsx/).

The `tui lsp` language server works with any editor that speaks JSON-RPC over stdio. It proxies Go-specific features through gopls with `.gsx` to `.go` source mapping.

## Documentation

Guides, syntax reference, and API docs: **[go-tui.dev](https://go-tui.dev)**

## Why are there so many files?

I wanted the imports to all be at the root so there is only a single package for the user to import and worry about (rather than an elements, styles, layout, etc.). Go Unfortunately doesn't have any indirection semantics for imports so I had to put a bunch of files in the root.

## License

MIT