package main import ( "fmt" "os" "os/exec" "os/signal" "strings" "syscall" "time" ) // clearClipboardAfter clears the clipboard after a delay // Runs in a goroutine so it doesn't block func clearClipboardAfter(duration time.Duration) { go func() { time.Sleep(duration) clearClipboard() }() } // clearClipboard clears the clipboard // Tries both Wayland and X11 - whichever is available func clearClipboard() { var cmd *exec.Cmd if os.Getenv("WAYLAND_DISPLAY") != "" { cmd = exec.Command("wl-copy", "--clear") } else if os.Getenv("DISPLAY") != "" { // X11 - clear by copying /dev/null (hacky but works) cmd = exec.Command("xclip", "-selection", "clipboard", "/dev/null") } else { return // No clipboard available, nothing to clear } cmd.Run() // Best effort - if it fails, oh well } // clipToClipboard copies text to clipboard and sets up auto-clear func clipToClipboard(text, path string) { clipTime := 10 if env := os.Getenv("PASSAGE_CLIP_TIME"); env != "" { if n, err := fmt.Sscanf(env, "%d", &clipTime); err != nil || n != 1 || clipTime < 0 || clipTime > 3600 { clipTime = 10 // Reset to default on invalid input } } // Try xclip (X11) or wl-copy (Wayland) var cmd *exec.Cmd if os.Getenv("WAYLAND_DISPLAY") != "" { cmd = exec.Command("wl-copy") } else if os.Getenv("DISPLAY") != "" { cmd = exec.Command("xclip", "-selection", "clipboard") } else { fmt.Fprintf(os.Stderr, "Error: no clipboard available (needs X11 or Wayland)\n") os.Exit(1) } cmd.Stdin = strings.NewReader(text) if err := cmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "Error: failed to copy to clipboard: %v\n", err) os.Exit(1) } fmt.Printf("Copied %s to clipboard. Will clear in %d seconds.\n", path, clipTime) // Set up signal handler to clear clipboard if user hits Ctrl+C sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) // Auto-clear after the timeout clearClipboardAfter(time.Duration(clipTime) * time.Second) // Also clear if interrupted - don't leave passwords lying around go func() { <-sigChan clearClipboard() os.Exit(0) }() }