diff --git a/package.json b/package.json index a026e54..dac59e6 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "dependencies": { "@ant-design/icons": "^4.8.0", "@tauri-apps/api": "^1.2.0", - "antd": "^5.0.6", + "antd": "^5.1.0", "dayjs": "^1.11.7", "lodash": "^4.17.21", "react": "^18.2.0", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 5cbf212..8c4540d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -19,9 +19,11 @@ serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } tauri = { version = "1.2.2", features = ["api-all", "devtools", "system-tray", "updater"] } tauri-plugin-positioner = { version = "1.0.4", features = ["system-tray"] } +tokio = { version = "1.23.0", features = ["macros"] } log = "0.4.17" csv = "1.1.6" thiserror = "1.0.38" +reqwest = "0.11.13" [dependencies.tauri-plugin-log] git = "https://github.com/tauri-apps/tauri-plugin-log" diff --git a/src-tauri/src/app/cmd.rs b/src-tauri/src/app/cmd.rs index 0d4adb5..fc5d3c0 100644 --- a/src-tauri/src/app/cmd.rs +++ b/src-tauri/src/app/cmd.rs @@ -88,3 +88,12 @@ 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(); +} diff --git a/src-tauri/src/app/menu.rs b/src-tauri/src/app/menu.rs index 6d548c4..e59b8aa 100644 --- a/src-tauri/src/app/menu.rs +++ b/src-tauri/src/app/menu.rs @@ -47,6 +47,10 @@ pub fn init() -> Menu { let preferences_menu = Submenu::new( "Preferences", Menu::with_items([ + CustomMenuItem::new("control_center".to_string(), "Control Center") + .accelerator("CmdOrCtrl+Shift+P") + .into(), + MenuItem::Separator.into(), Submenu::new( "Theme", Menu::new() @@ -67,13 +71,11 @@ pub fn init() -> Menu { 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") .into(), - CustomMenuItem::new("control_center".to_string(), "Control Center") - .accelerator("CmdOrCtrl+Shift+P") - .into(), + MenuItem::Separator.into(), + CustomMenuItem::new("sync_prompts".to_string(), "Sync Prompts").into(), MenuItem::Separator.into(), CustomMenuItem::new("go_conf".to_string(), "Go to Config") .accelerator("CmdOrCtrl+Shift+G") @@ -178,6 +180,21 @@ 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()), + "sync_prompts" => { + tauri::api::dialog::ask( + app.get_window("main").as_ref(), + "Sync Prompts", + "Data sync will enable all prompts, are you sure you want to sync?", + move |is_restart| { + if is_restart { + app.get_window("main") + .unwrap() + .eval("window.__sync_prompts && window.__sync_prompts()") + .unwrap() + } + }, + ); + } "hide_dock_icon" => { ChatConfJson::amend(&serde_json::json!({ "hide_dock_icon": true }), Some(app)).unwrap() } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 52086f4..22841b3 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -15,7 +15,8 @@ use tauri_plugin_log::{ LogTarget, LoggerBuilder, }; -fn main() { +#[tokio::main] +async fn main() { ChatConfJson::init(); let chat_conf = ChatConfJson::get_chat_conf(); let context = tauri::generate_context!(); @@ -59,6 +60,7 @@ fn main() { cmd::open_file, cmd::get_chat_model, cmd::parse_prompt, + cmd::window_reload, fs_extra::metadata, ]) .setup(setup::init) diff --git a/src/hooks/useChatModel.ts b/src/hooks/useChatModel.ts index dca7058..d9a6b55 100644 --- a/src/hooks/useChatModel.ts +++ b/src/hooks/useChatModel.ts @@ -1,5 +1,6 @@ import { useState } from 'react'; import { clone } from 'lodash'; +import { invoke } from '@tauri-apps/api'; import { CHAT_MODEL_JSON, readJSON, writeJSON } from '@/utils'; import useInit from '@/hooks/useInit'; @@ -16,6 +17,7 @@ export default function useChatModel(key: string) { const oData = clone(modelJson); oData[key] = data; await writeJSON(CHAT_MODEL_JSON, oData); + await invoke('window_reload', { label: 'core' }); setModelJson(oData); } diff --git a/src/hooks/useEvent.ts b/src/hooks/useEvent.ts new file mode 100644 index 0000000..79286da --- /dev/null +++ b/src/hooks/useEvent.ts @@ -0,0 +1,28 @@ +import { invoke, http, fs, dialog } from '@tauri-apps/api'; + +import useInit from '@/hooks/useInit'; +import useChatModel from '@/hooks/useChatModel'; +import { GITHUB_PROMPTS_CSV_URL, chatPromptsPath, genCmd } from '@/utils'; + +export default function useEvent() { + const { modelSet } = useChatModel('sys_sync_prompts'); + // Using `emit` and `listen` will be triggered multiple times in development mode. + // So here we use `eval` to call `__sync_prompt` + useInit(() => { + (window as any).__sync_prompts = async () => { + const res = await http.fetch(GITHUB_PROMPTS_CSV_URL, { + method: 'GET', + responseType: http.ResponseType.Text, + }); + const data = (res.data || '') as string; + if (res.ok) { + await fs.writeTextFile(await chatPromptsPath(), data); + const list: Record[] = await invoke('parse_prompt', { data }); + modelSet(list.map(i => ({ cmd: genCmd(i.act), enable: true, tags: ['chatgpt-prompts'], ...i }))); + dialog.message('ChatGPT Prompts data has been synchronized!'); + } else { + dialog.message('ChatGPT Prompts data sync failed, please try again!'); + } + } + }) +} diff --git a/src/layout/index.tsx b/src/layout/index.tsx index a617f89..36c514e 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -39,6 +39,7 @@ const ChatLayout: FC = ({ children }) => { mode="inline" inlineIndent={12} items={menuItems} + defaultOpenKeys={['/model']} onClick={(i) => go(i.key)} /> diff --git a/src/main.tsx b/src/main.tsx index 488f0cd..2bbdc78 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,15 +2,23 @@ import { StrictMode, Suspense } from 'react'; import { BrowserRouter } from 'react-router-dom'; import ReactDOM from 'react-dom/client'; +import useEvent from '@/hooks/useEvent'; import Layout from '@/layout'; import './main.scss'; +const App = () => { + useEvent(); + return ( + + + + ); +} + ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - - - + ); diff --git a/src/routes.tsx b/src/routes.tsx index 604d5f0..eff592e 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -9,9 +9,9 @@ import { import type { MenuProps } from 'antd'; import General from '@view/General'; -import LanguageModel from '@/view/LanguageModel'; -import SyncPrompts from '@/view/SyncPrompts'; -import SyncMore from '@/view/SyncMore'; +import UserCustom from '@/view/model/UserCustom'; +import SyncPrompts from '@/view/model/SyncPrompts'; +import SyncMore from '@/view/model/SyncMore'; export type ChatRouteMetaObject = { label: string; @@ -35,7 +35,7 @@ export const routes: Array = [ }, }, { - path: '/language-model', + path: '/model', meta: { label: 'Language Model', icon: , @@ -43,7 +43,7 @@ export const routes: Array = [ children: [ { path: 'user-custom', - element: , + element: , meta: { label: 'User Custom', icon: , diff --git a/src/utils.ts b/src/utils.ts index 6fa440a..5f7909a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -48,4 +48,6 @@ export const writeJSON = async (path: string, data: Record) => { await writeTextFile(file, JSON.stringify(data, null, 2)); } -export const fmtDate = (date: any) => dayjs(date).format('YYYY-MM-DD HH:mm:ss'); \ No newline at end of file +export const fmtDate = (date: any) => dayjs(date).format('YYYY-MM-DD HH:mm:ss'); + +export const genCmd = (act: string) => act.replace(/\s+|\/+/g, '_').replace(/[^\d\w]/g, '').toLocaleLowerCase(); \ No newline at end of file diff --git a/src/view/SyncMore/config.tsx b/src/view/SyncMore/config.tsx deleted file mode 100644 index a2e2a02..0000000 --- a/src/view/SyncMore/config.tsx +++ /dev/null @@ -1,22 +0,0 @@ -// import { Switch, Tag, Tooltip } from 'antd'; - -export const genCmd = (act: string) => act.replace(/\s+|\/+/g, '_').replace(/[^\d\w]/g, '').toLocaleLowerCase(); - -export const recordColumns = () => [ - { - title: 'URL', - dataIndex: 'url', - // fixed: 'left', - // width: 120, - key: 'url', - }, - { - title: 'File Type', - dataIndex: 'file_type', - key: 'file_type', - // width: 200, - }, - { - title: 'Action', - } -]; diff --git a/src/view/model/SyncMore/config.tsx b/src/view/model/SyncMore/config.tsx new file mode 100644 index 0000000..3fc3429 --- /dev/null +++ b/src/view/model/SyncMore/config.tsx @@ -0,0 +1,15 @@ +export const recordColumns = () => [ + { + title: 'URL', + dataIndex: 'url', + key: 'url', + }, + { + title: 'File Type', + dataIndex: 'file_type', + key: 'file_type', + }, + { + title: 'Action', + } +]; diff --git a/src/view/SyncMore/index.scss b/src/view/model/SyncMore/index.scss similarity index 100% rename from src/view/SyncMore/index.scss rename to src/view/model/SyncMore/index.scss diff --git a/src/view/SyncMore/index.tsx b/src/view/model/SyncMore/index.tsx similarity index 95% rename from src/view/SyncMore/index.tsx rename to src/view/model/SyncMore/index.tsx index f9f9d46..a88e96b 100644 --- a/src/view/SyncMore/index.tsx +++ b/src/view/model/SyncMore/index.tsx @@ -9,7 +9,7 @@ export default function SyncMore() { act.replace(/\s+|\/+/g, '_').replace(/[^\d\w]/g, '').toLocaleLowerCase(); +import { genCmd } from '@/utils'; export const modelColumns = () => [ { diff --git a/src/view/SyncPrompts/index.scss b/src/view/model/SyncPrompts/index.scss similarity index 100% rename from src/view/SyncPrompts/index.scss rename to src/view/model/SyncPrompts/index.scss diff --git a/src/view/SyncPrompts/index.tsx b/src/view/model/SyncPrompts/index.tsx similarity index 87% rename from src/view/SyncPrompts/index.tsx rename to src/view/model/SyncPrompts/index.tsx index 12809ec..70136df 100644 --- a/src/view/SyncPrompts/index.tsx +++ b/src/view/model/SyncPrompts/index.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { Table, Button, message } from 'antd'; +import { Table, Button, message, Popconfirm } from 'antd'; import { invoke } from '@tauri-apps/api'; import { fetch, ResponseType } from '@tauri-apps/api/http'; import { writeTextFile } from '@tauri-apps/api/fs'; @@ -8,15 +8,14 @@ import useColumns from '@/hooks/useColumns'; import useData from '@/hooks/useData'; import useChatModel from '@/hooks/useChatModel'; import useTable, { TABLE_PAGINATION } from '@/hooks/useTable'; -import { fmtDate, chatPromptsPath, GITHUB_PROMPTS_CSV_URL } from '@/utils'; -import { modelColumns, genCmd } from './config'; +import { fmtDate, chatPromptsPath, GITHUB_PROMPTS_CSV_URL, genCmd } from '@/utils'; +import { modelColumns } from './config'; import './index.scss'; const promptsURL = 'https://github.com/f/awesome-chatgpt-prompts/blob/main/prompts.csv'; export default function LanguageModel() { const { rowSelection, selectedRowIDs } = useTable(); - const [loading, setLoading] = useState(false); const [lastUpdated, setLastUpdated] = useState(); const { modelJson, modelSet } = useChatModel('sys_sync_prompts'); const { opData, opInit, opReplace, opReplaceItems, opSafeKey } = useData([]); @@ -35,7 +34,6 @@ export default function LanguageModel() { }, [modelJson?.sys_sync_prompts]) const handleSync = async () => { - setLoading(true); const res = await fetch(GITHUB_PROMPTS_CSV_URL, { method: 'GET', responseType: ResponseType.Text, @@ -52,7 +50,6 @@ export default function LanguageModel() { } else { message.error('ChatGPT Prompts data sync failed, please try again!'); } - setLoading(false); }; useEffect(() => { @@ -79,7 +76,15 @@ export default function LanguageModel() { )} - + Data sync will enable all prompts,
are you sure you want to sync?} + placement="topLeft" + onConfirm={handleSync} + okText="Yes" + cancelText="No" + > + +
URL: f/awesome-chatgpt-prompts/prompts.csv diff --git a/src/view/LanguageModel/Form.tsx b/src/view/model/UserCustom/Form.tsx similarity index 100% rename from src/view/LanguageModel/Form.tsx rename to src/view/model/UserCustom/Form.tsx diff --git a/src/view/LanguageModel/config.tsx b/src/view/model/UserCustom/config.tsx similarity index 100% rename from src/view/LanguageModel/config.tsx rename to src/view/model/UserCustom/config.tsx diff --git a/src/view/LanguageModel/index.scss b/src/view/model/UserCustom/index.scss similarity index 100% rename from src/view/LanguageModel/index.scss rename to src/view/model/UserCustom/index.scss diff --git a/src/view/LanguageModel/index.tsx b/src/view/model/UserCustom/index.tsx similarity index 98% rename from src/view/LanguageModel/index.tsx rename to src/view/model/UserCustom/index.tsx index e25a0d0..c9d23ab 100644 --- a/src/view/LanguageModel/index.tsx +++ b/src/view/model/UserCustom/index.tsx @@ -7,7 +7,7 @@ import useData from '@/hooks/useData'; import useChatModel from '@/hooks/useChatModel'; import useColumns from '@/hooks/useColumns'; import { TABLE_PAGINATION } from '@/hooks/useTable'; -import { chatModelPath } from '@/utils'; +import { chatModelPath, genCmd } from '@/utils'; import { modelColumns } from './config'; import LanguageModelForm from './Form'; import './index.scss';