From d25735fb26329c7d620678b7401db033e36a870d Mon Sep 17 00:00:00 2001 From: darkhz Date: Tue, 7 Apr 2026 21:38:47 +0300 Subject: [PATCH 1/5] Migrate to BubbleTea v2 Upgrade all existing Charm libraries to v2 as well. Followed the migration guide specified at https://github.com/charmbracelet/bubbletea/blob/main/UPGRADE_GUIDE_V2.md. --- examples/basic/03-styling/main.go | 2 +- examples/basic/04-comparison-display/main.go | 2 +- .../basic/05-workflow-visualization/main.go | 2 +- examples/basic/07-builders-nested/main.go | 2 +- .../intermediate/01-keyboard-controls/main.go | 4 +- examples/intermediate/02-search/main.go | 4 +- examples/intermediate/03-viewport/main.go | 4 +- examples/intermediate/04-file-browser/main.go | 25 +++--- go.mod | 35 ++++----- go.sum | 77 +++++++++---------- providers.go | 2 +- providers_test.go | 2 +- renderer.go | 21 ++--- renderer_bench_test.go | 6 +- renderer_test.go | 12 +-- tui.go | 16 ++-- tui_test.go | 39 +++++----- 17 files changed, 126 insertions(+), 129 deletions(-) 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) { From dad6abd487914ffe86614df39233919b310a60f7 Mon Sep 17 00:00:00 2001 From: Shane Date: Wed, 22 Apr 2026 11:53:13 -0400 Subject: [PATCH 2/5] Update go deps --- go.mod | 18 +++++++++--------- go.sum | 36 ++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index 893cc85..31e4233 100644 --- a/go.mod +++ b/go.mod @@ -4,25 +4,25 @@ go 1.25.0 require ( charm.land/bubbles/v2 v2.1.0 - charm.land/bubbletea/v2 v2.0.2 - charm.land/lipgloss/v2 v2.0.2 + charm.land/bubbletea/v2 v2.0.6 + charm.land/lipgloss/v2 v2.0.3 github.com/google/go-cmp v0.7.0 - github.com/mattn/go-runewidth v0.0.21 + github.com/mattn/go-runewidth v0.0.23 ) require ( - 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/colorprofile v0.4.3 // indirect + github.com/charmbracelet/ultraviolet v0.0.0-20260422141423-a0f1f21775f7 // indirect + github.com/charmbracelet/x/ansi v0.11.7 // 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/lucasb-eyer/go-colorful v1.4.0 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.42.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect ) diff --git a/go.sum b/go.sum index cf498f6..c0fe102 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,17 @@ 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= +charm.land/bubbletea/v2 v2.0.6 h1:UHN/91OyuhaOFGSrBXQ/hMZD8IO1Uc4BvHlgHXL2WJo= +charm.land/bubbletea/v2 v2.0.6/go.mod h1:MH/D8ZLlN3op37vQvijKuU29g3rqTp+aQapURFonF9g= +charm.land/lipgloss/v2 v2.0.3 h1:yM2zJ4Cf5Y51b7RHIwioil4ApI/aypFXXVHSwlM6RzU= +charm.land/lipgloss/v2 v2.0.3/go.mod h1:7myLU9iG/3xluAWzpY/fSxYYHCgoKTie7laxk6ATwXA= 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/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-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-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= @@ -26,10 +26,10 @@ github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJ 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.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/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4= +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/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= @@ -38,7 +38,7 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= 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= +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= From 248a1345feeb5b91a9a20d409a3db3f351cb41f6 Mon Sep 17 00:00:00 2001 From: Shane Date: Wed, 22 Apr 2026 12:19:58 -0400 Subject: [PATCH 3/5] Fix broken tests and viewport height bug --- CHANGELOG.md | 10 +++++-- extensions/s3/go.mod | 32 ++++++++++---------- extensions/s3/go.sum | 70 +++++++++++++++++++++----------------------- renderer.go | 33 +++++++++++---------- renderer_test.go | 2 +- tui.go | 9 +++++- tui_test.go | 4 +-- 7 files changed, 85 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c730e39..6194a51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## [v2.0.0] - 2026-04-22 +### Updated +- Updated charm ecosystem libraries to v2. (darkhz) ### Added -- Golang CI lint config and workflow +- Golang CI lint config and workflow. +### Fixed +- Fix viewport autoscroll silently clamping to top because content length was set after the scroll offset. ## [v1.8.2] - 2025-09-20 ### Updated -- Expose Option for use in the sub S3 extension +- Expose Option for use in the sub S3 extension. ## [v1.8.1] - 2025-09-03 ### Fixed diff --git a/extensions/s3/go.mod b/extensions/s3/go.mod index fce612a..9409516 100644 --- a/extensions/s3/go.mod +++ b/extensions/s3/go.mod @@ -21,6 +21,9 @@ require ( atomicgo.dev/cursor v0.2.0 // indirect atomicgo.dev/keyboard v0.2.9 // indirect atomicgo.dev/schedule v0.1.0 // indirect + charm.land/bubbles/v2 v2.1.0 // indirect + charm.land/bubbletea/v2 v2.0.6 // indirect + charm.land/lipgloss/v2 v2.0.3 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.18.10 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.6 // indirect @@ -35,34 +38,29 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.29.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.2 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.38.2 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/bubbles v0.21.0 // indirect - github.com/charmbracelet/bubbletea v1.3.6 // indirect - github.com/charmbracelet/colorprofile v0.3.1 // indirect - github.com/charmbracelet/lipgloss v1.1.0 // 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/charmbracelet/colorprofile v0.4.3 // indirect + github.com/charmbracelet/ultraviolet v0.0.0-20260422141423-a0f1f21775f7 // indirect + github.com/charmbracelet/x/ansi v0.11.7 // 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/containerd/console v1.0.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/gookit/color v1.5.4 // indirect github.com/lithammer/fuzzysearch v1.1.8 // 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/mattn/go-runewidth v0.0.16 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/lucasb-eyer/go-colorful v1.4.0 // indirect + github.com/mattn/go-runewidth v0.0.23 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.16.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/stretchr/testify v1.10.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect golang.org/x/net v0.40.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect golang.org/x/term v0.32.0 // indirect golang.org/x/text v0.27.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect diff --git a/extensions/s3/go.sum b/extensions/s3/go.sum index 6080858..0e5e17f 100644 --- a/extensions/s3/go.sum +++ b/extensions/s3/go.sum @@ -6,6 +6,12 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= +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.6 h1:UHN/91OyuhaOFGSrBXQ/hMZD8IO1Uc4BvHlgHXL2WJo= +charm.land/bubbletea/v2 v2.0.6/go.mod h1:MH/D8ZLlN3op37vQvijKuU29g3rqTp+aQapURFonF9g= +charm.land/lipgloss/v2 v2.0.3 h1:yM2zJ4Cf5Y51b7RHIwioil4ApI/aypFXXVHSwlM6RzU= +charm.land/lipgloss/v2 v2.0.3/go.mod h1:7myLU9iG/3xluAWzpY/fSxYYHCgoKTie7laxk6ATwXA= github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= @@ -52,22 +58,26 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.38.2 h1:YZPjhyaGzhDQEvsffDEcpycq49nl github.com/aws/aws-sdk-go-v2/service/sts v1.38.2/go.mod h1:2dIN8qhQfv37BdUYGgEC8Q3tteM3zFxTI1MLO2O3J3c= github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= -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/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.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-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-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/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= @@ -75,8 +85,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/failsafe-go/failsafe-go v0.7.0 h1:LRz2oQD59ElNRuCDGr7RkLhUDczWampYHYRVPZPsswo= github.com/failsafe-go/failsafe-go v0.7.0/go.mod h1:qkYFeHT1f1V/LRd995f++Ypn90nAMcAuBM/bAYQePvY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -95,21 +103,13 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= -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/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4= +github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -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/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw= +github.com/mattn/go-runewidth v0.0.23/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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -156,23 +156,21 @@ golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +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.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/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/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/renderer.go b/renderer.go index 3ad002a..7232b38 100644 --- a/renderer.go +++ b/renderer.go @@ -197,8 +197,13 @@ func renderTree[T any](ctx context.Context, tree *Tree[T]) (string, int, error) // renderTreeWithViewport combines tree rendering with viewport scrolling. // It automatically positions the viewport to keep the focused line visible. func renderTreeWithViewport[T any](ctx context.Context, tree *Tree[T], vp *viewport.Model) (string, error) { - // First, find the focused line position to determine if we need to adjust the viewport - focusedLineIndex := findFocusedLineIndex(ctx, tree) + // First, find the focused line position and count total visible lines + focusedLineIndex, totalVisibleLines := findFocusedLineIndex(ctx, tree) + + // Update viewport's understanding of total content BEFORE adjusting the + // offset. SetYOffset clamps to maxYOffset, which depends on the current + // content length, so the content must be sized first for scrolling to work. + vp.SetContent(strings.Repeat("\n", max(0, totalVisibleLines-1))) // Auto-scroll to keep focused line visible BEFORE rendering if focusedLineIndex >= 0 && vp.Height() > 0 { @@ -215,37 +220,33 @@ func renderTreeWithViewport[T any](ctx context.Context, tree *Tree[T], vp *viewp } // Now render only the visible portion with the correct viewport offset - content, totalLines, err := renderViewportOnly(ctx, tree, vp) - - // Update viewport's understanding of total content for scrollbar - // We use empty lines to set the height without the memory cost of actual content - vp.SetContent(strings.Repeat("\n", max(0, totalLines-1))) + content, _, err := renderViewportOnly(ctx, tree, vp) // Return just the visible content return content, err } -// findFocusedLineIndex quickly scans through the tree to find the focused line's position. -// This is a lightweight operation that doesn't render anything. -func findFocusedLineIndex[T any](ctx context.Context, tree *Tree[T]) int { +// findFocusedLineIndex scans the tree once, returning the focused line index +// (or -1 if none) and the total count of visible lines. +func findFocusedLineIndex[T any](ctx context.Context, tree *Tree[T]) (int, int) { + focusedIdx := -1 lineIdx := 0 for info, err := range tree.AllVisible(ctx) { if err != nil { - return -1 + return -1, lineIdx } - if tree.IsFocused(info.Node.ID()) { - return lineIdx + if focusedIdx == -1 && tree.IsFocused(info.Node.ID()) { + focusedIdx = lineIdx } lineIdx++ - // Check for context cancellation periodically if lineIdx%100 == 0 { if ctx.Err() != nil { - return -1 + return focusedIdx, lineIdx } } } - return -1 + return focusedIdx, lineIdx } // renderViewportOnly efficiently renders only the visible lines in the viewport diff --git a/renderer_test.go b/renderer_test.go index 7a5271f..a3e4773 100644 --- a/renderer_test.go +++ b/renderer_test.go @@ -134,7 +134,7 @@ func TestRenderNode(t *testing.T) { return lipgloss.NewStyle() }, }, - want: "└── 📁 focused", + want: "\x1b[1m└── 📁 focused\x1b[m", wantErr: false, }, { diff --git a/tui.go b/tui.go index 25abd0a..a59b3fb 100644 --- a/tui.go +++ b/tui.go @@ -233,7 +233,14 @@ func (m *TuiTreeModel[T]) handleKeypress(msg tea.KeyMsg) (tea.Model, tea.Cmd) { return m, nil } - // Add printable characters to search + // Add printable characters to search. Prefer msg.Text so keys whose + // String() returns a name (e.g. "space") still contribute the literal + // character typed by the user. + if text := msg.Key().Text; len(text) == 1 && text >= " " && text <= "~" { + m.searchTerm += text + _, _ = m.Search(m.searchTerm) + return m, nil + } if len(key) == 1 && key >= " " && key <= "~" { m.searchTerm += key _, _ = m.Search(m.searchTerm) diff --git a/tui_test.go b/tui_test.go index a47efc6..d7f0e0f 100644 --- a/tui_test.go +++ b/tui_test.go @@ -407,11 +407,11 @@ func TestUpdateViewportDimensions(t *testing.T) { model.updateViewportDimensions() if model.viewport.Width() != test.wantWidth { - t.Errorf("updateViewportDimensions() viewport width = %v, want %v", model.viewport.Width, test.wantWidth) + t.Errorf("updateViewportDimensions() viewport width = %v, want %v", model.viewport.Width(), test.wantWidth) } if model.viewport.Height() != test.wantHeight { - t.Errorf("updateViewportDimensions() viewport height = %v, want %v", model.viewport.Height, test.wantHeight) + t.Errorf("updateViewportDimensions() viewport height = %v, want %v", model.viewport.Height(), test.wantHeight) } }) } From a02f302151f2d27ace05eac47bbed50ce00e75ed Mon Sep 17 00:00:00 2001 From: Shane Date: Wed, 22 Apr 2026 12:31:43 -0400 Subject: [PATCH 4/5] Update golang ci version --- .github/workflows/golangci-lint.yml | 2 +- CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 11f777b..708fe56 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -21,4 +21,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v8 with: - version: v2.1 \ No newline at end of file + version: v2.5 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6194a51..32410fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v2.0.0] - 2026-04-22 ### Updated - Updated charm ecosystem libraries to v2. (darkhz) +- golangci version to be go 1.25 compatible. ### Added - Golang CI lint config and workflow. ### Fixed From d4249fa08e8e113527b31039213c20452f9e9e3c Mon Sep 17 00:00:00 2001 From: Shane Date: Wed, 22 Apr 2026 12:56:29 -0400 Subject: [PATCH 5/5] Add new option to restore old TUI behavior for standalone apps --- CHANGELOG.md | 1 + .../intermediate/01-keyboard-controls/main.go | 1 + examples/intermediate/02-search/main.go | 1 + examples/intermediate/03-viewport/main.go | 1 + tui.go | 16 ++++++++++++++-- 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32410fc..7a2f09e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - golangci version to be go 1.25 compatible. ### Added - Golang CI lint config and workflow. +- `WithTuiAltScreen` option to restore old alt screen behavior. ### Fixed - Fix viewport autoscroll silently clamping to top because content length was set after the scroll offset. diff --git a/examples/intermediate/01-keyboard-controls/main.go b/examples/intermediate/01-keyboard-controls/main.go index 5f6abd7..275a6cb 100644 --- a/examples/intermediate/01-keyboard-controls/main.go +++ b/examples/intermediate/01-keyboard-controls/main.go @@ -23,6 +23,7 @@ func main() { tree, treeview.WithTuiWidth[string](80), treeview.WithTuiHeight[string](25), + treeview.WithTuiAltScreen[string](true), ) // Create the program with navigation help bar diff --git a/examples/intermediate/02-search/main.go b/examples/intermediate/02-search/main.go index fe5bd4b..35c3583 100644 --- a/examples/intermediate/02-search/main.go +++ b/examples/intermediate/02-search/main.go @@ -38,6 +38,7 @@ func main() { tree, treeview.WithTuiWidth[Product](80), treeview.WithTuiHeight[Product](25), + treeview.WithTuiAltScreen[Product](true), ) // Run the program diff --git a/examples/intermediate/03-viewport/main.go b/examples/intermediate/03-viewport/main.go index facc5ef..3b8ba3d 100644 --- a/examples/intermediate/03-viewport/main.go +++ b/examples/intermediate/03-viewport/main.go @@ -23,6 +23,7 @@ func main() { tree, treeview.WithTuiWidth[*FileData](80), treeview.WithTuiHeight[*FileData](20), + treeview.WithTuiAltScreen[*FileData](true), ) // Expand root to show content immediately diff --git a/tui.go b/tui.go index a59b3fb..78deb36 100644 --- a/tui.go +++ b/tui.go @@ -49,6 +49,12 @@ func WithTuiDisableNavBar[T any](disable bool) TuiTreeModelOption[T] { return func(m *TuiTreeModel[T]) { m.disableNavBar = disable } } +// WithTuiAltScreen renders the TUI in the terminal's alternate screen buffer, +// restoring the previous terminal contents when the program exits. +func WithTuiAltScreen[T any](enable bool) TuiTreeModelOption[T] { + return func(m *TuiTreeModel[T]) { m.altScreen = enable } +} + // KeyMap groups key bindings for the interactive TUI. Provide your own via // WithTuiKeyMap if you need to accommodate non-US layouts or match existing shortcuts. type KeyMap struct { @@ -115,6 +121,7 @@ type TuiTreeModel[T any] struct { searchTimeout time.Duration disableNavBar bool + altScreen bool } // NewTuiTreeModel creates an interactive Bubble Tea TUI model using functional options. @@ -390,10 +397,14 @@ 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() tea.View { + view := tea.NewView("") + view.AltScreen = m.altScreen + // Render the tree result, err := renderTreeWithViewport(context.Background(), m.Tree, m.viewport) if err != nil { - return tea.NewView("Error rendering tree: " + err.Error()) + view.SetContent("Error rendering tree: " + err.Error()) + return view } // Add search UI at the top if in search mode @@ -408,7 +419,8 @@ func (m *TuiTreeModel[T]) View() tea.View { result += m.NavBar() } - return tea.NewView(result) + view.SetContent(result) + return view } // NavBar returns the navigation bar string that shows available keyboard commands.