@owdproject/core
Version:
Nuxt Desktop module for browser desktop experiences (windows, shell, apps)
286 lines (237 loc) • 7.04 kB
text/typescript
import { reactive, markRaw, computed } from 'vue'
import { ApplicationController } from '../internal/controllers/ApplicationController'
import { normalizeApplicationConfig } from '../utils/utilApp'
import { debugLog } from '../utils/utilDebug'
import * as shellwords from 'shellwords'
import getopts from "getopts"
const apps = reactive(new Map<string, IApplicationController>())
/**
* Application Manager Composable (Singleton)
*/
export function useApplicationManager() {
/**
* Define new application
*
* @param id
* @param config
*/
async function defineApp(id: string, config: ApplicationConfig) {
if (isAppDefined(id)) {
debugLog(`App "${id}" is already defined`)
return getAppById(id)!
}
const normalizedConfig = normalizeApplicationConfig(config)
const applicationConfig = markRaw(normalizedConfig)
const applicationController: IApplicationController = new ApplicationController(id, applicationConfig)
await applicationController.initApplication()
apps.set(id, markRaw(applicationController))
return applicationController
}
/**
* Check if app has been defined
*
* @param {string} id
*/
function isAppDefined(id: string) {
return getAppById(id)
}
/**
* Retrieves an app instance by its unique identifier
*
* @param {string} id
*/
function getAppById(id: string) {
return apps.get(id)
}
/**
* Check if app is running
*
* @param {string} id
*/
function isAppRunning(id: string) {
if (!isAppDefined(id)) {
throw Error(`App "${id}" is not defined`)
}
const applicationController = getAppById(id)!
return applicationController.isRunning
}
/**
* Launch app entry
*
* @param id
* @param entryKey
* @param argument
*/
async function launchAppEntry(
id: string,
entryKey: string,
argument: string = ''
): Promise<IApplicationController | undefined | void> {
if (!isAppDefined(id)) {
throw Error(`App "${id}" is not defined`)
}
const applicationController = getAppById(id)!
if (
applicationController.config.entries &&
!applicationController.config.entries.hasOwnProperty(entryKey)
) {
throw Error(`App entry "${entryKey}" is not defined in ${id} application`)
}
const entry: ApplicationEntry = applicationController.config.entries[entryKey]!
if (argument) argument = ` ${argument}`
await execAppCommand(applicationController.id, entry.command + argument)
}
/**
* Run app command
*
* @param id
* @param rawCommand
*/
async function execAppCommand(
id: string,
rawCommand: string,
): Promise<CommandOutput> {
if (!isAppDefined(id)) {
throw Error(`App "${id}" is not defined`)
}
const applicationController = getAppById(id)!
const args = shellwords.split(rawCommand)
const parsed = getopts(args)
const command: string = parsed._[0]
if (
applicationController.config.commands &&
!applicationController.config.commands.hasOwnProperty(command)
) {
throw Error(
`App command "${command}" is not defined in ${id} application`,
)
}
const commandFn: any = applicationController.config.commands![command]
const commandOutput = await commandFn(applicationController, parsed)
applicationController.setRunning(true)
return commandOutput
}
/**
* Close application
*
* @param id
*/
function closeApp(id: string) {
if (!isAppDefined(id)) {
throw Error(`App "${id}" is not defined`)
}
const applicationController = getAppById(id)!
applicationController.closeAllWindows()
applicationController.setRunning(false)
}
/**
* Array of available menu entries for system bars, docks
*/
const appsEntries = computed(() => {
const entries: ApplicationEntryWithInherited[] = []
for (const applicationController of apps.values()) {
if (!applicationController.config.entries) {
continue
}
for (const entryKey of Object.keys(applicationController.config.entries)) {
const entry = applicationController.config.entries[entryKey]!
entries.push({
application: applicationController,
title: entry.title ?? applicationController.config.title,
icon: entry.icon ?? applicationController.config.icon,
category: entry.category ?? applicationController.config.category,
visibility: entry.visibility ?? 'primary',
command: entry.command,
})
}
}
return entries
})
/**
* Array of opened windows for system bars, docks
*/
const windowsOpened = computed(() => {
const windows = new Map<string, IWindowController>()
for (const applicationController of apps.values()) {
if (
!applicationController.isRunning &&
applicationController.openWindowCount.value === 0
) {
continue
}
for (const [windowId, window] of applicationController.windows) {
windows.set(windowId, window)
}
}
return windows
})
/**
* Array of running applications
*/
const appsRunning = computed(() => {
const runningApps: IApplicationController[] = []
for (const applicationController of apps.values()) {
const openCount = applicationController.openWindowCount.value
if (applicationController.isRunning || openCount > 0) {
runningApps.push(applicationController)
}
}
return runningApps
})
function getWindowOpenedId(windowId: string) {
for (const window of windowsOpened.value.values()) {
if (window.state.id === windowId) {
return window
}
}
}
/**
* Gets all unique categories from the installed apps
*/
const appCategories = computed(() => {
const categories = new Set<string>()
for (const applicationController of apps.values()) {
if (applicationController.config.category) {
categories.add(applicationController.config.category)
}
}
return Array.from(categories).sort()
})
/**
* Gets the apps ordered by category
*/
const appsByCategory = computed(() => {
const categorizedApps: {
[category: string]: IApplicationController[]
} = {}
for (const applicationController of apps.values()) {
const category = applicationController.config.category || 'other'
if (!categorizedApps[category]) {
categorizedApps[category] = []
}
categorizedApps[category].push(applicationController)
}
for (const category in categorizedApps) {
categorizedApps[category].sort((a, b) =>
a.config.title.localeCompare(b.config.title),
)
}
return categorizedApps
})
return {
defineApp,
isAppDefined,
getAppById,
isAppRunning,
launchAppEntry,
execAppCommand,
closeApp,
getWindowOpenedId,
apps,
appsEntries,
appsRunning,
windowsOpened,
appCategories,
appsByCategory,
}
}