From fa52bf2693d38951e50e7ea2b62c4a508a5efb82 Mon Sep 17 00:00:00 2001 From: "Claude Opus 4.7" Date: Thu, 21 May 2026 00:19:35 -0500 Subject: [PATCH] client: actually write incoming messages to the OS clipboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MVP only printed to stdout. Now the listener calls clipboard.WriteAll on every received message, except when the message originated from itself (to avoid clobbering local edits with our own prior send). Adds: - github.com/atotto/clipboard (cross-platform: Win/macOS/Linux) - -no-clipboard flag for stdout-only mode - "→ clipboard updated" trace line so the user can confirm the write Co-Authored-By: Claude Opus 4.7 --- client/main.go | 26 ++++++++++++++++++++++++-- go.mod | 2 ++ go.sum | 2 ++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 go.sum diff --git a/client/main.go b/client/main.go index 1a2662d..2699b72 100644 --- a/client/main.go +++ b/client/main.go @@ -1,5 +1,5 @@ -// tether-client: connects to a tether-server, prints messages to stdout. -// MVP — does not yet touch the OS clipboard. +// tether-client: connects to a tether-server, writes incoming messages +// to the OS clipboard and prints them to stdout. package main import ( @@ -14,6 +14,13 @@ import ( "os" "strings" "time" + + "github.com/atotto/clipboard" +) + +var ( + noClipboard bool + myLabel string ) type Message struct { @@ -26,13 +33,20 @@ func main() { server := flag.String("server", "http://localhost:8765", "tether-server base URL") label := flag.String("label", "linux-client", "X-Tether-Client label") sendText := flag.String("send", "", "send this text and exit (otherwise listen)") + flag.BoolVar(&noClipboard, "no-clipboard", false, "don't write incoming messages to the OS clipboard") flag.Parse() + myLabel = *label if *sendText != "" { send(*server, *label, *sendText) return } + if !noClipboard && clipboard.Unsupported { + fmt.Fprintln(os.Stderr, "tether-client: OS clipboard unsupported on this platform; falling back to stdout-only") + noClipboard = true + } + for { if err := listen(*server, *label); err != nil { log.Printf("stream error: %v — reconnecting in 3s", err) @@ -85,6 +99,14 @@ func listen(server, label string) error { if err := json.Unmarshal([]byte(data), &m); err == nil { ts := time.UnixMilli(m.TS).Format("15:04:05") fmt.Printf("\n────── %s from %s ──────\n%s\n", ts, m.Source, m.Text) + // Don't echo our own message back into our own clipboard + if !noClipboard && m.Source != myLabel { + if err := clipboard.WriteAll(m.Text); err != nil { + fmt.Fprintf(os.Stderr, " ! clipboard write error: %v\n", err) + } else { + fmt.Fprintln(os.Stderr, " → clipboard updated") + } + } } } ev, data = "", "" diff --git a/go.mod b/go.mod index 78fdef5..7b4be4e 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/pecord/tether go 1.22 + +require github.com/atotto/clipboard v0.1.4 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..244b67e --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=