├── logo.png ├── manifest.json ├── README.md ├── style.css └── content.js /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dhananjay-JSR/deep-sync/HEAD/logo.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DeepSync", 3 | "description": "DeepSync allows Users to share their deepseek chat session with others", 4 | "author": { 5 | "email": "hello@dhananjaay.dev" 6 | }, 7 | "homepage_url": "https://dhananjaay.dev", 8 | "version": "1.1", 9 | "manifest_version": 3, 10 | "permissions": ["activeTab", "scripting"], 11 | "host_permissions": ["https://chat.deepseek.com/*"], 12 | "content_scripts": [ 13 | { 14 | "matches": ["https://chat.deepseek.com/*"], 15 | "js": ["content.js"], 16 | "css": ["style.css"] 17 | } 18 | ], 19 | "action": { 20 | "default_icon": "logo.png" 21 | } 22 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Deepseek Chat Share Extension 5 | 6 | This is a browser extension that enhances the Deepseek web UI by allowing users to share their chat conversations publicly, privately with a password, or with an expiration link. The extension adds a "Share" button to the Deepseek interface, enabling users to generate shareable links with various privacy settings. 7 | 8 | ![image](https://github.com/user-attachments/assets/5f88eceb-d82d-44e2-9a2d-a4ba3acdeb28) 9 | 10 | ## Features 11 | 12 | - **Public Sharing**: Generate a public link that anyone can access to view the chat. 13 | - **Private Sharing with Password**: Create a private link that requires a password to access the chat. 14 | - **Expiration Link**: Generate a link that expires after a specified time, ensuring the chat is only accessible for a limited duration. 15 | - **Easy Integration**: Simply install the extension, and the "Share" button will appear in the Deepseek web UI. 16 | 17 | ## Installation 18 | 19 | ### Chrome 20 | 21 | 1. Download the extension files from the [Releases](https://github.com/Dhananjay-JSR/deep-sync/releases) page. 22 | 2. Extract the ZIP file to a folder on your computer. 23 | 3. Open Chrome and go to `chrome://extensions/`. 24 | 4. Enable "Developer mode" in the top right corner. 25 | 5. Click "Load unpacked" and select the folder where you extracted the extension files. 26 | 6. The extension should now be installed and ready to use. 27 | 28 | [Dhananjay Senday](https://dhananjaay.dev/) 29 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | .settings-overlay { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background: rgba(0, 0, 0, 0.5); 8 | display: flex; 9 | justify-content: center; 10 | align-items: center; 11 | z-index: 1002; 12 | font-family: 'Arial', sans-serif; 13 | } 14 | 15 | .settings-overlay-content { 16 | background: #3A3A46; 17 | padding: 30px; 18 | border-radius: 12px; 19 | text-align: left; 20 | max-width: 450px; 21 | width: 100%; 22 | box-shadow: 0 10px 25px rgba(0,0,0,0.1); 23 | position: relative; 24 | color: #F8FAFF; 25 | } 26 | 27 | .settings-overlay-content h2 { 28 | margin-bottom: 20px; 29 | font-size: 24px; 30 | color: #F8FAFF; 31 | } 32 | 33 | .settings-option { 34 | margin-bottom: 20px; 35 | } 36 | 37 | .settings-option label { 38 | display: block; 39 | margin-bottom: 10px; 40 | font-size: 16px; 41 | color: #F8FAFF; 42 | } 43 | 44 | .settings-option input[type="radio"], 45 | .settings-option input[type="checkbox"] { 46 | margin-right: 10px; 47 | } 48 | 49 | .password-field { 50 | display: flex; 51 | gap: 10px; 52 | margin-top: 10px; 53 | } 54 | 55 | .password-field input { 56 | flex: 1; 57 | padding: 10px; 58 | border: 1px solid #555562; 59 | border-radius: 6px; 60 | font-size: 14px; 61 | background: #555562; 62 | color: #F8FAFF; 63 | } 64 | 65 | .password-field .show-password-button { 66 | padding: 10px 15px; 67 | border: none; 68 | border-radius: 6px; 69 | background: #555562; 70 | color: #F8FAFF; 71 | cursor: pointer; 72 | font-size: 14px; 73 | transition: background 0.3s ease; 74 | } 75 | 76 | .password-field .show-password-button:hover { 77 | background: #6c757d; 78 | } 79 | 80 | .expiry-fields { 81 | margin-top: 10px; 82 | } 83 | 84 | .expiry-fields input { 85 | width: 100%; 86 | padding: 10px; 87 | border: 1px solid #555562; 88 | border-radius: 6px; 89 | font-size: 14px; 90 | background: #555562; 91 | color: #F8FAFF; 92 | margin-bottom: 10px; 93 | } 94 | 95 | .settings-buttons { 96 | display: flex; 97 | justify-content: flex-end; 98 | margin-top: 20px; 99 | } 100 | 101 | .settings-buttons button { 102 | margin-left: 10px; 103 | padding: 12px 24px; 104 | border: none; 105 | border-radius: 6px; 106 | cursor: pointer; 107 | font-size: 16px; 108 | transition: all 0.3s ease; 109 | } 110 | 111 | .settings-buttons .create-button { 112 | background: #007bff; 113 | color: #F8FAFF; 114 | font-weight: bold; 115 | border: 2px solid #007bff; 116 | transition: background 0.3s ease, transform 0.2s ease; 117 | } 118 | 119 | .settings-buttons .create-button:hover { 120 | background: #0056b3; 121 | border-color: #0056b3; 122 | transform: scale(1.05); 123 | } 124 | 125 | .settings-buttons .create-button:active { 126 | background: #004080; 127 | border-color: #004080; 128 | transform: scale(0.95); 129 | } 130 | 131 | .settings-buttons .cancel-button { 132 | background: #555562; 133 | color: #F8FAFF; 134 | } 135 | 136 | .settings-buttons .cancel-button:hover { 137 | background: #6c757d; 138 | } 139 | 140 | 141 | .loader { 142 | border: 3px solid #F8FAFF; 143 | border-top: 3px solid #555562; 144 | border-radius: 50%; 145 | width: 16px; 146 | height: 16px; 147 | animation: spin 1s linear infinite; 148 | display: inline-block; 149 | margin-right: 8px; 150 | } 151 | 152 | @keyframes spin { 153 | 0% { transform: rotate(0deg); } 154 | 100% { transform: rotate(360deg); } 155 | } 156 | 157 | 158 | .url-container { 159 | display: flex; 160 | align-items: center; 161 | gap: 10px; 162 | margin-bottom: 20px; 163 | } 164 | 165 | .url-text { 166 | flex: 1; 167 | text-overflow: ellipsis; 168 | overflow: hidden; 169 | white-space: nowrap; 170 | color: #F8FAFF; 171 | } 172 | 173 | .copy-url-button { 174 | padding: 10px 15px; 175 | border: none; 176 | border-radius: 6px; 177 | background: #555562; 178 | color: #F8FAFF; 179 | cursor: pointer; 180 | font-size: 14px; 181 | transition: background 0.3s ease; 182 | } 183 | 184 | .copy-url-button:hover { 185 | background: #6c757d; 186 | } 187 | 188 | .close-button { 189 | padding: 10px 15px; 190 | border: none; 191 | border-radius: 6px; 192 | background: #555562; 193 | color: #F8FAFF; 194 | cursor: pointer; 195 | font-size: 14px; 196 | transition: background 0.3s ease; 197 | } 198 | 199 | .close-button:hover { 200 | background: #6c757d; 201 | } -------------------------------------------------------------------------------- /content.js: -------------------------------------------------------------------------------- 1 | let contextMenuTriggerButton = null; 2 | const SERVER_URL = `https://chat.jaay.fun`; 3 | 4 | function isChatHistoryLoaded() { 5 | const chatHistoryContainer = document.querySelector("._03210fb"); 6 | return chatHistoryContainer !== null; 7 | } 8 | 9 | function isDropdownMenuOpen() { 10 | const dropdownMenu = document.querySelector('.ds-dropdown-menu[role="menu"]'); 11 | return dropdownMenu && dropdownMenu.children.length > 0; 12 | } 13 | 14 | function removeDropdownChildren() { 15 | const dropdownMenu = document.querySelector('.ds-dropdown-menu[role="menu"]'); 16 | if (dropdownMenu) { 17 | while (dropdownMenu.firstChild) { 18 | dropdownMenu.removeChild(dropdownMenu.firstChild); 19 | } 20 | } 21 | } 22 | 23 | function showSecondOverlay(url, isPasswordProtected = false) { 24 | const overlay = document.createElement("div"); 25 | overlay.className = "second-overlay"; 26 | overlay.innerHTML = ` 27 |
28 |
29 | ${url} 30 |
31 |
32 |
33 | 34 | 35 |
36 |
37 | `; 38 | document.body.appendChild(overlay); 39 | 40 | const copyButton = overlay.querySelector(".copy-button"); 41 | copyButton.addEventListener("click", () => { 42 | navigator.clipboard.writeText(url).then(() => { 43 | alert("URL copied to clipboard!"); 44 | overlay.remove(); 45 | }); 46 | }); 47 | 48 | const closeButton = overlay.querySelector(".close-button"); 49 | closeButton.addEventListener("click", () => { 50 | overlay.remove(); 51 | }); 52 | } 53 | function showSettingsOverlay() { 54 | const overlay = document.createElement("div"); 55 | overlay.className = "settings-overlay"; 56 | overlay.innerHTML = ` 57 |
58 |

Share Settings

59 |
60 | 63 |
64 |
65 | 68 | 72 |
73 |
74 | 77 | 82 |
83 |
84 | 85 | 86 |
87 |
88 | `; 89 | document.body.appendChild(overlay); 90 | 91 | const linkTypeRadios = overlay.querySelectorAll('input[name="link-type"]'); 92 | const passwordField = overlay.querySelector('.password-field'); 93 | const expiryCheckbox = overlay.querySelector('#expiry-checkbox'); 94 | const expiryFields = overlay.querySelector('.expiry-fields'); 95 | const passwordInput = overlay.querySelector('#password-input'); 96 | const showPasswordButton = overlay.querySelector('.show-password-button'); 97 | const createButton = overlay.querySelector('.create-button'); 98 | 99 | linkTypeRadios.forEach(radio => { 100 | radio.addEventListener('change', () => { 101 | if (radio.value === 'private') { 102 | passwordField.style.display = 'flex'; 103 | } else { 104 | passwordField.style.display = 'none'; 105 | } 106 | }); 107 | }); 108 | 109 | expiryCheckbox.addEventListener('change', () => { 110 | if (expiryCheckbox.checked) { 111 | expiryFields.style.display = 'block'; 112 | } else { 113 | expiryFields.style.display = 'none'; 114 | } 115 | }); 116 | 117 | showPasswordButton.addEventListener('click', () => { 118 | if (passwordInput.type === 'password') { 119 | passwordInput.type = 'text'; 120 | showPasswordButton.textContent = 'Hide'; 121 | } else { 122 | passwordInput.type = 'password'; 123 | showPasswordButton.textContent = 'Show'; 124 | } 125 | }); 126 | 127 | createButton.addEventListener('click', async () => { 128 | createButton.disabled = true; 129 | createButton.innerHTML = `
Creating...`; 130 | 131 | const isPrivateLink = overlay.querySelector('input[name="link-type"]:checked').value === 'private'; 132 | const password = isPrivateLink ? overlay.querySelector('#password-input').value : null; 133 | const expiry = expiryCheckbox.checked ? { 134 | days: parseInt(overlay.querySelector('#days').value) || 0, 135 | hours: parseInt(overlay.querySelector('#hours').value) || 0, 136 | minutes: parseInt(overlay.querySelector('#minutes').value) || 0 137 | } : null; 138 | 139 | const buttonName = contextMenuTriggerButton.querySelector(".c08e6e93").innerText; 140 | // deepseek doesn't store chat id in html so had to found my own way to get chat id 141 | // :( if it works it works !!! 142 | const token = localStorage.getItem("userToken"); 143 | const parsedToken = JSON.parse(token); 144 | 145 | try { 146 | const data = await fetch("https://chat.deepseek.com/api/v0/chat_session/fetch_page?count=500", { 147 | method: "GET", 148 | credentials: "include", 149 | headers: { 150 | "Authorization": `Bearer ${parsedToken.value}` 151 | } 152 | }); 153 | 154 | const body = await data.json(); 155 | const chat_sessions = body.data.biz_data.chat_sessions; 156 | let found_id = null; 157 | 158 | for (let i = 0; i < chat_sessions.length; i++) { 159 | const title = chat_sessions[i].title; 160 | if (title === buttonName) { 161 | found_id = chat_sessions[i].id; 162 | break; 163 | } 164 | } 165 | 166 | if (found_id) { 167 | const url = `https://chat.deepseek.com/api/v0/chat/history_messages?chat_session_id=${found_id}`; 168 | const data = await fetch(url, { 169 | method: "GET", 170 | credentials: "include", 171 | headers: { 172 | "Authorization": `Bearer ${parsedToken.value}` 173 | } 174 | }); 175 | 176 | const body = await data.json(); 177 | const messages = body.data.biz_data; 178 | 179 | const upload_data = await fetch(SERVER_URL+"/api", { 180 | method: "POST", 181 | headers: { 182 | "Content-Type": "application/json" 183 | }, 184 | body: JSON.stringify({ 185 | messages, 186 | settings: { 187 | isPrivateLink, 188 | password, 189 | expiry 190 | } 191 | }) 192 | }); 193 | 194 | const response = await upload_data.json(); 195 | const id = response.id; 196 | const shareableUrl = `${SERVER_URL}/${id}`; 197 | 198 | overlay.querySelector('.settings-overlay-content').innerHTML = ` 199 |

Shareable Link

200 |
201 | ${shareableUrl} 202 | 203 |
204 | 205 | `; 206 | 207 | const copyUrlButton = overlay.querySelector('.copy-url-button'); 208 | copyUrlButton.addEventListener('click', () => { 209 | navigator.clipboard.writeText(shareableUrl).then(() => { 210 | alert("URL copied to clipboard!"); 211 | }); 212 | }); 213 | 214 | const closeButton = overlay.querySelector('.close-button'); 215 | closeButton.addEventListener('click', () => { 216 | overlay.remove(); 217 | }); 218 | } 219 | } catch (error) { 220 | console.error("Error:", error); 221 | alert("An error occurred. Please try again."); 222 | createButton.disabled = false; 223 | createButton.textContent = "Create"; 224 | } 225 | }); 226 | 227 | const cancelButton = overlay.querySelector('.cancel-button'); 228 | cancelButton.addEventListener('click', () => { 229 | overlay.remove(); 230 | }); 231 | } 232 | 233 | function trackContextMenuTrigger(event) { 234 | const button = event.target.closest("._83421f9"); 235 | if (button) { 236 | const parentContainer = button.closest("._83421f9"); 237 | if (parentContainer) { 238 | const buttonText = parentContainer.querySelector(".c08e6e93").innerText; 239 | contextMenuTriggerButton = parentContainer; 240 | } 241 | } 242 | } 243 | 244 | function showDimmerOverlay() { 245 | const overlay = document.createElement("div"); 246 | overlay.className = "dimmer-overlay"; 247 | overlay.innerHTML = ` 248 |
Loading...
249 | `; 250 | document.body.appendChild(overlay); 251 | } 252 | 253 | function hideDimmerOverlay() { 254 | const overlay = document.querySelector(".dimmer-overlay"); 255 | if (overlay) { 256 | overlay.remove(); 257 | } 258 | } 259 | 260 | function injectShareButton() { 261 | const dropdownMenu = document.querySelector('.ds-dropdown-menu[role="menu"]'); 262 | if (dropdownMenu && !dropdownMenu.querySelector(".ds-dropdown-menu-option__label[data-action='share']")) { 263 | const dropdownItems = dropdownMenu.children; 264 | 265 | if (dropdownItems.length >= 2) { 266 | const shareButton = document.createElement("div"); 267 | shareButton.className = "ds-dropdown-menu-option ds-dropdown-menu-option--none"; 268 | shareButton.style.cursor = "pointer"; 269 | 270 | const iconContainer = document.createElement("div"); 271 | iconContainer.className = "ds-dropdown-menu-option__icon"; 272 | 273 | const iconSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); 274 | iconSvg.setAttribute("width", "24"); 275 | iconSvg.setAttribute("height", "24"); 276 | iconSvg.setAttribute("viewBox", "0 0 24 24"); 277 | iconSvg.setAttribute("fill", "none"); 278 | iconSvg.innerHTML = ` 279 | 280 | `; 281 | 282 | iconContainer.appendChild(iconSvg); 283 | 284 | const labelContainer = document.createElement("div"); 285 | labelContainer.className = "ds-dropdown-menu-option__label"; 286 | labelContainer.setAttribute("data-action", "share"); 287 | labelContainer.innerText = "Share"; 288 | 289 | shareButton.appendChild(iconContainer); 290 | shareButton.appendChild(labelContainer); 291 | 292 | shareButton.addEventListener("click", () => { 293 | if (contextMenuTriggerButton) { 294 | removeDropdownChildren(); 295 | showSettingsOverlay(); 296 | } else { 297 | console.error("No context menu trigger button found ERR!!!!"); 298 | } 299 | }); 300 | 301 | dropdownMenu.insertBefore(shareButton, dropdownItems[1]); 302 | } 303 | } 304 | } 305 | 306 | function observeDOM() { 307 | const observer = new MutationObserver((mutationsList) => { 308 | for (const mutation of mutationsList) { 309 | if (mutation.type === "childList") { 310 | if (isChatHistoryLoaded()) { 311 | document.querySelectorAll("._2090548").forEach((button) => { 312 | button.addEventListener("click", trackContextMenuTrigger); 313 | }); 314 | } 315 | 316 | if (isDropdownMenuOpen() && isChatHistoryLoaded()) { 317 | injectShareButton(); 318 | } 319 | } 320 | } 321 | }); 322 | observer.observe(document.body, { childList: true, subtree: true }); 323 | } 324 | 325 | observeDOM(); --------------------------------------------------------------------------------