Skip to content

Commit 27cf5ab

Browse files
authored
Merge pull request #64 from GustavoCaso/better-tui-code-organization
Better code organization and added status bar
2 parents 9b06a22 + db85561 commit 27cf5ab

11 files changed

Lines changed: 264 additions & 216 deletions

File tree

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ linters:
433433
- gocyclo
434434
- cyclop
435435
# TUI styles are idiomatic as global variables with lipgloss
436-
- path: internal/tui/styles\.go
436+
- path: internal/tui/styles/.*\.go
437437
linters:
438438
- gochecknoglobals
439439
# TUI component styles are idiomatic as global variables with lipgloss

cmd/meeseeks/tui.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"github.com/GustavoCaso/meeseeks/internal/logger"
1313
"github.com/GustavoCaso/meeseeks/internal/server"
1414
"github.com/GustavoCaso/meeseeks/internal/tui"
15-
"github.com/GustavoCaso/meeseeks/internal/tui/tabs"
15+
"github.com/GustavoCaso/meeseeks/internal/tui/tab"
1616
)
1717

1818
func tuiCommand(args []string, _ *logger.Logger) error {
@@ -39,13 +39,13 @@ func tuiCommand(args []string, _ *logger.Logger) error {
3939
client := server.NewClient(ctx, socketPath)
4040

4141
// Create tabs
42-
tabList := []tui.Tab{
43-
tabs.NewPrograms(client),
44-
tabs.NewConfig(client, configPath),
42+
tabList := []tab.Tab{
43+
tab.NewPrograms(client),
44+
tab.NewConfig(client, configPath),
4545
}
4646

4747
// Create and run app
48-
app := tui.NewApp(client, configPath, tabList)
48+
app := tui.NewApp(client, configPath, tabList, version)
4949
p := tea.NewProgram(app, tea.WithAltScreen())
5050

5151
if _, err := p.Run(); err != nil {

internal/tui/app.go

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import (
77
"time"
88

99
tuiContext "github.com/GustavoCaso/meeseeks/internal/tui/context"
10+
"github.com/GustavoCaso/meeseeks/internal/tui/messages"
11+
"github.com/GustavoCaso/meeseeks/internal/tui/styles"
12+
"github.com/GustavoCaso/meeseeks/internal/tui/tab"
1013

1114
"github.com/charmbracelet/bubbles/key"
1215
tea "github.com/charmbracelet/bubbletea"
@@ -24,11 +27,12 @@ const statusBarTickInterval = 2 * time.Second
2427
type App struct {
2528
client *server.Client
2629
configPath string
27-
tabs []Tab
30+
tabs []tab.Tab
2831
activeTab int
2932
ctx tuiContext.Context
3033
header components.Component
31-
footer components.Component
34+
status components.Component
35+
help components.Component
3236
width int
3337
height int
3438
globalKeys GlobalKeyMap
@@ -37,7 +41,7 @@ type App struct {
3741
}
3842

3943
// NewApp creates a new App instance.
40-
func NewApp(client *server.Client, configPath string, tabs []Tab) *App {
44+
func NewApp(client *server.Client, configPath string, tabs []tab.Tab, version string) *App {
4145
ctx := tuiContext.Context{}
4246
tabNames := make([]string, len(tabs))
4347
for i, tab := range tabs {
@@ -51,7 +55,8 @@ func NewApp(client *server.Client, configPath string, tabs []Tab) *App {
5155
activeTab: 0,
5256
ctx: ctx,
5357
header: components.NewHeader(ctx, tabNames),
54-
footer: components.NewFooter(ctx),
58+
status: components.NewStatus(ctx, version),
59+
help: components.NewHelp(ctx),
5560
globalKeys: DefaultGlobalKeyMap(),
5661
}
5762
}
@@ -77,13 +82,17 @@ func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
7782
switch msg := msg.(type) {
7883
case tea.WindowSizeMsg:
7984
return a.handleWindowSize(msg)
80-
case TickMsg:
85+
case messages.TickMsg:
8186
return a, tea.Batch(a.tick(), a.fetchStatus())
82-
case ClearStatusBarTickMsg:
87+
case messages.ClearStatusBarTickMsg:
8388
return a, tea.Batch(a.clearStatusBarTick(), a.clearStatus())
84-
case SwitchTabMsg:
89+
case messages.SwitchTabMsg:
8590
return a.handleSwitchTab(msg)
86-
case ErrorMsg:
91+
case messages.SetStatusBarMsg:
92+
return a.handleSetStatus(msg)
93+
case messages.ClearStatusBarMsg:
94+
return a.handleClearStatus(msg)
95+
case messages.ErrorMsg:
8796
a.err = msg.Error
8897
return a, nil
8998
case tea.KeyMsg:
@@ -102,21 +111,22 @@ func (a *App) handleWindowSize(msg tea.WindowSizeMsg) (tea.Model, tea.Cmd) {
102111
a.width = msg.Width
103112
a.height = msg.Height
104113

105-
// Header and footer each take ~2 lines (content + border)
106114
headerHeight := lipgloss.Height(a.header.View())
107-
footerHeight := lipgloss.Height(a.footer.View())
108-
contentHeight := a.height - headerHeight - footerHeight
115+
statusHeight := lipgloss.Height(a.status.View())
116+
helpHeight := 1 // We do not want to help to take more space than one
117+
contentHeight := a.height - headerHeight - statusHeight - helpHeight
109118

110119
a.header.SetSize(a.width, headerHeight)
111-
a.footer.SetSize(a.width, footerHeight)
120+
a.status.SetSize(a.width, statusHeight)
121+
a.help.SetSize(a.width, helpHeight)
112122

113123
for _, tab := range a.tabs {
114124
tab.SetSize(a.width, contentHeight)
115125
}
116126
return a, nil
117127
}
118128

119-
func (a *App) handleSwitchTab(msg SwitchTabMsg) (tea.Model, tea.Cmd) {
129+
func (a *App) handleSwitchTab(msg messages.SwitchTabMsg) (tea.Model, tea.Cmd) {
120130
a.activeTab = msg.TabIndex
121131
if a.activeTab < len(a.tabs) {
122132
tab, cmd := a.tabs[a.activeTab].Update(msg)
@@ -126,13 +136,28 @@ func (a *App) handleSwitchTab(msg SwitchTabMsg) (tea.Model, tea.Cmd) {
126136
return a, nil
127137
}
128138

139+
func (a *App) handleSetStatus(msg messages.SetStatusBarMsg) (tea.Model, tea.Cmd) {
140+
status, cmd := a.status.Update(msg)
141+
a.status = status
142+
143+
return a, cmd
144+
}
145+
146+
func (a *App) handleClearStatus(msg messages.ClearStatusBarMsg) (tea.Model, tea.Cmd) {
147+
status, cmd := a.status.Update(msg)
148+
a.status = status
149+
150+
return a, cmd
151+
}
152+
129153
func (a *App) syncContext() {
130154
a.ctx.ActiveTab = a.activeTab
131155
if a.activeTab < len(a.tabs) {
132156
a.ctx.HelpBindings = a.tabs[a.activeTab].ShortHelp()
133157
}
134158
a.header.SyncAppContext(a.ctx)
135-
a.footer.SyncAppContext(a.ctx)
159+
a.status.SyncAppContext(a.ctx)
160+
a.help.SyncAppContext(a.ctx)
136161
}
137162

138163
func (a *App) handleGlobalKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd, bool) {
@@ -162,8 +187,8 @@ func (a *App) forwardToTabs(msg tea.Msg) (tea.Model, tea.Cmd) {
162187
}
163188
}
164189

165-
_, initConfigMsg := msg.(InitialConfigLoadMsg)
166-
_, statusUpdateMsg := msg.(StatusUpdateMsg)
190+
_, initConfigMsg := msg.(messages.InitialConfigLoadMsg)
191+
_, statusUpdateMsg := msg.(messages.StatusUpdateMsg)
167192

168193
if initConfigMsg || statusUpdateMsg {
169194
for i, tab := range a.tabs {
@@ -187,27 +212,28 @@ func (a *App) View() string {
187212

188213
header := a.header.View()
189214
tabContent := a.tabs[a.activeTab].View()
190-
content := ContentStyle.Render(tabContent)
191-
footer := a.footer.View()
215+
content := styles.ContentStyle.Render(tabContent)
216+
status := a.status.View()
217+
help := a.help.View()
192218

193-
return lipgloss.JoinVertical(lipgloss.Top, header, content, footer)
219+
return lipgloss.JoinVertical(lipgloss.Top, header, content, status, help)
194220
}
195221

196222
func (a *App) tick() tea.Cmd {
197223
return tea.Tick(tickInterval, func(time.Time) tea.Msg {
198-
return TickMsg{}
224+
return messages.TickMsg{}
199225
})
200226
}
201227

202228
func (a *App) clearStatusBarTick() tea.Cmd {
203229
return tea.Tick(statusBarTickInterval, func(time.Time) tea.Msg {
204-
return ClearStatusBarTickMsg{}
230+
return messages.ClearStatusBarTickMsg{}
205231
})
206232
}
207233

208234
func (a *App) clearStatus() tea.Cmd {
209235
return func() tea.Msg {
210-
return ClearStatusBarMsg{}
236+
return messages.ClearStatusBarMsg{}
211237
}
212238
}
213239

@@ -216,20 +242,20 @@ func (a *App) fetchStatus() tea.Cmd {
216242
ctx := context.Background()
217243
resp, err := a.client.Statistics(ctx, "")
218244
if err != nil {
219-
return StatusUpdateMsg{Err: err}
245+
return messages.StatusUpdateMsg{Err: err}
220246
}
221247

222248
if !resp.Success {
223-
return StatusUpdateMsg{Err: fmt.Errorf("%s", resp.Error)}
249+
return messages.StatusUpdateMsg{Err: fmt.Errorf("%s", resp.Error)}
224250
}
225251

226252
// Parse statistics from response
227253
stats, err := parseStatistics(resp.Data)
228254
if err != nil {
229-
return StatusUpdateMsg{Err: err}
255+
return messages.StatusUpdateMsg{Err: err}
230256
}
231257

232-
return StatusUpdateMsg{Statistics: stats}
258+
return messages.StatusUpdateMsg{Statistics: stats}
233259
}
234260
}
235261

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,45 +16,40 @@ var (
1616
BorderStyle(lipgloss.NormalBorder()).
1717
BorderTop(true).
1818
Foreground(lipgloss.Color("8"))
19-
HelpBarStyleWidth = lipgloss.Width(HelpBarStyle.Render(""))
20-
21-
HelpKeyStyle = lipgloss.NewStyle().
22-
Bold(true)
23-
24-
HelpDescStyle = lipgloss.NewStyle().
25-
Foreground(lipgloss.Color("8"))
19+
HelpKeyStyle = lipgloss.NewStyle().Bold(true)
20+
HelpDescStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
2621
)
2722

28-
type footer struct {
23+
type help struct {
2924
ctx context.Context
3025
width int
3126
height int
3227
content string
3328
}
3429

35-
func NewFooter(ctx context.Context) Component {
36-
return &footer{
30+
func NewHelp(ctx context.Context) Component {
31+
return &help{
3732
ctx: ctx,
3833
width: 0,
3934
height: 0,
4035
content: "",
4136
}
4237
}
4338

44-
func (f *footer) SetSize(width, height int) {
45-
f.width = width
46-
f.height = height
39+
func (h *help) SetSize(width, height int) {
40+
h.width = width
41+
h.height = height
4742
}
4843

49-
func (f *footer) Init() tea.Cmd {
44+
func (h *help) Init() tea.Cmd {
5045
return nil
5146
}
5247

53-
func (f *footer) updateContent() {
48+
func (h *help) updateContent() {
5449
var keys []string
5550

5651
// Add tab-specific key bindings
57-
for _, binding := range f.ctx.HelpBindings {
52+
for _, binding := range h.ctx.HelpBindings {
5853
keys = append(keys, fmt.Sprintf("%s %s",
5954
HelpKeyStyle.Render(binding.Help().Key),
6055
HelpDescStyle.Render(binding.Help().Desc)))
@@ -65,20 +60,21 @@ func (f *footer) updateContent() {
6560
HelpKeyStyle.Render("q"),
6661
HelpDescStyle.Render("quit")))
6762

68-
content := strings.Join(keys, " ")
69-
f.content = HelpBarStyle.Width(f.width - HelpBarStyleWidth).MaxHeight(f.height).Render(content)
63+
h.content = HelpBarStyle.Width(h.width).
64+
Height(h.height).
65+
Render(strings.Join(keys, " "))
7066
}
7167

72-
func (f *footer) View() string {
73-
f.updateContent()
74-
return f.content
68+
func (h *help) View() string {
69+
h.updateContent()
70+
return h.content
7571
}
7672

77-
func (f *footer) Update(msg tea.Msg) (Component, tea.Cmd) {
78-
return f, nil
73+
func (h *help) Update(msg tea.Msg) (Component, tea.Cmd) {
74+
return h, nil
7975
}
8076

81-
func (f *footer) SyncAppContext(ctx context.Context) {
82-
f.ctx = ctx
83-
f.updateContent()
77+
func (h *help) SyncAppContext(ctx context.Context) {
78+
h.ctx = ctx
79+
h.updateContent()
8480
}

0 commit comments

Comments
 (0)