diff --git a/AWESOME.md b/AWESOME.md index bb72e9d..9d75821 100644 --- a/AWESOME.md +++ b/AWESOME.md @@ -10,6 +10,7 @@ - [ChatGPT Export and Share](https://github.com/liady/ChatGPT-pdf) - A Chrome extension for downloading your ChatGPT history to PNG, PDF or creating a sharable link - [ChatGPT for Google](https://github.com/wong2/chat-gpt-google-extension) - A browser extension to display ChatGPT response alongside Google Search results - [ChatGPT Extension](https://github.com/kazuki-sf/ChatGPT_Extension) - ChatGPT Extension is a really simple Chrome Extension (manifest v3) that you can access OpenAI's ChatGPT from anywhere on the web. +- [ChatGPT-Google](https://github.com/ZohaibAhmed/ChatGPT-Google) - Chrome Extension that Integrates ChatGPT (Unofficial) into Google Search `VSCode` @@ -18,3 +19,8 @@ `Bot` - [ChatGPT Telegram Bot](https://github.com/altryne/chatGPT-telegram-bot) - This is a very early attempt at having chatGPT work within a telegram bot + +## Tools + +- [commitgpt](https://github.com/RomanHotsiy/commitgpt) - Automatically generate commit messages using ChatGPT +- [ShareGPT](https://sharegpt.com/) - ShareGPT: Share your wildest ChatGPT conversations with one click. diff --git a/package.json b/package.json index d0a2045..16b0a5b 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "dependencies": { "@tauri-apps/api": "^1.2.0", "antd": "^5.0.6", + "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.4.5" @@ -39,6 +40,7 @@ "devDependencies": { "@tauri-apps/cli": "^1.2.2", "@tauri-release/cli": "^0.2.3", + "@types/lodash": "^4.14.191", "@types/node": "^18.7.10", "@types/react": "^18.0.15", "@types/react-dom": "^18.0.6", diff --git a/src-tauri/src/app/cmd.rs b/src-tauri/src/app/cmd.rs index 4c883c7..da56c18 100644 --- a/src-tauri/src/app/cmd.rs +++ b/src-tauri/src/app/cmd.rs @@ -35,9 +35,8 @@ pub fn get_chat_conf() -> ChatConfJson { } #[command] -pub fn form_confirm(app: AppHandle, data: serde_json::Value) { +pub fn form_confirm(_app: AppHandle, data: serde_json::Value) { ChatConfJson::amend(&serde_json::json!(data)).unwrap(); - tauri::api::process::restart(&app.env()); } #[command] diff --git a/src-tauri/src/app/menu.rs b/src-tauri/src/app/menu.rs index a91b17f..a230641 100644 --- a/src-tauri/src/app/menu.rs +++ b/src-tauri/src/app/menu.rs @@ -1,5 +1,4 @@ use crate::{ - app::window, conf::{self, ChatConfJson}, utils, }; @@ -71,14 +70,6 @@ pub fn init(context: &Context) -> Menu { #[cfg(target_os = "macos")] titlebar_menu.into(), MenuItem::Separator.into(), - // fix: Checking if the site connection is secure - // @link: https://github.com/lencx/ChatGPT/issues/17 - CustomMenuItem::new("user_agent".to_string(), "User Agent") - .accelerator("CmdOrCtrl+U") - .into(), - CustomMenuItem::new("switch_origin".to_string(), "Switch Origin") - .accelerator("CmdOrCtrl+O") - .into(), CustomMenuItem::new("inject_script".to_string(), "Inject Script") .accelerator("CmdOrCtrl+J") .into(), @@ -174,8 +165,6 @@ pub fn menu_handler(event: WindowMenuEvent) { "inject_script" => open(&app, script_path), "go_conf" => utils::open_file(utils::chat_root()), "clear_conf" => utils::clear_conf(&app), - "switch_origin" => window::origin_window(&app), - "user_agent" => window::ua_window(&app), "awesome" => open(&app, conf::AWESOME_URL.to_string()), "titlebar" => { let chat_conf = conf::ChatConfJson::get_chat_conf(); diff --git a/src-tauri/src/app/window.rs b/src-tauri/src/app/window.rs index b0c5f08..1548347 100644 --- a/src-tauri/src/app/window.rs +++ b/src-tauri/src/app/window.rs @@ -23,31 +23,3 @@ pub fn mini_window(handle: &tauri::AppHandle) { .hide() .unwrap(); } - -pub fn origin_window(handle: &tauri::AppHandle) { - let chat_conf = conf::ChatConfJson::get_chat_conf(); - WindowBuilder::new(handle, "origin", WindowUrl::App(chat_conf.origin.into())) - .resizable(false) - .fullscreen(false) - .inner_size(400.0, 300.0) - .always_on_top(true) - .decorations(false) - .initialization_script(include_str!("../assets/core.js")) - .initialization_script(include_str!("../assets/origin.js")) - .build() - .unwrap(); -} - -pub fn ua_window(handle: &tauri::AppHandle) { - let chat_conf = conf::ChatConfJson::get_chat_conf(); - WindowBuilder::new(handle, "ua", WindowUrl::App(chat_conf.origin.into())) - .resizable(false) - .fullscreen(false) - .inner_size(540.0, 480.0) - .always_on_top(true) - .decorations(false) - .initialization_script(include_str!("../assets/core.js")) - .initialization_script(include_str!("../assets/ua.js")) - .build() - .unwrap(); -} diff --git a/src-tauri/src/assets/origin.js b/src-tauri/src/assets/origin.js deleted file mode 100644 index b2264f7..0000000 --- a/src-tauri/src/assets/origin.js +++ /dev/null @@ -1,79 +0,0 @@ -// *** Core Script - Origin *** - -function init() { - document.body.innerHTML = ` -

Switch Origin

- -
- - -
`; - - const srcipt = document.createElement('script'); - srcipt.innerHTML = `const input = document.getElementById('input'); - const cancelBtn = document.getElementById('cancel'); - const confirmBtn = document.getElementById('confirm'); - cancelBtn.addEventListener('click', () => { - window.invoke('form_cancel', { label: 'origin', title: 'Switch Origin', msg: 'Are you sure you want to cancel editing?' }); - }) - confirmBtn.addEventListener('click', () => { - if (/^https?:\\/\\//.test(input.value)) { - window.invoke('form_confirm', { data: { origin: input.value } }); - } else { - window.invoke('form_msg', { label: 'origin', title: 'Switch Origin', msg: 'Invalid URL!' }); - } - })`; - document.head.appendChild(srcipt); -} - -// run init -if ( - document.readyState === "complete" || - document.readyState === "interactive" -) { - init(); -} else { - document.addEventListener("DOMContentLoaded", init); -} \ No newline at end of file diff --git a/src-tauri/src/assets/ua.js b/src-tauri/src/assets/ua.js deleted file mode 100644 index 2bab85b..0000000 --- a/src-tauri/src/assets/ua.js +++ /dev/null @@ -1,91 +0,0 @@ -// *** Core Script - User Agent *** - -function init() { - document.body.innerHTML = ` -

User Agent

-
- - -
-
- - -
-
- - -
`; - - const srcipt = document.createElement('script'); - srcipt.innerHTML = `const ua_pc = document.getElementById('ua_pc'); - const ua_phone = document.getElementById('ua_phone'); - const cancelBtn = document.getElementById('cancel'); - const confirmBtn = document.getElementById('confirm'); - cancelBtn.addEventListener('click', () => { - window.invoke('form_cancel', { label: 'ua', title: 'User Agent', msg: 'Are you sure you want to cancel editing?' }); - }) - confirmBtn.addEventListener('click', () => { - window.invoke('form_confirm', { data: { ua_pc: ua_pc.value, ua_phone: ua_phone.value } }); - })`; - document.head.appendChild(srcipt); -} - -// run init -if ( - document.readyState === "complete" || - document.readyState === "interactive" -) { - init(); -} else { - document.addEventListener("DOMContentLoaded", init); -} \ No newline at end of file diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 641fb19..ccdbd5f 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -69,7 +69,9 @@ "label": "main", "url": "index.html", "title": "ChatGPT", - "visible": false + "visible": false, + "width": 800, + "height": 600 } ] } diff --git a/src/layout/index.scss b/src/layout/index.scss index a535f01..0da7e39 100644 --- a/src/layout/index.scss +++ b/src/layout/index.scss @@ -10,4 +10,15 @@ .chat-container { padding: 20px; +} + +.ant-menu { + .ant-menu-item { + background-color: #f8f8f8; + } +} + +.ant-layout-footer { + color: #666 !important; + opacity: 0.7; } \ No newline at end of file diff --git a/src/layout/index.tsx b/src/layout/index.tsx index d5e4cfa..f6afada 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -1,51 +1,30 @@ import { FC, useState } from 'react'; -import { - DesktopOutlined, - BulbOutlined -} from '@ant-design/icons'; -import type { MenuProps } from 'antd'; import { Layout, Menu } from 'antd'; +import { useNavigate } from 'react-router-dom'; + +import Routes, { menuItems } from '@/routes'; + import './index.scss'; const { Content, Footer, Sider } = Layout; -type MenuItem = Required['items'][number]; - -function getItem( - label: React.ReactNode, - key: React.Key, - icon?: React.ReactNode, - children?: MenuItem[], -): MenuItem { - return { - key, - icon, - children, - label, - } as MenuItem; -} - -const items: MenuItem[] = [ - getItem('General', 'general', ), - getItem('ChatGPT Prompts', 'chatgpt-prompts', ), -]; - interface ChatLayoutProps { - children: React.ReactNode; + children?: React.ReactNode; } const ChatLayout: FC = ({ children }) => { const [collapsed, setCollapsed] = useState(false); + const go = useNavigate(); return ( setCollapsed(value)}>
- + go(i.key)} /> - {children} + diff --git a/src/main.tsx b/src/main.tsx index 517703b..488f0cd 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,14 +2,14 @@ import { StrictMode, Suspense } from 'react'; import { BrowserRouter } from 'react-router-dom'; import ReactDOM from 'react-dom/client'; -import Routes from './routes'; +import Layout from '@/layout'; import './main.scss'; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - + diff --git a/src/routes.tsx b/src/routes.tsx index ce1137c..c885548 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,22 +1,44 @@ -import { useLayoutEffect } from 'react'; -import { useLocation, useRoutes } from 'react-router-dom'; +import { useRoutes } from 'react-router-dom'; +import { + DesktopOutlined, + BulbOutlined +} from '@ant-design/icons'; import type { RouteObject } from 'react-router-dom'; +import type { MenuProps } from 'antd'; -import App from '@view/App'; +import General from '@view/General'; +import ChatGPTPrompts from '@view/ChatGPTPrompts'; -const routes: RouteObject[] = [ +export type ChatRouteObject = { + label: string; + icon?: React.ReactNode, +}; + +export const routes: Array = [ { path: '/', - element: - } + element: , + meta: { + label: 'General', + icon: , + }, + }, + { + path: '/chatgpt-prompts', + element: , + meta: { + label: 'ChatGPT Prompts', + icon: , + }, + }, ]; +type MenuItem = Required['items'][number]; +export const menuItems: MenuItem[] = routes.map(i => ({ + ...i.meta, + key: i.path || '', +})); + export default () => { - const location = useLocation(); - const pathname = location.pathname; - useLayoutEffect(() => { - const name = pathname.substring(1).replace(/\//gi, '_'); - document.body.className = `${name ? name : 'main'}_screen` - }, [pathname]); return useRoutes(routes); }; \ No newline at end of file diff --git a/src/view/App.tsx b/src/view/App.tsx deleted file mode 100644 index 85dbcf9..0000000 --- a/src/view/App.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import Layout from "@layout/index"; - -export default function Dashboard() { - return ( - - Hello - - ) -} \ No newline at end of file diff --git a/src/view/ChatGPTPrompts.tsx b/src/view/ChatGPTPrompts.tsx new file mode 100644 index 0000000..3e4087f --- /dev/null +++ b/src/view/ChatGPTPrompts.tsx @@ -0,0 +1,7 @@ +export default function Dashboard() { + return ( +
+ TODO: ChatGPT Prompts +
+ ) +} \ No newline at end of file diff --git a/src/view/General.tsx b/src/view/General.tsx new file mode 100644 index 0000000..43654a7 --- /dev/null +++ b/src/view/General.tsx @@ -0,0 +1,93 @@ +import { useEffect, useState } from 'react'; +import { Form, Radio, Switch, Input, Button, Space, message } from 'antd'; +import { invoke } from '@tauri-apps/api'; +import { platform } from '@tauri-apps/api/os'; +import { ask } from '@tauri-apps/api/dialog'; +import { relaunch } from '@tauri-apps/api/process'; +import { clone, pick, isEqual } from 'lodash'; + +const restartNames = ['origin', 'ua_window', 'ua_tray'] + +export default function General() { + const [form] = Form.useForm(); + const [platformInfo, setPlatform] = useState(''); + const [chatConf, setChatConf] = useState(null); + + const init = async () => { + setPlatform(await platform()); + const chatData = await invoke('get_chat_conf'); + setChatConf(chatData); + } + + useEffect(() => { + init(); + }, []) + + useEffect(() => { + form.setFieldsValue(clone(chatConf)); + }, [chatConf]) + + console.log('«28» /src/view/General.tsx ~> ', chatConf); + + + const onCancel = () => { + form.setFieldsValue(chatConf); + }; + + const onFinish = async (values: any) => { + await invoke('form_confirm', { data: values, label: 'main' }); + console.log('«33» /src/view/General.tsx ~> ', pick(chatConf, restartNames), pick(values, restartNames)); + + if (!isEqual(pick(chatConf, restartNames), pick(values, restartNames))) { + const isOk = await ask(`Configuration saved successfully, whether to restart?`, { + title: 'ChatGPT Preferences' + }); + if (isOk) relaunch(); + return; + } + + message.success('Configuration saved successfully'); + }; + + return ( +
+ + + Light + Dark + + + + + + {platformInfo === 'darwin' && ( + + + + )} + + + + + + + + + + + + + + + +
+ ) +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 9b493e0..474aa73 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,7 @@ "jsx": "react-jsx", "baseUrl": ".", "paths": { + "@/*": ["src/*"], "@view/*": ["src/view/*"], "@comps/*": ["src/components/*"], "@layout/*": ["src/layout/*"],