-
Notifications
You must be signed in to change notification settings - Fork 0
Embedding
go-term is a Go library. You embed it into a go-gui application. This page covers the two embedding layers:
-
term— the core widget. It gives you one terminal in one window. -
term/workspace— the pane manager. It gives you tabs and split panes.
The examples/minimal directory holds a complete single-terminal program.
The examples/falcon directory holds a complete workspace program.
- Go 1.26 or newer (the
godirective ingo.mod) - a
go-guiwindow - macOS, Linux, or Windows
Add the module to your project:
go get github.com/go-gui-org/go-term@latest
Pin a minor version. go-term is pre-1.0, and minor bumps can add fields.
This program creates a window, starts a shell, and renders it:
package main
import (
"log"
"github.com/go-gui-org/go-gui/gui"
"github.com/go-gui-org/go-gui/gui/backend"
"github.com/go-gui-org/go-term/term"
)
func main() {
gui.SetTheme(gui.ThemeDark.WithBorders(true))
var tm *term.Term
w := gui.NewWindow(gui.WindowCfg{
Title: "go-term minimal",
Width: 800,
Height: 500,
OnInit: func(w *gui.Window) {
var err error
tm, err = term.New(w, term.Cfg{
TextStyle: gui.TextStyle{Family: "monospace", Size: 14},
})
if err != nil {
log.Fatalf("term.New: %v", err)
}
w.UpdateView(tm.View)
},
})
defer func() {
if tm != nil {
_ = tm.Close()
}
}()
backend.Run(w)
}The steps:
- Create a
gui.NewWindowwith anOnInitcallback. - Call
term.NewinsideOnInit, with the window and aterm.Cfg. - Pass
tm.Viewtow.UpdateView. - Call
tm.Closewhen the window closes.
New starts the shell and its reader goroutine before it returns.
term.Cfg carries the widget's options. Every field is optional. A zero value
selects the built-in default.
| Field | Meaning |
|---|---|
TextStyle |
Font family and size. Zero means gui.CurrentTheme().M5
|
Themes |
Named themes for runtime switching. The first entry is the initial theme |
Command, Args
|
The binary to run instead of $SHELL
|
Env |
Extra environment variables for the child, applied last |
Dir |
Working directory for the child. Empty inherits the process CWD |
Identity |
Value for TERM_PROGRAM. Defaults to go-term
|
ScrollbackRows |
Scrollback cap. Zero means 5000, negative disables it |
BellMode |
How BEL is signalled: auto, audible, visual, both, none
|
ScrollbarWidth |
Scrollbar thumb width in px. Negative hides the scrollbar |
MinimumContrast |
WCAG ratio floor, 1.0–21.0, applied at render time |
MiddleClickPaste |
Middle-click pastes. Off by default |
NotifyAfter |
Notify when a long command finishes while you look away |
AllowOSC52Write |
Permit child programs to write the clipboard. Off by default |
DisableGraphics |
Skip sixel, Kitty, and iTerm2 image decoding |
NoWindowHandler |
Do not install a window event handler. Set for pane managers |
DownloadDir |
Where OSC 1337 file transfers land. Empty disables transfers |
RecordPath |
Start a session recording at this path |
RecordInput |
Record keystrokes into recordings. Off by default |
KeyBindings |
Override the default chords for term-level actions |
New spawns the child process and the reader goroutine. It also starts the
blink, scroll, momentum, write, and resize loops.
Call Close to kill the child, close the PTY, and stop the loops. Always
call it when the window closes, or you leak file descriptors and
goroutines. Close is idempotent.
Use Cfg.OnExit to detect child death. Use Term.Alive to poll it.
The exported methods are safe to call from any goroutine: View, Close,
Cwd, Theme, SetTheme, Rows, Cols, Write, PID, Alive,
SetFocused, HandleWindowEvent, SetMinimumContrast, SetKeyBindings,
StartRecording, StopRecording, SendInput.
Cfg carries callbacks for the events a host cares about:
| Callback | Fires | Thread |
|---|---|---|
OnTitle |
The child sets the window title (OSC 0/1/2) | main |
OnNotify |
A child asks for a desktop notification (OSC 9/777) | background |
OnActivity |
Output changed the screen, or the bell rang | main |
OnExit |
The child process exits | reader |
OnClickFocus |
The user clicks the canvas | main |
OnInput |
User input is about to go to the child | main |
With OnTitle nil, the widget sets the window title itself. That is right
for a single-terminal window. A pane manager sets OnTitle to keep titles
per pane.
OnInput receives every user byte sequence. It runs alongside the local
write and cannot suppress it. A pane manager mirrors input to sibling panes
with it. Mouse reporting and focus reports are excluded on purpose.
OnActivity fires at most once per PTY read, not per cell. A bell outranks
plain output in the kind it reports.
Themes are named color schemes. NamedTheme pairs a display name with a
Theme.
term.BundledThemes returns the bundled corpus, sorted by name. There are
602 themes. term.DefaultTheme is the fallback used when Cfg.Themes is
empty.
Register Default first and the corpus after it. The first entry seeds the
grid and decides COLORFGBG at spawn. That value cannot be corrected once
the child runs, so the order matters.
themes := []term.NamedTheme{
{Name: "Default", Theme: term.DefaultTheme},
}
themes = append(themes, term.BundledThemes()...)Theme.IsDark reports the light or dark character of a theme. The child
asks the same question through DSR ?996.
A light theme does not fix an application's own colors. A truecolor SGR is
not themeable. Cfg.MinimumContrast is the render-time floor that forces
text above a contrast ratio against its cell background. The grid keeps the
color the child sent, so copy, search, and recordings are unaffected.
The child environment is the parent's, with the host terminal identity
scrubbed and TERM, COLORTERM, and COLORFGBG set for the pane. Entries
in Cfg.Env apply last, so they win over all of it, including
TERM_PROGRAM.
TERM_PROGRAM is how TUI file managers choose their image protocol. yazi
and superfile use the Kitty Graphics Protocol under a name they recognize
and fall back to sixel otherwise. Set Cfg.Identity (or an Env entry) to
name a known emulator and get its image quality. Falcon advertises Falcon.
A window with several panes is a pane manager. Set Cfg.NoWindowHandler to
true so the widget does not install its own window event handler.
Then the manager owns the window-level event dispatch. It routes events to the focused pane:
- keyboard input to the focused pane's
Viewcontainer -
Term.HandleWindowEventfor the rest
Use Term.SetFocused to move focus between panes. OnClickFocus tells you
when the user clicks a pane. OnActivity tells you when a background pane
did something.
Term.Rows, Term.Cols, Term.Write, Term.PID, Term.Alive, and
Term.Cwd support introspection without touching internal state.
Term.SendInput replays captured input onto a pane, per its bracketed-paste
state.
term/workspace is the ready-made pane manager. It builds a multi-tab,
multi-pane window on top of term, and it reads the user config file.
ws, err := workspace.New(w, workspace.Cfg{
TextStyle: defaultTextStyle(),
Themes: themeList(),
Identity: "Falcon",
ConfigPath: "",
})
if err != nil {
log.Fatal(err)
}
w.UpdateView(ws.View)workspace.Cfg adds the window-level fields:
| Field | Meaning |
|---|---|
Identity |
TERM_PROGRAM for every pane. Defaults to go-term
|
ConfigPath |
Path to the user config file. Empty uses the default location |
RecordDir |
Where Cmd+Shift+R recordings land |
RecordInput |
Record keystrokes into recordings |
DownloadDir |
Where OSC 1337 transfers land. Empty disables them |
ExitWhenLastShellExits |
Close the window when the last shell exits |
OnLastShellExit |
Runs instead of the close when the last shell exits |
OnColorScheme |
Reports light/dark changes, so the host can theme its chrome |
The workspace wires the multi-pane callbacks for you: focus, activity, bell, title, exit, and broadcast input.
Workspace.Save(path) writes the tab and pane layout to a JSON file,
atomically. workspace.Restore(w, cfg, path) rebuilds the workspace from
that file.
Restore falls back to a fresh workspace on a missing, unparseable, or
version-mismatched file. A bad workspace file never blocks startup.
workspace.DefaultWorkspacePath and workspace.DefaultConfigPath return
the standard locations, resolved against $XDG_CONFIG_HOME,
~/.config, or os.UserConfigDir().
Falcon restores on startup and saves on quit, with two paths so a read-only template layout is possible. See Falcon.
The workspace layer reads the user config file: an INI-style file that overrides fonts, theme, scrollback, bell, keybindings, and the child environment. It can be reloaded without restarting. The full reference is on the Configuration page.
term.NewReplay returns a Term that plays back a .gtr recording
instead of spawning a shell. The widget runs the entire normal stack —
parser, grid, rendering, scrollback, search, graphics — against recorded
bytes.
ReplayCfg controls playback:
| Field | Meaning |
|---|---|
Path |
The .gtr file to play. Required |
Speed |
Playback multiplier. 2 plays twice as fast |
IdleLimit |
Caps any single gap between frames. Zero means no cap |
Loop |
Restart at the end of the stream |
Controls |
Interpret space, +/-, ., and 0 as playback commands |
Cfg.Command, Args, Env, and Dir are ignored for replay: no child
exists. The widget holds on the final frame until Close.
Recordings store the PTY bytes verbatim. A rendering bug reproduces in
replay, which makes recordings the preferred way to file a bug report.
Record them with StartRecording, or with falcon's Cmd+Shift+R. The
gotermrec CLI inspects and converts them. See Usage.
go-term is pre-1.0, and the contract is deliberate:
-
Cfgfields: new fields can appear. Renames and removals go through a deprecation cycle. New fields are zero-value-safe. -
Termmethods: new methods can appear. Existing signatures stay. -
Termis an opaque handle. All fields are unexported. Embedders interact through methods only. - Import only the
termpackage. The source-file organisation is not a contract.
Pin a minor version in go.mod. Use Cfg zero values for everything you do
not set. Read the changelog before a minor upgrade.