diff --git a/README.md b/README.md index 9df7149..764eaae 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,6 @@ -注意:Windows 下不能安装到 `C:\Program File`,会直接闪退,建议安装到其他非管理员权限目录,比如 `D:\Program Files (x86)` 。 ## 命令行打包 diff --git a/README_EN.md b/README_EN.md index b6cb559..1852ebb 100644 --- a/README_EN.md +++ b/README_EN.md @@ -93,8 +93,6 @@ -Note: it cannot be installed to `C:\Program File` under Windows, and it will crash directly. It is recommended to install to other non-administrator directories, such as `D:\Program Files (x86)`. - ## Command line packing diff --git a/bin/README.md b/bin/README.md index 186b364..c09bc25 100644 --- a/bin/README.md +++ b/bin/README.md @@ -4,7 +4,7 @@ npm install -g pake-cli ``` -如果安装失败提示没有权限,请参考该贴解决:[链接](https://gist.github.com/Giancarlos/d087f8a9e6516716da98ad0c0f5a8f58),注意:**尽量别用 sudo 权限**。 +如果安装失败提示没有权限,请参考该贴解决:[链接](https://gist.github.com/Giancarlos/d087f8a9e6516716da98ad0c0f5a8f58),**或者使用 `sudo` 权限**。 ## 用法 diff --git a/bin/README_EN.md b/bin/README_EN.md index 2ae5eef..8250cc4 100644 --- a/bin/README_EN.md +++ b/bin/README_EN.md @@ -4,7 +4,7 @@ npm install -g pake-cli ``` -If the installation fails and you are prompted that you do not have permission, please see this [website](https://gist.github.com/Giancarlos/d087f8a9e6516716da98ad0c0f5a8f58) , attention! **try not to use sudo permissions**. +If the installation fails and you are prompted that you do not have permission, please see this [website](https://gist.github.com/Giancarlos/d087f8a9e6516716da98ad0c0f5a8f58) , **or use `sudo` permissions**. ## Usage @@ -34,7 +34,7 @@ The application name, if not specified when entering, will prompt you to enter, #### [icon] -应用 icon,支持本地/远程文件,默认为 Pake 自带图标。 +The application icon, support local and remote files. The default is brand icon of Pake. - MacOS must be `.icns` - Windows must be `.ico` diff --git a/bin/builders/BuilderFactory.ts b/bin/builders/BuilderFactory.ts index aa3e97c..759ef70 100644 --- a/bin/builders/BuilderFactory.ts +++ b/bin/builders/BuilderFactory.ts @@ -6,7 +6,6 @@ import LinuxBuilder from './LinuxBuilder.js'; export default class BuilderFactory { static create(): IBuilder { - console.log("now platform is ", process.platform); if (IS_MAC) { return new MacBuilder(); } diff --git a/bin/builders/LinuxBuilder.ts b/bin/builders/LinuxBuilder.ts index ed55ab9..cec50b1 100644 --- a/bin/builders/LinuxBuilder.ts +++ b/bin/builders/LinuxBuilder.ts @@ -46,21 +46,6 @@ export default class LinuxBuilder implements IBuilder { const { name } = options; await mergeTauriConfig(url, options, tauriConf); - // write desktop - const assertSrc = `src-tauri/assets/${name}.desktop`; - const assertPath = path.join(npmDirectory, assertSrc); - const desktopStr = ` -[Desktop Entry] -Encoding=UTF-8 -Categories=Office -Exec=${name} -Icon=${name} -Name=${name} -StartupNotify=true -Terminal=false -Type=Application - ` - await fs.writeFile(assertPath, desktopStr); const _ = await shellExec(`cd ${npmDirectory} && npm install && npm run build`); let arch = ""; if (process.arch === "x64") { diff --git a/bin/builders/common.ts b/bin/builders/common.ts index 4ba578f..b081e35 100644 --- a/bin/builders/common.ts +++ b/bin/builders/common.ts @@ -3,6 +3,7 @@ import prompts from 'prompts'; import path from 'path'; import fs from 'fs/promises'; import { npmDirectory } from '@/utils/dir.js'; +import logger from '@/options/logger.js'; export async function promptText(message: string, initial?: string) { const response = await prompts({ @@ -40,20 +41,41 @@ export async function mergeTauriConfig( Object.assign(tauriConf.tauri.windows[0], { url, ...tauriConfWindowOptions }); tauriConf.package.productName = name; tauriConf.tauri.bundle.identifier = identifier; - tauriConf.tauri.bundle.icon = [options.icon]; - if (process.platform === "win32") { - const ico_path = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}_32.ico`); - tauriConf.tauri.bundle.resources = [`png/${name.toLowerCase()}_32.ico`]; - await fs.copyFile(options.icon, ico_path); - } - if (process.platform === "linux") { - const installSrc = `/usr/share/applications/${name}.desktop`; - const assertSrc = `src-tauri/assets/${name}.desktop`; - const assertPath = path.join(npmDirectory, assertSrc); - tauriConf.tauri.bundle.deb.files = { - [installSrc]: assertPath + const exists = await fs.stat(options.icon) + .then(() => true) + .catch(() => false); + if (exists) { + let updateIconPath = true; + let customIconExt = path.extname(options.icon).toLowerCase(); + if (process.platform === "win32") { + if (customIconExt === ".ico") { + const ico_path = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}_32.ico`); + tauriConf.tauri.bundle.resources = [`png/${name.toLowerCase()}_32.ico`]; + await fs.copyFile(options.icon, ico_path); + } else { + updateIconPath = false; + logger.warn(`icon file in Windows must be 256 * 256 pix with .ico type, but you give ${customIconExt}`); + } } - + if (process.platform === "linux") { + delete tauriConf.tauri.bundle.deb.files; + if (customIconExt != ".png") { + updateIconPath = false; + logger.warn(`icon file in Linux must be 512 * 512 pix with .png type, but you give ${customIconExt}`); + } + } + + if (process.platform === "darwin" && customIconExt !== ".icns") { + updateIconPath = false; + logger.warn(`icon file in MacOS must be .icns type, but you give ${customIconExt}`); + } + if (updateIconPath) { + tauriConf.tauri.bundle.icon = [options.icon]; + } else { + logger.warn(`icon file will not change with default.`); + } + } else { + logger.warn("the custom icon path may not exists. we will use default icon to replace it"); } diff --git a/bin/options/icon.ts b/bin/options/icon.ts index 8b803b9..8aff0d6 100644 --- a/bin/options/icon.ts +++ b/bin/options/icon.ts @@ -4,8 +4,9 @@ import { PakeAppOptions } from '../types.js'; import { dir } from 'tmp-promise'; import path from 'path'; import fs from 'fs/promises'; -import { fileURLToPath } from 'url'; import logger from './logger.js'; +import { npmDirectory } from '@/utils/dir.js'; +import { IS_LINUX, IS_WIN } from '@/utils/platform.js'; export async function handleIcon(options: PakeAppOptions, url: string) { if (options.icon) { @@ -16,14 +17,20 @@ export async function handleIcon(options: PakeAppOptions, url: string) { } } if (!options.icon) { - return inferIcon(options.name, url); + return getDefaultIcon(); } } -export async function inferIcon(name: string, url: string) { +export async function getDefaultIcon() { logger.info('You have not provided an app icon, use the default icon.(use --icon option to assign an icon)') - const npmDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); - return path.join(npmDirectory, 'pake-default.icns'); + let iconPath = 'src-tauri/icons/icon.icns'; + if (IS_WIN) { + iconPath = 'src-tauri/png/icon_256.ico'; + } else if (IS_LINUX) { + iconPath = 'src-tauri/png/icon_512.png'; + } + + return path.join(npmDirectory, iconPath); } // export async function getIconFromPageUrl(url: string) { diff --git a/dist/cli.js b/dist/cli.js index dfafabf..dfed20e 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -6,11 +6,11 @@ import isurl from 'is-url'; import prompts from 'prompts'; import path from 'path'; import fs from 'fs/promises'; +import chalk from 'chalk'; import crypto from 'crypto'; import axios from 'axios'; import { fileTypeFromBuffer } from 'file-type'; import { dir } from 'tmp-promise'; -import chalk from 'chalk'; import ora from 'ora'; import shelljs from 'shelljs'; import updateNotifier from 'update-notifier'; @@ -1593,6 +1593,24 @@ function validateUrlInput(url) { const npmDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); +const logger = { + info(...msg) { + log.info(...msg.map((m) => chalk.blue.bold(m))); + }, + debug(...msg) { + log.debug(...msg); + }, + error(...msg) { + log.error(...msg.map((m) => chalk.red.bold(m))); + }, + warn(...msg) { + log.info(...msg.map((m) => chalk.yellow.bold(m))); + }, + success(...msg) { + log.info(...msg.map((m) => chalk.green.bold(m))); + } +}; + function promptText(message, initial) { return __awaiter(this, void 0, void 0, function* () { const response = yield prompts({ @@ -1617,19 +1635,43 @@ function mergeTauriConfig(url, options, tauriConf) { Object.assign(tauriConf.tauri.windows[0], Object.assign({ url }, tauriConfWindowOptions)); tauriConf.package.productName = name; tauriConf.tauri.bundle.identifier = identifier; - tauriConf.tauri.bundle.icon = [options.icon]; - if (process.platform === "win32") { - const ico_path = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}_32.ico`); - tauriConf.tauri.bundle.resources = [`png/${name.toLowerCase()}_32.ico`]; - yield fs.copyFile(options.icon, ico_path); + const exists = yield fs.stat(options.icon) + .then(() => true) + .catch(() => false); + if (exists) { + let updateIconPath = true; + let customIconExt = path.extname(options.icon).toLowerCase(); + if (process.platform === "win32") { + if (customIconExt === ".ico") { + const ico_path = path.join(npmDirectory, `src-tauri/png/${name.toLowerCase()}_32.ico`); + tauriConf.tauri.bundle.resources = [`png/${name.toLowerCase()}_32.ico`]; + yield fs.copyFile(options.icon, ico_path); + } + else { + updateIconPath = false; + logger.warn(`icon file in Windows must be 256 * 256 pix with .ico type, but you give ${customIconExt}`); + } + } + if (process.platform === "linux") { + delete tauriConf.tauri.bundle.deb.files; + if (customIconExt != ".png") { + updateIconPath = false; + logger.warn(`icon file in Linux must be 512 * 512 pix with .png type, but you give ${customIconExt}`); + } + } + if (process.platform === "darwin" && customIconExt !== ".icns") { + updateIconPath = false; + logger.warn(`icon file in MacOS must be .icns type, but you give ${customIconExt}`); + } + if (updateIconPath) { + tauriConf.tauri.bundle.icon = [options.icon]; + } + else { + logger.warn(`icon file will not change with default.`); + } } - if (process.platform === "linux") { - const installSrc = `/usr/share/applications/${name}.desktop`; - const assertSrc = `src-tauri/assets/${name}.desktop`; - const assertPath = path.join(npmDirectory, assertSrc); - tauriConf.tauri.bundle.deb.files = { - [installSrc]: assertPath - }; + else { + logger.warn("the custom icon path may not exists. we will use default icon to replace it"); } let configPath = ""; switch (process.platform) { @@ -1660,23 +1702,9 @@ function getIdentifier(name, url) { return `pake-${postFixHash}`; } -const logger = { - info(...msg) { - log.info(...msg.map((m) => chalk.blue.bold(m))); - }, - debug(...msg) { - log.debug(...msg); - }, - error(...msg) { - log.error(...msg.map((m) => chalk.red.bold(m))); - }, - warn(...msg) { - log.info(...msg.map((m) => chalk.yellow.bold(m))); - }, - success(...msg) { - log.info(...msg.map((m) => chalk.green.bold(m))); - } -}; +const IS_MAC = process.platform === 'darwin'; +const IS_WIN = process.platform === 'win32'; +const IS_LINUX = process.platform === 'linux'; function handleIcon(options, url) { return __awaiter(this, void 0, void 0, function* () { @@ -1689,15 +1717,21 @@ function handleIcon(options, url) { } } if (!options.icon) { - return inferIcon(options.name); + return getDefaultIcon(); } }); } -function inferIcon(name, url) { +function getDefaultIcon() { return __awaiter(this, void 0, void 0, function* () { logger.info('You have not provided an app icon, use the default icon.(use --icon option to assign an icon)'); - const npmDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); - return path.join(npmDirectory, 'pake-default.icns'); + let iconPath = 'src-tauri/icons/icon.icns'; + if (IS_WIN) { + iconPath = 'src-tauri/png/icon_256.ico'; + } + else if (IS_LINUX) { + iconPath = 'src-tauri/png/icon_512.png'; + } + return path.join(npmDirectory, iconPath); }); } // export async function getIconFromPageUrl(url: string) { @@ -1773,10 +1807,6 @@ function handleOptions(options, url) { }); } -const IS_MAC = process.platform === 'darwin'; -const IS_WIN = process.platform === 'win32'; -const IS_LINUX = process.platform === 'linux'; - function shellExec(command) { return new Promise((resolve, reject) => { shelljs.exec(command, { async: true, silent: false }, (code) => { @@ -1870,7 +1900,8 @@ var tauri$2 = { wix: { language: [ "en-US" - ] + ], + template: "assets/main.wxs" } } } @@ -2092,21 +2123,6 @@ class LinuxBuilder { logger.debug('PakeAppOptions', options); const { name } = options; yield mergeTauriConfig(url, options, tauriConf); - // write desktop - const assertSrc = `src-tauri/assets/${name}.desktop`; - const assertPath = path.join(npmDirectory, assertSrc); - const desktopStr = ` -[Desktop Entry] -Encoding=UTF-8 -Categories=Office -Exec=${name} -Icon=${name} -Name=${name} -StartupNotify=true -Terminal=false -Type=Application - `; - yield fs.writeFile(assertPath, desktopStr); yield shellExec(`cd ${npmDirectory} && npm install && npm run build`); let arch = ""; if (process.arch === "x64") { @@ -2137,7 +2153,6 @@ Type=Application class BuilderFactory { static create() { - console.log("now platform is ", process.platform); if (IS_MAC) { return new MacBuilder(); } @@ -2152,7 +2167,7 @@ class BuilderFactory { } var name = "pake-cli"; -var version = "0.1.1"; +var version = "0.1.2"; var description = "🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App 🤱🏻 A simple way to make any web page a desktop application using Rust."; var bin = { pake: "./cli.js" @@ -2168,8 +2183,7 @@ var author = { var files = [ "dist", "src-tauri", - "cli.js", - "pake-default.icns" + "cli.js" ]; var scripts = { start: "npm run dev", diff --git a/package.json b/package.json index 75ff93c..cc51ecb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pake-cli", - "version": "0.1.1", + "version": "0.1.2", "description": "🤱🏻 很简单的用 Rust 打包网页生成很小的桌面 App 🤱🏻 A simple way to make any web page a desktop application using Rust.", "bin": { "pake": "./cli.js" @@ -16,8 +16,7 @@ "files": [ "dist", "src-tauri", - "cli.js", - "pake-default.icns" + "cli.js" ], "scripts": { "start": "npm run dev", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 99c601e..3870bb4 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -42,6 +42,7 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" name = "app" version = "0.1.0" dependencies = [ + "home", "image", "serde", "serde_json", @@ -1105,6 +1106,15 @@ dependencies = [ "libc", ] +[[package]] +name = "home" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" +dependencies = [ + "winapi", +] + [[package]] name = "html5ever" version = "0.25.2" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c010fb1..be38701 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -19,6 +19,7 @@ serde_json = "1.0.88" serde = { version = "1.0.147", features = ["derive"] } tauri = { version = "1.2.1", features = [] } image = "0.24.5" +home = "0.5" tauri-utils = "1.2.1" webbrowser = "0.8.2" wry = "0.23.1" diff --git a/src-tauri/assets/main.wxs b/src-tauri/assets/main.wxs new file mode 100644 index 0000000..4b92bd7 --- /dev/null +++ b/src-tauri/assets/main.wxs @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + + + + + {{#if allow_downgrades}} + + {{else}} + + {{/if}} + + + Installed AND NOT UPGRADINGPRODUCTCODE + + + + + {{#if banner_path}} + + {{/if}} + {{#if dialog_image_path}} + + {{/if}} + {{#if license}} + + {{/if}} + + + + + + + + + + + + + + + + + + + WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed + + + + {{#unless license}} + + 1 + 1 + {{/unless}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{#each binaries as |bin| ~}} + + + + {{/each~}} + {{#if enable_elevated_update_task}} + + + + + + + + + + {{/if}} + {{{resources}}} + + + + + + + + + + + + + + + + + + + + + {{#each merge_modules as |msm| ~}} + + + + + + + + {{/each~}} + + + + + + {{#each resource_file_ids as |resource_file_id| ~}} + + {{/each~}} + + {{#if enable_elevated_update_task}} + + + + {{/if}} + + + + + + + + + + + {{#each binaries as |bin| ~}} + + {{/each~}} + + + + + {{#each component_group_refs as |id| ~}} + + {{/each~}} + {{#each component_refs as |id| ~}} + + {{/each~}} + {{#each feature_group_refs as |id| ~}} + + {{/each~}} + {{#each feature_refs as |id| ~}} + + {{/each~}} + {{#each merge_refs as |id| ~}} + + {{/each~}} + + + {{#if install_webview}} + + + + + + + {{#if download_bootstrapper}} + + + + + + + {{/if}} + + + {{#if webview2_bootstrapper_path}} + + + + + + + + {{/if}} + + + {{#if webview2_installer_path}} + + + + + + + + {{/if}} + + {{/if}} + + {{#if enable_elevated_update_task}} + + + + + NOT(REMOVE) + + + + + + + (REMOVE = "ALL") AND NOT UPGRADINGPRODUCTCODE + + + {{/if}} + + + + diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 80bd9f9..83943aa 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -23,7 +23,7 @@ use wry::application::{ #[cfg(target_os = "windows")] use wry::application::window::Icon; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "windows"))] use wry::webview::WebContext; fn main() -> wry::Result<()> { @@ -147,35 +147,21 @@ fn main() -> wry::Result<()> { .with_back_forward_navigation_gestures(true) .build()?; - #[cfg(target_os = "windows")] - let webview = WebViewBuilder::new(window)? - // .with_user_agent(user_agent_string) - // .with_accept_first_mouse(true) - .with_url(&url.to_string())? - .with_devtools(cfg!(feature = "devtools")) - .with_initialization_script(include_str!("pake.js")) - .with_ipc_handler(handler) - .build()?; - - // 自定义cookie文件夹,仅用于Linux - // Custom Cookie folder, only for Linux - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "windows"))] let webview = { - let user = std::env::var_os("USER"); - let config_path = match user { - Some(v) => format!( - "/home/{}/.config/{}", - v.into_string().unwrap(), - package_name, - ), - None => panic!("can't found any user"), + let home_dir = match home::home_dir() { + Some(path1) => path1, + None => panic!("Error, can't found you home dir!!"), }; - let data_path = std::path::PathBuf::from(&config_path); - if !std::path::Path::new(&data_path).exists() { - std::fs::create_dir(&data_path) - .unwrap_or_else(|_| panic!("can't create dir {}", &config_path)); + #[cfg(target_os = "windows")] + let data_dir = home_dir.join("AppData").join("Roaming").join(package_name); + #[cfg(target_os = "linux")] + let data_dir = home_dir.join(".config").join(package_name); + if !data_dir.exists() { + std::fs::create_dir(&data_dir) + .unwrap_or_else(|_| panic!("can't create dir {}", data_dir.display())); } - let mut web_content = WebContext::new(Some(data_path)); + let mut web_content = WebContext::new(Some(data_dir)); WebViewBuilder::new(window)? // .with_user_agent(user_agent_string) .with_url(&url.to_string())? diff --git a/src-tauri/tauri.windows.conf.json b/src-tauri/tauri.windows.conf.json index 357f830..0c2eb5b 100644 --- a/src-tauri/tauri.windows.conf.json +++ b/src-tauri/tauri.windows.conf.json @@ -19,7 +19,8 @@ "digestAlgorithm": "sha256", "timestampUrl": "", "wix": { - "language": ["en-US"] + "language": ["en-US"], + "template": "assets/main.wxs" } } }