Cleanup and reorganize styles, theming prep

Christian Rocha created

Change summary

tui/bubble.go                   |  39 ++++----
tui/bubbles/repo/bubble.go      |  46 +++++-----
tui/bubbles/selection/bubble.go |  25 ++---
tui/commands.go                 |  14 +--
tui/style.go                    | 115 -------------------------
tui/style/style.go              | 155 +++++++++++++++++++++++++++++++++++
6 files changed, 215 insertions(+), 179 deletions(-)

Detailed changes

tui/bubble.go 🔗

@@ -6,6 +6,7 @@ import (
 	"smoothie/tui/bubbles/commits"
 	"smoothie/tui/bubbles/repo"
 	"smoothie/tui/bubbles/selection"
+	"smoothie/tui/style"
 	"strings"
 
 	tea "github.com/charmbracelet/bubbletea"
@@ -48,6 +49,7 @@ type SessionConfig struct {
 
 type Bubble struct {
 	config        *Config
+	styles        *style.Styles
 	state         sessionState
 	error         string
 	width         int
@@ -66,6 +68,7 @@ type Bubble struct {
 func NewBubble(cfg *Config, sCfg *SessionConfig) *Bubble {
 	b := &Bubble{
 		config:        cfg,
+		styles:        style.DefaultStyles(),
 		width:         sCfg.Width,
 		height:        sCfg.Height,
 		windowChanges: sCfg.WindowChanges,
@@ -143,10 +146,9 @@ func (b *Bubble) viewForBox(i int) string {
 	switch box := b.boxes[i].(type) {
 	case *selection.Bubble:
 		var s lipgloss.Style
+		s = b.styles.Menu
 		if isActive {
-			s = menuActiveStyle
-		} else {
-			s = menuStyle
+			s.Copy().BorderForeground(b.styles.ActiveBorderColor)
 		}
 		return s.Render(box.View())
 	case *repo.Bubble:
@@ -158,8 +160,8 @@ func (b *Bubble) viewForBox(i int) string {
 }
 
 func (b Bubble) headerView() string {
-	w := b.width - appBoxStyle.GetHorizontalFrameSize()
-	return headerStyle.Copy().Width(w).Render(b.config.Name)
+	w := b.width - b.styles.App.GetHorizontalFrameSize()
+	return b.styles.Header.Copy().Width(w).Render(b.config.Name)
 }
 
 func (b Bubble) footerView() string {
@@ -179,27 +181,28 @@ func (b Bubble) footerView() string {
 		}
 	}
 	for i, v := range h {
-		fmt.Fprint(w, v)
+		fmt.Fprint(w, v.Render(b.styles))
 		if i != len(h)-1 {
-			fmt.Fprint(w, helpDivider)
+			fmt.Fprint(w, b.styles.HelpDivider)
 		}
 	}
-	return footerStyle.Copy().Width(b.width).Render(w.String())
+	return b.styles.Footer.Copy().Width(b.width).Render(w.String())
 }
 
 func (b Bubble) errorView() string {
-	s := lipgloss.JoinHorizontal(
+	s := b.styles
+	str := lipgloss.JoinHorizontal(
 		lipgloss.Top,
-		errorHeaderStyle.Render("Bummer"),
-		errorBodyStyle.Render(b.error),
+		s.ErrorTitle.Render("Bummer"),
+		s.ErrorBody.Render(b.error),
 	)
 	h := b.height -
-		appBoxStyle.GetVerticalFrameSize() -
+		s.App.GetVerticalFrameSize() -
 		lipgloss.Height(b.headerView()) -
 		lipgloss.Height(b.footerView()) -
-		contentBoxStyle.GetVerticalFrameSize() +
-		3 // TODO: figure out why we need this
-	return errorStyle.Copy().Height(h).Render(s)
+		s.RepoBody.GetVerticalFrameSize() +
+		3 // TODO: this is repo header height -- get it dynamically
+	return s.Error.Copy().Height(h).Render(str)
 }
 
 func (b Bubble) View() string {
@@ -216,7 +219,7 @@ func (b Bubble) View() string {
 	}
 	s.WriteRune('\n')
 	s.WriteString(b.footerView())
-	return appBoxStyle.Render(s.String())
+	return b.styles.App.Render(s.String())
 }
 
 type helpEntry struct {
@@ -224,6 +227,6 @@ type helpEntry struct {
 	val string
 }
 
-func (h helpEntry) String() string {
-	return fmt.Sprintf("%s %s", helpKeyStyle.Render(h.key), helpValueStyle.Render(h.val))
+func (h helpEntry) Render(s *style.Styles) string {
+	return fmt.Sprintf("%s %s", s.HelpKey.Render(h.key), s.HelpValue.Render(h.val))
 }

tui/bubbles/repo/bubble.go 🔗

@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"fmt"
 	"smoothie/git"
+	"smoothie/tui/style"
 	"strconv"
 	"text/template"
 
@@ -22,21 +23,18 @@ type ErrMsg struct {
 }
 
 type Bubble struct {
-	templateObject    interface{}
-	repoSource        *git.RepoSource
-	name              string
-	repo              *git.Repo
-	readmeViewport    *ViewportBubble
-	readme            string
-	height            int
-	heightMargin      int
-	width             int
-	widthMargin       int
-	Active            bool
-	TitleStyle        lipgloss.Style
-	NoteStyle         lipgloss.Style
-	BodyStyle         lipgloss.Style
-	ActiveBorderColor lipgloss.Color
+	templateObject interface{}
+	repoSource     *git.RepoSource
+	name           string
+	repo           *git.Repo
+	styles         *style.Styles
+	readmeViewport *ViewportBubble
+	readme         string
+	height         int
+	heightMargin   int
+	width          int
+	widthMargin    int
+	Active         bool
 
 	// XXX: ideally, we get these from the parent as a pointer. Currently, we
 	// can't add a *tui.Config because it's an illegal import cycle. One
@@ -46,11 +44,12 @@ type Bubble struct {
 	Port int64
 }
 
-func NewBubble(rs *git.RepoSource, name string, width, wm, height, hm int, tmp interface{}) *Bubble {
+func NewBubble(rs *git.RepoSource, name string, styles *style.Styles, width, wm, height, hm int, tmp interface{}) *Bubble {
 	b := &Bubble{
 		templateObject: tmp,
 		repoSource:     rs,
 		name:           name,
+		styles:         styles,
 		heightMargin:   hm,
 		widthMargin:    wm,
 		readmeViewport: &ViewportBubble{
@@ -97,11 +96,11 @@ func (b *Bubble) GotoTop() {
 }
 
 func (b Bubble) headerView() string {
-	ts := b.TitleStyle
-	ns := b.NoteStyle
+	ts := b.styles.RepoTitle
+	ns := b.styles.RepoNote
 	if b.Active {
-		ts = ts.Copy().BorderForeground(b.ActiveBorderColor)
-		ns = ns.Copy().BorderForeground(b.ActiveBorderColor)
+		ts = ts.Copy().BorderForeground(b.styles.ActiveBorderColor)
+		ns = ns.Copy().BorderForeground(b.styles.ActiveBorderColor)
 	}
 	n := b.name
 	if n == "config" {
@@ -114,13 +113,14 @@ func (b Bubble) headerView() string {
 }
 
 func (b *Bubble) View() string {
+	s := b.styles
 	header := b.headerView()
-	bs := b.BodyStyle.Copy()
+	bs := s.RepoBody.Copy()
 	if b.Active {
-		bs = bs.BorderForeground(b.ActiveBorderColor)
+		bs = bs.BorderForeground(s.ActiveBorderColor)
 	}
 	body := bs.
-		Width(b.width - b.widthMargin - b.BodyStyle.GetVerticalFrameSize()).
+		Width(b.width - b.widthMargin - s.RepoBody.GetVerticalFrameSize()).
 		Height(b.height - b.heightMargin - lipgloss.Height(header)).
 		Render(b.readmeViewport.View())
 	return header + body

tui/bubbles/selection/bubble.go 🔗

@@ -1,8 +1,9 @@
 package selection
 
 import (
+	"smoothie/tui/style"
+
 	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/lipgloss"
 )
 
 type SelectedMsg struct {
@@ -16,19 +17,15 @@ type ActiveMsg struct {
 }
 
 type Bubble struct {
-	NormalStyle   lipgloss.Style
-	SelectedStyle lipgloss.Style
-	Cursor        string
-	Items         []string
-	SelectedItem  int
+	Items        []string
+	SelectedItem int
+	styles       *style.Styles
 }
 
-func NewBubble(items []string, normalStyle, selectedStyle lipgloss.Style, cursor string) *Bubble {
+func NewBubble(items []string, styles *style.Styles) *Bubble {
 	return &Bubble{
-		NormalStyle:   normalStyle,
-		SelectedStyle: selectedStyle,
-		Cursor:        cursor,
-		Items:         items,
+		Items:  items,
+		styles: styles,
 	}
 }
 
@@ -40,10 +37,10 @@ func (b Bubble) View() string {
 	s := ""
 	for i, item := range b.Items {
 		if i == b.SelectedItem {
-			s += b.Cursor
-			s += b.SelectedStyle.Render(item)
+			s += b.styles.MenuCursor.String()
+			s += b.styles.SelectedMenuItem.Render(item)
 		} else {
-			s += b.NormalStyle.Render(item)
+			s += b.styles.MenuItem.Render(item)
 		}
 		if i < len(b.Items)-1 {
 			s += "\n"

tui/commands.go 🔗

@@ -49,18 +49,14 @@ func (b *Bubble) setupCmd() tea.Msg {
 			tmplConfig = b.config
 		}
 		width := b.width
-		boxLeftWidth := menuStyle.GetWidth() + menuStyle.GetHorizontalFrameSize()
+		boxLeftWidth := b.styles.Menu.GetWidth() + b.styles.Menu.GetHorizontalFrameSize()
 		// TODO: also send this along with a tea.WindowSizeMsg
 		var heightMargin = lipgloss.Height(b.headerView()) +
 			lipgloss.Height(b.footerView()) +
-			contentBoxStyle.GetVerticalFrameSize() +
-			appBoxStyle.GetVerticalMargins() +
+			b.styles.RepoBody.GetVerticalFrameSize() +
+			b.styles.App.GetVerticalMargins() +
 			3 // TODO: make this dynamic (this is the height of the repo info)
-		rb := repo.NewBubble(b.repoSource, me.Repo, width, boxLeftWidth, b.height, heightMargin, tmplConfig)
-		rb.TitleStyle = contentBoxTitleStyle
-		rb.NoteStyle = contentBoxNoteStyle
-		rb.BodyStyle = contentBoxStyle
-		rb.ActiveBorderColor = activeBorderColor
+		rb := repo.NewBubble(b.repoSource, me.Repo, b.styles, width, boxLeftWidth, b.height, heightMargin, tmplConfig)
 		rb.Host = b.config.Host
 		rb.Port = b.config.Port
 		initCmd := rb.Init()
@@ -73,7 +69,7 @@ func (b *Bubble) setupCmd() tea.Msg {
 		b.repoMenu = append(b.repoMenu, me)
 		rs = append(rs, me.Name)
 	}
-	b.repoSelect = selection.NewBubble(rs, menuItemStyle, selectedMenuItemStyle, menuCursor.String())
+	b.repoSelect = selection.NewBubble(rs, b.styles)
 	b.boxes[0] = b.repoSelect
 	/*
 		b.commitsLog = commits.NewBubble(

tui/style.go 🔗

@@ -1,115 +0,0 @@
-package tui
-
-import (
-	"github.com/charmbracelet/lipgloss"
-)
-
-var activeBorderColor = lipgloss.Color("62")
-var inactiveBorderColor = lipgloss.Color("236")
-
-var viewportTitleBorder = lipgloss.Border{
-	Top:         "─",
-	Bottom:      "─",
-	Left:        "│",
-	Right:       "│",
-	TopLeft:     "╭",
-	TopRight:    "┬",
-	BottomLeft:  "├",
-	BottomRight: "┴",
-}
-
-var viewportNoteBorder = lipgloss.Border{
-	Top:         "─",
-	Bottom:      "─",
-	Left:        "",
-	Right:       "│",
-	TopLeft:     "",
-	TopRight:    "╮",
-	BottomLeft:  "",
-	BottomRight: "┤",
-}
-
-var viewportBodyBorder = lipgloss.Border{
-	Top:         "",
-	Bottom:      "─",
-	Left:        "│",
-	Right:       "│",
-	TopLeft:     "",
-	TopRight:    "",
-	BottomLeft:  "╰",
-	BottomRight: "╯",
-}
-
-var appBoxStyle = lipgloss.NewStyle().
-	Margin(1, 2)
-
-var headerStyle = lipgloss.NewStyle().
-	Foreground(lipgloss.Color("62")).
-	Align(lipgloss.Right).
-	PaddingRight(1).
-	Bold(true)
-
-var menuStyle = lipgloss.NewStyle().
-	BorderStyle(lipgloss.RoundedBorder()).
-	BorderForeground(inactiveBorderColor).
-	Padding(1, 2).
-	MarginRight(1).
-	Width(24)
-
-var menuActiveStyle = menuStyle.Copy().
-	BorderStyle(lipgloss.RoundedBorder()).
-	BorderForeground(activeBorderColor)
-
-var menuCursor = lipgloss.NewStyle().
-	Foreground(lipgloss.Color("213")).
-	SetString(">")
-
-var contentBoxTitleStyle = lipgloss.NewStyle().
-	Border(viewportTitleBorder).
-	BorderForeground(inactiveBorderColor).
-	Padding(0, 2)
-
-var contentBoxNoteStyle = lipgloss.NewStyle().
-	Border(viewportNoteBorder, true, true, true, false).
-	BorderForeground(inactiveBorderColor).
-	Padding(0, 2)
-
-var contentBoxStyle = lipgloss.NewStyle().
-	BorderStyle(viewportBodyBorder).
-	BorderForeground(inactiveBorderColor).
-	PaddingRight(1)
-
-var menuItemStyle = lipgloss.NewStyle().
-	Foreground(lipgloss.Color("252")).
-	PaddingLeft(2)
-
-var selectedMenuItemStyle = lipgloss.NewStyle().
-	Foreground(lipgloss.Color("207")).
-	PaddingLeft(1)
-
-var footerStyle = lipgloss.NewStyle().
-	MarginTop(1)
-
-var helpKeyStyle = lipgloss.NewStyle().
-	Foreground(lipgloss.Color("241"))
-
-var helpValueStyle = lipgloss.NewStyle().
-	Foreground(lipgloss.Color("239"))
-
-var errorStyle = lipgloss.NewStyle().
-	Padding(1)
-
-var errorHeaderStyle = lipgloss.NewStyle().
-	Foreground(lipgloss.Color("230")).
-	Background(lipgloss.Color("204")).
-	Bold(true).
-	Padding(0, 1)
-
-var errorBodyStyle = lipgloss.NewStyle().
-	Foreground(lipgloss.Color("252")).
-	MarginLeft(2).
-	Width(52) // for now
-
-var helpDivider = lipgloss.NewStyle().
-	Foreground(lipgloss.Color("237")).
-	SetString(" • ")

tui/style/style.go 🔗

@@ -0,0 +1,155 @@
+package style
+
+import (
+	"github.com/charmbracelet/lipgloss"
+)
+
+// XXX: For now, this is in its own package so that it can be shared between
+// different packages without incurring an illegal import cycle.
+
+type Styles struct {
+	ActiveBorderColor   lipgloss.Color
+	InactiveBorderColor lipgloss.Color
+
+	App    lipgloss.Style
+	Header lipgloss.Style
+
+	Menu             lipgloss.Style
+	MenuCursor       lipgloss.Style
+	MenuItem         lipgloss.Style
+	SelectedMenuItem lipgloss.Style
+
+	RepoTitleBorder lipgloss.Border
+	RepoNoteBorder  lipgloss.Border
+	RepoBodyBorder  lipgloss.Border
+
+	RepoTitle lipgloss.Style
+	RepoNote  lipgloss.Style
+	RepoBody  lipgloss.Style
+
+	Footer      lipgloss.Style
+	HelpKey     lipgloss.Style
+	HelpValue   lipgloss.Style
+	HelpDivider lipgloss.Style
+
+	Error      lipgloss.Style
+	ErrorTitle lipgloss.Style
+	ErrorBody  lipgloss.Style
+
+	Command lipgloss.Style
+}
+
+func DefaultStyles() *Styles {
+	s := new(Styles)
+
+	s.ActiveBorderColor = lipgloss.Color("62")
+	s.InactiveBorderColor = lipgloss.Color("236")
+
+	s.App = lipgloss.NewStyle().
+		Margin(1, 2)
+
+	s.Header = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("62")).
+		Align(lipgloss.Right).
+		Bold(true)
+
+	s.Menu = lipgloss.NewStyle().
+		BorderStyle(lipgloss.RoundedBorder()).
+		BorderForeground(s.InactiveBorderColor).
+		Padding(1, 2).
+		MarginRight(1).
+		Width(24)
+
+	s.MenuCursor = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("213")).
+		SetString(">")
+
+	s.MenuItem = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("252")).
+		PaddingLeft(2)
+
+	s.SelectedMenuItem = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("207")).
+		PaddingLeft(1)
+
+	s.RepoTitleBorder = lipgloss.Border{
+		Top:         "─",
+		Bottom:      "─",
+		Left:        "│",
+		Right:       "│",
+		TopLeft:     "╭",
+		TopRight:    "┬",
+		BottomLeft:  "├",
+		BottomRight: "┴",
+	}
+
+	s.RepoNoteBorder = lipgloss.Border{
+		Top:         "─",
+		Bottom:      "─",
+		Left:        "",
+		Right:       "│",
+		TopLeft:     "",
+		TopRight:    "╮",
+		BottomLeft:  "",
+		BottomRight: "┤",
+	}
+
+	s.RepoBodyBorder = lipgloss.Border{
+		Top:         "",
+		Bottom:      "─",
+		Left:        "│",
+		Right:       "│",
+		TopLeft:     "",
+		TopRight:    "",
+		BottomLeft:  "╰",
+		BottomRight: "╯",
+	}
+
+	s.RepoTitle = lipgloss.NewStyle().
+		Border(s.RepoTitleBorder).
+		BorderForeground(s.InactiveBorderColor).
+		Padding(0, 2)
+
+	s.RepoNote = lipgloss.NewStyle().
+		Border(s.RepoNoteBorder, true, true, true, false).
+		BorderForeground(s.InactiveBorderColor).
+		Padding(0, 2)
+
+	s.RepoBody = lipgloss.NewStyle().
+		BorderStyle(s.RepoBodyBorder).
+		BorderForeground(s.InactiveBorderColor).
+		PaddingRight(1)
+
+	s.Footer = lipgloss.NewStyle().
+		MarginTop(1)
+
+	s.HelpKey = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("241"))
+
+	s.HelpValue = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("239"))
+
+	s.HelpDivider = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("237")).
+		SetString(" • ")
+
+	s.Error = lipgloss.NewStyle().
+		Padding(1)
+
+	s.ErrorTitle = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("230")).
+		Background(lipgloss.Color("204")).
+		Bold(true).
+		Padding(0, 1)
+
+	s.ErrorBody = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("252")).
+		MarginLeft(2).
+		Width(52) // for now
+
+	s.Command = lipgloss.NewStyle().
+		Background(lipgloss.Color("237")).
+		Foreground(lipgloss.Color("204"))
+
+	return s
+}