juice-email-cli 2.3.0 → 2.3.2

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/README.md CHANGED
@@ -15,7 +15,9 @@
15
15
  - **双文件输出** —— 普通模式生成 `.output.html` + `.minified.html`
16
16
  - **四文件输出** —— 片段模式生成 `.raw.html` + `.html` + `.output.html` + `.minified.html`
17
17
  - **交互模式** —— 无参数运行,逐步选择品牌、模板、片段、配置
18
- - **Windows 右键菜单** —— 子菜单层级,一键注册/卸载
18
+ - **资源浏览** —— `juice view` 查看 EDM 资源树,`-i` 交互式逐层翻页
19
+ - **资源拷贝** —— `juice init` 将模板/片段/配置拷贝到当前目录
20
+ - **Windows 右键菜单** —— 文件/文件夹/空白处多场景支持,一键注册/卸载
19
21
 
20
22
  ---
21
23
 
@@ -97,6 +99,41 @@ juice
97
99
 
98
100
  流程:选择品牌 → 模板 → 片段文件夹 → 片段 HTML → 配置文件 → 输出文件名 → 确认执行。
99
101
 
102
+ ### 浏览资源(查看 EDM 模板库)
103
+
104
+ ```bash
105
+ # 查看完整资源树
106
+ juice view
107
+
108
+ # 查看指定品牌/系列/变体
109
+ juice view elabscience
110
+ juice view elabscience/literature
111
+
112
+ # 列出所有模板 / 系列 / 片段
113
+ juice view --templates
114
+ juice view --series
115
+ juice view --snippets
116
+
117
+ # 交互式浏览(可上下翻层级,叶节点可直接拷贝)
118
+ juice view -i
119
+ juice view -i elabscience
120
+ ```
121
+
122
+ ### 拷贝资源到当前目录
123
+
124
+ ```bash
125
+ # 交互式选择并多选拷贝
126
+ juice init
127
+
128
+ # 从指定 EDM 路径拷贝
129
+ juice init elabscience/templates/standard
130
+
131
+ # 仅拷贝指定文件
132
+ juice init --template edm/elabscience/templates/standard/template.html
133
+ juice init --snippet edm/elabscience/series/literature/default/snippet.html
134
+ juice init --config edm/elabscience/series/literature/default/juice.yaml
135
+ ```
136
+
100
137
  ### 参数说明
101
138
 
102
139
  | 参数 | 简写 | 说明 |
@@ -116,7 +153,9 @@ juice
116
153
  |------|------|------|----------|
117
154
  | ✓ | * | * | 片段模式(-s 指定片段,-f 可选指定模板) |
118
155
  | ✗ | ✓ | * | 普通模式(生成 .output.html + .minified.html) |
119
- | ✗ | ✗ | * | 交互式片段模式(逐步选择) |
156
+ | ✗ | ✗ | | 交互式片段模式(逐步选择) |
157
+ | — | — | — | `juice view [path]` 浏览资源 / `-i` 交互浏览 |
158
+ | — | — | — | `juice init [path]` 拷贝资源到当前目录 |
120
159
 
121
160
  ---
122
161
 
@@ -312,21 +351,36 @@ edm/
312
351
 
313
352
  ## Windows 右键菜单
314
353
 
315
- 注册后,在 `.html` / `.htm` / `.yaml` / `.yml` 文件上右键可看到统一子菜单(所有文件类型共享):
354
+ ### 文件右键(.html / .htm
316
355
 
317
356
  ```
318
357
  📧 用 juice 生成邮件 HTML
319
358
  ├── 📄 作为模板,生成邮件 HTML → juice -f %1(后台执行)
320
359
  ├── 🧩 作为片段,拼接邮件 HTML → juice -s %1(交互选择模板)
321
- ├── ⚙️ 作为配置,拼接邮件 HTML → juice -c %1(交互选择品牌/模板/片段)
360
+ ├── 📦 查看资源列表 → juice view
361
+ ├── 📋 浏览资源库 → juice view -i
322
362
  └── 📂 打开 PowerShell ← 仅已安装 pwsh 时出现
323
363
  ```
324
364
 
325
- | 子命令 | 说明 |
326
- |--------|------|
327
- | 📄 作为模板,生成邮件 HTML | 将文件当模板处理,CSS 内联 + 变量替换 + 压缩 |
328
- | 🧩 作为片段,拼接邮件 HTML | 将文件当片段,打开终端选择模板后拼接输出 |
329
- | ⚙️ 作为配置,拼接邮件 HTML | 将文件当配置,打开终端选择品牌/模板/片段 |
365
+ ### 文件右键(.yaml / .yml)
366
+
367
+ ```
368
+ 📧 juice 生成邮件 HTML
369
+ ├── ⚙️ 作为配置,拼接邮件 HTML juice -c %1(交互选择品牌/模板/片段)
370
+ ├── 📦 查看资源列表 → juice view
371
+ ├── 📋 浏览资源库 → juice view -i
372
+ └── 📂 打开 PowerShell
373
+ ```
374
+
375
+ ### 文件夹 / 空白处右键
376
+
377
+ ```
378
+ 📧 juice 邮件工具
379
+ ├── 📥 从资源库拷贝到此处 → juice init
380
+ ├── 📦 查看资源列表 → juice view
381
+ ├── 📋 浏览资源库 → juice view -i
382
+ └── 📂 在此打开终端
383
+ ```
330
384
 
331
385
  ```bash
332
386
  juice --install # 注册(当前用户,无需管理员)
@@ -342,21 +396,31 @@ juice --uninstall # 卸载
342
396
  ```
343
397
  juice-cli/
344
398
  ├── bin/
345
- │ └── juice.js # CLI 入口
399
+ │ └── juice.js # CLI 入口(Commander.js)
346
400
  ├── src/
347
401
  │ ├── index.js # 核心逻辑(配置合并、模板处理、双输出)
348
402
  │ ├── snippet.js # 片段组装模式 + 交互式提示
403
+ │ ├── view.js # EDM 资源查看/浏览(juice view)
404
+ │ ├── init.js # EDM 资源拷贝初始化(juice init)
349
405
  │ └── context-menu.js # Windows 右键菜单注册
350
406
  ├── defaults/
351
407
  │ └── juice.yaml # CLI 内置默认配置
352
408
  ├── edm/ # EDM 模板库(npm 发布时包含)
353
409
  │ ├── elabscience/
354
- │ │ ├── elabscience-template.html
355
- │ │ └── literature/
356
- │ │ ├── snippet.html
357
- │ │ └── juice.yaml
410
+ │ │ ├── _meta.yaml
411
+ │ │ ├── templates/
412
+ │ │ │ └── standard/
413
+ │ │ └── template.html
414
+ │ │ └── series/
415
+ │ │ └── literature/
416
+ │ │ └── default/
417
+ │ │ ├── snippet.html
418
+ │ │ └── juice.yaml
358
419
  │ └── procell/
359
- └── procell-template.html
420
+ ├── _meta.yaml
421
+ │ └── templates/
422
+ │ └── standard/
423
+ │ └── template.html
360
424
  ├── icons/
361
425
  │ └── juice-icon.ico # 右键菜单图标
362
426
  ├── scripts/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juice-email-cli",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "CLI tool to generate email-client-compatible HTML with juice (CSS inlining) + Mustache templating + minification",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -56,19 +56,28 @@ function regAdd(key, valueName, type, data) {
56
56
  if (type) args.push('/t', type);
57
57
  if (data !== undefined) args.push('/d', data);
58
58
 
59
- const result = spawnSync('reg', args, { stdio: 'pipe' });
60
- if (result.status !== 0) {
61
- const msg = (result.stderr || '').toString().trim();
62
- console.warn(chalk.yellow(` ⚠ reg add 失败\n 键:${key}\n 原因:${msg}`));
59
+ try {
60
+ const result = spawnSync('reg', args, { stdio: 'pipe', timeout: 10000 });
61
+ if (result.status !== 0) {
62
+ const msg = (result.stderr || '').toString().trim();
63
+ console.warn(chalk.yellow(` ⚠ reg add 失败\n 键:${key}\n 原因:${msg}`));
64
+ return false;
65
+ }
66
+ return true;
67
+ } catch (e) {
68
+ console.warn(chalk.yellow(` ⚠ reg add 超时:${key}`));
63
69
  return false;
64
70
  }
65
- return true;
66
71
  }
67
72
 
68
73
  function regDelete(key) {
69
74
  if (!isWindows) return false;
70
- const result = spawnSync('reg', ['delete', key, '/f'], { stdio: 'pipe' });
71
- return result.status === 0;
75
+ try {
76
+ const result = spawnSync('reg', ['delete', key, '/f'], { stdio: 'pipe', timeout: 10000 });
77
+ return result.status === 0;
78
+ } catch (_) {
79
+ return false;
80
+ }
72
81
  }
73
82
 
74
83
  // ─── 菜单结构常量 ─────────────────────────────────────────────────────────────
@@ -307,16 +316,19 @@ async function registerContextMenu() {
307
316
  let ok = true;
308
317
 
309
318
  // 先删除旧容器再重建,避免注册残留旧子命令(如旧版 HTML 容器的 WithConfig)
319
+ console.log(chalk.gray(' 清理旧版菜单...'));
310
320
  regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_HTML}`);
311
321
  regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_YAML}`);
312
322
  regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_DIR}`);
313
323
  regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_BG}`);
314
324
 
315
325
  // HTML 容器:generate + snippet + pwsh
326
+ console.log(chalk.gray(' 注册 .html 文件菜单...'));
316
327
  const htmlContainer = `${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_HTML}`;
317
328
  registerSubCommands(htmlContainer, 'html', nodePath, scriptPath, iconPath, pwshPath);
318
329
 
319
330
  // YAML 容器:config + pwsh
331
+ console.log(chalk.gray(' 注册 .yaml 文件菜单...'));
320
332
  const yamlContainer = `${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_YAML}`;
321
333
  registerSubCommands(yamlContainer, 'yaml', nodePath, scriptPath, iconPath, pwshPath);
322
334
 
@@ -337,6 +349,7 @@ async function registerContextMenu() {
337
349
  }
338
350
 
339
351
  // Directory / Background → 独立容器
352
+ console.log(chalk.gray(' 注册文件夹/空白处菜单...'));
340
353
  registerDirBgMenus(nodePath, scriptPath, iconPath, pwshPath);
341
354
 
342
355
  if (!ok) {