From c475398ce9ed17dc59b3da2bd6f1e4c882c9b721 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 5 Jan 2026 18:10:47 +0000
Subject: [PATCH 1/5] Initial plan
From a1d6c3404e285c7b7ecf92d25f1dd4d1f1d8780c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 5 Jan 2026 18:32:51 +0000
Subject: [PATCH 2/5] Fix login functionality and redesign UI as modern desktop
OS
Co-authored-by: masterofmagic999 <237843750+masterofmagic999@users.noreply.github.com>
---
public/app.js | 390 ++++++++++++++---
public/index.html | 347 +++++++++++----
public/styles.css | 1039 +++++++++++++++++++++++++++++----------------
3 files changed, 1264 insertions(+), 512 deletions(-)
diff --git a/public/app.js b/public/app.js
index 7cee3bb..df7b881 100644
--- a/public/app.js
+++ b/public/app.js
@@ -2,6 +2,9 @@ let socket;
let terminal;
let fitAddon;
let currentUser = null;
+let openWindows = {};
+let windowZIndex = 100;
+let activeWindow = null;
// Toast notification function
function showToast(message, type = 'success') {
@@ -91,6 +94,7 @@ async function logout() {
try {
await fetch('/api/logout', { method: 'POST' });
currentUser = null;
+ openWindows = {};
showAuthScreen();
showToast('Logged out successfully');
} catch (error) {
@@ -115,38 +119,189 @@ function showAuthScreen() {
terminal.dispose();
terminal = null;
}
+ // Close all windows
+ document.querySelectorAll('.window').forEach(w => w.style.display = 'none');
+ updateTaskbar();
}
function showAppScreen() {
document.getElementById('auth-screen').classList.remove('active');
document.getElementById('app-screen').classList.add('active');
document.getElementById('username-display').textContent = currentUser;
- initializeTerminal();
+ document.getElementById('settings-username').textContent = currentUser;
loadInstalledApps();
loadHistory();
+ updateClock();
+ setInterval(updateClock, 1000);
}
-// Tab navigation
-function showTab(tabName, event) {
- // Remove active class from all tabs and buttons
- document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
- document.querySelectorAll('.nav-btn').forEach(btn => btn.classList.remove('active'));
+function updateClock() {
+ const clock = document.getElementById('tray-clock');
+ if (clock) {
+ const now = new Date();
+ clock.textContent = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
+ }
+}
- // Add active class to selected tab and button
- document.getElementById(`${tabName}-tab`).classList.add('active');
- if (event) {
- event.target.closest('.nav-btn').classList.add('active');
- } else {
- // If no event, find the button by tabName
- document.querySelector(`[onclick*="${tabName}"]`)?.classList.add('active');
+// Window management
+function openWindow(appName) {
+ const windowEl = document.getElementById(`${appName}-window`);
+ if (!windowEl) return;
+
+ if (openWindows[appName]) {
+ // Window already open, focus it
+ focusWindow(appName);
+ if (windowEl.classList.contains('minimized')) {
+ windowEl.classList.remove('minimized');
+ }
+ return;
+ }
+
+ // Set initial position
+ const offset = Object.keys(openWindows).length * 30;
+ windowEl.style.top = `${50 + offset}px`;
+ windowEl.style.left = `${100 + offset}px`;
+ windowEl.style.width = '700px';
+ windowEl.style.height = '500px';
+ windowEl.style.display = 'flex';
+
+ openWindows[appName] = true;
+ focusWindow(appName);
+ updateTaskbar();
+
+ // Initialize terminal if opening terminal window
+ if (appName === 'terminal' && !terminal) {
+ setTimeout(initializeTerminal, 100);
+ }
+
+ // Hide start menu
+ hideStartMenu();
+}
+
+function closeWindow(appName) {
+ const windowEl = document.getElementById(`${appName}-window`);
+ if (!windowEl) return;
+
+ windowEl.style.display = 'none';
+ windowEl.classList.remove('maximized', 'minimized', 'focused');
+ delete openWindows[appName];
+
+ if (appName === 'terminal' && terminal) {
+ terminal.dispose();
+ terminal = null;
+ }
+
+ updateTaskbar();
+}
+
+function minimizeWindow(appName) {
+ const windowEl = document.getElementById(`${appName}-window`);
+ if (!windowEl) return;
+
+ windowEl.classList.add('minimized');
+ windowEl.classList.remove('focused');
+ updateTaskbar();
+}
+
+function maximizeWindow(appName) {
+ const windowEl = document.getElementById(`${appName}-window`);
+ if (!windowEl) return;
+
+ windowEl.classList.toggle('maximized');
+
+ if (appName === 'terminal' && fitAddon) {
+ setTimeout(() => fitAddon.fit(), 100);
}
+}
+
+function focusWindow(appName) {
+ // Remove focus from all windows
+ document.querySelectorAll('.window').forEach(w => w.classList.remove('focused'));
+
+ const windowEl = document.getElementById(`${appName}-window`);
+ if (!windowEl) return;
+
+ windowEl.style.zIndex = ++windowZIndex;
+ windowEl.classList.add('focused');
+ activeWindow = appName;
+ updateTaskbar();
+}
+
+function updateTaskbar() {
+ const taskbarApps = document.getElementById('taskbar-apps');
+ taskbarApps.innerHTML = '';
+
+ const appNames = {
+ terminal: { icon: 'đģ', name: 'Terminal' },
+ browser: { icon: 'đ', name: 'Browser' },
+ store: { icon: 'đĻ', name: 'App Store' },
+ apps: { icon: 'đą', name: 'My Apps' },
+ history: { icon: 'đ', name: 'History' },
+ settings: { icon: 'âī¸', name: 'Settings' }
+ };
+
+ for (const [app, isOpen] of Object.entries(openWindows)) {
+ if (isOpen && appNames[app]) {
+ const btn = document.createElement('button');
+ btn.className = `taskbar-app${activeWindow === app ? ' active' : ''}`;
+ btn.innerHTML = `${appNames[app].icon} ${appNames[app].name}`;
+ btn.addEventListener('click', () => {
+ const windowEl = document.getElementById(`${app}-window`);
+ if (windowEl.classList.contains('minimized')) {
+ windowEl.classList.remove('minimized');
+ focusWindow(app);
+ } else if (activeWindow === app) {
+ minimizeWindow(app);
+ } else {
+ focusWindow(app);
+ }
+ });
+ taskbarApps.appendChild(btn);
+ }
+ }
+}
- // Initialize terminal if switching to terminal tab
- if (tabName === 'terminal' && !terminal) {
- initializeTerminal();
+// Start menu
+function toggleStartMenu() {
+ const startMenu = document.getElementById('start-menu');
+ if (startMenu.style.display === 'none' || !startMenu.style.display) {
+ startMenu.style.display = 'block';
+ } else {
+ hideStartMenu();
}
}
+function hideStartMenu() {
+ document.getElementById('start-menu').style.display = 'none';
+}
+
+// Make windows draggable
+function initDraggable(windowEl) {
+ const titlebar = windowEl.querySelector('.window-titlebar');
+ let isDragging = false;
+ let offsetX, offsetY;
+
+ titlebar.addEventListener('mousedown', (e) => {
+ if (e.target.closest('.window-controls')) return;
+ if (windowEl.classList.contains('maximized')) return;
+
+ isDragging = true;
+ offsetX = e.clientX - windowEl.offsetLeft;
+ offsetY = e.clientY - windowEl.offsetTop;
+ focusWindow(windowEl.id.replace('-window', ''));
+ });
+
+ document.addEventListener('mousemove', (e) => {
+ if (!isDragging) return;
+ windowEl.style.left = `${e.clientX - offsetX}px`;
+ windowEl.style.top = `${e.clientY - offsetY}px`;
+ });
+
+ document.addEventListener('mouseup', () => {
+ isDragging = false;
+ });
+}
+
// Terminal functions
function initializeTerminal() {
if (terminal) return;
@@ -159,34 +314,36 @@ function initializeTerminal() {
fontSize: 14,
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
theme: {
- background: '#1e1e1e',
- foreground: '#d4d4d4',
+ background: '#0a0a0a',
+ foreground: '#e4e4e7',
cursor: '#ffffff',
- black: '#000000',
- red: '#cd3131',
- green: '#0dbc79',
- yellow: '#e5e510',
- blue: '#2472c8',
- magenta: '#bc3fbc',
- cyan: '#11a8cd',
- white: '#e5e5e5',
- brightBlack: '#666666',
- brightRed: '#f14c4c',
- brightGreen: '#23d18b',
- brightYellow: '#f5f543',
- brightBlue: '#3b8eea',
- brightMagenta: '#d670d6',
- brightCyan: '#29b8db',
- brightWhite: '#e5e5e5'
+ cursorAccent: '#0a0a0a',
+ black: '#18181b',
+ red: '#ef4444',
+ green: '#22c55e',
+ yellow: '#eab308',
+ blue: '#3b82f6',
+ magenta: '#a855f7',
+ cyan: '#06b6d4',
+ white: '#f4f4f5',
+ brightBlack: '#52525b',
+ brightRed: '#f87171',
+ brightGreen: '#4ade80',
+ brightYellow: '#facc15',
+ brightBlue: '#60a5fa',
+ brightMagenta: '#c084fc',
+ brightCyan: '#22d3ee',
+ brightWhite: '#fafafa'
}
});
fitAddon = new FitAddon.FitAddon();
terminal.loadAddon(fitAddon);
terminal.open(terminalElement);
- fitAddon.fit();
+
+ setTimeout(() => fitAddon.fit(), 50);
- // Connect to WebSocket (session handled via cookies)
+ // Connect to WebSocket
socket = io();
socket.on('connect', () => {
@@ -198,7 +355,11 @@ function initializeTerminal() {
});
socket.on('terminal-exit', () => {
- terminal.writeln('\r\nTerminal session ended. Refresh to start a new session.');
+ terminal.writeln('\r\nTerminal session ended. Reopen to start a new session.');
+ });
+
+ socket.on('terminal-error', (error) => {
+ showToast(error, 'error');
});
terminal.onData((data) => {
@@ -206,15 +367,19 @@ function initializeTerminal() {
});
// Handle window resize
- window.addEventListener('resize', () => {
+ const resizeObserver = new ResizeObserver(() => {
if (fitAddon && terminal) {
fitAddon.fit();
- socket.emit('terminal-resize', {
- cols: terminal.cols,
- rows: terminal.rows
- });
+ if (socket) {
+ socket.emit('terminal-resize', {
+ cols: terminal.cols,
+ rows: terminal.rows
+ });
+ }
}
});
+
+ resizeObserver.observe(terminalElement);
}
// Browser functions
@@ -234,13 +399,14 @@ function navigateTo() {
const iframe = document.getElementById('browser-frame');
iframe.src = proxyUrl;
- // Save to history
saveToHistory(url, 'Web Page');
}
function refreshBrowser() {
const iframe = document.getElementById('browser-frame');
- iframe.src = iframe.src;
+ if (iframe.src) {
+ iframe.src = iframe.src;
+ }
}
async function saveToHistory(url, title) {
@@ -269,46 +435,52 @@ async function loadHistory() {
historyList.innerHTML = history.map(item => `
`).join('');
+
+ // Add event listeners to history links
+ historyList.querySelectorAll('.history-link').forEach(link => {
+ link.addEventListener('click', (e) => {
+ e.preventDefault();
+ loadFromHistory(link.dataset.url);
+ });
+ });
} catch (error) {
document.getElementById('history-list').innerHTML = 'Failed to load history
';
}
}
+function escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+}
+
function loadFromHistory(url) {
document.getElementById('url-input').value = url;
- // Find and click the browser button
- const browserBtn = document.querySelector('[onclick*="browser"]');
- if (browserBtn) browserBtn.click();
+ openWindow('browser');
setTimeout(() => navigateTo(), 100);
}
// App installation functions
async function installApp(appName, version) {
try {
- // Show installation in terminal
- const terminalBtn = document.querySelector('[onclick*="terminal"]');
- if (terminalBtn) terminalBtn.click();
+ openWindow('terminal');
showToast(`Installing ${appName}...`);
- // Track installation
await fetch('/api/apps/install', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ appName, appVersion: version })
});
- // Send installation command to terminal (if applicable)
if (socket && terminal) {
- // Different package managers for different apps
let installCmd = '';
if (['nodejs', 'python3', 'git', 'vim', 'curl', 'wget'].includes(appName)) {
- // For apt-based systems
installCmd = `sudo apt-get update && sudo apt-get install -y ${appName}\r`;
}
socket.emit('terminal-input', installCmd);
@@ -335,8 +507,8 @@ async function loadInstalledApps() {
appsList.innerHTML = apps.map(app => `
-
${app.app_name}
-
Version: ${app.app_version || 'latest'}
+
${escapeHtml(app.app_name)}
+
Version: ${escapeHtml(app.app_version || 'latest')}
Installed: ${new Date(app.installed_at).toLocaleString()}
@@ -364,6 +536,104 @@ async function checkAuth() {
document.addEventListener('DOMContentLoaded', () => {
checkAuth();
+ // Auth form event listeners
+ document.getElementById('login-btn').addEventListener('click', login);
+ document.getElementById('register-btn').addEventListener('click', register);
+ document.getElementById('show-register-link').addEventListener('click', (e) => {
+ e.preventDefault();
+ showRegister();
+ });
+ document.getElementById('show-login-link').addEventListener('click', (e) => {
+ e.preventDefault();
+ showLogin();
+ });
+
+ // Logout buttons
+ document.getElementById('logout-btn').addEventListener('click', logout);
+ document.getElementById('start-logout-btn').addEventListener('click', () => {
+ hideStartMenu();
+ logout();
+ });
+
+ // Start menu
+ document.getElementById('start-btn').addEventListener('click', toggleStartMenu);
+
+ // Close start menu when clicking outside
+ document.addEventListener('click', (e) => {
+ const startMenu = document.getElementById('start-menu');
+ const startBtn = document.getElementById('start-btn');
+ if (!startMenu.contains(e.target) && !startBtn.contains(e.target)) {
+ hideStartMenu();
+ }
+ });
+
+ // Desktop icons - double click to open
+ document.querySelectorAll('.desktop-icon').forEach(icon => {
+ icon.addEventListener('dblclick', () => {
+ const appName = icon.dataset.app;
+ openWindow(appName);
+ });
+ });
+
+ // Start menu items
+ document.querySelectorAll('.start-menu-item').forEach(item => {
+ item.addEventListener('click', () => {
+ const appName = item.dataset.app;
+ openWindow(appName);
+ });
+ });
+
+ // Window controls
+ document.querySelectorAll('.window-btn.close').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const windowName = btn.dataset.window;
+ closeWindow(windowName);
+ });
+ });
+
+ document.querySelectorAll('.window-btn.minimize').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const windowName = btn.dataset.window;
+ minimizeWindow(windowName);
+ });
+ });
+
+ document.querySelectorAll('.window-btn.maximize').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const windowName = btn.dataset.window;
+ maximizeWindow(windowName);
+ });
+ });
+
+ // Make windows draggable and focusable
+ document.querySelectorAll('.window').forEach(windowEl => {
+ initDraggable(windowEl);
+ windowEl.addEventListener('mousedown', () => {
+ focusWindow(windowEl.id.replace('-window', ''));
+ });
+ });
+
+ // Browser controls
+ document.getElementById('go-btn').addEventListener('click', navigateTo);
+ document.getElementById('refresh-btn').addEventListener('click', refreshBrowser);
+ document.getElementById('url-input').addEventListener('keypress', (e) => {
+ if (e.key === 'Enter') navigateTo();
+ });
+
+ // App install buttons
+ document.querySelectorAll('.install-btn').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const appName = btn.dataset.app;
+ const version = btn.dataset.version;
+ installApp(appName, version);
+ });
+ });
+
+ // Welcome widget close
+ document.getElementById('close-welcome').addEventListener('click', () => {
+ document.getElementById('welcome-widget').style.display = 'none';
+ });
+
// Add Enter key support for login/register
document.getElementById('login-password').addEventListener('keypress', (e) => {
if (e.key === 'Enter') login();
@@ -372,8 +642,4 @@ document.addEventListener('DOMContentLoaded', () => {
document.getElementById('register-password').addEventListener('keypress', (e) => {
if (e.key === 'Enter') register();
});
-
- document.getElementById('url-input').addEventListener('keypress', (e) => {
- if (e.key === 'Enter') navigateTo();
- });
});
diff --git a/public/index.html b/public/index.html
index ece6e69..5590497 100644
--- a/public/index.html
+++ b/public/index.html
@@ -18,136 +18,295 @@ CloudettteVM
Login
-
- Don't have an account? Register
+
+ Don't have an account? Register
-
+
-