Merge pull request #678 from charmbracelet/v2-exp

Ayman Bagabas created

Use the new v2 stack

Change summary

cmd/soft/browse/browse.go                  |  21 +-
cmd/soft/hook/hook.go                      |   2 
cmd/soft/main.go                           |   6 
cmd/soft/serve/server.go                   |   2 
go.mod                                     |  41 ++---
go.sum                                     |  71 ++++-----
pkg/backend/auth.go                        |   2 
pkg/backend/backend.go                     |   2 
pkg/backend/webhooks.go                    |   2 
pkg/cron/cron.go                           |   2 
pkg/cron/cron_test.go                      |   2 
pkg/daemon/daemon.go                       |   2 
pkg/db/db.go                               |   2 
pkg/db/logger.go                           |   2 
pkg/db/migrate/0003_migrate_lfs_objects.go |   2 
pkg/db/migrate/migrate.go                  |   2 
pkg/git/git.go                             |   2 
pkg/git/lfs.go                             |   2 
pkg/git/lfs_auth.go                        |   2 
pkg/git/lfs_log.go                         |   2 
pkg/git/service.go                         |   2 
pkg/hooks/gen.go                           |   2 
pkg/jobs/mirror.go                         |   2 
pkg/lfs/basic_transfer.go                  |   2 
pkg/lfs/http_client.go                     |   2 
pkg/log/log.go                             |   2 
pkg/ssh/cmd/blob.go                        |  14 +
pkg/ssh/cmd/commit.go                      |   7 
pkg/ssh/cmd/git.go                         |   2 
pkg/ssh/cmd/repo.go                        |   7 
pkg/ssh/cmd/tag.go                         |   2 
pkg/ssh/cmd/token.go                       |   2 
pkg/ssh/cmd/webhooks.go                    |   2 
pkg/ssh/middleware.go                      |  15 -
pkg/ssh/session.go                         |  22 +--
pkg/ssh/session_test.go                    |   9 
pkg/ssh/ssh.go                             |  21 +-
pkg/ssh/ui.go                              |  66 ++++-----
pkg/store/database/database.go             |   2 
pkg/ui/common/common.go                    |  26 +--
pkg/ui/common/component.go                 |   5 
pkg/ui/common/error.go                     |   2 
pkg/ui/common/format.go                    |   2 
pkg/ui/common/style.go                     |  23 +--
pkg/ui/components/code/code.go             |  42 +----
pkg/ui/components/footer/footer.go         |   8 
pkg/ui/components/header/header.go         |   2 
pkg/ui/components/selector/selector.go     |  22 +-
pkg/ui/components/statusbar/statusbar.go   |  10 
pkg/ui/components/tabs/tabs.go             |  15 -
pkg/ui/components/viewport/viewport.go     |  40 +----
pkg/ui/keymap/keymap.go                    |   2 
pkg/ui/pages/repo/files.go                 |   8 
pkg/ui/pages/repo/filesitem.go             |  12 
pkg/ui/pages/repo/log.go                   |  16 +-
pkg/ui/pages/repo/logitem.go               |  10 
pkg/ui/pages/repo/readme.go                |   6 
pkg/ui/pages/repo/refs.go                  |   8 
pkg/ui/pages/repo/refsitem.go              |  10 
pkg/ui/pages/repo/repo.go                  |  29 +--
pkg/ui/pages/repo/stash.go                 |  10 
pkg/ui/pages/repo/stashitem.go             |   8 
pkg/ui/pages/selection/item.go             |  16 +-
pkg/ui/pages/selection/selection.go        |  20 +-
pkg/ui/styles/styles.go                    | 168 ++++++++++++-----------
pkg/web/auth.go                            |   2 
pkg/web/context.go                         |   2 
pkg/web/git.go                             |   2 
pkg/web/git_lfs.go                         |   2 
pkg/web/goget.go                           |   2 
pkg/web/http.go                            |   2 
pkg/web/logging.go                         |   2 
pkg/web/server.go                          |   2 
73 files changed, 397 insertions(+), 493 deletions(-)

Detailed changes

cmd/soft/browse/browse.go 🔗

@@ -5,9 +5,9 @@ import (
 	"path/filepath"
 	"time"
 
-	"github.com/charmbracelet/bubbles/key"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/key"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
@@ -39,9 +39,8 @@ var Command = &cobra.Command{
 
 		// Bubble Tea uses Termenv default output so we have to use the same
 		// thing here.
-		output := lipgloss.DefaultRenderer()
 		ctx := cmd.Context()
-		c := common.NewCommon(ctx, output, 0, 0)
+		c := common.NewCommon(ctx, 0, 0)
 		c.HideCloneCmd = true
 		comps := []common.TabComponent{
 			repo.NewReadme(c),
@@ -152,7 +151,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	switch msg := msg.(type) {
 	case tea.WindowSizeMsg:
 		m.SetSize(msg.Width, msg.Height)
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch {
 		case key.Matches(msg, m.common.KeyMap.Back) && m.error != nil:
 			m.error = nil
@@ -166,12 +165,10 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			m.common.Zone.Close()
 			return m, tea.Quit
 		}
-	case tea.MouseMsg:
-		if msg.Action != tea.MouseActionPress {
-			break
-		}
-		switch msg.Button {
-		case tea.MouseButtonLeft:
+	case tea.MouseClickMsg:
+		mouse := msg.Mouse()
+		switch mouse.Button {
+		case tea.MouseLeft:
 			switch {
 			case m.common.Zone.Get("footer").InBounds(msg):
 				cmds = append(cmds, footer.ToggleFooterCmd)

cmd/soft/hook/hook.go 🔗

@@ -12,7 +12,7 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/cmd"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"

cmd/soft/main.go 🔗

@@ -7,7 +7,8 @@ import (
 	"runtime/debug"
 	"strconv"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/colorprofile"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/cmd/soft/admin"
 	"github.com/charmbracelet/soft-serve/cmd/soft/browse"
 	"github.com/charmbracelet/soft-serve/cmd/soft/hook"
@@ -18,7 +19,6 @@ import (
 	"github.com/charmbracelet/soft-serve/pkg/version"
 	mcobra "github.com/muesli/mango-cobra"
 	"github.com/muesli/roff"
-	"github.com/muesli/termenv"
 	"github.com/spf13/cobra"
 	"go.uber.org/automaxprocs/maxprocs"
 )
@@ -67,7 +67,7 @@ var (
 
 func init() {
 	if noColor, _ := strconv.ParseBool(os.Getenv("SOFT_SERVE_NO_COLOR")); noColor {
-		common.DefaultColorProfile = termenv.Ascii
+		common.DefaultColorProfile = colorprofile.NoTTY
 	}
 
 	rootCmd.AddCommand(

cmd/soft/serve/server.go 🔗

@@ -6,7 +6,7 @@ import (
 	"fmt"
 	"net/http"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"

go.mod 🔗

@@ -2,31 +2,33 @@ module github.com/charmbracelet/soft-serve
 
 go 1.23.0
 
-toolchain go1.24.1
-
 require (
-	github.com/charmbracelet/bubbles v0.21.0
-	github.com/charmbracelet/bubbletea v1.3.5
-	github.com/charmbracelet/glamour v0.10.0
-	github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
-	github.com/charmbracelet/wish v1.4.7
 	github.com/dustin/go-humanize v1.0.1
 	github.com/go-git/go-git/v5 v5.16.2
 	github.com/matryer/is v1.4.1
 	github.com/muesli/reflow v0.3.0
-	github.com/muesli/termenv v0.16.0
-	github.com/sergi/go-diff v1.4.0
 )
 
 require (
 	github.com/alecthomas/chroma/v2 v2.18.0
+	github.com/aymanbagabas/bubblezone/v2 v2.0.0-20250319214444-bb232f16d5e3
+)
+
+require (
 	github.com/aymanbagabas/git-module v1.8.4-0.20231101154130-8d27204ac6d2
 	github.com/caarlos0/duration v0.0.0-20240108180406-5d492514f3c7
-	github.com/caarlos0/env/v11 v11.3.1
+	github.com/caarlos0/env/v11 v11.2.2
+	github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250603123720-56bbc4a1ba66
+	github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3.0.20250617194119-3f1d09f7d826
+	github.com/charmbracelet/colorprofile v0.3.1
 	github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20240708204110-bacbfdb68d92
+	github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe
 	github.com/charmbracelet/keygen v0.5.3
-	github.com/charmbracelet/log v0.4.2
-	github.com/charmbracelet/ssh v0.0.0-20250213143314-8712ec3ff3ef
+	github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.2.0.20250703152125-8e1c474f8a71
+	github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706
+	github.com/charmbracelet/ssh v0.0.0-20250128164007-98fd5ae11894
+	github.com/charmbracelet/wish/v2 v2.0.0-20250505151211-5996fc7c1f33
+	github.com/charmbracelet/x/ansi v0.9.3
 	github.com/go-jose/go-jose/v3 v3.0.4
 	github.com/gobwas/glob v0.2.3
 	github.com/golang-jwt/jwt/v5 v5.2.2
@@ -37,12 +39,12 @@ require (
 	github.com/hashicorp/golang-lru/v2 v2.0.7
 	github.com/jmoiron/sqlx v1.4.0
 	github.com/lib/pq v1.10.9
-	github.com/lrstanley/bubblezone v1.0.0
 	github.com/muesli/mango-cobra v1.2.0
 	github.com/muesli/roff v0.1.0
 	github.com/prometheus/client_golang v1.22.0
 	github.com/robfig/cron/v3 v3.0.1
 	github.com/rogpeppe/go-internal v1.14.1
+	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
 	github.com/spf13/cobra v1.9.1
 	go.uber.org/automaxprocs v1.6.0
 	golang.org/x/crypto v0.39.0
@@ -54,23 +56,19 @@ require (
 require (
 	github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
 	github.com/atotto/clipboard v0.1.4 // indirect
-	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
 	github.com/aymerick/douceur v0.2.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
-	github.com/charmbracelet/colorprofile v0.3.1 // indirect
-	github.com/charmbracelet/x/ansi v0.8.0 // indirect
-	github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
+	github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa // indirect
 	github.com/charmbracelet/x/conpty v0.1.0 // indirect
 	github.com/charmbracelet/x/errors v0.0.0-20240725160154-f9f6568126ec // indirect
 	github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
-	github.com/charmbracelet/x/input v0.3.4 // indirect
+	github.com/charmbracelet/x/input v0.3.5-0.20250509021451-13796e822d86 // indirect
 	github.com/charmbracelet/x/term v0.2.1 // indirect
 	github.com/charmbracelet/x/termios v0.1.0 // indirect
-	github.com/charmbracelet/x/windows v0.2.0 // indirect
+	github.com/charmbracelet/x/windows v0.2.1 // indirect
 	github.com/creack/pty v1.1.21 // indirect
 	github.com/dlclark/regexp2 v1.11.5 // indirect
-	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
 	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
@@ -79,11 +77,9 @@ require (
 	github.com/inconshreveable/mousetrap v1.1.0 // 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/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 // indirect
 	github.com/microcosm-cc/bluemonday v1.0.27 // indirect
-	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
 	github.com/muesli/cancelreader v0.2.2 // indirect
 	github.com/muesli/mango v0.2.0 // indirect
 	github.com/muesli/mango-pflag v0.1.0 // indirect
@@ -102,7 +98,6 @@ require (
 	golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
 	golang.org/x/net v0.40.0 // indirect
 	golang.org/x/sys v0.33.0 // indirect
-	golang.org/x/term v0.32.0 // indirect
 	golang.org/x/text v0.26.0 // indirect
 	golang.org/x/tools v0.33.0 // indirect
 	google.golang.org/protobuf v1.36.5 // indirect

go.sum 🔗

@@ -10,10 +10,10 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
 github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
 github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
+github.com/aymanbagabas/bubblezone/v2 v2.0.0-20250319214444-bb232f16d5e3 h1:1z2ihw0YUYUhNmRaavyXvG9ZU/9Tj0vj6sA3z5DFIJ8=
+github.com/aymanbagabas/bubblezone/v2 v2.0.0-20250319214444-bb232f16d5e3/go.mod h1:sJwqZoo/BSKSizmr0pSJ758RuRsnjlkrOaxPtwlWtOs=
 github.com/aymanbagabas/git-module v1.8.4-0.20231101154130-8d27204ac6d2 h1:3w5KT+shE3hzWhORGiu2liVjEoaCEXm9uZP47+Gw4So=
 github.com/aymanbagabas/git-module v1.8.4-0.20231101154130-8d27204ac6d2/go.mod h1:d4gQ7/3/S2sPq4NnKdtAgUOVr6XtLpWFtxyVV5/+76U=
-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.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
 github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
 github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
@@ -22,50 +22,50 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/caarlos0/duration v0.0.0-20240108180406-5d492514f3c7 h1:kJP/C2eL9DCKrCOlX6lPVmAUAb6U4u9xllgws1kP9ds=
 github.com/caarlos0/duration v0.0.0-20240108180406-5d492514f3c7/go.mod h1:mSkwb/eZEwOJJJ4tqAKiuhLIPe0e9+FKhlU0oMCpbf8=
-github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
-github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
+github.com/caarlos0/env/v11 v11.2.2 h1:95fApNrUyueipoZN/EhA8mMxiNxrBwDa+oAZrMWl3Kg=
+github.com/caarlos0/env/v11 v11.2.2/go.mod h1:JBfcdeQiBoI3Zh1QRAWfe+tpiNTmDtcCj/hHHHMx0vc=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-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.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc=
-github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54=
+github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250603123720-56bbc4a1ba66 h1:LDUnLzW0DZ4riy4juBAXJWeE94vvVChgZwzhZL/4wuk=
+github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250603123720-56bbc4a1ba66/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
+github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3.0.20250617194119-3f1d09f7d826 h1:pQxCWMojVjHePqGzWsANhplourZYsD6AuR6eR2Hi5yc=
+github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3.0.20250617194119-3f1d09f7d826/go.mod h1:bfzSaUDPMKrWDjD6wo/ato9lfDdEX83rZgwcXHYWJ98=
 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/git-lfs-transfer v0.1.1-0.20240708204110-bacbfdb68d92 h1:KtQlsiHfY3K4AoIEh0yUE/wCLHteZ9EzV1hKmx+p7U8=
 github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20240708204110-bacbfdb68d92/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits=
-github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V6kXldcY=
-github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk=
+github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe h1:i6ce4CcAlPpTj2ER69m1DBeLZ3RRcHnKExuwhKa3GfY=
+github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe/go.mod h1:p3Q+aN4eQKeM5jhrmXPMgPrlKbmc59rWSnMsSA3udhk=
 github.com/charmbracelet/keygen v0.5.3 h1:2MSDC62OUbDy6VmjIE2jM24LuXUvKywLCmaJDmr/Z/4=
 github.com/charmbracelet/keygen v0.5.3/go.mod h1:TcpNoMAO5GSmhx3SgcEMqCrtn8BahKhB8AlwnLjRUpk=
-github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
-github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
-github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
-github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
-github.com/charmbracelet/ssh v0.0.0-20250213143314-8712ec3ff3ef h1:dNZwn4is5svUd+sQEGsrXtp7VwD2ipYaCkKMzcpAEIE=
-github.com/charmbracelet/ssh v0.0.0-20250213143314-8712ec3ff3ef/go.mod h1:hg+I6gvlMl16nS9ZzQNgBIrrCasGwEw0QiLsDcP01Ko=
-github.com/charmbracelet/wish v1.4.7 h1:O+jdLac3s6GaqkOHHSwezejNK04vl6VjO1A+hl8J8Yc=
-github.com/charmbracelet/wish v1.4.7/go.mod h1:OBZ8vC62JC5cvbxJLh+bIWtG7Ctmct+ewziuUWK+G14=
-github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
-github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
-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/lipgloss/v2 v2.0.0-beta.2.0.20250703152125-8e1c474f8a71 h1:X0tsNa2UHCKNw+illiavosasVzqioRo32SRV35iwr2I=
+github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.2.0.20250703152125-8e1c474f8a71/go.mod h1:EJWvaCrhOhNGVZMvcjc0yVryl4qqpMs8tz0r9WyEkdQ=
+github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706 h1:WkwO6Ks3mSIGnGuSdKl9qDSyfbYK50z2wc2gGMggegE=
+github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706/go.mod h1:mjJGp00cxcfvD5xdCa+bso251Jt4owrQvuimJtVmEmM=
+github.com/charmbracelet/ssh v0.0.0-20250128164007-98fd5ae11894 h1:Ffon9TbltLGBsT6XE//YvNuu4OAaThXioqalhH11xEw=
+github.com/charmbracelet/ssh v0.0.0-20250128164007-98fd5ae11894/go.mod h1:hg+I6gvlMl16nS9ZzQNgBIrrCasGwEw0QiLsDcP01Ko=
+github.com/charmbracelet/wish/v2 v2.0.0-20250505151211-5996fc7c1f33 h1:/TlxP6Lw9Z7NJCQ7TSbuvfO3/AL0UXGDPwH1zO9pV8U=
+github.com/charmbracelet/wish/v2 v2.0.0-20250505151211-5996fc7c1f33/go.mod h1:Tssk2d4IKqV3tNiPgwrYSvJMQu+nN9J4JlGmYaXpiR4=
+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.14-0.20250516160309-24eee56f89fa h1:lphz0Z3rsiOtMYiz8axkT24i9yFiueDhJbzyNUADmME=
+github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa/go.mod h1:xBlh2Yi3DL3zy/2n15kITpg0YZardf/aa/hgUaIM6Rk=
 github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=
 github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
 github.com/charmbracelet/x/errors v0.0.0-20240725160154-f9f6568126ec h1:O8c7pFFK0imuHH5JBqv5smlbVoFn4CZKGjtvCQKu1WE=
 github.com/charmbracelet/x/errors v0.0.0-20240725160154-f9f6568126ec/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
-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-20250207160936-21c02780d27a h1:FsHEJ52OC4VuTzU8t+n5frMjLvpYWEznSr/u8tnkCYw=
+github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
 github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
 github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
-github.com/charmbracelet/x/input v0.3.4 h1:Mujmnv/4DaitU0p+kIsrlfZl/UlmeLKw1wAP3e1fMN0=
-github.com/charmbracelet/x/input v0.3.4/go.mod h1:JI8RcvdZWQIhn09VzeK3hdp4lTz7+yhiEdpEQtZN+2c=
+github.com/charmbracelet/x/input v0.3.5-0.20250509021451-13796e822d86 h1:BxAEmOBIDajkgao3EsbBxKQCYvgYPGdT62WASLvtf4Y=
+github.com/charmbracelet/x/input v0.3.5-0.20250509021451-13796e822d86/go.mod h1:62Rp/6EtTxoeJDSdtpA3tJp3y3ZRpsiekBSje+K8htA=
 github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
 github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
 github.com/charmbracelet/x/termios v0.1.0 h1:y4rjAHeFksBAfGbkRDmVinMg7x7DELIGAFbdNvxg97k=
 github.com/charmbracelet/x/termios v0.1.0/go.mod h1:H/EVv/KRnrYjz+fCYa9bsKdqF3S8ouDK0AZEbG7r+/U=
-github.com/charmbracelet/x/windows v0.2.0 h1:ilXA1GJjTNkgOm94CLPeSz7rar54jtFatdmoiONPuEw=
-github.com/charmbracelet/x/windows v0.2.0/go.mod h1:ZibNFR49ZFqCXgP76sYanisxRyC+EYrBE7TTknD8s1s=
+github.com/charmbracelet/x/windows v0.2.1 h1:3x7vnbpQrjpuq/4L+I4gNsG5htYoCiA5oe9hLjAij5I=
+github.com/charmbracelet/x/windows v0.2.1/go.mod h1:ptZp16h40gDYqs5TSawSVW+yiLB13j4kSMA0lSCHL0M=
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
 github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
 github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
@@ -76,8 +76,6 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
 github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
@@ -133,16 +131,12 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
 github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lrstanley/bubblezone v1.0.0 h1:bIpUaBilD42rAQwlg/4u5aTqVAt6DSRKYZuSdmkr8UA=
-github.com/lrstanley/bubblezone v1.0.0/go.mod h1:kcTekA8HE/0Ll2bWzqHlhA2c513KDNLW7uDfDP4Mly8=
 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/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
 github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
 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.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
 github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@@ -153,8 +147,6 @@ github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 h1:YocNLcTBdEd
 github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
 github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
 github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
-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/mango v0.2.0 h1:iNNc0c5VLQ6fsMgAqGQofByNUBH2Q2nEbD6TaI+5yyQ=
@@ -167,8 +159,6 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
 github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
 github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
-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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
@@ -200,8 +190,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
 github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
-github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
-github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
 github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
 github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
 github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
@@ -254,7 +244,6 @@ golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 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-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-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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

pkg/backend/auth.go 🔗

@@ -5,7 +5,7 @@ import (
 	"crypto/sha256"
 	"encoding/hex"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"golang.org/x/crypto/bcrypt"
 )
 

pkg/backend/backend.go 🔗

@@ -3,7 +3,7 @@ package backend
 import (
 	"context"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/db"
 	"github.com/charmbracelet/soft-serve/pkg/store"

pkg/backend/webhooks.go 🔗

@@ -4,7 +4,7 @@ import (
 	"context"
 	"encoding/json"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/db"
 	"github.com/charmbracelet/soft-serve/pkg/db/models"
 	"github.com/charmbracelet/soft-serve/pkg/proto"

pkg/cron/cron.go 🔗

@@ -4,7 +4,7 @@ import (
 	"context"
 	"time"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/robfig/cron/v3"
 )
 

pkg/cron/cron_test.go 🔗

@@ -6,7 +6,7 @@ import (
 	"fmt"
 	"testing"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 )
 
 func TestCronLogger(t *testing.T) {

pkg/daemon/daemon.go 🔗

@@ -12,7 +12,7 @@ import (
 	"sync/atomic"
 	"time"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/access"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"

pkg/db/db.go 🔗

@@ -6,7 +6,7 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/jmoiron/sqlx"
 	_ "github.com/lib/pq"  // postgres driver

pkg/db/logger.go 🔗

@@ -5,7 +5,7 @@ import (
 	"database/sql"
 	"strings"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/jmoiron/sqlx"
 )
 

pkg/db/migrate/0003_migrate_lfs_objects.go 🔗

@@ -6,7 +6,7 @@ import (
 	"path/filepath"
 	"strconv"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/db"
 	"github.com/charmbracelet/soft-serve/pkg/db/models"

pkg/db/migrate/migrate.go 🔗

@@ -6,7 +6,7 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/db"
 )
 

pkg/git/git.go 🔗

@@ -9,7 +9,7 @@ import (
 	"strings"
 
 	gitm "github.com/aymanbagabas/git-module"
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/go-git/go-git/v5/plumbing/format/pktline"
 )

pkg/git/lfs.go 🔗

@@ -12,7 +12,7 @@ import (
 	"time"
 
 	"github.com/charmbracelet/git-lfs-transfer/transfer"
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/db"
 	"github.com/charmbracelet/soft-serve/pkg/db/models"

pkg/git/lfs_auth.go 🔗

@@ -7,7 +7,7 @@ import (
 	"fmt"
 	"time"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/jwk"
 	"github.com/charmbracelet/soft-serve/pkg/lfs"

pkg/git/lfs_log.go 🔗

@@ -2,7 +2,7 @@ package git
 
 import (
 	"github.com/charmbracelet/git-lfs-transfer/transfer"
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 )
 
 type lfsLogger struct {

pkg/git/service.go 🔗

@@ -10,7 +10,7 @@ import (
 	"strings"
 	"sync"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 )
 
 // Service is a Git daemon service.

pkg/hooks/gen.go 🔗

@@ -7,7 +7,7 @@ import (
 	"path/filepath"
 	"text/template"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/utils"
 )

pkg/jobs/mirror.go 🔗

@@ -7,7 +7,7 @@ import (
 	"runtime"
 	"strings"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"

pkg/lfs/basic_transfer.go 🔗

@@ -9,7 +9,7 @@ import (
 	"io"
 	"net/http"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 )
 
 // BasicTransferAdapter implements the "basic" adapter

pkg/lfs/http_client.go 🔗

@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"net/http"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 )
 
 // httpClient is a Git LFS client to communicate with a LFS source API.

pkg/log/log.go 🔗

@@ -5,7 +5,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 )
 

pkg/ssh/cmd/blob.go 🔗

@@ -2,8 +2,8 @@ package cmd
 
 import (
 	"fmt"
+	"os"
 
-	"github.com/charmbracelet/lipgloss"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
@@ -12,12 +12,16 @@ import (
 )
 
 // blobCommand returns a command that prints the contents of a file.
-func blobCommand(renderer *lipgloss.Renderer) *cobra.Command {
+func blobCommand() *cobra.Command {
 	var linenumber bool
 	var color bool
 	var raw bool
+	var noColor bool
+	if testrun, ok := os.LookupEnv("SOFT_SERVE_NO_COLOR"); ok && testrun == "1" {
+		noColor = true
+	}
 
-	styles := styles.DefaultStyles(renderer)
+	styles := styles.DefaultStyles()
 	cmd := &cobra.Command{
 		Use:               "blob REPOSITORY [REFERENCE] [PATH]",
 		Aliases:           []string{"cat", "show"},
@@ -84,7 +88,7 @@ func blobCommand(renderer *lipgloss.Renderer) *cobra.Command {
 					return fmt.Errorf("binary file: use --raw to print")
 				}
 			} else {
-				if color {
+				if color && !noColor {
 					c, err = common.FormatHighlight(fp, c)
 					if err != nil {
 						return err
@@ -92,7 +96,7 @@ func blobCommand(renderer *lipgloss.Renderer) *cobra.Command {
 				}
 
 				if linenumber {
-					c, _ = common.FormatLineNumber(styles, c, color)
+					c, _ = common.FormatLineNumber(styles, c, color && !noColor)
 				}
 
 				cmd.Println(c)

pkg/ssh/cmd/commit.go 🔗

@@ -5,8 +5,7 @@ import (
 	"strings"
 	"time"
 
-	gansi "github.com/charmbracelet/glamour/ansi"
-	"github.com/charmbracelet/lipgloss"
+	gansi "github.com/charmbracelet/glamour/v2/ansi"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
@@ -15,7 +14,7 @@ import (
 )
 
 // commitCommand returns a command that prints the contents of a commit.
-func commitCommand(renderer *lipgloss.Renderer) *cobra.Command {
+func commitCommand() *cobra.Command {
 	var color bool
 	var patchOnly bool
 
@@ -55,7 +54,7 @@ func commitCommand(renderer *lipgloss.Renderer) *cobra.Command {
 				return err
 			}
 
-			commonStyle := styles.DefaultStyles(renderer)
+			commonStyle := styles.DefaultStyles()
 			style := commonStyle.Log
 
 			s := strings.Builder{}

pkg/ssh/cmd/git.go 🔗

@@ -6,7 +6,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/access"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"

pkg/ssh/cmd/repo.go 🔗

@@ -4,14 +4,13 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/charmbracelet/lipgloss"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/spf13/cobra"
 )
 
 // RepoCommand returns a command for managing repositories.
-func RepoCommand(renderer *lipgloss.Renderer) *cobra.Command {
+func RepoCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:     "repo",
 		Aliases: []string{"repos", "repository", "repositories"},
@@ -19,10 +18,10 @@ func RepoCommand(renderer *lipgloss.Renderer) *cobra.Command {
 	}
 
 	cmd.AddCommand(
-		blobCommand(renderer),
+		blobCommand(),
 		branchCommand(),
 		collabCommand(),
-		commitCommand(renderer),
+		commitCommand(),
 		createCommand(),
 		deleteCommand(),
 		descriptionCommand(),

pkg/ssh/cmd/tag.go 🔗

@@ -3,7 +3,7 @@ package cmd
 import (
 	"strings"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/proto"

pkg/ssh/cmd/token.go 🔗

@@ -6,7 +6,7 @@ import (
 	"time"
 
 	"github.com/caarlos0/duration"
-	"github.com/charmbracelet/lipgloss/table"
+	"github.com/charmbracelet/lipgloss/v2/table"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/dustin/go-humanize"

pkg/ssh/cmd/webhooks.go 🔗

@@ -5,7 +5,7 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/charmbracelet/lipgloss/table"
+	"github.com/charmbracelet/lipgloss/v2/table"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/webhook"
 	"github.com/dustin/go-humanize"

pkg/ssh/middleware.go 🔗

@@ -2,10 +2,9 @@ package ssh
 
 import (
 	"fmt"
-	"os"
 	"time"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/db"
@@ -14,9 +13,7 @@ import (
 	"github.com/charmbracelet/soft-serve/pkg/sshutils"
 	"github.com/charmbracelet/soft-serve/pkg/store"
 	"github.com/charmbracelet/ssh"
-	"github.com/charmbracelet/wish"
-	bm "github.com/charmbracelet/wish/bubbletea"
-	"github.com/muesli/termenv"
+	"github.com/charmbracelet/wish/v2"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
 	"github.com/spf13/cobra"
@@ -91,12 +88,6 @@ func CommandMiddleware(sh ssh.Handler) ssh.Handler {
 		ctx := s.Context()
 		cfg := config.FromContext(ctx)
 
-		renderer := bm.MakeRenderer(s)
-		if testrun, ok := os.LookupEnv("SOFT_SERVE_NO_COLOR"); ok && testrun == "1" {
-			// Disable colors when running tests.
-			renderer.SetColorProfile(termenv.Ascii)
-		}
-
 		args := s.Command()
 		cliCommandCounter.WithLabelValues(cmd.CommandName(args)).Inc()
 		rootCmd := &cobra.Command{
@@ -111,7 +102,7 @@ func CommandMiddleware(sh ssh.Handler) ssh.Handler {
 			cmd.GitUploadPackCommand(),
 			cmd.GitUploadArchiveCommand(),
 			cmd.GitReceivePackCommand(),
-			cmd.RepoCommand(renderer),
+			cmd.RepoCommand(),
 			cmd.SettingsCommand(),
 			cmd.UserCommand(),
 			cmd.InfoCommand(),

pkg/ssh/session.go 🔗

@@ -1,19 +1,17 @@
 package ssh
 
 import (
-	"os"
 	"time"
 
-	tea "github.com/charmbracelet/bubbletea"
+	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/charmbracelet/soft-serve/pkg/access"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 	"github.com/charmbracelet/ssh"
-	"github.com/charmbracelet/wish"
-	bm "github.com/charmbracelet/wish/bubbletea"
-	"github.com/muesli/termenv"
+	"github.com/charmbracelet/wish/v2"
+	bm "github.com/charmbracelet/wish/v2/bubbletea"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
 )
@@ -54,22 +52,18 @@ func SessionHandler(s ssh.Session) *tea.Program {
 		}
 	}
 
-	renderer := bm.MakeRenderer(s)
-	if testrun, ok := os.LookupEnv("SOFT_SERVE_NO_COLOR"); ok && testrun == "1" {
-		// Disable colors when running tests.
-		renderer.SetColorProfile(termenv.Ascii)
-	}
-
-	c := common.NewCommon(ctx, renderer, pty.Window.Width, pty.Window.Height)
-	c.SetValue(common.ConfigKey, cfg)
-	m := NewUI(c, initialRepo)
 	opts := bm.MakeOptions(s)
 	opts = append(opts,
 		tea.WithAltScreen(),
 		tea.WithoutCatchPanics(),
 		tea.WithMouseCellMotion(),
 		tea.WithContext(ctx),
+		tea.WithColorProfile(common.DefaultColorProfile),
 	)
+
+	c := common.NewCommon(ctx, pty.Window.Width, pty.Window.Height)
+	c.SetValue(common.ConfigKey, cfg)
+	m := NewUI(c, initialRepo)
 	p := tea.NewProgram(m, opts...)
 
 	tuiSessionCounter.WithLabelValues(initialRepo, pty.Term).Inc()

pkg/ssh/session_test.go 🔗

@@ -8,7 +8,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/db"
@@ -17,10 +17,9 @@ import (
 	"github.com/charmbracelet/soft-serve/pkg/store/database"
 	"github.com/charmbracelet/soft-serve/pkg/test"
 	"github.com/charmbracelet/ssh"
-	bm "github.com/charmbracelet/wish/bubbletea"
-	"github.com/charmbracelet/wish/testsession"
+	bm "github.com/charmbracelet/wish/v2/bubbletea"
+	"github.com/charmbracelet/wish/v2/testsession"
 	"github.com/matryer/is"
-	"github.com/muesli/termenv"
 	gossh "golang.org/x/crypto/ssh"
 	_ "modernc.org/sqlite" // sqlite driver
 )
@@ -79,7 +78,7 @@ func setup(tb testing.TB) (*gossh.Session, func() error) {
 	be := backend.New(ctx, cfg, dbx, dbstore)
 	ctx = backend.WithContext(ctx, be)
 	return testsession.New(tb, &ssh.Server{
-		Handler: ContextMiddleware(cfg, dbx, dbstore, be, log.Default())(bm.MiddlewareWithProgramHandler(SessionHandler, termenv.ANSI256)(func(s ssh.Session) {
+		Handler: ContextMiddleware(cfg, dbx, dbstore, be, log.Default())(bm.MiddlewareWithProgramHandler(SessionHandler)(func(s ssh.Session) {
 			_, _, active := s.Pty()
 			if !active {
 				os.Exit(1)

pkg/ssh/ssh.go 🔗

@@ -5,22 +5,20 @@ import (
 	"fmt"
 	"net"
 	"os"
-	"runtime"
 	"strconv"
 	"time"
 
 	"github.com/charmbracelet/keygen"
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/db"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/store"
-	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 	"github.com/charmbracelet/ssh"
-	"github.com/charmbracelet/wish"
-	bm "github.com/charmbracelet/wish/bubbletea"
-	rm "github.com/charmbracelet/wish/recover"
+	"github.com/charmbracelet/wish/v2"
+	bm "github.com/charmbracelet/wish/v2/bubbletea"
+	rm "github.com/charmbracelet/wish/v2/recover"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
 	gossh "golang.org/x/crypto/ssh"
@@ -71,7 +69,7 @@ func NewSSHServer(ctx context.Context) (*SSHServer, error) {
 		rm.MiddlewareWithLogger(
 			logger,
 			// BubbleTea middleware.
-			bm.MiddlewareWithProgramHandler(SessionHandler, common.DefaultColorProfile),
+			bm.MiddlewareWithProgramHandler(SessionHandler),
 			// CLI middleware.
 			CommandMiddleware,
 			// Logging middleware.
@@ -93,11 +91,10 @@ func NewSSHServer(ctx context.Context) (*SSHServer, error) {
 		wish.WithHostKeyPath(cfg.SSH.KeyPath),
 		wish.WithMiddleware(mw...),
 	}
-	if runtime.GOOS == "windows" {
-		opts = append(opts, ssh.EmulatePty())
-	} else {
-		opts = append(opts, ssh.AllocatePty())
-	}
+
+	// TODO: Support a real PTY in future version.
+	opts = append(opts, ssh.EmulatePty())
+
 	s.srv, err = wish.NewServer(opts...)
 	if err != nil {
 		return nil, err

pkg/ssh/ui.go 🔗

@@ -3,10 +3,10 @@ package ssh
 import (
 	"errors"
 
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/list"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/list"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
@@ -179,40 +179,34 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 				cmds = append(cmds, cmd)
 			}
 		}
-	case tea.KeyMsg, tea.MouseMsg:
-		switch msg := msg.(type) {
-		case tea.KeyMsg:
+	case tea.KeyPressMsg:
+		switch {
+		case key.Matches(msg, ui.common.KeyMap.Back) && ui.error != nil:
+			ui.error = nil
+			ui.state = readyState
+			// Always show the footer on error.
+			ui.showFooter = ui.footer.ShowAll()
+		case key.Matches(msg, ui.common.KeyMap.Help):
+			cmds = append(cmds, footer.ToggleFooterCmd)
+		case key.Matches(msg, ui.common.KeyMap.Quit):
+			if !ui.IsFiltering() {
+				// Stop bubblezone background workers.
+				ui.common.Zone.Close()
+				return ui, tea.Quit
+			}
+		case ui.activePage == repoPage &&
+			ui.pages[ui.activePage].(*repo.Repo).Path() == "" &&
+			key.Matches(msg, ui.common.KeyMap.Back):
+			ui.activePage = selectionPage
+			// Always show the footer on selection page.
+			ui.showFooter = true
+		}
+	case tea.MouseClickMsg:
+		switch msg.Mouse().Button {
+		case tea.MouseLeft:
 			switch {
-			case key.Matches(msg, ui.common.KeyMap.Back) && ui.error != nil:
-				ui.error = nil
-				ui.state = readyState
-				// Always show the footer on error.
-				ui.showFooter = ui.footer.ShowAll()
-			case key.Matches(msg, ui.common.KeyMap.Help):
+			case ui.common.Zone.Get("footer").InBounds(msg):
 				cmds = append(cmds, footer.ToggleFooterCmd)
-			case key.Matches(msg, ui.common.KeyMap.Quit):
-				if !ui.IsFiltering() {
-					// Stop bubblezone background workers.
-					ui.common.Zone.Close()
-					return ui, tea.Quit
-				}
-			case ui.activePage == repoPage &&
-				ui.pages[ui.activePage].(*repo.Repo).Path() == "" &&
-				key.Matches(msg, ui.common.KeyMap.Back):
-				ui.activePage = selectionPage
-				// Always show the footer on selection page.
-				ui.showFooter = true
-			}
-		case tea.MouseMsg:
-			if msg.Action != tea.MouseActionPress {
-				break
-			}
-			switch msg.Button {
-			case tea.MouseButtonLeft:
-				switch {
-				case ui.common.Zone.Get("footer").InBounds(msg):
-					cmds = append(cmds, footer.ToggleFooterCmd)
-				}
 			}
 		}
 	case footer.ToggleFooterMsg:

pkg/store/database/database.go 🔗

@@ -3,7 +3,7 @@ package database
 import (
 	"context"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/db"
 	"github.com/charmbracelet/soft-serve/pkg/store"

pkg/ui/common/common.go 🔗

@@ -5,16 +5,14 @@ import (
 	"fmt"
 
 	"github.com/alecthomas/chroma/v2/lexers"
-	"github.com/charmbracelet/lipgloss"
-	"github.com/charmbracelet/log"
+	zone "github.com/aymanbagabas/bubblezone/v2"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/ui/keymap"
 	"github.com/charmbracelet/soft-serve/pkg/ui/styles"
 	"github.com/charmbracelet/ssh"
-	zone "github.com/lrstanley/bubblezone"
-	"github.com/muesli/termenv"
 )
 
 type contextKey struct {
@@ -34,27 +32,23 @@ type Common struct {
 	Styles        *styles.Styles
 	KeyMap        *keymap.KeyMap
 	Zone          *zone.Manager
-	Renderer      *lipgloss.Renderer
-	Output        *termenv.Output
 	Logger        *log.Logger
 	HideCloneCmd  bool
 }
 
 // NewCommon returns a new Common struct.
-func NewCommon(ctx context.Context, out *lipgloss.Renderer, width, height int) Common {
+func NewCommon(ctx context.Context, width, height int) Common {
 	if ctx == nil {
 		ctx = context.TODO()
 	}
 	return Common{
-		ctx:      ctx,
-		Width:    width,
-		Height:   height,
-		Renderer: out,
-		Output:   out.Output(),
-		Styles:   styles.DefaultStyles(out),
-		KeyMap:   keymap.DefaultKeyMap(),
-		Zone:     zone.New(),
-		Logger:   log.FromContext(ctx).WithPrefix("ui"),
+		ctx:    ctx,
+		Width:  width,
+		Height: height,
+		Styles: styles.DefaultStyles(),
+		KeyMap: keymap.DefaultKeyMap(),
+		Zone:   zone.New(),
+		Logger: log.FromContext(ctx).WithPrefix("ui"),
 	}
 }
 

pkg/ui/common/component.go 🔗

@@ -1,13 +1,14 @@
 package common
 
 import (
-	"github.com/charmbracelet/bubbles/help"
-	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/bubbles/v2/help"
+	tea "github.com/charmbracelet/bubbletea/v2"
 )
 
 // Component represents a Bubble Tea model that implements a SetSize function.
 type Component interface {
 	tea.Model
+	tea.ViewModel
 	help.KeyMap
 	SetSize(width, height int)
 }

pkg/ui/common/error.go 🔗

@@ -3,7 +3,7 @@ package common
 import (
 	"errors"
 
-	tea "github.com/charmbracelet/bubbletea"
+	tea "github.com/charmbracelet/bubbletea/v2"
 )
 
 // ErrMissingRepo indicates that the requested repository could not be found.

pkg/ui/common/format.go 🔗

@@ -6,7 +6,7 @@ import (
 	"strings"
 
 	"github.com/alecthomas/chroma/v2/lexers"
-	gansi "github.com/charmbracelet/glamour/ansi"
+	gansi "github.com/charmbracelet/glamour/v2/ansi"
 	"github.com/charmbracelet/soft-serve/pkg/ui/styles"
 )
 

pkg/ui/common/style.go 🔗

@@ -1,13 +1,13 @@
 package common
 
 import (
-	gansi "github.com/charmbracelet/glamour/ansi"
-	"github.com/charmbracelet/glamour/styles"
-	"github.com/muesli/termenv"
+	"github.com/charmbracelet/colorprofile"
+	gansi "github.com/charmbracelet/glamour/v2/ansi"
+	"github.com/charmbracelet/glamour/v2/styles"
 )
 
 // DefaultColorProfile is the default color profile used by the SSH server.
-var DefaultColorProfile = termenv.ANSI256
+var DefaultColorProfile = colorprofile.ANSI256
 
 func strptr(s string) *string {
 	return &s
@@ -17,29 +17,20 @@ func strptr(s string) *string {
 func StyleConfig() gansi.StyleConfig {
 	noColor := strptr("")
 	s := styles.DarkStyleConfig
-	s.H1.BackgroundColor = noColor
-	s.H1.Prefix = "# "
-	s.H1.Suffix = ""
-	s.H1.Color = strptr("39")
-	s.Document.StylePrimitive.Color = noColor
-	s.CodeBlock.Chroma.Text.Color = noColor
-	s.CodeBlock.Chroma.Name.Color = noColor
 	// This fixes an issue with the default style config. For example
 	// highlighting empty spaces with red in Dockerfile type.
 	s.CodeBlock.Chroma.Error.BackgroundColor = noColor
 	return s
 }
 
-// StyleRenderer returns a new Glamour renderer with the DefaultColorProfile.
+// StyleRenderer returns a new Glamour renderer.
 func StyleRenderer() gansi.RenderContext {
 	return StyleRendererWithStyles(StyleConfig())
 }
 
-// StyleRendererWithStyles returns a new Glamour renderer with the
-// DefaultColorProfile and styles.
+// StyleRendererWithStyles returns a new Glamour renderer.
 func StyleRendererWithStyles(styles gansi.StyleConfig) gansi.RenderContext {
 	return gansi.NewRenderContext(gansi.Options{
-		ColorProfile: DefaultColorProfile,
-		Styles:       styles,
+		Styles: styles,
 	})
 }

pkg/ui/components/code/code.go 🔗

@@ -6,13 +6,12 @@ import (
 	"sync"
 
 	"github.com/alecthomas/chroma/v2/lexers"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/glamour"
-	gansi "github.com/charmbracelet/glamour/ansi"
-	"github.com/charmbracelet/lipgloss"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/glamour/v2"
+	gansi "github.com/charmbracelet/glamour/v2/ansi"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 	vp "github.com/charmbracelet/soft-serve/pkg/ui/components/viewport"
-	"github.com/muesli/termenv"
 )
 
 const (
@@ -77,7 +76,11 @@ func (r *Code) SetSideNote(s string) tea.Cmd {
 
 // Init implements tea.Model.
 func (r *Code) Init() tea.Cmd {
-	w := r.common.Width
+	// XXX: We probably won't need the GetHorizontalFrameSize margin
+	// subtraction if we get the new viewport soft wrapping to play nicely with
+	// Glamour. This also introduces a bug where when it soft wraps, the
+	// viewport scrolls left/right for 2 columns on each side of the screen.
+	w := r.common.Width - r.common.Styles.App.GetHorizontalFrameSize()
 	content := r.content
 	if content == "" {
 		r.Viewport.Model.SetContent(r.NoContentStyle.String())
@@ -110,7 +113,7 @@ func (r *Code) Init() tea.Cmd {
 
 	if r.sidenote != "" {
 		lines := strings.Split(r.sidenote, "\n")
-		sideNoteWidth := int(math.Ceil(float64(r.Model.Width) * r.SideNotePercent))
+		sideNoteWidth := int(math.Ceil(float64(r.Model.Width()) * r.SideNotePercent))
 		for i, l := range lines {
 			lines[i] = common.TruncateString(l, sideNoteWidth)
 		}
@@ -121,7 +124,7 @@ func (r *Code) Init() tea.Cmd {
 	// https://github.com/muesli/reflow/issues/43
 	//
 	// TODO: solve this upstream in Glamour/Reflow.
-	content = r.common.Renderer.NewStyle().Width(w).Render(content)
+	content = lipgloss.NewStyle().Width(w).Render(content)
 
 	r.Viewport.Model.SetContent(content)
 
@@ -169,26 +172,6 @@ func (r *Code) HalfViewUp() {
 	r.Viewport.HalfViewUp()
 }
 
-// ViewUp moves the viewport up by a page.
-func (r *Code) ViewUp() []string {
-	return r.Viewport.ViewUp()
-}
-
-// ViewDown moves the viewport down by a page.
-func (r *Code) ViewDown() []string {
-	return r.Viewport.ViewDown()
-}
-
-// LineUp moves the viewport up by the given number of lines.
-func (r *Code) LineUp(n int) []string {
-	return r.Viewport.LineUp(n)
-}
-
-// LineDown moves the viewport down by the given number of lines.
-func (r *Code) LineDown(n int) []string {
-	return r.Viewport.LineDown(n)
-}
-
 // ScrollPercent returns the viewport's scroll percentage.
 func (r *Code) ScrollPercent() float64 {
 	return r.Viewport.ScrollPercent()
@@ -244,8 +227,7 @@ func (r *Code) renderFile(path, content string) (string, error) {
 		var m uint
 		st.CodeBlock.Margin = &m
 		rc = gansi.NewRenderContext(gansi.Options{
-			ColorProfile: termenv.TrueColor,
-			Styles:       st,
+			Styles: st,
 		})
 	}
 	err := formatter.Render(&s, rc)

pkg/ui/components/footer/footer.go 🔗

@@ -1,10 +1,10 @@
 package footer
 
 import (
-	"github.com/charmbracelet/bubbles/help"
-	"github.com/charmbracelet/bubbles/key"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/help"
+	"github.com/charmbracelet/bubbles/v2/key"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 )
 

pkg/ui/components/header/header.go 🔗

@@ -3,7 +3,7 @@ package header
 import (
 	"strings"
 
-	tea "github.com/charmbracelet/bubbletea"
+	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 )
 

pkg/ui/components/selector/selector.go 🔗

@@ -3,9 +3,9 @@ package selector
 import (
 	"sync"
 
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/list"
-	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/list"
+	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 )
 
@@ -230,16 +230,14 @@ func (s *Selector) Init() tea.Cmd {
 func (s *Selector) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	cmds := make([]tea.Cmd, 0)
 	switch msg := msg.(type) {
-	case tea.MouseMsg:
-		if msg.Action != tea.MouseActionPress {
-			break
-		}
-		switch msg.Button {
-		case tea.MouseButtonWheelUp:
+	case tea.MouseClickMsg:
+		m := msg.Mouse()
+		switch m.Button {
+		case tea.MouseWheelUp:
 			s.CursorUp()
-		case tea.MouseButtonWheelDown:
+		case tea.MouseWheelDown:
 			s.CursorDown()
-		case tea.MouseButtonLeft:
+		case tea.MouseLeft:
 			curIdx := s.Index()
 			for i, item := range s.Items() {
 				item, _ := item.(IdentifiableItem)
@@ -254,7 +252,7 @@ func (s *Selector) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 				}
 			}
 		}
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		filterState := s.FilterState()
 		switch {
 		case key.Matches(msg, s.common.KeyMap.Help):

pkg/ui/components/statusbar/statusbar.go 🔗

@@ -1,10 +1,10 @@
 package statusbar
 
 import (
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
-	"github.com/muesli/reflow/truncate"
+	"github.com/charmbracelet/x/ansi"
 )
 
 // Model is a status bar model.
@@ -75,12 +75,12 @@ func (s *Model) View() string {
 	}
 	branch := st.StatusBarBranch.Render(s.extra)
 	maxWidth := s.common.Width - w(key) - w(info) - w(branch) - w(help)
-	v := truncate.StringWithTail(s.value, uint(maxWidth-st.StatusBarValue.GetHorizontalFrameSize()), "…") //nolint:gosec
+	v := ansi.Truncate(s.value, maxWidth-st.StatusBarValue.GetHorizontalFrameSize(), "…")
 	value := st.StatusBarValue.
 		Width(maxWidth).
 		Render(v)
 
-	return s.common.Renderer.NewStyle().MaxWidth(s.common.Width).
+	return lipgloss.NewStyle().MaxWidth(s.common.Width).
 		Render(
 			lipgloss.JoinHorizontal(lipgloss.Top,
 				key,

pkg/ui/components/tabs/tabs.go 🔗

@@ -3,8 +3,8 @@ package tabs
 import (
 	"strings"
 
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 )
 
@@ -54,7 +54,7 @@ func (t *Tabs) Init() tea.Cmd {
 func (t *Tabs) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	cmds := make([]tea.Cmd, 0)
 	switch msg := msg.(type) {
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch msg.String() {
 		case "tab":
 			t.activeTab = (t.activeTab + 1) % len(t.tabs)
@@ -63,12 +63,9 @@ func (t *Tabs) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			t.activeTab = (t.activeTab - 1 + len(t.tabs)) % len(t.tabs)
 			cmds = append(cmds, t.activeTabCmd)
 		}
-	case tea.MouseMsg:
-		if msg.Action != tea.MouseActionPress {
-			break
-		}
+	case tea.MouseClickMsg:
 		switch msg.Button {
-		case tea.MouseButtonLeft:
+		case tea.MouseLeft:
 			for i, tab := range t.tabs {
 				if t.common.Zone.Get(tab).InBounds(msg) {
 					t.activeTab = i
@@ -109,7 +106,7 @@ func (t *Tabs) View() string {
 			s.WriteString(sep.String())
 		}
 	}
-	return t.common.Renderer.NewStyle().
+	return lipgloss.NewStyle().
 		MaxWidth(t.common.Width).
 		Render(s.String())
 }

pkg/ui/components/viewport/viewport.go 🔗

@@ -1,9 +1,9 @@
 package viewport
 
 import (
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/viewport"
-	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/viewport"
+	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 )
 
@@ -15,7 +15,9 @@ type Viewport struct {
 
 // New returns a new Viewport.
 func New(c common.Common) *Viewport {
-	vp := viewport.New(c.Width, c.Height)
+	vp := viewport.New()
+	vp.SetWidth(c.Width)
+	vp.SetHeight(c.Height)
 	vp.MouseWheelEnabled = true
 	return &Viewport{
 		common: c,
@@ -26,8 +28,8 @@ func New(c common.Common) *Viewport {
 // SetSize implements common.Component.
 func (v *Viewport) SetSize(width, height int) {
 	v.common.SetSize(width, height)
-	v.Model.Width = width
-	v.Model.Height = height
+	v.Model.SetWidth(width)
+	v.Model.SetHeight(height)
 }
 
 // Init implements tea.Model.
@@ -38,7 +40,7 @@ func (v *Viewport) Init() tea.Cmd {
 // Update implements tea.Model.
 func (v *Viewport) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	switch msg := msg.(type) {
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch {
 		case key.Matches(msg, v.common.KeyMap.GotoTop):
 			v.GotoTop()
@@ -73,32 +75,12 @@ func (v *Viewport) GotoBottom() {
 
 // HalfViewDown moves the viewport down by half the viewport height.
 func (v *Viewport) HalfViewDown() {
-	v.Model.HalfViewDown()
+	v.Model.HalfPageDown()
 }
 
 // HalfViewUp moves the viewport up by half the viewport height.
 func (v *Viewport) HalfViewUp() {
-	v.Model.HalfViewUp()
-}
-
-// ViewUp moves the viewport up by a page.
-func (v *Viewport) ViewUp() []string {
-	return v.Model.ViewUp()
-}
-
-// ViewDown moves the viewport down by a page.
-func (v *Viewport) ViewDown() []string {
-	return v.Model.ViewDown()
-}
-
-// LineUp moves the viewport up by the given number of lines.
-func (v *Viewport) LineUp(n int) []string {
-	return v.Model.LineUp(n)
-}
-
-// LineDown moves the viewport down by the given number of lines.
-func (v *Viewport) LineDown(n int) []string {
-	return v.Model.LineDown(n)
+	v.Model.HalfPageUp()
 }
 
 // ScrollPercent returns the viewport's scroll percentage.

pkg/ui/keymap/keymap.go 🔗

@@ -1,6 +1,6 @@
 package keymap
 
-import "github.com/charmbracelet/bubbles/key"
+import "github.com/charmbracelet/bubbles/v2/key"
 
 // KeyMap is a map of key bindings for the UI.
 type KeyMap struct {

pkg/ui/pages/repo/files.go 🔗

@@ -7,9 +7,9 @@ import (
 	"strings"
 
 	gitm "github.com/aymanbagabas/git-module"
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/spinner"
-	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/spinner"
+	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
@@ -264,7 +264,7 @@ func (f *Files) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		case filesViewFiles, filesViewContent:
 			cmds = append(cmds, f.deselectItemCmd())
 		}
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch f.activeView {
 		case filesViewFiles:
 			switch {

pkg/ui/pages/repo/filesitem.go 🔗

@@ -6,10 +6,10 @@ import (
 	"io/fs"
 	"strings"
 
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/list"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/list"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 	"github.com/dustin/go-humanize"
@@ -82,7 +82,7 @@ func (d FileItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
 		return nil
 	}
 	switch msg := msg.(type) {
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch {
 		case key.Matches(msg, d.common.KeyMap.Copy):
 			return copyCmd(item.entry.Name(), fmt.Sprintf("File name %q copied to clipboard", item.entry.Name()))
@@ -139,7 +139,7 @@ func (d FileItemDelegate) Render(w io.Writer, m list.Model, index int, listItem
 	name = nameStyle.Render(name)
 	size = sizeStyle.Render(size)
 	modeStr := modeStyle.Render(mode.String())
-	truncate := d.common.Renderer.NewStyle().MaxWidth(m.Width() -
+	truncate := lipgloss.NewStyle().MaxWidth(m.Width() -
 		s.Selector.GetHorizontalFrameSize() -
 		s.Selector.GetWidth())
 	//nolint:errcheck

pkg/ui/pages/repo/log.go 🔗

@@ -5,11 +5,11 @@ import (
 	"strings"
 	"time"
 
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/spinner"
-	tea "github.com/charmbracelet/bubbletea"
-	gansi "github.com/charmbracelet/glamour/ansi"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/spinner"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	gansi "github.com/charmbracelet/glamour/v2/ansi"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
@@ -221,11 +221,11 @@ func (l *Log) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		if i != nil {
 			l.activeCommit = i.(LogItem).Commit
 		}
-	case tea.KeyMsg, tea.MouseMsg:
+	case tea.KeyPressMsg, tea.MouseClickMsg:
 		switch l.activeView {
 		case logViewCommits:
 			switch kmsg := msg.(type) {
-			case tea.KeyMsg:
+			case tea.KeyPressMsg:
 				switch {
 				case key.Matches(kmsg, l.common.KeyMap.SelectItem):
 					cmds = append(cmds, l.selector.SelectItemCmd)
@@ -248,7 +248,7 @@ func (l *Log) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			cmds = append(cmds, cmd)
 		case logViewDiff:
 			switch kmsg := msg.(type) {
-			case tea.KeyMsg:
+			case tea.KeyPressMsg:
 				switch {
 				case key.Matches(kmsg, l.common.KeyMap.BackItem):
 					l.goBack()

pkg/ui/pages/repo/logitem.go 🔗

@@ -6,10 +6,10 @@ import (
 	"strings"
 	"time"
 
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/list"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/list"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 	"github.com/muesli/reflow/truncate"
@@ -62,7 +62,7 @@ func (d LogItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
 		return nil
 	}
 	switch msg := msg.(type) {
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch {
 		case key.Matches(msg, d.common.KeyMap.Copy):
 			return copyCmd(item.Hash(), "Commit hash copied to clipboard")

pkg/ui/pages/repo/readme.go 🔗

@@ -4,9 +4,9 @@ import (
 	"fmt"
 	"path/filepath"
 
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/spinner"
-	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/spinner"
+	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"

pkg/ui/pages/repo/refs.go 🔗

@@ -5,9 +5,9 @@ import (
 	"sort"
 	"strings"
 
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/spinner"
-	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/spinner"
+	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
@@ -152,7 +152,7 @@ func (r *Refs) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 				switchTabCmd(&Files{}),
 			)
 		}
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch {
 		case key.Matches(msg, r.common.KeyMap.SelectItem):
 			cmds = append(cmds, r.selector.SelectItemCmd)

pkg/ui/pages/repo/refsitem.go 🔗

@@ -6,10 +6,10 @@ import (
 	"strings"
 	"time"
 
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/list"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/list"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 	"github.com/dustin/go-humanize"
@@ -83,7 +83,7 @@ func (d RefItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
 		return nil
 	}
 	switch msg := msg.(type) {
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch {
 		case key.Matches(msg, d.common.KeyMap.Copy):
 			return copyCmd(item.ID(), fmt.Sprintf("Reference %q copied to clipboard", item.ID()))

pkg/ui/pages/repo/repo.go 🔗

@@ -4,11 +4,11 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/charmbracelet/bubbles/help"
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/spinner"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/help"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/spinner"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
@@ -171,7 +171,7 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		}
 	case tabs.ActiveTabMsg:
 		r.activeTab = int(msg)
-	case tea.KeyMsg, tea.MouseMsg:
+	case tea.KeyPressMsg, tea.MouseClickMsg:
 		t, cmd := r.tabs.Update(msg)
 		r.tabs = t.(*tabs.Tabs)
 		if cmd != nil {
@@ -185,17 +185,14 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			}
 		}
 		switch msg := msg.(type) {
-		case tea.MouseMsg:
-			if msg.Action != tea.MouseActionPress {
-				break
-			}
+		case tea.MouseClickMsg:
 			switch msg.Button {
-			case tea.MouseButtonLeft:
+			case tea.MouseLeft:
 				switch {
 				case r.common.Zone.Get("repo-help").InBounds(msg):
 					cmds = append(cmds, footer.ToggleFooterCmd)
 				}
-			case tea.MouseButtonRight:
+			case tea.MouseRight:
 				switch {
 				case r.common.Zone.Get("repo-main").InBounds(msg):
 					cmds = append(cmds, goBackCmd)
@@ -203,7 +200,7 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			}
 		}
 		switch msg := msg.(type) {
-		case tea.KeyMsg:
+		case tea.KeyPressMsg:
 			switch {
 			case key.Matches(msg, r.common.KeyMap.Back):
 				cmds = append(cmds, goBackCmd)
@@ -212,7 +209,7 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	case CopyMsg:
 		txt := msg.Text
 		if cfg := r.common.Config(); cfg != nil {
-			r.common.Output.Copy(txt)
+			cmds = append(cmds, tea.SetClipboard(txt))
 		}
 		r.statusbar.SetStatus("", msg.Message, "", "")
 	case ReadmeMsg:
@@ -274,7 +271,7 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	// Update the status bar on these events
 	// Must come after we've updated the active tab
 	switch msg.(type) {
-	case RepoMsg, RefMsg, tabs.ActiveTabMsg, tea.KeyMsg, tea.MouseMsg,
+	case RepoMsg, RefMsg, tabs.ActiveTabMsg, tea.KeyPressMsg, tea.MouseClickMsg,
 		FileItemsMsg, FileContentMsg, FileBlameMsg, selector.ActiveMsg,
 		LogItemsMsg, GoBackMsg, LogDiffMsg, EmptyRepoMsg,
 		StashListMsg, StashPatchMsg:
@@ -326,7 +323,7 @@ func (r *Repo) headerView() string {
 	if r.selectedRepo == nil {
 		return ""
 	}
-	truncate := r.common.Renderer.NewStyle().MaxWidth(r.common.Width)
+	truncate := lipgloss.NewStyle().MaxWidth(r.common.Width)
 	header := r.selectedRepo.ProjectName()
 	if header == "" {
 		header = r.selectedRepo.Name()

pkg/ui/pages/repo/stash.go 🔗

@@ -4,10 +4,10 @@ import (
 	"fmt"
 
 	gitm "github.com/aymanbagabas/git-module"
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/spinner"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/spinner"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
@@ -165,7 +165,7 @@ func (s *Stash) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 				cmds = append(cmds, cmd)
 			}
 		}
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch s.state {
 		case stashStateList:
 			switch {

pkg/ui/pages/repo/stashitem.go 🔗

@@ -5,9 +5,9 @@ import (
 	"io"
 
 	gitm "github.com/aymanbagabas/git-module"
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/list"
-	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/list"
+	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 )
 
@@ -65,7 +65,7 @@ func (d StashItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
 	}
 
 	switch msg := msg.(type) {
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch {
 		case key.Matches(msg, d.common.KeyMap.Copy):
 			return copyCmd(item.Title(), fmt.Sprintf("Stash message %q copied to clipboard", item.Title()))

pkg/ui/pages/selection/item.go 🔗

@@ -7,10 +7,10 @@ import (
 	"strings"
 	"time"
 
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/list"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/list"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/pkg/proto"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 	"github.com/dustin/go-humanize"
@@ -135,12 +135,14 @@ func (d *ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
 		return nil
 	}
 	switch msg := msg.(type) {
-	case tea.KeyMsg:
+	case tea.KeyPressMsg:
 		switch {
 		case key.Matches(msg, d.common.KeyMap.Copy):
 			d.copiedIdx = idx
-			d.common.Output.Copy(item.Command())
-			return m.SetItem(idx, item)
+			return tea.Batch(
+				tea.SetClipboard(item.Command()),
+				m.SetItem(idx, item),
+			)
 		}
 	}
 	return nil

pkg/ui/pages/selection/selection.go 🔗

@@ -4,10 +4,10 @@ import (
 	"fmt"
 	"sort"
 
-	"github.com/charmbracelet/bubbles/key"
-	"github.com/charmbracelet/bubbles/list"
-	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/bubbles/v2/key"
+	"github.com/charmbracelet/bubbles/v2/list"
+	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
 	"github.com/charmbracelet/soft-serve/pkg/access"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
@@ -51,7 +51,7 @@ func New(c common.Common) *Selection {
 		ts[i] = b.String()
 	}
 	t := tabs.New(c, ts)
-	t.TabSeparator = c.Renderer.NewStyle()
+	t.TabSeparator = lipgloss.NewStyle()
 	t.TabInactive = c.Styles.TopLevelNormalTab
 	t.TabActive = c.Styles.TopLevelActiveTab
 	t.TabDot = c.Styles.TopLevelActiveTabDot
@@ -250,9 +250,9 @@ func (s *Selection) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		if cmd != nil {
 			cmds = append(cmds, cmd)
 		}
-	case tea.KeyMsg, tea.MouseMsg:
+	case tea.KeyPressMsg, tea.MouseMsg:
 		switch msg := msg.(type) {
-		case tea.KeyMsg:
+		case tea.KeyPressMsg:
 			switch {
 			case key.Matches(msg, s.common.KeyMap.Back):
 				cmds = append(cmds, s.selector.Init())
@@ -289,15 +289,15 @@ func (s *Selection) View() string {
 	wm, hm := s.getMargins()
 	switch s.activePane {
 	case selectorPane:
-		ss := s.common.Renderer.NewStyle().
+		ss := lipgloss.NewStyle().
 			Width(s.common.Width - wm).
 			Height(s.common.Height - hm)
 		view = ss.Render(s.selector.View())
 	case readmePane:
-		rs := s.common.Renderer.NewStyle().
+		rs := lipgloss.NewStyle().
 			Height(s.common.Height - hm)
 		status := fmt.Sprintf("☰ %.f%%", s.readme.ScrollPercent()*100)
-		readmeStatus := s.common.Renderer.NewStyle().
+		readmeStatus := lipgloss.NewStyle().
 			Align(lipgloss.Right).
 			Width(s.common.Width - wm).
 			Foreground(s.common.Styles.InactiveBorderColor).

pkg/ui/styles/styles.go 🔗

@@ -1,7 +1,9 @@
 package styles
 
 import (
-	"github.com/charmbracelet/lipgloss"
+	"image/color"
+
+	"github.com/charmbracelet/lipgloss/v2"
 )
 
 // XXX: For now, this is in its own package so that it can be shared between
@@ -9,8 +11,8 @@ import (
 
 // Styles defines styles for the UI.
 type Styles struct {
-	ActiveBorderColor   lipgloss.Color
-	InactiveBorderColor lipgloss.Color
+	ActiveBorderColor   color.Color
+	InactiveBorderColor color.Color
 
 	App                  lipgloss.Style
 	ServerName           lipgloss.Style
@@ -165,7 +167,7 @@ type Styles struct {
 }
 
 // DefaultStyles returns default styles for the UI.
-func DefaultStyles(r *lipgloss.Renderer) *Styles {
+func DefaultStyles() *Styles {
 	highlightColor := lipgloss.Color("210")
 	highlightColorDim := lipgloss.Color("174")
 	selectorColor := lipgloss.Color("167")
@@ -176,10 +178,10 @@ func DefaultStyles(r *lipgloss.Renderer) *Styles {
 	s.ActiveBorderColor = lipgloss.Color("62")
 	s.InactiveBorderColor = lipgloss.Color("241")
 
-	s.App = r.NewStyle().
+	s.App = lipgloss.NewStyle().
 		Margin(1, 2)
 
-	s.ServerName = r.NewStyle().
+	s.ServerName = lipgloss.NewStyle().
 		Height(1).
 		MarginLeft(1).
 		MarginBottom(1).
@@ -188,29 +190,29 @@ func DefaultStyles(r *lipgloss.Renderer) *Styles {
 		Foreground(lipgloss.Color("229")).
 		Bold(true)
 
-	s.TopLevelNormalTab = r.NewStyle().
+	s.TopLevelNormalTab = lipgloss.NewStyle().
 		MarginRight(2)
 
 	s.TopLevelActiveTab = s.TopLevelNormalTab.
 		Foreground(lipgloss.Color("36"))
 
-	s.TopLevelActiveTabDot = r.NewStyle().
+	s.TopLevelActiveTabDot = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("36"))
 
-	s.RepoSelector.Normal.Base = r.NewStyle().
+	s.RepoSelector.Normal.Base = lipgloss.NewStyle().
 		PaddingLeft(1).
 		Border(lipgloss.Border{Left: " "}, false, false, false, true).
 		Height(3)
 
-	s.RepoSelector.Normal.Title = r.NewStyle().Bold(true)
+	s.RepoSelector.Normal.Title = lipgloss.NewStyle().Bold(true)
 
-	s.RepoSelector.Normal.Desc = r.NewStyle().
+	s.RepoSelector.Normal.Desc = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("243"))
 
-	s.RepoSelector.Normal.Command = r.NewStyle().
+	s.RepoSelector.Normal.Command = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("132"))
 
-	s.RepoSelector.Normal.Updated = r.NewStyle().
+	s.RepoSelector.Normal.Updated = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("243"))
 
 	s.RepoSelector.Active.Base = s.RepoSelector.Normal.Base.
@@ -229,78 +231,78 @@ func DefaultStyles(r *lipgloss.Renderer) *Styles {
 	s.RepoSelector.Active.Command = s.RepoSelector.Normal.Command.
 		Foreground(lipgloss.Color("204"))
 
-	s.MenuItem = r.NewStyle().
+	s.MenuItem = lipgloss.NewStyle().
 		PaddingLeft(1).
 		Border(lipgloss.Border{
 			Left: " ",
 		}, false, false, false, true).
 		Height(3)
 
-	s.MenuLastUpdate = r.NewStyle().
+	s.MenuLastUpdate = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("241")).
 		Align(lipgloss.Right)
 
-	s.Repo.Base = r.NewStyle()
+	s.Repo.Base = lipgloss.NewStyle()
 
-	s.Repo.Title = r.NewStyle().
+	s.Repo.Title = lipgloss.NewStyle().
 		Padding(0, 2)
 
-	s.Repo.Command = r.NewStyle().
+	s.Repo.Command = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("168"))
 
-	s.Repo.Body = r.NewStyle().
+	s.Repo.Body = lipgloss.NewStyle().
 		Margin(1, 0)
 
-	s.Repo.Header = r.NewStyle().
+	s.Repo.Header = lipgloss.NewStyle().
 		MaxHeight(2).
 		Border(lipgloss.NormalBorder(), false, false, true, false).
 		BorderForeground(lipgloss.Color("236"))
 
-	s.Repo.HeaderName = r.NewStyle().
+	s.Repo.HeaderName = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("212")).
 		Bold(true)
 
-	s.Repo.HeaderDesc = r.NewStyle().
+	s.Repo.HeaderDesc = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("243"))
 
-	s.Footer = r.NewStyle().
+	s.Footer = lipgloss.NewStyle().
 		MarginTop(1).
 		Padding(0, 1).
 		Height(1)
 
-	s.Branch = r.NewStyle().
+	s.Branch = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("203")).
 		Background(lipgloss.Color("236")).
 		Padding(0, 1)
 
-	s.HelpKey = r.NewStyle().
+	s.HelpKey = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("241"))
 
-	s.HelpValue = r.NewStyle().
+	s.HelpValue = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("239"))
 
-	s.HelpDivider = r.NewStyle().
+	s.HelpDivider = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("237")).
 		SetString(" • ")
 
-	s.URLStyle = r.NewStyle().
+	s.URLStyle = lipgloss.NewStyle().
 		MarginLeft(1).
 		Foreground(lipgloss.Color("168"))
 
-	s.Error = r.NewStyle().
+	s.Error = lipgloss.NewStyle().
 		MarginTop(2)
 
-	s.ErrorTitle = r.NewStyle().
+	s.ErrorTitle = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("230")).
 		Background(lipgloss.Color("204")).
 		Bold(true).
 		Padding(0, 1)
 
-	s.ErrorBody = r.NewStyle().
+	s.ErrorBody = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("252")).
 		MarginLeft(2)
 
-	s.LogItem.Normal.Base = r.NewStyle().
+	s.LogItem.Normal.Base = lipgloss.NewStyle().
 		Border(lipgloss.Border{
 			Left: " ",
 		}, false, false, false, true).
@@ -315,113 +317,113 @@ func DefaultStyles(r *lipgloss.Renderer) *Styles {
 	s.LogItem.Active.Hash = s.LogItem.Normal.Hash.
 		Foreground(hashColor)
 
-	s.LogItem.Active.Hash = r.NewStyle().
+	s.LogItem.Active.Hash = lipgloss.NewStyle().
 		Bold(true).
 		Foreground(highlightColor)
 
-	s.LogItem.Normal.Title = r.NewStyle().
+	s.LogItem.Normal.Title = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("105"))
 
-	s.LogItem.Active.Title = r.NewStyle().
+	s.LogItem.Active.Title = lipgloss.NewStyle().
 		Foreground(highlightColor).
 		Bold(true)
 
-	s.LogItem.Normal.Desc = r.NewStyle().
+	s.LogItem.Normal.Desc = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("246"))
 
-	s.LogItem.Active.Desc = r.NewStyle().
+	s.LogItem.Active.Desc = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("95"))
 
 	s.LogItem.Active.Keyword = s.LogItem.Active.Desc.
 		Foreground(highlightColorDim)
 
-	s.LogItem.Normal.Hash = r.NewStyle().
+	s.LogItem.Normal.Hash = lipgloss.NewStyle().
 		Foreground(hashColor)
 
-	s.LogItem.Active.Hash = r.NewStyle().
+	s.LogItem.Active.Hash = lipgloss.NewStyle().
 		Foreground(highlightColor)
 
-	s.Log.Commit = r.NewStyle().
+	s.Log.Commit = lipgloss.NewStyle().
 		Margin(0, 2)
 
-	s.Log.CommitHash = r.NewStyle().
+	s.Log.CommitHash = lipgloss.NewStyle().
 		Foreground(hashColor).
 		Bold(true)
 
-	s.Log.CommitBody = r.NewStyle().
+	s.Log.CommitBody = lipgloss.NewStyle().
 		MarginTop(1).
 		MarginLeft(2)
 
-	s.Log.CommitStatsAdd = r.NewStyle().
+	s.Log.CommitStatsAdd = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("42")).
 		Bold(true)
 
-	s.Log.CommitStatsDel = r.NewStyle().
+	s.Log.CommitStatsDel = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("203")).
 		Bold(true)
 
-	s.Log.Paginator = r.NewStyle().
+	s.Log.Paginator = lipgloss.NewStyle().
 		Margin(0).
 		Align(lipgloss.Center)
 
-	s.Ref.Normal.Item = r.NewStyle()
+	s.Ref.Normal.Item = lipgloss.NewStyle()
 
-	s.Ref.ItemSelector = r.NewStyle().
+	s.Ref.ItemSelector = lipgloss.NewStyle().
 		Foreground(selectorColor).
 		SetString("> ")
 
-	s.Ref.Active.Item = r.NewStyle().
+	s.Ref.Active.Item = lipgloss.NewStyle().
 		Foreground(highlightColorDim)
 
-	s.Ref.Normal.Base = r.NewStyle()
+	s.Ref.Normal.Base = lipgloss.NewStyle()
 
-	s.Ref.Active.Base = r.NewStyle()
+	s.Ref.Active.Base = lipgloss.NewStyle()
 
-	s.Ref.Normal.ItemTag = r.NewStyle().
+	s.Ref.Normal.ItemTag = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("39"))
 
-	s.Ref.Active.ItemTag = r.NewStyle().
+	s.Ref.Active.ItemTag = lipgloss.NewStyle().
 		Bold(true).
 		Foreground(highlightColor)
 
-	s.Ref.Active.Item = r.NewStyle().
+	s.Ref.Active.Item = lipgloss.NewStyle().
 		Bold(true).
 		Foreground(highlightColor)
 
-	s.Ref.Normal.ItemDesc = r.NewStyle().
+	s.Ref.Normal.ItemDesc = lipgloss.NewStyle().
 		Faint(true)
 
-	s.Ref.Active.ItemDesc = r.NewStyle().
+	s.Ref.Active.ItemDesc = lipgloss.NewStyle().
 		Foreground(highlightColor).
 		Faint(true)
 
-	s.Ref.Normal.ItemHash = r.NewStyle().
+	s.Ref.Normal.ItemHash = lipgloss.NewStyle().
 		Foreground(hashColor).
 		Bold(true)
 
-	s.Ref.Active.ItemHash = r.NewStyle().
+	s.Ref.Active.ItemHash = lipgloss.NewStyle().
 		Foreground(highlightColor).
 		Bold(true)
 
 	s.Ref.Paginator = s.Log.Paginator
 
-	s.Ref.Selector = r.NewStyle()
+	s.Ref.Selector = lipgloss.NewStyle()
 
 	s.Tree.Selector = s.Tree.Normal.FileName.
 		Width(1).
 		Foreground(selectorColor)
 
-	s.Tree.Normal.FileName = r.NewStyle().
+	s.Tree.Normal.FileName = lipgloss.NewStyle().
 		MarginLeft(1)
 
 	s.Tree.Active.FileName = s.Tree.Normal.FileName.
 		Bold(true).
 		Foreground(highlightColor)
 
-	s.Tree.Normal.FileDir = r.NewStyle().
+	s.Tree.Normal.FileDir = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("39"))
 
-	s.Tree.Active.FileDir = r.NewStyle().
+	s.Tree.Active.FileDir = lipgloss.NewStyle().
 		Foreground(highlightColor)
 
 	s.Tree.Normal.FileMode = s.Tree.Active.FileName.
@@ -437,87 +439,87 @@ func DefaultStyles(r *lipgloss.Renderer) *Styles {
 	s.Tree.Active.FileSize = s.Tree.Normal.FileName.
 		Foreground(highlightColorDim)
 
-	s.Tree.FileContent = r.NewStyle()
+	s.Tree.FileContent = lipgloss.NewStyle()
 
 	s.Tree.Paginator = s.Log.Paginator
 
-	s.Tree.Blame.Hash = r.NewStyle().
+	s.Tree.Blame.Hash = lipgloss.NewStyle().
 		Foreground(hashColor).
 		Bold(true)
 
-	s.Tree.Blame.Message = r.NewStyle()
+	s.Tree.Blame.Message = lipgloss.NewStyle()
 
-	s.Tree.Blame.Who = r.NewStyle().
+	s.Tree.Blame.Who = lipgloss.NewStyle().
 		Faint(true)
 
-	s.Spinner = r.NewStyle().
+	s.Spinner = lipgloss.NewStyle().
 		MarginTop(1).
 		MarginLeft(2).
 		Foreground(lipgloss.Color("205"))
 
-	s.SpinnerContainer = r.NewStyle()
+	s.SpinnerContainer = lipgloss.NewStyle()
 
-	s.NoContent = r.NewStyle().
+	s.NoContent = lipgloss.NewStyle().
 		MarginTop(1).
 		MarginLeft(2).
 		Foreground(lipgloss.Color("242"))
 
-	s.StatusBar = r.NewStyle().
+	s.StatusBar = lipgloss.NewStyle().
 		Height(1)
 
-	s.StatusBarKey = r.NewStyle().
+	s.StatusBarKey = lipgloss.NewStyle().
 		Bold(true).
 		Padding(0, 1).
 		Background(lipgloss.Color("206")).
 		Foreground(lipgloss.Color("228"))
 
-	s.StatusBarValue = r.NewStyle().
+	s.StatusBarValue = lipgloss.NewStyle().
 		Padding(0, 1).
 		Background(lipgloss.Color("235")).
 		Foreground(lipgloss.Color("243"))
 
-	s.StatusBarInfo = r.NewStyle().
+	s.StatusBarInfo = lipgloss.NewStyle().
 		Padding(0, 1).
 		Background(lipgloss.Color("212")).
 		Foreground(lipgloss.Color("230"))
 
-	s.StatusBarBranch = r.NewStyle().
+	s.StatusBarBranch = lipgloss.NewStyle().
 		Padding(0, 1).
 		Background(lipgloss.Color("62")).
 		Foreground(lipgloss.Color("230"))
 
-	s.StatusBarHelp = r.NewStyle().
+	s.StatusBarHelp = lipgloss.NewStyle().
 		Padding(0, 1).
 		Background(lipgloss.Color("237")).
 		Foreground(lipgloss.Color("243"))
 
-	s.Tabs = r.NewStyle().
+	s.Tabs = lipgloss.NewStyle().
 		Height(1)
 
-	s.TabInactive = r.NewStyle()
+	s.TabInactive = lipgloss.NewStyle()
 
-	s.TabActive = r.NewStyle().
+	s.TabActive = lipgloss.NewStyle().
 		Underline(true).
 		Foreground(lipgloss.Color("36"))
 
-	s.TabSeparator = r.NewStyle().
+	s.TabSeparator = lipgloss.NewStyle().
 		SetString("│").
 		Padding(0, 1).
 		Foreground(lipgloss.Color("238"))
 
-	s.Code.LineDigit = r.NewStyle().Foreground(lipgloss.Color("239"))
+	s.Code.LineDigit = lipgloss.NewStyle().Foreground(lipgloss.Color("239"))
 
-	s.Code.LineBar = r.NewStyle().Foreground(lipgloss.Color("236"))
+	s.Code.LineBar = lipgloss.NewStyle().Foreground(lipgloss.Color("236"))
 
-	s.Stash.Normal.Message = r.NewStyle().MarginLeft(1)
+	s.Stash.Normal.Message = lipgloss.NewStyle().MarginLeft(1)
 
 	s.Stash.Active.Message = s.Stash.Normal.Message.Foreground(selectorColor)
 
-	s.Stash.Title = r.NewStyle().
+	s.Stash.Title = lipgloss.NewStyle().
 		Foreground(hashColor).
 		Bold(true)
 
-	s.Stash.Selector = r.NewStyle().
+	s.Stash.Selector = lipgloss.NewStyle().
 		Width(1).
 		Foreground(selectorColor)
 

pkg/web/auth.go 🔗

@@ -7,7 +7,7 @@ import (
 	"net/http"
 	"strings"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/proto"

pkg/web/context.go 🔗

@@ -4,7 +4,7 @@ import (
 	"context"
 	"net/http"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/db"

pkg/web/git.go 🔗

@@ -14,7 +14,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	gitb "github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/access"
 	"github.com/charmbracelet/soft-serve/pkg/backend"

pkg/web/git_lfs.go 🔗

@@ -13,7 +13,7 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/access"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"

pkg/web/goget.go 🔗

@@ -6,7 +6,7 @@ import (
 	"path"
 	"text/template"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	"github.com/charmbracelet/soft-serve/pkg/utils"

pkg/web/http.go 🔗

@@ -5,7 +5,7 @@ import (
 	"net/http"
 	"time"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 )
 

pkg/web/logging.go 🔗

@@ -7,7 +7,7 @@ import (
 	"net/http"
 	"time"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/dustin/go-humanize"
 )
 

pkg/web/server.go 🔗

@@ -4,7 +4,7 @@ import (
 	"context"
 	"net/http"
 
-	"github.com/charmbracelet/log"
+	"github.com/charmbracelet/log/v2"
 	"github.com/gorilla/handlers"
 	"github.com/gorilla/mux"
 )