dsh-clean-desktop-shell 0.1.11 → 0.1.13
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/CONTRIBUTORS.md +25 -19
- package/README.en.md +422 -333
- package/README.md +363 -278
- package/electron/aumid.js +17 -17
- package/electron/crashGuard.js +60 -60
- package/electron/error.html +113 -113
- package/electron/main.js +203 -187
- package/electron/outage.js +60 -0
- package/electron/preload.js +216 -49
- package/electron/progress-preload.js +11 -11
- package/electron/progress.html +79 -79
- package/electron/progress.js +56 -56
- package/electron/service.js +559 -458
- package/electron/shortcut.js +122 -122
- package/electron/tray.js +232 -232
- package/electron/window.js +603 -264
- package/lib/client.js +41 -6
- package/lib/common.js +74 -74
- package/lib/icon.js +85 -51
- package/lib/index.js +90 -15
- package/lib/runtime.js +423 -423
- package/package.json +140 -127
- package/scripts/check-syntax.mjs +54 -54
- package/scripts/selftest-recovery.mjs +140 -0
- package/scripts/selftest-runtime.mjs +90 -90
- package/src/client/client.js +41 -6
- package/src/host/common.js +74 -74
- package/src/host/icon.js +85 -51
- package/src/host/index.js +90 -15
- package/src/host/runtime.js +423 -423
- package/version.txt +1 -1
package/electron/shortcut.js
CHANGED
|
@@ -1,122 +1,122 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Desktop shortcut management (Windows .lnk via Electron's shell API).
|
|
3
|
-
*
|
|
4
|
-
* Used by:
|
|
5
|
-
* - first-run prompt in main.js ("create a desktop shortcut?")
|
|
6
|
-
* - the tray "create desktop shortcut" item (always available)
|
|
7
|
-
*
|
|
8
|
-
* Works in both distribution branches:
|
|
9
|
-
* - installer (packaged): targets the installed exe, no arguments
|
|
10
|
-
* - plugin-market (bare runtime): targets the provisioned electron.exe
|
|
11
|
-
* with the plugin's electron/main.js as its argument
|
|
12
|
-
*
|
|
13
|
-
* Pure dev mode (npm run dev from a checkout) has no stable executable,
|
|
14
|
-
* so the feature stays disabled there.
|
|
15
|
-
*
|
|
16
|
-
* Electron's built-in shortcut API replaced the earlier hand-rolled
|
|
17
|
-
* PowerShell + WScript.Shell COM script: fewer moving parts (no spawned
|
|
18
|
-
* interpreter to hang or quote-escape), and it composes with the
|
|
19
|
-
* Start-menu shortcut path that already used the same API.
|
|
20
|
-
*/
|
|
21
|
-
import { app, shell } from 'electron'
|
|
22
|
-
import { existsSync, readdirSync } from 'node:fs'
|
|
23
|
-
import { dirname, join } from 'node:path'
|
|
24
|
-
import { fileURLToPath } from 'node:url'
|
|
25
|
-
import { APP_USER_MODEL_ID } from './aumid.js'
|
|
26
|
-
|
|
27
|
-
const isWin = process.platform === 'win32'
|
|
28
|
-
const SHORTCUT_NAME = 'DSH Clean Desktop Shell.lnk'
|
|
29
|
-
const PKG_ROOT = dirname(dirname(fileURLToPath(import.meta.url)))
|
|
30
|
-
const ICON_ICO = join(PKG_ROOT, 'build', 'icon.ico')
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Plugin-market mode: the host half spawns a bare electron runtime with the
|
|
34
|
-
* plugin's electron/main.js as its entry. process.execPath is the runtime
|
|
35
|
-
* electron.exe; process.argv[1] is the entry script (packaged installers
|
|
36
|
-
* have no such argument).
|
|
37
|
-
*/
|
|
38
|
-
function pluginArgs() {
|
|
39
|
-
if (app.isPackaged) return []
|
|
40
|
-
const entry = process.argv[1]
|
|
41
|
-
if (entry && /\.js$/i.test(entry)) return [entry]
|
|
42
|
-
return []
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Shortcuts need a stable executable — supported when packaged or in
|
|
46
|
-
* plugin mode (where the runtime path is fixed under DSH_HOME). */
|
|
47
|
-
export function shortcutSupported() {
|
|
48
|
-
return isWin && (app.isPackaged || pluginArgs().length > 0)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* The user's real Desktop. app.getPath('desktop') resolves OneDrive
|
|
53
|
-
* redirection and roaming profile policies — a hardcoded ~/Desktop misses
|
|
54
|
-
* those and writes the shortcut where the user will never see it.
|
|
55
|
-
*/
|
|
56
|
-
function desktopDir() {
|
|
57
|
-
try {
|
|
58
|
-
return app.getPath('desktop')
|
|
59
|
-
} catch {
|
|
60
|
-
return join(app.getPath('home'), 'Desktop')
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** True when a desktop shortcut already points at this app's exe. */
|
|
65
|
-
export function hasDesktopShortcut() {
|
|
66
|
-
if (!isWin || !shortcutSupported()) return false
|
|
67
|
-
try {
|
|
68
|
-
const target = process.execPath.toLowerCase()
|
|
69
|
-
for (const entry of readdirSync(desktopDir())) {
|
|
70
|
-
if (!entry.toLowerCase().endsWith('.lnk')) continue
|
|
71
|
-
try {
|
|
72
|
-
// readShortcutLink throws on invalid/non-shortcut files — skip those.
|
|
73
|
-
const link = shell.readShortcutLink(join(desktopDir(), entry))
|
|
74
|
-
if (String(link.target).toLowerCase() === target) return true
|
|
75
|
-
} catch {
|
|
76
|
-
// unreadable .lnk — not ours
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
} catch {
|
|
80
|
-
// unreadable desktop — treat as "not present"
|
|
81
|
-
}
|
|
82
|
-
return false
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Create (or overwrite) the desktop shortcut for this app. Returns bool. */
|
|
86
|
-
export function createDesktopShortcut() {
|
|
87
|
-
if (!shortcutSupported()) return false
|
|
88
|
-
const args = pluginArgs()
|
|
89
|
-
const ico = existsSync(ICON_ICO) ? ICON_ICO : undefined
|
|
90
|
-
// 'overwrite' makes the call idempotent (safe to re-run after updates).
|
|
91
|
-
return shell.writeShortcutLink(join(desktopDir(), SHORTCUT_NAME), 'overwrite', {
|
|
92
|
-
target: process.execPath,
|
|
93
|
-
args: args.length ? args.join(' ') : undefined,
|
|
94
|
-
icon: ico,
|
|
95
|
-
iconIndex: 0,
|
|
96
|
-
appUserModelId: APP_USER_MODEL_ID,
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Ensure a Start-menu shortcut carrying the AppUserModelID. This is what
|
|
102
|
-
* makes the taskbar button show our icon: Windows matches a running
|
|
103
|
-
* window's AUMID to a shortcut's icon (bare runtime electron.exe has no
|
|
104
|
-
* icon resource of its own). Idempotent — the shortcut is only created
|
|
105
|
-
* when missing.
|
|
106
|
-
*/
|
|
107
|
-
export function ensureStartMenuShortcut() {
|
|
108
|
-
if (!isWin || !shortcutSupported()) return false
|
|
109
|
-
const lnkDir = join(
|
|
110
|
-
process.env.APPDATA || join(app.getPath('home'), 'AppData', 'Roaming'),
|
|
111
|
-
'Microsoft', 'Windows', 'Start Menu', 'Programs',
|
|
112
|
-
)
|
|
113
|
-
const lnk = join(lnkDir, SHORTCUT_NAME)
|
|
114
|
-
const args = pluginArgs()
|
|
115
|
-
return shell.writeShortcutLink(lnk, 'create', {
|
|
116
|
-
target: process.execPath,
|
|
117
|
-
args: args.length ? args.join(' ') : undefined,
|
|
118
|
-
icon: existsSync(ICON_ICO) ? ICON_ICO : undefined,
|
|
119
|
-
iconIndex: 0,
|
|
120
|
-
appUserModelId: APP_USER_MODEL_ID,
|
|
121
|
-
})
|
|
122
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Desktop shortcut management (Windows .lnk via Electron's shell API).
|
|
3
|
+
*
|
|
4
|
+
* Used by:
|
|
5
|
+
* - first-run prompt in main.js ("create a desktop shortcut?")
|
|
6
|
+
* - the tray "create desktop shortcut" item (always available)
|
|
7
|
+
*
|
|
8
|
+
* Works in both distribution branches:
|
|
9
|
+
* - installer (packaged): targets the installed exe, no arguments
|
|
10
|
+
* - plugin-market (bare runtime): targets the provisioned electron.exe
|
|
11
|
+
* with the plugin's electron/main.js as its argument
|
|
12
|
+
*
|
|
13
|
+
* Pure dev mode (npm run dev from a checkout) has no stable executable,
|
|
14
|
+
* so the feature stays disabled there.
|
|
15
|
+
*
|
|
16
|
+
* Electron's built-in shortcut API replaced the earlier hand-rolled
|
|
17
|
+
* PowerShell + WScript.Shell COM script: fewer moving parts (no spawned
|
|
18
|
+
* interpreter to hang or quote-escape), and it composes with the
|
|
19
|
+
* Start-menu shortcut path that already used the same API.
|
|
20
|
+
*/
|
|
21
|
+
import { app, shell } from 'electron'
|
|
22
|
+
import { existsSync, readdirSync } from 'node:fs'
|
|
23
|
+
import { dirname, join } from 'node:path'
|
|
24
|
+
import { fileURLToPath } from 'node:url'
|
|
25
|
+
import { APP_USER_MODEL_ID } from './aumid.js'
|
|
26
|
+
|
|
27
|
+
const isWin = process.platform === 'win32'
|
|
28
|
+
const SHORTCUT_NAME = 'DSH Clean Desktop Shell.lnk'
|
|
29
|
+
const PKG_ROOT = dirname(dirname(fileURLToPath(import.meta.url)))
|
|
30
|
+
const ICON_ICO = join(PKG_ROOT, 'build', 'icon.ico')
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Plugin-market mode: the host half spawns a bare electron runtime with the
|
|
34
|
+
* plugin's electron/main.js as its entry. process.execPath is the runtime
|
|
35
|
+
* electron.exe; process.argv[1] is the entry script (packaged installers
|
|
36
|
+
* have no such argument).
|
|
37
|
+
*/
|
|
38
|
+
function pluginArgs() {
|
|
39
|
+
if (app.isPackaged) return []
|
|
40
|
+
const entry = process.argv[1]
|
|
41
|
+
if (entry && /\.js$/i.test(entry)) return [entry]
|
|
42
|
+
return []
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Shortcuts need a stable executable — supported when packaged or in
|
|
46
|
+
* plugin mode (where the runtime path is fixed under DSH_HOME). */
|
|
47
|
+
export function shortcutSupported() {
|
|
48
|
+
return isWin && (app.isPackaged || pluginArgs().length > 0)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The user's real Desktop. app.getPath('desktop') resolves OneDrive
|
|
53
|
+
* redirection and roaming profile policies — a hardcoded ~/Desktop misses
|
|
54
|
+
* those and writes the shortcut where the user will never see it.
|
|
55
|
+
*/
|
|
56
|
+
function desktopDir() {
|
|
57
|
+
try {
|
|
58
|
+
return app.getPath('desktop')
|
|
59
|
+
} catch {
|
|
60
|
+
return join(app.getPath('home'), 'Desktop')
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** True when a desktop shortcut already points at this app's exe. */
|
|
65
|
+
export function hasDesktopShortcut() {
|
|
66
|
+
if (!isWin || !shortcutSupported()) return false
|
|
67
|
+
try {
|
|
68
|
+
const target = process.execPath.toLowerCase()
|
|
69
|
+
for (const entry of readdirSync(desktopDir())) {
|
|
70
|
+
if (!entry.toLowerCase().endsWith('.lnk')) continue
|
|
71
|
+
try {
|
|
72
|
+
// readShortcutLink throws on invalid/non-shortcut files — skip those.
|
|
73
|
+
const link = shell.readShortcutLink(join(desktopDir(), entry))
|
|
74
|
+
if (String(link.target).toLowerCase() === target) return true
|
|
75
|
+
} catch {
|
|
76
|
+
// unreadable .lnk — not ours
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
// unreadable desktop — treat as "not present"
|
|
81
|
+
}
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Create (or overwrite) the desktop shortcut for this app. Returns bool. */
|
|
86
|
+
export function createDesktopShortcut() {
|
|
87
|
+
if (!shortcutSupported()) return false
|
|
88
|
+
const args = pluginArgs()
|
|
89
|
+
const ico = existsSync(ICON_ICO) ? ICON_ICO : undefined
|
|
90
|
+
// 'overwrite' makes the call idempotent (safe to re-run after updates).
|
|
91
|
+
return shell.writeShortcutLink(join(desktopDir(), SHORTCUT_NAME), 'overwrite', {
|
|
92
|
+
target: process.execPath,
|
|
93
|
+
args: args.length ? args.join(' ') : undefined,
|
|
94
|
+
icon: ico,
|
|
95
|
+
iconIndex: 0,
|
|
96
|
+
appUserModelId: APP_USER_MODEL_ID,
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Ensure a Start-menu shortcut carrying the AppUserModelID. This is what
|
|
102
|
+
* makes the taskbar button show our icon: Windows matches a running
|
|
103
|
+
* window's AUMID to a shortcut's icon (bare runtime electron.exe has no
|
|
104
|
+
* icon resource of its own). Idempotent — the shortcut is only created
|
|
105
|
+
* when missing.
|
|
106
|
+
*/
|
|
107
|
+
export function ensureStartMenuShortcut() {
|
|
108
|
+
if (!isWin || !shortcutSupported()) return false
|
|
109
|
+
const lnkDir = join(
|
|
110
|
+
process.env.APPDATA || join(app.getPath('home'), 'AppData', 'Roaming'),
|
|
111
|
+
'Microsoft', 'Windows', 'Start Menu', 'Programs',
|
|
112
|
+
)
|
|
113
|
+
const lnk = join(lnkDir, SHORTCUT_NAME)
|
|
114
|
+
const args = pluginArgs()
|
|
115
|
+
return shell.writeShortcutLink(lnk, 'create', {
|
|
116
|
+
target: process.execPath,
|
|
117
|
+
args: args.length ? args.join(' ') : undefined,
|
|
118
|
+
icon: existsSync(ICON_ICO) ? ICON_ICO : undefined,
|
|
119
|
+
iconIndex: 0,
|
|
120
|
+
appUserModelId: APP_USER_MODEL_ID,
|
|
121
|
+
})
|
|
122
|
+
}
|