feat: the logo on top of the status pane, as text further down #18
No files matched your search
+2
-1
@@ -113,7 +113,8 @@ Secrets. Going into it (`l`, or `4`) keeps it on that; there `j`/`k`,
|
|||||||
it's hidden.
|
it's hidden.
|
||||||
|
|
||||||
On top, the status pane (`0`) shows the version, a newer release and how
|
On top, the status pane (`0`) shows the version, a newer release and how
|
||||||
your accounts are doing. Focused, the right pane lists more: the version
|
your accounts are doing. Focused, the right pane lists more under the
|
||||||
|
logo (when there's room): the version
|
||||||
and updates (`U`), each account (`L` on one that needs it signs in), the
|
and updates (`U`), each account (`L` on one that needs it signs in), the
|
||||||
config, state and cache files (`e` opens one in your editor), and links
|
config, state and cache files (`e` opens one in your editor), and links
|
||||||
to the docs, source, issues, releases and changelog (`o` opens one; on
|
to the docs, source, issues, releases and changelog (`o` opens one; on
|
||||||
|
|||||||
@@ -1190,7 +1190,7 @@ func TestStatusPane(t *testing.T) {
|
|||||||
t.Fatalf("focus %v", d.m.focus)
|
t.Fatalf("focus %v", d.m.focus)
|
||||||
}
|
}
|
||||||
scr := d.screen()
|
scr := d.screen()
|
||||||
for _, want := range []string{"version v0.1.0", "GPL-3.0-or-later", "Azure · 2 subscriptions", "Azure · admin · sign-in required",
|
for _, want := range []string{logo[2], "version v0.1.0", "GPL-3.0-or-later", "Azure · 2 subscriptions", "Azure · admin · sign-in required",
|
||||||
"/cfg/config.yaml", "/state/state.json", "lazysecrets/…/docs"} {
|
"/cfg/config.yaml", "/state/state.json", "lazysecrets/…/docs"} {
|
||||||
if !strings.Contains(scr, want) {
|
if !strings.Contains(scr, want) {
|
||||||
t.Errorf("missing %q:\n%s", want, scr)
|
t.Errorf("missing %q:\n%s", want, scr)
|
||||||
@@ -1216,6 +1216,11 @@ func TestStatusPane(t *testing.T) {
|
|||||||
if len(opened) != 1 || !strings.HasSuffix(opened[0], "/src/branch/main/docs") {
|
if len(opened) != 1 || !strings.HasSuffix(opened[0], "/src/branch/main/docs") {
|
||||||
t.Errorf("o opened %q", opened)
|
t.Errorf("o opened %q", opened)
|
||||||
}
|
}
|
||||||
|
d.m.fields.SelectWhere(func(f panes.Field) bool { return f.Label == "version" })
|
||||||
|
d.keys("o")
|
||||||
|
if len(opened) != 2 || !strings.HasSuffix(opened[1], "/releases/tag/v0.1.0") {
|
||||||
|
t.Errorf("o on the version opened %q", opened)
|
||||||
|
}
|
||||||
d.keys("shift+tab")
|
d.keys("shift+tab")
|
||||||
if d.m.focus != paneStatus {
|
if d.m.focus != paneStatus {
|
||||||
t.Errorf("back to %v", d.m.focus)
|
t.Errorf("back to %v", d.m.focus)
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ func (m *Model) syncFields() tea.Cmd {
|
|||||||
m.fields.Hide()
|
m.fields.Hide()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
m.fields.Header, m.fields.HeaderShort = nil, ""
|
||||||
switch m.detailOf() {
|
switch m.detailOf() {
|
||||||
case paneSources:
|
case paneSources:
|
||||||
r, ok := m.sources.Selected()
|
r, ok := m.sources.Selected()
|
||||||
@@ -68,6 +69,7 @@ func (m *Model) syncFields() tea.Cmd {
|
|||||||
m.fields.Show("", nil)
|
m.fields.Show("", nil)
|
||||||
}
|
}
|
||||||
case paneStatus:
|
case paneStatus:
|
||||||
|
m.fields.Header, m.fields.HeaderShort = logo, strings.TrimSpace("lazysecrets "+m.versionLabel())
|
||||||
m.fields.Show("lazysecrets", m.statusFields())
|
m.fields.Show("lazysecrets", m.statusFields())
|
||||||
case paneContainers:
|
case paneContainers:
|
||||||
if c, ok := m.containers.Selected(); ok {
|
if c, ok := m.containers.Selected(); ok {
|
||||||
|
|||||||
@@ -16,6 +16,16 @@ import (
|
|||||||
// (0), the right pane has the rest: updates, each account, the files
|
// (0), the right pane has the rest: updates, each account, the files
|
||||||
// lazysecrets uses, links, and the changelog.
|
// lazysecrets uses, links, and the changelog.
|
||||||
|
|
||||||
|
// logo heads the status pane's rows.
|
||||||
|
var logo = []string{
|
||||||
|
` _ _`,
|
||||||
|
`| | __ _ _____ _ ___ ___ ___ _ __ ___| |_ ___`,
|
||||||
|
`| |/ _` + "`" + ` |_ / | | / __|/ _ \/ __| '__/ _ \ __/ __|`,
|
||||||
|
`| | (_| |/ /| |_| \__ \ __/ (__| | | __/ |_\__ \`,
|
||||||
|
`|_|\__,_/___|\__, |___/\___|\___|_| \___|\__|___/`,
|
||||||
|
` |___/`,
|
||||||
|
}
|
||||||
|
|
||||||
// statusH is the status pane's height: one line in a box.
|
// statusH is the status pane's height: one line in a box.
|
||||||
const statusH = 3
|
const statusH = 3
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ type List[T any] struct {
|
|||||||
// Age says how old the items are ("2h ago") while they reload, by
|
// Age says how old the items are ("2h ago") while they reload, by
|
||||||
// the counter.
|
// the counter.
|
||||||
Age string
|
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]
|
item Item[T]
|
||||||
items []T
|
items []T
|
||||||
@@ -163,9 +168,36 @@ func (l List[T]) rows() int {
|
|||||||
if l.filtering {
|
if l.filtering {
|
||||||
r--
|
r--
|
||||||
}
|
}
|
||||||
|
r -= len(l.header())
|
||||||
return max(0, r)
|
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.
|
// Filtering reports whether the filter input has focus.
|
||||||
func (l List[T]) Filtering() bool { return l.filtering }
|
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 {
|
if l.filtering {
|
||||||
body = append(body, l.input.View())
|
body = append(body, l.input.View())
|
||||||
}
|
}
|
||||||
|
for _, line := range l.header() {
|
||||||
|
body = append(body, st.Notice.Render(" "+line))
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case l.Loading && len(l.items) == 0:
|
case l.Loading && len(l.items) == 0:
|
||||||
|
|||||||
@@ -177,3 +177,27 @@ func TestHighlightKeepsColours(t *testing.T) {
|
|||||||
t.Error("reverse kept the colours")
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user