1package tui
2
3import (
4 tea "github.com/charmbracelet/bubbletea"
5 "github.com/charmbracelet/lipgloss"
6 "github.com/charmbracelet/soft-serve/git"
7 "github.com/charmbracelet/soft-serve/internal/tui/style"
8 "github.com/charmbracelet/soft-serve/tui/about"
9 "github.com/charmbracelet/soft-serve/tui/common"
10 "github.com/charmbracelet/soft-serve/tui/log"
11 "github.com/charmbracelet/soft-serve/tui/refs"
12 "github.com/charmbracelet/soft-serve/tui/tree"
13)
14
15const (
16 repoNameMaxWidth = 32
17)
18
19type state int
20
21const (
22 aboutState state = iota
23 refsState
24 logState
25 treeState
26)
27
28type Bubble struct {
29 state state
30 repo common.GitRepo
31 height int
32 heightMargin int
33 width int
34 widthMargin int
35 style *style.Styles
36 boxes []tea.Model
37 ref *git.Reference
38}
39
40func NewBubble(repo common.GitRepo, styles *style.Styles, width, wm, height, hm int) *Bubble {
41 b := &Bubble{
42 repo: repo,
43 state: aboutState,
44 width: width,
45 widthMargin: wm,
46 height: height,
47 heightMargin: hm,
48 style: styles,
49 boxes: make([]tea.Model, 4),
50 }
51 heightMargin := hm + lipgloss.Height(b.headerView())
52 b.boxes[aboutState] = about.NewBubble(repo, b.style, b.width, wm, b.height, heightMargin)
53 b.boxes[refsState] = refs.NewBubble(repo, b.style, b.width, wm, b.height, heightMargin)
54 b.boxes[logState] = log.NewBubble(repo, b.style, width, wm, height, heightMargin)
55 b.boxes[treeState] = tree.NewBubble(repo, b.style, width, wm, height, heightMargin)
56 return b
57}
58
59func (b *Bubble) Init() tea.Cmd {
60 return b.setupCmd
61}
62
63func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
64 cmds := make([]tea.Cmd, 0)
65 switch msg := msg.(type) {
66 case tea.KeyMsg:
67 if b.repo.Name() != "config" {
68 switch msg.String() {
69 case "R":
70 b.state = aboutState
71 case "B":
72 b.state = refsState
73 case "C":
74 b.state = logState
75 case "F":
76 b.state = treeState
77 }
78 }
79 case tea.WindowSizeMsg:
80 b.width = msg.Width
81 b.height = msg.Height
82 for i, bx := range b.boxes {
83 m, cmd := bx.Update(msg)
84 b.boxes[i] = m
85 if cmd != nil {
86 cmds = append(cmds, cmd)
87 }
88 }
89 case refs.RefMsg:
90 b.state = treeState
91 b.ref = msg
92 for i, bx := range b.boxes {
93 m, cmd := bx.Update(msg)
94 b.boxes[i] = m
95 if cmd != nil {
96 cmds = append(cmds, cmd)
97 }
98 }
99 }
100 m, cmd := b.boxes[b.state].Update(msg)
101 b.boxes[b.state] = m
102 if cmd != nil {
103 cmds = append(cmds, cmd)
104 }
105 return b, tea.Batch(cmds...)
106}
107
108func (b *Bubble) Help() []common.HelpEntry {
109 h := []common.HelpEntry{}
110 h = append(h, b.boxes[b.state].(common.BubbleHelper).Help()...)
111 if b.repo.Name() != "config" {
112 h = append(h, common.HelpEntry{Key: "R", Value: "readme"})
113 h = append(h, common.HelpEntry{Key: "F", Value: "files"})
114 h = append(h, common.HelpEntry{Key: "C", Value: "commits"})
115 h = append(h, common.HelpEntry{Key: "B", Value: "branches"})
116 }
117 return h
118}
119
120func (b *Bubble) Reference() *git.Reference {
121 return b.ref
122}
123
124func (b *Bubble) headerView() string {
125 // TODO better header, tabs?
126 return ""
127}
128
129func (b *Bubble) View() string {
130 header := b.headerView()
131 return header + b.boxes[b.state].View()
132}
133
134func (b *Bubble) setupCmd() tea.Msg {
135 head, err := b.repo.HEAD()
136 if err != nil {
137 return common.ErrMsg{Err: err}
138 }
139 b.ref = head
140 cmds := make([]tea.Cmd, 0)
141 for _, bx := range b.boxes {
142 if bx != nil {
143 initCmd := bx.Init()
144 if initCmd != nil {
145 msg := initCmd()
146 switch msg := msg.(type) {
147 case common.ErrMsg:
148 return msg
149 }
150 }
151 cmds = append(cmds, initCmd)
152 }
153 }
154 return tea.Batch(cmds...)
155}