feat: lazysecrets open --action ends in the secrets list, like Go to

This commit is contained in:
keb committed 2026-10-14 21:24:01 +02:00
1 parent a4002a785c
commit bdf949339b
4 files changed
+48 -16

No files matched your search

+4 -2
View File
@@ -121,7 +121,8 @@ account) and jumps to it, asking which account to use when several can see
it, ending on a secret's details as `enter` on it would. Links say what, it, ending on a secret's details as `enter` on it would. Links say what,
not what to do: opening a link to a secret asks whether to just go to it, not what to do: opening a link to a secret asks whether to just go to it,
also copy or show its value, or (`y`) copy its value without going there. also copy or show its value, or (`y`) copy its value without going there.
`lazysecrets open --action go|copy|show <link>` does one without asking, `lazysecrets open --action go|copy|show <link>` does one without asking
and ends in the list, like Go to,
and `--account <account>` picks the account (as [`list`](#listing-for-fzf-and-pickers) and `--account <account>` picks the account (as [`list`](#listing-for-fzf-and-pickers)
prints it) when several can see it. prints it) when several can see it.
@@ -175,7 +176,8 @@ when several of yours can see it: without, `open` asks which, and `get`
tries the one that last read it first. The link stays as it is, to copy tries the one that last read it first. The link stays as it is, to copy
and share; the account isn't part of it. `--action` tells `open` what to and share; the account isn't part of it. `--action` tells `open` what to
do rather than asking, as it does for a link clicked elsewhere: `go` to do rather than asking, as it does for a link clicked elsewhere: `go` to
it, `copy` its value too, or `show` it. it, `copy` its value too, or `show` it. It ends in the secrets list, like
Go to, rather than on the details as a clicked link does.
What's [cached](config.md#cache) prints at once. Then, like Go to, it loads What's [cached](config.md#cache) prints at once. Then, like Go to, it loads
what's missing or older than `--max-age` (2m), and what's new in it follows what's missing or older than `--max-age` (2m), and what's new in it follows
+3 -2
View File
@@ -134,7 +134,8 @@ type Options struct {
// Open is a reference to open at startup (lazysecrets open <ref>). // Open is a reference to open at startup (lazysecrets open <ref>).
Open *ref.Ref Open *ref.Ref
// OpenAction, if set, is what to do with Open, rather than asking: // OpenAction, if set, is what to do with Open, rather than asking:
// go to it, and maybe copy or show its value (OpenActions). // go to it, and maybe copy or show its value (OpenActions). It ends in
// the list, like Go to, rather than on the details.
OpenAction string OpenAction string
// Styles, if set, replaces the default theme (styles.FromTheme), and // Styles, if set, replaces the default theme (styles.FromTheme), and
// DefaultFg the terminal's default text colour. // DefaultFg the terminal's default text colour.
@@ -602,7 +603,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case openRefMsg: case openRefMsg:
m.deferHandlerSetup() // the link first m.deferHandlerSetup() // the link first
if act, ok := OpenActions[msg.action]; ok && !msg.ref.IsShare() { if act, ok := OpenActions[msg.action]; ok && !msg.ref.IsShare() {
cmds = append(cmds, m.openRef(msg.ref, act)) cmds = append(cmds, m.findRef(msg.ref, act))
} else { } else {
cmds = append(cmds, m.offerRef("Open link", msg.ref, false)) cmds = append(cmds, m.offerRef("Open link", msg.ref, false))
} }
+37 -8
View File
@@ -56,9 +56,12 @@ type resolvedMsg struct {
act linkAction act linkAction
sourceRow string sourceRow string
others []string // other accounts' rows that also found the vault others []string // other accounts' rows that also found the vault
choices []choice // several candidates: ask // find ends in the list, like Go to, not on the details: opened by a
linked *provider.Container // tool that found it (lazysecrets open --action, from fzf).
err error find bool
choices []choice // several candidates: ask
linked *provider.Container
err error
} }
type choice struct { type choice struct {
@@ -407,22 +410,22 @@ func (m *Model) handleResolved(msg resolvedMsg) {
for i := range msg.choices { for i := range msg.choices {
msg.choices[i].label = "Open in " + msg.choices[i].label msg.choices[i].label = "Open in " + msg.choices[i].label
} }
m.chooseSource("Scope "+msg.ref.Scope+" is in several workspaces", msg.ref, msg.act, msg.choices, true) m.chooseSource("Scope "+msg.ref.Scope+" is in several workspaces", msg.ref, msg.act, msg.choices, !msg.find)
case len(msg.others) > 0: case len(msg.others) > 0:
var choices []choice var choices []choice
for _, row := range append([]string{msg.sourceRow}, msg.others...) { for _, row := range append([]string{msg.sourceRow}, msg.others...) {
choices = append(choices, choice{label: "Open with " + m.sourceLabel(row), sourceRow: row}) choices = append(choices, choice{label: "Open with " + m.sourceLabel(row), sourceRow: row})
} }
if row, ok := preferredRow(msg.ref, choices); ok { if row, ok := preferredRow(msg.ref, choices); ok {
m.startLinkNav(msg.ref, msg.act, row) m.navResolved(msg, row)
return return
} }
m.chooseSource("Several accounts can see "+msg.ref.ContainerName(), msg.ref, msg.act, choices, true) m.chooseSource("Several accounts can see "+msg.ref.ContainerName(), msg.ref, msg.act, choices, !msg.find)
case msg.linked != nil: case msg.linked != nil:
m.addLinked(*msg.linked) m.addLinked(*msg.linked)
m.startLinkNav(msg.ref, msg.act, linksRowID) m.navResolved(msg, linksRowID)
default: default:
m.startLinkNav(msg.ref, msg.act, msg.sourceRow) m.navResolved(msg, msg.sourceRow)
} }
} }
@@ -437,6 +440,32 @@ func preferredRow(r ref.Ref, choices []choice) (string, bool) {
return "", false return "", false
} }
// navResolved goes to what a resolved link points at, under row.
func (m *Model) navResolved(msg resolvedMsg, row string) {
if msg.find {
m.startNav(msg.ref, msg.act, row)
return
}
m.startLinkNav(msg.ref, msg.act, row)
}
// findRef is openRef ending in the list, like Go to: for a link a tool
// found (lazysecrets open --action).
func (m *Model) findRef(r ref.Ref, act linkAction) tea.Cmd {
cmd := m.openRef(r, act)
if cmd == nil {
return nil
}
return func() tea.Msg {
msg := cmd()
if rm, ok := msg.(resolvedMsg); ok {
rm.find = true
return rm
}
return msg
}
}
// startLinkNav opens a link: like startNav, ending on its object's // startLinkNav opens a link: like startNav, ending on its object's
// details. // details.
func (m *Model) startLinkNav(r ref.Ref, act linkAction, sourceRow string) { func (m *Model) startLinkNav(r ref.Ref, act linkAction, sourceRow string) {
+4 -4
View File
@@ -203,8 +203,8 @@ func TestOpenPromptAndStartupOpen(t *testing.T) {
t.Fatal("open at startup didn't ask what to do") t.Fatal("open at startup didn't ask what to do")
} }
d2.keys("o") d2.keys("o")
if _, _, s := d2.selected(); s != "api-key" { if _, _, s := d2.selected(); s != "api-key" || d2.m.focus != paneDetail {
t.Errorf("open at startup: selected %q", s) t.Errorf("open at startup: selected %q, focus %v, want the details", s, d2.m.focus)
} }
// --action: no question, it does that. // --action: no question, it does that.
@@ -212,8 +212,8 @@ func TestOpenPromptAndStartupOpen(t *testing.T) {
if d3.m.prompt != nil { if d3.m.prompt != nil {
t.Fatalf("--action go asked: %q", d3.m.prompt.title) t.Fatalf("--action go asked: %q", d3.m.prompt.title)
} }
if _, _, s := d3.selected(); s != "api-key" { if _, _, s := d3.selected(); s != "api-key" || d3.m.focus != paneSecrets {
t.Errorf("--action go: selected %q", s) t.Errorf("--action go: selected %q, focus %v, want the secrets list, like Go to", s, d3.m.focus)
} }
d4 := newDriverOpts(t, Options{Open: &r, OpenAction: "copy"}) d4 := newDriverOpts(t, Options{Open: &r, OpenAction: "copy"})
if _, _, s := d4.selected(); s != "api-key" || d4.clip().content == "" { if _, _, s := d4.selected(); s != "api-key" || d4.clip().content == "" {