pake-cli 3.15.2 โ†’ 3.15.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/README.md CHANGED
@@ -197,7 +197,7 @@ Using Pake from a script or AI agent? Pass `--json` for machine-readable results
197
197
  Copy this to your AI agent to get started:
198
198
 
199
199
  ```text
200
- Use Pake (npm i -g pake-cli) to package webpages as desktop apps. Read https://raw.githubusercontent.com/tw93/Pake/main/llms.txt first; always run pake with --json and parse stdout as a single JSON object. Package <url-or-local-dist> into an app named <AppName>.
200
+ Use Pake (npm i -g pake-cli) to package webpages as desktop apps. Read https://unpkg.com/pake-cli@latest/llms.txt first; always run pake with --json and parse stdout as a single JSON object. Package <url-or-local-dist> into an app named <AppName>.
201
201
  ```
202
202
 
203
203
  ## Development
package/dist/cli.js CHANGED
@@ -20,7 +20,7 @@ import * as psl from 'psl';
20
20
  import { InvalidArgumentError, program as program$1, Option } from 'commander';
21
21
 
22
22
  var name = "pake-cli";
23
- var version = "3.15.2";
23
+ var version = "3.15.3";
24
24
  var description = "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with one command. ๐Ÿคฑ๐Ÿป ไธ€้”ฎๆ‰“ๅŒ…็ฝ‘้กต็”Ÿๆˆ่ฝป้‡ๆกŒ้ขๅบ”็”จใ€‚";
25
25
  var engines = {
26
26
  node: ">=18.0.0"
@@ -47,7 +47,8 @@ var keywords = [
47
47
  ];
48
48
  var files = [
49
49
  "LICENSE-EXCEPTION",
50
- "dist",
50
+ "llms.txt",
51
+ "dist/cli.js",
51
52
  "src-tauri"
52
53
  ];
53
54
  var scripts = {
package/llms.txt ADDED
@@ -0,0 +1,31 @@
1
+ # Pake
2
+
3
+ > Pake turns any webpage or local web build into a lightweight desktop app (~5MB) for macOS, Windows, and Linux, using Tauri (Rust) and the system webview. One CLI command packages a URL, a local HTML file, or a static directory into a native installer.
4
+
5
+ ## Install and run
6
+
7
+ - Install: `npm install -g pake-cli` (requires Node 18+ and Rust; the CLI offers to install Rust when missing)
8
+ - Basic: `pake https://example.com --name MyApp`
9
+ - Local build output: `pake ./dist --name MyTool` (directory must contain index.html at its root; hash routing only, history-mode SPA routing unsupported)
10
+
11
+ ## Agent contract
12
+
13
+ - Always pass `--json` in automation: stdout carries exactly one JSON object `{ok, name, platform, arch, outputs: [{path, sizeBytes, format}], warnings, error: {code, message, hint} | null}`; all logs go to stderr; interactive prompts are disabled.
14
+ - Exit codes: 0 success, 2 invalid input, 3 build failure, 4 missing environment or dependency setup failure, 1 unexpected.
15
+ - On Linux multi-target builds, ok:true can carry fewer outputs than requested formats; failed targets land in warnings. Check outputs[].format.
16
+ - Error codes: INVALID_INPUT, ENV_MISSING, BUILD_FAILED, UNEXPECTED, plus NETWORK (reserved; current versions report network failures under the phase code, e.g. ENV_MISSING or BUILD_FAILED). `error.hint` states how to fix.
17
+ - Declarative config: `pake --config app.json --json`; fields are camelCase CLI option names plus `url`. Explicit CLI flags win over config fields. Unknown fields fail fast. A relative `url` path resolves against the process working directory, not the config file's directory.
18
+ - Config schema: https://raw.githubusercontent.com/tw93/Pake/main/schema/pake.schema.json
19
+
20
+ ## Docs
21
+
22
+ - [CLI usage, all options](https://github.com/tw93/Pake/blob/main/docs/cli-usage.md): every flag with defaults and examples
23
+ - [Advanced usage](https://github.com/tw93/Pake/blob/main/docs/advanced-usage.md): style injection, containers, customization
24
+ - [FAQ](https://github.com/tw93/Pake/blob/main/docs/faq.md): platform-specific troubleshooting
25
+
26
+ ## Notes
27
+
28
+ - Output formats: dmg/app (macOS), msi (Windows), deb/AppImage/rpm/zst (Linux); pick with `--targets`.
29
+ - Icons auto-fetch from the site favicon when `--icon` is omitted.
30
+ - `--proxy-url` is baked into the app webview at build time; it does not proxy the icon download itself.
31
+ - Google OAuth and similar providers may reject embedded webviews regardless of flags; `--new-window` and `--safe-domain` help with most SSO flows.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pake-cli",
3
- "version": "3.15.2",
3
+ "version": "3.15.3",
4
4
  "description": "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with one command. ๐Ÿคฑ๐Ÿป ไธ€้”ฎๆ‰“ๅŒ…็ฝ‘้กต็”Ÿๆˆ่ฝป้‡ๆกŒ้ขๅบ”็”จใ€‚",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -27,7 +27,8 @@
27
27
  ],
28
28
  "files": [
29
29
  "LICENSE-EXCEPTION",
30
- "dist",
30
+ "llms.txt",
31
+ "dist/cli.js",
31
32
  "src-tauri"
32
33
  ],
33
34
  "scripts": {
@@ -2564,7 +2564,7 @@ dependencies = [
2564
2564
 
2565
2565
  [[package]]
2566
2566
  name = "pake"
2567
- version = "3.15.2"
2567
+ version = "3.15.3"
2568
2568
  dependencies = [
2569
2569
  "objc2",
2570
2570
  "objc2-app-kit",
@@ -2582,6 +2582,7 @@ dependencies = [
2582
2582
  "tauri-plugin-single-instance",
2583
2583
  "tauri-plugin-window-state",
2584
2584
  "tokio",
2585
+ "windows-sys 0.61.2",
2585
2586
  ]
2586
2587
 
2587
2588
  [[package]]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "pake"
3
- version = "3.15.2"
3
+ version = "3.15.3"
4
4
  description = "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with Rust."
5
5
  authors = ["Tw93"]
6
6
  license = "GPL-3.0-or-later"
@@ -41,6 +41,13 @@ objc2 = "0.6"
41
41
  objc2-app-kit = { version = "0.3", features = ["NSApplication", "NSDockTile"] }
42
42
  objc2-foundation = { version = "0.3", features = ["NSString"] }
43
43
 
44
+ [target.'cfg(windows)'.dependencies]
45
+ windows-sys = { version = "0.61.2", features = [
46
+ "Win32_Foundation",
47
+ "Win32_UI_Shell",
48
+ "Win32_UI_WindowsAndMessaging",
49
+ ] }
50
+
44
51
  [features]
45
52
  # this feature is used for development builds from development cli
46
53
  cli-build = []
@@ -3,6 +3,8 @@ use crate::util::{
3
3
  check_file_or_append, get_data_dir, get_download_message_with_lang, sanitize_download_filename,
4
4
  show_toast, MessageType,
5
5
  };
6
+ #[cfg(target_os = "windows")]
7
+ use std::{os::windows::ffi::OsStrExt, ptr, sync::OnceLock};
6
8
  use std::{
7
9
  path::PathBuf,
8
10
  str::FromStr,
@@ -13,6 +15,12 @@ use tauri::{
13
15
  AppHandle, Config, Manager, Url, WebviewUrl, WebviewWindow, WebviewWindowBuilder,
14
16
  };
15
17
 
18
+ #[cfg(target_os = "windows")]
19
+ use windows_sys::Win32::UI::{
20
+ Shell::ExtractIconExW,
21
+ WindowsAndMessaging::{SendMessageW, ICON_BIG, WM_SETICON},
22
+ };
23
+
16
24
  use tauri::Theme;
17
25
 
18
26
  #[cfg(target_os = "macos")]
@@ -69,14 +77,70 @@ pub fn open_additional_window(app: &AppHandle) -> tauri::Result<WebviewWindow> {
69
77
  build_window_with_label(app, &state.pake_config, &state.tauri_config, &label)
70
78
  }
71
79
 
72
- // Apps autostarted at Windows logon can register their window icon before
73
- // Explorer's icon cache is ready, leaving a blank taskbar icon until the icon
74
- // is asserted again (#1323), so re-apply it whenever a window becomes visible.
80
+ #[cfg(target_os = "windows")]
81
+ fn taskbar_icon_handle() -> Option<isize> {
82
+ static TASKBAR_ICON: OnceLock<Option<isize>> = OnceLock::new();
83
+
84
+ *TASKBAR_ICON.get_or_init(|| {
85
+ let executable = match std::env::current_exe() {
86
+ Ok(path) => path,
87
+ Err(error) => {
88
+ eprintln!(
89
+ "[Pake] Failed to resolve the app executable for its taskbar icon: {error}"
90
+ );
91
+ return None;
92
+ }
93
+ };
94
+ let executable_wide: Vec<u16> = executable
95
+ .as_os_str()
96
+ .encode_wide()
97
+ .chain(std::iter::once(0))
98
+ .collect();
99
+ let mut large_icon = ptr::null_mut();
100
+ let extracted = unsafe {
101
+ ExtractIconExW(
102
+ executable_wide.as_ptr(),
103
+ 0,
104
+ &mut large_icon,
105
+ ptr::null_mut(),
106
+ 1,
107
+ )
108
+ };
109
+ if extracted == 0 || large_icon.is_null() {
110
+ eprintln!(
111
+ "[Pake] Failed to extract the taskbar icon from {}.",
112
+ executable.display()
113
+ );
114
+ return None;
115
+ }
116
+
117
+ // WM_SETICON keeps this handle rather than copying it. Cache the single
118
+ // extracted icon for the process lifetime so repeated tray restores do
119
+ // not leak a new HICON or leave the window with a dangling handle.
120
+ Some(large_icon as isize)
121
+ })
122
+ }
123
+
124
+ // Apps autostarted at Windows logon can register their icons before Explorer's
125
+ // icon cache is ready (#1323). Re-assert both the small/title-bar icon and the
126
+ // large taskbar icon whenever a window becomes visible.
75
127
  #[cfg(target_os = "windows")]
76
128
  pub fn reapply_window_icon(window: &WebviewWindow) {
77
129
  if let Some(icon) = window.app_handle().default_window_icon().cloned() {
78
130
  if let Err(error) = window.set_icon(icon) {
79
- eprintln!("[Pake] Failed to re-apply window icon: {error}");
131
+ eprintln!("[Pake] Failed to re-apply the window icon: {error}");
132
+ }
133
+ }
134
+
135
+ let Some(taskbar_icon) = taskbar_icon_handle() else {
136
+ return;
137
+ };
138
+ match window.hwnd() {
139
+ Ok(hwnd) => unsafe {
140
+ SendMessageW(hwnd.0, WM_SETICON, ICON_BIG as usize, taskbar_icon);
141
+ },
142
+ Err(error) => {
143
+ eprintln!("[Pake] Failed to resolve the window handle for its taskbar icon: {error}");
80
144
  }
81
145
  }
82
146
  }
@@ -114,6 +178,7 @@ fn open_requested_window(
114
178
 
115
179
  let title = target_url.host_str().unwrap_or(target_url.as_str());
116
180
  let _ = window.set_title(title);
181
+ reapply_window_icon(&window);
117
182
  let _ = window.set_focus();
118
183
 
119
184
  Ok(window)
@@ -126,6 +191,7 @@ pub fn open_additional_window_safe(app: &AppHandle) {
126
191
  std::thread::spawn(move || {
127
192
  if let Ok(window) = open_additional_window(&app_handle) {
128
193
  let _ = window.show();
194
+ reapply_window_icon(&window);
129
195
  let _ = window.set_focus();
130
196
  }
131
197
  });
@@ -135,6 +201,7 @@ pub fn open_additional_window_safe(app: &AppHandle) {
135
201
  {
136
202
  if let Ok(window) = open_additional_window(app) {
137
203
  let _ = window.show();
204
+ reapply_window_icon(&window);
138
205
  let _ = window.set_focus();
139
206
  }
140
207
  }
@@ -302,6 +302,7 @@ pub fn run_app() {
302
302
  if !has_visible_windows {
303
303
  if let Some(window) = _app.get_webview_window("pake") {
304
304
  let _ = window.show();
305
+ reapply_window_icon(&window);
305
306
  let _ = window.set_focus();
306
307
  }
307
308
  }
@@ -183,16 +183,21 @@ mod tests {
183
183
  use std::env;
184
184
  use std::fs;
185
185
  use std::path::PathBuf;
186
+ use std::sync::atomic::{AtomicU64, Ordering};
187
+
188
+ static TEMP_PATH_COUNTER: AtomicU64 = AtomicU64::new(0);
186
189
 
187
190
  fn temp_path(name: &str) -> PathBuf {
188
191
  let mut dir = env::temp_dir();
192
+ let unique = TEMP_PATH_COUNTER.fetch_add(1, Ordering::Relaxed);
189
193
  dir.push(format!(
190
- "pake-util-test-{}-{}",
194
+ "pake-util-test-{}-{}-{}",
191
195
  std::process::id(),
192
196
  std::time::SystemTime::now()
193
197
  .duration_since(std::time::UNIX_EPOCH)
194
198
  .map(|d| d.as_nanos())
195
- .unwrap_or(0)
199
+ .unwrap_or(0),
200
+ unique
196
201
  ));
197
202
  fs::create_dir_all(&dir).unwrap();
198
203
  dir.push(name);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "productName": "Weekly",
3
3
  "identifier": "com.pake.weekly",
4
- "version": "3.15.2",
4
+ "version": "3.15.3",
5
5
  "app": {
6
6
  "withGlobalTauri": true,
7
7
  "trayIcon": {