Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
146 commits
Select commit Hold shift + click to select a range
a93bfef
feat!: use bubbletea@v2-exp
aymanbagabas Sep 9, 2024
8972b56
feat!: make Init return the model
aymanbagabas Sep 10, 2024
488bef7
Merge branch 'master' into v2-exp
aymanbagabas Sep 18, 2024
0fdf5f5
feat: use bubbletea/v2
aymanbagabas Sep 18, 2024
a2602f8
feat!: v2: move to v2 module
aymanbagabas Sep 18, 2024
3a53c6a
chore: update bubbletea to v2.0.0-alpha.1
aymanbagabas Sep 18, 2024
f81fd52
fix: spacebar keybinding is now "space" instead of a literal space
aymanbagabas Sep 18, 2024
95541c2
refactor!: viewport: remove deprecated HighPerformanceRendering
aymanbagabas Sep 19, 2024
09242fd
refactor!: remove deprecated references
aymanbagabas Sep 19, 2024
f4fca1b
fix(textarea): add support for bracketed-paste msg
aymanbagabas Oct 10, 2024
f1bc5cb
chore: merge pull request #619 from charmbracelet/v2-remove-deprecated
aymanbagabas Oct 21, 2024
17a0051
chore: merge pull request #620 from charmbracelet/v2-deprecated
aymanbagabas Oct 21, 2024
287c9c4
chore: merge branch 'master' into v2-exp
aymanbagabas Oct 21, 2024
e2033f3
chore: go mod tidy
aymanbagabas Oct 21, 2024
4e05c24
chore: move runeutil and memoization into internal
meowgorithm Oct 24, 2024
57ac102
chore(merge): branch 'master' into v2-mini-tidy
meowgorithm Oct 24, 2024
4070e9c
chore(help): fix tests
meowgorithm Oct 24, 2024
046e445
chore(merge): pull request #649 from charmbracelet/v2-mini-tidy
meowgorithm Oct 24, 2024
2ecd141
chore(deps): use Lip Gloss v2 (v2-exp); bump Bubble Tea to latest v2-exp
meowgorithm Oct 24, 2024
fadc27c
chore(help): minimal updates for Lip Gloss v2
meowgorithm Oct 24, 2024
c8d49ea
chore(filepicker): minimal updates for Lip Gloss v2
meowgorithm Oct 24, 2024
ef6a3d2
chore(list): minimal updates for Lip Gloss v2
meowgorithm Oct 24, 2024
1efec15
chore(textarea): minimal updates for Lip Gloss v2
meowgorithm Oct 24, 2024
9f3b67a
fix(help): fix tests
meowgorithm Oct 24, 2024
12271df
chore(textarea): nest focused and blurred styles in a parent struct
meowgorithm Oct 24, 2024
db64e3c
feat: update lipgloss to v2
aymanbagabas Oct 29, 2024
c560f6a
feat!(timer): add WithInterval(duration) option, drop NewWithInterval()
meowgorithm Oct 31, 2024
73fe314
feat!(textinput): DefaultKeyMap is now a function; was formerly a global
meowgorithm Oct 31, 2024
4ce3faa
feat!(textarea): DefaultKeyMap is now a func instead of a global
meowgorithm Oct 31, 2024
6415ecb
feat!(stopwatch): add WithInterval(duration) option, drop NewWithInte…
meowgorithm Oct 31, 2024
e8ba813
feat!(filepicker): use getters and setters for height
meowgorithm Oct 31, 2024
4b0f9c8
feat!(progress): width now uses getters and setters
meowgorithm Oct 31, 2024
241db06
feat!(paginator): DefaultKeyMap is now a func instead of a global
meowgorithm Oct 31, 2024
3b879e7
feat!(viewport): width and height are now optional args in New()
meowgorithm Oct 31, 2024
52bd03e
feat!(viewport): use getters and setters for width and height
meowgorithm Oct 31, 2024
591dfd5
chore(textinput): use a getter and setter for width
meowgorithm Oct 31, 2024
cc9a727
fix(timer): restore 1s default interval
meowgorithm Oct 31, 2024
148439f
feat(help): expose function for choosing light or dark styles
meowgorithm Nov 5, 2024
fc0fa78
feat(textarea): add helpers for default light and dark styles
meowgorithm Nov 6, 2024
812f74f
chore: remove unused YPosition field from viewport.Model
aymanbagabas Nov 6, 2024
cc009fb
chore: merge branch 'master' into v2-exp
aymanbagabas Nov 12, 2024
aa74e9f
chore: update dependencies to use v2-alpha.2
aymanbagabas Nov 12, 2024
0719711
chore: merge branch 'master' into v2-exp
aymanbagabas Nov 12, 2024
0a6b6e6
chore(merge): branch 'master' into v2-exp
meowgorithm Nov 15, 2024
bd415b4
chore: update dependencies and fix colors
aymanbagabas Nov 21, 2024
2e3a423
chore(progress)!: migrate progress to lipgloss (#676)
aymanbagabas Dec 12, 2024
f28e78e
Merge remote-tracking branch 'origin/master' into v2-exp
caarlos0 Jan 14, 2025
3487634
chore(deps): update ansi
caarlos0 Jan 14, 2025
fbe642d
chore(deps): update
caarlos0 Jan 14, 2025
0c83e6f
chore(deps): update
caarlos0 Jan 15, 2025
518ff7d
feat(textarea): add Cursor method (#712)
aymanbagabas Jan 23, 2025
421843b
chore(merge): branch 'master' into v2-exp
meowgorithm Jan 28, 2025
a0d9fe3
refactor: progress: return Model instead of tea.Model (#719)
aymanbagabas Jan 29, 2025
9d5d140
chore(textarea,cursor): flesh out real cursor API
meowgorithm Jan 28, 2025
7cdd5f3
chore(textarea,cursor): use textarea to manage virtual cursor styling
meowgorithm Jan 28, 2025
0b3c0a2
fix(textarea): doc comment
meowgorithm Jan 28, 2025
58b94b3
fix(textarea): doc comment typo
meowgorithm Jan 28, 2025
0e5ff3c
chore(textarea): rename Model.SetCursor to Model.SetCursorColumn
meowgorithm Jan 28, 2025
eb985a2
chore(textarea): improve cursor styling API
meowgorithm Jan 28, 2025
1fd9c26
chore(textarea,cursor): simplify cursor API
meowgorithm Jan 29, 2025
ad8e4bf
chore(textarea): improve comments, note ares that need attention
meowgorithm Jan 29, 2025
24016e6
chore(textarea): consolidate line number rendering into a method
meowgorithm Jan 30, 2025
de97754
fix(textarea): infer if we should use focused or blurred styles
meowgorithm Jan 30, 2025
63fb8c6
fix(textarea): don't hardcode line number gutter width
meowgorithm Jan 30, 2025
a6b8a45
chore(textarea): consolidate prompt rendering in a method
meowgorithm Jan 30, 2025
78262b8
chore(textarea): make placeholder rendering more readable
meowgorithm Jan 30, 2025
2824966
feat(textarea): auto-calculate inner X/Y cursor offset
meowgorithm Jan 30, 2025
841c9ce
chore(textarea): comments and cleanup
meowgorithm Jan 30, 2025
b2e3cc5
chore(textarea): account for margins and padding in cursor offsets
meowgorithm Jan 31, 2025
edbb81c
feat(viewport)!: gutter column, soft wrap, search highlight (#697)
caarlos0 Feb 4, 2025
061e464
refactor!: restore bubbletea model interface
aymanbagabas Feb 4, 2025
3b1b924
fix(table): crlf on windows
aymanbagabas Feb 4, 2025
3961136
fix(viewport): handle nil LeftGutterFunc (#723)
aymanbagabas Feb 4, 2025
a7783a1
chore: go mod tidy
aymanbagabas Feb 4, 2025
64c16e9
feat(v2)!: update to go 1.22 (#724)
caarlos0 Feb 5, 2025
605d06c
feat: real cursor in textinput (#727)
caarlos0 Feb 7, 2025
c5b7684
chore(go.mod): update `github.com/charmbracelet/x/exp/golden`
andreynering Feb 7, 2025
c20ae83
fix: lint issues (#730)
caarlos0 Feb 10, 2025
4491afa
feat(viewport): SetLines (#728)
caarlos0 Feb 11, 2025
739fec5
chore(input,textarea): return nil for cursors when using virtual cursors
meowgorithm Feb 25, 2025
6af59e6
fix(textarea): cursor position calculation
aymanbagabas Mar 10, 2025
59511a7
fix(textinput): account for width when calculating cursor position
aymanbagabas Mar 10, 2025
7149345
Merge remote-tracking branch 'origin/master' into v2-exp
caarlos0 Mar 13, 2025
bd20b89
fix(viewport): remove LineNumbersGutter
caarlos0 Mar 18, 2025
d5bc1ac
Merge remote-tracking branch 'origin/master' into v2-exp-update
caarlos0 Mar 25, 2025
acc929c
fix: lint
caarlos0 Mar 25, 2025
d179ef7
chore: upgrade dependencies and go.mod
aymanbagabas Mar 26, 2025
88ad104
Merge branch 'v2-exp' into v2-exp-update
aymanbagabas Mar 26, 2025
c860c66
Merge pull request #756 from charmbracelet/v2-exp-update
aymanbagabas Mar 26, 2025
4d1c64f
chore: upgrade to beta.1 of lipgloss and bubbletea
aymanbagabas Mar 26, 2025
6010391
test(table): update golden files for alignment tests
aymanbagabas Mar 26, 2025
4286c73
Merge remote-tracking branch 'origin/master' into v2-exp
caarlos0 Mar 27, 2025
b1cef26
fix: normalize yoffset
caarlos0 Mar 27, 2025
6f4a536
Update viewport/viewport.go
caarlos0 Mar 27, 2025
3a0776e
Merge pull request #765 from charmbracelet/updates
caarlos0 Mar 28, 2025
a0a432e
feat(viewport): horizontal scroll with mouse wheel
UnseenBook Mar 25, 2025
3820007
Merge branch 'UnseenBook/master' into v2-exp
caarlos0 Mar 28, 2025
6a68f36
Merge remote-tracking branch 'origin/master' into v2-exp
caarlos0 Mar 28, 2025
9d2d5df
Merge remote-tracking branch 'origin/master' into v2-exp
caarlos0 Mar 31, 2025
bb6a2a4
Merge remote-tracking branch 'origin/master' into v2-exp
caarlos0 Apr 14, 2025
dffbe59
Merge branch 'master' into v2-exp-table-tests
andreynering May 16, 2025
7383985
test: update golden files with nbsp for v2
andreynering May 16, 2025
47b0944
test: fix tests by ignoring func references
andreynering May 16, 2025
081e998
Merge pull request #790 from charmbracelet/v2-exp-table-tests
andreynering May 16, 2025
ba5555a
fix(cursor): set ID on virutal cursors
meowgorithm May 25, 2025
8e84f33
fix!(textinput): cursor fixes and improvements
meowgorithm May 26, 2025
b531de8
fix!(textarea): virtual cursor blink
meowgorithm May 26, 2025
bb1d1d2
fix(textarea): suppress blink messages when real cursor is active
meowgorithm May 26, 2025
3cbcdd9
chore(textinput): minor logic improvement with regard to cursors
meowgorithm May 26, 2025
d42b7c4
fix(textarea/tests): update tests per API changes
meowgorithm May 26, 2025
b2bcf22
chore(textarea,textinput/lint): modernize loops
meowgorithm May 26, 2025
0f113d1
chore(textarea,textinput/lint): use slices.Delete()
meowgorithm May 26, 2025
e89dc94
chore!(cursor): improve naming around 'blinking'
meowgorithm May 26, 2025
b3f0c9e
fix(textarea): cursorline now fills the line when a placeholder is pr…
meowgorithm May 26, 2025
04ec518
chore: run `modernize`
andreynering May 30, 2025
56bbc4a
fix(textinput,textarea): don't draw real cusor when blurred
meowgorithm Jun 3, 2025
696244a
feat(textarea): expose MoveToBegin and MoveToEnd methods (#809)
aymanbagabas Jul 10, 2025
a4c42b5
feat(textarea): add focus status to setPromptFunc (#797)
meowgorithm Jul 10, 2025
1e2ffbb
feat(textarea): get the word under the cursor (#814)
caarlos0 Jul 16, 2025
c4068c6
fix(textarea): ensure viewport content is set during update
aymanbagabas Aug 1, 2025
efdc0e8
fix(textarea): use pointer receiver for Model methods
aymanbagabas Aug 7, 2025
86f3326
fix(textarea): update tests to reflect changes in textarea content
aymanbagabas Aug 7, 2025
601216f
Merge pull request #822 from charmbracelet/textarea/set-vp-content
aymanbagabas Aug 20, 2025
c690ea5
feat(textarea): add ScrollYOffset and ScrollPosition methods
lrstanley Aug 19, 2025
50038eb
fix(textarea): ensure cursor is always in view (#840)
lrstanley Sep 8, 2025
7445f97
refactor(viewport): softwrap; improve perf; various bug fixes (#823)
lrstanley Sep 12, 2025
bc03e71
feat(textarea): add PageUp & PageDown support (#844)
lrstanley Sep 23, 2025
c2223a7
fix(list): ensure correct cursor positions with page/cursor methods (…
lrstanley Sep 29, 2025
e606fbc
chore(deps): update
caarlos0 Oct 1, 2025
f03bfcc
Merge remote-tracking branch 'origin/master' into v2-exp
caarlos0 Oct 1, 2025
5187641
chore: bump dependencies
aymanbagabas Nov 4, 2025
e8fcfc5
refactor: use msg.Content for PasteMsg in textinput and textarea
aymanbagabas Nov 4, 2025
ffc9ec2
chore(table): update golden files for view and alignment tests
aymanbagabas Nov 4, 2025
84fd71d
fix: use charm.land import path for bubbletea
aymanbagabas Nov 4, 2025
07735d1
refactor: update module path to charm.land
aymanbagabas Nov 4, 2025
a10cfdd
chore: update import paths from github.com/charmbracelet/bubbles/v2 t…
aymanbagabas Nov 4, 2025
da0b892
refactor: migrate imports to charm.land/lipgloss
aymanbagabas Nov 4, 2025
c2b8227
chore: bump dependencies to include latest ultraviolet changes
aymanbagabas Nov 6, 2025
5fdd73f
chore: format file imports with gofumpt
aymanbagabas Nov 10, 2025
faa17cb
feat(progress): support multiple stops and improved blend algorithm (…
lrstanley Nov 10, 2025
84a82df
chore(textarea): remove pointer receiver on update and view (#858)
meowgorithm Nov 10, 2025
538d39c
refactor(help): use setter/getter for help width
aymanbagabas Nov 17, 2025
93a004a
fix(viewport): optimize subline splitting by skipping lines without l…
aymanbagabas Dec 8, 2025
ae99f46
feat(v2/textarea): expose Column(), clarify 0-indexing (#875)
caarlos0 Jan 9, 2026
97b179b
chore: update LICENSE copyright
meowgorithm Feb 10, 2026
cc76d41
Merge branch 'master' into v2-exp
aymanbagabas Feb 10, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jobs:
coverage:
strategy:
matrix:
go-version: [^1.18]
go-version: [^1.22]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
env:
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020-2025 Charmbracelet, Inc
Copyright (c) 2020-2026 Charmbracelet, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ var DefaultKeyMap = KeyMap{

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
case tea.KeyPressMsg:
switch {
case key.Matches(msg, DefaultKeyMap.Up):
// The user pressed up
Expand Down
66 changes: 43 additions & 23 deletions cursor/cursor.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
// Package cursor provides cursor functionality for Bubble Tea applications.
// Package cursor provides a virtual cursor to support the textinput and
// textarea elements.
package cursor

import (
"context"
"sync/atomic"
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
)

const defaultBlinkSpeed = time.Millisecond * 530

// Internal ID management. Used during animating to ensure that frame messages
// are received only by spinner components that sent them.
var lastID int64

func nextID() int {
return int(atomic.AddInt64(&lastID, 1))
}

// initialBlinkMsg initializes cursor blinking.
type initialBlinkMsg struct{}

Expand Down Expand Up @@ -52,36 +62,47 @@ func (c Mode) String() string {

// Model is the Bubble Tea model for this cursor element.
type Model struct {
BlinkSpeed time.Duration
// Style for styling the cursor block.
// Style styles the cursor block.
Style lipgloss.Style
// TextStyle is the style used for the cursor when it is hidden (when blinking).
// I.e. displaying normal text.

// TextStyle is the style used for the cursor when it is blinking
// (hidden), i.e. displaying normal text.
TextStyle lipgloss.Style

// BlinkSpeed is the speed at which the cursor blinks. This has no effect
// unless [CursorMode] is not set to [CursorBlink].
BlinkSpeed time.Duration

// IsBlinked is the state of the cursor blink. When true, the cursor is
// hidden.
IsBlinked bool

// char is the character under the cursor
char string

// The ID of this Model as it relates to other cursors
id int

// focus indicates whether the containing input is focused
focus bool
// Cursor Blink state.
Blink bool

// Used to manage cursor blink
blinkCtx *blinkCtx

// The ID of the blink message we're expecting to receive.
blinkTag int

// mode determines the behavior of the cursor
mode Mode
}

// New creates a new model with default settings.
func New() Model {
return Model{
id: nextID(),
BlinkSpeed: defaultBlinkSpeed,

Blink: true,
mode: CursorBlink,
IsBlinked: true,
mode: CursorBlink,

blinkCtx: &blinkCtx{
ctx: context.Background(),
Expand All @@ -99,7 +120,7 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
return m, nil
}

cmd := m.BlinkCmd()
cmd := m.Blink()
return m, cmd

case tea.FocusMsg:
Expand All @@ -125,8 +146,8 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {

var cmd tea.Cmd
if m.mode == CursorBlink {
m.Blink = !m.Blink
cmd = m.BlinkCmd()
m.IsBlinked = !m.IsBlinked
cmd = m.Blink()
}
return m, cmd

Expand All @@ -151,15 +172,15 @@ func (m *Model) SetMode(mode Mode) tea.Cmd {
return nil
}
m.mode = mode
m.Blink = m.mode == CursorHide || !m.focus
m.IsBlinked = m.mode == CursorHide || !m.focus
if mode == CursorBlink {
return Blink
}
return nil
}

// BlinkCmd is a command used to manage cursor blinking.
func (m *Model) BlinkCmd() tea.Cmd {
// Blink is a command used to manage cursor blinking.
func (m *Model) Blink() tea.Cmd {
if m.mode != CursorBlink {
return nil
}
Expand All @@ -172,7 +193,6 @@ func (m *Model) BlinkCmd() tea.Cmd {
m.blinkCtx.cancel = cancel

m.blinkTag++

blinkMsg := BlinkMsg{id: m.id, tag: m.blinkTag}

return func() tea.Msg {
Expand All @@ -193,18 +213,18 @@ func Blink() tea.Msg {
// Focus focuses the cursor to allow it to blink if desired.
func (m *Model) Focus() tea.Cmd {
m.focus = true
m.Blink = m.mode == CursorHide // show the cursor unless we've explicitly hidden it
m.IsBlinked = m.mode == CursorHide // show the cursor unless we've explicitly hidden it

if m.mode == CursorBlink && m.focus {
return m.BlinkCmd()
return m.Blink()
}
return nil
}

// Blur blurs the cursor.
func (m *Model) Blur() {
m.focus = false
m.Blink = true
m.IsBlinked = true
}

// SetChar sets the character under the cursor.
Expand All @@ -214,7 +234,7 @@ func (m *Model) SetChar(char string) {

// View displays the cursor.
func (m Model) View() string {
if m.Blink {
if m.IsBlinked {
return m.TextStyle.Inline(true).Render(m.char)
}
return m.Style.Inline(true).Reverse(true).Render(m.char)
Expand Down
16 changes: 8 additions & 8 deletions cursor/cursor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

// TestBlinkCmdDataRace tests for a race on [Cursor.blinkTag].
//
// The original [Model.BlinkCmd] implementation returned a closure over the pointer receiver:
// The original [Model.Blink] implementation returned a closure over the pointer receiver:
//
// return func() tea.Msg {
// defer cancel()
Expand All @@ -20,20 +20,20 @@ import (
// }
//
// A race on “m.blinkTag” will occur if:
// 1. [Model.BlinkCmd] is called e.g. by calling [Model.Focus] from
// ["github.com/charmbracelet/bubbletea".Model.Update];
// 2. ["github.com/charmbracelet/bubbletea".handleCommands] is kept sufficiently busy that it does not receive and
// 1. [Model.Blink] is called e.g. by calling [Model.Focus] from
// ["charm.land/bubbletea/v2".Model.Update];
// 2. ["charm.land/bubbletea/v2".handleCommands] is kept sufficiently busy that it does not receive and
// execute the [Model.BlinkCmd] e.g. by other long running command or commands;
// 3. at least [Mode.BlinkSpeed] time elapses;
// 4. [Model.BlinkCmd] is called again;
// 5. ["github.com/charmbracelet/bubbletea".handleCommands] gets around to receiving and executing the original
// 4. [Model.Blink] is called again;
// 5. ["charm.land/bubbletea/v2".handleCommands] gets around to receiving and executing the original
// closure.
//
// Even if this did not formally race, the value of the tag fetched would be semantically incorrect (likely being the
// current value rather than the value at the time the closure was created).
func TestBlinkCmdDataRace(t *testing.T) {
m := New()
cmd := m.BlinkCmd()
cmd := m.Blink()
var wg sync.WaitGroup
wg.Add(2)
go func() {
Expand All @@ -44,7 +44,7 @@ func TestBlinkCmdDataRace(t *testing.T) {
go func() {
defer wg.Done()
time.Sleep(m.BlinkSpeed * 2)
m.BlinkCmd()
m.Blink()
}()
wg.Wait()
}
Loading
Loading