Skip to content

Commit 76e537c

Browse files
committed
feat: upgrade bubbletea, bubbles, and lipgloss to v2
Migrate from github.com/charmbracelet/* v1 to charm.land/*/v2. Key API changes: - View() returns tea.View struct with declarative AltScreen/MouseMode fields - tea.KeyMsg -> tea.KeyPressMsg, msg.Type -> msg.Code - tea.MouseMsg split into MouseClickMsg/MouseWheelMsg with shortened constants - viewport.New uses functional options, fields use getter/setter methods - textinput styles restructured into Focused/Blurred StyleState via SetStyles() - help.Width uses SetWidth() setter - lipgloss v2 Width/Height now include borders in their calculation, requiring pane styles to use total dimensions instead of inner content dimensions - lipgloss v2 auto-wraps content when Width is set, requiring tree delegate to truncate items with ansi.Truncate to prevent vertical overflow
1 parent df48653 commit 76e537c

11 files changed

Lines changed: 101 additions & 96 deletions

File tree

go.mod

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,30 @@ module github.com/block/drift
33
go 1.26.1
44

55
require (
6+
charm.land/bubbles/v2 v2.0.0
7+
charm.land/bubbletea/v2 v2.0.2
8+
charm.land/lipgloss/v2 v2.0.2
69
github.com/alecthomas/kong v1.14.0
710
github.com/atotto/clipboard v0.1.4
8-
github.com/charmbracelet/bubbles v1.0.0
9-
github.com/charmbracelet/bubbletea v1.3.10
10-
github.com/charmbracelet/lipgloss v1.1.0
1111
github.com/charmbracelet/x/ansi v0.11.6
1212
github.com/sahilm/fuzzy v0.1.1
1313
golang.org/x/term v0.41.0
1414
znkr.io/diff v1.0.0
1515
)
1616

1717
require (
18-
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
1918
github.com/charmbracelet/colorprofile v0.4.3 // indirect
20-
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
19+
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
2120
github.com/charmbracelet/x/term v0.2.2 // indirect
21+
github.com/charmbracelet/x/termios v0.1.1 // indirect
22+
github.com/charmbracelet/x/windows v0.2.2 // indirect
2223
github.com/clipperhouse/displaywidth v0.11.0 // indirect
2324
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
24-
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
2525
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
26-
github.com/mattn/go-isatty v0.0.20 // indirect
27-
github.com/mattn/go-localereader v0.0.1 // indirect
2826
github.com/mattn/go-runewidth v0.0.21 // indirect
29-
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
3027
github.com/muesli/cancelreader v0.2.2 // indirect
31-
github.com/muesli/termenv v0.16.0 // indirect
3228
github.com/rivo/uniseg v0.4.7 // indirect
3329
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
30+
golang.org/x/sync v0.20.0 // indirect
3431
golang.org/x/sys v0.42.0 // indirect
35-
golang.org/x/text v0.35.0 // indirect
3632
)

go.sum

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
charm.land/bubbles/v2 v2.0.0 h1:tE3eK/pHjmtrDiRdoC9uGNLgpopOd8fjhEe31B/ai5s=
2+
charm.land/bubbles/v2 v2.0.0/go.mod h1:rCHoleP2XhU8um45NTuOWBPNVHxnkXKTiZqcclL/qOI=
3+
charm.land/bubbletea/v2 v2.0.2 h1:4CRtRnuZOdFDTWSff9r8QFt/9+z6Emubz3aDMnf/dx0=
4+
charm.land/bubbletea/v2 v2.0.2/go.mod h1:3LRff2U4WIYXy7MTxfbAQ+AdfM3D8Xuvz2wbsOD9OHQ=
5+
charm.land/lipgloss/v2 v2.0.2 h1:xFolbF8JdpNkM2cEPTfXEcW1p6NRzOWTSamRfYEw8cs=
6+
charm.land/lipgloss/v2 v2.0.2/go.mod h1:KjPle2Qd3YmvP1KL5OMHiHysGcNwq6u83MUjYkFvEkM=
17
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
28
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
39
github.com/alecthomas/kong v1.14.0 h1:gFgEUZWu2ZmZ+UhyZ1bDhuutbKN1nTtJTwh19Wsn21s=
@@ -6,32 +12,26 @@ github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs
612
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
713
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
814
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
9-
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
10-
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
11-
github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
12-
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
13-
github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
14-
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
15-
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
16-
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
15+
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
16+
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
1717
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
1818
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
19-
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
20-
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
19+
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 h1:eyFRbAmexyt43hVfeyBofiGSEmJ7krjLOYt/9CF5NKA=
20+
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8/go.mod h1:SQpCTRNBtzJkwku5ye4S3HEuthAlGy2n9VXZnWkEW98=
2121
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
2222
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
23-
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
24-
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
25-
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
26-
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
23+
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
24+
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
2725
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
2826
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
27+
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
28+
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
29+
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
30+
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
2931
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
3032
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
3133
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
3234
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
33-
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
34-
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
3535
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
3636
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
3737
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
@@ -40,18 +40,10 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
4040
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
4141
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
4242
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
43-
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
44-
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
45-
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
46-
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
4743
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
4844
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
49-
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
50-
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
5145
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
5246
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
53-
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
54-
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
5547
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
5648
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
5749
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
@@ -60,14 +52,12 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM
6052
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
6153
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
6254
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
63-
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
64-
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
55+
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
56+
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
6557
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
6658
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
6759
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
6860
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
69-
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
70-
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
7161
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
7262
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
7363
znkr.io/diff v1.0.0 h1:UCiRHh9y99KcaIZtpJO9BxXVV+idvbh0YhNJMyoFU3U=

tui/alert.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package tui
33
import (
44
"time"
55

6-
tea "github.com/charmbracelet/bubbletea"
7-
"github.com/charmbracelet/lipgloss"
6+
tea "charm.land/bubbletea/v2"
7+
"charm.land/lipgloss/v2"
88
)
99

1010
// alertLevel controls the visual style of an alert.

tui/app.go

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ package tui
33
import (
44
"strings"
55

6+
"charm.land/bubbles/v2/help"
7+
"charm.land/bubbles/v2/key"
8+
tea "charm.land/bubbletea/v2"
9+
"charm.land/lipgloss/v2"
610
"github.com/atotto/clipboard"
711
"github.com/block/drift/compare"
8-
"github.com/charmbracelet/bubbles/help"
9-
"github.com/charmbracelet/bubbles/key"
10-
tea "github.com/charmbracelet/bubbletea"
11-
"github.com/charmbracelet/lipgloss"
1212
)
1313

1414
// focusedPane tracks which pane has keyboard focus.
@@ -87,7 +87,7 @@ func New(result *compare.Result) Model {
8787
}
8888

8989
func (m Model) Init() tea.Cmd {
90-
cmds := []tea.Cmd{tea.WindowSize()}
90+
cmds := []tea.Cmd{tea.RequestWindowSize}
9191
if m.standalone {
9292
// Auto-load detail for the single root node.
9393
cmds = append(cmds, func() tea.Msg {
@@ -102,11 +102,11 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
102102
case tea.WindowSizeMsg:
103103
m.width = msg.Width
104104
m.height = msg.Height
105-
m.help.Width = msg.Width
105+
m.help.SetWidth(msg.Width)
106106
m.layout()
107107
return m, nil
108108

109-
case tea.KeyMsg:
109+
case tea.KeyPressMsg:
110110
// While search is active, route input to the search bar.
111111
if m.search.Active() {
112112
return m.updateSearch(msg)
@@ -136,16 +136,16 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
136136
cmd := m.search.Activate()
137137
m.layout()
138138
return m, cmd
139-
case msg.Type == tea.KeyEscape && m.search.HasQuery():
139+
case msg.Code == tea.KeyEscape && m.search.HasQuery():
140140
// Clear confirmed search.
141141
m.search.Deactivate()
142142
m.applySearchQuery("")
143143
m.layout()
144144
return m, nil
145145
}
146146

147-
case tea.MouseMsg:
148-
if !m.standalone && msg.Button == tea.MouseButtonLeft && msg.Action == tea.MouseActionPress {
147+
case tea.MouseClickMsg:
148+
if !m.standalone && msg.Button == tea.MouseLeft {
149149
treeWidth := int(float64(m.width) * treeSplitRatio)
150150
if msg.X < treeWidth {
151151
m.focus = paneTree
@@ -240,8 +240,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
240240
}
241241

242242
// updateSearch handles key messages while the search bar is active.
243-
func (m Model) updateSearch(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
244-
switch msg.Type {
243+
func (m Model) updateSearch(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
244+
switch msg.Code {
245245
case tea.KeyEscape:
246246
// Cancel: clear search and restore.
247247
m.search.Deactivate()
@@ -316,9 +316,9 @@ func (m *Model) loadDetailFor(node *compare.Node) tea.Cmd {
316316
}
317317
}
318318

319-
func (m Model) View() string {
319+
func (m Model) View() tea.View {
320320
if m.width == 0 || m.height == 0 {
321-
return ""
321+
return tea.View{AltScreen: true, MouseMode: tea.MouseModeCellMotion}
322322
}
323323

324324
header := m.renderHeader()
@@ -329,16 +329,20 @@ func (m Model) View() string {
329329
chrome := lipgloss.Height(header) + 1 + lipgloss.Height(summary) + 1 + lipgloss.Height(helpView)
330330
contentHeight := m.height - chrome
331331
if contentHeight < 1 {
332-
return header + "\n" + summary + "\n" + helpView
332+
return tea.View{
333+
Content: header + "\n" + summary + "\n" + helpView,
334+
AltScreen: true,
335+
MouseMode: tea.MouseModeCellMotion,
336+
}
333337
}
334338

335-
innerH := contentHeight - borderHeight
336339
var content string
337340

338341
if m.standalone {
339342
// Full-width detail for standalone mode.
343+
// In lipgloss v2, Width/Height include borders, so pass total dimensions.
340344
detailInnerW := m.width - panePadding
341-
detailStyle := styleFocusedBorder.Width(detailInnerW).Height(innerH).MaxHeight(innerH + borderHeight)
345+
detailStyle := styleFocusedBorder.Width(m.width).Height(contentHeight).MaxHeight(contentHeight)
342346

343347
content = detailStyle.Render(m.detailPaneChrome(detailInnerW) + m.detail.View())
344348
} else {
@@ -347,13 +351,14 @@ func (m Model) View() string {
347351
detailWidth := m.width - treeWidth
348352

349353
// Tree pane with filter badge and optional search bar.
354+
// In lipgloss v2, Width/Height include borders, so pass total pane dimensions.
350355
treeInnerW := treeWidth - panePadding
351-
treeStyle := m.paneStyle(paneTree).Width(treeInnerW).Height(innerH).MaxHeight(innerH + borderHeight)
356+
treeStyle := m.paneStyle(paneTree).Width(treeWidth).Height(contentHeight).MaxHeight(contentHeight)
352357
treeView := treeStyle.Render(m.treePaneChrome(treeInnerW) + m.tree.View())
353358

354359
// Detail pane with optional search bar.
355360
detailInnerW := detailWidth - panePadding
356-
detailStyle := m.paneStyle(paneDetail).Width(detailInnerW).Height(innerH).MaxHeight(innerH + borderHeight)
361+
detailStyle := m.paneStyle(paneDetail).Width(detailWidth).Height(contentHeight).MaxHeight(contentHeight)
357362
detailView := detailStyle.Render(m.detailPaneChrome(detailInnerW) + m.detail.View())
358363

359364
content = lipgloss.JoinHorizontal(lipgloss.Top, treeView, detailView)
@@ -372,7 +377,11 @@ func (m Model) View() string {
372377
view = centerOverlay(m.alert.Render(), view, m.width, m.height)
373378
}
374379

375-
return view
380+
return tea.View{
381+
Content: view,
382+
AltScreen: true,
383+
MouseMode: tea.MouseModeCellMotion,
384+
}
376385
}
377386

378387
// searchActiveFor returns true if the search bar should be shown in the given pane.
@@ -471,7 +480,7 @@ func (m *Model) layout() {
471480
// Run starts the TUI program.
472481
func Run(result *compare.Result) error {
473482
m := New(result)
474-
p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion())
483+
p := tea.NewProgram(m)
475484
_, err := p.Run()
476485
return err
477486
}

tui/components.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package tui
33
import (
44
"strings"
55

6+
"charm.land/lipgloss/v2"
67
"github.com/block/drift/compare"
7-
"github.com/charmbracelet/lipgloss"
88
"github.com/charmbracelet/x/ansi"
99
)
1010

tui/detail.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package tui
22

33
import (
4+
"charm.land/bubbles/v2/viewport"
5+
tea "charm.land/bubbletea/v2"
46
"github.com/block/drift/compare"
5-
"github.com/charmbracelet/bubbles/viewport"
6-
tea "github.com/charmbracelet/bubbletea"
77
)
88

99
// detailModel manages the detail pane viewport.
@@ -18,8 +18,7 @@ type detailModel struct {
1818
}
1919

2020
func newDetailModel(width, height int) detailModel {
21-
vp := viewport.New(width, height)
22-
vp.MouseWheelEnabled = true
21+
vp := viewport.New(viewport.WithWidth(width), viewport.WithHeight(height))
2322
return detailModel{viewport: vp}
2423
}
2524

@@ -38,8 +37,8 @@ func (m detailModel) View() string {
3837

3938
// SetSize updates the viewport dimensions.
4039
func (m *detailModel) SetSize(width, height int) {
41-
m.viewport.Width = width
42-
m.viewport.Height = height
40+
m.viewport.SetWidth(width)
41+
m.viewport.SetHeight(height)
4342
}
4443

4544
// SetContent sets the rendered detail content and scrolls to top.
@@ -48,7 +47,7 @@ func (m *detailModel) SetContent(node *compare.Node, detail *compare.DetailResul
4847
m.lastDetail = detail
4948
m.lastErr = nil
5049
m.ready = true
51-
m.renderedContent = renderDetail(node, detail, m.viewport.Width)
50+
m.renderedContent = renderDetail(node, detail, m.viewport.Width())
5251
m.applySearch()
5352
m.viewport.GotoTop()
5453
}
@@ -59,7 +58,7 @@ func (m *detailModel) SetLoading(node *compare.Node) {
5958
m.lastDetail = nil
6059
m.lastErr = nil
6160
m.ready = true
62-
header := NodeHeaderView{Node: node, Width: m.viewport.Width}.Render()
61+
header := NodeHeaderView{Node: node, Width: m.viewport.Width()}.Render()
6362
m.viewport.SetContent(header + "\n\n" + styleDim.Render(" Loading..."))
6463
m.viewport.GotoTop()
6564
}
@@ -70,7 +69,7 @@ func (m *detailModel) SetError(node *compare.Node, err error) {
7069
m.lastDetail = nil
7170
m.lastErr = err
7271
m.ready = true
73-
m.viewport.SetContent(ErrorView{Node: node, Err: err, Width: m.viewport.Width}.Render())
72+
m.viewport.SetContent(ErrorView{Node: node, Err: err, Width: m.viewport.Width()}.Render())
7473
m.viewport.GotoTop()
7574
}
7675

@@ -130,10 +129,10 @@ func (m detailModel) CopyableText() string {
130129
return ""
131130
}
132131
if m.lastErr != nil {
133-
return ErrorView{Node: m.node, Err: m.lastErr, Width: m.viewport.Width}.CopyableText()
132+
return ErrorView{Node: m.node, Err: m.lastErr, Width: m.viewport.Width()}.CopyableText()
134133
}
135134
if m.lastDetail != nil {
136-
return copyableDetail(m.node, m.lastDetail, m.viewport.Width)
135+
return copyableDetail(m.node, m.lastDetail, m.viewport.Width())
137136
}
138-
return NodeHeaderView{Node: m.node, Width: m.viewport.Width}.CopyableText()
137+
return NodeHeaderView{Node: m.node, Width: m.viewport.Width()}.CopyableText()
139138
}

tui/help.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package tui
22

33
import (
4-
"github.com/charmbracelet/bubbles/key"
4+
"charm.land/bubbles/v2/key"
55
)
66

77
// keyMap defines all keybindings for the app.

tui/overlay.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package tui
33
import (
44
"strings"
55

6-
"github.com/charmbracelet/lipgloss"
6+
"charm.land/lipgloss/v2"
77
"github.com/charmbracelet/x/ansi"
88
)
99

0 commit comments

Comments
 (0)