Like Immich's and RomM's release notes: every changelog entry now links to its pull request and its commit, in `CHANGELOG.md` and in the release notes on Gitea: ``` - Search with fzf's scoring and query syntax ([#11](…/pulls/11)) ([65dc2d7](…/commit/65dc2d7…)) ``` - `cliff.toml`: the entry template adds the short commit hash as a link; a postprocessor turns the squash commit's `(#N)` into a link to the PR. - The app shows release notes as text (`U`, and the status pane's changelog to come), so `Release.Notes()` turns Markdown links back into their text: `Search (#11) (65dc2d7)`. Checked with `git cliff --unreleased` on main: every entry since v0.1.0 gets both links. Test for the link stripping. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Karl-Einar Bergstrøm <karl-einarb@outlook.com> Reviewed-on: #11
77 lines
2.1 KiB
Go
77 lines
2.1 KiB
Go
package panes
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/charmbracelet/x/ansi"
|
|
|
|
"git.kebcloud.no/keb/lazysecrets/internal/ui/styles"
|
|
)
|
|
|
|
// Fields is the fourth pane for anything but a secret (an account, and
|
|
// what describes vaults, scopes and files): labelled values, one per row,
|
|
// a label on the first of several values, to move through, filter and
|
|
// copy from. It's a List, so / filters it like the other panes. Secrets
|
|
// have Detail, for revealing and versions.
|
|
type Fields struct {
|
|
List[Field]
|
|
shown bool
|
|
}
|
|
|
|
// Field is a row: a value under a label. Info rows (loading, errors)
|
|
// can't be copied; Err ones are errors. A row can also be opened: URL in
|
|
// the browser (o), Path in the editor (e), or Provider signed in to (L).
|
|
type Field struct {
|
|
Label, Value string
|
|
Info, Err bool
|
|
|
|
URL, Path, Provider string
|
|
}
|
|
|
|
func NewFields() Fields {
|
|
l := NewList("", Item[Field]{
|
|
ID: func(f Field) string { return f.Label + "\x00" + f.Value },
|
|
Filter: func(f Field) string { return f.Value },
|
|
// "/tenant" finds the tenant's rows.
|
|
FilterExtra: func(f Field) string { return f.Label },
|
|
Render: func(st styles.Styles, f Field, width int) string {
|
|
value := f.Value
|
|
switch {
|
|
case f.Err:
|
|
value = st.Error.Render(value)
|
|
case f.Info:
|
|
value = st.Muted.Render(value)
|
|
}
|
|
return ansi.Truncate(st.Label.Render(fmt.Sprintf("%-10s", f.Label))+" "+value, width, "…")
|
|
},
|
|
})
|
|
l.Empty = "nothing to show"
|
|
return Fields{List: l}
|
|
}
|
|
|
|
// Show shows fields under title, instead of the secret pane. Another
|
|
// title (another account or vault) starts over, without a filter.
|
|
func (s *Fields) Show(title string, fields []Field) {
|
|
if title != s.Title {
|
|
s.Reset()
|
|
s.Title = title
|
|
}
|
|
s.shown = true
|
|
s.SetItems(fields)
|
|
}
|
|
|
|
// Hide gives the fourth pane back to the secret pane.
|
|
func (s *Fields) Hide() { s.shown = false }
|
|
|
|
// Shown reports whether the fourth pane shows Fields.
|
|
func (s Fields) Shown() bool { return s.shown }
|
|
|
|
// Selected is the field under the cursor, unless it's an info row.
|
|
func (s Fields) Selected() (Field, bool) {
|
|
f, ok := s.List.Selected()
|
|
if !ok || f.Info {
|
|
return Field{}, false
|
|
}
|
|
return f, true
|
|
}
|