2025-09-10 23:43:41 +00:00
|
|
|
<script setup>
|
|
|
|
import Taskbar from '~/components/Taskbar.vue';
|
|
|
|
import AboutMeWindow from '~/components/AboutMeWindow.vue';
|
2025-09-11 14:22:41 +00:00
|
|
|
import TerminalWindow from '~/components/TerminalWindow.vue';
|
2025-09-11 16:50:28 +00:00
|
|
|
import WinampWindow from '~/components/WinampWindow.vue';
|
|
|
|
import { ref, reactive, onMounted, computed, provide, onUnmounted } from 'vue';
|
2025-09-11 11:35:49 +00:00
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
// --- Data Definitions ---
|
2025-09-10 23:43:41 +00:00
|
|
|
|
|
|
|
// Reactive array to hold all window states
|
|
|
|
const windows = reactive([]);
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
// Keep track of the highest z-index
|
2025-09-10 23:43:41 +00:00
|
|
|
const currentZIndex = ref(100);
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
// Desktop icons container position
|
|
|
|
const iconsContainerX = ref(0);
|
|
|
|
const iconsContainerY = ref(0);
|
2025-09-10 23:43:41 +00:00
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
// 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);
|
2025-09-10 23:43:41 +00:00
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
// 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));
|
2025-09-10 23:43:41 +00:00
|
|
|
|
2025-09-11 14:22:41 +00:00
|
|
|
const openWindow = (type, titleKey, initialWidth = 600, initialHeight = 400) => {
|
2025-09-10 23:43:41 +00:00
|
|
|
const existingWindow = windows.find(w => w.type === type);
|
|
|
|
if (existingWindow) {
|
|
|
|
bringWindowToFront(existingWindow.id);
|
2025-09-11 16:50:28 +00:00
|
|
|
if (existingWindow.isMinimized) restoreWindow(existingWindow.id);
|
|
|
|
existingWindow.isVisible = true;
|
2025-09-10 23:43:41 +00:00
|
|
|
} else {
|
|
|
|
currentZIndex.value++;
|
|
|
|
const desktopWidth = window.innerWidth;
|
2025-09-11 16:50:28 +00:00
|
|
|
const desktopHeight = window.innerHeight - 40;
|
2025-09-10 23:43:41 +00:00
|
|
|
const initialX = (desktopWidth - initialWidth) / 2;
|
2025-09-11 16:50:28 +00:00
|
|
|
const initialY = (desktopHeight - initialHeight) / 2 + 40;
|
2025-09-10 23:43:41 +00:00
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
windows.push({
|
2025-09-10 23:43:41 +00:00
|
|
|
id: generateUniqueId(),
|
2025-09-11 16:50:28 +00:00
|
|
|
type, title: titleKey, isVisible: true, isMinimized: false,
|
|
|
|
x: initialX, y: initialY, width: initialWidth, height: initialHeight,
|
2025-09-10 23:43:41 +00:00
|
|
|
zIndex: currentZIndex.value,
|
2025-09-11 16:50:28 +00:00
|
|
|
});
|
2025-09-10 23:43:41 +00:00
|
|
|
}
|
2025-09-11 16:50:28 +00:00
|
|
|
saveWindowsToLocalStorage();
|
2025-09-10 23:43:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const closeWindow = (id) => {
|
|
|
|
const index = windows.findIndex(w => w.id === id);
|
|
|
|
if (index !== -1) {
|
|
|
|
windows.splice(index, 1);
|
|
|
|
saveWindowsToLocalStorage();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const closeAllWindows = () => {
|
2025-09-11 16:50:28 +00:00
|
|
|
windows.length = 0;
|
2025-09-10 23:43:41 +00:00
|
|
|
saveWindowsToLocalStorage();
|
|
|
|
};
|
|
|
|
|
|
|
|
const minimizeWindow = (id) => {
|
2025-09-11 16:50:28 +00:00
|
|
|
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}`);
|
2025-09-10 23:43:41 +00:00
|
|
|
saveWindowsToLocalStorage();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const restoreWindow = (id) => {
|
2025-09-11 16:50:28 +00:00
|
|
|
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}`);
|
2025-09-10 23:43:41 +00:00
|
|
|
bringWindowToFront(id);
|
|
|
|
saveWindowsToLocalStorage();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const bringWindowToFront = (id) => {
|
2025-09-11 16:50:28 +00:00
|
|
|
const win = windows.find(w => w.id === id);
|
|
|
|
if (win) {
|
2025-09-10 23:43:41 +00:00
|
|
|
currentZIndex.value++;
|
2025-09-11 16:50:28 +00:00
|
|
|
win.zIndex = currentZIndex.value;
|
|
|
|
console.log(`Bringing window ${id} to front. New zIndex: ${win.zIndex}`);
|
2025-09-10 23:43:41 +00:00
|
|
|
saveWindowsToLocalStorage();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
const updateWindowPosition = (id, { x, y }) => {
|
|
|
|
const win = windows.find(w => w.id === id);
|
|
|
|
if (win) {
|
|
|
|
win.x = x;
|
|
|
|
win.y = y;
|
2025-09-10 23:43:41 +00:00
|
|
|
saveWindowsToLocalStorage();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
const updateWindowDimensions = (id, { width, height }) => {
|
|
|
|
const win = windows.find(w => w.id === id);
|
|
|
|
if (win) {
|
|
|
|
win.width = width;
|
|
|
|
win.height = height;
|
|
|
|
saveWindowsToLocalStorage();
|
|
|
|
}
|
2025-09-10 23:43:41 +00:00
|
|
|
};
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
// --- 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);
|
2025-09-11 14:22:41 +00:00
|
|
|
};
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
// 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);
|
2025-09-10 23:43:41 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
2025-09-11 16:50:28 +00:00
|
|
|
window.removeEventListener('mousemove', handleIconsContainerMouseMove);
|
|
|
|
window.removeEventListener('mouseup', handleIconsContainerMouseUp);
|
2025-09-10 23:43:41 +00:00
|
|
|
});
|
2025-09-11 16:50:28 +00:00
|
|
|
|
|
|
|
const minimizedWindows = computed(() => windows.filter(win => win.isMinimized));
|
|
|
|
|
2025-09-10 23:43:41 +00:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
|
|
<div>
|
|
|
|
<Taskbar
|
2025-09-11 16:50:28 +00:00
|
|
|
:menu-items="startMenuItems"
|
2025-09-10 23:43:41 +00:00
|
|
|
:minimized-windows="minimizedWindows"
|
2025-09-11 16:50:28 +00:00
|
|
|
@menu-action="handleMenuAction"
|
2025-09-10 23:43:41 +00:00
|
|
|
@restore-window="restoreWindow"
|
|
|
|
/>
|
|
|
|
<div class="desktop-content">
|
2025-09-11 16:50:28 +00:00
|
|
|
<div
|
|
|
|
class="desktop-icons-container"
|
|
|
|
:style="{ left: `${iconsContainerX}px`, top: `${iconsContainerY}px`, cursor: isIconsContainerDragging ? 'grabbing' : 'grab' }"
|
|
|
|
@mousedown="handleIconsContainerMouseDown"
|
|
|
|
>
|
|
|
|
<NuxtPage />
|
|
|
|
</div>
|
2025-09-10 23:43:41 +00:00
|
|
|
</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)"
|
2025-09-11 16:50:28 +00:00
|
|
|
@update-position="updateWindowPosition(window.id, $event)"
|
|
|
|
@update-dimensions="updateWindowDimensions(window.id, $event)"
|
2025-09-10 23:43:41 +00:00
|
|
|
/>
|
2025-09-11 14:22:41 +00:00
|
|
|
<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)"
|
2025-09-11 16:50:28 +00:00
|
|
|
@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)"
|
2025-09-11 14:22:41 +00:00
|
|
|
/>
|
2025-09-10 23:43:41 +00:00
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<style>
|
2025-09-11 16:50:28 +00:00
|
|
|
html {
|
|
|
|
background-color: #008080;
|
|
|
|
background-image: url('https://picsum.photos/1920/1080');
|
2025-09-10 23:43:41 +00:00
|
|
|
background-size: cover;
|
|
|
|
background-position: center;
|
|
|
|
background-repeat: no-repeat;
|
2025-09-11 16:50:28 +00:00
|
|
|
height: 100vh; /* Use vh for full viewport height */
|
|
|
|
}
|
|
|
|
|
|
|
|
html, body {
|
2025-09-10 23:43:41 +00:00
|
|
|
margin: 0;
|
2025-09-11 16:50:28 +00:00
|
|
|
padding: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
body {
|
|
|
|
height: 100vh; /* Use vh for full viewport height */
|
|
|
|
font-family: 'Courier New', Courier, monospace;
|
2025-09-10 23:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.taskbar {
|
|
|
|
position: fixed;
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
width: 100%;
|
|
|
|
height: 40px;
|
2025-09-11 16:50:28 +00:00
|
|
|
z-index: 1000;
|
2025-09-10 23:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.desktop-content {
|
2025-09-11 16:50:28 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 40px;
|
|
|
|
left: 0;
|
2025-09-10 23:43:41 +00:00
|
|
|
width: 100%;
|
2025-09-11 16:50:28 +00:00
|
|
|
height: calc(100vh - 40px); /* Changed to 100vh - 40px */
|
2025-09-10 23:43:41 +00:00
|
|
|
box-sizing: border-box;
|
2025-09-11 16:50:28 +00:00
|
|
|
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;
|
2025-09-10 23:43:41 +00:00
|
|
|
}
|
2025-09-11 16:50:28 +00:00
|
|
|
</style>
|