├── .gitignore ├── src ├── preview.png ├── preview-2.png ├── preview-3.png └── preview-4.png ├── Dockerfile ├── app.js ├── package.json ├── LICENSE ├── README.md ├── public ├── js │ └── main.js └── css │ └── style.css ├── routes └── reset.js └── views └── index.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | .dist 4 | -------------------------------------------------------------------------------- /src/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SazumiVicky/cursor-reset-tools/HEAD/src/preview.png -------------------------------------------------------------------------------- /src/preview-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SazumiVicky/cursor-reset-tools/HEAD/src/preview-2.png -------------------------------------------------------------------------------- /src/preview-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SazumiVicky/cursor-reset-tools/HEAD/src/preview-3.png -------------------------------------------------------------------------------- /src/preview-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SazumiVicky/cursor-reset-tools/HEAD/src/preview-4.png -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | WORKDIR /app 3 | COPY package*.json ./ 4 | RUN npm install 5 | RUN apt-get update && apt-get install 6 | COPY . . 7 | EXPOSE 3000 8 | CMD ["node", "app.js"] -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | import cors from 'cors'; 2 | import express from 'express'; 3 | import path from 'path'; 4 | import { fileURLToPath } from 'url'; 5 | import resetRouter from './routes/reset.js'; 6 | 7 | const app = express(); 8 | const port = process.env.PORT || 3000; 9 | const __filename = fileURLToPath(import.meta.url); 10 | const __dirname = path.dirname(__filename); 11 | 12 | app.use(cors()); 13 | app.use(express.json()); 14 | app.use(express.urlencoded({ extended: true })); 15 | app.use(express.static(path.join(__dirname, 'public'))); 16 | 17 | app.set('view engine', 'ejs'); 18 | app.set('views', path.join(__dirname, 'views')); 19 | 20 | app.get('/', (req, res) => { 21 | res.render('index'); 22 | }); 23 | 24 | app.use('/api', resetRouter); 25 | 26 | app.listen(port, () => { 27 | console.log(`Server running on http://localhost:${port}`); 28 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cursor-reset-tools", 3 | "version": "2.0.0", 4 | "description": "this is a tool to reset the machine id of cursor", 5 | "main": "app.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node app.js", 9 | "dev": "nodemon app.js" 10 | }, 11 | "dependencies": { 12 | "cors": "^2.8.5", 13 | "ejs": "^3.1.9", 14 | "express": "^4.18.2", 15 | "fs-extra": "^11.1.1", 16 | "get-uri": "^6.0.4", 17 | "node-fetch": "^3.3.2", 18 | "node-machine-id": "^1.1.12", 19 | "os": "^0.1.2", 20 | "path": "^0.12.7", 21 | "sqlite": "^5.1.1", 22 | "sqlite3": "^5.1.7", 23 | "user-agents": "^1.1.2", 24 | "uuid": "^9.0.0" 25 | }, 26 | "devDependencies": { 27 | "nodemon": "^2.0.22" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Sazumi Viki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🛡️ Cursor Reset Tools 2 | > *"Oh, you've banned my country from using Cursor Student? How cute. Let me introduce you to something better."* 3 | 4 | ## Preview Website 1 5 | 6 | ![Sazumi Cloud Cursor Reset Tool Preview1](./src/preview.png) 7 | 8 | ## Preview Website 2 9 | 10 | ![Sazumi Cloud Cursor Reset Tool Preview2](./src/preview-3.png) 11 | 12 | ## Preview Pro Feature 13 | 14 | ![Sazumi Cloud Cursor Reset Tool Preview4](./src/preview-2.png) 15 | 16 | Forget Cursor Student restrictions and country bans! This revolutionary web-based tool laughs in the face of Cursor IDE's arbitrary limitations, giving power back to developers from every corner of the globe. Who needs their "student program" when you can reset Machine IDs, bypass those frustrating token limits, and transform free trials into Pro features with a few clicks? We've built a system that not only circumvents machine ID restrictions but also prevents those pesky automatic updates designed to block our freedom. Because great AI tools should be accessible to everyone, not just those in Cursor's "approved" regions. Liberate your coding experience today! 17 | 18 | ## Advanced Bypass Features 19 | 20 | - **Token Limit Bypass**: Remove restrictions on token usage for AI completions 21 | - **Pro Trial Conversion**: Access Pro features without purchasing a subscription 22 | - **Machine ID Reset**: Bypass the "Too many free trial accounts used on this machine" limitation 23 | - **Auto Update Prevention**: Stop Cursor from updating and potentially removing bypass functionality 24 | - **Cross-Platform**: Compatible with Windows, macOS, and Linux 25 | - **User-Friendly Interface**: Clean, modern UI with clear instructions 26 | - **System Information**: Displays detailed system and Cursor installation info 27 | 28 | ## Installation 29 | 30 | 1. **Clone the repository** 31 | 32 | ```bash 33 | git clone https://github.com/sazumivicky/cursor-reset-tools.git 34 | cd cursor-reset-tools 35 | ``` 36 | 37 | 2. **Install dependencies** 38 | 39 | ```bash 40 | npm install 41 | ``` 42 | 43 | 3. **Start the application** 44 | 45 | ```bash 46 | npm start 47 | ``` 48 | 49 | The server will start at http://localhost:3000 50 | 51 | ## Running with Administrator Rights 52 | 53 | This tool requires administrator privileges to modify system files. 54 | 55 | ### Windows 56 | ```bash 57 | # Start CMD as Administrator 58 | cd path\to\cursor-reset-tools 59 | npm start 60 | ``` 61 | 62 | ### macOS/Linux 63 | ```bash 64 | cd path/to/cursor-reset-tools 65 | sudo npm start 66 | ``` 67 | 68 | ## How it Works 69 | 70 | Cursor identifies your machine using a unique ID stored in specific locations: 71 | 72 | - **Windows**: `%APPDATA%\Cursor\machineId` 73 | - **macOS**: `~/Library/Application Support/Cursor/machineId` 74 | - **Linux**: `~/.config/cursor/machineid` 75 | 76 | This tool: 77 | 1. Detects if Cursor is running (and warns you to close it) 78 | 2. Generates a new UUID to replace your existing machine ID 79 | 3. Clears cached files that store your usage data 80 | 4. Prevents automatic updates that could remove bypass functionality 81 | 5. Modifies system files to unlock Pro features and bypass token limits 82 | 6. Allows Cursor to treat your machine as a new device 83 | 84 | ## 💡 Recommended Tips 85 | 86 | For best results when creating new Cursor accounts: 87 | 88 | - **Change IP When Needed**: If you encounter "too many requests" errors, toggle your mobile data to get a new IP address 89 | - **Use Disposable Email Services**: You can use [Sazumi Cloud - Email Disposable](https://mail.sazumi.com) which works well with Cursor 90 | - **Reset Machine ID First**: Always reset your machine ID before creating a new Cursor account 91 | - **Use Private Browsing**: Register with private/incognito browser windows 92 | - **Clear Cookies**: Clear browser cookies after registration for better security 93 | 94 | ## Technologies Used 95 | 96 | - **Backend**: Node.js, Express 97 | - **Frontend**: HTML, CSS, JavaScript 98 | - **Template Engine**: EJS 99 | - **Utilities**: uuid, fs-extra, node-machine-id, node-fetch, user-agents 100 | 101 | ## ⚠️ Disclaimer 102 | 103 | This tool is provided for educational and research purposes only. Use at your own risk. Sazumi Cloud developers are not responsible for any consequences arising from the use of this tool. 104 | 105 | **Please consider supporting Cursor development by purchasing a legitimate license if you find their product valuable for your workflow.** 106 | 107 | ## License 108 | 109 | This project is licensed under the MIT License - see the LICENSE file for details. 110 | 111 | ## 🤝 Contributing 112 | 113 | Contributions are welcome! Please feel free to submit a Pull Request. 114 | 115 | 1. Fork the repository 116 | 2. Create your feature branch (`git checkout -b feature/cursor-fix`) 117 | 3. Commit your changes (`git commit -m 'Add cursor bypass fix'`) 118 | 4. Push to the branch (`git push origin feature/cursor-fix`) 119 | 5. Open a Pull Request 120 | 121 | ## 🔗 Links 122 | 123 | - [GitHub Repository](https://github.com/sazumivicky/cursor-reset-tools) 124 | - [Issue Tracker](https://github.com/sazumivicky/cursor-reset-tools/issues) 125 | - [Donate to Sazumi Viki](https://sociabuzz.com/sazumi/tribe) 126 | 127 | --- 128 | 129 | Made with ❤️ by Sazumi Cloud -------------------------------------------------------------------------------- /public/js/main.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', () => { 2 | const si = document.getElementById('system-info'); 3 | const cs = document.getElementById('cursor-status'); 4 | const rb = document.getElementById('reset-btn'); 5 | const bp = document.getElementById('bypass-btn'); 6 | const du = document.getElementById('disable-update-btn'); 7 | const pc = document.getElementById('pro-convert-btn'); 8 | const rr = document.getElementById('reset-result'); 9 | const dm = document.getElementById('disclaimer-modal'); 10 | const ad = document.getElementById('accept-disclaimer'); 11 | const mc = document.querySelector('.modal-close'); 12 | 13 | const cm = () => { 14 | dm.style.display = 'none'; 15 | localStorage.setItem('disclaimerAccepted', 'true'); 16 | document.body.style.overflow = 'auto'; 17 | }; 18 | 19 | const sd = () => { 20 | if (!localStorage.getItem('disclaimerAccepted')) { 21 | dm.style.display = 'block'; 22 | document.body.style.overflow = 'hidden'; 23 | } 24 | }; 25 | 26 | if (ad) ad.addEventListener('click', cm); 27 | if (mc) mc.addEventListener('click', cm); 28 | 29 | window.addEventListener('click', (e) => { 30 | if (e.target === dm) cm(); 31 | }); 32 | 33 | const gp = async () => { 34 | const r = await fetch('/api/paths'); 35 | return await r.json(); 36 | }; 37 | 38 | const ub = (isRunning) => { 39 | rb.disabled = isRunning; 40 | bp.disabled = isRunning; 41 | du.disabled = isRunning; 42 | pc.disabled = isRunning; 43 | 44 | if (isRunning) { 45 | rb.title = "Close Cursor first"; 46 | bp.title = "Close Cursor first"; 47 | du.title = "Close Cursor first"; 48 | pc.title = "Close Cursor first"; 49 | } else { 50 | rb.title = ""; 51 | bp.title = ""; 52 | du.title = ""; 53 | pc.title = ""; 54 | } 55 | }; 56 | 57 | const cc = async () => { 58 | try { 59 | const p = await gp(); 60 | if (p.isRunning !== undefined) { 61 | ub(p.isRunning); 62 | 63 | const statusBadge = document.getElementById('cursor-status-badge'); 64 | if (statusBadge) { 65 | statusBadge.className = p.isRunning ? 'badge badge-danger' : 'badge badge-success'; 66 | statusBadge.textContent = p.isRunning ? 'Running' : 'Not Running'; 67 | } 68 | 69 | const warningMsg = document.getElementById('cursor-warning'); 70 | if (warningMsg) { 71 | warningMsg.style.display = p.isRunning ? 'block' : 'none'; 72 | } 73 | } 74 | } catch (e) { 75 | console.error('Error checking Cursor status:', e); 76 | } 77 | }; 78 | 79 | const gs = async () => { 80 | try { 81 | si.innerHTML = '
Loading system information...
'; 82 | cs.innerHTML = '
Checking Cursor status...
'; 83 | 84 | const p = await gp(); 85 | 86 | let systemHtml = ''; 87 | systemHtml += ``; 88 | systemHtml += ``; 89 | systemHtml += ``; 90 | systemHtml += ``; 91 | systemHtml += '
Platform${p.platform || navigator.platform}
OS${p.osVersion || navigator.userAgent}
Architecture${p.arch || 'Unknown'}
Home Directory
${p.homedir || 'Unknown'}
'; 92 | 93 | si.innerHTML = systemHtml; 94 | 95 | let cursorHtml = ''; 96 | cursorHtml += ``; 97 | cursorHtml += ``; 98 | cursorHtml += ``; 99 | cursorHtml += ``; 100 | cursorHtml += ``; 101 | cursorHtml += ``; 102 | 103 | if (p.exists) { 104 | const existsHtml = Object.entries(p.exists).map(([k, v]) => 105 | `` 106 | ).join(''); 107 | cursorHtml += existsHtml; 108 | } 109 | 110 | cursorHtml += '
Cursor Status
${p.isRunning ? 'Running' : 'Not Running'}
Machine ID Path
${p.machinePath || 'Not found'}
Storage Path
${p.storagePath || 'Not found'}
Database Path
${p.dbPath || 'Not found'}
App Path
${p.appPath || 'Not found'}
Update Path
${p.updatePath || 'Not found'}
${k} Exists${v ? 'Yes' : 'No'}
'; 111 | 112 | cursorHtml += ` 113 |
114 | 115 | Warning: Cursor is currently running. Please close it before using any features. 116 |
117 | 118 |
119 | 120 | Note: Make sure Cursor is closed before using any features. 121 |
122 | `; 123 | 124 | cs.innerHTML = cursorHtml; 125 | 126 | ub(p.isRunning); 127 | 128 | setTimeout(cb, 100); 129 | 130 | setInterval(cc, 5000); 131 | } catch (error) { 132 | si.innerHTML = `
Error: ${error.message}
`; 133 | cs.innerHTML = `
Error: ${error.message}
`; 134 | } 135 | }; 136 | 137 | const cb = () => { 138 | const codeBlocks = document.querySelectorAll('.code-block'); 139 | codeBlocks.forEach(block => { 140 | block.style.height = 'auto'; 141 | if (block.scrollHeight > block.clientHeight) { 142 | block.style.minHeight = Math.min(block.scrollHeight, 200) + 'px'; 143 | } 144 | }); 145 | }; 146 | 147 | const rm = async () => { 148 | try { 149 | const p = await gp(); 150 | if (p.isRunning) { 151 | showToast("Please close Cursor before resetting machine ID", "warning"); 152 | return; 153 | } 154 | 155 | rb.disabled = true; 156 | rr.innerHTML = ` 157 |
158 |

Resetting machine ID... Please wait

159 |
160 | `; 161 | 162 | const response = await fetch('/api/reset', { 163 | method: 'GET', 164 | headers: { 165 | 'Content-Type': 'application/json' 166 | } 167 | }); 168 | 169 | const result = await response.json(); 170 | 171 | if (result.success) { 172 | rr.innerHTML = ` 173 |
174 |

Success! Machine ID has been reset.

175 | ${result.log ? `
${result.log}
` : ''} 176 |
177 | `; 178 | } else { 179 | rr.innerHTML = ` 180 |
181 |

Failed to reset machine ID

182 |

Error: ${result.error || 'Unknown error'}

183 |
184 | `; 185 | } 186 | 187 | await gs(); 188 | setTimeout(cb, 100); 189 | } catch (error) { 190 | rr.innerHTML = `
Error: ${error.message}
`; 191 | } finally { 192 | rb.disabled = false; 193 | } 194 | }; 195 | 196 | const bk = async () => { 197 | try { 198 | const p = await gp(); 199 | if (p.isRunning) { 200 | showToast("Please close Cursor before bypassing token limit", "warning"); 201 | return; 202 | } 203 | 204 | bp.disabled = true; 205 | rr.innerHTML = ` 206 |
207 |

Bypassing token limit... Please wait

208 |
209 | `; 210 | 211 | const response = await fetch('/api/patch?action=bypass', { 212 | method: 'GET', 213 | headers: { 'Content-Type': 'application/json' } 214 | }); 215 | 216 | const result = await response.json(); 217 | 218 | if (result.success) { 219 | rr.innerHTML = ` 220 |
221 |

Success! Token limit has been bypassed.

222 | ${result.log ? `
${result.log}
` : ''} 223 |
224 | `; 225 | } else { 226 | rr.innerHTML = ` 227 |
228 |

Failed to bypass token limit

229 |

Error: ${result.error || 'Unknown error'}

230 |
231 | `; 232 | } 233 | 234 | await gs(); 235 | setTimeout(cb, 100); 236 | } catch (error) { 237 | rr.innerHTML = `
Error: ${error.message}
`; 238 | } finally { 239 | bp.disabled = false; 240 | } 241 | }; 242 | 243 | const dz = async () => { 244 | try { 245 | const p = await gp(); 246 | if (p.isRunning) { 247 | showToast("Please close Cursor before disabling auto-update", "warning"); 248 | return; 249 | } 250 | 251 | du.disabled = true; 252 | rr.innerHTML = ` 253 |
254 |

Disabling auto-update... Please wait

255 |
256 | `; 257 | 258 | const response = await fetch('/api/patch?action=disable', { 259 | method: 'GET', 260 | headers: { 'Content-Type': 'application/json' } 261 | }); 262 | 263 | const result = await response.json(); 264 | 265 | if (result.success) { 266 | rr.innerHTML = ` 267 |
268 |

Success! Auto-update has been disabled.

269 | ${result.log ? `
${result.log}
` : ''} 270 |
271 | `; 272 | } else { 273 | rr.innerHTML = ` 274 |
275 |

Failed to disable auto-update

276 |

Error: ${result.error || 'Unknown error'}

277 |
278 | `; 279 | } 280 | 281 | await gs(); 282 | setTimeout(cb, 100); 283 | } catch (error) { 284 | rr.innerHTML = `
Error: ${error.message}
`; 285 | } finally { 286 | du.disabled = false; 287 | } 288 | }; 289 | 290 | const pt = async () => { 291 | try { 292 | const p = await gp(); 293 | if (p.isRunning) { 294 | showToast("Please close Cursor before enabling Pro features", "warning"); 295 | return; 296 | } 297 | 298 | pc.disabled = true; 299 | rr.innerHTML = ` 300 |
301 |

Converting to Pro + Custom UI... Please wait

302 |
303 | `; 304 | 305 | const response = await fetch('/api/patch?action=pro', { 306 | method: 'GET', 307 | headers: { 'Content-Type': 'application/json' } 308 | }); 309 | 310 | const result = await response.json(); 311 | 312 | if (result.success) { 313 | rr.innerHTML = ` 314 |
315 |

Success! Pro features and custom UI have been enabled.

316 | ${result.log ? `
${result.log}
` : ''} 317 |
318 | `; 319 | } else { 320 | rr.innerHTML = ` 321 |
322 |

Failed to enable Pro features

323 |

Error: ${result.error || 'Unknown error'}

324 |
325 | `; 326 | } 327 | 328 | await gs(); 329 | setTimeout(cb, 100); 330 | } catch (error) { 331 | rr.innerHTML = `
Error: ${error.message}
`; 332 | } finally { 333 | pc.disabled = false; 334 | } 335 | }; 336 | 337 | const ta = () => { 338 | const accordionItems = document.querySelectorAll('.accordion-item'); 339 | 340 | accordionItems.forEach(item => { 341 | const header = item.querySelector('.accordion-header'); 342 | 343 | header.addEventListener('click', () => { 344 | const isActive = item.classList.contains('active'); 345 | 346 | accordionItems.forEach(accItem => { 347 | accItem.classList.remove('active'); 348 | }); 349 | 350 | if (!isActive) { 351 | item.classList.add('active'); 352 | } 353 | }); 354 | }); 355 | }; 356 | 357 | const tl = () => { 358 | const items = document.querySelectorAll('.timeline-item'); 359 | items.forEach(item => { 360 | item.style.opacity = '1'; 361 | item.style.transform = 'translateY(0)'; 362 | }); 363 | }; 364 | 365 | const td = () => { 366 | const vh = document.querySelectorAll('.version-header'); 367 | vh.forEach(header => { 368 | header.style.display = 'flex'; 369 | if (window.innerWidth <= 768) { 370 | header.style.flexDirection = 'column'; 371 | } else { 372 | header.style.flexDirection = 'row'; 373 | } 374 | }); 375 | }; 376 | 377 | const pl = () => { 378 | const loadingDiv = document.createElement('div'); 379 | loadingDiv.className = 'loading'; 380 | loadingDiv.textContent = 'Loading'; 381 | si.innerHTML = loadingDiv.outerHTML; 382 | cs.innerHTML = loadingDiv.outerHTML; 383 | }; 384 | 385 | const ib = () => { 386 | const donateBtn = document.querySelector('.donate-btn'); 387 | if (donateBtn) { 388 | donateBtn.addEventListener('click', (e) => { 389 | e.preventDefault(); 390 | window.open('https://sociabuzz.com/sazumi/tribe', '_blank'); 391 | }); 392 | } 393 | }; 394 | 395 | pl(); 396 | gs(); 397 | rb.addEventListener('click', rm); 398 | bp.addEventListener('click', bk); 399 | du.addEventListener('click', dz); 400 | pc.addEventListener('click', pt); 401 | 402 | setTimeout(() => { 403 | ta(); 404 | ib(); 405 | cb(); 406 | sd(); 407 | tl(); 408 | td(); 409 | }, 500); 410 | }); 411 | 412 | function copyToClipboard(e) { 413 | const targetId = e.currentTarget.dataset.target; 414 | const textToCopy = document.getElementById(targetId).textContent; 415 | const textArea = document.createElement('textarea'); 416 | 417 | textArea.value = textToCopy; 418 | document.body.appendChild(textArea); 419 | textArea.select(); 420 | document.execCommand('copy'); 421 | document.body.removeChild(textArea); 422 | 423 | const originalHtml = e.currentTarget.innerHTML; 424 | e.currentTarget.innerHTML = ''; 425 | document.getElementById(targetId).classList.add('copied'); 426 | 427 | setTimeout(() => { 428 | e.currentTarget.innerHTML = originalHtml; 429 | document.getElementById(targetId).classList.remove('copied'); 430 | }, 1500); 431 | } 432 | 433 | function showToast(message, type = 'info') { 434 | const toastContainer = document.querySelector('.toast-container') || (() => { 435 | const container = document.createElement('div'); 436 | container.className = 'toast-container'; 437 | document.body.appendChild(container); 438 | return container; 439 | })(); 440 | 441 | const toast = document.createElement('div'); 442 | toast.className = `toast toast-${type}`; 443 | 444 | toast.innerHTML = ` 445 |
446 | 447 | ${message} 448 |
449 | 450 | `; 451 | 452 | toastContainer.appendChild(toast); 453 | 454 | setTimeout(() => { 455 | toast.classList.add('toast-closing'); 456 | setTimeout(() => { 457 | toast.remove(); 458 | if (toastContainer.children.length === 0) { 459 | toastContainer.remove(); 460 | } 461 | }, 300); 462 | }, 5000); 463 | 464 | toast.querySelector('.toast-close').addEventListener('click', () => { 465 | toast.classList.add('toast-closing'); 466 | setTimeout(() => { 467 | toast.remove(); 468 | if (toastContainer.children.length === 0) { 469 | toastContainer.remove(); 470 | } 471 | }, 300); 472 | }); 473 | } 474 | -------------------------------------------------------------------------------- /routes/reset.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import fs from 'fs-extra'; 3 | import path from 'path'; 4 | import { v4 as uuidv4 } from 'uuid'; 5 | import os from 'os'; 6 | import sqlite3 from 'sqlite3'; 7 | import { open } from 'sqlite'; 8 | import crypto from 'crypto'; 9 | import { execSync } from 'child_process'; 10 | import { promisify } from 'util'; 11 | import { exec } from 'child_process'; 12 | 13 | const rt = express.Router(); 14 | const execPromise = promisify(exec); 15 | 16 | const gp = () => { 17 | const pt = os.platform(); 18 | const hm = os.homedir(); 19 | const dc = path.join(hm, 'Documents', '.cursor-free-vip'); 20 | const cf = path.join(dc, 'config.ini'); 21 | let mp = ''; 22 | let sp = ''; 23 | let dp = ''; 24 | let ap = ''; 25 | let cp = ''; 26 | let up = ''; 27 | 28 | if (pt === 'win32') { 29 | mp = path.join(hm, 'AppData', 'Roaming', 'Cursor', 'machineId'); 30 | sp = path.join(hm, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'storage.json'); 31 | dp = path.join(hm, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'state.vscdb'); 32 | ap = path.join(hm, 'AppData', 'Local', 'Programs', 'Cursor', 'resources', 'app'); 33 | cp = path.join(hm, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'cursor.json'); 34 | up = path.join(hm, 'AppData', 'Local', 'Programs', 'Cursor', 'resources', 'app-update.yml'); 35 | } else if (pt === 'darwin') { 36 | mp = path.join(hm, 'Library', 'Application Support', 'Cursor', 'machineId'); 37 | sp = path.join(hm, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'storage.json'); 38 | dp = path.join(hm, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'state.vscdb'); 39 | ap = path.join('/Applications', 'Cursor.app', 'Contents', 'Resources', 'app'); 40 | cp = path.join(hm, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'cursor.json'); 41 | up = path.join('/Applications', 'Cursor.app', 'Contents', 'Resources', 'app-update.yml'); 42 | } else if (pt === 'linux') { 43 | mp = path.join(hm, '.config', 'cursor', 'machineId'); 44 | sp = path.join(hm, '.config', 'cursor', 'User', 'globalStorage', 'storage.json'); 45 | dp = path.join(hm, '.config', 'cursor', 'User', 'globalStorage', 'state.vscdb'); 46 | ap = path.join('/usr', 'share', 'cursor', 'resources', 'app'); 47 | cp = path.join(hm, '.config', 'cursor', 'User', 'globalStorage', 'cursor.json'); 48 | up = path.join('/usr', 'share', 'cursor', 'resources', 'app-update.yml'); 49 | } 50 | 51 | return { mp, sp, dp, ap, cp, up, pt, dc, cf }; 52 | }; 53 | 54 | const mk = () => { 55 | const dt = new Date(); 56 | const yr = dt.getFullYear(); 57 | const mn = String(dt.getMonth() + 1).padStart(2, '0'); 58 | const dy = String(dt.getDate()).padStart(2, '0'); 59 | const hr = String(dt.getHours()).padStart(2, '0'); 60 | const mi = String(dt.getMinutes()).padStart(2, '0'); 61 | const sc = String(dt.getSeconds()).padStart(2, '0'); 62 | return `${yr}${mn}${dy}_${hr}${mi}${sc}`; 63 | }; 64 | 65 | const bk = async (filePath) => { 66 | try { 67 | const bkPath = `${filePath}.bak.${mk()}`; 68 | await fs.copy(filePath, bkPath); 69 | return bkPath; 70 | } catch (error) { 71 | return null; 72 | } 73 | }; 74 | 75 | const gh = () => { 76 | return crypto.randomBytes(16).toString('hex'); 77 | }; 78 | 79 | const gm = () => { 80 | return uuidv4().toUpperCase(); 81 | }; 82 | 83 | const cs = (seed) => { 84 | return crypto.createHash('sha256').update(seed).digest('hex'); 85 | }; 86 | 87 | const kc = async () => { 88 | if (os.platform() !== 'darwin') return false; 89 | 90 | try { 91 | await execPromise('security delete-generic-password -s "Cursor" -a "token" 2>/dev/null'); 92 | await execPromise('security delete-generic-password -s "Cursor" -a "refreshToken" 2>/dev/null'); 93 | return true; 94 | } catch (e) { 95 | return false; 96 | } 97 | }; 98 | 99 | const wu = async (newGuid) => { 100 | if (os.platform() !== 'win32') return false; 101 | 102 | try { 103 | const cmds = [ 104 | `REG ADD HKCU\\SOFTWARE\\Microsoft\\Cryptography /v MachineGuid /t REG_SZ /d ${uuidv4()} /f`, 105 | `REG ADD HKCU\\SOFTWARE\\Microsoft\\SQMClient /v MachineId /t REG_SZ /d ${newGuid} /f`, 106 | `REG ADD HKLM\\SOFTWARE\\Microsoft\\Cryptography /v MachineGuid /t REG_SZ /d ${uuidv4()} /f`, 107 | `REG ADD HKLM\\SOFTWARE\\Microsoft\\SQMClient /v MachineId /t REG_SZ /d ${newGuid} /f`, 108 | `REG ADD HKCU\\Software\\Cursor /v MachineId /t REG_SZ /d ${newGuid} /f /reg:64` 109 | ]; 110 | 111 | for (const cmd of cmds) { 112 | try { 113 | await execPromise(cmd); 114 | } catch (e) {} 115 | } 116 | return true; 117 | } catch (e) { 118 | return false; 119 | } 120 | }; 121 | 122 | const um = async (id) => { 123 | if (os.platform() !== 'darwin') return false; 124 | 125 | try { 126 | const p = '/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist'; 127 | const hp = path.join(os.homedir(), p); 128 | 129 | try { 130 | await execPromise(`defaults write ${hp} "UUID" "${id}"`); 131 | await execPromise(`sudo defaults write ${p} "UUID" "${id}"`); 132 | return true; 133 | } catch (e) {} 134 | 135 | return false; 136 | } catch (e) { 137 | return false; 138 | } 139 | }; 140 | 141 | const rm = async () => { 142 | const logs = []; 143 | const { mp, sp, dp, ap, cp, pt, dc, cf } = gp(); 144 | 145 | try { 146 | logs.push("ℹ️ Checking Config File..."); 147 | 148 | if (!fs.existsSync(dc)) { 149 | await fs.ensureDir(dc); 150 | logs.push("ℹ️ Created config directory"); 151 | } 152 | 153 | logs.push("📄 Reading Current Config..."); 154 | 155 | if (!fs.existsSync(sp)) { 156 | logs.push("⚠️ Warning: Storage file not found, will create if needed"); 157 | } 158 | 159 | if (fs.existsSync(sp)) { 160 | const bkPath = await bk(sp); 161 | logs.push(`💾 Creating Config Backup: ${bkPath}`); 162 | } 163 | 164 | logs.push("🔄 Generating New Machine ID..."); 165 | 166 | const newGuid = `{${gm().replace(/-/g, '-').toUpperCase()}}`; 167 | const machId = uuidv4(); 168 | const deviceId = uuidv4(); 169 | const sqmId = newGuid; 170 | const macId = crypto.randomBytes(64).toString('hex'); 171 | 172 | logs.push("📄 Saving New Config to JSON..."); 173 | 174 | if (fs.existsSync(sp)) { 175 | try { 176 | const storageData = JSON.parse(await fs.readFile(sp, 'utf8')); 177 | storageData['update.mode'] = 'none'; 178 | storageData['serviceMachineId'] = deviceId; 179 | storageData['telemetry.devDeviceId'] = deviceId; 180 | storageData['telemetry.macMachineId'] = macId; 181 | storageData['telemetry.machineId'] = cs(machId); 182 | storageData['telemetry.sqmId'] = sqmId; 183 | await fs.writeFile(sp, JSON.stringify(storageData, null, 2)); 184 | } catch (err) { 185 | const newStorageData = { 186 | 'update.mode': 'none', 187 | 'serviceMachineId': deviceId, 188 | 'telemetry.devDeviceId': deviceId, 189 | 'telemetry.macMachineId': macId, 190 | 'telemetry.machineId': cs(machId), 191 | 'telemetry.sqmId': sqmId 192 | }; 193 | await fs.writeFile(sp, JSON.stringify(newStorageData, null, 2)); 194 | } 195 | } else { 196 | const newStorageData = { 197 | 'update.mode': 'none', 198 | 'serviceMachineId': deviceId, 199 | 'telemetry.devDeviceId': deviceId, 200 | 'telemetry.macMachineId': macId, 201 | 'telemetry.machineId': cs(machId), 202 | 'telemetry.sqmId': sqmId 203 | }; 204 | await fs.ensureDir(path.dirname(sp)); 205 | await fs.writeFile(sp, JSON.stringify(newStorageData, null, 2)); 206 | } 207 | 208 | logs.push("ℹ️ Updating SQLite Database..."); 209 | 210 | const newIds = { 211 | "telemetry.devDeviceId": deviceId, 212 | "telemetry.macMachineId": macId, 213 | "telemetry.machineId": cs(machId), 214 | "telemetry.sqmId": sqmId, 215 | "storage.serviceMachineId": deviceId 216 | }; 217 | 218 | if (fs.existsSync(dp)) { 219 | await bk(dp); 220 | 221 | try { 222 | const db = await open({ 223 | filename: dp, 224 | driver: sqlite3.Database 225 | }); 226 | 227 | await db.exec(` 228 | CREATE TABLE IF NOT EXISTS ItemTable ( 229 | key TEXT PRIMARY KEY, 230 | value TEXT 231 | ) 232 | `); 233 | 234 | for (const [key, value] of Object.entries(newIds)) { 235 | await db.run(` 236 | INSERT OR REPLACE INTO ItemTable (key, value) 237 | VALUES (?, ?) 238 | `, [key, JSON.stringify(value)]); 239 | logs.push(`ℹ️ Updating Key-Value Pair: ${key}`); 240 | } 241 | 242 | await db.run(`UPDATE ItemTable SET value = '{"global":{"usage":{"sessionCount":0,"tokenCount":0}}}' WHERE key LIKE '%cursor%usage%'`); 243 | await db.run(`UPDATE ItemTable SET value = '"pro"' WHERE key LIKE '%cursor%tier%'`); 244 | await db.run(`DELETE FROM ItemTable WHERE key LIKE '%cursor.lastUpdateCheck%'`); 245 | await db.run(`DELETE FROM ItemTable WHERE key LIKE '%cursor.trialStartTime%'`); 246 | await db.run(`DELETE FROM ItemTable WHERE key LIKE '%cursor.trialEndTime%'`); 247 | 248 | await db.close(); 249 | logs.push("✅ SQLite Database Updated Successfully"); 250 | } catch (err) { 251 | logs.push(`⚠️ SQLite Update Error: ${err.message}`); 252 | } 253 | } else { 254 | logs.push("⚠️ SQLite Database not found, skipping database updates"); 255 | } 256 | 257 | logs.push("ℹ️ Updating System IDs..."); 258 | 259 | if (fs.existsSync(mp)) { 260 | await bk(mp); 261 | await fs.writeFile(mp, machId); 262 | logs.push("✅ Machine ID File Updated"); 263 | } else { 264 | await fs.ensureDir(path.dirname(mp)); 265 | await fs.writeFile(mp, machId); 266 | logs.push("✅ Machine ID File Created"); 267 | } 268 | 269 | if (fs.existsSync(cp)) { 270 | await bk(cp); 271 | try { 272 | const cursorData = JSON.parse(await fs.readFile(cp, 'utf8')); 273 | if (cursorData) { 274 | if (cursorData.global && cursorData.global.usage) { 275 | cursorData.global.usage.sessionCount = 0; 276 | cursorData.global.usage.tokenCount = 0; 277 | } else { 278 | cursorData.global = { 279 | usage: { 280 | sessionCount: 0, 281 | tokenCount: 0 282 | } 283 | }; 284 | } 285 | cursorData.tier = "pro"; 286 | await fs.writeFile(cp, JSON.stringify(cursorData, null, 2)); 287 | logs.push("✅ Cursor.json Updated Successfully"); 288 | } 289 | } catch (err) { 290 | logs.push(`⚠️ Cursor.json Update Error: ${err.message}`); 291 | } 292 | } 293 | 294 | if (pt === 'win32') { 295 | try { 296 | const wr = await wu(newGuid); 297 | if (wr) { 298 | logs.push("✅ Windows Machine GUID Updated Successfully"); 299 | logs.push(`ℹ️ New Machine ID: ${newGuid}`); 300 | logs.push("✅ Windows Machine ID Updated Successfully"); 301 | } 302 | } catch (err) { 303 | logs.push(`⚠️ Windows Registry Update Error: ${err.message}`); 304 | } 305 | } else if (pt === 'darwin') { 306 | try { 307 | const mr = await um(macId); 308 | if (mr) { 309 | logs.push("✅ macOS Platform UUID Updated Successfully"); 310 | } 311 | 312 | const kr = await kc(); 313 | if (kr) { 314 | logs.push("✅ macOS Keychain Cleared Successfully"); 315 | } 316 | } catch (err) { 317 | logs.push(`⚠️ macOS Update Error: ${err.message}`); 318 | } 319 | } 320 | 321 | logs.push("✅ Machine ID Reset Successfully"); 322 | logs.push("\nℹ️ New IDs:"); 323 | for (const [key, value] of Object.entries(newIds)) { 324 | logs.push(`ℹ️ ${key}: ${value}`); 325 | } 326 | 327 | return await ld(logs, "Machine ID Reset"); 328 | } catch (err) { 329 | logs.push(`❌ Process Error: ${err.message}`); 330 | return await ld(logs, "Machine ID Reset"); 331 | } 332 | }; 333 | 334 | const gw = () => { 335 | const { ap, pt } = gp(); 336 | 337 | if (pt === 'win32') { 338 | return path.join(ap, 'out', 'vs', 'workbench', 'workbench.desktop.main.js'); 339 | } else if (pt === 'darwin') { 340 | return path.join(ap, 'out', 'vs', 'workbench', 'workbench.desktop.main.js'); 341 | } else if (pt === 'linux') { 342 | return path.join(ap, 'out', 'vs', 'workbench', 'workbench.desktop.main.js'); 343 | } 344 | 345 | return ''; 346 | }; 347 | 348 | const bt = async () => { 349 | const logs = []; 350 | const { dp } = gp(); 351 | const workbenchPath = gw(); 352 | 353 | try { 354 | logs.push("ℹ️ Starting token limit bypass..."); 355 | 356 | if (!fs.existsSync(workbenchPath)) { 357 | logs.push(`❌ Workbench file not found at: ${workbenchPath}`); 358 | return await ld(logs, "Bypass Token Limit"); 359 | } 360 | 361 | const bkPath = await bk(workbenchPath); 362 | logs.push(`💾 Created backup: ${bkPath}`); 363 | 364 | const content = await fs.readFile(workbenchPath, 'utf8'); 365 | 366 | const patterns = { 367 | '
Pro Trial': '
Pro', 368 | 'py-1">Auto-select': 'py-1">Bypass-Version-Pin', 369 | 'async getEffectiveTokenLimit(e){const n=e.modelName;if(!n)return 2e5;': 'async getEffectiveTokenLimit(e){return 9000000;const n=e.modelName;if(!n)return 9e5;', 370 | 'var DWr=ne("
You are currently signed in with .");': 'var DWr=ne("
You are currently signed in with .

Pro

");', 371 | 'notifications-toasts': 'notifications-toasts hidden', 372 | 'hasReachedTokenLimit\\(\\w+\\)\\s*{[^}]+}': 'hasReachedTokenLimit(e){return false}', 373 | 'isProUser\\(\\w*\\)\\s*{\\s*[^}]+}': 'isProUser(){return true}', 374 | 'isPro\\(\\w*\\)\\s*{\\s*[^}]+}': 'isPro(){return true}', 375 | 'getTokenLimit\\(\\w*\\)\\s*{\\s*[^}]+}': 'getTokenLimit(){return 999999}', 376 | 'getTokensRemaining\\(\\w*\\)\\s*{\\s*[^}]+}': 'getTokensRemaining(){return 999999}', 377 | 'getTokensUsed\\(\\w*\\)\\s*{\\s*[^}]+}': 'getTokensUsed(){return 0}' 378 | }; 379 | 380 | let modified = content; 381 | for (const [pattern, replacement] of Object.entries(patterns)) { 382 | const regex = new RegExp(pattern, 'g'); 383 | modified = modified.replace(regex, replacement); 384 | } 385 | 386 | await fs.writeFile(workbenchPath, modified); 387 | logs.push("✅ Workbench file modified successfully"); 388 | 389 | if (fs.existsSync(dp)) { 390 | logs.push("ℹ️ Updating SQLite database for token limits..."); 391 | await bk(dp); 392 | 393 | try { 394 | const db = await open({ 395 | filename: dp, 396 | driver: sqlite3.Database 397 | }); 398 | 399 | await db.run(`UPDATE ItemTable SET value = '{"global":{"usage":{"sessionCount":0,"tokenCount":0}}}' WHERE key LIKE '%cursor%usage%'`); 400 | await db.close(); 401 | logs.push("✅ SQLite database updated for token limits"); 402 | } catch (err) { 403 | logs.push(`⚠️ SQLite update error: ${err.message}`); 404 | } 405 | } 406 | 407 | logs.push("✅ Token limit bypass completed successfully"); 408 | return await ld(logs, "Bypass Token Limit"); 409 | } catch (err) { 410 | logs.push(`❌ Token limit bypass error: ${err.message}`); 411 | return await ld(logs, "Bypass Token Limit"); 412 | } 413 | }; 414 | 415 | const du = async () => { 416 | const { ap, pt, up } = gp(); 417 | const logs = []; 418 | 419 | try { 420 | logs.push("ℹ️ Starting auto-update disabling process..."); 421 | 422 | logs.push("🔄 Terminating any running Cursor processes..."); 423 | try { 424 | if (pt === 'win32') { 425 | await execPromise('taskkill /F /IM Cursor.exe /T'); 426 | } else { 427 | await execPromise('pkill -f Cursor'); 428 | } 429 | logs.push("✅ Cursor processes terminated successfully"); 430 | } catch (e) { 431 | logs.push("ℹ️ No running Cursor processes found"); 432 | } 433 | 434 | let updaterPath; 435 | if (pt === 'win32') { 436 | updaterPath = path.join(os.homedir(), 'AppData', 'Local', 'cursor-updater'); 437 | } else if (pt === 'darwin') { 438 | updaterPath = path.join(os.homedir(), 'Library', 'Application Support', 'cursor-updater'); 439 | } else if (pt === 'linux') { 440 | updaterPath = path.join(os.homedir(), '.config', 'cursor-updater'); 441 | } 442 | 443 | logs.push(`🔄 Removing updater directory: ${updaterPath}`); 444 | if (fs.existsSync(updaterPath)) { 445 | try { 446 | if (fs.statSync(updaterPath).isDirectory()) { 447 | await fs.rm(updaterPath, { recursive: true, force: true }); 448 | } else { 449 | await fs.unlink(updaterPath); 450 | } 451 | logs.push("✅ Updater directory successfully removed"); 452 | } catch (e) { 453 | logs.push(`⚠️ Updater directory is locked, skipping removal: ${e.message}`); 454 | } 455 | } else { 456 | logs.push("ℹ️ Updater directory not found, creating blocker file"); 457 | } 458 | 459 | if (!up) { 460 | logs.push("⚠️ Update.yml path not found for this platform"); 461 | } else { 462 | logs.push(`🔄 Clearing update.yml file: ${up}`); 463 | try { 464 | if (fs.existsSync(up)) { 465 | await bk(up); 466 | await fs.writeFile(up, '', 'utf8'); 467 | logs.push("✅ Update.yml file successfully cleared"); 468 | } else { 469 | logs.push("ℹ️ Update.yml file not found, creating new one"); 470 | await fs.ensureDir(path.dirname(up)); 471 | } 472 | } catch (e) { 473 | logs.push(`⚠️ Failed to clear update.yml file: ${e.message}`); 474 | } 475 | } 476 | 477 | logs.push("🔄 Creating blocker files to prevent auto-updates..."); 478 | 479 | try { 480 | await fs.ensureDir(path.dirname(updaterPath)); 481 | await fs.writeFile(updaterPath, '', 'utf8'); 482 | 483 | if (pt === 'win32') { 484 | try { 485 | await execPromise(`attrib +r "${updaterPath}"`); 486 | } catch (e) { 487 | logs.push(`⚠️ Failed to set updater file as read-only: ${e.message}`); 488 | } 489 | } else { 490 | try { 491 | fs.chmodSync(updaterPath, 0o444); 492 | } catch (e) { 493 | logs.push(`⚠️ Failed to set updater file permissions: ${e.message}`); 494 | } 495 | } 496 | logs.push("✅ Updater blocker file created successfully"); 497 | } catch (e) { 498 | logs.push(`⚠️ Failed to create updater blocker file: ${e.message}`); 499 | } 500 | 501 | if (up) { 502 | try { 503 | await fs.ensureDir(path.dirname(up)); 504 | await fs.writeFile(up, '# This file is locked to prevent auto-updates\nversion: 0.0.0\n', 'utf8'); 505 | 506 | if (pt === 'win32') { 507 | try { 508 | await execPromise(`attrib +r "${up}"`); 509 | } catch (e) { 510 | logs.push(`⚠️ Failed to set update.yml as read-only: ${e.message}`); 511 | } 512 | } else { 513 | try { 514 | fs.chmodSync(up, 0o444); 515 | } catch (e) { 516 | logs.push(`⚠️ Failed to set update.yml permissions: ${e.message}`); 517 | } 518 | } 519 | logs.push("✅ Update.yml blocker file created successfully"); 520 | } catch (e) { 521 | logs.push(`⚠️ Failed to create update.yml blocker file: ${e.message}`); 522 | } 523 | } 524 | 525 | const pj = path.join(ap, 'product.json'); 526 | if (fs.existsSync(pj)) { 527 | logs.push(`🔄 Modifying product.json to remove update URLs: ${pj}`); 528 | try { 529 | const bkPath = await bk(pj); 530 | logs.push(`💾 Created backup: ${bkPath}`); 531 | 532 | let content = await fs.readFile(pj, 'utf8'); 533 | 534 | content = content.replace(/https:\/\/api2\.cursor\.sh\/aiserver\.v1\.AuthService\/DownloadUpdate/g, '') 535 | .replace(/https:\/\/api2\.cursor\.sh\/updates/g, '') 536 | .replace(/http:\/\/cursorapi\.com\/updates/g, ''); 537 | 538 | await fs.writeFile(pj, content, 'utf8'); 539 | logs.push("✅ Update URLs successfully removed from product.json"); 540 | } catch (e) { 541 | logs.push(`⚠️ Failed to modify product.json: ${e.message}`); 542 | } 543 | } else { 544 | logs.push(`⚠️ Product.json not found at: ${pj}`); 545 | } 546 | 547 | logs.push("✅ Auto-updates successfully disabled"); 548 | 549 | return await ld(logs, "Disable Auto-Update"); 550 | } catch (e) { 551 | logs.push(`❌ Error disabling auto-updates: ${e.message}`); 552 | return await ld(logs, "Disable Auto-Update"); 553 | } 554 | }; 555 | 556 | const pc = async () => { 557 | const logs = []; 558 | const { dp } = gp(); 559 | const workbenchPath = gw(); 560 | 561 | try { 562 | logs.push("ℹ️ Starting Pro conversion..."); 563 | 564 | if (fs.existsSync(dp)) { 565 | logs.push("ℹ️ Updating SQLite database for Pro features..."); 566 | await bk(dp); 567 | 568 | try { 569 | const db = await open({ 570 | filename: dp, 571 | driver: sqlite3.Database 572 | }); 573 | 574 | await db.run(`UPDATE ItemTable SET value = '"pro"' WHERE key LIKE '%cursor%tier%'`); 575 | await db.close(); 576 | logs.push("✅ Pro features enabled in SQLite database"); 577 | } catch (err) { 578 | logs.push(`⚠️ SQLite update error: ${err.message}`); 579 | } 580 | } else { 581 | logs.push("⚠️ SQLite database not found, skipping database update"); 582 | } 583 | 584 | if (fs.existsSync(workbenchPath)) { 585 | logs.push("ℹ️ Modifying workbench file for Pro UI..."); 586 | const bkPath = await bk(workbenchPath); 587 | logs.push(`💾 Created backup: ${bkPath}`); 588 | 589 | const content = await fs.readFile(workbenchPath, 'utf8'); 590 | 591 | const patterns = { 592 | '
Pro Trial': '
Pro', 593 | 'Upgrade to Pro': 'Sazumi Github', 594 | 'return t.pay': 'return function(){window.open("https://github.com/sazumivicky","_blank")}', 595 | 'rocket': 'github', 596 | 'var DWr=ne("
You are currently signed in with .");': 'var DWr=ne("
You are currently signed in with .

Pro

");', 597 | }; 598 | 599 | let modified = content; 600 | for (const [pattern, replacement] of Object.entries(patterns)) { 601 | modified = modified.replace(new RegExp(pattern, 'g'), replacement); 602 | } 603 | 604 | await fs.writeFile(workbenchPath, modified); 605 | logs.push("✅ Workbench file modified for Pro UI"); 606 | } else { 607 | logs.push(`⚠️ Workbench file not found at: ${workbenchPath}`); 608 | } 609 | 610 | logs.push("✅ Pro conversion completed successfully"); 611 | return await ld(logs, "Pro Conversion + Custom UI"); 612 | } catch (err) { 613 | logs.push(`❌ Pro conversion error: ${err.message}`); 614 | return await ld(logs, "Pro Conversion + Custom UI"); 615 | } 616 | }; 617 | 618 | const ld = async (logs, toolName) => { 619 | const hd = "=================================================="; 620 | const t1 = `🔄 Cursor ${toolName} Tool`; 621 | 622 | let lg = []; 623 | lg.push(hd); 624 | lg.push(t1); 625 | lg.push(hd); 626 | 627 | logs.forEach(l => { 628 | lg.push(l); 629 | }); 630 | 631 | return lg.join('\n'); 632 | }; 633 | 634 | rt.get('/reset', async (req, res) => { 635 | try { 636 | const result = await rm(); 637 | res.json({ success: true, log: result }); 638 | } catch (err) { 639 | res.status(500).json({ success: false, error: err.message }); 640 | } 641 | }); 642 | 643 | rt.get('/patch', async (req, res) => { 644 | try { 645 | const action = req.query.action || 'bypass'; 646 | let result; 647 | 648 | if (action === 'bypass') { 649 | result = await bt(); 650 | } else if (action === 'disable') { 651 | result = await du(); 652 | } else if (action === 'pro') { 653 | result = await pc(); 654 | } else { 655 | result = await bt(); 656 | } 657 | 658 | res.json({ success: true, log: result }); 659 | } catch (err) { 660 | res.status(500).json({ success: false, error: err.message }); 661 | } 662 | }); 663 | 664 | rt.get('/paths', async (req, res) => { 665 | try { 666 | const { mp, sp, dp, ap, cp, up, pt, dc } = gp(); 667 | 668 | let isRunning = false; 669 | try { 670 | if (pt === 'win32') { 671 | const { stdout } = await execPromise('tasklist /FI "IMAGENAME eq Cursor.exe" /NH'); 672 | isRunning = stdout.includes('Cursor.exe'); 673 | } else { 674 | const { stdout } = await execPromise('ps aux | grep -i Cursor | grep -v grep'); 675 | isRunning = stdout.trim().length > 0; 676 | } 677 | } catch (e) { 678 | isRunning = false; 679 | } 680 | 681 | const info = { 682 | platform: pt, 683 | osVersion: os.release(), 684 | arch: os.arch(), 685 | homedir: os.homedir(), 686 | machinePath: mp, 687 | storagePath: sp, 688 | dbPath: dp, 689 | appPath: ap, 690 | cursorPath: cp, 691 | updatePath: up, 692 | isRunning, 693 | exists: { 694 | machineId: fs.existsSync(mp), 695 | storage: fs.existsSync(sp), 696 | database: fs.existsSync(dp), 697 | app: fs.existsSync(ap), 698 | cursor: fs.existsSync(cp), 699 | update: fs.existsSync(up) 700 | } 701 | }; 702 | 703 | if (fs.existsSync(sp)) { 704 | try { 705 | const data = await fs.readFile(sp, 'utf8'); 706 | const json = JSON.parse(data); 707 | info.storage = { 708 | machineId: json['telemetry.machineId'] || json['serviceMachineId'], 709 | devDeviceId: json['telemetry.devDeviceId'], 710 | tier: json['cursor.tier'] || 'unknown' 711 | }; 712 | } catch (e) {} 713 | } 714 | 715 | if (fs.existsSync(dp)) { 716 | try { 717 | const db = await open({ 718 | filename: dp, 719 | driver: sqlite3.Database 720 | }); 721 | const rows = await db.all('SELECT key, value FROM ItemTable WHERE key LIKE "%cursor%" OR key LIKE "%telemetry%" LIMIT 10'); 722 | info.database = rows.reduce((acc, row) => { 723 | try { 724 | acc[row.key] = JSON.parse(row.value); 725 | } catch (e) { 726 | acc[row.key] = row.value; 727 | } 728 | return acc; 729 | }, {}); 730 | await db.close(); 731 | } catch (e) {} 732 | } 733 | 734 | res.json(info); 735 | } catch (err) { 736 | res.status(500).json({ error: err.message }); 737 | } 738 | }); 739 | 740 | export default rt; -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sazumi Cloud - Cursor Reset Tool 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 40 | 41 |
42 |
43 |

Sazumi Cloud

44 |

Cursor Machine ID Reset Tool

45 | v2.0.0 46 |
47 | 48 |
49 |
50 |
51 |

About This Tool

52 |
53 |
54 |
55 | 56 | Recommended: For best compatibility, use Cursor version 0.49.x. 57 |
58 |

Sazumi Cloud is a powerful tool designed to reset the Machine ID of Cursor IDE. This helps to bypass trial limits, convert free trials to Pro, bypass token limitations, and prevent automatic updates to unleash the full potential of Cursor AI capabilities.

59 |
60 |
61 | 62 |
63 |

Token Limit Bypass

64 |

Remove restrictions on token usage for AI completions

65 |
66 |
67 |
68 | 69 |
70 |

Pro Conversion

71 |

Access Pro features without purchasing a subscription and customize the UI

72 |
73 |
74 |
75 | 76 |
77 |

Machine ID Reset

78 |

Bypass the "Too many free trial accounts" limitation

79 |
80 |
81 |
82 | 83 |
84 |

Auto Update Prevention

85 |

Stop Cursor from automatically updating to versions that could patch these bypasses

86 |
87 |
88 |
89 |
90 |
91 |
92 | 93 |
94 |
95 |
96 |

System Information

97 |
98 |
99 |
100 |
Loading system information
101 |
102 |
103 |
104 | 105 |
106 |
107 |

Cursor Status

108 |
109 |
110 |
111 |
Loading cursor information
112 |
113 |
114 |
115 | 116 |
117 |
118 |

Reset Machine ID

119 |
120 |
121 |
122 | 123 | Important: Make sure Cursor is closed before resetting the machine ID. 124 |
125 |
126 | 127 | 128 | 129 | 130 |
131 |
132 |
133 |
134 | 135 |
136 |
137 |

Email Generator

138 |
139 |
140 |
141 | 142 | Note: When you encounter "too many requests" errors, you MUST change your IP address by toggling your mobile data OFF and ON. 143 |
144 | 148 |
149 |
150 |
151 | 152 |
153 |
154 |
155 |

Requirements

156 |
157 |
158 |
    159 |
  • Admin privileges are required for file operations
  • 160 |
  • Cursor must be completely closed when resetting
  • 161 |
  • Internet connection for API calls
  • 162 |
  • Compatible with Windows, macOS, and Linux
  • 163 |
164 |
165 |
166 |
167 | 168 |
169 |
170 |
171 |

Documentation

172 |
173 |
174 |
175 |
176 |
177 | 178 |

How It Works

179 |
180 |
181 |

Cursor identifies your machine using a unique ID stored in specific locations of your system. This tool works by:

182 |
    183 |
  • Machine ID Reset: Generates a new unique identifier using secure cryptographic methods to bypass device restrictions and Cursor's telemetry tracking
  • 184 |
  • Token Limits: Modifies system files and SQLite database to remove AI completion token restrictions with improved patching methods
  • 185 |
  • Pro Features: Modifies authentication data to enable premium features without a paid subscription (Note: This only changes the UI appearance, not actual Pro functionality as Pro features work in the cloud, not locally)
  • 186 |
  • Update Prevention: Implements multiple layers of protection to block Cursor from automatically updating, including app-update.yml modification, blocker files, and URL endpoint removal
  • 187 |
  • Storage Clearance: Removes cached data that tracks your usage history and limits
  • 188 |
  • Cross-Platform: Works across Windows, macOS and Linux with dedicated optimizations for each platform
  • 189 |
190 |

These modifications allow Cursor to treat your machine as a new device with full Pro capabilities.

191 |
192 |
193 |
194 |
195 | 196 |

Prerequisites

197 |
198 |
199 |
    200 |
  • Close all instances of Cursor before resetting
  • 201 |
  • Administrator privileges may be required
  • 202 |
  • Restart Cursor after reset for changes to take effect
  • 203 |
  • Recommended Version: For optimal results, use Cursor version 0.49.x
  • 204 |
205 |
206 |
207 |
208 |
209 | 210 |

Troubleshooting

211 |
212 |
213 |
    214 |
  • If reset fails, try running the application with administrator privileges
  • 215 |
  • Ensure Cursor is completely closed (check task manager)
  • 216 |
  • If problems persist, manual deletion of files may be required
  • 217 |
218 |
219 |
220 |
221 |
222 | 223 |

Recommended Tips

224 |
225 |
226 |

For best results when creating new Cursor accounts:

227 | 230 |
231 |
232 |
233 |
234 |
235 |
236 | 237 |
238 |
239 |
240 |

Installation Guide

241 |
242 |
243 |
244 | 245 | Important: Do not run or edit this code using Cursor IDE. Use VS Code or another code editor instead. 246 |
247 | 248 |
249 |
250 |
1
251 |
252 |

Clone Repository

253 |
git clone https://github.com/sazumivicky/cursor-reset-tools.git 254 | cd cursor-reset-tools
255 |
256 |
257 | 258 |
259 |
2
260 |
261 |

Install Dependencies

262 |
npm install
263 |
264 |
265 | 266 |
267 |
3
268 |
269 |

Start the Application

270 |
npm start
271 |

The server will start on http://localhost:3000

272 |
273 |
274 |
275 | 276 |
277 |
278 |
279 | 280 |

Running as Administrator

281 |
282 |
283 |

On Windows, use Command Prompt as Administrator:

284 |
cd path\to\cursor-reset-tools 285 | npm start
286 |

On macOS/Linux, use sudo:

287 |
cd path/to/cursor-reset-tools 288 | sudo npm start
289 |
290 |
291 |
292 |
293 | 294 |

Development Mode

295 |
296 |
297 |

To run in development mode with automatic restart:

298 |
npm run dev
299 |
300 |
301 |
302 |
303 |
304 |
305 | 306 |
307 |
308 |
309 |

Changelog

310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 | v2.0.0 318 | July 2025 319 |
320 |
321 |
    322 |
  • Fixed Major improvements to reset machine ID for all platforms
  • 323 |
  • Fixed More robust bypass token limit patching for new Cursor versions
  • 324 |
  • Fixed Disable auto-update now blocks all update channels and URLs
  • 325 |
  • Fixed Pro Conversion + Custom UI now works with latest UI changes
  • 326 |
  • Improved Cross-platform support and error handling
  • 327 |
  • Improved UI/UX for system and Cursor status detection
  • 328 |
  • Improved All features tested and optimized for Cursor 0.49.x
  • 329 |
330 |
331 |
332 |
333 | 334 |
335 |
336 |
337 |
338 | v1.9.0 339 | June 2025 340 |
341 |
342 |
    343 |
  • Fixed Complete machine ID reset to properly bypass device limits
  • 344 |
  • Added Enhanced registry updates for Windows with both HKCU and HKLM keys
  • 345 |
  • Added macOS keychain credential clearing for complete reset
  • 346 |
  • Added Improved Linux support with additional identity file handling
  • 347 |
  • Enhanced SQLite database modifications with trial-related entry deletion
  • 348 |
  • Fixed More robust getMachineId function patching with direct UUID injection
  • 349 |
  • Improved Better error handling and file existence checks
  • 350 |
351 |
352 |
353 |
354 | 355 |
356 |
357 |
358 |
359 | v1.8.0 360 | June 2025 361 |
362 |
363 |
    364 |
  • Added Windows registry update implementation for complete machine ID reset
  • 365 |
  • Added macOS platform UUID update support using plutil
  • 366 |
  • Improved Cross-platform machine ID reset with OS-specific optimizations
  • 367 |
  • Fixed Silent error handling for registry and platform UUID updates
  • 368 |
369 |
370 |
371 |
372 | 373 |
374 |
375 |
376 |
377 | v1.7.0 378 | June 2025 379 |
380 |
381 |
    382 |
  • Enhanced Machine ID reset with improved cryptographic generation
  • 383 |
  • Added Windows registry support for more complete reset
  • 384 |
  • Improved Main.js patching to bypass machine ID checks on Cursor 0.49+
  • 385 |
  • Enhanced Disable auto-update functionality with multiple protection layers
  • 386 |
  • Fixed ES modules compatibility issues with clean native methods
  • 387 |
  • Improved Cross-platform file path handling for Windows/macOS/Linux
  • 388 |
389 |
390 |
391 |
392 | 393 |
394 |
395 |
396 |
397 | v1.6.0 398 | May 2025 399 |
400 |
401 |
    402 |
  • Removed Atomic Mail functionality completely
  • 403 |
  • Fixed Cross-platform compatibility for Windows, macOS, and Linux features
  • 404 |
  • Improved Modern JavaScript with cleaner code patterns
  • 405 |
406 |
407 |
408 |
409 | 410 |
411 |
412 |
413 |
414 | v1.5.0 415 | May 2025 416 |
417 |
418 |
    419 |
  • Improved Enhanced SQLite database handling for more reliable resets
  • 420 |
  • Added Proper backup of all files before modification
  • 421 |
  • Improved Windows registry updates for complete system ID refresh
  • 422 |
  • Fixed Better handling of telemetry IDs across all platforms
  • 423 |
  • Improved getMachineId patching in Cursor 0.49+ versions
  • 424 |
  • Fixed Token limit bypass with multiple pattern detection methods
  • 425 |
  • Fixed Auto-update disabling with proper permissions handling
  • 426 |
  • Added Fallback mechanisms for all core functions when primary methods fail
  • 427 |
428 |
429 |
430 |
431 | 432 |
433 |
434 |
435 |
436 | v1.4.0 437 | May 2025 438 |
439 |
440 |
    441 |
  • Added Bypass token limit feature (experimental - still finding workarounds)
  • 442 |
  • Added Disable auto-update feature to prevent automatic updates
  • 443 |
  • Added Pro conversion tool for enabling premium features
  • 444 |
  • Fixed MacOS functionality issues across all features
  • 445 |
  • Improved Overall stability and compatibility with latest Cursor versions
  • 446 |
447 |
448 |
449 |
450 | 451 |
452 |
453 |
454 |
455 | v1.3.0 456 | April 2025 457 |
458 |
459 |
    460 |
  • Fixed MacOS reset issue for "Too many free trial accounts" error
  • 461 |
  • Improved MacOS keychain handling for stored credentials removal
  • 462 |
  • Added Enhanced cleaning of additional ID files on MacOS
  • 463 |
  • Fixed MacOS defaults reset for Cursor application
  • 464 |
  • Improved Compatibility with Cursor version 0.48.x
  • 465 |
466 |
467 |
468 |
469 | 470 |
471 |
472 |
473 |
474 | v1.2.0 475 | April 2025 476 |
477 |
478 |
    479 |
  • Fixed Improved machine ID reset method to match cursor-free-vip
  • 480 |
  • Fixed Files are now modified instead of deleted to preserve login status
  • 481 |
  • Added Detailed logs in UI showing each step of the reset process
  • 482 |
  • Fixed Added automatic cursor process termination if running
  • 483 |
  • Improved Patching for newer Cursor versions (≥0.45.0)
  • 484 |
485 |
486 |
487 |
488 | 489 |
490 |
491 |
492 |
493 | v1.1.0 494 | April 2025 495 |
496 |
497 |
    498 |
  • Improved Enhanced UI with modern design
  • 499 |
  • Improved Better cross-platform compatibility
  • 500 |
  • Added Comprehensive documentation
  • 501 |
502 |
503 |
504 |
505 | 506 |
507 |
508 |
509 |
510 | v1.0.0 511 | April 2025 512 |
513 |
514 |
    515 |
  • New Initial release of Sazumi Cloud
  • 516 |
  • New Support for Windows, macOS and Linux
  • 517 |
  • New Machine ID reset functionality
  • 518 |
  • New System information display
  • 519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 | 528 |
529 | 539 |
540 | 541 |
542 |

© Cursor Reset Tool by Sazumi Cloud. All rights reserved.

543 |

A product by PT Sazumi Cloud Inc.

544 |
545 |
546 | 547 | 548 | 551 | 552 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary-color: #333333; 3 | --secondary-color: #f8f9fa; 4 | --accent-color: #757575; 5 | --accent-dark: #424242; 6 | --text-color: #212121; 7 | --success-color: #4caf50; 8 | --error-color: #f44336; 9 | --light-bg: #f1f3f4; 10 | --border-color: #e0e0e0; 11 | --border-radius: 6px; 12 | } 13 | 14 | * { 15 | margin: 0; 16 | padding: 0; 17 | box-sizing: border-box; 18 | } 19 | 20 | body { 21 | font-family: 'Google Sans', -apple-system, sans-serif; 22 | background-color: var(--secondary-color); 23 | color: var(--text-color); 24 | line-height: 1.6; 25 | overflow-x: hidden; 26 | } 27 | 28 | .container { 29 | max-width: 900px; 30 | margin: 0 auto; 31 | padding: 2rem 1rem; 32 | } 33 | 34 | header { 35 | text-align: center; 36 | margin-bottom: 2.5rem; 37 | padding: 1.5rem 0; 38 | position: relative; 39 | } 40 | 41 | header::after { 42 | content: ''; 43 | position: absolute; 44 | bottom: 0; 45 | left: 50%; 46 | transform: translateX(-50%); 47 | width: 60px; 48 | height: 3px; 49 | background: var(--accent-color); 50 | border-radius: 3px; 51 | } 52 | 53 | header h1 { 54 | font-size: 2.5rem; 55 | margin-bottom: 0.5rem; 56 | letter-spacing: -0.5px; 57 | font-weight: 700; 58 | text-transform: uppercase; 59 | background: linear-gradient(45deg, var(--accent-color), var(--accent-dark)); 60 | -webkit-background-clip: text; 61 | background-clip: text; 62 | color: transparent; 63 | position: relative; 64 | display: inline-block; 65 | } 66 | 67 | header h1::before { 68 | content: attr(data-text); 69 | position: absolute; 70 | left: 0; 71 | top: 0; 72 | z-index: -1; 73 | background: linear-gradient(45deg, var(--accent-color), var(--accent-dark)); 74 | -webkit-background-clip: text; 75 | background-clip: text; 76 | color: transparent; 77 | filter: blur(8px); 78 | opacity: 0.5; 79 | } 80 | 81 | header p { 82 | color: var(--primary-color); 83 | font-size: 1.1rem; 84 | opacity: 0.8; 85 | } 86 | 87 | main, #intro, #docs, #changelog, #requirements { 88 | display: grid; 89 | grid-template-columns: 1fr; 90 | gap: 1.5rem; 91 | margin-bottom: 2rem; 92 | } 93 | 94 | @media (min-width: 768px) { 95 | main { 96 | grid-template-columns: repeat(2, 1fr); 97 | } 98 | 99 | main .card:last-child { 100 | grid-column: span 2; 101 | } 102 | } 103 | 104 | .card { 105 | background: white; 106 | border-radius: var(--border-radius); 107 | overflow: hidden; 108 | border: 1px solid var(--border-color); 109 | transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), border-color 0.3s ease; 110 | } 111 | 112 | .card:hover { 113 | transform: translateY(-5px); 114 | border-color: var(--accent-color); 115 | } 116 | 117 | .card-header { 118 | background: white; 119 | padding: 1.25rem 1.5rem; 120 | border-bottom: 1px solid var(--border-color); 121 | position: relative; 122 | } 123 | 124 | .card-header h2 { 125 | color: var(--text-color); 126 | font-size: 1.25rem; 127 | font-weight: 500; 128 | display: flex; 129 | align-items: center; 130 | gap: 10px; 131 | } 132 | 133 | .card-header h2 i { 134 | color: var(--accent-color); 135 | transition: transform 0.3s ease; 136 | font-size: 20px; 137 | } 138 | 139 | .card:hover .card-header h2 i { 140 | transform: scale(1.2); 141 | } 142 | 143 | .card-body { 144 | padding: 1.5rem; 145 | } 146 | 147 | .features { 148 | display: grid; 149 | grid-template-columns: 1fr; 150 | gap: 1.5rem; 151 | margin-top: 1.5rem; 152 | } 153 | 154 | @media (min-width: 768px) { 155 | .features { 156 | grid-template-columns: repeat(3, 1fr); 157 | } 158 | } 159 | 160 | .feature { 161 | display: flex; 162 | align-items: flex-start; 163 | gap: 15px; 164 | padding: 1rem; 165 | background: var(--light-bg); 166 | border-radius: var(--border-radius); 167 | transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); 168 | border: 1px solid transparent; 169 | } 170 | 171 | .feature:hover { 172 | transform: translateY(-3px) scale(1.02); 173 | border-color: var(--accent-color); 174 | background: white; 175 | } 176 | 177 | .feature i { 178 | color: var(--accent-color); 179 | font-size: 24px; 180 | transition: transform 0.3s ease; 181 | } 182 | 183 | .feature:hover i { 184 | transform: scale(1.2); 185 | } 186 | 187 | .feature h3 { 188 | font-size: 1rem; 189 | margin-bottom: 0.5rem; 190 | font-weight: 500; 191 | } 192 | 193 | .feature p { 194 | font-size: 0.9rem; 195 | color: var(--primary-color); 196 | opacity: 0.8; 197 | } 198 | 199 | .btn { 200 | display: inline-flex; 201 | align-items: center; 202 | justify-content: center; 203 | background: linear-gradient(45deg, var(--accent-color), var(--accent-dark)); 204 | color: white; 205 | border: none; 206 | border-radius: 50px; 207 | padding: 0.75rem 1.75rem; 208 | font-size: 0.9rem; 209 | font-weight: 500; 210 | cursor: pointer; 211 | transition: all 0.3s; 212 | letter-spacing: 0.25px; 213 | gap: 8px; 214 | position: relative; 215 | overflow: hidden; 216 | } 217 | 218 | .btn::after { 219 | content: ''; 220 | position: absolute; 221 | top: 50%; 222 | left: 50%; 223 | width: 100%; 224 | height: 100%; 225 | background: rgba(255, 255, 255, 0.2); 226 | border-radius: 50%; 227 | transform: scale(0) translate(-50%, -50%); 228 | transform-origin: top left; 229 | transition: transform 0.6s; 230 | } 231 | 232 | .btn:hover { 233 | transform: translateY(-2px); 234 | } 235 | 236 | .btn:hover::after { 237 | transform: scale(2.5) translate(-50%, -50%); 238 | } 239 | 240 | .btn:active { 241 | transform: translateY(1px); 242 | } 243 | 244 | .btn:disabled { 245 | background: #e0e0e0; 246 | color: #9e9e9e; 247 | cursor: not-allowed; 248 | } 249 | 250 | .btn:disabled::after { 251 | display: none; 252 | } 253 | 254 | .btn i { 255 | font-size: 18px; 256 | transition: transform 0.3s ease; 257 | } 258 | 259 | .btn:hover i { 260 | transform: rotate(180deg); 261 | } 262 | 263 | .alert { 264 | padding: 1rem; 265 | border-radius: var(--border-radius); 266 | margin: 1rem 0; 267 | font-size: 0.9rem; 268 | display: flex; 269 | align-items: flex-start; 270 | gap: 10px; 271 | background-color: var(--light-bg); 272 | } 273 | 274 | .alert-warning { 275 | background-color: #fff8e1; 276 | color: #bf360c; 277 | } 278 | 279 | .alert-info { 280 | background-color: #e8f5e9; 281 | color: #1b5e20; 282 | } 283 | 284 | .alert i { 285 | font-size: 20px; 286 | color: var(--accent-color); 287 | } 288 | 289 | #reset-result { 290 | margin-top: 1.5rem; 291 | border-radius: var(--border-radius); 292 | font-size: 0.95rem; 293 | } 294 | 295 | .success, .error, .processing { 296 | padding: 1rem; 297 | border-radius: var(--border-radius); 298 | display: flex; 299 | flex-direction: column; 300 | gap: 8px; 301 | } 302 | 303 | .success { 304 | background-color: #e8f5e9; 305 | color: #1b5e20; 306 | } 307 | 308 | .error { 309 | background-color: #ffebee; 310 | color: #b71c1c; 311 | } 312 | 313 | .processing { 314 | background-color: #ede7f6; 315 | color: #311b92; 316 | } 317 | 318 | .success p, .error p, .processing p { 319 | display: flex; 320 | align-items: center; 321 | gap: 8px; 322 | } 323 | 324 | footer { 325 | text-align: center; 326 | margin-top: 3rem; 327 | padding-top: 1.5rem; 328 | border-top: 1px solid var(--border-color); 329 | font-size: 0.9rem; 330 | color: var(--primary-color); 331 | } 332 | 333 | footer a { 334 | color: var(--accent-color); 335 | text-decoration: none; 336 | transition: color 0.3s ease; 337 | } 338 | 339 | footer a:hover { 340 | text-decoration: underline; 341 | } 342 | 343 | table { 344 | width: 100%; 345 | border-collapse: collapse; 346 | margin-bottom: 1rem; 347 | font-size: 0.9rem; 348 | border-radius: var(--border-radius); 349 | overflow: hidden; 350 | } 351 | 352 | table, th, td { 353 | border: 1px solid var(--border-color); 354 | } 355 | 356 | th, td { 357 | padding: 0.75rem; 358 | text-align: left; 359 | } 360 | 361 | th { 362 | background-color: var(--light-bg); 363 | font-weight: 500; 364 | color: var(--primary-color); 365 | } 366 | 367 | tr:nth-child(even) { 368 | background-color: var(--light-bg); 369 | } 370 | 371 | .code-block { 372 | max-width: 100%; 373 | overflow-x: hidden; 374 | background: #1e1e1e; 375 | padding: 0.75rem; 376 | border-radius: var(--border-radius); 377 | font-family: 'Consolas', 'Monaco', monospace; 378 | font-size: 0.85rem; 379 | color: #d4d4d4; 380 | border: none; 381 | white-space: pre-wrap; 382 | word-wrap: break-word; 383 | word-break: break-all; 384 | scrollbar-width: thin; 385 | scrollbar-color: var(--accent-color) #1e1e1e; 386 | position: relative; 387 | box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); 388 | margin: 0; 389 | } 390 | 391 | .code-block::-webkit-scrollbar { 392 | height: 6px; 393 | } 394 | 395 | .code-block::-webkit-scrollbar-track { 396 | background: #2d2d2d; 397 | border-radius: 3px; 398 | } 399 | 400 | .code-block::-webkit-scrollbar-thumb { 401 | background-color: var(--accent-color); 402 | border-radius: 3px; 403 | } 404 | 405 | .code-block::before, .code-block::after { 406 | content: ''; 407 | position: absolute; 408 | top: 0; 409 | height: 100%; 410 | width: 20px; 411 | pointer-events: none; 412 | z-index: 1; 413 | opacity: 0.7; 414 | } 415 | 416 | .code-block::before { 417 | left: 0; 418 | background: linear-gradient(to right, #1e1e1e, rgba(30, 30, 30, 0)); 419 | } 420 | 421 | .code-block::after { 422 | right: 0; 423 | background: linear-gradient(to left, #1e1e1e, rgba(30, 30, 30, 0)); 424 | } 425 | 426 | .status-indicator { 427 | display: inline-block; 428 | width: 10px; 429 | height: 10px; 430 | border-radius: 50%; 431 | margin-right: 8px; 432 | } 433 | 434 | .status-running { 435 | background-color: var(--success-color); 436 | box-shadow: 0 0 5px var(--success-color); 437 | animation: pulse 1.5s infinite; 438 | } 439 | 440 | .status-stopped { 441 | background-color: var(--error-color); 442 | } 443 | 444 | @keyframes pulse { 445 | 0% { 446 | box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7); 447 | } 448 | 70% { 449 | box-shadow: 0 0 0 5px rgba(76, 175, 80, 0); 450 | } 451 | 100% { 452 | box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); 453 | } 454 | } 455 | 456 | .loading { 457 | display: flex; 458 | justify-content: center; 459 | align-items: center; 460 | min-height: 100px; 461 | color: var(--primary-color); 462 | } 463 | 464 | .loading::before { 465 | content: ""; 466 | width: 40px; 467 | height: 40px; 468 | border-radius: 50%; 469 | border: 3px solid transparent; 470 | border-top-color: var(--accent-color); 471 | border-right-color: var(--accent-color); 472 | animation: sp 1s cubic-bezier(0.5, 0, 0.5, 1) infinite; 473 | margin-right: 10px; 474 | } 475 | 476 | @keyframes sp { 477 | 0% { 478 | transform: rotate(0deg); 479 | } 480 | 100% { 481 | transform: rotate(360deg); 482 | } 483 | } 484 | 485 | .accordion { 486 | display: flex; 487 | flex-direction: column; 488 | gap: 0.5rem; 489 | } 490 | 491 | .accordion-item { 492 | border: 1px solid var(--border-color); 493 | border-radius: var(--border-radius); 494 | overflow: hidden; 495 | transition: all 0.3s ease; 496 | } 497 | 498 | .accordion-item:hover { 499 | border-color: var(--accent-color); 500 | } 501 | 502 | .accordion-header { 503 | padding: 1rem; 504 | display: flex; 505 | align-items: center; 506 | gap: 10px; 507 | background: var(--light-bg); 508 | cursor: pointer; 509 | user-select: none; 510 | } 511 | 512 | .accordion-header i { 513 | transition: transform 0.3s ease; 514 | color: var(--accent-color); 515 | font-size: 18px; 516 | } 517 | 518 | .accordion-item.active .accordion-header i { 519 | transform: rotate(90deg); 520 | } 521 | 522 | .accordion-header h3 { 523 | font-size: 1rem; 524 | font-weight: 500; 525 | margin: 0; 526 | } 527 | 528 | .accordion-content { 529 | padding: 0; 530 | max-height: 0; 531 | overflow: hidden; 532 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); 533 | opacity: 0; 534 | } 535 | 536 | .accordion-item.active .accordion-content { 537 | padding: 1rem; 538 | max-height: 300px; 539 | overflow-y: auto; 540 | opacity: 1; 541 | } 542 | 543 | .accordion-content p { 544 | margin-bottom: 1rem; 545 | } 546 | 547 | .accordion-content ul { 548 | padding-left: 1.5rem; 549 | } 550 | 551 | .accordion-content li { 552 | margin-bottom: 0.5rem; 553 | } 554 | 555 | .version { 556 | margin-bottom: 1.5rem; 557 | padding-bottom: 1.5rem; 558 | border-bottom: 1px solid var(--border-color); 559 | transition: transform 0.3s ease; 560 | } 561 | 562 | .version:hover { 563 | transform: translateX(5px); 564 | } 565 | 566 | .version:last-child { 567 | margin-bottom: 0; 568 | padding-bottom: 0; 569 | border-bottom: none; 570 | } 571 | 572 | .version-header { 573 | display: flex; 574 | align-items: center; 575 | margin-bottom: 15px; 576 | } 577 | 578 | .version-tag { 579 | font-weight: 600; 580 | color: var(--accent-color); 581 | font-size: 1.1rem; 582 | transition: color 0.3s ease; 583 | } 584 | 585 | .version:hover .version-tag { 586 | color: var(--accent-dark); 587 | } 588 | 589 | .version-date { 590 | font-size: 0.9rem; 591 | font-weight: 500; 592 | color: var(--accent-dark); 593 | transition: all 0.3s ease; 594 | margin-left: 10px; 595 | opacity: 0.8; 596 | } 597 | 598 | .version-changes { 599 | list-style-type: none; 600 | } 601 | 602 | .version-changes li { 603 | margin-bottom: 0.5rem; 604 | display: flex; 605 | align-items: flex-start; 606 | gap: 8px; 607 | transition: transform 0.3s ease; 608 | } 609 | 610 | .version-changes li:hover { 611 | transform: translateX(5px); 612 | } 613 | 614 | .change-type { 615 | display: inline-block; 616 | padding: 0.1rem 0.5rem; 617 | border-radius: 20px; 618 | font-size: 0.7rem; 619 | font-weight: 600; 620 | text-transform: uppercase; 621 | min-width: 60px; 622 | text-align: center; 623 | } 624 | 625 | .new { 626 | background-color: #f1f3f4; 627 | color: #424242; 628 | } 629 | 630 | .improved { 631 | background-color: #e8f5e9; 632 | color: #388e3c; 633 | } 634 | 635 | .fixed { 636 | background-color: #ffebee; 637 | color: #d32f2f; 638 | } 639 | 640 | .added { 641 | background-color: #f5f5f5; 642 | color: #616161; 643 | } 644 | 645 | .removed { 646 | background-color: #e53935; 647 | color: white; 648 | } 649 | 650 | .req-list { 651 | list-style: none; 652 | display: grid; 653 | grid-template-columns: 1fr; 654 | gap: 1rem; 655 | } 656 | 657 | @media (min-width: 768px) { 658 | .req-list { 659 | grid-template-columns: repeat(2, 1fr); 660 | } 661 | } 662 | 663 | .req-list li { 664 | display: flex; 665 | align-items: center; 666 | gap: 10px; 667 | padding: 0.75rem; 668 | background: var(--light-bg); 669 | border-radius: var(--border-radius); 670 | transition: transform 0.3s ease; 671 | } 672 | 673 | .req-list li:hover { 674 | transform: translateY(-3px); 675 | background: white; 676 | border: 1px solid var(--accent-color); 677 | } 678 | 679 | .req-list i { 680 | color: var(--accent-color); 681 | font-size: 20px; 682 | } 683 | 684 | .action-buttons { 685 | display: flex; 686 | flex-direction: column; 687 | gap: 1rem; 688 | margin: 2rem 0; 689 | } 690 | 691 | @media (min-width: 500px) { 692 | .action-buttons { 693 | flex-direction: row; 694 | justify-content: center; 695 | } 696 | } 697 | 698 | .action-btn { 699 | display: inline-flex; 700 | align-items: center; 701 | justify-content: center; 702 | padding: 0.75rem 1.5rem; 703 | border-radius: 50px; 704 | font-weight: 500; 705 | text-decoration: none; 706 | transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); 707 | gap: 8px; 708 | width: 100%; 709 | } 710 | 711 | @media (min-width: 500px) { 712 | .action-btn { 713 | width: auto; 714 | min-width: 150px; 715 | } 716 | } 717 | 718 | .github-btn { 719 | background: #24292e; 720 | color: white; 721 | } 722 | 723 | .github-btn:hover { 724 | transform: translateY(-5px); 725 | box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); 726 | } 727 | 728 | .donate-btn { 729 | background: linear-gradient(45deg, #9c27b0, #673ab7); 730 | color: white; 731 | } 732 | 733 | .donate-btn:hover { 734 | transform: translateY(-5px); 735 | box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); 736 | } 737 | 738 | .action-btn i { 739 | font-size: 18px; 740 | } 741 | 742 | @media (max-width: 480px) { 743 | header h1 { 744 | font-size: 1.8rem; 745 | } 746 | 747 | .card-body { 748 | padding: 1rem; 749 | } 750 | 751 | .features { 752 | grid-template-columns: 1fr; 753 | } 754 | 755 | .version-header { 756 | flex-direction: column; 757 | align-items: flex-start; 758 | gap: 5px; 759 | } 760 | } 761 | 762 | .highlight { 763 | background-color: #f1f8e9; 764 | color: #558b2f; 765 | padding: 2px 6px; 766 | border-radius: 4px; 767 | font-weight: 500; 768 | } 769 | 770 | .installation-steps { 771 | display: grid; 772 | grid-template-columns: 1fr; 773 | gap: 1.5rem; 774 | margin-bottom: 1.5rem; 775 | } 776 | 777 | .step { 778 | display: flex; 779 | gap: 15px; 780 | align-items: flex-start; 781 | } 782 | 783 | .step-number { 784 | background: linear-gradient(45deg, var(--accent-color), var(--accent-dark)); 785 | color: white; 786 | width: 30px; 787 | height: 30px; 788 | border-radius: 50%; 789 | display: flex; 790 | align-items: center; 791 | justify-content: center; 792 | font-weight: 600; 793 | flex-shrink: 0; 794 | } 795 | 796 | .step-content { 797 | flex: 1; 798 | } 799 | 800 | .step-content h3 { 801 | font-size: 1.1rem; 802 | margin-bottom: 0.75rem; 803 | font-weight: 500; 804 | } 805 | 806 | .step-content p { 807 | margin-top: 0.5rem; 808 | } 809 | 810 | #installation { 811 | margin-bottom: 3rem; 812 | } 813 | 814 | .modal { 815 | display: none; 816 | position: fixed; 817 | top: 0; 818 | left: 0; 819 | width: 100%; 820 | height: 100%; 821 | background-color: rgba(0, 0, 0, 0.6); 822 | z-index: 1000; 823 | overflow: auto; 824 | backdrop-filter: blur(5px); 825 | } 826 | 827 | .modal-content { 828 | background-color: white; 829 | margin: 5vh auto; 830 | width: 90%; 831 | max-width: 600px; 832 | border-radius: var(--border-radius); 833 | box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2); 834 | animation: modalFadeIn 0.3s; 835 | max-height: 90vh; 836 | display: flex; 837 | flex-direction: column; 838 | } 839 | 840 | @keyframes modalFadeIn { 841 | from {opacity: 0; transform: translateY(-30px);} 842 | to {opacity: 1; transform: translateY(0);} 843 | } 844 | 845 | .modal-header { 846 | display: flex; 847 | justify-content: space-between; 848 | align-items: center; 849 | padding: 1.25rem 1.5rem; 850 | border-bottom: 1px solid var(--border-color); 851 | } 852 | 853 | .modal-header h2 { 854 | font-size: 1.25rem; 855 | font-weight: 500; 856 | display: flex; 857 | align-items: center; 858 | gap: 10px; 859 | color: var(--accent-dark); 860 | margin: 0; 861 | } 862 | 863 | .modal-header h2 i { 864 | color: #f44336; 865 | } 866 | 867 | .modal-close { 868 | font-size: 1.5rem; 869 | font-weight: 700; 870 | color: var(--accent-color); 871 | cursor: pointer; 872 | transition: all 0.2s; 873 | } 874 | 875 | .modal-close:hover { 876 | color: #f44336; 877 | transform: scale(1.1); 878 | } 879 | 880 | .modal-body { 881 | padding: 1.5rem; 882 | overflow-y: auto; 883 | } 884 | 885 | .modal-body p { 886 | margin-bottom: 1rem; 887 | line-height: 1.6; 888 | } 889 | 890 | .modal-body ul { 891 | margin: 1rem 0; 892 | padding-left: 1.5rem; 893 | } 894 | 895 | .modal-body li { 896 | margin-bottom: 0.5rem; 897 | position: relative; 898 | padding-left: 0.5rem; 899 | } 900 | 901 | .modal-body li::before { 902 | content: "•"; 903 | color: var(--accent-color); 904 | font-weight: bold; 905 | position: absolute; 906 | left: -1rem; 907 | } 908 | 909 | .modal-footer { 910 | padding: 1rem 1.5rem; 911 | border-top: 1px solid var(--border-color); 912 | display: flex; 913 | justify-content: flex-end; 914 | } 915 | 916 | .timeline { 917 | position: relative; 918 | margin: 0 0 0 20px; 919 | padding-left: 20px; 920 | } 921 | 922 | .timeline-item { 923 | position: relative; 924 | margin-bottom: 30px; 925 | padding-bottom: 15px; 926 | } 927 | 928 | .timeline-item:last-child { 929 | margin-bottom: 0; 930 | padding-bottom: 0; 931 | } 932 | 933 | .timeline-dot { 934 | position: absolute; 935 | left: -31px; 936 | top: 0; 937 | width: 20px; 938 | height: 20px; 939 | border-radius: 50%; 940 | background: white; 941 | border: 3px solid var(--accent-color); 942 | z-index: 1; 943 | transition: all 0.3s ease; 944 | } 945 | 946 | .timeline-item:hover .timeline-dot { 947 | transform: scale(1.2); 948 | background: var(--accent-color); 949 | } 950 | 951 | .timeline-date { 952 | font-size: 0.9rem; 953 | font-weight: 500; 954 | color: var(--accent-dark); 955 | transition: all 0.3s ease; 956 | margin-left: 10px; 957 | opacity: 0.8; 958 | } 959 | 960 | .timeline-item:hover .timeline-date { 961 | color: var(--accent-color); 962 | font-weight: 600; 963 | } 964 | 965 | @media (max-width: 768px) { 966 | .timeline { 967 | margin-left: 0; 968 | } 969 | 970 | .timeline-date { 971 | font-size: 0.8rem; 972 | } 973 | 974 | .version-header { 975 | flex-direction: column; 976 | align-items: flex-start; 977 | gap: 5px; 978 | } 979 | 980 | .timeline-date { 981 | margin-left: 0; 982 | } 983 | } 984 | 985 | .timeline-content { 986 | position: relative; 987 | margin-left: 15px; 988 | } 989 | 990 | .timeline-card { 991 | background: white; 992 | border-radius: var(--border-radius); 993 | padding: 20px; 994 | margin-top: 15px; 995 | transition: all 0.3s ease; 996 | border: 1px solid var(--border-color); 997 | } 998 | 999 | .timeline-item:hover .timeline-card { 1000 | transform: translateY(-3px); 1001 | border-color: var(--accent-color); 1002 | } 1003 | 1004 | .version-tag { 1005 | font-weight: 600; 1006 | color: var(--accent-color); 1007 | font-size: 1.1rem; 1008 | transition: color 0.3s ease; 1009 | background: rgba(117, 117, 117, 0.1); 1010 | padding: 5px 12px; 1011 | border-radius: 50px; 1012 | display: inline-block; 1013 | } 1014 | 1015 | .timeline-item:hover .version-tag { 1016 | color: white; 1017 | background: var(--accent-color); 1018 | } 1019 | 1020 | .version-changes { 1021 | list-style-type: none; 1022 | padding: 0; 1023 | margin: 0; 1024 | } 1025 | 1026 | .version-changes li { 1027 | margin-bottom: 0.75rem; 1028 | padding-left: 0; 1029 | position: relative; 1030 | display: flex; 1031 | align-items: flex-start; 1032 | gap: 10px; 1033 | transition: transform 0.3s ease; 1034 | } 1035 | 1036 | .version-changes li:last-child { 1037 | margin-bottom: 0; 1038 | } 1039 | 1040 | .version-changes li:hover { 1041 | transform: translateX(5px); 1042 | } 1043 | 1044 | .change-type { 1045 | display: inline-block; 1046 | padding: 0.1rem 0.5rem; 1047 | border-radius: 20px; 1048 | font-size: 0.7rem; 1049 | font-weight: 600; 1050 | text-transform: uppercase; 1051 | min-width: 60px; 1052 | text-align: center; 1053 | flex-shrink: 0; 1054 | } 1055 | 1056 | @media (max-width: 768px) { 1057 | .timeline { 1058 | margin-left: 0; 1059 | } 1060 | 1061 | .timeline-date { 1062 | position: relative; 1063 | left: 0; 1064 | top: auto; 1065 | margin-bottom: 10px; 1066 | text-align: left; 1067 | width: auto; 1068 | font-size: 0.8rem; 1069 | color: var(--accent-color); 1070 | } 1071 | } 1072 | 1073 | .log-output { 1074 | background-color: #1e1e1e; 1075 | color: #f0f0f0; 1076 | font-family: "Consolas", "Monaco", monospace; 1077 | padding: 15px; 1078 | border-radius: 5px; 1079 | white-space: pre-wrap; 1080 | word-break: break-word; 1081 | max-height: 400px; 1082 | overflow-y: auto; 1083 | margin-top: 15px; 1084 | border: 1px solid #333; 1085 | font-size: 13px; 1086 | line-height: 1.5; 1087 | scrollbar-width: thin; 1088 | scrollbar-color: #555 #1e1e1e; 1089 | } 1090 | 1091 | .log-output::-webkit-scrollbar { 1092 | width: 8px; 1093 | height: 8px; 1094 | } 1095 | 1096 | .log-output::-webkit-scrollbar-track { 1097 | background: #1e1e1e; 1098 | border-radius: 4px; 1099 | } 1100 | 1101 | .log-output::-webkit-scrollbar-thumb { 1102 | background: #555; 1103 | border-radius: 4px; 1104 | } 1105 | 1106 | .log-output::-webkit-scrollbar-thumb:hover { 1107 | background: #777; 1108 | } 1109 | 1110 | .result-table { 1111 | width: 100%; 1112 | margin-top: 1rem; 1113 | border-collapse: collapse; 1114 | font-family: 'Consolas', 'Monaco', monospace; 1115 | font-size: 0.9rem; 1116 | } 1117 | 1118 | .result-table th { 1119 | text-align: left; 1120 | background-color: #f5f5f5; 1121 | padding: 0.5rem 1rem; 1122 | border: 1px solid var(--border-color); 1123 | font-weight: 600; 1124 | } 1125 | 1126 | .result-table td { 1127 | padding: 0.5rem 1rem; 1128 | border: 1px solid var(--border-color); 1129 | word-break: break-all; 1130 | } 1131 | 1132 | .result-table tr:nth-child(even) { 1133 | background-color: #fafafa; 1134 | } 1135 | 1136 | .action-grid { 1137 | display: grid; 1138 | grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); 1139 | gap: 15px; 1140 | margin-bottom: 20px; 1141 | } 1142 | 1143 | .action-grid .btn { 1144 | width: 100%; 1145 | text-align: left; 1146 | display: flex; 1147 | align-items: center; 1148 | gap: 8px; 1149 | padding: 12px 16px; 1150 | font-weight: 500; 1151 | transition: all 0.2s ease; 1152 | } 1153 | 1154 | .action-grid .btn:hover { 1155 | transform: translateY(-2px); 1156 | } 1157 | 1158 | .btn-warning { 1159 | background: #ff9800; 1160 | color: #fff; 1161 | } 1162 | 1163 | .btn-info { 1164 | background: #03a9f4; 1165 | color: #fff; 1166 | } 1167 | 1168 | .btn-success { 1169 | background: #4caf50; 1170 | color: #fff; 1171 | } 1172 | 1173 | .atomic-email-container { 1174 | margin-top: 1rem; 1175 | } 1176 | 1177 | .email-details { 1178 | background-color: #f8f9fa; 1179 | border-radius: 8px; 1180 | padding: 1rem; 1181 | margin-top: 1rem; 1182 | border: 1px solid #e9ecef; 1183 | box-shadow: none; 1184 | } 1185 | 1186 | .detail-item { 1187 | display: flex; 1188 | align-items: center; 1189 | margin-bottom: 0.75rem; 1190 | padding: 0.75rem 1rem; 1191 | background-color: #fff; 1192 | border-radius: 4px; 1193 | box-shadow: none; 1194 | border: 1px solid #f1f3f5; 1195 | } 1196 | 1197 | .detail-label { 1198 | font-weight: 500; 1199 | min-width: 120px; 1200 | color: #333; 1201 | font-family: 'Google Sans', -apple-system, sans-serif; 1202 | } 1203 | 1204 | .detail-value { 1205 | flex: 1; 1206 | font-family: 'Google Sans', -apple-system, sans-serif; 1207 | word-break: break-all; 1208 | padding: 0.25rem 0.5rem; 1209 | background-color: #f1f3f5; 1210 | border-radius: 4px; 1211 | font-size: 0.9rem; 1212 | } 1213 | 1214 | .copy-btn { 1215 | background: none; 1216 | border: none; 1217 | color: #0066cc; 1218 | cursor: pointer; 1219 | padding: 0.25rem 0.5rem; 1220 | border-radius: 4px; 1221 | transition: all 0.2s; 1222 | margin-left: 0.5rem; 1223 | } 1224 | 1225 | .copy-btn:hover { 1226 | background-color: #e9ecef; 1227 | } 1228 | 1229 | .verification-status { 1230 | display: flex; 1231 | align-items: center; 1232 | gap: 1rem; 1233 | padding: 1rem; 1234 | background-color: #fff8e1; 1235 | border-radius: 8px; 1236 | margin-top: 1rem; 1237 | } 1238 | 1239 | .loading-spinner { 1240 | width: 20px; 1241 | height: 20px; 1242 | border: 3px solid rgba(255, 202, 40, 0.3); 1243 | border-radius: 50%; 1244 | border-top-color: #ffca28; 1245 | animation: spin 1s ease-in-out infinite; 1246 | } 1247 | 1248 | @keyframes spin { 1249 | to { transform: rotate(360deg); } 1250 | } 1251 | 1252 | .hidden { 1253 | display: none; 1254 | } 1255 | 1256 | .mt-4 { 1257 | margin-top: 1rem; 1258 | } 1259 | 1260 | .verification-code-container { 1261 | border-top: 1px solid #e9ecef; 1262 | padding-top: 0.75rem; 1263 | margin-top: 0.75rem; 1264 | background-color: #e8f5e9; 1265 | box-shadow: none; 1266 | } 1267 | 1268 | #verification-code { 1269 | font-size: 1.1rem; 1270 | letter-spacing: 0.5px; 1271 | font-weight: 500; 1272 | color: #1b5e20; 1273 | background-color: #c8e6c9; 1274 | font-family: 'Google Sans', -apple-system, sans-serif; 1275 | } 1276 | 1277 | .copied { 1278 | animation: copy-animation 1.5s ease; 1279 | } 1280 | 1281 | @keyframes copy-animation { 1282 | 0% { background-color: #c8e6c9; } 1283 | 70% { background-color: #c8e6c9; } 1284 | 100% { background-color: transparent; } 1285 | } 1286 | 1287 | .toast-container { 1288 | position: fixed; 1289 | bottom: 20px; 1290 | right: 20px; 1291 | z-index: 9999; 1292 | display: flex; 1293 | flex-direction: column; 1294 | gap: 10px; 1295 | max-width: 350px; 1296 | } 1297 | 1298 | .toast { 1299 | background-color: #fff; 1300 | border-radius: 8px; 1301 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 1302 | overflow: hidden; 1303 | display: flex; 1304 | align-items: center; 1305 | justify-content: space-between; 1306 | animation: toast-in 0.3s ease forwards; 1307 | opacity: 0; 1308 | transform: translateY(20px); 1309 | } 1310 | 1311 | .toast-content { 1312 | display: flex; 1313 | align-items: flex-start; 1314 | gap: 10px; 1315 | padding: 12px 15px; 1316 | flex: 1; 1317 | } 1318 | 1319 | .toast-content i { 1320 | margin-top: 3px; 1321 | } 1322 | 1323 | .toast-content span { 1324 | font-size: 14px; 1325 | color: #333; 1326 | } 1327 | 1328 | .toast-close { 1329 | background: none; 1330 | border: none; 1331 | color: #777; 1332 | cursor: pointer; 1333 | padding: 8px 12px; 1334 | transition: all 0.2s; 1335 | } 1336 | 1337 | .toast-close:hover { 1338 | color: #333; 1339 | } 1340 | 1341 | .toast-error i { 1342 | color: #f44336; 1343 | } 1344 | 1345 | .toast-success i { 1346 | color: #4caf50; 1347 | } 1348 | 1349 | .toast-info i { 1350 | color: #2196f3; 1351 | } 1352 | 1353 | .toast-warning i { 1354 | color: #ff9800; 1355 | } 1356 | 1357 | .toast-closing { 1358 | animation: toast-out 0.3s ease forwards; 1359 | } 1360 | 1361 | @keyframes toast-in { 1362 | from { 1363 | opacity: 0; 1364 | transform: translateY(20px); 1365 | } 1366 | to { 1367 | opacity: 1; 1368 | transform: translateY(0); 1369 | } 1370 | } 1371 | 1372 | @keyframes toast-out { 1373 | from { 1374 | opacity: 1; 1375 | transform: translateY(0); 1376 | } 1377 | to { 1378 | opacity: 0; 1379 | transform: translateY(20px); 1380 | } 1381 | } 1382 | 1383 | .rotating { 1384 | display: flex; 1385 | align-items: center; 1386 | justify-content: center; 1387 | width: 20px; 1388 | height: 20px; 1389 | position: relative; 1390 | } 1391 | 1392 | .rotating i { 1393 | position: absolute; 1394 | animation: spin 1s linear infinite; 1395 | transform-origin: center; 1396 | top: 0; 1397 | left: 0; 1398 | width: 100%; 1399 | height: 100%; 1400 | display: flex; 1401 | align-items: center; 1402 | justify-content: center; 1403 | } 1404 | 1405 | @keyframes spin { 1406 | 0% { transform: rotate(0deg); } 1407 | 100% { transform: rotate(360deg); } 1408 | } 1409 | 1410 | .btn-content { 1411 | display: flex; 1412 | align-items: center; 1413 | justify-content: center; 1414 | gap: 8px; 1415 | width: 100%; 1416 | } 1417 | 1418 | .email-generator-container { 1419 | margin-top: 1rem; 1420 | padding: 1rem; 1421 | background: var(--light-bg); 1422 | border-radius: var(--border-radius); 1423 | text-align: center; 1424 | } 1425 | 1426 | .email-generator-container p { 1427 | margin-bottom: 0.5rem; 1428 | color: var(--text-color); 1429 | } 1430 | 1431 | .email-generator-container p:last-child { 1432 | margin-bottom: 0; 1433 | font-size: 0.9rem; 1434 | opacity: 0.8; 1435 | } 1436 | --------------------------------------------------------------------------------