@bghitcode/bghitapp 1.0.0
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/LICENSE +21 -0
- package/README.md +203 -0
- package/dist/cli.js +2995 -0
- package/package.json +104 -0
- package/src-tauri/Cargo.lock +5966 -0
- package/src-tauri/Cargo.toml +59 -0
- package/src-tauri/Info.plist +14 -0
- package/src-tauri/assets/macos/dmg/background.png +0 -0
- package/src-tauri/assets/main.wxs +350 -0
- package/src-tauri/bghitapp.json +42 -0
- package/src-tauri/build.rs +5 -0
- package/src-tauri/capabilities/default.json +29 -0
- package/src-tauri/entitlements.plist +7 -0
- package/src-tauri/icons/chatgpt.icns +0 -0
- package/src-tauri/icons/deepseek.icns +0 -0
- package/src-tauri/icons/excalidraw.icns +0 -0
- package/src-tauri/icons/flomo.icns +0 -0
- package/src-tauri/icons/gemini.icns +0 -0
- package/src-tauri/icons/grok.icns +0 -0
- package/src-tauri/icons/icon.icns +0 -0
- package/src-tauri/icons/icon.png +0 -0
- package/src-tauri/icons/lizhi.icns +0 -0
- package/src-tauri/icons/programmusic.icns +0 -0
- package/src-tauri/icons/qwerty.icns +0 -0
- package/src-tauri/icons/twitter.icns +0 -0
- package/src-tauri/icons/wechat.icns +0 -0
- package/src-tauri/icons/weekly.icns +0 -0
- package/src-tauri/icons/weread.icns +0 -0
- package/src-tauri/icons/xiaohongshu.icns +0 -0
- package/src-tauri/icons/youtube.icns +0 -0
- package/src-tauri/icons/youtubemusic.icns +0 -0
- package/src-tauri/rust_proxy.toml +10 -0
- package/src-tauri/src/app/config.rs +100 -0
- package/src-tauri/src/app/invoke.rs +242 -0
- package/src-tauri/src/app/menu.rs +324 -0
- package/src-tauri/src/app/mod.rs +6 -0
- package/src-tauri/src/app/setup.rs +172 -0
- package/src-tauri/src/app/window.rs +577 -0
- package/src-tauri/src/inject/auth.js +75 -0
- package/src-tauri/src/inject/custom.js +0 -0
- package/src-tauri/src/inject/event.js +1111 -0
- package/src-tauri/src/inject/find.js +708 -0
- package/src-tauri/src/inject/fullscreen.js +253 -0
- package/src-tauri/src/inject/offline.js +68 -0
- package/src-tauri/src/inject/splash-transition.js +13 -0
- package/src-tauri/src/inject/style.js +505 -0
- package/src-tauri/src/inject/theme_refresh.js +59 -0
- package/src-tauri/src/inject/toast.js +22 -0
- package/src-tauri/src/lib.rs +227 -0
- package/src-tauri/src/main.rs +8 -0
- package/src-tauri/src/util.rs +245 -0
- package/src-tauri/tauri.conf.json +20 -0
- package/src-tauri/tauri.linux.conf.json +12 -0
- package/src-tauri/tauri.macos.conf.json +28 -0
- package/src-tauri/tauri.windows.conf.json +15 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
use serde::{Deserialize, Serialize};
|
|
2
|
+
|
|
3
|
+
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
4
|
+
pub struct WindowConfig {
|
|
5
|
+
pub url: String,
|
|
6
|
+
pub hide_title_bar: bool,
|
|
7
|
+
pub fullscreen: bool,
|
|
8
|
+
pub maximize: bool,
|
|
9
|
+
pub width: f64,
|
|
10
|
+
pub height: f64,
|
|
11
|
+
pub resizable: bool,
|
|
12
|
+
pub url_type: String,
|
|
13
|
+
pub always_on_top: bool,
|
|
14
|
+
pub dark_mode: bool,
|
|
15
|
+
pub disabled_web_shortcuts: bool,
|
|
16
|
+
pub activation_shortcut: String,
|
|
17
|
+
pub hide_on_close: bool,
|
|
18
|
+
pub incognito: bool,
|
|
19
|
+
pub title: Option<String>,
|
|
20
|
+
pub enable_wasm: bool,
|
|
21
|
+
pub enable_drag_drop: bool,
|
|
22
|
+
#[serde(default)]
|
|
23
|
+
pub new_window: bool,
|
|
24
|
+
pub start_to_tray: bool,
|
|
25
|
+
#[serde(default)]
|
|
26
|
+
pub force_internal_navigation: bool,
|
|
27
|
+
#[serde(default)]
|
|
28
|
+
pub internal_url_regex: String,
|
|
29
|
+
#[serde(default)]
|
|
30
|
+
pub enable_find: bool,
|
|
31
|
+
#[serde(default = "default_zoom")]
|
|
32
|
+
pub zoom: u32,
|
|
33
|
+
#[serde(default)]
|
|
34
|
+
pub min_width: f64,
|
|
35
|
+
#[serde(default)]
|
|
36
|
+
pub min_height: f64,
|
|
37
|
+
#[serde(default)]
|
|
38
|
+
pub ignore_certificate_errors: bool,
|
|
39
|
+
#[serde(default)]
|
|
40
|
+
pub splash: String,
|
|
41
|
+
#[serde(default)]
|
|
42
|
+
pub auto_splash: bool,
|
|
43
|
+
#[serde(default)]
|
|
44
|
+
pub offline: bool,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fn default_zoom() -> u32 {
|
|
48
|
+
100
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
52
|
+
pub struct PlatformSpecific<T> {
|
|
53
|
+
pub macos: T,
|
|
54
|
+
pub linux: T,
|
|
55
|
+
pub windows: T,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
impl<T> PlatformSpecific<T> {
|
|
59
|
+
pub const fn get(&self) -> &T {
|
|
60
|
+
#[cfg(target_os = "macos")]
|
|
61
|
+
let platform = &self.macos;
|
|
62
|
+
#[cfg(target_os = "linux")]
|
|
63
|
+
let platform = &self.linux;
|
|
64
|
+
#[cfg(target_os = "windows")]
|
|
65
|
+
let platform = &self.windows;
|
|
66
|
+
|
|
67
|
+
platform
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
impl<T> PlatformSpecific<T>
|
|
72
|
+
where
|
|
73
|
+
T: Copy,
|
|
74
|
+
{
|
|
75
|
+
pub const fn copied(&self) -> T {
|
|
76
|
+
*self.get()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pub type UserAgent = PlatformSpecific<String>;
|
|
81
|
+
pub type FunctionON = PlatformSpecific<bool>;
|
|
82
|
+
|
|
83
|
+
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
84
|
+
pub struct BghitappConfig {
|
|
85
|
+
pub windows: Vec<WindowConfig>,
|
|
86
|
+
pub user_agent: UserAgent,
|
|
87
|
+
pub system_tray: FunctionON,
|
|
88
|
+
pub system_tray_path: String,
|
|
89
|
+
pub proxy_url: String,
|
|
90
|
+
#[serde(default)]
|
|
91
|
+
pub multi_instance: bool,
|
|
92
|
+
#[serde(default)]
|
|
93
|
+
pub multi_window: bool,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
impl BghitappConfig {
|
|
97
|
+
pub fn show_system_tray(&self) -> bool {
|
|
98
|
+
self.system_tray.copied()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
use crate::util::{check_file_or_append, get_download_message_with_lang, show_toast, MessageType};
|
|
2
|
+
use std::fs::File;
|
|
3
|
+
use std::io::Write;
|
|
4
|
+
use std::str::FromStr;
|
|
5
|
+
use std::sync::atomic::{AtomicI64, Ordering};
|
|
6
|
+
use tauri::http::Method;
|
|
7
|
+
use tauri::{command, AppHandle, Manager, Url, WebviewWindow};
|
|
8
|
+
use tauri_plugin_http::reqwest::{ClientBuilder, Request};
|
|
9
|
+
|
|
10
|
+
#[cfg(target_os = "macos")]
|
|
11
|
+
use tauri::Theme;
|
|
12
|
+
|
|
13
|
+
static BADGE_COUNT: AtomicI64 = AtomicI64::new(0);
|
|
14
|
+
const MAX_BADGE_COUNT: i64 = 99_999;
|
|
15
|
+
const MAX_BADGE_LABEL_CHARS: usize = 16;
|
|
16
|
+
|
|
17
|
+
fn normalize_badge_count(count: Option<i64>) -> Option<i64> {
|
|
18
|
+
count.filter(|n| (1..=MAX_BADGE_COUNT).contains(n))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
fn normalize_badge_label(label: Option<&str>) -> Result<Option<String>, String> {
|
|
22
|
+
let Some(label) = label.map(str::trim).filter(|label| !label.is_empty()) else {
|
|
23
|
+
return Ok(None);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
if label.chars().count() > MAX_BADGE_LABEL_CHARS {
|
|
27
|
+
return Err(format!(
|
|
28
|
+
"Badge label must be {MAX_BADGE_LABEL_CHARS} characters or fewer"
|
|
29
|
+
));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Ok(Some(label.to_string()))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn apply_badge(app: &AppHandle, count: Option<i64>) -> Result<(), String> {
|
|
36
|
+
let label = normalize_badge_count(count).map(|n| n.to_string());
|
|
37
|
+
apply_badge_label(app, label.as_deref())
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[cfg(target_os = "macos")]
|
|
41
|
+
fn apply_badge_label(app: &AppHandle, label: Option<&str>) -> Result<(), String> {
|
|
42
|
+
use objc2::MainThreadMarker;
|
|
43
|
+
use objc2_app_kit::NSApplication;
|
|
44
|
+
use objc2_foundation::NSString;
|
|
45
|
+
|
|
46
|
+
let label = label.map(str::to_owned);
|
|
47
|
+
app.run_on_main_thread(move || {
|
|
48
|
+
let Some(mtm) = MainThreadMarker::new() else {
|
|
49
|
+
return;
|
|
50
|
+
};
|
|
51
|
+
let dock_tile = NSApplication::sharedApplication(mtm).dockTile();
|
|
52
|
+
let ns_label = label.as_deref().map(NSString::from_str);
|
|
53
|
+
dock_tile.setBadgeLabel(ns_label.as_deref());
|
|
54
|
+
})
|
|
55
|
+
.map_err(|e| format!("Failed to dispatch dock badge update: {e}"))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#[cfg(not(target_os = "macos"))]
|
|
59
|
+
fn apply_badge_label(app: &AppHandle, label: Option<&str>) -> Result<(), String> {
|
|
60
|
+
let window = app
|
|
61
|
+
.get_webview_window("bghitapp")
|
|
62
|
+
.ok_or("Main window not found")?;
|
|
63
|
+
let count = label.and_then(|s| s.parse::<i64>().ok());
|
|
64
|
+
window
|
|
65
|
+
.set_badge_count(count)
|
|
66
|
+
.map_err(|e| format!("Failed to set badge count: {e}"))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#[derive(serde::Deserialize)]
|
|
70
|
+
pub struct DownloadFileParams {
|
|
71
|
+
url: String,
|
|
72
|
+
filename: String,
|
|
73
|
+
language: Option<String>,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#[derive(serde::Deserialize)]
|
|
77
|
+
pub struct NotificationParams {
|
|
78
|
+
title: String,
|
|
79
|
+
body: String,
|
|
80
|
+
icon: String,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
#[command]
|
|
84
|
+
pub async fn download_file(app: AppHandle, params: DownloadFileParams) -> Result<(), String> {
|
|
85
|
+
let window: WebviewWindow = app.get_webview_window("bghitapp").ok_or("Window not found")?;
|
|
86
|
+
|
|
87
|
+
show_toast(
|
|
88
|
+
&window,
|
|
89
|
+
&get_download_message_with_lang(MessageType::Start, params.language.clone()),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
let download_dir = app
|
|
93
|
+
.path()
|
|
94
|
+
.download_dir()
|
|
95
|
+
.map_err(|e| format!("Failed to get download dir: {}", e))?;
|
|
96
|
+
|
|
97
|
+
let output_path = download_dir.join(¶ms.filename);
|
|
98
|
+
|
|
99
|
+
let path_str = output_path.to_str().ok_or("Invalid output path")?;
|
|
100
|
+
|
|
101
|
+
let file_path = check_file_or_append(path_str);
|
|
102
|
+
|
|
103
|
+
let client = ClientBuilder::new()
|
|
104
|
+
.build()
|
|
105
|
+
.map_err(|e| format!("Failed to build client: {}", e))?;
|
|
106
|
+
|
|
107
|
+
let url = Url::from_str(¶ms.url).map_err(|e| format!("Invalid URL: {}", e))?;
|
|
108
|
+
|
|
109
|
+
let request = Request::new(Method::GET, url);
|
|
110
|
+
|
|
111
|
+
let response = client.execute(request).await;
|
|
112
|
+
|
|
113
|
+
match response {
|
|
114
|
+
Ok(mut res) => {
|
|
115
|
+
let mut file =
|
|
116
|
+
File::create(file_path).map_err(|e| format!("Failed to create file: {}", e))?;
|
|
117
|
+
|
|
118
|
+
while let Some(chunk) = res
|
|
119
|
+
.chunk()
|
|
120
|
+
.await
|
|
121
|
+
.map_err(|e| format!("Failed to get chunk: {}", e))?
|
|
122
|
+
{
|
|
123
|
+
file.write_all(&chunk)
|
|
124
|
+
.map_err(|e| format!("Failed to write chunk: {}", e))?;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
show_toast(
|
|
128
|
+
&window,
|
|
129
|
+
&get_download_message_with_lang(MessageType::Success, params.language.clone()),
|
|
130
|
+
);
|
|
131
|
+
Ok(())
|
|
132
|
+
}
|
|
133
|
+
Err(e) => {
|
|
134
|
+
show_toast(
|
|
135
|
+
&window,
|
|
136
|
+
&get_download_message_with_lang(MessageType::Failure, params.language),
|
|
137
|
+
);
|
|
138
|
+
Err(e.to_string())
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#[command]
|
|
144
|
+
pub fn send_notification(app: AppHandle, params: NotificationParams) -> Result<(), String> {
|
|
145
|
+
use tauri_plugin_notification::NotificationExt;
|
|
146
|
+
app.notification()
|
|
147
|
+
.builder()
|
|
148
|
+
.title(¶ms.title)
|
|
149
|
+
.body(¶ms.body)
|
|
150
|
+
.icon(¶ms.icon)
|
|
151
|
+
.show()
|
|
152
|
+
.map_err(|e| format!("Failed to show notification: {}", e))?;
|
|
153
|
+
Ok(())
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
#[command]
|
|
157
|
+
pub fn set_dock_badge(app: AppHandle, count: Option<i64>) -> Result<(), String> {
|
|
158
|
+
let normalized = normalize_badge_count(count);
|
|
159
|
+
BADGE_COUNT.store(normalized.unwrap_or(0), Ordering::SeqCst);
|
|
160
|
+
apply_badge(&app, normalized)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
#[command]
|
|
164
|
+
pub fn increment_dock_badge(app: AppHandle) -> Result<(), String> {
|
|
165
|
+
let current = BADGE_COUNT.load(Ordering::SeqCst);
|
|
166
|
+
let next = current.saturating_add(1).clamp(1, MAX_BADGE_COUNT);
|
|
167
|
+
BADGE_COUNT.store(next, Ordering::SeqCst);
|
|
168
|
+
apply_badge(&app, Some(next))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#[command]
|
|
172
|
+
pub fn clear_dock_badge(app: AppHandle) -> Result<(), String> {
|
|
173
|
+
BADGE_COUNT.store(0, Ordering::SeqCst);
|
|
174
|
+
apply_badge(&app, None)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
#[command]
|
|
178
|
+
pub fn set_dock_badge_label(app: AppHandle, label: Option<String>) -> Result<(), String> {
|
|
179
|
+
BADGE_COUNT.store(0, Ordering::SeqCst);
|
|
180
|
+
let label = normalize_badge_label(label.as_deref())?;
|
|
181
|
+
apply_badge_label(&app, label.as_deref())
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#[command]
|
|
185
|
+
pub async fn update_theme_mode(app: AppHandle, mode: String) {
|
|
186
|
+
#[cfg(target_os = "macos")]
|
|
187
|
+
{
|
|
188
|
+
if let Some(window) = app.get_webview_window("bghitapp") {
|
|
189
|
+
let theme = if mode == "dark" {
|
|
190
|
+
Theme::Dark
|
|
191
|
+
} else {
|
|
192
|
+
Theme::Light
|
|
193
|
+
};
|
|
194
|
+
let _ = window.set_theme(Some(theme));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
#[cfg(not(target_os = "macos"))]
|
|
198
|
+
{
|
|
199
|
+
let _ = app;
|
|
200
|
+
let _ = mode;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
#[command]
|
|
205
|
+
#[allow(unreachable_code)]
|
|
206
|
+
pub fn clear_cache_and_restart(app: AppHandle) -> Result<(), String> {
|
|
207
|
+
if let Some(window) = app.get_webview_window("bghitapp") {
|
|
208
|
+
match window.clear_all_browsing_data() {
|
|
209
|
+
Ok(_) => {
|
|
210
|
+
// Clear all browsing data successfully
|
|
211
|
+
app.restart();
|
|
212
|
+
Ok(())
|
|
213
|
+
}
|
|
214
|
+
Err(e) => {
|
|
215
|
+
eprintln!("Failed to clear browsing data: {}", e);
|
|
216
|
+
Err(format!("Failed to clear browsing data: {}", e))
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
Err("Main window not found".to_string())
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
#[command]
|
|
225
|
+
pub async fn close_splashscreen(app: AppHandle) -> Result<(), String> {
|
|
226
|
+
if let Some(splash) = app.get_webview_window("splash") {
|
|
227
|
+
let _ = splash.eval("document.body.classList.add('fade-out')");
|
|
228
|
+
tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
|
|
229
|
+
if let Err(e) = splash.destroy() {
|
|
230
|
+
eprintln!("[BghitApp] Failed to destroy splash window: {}", e);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if let Some(main) = app.get_webview_window("bghitapp") {
|
|
234
|
+
if let Err(e) = main.show() {
|
|
235
|
+
eprintln!("[BghitApp] Failed to show main window: {}", e);
|
|
236
|
+
}
|
|
237
|
+
if let Err(e) = main.set_focus() {
|
|
238
|
+
eprintln!("[BghitApp] Failed to focus main window: {}", e);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
Ok(())
|
|
242
|
+
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// Menu functionality is only used on macOS
|
|
2
|
+
#![cfg(target_os = "macos")]
|
|
3
|
+
|
|
4
|
+
use crate::app::window::open_additional_window_safe;
|
|
5
|
+
use tauri::menu::{AboutMetadata, Menu, MenuItem, PredefinedMenuItem, Submenu};
|
|
6
|
+
use tauri::{AppHandle, Manager, Wry};
|
|
7
|
+
use tauri_plugin_opener::OpenerExt;
|
|
8
|
+
|
|
9
|
+
pub fn set_app_menu(
|
|
10
|
+
app: &AppHandle<Wry>,
|
|
11
|
+
allow_multi_window: bool,
|
|
12
|
+
enable_find: bool,
|
|
13
|
+
) -> tauri::Result<()> {
|
|
14
|
+
let bghitapp_version = env!("CARGO_PKG_VERSION");
|
|
15
|
+
let bghitapp_menu_item_title = format!("Built with BghitApp V{}", bghitapp_version);
|
|
16
|
+
|
|
17
|
+
let window_submenu = window_menu(app)?;
|
|
18
|
+
|
|
19
|
+
let menu = Menu::with_items(
|
|
20
|
+
app,
|
|
21
|
+
&[
|
|
22
|
+
&app_menu(app)?,
|
|
23
|
+
&file_menu(app, allow_multi_window)?,
|
|
24
|
+
&edit_menu(app, enable_find)?,
|
|
25
|
+
&view_menu(app)?,
|
|
26
|
+
&navigation_menu(app)?,
|
|
27
|
+
&window_submenu,
|
|
28
|
+
&help_menu(app, &bghitapp_menu_item_title)?,
|
|
29
|
+
],
|
|
30
|
+
)?;
|
|
31
|
+
|
|
32
|
+
app.set_menu(menu)?;
|
|
33
|
+
|
|
34
|
+
// AppKit injects Move & Resize, Fill, Center, Full Screen Tile, and
|
|
35
|
+
// window-cycling once the submenu is registered as the windows menu.
|
|
36
|
+
window_submenu.set_as_windows_menu_for_nsapp()?;
|
|
37
|
+
|
|
38
|
+
Ok(())
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
fn app_menu(app: &AppHandle<Wry>) -> tauri::Result<Submenu<Wry>> {
|
|
42
|
+
let app_menu = Submenu::new(app, "BghitApp", true)?;
|
|
43
|
+
let about_metadata = AboutMetadata::default();
|
|
44
|
+
app_menu.append(&PredefinedMenuItem::about(
|
|
45
|
+
app,
|
|
46
|
+
Some("BghitApp"),
|
|
47
|
+
Some(about_metadata),
|
|
48
|
+
)?)?;
|
|
49
|
+
app_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
50
|
+
app_menu.append(&PredefinedMenuItem::services(app, None)?)?;
|
|
51
|
+
app_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
52
|
+
app_menu.append(&PredefinedMenuItem::hide(app, None)?)?;
|
|
53
|
+
app_menu.append(&PredefinedMenuItem::hide_others(app, None)?)?;
|
|
54
|
+
app_menu.append(&PredefinedMenuItem::show_all(app, None)?)?;
|
|
55
|
+
app_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
56
|
+
app_menu.append(&PredefinedMenuItem::quit(app, None)?)?;
|
|
57
|
+
Ok(app_menu)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn file_menu(app: &AppHandle<Wry>, allow_multi_window: bool) -> tauri::Result<Submenu<Wry>> {
|
|
61
|
+
let file_menu = Submenu::new(app, "File", true)?;
|
|
62
|
+
if allow_multi_window {
|
|
63
|
+
file_menu.append(&MenuItem::with_id(
|
|
64
|
+
app,
|
|
65
|
+
"new_window",
|
|
66
|
+
"New Window",
|
|
67
|
+
true,
|
|
68
|
+
Some("CmdOrCtrl+N"),
|
|
69
|
+
)?)?;
|
|
70
|
+
file_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
71
|
+
}
|
|
72
|
+
file_menu.append(&PredefinedMenuItem::close_window(app, None)?)?;
|
|
73
|
+
file_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
74
|
+
file_menu.append(&MenuItem::with_id(
|
|
75
|
+
app,
|
|
76
|
+
"clear_cache_restart",
|
|
77
|
+
"Clear Cache & Restart",
|
|
78
|
+
true,
|
|
79
|
+
Some("CmdOrCtrl+Shift+Backspace"),
|
|
80
|
+
)?)?;
|
|
81
|
+
Ok(file_menu)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fn edit_menu(app: &AppHandle<Wry>, enable_find: bool) -> tauri::Result<Submenu<Wry>> {
|
|
85
|
+
let edit_menu = Submenu::new(app, "Edit", true)?;
|
|
86
|
+
edit_menu.append(&PredefinedMenuItem::undo(app, None)?)?;
|
|
87
|
+
edit_menu.append(&PredefinedMenuItem::redo(app, None)?)?;
|
|
88
|
+
edit_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
89
|
+
edit_menu.append(&PredefinedMenuItem::cut(app, None)?)?;
|
|
90
|
+
edit_menu.append(&PredefinedMenuItem::copy(app, None)?)?;
|
|
91
|
+
edit_menu.append(&PredefinedMenuItem::paste(app, None)?)?;
|
|
92
|
+
edit_menu.append(&MenuItem::with_id(
|
|
93
|
+
app,
|
|
94
|
+
"paste_and_match_style",
|
|
95
|
+
"Paste and Match Style",
|
|
96
|
+
true,
|
|
97
|
+
Some("CmdOrCtrl+Shift+Option+V"),
|
|
98
|
+
)?)?;
|
|
99
|
+
edit_menu.append(&PredefinedMenuItem::select_all(app, None)?)?;
|
|
100
|
+
edit_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
101
|
+
if enable_find {
|
|
102
|
+
edit_menu.append(&MenuItem::with_id(
|
|
103
|
+
app,
|
|
104
|
+
"find",
|
|
105
|
+
"Find",
|
|
106
|
+
true,
|
|
107
|
+
Some("CmdOrCtrl+F"),
|
|
108
|
+
)?)?;
|
|
109
|
+
edit_menu.append(&MenuItem::with_id(
|
|
110
|
+
app,
|
|
111
|
+
"find_next",
|
|
112
|
+
"Find Next",
|
|
113
|
+
true,
|
|
114
|
+
Some("CmdOrCtrl+G"),
|
|
115
|
+
)?)?;
|
|
116
|
+
edit_menu.append(&MenuItem::with_id(
|
|
117
|
+
app,
|
|
118
|
+
"find_previous",
|
|
119
|
+
"Find Previous",
|
|
120
|
+
true,
|
|
121
|
+
Some("CmdOrCtrl+Shift+G"),
|
|
122
|
+
)?)?;
|
|
123
|
+
edit_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
124
|
+
}
|
|
125
|
+
edit_menu.append(&MenuItem::with_id(
|
|
126
|
+
app,
|
|
127
|
+
"copy_url",
|
|
128
|
+
"Copy URL",
|
|
129
|
+
true,
|
|
130
|
+
Some("CmdOrCtrl+L"),
|
|
131
|
+
)?)?;
|
|
132
|
+
Ok(edit_menu)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
fn view_menu(app: &AppHandle<Wry>) -> tauri::Result<Submenu<Wry>> {
|
|
136
|
+
let view_menu = Submenu::new(app, "View", true)?;
|
|
137
|
+
view_menu.append(&MenuItem::with_id(
|
|
138
|
+
app,
|
|
139
|
+
"reload",
|
|
140
|
+
"Reload",
|
|
141
|
+
true,
|
|
142
|
+
Some("CmdOrCtrl+R"),
|
|
143
|
+
)?)?;
|
|
144
|
+
view_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
145
|
+
view_menu.append(&MenuItem::with_id(
|
|
146
|
+
app,
|
|
147
|
+
"zoom_in",
|
|
148
|
+
"Zoom In",
|
|
149
|
+
true,
|
|
150
|
+
Some("CmdOrCtrl+="),
|
|
151
|
+
)?)?;
|
|
152
|
+
view_menu.append(&MenuItem::with_id(
|
|
153
|
+
app,
|
|
154
|
+
"zoom_out",
|
|
155
|
+
"Zoom Out",
|
|
156
|
+
true,
|
|
157
|
+
Some("CmdOrCtrl+-"),
|
|
158
|
+
)?)?;
|
|
159
|
+
view_menu.append(&MenuItem::with_id(
|
|
160
|
+
app,
|
|
161
|
+
"zoom_reset",
|
|
162
|
+
"Actual Size",
|
|
163
|
+
true,
|
|
164
|
+
Some("CmdOrCtrl+0"),
|
|
165
|
+
)?)?;
|
|
166
|
+
view_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
167
|
+
view_menu.append(&PredefinedMenuItem::fullscreen(app, None)?)?;
|
|
168
|
+
view_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
169
|
+
view_menu.append(&MenuItem::with_id(
|
|
170
|
+
app,
|
|
171
|
+
"toggle_devtools",
|
|
172
|
+
"Toggle Developer Tools",
|
|
173
|
+
cfg!(debug_assertions),
|
|
174
|
+
Some("CmdOrCtrl+Option+I"),
|
|
175
|
+
)?)?;
|
|
176
|
+
Ok(view_menu)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
fn navigation_menu(app: &AppHandle<Wry>) -> tauri::Result<Submenu<Wry>> {
|
|
180
|
+
let navigation_menu = Submenu::new(app, "Navigation", true)?;
|
|
181
|
+
navigation_menu.append(&MenuItem::with_id(
|
|
182
|
+
app,
|
|
183
|
+
"go_back",
|
|
184
|
+
"Back",
|
|
185
|
+
true,
|
|
186
|
+
Some("CmdOrCtrl+["),
|
|
187
|
+
)?)?;
|
|
188
|
+
navigation_menu.append(&MenuItem::with_id(
|
|
189
|
+
app,
|
|
190
|
+
"go_forward",
|
|
191
|
+
"Forward",
|
|
192
|
+
true,
|
|
193
|
+
Some("CmdOrCtrl+]"),
|
|
194
|
+
)?)?;
|
|
195
|
+
navigation_menu.append(&MenuItem::with_id(
|
|
196
|
+
app,
|
|
197
|
+
"go_home",
|
|
198
|
+
"Go Home",
|
|
199
|
+
true,
|
|
200
|
+
Some("CmdOrCtrl+Shift+H"),
|
|
201
|
+
)?)?;
|
|
202
|
+
Ok(navigation_menu)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
fn window_menu(app: &AppHandle<Wry>) -> tauri::Result<Submenu<Wry>> {
|
|
206
|
+
let window_menu = Submenu::new(app, "Window", true)?;
|
|
207
|
+
window_menu.append(&PredefinedMenuItem::minimize(app, None)?)?;
|
|
208
|
+
window_menu.append(&PredefinedMenuItem::maximize(app, None)?)?;
|
|
209
|
+
window_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
210
|
+
window_menu.append(&MenuItem::with_id(
|
|
211
|
+
app,
|
|
212
|
+
"always_on_top",
|
|
213
|
+
"Toggle Always on Top",
|
|
214
|
+
true,
|
|
215
|
+
None::<&str>,
|
|
216
|
+
)?)?;
|
|
217
|
+
window_menu.append(&PredefinedMenuItem::separator(app)?)?;
|
|
218
|
+
window_menu.append(&PredefinedMenuItem::close_window(app, None)?)?;
|
|
219
|
+
Ok(window_menu)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
fn help_menu(app: &AppHandle<Wry>, title: &str) -> tauri::Result<Submenu<Wry>> {
|
|
223
|
+
let help_menu = Submenu::new(app, "Help", true)?;
|
|
224
|
+
let github_item = MenuItem::with_id(app, "bghitapp_github_link", title, true, None::<&str>)?;
|
|
225
|
+
help_menu.append(&github_item)?;
|
|
226
|
+
Ok(help_menu)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
pub fn handle_menu_click(app_handle: &AppHandle, id: &str) {
|
|
230
|
+
match id {
|
|
231
|
+
"new_window" => {
|
|
232
|
+
open_additional_window_safe(app_handle);
|
|
233
|
+
}
|
|
234
|
+
"bghitapp_github_link" => {
|
|
235
|
+
let _ = app_handle
|
|
236
|
+
.opener()
|
|
237
|
+
.open_url("https://github.com/BghitCode/bghitapp", None::<&str>);
|
|
238
|
+
}
|
|
239
|
+
"reload" => {
|
|
240
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
241
|
+
let _ = window.eval("window.location.reload()");
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
"toggle_devtools" => {
|
|
245
|
+
#[cfg(debug_assertions)] // Only allow in debug builds
|
|
246
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
247
|
+
if window.is_devtools_open() {
|
|
248
|
+
window.close_devtools();
|
|
249
|
+
} else {
|
|
250
|
+
window.open_devtools();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
"zoom_in" => {
|
|
255
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
256
|
+
let _ = window.eval("zoomIn()");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
"zoom_out" => {
|
|
260
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
261
|
+
let _ = window.eval("zoomOut()");
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
"zoom_reset" => {
|
|
265
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
266
|
+
let _ = window.eval("setZoom('100%')");
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
"go_back" => {
|
|
270
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
271
|
+
let _ = window.eval("window.history.back()");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
"go_forward" => {
|
|
275
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
276
|
+
let _ = window.eval("window.history.forward()");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
"go_home" => {
|
|
280
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
281
|
+
let _ = window.eval("window.location.href = window.bghitappConfig.url");
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
"copy_url" => {
|
|
285
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
286
|
+
let _ = window.eval("navigator.clipboard.writeText(window.location.href)");
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
"paste_and_match_style" => {
|
|
290
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
291
|
+
let _ = window.eval("triggerPasteAsPlainText()");
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
"find" => {
|
|
295
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
296
|
+
let _ = window.eval("window.bghitappFind?.open()");
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
"find_next" => {
|
|
300
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
301
|
+
let _ = window.eval("window.bghitappFind?.next()");
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
"find_previous" => {
|
|
305
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
306
|
+
let _ = window.eval("window.bghitappFind?.previous()");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
"clear_cache_restart" => {
|
|
310
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
311
|
+
if let Ok(_) = window.clear_all_browsing_data() {
|
|
312
|
+
app_handle.restart();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
"always_on_top" => {
|
|
317
|
+
if let Some(window) = app_handle.get_webview_window("bghitapp") {
|
|
318
|
+
let is_on_top = window.is_always_on_top().unwrap_or(false);
|
|
319
|
+
let _ = window.set_always_on_top(!is_on_top);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
_ => {}
|
|
323
|
+
}
|
|
324
|
+
}
|