Spaces:
Running
Running
| let editors = {}; | |
| let currentTab = 0; | |
| let tabs = []; | |
| let modules = {}; | |
| let currentTheme = 'dark'; | |
| // Initialize modules | |
| const moduleData = { | |
| 'auth': { | |
| name: 'Authentication Module', | |
| description: 'Handles user authentication, sessions, and security', | |
| version: '2.1.4', | |
| language: 'php', | |
| content: `<?php | |
| namespace App\\Modules\\Auth; | |
| class AuthController { | |
| private $userService; | |
| public function __construct() { | |
| $this->userService = new UserService(); | |
| } | |
| public function login($credentials) { | |
| if ($this->validateCredentials($credentials)) { | |
| $user = $this->userService->authenticate($credentials); | |
| if ($user) { | |
| return $this->createSession($user); | |
| } | |
| } | |
| throw new AuthenticationException("Invalid credentials"); | |
| } | |
| private function validateCredentials($credentials) { | |
| return isset($credentials['email']) && isset($credentials['password']); | |
| } | |
| private function createSession($user) { | |
| $token = JWT::encode([ | |
| 'user_id' => $user->id, | |
| 'exp' => time() + 3600 | |
| ], $_ENV['JWT_SECRET']); | |
| return [ | |
| 'token' => $token, | |
| 'user' => $user->toArray() | |
| ]; | |
| } | |
| }` | |
| }, | |
| 'api': { | |
| name: 'API Module', | |
| description: 'RESTful API endpoints and middleware', | |
| version: '1.5.2', | |
| language: 'javascript', | |
| content: `const express = require('express'); | |
| const router = express.Router(); | |
| const { authenticate, authorize } = require('../middleware/auth'); | |
| // Apply authentication middleware to all routes | |
| router.use(authenticate); | |
| // User endpoints | |
| router.get('/users', authorize('admin'), async (req, res) => { | |
| try { | |
| const users = await User.find().select('-password'); | |
| res.json({ | |
| success: true, | |
| data: users, | |
| count: users.length | |
| }); | |
| } catch (error) { | |
| res.status(500).json({ | |
| success: false, | |
| message: error.message | |
| }); | |
| } | |
| }); | |
| router.post('/users', authorize('admin'), async (req, res) => { | |
| try { | |
| const user = new User(req.body); | |
| await user.save(); | |
| res.status(201).json({ | |
| success: true, | |
| data: user | |
| }); | |
| } catch (error) { | |
| res.status(400).json({ | |
| success: false, | |
| message: error.message | |
| }); | |
| } | |
| }); | |
| module.exports = router;` | |
| }, | |
| 'frontend': { | |
| name: 'Frontend Module', | |
| description: 'React components and UI elements', | |
| version: '3.0.1', | |
| language: 'htmlmixed', | |
| content: `<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Dashboard</title> | |
| <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> | |
| <style> | |
| .card-hover { | |
| transition: transform 0.3s ease, box-shadow 0.3s ease; | |
| } | |
| .card-hover:hover { | |
| transform: translateY(-4px); | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50"> | |
| <nav class="bg-white shadow-lg sticky top-0 z-50"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="flex justify-between h-16"> | |
| <div class="flex items-center"> | |
| <h1 class="text-xl font-bold text-gray-800">Dashboard</h1> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <button class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"> | |
| New Project | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </nav> | |
| <main class="max-w-7xl mx-auto px-4 py-8"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| <div class="card-hover bg-white rounded-lg shadow p-6"> | |
| <h3 class="text-lg font-semibold mb-2">Analytics</h3> | |
| <p class="text-gray-600">View detailed analytics and reports</p> | |
| </div> | |
| <div class="card-hover bg-white rounded-lg shadow p-6"> | |
| <h3 class="text-lg font-semibold mb-2">Users</h3> | |
| <p class="text-gray-600">Manage user accounts and permissions</p> | |
| </div> | |
| <div class="card-hover bg-white rounded-lg shadow p-6"> | |
| <h3 class="text-lg font-semibold mb-2">Settings</h3> | |
| <p class="text-gray-600">Configure application settings</p> | |
| </div> | |
| </div> | |
| </main> | |
| </body> | |
| </html>` | |
| } | |
| }; | |
| // Version history storage | |
| const versionHistory = { | |
| 'auth': [ | |
| { version: '2.1.4', date: '2024-01-15', changes: 'Fixed session timeout issue' }, | |
| { version: '2.1.3', date: '2024-01-10', changes: 'Improved password hashing' }, | |
| { version: '2.1.2', date: '2024-01-05', changes: 'Added multi-factor authentication' } | |
| ], | |
| 'api': [ | |
| { version: '1.5.2', date: '2024-01-14', changes: 'Optimized database queries' }, | |
| { version: '1.5.1', date: '2024-01-08', changes: 'Fixed CORS issues' }, | |
| { version: '1.5.0', date: '2024-01-01', changes: 'Added rate limiting' } | |
| ], | |
| 'frontend': [ | |
| { version: '3.0.1', date: '2024-01-16', changes: 'Fixed responsive design bugs' }, | |
| { version: '3.0.0', date: '2024-01-01', changes: 'Complete UI redesign with Tailwind' } | |
| ] | |
| }; | |
| // Initialize application | |
| document.addEventListener('DOMContentLoaded', function() { | |
| initializeModules(); | |
| createDefaultTabs(); | |
| renderModuleList(); | |
| // Set initial theme | |
| document.documentElement.setAttribute('data-theme', currentTheme); | |
| }); | |
| function initializeModules() { | |
| for (const [key, module] of Object.entries(moduleData)) { | |
| modules[key] = { ...module, id: key }; | |
| } | |
| } | |
| function createDefaultTabs() { | |
| addTab('auth', 'auth.php'); | |
| addTab('api', 'api.js'); | |
| addTab('frontend', 'index.html'); | |
| } | |
| function addTab(moduleId, filename) { | |
| const tabId = `tab-${Date.now()}`; | |
| const module = modules[moduleId]; | |
| if (!module) return; | |
| tabs.push({ | |
| id: tabId, | |
| moduleId: moduleId, | |
| filename: filename, | |
| module: module | |
| }); | |
| // Create tab UI | |
| const tabContainer = document.getElementById('tabContainer'); | |
| const tabElement = document.createElement('div'); | |
| tabElement.className = `tab-item flex items-center gap-2 px-4 py-3 bg-gray-700 hover:bg-gray-600 cursor-pointer transition-colors border-r border-gray-600 ${tabs.length === 1 ? 'tab-active' : ''}`; | |
| tabElement.id = tabId; | |
| tabElement.onclick = () => switchTab(tabs.length - 1); | |
| const extension = filename.split('.').pop().toLowerCase(); | |
| const iconMap = { | |
| 'php': '<i data-feather="code" class="w-4 h-4 text-purple-400"></i>', | |
| 'js': '<i data-feather="git-branch" class="w-4 h-4 text-yellow-400"></i>', | |
| 'html': '<i data-feather="globe" class="w-4 h-4 text-orange-400"></i>', | |
| 'css': '<i data-feather="droplet" class="w-4 h-4 text-blue-400"></i>' | |
| }; | |
| tabElement.innerHTML = ` | |
| ${iconMap[extension] || '<i data-feather="file" class="w-4 h-4"></i>'} | |
| <span class="text-sm">${filename}</span> | |
| <button onclick="event.stopPropagation(); closeTab('${tabId}')" class="ml-2 hover:bg-gray-800 rounded p-1"> | |
| <i data-feather="x" class="w-3 h-3"></i> | |
| </button> | |
| `; | |
| tabContainer.appendChild(tabElement); | |
| // Create editor | |
| createEditor(tabId, module.content, module.language); | |
| // Switch to new tab | |
| switchTab(tabs.length - 1); | |
| feather.replace(); | |
| } | |
| function createEditor(tabId, content, mode) { | |
| const container = document.getElementById('editorContainer'); | |
| const editorDiv = document.createElement('div'); | |
| editorDiv.id = `editor-${tabId}`; | |
| editorDiv.className = 'h-full hidden'; | |
| editorDiv.innerHTML = `<textarea id="textarea-${tabId}"></textarea>`; | |
| container.appendChild(editorDiv); | |
| const editor = CodeMirror.fromTextArea(document.getElementById(`textarea-${tabId}`), { | |
| value: content, | |
| mode: mode, | |
| theme: currentTheme === 'dark' ? 'monokai' : 'default', | |
| lineNumbers: true, | |
| lineWrapping: true, | |
| autoCloseBrackets: true, | |
| matchBrackets: true, | |
| indentUnit: 4, | |
| tabSize: 4, | |
| indentWithTabs: false | |
| }); | |
| editor.setValue(content); | |
| // Update cursor position | |
| editor.on('cursorActivity', function() { | |
| const cursor = editor.getCursor(); | |
| document.getElementById('cursorPosition').textContent = `Ln ${cursor.line + 1}, Col ${cursor.ch + 1}`; | |
| }); | |
| // Update file info | |
| editor.on('change', function() { | |
| const tab = tabs.find(t => t.id === tabId); | |
| if (tab) { | |
| document.getElementById('fileInfo').textContent = `${tab.filename} • Modified`; | |
| } | |
| }); | |
| editors[tabId] = editor; | |
| } | |
| function switchTab(index) { | |
| // Hide all editors | |
| tabs.forEach(tab => { | |
| const editorDiv = document.getElementById(`editor-${tab.id}`); | |
| if (editorDiv) editorDiv.classList.add('hidden'); | |
| const tabElement = document.getElementById(tab.id); | |
| if (tabElement) tabElement.classList.remove('tab-active'); | |
| }); | |
| // Show selected editor | |
| const selectedTab = tabs[index]; | |
| const editorDiv = document.getElementById(`editor-${selectedTab.id}`); | |
| if (editorDiv) { | |
| editorDiv.classList.remove('hidden'); | |
| // Refresh editor to fix display issues | |
| setTimeout(() => { | |
| editors[selectedTab.id].refresh(); | |
| }, 10); | |
| } | |
| const tabElement = document.getElementById(selectedTab.id); | |
| if (tabElement) tabElement.classList.add('tab-active'); | |
| currentTab = index; | |
| // Update status bar | |
| document.getElementById('fileInfo').textContent = selectedTab.filename; | |
| document.getElementById('language').textContent = selectedTab.module.language.toUpperCase(); | |
| document.getElementById('gitBranch').textContent = selectedTab.moduleId; | |
| } | |
| function closeTab(tabId) { | |
| const index = tabs.findIndex(t => t.id === tabId); | |
| if (index === -1) return; | |
| // Remove tab | |
| tabs.splice(index, 1); | |
| // Remove UI elements | |
| const tabElement = document.getElementById(tabId); | |
| if (tabElement) tabElement.remove(); | |
| const editorDiv = document.getElementById(`editor-${tabId}`); | |
| if (editorDiv) editorDiv.remove(); | |
| delete editors[tabId]; | |
| // Switch to another tab if available | |
| if (tabs.length > 0) { | |
| switchTab(Math.min(currentTab, tabs.length - 1)); | |
| } | |
| } | |
| function addNewTab() { | |
| // Create a simple file selection dialog | |
| const moduleKeys = Object.keys(modules); | |
| const moduleId = moduleKeys[Math.floor(Math.random() * moduleKeys.length)]; | |
| const module = modules[moduleId]; | |
| const extension = module.language === 'htmlmixed' ? 'html' : module.language; | |
| const filename = `new-${Date.now()}.${extension}`; | |
| addTab(moduleId, filename); | |
| } | |
| function renderModuleList() { | |
| const moduleList = document.getElementById('moduleList'); | |
| moduleList.innerHTML = ''; | |
| Object.values(modules).forEach(module => { | |
| const moduleElement = document.createElement('div'); | |
| moduleElement.className = 'module-item bg-gray-700 rounded-lg p-4 cursor-pointer'; | |
| moduleElement.innerHTML = ` | |
| <div class="flex items-start justify-between mb-2"> | |
| <div class="flex-1"> | |
| <h3 class="font-semibold text-white flex items-center gap-2"> | |
| ${getModuleIcon(module.language)} | |
| ${module.name} | |
| </h3> | |
| <p class="text-xs text-gray-400 mt-1">${module.description}</p> | |
| <div class="flex items-center gap-2 mt-2"> | |
| <span class="text-xs bg-blue-600 text-white px-2 py-1 rounded">v${module.version}</span> | |
| <span class="text-xs text-gray-400">${module.language}</span> | |
| </div> | |
| </div> | |
| <div class="flex flex-col gap-1"> | |
| <button onclick="showVersionHistory('${module.id}')" class="action-button p-2 hover:bg-gray-600 rounded" title="Version History"> | |
| <i data-feather="git-branch" class="w-3 h-3"></i> | |
| </button> | |
| <button onclick="showDiffView('${module.id}')" class="action-button p-2 hover:bg-gray-600 rounded" title="Show Diff"> | |
| <i data-feather="git-merge" class="w-3 h-3"></i> | |
| </button> | |
| <button onclick="changeModuleVersion('${module.id}')" class="action-button p-2 hover:bg-gray-600 rounded" title="Change Version"> | |
| <i data-feather="refresh-cw" class="w-3 h-3"></i> | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| moduleElement.onclick = (e) => { | |
| if (e.target.closest('button')) return; | |
| openModuleTab(module.id); | |
| }; | |
| moduleList.appendChild(moduleElement); | |
| }); | |
| feather.replace(); | |
| } | |
| function getModuleIcon(language) { | |
| const icons = { | |
| 'php': '<i data-feather="code" class="w-4 h-4 text-purple-400"></i>', | |
| 'javascript': '<i data-feather="git-branch" class="w-4 h-4 text-yellow-400"></i>', | |
| 'htmlmixed': '<i data-feather="globe" class="w-4 h-4 text-orange-400"></i>' | |
| }; | |
| return icons[language] || '<i data-feather="file" class="w-4 h-4"></i>'; | |
| } | |
| function openModuleTab(moduleId) { | |
| const module = modules[moduleId]; | |
| if (!module) return; | |
| // Check if tab already exists | |
| const existingTab = tabs.find(t => t.moduleId === moduleId); | |
| if (existingTab) { | |
| const index = tabs.findIndex(t => t.id === existingTab.id); | |
| switchTab(index); | |
| return; | |
| } | |
| const extension = module.language === 'htmlmixed' ? 'html' : module.language; | |
| const filename = `${moduleId}.${extension}`; | |
| addTab(moduleId, filename); | |
| } | |
| function showVersionHistory(moduleId) { | |
| const modal = document.getElementById('versionModal'); | |
| const versionList = document.getElementById('versionList'); | |
| const history = versionHistory[moduleId] || []; | |
| versionList.innerHTML = history.map(v => ` | |
| <div class="version-item bg-gray-700 rounded p-3 text-sm"> | |
| <div class="flex justify-between items-start"> | |
| <div> | |
| <span class="font-semibold text-white">v${v.version}</span> | |
| <span class="text-xs text-gray-400 ml-2">${v.date}</span> | |
| </div> | |
| </div> | |
| <p class="text-xs text-gray-300 mt-1">${v.changes}</p> | |
| </div> | |
| `).join(''); | |
| modal.classList.remove('hidden'); | |
| modal.dataset.moduleId = moduleId; | |
| } | |
| function closeVersionModal() { | |
| document.getElementById('versionModal').classList.add('hidden'); | |
| } | |
| function createPatch() { | |
| const modal = document.getElementById('versionModal'); | |
| const moduleId = modal.dataset.moduleId; | |
| if (moduleId) { | |
| alert(`Creating patch for ${modules[moduleId]?.name || 'Module'}...`); | |
| } | |
| closeVersionModal(); | |
| } | |
| function showDiffView(moduleId) { | |
| const module = modules[moduleId]; | |
| if (!module) return; | |
| // Create a diff view tab | |
| const tabId = `diff-${Date.now()}`; | |
| const tabContainer = document.getElementById('tabContainer'); | |
| const tabElement = document.createElement('div'); | |
| tabElement.className = 'tab-item flex items-center gap-2 px-4 py-3 bg-gray-700 hover:bg-gray-600 cursor-pointer transition-colors border-r border-gray-600'; | |
| tabElement.id = tabId; | |
| tabElement.innerHTML = ` | |
| <i data-feather="git-merge" class="w-4 h-4 text-green-400"></i> | |
| <span class="text-sm">${moduleId} - Diff</span> | |
| <button onclick="event.stopPropagation(); closeTab('${tabId}')" class="ml-2 hover:bg-gray-800 rounded p-1"> | |
| <i data-feather="x" class="w-3 h-3"></i> | |
| </button> | |
| `; | |
| tabContainer.appendChild(tabElement); | |
| tabs.push({ | |
| id: tabId, | |
| moduleId: moduleId, | |
| filename: `${moduleId} - Diff`, | |
| module: module, | |
| isDiff: true | |
| }); | |
| // Create diff view | |
| const container = document.getElementById('editorContainer'); | |
| const editorDiv = document.createElement('div'); | |
| editorDiv.id = `editor-${tabId}`; | |
| editorDiv.className = 'h-full hidden'; | |
| editorDiv.innerHTML = ` | |
| <div class="flex h-full"> | |
| <div class="w-1/2 h-full"> | |
| <div class="bg-gray-700 px-4 py-2 text-sm font-semibold">Current Version</div> | |
| <textarea id="diff-left-${tabId}"></textarea> | |
| </div> | |
| <div class="w-1/2 h-full border-l border-gray-600"> | |
| <div class="bg-gray-700 px-4 py-2 text-sm font-semibold">Previous Version</div> | |
| <textarea id="diff-right-${tabId}"></textarea> | |
| </div> | |
| </div> | |
| `; | |
| container.appendChild(editorDiv); | |
| // Initialize diff editors | |
| const leftEditor = CodeMirror.fromTextArea(document.getElementById(`diff-left-${tabId}`), { | |
| mode: module.language, | |
| theme: currentTheme === 'dark' ? 'monokai' : 'default', | |
| lineNumbers: true, | |
| readOnly: true | |
| }); | |
| const rightEditor = CodeMirror.fromTextArea(document.getElementById(`diff-right-${tabId}`), { | |
| mode: module.language, | |
| theme: currentTheme === 'dark' ? 'monokai' : 'default', | |
| lineNumbers: true, | |
| readOnly: true | |
| }); | |
| leftEditor.setValue(module.content); | |
| rightEditor.setValue(module.content.substring(0, Math.floor(module.content.length * 0.7))); | |
| editors[tabId] = { left: leftEditor, right: rightEditor, isDiff: true }; | |
| switchTab(tabs.length - 1); | |
| feather.replace(); | |
| } | |
| function changeModuleVersion(moduleId) { | |
| const module = modules[moduleId]; | |
| if (!module) return; | |
| const versions = versionHistory[moduleId] || []; | |
| if (versions.length === 0) { | |
| alert('No previous versions available'); | |
| return; | |
| } | |
| const newVersion = versions[0]; | |
| module.version = newVersion.version; | |
| renderModuleList(); | |
| alert(`Module updated to version ${newVersion.version}`); | |
| } | |
| function toggleTheme() { | |
| currentTheme = currentTheme === 'dark' ? 'light' : 'dark'; | |
| document.documentElement.setAttribute('data-theme', currentTheme); | |
| const themeIcon = document.querySelector('.theme-icon'); | |
| themeIcon.setAttribute('data-feather', currentTheme === 'dark' ? 'sun' : 'moon'); | |
| feather.replace(); | |
| // Update all editor themes | |
| Object.values(editors).forEach(editor => { | |
| if (editor.isDiff) { | |
| if (editor.left) editor.left.setOption('theme', currentTheme === 'dark' ? 'monokai' : 'default'); | |
| if (editor.right) editor.right.setOption('theme', currentTheme === 'dark' ? 'monokai' : 'default'); | |
| } else { | |
| editor.setOption('theme', currentTheme === 'dark' ? 'monokai' : 'default'); | |
| } | |
| }); | |
| } |