options.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package tea
  2. import (
  3. "context"
  4. "io"
  5. "sync/atomic"
  6. )
  7. // ProgramOption is used to set options when initializing a Program. Program can
  8. // accept a variable number of options.
  9. //
  10. // Example usage:
  11. //
  12. // p := NewProgram(model, WithInput(someInput), WithOutput(someOutput))
  13. type ProgramOption func(*Program)
  14. // WithContext lets you specify a context in which to run the Program. This is
  15. // useful if you want to cancel the execution from outside. When a Program gets
  16. // cancelled it will exit with an error ErrProgramKilled.
  17. func WithContext(ctx context.Context) ProgramOption {
  18. return func(p *Program) {
  19. p.ctx = ctx
  20. }
  21. }
  22. // WithOutput sets the output which, by default, is stdout. In most cases you
  23. // won't need to use this.
  24. func WithOutput(output io.Writer) ProgramOption {
  25. return func(p *Program) {
  26. p.output = output
  27. }
  28. }
  29. // WithInput sets the input which, by default, is stdin. In most cases you
  30. // won't need to use this. To disable input entirely pass nil.
  31. //
  32. // p := NewProgram(model, WithInput(nil))
  33. func WithInput(input io.Reader) ProgramOption {
  34. return func(p *Program) {
  35. p.input = input
  36. p.inputType = customInput
  37. }
  38. }
  39. // WithInputTTY opens a new TTY for input (or console input device on Windows).
  40. func WithInputTTY() ProgramOption {
  41. return func(p *Program) {
  42. p.inputType = ttyInput
  43. }
  44. }
  45. // WithEnvironment sets the environment variables that the program will use.
  46. // This useful when the program is running in a remote session (e.g. SSH) and
  47. // you want to pass the environment variables from the remote session to the
  48. // program.
  49. //
  50. // Example:
  51. //
  52. // var sess ssh.Session // ssh.Session is a type from the github.com/charmbracelet/ssh package
  53. // pty, _, _ := sess.Pty()
  54. // environ := append(sess.Environ(), "TERM="+pty.Term)
  55. // p := tea.NewProgram(model, tea.WithEnvironment(environ)
  56. func WithEnvironment(env []string) ProgramOption {
  57. return func(p *Program) {
  58. p.environ = env
  59. }
  60. }
  61. // WithoutSignalHandler disables the signal handler that Bubble Tea sets up for
  62. // Programs. This is useful if you want to handle signals yourself.
  63. func WithoutSignalHandler() ProgramOption {
  64. return func(p *Program) {
  65. p.startupOptions |= withoutSignalHandler
  66. }
  67. }
  68. // WithoutCatchPanics disables the panic catching that Bubble Tea does by
  69. // default. If panic catching is disabled the terminal will be in a fairly
  70. // unusable state after a panic because Bubble Tea will not perform its usual
  71. // cleanup on exit.
  72. func WithoutCatchPanics() ProgramOption {
  73. return func(p *Program) {
  74. p.startupOptions |= withoutCatchPanics
  75. }
  76. }
  77. // WithoutSignals will ignore OS signals.
  78. // This is mainly useful for testing.
  79. func WithoutSignals() ProgramOption {
  80. return func(p *Program) {
  81. atomic.StoreUint32(&p.ignoreSignals, 1)
  82. }
  83. }
  84. // WithAltScreen starts the program with the alternate screen buffer enabled
  85. // (i.e. the program starts in full window mode). Note that the altscreen will
  86. // be automatically exited when the program quits.
  87. //
  88. // Example:
  89. //
  90. // p := tea.NewProgram(Model{}, tea.WithAltScreen())
  91. // if _, err := p.Run(); err != nil {
  92. // fmt.Println("Error running program:", err)
  93. // os.Exit(1)
  94. // }
  95. //
  96. // To enter the altscreen once the program has already started running use the
  97. // EnterAltScreen command.
  98. func WithAltScreen() ProgramOption {
  99. return func(p *Program) {
  100. p.startupOptions |= withAltScreen
  101. }
  102. }
  103. // WithoutBracketedPaste starts the program with bracketed paste disabled.
  104. func WithoutBracketedPaste() ProgramOption {
  105. return func(p *Program) {
  106. p.startupOptions |= withoutBracketedPaste
  107. }
  108. }
  109. // WithMouseCellMotion starts the program with the mouse enabled in "cell
  110. // motion" mode.
  111. //
  112. // Cell motion mode enables mouse click, release, and wheel events. Mouse
  113. // movement events are also captured if a mouse button is pressed (i.e., drag
  114. // events). Cell motion mode is better supported than all motion mode.
  115. //
  116. // This will try to enable the mouse in extended mode (SGR), if that is not
  117. // supported by the terminal it will fall back to normal mode (X10).
  118. //
  119. // To enable mouse cell motion once the program has already started running use
  120. // the EnableMouseCellMotion command. To disable the mouse when the program is
  121. // running use the DisableMouse command.
  122. //
  123. // The mouse will be automatically disabled when the program exits.
  124. func WithMouseCellMotion() ProgramOption {
  125. return func(p *Program) {
  126. p.startupOptions |= withMouseCellMotion // set
  127. p.startupOptions &^= withMouseAllMotion // clear
  128. }
  129. }
  130. // WithMouseAllMotion starts the program with the mouse enabled in "all motion"
  131. // mode.
  132. //
  133. // EnableMouseAllMotion is a special command that enables mouse click, release,
  134. // wheel, and motion events, which are delivered regardless of whether a mouse
  135. // button is pressed, effectively enabling support for hover interactions.
  136. //
  137. // This will try to enable the mouse in extended mode (SGR), if that is not
  138. // supported by the terminal it will fall back to normal mode (X10).
  139. //
  140. // Many modern terminals support this, but not all. If in doubt, use
  141. // EnableMouseCellMotion instead.
  142. //
  143. // To enable the mouse once the program has already started running use the
  144. // EnableMouseAllMotion command. To disable the mouse when the program is
  145. // running use the DisableMouse command.
  146. //
  147. // The mouse will be automatically disabled when the program exits.
  148. func WithMouseAllMotion() ProgramOption {
  149. return func(p *Program) {
  150. p.startupOptions |= withMouseAllMotion // set
  151. p.startupOptions &^= withMouseCellMotion // clear
  152. }
  153. }
  154. // WithoutRenderer disables the renderer. When this is set output and log
  155. // statements will be plainly sent to stdout (or another output if one is set)
  156. // without any rendering and redrawing logic. In other words, printing and
  157. // logging will behave the same way it would in a non-TUI commandline tool.
  158. // This can be useful if you want to use the Bubble Tea framework for a non-TUI
  159. // application, or to provide an additional non-TUI mode to your Bubble Tea
  160. // programs. For example, your program could behave like a daemon if output is
  161. // not a TTY.
  162. func WithoutRenderer() ProgramOption {
  163. return func(p *Program) {
  164. p.renderer = &nilRenderer{}
  165. }
  166. }
  167. // WithANSICompressor removes redundant ANSI sequences to produce potentially
  168. // smaller output, at the cost of some processing overhead.
  169. //
  170. // This feature is provisional, and may be changed or removed in a future version
  171. // of this package.
  172. //
  173. // Deprecated: this incurs a noticeable performance hit. A future release will
  174. // optimize ANSI automatically without the performance penalty.
  175. func WithANSICompressor() ProgramOption {
  176. return func(p *Program) {
  177. p.startupOptions |= withANSICompressor
  178. }
  179. }
  180. // WithFilter supplies an event filter that will be invoked before Bubble Tea
  181. // processes a tea.Msg. The event filter can return any tea.Msg which will then
  182. // get handled by Bubble Tea instead of the original event. If the event filter
  183. // returns nil, the event will be ignored and Bubble Tea will not process it.
  184. //
  185. // As an example, this could be used to prevent a program from shutting down if
  186. // there are unsaved changes.
  187. //
  188. // Example:
  189. //
  190. // func filter(m tea.Model, msg tea.Msg) tea.Msg {
  191. // if _, ok := msg.(tea.QuitMsg); !ok {
  192. // return msg
  193. // }
  194. //
  195. // model := m.(myModel)
  196. // if model.hasChanges {
  197. // return nil
  198. // }
  199. //
  200. // return msg
  201. // }
  202. //
  203. // p := tea.NewProgram(Model{}, tea.WithFilter(filter));
  204. //
  205. // if _,err := p.Run(); err != nil {
  206. // fmt.Println("Error running program:", err)
  207. // os.Exit(1)
  208. // }
  209. func WithFilter(filter func(Model, Msg) Msg) ProgramOption {
  210. return func(p *Program) {
  211. p.filter = filter
  212. }
  213. }
  214. // WithFPS sets a custom maximum FPS at which the renderer should run. If
  215. // less than 1, the default value of 60 will be used. If over 120, the FPS
  216. // will be capped at 120.
  217. func WithFPS(fps int) ProgramOption {
  218. return func(p *Program) {
  219. p.fps = fps
  220. }
  221. }
  222. // WithReportFocus enables reporting when the terminal gains and loses
  223. // focus. When this is enabled [FocusMsg] and [BlurMsg] messages will be sent
  224. // to your Update method.
  225. //
  226. // Note that while most terminals and multiplexers support focus reporting,
  227. // some do not. Also note that tmux needs to be configured to report focus
  228. // events.
  229. func WithReportFocus() ProgramOption {
  230. return func(p *Program) {
  231. p.startupOptions |= withReportFocus
  232. }
  233. }