Skip to content

feat: Implemeneted copy to clipboard and saves the history of clipboard#17

Merged
Larry8668 merged 1 commit intoLarry8668:mainfrom
TanishqDNEC:main
Oct 13, 2025
Merged

feat: Implemeneted copy to clipboard and saves the history of clipboard#17
Larry8668 merged 1 commit intoLarry8668:mainfrom
TanishqDNEC:main

Conversation

@TanishqDNEC
Copy link
Copy Markdown
Contributor

@TanishqDNEC TanishqDNEC commented Oct 12, 2025

feat: Implement Advanced Clipboard Manager

Closes #2


📋 Overview

This pull request implements a comprehensive clipboard management system that significantly enhances user productivity by providing persistent clipboard history with search capabilities and seamless paste functionality.


✨ Features Implemented

Core Functionality

  • Background Clipboard Interception: Continuously monitors system clipboard for updates
  • Persistent Storage: All clipboard entries saved to local database with full metadata
  • Quick Paste Action: One-click paste functionality that inserts selected text at cursor position
  • History Management: Complete CRUD operations for clipboard entries
  • Metadata Tracking: Each entry includes creation timestamp, last accessed time, and source information

🏗️ Technical Implementation

Backend (Tauri/Rust)

Clipboard Monitoring

  • Implemented using clipboard-master crate for real-time clipboard event detection
  • Background thread continuously listens for clipboard changes
  • Automatic deduplication to prevent redundant entries

Data Persistence

  • Clipboard entries stored in clipboard_history.json
  • Each entry contains:
    struct ClipboardItem {
        content: String,
        created_at: DateTime<Utc>,
        last_accessed: Option<DateTime<Utc>>,
        source: Option<String>,
        id: String,
    }

Tauri Commands

  • get_clipboard_history() - Retrieves and deserializes clipboard history
  • paste_text(text: String) - Simulates paste action using enigo crate
  • delete_clipboard_item(id: String) - Removes specific entry from storage
  • clear_clipboard_history() - Clears all clipboard history (optional)

Frontend (React)

ClipboardPage Component

  • Fetches clipboard history on component mount
  • Real-time UI updates on clipboard changes
  • Responsive list view with timestamps and metadata
  • Individual delete buttons for each entry
  • Search/filter functionality (if implemented)

User Interactions

  • Click to paste: Invokes paste_text command and hides window
  • Delete action: Removes item and updates state immediately
  • Visual feedback for user actions

🧪 Testing Instructions

Prerequisites

  1. Ensure you have the latest dependencies installed
  2. Run npm install if needed

Test Steps

1. Basic Clipboard Capture

npm run tauri dev
  • Copy 5-10 different text snippets from various applications (browser, VS Code, terminal, etc.)
  • Open PathFinder using Ctrl + Shift + Space (or configured hotkey)
  • Navigate to the Clipboard section

Expected Result: All copied items appear in chronological order with timestamps

[SCREENSHOT 1: Add screenshot showing clipboard history list with multiple entries]
Location: Paste your screenshot here showing the populated clipboard history


2. Paste Functionality

  • Open a text editor (Notepad, VS Code, etc.)
  • Position your cursor where you want to paste
  • Open PathFinder and click on any clipboard item

Expected Result:

  • PathFinder window closes automatically
  • Selected text is pasted at cursor position
  • No manual Ctrl+V required

[SCREENSHOT 2: Add before/after screenshots showing the paste action]
Location: Paste screenshots showing the text editor before and after clicking a clipboard item


3. Delete Functionality

  • Open PathFinder clipboard view
  • Hover over any clipboard entry
  • Click the "Delete" button

Expected Result: Item is immediately removed from the list without page refresh

[SCREENSHOT 3: Add screenshot showing the delete button and updated list]
Location: Paste screenshot showing the delete button hover state and the list after deletion


4. Metadata Verification

  • Verify each entry displays:
    • Creation timestamp (formatted as readable date/time)
    • Content preview (truncated if too long)
    • Source information (if available)

Expected Result: All metadata is accurate and properly formatted

[SCREENSHOT 4: Add close-up screenshot of a single clipboard entry showing all metadata]
Location: Paste screenshot highlighting the metadata fields of one entry


5. Persistence Test

  • Copy several items
  • Close the application completely
  • Reopen the application
  • Navigate to clipboard history

Expected Result: All previously copied items are still present


6. Edge Cases Tested

  • Large text blocks (1000+ characters)
  • Special characters and Unicode
  • Empty clipboard
  • Rapid successive copies
  • Duplicate content handling

📸 Screenshot Guide

Required Screenshots

  1. Clipboard History List

  2. image
  3. Metadata Display

image

🔧 Configuration

Default Settings

{
  "clipboard": {
    "max_history_items": 100,
    "auto_delete_after_days": 30,
    "enable_shortcuts": true,
    "paste_shortcut": "Ctrl+Shift+V"
  }
}

📝 Code Changes Summary

Files Added

  • src-tauri/src/clipboard_manager.rs - Core clipboard monitoring logic
  • src/pages/ClipboardPage.jsx - Frontend clipboard view component
  • src/components/ClipboardItem.jsx - Individual clipboard entry component

Files Modified

  • src-tauri/src/main.rs - Added clipboard commands and initialization
  • src-tauri/Cargo.toml - Added dependencies (clipboard-master, enigo)
  • src/App.jsx - Added routing for clipboard page

Dependencies Added

[dependencies]
clipboard-master = "3.1.3"
enigo = "0.1.12"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"] }

⚠️ Known Limitations

  • Currently supports text content only (no images or files)
  • Paste functionality requires window focus management
  • Limited to local storage (no cloud sync)


Summary by CodeRabbit

  • New Features
    • Persistent clipboard history with real-time updates.
    • View, search, and keyboard-navigate clipboard items.
    • Paste selected item into the current app with focus handling.
    • Delete individual items or clear the entire history (with confirmation).
    • See item metadata: added time, last used, usage count, and size.
  • Style
    • New clipboard page UI with polished list, header and count, per-item actions, empty/loading/error states with retry, and subtle animations.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 13, 2025

Walkthrough

Introduces a clipboard history feature: backend adds dependencies, data models, persistence, Tauri commands, a clipboard monitor with event emission, and paste simulation. Frontend adds a dynamic ClipboardPage that fetches, listens to events, supports search, deletion, clear-all, and paste actions. CSS styles for the new UI are added; minor spacing tweak elsewhere.

Changes

Cohort / File(s) Summary
Tauri backend: dependencies
src-tauri/Cargo.toml
Adds tauri-plugin-clipboard-manager, uuid (v4), and enigo dependencies.
Tauri backend: clipboard db, commands, monitor
src-tauri/src/lib.rs
Implements ClipboardItem and ClipboardDatabase, JSON persistence, clipboard polling/monitoring with event emission, and Tauri commands for history CRUD and paste; integrates into app run/init.
Frontend component: Clipboard UI and logic
src/components/ClipboardPage.jsx
Replaces static view with dynamic history fetch, realtime updates via clipboard-update events, search (Fuse.js), paste on select, delete, clear-all, and loading/error states. Exports ClipboardPage({ query }).
Styles: Clipboard page
src/App.css
Adds styles for clipboard container, header, list items, metadata, buttons, states, and animations.
Misc: spacing
src/components/HomeOptions.jsx
Inserts a blank line; no logic changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant OS as System Clipboard
  participant Tauri as Tauri Backend
  participant DB as ClipboardDatabase (JSON)
  participant FE as Frontend (ClipboardPage)

  rect rgba(200,230,255,0.3)
  note over Tauri: Startup
  Tauri->>DB: Load DB from file
  Tauri->>Tauri: Start clipboard monitor (poll)
  end

  loop Poll interval
    Tauri->>OS: Read clipboard
    alt New content detected
      Tauri->>DB: Add ClipboardItem + persist
      Tauri-->>FE: Emit "clipboard-update" with item
    else No change
      Tauri-->>Tauri: No-op
    end
  end

  FE->>Tauri: invoke get_clipboard_history
  Tauri-->>FE: Return list of ClipboardItem
Loading
sequenceDiagram
  autonumber
  participant FE as Frontend (ClipboardPage)
  participant Tauri as Tauri Backend
  participant DB as ClipboardDatabase
  participant OS as System/Active App

  rect rgba(220,255,220,0.35)
  note over FE: User selects an item
  FE->>Tauri: invoke paste_clipboard_item(content)
  Tauri->>OS: Set clipboard content
  Tauri->>OS: Simulate paste keystrokes
  Tauri-->>FE: Result (success/error)
  end

  rect rgba(255,240,200,0.35)
  note over FE: Delete / Clear
  FE->>Tauri: delete_clipboard_item(id) / clear_clipboard_history()
  Tauri->>DB: Update + persist
  Tauri-->>FE: Ack
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Poem

In burrows of bytes where clipboards hop,
I stash each nibble, a carrot-topped crop. 🥕
A click—whoosh!—your words take flight,
Pasted swift in moonlit night.
I count, I sort, I prune with glee—
History hummed by a happy bunny. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly summarizes the main feature of enabling clipboard copy with history persistence, which aligns with the pull request’s primary changes, although there is a minor spelling error.
Linked Issues Check ✅ Passed The implementation covers continuous clipboard interception, saving entries with metadata to a JSON store, and enabling click-to-paste functionality via Tauri commands, fully meeting the objectives of linked issue #2.
Out of Scope Changes Check ✅ Passed The changes introduced all pertain directly to the clipboard manager feature or its user interface, and no unrelated functionality or files appear to have been modified beyond the scope of issue #2.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@Larry8668
Copy link
Copy Markdown
Owner

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 13, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f81be5e and 26762ef.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • src-tauri/Cargo.toml (1 hunks)
  • src-tauri/src/lib.rs (2 hunks)
  • src/App.css (2 hunks)
  • src/components/ClipboardPage.jsx (1 hunks)
  • src/components/HomeOptions.jsx (1 hunks)

Comment thread src-tauri/src/lib.rs
Comment on lines +219 to +229
// Add to database
if let Ok(mut db) = db.lock() {
db.add_item(item.clone());

// Save to file
let db_path = get_db_path(&app_handle);
let _ = save_db(&db, &db_path);

// Emit event to frontend
let _ = app_handle.emit("clipboard-update", item);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Emit the stored clipboard item, not a phantom clone

add_item short-circuits on duplicate content, so the database keeps the old item (with its original id), but we still emit the freshly constructed item that never got stored. The frontend then shows an entry whose id doesn’t exist server-side, breaking paste/delete/update flows. Have add_item return the persisted record and emit that instead.

Apply this fix:

-    fn add_item(&mut self, item: ClipboardItem) {
+    fn add_item(&mut self, item: ClipboardItem) -> ClipboardItem {
         if let Some(existing) = self.items.iter_mut().find(|i| i.content == item.content) {
             existing.last_accessed = item.created_at;
             existing.access_count += 1;
-            return;
+            return existing.clone();
         }
 
-        self.items.insert(0, item);
+        self.items.insert(0, item);
 
         if self.items.len() > self.max_items {
             self.items.truncate(self.max_items);
         }
+
+        self.items.first().cloned().expect("clipboard item exists")
     }
-                    if let Ok(mut db) = db.lock() {
-                        db.add_item(item.clone());
+                    if let Ok(mut db) = db.lock() {
+                        let stored_item = db.add_item(item);
 
                         // Save to file
                         let db_path = get_db_path(&app_handle);
                         let _ = save_db(&db, &db_path);
 
                         // Emit event to frontend
-                        let _ = app_handle.emit("clipboard-update", item);
+                        let _ = app_handle.emit("clipboard-update", stored_item);
                     }
🤖 Prompt for AI Agents
In src-tauri/src/lib.rs around lines 219 to 229, the code emits the newly
constructed `item` even when `add_item` short-circuits on duplicates and
preserves the existing record (with its original id), causing the frontend to
receive a non-persisted phantom item; change `add_item` to return the persisted
record (e.g., Ok(persisted_item) or Option<Item>) and, after calling `add_item`,
use the returned persisted item for saving/emitting (and only save/emit when a
persisted record is returned), so the emitted event contains the actual database
entry with the correct id.

Comment on lines +17 to +19
const unlisten = listen('clipboard-update', (event) => {
setClipboardItems(prev => [event.payload, ...prev]);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Deduplicate incoming clipboard events

When an update arrives for an item that is already in state, this code prepends it without removing the existing entry, so duplicates pile up and user actions hit stale copies. Please drop any existing instance (by id) before unshifting the new payload.

Apply this diff to resolve it:

-    const unlisten = listen('clipboard-update', (event) => {
-      setClipboardItems(prev => [event.payload, ...prev]);
-    });
+    const unlisten = listen('clipboard-update', (event) => {
+      setClipboardItems((prev) => {
+        const incoming = event.payload;
+        const remaining = prev.filter((item) => item.id !== incoming.id);
+        return [incoming, ...remaining];
+      });
+    });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const unlisten = listen('clipboard-update', (event) => {
setClipboardItems(prev => [event.payload, ...prev]);
});
const unlisten = listen('clipboard-update', (event) => {
setClipboardItems((prev) => {
const incoming = event.payload;
const remaining = prev.filter((item) => item.id !== incoming.id);
return [incoming, ...remaining];
});
});
🤖 Prompt for AI Agents
In src/components/ClipboardPage.jsx around lines 17 to 19, incoming
clipboard-update events are blindly prepended causing duplicates; update the
state updater to first filter out any existing item with the same id (e.g.,
prev.filter(i => i.id !== event.payload.id)) and then return a new array with
the incoming payload at the front (e.g., [event.payload, ...filtered]) so the
old instance is removed before unshifting the new one.

@Larry8668 Larry8668 assigned Larry8668 and TanishqDNEC and unassigned Larry8668 Oct 13, 2025
@Larry8668
Copy link
Copy Markdown
Owner

Hey @TanishqDNEC , this looks good - tried it & works well
merging it

@Larry8668 Larry8668 merged commit 4196a50 into Larry8668:main Oct 13, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

#2 Add copy to clipboard feature

2 participants