82 lines
2.2 KiB
Go
82 lines
2.2 KiB
Go
package clipboard
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
)
|
|
|
|
// Copy places RTF and HTML onto the system clipboard.
|
|
// HTML is needed for web apps (Slack, Gmail); RTF for native apps (TextEdit, Word).
|
|
func Copy(rtf, html []byte) error {
|
|
switch runtime.GOOS {
|
|
case "darwin":
|
|
return copyDarwin(rtf, html)
|
|
case "linux":
|
|
return copyLinux(rtf, html)
|
|
default:
|
|
return fmt.Errorf("unsupported platform: %s", runtime.GOOS)
|
|
}
|
|
}
|
|
|
|
func copyDarwin(rtf, html []byte) error {
|
|
rtfFile, err := writeTempFile("mach-*.rtf", rtf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.Remove(rtfFile)
|
|
|
|
htmlFile, err := writeTempFile("mach-*.html", html)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.Remove(htmlFile)
|
|
|
|
// Use Swift to set public.rtf, public.html, and public.utf8-plain-text
|
|
// on NSPasteboard. All three are needed:
|
|
// - public.html → web apps (Slack, Gmail, Google Docs)
|
|
// - public.rtf → native apps (TextEdit, Word, Pages)
|
|
// - public.string → plain text fallback (Slack/Electron requires this)
|
|
swift := fmt.Sprintf(`
|
|
import AppKit
|
|
let rtfData = try! Data(contentsOf: URL(fileURLWithPath: "%s"))
|
|
let htmlData = try! Data(contentsOf: URL(fileURLWithPath: "%s"))
|
|
let htmlString = String(data: htmlData, encoding: .utf8) ?? ""
|
|
let pb = NSPasteboard.general
|
|
pb.clearContents()
|
|
pb.setData(rtfData, forType: .rtf)
|
|
pb.setData(htmlData, forType: .html)
|
|
pb.setString(htmlString, forType: .string)
|
|
`, rtfFile, htmlFile)
|
|
|
|
cmd := exec.Command("swift", "-")
|
|
cmd.Stdin = bytes.NewReader([]byte(swift))
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("clipboard write failed: %s: %w", string(out), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func copyLinux(rtf, html []byte) error {
|
|
// xclip can only set one target at a time; prefer HTML for broader compat
|
|
cmd := exec.Command("xclip", "-selection", "clipboard", "-t", "text/html", "-i")
|
|
cmd.Stdin = bytes.NewReader(html)
|
|
return cmd.Run()
|
|
}
|
|
|
|
func writeTempFile(pattern string, data []byte) (string, error) {
|
|
f, err := os.CreateTemp("", pattern)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create temp file: %w", err)
|
|
}
|
|
if _, err := f.Write(data); err != nil {
|
|
f.Close()
|
|
os.Remove(f.Name())
|
|
return "", fmt.Errorf("failed to write temp file: %w", err)
|
|
}
|
|
f.Close()
|
|
return f.Name(), nil
|
|
}
|