codecraft-tablet-ai / index.html
epcpm's picture
注意点・改善候補
9066da9 verified
<!DOCTYPE html>
<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, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
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 `<!DOCTYPE html>\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>