jercox's picture
fullscreen code editor interface, it must to habe 1 Sigil Tab (php sintax), 2 JS, 3 HTML/CSS, with a lateral vertial column with the name of the module, description, actual version, and the option to change of version or make diff or patch, fully functional
0ad11e8 verified
Raw
History Blame Contribute Delete
19.5 kB
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');
}
});
}