bubbletea

package
v0.0.0-...-ccefcf7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 4, 2026 License: GPL-3.0 Imports: 18 Imported by: 0

Documentation

Overview

Package bubbletea provides JavaScript bindings for github.com/charmbracelet/bubbletea.

The module is exposed as "osm:bubbletea" and provides TUI program capabilities. All functionality is exposed to JavaScript, following the established pattern of no global state.

JavaScript API

const tea = require('osm:bubbletea');

// Create a program with a model
const model = tea.newModel({
	init: function() {
		return { count: 0 };
	},
	update: function(msg, model) {
		switch (msg.type) {
			case 'Key':
				if (msg.key === 'q') return [model, tea.quit()];
				if (msg.key === 'up') return [{ count: model.count + 1 }, null];
				if (msg.key === 'down') return [{ count: model.count - 1 }, null];
				break;
			case 'Focus':
				// Terminal gained focus
				break;
			case 'Blur':
				// Terminal lost focus
				break;
		}
		return [model, null];
	},
	view: function(model) {
		return 'Count: ' + model.count + '\nPress q to quit';
	}
});

// Run the program
tea.run(model);

// Commands - all return opaque command objects
tea.quit();                    // Quit the program
tea.clearScreen();             // Clear the screen
tea.batch(...cmds);            // Batch multiple commands
tea.sequence(...cmds);         // Execute commands in sequence
tea.tick(durationMs, id);      // Timer command (returns tickMsg with id)
tea.setWindowTitle(title);     // Set terminal window title
tea.hideCursor();              // Hide the cursor
tea.showCursor();              // Show the cursor
tea.enterAltScreen();          // Enter alternate screen buffer
tea.exitAltScreen();           // Exit alternate screen buffer
tea.enableBracketedPaste();    // Enable bracketed paste mode
tea.disableBracketedPaste();   // Disable bracketed paste mode
tea.enableReportFocus();       // Enable focus/blur reporting
tea.disableReportFocus();      // Disable focus/blur reporting
tea.windowSize();              // Query current window size

// Key events
// msg.type === 'Key'
// msg.key - the key name ('q', 'enter', 'up', 'down', etc.)
// msg.runes - array of runes (for unicode/IME input)
// msg.alt - alt modifier
// msg.ctrl - ctrl modifier
// msg.paste - true if this is part of a bracketed paste

// Mouse events (when enabled)
// msg.type === 'Mouse'
// msg.x, msg.y - coordinates
// msg.button - button name
// msg.action - 'press', 'release', 'motion'
// msg.alt, msg.ctrl, msg.shift - modifiers

// Window size events
// msg.type === 'WindowSize'
// msg.width, msg.height - terminal dimensions

// Focus events (when reportFocus enabled)
// msg.type === 'Focus'  - terminal gained focus
// msg.type === 'Blur'   - terminal lost focus

// Tick events (from tick command)
// msg.type === 'Tick'
// msg.id - the id passed to tick()
// msg.time - timestamp in milliseconds

// Program options
tea.run(model, {
    altScreen: true,       // Use alternate screen buffer
    mouse: true,           // Enable mouse support (all motion)
    mouseCellMotion: true, // Enable mouse cell motion only
    bracketedPaste: true,  // Enable bracketed paste
    reportFocus: true,     // Enable focus/blur reporting
});

Error Handling

All command functions validate their inputs and return error objects when invalid arguments are provided:

const cmd = tea.tick(-100, 'timer'); // Returns { error: 'BT001: duration must be positive' }

Error codes:

  • BT001: Invalid duration (tick command)
  • BT004: Program execution failed
  • BT005: Invalid model object
  • BT006: Invalid arguments
  • BT007: Panic during program execution

Implementation Notes

All additions follow these patterns:

  1. No global state - all state managed per Manager instance
  2. JavaScript callbacks properly synchronized with Go goroutines
  3. All functionality exposed via Require() function pattern
  4. Commands are opaque objects - JS cannot forge invalid commands
  5. Comprehensive unit tests using simulation screens
  6. Deterministic testing - no timing-dependent tests
  7. Proper TTY detection with fallback for non-TTY environments
  8. Terminal state cleanup guaranteed even on panic/force-quit

Index

Constants

View Source
const (
	ErrCodeInvalidDuration = "BT001" // Invalid duration (tick command)
	ErrCodeProgramFailed   = "BT004" // Program execution failed
	ErrCodeInvalidModel    = "BT005" // Invalid model object
	ErrCodeInvalidArgs     = "BT006" // Invalid arguments
	ErrCodePanic           = "BT007" // Panic during program execution
)

Error codes for bubbletea operations.

Variables

AllKeyTypes returns all known tea.KeyType values that have string representations. Uses canonical constant names for deterministic output.

AllMouseActions returns all known tea.MouseAction values that have string representations.

AllMouseButtons returns all known tea.MouseButton values that have string representations.

View Source
var KeyDefs = map[string]KeyDef{
	" ":                {Name: "KeySpace", String: " ", Type: tea.KeySpace},
	"backspace":        {Name: "KeyBackspace", String: "backspace", Type: tea.KeyBackspace},
	"ctrl+@":           {Name: "KeyCtrlAt", String: "ctrl+@", Type: tea.KeyCtrlAt},
	"ctrl+\\":          {Name: "KeyCtrlBackslash", String: "ctrl+\\", Type: tea.KeyCtrlBackslash},
	"ctrl+]":           {Name: "KeyCtrlCloseBracket", String: "ctrl+]", Type: tea.KeyCtrlCloseBracket},
	"ctrl+^":           {Name: "KeyCtrlCaret", String: "ctrl+^", Type: tea.KeyCtrlCaret},
	"ctrl+_":           {Name: "KeyCtrlUnderscore", String: "ctrl+_", Type: tea.KeyCtrlUnderscore},
	"ctrl+a":           {Name: "KeyCtrlA", String: "ctrl+a", Type: tea.KeyCtrlA},
	"ctrl+b":           {Name: "KeyCtrlB", String: "ctrl+b", Type: tea.KeyCtrlB},
	"ctrl+c":           {Name: "KeyBreak", String: "ctrl+c", Type: tea.KeyBreak},
	"ctrl+d":           {Name: "KeyCtrlD", String: "ctrl+d", Type: tea.KeyCtrlD},
	"ctrl+down":        {Name: "KeyCtrlDown", String: "ctrl+down", Type: tea.KeyCtrlDown},
	"ctrl+e":           {Name: "KeyCtrlE", String: "ctrl+e", Type: tea.KeyCtrlE},
	"ctrl+end":         {Name: "KeyCtrlEnd", String: "ctrl+end", Type: tea.KeyCtrlEnd},
	"ctrl+f":           {Name: "KeyCtrlF", String: "ctrl+f", Type: tea.KeyCtrlF},
	"ctrl+g":           {Name: "KeyCtrlG", String: "ctrl+g", Type: tea.KeyCtrlG},
	"ctrl+h":           {Name: "KeyCtrlH", String: "ctrl+h", Type: tea.KeyCtrlH},
	"ctrl+home":        {Name: "KeyCtrlHome", String: "ctrl+home", Type: tea.KeyCtrlHome},
	"ctrl+j":           {Name: "KeyCtrlJ", String: "ctrl+j", Type: tea.KeyCtrlJ},
	"ctrl+k":           {Name: "KeyCtrlK", String: "ctrl+k", Type: tea.KeyCtrlK},
	"ctrl+l":           {Name: "KeyCtrlL", String: "ctrl+l", Type: tea.KeyCtrlL},
	"ctrl+left":        {Name: "KeyCtrlLeft", String: "ctrl+left", Type: tea.KeyCtrlLeft},
	"ctrl+n":           {Name: "KeyCtrlN", String: "ctrl+n", Type: tea.KeyCtrlN},
	"ctrl+o":           {Name: "KeyCtrlO", String: "ctrl+o", Type: tea.KeyCtrlO},
	"ctrl+p":           {Name: "KeyCtrlP", String: "ctrl+p", Type: tea.KeyCtrlP},
	"ctrl+pgdown":      {Name: "KeyCtrlPgDown", String: "ctrl+pgdown", Type: tea.KeyCtrlPgDown},
	"ctrl+pgup":        {Name: "KeyCtrlPgUp", String: "ctrl+pgup", Type: tea.KeyCtrlPgUp},
	"ctrl+q":           {Name: "KeyCtrlQ", String: "ctrl+q", Type: tea.KeyCtrlQ},
	"ctrl+r":           {Name: "KeyCtrlR", String: "ctrl+r", Type: tea.KeyCtrlR},
	"ctrl+right":       {Name: "KeyCtrlRight", String: "ctrl+right", Type: tea.KeyCtrlRight},
	"ctrl+s":           {Name: "KeyCtrlS", String: "ctrl+s", Type: tea.KeyCtrlS},
	"ctrl+shift+down":  {Name: "KeyCtrlShiftDown", String: "ctrl+shift+down", Type: tea.KeyCtrlShiftDown},
	"ctrl+shift+end":   {Name: "KeyCtrlShiftEnd", String: "ctrl+shift+end", Type: tea.KeyCtrlShiftEnd},
	"ctrl+shift+home":  {Name: "KeyCtrlShiftHome", String: "ctrl+shift+home", Type: tea.KeyCtrlShiftHome},
	"ctrl+shift+left":  {Name: "KeyCtrlShiftLeft", String: "ctrl+shift+left", Type: tea.KeyCtrlShiftLeft},
	"ctrl+shift+right": {Name: "KeyCtrlShiftRight", String: "ctrl+shift+right", Type: tea.KeyCtrlShiftRight},
	"ctrl+shift+up":    {Name: "KeyCtrlShiftUp", String: "ctrl+shift+up", Type: tea.KeyCtrlShiftUp},
	"ctrl+t":           {Name: "KeyCtrlT", String: "ctrl+t", Type: tea.KeyCtrlT},
	"ctrl+u":           {Name: "KeyCtrlU", String: "ctrl+u", Type: tea.KeyCtrlU},
	"ctrl+up":          {Name: "KeyCtrlUp", String: "ctrl+up", Type: tea.KeyCtrlUp},
	"ctrl+v":           {Name: "KeyCtrlV", String: "ctrl+v", Type: tea.KeyCtrlV},
	"ctrl+w":           {Name: "KeyCtrlW", String: "ctrl+w", Type: tea.KeyCtrlW},
	"ctrl+x":           {Name: "KeyCtrlX", String: "ctrl+x", Type: tea.KeyCtrlX},
	"ctrl+y":           {Name: "KeyCtrlY", String: "ctrl+y", Type: tea.KeyCtrlY},
	"ctrl+z":           {Name: "KeyCtrlZ", String: "ctrl+z", Type: tea.KeyCtrlZ},
	"delete":           {Name: "KeyDelete", String: "delete", Type: tea.KeyDelete},
	"down":             {Name: "KeyDown", String: "down", Type: tea.KeyDown},
	"end":              {Name: "KeyEnd", String: "end", Type: tea.KeyEnd},
	"enter":            {Name: "KeyEnter", String: "enter", Type: tea.KeyEnter},
	"esc":              {Name: "KeyEsc", String: "esc", Type: tea.KeyEsc},
	"f1":               {Name: "KeyF1", String: "f1", Type: tea.KeyF1},
	"f10":              {Name: "KeyF10", String: "f10", Type: tea.KeyF10},
	"f11":              {Name: "KeyF11", String: "f11", Type: tea.KeyF11},
	"f12":              {Name: "KeyF12", String: "f12", Type: tea.KeyF12},
	"f13":              {Name: "KeyF13", String: "f13", Type: tea.KeyF13},
	"f14":              {Name: "KeyF14", String: "f14", Type: tea.KeyF14},
	"f15":              {Name: "KeyF15", String: "f15", Type: tea.KeyF15},
	"f16":              {Name: "KeyF16", String: "f16", Type: tea.KeyF16},
	"f17":              {Name: "KeyF17", String: "f17", Type: tea.KeyF17},
	"f18":              {Name: "KeyF18", String: "f18", Type: tea.KeyF18},
	"f19":              {Name: "KeyF19", String: "f19", Type: tea.KeyF19},
	"f2":               {Name: "KeyF2", String: "f2", Type: tea.KeyF2},
	"f20":              {Name: "KeyF20", String: "f20", Type: tea.KeyF20},
	"f3":               {Name: "KeyF3", String: "f3", Type: tea.KeyF3},
	"f4":               {Name: "KeyF4", String: "f4", Type: tea.KeyF4},
	"f5":               {Name: "KeyF5", String: "f5", Type: tea.KeyF5},
	"f6":               {Name: "KeyF6", String: "f6", Type: tea.KeyF6},
	"f7":               {Name: "KeyF7", String: "f7", Type: tea.KeyF7},
	"f8":               {Name: "KeyF8", String: "f8", Type: tea.KeyF8},
	"f9":               {Name: "KeyF9", String: "f9", Type: tea.KeyF9},
	"home":             {Name: "KeyHome", String: "home", Type: tea.KeyHome},
	"insert":           {Name: "KeyInsert", String: "insert", Type: tea.KeyInsert},
	"left":             {Name: "KeyLeft", String: "left", Type: tea.KeyLeft},
	"pgdown":           {Name: "KeyPgDown", String: "pgdown", Type: tea.KeyPgDown},
	"pgup":             {Name: "KeyPgUp", String: "pgup", Type: tea.KeyPgUp},
	"right":            {Name: "KeyRight", String: "right", Type: tea.KeyRight},
	"runes":            {Name: "KeyRunes", String: "runes", Type: tea.KeyRunes},
	"shift+down":       {Name: "KeyShiftDown", String: "shift+down", Type: tea.KeyShiftDown},
	"shift+end":        {Name: "KeyShiftEnd", String: "shift+end", Type: tea.KeyShiftEnd},
	"shift+home":       {Name: "KeyShiftHome", String: "shift+home", Type: tea.KeyShiftHome},
	"shift+left":       {Name: "KeyShiftLeft", String: "shift+left", Type: tea.KeyShiftLeft},
	"shift+right":      {Name: "KeyShiftRight", String: "shift+right", Type: tea.KeyShiftRight},
	"shift+tab":        {Name: "KeyShiftTab", String: "shift+tab", Type: tea.KeyShiftTab},
	"shift+up":         {Name: "KeyShiftUp", String: "shift+up", Type: tea.KeyShiftUp},
	"tab":              {Name: "KeyTab", String: "tab", Type: tea.KeyTab},
	"up":               {Name: "KeyUp", String: "up", Type: tea.KeyUp},
}

KeyDefs contains all bubbletea KeyType definitions. The map is keyed by the String() representation for JS lookup efficiency. When multiple KeyType constants produce the same string, a canonical name is chosen.

View Source
var KeyDefsByName = map[string]KeyDef{
	"KeyBackspace":        KeyDefs["backspace"],
	"KeyBreak":            KeyDefs["ctrl+c"],
	"KeyCtrlA":            KeyDefs["ctrl+a"],
	"KeyCtrlAt":           KeyDefs["ctrl+@"],
	"KeyCtrlB":            KeyDefs["ctrl+b"],
	"KeyCtrlBackslash":    KeyDefs["ctrl+\\"],
	"KeyCtrlC":            KeyDefs["ctrl+c"],
	"KeyCtrlCaret":        KeyDefs["ctrl+^"],
	"KeyCtrlCloseBracket": KeyDefs["ctrl+]"],
	"KeyCtrlD":            KeyDefs["ctrl+d"],
	"KeyCtrlDown":         KeyDefs["ctrl+down"],
	"KeyCtrlE":            KeyDefs["ctrl+e"],
	"KeyCtrlEnd":          KeyDefs["ctrl+end"],
	"KeyCtrlF":            KeyDefs["ctrl+f"],
	"KeyCtrlG":            KeyDefs["ctrl+g"],
	"KeyCtrlH":            KeyDefs["ctrl+h"],
	"KeyCtrlHome":         KeyDefs["ctrl+home"],
	"KeyCtrlI":            KeyDefs["tab"],
	"KeyCtrlJ":            KeyDefs["ctrl+j"],
	"KeyCtrlK":            KeyDefs["ctrl+k"],
	"KeyCtrlL":            KeyDefs["ctrl+l"],
	"KeyCtrlLeft":         KeyDefs["ctrl+left"],
	"KeyCtrlM":            KeyDefs["enter"],
	"KeyCtrlN":            KeyDefs["ctrl+n"],
	"KeyCtrlO":            KeyDefs["ctrl+o"],
	"KeyCtrlOpenBracket":  KeyDefs["esc"],
	"KeyCtrlP":            KeyDefs["ctrl+p"],
	"KeyCtrlPgDown":       KeyDefs["ctrl+pgdown"],
	"KeyCtrlPgUp":         KeyDefs["ctrl+pgup"],
	"KeyCtrlQ":            KeyDefs["ctrl+q"],
	"KeyCtrlQuestionMark": KeyDefs["backspace"],
	"KeyCtrlR":            KeyDefs["ctrl+r"],
	"KeyCtrlRight":        KeyDefs["ctrl+right"],
	"KeyCtrlS":            KeyDefs["ctrl+s"],
	"KeyCtrlShiftDown":    KeyDefs["ctrl+shift+down"],
	"KeyCtrlShiftEnd":     KeyDefs["ctrl+shift+end"],
	"KeyCtrlShiftHome":    KeyDefs["ctrl+shift+home"],
	"KeyCtrlShiftLeft":    KeyDefs["ctrl+shift+left"],
	"KeyCtrlShiftRight":   KeyDefs["ctrl+shift+right"],
	"KeyCtrlShiftUp":      KeyDefs["ctrl+shift+up"],
	"KeyCtrlT":            KeyDefs["ctrl+t"],
	"KeyCtrlU":            KeyDefs["ctrl+u"],
	"KeyCtrlUnderscore":   KeyDefs["ctrl+_"],
	"KeyCtrlUp":           KeyDefs["ctrl+up"],
	"KeyCtrlV":            KeyDefs["ctrl+v"],
	"KeyCtrlW":            KeyDefs["ctrl+w"],
	"KeyCtrlX":            KeyDefs["ctrl+x"],
	"KeyCtrlY":            KeyDefs["ctrl+y"],
	"KeyCtrlZ":            KeyDefs["ctrl+z"],
	"KeyDelete":           KeyDefs["delete"],
	"KeyDown":             KeyDefs["down"],
	"KeyEnd":              KeyDefs["end"],
	"KeyEnter":            KeyDefs["enter"],
	"KeyEsc":              KeyDefs["esc"],
	"KeyEscape":           KeyDefs["esc"],
	"KeyF1":               KeyDefs["f1"],
	"KeyF10":              KeyDefs["f10"],
	"KeyF11":              KeyDefs["f11"],
	"KeyF12":              KeyDefs["f12"],
	"KeyF13":              KeyDefs["f13"],
	"KeyF14":              KeyDefs["f14"],
	"KeyF15":              KeyDefs["f15"],
	"KeyF16":              KeyDefs["f16"],
	"KeyF17":              KeyDefs["f17"],
	"KeyF18":              KeyDefs["f18"],
	"KeyF19":              KeyDefs["f19"],
	"KeyF2":               KeyDefs["f2"],
	"KeyF20":              KeyDefs["f20"],
	"KeyF3":               KeyDefs["f3"],
	"KeyF4":               KeyDefs["f4"],
	"KeyF5":               KeyDefs["f5"],
	"KeyF6":               KeyDefs["f6"],
	"KeyF7":               KeyDefs["f7"],
	"KeyF8":               KeyDefs["f8"],
	"KeyF9":               KeyDefs["f9"],
	"KeyHome":             KeyDefs["home"],
	"KeyInsert":           KeyDefs["insert"],
	"KeyLeft":             KeyDefs["left"],
	"KeyNull":             KeyDefs["ctrl+@"],
	"KeyPgDown":           KeyDefs["pgdown"],
	"KeyPgUp":             KeyDefs["pgup"],
	"KeyRight":            KeyDefs["right"],
	"KeyRunes":            KeyDefs["runes"],
	"KeyShiftDown":        KeyDefs["shift+down"],
	"KeyShiftEnd":         KeyDefs["shift+end"],
	"KeyShiftHome":        KeyDefs["shift+home"],
	"KeyShiftLeft":        KeyDefs["shift+left"],
	"KeyShiftRight":       KeyDefs["shift+right"],
	"KeyShiftTab":         KeyDefs["shift+tab"],
	"KeyShiftUp":          KeyDefs["shift+up"],
	"KeySpace":            KeyDefs[" "],
	"KeyTab":              KeyDefs["tab"],
	"KeyUp":               KeyDefs["up"],
}

KeyDefsByName contains all bubbletea KeyType definitions keyed by constant name. Multiple constant names may map to the same key (e.g., KeyEsc and KeyEscape both map to "esc").

View Source
var MouseActionDefs = map[string]MouseActionDef{
	"motion":  {Name: "MouseActionMotion", String: "motion", Action: tea.MouseActionMotion},
	"press":   {Name: "MouseActionPress", String: "press", Action: tea.MouseActionPress},
	"release": {Name: "MouseActionRelease", String: "release", Action: tea.MouseActionRelease},
}

MouseActionDefs contains all bubbletea MouseAction definitions. The map is keyed by the String() representation for JS lookup efficiency.

View Source
var MouseActionDefsByName = map[string]MouseActionDef{
	"MouseActionMotion":  MouseActionDefs["motion"],
	"MouseActionPress":   MouseActionDefs["press"],
	"MouseActionRelease": MouseActionDefs["release"],
}

MouseActionDefsByName contains all bubbletea MouseAction definitions keyed by constant name.

View Source
var MouseButtonDefs = map[string]MouseButtonDef{
	"backward":    {Name: "MouseButtonBackward", String: "backward", Button: tea.MouseButtonBackward},
	"button 10":   {Name: "MouseButton10", String: "button 10", Button: tea.MouseButton10},
	"button 11":   {Name: "MouseButton11", String: "button 11", Button: tea.MouseButton11},
	"forward":     {Name: "MouseButtonForward", String: "forward", Button: tea.MouseButtonForward},
	"left":        {Name: "MouseButtonLeft", String: "left", Button: tea.MouseButtonLeft},
	"middle":      {Name: "MouseButtonMiddle", String: "middle", Button: tea.MouseButtonMiddle},
	"none":        {Name: "MouseButtonNone", String: "none", Button: tea.MouseButtonNone},
	"right":       {Name: "MouseButtonRight", String: "right", Button: tea.MouseButtonRight},
	"wheel down":  {Name: "MouseButtonWheelDown", String: "wheel down", Button: tea.MouseButtonWheelDown},
	"wheel left":  {Name: "MouseButtonWheelLeft", String: "wheel left", Button: tea.MouseButtonWheelLeft},
	"wheel right": {Name: "MouseButtonWheelRight", String: "wheel right", Button: tea.MouseButtonWheelRight},
	"wheel up":    {Name: "MouseButtonWheelUp", String: "wheel up", Button: tea.MouseButtonWheelUp},
}

MouseButtonDefs contains all bubbletea MouseButton definitions. The map is keyed by the String() representation for JS lookup efficiency.

View Source
var MouseButtonDefsByName = map[string]MouseButtonDef{
	"MouseButton10":         MouseButtonDefs["button 10"],
	"MouseButton11":         MouseButtonDefs["button 11"],
	"MouseButtonBackward":   MouseButtonDefs["backward"],
	"MouseButtonForward":    MouseButtonDefs["forward"],
	"MouseButtonLeft":       MouseButtonDefs["left"],
	"MouseButtonMiddle":     MouseButtonDefs["middle"],
	"MouseButtonNone":       MouseButtonDefs["none"],
	"MouseButtonRight":      MouseButtonDefs["right"],
	"MouseButtonWheelDown":  MouseButtonDefs["wheel down"],
	"MouseButtonWheelLeft":  MouseButtonDefs["wheel left"],
	"MouseButtonWheelRight": MouseButtonDefs["wheel right"],
	"MouseButtonWheelUp":    MouseButtonDefs["wheel up"],
}

MouseButtonDefsByName contains all bubbletea MouseButton definitions keyed by constant name.

Functions

func IsWheelButton

func IsWheelButton(b tea.MouseButton) bool

IsWheelButton returns true if the button is a wheel button. This mirrors tea.MouseEvent.IsWheel() for use in parsing/validation.

func JSToMouseEvent

func JSToMouseEvent(buttonStr, actionStr string, x, y int, alt, ctrl, shift bool) tea.MouseMsg

JSToMouseEvent converts a JavaScript mouse event object to a tea.MouseMsg. This is the inverse of MouseEventToJS.

func JsToTeaMsg

func JsToTeaMsg(runtime *goja.Runtime, obj *goja.Object) tea.Msg

JsToTeaMsg converts a JavaScript message object to a tea.Msg. This provides a standard way to decode events sent from JS components to Go models. It handles standard "Key", "Mouse", and "WindowSize" event types. Returns nil if the message cannot be converted (invalid object, unknown type, etc.)

func MouseEventToJS

func MouseEventToJS(msg tea.MouseMsg) map[string]interface{}

MouseEventToJS converts a tea.MouseEvent to a JavaScript-compatible map. This uses the generated MouseButtonDefs and MouseActionDefs for consistency with tea.MouseEvent.String().

func ParseKey

func ParseKey(s string) (tea.Key, bool)

ParseKey parses a string representation of a key event (as generated by Key.String()) back into a Key struct.

The boolean indicates whether the parsing was unambiguous.

It handles: 1. "alt+" modifiers 2. Bracketed paste format "[...]" 3. Named keys (e.g., "enter", "ctrl+c") 4. Raw runes (as a fallback) 5. All zero values (if input is empty string)

Case 4 returns false if the (non-alt) string is not a single printable character AND not a single rune. N.B. The special case " " (space) is therefore the sole single printable character key that doesn't return true.

func ParseMouseEvent

func ParseMouseEvent(s string) (tea.MouseEvent, bool)

ParseMouseEvent parses a string representation of a mouse event back into a MouseEvent.

The string format matches tea.MouseEvent.String(), which is:

  • [ctrl+][alt+][shift+]<button>[ <action>]

Where:

  • Modifiers (ctrl+, alt+, shift+) are optional prefixes
  • Button is required (e.g., "left", "right", "wheel up")
  • Action is optional for wheel buttons (they only press), required for regular buttons

Examples:

  • "left press" -> left button press
  • "right release" -> right button release
  • "wheel up" -> wheel up (action is always press for wheels)
  • "ctrl+alt+left press" -> left button press with ctrl and alt modifiers
  • "motion" -> motion event (button is none)
  • "release" -> release event (button is none)

The returned boolean indicates whether the parsing was successful.

func Require

func Require(baseCtx context.Context, manager *Manager) func(runtime *goja.Runtime, module *goja.Object)

Require returns a CommonJS native module under "osm:bubbletea". It exposes bubbletea functionality for building terminal UIs.

func WrapCmd

func WrapCmd(runtime *goja.Runtime, cmd tea.Cmd) goja.Value

WrapCmd wraps a tea.Cmd as an opaque JavaScript value. JavaScript receives the Go function wrapped via runtime.ToValue(). When JavaScript passes this value back, Go can retrieve the original tea.Cmd using Export(). NO REGISTRY NEEDED - goja handles this natively.

If cmd is nil, returns goja.Null().

Usage:

// In viewport/textarea update():
newModel, cmd := vp.Update(msg)
// update the underlying model instance via pointer.
*vp = newModel
// return [<model object>, <wrapped cmd>]
return runtime.NewArray(
	// Return the same JS object, which wraps the model instance / hides the Go state.
	obj,
	// Wrap the tea.Cmd as an opaque value - JS can pass this back and Go
	// can retrieve the original function via Export().
	bubbletea.WrapCmd(runtime, cmd),
)

// In valueToCmd():
if exported := val.Export(); exported != nil {
    if cmd, ok := exported.(tea.Cmd); ok {
        return cmd
    }
}

Types

type InputValidationResult

type InputValidationResult struct {
	// Valid indicates whether the input should be accepted.
	Valid bool
	// Reason provides a human-readable explanation (for debugging).
	Reason string
}

InputValidationResult contains the result of input validation.

func ValidateLabelInput

func ValidateLabelInput(keyStr string, isPaste bool) InputValidationResult

ValidateLabelInput determines if a key event should be accepted for a label field. More restrictive than textarea: only single printable characters and backspace.

Parameters:

  • keyStr: The key string representation (from tea.Key.String())
  • isPaste: Whether this is a bracketed paste event

Paste events pass through for labels too.

func ValidateTextareaInput

func ValidateTextareaInput(keyStr string, isPaste bool) InputValidationResult

ValidateTextareaInput determines if a key event should be forwarded to a textarea.

This uses a WHITELIST approach: only explicitly allowed inputs pass through. This prevents garbage (fragmented escape sequences from rapid mouse/scroll events) from corrupting document content.

Parameters:

  • keyStr: The key string representation (from tea.Key.String())
  • isPaste: Whether this is a bracketed paste event (msg.paste === true)

Paste events always pass through - they are pre-validated by the terminal's bracketed paste mode and should flow without interference.

Valid inputs:

  • Single printable ASCII characters (0x20-0x7E)
  • Single Unicode characters (non-control)
  • Recognized control/navigation keys from KeyDefs
  • Any paste event (isPaste == true)

Invalid inputs (REJECTED):

  • Empty strings
  • Multi-character strings that aren't recognized named keys
  • Control characters (0x00-0x1F, 0x7F) except via named keys

type KeyDef

type KeyDef struct {
	// Name is the Go constant name (e.g., "KeyEnter").
	Name string
	// String is the string representation from Key.String() (e.g., "enter").
	String string
	// Type is the actual tea.KeyType value.
	Type tea.KeyType
}

KeyDef represents metadata about a bubbletea key type.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager holds bubbletea-related state per engine instance.

func NewManager

func NewManager(
	ctx context.Context,
	input io.Reader,
	output io.Writer,
	signalNotify func(c chan<- os.Signal, sig ...os.Signal),
	signalStop func(c chan<- os.Signal),
) *Manager

NewManager creates a new bubbletea manager for an engine instance. Input and output can be nil to use os.Stdin and os.Stdout. Automatically detects TTY and sets up proper terminal handling.

func NewManagerWithStderr

func NewManagerWithStderr(
	ctx context.Context,
	input io.Reader,
	output io.Writer,
	stderr io.Writer,
	signalNotify func(c chan<- os.Signal, sig ...os.Signal),
	signalStop func(c chan<- os.Signal),
) *Manager

NewManagerWithStderr creates a new bubbletea manager with explicit stderr for error logging. Input, output, and stderr can be nil to use os.Stdin, os.Stdout, and os.Stderr. Automatically detects TTY and sets up proper terminal handling.

func (*Manager) IsTTY

func (m *Manager) IsTTY() bool

IsTTY returns whether the manager has access to a TTY.

func (*Manager) SendStateRefresh

func (m *Manager) SendStateRefresh(key string)

SendStateRefresh sends a state refresh message to the currently running program. This is safe to call from any goroutine. If no program is running, it's a no-op. The key parameter indicates which state key changed (for debugging/logging).

type MouseActionDef

type MouseActionDef struct {
	// Name is the Go constant name (e.g., "MouseActionPress").
	Name string
	// String is the string representation (e.g., "press").
	String string
	// Action is the actual tea.MouseAction value.
	Action tea.MouseAction
}

MouseActionDef represents metadata about a bubbletea MouseAction.

type MouseButtonDef

type MouseButtonDef struct {
	// Name is the Go constant name (e.g., "MouseButtonLeft").
	Name string
	// String is the string representation (e.g., "left").
	String string
	// Button is the actual tea.MouseButton value.
	Button tea.MouseButton
}

MouseButtonDef represents metadata about a bubbletea MouseButton.

type TerminalChecker

type TerminalChecker interface {
	// Fd returns the file descriptor of the underlying terminal.
	// Returns ^uintptr(0) (invalid) if the underlying resource doesn't have an Fd.
	Fd() uintptr

	// IsTerminal returns true if the underlying resource is a terminal.
	IsTerminal() bool
}

TerminalChecker provides terminal detection and file descriptor access. This interface allows dependency injection for testing and proper integration with terminal management wrappers like TUIReader/TUIWriter.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL