feat: an account pane on Azure account rows: fields and groups to move through and copy
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f6c4f5f6f6
commit
b37caa5a25
13 files changed
+396
-27
No files matched your search
@@ -0,0 +1,88 @@
|
||||
package panes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/ui/styles"
|
||||
)
|
||||
|
||||
// SourceDetail is the fourth pane on a provider's row: what's known about
|
||||
// its account, one field per row, to move through and copy from.
|
||||
type SourceDetail struct {
|
||||
title string
|
||||
fields []Field
|
||||
sel int
|
||||
w, h int
|
||||
}
|
||||
|
||||
// Field is a row: a value under a label. Info rows (loading, errors)
|
||||
// can't be copied.
|
||||
type Field struct {
|
||||
Label, Value string
|
||||
Info bool
|
||||
}
|
||||
|
||||
// Set shows fields under title, keeping the cursor where it was for the
|
||||
// same title.
|
||||
func (s *SourceDetail) Set(title string, fields []Field) {
|
||||
if title != s.title {
|
||||
s.sel = 0
|
||||
}
|
||||
s.title, s.fields = title, fields
|
||||
s.sel = max(0, min(s.sel, len(fields)-1))
|
||||
}
|
||||
|
||||
// Shown reports whether there's anything to show.
|
||||
func (s SourceDetail) Shown() bool { return len(s.fields) > 0 }
|
||||
|
||||
func (s *SourceDetail) SetSize(w, h int) { s.w, s.h = w, h }
|
||||
|
||||
func (s *SourceDetail) Move(delta int) { s.sel = max(0, min(s.sel+delta, len(s.fields)-1)) }
|
||||
func (s *SourceDetail) Top() { s.sel = 0 }
|
||||
func (s *SourceDetail) Bottom() { s.sel = max(0, len(s.fields)-1) }
|
||||
|
||||
// Selected is the field under the cursor, unless it's an info row.
|
||||
func (s SourceDetail) Selected() (Field, bool) {
|
||||
if s.sel >= len(s.fields) || s.fields[s.sel].Info {
|
||||
return Field{}, false
|
||||
}
|
||||
return s.fields[s.sel], true
|
||||
}
|
||||
|
||||
func (s SourceDetail) View(st styles.Styles, focused bool) string {
|
||||
inner := max(1, s.w-2)
|
||||
rows := max(0, s.h-2)
|
||||
offset := 0
|
||||
if s.sel >= rows { // keep the cursor in view
|
||||
offset = s.sel - rows + 1
|
||||
}
|
||||
var body []string
|
||||
for i, f := range s.fields[min(offset, len(s.fields)):] {
|
||||
if i >= rows {
|
||||
break
|
||||
}
|
||||
value := f.Value
|
||||
if f.Info {
|
||||
value = st.Muted.Render(value)
|
||||
}
|
||||
line := " " + st.Label.Render(fmt.Sprintf("%-10s", f.Label)) + " " + value
|
||||
line = ansi.Truncate(line, inner, "…")
|
||||
if i+offset == s.sel {
|
||||
pad := strings.Repeat(" ", max(0, inner-ansi.StringWidth(line)))
|
||||
if focused {
|
||||
line = st.Selected.Render(ansi.Strip(line) + pad)
|
||||
} else {
|
||||
line = st.SelectedBlurred.Render(ansi.Strip(line) + pad)
|
||||
}
|
||||
}
|
||||
body = append(body, line)
|
||||
}
|
||||
footer := ""
|
||||
if len(s.fields) > rows {
|
||||
footer = fmt.Sprintf("%d of %d", s.sel+1, len(s.fields))
|
||||
}
|
||||
return Box(st, TitleStyle(st, focused).Render(s.title), footer, body, s.w, s.h, focused)
|
||||
}
|
||||
Reference in new issue
Block a user