2025-09-25 05:38:59 +00:00
|
|
|
import { ref, computed } from 'vue';
|
|
|
|
import { defineStore } from 'pinia';
|
|
|
|
|
|
|
|
export interface AppInfo {
|
|
|
|
id: string;
|
|
|
|
name: string;
|
|
|
|
icon: string;
|
|
|
|
component: string;
|
|
|
|
description: string;
|
|
|
|
category: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface AppInstance {
|
|
|
|
id: string;
|
|
|
|
appId: string;
|
|
|
|
title: string;
|
|
|
|
x: number;
|
|
|
|
y: number;
|
|
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
zIndex: number;
|
|
|
|
isMinimized: boolean;
|
|
|
|
isMaximized: boolean;
|
|
|
|
isFocused: boolean;
|
2025-09-25 06:38:14 +00:00
|
|
|
maxWidth?: number;
|
|
|
|
maxHeight?: number;
|
|
|
|
minWidth?: number;
|
|
|
|
minHeight?: number;
|
2025-09-25 05:38:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let appInstanceIdCounter = 0;
|
|
|
|
|
|
|
|
export const useAppsStore = defineStore('apps', () => {
|
|
|
|
// Available apps registry
|
|
|
|
const availableApps = ref<AppInfo[]>([
|
|
|
|
{
|
2025-09-25 09:21:54 +00:00
|
|
|
id: 'livestream-hub',
|
|
|
|
name: 'livestream-hub', // Use translation key instead of hardcoded name
|
2025-09-25 17:20:06 +00:00
|
|
|
icon: '📰',
|
|
|
|
component: 'NewsHub',
|
2025-09-25 09:21:54 +00:00
|
|
|
description: 'Discover and watch live streams from popular streamers',
|
|
|
|
category: 'Entertainment'
|
2025-09-25 05:38:59 +00:00
|
|
|
},
|
|
|
|
// More apps can be added here in the future
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Running app instances
|
|
|
|
const appInstances = ref<AppInstance[]>([]);
|
|
|
|
const nextZIndex = ref(100);
|
|
|
|
|
|
|
|
// Getters
|
|
|
|
const getAppById = computed(() => {
|
|
|
|
return (id: string) => availableApps.value.find(app => app.id === id);
|
|
|
|
});
|
|
|
|
|
|
|
|
const getAppInstanceById = computed(() => {
|
|
|
|
return (id: string) => appInstances.value.find(instance => instance.id === id);
|
|
|
|
});
|
|
|
|
|
|
|
|
const orderedAppInstances = computed(() => {
|
|
|
|
return [...appInstances.value].sort((a, b) => a.zIndex - b.zIndex);
|
|
|
|
});
|
|
|
|
|
|
|
|
const focusedAppInstance = computed(() => {
|
|
|
|
return appInstances.value.find(instance => instance.isFocused);
|
|
|
|
});
|
|
|
|
|
2025-09-25 06:38:14 +00:00
|
|
|
// Get default size and constraints for specific app
|
|
|
|
function getAppSizeConstraints(appId: string) {
|
|
|
|
const appConstraints: Record<string, {
|
|
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
maxWidth?: number;
|
|
|
|
maxHeight?: number;
|
|
|
|
minWidth?: number;
|
|
|
|
minHeight?: number;
|
|
|
|
}> = {
|
2025-09-25 09:21:54 +00:00
|
|
|
'livestream-hub': {
|
|
|
|
width: 800,
|
|
|
|
height: 600,
|
|
|
|
maxWidth: 1200, // Allow wider view for better content display
|
|
|
|
maxHeight: 800, // Allow taller view for more streamers
|
|
|
|
minWidth: 600, // Minimum usable width
|
|
|
|
minHeight: 500 // Minimum usable height
|
2025-09-25 06:38:14 +00:00
|
|
|
},
|
|
|
|
'text-editor': {
|
|
|
|
width: 600,
|
|
|
|
height: 400,
|
|
|
|
maxWidth: 1200,
|
|
|
|
maxHeight: 800,
|
|
|
|
minWidth: 400,
|
|
|
|
minHeight: 300
|
|
|
|
},
|
|
|
|
'file-manager': {
|
|
|
|
width: 800,
|
|
|
|
height: 500,
|
|
|
|
maxWidth: 1400,
|
|
|
|
maxHeight: 900,
|
|
|
|
minWidth: 600,
|
|
|
|
minHeight: 400
|
|
|
|
},
|
|
|
|
'web-browser': {
|
|
|
|
width: 1000,
|
|
|
|
height: 600,
|
|
|
|
maxWidth: 1600,
|
|
|
|
maxHeight: 1000,
|
|
|
|
minWidth: 800,
|
|
|
|
minHeight: 500
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return appConstraints[appId] || {
|
|
|
|
width: 400,
|
|
|
|
height: 300,
|
|
|
|
maxWidth: 800,
|
|
|
|
maxHeight: 600,
|
|
|
|
minWidth: 300,
|
|
|
|
minHeight: 200
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2025-09-25 05:38:59 +00:00
|
|
|
// Actions
|
|
|
|
function launchApp(appId: string) {
|
|
|
|
const app = getAppById.value(appId);
|
|
|
|
if (!app) {
|
|
|
|
console.error(`App with id "${appId}" not found`);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2025-09-25 06:38:14 +00:00
|
|
|
const constraints = getAppSizeConstraints(appId);
|
2025-09-25 05:38:59 +00:00
|
|
|
const newInstanceId = appInstanceIdCounter++;
|
|
|
|
const newInstance: AppInstance = {
|
|
|
|
id: `app-${appId}-${newInstanceId}`,
|
|
|
|
appId: appId,
|
|
|
|
title: `${app.name} #${newInstanceId + 1}`,
|
|
|
|
x: Math.random() * 200 + 50,
|
|
|
|
y: Math.random() * 100 + 50 + 48, // Below taskbar
|
2025-09-25 06:38:14 +00:00
|
|
|
width: constraints.width,
|
|
|
|
height: constraints.height,
|
2025-09-25 05:38:59 +00:00
|
|
|
zIndex: nextZIndex.value++,
|
|
|
|
isMinimized: false,
|
|
|
|
isMaximized: false,
|
|
|
|
isFocused: true,
|
2025-09-25 06:38:14 +00:00
|
|
|
maxWidth: constraints.maxWidth,
|
|
|
|
maxHeight: constraints.maxHeight,
|
|
|
|
minWidth: constraints.minWidth,
|
|
|
|
minHeight: constraints.minHeight,
|
2025-09-25 05:38:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Unfocus all other instances
|
|
|
|
appInstances.value.forEach(instance => instance.isFocused = false);
|
|
|
|
|
|
|
|
appInstances.value.push(newInstance);
|
|
|
|
return newInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
function focusAppInstance(instanceId: string) {
|
|
|
|
const instance = appInstances.value.find(i => i.id === instanceId);
|
|
|
|
if (!instance || instance.isFocused) return;
|
|
|
|
|
|
|
|
// Unfocus all other instances
|
|
|
|
appInstances.value.forEach(i => i.isFocused = false);
|
|
|
|
|
|
|
|
instance.zIndex = nextZIndex.value++;
|
|
|
|
instance.isFocused = true;
|
|
|
|
|
|
|
|
if (instance.isMinimized) {
|
|
|
|
instance.isMinimized = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function closeAppInstance(instanceId: string) {
|
|
|
|
appInstances.value = appInstances.value.filter(i => i.id !== instanceId);
|
|
|
|
}
|
|
|
|
|
|
|
|
function minimizeAppInstance(instanceId: string) {
|
|
|
|
const instance = appInstances.value.find(i => i.id === instanceId);
|
|
|
|
if (instance) {
|
|
|
|
instance.isMinimized = true;
|
|
|
|
instance.isFocused = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function toggleMaximizeAppInstance(instanceId: string) {
|
|
|
|
const instance = appInstances.value.find(i => i.id === instanceId);
|
|
|
|
if (instance) {
|
|
|
|
instance.isMaximized = !instance.isMaximized;
|
|
|
|
focusAppInstance(instanceId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateAppInstancePosition({ id, x, y }: { id: string; x: number; y: number }) {
|
|
|
|
const instance = appInstances.value.find(i => i.id === id);
|
|
|
|
if (instance && !instance.isMaximized) {
|
|
|
|
instance.x = x;
|
|
|
|
instance.y = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateAppInstanceSize({ id, width, height }: { id: string; width: number; height: number }) {
|
|
|
|
const instance = appInstances.value.find(i => i.id === id);
|
|
|
|
if (instance && !instance.isMaximized) {
|
|
|
|
instance.width = width;
|
|
|
|
instance.height = height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function closeAllAppInstances() {
|
|
|
|
appInstances.value = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
availableApps,
|
|
|
|
appInstances,
|
|
|
|
nextZIndex,
|
|
|
|
getAppById,
|
|
|
|
getAppInstanceById,
|
|
|
|
orderedAppInstances,
|
|
|
|
focusedAppInstance,
|
|
|
|
launchApp,
|
|
|
|
focusAppInstance,
|
|
|
|
closeAppInstance,
|
|
|
|
minimizeAppInstance,
|
|
|
|
toggleMaximizeAppInstance,
|
|
|
|
updateAppInstancePosition,
|
|
|
|
updateAppInstanceSize,
|
|
|
|
closeAllAppInstances,
|
|
|
|
};
|
|
|
|
});
|