From 38302ddd2967b556f05531d667294bc62896986d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl-Einar=20Bergstr=C3=B8m?= Date: Sun, 11 Oct 2026 13:33:18 +0200 Subject: [PATCH] feat: the status pane's logo turns to a line of text further down, or without room Co-Authored-By: Claude Opus 5.5 (1M context) --- internal/ui/app/fields.go | 4 ++-- internal/ui/panes/list.go | 42 +++++++++++++++++++--------------- internal/ui/panes/list_test.go | 24 +++++++++++++++++++ 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/internal/ui/app/fields.go b/internal/ui/app/fields.go index 802fffe..8983ffd 100644 --- a/internal/ui/app/fields.go +++ b/internal/ui/app/fields.go @@ -49,7 +49,7 @@ func (m *Model) syncFields() tea.Cmd { m.fields.Hide() return nil } - m.fields.Header = nil + m.fields.Header, m.fields.HeaderShort = nil, "" switch m.detailOf() { case paneSources: r, ok := m.sources.Selected() @@ -66,7 +66,7 @@ func (m *Model) syncFields() tea.Cmd { m.fields.Show("", nil) } case paneStatus: - m.fields.Header = logo + m.fields.Header, m.fields.HeaderShort = logo, strings.TrimSpace("lazysecrets "+m.versionLabel()) m.fields.Show("lazysecrets", m.statusFields()) case paneContainers: if c, ok := m.containers.Selected(); ok { diff --git a/internal/ui/panes/list.go b/internal/ui/panes/list.go index 535cedc..c261e7f 100644 --- a/internal/ui/panes/list.go +++ b/internal/ui/panes/list.go @@ -34,9 +34,11 @@ type List[T any] struct { Empty string // shown when loaded with no items Loading bool Err error - // Header lines (a logo) go above the items, when the pane has room - // for them and there's no filter. - Header []string + // Header lines (a logo) go above the items while the cursor is in + // the rows under it, the pane has room and there's no filter; else + // HeaderShort, a line of plain text, does. + Header []string + HeaderShort string item Item[T] items []T @@ -162,27 +164,34 @@ func (l List[T]) rows() int { if l.filtering { r-- } - if l.headerShown() { - r -= len(l.Header) + 1 - } + r -= len(l.header()) return max(0, r) } -// headerMinRows is how many item rows the header leaves at least. +// headerMinRows is how many item rows Header leaves at least. const headerMinRows = 5 -// headerShown reports whether Header fits: as wide as the pane allows, -// leaving headerMinRows, a blank line under it, and no filter. -func (l List[T]) headerShown() bool { +// header is the lines above the items: Header and a blank line, if it +// fits, leaves headerMinRows and the cursor is in the rows under it; +// else HeaderShort. +func (l List[T]) header() []string { + short := []string{} + if l.HeaderShort != "" { + short = []string{l.HeaderShort} + } if len(l.Header) == 0 || l.filtering || l.input.Value() != "" { - return false + return short } for _, line := range l.Header { if ansi.StringWidth(line) > l.w-3 { - return false + return short } } - return l.h-2-len(l.Header)-1 >= headerMinRows + below := l.h - 2 - len(l.Header) - 1 + if below < headerMinRows || l.cursor >= below { + return short + } + return append(l.Header[:len(l.Header):len(l.Header)], "") } // Filtering reports whether the filter input has focus. @@ -348,11 +357,8 @@ func (l List[T]) View(st styles.Styles, focused bool, spin string) string { if l.filtering { body = append(body, l.input.View()) } - if l.headerShown() { - for _, line := range l.Header { - body = append(body, st.Notice.Render(" "+line)) - } - body = append(body, "") + for _, line := range l.header() { + body = append(body, st.Notice.Render(" "+line)) } switch { diff --git a/internal/ui/panes/list_test.go b/internal/ui/panes/list_test.go index 2b2209d..aa8aafc 100644 --- a/internal/ui/panes/list_test.go +++ b/internal/ui/panes/list_test.go @@ -177,3 +177,27 @@ func TestHighlightKeepsColours(t *testing.T) { t.Error("reverse kept the colours") } } + +func TestListHeader(t *testing.T) { + l := NewList("t", Item[string]{ + ID: func(s string) string { return s }, + Filter: func(s string) string { return s }, + Render: func(_ styles.Styles, s string, _ int) string { return s }, + }) + l.Header, l.HeaderShort = []string{"ART1", "ART2"}, "short" + l.SetItems([]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}) + l.SetSize(30, 2+2+1+6) // the art, a blank line and 6 rows + st := styles.Default() + if v := l.View(st, true, ""); !strings.Contains(v, "ART1") || strings.Contains(v, "short") { + t.Errorf("at the top, the art:\n%s", v) + } + l.Move(6) // below the rows under the art + if v := l.View(st, true, ""); strings.Contains(v, "ART1") || !strings.Contains(v, "short") || !strings.Contains(v, " g") { + t.Errorf("further down, the text:\n%s", v) + } + l.Top() + l.SetSize(30, 8) // too short for the art and 5 rows + if v := l.View(st, true, ""); strings.Contains(v, "ART1") || !strings.Contains(v, "short") { + t.Errorf("no room, the text:\n%s", v) + } +}