Files
lazysecrets/internal/ui/panes/fields.go
T
keb 1c8d69d957 feat: the right pane describes what's selected in the pane you're in (#9)
The right pane kept showing the selected secret when you moved to Vaults or Sources. Now it follows focus:

| focus | right pane |
|---|---|
| Sources | the account (group row), or the subscription / workspace / repo: name, note, ID / profile / path, host or remote, account, web page, vault count |
| Vaults | the vault / scope / file: name, note, URI / backend / format, source, web page, counts of loaded secrets, keys, certificates |
| Secrets | the secret, as before |
| the right pane itself | what it described when you went in; `shift+tab` goes back there |

All but the secret use the `Fields` pane (#7), so `j`/`k`, `/` and `y` work on them; `Detail` loses the error message it got in #4, which is now a red "problem" row in `Fields`. A value revealed from a left pane stays in view until hidden, so `space` from any pane still shows it. `tab` from a subscription goes to Vaults as before; only an account row skips to its details.

This also changes the startup view: focus starts in Sources, so the right pane describes the first subscription instead of the first secret (golden output updated). Tests that read the secret pane focus Secrets first; a new test walks secret → vault → subscription → into the pane → back, and reveals from Sources.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Karl-Einar Bergstrøm <karl-einarb@outlook.com>
Reviewed-on: #9
2026-10-11 10:59:40 +02:00

74 lines
2.0 KiB
Go

package panes
import (
"fmt"
"github.com/charmbracelet/x/ansi"
"git.kebcloud.no/keb/lazysecrets/internal/ui/styles"
)
// Fields is the fourth pane for anything but a secret (an account, and
// what describes vaults, scopes and files): labelled values, one per row,
// a label on the first of several values, to move through, filter and
// copy from. It's a List, so / filters it like the other panes. Secrets
// have Detail, for revealing and versions.
type Fields struct {
List[Field]
shown bool
}
// Field is a row: a value under a label. Info rows (loading, errors)
// can't be copied; Err ones are errors.
type Field struct {
Label, Value string
Info, Err bool
}
func NewFields() Fields {
l := NewList("", Item[Field]{
ID: func(f Field) string { return f.Label + "\x00" + f.Value },
Filter: func(f Field) string { return f.Value },
// "/tenant" finds the tenant's rows.
FilterExtra: func(f Field) string { return f.Label },
Render: func(st styles.Styles, f Field, width int) string {
value := f.Value
switch {
case f.Err:
value = st.Error.Render(value)
case f.Info:
value = st.Muted.Render(value)
}
return ansi.Truncate(st.Label.Render(fmt.Sprintf("%-10s", f.Label))+" "+value, width, "…")
},
})
l.Empty = "nothing to show"
return Fields{List: l}
}
// Show shows fields under title, instead of the secret pane. Another
// title (another account or vault) starts over, without a filter.
func (s *Fields) Show(title string, fields []Field) {
if title != s.Title {
s.Reset()
s.Title = title
}
s.shown = true
s.SetItems(fields)
}
// Hide gives the fourth pane back to the secret pane.
func (s *Fields) Hide() { s.shown = false }
// Shown reports whether the fourth pane shows Fields.
func (s Fields) Shown() bool { return s.shown }
// Selected is the field under the cursor, unless it's an info row.
func (s Fields) Selected() (Field, bool) {
f, ok := s.List.Selected()
if !ok || f.Info {
return Field{}, false
}
return f, true
}