Merge branch 'dev'
This commit is contained in:
@@ -57,12 +57,14 @@ pnpm run dev # Development with hot reload
|
|||||||
- **CLI Tool** (`bin/`): Main entry point, builders, options processing
|
- **CLI Tool** (`bin/`): Main entry point, builders, options processing
|
||||||
- **Tauri App** (`src-tauri/`): Rust application, window/tray management, injection logic
|
- **Tauri App** (`src-tauri/`): Rust application, window/tray management, injection logic
|
||||||
- **Config Files**: `pake.json`, `tauri.conf.json`, platform-specific configs
|
- **Config Files**: `pake.json`, `tauri.conf.json`, platform-specific configs
|
||||||
|
- **Injection System** (`src-tauri/src/inject/event.js`): Custom event handlers, shortcuts, downloads, notifications
|
||||||
|
|
||||||
## Documentation Guidelines
|
## Documentation Guidelines
|
||||||
|
|
||||||
- **Main README**: Common parameters only
|
- **Main README**: Common parameters only
|
||||||
- **CLI Documentation** (`docs/cli-usage.md`): ALL parameters with examples
|
- **CLI Documentation** (`docs/cli-usage.md`): ALL parameters with examples
|
||||||
- **Rare parameters**: Full docs in CLI usage, minimal in main README
|
- **Rare parameters**: Full docs in CLI usage, minimal in main README
|
||||||
|
- **NO technical documentation files**: Do not create separate technical docs, design docs, or implementation notes - keep technical details in memory/conversation only
|
||||||
|
|
||||||
## Platform Specifics
|
## Platform Specifics
|
||||||
|
|
||||||
|
|||||||
23
bin/builders/BaseBuilder.ts
vendored
23
bin/builders/BaseBuilder.ts
vendored
@@ -123,14 +123,12 @@ export default abstract class BaseBuilder {
|
|||||||
`cd "${npmDirectory}" && ${packageManager} install${registryOption}${peerDepsOption}`,
|
`cd "${npmDirectory}" && ${packageManager} install${registryOption}${peerDepsOption}`,
|
||||||
timeout,
|
timeout,
|
||||||
buildEnv,
|
buildEnv,
|
||||||
this.options.debug,
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await shellExec(
|
await shellExec(
|
||||||
`cd "${npmDirectory}" && ${packageManager} install${peerDepsOption}`,
|
`cd "${npmDirectory}" && ${packageManager} install${peerDepsOption}`,
|
||||||
timeout,
|
timeout,
|
||||||
buildEnv,
|
buildEnv,
|
||||||
this.options.debug,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
spinner.succeed(chalk.green('Package installed!'));
|
spinner.succeed(chalk.green('Package installed!'));
|
||||||
@@ -169,11 +167,24 @@ export default abstract class BaseBuilder {
|
|||||||
...(process.env.NO_STRIP && { NO_STRIP: process.env.NO_STRIP }),
|
...(process.env.NO_STRIP && { NO_STRIP: process.env.NO_STRIP }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Warn users about potential AppImage build failures on modern Linux systems.
|
||||||
|
// The linuxdeploy tool bundled in Tauri uses an older strip tool that doesn't
|
||||||
|
// recognize the .relr.dyn section introduced in glibc 2.38+.
|
||||||
|
if (process.platform === 'linux' && this.options.targets === 'appimage') {
|
||||||
|
if (!buildEnv.NO_STRIP) {
|
||||||
|
logger.warn(
|
||||||
|
'⚠ Building AppImage on Linux may fail due to strip incompatibility with glibc 2.38+',
|
||||||
|
);
|
||||||
|
logger.warn(
|
||||||
|
'⚠ If build fails, retry with: NO_STRIP=1 pake <url> --targets appimage',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await shellExec(
|
await shellExec(
|
||||||
`cd "${npmDirectory}" && ${this.getBuildCommand(packageManager)}`,
|
`cd "${npmDirectory}" && ${this.getBuildCommand(packageManager)}`,
|
||||||
this.getBuildTimeout(),
|
this.getBuildTimeout(),
|
||||||
buildEnv,
|
buildEnv,
|
||||||
this.options.debug,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Copy app
|
// Copy app
|
||||||
@@ -280,6 +291,12 @@ export default abstract class BaseBuilder {
|
|||||||
fullCommand += ` --target ${target}`;
|
fullCommand += ` --target ${target}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable verbose output in debug mode to help diagnose build issues.
|
||||||
|
// This provides detailed logs from Tauri CLI and bundler tools.
|
||||||
|
if (this.options.debug) {
|
||||||
|
fullCommand += ' --verbose';
|
||||||
|
}
|
||||||
|
|
||||||
return fullCommand;
|
return fullCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
bin/builders/LinuxBuilder.ts
vendored
7
bin/builders/LinuxBuilder.ts
vendored
@@ -78,6 +78,13 @@ export default class LinuxBuilder extends BaseBuilder {
|
|||||||
fullCommand += ` --features ${features.join(',')}`;
|
fullCommand += ` --features ${features.join(',')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable verbose output for AppImage builds when debugging or PAKE_VERBOSE is set.
|
||||||
|
// AppImage builds often fail with minimal error messages from linuxdeploy,
|
||||||
|
// so verbose mode helps diagnose issues like strip failures and missing dependencies.
|
||||||
|
if (this.options.targets === 'appimage' && (this.options.debug || process.env.PAKE_VERBOSE)) {
|
||||||
|
fullCommand += ' --verbose';
|
||||||
|
}
|
||||||
|
|
||||||
return fullCommand;
|
return fullCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
bin/cli.ts
vendored
10
bin/cli.ts
vendored
@@ -99,6 +99,11 @@ program
|
|||||||
.default(DEFAULT.alwaysOnTop)
|
.default(DEFAULT.alwaysOnTop)
|
||||||
.hideHelp(),
|
.hideHelp(),
|
||||||
)
|
)
|
||||||
|
.addOption(
|
||||||
|
new Option('--maximize', 'Start window maximized')
|
||||||
|
.default(DEFAULT.maximize)
|
||||||
|
.hideHelp(),
|
||||||
|
)
|
||||||
.addOption(
|
.addOption(
|
||||||
new Option('--dark-mode', 'Force Mac app to use dark mode')
|
new Option('--dark-mode', 'Force Mac app to use dark mode')
|
||||||
.default(DEFAULT.darkMode)
|
.default(DEFAULT.darkMode)
|
||||||
@@ -164,6 +169,11 @@ program
|
|||||||
.default(DEFAULT.multiInstance)
|
.default(DEFAULT.multiInstance)
|
||||||
.hideHelp(),
|
.hideHelp(),
|
||||||
)
|
)
|
||||||
|
.addOption(
|
||||||
|
new Option('--start-to-tray', 'Start app minimized to tray')
|
||||||
|
.default(DEFAULT.startToTray)
|
||||||
|
.hideHelp(),
|
||||||
|
)
|
||||||
.addOption(
|
.addOption(
|
||||||
new Option('--installer-language <string>', 'Installer language')
|
new Option('--installer-language <string>', 'Installer language')
|
||||||
.default(DEFAULT.installerLanguage)
|
.default(DEFAULT.installerLanguage)
|
||||||
|
|||||||
2
bin/defaults.ts
vendored
2
bin/defaults.ts
vendored
@@ -5,6 +5,7 @@ export const DEFAULT_PAKE_OPTIONS: PakeCliOptions = {
|
|||||||
height: 780,
|
height: 780,
|
||||||
width: 1200,
|
width: 1200,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
|
maximize: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
hideTitleBar: false,
|
hideTitleBar: false,
|
||||||
alwaysOnTop: false,
|
alwaysOnTop: false,
|
||||||
@@ -28,6 +29,7 @@ export const DEFAULT_PAKE_OPTIONS: PakeCliOptions = {
|
|||||||
enableDragDrop: false,
|
enableDragDrop: false,
|
||||||
keepBinary: false,
|
keepBinary: false,
|
||||||
multiInstance: false,
|
multiInstance: false,
|
||||||
|
startToTray: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Just for cli development
|
// Just for cli development
|
||||||
|
|||||||
4
bin/helpers/merge.ts
vendored
4
bin/helpers/merge.ts
vendored
@@ -49,6 +49,7 @@ export async function mergeConfig(
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fullscreen,
|
fullscreen,
|
||||||
|
maximize,
|
||||||
hideTitleBar,
|
hideTitleBar,
|
||||||
alwaysOnTop,
|
alwaysOnTop,
|
||||||
appVersion,
|
appVersion,
|
||||||
@@ -71,6 +72,7 @@ export async function mergeConfig(
|
|||||||
wasm,
|
wasm,
|
||||||
enableDragDrop,
|
enableDragDrop,
|
||||||
multiInstance,
|
multiInstance,
|
||||||
|
startToTray,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const { platform } = process;
|
const { platform } = process;
|
||||||
@@ -81,6 +83,7 @@ export async function mergeConfig(
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fullscreen,
|
fullscreen,
|
||||||
|
maximize,
|
||||||
resizable,
|
resizable,
|
||||||
hide_title_bar: hideTitleBar,
|
hide_title_bar: hideTitleBar,
|
||||||
activation_shortcut: activationShortcut,
|
activation_shortcut: activationShortcut,
|
||||||
@@ -92,6 +95,7 @@ export async function mergeConfig(
|
|||||||
title: title || null,
|
title: title || null,
|
||||||
enable_wasm: wasm,
|
enable_wasm: wasm,
|
||||||
enable_drag_drop: enableDragDrop,
|
enable_drag_drop: enableDragDrop,
|
||||||
|
start_to_tray: startToTray && showSystemTray,
|
||||||
};
|
};
|
||||||
Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions });
|
Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions });
|
||||||
|
|
||||||
|
|||||||
1
bin/helpers/rust.ts
vendored
1
bin/helpers/rust.ts
vendored
@@ -83,7 +83,6 @@ export async function installRust() {
|
|||||||
IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac,
|
IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac,
|
||||||
300000,
|
300000,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
spinner.succeed(chalk.green('✔ Rust installed successfully!'));
|
spinner.succeed(chalk.green('✔ Rust installed successfully!'));
|
||||||
ensureRustEnv();
|
ensureRustEnv();
|
||||||
|
|||||||
6
bin/types.ts
vendored
6
bin/types.ts
vendored
@@ -24,6 +24,9 @@ export interface PakeCliOptions {
|
|||||||
// Whether the window can be fullscreen, default false
|
// Whether the window can be fullscreen, default false
|
||||||
fullscreen: boolean;
|
fullscreen: boolean;
|
||||||
|
|
||||||
|
// Start window maximized, default false
|
||||||
|
maximize: boolean;
|
||||||
|
|
||||||
// Enable immersive header, default false.
|
// Enable immersive header, default false.
|
||||||
hideTitleBar: boolean;
|
hideTitleBar: boolean;
|
||||||
|
|
||||||
@@ -90,6 +93,9 @@ export interface PakeCliOptions {
|
|||||||
|
|
||||||
// Allow multiple instances, default false (single instance)
|
// Allow multiple instances, default false (single instance)
|
||||||
multiInstance: boolean;
|
multiInstance: boolean;
|
||||||
|
|
||||||
|
// Start app minimized to tray, default false
|
||||||
|
startToTray: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PakeAppOptions extends PakeCliOptions {
|
export interface PakeAppOptions extends PakeCliOptions {
|
||||||
|
|||||||
23
bin/utils/shell.ts
vendored
23
bin/utils/shell.ts
vendored
@@ -5,12 +5,13 @@ export async function shellExec(
|
|||||||
command: string,
|
command: string,
|
||||||
timeout: number = 300000,
|
timeout: number = 300000,
|
||||||
env?: Record<string, string>,
|
env?: Record<string, string>,
|
||||||
showOutput: boolean = false,
|
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const { exitCode } = await execa(command, {
|
const { exitCode } = await execa(command, {
|
||||||
cwd: npmDirectory,
|
cwd: npmDirectory,
|
||||||
stdio: showOutput ? 'inherit' : ['inherit', 'pipe', 'inherit'],
|
// Use 'inherit' to show all output directly to user in real-time.
|
||||||
|
// This ensures linuxdeploy and other tool outputs are visible during builds.
|
||||||
|
stdio: 'inherit',
|
||||||
shell: true,
|
shell: true,
|
||||||
timeout,
|
timeout,
|
||||||
env: env ? { ...process.env, ...env } : process.env,
|
env: env ? { ...process.env, ...env } : process.env,
|
||||||
@@ -28,6 +29,8 @@ export async function shellExec(
|
|||||||
|
|
||||||
let errorMsg = `Error occurred while executing command "${command}". Exit code: ${exitCode}. Details: ${errorMessage}`;
|
let errorMsg = `Error occurred while executing command "${command}". Exit code: ${exitCode}. Details: ${errorMessage}`;
|
||||||
|
|
||||||
|
// Provide helpful guidance for common Linux AppImage build failures
|
||||||
|
// caused by strip tool incompatibility with modern glibc (2.38+)
|
||||||
if (
|
if (
|
||||||
process.platform === 'linux' &&
|
process.platform === 'linux' &&
|
||||||
(errorMessage.includes('linuxdeploy') ||
|
(errorMessage.includes('linuxdeploy') ||
|
||||||
@@ -35,10 +38,18 @@ export async function shellExec(
|
|||||||
errorMessage.includes('strip'))
|
errorMessage.includes('strip'))
|
||||||
) {
|
) {
|
||||||
errorMsg +=
|
errorMsg +=
|
||||||
'\n\nLinux AppImage build error. Try one of these solutions:\n' +
|
'\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' +
|
||||||
' 1. Run with: NO_STRIP=true pake <url> --targets appimage\n' +
|
'Linux AppImage Build Failed\n' +
|
||||||
' 2. Use DEB format instead: pake <url> --targets deb\n' +
|
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n' +
|
||||||
' 3. See detailed solutions: https://github.com/tw93/Pake/blob/main/docs/faq.md';
|
'Cause: Strip tool incompatibility with glibc 2.38+\n' +
|
||||||
|
' (affects Debian Trixie, Arch Linux, and other modern distros)\n\n' +
|
||||||
|
'Quick fix:\n' +
|
||||||
|
' NO_STRIP=1 pake <url> --targets appimage --debug\n\n' +
|
||||||
|
'Alternatives:\n' +
|
||||||
|
' • Use DEB format: pake <url> --targets deb\n' +
|
||||||
|
' • Update binutils: sudo apt install binutils (or pacman -S binutils)\n' +
|
||||||
|
' • Detailed guide: https://github.com/tw93/Pake/blob/main/docs/faq.md\n' +
|
||||||
|
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(errorMsg);
|
throw new Error(errorMsg);
|
||||||
|
|||||||
66
dist/cli.js
vendored
66
dist/cli.js
vendored
@@ -23,7 +23,7 @@ import sharp from 'sharp';
|
|||||||
import * as psl from 'psl';
|
import * as psl from 'psl';
|
||||||
|
|
||||||
var name = "pake-cli";
|
var name = "pake-cli";
|
||||||
var version = "3.4.0";
|
var version = "3.4.1";
|
||||||
var description = "🤱🏻 Turn any webpage into a desktop app with one command. 🤱🏻 一键打包网页生成轻量桌面应用。";
|
var description = "🤱🏻 Turn any webpage into a desktop app with one command. 🤱🏻 一键打包网页生成轻量桌面应用。";
|
||||||
var engines = {
|
var engines = {
|
||||||
node: ">=18.0.0"
|
node: ">=18.0.0"
|
||||||
@@ -201,11 +201,13 @@ const IS_MAC = platform$1 === 'darwin';
|
|||||||
const IS_WIN = platform$1 === 'win32';
|
const IS_WIN = platform$1 === 'win32';
|
||||||
const IS_LINUX = platform$1 === 'linux';
|
const IS_LINUX = platform$1 === 'linux';
|
||||||
|
|
||||||
async function shellExec(command, timeout = 300000, env, showOutput = false) {
|
async function shellExec(command, timeout = 300000, env) {
|
||||||
try {
|
try {
|
||||||
const { exitCode } = await execa(command, {
|
const { exitCode } = await execa(command, {
|
||||||
cwd: npmDirectory,
|
cwd: npmDirectory,
|
||||||
stdio: showOutput ? 'inherit' : ['inherit', 'pipe', 'inherit'],
|
// Use 'inherit' to show all output directly to user in real-time.
|
||||||
|
// This ensures linuxdeploy and other tool outputs are visible during builds.
|
||||||
|
stdio: 'inherit',
|
||||||
shell: true,
|
shell: true,
|
||||||
timeout,
|
timeout,
|
||||||
env: env ? { ...process.env, ...env } : process.env,
|
env: env ? { ...process.env, ...env } : process.env,
|
||||||
@@ -219,15 +221,25 @@ async function shellExec(command, timeout = 300000, env, showOutput = false) {
|
|||||||
throw new Error(`Command timed out after ${timeout}ms: "${command}". Try increasing timeout or check network connectivity.`);
|
throw new Error(`Command timed out after ${timeout}ms: "${command}". Try increasing timeout or check network connectivity.`);
|
||||||
}
|
}
|
||||||
let errorMsg = `Error occurred while executing command "${command}". Exit code: ${exitCode}. Details: ${errorMessage}`;
|
let errorMsg = `Error occurred while executing command "${command}". Exit code: ${exitCode}. Details: ${errorMessage}`;
|
||||||
|
// Provide helpful guidance for common Linux AppImage build failures
|
||||||
|
// caused by strip tool incompatibility with modern glibc (2.38+)
|
||||||
if (process.platform === 'linux' &&
|
if (process.platform === 'linux' &&
|
||||||
(errorMessage.includes('linuxdeploy') ||
|
(errorMessage.includes('linuxdeploy') ||
|
||||||
errorMessage.includes('appimage') ||
|
errorMessage.includes('appimage') ||
|
||||||
errorMessage.includes('strip'))) {
|
errorMessage.includes('strip'))) {
|
||||||
errorMsg +=
|
errorMsg +=
|
||||||
'\n\nLinux AppImage build error. Try one of these solutions:\n' +
|
'\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' +
|
||||||
' 1. Run with: NO_STRIP=true pake <url> --targets appimage\n' +
|
'Linux AppImage Build Failed\n' +
|
||||||
' 2. Use DEB format instead: pake <url> --targets deb\n' +
|
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n' +
|
||||||
' 3. See detailed solutions: https://github.com/tw93/Pake/blob/main/docs/faq.md';
|
'Cause: Strip tool incompatibility with glibc 2.38+\n' +
|
||||||
|
' (affects Debian Trixie, Arch Linux, and other modern distros)\n\n' +
|
||||||
|
'Quick fix:\n' +
|
||||||
|
' NO_STRIP=1 pake <url> --targets appimage --debug\n\n' +
|
||||||
|
'Alternatives:\n' +
|
||||||
|
' • Use DEB format: pake <url> --targets deb\n' +
|
||||||
|
' • Update binutils: sudo apt install binutils (or pacman -S binutils)\n' +
|
||||||
|
' • Detailed guide: https://github.com/tw93/Pake/blob/main/docs/faq.md\n' +
|
||||||
|
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
|
||||||
}
|
}
|
||||||
throw new Error(errorMsg);
|
throw new Error(errorMsg);
|
||||||
}
|
}
|
||||||
@@ -351,7 +363,7 @@ async function installRust() {
|
|||||||
const rustInstallScriptForWindows = 'winget install --id Rustlang.Rustup';
|
const rustInstallScriptForWindows = 'winget install --id Rustlang.Rustup';
|
||||||
const spinner = getSpinner('Downloading Rust...');
|
const spinner = getSpinner('Downloading Rust...');
|
||||||
try {
|
try {
|
||||||
await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac, 300000, undefined, true);
|
await shellExec(IS_WIN ? rustInstallScriptForWindows : rustInstallScriptForMac, 300000, undefined);
|
||||||
spinner.succeed(chalk.green('✔ Rust installed successfully!'));
|
spinner.succeed(chalk.green('✔ Rust installed successfully!'));
|
||||||
ensureRustEnv();
|
ensureRustEnv();
|
||||||
}
|
}
|
||||||
@@ -448,13 +460,14 @@ async function mergeConfig(url, options, tauriConf) {
|
|||||||
await fsExtra.copy(sourcePath, destPath);
|
await fsExtra.copy(sourcePath, destPath);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
const { width, height, fullscreen, hideTitleBar, alwaysOnTop, appVersion, darkMode, disabledWebShortcuts, activationShortcut, userAgent, showSystemTray, systemTrayIcon, useLocalFile, identifier, name, resizable = true, inject, proxyUrl, installerLanguage, hideOnClose, incognito, title, wasm, enableDragDrop, multiInstance, } = options;
|
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, } = options;
|
||||||
const { platform } = process;
|
const { platform } = process;
|
||||||
const platformHideOnClose = hideOnClose ?? platform === 'darwin';
|
const platformHideOnClose = hideOnClose ?? platform === 'darwin';
|
||||||
const tauriConfWindowOptions = {
|
const tauriConfWindowOptions = {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fullscreen,
|
fullscreen,
|
||||||
|
maximize,
|
||||||
resizable,
|
resizable,
|
||||||
hide_title_bar: hideTitleBar,
|
hide_title_bar: hideTitleBar,
|
||||||
activation_shortcut: activationShortcut,
|
activation_shortcut: activationShortcut,
|
||||||
@@ -466,6 +479,7 @@ async function mergeConfig(url, options, tauriConf) {
|
|||||||
title: title || null,
|
title: title || null,
|
||||||
enable_wasm: wasm,
|
enable_wasm: wasm,
|
||||||
enable_drag_drop: enableDragDrop,
|
enable_drag_drop: enableDragDrop,
|
||||||
|
start_to_tray: startToTray && showSystemTray,
|
||||||
};
|
};
|
||||||
Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions });
|
Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions });
|
||||||
tauriConf.productName = name;
|
tauriConf.productName = name;
|
||||||
@@ -774,10 +788,10 @@ class BaseBuilder {
|
|||||||
logger.info(`✺ Located in China, using ${packageManager}/rsProxy CN mirror.`);
|
logger.info(`✺ Located in China, using ${packageManager}/rsProxy CN mirror.`);
|
||||||
const projectCnConf = path.join(tauriSrcPath, 'rust_proxy.toml');
|
const projectCnConf = path.join(tauriSrcPath, 'rust_proxy.toml');
|
||||||
await fsExtra.copy(projectCnConf, projectConf);
|
await fsExtra.copy(projectCnConf, projectConf);
|
||||||
await shellExec(`cd "${npmDirectory}" && ${packageManager} install${registryOption}${peerDepsOption}`, timeout, buildEnv, this.options.debug);
|
await shellExec(`cd "${npmDirectory}" && ${packageManager} install${registryOption}${peerDepsOption}`, timeout, buildEnv);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await shellExec(`cd "${npmDirectory}" && ${packageManager} install${peerDepsOption}`, timeout, buildEnv, this.options.debug);
|
await shellExec(`cd "${npmDirectory}" && ${packageManager} install${peerDepsOption}`, timeout, buildEnv);
|
||||||
}
|
}
|
||||||
spinner.succeed(chalk.green('Package installed!'));
|
spinner.succeed(chalk.green('Package installed!'));
|
||||||
if (!tauriTargetPathExists) {
|
if (!tauriTargetPathExists) {
|
||||||
@@ -806,7 +820,16 @@ class BaseBuilder {
|
|||||||
...this.getBuildEnvironment(),
|
...this.getBuildEnvironment(),
|
||||||
...(process.env.NO_STRIP && { NO_STRIP: process.env.NO_STRIP }),
|
...(process.env.NO_STRIP && { NO_STRIP: process.env.NO_STRIP }),
|
||||||
};
|
};
|
||||||
await shellExec(`cd "${npmDirectory}" && ${this.getBuildCommand(packageManager)}`, this.getBuildTimeout(), buildEnv, this.options.debug);
|
// Warn users about potential AppImage build failures on modern Linux systems.
|
||||||
|
// The linuxdeploy tool bundled in Tauri uses an older strip tool that doesn't
|
||||||
|
// recognize the .relr.dyn section introduced in glibc 2.38+.
|
||||||
|
if (process.platform === 'linux' && this.options.targets === 'appimage') {
|
||||||
|
if (!buildEnv.NO_STRIP) {
|
||||||
|
logger.warn('⚠ Building AppImage on Linux may fail due to strip incompatibility with glibc 2.38+');
|
||||||
|
logger.warn('⚠ If build fails, retry with: NO_STRIP=1 pake <url> --targets appimage');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await shellExec(`cd "${npmDirectory}" && ${this.getBuildCommand(packageManager)}`, this.getBuildTimeout(), buildEnv);
|
||||||
// Copy app
|
// Copy app
|
||||||
const fileName = this.getFileName();
|
const fileName = this.getFileName();
|
||||||
const fileType = this.getFileType(target);
|
const fileType = this.getFileType(target);
|
||||||
@@ -865,6 +888,11 @@ class BaseBuilder {
|
|||||||
if (target) {
|
if (target) {
|
||||||
fullCommand += ` --target ${target}`;
|
fullCommand += ` --target ${target}`;
|
||||||
}
|
}
|
||||||
|
// Enable verbose output in debug mode to help diagnose build issues.
|
||||||
|
// This provides detailed logs from Tauri CLI and bundler tools.
|
||||||
|
if (this.options.debug) {
|
||||||
|
fullCommand += ' --verbose';
|
||||||
|
}
|
||||||
return fullCommand;
|
return fullCommand;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -1178,6 +1206,12 @@ class LinuxBuilder extends BaseBuilder {
|
|||||||
if (features.length > 0) {
|
if (features.length > 0) {
|
||||||
fullCommand += ` --features ${features.join(',')}`;
|
fullCommand += ` --features ${features.join(',')}`;
|
||||||
}
|
}
|
||||||
|
// Enable verbose output for AppImage builds when debugging or PAKE_VERBOSE is set.
|
||||||
|
// AppImage builds often fail with minimal error messages from linuxdeploy,
|
||||||
|
// so verbose mode helps diagnose issues like strip failures and missing dependencies.
|
||||||
|
if (this.options.targets === 'appimage' && (this.options.debug || process.env.PAKE_VERBOSE)) {
|
||||||
|
fullCommand += ' --verbose';
|
||||||
|
}
|
||||||
return fullCommand;
|
return fullCommand;
|
||||||
}
|
}
|
||||||
getBasePath() {
|
getBasePath() {
|
||||||
@@ -1227,6 +1261,7 @@ const DEFAULT_PAKE_OPTIONS = {
|
|||||||
height: 780,
|
height: 780,
|
||||||
width: 1200,
|
width: 1200,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
|
maximize: false,
|
||||||
hideTitleBar: false,
|
hideTitleBar: false,
|
||||||
alwaysOnTop: false,
|
alwaysOnTop: false,
|
||||||
appVersion: '1.0.0',
|
appVersion: '1.0.0',
|
||||||
@@ -1249,6 +1284,7 @@ const DEFAULT_PAKE_OPTIONS = {
|
|||||||
enableDragDrop: false,
|
enableDragDrop: false,
|
||||||
keepBinary: false,
|
keepBinary: false,
|
||||||
multiInstance: false,
|
multiInstance: false,
|
||||||
|
startToTray: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
async function checkUpdateTips() {
|
async function checkUpdateTips() {
|
||||||
@@ -1731,6 +1767,9 @@ program
|
|||||||
.addOption(new Option('--always-on-top', 'Always on the top level')
|
.addOption(new Option('--always-on-top', 'Always on the top level')
|
||||||
.default(DEFAULT_PAKE_OPTIONS.alwaysOnTop)
|
.default(DEFAULT_PAKE_OPTIONS.alwaysOnTop)
|
||||||
.hideHelp())
|
.hideHelp())
|
||||||
|
.addOption(new Option('--maximize', 'Start window maximized')
|
||||||
|
.default(DEFAULT_PAKE_OPTIONS.maximize)
|
||||||
|
.hideHelp())
|
||||||
.addOption(new Option('--dark-mode', 'Force Mac app to use dark mode')
|
.addOption(new Option('--dark-mode', 'Force Mac app to use dark mode')
|
||||||
.default(DEFAULT_PAKE_OPTIONS.darkMode)
|
.default(DEFAULT_PAKE_OPTIONS.darkMode)
|
||||||
.hideHelp())
|
.hideHelp())
|
||||||
@@ -1774,6 +1813,9 @@ program
|
|||||||
.addOption(new Option('--multi-instance', 'Allow multiple app instances')
|
.addOption(new Option('--multi-instance', 'Allow multiple app instances')
|
||||||
.default(DEFAULT_PAKE_OPTIONS.multiInstance)
|
.default(DEFAULT_PAKE_OPTIONS.multiInstance)
|
||||||
.hideHelp())
|
.hideHelp())
|
||||||
|
.addOption(new Option('--start-to-tray', 'Start app minimized to tray')
|
||||||
|
.default(DEFAULT_PAKE_OPTIONS.startToTray)
|
||||||
|
.hideHelp())
|
||||||
.addOption(new Option('--installer-language <string>', 'Installer language')
|
.addOption(new Option('--installer-language <string>', 'Installer language')
|
||||||
.default(DEFAULT_PAKE_OPTIONS.installerLanguage)
|
.default(DEFAULT_PAKE_OPTIONS.installerLanguage)
|
||||||
.hideHelp())
|
.hideHelp())
|
||||||
|
|||||||
22
docs/cli-usage.md
vendored
22
docs/cli-usage.md
vendored
@@ -155,6 +155,15 @@ screen.
|
|||||||
--fullscreen
|
--fullscreen
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### [maximize]
|
||||||
|
|
||||||
|
Determine whether the application launches with a maximized window. Default is `false`. Use the following command to enable
|
||||||
|
maximize.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
--maximize
|
||||||
|
```
|
||||||
|
|
||||||
#### [activation-shortcut]
|
#### [activation-shortcut]
|
||||||
|
|
||||||
Set the activation shortcut for the application. Default is empty, so it does not take effect. You can customize the activation shortcut with the following commands, e.g. `CmdOrControl+Shift+P`. Usage can refer to [available-modifiers](https://www.electronjs.org/docs/latest/api/accelerator#available-modifiers).
|
Set the activation shortcut for the application. Default is empty, so it does not take effect. You can customize the activation shortcut with the following commands, e.g. `CmdOrControl+Shift+P`. Usage can refer to [available-modifiers](https://www.electronjs.org/docs/latest/api/accelerator#available-modifiers).
|
||||||
@@ -288,6 +297,19 @@ Hide window instead of closing the application when clicking close button. Platf
|
|||||||
--hide-on-close false
|
--hide-on-close false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### [start-to-tray]
|
||||||
|
|
||||||
|
Start the application minimized to system tray instead of showing the window. Must be used with `--show-system-tray`. Default is `false`.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
--start-to-tray
|
||||||
|
|
||||||
|
# Example: Start hidden to tray (must use with --show-system-tray)
|
||||||
|
pake https://github.com --name GitHub --show-system-tray --start-to-tray
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: Double-click the tray icon to show/hide the window. If used without `--show-system-tray`, this option is ignored.
|
||||||
|
|
||||||
#### [title]
|
#### [title]
|
||||||
|
|
||||||
Set the window title bar text. macOS shows no title if not specified; Windows/Linux fallback to app name.
|
Set the window title bar text. macOS shows no title if not specified; Windows/Linux fallback to app name.
|
||||||
|
|||||||
21
docs/cli-usage_CN.md
vendored
21
docs/cli-usage_CN.md
vendored
@@ -154,6 +154,14 @@ pake https://github.com --name GitHub
|
|||||||
--fullscreen
|
--fullscreen
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### [maximize]
|
||||||
|
|
||||||
|
设置应用程序是否在启动时最大化窗口,默认为 `false`。使用以下命令可以设置应用程序启动时窗口最大化。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
--maximize
|
||||||
|
```
|
||||||
|
|
||||||
#### [activation-shortcut]
|
#### [activation-shortcut]
|
||||||
|
|
||||||
设置应用程序的激活快捷键。默认为空,不生效,可以使用以下命令自定义激活快捷键,例如 `CmdOrControl+Shift+P`,使用可参考 [available-modifiers](https://www.electronjs.org/docs/latest/api/accelerator#available-modifiers)。
|
设置应用程序的激活快捷键。默认为空,不生效,可以使用以下命令自定义激活快捷键,例如 `CmdOrControl+Shift+P`,使用可参考 [available-modifiers](https://www.electronjs.org/docs/latest/api/accelerator#available-modifiers)。
|
||||||
@@ -287,6 +295,19 @@ pake https://github.com --name GitHub
|
|||||||
--hide-on-close false
|
--hide-on-close false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### [start-to-tray]
|
||||||
|
|
||||||
|
启动时将应用程序最小化到系统托盘而不是显示窗口。必须与 `--show-system-tray` 一起使用。默认为 `false`。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
--start-to-tray
|
||||||
|
|
||||||
|
# 示例:启动时隐藏到托盘(必须与 --show-system-tray 一起使用)
|
||||||
|
pake https://github.com --name GitHub --show-system-tray --start-to-tray
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:双击托盘图标可以显示/隐藏窗口。如果不与 `--show-system-tray` 一起使用,此选项将被忽略。
|
||||||
|
|
||||||
#### [title]
|
#### [title]
|
||||||
|
|
||||||
设置窗口标题栏文本,macOS 未指定时不显示标题,Windows/Linux 回退使用应用名称。
|
设置窗口标题栏文本,macOS 未指定时不显示标题,Windows/Linux 回退使用应用名称。
|
||||||
|
|||||||
2
package.json
vendored
2
package.json
vendored
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pake-cli",
|
"name": "pake-cli",
|
||||||
"version": "3.4.0",
|
"version": "3.4.1",
|
||||||
"description": "🤱🏻 Turn any webpage into a desktop app with one command. 🤱🏻 一键打包网页生成轻量桌面应用。",
|
"description": "🤱🏻 Turn any webpage into a desktop app with one command. 🤱🏻 一键打包网页生成轻量桌面应用。",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
|
|||||||
4
src-tauri/pake.json
vendored
4
src-tauri/pake.json
vendored
@@ -15,7 +15,9 @@
|
|||||||
"hide_on_close": true,
|
"hide_on_close": true,
|
||||||
"incognito": false,
|
"incognito": false,
|
||||||
"enable_wasm": false,
|
"enable_wasm": false,
|
||||||
"enable_drag_drop": false
|
"enable_drag_drop": false,
|
||||||
|
"maximize": false,
|
||||||
|
"start_to_tray": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"user_agent": {
|
"user_agent": {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ pub struct WindowConfig {
|
|||||||
pub url: String,
|
pub url: String,
|
||||||
pub hide_title_bar: bool,
|
pub hide_title_bar: bool,
|
||||||
pub fullscreen: bool,
|
pub fullscreen: bool,
|
||||||
|
pub maximize: bool,
|
||||||
pub width: f64,
|
pub width: f64,
|
||||||
pub height: f64,
|
pub height: f64,
|
||||||
pub resizable: bool,
|
pub resizable: bool,
|
||||||
@@ -18,6 +19,7 @@ pub struct WindowConfig {
|
|||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
pub enable_wasm: bool,
|
pub enable_wasm: bool,
|
||||||
pub enable_drag_drop: bool,
|
pub enable_drag_drop: bool,
|
||||||
|
pub start_to_tray: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ use std::sync::{Arc, Mutex};
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use tauri::{
|
use tauri::{
|
||||||
menu::{MenuBuilder, MenuItemBuilder},
|
menu::{MenuBuilder, MenuItemBuilder},
|
||||||
tray::TrayIconBuilder,
|
tray::{TrayIconBuilder, TrayIconEvent},
|
||||||
AppHandle, Manager,
|
AppHandle, Manager,
|
||||||
};
|
};
|
||||||
use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut};
|
use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut};
|
||||||
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
|
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
|
||||||
|
|
||||||
pub fn set_system_tray(app: &AppHandle, show_system_tray: bool) -> tauri::Result<()> {
|
pub fn set_system_tray(app: &AppHandle, show_system_tray: bool, tray_icon_path: &str) -> tauri::Result<()> {
|
||||||
if !show_system_tray {
|
if !show_system_tray {
|
||||||
app.remove_tray_by_id("pake-tray");
|
app.remove_tray_by_id("pake-tray");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -44,7 +44,30 @@ pub fn set_system_tray(app: &AppHandle, show_system_tray: bool) -> tauri::Result
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
})
|
})
|
||||||
.icon(app.default_window_icon().unwrap().clone())
|
.on_tray_icon_event(|tray, event| match event {
|
||||||
|
TrayIconEvent::Click { button, .. } => {
|
||||||
|
if button == tauri::tray::MouseButton::Left {
|
||||||
|
if let Some(window) = tray.app_handle().get_webview_window("pake") {
|
||||||
|
let is_visible = window.is_visible().unwrap_or(false);
|
||||||
|
if is_visible {
|
||||||
|
window.hide().unwrap();
|
||||||
|
} else {
|
||||||
|
window.show().unwrap();
|
||||||
|
window.set_focus().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
})
|
||||||
|
.icon(if tray_icon_path.is_empty() {
|
||||||
|
app.default_window_icon().unwrap_or_else(|| panic!("Failed to get default window icon")).clone()
|
||||||
|
} else {
|
||||||
|
tauri::image::Image::from_path(tray_icon_path).unwrap_or_else(|_| {
|
||||||
|
// If custom tray icon fails to load, fallback to default
|
||||||
|
app.default_window_icon().unwrap_or_else(|| panic!("Failed to get default window icon")).clone()
|
||||||
|
})
|
||||||
|
})
|
||||||
.build(app)?;
|
.build(app)?;
|
||||||
|
|
||||||
tray.set_icon_as_template(false)?;
|
tray.set_icon_as_template(false)?;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) ->
|
|||||||
.user_agent(user_agent)
|
.user_agent(user_agent)
|
||||||
.resizable(window_config.resizable)
|
.resizable(window_config.resizable)
|
||||||
.fullscreen(window_config.fullscreen)
|
.fullscreen(window_config.fullscreen)
|
||||||
|
.maximized(window_config.maximize)
|
||||||
.inner_size(window_config.width, window_config.height)
|
.inner_size(window_config.width, window_config.height)
|
||||||
.always_on_top(window_config.always_on_top)
|
.always_on_top(window_config.always_on_top)
|
||||||
.incognito(window_config.incognito);
|
.incognito(window_config.incognito);
|
||||||
@@ -65,14 +66,7 @@ pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) ->
|
|||||||
.additional_browser_args("--enable-unsafe-webgpu");
|
.additional_browser_args("--enable-unsafe-webgpu");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !config.proxy_url.is_empty() {
|
// Platform-specific configuration must be set before proxy on Windows/Linux
|
||||||
if let Ok(proxy_url) = Url::from_str(&config.proxy_url) {
|
|
||||||
window_builder = window_builder.proxy_url(proxy_url);
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
println!("Proxy configured: {}", config.proxy_url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
let title_bar_style = if window_config.hide_title_bar {
|
let title_bar_style = if window_config.hide_title_bar {
|
||||||
@@ -87,7 +81,7 @@ pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Windows and Linux share the same configuration
|
// Windows and Linux: set data_directory before proxy_url
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
{
|
{
|
||||||
window_builder = window_builder
|
window_builder = window_builder
|
||||||
@@ -96,5 +90,14 @@ pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) ->
|
|||||||
.theme(None);
|
.theme(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set proxy after platform-specific configs (required for Windows/Linux)
|
||||||
|
if !config.proxy_url.is_empty() {
|
||||||
|
if let Ok(proxy_url) = Url::from_str(&config.proxy_url) {
|
||||||
|
window_builder = window_builder.proxy_url(proxy_url);
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
println!("Proxy configured: {}", config.proxy_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window_builder.build().expect("Failed to build window")
|
window_builder.build().expect("Failed to build window")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ pub fn run_app() {
|
|||||||
let hide_on_close = pake_config.windows[0].hide_on_close;
|
let hide_on_close = pake_config.windows[0].hide_on_close;
|
||||||
let activation_shortcut = pake_config.windows[0].activation_shortcut.clone();
|
let activation_shortcut = pake_config.windows[0].activation_shortcut.clone();
|
||||||
let init_fullscreen = pake_config.windows[0].fullscreen;
|
let init_fullscreen = pake_config.windows[0].fullscreen;
|
||||||
|
let start_to_tray = pake_config.windows[0].start_to_tray && show_system_tray; // Only valid when tray is enabled
|
||||||
let multi_instance = pake_config.multi_instance;
|
let multi_instance = pake_config.multi_instance;
|
||||||
|
|
||||||
let window_state_plugin = WindowStatePlugin::default()
|
let window_state_plugin = WindowStatePlugin::default()
|
||||||
@@ -62,15 +63,18 @@ pub fn run_app() {
|
|||||||
])
|
])
|
||||||
.setup(move |app| {
|
.setup(move |app| {
|
||||||
let window = set_window(app, &pake_config, &tauri_config);
|
let window = set_window(app, &pake_config, &tauri_config);
|
||||||
set_system_tray(app.app_handle(), show_system_tray).unwrap();
|
set_system_tray(app.app_handle(), show_system_tray, &pake_config.system_tray_path).unwrap();
|
||||||
set_global_shortcut(app.app_handle(), activation_shortcut).unwrap();
|
set_global_shortcut(app.app_handle(), activation_shortcut).unwrap();
|
||||||
|
|
||||||
// Show window after state restoration to prevent position flashing
|
// Show window after state restoration to prevent position flashing
|
||||||
let window_clone = window.clone();
|
// Unless start_to_tray is enabled, then keep it hidden
|
||||||
tauri::async_runtime::spawn(async move {
|
if !start_to_tray {
|
||||||
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
|
let window_clone = window.clone();
|
||||||
window_clone.show().unwrap();
|
tauri::async_runtime::spawn(async move {
|
||||||
});
|
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
|
||||||
|
window_clone.show().unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user