diff --git a/examples/basic/03-styling/main.go b/examples/basic/03-styling/main.go index 31276ac..94d1708 100644 --- a/examples/basic/03-styling/main.go +++ b/examples/basic/03-styling/main.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/charmbracelet/lipgloss" + "charm.land/lipgloss/v2" "github.com/Digital-Shane/treeview" "github.com/Digital-Shane/treeview/examples/shared" diff --git a/examples/basic/04-comparison-display/main.go b/examples/basic/04-comparison-display/main.go index 0aa79ce..2ed870f 100644 --- a/examples/basic/04-comparison-display/main.go +++ b/examples/basic/04-comparison-display/main.go @@ -6,7 +6,7 @@ import ( "github.com/Digital-Shane/treeview" "github.com/Digital-Shane/treeview/examples/shared" - "github.com/charmbracelet/lipgloss" + "charm.land/lipgloss/v2" ) //////////////////////////////////////////////////////////////////// diff --git a/examples/basic/05-workflow-visualization/main.go b/examples/basic/05-workflow-visualization/main.go index 6921ca5..a453c27 100644 --- a/examples/basic/05-workflow-visualization/main.go +++ b/examples/basic/05-workflow-visualization/main.go @@ -7,7 +7,7 @@ import ( "github.com/Digital-Shane/treeview" "github.com/Digital-Shane/treeview/examples/shared" - "github.com/charmbracelet/lipgloss" + "charm.land/lipgloss/v2" ) //////////////////////////////////////////////////////////////////// diff --git a/examples/basic/07-builders-nested/main.go b/examples/basic/07-builders-nested/main.go index 6b2d2fc..02293e0 100644 --- a/examples/basic/07-builders-nested/main.go +++ b/examples/basic/07-builders-nested/main.go @@ -6,7 +6,7 @@ import ( "github.com/Digital-Shane/treeview" "github.com/Digital-Shane/treeview/examples/shared" - "github.com/charmbracelet/lipgloss" + "charm.land/lipgloss/v2" ) // ========================================================================== diff --git a/examples/intermediate/01-keyboard-controls/main.go b/examples/intermediate/01-keyboard-controls/main.go index ae32318..5f6abd7 100644 --- a/examples/intermediate/01-keyboard-controls/main.go +++ b/examples/intermediate/01-keyboard-controls/main.go @@ -4,8 +4,8 @@ import ( "fmt" "os" + tea "charm.land/bubbletea/v2" "github.com/Digital-Shane/treeview" - "github.com/charmbracelet/bubbletea" ) func main() { @@ -26,7 +26,7 @@ func main() { ) // Create the program with navigation help bar - p := tea.NewProgram(model, tea.WithAltScreen()) + p := tea.NewProgram(model) // Run the program directly if _, err := p.Run(); err != nil { diff --git a/examples/intermediate/02-search/main.go b/examples/intermediate/02-search/main.go index f6a71a2..fe5bd4b 100644 --- a/examples/intermediate/02-search/main.go +++ b/examples/intermediate/02-search/main.go @@ -6,8 +6,8 @@ import ( "os" "strings" + tea "charm.land/bubbletea/v2" "github.com/Digital-Shane/treeview" - "github.com/charmbracelet/bubbletea" ) // Product represents a product with additional searchable data @@ -41,7 +41,7 @@ func main() { ) // Run the program - p := tea.NewProgram(model, tea.WithAltScreen()) + p := tea.NewProgram(model) if _, err := p.Run(); err != nil { fmt.Printf("Error running program: %v\n", err) os.Exit(1) diff --git a/examples/intermediate/03-viewport/main.go b/examples/intermediate/03-viewport/main.go index 2f88548..facc5ef 100644 --- a/examples/intermediate/03-viewport/main.go +++ b/examples/intermediate/03-viewport/main.go @@ -4,8 +4,8 @@ import ( "fmt" "log" + tea "charm.land/bubbletea/v2" "github.com/Digital-Shane/treeview" - "github.com/charmbracelet/bubbletea" ) func main() { @@ -32,7 +32,7 @@ func main() { } // Create the program - p := tea.NewProgram(model, tea.WithAltScreen()) + p := tea.NewProgram(model) // Run the program if _, err := p.Run(); err != nil { diff --git a/examples/intermediate/04-file-browser/main.go b/examples/intermediate/04-file-browser/main.go index 4a6d57e..85d9794 100644 --- a/examples/intermediate/04-file-browser/main.go +++ b/examples/intermediate/04-file-browser/main.go @@ -10,9 +10,9 @@ import ( "strings" "time" + tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" "github.com/Digital-Shane/treeview" - "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" ) func main() { @@ -26,7 +26,7 @@ func main() { model := NewFileBrowserModel(initialPath) // Create the program - p := tea.NewProgram(model, tea.WithAltScreen()) + p := tea.NewProgram(model) // Run the program if _, err := p.Run(); err != nil { @@ -269,9 +269,14 @@ func (m *FileBrowserModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } // View renders the complete file browser interface -func (m *FileBrowserModel) View() string { +func (m *FileBrowserModel) View() tea.View { + var view tea.View + + view.AltScreen = true + if m.treeModel == nil { - return "Loading..." + view.SetContent("Loading...") + return view } var b strings.Builder @@ -281,13 +286,15 @@ func (m *FileBrowserModel) View() string { if m.showMetadata { b.WriteString(m.renderTwoPanelLayout()) } else { - b.WriteString(m.treeModel.View()) + b.WriteString(m.treeModel.View().Content) } b.WriteByte('\n') b.WriteString(m.renderStatusBar()) - return b.String() + view.SetContent(b.String()) + + return view } // renderHeader creates the header bar @@ -309,7 +316,7 @@ func (m *FileBrowserModel) renderTwoPanelLayout() string { metadataView := m.renderMetadataPanel(metadataWidth) treeView := m.treeModel.View() - return lipgloss.JoinHorizontal(lipgloss.Top, metadataView, " │ ", treeView) + return lipgloss.JoinHorizontal(lipgloss.Top, metadataView, " │ ", treeView.Content) } // renderMetadataPanel creates the metadata side panel @@ -385,7 +392,7 @@ func (m *FileBrowserModel) renderMultiNodeMetadata(nodes []*treeview.Node[treevi // Count by type var files, dirs int var totalSize int64 - var extensions = make(map[string]int) + extensions := make(map[string]int) for _, node := range nodes { data := node.Data() diff --git a/go.mod b/go.mod index cd224f3..893cc85 100644 --- a/go.mod +++ b/go.mod @@ -1,31 +1,28 @@ module github.com/Digital-Shane/treeview -go 1.24 +go 1.25.0 require ( - github.com/charmbracelet/bubbles v0.21.0 - github.com/charmbracelet/bubbletea v1.3.6 - github.com/charmbracelet/lipgloss v1.1.0 + charm.land/bubbles/v2 v2.1.0 + charm.land/bubbletea/v2 v2.0.2 + charm.land/lipgloss/v2 v2.0.2 github.com/google/go-cmp v0.7.0 - github.com/mattn/go-runewidth v0.0.16 + github.com/mattn/go-runewidth v0.0.21 ) require ( - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/colorprofile v0.3.1 // indirect - github.com/charmbracelet/x/ansi v0.9.3 // indirect - github.com/charmbracelet/x/cellbuf v0.0.13 // indirect - github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/charmbracelet/colorprofile v0.4.2 // indirect + github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect + github.com/charmbracelet/x/ansi v0.11.6 // indirect + github.com/charmbracelet/x/term v0.2.2 // indirect + github.com/charmbracelet/x/termios v0.1.1 // indirect + github.com/charmbracelet/x/windows v0.2.2 // indirect + github.com/clipperhouse/displaywidth v0.11.0 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect + github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.16.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/text v0.27.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.42.0 // indirect ) diff --git a/go.sum b/go.sum index 56c4cc9..cf498f6 100644 --- a/go.sum +++ b/go.sum @@ -1,49 +1,44 @@ -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= -github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= -github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU= -github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc= -github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40= -github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= -github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= -github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= -github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0= -github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= -github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= -github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= -github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= -github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= -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= +charm.land/bubbles/v2 v2.1.0 h1:YSnNh5cPYlYjPxRrzs5VEn3vwhtEn3jVGRBT3M7/I0g= +charm.land/bubbles/v2 v2.1.0/go.mod h1:l97h4hym2hvWBVfmJDtrEHHCtkIKeTEb3TTJ4ZOB3wY= +charm.land/bubbletea/v2 v2.0.2 h1:4CRtRnuZOdFDTWSff9r8QFt/9+z6Emubz3aDMnf/dx0= +charm.land/bubbletea/v2 v2.0.2/go.mod h1:3LRff2U4WIYXy7MTxfbAQ+AdfM3D8Xuvz2wbsOD9OHQ= +charm.land/lipgloss/v2 v2.0.2 h1:xFolbF8JdpNkM2cEPTfXEcW1p6NRzOWTSamRfYEw8cs= +charm.land/lipgloss/v2 v2.0.2/go.mod h1:KjPle2Qd3YmvP1KL5OMHiHysGcNwq6u83MUjYkFvEkM= +github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o= +github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w= +github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY= +github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8= +github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 h1:eyFRbAmexyt43hVfeyBofiGSEmJ7krjLOYt/9CF5NKA= +github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8/go.mod h1:SQpCTRNBtzJkwku5ye4S3HEuthAlGy2n9VXZnWkEW98= +github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8= +github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ= +github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA= +github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I= +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= +github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= +github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM= +github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k= +github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= +github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -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-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= -github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= +github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w= +github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 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= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -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.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +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/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= diff --git a/providers.go b/providers.go index 67b5ec3..6107893 100644 --- a/providers.go +++ b/providers.go @@ -1,7 +1,7 @@ package treeview import ( - "github.com/charmbracelet/lipgloss" + "charm.land/lipgloss/v2" ) // NodeProvider lets you plug custom rendering logic into treeview. The generic diff --git a/providers_test.go b/providers_test.go index f558fe2..e1574ea 100644 --- a/providers_test.go +++ b/providers_test.go @@ -3,7 +3,7 @@ package treeview import ( "testing" - "github.com/charmbracelet/lipgloss" + "charm.land/lipgloss/v2" ) // Test helpers for creating test nodes diff --git a/renderer.go b/renderer.go index 2250210..3ad002a 100644 --- a/renderer.go +++ b/renderer.go @@ -7,7 +7,7 @@ import ( "sync" "unicode/utf8" - "github.com/charmbracelet/bubbles/viewport" + "charm.land/bubbles/v2/viewport" "github.com/mattn/go-runewidth" ) @@ -201,15 +201,16 @@ func renderTreeWithViewport[T any](ctx context.Context, tree *Tree[T], vp *viewp focusedLineIndex := findFocusedLineIndex(ctx, tree) // Auto-scroll to keep focused line visible BEFORE rendering - if focusedLineIndex >= 0 && vp.Height > 0 { + if focusedLineIndex >= 0 && vp.Height() > 0 { // If focused line is above viewport, scroll up - if focusedLineIndex < vp.YOffset { - vp.YOffset = focusedLineIndex - } else if focusedLineIndex >= vp.YOffset+vp.Height { + if focusedLineIndex < vp.YOffset() { + vp.SetYOffset(focusedLineIndex) + } else if focusedLineIndex >= vp.YOffset()+vp.Height() { // If focused line is below viewport, scroll down // Keep one line of context if possible - vp.YOffset = focusedLineIndex - vp.Height + 1 - vp.YOffset = max(vp.YOffset, 0) + offset := focusedLineIndex - vp.Height() + 1 + offset = max(offset, 0) + vp.SetYOffset(offset) } } @@ -258,12 +259,12 @@ func renderViewportOnly[T any](ctx context.Context, tree *Tree[T], vp *viewport. }() // Calculate the range of lines we need to render - startLine := vp.YOffset - endLine := vp.YOffset + vp.Height + startLine := vp.YOffset() + endLine := vp.YOffset() + vp.Height() // Track state for single-pass rendering currentLine := 0 - renderBuffer := make([]string, 0, vp.Height) // Pre-allocate for viewport height + renderBuffer := make([]string, 0, vp.Height()) // Pre-allocate for viewport height // ancestorIsLastChild tracks whether each ancestor (at each depth level) was the last // child among its siblings. This determines whether we draw a vertical continuation diff --git a/renderer_bench_test.go b/renderer_bench_test.go index a26f8bc..b266342 100644 --- a/renderer_bench_test.go +++ b/renderer_bench_test.go @@ -5,8 +5,8 @@ import ( "fmt" "testing" - "github.com/charmbracelet/bubbles/viewport" - "github.com/charmbracelet/lipgloss" + "charm.land/bubbles/v2/viewport" + "charm.land/lipgloss/v2" ) // benchProvider is a minimal provider to minimize noise in benchmarks. @@ -63,7 +63,7 @@ var benchSink string // global sink to avoid compiler elimination func benchmarkRenderer(b *testing.B, fn func(context.Context, *Tree[string], *viewport.Model) (string, error), tree *Tree[string]) { ctx := context.Background() - vp := viewport.New(100, 30) + vp := viewport.New(viewport.WithWidth(100), viewport.WithHeight(30)) b.ResetTimer() for i := 0; i < b.N; i++ { vpcopy := vp // copy so offsets reset diff --git a/renderer_test.go b/renderer_test.go index 6c181ab..7a5271f 100644 --- a/renderer_test.go +++ b/renderer_test.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - "github.com/charmbracelet/bubbles/viewport" - "github.com/charmbracelet/lipgloss" + "charm.land/bubbles/v2/viewport" + "charm.land/lipgloss/v2" "github.com/google/go-cmp/cmp" ) @@ -194,7 +194,6 @@ func createTestTree() (*Node[mockData], *Node[mockData], *Node[mockData], *Node[ } func TestRenderTree(t *testing.T) { - tests := []struct { name string tree *Tree[mockData] @@ -352,12 +351,9 @@ func TestRenderTreeWithViewport(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctx := context.Background() - vp := &viewport.Model{ - Height: test.viewportHeight, - Width: test.viewportWidth, - } + vp := viewport.New(viewport.WithHeight(test.viewportHeight), viewport.WithWidth(test.viewportWidth)) - got, err := renderTreeWithViewport(ctx, test.tree, vp) + got, err := renderTreeWithViewport(ctx, test.tree, &vp) if (err != nil) != test.wantErr { t.Errorf("renderTreeWithViewport() error = %v, wantErr %v", err, test.wantErr) diff --git a/tui.go b/tui.go index 07ac2e3..25abd0a 100644 --- a/tui.go +++ b/tui.go @@ -7,8 +7,8 @@ import ( "strings" "time" - "github.com/charmbracelet/bubbles/viewport" - "github.com/charmbracelet/bubbletea" + "charm.land/bubbles/v2/viewport" + tea "charm.land/bubbletea/v2" ) // TuiTreeModelOption represents a functional Option that configures a TuiTreeModel instance directly. @@ -134,7 +134,7 @@ type TuiTreeModel[T any] struct { // ) func NewTuiTreeModel[T any](tree *Tree[T], opts ...TuiTreeModelOption[T]) *TuiTreeModel[T] { // Initialize the TUI model with default components - vp := viewport.New(80, 24) + vp := viewport.New(viewport.WithWidth(80), viewport.WithHeight(24)) m := &TuiTreeModel[T]{ Tree: tree, keyMap: DefaultKeyMap(), @@ -382,11 +382,11 @@ func (m *TuiTreeModel[T]) Search(term string) ([]*Node[T], error) { } // View renders the tree plus an optional search bar and navigation legend. -func (m *TuiTreeModel[T]) View() string { +func (m *TuiTreeModel[T]) View() tea.View { // Render the tree result, err := renderTreeWithViewport(context.Background(), m.Tree, m.viewport) if err != nil { - return "Error rendering tree: " + err.Error() + return tea.NewView("Error rendering tree: " + err.Error()) } // Add search UI at the top if in search mode @@ -401,7 +401,7 @@ func (m *TuiTreeModel[T]) View() string { result += m.NavBar() } - return result + return tea.NewView(result) } // NavBar returns the navigation bar string that shows available keyboard commands. @@ -459,8 +459,8 @@ func (m *TuiTreeModel[T]) updateViewportDimensions() { viewHeight -= 2 } - m.viewport.Width = m.width - m.viewport.Height = viewHeight + m.viewport.SetWidth(m.width) + m.viewport.SetHeight(viewHeight) // Update tree truncation width to match viewport width m.mu.Lock() diff --git a/tui_test.go b/tui_test.go index 081ee5c..a47efc6 100644 --- a/tui_test.go +++ b/tui_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/charmbracelet/bubbletea" + tea "charm.land/bubbletea/v2" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" ) @@ -406,11 +406,11 @@ func TestUpdateViewportDimensions(t *testing.T) { model.showSearch = test.showSearch model.updateViewportDimensions() - if model.viewport.Width != test.wantWidth { + if model.viewport.Width() != test.wantWidth { t.Errorf("updateViewportDimensions() viewport width = %v, want %v", model.viewport.Width, test.wantWidth) } - if model.viewport.Height != test.wantHeight { + if model.viewport.Height() != test.wantHeight { t.Errorf("updateViewportDimensions() viewport height = %v, want %v", model.viewport.Height, test.wantHeight) } }) @@ -567,23 +567,23 @@ func TestHandleKeypress_Navigation(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - msg := tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune(test.key)} + msg := tea.KeyPressMsg{Text: test.key} switch test.key { case "esc": - msg = tea.KeyMsg{Type: tea.KeyEsc} + msg = tea.KeyPressMsg{Code: tea.KeyEsc} case "up": - msg = tea.KeyMsg{Type: tea.KeyUp} + msg = tea.KeyPressMsg{Code: tea.KeyUp} case "down": - msg = tea.KeyMsg{Type: tea.KeyDown} + msg = tea.KeyPressMsg{Code: tea.KeyDown} case "right": - msg = tea.KeyMsg{Type: tea.KeyRight} + msg = tea.KeyPressMsg{Code: tea.KeyRight} case "left": - msg = tea.KeyMsg{Type: tea.KeyLeft} + msg = tea.KeyPressMsg{Code: tea.KeyLeft} case "enter": - msg = tea.KeyMsg{Type: tea.KeyEnter} + msg = tea.KeyPressMsg{Code: tea.KeyEnter} case "ctrl+r": - msg = tea.KeyMsg{Type: tea.KeyCtrlR} + msg = tea.KeyPressMsg{Text: "ctrl+r"} } gotModel, gotCmd := model.handleKeypress(msg) @@ -678,20 +678,20 @@ func TestHandleKeypress_SearchMode(t *testing.T) { model.showSearch = true model.searchTerm = test.initialTerm - var msg tea.KeyMsg + var msg tea.KeyPressMsg switch test.key { case "enter": - msg = tea.KeyMsg{Type: tea.KeyEnter} + msg = tea.KeyPressMsg{Code: tea.KeyEnter} case "esc": - msg = tea.KeyMsg{Type: tea.KeyEsc} + msg = tea.KeyPressMsg{Code: tea.KeyEsc} case "backspace": - msg = tea.KeyMsg{Type: tea.KeyBackspace} + msg = tea.KeyPressMsg{Code: tea.KeyBackspace} case "delete": - msg = tea.KeyMsg{Type: tea.KeyDelete} + msg = tea.KeyPressMsg{Code: tea.KeyDelete} case "ctrl+r": - msg = tea.KeyMsg{Type: tea.KeyCtrlR} + msg = tea.KeyPressMsg{Text: "ctrl+r"} default: - msg = tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune(test.key)} + msg = tea.KeyPressMsg{Text: test.key} } gotModel, _ := model.handleKeypress(msg) @@ -850,7 +850,8 @@ func TestView(t *testing.T) { model.showSearch = test.showSearch model.searchTerm = test.searchTerm - got := model.View() + v := model.View() + got := v.Content for _, want := range test.wantContains { if !strings.Contains(got, want) {