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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
|
# Buffer and Rendering Reference
## Overview
go-tui uses a double-buffered character grid to manage terminal output. Every frame, your component tree renders into the back buffer. The framework then diffs the back buffer against the front buffer, sends only the changed cells to the terminal, and swaps the buffers. This keeps screen updates fast and flicker-free.
Most applications never interact with the buffer directly. The `App` handles layout, rendering, and flushing automatically. These types matter when you're writing tests (reading buffer contents), building custom rendering logic, or working with the lower-level drawing functions.
## Buffer
```go
type Buffer struct {
// unexported fields
}
```
A double-buffered 2D grid of `Cell` values. Writes go to the back buffer. `Diff()` computes what changed, and `Swap()` promotes the back buffer to front.
### NewBuffer
```go
func NewBuffer(width, height int) *Buffer
```
Creates a new buffer with the given dimensions. Both buffers are initialized with spaces and default styling. Negative dimensions are clamped to 0.
```go
buf := tui.NewBuffer(80, 24)
```
### Query methods
#### Width
```go
func (b *Buffer) Width() int
```
Returns the buffer width in columns.
#### Height
```go
func (b *Buffer) Height() int
```
Returns the buffer height in rows.
#### Size
```go
func (b *Buffer) Size() (width, height int)
```
Returns both dimensions at once.
#### Rect
```go
func (b *Buffer) Rect() Rect
```
Returns the buffer bounds as a `Rect` starting at `(0, 0)`.
```go
r := buf.Rect() // Rect{X: 0, Y: 0, Width: 80, Height: 24}
```
### Reading cells
#### Cell
```go
func (b *Buffer) Cell(x, y int) Cell
```
Returns the cell at `(x, y)` from the back buffer. Returns an empty `Cell{}` if the position is out of bounds.
```go
c := buf.Cell(5, 3)
if c.Rune == '>' {
// ...
}
```
### Writing cells
#### SetCell
```go
func (b *Buffer) SetCell(x, y int, c Cell)
```
Sets the cell at `(x, y)` in the back buffer. Does nothing if the position is out of bounds.
```go
buf.SetCell(0, 0, tui.NewCell('X', tui.NewStyle().Bold()))
```
#### SetRune
```go
func (b *Buffer) SetRune(x, y int, r rune, style Style)
```
Sets a rune at `(x, y)` with the given style. Handles wide characters (CJK, emoji) by automatically placing continuation cells. Also cleans up any overlapped wide characters at the target position.
If a wide character (width 2) would land in the last column, a space is placed instead since the character can't fit.
```go
buf.SetRune(10, 5, 'A', tui.NewStyle().Foreground(tui.ANSIColor(tui.Cyan)))
```
#### SetString
```go
func (b *Buffer) SetString(x, y int, s string, style Style) int
```
Writes a string starting at `(x, y)`. Returns the total display width consumed. Stops at the buffer edge without wrapping. Handles wide characters correctly.
```go
w := buf.SetString(2, 0, "Hello, world!", tui.NewStyle())
// w == 13
```
#### SetStringClipped
```go
func (b *Buffer) SetStringClipped(x, y int, s string, style Style, clipRect Rect) int
```
Writes a string clipped to a rectangle. Characters outside `clipRect` are not rendered. Returns the display width of rendered characters.
```go
clip := tui.NewRect(5, 0, 10, 1)
buf.SetStringClipped(0, 0, "This is a long string", tui.NewStyle(), clip)
// Only characters within columns 5-14 are drawn
```
### Gradient writing
#### SetStringGradient
```go
func (b *Buffer) SetStringGradient(x, y int, s string, g Gradient, baseStyle Style) int
```
Writes a string with a gradient applied per-character along the horizontal axis. The gradient color is applied to the foreground. Returns the total display width consumed.
```go
g := tui.NewGradient(tui.ANSIColor(tui.Cyan), tui.ANSIColor(tui.Magenta))
buf.SetStringGradient(0, 0, "Rainbow Text", g, tui.NewStyle())
```
#### FillGradient
```go
func (b *Buffer) FillGradient(rect Rect, r rune, g Gradient, baseStyle Style)
```
Fills a rectangle with a gradient background. The gradient direction determines how the color interpolates:
| Direction | Interpolation |
|-----------|--------------|
| `GradientHorizontal` | Left to right |
| `GradientVertical` | Top to bottom |
| `GradientDiagonalDown` | Top-left to bottom-right |
| `GradientDiagonalUp` | Bottom-left to top-right |
The gradient color is applied to the background of each cell.
```go
g := tui.NewGradient(tui.ANSIColor(tui.Blue), tui.ANSIColor(tui.Green))
g = g.WithDirection(tui.GradientVertical)
buf.FillGradient(buf.Rect(), ' ', g, tui.NewStyle())
```
### Fill and clear
#### Fill
```go
func (b *Buffer) Fill(rect Rect, r rune, style Style)
```
Fills a rectangle with the given rune and style. The rect is intersected with the buffer bounds, so out-of-bounds regions are ignored.
```go
buf.Fill(tui.NewRect(0, 0, 20, 5), '.', tui.NewStyle().Foreground(tui.ANSIColor(tui.BrightBlack)))
```
#### Clear
```go
func (b *Buffer) Clear()
```
Clears the entire back buffer to spaces with default styling. Equivalent to `ClearRect(buf.Rect())`.
#### ClearRect
```go
func (b *Buffer) ClearRect(rect Rect)
```
Clears a rectangular region to spaces with default styling. Handles wide characters at the edges of the cleared region (clears originating or continuation cells as needed).
### Diffing and swapping
#### Diff
```go
func (b *Buffer) Diff() []CellChange
```
Returns all cells that changed between the front and back buffers. Changes are returned in row-major order (top-to-bottom, left-to-right), which minimizes cursor movement when writing to the terminal.
#### Swap
```go
func (b *Buffer) Swap()
```
Copies the back buffer to the front buffer. Call this after flushing changes to the terminal so the next diff starts from the current state.
The typical rendering cycle is:
```go
// 1. Write to the back buffer
buf.SetString(0, 0, "Hello", style)
// 2. Compute what changed
changes := buf.Diff()
// 3. Send changes to the terminal
term.Flush(changes)
// 4. Promote back to front
buf.Swap()
```
### Output
#### String
```go
func (b *Buffer) String() string
```
Renders the back buffer to a string for debugging or testing. Each row is separated by a newline. Continuation cells (from wide characters) are skipped.
```go
fmt.Println(buf.String())
```
#### StringTrimmed
```go
func (b *Buffer) StringTrimmed() string
```
Like `String()`, but trailing spaces are removed from each line. Useful in tests where you want to assert on content without worrying about trailing whitespace.
```go
got := buf.StringTrimmed()
if got != "Hello\nWorld" {
t.Errorf("unexpected: %q", got)
}
```
### Resizing
#### Resize
```go
func (b *Buffer) Resize(width, height int)
```
Changes the buffer dimensions. Content in the overlapping region is preserved; new areas are filled with spaces. Does nothing if the dimensions haven't changed. Negative values are clamped to 0.
## Cell
```go
type Cell struct {
Rune rune // The character (0 for continuation cells)
Style Style // Visual styling
Width uint8 // Display width: 1 for normal, 2 for wide, 0 for continuation
}
```
A single character cell in the terminal buffer. Wide characters (CJK, emoji) occupy two cells: the first cell holds the rune with `Width == 2`, and the next cell is a continuation with `Rune == 0` and `Width == 0`.
### NewCell
```go
func NewCell(r rune, style Style) Cell
```
Creates a Cell with automatic width detection via `RuneWidth()`.
```go
c := tui.NewCell('A', tui.NewStyle().Bold())
// c.Width == 1
c = tui.NewCell('漢', tui.NewStyle())
// c.Width == 2
```
### NewCellWithWidth
```go
func NewCellWithWidth(r rune, style Style, width uint8) Cell
```
Creates a Cell with an explicit width. Use this for continuation cells (`width == 0`) or when the width is already known.
```go
// Continuation cell for the second half of a wide character
cont := tui.NewCellWithWidth(0, tui.NewStyle(), 0)
```
### IsContinuation
```go
func (c Cell) IsContinuation() bool
```
Returns `true` if this cell is the second half of a wide character (`Width == 0`). Continuation cells should not be drawn directly.
### Equal
```go
func (c Cell) Equal(other Cell) bool
```
Returns `true` if both cells have the same rune, style, and width. Used internally by `Diff()` to detect changes.
### IsEmpty
```go
func (c Cell) IsEmpty() bool
```
Returns `true` if the cell is blank. A cell is empty when:
- Its rune is `0` (regardless of style), or
- Its rune is `' '` (space) with default styling
### RuneWidth
```go
func RuneWidth(r rune) int
```
Package-level function that returns the display width of a rune in terminal cells. Returns 1 for most characters and 2 for wide characters (CJK ideographs, fullwidth forms, emoji).
Zero-width Unicode categories (combining marks, variation selectors, format controls) are treated as width 1 in this model because `Width == 0` is reserved for continuation cells.
```go
tui.RuneWidth('A') // 1
tui.RuneWidth('漢') // 2
tui.RuneWidth('🎉') // 2
```
## CellChange
```go
type CellChange struct {
X, Y int
Cell Cell
}
```
Represents a single cell that differs between the front and back buffers. Returned by `Buffer.Diff()` and consumed by `Terminal.Flush()`.
## Render functions
These package-level functions handle the full render cycle.
### Render
```go
func Render(term Terminal, buf *Buffer)
```
The primary rendering function for normal frame updates. Computes the diff between front and back buffers, flushes only the changed cells to the terminal via `term.Flush()`, then swaps the buffers. If nothing changed, no terminal I/O occurs.
### RenderFull
```go
func RenderFull(term Terminal, buf *Buffer)
```
Forces a complete redraw. Sends every cell to the terminal regardless of whether it changed. Calls `term.Clear()` first, then flushes all cells, then swaps. Use this after:
- Initial application startup
- Terminal resize
- Recovering from external terminal corruption
- Switching back from alternate screen
### RenderTree
```go
func RenderTree(buf *Buffer, root *Element)
```
Traverses an element tree and draws each element to the buffer. Handles background fills, borders, text content, gradients, scroll clipping, and overflow clipping. This is the bridge between the layout system and the buffer: after layout calculates positions, `RenderTree` writes the visual output.
```go
root := tui.New(
tui.WithText("Hello"),
tui.WithBorder(tui.BorderRounded),
)
root.Calculate(80, 24)
buf := tui.NewBuffer(80, 24)
tui.RenderTree(buf, root)
fmt.Println(buf.StringTrimmed())
```
## Drawing functions
These functions draw borders and filled boxes directly into a buffer. They live in the `tui` package alongside the buffer types. For full details, see [Styling Reference](styling.md).
```go
func DrawBox(buf *Buffer, rect Rect, border BorderStyle, style Style)
func DrawBoxGradient(buf *Buffer, rect Rect, border BorderStyle, g Gradient, baseStyle Style)
func DrawBoxClipped(buf *Buffer, rect Rect, border BorderStyle, style Style, clipRect Rect)
func DrawBoxGradientClipped(buf *Buffer, rect Rect, border BorderStyle, g Gradient, baseStyle Style, clipRect Rect)
func DrawBoxWithTitle(buf *Buffer, rect Rect, border BorderStyle, title string, style Style)
func FillBox(buf *Buffer, rect Rect, r rune, style Style)
```
| Function | Description |
|----------|-------------|
| `DrawBox` | Draws a border around a rectangle. |
| `DrawBoxGradient` | Draws a border with a gradient applied to the border characters. |
| `DrawBoxClipped` | Draws a border clipped to a visible region. |
| `DrawBoxGradientClipped` | Draws a gradient border clipped to a visible region. |
| `DrawBoxWithTitle` | Draws a border with a title string inset in the top edge. |
| `FillBox` | Fills the interior of a bordered rectangle (inside the border, not including it). |
## See also
- [Element Reference](element.md) -- element tree that produces the content rendered into buffers
- [Styling Reference](styling.md) -- Style, Color, Gradient, and BorderStyle types
- [Terminal Reference](terminal.md) -- the Terminal interface that receives flushed changes
- [Testing Reference](testing.md) -- MockTerminal for reading buffer output in tests
|