2025-09-23 16:43:57 +00:00
|
|
|
<script setup lang="ts">
|
|
|
|
import { ref } from 'vue';
|
|
|
|
import { storeToRefs } from 'pinia';
|
|
|
|
import { useWindowsStore } from '../stores/windows';
|
2025-09-25 05:38:59 +00:00
|
|
|
import { useAppsStore } from '../stores/apps';
|
|
|
|
import { useDesktopStore } from '../stores/desktop';
|
2025-09-23 16:43:57 +00:00
|
|
|
import { useUIStore } from '../stores/ui';
|
2025-09-25 05:38:59 +00:00
|
|
|
import { useSettingsStore } from '../stores/settings';
|
2025-09-23 16:43:57 +00:00
|
|
|
import Window from './Window.vue';
|
2025-09-25 05:38:59 +00:00
|
|
|
import AppWindow from './AppWindow.vue';
|
|
|
|
import DesktopIcon from './DesktopIcon.vue';
|
2025-09-23 16:43:57 +00:00
|
|
|
import Taskbar from './Taskbar.vue';
|
|
|
|
import SnapPreview from './SnapPreview.vue';
|
|
|
|
import StartMenu from './StartMenu.vue';
|
|
|
|
import type { SnapType } from '../composables/useDraggable';
|
|
|
|
|
|
|
|
const windowsStore = useWindowsStore();
|
2025-09-25 05:38:59 +00:00
|
|
|
const appsStore = useAppsStore();
|
|
|
|
const desktopStore = useDesktopStore();
|
2025-09-23 16:43:57 +00:00
|
|
|
const uiStore = useUIStore();
|
2025-09-24 09:45:36 +00:00
|
|
|
const settingsStore = useSettingsStore();
|
2025-09-23 16:43:57 +00:00
|
|
|
|
|
|
|
const { orderedWindows } = storeToRefs(windowsStore);
|
2025-09-25 05:38:59 +00:00
|
|
|
const { orderedAppInstances, availableApps } = storeToRefs(appsStore);
|
|
|
|
const { iconPositions } = storeToRefs(desktopStore);
|
|
|
|
const { isStartMenuOpen } = storeToRefs(uiStore);
|
2025-09-25 09:21:54 +00:00
|
|
|
const { snapWindow, closeAllWindows } = windowsStore;
|
2025-09-25 05:38:59 +00:00
|
|
|
const { launchApp, closeAllAppInstances } = appsStore;
|
|
|
|
const { initializeDesktopIcons, updateIconPosition } = desktopStore;
|
2025-09-23 16:43:57 +00:00
|
|
|
const { closeStartMenu } = uiStore;
|
2025-09-24 09:45:36 +00:00
|
|
|
const { toggleTheme } = settingsStore;
|
2025-09-23 16:43:57 +00:00
|
|
|
|
|
|
|
const snapPreview = ref<{ x: number; y: number; width: number; height: number; } | null>(null);
|
|
|
|
|
2025-09-24 09:45:36 +00:00
|
|
|
// --- Start Menu Event Handlers ---
|
|
|
|
function handleMenuAction(action: () => void) {
|
|
|
|
action();
|
|
|
|
closeStartMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleAbout() { handleMenuAction(() => console.log('About clicked')); }
|
|
|
|
function handleSettings() { handleMenuAction(() => console.log('Settings clicked')); }
|
|
|
|
function handleSignOut() { handleMenuAction(() => console.log('Sign Out clicked')); }
|
|
|
|
function handleToggleTheme() { handleMenuAction(toggleTheme); }
|
2025-09-25 05:38:59 +00:00
|
|
|
function handleCloseAllWindows() { handleMenuAction(() => { closeAllWindows(); closeAllAppInstances(); }); }
|
2025-09-24 09:45:36 +00:00
|
|
|
// --------------------------------
|
|
|
|
|
2025-09-23 16:43:57 +00:00
|
|
|
function handleSnapPreview(snapType: SnapType) {
|
2025-09-25 05:38:59 +00:00
|
|
|
if (!snapType || typeof window === 'undefined') {
|
2025-09-23 16:43:57 +00:00
|
|
|
snapPreview.value = null;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-09-24 09:45:36 +00:00
|
|
|
const taskbarHeight = 22;
|
2025-09-23 16:43:57 +00:00
|
|
|
const screenWidth = window.innerWidth;
|
|
|
|
const screenHeight = window.innerHeight - taskbarHeight;
|
|
|
|
|
|
|
|
switch (snapType) {
|
|
|
|
case 'left':
|
|
|
|
snapPreview.value = { x: 0, y: taskbarHeight, width: screenWidth / 2, height: screenHeight };
|
|
|
|
break;
|
|
|
|
case 'right':
|
|
|
|
snapPreview.value = { x: screenWidth / 2, y: taskbarHeight, width: screenWidth / 2, height: screenHeight };
|
|
|
|
break;
|
|
|
|
case 'top':
|
|
|
|
snapPreview.value = { x: 0, y: taskbarHeight, width: screenWidth, height: screenHeight };
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleSnapExecute({ windowId, snapType }: { windowId: string; snapType: SnapType }) {
|
|
|
|
snapPreview.value = null;
|
2025-09-25 05:38:59 +00:00
|
|
|
if (snapType && typeof window !== 'undefined') {
|
|
|
|
// Try to snap app window first, then regular window
|
|
|
|
const appInstance = appsStore.getAppInstanceById(windowId);
|
|
|
|
if (appInstance) {
|
|
|
|
// Handle app window snapping
|
|
|
|
const taskbarHeight = 22;
|
|
|
|
const screenWidth = window.innerWidth;
|
|
|
|
const screenHeight = window.innerHeight - taskbarHeight;
|
|
|
|
|
|
|
|
appInstance.isMaximized = false;
|
|
|
|
|
|
|
|
switch (snapType) {
|
|
|
|
case 'left':
|
|
|
|
appInstance.x = 0;
|
|
|
|
appInstance.y = taskbarHeight;
|
|
|
|
appInstance.width = screenWidth / 2;
|
|
|
|
appInstance.height = screenHeight;
|
|
|
|
break;
|
|
|
|
case 'right':
|
|
|
|
appInstance.x = screenWidth / 2;
|
|
|
|
appInstance.y = taskbarHeight;
|
|
|
|
appInstance.width = screenWidth / 2;
|
|
|
|
appInstance.height = screenHeight;
|
|
|
|
break;
|
|
|
|
case 'top':
|
|
|
|
appInstance.x = 0;
|
|
|
|
appInstance.y = taskbarHeight;
|
|
|
|
appInstance.width = screenWidth;
|
|
|
|
appInstance.height = screenHeight;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
appsStore.focusAppInstance(windowId);
|
|
|
|
} else {
|
|
|
|
// Handle regular window snapping
|
|
|
|
snapWindow(windowId, snapType);
|
|
|
|
}
|
2025-09-23 16:43:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleDesktopClick() {
|
|
|
|
closeStartMenu();
|
|
|
|
}
|
|
|
|
|
2025-09-25 05:38:59 +00:00
|
|
|
// Initialize desktop icons when apps are available
|
|
|
|
function initializeDesktop() {
|
|
|
|
if (availableApps.value.length > 0) {
|
|
|
|
initializeDesktopIcons(availableApps.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle icon position updates
|
|
|
|
function handleIconPositionChange(appId: string, x: number, y: number) {
|
|
|
|
updateIconPosition(appId, x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle app launch from desktop icon
|
|
|
|
function handleAppLaunch(appId: string) {
|
|
|
|
launchApp(appId);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get app info for desktop icons
|
|
|
|
const desktopIcons = computed(() => {
|
|
|
|
return iconPositions.value.map(position => {
|
|
|
|
const app = availableApps.value.find(app => app.id === position.appId);
|
|
|
|
return app ? { ...app, ...position } : null;
|
|
|
|
}).filter(Boolean);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Initialize desktop on mount
|
|
|
|
import { onMounted, watch } from 'vue';
|
|
|
|
onMounted(() => {
|
|
|
|
initializeDesktop();
|
|
|
|
});
|
|
|
|
|
|
|
|
// Watch for changes in available apps
|
|
|
|
watch(availableApps, () => {
|
|
|
|
initializeDesktop();
|
|
|
|
}, { immediate: true });
|
|
|
|
|
2025-09-23 16:43:57 +00:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
|
|
<div class="desktop" @click.self="handleDesktopClick">
|
2025-09-25 05:38:59 +00:00
|
|
|
<!-- Regular Windows -->
|
2025-09-23 16:43:57 +00:00
|
|
|
<Window
|
|
|
|
v-for="window in orderedWindows"
|
|
|
|
:key="window.id"
|
|
|
|
:window="window"
|
|
|
|
@snap-preview="handleSnapPreview"
|
|
|
|
@snap-execute="handleSnapExecute"
|
|
|
|
/>
|
|
|
|
|
2025-09-25 05:38:59 +00:00
|
|
|
<!-- App Windows -->
|
|
|
|
<AppWindow
|
|
|
|
v-for="appInstance in orderedAppInstances"
|
|
|
|
:key="appInstance.id"
|
|
|
|
:instance="appInstance"
|
|
|
|
@snap-preview="handleSnapPreview"
|
|
|
|
@snap-execute="handleSnapExecute"
|
|
|
|
/>
|
|
|
|
|
|
|
|
<!-- Desktop Icons -->
|
|
|
|
<DesktopIcon
|
|
|
|
v-for="icon in desktopIcons"
|
|
|
|
:key="icon.appId"
|
|
|
|
:app="icon"
|
|
|
|
:x="icon.x"
|
|
|
|
:y="icon.y"
|
|
|
|
@position-change="(x, y) => handleIconPositionChange(icon.appId, x, y)"
|
|
|
|
@launch="handleAppLaunch"
|
|
|
|
/>
|
|
|
|
|
2025-09-23 16:43:57 +00:00
|
|
|
<SnapPreview v-if="snapPreview" :preview="snapPreview" />
|
2025-09-24 09:45:36 +00:00
|
|
|
<StartMenu
|
|
|
|
v-if="isStartMenuOpen"
|
|
|
|
@about="handleAbout"
|
|
|
|
@settings="handleSettings"
|
|
|
|
@toggle-theme="handleToggleTheme"
|
|
|
|
@sign-out="handleSignOut"
|
|
|
|
@close-all-windows="handleCloseAllWindows"
|
|
|
|
/>
|
2025-09-23 16:43:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
<Taskbar />
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
.desktop {
|
|
|
|
position: relative;
|
|
|
|
width: 100vw;
|
|
|
|
height: 100vh;
|
|
|
|
background: var(--background-desktop);
|
|
|
|
overflow: hidden;
|
2025-09-24 09:45:36 +00:00
|
|
|
padding-top: 22px;
|
2025-09-23 16:43:57 +00:00
|
|
|
}
|
2025-09-25 05:38:59 +00:00
|
|
|
|
2025-09-23 16:43:57 +00:00
|
|
|
</style>
|