Skip to content

Commit 6e3a92b

Browse files
fix(tui): preserve selections during refresh (#591)
closes #586 ## Summary - Preserve last queue entry visible in distraction-free mode during job refresh - Preserve review view selection during job refresh so it doesn't jump back to the first item 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d2d671f commit 6e3a92b

2 files changed

Lines changed: 52 additions & 2 deletions

File tree

cmd/roborev/tui/queue_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,46 @@ func TestTUIQueueDistractionFreeToggle(t *testing.T) {
347347
assert.Contains(t, output, "JobID")
348348
}
349349

350+
// Regression test for #586: distraction-free mode lost the last queue entry.
351+
// The lipgloss table drops the last data row when Headers() is not called,
352+
// so compact mode must still supply (empty) headers and strip the resulting
353+
// blank line.
354+
func TestTUIQueueDistractionFreePreservesLastJob(t *testing.T) {
355+
assert := assert.New(t)
356+
for _, h := range []int{20, 24, 30} {
357+
for _, n := range []int{10, 25, 40} {
358+
t.Run(fmt.Sprintf("h%d_jobs%d", h, n), func(t *testing.T) {
359+
m := newTuiModel("http://localhost")
360+
m.currentView = tuiViewQueue
361+
m.width = 120
362+
m.height = h
363+
for i := range n {
364+
m.jobs = append(m.jobs, makeJob(int64(9300+i)))
365+
}
366+
lastJobID := fmt.Sprintf("%d", 9300+n-1)
367+
// Select near the end so the last job is in the scroll window.
368+
m.selectedIdx = n - 2
369+
m.selectedJobID = int64(9300 + n - 2)
370+
371+
normalOutput := m.View()
372+
normalHasLast := strings.Contains(normalOutput, lastJobID)
373+
374+
m2, _ := updateModel(t, m, tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'D'}})
375+
compactOutput := m2.View()
376+
compactHasLast := strings.Contains(compactOutput, lastJobID)
377+
378+
if normalHasLast {
379+
assert.True(compactHasLast,
380+
"h=%d n=%d: job %s visible before D but missing after", h, n, lastJobID)
381+
}
382+
compactLines := strings.Count(compactOutput, "\n") + 1
383+
assert.LessOrEqual(compactLines, h,
384+
"h=%d n=%d: output %d lines exceeds terminal height", h, n, compactLines)
385+
})
386+
}
387+
}
388+
}
389+
350390
func TestTUITasksMouseClickSelectsRow(t *testing.T) {
351391
m := newTuiModel("http://localhost")
352392
m.currentView = tuiViewTasks

cmd/roborev/tui/render_queue.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,16 +530,26 @@ func (m model) renderQueueView() string {
530530
return s
531531
})
532532

533+
// Always set headers — lipgloss table drops the last data row
534+
// when Headers() is not called.
535+
headers := make([]string, len(visCols))
533536
if !compact {
534-
headers := make([]string, len(visCols))
535537
for vi, c := range visCols {
536538
headers[vi] = allHeaders[c]
537539
}
538-
t = t.Headers(headers...)
539540
}
541+
t = t.Headers(headers...)
540542
t = t.Rows(rows...)
541543

542544
tableStr := t.Render()
545+
546+
// In compact mode, strip the empty header line we added as a
547+
// workaround (it renders as a row of spaces).
548+
if compact {
549+
if idx := strings.Index(tableStr, "\n"); idx >= 0 {
550+
tableStr = tableStr[idx+1:]
551+
}
552+
}
543553
b.WriteString(tableStr)
544554
b.WriteString("\x1b[K\n")
545555

0 commit comments

Comments
 (0)