@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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +203 -0
  3. package/dist/cli.js +2995 -0
  4. package/package.json +104 -0
  5. package/src-tauri/Cargo.lock +5966 -0
  6. package/src-tauri/Cargo.toml +59 -0
  7. package/src-tauri/Info.plist +14 -0
  8. package/src-tauri/assets/macos/dmg/background.png +0 -0
  9. package/src-tauri/assets/main.wxs +350 -0
  10. package/src-tauri/bghitapp.json +42 -0
  11. package/src-tauri/build.rs +5 -0
  12. package/src-tauri/capabilities/default.json +29 -0
  13. package/src-tauri/entitlements.plist +7 -0
  14. package/src-tauri/icons/chatgpt.icns +0 -0
  15. package/src-tauri/icons/deepseek.icns +0 -0
  16. package/src-tauri/icons/excalidraw.icns +0 -0
  17. package/src-tauri/icons/flomo.icns +0 -0
  18. package/src-tauri/icons/gemini.icns +0 -0
  19. package/src-tauri/icons/grok.icns +0 -0
  20. package/src-tauri/icons/icon.icns +0 -0
  21. package/src-tauri/icons/icon.png +0 -0
  22. package/src-tauri/icons/lizhi.icns +0 -0
  23. package/src-tauri/icons/programmusic.icns +0 -0
  24. package/src-tauri/icons/qwerty.icns +0 -0
  25. package/src-tauri/icons/twitter.icns +0 -0
  26. package/src-tauri/icons/wechat.icns +0 -0
  27. package/src-tauri/icons/weekly.icns +0 -0
  28. package/src-tauri/icons/weread.icns +0 -0
  29. package/src-tauri/icons/xiaohongshu.icns +0 -0
  30. package/src-tauri/icons/youtube.icns +0 -0
  31. package/src-tauri/icons/youtubemusic.icns +0 -0
  32. package/src-tauri/rust_proxy.toml +10 -0
  33. package/src-tauri/src/app/config.rs +100 -0
  34. package/src-tauri/src/app/invoke.rs +242 -0
  35. package/src-tauri/src/app/menu.rs +324 -0
  36. package/src-tauri/src/app/mod.rs +6 -0
  37. package/src-tauri/src/app/setup.rs +172 -0
  38. package/src-tauri/src/app/window.rs +577 -0
  39. package/src-tauri/src/inject/auth.js +75 -0
  40. package/src-tauri/src/inject/custom.js +0 -0
  41. package/src-tauri/src/inject/event.js +1111 -0
  42. package/src-tauri/src/inject/find.js +708 -0
  43. package/src-tauri/src/inject/fullscreen.js +253 -0
  44. package/src-tauri/src/inject/offline.js +68 -0
  45. package/src-tauri/src/inject/splash-transition.js +13 -0
  46. package/src-tauri/src/inject/style.js +505 -0
  47. package/src-tauri/src/inject/theme_refresh.js +59 -0
  48. package/src-tauri/src/inject/toast.js +22 -0
  49. package/src-tauri/src/lib.rs +227 -0
  50. package/src-tauri/src/main.rs +8 -0
  51. package/src-tauri/src/util.rs +245 -0
  52. package/src-tauri/tauri.conf.json +20 -0
  53. package/src-tauri/tauri.linux.conf.json +12 -0
  54. package/src-tauri/tauri.macos.conf.json +28 -0
  55. 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(&params.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(&params.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(&params.title)
149
+ .body(&params.body)
150
+ .icon(&params.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
+ }
@@ -0,0 +1,6 @@
1
+ pub mod config;
2
+ pub mod invoke;
3
+ #[cfg(target_os = "macos")]
4
+ pub mod menu;
5
+ pub mod setup;
6
+ pub mod window;