From aa65c7cd194c2c4cf56fe8a7e2da4d5369f37446 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Tue, 11 Nov 2025 15:21:20 +0800 Subject: [PATCH 1/6] Prevent duplicate downloads --- package.json | 2 +- src-tauri/src/inject/event.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 87902ce..46a3b29 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "3.4.3", + "version": "3.4.4", "description": "🤱🏻 Turn any webpage into a desktop app with one command. 🤱🏻 一键打包网页生成轻量桌面应用。", "engines": { "node": ">=18.0.0" diff --git a/src-tauri/src/inject/event.js b/src-tauri/src/inject/event.js index ab9583c..bfe6dea 100644 --- a/src-tauri/src/inject/event.js +++ b/src-tauri/src/inject/event.js @@ -319,9 +319,13 @@ document.addEventListener("DOMContentLoaded", () => { const url = anchorEle.href; const filename = anchorEle.download || getFilenameFromUrl(url); if (window.blobToUrlCaches.has(url)) { + e.preventDefault(); + e.stopImmediatePropagation(); downloadFromBlobUrl(url, filename); // case: download from dataURL -> convert dataURL -> } else if (url.startsWith("data:")) { + e.preventDefault(); + e.stopImmediatePropagation(); downloadFromDataUri(url, filename); } }, From eb829ca85cb29721c5f9daae1cf5048dc7915223 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Tue, 11 Nov 2025 15:40:10 +0800 Subject: [PATCH 2/6] Support force internal navigation parameter --- bin/cli.ts | 8 ++++++++ bin/defaults.ts | 1 + bin/helpers/merge.ts | 2 ++ bin/types.ts | 3 +++ docs/cli-usage.md | 8 ++++++++ docs/cli-usage_CN.md | 8 ++++++++ src-tauri/pake.json | 3 ++- src-tauri/src/app/config.rs | 2 ++ src-tauri/src/inject/event.js | 24 ++++++++++++++++++++++-- 9 files changed, 56 insertions(+), 3 deletions(-) diff --git a/bin/cli.ts b/bin/cli.ts index 2a74625..78bc857 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -174,6 +174,14 @@ program .default(DEFAULT.startToTray) .hideHelp(), ) + .addOption( + new Option( + '--force-internal-navigation', + 'Keep every link inside the Pake window instead of opening external handlers', + ) + .default(DEFAULT.forceInternalNavigation) + .hideHelp(), + ) .addOption( new Option('--installer-language ', 'Installer language') .default(DEFAULT.installerLanguage) diff --git a/bin/defaults.ts b/bin/defaults.ts index a78501b..fed0e1c 100644 --- a/bin/defaults.ts +++ b/bin/defaults.ts @@ -30,6 +30,7 @@ export const DEFAULT_PAKE_OPTIONS: PakeCliOptions = { keepBinary: false, multiInstance: false, startToTray: false, + forceInternalNavigation: false, }; // Just for cli development diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts index 311fd92..dbb3630 100644 --- a/bin/helpers/merge.ts +++ b/bin/helpers/merge.ts @@ -73,6 +73,7 @@ export async function mergeConfig( enableDragDrop, multiInstance, startToTray, + forceInternalNavigation, } = options; const { platform } = process; @@ -96,6 +97,7 @@ export async function mergeConfig( enable_wasm: wasm, enable_drag_drop: enableDragDrop, start_to_tray: startToTray && showSystemTray, + force_internal_navigation: forceInternalNavigation, }; Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); diff --git a/bin/types.ts b/bin/types.ts index 4559207..3a521a2 100644 --- a/bin/types.ts +++ b/bin/types.ts @@ -96,6 +96,9 @@ export interface PakeCliOptions { // Start app minimized to tray, default false startToTray: boolean; + + // Force navigation to stay inside the Pake window even for external links + forceInternalNavigation: boolean; } export interface PakeAppOptions extends PakeCliOptions { diff --git a/docs/cli-usage.md b/docs/cli-usage.md index c003a8c..8755c6c 100644 --- a/docs/cli-usage.md +++ b/docs/cli-usage.md @@ -204,6 +204,14 @@ Sets whether to disable web shortcuts in the original Pake container, defaults t --disabled-web-shortcuts ``` +#### [force-internal-navigation] + +Keeps every clicked link (even pointing to other domains) inside the Pake window instead of letting the OS open an external browser or helper. Default is `false`. + +```shell +--force-internal-navigation +``` + #### [multi-arch] Package the application to support both Intel and M1 chips, exclusively for macOS. Default is `false`. diff --git a/docs/cli-usage_CN.md b/docs/cli-usage_CN.md index 6ac2d6b..6090d72 100644 --- a/docs/cli-usage_CN.md +++ b/docs/cli-usage_CN.md @@ -202,6 +202,14 @@ pake https://github.com --name GitHub --disabled-web-shortcuts ``` +#### [force-internal-navigation] + +启用后所有点击的链接(即使是跨域)都会在 Pake 窗口内打开,不会再调用外部浏览器或辅助程序。默认 `false`。 + +```shell +--force-internal-navigation +``` + #### [multi-arch] 设置打包结果同时支持 Intel 和 M1 芯片,仅适用于 macOS,默认为 `false`。 diff --git a/src-tauri/pake.json b/src-tauri/pake.json index 97ad31a..60f9b59 100644 --- a/src-tauri/pake.json +++ b/src-tauri/pake.json @@ -17,7 +17,8 @@ "enable_wasm": false, "enable_drag_drop": false, "maximize": false, - "start_to_tray": false + "start_to_tray": false, + "force_internal_navigation": false } ], "user_agent": { diff --git a/src-tauri/src/app/config.rs b/src-tauri/src/app/config.rs index 9246b7b..2544fb5 100644 --- a/src-tauri/src/app/config.rs +++ b/src-tauri/src/app/config.rs @@ -20,6 +20,8 @@ pub struct WindowConfig { pub enable_wasm: bool, pub enable_drag_drop: bool, pub start_to_tray: bool, + #[serde(default)] + pub force_internal_navigation: bool, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src-tauri/src/inject/event.js b/src-tauri/src/inject/event.js index bfe6dea..54ed16d 100644 --- a/src-tauri/src/inject/event.js +++ b/src-tauri/src/inject/event.js @@ -192,6 +192,8 @@ document.addEventListener("DOMContentLoaded", () => { const tauri = window.__TAURI__; const appWindow = tauri.window.getCurrentWindow(); const invoke = tauri.core.invoke; + const pakeConfig = window["pakeConfig"] || {}; + const forceInternalNavigation = pakeConfig.force_internal_navigation === true; if (!document.getElementById("pake-top-dom")) { const topDom = document.createElement("div"); @@ -394,9 +396,15 @@ document.addEventListener("DOMContentLoaded", () => { // Handle _blank links: same domain navigates in-app, cross-domain opens new window if (target === "_blank") { + if (forceInternalNavigation) { + e.preventDefault(); + e.stopImmediatePropagation(); + window.location.href = absoluteUrl; + return; + } + if (isSameDomain(absoluteUrl)) { - // For same-domain links, let the browser/SPA handle it naturally - // This prevents full page reload in SPA apps like Discord + // For same-domain links, let the browser handle it naturally return; } @@ -413,6 +421,10 @@ document.addEventListener("DOMContentLoaded", () => { } if (target === "_new") { + if (forceInternalNavigation) { + return; + } + e.preventDefault(); handleExternalLink(absoluteUrl); return; @@ -435,6 +447,10 @@ document.addEventListener("DOMContentLoaded", () => { // Handle regular links: same domain allows normal navigation, cross-domain opens new window if (!target || target === "_self") { if (!isSameDomain(absoluteUrl)) { + if (forceInternalNavigation) { + return; + } + e.preventDefault(); e.stopImmediatePropagation(); const newWindow = originalWindowOpen.call( @@ -468,6 +484,10 @@ document.addEventListener("DOMContentLoaded", () => { const absoluteUrl = hrefUrl.href; if (!isSameDomain(absoluteUrl)) { + if (forceInternalNavigation) { + return originalWindowOpen.call(window, absoluteUrl, name, specs); + } + handleExternalLink(absoluteUrl); return null; } From d597d667226c90e707d4f0634b3b97cb5d58ca73 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Tue, 11 Nov 2025 15:50:38 +0800 Subject: [PATCH 3/6] Fix the issue of Linux in chart PNG conversion. --- scripts/configure-tauri.mjs | 50 ++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/scripts/configure-tauri.mjs b/scripts/configure-tauri.mjs index 7ce14c6..0a0e106 100755 --- a/scripts/configure-tauri.mjs +++ b/scripts/configure-tauri.mjs @@ -6,6 +6,7 @@ import linuxJson from "../src-tauri/tauri.linux.conf.json" with { type: "json" } import { writeFileSync, existsSync, copyFileSync } from "fs"; import os from "os"; +import sharp from "sharp"; /** * Configuration script for Tauri app generation @@ -121,20 +122,40 @@ function updateBaseConfigs() { } } -function ensureIconExists(iconPath, defaultPath, description = "icon") { +async function ensureRgbaPng(iconPath) { + try { + const buffer = await sharp(iconPath) + .ensureAlpha() + .png({ force: true }) + .toBuffer(); + writeFileSync(iconPath, buffer); + } catch (error) { + console.warn(`Failed to normalize ${iconPath} to RGBA: ${error.message}`); + } +} + +async function ensureIconExists( + iconPath, + defaultPath, + description = "icon", + ensureRgba = false, +) { if (!existsSync(iconPath)) { - // For official release apps, icons should already exist if (process.env.PAKE_CREATE_APP === "1") { console.warn( `${description} for ${process.env.NAME} not found at ${iconPath}`, ); - return; // Don't auto-generate for release builds + return; } console.warn( `${description} for ${process.env.NAME} not found, using default`, ); copyFileSync(defaultPath, iconPath); } + + if (ensureRgba && existsSync(iconPath)) { + await ensureRgbaPng(iconPath); + } } function updatePlatformConfig(platformConfig, platformVars) { @@ -150,8 +171,13 @@ function updatePlatformConfig(platformConfig, platformVars) { // Platform-specific handlers const platformHandlers = { - linux: (config) => { - ensureIconExists(config.iconPath, config.defaultIcon, "Linux icon"); + linux: async (config) => { + await ensureIconExists( + config.iconPath, + config.defaultIcon, + "Linux icon", + true, + ); // Update desktop entry linuxJson.bundle.linux.deb.files = { @@ -162,14 +188,14 @@ const platformHandlers = { updatePlatformConfig(linuxJson, config); }, - darwin: (config) => { - ensureIconExists(config.iconPath, config.defaultIcon, "macOS icon"); + darwin: async (config) => { + await ensureIconExists(config.iconPath, config.defaultIcon, "macOS icon"); updatePlatformConfig(macosJson, config); }, - win32: (config) => { - ensureIconExists(config.iconPath, config.defaultIcon, "Windows icon"); - ensureIconExists( + win32: async (config) => { + await ensureIconExists(config.iconPath, config.defaultIcon, "Windows icon"); + await ensureIconExists( config.hdIconPath, config.hdDefaultIcon, "Windows HD icon", @@ -196,7 +222,7 @@ function saveConfigurations() { } // Main execution -function main() { +async function main() { try { validateEnvironment(); updateBaseConfigs(); @@ -210,7 +236,7 @@ function main() { const handler = platformHandlers[platform]; if (handler) { - handler(platformConfig); + await handler(platformConfig); } saveConfigurations(); From d28d6cb49354d523290205c178a4c54303f09f5d Mon Sep 17 00:00:00 2001 From: Tw93 Date: Tue, 11 Nov 2025 15:55:03 +0800 Subject: [PATCH 4/6] Increase the content output --- bin/cli.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/cli.ts b/bin/cli.ts index 78bc857..5a7f126 100644 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -212,6 +212,7 @@ program } log.setDefaultLevel('info'); + log.setLevel('info'); if (options.debug) { log.setLevel('debug'); } From 4fc524e0b2e90213a7dc653c34a1876763f5ea78 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Tue, 11 Nov 2025 15:56:34 +0800 Subject: [PATCH 5/6] 3.5.0 --- dist/cli.js | 20 +++++++++++++------- package.json | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/dist/cli.js b/dist/cli.js index 76caf3d..4e0d45f 100755 --- a/dist/cli.js +++ b/dist/cli.js @@ -23,7 +23,7 @@ import sharp from 'sharp'; import * as psl from 'psl'; var name = "pake-cli"; -var version = "3.4.2"; +var version = "3.5.0"; var description = "🤱🏻 Turn any webpage into a desktop app with one command. 🤱🏻 一键打包网页生成轻量桌面应用。"; var engines = { node: ">=18.0.0" @@ -73,8 +73,8 @@ var type = "module"; var exports = "./dist/cli.js"; var license = "MIT"; var dependencies = { - "@tauri-apps/api": "^2.8.0", - "@tauri-apps/cli": "^2.8.4", + "@tauri-apps/api": "^2.9.0", + "@tauri-apps/cli": "^2.9.0", axios: "^1.12.2", chalk: "^5.6.2", commander: "^12.1.0", @@ -92,12 +92,12 @@ var dependencies = { }; var devDependencies = { "@rollup/plugin-alias": "^5.1.1", - "@rollup/plugin-commonjs": "^28.0.6", + "@rollup/plugin-commonjs": "^28.0.8", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-replace": "^6.0.2", "@rollup/plugin-terser": "^0.4.4", "@types/fs-extra": "^11.0.4", - "@types/node": "^20.19.21", + "@types/node": "^20.19.23", "@types/page-icon": "^0.3.6", "@types/prompts": "^2.4.9", "@types/tmp": "^0.2.6", @@ -105,7 +105,7 @@ var devDependencies = { "app-root-path": "^3.1.0", "cross-env": "^7.0.3", prettier: "^3.6.2", - rollup: "^4.52.4", + rollup: "^4.52.5", "rollup-plugin-typescript2": "^0.36.0", tslib: "^2.8.1", typescript: "^5.9.3" @@ -470,7 +470,7 @@ async function mergeConfig(url, options, tauriConf) { await fsExtra.copy(sourcePath, destPath); } })); - 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 { 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, forceInternalNavigation, } = options; const { platform } = process; const platformHideOnClose = hideOnClose ?? platform === 'darwin'; const tauriConfWindowOptions = { @@ -490,6 +490,7 @@ async function mergeConfig(url, options, tauriConf) { enable_wasm: wasm, enable_drag_drop: enableDragDrop, start_to_tray: startToTray && showSystemTray, + force_internal_navigation: forceInternalNavigation, }; Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions }); tauriConf.productName = name; @@ -1331,6 +1332,7 @@ const DEFAULT_PAKE_OPTIONS = { keepBinary: false, multiInstance: false, startToTray: false, + forceInternalNavigation: false, }; async function checkUpdateTips() { @@ -1862,6 +1864,9 @@ program .addOption(new Option('--start-to-tray', 'Start app minimized to tray') .default(DEFAULT_PAKE_OPTIONS.startToTray) .hideHelp()) + .addOption(new Option('--force-internal-navigation', 'Keep every link inside the Pake window instead of opening external handlers') + .default(DEFAULT_PAKE_OPTIONS.forceInternalNavigation) + .hideHelp()) .addOption(new Option('--installer-language ', 'Installer language') .default(DEFAULT_PAKE_OPTIONS.installerLanguage) .hideHelp()) @@ -1888,6 +1893,7 @@ program return; } log.setDefaultLevel('info'); + log.setLevel('info'); if (options.debug) { log.setLevel('debug'); } diff --git a/package.json b/package.json index 46a3b29..7b760fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "3.4.4", + "version": "3.5.0", "description": "🤱🏻 Turn any webpage into a desktop app with one command. 🤱🏻 一键打包网页生成轻量桌面应用。", "engines": { "node": ">=18.0.0" From 77e212d2f6a7c60f0ff0eec0a3efe053d794a414 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Tue, 11 Nov 2025 16:03:08 +0800 Subject: [PATCH 6/6] 3.5.1 --- dist/cli.js | 2 +- package.json | 2 +- src-tauri/src/inject/event.js | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dist/cli.js b/dist/cli.js index 4e0d45f..8a70f86 100755 --- a/dist/cli.js +++ b/dist/cli.js @@ -23,7 +23,7 @@ import sharp from 'sharp'; import * as psl from 'psl'; var name = "pake-cli"; -var version = "3.5.0"; +var version = "3.5.1"; var description = "🤱🏻 Turn any webpage into a desktop app with one command. 🤱🏻 一键打包网页生成轻量桌面应用。"; var engines = { node: ">=18.0.0" diff --git a/package.json b/package.json index 7b760fe..c224d85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "3.5.0", + "version": "3.5.1", "description": "🤱🏻 Turn any webpage into a desktop app with one command. 🤱🏻 一键打包网页生成轻量桌面应用。", "engines": { "node": ">=18.0.0" diff --git a/src-tauri/src/inject/event.js b/src-tauri/src/inject/event.js index 54ed16d..6796867 100644 --- a/src-tauri/src/inject/event.js +++ b/src-tauri/src/inject/event.js @@ -422,6 +422,9 @@ document.addEventListener("DOMContentLoaded", () => { if (target === "_new") { if (forceInternalNavigation) { + e.preventDefault(); + e.stopImmediatePropagation(); + window.location.href = absoluteUrl; return; }