juice-email-cli 2.1.10 → 2.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/package.json +1 -1
- package/src/context-menu.js +76 -27
package/package.json
CHANGED
package/src/context-menu.js
CHANGED
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
* └── 📂 打开 PowerShell (可选)
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
const {
|
|
17
|
+
const { spawnSync } = require('child_process');
|
|
18
|
+
const fs = require('fs');
|
|
18
19
|
const path = require('path');
|
|
19
20
|
const chalk = require('chalk');
|
|
20
21
|
|
|
@@ -34,41 +35,61 @@ function getIconPath() {
|
|
|
34
35
|
|
|
35
36
|
// ─── 注册表操作封装 ───────────────────────────────────────────────────────────
|
|
36
37
|
|
|
38
|
+
const isWindows = process.platform === 'win32';
|
|
39
|
+
|
|
37
40
|
/**
|
|
38
|
-
* 执行 reg add
|
|
41
|
+
* 执行 reg add — 使用 spawnSync 直接调用 reg.exe,避免 cmd.exe 解析特殊字符
|
|
39
42
|
*/
|
|
40
43
|
function regAdd(key, valueName, type, data) {
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
if (!isWindows) return false;
|
|
45
|
+
const args = ['add', key, '/f'];
|
|
46
|
+
if (valueName === '') {
|
|
47
|
+
args.push('/ve');
|
|
48
|
+
} else {
|
|
49
|
+
args.push('/v', valueName);
|
|
50
|
+
}
|
|
51
|
+
if (type) args.push('/t', type);
|
|
52
|
+
if (data !== undefined) args.push('/d', data);
|
|
53
|
+
|
|
54
|
+
const result = spawnSync('reg', args, { stdio: 'pipe' });
|
|
55
|
+
if (result.status !== 0) {
|
|
56
|
+
const msg = (result.stderr || '').toString().trim();
|
|
50
57
|
console.warn(chalk.yellow(` ⚠ reg add 失败\n 键:${key}\n 原因:${msg}`));
|
|
51
58
|
return false;
|
|
52
59
|
}
|
|
60
|
+
return true;
|
|
53
61
|
}
|
|
54
62
|
|
|
55
63
|
/**
|
|
56
|
-
* 执行 reg delete
|
|
64
|
+
* 执行 reg delete — 键不存在时静默忽略
|
|
57
65
|
*/
|
|
58
66
|
function regDelete(key) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
} catch (_) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
67
|
+
if (!isWindows) return false;
|
|
68
|
+
const result = spawnSync('reg', ['delete', key, '/f'], { stdio: 'pipe' });
|
|
69
|
+
return result.status === 0;
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
/**
|
|
68
|
-
*
|
|
73
|
+
* 清理旧版本残留在 HKLM 的注册表项(v1 使用 HKCR/HKLM,需要管理员权限)
|
|
74
|
+
*
|
|
75
|
+
* 旧版本用管理员权限安装时写入 HKLM,升级到 HKCU 版本后这些残留不会被自动清理。
|
|
76
|
+
* Windows 合并 HKLM + HKCU 注册表视图时,HKLM 里的旧菜单名可能覆盖 HKCU 的新菜单名,
|
|
77
|
+
* 导致用户更新后重启电脑仍然看到旧版菜单。
|
|
78
|
+
*
|
|
79
|
+
* 没有管理员权限时 reg delete 静默失败,不影响后续 HKCU 写入。
|
|
69
80
|
*/
|
|
70
|
-
function
|
|
71
|
-
|
|
81
|
+
function cleanLegacyHklmEntries() {
|
|
82
|
+
if (!isWindows) return;
|
|
83
|
+
|
|
84
|
+
// 清理旧版父菜单(HKLM\Software\Classes\SystemFileAssociations\.*\shell\JuiceEmail)
|
|
85
|
+
for (const root of LEGACY_HKLM_ROOTS) {
|
|
86
|
+
regDelete(`${root}\\${PARENT_KEY_NAME}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 清理旧版子命令(HKLM\...\CommandStore\shell\JuiceEmail.*)
|
|
90
|
+
for (const name of Object.values(SUBCMDS)) {
|
|
91
|
+
regDelete(`${LEGACY_HKLM_SUBCMD_SPACE}\\${name}`);
|
|
92
|
+
}
|
|
72
93
|
}
|
|
73
94
|
|
|
74
95
|
// ─── 菜单结构常量 ─────────────────────────────────────────────────────────────
|
|
@@ -91,6 +112,17 @@ const YAML_ROOTS = [
|
|
|
91
112
|
// CommandStore 子命令注册空间
|
|
92
113
|
const SUBCMD_SPACE = 'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CommandStore\\shell';
|
|
93
114
|
|
|
115
|
+
// 旧版本可能残留在 HKLM 的路径(v1 使用 HKCR/HKLM,需要管理员权限)
|
|
116
|
+
// 这些残留不清理会导致 Windows 合并注册表视图时显示旧菜单名
|
|
117
|
+
const LEGACY_HKLM_ROOTS = [
|
|
118
|
+
'HKEY_LOCAL_MACHINE\\Software\\Classes\\SystemFileAssociations\\.html\\shell',
|
|
119
|
+
'HKEY_LOCAL_MACHINE\\Software\\Classes\\SystemFileAssociations\\.htm\\shell',
|
|
120
|
+
'HKEY_LOCAL_MACHINE\\Software\\Classes\\SystemFileAssociations\\.yaml\\shell',
|
|
121
|
+
'HKEY_LOCAL_MACHINE\\Software\\Classes\\SystemFileAssociations\\.yml\\shell',
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
const LEGACY_HKLM_SUBCMD_SPACE = 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CommandStore\\shell';
|
|
125
|
+
|
|
94
126
|
// 子命令名称常量(register / unregister 共用,避免硬编码不一致)
|
|
95
127
|
const SUBCMDS = {
|
|
96
128
|
generate: 'JuiceEmail.Generate',
|
|
@@ -126,8 +158,16 @@ function wrapInteractive(nodePath, scriptPath, cliArgs) {
|
|
|
126
158
|
// ─── 注册 ─────────────────────────────────────────────────────────────────────
|
|
127
159
|
|
|
128
160
|
async function registerContextMenu() {
|
|
161
|
+
if (!isWindows) {
|
|
162
|
+
console.log(chalk.gray(' 右键菜单仅支持 Windows,已跳过。\n'));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
129
166
|
console.log(chalk.cyan('\n 注册 juice 右键菜单(当前用户,无需管理员权限)...\n'));
|
|
130
167
|
|
|
168
|
+
// 先清理旧版本可能残留在 HKLM 的注册表项,避免新旧菜单名冲突
|
|
169
|
+
cleanLegacyHklmEntries();
|
|
170
|
+
|
|
131
171
|
const nodePath = getNodePath();
|
|
132
172
|
const scriptPath = getJuiceScript();
|
|
133
173
|
const iconPath = getIconPath();
|
|
@@ -204,25 +244,33 @@ async function registerContextMenu() {
|
|
|
204
244
|
// ─── 取消注册 ─────────────────────────────────────────────────────────────────
|
|
205
245
|
|
|
206
246
|
async function unregisterContextMenu() {
|
|
247
|
+
if (!isWindows) {
|
|
248
|
+
console.log(chalk.gray(' 右键菜单仅支持 Windows,已跳过。\n'));
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
207
252
|
console.log(chalk.cyan('\n 取消注册 juice 右键菜单...\n'));
|
|
208
253
|
|
|
209
254
|
let removed = 0;
|
|
210
255
|
|
|
211
|
-
// 清理
|
|
256
|
+
// 清理 HKCU 父菜单
|
|
212
257
|
for (const root of HTML_ROOTS) {
|
|
213
258
|
if (regDelete(`${root}\\${PARENT_KEY_NAME}`)) removed++;
|
|
214
259
|
}
|
|
215
260
|
|
|
216
|
-
// 清理 YAML 父菜单
|
|
261
|
+
// 清理 HKCU YAML 父菜单
|
|
217
262
|
for (const root of YAML_ROOTS) {
|
|
218
263
|
if (regDelete(`${root}\\${PARENT_KEY_NAME}`)) removed++;
|
|
219
264
|
}
|
|
220
265
|
|
|
221
|
-
//
|
|
266
|
+
// 清理 HKCU 子命令
|
|
222
267
|
for (const name of Object.values(SUBCMDS)) {
|
|
223
268
|
regDelete(`${SUBCMD_SPACE}\\${name}`);
|
|
224
269
|
}
|
|
225
270
|
|
|
271
|
+
// 同时清理旧版本可能残留在 HKLM 的注册表项
|
|
272
|
+
cleanLegacyHklmEntries();
|
|
273
|
+
|
|
226
274
|
if (removed > 0) {
|
|
227
275
|
console.log(chalk.green(` ✔ 已移除 ${removed} 个右键菜单项。\n`));
|
|
228
276
|
} else {
|
|
@@ -239,11 +287,12 @@ function resolvePwsh() {
|
|
|
239
287
|
path.join(process.env['LOCALAPPDATA'] || '', 'Microsoft', 'PowerShell', 'pwsh.exe'),
|
|
240
288
|
];
|
|
241
289
|
for (const p of candidates) {
|
|
242
|
-
if (
|
|
290
|
+
if (fs.existsSync(p)) return p;
|
|
243
291
|
}
|
|
244
292
|
try {
|
|
245
|
-
const result =
|
|
246
|
-
|
|
293
|
+
const result = spawnSync('where', ['pwsh'], { stdio: 'pipe' });
|
|
294
|
+
const line = result.stdout.toString().trim().split('\n')[0].trim();
|
|
295
|
+
if (line && fs.existsSync(line)) return line;
|
|
247
296
|
} catch (_) {}
|
|
248
297
|
return null;
|
|
249
298
|
}
|