Skip to content

Commit

Permalink
feat!: tea contexts and query terminal capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed May 10, 2024
1 parent 656565f commit a434f25
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 33 deletions.
67 changes: 67 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package tea

import (
"context"
"image/color"

"github.com/charmbracelet/lipgloss"
)

// 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

// 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?
}

type teaContext struct {
context.Context

profile lipgloss.Profile
kittyFlags int
backgroundColor color.Color
hasLightBg bool // cached value
}

func newContext(ctx context.Context) *teaContext {
c := new(teaContext)
c.Context = ctx
c.kittyFlags = -1
return c
}

func (c *teaContext) BackgroundColor() color.Color {
return c.backgroundColor
}

func (c *teaContext) SupportsEnhancedKeyboard() bool {
return c.kittyFlags >= 0
}

func (c *teaContext) NewStyle() lipgloss.Style {
return lipgloss.NewStyle().ColorProfile(c.profile).HasLightBackground(c.hasLightBg)
}

func (c *teaContext) ColorProfile() lipgloss.Profile {
return c.profile
}
9 changes: 6 additions & 3 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/charmbracelet/bubbletea v0.25.1-0.20240306212323-3df8b37dba50
github.com/charmbracelet/glamour v0.7.0
github.com/charmbracelet/harmonica v0.2.0
github.com/charmbracelet/lipgloss v0.10.0
github.com/charmbracelet/lipgloss v0.10.1-0.20240510203806-a6dc2ab509a8
github.com/charmbracelet/x/exp/teatest v0.0.0-20240229115032-4b79243a3516
github.com/fogleman/ease v0.0.0-20170301025033-8da417bf1776
github.com/lucasb-eyer/go-colorful v1.2.0
Expand All @@ -20,8 +20,12 @@ require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymanbagabas/go-udiff v0.2.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/charmbracelet/x/ansi v0.1.0 // indirect
github.com/charmbracelet/x/exp/golden v0.0.0-20240222125807-0344fda748f8 // indirect
github.com/charmbracelet/x/exp/term v0.0.0-20240422202207-14b82ac81136 // indirect
github.com/charmbracelet/x/input v0.1.0 // indirect
github.com/charmbracelet/x/term v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.1.0 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
Expand All @@ -40,8 +44,7 @@ require (
github.com/yuin/goldmark-emoji v1.0.2 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/sys v0.20.0 // indirect
)

replace github.com/charmbracelet/bubbletea => ../
20 changes: 14 additions & 6 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,24 @@ github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtj
github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
github.com/charmbracelet/lipgloss v0.10.1-0.20240510203806-a6dc2ab509a8 h1:aOfL0f8fhgqKe+iOIQYAIazSwTO4J0axB5ztPxw5JHY=
github.com/charmbracelet/lipgloss v0.10.1-0.20240510203806-a6dc2ab509a8/go.mod h1:axBjGt1mTZwwTM0kfcq8fPWFzNPj+ZAOjfLCr7JW9fk=
github.com/charmbracelet/x/ansi v0.1.0 h1:o4NbQQCoVgbLpD5RC1cI687baoLwrLZyCOTGlF0gne4=
github.com/charmbracelet/x/ansi v0.1.0/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/exp/golden v0.0.0-20240222125807-0344fda748f8 h1:kyT+aGp1z5jwlus3OY0cP6FuT05jYeeExx/4TYxnyrs=
github.com/charmbracelet/x/exp/golden v0.0.0-20240222125807-0344fda748f8/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/exp/teatest v0.0.0-20240229115032-4b79243a3516 h1:7IZFEUZpEgjlTSd7P1MRRhGXs7t4F6mENeMw17TxnQs=
github.com/charmbracelet/x/exp/teatest v0.0.0-20240229115032-4b79243a3516/go.mod h1:SG24wGkG/mix5V2dZLXfQ6Bod43HGvk9CkTDxATwKN4=
github.com/charmbracelet/x/exp/term v0.0.0-20240422202207-14b82ac81136 h1:OJugUni1HhqJHullRJdl4JuYofZDty/NZRFzCzTCVj8=
github.com/charmbracelet/x/exp/term v0.0.0-20240422202207-14b82ac81136/go.mod h1:yQqGHmheaQfkqiJWjklPHVAq1dKbk8uGbcoS/lcKCJ0=
github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ=
github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28=
github.com/charmbracelet/x/term v0.1.1-0.20240510181320-e66de7a51531 h1:AuOfTqmSzOM4S4vgOzeb3gvFqhSVY1h+5RJJmr4VkDc=
github.com/charmbracelet/x/term v0.1.1-0.20240510181320-e66de7a51531/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4=
github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
Expand Down Expand Up @@ -78,7 +88,5 @@ golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module github.com/charmbracelet/bubbletea
go 1.18

require (
github.com/charmbracelet/lipgloss v0.10.1-0.20240510203806-a6dc2ab509a8
github.com/charmbracelet/x/ansi v0.1.0
github.com/charmbracelet/x/input v0.1.0
github.com/charmbracelet/x/term v0.1.0
github.com/charmbracelet/x/term v0.1.1
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6
github.com/muesli/cancelreader v0.2.2
golang.org/x/sync v0.7.0
Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
github.com/charmbracelet/lipgloss v0.10.1-0.20240510203806-a6dc2ab509a8 h1:aOfL0f8fhgqKe+iOIQYAIazSwTO4J0axB5ztPxw5JHY=
github.com/charmbracelet/lipgloss v0.10.1-0.20240510203806-a6dc2ab509a8/go.mod h1:axBjGt1mTZwwTM0kfcq8fPWFzNPj+ZAOjfLCr7JW9fk=
github.com/charmbracelet/x/ansi v0.1.0 h1:o4NbQQCoVgbLpD5RC1cI687baoLwrLZyCOTGlF0gne4=
github.com/charmbracelet/x/ansi v0.1.0/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ=
github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28=
github.com/charmbracelet/x/term v0.1.0 h1:yOhOuAKvqAIIrUg2TDHnURdxVXOBQIjMhfgg/avR6j0=
github.com/charmbracelet/x/term v0.1.0/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4=
github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
Expand Down
2 changes: 2 additions & 0 deletions nil_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ func (n nilRenderer) enableMouseSGRMode() {}
func (n nilRenderer) disableMouseSGRMode() {}
func (n nilRenderer) bracketedPasteActive() bool { return false }
func (n nilRenderer) setWindowTitle(_ string) {}
func (n nilRenderer) requestBackgroundColor() {}
func (n nilRenderer) requestDeviceAttributes() {}
2 changes: 1 addition & 1 deletion options.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type ProgramOption func(*Program)
// cancelled it will exit with an error ErrProgramKilled.
func WithContext(ctx context.Context) ProgramOption {
return func(p *Program) {
p.ctx = ctx
p.ctx = newContext(ctx)
}
}

Expand Down
7 changes: 7 additions & 0 deletions renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ type renderer interface {

// setWindowTitle sets the terminal window title.
setWindowTitle(string)

// requestBackgroundColor reports the terminal background color.
requestBackgroundColor()

// requestDeviceAttributes requests the terminal to send its device
// attributes DA1.
requestDeviceAttributes()
}

// repaintMsg forces a full repaint.
Expand Down
32 changes: 32 additions & 0 deletions screen.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ type WindowSizeMsg = input.WindowSizeEvent
// a bracketed paste event occurs.
type PasteMsg = input.PasteEvent

// BackgroundColorMsg is used to report the terminal's background color. It's
// sent to Update once initially and whenever the background color is queried
// using RequestBackgroundColor Cmd.
type BackgroundColorMsg = input.BackgroundColorEvent

// PrimaryDeviceAttributesMsg is used to report the terminal's primary device
// attributes.
type PrimaryDeviceAttributesMsg = input.PrimaryDeviceAttributesEvent

// ClearScreen is a special command that tells the program to clear the screen
// before the next update. This can be used to move the cursor to the top left
// of the screen and clear visual clutter when the alt screen is not in use.
Expand Down Expand Up @@ -147,6 +156,29 @@ func DisableBracketedPaste() Msg {
// disableBracketedPasteMsg with DisableBracketedPaste.
type disableBracketedPasteMsg struct{}

// RequestBackgroundColor is a special command that requests the terminal's
// background color. The background color will be sent to the program in a
// BackgroundColorMsg.
func RequestBackgroundColor() Msg {
return requestBackgroundColorMsg{}
}

// requestBackgroundColorMsg is an internal message that requests the terminal's
// background color. You can send a requestBackgroundColorMsg with
// RequestBackgroundColor.
type requestBackgroundColorMsg struct{}

// requestPrimaryDeviceAttributes is a special command that requests the
// primary device attributes (DA1) from the terminal. The response will be
// sent to the program in a DeviceAttributesMsg.
func requestPrimaryDeviceAttributes() Msg {

Check failure on line 174 in screen.go

View workflow job for this annotation

GitHub Actions / lint

func `requestPrimaryDeviceAttributes` is unused (unused)
return requestPrimaryDeviceAttributesMsg{}
}

// requestPrimaryDeviceAttributesMsg is an internal message that requests the
// primary device attributes (DA1) from the terminal.
type requestPrimaryDeviceAttributesMsg struct{}

Check failure on line 180 in screen.go

View workflow job for this annotation

GitHub Actions / lint

type `requestPrimaryDeviceAttributesMsg` is unused (unused)

// EnterAltScreen enters the alternate screen buffer, which consumes the entire
// terminal window. ExitAltScreen will return the terminal to its former state.
//
Expand Down
11 changes: 11 additions & 0 deletions standard_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,17 @@ func (r *standardRenderer) setWindowTitle(title string) {
r.execute(ansi.SetWindowTitle(title))
}

// requestBackgroundColor requests the background color from the terminal.
func (r *standardRenderer) requestBackgroundColor() {
r.execute(ansi.RequestBackgroundColor)
}

// requestDeviceAttributes requests the terminal to send its device request
// DA1.
func (r *standardRenderer) requestDeviceAttributes() {
r.execute(ansi.RequestPrimaryDeviceAttributes)
}

// setIgnoredLines specifies lines not to be touched by the standard Bubble Tea
// renderer.
func (r *standardRenderer) setIgnoredLines(from int, to int) {
Expand Down

0 comments on commit a434f25

Please sign in to comment.