From 22f8f7a0086bee4597786f3b75fff02cb09a2e12 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sun, 4 Oct 2026 10:12:33 +0100 Subject: [PATCH] refactor(tui): move to Bubble Tea, Lip Gloss and Bubbles v2 Switch imports to the charm.land v2 modules. Return tea.View from Model.View, set alt screen and window title there, use KeyPressMsg and the "space" key name, and call SetWidth on text inputs. --- go.mod | 18 +++++------ go.sum | 45 +++++++++++----------------- internal/tui/app.go | 64 ++++++++++++++++++++++------------------ internal/tui/app_test.go | 25 ++++++++++++++++ internal/tui/backups.go | 2 +- internal/tui/form.go | 8 ++--- internal/tui/groups.go | 8 ++--- internal/tui/list.go | 4 +-- internal/tui/presets.go | 8 ++--- internal/tui/styles.go | 2 +- 10 files changed, 101 insertions(+), 83 deletions(-) create mode 100644 internal/tui/app_test.go diff --git a/go.mod b/go.mod index b3e872a..5aec5d2 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/lukaszraczylo/lolcathost go 1.26.0 require ( - github.com/charmbracelet/bubbles v1.0.0 - github.com/charmbracelet/bubbletea v1.3.10 - github.com/charmbracelet/lipgloss v1.1.0 + charm.land/bubbles/v2 v2.2.1 + charm.land/bubbletea/v2 v2.0.10 + charm.land/lipgloss/v2 v2.0.6 github.com/fsnotify/fsnotify v1.10.1 github.com/lukaszraczylo/oss-telemetry v0.2.5 github.com/stretchr/testify v1.12.1 @@ -15,23 +15,19 @@ require ( require ( github.com/atotto/clipboard v0.1.4 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/charmbracelet/colorprofile v0.4.3 // indirect + github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886 // indirect github.com/charmbracelet/x/ansi v0.11.8 // indirect - github.com/charmbracelet/x/cellbuf v0.0.15 // 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/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/lucasb-eyer/go-colorful v1.4.1 // indirect - github.com/mattn/go-isatty v0.0.24 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.30 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // 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 v1.2.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/text v0.42.0 // indirect + golang.org/x/sync v0.23.0 // indirect ) diff --git a/go.sum b/go.sum index 1fe2f6c..962cc0d 100644 --- a/go.sum +++ b/go.sum @@ -1,49 +1,41 @@ +charm.land/bubbles/v2 v2.2.1 h1:Fq1+qm5hV6GkvzLQDhCBpXXE5tLgvh1PRriCLwSvIQU= +charm.land/bubbles/v2 v2.2.1/go.mod h1:wdMgn+sje1KNXdwFizIWjbf328fIUBxqEmJ/vYPo8yc= +charm.land/bubbletea/v2 v2.0.10 h1:oolvo20VBpI0PfqE7iFjkZ1bx0WpmXGfnKz5Yldjq5o= +charm.land/bubbletea/v2 v2.0.10/go.mod h1:QOatcnhOjYIfxzUSTz6raF7Ex4R/rIuHa3SnBdCCpMc= +charm.land/lipgloss/v2 v2.0.6 h1:EaGKeuA8FvF+v2BT5VmZd2LoYLaMZJXA5n34th8nCIQ= +charm.land/lipgloss/v2 v2.0.6/go.mod h1:ipDDJNSGa1hlwDtSfW1s2/xR8Vdhbut4PXh2zEKZd0Q= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -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/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= -github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= -github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc= -github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E= -github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= -github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= +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/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= -github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886 h1:rdnVWKgJpTVXKuKuJyxDJ+NFJdUaUqGvyGy61OcvlbA= +github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886/go.mod h1:nAw0d9PhFp1qdzi2xhQU5YOu5sVpDIHWlaW2Uz/bCro= github.com/charmbracelet/x/ansi v0.11.8 h1:JMFwp0CgDC2+jcOB162HH5k7I3FVbgFSMMYg7dSPBQQ= github.com/charmbracelet/x/ansi v0.11.8/go.mod h1:ZNN+3mXny/516oTQPLMPIBeSINvNJJQ8uQXDgbeJxY0= -github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI= -github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q= -github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= -github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +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/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/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/lucasb-eyer/go-colorful v1.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss= github.com/lucasb-eyer/go-colorful v1.4.1/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lukaszraczylo/oss-telemetry v0.2.5 h1:W6Qgwp7IY4yBRO361MmnEAmM59motdQpbAFAx8v6fJM= github.com/lukaszraczylo/oss-telemetry v0.2.5/go.mod h1:+Cn78qZo8rc3T9eZt0v3oICYRdd75wORtSidc8lNjDQ= -github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= -github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= -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.30 h1:+KUuiDA4fF0R1p5FeueHefjDm+GIM+kWfFnDjybOPgk= github.com/mattn/go-runewidth v0.0.30/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8= -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/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.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= @@ -52,11 +44,10 @@ github.com/xo/terminfo v1.2.0 h1:d0ZTOCpuGE0lwSAOs0zcJjwz3jWQyqcRt9XbGJpCOl4= github.com/xo/terminfo v1.2.0/go.mod h1:lGzkSo8Fe7IRh/w+Gqz7n5mDog4FVXCj3gy/DYTBqio= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= +golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= -golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= -golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/tui/app.go b/internal/tui/app.go index 6f4e6f4..59318cf 100644 --- a/internal/tui/app.go +++ b/internal/tui/app.go @@ -7,9 +7,9 @@ import ( "strings" "time" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" "github.com/lukaszraczylo/lolcathost/internal/client" "github.com/lukaszraczylo/lolcathost/internal/config" @@ -143,7 +143,7 @@ func NewModel(socketPath string) *Model { searchInput := textinput.New() searchInput.Placeholder = "Search..." searchInput.CharLimit = 100 - searchInput.Width = 50 + searchInput.SetWidth(50) return &Model{ client: client.New(socketPath), @@ -161,7 +161,6 @@ func NewModel(socketPath string) *Model { func (m *Model) Init() tea.Cmd { return tea.Batch( m.connect(), - tea.SetWindowTitle("lolcathost"), m.tick(), m.checkForUpdate(), ) @@ -330,9 +329,9 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if searchWidth > 60 { searchWidth = 60 } - m.searchInput.Width = searchWidth + m.searchInput.SetWidth(searchWidth) - case tea.KeyMsg: + case tea.KeyPressMsg: cmd := m.handleKey(msg) if cmd != nil { cmds = append(cmds, cmd) @@ -505,7 +504,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Batch(cmds...) } -func (m *Model) handleKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleKey(msg tea.KeyPressMsg) tea.Cmd { // Global keys switch msg.String() { case "ctrl+c": @@ -535,7 +534,7 @@ func (m *Model) handleKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handleListKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleListKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "q": return tea.Quit @@ -549,7 +548,7 @@ func (m *Model) handleListKey(msg tea.KeyMsg) tea.Cmd { m.list.MoveUp() case "down", "j": m.list.MoveDown() - case " ", "enter": + case "space", "enter": return m.toggleSelected() case "n": m.mode = ViewForm @@ -587,7 +586,7 @@ func (m *Model) handleListKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handleFormKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleFormKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "esc": m.mode = ViewList @@ -615,7 +614,7 @@ func (m *Model) handleFormKey(msg tea.KeyMsg) tea.Cmd { return m.form.Update(msg) } -func (m *Model) handlePresetKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handlePresetKey(msg tea.KeyPressMsg) tea.Cmd { // Handle based on preset picker mode switch m.presetPicker.Mode() { case PresetModeSelect: @@ -630,7 +629,7 @@ func (m *Model) handlePresetKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handlePresetSelectKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handlePresetSelectKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "esc", "q": m.mode = ViewList @@ -652,7 +651,7 @@ func (m *Model) handlePresetSelectKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handlePresetFormKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handlePresetFormKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "esc": m.presetPicker.CancelForm() @@ -690,7 +689,7 @@ func (m *Model) handlePresetFormKey(msg tea.KeyMsg) tea.Cmd { return m.presetPicker.Update(msg) } -func (m *Model) handlePresetPickerKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handlePresetPickerKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "esc": m.presetPicker.ClosePicker() @@ -700,13 +699,13 @@ func (m *Model) handlePresetPickerKey(msg tea.KeyMsg) tea.Cmd { m.presetPicker.PickerMoveUp() case "down", "j": m.presetPicker.PickerMoveDown() - case " ": + case "space": m.presetPicker.TogglePickerSelection() } return nil } -func (m *Model) handlePresetDeleteKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handlePresetDeleteKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "y", "Y": if preset := m.presetPicker.Selected(); preset != "" { @@ -719,7 +718,7 @@ func (m *Model) handlePresetDeleteKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handleGroupKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleGroupKey(msg tea.KeyPressMsg) tea.Cmd { // Handle based on group picker mode switch m.groupPicker.Mode() { case GroupModeSelect: @@ -732,7 +731,7 @@ func (m *Model) handleGroupKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handleGroupSelectKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleGroupSelectKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "esc", "q": m.mode = ViewList @@ -750,7 +749,7 @@ func (m *Model) handleGroupSelectKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handleGroupFormKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleGroupFormKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "esc": m.groupPicker.CancelForm() @@ -770,7 +769,7 @@ func (m *Model) handleGroupFormKey(msg tea.KeyMsg) tea.Cmd { return m.groupPicker.Update(msg) } -func (m *Model) handleGroupDeleteKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleGroupDeleteKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "y", "Y": if group := m.groupPicker.Selected(); group != "" { @@ -783,7 +782,7 @@ func (m *Model) handleGroupDeleteKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handleBackupKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleBackupKey(msg tea.KeyPressMsg) tea.Cmd { switch m.backupPicker.Mode() { case BackupModeSelect: return m.handleBackupSelectKey(msg) @@ -793,7 +792,7 @@ func (m *Model) handleBackupKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handleBackupSelectKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleBackupSelectKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "esc", "q": m.mode = ViewList @@ -821,7 +820,7 @@ func (m *Model) handleBackupSelectKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handleBackupRestoreKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleBackupRestoreKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "y", "Y": if backup := m.backupPicker.Selected(); backup != "" { @@ -834,7 +833,7 @@ func (m *Model) handleBackupRestoreKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handleHelpKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleHelpKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "esc", "q", "?": m.mode = ViewList @@ -842,7 +841,7 @@ func (m *Model) handleHelpKey(msg tea.KeyMsg) tea.Cmd { return nil } -func (m *Model) handleSearchKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleSearchKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "esc": m.mode = ViewList @@ -860,7 +859,7 @@ func (m *Model) handleSearchKey(msg tea.KeyMsg) tea.Cmd { return cmd } -func (m *Model) handleConfirmDeleteKey(msg tea.KeyMsg) tea.Cmd { +func (m *Model) handleConfirmDeleteKey(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "y", "Y": alias := m.pendingDeleteAlias @@ -900,7 +899,14 @@ func (m *Model) setSuccess(msg string) { } // View renders the UI. -func (m *Model) View() string { +func (m *Model) View() tea.View { + v := tea.NewView(m.render()) + v.AltScreen = true + v.WindowTitle = "lolcathost" + return v +} + +func (m *Model) render() string { var sb strings.Builder // Title with version @@ -1142,7 +1148,7 @@ func RunWithVersion(socketPath, version, githubOwner, githubRepo string) error { m.version = version m.githubOwner = githubOwner m.githubRepo = githubRepo - p := tea.NewProgram(m, tea.WithAltScreen()) + p := tea.NewProgram(m) _, err := p.Run() return err diff --git a/internal/tui/app_test.go b/internal/tui/app_test.go new file mode 100644 index 0000000..5476c4b --- /dev/null +++ b/internal/tui/app_test.go @@ -0,0 +1,25 @@ +package tui + +import ( + "testing" + + tea "charm.land/bubbletea/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestModel_View_UsesAltScreenAndTitle(t *testing.T) { + m := NewModel("/nonexistent.sock") + + v := m.View() + + assert.True(t, v.AltScreen) + assert.Equal(t, "lolcathost", v.WindowTitle) + assert.Contains(t, v.Content, "lolcathost - Host Management") +} + +func TestModel_SpaceKeyIsNamedSpace(t *testing.T) { + msg := tea.KeyPressMsg{Code: tea.KeySpace, Text: " "} + + require.Equal(t, "space", msg.String()) +} diff --git a/internal/tui/backups.go b/internal/tui/backups.go index 2c4b943..25e0f9d 100644 --- a/internal/tui/backups.go +++ b/internal/tui/backups.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/charmbracelet/lipgloss" + "charm.land/lipgloss/v2" "github.com/lukaszraczylo/lolcathost/internal/protocol" ) diff --git a/internal/tui/form.go b/internal/tui/form.go index b9b54f7..11660ba 100644 --- a/internal/tui/form.go +++ b/internal/tui/form.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" "github.com/lukaszraczylo/lolcathost/internal/config" ) @@ -127,14 +127,14 @@ func (f *Form) SetSize(width, height int) { inputWidth := min(50, width-10) for i := range f.fields { - f.fields[i].Width = inputWidth + f.fields[i].SetWidth(inputWidth) } } // Update handles input events. func (f *Form) Update(msg tea.Msg) tea.Cmd { switch msg := msg.(type) { - case tea.KeyMsg: + case tea.KeyPressMsg: // Handle group dropdown navigation if f.focus == FieldGroup { switch msg.String() { diff --git a/internal/tui/groups.go b/internal/tui/groups.go index 79da20f..0d98413 100644 --- a/internal/tui/groups.go +++ b/internal/tui/groups.go @@ -4,8 +4,8 @@ package tui import ( "strings" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" ) // GroupMode represents the group view mode. @@ -53,7 +53,7 @@ func (g *GroupPicker) SetGroups(groups []string) { func (g *GroupPicker) SetSize(width, height int) { g.width = width g.height = height - g.input.Width = min(50, width-10) + g.input.SetWidth(min(50, width-10)) } // MoveUp moves the cursor up. @@ -126,7 +126,7 @@ func (g *GroupPicker) CancelForm() { } // Update handles input events for form mode. -func (g *GroupPicker) Update(msg tea.KeyMsg) tea.Cmd { +func (g *GroupPicker) Update(msg tea.KeyPressMsg) tea.Cmd { var cmd tea.Cmd g.input, cmd = g.input.Update(msg) return cmd diff --git a/internal/tui/list.go b/internal/tui/list.go index 828398e..6125a39 100644 --- a/internal/tui/list.go +++ b/internal/tui/list.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/charmbracelet/lipgloss" - "github.com/charmbracelet/lipgloss/table" + "charm.land/lipgloss/v2" + "charm.land/lipgloss/v2/table" "github.com/lukaszraczylo/lolcathost/internal/protocol" ) diff --git a/internal/tui/presets.go b/internal/tui/presets.go index dda707d..58c96f7 100644 --- a/internal/tui/presets.go +++ b/internal/tui/presets.go @@ -4,8 +4,8 @@ package tui import ( "strings" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" "github.com/lukaszraczylo/lolcathost/internal/protocol" ) @@ -108,7 +108,7 @@ func (p *PresetPicker) SetSize(width, height int) { inputWidth := min(60, width-10) for i := range p.fields { - p.fields[i].Width = inputWidth + p.fields[i].SetWidth(inputWidth) } } @@ -223,7 +223,7 @@ func (p *PresetPicker) CancelForm() { } // Update handles input events for form mode. -func (p *PresetPicker) Update(msg tea.KeyMsg) tea.Cmd { +func (p *PresetPicker) Update(msg tea.KeyPressMsg) tea.Cmd { switch msg.String() { case "tab", "down": p.nextField() diff --git a/internal/tui/styles.go b/internal/tui/styles.go index 16df6d1..4b80ebf 100644 --- a/internal/tui/styles.go +++ b/internal/tui/styles.go @@ -4,7 +4,7 @@ package tui import ( "strings" - "github.com/charmbracelet/lipgloss" + "charm.land/lipgloss/v2" ) // Colors - matching kportal style, optimized for dark terminals