Commit 6fe1a0ef6fde

Vincent Demeester <vincent@sbr.pm>
2025-12-12 15:28:23
feat(clipboard): Add comprehensive clipboard history management
- Enable persistent clipboard history with fuzzel integration - Support 1000 items with improved deduplication (100-entry search) - Provide pattern-based cleanup tool for sensitive data removal Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 8e3de49
Changed files (8)
dots
.config
home
common
pkgs
tools
dots/.config/niri/config.kdl
@@ -186,6 +186,8 @@ binds {
     Mod+Shift+D hotkey-overlay-title="Run Raffi" { spawn "raffi" "-I"; }
     // FIXME do not use nix run, but needs niri configuration
     Mod+Control+D hotkey-overlay-title="Emoji picker" { spawn "rofimoji" "--selector" "fuzzel" "--clipboarder" "wl-copy" "--typer" "wtype" "--action" "type" "copy"; }
+    Mod+Control+V hotkey-overlay-title="Clipboard History" { spawn "bash" "-c" "cliphist list | fuzzel --dmenu --width=100 --lines=20 | cliphist decode | wl-copy"; }
+    Mod+Control+Shift+V hotkey-overlay-title="Delete Clipboard Entry" { spawn "bash" "-c" "cliphist list | fuzzel --dmenu --width=100 --lines=20 | cliphist delete"; }
     Super+Alt+L hotkey-overlay-title="Lock the Screen" { spawn "swaylock" "-m" "fill" "-i" "/home/vincent/desktop/pictures/lockscreen"; }
 
     // You can also use a shell. Do this if you need pipes, multiple commands, etc.
home/common/desktop/niri/default.nix
@@ -31,6 +31,12 @@
     cliphist = {
       enable = true;
       package = pkgs.cliphist;
+      extraOptions = [
+        "-max-dedupe-search"
+        "100"
+        "-max-items"
+        "1000"
+      ];
     };
   };
   programs.niri = {
home/common/desktop/sway/default.nix
@@ -45,6 +45,12 @@ in
     cliphist = {
       enable = true;
       package = pkgs.cliphist;
+      extraOptions = [
+        "-max-dedupe-search"
+        "100"
+        "-max-items"
+        "1000"
+      ];
     };
   };
 
pkgs/default.nix
@@ -26,6 +26,7 @@ in
   gh-resolve-conflicts = pkgs.callPackage ../tools/gh-resolve-conflicts { };
   arr = pkgs.callPackage ../tools/arr { };
   download-kiwix-zim = pkgs.callPackage ../tools/download-kiwix-zim { };
+  cliphist-cleanup = pkgs.callPackage ../tools/cliphist-cleanup { };
   toggle-color-scheme = pkgs.callPackage ./toggle-color-scheme { };
   homepage = pkgs.callPackage ./homepage { inherit globals; };
 
tools/cliphist-cleanup/default.nix
@@ -0,0 +1,14 @@
+{ buildGoModule }:
+
+buildGoModule {
+  pname = "cliphist-cleanup";
+  version = "0.1.0";
+  src = ./.;
+
+  vendorHash = null;
+
+  meta = {
+    description = "Clean up cliphist clipboard history by pattern matching";
+    mainProgram = "cliphist-cleanup";
+  };
+}
tools/cliphist-cleanup/go.mod
@@ -0,0 +1,3 @@
+module cliphist-cleanup
+
+go 1.25.4
tools/cliphist-cleanup/main.go
@@ -0,0 +1,108 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+	"os/exec"
+	"regexp"
+	"strings"
+)
+
+func main() {
+	if len(os.Args) < 2 {
+		fmt.Fprintln(os.Stderr, "Usage: cliphist-cleanup <pattern> [pattern2] [pattern3] ...")
+		fmt.Fprintln(os.Stderr, "")
+		fmt.Fprintln(os.Stderr, "Examples:")
+		fmt.Fprintln(os.Stderr, "  cliphist-cleanup 'Signed-off-by:'")
+		fmt.Fprintln(os.Stderr, "  cliphist-cleanup '^# This is' 'Co-Authored-By:'")
+		fmt.Fprintln(os.Stderr, "")
+		fmt.Fprintln(os.Stderr, "Patterns are treated as regular expressions (case-insensitive).")
+		os.Exit(1)
+	}
+
+	// Compile patterns
+	patterns := make([]*regexp.Regexp, 0, len(os.Args)-1)
+	for _, pattern := range os.Args[1:] {
+		re, err := regexp.Compile("(?i)" + pattern)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Error compiling pattern '%s': %v\n", pattern, err)
+			os.Exit(1)
+		}
+		patterns = append(patterns, re)
+	}
+
+	// Get clipboard history
+	cmd := exec.Command("cliphist", "list")
+	stdout, err := cmd.StdoutPipe()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error creating pipe: %v\n", err)
+		os.Exit(1)
+	}
+
+	if err := cmd.Start(); err != nil {
+		fmt.Fprintf(os.Stderr, "Error starting cliphist list: %v\n", err)
+		os.Exit(1)
+	}
+
+	// Read entries and delete matching ones
+	scanner := bufio.NewScanner(stdout)
+	deleted := 0
+	checked := 0
+
+	for scanner.Scan() {
+		line := scanner.Text()
+		checked++
+
+		// Check if line matches any pattern
+		matched := false
+		for _, re := range patterns {
+			if re.MatchString(line) {
+				matched = true
+				break
+			}
+		}
+
+		if matched {
+			// Delete this entry using delete-query
+			// The line format from cliphist list is: "ID\tCONTENT"
+			// We need to extract the content part after the tab
+			parts := strings.SplitN(line, "\t", 2)
+			if len(parts) < 2 {
+				continue
+			}
+
+			content := parts[1]
+			deleteCmd := exec.Command("cliphist", "delete-query", content)
+			if err := deleteCmd.Run(); err != nil {
+				fmt.Fprintf(os.Stderr, "Warning: Failed to delete entry: %v\n", err)
+			} else {
+				deleted++
+				fmt.Printf("Deleted: %s\n", truncate(content, 80))
+			}
+		}
+	}
+
+	if err := scanner.Err(); err != nil {
+		fmt.Fprintf(os.Stderr, "Error reading cliphist output: %v\n", err)
+		os.Exit(1)
+	}
+
+	if err := cmd.Wait(); err != nil {
+		fmt.Fprintf(os.Stderr, "Error waiting for cliphist: %v\n", err)
+		os.Exit(1)
+	}
+
+	fmt.Printf("\nChecked %d entries, deleted %d entries\n", checked, deleted)
+}
+
+func truncate(s string, maxLen int) string {
+	// Replace newlines with spaces for display
+	s = strings.ReplaceAll(s, "\n", " ")
+	s = strings.ReplaceAll(s, "\r", " ")
+
+	if len(s) <= maxLen {
+		return s
+	}
+	return s[:maxLen-3] + "..."
+}
tools/cliphist-cleanup/README.md
@@ -0,0 +1,48 @@
+# cliphist-cleanup
+
+Clean up your clipboard history by deleting entries matching specific patterns.
+
+## Usage
+
+```bash
+cliphist-cleanup <pattern> [pattern2] [pattern3] ...
+```
+
+Patterns are treated as case-insensitive regular expressions.
+
+## Examples
+
+Delete all git sign-off entries:
+```bash
+cliphist-cleanup 'Signed-off-by:'
+```
+
+Delete multiple types of git commit messages:
+```bash
+cliphist-cleanup '# This is a combination' 'Co-Authored-By:'
+```
+
+Delete entries starting with specific text:
+```bash
+cliphist-cleanup '^password:' '^token:'
+```
+
+## How It Works
+
+1. Lists all clipboard history entries using `cliphist list`
+2. Matches each entry against the provided patterns
+3. Deletes matching entries using `cliphist delete-query`
+4. Reports the number of entries checked and deleted
+
+## Building
+
+```bash
+nix build .#cliphist-cleanup
+```
+
+## Installing
+
+Add to your home-manager packages or install directly:
+```bash
+nix profile install .#cliphist-cleanup
+```