### Creating Headless Emulator with emulator.New Source: https://context7.com/taigrr/bubbleterm/llms.txt Illustrates how to create a new, headless terminal emulator instance without Bubbletea integration. This is useful for programmatic access to terminal output, testing, or integration with other frameworks. The example shows starting a command and retrieving the rendered screen output. ```go package main import ( "fmt" "log" "os/exec" "time" "github.com/taigrr/bubbleterm/emulator" ) func main() { // Create a new emulator with 80 columns and 24 rows emu, err := emulator.New(80, 24) if err != nil { log.Fatal(err) } defer emu.Close() // Start a command cmd := exec.Command("htop") if err := emu.StartCommand(cmd); err != nil { log.Fatal(err) } // Wait for output to appear time.Sleep(1 * time.Second) // Get the rendered screen frame := emu.GetScreen() fmt.Println("Terminal output:") for i, row := range frame.Rows { fmt.Printf("%2d: %s\n", i, row) } // Check damage tracking for changed lines fmt.Printf("\nDamaged lines: %d\n", len(frame.Damage)) for _, damage := range frame.Damage { fmt.Printf(" Row %d: columns %d-%d\n", damage.Row, damage.X1, damage.X2) } } ``` -------------------------------- ### Installation Source: https://context7.com/taigrr/bubbleterm/llms.txt Instructions on how to install the Bubbleterm library using Go modules. ```APIDOC ## Installation Install the library using Go modules. ```bash go get github.com/taigrr/bubbleterm ``` ``` -------------------------------- ### Creating Emulator from Pipes with emulator.NewFromPipes Source: https://context7.com/taigrr/bubbleterm/llms.txt Explains how to create a headless terminal emulator that reads from a provided reader and writes to a writer, bypassing the need for a PTY. This method is particularly useful when the process is already running and its input/output streams are available. The example demonstrates starting a bash command and capturing its output. ```go package main import ( "fmt" "log" "os/exec" "time" "github.com/taigrr/bubbleterm/emulator" ) func main() { // Start a process with pipes cmd := exec.Command("bash", "-c", "echo 'Hello from pipes!'; sleep 1; ls -la") stdin, _ := cmd.StdinPipe() stdout, _ := cmd.StdoutPipe() if err := cmd.Start(); err != nil { log.Fatal(err) } // Create emulator from pipes emu, err := emulator.NewFromPipes(80, 24, stdout, stdin) if err != nil { log.Fatal(err) } defer emu.Close() // Wait and capture output time.Sleep(2 * time.Second) frame := emu.GetScreen() for i, row := range frame.Rows { if row != "" { fmt.Printf("%2d: %s\n", i, row) } } } ``` -------------------------------- ### Send Keyboard Input: SendKey and Write Source: https://context7.com/taigrr/bubbleterm/llms.txt Demonstrates sending keyboard input to the terminal using SendKey for strings and Write for raw bytes. Includes examples of sending commands, special keys, and interrupting processes. ```Go package main import ( "fmt" "log" "os/exec" "time" "github.com/taigrr/bubbleterm/emulator" ) func main() { emu, err := emulator.New(80, 24) if err != nil { log.Fatal(err) } defer emu.Close() // Start bash cmd := exec.Command("bash") emu.StartCommand(cmd) time.Sleep(200 * time.Millisecond) // Send a command using SendKey emu.SendKey("echo 'Hello World'\r") time.Sleep(200 * time.Millisecond) // Send raw bytes using Write emu.Write([]byte("ls -la\r")) time.Sleep(200 * time.Millisecond) // Send special keys (arrow up for history) emu.SendKey("\x1b[A") // Up arrow time.Sleep(100 * time.Millisecond) // Send Ctrl+C to interrupt emu.SendKey("\x03") frame := emu.GetScreen() for i, row := range frame.Rows { fmt.Printf("%2d: %s\n", i, row) } } ``` -------------------------------- ### Core Terminal Emulator API Usage Source: https://github.com/taigrr/bubbleterm/blob/master/README.md Provides the fundamental API methods for initializing the emulator, starting processes, retrieving rendered output, and managing lifecycle. ```go emu, err := emulator.New(width, height) cmd := exec.Command("your-command") emu.StartCommand(cmd) frame := emu.GetScreen() for _, row := range frame.Rows { fmt.Println(row) } emu.Resize(newWidth, newHeight) emu.Close() ``` -------------------------------- ### Initialize Terminal with bubbleterm.New Source: https://context7.com/taigrr/bubbleterm/llms.txt Creates a terminal model with specific dimensions without immediately starting a process. This allows for deferred command execution using StartCommand. ```go package main import ( "log" "os/exec" tea "charm.land/bubbletea/v2" "github.com/taigrr/bubbleterm" ) func main() { terminal, err := bubbleterm.New(80, 24) if err != nil { log.Fatal(err) } defer terminal.Close() cmd := exec.Command("bash") startCmd := terminal.StartCommand(cmd) _ = startCmd } ``` -------------------------------- ### bubbleterm.NewWithCommand Source: https://context7.com/taigrr/bubbleterm/llms.txt Creates a new terminal bubble and immediately starts the specified command. This is the most common way to create a terminal that runs a specific program. ```APIDOC ## bubbleterm.NewWithCommand Creates a new terminal bubble and immediately starts the specified command. This is the most common way to create a terminal that runs a specific program. ### Method ```go func NewWithCommand(cols, rows int, cmd *exec.Cmd) (*Model, error) ``` ### Parameters #### Path Parameters - **cols** (int) - Required - The number of columns for the terminal. - **rows** (int) - Required - The number of rows for the terminal. - **cmd** (*exec.Cmd) - Required - The command to execute within the terminal. ### Request Example ```go package main import ( "log" "os/exec" tea "charm.land/bubbletea/v2" "github.com/taigrr/bubbleterm" ) type model struct { terminal *bubbleterm.Model } func main() { // Create terminal and start htop in one call cmd := exec.Command("htop") terminal, err := bubbleterm.NewWithCommand(80, 24, cmd) if err != nil { log.Fatal(err) } m := model{terminal: terminal} p := tea.NewProgram(&m) if _, err := p.Run(); err != nil { log.Fatal(err) } } func (m *model) Init() tea.Cmd { m.terminal.Focus() return m.terminal.Init() } func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: if msg.String() == "ctrl+c" || msg.String() == "q" { m.terminal.Close() return m, tea.Quit } } // Forward all messages to the terminal bubble terminalModel, cmd := m.terminal.Update(msg) m.terminal = terminalModel.(*bubbleterm.Model) return m, cmd } func (m *model) View() tea.View { v := m.terminal.View() v.AltScreen = true return v } ``` ``` -------------------------------- ### Install Bubbleterm via Go Modules Source: https://context7.com/taigrr/bubbleterm/llms.txt The standard command to add the Bubbleterm library to your Go project dependencies. ```bash go get github.com/taigrr/bubbleterm ``` -------------------------------- ### bubbleterm.New Source: https://context7.com/taigrr/bubbleterm/llms.txt Creates a new terminal bubble with specified dimensions without starting a command. Use this when you want to create a terminal model and start a command later, or when integrating with existing process I/O. ```APIDOC ## bubbleterm.New Creates a new terminal bubble with specified dimensions without starting a command. Use this when you want to create a terminal model and start a command later, or when integrating with existing process I/O. ### Method ```go func New(cols, rows int) (*Model, error) ``` ### Parameters #### Path Parameters - **cols** (int) - Required - The number of columns for the terminal. - **rows** (int) - Required - The number of rows for the terminal. ### Request Example ```go package main import ( "log" "os/exec" tea "charm.land/bubbletea/v2" "github.com/taigrr/bubbleterm" ) func main() { // Create a new terminal bubble with 80 columns and 24 rows terminal, err := bubbleterm.New(80, 24) if err != nil { log.Fatal(err) } defer terminal.Close() // Start a command later using StartCommand cmd := exec.Command("bash") startCmd := terminal.StartCommand(cmd) // The startCmd can be returned from Init() or Update() in your Bubbletea model _ = startCmd } ``` ``` -------------------------------- ### Resizing Terminal with Emulator.Resize Source: https://context7.com/taigrr/bubbleterm/llms.txt Details how to change the terminal dimensions using the `Resize` method, which also updates the PTY size. This action sends a `SIGWINCH` signal to the running process, notifying it of the size change. The example shows the initial output and the output after resizing the terminal. ```go package main import ( "fmt" "log" "os/exec" "time" "github.com/taigrr/bubbleterm/emulator" ) func main() { emu, err := emulator.New(80, 24) if err != nil { log.Fatal(err) } defer emu.Close() cmd := exec.Command("htop") emu.StartCommand(cmd) // Initial output at 80x24 time.Sleep(500 * time.Millisecond) frame := emu.GetScreen() fmt.Printf("Initial size: %d rows\n", len(frame.Rows)) // Resize to 100x40 if err := emu.Resize(100, 40); err != nil { log.Fatal(err) } // Output after resize time.Sleep(500 * time.Millisecond) frame = emu.GetScreen() fmt.Printf("After resize: %d rows\n", len(frame.Rows)) for i, row := range frame.Rows { fmt.Printf("%2d: %s\n", i, row) } } ``` -------------------------------- ### Get Cursor State: Cursor Source: https://context7.com/taigrr/bubbleterm/llms.txt Demonstrates how to retrieve the current cursor's position and visibility state using the Cursor method. This is useful for custom UI rendering. ```Go package main import ( "fmt" "log" "os/exec" "time" "github.com/taigrr/bubbleterm/emulator" ) func main() { emu, err := emulator.New(80, 24) if err != nil { log.Fatal(err) } defer emu.Close() cmd := exec.Command("bash") emu.StartCommand(cmd) time.Sleep(200 * time.Millisecond) // Get cursor position pos, visible := emu.Cursor() fmt.Printf("Cursor position: (%d, %d), Visible: %t\n", pos.X, pos.Y, visible) // Type some text and check cursor again emu.SendKey("hello") time.Sleep(100 * time.Millisecond) pos, visible = emu.Cursor() fmt.Printf("After typing: (%d, %d), Visible: %t\n", pos.X, pos.Y, visible) } ``` -------------------------------- ### Control Terminal Input Focus with Bubbletea Source: https://context7.com/taigrr/bubbleterm/llms.txt Illustrates how to manage keyboard input focus for multiple terminals using bubbleterm.Model.Focus and bubbleterm.Model.Blur. When a terminal is focused, it receives key events; when blurred, it ignores them. This example cycles focus between terminals using the 'tab' key and enters/exits an 'insert mode' with 'i' and 'esc' keys. ```go package main import ( "os/exec" tea "charm.land/bubbletea/v2" "github.com/taigrr/bubbleterm" ) type model struct { terminals []*bubbleterm.Model focused int } func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "tab": // Blur current terminal m.terminals[m.focused].Blur() // Move to next terminal m.focused = (m.focused + 1) % len(m.terminals) // Focus new terminal m.terminals[m.focused].Focus() return m, nil case "i": // Enter insert mode - focus terminal m.terminals[m.focused].Focus() return m, nil case "esc": // Exit insert mode - blur terminal m.terminals[m.focused].Blur() return m, nil } } // Only forward to focused terminal if m.terminals[m.focused].Focused() { terminalModel, cmd := m.terminals[m.focused].Update(msg) m.terminals[m.focused] = terminalModel.(*bubbleterm.Model) return m, cmd } return m, nil } ``` -------------------------------- ### Manage Terminal Polling with Bubbletea Source: https://context7.com/taigrr/bubbleterm/llms.txt Explains how to control the automatic polling behavior of terminals using bubbleterm.Model.SetAutoPoll and bubbleterm.UpdateTerminal. When auto-poll is disabled, manual calls to UpdateTerminal are required to fetch new output, which is beneficial for centralized update loops managing multiple terminals. This example uses a central ticker to poll terminals. ```go package main import ( "os/exec" "time" tea "charm.land/bubbletea/v2" "github.com/taigrr/bubbleterm" ) type centralTickMsg struct{} type model struct { terminals []*bubbleterm.Model } func (m *model) Init() tea.Cmd { var cmds []tea.Cmd // Initialize all terminals with auto-poll disabled for _, term := range m.terminals { term.SetAutoPoll(false) cmds = append(cmds, term.Init()) } // Start centralized ticker at 30 FPS cmds = append(cmds, tea.Tick(time.Millisecond*33, func(time.Time) tea.Msg { return centralTickMsg{} })) return tea.Batch(cmds...) } func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg.(type) { case centralTickMsg: // Manually poll all terminals for i := range m.terminals { cmd := m.terminals[i].UpdateTerminal() if cmd != nil { cmds = append(cmds, cmd) } } // Schedule next tick cmds = append(cmds, tea.Tick(time.Millisecond*33, func(time.Time) tea.Msg { return centralTickMsg{} })) } return m, tea.Batch(cmds...) } ``` -------------------------------- ### Use Headless Terminal Emulator for Programmatic Access Source: https://github.com/taigrr/bubbleterm/blob/master/README.md Shows how to use the emulator without a TUI to run commands, capture screen frames, and resize the terminal dynamically. ```go emu, err := emulator.New(80, 24) defer emu.Close() cmd := exec.Command("htop") err = emu.StartCommand(cmd) frame := emu.GetScreen() for i, row := range frame.Rows { fmt.Printf("%2d: %s\n", i, row) } emu.Resize(100, 40) ``` -------------------------------- ### Initialize Terminal with bubbleterm.NewWithCommand Source: https://context7.com/taigrr/bubbleterm/llms.txt Creates a terminal instance and immediately executes a command. This is the recommended approach for standard TUI applications. ```go package main import ( "log" "os/exec" tea "charm.land/bubbletea/v2" "github.com/taigrr/bubbleterm" ) type model struct { terminal *bubbleterm.Model } func main() { cmd := exec.Command("htop") terminal, err := bubbleterm.NewWithCommand(80, 24, cmd) if err != nil { log.Fatal(err) } m := model{terminal: terminal} p := tea.NewProgram(&m) if _, err := p.Run(); err != nil { log.Fatal(err) } } func (m *model) Init() tea.Cmd { m.terminal.Focus() return m.terminal.Init() } func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: if msg.String() == "ctrl+c" || msg.String() == "q" { m.terminal.Close() return m, tea.Quit } } terminalModel, cmd := m.terminal.Update(msg) m.terminal = terminalModel.(*bubbleterm.Model) return m, cmd } func (m *model) View() tea.View { v := m.terminal.View() v.AltScreen = true return v } ``` -------------------------------- ### emulator.NewFromPipes Source: https://context7.com/taigrr/bubbleterm/llms.txt Initializes a headless terminal emulator using provided reader and writer pipes, bypassing the need for a PTY. This is useful for integrating with already running processes. ```APIDOC ## emulator.NewFromPipes ### Description Creates a headless terminal emulator that reads from a reader and writes to a writer, instead of using a PTY. This is useful when the process is already running. ### Method `emulator.NewFromPipes` ### Endpoint N/A (Constructor function) ### Parameters - **cols** (int) - Required - The number of columns for the terminal emulator. - **rows** (int) - Required - The number of rows for the terminal emulator. - **reader** (io.Reader) - Required - The reader to receive output from the process. - **writer** (io.Writer) - Required - The writer to send input to the process. ### Request Example ```go cmd := exec.Command("bash") stdin, _ := cmd.StdinPipe() stdout, _ := cmd.StdoutPipe() emu, err := emulator.NewFromPipes(80, 24, stdout, stdin) ``` ### Response #### Success Response (Type: *emulator.Emulator, error) - **emu** (*emulator.Emulator) - A new headless terminal emulator instance connected to the pipes. - **err** (error) - An error if the emulator could not be created. #### Response Example ```go // Example usage after creation time.Sleep(2 * time.Second) frame := emu.GetScreen() ``` ``` -------------------------------- ### Bubbletea Integration with Bubbleterm Source: https://github.com/taigrr/bubbleterm/blob/master/README.md Demonstrates how to create and integrate a Bubbleterm terminal instance within a Bubbletea application's model. It shows the necessary Init, Update, and View methods for seamless integration, handling terminal initialization and message updates. ```go // Create terminal bubble terminal, err := bubbleterm.NewWithCommand(width, height, cmd) terminal.SetAutoPoll(false) // Disable auto-polling for updates // In your Bubbletea model func (m *model) Init() tea.Cmd { return m.terminal.Init() } func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { terminalModel, cmd := m.terminal.Update(msg) m.terminal = terminalModel.(*bubbleterm.Model) return m, cmd } func (m *model) View() string { return m.terminal.View() } ``` -------------------------------- ### Accessing Emulator with Model.GetEmulator Source: https://context7.com/taigrr/bubbleterm/llms.txt Demonstrates how to retrieve the underlying emulator instance from a Bubbletea model to access low-level terminal functionalities like process monitoring and cursor position. It shows how to check if a process has exited and retrieve the cursor's coordinates. ```go package main import ( "fmt" "os/exec" tea "charm.land/bubbletea/v2" "github.com/taigrr/bubbleterm" ) type model struct { terminal *bubbleterm.Model } func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Access underlying emulator emu := m.terminal.GetEmulator() // Check if process has exited if emu.IsProcessExited() { fmt.Println("Process has exited") return m, tea.Quit } // Get cursor position pos, visible := emu.Cursor() if visible { fmt.Printf("Cursor at (%d, %d)\n", pos.X, pos.Y) } // Get emulator ID for message routing id := emu.ID() _ = id terminalModel, cmd := m.terminal.Update(msg) m.terminal = terminalModel.(*bubbleterm.Model) return m, cmd } ``` -------------------------------- ### emulator.New Source: https://context7.com/taigrr/bubbleterm/llms.txt Creates a new headless terminal emulator without Bubbletea integration, suitable for programmatic access to terminal output, testing, or integration with other frameworks. ```APIDOC ## emulator.New ### Description Creates a new headless terminal emulator without Bubbletea integration. This is ideal for programmatic access to terminal output, testing, or integration with other frameworks. ### Method `emulator.New` ### Endpoint N/A (Constructor function) ### Parameters - **cols** (int) - Required - The number of columns for the terminal emulator. - **rows** (int) - Required - The number of rows for the terminal emulator. ### Request Example ```go emu, err := emulator.New(80, 24) ``` ### Response #### Success Response (Type: *emulator.Emulator, error) - **emu** (*emulator.Emulator) - A new headless terminal emulator instance. - **err** (error) - An error if the emulator could not be created. #### Response Example ```go // Example usage after creation cmd := exec.Command("htop") if err := emu.StartCommand(cmd); err != nil { log.Fatal(err) } frame := emu.GetScreen() ``` ``` -------------------------------- ### Configure Framerate: SetFrameRate Source: https://context7.com/taigrr/bubbleterm/llms.txt Explains how to adjust the emulator's rendering framerate using SetFrameRate. Lowering the framerate can reduce CPU usage at the cost of display responsiveness. ```Go package main import ( "log" "os/exec" "github.com/taigrr/bubbleterm/emulator" ) func main() { emu, err := emulator.New(80, 24) if err != nil { log.Fatal(err) } defer emu.Close() // Set framerate to 60 FPS for smoother display emu.SetFrameRate(60) // Or set to 15 FPS to reduce CPU usage emu.SetFrameRate(15) // Default is 30 FPS emu.SetFrameRate(30) cmd := exec.Command("htop") emu.StartCommand(cmd) } ``` -------------------------------- ### Initialize Terminal with bubbleterm.NewWithPipes Source: https://context7.com/taigrr/bubbleterm/llms.txt Connects the terminal emulator to existing stdin/stdout pipes. Useful for embedding a terminal view into an already-running process. ```go package main import ( "log" "os/exec" "github.com/taigrr/bubbleterm" ) func main() { cmd := exec.Command("bash") stdin, err := cmd.StdinPipe() if err != nil { log.Fatal(err) } stdout, err := cmd.StdoutPipe() if err != nil { log.Fatal(err) } if err := cmd.Start(); err != nil { log.Fatal(err) } terminal, err := bubbleterm.NewWithPipes(80, 24, stdout, stdin) if err != nil { log.Fatal(err) } defer terminal.Close() _ = terminal } ``` -------------------------------- ### Resize Terminal Dynamically with Bubbletea Source: https://context7.com/taigrr/bubbleterm/llms.txt Demonstrates how to dynamically change the terminal dimensions using the bubbleterm.Model.Resize method. This function updates the terminal's internal state and signals the running process about the size change via SIGWINCH. It handles both window size messages and manual resize commands. ```go package main import ( "log" "os/exec" tea "charm.land/bubbletea/v2" "github.com/taigrr/bubbleterm" ) type model struct { terminal *bubbleterm.Model width int height int } func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { case tea.WindowSizeMsg: // Handle window resize m.width = msg.Width m.height = msg.Height // Resize terminal to match window (minus space for UI elements) resizeCmd := m.terminal.Resize(msg.Width-2, msg.Height-2) cmds = append(cmds, resizeCmd) case tea.KeyMsg: if msg.String() == "+" { // Increase terminal size by 5 columns and 2 rows resizeCmd := m.terminal.Resize(m.width+5, m.height+2) cmds = append(cmds, resizeCmd) } } terminalModel, cmd := m.terminal.Update(msg) m.terminal = terminalModel.(*bubbleterm.Model) cmds = append(cmds, cmd) return m, tea.Batch(cmds...) } ``` -------------------------------- ### Implement Multi-Window Terminal Manager in Go Source: https://context7.com/taigrr/bubbleterm/llms.txt A complete implementation of a multi-window manager using Bubbleterm. It features a central ticker for polling terminal processes, dynamic window creation, and keyboard-based focus management. ```go package main import ( "fmt" "os/exec" "time" tea "charm.land/bubbletea/v2" "github.com/taigrr/bubbleterm" ) type centralTickMsg struct{} type TerminalWindow struct { ID string X, Y int Width int Height int Terminal *bubbleterm.Model } type MultiWindowModel struct { Windows []TerminalWindow FocusedWindow int InsertMode bool } func NewMultiWindowModel() *MultiWindowModel { return &MultiWindowModel{ Windows: []TerminalWindow{}, FocusedWindow: -1, } } func (m *MultiWindowModel) Init() tea.Cmd { return tea.Tick(time.Millisecond*33, func(time.Time) tea.Msg { return centralTickMsg{} }) } func (m *MultiWindowModel) CreateWindow(x, y int) tea.Cmd { cmd := exec.Command("bash") terminal, err := bubbleterm.NewWithCommand(40, 12, cmd) if err != nil { return nil } terminal.SetAutoPoll(false) window := TerminalWindow{ ID: fmt.Sprintf("window-%d", len(m.Windows)), X: x, Y: y, Width: 42, Height: 14, Terminal: terminal, } m.Windows = append(m.Windows, window) m.FocusedWindow = len(m.Windows) - 1 return terminal.Init() } func (m *MultiWindowModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: if !m.InsertMode { switch msg.String() { case "ctrl+c", "q": for _, w := range m.Windows { w.Terminal.Close() } return m, tea.Quit case "i": if m.FocusedWindow >= 0 { m.InsertMode = true m.Windows[m.FocusedWindow].Terminal.Focus() } case "n": cmds = append(cmds, m.CreateWindow(5, 5)) } } else { if msg.String() == "esc" { m.InsertMode = false if m.FocusedWindow >= 0 { m.Windows[m.FocusedWindow].Terminal.Blur() } } else if m.FocusedWindow >= 0 { terminalModel, cmd := m.Windows[m.FocusedWindow].Terminal.Update(msg) m.Windows[m.FocusedWindow].Terminal = terminalModel.(*bubbleterm.Model) cmds = append(cmds, cmd) } } case centralTickMsg: var deadWindows []int for i := range m.Windows { cmd := m.Windows[i].Terminal.UpdateTerminal() cmds = append(cmds, cmd) if m.Windows[i].Terminal.GetEmulator().IsProcessExited() { deadWindows = append(deadWindows, i) } } for i := len(deadWindows) - 1; i >= 0; i-- { idx := deadWindows[i] m.Windows[idx].Terminal.Close() m.Windows = append(m.Windows[:idx], m.Windows[idx+1:]...) if m.FocusedWindow >= idx { m.FocusedWindow-- } } cmds = append(cmds, tea.Tick(time.Millisecond*33, func(time.Time) tea.Msg { return centralTickMsg{} })) } return m, tea.Batch(cmds...) } func (m *MultiWindowModel) View() tea.View { content := "Press 'n' for new window, 'i' for insert mode, ESC to exit insert mode\n" for i, w := range m.Windows { focused := "" if i == m.FocusedWindow { focused = " [FOCUSED]" } content += fmt.Sprintf("Window %s at (%d,%d)%s\n", w.ID, w.X, w.Y, focused) content += w.Terminal.View().Content + "\n" } return tea.NewView(content) } ``` -------------------------------- ### Integrate Terminal Emulator with Bubbletea Source: https://github.com/taigrr/bubbleterm/blob/master/README.md Demonstrates how to embed a terminal emulator within a Bubbletea model. It handles command execution and forwards messages to the terminal component. ```go cmd := exec.Command("htop") terminal, err := bubbleterm.NewWithCommand(80, 24, cmd) func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd terminalModel, cmd := m.terminal.Update(msg) m.terminal = terminalModel.(*bubbleterm.Model) return m, cmd } ``` -------------------------------- ### Bubbletea Integration Source: https://github.com/taigrr/bubbleterm/blob/master/README.md Demonstrates how to create and integrate a new terminal bubble into a Bubbletea model. ```APIDOC ## Bubbletea Integration ### Description This section shows how to initialize a new terminal emulator using `bubbleterm.NewWithCommand` and integrate it into a Bubbletea application's model lifecycle. ### Method `bubbleterm.NewWithCommand(width, height, cmd)` ### Endpoint N/A (Library Function) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go terminal, err := bubbleterm.NewWithCommand(width, height, cmd) terminal.SetAutoPoll(false) // Disable auto-polling for updates ``` ### Response #### Success Response (200) N/A (Library Function) #### Response Example N/A (Library Function) ### Bubbletea Model Integration #### Description Shows how to implement the `Init`, `Update`, and `View` methods of a Bubbletea model to manage the terminal emulator. #### Method Bubbletea Model Methods (`Init`, `Update`, `View`) #### Endpoint N/A (Bubbletea Model) #### Parameters None #### Request Body None ### Request Example ```go // In your Bubbletea model func (m *model) Init() tea.Cmd { return m.terminal.Init() } func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { terminalModel, cmd := m.terminal.Update(msg) m.terminal = terminalModel.(*bubbleterm.Model) return m, cmd } func (m *model) View() string { return m.terminal.View() } ``` ### Response #### Success Response (200) N/A (Bubbletea Model) #### Response Example N/A (Bubbletea Model) ``` -------------------------------- ### Monitor Process Lifecycle: SetOnExit and IsProcessExited Source: https://context7.com/taigrr/bubbleterm/llms.txt Shows how to monitor the lifecycle of the terminal process. SetOnExit registers a callback for when the process terminates, while IsProcessExited checks its current status. ```Go package main import ( "fmt" "log" "os/exec" "time" "github.com/taigrr/bubbleterm/emulator" ) func main() { emu, err := emulator.New(80, 24) if err != nil { log.Fatal(err) } defer emu.Close() // Set exit callback emu.SetOnExit(func(emulatorID string) { fmt.Printf("Process in emulator %s has exited\n", emulatorID) }) // Start a short-lived command cmd := exec.Command("bash", "-c", "echo 'Running...'; sleep 2; echo 'Done!'") emu.StartCommand(cmd) // Poll for process exit for i := 0; i < 30; i++ { if emu.IsProcessExited() { fmt.Println("Process has exited!") break } fmt.Printf("Still running... (check %d)\n", i+1) time.Sleep(100 * time.Millisecond) } // Get final output frame := emu.GetScreen() for _, row := range frame.Rows { if row != "" { fmt.Println(row) } } } ``` -------------------------------- ### Send Input to Terminal with Model.SendInput Source: https://context7.com/taigrr/bubbleterm/llms.txt Programmatically injects strings into the terminal's input stream, allowing for automated command execution or interaction. ```go func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+l": return m, m.terminal.SendInput("clear\n") case "ctrl+d": return m, m.terminal.SendInput("\x04") } } terminalModel, cmd := m.terminal.Update(msg) m.terminal = terminalModel.(*bubbleterm.Model) return m, cmd } ``` -------------------------------- ### Model.GetEmulator Source: https://context7.com/taigrr/bubbleterm/llms.txt Retrieves the underlying emulator instance from a Bubbletea model, allowing direct access to low-level functionalities such as process monitoring and cursor position. ```APIDOC ## Model.GetEmulator ### Description Returns the underlying emulator for direct access to low-level functionality like process monitoring and cursor position. ### Method (Implicitly accessed via a `model` struct) ### Endpoint N/A (Method within a struct) ### Parameters None ### Request Example ```go emu := m.terminal.GetEmulator() ``` ### Response #### Success Response (Type: *bubbleterm.Emulator) - **emu** (*bubbleterm.Emulator) - The underlying emulator instance. #### Response Example ```go // Accessing emulator properties if emu.IsProcessExited() { fmt.Println("Process has exited") } pos, visible := emu.Cursor() ``` ``` -------------------------------- ### Emulator.Resize Source: https://context7.com/taigrr/bubbleterm/llms.txt Adjusts the terminal dimensions of the emulator and notifies the underlying process of the change by sending a SIGWINCH signal. This is crucial for responsive terminal applications. ```APIDOC ## Emulator.Resize ### Description Changes the terminal dimensions and updates the PTY size. The running process receives a SIGWINCH signal to notify it of the size change. ### Method `emu.Resize(cols int, rows int)` ### Endpoint N/A (Method on an emulator instance) ### Parameters - **cols** (int) - Required - The new number of columns for the terminal. - **rows** (int) - Required - The new number of rows for the terminal. ### Request Example ```go if err := emu.Resize(100, 40); err != nil { log.Fatal(err) } ``` ### Response #### Success Response (200) No explicit return value on success, but the terminal dimensions are updated. #### Error Response (e.g., 500) - **error** (error) - An error if resizing fails. #### Response Example ```go // After resizing, you can get the screen again to see the effect frame := emu.GetScreen() fmt.Printf("New size: %d rows\n", len(frame.Rows)) ``` ``` -------------------------------- ### Advanced Bubbleterm Features: Focus, Input, and Polling Source: https://github.com/taigrr/bubbleterm/blob/master/README.md Explains advanced functionalities of Bubbleterm, including managing terminal focus (Focus, Blur, Focused), sending manual input to the terminal (SendInput), checking process exit status, and controlling the update loop with manual polling. ```go // Focus management terminal.Focus() terminal.Blur() focused := terminal.Focused() // Manual input sending terminal.SendInput("ls\n") // Process monitoring if terminal.GetEmulator().IsProcessExited() { // Handle process exit } // Auto-polling control (for custom update loops) terminal.SetAutoPoll(false) cmd := terminal.UpdateTerminal() // Manual poll ``` -------------------------------- ### Send Mouse Events: SendMouse Source: https://context7.com/taigrr/bubbleterm/llms.txt Illustrates how to send mouse events to the terminal using the SendMouse function. Covers clicks, releases, and motion events for mouse-aware applications. ```Go package main import ( "log" "os/exec" "time" "github.com/taigrr/bubbleterm/emulator" ) func main() { emu, err := emulator.New(80, 24) if err != nil { log.Fatal(err) } defer emu.Close() // Start a mouse-aware application cmd := exec.Command("htop") emu.StartCommand(cmd) time.Sleep(500 * time.Millisecond) // Mouse button constants: // 0 = Left button // 1 = Middle button // 2 = Right button // -1 = Motion (no button) // Send left click at position (10, 5) emu.SendMouse(0, 10, 5, true) // Press emu.SendMouse(0, 10, 5, false) // Release // Send mouse motion to position (20, 10) emu.SendMouse(-1, 20, 10, false) // Send right click emu.SendMouse(2, 15, 8, true) emu.SendMouse(2, 15, 8, false) } ``` -------------------------------- ### bubbleterm.NewWithPipes Source: https://context7.com/taigrr/bubbleterm/llms.txt Creates a terminal bubble that reads process output from a reader and writes user input to a writer. This is useful when embedding a terminal view for an already-running process where you have access to its stdin/stdout pipes. ```APIDOC ## bubbleterm.NewWithPipes Creates a terminal bubble that reads process output from a reader and writes user input to a writer. This is useful when embedding a terminal view for an already-running process where you have access to its stdin/stdout pipes. ### Method ```go func NewWithPipes(cols, rows int, stdout io.Reader, stdin io.Writer) (*Model, error) ``` ### Parameters #### Path Parameters - **cols** (int) - Required - The number of columns for the terminal. - **rows** (int) - Required - The number of rows for the terminal. - **stdout** (io.Reader) - Required - The reader for the process's standard output. - **stdin** (io.Writer) - Required - The writer for the process's standard input. ### Request Example ```go package main import ( "log" "os/exec" "io" "github.com/taigrr/bubbleterm" ) func main() { // Start a process manually with pipes cmd := exec.Command("bash") stdin, err := cmd.StdinPipe() if err != nil { log.Fatal(err) } stdout, err := cmd.StdoutPipe() if err != nil { log.Fatal(err) } if err := cmd.Start(); err != nil { log.Fatal(err) } // Create terminal using the pipes terminal, err := bubbleterm.NewWithPipes(80, 24, stdout, stdin) if err != nil { log.Fatal(err) } defer terminal.Close() // Use terminal in your Bubbletea model as usual _ = terminal } ``` ``` -------------------------------- ### Model.SendInput Source: https://context7.com/taigrr/bubbleterm/llms.txt Sends input string directly to the terminal. This is useful for programmatically controlling the terminal, such as sending commands or responding to prompts. ```APIDOC ## Model.SendInput Sends input string directly to the terminal. This is useful for programmatically controlling the terminal, such as sending commands or responding to prompts. ### Method ```go func (m *Model) SendInput(input string) tea.Cmd ``` ### Parameters #### Path Parameters - **input** (string) - Required - The string to send as input to the terminal. ### Request Example ```go package main import ( "log" "os/exec" tea "charm.land/bubbletea/v2" "github.com/taigrr/bubbleterm" ) type model struct { terminal *bubbleterm.Model } func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+l": // Send clear command to terminal return m, m.terminal.SendInput("clear\n") case "ctrl+d": // Send EOF return m, m.terminal.SendInput("\x04") } } terminalModel, cmd := m.terminal.Update(msg) m.terminal = terminalModel.(*bubbleterm.Model) return m, cmd } ``` ``` -------------------------------- ### Advanced Features Source: https://github.com/taigrr/bubbleterm/blob/master/README.md Explores advanced functionalities of the Bubbleterm library, including focus management, manual input, process monitoring, and custom polling. ```APIDOC ## Advanced Features ### Description This section details advanced features available in the Bubbleterm library, such as controlling terminal focus, sending input manually, checking process status, and managing the update loop. ### Focus Management #### Description Control and check the focus state of the terminal emulator. #### Methods - `Focus()`: Sets focus to the terminal. - `Blur()`: Removes focus from the terminal. - `Focused()`: Returns a boolean indicating if the terminal is focused. #### Example ```go terminal.Focus() terminal.Blur() focused := terminal.Focused() ``` ### Manual Input Sending #### Description Send raw input strings directly to the terminal emulator. #### Method `SendInput(input string)` #### Example ```go terminal.SendInput("ls\n") ``` ### Process Monitoring #### Description Check the status of the underlying process being emulated. #### Method `GetEmulator().IsProcessExited()`: Returns true if the process has exited. #### Example ```go if terminal.GetEmulator().IsProcessExited() { // Handle process exit } ``` ### Auto-polling Control #### Description Manually control the terminal's update loop, useful for custom event handling or performance optimization. #### Methods - `SetAutoPoll(false)`: Disables automatic polling. - `UpdateTerminal()`: Manually triggers an update (poll). #### Example ```go terminal.SetAutoPoll(false) cmd := terminal.UpdateTerminal() // Manual poll ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.