package main import ( "bytes" "fmt" "os/exec" "runtime" ) type textClipboard interface { ReadText() (string, error) WriteText(string) error } type systemTextClipboard struct{} func (systemTextClipboard) ReadText() (string, error) { command, args, err := clipboardCommand(false) if err != nil { return "", err } output, err := exec.Command(command, args...).Output() if err != nil { return "", fmt.Errorf("read system clipboard: %w", err) } return string(output), nil } func (systemTextClipboard) WriteText(value string) error { command, args, err := clipboardCommand(true) if err != nil { return err } process := exec.Command(command, args...) process.Stdin = bytes.NewBufferString(value) if output, err := process.CombinedOutput(); err != nil { if len(output) > 0 { return fmt.Errorf("write system clipboard: %w: %s", err, bytes.TrimSpace(output)) } return fmt.Errorf("write system clipboard: %w", err) } return nil } func clipboardCommand(write bool) (string, []string, error) { switch runtime.GOOS { case "darwin": if write { return "pbcopy", nil, nil } return "pbpaste", nil, nil case "windows": script := "Get-Clipboard -Raw" if write { script = "$input | Set-Clipboard" } return "powershell.exe", []string{"-NoProfile", "-NonInteractive", "-Command", script}, nil default: type candidate struct { command string write []string read []string } candidates := []candidate{ {command: "wl-copy", read: []string{"-n"}, write: nil}, {command: "xclip", read: []string{"-selection", "clipboard", "-o"}, write: []string{"-selection", "clipboard", "-i"}}, {command: "xsel", read: []string{"--clipboard", "--output"}, write: []string{"--clipboard", "--input"}}, } for _, candidate := range candidates { command := candidate.command args := candidate.read if candidate.command == "wl-copy" && !write { command = "wl-paste" } if write { args = candidate.write } if _, err := exec.LookPath(command); err == nil { return command, args, nil } } return "", nil, fmt.Errorf("system clipboard unavailable: install wl-clipboard, xclip, or xsel") } }