anycoder-5ddb602d / index.html
XXXMARK's picture
Upload folder using huggingface_hub
e019507 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CODE HUB with AnyCoder</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
code: {
bg: '#0f172a',
sidebar: '#1e293b',
accent: '#3b82f6',
text: '#e2e8f0',
comment: '#64748b'
}
},
fontFamily: {
mono: ['Fira Code', 'Monaco', 'Consolas', 'monospace']
}
}
}
}
</script>
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;600&display=swap" rel="stylesheet">
<script src="https://unpkg.com/lucide@latest"></script>
<style>
body { font-family: 'Fira Code', monospace; }
.scrollbar-hide::-webkit-scrollbar { display: none; }
.gradient-border {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 1px;
}
.glass-effect {
background: rgba(30, 41, 59, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.code-editor {
tab-size: 2;
caret-color: #3b82f6;
}
.syntax-keyword { color: #c678dd; }
.syntax-string { color: #98c379; }
.syntax-comment { color: #5c6370; font-style: italic; }
.syntax-function { color: #61afef; }
.pulse-glow {
animation: pulse-glow 2s ease-in-out infinite;
}
@keyframes pulse-glow {
0%, 100% { box-shadow: 0 0 20px rgba(59, 130, 246, 0.3); }
50% { box-shadow: 0 0 40px rgba(59, 130, 246, 0.6); }
}
</style>
</head>
<body class="bg-code-bg text-code-text min-h-screen">
<!-- Header -->
<header class="glass-effect sticky top-0 z-50 border-b border-white/10">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<div class="flex items-center space-x-3">
<div class="gradient-border rounded-lg">
<div class="bg-code-bg rounded-lg p-2">
<i data-lucide="code-2" class="w-6 h-6 text-blue-400"></i>
</div>
</div>
<div>
<h1 class="text-xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
CODE HUB
</h1>
<p class="text-xs text-code-comment">with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">AnyCoder</a></p>
</div>
</div>
<nav class="hidden md:flex items-center space-x-1">
<button onclick="switchTab('generate')" class="nav-btn px-4 py-2 rounded-lg text-sm font-medium transition-all hover:bg-white/10 active" data-tab="generate">
<i data-lucide="sparkles" class="w-4 h-4 inline mr-1"></i>Generate
</button>
<button onclick="switchTab('upload')" class="nav-btn px-4 py-2 rounded-lg text-sm font-medium transition-all hover:bg-white/10" data-tab="upload">
<i data-lucide="upload" class="w-4 h-4 inline mr-1"></i>Upload
</button>
<button onclick="switchTab('templates')" class="nav-btn px-4 py-2 rounded-lg text-sm font-medium transition-all hover:bg-white/10" data-tab="templates">
<i data-lucide="layout-template" class="w-4 h-4 inline mr-1"></i>Templates
</button>
</nav>
<div class="flex items-center space-x-3">
<button onclick="toggleTheme()" class="p-2 rounded-lg hover:bg-white/10 transition-colors">
<i data-lucide="moon" class="w-5 h-5"></i>
</button>
<button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all pulse-glow">
<i data-lucide="zap" class="w-4 h-4 inline mr-1"></i>Pro
</button>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Generate Tab -->
<div id="generate-tab" class="tab-content">
<div class="grid lg:grid-cols-2 gap-6">
<!-- Input Panel -->
<div class="space-y-6">
<div class="glass-effect rounded-xl p-6">
<h2 class="text-lg font-semibold mb-4 flex items-center">
<i data-lucide="message-square" class="w-5 h-5 mr-2 text-blue-400"></i>
Describe Your App
</h2>
<textarea id="prompt-input" class="w-full h-32 bg-code-bg/50 border border-white/10 rounded-lg p-4 text-sm resize-none focus:outline-none focus:border-blue-500 transition-colors" placeholder="Example: Create a todo list app with local storage, dark mode toggle, and drag-and-drop reordering..."></textarea>
<div class="mt-4">
<label class="text-sm text-code-comment mb-2 block">Quick Presets</label>
<div class="flex flex-wrap gap-2">
<button onclick="setPreset('landing')" class="px-3 py-1.5 bg-white/5 hover:bg-white/10 rounded-lg text-xs transition-colors">Landing Page</button>
<button onclick="setPreset('dashboard')" class="px-3 py-1.5 bg-white/5 hover:bg-white/10 rounded-lg text-xs transition-colors">Dashboard</button>
<button onclick="setPreset('api')" class="px-3 py-1.5 bg-white/5 hover:bg-white/10 rounded-lg text-xs transition-colors">REST API</button>
<button onclick="setPreset('game')" class="px-3 py-1.5 bg-white/5 hover:bg-white/10 rounded-lg text-xs transition-colors">Browser Game</button>
</div>
</div>
</div>
<div class="glass-effect rounded-xl p-6">
<h3 class="text-sm font-semibold mb-4 flex items-center">
<i data-lucide="settings" class="w-4 h-4 mr-2 text-purple-400"></i>
Configuration
</h3>
<div class="space-y-4">
<div>
<label class="text-xs text-code-comment mb-2 block">Framework / Language</label>
<select id="framework-select" class="w-full bg-code-bg/50 border border-white/10 rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-blue-500">
<option value="html">HTML + Tailwind CSS</option>
<option value="react">React + TypeScript</option>
<option value="vue">Vue 3 + Composition API</option>
<option value="python">Python + Flask</option>
<option value="python-django">Python + Django</option>
<option value="node">Node.js + Express</option>
</select>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">Include Comments</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" checked class="sr-only peer">
<div class="w-11 h-6 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-500"></div>
</label>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">Responsive Design</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" checked class="sr-only peer">
<div class="w-11 h-6 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-500"></div>
</label>
</div>
</div>
</div>
<button onclick="generateCode()" id="generate-btn" class="w-full bg-gradient-to-r from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 text-white font-semibold py-4 rounded-xl transition-all transform hover:scale-[1.02] flex items-center justify-center gap-2">
<i data-lucide="sparkles" class="w-5 h-5"></i>
Generate with AnyCoder
</button>
</div>
<!-- Output Panel -->
<div class="glass-effect rounded-xl overflow-hidden flex flex-col h-[800px]">
<div class="flex items-center justify-between px-4 py-3 bg-code-sidebar border-b border-white/10">
<div class="flex items-center gap-2">
<span class="text-sm font-medium">Generated Code</span>
<span id="status-badge" class="hidden px-2 py-0.5 bg-green-500/20 text-green-400 text-xs rounded-full">Ready</span>
</div>
<div class="flex items-center gap-2">
<button onclick="copyCode()" class="p-2 hover:bg-white/10 rounded-lg transition-colors" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
<button onclick="downloadCode()" class="p-2 hover:bg-white/10 rounded-lg transition-colors" title="Download">
<i data-lucide="download" class="w-4 h-4"></i>
</button>
<button onclick="saveToTemplates()" class="p-2 hover:bg-white/10 rounded-lg transition-colors" title="Save to Templates">
<i data-lucide="save" class="w-4 h-4"></i>
</button>
</div>
</div>
<div class="flex-1 flex">
<!-- File Tabs -->
<div class="w-48 bg-code-sidebar border-r border-white/10 overflow-y-auto">
<div id="file-tree" class="p-2 space-y-1">
<div class="px-3 py-2 bg-blue-500/20 rounded-lg text-sm text-blue-300 cursor-pointer">index.html</div>
</div>
</div>
<!-- Code Editor -->
<div class="flex-1 bg-code-bg relative">
<pre id="code-output" class="w-full h-full p-4 text-sm font-mono overflow-auto code-editor"><code class="text-code-comment">// Your generated code will appear here...
// Describe your app and click "Generate with AnyCoder"</code></pre>
<!-- Loading Overlay -->
<div id="loading-overlay" class="absolute inset-0 bg-code-bg/90 flex items-center justify-center hidden">
<div class="text-center">
<div class="w-16 h-16 border-4 border-blue-500/30 border-t-blue-500 rounded-full animate-spin mx-auto mb-4"></div>
<p class="text-blue-400 animate-pulse">AnyCoder is generating your code...</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Upload Tab -->
<div id="upload-tab" class="tab-content hidden">
<div class="max-w-4xl mx-auto">
<div class="glass-effect rounded-xl p-8 text-center">
<div class="w-24 h-24 mx-auto mb-6 rounded-full bg-blue-500/20 flex items-center justify-center">
<i data-lucide="upload-cloud" class="w-12 h-12 text-blue-400"></i>
</div>
<h2 class="text-2xl font-bold mb-2">Upload Your Code</h2>
<p class="text-code-comment mb-6">Drag and drop your files here, or click to browse</p>
<div id="drop-zone" class="border-2 border-dashed border-white/20 rounded-xl p-12 hover:border-blue-500/50 hover:bg-blue-500/5 transition-all cursor-pointer">
<input type="file" id="file-input" multiple class="hidden" accept=".js,.html,.css,.py,.jsx,.tsx,.vue,.json">
<p class="text-sm text-code-comment">Supported: .js, .html, .css, .py, .jsx, .tsx, .vue, .json</p>
</div>
<div id="uploaded-files" class="mt-6 text-left hidden">
<h3 class="text-sm font-medium mb-3">Uploaded Files</h3>
<div id="file-list" class="space-y-2"></div>
</div>
</div>
</div>
</div>
<!-- Templates Tab -->
<div id="templates-tab" class="tab-content hidden">
<div class="mb-6">
<h2 class="text-2xl font-bold mb-2">Code Templates</h2>
<p class="text-code-comment">Save, organize, and reuse your favorite code snippets</p>
</div>
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-4" id="templates-grid">
<!-- Templates will be dynamically added here -->
</div>
<div id="empty-templates" class="text-center py-12">
<div class="w-16 h-16 mx-auto mb-4 rounded-full bg-white/5 flex items-center justify-center">
<i data-lucide="folder-open" class="w-8 h-8 text-code-comment"></i>
</div>
<p class="text-code-comment">No saved templates yet</p>
<p class="text-sm text-code-comment mt-1">Generate code and save it to templates</p>
</div>
</div>
</main>
<!-- Preview Modal -->
<div id="preview-modal" class="fixed inset-0 z-50 hidden">
<div class="absolute inset-0 bg-black/80 backdrop-blur-sm" onclick="closePreview()"></div>
<div class="absolute inset-4 md:inset-8 bg-code-bg rounded-xl overflow-hidden flex flex-col">
<div class="flex items-center justify-between px-4 py-3 bg-code-sidebar border-b border-white/10">
<div class="flex items-center gap-2">
<i data-lucide="eye" class="w-4 h-4 text-blue-400"></i>
<span class="text-sm font-medium">Live Preview</span>
</div>
<div class="flex items-center gap-2">
<button onclick="refreshPreview()" class="p-2 hover:bg-white/10 rounded-lg transition-colors">
<i data-lucide="refresh-cw" class="w-4 h-4"></i>
</button>
<button onclick="closePreview()" class="p-2 hover:bg-white/10 rounded-lg transition-colors">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
</div>
</div>
<iframe id="preview-frame" class="flex-1 w-full bg-white"></iframe>
</div>
</div>
<script>
// Initialize Lucide icons
lucide.createIcons();
// State management
let currentTab = 'generate';
let generatedCode = '';
let savedTemplates = JSON.parse(localStorage.getItem('codeHubTemplates')) || [];
let currentFiles = new Map();
// Tab switching
function switchTab(tab) {
currentTab = tab;
// Update nav buttons
document.querySelectorAll('.nav-btn').forEach(btn => {
btn.classList.remove('bg-blue-500/20', 'text-blue-300');
if (btn.dataset.tab === tab) {
btn.classList.add('bg-blue-500/20', 'text-blue-300');
}
});
// Update tab content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.add('hidden');
});
document.getElementById(`${tab}-tab`).classList.remove('hidden');
// Refresh icons
lucide.createIcons();
}
// Presets
function setPreset(type) {
const presets = {
landing: 'Create a modern landing page with hero section, features grid, testimonials, and call-to-action. Include smooth scroll animations and responsive design.',
dashboard: 'Build an analytics dashboard with sidebar navigation, stats cards, line charts, recent activity feed, and data tables with sorting and filtering.',
api: 'Create a REST API with CRUD operations for a blog system. Include user authentication, post management, comments, and proper error handling.',
game: 'Build a browser-based snake game with score tracking, high scores, pause functionality, and mobile touch controls.'
};
document.getElementById('prompt-input').value = presets[type];
}
// Code generation simulation
function generateCode() {
const prompt = document.getElementById('prompt-input').value;
const framework = document.getElementById('framework-select').value;
if (!prompt.trim()) {
alert('Please describe what you want to build');
return;
}
// Show loading
document.getElementById('loading-overlay').classList.remove('hidden');
// Simulate generation delay
setTimeout(() => {
generatedCode = generateSampleCode(framework, prompt);
document.getElementById('code-output').innerHTML = highlightSyntax(generatedCode);
document.getElementById('loading-overlay').classList.add('hidden');
document.getElementById('status-badge').classList.remove('hidden');
// Update file tree
updateFileTree(framework);
}, 2000);
}
function generateSampleCode(framework, prompt) {
const codes = {
html: `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Generated App</title>
<script src="https://cdn.tailwindcss.com"><\/script>
</head>
<body class="bg-gray-900 text-white min-h-screen">
<!-- Generated based on: ${prompt.substring(0, 50)}... -->
<header class="p-6 border-b border-gray-800">
<h1 class="text-2xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
Welcome to Your App
</h1>
</header>
<main class="p-6">
<div class="grid md:grid-cols-3 gap-4">
<div class="p-4 bg-gray-800 rounded-lg">
<h3 class="font-semibold mb-2">Feature 1</h3>
<p class="text-sm text-gray-400">Auto-generated component</p>
</div>
<div class="p-4 bg-gray-800 rounded-lg">
<h3 class="font-semibold mb-2">Feature 2</h3>
<p class="text-sm text-gray-400">Auto-generated component</p>
</div>
<div class="p-4 bg-gray-800 rounded-lg">
<h3 class="font-semibold mb-2">Feature 3</h3>
<p class="text-sm text-gray-400">Auto-generated component</p>
</div>
</div>
</main>
<script>
// Generated by AnyCoder
console.log('App initialized');
<\/script>
</body>
</html>`,
react: `import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
// Generated by AnyCoder based on: ${prompt.substring(0, 50)}...
export default function App() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Simulate data fetching
const fetchData = async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
setData([
{ id: 1, title: 'Feature 1', desc: 'Auto-generated component' },
{ id: 2, title: 'Feature 2', desc: 'Auto-generated component' },
{ id: 3, title: 'Feature 3', desc: 'Auto-generated component' },
]);
setLoading(false);
};
fetchData();
}, []);
if (loading) {
return (
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-blue-500" />
</div>
);
}
return (
<div className="min-h-screen bg-gray-900 text-white p-6">
<header className="mb-8">
<h1 className="text-3xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
Welcome to Your App
</h1>
</header>
<div className="grid md:grid-cols-3 gap-4">
{data.map(item => (
<Card key={item.id} className="bg-gray-800 border-gray-700">
<CardHeader>
<CardTitle className="text-white">{item.title}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-gray-400">{item.desc}</p>
</CardContent>
</Card>
))}
</div>
</div>
);
}`,
python: `from flask import Flask, jsonify, request
from flask_cors import CORS
import os
# Generated by AnyCoder based on: ${prompt.substring(0, 50)}...
app = Flask(__name__)
CORS(app)
# In-memory storage (replace with database in production)
data_store = {
'items': [
{'id': 1, 'name': 'Feature 1', 'description': 'Auto-generated component'},
{'id': 2, 'name': 'Feature 2', 'description': 'Auto-generated component'},
{'id': 3, 'name': 'Feature 3', 'description': 'Auto-generated component'},
]
}
@app.route('/')
def index():
return jsonify({
'message': 'Welcome to Your API',
'version': '1.0.0',
'endpoints': [
'/api/items',
'/api/items/<id>',
]
})
@app.route('/api/items', methods=['GET'])
def get_items():
return jsonify({'items': data_store['items']})
@app.route('/api/items/<int:item_id>', methods=['GET'])
def get_item(item_id):
item = next((i for i in data_store['items'] if i['id'] == item_id), None)
if item:
return jsonify(item)
return jsonify({'error': 'Item not found'}), 404
@app.route('/api/items', methods=['POST'])
def create_item():
data = request.get_json()
new_item = {
'id': len(data_store['items']) + 1,
'name': data.get('name', ''),
'description': data.get('description', '')
}
data_store['items'].append(new_item)
return jsonify(new_item), 201
@app.route('/api/items/<int:item_id>', methods=['PUT'])
def update_item(item_id):
item = next((i for i in data_store['items'] if i['id'] == item_id), None)
if not item:
return jsonify({'error': 'Item not found'}), 404
data = request.get_json()
item['name'] = data.get('name', item['name'])
item['description'] = data.get('description', item['description'])
return jsonify(item)
@app.route('/api/items/<int:item_id>', methods=['DELETE'])
def delete_item(item_id):
global data_store
data_store['items'] = [i for i in data_store['items'] if i['id'] != item_id]
return jsonify({'message': 'Item deleted'})
if __name__ == '__main__':
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port, debug=True)`
};
return codes[framework] || codes.html;
}
function highlightSyntax(code) {
// Simple syntax highlighting
let highlighted = code
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\/\/.*/g, '<span class="syntax-comment">$&</span>')
.replace(/\/\*[\s\S]*?\*\//g, '<span class="syntax-comment">$&</span>')
.replace(/&lt;!--[\s\S]*?--&gt;/g, '<span class="syntax-comment">$&</span>')
.replace(/\b(const|let|var|function|class|import|export|from|return|if|else|for|while|async|await|try|catch)\b/g, '<span class="syntax-keyword">$1</span>')
.replace(/\b(console|document|window|React|useState|useEffect)\b/g, '<span class="syntax-function">$1</span>')
.replace(/(['"`])(.*?)\1/g, '<span class="syntax-string">$1$2$1</span>');
return highlighted;
}
function updateFileTree(framework) {
const fileTrees = {
html: ['index.html', 'styles.css', 'script.js'],
react: ['App.tsx', 'components/', 'hooks/', 'utils/', 'types.ts'],
vue: ['App.vue', 'components/', 'composables/', 'stores/'],
python: ['app.py', 'requirements.txt', 'config.py', 'models/', 'routes/'],
'python-django': ['manage.py', 'settings.py', 'urls.py', 'models.py', 'views.py'],
node: ['server.js', 'package.json', 'routes/', 'middleware/', 'models/']
};
const files = fileTrees[framework] || fileTrees.html;
const treeHtml = files.map((file, i) => {
const isFirst = i === 0;
const icon = file.includes('.') ? 'file-code' : 'folder';
return `<div class="px-3 py-2 ${isFirst ? 'bg-blue-500/20 text-blue-300' : 'hover:bg-white/5'} rounded-lg text-sm cursor-pointer flex items-center gap-2 transition-colors">
<i data-lucide="${icon}" class="w-4 h-4"></i>
${file}
</div>`;
}).join('');
document.getElementById('file-tree').innerHTML = treeHtml;
lucide.createIcons();
}
function copyCode() {
const code = document.getElementById('code-output').textContent;
navigator.clipboard.writeText(code).then(() => {
showToast('Code copied to clipboard!');
});
}
function downloadCode() {
const code = document.getElementById('code-output').textContent;
const framework = document.getElementById('framework-select').value;
const extensions = {
html: 'html', react: 'tsx', vue: 'vue', python: 'py', 'python-django': 'py', node: 'js'
};
const ext = extensions[framework] || 'txt';
const blob = new Blob([code], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `generated-code.${ext}`;
a.click();
URL.revokeObjectURL(url);
showToast('Code downloaded!');
}
function saveToTemplates() {
const code = document.getElementById('code-output').textContent;
if (!code || code.includes('Your generated code will appear here')) {
alert('Please generate code first');
return;
}
const name = prompt('Enter a name for this template:', 'My Template');
if (!name) return;
const template = {
id: Date.now(),
name,
code,
framework: document.getElementById('framework-select').value,
timestamp: new Date().toISOString(),
preview: code.substring(0, 100) + '...'
};
savedTemplates.push(template);
localStorage.setItem('codeHubTemplates', JSON.stringify(savedTemplates));
showToast('Template saved!');
renderTemplates();
}
function renderTemplates() {
const grid = document.getElementById('templates-grid');
const empty = document.getElementById('empty-templates');
if (savedTemplates.length === 0) {
grid.innerHTML = '';
empty.classList.remove('hidden');
return;
}
empty.classList.add('hidden');
grid.innerHTML = savedTemplates.map(t => {
const frameworkIcons = {
html: '🌐', react: '⚛️', vue: '💚', python: '🐍', 'python-django': '🎸', node: '💚'
};
const date = new Date(t.timestamp).toLocaleDateString();
return `<div class="glass-effect rounded-xl p-4 hover:bg-white/5 transition-all group">
<div class="flex items-start justify-between mb-3">
<div class="flex items-center gap-2">
<span class="text-2xl">${frameworkIcons[t.framework] || '📄'}</span>
<div>
<h3 class="font-medium text-sm">${t.name}</h3>
<p class="text-xs text-code-comment">${date}</p>
</div>
</div>
<div class="opacity-0 group-hover:opacity-100 transition-opacity flex gap-1">
<button onclick="loadTemplate(${t.id})" class="p-1.5 hover:bg-blue-500/20 rounded transition-colors" title="Load">
<i data-lucide="play" class="w-4 h-4 text-blue-400"></i>
</button>
<button onclick="deleteTemplate(${t.id})" class="p-1.5 hover:bg-red-500/20 rounded transition-colors" title="Delete">
<i data-lucide="trash-2" class="w-4 h-4 text-red-400"></i>
</button>
</div>
</div>
<div class="bg-code-bg/50 rounded-lg p-3 font-mono text-xs text-code-comment overflow-hidden">
${t.preview.replace(/</g, '&lt;')}
</div>
</div>`;
}).join('');
lucide.createIcons();
}
function loadTemplate(id) {
const template = savedTemplates.find(t => t.id === id);
if (!template) return;
generatedCode = template.code;
document.getElementById('code-output').innerHTML = highlightSyntax(generatedCode);
document.getElementById('framework-select').value = template.framework;
document.getElementById('status-badge').classList.remove('hidden');
updateFileTree(template.framework);
switchTab('generate');
showToast('Template loaded!');
}
function deleteTemplate(id) {
if (!confirm('Delete this template?')) return;
savedTemplates = savedTemplates.filter(t => t.id !== id);
localStorage.setItem('codeHubTemplates', JSON.stringify(savedTemplates));
renderTemplates();
showToast('Template deleted');
}
// File upload handling
const dropZone = document.getElementById('drop-zone');
const fileInput = document.getElementById('file-input');
dropZone.addEventListener('click', () => fileInput.click());
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('border-blue-500', 'bg-blue-500/10');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('border-blue-500', 'bg-blue-500/10');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('border-blue-500', 'bg-blue-500/10');
handleFiles(e.dataTransfer.files);
});
fileInput.addEventListener('change', (e) => handleFiles(e.target.files));
function handleFiles(files) {
const fileList = document.getElementById('file-list');
const uploadedFiles = document.getElementById('uploaded-files');
uploadedFiles.classList.remove('hidden');
Array.from(files).forEach(file => {
const reader = new FileReader();
reader.onload = (e) => {
currentFiles.set(file.name, e.target.result);
const div = document.createElement('div');
div.className = 'flex items-center justify-between p-3 bg-white/5 rounded-lg';
div.innerHTML = `
<div class="flex items-center gap-3">
<i data-lucide="file-code" class="w-5 h-5 text-blue-400"></i>
<div>
<p class="text-sm font-medium">${file.name}</p>
<p class="text-xs text-code-comment">${(file.size / 1024).toFixed(1)} KB</p>
</div>
</div>
<button onclick="loadUploadedFile('${file.name}')" class="p-2 hover:bg-blue-500/20 rounded-lg transition-colors">
<i data-lucide="play" class="w-4 h-4 text-blue-400"></i>
</button>
`;
fileList.appendChild(div);
lucide.createIcons();
};
reader.readAsText(file);
});
}
function loadUploadedFile(filename) {
const content = currentFiles.get(filename);
if (!content) return;
generatedCode = content;
document.getElementById('code-output').textContent = content;
document.getElementById('status-badge').classList.remove('hidden');
switchTab('generate');
showToast(`Loaded ${filename}`);
}
// Preview functionality
function openPreview() {
if (!generatedCode) {
alert('Please generate code first');
return;
}
document.getElementById('preview-modal').classList.remove('hidden');
refreshPreview();
}
function closePreview() {
document.getElementById('preview-modal').classList.add('hidden');
}
function refreshPreview() {
const frame = document.getElementById('preview-frame');
const doc = frame.contentDocument || frame.contentWindow.document;
doc.open();
doc.write(generatedCode);
doc.close();
}
// Toast notifications
function showToast(message) {
const toast = document.createElement('div');
toast.className = 'fixed bottom-4 right-4 bg-blue-500 text-white px-6 py-3 rounded-lg shadow-lg transform translate-y-0 transition-all z-50';
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateY(20px)';
setTimeout(() => toast.remove(), 300);
}, 3000);
}
// Theme toggle
function toggleTheme() {
document.body.classList.toggle('light-theme');
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
renderTemplates();
// Add preview button to code panel
const codePanel = document.querySelector('#generate-tab .glass-effect:last-child');
const previewBtn = document.createElement('button');
previewBtn.className = 'p-2 hover:bg-white/10 rounded-lg transition-colors';
previewBtn.innerHTML = '<i data-lucide="eye" class="w-4 h-4"></i>';
previewBtn.title = 'Preview';
previewBtn.onclick = openPreview;
codePanel.querySelector('.flex.items-center.gap-2').parentElement.appendChild(previewBtn);
lucide.createIcons();
});
</script>
</body>
</html>