@owdproject/core 3.1.3 → 3.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +6 -7
- package/bin/owd.js +14 -14
- package/index.ts +5 -2
- package/module.ts +78 -61
- package/package.json +19 -17
- package/project.json +4 -0
- package/runtime/components/Core/Application/CoreApplicationRender.vue +6 -5
- package/runtime/components/Core/Application/CoreApplicationWindowsRender.vue +8 -8
- package/runtime/components/Core/Background/CoreBackground.vue +1 -1
- package/runtime/components/Core/Desktop/CoreDesktop.vue +31 -24
- package/runtime/components/Core/Explorer/CoreExplorerFile.vue +47 -38
- package/runtime/components/Core/Explorer/CoreExplorerFolder.vue +0 -1
- package/runtime/components/Core/Time/CoreTime.vue +10 -7
- package/runtime/components/Core/Window/CoreWindow.vue +49 -35
- package/runtime/components/Core/Window/CoreWindowContent.vue +3 -5
- package/runtime/components/Core/Window/CoreWindowNav.vue +12 -7
- package/runtime/composables/useApplicationEntries.ts +51 -0
- package/runtime/composables/useApplicationManager.ts +278 -5
- package/runtime/composables/useDesktopManager.ts +22 -4
- package/runtime/composables/useTerminalManager.ts +63 -4
- package/runtime/core/controllers/ApplicationController.ts +203 -192
- package/runtime/core/controllers/WindowController.ts +342 -334
- package/runtime/plugins/resize.client.ts +4 -4
- package/runtime/stores/storeApplication.ts +236 -0
- package/runtime/stores/storeApplicationMeta.ts +12 -8
- package/runtime/stores/storeApplicationWindows.ts +16 -12
- package/runtime/stores/storeDesktop.ts +35 -29
- package/runtime/stores/storeDesktopDefaultApps.ts +53 -0
- package/runtime/stores/storeDesktopVolume.ts +12 -12
- package/runtime/stores/storeDesktopWindow.ts +13 -13
- package/runtime/stores/storeDesktopWorkspace.ts +35 -31
- package/runtime/utils/utilApp.ts +27 -22
- package/runtime/utils/utilCommon.ts +43 -43
- package/runtime/utils/utilDebug.ts +17 -17
- package/runtime/utils/utilDesktop.ts +4 -4
- package/runtime/utils/utilTerminal.ts +6 -5
- package/runtime/utils/utilWindow.ts +11 -11
- package/test/fixtures/basic/app.vue +1 -2
- package/test/fixtures/basic/nuxt.config.ts +1 -3
- package/tsconfig.app.json +1 -1
- package/tsconfig.json +1 -1
- package/types/index.d.ts +235 -207
- package/vitest.config.ts +3 -3
- package/eslint.config.mjs +0 -30
- package/runtime/composables/useAppEntries.ts +0 -36
- package/runtime/composables/useClipboardFs.ts +0 -25
- package/runtime/core/managers/ApplicationManager.ts +0 -237
- package/runtime/core/managers/DesktopManager.ts +0 -37
- package/runtime/core/managers/TerminalManager.ts +0 -44
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<li
|
|
3
|
-
|
|
2
|
+
<li
|
|
3
|
+
:class="[
|
|
4
|
+
'owd-explorer-file',
|
|
5
|
+
{ 'owd-explorer-file--inactive': props.inactive },
|
|
6
|
+
]"
|
|
7
|
+
>
|
|
8
|
+
<a
|
|
9
|
+
:href="props.url"
|
|
10
|
+
:target="props.target"
|
|
11
|
+
:title="props.title"
|
|
12
|
+
@click="props.fn"
|
|
13
|
+
>
|
|
4
14
|
<img :src="props.icon" :alt="props.title" />
|
|
5
15
|
<div class="owd-explorer-file__name" v-html="props.name" />
|
|
6
16
|
</a>
|
|
@@ -19,49 +29,48 @@ const props = defineProps<{
|
|
|
19
29
|
</script>
|
|
20
30
|
|
|
21
31
|
<style scoped lang="scss">
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
li.file {
|
|
33
|
+
width: 100px;
|
|
34
|
+
margin: 0 5px 5px 0;
|
|
35
|
+
text-align: center;
|
|
36
|
+
float: left;
|
|
37
|
+
border-radius: 2px;
|
|
38
|
+
border: 1px solid rgba(0, 0, 0, 0);
|
|
29
39
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@media(min-width: 480px) {
|
|
35
|
-
&:not(.inactive):hover {
|
|
40
|
+
@media (max-width: 480px) {
|
|
41
|
+
margin-right: 0;
|
|
42
|
+
}
|
|
36
43
|
|
|
37
|
-
|
|
44
|
+
@media (min-width: 480px) {
|
|
45
|
+
&:not(.inactive):hover {
|
|
38
46
|
}
|
|
47
|
+
}
|
|
39
48
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
&.inactive {
|
|
50
|
+
filter: grayscale(100%);
|
|
51
|
+
opacity: 0.2;
|
|
52
|
+
}
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
a {
|
|
55
|
+
display: block;
|
|
56
|
+
color: #ccc;
|
|
57
|
+
text-decoration: none;
|
|
58
|
+
transition: color 0.15s ease-in-out;
|
|
59
|
+
padding: 10px 0;
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
img {
|
|
62
|
+
vertical-align: middle;
|
|
63
|
+
height: 32px;
|
|
64
|
+
border-radius: 2px;
|
|
65
|
+
}
|
|
57
66
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
67
|
+
div.name {
|
|
68
|
+
font-size: 12px;
|
|
69
|
+
letter-spacing: -0.2px;
|
|
70
|
+
line-height: 10px;
|
|
71
|
+
margin-top: 10px;
|
|
72
|
+
cursor: pointer;
|
|
65
73
|
}
|
|
66
74
|
}
|
|
75
|
+
}
|
|
67
76
|
</style>
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {ref, onMounted, onUnmounted} from 'vue'
|
|
3
|
-
import {computed} from
|
|
4
|
-
import {useI18n} from
|
|
2
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
3
|
+
import { computed } from '@vue/reactivity'
|
|
4
|
+
import { useI18n } from 'vue-i18n'
|
|
5
5
|
|
|
6
6
|
const date: Ref<Date> = ref(new Date())
|
|
7
|
-
const {locale} = useI18n()
|
|
7
|
+
const { locale } = useI18n()
|
|
8
8
|
|
|
9
9
|
let minuteTimeout: any
|
|
10
10
|
|
|
11
11
|
onMounted(() => {
|
|
12
12
|
function scheduleNextMinute() {
|
|
13
13
|
const now = new Date()
|
|
14
|
-
const millisecondsToNextMinute =
|
|
14
|
+
const millisecondsToNextMinute =
|
|
15
|
+
(60 - now.getSeconds()) * 1000 - now.getMilliseconds()
|
|
15
16
|
minuteTimeout = setTimeout(() => {
|
|
16
17
|
date.value = new Date()
|
|
17
18
|
scheduleNextMinute()
|
|
@@ -44,10 +45,12 @@ const time = computed(() => {
|
|
|
44
45
|
|
|
45
46
|
if (minutes.length === 1) minutes = `0${minutes}`
|
|
46
47
|
|
|
47
|
-
return locale.value === 'it'
|
|
48
|
+
return locale.value === 'it'
|
|
49
|
+
? `${hours}:${minutes}`
|
|
50
|
+
: `${hours}:${minutes} ${period}`
|
|
48
51
|
})
|
|
49
52
|
</script>
|
|
50
53
|
|
|
51
54
|
<template>
|
|
52
55
|
<time class="owd-time" v-text="time" />
|
|
53
|
-
</template>
|
|
56
|
+
</template>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {ref,provide} from 'vue'
|
|
3
|
-
import {computed} from
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { ref, provide } from 'vue'
|
|
3
|
+
import { computed } from '@vue/reactivity'
|
|
4
|
+
import { useAppConfig } from 'nuxt/app'
|
|
5
|
+
import { useDesktopManager } from '@owdproject/core/runtime/composables/useDesktopManager'
|
|
6
|
+
import { handleWindowControllerProps } from '@owdproject/core/runtime/utils/utilWindow'
|
|
6
7
|
|
|
7
8
|
const props = defineProps<{
|
|
8
9
|
window?: IWindowController
|
|
@@ -24,11 +25,14 @@ const emit = defineEmits([
|
|
|
24
25
|
'restore',
|
|
25
26
|
'maximize',
|
|
26
27
|
'unmaximize',
|
|
27
|
-
'toggle-maximize'
|
|
28
|
+
'toggle-maximize',
|
|
28
29
|
])
|
|
29
30
|
|
|
31
|
+
const appConfig = useAppConfig()
|
|
30
32
|
const desktopManager = useDesktopManager() // used in <style v-bind>
|
|
31
|
-
const windowController: IWindowController = handleWindowControllerProps(
|
|
33
|
+
const windowController: IWindowController = handleWindowControllerProps(
|
|
34
|
+
props.window,
|
|
35
|
+
)
|
|
32
36
|
|
|
33
37
|
provide<IWindowController>('windowController', windowController)
|
|
34
38
|
provide<WindowContent>('windowContent', props.content ?? {})
|
|
@@ -54,7 +58,7 @@ function onWindowDragMove(data: any) {
|
|
|
54
58
|
function onWindowDragEnd(data: { left: number; top: number }) {
|
|
55
59
|
windowController?.actions?.setPosition({
|
|
56
60
|
x: data.left,
|
|
57
|
-
y: data.top
|
|
61
|
+
y: data.top,
|
|
58
62
|
})
|
|
59
63
|
|
|
60
64
|
isDragging.value = false
|
|
@@ -75,17 +79,22 @@ function onWindowResizeMove(data: any) {
|
|
|
75
79
|
/**
|
|
76
80
|
* Window end resize event
|
|
77
81
|
*/
|
|
78
|
-
function onWindowResizeEnd(data: {
|
|
82
|
+
function onWindowResizeEnd(data: {
|
|
83
|
+
left: number
|
|
84
|
+
top: number
|
|
85
|
+
width: number
|
|
86
|
+
height: number
|
|
87
|
+
}) {
|
|
79
88
|
isResizing.value = false
|
|
80
89
|
|
|
81
90
|
windowController?.actions?.setPosition({
|
|
82
91
|
x: data.left,
|
|
83
|
-
y: data.top
|
|
92
|
+
y: data.top,
|
|
84
93
|
})
|
|
85
94
|
|
|
86
95
|
windowController?.actions?.setSize({
|
|
87
96
|
width: data.width,
|
|
88
|
-
height: data.height
|
|
97
|
+
height: data.height,
|
|
89
98
|
})
|
|
90
99
|
|
|
91
100
|
emit('resize:end', data)
|
|
@@ -112,37 +121,42 @@ const classes = computed(() => {
|
|
|
112
121
|
|
|
113
122
|
<template>
|
|
114
123
|
<vue-resizable
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
ref="windowResizableController"
|
|
125
|
+
:class="classes"
|
|
126
|
+
:width="windowController?.size.width"
|
|
127
|
+
:height="windowController?.size.height"
|
|
128
|
+
:min-width="windowController?.size.minWidth"
|
|
129
|
+
:min-height="windowController?.size.minHeight"
|
|
130
|
+
:max-width="windowController?.size.maxWidth"
|
|
131
|
+
:max-height="windowController?.size.maxHeight"
|
|
132
|
+
:left="windowController?.position?.x"
|
|
133
|
+
:top="windowController?.position?.y"
|
|
134
|
+
:active="windowController?.isResizable ? undefined : []"
|
|
135
|
+
fit-parent
|
|
136
|
+
@drag:start="onWindowDragStart"
|
|
137
|
+
@drag:move="onWindowDragMove"
|
|
138
|
+
@drag:end="onWindowDragEnd"
|
|
139
|
+
@resize:start="onWindowResizeStart"
|
|
140
|
+
@resize:move="onWindowResizeMove"
|
|
141
|
+
@resize:end="onWindowResizeEnd"
|
|
142
|
+
:style="{ zIndex: windowController?.position?.z }"
|
|
143
|
+
:drag-selector="
|
|
144
|
+
windowController?.isDraggable
|
|
145
|
+
? '.owd-window-nav__draggable'
|
|
146
|
+
: '.owd-window-nav__draggable-none'
|
|
147
|
+
"
|
|
148
|
+
@pointerdown="onWindowPointerDown"
|
|
134
149
|
>
|
|
135
|
-
|
|
136
|
-
<slot/>
|
|
137
|
-
|
|
150
|
+
<slot />
|
|
138
151
|
</vue-resizable>
|
|
139
152
|
</template>
|
|
140
153
|
|
|
141
154
|
<style scoped lang="scss">
|
|
142
155
|
.owd-window {
|
|
143
|
-
position: v-bind('
|
|
156
|
+
position: v-bind('appConfig.desktop.windows.position');
|
|
144
157
|
|
|
145
|
-
&--dragging,
|
|
158
|
+
&--dragging,
|
|
159
|
+
&--resizing {
|
|
146
160
|
:deep(.owd-window__content) {
|
|
147
161
|
pointer-events: none;
|
|
148
162
|
}
|
|
@@ -153,4 +167,4 @@ const classes = computed(() => {
|
|
|
153
167
|
height: 100%;
|
|
154
168
|
}
|
|
155
169
|
}
|
|
156
|
-
</style>
|
|
170
|
+
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {inject} from
|
|
3
|
-
import {computed} from
|
|
2
|
+
import { inject } from 'vue'
|
|
3
|
+
import { computed } from '@vue/reactivity'
|
|
4
4
|
|
|
5
5
|
const windowContent = inject<WindowContent>('windowContent')
|
|
6
6
|
|
|
@@ -25,6 +25,4 @@ const classes = computed(() => {
|
|
|
25
25
|
</div>
|
|
26
26
|
</template>
|
|
27
27
|
|
|
28
|
-
<style scoped lang="scss">
|
|
29
|
-
|
|
30
|
-
</style>
|
|
28
|
+
<style scoped lang="scss"></style>
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {inject} from
|
|
3
|
-
import {computed} from
|
|
2
|
+
import { inject } from 'vue'
|
|
3
|
+
import { computed } from '@vue/reactivity'
|
|
4
4
|
|
|
5
5
|
const windowController = inject<IWindowController>('windowController')
|
|
6
6
|
|
|
7
7
|
const classes = computed(() => {
|
|
8
8
|
const list = ['owd-window-nav']
|
|
9
9
|
|
|
10
|
-
if (
|
|
10
|
+
if (
|
|
11
|
+
typeof windowController?.state.active === 'undefined' ||
|
|
12
|
+
!!windowController?.state.active
|
|
13
|
+
) {
|
|
11
14
|
list.push('owd-window-nav--active')
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
if (
|
|
17
|
+
if (
|
|
18
|
+
typeof windowController?.state.focused === 'undefined' ||
|
|
19
|
+
!!windowController?.state.focused
|
|
20
|
+
) {
|
|
15
21
|
list.push('owd-window-nav--focused')
|
|
16
22
|
}
|
|
17
23
|
|
|
@@ -21,10 +27,9 @@ const classes = computed(() => {
|
|
|
21
27
|
|
|
22
28
|
<template>
|
|
23
29
|
<div :class="classes">
|
|
24
|
-
<div class="owd-window-nav__draggable"/>
|
|
25
|
-
|
|
26
|
-
<slot/>
|
|
30
|
+
<div class="owd-window-nav__draggable" />
|
|
27
31
|
|
|
32
|
+
<slot />
|
|
28
33
|
</div>
|
|
29
34
|
</template>
|
|
30
35
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useApplicationManager } from './useApplicationManager'
|
|
2
|
+
import { computed } from '@vue/reactivity'
|
|
3
|
+
|
|
4
|
+
type SortBy =
|
|
5
|
+
| 'title'
|
|
6
|
+
| 'category'
|
|
7
|
+
| ((
|
|
8
|
+
a: ApplicationEntryWithInherited,
|
|
9
|
+
b: ApplicationEntryWithInherited,
|
|
10
|
+
) => number)
|
|
11
|
+
type Visibility =
|
|
12
|
+
| 'primary'
|
|
13
|
+
| 'all'
|
|
14
|
+
| ((entry: ApplicationEntryWithInherited) => boolean)
|
|
15
|
+
|
|
16
|
+
export function useApplicationEntries() {
|
|
17
|
+
const applicationManager = useApplicationManager()
|
|
18
|
+
|
|
19
|
+
const sortedAppEntries = function (
|
|
20
|
+
sortBy: SortBy = 'title',
|
|
21
|
+
visibility: Visibility = 'primary',
|
|
22
|
+
): Ref<ApplicationEntryWithInherited[]> {
|
|
23
|
+
return computed(() => {
|
|
24
|
+
const currentEntries = [...applicationManager.appsEntries.value]
|
|
25
|
+
|
|
26
|
+
// filtering
|
|
27
|
+
const filtered =
|
|
28
|
+
typeof visibility === 'function'
|
|
29
|
+
? currentEntries.filter(visibility)
|
|
30
|
+
: visibility === 'primary'
|
|
31
|
+
? currentEntries.filter((e) => e.visibility !== 'secondary')
|
|
32
|
+
: currentEntries
|
|
33
|
+
|
|
34
|
+
// sorting
|
|
35
|
+
const sorted =
|
|
36
|
+
typeof sortBy === 'function'
|
|
37
|
+
? filtered.sort(sortBy)
|
|
38
|
+
: sortBy === 'title'
|
|
39
|
+
? filtered.sort((a, b) =>
|
|
40
|
+
(a.title || '').localeCompare(b.title || ''),
|
|
41
|
+
)
|
|
42
|
+
: filtered.sort((a, b) =>
|
|
43
|
+
(a.category || '').localeCompare(b.category || ''),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return sorted
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { sortedAppEntries }
|
|
51
|
+
}
|
|
@@ -1,7 +1,280 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { reactive, markRaw, computed } from 'vue'
|
|
2
|
+
import { ApplicationController } from '../core/controllers/ApplicationController'
|
|
3
|
+
import { normalizeApplicationConfig } from '../utils/utilApp'
|
|
4
|
+
import { debugLog } from '../utils/utilDebug'
|
|
5
|
+
import * as shellwords from 'shellwords'
|
|
6
|
+
import getopts from "getopts"
|
|
2
7
|
|
|
3
|
-
const
|
|
8
|
+
const apps = reactive(new Map<string, IApplicationController>())
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Application Manager Composable (Singleton)
|
|
12
|
+
*/
|
|
13
|
+
export function useApplicationManager() {
|
|
14
|
+
/**
|
|
15
|
+
* Define new application
|
|
16
|
+
*
|
|
17
|
+
* @param id
|
|
18
|
+
* @param config
|
|
19
|
+
*/
|
|
20
|
+
async function defineApp(id: string, config: ApplicationConfig) {
|
|
21
|
+
if (isAppDefined(id)) {
|
|
22
|
+
debugLog(`App "${id}" is already defined`)
|
|
23
|
+
return getAppById(id)!
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const normalizedConfig = normalizeApplicationConfig(config)
|
|
27
|
+
const applicationConfig = markRaw(normalizedConfig)
|
|
28
|
+
|
|
29
|
+
const applicationController: IApplicationController = new ApplicationController(id, applicationConfig)
|
|
30
|
+
await applicationController.initApplication()
|
|
31
|
+
|
|
32
|
+
apps.set(id, applicationController)
|
|
33
|
+
|
|
34
|
+
return applicationController
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check if app has been defined
|
|
39
|
+
*
|
|
40
|
+
* @param {string} id
|
|
41
|
+
*/
|
|
42
|
+
function isAppDefined(id: string) {
|
|
43
|
+
return getAppById(id)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Retrieves an app instance by its unique identifier
|
|
48
|
+
*
|
|
49
|
+
* @param {string} id
|
|
50
|
+
*/
|
|
51
|
+
function getAppById(id: string) {
|
|
52
|
+
return apps.get(id)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if app is running
|
|
57
|
+
*
|
|
58
|
+
* @param {string} id
|
|
59
|
+
*/
|
|
60
|
+
function isAppRunning(id: string) {
|
|
61
|
+
if (!isAppDefined(id)) {
|
|
62
|
+
throw Error(`App "${id}" is not defined`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const applicationController = getAppById(id)!
|
|
66
|
+
return applicationController.isRunning
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Launch app entry
|
|
71
|
+
*
|
|
72
|
+
* @param id
|
|
73
|
+
* @param entryKey
|
|
74
|
+
* @param argument
|
|
75
|
+
*/
|
|
76
|
+
async function launchAppEntry(
|
|
77
|
+
id: string,
|
|
78
|
+
entryKey: string,
|
|
79
|
+
argument: string = ''
|
|
80
|
+
): Promise<IApplicationController | undefined | void> {
|
|
81
|
+
if (!isAppDefined(id)) {
|
|
82
|
+
throw Error(`App "${id}" is not defined`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const applicationController = getAppById(id)!
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
applicationController.config.entries &&
|
|
89
|
+
!applicationController.config.entries.hasOwnProperty(entryKey)
|
|
90
|
+
) {
|
|
91
|
+
throw Error(`App entry "${entryKey}" is not defined in ${id} application`)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const entry: ApplicationEntry = applicationController.config.entries[entryKey]!
|
|
95
|
+
|
|
96
|
+
if (argument) argument = ` ${argument}`
|
|
97
|
+
|
|
98
|
+
await execAppCommand(applicationController.id, entry.command + argument)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Run app command
|
|
103
|
+
*
|
|
104
|
+
* @param id
|
|
105
|
+
* @param rawCommand
|
|
106
|
+
*/
|
|
107
|
+
async function execAppCommand(
|
|
108
|
+
id: string,
|
|
109
|
+
rawCommand: string,
|
|
110
|
+
): Promise<CommandOutput> {
|
|
111
|
+
if (!isAppDefined(id)) {
|
|
112
|
+
throw Error(`App "${id}" is not defined`)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const applicationController = getAppById(id)!
|
|
116
|
+
|
|
117
|
+
const args = shellwords.split(rawCommand)
|
|
118
|
+
const parsed = getopts(args)
|
|
119
|
+
const command: string = parsed._[0]
|
|
120
|
+
|
|
121
|
+
if (
|
|
122
|
+
applicationController.config.commands &&
|
|
123
|
+
!applicationController.config.commands.hasOwnProperty(command)
|
|
124
|
+
) {
|
|
125
|
+
throw Error(
|
|
126
|
+
`App command "${command}" is not defined in ${id} application`,
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const commandFn: any = applicationController.config.commands![command]
|
|
131
|
+
|
|
132
|
+
const commandOutput = await commandFn(applicationController, parsed)
|
|
133
|
+
|
|
134
|
+
applicationController.setRunning(true)
|
|
135
|
+
|
|
136
|
+
return commandOutput
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Close application
|
|
141
|
+
*
|
|
142
|
+
* @param id
|
|
143
|
+
*/
|
|
144
|
+
function closeApp(id: string) {
|
|
145
|
+
if (!isAppDefined(id)) {
|
|
146
|
+
throw Error(`App "${id}" is not defined`)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const applicationController = getAppById(id)!
|
|
150
|
+
|
|
151
|
+
applicationController.closeAllWindows()
|
|
152
|
+
applicationController.setRunning(false)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Array of available menu entries for system bars, docks
|
|
157
|
+
*/
|
|
158
|
+
const appsEntries = computed(() => {
|
|
159
|
+
const entries: ApplicationEntryWithInherited[] = []
|
|
160
|
+
|
|
161
|
+
for (const applicationController of apps.values()) {
|
|
162
|
+
if (!applicationController.config.entries) {
|
|
163
|
+
continue
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for (const entryKey of Object.keys(applicationController.config.entries)) {
|
|
167
|
+
const entry = applicationController.config.entries[entryKey]!
|
|
168
|
+
|
|
169
|
+
entries.push({
|
|
170
|
+
application: applicationController,
|
|
171
|
+
title: entry.title ?? applicationController.config.title,
|
|
172
|
+
icon: entry.icon ?? applicationController.config.icon,
|
|
173
|
+
category: entry.category ?? applicationController.config.category,
|
|
174
|
+
visibility: entry.visibility ?? 'primary',
|
|
175
|
+
command: entry.command,
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return entries
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Array of opened windows for system bars, docks
|
|
185
|
+
*/
|
|
186
|
+
const windowsOpened = computed(() => {
|
|
187
|
+
const windows: Map<string, IWindowController>[] = []
|
|
188
|
+
|
|
189
|
+
for (const applicationController of apps.values()) {
|
|
190
|
+
if (applicationController.isRunning) {
|
|
191
|
+
windows.push(...applicationController.windows)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return windows
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Array of running applications
|
|
200
|
+
*/
|
|
201
|
+
const appsRunning = computed(() => {
|
|
202
|
+
const runningApps: IApplicationController[] = []
|
|
203
|
+
|
|
204
|
+
for (const applicationController of apps.values()) {
|
|
205
|
+
if (applicationController.isRunning) {
|
|
206
|
+
runningApps.push(applicationController)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return runningApps
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
function getWindowOpenedId(windowId: string) {
|
|
214
|
+
const mapWindowFound = windowsOpened.value.find((window: any) => {
|
|
215
|
+
return window[1].state.id === windowId
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
if (mapWindowFound) {
|
|
219
|
+
// @ts-ignore
|
|
220
|
+
return mapWindowFound[1] as IWindowController
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Gets all unique categories from the installed apps
|
|
226
|
+
*/
|
|
227
|
+
const appCategories = computed(() => {
|
|
228
|
+
const categories = new Set<string>()
|
|
229
|
+
|
|
230
|
+
for (const applicationController of apps.values()) {
|
|
231
|
+
if (applicationController.config.category) {
|
|
232
|
+
categories.add(applicationController.config.category)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return Array.from(categories).sort()
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Gets the apps ordered by category
|
|
241
|
+
*/
|
|
242
|
+
const appsByCategory = computed(() => {
|
|
243
|
+
const categorizedApps: {
|
|
244
|
+
[category: string]: IApplicationController[]
|
|
245
|
+
} = {}
|
|
246
|
+
|
|
247
|
+
for (const applicationController of apps.values()) {
|
|
248
|
+
const category = applicationController.config.category || 'other'
|
|
249
|
+
if (!categorizedApps[category]) {
|
|
250
|
+
categorizedApps[category] = []
|
|
251
|
+
}
|
|
252
|
+
categorizedApps[category].push(applicationController)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
for (const category in categorizedApps) {
|
|
256
|
+
categorizedApps[category].sort((a, b) =>
|
|
257
|
+
a.config.title.localeCompare(b.config.title),
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return categorizedApps
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
apps,
|
|
266
|
+
defineApp,
|
|
267
|
+
isAppDefined,
|
|
268
|
+
getAppById,
|
|
269
|
+
isAppRunning,
|
|
270
|
+
launchAppEntry,
|
|
271
|
+
execAppCommand,
|
|
272
|
+
closeApp,
|
|
273
|
+
getWindowOpenedId,
|
|
274
|
+
appsEntries,
|
|
275
|
+
appsRunning,
|
|
276
|
+
windowsOpened,
|
|
277
|
+
appCategories,
|
|
278
|
+
appsByCategory,
|
|
279
|
+
}
|
|
280
|
+
}
|