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