@owdproject/core 3.1.4 → 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.
Files changed (57) hide show
  1. package/README.md +6 -6
  2. package/bin/cli.js +395 -0
  3. package/bin/desktop.js +5 -0
  4. package/bin/lib/catalog.js +293 -0
  5. package/bin/lib/config.js +109 -0
  6. package/bin/lib/desktopConfig.js +56 -0
  7. package/bin/lib/generateTemplate.js +389 -0
  8. package/bin/lib/install.js +408 -0
  9. package/bin/lib/migrateModulePlayground.config.mjs +164 -0
  10. package/bin/lib/migrateModulePlayground.mjs +313 -0
  11. package/bin/lib/npmVersions.js +124 -0
  12. package/bin/lib/playgroundContext.js +188 -0
  13. package/bin/lib/scaffold.js +197 -0
  14. package/bin/lib/status.js +289 -0
  15. package/bin/lib/validateModule.js +683 -0
  16. package/bin/lib/workspace.js +182 -0
  17. package/bin/owd.js +2 -25
  18. package/bin/tui.js +1379 -0
  19. package/dist/module.d.mts +13 -0
  20. package/dist/module.json +9 -0
  21. package/dist/module.mjs +219 -0
  22. package/dist/types.d.mts +7 -0
  23. package/module.ts +38 -12
  24. package/package.json +78 -22
  25. package/runtime/components/Core/Application/CoreApplicationRender.vue +3 -1
  26. package/runtime/components/Core/Application/CoreApplicationWindowsRender.vue +28 -7
  27. package/runtime/components/Core/Window/CoreWindow.vue +42 -8
  28. package/runtime/components/Core/Window/CoreWindowNav.vue +26 -8
  29. package/runtime/composables/useApplicationManager.ts +18 -13
  30. package/runtime/composables/useDesktopManager.ts +31 -7
  31. package/runtime/composables/useOwdDialogs.ts +13 -0
  32. package/runtime/composables/useWorkspaceOverview.ts +41 -0
  33. package/runtime/core/controllers/ApplicationController.ts +114 -24
  34. package/runtime/core/controllers/WindowController.ts +4 -9
  35. package/runtime/dialogs/owdDialogProvider.ts +81 -0
  36. package/runtime/plugins/01.owd-dialogs-fallback.ts +15 -0
  37. package/runtime/stores/storeApplicationMeta.ts +56 -12
  38. package/runtime/stores/storeDesktop.ts +79 -2
  39. package/runtime/stores/storeDesktopDefaultApps.ts +2 -2
  40. package/runtime/stores/storeDesktopExplorer.ts +74 -0
  41. package/runtime/stores/storeDesktopWorkspace.ts +14 -2
  42. package/runtime/utils/captureElementToCanvas.ts +21 -0
  43. package/runtime/utils/explorerEntryPath.ts +10 -0
  44. package/runtime/utils/resolveDesktopConfigPath.ts +52 -0
  45. package/runtime/utils/validateOwdUserConfig.ts +44 -0
  46. package/types/index.d.ts +64 -12
  47. package/CHANGELOG.md +0 -25
  48. package/project.json +0 -4
  49. package/runtime/stores/storeApplication.ts +0 -236
  50. package/test/basic.test.ts +0 -15
  51. package/test/fixtures/basic/app.vue +0 -5
  52. package/test/fixtures/basic/nuxt.config.ts +0 -5
  53. package/test/fixtures/basic/package.json +0 -5
  54. package/tsconfig.app.json +0 -31
  55. package/tsconfig.json +0 -12
  56. package/tsconfig.spec.json +0 -37
  57. package/vitest.config.ts +0 -24
@@ -0,0 +1,13 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ declare const _default: _nuxt_schema.NuxtModule<{
4
+ theme: string;
5
+ apps: never[];
6
+ modules: never[];
7
+ }, {
8
+ theme: string;
9
+ apps: never[];
10
+ modules: never[];
11
+ }, false>;
12
+
13
+ export { _default as default };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "desktop",
3
+ "configKey": "owd",
4
+ "version": "3.2.0",
5
+ "builder": {
6
+ "@nuxt/module-builder": "1.0.2",
7
+ "unbuild": "3.6.1"
8
+ }
9
+ }
@@ -0,0 +1,219 @@
1
+ import { defineNuxtModule, createResolver, installModule, addComponentsDir, addPlugin, addImportsDir } from '@nuxt/kit';
2
+ import { existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+
5
+ function deepMerge(target, source) {
6
+ function isObject(value) {
7
+ return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof RegExp);
8
+ }
9
+ if (isObject(target) && isObject(source)) {
10
+ const result = { ...target };
11
+ for (const key in source) {
12
+ const targetValue = target[key];
13
+ const sourceValue = source[key];
14
+ if (isObject(sourceValue) && isObject(targetValue)) {
15
+ result[key] = deepMerge(targetValue, sourceValue);
16
+ } else {
17
+ result[key] = sourceValue;
18
+ }
19
+ }
20
+ return result;
21
+ }
22
+ return source;
23
+ }
24
+
25
+ function assertValidOwdUserConfig(config, configFile = "desktop.config.ts") {
26
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
27
+ throw new Error(
28
+ `[@owdproject/core] ${configFile} must default-export a non-array object from defineDesktopConfig({ ... }).`
29
+ );
30
+ }
31
+ const c = config;
32
+ if (c.theme != null && typeof c.theme !== "string") {
33
+ throw new Error(
34
+ `[@owdproject/core] ${configFile}: \`theme\` must be a string (npm package name of the theme module).`
35
+ );
36
+ }
37
+ if (c.apps !== void 0) {
38
+ if (!Array.isArray(c.apps) || !c.apps.every((item) => typeof item === "string")) {
39
+ throw new Error(
40
+ `[@owdproject/core] ${configFile}: \`apps\` must be an array of strings (Nuxt module package names).`
41
+ );
42
+ }
43
+ }
44
+ if (c.modules !== void 0) {
45
+ if (!Array.isArray(c.modules) || !c.modules.every((item) => typeof item === "string")) {
46
+ throw new Error(
47
+ `[@owdproject/core] ${configFile}: \`modules\` must be an array of strings (Nuxt module package names).`
48
+ );
49
+ }
50
+ }
51
+ }
52
+
53
+ const DESKTOP_CONFIG_FILENAME = "desktop.config.ts";
54
+ const LEGACY_DESKTOP_CONFIG_FILENAME = "owd.config.ts";
55
+ const LEGACY_CONFIG_DEPRECATION = "[@owdproject/core] owd.config.ts is deprecated and kept only for backward compatibility; rename to desktop.config.ts (required from @owdproject/core 3.2).";
56
+ function resolveDesktopConfigPath(rootDir) {
57
+ const desktopPath = join(rootDir, DESKTOP_CONFIG_FILENAME);
58
+ const legacyPath = join(rootDir, LEGACY_DESKTOP_CONFIG_FILENAME);
59
+ const hasDesktop = existsSync(desktopPath);
60
+ const hasLegacy = existsSync(legacyPath);
61
+ if (hasDesktop && hasLegacy) {
62
+ console.warn(
63
+ "[@owdproject/core] Both desktop.config.ts and owd.config.ts exist; using desktop.config.ts. Remove owd.config.ts."
64
+ );
65
+ return { path: desktopPath, file: DESKTOP_CONFIG_FILENAME, legacy: false };
66
+ }
67
+ if (hasDesktop) {
68
+ return { path: desktopPath, file: DESKTOP_CONFIG_FILENAME, legacy: false };
69
+ }
70
+ if (hasLegacy) {
71
+ return {
72
+ path: legacyPath,
73
+ file: LEGACY_DESKTOP_CONFIG_FILENAME,
74
+ legacy: true
75
+ };
76
+ }
77
+ return null;
78
+ }
79
+ function warnLegacyDesktopConfig(resolved) {
80
+ if (resolved?.legacy) {
81
+ console.warn(LEGACY_CONFIG_DEPRECATION);
82
+ }
83
+ }
84
+
85
+ const version = "3.2.0";
86
+ const pkg = {
87
+ version: version};
88
+
89
+ const module$1 = defineNuxtModule({
90
+ meta: {
91
+ name: "desktop",
92
+ configKey: "owd"
93
+ },
94
+ defaults: {
95
+ theme: "@owdproject/theme-nova",
96
+ apps: [],
97
+ modules: []
98
+ },
99
+ async setup(_options, _nuxt) {
100
+ const { resolve } = createResolver(import.meta.url);
101
+ _nuxt.options.experimental = {
102
+ ..._nuxt.options.experimental,
103
+ viteEnvironmentApi: _nuxt.options.experimental?.viteEnvironmentApi ?? true
104
+ };
105
+ _nuxt.options.runtimeConfig.public.desktop = {};
106
+ let clientConfig;
107
+ const resolvedConfig = resolveDesktopConfigPath(_nuxt.options.rootDir);
108
+ if (!resolvedConfig) {
109
+ const hint = "Create desktop.config.ts next to your Nuxt root (e.g. desktop/desktop.config.ts), export default defineDesktopConfig({ theme, apps, modules }).";
110
+ throw new Error(`[@owdproject/core] Cannot find desktop.config.ts in ${_nuxt.options.rootDir}. ${hint}`);
111
+ }
112
+ warnLegacyDesktopConfig(resolvedConfig);
113
+ try {
114
+ clientConfig = (await import(resolvedConfig.path)).default;
115
+ } catch (e) {
116
+ const hint = "Export default defineDesktopConfig({ theme, apps, modules }) from desktop.config.ts.";
117
+ throw new Error(
118
+ `[@owdproject/core] Cannot load ${resolvedConfig.file}. ${hint}`,
119
+ { cause: e }
120
+ );
121
+ }
122
+ assertValidOwdUserConfig(clientConfig, resolvedConfig.file);
123
+ if (!clientConfig.theme) {
124
+ clientConfig.theme = "@owdproject/theme-nova";
125
+ }
126
+ _nuxt.options = {
127
+ ..._nuxt.options,
128
+ ...clientConfig
129
+ };
130
+ _nuxt.options.runtimeConfig.public.desktop = {
131
+ coreVersion: pkg.version
132
+ };
133
+ _nuxt.options.runtimeConfig.public.desktop = deepMerge(
134
+ _nuxt.options.runtimeConfig.public.desktop,
135
+ clientConfig
136
+ );
137
+ {
138
+ if (clientConfig.theme) {
139
+ await installModule(clientConfig.theme);
140
+ }
141
+ if (clientConfig.modules && Array.isArray(clientConfig.modules)) {
142
+ for (const modulePath of clientConfig.modules) {
143
+ await installModule(modulePath);
144
+ }
145
+ }
146
+ if (clientConfig.apps && Array.isArray(clientConfig.apps)) {
147
+ for (const appPath of clientConfig.apps) {
148
+ await installModule(appPath);
149
+ }
150
+ }
151
+ }
152
+ _nuxt.options.appConfig.desktop = _nuxt.options.runtimeConfig.public.desktop;
153
+ {
154
+ _nuxt.options.primevue = _nuxt.options.primevue || {};
155
+ _nuxt.options.primevue.options = _nuxt.options.primevue.options || {};
156
+ _nuxt.options.primevue.options.theme = _nuxt.options.primevue.options.theme || {};
157
+ await installModule("@primevue/nuxt-module");
158
+ }
159
+ {
160
+ const tailwindPaths = _nuxt.options.runtimeConfig.app.owd?.tailwindPaths || [];
161
+ tailwindPaths.push("./runtime/components/**/*.{vue,mjs,ts}");
162
+ _nuxt.options.tailwindcss = _nuxt.options.tailwindcss || {};
163
+ _nuxt.options.tailwindcss.config = _nuxt.options.tailwindcss.config || {};
164
+ _nuxt.options.tailwindcss.config.content = tailwindPaths;
165
+ await installModule("@nuxtjs/tailwindcss", {
166
+ viewer: false
167
+ });
168
+ }
169
+ {
170
+ await installModule("@pinia/nuxt");
171
+ }
172
+ {
173
+ await installModule("@nuxt/fonts");
174
+ }
175
+ {
176
+ await installModule("@nuxt/icon", {
177
+ clientBundle: {
178
+ scan: true,
179
+ sizeLimitKb: 256
180
+ }
181
+ });
182
+ }
183
+ {
184
+ await installModule("@vueuse/nuxt");
185
+ }
186
+ {
187
+ await installModule("@nuxtjs/i18n");
188
+ }
189
+ {
190
+ _nuxt.hook("vite:extendConfig", (viteConfig) => {
191
+ viteConfig.css = viteConfig.css || {};
192
+ viteConfig.css.preprocessorOptions = viteConfig.css.preprocessorOptions || {};
193
+ viteConfig.css.preprocessorOptions.scss = {
194
+ api: "modern-compiler"
195
+ };
196
+ });
197
+ }
198
+ {
199
+ _nuxt.options.css.push("sanitize.css");
200
+ }
201
+ {
202
+ addComponentsDir({
203
+ path: resolve("./runtime/components"),
204
+ prefix: "",
205
+ global: true
206
+ });
207
+ }
208
+ {
209
+ addPlugin(resolve("./runtime/plugins/resize.client.ts"));
210
+ addPlugin(resolve("./runtime/plugins/01.owd-dialogs-fallback.ts"));
211
+ addImportsDir(resolve("./runtime/composables"));
212
+ addImportsDir(resolve("./runtime/core"));
213
+ addImportsDir(resolve("./runtime/stores"));
214
+ addImportsDir(resolve("./runtime/utils"));
215
+ }
216
+ }
217
+ });
218
+
219
+ export { module$1 as default };
@@ -0,0 +1,7 @@
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module.mjs'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { default } from './module.mjs'
package/module.ts CHANGED
@@ -7,43 +7,68 @@ import {
7
7
  addPlugin
8
8
  } from '@nuxt/kit'
9
9
  import { deepMerge } from './runtime/utils/utilCommon'
10
+ import { assertValidOwdUserConfig } from './runtime/utils/validateOwdUserConfig'
11
+ import {
12
+ resolveDesktopConfigPath,
13
+ warnLegacyDesktopConfig,
14
+ } from './runtime/utils/resolveDesktopConfigPath'
10
15
  import pkg from './package.json'
11
16
 
12
17
  export default defineNuxtModule({
13
18
  meta: {
14
- name: 'owd-core',
15
- configKey: 'owd'
19
+ name: 'desktop',
20
+ configKey: 'owd',
16
21
  },
17
22
  defaults: {
18
- theme: '@owdproject/theme-win95',
23
+ theme: '@owdproject/theme-nova',
19
24
  apps: [],
20
25
  modules: [],
21
26
  },
22
27
  async setup(_options, _nuxt) {
23
28
  const { resolve } = createResolver(import.meta.url)
24
29
 
30
+ // Required for Nuxt 4 dev (playgrounds, module graphs): avoids
31
+ // "Vite Node IPC socket path not configured" when using @nuxt/kit pipelines.
32
+ _nuxt.options.experimental = {
33
+ ..._nuxt.options.experimental,
34
+ viteEnvironmentApi:
35
+ _nuxt.options.experimental?.viteEnvironmentApi ?? true,
36
+ }
37
+
25
38
  _nuxt.options.runtimeConfig.public.desktop = {}
26
39
 
27
- // get open web desktop config
40
+ // get open web desktop config (desktop.config.ts; owd.config.ts legacy)
28
41
 
29
42
  let clientConfig
30
43
 
31
- try {
44
+ const resolvedConfig = resolveDesktopConfigPath(_nuxt.options.rootDir)
32
45
 
33
- clientConfig = (
34
- await import(_nuxt.options.rootDir + '/owd.config.ts')
35
- ).default
46
+ if (!resolvedConfig) {
47
+ const hint =
48
+ 'Create desktop.config.ts next to your Nuxt root (e.g. desktop/desktop.config.ts), export default defineDesktopConfig({ theme, apps, modules }).'
49
+ throw new Error(`[@owdproject/core] Cannot find desktop.config.ts in ${_nuxt.options.rootDir}. ${hint}`)
50
+ }
51
+
52
+ warnLegacyDesktopConfig(resolvedConfig)
36
53
 
54
+ try {
55
+ clientConfig = (await import(resolvedConfig.path)).default
37
56
  } catch (e) {
38
- console.error('/desktop/owd.config.ts not found or invalid', e)
39
- return
57
+ const hint =
58
+ 'Export default defineDesktopConfig({ theme, apps, modules }) from desktop.config.ts.'
59
+ throw new Error(
60
+ `[@owdproject/core] Cannot load ${resolvedConfig.file}. ${hint}`,
61
+ { cause: e },
62
+ )
40
63
  }
41
64
 
65
+ assertValidOwdUserConfig(clientConfig, resolvedConfig.file)
66
+
42
67
  if (!clientConfig.theme) {
43
- clientConfig.theme = '@owdproject/theme-win95'
68
+ clientConfig.theme = '@owdproject/theme-nova'
44
69
  }
45
70
 
46
- // extend nuxt.config.ts with owd.config.ts
71
+ // extend nuxt.config.ts with desktop.config.ts
47
72
 
48
73
  _nuxt.options = {
49
74
  ..._nuxt.options,
@@ -186,6 +211,7 @@ export default defineNuxtModule({
186
211
 
187
212
  {
188
213
  addPlugin(resolve('./runtime/plugins/resize.client.ts'))
214
+ addPlugin(resolve('./runtime/plugins/01.owd-dialogs-fallback.ts'))
189
215
 
190
216
  // add other files
191
217
 
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "@owdproject/core",
3
- "version": "3.1.4",
3
+ "version": "3.2.0",
4
+ "description": "Nuxt Desktop module for browser desktop experiences (windows, shell, apps)",
4
5
  "keywords": [
5
- "web",
6
- "desktop",
7
- "vue",
8
6
  "nuxt",
7
+ "nuxt-module",
8
+ "nuxt-desktop",
9
+ "desktop",
9
10
  "web-desktop",
10
- "web-os"
11
+ "vue",
12
+ "owd",
13
+ "open-web-desktop"
11
14
  ],
12
15
  "homepage": "https://github.com/owdproject/client#readme",
13
16
  "bugs": {
@@ -24,30 +27,83 @@
24
27
  },
25
28
  "type": "module",
26
29
  "types": "types/index.d.ts",
30
+ "files": [
31
+ "bin",
32
+ "dist",
33
+ "runtime",
34
+ "types",
35
+ "index.ts",
36
+ "module.ts",
37
+ "package.json",
38
+ "README.md"
39
+ ],
40
+ "scripts": {
41
+ "dev": "pnpm run dev:prepare",
42
+ "dev:prepare": "nuxt-module-build build --stub",
43
+ "build:module": "nuxt-module-build build",
44
+ "prepack": "nuxt-module-build build"
45
+ },
46
+ "exports": {
47
+ ".": {
48
+ "types": "./types/index.d.ts",
49
+ "default": "./index.ts"
50
+ },
51
+ "./module": {
52
+ "types": "./dist/types.d.mts",
53
+ "development": "./module.ts",
54
+ "import": "./module.ts",
55
+ "default": "./module.ts"
56
+ },
57
+ "./nuxt": {
58
+ "types": "./dist/types.d.mts",
59
+ "development": "./module.ts",
60
+ "import": "./module.ts",
61
+ "default": "./module.ts"
62
+ },
63
+ "./package.json": "./package.json",
64
+ "./runtime/*": {
65
+ "types": "./runtime/*",
66
+ "development": "./runtime/*",
67
+ "import": "./runtime/*",
68
+ "default": "./runtime/*"
69
+ }
70
+ },
27
71
  "bin": {
72
+ "desktop": "bin/desktop.js",
28
73
  "owd": "bin/owd.js"
29
74
  },
30
75
  "dependencies": {
31
- "@nuxt/fonts": "^0.11.4",
32
- "@nuxt/icon": "^1.14.0",
33
- "@nuxt/kit": "^3.17.5",
34
- "@nuxtjs/i18n": "^9.5.5",
76
+ "@nuxt/fonts": "^0.14.0",
77
+ "@nuxt/icon": "^2.2.1",
78
+ "@nuxt/kit": "^4.4.4",
79
+ "@nuxtjs/i18n": "^10.3.0",
35
80
  "@nuxtjs/tailwindcss": "^6.14.0",
36
- "@pinia/nuxt": "^0.11.1",
37
- "@primeuix/themes": "^1.1.2",
38
- "@primevue/forms": "^4.3.5",
39
- "@primevue/nuxt-module": "^4.3.5",
40
- "@vueuse/components": "^13.4.0",
41
- "@vueuse/core": "^13.4.0",
42
- "@vueuse/nuxt": "^13.4.0",
81
+ "@pinia/nuxt": "^0.11.3",
82
+ "@primeuix/themes": "^2.0.3",
83
+ "@primevue/forms": "^4.5.5",
84
+ "@primevue/nuxt-module": "^4.5.5",
85
+ "@vueuse/components": "^14.3.0",
86
+ "@vueuse/core": "^14.3.0",
87
+ "@vueuse/nuxt": "^14.3.0",
43
88
  "mitt": "^3.0.1",
44
- "nanoid": "^5.1.5",
45
- "nuxt": "^3.17.5",
46
- "pinia": "^3.0.3",
89
+ "nanoid": "^5.1.11",
90
+ "nuxt": "^4.4.4",
91
+ "pinia": "^3.0.4",
47
92
  "sanitize.css": "^13.0.0",
48
- "sass": "^1.89.2",
49
- "shellwords": "^1.0.1",
93
+ "sass": "^1.99.0",
94
+ "shellwords": "^1.1.1",
50
95
  "vue-resizable": "^2.1.7",
51
- "getopts": "^2.3.0"
96
+ "getopts": "^2.3.0",
97
+ "neo-blessed": "^0.2.0"
98
+ },
99
+ "optionalDependencies": {
100
+ "html2canvas": "^1.4.1"
101
+ },
102
+ "devDependencies": {
103
+ "@nuxt/devtools": "^3.2.4",
104
+ "@nuxt/module-builder": "^1.0.2",
105
+ "@nuxt/schema": "^4.4.4",
106
+ "typescript": "~5.8.3",
107
+ "vue-tsc": "^2.2.10"
52
108
  }
53
109
  }
@@ -6,13 +6,15 @@ defineProps<{
6
6
  }>()
7
7
 
8
8
  const applicationManager = useApplicationManager()
9
+ const runningApps = applicationManager.appsRunning
9
10
  </script>
10
11
 
11
12
  <template>
12
13
  <CoreApplicationWindowsRender
13
- v-for="app of applicationManager.appsRunning.value"
14
+ v-for="app of runningApps"
14
15
  :key="app.id"
15
16
  :workspace-filter="workspaceFilter"
16
17
  :windows="app.windowsOpened"
18
+ :window-revision="app.openWindowCount.value"
17
19
  />
18
20
  </template>
@@ -1,17 +1,38 @@
1
1
  <script setup lang="ts">
2
- defineProps<{
2
+ import { computed } from 'vue'
3
+ import { useDesktopWorkspaceStore } from '../../../stores/storeDesktopWorkspace'
4
+
5
+ const props = defineProps<{
3
6
  workspaceFilter?: string
4
- windows: Map<string, IApplicationController['windows']>
7
+ windows: Map<string, IWindowController>
8
+ /** Tracks ApplicationController.openWindowCount so Vue re-renders when the map is replaced. */
9
+ windowRevision?: number
5
10
  }>()
11
+
12
+ const desktopWorkspaceStore = useDesktopWorkspaceStore()
13
+
14
+ const windowEntries = computed(() => {
15
+ void props.windowRevision
16
+ return Array.from(props.windows.entries())
17
+ })
18
+
19
+ function matchesWorkspace(window: IWindowController): boolean {
20
+ const filter = props.workspaceFilter
21
+ if (!filter) return true
22
+
23
+ const ws = window.state.workspace
24
+ if (!ws) {
25
+ return filter === desktopWorkspaceStore.active
26
+ }
27
+
28
+ return ws === filter
29
+ }
6
30
  </script>
7
31
 
8
32
  <template>
9
- <template v-for="[windowId, window] in windows" :key="windowId">
33
+ <template v-for="[windowId, window] in windowEntries" :key="windowId">
10
34
  <component
11
- v-if="
12
- !workspaceFilter ||
13
- (workspaceFilter && window.state.workspace === workspaceFilter)
14
- "
35
+ v-if="matchesWorkspace(window)"
15
36
  :window="window"
16
37
  :is="window.config.component"
17
38
  />
@@ -2,14 +2,16 @@
2
2
  import { ref, provide } from 'vue'
3
3
  import { computed } from '@vue/reactivity'
4
4
  import { useAppConfig } from 'nuxt/app'
5
- import { useDesktopManager } from '@owdproject/core/runtime/composables/useDesktopManager'
6
5
  import { handleWindowControllerProps } from '@owdproject/core/runtime/utils/utilWindow'
6
+ import { useDesktopWorkspaceStore } from '../../../stores/storeDesktopWorkspace'
7
7
 
8
8
  const props = defineProps<{
9
9
  window?: IWindowController
10
10
  content?: WindowContent
11
11
  }>()
12
12
 
13
+ const desktopWorkspaceStore = useDesktopWorkspaceStore()
14
+
13
15
  const emit = defineEmits([
14
16
  'resize:start',
15
17
  'resize:move',
@@ -29,7 +31,6 @@ const emit = defineEmits([
29
31
  ])
30
32
 
31
33
  const appConfig = useAppConfig()
32
- const desktopManager = useDesktopManager() // used in <style v-bind>
33
34
  const windowController: IWindowController = handleWindowControllerProps(
34
35
  props.window,
35
36
  )
@@ -117,6 +118,36 @@ const classes = computed(() => {
117
118
 
118
119
  return list
119
120
  })
121
+
122
+ /** While workspace overview is open, disable vue-resizable chrome drag and use native HTML5 drag to move windows between desktops. */
123
+ const workspaceChromeDragSelector = computed(() => {
124
+ if (desktopWorkspaceStore.overview) {
125
+ return '.owd-window-overview-drag-none'
126
+ }
127
+ return windowController?.isDraggable
128
+ ? '.owd-window-nav__draggable'
129
+ : '.owd-window-nav__draggable-none'
130
+ })
131
+
132
+ function onOverviewNativeDragStart(e: DragEvent) {
133
+ if (!desktopWorkspaceStore.overview || !windowController) return
134
+ e.stopPropagation()
135
+ e.dataTransfer?.setData('text/plain', windowController.state.id)
136
+ e.dataTransfer!.effectAllowed = 'move'
137
+ try {
138
+ const img = new Image()
139
+ img.src =
140
+ 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
141
+ e.dataTransfer?.setDragImage(img, 0, 0)
142
+ } catch {
143
+ /* noop */
144
+ }
145
+ }
146
+
147
+ /** `fit-parent` clamps drag/resize to the parent box — wrong for viewport-fixed shells (Win11, GNOME). */
148
+ const shouldFitParent = computed(
149
+ () => appConfig.desktop.windows.position !== 'fixed',
150
+ )
120
151
  </script>
121
152
 
122
153
  <template>
@@ -132,7 +163,7 @@ const classes = computed(() => {
132
163
  :left="windowController?.position?.x"
133
164
  :top="windowController?.position?.y"
134
165
  :active="windowController?.isResizable ? undefined : []"
135
- fit-parent
166
+ :fit-parent="shouldFitParent"
136
167
  @drag:start="onWindowDragStart"
137
168
  @drag:move="onWindowDragMove"
138
169
  @drag:end="onWindowDragEnd"
@@ -140,18 +171,21 @@ const classes = computed(() => {
140
171
  @resize:move="onWindowResizeMove"
141
172
  @resize:end="onWindowResizeEnd"
142
173
  :style="{ zIndex: windowController?.position?.z }"
143
- :drag-selector="
144
- windowController?.isDraggable
145
- ? '.owd-window-nav__draggable'
146
- : '.owd-window-nav__draggable-none'
147
- "
174
+ :drag-selector="workspaceChromeDragSelector"
175
+ :draggable="desktopWorkspaceStore.overview"
148
176
  @pointerdown="onWindowPointerDown"
177
+ @dragstart="onOverviewNativeDragStart"
149
178
  >
150
179
  <slot />
151
180
  </vue-resizable>
152
181
  </template>
153
182
 
154
183
  <style scoped lang="scss">
184
+ /* vue-resizable sets `.resizable-component { position: relative }` — override so desktop geometry stays consistent */
185
+ .owd-window.resizable-component {
186
+ position: v-bind('appConfig.desktop.windows.position') !important;
187
+ }
188
+
155
189
  .owd-window {
156
190
  position: v-bind('appConfig.desktop.windows.position');
157
191
 
@@ -14,10 +14,7 @@ const classes = computed(() => {
14
14
  list.push('owd-window-nav--active')
15
15
  }
16
16
 
17
- if (
18
- typeof windowController?.state.focused === 'undefined' ||
19
- !!windowController?.state.focused
20
- ) {
17
+ if (windowController?.state.focused) {
21
18
  list.push('owd-window-nav--focused')
22
19
  }
23
20
 
@@ -34,15 +31,36 @@ const classes = computed(() => {
34
31
  </template>
35
32
 
36
33
  <style lang="scss">
34
+ /**
35
+ * Title/icon sit above the drag layer in DOM order; use pointer-events so
36
+ * mousedown reaches `.owd-window-nav__draggable` for vue-resizable. Window
37
+ * controls stay clickable.
38
+ */
37
39
  .owd-window-nav {
38
40
  position: relative;
41
+ display: flex;
42
+ align-items: stretch;
43
+ width: 100%;
44
+ min-height: 0;
39
45
 
40
46
  &__draggable {
41
47
  position: absolute;
42
- top: 0;
43
- left: 0;
44
- right: 0;
45
- height: 100%;
48
+ inset: 0;
49
+ z-index: 1;
50
+ }
51
+
52
+ > :not(.owd-window-nav__draggable) {
53
+ position: relative;
54
+ z-index: 2;
55
+ pointer-events: none;
56
+ }
57
+
58
+ > .owd-window-nav__btn-group {
59
+ pointer-events: auto;
60
+ }
61
+
62
+ > .owd-window-nav__interactive {
63
+ pointer-events: auto;
46
64
  }
47
65
  }
48
66
  </style>