frontend/app/app.vue

335 lines
11 KiB
Vue

<script setup>
import Taskbar from '~/components/Taskbar.vue';
import AboutMeWindow from '~/components/AboutMeWindow.vue';
import TerminalWindow from '~/components/TerminalWindow.vue';
import WinampWindow from '~/components/WinampWindow.vue';
import { ref, reactive, onMounted, computed, provide, onUnmounted } from 'vue';
// --- Data Definitions ---
// Reactive array to hold all window states
const windows = reactive([]);
// Keep track of the highest z-index
const currentZIndex = ref(100);
// Desktop icons container position
const iconsContainerX = ref(0);
const iconsContainerY = ref(0);
// Dragging state for desktop icons container
const isIconsContainerDragging = ref(false);
const iconsContainerStartX = ref(0);
const iconsContainerStartY = ref(0);
const initialIconsContainerX = ref(0);
const initialIconsContainerY = ref(0);
// Define Start Menu Items with icons and actions
const startMenuItems = ref([
{
labelKey: 'terminal',
icon: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik0yMiAxNlY4QzIyIDYuODk1NDMgMjEuMTA0NiA2IDIwIDZIMTRMMTIgNEg0QzIuODk1NDMgNCAyIDQuODk1NDMgMiA2VjE4QzIgMTkuMTA0NiAyLjg5NTQzIDIwIDQgMjBIMjBDMjEuMTA0NiAyMCAyMiAxOS4xMDQ2IDIyIDE4VjE2WiIgZmlsbD0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0iTTggMTJMMTAgMTQuNUwxMiAxMkwxMCA5LjVMOCAxMloiIGZpbGw9IndoaXRlIi8+CiAgICA8cGF0aCBkPSJNMTAgMTZIMTRWMThIMTBWMTZaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4=',
action: 'openTerminal',
},
{
labelKey: 'about_me',
icon: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBmaWxsPSIjRkZGRjAwIi8+CiAgICA8cGF0aCBkPSJNMTEgN0gxM1Y5SDExVjdaTTExIDExSDEzVjE3SDExVjExWiIgZmlsbD0iYmxhY2siLz4KPC9zdmc+Cg==',
action: 'openAboutMe',
},
{
labelKey: 'close_all_windows',
icon: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik0xOC41IDUuNUw1LjUgMTguNU01LjUgNS41TDE4LjUgMTguNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIyIi8+Cjwvc3ZnPgo=',
action: 'closeAllWindows',
},
]);
// --- Window Management Functions ---
const generateUniqueId = () => Date.now().toString(36) + Math.random().toString(36).substr(2);
const saveWindowsToLocalStorage = () => localStorage.setItem('windows', JSON.stringify(windows));
const openWindow = (type, titleKey, initialWidth = 600, initialHeight = 400) => {
const existingWindow = windows.find(w => w.type === type);
if (existingWindow) {
bringWindowToFront(existingWindow.id);
if (existingWindow.isMinimized) restoreWindow(existingWindow.id);
existingWindow.isVisible = true;
} else {
currentZIndex.value++;
const desktopWidth = window.innerWidth;
const desktopHeight = window.innerHeight - 40;
const initialX = (desktopWidth - initialWidth) / 2;
const initialY = (desktopHeight - initialHeight) / 2 + 40;
windows.push({
id: generateUniqueId(),
type, title: titleKey, isVisible: true, isMinimized: false,
x: initialX, y: initialY, width: initialWidth, height: initialHeight,
zIndex: currentZIndex.value,
});
}
saveWindowsToLocalStorage();
};
const closeWindow = (id) => {
const index = windows.findIndex(w => w.id === id);
if (index !== -1) {
windows.splice(index, 1);
saveWindowsToLocalStorage();
}
};
const closeAllWindows = () => {
windows.length = 0;
saveWindowsToLocalStorage();
};
const minimizeWindow = (id) => {
const win = windows.find(w => w.id === id);
if (win) {
console.log(`Minimizing window ${id}. Before: isMinimized=${win.isMinimized}`);
win.isMinimized = true;
console.log(`Minimizing window ${id}. After: isMinimized=${win.isMinimized}`);
saveWindowsToLocalStorage();
}
};
const restoreWindow = (id) => {
const win = windows.find(w => w.id === id);
if (win) {
console.log(`Restoring window ${id}. Before: isMinimized=${win.isMinimized}`);
win.isMinimized = false;
console.log(`Restoring window ${id}. After: isMinimized=${win.isMinimized}`);
bringWindowToFront(id);
saveWindowsToLocalStorage();
}
};
const bringWindowToFront = (id) => {
const win = windows.find(w => w.id === id);
if (win) {
currentZIndex.value++;
win.zIndex = currentZIndex.value;
console.log(`Bringing window ${id} to front. New zIndex: ${win.zIndex}`);
saveWindowsToLocalStorage();
}
};
const updateWindowPosition = (id, { x, y }) => {
const win = windows.find(w => w.id === id);
if (win) {
win.x = x;
win.y = y;
saveWindowsToLocalStorage();
}
};
const updateWindowDimensions = (id, { width, height }) => {
const win = windows.find(w => w.id === id);
if (win) {
win.width = width;
win.height = height;
saveWindowsToLocalStorage();
}
};
// --- Action Handlers ---
const openAboutMeWindow = () => openWindow('about-me', 'about_me');
const openTerminalWindow = () => openWindow('terminal', 'terminal');
const openWinampWindow = () => {
console.log('Opening Winamp window via provide/inject.');
openWindow('winamp', 'Winamp', 400, 200);
};
// Provide the openWinampWindow function
provide('openWinamp', openWinampWindow);
const menuActions = {
openTerminal: openTerminalWindow,
openAboutMe: openAboutMeWindow,
closeAllWindows: closeAllWindows,
};
const handleMenuAction = (action) => {
const func = menuActions[action];
if (func) func();
};
// --- Desktop Icons Container Dragging Functions ---
const handleIconsContainerMouseDown = (e) => {
// Only drag if not clicking on a window or taskbar
if (e.target.closest('.window-wrapper') || e.target.closest('.taskbar')) {
console.log('Clicked on a window or taskbar, not dragging desktop icons container.');
return;
}
isIconsContainerDragging.value = true;
iconsContainerStartX.value = e.clientX;
iconsContainerStartY.value = e.clientY;
initialIconsContainerX.value = iconsContainerX.value;
initialIconsContainerY.value = iconsContainerY.value;
console.log(`Icons container drag started. Initial pos: (${initialIconsContainerX.value}, ${initialIconsContainerY.value})`);
};
const handleIconsContainerMouseMove = (e) => {
if (!isIconsContainerDragging.value) return;
const dx = e.clientX - iconsContainerStartX.value;
const dy = e.clientY - iconsContainerStartY.value;
let newX = initialIconsContainerX.value + dx;
let newY = initialIconsContainerY.value + dy;
// Get desktop content dimensions
const desktopContent = document.querySelector('.desktop-content');
const desktopWidth = desktopContent.offsetWidth;
const desktopHeight = desktopContent.offsetHeight;
// Get NuxtPage (icons container) dimensions
const nuxtPage = document.querySelector('.desktop-icons-container');
const nuxtPageWidth = nuxtPage.offsetWidth;
const nuxtPageHeight = nuxtPage.offsetHeight;
// Define boundaries
const minX = -(nuxtPageWidth - desktopWidth);
const minY = -(nuxtPageHeight - desktopHeight);
const maxX = 0;
const maxY = 0;
// Clamp newX and newY within boundaries
iconsContainerX.value = Math.max(minX, Math.min(maxX, newX));
iconsContainerY.value = Math.max(minY, Math.min(maxY, newY));
console.log(`Icons container dragging. Current pos: (${iconsContainerX.value}, ${iconsContainerY.value})`);
};
const handleIconsContainerMouseUp = () => {
if (isIconsContainerDragging.value) {
isIconsContainerDragging.value = false;
console.log(`Icons container drag ended. Final pos: (${iconsContainerX.value}, ${iconsContainerY.value})`);
}
};
// --- Lifecycle Hooks ---
onMounted(() => {
const savedWindows = localStorage.getItem('windows');
if (savedWindows) {
const parsedWindows = JSON.parse(savedWindows);
parsedWindows.forEach(win => {
if (win.zIndex > currentZIndex.value) currentZIndex.value = win.zIndex;
windows.push(win);
});
}
window.addEventListener('mousemove', handleIconsContainerMouseMove);
window.addEventListener('mouseup', handleIconsContainerMouseUp);
});
onUnmounted(() => {
window.removeEventListener('mousemove', handleIconsContainerMouseMove);
window.removeEventListener('mouseup', handleIconsContainerMouseUp);
});
const minimizedWindows = computed(() => windows.filter(win => win.isMinimized));
</script>
<template>
<div>
<Taskbar
:menu-items="startMenuItems"
:minimized-windows="minimizedWindows"
@menu-action="handleMenuAction"
@restore-window="restoreWindow"
/>
<div class="desktop-content">
<div
class="desktop-icons-container"
:style="{ left: `${iconsContainerX}px`, top: `${iconsContainerY}px`, cursor: isIconsContainerDragging ? 'grabbing' : 'grab' }"
@mousedown="handleIconsContainerMouseDown"
>
<NuxtPage />
</div>
</div>
<template v-for="window in windows" :key="window.id">
<AboutMeWindow
v-if="window.type === 'about-me' && window.isVisible && !window.isMinimized"
:window-data="window"
@close="closeWindow(window.id)"
@minimize="minimizeWindow(window.id)"
@bring-to-front="bringWindowToFront(window.id)"
@update-position="updateWindowPosition(window.id, $event)"
@update-dimensions="updateWindowDimensions(window.id, $event)"
/>
<TerminalWindow
v-if="window.type === 'terminal' && window.isVisible && !window.isMinimized"
:window-data="window"
@close="closeWindow(window.id)"
@minimize="minimizeWindow(window.id)"
@bring-to-front="bringWindowToFront(window.id)"
@update-position="updateWindowPosition(window.id, $event)"
@update-dimensions="updateWindowDimensions(window.id, $event)"
/>
<WinampWindow
v-if="window.type === 'winamp' && window.isVisible && !window.isMinimized"
:window-data="window"
@close="closeWindow(window.id)"
@minimize="minimizeWindow(window.id)"
@bring-to-front="bringWindowToFront(window.id)"
@update-position="updateWindowPosition(window.id, $event)"
@update-dimensions="updateWindowDimensions(window.id, $event)"
/>
</template>
</div>
</template>
<style>
html {
background-color: #008080;
background-image: url('https://picsum.photos/1920/1080');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh; /* Use vh for full viewport height */
}
html, body {
margin: 0;
padding: 0;
}
body {
height: 100vh; /* Use vh for full viewport height */
font-family: 'Courier New', Courier, monospace;
}
.taskbar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 40px;
z-index: 1000;
}
.desktop-content {
position: absolute;
top: 40px;
left: 0;
width: 100%;
height: calc(100vh - 40px); /* Changed to 100vh - 40px */
box-sizing: border-box;
background-color: transparent;
border: none;
box-shadow: none;
overflow: hidden; /* Prevent scrollbars on the desktop content itself */
}
.desktop-icons-container {
position: relative; /* Allow positioning of children (NuxtPage) */
width: 100%;
height: 100%;
cursor: grab;
}
</style>