package sources import ( "context" "errors" "fmt" "github.com/atotto/clipboard" "github.com/salvacybersec/keyhunter/pkg/types" ) // ClipboardSource reads the current OS clipboard contents and emits them // as a single chunk stream with Source="clipboard". Requires xclip/xsel/ // wl-clipboard on Linux, pbpaste on macOS, or native API on Windows. type ClipboardSource struct { // Reader overrides the clipboard reader; when nil the real clipboard is used. // Tests inject a func returning a fixture. Reader func() (string, error) ChunkSize int } // NewClipboardSource returns a ClipboardSource bound to the real OS clipboard. func NewClipboardSource() *ClipboardSource { return &ClipboardSource{Reader: clipboard.ReadAll, ChunkSize: defaultChunkSize} } // Chunks reads the clipboard and emits its contents. func (c *ClipboardSource) Chunks(ctx context.Context, out chan<- types.Chunk) error { if clipboard.Unsupported && c.Reader == nil { return errors.New("ClipboardSource: clipboard tooling unavailable (install xclip/xsel/wl-clipboard on Linux)") } reader := c.Reader if reader == nil { reader = clipboard.ReadAll } text, err := reader() if err != nil { return fmt.Errorf("ClipboardSource: read: %w", err) } if text == "" { return nil } return emitByteChunks(ctx, []byte(text), "clipboard", c.ChunkSize, out) }