options.go

  1package tea
  2
  3import (
  4	"context"
  5	"io"
  6	"sync/atomic"
  7
  8	"github.com/charmbracelet/colorprofile"
  9	"github.com/charmbracelet/x/ansi"
 10)
 11
 12// ProgramOption is used to set options when initializing a Program. Program can
 13// accept a variable number of options.
 14//
 15// Example usage:
 16//
 17//	p := NewProgram(model, WithInput(someInput), WithOutput(someOutput))
 18type ProgramOption func(*Program)
 19
 20// WithContext lets you specify a context in which to run the Program. This is
 21// useful if you want to cancel the execution from outside. When a Program gets
 22// cancelled it will exit with an error ErrProgramKilled.
 23func WithContext(ctx context.Context) ProgramOption {
 24	return func(p *Program) {
 25		p.externalCtx = ctx
 26	}
 27}
 28
 29// WithOutput sets the output which, by default, is stdout. In most cases you
 30// won't need to use this.
 31func WithOutput(output io.Writer) ProgramOption {
 32	return func(p *Program) {
 33		p.output = output
 34	}
 35}
 36
 37// WithInput sets the input which, by default, is stdin. In most cases you
 38// won't need to use this. To disable input entirely pass nil.
 39//
 40//	p := NewProgram(model, WithInput(nil))
 41func WithInput(input io.Reader) ProgramOption {
 42	return func(p *Program) {
 43		p.input = input
 44		p.inputType = customInput
 45	}
 46}
 47
 48// WithInputTTY opens a new TTY for input (or console input device on Windows).
 49func WithInputTTY() ProgramOption {
 50	return func(p *Program) {
 51		p.inputType = ttyInput
 52	}
 53}
 54
 55// WithEnvironment sets the environment variables that the program will use.
 56// This useful when the program is running in a remote session (e.g. SSH) and
 57// you want to pass the environment variables from the remote session to the
 58// program.
 59//
 60// Example:
 61//
 62//	var sess ssh.Session // ssh.Session is a type from the github.com/charmbracelet/ssh package
 63//	pty, _, _ := sess.Pty()
 64//	environ := append(sess.Environ(), "TERM="+pty.Term)
 65//	p := tea.NewProgram(model, tea.WithEnvironment(environ)
 66func WithEnvironment(env []string) ProgramOption {
 67	return func(p *Program) {
 68		p.environ = env
 69	}
 70}
 71
 72// WithoutSignalHandler disables the signal handler that Bubble Tea sets up for
 73// Programs. This is useful if you want to handle signals yourself.
 74func WithoutSignalHandler() ProgramOption {
 75	return func(p *Program) {
 76		p.startupOptions |= withoutSignalHandler
 77	}
 78}
 79
 80// WithoutCatchPanics disables the panic catching that Bubble Tea does by
 81// default. If panic catching is disabled the terminal will be in a fairly
 82// unusable state after a panic because Bubble Tea will not perform its usual
 83// cleanup on exit.
 84func WithoutCatchPanics() ProgramOption {
 85	return func(p *Program) {
 86		p.startupOptions |= withoutCatchPanics
 87	}
 88}
 89
 90// WithoutSignals will ignore OS signals.
 91// This is mainly useful for testing.
 92func WithoutSignals() ProgramOption {
 93	return func(p *Program) {
 94		atomic.StoreUint32(&p.ignoreSignals, 1)
 95	}
 96}
 97
 98// WithAltScreen starts the program with the alternate screen buffer enabled
 99// (i.e. the program starts in full window mode). Note that the altscreen will
100// be automatically exited when the program quits.
101//
102// Example:
103//
104//	p := tea.NewProgram(Model{}, tea.WithAltScreen())
105//	if _, err := p.Run(); err != nil {
106//	    fmt.Println("Error running program:", err)
107//	    os.Exit(1)
108//	}
109//
110// To enter the altscreen once the program has already started running use the
111// EnterAltScreen command.
112func WithAltScreen() ProgramOption {
113	return func(p *Program) {
114		p.startupOptions |= withAltScreen
115	}
116}
117
118// WithoutBracketedPaste starts the program with bracketed paste disabled.
119func WithoutBracketedPaste() ProgramOption {
120	return func(p *Program) {
121		p.startupOptions |= withoutBracketedPaste
122	}
123}
124
125// WithMouseCellMotion starts the program with the mouse enabled in "cell
126// motion" mode.
127//
128// Cell motion mode enables mouse click, release, and wheel events. Mouse
129// movement events are also captured if a mouse button is pressed (i.e., drag
130// events). Cell motion mode is better supported than all motion mode.
131//
132// This will try to enable the mouse in extended mode (SGR), if that is not
133// supported by the terminal it will fall back to normal mode (X10).
134//
135// To enable mouse cell motion once the program has already started running use
136// the EnableMouseCellMotion command. To disable the mouse when the program is
137// running use the DisableMouse command.
138//
139// The mouse will be automatically disabled when the program exits.
140func WithMouseCellMotion() ProgramOption {
141	return func(p *Program) {
142		p.startupOptions |= withMouseCellMotion // set
143		p.startupOptions &^= withMouseAllMotion // clear
144	}
145}
146
147// WithMouseAllMotion starts the program with the mouse enabled in "all motion"
148// mode.
149//
150// EnableMouseAllMotion is a special command that enables mouse click, release,
151// wheel, and motion events, which are delivered regardless of whether a mouse
152// button is pressed, effectively enabling support for hover interactions.
153//
154// This will try to enable the mouse in extended mode (SGR), if that is not
155// supported by the terminal it will fall back to normal mode (X10).
156//
157// Many modern terminals support this, but not all. If in doubt, use
158// EnableMouseCellMotion instead.
159//
160// To enable the mouse once the program has already started running use the
161// EnableMouseAllMotion command. To disable the mouse when the program is
162// running use the DisableMouse command.
163//
164// The mouse will be automatically disabled when the program exits.
165func WithMouseAllMotion() ProgramOption {
166	return func(p *Program) {
167		p.startupOptions |= withMouseAllMotion   // set
168		p.startupOptions &^= withMouseCellMotion // clear
169	}
170}
171
172// WithFilter supplies an event filter that will be invoked before Bubble Tea
173// processes a tea.Msg. The event filter can return any tea.Msg which will then
174// get handled by Bubble Tea instead of the original event. If the event filter
175// returns nil, the event will be ignored and Bubble Tea will not process it.
176//
177// As an example, this could be used to prevent a program from shutting down if
178// there are unsaved changes.
179//
180// Example:
181//
182//	func filter(m tea.Model, msg tea.Msg) tea.Msg {
183//		if _, ok := msg.(tea.QuitMsg); !ok {
184//			return msg
185//		}
186//
187//		model := m.(myModel)
188//		if model.hasChanges {
189//			return nil
190//		}
191//
192//		return msg
193//	}
194//
195//	p := tea.NewProgram(Model{}, tea.WithFilter(filter));
196//
197//	if _,err := p.Run(); err != nil {
198//		fmt.Println("Error running program:", err)
199//		os.Exit(1)
200//	}
201func WithFilter(filter func(Model, Msg) Msg) ProgramOption {
202	return func(p *Program) {
203		p.filter = filter
204	}
205}
206
207// WithFPS sets a custom maximum FPS at which the renderer should run. If
208// less than 1, the default value of 60 will be used. If over 120, the FPS
209// will be capped at 120.
210func WithFPS(fps int) ProgramOption {
211	return func(p *Program) {
212		p.fps = fps
213	}
214}
215
216// WithReportFocus enables reporting when the terminal gains and loses
217// focus. When this is enabled [FocusMsg] and [BlurMsg] messages will be sent
218// to your Update method.
219//
220// Note that while most terminals and multiplexers support focus reporting,
221// some do not. Also note that tmux needs to be configured to report focus
222// events.
223func WithReportFocus() ProgramOption {
224	return func(p *Program) {
225		p.startupOptions |= withReportFocus
226	}
227}
228
229// WithKeyReleases enables support for reporting key release events. This is
230// useful for terminals that support the Kitty keyboard protocol "Report event
231// types" progressive enhancement feature.
232//
233// Note that not all terminals support this feature. If the terminal does not
234// support this feature, the program will not receive key release events.
235func WithKeyReleases() ProgramOption {
236	return func(p *Program) {
237		p.requestedEnhancements.kittyFlags |= ansi.KittyReportEventTypes
238		p.requestedEnhancements.keyReleases = true
239	}
240}
241
242// WithUniformKeyLayout enables support for reporting key events as though they
243// were on a PC-101 layout. This is useful for uniform key event reporting
244// across different keyboard layouts. This is equivalent to the Kitty keyboard
245// protocol "Report alternate keys" and "Report all keys as escape codes"
246// progressive enhancement features.
247//
248// Note that not all terminals support this feature. If the terminal does not
249// support this feature, the program will not receive key events in
250// uniform layout format.
251func WithUniformKeyLayout() ProgramOption {
252	return func(p *Program) {
253		p.requestedEnhancements.kittyFlags |= ansi.KittyReportAlternateKeys | ansi.KittyReportAllKeysAsEscapeCodes
254	}
255}
256
257// WithGraphemeClustering disables grapheme clustering. This is useful if you
258// want to disable grapheme clustering for your program.
259//
260// Grapheme clustering is a character width calculation method that accurately
261// calculates the width of wide characters in a terminal. This is useful for
262// properly rendering double width characters such as emojis and CJK
263// characters.
264//
265// See https://mitchellh.com/writing/grapheme-clusters-in-terminals
266func WithGraphemeClustering() ProgramOption {
267	return func(p *Program) {
268		p.startupOptions |= withGraphemeClustering
269	}
270}
271
272// WithColorProfile sets the color profile that the program will use. This is
273// useful when you want to force a specific color profile. By default, Bubble
274// Tea will try to detect the terminal's color profile from environment
275// variables and terminfo capabilities. Use [tea.WithEnvironment] to set custom
276// environment variables.
277func WithColorProfile(profile colorprofile.Profile) ProgramOption {
278	return func(p *Program) {
279		p.startupOptions |= withColorProfile
280		p.profile = profile
281	}
282}
283
284// WithWindowSize sets the initial size of the terminal window. This is useful
285// when you need to set the initial size of the terminal window, for example
286// during testing or when you want to run your program in a non-interactive
287// environment.
288func WithWindowSize(width, height int) ProgramOption {
289	return func(p *Program) {
290		p.width = width
291		p.height = height
292	}
293}
294
295// WithoutKeyEnhancements disables all key enhancements. This is useful if you
296// want to disable all key enhancements for your program and keep your program
297// legacy compatible with older terminals.
298func WithoutKeyEnhancements() ProgramOption {
299	return func(p *Program) {
300		p.startupOptions |= withoutKeyEnhancements
301	}
302}