pake-cli 3.6.0 โ†’ 3.6.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/dist/cli.js CHANGED
@@ -1,10 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import chalk from 'chalk';
3
- import { InvalidArgumentError, program, Option } from 'commander';
4
2
  import log from 'loglevel';
5
3
  import path from 'path';
6
4
  import fsExtra from 'fs-extra';
7
5
  import { fileURLToPath } from 'url';
6
+ import chalk from 'chalk';
8
7
  import prompts from 'prompts';
9
8
  import os from 'os';
10
9
  import { execa, execaSync } from 'execa';
@@ -21,113 +20,7 @@ import { fileTypeFromBuffer } from 'file-type';
21
20
  import icongen from 'icon-gen';
22
21
  import sharp from 'sharp';
23
22
  import * as psl from 'psl';
24
-
25
- var name = "pake-cli";
26
- var version = "3.6.0";
27
- var description = "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with one command. ๐Ÿคฑ๐Ÿป ไธ€้”ฎๆ‰“ๅŒ…็ฝ‘้กต็”Ÿๆˆ่ฝป้‡ๆกŒ้ขๅบ”็”จใ€‚";
28
- var engines = {
29
- node: ">=18.0.0"
30
- };
31
- var packageManager = "pnpm@10.15.0";
32
- var bin = {
33
- pake: "./dist/cli.js"
34
- };
35
- var repository = {
36
- type: "git",
37
- url: "https://github.com/tw93/pake.git"
38
- };
39
- var author = {
40
- name: "Tw93",
41
- email: "tw93@qq.com"
42
- };
43
- var keywords = [
44
- "pake",
45
- "pake-cli",
46
- "rust",
47
- "tauri",
48
- "no-electron",
49
- "productivity"
50
- ];
51
- var files = [
52
- "dist",
53
- "src-tauri"
54
- ];
55
- var scripts = {
56
- start: "pnpm run dev",
57
- dev: "pnpm run tauri dev",
58
- build: "tauri build",
59
- "build:debug": "tauri build --debug",
60
- "build:mac": "tauri build --target universal-apple-darwin",
61
- "build:config": "chmod +x scripts/configure-tauri.mjs && node scripts/configure-tauri.mjs",
62
- analyze: "cd src-tauri && cargo bloat --release --crates",
63
- tauri: "tauri",
64
- cli: "cross-env NODE_ENV=development rollup -c -w",
65
- "cli:build": "cross-env NODE_ENV=production rollup -c",
66
- test: "pnpm run cli:build && cross-env PAKE_CREATE_APP=1 node tests/index.js",
67
- format: "prettier --write . --ignore-unknown && find tests -name '*.js' -exec sed -i '' 's/[[:space:]]*$//' {} \\; && cd src-tauri && cargo fmt --verbose",
68
- "format:check": "prettier --check . --ignore-unknown",
69
- update: "pnpm update --verbose && cd src-tauri && cargo update",
70
- prepublishOnly: "pnpm run cli:build"
71
- };
72
- var type = "module";
73
- var exports = "./dist/cli.js";
74
- var license = "MIT";
75
- var dependencies = {
76
- "@tauri-apps/api": "^2.9.0",
77
- "@tauri-apps/cli": "^2.9.0",
78
- axios: "^1.12.2",
79
- chalk: "^5.6.2",
80
- commander: "^12.1.0",
81
- execa: "^9.6.0",
82
- "file-type": "^18.7.0",
83
- "fs-extra": "^11.3.2",
84
- "icon-gen": "^5.0.0",
85
- loglevel: "^1.9.2",
86
- ora: "^8.2.0",
87
- prompts: "^2.4.2",
88
- psl: "^1.15.0",
89
- sharp: "^0.33.5",
90
- "tmp-promise": "^3.0.3",
91
- "update-notifier": "^7.3.1"
92
- };
93
- var devDependencies = {
94
- "@rollup/plugin-alias": "^5.1.1",
95
- "@rollup/plugin-commonjs": "^28.0.8",
96
- "@rollup/plugin-json": "^6.1.0",
97
- "@rollup/plugin-replace": "^6.0.2",
98
- "@rollup/plugin-terser": "^0.4.4",
99
- "@types/fs-extra": "^11.0.4",
100
- "@types/node": "^20.19.23",
101
- "@types/page-icon": "^0.3.6",
102
- "@types/prompts": "^2.4.9",
103
- "@types/tmp": "^0.2.6",
104
- "@types/update-notifier": "^6.0.8",
105
- "app-root-path": "^3.1.0",
106
- "cross-env": "^7.0.3",
107
- prettier: "^3.6.2",
108
- rollup: "^4.52.5",
109
- "rollup-plugin-typescript2": "^0.36.0",
110
- tslib: "^2.8.1",
111
- typescript: "^5.9.3"
112
- };
113
- var packageJson = {
114
- name: name,
115
- version: version,
116
- description: description,
117
- engines: engines,
118
- packageManager: packageManager,
119
- bin: bin,
120
- repository: repository,
121
- author: author,
122
- keywords: keywords,
123
- files: files,
124
- scripts: scripts,
125
- type: type,
126
- exports: exports,
127
- license: license,
128
- dependencies: dependencies,
129
- devDependencies: devDependencies
130
- };
23
+ import { InvalidArgumentError, program as program$1, Option } from 'commander';
131
24
 
132
25
  // Convert the current module URL to a file path
133
26
  const currentModulePath = fileURLToPath(import.meta.url);
@@ -379,7 +272,12 @@ async function installRust() {
379
272
  }
380
273
  catch (error) {
381
274
  spinner.fail(chalk.red('โœ• Rust installation failed!'));
382
- console.error(error.message);
275
+ if (error instanceof Error) {
276
+ console.error(error.message);
277
+ }
278
+ else {
279
+ console.error(error);
280
+ }
383
281
  process.exit(1);
384
282
  }
385
283
  }
@@ -421,6 +319,9 @@ function generateSafeFilename(name) {
421
319
  .replace(/\.+$/g, '')
422
320
  .slice(0, 255);
423
321
  }
322
+ function getSafeAppName(name) {
323
+ return generateSafeFilename(name).toLowerCase();
324
+ }
424
325
  function generateLinuxPackageName(name) {
425
326
  return name
426
327
  .toLowerCase()
@@ -448,12 +349,6 @@ function generateIdentifierSafeName(name) {
448
349
  return cleaned;
449
350
  }
450
351
 
451
- /**
452
- * Helper function to generate safe lowercase app name for file paths
453
- */
454
- function getSafeAppName(name) {
455
- return generateSafeFilename(name).toLowerCase();
456
- }
457
352
  async function mergeConfig(url, options, tauriConf) {
458
353
  // Ensure .pake directory exists and copy source templates if needed
459
354
  const srcTauriDir = path.join(npmDirectory, 'src-tauri');
@@ -474,7 +369,7 @@ async function mergeConfig(url, options, tauriConf) {
474
369
  await fsExtra.copy(sourcePath, destPath);
475
370
  }
476
371
  }));
477
- const { width, height, fullscreen, maximize, hideTitleBar, alwaysOnTop, appVersion, darkMode, disabledWebShortcuts, activationShortcut, userAgent, showSystemTray, systemTrayIcon, useLocalFile, identifier, name, resizable = true, inject, proxyUrl, installerLanguage, hideOnClose, incognito, title, wasm, enableDragDrop, multiInstance, startToTray, forceInternalNavigation, zoom, minWidth, minHeight, ignoreCertificateErrors, } = options;
372
+ const { width, height, fullscreen, maximize, hideTitleBar, alwaysOnTop, appVersion, darkMode, disabledWebShortcuts, activationShortcut, userAgent, showSystemTray, systemTrayIcon, useLocalFile, identifier, name = 'pake-app', resizable = true, inject, proxyUrl, installerLanguage, hideOnClose, incognito, title, wasm, enableDragDrop, multiInstance, startToTray, forceInternalNavigation, zoom, minWidth, minHeight, ignoreCertificateErrors, } = options;
478
373
  const { platform } = process;
479
374
  const platformHideOnClose = hideOnClose ?? platform === 'darwin';
480
375
  const tauriConfWindowOptions = {
@@ -555,10 +450,13 @@ async function mergeConfig(url, options, tauriConf) {
555
450
  const identifier = `com.pake.${appNameSafe}`;
556
451
  const desktopFileName = `${identifier}.desktop`;
557
452
  // Create desktop file content
453
+ // Determine if title contains Chinese characters for Name[zh_CN]
454
+ const chineseName = title && /[\u4e00-\u9fa5]/.test(title) ? title : null;
558
455
  const desktopContent = `[Desktop Entry]
559
456
  Version=1.0
560
457
  Type=Application
561
458
  Name=${name}
459
+ ${chineseName ? `Name[zh_CN]=${chineseName}` : ''}
562
460
  Comment=${name}
563
461
  Exec=pake-${appNameSafe}
564
462
  Icon=${appNameSafe}_512
@@ -822,7 +720,9 @@ class BaseBuilder {
822
720
  }
823
721
  catch (error) {
824
722
  // If installation times out and we haven't tried the mirror yet, retry with mirror
825
- if (error.message?.includes('timed out') && !usedMirror) {
723
+ if (error instanceof Error &&
724
+ error.message.includes('timed out') &&
725
+ !usedMirror) {
826
726
  spinner.fail(chalk.yellow('Installation timed out, retrying with CN mirror...'));
827
727
  logger.info('โœบ Retrying installation with CN mirror for better speed...');
828
728
  const retrySpinner = getSpinner('Retrying installation...');
@@ -851,10 +751,18 @@ class BaseBuilder {
851
751
  await this.buildAndCopy(url, this.options.targets);
852
752
  }
853
753
  async start(url) {
754
+ logger.info('Pake dev server starting...');
854
755
  await mergeConfig(url, this.options, tauriConfig);
756
+ const packageManager = await this.detectPackageManager();
757
+ const configPath = path.join(npmDirectory, 'src-tauri', '.pake', 'tauri.conf.json');
758
+ const features = this.getBuildFeatures();
759
+ const featureArgs = features.length > 0 ? `--features ${features.join(',')}` : '';
760
+ const argSeparator = packageManager === 'npm' ? ' --' : '';
761
+ const command = `cd "${npmDirectory}" && ${packageManager} run tauri${argSeparator} dev --config "${configPath}" ${featureArgs}`;
762
+ await shellExec(command);
855
763
  }
856
764
  async buildAndCopy(url, target) {
857
- const { name } = this.options;
765
+ const { name = 'pake-app' } = this.options;
858
766
  await mergeConfig(url, this.options, tauriConfig);
859
767
  // Detect available package manager
860
768
  const packageManager = await this.detectPackageManager();
@@ -1121,7 +1029,7 @@ class MacBuilder extends BaseBuilder {
1121
1029
  this.buildArch = validArchs.includes(options.targets || '')
1122
1030
  ? options.targets
1123
1031
  : 'auto';
1124
- if (process.env.PAKE_CREATE_APP === '1') {
1032
+ if (options.iterativeBuild || process.env.PAKE_CREATE_APP === '1') {
1125
1033
  this.buildFormat = 'app';
1126
1034
  }
1127
1035
  else {
@@ -1130,7 +1038,7 @@ class MacBuilder extends BaseBuilder {
1130
1038
  this.options.targets = this.buildFormat;
1131
1039
  }
1132
1040
  getFileName() {
1133
- const { name } = this.options;
1041
+ const { name = 'pake-app' } = this.options;
1134
1042
  if (this.buildFormat === 'app') {
1135
1043
  return name;
1136
1044
  }
@@ -1249,7 +1157,7 @@ class LinuxBuilder extends BaseBuilder {
1249
1157
  this.options.targets = this.buildFormat;
1250
1158
  }
1251
1159
  getFileName() {
1252
- const { name, targets } = this.options;
1160
+ const { name = 'pake-app', targets } = this.options;
1253
1161
  const version = tauriConfig.version;
1254
1162
  let arch;
1255
1163
  if (this.buildArch === 'arm64') {
@@ -1283,7 +1191,7 @@ class LinuxBuilder extends BaseBuilder {
1283
1191
  getBuildCommand(packageManager = 'pnpm') {
1284
1192
  const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
1285
1193
  const buildTarget = this.buildArch === 'arm64'
1286
- ? this.getTauriTarget(this.buildArch, 'linux')
1194
+ ? (this.getTauriTarget(this.buildArch, 'linux') ?? undefined)
1287
1195
  : undefined;
1288
1196
  let fullCommand = this.buildBaseCommand(packageManager, configPath, buildTarget);
1289
1197
  const features = this.getBuildFeatures();
@@ -1341,40 +1249,112 @@ class BuilderProvider {
1341
1249
  }
1342
1250
  }
1343
1251
 
1344
- const DEFAULT_PAKE_OPTIONS = {
1345
- icon: '',
1346
- height: 780,
1347
- width: 1200,
1348
- fullscreen: false,
1349
- maximize: false,
1350
- hideTitleBar: false,
1351
- alwaysOnTop: false,
1352
- appVersion: '1.0.0',
1353
- darkMode: false,
1354
- disabledWebShortcuts: false,
1355
- activationShortcut: '',
1356
- userAgent: '',
1357
- showSystemTray: false,
1358
- multiArch: false,
1359
- targets: 'deb',
1360
- useLocalFile: false,
1361
- systemTrayIcon: '',
1362
- proxyUrl: '',
1363
- debug: false,
1364
- inject: [],
1365
- installerLanguage: 'en-US',
1366
- hideOnClose: undefined, // Platform-specific: true for macOS, false for others
1367
- incognito: false,
1368
- wasm: false,
1369
- enableDragDrop: false,
1370
- keepBinary: false,
1371
- multiInstance: false,
1372
- startToTray: false,
1373
- forceInternalNavigation: false,
1374
- zoom: 100,
1375
- minWidth: 0,
1376
- minHeight: 0,
1377
- ignoreCertificateErrors: false,
1252
+ var name = "pake-cli";
1253
+ var version = "3.6.2";
1254
+ var description = "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with one command. ๐Ÿคฑ๐Ÿป ไธ€้”ฎๆ‰“ๅŒ…็ฝ‘้กต็”Ÿๆˆ่ฝป้‡ๆกŒ้ขๅบ”็”จใ€‚";
1255
+ var engines = {
1256
+ node: ">=18.0.0"
1257
+ };
1258
+ var packageManager = "pnpm@10.15.0";
1259
+ var bin = {
1260
+ pake: "./dist/cli.js"
1261
+ };
1262
+ var repository = {
1263
+ type: "git",
1264
+ url: "https://github.com/tw93/pake.git"
1265
+ };
1266
+ var author = {
1267
+ name: "Tw93",
1268
+ email: "tw93@qq.com"
1269
+ };
1270
+ var keywords = [
1271
+ "pake",
1272
+ "pake-cli",
1273
+ "rust",
1274
+ "tauri",
1275
+ "no-electron",
1276
+ "productivity"
1277
+ ];
1278
+ var files = [
1279
+ "dist",
1280
+ "src-tauri"
1281
+ ];
1282
+ var scripts = {
1283
+ start: "pnpm run dev",
1284
+ dev: "pnpm run tauri dev",
1285
+ build: "tauri build",
1286
+ "build:debug": "tauri build --debug",
1287
+ "build:mac": "tauri build --target universal-apple-darwin",
1288
+ analyze: "cd src-tauri && cargo bloat --release --crates",
1289
+ tauri: "tauri",
1290
+ cli: "cross-env NODE_ENV=development rollup -c -w",
1291
+ "cli:dev": "cross-env NODE_ENV=development rollup -c -w",
1292
+ "cli:build": "cross-env NODE_ENV=production rollup -c",
1293
+ test: "pnpm run cli:build && cross-env PAKE_CREATE_APP=1 node tests/index.js",
1294
+ format: "prettier --write . --ignore-unknown && find tests -name '*.js' -exec sed -i '' 's/[[:space:]]*$//' {} \\; && cd src-tauri && cargo fmt --verbose",
1295
+ "format:check": "prettier --check . --ignore-unknown",
1296
+ update: "pnpm update --verbose && cd src-tauri && cargo update",
1297
+ prepublishOnly: "pnpm run cli:build"
1298
+ };
1299
+ var type = "module";
1300
+ var exports = "./dist/cli.js";
1301
+ var license = "MIT";
1302
+ var dependencies = {
1303
+ "@tauri-apps/api": "^2.9.0",
1304
+ "@tauri-apps/cli": "^2.9.0",
1305
+ axios: "^1.12.2",
1306
+ chalk: "^5.6.2",
1307
+ commander: "^12.1.0",
1308
+ execa: "^9.6.0",
1309
+ "file-type": "^18.7.0",
1310
+ "fs-extra": "^11.3.2",
1311
+ "icon-gen": "^5.0.0",
1312
+ loglevel: "^1.9.2",
1313
+ ora: "^8.2.0",
1314
+ prompts: "^2.4.2",
1315
+ psl: "^1.15.0",
1316
+ sharp: "^0.33.5",
1317
+ "tmp-promise": "^3.0.3",
1318
+ "update-notifier": "^7.3.1"
1319
+ };
1320
+ var devDependencies = {
1321
+ "@rollup/plugin-alias": "^5.1.1",
1322
+ "@rollup/plugin-commonjs": "^28.0.8",
1323
+ "@rollup/plugin-json": "^6.1.0",
1324
+ "@rollup/plugin-replace": "^6.0.2",
1325
+ "@rollup/plugin-terser": "^0.4.4",
1326
+ "@types/fs-extra": "^11.0.4",
1327
+ "@types/node": "^20.19.23",
1328
+ "@types/page-icon": "^0.3.6",
1329
+ "@types/prompts": "^2.4.9",
1330
+ "@types/tmp": "^0.2.6",
1331
+ "@types/update-notifier": "^6.0.8",
1332
+ "app-root-path": "^3.1.0",
1333
+ "cross-env": "^7.0.3",
1334
+ prettier: "^3.6.2",
1335
+ rollup: "^4.52.5",
1336
+ "rollup-plugin-typescript2": "^0.36.0",
1337
+ tslib: "^2.8.1",
1338
+ typescript: "^5.9.3",
1339
+ vitest: "^4.0.15"
1340
+ };
1341
+ var packageJson = {
1342
+ name: name,
1343
+ version: version,
1344
+ description: description,
1345
+ engines: engines,
1346
+ packageManager: packageManager,
1347
+ bin: bin,
1348
+ repository: repository,
1349
+ author: author,
1350
+ keywords: keywords,
1351
+ files: files,
1352
+ scripts: scripts,
1353
+ type: type,
1354
+ exports: exports,
1355
+ license: license,
1356
+ dependencies: dependencies,
1357
+ devDependencies: devDependencies
1378
1358
  };
1379
1359
 
1380
1360
  async function checkUpdateTips() {
@@ -1444,8 +1424,8 @@ async function preprocessIcon(inputPath) {
1444
1424
  create: {
1445
1425
  width: metadata.width || 512,
1446
1426
  height: metadata.height || 512,
1447
- channels: 3,
1448
- background: ICON_CONFIG.whiteBackground,
1427
+ channels: 4,
1428
+ background: { ...ICON_CONFIG.whiteBackground, alpha: 1 },
1449
1429
  },
1450
1430
  })
1451
1431
  .composite([{ input: inputPath }])
@@ -1454,7 +1434,57 @@ async function preprocessIcon(inputPath) {
1454
1434
  return outputPath;
1455
1435
  }
1456
1436
  catch (error) {
1457
- logger.warn(`Failed to add background to icon: ${error.message}`);
1437
+ if (error instanceof Error) {
1438
+ logger.warn(`Failed to add background to icon: ${error.message}`);
1439
+ }
1440
+ return inputPath;
1441
+ }
1442
+ }
1443
+ /**
1444
+ * Applies macOS squircle mask to icon
1445
+ */
1446
+ async function applyMacOSMask(inputPath) {
1447
+ try {
1448
+ const { path: tempDir } = await dir();
1449
+ const outputPath = path.join(tempDir, 'icon-macos-rounded.png');
1450
+ // 1. Create a 1024x1024 rounded rect mask
1451
+ // rx="224" is closer to the smooth Apple squircle look for 1024px
1452
+ const mask = Buffer.from('<svg width="1024" height="1024"><rect x="0" y="0" width="1024" height="1024" rx="224" ry="224" fill="white"/></svg>');
1453
+ // 2. Load input, resize to 1024, apply mask
1454
+ const maskedBuffer = await sharp(inputPath)
1455
+ .resize(1024, 1024, {
1456
+ fit: 'contain',
1457
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
1458
+ })
1459
+ .composite([
1460
+ {
1461
+ input: mask,
1462
+ blend: 'dest-in',
1463
+ },
1464
+ ])
1465
+ .png()
1466
+ .toBuffer();
1467
+ // 3. Resize to 840x840 (~18% padding) to solve "too big" visual issue
1468
+ // Native MacOS icons often leave some breathing room
1469
+ await sharp(maskedBuffer)
1470
+ .resize(840, 840, {
1471
+ fit: 'contain',
1472
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
1473
+ })
1474
+ .extend({
1475
+ top: 92,
1476
+ bottom: 92,
1477
+ left: 92,
1478
+ right: 92,
1479
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
1480
+ })
1481
+ .toFile(outputPath);
1482
+ return outputPath;
1483
+ }
1484
+ catch (error) {
1485
+ if (error instanceof Error) {
1486
+ logger.warn(`Failed to apply macOS mask: ${error.message}`);
1487
+ }
1458
1488
  return inputPath;
1459
1489
  }
1460
1490
  }
@@ -1490,12 +1520,14 @@ async function convertIconFormat(inputPath, appName) {
1490
1520
  fit: 'contain',
1491
1521
  background: ICON_CONFIG.transparentBackground,
1492
1522
  })
1523
+ .ensureAlpha()
1493
1524
  .png()
1494
1525
  .toFile(outputPath);
1495
1526
  return outputPath;
1496
1527
  }
1497
1528
  // macOS
1498
- await icongen(processedInputPath, platformOutputDir, {
1529
+ const macIconPath = await applyMacOSMask(processedInputPath);
1530
+ await icongen(macIconPath, platformOutputDir, {
1499
1531
  report: false,
1500
1532
  icns: { name: iconName, sizes: PLATFORM_CONFIG.macos.sizes },
1501
1533
  });
@@ -1503,7 +1535,9 @@ async function convertIconFormat(inputPath, appName) {
1503
1535
  return (await fsExtra.pathExists(outputPath)) ? outputPath : null;
1504
1536
  }
1505
1537
  catch (error) {
1506
- logger.warn(`Icon format conversion failed: ${error.message}`);
1538
+ if (error instanceof Error) {
1539
+ logger.warn(`Icon format conversion failed: ${error.message}`);
1540
+ }
1507
1541
  return null;
1508
1542
  }
1509
1543
  }
@@ -1641,14 +1675,18 @@ async function tryGetFavicon(url, appName) {
1641
1675
  }
1642
1676
  }
1643
1677
  catch (error) {
1644
- logger.debug(`Icon service ${serviceUrl} failed: ${error.message}`);
1645
- // Platform-specific error handling
1678
+ if (error instanceof Error) {
1679
+ logger.debug(`Icon service ${serviceUrl} failed: ${error.message}`);
1680
+ }
1681
+ // Network error handling
1646
1682
  if ((IS_LINUX || IS_WIN) && error.code === 'ENOTFOUND') {
1647
- logger.debug(`DNS resolution failed for ${serviceUrl}, trying next service...`);
1683
+ return null;
1648
1684
  }
1649
- // Windows-specific icon conversion errors
1650
- if (IS_WIN && error.message.includes('icongen')) {
1651
- logger.debug(`Windows icon conversion failed for ${serviceUrl}, trying next service...`);
1685
+ // Icon generation error on Windows
1686
+ if (IS_WIN &&
1687
+ error instanceof Error &&
1688
+ error.message.includes('icongen')) {
1689
+ return null;
1652
1690
  }
1653
1691
  continue;
1654
1692
  }
@@ -1657,7 +1695,9 @@ async function tryGetFavicon(url, appName) {
1657
1695
  return null;
1658
1696
  }
1659
1697
  catch (error) {
1660
- logger.warn(`Failed to fetch favicon: ${error.message}`);
1698
+ if (error instanceof Error) {
1699
+ logger.warn(`Failed to fetch favicon: ${error.message}`);
1700
+ }
1661
1701
  return null;
1662
1702
  }
1663
1703
  }
@@ -1682,7 +1722,7 @@ async function downloadIcon(iconUrl, showSpinner = true, customTimeout) {
1682
1722
  }
1683
1723
  catch (error) {
1684
1724
  if (showSpinner && !(error.response?.status === 404)) {
1685
- throw error;
1725
+ logger.error('Icon download failed!', error);
1686
1726
  }
1687
1727
  return null;
1688
1728
  }
@@ -1765,7 +1805,7 @@ async function handleOptions(options, url) {
1765
1805
  if (name && platform === 'linux') {
1766
1806
  name = generateLinuxPackageName(name);
1767
1807
  }
1768
- if (!isValidName(name, platform)) {
1808
+ if (name && !isValidName(name, platform)) {
1769
1809
  const LINUX_NAME_ERROR = `โœ• Name should only include lowercase letters, numbers, and dashes (not leading dashes). Examples: com-123-xxx, 123pan, pan123, weread, we-read, 123.`;
1770
1810
  const DEFAULT_NAME_ERROR = `โœ• Name should only include letters, numbers, dashes, and spaces (not leading dashes and spaces). Examples: 123pan, 123Pan, Pan123, weread, WeRead, WERead, we-read, We Read, 123.`;
1771
1811
  const errorMsg = platform === 'linux' ? LINUX_NAME_ERROR : DEFAULT_NAME_ERROR;
@@ -1784,10 +1824,58 @@ async function handleOptions(options, url) {
1784
1824
  identifier: getIdentifier(url),
1785
1825
  };
1786
1826
  const iconPath = await handleIcon(appOptions, url);
1787
- appOptions.icon = iconPath || undefined;
1827
+ appOptions.icon = iconPath || '';
1788
1828
  return appOptions;
1789
1829
  }
1790
1830
 
1831
+ const DEFAULT_PAKE_OPTIONS = {
1832
+ icon: '',
1833
+ height: 780,
1834
+ width: 1200,
1835
+ fullscreen: false,
1836
+ maximize: false,
1837
+ hideTitleBar: false,
1838
+ alwaysOnTop: false,
1839
+ appVersion: '1.0.0',
1840
+ darkMode: false,
1841
+ disabledWebShortcuts: false,
1842
+ activationShortcut: '',
1843
+ userAgent: '',
1844
+ showSystemTray: false,
1845
+ multiArch: false,
1846
+ targets: (() => {
1847
+ switch (process.platform) {
1848
+ case 'linux':
1849
+ return 'deb';
1850
+ case 'darwin':
1851
+ return 'dmg';
1852
+ case 'win32':
1853
+ return 'msi';
1854
+ default:
1855
+ return 'deb';
1856
+ }
1857
+ })(),
1858
+ useLocalFile: false,
1859
+ systemTrayIcon: '',
1860
+ proxyUrl: '',
1861
+ debug: false,
1862
+ inject: [],
1863
+ installerLanguage: 'en-US',
1864
+ hideOnClose: undefined, // Platform-specific: true for macOS, false for others
1865
+ incognito: false,
1866
+ wasm: false,
1867
+ enableDragDrop: false,
1868
+ keepBinary: false,
1869
+ multiInstance: false,
1870
+ startToTray: false,
1871
+ forceInternalNavigation: false,
1872
+ iterativeBuild: false,
1873
+ zoom: 100,
1874
+ minWidth: 0,
1875
+ minHeight: 0,
1876
+ ignoreCertificateErrors: false,
1877
+ };
1878
+
1791
1879
  function validateNumberInput(value) {
1792
1880
  const parsedValue = Number(value);
1793
1881
  if (isNaN(parsedValue)) {
@@ -1802,152 +1890,158 @@ function validateUrlInput(url) {
1802
1890
  return normalizeUrl(url);
1803
1891
  }
1804
1892
  catch (error) {
1805
- throw new InvalidArgumentError(error.message);
1893
+ if (error instanceof Error) {
1894
+ throw new InvalidArgumentError(error.message);
1895
+ }
1896
+ throw error;
1806
1897
  }
1807
1898
  }
1808
1899
  return url;
1809
1900
  }
1810
1901
 
1811
- const { green, yellow } = chalk;
1812
- const logo = `${chalk.green(' ____ _')}
1902
+ function getCliProgram() {
1903
+ const { green, yellow } = chalk;
1904
+ const logo = `${chalk.green(' ____ _')}
1813
1905
  ${green('| _ \\ __ _| | _____')}
1814
1906
  ${green('| |_) / _` | |/ / _ \\')}
1815
1907
  ${green('| __/ (_| | < __/')} ${yellow('https://github.com/tw93/pake')}
1816
1908
  ${green('|_| \\__,_|_|\\_\\___| can turn any webpage into a desktop app with Rust.')}
1817
1909
  `;
1818
- program
1819
- .addHelpText('beforeAll', logo)
1820
- .usage(`[url] [options]`)
1821
- .showHelpAfterError();
1822
- program
1823
- .argument('[url]', 'The web URL you want to package', validateUrlInput)
1824
- // Refer to https://github.com/tj/commander.js#custom-option-processing, turn string array into a string connected with custom connectors.
1825
- // If the platform is Linux, use `-` as the connector, and convert all characters to lowercase.
1826
- // For example, Google Translate will become google-translate.
1827
- .option('--name <string>', 'Application name')
1828
- .option('--icon <string>', 'Application icon', DEFAULT_PAKE_OPTIONS.icon)
1829
- .option('--width <number>', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width)
1830
- .option('--height <number>', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height)
1831
- .option('--use-local-file', 'Use local file packaging', DEFAULT_PAKE_OPTIONS.useLocalFile)
1832
- .option('--fullscreen', 'Start in full screen', DEFAULT_PAKE_OPTIONS.fullscreen)
1833
- .option('--hide-title-bar', 'For Mac, hide title bar', DEFAULT_PAKE_OPTIONS.hideTitleBar)
1834
- .option('--multi-arch', 'For Mac, both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch)
1835
- .option('--inject <files>', 'Inject local CSS/JS files into the page', (val, previous) => {
1836
- if (!val)
1837
- return DEFAULT_PAKE_OPTIONS.inject;
1838
- // Split by comma and trim whitespace, filter out empty strings
1839
- const files = val
1840
- .split(',')
1841
- .map((item) => item.trim())
1842
- .filter((item) => item.length > 0);
1843
- // If previous values exist (from multiple --inject options), merge them
1844
- return previous ? [...previous, ...files] : files;
1845
- }, DEFAULT_PAKE_OPTIONS.inject)
1846
- .option('--debug', 'Debug build and more output', DEFAULT_PAKE_OPTIONS.debug)
1847
- .addOption(new Option('--proxy-url <url>', 'Proxy URL for all network requests (http://, https://, socks5://)')
1848
- .default(DEFAULT_PAKE_OPTIONS.proxyUrl)
1849
- .hideHelp())
1850
- .addOption(new Option('--user-agent <string>', 'Custom user agent')
1851
- .default(DEFAULT_PAKE_OPTIONS.userAgent)
1852
- .hideHelp())
1853
- .addOption(new Option('--targets <string>', 'Build target format for your system').default(DEFAULT_PAKE_OPTIONS.targets))
1854
- .addOption(new Option('--app-version <string>', 'App version, the same as package.json version')
1855
- .default(DEFAULT_PAKE_OPTIONS.appVersion)
1856
- .hideHelp())
1857
- .addOption(new Option('--always-on-top', 'Always on the top level')
1858
- .default(DEFAULT_PAKE_OPTIONS.alwaysOnTop)
1859
- .hideHelp())
1860
- .addOption(new Option('--maximize', 'Start window maximized')
1861
- .default(DEFAULT_PAKE_OPTIONS.maximize)
1862
- .hideHelp())
1863
- .addOption(new Option('--dark-mode', 'Force Mac app to use dark mode')
1864
- .default(DEFAULT_PAKE_OPTIONS.darkMode)
1865
- .hideHelp())
1866
- .addOption(new Option('--disabled-web-shortcuts', 'Disabled webPage shortcuts')
1867
- .default(DEFAULT_PAKE_OPTIONS.disabledWebShortcuts)
1868
- .hideHelp())
1869
- .addOption(new Option('--activation-shortcut <string>', 'Shortcut key to active App')
1870
- .default(DEFAULT_PAKE_OPTIONS.activationShortcut)
1871
- .hideHelp())
1872
- .addOption(new Option('--show-system-tray', 'Show system tray in app')
1873
- .default(DEFAULT_PAKE_OPTIONS.showSystemTray)
1874
- .hideHelp())
1875
- .addOption(new Option('--system-tray-icon <string>', 'Custom system tray icon')
1876
- .default(DEFAULT_PAKE_OPTIONS.systemTrayIcon)
1877
- .hideHelp())
1878
- .addOption(new Option('--hide-on-close [boolean]', 'Hide window on close instead of exiting (default: true for macOS, false for others)')
1879
- .default(DEFAULT_PAKE_OPTIONS.hideOnClose)
1880
- .argParser((value) => {
1881
- if (value === undefined)
1882
- return true; // --hide-on-close without value
1883
- if (value === 'true')
1884
- return true;
1885
- if (value === 'false')
1886
- return false;
1887
- throw new Error('--hide-on-close must be true or false');
1888
- })
1889
- .hideHelp())
1890
- .addOption(new Option('--title <string>', 'Window title').hideHelp())
1891
- .addOption(new Option('--incognito', 'Launch app in incognito/private mode')
1892
- .default(DEFAULT_PAKE_OPTIONS.incognito)
1893
- .hideHelp())
1894
- .addOption(new Option('--wasm', 'Enable WebAssembly support (Flutter Web, etc.)')
1895
- .default(DEFAULT_PAKE_OPTIONS.wasm)
1896
- .hideHelp())
1897
- .addOption(new Option('--enable-drag-drop', 'Enable drag and drop functionality')
1898
- .default(DEFAULT_PAKE_OPTIONS.enableDragDrop)
1899
- .hideHelp())
1900
- .addOption(new Option('--keep-binary', 'Keep raw binary file alongside installer')
1901
- .default(DEFAULT_PAKE_OPTIONS.keepBinary)
1902
- .hideHelp())
1903
- .addOption(new Option('--multi-instance', 'Allow multiple app instances')
1904
- .default(DEFAULT_PAKE_OPTIONS.multiInstance)
1905
- .hideHelp())
1906
- .addOption(new Option('--start-to-tray', 'Start app minimized to tray')
1907
- .default(DEFAULT_PAKE_OPTIONS.startToTray)
1908
- .hideHelp())
1909
- .addOption(new Option('--force-internal-navigation', 'Keep every link inside the Pake window instead of opening external handlers')
1910
- .default(DEFAULT_PAKE_OPTIONS.forceInternalNavigation)
1911
- .hideHelp())
1912
- .addOption(new Option('--installer-language <string>', 'Installer language')
1913
- .default(DEFAULT_PAKE_OPTIONS.installerLanguage)
1914
- .hideHelp())
1915
- .addOption(new Option('--zoom <number>', 'Initial page zoom level (50-200)')
1916
- .default(DEFAULT_PAKE_OPTIONS.zoom)
1917
- .argParser((value) => {
1918
- const zoom = parseInt(value);
1919
- if (isNaN(zoom) || zoom < 50 || zoom > 200) {
1920
- throw new Error('--zoom must be a number between 50 and 200');
1921
- }
1922
- return zoom;
1923
- })
1924
- .hideHelp())
1925
- .addOption(new Option('--min-width <number>', 'Minimum window width')
1926
- .default(DEFAULT_PAKE_OPTIONS.minWidth)
1927
- .argParser(validateNumberInput)
1928
- .hideHelp())
1929
- .addOption(new Option('--min-height <number>', 'Minimum window height')
1930
- .default(DEFAULT_PAKE_OPTIONS.minHeight)
1931
- .argParser(validateNumberInput)
1932
- .hideHelp())
1933
- .addOption(new Option('--ignore-certificate-errors', 'Ignore certificate errors (for self-signed certificates)')
1934
- .default(DEFAULT_PAKE_OPTIONS.ignoreCertificateErrors)
1935
- .hideHelp())
1936
- .version(packageJson.version, '-v, --version')
1937
- .configureHelp({
1938
- sortSubcommands: true,
1939
- optionTerm: (option) => {
1940
- if (option.flags === '-v, --version' || option.flags === '-h, --help')
1941
- return '';
1942
- return option.flags;
1943
- },
1944
- optionDescription: (option) => {
1945
- if (option.flags === '-v, --version' || option.flags === '-h, --help')
1946
- return '';
1947
- return option.description;
1948
- },
1949
- })
1950
- .action(async (url, options) => {
1910
+ return program$1
1911
+ .addHelpText('beforeAll', logo)
1912
+ .usage(`[url] [options]`)
1913
+ .showHelpAfterError()
1914
+ .argument('[url]', 'The web URL you want to package', validateUrlInput)
1915
+ .option('--name <string>', 'Application name')
1916
+ .option('--icon <string>', 'Application icon', DEFAULT_PAKE_OPTIONS.icon)
1917
+ .option('--width <number>', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width)
1918
+ .option('--height <number>', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height)
1919
+ .option('--use-local-file', 'Use local file packaging', DEFAULT_PAKE_OPTIONS.useLocalFile)
1920
+ .option('--fullscreen', 'Start in full screen', DEFAULT_PAKE_OPTIONS.fullscreen)
1921
+ .option('--hide-title-bar', 'For Mac, hide title bar', DEFAULT_PAKE_OPTIONS.hideTitleBar)
1922
+ .option('--multi-arch', 'For Mac, both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch)
1923
+ .option('--inject <files>', 'Inject local CSS/JS files into the page', (val, previous) => {
1924
+ if (!val)
1925
+ return DEFAULT_PAKE_OPTIONS.inject;
1926
+ // Split by comma and trim whitespace, filter out empty strings
1927
+ const files = val
1928
+ .split(',')
1929
+ .map((item) => item.trim())
1930
+ .filter((item) => item.length > 0);
1931
+ // If previous values exist (from multiple --inject options), merge them
1932
+ return previous ? [...previous, ...files] : files;
1933
+ }, DEFAULT_PAKE_OPTIONS.inject)
1934
+ .option('--debug', 'Debug build and more output', DEFAULT_PAKE_OPTIONS.debug)
1935
+ .addOption(new Option('--proxy-url <url>', 'Proxy URL for all network requests (http://, https://, socks5://)')
1936
+ .default(DEFAULT_PAKE_OPTIONS.proxyUrl)
1937
+ .hideHelp())
1938
+ .addOption(new Option('--user-agent <string>', 'Custom user agent')
1939
+ .default(DEFAULT_PAKE_OPTIONS.userAgent)
1940
+ .hideHelp())
1941
+ .addOption(new Option('--targets <string>', 'Build target format for your system').default(DEFAULT_PAKE_OPTIONS.targets))
1942
+ .addOption(new Option('--app-version <string>', 'App version, the same as package.json version')
1943
+ .default(DEFAULT_PAKE_OPTIONS.appVersion)
1944
+ .hideHelp())
1945
+ .addOption(new Option('--always-on-top', 'Always on the top level')
1946
+ .default(DEFAULT_PAKE_OPTIONS.alwaysOnTop)
1947
+ .hideHelp())
1948
+ .addOption(new Option('--maximize', 'Start window maximized')
1949
+ .default(DEFAULT_PAKE_OPTIONS.maximize)
1950
+ .hideHelp())
1951
+ .addOption(new Option('--dark-mode', 'Force Mac app to use dark mode')
1952
+ .default(DEFAULT_PAKE_OPTIONS.darkMode)
1953
+ .hideHelp())
1954
+ .addOption(new Option('--disabled-web-shortcuts', 'Disabled webPage shortcuts')
1955
+ .default(DEFAULT_PAKE_OPTIONS.disabledWebShortcuts)
1956
+ .hideHelp())
1957
+ .addOption(new Option('--activation-shortcut <string>', 'Shortcut key to active App')
1958
+ .default(DEFAULT_PAKE_OPTIONS.activationShortcut)
1959
+ .hideHelp())
1960
+ .addOption(new Option('--show-system-tray', 'Show system tray in app')
1961
+ .default(DEFAULT_PAKE_OPTIONS.showSystemTray)
1962
+ .hideHelp())
1963
+ .addOption(new Option('--system-tray-icon <string>', 'Custom system tray icon')
1964
+ .default(DEFAULT_PAKE_OPTIONS.systemTrayIcon)
1965
+ .hideHelp())
1966
+ .addOption(new Option('--hide-on-close [boolean]', 'Hide window on close instead of exiting (default: true for macOS, false for others)')
1967
+ .default(DEFAULT_PAKE_OPTIONS.hideOnClose)
1968
+ .argParser((value) => {
1969
+ if (value === undefined)
1970
+ return true; // --hide-on-close without value
1971
+ if (value === 'true')
1972
+ return true;
1973
+ if (value === 'false')
1974
+ return false;
1975
+ throw new Error('--hide-on-close must be true or false');
1976
+ })
1977
+ .hideHelp())
1978
+ .addOption(new Option('--title <string>', 'Window title').hideHelp())
1979
+ .addOption(new Option('--incognito', 'Launch app in incognito/private mode')
1980
+ .default(DEFAULT_PAKE_OPTIONS.incognito)
1981
+ .hideHelp())
1982
+ .addOption(new Option('--wasm', 'Enable WebAssembly support (Flutter Web, etc.)')
1983
+ .default(DEFAULT_PAKE_OPTIONS.wasm)
1984
+ .hideHelp())
1985
+ .addOption(new Option('--enable-drag-drop', 'Enable drag and drop functionality')
1986
+ .default(DEFAULT_PAKE_OPTIONS.enableDragDrop)
1987
+ .hideHelp())
1988
+ .addOption(new Option('--keep-binary', 'Keep raw binary file alongside installer')
1989
+ .default(DEFAULT_PAKE_OPTIONS.keepBinary)
1990
+ .hideHelp())
1991
+ .addOption(new Option('--multi-instance', 'Allow multiple app instances')
1992
+ .default(DEFAULT_PAKE_OPTIONS.multiInstance)
1993
+ .hideHelp())
1994
+ .addOption(new Option('--start-to-tray', 'Start app minimized to tray')
1995
+ .default(DEFAULT_PAKE_OPTIONS.startToTray)
1996
+ .hideHelp())
1997
+ .addOption(new Option('--force-internal-navigation', 'Keep every link inside the Pake window instead of opening external handlers')
1998
+ .default(DEFAULT_PAKE_OPTIONS.forceInternalNavigation)
1999
+ .hideHelp())
2000
+ .addOption(new Option('--installer-language <string>', 'Installer language')
2001
+ .default(DEFAULT_PAKE_OPTIONS.installerLanguage)
2002
+ .hideHelp())
2003
+ .addOption(new Option('--zoom <number>', 'Initial page zoom level (50-200)')
2004
+ .default(DEFAULT_PAKE_OPTIONS.zoom)
2005
+ .argParser((value) => {
2006
+ const zoom = parseInt(value);
2007
+ if (isNaN(zoom) || zoom < 50 || zoom > 200) {
2008
+ throw new Error('--zoom must be a number between 50 and 200');
2009
+ }
2010
+ return zoom;
2011
+ })
2012
+ .hideHelp())
2013
+ .addOption(new Option('--min-width <number>', 'Minimum window width')
2014
+ .default(DEFAULT_PAKE_OPTIONS.minWidth)
2015
+ .argParser(validateNumberInput)
2016
+ .hideHelp())
2017
+ .addOption(new Option('--min-height <number>', 'Minimum window height')
2018
+ .default(DEFAULT_PAKE_OPTIONS.minHeight)
2019
+ .argParser(validateNumberInput)
2020
+ .hideHelp())
2021
+ .addOption(new Option('--ignore-certificate-errors', 'Ignore certificate errors (for self-signed certificates)')
2022
+ .default(DEFAULT_PAKE_OPTIONS.ignoreCertificateErrors)
2023
+ .hideHelp())
2024
+ .addOption(new Option('--iterative-build', 'Turn on rapid build mode (app only, no dmg/deb/msi), good for debugging')
2025
+ .default(DEFAULT_PAKE_OPTIONS.iterativeBuild)
2026
+ .hideHelp())
2027
+ .version(packageJson.version, '-v, --version')
2028
+ .configureHelp({
2029
+ sortSubcommands: true,
2030
+ optionTerm: (option) => {
2031
+ if (option.flags === '-v, --version' || option.flags === '-h, --help')
2032
+ return '';
2033
+ return option.flags;
2034
+ },
2035
+ optionDescription: (option) => {
2036
+ if (option.flags === '-v, --version' || option.flags === '-h, --help')
2037
+ return '';
2038
+ return option.description;
2039
+ },
2040
+ });
2041
+ }
2042
+
2043
+ const program = getCliProgram();
2044
+ program.action(async (url, options) => {
1951
2045
  await checkUpdateTips();
1952
2046
  if (!url) {
1953
2047
  program.help({