Skip to content

Commit 53cf070

Browse files
committed
Refactor GitLab Runner TUI to enhance tab management and status display. Introduced a new switchTab method for initializing views on tab switch, and moved help text to a status bar for better UI clarity. Updated main.go and various view files to reflect these changes, improving user experience and code organization.
1 parent eea0eec commit 53cf070

File tree

6 files changed

+113
-49
lines changed

6 files changed

+113
-49
lines changed

cmd/gitlab-runner-tui/main.go

Lines changed: 108 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"log"
77
"os"
8+
"strings"
89

910
tea "github.com/charmbracelet/bubbletea"
1011
"github.com/charmbracelet/lipgloss"
@@ -13,24 +14,25 @@ import (
1314
)
1415

1516
type model struct {
16-
tabs []string
17-
activeTab int
18-
runnersView *ui.RunnersView
19-
logsView *ui.LogsView
20-
configView *ui.ConfigView
21-
systemView *ui.SystemView
22-
historyView *ui.HistoryView
23-
width int
24-
height int
25-
quitting bool
26-
debugMode bool
17+
tabs []string
18+
activeTab int
19+
runnersView *ui.RunnersView
20+
logsView *ui.LogsView
21+
configView *ui.ConfigView
22+
systemView *ui.SystemView
23+
historyView *ui.HistoryView
24+
width int
25+
height int
26+
quitting bool
27+
debugMode bool
28+
initialized map[int]bool
2729
}
2830

2931
func initialModel(configPath string, debugMode bool) model {
3032
service := runner.NewService(configPath)
3133
service.SetDebugMode(debugMode)
3234

33-
return model{
35+
m := model{
3436
tabs: []string{"Runners", "Logs", "Config", "System", "History"},
3537
activeTab: 0,
3638
runnersView: ui.NewRunnersView(service),
@@ -39,17 +41,15 @@ func initialModel(configPath string, debugMode bool) model {
3941
systemView: ui.NewSystemView(service),
4042
historyView: ui.NewHistoryView(service),
4143
debugMode: debugMode,
44+
initialized: make(map[int]bool),
4245
}
46+
m.initialized[0] = true // Mark first tab as initialized
47+
return m
4348
}
4449

4550
func (m model) Init() tea.Cmd {
46-
return tea.Batch(
47-
m.runnersView.Init(),
48-
m.logsView.Init(),
49-
m.configView.Init(),
50-
m.systemView.Init(),
51-
m.historyView.Init(),
52-
)
51+
// Only initialize the first view
52+
return m.runnersView.Init()
5353
}
5454

5555
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
@@ -80,15 +80,16 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
8080

8181
case "tab":
8282
m.activeTab = (m.activeTab + 1) % len(m.tabs)
83-
return m, nil
83+
return m.switchTab()
8484

8585
case "shift+tab":
8686
m.activeTab = (m.activeTab - 1 + len(m.tabs)) % len(m.tabs)
87-
return m, nil
87+
return m.switchTab()
8888

8989
case "1", "2", "3", "4", "5":
9090
if idx := int(msg.String()[0] - '1'); idx < len(m.tabs) {
9191
m.activeTab = idx
92+
return m.switchTab()
9293
}
9394
return m, nil
9495

@@ -97,6 +98,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
9798
if runner := m.runnersView.GetSelectedRunner(); runner != nil {
9899
m.logsView.SetRunner(runner.Name)
99100
m.activeTab = 1
101+
return m.switchTab()
100102
}
101103
}
102104
}
@@ -128,6 +130,23 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
128130
return m, tea.Batch(cmds...)
129131
}
130132

133+
func (m model) switchTab() (model, tea.Cmd) {
134+
if !m.initialized[m.activeTab] {
135+
m.initialized[m.activeTab] = true
136+
switch m.activeTab {
137+
case 1:
138+
return m, m.logsView.Init()
139+
case 2:
140+
return m, m.configView.Init()
141+
case 3:
142+
return m, m.systemView.Init()
143+
case 4:
144+
return m, m.historyView.Init()
145+
}
146+
}
147+
return m, nil
148+
}
149+
131150
func (m model) View() string {
132151
if m.quitting {
133152
return ""
@@ -149,10 +168,23 @@ func (m model) View() string {
149168
content = m.historyView.View()
150169
}
151170

171+
statusBar := m.renderStatusBar()
172+
173+
// Calculate available height for content
174+
availableHeight := m.height - lipgloss.Height(tabBar) - lipgloss.Height(statusBar) - 1
175+
176+
// Ensure content doesn't overflow
177+
contentLines := strings.Split(content, "\n")
178+
if len(contentLines) > availableHeight {
179+
content = strings.Join(contentLines[:availableHeight], "\n")
180+
}
181+
152182
return lipgloss.JoinVertical(
153183
lipgloss.Left,
154184
tabBar,
155185
content,
186+
strings.Repeat("\n", m.height-lipgloss.Height(tabBar)-lipgloss.Height(content)-lipgloss.Height(statusBar)),
187+
statusBar,
156188
)
157189
}
158190

@@ -170,6 +202,61 @@ func (m model) renderTabBar() string {
170202
return lipgloss.JoinHorizontal(lipgloss.Top, tabs...)
171203
}
172204

205+
func (m model) renderStatusBar() string {
206+
// Create styles for the status bar
207+
statusStyle := lipgloss.NewStyle().
208+
Background(ui.ColorPrimary).
209+
Foreground(ui.ColorBg).
210+
Padding(0, 1)
211+
212+
helpStyle := lipgloss.NewStyle().
213+
Background(ui.ColorSecondary).
214+
Foreground(ui.ColorBg).
215+
Padding(0, 1)
216+
217+
// Build contextual help based on active tab
218+
var commands []string
219+
220+
// Global commands
221+
commands = append(commands, "Tab/Shift+Tab: Switch tabs", "1-5: Jump to tab", "q: Quit")
222+
223+
// Tab-specific commands
224+
switch m.activeTab {
225+
case 0: // Runners
226+
commands = append(commands, "↑/↓: Navigate", "Enter: View logs", "r: Refresh")
227+
case 1: // Logs
228+
commands = append(commands, "↑/↓: Scroll", "g/G: Top/Bottom", "a: Auto-scroll", "c: Clear", "r: Refresh")
229+
case 2: // Config
230+
commands = append(commands, "Tab: Next field", "Ctrl+S: Save", "r: Edit runners")
231+
case 3: // System
232+
commands = append(commands, "r: Refresh", "s: Restart service")
233+
case 4: // History
234+
commands = append(commands, "↑/↓: Navigate", "r: Refresh")
235+
}
236+
237+
// Add debug mode indicator if enabled
238+
statusText := ""
239+
if m.debugMode {
240+
statusText = " [DEBUG] "
241+
}
242+
243+
// Combine status and help
244+
status := statusStyle.Render(statusText + m.tabs[m.activeTab])
245+
help := helpStyle.Render(strings.Join(commands, " • "))
246+
247+
// Fill the remaining width
248+
statusBarContent := lipgloss.JoinHorizontal(lipgloss.Top, status, " ", help)
249+
remainingWidth := m.width - lipgloss.Width(statusBarContent)
250+
if remainingWidth > 0 {
251+
filler := lipgloss.NewStyle().
252+
Background(ui.ColorSecondary).
253+
Render(strings.Repeat(" ", remainingWidth))
254+
statusBarContent = lipgloss.JoinHorizontal(lipgloss.Top, statusBarContent, filler)
255+
}
256+
257+
return statusBarContent
258+
}
259+
173260
func main() {
174261
var configPath string
175262
var debugMode bool

pkg/ui/config_view.go

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -228,18 +228,7 @@ func (v *ConfigView) View() string {
228228
content = append(content, "", SuccessBoxStyle.Render(v.successMsg))
229229
}
230230

231-
help := []string{
232-
"'tab' navigate",
233-
"'ctrl+s' save",
234-
}
235-
if !v.editingRunner {
236-
help = append(help, "'r' edit runners")
237-
} else {
238-
help = append(help, "'↑/↓' select runner", "'esc' back")
239-
}
240-
help = append(help, "'q' quit")
241-
242-
content = append(content, "", HelpStyle.Render(strings.Join(help, " • ")))
231+
// Help is now shown in the status bar
243232

244233
return lipgloss.JoinVertical(lipgloss.Left, content...)
245234
}

pkg/ui/history_view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func (v *HistoryView) View() string {
149149
)
150150
}
151151

152-
content = append(content, "", HelpStyle.Render("Press 'r' to refresh • 'q' to go back"))
152+
// Help is now shown in the status bar
153153

154154
return lipgloss.JoinVertical(lipgloss.Left, content...)
155155
}

pkg/ui/logs_view.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,7 @@ func (v *LogsView) View() string {
132132
)
133133
}
134134

135-
help := []string{
136-
"'r' refresh",
137-
"'a' toggle auto-scroll",
138-
"'g'/'G' top/bottom",
139-
"'c' clear",
140-
"'q' back",
141-
}
142-
content = append(content, "", HelpStyle.Render(strings.Join(help, " • ")))
135+
// Help is now shown in the status bar
143136

144137
return lipgloss.JoinVertical(lipgloss.Left, content...)
145138
}

pkg/ui/runners_view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func (v *RunnersView) View() string {
136136
content = append(content, v.table.View())
137137
}
138138

139-
content = append(content, "", HelpStyle.Render("Press 'r' to refresh • 'enter' to view details • 'q' to quit"))
139+
// Help is now shown in the status bar
140140

141141
return lipgloss.JoinVertical(lipgloss.Left, content...)
142142
}

pkg/ui/system_view.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,7 @@ func (v *SystemView) View() string {
145145
content = append(content, v.memProgress.ViewAs(memPercent))
146146
}
147147

148-
help := []string{
149-
"'r' refresh",
150-
"'s' restart service",
151-
"'q' back",
152-
}
153-
content = append(content, "", HelpStyle.Render(lipgloss.JoinHorizontal(lipgloss.Left, help...)))
148+
// Help is now shown in the status bar
154149

155150
return lipgloss.JoinVertical(lipgloss.Left, content...)
156151
}

0 commit comments

Comments
 (0)