wip: copy text

Ayman Bagabas created

Change summary

go.mod                             |  1 
go.sum                             |  2 +
server/session.go                  | 13 ++++++++---
ui/components/yankable/yankable.go | 33 ++++++++++++++++++++++++++++---
ui/pages/selection/item.go         | 14 ++++++------
ui/pages/selection/selection.go    |  3 +
ui/session/session.go              |  1 
7 files changed, 51 insertions(+), 16 deletions(-)

Detailed changes

go.mod 🔗

@@ -22,6 +22,7 @@ require (
 )
 
 require (
+	github.com/aymanbagabas/go-osc52 v1.0.0
 	github.com/charmbracelet/keygen v0.3.0
 	github.com/gobwas/glob v0.2.3
 	github.com/gogs/git-module v1.6.0

go.sum 🔗

@@ -17,6 +17,8 @@ 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/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=

server/session.go 🔗

@@ -16,8 +16,8 @@ import (
 type Session struct {
 	tea.Model
 	*tea.Program
-	ssh.Session
-	Cfg *appCfg.Config
+	session ssh.Session
+	Cfg     *appCfg.Config
 }
 
 func (s *Session) Config() *appCfg.Config {
@@ -29,8 +29,13 @@ func (s *Session) Send(msg tea.Msg) {
 }
 
 func (s *Session) PublicKey() ssh.PublicKey {
-	return s.Session.PublicKey()
+	return s.session.PublicKey()
 }
+
+func (s *Session) Session() ssh.Session {
+	return s.session
+}
+
 func SessionHandler(ac *appCfg.Config) bm.ProgramHandler {
 	return func(s ssh.Session) *tea.Program {
 		pty, _, active := s.Pty()
@@ -39,7 +44,7 @@ func SessionHandler(ac *appCfg.Config) bm.ProgramHandler {
 			return nil
 		}
 		sess := &Session{
-			Session: s,
+			session: s,
 			Cfg:     ac,
 		}
 		cmd := s.Command()

ui/components/yankable/yankable.go 🔗

@@ -1,8 +1,14 @@
 package yankable
 
 import (
+	"fmt"
+	"log"
+	"strings"
+
+	"github.com/aymanbagabas/go-osc52"
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/charmbracelet/lipgloss"
+	"github.com/gliderlabs/ssh"
 )
 
 type Yankable struct {
@@ -10,14 +16,29 @@ type Yankable struct {
 	style     lipgloss.Style
 	text      string
 	clicked   bool
+	osc52     *osc52.Output
 }
 
-func New(style, yankStyle lipgloss.Style, text string) *Yankable {
+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)
 	return &Yankable{
 		yankStyle: yankStyle,
 		style:     style,
 		text:      text,
 		clicked:   false,
+		osc52:     osc52.NewOutput(s, environ),
 	}
 }
 
@@ -30,18 +51,17 @@ func (y *Yankable) Init() tea.Cmd {
 }
 
 func (y *Yankable) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
-	cmds := make([]tea.Cmd, 0)
 	switch msg := msg.(type) {
 	case tea.MouseMsg:
 		switch msg.Type {
 		case tea.MouseRight:
 			y.clicked = true
-			return y, nil
+			return y, y.copy()
 		}
 	default:
 		y.clicked = false
 	}
-	return y, tea.Batch(cmds...)
+	return y, nil
 }
 
 func (y *Yankable) View() string {
@@ -50,3 +70,8 @@ func (y *Yankable) View() string {
 	}
 	return y.style.Render(y.text)
 }
+
+func (y *Yankable) copy() tea.Cmd {
+	y.osc52.Copy(y.text)
+	return nil
+}

ui/pages/selection/item.go 🔗

@@ -55,9 +55,9 @@ func (d ItemDelegate) Spacing() int { return 1 }
 // Update implements list.ItemDelegate.
 func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
 	cmds := make([]tea.Cmd, 0)
-	if d.activeBox == nil || *d.activeBox != selectorBox {
-		return nil
-	}
+	// if d.activeBox == nil || *d.activeBox != selectorBox {
+	// 	return nil
+	// }
 	for i, item := range m.VisibleItems() {
 		itm, ok := item.(Item)
 		if !ok {
@@ -66,14 +66,14 @@ func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
 		// FIXME check if X & Y are within the item box
 		switch msg := msg.(type) {
 		case tea.MouseMsg:
-			x := msg.X
+			// x := msg.X
 			y := msg.Y
-			minX := (i * d.Width())
-			maxX := minX + d.Width()
+			// minX := (i * d.Width())
+			// maxX := minX + d.Width()
 			minY := (i * d.Height())
 			maxY := minY + d.Height()
 			// log.Printf("i: %d, x: %d, y: %d", i, x, y)
-			if y < minY || y > maxY || x < minX || x > maxX {
+			if y < minY || y > maxY {
 				continue
 			}
 		}

ui/pages/selection/selection.go 🔗

@@ -106,9 +106,10 @@ func (s *Selection) FullHelp() [][]key.Binding {
 func (s *Selection) Init() tea.Cmd {
 	items := make([]list.Item, 0)
 	cfg := s.s.Config()
-	// TODO fix yankable component
+	// TODO clean up this
 	yank := func(text string) *yankable.Yankable {
 		return yankable.New(
+			s.s.Session(),
 			lipgloss.NewStyle().Foreground(lipgloss.Color("168")),
 			lipgloss.NewStyle().Foreground(lipgloss.Color("168")).SetString("Copied!"),
 			text,

ui/session/session.go 🔗

@@ -14,4 +14,5 @@ type Session interface {
 	Config() *appCfg.Config
 	// PublicKey returns the public key of the user.
 	PublicKey() ssh.PublicKey
+	Session() ssh.Session
 }