Download index.html from wergrom/windeskos-virtual-desktop: direct link, hf CLI and curl.
- Browser
- Download file 47.2 kB
-
https://huggingface.co/spaces/wergrom/windeskos-virtual-desktop/resolve/main/index.html
- Command line
-
hf download hf://spaces/wergrom/windeskos-virtual-desktop/index.html
-
curl -L -o index.html https://huggingface.co/spaces/wergrom/windeskos-virtual-desktop/resolve/main/index.html
47.2 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>WinDeskOS - Virtual Desktop</title> | |
| <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Segoe+UI:wght@300;400;500;600&display=swap'); | |
| * { | |
| font-family: 'Segoe UI', sans-serif; | |
| } | |
| .window { | |
| box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| backdrop-filter: blur(10px); | |
| } | |
| .desktop-icon { | |
| transition: all 0.2s ease; | |
| position: absolute; | |
| } | |
| .desktop-icon:hover { | |
| background-color: rgba(255, 255, 255, 0.1); | |
| transform: scale(1.05); | |
| } | |
| .desktop-icon.dragging { | |
| opacity: 0.8; | |
| z-index: 9999; | |
| } | |
| .taskbar-item { | |
| transition: all 0.2s ease; | |
| } | |
| .taskbar-item:hover { | |
| background-color: rgba(255, 255, 255, 0.1); | |
| } | |
| .start-menu { | |
| transition: all 0.3s ease; | |
| transform-origin: bottom left; | |
| } | |
| .context-menu { | |
| box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3); | |
| } | |
| .desktop-bg { | |
| background: linear-gradient(135deg, #0078d4 0%, #106ebe 100%); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white overflow-hidden h-screen flex flex-col"> | |
| <!-- Desktop Area --> | |
| <div class="desktop-bg flex-1 relative p-4" id="desktop"> | |
| <!-- Desktop Icons --> | |
| <div id="desktop-icon-1" class="desktop-icon cursor-pointer p-2 rounded text-center" style="top: 16px; left: 16px;" data-app="recycle-bin"> | |
| <i data-feather="trash-2" class="w-8 h-8 mx-auto text-blue-300"></i> | |
| <p class="text-xs mt-1 text-white">Recycle Bin</p> | |
| </div> | |
| <div id="desktop-icon-2" class="desktop-icon cursor-pointer p-2 rounded text-center" style="top: 16px; left: 96px;" data-app="documents"> | |
| <i data-feather="folder" class="w-8 h-8 mx-auto text-yellow-300"></i> | |
| <p class="text-xs mt-1 text-white">Documents</p> | |
| </div> | |
| <div id="desktop-icon-3" class="desktop-icon cursor-pointer p-2 rounded text-center" style="top: 80px; left: 16px;" data-app="pictures"> | |
| <i data-feather="image" class="w-8 h-8 mx-auto text-green-300"></i> | |
| <p class="text-xs mt-1 text-white">Pictures</p> | |
| </div> | |
| <div id="desktop-icon-4" class="desktop-icon cursor-pointer p-2 rounded text-center" style="top: 80px; left: 96px;" data-app="browser"> | |
| <i data-feather="globe" class="w-8 h-8 mx-auto text-purple-300"></i> | |
| <p class="text-xs mt-1 text-white">Browser</p> | |
| </div> | |
| <!-- Windows --> | |
| <div id="windows-container" class="absolute inset-0 pointer-events-none"> | |
| <!-- Windows will be dynamically added here --> | |
| </div> | |
| </div> | |
| <!-- Taskbar --> | |
| <div class="bg-gray-800 bg-opacity-90 border-t border-gray-700 h-10 flex items-center px-2"> | |
| <!-- Start Button --> | |
| <button id="start-button" class="taskbar-item h-8 w-8 bg-blue-600 hover:bg-blue-500 rounded flex items-center justify-center mr-2"> | |
| <i data-feather="grid" class="w-4 h-4"></i> | |
| </button> | |
| <!-- User Profile Button --> | |
| <div class="taskbar-item h-8 px-3 bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center cursor-pointer mr-2" onclick="showUserMenu()"> | |
| <i data-feather="user" class="w-4 h-4 mr-2"></i> | |
| <span id="username-display" class="text-xs">User</span> | |
| </div> | |
| <!-- Taskbar Apps --> | |
| <div class="flex space-x-1"> | |
| <div class="taskbar-item h-8 w-8 bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center cursor-pointer" onclick="openWindow('browser')"> | |
| <i data-feather="globe" class="w-4 h-4"></i> | |
| </div> | |
| <div class="taskbar-item h-8 w-8 bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center cursor-pointer" onclick="openWindow('explorer')"> | |
| <i data-feather="folder" class="w-4 h-4"></i> | |
| </div> | |
| <div class="taskbar-item h-8 w-8 bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center cursor-pointer" onclick="openWindow('settings')"> | |
| <i data-feather="settings" class="w-4 h-4"></i> | |
| </div> | |
| </div> | |
| <!-- System Tray --> | |
| <div class="ml-auto flex items-center space-x-3"> | |
| <div class="text-xs cursor-pointer hover:text-blue-300" onclick="toggleVolume()"> | |
| <i data-feather="volume-2" id="volume-icon" class="w-4 h-4"></i> | |
| </div> | |
| <div class="text-xs cursor-pointer hover:text-blue-300" onclick="toggleWiFiMenu()"> | |
| <i data-feather="wifi" id="wifi-icon" class="w-4 h-4"></i> | |
| </div> | |
| <div class="text-xs cursor-pointer hover:text-blue-300" onclick="toggleBatteryInfo()"> | |
| <i data-feather="battery" id="battery-icon" class="w-4 h-4"></i> | |
| </div> | |
| <div class="text-xs"> | |
| <span id="time">15:30</span> | |
| </div> | |
| <div class="w-4 h-4 bg-green-500 rounded-full cursor-pointer" onclick="showNotificationCenter()"></div> | |
| </div> | |
| </div> | |
| <!-- User Menu --> | |
| <div id="user-menu" class="fixed bottom-12 left-14 w-48 bg-gray-800 bg-opacity-95 rounded-lg window hidden transform scale-95 opacity-0 pointer-events-none z-50"> | |
| <div class="p-3 border-b border-gray-700"> | |
| <div class="text-sm font-semibold" id="user-menu-name">User</div> | |
| <div class="text-xs text-gray-400" id="user-menu-email">user@example.com</div> | |
| </div> | |
| <div class="p-1"> | |
| <div class="px-3 py-2 hover:bg-gray-700 cursor-pointer rounded text-sm flex items-center space-x-2" onclick="showUserSettings()"> | |
| <i data-feather="settings" class="w-4 h-4"></i> | |
| <span>Account Settings</span> | |
| </div> | |
| <div class="px-3 py-2 hover:bg-gray-700 cursor-pointer rounded text-sm flex items-center space-x-2" onclick="lockSession()"> | |
| <i data-feather="lock" class="w-4 h-4"></i> | |
| <span>Lock Session</span> | |
| </div> | |
| <div class="border-t border-gray-700 my-1"></div> | |
| <div class="px-3 py-2 hover:bg-gray-700 cursor-pointer rounded text-sm flex items-center space-x-2" onclick="logout()"> | |
| <i data-feather="log-out" class="w-4 h-4"></i> | |
| <span>Sign Out</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Start Menu --> | |
| <div id="start-menu" class="start-menu fixed bottom-10 left-2 w-80 h-96 bg-gray-800 bg-opacity-95 rounded-lg window hidden transform scale-95 opacity-0 pointer-events-none"> | |
| <div class="p-4"> | |
| <div class="text-xl font-semibold mb-4">Applications</div> | |
| <div class="grid grid-cols-2 gap-2"> | |
| <div class="p-3 rounded hover:bg-gray-700 cursor-pointer flex items-center space-x-2" onclick="openWindow('browser')"> | |
| <i data-feather="globe" class="w-6 h-6 text-blue-400"></i> | |
| <span>Browser</span> | |
| </div> | |
| <div class="p-3 rounded hover:bg-gray-700 cursor-pointer flex items-center space-x-2" onclick="openWindow('explorer')"> | |
| <i data-feather="folder" class="w-6 h-6 text-yellow-400"></i> | |
| <span>File Explorer</span> | |
| </div> | |
| <div class="p-3 rounded hover:bg-gray-700 cursor-pointer flex items-center space-x-2" onclick="openWindow('settings')"> | |
| <i data-feather="settings" class="w-6 h-6 text-gray-400"></i> | |
| <span>Settings</span> | |
| </div> | |
| <div class="p-3 rounded hover:bg-gray-700 cursor-pointer flex items-center space-x-2" onclick="openWindow('notepad')"> | |
| <i data-feather="edit" class="w-6 h-6 text-green-400"></i> | |
| <span>Notepad</span> | |
| </div> | |
| <div class="p-3 rounded hover:bg-gray-700 cursor-pointer flex items-center space-x-2" onclick="openWindow('calculator')"> | |
| <i data-feather="calculator" class="w-6 h-6 text-purple-400"></i> | |
| <span>Calculator</span> | |
| </div> | |
| <div class="p-3 rounded hover:bg-gray-700 cursor-pointer flex items-center space-x-2" onclick="openWindow('weather')"> | |
| <i data-feather="cloud" class="w-6 h-6 text-blue-400"></i> | |
| <span>Weather</span> | |
| </div> | |
| <div class="p-3 rounded hover:bg-gray-700 cursor-pointer flex items-center space-x-2" onclick="openWindow('calendar')"> | |
| <i data-feather="calendar" class="w-6 h-6 text-red-400"></i> | |
| <span>Calendar</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Context Menu --> | |
| <div id="context-menu" class="context-menu fixed bg-gray-800 bg-opacity-95 rounded-lg py-2 hidden z-50"> | |
| <div class="px-4 py-2 hover:bg-gray-700 cursor-pointer flex items-center space-x-2"> | |
| <i data-feather="folder-plus" class="w-4 h-4"></i> | |
| <span>New Folder</span> | |
| </div> | |
| <div class="px-4 py-2 hover:bg-gray-700 cursor-pointer flex items-center space-x-2"> | |
| <i data-feather="file-text" class="w-4 h-4"></i> | |
| <span>New Text Document</span> | |
| </div> | |
| <div class="border-t border-gray-700 my-1"></div> | |
| <div class="px-4 py-2 hover:bg-gray-700 cursor-pointer flex items-center space-x-2"> | |
| <i data-feather="settings" class="w-4 h-4"></i> | |
| <span>Personalize</span> | |
| </div> | |
| <div class="px-4 py-2 hover:bg-gray-700 cursor-pointer flex items-center space-x-2"> | |
| <i data-feather="info" class="w-4 h-4"></i> | |
| <span>Properties</span> | |
| </div> | |
| <div class="border-t border-gray-700 my-1"></div> | |
| <div class="px-4 py-2 hover:bg-gray-700 cursor-pointer flex items-center space-x-2"> | |
| <i data-feather="refresh-cw" class="w-4 h-4"></i> | |
| <span>Refresh</span> | |
| </div> | |
| </div> | |
| <!-- Notification Center --> | |
| <div id="notification-center" class="fixed right-2 bottom-12 w-80 h-96 bg-gray-800 bg-opacity-95 rounded-lg window hidden transform scale-95 opacity-0 pointer-events-none z-50"> | |
| <div class="p-4"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <div class="text-lg font-semibold">Notifications</div> | |
| <button onclick="hideNotificationCenter()" class="w-6 h-6 rounded hover:bg-gray-700 flex items-center justify-center"> | |
| <i data-feather="x" class="w-4 h-4"></i> | |
| </button> | |
| </div> | |
| <div class="space-y-2 max-h-64 overflow-y-auto"> | |
| <div class="p-3 bg-gray-700 rounded"> | |
| <div class="text-sm font-medium">System</div> | |
| <div class="text-xs text-gray-300">Welcome to WinDeskOS!</div> | |
| <div class="text-xs text-gray-400 mt-1">Just now</div> | |
| </div> | |
| </div> | |
| <div class="border-t border-gray-700 mt-4 pt-4"> | |
| <div class="text-sm font-semibold mb-2">Quick Settings</div> | |
| <div class="grid grid-cols-3 gap-2"> | |
| <div class="p-2 bg-gray-700 rounded text-center cursor-pointer hover:bg-gray-600" onclick="toggleDarkMode()"> | |
| <i data-feather="moon" class="w-5 h-5 mx-auto"></i> | |
| <div class="text-xs mt-1">Dark Mode</div> | |
| </div> | |
| <div class="p-2 bg-gray-700 rounded text-center cursor-pointer hover:bg-gray-600" onclick="toggleVolume()"> | |
| <i data-feather="volume-2" class="w-5 h-5 mx-auto"></i> | |
| <div class="text-xs mt-1">Volume</div> | |
| </div> | |
| <div class="p-2 bg-gray-700 rounded text-center cursor-pointer hover:bg-gray-600" onclick="toggleWiFi()"> | |
| <i data-feather="wifi" class="w-5 h-5 mx-auto"></i> | |
| <div class="text-xs mt-1">Wi-Fi</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Calculator App Content --> | |
| <script id="calculator-template" type="text/template"> | |
| </script> | |
| <!-- Weather App Content --> | |
| <script id="weather-template" type="text/template"> | |
| </script> | |
| <script> | |
| let windows = []; | |
| let zIndexCounter = 1000; | |
| let currentUser = null; | |
| // Check authentication on page load | |
| function checkAuthentication() { | |
| const userData = localStorage.getItem('currentUser'); | |
| // Allow admin user to bypass login for demo purposes | |
| if (!userData) { | |
| // Create demo admin user if none exists | |
| const demoUser = { | |
| username: 'admin', | |
| rememberMe: true, | |
| loginTime: new Date().toISOString() | |
| }; | |
| localStorage.setItem('currentUser', JSON.stringify(demoUser)); | |
| currentUser = demoUser; | |
| } else { | |
| currentUser = JSON.parse(userData); | |
| } | |
| document.getElementById('username-display').textContent = currentUser.username; | |
| document.getElementById('user-menu-name').textContent = currentUser.username; | |
| document.getElementById('user-menu-email').textContent = `${currentUser.username}@windeskos.com`; | |
| // Load user-specific data | |
| loadUserData(); | |
| } | |
| // Update time | |
| function updateTime() { | |
| const now = new Date(); | |
| document.getElementById('time').textContent = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}); | |
| } | |
| setInterval(updateTime, 1000); | |
| updateTime(); | |
| // User menu functions | |
| function showUserMenu() { | |
| const menu = document.getElementById('user-menu'); | |
| const isVisible = !menu.classList.contains('hidden'); | |
| if (isVisible) { | |
| menu.classList.add('hidden'); | |
| menu.classList.remove('scale-100', 'opacity-100'); | |
| menu.classList.add('scale-95', 'opacity-0'); | |
| menu.style.pointerEvents = 'none'; | |
| } else { | |
| menu.classList.remove('hidden'); | |
| setTimeout(() => { | |
| menu.classList.remove('scale-95', 'opacity-0'); | |
| menu.classList.add('scale-100', 'opacity-100'); | |
| menu.style.pointerEvents = 'auto'; | |
| }, 10); | |
| } | |
| } | |
| function logout() { | |
| // Save user data before logout | |
| saveUserData(); | |
| // Clear current session | |
| localStorage.removeItem('currentUser'); | |
| // Redirect to login | |
| window.location.href = 'login.html'; | |
| } | |
| function lockSession() { | |
| saveUserData(); | |
| window.location.href = 'login.html?locked=true'; | |
| } | |
| function showUserSettings() { | |
| alert('User settings would open here'); | |
| } | |
| // User data management | |
| function saveUserData() { | |
| if (!currentUser) return; | |
| const userData = { | |
| desktopIconPositions: JSON.parse(localStorage.getItem('desktopIconPositions') || '{}'), | |
| openWindows: windows.map(windowId => { | |
| const window = document.getElementById(windowId); | |
| if (window) { | |
| return { | |
| type: window.getAttribute('data-window-type'), | |
| position: { | |
| left: window.style.left, | |
| top: window.style.top, | |
| width: window.style.width, | |
| height: window.style.height | |
| } | |
| }; | |
| } | |
| return null; | |
| }).filter(Boolean), | |
| calculatorState: { | |
| currentInput: currentInput, | |
| previousInput: previousInput, | |
| operation: operation, | |
| shouldResetInput: shouldResetInput | |
| }, | |
| lastSaved: new Date().toISOString() | |
| }; | |
| localStorage.setItem(`userData_${currentUser.username}`, JSON.stringify(userData)); | |
| } | |
| function loadUserData() { | |
| if (!currentUser) return; | |
| const userData = localStorage.getItem(`userData_${currentUser.username}`); | |
| if (userData) { | |
| const data = JSON.parse(userData); | |
| // Load desktop icon positions | |
| if (data.desktopIconPositions) { | |
| Object.keys(data.desktopIconPositions).forEach(iconId => { | |
| const icon = document.getElementById(iconId); | |
| if (icon && data.desktopIconPositions[iconId]) { | |
| icon.style.left = data.desktopIconPositions[iconId].left; | |
| icon.style.top = data.desktopIconPositions[iconId].top; | |
| } | |
| }); | |
| } | |
| // Load calculator state | |
| if (data.calculatorState) { | |
| currentInput = data.calculatorState.currentInput || '0'; | |
| previousInput = data.calculatorState.previousInput || '0'; | |
| operation = data.calculatorState.operation || null; | |
| shouldResetInput = data.calculatorState.shouldResetInput || false; | |
| } | |
| // Open saved windows | |
| if (data.openWindows && data.openWindows.length > 0) { | |
| setTimeout(() => { | |
| data.openWindows.forEach(windowData => { | |
| const windowElement = createWindow(windowData.type); | |
| if (windowElement && windowData.position) { | |
| windowElement.style.left = windowData.position.left; | |
| windowElement.style.top = windowData.position.top; | |
| windowElement.style.width = windowData.position.width; | |
| windowElement.style.height = windowData.position.height; | |
| } | |
| }); | |
| }, 500); | |
| } | |
| } | |
| } | |
| // Auto-save user data periodically | |
| setInterval(saveUserData, 30000); // Save every 30 seconds | |
| // Save data before page unload | |
| window.addEventListener('beforeunload', saveUserData); | |
| // Start menu toggle | |
| document.getElementById('start-button').addEventListener('click', function(e) { | |
| e.stopPropagation(); | |
| const menu = document.getElementById('start-menu'); | |
| const isVisible = !menu.classList.contains('hidden'); | |
| if (isVisible) { | |
| menu.classList.add('hidden'); | |
| menu.classList.remove('scale-100', 'opacity-100'); | |
| menu.classList.add('scale-95', 'opacity-0'); | |
| menu.style.pointerEvents = 'none'; | |
| } else { | |
| menu.classList.remove('hidden'); | |
| setTimeout(() => { | |
| menu.classList.remove('scale-95', 'opacity-0'); | |
| menu.classList.add('scale-100', 'opacity-100'); | |
| menu.style.pointerEvents = 'auto'; | |
| }, 10); | |
| } | |
| }); | |
| // Close start menu when clicking elsewhere | |
| document.addEventListener('click', function(e) { | |
| if (!e.target.closest('#start-menu') && !e.target.closest('#start-button')) { | |
| const menu = document.getElementById('start-menu'); | |
| menu.classList.add('hidden'); | |
| menu.classList.remove('scale-100', 'opacity-100'); | |
| menu.classList.add('scale-95', 'opacity-0'); | |
| menu.style.pointerEvents = 'none'; | |
| } | |
| }); | |
| // Context menu | |
| document.getElementById('desktop').addEventListener('contextmenu', function(e) { | |
| e.preventDefault(); | |
| const menu = document.getElementById('context-menu'); | |
| menu.style.left = e.pageX + 'px'; | |
| menu.style.top = e.pageY + 'px'; | |
| menu.classList.remove('hidden'); | |
| }); | |
| document.addEventListener('click', function() { | |
| document.getElementById('context-menu').classList.add('hidden'); | |
| }); | |
| // Desktop icon dragging functionality | |
| function setupIconDragging() { | |
| const desktopIcons = document.querySelectorAll('.desktop-icon'); | |
| let dragTimeout = null; | |
| let lastClickTime = 0; | |
| desktopIcons.forEach(icon => { | |
| let isDragging = false; | |
| let startX, startY, startLeft, startTop; | |
| let clickCount = 0; | |
| icon.addEventListener('mousedown', function(e) { | |
| if (e.button !== 0) return; // Only left click | |
| // Start drag after short delay to distinguish from click | |
| dragTimeout = setTimeout(() => { | |
| isDragging = true; | |
| startX = e.clientX; | |
| startY = e.clientY; | |
| const computedStyle = window.getComputedStyle(icon); | |
| startLeft = parseInt(computedStyle.left) || 0; | |
| startTop = parseInt(computedStyle.top) || 0; | |
| icon.classList.add('dragging'); | |
| icon.style.zIndex = '9999'; | |
| e.preventDefault(); | |
| }, 150); // Reduced delay for faster response | |
| }); | |
| document.addEventListener('mousemove', function(e) { | |
| if (!isDragging) return; | |
| clearTimeout(dragTimeout); | |
| const dx = e.clientX - startX; | |
| const dy = e.clientY - startY; | |
| icon.style.left = (startLeft + dx) + 'px'; | |
| icon.style.top = (startTop + dy) + 'px'; | |
| }); | |
| document.addEventListener('mouseup', function() { | |
| clearTimeout(dragTimeout); | |
| if (isDragging) { | |
| isDragging = false; | |
| icon.classList.remove('dragging'); | |
| icon.style.zIndex = ''; | |
| // Save position to localStorage | |
| const positions = JSON.parse(localStorage.getItem('desktopIconPositions') || '{}'); | |
| positions[icon.id] = { | |
| left: icon.style.left, | |
| top: icon.style.top | |
| }; | |
| localStorage.setItem('desktopIconPositions', JSON.stringify(positions)); | |
| } | |
| }); | |
| // Double click to open app | |
| icon.addEventListener('click', function(e) { | |
| clearTimeout(dragTimeout); | |
| if (isDragging) return; | |
| const currentTime = new Date().getTime(); | |
| const timeDiff = currentTime - lastClickTime; | |
| if (timeDiff < 300 && timeDiff > 0) { // Double click within 300ms | |
| const appType = icon.getAttribute('data-app'); | |
| openWindow(appType); | |
| lastClickTime = 0; | |
| } else { | |
| lastClickTime = currentTime; | |
| } | |
| }); | |
| }); | |
| } | |
| // Load saved icon positions | |
| function loadIconPositions() { | |
| const positions = JSON.parse(localStorage.getItem('desktopIconPositions') || '{}'); | |
| Object.keys(positions).forEach(iconId => { | |
| const icon = document.getElementById(iconId); | |
| if (icon && positions[iconId]) { | |
| icon.style.left = positions[iconId].left; | |
| icon.style.top = positions[iconId].top; | |
| } | |
| }); | |
| } | |
| // Window management | |
| function openWindow(type) { | |
| const windowElement = createWindow(type); | |
| } | |
| function createWindow(type) { | |
| const windowId = 'window-' + Date.now(); | |
| const windowContent = getWindowContent(type); | |
| const windowElement = document.createElement('div'); | |
| windowElement.id = windowId; | |
| windowElement.setAttribute('data-window-type', type); | |
| windowElement.className = 'window bg-gray-800 bg-opacity-95 rounded-lg absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-96 h-64 pointer-events-auto'; | |
| windowElement.style.zIndex = zIndexCounter++; | |
| windowElement.innerHTML = ` | |
| <div class="flex items-center justify-between p-3 bg-gray-900 rounded-t-lg cursor-move border-b border-gray-700"> | |
| <div class="flex items-center space-x-2"> | |
| <i data-feather="${windowContent.icon}" class="w-4 h-4 ${windowContent.iconColor}"></i> | |
| <span>${windowContent.title}</span> | |
| </div> | |
| <div class="flex space-x-1"> | |
| <button class="w-6 h-6 rounded hover:bg-gray-700 flex items-center justify-center">–</button> | |
| <button class="w-6 h-6 rounded hover:bg-gray-700 flex items-center justify-center">□</button> | |
| <button class="w-6 h-6 rounded hover:bg-red-600 flex items-center justify-center" onclick="closeWindow('${windowId}')">×</button> | |
| </div> | |
| </div> | |
| <div class="p-4 h-[calc(100%-3rem)]"> | |
| ${windowContent.content} | |
| </div> | |
| `; | |
| document.getElementById('windows-container').appendChild(windowElement); | |
| feather.replace(); | |
| // Make window draggable | |
| makeDraggable(windowElement); | |
| windows.push(windowId); | |
| return windowElement; | |
| } | |
| function closeWindow(id) { | |
| const window = document.getElementById(id); | |
| if (window) { | |
| window.remove(); | |
| windows = windows.filter(winId => winId !== id); | |
| } | |
| } | |
| function makeDraggable(element) { | |
| const header = element.querySelector('.bg-gray-900'); | |
| let isDragging = false; | |
| let isResizing = false; | |
| let resizeDirection = ''; | |
| let startX, startY, startLeft, startTop, startWidth, startHeight; | |
| // Add resize handles | |
| const resizeHandles = ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw']; | |
| resizeHandles.forEach(dir => { | |
| const handle = document.createElement('div'); | |
| handle.className = `resize-handle resize-${dir}`; | |
| handle.style.cssText = ` | |
| position: absolute; | |
| background: transparent; | |
| z-index: 10; | |
| `; | |
| // Position and size based on direction | |
| switch(dir) { | |
| case 'n': handle.style.top = '0'; handle.style.left = '10px'; handle.style.right = '10px'; handle.style.height = '4px'; handle.style.cursor = 'ns-resize'; break; | |
| case 'ne': handle.style.top = '0'; handle.style.right = '0'; handle.style.width = '6px'; handle.style.height = '6px'; handle.style.cursor = 'nesw-resize'; break; | |
| case 'e': handle.style.top = '10px'; handle.style.right = '0'; handle.style.width = '4px'; handle.style.bottom = '10px'; handle.style.cursor = 'ew-resize'; break; | |
| case 'se': handle.style.bottom = '0'; handle.style.right = '0'; handle.style.width = '6px'; handle.style.height = '6px'; handle.style.cursor = 'nwse-resize'; break; | |
| case 's': handle.style.bottom = '0'; handle.style.left = '10px'; handle.style.right = '10px'; handle.style.height = '4px'; handle.style.cursor = 'ns-resize'; break; | |
| case 'sw': handle.style.bottom = '0'; handle.style.left = '0'; handle.style.width = '6px'; handle.style.height = '6px'; handle.style.cursor = 'nesw-resize'; break; | |
| case 'w': handle.style.top = '10px'; handle.style.left = '0'; handle.style.width = '4px'; handle.style.bottom = '10px'; handle.style.cursor = 'ew-resize'; break; | |
| case 'nw': handle.style.top = '0'; handle.style.left = '0'; handle.style.width = '6px'; handle.style.height = '6px'; handle.style.cursor = 'nwse-resize'; break; | |
| } | |
| handle.addEventListener('mousedown', function(e) { | |
| e.stopPropagation(); | |
| isResizing = true; | |
| resizeDirection = dir; | |
| startX = e.clientX; | |
| startY = e.clientY; | |
| startWidth = element.offsetWidth; | |
| startHeight = element.offsetHeight; | |
| startLeft = parseInt(element.style.left) || (window.innerWidth/2 - startWidth/2); | |
| startTop = parseInt(element.style.top) || (window.innerHeight/2 - startHeight/2); | |
| // Bring to front | |
| element.style.zIndex = zIndexCounter++; | |
| }); | |
| element.appendChild(handle); | |
| }); | |
| header.addEventListener('mousedown', function(e) { | |
| isDragging = true; | |
| startX = e.clientX; | |
| startY = e.clientY; | |
| // Get current position from computed style | |
| const computedStyle = window.getComputedStyle(element); | |
| startLeft = parseInt(computedStyle.left) || (window.innerWidth/2 - element.offsetWidth/2); | |
| startTop = parseInt(computedStyle.top) || (window.innerHeight/2 - element.offsetHeight/2); | |
| // Bring to front | |
| element.style.zIndex = zIndexCounter++; | |
| element.style.left = startLeft + 'px'; | |
| element.style.top = startTop + 'px'; | |
| }); | |
| document.addEventListener('mousemove', function(e) { | |
| if (isDragging) { | |
| const dx = e.clientX - startX; | |
| const dy = e.clientY - startY; | |
| element.style.left = (startLeft + dx) + 'px'; | |
| element.style.top = (startTop + dy) + 'px'; | |
| } | |
| if (isResizing) { | |
| const dx = e.clientX - startX; | |
| const dy = e.clientY - startY; | |
| let newWidth = startWidth; | |
| let newHeight = startHeight; | |
| let newLeft = startLeft; | |
| let newTop = startTop; | |
| switch(resizeDirection) { | |
| case 'e': | |
| newWidth = Math.max(200, startWidth + dx); | |
| break; | |
| case 'w': | |
| newWidth = Math.max(200, startWidth - dx); | |
| newLeft = startLeft + dx; | |
| break; | |
| case 's': | |
| newHeight = Math.max(150, startHeight + dy); | |
| break; | |
| case 'n': | |
| newHeight = Math.max(150, startHeight - dy); | |
| newTop = startTop + dy; | |
| break; | |
| case 'se': | |
| newWidth = Math.max(200, startWidth + dx); | |
| newHeight = Math.max(150, startHeight + dy); | |
| break; | |
| case 'sw': | |
| newWidth = Math.max(200, startWidth - dx); | |
| newHeight = Math.max(150, startHeight + dy); | |
| newLeft = startLeft + dx; | |
| break; | |
| case 'ne': | |
| newWidth = Math.max(200, startWidth + dx); | |
| newHeight = Math.max(150, startHeight - dy); | |
| newTop = startTop + dy; | |
| break; | |
| case 'nw': | |
| newWidth = Math.max(200, startWidth - dx); | |
| newHeight = Math.max(150, startHeight - dy); | |
| newLeft = startLeft + dx; | |
| newTop = startTop + dy; | |
| break; | |
| } | |
| element.style.width = newWidth + 'px'; | |
| element.style.height = newHeight + 'px'; | |
| element.style.left = newLeft + 'px'; | |
| element.style.top = newTop + 'px'; | |
| } | |
| }); | |
| document.addEventListener('mouseup', function() { | |
| isDragging = false; | |
| isResizing = false; | |
| }); | |
| } | |
| function getWindowContent(type) { | |
| const contents = { | |
| 'browser': { | |
| icon: 'globe', | |
| iconColor: 'text-blue-400', | |
| title: 'Browser', | |
| content: '<div class="h-full flex items-center justify-center"><p class="text-gray-400">Browser content would go here</p></div>' | |
| }, | |
| 'explorer': { | |
| icon: 'folder', | |
| iconColor: 'text-yellow-400', | |
| title: 'File Explorer', | |
| content: '<div class="h-full flex items-center justify-center"><p class="text-gray-400">File Explorer content would go here</p></div>' | |
| }, | |
| 'settings': { | |
| icon: 'settings', | |
| iconColor: 'text-gray-400', | |
| title: 'Settings', | |
| content: '<div class="h-full flex items-center justify-center"><p class="text-gray-400">Settings content would go here</p></div>' | |
| }, | |
| 'notepad': { | |
| icon: 'edit', | |
| iconColor: 'text-green-400', | |
| title: 'Notepad', | |
| content: '<textarea class="w-full h-full bg-gray-700 text-white p-2 rounded resize-none" placeholder="Start typing..."></textarea>' | |
| }, | |
| 'calculator': { | |
| icon: 'calculator', | |
| iconColor: 'text-purple-400', | |
| title: 'Calculator', | |
| content: document.getElementById('calculator-template').innerHTML | |
| }, | |
| 'weather': { | |
| icon: 'cloud', | |
| iconColor: 'text-blue-400', | |
| title: 'Weather', | |
| content: document.getElementById('weather-template').innerHTML | |
| }, | |
| 'calendar': { | |
| icon: 'calendar', | |
| iconColor: 'text-red-400', | |
| title: 'Calendar', | |
| content: '<div class="h-full flex items-center justify-center"><p class="text-gray-400">Calendar content would go here</p></div>' | |
| }, | |
| 'documents': { | |
| icon: 'folder', | |
| iconColor: 'text-yellow-400', | |
| title: 'Documents', | |
| content: '<div class="h-full flex items-center justify-center"><p class="text-gray-400">Documents folder content</p></div>' | |
| }, | |
| 'pictures': { | |
| icon: 'image', | |
| iconColor: 'text-green-400', | |
| title: 'Pictures', | |
| content: '<div class="h-full flex items-center justify-center"><p class="text-gray-400">Pictures folder content</p></div>' | |
| }, | |
| 'recycle-bin': { | |
| icon: 'trash-2', | |
| iconColor: 'text-blue-400', | |
| title: 'Recycle Bin', | |
| content: '<div class="h-full flex items-center justify-center"><p class="text-gray-400">Recycle Bin is empty</p></div>' | |
| } | |
| }; | |
| return contents[type] || contents['browser']; | |
| } | |
| // Calculator functionality | |
| let currentInput = '0'; | |
| let previousInput = '0'; | |
| let operation = null; | |
| let shouldResetInput = false; | |
| function calcInput(value) { | |
| if (currentInput === '0' || shouldResetInput) { | |
| currentInput = value; | |
| shouldResetInput = false; | |
| } else { | |
| currentInput += value; | |
| } | |
| updateCalcDisplay(); | |
| } | |
| function calcOperation(op) { | |
| if (operation !== null) calcCalculate(); | |
| previousInput = currentInput; | |
| operation = op; | |
| shouldResetInput = true; | |
| } | |
| function calcCalculate() { | |
| let result; | |
| const prev = parseFloat(previousInput); | |
| const current = parseFloat(currentInput); | |
| if (isNaN(prev) || isNaN(current)) return; | |
| switch(operation) { | |
| case '+': result = prev + current; break; | |
| case '-': result = prev - current; break; | |
| case '*': result = prev * current; break; | |
| case '/': result = prev / current; break; | |
| case '%': result = prev % current; break; | |
| default: return; | |
| } | |
| currentInput = result.toString(); | |
| operation = null; | |
| shouldResetInput = true; | |
| updateCalcDisplay(); | |
| } | |
| function calcClear() { | |
| currentInput = '0'; | |
| previousInput = '0'; | |
| operation = null; | |
| shouldResetInput = false; | |
| updateCalcDisplay(); | |
| } | |
| function calcBackspace() { | |
| currentInput = currentInput.slice(0, -1); | |
| if (currentInput === '') currentInput = '0'; | |
| updateCalcDisplay(); | |
| } | |
| function updateCalcDisplay() { | |
| const display = document.getElementById('calc-display'); | |
| if (display) display.textContent = currentInput; | |
| } | |
| // Weather functionality | |
| async function fetchWeather() { | |
| try { | |
| const response = await fetch('https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t=temperature_2m,weather_code'); | |
| const data = await response.json(); | |
| const tempElement = document.getElementById('weather-temp'); | |
| const descElement = document.getElementById('weather-desc'); | |
| const locationElement = document.getElementById('weather-location'); | |
| if (tempElement) tempElement.textContent = `${data.current.temperature_2m}°C`; | |
| if (descElement) descElement.textContent = getWeatherDescription(data.current.weather_code); | |
| if (locationElement) locationElement.textContent = 'Berlin, Germany'; | |
| } catch (error) { | |
| console.error('Error fetching weather:', error); | |
| } | |
| } | |
| function getWeatherDescription(code) { | |
| const weatherCodes = { | |
| 0: 'Clear sky', | |
| 1: 'Mainly clear', | |
| 2: 'Partly cloudy', | |
| 3: 'Overcast', | |
| 45: 'Fog', | |
| 48: 'Depositing rime fog', | |
| 51: 'Light drizzle', | |
| 53: 'Moderate drizzle', | |
| 55: 'Dense drizzle', | |
| 61: 'Slight rain', | |
| 63: 'Moderate rain', | |
| 65: 'Heavy rain', | |
| 80: 'Slight rain showers', | |
| 81: 'Moderate rain showers', | |
| 82: 'Violent rain showers' | |
| }; | |
| return weatherCodes[code] || 'Unknown weather'; | |
| } | |
| // System tray functionality | |
| function toggleVolume() { | |
| alert('Volume control would open here'); | |
| } | |
| function toggleWiFiMenu() { | |
| alert('Wi-Fi settings would open here'); | |
| } | |
| function toggleBatteryInfo() { | |
| alert('Battery information would show here'); | |
| } | |
| function showNotificationCenter() { | |
| const center = document.getElementById('notification-center'); | |
| center.classList.remove('hidden'); | |
| setTimeout(() => { | |
| center.classList.remove('scale-95', 'opacity-0'); | |
| center.classList.add('scale-100', 'opacity-100'); | |
| center.style.pointerEvents = 'auto'; | |
| }, 10); | |
| } | |
| function hideNotificationCenter() { | |
| const center = document.getElementById('notification-center'); | |
| center.classList.add('hidden'); | |
| center.classList.remove('scale-100', 'opacity-100'); | |
| center.classList.add('scale-95', 'opacity-0'); | |
| center.style.pointerEvents = 'none'; | |
| } | |
| function toggleDarkMode() { | |
| document.body.classList.toggle('bg-gray-900'); | |
| document.body.classList.toggle('bg-gray-100'); | |
| document.body.classList.toggle('text-white'); | |
| document.body.classList.toggle('text-gray-900'); | |
| } | |
| function toggleWiFi() { | |
| const wifiIcon = document.getElementById('wifi-icon'); | |
| const isConnected = wifiIcon.getAttribute('data-feather') === 'wifi'; | |
| feather.replace({ | |
| 'wifi-icon': isConnected ? 'wifi-off' : 'wifi' | |
| }); | |
| } | |
| // Initialize feather icons and handle drag prevention | |
| feather.replace(); | |
| // Prevent drag on icons | |
| document.querySelectorAll('.desktop-icon i, .taskbar-item i').forEach(icon => { | |
| icon.addEventListener('mousedown', function(e) { | |
| e.stopPropagation(); | |
| }); | |
| }); | |
| // Setup icon dragging and load positions | |
| setupIconDragging(); | |
| loadIconPositions(); | |
| // Initial weather fetch | |
| setTimeout(fetchWeather, 1000); | |
| // Initialize authentication check | |
| checkAuthentication(); | |
| </script> | |
| </body> | |
| </html> | |