VS Code Web Editor
Manager
1
` }, 'style.css': { type: 'file', content: `body { font-family: sans-serif; background-color: #f0f0f0; color: #333; text-align: center; padding: 2rem; } h1 { color: #2c3e50; } #demo-btn { background-color: #3498db; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 16px; margin-top: 20px; } #demo-btn:hover { background-color: #2980b9; }` }, 'script.js': { type: 'file', content: `document.getElementById('demo-btn').addEventListener('click', function() { alert('Button clicked! This is working!'); });` }, 'src': { type: 'folder', children: { 'main.js': { type: 'file', content: `// Main application logic` }, 'components': { type: 'folder', children: { 'button.js': { type: 'file', content: `// A custom button component` } } } } } }; // DOM elements const sidebar = document.getElementById('sidebar'); const fileTree = document.getElementById('file-tree'); const tabBar = document.getElementById('tab-bar'); const editor = document.getElementById('editor'); const lineNumbers = document.getElementById('line-numbers'); const addTabBtn = document.getElementById('add-tab-btn'); const newFileBtn = document.getElementById('new-file-btn'); const newFolderBtn = document.getElementById('new-folder-btn'); const managerTab = document.getElementById('manager-tab'); const contextMenu = document.getElementById('context-menu'); const importFileInput = document.getElementById('import-file-input'); const runBtn = document.getElementById('run-btn'); // State variables let activeFile = null; let fileCounter = 1; // Function to toggle the sidebar's visibility on small screens function toggleSidebar() { // Only toggle on screens smaller than large breakpoint (lg) if (window.innerWidth < 1024) { sidebar.classList.toggle('-translate-x-full'); } } // Function to render the file tree recursively function renderFileTree(container, files, path = '') { container.innerHTML = ''; for (const name in files) { const item = files[name]; const fullPath = path + name; const element = document.createElement('div'); element.classList.add('pl-4', 'py-1', 'group', 'flex', 'items-center', 'justify-between', 'text-gray-300', 'rounded', 'transition-colors', 'duration-200'); // Content for the file/folder, including the icon and name const contentDiv = document.createElement('div'); contentDiv.classList.add('flex', 'items-center', 'space-x-2', 'flex-1', 'cursor-pointer'); if (item.type === 'folder') { // Folder icon and name contentDiv.innerHTML = `${name}`; contentDiv.onclick = (e) => { e.stopPropagation(); // Toggle folder visibility const childrenContainer = element.nextElementSibling; if (childrenContainer) { childrenContainer.classList.toggle('hidden'); contentDiv.querySelector('i').classList.toggle('fa-caret-right'); contentDiv.querySelector('i').classList.toggle('fa-caret-down'); } }; element.appendChild(contentDiv); // Context menu icon for the folder const menuIcon = document.createElement('i'); menuIcon.classList.add('fas', 'fa-ellipsis-v', 'text-gray-500', 'cursor-pointer', 'p-1', 'opacity-0', 'group-hover:opacity-100', 'transition-opacity'); menuIcon.onclick = (e) => showContextMenu(e, fullPath, 'folder'); element.appendChild(menuIcon); container.appendChild(element); const childrenContainer = document.createElement('div'); childrenContainer.classList.add('pl-4', 'hidden'); renderFileTree(childrenContainer, item.children, fullPath + '/'); container.appendChild(childrenContainer); } else { // File icon and name contentDiv.innerHTML = `${name}`; contentDiv.onclick = (e) => { e.stopPropagation(); openFile(fullPath, name); // Hide sidebar on mobile after clicking a file if (window.innerWidth < 1024) { toggleSidebar(); } }; element.appendChild(contentDiv); // Context menu icon for the file const menuIcon = document.createElement('i'); menuIcon.classList.add('fas', 'fa-ellipsis-v', 'text-gray-500', 'cursor-pointer', 'p-1', 'opacity-0', 'group-hover:opacity-100', 'transition-opacity'); menuIcon.onclick = (e) => showContextMenu(e, fullPath, 'file'); element.appendChild(menuIcon); container.appendChild(element); } } } // Function to show the context menu function showContextMenu(event, path, type) { event.stopPropagation(); hideContextMenu(); // Hide any existing menu // Set the menu position contextMenu.style.left = `${event.clientX}px`; contextMenu.style.top = `${event.clientY}px`; // Determine menu items based on type const menuItems = []; if (type === 'file') { menuItems.push( { name: 'Rename', icon: 'fas fa-pen-to-square', action: () => renameItem(path) }, { name: 'Copy', icon: 'fas fa-copy', action: () => copyFile(path) }, { name: 'Delete', icon: 'fas fa-trash-alt', action: () => deleteItem(path) } ); } else if (type === 'folder') { menuItems.push( { name: 'Rename', icon: 'fas fa-pen-to-square', action: () => renameItem(path) }, { name: 'New File', icon: 'fas fa-file-alt', action: () => createNewItemInFolder(path, 'file') }, { name: 'New Folder', icon: 'fas fa-folder-plus', action: () => createNewItemInFolder(path, 'folder') }, { name: 'Import', icon: 'fas fa-upload', action: () => importFile(path) }, { name: 'Delete', icon: 'fas fa-trash-alt', action: () => deleteItem(path) } ); } // Populate the menu contextMenu.innerHTML = ''; menuItems.forEach(item => { const menuItem = document.createElement('div'); menuItem.classList.add('context-menu-item'); menuItem.innerHTML = `${item.name}`; menuItem.onclick = (e) => { e.stopPropagation(); item.action(); hideContextMenu(); }; contextMenu.appendChild(menuItem); }); contextMenu.classList.remove('hidden'); } // Function to hide the context menu function hideContextMenu() { contextMenu.classList.add('hidden'); } // Function to open a file and update the editor function openFile(path, name) { // Find the file content in the file system object const pathParts = path.split('/'); let current = fileSystem; for (const part of pathParts) { if (current[part] && current[part].type === 'folder') { current = current[part].children; } else if (current[part] && current[part].type === 'file') { current = current[part]; } } const content = current.content; if (content !== undefined) { editor.value = content; activeFile = path; updateTabs(path, name); updateLineNumbers(); // Call to update line numbers editor.focus(); } } // Function to update the tabs at the top function updateTabs(path, name) { const existingTab = document.getElementById(`tab-${path}`); // If tab already exists, just make it active if (existingTab) { document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('bg-gray-600', 'border-gray-500')); existingTab.classList.add('bg-gray-600', 'border-gray-500'); return; } // Create a new tab element const newTab = document.createElement('div'); newTab.id = `tab-${path}`; newTab.classList.add('tab', 'px-4', 'py-2', 'flex', 'items-center', 'space-x-2', 'cursor-pointer', 'bg-gray-700', 'border-r', 'border-gray-700', 'hover:bg-gray-600', 'transition-colors', 'duration-200'); newTab.innerHTML = `${name} `; newTab.onclick = () => openFile(path, name); // Remove active state from other tabs document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('bg-gray-600', 'border-gray-500')); newTab.classList.add('bg-gray-600', 'border-gray-500'); // Add new tab to the bar tabBar.appendChild(newTab); } // Function to close a tab function closeTab(event, path) { event.stopPropagation(); const tabToRemove = document.getElementById(`tab-${path}`); tabBar.removeChild(tabToRemove); // If the closed tab was the active one, open the last remaining tab if (activeFile === path) { const remainingTabs = tabBar.querySelectorAll('.tab'); if (remainingTabs.length > 0) { const lastTab = remainingTabs[remainingTabs.length - 1]; const newPath = lastTab.id.replace('tab-', ''); const newName = lastTab.querySelector('span').textContent; openFile(newPath, newName); } else { editor.value = ''; activeFile = null; updateLineNumbers(); } } } // Function to save the editor content to the file system function saveContent() { if (activeFile) { const pathParts = activeFile.split('/'); let current = fileSystem; for (let i = 0; i < pathParts.length - 1; i++) { current = current[pathParts[i]].children; } const fileName = pathParts[pathParts.length - 1]; current[fileName].content = editor.value; } } // Function to get the parent folder object and item name from a path function getParentAndItem(path) { const pathParts = path.split('/').filter(p => p); const itemName = pathParts.pop(); let parent = fileSystem; for (const part of pathParts) { parent = parent[part].children; } return { parent, itemName }; } // --- Context Menu Action Functions --- // Handles renaming a file or folder function renameItem(path) { const { parent, itemName } = getParentAndItem(path); const newName = prompt(`Rename '${itemName}' to:`, itemName); if (newName && newName !== itemName) { if (parent[newName]) { alert('An item with this name already exists!'); return; } parent[newName] = parent[itemName]; delete parent[itemName]; // If the renamed item was active, update the path if (activeFile === path) { activeFile = path.replace(itemName, newName); } // Update tabs if a tab for this file exists const existingTab = document.getElementById(`tab-${path}`); if (existingTab) { existingTab.id = `tab-${path.replace(itemName, newName)}`; existingTab.querySelector('span').textContent = newName; existingTab.onclick = () => openFile(activeFile, newName); } renderFileTree(fileTree, fileSystem); } } // Handles deleting a file or folder function deleteItem(path) { const { parent, itemName } = getParentAndItem(path); const isConfirmed = confirm(`Are you sure you want to delete '${itemName}'? This cannot be undone.`); if (isConfirmed) { delete parent[itemName]; if (activeFile === path) { activeFile = null; editor.value = ''; } closeTab(new Event('click'), path); renderFileTree(fileTree, fileSystem); } } // Handles creating a new file or folder in a folder function createNewItemInFolder(path, type) { const { parent, itemName } = getParentAndItem(path); const newName = prompt(`Enter new ${type} name:`); if (newName) { const currentFolder = parent[itemName].children; if (currentFolder[newName]) { alert('An item with this name already exists!'); return; } if (type === 'file') { currentFolder[newName] = { type: 'file', content: '' }; openFile(`${path}/${newName}`, newName); } else if (type === 'folder') { currentFolder[newName] = { type: 'folder', children: {} }; } renderFileTree(fileTree, fileSystem); } } // Handles copying a file function copyFile(path) { const { parent, itemName } = getParentAndItem(path); const fileToCopy = parent[itemName]; const newName = prompt(`Enter new file name:`, `copy_of_${itemName}`); if (newName) { if (parent[newName]) { alert('An item with this name already exists!'); return; } parent[newName] = { ...fileToCopy }; // Shallow copy renderFileTree(fileTree, fileSystem); } } // Handles importing a file from the user's computer function importFile(path) { importFileInput.onclick = null; // Clear previous listeners importFileInput.onchange = (event) => { const file = event.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { const content = e.target.result; const { parent, itemName } = getParentAndItem(path); const currentFolder = parent[itemName].children; const newFileName = file.name; if (currentFolder[newFileName]) { alert('An item with this name already exists!'); return; } currentFolder[newFileName] = { type: 'file', content: content }; renderFileTree(fileTree, fileSystem); openFile(`${path}/${newFileName}`, newFileName); }; reader.readAsText(file); } }; importFileInput.click(); // Trigger the file selection dialog } // --- Line Number Logic --- function updateLineNumbers() { const numberOfLines = editor.value.split('\n').length; lineNumbers.innerHTML = ''; // Clear existing numbers let lineNumbersHTML = ''; for (let i = 1; i <= numberOfLines; i++) { lineNumbersHTML += `
${i}
`; } lineNumbers.innerHTML = lineNumbersHTML; } // Function to run the code function runCode() { // Save all content before running saveContent(); // Find the HTML file (prefer index.html if it exists) let htmlFile = fileSystem['index.html']; if (!htmlFile) { // Look for any HTML file in the root for (const fileName in fileSystem) { if (fileSystem[fileName].type === 'file' && fileName.endsWith('.html')) { htmlFile = fileSystem[fileName]; break; } } } if (!htmlFile) { alert('No HTML file found to run. Please create an index.html file first.'); return; } // Create a new window with the HTML content const previewWindow = window.open('', '_blank'); // Get all CSS and JS files that might be linked let cssContent = ''; let jsContent = ''; // Collect all CSS files for (const fileName in fileSystem) { if (fileSystem[fileName].type === 'file' && fileName.endsWith('.css')) { cssContent += `\n`; } } // Collect all JS files for (const fileName in fileSystem) { if (fileSystem[fileName].type === 'file' && fileName.endsWith('.js')) { jsContent += `\n`; } } // Combine all the content let fullHtml = htmlFile.content; // Inject CSS and JS if they're not already linked in the HTML if (!fullHtml.includes(''); if (headEnd !== -1) { fullHtml = fullHtml.slice(0, headEnd) + cssContent + fullHtml.slice(headEnd); } else { fullHtml = `${cssContent}` + fullHtml; } } if (!fullHtml.includes('