@owdproject/core 3.4.0 → 4.0.0-alpha.3

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 (108) hide show
  1. package/README.md +112 -13
  2. package/dist/dom/index.d.mts +65 -0
  3. package/dist/dom/index.d.ts +65 -0
  4. package/dist/dom/index.mjs +224 -0
  5. package/dist/events/index.d.mts +70 -0
  6. package/dist/events/index.d.ts +70 -0
  7. package/dist/events/index.mjs +3 -0
  8. package/dist/index.d.mts +73 -0
  9. package/dist/index.d.ts +73 -0
  10. package/dist/index.mjs +115 -0
  11. package/dist/layout/index.d.mts +41 -0
  12. package/dist/layout/index.d.ts +41 -0
  13. package/dist/layout/index.mjs +64 -0
  14. package/dist/machines/index.d.mts +31 -0
  15. package/dist/machines/index.d.ts +31 -0
  16. package/dist/machines/index.mjs +2 -0
  17. package/dist/managers/index.d.mts +117 -0
  18. package/dist/managers/index.d.ts +117 -0
  19. package/dist/managers/index.mjs +6 -0
  20. package/dist/session/index.d.mts +57 -0
  21. package/dist/session/index.d.ts +57 -0
  22. package/dist/session/index.mjs +65 -0
  23. package/dist/shared/core.B0B0KfVK.mjs +865 -0
  24. package/dist/shared/core.B1XxB289.mjs +239 -0
  25. package/dist/shared/core.B3mp7ztE.mjs +237 -0
  26. package/dist/shared/core.BOvFdEVQ.d.ts +114 -0
  27. package/dist/shared/core.CsDFgGnX.mjs +40 -0
  28. package/dist/shared/core.Di1eFcr_.d.mts +114 -0
  29. package/dist/shared/core.Pd_Ogt4g.mjs +101 -0
  30. package/dist/shared/core.b3Xldzyu.mjs +29 -0
  31. package/dist/types/index.d.mts +266 -0
  32. package/dist/types/index.d.ts +266 -0
  33. package/dist/types/index.mjs +1 -0
  34. package/package.json +56 -112
  35. package/CHANGELOG.md +0 -98
  36. package/DESKTOP_KERNEL.md +0 -232
  37. package/MIGRATION_3.3.2.md +0 -23
  38. package/MIGRATION_3.4.md +0 -126
  39. package/PLAYGROUND.md +0 -91
  40. package/dist/module.d.mts +0 -13
  41. package/dist/module.json +0 -9
  42. package/dist/module.mjs +0 -254
  43. package/dist/types.d.mts +0 -7
  44. package/index.ts +0 -8
  45. package/kit/authoring.ts +0 -340
  46. package/kit/defineDesktopApp.ts +0 -56
  47. package/module.ts +0 -203
  48. package/runtime/components/Application/DesktopCoreApplicationRender.vue +0 -20
  49. package/runtime/components/Application/DesktopCoreApplicationWindowsRender.vue +0 -38
  50. package/runtime/components/background/DesktopCoreBackground.vue +0 -16
  51. package/runtime/components/core/DesktopCore.vue +0 -43
  52. package/runtime/components/time/DesktopCoreTime.vue +0 -56
  53. package/runtime/components/window/DesktopCoreWindow.vue +0 -215
  54. package/runtime/components/window/DesktopCoreWindowContent.vue +0 -28
  55. package/runtime/components/window/DesktopCoreWindowNav.vue +0 -66
  56. package/runtime/components/window/DesktopCoreWindowSnapHintsBase.vue +0 -74
  57. package/runtime/components/workspace/DesktopCoreWorkspaceEdgeHintsBase.vue +0 -84
  58. package/runtime/composables/useApplicationEntries.ts +0 -51
  59. package/runtime/composables/useApplicationManager.ts +0 -285
  60. package/runtime/composables/useBlockNonInputContextMenu.ts +0 -30
  61. package/runtime/composables/useDesktopConfig.ts +0 -8
  62. package/runtime/composables/useDesktopDialogs.ts +0 -16
  63. package/runtime/composables/useDesktopExtension.ts +0 -8
  64. package/runtime/composables/useDesktopManager.ts +0 -51
  65. package/runtime/composables/useDesktopManifest.ts +0 -17
  66. package/runtime/composables/useDesktopSession.ts +0 -27
  67. package/runtime/composables/useDesktopShellIdentity.ts +0 -99
  68. package/runtime/composables/useDesktopShellOptions.ts +0 -36
  69. package/runtime/composables/useDesktopShellReady.ts +0 -7
  70. package/runtime/composables/useDesktopWorkArea.ts +0 -93
  71. package/runtime/composables/useTerminalManager.ts +0 -159
  72. package/runtime/composables/useToggleWindowMaximize.ts +0 -35
  73. package/runtime/composables/useWindowDragHandlers.ts +0 -47
  74. package/runtime/composables/useWindowSnapDrop.ts +0 -112
  75. package/runtime/composables/useWorkspaceEdgeDrop.ts +0 -176
  76. package/runtime/composables/useWorkspaceManager.ts +0 -67
  77. package/runtime/composables/useWorkspaceOverviewCapture.ts +0 -92
  78. package/runtime/composables/useWorkspaceOverviewLiveScale.ts +0 -151
  79. package/runtime/constants/desktopShellKeys.ts +0 -12
  80. package/runtime/constants/shellLayout.ts +0 -5
  81. package/runtime/dialogs/desktopDialogProvider.ts +0 -73
  82. package/runtime/internal/controllers/ApplicationController.ts +0 -351
  83. package/runtime/internal/controllers/WindowController.ts +0 -547
  84. package/runtime/plugins/01.desktop-shell-init.client.ts +0 -28
  85. package/runtime/plugins/02.desktop-register-desktop-apps.client.ts +0 -13
  86. package/runtime/plugins/resize.client.ts +0 -6
  87. package/runtime/stores/storeApplicationMeta.ts +0 -60
  88. package/runtime/stores/storeApplicationWindows.ts +0 -21
  89. package/runtime/stores/storeDesktop.ts +0 -71
  90. package/runtime/stores/storeDesktopDefaultApps.ts +0 -54
  91. package/runtime/stores/storeDesktopVolume.ts +0 -19
  92. package/runtime/stores/storeDesktopWindow.ts +0 -38
  93. package/runtime/stores/storeDesktopWorkspace.ts +0 -105
  94. package/runtime/stores/storeIds.ts +0 -35
  95. package/runtime/types/desktopMenu.ts +0 -10
  96. package/runtime/utils/initDesktopShell.ts +0 -80
  97. package/runtime/utils/migratePersistedStoreIds.ts +0 -57
  98. package/runtime/utils/utilApp.ts +0 -25
  99. package/runtime/utils/utilCaptureElementToCanvas.ts +0 -21
  100. package/runtime/utils/utilCommon.ts +0 -26
  101. package/runtime/utils/utilDebug.ts +0 -30
  102. package/runtime/utils/utilDetectSnapZone.ts +0 -57
  103. package/runtime/utils/utilTerminal.ts +0 -6
  104. package/runtime/utils/utilWindowControllerAdapter.ts +0 -15
  105. package/runtime/utils/utilWindowLayout.ts +0 -82
  106. package/runtime/utils/utilWindowMaximizeLayout.ts +0 -44
  107. package/runtime/utils/utilWorkspaceWindows.ts +0 -33
  108. package/types/index.d.ts +0 -531
@@ -1,99 +0,0 @@
1
- import { computed, ref } from 'vue'
2
- import { useRuntimeConfig } from 'nuxt/app'
3
-
4
- export type ShellIdentity = {
5
- userId: string
6
- displayName: string
7
- avatarUrl: string | null
8
- userHome: string
9
- }
10
-
11
- const GUEST_USER_ID = 'guest'
12
-
13
- const identity = ref<ShellIdentity | null>(null)
14
-
15
- export function sanitizeUserHomeSegment(input: string): string {
16
- const cleaned = input
17
- .trim()
18
- .replace(/^@/, '')
19
- .replace(/[^\w.\-]/g, '_')
20
- .slice(0, 64)
21
- return cleaned || 'user'
22
- }
23
-
24
- /** Build a VFS home path from an auth handle (e.g. Bluesky `@alice.bsky.social`). */
25
- export function userHomeFromHandle(handle: string, base = '/home'): string {
26
- const baseNorm = base.endsWith('/') ? base.slice(0, -1) : base
27
- return `${baseNorm}/${sanitizeUserHomeSegment(handle)}`
28
- }
29
-
30
- function resolveDefaultHome(): string {
31
- try {
32
- const config = useRuntimeConfig()
33
- const desktop = config.public.desktop as {
34
- fs?: { defaultUserHome?: string }
35
- }
36
- const home = desktop.fs?.defaultUserHome?.trim()
37
- if (home) return home.startsWith('/') ? home : `/${home}`
38
- } catch {
39
- /* outside Nuxt setup */
40
- }
41
- return '/home/Guest'
42
- }
43
-
44
- function guestIdentity(defaultHome: string): ShellIdentity {
45
- return {
46
- userId: GUEST_USER_ID,
47
- displayName: 'Guest',
48
- avatarUrl: null,
49
- userHome: defaultHome,
50
- }
51
- }
52
-
53
- /**
54
- * Shell session identity (display + VFS home). Auth modules call {@link setShellIdentity}
55
- * after login; defaults to Guest.
56
- */
57
- export function useDesktopShellIdentity() {
58
- function ensureIdentity(): ShellIdentity {
59
- if (!identity.value) {
60
- identity.value = guestIdentity(resolveDefaultHome())
61
- }
62
- return identity.value
63
- }
64
-
65
- const userId = computed(() => ensureIdentity().userId)
66
- const displayName = computed(() => ensureIdentity().displayName)
67
- const avatarUrl = computed(() => ensureIdentity().avatarUrl)
68
- const userHome = computed(() => ensureIdentity().userHome)
69
- const isGuest = computed(() => userId.value === GUEST_USER_ID)
70
-
71
- function setShellIdentity(partial: Partial<ShellIdentity>) {
72
- const current = ensureIdentity()
73
- identity.value = {
74
- ...current,
75
- ...partial,
76
- userId: partial.userId?.trim() || current.userId,
77
- displayName: partial.displayName?.trim() || current.displayName,
78
- userHome: partial.userHome?.trim()
79
- ? partial.userHome.startsWith('/')
80
- ? partial.userHome
81
- : `/${partial.userHome}`
82
- : current.userHome,
83
- }
84
- }
85
-
86
- function clearShellIdentity() {
87
- identity.value = guestIdentity(resolveDefaultHome())
88
- }
89
-
90
- return {
91
- userId,
92
- displayName,
93
- avatarUrl,
94
- userHome,
95
- isGuest,
96
- setShellIdentity,
97
- clearShellIdentity,
98
- }
99
- }
@@ -1,36 +0,0 @@
1
- import { useAppConfig } from 'nuxt/app'
2
- import { computed } from 'vue'
3
-
4
- /**
5
- * Reads shared desktop shell flags from `appConfig.desktop` (merged from runtime config).
6
- * Themes use this for taskbar/dock visibility without duplicating `useAppConfig` wiring.
7
- */
8
- type DesktopAppShape = {
9
- systemBar?: {
10
- enabled?: boolean
11
- position?: string
12
- startButton?: boolean
13
- }
14
- }
15
-
16
- export function useDesktopShellOptions() {
17
- const appConfig = useAppConfig()
18
-
19
- const desktop = () => appConfig.desktop as DesktopAppShape | undefined
20
-
21
- const systemBarEnabled = computed(
22
- () => desktop()?.systemBar?.enabled !== false,
23
- )
24
-
25
- const systemBarPosition = computed(() => desktop()?.systemBar?.position)
26
-
27
- const startButtonEnabled = computed(
28
- () => desktop()?.systemBar?.startButton !== false,
29
- )
30
-
31
- return {
32
- systemBarEnabled,
33
- systemBarPosition,
34
- startButtonEnabled,
35
- }
36
- }
@@ -1,7 +0,0 @@
1
- import { useNuxtApp } from 'nuxt/app'
2
-
3
- /** True after `desktop-shell-init` has bound Pinia and run kernel workspace bootstrap. */
4
- export function useDesktopShellReady() {
5
- const nuxtApp = useNuxtApp()
6
- return nuxtApp.$desktopShellReady === true
7
- }
@@ -1,93 +0,0 @@
1
- import type { Ref } from 'vue'
2
- import { onScopeDispose, watch } from 'vue'
3
- import {
4
- EMPTY_DESKTOP_WORK_AREA,
5
- useDesktopWindowStore,
6
- } from '@owdproject/core/runtime/stores/storeDesktopWindow'
7
- import { useDesktopWorkspaceStore } from '@owdproject/core/runtime/stores/storeDesktopWorkspace'
8
-
9
- function measureElement(el: HTMLElement) {
10
- const rect = el.getBoundingClientRect()
11
- return {
12
- x: rect.left,
13
- y: rect.top,
14
- width: rect.width,
15
- height: rect.height,
16
- }
17
- }
18
-
19
- /**
20
- * Tracks the desktop shell stage rectangle (area under system bar, above dock).
21
- * Writes measured bounds to {@link useDesktopWindowStore.workArea}.
22
- * Pass the same ref themes use for workspace overview live scale.
23
- */
24
- export function useDesktopWorkArea(
25
- shellStageRef: Ref<HTMLElement | null | undefined>,
26
- ) {
27
- const desktopWindowStore = useDesktopWindowStore()
28
- const desktopWorkspaceStore = useDesktopWorkspaceStore()
29
-
30
- let resizeObserver: ResizeObserver | null = null
31
-
32
- function refreshWorkArea() {
33
- const el = shellStageRef.value
34
- if (!el) {
35
- desktopWindowStore.setWorkArea({ ...EMPTY_DESKTOP_WORK_AREA })
36
- return
37
- }
38
- desktopWindowStore.setWorkArea(measureElement(el))
39
- }
40
-
41
- function disconnectObserver() {
42
- resizeObserver?.disconnect()
43
- resizeObserver = null
44
- }
45
-
46
- function observeStage(el: HTMLElement) {
47
- disconnectObserver()
48
- refreshWorkArea()
49
-
50
- if (typeof ResizeObserver === 'undefined') {
51
- return
52
- }
53
-
54
- resizeObserver = new ResizeObserver(() => refreshWorkArea())
55
- resizeObserver.observe(el)
56
- }
57
-
58
- watch(
59
- shellStageRef,
60
- (el) => {
61
- disconnectObserver()
62
- if (el instanceof HTMLElement) {
63
- observeStage(el)
64
- } else {
65
- desktopWindowStore.setWorkArea({ ...EMPTY_DESKTOP_WORK_AREA })
66
- }
67
- },
68
- { immediate: true },
69
- )
70
-
71
- watch(
72
- () => desktopWorkspaceStore.overview,
73
- (open) => {
74
- if (!open) {
75
- refreshWorkArea()
76
- }
77
- },
78
- )
79
-
80
- if (typeof globalThis !== 'undefined') {
81
- globalThis.addEventListener('resize', refreshWorkArea)
82
- onScopeDispose(() => {
83
- globalThis.removeEventListener('resize', refreshWorkArea)
84
- disconnectObserver()
85
- })
86
- } else {
87
- onScopeDispose(disconnectObserver)
88
- }
89
-
90
- return {
91
- refreshWorkArea,
92
- }
93
- }
@@ -1,159 +0,0 @@
1
- import { useApplicationManager } from './useApplicationManager'
2
- import { debugLog } from '../utils/utilDebug'
3
-
4
- const commands = new Map<string, TerminalCommand>()
5
-
6
- // ─── formatting helpers ──────────────────────────────────────────────────────
7
-
8
- function padEnd(str: string, len: number): string {
9
- return str.length >= len ? str : str + ' '.repeat(len - str.length)
10
- }
11
-
12
- function buildHelpList(): string {
13
- const sorted = Array.from(commands.values()).sort((a, b) =>
14
- a.name.localeCompare(b.name),
15
- )
16
-
17
- const colWidth = Math.max(...sorted.map((c) => c.name.length)) + 4
18
-
19
- const rows = sorted
20
- .map((cmd) => ` ${padEnd(cmd.name, colWidth)}${cmd.description ?? ''}`)
21
- .join('\n')
22
-
23
- return (
24
- `Available commands:\n\n` +
25
- rows +
26
- `\n\nType 'help <command>' for more details.`
27
- )
28
- }
29
-
30
- function buildHelpDetail(cmd: TerminalCommand): string {
31
- const lines: string[] = []
32
-
33
- lines.push(` ${cmd.name}${cmd.description ? ` — ${cmd.description}` : ''}`)
34
- lines.push('')
35
-
36
- if (cmd.usage) {
37
- lines.push(` Usage: ${cmd.usage}`)
38
- lines.push('')
39
- }
40
-
41
- if (cmd.args && cmd.args.length > 0) {
42
- lines.push(' Arguments:')
43
- const argColW = Math.max(...cmd.args.map((a) => a.name.length)) + 4
44
- for (const arg of cmd.args) {
45
- const required = arg.required ? '' : ' (optional)'
46
- lines.push(
47
- ` ${padEnd(arg.name, argColW)}${arg.description ?? ''}${required}`,
48
- )
49
- }
50
- lines.push('')
51
- }
52
-
53
- if (cmd.options && cmd.options.length > 0) {
54
- lines.push(' Options:')
55
- const optColW = Math.max(...cmd.options.map((o) => o.flag.length)) + 4
56
- for (const opt of cmd.options) {
57
- lines.push(` ${padEnd(opt.flag, optColW)}${opt.description ?? ''}`)
58
- }
59
- lines.push('')
60
- }
61
-
62
- return lines.join('\n').trimEnd()
63
- }
64
-
65
- // ─── composable ─────────────────────────────────────────────────────────────
66
-
67
- /**
68
- * Terminal Manager Composable (Singleton)
69
- */
70
- export function useTerminalManager(): ITerminalManager {
71
- /**
72
- * Add a new terminal command
73
- */
74
- const addCommand = (command: TerminalCommand) => {
75
- for (const existingCommand of commands.keys()) {
76
- if (
77
- existingCommand.startsWith(command.name) ||
78
- command.name.startsWith(existingCommand)
79
- ) {
80
- throw new Error(`Command prefix conflict with "${existingCommand}"`)
81
- }
82
- }
83
-
84
- commands.set(command.name, command)
85
- debugLog(`Registered terminal command: ${command.name} → ${command.applicationId}`)
86
- }
87
-
88
- /**
89
- * List all registered command names
90
- */
91
- const listCommands = (): string[] => {
92
- return Array.from(commands.keys())
93
- }
94
-
95
- /**
96
- * Get a single command by name
97
- */
98
- const getCommand = (name: string): TerminalCommand | undefined => {
99
- return commands.get(name)
100
- }
101
-
102
- /**
103
- * Get all registered commands with metadata
104
- */
105
- const getAllCommands = (): TerminalCommand[] => {
106
- return Array.from(commands.values())
107
- }
108
-
109
- /**
110
- * Execute a terminal command
111
- */
112
- const execCommand = async (input: string): Promise<CommandOutput | void> => {
113
- const applicationManager = useApplicationManager()
114
-
115
- const trimmed = input.trim()
116
- const [name, ...rest] = trimmed.split(/\s+/)
117
-
118
- if (!name) return
119
-
120
- // ── built-in: help ────────────────────────────────────────────────────────
121
- if (name === 'help') {
122
- const target = rest[0]
123
-
124
- if (!target) {
125
- return { message: buildHelpList() }
126
- }
127
-
128
- const cmd = commands.get(target)
129
- if (!cmd) {
130
- return {
131
- message: `help: command not found: '${target}'\nType 'help' to list available commands.`,
132
- isError: true,
133
- }
134
- }
135
-
136
- return { message: buildHelpDetail(cmd) }
137
- }
138
- // ─────────────────────────────────────────────────────────────────────────
139
-
140
- if (!commands.has(name)) {
141
- return {
142
- message: `Command not found: '${name}'\nType 'help' to list available commands.`,
143
- isError: true,
144
- }
145
- }
146
-
147
- const application = commands.get(name)!
148
-
149
- return applicationManager.execAppCommand(application.applicationId, input)
150
- }
151
-
152
- return {
153
- addCommand,
154
- listCommands,
155
- getCommand,
156
- getAllCommands,
157
- execCommand,
158
- }
159
- }
@@ -1,35 +0,0 @@
1
- import { inject } from 'vue'
2
- import {
3
- useDesktopWindowStore,
4
- } from '../stores/storeDesktopWindow'
5
- import { toggleWindowMaximizeLayout } from '../utils/utilWindowMaximizeLayout'
6
- import {
7
- DESKTOP_TOGGLE_WINDOW_MAXIMIZE_KEY,
8
- type DesktopToggleWindowMaximizeFn,
9
- } from '../constants/desktopShellKeys'
10
-
11
- export type { DesktopToggleWindowMaximizeFn }
12
-
13
- /**
14
- * Maximize / restore a window. Themes may override via
15
- * {@link DESKTOP_TOGGLE_WINDOW_MAXIMIZE_KEY}; otherwise uses
16
- * {@link useDesktopWindowStore.workArea} (when measured) or
17
- * {@link IWindowController.actions.toggleMaximize}.
18
- */
19
- export function useToggleWindowMaximize(): DesktopToggleWindowMaximizeFn {
20
- const override = inject(DESKTOP_TOGGLE_WINDOW_MAXIMIZE_KEY, null)
21
- if (override) return override
22
-
23
- const desktopWindowStore = useDesktopWindowStore()
24
-
25
- return function toggle(win: IWindowController | undefined) {
26
- if (!win?.instanced) return false
27
-
28
- const workArea = desktopWindowStore.workArea
29
- if (workArea.width > 0 && workArea.height > 0) {
30
- return toggleWindowMaximizeLayout(win, workArea)
31
- }
32
-
33
- return win.actions.toggleMaximize()
34
- }
35
- }
@@ -1,47 +0,0 @@
1
- import type { IWindowController } from '@owdproject/core'
2
- import { useDesktopWindowStore } from '../stores/storeDesktopWindow'
3
- import { useDesktopWorkspaceStore } from '../stores/storeDesktopWorkspace'
4
- import { useWorkspaceEdgeDrop } from './useWorkspaceEdgeDrop'
5
- import { useWindowSnapDrop } from './useWindowSnapDrop'
6
-
7
- /**
8
- * Wire workspace edge drop and window snap to theme `Window.vue` drag events.
9
- */
10
- export function useWindowDragHandlers(
11
- getWindow: () => IWindowController | undefined,
12
- ) {
13
- const desktopWindowStore = useDesktopWindowStore()
14
- const desktopWorkspaceStore = useDesktopWorkspaceStore()
15
- const { beginWindowDrag, endWindowDrag } = useWorkspaceEdgeDrop()
16
- const { beginWindowSnapDrag, endWindowSnapDrag } = useWindowSnapDrop()
17
-
18
- function onDragStart() {
19
- const win = getWindow()
20
- if (!win?.state?.id || desktopWorkspaceStore.overview) return
21
-
22
- beginWindowDrag(win.state.id)
23
- beginWindowSnapDrag(
24
- win.state.id,
25
- desktopWindowStore.workArea,
26
- win.isMaximizable,
27
- )
28
- }
29
-
30
- function onDragEnd(data: { left: number; top: number }) {
31
- const win = getWindow()
32
- if (!win || desktopWorkspaceStore.overview) return
33
-
34
- const snapped = endWindowSnapDrag(win)
35
- if (snapped) return
36
-
37
- const moved = endWindowDrag(win)
38
- if (!moved) {
39
- win.actions.setPosition({ x: data.left, y: data.top })
40
- if (win.state.layout && win.state.layout !== 'normal') {
41
- win.actions.clearLayout()
42
- }
43
- }
44
- }
45
-
46
- return { onDragStart, onDragEnd }
47
- }
@@ -1,112 +0,0 @@
1
- import type { DesktopWorkAreaRect, IWindowController } from '@owdproject/core'
2
- import type { Ref } from 'vue'
3
- import { computed, ref } from 'vue'
4
- import { useDesktopWorkspaceStore } from '../stores/storeDesktopWorkspace'
5
- import {
6
- detectSnapZone,
7
- type SnapZoneHint,
8
- } from '../utils/utilDetectSnapZone'
9
- import { applySnapToWindow, computeSnapRect } from '../utils/utilWindowLayout'
10
- import { useWorkspaceEdgeDrop } from './useWorkspaceEdgeDrop'
11
-
12
- const snapHint = ref<SnapZoneHint | null>(null)
13
- const isSnapDragActive = ref(false)
14
- let snapDraggingWindowId: string | null = null
15
- let activeWorkArea: DesktopWorkAreaRect = { x: 0, y: 0, width: 0, height: 0 }
16
-
17
- function onDocumentPointerMove(e: PointerEvent) {
18
- updateSnapHintFromPointer(e.clientX, e.clientY)
19
- }
20
-
21
- function onDocumentMouseMove(e: MouseEvent) {
22
- updateSnapHintFromPointer(e.clientX, e.clientY)
23
- }
24
-
25
- function stopSnapPointerTracking() {
26
- document.removeEventListener('pointermove', onDocumentPointerMove)
27
- document.removeEventListener('mousemove', onDocumentMouseMove)
28
- }
29
-
30
- function updateSnapHintFromPointer(clientX: number, clientY: number) {
31
- const desktopWorkspaceStore = useDesktopWorkspaceStore()
32
-
33
- if (!isSnapDragActive.value || desktopWorkspaceStore.overview) {
34
- snapHint.value = null
35
- return
36
- }
37
-
38
- const deferSide =
39
- desktopWorkspaceStore.list.length > 1 &&
40
- !desktopWorkspaceStore.overview
41
-
42
- snapHint.value = detectSnapZone(clientX, clientY, activeWorkArea, {
43
- deferSideSnapForWorkspaceEdge: deferSide,
44
- })
45
- }
46
-
47
- /**
48
- * While dragging a maximizable window, highlight snap zones in the shell work area.
49
- */
50
- export function useWindowSnapDrop() {
51
- const desktopWorkspaceStore = useDesktopWorkspaceStore()
52
-
53
- function beginWindowSnapDrag(
54
- windowId: string,
55
- workArea: DesktopWorkAreaRect,
56
- maximizable: boolean,
57
- ) {
58
- if (desktopWorkspaceStore.overview) return
59
- if (!maximizable) return
60
- if (workArea.width <= 0 || workArea.height <= 0) return
61
-
62
- snapDraggingWindowId = windowId
63
- isSnapDragActive.value = true
64
- activeWorkArea = { ...workArea }
65
- snapHint.value = null
66
-
67
- document.addEventListener('pointermove', onDocumentPointerMove, {
68
- passive: true,
69
- })
70
- document.addEventListener('mousemove', onDocumentMouseMove, {
71
- passive: true,
72
- })
73
- }
74
-
75
- function commitSnapDrop(win: IWindowController): boolean {
76
- const zone = snapHint.value
77
- if (!zone || !snapDraggingWindowId) return false
78
-
79
- return applySnapToWindow(win, zone, activeWorkArea)
80
- }
81
-
82
- function endWindowSnapDrag(win?: IWindowController): boolean {
83
- stopSnapPointerTracking()
84
-
85
- let snapped = false
86
- if (win && snapHint.value) {
87
- snapped = commitSnapDrop(win)
88
- }
89
-
90
- isSnapDragActive.value = false
91
- snapDraggingWindowId = null
92
- snapHint.value = null
93
-
94
- return snapped
95
- }
96
-
97
- const snapHintRect = computed(() => {
98
- const zone = snapHint.value
99
- if (!zone || activeWorkArea.width <= 0) return null
100
-
101
- return computeSnapRect(zone, activeWorkArea)
102
- })
103
-
104
- return {
105
- snapHint,
106
- snapHintRect,
107
- isSnapDragActive,
108
- beginWindowSnapDrag,
109
- endWindowSnapDrag,
110
- activeWorkArea: () => activeWorkArea,
111
- }
112
- }