juice-email-cli 2.3.2 โ†’ 2.3.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juice-email-cli",
3
- "version": "2.3.2",
3
+ "version": "2.3.3",
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": {
@@ -228,7 +228,7 @@ function registerSubCommands(containerPath, kind, nodePath, scriptPath, iconPath
228
228
  const viewKey = `${containerPath}\\shell\\${SUBCMDS.viewEdm}`;
229
229
  regAdd(viewKey, 'MUIVerb', 'REG_SZ', '๐Ÿ“ฆ ๆŸฅ็œ‹่ต„ๆบๅˆ—่กจ');
230
230
  regAdd(viewKey, 'Icon', 'REG_SZ', iconPath);
231
- regAdd(`${viewKey}\\command`, '', 'REG_SZ', wrapInteractive(nodePath, scriptPath, 'view'));
231
+ regAdd(`${viewKey}\\command`, '', 'REG_SZ', wrapWithPause(nodePath, scriptPath, 'view'));
232
232
 
233
233
  const viewIntKey = `${containerPath}\\shell\\${SUBCMDS.viewEdmInt}`;
234
234
  regAdd(viewIntKey, 'MUIVerb', 'REG_SZ', '๐Ÿ“‹ ๆต่งˆ่ต„ๆบๅบ“');
@@ -246,52 +246,71 @@ function registerSubCommands(containerPath, kind, nodePath, scriptPath, iconPath
246
246
  }
247
247
  }
248
248
 
249
+ // โ”€โ”€โ”€ ้žไบคไบ’ๅ‘ฝไปคๅŒ…่ฃ…๏ผˆๅง‹็ปˆๆš‚ๅœไปฅไพฟ็”จๆˆทๆŸฅ็œ‹่พ“ๅ‡บ๏ผ‰โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
250
+
251
+ function wrapWithPause(nodePath, scriptPath, cliArgs) {
252
+ const node = nodePath.replace(/'/g, "''");
253
+ const script = scriptPath.replace(/'/g, "''");
254
+ const ps = [
255
+ `& '${node}' '${script}' ${cliArgs}`,
256
+ `Write-Host ''`,
257
+ `Read-Host 'Press Enter to close'`,
258
+ ].join('; ');
259
+ return `powershell.exe -Command "${ps}"`;
260
+ }
261
+
249
262
  // โ”€โ”€โ”€ Directory / Background ่œๅ•ๆณจๅ†Œ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
250
263
 
264
+ /**
265
+ * Register individual shell commands for Directory and Directory\Background.
266
+ * ExtendedSubCommandsKey doesn't work reliably for non-file-type associations,
267
+ * so we register each item as a standalone shell verb.
268
+ */
251
269
  function registerDirBgMenus(nodePath, scriptPath, iconPath, pwshPath) {
252
- // Register directory container subcommands
253
- const dirContainer = `${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_DIR}`;
254
- const bgContainer = `${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_BG}`;
270
+ const roots = [
271
+ `${HKCU_SHELL}\\Directory\\shell`,
272
+ `${HKCU_SHELL}\\Directory\\Background\\shell`,
273
+ ];
255
274
 
256
- for (const containerPath of [dirContainer, bgContainer]) {
275
+ // First, clean up old keys from all previous versions to prevent stale entries
276
+ for (const root of roots) {
277
+ // Old parent key (ExtendedSubCommandsKey approach, v2.x commit 3fc761e)
278
+ regDelete(`${root}\\${PARENT_KEY_NAME}`);
279
+ // Old individual entries from any prior version
280
+ regDelete(`${root}\\JuiceEmail.Init`);
281
+ regDelete(`${root}\\JuiceEmail.View`);
282
+ regDelete(`${root}\\JuiceEmail.Browse`);
283
+ regDelete(`${root}\\JuiceEmail.Pwsh`);
284
+ }
285
+
286
+ for (const root of roots) {
257
287
  // ๐Ÿ“ฅ ไปŽ่ต„ๆบๅบ“ๆ‹ท่ดๅˆฐๆญคๅค„
258
- const initKey = `${containerPath}\\shell\\${SUBCMDS.initEdm}`;
288
+ const initKey = `${root}\\JuiceEmail.Init`;
259
289
  regAdd(initKey, 'MUIVerb', 'REG_SZ', '๐Ÿ“ฅ ไปŽ่ต„ๆบๅบ“ๆ‹ท่ดๅˆฐๆญคๅค„');
260
290
  regAdd(initKey, 'Icon', 'REG_SZ', iconPath);
261
291
  regAdd(`${initKey}\\command`, '', 'REG_SZ', wrapInteractive(nodePath, scriptPath, 'init'));
262
292
 
263
293
  // ๐Ÿ“ฆ ๆŸฅ็œ‹่ต„ๆบๅˆ—่กจ
264
- const viewKey = `${containerPath}\\shell\\${SUBCMDS.viewEdm}`;
294
+ const viewKey = `${root}\\JuiceEmail.View`;
265
295
  regAdd(viewKey, 'MUIVerb', 'REG_SZ', '๐Ÿ“ฆ ๆŸฅ็œ‹่ต„ๆบๅˆ—่กจ');
266
296
  regAdd(viewKey, 'Icon', 'REG_SZ', iconPath);
267
- regAdd(`${viewKey}\\command`, '', 'REG_SZ', wrapInteractive(nodePath, scriptPath, 'view'));
297
+ regAdd(`${viewKey}\\command`, '', 'REG_SZ', wrapWithPause(nodePath, scriptPath, 'view'));
268
298
 
269
299
  // ๐Ÿ“‹ ๆต่งˆ่ต„ๆบๅบ“
270
- const viewIntKey = `${containerPath}\\shell\\${SUBCMDS.viewEdmInt}`;
271
- regAdd(viewIntKey, 'MUIVerb', 'REG_SZ', '๐Ÿ“‹ ๆต่งˆ่ต„ๆบๅบ“');
272
- regAdd(viewIntKey, 'Icon', 'REG_SZ', iconPath);
273
- regAdd(`${viewIntKey}\\command`, '', 'REG_SZ', wrapInteractive(nodePath, scriptPath, 'view -i'));
300
+ const browseKey = `${root}\\JuiceEmail.Browse`;
301
+ regAdd(browseKey, 'MUIVerb', 'REG_SZ', '๐Ÿ“‹ ๆต่งˆ่ต„ๆบๅบ“');
302
+ regAdd(browseKey, 'Icon', 'REG_SZ', iconPath);
303
+ regAdd(`${browseKey}\\command`, '', 'REG_SZ', wrapInteractive(nodePath, scriptPath, 'view -i'));
274
304
 
275
305
  // ๐Ÿ“‚ ๅœจๆญคๆ‰“ๅผ€็ปˆ็ซฏ
276
306
  if (pwshPath) {
277
- const pwshKey = `${containerPath}\\shell\\${SUBCMDS.pwsh}`;
307
+ const pwshKey = `${root}\\JuiceEmail.Pwsh`;
278
308
  regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '๐Ÿ“‚ ๅœจๆญคๆ‰“ๅผ€็ปˆ็ซฏ');
279
309
  regAdd(pwshKey, 'Icon', 'REG_SZ', pwshPath);
280
310
  regAdd(`${pwshKey}\\command`, '', 'REG_SZ',
281
- `"${pwshPath}" -NoExit -Command "Set-Location -LiteralPath '%1'"`);
311
+ `"${pwshPath}" -NoExit -Command "Set-Location -LiteralPath '%V'"`);
282
312
  }
283
313
  }
284
-
285
- // Parent keys: Directory and Directory\Background
286
- const dirParent = `${HKCU_SHELL}\\Directory\\shell\\${PARENT_KEY_NAME}`;
287
- regAdd(dirParent, 'MUIVerb', 'REG_SZ', '๐Ÿ“ง juice ้‚ฎไปถๅทฅๅ…ท');
288
- regAdd(dirParent, 'Icon', 'REG_SZ', iconPath);
289
- regAdd(dirParent, 'ExtendedSubCommandsKey', 'REG_SZ', SUB_CMDS_CONTAINER_DIR);
290
-
291
- const bgParent = `${HKCU_SHELL}\\Directory\\Background\\shell\\${PARENT_KEY_NAME}`;
292
- regAdd(bgParent, 'MUIVerb', 'REG_SZ', '๐Ÿ“ง juice ้‚ฎไปถๅทฅๅ…ท');
293
- regAdd(bgParent, 'Icon', 'REG_SZ', iconPath);
294
- regAdd(bgParent, 'ExtendedSubCommandsKey', 'REG_SZ', SUB_CMDS_CONTAINER_BG);
295
314
  }
296
315
 
297
316
  // โ”€โ”€โ”€ ๆณจๅ†Œ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -373,11 +392,10 @@ async function registerContextMenu() {
373
392
  ` โ”œโ”€โ”€ ๐Ÿ“‹ ๆต่งˆ่ต„ๆบๅบ“ โ†’ juice view -i\n` +
374
393
  (pwshPath ? ` โ””โ”€โ”€ ๐Ÿ“‚ ๆ‰“ๅผ€ PowerShell\n` : '') +
375
394
  `\n ${chalk.bold('ๆ–‡ไปถๅคน / ็ฉบ็™ฝๅค„')} ๅณ้”ฎ๏ผš\n` +
376
- ` ${chalk.bold('๐Ÿ“ง juice ้‚ฎไปถๅทฅๅ…ท')}\n` +
377
- ` โ”œโ”€โ”€ ๐Ÿ“ฅ ไปŽ่ต„ๆบๅบ“ๆ‹ท่ดๅˆฐๆญคๅค„ โ†’ juice init\n` +
378
- ` โ”œโ”€โ”€ ๐Ÿ“ฆ ๆŸฅ็œ‹่ต„ๆบๅˆ—่กจ โ†’ juice view\n` +
379
- ` โ”œโ”€โ”€ ๐Ÿ“‹ ๆต่งˆ่ต„ๆบๅบ“ โ†’ juice view -i\n` +
380
- (pwshPath ? ` โ””โ”€โ”€ ๐Ÿ“‚ ๅœจๆญคๆ‰“ๅผ€็ปˆ็ซฏ\n` : '') +
395
+ ` ๐Ÿ“ฅ ไปŽ่ต„ๆบๅบ“ๆ‹ท่ดๅˆฐๆญคๅค„ โ†’ juice init\n` +
396
+ ` ๐Ÿ“ฆ ๆŸฅ็œ‹่ต„ๆบๅˆ—่กจ โ†’ juice view\n` +
397
+ ` ๐Ÿ“‹ ๆต่งˆ่ต„ๆบๅบ“ โ†’ juice view -i\n` +
398
+ (pwshPath ? ` ๐Ÿ“‚ ๅœจๆญคๆ‰“ๅผ€็ปˆ็ซฏ\n` : '') +
381
399
  '\n' +
382
400
  chalk.gray(' ๆณจๆ„๏ผšๅฆ‚่œๅ•ๆœชๅ‡บ็Žฐ๏ผŒ่ฏท้‡ๅฏๆ–‡ไปถ่ต„ๆบ็ฎก็†ๅ™จ๏ผˆexplorer.exe๏ผ‰ใ€‚\n')
383
401
  );
@@ -395,22 +413,35 @@ async function unregisterContextMenu() {
395
413
 
396
414
  let removed = 0;
397
415
 
398
- // ๆธ…็† HKCU ็ˆถ่œๅ•๏ผˆ4 ไธชๆ–‡ไปถ็ฑปๅž‹ + Directory + Background๏ผ‰
416
+ // ๆธ…็† HKCU ็ˆถ่œๅ•๏ผˆๆ–‡ไปถ็ฑปๅž‹๏ผ‰
399
417
  const allRoots = [
400
418
  ...HTML_ROOTS,
401
419
  ...YAML_ROOTS,
402
- `${HKCU_SHELL}\\Directory\\shell`,
403
- `${HKCU_SHELL}\\Directory\\Background\\shell`,
404
420
  ];
405
421
  for (const root of allRoots) {
406
422
  if (regDelete(`${root}\\${PARENT_KEY_NAME}`)) removed++;
407
423
  }
408
424
 
409
- // ๆธ…็†ๆ‰€ๆœ‰ๅญๅ‘ฝไปคๅฎนๅ™จ
425
+ // ๆธ…็† Directory / Background ็‹ฌ็ซ‹่œๅ•้กน + ๆ—ง็‰ˆ็ˆถ้”ฎ
426
+ const dirBgRoots = [
427
+ `${HKCU_SHELL}\\Directory\\shell`,
428
+ `${HKCU_SHELL}\\Directory\\Background\\shell`,
429
+ ];
430
+ const dirBgKeys = ['JuiceEmail.Init', 'JuiceEmail.View', 'JuiceEmail.Browse', 'JuiceEmail.Pwsh',
431
+ PARENT_KEY_NAME]; // PARENT_KEY_NAME = 'JuiceEmail' (old ExtendedSubCommandsKey parent)
432
+ for (const root of dirBgRoots) {
433
+ for (const key of dirBgKeys) {
434
+ if (regDelete(`${root}\\${key}`)) removed++;
435
+ }
436
+ }
437
+
438
+ // ๆธ…็†ๆ‰€ๆœ‰ๅญๅ‘ฝไปคๅฎนๅ™จ๏ผˆๅซๆ—ง็‰ˆ Dir/Bg ๅฎนๅ™จ๏ผ‰
410
439
  if (regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_HTML}`)) removed++;
411
440
  if (regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_YAML}`)) removed++;
412
441
  if (regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_DIR}`)) removed++;
413
442
  if (regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_BG}`)) removed++;
443
+ if (regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_DIR}`)) removed++;
444
+ if (regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_BG}`)) removed++;
414
445
 
415
446
  // ๆธ…็†ๆ—ง็‰ˆๅ…ฑไบซๅฎนๅ™จ๏ผˆv2.1.14 ไน‹ๅ‰ๅชๆœ‰ไธ€ไธชๅฎนๅ™จ๏ผ‰
416
447
  regDelete(`${HKCU_SHELL}\\JuiceEmail.SubCommands`);
package/src/view.js CHANGED
@@ -420,20 +420,6 @@ async function interactiveBrowse(edmDir, startParsed) {
420
420
  description: allSeries.length + ' ไธช็ณปๅˆ—',
421
421
  });
422
422
  }
423
-
424
- // Copy options
425
- if (versions.length > 0) {
426
- choices.push({
427
- name: '๐Ÿ“ฅ ๆ‹ท่ดๆจกๆฟๅˆฐๅฝ“ๅ‰็›ฎๅฝ•',
428
- value: { action: 'copy-template' },
429
- });
430
- }
431
- if (allSeries.length > 0) {
432
- choices.push({
433
- name: '๐Ÿ“ฅ ๆ‹ท่ด้…็ฝฎๅˆฐๅฝ“ๅ‰็›ฎๅฝ•',
434
- value: { action: 'copy-config-brand' },
435
- });
436
- }
437
423
  break;
438
424
  }
439
425
 
@@ -511,27 +497,15 @@ async function interactiveBrowse(edmDir, startParsed) {
511
497
  if (infoLines.length > 0) {
512
498
  console.log(chalk.dim('\n ' + infoLines.join('\n ') + '\n'));
513
499
  }
514
- // Copy actions
515
- if (fs.existsSync(snipPath)) {
516
- choices.push({
517
- name: '๐Ÿ“ฅ ๆ‹ท่ด็‰‡ๆฎตๅˆฐๅฝ“ๅ‰็›ฎๅฝ•',
518
- value: { action: 'copy-snippet' },
519
- });
520
- }
521
- if (configs.length > 0) {
522
- choices.push({
523
- name: '๐Ÿ“ฅ ๆ‹ท่ด้…็ฝฎๅˆฐๅฝ“ๅ‰็›ฎๅฝ•',
524
- value: { action: 'copy-config' },
525
- });
526
- }
527
- // Also allow copying the template
500
+ // Multi-select copy
501
+ const hasResources = fs.existsSync(snipPath) || configs.length > 0;
528
502
  let versions;
529
503
  const brandDir = path.join(edmDir, current.brand);
530
504
  try { versions = findTemplateVersions(brandDir); } catch (_) { versions = []; }
531
- if (versions.length > 0) {
505
+ if (hasResources || versions.length > 0) {
532
506
  choices.push({
533
- name: '๐Ÿ“ฅ ๆ‹ท่ดๆจกๆฟๅˆฐๅฝ“ๅ‰็›ฎๅฝ•',
534
- value: { action: 'copy-template' },
507
+ name: '๐Ÿ“ฅ ๆ‹ท่ดๅˆฐๅฝ“ๅ‰็›ฎๅฝ•',
508
+ value: { action: 'copy-multi' },
535
509
  });
536
510
  }
537
511
  break;
@@ -558,125 +532,95 @@ async function interactiveBrowse(edmDir, startParsed) {
558
532
 
559
533
  // Copy actions
560
534
  if (result.action === 'copy-template') {
561
- let tplPath;
562
- if (current.level === 'brand') {
563
- const brandDir = path.join(edmDir, current.brand);
564
- const versions = findTemplateVersions(brandDir);
565
- if (versions.length === 1) {
566
- tplPath = versions[0].templatePath;
567
- } else {
568
- // Let user pick which template version
569
- const tplChoices = versions.map(v => ({
570
- name: formatName(v.meta, v.name) + formatDesc(v.meta),
571
- value: v.templatePath,
572
- }));
573
- tplPath = await showMenu('้€‰ๆ‹ฉ่ฆๆ‹ท่ด็š„ๆจกๆฟ็‰ˆๆœฌ', tplChoices);
574
- }
575
- } else if (current.templatePath) {
576
- tplPath = current.templatePath;
577
- } else if (current.versionData) {
578
- tplPath = current.versionData.templatePath;
579
- } else {
580
- const brandDir = path.join(edmDir, current.brand);
581
- const versions = findTemplateVersions(brandDir);
582
- if (versions.length === 0) continue;
583
- if (versions.length === 1) {
584
- tplPath = versions[0].templatePath;
585
- } else {
586
- const tplChoices = versions.map(v => ({
587
- name: formatName(v.meta, v.name) + formatDesc(v.meta),
588
- value: v.templatePath,
589
- }));
590
- tplPath = await showMenu('้€‰ๆ‹ฉ่ฆๆ‹ท่ด็š„ๆจกๆฟ็‰ˆๆœฌ', tplChoices);
591
- }
592
- }
593
- if (tplPath) {
535
+ const v = current.versionData;
536
+ if (v && v.templatePath) {
594
537
  console.log();
595
- await copyResource('template', tplPath, process.cwd());
538
+ await copyResource('template', v.templatePath, process.cwd());
596
539
  console.log();
597
540
  }
598
541
  continue;
599
542
  }
600
543
 
601
- if (result.action === 'copy-snippet') {
544
+ if (result.action === 'copy-multi') {
545
+ const brandDir = path.join(edmDir, current.brand);
546
+ const copyItems = [];
547
+
548
+ // Template
549
+ let versions;
550
+ try { versions = findTemplateVersions(brandDir); } catch (_) { versions = []; }
551
+ if (versions.length > 0) {
552
+ const tplPath = versions[0].templatePath;
553
+ copyItems.push({
554
+ name: '๐Ÿ“‹ ๆจกๆฟ HTML',
555
+ value: 'template',
556
+ description: path.basename(tplPath),
557
+ checked: true,
558
+ });
559
+ }
560
+
561
+ // Snippet
602
562
  const snipPath = path.join(current.variantData.path, 'snippet.html');
603
563
  if (fs.existsSync(snipPath)) {
604
- console.log();
605
- await copyResource('snippet', snipPath, process.cwd());
606
- console.log();
564
+ copyItems.push({
565
+ name: '๐Ÿงฉ ็‰‡ๆฎต HTML',
566
+ value: 'snippet',
567
+ description: path.basename(snipPath),
568
+ checked: true,
569
+ });
607
570
  }
608
- continue;
609
- }
610
571
 
611
- if (result.action === 'copy-config') {
572
+ // Config
612
573
  const configs = findConfigs(current.variantData.path);
613
- if (configs.length === 1) {
614
- console.log();
615
- await copyResource('config', configs[0].path, process.cwd());
616
- console.log();
617
- } else if (configs.length > 1) {
618
- const cfgChoices = configs.map(c => ({
619
- name: c.isOptimal ? c.name + chalk.green(' (ๆœ€ไผ˜้…ๅฏน)') : c.name,
620
- value: c.path,
621
- }));
622
- const cfgPath = await showMenu('้€‰ๆ‹ฉ่ฆๆ‹ท่ด็š„้…็ฝฎๆ–‡ไปถ', cfgChoices);
623
- if (cfgPath) {
624
- console.log();
625
- await copyResource('config', cfgPath, process.cwd());
626
- console.log();
627
- }
628
- }
629
- continue;
630
- }
631
-
632
- if (result.action === 'copy-config-brand') {
633
- // From brand level: pick series โ†’ variant โ†’ config
634
- const brandDir = path.join(edmDir, current.brand);
635
- const allSeries = findSeriesDirs(brandDir);
636
- if (allSeries.length === 0) continue;
637
-
638
- let series;
639
- if (allSeries.length === 1) {
640
- series = allSeries[0];
641
- } else {
642
- const sChoices = allSeries.map(s => ({
643
- name: formatName(s.meta, s.name) + formatDesc(s.meta),
644
- value: s,
645
- }));
646
- series = await showMenu('้€‰ๆ‹ฉ็ณปๅˆ—', sChoices);
574
+ if (configs.length > 0) {
575
+ copyItems.push({
576
+ name: 'โš™๏ธ ้…็ฝฎๆ–‡ไปถ',
577
+ value: 'config',
578
+ description: configs.map(c => c.name).join(', '),
579
+ checked: true,
580
+ });
647
581
  }
648
- if (!series) continue;
649
582
 
650
- const variants = findSnippetVariants(series.path);
651
- if (variants.length === 0) continue;
652
- let variant;
653
- if (variants.length === 1) {
654
- variant = variants[0];
655
- } else {
656
- const vChoices = variants.map(v => ({
657
- name: formatName(v.meta, v.name) + formatDesc(v.meta),
658
- value: v,
659
- }));
660
- variant = await showMenu('้€‰ๆ‹ฉๅ˜ไฝ“', vChoices);
661
- }
662
- if (!variant) continue;
583
+ if (copyItems.length > 0) {
584
+ const { checkbox } = await import('@inquirer/prompts');
585
+ const selected = await checkbox({
586
+ message: '้€‰ๆ‹ฉ่ฆๆ‹ท่ด็š„ๅ†…ๅฎน๏ผš',
587
+ choices: copyItems,
588
+ });
663
589
 
664
- const configs = findConfigs(variant.path);
665
- if (configs.length === 0) continue;
666
- let cfgPath;
667
- if (configs.length === 1) {
668
- cfgPath = configs[0].path;
669
- } else {
670
- const cfgChoices = configs.map(c => ({
671
- name: c.isOptimal ? c.name + chalk.green(' (ๆœ€ไผ˜้…ๅฏน)') : c.name,
672
- value: c.path,
673
- }));
674
- cfgPath = await showMenu('้€‰ๆ‹ฉ้…็ฝฎๆ–‡ไปถ', cfgChoices);
675
- }
676
- if (cfgPath) {
677
- console.log();
678
- await copyResource('config', cfgPath, process.cwd());
679
- console.log();
590
+ if (selected.length > 0) {
591
+ const defaultBaseName = `${current.brand}-${current.series}-${current.variant}`;
592
+ const { promptOutputName: pn } = require('./snippet');
593
+ const outputBaseName = await pn(defaultBaseName, process.cwd());
594
+ const cwd = process.cwd();
595
+ const cwdRel = (p) => './' + path.relative(cwd, p);
596
+
597
+ console.log(chalk.green('\nโœ” ๅทฒๆ‹ท่ด๏ผš'));
598
+ if (selected.includes('template') && versions.length > 0) {
599
+ const tplPath = versions[0].templatePath;
600
+ const dest = path.join(cwd, outputBaseName + path.extname(tplPath));
601
+ fs.copyFileSync(tplPath, dest);
602
+ console.log(` ${chalk.cyan('ยท')} ${cwdRel(dest)} ${chalk.gray('(ๆจกๆฟ, ' + fmtBytes(fs.statSync(dest).size) + ')')}`);
603
+ }
604
+ if (selected.includes('snippet') && fs.existsSync(snipPath)) {
605
+ const dest = path.join(cwd, outputBaseName + '-snippet' + path.extname(snipPath));
606
+ fs.copyFileSync(snipPath, dest);
607
+ console.log(` ${chalk.cyan('ยท')} ${cwdRel(dest)} ${chalk.gray('(็‰‡ๆฎต, ' + fmtBytes(fs.statSync(dest).size) + ')')}`);
608
+ }
609
+ if (selected.includes('config') && configs.length > 0) {
610
+ const optimal = configs.find(c => c.isOptimal);
611
+ const cfgPath = optimal ? optimal.path : configs[0].path;
612
+ const dest = path.join(cwd, 'juice.yaml');
613
+ if (fs.existsSync(dest)) {
614
+ const alt = path.join(cwd, 'juice-v1.yaml');
615
+ fs.copyFileSync(cfgPath, alt);
616
+ console.log(` ${chalk.cyan('ยท')} ${cwdRel(alt)} ${chalk.gray('(้…็ฝฎ, ' + fmtBytes(fs.statSync(alt).size) + ')')}`);
617
+ } else {
618
+ fs.copyFileSync(cfgPath, dest);
619
+ console.log(` ${chalk.cyan('ยท')} ${cwdRel(dest)} ${chalk.gray('(้…็ฝฎ, ' + fmtBytes(fs.statSync(dest).size) + ')')}`);
620
+ }
621
+ }
622
+ console.log();
623
+ }
680
624
  }
681
625
  continue;
682
626
  }