> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/SudhansuuRanjan/clipsync/llms.txt
> Use this file to discover all available pages before exploring further.

# Clipboard Synchronization

> Real-time clipboard content synchronization across devices using Supabase realtime subscriptions.

## Overview

ClipSync provides real-time clipboard synchronization across all devices in a session. Content sent from any device instantly appears on all other connected devices using Supabase's realtime database subscriptions.

## Real-Time Synchronization

### How It Works

ClipSync uses Supabase's PostgreSQL realtime capabilities to instantly sync clipboard updates:

```javascript src/App.jsx:385-410 theme={null}
useEffect(() => {
    if (!sessionCode) return;
    const channel = supabase
        .channel("clipboard")
        .on("postgres_changes", { 
            event: "*", 
            schema: "public", 
            table: "clipboard" 
        }, (payload) => {
            if (payload.new.session_code === sessionCode && payload.eventType === "INSERT") {
                setHistory((prev) => [payload.new, ...prev]);
                setClipboard("");
            }

            if (payload.eventType === "DELETE") {
                if (deleteOne) {
                    setHistory([]);
                } else {
                    setHistory((prev) => prev.filter((item) => item.id !== payload.old.id));
                }
            }
        })
        .subscribe();

    return () => {
        supabase.removeChannel(channel);
    };
}, [sessionCode, isOffline]);
```

<Info>
  The realtime subscription only listens for updates matching your current session code, ensuring privacy between sessions.
</Info>

## Sending Content to Clipboard

### Text Content

To send text content:

1. Type or paste content into the textarea
2. Optionally mark content as sensitive
3. Click "Send to Clipboard"

```javascript src/App.jsx:167-205 theme={null}
const updateClipboard = async () => {
    // if fileURL exists, then it is a file so text can be empty
    if (!clipboard && !fileUrl) return toast.error("Please enter some text to update clipboard");
    if (clipboard.length > 15000) return toast.error("Clipboard content is too long. Please keep it under 15000 characters.");

    let firstTime = false;

    if (!sessionCode) {
        await createSession(setSessionCode);
        firstTime = true;
    }
    const code = localStorage.getItem("sessionCode");
    await supabase.from("clipboard").insert([{
        session_code: code,
        content: clipboard,
        fileUrl: fileUrl ? fileUrl.url : null,
        file: fileUrl ? fileUrl : null,
        sensitive: isSensitive,
    }]);

    if (history.length == 0 && firstTime) {
        // Manually fetch latest history to update UI immediately
        const { data, error: fetchError } = await supabase
            .from("clipboard")
            .select("*")
            .eq("session_code", code)
            .order("created_at", { ascending: false });

        if (!fetchError) {
            setHistory(data);
        }
    }

    setFileUrl(null);
    setClipboard("");
    sessionStorage.removeItem("clipboard");
    setIsSensitive(false);
    toast.success("Clipboard updated successfully!");
};
```

### Paste from System Clipboard

ClipSync can read directly from your system clipboard:

```javascript src/App.jsx:212-223 theme={null}
const addClipboardText = () => {
    navigator.clipboard.readText().then((text) => {
        if (text.trim()) {
            setClipboard(text);
            toast.success("Clipboard text pasted successfully!");
        } else {
            alert("Clipboard is empty or contains unsupported data.");
        }
    }).catch(() => {
        alert("An error occurred while reading clipboard");
    });
};
```

<Note>
  Your browser may prompt for clipboard access permission the first time you use this feature.
</Note>

## Character Limits

ClipSync enforces a **15,000 character limit** per clipboard entry to ensure performance and database efficiency.

From App.jsx:170:

```javascript src/App.jsx:170 theme={null}
if (clipboard.length > 15000) 
    return toast.error("Clipboard content is too long. Please keep it under 15000 characters.");
```

<Warning>
  Content exceeding 15,000 characters will be rejected. Consider splitting large content into multiple entries or using file sharing for longer text.
</Warning>

## Copying from History

Each clipboard entry in the history can be copied back to your system clipboard with one click:

```javascript src/App.jsx:207-210 theme={null}
const copyToClipboard = (content) => {
    navigator.clipboard.writeText(content);
    toast.success("Text copied to clipboard!");
};
```

The copy button is available for each history item:

```javascript src/App.jsx:679 theme={null}
<button 
    aria-label="Copy Content" 
    className="text-blue-500 active:text-blue-700 active:scale-95" 
    onClick={() => copyToClipboard(item.content)}>
    <Copy size={19} />
</button>
```

## Sensitive Content Masking

ClipSync includes a sensitive content feature to protect private information when sharing your screen or device.

### How It Works

1. Check the "Sensitive" checkbox before sending content
2. The content is stored normally in the database
3. When displayed in the history, it's replaced with asterisks

### Implementation

From utils/index.js:1-11:

```javascript src/utils/index.js:1-11 theme={null}
export const convertLinksToAnchor = (text, item) => {
    if(item.sensitive) {
        // replace it with ******
        return "**********************";
    }
    const urlRegex = /(https?:\/\/[^\s]+|www\.[^\s]+)/g;
    return text.replace(urlRegex, (url) => {
        let hyperlink = url.startsWith("www.") ? `https://${url}` : url;
        return `<a href="${hyperlink}" target="_blank" rel="noopener noreferrer" class="text-blue-500 underline">${url}</a>`;
    });
};
```

UI checkbox from App.jsx:531-536:

```javascript src/App.jsx:531-536 theme={null}
<div className="flex absolute items-center gap-1 w-fit right-3 bottom-3 z-10">
    <label htmlFor="is-sensitive">Sensitive</label>
    <input
        checked={isSensitive}
        onChange={(e) => setIsSensitive(e.target.checked)}
        type="checkbox" id="is-sensitive" className="ml-2" />
</div>
```

<Tip>
  Use sensitive mode for passwords, API keys, personal information, or any content you want to keep private from onlookers.
</Tip>

## Automatic Link Detection

Clipboard content is automatically scanned for URLs, which are converted to clickable links:

```javascript src/App.jsx:650-656 theme={null}
<p
    onClick={() => toggleExpand(item.id)}
    className={`text-sm flex-1 word-wrap link-wrap cursor-pointer truncate text-wrap w-fit`}
    dangerouslySetInnerHTML={{
        __html: expandedId === item.id
            ? convertLinksToAnchor(item.content, item)
            : item.content.length > 180
                ? convertLinksToAnchor(item.content.substring(0, 180), item) + "..."
                : convertLinksToAnchor(item.content.substring(0, 180), item)
    }}
></p>
```

## Clipboard History

### Viewing History

All clipboard entries are stored and displayed in chronological order (newest first):

```javascript src/App.jsx:31-38 theme={null}
const fetchClipboardHistory = async () => {
    if (!sessionCode) return;
    let { data, error } = await supabase
        .from("clipboard")
        .select("*")
        .eq("session_code", sessionCode);
    if (!error) setClipboard(data || []);
};
```

### Searching History

ClipSync includes a search feature to filter clipboard history:

```javascript src/App.jsx:412-420 theme={null}
const handleSearch = (e) => {
    setSearchKeyword(e.target.value);
    if (e.target.value.trim() === "") {
        setSearchResults(history);
        return;
    }
    const results = history.filter((item) => 
        item.content.toLowerCase().includes(e.target.value.toLowerCase()));
    setSearchResults(results);
}
```

### Editing History Items

You can edit previously sent content:

```javascript src/App.jsx:249-266 theme={null}
const handleEdit = async (id) => {
    setDeleteOne(true);
    // fetch item from database
    const content = history.find((item) => item.id === id).content;
    setClipboard(content);
    // delete item from database
    await supabase.from("clipboard").delete().eq("id", id);

    // set file if exists
    setFileUrl(history.find((item) => item.id === id).file);

    // delete item from history
    const newHistory = history.filter((item) => item.id !== id);
    setHistory(newHistory);
    setDeleteOne(false);

    toast.success("Clipboard content added to editor!");
}
```

<Info>
  Editing removes the original entry and loads it back into the editor. You'll need to send it again after making changes.
</Info>

## Clearing History

Delete all clipboard entries from your session:

```javascript src/App.jsx:225-243 theme={null}
const deleteAll = async () => {
    const response = confirm("Are you sure you want to clear clipboards?");
    if (!response) return;
    if (history.length == 0) return toast.error("No items in your clipboard history");
    
    // delete all items from database with session code if file exists delete from storage
    history.forEach(async (item) => {
        if (item.file) {
            await supabase.storage.from("clipboard").remove([item.file.name]);
        }
    });

    const { error } = await supabase.from("clipboard").delete().eq("session_code", sessionCode);
    if (error) {
        toast.error("An error occurred while deleting clipboard history");
        return;
    }
    setHistory([]);
    toast.success("Clipboard history deleted successfully!");
};
```

<Warning>
  Deleting history is permanent and affects all devices in the session. Associated files are also removed from storage.
</Warning>
