Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>CodeCraft Tablet AI</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js"></script> | |
| <style> | |
| .code-area { | |
| font-family: 'Courier New', monospace; | |
| tab-size: 4; | |
| } | |
| .diff-added { | |
| background-color: rgba(74, 222, 128, 0.3); | |
| } | |
| .diff-removed { | |
| background-color: rgba(248, 113, 113, 0.3); | |
| } | |
| .touch-button { | |
| min-height: 48px; | |
| min-width: 120px; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100"> | |
| <div class="container mx-auto px-4 py-6 max-w-4xl"> | |
| <!-- Header --> | |
| <header class="mb-8"> | |
| <h1 class="text-3xl font-bold text-gray-800">CodeCraft Tablet AI</h1> | |
| <p class="text-gray-600">Offline code generator for Android tablets</p> | |
| </header> | |
| <!-- Main Tabs --> | |
| <div class="mb-6"> | |
| <div class="flex overflow-x-auto space-x-2"> | |
| <button id="tab-code" class="px-4 py-2 bg-blue-500 text-white rounded-lg touch-button active">Code Generator</button> | |
| <button id="tab-bugfix" class="px-4 py-2 bg-gray-300 rounded-lg touch-button">Bug Fixer</button> | |
| <button id="tab-goals" class="px-4 py-2 bg-gray-300 rounded-lg touch-button">Goal Manager</button> | |
| <button id="tab-history" class="px-4 py-2 bg-gray-300 rounded-lg touch-button">History</button> | |
| </div> | |
| </div> | |
| <!-- Code Generator Tab --> | |
| <div id="content-code" class="tab-content"> | |
| <div class="bg-white rounded-lg shadow-md p-6 mb-6"> | |
| <h2 class="text-xl font-semibold mb-4">Describe what you want to code</h2> | |
| <textarea id="code-prompt" class="w-full p-3 border rounded-lg mb-4" rows="4" placeholder="e.g. Create a Python function to calculate factorial..."></textarea> | |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-2 mb-4"> | |
| <select id="language-select" class="p-2 border rounded-lg"> | |
| <option value="python">Python</option> | |
| <option value="javascript">JavaScript</option> | |
| <option value="html">HTML</option> | |
| <option value="css">CSS</option> | |
| <option value="c">C</option> | |
| <option value="cpp">C++</option> | |
| <option value="java">Java</option> | |
| </select> | |
| <button id="generate-code" class="bg-blue-500 text-white p-2 rounded-lg touch-button">Generate Code</button> | |
| <button id="explain-code" class="bg-green-500 text-white p-2 rounded-lg touch-button">Explain</button> | |
| <button id="optimize-code" class="bg-purple-500 text-white p-2 rounded-lg touch-button">Optimize</button> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium mb-1">Generated Code:</label> | |
| <pre id="generated-code" class="code-area bg-gray-100 p-4 rounded-lg overflow-auto" style="min-height: 200px;"></pre> | |
| </div> | |
| <div class="flex space-x-2"> | |
| <button id="copy-code" class="bg-gray-300 p-2 rounded-lg touch-button flex items-center"> | |
| <i data-feather="copy" class="mr-1"></i> Copy | |
| </button> | |
| <button id="download-code" class="bg-gray-300 p-2 rounded-lg touch-button flex items-center"> | |
| <i data-feather="download" class="mr-1"></i> Download | |
| </button> | |
| </div> | |
| </div> | |
| <div id="code-explanation" class="bg-white rounded-lg shadow-md p-6 hidden"> | |
| <h2 class="text-xl font-semibold mb-4">Code Explanation</h2> | |
| <div id="explanation-content" class="prose"></div> | |
| </div> | |
| </div> | |
| <!-- Bug Fixer Tab --> | |
| <div id="content-bugfix" class="tab-content hidden"> | |
| <div class="bg-white rounded-lg shadow-md p-6 mb-6"> | |
| <h2 class="text-xl font-semibold mb-4">Paste your buggy code</h2> | |
| <textarea id="buggy-code" class="w-full p-3 border rounded-lg mb-4 code-area" rows="8" placeholder="Paste your code here..."></textarea> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium mb-1">Error description (optional):</label> | |
| <input id="error-description" type="text" class="w-full p-2 border rounded-lg" placeholder="e.g. 'IndexError: list index out of range'"> | |
| </div> | |
| <button id="fix-code" class="bg-red-500 text-white p-2 rounded-lg touch-button mb-4">Find and Fix Bugs</button> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium mb-1">Fixed Code:</label> | |
| <pre id="fixed-code" class="code-area bg-gray-100 p-4 rounded-lg overflow-auto" style="min-height: 200px;"></pre> | |
| </div> | |
| <div id="diff-view" class="hidden"> | |
| <h3 class="text-lg font-medium mb-2">Changes:</h3> | |
| <div id="diff-content" class="bg-gray-100 p-4 rounded-lg overflow-auto"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Goal Manager Tab --> | |
| <div id="content-goals" class="tab-content hidden"> | |
| <div class="bg-white rounded-lg shadow-md p-6 mb-6"> | |
| <h2 class="text-xl font-semibold mb-4">What do you want to achieve?</h2> | |
| <textarea id="goal-input" class="w-full p-3 border rounded-lg mb-4" rows="3" placeholder="e.g. I want to build a calculator app with Python..."></textarea> | |
| <button id="generate-goal" class="bg-blue-500 text-white p-2 rounded-lg touch-button mb-6">Generate Plan</button> | |
| <div id="goal-plan" class="hidden"> | |
| <h3 class="text-lg font-medium mb-2">Project Plan:</h3> | |
| <div id="goal-steps" class="mb-6 space-y-2"></div> | |
| <div class="flex space-x-2"> | |
| <button id="start-project" class="bg-green-500 text-white p-2 rounded-lg touch-button">Start Project</button> | |
| <button id="save-goal" class="bg-gray-300 p-2 rounded-lg touch-button">Save Plan</button> | |
| </div> | |
| </div> | |
| <div id="active-goals" class="mt-8"> | |
| <h3 class="text-lg font-medium mb-2">Active Goals</h3> | |
| <div id="goals-list" class="space-y-2"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- History Tab --> | |
| <div id="content-history" class="tab-content hidden"> | |
| <div class="bg-white rounded-lg shadow-md p-6"> | |
| <h2 class="text-xl font-semibold mb-4">Your History</h2> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium mb-1">Filter by type:</label> | |
| <select id="history-filter" class="p-2 border rounded-lg"> | |
| <option value="all">All</option> | |
| <option value="code">Code Generation</option> | |
| <option value="bugfix">Bug Fixes</option> | |
| <option value="goal">Goals</option> | |
| </select> | |
| </div> | |
| <div id="history-items" class="space-y-4"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Status Bar --> | |
| <div class="fixed bottom-0 left-0 right-0 bg-gray-800 text-white p-2 text-sm"> | |
| <div class="container mx-auto flex justify-between items-center"> | |
| <span id="status-message">Ready</span> | |
| <span id="model-status">Model: Offline Templates</span> | |
| </div> | |
| </div> | |
| <script> | |
| // Initialize feather icons | |
| feather.replace(); | |
| // Tab switching | |
| document.querySelectorAll('[id^="tab-"]').forEach(tab => { | |
| tab.addEventListener('click', () => { | |
| // Update active tab | |
| document.querySelectorAll('[id^="tab-"]').forEach(t => { | |
| t.classList.remove('bg-blue-500', 'text-white'); | |
| t.classList.add('bg-gray-300'); | |
| }); | |
| tab.classList.remove('bg-gray-300'); | |
| tab.classList.add('bg-blue-500', 'text-white'); | |
| // Show corresponding content | |
| document.querySelectorAll('.tab-content').forEach(content => { | |
| content.classList.add('hidden'); | |
| }); | |
| document.getElementById(`content-${tab.id.split('-')[1]}`).classList.remove('hidden'); | |
| }); | |
| }); | |
| // Code generation with fallback to templates | |
| document.getElementById('generate-code').addEventListener('click', async () => { | |
| const prompt = document.getElementById('code-prompt').value; | |
| const language = document.getElementById('language-select').value; | |
| if (!prompt) { | |
| updateStatus('Please enter a description'); | |
| return; | |
| } | |
| updateStatus('Generating code...'); | |
| try { | |
| // Try to use WASM model first | |
| const generatedCode = await generateWithModel(prompt, language); | |
| document.getElementById('generated-code').textContent = generatedCode; | |
| saveToHistory('code', {prompt, language, code: generatedCode}); | |
| updateStatus('Code generated successfully'); | |
| } catch (error) { | |
| console.error('Model error:', error); | |
| updateStatus('Model failed, using offline template'); | |
| // Fallback to template | |
| const templateCode = generateFromTemplate(prompt, language); | |
| document.getElementById('generated-code').textContent = templateCode; | |
| saveToHistory('code', {prompt, language, code: templateCode, usedTemplate: true}); | |
| updateStatus('Code generated from template'); | |
| } | |
| }); | |
| // Bug fixing functionality | |
| document.getElementById('fix-code').addEventListener('click', async () => { | |
| const buggyCode = document.getElementById('buggy-code').value; | |
| const errorDesc = document.getElementById('error-description').value; | |
| if (!buggyCode) { | |
| updateStatus('Please paste your buggy code'); | |
| return; | |
| } | |
| updateStatus('Analyzing code for bugs...'); | |
| try { | |
| // Try to use WASM model first | |
| const fixedCode = await fixWithModel(buggyCode, errorDesc); | |
| document.getElementById('fixed-code').textContent = fixedCode; | |
| // Show diff | |
| showDiff(buggyCode, fixedCode); | |
| saveToHistory('bugfix', {buggyCode, errorDesc, fixedCode}); | |
| updateStatus('Bugs fixed successfully'); | |
| } catch (error) { | |
| console.error('Model error:', error); | |
| updateStatus('Model failed, using offline template'); | |
| // Fallback to template | |
| const fixedCode = fixFromTemplate(buggyCode, errorDesc); | |
| document.getElementById('fixed-code').textContent = fixedCode; | |
| showDiff(buggyCode, fixedCode); | |
| saveToHistory('bugfix', {buggyCode, errorDesc, fixedCode, usedTemplate: true}); | |
| updateStatus('Bugs fixed from template'); | |
| } | |
| }); | |
| // Goal generation | |
| document.getElementById('generate-goal').addEventListener('click', async () => { | |
| const goalText = document.getElementById('goal-input').value; | |
| if (!goalText) { | |
| updateStatus('Please describe your goal'); | |
| return; | |
| } | |
| updateStatus('Generating project plan...'); | |
| try { | |
| // Try to use WASM model first | |
| const goalPlan = await generateGoalPlan(goalText); | |
| displayGoalPlan(goalPlan); | |
| saveToHistory('goal', {goalText, plan: goalPlan}); | |
| updateStatus('Project plan generated'); | |
| } catch (error) { | |
| console.error('Model error:', error); | |
| updateStatus('Model failed, using offline template'); | |
| // Fallback to template | |
| const goalPlan = generateGoalFromTemplate(goalText); | |
| displayGoalPlan(goalPlan); | |
| saveToHistory('goal', {goalText, plan: goalPlan, usedTemplate: true}); | |
| updateStatus('Project plan generated from template'); | |
| } | |
| }); | |
| // Helper functions | |
| function updateStatus(message) { | |
| document.getElementById('status-message').textContent = message; | |
| } | |
| function showDiff(original, modified) { | |
| // Simple diff implementation for tablet display | |
| const originalLines = original.split('\n'); | |
| const modifiedLines = modified.split('\n'); | |
| let diffHtml = ''; | |
| for (let i = 0; i < Math.max(originalLines.length, modifiedLines.length); i++) { | |
| const originalLine = originalLines[i] || ''; | |
| const modifiedLine = modifiedLines[i] || ''; | |
| if (originalLine !== modifiedLine) { | |
| if (originalLine && !modifiedLine) { | |
| diffHtml += `<div class="diff-removed">- ${escapeHtml(originalLine)}</div>`; | |
| } else if (!originalLine && modifiedLine) { | |
| diffHtml += `<div class="diff-added">+ ${escapeHtml(modifiedLine)}</div>`; | |
| } else { | |
| diffHtml += `<div class="diff-removed">- ${escapeHtml(originalLine)}</div>`; | |
| diffHtml += `<div class="diff-added">+ ${escapeHtml(modifiedLine)}</div>`; | |
| } | |
| } else { | |
| diffHtml += `<div> ${escapeHtml(originalLine)}</div>`; | |
| } | |
| } | |
| document.getElementById('diff-content').innerHTML = diffHtml; | |
| document.getElementById('diff-view').classList.remove('hidden'); | |
| } | |
| function escapeHtml(unsafe) { | |
| return unsafe | |
| .replace(/&/g, "&") | |
| .replace(/</g, "<") | |
| .replace(/>/g, ">") | |
| .replace(/"/g, """) | |
| .replace(/'/g, "'"); | |
| } | |
| function displayGoalPlan(plan) { | |
| const stepsContainer = document.getElementById('goal-steps'); | |
| stepsContainer.innerHTML = ''; | |
| plan.steps.forEach((step, index) => { | |
| const stepElement = document.createElement('div'); | |
| stepElement.className = 'p-3 bg-gray-100 rounded-lg'; | |
| stepElement.innerHTML = ` | |
| <div class="font-medium">Step ${index + 1}: ${step.title}</div> | |
| <div class="text-sm text-gray-600">${step.description}</div> | |
| ${step.code ? `<pre class="mt-2 p-2 bg-gray-200 text-sm overflow-x-auto">${step.code}</pre>` : ''} | |
| `; | |
| stepsContainer.appendChild(stepElement); | |
| }); | |
| document.getElementById('goal-plan').classList.remove('hidden'); | |
| } | |
| // Model functions with optimized WASM implementation | |
| let modelLoaded = false; | |
| let session = null; | |
| async function loadModel() { | |
| try { | |
| // Load a tiny, optimized model for mobile devices | |
| const modelPath = './models/tiny-codegen-16m-quant.onnx'; | |
| const options = { | |
| executionProviders: ['wasm'], | |
| graphOptimizationLevel: 'all', | |
| enableCpuMemArena: true, | |
| }; | |
| session = await ort.InferenceSession.create(modelPath, options); | |
| modelLoaded = true; | |
| document.getElementById('model-status').textContent = 'Model: Loaded (WASM)'; | |
| return true; | |
| } catch (error) { | |
| console.error('Model load failed:', error); | |
| document.getElementById('model-status').textContent = 'Model: Offline Templates'; | |
| return false; | |
| } | |
| } | |
| async function generateWithModel(prompt, language) { | |
| if (!modelLoaded && !(await loadModel())) { | |
| throw new Error('Model not loaded'); | |
| } | |
| // More efficient prompt encoding | |
| const tokens = new Array(64).fill(0); | |
| const words = prompt.toLowerCase().split(/\s+/); | |
| for (let i = 0; i < Math.min(words.length, 64); i++) { | |
| tokens[i] = words[i].length % 100; | |
| } | |
| const input = { | |
| input_ids: new ort.Tensor('int32', tokens, [1, 64]) | |
| }; | |
| try { | |
| const results = await session.run(input); | |
| let output = ''; | |
| // Process output more efficiently | |
| for (let i = 0; i < results.output.data.length; i++) { | |
| const val = results.output.data[i]; | |
| if (val === 0) break; | |
| output += String.fromCharCode(97 + (val % 26)); | |
| } | |
| return formatOutput(output, language); | |
| } catch (error) { | |
| console.error('Inference error:', error); | |
| throw error; | |
| } | |
| } | |
| async function fixWithModel(code, errorDesc) { | |
| if (!modelLoaded && !(await loadModel())) { | |
| throw new Error('Model not loaded'); | |
| } | |
| const input = { | |
| input_ids: new ort.Tensor('int32', encodePrompt(`Fix this ${errorDesc}:\n${code}`, 'fix'), [1, 256]) | |
| }; | |
| const results = await session.run(input); | |
| return decodeOutput(results.output.data, 'fix'); | |
| } | |
| async function generateGoalPlan(goalText) { | |
| if (!modelLoaded && !(await loadModel())) { | |
| throw new Error('Model not loaded'); | |
| } | |
| const input = { | |
| input_ids: new ort.Tensor('int32', encodePrompt(`Plan for: ${goalText}`, 'plan'), [1, 128]) | |
| }; | |
| const results = await session.run(input); | |
| return parsePlanOutput(decodeOutput(results.output.data, 'plan')); | |
| } | |
| // Helper functions for model I/O | |
| function encodePrompt(prompt, type) { | |
| // Simplified tokenization for demo | |
| const tokens = []; | |
| const words = prompt.toLowerCase().split(/\s+/); | |
| for (const word of words) { | |
| tokens.push(word.length % 100); // Simple hash-like tokenization | |
| } | |
| // Pad to fixed length | |
| while (tokens.length < 128) tokens.push(0); | |
| return tokens.slice(0, 128); | |
| } | |
| function decodeOutput(tensor, type) { | |
| // Convert output tensor to text | |
| let output = ''; | |
| for (const val of tensor) { | |
| if (val === 0) break; | |
| output += String.fromCharCode(97 + (val % 26)); | |
| } | |
| return formatOutput(output, type); | |
| } | |
| function formatOutput(text, type) { | |
| switch(type) { | |
| case 'python': | |
| return `# Generated code\n${text.replace(/(\n)/g, '\n# ')}`; | |
| case 'fix': | |
| return `// Fixed code\n${text}`; | |
| case 'plan': | |
| return parsePlanOutput(text); | |
| default: | |
| return text; | |
| } | |
| } | |
| function parsePlanOutput(text) { | |
| const steps = text.split('\n').filter(l => l.trim()); | |
| return { | |
| title: steps[0] || 'Project Plan', | |
| steps: steps.slice(1).map((step, i) => ({ | |
| title: `Step ${i+1}`, | |
| description: step | |
| })) | |
| }; | |
| } | |
| // Enhanced template-based fallbacks | |
| function generateFromTemplate(prompt, language) { | |
| const lowerPrompt = prompt.toLowerCase(); | |
| // Extended template library | |
| const templates = { | |
| python: { | |
| 'factorial': `def factorial(n): | |
| """Calculate factorial recursively""" | |
| return 1 if n == 0 else n * factorial(n-1)`, | |
| 'fibonacci': `def fibonacci(n): | |
| """Calculate Fibonacci sequence""" | |
| a, b = 0, 1 | |
| for _ in range(n): | |
| a, b = b, a + b | |
| return a`, | |
| 'prime': `def is_prime(n): | |
| """Check if number is prime""" | |
| if n < 2: return False | |
| for i in range(2, int(n**0.5)+1): | |
| if n % i == 0: return False | |
| return True`, | |
| 'sort list': `def sort_list(items): | |
| """Sort list in ascending order""" | |
| return sorted(items)`, | |
| 'dictionary': `data = { | |
| # Key-value pairs | |
| }` | |
| }, | |
| javascript: { | |
| 'factorial': `function factorial(n) { | |
| if (n === 0) return 1; | |
| return n * factorial(n - 1); | |
| }`, | |
| 'fibonacci': `function fibonacci(n) { | |
| if (n <= 1) return n; | |
| return fibonacci(n - 1) + fibonacci(n - 2); | |
| }` | |
| }, | |
| html: { | |
| 'calculator': `<!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Calculator</title> | |
| <style> | |
| .calculator { /* basic styles */ } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="calculator"> | |
| <!-- Calculator UI would go here --> | |
| </div> | |
| <script src="calculator.js"></script> | |
| </body> | |
| </html>` | |
| } | |
| }; | |
| // Check if prompt matches any template | |
| const lowerPrompt = prompt.toLowerCase(); | |
| for (const [key, code] of Object.entries(templates[language] || {})) { | |
| if (lowerPrompt.includes(key)) { | |
| return code; | |
| } | |
| } | |
| // Default template | |
| switch(language) { | |
| case 'python': | |
| return `# ${prompt}\ndef main():\n # Your code here\n pass\n\nif __name__ == "__main__":\n main()`; | |
| case 'javascript': | |
| return `// ${prompt}\nfunction main() {\n // Your code here\n}\n\nmain();`; | |
| case 'html': | |
| return `\n<html>\n<head>\n <title>${prompt}</title>\n</head>\n<body>\n <!-- Your content here -->\n</body>\n</html>`; | |
| default: | |
| return `/* ${prompt} */\n// Your ${language} code here`; | |
| } | |
| } | |
| function fixFromTemplate(code, errorDesc) { | |
| const lowerError = errorDesc?.toLowerCase() || ''; | |
| const lines = code.split('\n'); | |
| // Enhanced error detection with line numbers | |
| const lineMatch = lowerError.match(/line (\d+)/); | |
| let problemLine = lineMatch ? parseInt(lineMatch[1]) - 1 : -1; | |
| // More comprehensive error patterns | |
| if (lowerError.includes('indexerror') || lowerError.includes('out of range')) { | |
| // Add bounds checking with line targeting | |
| if (problemLine >= 0 && problemLine < lines.length) { | |
| lines[problemLine] = lines[problemLine].replace( | |
| /(\w+)\s*\[([^\]]+)\]/g, | |
| (match, arr, idx) => `if 0 <= ${idx} < len(${arr}):\n ${match}\nelse:\n # Handle out of range` | |
| ); | |
| } else { | |
| // Global replacement if line not specified | |
| code = code.replace(/(\w+)\s*\[([^\]]+)\]/g, | |
| (match, arr, idx) => `if 0 <= ${idx} < len(${arr}):\n ${match}\nelse:\n # Handle out of range`); | |
| } | |
| } | |
| if (lowerError.includes('syntaxerror') || lowerError.includes('invalid syntax')) { | |
| // More syntax fixes with context awareness | |
| if (problemLine >= 0 && problemLine < lines.length) { | |
| const line = lines[problemLine]; | |
| if (line.includes('=') && !line.includes('==')) { | |
| lines[problemLine] = line.replace(/=([^=])/g, '==$1'); | |
| } | |
| if (line.trim().endsWith(':')) { | |
| lines[problemLine] += ' pass'; | |
| } | |
| if (line.includes('print ') && !line.includes('(')) { | |
| lines[problemLine] = line.replace(/print (\w+)/, 'print($1)'); | |
| } | |
| } | |
| } | |
| // Add type conversion where needed | |
| if (lowerError.includes('typeerror') || lowerError.includes('concatenate')) { | |
| for (let i = 0; i < lines.length; i++) { | |
| if (lines[i].includes('+')) { | |
| lines[i] = lines[i].replace(/"([^"]*)"\s*\+\s*([^+\s;]+)/g, | |
| '"$1" + str($2)'); | |
| } | |
| } | |
| } | |
| // For undefined errors, wrap in try-catch | |
| if (problemLine >= 0 && !lowerError.includes('indentationerror')) { | |
| const indent = lines[problemLine].match(/^\s*/)[0]; | |
| lines[problemLine] = `${indent}try:\n${indent} ${lines[problemLine].trim()}\n${indent}except Exception as e:\n${indent} print(f"Error: {e}")`; | |
| } | |
| return lines.join('\n'); | |
| } | |
| function generateGoalFromTemplate(goalText) { | |
| // Simple goal breakdown template | |
| return { | |
| title: goalText, | |
| steps: [ | |
| { | |
| title: "Setup project structure", | |
| description: "Create basic files and folders for your project", | |
| code: "mkdir myproject\ncd myproject" | |
| }, | |
| { | |
| title: "Implement core functionality", | |
| description: "Write the main code that makes your project work" | |
| }, | |
| { | |
| title: "Add user interface", | |
| description: "Create the visual elements users will interact with" | |
| }, | |
| { | |
| title: "Test and debug", | |
| description: "Find and fix any issues in your code" | |
| }, | |
| { | |
| title: "Document your project", | |
| description: "Write instructions so others can use your project" | |
| } | |
| ] | |
| }; | |
| } | |
| // Enhanced history management with IndexedDB | |
| const dbName = 'CodeCraftDB'; | |
| const dbVersion = 1; | |
| let db; | |
| function initDB() { | |
| return new Promise((resolve, reject) => { | |
| const request = indexedDB.open(dbName, dbVersion); | |
| request.onupgradeneeded = (event) => { | |
| db = event.target.result; | |
| if (!db.objectStoreNames.contains('history')) { | |
| const store = db.createObjectStore('history', { keyPath: 'id', autoIncrement: true }); | |
| store.createIndex('type', 'type', { unique: false }); | |
| store.createIndex('timestamp', 'timestamp', { unique: false }); | |
| } | |
| }; | |
| request.onsuccess = (event) => { | |
| db = event.target.result; | |
| resolve(db); | |
| }; | |
| request.onerror = (event) => { | |
| console.error('IndexedDB error:', event.target.error); | |
| reject(event.target.error); | |
| }; | |
| }); | |
| } | |
| async function saveToHistory(type, data) { | |
| if (!db) { | |
| try { | |
| await initDB(); | |
| } catch (error) { | |
| console.error('Failed to init DB, falling back to localStorage'); | |
| return saveToLocalStorage(type, data); | |
| } | |
| } | |
| const transaction = db.transaction(['history'], 'readwrite'); | |
| const store = transaction.objectStore('history'); | |
| const historyItem = { | |
| type, | |
| data, | |
| timestamp: new Date().toISOString() | |
| }; | |
| return new Promise((resolve) => { | |
| const request = store.add(historyItem); | |
| request.onsuccess = () => { | |
| resolve(); | |
| }; | |
| request.onerror = (event) => { | |
| console.error('Error saving to IndexedDB:', event.target.error); | |
| saveToLocalStorage(type, data); | |
| resolve(); | |
| }; | |
| }); | |
| } | |
| function saveToLocalStorage(type, data) { | |
| const historyItem = { | |
| type, | |
| data, | |
| timestamp: new Date().toISOString() | |
| }; | |
| const history = JSON.parse(localStorage.getItem('codecraft-history') || '[]'); | |
| history.unshift(historyItem); | |
| localStorage.setItem('codecraft-history', JSON.stringify(history.slice(0, 100))); | |
| } | |
| async function loadHistory(filter = 'all') { | |
| if (!db) { | |
| try { | |
| await initDB(); | |
| } catch (error) { | |
| console.error('Failed to init DB, using localStorage'); | |
| return loadFromLocalStorage(filter); | |
| } | |
| } | |
| return new Promise((resolve) => { | |
| const transaction = db.transaction(['history'], 'readonly'); | |
| const store = transaction.objectStore('history'); | |
| const index = store.index('timestamp'); | |
| const request = index.openCursor(null, 'prev'); | |
| const results = []; | |
| request.onsuccess = (event) => { | |
| const cursor = event.target.result; | |
| if (cursor) { | |
| if (filter === 'all' || cursor.value.type === filter) { | |
| results.push(cursor.value); | |
| } | |
| cursor.continue(); | |
| } else { | |
| resolve(results.slice(0, 100)); | |
| } | |
| }; | |
| request.onerror = (event) => { | |
| console.error('Error loading from IndexedDB:', event.target.error); | |
| resolve(loadFromLocalStorage(filter)); | |
| }; | |
| }); | |
| } | |
| function loadFromLocalStorage(filter) { | |
| const history = JSON.parse(localStorage.getItem('codecraft-history') || '[]'); | |
| return filter === 'all' ? history : history.filter(item => item.type === filter); | |
| } | |
| async function loadHistory(filter = 'all') { | |
| if (!db) { | |
| try { | |
| await initDB(); | |
| } catch (error) { | |
| console.error('DB init failed, using localStorage'); | |
| const history = JSON.parse(localStorage.getItem('codecraft-history') || '[]'); | |
| return filter === 'all' ? history : history.filter(item => item.type === filter); | |
| } | |
| } | |
| return new Promise((resolve) => { | |
| const transaction = db.transaction(['history'], 'readonly'); | |
| const store = transaction.objectStore('history'); | |
| const index = store.index('timestamp'); | |
| const request = index.openCursor(null, 'prev'); | |
| const results = []; | |
| request.onsuccess = (event) => { | |
| const cursor = event.target.result; | |
| if (cursor) { | |
| if (filter === 'all' || cursor.value.type === filter) { | |
| results.push(cursor.value); | |
| } | |
| if (results.length < 100) { | |
| cursor.continue(); | |
| } else { | |
| resolve(results); | |
| } | |
| } else { | |
| resolve(results); | |
| } | |
| }; | |
| request.onerror = () => { | |
| const history = JSON.parse(localStorage.getItem('codecraft-history') || '[]'); | |
| resolve(filter === 'all' ? history : history.filter(item => item.type === filter)); | |
| }; | |
| }); | |
| } | |
| // Initialize the app with model loading | |
| document.addEventListener('DOMContentLoaded', async () => { | |
| // Initialize IndexedDB | |
| try { | |
| await initDB(); | |
| } catch (error) { | |
| console.error('IndexedDB initialization failed:', error); | |
| } | |
| // Check and load model | |
| if (typeof ort !== 'undefined') { | |
| document.getElementById('model-status').textContent = 'Model: Loading...'; | |
| try { | |
| await loadModel(); | |
| } catch (error) { | |
| console.error('Model loading failed:', error); | |
| document.getElementById('model-status').textContent = 'Model: Offline Templates'; | |
| } | |
| } | |
| // Load history when history tab is shown | |
| document.getElementById('tab-history').addEventListener('click', () => { | |
| const filter = document.getElementById('history-filter').value; | |
| const historyItems = loadHistory(filter); | |
| const container = document.getElementById('history-items'); | |
| container.innerHTML = ''; | |
| historyItems.forEach(item => { | |
| const itemElement = document.createElement('div'); | |
| itemElement.className = 'p-4 bg-white rounded-lg shadow'; | |
| let content = ''; | |
| if (item.type === 'code') { | |
| content = ` | |
| <div class="font-medium">Code Generation (${item.data.language})</div> | |
| <div class="text-sm text-gray-600 mb-2">${item.data.prompt}</div> | |
| <pre class="p-2 bg-gray-100 text-sm overflow-x-auto">${item.data.code.substring(0, 100)}...</pre> | |
| `; | |
| } else if (item.type === 'bugfix') { | |
| content = ` | |
| <div class="font-medium">Bug Fix</div> | |
| <div class="text-sm text-gray-600 mb-2">${item.data.errorDesc || 'No error description'}</div> | |
| <div class="text-sm">Fixed ${item.data.buggyCode.split('\n').length} lines of code</div> | |
| `; | |
| } else if (item.type === 'goal') { | |
| content = ` | |
| <div class="font-medium">Goal Plan</div> | |
| <div class="text-sm text-gray-600 mb-2">${item.data.goalText}</div> | |
| <div class="text-sm">${item.data.plan.steps.length} steps planned</div> | |
| `; | |
| } | |
| itemElement.innerHTML = content; | |
| container.appendChild(itemElement); | |
| }); | |
| }); | |
| // Copy and download functionality | |
| document.getElementById('copy-code').addEventListener('click', () => { | |
| const code = document.getElementById('generated-code').textContent; | |
| navigator.clipboard.writeText(code); | |
| updateStatus('Code copied to clipboard'); | |
| }); | |
| document.getElementById('download-code').addEventListener('click', () => { | |
| const code = document.getElementById('generated-code').textContent; | |
| const language = document.getElementById('language-select').value; | |
| const extension = { | |
| 'python': 'py', | |
| 'javascript': 'js', | |
| 'html': 'html', | |
| 'css': 'css', | |
| 'c': 'c', | |
| 'cpp': 'cpp', | |
| 'java': 'java' | |
| }[language] || 'txt'; | |
| const blob = new Blob([code], {type: 'text/plain'}); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `code.${extension}`; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| updateStatus('Code downloaded'); | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |