cpp.html (44046B)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 <title>Amit Dutta // C++ Compiler</title> 7 <link rel="icon" type="image/x-icon" href="https://compiler.aranag.site/logo.png"> 8 9 <!-- Fonts & Icons --> 10 <link rel="preconnect" href="https://fonts.googleapis.com"> 11 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 12 <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet"> 13 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"> 14 15 <!-- Tailwind CSS --> 16 <script src="https://cdn.tailwindcss.com"></script> 17 18 <!-- CodeMirror 5 (Professional Editor) --> 19 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css"> 20 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/dracula.min.css"> 21 <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script> 22 <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/clike/clike.min.js"></script> 23 <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/edit/closebrackets.min.js"></script> 24 <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/edit/matchbrackets.min.js"></script> 25 <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/selection/active-line.min.js"></script> 26 27 <!-- Xterm.js --> 28 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" /> 29 <script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js"></script> 30 <script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script> 31 32 <style> 33 :root { 34 --bg-main: #050505; 35 --accent: #bef264; /* Lime 300 */ 36 --text-muted: #737373; 37 --border-color: rgba(255, 255, 255, 0.1); 38 } 39 40 body { 41 font-family: 'Inter', sans-serif; 42 background-color: var(--bg-main); 43 color: #f5f5f5; 44 height: 100vh; 45 height: 100dvh; 46 overflow: hidden; 47 display: flex; 48 flex-direction: column; 49 } 50 51 /* Portfolio Aesthetic */ 52 .font-mono { font-family: 'JetBrains Mono', monospace; } 53 .font-heading { font-family: 'Space Grotesk', sans-serif; } 54 55 /* Noise Overlay */ 56 .bg-noise { 57 position: fixed; top: 0; left: 0; width: 100%; height: 100%; 58 pointer-events: none; z-index: 0; opacity: 0.03; 59 background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E"); 60 } 61 62 /* CRT Scanline */ 63 .crt-scanline { 64 position: fixed; top: 0; left: 0; width: 100%; height: 2px; 65 background: rgba(190, 242, 100, 0.1); opacity: 0.5; 66 pointer-events: none; z-index: 50; 67 animation: scanline 8s linear infinite; 68 } 69 @keyframes scanline { 0% { top: -10vh; } 100% { top: 110vh; } } 70 71 /* CodeMirror Customization */ 72 .CodeMirror { 73 height: 100% !important; 74 font-family: 'JetBrains Mono', monospace; 75 font-size: 13px; 76 background-color: #0c0f16 !important; /* Slightly lighter than pure black */ 77 color: #f8f8f2; 78 } 79 .CodeMirror-gutters { 80 background-color: #0c0f16 !important; 81 border-right: 1px solid rgba(255,255,255,0.05) !important; 82 } 83 .CodeMirror-linenumber { 84 color: #555 !important; 85 } 86 .CodeMirror-cursor { 87 border-left: 2px solid var(--accent) !important; 88 } 89 .CodeMirror-selected { 90 background: rgba(190, 242, 100, 0.2) !important; 91 } 92 .cm-s-dracula .CodeMirror-activeline-background { 93 background: rgba(255, 255, 255, 0.03) !important; 94 } 95 96 /* Scrollbar styling */ 97 .CodeMirror-scroll::-webkit-scrollbar, 98 .xterm-viewport::-webkit-scrollbar, 99 .custom-scrollbar::-webkit-scrollbar { 100 width: 6px; height: 6px; 101 } 102 .CodeMirror-scroll::-webkit-scrollbar-track, 103 .xterm-viewport::-webkit-scrollbar-track, 104 .custom-scrollbar::-webkit-scrollbar-track { 105 background: #050505; 106 } 107 .CodeMirror-scroll::-webkit-scrollbar-thumb, 108 .xterm-viewport::-webkit-scrollbar-thumb, 109 .custom-scrollbar::-webkit-scrollbar-thumb { 110 background: #333; border-radius: 3px; 111 } 112 113 /* Mobile Helper Bar */ 114 #mobile-helper-bar { 115 display: none; /* Shown via JS on mobile */ 116 background: #151515; 117 border-top: 1px solid rgba(255,255,255,0.1); 118 padding: 8px; 119 overflow-x: auto; 120 white-space: nowrap; 121 -webkit-overflow-scrolling: touch; 122 } 123 @media (max-width: 768px) { 124 #mobile-helper-bar { display: flex; gap: 8px; } 125 } 126 .helper-key { 127 background: rgba(255,255,255,0.1); 128 color: var(--accent); 129 border: 1px solid rgba(255,255,255,0.1); 130 border-radius: 4px; 131 padding: 6px 12px; 132 font-family: 'JetBrains Mono', monospace; 133 font-size: 12px; 134 font-weight: bold; 135 cursor: pointer; 136 user-select: none; 137 } 138 .helper-key:active { background: var(--accent); color: black; } 139 140 /* Toast Notification */ 141 .toast { 142 position: fixed; 143 bottom: 2rem; 144 left: 50%; 145 transform: translateX(-50%) translateY(100px); 146 background: #1a1a1a; 147 border: 1px solid var(--accent); 148 color: var(--accent); 149 padding: 0.5rem 1.5rem; 150 border-radius: 9999px; 151 font-family: 'JetBrains Mono', monospace; 152 font-size: 0.8rem; 153 transition: transform 0.3s ease; 154 z-index: 100; 155 } 156 .toast.show { transform: translateX(-50%) translateY(0); } 157 158 /* Connection Dot Pulse */ 159 .pulse-dot { 160 width: 8px; 161 height: 8px; 162 border-radius: 50%; 163 display: inline-block; 164 box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7); 165 animation: pulse-white 2s infinite; 166 } 167 .pulse-dot.connected { 168 background-color: #bef264; /* Lime */ 169 box-shadow: 0 0 0 0 rgba(190, 242, 100, 0.7); 170 animation: pulse-lime 2s infinite; 171 } 172 .pulse-dot.disconnected { 173 background-color: #ef4444; /* Red */ 174 box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); 175 animation: pulse-red 2s infinite; 176 } 177 178 @keyframes pulse-lime { 179 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(190, 242, 100, 0.7); } 180 70% { transform: scale(1); box-shadow: 0 0 0 6px rgba(190, 242, 100, 0); } 181 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(190, 242, 100, 0); } 182 } 183 @keyframes pulse-red { 184 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); } 185 70% { transform: scale(1); box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); } 186 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } 187 } 188 189 /* AI Text Overlay */ 190 .ai-overlay { 191 position: absolute; 192 bottom: 10px; 193 right: 20px; 194 background: rgba(20, 20, 20, 0.9); 195 border: 1px solid #ef4444; 196 color: #fca5a5; 197 padding: 8px 12px; 198 border-radius: 6px; 199 font-family: 'JetBrains Mono', monospace; 200 font-size: 12px; 201 z-index: 60; 202 display: none; 203 backdrop-filter: blur(4px); 204 animation: slideIn 0.3s ease-out; 205 box-shadow: 0 4px 12px rgba(0,0,0,0.5); 206 } 207 .ai-overlay.visible { display: flex; align-items: center; gap: 8px; } 208 209 .ai-link { 210 color: #fff; 211 text-decoration: underline; 212 text-decoration-style: dotted; 213 text-underline-offset: 4px; 214 cursor: pointer; 215 font-weight: bold; 216 transition: color 0.2s; 217 } 218 .ai-link:hover { color: #bef264; text-decoration-style: solid; } 219 220 @keyframes slideIn { 221 from { transform: translateY(20px); opacity: 0; } 222 to { transform: translateY(0); opacity: 1; } 223 } 224 225 /* Modals (AI & Clear) */ 226 .modal-overlay { 227 position: fixed; top: 0; left: 0; width: 100%; height: 100%; 228 background: rgba(0,0,0,0.8); backdrop-filter: blur(5px); 229 z-index: 200; display: none; align-items: center; justify-content: center; 230 } 231 .modal-overlay.open { display: flex; } 232 233 .modal-content { 234 background: #0f0f0f; 235 border: 1px solid var(--accent); 236 width: 90%; max-width: 600px; 237 border-radius: 12px; 238 display: flex; flex-direction: column; 239 box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.04); 240 overflow: hidden; 241 animation: modalPop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); 242 } 243 244 @keyframes modalPop { 245 0% { transform: scale(0.9); opacity: 0; } 246 100% { transform: scale(1); opacity: 1; } 247 } 248 249 /* Editor Toolbar Buttons */ 250 .editor-btn { 251 background: rgba(255,255,255,0.05); 252 border: 1px solid rgba(255,255,255,0.1); 253 color: #737373; 254 width: 32px; 255 height: 32px; 256 display: flex; 257 align-items: center; 258 justify-content: center; 259 border-radius: 4px; 260 transition: all 0.2s; 261 cursor: pointer; 262 } 263 .editor-btn:hover { 264 background: rgba(255,255,255,0.1); 265 color: #fff; 266 border-color: rgba(255,255,255,0.3); 267 } 268 .editor-btn.danger:hover { 269 background: rgba(239, 68, 68, 0.1); 270 color: #ef4444; 271 border-color: #ef4444; 272 } 273 </style> 274 </head> 275 <body class="antialiased selection:bg-lime-300 selection:text-black"> 276 277 <div class="bg-noise"></div> 278 <div class="crt-scanline"></div> 279 <div id="toast" class="toast">Link Copied to Clipboard!</div> 280 281 <!-- AI Overlay in Terminal --> 282 <div id="ai-suggestion" class="ai-overlay"> 283 <i class="fas fa-exclamation-triangle text-red-500"></i> 284 <span>Error detected. <span id="ask-ai-link" class="ai-link">Ask AI</span> to fix it.</span> 285 </div> 286 287 <!-- Clear Confirmation Modal --> 288 <div id="clear-modal" class="modal-overlay"> 289 <div class="modal-content max-w-[320px] p-0 border-red-500/50"> 290 <div class="bg-red-500/10 px-6 py-4 border-b border-red-500/20"> 291 <h3 class="font-heading font-bold text-lg text-red-500 flex items-center gap-2"> 292 <i class="fas fa-exclamation-circle"></i> CLEAR CODE 293 </h3> 294 </div> 295 <div class="p-6 text-center"> 296 <p class="text-neutral-300 text-sm font-sans mb-6">Are you sure you want to clear the editor? This action will remove all current code.</p> 297 <div class="flex justify-center gap-3"> 298 <button id="cancel-clear-btn" class="px-4 py-2 rounded text-xs font-mono font-bold uppercase text-neutral-400 hover:text-white hover:bg-white/10 transition-all border border-transparent">Cancel</button> 299 <button id="confirm-clear-btn" class="bg-red-500/10 hover:bg-red-500 hover:text-black text-red-500 border border-red-500/20 px-4 py-2 rounded text-xs font-mono font-bold uppercase transition-all flex items-center gap-2"> 300 <i class="fas fa-trash-alt"></i> Confirm 301 </button> 302 </div> 303 </div> 304 </div> 305 </div> 306 307 <!-- AI Fix Modal --> 308 <div id="ai-modal" class="modal-overlay"> 309 <div class="modal-content max-h-[80vh]"> 310 <!-- Modal Header --> 311 <div class="bg-white/5 px-6 py-4 border-b border-white/10 flex justify-between items-center"> 312 <h3 class="font-heading font-bold text-lg text-lime-300 flex items-center gap-2"> 313 <i class="fas fa-robot"></i> AI Diagnosis 314 </h3> 315 <button id="close-modal" class="text-neutral-500 hover:text-white transition-colors"> 316 <i class="fas fa-times"></i> 317 </button> 318 </div> 319 320 <!-- Modal Body --> 321 <div class="p-6 overflow-y-auto custom-scrollbar flex-1"> 322 <div id="ai-loading" class="hidden flex flex-col items-center justify-center py-8 gap-4"> 323 <div class="pulse-dot connected" style="width: 12px; height: 12px;"></div> 324 <p class="font-mono text-neutral-400 text-sm animate-pulse">Analyzing compile error...</p> 325 </div> 326 327 <div id="ai-result" class="hidden space-y-4"> 328 <div> 329 <h4 class="font-mono text-xs uppercase text-neutral-500 mb-2">Explanation</h4> 330 <p id="ai-explanation" class="text-sm text-neutral-200 leading-relaxed font-sans"></p> 331 </div> 332 333 <div> 334 <h4 class="font-mono text-xs uppercase text-neutral-500 mb-2">Suggested Fix</h4> 335 <div class="relative group"> 336 <pre id="ai-code-preview" class="bg-black/30 border border-white/10 p-3 rounded text-xs font-mono text-lime-100 overflow-x-auto custom-scrollbar"></pre> 337 </div> 338 </div> 339 </div> 340 341 <div id="ai-error-msg" class="hidden text-red-400 text-sm font-mono text-center py-4"></div> 342 </div> 343 344 <!-- Modal Footer --> 345 <div class="bg-white/5 px-6 py-4 border-t border-white/10 flex justify-end gap-3"> 346 <button id="cancel-fix-btn" class="px-4 py-2 rounded text-xs font-mono font-bold uppercase text-neutral-400 hover:text-white hover:bg-white/10 transition-all">Dismiss</button> 347 <button id="apply-fix-btn" disabled class="bg-lime-300/10 hover:bg-lime-300 hover:text-black text-lime-300 border border-lime-300/20 px-4 py-2 rounded text-xs font-mono font-bold uppercase transition-all flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"> 348 <i class="fas fa-check"></i> Apply Fix 349 </button> 350 </div> 351 </div> 352 </div> 353 354 <!-- Header (Responsive) --> 355 <header class="h-auto md:h-16 flex flex-col md:flex-row items-center justify-between px-4 py-3 md:px-6 shrink-0 border-b border-white/10 z-20 bg-neutral-900/50 backdrop-blur-md gap-3 md:gap-4"> 356 357 <!-- Top Row: Title & Editor Tools --> 358 <div class="flex items-center justify-between w-full md:w-auto gap-6"> 359 <h1 class="text-lg md:text-xl font-heading font-bold text-white tracking-tight flex items-center"> 360 <span class="text-lime-300 mr-2">//</span>CPP.COMPILER 361 <span id="conn-dot" class="pulse-dot disconnected ml-3" title="Disconnected"></span> 362 </h1> 363 364 <!-- Editor Tools --> 365 <div class="flex items-center gap-2"> 366 <button id="undo-btn" class="editor-btn" title="Undo (Ctrl+Z)"> 367 <i class="fas fa-undo text-xs"></i> 368 </button> 369 <button id="redo-btn" class="editor-btn" title="Redo (Ctrl+Y)"> 370 <i class="fas fa-redo text-xs"></i> 371 </button> 372 <div class="w-px h-4 bg-white/10 mx-1"></div> 373 <button id="clear-code-btn" class="editor-btn danger" title="Clear Code"> 374 <i class="fas fa-times text-xs"></i> 375 </button> 376 </div> 377 378 <!-- Mobile Only: Exit Link --> 379 <a href="./index.html" class="md:hidden font-mono text-[10px] text-neutral-500 hover:text-white uppercase tracking-wider transition-colors ml-auto">[ EXIT ]</a> 380 </div> 381 382 <!-- Middle: Connection Text --> 383 <div class="hidden md:flex items-center gap-2 font-mono text-xs text-neutral-500"> 384 <span id="connection-status">Connecting to server...</span> 385 </div> 386 387 <!-- Bottom/Right: Buttons --> 388 <div class="flex items-center gap-2 w-full md:w-auto justify-end"> 389 <a href="./index.html" class="hidden md:inline font-mono text-[10px] text-neutral-500 hover:text-white uppercase tracking-wider transition-colors mr-2">[ EXIT ]</a> 390 391 <button id="download-btn" class="bg-white/5 hover:bg-neutral-800 text-white border border-white/10 px-3 py-2 rounded font-mono text-[10px] font-bold uppercase tracking-wider flex items-center justify-center transition-all" title="Download .cpp"> 392 <i class="fas fa-download"></i> 393 </button> 394 395 <button id="reset-btn" class="bg-white/5 hover:bg-neutral-800 text-white border border-white/10 px-3 py-2 rounded font-mono text-[10px] font-bold uppercase tracking-wider flex items-center justify-center transition-all" title="Reset Terminal"> 396 <i class="fas fa-rotate-left"></i> 397 </button> 398 399 <button id="share-btn" class="bg-white/5 hover:bg-neutral-800 text-white border border-white/10 px-3 py-2 rounded font-mono text-[10px] font-bold uppercase tracking-wider flex items-center justify-center transition-all" title="Share Code"> 400 <i class="fas fa-share-alt"></i> 401 </button> 402 403 <button id="run-btn" disabled class="bg-white/5 hover:bg-lime-300 hover:text-black disabled:opacity-50 disabled:cursor-not-allowed text-white border border-white/10 px-4 py-2 rounded font-mono text-xs font-bold uppercase tracking-wider flex items-center gap-2 transition-all flex-1 md:flex-none justify-center shadow-lg shadow-black/50"> 404 <i class="fas fa-play text-[10px]"></i> <span class="md:hidden lg:inline">EXECUTE</span><span class="hidden md:inline lg:hidden">RUN</span> 405 </button> 406 </div> 407 </header> 408 409 <!-- Main Workspace --> 410 <div class="flex-1 flex flex-col md:flex-row min-h-0 z-10 relative"> 411 412 <!-- Code Editor --> 413 <div class="flex-1 flex flex-col h-[60%] md:h-auto min-h-0 border-b md:border-b-0 md:border-r border-white/10 relative group"> 414 <div class="bg-black/20 px-4 py-2 text-[10px] font-mono text-lime-300 uppercase border-b border-white/10 flex justify-between items-center shrink-0"> 415 <span>SRC // main.cpp</span> 416 <span class="text-neutral-600">G++ 9.4.0</span> 417 </div> 418 419 <!-- CodeMirror Container --> 420 <div id="editor-container" class="relative bg-[#0c0f16] flex-1 overflow-hidden"> 421 <textarea id="code-input" class="hidden">#include <iostream> 422 423 int main() { 424 std::cout << "Hello C++ Compiler!" << std::endl; 425 return 0; 426 }</textarea> 427 </div> 428 429 <!-- Mobile Helper Bar --> 430 <div id="mobile-helper-bar"> 431 <div class="helper-key" data-char=" ">Tab</div> 432 <div class="helper-key" data-char=";">;</div> 433 <div class="helper-key" data-char="=">=</div> 434 <div class="helper-key" data-char="()">()</div> 435 <div class="helper-key" data-char="[]">[]</div> 436 <div class="helper-key" data-char="{}">{}</div> 437 <div class="helper-key" data-char='""'>""</div> 438 <div class="helper-key" data-char="''">''</div> 439 <div class="helper-key" data-char="#">#</div> 440 <div class="helper-key" data-char="<<"><<</div> 441 <div class="helper-key" data-char=">>">>></div> 442 <div class="helper-key" data-char="cout">cout</div> 443 </div> 444 </div> 445 446 <!-- Terminal --> 447 <div class="flex-1 flex flex-col h-[40%] md:h-auto min-h-0 bg-[#050505]"> 448 <div class="bg-black/20 px-4 py-2 text-[10px] font-mono text-lime-300 uppercase border-b border-white/10 flex justify-between items-center shrink-0"> 449 <span>OUT // TERMINAL</span> 450 <div class="flex gap-2"> 451 <button id="copy-btn" class="text-neutral-500 hover:text-white transition-colors" title="Copy Output"><i class="fas fa-copy"></i></button> 452 <span class="text-neutral-600">XTERM.JS</span> 453 </div> 454 </div> 455 <div class="flex-1 relative"> 456 <div id="terminal-container" class="absolute inset-0 w-full h-full p-2 overflow-hidden"></div> 457 </div> 458 </div> 459 </div> 460 461 <script type="module"> 462 import { initializeApp } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js"; 463 import { getFirestore, doc, getDoc, setDoc } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js"; 464 import { getAuth, signInAnonymously } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-auth.js"; 465 466 // --- Firebase Config --- 467 const firebaseConfig = { 468 apiKey: "AIzaSyC8U49NgW7EtYBeKL0Sw08-QvM_Bl59zDw", 469 authDomain: "compiler-aranag-site.firebaseapp.com", 470 projectId: "compiler-aranag-site", 471 storageBucket: "compiler-aranag-site.firebasestorage.app", 472 messagingSenderId: "558890639400", 473 appId: "1:558890639400:web:125525d10ab51696a5951d", 474 measurementId: "G-KH9BGSRRGY" 475 }; 476 477 // Initialize Firebase 478 const app = initializeApp(firebaseConfig); 479 const db = getFirestore(app); 480 const auth = getAuth(app); 481 482 // Attempt initial sign-in 483 signInAnonymously(auth).catch((error) => { 484 console.warn("Auto-Auth failed. App will try unauthenticated access.", error.code); 485 }); 486 487 // --- Helper: Generate ID --- 488 function generateId(length = 7) { 489 const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 490 let result = ''; 491 for (let i = 0; i < length; i++) { 492 result += chars.charAt(Math.floor(Math.random() * chars.length)); 493 } 494 return result; 495 } 496 497 // --- DOM Elements --- 498 const runBtn = document.getElementById('run-btn'); 499 const resetBtn = document.getElementById('reset-btn'); 500 const shareBtn = document.getElementById('share-btn'); 501 const copyBtn = document.getElementById('copy-btn'); 502 const downloadBtn = document.getElementById('download-btn'); 503 const connDot = document.getElementById('conn-dot'); 504 const connectionStatus = document.getElementById('connection-status'); 505 const toast = document.getElementById('toast'); 506 507 // Editor Tool Elements 508 const undoBtn = document.getElementById('undo-btn'); 509 const redoBtn = document.getElementById('redo-btn'); 510 const clearCodeBtn = document.getElementById('clear-code-btn'); 511 const clearModal = document.getElementById('clear-modal'); 512 const cancelClearBtn = document.getElementById('cancel-clear-btn'); 513 const confirmClearBtn = document.getElementById('confirm-clear-btn'); 514 515 // AI Elements 516 const aiSuggestion = document.getElementById('ai-suggestion'); 517 const askAiLink = document.getElementById('ask-ai-link'); 518 const aiModal = document.getElementById('ai-modal'); 519 const closeModal = document.getElementById('close-modal'); 520 const cancelFixBtn = document.getElementById('cancel-fix-btn'); 521 const applyFixBtn = document.getElementById('apply-fix-btn'); 522 const aiLoading = document.getElementById('ai-loading'); 523 const aiResult = document.getElementById('ai-result'); 524 const aiExplanation = document.getElementById('ai-explanation'); 525 const aiCodePreview = document.getElementById('ai-code-preview'); 526 const aiErrorMsg = document.getElementById('ai-error-msg'); 527 528 // --- State --- 529 let socket = null; 530 let currentLine = ""; 531 let startTime = 0; 532 let lastSuggestedCode = ""; 533 let outputBuffer = ""; 534 535 // --- CodeMirror Setup --- 536 const editor = CodeMirror.fromTextArea(document.getElementById("code-input"), { 537 mode: "text/x-c++src", 538 theme: "dracula", 539 lineNumbers: true, 540 indentUnit: 4, 541 matchBrackets: true, 542 autoCloseBrackets: true, 543 styleActiveLine: true, 544 inputStyle: "contenteditable", 545 viewportMargin: Infinity, 546 extraKeys: { 547 "Backspace": function(cm) { 548 if (cm.somethingSelected()) { 549 cm.execCommand("delCharBefore"); 550 return; 551 } 552 var cursor = cm.getCursor(); 553 var line = cm.getLine(cursor.line); 554 // If cursor is not at start, and everything before it is whitespace 555 if (cursor.ch > 0 && /^\s+$/.test(line.slice(0, cursor.ch))) { 556 cm.execCommand("indentLess"); 557 } else { 558 cm.execCommand("delCharBefore"); 559 } 560 } 561 }, 562 }); 563 564 // Fix resize issues 565 window.addEventListener('resize', () => editor.refresh()); 566 567 // Local Auto-Save 568 const LOCAL_STORAGE_KEY = 'cpp_compiler_code_autosave'; 569 editor.on('change', () => { 570 localStorage.setItem(LOCAL_STORAGE_KEY, editor.getValue()); 571 }); 572 573 // Load Auto-Save on Start (if not shared) 574 const savedCode = localStorage.getItem(LOCAL_STORAGE_KEY); 575 if (savedCode && !window.location.search.includes('share') && !window.location.search.includes('code')) { 576 editor.setValue(savedCode); 577 } 578 579 // --- Editor Toolbar Logic --- 580 undoBtn.addEventListener('click', () => { editor.undo(); editor.focus(); }); 581 redoBtn.addEventListener('click', () => { editor.redo(); editor.focus(); }); 582 583 clearCodeBtn.addEventListener('click', () => { 584 clearModal.classList.add('open'); 585 }); 586 587 cancelClearBtn.addEventListener('click', () => { 588 clearModal.classList.remove('open'); 589 }); 590 591 confirmClearBtn.addEventListener('click', () => { 592 editor.setValue(''); 593 editor.clearHistory(); 594 editor.focus(); 595 clearModal.classList.remove('open'); 596 showToast("Editor Cleared"); 597 }); 598 599 clearModal.addEventListener('click', (e) => { 600 if (e.target === clearModal) clearModal.classList.remove('open'); 601 }); 602 603 // --- Mobile Helper Bar --- 604 document.querySelectorAll('.helper-key').forEach(key => { 605 key.addEventListener('click', (e) => { 606 const char = key.getAttribute('data-char'); 607 const doc = editor.getDoc(); 608 const cursor = doc.getCursor(); 609 610 if (char === '()') { 611 doc.replaceRange('()', cursor); 612 doc.setCursor({line: cursor.line, ch: cursor.ch + 1}); 613 } else if (char === '[]') { 614 doc.replaceRange('[]', cursor); 615 doc.setCursor({line: cursor.line, ch: cursor.ch + 1}); 616 } else if (char === '{}') { 617 doc.replaceRange('{}', cursor); 618 doc.setCursor({line: cursor.line, ch: cursor.ch + 1}); 619 } else if (char === '""') { 620 doc.replaceRange('""', cursor); 621 doc.setCursor({line: cursor.line, ch: cursor.ch + 1}); 622 } else if (char === "''") { 623 doc.replaceRange("''", cursor); 624 doc.setCursor({line: cursor.line, ch: cursor.ch + 1}); 625 } else if (char === "cout") { 626 doc.replaceRange('cout << ', cursor); 627 doc.setCursor({line: cursor.line, ch: cursor.ch + 8}); 628 } else { 629 doc.replaceRange(char, cursor); 630 } 631 editor.focus(); 632 }); 633 }); 634 635 // --- Download Button --- 636 downloadBtn.addEventListener('click', () => { 637 const code = editor.getValue(); 638 const blob = new Blob([code], { type: 'text/x-c++src' }); 639 const url = window.URL.createObjectURL(blob); 640 const a = document.createElement('a'); 641 a.href = url; 642 a.download = 'main.cpp'; 643 document.body.appendChild(a); 644 a.click(); 645 window.URL.revokeObjectURL(url); 646 document.body.removeChild(a); 647 showToast("Downloaded main.cpp"); 648 }); 649 650 window.addEventListener('load', async () => { 651 const params = new URLSearchParams(window.location.search); 652 const shareId = params.get('share'); 653 const sharedCodeOld = params.get('code'); 654 655 if (shareId) { 656 // Fetch from Firebase 657 try { 658 toast.textContent = "Loading shared code..."; 659 toast.classList.add('show'); 660 661 const docRef = doc(db, "shares", shareId); 662 const docSnap = await getDoc(docRef); 663 664 if (docSnap.exists()) { 665 const data = docSnap.data(); 666 editor.setValue(data.code); 667 668 // Restore Output if it exists 669 if (data.output && term) { 670 term.write('\r\n\x1b[38;2;115;115;115m> PREVIOUS OUTPUT:\x1b[0m\r\n'); 671 term.write(data.output.replace(/\n/g, '\r\n')); 672 term.write('\r\n\x1b[38;2;115;115;115m> ----------------\x1b[0m\r\n'); 673 } 674 675 toast.textContent = "Code loaded successfully"; 676 } else { 677 toast.textContent = "Shared code not found"; 678 } 679 setTimeout(() => toast.classList.remove('show'), 3000); 680 } catch (e) { 681 console.error("Fetch error:", e); 682 if (e.code === 'permission-denied') { 683 toast.textContent = "Error: Database Access Denied"; 684 } else { 685 toast.textContent = "Error loading code"; 686 } 687 setTimeout(() => toast.classList.remove('show'), 3000); 688 } 689 } else if (sharedCodeOld) { 690 // Fallback for Base64 691 try { editor.setValue(atob(sharedCodeOld)); } 692 catch(e) {} 693 } 694 setTimeout(connect, 500); 695 }); 696 697 // --- Terminal Setup --- 698 const term = new Terminal({ 699 theme: { 700 background: '#050505', 701 foreground: '#f5f5f5', 702 cursor: '#bef264', 703 selectionBackground: 'rgba(190, 242, 100, 0.3)', 704 black: '#050505', 705 green: '#bef264', 706 yellow: '#facc15', 707 red: '#ef4444', 708 }, 709 fontFamily: "'JetBrains Mono', monospace", 710 fontSize: 12, 711 cursorBlink: true, 712 convertEol: true 713 }); 714 const fitAddon = new FitAddon.FitAddon(); 715 term.loadAddon(fitAddon); 716 term.open(document.getElementById('terminal-container')); 717 fitAddon.fit(); 718 window.addEventListener('resize', () => fitAddon.fit()); 719 720 // Helper: Get Clean Terminal Output 721 function getTerminalContent() { 722 if (!term) return ""; 723 const buffer = term.buffer.active; 724 let text = ""; 725 for (let i = 0; i < buffer.length; i++) { 726 const line = buffer.getLine(i); 727 if (line) { 728 let lineText = line.translateToString(true); 729 // Filter system messages 730 if(lineText.trim().startsWith("> [") || 731 lineText.includes("SYSTEM READY") || 732 lineText.includes("ATTEMPTING") || 733 lineText.includes("CONNECTION") || 734 lineText.includes("PREVIOUS OUTPUT") || 735 lineText.includes("> ----------------") || 736 lineText.includes("> ./a.out") 737 ) { 738 continue; 739 } 740 text += lineText + "\n"; 741 } 742 } 743 return text.trim(); 744 } 745 746 // Custom Key Handler for Ctrl+C Copy 747 term.attachCustomKeyEventHandler((arg) => { 748 if (arg.ctrlKey && arg.code === "KeyC" && arg.type === "keydown") { 749 const selection = term.getSelection(); 750 if (selection) { 751 fallbackCopyTextToClipboard(selection); 752 return false; 753 } 754 } 755 return true; 756 }); 757 758 term.write('\x1b[38;2;115;115;115m> SYSTEM READY.\r\n> ATTEMPTING CONNECTION...\x1b[0m\r\n'); 759 760 // --- WebSocket Logic --- 761 function connect() { 762 const url = "wss://compiler-z4x4.onrender.com"; 763 try { socket = new WebSocket(url); } catch (e) { 764 term.write(`\r\n\x1b[31m> INVALID URL.\x1b[0m\r\n`); 765 return; 766 } 767 768 socket.onopen = () => { 769 connDot.className = "pulse-dot connected ml-3"; 770 if(connectionStatus) connectionStatus.textContent = "Connected to Server"; 771 if(connectionStatus) connectionStatus.className = "hidden md:flex items-center gap-2 font-mono text-xs text-lime-300"; 772 773 term.write(`\x1b[38;2;190;242;100m> CONNECTION ESTABLISHED.\x1b[0m\r\n> ./a.out\r\n`); 774 runBtn.disabled = false; 775 }; 776 777 socket.onclose = () => { 778 connDot.className = "pulse-dot disconnected ml-3"; 779 if(connectionStatus) connectionStatus.textContent = "Disconnected"; 780 if(connectionStatus) connectionStatus.className = "hidden md:flex items-center gap-2 font-mono text-xs text-red-500"; 781 782 term.write(`\r\n\x1b[31m> CONNECTION LOST. RECONNECTING...\x1b[0m\r\n`); 783 runBtn.disabled = true; 784 setTimeout(connect, 3000); 785 socket = null; 786 }; 787 788 socket.onmessage = (event) => { 789 const msg = JSON.parse(event.data); 790 if (msg.type === 'stdout') { 791 let text = msg.data; 792 outputBuffer += text; 793 794 // Trigger AI Overlay on Error 795 if (outputBuffer.toLowerCase().includes("error") || outputBuffer.toLowerCase().includes("warning")) { 796 aiSuggestion.classList.add('visible'); 797 } 798 799 if (!text.includes('\r')) { 800 text = text.replace(/\n/g, '\r\n'); 801 } 802 term.write(text); 803 } else if (msg.type === 'status') { 804 if (msg.msg.includes("Program finished")) { 805 const duration = (Date.now() - startTime) / 1000; 806 term.write(`\r\n\x1b[38;2;115;115;115m[Finished in ${duration.toFixed(3)}s]\x1b[0m\r\n`); 807 } 808 } else if (msg.type === 'ai_response') { 809 handleAiResponse(msg.data); 810 } else if (msg.type === 'ai_error') { 811 showAiError(msg.msg); 812 } 813 }; 814 } 815 816 // --- Run Logic --- 817 runBtn.addEventListener('click', () => { 818 if (!socket || socket.readyState !== WebSocket.OPEN) return; 819 if (document.activeElement) document.activeElement.blur(); 820 821 term.reset(); 822 currentLine = ""; 823 outputBuffer = ""; 824 aiSuggestion.classList.remove('visible'); 825 826 let code = editor.getValue(); 827 code = code.replace(/\u00A0/g, " ").replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"'); 828 829 startTime = Date.now(); 830 term.write('\x1b[32m> g++ main.cpp -o main && ./main\x1b[0m\r\n'); 831 832 socket.send(JSON.stringify({ 833 type: 'run', 834 code: code, 835 language: 'cpp' 836 })); 837 838 term.focus(); 839 }); 840 841 // --- AI Feature Logic --- 842 askAiLink.addEventListener('click', () => { 843 const errorContext = getTerminalContent(); 844 const currentCode = editor.getValue(); 845 846 aiModal.classList.add('open'); 847 aiLoading.classList.remove('hidden'); 848 aiResult.classList.add('hidden'); 849 aiErrorMsg.classList.add('hidden'); 850 applyFixBtn.disabled = true; 851 852 socket.send(JSON.stringify({ 853 type: 'ai_fix', 854 code: currentCode, 855 error: errorContext, 856 language: 'cpp' 857 })); 858 }); 859 860 function handleAiResponse(data) { 861 aiLoading.classList.add('hidden'); 862 aiResult.classList.remove('hidden'); 863 aiExplanation.textContent = data.explanation; 864 aiCodePreview.textContent = data.fixed_code; 865 lastSuggestedCode = data.fixed_code; 866 applyFixBtn.disabled = false; 867 } 868 869 function showAiError(msg) { 870 aiLoading.classList.add('hidden'); 871 aiErrorMsg.textContent = msg; 872 aiErrorMsg.classList.remove('hidden'); 873 } 874 875 applyFixBtn.addEventListener('click', () => { 876 if (lastSuggestedCode) { 877 editor.setValue(lastSuggestedCode); 878 closeAiModal(); 879 showToast("Fix Applied Successfully!"); 880 aiSuggestion.classList.remove('visible'); 881 } 882 }); 883 884 function closeAiModal() { 885 aiModal.classList.remove('open'); 886 } 887 closeModal.addEventListener('click', closeAiModal); 888 cancelFixBtn.addEventListener('click', closeAiModal); 889 aiModal.addEventListener('click', (e) => { 890 if (e.target === aiModal) closeAiModal(); 891 }); 892 893 // --- Reset Logic --- 894 resetBtn.addEventListener('click', () => { 895 term.reset(); 896 currentLine = ""; 897 outputBuffer = ""; 898 aiSuggestion.classList.remove('visible'); 899 term.write(`\x1b[38;2;190;242;100m> CONNECTION ESTABLISHED.\x1b[0m\r\n> ./a.out\r\n`); 900 }); 901 902 // --- Share Functionality with Firebase --- 903 shareBtn.addEventListener('click', async () => { 904 const originalIcon = shareBtn.innerHTML; 905 shareBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>'; 906 shareBtn.disabled = true; 907 908 try { 909 if (!auth.currentUser) { 910 try { 911 await signInAnonymously(auth); 912 } catch (authError) { 913 console.warn("Anonymous auth failed:", authError.code); 914 } 915 } 916 917 const code = editor.getValue(); 918 const output = getTerminalContent(); 919 const shareId = generateId(); 920 921 await setDoc(doc(db, "shares", shareId), { 922 code: code, 923 language: 'cpp', 924 output: output, 925 createdAt: new Date().toISOString() 926 }); 927 928 const url = new URL(window.location.href); 929 url.searchParams.delete('code'); 930 url.searchParams.set('share', shareId); 931 const shareUrl = url.toString(); 932 933 if (navigator.share) { 934 try { 935 await navigator.share({ 936 title: document.title, 937 text: 'Check out my C++ code!', 938 url: shareUrl 939 }); 940 } catch (err) { 941 fallbackCopyTextToClipboard(shareUrl); 942 } 943 } else { 944 fallbackCopyTextToClipboard(shareUrl); 945 } 946 } catch (error) { 947 console.error("Share failed:", error); 948 if (error.code === 'permission-denied') { 949 showToast("Share Failed: Access Denied"); 950 } else { 951 showToast("Share Failed: " + error.message); 952 } 953 } finally { 954 shareBtn.innerHTML = originalIcon; 955 shareBtn.disabled = false; 956 } 957 }); 958 959 function showToast(msg) { 960 toast.textContent = msg; 961 toast.classList.add('show'); 962 setTimeout(() => toast.classList.remove('show'), 3000); 963 } 964 965 // --- Smart Copy Function --- 966 copyBtn.addEventListener('click', () => { 967 const text = getTerminalContent(); 968 fallbackCopyTextToClipboard(text); 969 }); 970 971 function fallbackCopyTextToClipboard(text) { 972 const textArea = document.createElement("textarea"); 973 textArea.value = text; 974 textArea.style.position = "fixed"; 975 textArea.style.top = "0"; 976 textArea.style.left = "0"; 977 textArea.style.opacity = "0"; 978 document.body.appendChild(textArea); 979 textArea.focus(); 980 textArea.select(); 981 try { 982 document.execCommand('copy'); 983 showToast("Copied to Clipboard!"); 984 } catch (err) { 985 showToast("Copy Failed"); 986 } 987 document.body.removeChild(textArea); 988 } 989 990 // Terminal Input 991 term.onData((data) => { 992 if (!socket || socket.readyState !== WebSocket.OPEN) return; 993 994 if (data === '\r') { 995 term.write('\r\n'); 996 socket.send(JSON.stringify({ type: 'input', data: currentLine + '\n' })); 997 currentLine = ""; 998 } else if (data === '\x7f' || data === '\b') { 999 if (currentLine.length > 0) { 1000 currentLine = currentLine.slice(0, -1); 1001 term.write('\b \b'); 1002 } 1003 } else { 1004 currentLine += data; 1005 term.write(data); 1006 } 1007 }); 1008 </script> 1009 </body> 1010 </html>