diff --git a/src-tauri/src/app/cmd.rs b/src-tauri/src/app/cmd.rs index 0867256..0a4c655 100644 --- a/src-tauri/src/app/cmd.rs +++ b/src-tauri/src/app/cmd.rs @@ -140,16 +140,6 @@ pub fn parse_prompt(data: String) -> Vec { list } -#[command] -pub fn window_reload(app: AppHandle, label: &str) { - app - .app_handle() - .get_window(label) - .unwrap() - .eval("window.location.reload()") - .unwrap(); -} - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct ModelRecord { pub cmd: String, @@ -345,8 +335,8 @@ pub async fn sync_prompts(app: AppHandle, time: u64) -> Option> "Sync Prompts", "ChatGPT Prompts data has been synchronized!", ); - window_reload(app.clone(), "core"); - window_reload(app, "tray"); + window::window_reload(app.clone(), "core"); + window::window_reload(app, "tray"); return Some(data2); } diff --git a/src-tauri/src/app/menu.rs b/src-tauri/src/app/menu.rs index daaddcb..1556ebb 100644 --- a/src-tauri/src/app/menu.rs +++ b/src-tauri/src/app/menu.rs @@ -1,5 +1,5 @@ use crate::{ - app::{cmd, window}, + app::window, conf::{self, ChatConfJson}, utils, }; @@ -250,8 +250,8 @@ pub fn menu_handler(event: WindowMenuEvent) { .set_selected(popup_search) .unwrap(); ChatConfJson::amend(&serde_json::json!({ "popup_search": popup_search }), None).unwrap(); - cmd::window_reload(app.clone(), "core"); - cmd::window_reload(app, "tray"); + window::window_reload(app.clone(), "core"); + window::window_reload(app, "tray"); } "sync_prompts" => { tauri::api::dialog::ask( diff --git a/src-tauri/src/app/setup.rs b/src-tauri/src/app/setup.rs index a2a9bc0..54064b6 100644 --- a/src-tauri/src/app/setup.rs +++ b/src-tauri/src/app/setup.rs @@ -50,7 +50,12 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box } else { let app = app.handle(); tauri::async_runtime::spawn(async move { - let mut main_win = WindowBuilder::new(&app, "core", WindowUrl::App(url.clone().into())) + let link = if chat_conf.dashboard { + "index.html" + } else { + &url + }; + let mut main_win = WindowBuilder::new(&app, "core", WindowUrl::App(link.into())) .title("ChatGPT") .resizable(true) .fullscreen(false) @@ -60,7 +65,7 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box main_win = main_win.hidden_title(true); } - if url == "https://chat.openai.com" { + if url == "https://chat.openai.com" && !chat_conf.dashboard { main_win = main_win .initialization_script(include_str!("../vendors/floating-ui-core.js")) .initialization_script(include_str!("../vendors/floating-ui-dom.js")) diff --git a/src-tauri/src/app/window.rs b/src-tauri/src/app/window.rs index 7f4164e..f3f5e2e 100644 --- a/src-tauri/src/app/window.rs +++ b/src-tauri/src/app/window.rs @@ -97,14 +97,18 @@ pub fn control_window(handle: &tauri::AppHandle) { let app = handle.clone(); tauri::async_runtime::spawn(async move { if app.app_handle().get_window("main").is_none() { - WindowBuilder::new(&app, "main", WindowUrl::App("index.html".into())) - .title("Control Center") - .resizable(true) - .fullscreen(false) - .inner_size(1000.0, 700.0) - .min_inner_size(800.0, 600.0) - .build() - .unwrap(); + WindowBuilder::new( + &app, + "main", + WindowUrl::App("index.html?type=control".into()), + ) + .title("Control Center") + .resizable(true) + .fullscreen(false) + .inner_size(1000.0, 700.0) + .min_inner_size(800.0, 600.0) + .build() + .unwrap(); } else { let main_win = app.app_handle().get_window("main").unwrap(); main_win.show().unwrap(); @@ -112,3 +116,40 @@ pub fn control_window(handle: &tauri::AppHandle) { } }); } + +#[tauri::command] +pub async fn wa_window( + app: tauri::AppHandle, + label: String, + title: String, + url: String, + script: Option, +) { + info!("wa_window: {} :=> {}", title, url); + let win = app.get_window(&label); + if win.is_none() { + tauri::async_runtime::spawn(async move { + tauri::WindowBuilder::new(&app, label, tauri::WindowUrl::App(url.parse().unwrap())) + .initialization_script(&script.unwrap_or_default()) + .initialization_script(include_str!("../scripts/core.js")) + .title(title) + .build() + .unwrap(); + }); + } else { + if !win.clone().unwrap().is_visible().unwrap() { + win.clone().unwrap().show().unwrap(); + } + win.unwrap().set_focus().unwrap(); + } +} + +#[tauri::command] +pub fn window_reload(app: tauri::AppHandle, label: &str) { + app + .app_handle() + .get_window(label) + .unwrap() + .eval("window.location.reload()") + .unwrap(); +} diff --git a/src-tauri/src/conf.rs b/src-tauri/src/conf.rs index 56b9ab0..c8455ce 100644 --- a/src-tauri/src/conf.rs +++ b/src-tauri/src/conf.rs @@ -18,6 +18,7 @@ pub const BUY_COFFEE: &str = "https://www.buymeacoffee.com/lencx"; pub const GITHUB_PROMPTS_CSV_URL: &str = "https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv"; pub const DEFAULT_CHAT_CONF: &str = r#"{ + "dashboard": false, "stay_on_top": false, "auto_update": "Prompt", "theme": "Light", @@ -33,6 +34,7 @@ pub const DEFAULT_CHAT_CONF: &str = r#"{ "ua_tray": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1" }"#; pub const DEFAULT_CHAT_CONF_MAC: &str = r#"{ + "dashboard": false, "stay_on_top": false, "auto_update": "Prompt", "theme": "Light", @@ -58,6 +60,7 @@ pub struct ChatConfJson { pub theme: String, // auto update policy, Prompt/Silent/Disable pub auto_update: String, + pub dashboard: bool, pub tray: bool, pub popup_search: bool, pub stay_on_top: bool, @@ -159,15 +162,6 @@ impl ChatConfJson { if let Some(handle) = app { tauri::api::process::restart(&handle.env()); - // tauri::api::dialog::ask( - // handle.get_window("core").as_ref(), - // "ChatGPT Restart", - // "Whether to restart immediately?", - // move |is_restart| { - // if is_restart { - // } - // }, - // ); } Ok(()) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 2be1464..23ad286 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,7 +7,7 @@ mod app; mod conf; mod utils; -use app::{cmd, fs_extra, menu, setup}; +use app::{cmd, fs_extra, menu, setup, window}; use conf::ChatConfJson; use tauri::api::path; use tauri_plugin_autostart::MacosLauncher; @@ -73,13 +73,14 @@ async fn main() { cmd::parse_prompt, cmd::sync_prompts, cmd::sync_user_prompts, - cmd::window_reload, cmd::dalle2_window, cmd::cmd_list, cmd::download_list, cmd::get_download_list, cmd::get_data, fs_extra::metadata, + window::window_reload, + window::wa_window, ]) .setup(setup::init) .menu(menu::init()); diff --git a/src/layout/index.tsx b/src/layout/index.tsx index d6fcd97..b5bdf49 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -13,13 +13,18 @@ const { Content, Footer, Sider } = Layout; export default function ChatLayout() { const [collapsed, setCollapsed] = useState(false); + const [isDashboard, setDashboard] = useState(null); const [appInfo, setAppInfo] = useState>({}); const location = useLocation(); const [menuKey, setMenuKey] = useState(location.pathname); const go = useNavigate(); useEffect(() => { + if (location.search === '?type=control') { + go('/awesome'); + } setMenuKey(location.pathname); + setDashboard(location.pathname === '/'); }, [location.pathname]); useInit(async () => { @@ -36,69 +41,75 @@ export default function ChatLayout() { const isDark = appInfo.appTheme === 'dark'; + if (isDashboard === null) return null; + return ( - - setCollapsed(value)} - style={{ - overflow: 'auto', - height: '100vh', - position: 'fixed', - left: 0, - top: 0, - bottom: 0, - zIndex: 999, - }} - > -
- -
-
- {appInfo.appName} - - {appInfo.appVersion} - - - - - - -
- - go(i.key)} - /> - - - + ) : ( + + setCollapsed(value)} style={{ - overflow: 'inherit', + overflow: 'auto', + height: '100vh', + position: 'fixed', + left: 0, + top: 0, + bottom: 0, + zIndex: 999, }} > - - - +
+ +
+
+ {appInfo.appName} + + {appInfo.appVersion} + + + + + + +
+ + go(i.key)} + /> + + + + + + + - + )} ); } diff --git a/src/main.scss b/src/main.scss index f45ca3e..03f6aec 100644 --- a/src/main.scss +++ b/src/main.scss @@ -15,9 +15,11 @@ } html, -body { +body, +#root { padding: 0; margin: 0; + height: 100%; } .ant-table-selection-col, diff --git a/src/routes.tsx b/src/routes.tsx index f3e711f..bb4881a 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -22,6 +22,7 @@ import SyncRecord from '@/view/model/SyncRecord'; import Download from '@/view/download'; import Notes from '@/view/notes'; import Markdown from '@/view/markdown'; +import Dashboard from '@/view/dashboard'; export type ChatRouteMetaObject = { label: string; @@ -38,7 +39,7 @@ type ChatRouteObject = { export const routes: Array = [ { - path: '/', + path: '/awesome', element: , meta: { label: 'Awesome', @@ -121,6 +122,11 @@ export const routes: Array = [ icon: , }, }, + { + path: '/', + element: , + hideMenu: true, + }, ]; type MenuItem = Required['items'][number]; diff --git a/src/view/dashboard/index.scss b/src/view/dashboard/index.scss new file mode 100644 index 0000000..4387795 --- /dev/null +++ b/src/view/dashboard/index.scss @@ -0,0 +1,43 @@ +.dashboard { + position: fixed; + width: calc(100% - 30px); + height: calc(100% - 30px); + overflow-y: auto; + padding: 15px; + + &.dark { + background-color: #000; + } + + &.has-top-dom { + padding-top: 30px; + } + + .group-item { + margin-bottom: 20px; + + .title { + font-weight: bold; + font-size: 18px; + margin-bottom: 10px; + } + + .item { + .ant-card-body { + padding: 10px; + text-align: center; + font-weight: 500; + font-size: 14px; + } + + span { + display: block; + height: 100%; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + } +} diff --git a/src/view/dashboard/index.tsx b/src/view/dashboard/index.tsx new file mode 100644 index 0000000..f6dc49e --- /dev/null +++ b/src/view/dashboard/index.tsx @@ -0,0 +1,78 @@ +import { useEffect, useState } from 'react'; +import clsx from 'clsx'; +import { Row, Col, Card } from 'antd'; +import { os, invoke } from '@tauri-apps/api'; + +import useInit from '@/hooks/useInit'; +import useJson from '@/hooks/useJson'; +import { CHAT_AWESOME_JSON, CHAT_CONF_JSON, readJSON } from '@/utils'; +import './index.scss'; + +export default function Dashboard() { + const { json } = useJson[]>(CHAT_AWESOME_JSON); + const [list, setList] = useState[]]>>([]); + const [hasClass, setClass] = useState(false); + const [theme, setTheme] = useState(''); + + useInit(async () => { + const getOS = await os.platform(); + const conf = await readJSON(CHAT_CONF_JSON); + const appTheme = await invoke('get_theme'); + setTheme(appTheme as string); + setClass(!conf?.titlebar && getOS === 'darwin'); + }); + + useEffect(() => { + const categories = new Map(); + + json?.forEach((i) => { + if (!i.enable) return; + if (!categories.has(i.category)) { + categories.set(i.category, []); + } + categories.get(i?.category).push(i); + }); + setList(Array.from(categories)); + }, [json?.length]); + + const handleLink = async (item: Record) => { + await invoke('wa_window', { + label: btoa(item.url).replace(/[^a-zA-Z0-9]/g, ''), + title: item.title, + url: item.url, + }); + }; + + return ( +
+
+ {list.map((i) => { + return ( +
+ + + {i[1].map((j, idx) => { + return ( + + handleLink(j)}> + {j?.title} + + + ); + })} + + +
+ ); + })} +
+
+ ); +} diff --git a/src/view/settings/General.tsx b/src/view/settings/General.tsx index 6daddb8..4d47aad 100644 --- a/src/view/settings/General.tsx +++ b/src/view/settings/General.tsx @@ -60,6 +60,9 @@ export default function General() { return ( <> + + +