pake-cli 3.6.1 โ 3.6.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/dist/cli.js +327 -222
- package/dist/dev.js +1100 -215
- package/dist/dev.js.map +1 -1
- package/package.json +3 -4
- package/src-tauri/.cargo/config.toml +10 -0
- package/src-tauri/.pake/pake.json +42 -0
- package/src-tauri/.pake/tauri.conf.json +24 -0
- package/src-tauri/.pake/tauri.linux.conf.json +13 -0
- package/src-tauri/.pake/tauri.macos.conf.json +11 -0
- package/src-tauri/.pake/tauri.windows.conf.json +15 -0
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +7 -2
- package/src-tauri/icons/icon.icns +0 -0
- package/src-tauri/icons/icon.png +0 -0
- package/src-tauri/src/app/invoke.rs +57 -27
- package/src-tauri/src/app/menu.rs +244 -0
- package/src-tauri/src/app/mod.rs +1 -0
- package/src-tauri/src/lib.rs +12 -236
- package/src-tauri/assets/com-tw93-weekly.desktop +0 -10
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { InvalidArgumentError, program, Option } from 'commander';
|
|
4
2
|
import log from 'loglevel';
|
|
3
|
+
import updateNotifier from 'update-notifier';
|
|
5
4
|
import path from 'path';
|
|
6
5
|
import fsExtra from 'fs-extra';
|
|
7
6
|
import { fileURLToPath } from 'url';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
8
|
import prompts from 'prompts';
|
|
9
9
|
import os from 'os';
|
|
10
10
|
import { execa, execaSync } from 'execa';
|
|
@@ -14,16 +14,15 @@ import dns from 'dns';
|
|
|
14
14
|
import http from 'http';
|
|
15
15
|
import { promisify } from 'util';
|
|
16
16
|
import fs from 'fs';
|
|
17
|
-
import updateNotifier from 'update-notifier';
|
|
18
|
-
import axios from 'axios';
|
|
19
17
|
import { dir } from 'tmp-promise';
|
|
20
18
|
import { fileTypeFromBuffer } from 'file-type';
|
|
21
19
|
import icongen from 'icon-gen';
|
|
22
20
|
import sharp from 'sharp';
|
|
23
21
|
import * as psl from 'psl';
|
|
22
|
+
import { InvalidArgumentError, program as program$1, Option } from 'commander';
|
|
24
23
|
|
|
25
24
|
var name = "pake-cli";
|
|
26
|
-
var version = "3.6.
|
|
25
|
+
var version = "3.6.3";
|
|
27
26
|
var description = "๐คฑ๐ป Turn any webpage into a desktop app with one command. ๐คฑ๐ป ไธ้ฎๆๅ
็ฝ้กต็ๆ่ฝป้ๆก้ขๅบ็จใ";
|
|
28
27
|
var engines = {
|
|
29
28
|
node: ">=18.0.0"
|
|
@@ -58,7 +57,6 @@ var scripts = {
|
|
|
58
57
|
build: "tauri build",
|
|
59
58
|
"build:debug": "tauri build --debug",
|
|
60
59
|
"build:mac": "tauri build --target universal-apple-darwin",
|
|
61
|
-
"build:config": "chmod +x scripts/configure-tauri.mjs && node scripts/configure-tauri.mjs",
|
|
62
60
|
analyze: "cd src-tauri && cargo bloat --release --crates",
|
|
63
61
|
tauri: "tauri",
|
|
64
62
|
cli: "cross-env NODE_ENV=development rollup -c -w",
|
|
@@ -75,7 +73,6 @@ var license = "MIT";
|
|
|
75
73
|
var dependencies = {
|
|
76
74
|
"@tauri-apps/api": "^2.9.0",
|
|
77
75
|
"@tauri-apps/cli": "^2.9.0",
|
|
78
|
-
axios: "^1.12.2",
|
|
79
76
|
chalk: "^5.6.2",
|
|
80
77
|
commander: "^12.1.0",
|
|
81
78
|
execa: "^9.6.0",
|
|
@@ -108,7 +105,8 @@ var devDependencies = {
|
|
|
108
105
|
rollup: "^4.52.5",
|
|
109
106
|
"rollup-plugin-typescript2": "^0.36.0",
|
|
110
107
|
tslib: "^2.8.1",
|
|
111
|
-
typescript: "^5.9.3"
|
|
108
|
+
typescript: "^5.9.3",
|
|
109
|
+
vitest: "^4.0.15"
|
|
112
110
|
};
|
|
113
111
|
var packageJson = {
|
|
114
112
|
name: name,
|
|
@@ -379,7 +377,12 @@ async function installRust() {
|
|
|
379
377
|
}
|
|
380
378
|
catch (error) {
|
|
381
379
|
spinner.fail(chalk.red('โ Rust installation failed!'));
|
|
382
|
-
|
|
380
|
+
if (error instanceof Error) {
|
|
381
|
+
console.error(error.message);
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
console.error(error);
|
|
385
|
+
}
|
|
383
386
|
process.exit(1);
|
|
384
387
|
}
|
|
385
388
|
}
|
|
@@ -421,6 +424,9 @@ function generateSafeFilename(name) {
|
|
|
421
424
|
.replace(/\.+$/g, '')
|
|
422
425
|
.slice(0, 255);
|
|
423
426
|
}
|
|
427
|
+
function getSafeAppName(name) {
|
|
428
|
+
return generateSafeFilename(name).toLowerCase();
|
|
429
|
+
}
|
|
424
430
|
function generateLinuxPackageName(name) {
|
|
425
431
|
return name
|
|
426
432
|
.toLowerCase()
|
|
@@ -448,12 +454,6 @@ function generateIdentifierSafeName(name) {
|
|
|
448
454
|
return cleaned;
|
|
449
455
|
}
|
|
450
456
|
|
|
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
457
|
async function mergeConfig(url, options, tauriConf) {
|
|
458
458
|
// Ensure .pake directory exists and copy source templates if needed
|
|
459
459
|
const srcTauriDir = path.join(npmDirectory, 'src-tauri');
|
|
@@ -474,7 +474,7 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
474
474
|
await fsExtra.copy(sourcePath, destPath);
|
|
475
475
|
}
|
|
476
476
|
}));
|
|
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;
|
|
477
|
+
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
478
|
const { platform } = process;
|
|
479
479
|
const platformHideOnClose = hideOnClose ?? platform === 'darwin';
|
|
480
480
|
const tauriConfWindowOptions = {
|
|
@@ -490,7 +490,7 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
490
490
|
disabled_web_shortcuts: disabledWebShortcuts,
|
|
491
491
|
hide_on_close: platformHideOnClose,
|
|
492
492
|
incognito: incognito,
|
|
493
|
-
title: title
|
|
493
|
+
title: title,
|
|
494
494
|
enable_wasm: wasm,
|
|
495
495
|
enable_drag_drop: enableDragDrop,
|
|
496
496
|
start_to_tray: startToTray && showSystemTray,
|
|
@@ -555,10 +555,13 @@ async function mergeConfig(url, options, tauriConf) {
|
|
|
555
555
|
const identifier = `com.pake.${appNameSafe}`;
|
|
556
556
|
const desktopFileName = `${identifier}.desktop`;
|
|
557
557
|
// Create desktop file content
|
|
558
|
+
// Determine if title contains Chinese characters for Name[zh_CN]
|
|
559
|
+
const chineseName = title && /[\u4e00-\u9fa5]/.test(title) ? title : null;
|
|
558
560
|
const desktopContent = `[Desktop Entry]
|
|
559
561
|
Version=1.0
|
|
560
562
|
Type=Application
|
|
561
563
|
Name=${name}
|
|
564
|
+
${chineseName ? `Name[zh_CN]=${chineseName}` : ''}
|
|
562
565
|
Comment=${name}
|
|
563
566
|
Exec=pake-${appNameSafe}
|
|
564
567
|
Icon=${appNameSafe}_512
|
|
@@ -822,7 +825,9 @@ class BaseBuilder {
|
|
|
822
825
|
}
|
|
823
826
|
catch (error) {
|
|
824
827
|
// If installation times out and we haven't tried the mirror yet, retry with mirror
|
|
825
|
-
if (error
|
|
828
|
+
if (error instanceof Error &&
|
|
829
|
+
error.message.includes('timed out') &&
|
|
830
|
+
!usedMirror) {
|
|
826
831
|
spinner.fail(chalk.yellow('Installation timed out, retrying with CN mirror...'));
|
|
827
832
|
logger.info('โบ Retrying installation with CN mirror for better speed...');
|
|
828
833
|
const retrySpinner = getSpinner('Retrying installation...');
|
|
@@ -851,10 +856,18 @@ class BaseBuilder {
|
|
|
851
856
|
await this.buildAndCopy(url, this.options.targets);
|
|
852
857
|
}
|
|
853
858
|
async start(url) {
|
|
859
|
+
logger.info('Pake dev server starting...');
|
|
854
860
|
await mergeConfig(url, this.options, tauriConfig);
|
|
861
|
+
const packageManager = await this.detectPackageManager();
|
|
862
|
+
const configPath = path.join(npmDirectory, 'src-tauri', '.pake', 'tauri.conf.json');
|
|
863
|
+
const features = this.getBuildFeatures();
|
|
864
|
+
const featureArgs = features.length > 0 ? `--features ${features.join(',')}` : '';
|
|
865
|
+
const argSeparator = packageManager === 'npm' ? ' --' : '';
|
|
866
|
+
const command = `cd "${npmDirectory}" && ${packageManager} run tauri${argSeparator} dev --config "${configPath}" ${featureArgs}`;
|
|
867
|
+
await shellExec(command);
|
|
855
868
|
}
|
|
856
869
|
async buildAndCopy(url, target) {
|
|
857
|
-
const { name } = this.options;
|
|
870
|
+
const { name = 'pake-app' } = this.options;
|
|
858
871
|
await mergeConfig(url, this.options, tauriConfig);
|
|
859
872
|
// Detect available package manager
|
|
860
873
|
const packageManager = await this.detectPackageManager();
|
|
@@ -1121,7 +1134,7 @@ class MacBuilder extends BaseBuilder {
|
|
|
1121
1134
|
this.buildArch = validArchs.includes(options.targets || '')
|
|
1122
1135
|
? options.targets
|
|
1123
1136
|
: 'auto';
|
|
1124
|
-
if (process.env.PAKE_CREATE_APP === '1') {
|
|
1137
|
+
if (options.iterativeBuild || process.env.PAKE_CREATE_APP === '1') {
|
|
1125
1138
|
this.buildFormat = 'app';
|
|
1126
1139
|
}
|
|
1127
1140
|
else {
|
|
@@ -1130,7 +1143,7 @@ class MacBuilder extends BaseBuilder {
|
|
|
1130
1143
|
this.options.targets = this.buildFormat;
|
|
1131
1144
|
}
|
|
1132
1145
|
getFileName() {
|
|
1133
|
-
const { name } = this.options;
|
|
1146
|
+
const { name = 'pake-app' } = this.options;
|
|
1134
1147
|
if (this.buildFormat === 'app') {
|
|
1135
1148
|
return name;
|
|
1136
1149
|
}
|
|
@@ -1249,7 +1262,7 @@ class LinuxBuilder extends BaseBuilder {
|
|
|
1249
1262
|
this.options.targets = this.buildFormat;
|
|
1250
1263
|
}
|
|
1251
1264
|
getFileName() {
|
|
1252
|
-
const { name, targets } = this.options;
|
|
1265
|
+
const { name = 'pake-app', targets } = this.options;
|
|
1253
1266
|
const version = tauriConfig.version;
|
|
1254
1267
|
let arch;
|
|
1255
1268
|
if (this.buildArch === 'arm64') {
|
|
@@ -1283,7 +1296,7 @@ class LinuxBuilder extends BaseBuilder {
|
|
|
1283
1296
|
getBuildCommand(packageManager = 'pnpm') {
|
|
1284
1297
|
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
|
1285
1298
|
const buildTarget = this.buildArch === 'arm64'
|
|
1286
|
-
? this.getTauriTarget(this.buildArch, 'linux')
|
|
1299
|
+
? (this.getTauriTarget(this.buildArch, 'linux') ?? undefined)
|
|
1287
1300
|
: undefined;
|
|
1288
1301
|
let fullCommand = this.buildBaseCommand(packageManager, configPath, buildTarget);
|
|
1289
1302
|
const features = this.getBuildFeatures();
|
|
@@ -1341,48 +1354,6 @@ class BuilderProvider {
|
|
|
1341
1354
|
}
|
|
1342
1355
|
}
|
|
1343
1356
|
|
|
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,
|
|
1378
|
-
};
|
|
1379
|
-
|
|
1380
|
-
async function checkUpdateTips() {
|
|
1381
|
-
updateNotifier({ pkg: packageJson, updateCheckInterval: 1000 * 60 }).notify({
|
|
1382
|
-
isGlobal: true,
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
1357
|
const ICON_CONFIG = {
|
|
1387
1358
|
minFileSize: 100,
|
|
1388
1359
|
supportedFormats: ['png', 'ico', 'jpeg', 'jpg', 'webp', 'icns'],
|
|
@@ -1454,7 +1425,57 @@ async function preprocessIcon(inputPath) {
|
|
|
1454
1425
|
return outputPath;
|
|
1455
1426
|
}
|
|
1456
1427
|
catch (error) {
|
|
1457
|
-
|
|
1428
|
+
if (error instanceof Error) {
|
|
1429
|
+
logger.warn(`Failed to add background to icon: ${error.message}`);
|
|
1430
|
+
}
|
|
1431
|
+
return inputPath;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Applies macOS squircle mask to icon
|
|
1436
|
+
*/
|
|
1437
|
+
async function applyMacOSMask(inputPath) {
|
|
1438
|
+
try {
|
|
1439
|
+
const { path: tempDir } = await dir();
|
|
1440
|
+
const outputPath = path.join(tempDir, 'icon-macos-rounded.png');
|
|
1441
|
+
// 1. Create a 1024x1024 rounded rect mask
|
|
1442
|
+
// rx="224" is closer to the smooth Apple squircle look for 1024px
|
|
1443
|
+
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>');
|
|
1444
|
+
// 2. Load input, resize to 1024, apply mask
|
|
1445
|
+
const maskedBuffer = await sharp(inputPath)
|
|
1446
|
+
.resize(1024, 1024, {
|
|
1447
|
+
fit: 'contain',
|
|
1448
|
+
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
|
1449
|
+
})
|
|
1450
|
+
.composite([
|
|
1451
|
+
{
|
|
1452
|
+
input: mask,
|
|
1453
|
+
blend: 'dest-in',
|
|
1454
|
+
},
|
|
1455
|
+
])
|
|
1456
|
+
.png()
|
|
1457
|
+
.toBuffer();
|
|
1458
|
+
// 3. Resize to 840x840 (~18% padding) to solve "too big" visual issue
|
|
1459
|
+
// Native MacOS icons often leave some breathing room
|
|
1460
|
+
await sharp(maskedBuffer)
|
|
1461
|
+
.resize(840, 840, {
|
|
1462
|
+
fit: 'contain',
|
|
1463
|
+
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
|
1464
|
+
})
|
|
1465
|
+
.extend({
|
|
1466
|
+
top: 92,
|
|
1467
|
+
bottom: 92,
|
|
1468
|
+
left: 92,
|
|
1469
|
+
right: 92,
|
|
1470
|
+
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
|
1471
|
+
})
|
|
1472
|
+
.toFile(outputPath);
|
|
1473
|
+
return outputPath;
|
|
1474
|
+
}
|
|
1475
|
+
catch (error) {
|
|
1476
|
+
if (error instanceof Error) {
|
|
1477
|
+
logger.warn(`Failed to apply macOS mask: ${error.message}`);
|
|
1478
|
+
}
|
|
1458
1479
|
return inputPath;
|
|
1459
1480
|
}
|
|
1460
1481
|
}
|
|
@@ -1496,7 +1517,8 @@ async function convertIconFormat(inputPath, appName) {
|
|
|
1496
1517
|
return outputPath;
|
|
1497
1518
|
}
|
|
1498
1519
|
// macOS
|
|
1499
|
-
await
|
|
1520
|
+
const macIconPath = await applyMacOSMask(processedInputPath);
|
|
1521
|
+
await icongen(macIconPath, platformOutputDir, {
|
|
1500
1522
|
report: false,
|
|
1501
1523
|
icns: { name: iconName, sizes: PLATFORM_CONFIG.macos.sizes },
|
|
1502
1524
|
});
|
|
@@ -1504,7 +1526,9 @@ async function convertIconFormat(inputPath, appName) {
|
|
|
1504
1526
|
return (await fsExtra.pathExists(outputPath)) ? outputPath : null;
|
|
1505
1527
|
}
|
|
1506
1528
|
catch (error) {
|
|
1507
|
-
|
|
1529
|
+
if (error instanceof Error) {
|
|
1530
|
+
logger.warn(`Icon format conversion failed: ${error.message}`);
|
|
1531
|
+
}
|
|
1508
1532
|
return null;
|
|
1509
1533
|
}
|
|
1510
1534
|
}
|
|
@@ -1642,14 +1666,18 @@ async function tryGetFavicon(url, appName) {
|
|
|
1642
1666
|
}
|
|
1643
1667
|
}
|
|
1644
1668
|
catch (error) {
|
|
1645
|
-
|
|
1646
|
-
|
|
1669
|
+
if (error instanceof Error) {
|
|
1670
|
+
logger.debug(`Icon service ${serviceUrl} failed: ${error.message}`);
|
|
1671
|
+
}
|
|
1672
|
+
// Network error handling
|
|
1647
1673
|
if ((IS_LINUX || IS_WIN) && error.code === 'ENOTFOUND') {
|
|
1648
|
-
|
|
1674
|
+
return null;
|
|
1649
1675
|
}
|
|
1650
|
-
//
|
|
1651
|
-
if (IS_WIN &&
|
|
1652
|
-
|
|
1676
|
+
// Icon generation error on Windows
|
|
1677
|
+
if (IS_WIN &&
|
|
1678
|
+
error instanceof Error &&
|
|
1679
|
+
error.message.includes('icongen')) {
|
|
1680
|
+
return null;
|
|
1653
1681
|
}
|
|
1654
1682
|
continue;
|
|
1655
1683
|
}
|
|
@@ -1658,7 +1686,9 @@ async function tryGetFavicon(url, appName) {
|
|
|
1658
1686
|
return null;
|
|
1659
1687
|
}
|
|
1660
1688
|
catch (error) {
|
|
1661
|
-
|
|
1689
|
+
if (error instanceof Error) {
|
|
1690
|
+
logger.warn(`Failed to fetch favicon: ${error.message}`);
|
|
1691
|
+
}
|
|
1662
1692
|
return null;
|
|
1663
1693
|
}
|
|
1664
1694
|
}
|
|
@@ -1666,24 +1696,40 @@ async function tryGetFavicon(url, appName) {
|
|
|
1666
1696
|
* Downloads icon from URL
|
|
1667
1697
|
*/
|
|
1668
1698
|
async function downloadIcon(iconUrl, showSpinner = true, customTimeout) {
|
|
1699
|
+
const controller = new AbortController();
|
|
1700
|
+
const timeoutId = setTimeout(() => {
|
|
1701
|
+
controller.abort();
|
|
1702
|
+
}, customTimeout || 10000);
|
|
1669
1703
|
try {
|
|
1670
|
-
const response = await
|
|
1671
|
-
|
|
1672
|
-
timeout: customTimeout || 10000,
|
|
1704
|
+
const response = await fetch(iconUrl, {
|
|
1705
|
+
signal: controller.signal,
|
|
1673
1706
|
});
|
|
1674
|
-
|
|
1675
|
-
if (!
|
|
1707
|
+
clearTimeout(timeoutId);
|
|
1708
|
+
if (!response.ok) {
|
|
1709
|
+
if (response.status === 404 && !showSpinner) {
|
|
1710
|
+
return null;
|
|
1711
|
+
}
|
|
1712
|
+
throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
1713
|
+
}
|
|
1714
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
1715
|
+
if (!arrayBuffer || arrayBuffer.byteLength < ICON_CONFIG.minFileSize)
|
|
1676
1716
|
return null;
|
|
1677
|
-
const fileDetails = await fileTypeFromBuffer(
|
|
1717
|
+
const fileDetails = await fileTypeFromBuffer(arrayBuffer);
|
|
1678
1718
|
if (!fileDetails ||
|
|
1679
1719
|
!ICON_CONFIG.supportedFormats.includes(fileDetails.ext)) {
|
|
1680
1720
|
return null;
|
|
1681
1721
|
}
|
|
1682
|
-
return await saveIconFile(
|
|
1722
|
+
return await saveIconFile(arrayBuffer, fileDetails.ext);
|
|
1683
1723
|
}
|
|
1684
1724
|
catch (error) {
|
|
1685
|
-
|
|
1686
|
-
|
|
1725
|
+
clearTimeout(timeoutId);
|
|
1726
|
+
if (showSpinner) {
|
|
1727
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
1728
|
+
logger.error('Icon download timed out!');
|
|
1729
|
+
}
|
|
1730
|
+
else {
|
|
1731
|
+
logger.error('Icon download failed!', error instanceof Error ? error.message : String(error));
|
|
1732
|
+
}
|
|
1687
1733
|
}
|
|
1688
1734
|
return null;
|
|
1689
1735
|
}
|
|
@@ -1766,7 +1812,7 @@ async function handleOptions(options, url) {
|
|
|
1766
1812
|
if (name && platform === 'linux') {
|
|
1767
1813
|
name = generateLinuxPackageName(name);
|
|
1768
1814
|
}
|
|
1769
|
-
if (!isValidName(name, platform)) {
|
|
1815
|
+
if (name && !isValidName(name, platform)) {
|
|
1770
1816
|
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
1817
|
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
1818
|
const errorMsg = platform === 'linux' ? LINUX_NAME_ERROR : DEFAULT_NAME_ERROR;
|
|
@@ -1785,10 +1831,58 @@ async function handleOptions(options, url) {
|
|
|
1785
1831
|
identifier: getIdentifier(url),
|
|
1786
1832
|
};
|
|
1787
1833
|
const iconPath = await handleIcon(appOptions, url);
|
|
1788
|
-
appOptions.icon = iconPath ||
|
|
1834
|
+
appOptions.icon = iconPath || '';
|
|
1789
1835
|
return appOptions;
|
|
1790
1836
|
}
|
|
1791
1837
|
|
|
1838
|
+
const DEFAULT_PAKE_OPTIONS = {
|
|
1839
|
+
icon: '',
|
|
1840
|
+
height: 780,
|
|
1841
|
+
width: 1200,
|
|
1842
|
+
fullscreen: false,
|
|
1843
|
+
maximize: false,
|
|
1844
|
+
hideTitleBar: false,
|
|
1845
|
+
alwaysOnTop: false,
|
|
1846
|
+
appVersion: '1.0.0',
|
|
1847
|
+
darkMode: false,
|
|
1848
|
+
disabledWebShortcuts: false,
|
|
1849
|
+
activationShortcut: '',
|
|
1850
|
+
userAgent: '',
|
|
1851
|
+
showSystemTray: false,
|
|
1852
|
+
multiArch: false,
|
|
1853
|
+
targets: (() => {
|
|
1854
|
+
switch (process.platform) {
|
|
1855
|
+
case 'linux':
|
|
1856
|
+
return 'deb';
|
|
1857
|
+
case 'darwin':
|
|
1858
|
+
return 'dmg';
|
|
1859
|
+
case 'win32':
|
|
1860
|
+
return 'msi';
|
|
1861
|
+
default:
|
|
1862
|
+
return 'deb';
|
|
1863
|
+
}
|
|
1864
|
+
})(),
|
|
1865
|
+
useLocalFile: false,
|
|
1866
|
+
systemTrayIcon: '',
|
|
1867
|
+
proxyUrl: '',
|
|
1868
|
+
debug: false,
|
|
1869
|
+
inject: [],
|
|
1870
|
+
installerLanguage: 'en-US',
|
|
1871
|
+
hideOnClose: undefined, // Platform-specific: true for macOS, false for others
|
|
1872
|
+
incognito: false,
|
|
1873
|
+
wasm: false,
|
|
1874
|
+
enableDragDrop: false,
|
|
1875
|
+
keepBinary: false,
|
|
1876
|
+
multiInstance: false,
|
|
1877
|
+
startToTray: false,
|
|
1878
|
+
forceInternalNavigation: false,
|
|
1879
|
+
iterativeBuild: false,
|
|
1880
|
+
zoom: 100,
|
|
1881
|
+
minWidth: 0,
|
|
1882
|
+
minHeight: 0,
|
|
1883
|
+
ignoreCertificateErrors: false,
|
|
1884
|
+
};
|
|
1885
|
+
|
|
1792
1886
|
function validateNumberInput(value) {
|
|
1793
1887
|
const parsedValue = Number(value);
|
|
1794
1888
|
if (isNaN(parsedValue)) {
|
|
@@ -1803,152 +1897,163 @@ function validateUrlInput(url) {
|
|
|
1803
1897
|
return normalizeUrl(url);
|
|
1804
1898
|
}
|
|
1805
1899
|
catch (error) {
|
|
1806
|
-
|
|
1900
|
+
if (error instanceof Error) {
|
|
1901
|
+
throw new InvalidArgumentError(error.message);
|
|
1902
|
+
}
|
|
1903
|
+
throw error;
|
|
1807
1904
|
}
|
|
1808
1905
|
}
|
|
1809
1906
|
return url;
|
|
1810
1907
|
}
|
|
1811
1908
|
|
|
1812
|
-
|
|
1813
|
-
const
|
|
1909
|
+
function getCliProgram() {
|
|
1910
|
+
const { green, yellow } = chalk;
|
|
1911
|
+
const logo = `${chalk.green(' ____ _')}
|
|
1814
1912
|
${green('| _ \\ __ _| | _____')}
|
|
1815
1913
|
${green('| |_) / _` | |/ / _ \\')}
|
|
1816
1914
|
${green('| __/ (_| | < __/')} ${yellow('https://github.com/tw93/pake')}
|
|
1817
1915
|
${green('|_| \\__,_|_|\\_\\___| can turn any webpage into a desktop app with Rust.')}
|
|
1818
1916
|
`;
|
|
1819
|
-
program
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
.
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
return
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
return
|
|
1948
|
-
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1917
|
+
return program$1
|
|
1918
|
+
.addHelpText('beforeAll', logo)
|
|
1919
|
+
.usage(`[url] [options]`)
|
|
1920
|
+
.showHelpAfterError()
|
|
1921
|
+
.argument('[url]', 'The web URL you want to package', validateUrlInput)
|
|
1922
|
+
.option('--name <string>', 'Application name')
|
|
1923
|
+
.option('--icon <string>', 'Application icon', DEFAULT_PAKE_OPTIONS.icon)
|
|
1924
|
+
.option('--width <number>', 'Window width', validateNumberInput, DEFAULT_PAKE_OPTIONS.width)
|
|
1925
|
+
.option('--height <number>', 'Window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height)
|
|
1926
|
+
.option('--use-local-file', 'Use local file packaging', DEFAULT_PAKE_OPTIONS.useLocalFile)
|
|
1927
|
+
.option('--fullscreen', 'Start in full screen', DEFAULT_PAKE_OPTIONS.fullscreen)
|
|
1928
|
+
.option('--hide-title-bar', 'For Mac, hide title bar', DEFAULT_PAKE_OPTIONS.hideTitleBar)
|
|
1929
|
+
.option('--multi-arch', 'For Mac, both Intel and M1', DEFAULT_PAKE_OPTIONS.multiArch)
|
|
1930
|
+
.option('--inject <files>', 'Inject local CSS/JS files into the page', (val, previous) => {
|
|
1931
|
+
if (!val)
|
|
1932
|
+
return DEFAULT_PAKE_OPTIONS.inject;
|
|
1933
|
+
// Split by comma and trim whitespace, filter out empty strings
|
|
1934
|
+
const files = val
|
|
1935
|
+
.split(',')
|
|
1936
|
+
.map((item) => item.trim())
|
|
1937
|
+
.filter((item) => item.length > 0);
|
|
1938
|
+
// If previous values exist (from multiple --inject options), merge them
|
|
1939
|
+
return previous ? [...previous, ...files] : files;
|
|
1940
|
+
}, DEFAULT_PAKE_OPTIONS.inject)
|
|
1941
|
+
.option('--debug', 'Debug build and more output', DEFAULT_PAKE_OPTIONS.debug)
|
|
1942
|
+
.addOption(new Option('--proxy-url <url>', 'Proxy URL for all network requests (http://, https://, socks5://)')
|
|
1943
|
+
.default(DEFAULT_PAKE_OPTIONS.proxyUrl)
|
|
1944
|
+
.hideHelp())
|
|
1945
|
+
.addOption(new Option('--user-agent <string>', 'Custom user agent')
|
|
1946
|
+
.default(DEFAULT_PAKE_OPTIONS.userAgent)
|
|
1947
|
+
.hideHelp())
|
|
1948
|
+
.addOption(new Option('--targets <string>', 'Build target format for your system').default(DEFAULT_PAKE_OPTIONS.targets))
|
|
1949
|
+
.addOption(new Option('--app-version <string>', 'App version, the same as package.json version')
|
|
1950
|
+
.default(DEFAULT_PAKE_OPTIONS.appVersion)
|
|
1951
|
+
.hideHelp())
|
|
1952
|
+
.addOption(new Option('--always-on-top', 'Always on the top level')
|
|
1953
|
+
.default(DEFAULT_PAKE_OPTIONS.alwaysOnTop)
|
|
1954
|
+
.hideHelp())
|
|
1955
|
+
.addOption(new Option('--maximize', 'Start window maximized')
|
|
1956
|
+
.default(DEFAULT_PAKE_OPTIONS.maximize)
|
|
1957
|
+
.hideHelp())
|
|
1958
|
+
.addOption(new Option('--dark-mode', 'Force Mac app to use dark mode')
|
|
1959
|
+
.default(DEFAULT_PAKE_OPTIONS.darkMode)
|
|
1960
|
+
.hideHelp())
|
|
1961
|
+
.addOption(new Option('--disabled-web-shortcuts', 'Disabled webPage shortcuts')
|
|
1962
|
+
.default(DEFAULT_PAKE_OPTIONS.disabledWebShortcuts)
|
|
1963
|
+
.hideHelp())
|
|
1964
|
+
.addOption(new Option('--activation-shortcut <string>', 'Shortcut key to active App')
|
|
1965
|
+
.default(DEFAULT_PAKE_OPTIONS.activationShortcut)
|
|
1966
|
+
.hideHelp())
|
|
1967
|
+
.addOption(new Option('--show-system-tray', 'Show system tray in app')
|
|
1968
|
+
.default(DEFAULT_PAKE_OPTIONS.showSystemTray)
|
|
1969
|
+
.hideHelp())
|
|
1970
|
+
.addOption(new Option('--system-tray-icon <string>', 'Custom system tray icon')
|
|
1971
|
+
.default(DEFAULT_PAKE_OPTIONS.systemTrayIcon)
|
|
1972
|
+
.hideHelp())
|
|
1973
|
+
.addOption(new Option('--hide-on-close [boolean]', 'Hide window on close instead of exiting (default: true for macOS, false for others)')
|
|
1974
|
+
.default(DEFAULT_PAKE_OPTIONS.hideOnClose)
|
|
1975
|
+
.argParser((value) => {
|
|
1976
|
+
if (value === undefined)
|
|
1977
|
+
return true; // --hide-on-close without value
|
|
1978
|
+
if (value === 'true')
|
|
1979
|
+
return true;
|
|
1980
|
+
if (value === 'false')
|
|
1981
|
+
return false;
|
|
1982
|
+
throw new Error('--hide-on-close must be true or false');
|
|
1983
|
+
})
|
|
1984
|
+
.hideHelp())
|
|
1985
|
+
.addOption(new Option('--title <string>', 'Window title').hideHelp())
|
|
1986
|
+
.addOption(new Option('--incognito', 'Launch app in incognito/private mode')
|
|
1987
|
+
.default(DEFAULT_PAKE_OPTIONS.incognito)
|
|
1988
|
+
.hideHelp())
|
|
1989
|
+
.addOption(new Option('--wasm', 'Enable WebAssembly support (Flutter Web, etc.)')
|
|
1990
|
+
.default(DEFAULT_PAKE_OPTIONS.wasm)
|
|
1991
|
+
.hideHelp())
|
|
1992
|
+
.addOption(new Option('--enable-drag-drop', 'Enable drag and drop functionality')
|
|
1993
|
+
.default(DEFAULT_PAKE_OPTIONS.enableDragDrop)
|
|
1994
|
+
.hideHelp())
|
|
1995
|
+
.addOption(new Option('--keep-binary', 'Keep raw binary file alongside installer')
|
|
1996
|
+
.default(DEFAULT_PAKE_OPTIONS.keepBinary)
|
|
1997
|
+
.hideHelp())
|
|
1998
|
+
.addOption(new Option('--multi-instance', 'Allow multiple app instances')
|
|
1999
|
+
.default(DEFAULT_PAKE_OPTIONS.multiInstance)
|
|
2000
|
+
.hideHelp())
|
|
2001
|
+
.addOption(new Option('--start-to-tray', 'Start app minimized to tray')
|
|
2002
|
+
.default(DEFAULT_PAKE_OPTIONS.startToTray)
|
|
2003
|
+
.hideHelp())
|
|
2004
|
+
.addOption(new Option('--force-internal-navigation', 'Keep every link inside the Pake window instead of opening external handlers')
|
|
2005
|
+
.default(DEFAULT_PAKE_OPTIONS.forceInternalNavigation)
|
|
2006
|
+
.hideHelp())
|
|
2007
|
+
.addOption(new Option('--installer-language <string>', 'Installer language')
|
|
2008
|
+
.default(DEFAULT_PAKE_OPTIONS.installerLanguage)
|
|
2009
|
+
.hideHelp())
|
|
2010
|
+
.addOption(new Option('--zoom <number>', 'Initial page zoom level (50-200)')
|
|
2011
|
+
.default(DEFAULT_PAKE_OPTIONS.zoom)
|
|
2012
|
+
.argParser((value) => {
|
|
2013
|
+
const zoom = parseInt(value);
|
|
2014
|
+
if (isNaN(zoom) || zoom < 50 || zoom > 200) {
|
|
2015
|
+
throw new Error('--zoom must be a number between 50 and 200');
|
|
2016
|
+
}
|
|
2017
|
+
return zoom;
|
|
2018
|
+
})
|
|
2019
|
+
.hideHelp())
|
|
2020
|
+
.addOption(new Option('--min-width <number>', 'Minimum window width')
|
|
2021
|
+
.default(DEFAULT_PAKE_OPTIONS.minWidth)
|
|
2022
|
+
.argParser(validateNumberInput)
|
|
2023
|
+
.hideHelp())
|
|
2024
|
+
.addOption(new Option('--min-height <number>', 'Minimum window height')
|
|
2025
|
+
.default(DEFAULT_PAKE_OPTIONS.minHeight)
|
|
2026
|
+
.argParser(validateNumberInput)
|
|
2027
|
+
.hideHelp())
|
|
2028
|
+
.addOption(new Option('--ignore-certificate-errors', 'Ignore certificate errors (for self-signed certificates)')
|
|
2029
|
+
.default(DEFAULT_PAKE_OPTIONS.ignoreCertificateErrors)
|
|
2030
|
+
.hideHelp())
|
|
2031
|
+
.addOption(new Option('--iterative-build', 'Turn on rapid build mode (app only, no dmg/deb/msi), good for debugging')
|
|
2032
|
+
.default(DEFAULT_PAKE_OPTIONS.iterativeBuild)
|
|
2033
|
+
.hideHelp())
|
|
2034
|
+
.version(packageJson.version, '-v, --version')
|
|
2035
|
+
.configureHelp({
|
|
2036
|
+
sortSubcommands: true,
|
|
2037
|
+
optionTerm: (option) => {
|
|
2038
|
+
if (option.flags === '-v, --version' || option.flags === '-h, --help')
|
|
2039
|
+
return '';
|
|
2040
|
+
return option.flags;
|
|
2041
|
+
},
|
|
2042
|
+
optionDescription: (option) => {
|
|
2043
|
+
if (option.flags === '-v, --version' || option.flags === '-h, --help')
|
|
2044
|
+
return '';
|
|
2045
|
+
return option.description;
|
|
2046
|
+
},
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
const program = getCliProgram();
|
|
2051
|
+
async function checkUpdateTips() {
|
|
2052
|
+
updateNotifier({ pkg: packageJson, updateCheckInterval: 1000 * 60 }).notify({
|
|
2053
|
+
isGlobal: true,
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
program.action(async (url, options) => {
|
|
1952
2057
|
await checkUpdateTips();
|
|
1953
2058
|
if (!url) {
|
|
1954
2059
|
program.help({
|