diff --git a/go.sum b/go.sum index 54cbe32c6164f809f0bd7433dae17ed4a3f3a8d9..dc21343eae38117d515bd2e4bb24ec3a11cb2cd0 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aymanbagabas/go-osc52 v1.0.0 h1:yjRSILSEUSIhuRcyOJ55tbSKtvBLmJDgbPErH8pXcxM= -github.com/aymanbagabas/go-osc52 v1.0.0/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymanbagabas/go-osc52 v1.0.1 h1:juDXgeKhMfVnylcoA4S7p9E4q+9DErUZGkX8t2ZR2j8= github.com/aymanbagabas/go-osc52 v1.0.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= diff --git a/ui/components/selector/selector.go b/ui/components/selector/selector.go index 6444f5ffb87bc6fbbeccffcea41deb9f090a98d6..c615022dc1aef3a789b48a7c8b1cfa8068240d49 100644 --- a/ui/components/selector/selector.go +++ b/ui/components/selector/selector.go @@ -17,7 +17,7 @@ type Selector struct { // IdentifiableItem is an item that can be identified by a string and extends list.Item. type IdentifiableItem interface { - list.Item + list.DefaultItem ID() string } diff --git a/ui/pages/selection/item.go b/ui/pages/selection/item.go index 63eb095aa762c0b9cece2c0845052389320d3833..8736d0dc094085269b70e13b9b57ab33e91cd51a 100644 --- a/ui/pages/selection/item.go +++ b/ui/pages/selection/item.go @@ -16,20 +16,26 @@ import ( // Item represents a single item in the selector. type Item struct { - Title string - Name string - Description string - LastUpdate time.Time - URL *yankable.Yankable + name string + repo string + desc string + lastUpdate time.Time + url *yankable.Yankable } // ID implements selector.IdentifiableItem. func (i Item) ID() string { - return i.Name + return i.repo } +// Title returns the item title. Implements list.DefaultItem. +func (i Item) Title() string { return i.name } + +// Description returns the item description. Implements list.DefaultItem. +func (i Item) Description() string { return i.desc } + // FilterValue implements list.Item. -func (i Item) FilterValue() string { return i.Title } +func (i Item) FilterValue() string { return i.name } // ItemDelegate is the delegate for the item. type ItemDelegate struct { @@ -77,8 +83,8 @@ func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { continue } } - y, cmd := itm.URL.Update(msg) - itm.URL = y.(*yankable.Yankable) + y, cmd := itm.url.Update(msg) + itm.url = y.(*yankable.Yankable) if cmd != nil { cmds = append(cmds, cmd) } @@ -90,29 +96,50 @@ func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { func (d ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { i := listItem.(Item) s := strings.Builder{} - style := d.styles.MenuItem.Copy() - if index == m.Index() { - style = style.BorderForeground(d.styles.ActiveBorderColor) + var matchedRunes []int + + // Conditions + var ( + isSelected = index == m.Index() + // emptyFilter = m.FilterState() == list.Filtering && m.FilterValue() == "" + isFiltered = m.FilterState() == list.Filtering || m.FilterState() == list.FilterApplied + ) + + itemStyle := d.styles.MenuItem.Copy() + if isSelected { + itemStyle = itemStyle.BorderForeground(d.styles.ActiveBorderColor) if d.activeBox != nil && *d.activeBox == readmeBox { // TODO make this into its own color - style = style.BorderForeground(lipgloss.Color("15")) + itemStyle = itemStyle.BorderForeground(lipgloss.Color("15")) } } - titleStr := i.Title - updatedStr := fmt.Sprintf(" Updated %s", humanize.Time(i.LastUpdate)) + + title := i.name + updatedStr := fmt.Sprintf(" Updated %s", humanize.Time(i.lastUpdate)) updated := d.styles.MenuLastUpdate. Copy(). - Width(m.Width() - style.GetHorizontalFrameSize() - lipgloss.Width(titleStr)). + Width(m.Width() - itemStyle.GetHorizontalFrameSize() - lipgloss.Width(title)). Render(updatedStr) - title := lipgloss.NewStyle(). + titleStyle := lipgloss.NewStyle(). Align(lipgloss.Left). - Width(m.Width() - style.GetHorizontalFrameSize() - lipgloss.Width(updated)). - Render(titleStr) + Width(m.Width() - itemStyle.GetHorizontalFrameSize() - lipgloss.Width(updated)) + + if isFiltered && index < len(m.VisibleItems()) { + // Get indices of matched characters + matchedRunes = m.MatchesForItem(index) + } + + if isFiltered { + unmatched := lipgloss.NewStyle().Inline(true) + matched := unmatched.Copy().Underline(true) + title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched) + } + title = titleStyle.Render(title) s.WriteString(lipgloss.JoinHorizontal(lipgloss.Bottom, title, updated)) s.WriteString("\n") - s.WriteString(i.Description) + s.WriteString(i.desc) s.WriteString("\n\n") - s.WriteString(i.URL.View()) - w.Write([]byte(style.Render(s.String()))) + s.WriteString(i.url.View()) + w.Write([]byte(itemStyle.Render(s.String()))) } diff --git a/ui/pages/selection/selection.go b/ui/pages/selection/selection.go index 9b05b21d1064c92769b60152ae43a341217d0d8e..6ff991db39021d3517be519bd52d3f16f9f5f03f 100644 --- a/ui/pages/selection/selection.go +++ b/ui/pages/selection/selection.go @@ -140,29 +140,29 @@ func (s *Selection) Init() tea.Cmd { // Put configured repos first for _, r := range cfg.Repos { items = append(items, Item{ - Title: r.Name, - Name: r.Repo, - Description: r.Note, - LastUpdate: time.Now(), - URL: yank(repoUrl(cfg, r.Repo)), + name: r.Name, + repo: r.Repo, + desc: r.Note, + lastUpdate: time.Now(), // TODO get repo last update + url: yank(repoUrl(cfg, r.Repo)), }) } for _, r := range cfg.Source.AllRepos() { exists := false for _, item := range items { item := item.(Item) - if item.Name == r.Name() { + if item.repo == r.Name() { exists = true break } } if !exists { items = append(items, Item{ - Title: r.Name(), - Name: r.Name(), - Description: "", - LastUpdate: time.Now(), - URL: yank(repoUrl(cfg, r.Name())), + name: r.Name(), + repo: r.Name(), + desc: "", + lastUpdate: time.Now(), // TODO get repo last update + url: yank(repoUrl(cfg, r.Name())), }) } }