Spaces:
Running
Running
| <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, '&') | |
| .replace(/</g, '<') | |
| .replace(/>/g, '>') | |
| .replace(/\/\/.*/g, '<span class="syntax-comment">$&</span>') | |
| .replace(/\/\*[\s\S]*?\*\//g, '<span class="syntax-comment">$&</span>') | |
| .replace(/<!--[\s\S]*?-->/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, '<')} | |
| </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> |