Kaynağa Gözat

feat: attach image from clipboard

Closes #96
ayn2op 9 ay önce
ebeveyn
işleme
f91e7d39b5
5 değiştirilmiş dosya ile 44 ekleme ve 13 silme
  1. 32 13
      cmd/message_input.go
  2. 3 0
      go.mod
  3. 6 0
      go.sum
  4. 2 0
      internal/config/config.toml
  5. 1 0
      internal/config/keys.go

+ 32 - 13
cmd/message_input.go

@@ -1,6 +1,7 @@
 package cmd
 
 import (
+	"bytes"
 	"fmt"
 	"io"
 	"log/slog"
@@ -13,7 +14,6 @@ import (
 	"time"
 	"unicode"
 
-	"github.com/atotto/clipboard"
 	"github.com/ayn2op/discordo/internal/cache"
 	"github.com/ayn2op/discordo/internal/config"
 	"github.com/ayn2op/discordo/internal/consts"
@@ -28,6 +28,7 @@ import (
 	"github.com/ncruces/zenity"
 	"github.com/sahilm/fuzzy"
 	"github.com/yuin/goldmark/ast"
+	"golang.design/x/clipboard"
 )
 
 const tmpFilePattern = consts.Name + "_*.md"
@@ -56,15 +57,20 @@ func newMessageInput(cfg *config.Config) *messageInput {
 		mentionsList:    tview.NewList(),
 	}
 
+	if err := clipboard.Init(); err != nil {
+		slog.Warn("failed to init clipboard", "err", err)
+	} else {
+		mi.
+			SetClipboard(func(s string) {
+				clipboard.Write(clipboard.FmtText, []byte(s))
+			}, func() string {
+				data := clipboard.Read(clipboard.FmtText)
+				return string(data)
+			})
+	}
+
 	mi.Box = ui.ConfigureBox(mi.Box, &cfg.Theme)
-	mi.
-		SetClipboard(func(s string) {
-			clipboard.WriteAll(s)
-		}, func() string {
-			text, _ := clipboard.ReadAll()
-			return text
-		}).
-		SetInputCapture(mi.onInputCapture)
+	mi.SetInputCapture(mi.onInputCapture)
 
 	mi.mentionsList.Box = ui.ConfigureBox(mi.mentionsList.Box, &mi.cfg.Theme)
 	mi.mentionsList.
@@ -88,6 +94,10 @@ func (mi *messageInput) reset() {
 
 func (mi *messageInput) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
 	switch event.Name() {
+	case mi.cfg.Keys.MessageInput.Paste:
+		mi.paste()
+		return tcell.NewEventKey(tcell.KeyCtrlV, 0, tcell.ModNone)
+
 	case mi.cfg.Keys.MessageInput.Send:
 		if app.pages.GetVisibile(mentionsListPageName) {
 			mi.tabComplete(false)
@@ -140,6 +150,13 @@ func (mi *messageInput) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
 	return event
 }
 
+func (mi *messageInput) paste() {
+	if data := clipboard.Read(clipboard.FmtImage); data != nil {
+		name := "clipboard.png"
+		mi.attach(name, sendpart.File{Name: name, Reader: bytes.NewReader(data)})
+	}
+}
+
 func (mi *messageInput) send() {
 	if !app.guildsTree.selectedChannelID.IsValid() {
 		return
@@ -486,9 +503,11 @@ func (mi *messageInput) openFilePicker() {
 		}
 
 		name := filepath.Base(path)
-		mi.sendMessageData.Files = append(mi.sendMessageData.Files, sendpart.File{Name: name, Reader: file})
-
-		title := fmt.Sprintf("Attached %s", name)
-		mi.addTitle(title)
+		mi.attach(name, sendpart.File{Name: name, Reader: file})
 	}
 }
+
+func (mi *messageInput) attach(name string, file sendpart.File) {
+	mi.sendMessageData.Files = append(mi.sendMessageData.Files, file)
+	mi.addTitle("Attached " + name)
+}

+ 3 - 0
go.mod

@@ -18,6 +18,7 @@ require (
 	github.com/spf13/cobra v1.9.1
 	github.com/yuin/goldmark v1.7.13
 	github.com/zalando/go-keyring v0.2.6
+	golang.design/x/clipboard v0.7.1
 )
 
 require (
@@ -47,7 +48,9 @@ require (
 	github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
 	github.com/twmb/murmur3 v1.1.8 // indirect
 	go4.org v0.0.0-20230225012048-214862532bf5 // indirect
+	golang.org/x/exp/shiny v0.0.0-20250718183923-645b1fa84792 // indirect
 	golang.org/x/image v0.29.0 // indirect
+	golang.org/x/mobile v0.0.0-20250711185624-d5bb5ecc55c0 // indirect
 	golang.org/x/sys v0.34.0 // indirect
 	golang.org/x/term v0.33.0 // indirect
 	golang.org/x/text v0.27.0 // indirect

+ 6 - 0
go.sum

@@ -178,6 +178,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
 go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
+golang.design/x/clipboard v0.7.1 h1:OEG3CmcYRBNnRwpDp7+uWLiZi3hrMRJpE9JkkkYtz2c=
+golang.design/x/clipboard v0.7.1/go.mod h1:i5SiIqj0wLFw9P/1D7vfILFK0KHMk7ydE72HRrUIgkg=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -194,6 +196,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
 golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp/shiny v0.0.0-20250718183923-645b1fa84792 h1:YkeFUC3wFzzmWTi5lYbb5af46Tpk9DLWHXXwGjLF3R0=
+golang.org/x/exp/shiny v0.0.0-20250718183923-645b1fa84792/go.mod h1:DUdAjGCS1V5oj0c1HZTX5UNuMxBjfxuU/NIoy/wuiRw=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas=
@@ -209,6 +213,8 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
 golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mobile v0.0.0-20250711185624-d5bb5ecc55c0 h1:Z6EFcPz8e1cx0ge5jWCwqafndPjdsDQf8fk4Kw3pJoI=
+golang.org/x/mobile v0.0.0-20250711185624-d5bb5ecc55c0/go.mod h1:kqVs191xxTTCd39tk8zK1UD3jyCS1SPrMHTpJ9ujxZg=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=

+ 2 - 0
internal/config/config.toml

@@ -81,6 +81,8 @@ yank_id = "Rune[i]"
 # Only while typing a message
 # Alt+Enter: Insert a new line to the current text.
 [keys.message_input]
+# paste from clipboard (supports both text and images)
+paste = "Ctrl+V"
 send = "Enter"
 # Remove existing text or cancel reply.
 cancel = "Esc"

+ 1 - 0
internal/config/keys.go

@@ -50,6 +50,7 @@ type (
 	}
 
 	MessageInputKeys struct {
+		Paste       string `toml:"paste"`
 		Send        string `toml:"send"`
 		Cancel      string `toml:"cancel"`
 		TabComplete string `toml:"tab_complete"`