go.mod 🔗
@@ -22,7 +22,7 @@ require (
)
require (
- github.com/aymanbagabas/go-osc52 v1.0.0
+ github.com/aymanbagabas/go-osc52 v1.0.1
github.com/charmbracelet/keygen v0.3.0
github.com/gobwas/glob v0.2.3
github.com/gogs/git-module v1.6.0
Ayman Bagabas created
go.mod | 2
go.sum | 2 +
ui/components/code/code.go | 1
ui/components/selector/selector.go | 49 +++++++++++++++++++++++++++----
ui/components/yankable/yankable.go | 22 +------------
ui/pages/selection/selection.go | 31 ++++++++++++++++---
ui/styles/styles.go | 4 -
7 files changed, 76 insertions(+), 35 deletions(-)
@@ -22,7 +22,7 @@ require (
)
require (
- github.com/aymanbagabas/go-osc52 v1.0.0
+ github.com/aymanbagabas/go-osc52 v1.0.1
github.com/charmbracelet/keygen v0.3.0
github.com/gobwas/glob v0.2.3
github.com/gogs/git-module v1.6.0
@@ -19,6 +19,8 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
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=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/caarlos0/env/v6 v6.9.1 h1:zOkkjM0F6ltnQ5eBX6IPI41UP/KDGEK7rRPwGCNos8k=
@@ -70,6 +70,7 @@ func (r *Code) Init() tea.Cmd {
if err != nil {
return common.ErrorCmd(err)
}
+ // FIXME reset underline and color
c = wrap.String(f, w)
r.viewport.Viewport.SetContent(c)
return nil
@@ -9,9 +9,10 @@ import (
// Selector is a list of items that can be selected.
type Selector struct {
- list list.Model
- common common.Common
- active int
+ list list.Model
+ common common.Common
+ active int
+ filterState list.FilterState
}
// IdentifiableItem is an item that can be identified by a string and extends list.Item.
@@ -27,8 +28,12 @@ type SelectMsg string
type ActiveMsg string
// New creates a new selector.
-func New(common common.Common, items []list.Item, delegate list.ItemDelegate) *Selector {
- l := list.New(items, delegate, common.Width, common.Height)
+func New(common common.Common, items []IdentifiableItem, delegate list.ItemDelegate) *Selector {
+ itms := make([]list.Item, len(items))
+ for i, item := range items {
+ itms[i] = item
+ }
+ l := list.New(itms, delegate, common.Width, common.Height)
l.SetShowTitle(false)
l.SetShowHelp(false)
l.SetShowStatusBar(false)
@@ -76,12 +81,20 @@ func (s *Selector) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case key.Matches(msg, s.common.Keymap.Select):
cmds = append(cmds, s.selectCmd)
}
+ case list.FilterMatchesMsg:
+ cmds = append(cmds, s.activeFilterCmd)
}
m, cmd := s.list.Update(msg)
s.list = m
if cmd != nil {
cmds = append(cmds, cmd)
}
+ // Track filter state and update active item when filter state changes.
+ filterState := s.list.FilterState()
+ if s.filterState != filterState {
+ cmds = append(cmds, s.activeFilterCmd)
+ }
+ s.filterState = filterState
// Send ActiveMsg when index change.
if s.active != s.list.Index() {
cmds = append(cmds, s.activeCmd)
@@ -97,12 +110,34 @@ func (s *Selector) View() string {
func (s *Selector) selectCmd() tea.Msg {
item := s.list.SelectedItem()
- i := item.(IdentifiableItem)
+ i, ok := item.(IdentifiableItem)
+ if !ok {
+ return SelectMsg("")
+ }
return SelectMsg(i.ID())
}
func (s *Selector) activeCmd() tea.Msg {
item := s.list.SelectedItem()
- i := item.(IdentifiableItem)
+ i, ok := item.(IdentifiableItem)
+ if !ok {
+ return ActiveMsg("")
+ }
+ return ActiveMsg(i.ID())
+}
+
+func (s *Selector) activeFilterCmd() tea.Msg {
+ // Here we use VisibleItems because when list.FilterMatchesMsg is sent,
+ // VisibleItems is the only way to get the list of filtered items. The list
+ // bubble should export something like list.FilterMatchesMsg.Items().
+ items := s.list.VisibleItems()
+ if len(items) == 0 {
+ return nil
+ }
+ item := items[0]
+ i, ok := item.(IdentifiableItem)
+ if !ok {
+ return nil
+ }
return ActiveMsg(i.ID())
}
@@ -1,14 +1,11 @@
package yankable
import (
- "fmt"
- "log"
- "strings"
+ "io"
"github.com/aymanbagabas/go-osc52"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
- "github.com/gliderlabs/ssh"
)
type Yankable struct {
@@ -19,26 +16,13 @@ type Yankable struct {
osc52 *osc52.Output
}
-func New(s ssh.Session, style, yankStyle lipgloss.Style, text string) *Yankable {
- environ := s.Environ()
- termExists := false
- for _, env := range environ {
- if strings.HasPrefix(env, "TERM=") {
- termExists = true
- break
- }
- }
- if !termExists {
- pty, _, _ := s.Pty()
- environ = append(environ, fmt.Sprintf("TERM=%s", pty.Term))
- }
- log.Print(environ)
+func New(w io.Writer, environ []string, style, yankStyle lipgloss.Style, text string) *Yankable {
return &Yankable{
yankStyle: yankStyle,
style: style,
text: text,
clicked: false,
- osc52: osc52.NewOutput(s, environ),
+ osc52: osc52.NewOutput(w, environ),
}
}
@@ -2,6 +2,7 @@ package selection
import (
"fmt"
+ "strings"
"time"
"github.com/charmbracelet/bubbles/key"
@@ -37,12 +38,14 @@ func New(s session.Session, common common.Common) *Selection {
sel := &Selection{
s: s,
common: common,
- activeBox: 1,
+ activeBox: selectorBox, // start with the selector focused
}
readme := code.New(common, "", "")
readme.NoContentStyle = readme.NoContentStyle.SetString("No readme found.")
sel.readme = readme
- sel.selector = selector.New(common, []list.Item{}, ItemDelegate{common.Styles, &sel.activeBox})
+ sel.selector = selector.New(common,
+ []selector.IdentifiableItem{},
+ ItemDelegate{common.Styles, &sel.activeBox})
return sel
}
@@ -52,7 +55,11 @@ func (s *Selection) SetSize(width, height int) {
sw := s.common.Styles.SelectorBox.GetWidth()
wm := sw +
s.common.Styles.SelectorBox.GetHorizontalFrameSize() +
- s.common.Styles.ReadmeBox.GetHorizontalFrameSize()
+ s.common.Styles.ReadmeBox.GetHorizontalFrameSize() +
+ // +1 to get wrapping to work.
+ // This is needed because the readme box width has to be -1 from the
+ // readme style in order for wrapping to not break.
+ 1
hm := s.common.Styles.ReadmeBox.GetVerticalFrameSize()
s.readme.SetSize(width-wm, height-hm)
s.selector.SetSize(sw, height)
@@ -104,12 +111,26 @@ func (s *Selection) FullHelp() [][]key.Binding {
// Init implements tea.Model.
func (s *Selection) Init() tea.Cmd {
+ session := s.s.Session()
+ environ := session.Environ()
+ termExists := false
+ for _, env := range environ {
+ if strings.HasPrefix(env, "TERM=") {
+ termExists = true
+ break
+ }
+ }
+ if !termExists {
+ pty, _, _ := session.Pty()
+ environ = append(environ, fmt.Sprintf("TERM=%s", pty.Term))
+ }
items := make([]list.Item, 0)
cfg := s.s.Config()
// TODO clean up this
yank := func(text string) *yankable.Yankable {
return yankable.New(
- s.s.Session(),
+ session,
+ environ,
lipgloss.NewStyle().Foreground(lipgloss.Color("168")),
lipgloss.NewStyle().Foreground(lipgloss.Color("168")).SetString("Copied!"),
text,
@@ -122,7 +143,7 @@ func (s *Selection) Init() tea.Cmd {
Name: r.Repo,
Description: r.Note,
LastUpdate: time.Now(),
- URL: yank(repoUrl(cfg, r.Name)),
+ URL: yank(repoUrl(cfg, r.Repo)),
})
}
for _, r := range cfg.Source.AllRepos() {
@@ -122,9 +122,7 @@ func DefaultStyles() *Styles {
Width(64)
s.ReadmeBox = lipgloss.NewStyle().
- BorderForeground(s.InactiveBorderColor).
- Padding(1).
- MarginRight(1)
+ Margin(1, 1, 1, 0)
s.RepoTitleBorder = lipgloss.Border{
Top: "─",