cloud-compiler

A real-time, low-latency code ...
Log | Files | Refs | README | LICENSE

commit ff407690ce63ff2422b3f914d45499bc5ef32057
parent 8d3e3d8e24fa6d52ce1716d7173e523c16bf77cd
Author: Amit Dutta <amitdutta4255@gmail.com>
Date:   Tue, 13 Jan 2026 19:10:46 +0530

[2026-01-13] : added a new feature (AI debugging, Powered by Gemini 2.5 Flash)

Diffstat:
Mbackend.py | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mpublic/c.html | 277+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mpublic/cpp.html | 215++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mpublic/docs.html | 58++++++++++++++++++++++++++++++++++++----------------------
Mpublic/index.html | 11++++++++++-
Mpublic/python.html | 210++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
6 files changed, 729 insertions(+), 121 deletions(-)

diff --git a/backend.py b/backend.py @@ -1,4 +1,5 @@ import asyncio +import aiohttp # Added missing import from aiohttp import web import json import subprocess @@ -7,12 +8,15 @@ import sys import threading import re import importlib.util -import platform # <--- FIXED: Added missing import +import platform # ================================================================================== # CLOUD COMPILER SERVER (AIOHTTP) - RENDER COMPATIBLE # ================================================================================== +# Get API Key from Environment +GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY") + def install_package(package_name, ws=None, loop=None): """Attempt to install a python package via pip.""" try: @@ -43,13 +47,12 @@ def check_and_install_packages(code, ws=None, loop=None): from_imports = re.findall(r'^\s*from\s+(\w+)', code, re.MULTILINE) unique_packages = set(imports + from_imports) for pkg in unique_packages: - if pkg in ['os', 'sys', 'time', 'random', 'math', 'json', 'asyncio', 'threading', 'platform', 'subprocess', 're']: + if pkg in ['os', 'sys', 'time', 'random', 'math', 'json', 'asyncio', 'threading', 'platform', 'subprocess', 're', 'aiohttp']: continue install_package(pkg, ws, loop) async def handle_client(request): # --- HEALTH CHECK HANDLING --- - # Render sends HEAD/GET to root. If not a websocket upgrade, return OK. if request.headers.get("Upgrade", "").lower() != "websocket": return web.Response(text="OK") @@ -64,23 +67,18 @@ async def handle_client(request): def read_stream(stream, loop): try: while True: - # Read 1 byte/char at a time char = stream.read(1) if not char: break - # aiohttp's send_json is a coroutine asyncio.run_coroutine_threadsafe( ws.send_json({'type': 'stdout', 'data': char}), loop ) - # --- SIGNAL FINISH --- - # When stream ends (process exits), tell frontend asyncio.run_coroutine_threadsafe( ws.send_json({'type': 'status', 'msg': 'Program finished'}), loop ) - except Exception: pass @@ -136,6 +134,8 @@ async def handle_client(request): if compile_process.returncode != 0: await ws.send_json({'type': 'stdout', 'data': f"Compilation Error:\n{compile_process.stderr}"}) + # Send explicit error status so frontend knows to show AI button + await ws.send_json({'type': 'status', 'msg': 'Compilation Failed'}) continue await ws.send_json({'type': 'status', 'msg': 'Running C Binary...'}) @@ -168,6 +168,7 @@ async def handle_client(request): if compile_process.returncode != 0: await ws.send_json({'type': 'stdout', 'data': f"Compilation Error:\n{compile_process.stderr}"}) + await ws.send_json({'type': 'status', 'msg': 'Compilation Failed'}) continue await ws.send_json({'type': 'status', 'msg': 'Running C++ Binary...'}) @@ -196,12 +197,68 @@ async def handle_client(request): process.stdin.flush() except Exception: pass - + + # --- NEW: AI FIX HANDLER --- + elif data.get('type') == 'ai_fix': + code = data.get('code') + error_log = data.get('error') + language = data.get('language', 'c') # Default to C, but respect frontend input + + if not GEMINI_API_KEY: + await ws.send_json({'type': 'ai_error', 'msg': 'Server Error: GEMINI_API_KEY not configured.'}) + continue + + # Construct Prompt + json_format = '{\n "explanation": "Brief explanation of the bug (max 2 sentences)",\n "fixed_code": "The full corrected code"\n}' + + prompt = ( + f"You are an expert {language.upper()} programming debugger.\n" + f"CODE:\n{code}\n" + f"ERROR OUTPUT:\n{error_log}\n" + "TASK:\n" + "1. Analyze the error.\n" + "2. Provide a concise explanation.\n" + "3. Provide the COMPLETE corrected code.\n" + "RESPONSE FORMAT:\n" + "Return ONLY a valid JSON object with no markdown formatting or backticks:\n" + f"{json_format}" + ) + + try: + api_url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key={GEMINI_API_KEY}" + + async with aiohttp.ClientSession() as session: + async with session.post(api_url, json={ + "contents": [{ "parts": [{ "text": prompt }] }], + "generationConfig": { "responseMimeType": "application/json" } + }) as resp: + if resp.status != 200: + error_text = await resp.text() + await ws.send_json({'type': 'ai_error', 'msg': f"AI API Error: {error_text}"}) + else: + result = await resp.json() + # Extract text from Gemini response structure + content_text = result.get('candidates', [{}])[0].get('content', {}).get('parts', [{}])[0].get('text', '{}') + + # Send back to client + await ws.send_json({ + 'type': 'ai_response', + 'data': json.loads(content_text) + }) + + except Exception as e: + print(f"AI Error: {e}") + await ws.send_json({'type': 'ai_error', 'msg': f"Server Processing Error: {str(e)}"}) + elif msg.type == web.WSMsgType.ERROR: print(f'ws connection closed with exception {ws.exception()}') finally: - if process: process.kill() + if process: + try: + process.kill() + except: + pass print("Client disconnected") return ws @@ -209,7 +266,7 @@ async def handle_client(request): async def main(): port = int(os.environ.get("PORT", 8765)) app = web.Application() - # Route root path to the unified handler (handles both HTTP checks and WS upgrades) + # Route root path to the unified handler app.add_routes([web.get('/', handle_client)]) runner = web.AppRunner(app) diff --git a/public/c.html b/public/c.html @@ -145,6 +145,67 @@ 70% { transform: scale(1); box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); } 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } } + + /* AI Text Overlay */ + .ai-overlay { + position: absolute; + bottom: 10px; + right: 20px; + background: rgba(20, 20, 20, 0.9); + border: 1px solid #ef4444; + color: #fca5a5; + padding: 8px 12px; + border-radius: 6px; + font-family: 'JetBrains Mono', monospace; + font-size: 12px; + z-index: 60; + display: none; + backdrop-filter: blur(4px); + animation: slideIn 0.3s ease-out; + box-shadow: 0 4px 12px rgba(0,0,0,0.5); + } + .ai-overlay.visible { display: flex; align-items: center; gap: 8px; } + + .ai-link { + color: #fff; + text-decoration: underline; + text-decoration-style: dotted; + text-underline-offset: 4px; + cursor: pointer; + font-weight: bold; + transition: color 0.2s; + } + .ai-link:hover { color: #bef264; text-decoration-style: solid; } + + @keyframes slideIn { + from { transform: translateY(20px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } + } + + /* AI Modal */ + .modal-overlay { + position: fixed; top: 0; left: 0; width: 100%; height: 100%; + background: rgba(0,0,0,0.8); backdrop-filter: blur(5px); + z-index: 200; display: none; align-items: center; justify-content: center; + } + .modal-overlay.open { display: flex; } + + .modal-content { + background: #0f0f0f; + border: 1px solid var(--accent); + width: 90%; max-width: 600px; + max-height: 80vh; + border-radius: 12px; + display: flex; flex-direction: column; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + overflow: hidden; + animation: modalPop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + } + + @keyframes modalPop { + 0% { transform: scale(0.9); opacity: 0; } + 100% { transform: scale(1); opacity: 1; } + } </style> </head> <body class="antialiased selection:bg-lime-300 selection:text-black"> @@ -153,6 +214,59 @@ <div class="crt-scanline"></div> <div id="toast" class="toast">Link Copied to Clipboard!</div> + <!-- AI Overlay in Terminal --> + <div id="ai-suggestion" class="ai-overlay"> + <i class="fas fa-exclamation-triangle text-red-500"></i> + <span>Error detected. <span id="ask-ai-link" class="ai-link">Ask AI</span> to fix it.</span> + </div> + + <!-- AI Fix Modal --> + <div id="ai-modal" class="modal-overlay"> + <div class="modal-content"> + <!-- Modal Header --> + <div class="bg-white/5 px-6 py-4 border-b border-white/10 flex justify-between items-center"> + <h3 class="font-heading font-bold text-lg text-lime-300 flex items-center gap-2"> + <i class="fas fa-robot"></i> AI Diagnosis + </h3> + <button id="close-modal" class="text-neutral-500 hover:text-white transition-colors"> + <i class="fas fa-times"></i> + </button> + </div> + + <!-- Modal Body --> + <div class="p-6 overflow-y-auto custom-scrollbar flex-1"> + <div id="ai-loading" class="hidden flex flex-col items-center justify-center py-8 gap-4"> + <div class="pulse-dot connected" style="width: 12px; height: 12px;"></div> + <p class="font-mono text-neutral-400 text-sm animate-pulse">Analyzing compile error...</p> + </div> + + <div id="ai-result" class="hidden space-y-4"> + <div> + <h4 class="font-mono text-xs uppercase text-neutral-500 mb-2">Explanation</h4> + <p id="ai-explanation" class="text-sm text-neutral-200 leading-relaxed font-sans"></p> + </div> + + <div> + <h4 class="font-mono text-xs uppercase text-neutral-500 mb-2">Suggested Fix</h4> + <div class="relative group"> + <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> + </div> + </div> + </div> + + <div id="ai-error-msg" class="hidden text-red-400 text-sm font-mono text-center py-4"></div> + </div> + + <!-- Modal Footer --> + <div class="bg-white/5 px-6 py-4 border-t border-white/10 flex justify-end gap-3"> + <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> + <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"> + <i class="fas fa-check"></i> Apply Fix + </button> + </div> + </div> + </div> + <!-- Header (Responsive) --> <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"> @@ -225,14 +339,10 @@ int main() { }</textarea> </div> - <!-- AI Fix Button --> - <button id="ai-fix-btn" class="hidden absolute bottom-4 right-4 bg-red-500/10 border border-red-500/50 text-red-400 hover:bg-red-500 hover:text-white px-3 py-1.5 rounded-lg text-xs font-mono transition-all items-center gap-2 backdrop-blur-md z-20"> - <i class="fas fa-magic"></i> Fix with AI - </button> </div> <!-- Terminal --> - <div class="flex-1 flex flex-col h-[40%] md:h-auto min-h-0 bg-[#050505]"> + <div class="flex-1 flex flex-col h-[40%] md:h-auto min-h-0 bg-[#050505] relative"> <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"> <span>OUT // TERMINAL</span> <span class="text-neutral-600">XTERM.JS</span> @@ -264,12 +374,10 @@ int main() { const db = getFirestore(app); const auth = getAuth(app); - // Attempt initial sign-in, but don't crash if it fails (restricted op) signInAnonymously(auth).catch((error) => { - console.warn("Auto-Auth failed (likely disabled in console). App will try unauthenticated access.", error.code); + console.warn("Auto-Auth failed. App will try unauthenticated access.", error.code); }); - // --- Helper: Generate ID --- function generateId(length = 7) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; @@ -284,16 +392,29 @@ int main() { const resetBtn = document.getElementById('reset-btn'); const shareBtn = document.getElementById('share-btn'); const copyBtn = document.getElementById('copy-btn'); - const aiFixBtn = document.getElementById('ai-fix-btn'); const connDot = document.getElementById('conn-dot'); const connectionStatus = document.getElementById('connection-status'); const codeInput = document.getElementById('code-input'); const toast = document.getElementById('toast'); + + // AI Elements + const aiSuggestion = document.getElementById('ai-suggestion'); + const askAiLink = document.getElementById('ask-ai-link'); + const aiModal = document.getElementById('ai-modal'); + const closeModal = document.getElementById('close-modal'); + const cancelFixBtn = document.getElementById('cancel-fix-btn'); + const applyFixBtn = document.getElementById('apply-fix-btn'); + const aiLoading = document.getElementById('ai-loading'); + const aiResult = document.getElementById('ai-result'); + const aiExplanation = document.getElementById('ai-explanation'); + const aiCodePreview = document.getElementById('ai-code-preview'); + const aiErrorMsg = document.getElementById('ai-error-msg'); // --- State --- let socket = null; let currentLine = ""; let startTime = 0; + let lastSuggestedCode = ""; // --- EDITOR LOGIC --- codeInput.addEventListener('keydown', function(e) { @@ -334,7 +455,6 @@ int main() { const sharedCodeOld = params.get('code'); if (shareId) { - // Fetch from Firebase try { toast.textContent = "Loading shared code..."; toast.classList.add('show'); @@ -345,31 +465,22 @@ int main() { if (docSnap.exists()) { const data = docSnap.data(); codeInput.value = data.code; - - // Restore Output if it exists if (data.output && term) { term.write('\r\n\x1b[38;2;115;115;115m> PREVIOUS OUTPUT:\x1b[0m\r\n'); term.write(data.output.replace(/\n/g, '\r\n')); term.write('\r\n\x1b[38;2;115;115;115m> ----------------\x1b[0m\r\n'); } - toast.textContent = "Code loaded successfully"; } else { toast.textContent = "Shared code not found"; } setTimeout(() => toast.classList.remove('show'), 3000); } catch (e) { - console.error("Fetch error:", e); - // More descriptive error for user - if (e.code === 'permission-denied') { - toast.textContent = "Error: Database Access Denied"; - } else { - toast.textContent = "Error loading code"; - } - setTimeout(() => toast.classList.remove('show'), 3000); + console.error("Fetch error:", e); + toast.textContent = "Error loading code"; + setTimeout(() => toast.classList.remove('show'), 3000); } } else if (sharedCodeOld) { - // Fallback for Base64 try { codeInput.value = atob(sharedCodeOld); } catch(e) {} } @@ -426,18 +537,6 @@ int main() { return text.trim(); } - // Custom Key Handler for Ctrl+C Copy - term.attachCustomKeyEventHandler((arg) => { - if (arg.ctrlKey && arg.code === "KeyC" && arg.type === "keydown") { - const selection = term.getSelection(); - if (selection) { - fallbackCopyTextToClipboard(selection); - return false; - } - } - return true; - }); - term.write('\x1b[38;2;115;115;115m> SYSTEM READY.\r\n> ATTEMPTING CONNECTION...\x1b[0m\r\n'); // --- WebSocket Logic --- @@ -473,8 +572,9 @@ int main() { if (msg.type === 'stdout') { let text = msg.data; + // Trigger AI Overlay on Error if (text.toLowerCase().includes("error:") || text.toLowerCase().includes("warning:")) { - aiFixBtn.classList.remove('hidden'); + aiSuggestion.classList.add('visible'); } if (!text.includes('\r')) { @@ -482,11 +582,16 @@ int main() { } term.write(text); } else if (msg.type === 'status') { - // Detect "Program finished" signal from backend if (msg.msg.includes("Program finished")) { const duration = (Date.now() - startTime) / 1000; term.write(`\r\n\x1b[38;2;115;115;115m[Finished in ${duration.toFixed(3)}s]\x1b[0m\r\n`); + } else if (msg.msg === "Compilation Failed") { + aiSuggestion.classList.add('visible'); } + } else if (msg.type === 'ai_response') { + handleAiResponse(msg.data); + } else if (msg.type === 'ai_error') { + showAiError(msg.msg); } }; } @@ -498,7 +603,7 @@ int main() { document.activeElement.blur(); term.reset(); currentLine = ""; - aiFixBtn.classList.add('hidden'); + aiSuggestion.classList.remove('visible'); // Hide previous suggestions let code = codeInput.value; code = code.replace(/\u00A0/g, " ").replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"'); @@ -526,25 +631,76 @@ int main() { term.focus(); }); - // --- Reset Logic --- - resetBtn.addEventListener('click', () => { - term.reset(); - currentLine = ""; - term.write(`\x1b[38;2;190;242;100m> CONNECTION ESTABLISHED.\x1b[0m\r\n> ./a.out\r\n`); + // --- AI Feature Logic --- + + // 1. Open Modal and Request AI Fix + askAiLink.addEventListener('click', () => { + const errorContext = getTerminalContent(); + const currentCode = codeInput.value; + + // UI Update + aiModal.classList.add('open'); + aiLoading.classList.remove('hidden'); + aiResult.classList.add('hidden'); + aiErrorMsg.classList.add('hidden'); + applyFixBtn.disabled = true; + + // Send Request + socket.send(JSON.stringify({ + type: 'ai_fix', + code: currentCode, + error: errorContext, + language: 'c' + })); + }); + + // 2. Handle AI Response + function handleAiResponse(data) { + aiLoading.classList.add('hidden'); + aiResult.classList.remove('hidden'); + + aiExplanation.textContent = data.explanation; + aiCodePreview.textContent = data.fixed_code; + lastSuggestedCode = data.fixed_code; + + applyFixBtn.disabled = false; + } + + // 3. Handle AI Error + function showAiError(msg) { + aiLoading.classList.add('hidden'); + aiErrorMsg.textContent = msg; + aiErrorMsg.classList.remove('hidden'); + } + + // 4. Apply Fix + applyFixBtn.addEventListener('click', () => { + if (lastSuggestedCode) { + codeInput.value = lastSuggestedCode; + closeAiModal(); + showToast("Fix Applied Successfully!"); + aiSuggestion.classList.remove('visible'); + } + }); + + // 5. Modal Controls + function closeAiModal() { + aiModal.classList.remove('open'); + } + closeModal.addEventListener('click', closeAiModal); + cancelFixBtn.addEventListener('click', closeAiModal); + aiModal.addEventListener('click', (e) => { + if (e.target === aiModal) closeAiModal(); }); - // --- Share Functionality (Firebase) --- + + // --- Share Functionality --- shareBtn.addEventListener('click', async () => { - // Original Share icon animation const originalIcon = shareBtn.innerHTML; shareBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>'; shareBtn.disabled = true; try { - // Robust Auth Check: - // 1. If not logged in, try to log in. - // 2. If login fails (restricted), proceed anyway (swallow error). - // 3. This allows "Test Mode" (open DB) to work even if Auth is disabled. if (!auth.currentUser) { try { await signInAnonymously(auth); @@ -554,24 +710,21 @@ int main() { } const code = codeInput.value; - const output = getTerminalContent(); // Capture output + const output = getTerminalContent(); const shareId = generateId(); - // Save to Firestore await setDoc(doc(db, "shares", shareId), { code: code, language: 'c', - output: output, // Store output + output: output, createdAt: new Date().toISOString() }); - // Generate URL const url = new URL(window.location.href); - url.searchParams.delete('code'); // Remove legacy param + url.searchParams.delete('code'); url.searchParams.set('share', shareId); const shareUrl = url.toString(); - // Share or Copy if (navigator.share) { try { await navigator.share({ @@ -580,7 +733,6 @@ int main() { url: shareUrl }); } catch (err) { - // If canceled, still copy to clipboard as fallback fallbackCopyTextToClipboard(shareUrl); } } else { @@ -588,12 +740,7 @@ int main() { } } catch (error) { console.error("Share failed:", error); - if (error.code === 'permission-denied') { - showToast("Share Failed: Access Denied"); - console.log("Tip: Enable Anonymous Auth in Firebase Console OR set Firestore rules to public."); - } else { - showToast("Share Failed: " + error.message); - } + showToast("Share Failed"); } finally { shareBtn.innerHTML = originalIcon; shareBtn.disabled = false; @@ -631,8 +778,12 @@ int main() { document.body.removeChild(textArea); } - aiFixBtn.addEventListener('click', () => { - alert("AI Feature: Coming Soon via Backend Update"); + // --- Reset Logic --- + resetBtn.addEventListener('click', () => { + term.reset(); + currentLine = ""; + aiSuggestion.classList.remove('visible'); + term.write(`\x1b[38;2;190;242;100m> CONNECTION ESTABLISHED.\x1b[0m\r\n> ./a.out\r\n`); }); // Terminal Input diff --git a/public/cpp.html b/public/cpp.html @@ -145,6 +145,67 @@ 70% { transform: scale(1); box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); } 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } } + + /* AI Text Overlay */ + .ai-overlay { + position: absolute; + bottom: 10px; + right: 20px; + background: rgba(20, 20, 20, 0.9); + border: 1px solid #ef4444; + color: #fca5a5; + padding: 8px 12px; + border-radius: 6px; + font-family: 'JetBrains Mono', monospace; + font-size: 12px; + z-index: 60; + display: none; + backdrop-filter: blur(4px); + animation: slideIn 0.3s ease-out; + box-shadow: 0 4px 12px rgba(0,0,0,0.5); + } + .ai-overlay.visible { display: flex; align-items: center; gap: 8px; } + + .ai-link { + color: #fff; + text-decoration: underline; + text-decoration-style: dotted; + text-underline-offset: 4px; + cursor: pointer; + font-weight: bold; + transition: color 0.2s; + } + .ai-link:hover { color: #bef264; text-decoration-style: solid; } + + @keyframes slideIn { + from { transform: translateY(20px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } + } + + /* AI Modal */ + .modal-overlay { + position: fixed; top: 0; left: 0; width: 100%; height: 100%; + background: rgba(0,0,0,0.8); backdrop-filter: blur(5px); + z-index: 200; display: none; align-items: center; justify-content: center; + } + .modal-overlay.open { display: flex; } + + .modal-content { + background: #0f0f0f; + border: 1px solid var(--accent); + width: 90%; max-width: 600px; + max-height: 80vh; + border-radius: 12px; + display: flex; flex-direction: column; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + overflow: hidden; + animation: modalPop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + } + + @keyframes modalPop { + 0% { transform: scale(0.9); opacity: 0; } + 100% { transform: scale(1); opacity: 1; } + } </style> </head> <body class="antialiased selection:bg-lime-300 selection:text-black"> @@ -153,13 +214,66 @@ <div class="crt-scanline"></div> <div id="toast" class="toast">Link Copied to Clipboard!</div> + <!-- AI Overlay in Terminal --> + <div id="ai-suggestion" class="ai-overlay"> + <i class="fas fa-exclamation-triangle text-red-500"></i> + <span>Error detected. <span id="ask-ai-link" class="ai-link">Ask AI</span> to fix it.</span> + </div> + + <!-- AI Fix Modal --> + <div id="ai-modal" class="modal-overlay"> + <div class="modal-content"> + <!-- Modal Header --> + <div class="bg-white/5 px-6 py-4 border-b border-white/10 flex justify-between items-center"> + <h3 class="font-heading font-bold text-lg text-lime-300 flex items-center gap-2"> + <i class="fas fa-robot"></i> AI Diagnosis + </h3> + <button id="close-modal" class="text-neutral-500 hover:text-white transition-colors"> + <i class="fas fa-times"></i> + </button> + </div> + + <!-- Modal Body --> + <div class="p-6 overflow-y-auto custom-scrollbar flex-1"> + <div id="ai-loading" class="hidden flex flex-col items-center justify-center py-8 gap-4"> + <div class="pulse-dot connected" style="width: 12px; height: 12px;"></div> + <p class="font-mono text-neutral-400 text-sm animate-pulse">Analyzing compile error...</p> + </div> + + <div id="ai-result" class="hidden space-y-4"> + <div> + <h4 class="font-mono text-xs uppercase text-neutral-500 mb-2">Explanation</h4> + <p id="ai-explanation" class="text-sm text-neutral-200 leading-relaxed font-sans"></p> + </div> + + <div> + <h4 class="font-mono text-xs uppercase text-neutral-500 mb-2">Suggested Fix</h4> + <div class="relative group"> + <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> + </div> + </div> + </div> + + <div id="ai-error-msg" class="hidden text-red-400 text-sm font-mono text-center py-4"></div> + </div> + + <!-- Modal Footer --> + <div class="bg-white/5 px-6 py-4 border-t border-white/10 flex justify-end gap-3"> + <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> + <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"> + <i class="fas fa-check"></i> Apply Fix + </button> + </div> + </div> + </div> + <!-- Header (Responsive) --> <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"> <!-- Top Row: Title & Status --> <div class="flex items-center justify-between w-full md:w-auto"> <h1 class="text-lg md:text-xl font-heading font-bold text-white tracking-tight flex items-center"> - <span class="text-lime-300 mr-2">//</span>CPP.COMPILER + <span class="text-lime-300 mr-2">//</span>C++.COMPILER <span id="conn-dot" class="pulse-dot disconnected ml-3" title="Disconnected"></span> </h1> @@ -223,14 +337,10 @@ int main() { }</textarea> </div> - <!-- AI Fix Button --> - <button id="ai-fix-btn" class="hidden absolute bottom-4 right-4 bg-red-500/10 border border-red-500/50 text-red-400 hover:bg-red-500 hover:text-white px-3 py-1.5 rounded-lg text-xs font-mono transition-all items-center gap-2 backdrop-blur-md z-20"> - <i class="fas fa-magic"></i> Fix with AI - </button> </div> <!-- Terminal --> - <div class="flex-1 flex flex-col h-[40%] md:h-auto min-h-0 bg-[#050505]"> + <div class="flex-1 flex flex-col h-[40%] md:h-auto min-h-0 bg-[#050505] relative"> <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"> <span>OUT // TERMINAL</span> <span class="text-neutral-600">XTERM.JS</span> @@ -282,16 +392,29 @@ int main() { const resetBtn = document.getElementById('reset-btn'); const shareBtn = document.getElementById('share-btn'); const copyBtn = document.getElementById('copy-btn'); - const aiFixBtn = document.getElementById('ai-fix-btn'); const connDot = document.getElementById('conn-dot'); const connectionStatus = document.getElementById('connection-status'); const codeInput = document.getElementById('code-input'); const toast = document.getElementById('toast'); + + // AI Elements + const aiSuggestion = document.getElementById('ai-suggestion'); + const askAiLink = document.getElementById('ask-ai-link'); + const aiModal = document.getElementById('ai-modal'); + const closeModal = document.getElementById('close-modal'); + const cancelFixBtn = document.getElementById('cancel-fix-btn'); + const applyFixBtn = document.getElementById('apply-fix-btn'); + const aiLoading = document.getElementById('ai-loading'); + const aiResult = document.getElementById('ai-result'); + const aiExplanation = document.getElementById('ai-explanation'); + const aiCodePreview = document.getElementById('ai-code-preview'); + const aiErrorMsg = document.getElementById('ai-error-msg'); // --- State --- let socket = null; let currentLine = ""; let startTime = 0; + let lastSuggestedCode = ""; // --- EDITOR LOGIC (Indentation) --- codeInput.addEventListener('keydown', function(e) { @@ -471,8 +594,9 @@ int main() { if (msg.type === 'stdout') { let text = msg.data; + // Trigger AI Overlay on Error if (text.toLowerCase().includes("error:") || text.toLowerCase().includes("warning:")) { - aiFixBtn.classList.remove('hidden'); + aiSuggestion.classList.add('visible'); } if (!text.includes('\r')) { @@ -483,7 +607,13 @@ int main() { if (msg.msg.includes("Program finished")) { const duration = (Date.now() - startTime) / 1000; term.write(`\r\n\x1b[38;2;115;115;115m[Finished in ${duration.toFixed(3)}s]\x1b[0m\r\n`); + } else if (msg.msg === "Compilation Failed") { + aiSuggestion.classList.add('visible'); } + } else if (msg.type === 'ai_response') { + handleAiResponse(msg.data); + } else if (msg.type === 'ai_error') { + showAiError(msg.msg); } }; } @@ -495,7 +625,7 @@ int main() { document.activeElement.blur(); term.reset(); currentLine = ""; - aiFixBtn.classList.add('hidden'); + aiSuggestion.classList.remove('visible'); // Hide previous suggestions let code = codeInput.value; code = code.replace(/\u00A0/g, " ").replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"'); @@ -524,10 +654,73 @@ int main() { term.focus(); }); + // --- AI Feature Logic --- + + // 1. Open Modal and Request AI Fix + askAiLink.addEventListener('click', () => { + const errorContext = getTerminalContent(); + const currentCode = codeInput.value; + + // UI Update + aiModal.classList.add('open'); + aiLoading.classList.remove('hidden'); + aiResult.classList.add('hidden'); + aiErrorMsg.classList.add('hidden'); + applyFixBtn.disabled = true; + + // Send Request + socket.send(JSON.stringify({ + type: 'ai_fix', + code: currentCode, + error: errorContext, + language: 'cpp' + })); + }); + + // 2. Handle AI Response + function handleAiResponse(data) { + aiLoading.classList.add('hidden'); + aiResult.classList.remove('hidden'); + + aiExplanation.textContent = data.explanation; + aiCodePreview.textContent = data.fixed_code; + lastSuggestedCode = data.fixed_code; + + applyFixBtn.disabled = false; + } + + // 3. Handle AI Error + function showAiError(msg) { + aiLoading.classList.add('hidden'); + aiErrorMsg.textContent = msg; + aiErrorMsg.classList.remove('hidden'); + } + + // 4. Apply Fix + applyFixBtn.addEventListener('click', () => { + if (lastSuggestedCode) { + codeInput.value = lastSuggestedCode; + closeAiModal(); + showToast("Fix Applied Successfully!"); + aiSuggestion.classList.remove('visible'); + } + }); + + // 5. Modal Controls + function closeAiModal() { + aiModal.classList.remove('open'); + } + closeModal.addEventListener('click', closeAiModal); + cancelFixBtn.addEventListener('click', closeAiModal); + aiModal.addEventListener('click', (e) => { + if (e.target === aiModal) closeAiModal(); + }); + // --- Reset Logic --- resetBtn.addEventListener('click', () => { term.reset(); currentLine = ""; + aiSuggestion.classList.remove('visible'); term.write(`\x1b[38;2;190;242;100m> CONNECTION ESTABLISHED.\x1b[0m\r\n> ./a.out\r\n`); }); @@ -624,10 +817,6 @@ int main() { document.body.removeChild(textArea); } - aiFixBtn.addEventListener('click', () => { - showToast("AI Feature: Coming Soon via Backend Update"); - }); - // Terminal Input term.onData((data) => { if (!socket || socket.readyState !== WebSocket.OPEN) return; diff --git a/public/docs.html b/public/docs.html @@ -99,6 +99,7 @@ <a href="#communication" class="nav-link font-mono text-sm">WebSocket Protocol</a> <a href="#python-engine" class="nav-link font-mono text-sm">Python Engine</a> <a href="#cpp-engine" class="nav-link font-mono text-sm">C / C++ Engine</a> + <a href="#ai-debug" class="nav-link font-mono text-sm text-lime-300">New: AI Auto-Debug</a> <a href="#sharing" class="nav-link font-mono text-sm">Sharing & Persistence</a> <a href="#security" class="nav-link font-mono text-sm">Security & Sandbox</a> <a href="#privacy" class="nav-link font-mono text-sm">Privacy & Data</a> @@ -110,7 +111,9 @@ <div class="flex flex-wrap gap-2"> <span class="text-xs px-2 py-1 rounded bg-black border border-white/10 text-lime-300 font-mono">aiohttp</span> <span class="text-xs px-2 py-1 rounded bg-black border border-white/10 text-lime-300 font-mono">Firebase</span> - <span class="text-xs px-2 py-1 rounded bg-black border border-white/10 text-lime-300 font-mono">GCC 11</span> + <span class="text-xs px-2 py-1 rounded bg-black border border-white/10 text-lime-300 font-mono">Gemini AI</span> + <span class="text-xs px-2 py-1 rounded bg-black border border-white/10 text-lime-300 font-mono">Render</span> + <span class="text-xs px-2 py-1 rounded bg-black border border-white/10 text-lime-300 font-mono">WebSocket</span> </div> </div> </aside> @@ -238,41 +241,52 @@ def check_and_install_packages(code): </p> </section> - <!-- Sharing & Persistence --> - <section id="sharing" class="doc-section"> + <!-- AI Auto-Debug --> + <section id="ai-debug" class="doc-section"> <h2 class="text-2xl font-heading font-bold text-lime-300 mb-6 flex items-center"> - <span class="mr-3">05.</span> Sharing & Persistence + <span class="mr-3">05.</span> AI Auto-Debugger (New) </h2> <p class="text-neutral-300 mb-4"> - The sharing functionality leverages <strong>Firebase Firestore</strong> to provide persistent access to code and execution results. This ensures that sharing a link not only loads the correct source code but also restores the exact terminal state seen at the moment of sharing. + The latest version of the compiler includes an intelligent debugging assistant powered by <strong>Google Gemini 2.5 Flash</strong>. This feature automatically detects runtime errors and compilation failures in real-time. </p> - <h3 class="text-lg font-bold text-white mt-6 mb-2">How Sharing Works</h3> + <h3 class="text-lg font-bold text-white mt-6 mb-2">How it works</h3> <div class="grid grid-cols-1 md:grid-cols-2 gap-6 my-4"> <div class="p-6 rounded-xl bg-white/5 border border-white/10"> - <h4 class="text-white font-bold mb-2">1. Snapshot Creation</h4> + <h4 class="text-white font-bold mb-2">1. Error Detection</h4> <p class="text-sm text-neutral-400 mb-2"> - When the "Share" button is clicked, the system captures: + The frontend monitors the terminal output stream for keywords like <code>error:</code>, <code>warning:</code>, or <code>Traceback</code>. When detected, a "Ask AI" prompt appears overlaying the terminal. </p> - <ul class="list-disc list-inside text-xs text-neutral-500 space-y-1"> - <li>The complete source code from the editor.</li> - <li>The current terminal output (stripped of system messages).</li> - <li>Metadata (language, timestamp).</li> - </ul> </div> <div class="p-6 rounded-xl bg-white/5 border border-white/10"> - <h4 class="text-white font-bold mb-2">2. Storage & Retrieval</h4> + <h4 class="text-white font-bold mb-2">2. Contextual Analysis</h4> <p class="text-sm text-neutral-400 mb-2"> - This data is stored in a Firestore document under a unique 7-character ID. + When the user requests help, the system packages: </p> <ul class="list-disc list-inside text-xs text-neutral-500 space-y-1"> - <li><strong>Write:</strong> Uses Anonymous Auth to securely write to the DB.</li> - <li><strong>Read:</strong> The URL `?share=ID` fetches this document.</li> - <li><strong>Restore:</strong> The editor fills with code, and the terminal replays the saved output.</li> + <li>The full source code.</li> + <li>The exact error message from the terminal.</li> + <li>The language context.</li> </ul> </div> </div> - + + <div class="p-4 border-l-4 border-lime-300 bg-white/5 mt-4"> + <p class="text-sm text-neutral-300"> + <strong>Privacy Note:</strong> Your code is sent to the AI API <em>only</em> when you explicitly click the "Ask AI" link. The data is processed ephemerally and returned as a JSON object containing an explanation and the corrected code block. + </p> + </div> + </section> + + <!-- Sharing & Persistence --> + <section id="sharing" class="doc-section"> + <h2 class="text-2xl font-heading font-bold text-lime-300 mb-6 flex items-center"> + <span class="mr-3">06.</span> Sharing & Persistence + </h2> + <p class="text-neutral-300 mb-4"> + The sharing functionality leverages <strong>Firebase Firestore</strong> to provide persistent access to code and execution results. This ensures that sharing a link not only loads the correct source code but also restores the exact terminal state seen at the moment of sharing. + </p> + <div class="code-block"> <pre><code class="language-javascript">// Data Structure in Firestore { @@ -287,7 +301,7 @@ def check_and_install_packages(code): <!-- Security --> <section id="security" class="doc-section"> <h2 class="text-2xl font-heading font-bold text-lime-300 mb-6 flex items-center"> - <span class="mr-3">06.</span> Security & Limitations + <span class="mr-3">07.</span> Security & Limitations </h2> <p class="text-neutral-300 mb-4"> The current implementation runs code directly on the host instance. While effective for a portfolio demonstration or trusted environment, this setup is <strong>not sandboxed</strong>. @@ -315,7 +329,7 @@ def check_and_install_packages(code): <!-- Privacy & Data Handling --> <section id="privacy" class="doc-section"> <h2 class="text-2xl font-heading font-bold text-lime-300 mb-6 flex items-center"> - <span class="mr-3">07.</span> Privacy & Data Handling + <span class="mr-3">08.</span> Privacy & Data Handling </h2> <p class="text-neutral-300 mb-4"> We believe in complete transparency regarding how your data is handled. As a demonstration platform, we prioritize privacy and ephemeral execution. @@ -345,7 +359,7 @@ def check_and_install_packages(code): <!-- Legal & Disclaimer --> <section id="disclaimer" class="doc-section"> <h2 class="text-2xl font-heading font-bold text-lime-300 mb-6 flex items-center"> - <span class="mr-3">08.</span> Legal & Disclaimer + <span class="mr-3">09.</span> Legal & Disclaimer </h2> <div class="p-6 rounded-xl border border-red-500/20 bg-red-500/5"> <p class="text-neutral-300 mb-4"> diff --git a/public/index.html b/public/index.html @@ -250,7 +250,7 @@ <p class="text-neutral-400 max-w-xl mx-auto">A robust platform designed for real development tasks.</p> </div> - <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> + <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8"> <!-- Feature 1 --> <div class="compiler-card p-8" data-aos="fade-up" data-aos-delay="100"> <i class="fas fa-server text-2xl text-lime-300 mb-6"></i> @@ -269,6 +269,15 @@ <h3 class="text-xl font-bold text-white mb-3">Auto-Pip Install</h3> <p class="text-sm text-neutral-500 leading-relaxed">The server detects missing Python libraries and installs them on the fly.</p> </div> + <!-- Feature 4 (AI - NEW) --> + <div class="compiler-card p-8 border-lime-300/30" data-aos="fade-up" data-aos-delay="400"> + <div class="flex items-center justify-between mb-6"> + <i class="fas fa-robot text-2xl text-lime-300"></i> + <span class="text-[10px] bg-lime-300 text-black px-2 py-0.5 rounded font-bold uppercase">New</span> + </div> + <h3 class="text-xl font-bold text-white mb-3">AI Auto-Debug</h3> + <p class="text-sm text-neutral-500 leading-relaxed">Intelligent error detection and real-time code fixes powered by Gemini AI.</p> + </div> </div> </div> </section> diff --git a/public/python.html b/public/python.html @@ -145,6 +145,67 @@ 70% { transform: scale(1); box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); } 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } } + + /* AI Text Overlay */ + .ai-overlay { + position: absolute; + bottom: 10px; + right: 20px; + background: rgba(20, 20, 20, 0.9); + border: 1px solid #ef4444; + color: #fca5a5; + padding: 8px 12px; + border-radius: 6px; + font-family: 'JetBrains Mono', monospace; + font-size: 12px; + z-index: 60; + display: none; + backdrop-filter: blur(4px); + animation: slideIn 0.3s ease-out; + box-shadow: 0 4px 12px rgba(0,0,0,0.5); + } + .ai-overlay.visible { display: flex; align-items: center; gap: 8px; } + + .ai-link { + color: #fff; + text-decoration: underline; + text-decoration-style: dotted; + text-underline-offset: 4px; + cursor: pointer; + font-weight: bold; + transition: color 0.2s; + } + .ai-link:hover { color: #bef264; text-decoration-style: solid; } + + @keyframes slideIn { + from { transform: translateY(20px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } + } + + /* AI Modal */ + .modal-overlay { + position: fixed; top: 0; left: 0; width: 100%; height: 100%; + background: rgba(0,0,0,0.8); backdrop-filter: blur(5px); + z-index: 200; display: none; align-items: center; justify-content: center; + } + .modal-overlay.open { display: flex; } + + .modal-content { + background: #0f0f0f; + border: 1px solid var(--accent); + width: 90%; max-width: 600px; + max-height: 80vh; + border-radius: 12px; + display: flex; flex-direction: column; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + overflow: hidden; + animation: modalPop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + } + + @keyframes modalPop { + 0% { transform: scale(0.9); opacity: 0; } + 100% { transform: scale(1); opacity: 1; } + } </style> </head> <body class="antialiased selection:bg-lime-300 selection:text-black"> @@ -153,6 +214,59 @@ <div class="crt-scanline"></div> <div id="toast" class="toast">Link Copied to Clipboard!</div> + <!-- AI Overlay in Terminal --> + <div id="ai-suggestion" class="ai-overlay"> + <i class="fas fa-exclamation-triangle text-red-500"></i> + <span>Error detected. <span id="ask-ai-link" class="ai-link">Ask AI</span> to fix it.</span> + </div> + + <!-- AI Fix Modal --> + <div id="ai-modal" class="modal-overlay"> + <div class="modal-content"> + <!-- Modal Header --> + <div class="bg-white/5 px-6 py-4 border-b border-white/10 flex justify-between items-center"> + <h3 class="font-heading font-bold text-lg text-lime-300 flex items-center gap-2"> + <i class="fas fa-robot"></i> AI Diagnosis + </h3> + <button id="close-modal" class="text-neutral-500 hover:text-white transition-colors"> + <i class="fas fa-times"></i> + </button> + </div> + + <!-- Modal Body --> + <div class="p-6 overflow-y-auto custom-scrollbar flex-1"> + <div id="ai-loading" class="hidden flex flex-col items-center justify-center py-8 gap-4"> + <div class="pulse-dot connected" style="width: 12px; height: 12px;"></div> + <p class="font-mono text-neutral-400 text-sm animate-pulse">Analyzing compile error...</p> + </div> + + <div id="ai-result" class="hidden space-y-4"> + <div> + <h4 class="font-mono text-xs uppercase text-neutral-500 mb-2">Explanation</h4> + <p id="ai-explanation" class="text-sm text-neutral-200 leading-relaxed font-sans"></p> + </div> + + <div> + <h4 class="font-mono text-xs uppercase text-neutral-500 mb-2">Suggested Fix</h4> + <div class="relative group"> + <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> + </div> + </div> + </div> + + <div id="ai-error-msg" class="hidden text-red-400 text-sm font-mono text-center py-4"></div> + </div> + + <!-- Modal Footer --> + <div class="bg-white/5 px-6 py-4 border-t border-white/10 flex justify-end gap-3"> + <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> + <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"> + <i class="fas fa-check"></i> Apply Fix + </button> + </div> + </div> + </div> + <!-- Header (Responsive) --> <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"> @@ -214,10 +328,6 @@ print(time.strftime("%H:%M:%S")) print("Done.")</textarea> </div> - <!-- AI Fix Button --> - <button id="ai-fix-btn" class="hidden absolute bottom-4 right-4 bg-red-500/10 border border-red-500/50 text-red-400 hover:bg-red-500 hover:text-white px-3 py-1.5 rounded-lg text-xs font-mono transition-all items-center gap-2 backdrop-blur-md z-20"> - <i class="fas fa-magic"></i> Fix with AI - </button> </div> <!-- Terminal --> @@ -273,16 +383,29 @@ print("Done.")</textarea> const resetBtn = document.getElementById('reset-btn'); const shareBtn = document.getElementById('share-btn'); const copyBtn = document.getElementById('copy-btn'); - const aiFixBtn = document.getElementById('ai-fix-btn'); const connDot = document.getElementById('conn-dot'); const connectionStatus = document.getElementById('connection-status'); const codeInput = document.getElementById('code-input'); const toast = document.getElementById('toast'); + + // AI Elements + const aiSuggestion = document.getElementById('ai-suggestion'); + const askAiLink = document.getElementById('ask-ai-link'); + const aiModal = document.getElementById('ai-modal'); + const closeModal = document.getElementById('close-modal'); + const cancelFixBtn = document.getElementById('cancel-fix-btn'); + const applyFixBtn = document.getElementById('apply-fix-btn'); + const aiLoading = document.getElementById('ai-loading'); + const aiResult = document.getElementById('ai-result'); + const aiExplanation = document.getElementById('ai-explanation'); + const aiCodePreview = document.getElementById('ai-code-preview'); + const aiErrorMsg = document.getElementById('ai-error-msg'); // --- State --- let socket = null; let currentLine = ""; let startTime = 0; + let lastSuggestedCode = ""; // --- EDITOR LOGIC (Indentation for Python) --- codeInput.addEventListener('keydown', function(e) { @@ -453,8 +576,10 @@ print("Done.")</textarea> if (msg.type === 'stdout') { let text = msg.data; + // Trigger AI Overlay on Error + // Python errors: "Traceback (most recent call last):", "SyntaxError:", "NameError:", etc. if (text.toLowerCase().includes("error") || text.toLowerCase().includes("traceback")) { - aiFixBtn.classList.remove('hidden'); + aiSuggestion.classList.add('visible'); } if (!text.includes('\r')) { @@ -466,6 +591,10 @@ print("Done.")</textarea> const duration = (Date.now() - startTime) / 1000; term.write(`\r\n\x1b[38;2;115;115;115m[Finished in ${duration.toFixed(3)}s]\x1b[0m\r\n`); } + } else if (msg.type === 'ai_response') { + handleAiResponse(msg.data); + } else if (msg.type === 'ai_error') { + showAiError(msg.msg); } }; } @@ -477,7 +606,7 @@ print("Done.")</textarea> document.activeElement.blur(); term.reset(); currentLine = ""; - aiFixBtn.classList.add('hidden'); + aiSuggestion.classList.remove('visible'); // Hide previous suggestions let code = codeInput.value; // Basic sanitization @@ -495,10 +624,73 @@ print("Done.")</textarea> term.focus(); }); + // --- AI Feature Logic --- + + // 1. Open Modal and Request AI Fix + askAiLink.addEventListener('click', () => { + const errorContext = getTerminalContent(); + const currentCode = codeInput.value; + + // UI Update + aiModal.classList.add('open'); + aiLoading.classList.remove('hidden'); + aiResult.classList.add('hidden'); + aiErrorMsg.classList.add('hidden'); + applyFixBtn.disabled = true; + + // Send Request + socket.send(JSON.stringify({ + type: 'ai_fix', + code: currentCode, + error: errorContext, + language: 'python' + })); + }); + + // 2. Handle AI Response + function handleAiResponse(data) { + aiLoading.classList.add('hidden'); + aiResult.classList.remove('hidden'); + + aiExplanation.textContent = data.explanation; + aiCodePreview.textContent = data.fixed_code; + lastSuggestedCode = data.fixed_code; + + applyFixBtn.disabled = false; + } + + // 3. Handle AI Error + function showAiError(msg) { + aiLoading.classList.add('hidden'); + aiErrorMsg.textContent = msg; + aiErrorMsg.classList.remove('hidden'); + } + + // 4. Apply Fix + applyFixBtn.addEventListener('click', () => { + if (lastSuggestedCode) { + codeInput.value = lastSuggestedCode; + closeAiModal(); + showToast("Fix Applied Successfully!"); + aiSuggestion.classList.remove('visible'); + } + }); + + // 5. Modal Controls + function closeAiModal() { + aiModal.classList.remove('open'); + } + closeModal.addEventListener('click', closeAiModal); + cancelFixBtn.addEventListener('click', closeAiModal); + aiModal.addEventListener('click', (e) => { + if (e.target === aiModal) closeAiModal(); + }); + // --- Reset Logic --- resetBtn.addEventListener('click', () => { term.reset(); currentLine = ""; + aiSuggestion.classList.remove('visible'); term.write(`\x1b[38;2;190;242;100m> CONNECTION ESTABLISHED.\x1b[0m\r\n> python3 main.py\r\n`); }); @@ -595,10 +787,6 @@ print("Done.")</textarea> document.body.removeChild(textArea); } - aiFixBtn.addEventListener('click', () => { - showToast("AI Feature: Coming Soon via Backend Update"); - }); - // Terminal Input term.onData((data) => { if (!socket || socket.readyState !== WebSocket.OPEN) return;
© notamitgamer • Site Built: 2026-07-21 13:58:23 UTC • git-mirror commit: 1037f62 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror