feat: Implemeneted copy to clipboard and saves the history of clipboard#17
feat: Implemeneted copy to clipboard and saves the history of clipboard#17Larry8668 merged 1 commit intoLarry8668:mainfrom TanishqDNEC:main
Conversation
WalkthroughIntroduces 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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.jsonsrc-tauri/Cargo.lockis 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)
| // 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); | ||
| } |
There was a problem hiding this comment.
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.
| const unlisten = listen('clipboard-update', (event) => { | ||
| setClipboardItems(prev => [event.payload, ...prev]); | ||
| }); |
There was a problem hiding this comment.
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.
| 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.
|
Hey @TanishqDNEC , this looks good - tried it & works well |
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
🏗️ Technical Implementation
Backend (Tauri/Rust)
Clipboard Monitoring
clipboard-mastercrate for real-time clipboard event detectionData Persistence
clipboard_history.jsonTauri Commands
get_clipboard_history()- Retrieves and deserializes clipboard historypaste_text(text: String)- Simulates paste action usingenigocratedelete_clipboard_item(id: String)- Removes specific entry from storageclear_clipboard_history()- Clears all clipboard history (optional)Frontend (React)
ClipboardPage Component
User Interactions
paste_textcommand and hides window🧪 Testing Instructions
Prerequisites
npm installif neededTest Steps
1. Basic Clipboard Capture
Ctrl + Shift + Space(or configured hotkey)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
Expected Result:
Ctrl+Vrequired[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
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
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
Expected Result: All previously copied items are still present
6. Edge Cases Tested
📸 Screenshot Guide
Required Screenshots
Clipboard History List
Metadata Display
🔧 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 logicsrc/pages/ClipboardPage.jsx- Frontend clipboard view componentsrc/components/ClipboardItem.jsx- Individual clipboard entry componentFiles Modified
src-tauri/src/main.rs- Added clipboard commands and initializationsrc-tauri/Cargo.toml- Added dependencies (clipboard-master,enigo)src/App.jsx- Added routing for clipboard pageDependencies Added
Summary by CodeRabbit