/** * AlgorithmPress Desktop Integration * Wraps the PHP-WASM Builder in a desktop-like environment using NaraUI */ const DesktopIntegration = (function() { // Private state const state = { initialized: false, desktopMode: false, originalContent: null, windows: [], activeWindow: null, backgroundCanvas: null, animationController: null }; // Window management const windowManager = { createWindow: function(title, content, options = {}) { const defaults = { width: 800, height: 600, x: 50 + Math.random() * 100, y: 50 + Math.random() * 100, resizable: true, minimizable: true, maximizable: true, closable: true }; const windowOptions = { ...defaults, ...options }; // Create window container const windowEl = document.createElement('div'); windowEl.className = 'window-container opening'; windowEl.style.width = `${windowOptions.width}px`; windowEl.style.height = `${windowOptions.height}px`; windowEl.style.left = `${windowOptions.x}px`; windowEl.style.top = `${windowOptions.y}px`; windowEl.style.zIndex = 100 + state.windows.length; // Create window header const headerEl = document.createElement('div'); headerEl.className = 'window-header'; // Window title const titleEl = document.createElement('div'); titleEl.className = 'window-title'; titleEl.textContent = title; headerEl.appendChild(titleEl); // Window controls const controlsEl = document.createElement('div'); controlsEl.className = 'window-controls'; if (windowOptions.minimizable) { const minBtn = document.createElement('button'); minBtn.className = 'window-control minimize'; minBtn.innerHTML = '−'; minBtn.title = 'Minimize'; minBtn.addEventListener('click', () => this.minimizeWindow(windowEl)); controlsEl.appendChild(minBtn); } if (windowOptions.maximizable) { const maxBtn = document.createElement('button'); maxBtn.className = 'window-control maximize'; maxBtn.innerHTML = '□'; maxBtn.title = 'Maximize'; maxBtn.addEventListener('click', () => this.toggleMaximize(windowEl)); controlsEl.appendChild(maxBtn); } if (windowOptions.closable) { const closeBtn = document.createElement('button'); closeBtn.className = 'window-control close'; closeBtn.innerHTML = '×'; closeBtn.title = 'Close'; closeBtn.addEventListener('click', () => this.closeWindow(windowEl)); controlsEl.appendChild(closeBtn); } headerEl.appendChild(controlsEl); windowEl.appendChild(headerEl); // Window content const contentEl = document.createElement('div'); contentEl.className = 'window-content'; // If content is a DOM element, append it; otherwise set innerHTML if (content instanceof HTMLElement) { contentEl.appendChild(content); } else if (typeof content === 'string') { contentEl.innerHTML = content; } windowEl.appendChild(contentEl); // Window resize handle if resizable if (windowOptions.resizable) { const resizeHandle = document.createElement('div'); resizeHandle.className = 'window-resize-handle'; windowEl.appendChild(resizeHandle); // Initialize resize functionality this.initResizable(windowEl, resizeHandle); } // Add to desktop const desktop = document.getElementById('desktop-container'); desktop.appendChild(windowEl); // Register window with NaraUI if (window.NaraUI) { NaraUI.register(windowEl, { glassStrength: 1.0, reflectionStrength: 0.8, dynamicTextColor: false, priority: 10 }); NaraUI.register(headerEl, { glassStrength: 1.0, reflectionStrength: 1.0, dynamicTextColor: true, priority: 20 }); } // Make window draggable this.initDraggable(windowEl, headerEl); // Store window in state const windowData = { el: windowEl, title: title, options: windowOptions, minimized: false, maximized: false, originalDimensions: { width: windowOptions.width, height: windowOptions.height, x: windowOptions.x, y: windowOptions.y } }; state.windows.push(windowData); // Activate this window this.activateWindow(windowEl); // Remove opening class after animation completes setTimeout(() => { windowEl.classList.remove('opening'); }, 300); return windowEl; }, closeWindow: function(windowEl) { // Find window in array const index = state.windows.findIndex(w => w.el === windowEl); if (index !== -1) { // Add closing animation class windowEl.classList.add('closing'); // Wait for animation to complete setTimeout(() => { // Remove from NaraUI if (window.NaraUI) { NaraUI.unregister(windowEl); } // Remove from DOM windowEl.remove(); // Remove from state state.windows.splice(index, 1); // Activate the top-most remaining window if (state.windows.length > 0) { this.activateWindow(state.windows[state.windows.length - 1].el); } // Update taskbar taskbarManager.updateTaskbar(); }, 300); } }, minimizeWindow: function(windowEl) { const winData = state.windows.find(w => w.el === windowEl); if (!winData) return; winData.minimized = !winData.minimized; if (winData.minimized) { windowEl.classList.add('minimized'); // Activate another window if this was active if (state.activeWindow === windowEl) { const visibleWindows = state.windows.filter( w => !w.minimized && w.el !== windowEl ); if (visibleWindows.length > 0) { this.activateWindow(visibleWindows[visibleWindows.length - 1].el); } else { state.activeWindow = null; } } } else { windowEl.classList.remove('minimized'); this.activateWindow(windowEl); } // Update taskbar taskbarManager.updateTaskbar(); }, toggleMaximize: function(windowEl) { const winData = state.windows.find(w => w.el === windowEl); if (!winData) return; winData.maximized = !winData.maximized; if (winData.maximized) { // Store current dimensions before maximizing winData.originalDimensions = { width: windowEl.offsetWidth, height: windowEl.offsetHeight, x: windowEl.offsetLeft, y: windowEl.offsetTop }; windowEl.classList.add('maximized'); windowEl.style.width = '100%'; windowEl.style.height = 'calc(100% - 40px)'; // Account for taskbar windowEl.style.left = '0'; windowEl.style.top = '0'; } else { windowEl.classList.remove('maximized'); windowEl.style.width = `${winData.originalDimensions.width}px`; windowEl.style.height = `${winData.originalDimensions.height}px`; windowEl.style.left = `${winData.originalDimensions.x}px`; windowEl.style.top = `${winData.originalDimensions.y}px`; } this.activateWindow(windowEl); }, activateWindow: function(windowEl) { // Deactivate all windows state.windows.forEach(winData => { winData.el.classList.remove('active'); winData.el.style.zIndex = 100; }); // Activate the selected window const winData = state.windows.find(w => w.el === windowEl); if (winData && !winData.minimized) { windowEl.classList.add('active'); windowEl.style.zIndex = 200; state.activeWindow = windowEl; } }, initDraggable: function(windowEl, handleEl) { let offsetX, offsetY, isDragging = false; const startDrag = (e) => { // Don't drag if maximized const winData = state.windows.find(w => w.el === windowEl); if (winData && winData.maximized) return; // Activate window on drag start this.activateWindow(windowEl); // Calculate offset const rect = windowEl.getBoundingClientRect(); offsetX = e.clientX - rect.left; offsetY = e.clientY - rect.top; isDragging = true; // Prevent text selection during drag e.preventDefault(); }; const doDrag = (e) => { if (!isDragging) return; const desktopRect = document.getElementById('desktop-container').getBoundingClientRect(); const newLeft = e.clientX - desktopRect.left - offsetX; const newTop = e.clientY - desktopRect.top - offsetY; // Keep window within desktop bounds windowEl.style.left = `${Math.max(0, newLeft)}px`; windowEl.style.top = `${Math.max(0, newTop)}px`; }; const endDrag = () => { isDragging = false; }; // Add event listeners handleEl.addEventListener('mousedown', startDrag); document.addEventListener('mousemove', doDrag); document.addEventListener('mouseup', endDrag); }, initResizable: function(windowEl, handleEl) { let startWidth, startHeight, startX, startY, isResizing = false; const startResize = (e) => { // Activate window on resize start this.activateWindow(windowEl); // Get initial dimensions startWidth = windowEl.offsetWidth; startHeight = windowEl.offsetHeight; startX = e.clientX; startY = e.clientY; isResizing = true; // Prevent text selection during resize e.preventDefault(); }; const doResize = (e) => { if (!isResizing) return; // Calculate new dimensions const newWidth = startWidth + (e.clientX - startX); const newHeight = startHeight + (e.clientY - startY); // Apply new dimensions with min size limit windowEl.style.width = `${Math.max(300, newWidth)}px`; windowEl.style.height = `${Math.max(200, newHeight)}px`; }; const endResize = () => { isResizing = false; }; // Add event listeners handleEl.addEventListener('mousedown', startResize); document.addEventListener('mousemove', doResize); document.addEventListener('mouseup', endResize); } }; // Taskbar management const taskbarManager = { init: function() { // Create taskbar if it doesn't exist let taskbar = document.getElementById('desktop-taskbar'); if (!taskbar) { taskbar = document.createElement('div'); taskbar.id = 'desktop-taskbar'; // Start button const startBtn = document.createElement('button'); startBtn.id = 'start-button'; startBtn.innerHTML = ' AlgorithmPress'; startBtn.addEventListener('click', this.toggleStartMenu); taskbar.appendChild(startBtn); // Window buttons container const windowButtons = document.createElement('div'); windowButtons.id = 'taskbar-window-buttons'; taskbar.appendChild(windowButtons); // System tray const systemTray = document.createElement('div'); systemTray.id = 'system-tray'; // Toggle desktop mode button const toggleDesktopBtn = document.createElement('button'); toggleDesktopBtn.id = 'toggle-desktop-mode'; toggleDesktopBtn.innerHTML = ''; toggleDesktopBtn.title = 'Toggle Desktop Mode'; toggleDesktopBtn.addEventListener('click', () => { // Toggle between desktop and normal mode DesktopIntegration.toggleDesktopMode(); }); systemTray.appendChild(toggleDesktopBtn); // Clock const clock = document.createElement('div'); clock.id = 'taskbar-clock'; this.updateClock(clock); setInterval(() => this.updateClock(clock), 1000); systemTray.appendChild(clock); taskbar.appendChild(systemTray); // Add taskbar to desktop document.getElementById('desktop-container').appendChild(taskbar); // Register with NaraUI if (window.NaraUI) { NaraUI.register(taskbar, { glassStrength: 1.0, reflectionStrength: 0.6, dynamicTextColor: false, alwaysOn: true, priority: 15 }); } } this.updateTaskbar(); }, updateTaskbar: function() { const windowButtons = document.getElementById('taskbar-window-buttons'); if (!windowButtons) return; // Clear existing buttons windowButtons.innerHTML = ''; // Add a button for each window state.windows.forEach(winData => { const button = document.createElement('button'); button.className = 'taskbar-window-button'; if (state.activeWindow === winData.el) { button.classList.add('active'); } if (winData.minimized) { button.classList.add('minimized'); } button.textContent = winData.title; button.addEventListener('click', () => { if (winData.minimized) { windowManager.minimizeWindow(winData.el); } else if (state.activeWindow === winData.el) { windowManager.minimizeWindow(winData.el); } else { windowManager.activateWindow(winData.el); } }); windowButtons.appendChild(button); }); }, updateClock: function(clockEl) { const now = new Date(); clockEl.textContent = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); }, toggleStartMenu: function() { let startMenu = document.getElementById('start-menu'); if (startMenu) { startMenu.remove(); return; } // Create start menu startMenu = document.createElement('div'); startMenu.id = 'start-menu'; // Menu items const menuItems = [ { icon: 'fas fa-code', title: 'PHP Editor', action: () => { windowManager.createWindow('PHP Editor', document.getElementById('php-wasm-container'), { width: 1000, height: 700 }); startMenu.remove(); } }, { icon: 'fas fa-wordpress', title: 'WordPress Connector', action: () => { // Assuming there's a WordPress connector panel const wpPanel = document.createElement('div'); wpPanel.id = 'wp-connector-container'; wpPanel.innerHTML = `
Loading WordPress Connector...
`; const wpWindow = windowManager.createWindow('WordPress Connector', wpPanel, { width: 800, height: 600 }); // Initialize WordPress connector if (window.WordPressConnector && typeof window.WordPressConnector.init === 'function') { window.WordPressConnector.init(); } startMenu.remove(); } }, { icon: 'fas fa-cog', title: 'Settings', action: () => { windowManager.createWindow('Settings', '

System Settings

Settings options will appear here.

', { width: 500, height: 400 }); startMenu.remove(); } }, { icon: 'fas fa-question-circle', title: 'Help', action: () => { windowManager.createWindow('Help', '

AlgorithmPress Help

Documentation and help will appear here.

', { width: 600, height: 500 }); startMenu.remove(); } } ]; // Create menu items menuItems.forEach(item => { const menuItem = document.createElement('button'); menuItem.className = 'start-menu-item'; menuItem.innerHTML = ` ${item.title}`; menuItem.addEventListener('click', item.action); startMenu.appendChild(menuItem); }); // Add separator const separator = document.createElement('div'); separator.className = 'menu-separator'; startMenu.appendChild(separator); // Power options const powerBtn = document.createElement('button'); powerBtn.className = 'start-menu-item power-button'; powerBtn.innerHTML = ' Exit Desktop Mode'; powerBtn.addEventListener('click', () => { DesktopIntegration.toggleDesktopMode(); startMenu.remove(); }); startMenu.appendChild(powerBtn); // Position the menu const startBtn = document.getElementById('start-button'); const startBtnRect = startBtn.getBoundingClientRect(); const desktopRect = document.getElementById('desktop-container').getBoundingClientRect(); startMenu.style.left = `${startBtnRect.left - desktopRect.left}px`; startMenu.style.bottom = `${desktopRect.height - (startBtnRect.top - desktopRect.top)}px`; // Add to desktop document.getElementById('desktop-container').appendChild(startMenu); // Register with NaraUI if (window.NaraUI) { NaraUI.register(startMenu, { glassStrength: 0.95, reflectionStrength: 0.8, dynamicTextColor: true, priority: 25 }); } // Close menu when clicking outside const closeMenu = (e) => { if (!startMenu.contains(e.target) && e.target !== document.getElementById('start-button')) { startMenu.remove(); document.removeEventListener('click', closeMenu); } }; // Use setTimeout to avoid immediate closure setTimeout(() => { document.addEventListener('click', closeMenu); }, 100); } }; // Background animation const backgroundAnimator = { // Color themes for gradients colorThemes: [ // Blue-Red-Gold theme [ { x: 0, y: 0, color: '#1a2a6c' }, { x: 0.5, y: 0.5, color: '#b21f1f' }, { x: 1, y: 1, color: '#fdbb2d' } ], // Purple-Pink-Blue theme [ { x: 0, y: 0, color: '#41295a' }, { x: 0.5, y: 0.3, color: '#f25eac' }, { x: 1, y: 1, color: '#2F80ED' } ], // Dark Green-Teal-Light Green theme [ { x: 0, y: 0, color: '#134e5e' }, { x: 0.5, y: 0.5, color: '#2EB6AF' }, { x: 1, y: 1, color: '#71B280' } ], // Sunset theme [ { x: 0, y: 0, color: '#0B486B' }, { x: 0.4, y: 0.4, color: '#F56217' }, { x: 1, y: 1, color: '#FCB07E' } ], // Night sky theme [ { x: 0, y: 0, color: '#141E30' }, { x: 0.5, y: 0.5, color: '#243B55' }, { x: 1, y: 1, color: '#6593AB' } ], // Cherry Blossom theme [ { x: 0, y: 0, color: '#5E1D4C' }, { x: 0.5, y: 0.5, color: '#C33764' }, { x: 1, y: 1, color: '#FFC7C2' } ], // Aurora Borealis theme [ { x: 0, y: 0, color: '#003973' }, { x: 0.4, y: 0.5, color: '#048B9A' }, { x: 0.7, y: 0.7, color: '#13B995' }, { x: 1, y: 1, color: '#96DED1' } ], // Cyberpunk theme [ { x: 0, y: 0, color: '#09203f' }, { x: 0.5, y: 0.4, color: '#EE3E6D' }, { x: 1, y: 1, color: '#F9D978' } ], // Ocean theme [ { x: 0, y: 0, color: '#1A2980' }, { x: 0.6, y: 0.5, color: '#26D0CE' }, { x: 1, y: 1, color: '#9CECFB' } ] ], // Current animation state animState: { currentThemeIndex: 0, nextThemeIndex: 1, transitionProgress: 0, isTransitioning: false, timeUntilNextTransition: 60, // seconds until next theme change transitionDuration: 5, // seconds for transition lastTimestamp: 0 }, init: function(container) { // Create canvas for background animation const canvas = document.createElement('canvas'); canvas.id = 'desktop-background-canvas'; canvas.width = container.clientWidth; canvas.height = container.clientHeight; canvas.style.position = 'absolute'; canvas.style.top = '0'; canvas.style.left = '0'; canvas.style.width = '100%'; canvas.style.height = '100%'; canvas.style.zIndex = '0'; // Insert at beginning of container container.insertBefore(canvas, container.firstChild); // Store reference state.backgroundCanvas = canvas; // Initialize with random theme this.animState.currentThemeIndex = Math.floor(Math.random() * this.colorThemes.length); this.animState.nextThemeIndex = (this.animState.currentThemeIndex + 1) % this.colorThemes.length; // Start animation this.startAnimation(canvas); // Handle resize window.addEventListener('resize', () => { canvas.width = container.clientWidth; canvas.height = container.clientHeight; }); // Add theme cycle button to system tray this.addThemeCycleButton(); }, // Add a button to manually cycle themes addThemeCycleButton: function() { setTimeout(() => { const systemTray = document.getElementById('system-tray'); if (!systemTray) return; // Create button if it doesn't exist if (!document.getElementById('cycle-theme-button')) { const cycleButton = document.createElement('button'); cycleButton.id = 'cycle-theme-button'; cycleButton.className = 'system-tray-button'; cycleButton.innerHTML = ''; cycleButton.title = 'Cycle Background Theme'; cycleButton.addEventListener('click', () => this.cycleToNextTheme()); // Insert before clock const clock = document.getElementById('taskbar-clock'); if (clock) { systemTray.insertBefore(cycleButton, clock); } else { systemTray.appendChild(cycleButton); } } }, 1000); // Delay to ensure system tray exists }, // Manually cycle to the next theme cycleToNextTheme: function() { if (this.animState.isTransitioning) return; this.animState.isTransitioning = true; this.animState.transitionProgress = 0; this.animState.nextThemeIndex = (this.animState.currentThemeIndex + 1) % this.colorThemes.length; }, // Interpolate between two colors interpolateColor: function(color1, color2, factor) { // Parse hex colors to RGB const parseColor = (hexColor) => { const r = parseInt(hexColor.slice(1, 3), 16); const g = parseInt(hexColor.slice(3, 5), 16); const b = parseInt(hexColor.slice(5, 7), 16); return { r, g, b }; }; // Convert RGB to hex const rgbToHex = (r, g, b) => { return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); }; const c1 = parseColor(color1); const c2 = parseColor(color2); // Interpolate each channel const r = Math.round(c1.r + factor * (c2.r - c1.r)); const g = Math.round(c1.g + factor * (c2.g - c1.g)); const b = Math.round(c1.b + factor * (c2.b - c1.b)); return rgbToHex(r, g, b); }, // Get current gradient points (interpolated if transitioning) getCurrentGradientPoints: function() { if (!this.animState.isTransitioning) { return this.colorThemes[this.animState.currentThemeIndex]; } const currentTheme = this.colorThemes[this.animState.currentThemeIndex]; const nextTheme = this.colorThemes[this.animState.nextThemeIndex]; const progress = this.animState.transitionProgress; // Handle themes with different numbers of points const maxPoints = Math.max(currentTheme.length, nextTheme.length); const interpolatedPoints = []; for (let i = 0; i < maxPoints; i++) { const currentPoint = currentTheme[Math.min(i, currentTheme.length - 1)]; const nextPoint = nextTheme[Math.min(i, nextTheme.length - 1)]; // Interpolate position and color interpolatedPoints.push({ x: currentPoint.x + (nextPoint.x - currentPoint.x) * progress, y: currentPoint.y + (nextPoint.y - currentPoint.y) * progress, color: this.interpolateColor(currentPoint.color, nextPoint.color, progress) }); } return interpolatedPoints; }, startAnimation: function(canvas) { if (!canvas) return; const ctx = canvas.getContext('2d'); // Particle system const particles = []; const particleCount = 50; for (let i = 0; i < particleCount; i++) { particles.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, radius: Math.random() * 2 + 1, color: `rgba(255, 255, 255, ${Math.random() * 0.5 + 0.1})`, vx: Math.random() * 0.5 - 0.25, vy: Math.random() * 0.5 - 0.25 }); } // Animation variables let time = 0; let mouseX = 0; let mouseY = 0; // Track mouse position canvas.addEventListener('mousemove', (e) => { const rect = canvas.getBoundingClientRect(); mouseX = (e.clientX - rect.left) / canvas.width; mouseY = (e.clientY - rect.top) / canvas.height; }); // Animation function const animate = (timestamp) => { // Calculate delta time in seconds const deltaTime = (timestamp - this.animState.lastTimestamp) / 1000; this.animState.lastTimestamp = timestamp; // Update theme transition if (this.animState.isTransitioning) { // Progress the transition this.animState.transitionProgress += deltaTime / this.animState.transitionDuration; // Check if transition is complete if (this.animState.transitionProgress >= 1) { this.animState.isTransitioning = false; this.animState.transitionProgress = 0; this.animState.currentThemeIndex = this.animState.nextThemeIndex; this.animState.nextThemeIndex = (this.animState.currentThemeIndex + 1) % this.colorThemes.length; this.animState.timeUntilNextTransition = 60; // Reset timer } } else { // Count down to next automatic transition this.animState.timeUntilNextTransition -= deltaTime; if (this.animState.timeUntilNextTransition <= 0) { this.animState.isTransitioning = true; this.animState.transitionProgress = 0; } } time += 0.01; // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Get current gradient points (interpolated if transitioning) const gradientPoints = this.getCurrentGradientPoints(); // Create dynamic gradient const gradient = ctx.createRadialGradient( canvas.width * (0.5 + Math.sin(time * 0.2) * 0.2 + mouseX * 0.1), canvas.height * (0.5 + Math.cos(time * 0.3) * 0.2 + mouseY * 0.1), 0, canvas.width * 0.5, canvas.height * 0.5, Math.max(canvas.width, canvas.height) * 0.8 ); // Add color stops gradientPoints.forEach((point, index) => { const r = (index / gradientPoints.length) + Math.sin(time + index) * 0.1; gradient.addColorStop(Math.max(0, Math.min(1, r)), point.color); }); // Fill background ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw particles particles.forEach(p => { // Update position p.x += p.vx; p.y += p.vy; // Wrap around if (p.x < 0) p.x = canvas.width; if (p.x > canvas.width) p.x = 0; if (p.y < 0) p.y = canvas.height; if (p.y > canvas.height) p.y = 0; // Draw particle ctx.beginPath(); ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); ctx.fillStyle = p.color; ctx.fill(); }); // Draw blurred overlay ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'; ctx.filter = 'blur(40px)'; for (let i = 0; i < 3; i++) { const x = (Math.sin(time * 0.5 + i) * 0.5 + 0.5) * canvas.width; const y = (Math.cos(time * 0.7 + i) * 0.5 + 0.5) * canvas.height; const radius = 100 + Math.sin(time + i) * 50; ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fill(); } ctx.filter = 'none'; // Continue animation state.animationController = requestAnimationFrame(animate); }; // Start animation this.animState.lastTimestamp = performance.now(); state.animationController = requestAnimationFrame(animate); }, stopAnimation: function() { if (state.animationController) { cancelAnimationFrame(state.animationController); state.animationController = null; } } }; // CSS styles for desktop environment const injectStyles = function() { const styleEl = document.createElement('style'); styleEl.id = 'desktop-integration-styles'; styleEl.textContent = ` #desktop-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #111; overflow: hidden; font-family: 'Segoe UI', 'Arial', sans-serif; color: #fff; z-index: 99999; } /* Window styles */ .window-container { position: absolute; background-color: rgba(30, 30, 30, 0.7); border-radius: 8px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); overflow: hidden; transition: box-shadow 0.3s ease; min-width: 300px; min-height: 200px; } .window-container.active { box-shadow: 0 12px 48px rgba(0, 0, 0, 0.4); } .window-container.minimized { display: none; } .window-container.maximized { border-radius: 0; } .window-header { display: flex; justify-content: space-between; align-items: center; padding: 6px 12px; background-color: rgba(50, 50, 50, 0.7); cursor: move; height: 32px; } .window-title { font-size: 14px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 10px; } .window-controls { display: flex; gap: 6px; } .window-control { background: none; border: none; width: 24px; height: 24px; border-radius: 12px; color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 16px; transition: background-color 0.2s; } .window-control:hover { background-color: rgba(255, 255, 255, 0.1); } .window-control.close:hover { background-color: rgba(232, 17, 35, 0.9); } .window-content { height: calc(100% - 32px); overflow: auto; position: relative; } .window-resize-handle { position: absolute; bottom: 0; right: 0; width: 16px; height: 16px; cursor: nwse-resize; } .window-resize-handle::after { content: ''; position: absolute; right: 3px; bottom: 3px; width: 8px; height: 8px; border-right: 2px solid rgba(255, 255, 255, 0.5); border-bottom: 2px solid rgba(255, 255, 255, 0.5); } /* Taskbar styles */ #desktop-taskbar { position: absolute; bottom: 0; left: 0; width: 100%; height: 40px; background-color: rgba(30, 30, 30, 0.7); display: flex; align-items: center; padding: 0 8px; z-index: 1000; } #start-button { background: none; border: none; height: 32px; padding: 0 12px; border-radius: 4px; color: #fff; font-weight: 500; display: flex; align-items: center; gap: 8px; cursor: pointer; transition: background-color 0.2s; } #start-button:hover { background-color: rgba(255, 255, 255, 0.1); } #taskbar-window-buttons { display: flex; gap: 4px; margin-left: 8px; overflow-x: auto; flex: 1; } .taskbar-window-button { background: rgba(60, 60, 60, 0.5); border: none; height: 32px; padding: 0 12px; border-radius: 4px; color: #fff; font-size: 13px; display: flex; align-items: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; max-width: 200px; transition: background-color 0.2s; } .taskbar-window-button:hover { background-color: rgba(80, 80, 80, 0.5); } .taskbar-window-button.active { background-color: rgba(100, 100, 100, 0.8); box-shadow: inset 0 -2px 0 rgba(255, 255, 255, 0.5); } #system-tray { display: flex; align-items: center; gap: 8px; margin-left: auto; } #toggle-desktop-mode { background: none; border: none; height: 32px; width: 32px; border-radius: 4px; color: #fff; cursor: pointer; transition: background-color 0.2s; } #toggle-desktop-mode:hover { background-color: rgba(255, 255, 255, 0.1); } #taskbar-clock { font-size: 13px; padding: 0 8px; } /* Start menu */ #start-menu { position: absolute; bottom: 40px; width: 280px; background-color: rgba(30, 30, 30, 0.9); border-radius: 8px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); display: flex; flex-direction: column; overflow: hidden; z-index: 1100; } .start-menu-item { background: none; border: none; height: 40px; padding: 0 16px; display: flex; align-items: center; gap: 12px; color: #fff; text-align: left; cursor: pointer; transition: background-color 0.2s; } .start-menu-item:hover { background-color: rgba(255, 255, 255, 0.1); } .start-menu-item i { width: 16px; text-align: center; } .menu-separator { height: 1px; background-color: rgba(255, 255, 255, 0.1); margin: 4px 0; } .power-button { color: #e81123; } /* Loading spinner */ .loading-spinner { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; gap: 12px; color: rgba(255, 255, 255, 0.7); } .loading-spinner i { font-size: 24px; } /* Desktop button */ #desktop-toggle-button { position: fixed; top: 10px; right: 10px; z-index: 9998; background: rgba(0, 0, 0, 0.6); color: white; border: none; border-radius: 4px; padding: 6px 12px; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 6px; transition: background-color 0.2s; } #desktop-toggle-button:hover { background: rgba(0, 0, 0, 0.8); } /* Ensure FontAwesome icons are loaded */ .fa, .fas, .far, .fab { font-family: 'Font Awesome 5 Free', 'Font Awesome 5 Brands'; font-weight: 900; } `; document.head.appendChild(styleEl); }; // Add FontAwesome if not already present const loadFontAwesome = function() { if (!document.querySelector('link[href*="font-awesome"]')) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css'; document.head.appendChild(link); } }; // Initialize desktop environment const initDesktop = function() { // Create desktop container const desktopContainer = document.createElement('div'); desktopContainer.id = 'desktop-container'; document.body.appendChild(desktopContainer); // Initialize background animation backgroundAnimator.init(desktopContainer); // Initialize taskbar taskbarManager.init(); // Store original content if (!state.originalContent) { const mainContent = document.getElementById('php-wasm-container'); if (mainContent) { state.originalContent = mainContent.parentNode.innerHTML; } } // Create main window with PHP-WASM content if (document.getElementById('php-wasm-container')) { // Move the PHP-WASM container to desktop windowManager.createWindow('PHP Editor', document.getElementById('php-wasm-container'), { width: 1000, height: 700, x: (window.innerWidth - 1000) / 2, y: (window.innerHeight - 700) / 2 }); } else { // Create a placeholder window windowManager.createWindow('Welcome', `

Welcome to AlgorithmPress Desktop

The PHP-WASM container is not available.

`, { width: 600, height: 400, x: (window.innerWidth - 600) / 2, y: (window.innerHeight - 400) / 2 }); } // Initialize NaraUI if available if (window.NaraUI) { NaraUI.init({ intensity: 0.8, reflectionFrequency: 0.4, glassOpacity: 0.7, blurStrength: 12, glowStrength: 6, accentHue: 210 }); } state.desktopMode = true; }; // Exit desktop mode const exitDesktop = function() { // Stop background animation backgroundAnimator.stopAnimation(); // Restore original content if available if (state.originalContent) { document.body.innerHTML = state.originalContent; } // Clean up color synchronizer if (builderColorSynchronizer && typeof builderColorSynchronizer.destroy === 'function') { builderColorSynchronizer.destroy(); } // Remove desktop container const desktopContainer = document.getElementById('desktop-container'); if (desktopContainer) { desktopContainer.remove(); } // Add toggle button back addDesktopToggleButton(); // Dispatch event for other modules to know desktop mode is deactivated const event = new CustomEvent('desktop-mode-deactivated'); document.dispatchEvent(event); state.desktopMode = false; }; // Builder element color synchronizer const builderColorSynchronizer = { // State for tracking color updates state: { colorUpdateInterval: null, lastDominantColors: [], targetElements: [], colorUpdateFrequency: 500, // ms contrastThreshold: 4.5, // WCAG AA standard textElements: [] // Elements that need high contrast }, // Initialize color synchronization init: function() { // Clear any existing interval if (this.state.colorUpdateInterval) { clearInterval(this.state.colorUpdateInterval); } // Find builder elements that need to be synchronized this.findBuilderElements(); // Apply initial styles if possible this.applyDynamicStyles(); // Set up regular color sampling and style updates this.state.colorUpdateInterval = setInterval(() => { this.updateElementColors(); }, this.state.colorUpdateFrequency); // Listen for background theme changes document.addEventListener('background-theme-changed', () => { // Force immediate update when theme changes setTimeout(() => this.updateElementColors(), 100); }); console.log('Builder color synchronizer initialized'); }, // Find all builder elements that need color adaptation findBuilderElements: function() { const mainContainer = document.getElementById('php-wasm-container'); if (!mainContainer) return; // Reset target elements array this.state.targetElements = []; this.state.textElements = []; // Find all main container elements in the builder // These selectors should match your builder's main UI components const builderSelectors = [ '.php-editor', '.editor-panel', '.code-container', '.output-container', '.control-panel', '.builder-sidebar', '.tool-panel', '.component-preview', '.property-editor', '.builder-header', '.builder-footer', '.module-container', '.wp-connector-panel', '.code-window', '.config-panel', '.component-browser', '.preview-frame', '.toolbar-container', '.panel-header', '.panel-footer', '.file-browser', '.component-library', '.layout-editor', '.option-panel' ]; // Text elements that need high contrast const textSelectors = [ '.editor-label', '.option-label', '.component-name', '.file-name', '.property-name', '.menu-item-text', '.status-text', '.console-output', '.property-value', '.panel-title', 'textarea', 'input[type="text"]', 'select', '.component-description', '.help-text', '.tooltip-content', '.error-message', '.success-message', '.info-message' ]; // Find all matching elements builderSelectors.forEach(selector => { try { const elements = mainContainer.querySelectorAll(selector); if (elements && elements.length > 0) { elements.forEach(el => this.state.targetElements.push(el)); } } catch (e) { console.warn('Error finding builder elements:', e); } }); // Find text elements textSelectors.forEach(selector => { try { const elements = mainContainer.querySelectorAll(selector); if (elements && elements.length > 0) { elements.forEach(el => this.state.textElements.push(el)); } } catch (e) { console.warn('Error finding text elements:', e); } }); // Also handle the window content containing the PHP builder const builderWindow = document.querySelector('.window-content'); if (builderWindow) { this.state.targetElements.push(builderWindow); } console.log(`Found ${this.state.targetElements.length} builder elements to synchronize`); }, // Sample the current background colors sampleBackgroundColors: function() { const canvas = state.backgroundCanvas; if (!canvas) return null; const ctx = canvas.getContext('2d'); const width = canvas.width; const height = canvas.height; // Sample points in a grid pattern const samplePoints = [ {x: width * 0.25, y: height * 0.25}, {x: width * 0.75, y: height * 0.25}, {x: width * 0.5, y: height * 0.5}, {x: width * 0.25, y: height * 0.75}, {x: width * 0.75, y: height * 0.75} ]; const colors = []; // Sample colors from each point samplePoints.forEach(point => { try { const pixel = ctx.getImageData(point.x, point.y, 1, 1).data; colors.push({ r: pixel[0], g: pixel[1], b: pixel[2], a: pixel[3] / 255 }); } catch (e) { console.warn('Error sampling background color:', e); } }); return colors; }, // Calculate brightness of a color (0-1) calculateBrightness: function(r, g, b) { return (0.299 * r + 0.587 * g + 0.114 * b) / 255; }, // Calculate contrast ratio between two colors calculateContrast: function(color1, color2) { const luminance1 = this.calculateBrightness(color1.r, color1.g, color1.b); const luminance2 = this.calculateBrightness(color2.r, color2.g, color2.b); const lighter = Math.max(luminance1, luminance2); const darker = Math.min(luminance1, luminance2); return (lighter + 0.05) / (darker + 0.05); }, // Get appropriate text color for a background getTextColor: function(bgColor) { const brightness = this.calculateBrightness(bgColor.r, bgColor.g, bgColor.b); // If background is dark, use white text if (brightness < 0.5) { return {r: 255, g: 255, b: 255}; } else { return {r: 0, g: 0, b: 0}; } }, // Create a complementary color getComplementaryColor: function(color) { return { r: 255 - color.r, g: 255 - color.g, b: 255 - color.b, a: color.a }; }, // Create a derived color with adjustments deriveColor: function(baseColor, options = {}) { const { lighten = 0, darken = 0, alpha = null, saturation = 0 } = options; // Convert RGB to HSL let r = baseColor.r / 255; let g = baseColor.g / 255; let b = baseColor.b / 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let h, s, l = (max + min) / 2; if (max === min) { h = s = 0; } else { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } // Apply adjustments l = Math.max(0, Math.min(1, l + lighten - darken)); s = Math.max(0, Math.min(1, s + saturation)); // Convert back to RGB let r1, g1, b1; if (s === 0) { r1 = g1 = b1 = l; } else { const hue2rgb = (p, q, t) => { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1/6) return p + (q - p) * 6 * t; if (t < 1/2) return q; if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; return p; }; const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; r1 = hue2rgb(p, q, h + 1/3); g1 = hue2rgb(p, q, h); b1 = hue2rgb(p, q, h - 1/3); } // Return new color return { r: Math.round(r1 * 255), g: Math.round(g1 * 255), b: Math.round(b1 * 255), a: alpha !== null ? alpha : (baseColor.a || 1) }; }, // Convert color object to CSS rgba string colorToCss: function(color) { return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a || 1})`; }, // Update element colors based on background updateElementColors: function() { // Sample background colors const colors = this.sampleBackgroundColors(); if (!colors || colors.length === 0) return; // Calculate average color let avgR = 0, avgG = 0, avgB = 0; colors.forEach(color => { avgR += color.r; avgG += color.g; avgB += color.b; }); const avgColor = { r: Math.round(avgR / colors.length), g: Math.round(avgG / colors.length), b: Math.round(avgB / colors.length), a: 0.85 }; // Store for comparison this.state.lastDominantColors = colors; // Apply styles this.applyDynamicStyles(avgColor, colors); // Dispatch event for other components to react const event = new CustomEvent('builder-colors-updated', { detail: { dominantColor: avgColor, sampledColors: colors } }); document.dispatchEvent(event); }, // Apply dynamic styles to builder elements applyDynamicStyles: function(avgColor, colors) { if (!avgColor && this.state.lastDominantColors.length > 0) { // Use last colors if not provided colors = this.state.lastDominantColors; avgColor = { r: Math.round(colors.reduce((sum, c) => sum + c.r, 0) / colors.length), g: Math.round(colors.reduce((sum, c) => sum + c.g, 0) / colors.length), b: Math.round(colors.reduce((sum, c) => sum + c.b, 0) / colors.length), a: 0.85 }; } if (!avgColor) return; // Get text color based on background const textColor = this.getTextColor(avgColor); const isDarkBackground = this.calculateBrightness(avgColor.r, avgColor.g, avgColor.b) < 0.5; // Calculate base panel color (slightly lighter/darker than background) const panelBaseColor = this.deriveColor(avgColor, { lighten: isDarkBackground ? 0.1 : 0, darken: isDarkBackground ? 0 : 0.1, alpha: 0.7 }); // Calculate accent color const accentColor = colors.length >= 2 ? this.deriveColor(colors[1], { saturation: 0.2, alpha: 0.9 }) : this.deriveColor(avgColor, { saturation: 0.3, alpha: 0.9 }); // Apply styles to target elements this.state.targetElements.forEach((element, index) => { // Vary the transparency and color slightly for each element // to create visual interest and depth const adjustmentFactor = (index % 5) * 0.03; // Create derived colors for this specific element const elementBaseColor = this.deriveColor(panelBaseColor, { lighten: isDarkBackground ? adjustmentFactor : 0, darken: isDarkBackground ? 0 : adjustmentFactor, alpha: 0.7 - adjustmentFactor }); // Apply styles based on element type element.style.backgroundColor = this.colorToCss(elementBaseColor); // Add a subtle border for definition if it doesn't have one if (getComputedStyle(element).borderWidth === '0px') { const borderColor = this.deriveColor(elementBaseColor, { lighten: isDarkBackground ? 0.1 : 0, darken: isDarkBackground ? 0 : 0.1, alpha: 0.3 }); element.style.borderBottom = `1px solid ${this.colorToCss(borderColor)}`; } // Add box shadow for depth const shadowColor = isDarkBackground ? 'rgba(0, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0.15)'; element.style.boxShadow = `0 2px 8px ${shadowColor}`; // Add subtle backdrop filter if supported if ('backdropFilter' in document.documentElement.style) { element.style.backdropFilter = 'blur(8px)'; } else if ('webkitBackdropFilter' in document.documentElement.style) { element.style.webkitBackdropFilter = 'blur(8px)'; } // Update border radius for consistent styling if (getComputedStyle(element).borderRadius === '0px') { element.style.borderRadius = '6px'; } }); // Update text elements to ensure readability this.state.textElements.forEach(element => { // Get background color of this element or its parent const bgStyles = window.getComputedStyle(element); let bgColor = {r: avgColor.r, g: avgColor.g, b: avgColor.b}; if (bgStyles.backgroundColor !== 'rgba(0, 0, 0, 0)' && bgStyles.backgroundColor !== 'transparent') { // Extract RGB from background color const match = bgStyles.backgroundColor.match(/rgba?\((\d+), (\d+), (\d+)/); if (match) { bgColor = { r: parseInt(match[1]), g: parseInt(match[2]), b: parseInt(match[3]) }; } } // Determine text color based on background const elementTextColor = this.getTextColor(bgColor); // Apply high-contrast text color element.style.color = this.colorToCss(elementTextColor); // Add text shadow for better readability if text is light on dark if (elementTextColor.r > 200) { element.style.textShadow = '0 1px 2px rgba(0, 0, 0, 0.5)'; } else { element.style.textShadow = 'none'; } }); // Set CSS variables for consistent styling const rootElement = document.documentElement; rootElement.style.setProperty('--builder-bg-color', this.colorToCss(avgColor)); rootElement.style.setProperty('--builder-panel-color', this.colorToCss(panelBaseColor)); rootElement.style.setProperty('--builder-accent-color', this.colorToCss(accentColor)); rootElement.style.setProperty('--builder-text-color', this.colorToCss(textColor)); }, // Clean up when no longer needed destroy: function() { if (this.state.colorUpdateInterval) { clearInterval(this.state.colorUpdateInterval); this.state.colorUpdateInterval = null; } } }; // Add desktop toggle button to main UI const addDesktopToggleButton = function() { // Remove existing button if any const existingBtn = document.getElementById('desktop-toggle-button'); if (existingBtn) existingBtn.remove(); // Create new button const toggleButton = document.createElement('button'); toggleButton.id = 'desktop-toggle-button'; toggleButton.innerHTML = ' Desktop Mode'; toggleButton.addEventListener('click', () => { DesktopIntegration.toggleDesktopMode(); }); document.body.appendChild(toggleButton); }; // Public API return { // Initialize the desktop integration init: function() { if (state.initialized) return this; // Load Font Awesome loadFontAwesome(); // Inject CSS styles injectStyles(); // Add desktop toggle button addDesktopToggleButton(); state.initialized = true; return this; }, // Toggle between desktop and normal modes toggleDesktopMode: function() { if (state.desktopMode) { exitDesktop(); } else { initDesktop(); } return this; }, // Get current state getState: function() { return { initialized: state.initialized, desktopMode: state.desktopMode, windowCount: state.windows.length }; }, // Create a new window createWindow: function(title, content, options) { if (!state.desktopMode) { this.toggleDesktopMode(); } return windowManager.createWindow(title, content, options); }, // Cycle to the next background theme cycleBackgroundTheme: function() { if (state.desktopMode && backgroundAnimator) { backgroundAnimator.cycleToNextTheme(); } return this; }, // Set a specific background theme by index setBackgroundTheme: function(themeIndex) { if (!state.desktopMode || !backgroundAnimator) return this; if (themeIndex >= 0 && themeIndex < backgroundAnimator.colorThemes.length) { backgroundAnimator.animState.isTransitioning = true; backgroundAnimator.animState.transitionProgress = 0; backgroundAnimator.animState.nextThemeIndex = themeIndex; } return this; }, // Access to window manager for external use windowManager: windowManager }; })(); // Auto-initialize when DOM is ready document.addEventListener('DOMContentLoaded', function() { DesktopIntegration.init(); }); // Export for global access window.DesktopIntegration = DesktopIntegration;