wip: repo header

Ayman Bagabas created

Change summary

ui/components/code/code.go           | 50 +++++++++++++-
ui/components/footer/footer.go       |  2 
ui/components/selector/selector.go   | 42 ++++++++++--
ui/components/statusbar/statusbar.go |  5 +
ui/components/tabs/tabs.go           |  2 
ui/components/viewport/viewport.go   | 45 +++++++++++++
ui/keymap/keymap.go                  | 26 +++++++
ui/pages/repo/log.go                 | 53 +++++++++++++++
ui/pages/repo/repo.go                | 98 +++++++++++++++++++++++------
ui/pages/selection/selection.go      | 13 ++-
ui/styles/styles.go                  | 42 +++++++++---
ui/ui.go                             |  4 
12 files changed, 330 insertions(+), 52 deletions(-)

Detailed changes

ui/components/code/code.go 🔗

@@ -49,11 +49,6 @@ func (r *Code) SetContent(c, ext string) tea.Cmd {
 	return r.Init()
 }
 
-// GotoTop reset the viewport to the top.
-func (r *Code) GotoTop() {
-	r.viewport.Viewport.GotoTop()
-}
-
 // Init implements tea.Model.
 func (r *Code) Init() tea.Cmd {
 	w := r.common.Width
@@ -96,6 +91,51 @@ func (r *Code) View() string {
 	return r.viewport.View()
 }
 
+// GotoTop moves the viewport to the top of the log.
+func (r *Code) GotoTop() {
+	r.viewport.GotoTop()
+}
+
+// GotoBottom moves the viewport to the bottom of the log.
+func (r *Code) GotoBottom() {
+	r.viewport.GotoBottom()
+}
+
+// HalfViewDown moves the viewport down by half the viewport height.
+func (r *Code) HalfViewDown() {
+	r.viewport.HalfViewDown()
+}
+
+// HalfViewUp moves the viewport up by half the viewport height.
+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()
+}
+
 func styleConfig() gansi.StyleConfig {
 	noColor := ""
 	s := glamour.DarkStyleConfig

ui/components/footer/footer.go 🔗

@@ -17,7 +17,9 @@ type Footer struct {
 func New(c common.Common, keymap help.KeyMap) *Footer {
 	h := help.New()
 	h.Styles.ShortKey = c.Styles.HelpKey
+	h.Styles.ShortDesc = c.Styles.HelpValue
 	h.Styles.FullKey = c.Styles.HelpKey
+	h.Styles.FullDesc = c.Styles.HelpValue
 	f := &Footer{
 		common: c,
 		help:   h,

ui/components/selector/selector.go 🔗

@@ -9,6 +9,7 @@ import (
 
 // Selector is a list of items that can be selected.
 type Selector struct {
+	KeyMap      list.KeyMap
 	list        list.Model
 	common      common.Common
 	active      int
@@ -34,10 +35,7 @@ func New(common common.Common, items []IdentifiableItem, delegate list.ItemDeleg
 		itms[i] = item
 	}
 	l := list.New(itms, delegate, common.Width, common.Height)
-	l.SetShowTitle(false)
-	l.SetShowHelp(false)
-	l.SetShowStatusBar(false)
-	l.DisableQuitKeybindings()
+	l.KeyMap = list.DefaultKeyMap()
 	s := &Selector{
 		list:   l,
 		common: common,
@@ -46,9 +44,39 @@ func New(common common.Common, items []IdentifiableItem, delegate list.ItemDeleg
 	return s
 }
 
-// KeyMap returns the underlying list's keymap.
-func (s *Selector) KeyMap() list.KeyMap {
-	return s.list.KeyMap
+// SetShowTitle sets the show title flag.
+func (s *Selector) SetShowTitle(show bool) {
+	s.list.SetShowTitle(show)
+}
+
+// SetShowHelp sets the show help flag.
+func (s *Selector) SetShowHelp(show bool) {
+	s.list.SetShowHelp(show)
+}
+
+// SetShowStatusBar sets the show status bar flag.
+func (s *Selector) SetShowStatusBar(show bool) {
+	s.list.SetShowStatusBar(show)
+}
+
+// DisableQuitKeybindings disables the quit keybindings.
+func (s *Selector) DisableQuitKeybindings() {
+	s.list.DisableQuitKeybindings()
+}
+
+// SetShowFilter sets the show filter flag.
+func (s *Selector) SetShowFilter(show bool) {
+	s.list.SetShowFilter(show)
+}
+
+// SetShowPagination sets the show pagination flag.
+func (s *Selector) SetShowPagination(show bool) {
+	s.list.SetShowPagination(show)
+}
+
+// SetFilteringEnabled sets the filtering enabled flag.
+func (s *Selector) SetFilteringEnabled(enabled bool) {
+	s.list.SetFilteringEnabled(enabled)
 }
 
 // SetSize implements common.Component.

ui/components/statusbar/statusbar.go 🔗

@@ -46,7 +46,10 @@ func (s *StatusBar) View() string {
 	st := s.common.Styles
 	w := lipgloss.Width
 	key := st.StatusBarKey.Render(s.msg.Key)
-	info := st.StatusBarInfo.Render(s.msg.Info)
+	info := ""
+	if s.msg.Info != "" {
+		info = st.StatusBarInfo.Render(s.msg.Info)
+	}
 	branch := st.StatusBarBranch.Render(s.msg.Branch)
 	value := st.StatusBarValue.
 		Width(s.common.Width - w(key) - w(info) - w(branch)).

ui/components/tabs/tabs.go 🔗

@@ -53,7 +53,7 @@ func (t *Tabs) View() string {
 	s := strings.Builder{}
 	sep := t.common.Styles.TabSeparator
 	for i, tab := range t.tabs {
-		style := t.common.Styles.Tab.Copy()
+		style := t.common.Styles.TabInactive.Copy()
 		if i == t.activeTab {
 			style = t.common.Styles.TabActive.Copy()
 		}

ui/components/viewport/viewport.go 🔗

@@ -40,3 +40,48 @@ func (v *Viewport) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 func (v *Viewport) View() string {
 	return v.Viewport.View()
 }
+
+// GotoTop moves the viewport to the top of the log.
+func (v *Viewport) GotoTop() {
+	v.Viewport.GotoTop()
+}
+
+// GotoBottom moves the viewport to the bottom of the log.
+func (v *Viewport) GotoBottom() {
+	v.Viewport.GotoBottom()
+}
+
+// HalfViewDown moves the viewport down by half the viewport height.
+func (v *Viewport) HalfViewDown() {
+	v.Viewport.HalfViewDown()
+}
+
+// HalfViewUp moves the viewport up by half the viewport height.
+func (v *Viewport) HalfViewUp() {
+	v.Viewport.HalfViewUp()
+}
+
+// ViewUp moves the viewport up by a page.
+func (v *Viewport) ViewUp() []string {
+	return v.Viewport.ViewUp()
+}
+
+// ViewDown moves the viewport down by a page.
+func (v *Viewport) ViewDown() []string {
+	return v.Viewport.ViewDown()
+}
+
+// LineUp moves the viewport up by the given number of lines.
+func (v *Viewport) LineUp(n int) []string {
+	return v.Viewport.LineUp(n)
+}
+
+// LineDown moves the viewport down by the given number of lines.
+func (v *Viewport) LineDown(n int) []string {
+	return v.Viewport.LineDown(n)
+}
+
+// ScrollPercent returns the viewport's scroll percentage.
+func (v *Viewport) ScrollPercent() float64 {
+	return v.Viewport.ScrollPercent()
+}

ui/keymap/keymap.go 🔗

@@ -13,6 +13,8 @@ type KeyMap struct {
 	Select    key.Binding
 	Section   key.Binding
 	Back      key.Binding
+	PrevPage  key.Binding
+	NextPage  key.Binding
 }
 
 // DefaultKeyMap returns the default key map.
@@ -126,5 +128,29 @@ func DefaultKeyMap() *KeyMap {
 		),
 	)
 
+	km.PrevPage = key.NewBinding(
+		key.WithKeys(
+			"pgup",
+			"b",
+			"u",
+		),
+		key.WithHelp(
+			"pgup",
+			"prev page",
+		),
+	)
+
+	km.NextPage = key.NewBinding(
+		key.WithKeys(
+			"pgdown",
+			"f",
+			"d",
+		),
+		key.WithHelp(
+			"pgdn",
+			"next page",
+		),
+	)
+
 	return km
 }

ui/pages/repo/log.go 🔗

@@ -24,10 +24,20 @@ type Log struct {
 func NewLog(common common.Common) *Log {
 	l := &Log{
 		common:     common,
-		selector:   selector.New(common, []selector.IdentifiableItem{}, LogItemDelegate{common.Styles}),
 		vp:         viewport.New(),
 		activeView: logView,
 	}
+	selector := selector.New(common, []selector.IdentifiableItem{}, LogItemDelegate{common.Styles})
+	selector.SetShowFilter(false)
+	selector.SetShowHelp(false)
+	selector.SetShowPagination(true)
+	selector.SetShowStatusBar(false)
+	selector.SetShowTitle(false)
+	selector.SetFilteringEnabled(false)
+	selector.DisableQuitKeybindings()
+	selector.KeyMap.NextPage = common.Keymap.NextPage
+	selector.KeyMap.PrevPage = common.Keymap.PrevPage
+	l.selector = selector
 	return l
 }
 
@@ -37,6 +47,47 @@ func (l *Log) SetSize(width, height int) {
 	l.vp.SetSize(width, height)
 }
 
+// func (b *Bubble) countCommits() tea.Msg {
+// 	if b.ref == nil {
+// 		ref, err := b.repo.HEAD()
+// 		if err != nil {
+// 			return common.ErrMsg{Err: err}
+// 		}
+// 		b.ref = ref
+// 	}
+// 	count, err := b.repo.CountCommits(b.ref)
+// 	if err != nil {
+// 		return common.ErrMsg{Err: err}
+// 	}
+// 	return countMsg(count)
+// }
+
+// func (b *Bubble) updateItems() tea.Msg {
+// 	if b.count == 0 {
+// 		b.count = int64(b.countCommits().(countMsg))
+// 	}
+// 	count := b.count
+// 	items := make([]list.Item, count)
+// 	page := b.nextPage
+// 	limit := b.list.Paginator.PerPage
+// 	skip := page * limit
+// 	// CommitsByPage pages start at 1
+// 	cc, err := b.repo.CommitsByPage(b.ref, page+1, limit)
+// 	if err != nil {
+// 		return common.ErrMsg{Err: err}
+// 	}
+// 	for i, c := range cc {
+// 		idx := i + skip
+// 		if int64(idx) >= count {
+// 			break
+// 		}
+// 		items[idx] = item{c}
+// 	}
+// 	b.list.SetItems(items)
+// 	b.SetSize(b.width, b.height)
+// 	return itemsMsg{}
+// }
+
 func (l *Log) Init() tea.Cmd {
 	return nil
 }

ui/pages/repo/repo.go 🔗

@@ -1,6 +1,8 @@
 package repo
 
 import (
+	"fmt"
+
 	"github.com/charmbracelet/bubbles/key"
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/charmbracelet/lipgloss"
@@ -23,8 +25,13 @@ const (
 	tagsTab
 )
 
+// RepoMsg is a message that contains a git.Repository.
 type RepoMsg git.GitRepo
 
+// RefMsg is a message that contains a git.Reference.
+type RefMsg *ggit.Reference
+
+// Repo is a view for a git repository.
 type Repo struct {
 	common       common.Common
 	rs           git.GitRepoSource
@@ -37,32 +44,39 @@ type Repo struct {
 	ref          *ggit.Reference
 }
 
+// New returns a new Repo.
 func New(common common.Common, rs git.GitRepoSource) *Repo {
 	sb := statusbar.New(common)
 	tb := tabs.New(common, []string{"Readme", "Files", "Commits", "Branches", "Tags"})
 	readme := code.New(common, "", "")
 	readme.NoContentStyle = readme.NoContentStyle.SetString("No readme found.")
+	log := NewLog(common)
 	r := &Repo{
 		common:    common,
 		rs:        rs,
 		tabs:      tb,
 		statusbar: sb,
 		readme:    readme,
+		log:       log,
 	}
 	return r
 }
 
+// SetSize implements common.Component.
 func (r *Repo) SetSize(width, height int) {
 	r.common.SetSize(width, height)
-	hm := 4
+	hm := r.common.Styles.RepoBody.GetVerticalFrameSize() +
+		r.common.Styles.RepoHeader.GetHeight() +
+		r.common.Styles.RepoHeader.GetVerticalFrameSize() +
+		r.common.Styles.StatusBar.GetHeight() +
+		r.common.Styles.Tabs.GetHeight()
 	r.tabs.SetSize(width, height-hm)
 	r.statusbar.SetSize(width, height-hm)
 	r.readme.SetSize(width, height-hm)
-	if r.log != nil {
-		r.log.SetSize(width, height-hm)
-	}
+	r.log.SetSize(width, height-hm)
 }
 
+// ShortHelp implements help.KeyMap.
 func (r *Repo) ShortHelp() []key.Binding {
 	b := make([]key.Binding, 0)
 	tab := r.common.Keymap.Section
@@ -72,15 +86,18 @@ func (r *Repo) ShortHelp() []key.Binding {
 	return b
 }
 
+// FullHelp implements help.KeyMap.
 func (r *Repo) FullHelp() [][]key.Binding {
 	b := make([][]key.Binding, 0)
 	return b
 }
 
+// Init implements tea.View.
 func (r *Repo) Init() tea.Cmd {
 	return nil
 }
 
+// Update implements tea.Model.
 func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	cmds := make([]tea.Cmd, 0)
 	switch msg := msg.(type) {
@@ -89,9 +106,23 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		cmds = append(cmds, r.tabs.Init(), r.setRepoCmd(string(msg)))
 	case RepoMsg:
 		r.selectedRepo = git.GitRepo(msg)
-		cmds = append(cmds, r.updateStatusBarCmd, r.updateReadmeCmd)
+		r.readme.GotoTop()
+		cmds = append(cmds,
+			r.updateReadmeCmd,
+			r.updateRefCmd,
+		)
+	case RefMsg:
+		r.ref = msg
+		cmds = append(cmds,
+			r.updateStatusBarCmd,
+			r.log.Init(),
+		)
 	case tabs.ActiveTabMsg:
 		r.activeTab = tab(msg)
+	case tea.KeyMsg, tea.MouseMsg:
+		if r.selectedRepo != nil {
+			cmds = append(cmds, r.updateStatusBarCmd)
+		}
 	}
 	t, cmd := r.tabs.Update(msg)
 	r.tabs = t.(*tabs.Tabs)
@@ -112,10 +143,6 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		}
 	case filesTab:
 	case commitsTab:
-		if r.log == nil {
-			r.log = NewLog(r.common)
-			cmds = append(cmds, r.log.Init())
-		}
 		l, cmd := r.log.Update(msg)
 		r.log = l.(*Log)
 		if cmd != nil {
@@ -127,31 +154,49 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	return r, tea.Batch(cmds...)
 }
 
+// View implements tea.Model.
 func (r *Repo) View() string {
-	s := r.common.Styles.RepoBody.Copy().
+	s := r.common.Styles.Repo.Copy().
 		Width(r.common.Width).
 		Height(r.common.Height)
-	mainStyle := lipgloss.NewStyle().
-		Height(r.common.Height-4).
-		Margin(1, 0)
+	repoBodyStyle := r.common.Styles.RepoBody.Copy()
+	hm := repoBodyStyle.GetVerticalFrameSize() +
+		r.common.Styles.RepoHeader.GetHeight() +
+		r.common.Styles.RepoHeader.GetVerticalFrameSize() +
+		r.common.Styles.StatusBar.GetHeight() +
+		r.common.Styles.Tabs.GetHeight()
+	mainStyle := repoBodyStyle.
+		Height(r.common.Height - hm)
 	main := mainStyle.Render("")
 	switch r.activeTab {
 	case readmeTab:
 		main = mainStyle.Render(r.readme.View())
 	case filesTab:
 	case commitsTab:
-		if r.log != nil {
-			main = mainStyle.Render(r.log.View())
-		}
+		main = mainStyle.Render(r.log.View())
 	}
 	view := lipgloss.JoinVertical(lipgloss.Top,
-		r.tabs.View(),
+		r.headerView(),
 		main,
 		r.statusbar.View(),
 	)
 	return s.Render(view)
 }
 
+func (r *Repo) headerView() string {
+	if r.selectedRepo == nil {
+		return ""
+	}
+	name := r.common.Styles.RepoHeaderName.Render(r.selectedRepo.Name())
+	style := r.common.Styles.RepoHeader.Copy().Width(r.common.Width)
+	return style.Render(
+		lipgloss.JoinVertical(lipgloss.Top,
+			name,
+			r.tabs.View(),
+		),
+	)
+}
+
 func (r *Repo) setRepoCmd(repo string) tea.Cmd {
 	return func() tea.Msg {
 		for _, r := range r.rs.AllRepos() {
@@ -164,15 +209,16 @@ func (r *Repo) setRepoCmd(repo string) tea.Cmd {
 }
 
 func (r *Repo) updateStatusBarCmd() tea.Msg {
-	branch, err := r.selectedRepo.HEAD()
-	if err != nil {
-		return common.ErrorMsg(err)
+	info := ""
+	switch r.activeTab {
+	case readmeTab:
+		info = fmt.Sprintf("%.f%%", r.readme.ScrollPercent()*100)
 	}
 	return statusbar.StatusBarMsg{
 		Key:    r.selectedRepo.Name(),
 		Value:  "",
-		Info:   "",
-		Branch: branch.Name().Short(),
+		Info:   info,
+		Branch: r.ref.Name().Short(),
 	}
 }
 
@@ -183,3 +229,11 @@ func (r *Repo) updateReadmeCmd() tea.Msg {
 	rm, rp := r.selectedRepo.Readme()
 	return r.readme.SetContent(rm, rp)
 }
+
+func (r *Repo) updateRefCmd() tea.Msg {
+	head, err := r.selectedRepo.HEAD()
+	if err != nil {
+		return common.ErrorMsg(err)
+	}
+	return RefMsg(head)
+}

ui/pages/selection/selection.go 🔗

@@ -41,10 +41,15 @@ func New(s session.Session, common common.Common) *Selection {
 	}
 	readme := code.New(common, "", "")
 	readme.NoContentStyle = readme.NoContentStyle.SetString("No readme found.")
-	sel.readme = readme
-	sel.selector = selector.New(common,
+	selector := selector.New(common,
 		[]selector.IdentifiableItem{},
 		ItemDelegate{common.Styles, &sel.activeBox})
+	selector.SetShowTitle(false)
+	selector.SetShowHelp(false)
+	selector.SetShowStatusBar(false)
+	selector.DisableQuitKeybindings()
+	sel.selector = selector
+	sel.readme = readme
 	return sel
 }
 
@@ -66,7 +71,7 @@ func (s *Selection) SetSize(width, height int) {
 
 // ShortHelp implements help.KeyMap.
 func (s *Selection) ShortHelp() []key.Binding {
-	k := s.selector.KeyMap()
+	k := s.selector.KeyMap
 	kb := make([]key.Binding, 0)
 	kb = append(kb,
 		s.common.Keymap.UpDown,
@@ -85,7 +90,7 @@ func (s *Selection) ShortHelp() []key.Binding {
 // FullHelp implements help.KeyMap.
 // TODO implement full help on ?
 func (s *Selection) FullHelp() [][]key.Binding {
-	k := s.selector.KeyMap()
+	k := s.selector.KeyMap
 	return [][]key.Binding{
 		{
 			k.CursorUp,

ui/styles/styles.go 🔗

@@ -28,11 +28,14 @@ type Styles struct {
 	RepoNoteBorder  lipgloss.Border
 	RepoBodyBorder  lipgloss.Border
 
-	RepoTitle    lipgloss.Style
-	RepoTitleBox lipgloss.Style
-	RepoNote     lipgloss.Style
-	RepoNoteBox  lipgloss.Style
-	RepoBody     lipgloss.Style
+	Repo           lipgloss.Style
+	RepoTitle      lipgloss.Style
+	RepoTitleBox   lipgloss.Style
+	RepoNote       lipgloss.Style
+	RepoNoteBox    lipgloss.Style
+	RepoBody       lipgloss.Style
+	RepoHeader     lipgloss.Style
+	RepoHeaderName lipgloss.Style
 
 	Footer      lipgloss.Style
 	Branch      lipgloss.Style
@@ -80,12 +83,14 @@ type Styles struct {
 
 	CodeNoContent lipgloss.Style
 
+	StatusBar       lipgloss.Style
 	StatusBarKey    lipgloss.Style
 	StatusBarValue  lipgloss.Style
 	StatusBarInfo   lipgloss.Style
 	StatusBarBranch lipgloss.Style
 
-	Tab          lipgloss.Style
+	Tabs         lipgloss.Style
+	TabInactive  lipgloss.Style
 	TabActive    lipgloss.Style
 	TabSeparator lipgloss.Style
 }
@@ -166,6 +171,8 @@ func DefaultStyles() *Styles {
 		BottomRight: "╯",
 	}
 
+	s.Repo = lipgloss.NewStyle()
+
 	s.RepoTitle = lipgloss.NewStyle().
 		Padding(0, 2)
 
@@ -185,7 +192,16 @@ func DefaultStyles() *Styles {
 		BorderBottom(true).
 		BorderLeft(false)
 
-	s.RepoBody = lipgloss.NewStyle()
+	s.RepoBody = lipgloss.NewStyle().
+		Margin(1, 0)
+
+	s.RepoHeader = lipgloss.NewStyle().
+		Border(lipgloss.NormalBorder(), false, false, true, false).
+		BorderForeground(lipgloss.Color("241"))
+
+	s.RepoHeaderName = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("15")).
+		Bold(true)
 
 	s.Footer = lipgloss.NewStyle().
 		Height(1)
@@ -308,6 +324,9 @@ func DefaultStyles() *Styles {
 		MarginLeft(2).
 		Foreground(lipgloss.Color("#626262"))
 
+	s.StatusBar = lipgloss.NewStyle().
+		Height(1)
+
 	s.StatusBarKey = lipgloss.NewStyle().
 		Bold(true).
 		Padding(0, 1).
@@ -329,8 +348,11 @@ func DefaultStyles() *Styles {
 		Background(lipgloss.Color("#6E6ED8")).
 		Foreground(lipgloss.Color("#F1F1F1"))
 
-	s.Tab = lipgloss.NewStyle().
-		Foreground(lipgloss.Color("#F1F1F1"))
+	s.Tabs = lipgloss.NewStyle().
+		Height(1)
+
+	s.TabInactive = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("15"))
 
 	s.TabActive = lipgloss.NewStyle().
 		Foreground(lipgloss.Color("#6E6ED8")).
@@ -339,7 +361,7 @@ func DefaultStyles() *Styles {
 	s.TabSeparator = lipgloss.NewStyle().
 		SetString("│").
 		Padding(0, 1).
-		Foreground(lipgloss.Color("#777777"))
+		Foreground(lipgloss.Color("241"))
 
 	return s
 }

ui/ui.go 🔗

@@ -1,6 +1,7 @@
 package ui
 
 import (
+	"log"
 	"strings"
 
 	"github.com/charmbracelet/bubbles/key"
@@ -102,8 +103,9 @@ func (ui *UI) Init() tea.Cmd {
 }
 
 // Update implements tea.Model.
-// TODO update help when page change.
+// TODO show full help
 func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+	log.Printf("%T", msg)
 	cmds := make([]tea.Cmd, 0)
 	switch msg := msg.(type) {
 	case tea.WindowSizeMsg: