Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions cursed_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,20 +260,32 @@ func (s *cursedRenderer) flush(closing bool) error {

view := s.view
frameArea := uv.Rect(0, 0, s.width, s.height)
if len(view.Content) == 0 {
if len(view.Content) == 0 && view.ContentDrawable == nil {
// If the component is nil, we should clear the screen buffer.
frameArea.Max.Y = 0
}

content := uv.NewStyledString(view.Content)
var content uv.Drawable
if view.ContentDrawable != nil {
content = view.ContentDrawable
} else {
content = uv.NewStyledString(view.Content)
}

if !view.AltScreen {
// We need to resizes the screen based on the frame height and
// terminal width. This is because the frame height can change based on
// the content of the frame. For example, if the frame contains a list
// of items, the height of the frame will be the number of items in the
// list. This is different from the alt screen buffer, which has a
// fixed height and width.
frameHeight := content.Height()
frameHeight := frameArea.Dy()
switch content := content.(type) {
case interface{ Height() int }:
frameHeight = content.Height()
case interface{ Bounds() uv.Rectangle }:
frameHeight = content.Bounds().Dy()
}
if frameHeight != frameArea.Dy() {
frameArea.Max.Y = frameHeight
}
Expand Down Expand Up @@ -460,7 +472,7 @@ func (s *cursedRenderer) flush(closing bool) error {
// Render and queue changes to the screen buffer.
s.scr.Render(s.cellbuf.RenderBuffer)

if cur := view.Cursor; cur != nil {
if cur := view.Cursor; cur != nil && cur.X >= 0 && cur.Y >= 0 {
// MoveTo must come after [uv.TerminalRenderer.Render] because the
// cursor position might get updated during rendering.
s.scr.MoveTo(view.Cursor.X, view.Cursor.Y)
Expand Down Expand Up @@ -800,6 +812,7 @@ func viewEquals(a, b *View) bool {
}

if a.Content != b.Content ||
a.ContentDrawable != nil || b.ContentDrawable != nil ||
a.AltScreen != b.AltScreen ||
a.DisableBracketedPasteMode != b.DisableBracketedPasteMode ||
a.ReportFocus != b.ReportFocus ||
Expand Down
8 changes: 6 additions & 2 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@ require (
charm.land/lipgloss/v2 v2.0.3
github.com/charmbracelet/colorprofile v0.4.3
github.com/charmbracelet/harmonica v0.2.0
github.com/charmbracelet/ultraviolet v0.0.0-20260422141423-a0f1f21775f7
github.com/charmbracelet/x/ansi v0.11.7
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250602192518-9e722df69bbb
github.com/charmbracelet/x/mosaic v0.0.0-20260419004333-9332b2225b80
github.com/charmbracelet/x/term v0.2.2
github.com/fogleman/ease v0.0.0-20170301025033-8da417bf1776
github.com/lucasb-eyer/go-colorful v1.4.0
github.com/mattn/go-sixel v0.0.9
github.com/segmentio/ksuid v1.0.4
)

require (
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/charmbracelet/ultraviolet v0.0.0-20260413133134-73592393e1ad // indirect
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
github.com/charmbracelet/x/termios v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.2.2 // indirect
Expand All @@ -37,11 +39,13 @@ require (
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/soniakeys/quant v1.0.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yuin/goldmark v1.7.8 // indirect
github.com/yuin/goldmark-emoji v1.0.5 // indirect
golang.org/x/image v0.39.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/text v0.36.0 // indirect
)
16 changes: 12 additions & 4 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
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/ultraviolet v0.0.0-20260413133134-73592393e1ad h1:kxhuAdAdSGSiAadbEjP7v06nCb17jXjjqF2QOX+0QiE=
github.com/charmbracelet/ultraviolet v0.0.0-20260413133134-73592393e1ad/go.mod h1:bAAz7dh/FTYfC+oiHavL4mX1tOIBZ0ZwYjSi3qE6ivM=
github.com/charmbracelet/ultraviolet v0.0.0-20260422141423-a0f1f21775f7 h1:PeRlqWGEoO0apcS62iEgxQhVnFCTOYyQvi2sUTdf6IE=
github.com/charmbracelet/ultraviolet v0.0.0-20260422141423-a0f1f21775f7/go.mod h1:3YdTxlnV/L0bQ3VN8WOSw8doF7LZV/xawUQ4MuAPDvo=
github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250602192518-9e722df69bbb h1:oTM8tZxV7FY0ehvYjFuICouuhzE08UZYNqUIp/lDQdY=
Expand All @@ -32,6 +32,8 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6g
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
github.com/charmbracelet/x/mosaic v0.0.0-20260419004333-9332b2225b80 h1:Qi2H/XzcKGfpi6bogU+21egMUabZsmGt2auN2OUlR6M=
github.com/charmbracelet/x/mosaic v0.0.0-20260419004333-9332b2225b80/go.mod h1:oECgcdtNiSV6rNXH+RE90wNf+L0TztstTEbDXGVtCyU=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
Expand All @@ -58,6 +60,8 @@ github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-sixel v0.0.9 h1:ncx/rVU35Ut7/6gpVk4deC4/Wp2js9fDKmFmWnzmGoY=
github.com/mattn/go-sixel v0.0.9/go.mod h1:mfichvavqIDFW14LGU24ux/UZ/wF0/hG+4pUWOWrQgM=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
Expand All @@ -68,6 +72,8 @@ github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/soniakeys/quant v1.0.0 h1:N1um9ktjbkZVcywBVAAYpZYSHxEfJGzshHCxx/DaI0Y=
github.com/soniakeys/quant v1.0.0/go.mod h1:HI1k023QuVbD4H8i9YdfZP2munIHU4QpjsImz6Y6zds=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
Expand All @@ -77,11 +83,13 @@ github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC
github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ go 1.25.0

require (
github.com/charmbracelet/colorprofile v0.4.3
github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468
github.com/charmbracelet/ultraviolet v0.0.0-20260422141423-a0f1f21775f7
github.com/charmbracelet/x/ansi v0.11.7
github.com/charmbracelet/x/exp/golden v0.0.0-20241212170349-ad4b7ae0f25f
github.com/charmbracelet/x/term v0.2.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWp
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468 h1:Q9fO0y1Zo5KB/5Vu8JZoLGm1N3RzF9bNj3Ao3xoR+Ac=
github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468/go.mod h1:bAAz7dh/FTYfC+oiHavL4mX1tOIBZ0ZwYjSi3qE6ivM=
github.com/charmbracelet/ultraviolet v0.0.0-20260422141423-a0f1f21775f7 h1:PeRlqWGEoO0apcS62iEgxQhVnFCTOYyQvi2sUTdf6IE=
github.com/charmbracelet/ultraviolet v0.0.0-20260422141423-a0f1f21775f7/go.mod h1:3YdTxlnV/L0bQ3VN8WOSw8doF7LZV/xawUQ4MuAPDvo=
github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
github.com/charmbracelet/x/exp/golden v0.0.0-20241212170349-ad4b7ae0f25f h1:UytXHv0UxnsDFmL/7Z9Q5SBYPwSuRLXHbwx+6LycZ2w=
Expand Down
2 changes: 2 additions & 0 deletions input.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ func (p *Program) translateInputEvent(e uv.Event) Msg {
return KeyboardEnhancementsMsg(e)
case uv.ModeReportEvent:
return ModeReportMsg(e)
case uv.PixelSizeEvent:
return PixelSizeMsg(e)
}
return e
}
6 changes: 6 additions & 0 deletions screen.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ type WindowSizeMsg struct {
Height int
}

// PixelSizeMsg is used to report the terminal's window pixel size.
type PixelSizeMsg struct {
Width int
Height int
}

// 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
19 changes: 19 additions & 0 deletions tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,25 @@ type View struct {
// ```
Content string

// ContentDrawable is an alternative to [View.Content] that can be used to
// provide a [uv.Drawable] as the content of the view. This allows for
// direct drawing to the underlying buffer, which can be more efficient for
// complex views.
//
// If both [View.Content] and [View.ContentDrawable] are set,
// [View.ContentDrawable] takes precedence.
//
// Example:
//
// ```go
// var v tea.View
// var layers []*lipgloss.Layer
// comp := lipgloss.NewCompositor(layers...)
// v.ContentDrawable = comp
// return v
// ```
ContentDrawable uv.Drawable

// OnMouse is an optional mouse message handler that can be used to
// intercept mouse messages that depends on view content from last render.
// It can be useful for implementing view-specific behavior without
Expand Down
Loading