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 +1 -1
- package/dist/cli.js +3 -2
- package/llms.txt +31 -0
- package/package.json +3 -2
- package/src-tauri/Cargo.lock +2 -1
- package/src-tauri/Cargo.toml +8 -1
- package/src-tauri/src/app/window.rs +71 -4
- package/src-tauri/src/lib.rs +1 -0
- package/src-tauri/src/util.rs +7 -2
- package/src-tauri/tauri.conf.json +1 -1
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://
|
|
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.
|
|
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
|
-
"
|
|
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.
|
|
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
|
-
"
|
|
30
|
+
"llms.txt",
|
|
31
|
+
"dist/cli.js",
|
|
31
32
|
"src-tauri"
|
|
32
33
|
],
|
|
33
34
|
"scripts": {
|
package/src-tauri/Cargo.lock
CHANGED
|
@@ -2564,7 +2564,7 @@ dependencies = [
|
|
|
2564
2564
|
|
|
2565
2565
|
[[package]]
|
|
2566
2566
|
name = "pake"
|
|
2567
|
-
version = "3.15.
|
|
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]]
|
package/src-tauri/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "pake"
|
|
3
|
-
version = "3.15.
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
}
|
package/src-tauri/src/lib.rs
CHANGED
package/src-tauri/src/util.rs
CHANGED
|
@@ -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);
|