juice-email-cli 2.4.5 → 2.4.7
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/bin/juice.js +7 -7
- package/edm/elabscience/series/activity/_meta.yaml +2 -0
- package/edm/elabscience/series/activity/default/_meta.yaml +2 -0
- package/edm/elabscience/series/activity/default/juice.yaml +34 -0
- package/edm/elabscience/series/activity/default/snippet.html +9 -0
- package/package.json +2 -2
- package/src/context-menu.js +76 -87
- package/src/index.js +80 -1
package/bin/juice.js
CHANGED
|
@@ -111,13 +111,13 @@ program
|
|
|
111
111
|
juice --uninstall (卸载右键菜单)
|
|
112
112
|
|
|
113
113
|
菜单结构:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
用 juice 生成邮件 HTML
|
|
115
|
+
+-- 作为模板,生成邮件 HTML -> juice -f
|
|
116
|
+
+-- 作为片段,拼接邮件 HTML -> juice -s
|
|
117
|
+
+-- 查看可用资源 -> juice view
|
|
118
|
+
+-- 拷贝全部资源 -> juice init --all
|
|
119
|
+
+-- 选择资源拷贝 -> juice init
|
|
120
|
+
+-- 打开 PowerShell (仅已安装 pwsh 时出现)
|
|
121
121
|
|
|
122
122
|
更多信息:https://gitee.com/siriussupreme/juice-cli
|
|
123
123
|
`);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# juice.yaml - 用户配置文件示例
|
|
2
|
+
#
|
|
3
|
+
# 放置位置(优先级从低到高):
|
|
4
|
+
# ~/juice.yaml ← 用户主目录,对所有项目生效
|
|
5
|
+
# <模板文件目录>/juice.yaml ← 仅对当前项目生效(推荐)
|
|
6
|
+
# juice -c <此文件> ← 命令行指定
|
|
7
|
+
#
|
|
8
|
+
# 所有配置项均与 CLI 内置默认值深度合并,未填写的字段自动使用内置值。
|
|
9
|
+
# 完整默认配置见:<CLI安装目录>/defaults/juice.yaml
|
|
10
|
+
|
|
11
|
+
# ─── 模板变量(Mustache 语法)────────────────────────────────────────────────
|
|
12
|
+
variables:
|
|
13
|
+
banner:
|
|
14
|
+
pc: https://files.elabscience.cn/assets/images/article/202604221646/pc.webp
|
|
15
|
+
mobile: https://files.elabscience.cn/assets/images/article/202604221646/mobile.webp
|
|
16
|
+
|
|
17
|
+
information:
|
|
18
|
+
image: https://elabcomfile.oss-us-west-1.aliyuncs.com/edm/202605211020/CD38-NAD.webp
|
|
19
|
+
content: "This study identifies the CD38-NAD<sup>+</sup> axis as an immunometabolic checkpoint in ITP. Anti-CD38 antibodies deplete NAD<sup>+</sup>, driving M1 macrophages and FcγRI upregulation, which increases platelet phagocytosis. Restoring NAD<sup>+</sup> via CD38 inhibition or NMN reprograms macrophages to a resting state and blocks platelet destruction without affecting humoral immunity. In 25 refractory ITP patients, low-dose NMN (450 mg bid × 2 weeks) was well tolerated: 20% achieved platelets ≥50×10<sup>9</sup>/L, and 52% maintained response at week 8, with stable immunoglobulin levels. Thus, targeting CD38-NAD<sup>+</sup> metabolism offers a non-antibody-depleting therapeutic strategy."
|
|
20
|
+
link: https://www.procellsystem.com/resources/publications
|
|
21
|
+
buttonText: View More Publications
|
|
22
|
+
summary: Human plasma and mouse cell supernatant iNOS level were measured using Elabscience<sup>®</sup> Human/Mouse NOS2/iNOS(Nitric Oxide Synthase 2, Inducible) ELISA Kit, respectively.
|
|
23
|
+
|
|
24
|
+
# ── 产品列表 ────────────────────────────────────────────────────────────────
|
|
25
|
+
# 使用 {{#productLists}}...{{/productLists}} 循环遍历
|
|
26
|
+
# {{#productLists}}{{name}}{{catNo}}{{/productLists}}
|
|
27
|
+
productLists:
|
|
28
|
+
- name: Human NOS2/iNOS(Nitric Oxide Synthase 2, Inducible) ELISA Kit
|
|
29
|
+
catNo: E-EL-H0753
|
|
30
|
+
link: https://www.elabscience.com/p/human-nos2-inos-nitric-oxide-synthase-2-inducible-elisa-kit--e-el-h0753
|
|
31
|
+
|
|
32
|
+
- name: Mouse NOS2/iNOS(Nitric Oxide Synthase 2, Inducible) ELISA Kit
|
|
33
|
+
catNo: E-EL-M0696
|
|
34
|
+
link: https://www.elabscience.com/p/mouse-nos2-inos-nitric-oxide-synthase-2-inducible-elisa-kit--e-el-m0696
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{{#banner}}
|
|
2
|
+
<div>
|
|
3
|
+
<img class="{{#banner.mobile}}d-none d-md-block{{/banner.mobile}}{{^banner.mobile}}d-block{{/banner.mobile}} img-fluid m-auto border-0" src="{{banner.pc}}" />
|
|
4
|
+
|
|
5
|
+
{{#banner.mobile}}
|
|
6
|
+
<img class="d-block d-md-none img-fluid m-auto border-0" src="{{banner.mobile}}" />
|
|
7
|
+
{{/banner.mobile}}
|
|
8
|
+
</div>
|
|
9
|
+
{{/banner}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juice-email-cli",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI tool to generate email-client-compatible HTML with juice (CSS inlining) + Mustache templating + minification",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"url": "https://github.com/GuoSirius/juice-cli/issues"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@inquirer/prompts": "^8.
|
|
40
|
+
"@inquirer/prompts": "^8.5.0",
|
|
41
41
|
"chalk": "^5.6.2",
|
|
42
42
|
"commander": "^14.0.3",
|
|
43
43
|
"html-minifier-terser": "^7.2.0",
|
package/src/context-menu.js
CHANGED
|
@@ -6,28 +6,28 @@
|
|
|
6
6
|
*
|
|
7
7
|
* 菜单结构:
|
|
8
8
|
* .html / .htm:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* 用 juice 生成邮件 HTML
|
|
10
|
+
* +-- 作为模板,生成邮件 HTML -> juice -f
|
|
11
|
+
* +-- 作为片段,拼接邮件 HTML -> juice -s
|
|
12
|
+
* +-- 查看可用资源 -> juice view
|
|
13
|
+
* +-- 拷贝全部资源 -> juice init --all
|
|
14
|
+
* +-- 选择资源拷贝 -> juice init
|
|
15
|
+
* +-- 打开 PowerShell (可选)
|
|
16
16
|
*
|
|
17
17
|
* .yaml / .yml:
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
18
|
+
* 用 juice 生成邮件 HTML
|
|
19
|
+
* +-- 作为配置,拼接邮件 HTML -> juice -c
|
|
20
|
+
* +-- 查看可用资源
|
|
21
|
+
* +-- 拷贝全部资源
|
|
22
|
+
* +-- 选择资源拷贝
|
|
23
|
+
* +-- 打开 PowerShell (可选)
|
|
24
24
|
*
|
|
25
25
|
* 文件夹 / 空白处:
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
26
|
+
* 用 juice 生成邮件 HTML
|
|
27
|
+
* +-- 查看可用资源
|
|
28
|
+
* +-- 拷贝全部资源
|
|
29
|
+
* +-- 选择资源拷贝
|
|
30
|
+
* +-- 在此打开终端 (可选)
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
import { spawnSync } from 'child_process';
|
|
@@ -71,12 +71,12 @@ function regAdd(key, valueName, type, data) {
|
|
|
71
71
|
const result = spawnSync('reg', args, { stdio: 'pipe', timeout: 10000 });
|
|
72
72
|
if (result.status !== 0) {
|
|
73
73
|
const msg = (result.stderr || '').toString().trim();
|
|
74
|
-
console.warn(chalk.yellow(`
|
|
74
|
+
console.warn(chalk.yellow(` [!] reg add 失败\n 键:${key}\n 原因:${msg}`));
|
|
75
75
|
return false;
|
|
76
76
|
}
|
|
77
77
|
return true;
|
|
78
78
|
} catch (e) {
|
|
79
|
-
console.warn(chalk.yellow(`
|
|
79
|
+
console.warn(chalk.yellow(` [!] reg add 超时:${key}`));
|
|
80
80
|
return false;
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -158,24 +158,6 @@ function cleanLegacyCommandStore() {
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
// ─── PowerShell 包装 ──────────────────────────────────────────────────────────
|
|
162
|
-
|
|
163
|
-
function wrapInteractive(nodePath, scriptPath, cliArgs) {
|
|
164
|
-
const node = nodePath.replace(/'/g, "''");
|
|
165
|
-
const script = scriptPath.replace(/'/g, "''");
|
|
166
|
-
const ps = [
|
|
167
|
-
`& '${node}' '${script}' ${cliArgs}`,
|
|
168
|
-
`if ($LASTEXITCODE) {`,
|
|
169
|
-
` Write-Host ''`,
|
|
170
|
-
` Write-Host '[Failed] Exit code:' $LASTEXITCODE`,
|
|
171
|
-
` Write-Host '[Re-run] juice ${cliArgs}'`,
|
|
172
|
-
` Write-Host ''`,
|
|
173
|
-
` Read-Host 'Press Enter to close'`,
|
|
174
|
-
`}`,
|
|
175
|
-
].join('; ');
|
|
176
|
-
return `powershell.exe -Command "${ps}"`;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
161
|
// ─── 工具:查找 PowerShell 7 ─────────────────────────────────────────────────
|
|
180
162
|
|
|
181
163
|
function resolvePwsh() {
|
|
@@ -210,59 +192,66 @@ function registerSubCommands(containerPath, kind, nodePath, scriptPath, iconPath
|
|
|
210
192
|
function regResourceCmds(dirPrefix) {
|
|
211
193
|
const cd = dirPrefix || '';
|
|
212
194
|
|
|
213
|
-
//
|
|
195
|
+
// 查看可用资源
|
|
214
196
|
const vKey = `${containerPath}\\shell\\JuiceEmail.ViewRes`;
|
|
215
|
-
regAdd(vKey, 'MUIVerb', 'REG_SZ', '
|
|
197
|
+
regAdd(vKey, 'MUIVerb', 'REG_SZ', '查看可用资源');
|
|
216
198
|
regAdd(vKey, 'Icon', 'REG_SZ', iconPath);
|
|
217
199
|
const vPs = `${cd}& '${node}' '${script}' view; Write-Host ''; Read-Host 'Press Enter to close'`;
|
|
218
200
|
regAdd(`${vKey}\\command`, '', 'REG_SZ', `powershell.exe -Command "${vPs}"`);
|
|
219
201
|
|
|
220
|
-
//
|
|
202
|
+
// 拷贝全部资源
|
|
221
203
|
const aKey = `${containerPath}\\shell\\JuiceEmail.CopyAll`;
|
|
222
|
-
regAdd(aKey, 'MUIVerb', 'REG_SZ', '
|
|
204
|
+
regAdd(aKey, 'MUIVerb', 'REG_SZ', '拷贝全部资源');
|
|
223
205
|
regAdd(aKey, 'Icon', 'REG_SZ', iconPath);
|
|
224
206
|
const aPs = `${cd}& '${node}' '${script}' init --all .; Write-Host ''; Read-Host 'Press Enter to close'`;
|
|
225
207
|
regAdd(`${aKey}\\command`, '', 'REG_SZ', `powershell.exe -Command "${aPs}"`);
|
|
226
208
|
|
|
227
|
-
//
|
|
209
|
+
// 选择资源拷贝
|
|
228
210
|
const pKey = `${containerPath}\\shell\\JuiceEmail.CopyPick`;
|
|
229
|
-
regAdd(pKey, 'MUIVerb', 'REG_SZ', '
|
|
211
|
+
regAdd(pKey, 'MUIVerb', 'REG_SZ', '选择资源拷贝');
|
|
230
212
|
regAdd(pKey, 'Icon', 'REG_SZ', iconPath);
|
|
231
213
|
const pPs = `${cd}& '${node}' '${script}' init; Write-Host ''; Read-Host 'Press Enter to close'`;
|
|
232
214
|
regAdd(`${pKey}\\command`, '', 'REG_SZ', `powershell.exe -Command "${pPs}"`);
|
|
233
215
|
}
|
|
234
216
|
|
|
235
217
|
if (kind === 'html') {
|
|
218
|
+
const cdHtml = `Set-Location (Split-Path '%1'); `;
|
|
219
|
+
|
|
236
220
|
const genKey = `${containerPath}\\shell\\${SUBCMDS.generate}`;
|
|
237
|
-
regAdd(genKey, 'MUIVerb', 'REG_SZ', '
|
|
221
|
+
regAdd(genKey, 'MUIVerb', 'REG_SZ', '作为模板,生成邮件 HTML');
|
|
238
222
|
regAdd(genKey, 'Icon', 'REG_SZ', iconPath);
|
|
239
|
-
|
|
223
|
+
const genPs = `${cdHtml}& '${node}' '${script}' -f '%1'; Write-Host ''; Read-Host 'Press Enter to close'`;
|
|
224
|
+
regAdd(`${genKey}\\command`, '', 'REG_SZ', `powershell.exe -Command "${genPs}"`);
|
|
240
225
|
|
|
241
226
|
const snipKey = `${containerPath}\\shell\\${SUBCMDS.snippet}`;
|
|
242
|
-
regAdd(snipKey, 'MUIVerb', 'REG_SZ', '
|
|
227
|
+
regAdd(snipKey, 'MUIVerb', 'REG_SZ', '作为片段,拼接邮件 HTML');
|
|
243
228
|
regAdd(snipKey, 'Icon', 'REG_SZ', iconPath);
|
|
244
|
-
|
|
229
|
+
const snipPs = `${cdHtml}& '${node}' '${script}' -s '%1'; Write-Host ''; Read-Host 'Press Enter to close'`;
|
|
230
|
+
regAdd(`${snipKey}\\command`, '', 'REG_SZ', `powershell.exe -Command "${snipPs}"`);
|
|
245
231
|
|
|
246
|
-
regResourceCmds(
|
|
232
|
+
regResourceCmds(cdHtml);
|
|
247
233
|
|
|
248
234
|
if (pwshPath) {
|
|
249
235
|
const pwshKey = `${containerPath}\\shell\\${SUBCMDS.pwsh}`;
|
|
250
|
-
regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '
|
|
236
|
+
regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '打开 PowerShell');
|
|
251
237
|
regAdd(pwshKey, 'Icon', 'REG_SZ', pwshPath);
|
|
252
238
|
regAdd(`${pwshKey}\\command`, '', 'REG_SZ',
|
|
253
239
|
`"${pwshPath}" -NoExit -Command "Set-Location -LiteralPath (Split-Path '%1')"`);
|
|
254
240
|
}
|
|
255
241
|
} else if (kind === 'yaml') {
|
|
242
|
+
const cdYaml = `Set-Location (Split-Path '%1'); `;
|
|
243
|
+
|
|
256
244
|
const cfgKey = `${containerPath}\\shell\\${SUBCMDS.config}`;
|
|
257
|
-
regAdd(cfgKey, 'MUIVerb', 'REG_SZ', '
|
|
245
|
+
regAdd(cfgKey, 'MUIVerb', 'REG_SZ', '作为配置,拼接邮件 HTML');
|
|
258
246
|
regAdd(cfgKey, 'Icon', 'REG_SZ', iconPath);
|
|
259
|
-
|
|
247
|
+
const cfgPs = `${cdYaml}& '${node}' '${script}' -c '%1'; Write-Host ''; Read-Host 'Press Enter to close'`;
|
|
248
|
+
regAdd(`${cfgKey}\\command`, '', 'REG_SZ', `powershell.exe -Command "${cfgPs}"`);
|
|
260
249
|
|
|
261
|
-
regResourceCmds(
|
|
250
|
+
regResourceCmds(cdYaml);
|
|
262
251
|
|
|
263
252
|
if (pwshPath) {
|
|
264
253
|
const pwshKey = `${containerPath}\\shell\\${SUBCMDS.pwsh}`;
|
|
265
|
-
regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '
|
|
254
|
+
regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '打开 PowerShell');
|
|
266
255
|
regAdd(pwshKey, 'Icon', 'REG_SZ', pwshPath);
|
|
267
256
|
regAdd(`${pwshKey}\\command`, '', 'REG_SZ',
|
|
268
257
|
`"${pwshPath}" -NoExit -Command "Set-Location -LiteralPath (Split-Path '%1')"`);
|
|
@@ -272,30 +261,30 @@ function registerSubCommands(containerPath, kind, nodePath, scriptPath, iconPath
|
|
|
272
261
|
const dirRef = kind === 'dir' ? '%1' : '%V';
|
|
273
262
|
const cd = `Set-Location '${dirRef}'; `;
|
|
274
263
|
|
|
275
|
-
//
|
|
264
|
+
// 查看可用资源
|
|
276
265
|
const vKey = `${containerPath}\\shell\\JuiceEmail.ViewRes`;
|
|
277
|
-
regAdd(vKey, 'MUIVerb', 'REG_SZ', '
|
|
266
|
+
regAdd(vKey, 'MUIVerb', 'REG_SZ', '查看可用资源');
|
|
278
267
|
regAdd(vKey, 'Icon', 'REG_SZ', iconPath);
|
|
279
268
|
const vPs = `${cd}& '${node}' '${script}' view; Write-Host ''; Read-Host 'Press Enter to close'`;
|
|
280
269
|
regAdd(`${vKey}\\command`, '', 'REG_SZ', `powershell.exe -Command "${vPs}"`);
|
|
281
270
|
|
|
282
|
-
//
|
|
271
|
+
// 拷贝全部资源
|
|
283
272
|
const aKey = `${containerPath}\\shell\\JuiceEmail.CopyAll`;
|
|
284
|
-
regAdd(aKey, 'MUIVerb', 'REG_SZ', '
|
|
273
|
+
regAdd(aKey, 'MUIVerb', 'REG_SZ', '拷贝全部资源');
|
|
285
274
|
regAdd(aKey, 'Icon', 'REG_SZ', iconPath);
|
|
286
275
|
const aPs = `${cd}& '${node}' '${script}' init --all .; Write-Host ''; Read-Host 'Press Enter to close'`;
|
|
287
276
|
regAdd(`${aKey}\\command`, '', 'REG_SZ', `powershell.exe -Command "${aPs}"`);
|
|
288
277
|
|
|
289
|
-
//
|
|
278
|
+
// 选择资源拷贝
|
|
290
279
|
const pKey = `${containerPath}\\shell\\JuiceEmail.CopyPick`;
|
|
291
|
-
regAdd(pKey, 'MUIVerb', 'REG_SZ', '
|
|
280
|
+
regAdd(pKey, 'MUIVerb', 'REG_SZ', '选择资源拷贝');
|
|
292
281
|
regAdd(pKey, 'Icon', 'REG_SZ', iconPath);
|
|
293
282
|
const pPs = `${cd}& '${node}' '${script}' init; Write-Host ''; Read-Host 'Press Enter to close'`;
|
|
294
283
|
regAdd(`${pKey}\\command`, '', 'REG_SZ', `powershell.exe -Command "${pPs}"`);
|
|
295
284
|
|
|
296
285
|
if (pwshPath) {
|
|
297
286
|
const pwshKey = `${containerPath}\\shell\\JuiceEmail.Pwsh`;
|
|
298
|
-
regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '
|
|
287
|
+
regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '在此打开终端');
|
|
299
288
|
regAdd(pwshKey, 'Icon', 'REG_SZ', pwshPath);
|
|
300
289
|
regAdd(`${pwshKey}\\command`, '', 'REG_SZ',
|
|
301
290
|
`"${pwshPath}" -NoExit -Command "Set-Location -LiteralPath '${dirRef}'"`);
|
|
@@ -349,7 +338,7 @@ async function registerContextMenu() {
|
|
|
349
338
|
// .html / .htm → 引用 HTML 容器
|
|
350
339
|
for (const root of HTML_ROOTS) {
|
|
351
340
|
const parentKey = `${root}\\${PARENT_KEY_NAME}`;
|
|
352
|
-
ok = regAdd(parentKey, 'MUIVerb', 'REG_SZ', '
|
|
341
|
+
ok = regAdd(parentKey, 'MUIVerb', 'REG_SZ', '用 juice 生成邮件 HTML') && ok;
|
|
353
342
|
regAdd(parentKey, 'Icon', 'REG_SZ', iconPath);
|
|
354
343
|
regAdd(parentKey, 'ExtendedSubCommandsKey', 'REG_SZ', SUB_CMDS_CONTAINER_HTML);
|
|
355
344
|
}
|
|
@@ -357,7 +346,7 @@ async function registerContextMenu() {
|
|
|
357
346
|
// .yaml / .yml → 引用 YAML 容器
|
|
358
347
|
for (const root of YAML_ROOTS) {
|
|
359
348
|
const parentKey = `${root}\\${PARENT_KEY_NAME}`;
|
|
360
|
-
ok = regAdd(parentKey, 'MUIVerb', 'REG_SZ', '
|
|
349
|
+
ok = regAdd(parentKey, 'MUIVerb', 'REG_SZ', '用 juice 生成邮件 HTML') && ok;
|
|
361
350
|
regAdd(parentKey, 'Icon', 'REG_SZ', iconPath);
|
|
362
351
|
regAdd(parentKey, 'ExtendedSubCommandsKey', 'REG_SZ', SUB_CMDS_CONTAINER_YAML);
|
|
363
352
|
}
|
|
@@ -369,44 +358,44 @@ async function registerContextMenu() {
|
|
|
369
358
|
|
|
370
359
|
// Directory → 引用 Dir 容器
|
|
371
360
|
const dirParent = `${HKCU_SHELL}\\Directory\\shell\\${PARENT_KEY_NAME}`;
|
|
372
|
-
ok = regAdd(dirParent, 'MUIVerb', 'REG_SZ', '
|
|
361
|
+
ok = regAdd(dirParent, 'MUIVerb', 'REG_SZ', '用 juice 生成邮件 HTML') && ok;
|
|
373
362
|
regAdd(dirParent, 'Icon', 'REG_SZ', iconPath);
|
|
374
363
|
regAdd(dirParent, 'ExtendedSubCommandsKey', 'REG_SZ', SUB_CMDS_CONTAINER_DIR);
|
|
375
364
|
|
|
376
365
|
// Directory\Background → 引用 Bg 容器
|
|
377
366
|
const bgParent = `${HKCU_SHELL}\\Directory\\Background\\shell\\${PARENT_KEY_NAME}`;
|
|
378
|
-
ok = regAdd(bgParent, 'MUIVerb', 'REG_SZ', '
|
|
367
|
+
ok = regAdd(bgParent, 'MUIVerb', 'REG_SZ', '用 juice 生成邮件 HTML') && ok;
|
|
379
368
|
regAdd(bgParent, 'Icon', 'REG_SZ', iconPath);
|
|
380
369
|
regAdd(bgParent, 'ExtendedSubCommandsKey', 'REG_SZ', SUB_CMDS_CONTAINER_BG);
|
|
381
370
|
|
|
382
371
|
if (!ok) {
|
|
383
|
-
console.log(chalk.yellow('
|
|
372
|
+
console.log(chalk.yellow(' [!] 部分注册表项写入失败,右键菜单可能不完整。\n'));
|
|
384
373
|
}
|
|
385
374
|
|
|
386
375
|
console.log(
|
|
387
376
|
'\n' +
|
|
388
|
-
chalk.green('
|
|
377
|
+
chalk.green(' [OK] 右键菜单注册完成!') + '\n\n' +
|
|
389
378
|
` ${chalk.bold('.html / .htm')} 文件右键:\n` +
|
|
390
|
-
` ${chalk.bold('
|
|
391
|
-
`
|
|
392
|
-
`
|
|
393
|
-
`
|
|
394
|
-
`
|
|
395
|
-
`
|
|
396
|
-
(pwshPath ? `
|
|
379
|
+
` ${chalk.bold('用 juice 生成邮件 HTML')}\n` +
|
|
380
|
+
` +-- 作为模板,生成邮件 HTML -> juice -f\n` +
|
|
381
|
+
` +-- 作为片段,拼接邮件 HTML -> juice -s\n` +
|
|
382
|
+
` +-- 查看可用资源 -> juice view\n` +
|
|
383
|
+
` +-- 拷贝全部资源 -> juice init --all\n` +
|
|
384
|
+
` +-- 选择资源拷贝 -> juice init\n` +
|
|
385
|
+
(pwshPath ? ` +-- 打开 PowerShell\n` : '') +
|
|
397
386
|
`\n ${chalk.bold('.yaml / .yml')} 文件右键:\n` +
|
|
398
|
-
` ${chalk.bold('
|
|
399
|
-
`
|
|
400
|
-
`
|
|
401
|
-
`
|
|
402
|
-
`
|
|
403
|
-
(pwshPath ? `
|
|
387
|
+
` ${chalk.bold('用 juice 生成邮件 HTML')}\n` +
|
|
388
|
+
` +-- 作为配置,拼接邮件 HTML -> juice -c\n` +
|
|
389
|
+
` +-- 查看可用资源\n` +
|
|
390
|
+
` +-- 拷贝全部资源\n` +
|
|
391
|
+
` +-- 选择资源拷贝\n` +
|
|
392
|
+
(pwshPath ? ` +-- 打开 PowerShell\n` : '') +
|
|
404
393
|
`\n ${chalk.bold('文件夹 / 空白处')} 右键:\n` +
|
|
405
|
-
` ${chalk.bold('
|
|
406
|
-
`
|
|
407
|
-
`
|
|
408
|
-
`
|
|
409
|
-
(pwshPath ? `
|
|
394
|
+
` ${chalk.bold('用 juice 生成邮件 HTML')}\n` +
|
|
395
|
+
` +-- 查看可用资源\n` +
|
|
396
|
+
` +-- 拷贝全部资源\n` +
|
|
397
|
+
` +-- 选择资源拷贝\n` +
|
|
398
|
+
(pwshPath ? ` +-- 在此打开终端\n` : '') +
|
|
410
399
|
'\n' +
|
|
411
400
|
chalk.gray(' 注意:如菜单未出现,请重启文件资源管理器(explorer.exe)。\n')
|
|
412
401
|
);
|
|
@@ -454,9 +443,9 @@ async function unregisterContextMenu() {
|
|
|
454
443
|
cleanLegacyCommandStore();
|
|
455
444
|
|
|
456
445
|
if (removed > 0) {
|
|
457
|
-
console.log(chalk.green(`
|
|
446
|
+
console.log(chalk.green(` [OK] 已移除 ${removed} 个右键菜单项。\n`));
|
|
458
447
|
} else {
|
|
459
|
-
console.log(chalk.gray('
|
|
448
|
+
console.log(chalk.gray(' [INFO] 未找到已注册的右键菜单,无需卸载。\n'));
|
|
460
449
|
}
|
|
461
450
|
}
|
|
462
451
|
|
package/src/index.js
CHANGED
|
@@ -65,6 +65,71 @@ export function findConfigs(configPath, inputFile) {
|
|
|
65
65
|
return { highPriorityPath, homePath: resolveHomeConfig() };
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
// ─── 列出目录下所有配置文件 ─────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
function findYamlConfigFiles(dir) {
|
|
71
|
+
if (!fs.existsSync(dir)) return [];
|
|
72
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
73
|
+
return entries
|
|
74
|
+
.filter(e => e.isFile() && /\.ya?ml$/i.test(e.name) && e.name !== '_meta.yaml' && e.name !== '_meta.yml')
|
|
75
|
+
.map(e => ({ name: e.name, path: path.join(dir, e.name) }));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function resolveProjectConfig(inputFile) {
|
|
79
|
+
const inputDir = path.dirname(path.resolve(inputFile));
|
|
80
|
+
const yamlFiles = findYamlConfigFiles(inputDir);
|
|
81
|
+
|
|
82
|
+
if (yamlFiles.length === 0) return null;
|
|
83
|
+
|
|
84
|
+
if (yamlFiles.length === 1) {
|
|
85
|
+
const name = yamlFiles[0].name;
|
|
86
|
+
if (name === 'juice.yaml' || name === 'juice.yml') {
|
|
87
|
+
return yamlFiles[0].path;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const { select, input } = await import('@inquirer/prompts');
|
|
92
|
+
|
|
93
|
+
const choices = yamlFiles.map(f => {
|
|
94
|
+
const isOptimal = f.name === 'juice.yaml';
|
|
95
|
+
if (isOptimal) {
|
|
96
|
+
return {
|
|
97
|
+
name: `${chalk.green('●')} ${f.name} ${chalk.green('(最优配对)')}`,
|
|
98
|
+
value: { type: 'file', path: f.path },
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
name: ` ${f.name}`,
|
|
103
|
+
value: { type: 'file', path: f.path },
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const defaultIdx = yamlFiles.findIndex(f => f.name === 'juice.yaml');
|
|
108
|
+
|
|
109
|
+
choices.push(
|
|
110
|
+
{ name: ' [自定义] 输入其他路径...', value: { type: 'custom' } },
|
|
111
|
+
{ name: ' [跳过] 不使用项目配置', value: { type: 'skip' } },
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const result = await select({
|
|
115
|
+
message: '请选择配置文件:',
|
|
116
|
+
choices,
|
|
117
|
+
default: defaultIdx >= 0 ? defaultIdx : 0,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (result.type === 'custom') {
|
|
121
|
+
const customPath = await input({ message: '请输入配置文件路径:' });
|
|
122
|
+
const resolved = path.resolve(customPath);
|
|
123
|
+
if (!fs.existsSync(resolved)) {
|
|
124
|
+
throw new Error(`指定的配置文件不存在:${resolved}`);
|
|
125
|
+
}
|
|
126
|
+
return resolved;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (result.type === 'skip') return null;
|
|
130
|
+
return result.path;
|
|
131
|
+
}
|
|
132
|
+
|
|
68
133
|
// ─── 配置文件加载 ─────────────────────────────────────────────────────────────
|
|
69
134
|
|
|
70
135
|
export function loadYaml(filePath) {
|
|
@@ -207,7 +272,21 @@ export async function run({ file, config: configPath }) {
|
|
|
207
272
|
process.exit(1);
|
|
208
273
|
}
|
|
209
274
|
|
|
210
|
-
|
|
275
|
+
let highPriorityPath = null;
|
|
276
|
+
|
|
277
|
+
if (configPath) {
|
|
278
|
+
const resolved = path.resolve(configPath);
|
|
279
|
+
if (!fs.existsSync(resolved)) {
|
|
280
|
+
throw new Error(`指定的配置文件不存在:${resolved}`);
|
|
281
|
+
}
|
|
282
|
+
highPriorityPath = resolved;
|
|
283
|
+
} else {
|
|
284
|
+
spinner.stop();
|
|
285
|
+
highPriorityPath = await resolveProjectConfig(inputFile);
|
|
286
|
+
spinner.start();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const homePath = resolveHomeConfig();
|
|
211
290
|
const { config, layers } = buildConfig(highPriorityPath, homePath);
|
|
212
291
|
const encoding = (config.output && config.output.encoding) || 'utf8';
|
|
213
292
|
|