pake-cli 3.3.5 โ†’ 3.3.7-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -434,6 +434,13 @@ Pake's development can not be without these Hackers. They contributed a lot of c
434
434
  <sub><b>Jiaqi Gu</b></sub>
435
435
  </a>
436
436
  </td>
437
+ <td align="center">
438
+ <a href="https://github.com/JohannLai">
439
+ <img src="https://avatars.githubusercontent.com/u/10769405?v=4" width="90;" alt="JohannLai"/>
440
+ <br />
441
+ <sub><b>Johannlai</b></sub>
442
+ </a>
443
+ </td>
437
444
  <td align="center">
438
445
  <a href="https://github.com/Jason6987">
439
446
  <img src="https://avatars.githubusercontent.com/u/140222795?v=4" width="90;" alt="Jason6987"/>
@@ -441,6 +448,8 @@ Pake's development can not be without these Hackers. They contributed a lot of c
441
448
  <sub><b>Luminall</b></sub>
442
449
  </a>
443
450
  </td>
451
+ </tr>
452
+ <tr>
444
453
  <td align="center">
445
454
  <a href="https://github.com/Milo123459">
446
455
  <img src="https://avatars.githubusercontent.com/u/50248166?v=4" width="90;" alt="Milo123459"/>
@@ -448,8 +457,6 @@ Pake's development can not be without these Hackers. They contributed a lot of c
448
457
  <sub><b>Milo</b></sub>
449
458
  </a>
450
459
  </td>
451
- </tr>
452
- <tr>
453
460
  <td align="center">
454
461
  <a href="https://github.com/princemaple">
455
462
  <img src="https://avatars.githubusercontent.com/u/1329716?v=4" width="90;" alt="princemaple"/>
@@ -492,6 +499,8 @@ Pake's development can not be without these Hackers. They contributed a lot of c
492
499
  <sub><b>Null</b></sub>
493
500
  </a>
494
501
  </td>
502
+ </tr>
503
+ <tr>
495
504
  <td align="center">
496
505
  <a href="https://github.com/liudonghua123">
497
506
  <img src="https://avatars.githubusercontent.com/u/2276718?v=4" width="90;" alt="liudonghua123"/>
@@ -499,8 +508,6 @@ Pake's development can not be without these Hackers. They contributed a lot of c
499
508
  <sub><b>Liudonghua</b></sub>
500
509
  </a>
501
510
  </td>
502
- </tr>
503
- <tr>
504
511
  <td align="center">
505
512
  <a href="https://github.com/liusishan">
506
513
  <img src="https://avatars.githubusercontent.com/u/33129823?v=4" width="90;" alt="liusishan"/>
package/dist/cli.js CHANGED
@@ -22,7 +22,7 @@ import sharp from 'sharp';
22
22
  import * as psl from 'psl';
23
23
 
24
24
  var name = "pake-cli";
25
- var version = "3.3.5";
25
+ var version = "3.3.7-beta";
26
26
  var description = "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with one command. ๐Ÿคฑ๐Ÿป ไธ€้”ฎๆ‰“ๅŒ…็ฝ‘้กต็”Ÿๆˆ่ฝป้‡ๆกŒ้ขๅบ”็”จใ€‚";
27
27
  var engines = {
28
28
  node: ">=18.0.0"
@@ -328,6 +328,40 @@ async function combineFiles(files, output) {
328
328
  return files;
329
329
  }
330
330
 
331
+ function generateSafeFilename(name) {
332
+ return name
333
+ .replace(/[<>:"/\\|?*]/g, '_')
334
+ .replace(/\s+/g, '_')
335
+ .replace(/\.+$/g, '')
336
+ .slice(0, 255);
337
+ }
338
+ function generateLinuxPackageName(name) {
339
+ return name
340
+ .toLowerCase()
341
+ .replace(/[^a-z0-9\u4e00-\u9fff]+/g, '-')
342
+ .replace(/^-+|-+$/g, '')
343
+ .replace(/-+/g, '-');
344
+ }
345
+ function generateIdentifierSafeName(name) {
346
+ const cleaned = name
347
+ .replace(/[^a-zA-Z0-9\u4e00-\u9fff]/g, '')
348
+ .toLowerCase();
349
+ if (cleaned === '') {
350
+ const fallback = Array.from(name)
351
+ .map(char => {
352
+ const code = char.charCodeAt(0);
353
+ if ((code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122)) {
354
+ return char.toLowerCase();
355
+ }
356
+ return code.toString(16);
357
+ })
358
+ .join('')
359
+ .slice(0, 50);
360
+ return fallback || 'pake-app';
361
+ }
362
+ return cleaned;
363
+ }
364
+
331
365
  async function mergeConfig(url, options, tauriConf) {
332
366
  // Ensure .pake directory exists and copy source templates if needed
333
367
  const srcTauriDir = path.join(npmDirectory, 'src-tauri');
@@ -372,7 +406,7 @@ async function mergeConfig(url, options, tauriConf) {
372
406
  tauriConf.identifier = identifier;
373
407
  tauriConf.version = appVersion;
374
408
  if (platform === 'linux') {
375
- tauriConf.mainBinaryName = `pake-${name.toLowerCase()}`;
409
+ tauriConf.mainBinaryName = `pake-${generateIdentifierSafeName(name)}`;
376
410
  }
377
411
  if (platform == 'win32') {
378
412
  tauriConf.bundle.windows.wix.language[0] = installerLanguage;
@@ -418,8 +452,8 @@ async function mergeConfig(url, options, tauriConf) {
418
452
  // Remove hardcoded desktop files and regenerate with correct app name
419
453
  delete tauriConf.bundle.linux.deb.files;
420
454
  // Generate correct desktop file configuration
421
- const appNameLower = name.toLowerCase();
422
- const identifier = `com.pake.${appNameLower}`;
455
+ const appNameSafe = generateSafeFilename(name).toLowerCase();
456
+ const identifier = `com.pake.${appNameSafe}`;
423
457
  const desktopFileName = `${identifier}.desktop`;
424
458
  // Create desktop file content
425
459
  const desktopContent = `[Desktop Entry]
@@ -427,8 +461,8 @@ Version=1.0
427
461
  Type=Application
428
462
  Name=${name}
429
463
  Comment=${name}
430
- Exec=pake-${appNameLower}
431
- Icon=${appNameLower}_512
464
+ Exec=pake-${appNameSafe}
465
+ Icon=${appNameSafe}_512
432
466
  Categories=Network;WebBrowser;
433
467
  MimeType=text/html;text/xml;application/xhtml_xml;
434
468
  StartupNotify=true
@@ -472,28 +506,29 @@ StartupNotify=true
472
506
  const platformIconMap = {
473
507
  win32: {
474
508
  fileExt: '.ico',
475
- path: `png/${name.toLowerCase()}_256.ico`,
509
+ path: `png/${generateSafeFilename(name).toLowerCase()}_256.ico`,
476
510
  defaultIcon: 'png/icon_256.ico',
477
511
  message: 'Windows icon must be .ico and 256x256px.',
478
512
  },
479
513
  linux: {
480
514
  fileExt: '.png',
481
- path: `png/${name.toLowerCase()}_512.png`,
515
+ path: `png/${generateSafeFilename(name).toLowerCase()}_512.png`,
482
516
  defaultIcon: 'png/icon_512.png',
483
517
  message: 'Linux icon must be .png and 512x512px.',
484
518
  },
485
519
  darwin: {
486
520
  fileExt: '.icns',
487
- path: `icons/${name.toLowerCase()}.icns`,
521
+ path: `icons/${generateSafeFilename(name).toLowerCase()}.icns`,
488
522
  defaultIcon: 'icons/icon.icns',
489
523
  message: 'macOS icon must be .icns type.',
490
524
  },
491
525
  };
492
526
  const iconInfo = platformIconMap[platform];
493
- const exists = options.icon && (await fsExtra.pathExists(options.icon));
527
+ const resolvedIconPath = options.icon ? path.resolve(options.icon) : null;
528
+ const exists = resolvedIconPath && (await fsExtra.pathExists(resolvedIconPath));
494
529
  if (exists) {
495
530
  let updateIconPath = true;
496
- let customIconExt = path.extname(options.icon).toLowerCase();
531
+ let customIconExt = path.extname(resolvedIconPath).toLowerCase();
497
532
  if (customIconExt !== iconInfo.fileExt) {
498
533
  updateIconPath = false;
499
534
  logger.warn(`โœผ ${iconInfo.message}, but you give ${customIconExt}`);
@@ -503,10 +538,9 @@ StartupNotify=true
503
538
  const iconPath = path.join(npmDirectory, 'src-tauri/', iconInfo.path);
504
539
  tauriConf.bundle.resources = [iconInfo.path];
505
540
  // Avoid copying if source and destination are the same
506
- const absoluteIconPath = path.resolve(options.icon);
507
541
  const absoluteDestPath = path.resolve(iconPath);
508
- if (absoluteIconPath !== absoluteDestPath) {
509
- await fsExtra.copy(options.icon, iconPath);
542
+ if (resolvedIconPath !== absoluteDestPath) {
543
+ await fsExtra.copy(resolvedIconPath, iconPath);
510
544
  }
511
545
  }
512
546
  if (updateIconPath) {
@@ -528,8 +562,8 @@ StartupNotify=true
528
562
  // ้œ€่ฆๅˆคๆ–ญๅ›พๆ ‡ๆ ผๅผ๏ผŒ้ป˜่ฎคๅชๆ”ฏๆŒicoๅ’Œpngไธค็ง
529
563
  let iconExt = path.extname(systemTrayIcon).toLowerCase();
530
564
  if (iconExt == '.png' || iconExt == '.ico') {
531
- const trayIcoPath = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}${iconExt}`);
532
- trayIconPath = `png/${name.toLowerCase()}${iconExt}`;
565
+ const trayIcoPath = path.join(npmDirectory, `src-tauri/png/${generateSafeFilename(name).toLowerCase()}${iconExt}`);
566
+ trayIconPath = `png/${generateSafeFilename(name).toLowerCase()}${iconExt}`;
533
567
  await fsExtra.copy(systemTrayIcon, trayIcoPath);
534
568
  }
535
569
  else {
@@ -854,7 +888,7 @@ class BaseBuilder {
854
888
  const extension = process.platform === 'win32' ? '.exe' : '';
855
889
  // Linux uses the unique binary name we set in merge.ts
856
890
  if (process.platform === 'linux') {
857
- return `pake-${appName.toLowerCase()}${extension}`;
891
+ return `pake-${generateIdentifierSafeName(appName)}${extension}`;
858
892
  }
859
893
  // Windows and macOS use 'pake' as binary name
860
894
  return `pake${extension}`;
@@ -1169,12 +1203,11 @@ const API_KEYS = {
1169
1203
  logoDev: ['pk_JLLMUKGZRpaG5YclhXaTkg', 'pk_Ph745P8mQSeYFfW2Wk039A'],
1170
1204
  brandfetch: ['1idqvJC0CeFSeyp3Yf7', '1idej-yhU_ThggIHFyG'],
1171
1205
  };
1172
- /**
1173
- * Generates platform-specific icon paths and handles copying for Windows
1174
- */
1175
1206
  function generateIconPath(appName, isDefault = false) {
1176
- const safeName = appName.toLowerCase().replace(/[^a-z0-9-_]/g, '_');
1177
- const baseName = isDefault ? 'icon' : safeName;
1207
+ const safeName = isDefault
1208
+ ? 'icon'
1209
+ : generateSafeFilename(appName).toLowerCase();
1210
+ const baseName = safeName;
1178
1211
  if (IS_WIN) {
1179
1212
  return path.join(npmDirectory, 'src-tauri', 'png', `${baseName}_256.ico`);
1180
1213
  }
@@ -1237,7 +1270,7 @@ async function convertIconFormat(inputPath, appName) {
1237
1270
  const platformOutputDir = path.join(outputDir, 'converted-icons');
1238
1271
  await fsExtra.ensureDir(platformOutputDir);
1239
1272
  const processedInputPath = await preprocessIcon(inputPath);
1240
- const iconName = appName.toLowerCase();
1273
+ const iconName = generateSafeFilename(appName).toLowerCase();
1241
1274
  // Generate platform-specific format
1242
1275
  if (IS_WIN) {
1243
1276
  // Support multiple sizes for better Windows compatibility
@@ -1513,8 +1546,8 @@ function resolveAppName(name, platform) {
1513
1546
  }
1514
1547
  function isValidName(name, platform) {
1515
1548
  const platformRegexMapping = {
1516
- linux: /^[a-z0-9][a-z0-9-]*$/,
1517
- default: /^[a-zA-Z0-9][a-zA-Z0-9- ]*$/,
1549
+ linux: /^[a-z0-9\u4e00-\u9fff][a-z0-9\u4e00-\u9fff-]*$/,
1550
+ default: /^[a-zA-Z0-9\u4e00-\u9fff][a-zA-Z0-9\u4e00-\u9fff- ]*$/,
1518
1551
  };
1519
1552
  const reg = platformRegexMapping[platform] || platformRegexMapping.default;
1520
1553
  return !!name && reg.test(name);
@@ -1530,10 +1563,8 @@ async function handleOptions(options, url) {
1530
1563
  const namePrompt = await promptText(promptMessage, defaultName);
1531
1564
  name = namePrompt || defaultName;
1532
1565
  }
1533
- // Handle platform-specific name formatting
1534
1566
  if (name && platform === 'linux') {
1535
- // Convert to lowercase and replace spaces with dashes for Linux
1536
- name = name.toLowerCase().replace(/\s+/g, '-');
1567
+ name = generateLinuxPackageName(name);
1537
1568
  }
1538
1569
  if (!isValidName(name, platform)) {
1539
1570
  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.`;
@@ -1643,8 +1674,17 @@ program
1643
1674
  .addOption(new Option('--system-tray-icon <string>', 'Custom system tray icon')
1644
1675
  .default(DEFAULT_PAKE_OPTIONS.systemTrayIcon)
1645
1676
  .hideHelp())
1646
- .addOption(new Option('--hide-on-close', 'Hide window on close instead of exiting (default: true for macOS, false for others)')
1677
+ .addOption(new Option('--hide-on-close [boolean]', 'Hide window on close instead of exiting (default: true for macOS, false for others)')
1647
1678
  .default(DEFAULT_PAKE_OPTIONS.hideOnClose)
1679
+ .argParser((value) => {
1680
+ if (value === undefined)
1681
+ return true; // --hide-on-close without value
1682
+ if (value === 'true')
1683
+ return true;
1684
+ if (value === 'false')
1685
+ return false;
1686
+ throw new Error('--hide-on-close must be true or false');
1687
+ })
1648
1688
  .hideHelp())
1649
1689
  .addOption(new Option('--title <string>', 'Window title').hideHelp())
1650
1690
  .addOption(new Option('--incognito', 'Launch app in incognito/private mode')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pake-cli",
3
- "version": "3.3.5",
3
+ "version": "3.3.7-beta",
4
4
  "description": "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with one command. ๐Ÿคฑ๐Ÿป ไธ€้”ฎๆ‰“ๅŒ…็ฝ‘้กต็”Ÿๆˆ่ฝป้‡ๆกŒ้ขๅบ”็”จใ€‚",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -8,9 +8,3 @@ registry = "sparse+https://rsproxy.cn/index/"
8
8
  index = "https://rsproxy.cn/crates.io-index"
9
9
  [net]
10
10
  git-fetch-with-cli = true
11
-
12
- [env]
13
- # Fix for macOS 26 Beta compatibility issues
14
- # Forces use of compatible SDK when building on macOS 26 Beta
15
- MACOSX_DEPLOYMENT_TARGET = "15.5"
16
- SDKROOT = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk"
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "windows": [
3
3
  {
4
- "url": "https://weekly.tw93.fun",
4
+ "url": "https://github.com",
5
5
  "url_type": "web",
6
- "hide_title_bar": false,
6
+ "hide_title_bar": true,
7
7
  "fullscreen": false,
8
8
  "width": 1200,
9
- "height": 780,
9
+ "height": 800,
10
10
  "resizable": true,
11
11
  "always_on_top": false,
12
12
  "dark_mode": false,
@@ -1,6 +1,6 @@
1
1
  {
2
- "productName": "TestWorkflow",
3
- "identifier": "com.pake.33f03a",
2
+ "productName": "GitHubMultiArch",
3
+ "identifier": "com.pake.3097fc",
4
4
  "version": "1.0.0",
5
5
  "app": {
6
6
  "withGlobalTauri": true,
@@ -13,14 +13,14 @@
13
13
  },
14
14
  "bundle": {
15
15
  "icon": [
16
- "icons/testworkflow.icns"
16
+ "icons/githubmultiarch.icns"
17
17
  ],
18
18
  "active": true,
19
19
  "targets": [
20
- "dmg"
20
+ "app"
21
21
  ],
22
22
  "resources": [
23
- "icons/testworkflow.icns"
23
+ "icons/githubmultiarch.icns"
24
24
  ]
25
25
  }
26
26
  }
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "bundle": {
3
3
  "icon": [
4
- "icons/testworkflow.icns"
4
+ "icons/githubmultiarch.icns"
5
5
  ],
6
6
  "active": true,
7
7
  "targets": [
8
- "dmg"
8
+ "app"
9
9
  ],
10
10
  "resources": [
11
- "icons/testworkflow.icns"
11
+ "icons/githubmultiarch.icns"
12
12
  ]
13
13
  }
14
14
  }
@@ -252,40 +252,52 @@ document.addEventListener("DOMContentLoaded", () => {
252
252
  }
253
253
 
254
254
  function downloadFromDataUri(dataURI, filename) {
255
- const byteString = atob(dataURI.split(",")[1]);
256
- // write the bytes of the string to an ArrayBuffer
257
- const bufferArray = new ArrayBuffer(byteString.length);
258
-
259
- // create a view into the buffer
260
- const binary = new Uint8Array(bufferArray);
255
+ try {
256
+ const byteString = atob(dataURI.split(",")[1]);
257
+ // write the bytes of the string to an ArrayBuffer
258
+ const bufferArray = new ArrayBuffer(byteString.length);
261
259
 
262
- // set the bytes of the buffer to the correct values
263
- for (let i = 0; i < byteString.length; i++) {
264
- binary[i] = byteString.charCodeAt(i);
265
- }
260
+ // create a view into the buffer
261
+ const binary = new Uint8Array(bufferArray);
266
262
 
267
- // write the ArrayBuffer to a binary, and you're done
268
- const userLanguage = navigator.language || navigator.userLanguage;
269
- invoke("download_file_by_binary", {
270
- params: {
271
- filename,
272
- binary: Array.from(binary),
273
- language: userLanguage,
274
- },
275
- });
276
- }
263
+ // set the bytes of the buffer to the correct values
264
+ for (let i = 0; i < byteString.length; i++) {
265
+ binary[i] = byteString.charCodeAt(i);
266
+ }
277
267
 
278
- function downloadFromBlobUrl(blobUrl, filename) {
279
- convertBlobUrlToBinary(blobUrl).then((binary) => {
268
+ // write the ArrayBuffer to a binary, and you're done
280
269
  const userLanguage = navigator.language || navigator.userLanguage;
281
270
  invoke("download_file_by_binary", {
282
271
  params: {
283
272
  filename,
284
- binary,
273
+ binary: Array.from(binary),
285
274
  language: userLanguage,
286
275
  },
276
+ }).catch((error) => {
277
+ console.error("Failed to download data URI file:", filename, error);
278
+ });
279
+ } catch (error) {
280
+ console.error("Failed to process data URI:", dataURI, error);
281
+ }
282
+ }
283
+
284
+ function downloadFromBlobUrl(blobUrl, filename) {
285
+ convertBlobUrlToBinary(blobUrl)
286
+ .then((binary) => {
287
+ const userLanguage = navigator.language || navigator.userLanguage;
288
+ invoke("download_file_by_binary", {
289
+ params: {
290
+ filename,
291
+ binary,
292
+ language: userLanguage,
293
+ },
294
+ }).catch((error) => {
295
+ console.error("Failed to download blob file:", filename, error);
296
+ });
297
+ })
298
+ .catch((error) => {
299
+ console.error("Failed to convert blob to binary:", blobUrl, error);
287
300
  });
288
- });
289
301
  }
290
302
 
291
303
  // detect blob download by createElement("a")
@@ -323,8 +335,16 @@ document.addEventListener("DOMContentLoaded", () => {
323
335
  anchorElement.download || e.metaKey || e.ctrlKey || isDownloadableFile(url);
324
336
 
325
337
  const handleExternalLink = (url) => {
338
+ // Don't try to open blob: or data: URLs with shell
339
+ if (isSpecialDownload(url)) {
340
+ console.warn("Cannot open special URL with shell:", url);
341
+ return;
342
+ }
343
+
326
344
  invoke("plugin:shell|open", {
327
345
  path: url,
346
+ }).catch((error) => {
347
+ console.error("Failed to open URL with shell:", url, error);
328
348
  });
329
349
  };
330
350
 
@@ -351,6 +371,10 @@ document.addEventListener("DOMContentLoaded", () => {
351
371
  };
352
372
 
353
373
  const detectAnchorElementClick = (e) => {
374
+ // Safety check: ensure e.target exists and is an Element with closest method
375
+ if (!e.target || typeof e.target.closest !== "function") {
376
+ return;
377
+ }
354
378
  const anchorElement = e.target.closest("a");
355
379
 
356
380
  if (anchorElement && anchorElement.href) {
@@ -701,7 +725,10 @@ document.addEventListener("DOMContentLoaded", () => {
701
725
  }
702
726
 
703
727
  // Check for parent elements with background images
704
- const parentWithBg = target.closest('[style*="background-image"]');
728
+ const parentWithBg =
729
+ target && typeof target.closest === "function"
730
+ ? target.closest('[style*="background-image"]')
731
+ : null;
705
732
  if (parentWithBg) {
706
733
  const bgImage = parentWithBg.style.backgroundImage;
707
734
  const urlMatch = bgImage.match(/url\(["']?([^"')]+)["']?\)/);
@@ -770,7 +797,10 @@ document.addEventListener("DOMContentLoaded", () => {
770
797
  const mediaInfo = getMediaInfo(target);
771
798
 
772
799
  // Check for links (but not if it's media)
773
- const linkElement = target.closest("a");
800
+ const linkElement =
801
+ target && typeof target.closest === "function"
802
+ ? target.closest("a")
803
+ : null;
774
804
  const isLink = linkElement && linkElement.href && !mediaInfo.isMedia;
775
805
 
776
806
  // Only show custom menu for media or links