@owdproject/core 3.0.0-alpha.6 → 3.0.0-alpha.8

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/README.md CHANGED
@@ -3,13 +3,13 @@
3
3
  </p>
4
4
  <h1 align="center">Open Web Desktop</h1>
5
5
  <h3 align="center">
6
- A framework for building web-based desktop experiences
6
+ A framework for building web-based desktop experiences.
7
7
  </h3>
8
8
 
9
9
  ## Overview
10
10
  Open Web Desktop (OWD) is a framework designed to provide a simple environment for building web-based desktop experiences. It's built with TypeScript on top of the Nuxt.js framework.
11
11
 
12
- [Check out the demo](https://owdproject.github.io/docs) of the `client` base repository, decked with essential modules.
12
+ [Check out the demo](https://owdproject.github.io/docs) of the `client` base repository, decked with a couple of modules.
13
13
 
14
14
  ## Features
15
15
  - Open-source web desktop environment built with Nuxt.js
@@ -26,11 +26,11 @@ Required software:
26
26
  - [Node](https://nodejs.org)
27
27
 
28
28
  When you are ready, bootstrap a new project by running:
29
- ```
29
+ ```bash
30
30
  npm create owd
31
31
  ```
32
32
  Once the process is complete, you can start to develop:
33
- ```
33
+ ```bash
34
34
  # Run the dev server with hot-reload
35
35
  npm run dev
36
36
 
@@ -46,49 +46,58 @@ You can simply install a new application and define it this way.
46
46
 
47
47
  ```typescript
48
48
  // owd.config.ts
49
- import AppTodo from 'owd-app-todo/owd.config'
50
-
51
- export const owdConfig = {
52
- theme: ['github:owdproject/theme-win95', { install: true }],
53
-
54
- // apps to import
49
+ export default defineDesktopConfig({
55
50
  apps: [
56
- './node_modules/owd-app-todo',
57
- ],
58
-
59
- loader: async () => {
60
- await defineDesktopApp(AppTodo)
61
- }
62
- }
51
+ "@owdproject/app-about",
52
+ "@owdproject/app-todo",
53
+ ]
54
+ })
63
55
  ```
64
56
 
65
57
  ## Themes
66
58
  Themes allow you to customize the look and feel of your Open Web Desktop instance.
59
+ You can explore available themes, ranging from popular OS designs to fully custom creations, at [topics/owd-themes](https://github.com/topics/owd-themes).
67
60
 
68
61
  ### Install a new theme
69
- Install a new theme by defining its location in the `theme` property of your configuration file.
70
- Themes can be loaded from local paths or directly from GitHub repositories.
62
+ You can install a new theme by defining it in the `owd.config.ts` configuration.
63
+ Themes can be loaded from a local path or directly from a GitHub repository.
64
+
65
+ #### Using a local theme
66
+
67
+ Clone it first in a folder of your project like `./desktop/theme/theme-gnome`.
71
68
 
72
- **1. Using a local theme:**
69
+ ```bash
70
+ git clone git@github.com:owdproject/theme-gnome.git ./desktop/theme/theme-gnome
71
+ ```
73
72
 
74
- To use a theme from a local folder, specify the path like this:
73
+ Then specify the path theme in `owd.config.ts`:
75
74
 
76
75
  ```typescript
77
76
  // owd.config.ts
78
- export const owdConfig = {
79
- theme: './desktop/themes/owd-theme-win95',
80
- }
77
+ export default defineDesktopConfig({
78
+ theme: './dev/themes/theme-gnome', // define here the theme you just cloned
79
+ apps: [
80
+ ...
81
81
  ```
82
82
 
83
- **2. Using a theme from GitHub:**
83
+ #### Using a theme package
84
84
 
85
- To use a theme directly from a GitHub repository, specify it in this format: `github:<username>/<repository>`:
85
+ Use npm to install the theme package as a dependency of your Open Web Desktop client:
86
+
87
+ ```bash
88
+ npm install @owdproject/theme-gnome
89
+ ```
90
+
91
+ Then specify the path theme in `owd.config.ts`:
86
92
 
87
93
  ```typescript
88
94
  // owd.config.ts
89
- export const owdConfig = {
90
- theme: ['github:owdproject/theme-win95', { install: true }],
91
- }
95
+ import {defineDesktopConfig} from "@owdproject/core/runtime/utils/utilsDesktop"
96
+
97
+ export default defineDesktopConfig({
98
+ theme: '@owdproject/theme-gnome', // define here the theme you just installed
99
+ apps: [
100
+ ...
92
101
  ```
93
102
 
94
103
  ## Contributing
@@ -111,10 +120,10 @@ Then, clone your fork using HTTPS or SSH:
111
120
 
112
121
  ```bash
113
122
  # Clone using HTTPS
114
- git clone https://github.com/<your-username>/owd-client.git
123
+ git clone https://github.com/<your-username>/client.git
115
124
 
116
125
  # Clone using SSH
117
- git clone git@github.com:<your-username>/owd-client.git
126
+ git clone git@github.com:<your-username>/client.git
118
127
  ```
119
128
 
120
129
  Once you have cloned the repository, navigate to the project folder.
@@ -130,10 +139,10 @@ npm run dev
130
139
  This will launch Open Web Desktop in development mode, allowing you to test your changes and see them reflected in real-time.
131
140
  The dev server will be available at http://localhost:3000.
132
141
 
133
- ## Getting Involved
142
+ ## Sponsoring
134
143
 
135
144
  Open Web Desktop is a vast project. The code has been totally rewritten and is now actively being developed, but your support is what makes it all possible. If you're enjoying it and want to see more, consider donating. Your contribution helps keep the project alive and awesome.
136
145
 
137
- ### License
146
+ ## License
138
147
 
139
148
  Open Web Desktop is released under the [GNU General Public License v3](LICENSE).
package/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {defineNuxtModule, createResolver, addComponentsDir, addImportsDir, installModule, addPlugin} from '@nuxt/kit'
2
- import {useDesktopManager} from './runtime/composables/useDesktopManager'
2
+ import {deepMerge} from '@owdproject/core/runtime/utils/utilsCommon'
3
3
  import pkg from './package.json'
4
4
 
5
5
  export default defineNuxtModule({
@@ -9,24 +9,43 @@ export default defineNuxtModule({
9
9
  },
10
10
  async setup(options, nuxt) {
11
11
  const {resolve} = createResolver(import.meta.url)
12
- const desktopManager = useDesktopManager()
13
12
 
14
- await import(nuxt.options.rootDir + '/owd.config.ts')
13
+ const desktopConfig = (await import(nuxt.options.rootDir + '/owd.config.ts')).default
15
14
 
16
- // set core version to runtime variables
15
+ // set core version to runtime config
17
16
 
18
17
  nuxt.options.runtimeConfig.public.coreVersion = pkg.version
19
18
 
20
19
  {
21
20
 
21
+ // install open web desktop theme
22
+
23
+ if (desktopConfig.theme) {
24
+ await installModule(desktopConfig.theme)
25
+ }
26
+
27
+ // install open web desktop modules
28
+
29
+ if (desktopConfig.modules) {
30
+ for (const modulePath of desktopConfig.modules) {
31
+ await installModule(modulePath)
32
+ }
33
+ }
34
+
22
35
  // install open web desktop apps
23
36
 
24
- if (desktopManager.config.apps) {
25
- for (const appPath of desktopManager.config.apps) {
37
+ if (desktopConfig.apps) {
38
+ for (const appPath of desktopConfig.apps) {
26
39
  await installModule(appPath)
27
40
  }
28
41
  }
29
42
 
43
+ // assign open web desktop config to runtime config
44
+ nuxt.options.runtimeConfig.public.desktop = deepMerge(
45
+ nuxt.options.runtimeConfig.public.desktop,
46
+ desktopConfig
47
+ )
48
+
30
49
  }
31
50
 
32
51
  {
@@ -45,11 +64,12 @@ export default defineNuxtModule({
45
64
 
46
65
  // install tailwind
47
66
 
48
- const tailwindPaths = nuxt.options.appConfig?.owd?.tailwindPaths || []
67
+ const tailwindPaths = nuxt.options.runtimeConfig?.owd?.tailwindPaths || []
49
68
  tailwindPaths.push('./runtime/components/**/*.{vue,mjs,ts}') // Aggiungi sempre questo al core
50
69
 
51
70
  nuxt.options.tailwindcss = nuxt.options.tailwindcss || {}
52
71
  nuxt.options.tailwindcss.config = nuxt.options.tailwindcss.config || {}
72
+ // @ts-ignore
53
73
  nuxt.options.tailwindcss.config.content = tailwindPaths
54
74
 
55
75
  await installModule('@nuxtjs/tailwindcss', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owdproject/core",
3
- "version": "3.0.0-alpha.6",
3
+ "version": "3.0.0-alpha.8",
4
4
  "description": "Open Web Desktop core",
5
5
  "keywords": [
6
6
  "web",
@@ -43,5 +43,8 @@
43
43
  "sanitize.css": "^13.0.0",
44
44
  "sass": "^1.86.0",
45
45
  "vue-resizable": "^2.1.7"
46
+ },
47
+ "resolutions": {
48
+ "vite": "^6.3.2"
46
49
  }
47
50
  }
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import {onMounted, onUnmounted} from "vue"
3
+ import {useRuntimeConfig} from "nuxt/app"
3
4
  import {computed, toRaw} from "@vue/reactivity"
4
5
  import {useDesktopManager} from "../../../composables/useDesktopManager"
5
6
  import {useApplicationManager} from "../../../composables/useApplicationManager"
@@ -10,16 +11,21 @@ const props = withDefaults(defineProps<{
10
11
  windows?: DesktopWindowsConfig
11
12
  systemBar?: DesktopSystemBarConfig
12
13
  dockBar?: DesktopDockBarConfig
14
+ workspaces?: DesktopWorkspacesConfig
13
15
  }>(), {
14
16
  windows: {
15
17
  position: 'fixed',
16
18
  },
17
19
  systemBar: {},
18
20
  dockBar: {},
21
+ workspaces: {},
19
22
  })
20
23
 
24
+ // todo move to owd initialization
25
+
21
26
  const desktopManager = useDesktopManager()
22
27
  const applicationManager = useApplicationManager()
28
+ const runtimeConfig = useRuntimeConfig()
23
29
 
24
30
  // create firsts workspaces if not available
25
31
  const desktopStore = useDesktopStore()
@@ -33,11 +39,14 @@ if (desktopStore.$persistedState) {
33
39
  desktopWorkspaceStore.setupWorkspaces()
34
40
  }
35
41
 
42
+ desktopManager.setConfig(runtimeConfig.public.desktop)
43
+
36
44
  // override desktop configurations
37
45
  desktopManager.setConfig({
38
46
  windows: toRaw(props.windows),
39
- systemBar: toRaw( props.systemBar),
47
+ systemBar: toRaw(props.systemBar),
40
48
  dockBar: toRaw(props.dockBar),
49
+ workspaces: toRaw(props.workspaces),
41
50
  })
42
51
 
43
52
  // desktop resize
@@ -114,13 +114,13 @@ const classes = computed(() => {
114
114
  <vue-resizable
115
115
  ref="windowResizableController"
116
116
  :class="classes"
117
- :width="windowController?.state.size?.width"
118
- :height="windowController?.state.size?.height"
119
- :min-height="windowController?.state.size?.minHeight"
120
- :max-width="windowController?.state.size?.maxWidth"
121
- :max-height="windowController?.state.size?.maxHeight"
122
- :left="windowController?.state.position?.x"
123
- :top="windowController?.state.position?.y"
117
+ :width="windowController?.size.width"
118
+ :height="windowController?.size.height"
119
+ :min-height="windowController?.size.minHeight"
120
+ :max-width="windowController?.size.maxWidth"
121
+ :max-height="windowController?.size.maxHeight"
122
+ :left="windowController?.position?.x"
123
+ :top="windowController?.position?.y"
124
124
  :active="windowController?.isResizable ? undefined : []"
125
125
  @drag:start="onWindowDragStart"
126
126
  @drag:move="onWindowDragMove"
@@ -128,7 +128,7 @@ const classes = computed(() => {
128
128
  @resize:start="onWindowResizeStart"
129
129
  @resize:move="onWindowResizeMove"
130
130
  @resize:end="onWindowResizeEnd"
131
- :style="{zIndex: windowController?.state.position?.z}"
131
+ :style="{zIndex: windowController?.position?.z}"
132
132
  :drag-selector="windowController?.isDraggable ? '.owd-window-nav__draggable' : '.owd-window-nav__draggable-none'"
133
133
  @pointerdown="onWindowPointerDown"
134
134
  >
@@ -1,3 +1,7 @@
1
+ import {TerminalManager} from "../core/managers/TerminalManager";
2
+
3
+ const terminalManager = new TerminalManager()
4
+
1
5
  export function useTerminalManager() {
2
- // todo reimplement index of applications cmds
6
+ return terminalManager
3
7
  }
@@ -2,6 +2,7 @@ import {nanoid} from "nanoid";
2
2
  import {WindowController} from "./WindowController";
3
3
  import {useApplicationManager} from "../../composables/useApplicationManager"
4
4
  import {useApplicationState} from "../../composables/useApplicationState"
5
+ import {useTerminalManager} from "../../composables/useTerminalManager"
5
6
  import {useDesktopManager} from "../../composables/useDesktopManager"
6
7
  import {debugLog, debugError} from "../../utils/utilsDebug"
7
8
  import {useDesktopWorkspaceStore} from "../../stores/storeDesktopWorkspace"
@@ -10,6 +11,7 @@ import {reactive} from '@vue/reactivity'
10
11
  export class ApplicationController implements IApplicationController {
11
12
  private readonly applicationManager: IApplicationManager
12
13
  private readonly desktopManager: IDesktopManager
14
+ private readonly terminalManager: ITerminalManager
13
15
 
14
16
  public readonly id
15
17
  public readonly config
@@ -24,6 +26,7 @@ export class ApplicationController implements IApplicationController {
24
26
  config: ApplicationConfig
25
27
  ) {
26
28
  this.applicationManager = useApplicationManager()
29
+ this.terminalManager = useTerminalManager()
27
30
  this.desktopManager = useDesktopManager()
28
31
 
29
32
  this.id = id
@@ -32,6 +35,9 @@ export class ApplicationController implements IApplicationController {
32
35
  }
33
36
 
34
37
  public async initApplication(): Promise<void> {
38
+
39
+ // provides
40
+
35
41
  // set as default app for specific purposes
36
42
  // todo improve this and move it in a store
37
43
  if (this.config.provides) {
@@ -48,20 +54,34 @@ export class ApplicationController implements IApplicationController {
48
54
  }
49
55
  }
50
56
 
57
+ // terminal
58
+
59
+ if (this.config.commands) {
60
+ for (const commandKey of Object.keys(this.config.commands)) {
61
+ this.terminalManager.addCommand({
62
+ applicationId: this.id,
63
+ name: commandKey
64
+ })
65
+ }
66
+ }
67
+
68
+ // store
69
+
51
70
  if (this.store.$persistedState) {
52
71
  await this.store.$persistedState.isReady()
53
72
  }
54
73
 
55
74
  // set default meta values
56
- this.store.meta = this.config.meta ?? undefined
75
+ this.store.meta = this.config.meta ?? {}
76
+
77
+ // restore application state
78
+ await this.restoreApplication()
57
79
 
58
80
  // once app is defined, always run "onReady"
59
81
  if (typeof this.config.onReady === 'function') {
82
+ // todo transform in hook
60
83
  this.config.onReady(this)
61
84
  }
62
-
63
- // restore application state
64
- await this.restoreApplication()
65
85
  }
66
86
 
67
87
  /**
@@ -70,6 +90,7 @@ export class ApplicationController implements IApplicationController {
70
90
  */
71
91
  public async restoreApplication() {
72
92
  if (typeof this.config.onRestore === 'function') {
93
+ // todo transform in hook
73
94
  await this.config.onRestore(this)
74
95
  }
75
96
 
@@ -203,6 +224,6 @@ export class ApplicationController implements IApplicationController {
203
224
  // commands
204
225
 
205
226
  async execCommand(command: string): Promise<CommandOutput | void> {
206
- await this.applicationManager.execAppCommand(this.id, command)
227
+ return this.applicationManager.execAppCommand(this.id, command)
207
228
  }
208
229
  }
@@ -59,6 +59,7 @@ export class WindowController implements IWindowController {
59
59
  ) {
60
60
  this.application = application
61
61
  this.model = model
62
+
62
63
  this.storedState = windowStoredState
63
64
 
64
65
  this.setConfig(windowConfig)
@@ -119,6 +120,26 @@ export class WindowController implements IWindowController {
119
120
  if (typeof config.destroyable !== 'undefined') {
120
121
  this.config.destroyable = config.destroyable
121
122
  }
123
+
124
+ const DEFAULT_OVERRIDABLE = {
125
+ draggable: true,
126
+ resizable: false,
127
+ position: false,
128
+ size: false,
129
+ maximized: false,
130
+ destroyable: false,
131
+ minimizable: false,
132
+ maximizable: false,
133
+ }
134
+
135
+ DEFAULT_OVERRIDABLE.position = DEFAULT_OVERRIDABLE.draggable
136
+ DEFAULT_OVERRIDABLE.size = DEFAULT_OVERRIDABLE.resizable
137
+ DEFAULT_OVERRIDABLE.maximized = DEFAULT_OVERRIDABLE.maximizable
138
+
139
+ this.config.overridable = {
140
+ ...DEFAULT_OVERRIDABLE,
141
+ ...(config.overridable || {})
142
+ }
122
143
  }
123
144
 
124
145
  // state
@@ -128,17 +149,20 @@ export class WindowController implements IWindowController {
128
149
  }
129
150
 
130
151
  private restoreState() {
131
- if (!this.state.pinned) this.state.pinned = this.config.pinned
132
- if (!this.state.position) this.state.position = deepClone(this.config.position)
152
+ const overridable = this.config.overridable || {}
133
153
 
134
- if (!this.state.minimizable) this.state.minimizable = this.config.minimizable
135
- if (!this.state.maximized) this.state.maximized = this.config.maximized
136
- if (!this.state.destroyable) this.state.destroyable = this.config.destroyable
137
- if (!this.state.draggable) this.state.draggable = this.config.draggable
138
- if (!this.state.resizable) this.state.resizable = this.config.resizable
154
+ for (const key in overridable) {
155
+ if (overridable[key as keyof typeof overridable]) {
156
+ const stateKey = key as keyof WindowState
139
157
 
140
- if (!this.state.size) {
141
- this.state.size = deepClone(this.config.size)
158
+ if (typeof this.state[stateKey] === 'undefined') {
159
+ if (typeof this.config[stateKey] === 'object') {
160
+ this.state[stateKey] = deepClone(this.config[stateKey])
161
+ } else {
162
+ this.state[stateKey] = !!this.config[stateKey]
163
+ }
164
+ }
165
+ }
142
166
  }
143
167
  }
144
168
 
@@ -210,54 +234,30 @@ export class WindowController implements IWindowController {
210
234
  return this.application.config.icon
211
235
  }
212
236
 
213
- // sizes
214
-
215
- get width() {
216
- if (typeof this.state.size?.width === 'undefined') {
217
- return this.config.size.width
218
- }
219
-
220
- return this.state.size.width
221
- }
222
-
223
- get maxWidth() {
224
- if (typeof this.state.size?.maxWidth === 'undefined') {
225
- return this.config.size.maxWidth
226
- }
227
-
228
- return this.state.size.maxWidth
229
- }
230
-
231
- get minWidth() {
232
- if (typeof this.state.size?.minWidth === 'undefined') {
233
- return this.config.size.minWidth
234
- }
235
-
236
- return this.state.size.minWidth
237
- }
237
+ // position
238
238
 
239
- get height() {
240
- if (typeof this.state.size?.height === 'undefined') {
241
- return this.config.size.height
239
+ get position() {
240
+ if (typeof this.state.position === 'undefined') {
241
+ return this.config.position
242
242
  }
243
243
 
244
- return this.state.size.height
244
+ return this.state.position
245
245
  }
246
246
 
247
- get maxHeight() {
248
- if (typeof this.state.size?.maxHeight === 'undefined') {
249
- return this.config.size.maxHeight ?? 600
250
- }
247
+ // sizes
251
248
 
252
- return this.state.size.maxHeight
253
- }
249
+ get size() {
250
+ const stateSize = this.state.size || {}
251
+ const configSize = this.config.size || {}
254
252
 
255
- get minHeight() {
256
- if (typeof this.state.size?.minHeight === 'undefined') {
257
- return this.config.size.minHeight
253
+ return {
254
+ width: typeof stateSize.width !== 'undefined' ? stateSize.width : configSize.width,
255
+ height: typeof stateSize.height !== 'undefined' ? stateSize.height : configSize.height,
256
+ minWidth: typeof stateSize.minWidth !== 'undefined' ? stateSize.minWidth : configSize.minWidth,
257
+ maxWidth: typeof stateSize.maxWidth !== 'undefined' ? stateSize.maxWidth : configSize.maxWidth,
258
+ minHeight: typeof stateSize.minHeight !== 'undefined' ? stateSize.minHeight : configSize.minHeight,
259
+ maxHeight: typeof stateSize.maxHeight !== 'undefined' ? stateSize.maxHeight : (configSize.maxHeight ?? 600),
258
260
  }
259
-
260
- return this.state.size.minHeight
261
261
  }
262
262
 
263
263
  private setSize(data: { width: number, height: number}) {
@@ -272,11 +272,11 @@ export class WindowController implements IWindowController {
272
272
  // minimize
273
273
 
274
274
  get isMinimizable() {
275
- if (typeof this.state.minimizable === 'undefined') {
276
- return !!this.config.minimizable
275
+ if (this.config.overridable?.minimizable) {
276
+ return !!this.state.minimizable
277
277
  }
278
278
 
279
- return this.state.minimizable
279
+ return !!this.config.minimizable
280
280
  }
281
281
 
282
282
  private minimize() {
@@ -304,11 +304,11 @@ export class WindowController implements IWindowController {
304
304
  }
305
305
 
306
306
  get isMaximized() {
307
- if (typeof this.state.maximized === 'undefined') {
308
- return !!this.config.maximized
307
+ if (this.config.overridable?.maximized) {
308
+ return !!this.state.maximized
309
309
  }
310
310
 
311
- return this.state.maximized
311
+ return !!this.config.maximized
312
312
  }
313
313
 
314
314
  private toggleMaximize() {
@@ -341,7 +341,11 @@ export class WindowController implements IWindowController {
341
341
  // destroy
342
342
 
343
343
  get isDestroyable() {
344
- return !!this.state.destroyable
344
+ if (this.config.overridable?.destroyable) {
345
+ return !!this.state.destroyable
346
+ }
347
+
348
+ return !!this.config.destroyable
345
349
  }
346
350
 
347
351
  private destroy() {
@@ -356,12 +360,20 @@ export class WindowController implements IWindowController {
356
360
 
357
361
  // draggable
358
362
  get isDraggable() {
359
- return !!this.state.draggable
363
+ if (this.config.overridable?.draggable) {
364
+ return !!this.state.draggable
365
+ }
366
+
367
+ return !!this.config.draggable
360
368
  }
361
369
 
362
370
  // resizable
363
371
  get isResizable() {
364
- return !!this.state.resizable
372
+ if (this.config.overridable?.resizable) {
373
+ return !!this.state.resizable
374
+ }
375
+
376
+ return !!this.config.resizable
365
377
  }
366
378
 
367
379
  // workspace
@@ -69,7 +69,7 @@ export class ApplicationManager implements IApplicationManager {
69
69
  * @param id
70
70
  * @param entryKey
71
71
  */
72
- public async launchAppEntry(id: string, entryKey: string) {
72
+ public async launchAppEntry(id: string, entryKey: string): Promise<IApplicationController | undefined | void> {
73
73
  if (!this.isAppDefined(id)) {
74
74
  throw Error(`App "${id}" is not defined`);
75
75
  }
@@ -91,7 +91,7 @@ export class ApplicationManager implements IApplicationManager {
91
91
  * @param id
92
92
  * @param command
93
93
  */
94
- public async execAppCommand(id: string, command: string) {
94
+ public async execAppCommand(id: string, command: string): Promise<CommandOutput> {
95
95
  if (!this.isAppDefined(id)) {
96
96
  throw Error(`App "${id}" is not defined`);
97
97
  }
@@ -106,12 +106,11 @@ export class ApplicationManager implements IApplicationManager {
106
106
  }
107
107
 
108
108
  const commandFn: any = applicationController.config.commands![baseCommand]
109
-
110
- commandFn(applicationController, commandSplit.slice(1))
109
+ const commandOutput = await commandFn(applicationController, commandSplit.slice(1))
111
110
 
112
111
  applicationController.setRunning(true)
113
112
 
114
- return applicationController;
113
+ return commandOutput;
115
114
  }
116
115
 
117
116
  /**
@@ -149,7 +148,7 @@ export class ApplicationManager implements IApplicationManager {
149
148
  title: entry.title !== undefined ? entry.title : applicationController.config.title,
150
149
  icon: entry.icon !== undefined ? entry.icon : applicationController.config.icon,
151
150
  category: entry.category !== undefined ? entry.category : applicationController.config.category,
152
- visibility: entry.visibility,
151
+ visibility: entry.visibility ?? 'primary',
153
152
  command: entry.command
154
153
  })
155
154
  }
@@ -2,6 +2,7 @@ import {deepMerge} from "../../utils/utilsCommon"
2
2
 
3
3
  export class DesktopManager {
4
4
  public config: DesktopConfig = {} as DesktopConfig
5
+
5
6
  private defaultApps: DefaultAppsConfig = {}
6
7
 
7
8
  constructor() {
@@ -30,7 +31,7 @@ export class DesktopManager {
30
31
 
31
32
  private loadDefaultAppsFromConfig() {
32
33
  if (this.config.defaultApps) {
33
- this.defaultApps = { ...this.config.defaultApps }
34
+ this.defaultApps = {...this.config.defaultApps}
34
35
  }
35
36
  }
36
37
  }
@@ -0,0 +1,42 @@
1
+ import {useApplicationManager} from "../../composables/useApplicationManager"
2
+ import {debugLog} from "../../utils/utilsDebug"
3
+
4
+ export class TerminalManager {
5
+ private commands: Map<string, TerminalCommand> = new Map()
6
+
7
+ constructor() {
8
+ }
9
+
10
+ public addCommand(command: TerminalCommand) {
11
+ for (const existingCommand of this.commands.keys()) {
12
+ if (existingCommand.startsWith(command.name) || command.name.startsWith(existingCommand)) {
13
+ throw new Error(`Command prefix conflict with "${existingCommand}"`)
14
+ }
15
+ }
16
+
17
+ this.commands.set(command.name, command)
18
+ debugLog(`Registered terminal command: ${command.name} → ${command.applicationId}`)
19
+ }
20
+
21
+ public listCommands(): string[] {
22
+ return Array.from(this.commands.keys())
23
+ }
24
+
25
+ public async execCommand(input: string): Promise<CommandOutput | void> {
26
+ const applicationManager = useApplicationManager()
27
+
28
+ const [name, ...args] = input.trim().split(/\s+/)
29
+
30
+ if (!name) {
31
+ return
32
+ }
33
+
34
+ if (!this.commands.has(name)) {
35
+ throw new Error(`Command "${name}" not found`)
36
+ }
37
+
38
+ const cmd = this.commands.get(name)!
39
+
40
+ return applicationManager.execAppCommand(cmd.applicationId, input)
41
+ }
42
+ }
@@ -1,3 +1,5 @@
1
+ import type {Nuxt} from "@nuxt/schema";
2
+
1
3
  export function normalizeApplicationConfig(config: ApplicationConfig): ApplicationConfig {
2
4
  const normalizedEntries: Record<string, ApplicationEntry> = {}
3
5
 
@@ -20,12 +22,12 @@ export function normalizeApplicationConfig(config: ApplicationConfig): Applicati
20
22
  }
21
23
  }
22
24
 
23
- export function registerTailwindPath(nuxt, path: string) {
24
- nuxt.options.appConfig = nuxt.options.appConfig || {}
25
- nuxt.options.appConfig.owd = nuxt.options.appConfig.owd || {}
26
- nuxt.options.appConfig.owd.tailwindPaths = nuxt.options.appConfig.owd.tailwindPaths || []
25
+ export function registerTailwindPath(nuxt: Nuxt, path: string) {
26
+ nuxt.options.runtimeConfig = nuxt.options.runtimeConfig || {}
27
+ nuxt.options.runtimeConfig.owd = nuxt.options.runtimeConfig.owd || {}
28
+ nuxt.options.runtimeConfig.owd.tailwindPaths = nuxt.options.runtimeConfig.owd.tailwindPaths || []
27
29
 
28
- if (!nuxt.options.appConfig.owd.tailwindPaths.includes(path)) {
29
- nuxt.options.appConfig.owd.tailwindPaths.push(path)
30
+ if (!nuxt.options.runtimeConfig.owd.tailwindPaths.includes(path)) {
31
+ nuxt.options.runtimeConfig.owd.tailwindPaths.push(path)
30
32
  }
31
33
  }
@@ -1,7 +1,30 @@
1
- export function deepClone(obj: any) {
1
+ export function deepClone<T>(obj: T): T {
2
2
  return JSON.parse(JSON.stringify(obj))
3
3
  }
4
4
 
5
+ export function deepEqual(obj1, obj2) {
6
+ const keys1 = Object.keys(obj1);
7
+ const keys2 = Object.keys(obj2);
8
+ if (keys1.length !== keys2.length) {
9
+ return false;
10
+ }
11
+ for (const key of keys1) {
12
+ if (!obj2.hasOwnProperty(key)) {
13
+ return false;
14
+ }
15
+ if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') {
16
+ if (!deepEqual(obj1[key], obj2[key])) {
17
+ return false;
18
+ }
19
+ } else {
20
+ if (obj1[key] !== obj2[key]) {
21
+ return false;
22
+ }
23
+ }
24
+ }
25
+ return true;
26
+ }
27
+
5
28
  export function deepMerge<T>(target: T, source: Partial<T>): T {
6
29
  function isObject(value: any): value is Record<string, any> {
7
30
  return (
@@ -7,7 +7,5 @@ export function defineDesktopApp(config: ApplicationConfig) {
7
7
  }
8
8
 
9
9
  export function defineDesktopConfig(config: DesktopConfig) {
10
- const desktopManager = useDesktopManager()
11
- desktopManager.setConfig(config)
12
- return true
10
+ return config
13
11
  }
@@ -7,6 +7,7 @@ export function handleWindowControllerProps(windowController: any) {
7
7
 
8
8
  return {
9
9
  ...windowController,
10
+ ...windowController.config,
10
11
  state: {
11
12
  ...windowController.config,
12
13
  },
package/types/owd.d.ts CHANGED
@@ -11,12 +11,12 @@ interface IApplicationManager {
11
11
 
12
12
  get appsByCategory(): { [category: string]: IApplicationController[] }
13
13
 
14
- defineApp(appId: string, config: ApplicationConfig): Promise<IApplicationController>
14
+ defineApp(id: string, config: ApplicationConfig): Promise<IApplicationController>
15
15
 
16
16
  closeApp(id: string): void
17
17
 
18
- launchAppEntry(appId: string, entry: string): Promise<IApplicationController | undefined>
19
- execAppCommand(appId: string, command: string): Promise<IApplicationController | undefined>
18
+ launchAppEntry(id: string, entry: string): Promise<IApplicationController | undefined | void>
19
+ execAppCommand(id: string, command: string): Promise<CommandOutput | void>
20
20
 
21
21
  isAppDefined(id: string): boolean
22
22
 
@@ -56,7 +56,7 @@ interface ApplicationConfigProvide {
56
56
 
57
57
  type IApplicationMeta = { [key: string]: any }
58
58
 
59
- interface sIApplicationController {
59
+ interface IApplicationController {
60
60
  id: string
61
61
  config: ApplicationConfig
62
62
 
@@ -81,7 +81,7 @@ interface sIApplicationController {
81
81
 
82
82
  closeAllWindows(): void
83
83
 
84
- execCommand(command: string)
84
+ execCommand(command: string): Promise<CommandOutput | void>
85
85
 
86
86
  get windowsOpened(): Reactive<Map<string, IWindowController>>
87
87
  }
@@ -102,17 +102,7 @@ interface IWindowController {
102
102
  get icon(): string | undefined
103
103
 
104
104
  // sizes
105
- get width(): WindowSizeValue
106
-
107
- get maxWidth(): WindowSizeValue
108
-
109
- get minWidth(): WindowSizeValue
110
-
111
- get height(): WindowSizeValue
112
-
113
- get maxHeight(): WindowSizeValue
114
-
115
- get minHeight(): WindowSizeValue
105
+ get size(): WindowSize
116
106
 
117
107
  // minimize
118
108
  get isMinimizable(): boolean
@@ -170,7 +160,6 @@ interface WindowConfig {
170
160
  title?: string
171
161
  category?: string
172
162
  icon?: string
173
- pinned?: boolean
174
163
 
175
164
  component?: Raw<Component>
176
165
 
@@ -195,6 +184,8 @@ interface WindowConfig {
195
184
 
196
185
  // draggable
197
186
  resizable?: boolean
187
+
188
+ overridable?: Partial<Record<'position' | 'size' | 'maximized' | 'draggable' | 'resizable' | 'destroyable' | 'minimizable', boolean>>
198
189
  }
199
190
 
200
191
  interface WindowStoredState {
@@ -212,7 +203,6 @@ interface WindowState {
212
203
  createdAt: number
213
204
 
214
205
  category?: string
215
- pinned?: boolean
216
206
  workspace: string
217
207
 
218
208
  // position
@@ -286,13 +276,17 @@ interface IDesktopManager {
286
276
  }
287
277
 
288
278
  interface DesktopConfig {
289
- name?: string
290
- compatibility?: string
279
+ theme?: string
280
+ modules?: string[]
291
281
  apps?: string[]
282
+
283
+ name?: string
292
284
  defaultApps?: DefaultAppsConfig
293
285
  features?: string[]
286
+
294
287
  systemBar?: DesktopSystemBarConfig
295
288
  dockBar?: DesktopDockBarConfig
289
+ workspaces?: DesktopWorkspacesConfig
296
290
  }
297
291
 
298
292
  interface DesktopWindowsConfig {
@@ -310,11 +304,15 @@ interface DesktopSystemBarConfig {
310
304
  startButton?: boolean
311
305
  }
312
306
 
307
+ interface DesktopWorkspacesConfig {
308
+ enabled?: boolean
309
+ }
310
+
313
311
  // TERMINAL
314
312
 
315
- interface CommandFn {
316
- fn: (app: IApplicationController, args: string[]) => string | CommandOutput | void
317
- app: IApplicationController
313
+ type TerminalCommand = {
314
+ name: string
315
+ applicationId: string
318
316
  }
319
317
 
320
318
  interface CommandOutput {
package/types/index.d.ts DELETED
@@ -1,9 +0,0 @@
1
- declare module 'nuxt/schema' {
2
- interface AppConfig {
3
- owd: {
4
- apps: string[]
5
- }
6
- }
7
- }
8
-
9
- export {}