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
This commit was merged in pull request #9.
This commit is contained in:
keb committed 2026-10-11 10:59:40 +02:00
1 parent c8abc1dc95
commit 1c8d69d957
9 files changed
+232 -67

No files matched your search

-11
View File
@@ -46,7 +46,6 @@ type Detail struct {
versions []provider.Object // newest first; nil while unknown
note string // the user's note on what's shown
message string // shown instead of the hint when nothing is selected: an error
offset int
w, h int
@@ -72,10 +71,6 @@ func (d *Detail) Clear() {
*d = Detail{Now: d.Now, Icons: d.Icons, RevealKey: d.RevealKey, VersionsKey: d.VersionsKey, CopyKey: d.CopyKey, w: d.w, h: d.h} // drops the note too
}
// SetMessage sets what's shown instead of the hint when nothing is
// selected, such as why an account has nothing to show; "" for the hint.
func (d *Detail) SetMessage(s string) { d.message = s }
// SetDescribing marks extra metadata as being fetched.
func (d *Detail) SetDescribing() { d.describing, d.describeErr = true, nil }
@@ -191,12 +186,6 @@ func (d Detail) lines(st styles.Styles) []string {
muted := st.Muted.Render
if d.container == nil {
if d.message != "" {
for _, l := range strings.Split(ansi.Hardwrap(d.message, max(1, inner-2), true), "\n") {
out = append(out, " "+st.Error.Render(l))
}
return out
}
return []string{muted(" Select a vault or scope to browse its secrets.")}
}
c := *d.container