@owdproject/core 3.1.4-beta.0 → 3.2.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/README.md +6 -6
- package/bin/cli.js +395 -0
- package/bin/desktop.js +5 -0
- package/bin/lib/catalog.js +293 -0
- package/bin/lib/config.js +109 -0
- package/bin/lib/desktopConfig.js +56 -0
- package/bin/lib/generateTemplate.js +389 -0
- package/bin/lib/install.js +408 -0
- package/bin/lib/migrateModulePlayground.config.mjs +164 -0
- package/bin/lib/migrateModulePlayground.mjs +313 -0
- package/bin/lib/npmVersions.js +124 -0
- package/bin/lib/playgroundContext.js +188 -0
- package/bin/lib/scaffold.js +197 -0
- package/bin/lib/status.js +289 -0
- package/bin/lib/validateModule.js +683 -0
- package/bin/lib/workspace.js +182 -0
- package/bin/owd.js +2 -25
- package/bin/tui.js +1379 -0
- package/dist/module.d.mts +13 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +219 -0
- package/dist/types.d.mts +7 -0
- package/module.ts +53 -19
- package/package.json +78 -22
- package/runtime/components/Core/Application/CoreApplicationRender.vue +3 -1
- package/runtime/components/Core/Application/CoreApplicationWindowsRender.vue +28 -7
- package/runtime/components/Core/Desktop/CoreDesktop.vue +11 -7
- package/runtime/components/Core/Window/CoreWindow.vue +45 -9
- package/runtime/components/Core/Window/CoreWindowNav.vue +26 -8
- package/runtime/composables/useApplicationEntries.ts +1 -1
- package/runtime/composables/useApplicationManager.ts +282 -4
- package/runtime/composables/useDesktopManager.ts +46 -4
- package/runtime/composables/useOwdDialogs.ts +13 -0
- package/runtime/composables/useTerminalManager.ts +62 -3
- package/runtime/composables/useWorkspaceOverview.ts +41 -0
- package/runtime/core/controllers/ApplicationController.ts +120 -30
- package/runtime/core/controllers/WindowController.ts +5 -10
- package/runtime/dialogs/owdDialogProvider.ts +81 -0
- package/runtime/plugins/01.owd-dialogs-fallback.ts +15 -0
- package/runtime/stores/storeApplicationMeta.ts +56 -12
- package/runtime/stores/storeDesktop.ts +79 -0
- package/runtime/stores/storeDesktopDefaultApps.ts +53 -0
- package/runtime/stores/storeDesktopExplorer.ts +74 -0
- package/runtime/stores/storeDesktopWorkspace.ts +14 -2
- package/runtime/utils/captureElementToCanvas.ts +21 -0
- package/runtime/utils/explorerEntryPath.ts +10 -0
- package/runtime/utils/resolveDesktopConfigPath.ts +52 -0
- package/runtime/utils/validateOwdUserConfig.ts +44 -0
- package/types/index.d.ts +64 -12
- package/CHANGELOG.md +0 -25
- package/project.json +0 -4
- package/runtime/composables/useClipboardFs.ts +0 -25
- package/runtime/core/managers/ApplicationManager.ts +0 -281
- package/runtime/core/managers/DesktopManager.ts +0 -47
- package/runtime/core/managers/TerminalManager.ts +0 -48
- package/runtime/stores/storeApplication.ts +0 -235
- package/test/basic.test.ts +0 -15
- package/test/fixtures/basic/app.vue +0 -5
- package/test/fixtures/basic/nuxt.config.ts +0 -5
- package/test/fixtures/basic/package.json +0 -5
- package/tsconfig.app.json +0 -31
- package/tsconfig.json +0 -12
- package/tsconfig.spec.json +0 -37
- package/vitest.config.ts +0 -24
|
@@ -4,51 +4,72 @@ import { useApplicationManager } from '../../composables/useApplicationManager'
|
|
|
4
4
|
import { useApplicationWindowsStore } from '../../stores/storeApplicationWindows'
|
|
5
5
|
import { useApplicationMetaStore } from '../../stores/storeApplicationMeta'
|
|
6
6
|
import { useTerminalManager } from '../../composables/useTerminalManager'
|
|
7
|
-
import { useDesktopManager } from '../../composables/useDesktopManager'
|
|
8
7
|
import { debugLog, debugError } from '../../utils/utilDebug'
|
|
8
|
+
import { useDesktopDefaultAppsStore } from '../../stores/storeDesktopDefaultApps'
|
|
9
9
|
import { useDesktopWorkspaceStore } from '../../stores/storeDesktopWorkspace'
|
|
10
|
-
import {
|
|
10
|
+
import { useDesktopWindowStore } from '../../stores/storeDesktopWindow'
|
|
11
|
+
import { shallowRef } from 'vue'
|
|
12
|
+
import { unref } from 'vue'
|
|
11
13
|
|
|
12
14
|
export class ApplicationController implements IApplicationController {
|
|
13
15
|
private readonly applicationManager: IApplicationManager
|
|
14
|
-
private readonly desktopManager: IDesktopManager
|
|
15
16
|
private readonly terminalManager: ITerminalManager
|
|
16
17
|
|
|
17
18
|
public readonly id
|
|
18
19
|
public readonly config
|
|
19
20
|
public readonly storeWindows
|
|
20
|
-
public readonly storeMeta
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
private readonly _windows = shallowRef(new Map<string, IWindowController>())
|
|
23
|
+
|
|
24
|
+
/** Reactive window count for Vue (class getters are not tracked). */
|
|
25
|
+
public readonly openWindowCount = shallowRef(0)
|
|
26
|
+
|
|
27
|
+
get windows(): Map<string, IWindowController> {
|
|
28
|
+
return this._windows.value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private replaceWindows(
|
|
32
|
+
mutator: (next: Map<string, IWindowController>) => void,
|
|
33
|
+
) {
|
|
34
|
+
const next = new Map(this._windows.value)
|
|
35
|
+
mutator(next)
|
|
36
|
+
this._windows.value = next
|
|
37
|
+
this.openWindowCount.value = next.size
|
|
38
|
+
}
|
|
23
39
|
|
|
24
40
|
public isRunning = false
|
|
25
41
|
|
|
26
42
|
constructor(id: string, config: ApplicationConfig) {
|
|
27
43
|
this.applicationManager = useApplicationManager()
|
|
28
44
|
this.terminalManager = useTerminalManager()
|
|
29
|
-
this.desktopManager = useDesktopManager()
|
|
30
45
|
|
|
31
46
|
this.id = id
|
|
32
47
|
this.config = config
|
|
33
48
|
this.storeWindows = useApplicationWindowsStore(id)
|
|
34
|
-
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Lazy meta store — app modules should register state via `useApplicationMetaStore` before `defineDesktopApp`. */
|
|
52
|
+
get storeMeta() {
|
|
53
|
+
return useApplicationMetaStore(this.id)()
|
|
35
54
|
}
|
|
36
55
|
|
|
37
56
|
public async initApplication(): Promise<void> {
|
|
57
|
+
const desktopDefaultAppsStore = useDesktopDefaultAppsStore()
|
|
58
|
+
|
|
38
59
|
// provides
|
|
39
60
|
|
|
40
61
|
// set as default app for specific purposes
|
|
41
62
|
// todo improve this and move it in a store
|
|
42
63
|
if (this.config.provides) {
|
|
43
|
-
const existingDefault =
|
|
64
|
+
const existingDefault = desktopDefaultAppsStore.getDefaultApp(
|
|
44
65
|
this.config.provides.name,
|
|
45
66
|
)
|
|
46
67
|
|
|
47
68
|
if (!existingDefault) {
|
|
48
|
-
|
|
69
|
+
desktopDefaultAppsStore.setDefaultApp(
|
|
49
70
|
this.config.provides.name,
|
|
50
71
|
this,
|
|
51
|
-
this.config.provides.
|
|
72
|
+
this.config.provides.entry,
|
|
52
73
|
)
|
|
53
74
|
|
|
54
75
|
debugLog(
|
|
@@ -127,9 +148,32 @@ export class ApplicationController implements IApplicationController {
|
|
|
127
148
|
}
|
|
128
149
|
})
|
|
129
150
|
|
|
151
|
+
this.normalizeWindowFocus()
|
|
152
|
+
|
|
130
153
|
debugLog('Windows have been restored', this.windows)
|
|
131
154
|
}
|
|
132
155
|
|
|
156
|
+
/** Ensure a single focused window after restore or stale persisted state. */
|
|
157
|
+
private normalizeWindowFocus() {
|
|
158
|
+
if (this.windows.size === 0) {
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let topWindow: IWindowController | undefined
|
|
163
|
+
let topZ = -Infinity
|
|
164
|
+
|
|
165
|
+
for (const window of this.windows.values()) {
|
|
166
|
+
window.setFocus(false)
|
|
167
|
+
const z = window.state.position?.z ?? 0
|
|
168
|
+
if (z >= topZ) {
|
|
169
|
+
topZ = z
|
|
170
|
+
topWindow = window
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
topWindow?.setFocus(true)
|
|
175
|
+
}
|
|
176
|
+
|
|
133
177
|
public openWindow(
|
|
134
178
|
model: string,
|
|
135
179
|
windowStoredState: WindowStoredState | undefined,
|
|
@@ -142,6 +186,20 @@ export class ApplicationController implements IApplicationController {
|
|
|
142
186
|
return
|
|
143
187
|
}
|
|
144
188
|
|
|
189
|
+
if (!windowStoredState && this.config.singleton) {
|
|
190
|
+
const existing = this.getFirstWindowByModel(model)
|
|
191
|
+
if (existing) {
|
|
192
|
+
if (meta && typeof meta === 'object') {
|
|
193
|
+
Object.assign(existing.meta, meta)
|
|
194
|
+
}
|
|
195
|
+
if (!meta?.isRestoring) {
|
|
196
|
+
existing.actions.setActive(true)
|
|
197
|
+
existing.actions.bringToFront()
|
|
198
|
+
}
|
|
199
|
+
return existing
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
145
203
|
let windowId: string
|
|
146
204
|
|
|
147
205
|
if (!windowStoredState) {
|
|
@@ -151,32 +209,54 @@ export class ApplicationController implements IApplicationController {
|
|
|
151
209
|
model
|
|
152
210
|
] as WindowConfig
|
|
153
211
|
const screenHeight = window.innerHeight
|
|
154
|
-
const
|
|
212
|
+
const configHeight = windowConfig.size?.height
|
|
213
|
+
const layoutHeight =
|
|
214
|
+
typeof configHeight === 'number'
|
|
215
|
+
? configHeight
|
|
216
|
+
: typeof configHeight === 'string' && /^\d+$/.test(configHeight)
|
|
217
|
+
? Number(configHeight)
|
|
218
|
+
: 240
|
|
219
|
+
const centerY = (screenHeight - layoutHeight) / 2
|
|
155
220
|
const positionY =
|
|
156
221
|
windowConfig.position?.y !== undefined
|
|
157
222
|
? window.scrollY + windowConfig.position.y
|
|
158
223
|
: window.scrollY + centerY
|
|
159
|
-
|
|
160
|
-
this.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
224
|
+
const cascadeStep = 40
|
|
225
|
+
const cascadeOffset = this.getWindowsByModel(model).length * cascadeStep
|
|
226
|
+
const baseX = windowConfig.position?.x ?? 100
|
|
227
|
+
const desktopWindowStore = useDesktopWindowStore()
|
|
228
|
+
const initialZ = desktopWindowStore.incrementPositionZ()
|
|
229
|
+
|
|
230
|
+
const nextWindows = {
|
|
231
|
+
...this.storeWindows.windows,
|
|
232
|
+
[windowId]: {
|
|
233
|
+
model,
|
|
234
|
+
state: {
|
|
235
|
+
id: windowId,
|
|
236
|
+
active: true,
|
|
237
|
+
focused: false,
|
|
238
|
+
position: {
|
|
239
|
+
x: baseX + cascadeOffset,
|
|
240
|
+
y: positionY + cascadeOffset,
|
|
241
|
+
z: initialZ,
|
|
242
|
+
},
|
|
243
|
+
createdAt: +new Date(),
|
|
244
|
+
workspace: desktopWorkspaceStore.resolveActiveWorkspaceId(),
|
|
169
245
|
},
|
|
170
|
-
|
|
171
|
-
workspace: desktopWorkspaceStore.active,
|
|
246
|
+
meta,
|
|
172
247
|
},
|
|
173
|
-
meta,
|
|
174
248
|
}
|
|
249
|
+
this.storeWindows.windows = nextWindows
|
|
175
250
|
|
|
176
251
|
windowStoredState = this.storeWindows.windows[windowId]
|
|
177
252
|
} else {
|
|
178
253
|
// restore previous id if state is defined
|
|
179
|
-
windowId =
|
|
254
|
+
windowId =
|
|
255
|
+
windowStoredState.state?.id ??
|
|
256
|
+
Object.keys(this.storeWindows.windows).find(
|
|
257
|
+
(key) => this.storeWindows.windows[key] === windowStoredState,
|
|
258
|
+
) ??
|
|
259
|
+
`${model}-restored`
|
|
180
260
|
}
|
|
181
261
|
|
|
182
262
|
const windowConfig = this.config.windows[model] as WindowConfig
|
|
@@ -192,7 +272,9 @@ export class ApplicationController implements IApplicationController {
|
|
|
192
272
|
windowController.actions.bringToFront()
|
|
193
273
|
}
|
|
194
274
|
|
|
195
|
-
this.
|
|
275
|
+
this.replaceWindows((next) => {
|
|
276
|
+
next.set(windowId, windowController)
|
|
277
|
+
})
|
|
196
278
|
|
|
197
279
|
this.setRunning(true)
|
|
198
280
|
|
|
@@ -200,17 +282,25 @@ export class ApplicationController implements IApplicationController {
|
|
|
200
282
|
}
|
|
201
283
|
|
|
202
284
|
public closeWindow(windowId: string) {
|
|
203
|
-
|
|
204
|
-
|
|
285
|
+
if (this.storeWindows.windows[windowId]) {
|
|
286
|
+
const nextWindows = { ...this.storeWindows.windows }
|
|
287
|
+
delete nextWindows[windowId]
|
|
288
|
+
this.storeWindows.windows = nextWindows
|
|
289
|
+
}
|
|
205
290
|
|
|
206
|
-
|
|
291
|
+
this.replaceWindows((next) => {
|
|
292
|
+
next.delete(windowId)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
if (this.openWindowCount.value === 0) {
|
|
207
296
|
this.applicationManager.closeApp(this.id)
|
|
208
297
|
}
|
|
209
298
|
}
|
|
210
299
|
|
|
211
300
|
public closeAllWindows() {
|
|
212
301
|
this.storeWindows.windows = {}
|
|
213
|
-
this.
|
|
302
|
+
this._windows.value = new Map()
|
|
303
|
+
this.openWindowCount.value = 0
|
|
214
304
|
}
|
|
215
305
|
|
|
216
306
|
get windowsOpened() {
|
|
@@ -202,18 +202,13 @@ export class WindowController implements IWindowController {
|
|
|
202
202
|
this.state.position = { x: 0, y: 0, z: 0 }
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
// set focus false on all other windows
|
|
211
|
-
for (const [windowId, window] of applicationManager.windowsOpened) {
|
|
212
|
-
window.actions.setFocus(false)
|
|
205
|
+
for (const window of applicationManager.windowsOpened.value.values()) {
|
|
206
|
+
if (window !== this) {
|
|
207
|
+
window.setFocus(false)
|
|
208
|
+
}
|
|
213
209
|
}
|
|
214
210
|
|
|
215
211
|
this.setFocus(true)
|
|
216
|
-
|
|
217
212
|
this.state.position.z = desktopWindowStore.incrementPositionZ()
|
|
218
213
|
}
|
|
219
214
|
|
|
@@ -283,7 +278,7 @@ export class WindowController implements IWindowController {
|
|
|
283
278
|
maxHeight:
|
|
284
279
|
typeof stateSize.maxHeight !== 'undefined'
|
|
285
280
|
? stateSize.maxHeight
|
|
286
|
-
:
|
|
281
|
+
: configSize.maxHeight,
|
|
287
282
|
}
|
|
288
283
|
}
|
|
289
284
|
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { InjectionKey } from 'vue'
|
|
2
|
+
|
|
3
|
+
/** Options for a yes/no confirmation (theme renders labels and chrome). */
|
|
4
|
+
export interface OwdConfirmDialogOptions {
|
|
5
|
+
title?: string
|
|
6
|
+
message: string
|
|
7
|
+
danger?: boolean
|
|
8
|
+
acceptLabel?: string
|
|
9
|
+
rejectLabel?: string
|
|
10
|
+
/**
|
|
11
|
+
* Theme-specific payload (e.g. Win95 delete dialog passes `toTrash` for slot templates).
|
|
12
|
+
*/
|
|
13
|
+
extras?: Record<string, unknown>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Cross-theme dialog API. Themes implement with PrimeVue, GNOME dialogs, etc.
|
|
18
|
+
* Callers pass user-facing strings (usually from i18n); implementations only render.
|
|
19
|
+
*/
|
|
20
|
+
export interface OwdDialogProvider {
|
|
21
|
+
confirm(options: OwdConfirmDialogOptions): Promise<boolean>
|
|
22
|
+
alert(message: string, options?: { title?: string }): Promise<void>
|
|
23
|
+
prompt(
|
|
24
|
+
message: string,
|
|
25
|
+
defaultValue?: string,
|
|
26
|
+
): Promise<string | null>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const OWD_DIALOG_PROVIDER_KEY: InjectionKey<OwdDialogProvider> =
|
|
30
|
+
Symbol('owd-dialog-provider')
|
|
31
|
+
|
|
32
|
+
function browserGlobals(): {
|
|
33
|
+
confirm: (message?: string) => boolean
|
|
34
|
+
alert: (message?: string) => void
|
|
35
|
+
prompt: (message?: string, defaultText?: string) => string | null
|
|
36
|
+
} | null {
|
|
37
|
+
const g = globalThis as typeof globalThis & {
|
|
38
|
+
confirm?: (message?: string) => boolean
|
|
39
|
+
alert?: (message?: string) => void
|
|
40
|
+
prompt?: (message?: string, defaultText?: string) => string | null
|
|
41
|
+
}
|
|
42
|
+
if (typeof g.confirm !== 'function') return null
|
|
43
|
+
return {
|
|
44
|
+
confirm: g.confirm.bind(g),
|
|
45
|
+
alert:
|
|
46
|
+
typeof g.alert === 'function'
|
|
47
|
+
? g.alert.bind(g)
|
|
48
|
+
: (m?: string) => {
|
|
49
|
+
void m
|
|
50
|
+
},
|
|
51
|
+
prompt:
|
|
52
|
+
typeof g.prompt === 'function'
|
|
53
|
+
? g.prompt.bind(g)
|
|
54
|
+
: () => null,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createBrowserFallbackDialogProvider(): OwdDialogProvider {
|
|
59
|
+
return {
|
|
60
|
+
async confirm(opts) {
|
|
61
|
+
if (import.meta.server) return false
|
|
62
|
+
const w = browserGlobals()
|
|
63
|
+
if (!w) return false
|
|
64
|
+
const text = [opts.title, opts.message].filter(Boolean).join('\n\n')
|
|
65
|
+
return w.confirm(text)
|
|
66
|
+
},
|
|
67
|
+
async alert(message, opts) {
|
|
68
|
+
if (import.meta.server) return
|
|
69
|
+
const w = browserGlobals()
|
|
70
|
+
if (!w) return
|
|
71
|
+
w.alert([opts?.title, message].filter(Boolean).join('\n\n'))
|
|
72
|
+
},
|
|
73
|
+
async prompt(message, defaultValue = '') {
|
|
74
|
+
if (import.meta.server) return null
|
|
75
|
+
const w = browserGlobals()
|
|
76
|
+
if (!w) return null
|
|
77
|
+
const v = w.prompt(message, defaultValue)
|
|
78
|
+
return v === null ? null : v
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineNuxtPlugin } from 'nuxt/app'
|
|
2
|
+
import {
|
|
3
|
+
OWD_DIALOG_PROVIDER_KEY,
|
|
4
|
+
createBrowserFallbackDialogProvider,
|
|
5
|
+
} from '../dialogs/owdDialogProvider'
|
|
6
|
+
|
|
7
|
+
export default defineNuxtPlugin({
|
|
8
|
+
name: 'owd-dialogs-fallback',
|
|
9
|
+
setup(nuxtApp) {
|
|
10
|
+
nuxtApp.vueApp.provide(
|
|
11
|
+
OWD_DIALOG_PROVIDER_KEY,
|
|
12
|
+
createBrowserFallbackDialogProvider(),
|
|
13
|
+
)
|
|
14
|
+
},
|
|
15
|
+
})
|
|
@@ -1,15 +1,59 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref } from 'vue'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
const metaStoreRegistry = new Map<string, ReturnType<typeof defineStore>>()
|
|
5
|
+
/** Store ids created by the generic fallback (only `meta`), replaceable when an app registers real state. */
|
|
6
|
+
const fallbackMetaStoreIds = new Set<string>()
|
|
7
|
+
|
|
8
|
+
type MetaStoreSetup = (() => Record<string, unknown>) | Record<string, unknown>
|
|
9
|
+
|
|
10
|
+
function resolveSetup(states: MetaStoreSetup) {
|
|
11
|
+
return typeof states === 'function' ? states : () => states
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Per-app Pinia meta store factory. The first registration for an app id wins so
|
|
16
|
+
* app modules should call this from their store module (imported in the app plugin
|
|
17
|
+
* before `defineDesktopApp`).
|
|
18
|
+
*/
|
|
19
|
+
export const useApplicationMetaStore = (
|
|
20
|
+
appId: string,
|
|
21
|
+
states?: MetaStoreSetup,
|
|
22
|
+
) => {
|
|
23
|
+
const id = `owd/application/${appId}/meta`
|
|
24
|
+
|
|
25
|
+
if (
|
|
26
|
+
states !== undefined &&
|
|
27
|
+
(!metaStoreRegistry.has(id) || fallbackMetaStoreIds.has(id))
|
|
28
|
+
) {
|
|
29
|
+
fallbackMetaStoreIds.delete(id)
|
|
30
|
+
metaStoreRegistry.set(
|
|
31
|
+
id,
|
|
32
|
+
defineStore(id, resolveSetup(states), {
|
|
33
|
+
persistedState: {
|
|
34
|
+
persist: true,
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!metaStoreRegistry.has(id)) {
|
|
41
|
+
fallbackMetaStoreIds.add(id)
|
|
42
|
+
metaStoreRegistry.set(
|
|
43
|
+
id,
|
|
44
|
+
defineStore(
|
|
45
|
+
id,
|
|
46
|
+
() => ({
|
|
47
|
+
meta: ref<Record<string, unknown>>({}),
|
|
48
|
+
}),
|
|
49
|
+
{
|
|
50
|
+
persistedState: {
|
|
51
|
+
persist: true,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
),
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return metaStoreRegistry.get(id)!
|
|
15
59
|
}
|
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
2
|
import { ref } from 'vue'
|
|
3
3
|
|
|
4
|
+
export type DesktopWindowSurface = 'acrylic' | 'solid'
|
|
5
|
+
|
|
6
|
+
export type DesktopAppearance = 'dark' | 'light'
|
|
7
|
+
|
|
8
|
+
export type DesktopPersonalization = {
|
|
9
|
+
windowSurface: DesktopWindowSurface
|
|
10
|
+
/** Used when windowSurface is solid (CSS color string). */
|
|
11
|
+
windowTint: string
|
|
12
|
+
appearance: DesktopAppearance
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const defaultDesktopPersonalization: DesktopPersonalization = {
|
|
16
|
+
windowSurface: 'acrylic',
|
|
17
|
+
windowTint: '#2d2d30',
|
|
18
|
+
appearance: 'dark',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function mergePersonalization(
|
|
22
|
+
raw: Partial<DesktopPersonalization> | undefined | null,
|
|
23
|
+
): DesktopPersonalization {
|
|
24
|
+
const windowSurface =
|
|
25
|
+
raw?.windowSurface === 'solid' || raw?.windowSurface === 'acrylic'
|
|
26
|
+
? raw.windowSurface
|
|
27
|
+
: defaultDesktopPersonalization.windowSurface
|
|
28
|
+
const appearance =
|
|
29
|
+
raw?.appearance === 'light' || raw?.appearance === 'dark'
|
|
30
|
+
? raw.appearance
|
|
31
|
+
: defaultDesktopPersonalization.appearance
|
|
32
|
+
const tint =
|
|
33
|
+
typeof raw?.windowTint === 'string' && raw.windowTint.trim()
|
|
34
|
+
? raw.windowTint.trim()
|
|
35
|
+
: defaultDesktopPersonalization.windowTint
|
|
36
|
+
return { windowSurface, windowTint: tint, appearance }
|
|
37
|
+
}
|
|
38
|
+
|
|
4
39
|
export const useDesktopStore = defineStore(
|
|
5
40
|
'owd/desktop',
|
|
6
41
|
() => {
|
|
@@ -16,6 +51,12 @@ export const useDesktopStore = defineStore(
|
|
|
16
51
|
window: {
|
|
17
52
|
positionZ: number
|
|
18
53
|
}
|
|
54
|
+
explorer: {
|
|
55
|
+
navExpandedKeys: string[]
|
|
56
|
+
quickAccessPins: { id: string; label: string; path: string; icon?: string }[]
|
|
57
|
+
}
|
|
58
|
+
defaultApps: Record<string, { applicationId: string; entry: string }>
|
|
59
|
+
personalization: DesktopPersonalization
|
|
19
60
|
}>({
|
|
20
61
|
workspace: {
|
|
21
62
|
overview: false,
|
|
@@ -28,10 +69,48 @@ export const useDesktopStore = defineStore(
|
|
|
28
69
|
window: {
|
|
29
70
|
positionZ: 0,
|
|
30
71
|
},
|
|
72
|
+
explorer: {
|
|
73
|
+
navExpandedKeys: [
|
|
74
|
+
'quickAccess',
|
|
75
|
+
'thisPc',
|
|
76
|
+
],
|
|
77
|
+
quickAccessPins: [],
|
|
78
|
+
},
|
|
79
|
+
defaultApps: {},
|
|
80
|
+
personalization: { ...defaultDesktopPersonalization },
|
|
31
81
|
})
|
|
32
82
|
|
|
83
|
+
function setWindowSurface(value: DesktopWindowSurface) {
|
|
84
|
+
const base = mergePersonalization(state.value.personalization)
|
|
85
|
+
state.value.personalization = mergePersonalization({
|
|
86
|
+
...base,
|
|
87
|
+
windowSurface: value,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function setWindowTint(value: string) {
|
|
92
|
+
const v = value.trim()
|
|
93
|
+
if (!v) return
|
|
94
|
+
const base = mergePersonalization(state.value.personalization)
|
|
95
|
+
state.value.personalization = mergePersonalization({
|
|
96
|
+
...base,
|
|
97
|
+
windowTint: v,
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function setAppearance(value: DesktopAppearance) {
|
|
102
|
+
const base = mergePersonalization(state.value.personalization)
|
|
103
|
+
state.value.personalization = mergePersonalization({
|
|
104
|
+
...base,
|
|
105
|
+
appearance: value,
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
33
109
|
return {
|
|
34
110
|
state,
|
|
111
|
+
setWindowSurface,
|
|
112
|
+
setWindowTint,
|
|
113
|
+
setAppearance,
|
|
35
114
|
}
|
|
36
115
|
},
|
|
37
116
|
{
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { useDesktopStore } from './storeDesktop'
|
|
3
|
+
|
|
4
|
+
export const useDesktopDefaultAppsStore = defineStore(
|
|
5
|
+
'owd/desktop/defaultApps',
|
|
6
|
+
() => {
|
|
7
|
+
const desktopStore = useDesktopStore()
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Sets a default app for a given feature
|
|
11
|
+
*/
|
|
12
|
+
function setDefaultApp(
|
|
13
|
+
feature: string,
|
|
14
|
+
application: IApplicationController,
|
|
15
|
+
entry: string,
|
|
16
|
+
) {
|
|
17
|
+
if (!desktopStore.state.hasOwnProperty('defaultApps')) {
|
|
18
|
+
desktopStore.state.defaultApps = {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
desktopStore.state.defaultApps[feature] = {
|
|
22
|
+
applicationId: application.id,
|
|
23
|
+
entry,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Gets the default app config for a given feature
|
|
29
|
+
*/
|
|
30
|
+
function getDefaultApp(feature: string): DefaultAppConfig | undefined {
|
|
31
|
+
if (!desktopStore.state.hasOwnProperty('defaultApps')) {
|
|
32
|
+
desktopStore.state.defaultApps = {}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return desktopStore.state.defaultApps[feature]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Loads default apps from current config
|
|
40
|
+
*/
|
|
41
|
+
function loadDefaultAppsFromConfig(runtimeConfig) {
|
|
42
|
+
if (runtimeConfig.public.desktop.defaultApps) {
|
|
43
|
+
desktopStore.state.defaultApps = runtimeConfig.public.desktop.defaultApps
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
setDefaultApp,
|
|
49
|
+
getDefaultApp,
|
|
50
|
+
loadDefaultAppsFromConfig,
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { useDesktopStore } from './storeDesktop'
|
|
4
|
+
|
|
5
|
+
type ExplorerQuickAccessPin = {
|
|
6
|
+
id: string
|
|
7
|
+
label: string
|
|
8
|
+
path: string
|
|
9
|
+
icon?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const useDesktopExplorerStore = defineStore(
|
|
13
|
+
'owd/desktop/explorer',
|
|
14
|
+
() => {
|
|
15
|
+
const desktopStore = useDesktopStore()
|
|
16
|
+
|
|
17
|
+
const navExpandedKeys = computed(
|
|
18
|
+
() => desktopStore.state.explorer.navExpandedKeys,
|
|
19
|
+
)
|
|
20
|
+
const quickAccessPins = computed(
|
|
21
|
+
() => desktopStore.state.explorer.quickAccessPins,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
function setNavExpandedKeys(keys: string[]) {
|
|
25
|
+
desktopStore.state.explorer.navExpandedKeys = [...new Set(keys)]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function setQuickAccessPins(pins: ExplorerQuickAccessPin[]) {
|
|
29
|
+
desktopStore.state.explorer.quickAccessPins = pins
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function pinQuickAccess(entry: ExplorerQuickAccessPin) {
|
|
33
|
+
const exists = desktopStore.state.explorer.quickAccessPins.some(
|
|
34
|
+
(pin) => pin.path === entry.path,
|
|
35
|
+
)
|
|
36
|
+
if (exists) return
|
|
37
|
+
desktopStore.state.explorer.quickAccessPins.push(entry)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function unpinQuickAccess(path: string) {
|
|
41
|
+
desktopStore.state.explorer.quickAccessPins =
|
|
42
|
+
desktopStore.state.explorer.quickAccessPins.filter(
|
|
43
|
+
(pin) => pin.path !== path,
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function reorderQuickAccess(startIndex: number, endIndex: number) {
|
|
48
|
+
const list = [...desktopStore.state.explorer.quickAccessPins]
|
|
49
|
+
if (
|
|
50
|
+
startIndex < 0 ||
|
|
51
|
+
endIndex < 0 ||
|
|
52
|
+
startIndex >= list.length ||
|
|
53
|
+
endIndex >= list.length ||
|
|
54
|
+
startIndex === endIndex
|
|
55
|
+
) {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
const [item] = list.splice(startIndex, 1)
|
|
59
|
+
if (!item) return
|
|
60
|
+
list.splice(endIndex, 0, item)
|
|
61
|
+
desktopStore.state.explorer.quickAccessPins = list
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
navExpandedKeys,
|
|
66
|
+
quickAccessPins,
|
|
67
|
+
setNavExpandedKeys,
|
|
68
|
+
setQuickAccessPins,
|
|
69
|
+
pinQuickAccess,
|
|
70
|
+
unpinQuickAccess,
|
|
71
|
+
reorderQuickAccess,
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
)
|