9871df2e94a011ae6071ade9913ececf82f9f132

Author
Ayman Bagabas <ayman.bagabas@gmail.com>
Committer
Ayman Bagabas <ayman.bagabas@gmail.com>
Date

Message

refactor: use the latest v2 packages

Diff

This diff is truncated to protect this page.

   1diff --git a/cmd/soft/browse/browse.go b/cmd/soft/browse/browse.go
   2index eebf28893885e9844dccd6787f03e613c67e8ffd..10fd15d19f4964e1d6184fb055933561949035ba 100644
   3--- a/cmd/soft/browse/browse.go
   4+++ b/cmd/soft/browse/browse.go
   5@@ -5,9 +5,9 @@ import (
   6 	"path/filepath"
   7 	"time"
   8 
   9-	"github.com/charmbracelet/bubbles/key"
  10-	tea "github.com/charmbracelet/bubbletea"
  11-	"github.com/charmbracelet/lipgloss"
  12+	"github.com/charmbracelet/bubbles/v2/key"
  13+	tea "github.com/charmbracelet/bubbletea/v2"
  14+	"github.com/charmbracelet/lipgloss/v2"
  15 	"github.com/charmbracelet/soft-serve/git"
  16 	"github.com/charmbracelet/soft-serve/pkg/proto"
  17 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
  18@@ -39,9 +39,8 @@ var Command = &cobra.Command{
  19 
  20 		// Bubble Tea uses Termenv default output so we have to use the same
  21 		// thing here.
  22-		output := lipgloss.DefaultRenderer()
  23 		ctx := cmd.Context()
  24-		c := common.NewCommon(ctx, output, 0, 0)
  25+		c := common.NewCommon(ctx, 0, 0)
  26 		c.HideCloneCmd = true
  27 		comps := []common.TabComponent{
  28 			repo.NewReadme(c),
  29@@ -166,16 +165,14 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  30 			m.common.Zone.Close()
  31 			return m, tea.Quit
  32 		}
  33-	case tea.MouseMsg:
  34-		if msg.Action != tea.MouseActionPress {
  35-			break
  36-		}
  37-		switch msg.Button {
  38-		case tea.MouseButtonLeft:
  39-			switch {
  40-			case m.common.Zone.Get("footer").InBounds(msg):
  41-				cmds = append(cmds, footer.ToggleFooterCmd)
  42-			}
  43+	case tea.MouseClickMsg:
  44+		mouse := msg.Mouse()
  45+		switch mouse.Button {
  46+		case tea.MouseLeft:
  47+			// switch {
  48+			// case m.common.Zone.Get("footer").InBounds(msg):
  49+			// 	cmds = append(cmds, footer.ToggleFooterCmd)
  50+			// }
  51 		}
  52 	case footer.ToggleFooterMsg:
  53 		m.footer.SetShowAll(!m.footer.ShowAll())
  54diff --git a/cmd/soft/main.go b/cmd/soft/main.go
  55index 9849e8a268f22fba46644af33b1ee140f79f16a2..1abf8c2f8572476f0b0dd07deb8576f4395188b2 100644
  56--- a/cmd/soft/main.go
  57+++ b/cmd/soft/main.go
  58@@ -7,6 +7,7 @@ import (
  59 	"runtime/debug"
  60 	"strconv"
  61 
  62+	"github.com/charmbracelet/colorprofile"
  63 	"github.com/charmbracelet/log"
  64 	"github.com/charmbracelet/soft-serve/cmd/soft/admin"
  65 	"github.com/charmbracelet/soft-serve/cmd/soft/browse"
  66@@ -18,7 +19,6 @@ import (
  67 	"github.com/charmbracelet/soft-serve/pkg/version"
  68 	mcobra "github.com/muesli/mango-cobra"
  69 	"github.com/muesli/roff"
  70-	"github.com/muesli/termenv"
  71 	"github.com/spf13/cobra"
  72 	"go.uber.org/automaxprocs/maxprocs"
  73 )
  74@@ -67,7 +67,7 @@ var (
  75 
  76 func init() {
  77 	if noColor, _ := strconv.ParseBool(os.Getenv("SOFT_SERVE_NO_COLOR")); noColor {
  78-		common.DefaultColorProfile = termenv.Ascii
  79+		common.DefaultColorProfile = colorprofile.NoTTY
  80 	}
  81 
  82 	rootCmd.AddCommand(
  83diff --git a/go.mod b/go.mod
  84index 7390c6b3a6879c4cf46901d2c7cd42939ae57039..f9b88720be7645aeeb57209a25e1338ea633ac9a 100644
  85--- a/go.mod
  86+++ b/go.mod
  87@@ -2,11 +2,8 @@ module github.com/charmbracelet/soft-serve
  88 
  89 go 1.23.0
  90 
  91-toolchain go1.24.1
  92-
  93 require (
  94-	github.com/charmbracelet/bubbles v0.20.0
  95-	github.com/charmbracelet/bubbletea v1.3.4
  96+	github.com/charmbracelet/bubbletea v1.3.4 // indirect
  97 	github.com/charmbracelet/glamour v0.9.1
  98 	github.com/charmbracelet/lipgloss v1.1.0
  99 	github.com/charmbracelet/wish v1.4.6
 100@@ -22,12 +19,18 @@ require (
 101 	github.com/alecthomas/chroma/v2 v2.15.0
 102 	github.com/aymanbagabas/git-module v1.8.4-0.20231101154130-8d27204ac6d2
 103 	github.com/caarlos0/duration v0.0.0-20240108180406-5d492514f3c7
 104-	github.com/caarlos0/env/v11 v11.3.1
 105+	github.com/caarlos0/env/v11 v11.2.2
 106+	github.com/charmbracelet/bubbles/v2 v2.0.0-alpha.2.0.20250318132216-bd20b89a3e80
 107+	github.com/charmbracelet/bubbletea/v2 v2.0.0-alpha.2.0.20250318133619-2ce107c85fed
 108+	github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc
 109 	github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20240708204110-bacbfdb68d92
 110-	github.com/charmbracelet/keygen v0.5.3
 111-	github.com/charmbracelet/log v0.4.1
 112-	github.com/charmbracelet/ssh v0.0.0-20250213143314-8712ec3ff3ef
 113-	github.com/go-jose/go-jose/v3 v3.0.4
 114+	github.com/charmbracelet/keygen v0.5.1
 115+	github.com/charmbracelet/lipgloss/v2 v2.0.0-alpha.2.0.20250313133401-58331b1fff40
 116+	github.com/charmbracelet/log v0.4.1-0.20241101171137-e66b83d34ed9
 117+	github.com/charmbracelet/ssh v0.0.0-20250128164007-98fd5ae11894
 118+	github.com/charmbracelet/wish/v2 v2.0.0-20250319172925-3ae4bfe9c24e
 119+	github.com/charmbracelet/x/ansi v0.8.0
 120+	github.com/go-jose/go-jose/v3 v3.0.3
 121 	github.com/gobwas/glob v0.2.3
 122 	github.com/golang-jwt/jwt/v5 v5.2.1
 123 	github.com/google/go-querystring v1.1.0
 124@@ -45,7 +48,7 @@ require (
 125 	github.com/rogpeppe/go-internal v1.14.1
 126 	github.com/spf13/cobra v1.9.1
 127 	go.uber.org/automaxprocs v1.6.0
 128-	golang.org/x/crypto v0.36.0
 129+	golang.org/x/crypto v0.35.0
 130 	golang.org/x/sync v0.12.0
 131 	gopkg.in/yaml.v3 v3.0.1
 132 	modernc.org/sqlite v1.36.1
 133@@ -58,12 +61,11 @@ require (
 134 	github.com/aymerick/douceur v0.2.0 // indirect
 135 	github.com/beorn7/perks v1.0.1 // indirect
 136 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 137-	github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
 138-	github.com/charmbracelet/x/ansi v0.8.0 // indirect
 139-	github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
 140+	github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706 // indirect
 141+	github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
 142 	github.com/charmbracelet/x/conpty v0.1.0 // indirect
 143 	github.com/charmbracelet/x/errors v0.0.0-20240725160154-f9f6568126ec // indirect
 144-	github.com/charmbracelet/x/input v0.3.1 // indirect
 145+	github.com/charmbracelet/x/input v0.3.4 // indirect
 146 	github.com/charmbracelet/x/term v0.2.1 // indirect
 147 	github.com/charmbracelet/x/termios v0.1.0 // indirect
 148 	github.com/charmbracelet/x/windows v0.2.0 // indirect
 149@@ -100,7 +102,7 @@ require (
 150 	github.com/yuin/goldmark v1.7.8 // indirect
 151 	github.com/yuin/goldmark-emoji v1.0.5 // indirect
 152 	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
 153-	golang.org/x/net v0.36.0 // indirect
 154+	golang.org/x/net v0.35.0 // indirect
 155 	golang.org/x/sys v0.31.0 // indirect
 156 	golang.org/x/term v0.30.0 // indirect
 157 	golang.org/x/text v0.23.0 // indirect
 158diff --git a/go.sum b/go.sum
 159index 0dd0ca32a23b86e764e8fe5a9fc89367e92e5e44..637ac4dc47b78ce32a54134bf35278415a3a6970 100644
 160--- a/go.sum
 161+++ b/go.sum
 162@@ -22,42 +22,50 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 163 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 164 github.com/caarlos0/duration v0.0.0-20240108180406-5d492514f3c7 h1:kJP/C2eL9DCKrCOlX6lPVmAUAb6U4u9xllgws1kP9ds=
 165 github.com/caarlos0/duration v0.0.0-20240108180406-5d492514f3c7/go.mod h1:mSkwb/eZEwOJJJ4tqAKiuhLIPe0e9+FKhlU0oMCpbf8=
 166-github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
 167-github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
 168+github.com/caarlos0/env/v11 v11.2.2 h1:95fApNrUyueipoZN/EhA8mMxiNxrBwDa+oAZrMWl3Kg=
 169+github.com/caarlos0/env/v11 v11.2.2/go.mod h1:JBfcdeQiBoI3Zh1QRAWfe+tpiNTmDtcCj/hHHHMx0vc=
 170 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 171 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 172-github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
 173-github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
 174+github.com/charmbracelet/bubbles/v2 v2.0.0-alpha.2.0.20250318132216-bd20b89a3e80 h1:RlfUmUVZpshOB7YP4sdnIymgXb6tXhHeQ2854afgKoU=
 175+github.com/charmbracelet/bubbles/v2 v2.0.0-alpha.2.0.20250318132216-bd20b89a3e80/go.mod h1:OQsv1nZAu5v8GtZqNTqS4yY5mfR0VgImkpe2F0QYEfs=
 176 github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
 177 github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
 178+github.com/charmbracelet/bubbletea/v2 v2.0.0-alpha.2.0.20250318133619-2ce107c85fed h1:Fsn31oc6fmX7YgfZ1Am+19HVorJ8Bf6wJTebVNLcQP0=
 179+github.com/charmbracelet/bubbletea/v2 v2.0.0-alpha.2.0.20250318133619-2ce107c85fed/go.mod h1:ks5r0VCpADtYFlNU15krnFIbMn545yV3U6ee6UJrlto=
 180 github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
 181 github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
 182 github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20240708204110-bacbfdb68d92 h1:KtQlsiHfY3K4AoIEh0yUE/wCLHteZ9EzV1hKmx+p7U8=
 183 github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20240708204110-bacbfdb68d92/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits=
 184 github.com/charmbracelet/glamour v0.9.1 h1:Q7PdJLOx8EoepsXUvW6Puz5WQ3YUElIGQdYKrIpiGLA=
 185 github.com/charmbracelet/glamour v0.9.1/go.mod h1:+SHvIS8qnwhgTpVMiXwn7OfGomSqff1cHBCI8jLOetk=
 186-github.com/charmbracelet/keygen v0.5.3 h1:2MSDC62OUbDy6VmjIE2jM24LuXUvKywLCmaJDmr/Z/4=
 187-github.com/charmbracelet/keygen v0.5.3/go.mod h1:TcpNoMAO5GSmhx3SgcEMqCrtn8BahKhB8AlwnLjRUpk=
 188+github.com/charmbracelet/keygen v0.5.1 h1:zBkkYPtmKDVTw+cwUyY6ZwGDhRxXkEp0Oxs9sqMLqxI=
 189+github.com/charmbracelet/keygen v0.5.1/go.mod h1:zznJVmK/GWB6dAtjluqn2qsttiCBhA5MZSiwb80fcHw=
 190 github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
 191 github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
 192-github.com/charmbracelet/log v0.4.1 h1:6AYnoHKADkghm/vt4neaNEXkxcXLSV2g1rdyFDOpTyk=
 193-github.com/charmbracelet/log v0.4.1/go.mod h1:pXgyTsqsVu4N9hGdHmQ0xEA4RsXof402LX9ZgiITn2I=
 194-github.com/charmbracelet/ssh v0.0.0-20250213143314-8712ec3ff3ef h1:dNZwn4is5svUd+sQEGsrXtp7VwD2ipYaCkKMzcpAEIE=
 195-github.com/charmbracelet/ssh v0.0.0-20250213143314-8712ec3ff3ef/go.mod h1:hg+I6gvlMl16nS9ZzQNgBIrrCasGwEw0QiLsDcP01Ko=
 196+github.com/charmbracelet/lipgloss/v2 v2.0.0-alpha.2.0.20250313133401-58331b1fff40 h1:9IxBdCOOJoJ1PYXgxWXJCk3Fkl7h2n+b7VtY+5BbIuQ=
 197+github.com/charmbracelet/lipgloss/v2 v2.0.0-alpha.2.0.20250313133401-58331b1fff40/go.mod h1:XhU7tcZRWVGzkjWQ6XYRH7tIVqYuWLx6XLjVqAz+7FU=
 198+github.com/charmbracelet/log v0.4.1-0.20241101171137-e66b83d34ed9 h1:m72jc7WwobapT4NLVVAjejNsz26f2UQ2Mz74uTq/Tro=
 199+github.com/charmbracelet/log v0.4.1-0.20241101171137-e66b83d34ed9/go.mod h1:soIjG88SDQxYFpbhYXbrDTPbd/07bfo66OjADdnY5HE=
 200+github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706 h1:WkwO6Ks3mSIGnGuSdKl9qDSyfbYK50z2wc2gGMggegE=
 201+github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706/go.mod h1:mjJGp00cxcfvD5xdCa+bso251Jt4owrQvuimJtVmEmM=
 202+github.com/charmbracelet/ssh v0.0.0-20250128164007-98fd5ae11894 h1:Ffon9TbltLGBsT6XE//YvNuu4OAaThXioqalhH11xEw=
 203+github.com/charmbracelet/ssh v0.0.0-20250128164007-98fd5ae11894/go.mod h1:hg+I6gvlMl16nS9ZzQNgBIrrCasGwEw0QiLsDcP01Ko=
 204 github.com/charmbracelet/wish v1.4.6 h1:27WRqMTUmyFoZASoaAaEe78Je7LTU4VqyoBxnl4d9XA=
 205 github.com/charmbracelet/wish v1.4.6/go.mod h1:RRy2LFW3WQ3tlPmMMGgEeSMDVlFd5yqklGBVZWQSHmk=
 206+github.com/charmbracelet/wish/v2 v2.0.0-20250319172925-3ae4bfe9c24e h1:WjrZFTFMjmm9dyc+KzXpY8z2viWdwShU6Qqqu3ixAss=
 207+github.com/charmbracelet/wish/v2 v2.0.0-20250319172925-3ae4bfe9c24e/go.mod h1:olUvE0hW+a8zFU1vNvlEm6PJS+ElY1V5pcaV5pjzaGI=
 208 github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
 209 github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
 210-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
 211-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
 212+github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
 213+github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
 214 github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=
 215 github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
 216 github.com/charmbracelet/x/errors v0.0.0-20240725160154-f9f6568126ec h1:O8c7pFFK0imuHH5JBqv5smlbVoFn4CZKGjtvCQKu1WE=
 217 github.com/charmbracelet/x/errors v0.0.0-20240725160154-f9f6568126ec/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
 218-github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q=
 219-github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
 220-github.com/charmbracelet/x/input v0.3.1 h1:TE4s3fTRj+OUpJ86dKphrN99+NgBnto//EkWncMJQIg=
 221-github.com/charmbracelet/x/input v0.3.1/go.mod h1:4w9jS/NW62WrHSdmjbpzydvnbqkd+mtyK8WOWbHCdvs=
 222+github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a h1:FsHEJ52OC4VuTzU8t+n5frMjLvpYWEznSr/u8tnkCYw=
 223+github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
 224+github.com/charmbracelet/x/input v0.3.4 h1:Mujmnv/4DaitU0p+kIsrlfZl/UlmeLKw1wAP3e1fMN0=
 225+github.com/charmbracelet/x/input v0.3.4/go.mod h1:JI8RcvdZWQIhn09VzeK3hdp4lTz7+yhiEdpEQtZN+2c=
 226 github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
 227 github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
 228 github.com/charmbracelet/x/termios v0.1.0 h1:y4rjAHeFksBAfGbkRDmVinMg7x7DELIGAFbdNvxg97k=
 229@@ -84,8 +92,8 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66D
 230 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
 231 github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
 232 github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
 233-github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
 234-github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
 235+github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
 236+github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
 237 github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
 238 github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 239 github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
 240@@ -228,8 +236,8 @@ go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwE
 241 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 242 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 243 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 244-golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
 245-golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
 246+golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
 247+golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
 248 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
 249 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
 250 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 251@@ -241,8 +249,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 252 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 253 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 254 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 255-golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
 256-golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
 257+golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
 258+golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
 259 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 260 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 261 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 262diff --git a/pkg/ssh/cmd/blob.go b/pkg/ssh/cmd/blob.go
 263index 9678a37a49ce426f3e6a910c04a7f804159d95ee..45e0affb2ad7c0cc9b9a82efecf0fc8e177e63d7 100644
 264--- a/pkg/ssh/cmd/blob.go
 265+++ b/pkg/ssh/cmd/blob.go
 266@@ -3,7 +3,6 @@ package cmd
 267 import (
 268 	"fmt"
 269 
 270-	"github.com/charmbracelet/lipgloss"
 271 	"github.com/charmbracelet/soft-serve/git"
 272 	"github.com/charmbracelet/soft-serve/pkg/backend"
 273 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 274@@ -12,12 +11,12 @@ import (
 275 )
 276 
 277 // blobCommand returns a command that prints the contents of a file.
 278-func blobCommand(renderer *lipgloss.Renderer) *cobra.Command {
 279+func blobCommand() *cobra.Command {
 280 	var linenumber bool
 281 	var color bool
 282 	var raw bool
 283 
 284-	styles := styles.DefaultStyles(renderer)
 285+	styles := styles.DefaultStyles()
 286 	cmd := &cobra.Command{
 287 		Use:               "blob REPOSITORY [REFERENCE] [PATH]",
 288 		Aliases:           []string{"cat", "show"},
 289diff --git a/pkg/ssh/cmd/commit.go b/pkg/ssh/cmd/commit.go
 290index 52af6725a53692f7310a75cc6efbce33d0c8492b..4f91b2fed9ef2ca334d266ee4c43b8770ac73a06 100644
 291--- a/pkg/ssh/cmd/commit.go
 292+++ b/pkg/ssh/cmd/commit.go
 293@@ -6,7 +6,6 @@ import (
 294 	"time"
 295 
 296 	gansi "github.com/charmbracelet/glamour/ansi"
 297-	"github.com/charmbracelet/lipgloss"
 298 	"github.com/charmbracelet/soft-serve/git"
 299 	"github.com/charmbracelet/soft-serve/pkg/backend"
 300 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 301@@ -15,7 +14,7 @@ import (
 302 )
 303 
 304 // commitCommand returns a command that prints the contents of a commit.
 305-func commitCommand(renderer *lipgloss.Renderer) *cobra.Command {
 306+func commitCommand() *cobra.Command {
 307 	var color bool
 308 	var patchOnly bool
 309 
 310@@ -55,7 +54,7 @@ func commitCommand(renderer *lipgloss.Renderer) *cobra.Command {
 311 				return err
 312 			}
 313 
 314-			commonStyle := styles.DefaultStyles(renderer)
 315+			commonStyle := styles.DefaultStyles()
 316 			style := commonStyle.Log
 317 
 318 			s := strings.Builder{}
 319diff --git a/pkg/ssh/cmd/repo.go b/pkg/ssh/cmd/repo.go
 320index 6a2bb541e62e5ac97da0516bb28440a91ccfbe19..fa3e72ff9a3098b40314acbca119c86628f9850c 100644
 321--- a/pkg/ssh/cmd/repo.go
 322+++ b/pkg/ssh/cmd/repo.go
 323@@ -4,14 +4,13 @@ import (
 324 	"fmt"
 325 	"strings"
 326 
 327-	"github.com/charmbracelet/lipgloss"
 328 	"github.com/charmbracelet/soft-serve/pkg/backend"
 329 	"github.com/charmbracelet/soft-serve/pkg/proto"
 330 	"github.com/spf13/cobra"
 331 )
 332 
 333 // RepoCommand returns a command for managing repositories.
 334-func RepoCommand(renderer *lipgloss.Renderer) *cobra.Command {
 335+func RepoCommand() *cobra.Command {
 336 	cmd := &cobra.Command{
 337 		Use:     "repo",
 338 		Aliases: []string{"repos", "repository", "repositories"},
 339@@ -19,10 +18,10 @@ func RepoCommand(renderer *lipgloss.Renderer) *cobra.Command {
 340 	}
 341 
 342 	cmd.AddCommand(
 343-		blobCommand(renderer),
 344+		blobCommand(),
 345 		branchCommand(),
 346 		collabCommand(),
 347-		commitCommand(renderer),
 348+		commitCommand(),
 349 		createCommand(),
 350 		deleteCommand(),
 351 		descriptionCommand(),
 352diff --git a/pkg/ssh/middleware.go b/pkg/ssh/middleware.go
 353index 1bd93f99c0c81122f18c956056fa07cebaedee96..0bfb4c483a9bd5dfeadc23686841f0343a3e6110 100644
 354--- a/pkg/ssh/middleware.go
 355+++ b/pkg/ssh/middleware.go
 356@@ -2,7 +2,6 @@ package ssh
 357 
 358 import (
 359 	"fmt"
 360-	"os"
 361 	"time"
 362 
 363 	"github.com/charmbracelet/log"
 364@@ -14,9 +13,7 @@ import (
 365 	"github.com/charmbracelet/soft-serve/pkg/sshutils"
 366 	"github.com/charmbracelet/soft-serve/pkg/store"
 367 	"github.com/charmbracelet/ssh"
 368-	"github.com/charmbracelet/wish"
 369-	bm "github.com/charmbracelet/wish/bubbletea"
 370-	"github.com/muesli/termenv"
 371+	"github.com/charmbracelet/wish/v2"
 372 	"github.com/prometheus/client_golang/prometheus"
 373 	"github.com/prometheus/client_golang/prometheus/promauto"
 374 	"github.com/spf13/cobra"
 375@@ -91,12 +88,6 @@ func CommandMiddleware(sh ssh.Handler) ssh.Handler {
 376 		ctx := s.Context()
 377 		cfg := config.FromContext(ctx)
 378 
 379-		renderer := bm.MakeRenderer(s)
 380-		if testrun, ok := os.LookupEnv("SOFT_SERVE_NO_COLOR"); ok && testrun == "1" {
 381-			// Disable colors when running tests.
 382-			renderer.SetColorProfile(termenv.Ascii)
 383-		}
 384-
 385 		args := s.Command()
 386 		cliCommandCounter.WithLabelValues(cmd.CommandName(args)).Inc()
 387 		rootCmd := &cobra.Command{
 388@@ -111,7 +102,7 @@ func CommandMiddleware(sh ssh.Handler) ssh.Handler {
 389 			cmd.GitUploadPackCommand(),
 390 			cmd.GitUploadArchiveCommand(),
 391 			cmd.GitReceivePackCommand(),
 392-			cmd.RepoCommand(renderer),
 393+			cmd.RepoCommand(),
 394 			cmd.SettingsCommand(),
 395 			cmd.UserCommand(),
 396 			cmd.InfoCommand(),
 397diff --git a/pkg/ssh/session.go b/pkg/ssh/session.go
 398index aecd4619511e977c7367d069d2900fb4b5d1a3f1..067acb4286c9eb1526d6245192abe4b1f9c981ed 100644
 399--- a/pkg/ssh/session.go
 400+++ b/pkg/ssh/session.go
 401@@ -4,16 +4,16 @@ import (
 402 	"os"
 403 	"time"
 404 
 405-	tea "github.com/charmbracelet/bubbletea"
 406+	tea "github.com/charmbracelet/bubbletea/v2"
 407+	"github.com/charmbracelet/colorprofile"
 408 	"github.com/charmbracelet/soft-serve/pkg/access"
 409 	"github.com/charmbracelet/soft-serve/pkg/backend"
 410 	"github.com/charmbracelet/soft-serve/pkg/config"
 411 	"github.com/charmbracelet/soft-serve/pkg/proto"
 412 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 413 	"github.com/charmbracelet/ssh"
 414-	"github.com/charmbracelet/wish"
 415-	bm "github.com/charmbracelet/wish/bubbletea"
 416-	"github.com/muesli/termenv"
 417+	"github.com/charmbracelet/wish/v2"
 418+	bm "github.com/charmbracelet/wish/v2/bubbletea"
 419 	"github.com/prometheus/client_golang/prometheus"
 420 	"github.com/prometheus/client_golang/prometheus/promauto"
 421 )
 422@@ -54,15 +54,6 @@ func SessionHandler(s ssh.Session) *tea.Program {
 423 		}
 424 	}
 425 
 426-	renderer := bm.MakeRenderer(s)
 427-	if testrun, ok := os.LookupEnv("SOFT_SERVE_NO_COLOR"); ok && testrun == "1" {
 428-		// Disable colors when running tests.
 429-		renderer.SetColorProfile(termenv.Ascii)
 430-	}
 431-
 432-	c := common.NewCommon(ctx, renderer, pty.Window.Width, pty.Window.Height)
 433-	c.SetValue(common.ConfigKey, cfg)
 434-	m := NewUI(c, initialRepo)
 435 	opts := bm.MakeOptions(s)
 436 	opts = append(opts,
 437 		tea.WithAltScreen(),
 438@@ -70,6 +61,15 @@ func SessionHandler(s ssh.Session) *tea.Program {
 439 		tea.WithMouseCellMotion(),
 440 		tea.WithContext(ctx),
 441 	)
 442+
 443+	if testrun, ok := os.LookupEnv("SOFT_SERVE_NO_COLOR"); ok && testrun == "1" {
 444+		// Disable colors when running tests.
 445+		opts = append(opts, tea.WithColorProfile(colorprofile.NoTTY))
 446+	}
 447+
 448+	c := common.NewCommon(ctx, pty.Window.Width, pty.Window.Height)
 449+	c.SetValue(common.ConfigKey, cfg)
 450+	m := NewUI(c, initialRepo)
 451 	p := tea.NewProgram(m, opts...)
 452 
 453 	tuiSessionCounter.WithLabelValues(initialRepo, pty.Term).Inc()
 454diff --git a/pkg/ssh/ssh.go b/pkg/ssh/ssh.go
 455index f63b0d3897f53b95af494a99ee0a4dc00d872ddf..6ce1f7d113a8066db3c6960a0fb4ea2de37a570f 100644
 456--- a/pkg/ssh/ssh.go
 457+++ b/pkg/ssh/ssh.go
 458@@ -16,11 +16,10 @@ import (
 459 	"github.com/charmbracelet/soft-serve/pkg/db"
 460 	"github.com/charmbracelet/soft-serve/pkg/proto"
 461 	"github.com/charmbracelet/soft-serve/pkg/store"
 462-	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 463 	"github.com/charmbracelet/ssh"
 464-	"github.com/charmbracelet/wish"
 465-	bm "github.com/charmbracelet/wish/bubbletea"
 466-	rm "github.com/charmbracelet/wish/recover"
 467+	"github.com/charmbracelet/wish/v2"
 468+	bm "github.com/charmbracelet/wish/v2/bubbletea"
 469+	rm "github.com/charmbracelet/wish/v2/recover"
 470 	"github.com/prometheus/client_golang/prometheus"
 471 	"github.com/prometheus/client_golang/prometheus/promauto"
 472 	gossh "golang.org/x/crypto/ssh"
 473@@ -71,7 +70,7 @@ func NewSSHServer(ctx context.Context) (*SSHServer, error) {
 474 		rm.MiddlewareWithLogger(
 475 			logger,
 476 			// BubbleTea middleware.
 477-			bm.MiddlewareWithProgramHandler(SessionHandler, common.DefaultColorProfile),
 478+			bm.MiddlewareWithProgramHandler(SessionHandler),
 479 			// CLI middleware.
 480 			CommandMiddleware,
 481 			// Logging middleware.
 482diff --git a/pkg/ssh/ui.go b/pkg/ssh/ui.go
 483index 31f6a259cc2e677ac6b90ca2667c5ffee105cde4..f58c00398a0ffb4e7ad1f4ae3291093aa475dd9d 100644
 484--- a/pkg/ssh/ui.go
 485+++ b/pkg/ssh/ui.go
 486@@ -3,10 +3,10 @@ package ssh
 487 import (
 488 	"errors"
 489 
 490-	"github.com/charmbracelet/bubbles/key"
 491-	"github.com/charmbracelet/bubbles/list"
 492-	tea "github.com/charmbracelet/bubbletea"
 493-	"github.com/charmbracelet/lipgloss"
 494+	"github.com/charmbracelet/bubbles/v2/key"
 495+	"github.com/charmbracelet/bubbles/v2/list"
 496+	tea "github.com/charmbracelet/bubbletea/v2"
 497+	"github.com/charmbracelet/lipgloss/v2"
 498 	"github.com/charmbracelet/soft-serve/git"
 499 	"github.com/charmbracelet/soft-serve/pkg/proto"
 500 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 501@@ -203,16 +203,13 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 502 				// Always show the footer on selection page.
 503 				ui.showFooter = true
 504 			}
 505-		case tea.MouseMsg:
 506-			if msg.Action != tea.MouseActionPress {
 507-				break
 508-			}
 509-			switch msg.Button {
 510-			case tea.MouseButtonLeft:
 511-				switch {
 512-				case ui.common.Zone.Get("footer").InBounds(msg):
 513-					cmds = append(cmds, footer.ToggleFooterCmd)
 514-				}
 515+		case tea.MouseClickMsg:
 516+			switch msg.Mouse().Button {
 517+			case tea.MouseLeft:
 518+				// switch {
 519+				// case ui.common.Zone.Get("footer").InBounds(msg):
 520+				// 	cmds = append(cmds, footer.ToggleFooterCmd)
 521+				// }
 522 			}
 523 		}
 524 	case footer.ToggleFooterMsg:
 525diff --git a/pkg/ui/common/common.go b/pkg/ui/common/common.go
 526index 93d40f57328aa1d191dc5115e12b16f1f86d0c9d..df16d027c1042c3061b19fe89b655aa3cedf3081 100644
 527--- a/pkg/ui/common/common.go
 528+++ b/pkg/ui/common/common.go
 529@@ -5,7 +5,6 @@ import (
 530 	"fmt"
 531 
 532 	"github.com/alecthomas/chroma/v2/lexers"
 533-	"github.com/charmbracelet/lipgloss"
 534 	"github.com/charmbracelet/log"
 535 	"github.com/charmbracelet/soft-serve/git"
 536 	"github.com/charmbracelet/soft-serve/pkg/backend"
 537@@ -14,7 +13,6 @@ import (
 538 	"github.com/charmbracelet/soft-serve/pkg/ui/styles"
 539 	"github.com/charmbracelet/ssh"
 540 	zone "github.com/lrstanley/bubblezone"
 541-	"github.com/muesli/termenv"
 542 )
 543 
 544 type contextKey struct {
 545@@ -34,27 +32,23 @@ type Common struct {
 546 	Styles        *styles.Styles
 547 	KeyMap        *keymap.KeyMap
 548 	Zone          *zone.Manager
 549-	Renderer      *lipgloss.Renderer
 550-	Output        *termenv.Output
 551 	Logger        *log.Logger
 552 	HideCloneCmd  bool
 553 }
 554 
 555 // NewCommon returns a new Common struct.
 556-func NewCommon(ctx context.Context, out *lipgloss.Renderer, width, height int) Common {
 557+func NewCommon(ctx context.Context, width, height int) Common {
 558 	if ctx == nil {
 559 		ctx = context.TODO()
 560 	}
 561 	return Common{
 562-		ctx:      ctx,
 563-		Width:    width,
 564-		Height:   height,
 565-		Renderer: out,
 566-		Output:   out.Output(),
 567-		Styles:   styles.DefaultStyles(out),
 568-		KeyMap:   keymap.DefaultKeyMap(),
 569-		Zone:     zone.New(),
 570-		Logger:   log.FromContext(ctx).WithPrefix("ui"),
 571+		ctx:    ctx,
 572+		Width:  width,
 573+		Height: height,
 574+		Styles: styles.DefaultStyles(),
 575+		KeyMap: keymap.DefaultKeyMap(),
 576+		Zone:   zone.New(),
 577+		Logger: log.FromContext(ctx).WithPrefix("ui"),
 578 	}
 579 }
 580 
 581diff --git a/pkg/ui/common/component.go b/pkg/ui/common/component.go
 582index 6fe7761f9b9c85cabae967b5246d361cb811d363..4b152a4ba81080af791446601d06133847d80102 100644
 583--- a/pkg/ui/common/component.go
 584+++ b/pkg/ui/common/component.go
 585@@ -1,13 +1,14 @@
 586 package common
 587 
 588 import (
 589-	"github.com/charmbracelet/bubbles/help"
 590-	tea "github.com/charmbracelet/bubbletea"
 591+	"github.com/charmbracelet/bubbles/v2/help"
 592+	tea "github.com/charmbracelet/bubbletea/v2"
 593 )
 594 
 595 // Component represents a Bubble Tea model that implements a SetSize function.
 596 type Component interface {
 597 	tea.Model
 598+	tea.ViewModel
 599 	help.KeyMap
 600 	SetSize(width, height int)
 601 }
 602diff --git a/pkg/ui/common/error.go b/pkg/ui/common/error.go
 603index 753f5f26f77fc778157c2dc5d3c7b06440a29b8e..926df1d9ac7610b2a34648290e62c28a7d9d9629 100644
 604--- a/pkg/ui/common/error.go
 605+++ b/pkg/ui/common/error.go
 606@@ -3,7 +3,7 @@ package common
 607 import (
 608 	"errors"
 609 
 610-	tea "github.com/charmbracelet/bubbletea"
 611+	tea "github.com/charmbracelet/bubbletea/v2"
 612 )
 613 
 614 // ErrMissingRepo indicates that the requested repository could not be found.
 615diff --git a/pkg/ui/common/style.go b/pkg/ui/common/style.go
 616index 06d7ce3d2c9e4b61f2dd827051dd351f4bdca861..62239d0a5c9a122679362e17f6942b834eec4690 100644
 617--- a/pkg/ui/common/style.go
 618+++ b/pkg/ui/common/style.go
 619@@ -1,13 +1,14 @@
 620 package common
 621 
 622 import (
 623+	"github.com/charmbracelet/colorprofile"
 624 	gansi "github.com/charmbracelet/glamour/ansi"
 625 	"github.com/charmbracelet/glamour/styles"
 626 	"github.com/muesli/termenv"
 627 )
 628 
 629 // DefaultColorProfile is the default color profile used by the SSH server.
 630-var DefaultColorProfile = termenv.ANSI256
 631+var DefaultColorProfile = colorprofile.ANSI256
 632 
 633 func strptr(s string) *string {
 634 	return &s
 635@@ -39,7 +40,7 @@ func StyleRenderer() gansi.RenderContext {
 636 // DefaultColorProfile and styles.
 637 func StyleRendererWithStyles(styles gansi.StyleConfig) gansi.RenderContext {
 638 	return gansi.NewRenderContext(gansi.Options{
 639-		ColorProfile: DefaultColorProfile,
 640+		ColorProfile: termenv.ANSI256,
 641 		Styles:       styles,
 642 	})
 643 }
 644diff --git a/pkg/ui/components/code/code.go b/pkg/ui/components/code/code.go
 645index af7697260fc7d327f1791ded0ab84f69f8c86c72..50e56f4aff0112ac095c5788227b3c9112bfe9f0 100644
 646--- a/pkg/ui/components/code/code.go
 647+++ b/pkg/ui/components/code/code.go
 648@@ -6,10 +6,10 @@ import (
 649 	"sync"
 650 
 651 	"github.com/alecthomas/chroma/v2/lexers"
 652-	tea "github.com/charmbracelet/bubbletea"
 653+	tea "github.com/charmbracelet/bubbletea/v2"
 654 	"github.com/charmbracelet/glamour"
 655 	gansi "github.com/charmbracelet/glamour/ansi"
 656-	"github.com/charmbracelet/lipgloss"
 657+	"github.com/charmbracelet/lipgloss/v2"
 658 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 659 	vp "github.com/charmbracelet/soft-serve/pkg/ui/components/viewport"
 660 	"github.com/muesli/termenv"
 661@@ -110,7 +110,7 @@ func (r *Code) Init() tea.Cmd {
 662 
 663 	if r.sidenote != "" {
 664 		lines := strings.Split(r.sidenote, "\n")
 665-		sideNoteWidth := int(math.Ceil(float64(r.Model.Width) * r.SideNotePercent))
 666+		sideNoteWidth := int(math.Ceil(float64(r.Model.Width()) * r.SideNotePercent))
 667 		for i, l := range lines {
 668 			lines[i] = common.TruncateString(l, sideNoteWidth)
 669 		}
 670@@ -121,7 +121,7 @@ func (r *Code) Init() tea.Cmd {
 671 	// https://github.com/muesli/reflow/issues/43
 672 	//
 673 	// TODO: solve this upstream in Glamour/Reflow.
 674-	content = r.common.Renderer.NewStyle().Width(w).Render(content)
 675+	content = lipgloss.NewStyle().Width(w).Render(content)
 676 
 677 	r.Viewport.Model.SetContent(content)
 678 
 679@@ -169,26 +169,6 @@ func (r *Code) HalfViewUp() {
 680 	r.Viewport.HalfViewUp()
 681 }
 682 
 683-// ViewUp moves the viewport up by a page.
 684-func (r *Code) ViewUp() []string {
 685-	return r.Viewport.ViewUp()
 686-}
 687-
 688-// ViewDown moves the viewport down by a page.
 689-func (r *Code) ViewDown() []string {
 690-	return r.Viewport.ViewDown()
 691-}
 692-
 693-// LineUp moves the viewport up by the given number of lines.
 694-func (r *Code) LineUp(n int) []string {
 695-	return r.Viewport.LineUp(n)
 696-}
 697-
 698-// LineDown moves the viewport down by the given number of lines.
 699-func (r *Code) LineDown(n int) []string {
 700-	return r.Viewport.LineDown(n)
 701-}
 702-
 703 // ScrollPercent returns the viewport's scroll percentage.
 704 func (r *Code) ScrollPercent() float64 {
 705 	return r.Viewport.ScrollPercent()
 706diff --git a/pkg/ui/components/footer/footer.go b/pkg/ui/components/footer/footer.go
 707index 44c800f420e1913c72f1289a00b7d273269a8247..05e1160dedbe717a213de8907fbbb65dccc5bd4e 100644
 708--- a/pkg/ui/components/footer/footer.go
 709+++ b/pkg/ui/components/footer/footer.go
 710@@ -1,10 +1,10 @@
 711 package footer
 712 
 713 import (
 714-	"github.com/charmbracelet/bubbles/help"
 715-	"github.com/charmbracelet/bubbles/key"
 716-	tea "github.com/charmbracelet/bubbletea"
 717-	"github.com/charmbracelet/lipgloss"
 718+	"github.com/charmbracelet/bubbles/v2/help"
 719+	"github.com/charmbracelet/bubbles/v2/key"
 720+	tea "github.com/charmbracelet/bubbletea/v2"
 721+	"github.com/charmbracelet/lipgloss/v2"
 722 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 723 )
 724 
 725diff --git a/pkg/ui/components/header/header.go b/pkg/ui/components/header/header.go
 726index 7de511426f6da83252b84884c4fa3a64fd992e65..63e47a4911f6618c31448cdbc8c902e80314be11 100644
 727--- a/pkg/ui/components/header/header.go
 728+++ b/pkg/ui/components/header/header.go
 729@@ -3,7 +3,7 @@ package header
 730 import (
 731 	"strings"
 732 
 733-	tea "github.com/charmbracelet/bubbletea"
 734+	tea "github.com/charmbracelet/bubbletea/v2"
 735 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 736 )
 737 
 738diff --git a/pkg/ui/components/selector/selector.go b/pkg/ui/components/selector/selector.go
 739index 72239d4083847a2ccd72c7d0e10ea9bfa90e10ba..45cb031b8bd9b3ff8c928dbe07edf9b4628f5bf3 100644
 740--- a/pkg/ui/components/selector/selector.go
 741+++ b/pkg/ui/components/selector/selector.go
 742@@ -3,9 +3,9 @@ package selector
 743 import (
 744 	"sync"
 745 
 746-	"github.com/charmbracelet/bubbles/key"
 747-	"github.com/charmbracelet/bubbles/list"
 748-	tea "github.com/charmbracelet/bubbletea"
 749+	"github.com/charmbracelet/bubbles/v2/key"
 750+	"github.com/charmbracelet/bubbles/v2/list"
 751+	tea "github.com/charmbracelet/bubbletea/v2"
 752 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 753 )
 754 
 755@@ -230,29 +230,27 @@ func (s *Selector) Init() tea.Cmd {
 756 func (s *Selector) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 757 	cmds := make([]tea.Cmd, 0)
 758 	switch msg := msg.(type) {
 759-	case tea.MouseMsg:
 760-		if msg.Action != tea.MouseActionPress {
 761-			break
 762-		}
 763-		switch msg.Button {
 764-		case tea.MouseButtonWheelUp:
 765+	case tea.MouseClickMsg:
 766+		m := msg.Mouse()
 767+		switch m.Button {
 768+		case tea.MouseWheelUp:
 769 			s.CursorUp()
 770-		case tea.MouseButtonWheelDown:
 771+		case tea.MouseWheelDown:
 772 			s.CursorDown()
 773-		case tea.MouseButtonLeft:
 774-			curIdx := s.Index()
 775-			for i, item := range s.Items() {
 776-				item, _ := item.(IdentifiableItem)
 777-				// Check each item to see if it's in bounds.
 778-				if item != nil && s.common.Zone.Get(item.ID()).InBounds(msg) {
 779-					if i == curIdx {
 780-						cmds = append(cmds, s.SelectItemCmd)
 781-					} else {
 782-						s.Select(i)
 783-					}
 784-					break
 785-				}
 786-			}
 787+		case tea.MouseLeft:
 788+			// curIdx := s.Index()
 789+			// for i, item := range s.Items() {
 790+			// 	item, _ := item.(IdentifiableItem)
 791+			// 	// Check each item to see if it's in bounds.
 792+			// 	if item != nil && s.common.Zone.Get(item.ID()).InBounds(msg) {
 793+			// 		if i == curIdx {
 794+			// 			cmds = append(cmds, s.SelectItemCmd)
 795+			// 		} else {
 796+			// 			s.Select(i)
 797+			// 		}
 798+			// 		break
 799+			// 	}
 800+			// }
 801 		}
 802 	case tea.KeyMsg:
 803 		filterState := s.FilterState()
 804diff --git a/pkg/ui/components/statusbar/statusbar.go b/pkg/ui/components/statusbar/statusbar.go
 805index 20a5735e12da1729616ea9ead98ccfcadfda6a47..f913e644a2230ea4a3d6f1b46f9e7850de45a02e 100644
 806--- a/pkg/ui/components/statusbar/statusbar.go
 807+++ b/pkg/ui/components/statusbar/statusbar.go
 808@@ -1,10 +1,10 @@
 809 package statusbar
 810 
 811 import (
 812-	tea "github.com/charmbracelet/bubbletea"
 813-	"github.com/charmbracelet/lipgloss"
 814+	tea "github.com/charmbracelet/bubbletea/v2"
 815+	"github.com/charmbracelet/lipgloss/v2"
 816 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 817-	"github.com/muesli/reflow/truncate"
 818+	"github.com/charmbracelet/x/ansi"
 819 )
 820 
 821 // Model is a status bar model.
 822@@ -75,12 +75,12 @@ func (s *Model) View() string {
 823 	}
 824 	branch := st.StatusBarBranch.Render(s.extra)
 825 	maxWidth := s.common.Width - w(key) - w(info) - w(branch) - w(help)
 826-	v := truncate.StringWithTail(s.value, uint(maxWidth-st.StatusBarValue.GetHorizontalFrameSize()), "…") //nolint:gosec
 827+	v := ansi.Truncate(s.value, maxWidth-st.StatusBarValue.GetHorizontalFrameSize(), "…")
 828 	value := st.StatusBarValue.
 829 		Width(maxWidth).
 830 		Render(v)
 831 
 832-	return s.common.Renderer.NewStyle().MaxWidth(s.common.Width).
 833+	return lipgloss.NewStyle().MaxWidth(s.common.Width).
 834 		Render(
 835 			lipgloss.JoinHorizontal(lipgloss.Top,
 836 				key,
 837diff --git a/pkg/ui/components/tabs/tabs.go b/pkg/ui/components/tabs/tabs.go
 838index bd31566216326910b991f732813daef756e733f2..2756595a361de3a4b2505bb86311ba8a7adfbd27 100644
 839--- a/pkg/ui/components/tabs/tabs.go
 840+++ b/pkg/ui/components/tabs/tabs.go
 841@@ -3,8 +3,8 @@ package tabs
 842 import (
 843 	"strings"
 844 
 845-	tea "github.com/charmbracelet/bubbletea"
 846-	"github.com/charmbracelet/lipgloss"
 847+	tea "github.com/charmbracelet/bubbletea/v2"
 848+	"github.com/charmbracelet/lipgloss/v2"
 849 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 850 )
 851 
 852@@ -63,18 +63,15 @@ func (t *Tabs) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 853 			t.activeTab = (t.activeTab - 1 + len(t.tabs)) % len(t.tabs)
 854 			cmds = append(cmds, t.activeTabCmd)
 855 		}
 856-	case tea.MouseMsg:
 857-		if msg.Action != tea.MouseActionPress {
 858-			break
 859-		}
 860+	case tea.MouseClickMsg:
 861 		switch msg.Button {
 862-		case tea.MouseButtonLeft:
 863-			for i, tab := range t.tabs {
 864-				if t.common.Zone.Get(tab).InBounds(msg) {
 865-					t.activeTab = i
 866-					cmds = append(cmds, t.activeTabCmd)
 867-				}
 868-			}
 869+		case tea.MouseLeft:
 870+			// for i, tab := range t.tabs {
 871+			// 	if t.common.Zone.Get(tab).InBounds(msg) {
 872+			// 		t.activeTab = i
 873+			// 		cmds = append(cmds, t.activeTabCmd)
 874+			// 	}
 875+			// }
 876 		}
 877 	case SelectTabMsg:
 878 		tab := int(msg)
 879@@ -109,7 +106,7 @@ func (t *Tabs) View() string {
 880 			s.WriteString(sep.String())
 881 		}
 882 	}
 883-	return t.common.Renderer.NewStyle().
 884+	return lipgloss.NewStyle().
 885 		MaxWidth(t.common.Width).
 886 		Render(s.String())
 887 }
 888diff --git a/pkg/ui/components/viewport/viewport.go b/pkg/ui/components/viewport/viewport.go
 889index cd7b6c90951fc4293581ac976eef6dcbf067f637..c2355ae76b143eac896bd26ffed00e561bbab73e 100644
 890--- a/pkg/ui/components/viewport/viewport.go
 891+++ b/pkg/ui/components/viewport/viewport.go
 892@@ -1,9 +1,9 @@
 893 package viewport
 894 
 895 import (
 896-	"github.com/charmbracelet/bubbles/key"
 897-	"github.com/charmbracelet/bubbles/viewport"
 898-	tea "github.com/charmbracelet/bubbletea"
 899+	"github.com/charmbracelet/bubbles/v2/key"
 900+	"github.com/charmbracelet/bubbles/v2/viewport"
 901+	tea "github.com/charmbracelet/bubbletea/v2"
 902 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 903 )
 904 
 905@@ -15,7 +15,9 @@ type Viewport struct {
 906 
 907 // New returns a new Viewport.
 908 func New(c common.Common) *Viewport {
 909-	vp := viewport.New(c.Width, c.Height)
 910+	vp := viewport.New()
 911+	vp.SetWidth(c.Width)
 912+	vp.SetHeight(c.Height)
 913 	vp.MouseWheelEnabled = true
 914 	return &Viewport{
 915 		common: c,
 916@@ -26,8 +28,8 @@ func New(c common.Common) *Viewport {
 917 // SetSize implements common.Component.
 918 func (v *Viewport) SetSize(width, height int) {
 919 	v.common.SetSize(width, height)
 920-	v.Model.Width = width
 921-	v.Model.Height = height
 922+	v.Model.SetWidth(width)
 923+	v.Model.SetHeight(height)
 924 }
 925 
 926 // Init implements tea.Model.
 927@@ -81,26 +83,6 @@ func (v *Viewport) HalfViewUp() {
 928 	v.Model.HalfViewUp()
 929 }
 930 
 931-// ViewUp moves the viewport up by a page.
 932-func (v *Viewport) ViewUp() []string {
 933-	return v.Model.ViewUp()
 934-}
 935-
 936-// ViewDown moves the viewport down by a page.
 937-func (v *Viewport) ViewDown() []string {
 938-	return v.Model.ViewDown()
 939-}
 940-
 941-// LineUp moves the viewport up by the given number of lines.
 942-func (v *Viewport) LineUp(n int) []string {
 943-	return v.Model.LineUp(n)
 944-}
 945-
 946-// LineDown moves the viewport down by the given number of lines.
 947-func (v *Viewport) LineDown(n int) []string {
 948-	return v.Model.LineDown(n)
 949-}
 950-
 951 // ScrollPercent returns the viewport's scroll percentage.
 952 func (v *Viewport) ScrollPercent() float64 {
 953 	return v.Model.ScrollPercent()
 954diff --git a/pkg/ui/keymap/keymap.go b/pkg/ui/keymap/keymap.go
 955index ae09eaad7344c615d91fd92c92c1b36d634341a1..ad5d8fa736c7c9bda1c8b534d15fed084602ef76 100644
 956--- a/pkg/ui/keymap/keymap.go
 957+++ b/pkg/ui/keymap/keymap.go
 958@@ -1,6 +1,6 @@
 959 package keymap
 960 
 961-import "github.com/charmbracelet/bubbles/key"
 962+import "github.com/charmbracelet/bubbles/v2/key"
 963 
 964 // KeyMap is a map of key bindings for the UI.
 965 type KeyMap struct {
 966diff --git a/pkg/ui/pages/repo/files.go b/pkg/ui/pages/repo/files.go
 967index fd43969fe7d07644542e105985a6081c18b34b58..7a73750b06abe63897a5f2df7688b262be516222 100644
 968--- a/pkg/ui/pages/repo/files.go
 969+++ b/pkg/ui/pages/repo/files.go
 970@@ -7,9 +7,9 @@ import (
 971 	"strings"
 972 
 973 	gitm "github.com/aymanbagabas/git-module"
 974-	"github.com/charmbracelet/bubbles/key"
 975-	"github.com/charmbracelet/bubbles/spinner"
 976-	tea "github.com/charmbracelet/bubbletea"
 977+	"github.com/charmbracelet/bubbles/v2/key"
 978+	"github.com/charmbracelet/bubbles/v2/spinner"
 979+	tea "github.com/charmbracelet/bubbletea/v2"
 980 	"github.com/charmbracelet/soft-serve/git"
 981 	"github.com/charmbracelet/soft-serve/pkg/proto"
 982 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 983diff --git a/pkg/ui/pages/repo/filesitem.go b/pkg/ui/pages/repo/filesitem.go
 984index ca399fa1b74986f152d7005919048f2ab79762e0..d821eb74d3728f72e04f4cf42245fe76182d4045 100644
 985--- a/pkg/ui/pages/repo/filesitem.go
 986+++ b/pkg/ui/pages/repo/filesitem.go
 987@@ -6,10 +6,10 @@ import (
 988 	"io/fs"
 989 	"strings"
 990 
 991-	"github.com/charmbracelet/bubbles/key"
 992-	"github.com/charmbracelet/bubbles/list"
 993-	tea "github.com/charmbracelet/bubbletea"
 994-	"github.com/charmbracelet/lipgloss"
 995+	"github.com/charmbracelet/bubbles/v2/key"
 996+	"github.com/charmbracelet/bubbles/v2/list"
 997+	tea "github.com/charmbracelet/bubbletea/v2"
 998+	"github.com/charmbracelet/lipgloss/v2"
 999 	"github.com/charmbracelet/soft-serve/git"
1000 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1001 	"github.com/dustin/go-humanize"
1002@@ -139,7 +139,7 @@ func (d FileItemDelegate) Render(w io.Writer, m list.Model, index int, listItem
1003 	name = nameStyle.Render(name)
1004 	size = sizeStyle.Render(size)
1005 	modeStr := modeStyle.Render(mode.String())
1006-	truncate := d.common.Renderer.NewStyle().MaxWidth(m.Width() -
1007+	truncate := lipgloss.NewStyle().MaxWidth(m.Width() -
1008 		s.Selector.GetHorizontalFrameSize() -
1009 		s.Selector.GetWidth())
1010 	//nolint:errcheck
1011diff --git a/pkg/ui/pages/repo/log.go b/pkg/ui/pages/repo/log.go
1012index 8de8aa78d07c45ecc49529487d2729e089240813..1caabb4d6ba4ff42d6f91df56c723809c97bb8d4 100644
1013--- a/pkg/ui/pages/repo/log.go
1014+++ b/pkg/ui/pages/repo/log.go
1015@@ -5,11 +5,11 @@ import (
1016 	"strings"
1017 	"time"
1018 
1019-	"github.com/charmbracelet/bubbles/key"
1020-	"github.com/charmbracelet/bubbles/spinner"
1021-	tea "github.com/charmbracelet/bubbletea"
1022+	"github.com/charmbracelet/bubbles/v2/key"
1023+	"github.com/charmbracelet/bubbles/v2/spinner"
1024+	tea "github.com/charmbracelet/bubbletea/v2"
1025 	gansi "github.com/charmbracelet/glamour/ansi"
1026-	"github.com/charmbracelet/lipgloss"
1027+	"github.com/charmbracelet/lipgloss/v2"
1028 	"github.com/charmbracelet/soft-serve/git"
1029 	"github.com/charmbracelet/soft-serve/pkg/proto"
1030 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1031diff --git a/pkg/ui/pages/repo/logitem.go b/pkg/ui/pages/repo/logitem.go
1032index 0ff4da6814fd62c8be3fe4e9f4a741ace3cda6ca..d95fe865bb0987c2ed09f2a6bc1a3f7ff922d71e 100644
1033--- a/pkg/ui/pages/repo/logitem.go
1034+++ b/pkg/ui/pages/repo/logitem.go
1035@@ -6,10 +6,10 @@ import (
1036 	"strings"
1037 	"time"
1038 
1039-	"github.com/charmbracelet/bubbles/key"
1040-	"github.com/charmbracelet/bubbles/list"
1041-	tea "github.com/charmbracelet/bubbletea"
1042-	"github.com/charmbracelet/lipgloss"
1043+	"github.com/charmbracelet/bubbles/v2/key"
1044+	"github.com/charmbracelet/bubbles/v2/list"
1045+	tea "github.com/charmbracelet/bubbletea/v2"
1046+	"github.com/charmbracelet/lipgloss/v2"
1047 	"github.com/charmbracelet/soft-serve/git"
1048 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1049 	"github.com/muesli/reflow/truncate"
1050diff --git a/pkg/ui/pages/repo/readme.go b/pkg/ui/pages/repo/readme.go
1051index 74c5be2f4dd163466972c31704b130c20d5c3083..8978effcda5e4cac341c95007da99f54f3d926f8 100644
1052--- a/pkg/ui/pages/repo/readme.go
1053+++ b/pkg/ui/pages/repo/readme.go
1054@@ -4,9 +4,9 @@ import (
1055 	"fmt"
1056 	"path/filepath"
1057 
1058-	"github.com/charmbracelet/bubbles/key"
1059-	"github.com/charmbracelet/bubbles/spinner"
1060-	tea "github.com/charmbracelet/bubbletea"
1061+	"github.com/charmbracelet/bubbles/v2/key"
1062+	"github.com/charmbracelet/bubbles/v2/spinner"
1063+	tea "github.com/charmbracelet/bubbletea/v2"
1064 	"github.com/charmbracelet/soft-serve/pkg/backend"
1065 	"github.com/charmbracelet/soft-serve/pkg/proto"
1066 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1067diff --git a/pkg/ui/pages/repo/refs.go b/pkg/ui/pages/repo/refs.go
1068index f6d9c56d84303d66f9e94aa5aa44e51afb367523..f30f8f735d0496d03aba55d08d670e939674c38c 100644
1069--- a/pkg/ui/pages/repo/refs.go
1070+++ b/pkg/ui/pages/repo/refs.go
1071@@ -5,9 +5,9 @@ import (
1072 	"sort"
1073 	"strings"
1074 
1075-	"github.com/charmbracelet/bubbles/key"
1076-	"github.com/charmbracelet/bubbles/spinner"
1077-	tea "github.com/charmbracelet/bubbletea"
1078+	"github.com/charmbracelet/bubbles/v2/key"
1079+	"github.com/charmbracelet/bubbles/v2/spinner"
1080+	tea "github.com/charmbracelet/bubbletea/v2"
1081 	"github.com/charmbracelet/soft-serve/git"
1082 	"github.com/charmbracelet/soft-serve/pkg/proto"
1083 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1084diff --git a/pkg/ui/pages/repo/refsitem.go b/pkg/ui/pages/repo/refsitem.go
1085index 7bb10d6e94fb4013789f6c4f4c4dfd7935d8d09d..aaed5e7e51afbadf03579074d43670acd6438879 100644
1086--- a/pkg/ui/pages/repo/refsitem.go
1087+++ b/pkg/ui/pages/repo/refsitem.go
1088@@ -6,10 +6,10 @@ import (
1089 	"strings"
1090 	"time"
1091 
1092-	"github.com/charmbracelet/bubbles/key"
1093-	"github.com/charmbracelet/bubbles/list"
1094-	tea "github.com/charmbracelet/bubbletea"
1095-	"github.com/charmbracelet/lipgloss"
1096+	"github.com/charmbracelet/bubbles/v2/key"
1097+	"github.com/charmbracelet/bubbles/v2/list"
1098+	tea "github.com/charmbracelet/bubbletea/v2"
1099+	"github.com/charmbracelet/lipgloss/v2"
1100 	"github.com/charmbracelet/soft-serve/git"
1101 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1102 	"github.com/dustin/go-humanize"
1103diff --git a/pkg/ui/pages/repo/repo.go b/pkg/ui/pages/repo/repo.go
1104index adeaeed35ccd33460a5358895c286c6b43e70de2..b68c8084a359087e223a1c082aaa1fdfdce36f82 100644
1105--- a/pkg/ui/pages/repo/repo.go
1106+++ b/pkg/ui/pages/repo/repo.go
1107@@ -4,15 +4,14 @@ import (
1108 	"fmt"
1109 	"strings"
1110 
1111-	"github.com/charmbracelet/bubbles/help"
1112-	"github.com/charmbracelet/bubbles/key"
1113-	"github.com/charmbracelet/bubbles/spinner"
1114-	tea "github.com/charmbracelet/bubbletea"
1115-	"github.com/charmbracelet/lipgloss"
1116+	"github.com/charmbracelet/bubbles/v2/help"
1117+	"github.com/charmbracelet/bubbles/v2/key"
1118+	"github.com/charmbracelet/bubbles/v2/spinner"
1119+	tea "github.com/charmbracelet/bubbletea/v2"
1120+	"github.com/charmbracelet/lipgloss/v2"
1121 	"github.com/charmbracelet/soft-serve/git"
1122 	"github.com/charmbracelet/soft-serve/pkg/proto"
1123 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1124-	"github.com/charmbracelet/soft-serve/pkg/ui/components/footer"
1125 	"github.com/charmbracelet/soft-serve/pkg/ui/components/selector"
1126 	"github.com/charmbracelet/soft-serve/pkg/ui/components/statusbar"
1127 	"github.com/charmbracelet/soft-serve/pkg/ui/components/tabs"
1128@@ -177,29 +176,26 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
1129 		if cmd != nil {
1130 			cmds = append(cmds, cmd)
1131 		}
1132-		if r.selectedRepo != nil {
1133-			urlID := fmt.Sprintf("%s-url", r.selectedRepo.Name())
1134-			cmd := r.common.CloneCmd(r.common.Config().SSH.PublicURL, r.selectedRepo.Name())
1135-			if msg, ok := msg.(tea.MouseMsg); ok && r.common.Zone.Get(urlID).InBounds(msg) {
1136-				cmds = append(cmds, copyCmd(cmd, "Command copied to clipboard"))
1137-			}
1138-		}
1139+		// if r.selectedRepo != nil {
1140+		// 	urlID := fmt.Sprintf("%s-url", r.selectedRepo.Name())
1141+		// 	cmd := r.common.CloneCmd(r.common.Config().SSH.PublicURL, r.selectedRepo.Name())
1142+		// 	if msg, ok := msg.(tea.MouseMsg); ok && r.common.Zone.Get(urlID).InBounds(msg) {
1143+		// 		cmds = append(cmds, copyCmd(cmd, "Command copied to clipboard"))
1144+		// 	}
1145+		// }
1146 		switch msg := msg.(type) {
1147-		case tea.MouseMsg:
1148-			if msg.Action != tea.MouseActionPress {
1149-				break
1150-			}
1151+		case tea.MouseClickMsg:
1152 			switch msg.Button {
1153-			case tea.MouseButtonLeft:
1154-				switch {
1155-				case r.common.Zone.Get("repo-help").InBounds(msg):
1156-					cmds = append(cmds, footer.ToggleFooterCmd)
1157-				}
1158-			case tea.MouseButtonRight:
1159-				switch {
1160-				case r.common.Zone.Get("repo-main").InBounds(msg):
1161-					cmds = append(cmds, goBackCmd)
1162-				}
1163+			case tea.MouseLeft:
1164+				// switch {
1165+				// case r.common.Zone.Get("repo-help").InBounds(msg):
1166+				// 	cmds = append(cmds, footer.ToggleFooterCmd)
1167+				// }
1168+			case tea.MouseRight:
1169+				// switch {
1170+				// case r.common.Zone.Get("repo-main").InBounds(msg):
1171+				// 	cmds = append(cmds, goBackCmd)
1172+				// }
1173 			}
1174 		}
1175 		switch msg := msg.(type) {
1176@@ -212,7 +208,7 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
1177 	case CopyMsg:
1178 		txt := msg.Text
1179 		if cfg := r.common.Config(); cfg != nil {
1180-			r.common.Output.Copy(txt)
1181+			cmds = append(cmds, tea.SetClipboard(txt))
1182 		}
1183 		r.statusbar.SetStatus("", msg.Message, "", "")
1184 	case ReadmeMsg:
1185@@ -326,7 +322,7 @@ func (r *Repo) headerView() string {
1186 	if r.selectedRepo == nil {
1187 		return ""
1188 	}
1189-	truncate := r.common.Renderer.NewStyle().MaxWidth(r.common.Width)
1190+	truncate := lipgloss.NewStyle().MaxWidth(r.common.Width)
1191 	header := r.selectedRepo.ProjectName()
1192 	if header == "" {
1193 		header = r.selectedRepo.Name()
1194diff --git a/pkg/ui/pages/repo/stash.go b/pkg/ui/pages/repo/stash.go
1195index 667026b7baebc1556a3572049b6a4fb24b40e80a..37f8a1a85b5341a56884f0cb71c44e71dcc88c4d 100644
1196--- a/pkg/ui/pages/repo/stash.go
1197+++ b/pkg/ui/pages/repo/stash.go
1198@@ -4,10 +4,10 @@ import (
1199 	"fmt"
1200 
1201 	gitm "github.com/aymanbagabas/git-module"
1202-	"github.com/charmbracelet/bubbles/key"
1203-	"github.com/charmbracelet/bubbles/spinner"
1204-	tea "github.com/charmbracelet/bubbletea"
1205-	"github.com/charmbracelet/lipgloss"
1206+	"github.com/charmbracelet/bubbles/v2/key"
1207+	"github.com/charmbracelet/bubbles/v2/spinner"
1208+	tea "github.com/charmbracelet/bubbletea/v2"
1209+	"github.com/charmbracelet/lipgloss/v2"
1210 	"github.com/charmbracelet/soft-serve/git"
1211 	"github.com/charmbracelet/soft-serve/pkg/proto"
1212 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1213diff --git a/pkg/ui/pages/repo/stashitem.go b/pkg/ui/pages/repo/stashitem.go
1214index e6b6a9a822f12310c28b12d7be33e4ce4f517e92..77dd1a9274ff09fabdd1afff63f558236658eed3 100644
1215--- a/pkg/ui/pages/repo/stashitem.go
1216+++ b/pkg/ui/pages/repo/stashitem.go
1217@@ -5,9 +5,9 @@ import (
1218 	"io"
1219 
1220 	gitm "github.com/aymanbagabas/git-module"
1221-	"github.com/charmbracelet/bubbles/key"
1222-	"github.com/charmbracelet/bubbles/list"
1223-	tea "github.com/charmbracelet/bubbletea"
1224+	"github.com/charmbracelet/bubbles/v2/key"
1225+	"github.com/charmbracelet/bubbles/v2/list"
1226+	tea "github.com/charmbracelet/bubbletea/v2"
1227 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1228 )
1229 
1230diff --git a/pkg/ui/pages/selection/item.go b/pkg/ui/pages/selection/item.go
1231index 1842d7243ac18ac997861a130d5ffc9efb34d51e..4b338bda5d68c44bbf1c7e2a11c38dedda0811f0 100644
1232--- a/pkg/ui/pages/selection/item.go
1233+++ b/pkg/ui/pages/selection/item.go
1234@@ -7,10 +7,10 @@ import (
1235 	"strings"
1236 	"time"
1237 
1238-	"github.com/charmbracelet/bubbles/key"
1239-	"github.com/charmbracelet/bubbles/list"
1240-	tea "github.com/charmbracelet/bubbletea"
1241-	"github.com/charmbracelet/lipgloss"
1242+	"github.com/charmbracelet/bubbles/v2/key"
1243+	"github.com/charmbracelet/bubbles/v2/list"
1244+	tea "github.com/charmbracelet/bubbletea/v2"
1245+	"github.com/charmbracelet/lipgloss/v2"
1246 	"github.com/charmbracelet/soft-serve/pkg/proto"
1247 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1248 	"github.com/dustin/go-humanize"
1249@@ -139,8 +139,10 @@ func (d *ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
1250 		switch {
1251 		case key.Matches(msg, d.common.KeyMap.Copy):
1252 			d.copiedIdx = idx
1253-			d.common.Output.Copy(item.Command())
1254-			return m.SetItem(idx, item)
1255+			return tea.Batch(
1256+				tea.SetClipboard(item.Command()),
1257+				m.SetItem(idx, item),
1258+			)
1259 		}
1260 	}
1261 	return nil
1262diff --git a/pkg/ui/pages/selection/selection.go b/pkg/ui/pages/selection/selection.go
1263index 7144244c710c089626502be333b872b22a208ea7..3ced323a69c692f446d2988ee54347aab9a1c79d 100644
1264--- a/pkg/ui/pages/selection/selection.go
1265+++ b/pkg/ui/pages/selection/selection.go
1266@@ -4,10 +4,10 @@ import (
1267 	"fmt"
1268 	"sort"
1269 
1270-	"github.com/charmbracelet/bubbles/key"
1271-	"github.com/charmbracelet/bubbles/list"
1272-	tea "github.com/charmbracelet/bubbletea"
1273-	"github.com/charmbracelet/lipgloss"
1274+	"github.com/charmbracelet/bubbles/v2/key"
1275+	"github.com/charmbracelet/bubbles/v2/list"
1276+	tea "github.com/charmbracelet/bubbletea/v2"
1277+	"github.com/charmbracelet/lipgloss/v2"
1278 	"github.com/charmbracelet/soft-serve/pkg/access"
1279 	"github.com/charmbracelet/soft-serve/pkg/backend"
1280 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
1281@@ -51,7 +51,7 @@ func New(c common.Common) *Selection {
1282 		ts[i] = b.String()
1283 	}
1284 	t := tabs.New(c, ts)
1285-	t.TabSeparator = c.Renderer.NewStyle()
1286+	t.TabSeparator = lipgloss.NewStyle()
1287 	t.TabInactive = c.Styles.TopLevelNormalTab
1288 	t.TabActive = c.Styles.TopLevelActiveTab
1289 	t.TabDot = c.Styles.TopLevelActiveTabDot
1290@@ -289,15 +289,15 @@ func (s *Selection) View() string {
1291 	wm, hm := s.getMargins()
1292 	switch s.activePane {
1293 	case selectorPane:
1294-		ss := s.common.Renderer.NewStyle().
1295+		ss := lipgloss.NewStyle().
1296 			Width(s.common.Width - wm).
1297 			Height(s.common.Height - hm)
1298 		view = ss.Render(s.selector.View())
1299 	case readmePane:
1300-		rs := s.common.Renderer.NewStyle().
1301+		rs := lipgloss.NewStyle().
1302 			Height(s.common.Height - hm)
1303 		status := fmt.Sprintf("☰ %.f%%", s.readme.ScrollPercent()*100)
1304-		readmeStatus := s.common.Renderer.NewStyle().
1305+		readmeStatus := lipgloss.NewStyle().
1306 			Align(lipgloss.Right).
1307 			Width(s.common.Width - wm).
1308 			Foreground(s.common.Styles.InactiveBorderColor).
1309diff --git a/pkg/ui/styles/styles.go b/pkg/ui/styles/styles.go
1310index 293b152a2027e9a659242a4219ed7ecbaebaa215..46b8cff040150d5be3d9d98d72a6370bd47c59eb 100644
1311--- a/pkg/ui/styles/styles.go
1312+++ b/pkg/ui/styles/styles.go
1313@@ -1,7 +1,9 @@
1314 package styles
1315 
1316 import (
1317-	"github.com/charmbracelet/lipgloss"
1318+	"image/color"
1319+
1320+	"github.com/charmbracelet/lipgloss/v2"
1321 )
1322 
1323 // XXX: For now, this is in its own package so that it can be shared between
1324@@ -9,8 +11,8 @@ import (
1325 
1326 // Styles defines styles for the UI.
1327 type Styles struct {
1328-	ActiveBorderColor   lipgloss.Color
1329-	InactiveBorderColor lipgloss.Color
1330+	ActiveBorderColor   color.Color
1331+	InactiveBorderColor color.Color
1332 
1333 	App                  lipgloss.Style
1334 	ServerName           lipgloss.Style
1335@@ -165,7 +167,7 @@ type Styles struct {
1336 }
1337 
1338 // DefaultStyles returns default styles for the UI.
1339-func DefaultStyles(r *lipgloss.Renderer) *Styles {
1340+func DefaultStyles() *Styles {
1341 	highlightColor := lipgloss.Color("210")
1342 	highlightColorDim := lipgloss.Color("174")
1343 	selectorColor := lipgloss.Color("167")
1344@@ -176,10 +178,10 @@ func DefaultStyles(r *lipgloss.Renderer) *Styles {
1345 	s.ActiveBorderColor = lipgloss.Color("62")
1346 	s.InactiveBorderColor = lipgloss.Color("241")
1347 
1348-	s.App = r.NewStyle().
1349+	s.App = lipgloss.NewStyle().
1350 		Margin(1, 2)
1351 
1352-	s.ServerName = r.NewStyle().
1353+	s.ServerName = lipgloss.NewStyle().
1354 		Height(1).
1355 		MarginLeft(1).
1356 		MarginBottom(1).
1357@@ -188,29 +190,29 @@ func DefaultStyles(r *lipgloss.Renderer) *Styles {
1358 		Foreground(lipgloss.Color("229")).
1359 		Bold(true)
1360 
1361-	s.TopLevelNormalTab = r.NewStyle().
1362+	s.TopLevelNormalTab = lipgloss.NewStyle().
1363 		MarginRight(2)
1364 
1365 	s.TopLevelActiveTab = s.TopLevelNormalTab.
1366 		Foreground(lipgloss.Color("36"))
1367 
1368-	s.TopLevelActiveTabDot = r.NewStyle().
1369+	s.TopLevelActiveTabDot = lipgloss.NewStyle().
1370 		Foreground(lipgloss.Color("36"))
1371 
1372-	s.RepoSelector.Normal.Base = r.NewStyle().
1373+	s.RepoSelector.Normal.Base = lipgloss.NewStyle().
1374 		PaddingLeft(1).
1375 		Border(lipgloss.Border{Left: " "}, false, false, false, true).
1376 		Height(3)
1377 
1378-	s.RepoSelector.Normal.Title = r.NewStyle().Bold(true)
1379+	s.RepoSelector.Normal.Title = lipgloss.NewStyle().Bold(true)
1380 
1381-	s.RepoSelector.Normal.Desc = r.NewStyle().
1382+	s.RepoSelector.Normal.Desc = lipgloss.NewStyle().
1383 		Foreground(lipgloss.Color("243"))
1384 
1385-	s.RepoSelector.Normal.Command = r.NewStyle().
1386+	s.RepoSelector.Normal.Command = lipgloss.NewStyle().
1387 		Foreground(lipgloss.Color("132"))
1388 
1389-	s.RepoSelector.Normal.Updated = r.NewStyle().
1390+	s.RepoSelector.Normal.Updated = lipgloss.NewStyle().
1391 		Foreground(lipgloss.Color("243"))
1392 
1393 	s.RepoSelector.Active.Base = s.RepoSelector.Normal.Base.
1394@@ -229,78 +231,78 @@ func DefaultStyles(r *lipgloss.Renderer) *Styles {
1395 	s.RepoSelector.Active.Command = s.RepoSelector.Normal.Command.
1396 		Foreground(lipgloss.Color("204"))
1397 
1398-	s.MenuItem = r.NewStyle().
1399+	s.MenuItem = lipgloss.NewStyle().
1400 		PaddingLeft(1).
1401 		Border(lipgloss.Border{
1402 			Left: " ",
1403 		}, false, false, false, true).
1404 		Height(3)
1405 
1406-	s.MenuLastUpdate = r.NewStyle().
1407+	s.MenuLastUpdate = lipgloss.NewStyle().
1408 		Foreground(lipgloss.Color("241")).
1409 		Align(lipgloss.Right)
1410 
1411-	s.Repo.Base = r.NewStyle()
1412+	s.Repo.Base = lipgloss.NewStyle()