@owdproject/core 3.0.0-alpha.0
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/LICENSE +674 -0
- package/README.md +141 -0
- package/index.ts +10 -0
- package/module/index.ts +146 -0
- package/package.json +45 -0
- package/runtime/components/Core/Application/CoreApplicationRender.vue +17 -0
- package/runtime/components/Core/Application/CoreApplicationWindowsRender.vue +19 -0
- package/runtime/components/Core/Background/CoreBackground.vue +17 -0
- package/runtime/components/Core/Desktop/CoreDesktop.vue +77 -0
- package/runtime/components/Core/Explorer/CoreExplorerFile.vue +67 -0
- package/runtime/components/Core/Explorer/CoreExplorerFolder.vue +11 -0
- package/runtime/components/Core/Time/CoreTime.vue +53 -0
- package/runtime/components/Core/Window/CoreWindow.vue +157 -0
- package/runtime/components/Core/Window/CoreWindowContent.vue +30 -0
- package/runtime/components/Core/Window/CoreWindowNav.vue +43 -0
- package/runtime/composables/useAppEntries.ts +21 -0
- package/runtime/composables/useApplicationManager.ts +7 -0
- package/runtime/composables/useApplicationState.ts +19 -0
- package/runtime/composables/useDesktopManager.ts +7 -0
- package/runtime/composables/useTerminalManager.ts +3 -0
- package/runtime/core/controllers/ApplicationController.ts +209 -0
- package/runtime/core/controllers/WindowController.ts +413 -0
- package/runtime/core/managers/ApplicationManager.ts +256 -0
- package/runtime/core/managers/DesktopManager.ts +41 -0
- package/runtime/plugins/resize.client.ts +6 -0
- package/runtime/stores/storeDesktop.ts +39 -0
- package/runtime/stores/storeDesktopVolume.ts +17 -0
- package/runtime/stores/storeDesktopWindow.ts +20 -0
- package/runtime/stores/storeDesktopWorkspace.ts +59 -0
- package/runtime/utils/utilsCommon.ts +35 -0
- package/runtime/utils/utilsDebug.ts +30 -0
- package/runtime/utils/utilsDesktop.ts +12 -0
- package/runtime/utils/utilsTerminal.ts +5 -0
- package/runtime/utils/utilsWindow.ts +14 -0
- package/tsconfig.json +3 -0
- package/types/index.d.ts +9 -0
- package/types/owd.d.ts +313 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {ref,provide} from 'vue'
|
|
3
|
+
import {computed} from "@vue/reactivity"
|
|
4
|
+
import {useDesktopManager} from "../../../composables/useDesktopManager"
|
|
5
|
+
import {handleWindowControllerProps} from "../../../utils/utilsWindow"
|
|
6
|
+
|
|
7
|
+
const props = defineProps<{
|
|
8
|
+
window?: IWindowController
|
|
9
|
+
content?: WindowContent
|
|
10
|
+
}>()
|
|
11
|
+
|
|
12
|
+
const emit = defineEmits([
|
|
13
|
+
'resize:start',
|
|
14
|
+
'resize:move',
|
|
15
|
+
'resize:end',
|
|
16
|
+
'drag:start',
|
|
17
|
+
'drag:move',
|
|
18
|
+
'drag:end',
|
|
19
|
+
'close',
|
|
20
|
+
'open',
|
|
21
|
+
'blur',
|
|
22
|
+
'focus',
|
|
23
|
+
'minimize',
|
|
24
|
+
'restore',
|
|
25
|
+
'maximize',
|
|
26
|
+
'unmaximize',
|
|
27
|
+
'toggle-maximize'
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
const desktopManager = useDesktopManager() // used in <style v-bind>
|
|
31
|
+
const windowController: IWindowController = handleWindowControllerProps(props.window)
|
|
32
|
+
|
|
33
|
+
provide<IWindowController>('windowController', windowController)
|
|
34
|
+
provide<WindowContent>('windowContent', props.content ?? {})
|
|
35
|
+
|
|
36
|
+
const isDragging = ref(false)
|
|
37
|
+
const isResizing = ref(false)
|
|
38
|
+
|
|
39
|
+
function onWindowPointerDown() {
|
|
40
|
+
windowController?.actions?.setActive(true)
|
|
41
|
+
windowController?.actions?.bringToFront()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function onWindowDragStart(data: any) {
|
|
45
|
+
isDragging.value = true
|
|
46
|
+
|
|
47
|
+
emit('drag:start', data)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function onWindowDragMove(data: any) {
|
|
51
|
+
emit('drag:move', data)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function onWindowDragEnd(data: { left: number; top: number }) {
|
|
55
|
+
windowController?.actions?.setPosition({
|
|
56
|
+
x: data.left,
|
|
57
|
+
y: data.top
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
isDragging.value = false
|
|
61
|
+
|
|
62
|
+
emit('drag:end', data)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function onWindowResizeStart(data: any) {
|
|
66
|
+
isResizing.value = true
|
|
67
|
+
|
|
68
|
+
emit('resize:start', data)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function onWindowResizeMove(data: any) {
|
|
72
|
+
emit('resize:move', data)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Window end resize event
|
|
77
|
+
*/
|
|
78
|
+
function onWindowResizeEnd(data: { left: number; top: number, width: number, height: number }) {
|
|
79
|
+
isResizing.value = false
|
|
80
|
+
|
|
81
|
+
windowController?.actions?.setPosition({
|
|
82
|
+
x: data.left,
|
|
83
|
+
y: data.top
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
windowController?.actions?.setSize({
|
|
87
|
+
width: data.width,
|
|
88
|
+
height: data.height
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
emit('resize:end', data)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const classes = computed(() => {
|
|
95
|
+
const list = ['owd-window']
|
|
96
|
+
|
|
97
|
+
if (windowController?.state.focused) {
|
|
98
|
+
list.push('owd-window--focused')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (isDragging.value) {
|
|
102
|
+
list.push('owd-window--dragging')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isResizing.value) {
|
|
106
|
+
list.push('owd-window--resizing')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return list
|
|
110
|
+
})
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<template>
|
|
114
|
+
<vue-resizable
|
|
115
|
+
ref="windowResizableController"
|
|
116
|
+
:class="classes"
|
|
117
|
+
:width="windowController?.state.size?.width"
|
|
118
|
+
:height="windowController?.state.size?.height"
|
|
119
|
+
:min-width="windowController?.state.size?.minWidth"
|
|
120
|
+
:min-height="windowController?.state.size?.minHeight"
|
|
121
|
+
:max-width="windowController?.state.size?.maxWidth"
|
|
122
|
+
:max-height="windowController?.state.size?.maxHeight"
|
|
123
|
+
:left="windowController?.state.position?.x"
|
|
124
|
+
:top="windowController?.state.position?.y"
|
|
125
|
+
:active="windowController?.isResizable ? undefined : []"
|
|
126
|
+
@drag:start="onWindowDragStart"
|
|
127
|
+
@drag:move="onWindowDragMove"
|
|
128
|
+
@drag:end="onWindowDragEnd"
|
|
129
|
+
@resize:start="onWindowResizeStart"
|
|
130
|
+
@resize:move="onWindowResizeMove"
|
|
131
|
+
@resize:end="onWindowResizeEnd"
|
|
132
|
+
:style="{zIndex: windowController?.state.position?.z}"
|
|
133
|
+
:drag-selector="windowController?.isDraggable ? '.owd-window-nav__draggable' : '.owd-window-nav__draggable-none'"
|
|
134
|
+
@pointerdown="onWindowPointerDown"
|
|
135
|
+
>
|
|
136
|
+
|
|
137
|
+
<slot/>
|
|
138
|
+
|
|
139
|
+
</vue-resizable>
|
|
140
|
+
</template>
|
|
141
|
+
|
|
142
|
+
<style scoped lang="scss">
|
|
143
|
+
.owd-window {
|
|
144
|
+
position: v-bind('desktopManager.config.windows.position');
|
|
145
|
+
|
|
146
|
+
&--dragging, &--resizing {
|
|
147
|
+
:deep(.owd-window__content) {
|
|
148
|
+
pointer-events: none;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
:deep(.owd-window__content) {
|
|
153
|
+
overflow-y: auto;
|
|
154
|
+
height: 100%;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
</style>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {inject} from "vue"
|
|
3
|
+
import {computed} from "@vue/reactivity"
|
|
4
|
+
|
|
5
|
+
const windowContent = inject<WindowContent>('windowContent')
|
|
6
|
+
|
|
7
|
+
const classes = computed(() => {
|
|
8
|
+
const list = ['owd-window__content']
|
|
9
|
+
|
|
10
|
+
if (windowContent?.centered) {
|
|
11
|
+
list.push('owd-window__content--centered')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (windowContent?.padded) {
|
|
15
|
+
list.push('p-2')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return list
|
|
19
|
+
})
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div :class="classes">
|
|
24
|
+
<slot />
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<style scoped lang="scss">
|
|
29
|
+
|
|
30
|
+
</style>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {inject} from "vue"
|
|
3
|
+
import {computed} from "@vue/reactivity"
|
|
4
|
+
|
|
5
|
+
const windowController = inject<IWindowController>('windowController')
|
|
6
|
+
|
|
7
|
+
const classes = computed(() => {
|
|
8
|
+
const list = ['owd-window-nav']
|
|
9
|
+
|
|
10
|
+
if (typeof windowController?.state.active === 'undefined' || !!windowController?.state.active) {
|
|
11
|
+
list.push('owd-window-nav--active')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (typeof windowController?.state.focused === 'undefined' || !!windowController?.state.focused) {
|
|
15
|
+
list.push('owd-window-nav--focused')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return list
|
|
19
|
+
})
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div :class="classes">
|
|
24
|
+
<div class="owd-window-nav__draggable"/>
|
|
25
|
+
|
|
26
|
+
<slot/>
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<style lang="scss">
|
|
32
|
+
.owd-window-nav {
|
|
33
|
+
position: relative;
|
|
34
|
+
|
|
35
|
+
&__draggable {
|
|
36
|
+
position: absolute;
|
|
37
|
+
top: 0;
|
|
38
|
+
left: 0;
|
|
39
|
+
right: 0;
|
|
40
|
+
height: 100%;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {useApplicationManager} from './useApplicationManager'
|
|
2
|
+
import {computed} from "@vue/reactivity"
|
|
3
|
+
|
|
4
|
+
export function useAppEntries() {
|
|
5
|
+
const applicationManager = useApplicationManager()
|
|
6
|
+
|
|
7
|
+
const sortedAppEntries = function(sortBy: 'title'): Ref<ApplicationEntryWithInherited[]> {
|
|
8
|
+
return computed(() => {
|
|
9
|
+
const currentEntries = [...applicationManager.appsEntries]
|
|
10
|
+
|
|
11
|
+
if (sortBy === 'title') {
|
|
12
|
+
currentEntries.sort((a, b) => (a.title || '').localeCompare(b.title || ''))
|
|
13
|
+
} else if (sortBy === 'category') {
|
|
14
|
+
currentEntries.sort((a, b) => (a.category || '').localeCompare(b.category || ''))
|
|
15
|
+
}
|
|
16
|
+
return currentEntries
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return { sortedAppEntries }
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {defineStore} from "pinia"
|
|
2
|
+
import {ref} from "@vue/reactivity"
|
|
3
|
+
|
|
4
|
+
export const useApplicationState = (appId: string) => {
|
|
5
|
+
return defineStore(`owd/application/${appId}`, () => {
|
|
6
|
+
const meta: Ref<any> = ref({})
|
|
7
|
+
|
|
8
|
+
const windows: Ref<{[windowId: string]: WindowStoredState}> = ref({})
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
windows,
|
|
12
|
+
meta,
|
|
13
|
+
}
|
|
14
|
+
}, {
|
|
15
|
+
persistedState: {
|
|
16
|
+
persist: true
|
|
17
|
+
},
|
|
18
|
+
})()
|
|
19
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import {nanoid} from "nanoid";
|
|
2
|
+
import {WindowController} from "./WindowController";
|
|
3
|
+
import {useApplicationManager} from "../../composables/useApplicationManager"
|
|
4
|
+
import {useApplicationState} from "../../composables/useApplicationState"
|
|
5
|
+
import {useDesktopManager} from "../../composables/useDesktopManager"
|
|
6
|
+
import {debugLog, debugError} from "../../utils/utilsDebug"
|
|
7
|
+
import {useDesktopWorkspaceStore} from "../../stores/storeDesktopWorkspace"
|
|
8
|
+
import {reactive} from '@vue/reactivity'
|
|
9
|
+
|
|
10
|
+
export class ApplicationController implements IApplicationController {
|
|
11
|
+
private readonly applicationManager: IApplicationManager
|
|
12
|
+
private readonly desktopManager: IDesktopManager
|
|
13
|
+
|
|
14
|
+
public readonly id
|
|
15
|
+
public readonly config
|
|
16
|
+
public readonly store
|
|
17
|
+
|
|
18
|
+
public windows = reactive(new Map<string, IWindowController>())
|
|
19
|
+
|
|
20
|
+
public isRunning: boolean = false
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
id: string,
|
|
24
|
+
config: ApplicationConfig
|
|
25
|
+
) {
|
|
26
|
+
this.applicationManager = useApplicationManager()
|
|
27
|
+
this.desktopManager = useDesktopManager()
|
|
28
|
+
|
|
29
|
+
this.id = id
|
|
30
|
+
this.config = config
|
|
31
|
+
this.store = useApplicationState(id)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public async initApplication(): Promise<void> {
|
|
35
|
+
// set as default app for specific purposes
|
|
36
|
+
// todo improve this and move it in a store
|
|
37
|
+
if (this.config.provides) {
|
|
38
|
+
const existingDefault = this.desktopManager.getDefaultApp(this.config.provides.name)
|
|
39
|
+
|
|
40
|
+
if (!existingDefault) {
|
|
41
|
+
this.desktopManager.setDefaultApp(
|
|
42
|
+
this.config.provides.name,
|
|
43
|
+
this,
|
|
44
|
+
this.config.entries[this.config.provides.entry] as ApplicationEntry
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
debugLog(`${this.config.title} has been set as predefined app for "${this.config.provides}"`)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (this.store.$persistedState) {
|
|
52
|
+
await this.store.$persistedState.isReady()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// set default meta values
|
|
56
|
+
this.store.meta = this.config.meta ?? undefined
|
|
57
|
+
|
|
58
|
+
// once app is defined, always run "onReady"
|
|
59
|
+
if (typeof this.config.onReady === 'function') {
|
|
60
|
+
this.config.onReady(this)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// restore application state
|
|
64
|
+
await this.restoreApplication()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* App always tries to restore previous windows
|
|
69
|
+
* and returns a boolean if it succeeded
|
|
70
|
+
*/
|
|
71
|
+
public async restoreApplication() {
|
|
72
|
+
if (typeof this.config.onRestore === 'function') {
|
|
73
|
+
await this.config.onRestore(this)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!this.store.windows || Object.keys(this.store.windows).length === 0) {
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.restoreWindows()
|
|
81
|
+
|
|
82
|
+
this.setRunning(true)
|
|
83
|
+
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private restoreWindows() {
|
|
88
|
+
Object.keys(this.store.windows).map(windowId => {
|
|
89
|
+
const windowStore: WindowStoredState | undefined = this.store.windows[windowId]
|
|
90
|
+
|
|
91
|
+
if (windowStore) {
|
|
92
|
+
this.openWindow(windowStore.model, windowStore, {isRestoring: true})
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
debugLog('Windows have been restored', this.windows)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public openWindow(model: string, windowStoredState: WindowStoredState | undefined, meta?: any) {
|
|
100
|
+
const desktopWorkspaceStore = useDesktopWorkspaceStore()
|
|
101
|
+
|
|
102
|
+
if (!this.config.windows || !this.config.windows.hasOwnProperty(model)) {
|
|
103
|
+
debugError(`Window model "${model}" not found`)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let windowId: string
|
|
108
|
+
|
|
109
|
+
if (!windowStoredState) {
|
|
110
|
+
|
|
111
|
+
windowId = `${model}-${nanoid(6)}`
|
|
112
|
+
|
|
113
|
+
const windowConfig: WindowConfig = this.config.windows[model] as WindowConfig
|
|
114
|
+
const screenHeight = window.innerHeight
|
|
115
|
+
const centerY = (screenHeight - Number(windowConfig.size.height)) / 2
|
|
116
|
+
const positionY = windowConfig.position?.y !== undefined
|
|
117
|
+
? window.scrollY + windowConfig.position.y
|
|
118
|
+
: window.scrollY + centerY
|
|
119
|
+
|
|
120
|
+
this.store.windows[windowId] = {
|
|
121
|
+
model,
|
|
122
|
+
state: {
|
|
123
|
+
id: windowId,
|
|
124
|
+
active: true,
|
|
125
|
+
focused: false,
|
|
126
|
+
position: {
|
|
127
|
+
x: windowConfig.position?.x,
|
|
128
|
+
y: positionY,
|
|
129
|
+
},
|
|
130
|
+
createdAt: +new Date(),
|
|
131
|
+
workspace: desktopWorkspaceStore.active
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
windowStoredState = this.store.windows[windowId]
|
|
136
|
+
|
|
137
|
+
} else {
|
|
138
|
+
|
|
139
|
+
// restore previous id if state is defined
|
|
140
|
+
windowId = windowStoredState.state.id
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const windowConfig = this.config.windows[model] as WindowConfig
|
|
145
|
+
|
|
146
|
+
const windowController = new WindowController(
|
|
147
|
+
this,
|
|
148
|
+
model,
|
|
149
|
+
windowConfig,
|
|
150
|
+
windowStoredState!
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if (!meta?.isRestoring) {
|
|
154
|
+
windowController.actions.bringToFront()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.windows.set(
|
|
158
|
+
windowId,
|
|
159
|
+
windowController,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
this.setRunning(true)
|
|
163
|
+
|
|
164
|
+
return windowController
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public closeWindow(windowId: string) {
|
|
168
|
+
delete this.store.windows[windowId];
|
|
169
|
+
this.windows.delete(windowId);
|
|
170
|
+
|
|
171
|
+
if (this.windows.size === 0) {
|
|
172
|
+
this.applicationManager.closeApp(this.id)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public closeAllWindows() {
|
|
177
|
+
this.store.windows = {}
|
|
178
|
+
this.windows.clear()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
get windowsOpened() {
|
|
182
|
+
return this.windows;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public setRunning(value: boolean): void {
|
|
186
|
+
this.isRunning = value
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// meta
|
|
190
|
+
|
|
191
|
+
get meta() {
|
|
192
|
+
console.log('META', this.id, this.store.meta)
|
|
193
|
+
return this.store.meta
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
getMeta(key: string) {
|
|
197
|
+
return this.meta[key]
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
setMeta(key: string, value: any) {
|
|
201
|
+
this.meta[key] = value
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// commands
|
|
205
|
+
|
|
206
|
+
async execCommand(input: string): Promise<CommandOutput | void> {
|
|
207
|
+
await this.applicationManager.execAppCommand(this.id, input)
|
|
208
|
+
}
|
|
209
|
+
}
|