Hello world 👋
Release / Build and publish (push) Successful in 24s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kebandClaude Opus 5.5 committed 2026-10-12 10:29:42 +02:00
commit 46d6d37dc2
130 files changed
+26739

No files matched your search

+198
View File
@@ -0,0 +1,198 @@
package app
import (
"cmp"
"maps"
"slices"
"strings"
tea "charm.land/bubbletea/v2"
"git.kebcloud.no/keb/lazysecrets/internal/provider"
"git.kebcloud.no/keb/lazysecrets/internal/ref"
"git.kebcloud.no/keb/lazysecrets/internal/store"
)
// The Favourites and Recent scopes of the Go to popup list what's
// remembered in the store, so they work before anything has loaded.
// storeEntries lists starred items (sources first, then vaults, then
// secrets) and recently used ones (newest first), as Go to entries.
// Entries read as paths: "󰠅 platform-prod / kv-app-prod / api-key · dev",
// the account last, and only when there are several.
func (m *Model) storeEntries() (favs, recent []option) {
type entry struct {
depth int // 0 source, 1 vault, 2 secret: sources list first
sort string
opt option
}
nodes := m.store.Nodes()
keys := slices.Sorted(maps.Keys(nodes))
// entries lists jumps to the item through each of accounts; nil
// means through whichever accounts list a source, else the account
// that last read the item.
entries := func(prefix, key string, n store.Node, accounts []string) []entry {
r, err := ref.Parse(key)
if err != nil {
return nil
}
var out []entry
add := func(path []string, acct string, depth int, run func(*Model) tea.Cmd) {
account := ""
if p, ok := m.byID[acct]; ok {
account = accountOf(provider.Source{Kind: p.Kind(), ProviderLabel: p.Label()})
}
sort := strings.ToLower(strings.Join(path, "/")) + " " + account
out = append(out, entry{depth, sort, option{
label: m.pathLabel(r.Kind, path, account), note: n.Note, mark: " ",
id: prefix + key + "|" + acct, star: key, starAccount: acct, starred: n.StarredFor(acct),
run: func(m *Model) tea.Cmd {
m.used(key)
return run(m)
}}})
}
if r.IsSource() {
for _, p := range m.providers {
if p.Kind() != r.Kind || (accounts != nil && !slices.Contains(accounts, p.ID())) {
continue
}
for _, src := range m.sourceResults[p.ID()].sources {
if !sourceMatches(r, src) {
continue
}
row := sourceRowKey(p.ID(), src.ID)
add([]string{src.Name}, p.ID(), 0, func(m *Model) tea.Cmd {
m.nav = &nav{sourceRow: row, sourceOnly: true}
return nil
})
}
}
return out
}
if accounts == nil {
accounts = []string{n.Account}
}
depth := 1
if !r.IsContainer() {
depth = 2
}
path := slices.Clone(n.Path)
if len(path) == 0 && !r.IsContainer() {
path = []string{r.ContainerName()} // no context recorded
}
for _, acct := range accounts {
via := n
via.Account = acct
add(append(slices.Clone(path), cmp.Or(n.Name, r.Name, r.ContainerName())), acct, depth,
func(m *Model) tea.Cmd { return m.jumpTo(r, via) })
}
return out
}
var starred []entry
for _, k := range keys {
if n := nodes[k]; len(n.StarredBy) > 0 {
starred = append(starred, entries("fav|", k, n, n.StarredBy)...)
}
}
slices.SortFunc(starred, func(a, b entry) int {
return cmp.Or(a.depth-b.depth, strings.Compare(a.sort, b.sort))
})
for _, e := range starred {
favs = append(favs, e.opt)
}
if m.history {
var used []string
for _, k := range keys {
if !nodes[k].LastUsed.IsZero() {
used = append(used, k)
}
}
slices.SortStableFunc(used, func(a, b string) int { return nodes[b].LastUsed.Compare(nodes[a].LastUsed) })
for _, k := range used[:min(len(used), recentMax)] {
for _, e := range entries("recent|", k, nodes[k], nil) {
recent = append(recent, e.opt)
}
}
}
return favs, recent
}
// recentMax is how many recently used items Recent lists.
const recentMax = 20
// pathLabel draws a jump list entry: the provider's icon and the path in
// its colour, then the account.
func (m Model) pathLabel(kind provider.Kind, path []string, account string) string {
style := m.st.Kind(kind)
parts := make([]string, len(path))
for i, p := range path {
parts[i] = style.Render(p)
}
label := style.Render(m.icons.Prefix(kind)) + strings.Join(parts, m.st.Muted.Render(" / "))
if account != "" {
label += m.st.Muted.Render(" · " + account)
}
return label
}
// sourceMatches reports whether src is the subscription or workspace r
// links to.
func sourceMatches(r ref.Ref, src provider.Source) bool {
if r.Kind != provider.KindAzure { // by host: workspaces, repos
return strings.EqualFold(strings.TrimSuffix(src.Detail, "/"), r.Host)
}
return src.ID == r.Subscription
}
// accountOf is the account part of a source's provider label ("alice" in
// "Azure · alice"), or "" when there's a single account.
func accountOf(src provider.Source) string {
account, ok := strings.CutPrefix(src.ProviderLabel, string(src.Kind)+" · ")
if !ok {
return ""
}
return account
}
// jumpTo goes to a remembered vault or secret. With the account that last
// read it signed in, it goes straight there; otherwise it uses the
// accounts that list its subscription, asking if there are several, or
// finds it like a link. Reaching it updates the remembered account.
func (m *Model) jumpTo(r ref.Ref, n store.Node) tea.Cmd {
if n.Source == "" && !r.IsContainer() {
// A secret without context: its vault's record may have it.
parent := r
parent.Type, parent.Name, parent.Version = "", "", ""
if p := m.store.Node(parent.String()); p.Source != "" {
n.Source, n.Account = p.Source, cmp.Or(n.Account, p.Account)
}
}
if _, ok := m.byID[n.Account]; ok && n.Source != "" {
m.startNav(r, actGo, sourceRowKey(n.Account, n.Source))
return nil
}
var choices []choice
for _, p := range m.providers {
if p.Kind() != r.Kind {
continue
}
for _, src := range m.sourceResults[p.ID()].sources {
if src.ID == n.Source && n.Source != "" {
row := sourceRowKey(p.ID(), src.ID)
choices = append(choices, choice{label: "Open with " + m.sourceLabel(row), sourceRow: row})
}
}
}
switch len(choices) {
case 0:
return m.openRef(r, actGo)
case 1:
m.startNav(r, actGo, choices[0].sourceRow)
default:
m.chooseSource("Several accounts can see "+r.ContainerName(), r, actGo, choices)
}
return nil
}