pake-cli 3.6.1 โ†’ 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.1";
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() {
@@ -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
  }
@@ -1496,7 +1526,8 @@ async function convertIconFormat(inputPath, appName) {
1496
1526
  return outputPath;
1497
1527
  }
1498
1528
  // macOS
1499
- await icongen(processedInputPath, platformOutputDir, {
1529
+ const macIconPath = await applyMacOSMask(processedInputPath);
1530
+ await icongen(macIconPath, platformOutputDir, {
1500
1531
  report: false,
1501
1532
  icns: { name: iconName, sizes: PLATFORM_CONFIG.macos.sizes },
1502
1533
  });
@@ -1504,7 +1535,9 @@ async function convertIconFormat(inputPath, appName) {
1504
1535
  return (await fsExtra.pathExists(outputPath)) ? outputPath : null;
1505
1536
  }
1506
1537
  catch (error) {
1507
- logger.warn(`Icon format conversion failed: ${error.message}`);
1538
+ if (error instanceof Error) {
1539
+ logger.warn(`Icon format conversion failed: ${error.message}`);
1540
+ }
1508
1541
  return null;
1509
1542
  }
1510
1543
  }
@@ -1642,14 +1675,18 @@ async function tryGetFavicon(url, appName) {
1642
1675
  }
1643
1676
  }
1644
1677
  catch (error) {
1645
- logger.debug(`Icon service ${serviceUrl} failed: ${error.message}`);
1646
- // Platform-specific error handling
1678
+ if (error instanceof Error) {
1679
+ logger.debug(`Icon service ${serviceUrl} failed: ${error.message}`);
1680
+ }
1681
+ // Network error handling
1647
1682
  if ((IS_LINUX || IS_WIN) && error.code === 'ENOTFOUND') {
1648
- logger.debug(`DNS resolution failed for ${serviceUrl}, trying next service...`);
1683
+ return null;
1649
1684
  }
1650
- // Windows-specific icon conversion errors
1651
- if (IS_WIN && error.message.includes('icongen')) {
1652
- 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;
1653
1690
  }
1654
1691
  continue;
1655
1692
  }
@@ -1658,7 +1695,9 @@ async function tryGetFavicon(url, appName) {
1658
1695
  return null;
1659
1696
  }
1660
1697
  catch (error) {
1661
- logger.warn(`Failed to fetch favicon: ${error.message}`);
1698
+ if (error instanceof Error) {
1699
+ logger.warn(`Failed to fetch favicon: ${error.message}`);
1700
+ }
1662
1701
  return null;
1663
1702
  }
1664
1703
  }
@@ -1683,7 +1722,7 @@ async function downloadIcon(iconUrl, showSpinner = true, customTimeout) {
1683
1722
  }
1684
1723
  catch (error) {
1685
1724
  if (showSpinner && !(error.response?.status === 404)) {
1686
- throw error;
1725
+ logger.error('Icon download failed!', error);
1687
1726
  }
1688
1727
  return null;
1689
1728
  }
@@ -1766,7 +1805,7 @@ async function handleOptions(options, url) {
1766
1805
  if (name && platform === 'linux') {
1767
1806
  name = generateLinuxPackageName(name);
1768
1807
  }
1769
- if (!isValidName(name, platform)) {
1808
+ if (name && !isValidName(name, platform)) {
1770
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.`;
1771
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.`;
1772
1811
  const errorMsg = platform === 'linux' ? LINUX_NAME_ERROR : DEFAULT_NAME_ERROR;
@@ -1785,10 +1824,58 @@ async function handleOptions(options, url) {
1785
1824
  identifier: getIdentifier(url),
1786
1825
  };
1787
1826
  const iconPath = await handleIcon(appOptions, url);
1788
- appOptions.icon = iconPath || undefined;
1827
+ appOptions.icon = iconPath || '';
1789
1828
  return appOptions;
1790
1829
  }
1791
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
+
1792
1879
  function validateNumberInput(value) {
1793
1880
  const parsedValue = Number(value);
1794
1881
  if (isNaN(parsedValue)) {
@@ -1803,152 +1890,158 @@ function validateUrlInput(url) {
1803
1890
  return normalizeUrl(url);
1804
1891
  }
1805
1892
  catch (error) {
1806
- throw new InvalidArgumentError(error.message);
1893
+ if (error instanceof Error) {
1894
+ throw new InvalidArgumentError(error.message);
1895
+ }
1896
+ throw error;
1807
1897
  }
1808
1898
  }
1809
1899
  return url;
1810
1900
  }
1811
1901
 
1812
- const { green, yellow } = chalk;
1813
- const logo = `${chalk.green(' ____ _')}
1902
+ function getCliProgram() {
1903
+ const { green, yellow } = chalk;
1904
+ const logo = `${chalk.green(' ____ _')}
1814
1905
  ${green('| _ \\ __ _| | _____')}
1815
1906
  ${green('| |_) / _` | |/ / _ \\')}
1816
1907
  ${green('| __/ (_| | < __/')} ${yellow('https://github.com/tw93/pake')}
1817
1908
  ${green('|_| \\__,_|_|\\_\\___| can turn any webpage into a desktop app with Rust.')}
1818
1909
  `;
1819
- program
1820
- .addHelpText('beforeAll', logo)
1821
- .usage(`[url] [options]`)
1822
- .showHelpAfterError();
1823
- program
1824
- .argument('[url]', 'The web URL you want to package', validateUrlInput)
1825
- // Refer to https://github.com/tj/commander.js#custom-option-processing, turn string array into a string connected with custom connectors.
1826
- // If the platform is Linux, use `-` as the connector, and convert all characters to lowercase.
1827
- // For example, Google Translate will become google-translate.
1828
- .option('--name <string>', 'Application name')
1829
- .option('--icon <string>', 'Application icon', DEFAULT_PAKE_OPTIONS.icon)
1830
- .option('--width <number>', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width)
1831
- .option('--height <number>', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height)
1832
- .option('--use-local-file', 'Use local file packaging', DEFAULT_PAKE_OPTIONS.useLocalFile)
1833
- .option('--fullscreen', 'Start in full screen', DEFAULT_PAKE_OPTIONS.fullscreen)
1834
- .option('--hide-title-bar', 'For Mac, hide title bar', DEFAULT_PAKE_OPTIONS.hideTitleBar)
1835
- .option('--multi-arch', 'For Mac, both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch)
1836
- .option('--inject <files>', 'Inject local CSS/JS files into the page', (val, previous) => {
1837
- if (!val)
1838
- return DEFAULT_PAKE_OPTIONS.inject;
1839
- // Split by comma and trim whitespace, filter out empty strings
1840
- const files = val
1841
- .split(',')
1842
- .map((item) => item.trim())
1843
- .filter((item) => item.length > 0);
1844
- // If previous values exist (from multiple --inject options), merge them
1845
- return previous ? [...previous, ...files] : files;
1846
- }, DEFAULT_PAKE_OPTIONS.inject)
1847
- .option('--debug', 'Debug build and more output', DEFAULT_PAKE_OPTIONS.debug)
1848
- .addOption(new Option('--proxy-url <url>', 'Proxy URL for all network requests (http://, https://, socks5://)')
1849
- .default(DEFAULT_PAKE_OPTIONS.proxyUrl)
1850
- .hideHelp())
1851
- .addOption(new Option('--user-agent <string>', 'Custom user agent')
1852
- .default(DEFAULT_PAKE_OPTIONS.userAgent)
1853
- .hideHelp())
1854
- .addOption(new Option('--targets <string>', 'Build target format for your system').default(DEFAULT_PAKE_OPTIONS.targets))
1855
- .addOption(new Option('--app-version <string>', 'App version, the same as package.json version')
1856
- .default(DEFAULT_PAKE_OPTIONS.appVersion)
1857
- .hideHelp())
1858
- .addOption(new Option('--always-on-top', 'Always on the top level')
1859
- .default(DEFAULT_PAKE_OPTIONS.alwaysOnTop)
1860
- .hideHelp())
1861
- .addOption(new Option('--maximize', 'Start window maximized')
1862
- .default(DEFAULT_PAKE_OPTIONS.maximize)
1863
- .hideHelp())
1864
- .addOption(new Option('--dark-mode', 'Force Mac app to use dark mode')
1865
- .default(DEFAULT_PAKE_OPTIONS.darkMode)
1866
- .hideHelp())
1867
- .addOption(new Option('--disabled-web-shortcuts', 'Disabled webPage shortcuts')
1868
- .default(DEFAULT_PAKE_OPTIONS.disabledWebShortcuts)
1869
- .hideHelp())
1870
- .addOption(new Option('--activation-shortcut <string>', 'Shortcut key to active App')
1871
- .default(DEFAULT_PAKE_OPTIONS.activationShortcut)
1872
- .hideHelp())
1873
- .addOption(new Option('--show-system-tray', 'Show system tray in app')
1874
- .default(DEFAULT_PAKE_OPTIONS.showSystemTray)
1875
- .hideHelp())
1876
- .addOption(new Option('--system-tray-icon <string>', 'Custom system tray icon')
1877
- .default(DEFAULT_PAKE_OPTIONS.systemTrayIcon)
1878
- .hideHelp())
1879
- .addOption(new Option('--hide-on-close [boolean]', 'Hide window on close instead of exiting (default: true for macOS, false for others)')
1880
- .default(DEFAULT_PAKE_OPTIONS.hideOnClose)
1881
- .argParser((value) => {
1882
- if (value === undefined)
1883
- return true; // --hide-on-close without value
1884
- if (value === 'true')
1885
- return true;
1886
- if (value === 'false')
1887
- return false;
1888
- throw new Error('--hide-on-close must be true or false');
1889
- })
1890
- .hideHelp())
1891
- .addOption(new Option('--title <string>', 'Window title').hideHelp())
1892
- .addOption(new Option('--incognito', 'Launch app in incognito/private mode')
1893
- .default(DEFAULT_PAKE_OPTIONS.incognito)
1894
- .hideHelp())
1895
- .addOption(new Option('--wasm', 'Enable WebAssembly support (Flutter Web, etc.)')
1896
- .default(DEFAULT_PAKE_OPTIONS.wasm)
1897
- .hideHelp())
1898
- .addOption(new Option('--enable-drag-drop', 'Enable drag and drop functionality')
1899
- .default(DEFAULT_PAKE_OPTIONS.enableDragDrop)
1900
- .hideHelp())
1901
- .addOption(new Option('--keep-binary', 'Keep raw binary file alongside installer')
1902
- .default(DEFAULT_PAKE_OPTIONS.keepBinary)
1903
- .hideHelp())
1904
- .addOption(new Option('--multi-instance', 'Allow multiple app instances')
1905
- .default(DEFAULT_PAKE_OPTIONS.multiInstance)
1906
- .hideHelp())
1907
- .addOption(new Option('--start-to-tray', 'Start app minimized to tray')
1908
- .default(DEFAULT_PAKE_OPTIONS.startToTray)
1909
- .hideHelp())
1910
- .addOption(new Option('--force-internal-navigation', 'Keep every link inside the Pake window instead of opening external handlers')
1911
- .default(DEFAULT_PAKE_OPTIONS.forceInternalNavigation)
1912
- .hideHelp())
1913
- .addOption(new Option('--installer-language <string>', 'Installer language')
1914
- .default(DEFAULT_PAKE_OPTIONS.installerLanguage)
1915
- .hideHelp())
1916
- .addOption(new Option('--zoom <number>', 'Initial page zoom level (50-200)')
1917
- .default(DEFAULT_PAKE_OPTIONS.zoom)
1918
- .argParser((value) => {
1919
- const zoom = parseInt(value);
1920
- if (isNaN(zoom) || zoom < 50 || zoom > 200) {
1921
- throw new Error('--zoom must be a number between 50 and 200');
1922
- }
1923
- return zoom;
1924
- })
1925
- .hideHelp())
1926
- .addOption(new Option('--min-width <number>', 'Minimum window width')
1927
- .default(DEFAULT_PAKE_OPTIONS.minWidth)
1928
- .argParser(validateNumberInput)
1929
- .hideHelp())
1930
- .addOption(new Option('--min-height <number>', 'Minimum window height')
1931
- .default(DEFAULT_PAKE_OPTIONS.minHeight)
1932
- .argParser(validateNumberInput)
1933
- .hideHelp())
1934
- .addOption(new Option('--ignore-certificate-errors', 'Ignore certificate errors (for self-signed certificates)')
1935
- .default(DEFAULT_PAKE_OPTIONS.ignoreCertificateErrors)
1936
- .hideHelp())
1937
- .version(packageJson.version, '-v, --version')
1938
- .configureHelp({
1939
- sortSubcommands: true,
1940
- optionTerm: (option) => {
1941
- if (option.flags === '-v, --version' || option.flags === '-h, --help')
1942
- return '';
1943
- return option.flags;
1944
- },
1945
- optionDescription: (option) => {
1946
- if (option.flags === '-v, --version' || option.flags === '-h, --help')
1947
- return '';
1948
- return option.description;
1949
- },
1950
- })
1951
- .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) => {
1952
2045
  await checkUpdateTips();
1953
2046
  if (!url) {
1954
2047
  program.help({