feat: the key picker says what sharing does, and skips plain env values
CI / Checks (pull_request) Successful in 37s
PR title / Conventional title (pull_request) Successful in 1s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kebandClaude Opus 5.5 committed 2026-10-13 12:13:16 +02:00
1 parent 68fc76d938
commit b46991c7c2
5 files changed
+34 -10

No files matched your search

+1
View File
@@ -40,6 +40,7 @@ type option struct {
group string // the source row it's in (search)
kind provider.Kind // the kind of provider it's from (search)
hits []int // characters of the label the filter matched (Go to)
plain bool // a key in a plain env file: no key to encrypt with (Go to)
run func(m *Model) tea.Cmd
// header makes this a section title: not selectable, and hidden
// while a filter narrows the list.
+4 -2
View File
@@ -61,13 +61,14 @@ func (m *Model) searchEntries() (sources, items []option) {
var es []entry
// key is the store key, so ctrl+f stars the entry; mark shows how
// much is loaded, reload (ctrl+r) loads it again.
add := func(depth int, row string, kind provider.Kind, path []string, account, key, mark string, run, reload func(*Model) tea.Cmd) {
add := func(depth int, row string, kind provider.Kind, path []string, account, key, mark string, run, reload func(*Model) tea.Cmd) *option {
n := m.store.Node(key)
sort := strings.ToLower(strings.Join(path, "/")) + " " + account
es = append(es, entry{depth, sort,
option{label: m.pathLabel(kind, path, account), note: n.Note, star: key,
starAccount: providerOf(row), starred: n.StarredFor(providerOf(row)),
mark: mark, run: run, reload: reload, id: sort, group: row, kind: kind}})
return &es[len(es)-1].opt
}
for _, p := range m.providers {
for _, src := range m.sourceResults[p.ID()].sources {
@@ -98,10 +99,11 @@ func (m *Model) searchEntries() (sources, items []option) {
typ := strings.TrimSuffix(strings.ToLower(pluralLabels[t]), "s") // "key"
suffix = strings.Trim(typ+" · "+suffix, " ·")
}
add(2, row, c.Source.Kind, []string{src.Name, c.Name, o.Name}, suffix, objectKey(o), " ", func(m *Model) tea.Cmd {
e := add(2, row, c.Source.Kind, []string{src.Name, c.Name, o.Name}, suffix, objectKey(o), " ", func(m *Model) tea.Cmd {
m.startNav(r, actGo, row)
return nil
}, reload)
e.plain = c.Detail == provider.DetailPlainEnv
}
}
}
+12 -7
View File
@@ -57,7 +57,8 @@ type searchView struct {
// secrets only, and enter gives the selected one's link to pick
// instead of going there.
pick func(m *Model, r ref.Ref) tea.Cmd
pickTitle, pickNote string
pickTitle string
pickLines []string // what picking does, above the input
// loaded are the scopes a search has loaded since the popup opened:
// typing loads the scope once, so lists that fail aren't asked for
@@ -749,7 +750,14 @@ func (m Model) searchViewBox() string {
lists = panes.BoxCounted(m.st, title(resultsTitle, true), "", counter(v.res, len(v.shown)), right, resW, listH, focused)
}
body := []string{" " + v.input.View(), ""}
var body []string
if v.pick != nil {
for _, l := range v.pickLines {
body = append(body, " "+l)
}
body = append(body, "")
}
body = append(body, " "+v.input.View(), "")
for _, l := range strings.Split(lists, "\n") {
body = append(body, " "+l)
}
@@ -761,9 +769,6 @@ func (m Model) searchViewBox() string {
} else {
body = append(body, "") // the same height on every tab
}
if v.pick != nil && v.pickNote != "" {
body = append(body, " "+m.st.Muted.Render(v.pickNote))
}
km := m.keys
var foot string
switch {
@@ -808,10 +813,10 @@ func (v *searchView) selectLabel() string {
}
// pickable is the link of a Go to entry that a picker can pick: a
// secret (not a key or certificate).
// secret (not a key or certificate, nor a key in a plain env file).
func pickable(o option) (ref.Ref, bool) {
r, err := ref.Parse(o.star)
if err != nil || r.IsShare() || r.IsSource() || r.IsContainer() || (r.Type != "" && r.Type != provider.TypeSecret) {
if err != nil || o.plain || r.IsShare() || r.IsSource() || r.IsContainer() || (r.Type != "" && r.Type != provider.TypeSecret) {
return ref.Ref{}, false
}
return r, true
+9 -1
View File
@@ -131,7 +131,15 @@ func (m *Model) pickShareKey(s sharing) tea.Cmd {
return nil
}
m.search.pickTitle = "Encrypt with which secret?"
m.search.pickNote = "Whoever can read it can open the link. share.key sets a default."
what := s.files[0].Path
if len(s.files) > 1 {
what = plural(len(s.files), "env file")
}
m.search.pickLines = []string{
"Sharing " + what + " from " + s.repo.Name + " as an encrypted link.",
"Pick the secret to encrypt with: whoever can read it can open the link.",
m.st.Muted.Render("Its value isn't shown or put in the link. share.key sets a default."),
}
m.fillSearch(false)
return tea.Batch(cmd, m.searchLoads())
}
+8
View File
@@ -47,6 +47,14 @@ func shareFrom(t *testing.T, root string, paste bool) string {
if v == nil || v.pick == nil {
t.Fatalf("no secret picker: %s", d.m.flash.text)
}
for _, o := range v.shown {
if o.plain || strings.Contains(o.label, ".env") {
t.Errorf("a plain env value offered as the key: %s", o.label)
}
}
if !strings.Contains(d.screen(), "as an encrypted link") {
t.Errorf("doesn't say what picking does:\n%s", d.screen())
}
if paste {
// A reference pasted is picked as it is.
d.send(tea.PasteMsg{Content: "https://" + kv + "/secrets/db-password"})