pake-cli 1.3.0 → 2.0.0-alpha6
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 +32 -28
- package/dist/about_pake.html +16 -0
- package/dist/cli.js +388 -85
- package/package.json +3 -2
- package/src-tauri/.cargo/cn_config.bak +14 -0
- package/src-tauri/Cargo.lock +1420 -586
- package/src-tauri/Cargo.toml +13 -14
- package/src-tauri/icons/loop.icns +0 -0
- package/src-tauri/pake.json +28 -0
- package/src-tauri/png/loop_256.ico +0 -0
- package/src-tauri/png/loop_32.ico +0 -0
- package/src-tauri/png/loop_512.png +0 -0
- package/src-tauri/src/app/config.rs +63 -0
- package/src-tauri/src/app/invoke.rs +47 -0
- package/src-tauri/src/app/menu.rs +114 -0
- package/src-tauri/src/app/mod.rs +4 -0
- package/src-tauri/src/app/window.rs +49 -0
- package/src-tauri/src/inject/component.js +143 -0
- package/src-tauri/src/inject/event.js +175 -0
- package/src-tauri/src/{pake.js → inject/style.js} +44 -140
- package/src-tauri/src/main.rs +58 -267
- package/src-tauri/src/util.rs +78 -0
- package/src-tauri/tauri.conf.json +26 -29
- package/src-tauri/tauri.linux.conf.json +18 -30
package/src-tauri/Cargo.toml
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "app"
|
|
3
3
|
version = "0.1.0"
|
|
4
|
-
description = "
|
|
4
|
+
description = "🤱🏻 Turn any webpage into a desktop app with Rust."
|
|
5
5
|
authors = ["Tw93"]
|
|
6
|
-
license = ""
|
|
7
|
-
repository = ""
|
|
6
|
+
license = "MIT"
|
|
7
|
+
repository = "https://github.com/tw93/Pake"
|
|
8
8
|
default-run = "app"
|
|
9
9
|
edition = "2021"
|
|
10
10
|
rust-version = "1.63.0"
|
|
@@ -15,15 +15,11 @@ rust-version = "1.63.0"
|
|
|
15
15
|
tauri-build = { version = "1.2.1", features = [] }
|
|
16
16
|
|
|
17
17
|
[dependencies]
|
|
18
|
-
serde_json = "1.0.
|
|
19
|
-
serde = { version = "1.0.
|
|
20
|
-
tauri = { version = "1.2.4", features = [] }
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
tauri-utils = "1.2.1"
|
|
24
|
-
webbrowser = "0.8.7"
|
|
25
|
-
wry = "0.23.4"
|
|
26
|
-
dirs = "4.0"
|
|
18
|
+
serde_json = "1.0.89"
|
|
19
|
+
serde = { version = "1.0.150", features = ["derive"] }
|
|
20
|
+
tauri = { version = "1.2.4", features = ["api-all", "system-tray"] }
|
|
21
|
+
download_rs = { version = "0.2.0", features = ["sync_download"] }
|
|
22
|
+
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
|
|
27
23
|
|
|
28
24
|
[features]
|
|
29
25
|
# by default Tauri runs in production mode
|
|
@@ -32,5 +28,8 @@ default = ["custom-protocol"]
|
|
|
32
28
|
# this feature is used used for production builds where `devPath` points to the filesystem
|
|
33
29
|
# DO NOT remove this
|
|
34
30
|
custom-protocol = ["tauri/custom-protocol"]
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
|
|
32
|
+
# Pay attention to the code size during optimization in order to generate smaller binary files.
|
|
33
|
+
# [profile.release]
|
|
34
|
+
# opt-level = "s"
|
|
35
|
+
# lto = true
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"windows": [
|
|
3
|
+
{
|
|
4
|
+
"url": "https://weread.qq.com/",
|
|
5
|
+
"transparent": true,
|
|
6
|
+
"fullscreen": false,
|
|
7
|
+
"width": 1200,
|
|
8
|
+
"height": 780,
|
|
9
|
+
"resizable": true,
|
|
10
|
+
"url_type": "web"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"user_agent": {
|
|
14
|
+
"macos": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15",
|
|
15
|
+
"linux": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
|
|
16
|
+
"windows": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
|
|
17
|
+
},
|
|
18
|
+
"menu": {
|
|
19
|
+
"macos": true,
|
|
20
|
+
"linux": false,
|
|
21
|
+
"windows": false
|
|
22
|
+
},
|
|
23
|
+
"system_tray": {
|
|
24
|
+
"macos": false,
|
|
25
|
+
"linux": true,
|
|
26
|
+
"windows": true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
use serde::Deserialize;
|
|
2
|
+
|
|
3
|
+
#[derive(Debug, Deserialize)]
|
|
4
|
+
pub struct WindowConfig {
|
|
5
|
+
pub url: String,
|
|
6
|
+
pub transparent: bool,
|
|
7
|
+
pub fullscreen: bool,
|
|
8
|
+
pub width: f64,
|
|
9
|
+
pub height: f64,
|
|
10
|
+
pub resizable: bool,
|
|
11
|
+
pub url_type: String,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#[derive(Debug, Deserialize)]
|
|
15
|
+
pub struct PlatformSpecific<T> {
|
|
16
|
+
pub macos: T,
|
|
17
|
+
pub linux: T,
|
|
18
|
+
pub windows: T,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
impl<T> PlatformSpecific<T> {
|
|
22
|
+
pub const fn get(&self) -> &T {
|
|
23
|
+
#[cfg(target_os = "macos")]
|
|
24
|
+
let platform = &self.macos;
|
|
25
|
+
#[cfg(target_os = "linux")]
|
|
26
|
+
let platform = &self.linux;
|
|
27
|
+
#[cfg(target_os = "windows")]
|
|
28
|
+
let platform = &self.windows;
|
|
29
|
+
|
|
30
|
+
platform
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
impl<T> PlatformSpecific<T>
|
|
35
|
+
where
|
|
36
|
+
T: Copy,
|
|
37
|
+
{
|
|
38
|
+
pub const fn copied(&self) -> T {
|
|
39
|
+
*self.get()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pub type UserAgent = PlatformSpecific<String>;
|
|
44
|
+
pub type FunctionON = PlatformSpecific<bool>;
|
|
45
|
+
|
|
46
|
+
#[derive(Debug, Deserialize)]
|
|
47
|
+
pub struct PakeConfig {
|
|
48
|
+
pub windows: Vec<WindowConfig>,
|
|
49
|
+
pub user_agent: UserAgent,
|
|
50
|
+
pub menu: FunctionON,
|
|
51
|
+
pub system_tray: FunctionON,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
impl PakeConfig {
|
|
55
|
+
pub fn show_menu(&self) -> bool {
|
|
56
|
+
self.menu.copied()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#[cfg(not(target_os = "macos"))]
|
|
60
|
+
pub fn show_system_tray(&self) -> bool {
|
|
61
|
+
self.system_tray.copied()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
use crate::util::{check_file_or_append, get_download_message, show_toast};
|
|
2
|
+
use download_rs::sync_download::Download;
|
|
3
|
+
use tauri::{api, command, AppHandle, Manager, Window};
|
|
4
|
+
|
|
5
|
+
#[derive(serde::Deserialize)]
|
|
6
|
+
pub struct DownloadFileParams {
|
|
7
|
+
url: String,
|
|
8
|
+
filename: String,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
#[command]
|
|
12
|
+
pub fn drag_window(app: AppHandle) {
|
|
13
|
+
app.get_window("pake").unwrap().start_dragging().unwrap();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#[command]
|
|
17
|
+
pub fn fullscreen(app: AppHandle) {
|
|
18
|
+
let win = app.get_window("pake").unwrap();
|
|
19
|
+
if win.is_fullscreen().unwrap() {
|
|
20
|
+
win.set_fullscreen(false).unwrap();
|
|
21
|
+
} else {
|
|
22
|
+
win.set_fullscreen(true).unwrap();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#[tauri::command]
|
|
27
|
+
pub fn open_browser(app: AppHandle, url: String) {
|
|
28
|
+
api::shell::open(&app.shell_scope(), url, None).unwrap();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#[command]
|
|
32
|
+
pub async fn download_file(app: AppHandle, params: DownloadFileParams) -> Result<(), String> {
|
|
33
|
+
let window: Window = app.get_window("pake").unwrap();
|
|
34
|
+
let output_path = api::path::download_dir().unwrap().join(params.filename);
|
|
35
|
+
let file_path = check_file_or_append(output_path.to_str().unwrap());
|
|
36
|
+
let download = Download::new(¶ms.url, Some(&file_path), None);
|
|
37
|
+
match download.download() {
|
|
38
|
+
Ok(_) => {
|
|
39
|
+
show_toast(&window, &get_download_message());
|
|
40
|
+
Ok(())
|
|
41
|
+
}
|
|
42
|
+
Err(e) => {
|
|
43
|
+
show_toast(&window, &e.to_string());
|
|
44
|
+
Err(e.to_string())
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
use tauri::MenuItem;
|
|
2
|
+
|
|
3
|
+
use tauri::{CustomMenuItem, Menu, Submenu, WindowMenuEvent};
|
|
4
|
+
|
|
5
|
+
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
|
6
|
+
use tauri::{Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowBuilder, WindowUrl};
|
|
7
|
+
|
|
8
|
+
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
|
9
|
+
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
|
|
10
|
+
|
|
11
|
+
pub fn get_menu() -> Menu {
|
|
12
|
+
let close = CustomMenuItem::new("close".to_string(), "Close Window").accelerator("CmdOrCtrl+W");
|
|
13
|
+
let goto_url_item = CustomMenuItem::new("goto_url".to_string(), "Go to URL...")
|
|
14
|
+
.accelerator("CmdOrCtrl+Shift+L");
|
|
15
|
+
let first_menu = Menu::new()
|
|
16
|
+
.add_native_item(MenuItem::Copy)
|
|
17
|
+
.add_native_item(MenuItem::Cut)
|
|
18
|
+
.add_native_item(MenuItem::Paste)
|
|
19
|
+
.add_native_item(MenuItem::Undo)
|
|
20
|
+
.add_native_item(MenuItem::Redo)
|
|
21
|
+
.add_native_item(MenuItem::SelectAll)
|
|
22
|
+
.add_native_item(MenuItem::Separator)
|
|
23
|
+
.add_item(goto_url_item)
|
|
24
|
+
.add_native_item(MenuItem::Separator)
|
|
25
|
+
.add_native_item(MenuItem::EnterFullScreen)
|
|
26
|
+
.add_native_item(MenuItem::Minimize)
|
|
27
|
+
.add_native_item(MenuItem::Hide)
|
|
28
|
+
.add_native_item(MenuItem::HideOthers)
|
|
29
|
+
.add_native_item(MenuItem::ShowAll)
|
|
30
|
+
.add_native_item(MenuItem::Separator)
|
|
31
|
+
.add_item(close)
|
|
32
|
+
.add_native_item(MenuItem::Quit);
|
|
33
|
+
|
|
34
|
+
let app_menu = Submenu::new("File", first_menu);
|
|
35
|
+
Menu::new().add_submenu(app_menu)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub fn menu_event_handle(event: WindowMenuEvent) {
|
|
39
|
+
if event.menu_item_id() == "close" {
|
|
40
|
+
event.window().minimize().expect("can't minimize window");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if event.menu_item_id() == "goto_url" {
|
|
44
|
+
let js_code = "showUrlModal();";
|
|
45
|
+
event.window().eval(js_code).unwrap();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
|
50
|
+
pub fn get_system_tray(show_menu: bool) -> SystemTray {
|
|
51
|
+
let hide_app = CustomMenuItem::new("hide_app".to_string(), "Hide App");
|
|
52
|
+
let show_app = CustomMenuItem::new("show_app".to_string(), "Show App");
|
|
53
|
+
let quit = CustomMenuItem::new("quit".to_string(), "Quit");
|
|
54
|
+
let about = CustomMenuItem::new("about".to_string(), "About");
|
|
55
|
+
let tray_menu = SystemTrayMenu::new().add_item(hide_app).add_item(show_app);
|
|
56
|
+
if show_menu {
|
|
57
|
+
let hide_menu = CustomMenuItem::new("hide_menu".to_string(), "Hide Menu");
|
|
58
|
+
let show_menu = CustomMenuItem::new("show_menu".to_string(), "Show Menu");
|
|
59
|
+
let tray_menu = tray_menu
|
|
60
|
+
.add_item(hide_menu)
|
|
61
|
+
.add_item(show_menu)
|
|
62
|
+
.add_item(quit)
|
|
63
|
+
.add_item(about);
|
|
64
|
+
SystemTray::new().with_menu(tray_menu)
|
|
65
|
+
} else {
|
|
66
|
+
let tray_menu = tray_menu.add_item(quit).add_item(about);
|
|
67
|
+
SystemTray::new().with_menu(tray_menu)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
|
72
|
+
pub fn system_tray_handle(app: &tauri::AppHandle, event: SystemTrayEvent) {
|
|
73
|
+
if let SystemTrayEvent::MenuItemClick { tray_id: _, id, .. } = event {
|
|
74
|
+
match id.as_str() {
|
|
75
|
+
"hide_app" => {
|
|
76
|
+
app.get_window("pake").unwrap().hide().unwrap();
|
|
77
|
+
}
|
|
78
|
+
"show_app" => {
|
|
79
|
+
app.get_window("pake").unwrap().show().unwrap();
|
|
80
|
+
}
|
|
81
|
+
"hide_menu" => {
|
|
82
|
+
app.get_window("pake")
|
|
83
|
+
.unwrap()
|
|
84
|
+
.menu_handle()
|
|
85
|
+
.hide()
|
|
86
|
+
.unwrap();
|
|
87
|
+
}
|
|
88
|
+
"show_menu" => {
|
|
89
|
+
app.get_window("pake")
|
|
90
|
+
.unwrap()
|
|
91
|
+
.menu_handle()
|
|
92
|
+
.show()
|
|
93
|
+
.unwrap();
|
|
94
|
+
}
|
|
95
|
+
"quit" => {
|
|
96
|
+
let _res = app.save_window_state(StateFlags::all());
|
|
97
|
+
std::process::exit(0);
|
|
98
|
+
}
|
|
99
|
+
"about" => {
|
|
100
|
+
let _about_window = WindowBuilder::new(
|
|
101
|
+
app,
|
|
102
|
+
"about",
|
|
103
|
+
WindowUrl::App(std::path::PathBuf::from("about_pake.html")),
|
|
104
|
+
)
|
|
105
|
+
.resizable(true)
|
|
106
|
+
.title("About")
|
|
107
|
+
.inner_size(600.0, 400.0)
|
|
108
|
+
.build()
|
|
109
|
+
.expect("can't open about!");
|
|
110
|
+
}
|
|
111
|
+
_ => {}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
use crate::app::config::PakeConfig;
|
|
2
|
+
use std::path::PathBuf;
|
|
3
|
+
use tauri::{App, Window, WindowBuilder, WindowUrl};
|
|
4
|
+
|
|
5
|
+
#[cfg(target_os = "macos")]
|
|
6
|
+
use tauri::TitleBarStyle;
|
|
7
|
+
|
|
8
|
+
pub fn get_window(app: &mut App, config: PakeConfig, _data_dir: PathBuf) -> Window {
|
|
9
|
+
let window_config = config
|
|
10
|
+
.windows
|
|
11
|
+
.first()
|
|
12
|
+
.expect("At least one window configuration is required");
|
|
13
|
+
|
|
14
|
+
let user_agent = config.user_agent.get();
|
|
15
|
+
|
|
16
|
+
let url = match window_config.url_type.as_str() {
|
|
17
|
+
"web" => WindowUrl::App(window_config.url.parse().unwrap()),
|
|
18
|
+
"local" => WindowUrl::App(PathBuf::from(&window_config.url)),
|
|
19
|
+
_ => panic!("url type can only be web or local"),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
let mut window_builder = WindowBuilder::new(app, "pake", url)
|
|
23
|
+
.title("")
|
|
24
|
+
.user_agent(user_agent)
|
|
25
|
+
.visible(false) // Prevent initial shaking
|
|
26
|
+
.resizable(window_config.resizable)
|
|
27
|
+
.fullscreen(window_config.fullscreen)
|
|
28
|
+
.inner_size(window_config.width, window_config.height)
|
|
29
|
+
.initialization_script(include_str!("../inject/style.js"))
|
|
30
|
+
.initialization_script(include_str!("../inject/event.js"))
|
|
31
|
+
.initialization_script(include_str!("../inject/component.js"));
|
|
32
|
+
|
|
33
|
+
#[cfg(target_os = "macos")]
|
|
34
|
+
{
|
|
35
|
+
let title_bar_style = if window_config.transparent {
|
|
36
|
+
TitleBarStyle::Overlay
|
|
37
|
+
} else {
|
|
38
|
+
TitleBarStyle::Visible
|
|
39
|
+
};
|
|
40
|
+
window_builder = window_builder.title_bar_style(title_bar_style)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#[cfg(not(target_os = "macos"))]
|
|
44
|
+
{
|
|
45
|
+
window_builder = window_builder.data_directory(_data_dir);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
window_builder.build().unwrap()
|
|
49
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
2
|
+
// Create a modal
|
|
3
|
+
const modalHtml = `
|
|
4
|
+
<div id="pakeUrlModal" class="pake-modal">
|
|
5
|
+
<div class="pake-modal-container">
|
|
6
|
+
<div class="pake-modal-content">
|
|
7
|
+
<label for="pakeUrlInput">Enter URL to navigate anywhere</label>
|
|
8
|
+
<input type="text" id="pakeUrlInput" />
|
|
9
|
+
<button id="pakeUrlSubmit">Submit</button>
|
|
10
|
+
<button id="pakeUrlClose">Close</button>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const modalStyle = `
|
|
17
|
+
.pake-modal {
|
|
18
|
+
display: none;
|
|
19
|
+
position: fixed;
|
|
20
|
+
z-index: 1000;
|
|
21
|
+
left: 0;
|
|
22
|
+
top: 0;
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: 100%;
|
|
25
|
+
background-color: rgba(0, 0, 0, 0.4);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.pake-modal-container {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
width: 100%;
|
|
33
|
+
height: 100%;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.pake-modal-content {
|
|
37
|
+
background-color: #fff;
|
|
38
|
+
padding: 20px;
|
|
39
|
+
border-radius: 10px;
|
|
40
|
+
width: 80%;
|
|
41
|
+
max-width: 400px;
|
|
42
|
+
font-size:14px;
|
|
43
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.pake-modal-content label {
|
|
47
|
+
display: block;
|
|
48
|
+
color: #11182B;
|
|
49
|
+
margin-bottom: 12px;
|
|
50
|
+
font-weight: bold;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.pake-modal-content input[type="text"] {
|
|
54
|
+
width: 90%;
|
|
55
|
+
padding: 8px;
|
|
56
|
+
border: 1px solid #ccc;
|
|
57
|
+
border-radius: 4px;
|
|
58
|
+
font-size: 14px;
|
|
59
|
+
margin-bottom: 12px;
|
|
60
|
+
outline: none;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.pake-modal-content button {
|
|
64
|
+
background: #11182B;
|
|
65
|
+
color: #FFF;
|
|
66
|
+
padding: 6px 14px;
|
|
67
|
+
border-radius: 4px;
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
margin-right: 4px;
|
|
70
|
+
font-size:14px;
|
|
71
|
+
border: 1px solid #11182B;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#pakeUrlClose{
|
|
75
|
+
background: #fff;
|
|
76
|
+
color: #11182B;
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
const modalDiv = document.createElement('div');
|
|
81
|
+
modalDiv.innerHTML = modalHtml;
|
|
82
|
+
document.body.appendChild(modalDiv);
|
|
83
|
+
|
|
84
|
+
const modalStyleElement = document.createElement('style');
|
|
85
|
+
modalStyleElement.innerText = modalStyle;
|
|
86
|
+
document.head.appendChild(modalStyleElement);
|
|
87
|
+
|
|
88
|
+
const urlModal = document.getElementById('pakeUrlModal');
|
|
89
|
+
const urlInput = document.getElementById('pakeUrlInput');
|
|
90
|
+
const urlSubmit = document.getElementById('pakeUrlSubmit');
|
|
91
|
+
const urlClose = document.getElementById('pakeUrlClose');
|
|
92
|
+
|
|
93
|
+
urlSubmit.onclick = function () {
|
|
94
|
+
const url = urlInput.value;
|
|
95
|
+
if (url) {
|
|
96
|
+
window.location.href = url;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
urlClose.onclick = function () {
|
|
101
|
+
urlModal.style.display = 'none';
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
urlInput.addEventListener('keydown', function (event) {
|
|
105
|
+
if (event.key === 'Enter') {
|
|
106
|
+
const url = urlInput.value;
|
|
107
|
+
if (url) {
|
|
108
|
+
window.location.href = url;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
document.addEventListener('keydown', function (event) {
|
|
114
|
+
if (event.key === 'Escape' && urlModal.style.display === 'block') {
|
|
115
|
+
urlModal.style.display = 'none';
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
window.showUrlModal = function () {
|
|
120
|
+
urlModal.style.display = 'block';
|
|
121
|
+
urlInput.focus();
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Toast
|
|
125
|
+
function pakeToast(msg) {
|
|
126
|
+
const m = document.createElement('div');
|
|
127
|
+
m.innerHTML = msg;
|
|
128
|
+
m.style.cssText =
|
|
129
|
+
'max-width:60%;min-width: 80px;padding:0 12px;height: 32px;color: rgb(255, 255, 255);line-height: 32px;text-align: center;border-radius: 8px;position: fixed; bottom:24px;right: 28px;z-index: 999999;background: rgba(0, 0, 0,.8);font-size: 13px;';
|
|
130
|
+
document.body.appendChild(m);
|
|
131
|
+
setTimeout(function () {
|
|
132
|
+
const d = 0.5;
|
|
133
|
+
m.style.transition =
|
|
134
|
+
'transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
|
|
135
|
+
m.style.opacity = '0';
|
|
136
|
+
setTimeout(function () {
|
|
137
|
+
document.body.removeChild(m);
|
|
138
|
+
}, d * 1000);
|
|
139
|
+
}, 3000);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
window.pakeToast = pakeToast;
|
|
143
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const shortcuts = {
|
|
2
|
+
ArrowUp: () => scrollTo(0, 0),
|
|
3
|
+
ArrowDown: () => scrollTo(0, document.body.scrollHeight),
|
|
4
|
+
ArrowLeft: () => window.history.back(),
|
|
5
|
+
ArrowRight: () => window.history.forward(),
|
|
6
|
+
'[': () => window.history.back(),
|
|
7
|
+
']': () => window.history.forward(),
|
|
8
|
+
r: () => window.location.reload(),
|
|
9
|
+
'-': () => zoomOut(),
|
|
10
|
+
'=': () => zoomIn(),
|
|
11
|
+
'+': () => zoomIn(),
|
|
12
|
+
0: () => setZoom('100%'),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function setZoom(zoom) {
|
|
16
|
+
const html = document.getElementsByTagName('html')[0];
|
|
17
|
+
html.style.zoom = zoom;
|
|
18
|
+
window.localStorage.setItem('htmlZoom', zoom);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function zoomCommon(zoomChange) {
|
|
22
|
+
const currentZoom = window.localStorage.getItem('htmlZoom') || '100%';
|
|
23
|
+
setZoom(zoomChange(currentZoom));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function zoomIn() {
|
|
27
|
+
zoomCommon((currentZoom) => `${Math.min(parseInt(currentZoom) + 10, 200)}%`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function zoomOut() {
|
|
31
|
+
zoomCommon((currentZoom) => `${Math.max(parseInt(currentZoom) - 10, 30)}%`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function handleShortcut(event) {
|
|
35
|
+
if (shortcuts[event.key]) {
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
shortcuts[event.key]();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//这里参考 ChatGPT 的代码
|
|
42
|
+
const uid = () => window.crypto.getRandomValues(new Uint32Array(1))[0];
|
|
43
|
+
function transformCallback(callback = () => {}, once = false) {
|
|
44
|
+
const identifier = uid();
|
|
45
|
+
const prop = `_${identifier}`;
|
|
46
|
+
Object.defineProperty(window, prop, {
|
|
47
|
+
value: (result) => {
|
|
48
|
+
if (once) {
|
|
49
|
+
Reflect.deleteProperty(window, prop);
|
|
50
|
+
}
|
|
51
|
+
return callback(result);
|
|
52
|
+
},
|
|
53
|
+
writable: false,
|
|
54
|
+
configurable: true,
|
|
55
|
+
});
|
|
56
|
+
return identifier;
|
|
57
|
+
}
|
|
58
|
+
async function invoke(cmd, args) {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
if (!window.__TAURI_POST_MESSAGE__)
|
|
61
|
+
reject('__TAURI_POST_MESSAGE__ does not exist~');
|
|
62
|
+
const callback = transformCallback((e) => {
|
|
63
|
+
resolve(e);
|
|
64
|
+
Reflect.deleteProperty(window, `_${error}`);
|
|
65
|
+
}, true);
|
|
66
|
+
const error = transformCallback((e) => {
|
|
67
|
+
reject(e);
|
|
68
|
+
Reflect.deleteProperty(window, `_${callback}`);
|
|
69
|
+
}, true);
|
|
70
|
+
window.__TAURI_POST_MESSAGE__({
|
|
71
|
+
cmd,
|
|
72
|
+
callback,
|
|
73
|
+
error,
|
|
74
|
+
...args,
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
80
|
+
const topDom = document.createElement('div');
|
|
81
|
+
topDom.id = 'pack-top-dom';
|
|
82
|
+
document.body.appendChild(topDom);
|
|
83
|
+
const domEl = document.getElementById('pack-top-dom');
|
|
84
|
+
|
|
85
|
+
domEl.addEventListener('mousedown', (e) => {
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
if (e.buttons === 1 && e.detail !== 2) {
|
|
88
|
+
invoke('drag_window');
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
domEl.addEventListener('touchstart', () => {
|
|
93
|
+
invoke('drag_window');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
domEl.addEventListener('dblclick', () => {
|
|
97
|
+
invoke('fullscreen');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
document.addEventListener('keyup', (event) => {
|
|
101
|
+
if (/windows|linux/i.test(navigator.userAgent) && event.ctrlKey) {
|
|
102
|
+
handleShortcut(event);
|
|
103
|
+
}
|
|
104
|
+
if (/macintosh|mac os x/i.test(navigator.userAgent) && event.metaKey) {
|
|
105
|
+
handleShortcut(event);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
document.addEventListener('click', (e) => {
|
|
110
|
+
const anchorElement = e.target.closest('a');
|
|
111
|
+
|
|
112
|
+
if (anchorElement && anchorElement.href) {
|
|
113
|
+
const target = anchorElement.target;
|
|
114
|
+
anchorElement.target = '_self';
|
|
115
|
+
const hrefUrl = new URL(anchorElement.href);
|
|
116
|
+
const absoluteUrl = hrefUrl.href;
|
|
117
|
+
|
|
118
|
+
// Handling external link redirection.
|
|
119
|
+
if (window.location.host !== hrefUrl.host && (target === '_blank'|| target === '_new')) {
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
invoke('open_browser', { url: absoluteUrl });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Process download links for Rust to handle.
|
|
126
|
+
if (/\.[a-zA-Z0-9]+$/i.test(removeUrlParameters(absoluteUrl))) {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
invoke('download_file', {
|
|
129
|
+
params: {
|
|
130
|
+
url: absoluteUrl,
|
|
131
|
+
filename: getFilenameFromUrl(absoluteUrl),
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
setDefaultZoom();
|
|
139
|
+
|
|
140
|
+
// Rewrite the window.open function.
|
|
141
|
+
const originalWindowOpen = window.open;
|
|
142
|
+
window.open = function (url, name, specs) {
|
|
143
|
+
// Apple login and google login
|
|
144
|
+
if (name === 'AppleAuthentication') {
|
|
145
|
+
//do nothing
|
|
146
|
+
} else if (specs.includes('height=') || specs.includes('width=')) {
|
|
147
|
+
location.href = url;
|
|
148
|
+
} else {
|
|
149
|
+
const baseUrl = window.location.origin + window.location.pathname;
|
|
150
|
+
const hrefUrl = new URL(url, baseUrl);
|
|
151
|
+
invoke('open_browser', { url: hrefUrl.href });
|
|
152
|
+
}
|
|
153
|
+
// Call the original window.open function to maintain its normal functionality.
|
|
154
|
+
return originalWindowOpen.call(window, url, name, specs);
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
function setDefaultZoom() {
|
|
159
|
+
const htmlZoom = window.localStorage.getItem('htmlZoom');
|
|
160
|
+
if (htmlZoom) {
|
|
161
|
+
setZoom(htmlZoom);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function getFilenameFromUrl(url) {
|
|
166
|
+
const urlPath = new URL(url).pathname;
|
|
167
|
+
const filename = urlPath.substring(urlPath.lastIndexOf('/') + 1);
|
|
168
|
+
return filename;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function removeUrlParameters(url) {
|
|
172
|
+
const parsedUrl = new URL(url);
|
|
173
|
+
parsedUrl.search = '';
|
|
174
|
+
return parsedUrl.toString();
|
|
175
|
+
}
|