Skip to content

Commit 80fbe01

Browse files
committed
fix snapshot
1 parent 9988a14 commit 80fbe01

File tree

5 files changed

+578
-39
lines changed

5 files changed

+578
-39
lines changed

colors.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,20 @@ const (
8181
NamedColorDimForeground = 268 // Dim foreground
8282
)
8383

84-
// resolveDefaultColor converts a color.Color to RGBA using the default palette.
85-
// If c is nil, returns the default foreground or background based on fg.
84+
// ResolveDefaultColor converts a color.Color to RGBA using the default palette.
85+
// If c is nil, returns the default foreground or background based on the fg parameter.
8686
// IndexedColor and NamedColor are resolved using DefaultPalette.
87+
//
88+
// Example:
89+
//
90+
// cell := term.Cell(0, 0)
91+
// fgColor := headlessterm.ResolveDefaultColor(cell.Fg, true)
92+
// bgColor := headlessterm.ResolveDefaultColor(cell.Bg, false)
93+
func ResolveDefaultColor(c color.Color, fg bool) color.RGBA {
94+
return resolveDefaultColor(c, fg)
95+
}
96+
97+
// resolveDefaultColor is the internal implementation.
8798
func resolveDefaultColor(c color.Color, fg bool) color.RGBA {
8899
if c == nil {
89100
if fg {

doc.go

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
// Package headlessterm provides a headless VT220-compatible terminal emulator.
2+
//
3+
// This package emulates a terminal without any display, making it ideal for:
4+
// - Testing terminal applications without a GUI
5+
// - Building terminal multiplexers and recorders
6+
// - Creating terminal-based web applications
7+
// - Automated testing of CLI tools
8+
// - Screen scraping and automation
9+
//
10+
// # Quick Start
11+
//
12+
// Create a terminal and write ANSI sequences to it:
13+
//
14+
// term := headlessterm.New()
15+
// term.WriteString("\x1b[31mHello \x1b[32mWorld\x1b[0m!")
16+
// fmt.Println(term.String()) // "Hello World!"
17+
//
18+
// # Architecture
19+
//
20+
// The package is organized around these core types:
21+
//
22+
// - [Terminal]: The main emulator that processes ANSI sequences
23+
// - [Buffer]: A 2D grid of cells with scrollback support
24+
// - [Cell]: A single character with colors and attributes
25+
// - [Cursor]: Tracks position and rendering style
26+
//
27+
// # Terminal
28+
//
29+
// Terminal is the main entry point. It implements [io.Writer] so you can write
30+
// raw bytes containing ANSI escape sequences:
31+
//
32+
// term := headlessterm.New(
33+
// headlessterm.WithSize(24, 80), // 24 rows, 80 columns
34+
// headlessterm.WithScrollback(storage), // Enable scrollback
35+
// headlessterm.WithResponse(ptyWriter), // Handle terminal responses
36+
// )
37+
//
38+
// // Process output from a command
39+
// cmd := exec.Command("ls", "-la", "--color")
40+
// cmd.Stdout = term
41+
// cmd.Run()
42+
//
43+
// // Read the result
44+
// for row := 0; row < term.Rows(); row++ {
45+
// fmt.Println(term.LineContent(row))
46+
// }
47+
//
48+
// # Dual Buffers
49+
//
50+
// Terminal maintains two buffers:
51+
//
52+
// - Primary buffer: Normal mode with optional scrollback storage
53+
// - Alternate buffer: Used by full-screen apps (vim, less, htop), no scrollback
54+
//
55+
// Applications switch buffers via ANSI sequences (CSI ?1049h/l). Check which
56+
// buffer is active:
57+
//
58+
// if term.IsAlternateScreen() {
59+
// // Full-screen app is running
60+
// }
61+
//
62+
// # Cells and Attributes
63+
//
64+
// Each cell stores a character with styling information:
65+
//
66+
// cell := term.Cell(row, col)
67+
// if cell != nil {
68+
// fmt.Printf("Char: %c\n", cell.Char)
69+
// fmt.Printf("Bold: %v\n", cell.HasFlag(headlessterm.CellFlagBold))
70+
// fmt.Printf("FG: %v\n", cell.Fg)
71+
// fmt.Printf("BG: %v\n", cell.Bg)
72+
// }
73+
//
74+
// Cell flags include: Bold, Dim, Italic, Underline, Blink, Reverse, Hidden, Strike.
75+
//
76+
// # Colors
77+
//
78+
// Colors are stored using Go's [image/color] interface. The package supports:
79+
//
80+
// - Named colors (indices 0-15 for standard ANSI colors)
81+
// - 256-color palette (indices 0-255)
82+
// - True color (24-bit RGB via [color.RGBA])
83+
//
84+
// Use [ResolveDefaultColor] to convert any color to RGBA:
85+
//
86+
// rgba := headlessterm.ResolveDefaultColor(cell.Fg, true)
87+
//
88+
// # Scrollback
89+
//
90+
// Lines scrolled off the top of the primary buffer can be stored for later access.
91+
// Implement [ScrollbackProvider] or use the built-in memory storage:
92+
//
93+
// // In-memory scrollback with 10000 line limit
94+
// storage := headlessterm.NewMemoryScrollback(10000)
95+
// term := headlessterm.New(headlessterm.WithScrollback(storage))
96+
//
97+
// // Access scrollback
98+
// for i := 0; i < term.ScrollbackLen(); i++ {
99+
// line := term.ScrollbackLine(i) // []Cell
100+
// }
101+
//
102+
// # Providers
103+
//
104+
// Providers handle terminal events and queries. All are optional with no-op defaults:
105+
//
106+
// - [ResponseProvider]: Writes terminal responses (cursor position, etc.)
107+
// - [BellProvider]: Handles bell/beep events
108+
// - [TitleProvider]: Handles window title changes (OSC 0/1/2)
109+
// - [ClipboardProvider]: Handles clipboard operations (OSC 52)
110+
// - [ScrollbackProvider]: Stores lines scrolled off screen
111+
// - [RecordingProvider]: Captures raw input for replay
112+
// - [SizeProvider]: Provides pixel dimensions for queries
113+
// - [ShellIntegrationProvider]: Handles shell integration marks (OSC 133)
114+
//
115+
// Example with providers:
116+
//
117+
// term := headlessterm.New(
118+
// headlessterm.WithResponse(os.Stdout),
119+
// headlessterm.WithBell(&MyBellHandler{}),
120+
// headlessterm.WithTitle(&MyTitleHandler{}),
121+
// )
122+
//
123+
// # Middleware
124+
//
125+
// Middleware intercepts ANSI handler calls for custom behavior:
126+
//
127+
// mw := &headlessterm.Middleware{
128+
// Input: func(r rune, next func(rune)) {
129+
// log.Printf("Input: %c", r)
130+
// next(r) // Call default handler
131+
// },
132+
// Bell: func(next func()) {
133+
// log.Println("Bell!")
134+
// // Don't call next() to suppress the bell
135+
// },
136+
// }
137+
// term := headlessterm.New(headlessterm.WithMiddleware(mw))
138+
//
139+
// # Terminal Modes
140+
//
141+
// Various terminal behaviors are controlled by mode flags:
142+
//
143+
// term.HasMode(headlessterm.ModeLineWrap) // Auto line wrap enabled?
144+
// term.HasMode(headlessterm.ModeShowCursor) // Cursor visible?
145+
// term.HasMode(headlessterm.ModeBracketedPaste) // Bracketed paste enabled?
146+
//
147+
// See [TerminalMode] for all available modes.
148+
//
149+
// # Dirty Tracking
150+
//
151+
// Track which cells changed for efficient rendering:
152+
//
153+
// if term.HasDirty() {
154+
// for _, pos := range term.DirtyCells() {
155+
// // Redraw cell at pos.Row, pos.Col
156+
// }
157+
// term.ClearDirty()
158+
// }
159+
//
160+
// # Selection
161+
//
162+
// Manage text selections for copy/paste:
163+
//
164+
// term.SetSelection(
165+
// headlessterm.Position{Row: 0, Col: 0},
166+
// headlessterm.Position{Row: 2, Col: 10},
167+
// )
168+
// text := term.GetSelectedText()
169+
// term.ClearSelection()
170+
//
171+
// # Search
172+
//
173+
// Find text in the visible screen or scrollback:
174+
//
175+
// matches := term.Search("error")
176+
// for _, pos := range matches {
177+
// fmt.Printf("Found at row %d, col %d\n", pos.Row, pos.Col)
178+
// }
179+
//
180+
// // Search scrollback (returns negative row numbers)
181+
// scrollbackMatches := term.SearchScrollback("error")
182+
//
183+
// # Snapshots
184+
//
185+
// Capture the terminal state for serialization or rendering:
186+
//
187+
// // Text only (smallest)
188+
// snap := term.Snapshot(headlessterm.SnapshotDetailText)
189+
//
190+
// // With style segments (good for HTML rendering)
191+
// snap := term.Snapshot(headlessterm.SnapshotDetailStyled)
192+
//
193+
// // Full cell data (complete state, includes image references)
194+
// snap := term.Snapshot(headlessterm.SnapshotDetailFull)
195+
//
196+
// // Convert to JSON
197+
// data, _ := json.Marshal(snap)
198+
//
199+
// Snapshots include detailed attribute information:
200+
// - Underline styles: "single", "double", "curly", "dotted", "dashed"
201+
// - Blink types: "slow", "fast"
202+
// - Underline color (separate from foreground)
203+
// - Cell image references with UV coordinates for texture mapping
204+
//
205+
// # Image Support
206+
//
207+
// The terminal supports inline images via Sixel and Kitty graphics protocols:
208+
//
209+
// // Check if images are enabled
210+
// if term.SixelEnabled() || term.KittyEnabled() {
211+
// // Process image sequences
212+
// }
213+
//
214+
// // Access stored images
215+
// for _, placement := range term.ImagePlacements() {
216+
// img := term.Image(placement.ImageID)
217+
// // img.Data contains RGBA pixels
218+
// }
219+
//
220+
// // Configure image memory budget
221+
// term.SetImageMaxMemory(100 * 1024 * 1024) // 100MB
222+
//
223+
// # Shell Integration
224+
//
225+
// Track shell prompts and command output (OSC 133):
226+
//
227+
// term := headlessterm.New(
228+
// headlessterm.WithShellIntegration(&MyHandler{}),
229+
// )
230+
//
231+
// // Navigate between prompts
232+
// nextRow := term.NextPromptRow(currentRow, -1)
233+
// prevRow := term.PrevPromptRow(currentRow, -1)
234+
//
235+
// // Get last command output
236+
// output := term.GetLastCommandOutput()
237+
//
238+
// # Auto-Resize Mode
239+
//
240+
// In auto-resize mode, the buffer grows instead of scrolling:
241+
//
242+
// term := headlessterm.New(headlessterm.WithAutoResize())
243+
//
244+
// // Capture complete output without truncation
245+
// cmd.Stdout = term
246+
// cmd.Run()
247+
//
248+
// // Buffer has grown to fit all output
249+
// fmt.Printf("Total rows: %d\n", term.Rows())
250+
//
251+
// # Thread Safety
252+
//
253+
// All Terminal methods are safe for concurrent use. The terminal uses internal
254+
// locking to protect state. However, if you need to perform multiple operations
255+
// atomically, you should use your own synchronization.
256+
//
257+
// # Supported ANSI Sequences
258+
//
259+
// The terminal supports a comprehensive set of ANSI escape sequences including:
260+
//
261+
// - Cursor movement (CUU, CUD, CUF, CUB, CUP, HVP, etc.)
262+
// - Cursor save/restore (DECSC, DECRC)
263+
// - Erase commands (ED, EL, ECH)
264+
// - Insert/delete (ICH, DCH, IL, DL)
265+
// - Scrolling (SU, SD, DECSTBM)
266+
// - Character attributes (SGR) with full color support
267+
// - Terminal modes (DECSET, DECRST)
268+
// - Device status reports (DSR)
269+
// - Alternate screen buffer
270+
// - Bracketed paste mode
271+
// - Mouse reporting
272+
// - Window title (OSC 0/1/2)
273+
// - Clipboard (OSC 52)
274+
// - Hyperlinks (OSC 8)
275+
// - Shell integration (OSC 133)
276+
// - Sixel and Kitty graphics
277+
//
278+
// For the complete list of supported sequences, see the [go-ansicode] package
279+
// documentation.
280+
//
281+
// [go-ansicode]: https://github.com/danielgatis/go-ansicode
282+
package headlessterm

0 commit comments

Comments
 (0)