feat: l for details; details for vaults, scopes and files; getting started; the age of cached lists #17

Merged
keb merged 17 commits from feat/l-for-details into main 2026-10-11 14:24:09 +02:00
2 changed files with 27 additions and 9 deletions
Showing only changes of commit cd35a8acf4 - Show all commits

No files matched your search

+9 -1
View File
@@ -56,6 +56,7 @@ const (
slotDescribe // extra metadata for the selected object slotDescribe // extra metadata for the selected object
slotCopy // fetch a value straight to the clipboard slotCopy // fetch a value straight to the clipboard
slotVersions // count the selected object's versions slotVersions // count the selected object's versions
slotDetails // a vault's, scope's or file's details
slotCount slotCount
) )
@@ -591,7 +592,14 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.groups[msg.provider] = groupsState{names: msg.names, err: msg.err} m.groups[msg.provider] = groupsState{names: msg.names, err: msg.err}
case detailsMsg: case detailsMsg:
m.details[msg.key] = detailsState{attrs: msg.attrs, err: msg.err} if msg.req == m.reqID[slotDetails] {
m.done(slotDetails)
}
if errors.Is(msg.err, context.Canceled) || errors.Is(msg.err, context.DeadlineExceeded) {
delete(m.details, msg.key) // tried again when shown again
} else {
m.details[msg.key] = detailsState{attrs: msg.attrs, err: msg.err}
}
case crawlMsg: case crawlMsg:
cmds = append(cmds, m.handleCrawl(msg)) cmds = append(cmds, m.handleCrawl(msg))
+18 -8
View File
@@ -30,7 +30,8 @@ type groupsMsg struct {
} }
// A vault's, scope's or file's details beyond its listing load once a // A vault's, scope's or file's details beyond its listing load once a
// session, the first time it's shown, and again with refresh. // session, when the cursor rests on it, and again with refresh. They're
// extra: moving on cancels the load, and search never waits for them.
type detailsState struct { type detailsState struct {
attrs []provider.Attr attrs []provider.Attr
err error err error
@@ -38,6 +39,7 @@ type detailsState struct {
} }
type detailsMsg struct { type detailsMsg struct {
req int
key string key string
attrs []provider.Attr attrs []provider.Attr
err error err error
@@ -211,14 +213,22 @@ func (m *Model) loadDetails(c provider.Container) tea.Cmd {
if _, seen := m.details[key]; !ok || seen { if _, seen := m.details[key]; !ok || seen {
return nil return nil
} }
m.details[key] = detailsState{loading: true} // One at a time: the previous one, if it's still loading, is
timeout := m.timeout // cancelled and forgotten, to load when it's shown again.
return func() tea.Msg { for k, st := range m.details {
ctx, cancel := context.WithTimeout(context.Background(), timeout) if st.loading {
defer cancel() delete(m.details, k)
attrs, err := d.Details(ctx, c) }
return detailsMsg{key: key, attrs: attrs, err: err}
} }
m.details[key] = detailsState{loading: true}
ctx, req := m.begin(slotDetails)
return m.settled(ctx, func() tea.Msg {
attrs, err := d.Details(ctx, c)
if ctx.Err() != nil {
err = ctx.Err()
}
return detailsMsg{req: req, key: key, attrs: attrs, err: err}
})
} }
// problemFields show why an account has nothing to show, wrapped to the // problemFields show why an account has nothing to show, wrapped to the