From 647a89fdf8df5960899ba15c1149c4eeaa04b55b Mon Sep 17 00:00:00 2001 From: lencx Date: Fri, 16 Dec 2022 11:43:29 +0800 Subject: [PATCH] feat: hide dock icon (#35) --- README-ZH_CN.md | 6 +- README.md | 6 +- UPDATE_LOG.md | 7 ++- src-tauri/src/app/menu.rs | 118 ++++++++++++++++++++++++++--------- src-tauri/src/app/setup.rs | 76 ++++++++++++---------- src-tauri/src/app/window.rs | 39 ++++++------ src-tauri/src/assets/core.js | 2 +- src-tauri/src/conf.rs | 17 +++-- src-tauri/src/main.rs | 2 +- src/view/General.tsx | 2 +- 10 files changed, 182 insertions(+), 93 deletions(-) diff --git a/README-ZH_CN.md b/README-ZH_CN.md index 06614532b..bdd32182e 100644 --- a/README-ZH_CN.md +++ b/README-ZH_CN.md @@ -60,11 +60,13 @@ cask "popcorn-time", args: { "no-quarantine": true } - **Preferences (喜好)** - `Theme` - `Light`, `Dark` (仅支持 macOS 和 Windows) - - `Always On Top`: 窗口置顶 + - `Stay On Top`: 窗口置顶 - `Titlebar`: 是否显示 `Titlebar`,仅 macOS 支持 - `Inject Script`: 用于修改网站的用户自定义脚本 + - `Hide Dock Icon` ([#35](https://github.com/lencx/ChatGPT/issues/35)): 隐藏 Dock 中的应用图标 (仅 macOS 支持) + - 右键单击系统托盘图标来显示或隐藏在 Dock 里的应用图标 - `Control Center`: ChatGPT 应用的控制中心,它将为应用提供无限的可能 - - 设置 `Theme`,`Always on Top`,`Titlebar` 等 + - 设置 `Theme`,`Stay On Top`,`Titlebar` 等 - `User Agent` ([#17](https://github.com/lencx/ChatGPT/issues/17)): 自定义 `user agent` 防止网站安全检测,默认值为空 - `Switch Origin` ([#14](https://github.com/lencx/ChatGPT/issues/14)): 切换网站源地址,默认为 `https://chat.openai.com`。需要注意的是镜像网站的 UI 需要和原网站一致,否则可能会导致某些功能不工作 - `Go to Config`: 打开 ChatGPT 配置目录 (`path: ~/.chatgpt/*`) diff --git a/README.md b/README.md index 959bfa1b2..faf9afc65 100644 --- a/README.md +++ b/README.md @@ -61,11 +61,13 @@ cask "popcorn-time", args: { "no-quarantine": true } - **Preferences** - `Theme` - `Light`, `Dark` (Only macOS and Windows are supported). - - `Always on Top`: The window is always on top of other windows. + - `Stay On Top`: The window is stay on top of other windows. - `Titlebar`: Whether to display the titlebar, supported by macOS only. + - `Hide Dock Icon` ([#35](https://github.com/lencx/ChatGPT/issues/35)): Hide application icons from the Dock(support macOS only). + - Right-click on the system tray icon to show or hide the application icons in the Dock - `Inject Script`: Using scripts to modify pages. - `Control Center`: The control center of ChatGPT application, it will give unlimited imagination to the application. - - `Theme`, `Always on Top`, `Titlebar`, ... + - `Theme`, `Stay On Top`, `Titlebar`, ... - `User Agent` ([#17](https://github.com/lencx/ChatGPT/issues/17)): Custom `user agent`, which may be required in some scenarios. The default value is the empty string. - `Switch Origin` ([#14](https://github.com/lencx/ChatGPT/issues/14)): Switch the site source address, the default is `https://chat.openai.com`, please make sure the mirror site UI is the same as the original address. Otherwise, some functions may not be available. - `Go to Config`: Open the configuration file directory (`path: ~/.chatgpt/*`). diff --git a/UPDATE_LOG.md b/UPDATE_LOG.md index 53f128c14..4d2f01294 100644 --- a/UPDATE_LOG.md +++ b/UPDATE_LOG.md @@ -1,5 +1,10 @@ # UPDATE LOG +## v0.4.0 + +feat: menu enhancement +- hide application icons from the Dock (support macOS only) + ## v0.3.0 fix: can't open ChatGPT @@ -36,7 +41,7 @@ feat: tray window ## v0.1.6 feat: -- always on top +- stay on top - export ChatGPT history ## v0.1.5 diff --git a/src-tauri/src/app/menu.rs b/src-tauri/src/app/menu.rs index cb769ad91..a30f0de0c 100644 --- a/src-tauri/src/app/menu.rs +++ b/src-tauri/src/app/menu.rs @@ -3,15 +3,15 @@ use crate::{ utils, }; use tauri::{ - utils::assets::EmbeddedAssets, AboutMetadata, AppHandle, Context, CustomMenuItem, Manager, - Menu, MenuItem, Submenu, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowMenuEvent, + AboutMetadata, AppHandle, CustomMenuItem, Manager, Menu, MenuItem, Submenu, SystemTray, + SystemTrayEvent, SystemTrayMenu, WindowMenuEvent, }; use tauri_plugin_positioner::{on_tray_event, Position, WindowExt}; // --- Menu -pub fn init(context: &Context) -> Menu { +pub fn init() -> Menu { let chat_conf = ChatConfJson::get_chat_conf(); - let name = &context.package_info().name; + let name = "ChatGPT"; let app_menu = Submenu::new( name, Menu::new() @@ -25,18 +25,18 @@ pub fn init(context: &Context) -> Menu { .add_native_item(MenuItem::Quit), ); - let always_on_top = CustomMenuItem::new("always_on_top".to_string(), "Always on Top") - .accelerator("CmdOrCtrl+T"); + let stay_on_top = + CustomMenuItem::new("stay_on_top".to_string(), "Stay On Top").accelerator("CmdOrCtrl+T"); let titlebar = CustomMenuItem::new("titlebar".to_string(), "Titlebar").accelerator("CmdOrCtrl+B"); let theme_light = CustomMenuItem::new("theme_light".to_string(), "Light"); let theme_dark = CustomMenuItem::new("theme_dark".to_string(), "Dark"); let is_dark = chat_conf.theme == "Dark"; - let always_on_top_menu = if chat_conf.always_on_top { - always_on_top.selected() + let stay_on_top_menu = if chat_conf.stay_on_top { + stay_on_top.selected() } else { - always_on_top + stay_on_top }; let titlebar_menu = if chat_conf.titlebar { titlebar.selected() @@ -62,9 +62,11 @@ pub fn init(context: &Context) -> Menu { }), ) .into(), - always_on_top_menu.into(), + stay_on_top_menu.into(), #[cfg(target_os = "macos")] titlebar_menu.into(), + #[cfg(target_os = "macos")] + CustomMenuItem::new("hide_dock_icon".to_string(), "Hide Dock Icon").into(), MenuItem::Separator.into(), CustomMenuItem::new("inject_script".to_string(), "Inject Script") .accelerator("CmdOrCtrl+J") @@ -119,7 +121,6 @@ pub fn init(context: &Context) -> Menu { CustomMenuItem::new("scroll_bottom".to_string(), "Scroll to Bottom of Screen") .accelerator("CmdOrCtrl+Down"), ) - .add_native_item(MenuItem::Zoom) .add_native_item(MenuItem::Separator) .add_item( CustomMenuItem::new("reload".to_string(), "Refresh the Screen") @@ -127,6 +128,13 @@ pub fn init(context: &Context) -> Menu { ), ); + let window_menu = Submenu::new( + "Window", + Menu::new() + .add_native_item(MenuItem::Minimize) + .add_native_item(MenuItem::Zoom), + ); + let help_menu = Submenu::new( "Help", Menu::new() @@ -143,6 +151,7 @@ pub fn init(context: &Context) -> Menu { .add_submenu(preferences_menu) .add_submenu(edit_menu) .add_submenu(view_menu) + .add_submenu(window_menu) .add_submenu(help_menu) } @@ -165,6 +174,9 @@ pub fn menu_handler(event: WindowMenuEvent) { "go_conf" => utils::open_file(utils::chat_root()), "clear_conf" => utils::clear_conf(&app), "awesome" => open(&app, conf::AWESOME_URL.to_string()), + "hide_dock_icon" => { + ChatConfJson::amend(&serde_json::json!({ "hide_dock_icon": true }), Some(app)).unwrap() + } "titlebar" => { let chat_conf = conf::ChatConfJson::get_chat_conf(); ChatConfJson::amend( @@ -182,19 +194,15 @@ pub fn menu_handler(event: WindowMenuEvent) { }; ChatConfJson::amend(&serde_json::json!({ "theme": theme }), Some(app)).unwrap(); } - "always_on_top" => { - let mut always_on_top = state.always_on_top.lock().unwrap(); - *always_on_top = !*always_on_top; + "stay_on_top" => { + let mut stay_on_top = state.stay_on_top.lock().unwrap(); + *stay_on_top = !*stay_on_top; menu_handle .get_item(menu_id) - .set_selected(*always_on_top) + .set_selected(*stay_on_top) .unwrap(); - win.set_always_on_top(*always_on_top).unwrap(); - ChatConfJson::amend( - &serde_json::json!({ "always_on_top": *always_on_top }), - None, - ) - .unwrap(); + win.set_always_on_top(*stay_on_top).unwrap(); + ChatConfJson::amend(&serde_json::json!({ "stay_on_top": *stay_on_top }), None).unwrap(); } // View "reload" => win.eval("window.location.reload()").unwrap(), @@ -235,20 +243,70 @@ pub fn tray_menu() -> SystemTray { // --- SystemTray Event pub fn tray_handler(handle: &AppHandle, event: SystemTrayEvent) { - let core_win = handle.get_window("core").unwrap(); on_tray_event(handle, &event); - if let SystemTrayEvent::LeftClick { .. } = event { - core_win.minimize().unwrap(); - let mini_win = handle.get_window("mini").unwrap(); - mini_win.move_window(Position::TrayCenter).unwrap(); + match event { + SystemTrayEvent::LeftClick { .. } => { + let chat_conf = conf::ChatConfJson::get_chat_conf(); + + if !chat_conf.hide_dock_icon { + let core_win = handle.get_window("core").unwrap(); + core_win.minimize().unwrap(); + } - if mini_win.is_visible().unwrap() { - mini_win.hide().unwrap(); - } else { - mini_win.show().unwrap(); + let tray_win = handle.get_window("tray").unwrap(); + tray_win.move_window(Position::TrayCenter).unwrap(); + + if tray_win.is_visible().unwrap() { + tray_win.hide().unwrap(); + } else { + tray_win.show().unwrap(); + } + } + SystemTrayEvent::RightClick { .. } => { + let chat_conf = conf::ChatConfJson::get_chat_conf(); + let hide_dock = !chat_conf.hide_dock_icon; + let title; + let msg; + + if !hide_dock { + title = "Show Dock Icon"; + msg = "Are you sure you want to show the ChatGPT icon in the Dock?"; + } else { + title = "Hide Dock Icon"; + msg = "Are you sure you want to hide the ChatGPT icon in the Dock?"; + } + + let app = handle.clone(); + tauri::api::dialog::ask( + handle.get_window("tray").as_ref(), + title, + msg, + move |is_ok| { + if is_ok { + ChatConfJson::amend( + &serde_json::json!({ "hide_dock_icon": hide_dock }), + Some(app), + ) + .unwrap(); + } + }, + ); } + _ => (), } + + // if let SystemTrayEvent::LeftClick { .. } = event { + // core_win.minimize().unwrap(); + // let tray_win = handle.get_window("tray").unwrap(); + // tray_win.move_window(Position::TrayCenter).unwrap(); + + // if tray_win.is_visible().unwrap() { + // tray_win.hide().unwrap(); + // } else { + // tray_win.show().unwrap(); + // } + // } } pub fn open(app: &AppHandle, path: String) { diff --git a/src-tauri/src/app/setup.rs b/src-tauri/src/app/setup.rs index d7beb2e31..6ede893fa 100644 --- a/src-tauri/src/app/setup.rs +++ b/src-tauri/src/app/setup.rs @@ -5,40 +5,52 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box let chat_conf = ChatConfJson::get_chat_conf(); let url = chat_conf.origin.to_string(); let theme = ChatConfJson::theme(); - window::mini_window(&app.app_handle()); + let handle = app.app_handle(); - #[cfg(target_os = "macos")] - WindowBuilder::new(app, "core", WindowUrl::App(url.into())) - .resizable(true) - .fullscreen(false) - .inner_size(800.0, 600.0) - .hidden_title(true) - .theme(theme) - .always_on_top(chat_conf.always_on_top) - .title_bar_style(ChatConfJson::titlebar()) - .initialization_script(&utils::user_script()) - .initialization_script(include_str!("../assets/html2canvas.js")) - .initialization_script(include_str!("../assets/jspdf.js")) - .initialization_script(include_str!("../assets/core.js")) - .initialization_script(include_str!("../assets/export.js")) - .user_agent(&chat_conf.ua_window) - .build()?; + std::thread::spawn(move || { + window::tray_window(&handle); + }); - #[cfg(not(target_os = "macos"))] - WindowBuilder::new(app, "core", WindowUrl::App(url.into())) - .title("ChatGPT") - .resizable(true) - .fullscreen(false) - .inner_size(800.0, 600.0) - .theme(theme) - .always_on_top(chat_conf.always_on_top) - .initialization_script(&utils::user_script()) - .initialization_script(include_str!("../assets/html2canvas.js")) - .initialization_script(include_str!("../assets/jspdf.js")) - .initialization_script(include_str!("../assets/core.js")) - .initialization_script(include_str!("../assets/export.js")) - .user_agent(&chat_conf.ua_window) - .build()?; + if chat_conf.hide_dock_icon { + #[cfg(target_os = "macos")] + app.set_activation_policy(tauri::ActivationPolicy::Accessory); + } else { + let app = app.handle(); + std::thread::spawn(move || { + #[cfg(target_os = "macos")] + WindowBuilder::new(&app, "core", WindowUrl::App(url.into())) + .resizable(true) + .fullscreen(false) + .inner_size(800.0, 600.0) + .hidden_title(true) + .theme(theme) + .always_on_top(chat_conf.stay_on_top) + .title_bar_style(ChatConfJson::titlebar()) + .initialization_script(&utils::user_script()) + .initialization_script(include_str!("../assets/html2canvas.js")) + .initialization_script(include_str!("../assets/jspdf.js")) + .initialization_script(include_str!("../assets/core.js")) + .initialization_script(include_str!("../assets/export.js")) + .user_agent(&chat_conf.ua_window) + .build().unwrap(); + + #[cfg(not(target_os = "macos"))] + WindowBuilder::new(&app, "core", WindowUrl::App(url.into())) + .title("ChatGPT") + .resizable(true) + .fullscreen(false) + .inner_size(800.0, 600.0) + .theme(theme) + .always_on_top(chat_conf.stay_on_top) + .initialization_script(&utils::user_script()) + .initialization_script(include_str!("../assets/html2canvas.js")) + .initialization_script(include_str!("../assets/jspdf.js")) + .initialization_script(include_str!("../assets/core.js")) + .initialization_script(include_str!("../assets/export.js")) + .user_agent(&chat_conf.ua_window) + .build().unwrap(); + }); + } Ok(()) } diff --git a/src-tauri/src/app/window.rs b/src-tauri/src/app/window.rs index ea6b27e7a..b5c56838c 100644 --- a/src-tauri/src/app/window.rs +++ b/src-tauri/src/app/window.rs @@ -1,25 +1,28 @@ use crate::{conf, utils}; use tauri::{utils::config::WindowUrl, window::WindowBuilder}; -pub fn mini_window(handle: &tauri::AppHandle) { +pub fn tray_window(handle: &tauri::AppHandle) { let chat_conf = conf::ChatConfJson::get_chat_conf(); let theme = conf::ChatConfJson::theme(); + let app = handle.clone(); - WindowBuilder::new(handle, "mini", WindowUrl::App(chat_conf.origin.into())) - .resizable(false) - .fullscreen(false) - .inner_size(360.0, 540.0) - .decorations(false) - .always_on_top(true) - .theme(theme) - .initialization_script(&utils::user_script()) - .initialization_script(include_str!("../assets/html2canvas.js")) - .initialization_script(include_str!("../assets/jspdf.js")) - .initialization_script(include_str!("../assets/core.js")) - .initialization_script(include_str!("../assets/export.js")) - .user_agent(&chat_conf.ua_tray) - .build() - .unwrap() - .hide() - .unwrap(); + std::thread::spawn(move || { + WindowBuilder::new(&app, "tray", WindowUrl::App(chat_conf.origin.into())) + .resizable(false) + .fullscreen(false) + .inner_size(360.0, 540.0) + .decorations(false) + .always_on_top(true) + .theme(theme) + .initialization_script(&utils::user_script()) + .initialization_script(include_str!("../assets/html2canvas.js")) + .initialization_script(include_str!("../assets/jspdf.js")) + .initialization_script(include_str!("../assets/core.js")) + .initialization_script(include_str!("../assets/export.js")) + .user_agent(&chat_conf.ua_tray) + .build() + .unwrap() + .hide() + .unwrap(); + }); } diff --git a/src-tauri/src/assets/core.js b/src-tauri/src/assets/core.js index 143c1a845..3a2a433db 100644 --- a/src-tauri/src/assets/core.js +++ b/src-tauri/src/assets/core.js @@ -41,7 +41,7 @@ window.invoke = invoke; window.transformCallback = transformCallback; async function init() { - if (__TAURI_METADATA__.__currentWindow.label === 'mini') { + if (__TAURI_METADATA__.__currentWindow.label === 'tray') { document.getElementsByTagName('html')[0].style['font-size'] = '70%'; } diff --git a/src-tauri/src/conf.rs b/src-tauri/src/conf.rs index 482a37e83..921804f51 100644 --- a/src-tauri/src/conf.rs +++ b/src-tauri/src/conf.rs @@ -14,18 +14,20 @@ pub const ISSUES_URL: &str = "https://github.com/lencx/ChatGPT/issues"; pub const UPDATE_LOG_URL: &str = "https://github.com/lencx/ChatGPT/blob/main/UPDATE_LOG.md"; pub const AWESOME_URL: &str = "https://github.com/lencx/ChatGPT/blob/main/AWESOME.md"; pub const DEFAULT_CHAT_CONF: &str = r#"{ - "always_on_top": false, + "stay_on_top": false, "theme": "Light", "titlebar": true, + "hide_dock_icon": false, "default_origin": "https://chat.openai.com", "origin": "https://chat.openai.com", "ua_window": "", "ua_tray": "" }"#; pub const DEFAULT_CHAT_CONF_MAC: &str = r#"{ - "always_on_top": false, + "stay_on_top": false, "theme": "Light", "titlebar": false, + "hide_dock_icon": false, "default_origin": "https://chat.openai.com", "origin": "https://chat.openai.com", "ua_window": "", @@ -33,22 +35,27 @@ pub const DEFAULT_CHAT_CONF_MAC: &str = r#"{ }"#; pub struct ChatState { - pub always_on_top: Mutex, + pub stay_on_top: Mutex, } impl ChatState { pub fn default(chat_conf: ChatConfJson) -> Self { ChatState { - always_on_top: Mutex::new(chat_conf.always_on_top), + stay_on_top: Mutex::new(chat_conf.stay_on_top), } } } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct ChatConfJson { + // support macOS only pub titlebar: bool, - pub always_on_top: bool, + pub hide_dock_icon: bool, + + // macOS and Windows pub theme: String, + + pub stay_on_top: bool, pub default_origin: String, pub origin: String, pub ua_window: String, diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index d1cab6cac..b6d7b1d6c 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -29,7 +29,7 @@ fn main() { ]) .setup(setup::init) .plugin(tauri_plugin_positioner::init()) - .menu(menu::init(&context)) + .menu(menu::init()) .system_tray(menu::tray_menu()) .on_menu_event(menu::menu_handler) .on_system_tray_event(menu::tray_handler) diff --git a/src/view/General.tsx b/src/view/General.tsx index d1934ec7d..e2f2a7153 100644 --- a/src/view/General.tsx +++ b/src/view/General.tsx @@ -72,7 +72,7 @@ export default function General() { Dark - + {platformInfo === 'darwin' && (