pake-cli 3.12.1 โ†’ 3.13.0

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
@@ -10,17 +10,17 @@ import os from 'os';
10
10
  import { execa, execaSync } from 'execa';
11
11
  import crypto from 'crypto';
12
12
  import ora from 'ora';
13
- import fs from 'fs/promises';
13
+ import fs from 'fs';
14
+ import fs$1 from 'fs/promises';
14
15
  import { dir } from 'tmp-promise';
15
16
  import { fileTypeFromBuffer } from 'file-type';
16
17
  import icongen from 'icon-gen';
17
18
  import sharp from 'sharp';
18
19
  import * as psl from 'psl';
19
20
  import { InvalidArgumentError, program as program$1, Option } from 'commander';
20
- import fs$1 from 'fs';
21
21
 
22
22
  var name = "pake-cli";
23
- var version = "3.12.1";
23
+ var version = "3.13.0";
24
24
  var description = "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with one command. ๐Ÿคฑ๐Ÿป ไธ€้”ฎๆ‰“ๅŒ…็ฝ‘้กต็”Ÿๆˆ่ฝป้‡ๆกŒ้ขๅบ”็”จใ€‚";
25
25
  var engines = {
26
26
  node: ">=18.0.0"
@@ -230,6 +230,93 @@ const { platform: platform$1 } = process;
230
230
  const IS_MAC = platform$1 === 'darwin';
231
231
  const IS_WIN = platform$1 === 'win32';
232
232
  const IS_LINUX = platform$1 === 'linux';
233
+ // Distro IDs / ID_LIKE families that ship an RPM-based package manager.
234
+ const RPM_FAMILY_IDS = new Set([
235
+ 'rhel',
236
+ 'fedora',
237
+ 'centos',
238
+ 'rocky',
239
+ 'almalinux',
240
+ 'ol', // Oracle Linux
241
+ 'oracle',
242
+ 'amzn', // Amazon Linux
243
+ 'mariner',
244
+ 'azurelinux',
245
+ 'suse',
246
+ 'opensuse',
247
+ 'opensuse-leap',
248
+ 'opensuse-tumbleweed',
249
+ 'sles',
250
+ ]);
251
+ // Distro IDs / ID_LIKE families that ship a DEB-based package manager.
252
+ const DEB_FAMILY_IDS = new Set([
253
+ 'debian',
254
+ 'ubuntu',
255
+ 'linuxmint',
256
+ 'pop',
257
+ 'elementary',
258
+ 'kali',
259
+ 'raspbian',
260
+ 'devuan',
261
+ ]);
262
+ // Parse the shell-style key=value pairs of an /etc/os-release file, stripping
263
+ // the optional surrounding quotes around values.
264
+ function parseOsRelease(content) {
265
+ const fields = {};
266
+ for (const rawLine of content.split('\n')) {
267
+ const line = rawLine.trim();
268
+ if (!line || line.startsWith('#'))
269
+ continue;
270
+ const separator = line.indexOf('=');
271
+ if (separator === -1)
272
+ continue;
273
+ const key = line.slice(0, separator).trim();
274
+ let value = line.slice(separator + 1).trim();
275
+ if (value.length >= 2 &&
276
+ ((value.startsWith('"') && value.endsWith('"')) ||
277
+ (value.startsWith("'") && value.endsWith("'")))) {
278
+ value = value.slice(1, -1);
279
+ }
280
+ if (key)
281
+ fields[key] = value;
282
+ }
283
+ return fields;
284
+ }
285
+ // Detect the package family from /etc/os-release. The distro's own ID wins over
286
+ // ID_LIKE hints, and an unknown distro falls back to 'deb' to preserve Pake's
287
+ // historical default. Accepts content directly so the decision is unit-testable
288
+ // without a real /etc/os-release.
289
+ function detectLinuxPackageFamily(osReleaseContent) {
290
+ let content = osReleaseContent;
291
+ if (content === undefined) {
292
+ try {
293
+ content = fs.readFileSync('/etc/os-release', 'utf-8');
294
+ }
295
+ catch {
296
+ return 'deb';
297
+ }
298
+ }
299
+ const fields = parseOsRelease(content);
300
+ const id = (fields.ID ?? '').toLowerCase().trim();
301
+ const idLike = (fields.ID_LIKE ?? '')
302
+ .toLowerCase()
303
+ .split(/\s+/)
304
+ .filter(Boolean);
305
+ for (const token of [id, ...idLike]) {
306
+ if (DEB_FAMILY_IDS.has(token))
307
+ return 'deb';
308
+ if (RPM_FAMILY_IDS.has(token))
309
+ return 'rpm';
310
+ }
311
+ return 'deb';
312
+ }
313
+ // Default Linux bundle targets, chosen by the host distro's package family so
314
+ // RPM-based distros (Fedora/RHEL/Oracle/Rocky/Alma/openSUSE) get a native .rpm
315
+ // instead of a .deb their package manager cannot install. AppImage stays as a
316
+ // universal fallback in both cases.
317
+ function getDefaultLinuxTargets() {
318
+ return detectLinuxPackageFamily() === 'rpm' ? 'rpm,appimage' : 'deb,appimage';
319
+ }
233
320
 
234
321
  async function shellExec(command, timeout = 300000, env) {
235
322
  try {
@@ -339,7 +426,7 @@ function checkRustInstalled() {
339
426
  async function combineFiles(files, output) {
340
427
  const contents = await Promise.all(files.map(async (file) => {
341
428
  if (file.endsWith('.css')) {
342
- const fileContent = await fs.readFile(file, 'utf-8');
429
+ const fileContent = await fs$1.readFile(file, 'utf-8');
343
430
  return `window.addEventListener('DOMContentLoaded', (_event) => {
344
431
  const css = ${JSON.stringify(fileContent)};
345
432
  const style = document.createElement('style');
@@ -347,12 +434,12 @@ async function combineFiles(files, output) {
347
434
  document.head.appendChild(style);
348
435
  });`;
349
436
  }
350
- const fileContent = await fs.readFile(file);
437
+ const fileContent = await fs$1.readFile(file);
351
438
  return ("window.addEventListener('DOMContentLoaded', (_event) => { " +
352
439
  fileContent +
353
440
  ' });');
354
441
  }));
355
- await fs.writeFile(output, contents.join('\n'));
442
+ await fs$1.writeFile(output, contents.join('\n'));
356
443
  return files;
357
444
  }
358
445
 
@@ -1444,20 +1531,47 @@ class LinuxBuilder extends BaseBuilder {
1444
1531
  throw new Error(`No valid Linux target in "${this.options.targets}". Valid targets: ${LINUX_TARGET_TYPES.join(', ')}.`);
1445
1532
  }
1446
1533
  const useTemporaryDebForZst = needsTemporaryDebForZst(targets);
1534
+ // With a single explicit target, fail fast. With multiple targets (the
1535
+ // distro-aware default, or an explicit comma list) keep building the rest
1536
+ // when one fails, so a usable installer is still produced, e.g. AppImage
1537
+ // survives a .deb bundler abort on RPM-based distros.
1538
+ const isolateFailures = targets.length > 1;
1539
+ const failed = [];
1540
+ let firstError = null;
1447
1541
  for (const target of targets) {
1448
1542
  this.currentBuildType = target;
1449
- if (target === 'zst') {
1450
- if (useTemporaryDebForZst) {
1451
- await this.buildAndCopy(url, 'deb', false);
1543
+ try {
1544
+ if (target === 'zst') {
1545
+ if (useTemporaryDebForZst) {
1546
+ await this.buildAndCopy(url, 'deb', false);
1547
+ }
1548
+ await this.createArchPackageFromDeb({
1549
+ removeSourceDeb: useTemporaryDebForZst,
1550
+ });
1551
+ }
1552
+ else {
1553
+ await this.buildAndCopy(url, target);
1452
1554
  }
1453
- await this.createArchPackageFromDeb({
1454
- removeSourceDeb: useTemporaryDebForZst,
1455
- });
1456
1555
  }
1457
- else {
1458
- await this.buildAndCopy(url, target);
1556
+ catch (error) {
1557
+ const err = error instanceof Error ? error : new Error(String(error));
1558
+ if (!isolateFailures) {
1559
+ throw err;
1560
+ }
1561
+ if (!firstError) {
1562
+ firstError = err;
1563
+ }
1564
+ failed.push(target);
1565
+ logger.warn(`โœผ Failed to build "${target}" target: ${err.message.split('\n')[0]}`);
1459
1566
  }
1460
1567
  }
1568
+ // Every requested target failed: surface the first real error.
1569
+ if (firstError && failed.length === targets.length) {
1570
+ throw firstError;
1571
+ }
1572
+ if (failed.length > 0) {
1573
+ logger.warn(`โœผ Skipped failed Linux targets: ${failed.join(', ')}. Other formats built successfully.`);
1574
+ }
1461
1575
  }
1462
1576
  async ensureArchPackagingTools() {
1463
1577
  const requiredTools = [
@@ -2569,7 +2683,7 @@ const DEFAULT_PAKE_OPTIONS = {
2569
2683
  targets: (() => {
2570
2684
  switch (process.platform) {
2571
2685
  case 'linux':
2572
- return 'deb,appimage';
2686
+ return getDefaultLinuxTargets();
2573
2687
  case 'darwin':
2574
2688
  return 'dmg';
2575
2689
  case 'win32':
@@ -2621,7 +2735,7 @@ function validateNumberInput(value) {
2621
2735
  return parsedValue;
2622
2736
  }
2623
2737
  function validateUrlInput(url) {
2624
- const isFile = fs$1.existsSync(url);
2738
+ const isFile = fs.existsSync(url);
2625
2739
  if (!isFile) {
2626
2740
  try {
2627
2741
  return normalizeUrl(url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pake-cli",
3
- "version": "3.12.1",
3
+ "version": "3.13.0",
4
4
  "description": "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with one command. ๐Ÿคฑ๐Ÿป ไธ€้”ฎๆ‰“ๅŒ…็ฝ‘้กต็”Ÿๆˆ่ฝป้‡ๆกŒ้ขๅบ”็”จใ€‚",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -2564,7 +2564,7 @@ dependencies = [
2564
2564
 
2565
2565
  [[package]]
2566
2566
  name = "pake"
2567
- version = "3.12.1"
2567
+ version = "3.13.0"
2568
2568
  dependencies = [
2569
2569
  "objc2",
2570
2570
  "objc2-app-kit",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "pake"
3
- version = "3.12.1"
3
+ version = "3.13.0"
4
4
  description = "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with Rust."
5
5
  authors = ["Tw93"]
6
6
  license = "GPL-3.0-or-later"
@@ -191,3 +191,15 @@ pub async fn update_theme_mode(app: AppHandle, mode: String) {
191
191
  let _ = window.set_theme(Some(theme));
192
192
  }
193
193
  }
194
+
195
+ // Apply native WebView zoom (WKWebView pageZoom / WebView2 ZoomFactor / WebKitGTK
196
+ // zoom level) instead of CSS hacks. CSS `transform: scale` and `html.style.zoom`
197
+ // break complex SPAs like ChatGPT (fixed positioning shifts, unrepainted layers);
198
+ // native zoom recalculates layout the same way a browser does for Cmd/Ctrl +/-.
199
+ #[command]
200
+ pub fn set_zoom(window: WebviewWindow, percent: f64) -> Result<(), String> {
201
+ let factor = (percent / 100.0).clamp(0.3, 2.0);
202
+ window
203
+ .set_zoom(factor)
204
+ .map_err(|e| format!("Failed to set zoom: {}", e))
205
+ }
@@ -61,7 +61,12 @@ pub fn set_system_tray(
61
61
  }
62
62
  }
63
63
  "quit" => {
64
- let _ = app.save_window_state(StateFlags::all());
64
+ let flags = if _init_fullscreen {
65
+ StateFlags::all()
66
+ } else {
67
+ StateFlags::all() & !StateFlags::FULLSCREEN
68
+ };
69
+ let _ = app.save_window_state(flags);
65
70
  app.exit(0);
66
71
  }
67
72
  _ => (),
@@ -11,19 +11,13 @@ const shortcuts = {
11
11
  };
12
12
 
13
13
  function setZoom(zoom) {
14
- const html = document.getElementsByTagName("html")[0];
15
- const body = document.body;
16
- const zoomValue = parseFloat(zoom) / 100;
17
- const isWindows = /windows/i.test(navigator.userAgent);
18
-
19
- if (isWindows) {
20
- body.style.transform = `scale(${zoomValue})`;
21
- body.style.transformOrigin = "top left";
22
- body.style.width = `${100 / zoomValue}%`;
23
- body.style.height = `${100 / zoomValue}%`;
24
- } else {
25
- html.style.zoom = zoom;
26
- window.dispatchEvent(new Event("resize"));
14
+ // Use native WebView zoom (WKWebView pageZoom / WebView2 ZoomFactor) instead of
15
+ // CSS hacks. `transform: scale` and `html.style.zoom` break complex SPAs like
16
+ // ChatGPT: the page shifts right on Windows and parts of the UI stop repainting
17
+ // on macOS. Native zoom recalculates layout exactly like a browser does.
18
+ const invoke = window.__TAURI__?.core?.invoke;
19
+ if (invoke) {
20
+ invoke("set_zoom", { percent: parseFloat(zoom) }).catch(() => {});
27
21
  }
28
22
 
29
23
  window.localStorage.setItem("htmlZoom", zoom);
@@ -22,7 +22,7 @@ const GDK_BACKEND: &str = "GDK_BACKEND";
22
22
  use app::{
23
23
  invoke::{
24
24
  clear_dock_badge, download_file, increment_dock_badge, send_notification, set_dock_badge,
25
- set_dock_badge_label, update_theme_mode,
25
+ set_dock_badge_label, set_zoom, update_theme_mode,
26
26
  },
27
27
  setup::{set_global_shortcut, set_system_tray},
28
28
  window::{open_additional_window_safe, set_window, MultiWindowState},
@@ -155,7 +155,9 @@ pub fn run_app() {
155
155
  StateFlags::FULLSCREEN
156
156
  } else {
157
157
  // Prevent flickering on the first open.
158
- StateFlags::all() & !StateFlags::VISIBLE
158
+ // Exclude FULLSCREEN so a prior --fullscreen build's persisted state
159
+ // doesn't force fullscreen on a rebuild without --fullscreen.
160
+ StateFlags::all() & !StateFlags::VISIBLE & !StateFlags::FULLSCREEN
159
161
  })
160
162
  .build();
161
163
 
@@ -192,6 +194,7 @@ pub fn run_app() {
192
194
  set_dock_badge_label,
193
195
  clear_dock_badge,
194
196
  update_theme_mode,
197
+ set_zoom,
195
198
  ])
196
199
  .setup(move |app| {
197
200
  app.manage(MultiWindowState::new(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "productName": "Weekly",
3
3
  "identifier": "com.pake.weekly",
4
- "version": "3.12.1",
4
+ "version": "3.13.0",
5
5
  "app": {
6
6
  "withGlobalTauri": true,
7
7
  "trayIcon": {