Closed
Description
Description
Implement secure copy to clipboard functionality for code blocks and share links, ensuring sensitive data is never logged.
Security Requirements
- NEVER log or console.log copied content (could contain decrypted data)
- Copy full URLs including hash fragments (e.g.,
https://ghostpaste.dev/g/abc123#key=...
) - Ensure clipboard API doesn't leak data to browser extensions
- Clear clipboard data from memory after copy
Core Features
1. Share Link Copying
- Copy button next to share URL after gist creation
- Must include full URL with fragment (contains encryption key)
- Visual feedback: "Copied!" tooltip or button text change
- Button text: "Copy Link" → "Copied!" → "Copy Link" (after 2s)
2. Individual File Copying
- Copy button for each file in multi-file gists
- Appears on hover or always visible on mobile
- Copies only the content of that specific file
- Include filename in toast notification: "Copied file.js"
3. Full Gist Copying
- Optional: Copy all files as concatenated text
- Format:
// filename.js\n[content]\n\n// nextfile.js\n[content]
- Use case: Quick paste into single file
Visual Feedback
- Success: Green check icon + "Copied!" message
- Error: Red X icon + "Failed to copy" message
- Use toast notifications or inline feedback
- Animation: Subtle scale or fade effect
Implementation Details
// Secure copy function
async function secureCopy(text: string, label?: string) {
try {
await navigator.clipboard.writeText(text);
// Show success feedback
showToast({
title: "Copied\!",
description: label ? `Copied ${label}` : undefined,
variant: "success"
});
} catch (err) {
// Fallback for older browsers
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand("copy");
showToast({ title: "Copied\!", variant: "success" });
} catch {
showToast({ title: "Failed to copy", variant: "error" });
} finally {
document.body.removeChild(textArea);
}
}
// Clear from memory
text = "";
}
Acceptance Criteria
- Copy share link includes full URL with hash fragment
- Individual file copy buttons work for each file
- NO console.log or logging of copied content
- Visual feedback shows success/failure
- Fallback works for browsers without clipboard API
- Mobile touch targets are appropriately sized (44x44px min)
- Keyboard shortcut (Cmd/Ctrl+C) works when content selected
- Toast notifications don't block user interaction
- Memory is cleared after copy operation
UI/UX Guidelines
- Copy buttons use consistent icon (e.g., clipboard icon from Lucide)
- Hover state shows tooltip "Copy to clipboard"
- Success state changes icon to checkmark temporarily
- Error state explains why copy failed
- Mobile: Consider long-press to copy as alternative
Technical Notes
- Use navigator.clipboard API as primary method
- Implement textarea fallback for older browsers
- Test Safari iOS clipboard permissions
- Consider rate limiting to prevent spam (max 10 copies per minute)
- Use React hooks for copy state management
- Integrate with existing toast system from feat: add toast notification system #68
Related
- Part of Phase 4 UI Components
- Enhances user experience in viewer (feat: create loading state components #67)
- Works with CodeEditor component (feat: implement PIN authentication with PBKDF2-SHA256 #46)
- Uses toast notifications (feat: add toast notification system #68)