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 CHANGED
@@ -111,13 +111,13 @@ program
111
111
  juice --uninstall (卸载右键菜单)
112
112
 
113
113
  菜单结构:
114
- 📧 用 juice 生成邮件 HTML
115
- ├── 📄 作为模板,生成邮件 HTML juice -f(后台执行)
116
- ├── 🧩 作为片段,拼接邮件 HTML juice -s(交互选择模板)
117
- ├── 📋 查看可用资源 juice view
118
- ├── 📦 拷贝全部资源 juice init --all
119
- ├── 📥 选择资源拷贝 juice init
120
- └── 📂 打开 PowerShell (仅已安装 pwsh 时出现)
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,2 @@
1
+ name: "最新活动"
2
+ description: "伊莱瑞特最新活动->促销活动富文本模板"
@@ -0,0 +1,2 @@
1
+ name: "活动模板"
2
+ description: "伊莱瑞特中文站活动详情模板"
@@ -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>&reg;</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.5",
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.4.3",
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",
@@ -6,28 +6,28 @@
6
6
  *
7
7
  * 菜单结构:
8
8
  * .html / .htm:
9
- * 📧 用 juice 生成邮件 HTML
10
- * ├── 📄 作为模板,生成邮件 HTML juice -f(后台执行)
11
- * ├── 🧩 作为片段,拼接邮件 HTML juice -s(交互选择模板)
12
- * ├── 📋 查看可用资源 juice view
13
- * ├── 📦 拷贝全部资源 juice init --all
14
- * ├── 📥 选择资源拷贝 juice init
15
- * └── 📂 打开 PowerShell (可选)
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
- * 📧 用 juice 生成邮件 HTML
19
- * ├── ⚙️ 作为配置,拼接邮件 HTML juice -c(交互选择品牌/模板/片段)
20
- * ├── 📋 查看可用资源
21
- * ├── 📦 拷贝全部资源
22
- * ├── 📥 选择资源拷贝
23
- * └── 📂 打开 PowerShell (可选)
18
+ * 用 juice 生成邮件 HTML
19
+ * +-- 作为配置,拼接邮件 HTML -> juice -c
20
+ * +-- 查看可用资源
21
+ * +-- 拷贝全部资源
22
+ * +-- 选择资源拷贝
23
+ * +-- 打开 PowerShell (可选)
24
24
  *
25
25
  * 文件夹 / 空白处:
26
- * 📧 用 juice 生成邮件 HTML
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(` reg add 失败\n 键:${key}\n 原因:${msg}`));
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(` reg add 超时:${key}`));
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', '📄 作为模板,生成邮件 HTML');
221
+ regAdd(genKey, 'MUIVerb', 'REG_SZ', '作为模板,生成邮件 HTML');
238
222
  regAdd(genKey, 'Icon', 'REG_SZ', iconPath);
239
- regAdd(`${genKey}\\command`, '', 'REG_SZ', `"${nodePath}" "${scriptPath}" -f %1`);
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', '🧩 作为片段,拼接邮件 HTML');
227
+ regAdd(snipKey, 'MUIVerb', 'REG_SZ', '作为片段,拼接邮件 HTML');
243
228
  regAdd(snipKey, 'Icon', 'REG_SZ', iconPath);
244
- regAdd(`${snipKey}\\command`, '', 'REG_SZ', wrapInteractive(nodePath, scriptPath, '-s %1'));
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(`Set-Location (Split-Path '%1'); `);
232
+ regResourceCmds(cdHtml);
247
233
 
248
234
  if (pwshPath) {
249
235
  const pwshKey = `${containerPath}\\shell\\${SUBCMDS.pwsh}`;
250
- regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '📂 打开 PowerShell');
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', '⚙️ 作为配置,拼接邮件 HTML');
245
+ regAdd(cfgKey, 'MUIVerb', 'REG_SZ', '作为配置,拼接邮件 HTML');
258
246
  regAdd(cfgKey, 'Icon', 'REG_SZ', iconPath);
259
- regAdd(`${cfgKey}\\command`, '', 'REG_SZ', wrapInteractive(nodePath, scriptPath, '-c %1'));
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(`Set-Location (Split-Path '%1'); `);
250
+ regResourceCmds(cdYaml);
262
251
 
263
252
  if (pwshPath) {
264
253
  const pwshKey = `${containerPath}\\shell\\${SUBCMDS.pwsh}`;
265
- regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '📂 打开 PowerShell');
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', '📧 用 juice 生成邮件 HTML') && ok;
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', '📧 用 juice 生成邮件 HTML') && ok;
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', '📧 用 juice 生成邮件 HTML') && ok;
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', '📧 用 juice 生成邮件 HTML') && ok;
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(' 部分注册表项写入失败,右键菜单可能不完整。\n'));
372
+ console.log(chalk.yellow(' [!] 部分注册表项写入失败,右键菜单可能不完整。\n'));
384
373
  }
385
374
 
386
375
  console.log(
387
376
  '\n' +
388
- chalk.green(' 右键菜单注册完成!') + '\n\n' +
377
+ chalk.green(' [OK] 右键菜单注册完成!') + '\n\n' +
389
378
  ` ${chalk.bold('.html / .htm')} 文件右键:\n` +
390
- ` ${chalk.bold('📧 用 juice 生成邮件 HTML')}\n` +
391
- ` ├── 📄 作为模板,生成邮件 HTML juice -f(后台执行)\n` +
392
- ` ├── 🧩 作为片段,拼接邮件 HTML juice -s(交互选择模板)\n` +
393
- ` ├── 📋 查看可用资源 juice view\n` +
394
- ` ├── 📦 拷贝全部资源 juice init --all\n` +
395
- ` ├── 📥 选择资源拷贝 juice init\n` +
396
- (pwshPath ? ` └── 📂 打开 PowerShell\n` : '') +
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('📧 用 juice 生成邮件 HTML')}\n` +
399
- ` ├── ⚙️ 作为配置,拼接邮件 HTML juice -c(交互选择品牌/模板/片段)\n` +
400
- ` ├── 📋 查看可用资源\n` +
401
- ` ├── 📦 拷贝全部资源\n` +
402
- ` ├── 📥 选择资源拷贝\n` +
403
- (pwshPath ? ` └── 📂 打开 PowerShell\n` : '') +
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('📧 用 juice 生成邮件 HTML')}\n` +
406
- ` ├── 📋 查看可用资源\n` +
407
- ` ├── 📦 拷贝全部资源\n` +
408
- ` ├── 📥 选择资源拷贝\n` +
409
- (pwshPath ? ` └── 📂 在此打开终端\n` : '') +
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(` 已移除 ${removed} 个右键菜单项。\n`));
446
+ console.log(chalk.green(` [OK] 已移除 ${removed} 个右键菜单项。\n`));
458
447
  } else {
459
- console.log(chalk.gray(' 未找到已注册的右键菜单,无需卸载。\n'));
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
- const { highPriorityPath, homePath } = findConfigs(configPath, inputFile);
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