diff --git a/tui/bubble.go b/tui/bubble.go index c18c2a254..5c68eb0b0 100644 --- a/tui/bubble.go +++ b/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)) } diff --git a/tui/bubbles/repo/bubble.go b/tui/bubbles/repo/bubble.go index e4ea72c68..eafba0df5 100644 --- a/tui/bubbles/repo/bubble.go +++ b/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 diff --git a/tui/bubbles/selection/bubble.go b/tui/bubbles/selection/bubble.go index 75736b268..13f94f12b 100644 --- a/tui/bubbles/selection/bubble.go +++ b/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" diff --git a/tui/commands.go b/tui/commands.go index 795e1eaec..623cbedbf 100644 --- a/tui/commands.go +++ b/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( diff --git a/tui/style.go b/tui/style.go deleted file mode 100644 index 7d25bfbb8..000000000 --- a/tui/style.go +++ /dev/null @@ -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(" • ") diff --git a/tui/style/style.go b/tui/style/style.go new file mode 100644 index 000000000..aad8135b3 --- /dev/null +++ b/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 +}