The Clipboard Problem That Drove Me to Build My Own Solution

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • MyrinNew
    Senior Member
    • Feb 2024
    • 5175

    #1

    The Clipboard Problem That Drove Me to Build My Own Solution

    How losing the same code snippet 50 times led me down a rabbit hole

    I lost my clipboard again. I'd copied a complex SQL query, switched to another tab, copied something else, and the query was gone. I had to rebuild it from memory.


    This kept happening. As a remote worker constantly switching between my laptop and desktop, I'd copy something on one device, switch to the other, and it was gone. I tried a few clipboard managers, but they didn't fit my workflow.


    So I built my own. Here's what I learned, the challenges I hit, and what I'd do differently.





    The Problem (Or: Why I Got Frustrated)

    The Daily Frustration

    • Copy a code snippet on my laptop, switch to desktop, and it's gone
    • Copy an email template, switch tabs, copy something else, and lose the template
    • Scroll through 50 clipboard items to find the one I use daily


    Why Existing Solutions Didn't Work for Me

    I tried several tools:

    1. Built-in OS clipboard managers - Don't sync across devices
    2. Cloud clipboard tools - Too slow, clunky, or required too many steps
    3. Browser extensions - Saved everything but didn't prioritize what I actually used


    Most extensions just saved history. I'd have to scroll through dozens of items to find the email signature I paste daily. That defeated the purpose.





    What I Built

    I built a browser extension that:
    • Syncs clipboard history across Chrome and Edge browsers
    • Learns what I paste most and prioritizes those items
    • Provides a right-click context menu for quick access
    • Works on complex sites like Google Docs and Reddit


    The key difference: it tracks what I actually paste, not what I click. Items I paste frequently automatically move to the top.





    The Technical Challenges

    Clipboard API Limitations

    The Clipboard API has quirks:






    // This works in some contexts, not others
    navigator.clipboard.readText().then(text => {
    // But what if the page doesn't have focus?
    // What if it's a content script?
    });







    Issues:
    • Requires HTTPS or localhost
    • Content scripts can't always access clipboard directly
    • Permission handling varies by context


    Solution: Message passing between content scripts and background service worker, with fallbacks.


    Manifest V3 Gotchas

    Service workers can be killed anytime:






    // This state gets lost when the service worker dies
    let clipboardHistory = [];

    // Had to persist everything
    chrome.storage.local.set({ clipboardHistory });







    Challenges:
    • Service worker lifecycle is unpredictable
    • Content script injection timing issues
    • CSP restrictions block some approaches


    Solution: Persist state in chrome.storage.local and handle re-initialization.


    Paste Functionality on Complex Sites

    Pasting into Google Docs, Reddit, or Notion is tricky:






    // Method 1: Modern approach (doesn't always work)
    element.dispatchEvent(new ClipboardEvent('paste'));

    // Method 2: Deprecated but sometimes the only option
    document.execCommand('paste');

    // Method 3: Direct DOM manipulation (works on simple sites)
    element.textContent = clipboardText;







    I ended up trying multiple methods in sequence:






    async function pasteText(element, text) {
    // Try modern approach first
    try {
    element.focus();
    await navigator.clipboard.writeText(text);
    element.dispatchEvent(new ClipboardEvent('paste'));
    return true;
    } catch (e) {
    // Fallback to execCommand
    try {
    element.focus();
    document.execCommand('insertText', false, text);
    return true;
    } catch (e2) {
    // Last resort: direct manipulation
    element.textContent = text;
    return true;
    }
    }
    }







    Real-Time Sync Without Constant Polling

    I wanted changes to appear instantly across devices without polling:






    // Polling approach (what I wanted to avoid)
    setInterval(async () => {
    const latest = await fetchLatestClipboard();
    // Check for changes...
    }, 5000); // Too slow, or too resource-intensive







    Solution: Real-time subscriptions (using Supabase in my case):






    const subscription = supabase
    .channel('clipboard-changes')
    .on('postgres_changes', {
    event: 'INSERT',
    schema: 'public',
    table: 'clipboard_items'
    }, (payload) => {
    // Handle new item instantly
    updateLocalClipboard(payload.new);
    })
    .subscribe();







    Challenges:
    • Handling connection drops
    • Resolving conflicts when same item updated on multiple devices
    • Managing subscription lifecycle


    Cross-Browser Compatibility

    Chrome, Firefox, and Edge have differences:






    // Chrome uses chrome.*
    chrome.storage.local.get('key', callback);

    // Firefox uses browser.*
    browser.storage.local.get('key').then(callback);

    // Edge uses chrome.* but behaves slightly differently







    Solution: Abstraction layer:






    const browserAPI = {
    storage: {
    local: {
    get: (key) => {
    if (typeof chrome !== 'undefined' && chrome.storage) {
    return new Promise(resolve => {
    chrome.storage.local.get(key, resolve);
    });
    } else if (typeof browser !== 'undefined') {
    return browser.storage.local.get(key);
    }
    },
    // ... similar for set, remove, etc.
    }
    }
    };







    Smart Prioritization

    I wanted frequently used items to surface automatically. This required:
    • Tracking usage when items are actually pasted
    • Syncing usage data across devices
    • Efficient sorting and filtering


    The implementation involves tracking paste events and maintaining usage statistics, but the core concept is prioritizing items based on actual usage patterns rather than just recency.





    What I Learned

    1. Test on Real Sites Early

    Don't assume paste works everywhere. Test on Google Docs, Reddit, Notion, CodePen, etc.


    2. Service Worker State is Ephemeral

    Persist everything. Assume the service worker can die at any moment.


    3. Real-Time Sync is Harder Than It Looks

    Connection drops, conflicts, edge cases - it's more complex than it seems. Start simple and iterate.


    4. User Behavior is Unpredictable

    Track what users actually do, not what you think they do. I track pastes, not clicks.


    5. Cross-Browser Testing is Essential

    Chrome, Firefox, and Edge behave differently. Test all of them.





    What I'd Do Differently

    1. Start with simpler MVP - Local-only first, then add sync
    2. Better conflict resolution - Should have planned this earlier
    3. More error handling - Add retry logic from the start
    4. Better testing - Should have built testing infrastructure earlier
    5. Get user feedback sooner - Built in isolation too long





    Open Questions

    I'm still figuring out:

    1. How do you handle clipboard permissions in extensions reliably?
    2. Better approaches for paste compatibility across complex sites?
    3. How would you architect real-time sync for this use case?
    4. Manifest V3 service worker lifecycle - best practices?
    5. How do you test extensions across browsers efficiently?


    If you've built something similar or have ideas, I'd love to hear them.





    The Result

    After a few months of using it, it's working well. I rarely lose clipboard content, and frequently used items appear quickly. It's not perfect, but it fits my workflow.


    If you want to try it, it's available for Chrome and Edge. More importantly, I'd love to hear:
    • How you've solved similar problems
    • What challenges you've faced with browser extensions
    • What features would make this genuinely useful


    This is still a work in progress, and I'm learning as I go.





    Author: Anurag G.

    LinkedIn: https://www.linkedin.com/in/anu95




    More...
Working...