repos / pico

pico services - prose.sh, pastes.sh, imgs.sh, feeds.sh, pgs.sh
git clone https://github.com/picosh/pico.git

commit
8a5c190
parent
d0dbcaf
author
Eric Bower
date
2024-05-16 14:43:05 +0000 UTC
refactor: tui pages
12 files changed,  +69, -159
M tui/account/create.go
+2, -14
 1@@ -5,7 +5,6 @@ import (
 2 	"fmt"
 3 	"strings"
 4 
 5-	"github.com/charmbracelet/bubbles/spinner"
 6 	input "github.com/charmbracelet/bubbles/textinput"
 7 	tea "github.com/charmbracelet/bubbletea"
 8 	"github.com/charmbracelet/ssh"
 9@@ -58,7 +57,6 @@ type CreateModel struct {
10 	index     index
11 	errMsg    string
12 	input     input.Model
13-	spinner   spinner.Model
14 }
15 
16 // updateFocus updates the focused states in the model based on the current
17@@ -112,7 +110,6 @@ func NewCreateModel(styles common.Styles, dbpool db.DB, publicKey ssh.PublicKey)
18 		index:     textInput,
19 		errMsg:    "",
20 		input:     im,
21-		spinner:   common.NewSpinner(styles),
22 		publicKey: publicKey,
23 	}
24 }
25@@ -174,10 +171,7 @@ func Update(msg tea.Msg, m CreateModel) (CreateModel, tea.Cmd) {
26 					m.errMsg = ""
27 					m.newName = strings.TrimSpace(m.input.Value())
28 
29-					return m, tea.Batch(
30-						createAccount(m), // fire off the command, too
31-						m.spinner.Tick,
32-					)
33+					return m, createAccount(m)
34 				case cancelButton: // Exit
35 					m.Quit = true
36 					return m, nil
37@@ -220,12 +214,6 @@ func Update(msg tea.Msg, m CreateModel) (CreateModel, tea.Cmd) {
38 
39 		return m, nil
40 
41-	case spinner.TickMsg:
42-		var cmd tea.Cmd
43-		m.spinner, cmd = m.spinner.Update(msg)
44-
45-		return m, cmd
46-
47 	default:
48 		var cmd tea.Cmd
49 		m.input, cmd = m.input.Update(msg) // Do we still need this?
50@@ -257,7 +245,7 @@ func View(m CreateModel) string {
51 }
52 
53 func spinnerView(m CreateModel) string {
54-	return m.spinner.View() + " Creating account..."
55+	return "Creating account..."
56 }
57 
58 func createAccount(m CreateModel) tea.Cmd {
M tui/cms.go
+15, -13
  1@@ -4,7 +4,6 @@ import (
  2 	"errors"
  3 	"io"
  4 
  5-	"github.com/charmbracelet/bubbles/spinner"
  6 	tea "github.com/charmbracelet/bubbletea"
  7 	"github.com/charmbracelet/lipgloss"
  8 	"github.com/charmbracelet/ssh"
  9@@ -100,7 +99,6 @@ func CmsMiddleware(cfg *shared.ConfigSite) bm.Handler {
 10 			status:     statusInit,
 11 			menuChoice: unsetChoice,
 12 			styles:     styles,
 13-			spinner:    common.NewSpinner(styles),
 14 			terminalSize: tea.WindowSizeMsg{
 15 				Width:  80,
 16 				Height: 24,
 17@@ -135,7 +133,6 @@ type model struct {
 18 	menuChoice      menuChoice
 19 	styles          common.Styles
 20 	info            info.Model
 21-	spinner         spinner.Model
 22 	keys            keys.Model
 23 	tokens          tokens.Model
 24 	plus            plus.Model
 25@@ -146,7 +143,7 @@ type model struct {
 26 }
 27 
 28 func (m model) Init() tea.Cmd {
 29-	return m.spinner.Tick
 30+	return nil
 31 }
 32 
 33 func (m model) findUser() (*db.User, error) {
 34@@ -193,12 +190,16 @@ func (m model) findPlusFeatureFlag() (*db.FeatureFlag, error) {
 35 func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 36 	var (
 37 		cmds []tea.Cmd
 38-		cmd  tea.Cmd
 39 	)
 40 
 41 	switch msg := msg.(type) {
 42 	case tea.WindowSizeMsg:
 43 		m.terminalSize = msg
 44+		var cmd tea.Cmd
 45+		m.plus, cmd = plus.Update(msg, m.plus, m.terminalSize)
 46+		cmds = append(cmds, cmd)
 47+		m.notifications, cmd = notifications.Update(msg, m.notifications, m.terminalSize)
 48+		cmds = append(cmds, cmd)
 49 	case tea.KeyMsg:
 50 		switch msg.Type {
 51 		case tea.KeyCtrlC:
 52@@ -241,8 +242,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 53 		m.keys = keys.NewModel(m.styles, m.cfg.Logger, m.dbpool, m.user)
 54 		m.tokens = tokens.NewModel(m.styles, m.dbpool, m.user)
 55 		m.createAccount = account.NewCreateModel(m.styles, m.dbpool, m.publicKey)
 56-		m.plus = plus.NewModel(m.styles, m.user, m.session)
 57-		m.notifications = notifications.NewModel(m.styles, m.dbpool, m.user, m.session)
 58+		m.plus = plus.NewModel(m.styles, m.user, m.session, m.terminalSize)
 59+		m.notifications = notifications.NewModel(m.styles, m.dbpool, m.user, m.terminalSize)
 60 	}
 61 
 62 	switch m.status {
 63@@ -251,8 +252,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 64 		m.keys = keys.NewModel(m.styles, m.cfg.Logger, m.dbpool, m.user)
 65 		m.tokens = tokens.NewModel(m.styles, m.dbpool, m.user)
 66 		m.createAccount = account.NewCreateModel(m.styles, m.dbpool, m.publicKey)
 67-		m.plus = plus.NewModel(m.styles, m.user, m.session)
 68-		m.notifications = notifications.NewModel(m.styles, m.dbpool, m.user, m.session)
 69+		m.plus = plus.NewModel(m.styles, m.user, m.session, m.terminalSize)
 70+		m.notifications = notifications.NewModel(m.styles, m.dbpool, m.user, m.terminalSize)
 71 		// no user found? go to create account screen
 72 		if m.user == nil {
 73 			m.status = statusNoAccount
 74@@ -261,6 +262,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 75 		}
 76 	}
 77 
 78+	var cmd tea.Cmd
 79 	m, cmd = updateChildren(msg, m)
 80 	if cmd != nil {
 81 		cmds = append(cmds, cmd)
 82@@ -306,18 +308,18 @@ func updateChildren(msg tea.Msg, m model) (model, tea.Cmd) {
 83 			return m, tea.Quit
 84 		}
 85 	case statusBrowsingPlus:
 86-		m.plus, cmd = plus.Update(msg, m.plus)
 87+		m.plus, cmd = plus.Update(msg, m.plus, m.terminalSize)
 88 		if m.plus.Done {
 89-			m.plus = plus.NewModel(m.styles, m.user, m.session)
 90+			m.plus = plus.NewModel(m.styles, m.user, m.session, m.terminalSize)
 91 			m.status = statusReady
 92 		} else if m.plus.Quit {
 93 			m.status = statusQuitting
 94 			return m, tea.Quit
 95 		}
 96 	case statusBrowsingNotifications:
 97-		m.notifications, cmd = notifications.Update(msg, m.notifications)
 98+		m.notifications, cmd = notifications.Update(msg, m.notifications, m.terminalSize)
 99 		if m.notifications.Done {
100-			m.notifications = notifications.NewModel(m.styles, m.dbpool, m.user, m.session)
101+			m.notifications = notifications.NewModel(m.styles, m.dbpool, m.user, m.terminalSize)
102 			m.status = statusReady
103 		} else if m.notifications.Quit {
104 			m.status = statusQuitting
M tui/common/styles.go
+0, -9
 1@@ -27,13 +27,9 @@ type Styles struct {
 2 	Prompt,
 3 	FocusedPrompt,
 4 	Note,
 5-	NoteDim,
 6 	Delete,
 7-	DeleteDim,
 8 	Label,
 9-	LabelDim,
10 	ListKey,
11-	ListDim,
12 	InactivePagination,
13 	SelectionMarker,
14 	SelectedMenuItem,
15@@ -65,14 +61,9 @@ func DefaultStyles(renderer *lipgloss.Renderer) Styles {
16 	s.Prompt = renderer.NewStyle().MarginRight(1).SetString("•")
17 	s.FocusedPrompt = s.Prompt.Copy().Foreground(Fuschia)
18 	s.Note = renderer.NewStyle().Foreground(Green)
19-	s.NoteDim = renderer.NewStyle().
20-		Foreground(Green)
21 	s.Delete = s.Error.Copy()
22-	s.DeleteDim = renderer.NewStyle().Foreground(FaintRed)
23 	s.Label = renderer.NewStyle().Foreground(Fuschia)
24-	s.LabelDim = renderer.NewStyle().Foreground(Indigo)
25 	s.ListKey = renderer.NewStyle().Foreground(Indigo)
26-	s.ListDim = renderer.NewStyle().Foreground(SubtleIndigo)
27 	s.InactivePagination = renderer.NewStyle().
28 		Foreground(Grey)
29 	s.SelectionMarker = renderer.NewStyle().
M tui/common/views.go
+1, -13
 1@@ -4,7 +4,6 @@ import (
 2 	"fmt"
 3 	"strings"
 4 
 5-	"github.com/charmbracelet/bubbles/spinner"
 6 	"github.com/charmbracelet/lipgloss"
 7 )
 8 
 9@@ -52,7 +51,7 @@ func KeyValueView(styles Styles, stuff ...string) string {
10 			continue
11 		}
12 		// odd: value
13-		s += styles.LabelDim.Render(stuff[i])
14+		s += styles.Label.Render(stuff[i])
15 		s += "\n"
16 		index++
17 	}
18@@ -60,17 +59,6 @@ func KeyValueView(styles Styles, stuff ...string) string {
19 	return strings.TrimSpace(s)
20 }
21 
22-// NewSpinner returns a spinner model.
23-func NewSpinner(styles Styles) spinner.Model {
24-	spinnerStyle := styles.Renderer.NewStyle().
25-		Foreground(lipgloss.AdaptiveColor{Light: "#8E8E8E", Dark: "#747373"})
26-
27-	s := spinner.New()
28-	s.Spinner = spinner.Dot
29-	s.Style = spinnerStyle
30-	return s
31-}
32-
33 // OKButtonView returns a button reading "OK".
34 func OKButtonView(styles Styles, focused bool, defaultButton bool) string {
35 	return styledButton(styles, "OK", defaultButton, focused)
M tui/createkey/create.go
+20, -32
 1@@ -4,7 +4,6 @@ import (
 2 	"errors"
 3 	"strings"
 4 
 5-	"github.com/charmbracelet/bubbles/spinner"
 6 	input "github.com/charmbracelet/bubbles/textinput"
 7 	tea "github.com/charmbracelet/bubbletea"
 8 	"github.com/picosh/pico/db"
 9@@ -43,15 +42,14 @@ type Model struct {
10 	Done bool
11 	Quit bool
12 
13-	dbpool  db.DB
14-	user    *db.User
15-	styles  common.Styles
16-	state   state
17-	newKey  string
18-	index   index
19-	errMsg  string
20-	input   input.Model
21-	spinner spinner.Model
22+	dbpool db.DB
23+	user   *db.User
24+	styles common.Styles
25+	state  state
26+	newKey string
27+	index  index
28+	errMsg string
29+	input  input.Model
30 }
31 
32 // updateFocus updates the focused states in the model based on the current
33@@ -96,17 +94,16 @@ func NewModel(styles common.Styles, dbpool db.DB, user *db.User) Model {
34 	im.Focus()
35 
36 	return Model{
37-		Done:    false,
38-		Quit:    false,
39-		dbpool:  dbpool,
40-		user:    user,
41-		styles:  styles,
42-		state:   ready,
43-		newKey:  "",
44-		index:   textInput,
45-		errMsg:  "",
46-		input:   im,
47-		spinner: common.NewSpinner(styles),
48+		Done:   false,
49+		Quit:   false,
50+		dbpool: dbpool,
51+		user:   user,
52+		styles: styles,
53+		state:  ready,
54+		newKey: "",
55+		index:  textInput,
56+		errMsg: "",
57+		input:  im,
58 	}
59 }
60 
61@@ -162,10 +159,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
62 					m.errMsg = ""
63 					m.newKey = strings.TrimSpace(m.input.Value())
64 
65-					return m, tea.Batch(
66-						addPublicKey(m), // fire off the command, too
67-						m.spinner.Tick,
68-					)
69+					return m, addPublicKey(m)
70 				case cancelButton: // Exit this mini-app
71 					m.Done = true
72 					return m, nil
73@@ -210,12 +204,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
74 
75 		return m, nil
76 
77-	case spinner.TickMsg:
78-		var cmd tea.Cmd
79-		m.spinner, cmd = m.spinner.Update(msg)
80-
81-		return m, cmd
82-
83 	default:
84 		var cmd tea.Cmd
85 		m.input, cmd = m.input.Update(msg) // Do we still need this?
86@@ -243,7 +231,7 @@ func (m Model) View() string {
87 }
88 
89 func spinnerView(m Model) string {
90-	return m.spinner.View() + " Submitting..."
91+	return "Submitting..."
92 }
93 
94 func addPublicKey(m Model) tea.Cmd {
M tui/createtoken/create.go
+2, -14
 1@@ -4,7 +4,6 @@ import (
 2 	"fmt"
 3 	"strings"
 4 
 5-	"github.com/charmbracelet/bubbles/spinner"
 6 	input "github.com/charmbracelet/bubbles/textinput"
 7 	tea "github.com/charmbracelet/bubbletea"
 8 	"github.com/picosh/pico/db"
 9@@ -52,7 +51,6 @@ type Model struct {
10 	index     index
11 	errMsg    string
12 	input     input.Model
13-	spinner   spinner.Model
14 }
15 
16 // updateFocus updates the focused states in the model based on the current
17@@ -108,7 +106,6 @@ func NewModel(styles common.Styles, dbpool db.DB, user *db.User) Model {
18 		index:     textInput,
19 		errMsg:    "",
20 		input:     im,
21-		spinner:   common.NewSpinner(styles),
22 	}
23 }
24 
25@@ -170,10 +167,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
26 					m.errMsg = ""
27 					m.tokenName = strings.TrimSpace(m.input.Value())
28 
29-					return m, tea.Batch(
30-						addToken(m), // fire off the command, too
31-						m.spinner.Tick,
32-					)
33+					return m, addToken(m)
34 				case cancelButton: // Exit this mini-app
35 					m.Done = true
36 					return m, dismiss
37@@ -205,12 +199,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
38 
39 		return m, nil
40 
41-	case spinner.TickMsg:
42-		var cmd tea.Cmd
43-		m.spinner, cmd = m.spinner.Update(msg)
44-
45-		return m, cmd
46-
47 	default:
48 		var cmd tea.Cmd
49 		m.input, cmd = m.input.Update(msg) // Do we still need this?
50@@ -242,7 +230,7 @@ func (m Model) View() string {
51 }
52 
53 func spinnerView(m Model) string {
54-	return m.spinner.View() + " Submitting..."
55+	return "Submitting..."
56 }
57 
58 func dismiss() tea.Msg { return TokenDismissed(1) }
M tui/keys/keys.go
+4, -18
 1@@ -4,7 +4,6 @@ import (
 2 	"log/slog"
 3 
 4 	pager "github.com/charmbracelet/bubbles/paginator"
 5-	"github.com/charmbracelet/bubbles/spinner"
 6 	tea "github.com/charmbracelet/bubbletea"
 7 	"github.com/picosh/pico/db"
 8 	"github.com/picosh/pico/tui/common"
 9@@ -58,7 +57,6 @@ type Model struct {
10 	index          int             // index of selected key in relation to the current page
11 	Exit           bool
12 	Quit           bool
13-	spinner        spinner.Model
14 	createKey      createkey.Model
15 }
16 
17@@ -98,7 +96,6 @@ func NewModel(styles common.Styles, logger *slog.Logger, dbpool db.DB, user *db.
18 		activeKeyIndex: -1,
19 		keys:           []*db.PublicKey{},
20 		index:          0,
21-		spinner:        common.NewSpinner(styles),
22 		Exit:           false,
23 		Quit:           false,
24 	}
25@@ -106,9 +103,7 @@ func NewModel(styles common.Styles, logger *slog.Logger, dbpool db.DB, user *db.
26 
27 // Init is the Tea initialization function.
28 func (m Model) Init() tea.Cmd {
29-	return tea.Batch(
30-		m.spinner.Tick,
31-	)
32+	return nil
33 }
34 
35 // Update is the tea update function which handles incoming messages.
36@@ -226,12 +221,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
37 		m.state = stateNormal
38 		return m, fetchKeys(m.dbpool, m.user)
39 
40-	case spinner.TickMsg:
41-		var cmd tea.Cmd
42-		if m.state < stateNormal {
43-			m.spinner, cmd = m.spinner.Update(msg)
44-		}
45-		return m, cmd
46 	}
47 
48 	switch m.state {
49@@ -291,7 +280,7 @@ func (m Model) View() string {
50 
51 	switch m.state {
52 	case stateLoading:
53-		s = m.spinner.View() + " Loading...\n\n"
54+		s = "Loading...\n\n"
55 	case stateQuitting:
56 		s = "Thanks for using pico.sh!\n"
57 	case stateCreateKey:
58@@ -372,15 +361,12 @@ func helpView(m Model) string {
59 func (m Model) promptView(prompt string) string {
60 	st := m.styles.Delete.Copy().MarginTop(2).MarginRight(1)
61 	return st.Render(prompt) +
62-		m.styles.DeleteDim.Render("(y/N)")
63+		m.styles.Delete.Render("(y/N)")
64 }
65 
66 // LoadKeys returns the command necessary for loading the keys.
67 func LoadKeys(m Model) tea.Cmd {
68-	return tea.Batch(
69-		fetchKeys(m.dbpool, m.user),
70-		m.spinner.Tick,
71-	)
72+	return fetchKeys(m.dbpool, m.user)
73 }
74 
75 // fetchKeys loads the current set of keys via the charm client.
M tui/keys/keyview.go
+6, -6
 1@@ -34,7 +34,7 @@ type Fingerprint struct {
 2 func (f Fingerprint) String() string {
 3 	return fmt.Sprintf(
 4 		"%s %s",
 5-		f.Styles.ListDim.Render(strings.ToUpper(f.Algorithm)),
 6+		f.Styles.ListKey.Render(strings.ToUpper(f.Algorithm)),
 7 		f.Styles.ListKey.Render(f.Type+":"+f.Value),
 8 	)
 9 }
10@@ -64,7 +64,7 @@ func (f fingerprint) state(s keyState, styles common.Styles) string {
11 	if s == keyDeleting {
12 		return fmt.Sprintf(
13 			"%s %s",
14-			styles.DeleteDim.Render(strings.ToUpper(f.Algorithm)),
15+			styles.Delete.Render(strings.ToUpper(f.Algorithm)),
16 			styles.Delete.Render(f.Type+":"+f.Value),
17 		)
18 	}
19@@ -85,7 +85,7 @@ type styledKey struct {
20 }
21 
22 func (m Model) newStyledKey(styles common.Styles, key *db.PublicKey, active bool) styledKey {
23-	date := key.CreatedAt.Format("02 Jan 2006 15:04:05 MST")
24+	date := key.CreatedAt.Format("02 Jan 2006")
25 	fp, err := FingerprintSHA256(styles, key)
26 	if err != nil {
27 		fp = Fingerprint{Value: "[error generating fingerprint]"}
28@@ -93,7 +93,7 @@ func (m Model) newStyledKey(styles common.Styles, key *db.PublicKey, active bool
29 
30 	var note string
31 	if active {
32-		note = m.styles.NoteDim.Render("• ") + m.styles.Note.Render("Current Key")
33+		note = m.styles.Note.Render("• ") + m.styles.Note.Render("Current Key")
34 	}
35 
36 	// Default state
37@@ -106,7 +106,7 @@ func (m Model) newStyledKey(styles common.Styles, key *db.PublicKey, active bool
38 		dateLabel:    "Added:",
39 		commentLabel: "Name:",
40 		commentVal:   key.Name,
41-		dateVal:      styles.LabelDim.Render(date),
42+		dateVal:      styles.Label.Render(date),
43 		note:         note,
44 	}
45 }
46@@ -125,7 +125,7 @@ func (k *styledKey) deleting() {
47 	k.keyLabel = k.styles.Delete.Render("Key:")
48 	k.dateLabel = k.styles.Delete.Render("Added:")
49 	k.commentLabel = k.styles.Delete.Render("Name:")
50-	k.dateVal = k.styles.DeleteDim.Render(k.date)
51+	k.dateVal = k.styles.Delete.Render(k.date)
52 }
53 
54 func (k styledKey) render(state keyState) string {
M tui/notifications/notifications.go
+6, -10
 1@@ -6,7 +6,6 @@ import (
 2 	"github.com/charmbracelet/bubbles/viewport"
 3 	tea "github.com/charmbracelet/bubbletea"
 4 	"github.com/charmbracelet/glamour"
 5-	"github.com/charmbracelet/ssh"
 6 	"github.com/picosh/pico/db"
 7 	"github.com/picosh/pico/tui/common"
 8 )
 9@@ -71,10 +70,9 @@ func headerWidth(w int) int {
10 }
11 
12 // NewModel returns a new username model in its initial state.
13-func NewModel(styles common.Styles, dbpool db.DB, user *db.User, session ssh.Session) Model {
14-	pty, _, _ := session.Pty()
15+func NewModel(styles common.Styles, dbpool db.DB, user *db.User, termSize tea.WindowSizeMsg) Model {
16 	hh := headerHeight(styles)
17-	viewport := viewport.New(headerWidth(pty.Window.Width), pty.Window.Height-hh)
18+	viewport := viewport.New(headerWidth(termSize.Width), termSize.Height-hh)
19 	viewport.YPosition = hh
20 	if user != nil {
21 		viewport.SetContent(NotificationsView(dbpool, user.ID))
22@@ -90,7 +88,7 @@ func NewModel(styles common.Styles, dbpool db.DB, user *db.User, session ssh.Ses
23 }
24 
25 // Update is the Bubble Tea update loop.
26-func Update(msg tea.Msg, m Model) (Model, tea.Cmd) {
27+func Update(msg tea.Msg, m Model, termSize tea.WindowSizeMsg) (Model, tea.Cmd) {
28 	var cmd tea.Cmd
29 	var cmds []tea.Cmd
30 
31@@ -108,13 +106,11 @@ func Update(msg tea.Msg, m Model) (Model, tea.Cmd) {
32 				m.Done = true
33 			}
34 		}
35-
36-	case tea.WindowSizeMsg:
37-		m.viewport.Width = headerWidth(msg.Width)
38-		hh := headerHeight(m.styles)
39-		m.viewport.Height = msg.Height - hh
40 	}
41 
42+	m.viewport.Width = headerWidth(termSize.Width)
43+	hh := headerHeight(m.styles)
44+	m.viewport.Height = termSize.Height - hh
45 	m.viewport, cmd = m.viewport.Update(msg)
46 	cmds = append(cmds, cmd)
47 
M tui/plus/plus.go
+6, -9
 1@@ -94,10 +94,9 @@ func headerWidth(w int) int {
 2 }
 3 
 4 // NewModel returns a new username model in its initial state.
 5-func NewModel(styles common.Styles, user *db.User, session ssh.Session) Model {
 6-	pty, _, _ := session.Pty()
 7+func NewModel(styles common.Styles, user *db.User, session ssh.Session, termSize tea.WindowSizeMsg) Model {
 8 	hh := headerHeight(styles)
 9-	viewport := viewport.New(headerWidth(pty.Window.Width), pty.Window.Height-hh)
10+	viewport := viewport.New(headerWidth(termSize.Width), termSize.Height-hh)
11 	viewport.YPosition = hh
12 	if user != nil {
13 		viewport.SetContent(PlusView(user.Name))
14@@ -113,7 +112,7 @@ func NewModel(styles common.Styles, user *db.User, session ssh.Session) Model {
15 }
16 
17 // Update is the Bubble Tea update loop.
18-func Update(msg tea.Msg, m Model) (Model, tea.Cmd) {
19+func Update(msg tea.Msg, m Model, termSize tea.WindowSizeMsg) (Model, tea.Cmd) {
20 	var cmd tea.Cmd
21 	var cmds []tea.Cmd
22 
23@@ -131,13 +130,11 @@ func Update(msg tea.Msg, m Model) (Model, tea.Cmd) {
24 				m.Done = true
25 			}
26 		}
27-
28-	case tea.WindowSizeMsg:
29-		m.viewport.Width = headerWidth(msg.Width)
30-		hh := headerHeight(m.styles)
31-		m.viewport.Height = msg.Height - hh
32 	}
33 
34+	m.viewport.Width = headerWidth(termSize.Width)
35+	hh := headerHeight(m.styles)
36+	m.viewport.Height = termSize.Height - hh
37 	m.viewport, cmd = m.viewport.Update(msg)
38 	cmds = append(cmds, cmd)
39 
M tui/tokens/tokens.go
+4, -18
 1@@ -2,7 +2,6 @@ package tokens
 2 
 3 import (
 4 	pager "github.com/charmbracelet/bubbles/paginator"
 5-	"github.com/charmbracelet/bubbles/spinner"
 6 	tea "github.com/charmbracelet/bubbletea"
 7 	"github.com/picosh/pico/db"
 8 	"github.com/picosh/pico/tui/common"
 9@@ -53,7 +52,6 @@ type Model struct {
10 	index          int         // index of selected key in relation to the current page
11 	Exit           bool
12 	Quit           bool
13-	spinner        spinner.Model
14 	createKey      createtoken.Model
15 }
16 
17@@ -92,7 +90,6 @@ func NewModel(styles common.Styles, dbpool db.DB, user *db.User) Model {
18 		activeKeyIndex: -1,
19 		tokens:         []*db.Token{},
20 		index:          0,
21-		spinner:        common.NewSpinner(styles),
22 		Exit:           false,
23 		Quit:           false,
24 	}
25@@ -100,9 +97,7 @@ func NewModel(styles common.Styles, dbpool db.DB, user *db.User) Model {
26 
27 // Init is the Tea initialization function.
28 func (m Model) Init() tea.Cmd {
29-	return tea.Batch(
30-		m.spinner.Tick,
31-	)
32+	return nil
33 }
34 
35 // Update is the tea update function which handles incoming messages.
36@@ -192,12 +187,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
37 		m.state = stateNormal
38 		return m, fetchKeys(m.dbpool, m.user)
39 
40-	case spinner.TickMsg:
41-		var cmd tea.Cmd
42-		if m.state < stateNormal {
43-			m.spinner, cmd = m.spinner.Update(msg)
44-		}
45-		return m, cmd
46 	}
47 
48 	switch m.state {
49@@ -257,7 +246,7 @@ func (m Model) View() string {
50 
51 	switch m.state {
52 	case stateLoading:
53-		s = m.spinner.View() + " Loading...\n\n"
54+		s = "Loading...\n\n"
55 	case stateQuitting:
56 		s = "Thanks for using pico.sh!\n"
57 	case stateCreateKey:
58@@ -332,15 +321,12 @@ func helpView(m Model) string {
59 func (m Model) promptView(prompt string) string {
60 	st := m.styles.Delete.Copy().MarginTop(2).MarginRight(1)
61 	return st.Render(prompt) +
62-		m.styles.DeleteDim.Render("(y/N)")
63+		m.styles.Delete.Render("(y/N)")
64 }
65 
66 // LoadKeys returns the command necessary for loading the keys.
67 func LoadKeys(m Model) tea.Cmd {
68-	return tea.Batch(
69-		fetchKeys(m.dbpool, m.user),
70-		m.spinner.Tick,
71-	)
72+	return fetchKeys(m.dbpool, m.user)
73 }
74 
75 // fetchKeys loads the current set of keys via the charm client.
M tui/tokens/tokenview.go
+3, -3
 1@@ -18,7 +18,7 @@ type styledKey struct {
 2 }
 3 
 4 func (m Model) newStyledKey(styles common.Styles, token *db.Token, active bool) styledKey {
 5-	date := token.CreatedAt.Format("02 Jan 2006 15:04:05 MST")
 6+	date := token.CreatedAt.Format("02 Jan 2006")
 7 
 8 	// Default state
 9 	return styledKey{
10@@ -28,7 +28,7 @@ func (m Model) newStyledKey(styles common.Styles, token *db.Token, active bool)
11 		gutter:    " ",
12 		nameLabel: "Name:",
13 		dateLabel: "Added:",
14-		dateVal:   styles.LabelDim.Render(date),
15+		dateVal:   styles.Label.Render(date),
16 	}
17 }
18 
19@@ -44,7 +44,7 @@ func (k *styledKey) deleting() {
20 	k.gutter = common.VerticalLine(k.styles.Renderer, common.StateDeleting)
21 	k.nameLabel = k.styles.Delete.Render("Name:")
22 	k.dateLabel = k.styles.Delete.Render("Added:")
23-	k.dateVal = k.styles.DeleteDim.Render(k.date)
24+	k.dateVal = k.styles.Delete.Render(k.date)
25 }
26 
27 func (k styledKey) render(state keyState) string {