Skip to content

Commit cf298f5

Browse files
authored
Merge pull request #39 from hugoh/output-screen
feat(tui): add output screen toggle and Enter-to-close
2 parents 6fd9271 + 0da664b commit cf298f5

5 files changed

Lines changed: 118 additions & 3 deletions

File tree

internal/tui/bindings.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ var mainBindings = []binding{
176176

177177
return m, nil
178178
}, label: "history", desc: "Restore a previous repo selection", hrd: true, section: secGeneral, order: 25},
179+
{key: "o", handler: func(m *model) (tea.Model, tea.Cmd) {
180+
m.screen = screenOutput
181+
182+
return m, nil
183+
}, label: "output", desc: "Toggle last output", hrd: true, section: secGeneral, order: 27},
179184

180185
// Cmd bar
181186
{

internal/tui/screens.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,16 @@ func (m *model) handleGroupNewInput(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
134134

135135
func (m *model) handleOutputKey(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
136136
switch msg.String() {
137-
case keyEsc, "q":
137+
case keyEsc, "q", "o":
138138
m.screen = screenMain
139139

140140
return m, nil
141+
case "enter":
142+
if !m.executing {
143+
m.screen = screenMain
144+
145+
return m, nil
146+
}
141147
}
142148

143149
var cmd tea.Cmd

internal/tui/screens_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,111 @@ func TestHandleOutputKeyQ(t *testing.T) {
173173
assert.Nil(t, cmd, "expected nil cmd when closing output with q")
174174
}
175175

176+
func TestHandleOutputKeyEnterClosesWhenDone(t *testing.T) {
177+
m := &model{
178+
screen: screenOutput,
179+
executing: false,
180+
output: viewport.New(viewport.WithWidth(80), viewport.WithHeight(10)),
181+
}
182+
183+
_, cmd := m.handleOutputKey(tea.KeyPressMsg{Code: tea.KeyEnter})
184+
185+
assert.Equal(t, screenMain, m.screen, "Enter should close output when not executing")
186+
assert.Nil(t, cmd, "expected nil cmd")
187+
}
188+
189+
func TestHandleOutputKeyEnterNoOpWhileExecuting(t *testing.T) {
190+
m := &model{
191+
screen: screenOutput,
192+
executing: true,
193+
output: viewport.New(viewport.WithWidth(80), viewport.WithHeight(10)),
194+
}
195+
196+
_, _ = m.handleOutputKey(tea.KeyPressMsg{Code: tea.KeyEnter})
197+
198+
assert.Equal(t, screenOutput, m.screen, "Enter should not close output while executing")
199+
}
200+
201+
func TestHandleOutputKeyOClosesOutput(t *testing.T) {
202+
m := &model{
203+
screen: screenOutput,
204+
output: viewport.New(viewport.WithWidth(80), viewport.WithHeight(10)),
205+
}
206+
207+
_, cmd := m.handleOutputKey(tea.KeyPressMsg{Code: 'o'})
208+
209+
assert.Equal(t, screenMain, m.screen, "o should close the output screen")
210+
assert.Nil(t, cmd, "expected nil cmd")
211+
}
212+
213+
func TestOutputContentPreservedOnClose(t *testing.T) {
214+
const content = "repo1 ✓\nrepo2 ✗"
215+
216+
for _, tc := range []struct {
217+
name string
218+
key tea.KeyPressMsg
219+
}{
220+
{"Esc", tea.KeyPressMsg{Code: tea.KeyEsc}},
221+
{"q", tea.KeyPressMsg{Code: 'q'}},
222+
{"o", tea.KeyPressMsg{Code: 'o'}},
223+
{"Enter", tea.KeyPressMsg{Code: tea.KeyEnter}},
224+
} {
225+
t.Run(tc.name, func(t *testing.T) {
226+
m := &model{
227+
screen: screenOutput,
228+
output: viewport.New(viewport.WithWidth(80), viewport.WithHeight(10)),
229+
}
230+
m.output.SetContent(content)
231+
232+
_, _ = m.handleOutputKey(tc.key)
233+
234+
assert.Equal(
235+
t,
236+
content,
237+
m.output.GetContent(),
238+
"output content should be preserved after %s",
239+
tc.name,
240+
)
241+
})
242+
}
243+
}
244+
245+
func TestHandleEscOutputPreservesContent(t *testing.T) {
246+
const content = "repo1 ✓"
247+
248+
m := &model{
249+
screen: screenOutput,
250+
output: viewport.New(viewport.WithWidth(80), viewport.WithHeight(10)),
251+
}
252+
m.output.SetContent(content)
253+
254+
_, _ = m.handleEscKey()
255+
256+
assert.Equal(t, screenMain, m.screen, "screen")
257+
assert.Equal(t, content, m.output.GetContent(), "output content should be preserved after Esc")
258+
}
259+
260+
func TestHandleMainKeyOOpensOutputWithResults(t *testing.T) {
261+
m := &model{
262+
execResults: []execResult{{name: "repo1"}},
263+
output: viewport.New(viewport.WithWidth(80), viewport.WithHeight(10)),
264+
}
265+
266+
_, _ = m.handleKeyMsg(tea.KeyPressMsg{Code: 'o'})
267+
268+
assert.Equal(t, screenOutput, m.screen, "o should open output screen when results exist")
269+
}
270+
271+
func TestHandleMainKeyOOpensOutputWithoutResults(t *testing.T) {
272+
m := &model{
273+
output: viewport.New(viewport.WithWidth(80), viewport.WithHeight(10)),
274+
}
275+
276+
_, _ = m.handleKeyMsg(tea.KeyPressMsg{Code: 'o'})
277+
278+
assert.Equal(t, screenOutput, m.screen, "o should open output screen even with no results")
279+
}
280+
176281
func TestSortedSelected(t *testing.T) {
177282
selected := map[string]bool{"c": true, "a": true, "b": false}
178283
got := sortedSelected(selected)

internal/tui/update.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ func (m *model) handleEscKey() (tea.Model, tea.Cmd) {
189189
switch m.screen { //nolint:exhaustive
190190
case screenOutput:
191191
m.screen = screenMain
192-
m.output.SetContent("")
193192

194193
return m, nil
195194
case screenHelp, screenGroup, screenSelHistory:

internal/tui/view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func (m *model) outputView() string {
218218

219219
var left, right string
220220

221-
right = ui.MutedStyle().Render(" Esc/q:close")
221+
right = ui.MutedStyle().Render(" Enter/o/Esc/q:close")
222222

223223
if m.executing && m.execTotal > 0 {
224224
done := len(m.execResults)

0 commit comments

Comments
 (0)