| | <!DOCTYPE html> |
| | <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"> |
| | |
| | <div class="desktop-bg flex-1 relative p-4" id="desktop"> |
| | |
| | <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> |
| | |
| | <div id="windows-container" class="absolute inset-0 pointer-events-none"> |
| | |
| | </div> |
| | </div> |
| | |
| | <div class="bg-gray-800 bg-opacity-90 border-t border-gray-700 h-10 flex items-center px-2"> |
| | |
| | <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> |
| | |
| | |
| | <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> |
| | |
| | <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> |
| | |
| | <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> |
| | |
| | |
| | <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> |
| | |
| | <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> |
| | |
| | <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> |
| |
|
| | |
| | <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> |
| |
|
| | |
| | <script id="calculator-template" type="text/template"> |
| | <div class="h-full flex flex-col"> |
| | <div class="bg-gray-900 p-2 text-right text-2xl font-mono mb-2 rounded" id="calc-display">0</div> |
| | <div class="grid grid-cols-4 gap-2 flex-1"> |
| | <button class="bg-gray-700 hover:bg-gray-600 rounded p-2" onclick="calcClear()">C</button> |
| | <button class="bg-gray-700 hover:bg-gray-600 rounded p-2" onclick="calcBackspace()">⌫</button> |
| | <button class="bg-gray-700 hover:bg-gray-600 rounded p-2" onclick="calcOperation('%')">%</button> |
| | <button class="bg-blue-600 hover:bg-blue-500 rounded p-2" onclick="calcOperation('/')">/</button> |
| | |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2" onclick="calcInput('7')">7</button> |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2" onclick="calcInput('8')">8</button> |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2" onclick="calcInput('9')">9</button> |
| | <button class="bg-blue-600 hover:bg-blue-500 rounded p-2" onclick="calcOperation('*')">×</button> |
| | |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2" onclick="calcInput('4')">4</button> |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2" onclick="calcInput('5')">5</button> |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2" onclick="calcInput('6')">6</button> |
| | <button class="bg-blue-600 hover:bg-blue-500 rounded p-2" onclick="calcOperation('-')">-</button> |
| | |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2" onclick="calcInput('1')">1</button> |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2" onclick="calcInput('2')">2</button> |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2" onclick="calcInput('3')">3</button> |
| | <button class="bg-blue-600 hover:bg-blue-500 rounded p-2" onclick="calcOperation('+')">+</button> |
| | |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2 col-span-2" onclick="calcInput('0')">0</button> |
| | <button class="bg-gray-600 hover:bg-gray-500 rounded p-2" onclick="calcInput('.')">.</button> |
| | <button class="bg-green-600 hover:bg-green-500 rounded p-2" onclick="calcCalculate()">=</button> |
| | </div> |
| | </div> |
| | </script> |
| |
|
| | |
| | <script id="weather-template" type="text/template"> |
| | <div class="h-full flex flex-col items-center justify-center p-4"> |
| | <i data-feather="cloud" class="w-16 h-16 text-blue-400 mb-4"></i> |
| | <div class="text-3xl font-bold" id="weather-temp">--°C</div> |
| | <div class="text-lg text-gray-300 mb-2" id="weather-location">Loading...</div> |
| | <div class="text-sm text-gray-400" id="weather-desc">Fetching weather data</div> |
| | <button onclick="fetchWeather()" class="mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-500 rounded text-sm"> |
| | <i data-feather="refresh-cw" class="w-4 h-4 inline mr-1"></i> |
| | Refresh |
| | </button> |
| | </div> |
| | </script> |
| | <script> |
| | let windows = []; |
| | let zIndexCounter = 1000; |
| | let currentUser = null; |
| | |
| | function checkAuthentication() { |
| | const userData = localStorage.getItem('currentUser'); |
| | |
| | if (!userData) { |
| | |
| | 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`; |
| | |
| | |
| | loadUserData(); |
| | } |
| | |
| | function updateTime() { |
| | const now = new Date(); |
| | document.getElementById('time').textContent = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}); |
| | } |
| | setInterval(updateTime, 1000); |
| | updateTime(); |
| | |
| | |
| | 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() { |
| | |
| | saveUserData(); |
| | |
| | |
| | localStorage.removeItem('currentUser'); |
| | |
| | |
| | window.location.href = 'login.html'; |
| | } |
| | |
| | function lockSession() { |
| | saveUserData(); |
| | window.location.href = 'login.html?locked=true'; |
| | } |
| | |
| | function showUserSettings() { |
| | alert('User settings would open here'); |
| | } |
| | |
| | |
| | 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); |
| | |
| | |
| | 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; |
| | } |
| | }); |
| | } |
| | |
| | |
| | if (data.calculatorState) { |
| | currentInput = data.calculatorState.currentInput || '0'; |
| | previousInput = data.calculatorState.previousInput || '0'; |
| | operation = data.calculatorState.operation || null; |
| | shouldResetInput = data.calculatorState.shouldResetInput || false; |
| | } |
| | |
| | |
| | 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); |
| | } |
| | } |
| | } |
| | |
| | |
| | setInterval(saveUserData, 30000); |
| | |
| | |
| | window.addEventListener('beforeunload', saveUserData); |
| | |
| | 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); |
| | } |
| | }); |
| | |
| | |
| | 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'; |
| | } |
| | }); |
| | |
| | |
| | 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'); |
| | }); |
| | |
| | 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; |
| | |
| | |
| | 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); |
| | }); |
| | |
| | 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 = ''; |
| | |
| | |
| | const positions = JSON.parse(localStorage.getItem('desktopIconPositions') || '{}'); |
| | positions[icon.id] = { |
| | left: icon.style.left, |
| | top: icon.style.top |
| | }; |
| | localStorage.setItem('desktopIconPositions', JSON.stringify(positions)); |
| | } |
| | }); |
| | |
| | |
| | icon.addEventListener('click', function(e) { |
| | clearTimeout(dragTimeout); |
| | if (isDragging) return; |
| | |
| | const currentTime = new Date().getTime(); |
| | const timeDiff = currentTime - lastClickTime; |
| | |
| | if (timeDiff < 300 && timeDiff > 0) { |
| | const appType = icon.getAttribute('data-app'); |
| | openWindow(appType); |
| | lastClickTime = 0; |
| | } else { |
| | lastClickTime = currentTime; |
| | } |
| | }); |
| | }); |
| | } |
| | |
| | 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; |
| | } |
| | }); |
| | } |
| | |
| | |
| | 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(); |
| | |
| | |
| | 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; |
| | |
| | |
| | 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; |
| | `; |
| | |
| | |
| | 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); |
| | |
| | |
| | element.style.zIndex = zIndexCounter++; |
| | }); |
| | |
| | element.appendChild(handle); |
| | }); |
| | |
| | header.addEventListener('mousedown', function(e) { |
| | isDragging = true; |
| | startX = e.clientX; |
| | startY = e.clientY; |
| | |
| | |
| | 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); |
| | |
| | |
| | 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']; |
| | } |
| | |
| | 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; |
| | } |
| | |
| | |
| | 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'; |
| | } |
| | |
| | |
| | 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' |
| | }); |
| | } |
| | |
| | |
| | feather.replace(); |
| | |
| | |
| | document.querySelectorAll('.desktop-icon i, .taskbar-item i').forEach(icon => { |
| | icon.addEventListener('mousedown', function(e) { |
| | e.stopPropagation(); |
| | }); |
| | }); |
| | |
| | |
| | setupIconDragging(); |
| | loadIconPositions(); |
| | |
| | setTimeout(fetchWeather, 1000); |
| | |
| | |
| | checkAuthentication(); |
| | </script> |
| | </body> |
| | </html> |
| |
|