feat: the logo on top of the status pane, as text further down (#18)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit was merged in pull request #18.
This commit is contained in:
kebandClaude Opus 5.5 committed 2026-10-11 14:29:38 +02:00
1 parent 09bacd8438
commit 17054ad0ec
6 files changed
+79 -2

No files matched your search

+35
View File
@@ -37,6 +37,11 @@ type List[T any] struct {
// Age says how old the items are ("2h ago") while they reload, by
// the counter.
Age 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
@@ -163,9 +168,36 @@ func (l List[T]) rows() int {
if l.filtering {
r--
}
r -= len(l.header())
return max(0, r)
}
// headerMinRows is how many item rows Header leaves at least.
const headerMinRows = 5
// 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 short
}
for _, line := range l.Header {
if ansi.StringWidth(line) > l.w-3 {
return short
}
}
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.
func (l List[T]) Filtering() bool { return l.filtering }
@@ -329,6 +361,9 @@ func (l List[T]) View(st styles.Styles, focused bool, spin string) string {
if l.filtering {
body = append(body, l.input.View())
}
for _, line := range l.header() {
body = append(body, st.Notice.Render(" "+line))
}
switch {
case l.Loading && len(l.items) == 0:
+24
View File
@@ -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)
}
}