Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Model v2, program context #1010

Open
aymanbagabas opened this issue May 9, 2024 · 0 comments
Open

Proposal: Model v2, program context #1010

aymanbagabas opened this issue May 9, 2024 · 0 comments
Labels
proposal Introducing an API change

Comments

@aymanbagabas
Copy link
Member

aymanbagabas commented May 9, 2024

During the execution of the program, models need to access the underlying terminal features and capabilities. We can achieved this with an API change that introduces Bubble Tea Contexts. The nice thing about this Context type is that it can be used to control executions of goroutines because it embeds context.Context. Using tea.WithContext(ctx), users can access the provided context before running the program in their Bubble Tea model.

Bubble Tea Model Context

This fixes all sorts of issues with input and color, particularly in Wish. Basically, apps built on Bubble Tea v2 will “just work” if put behind a Wish server.

This also means that Bubble Tea now has first-class Lip Gloss support. Now, with the new advanced input handler, Bubble Tea can read and parse any type of sequence events as the terminal input buffer receive them.
This means that important events that can change the look and behavor of our program can be detected in Bubble Tea. When the program starts, it will query the terminal for Kitty Keyboard support and
the current terminal's background color. The background color is important because it affects Lip Gloss styles.

You can now use the new tea.Context to read different terminal capabilities. Bubble Tea now does all the heavy lifting of detecting terminal colors and background and can create ctx.NewStyle() that are
specific to the current running program (whether local or in a remote session). This will also pave the road for future improvements to be added on the context.

The new interface will look like this:

// Model contains the program's state as well as its core functions.
type Model interface {
	// Init is the first function that will be called. It returns an optional
	// initial command. To not perform an initial command return nil.
	Init(ctx Context) (Model, Cmd)

	// Update is called when a message is received. Use it to inspect messages
	// and, in response, update the model and/or send a command.
	Update(ctx Context, msg Msg) (Model, Cmd)

	// View renders the program's UI, which is just a string. The view is
	// rendered after every Update.
	View(ctx Context) string
}

// Context represents a Bubble Tea program's context. It is passed to the
// program's Init, Update, and View functions to provide information about the
// program's state and to allow them to interact with the terminal.
type Context interface {
	context.Context

	// BackgroundColor returns the current background color of the terminal.
	// It returns nil if the terminal's doesn't support querying the background
	// color.
	BackgroundColor() color.Color

	// HasLightBackground returns true if the terminal's background color is
	// light. This is useful for determining whether to use light or dark colors
	// in the program's UI.
	HasLightBackground() bool

	// SupportsEnhancedKeyboard reports whether the terminal supports enhanced
	// keyboard keys. On Windows, this means it supports virtual keys like and
	// the Windows Console API. On Unix, this means it supports the Kitty
	// Keyboard Protocol.
	SupportsEnhancedKeyboard() bool

	// NewStyle returns a new Lip Gloss style that is suitable for the program's
	// environment.
	NewStyle() lipgloss.Style

	// ColorProfile returns the terminal's color profile.
	ColorProfile() lipgloss.Profile
	
	// what else?
}

How to upgrade

//// Before

// Uses globals, bad 👎
var myStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#4CFFCC"))

type myModel struct{}

// Init does nothing!
func (m myModel) Init() tea.Cmd {
    return nil
}

// Update
func (m myModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg.(type) {
    case tea.KeyMsg:
      return m, tea.Printf("You pressed %s", msg)
    }
    return m, nil
}

// View
func (m myModel) View() string {
    return myStyle.Render("Hello there!")
}
//// After

// Model now takes a tea.Context argument
type myModel struct{
  style lipgloss.Style
}

// Init now also returns both the model and a cmd
func (m myModel) Init(ctx tea.Context) (tea.Model, tea.Cmd) {
    m.style = ctx.NewStyle().Foreground(lipgloss.Color("#4CFFCC"))
    return m, nil
}

// Update now takes a ctx and a msg
func (m myModel) Update(ctx tea.Context, msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg.(type) {
    case tea.KeyMsg:
      return m, tea.Printf("You pressed %s", msg)
    }
    return m, nil
}

// View now takes a ctx
func (m myModel) View(ctx tea.Context) string {
    return m.style.Render("Hello there!")
}

You can find a WIP version of this proposal here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal Introducing an API change
Projects
None yet
Development

No branches or pull requests

1 participant