mirror of
https://github.com/FranP-code/ChatGPT.git
synced 2025-10-13 00:13:25 +00:00
Merge pull request #32 from lencx/dev
This commit is contained in:
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1 +1,3 @@
|
||||
*.js linguist-vendored
|
||||
*.js linguist-vendored
|
||||
*.tsx linguist-vendored
|
||||
*.scss linguist-vendored
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -75,7 +75,7 @@ jobs:
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install app dependencies and build it
|
||||
run: yarn
|
||||
run: yarn && yarn build:fe
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0.3
|
||||
env:
|
||||
|
||||
29
.gitignore
vendored
29
.gitignore
vendored
@@ -1,9 +1,32 @@
|
||||
.DS_Store
|
||||
*.lock
|
||||
|
||||
package-lock.json
|
||||
node_modules/
|
||||
yarn.lock
|
||||
*.lock
|
||||
|
||||
# rust
|
||||
target/
|
||||
Cargo.lock
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
@@ -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.
|
||||
|
||||
22
README-ZH.md
22
README-ZH.md
@@ -20,9 +20,9 @@
|
||||
|
||||
**最新版:**
|
||||
|
||||
- `Mac`: [ChatGPT_0.2.1_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/ChatGPT_0.2.1_x64.dmg)
|
||||
- `Linux`: [chat-gpt_0.2.1_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/chat-gpt_0.2.1_amd64.deb)
|
||||
- `Windows`: [ChatGPT_0.2.1_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/ChatGPT_0.2.1_x64_en-US.msi)
|
||||
- `Mac`: [ChatGPT_v0.3.0_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/vv0.3.0/ChatGPT_v0.3.0_x64.dmg)
|
||||
- `Linux`: [chat-gpt_v0.3.0_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/vv0.3.0/chat-gpt_v0.3.0_amd64.deb)
|
||||
- `Windows`: [ChatGPT_v0.3.0_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/vv0.3.0/ChatGPT_v0.3.0_x64_en-US.msi)
|
||||
|
||||
[其他版本...](https://github.com/lencx/ChatGPT/releases)
|
||||
|
||||
@@ -60,9 +60,12 @@ cask "popcorn-time", args: { "no-quarantine": true }
|
||||
- `Theme` - `Light`, `Dark` (仅支持 macOS 和 Windows)
|
||||
- `Always On Top`: 窗口置顶
|
||||
- `Titlebar`: 是否显示 `Titlebar`,仅 macOS 支持
|
||||
- `User Agent` ([#17](https://github.com/lencx/ChatGPT/issues/17)): 自定义 `user agent` 防止网站安全检测,默认值为空。
|
||||
- `Inject Script`: 用于修改网站的用户自定义脚本
|
||||
- `Switch Origin` ([#14](https://github.com/lencx/ChatGPT/issues/14)): 切换网站源地址,默认为 `https://chat.openai.com`。需要注意的是镜像网站的 UI 需要和原网站一致,否则可能会导致某些功能不工作
|
||||
- `Control Center`: ChatGPT 应用的控制中心,它将为应用提供无限的可能
|
||||
- 设置 `Theme`,`Always 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/*`)
|
||||
- `Clear Config`: 清除 ChatGPT 配置数据 (`path: ~/.chatgpt/*`), 这是危险操作,请提前备份数据
|
||||
- `Restart ChatGPT`: 重启应用。如果注入脚本编辑完成,或者应用可卡死可以通过此菜单重新启动应用
|
||||
- `Awesome ChatGPT`: 一个很棒的 ChatGPT 推荐列表
|
||||
@@ -75,9 +78,10 @@ cask "popcorn-time", args: { "no-quarantine": true }
|
||||
|
||||
## 👀 预览
|
||||
|
||||
<img width="320" src="./assets/install.png" alt="install"> <img width="320" src="./assets/chat.png" alt="chat">
|
||||
<img width="320" src="./assets/install.png" alt="install"> <img width="320" src="./assets/control-center.png" alt="control center">
|
||||
<img width="320" src="./assets/export.png" alt="export"> <img width="320" src="./assets/tray.png" alt="tray">
|
||||
<img width="320" src="./assets/chat-ua.png" alt="user agent"> <img width="320" src="./assets/auto-update.png" alt="auto update">
|
||||
<img width="320" src="./assets/tray-login.png" alt="tray login"> <img width="320" src="./assets/auto-update.png" alt="auto update">
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -95,6 +99,10 @@ cask "popcorn-time", args: { "no-quarantine": true }
|
||||
|
||||
如果升级应用后无法打开,请尝试清除配置,它位于此目录 `~/.chatgpt/*`。
|
||||
|
||||
### 主窗口已经登录,但是系统托盘窗口显示未登录
|
||||
|
||||
可通过菜单项里的 `Restart ChatGPT` 重启应用来修复这个问题(`Menu -> Preferences -> Restart ChatGPT`)。
|
||||
|
||||
### 它是否安全?
|
||||
|
||||
它是安全的,仅仅只是对 [OpenAI ChatGPT](https://chat.openai.com) 网站的包装,注入了一些额外功能(均在本地,未发起网络请求),如果存疑,可以检查源代码。
|
||||
|
||||
21
README.md
21
README.md
@@ -20,9 +20,9 @@
|
||||
|
||||
**Latest:**
|
||||
|
||||
- `Mac`: [ChatGPT_0.2.1_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/ChatGPT_0.2.1_x64.dmg)
|
||||
- `Linux`: [chat-gpt_0.2.1_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/chat-gpt_0.2.1_amd64.deb)
|
||||
- `Windows`: [ChatGPT_0.2.1_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/ChatGPT_0.2.1_x64_en-US.msi)
|
||||
- `Mac`: [ChatGPT_vv0.3.0_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/vvv0.3.0/ChatGPT_vv0.3.0_x64.dmg)
|
||||
- `Linux`: [chat-gpt_vv0.3.0_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/vvv0.3.0/chat-gpt_vv0.3.0_amd64.deb)
|
||||
- `Windows`: [ChatGPT_vv0.3.0_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/vvv0.3.0/ChatGPT_vv0.3.0_x64_en-US.msi)
|
||||
|
||||
[Other version...](https://github.com/lencx/ChatGPT/releases)
|
||||
|
||||
@@ -60,9 +60,12 @@ cask "popcorn-time", args: { "no-quarantine": true }
|
||||
- `Theme` - `Light`, `Dark` (Only macOS and Windows are supported).
|
||||
- `Always on Top`: The window is always on top of other windows.
|
||||
- `Titlebar`: Whether to display the titlebar, supported by macOS only.
|
||||
- `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.
|
||||
- `Inject Script`: Using scripts to modify pages.
|
||||
- `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.
|
||||
- `Control Center`: The control center of ChatGPT application, it will give unlimited imagination to the application.
|
||||
- `Theme`, `Always 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/*`).
|
||||
- `Clear Config`: Clear the configuration file (`path: ~/.chatgpt/*`), dangerous operation, please backup the data in advance.
|
||||
- `Restart ChatGPT`: Restart the application, for example: the program is stuck or the injection script can take effect by restarting the application after editing.
|
||||
- `Awesome ChatGPT`: Recommended Related Resources.
|
||||
@@ -81,9 +84,9 @@ cask "popcorn-time", args: { "no-quarantine": true }
|
||||
|
||||
## 👀 Preview
|
||||
|
||||
<img width="320" src="./assets/install.png" alt="install"> <img width="320" src="./assets/chat.png" alt="chat">
|
||||
<img width="320" src="./assets/install.png" alt="install"> <img width="320" src="./assets/control-center.png" alt="control center">
|
||||
<img width="320" src="./assets/export.png" alt="export"> <img width="320" src="./assets/tray.png" alt="tray">
|
||||
<img width="320" src="./assets/chat-ua.png" alt="user agent"> <img width="320" src="./assets/auto-update.png" alt="auto update">
|
||||
<img width="320" src="./assets/tray-login.png" alt="tray login"> <img width="320" src="./assets/auto-update.png" alt="auto update">
|
||||
|
||||
---
|
||||
|
||||
@@ -95,6 +98,10 @@ cask "popcorn-time", args: { "no-quarantine": true }
|
||||
|
||||
If you cannot open the application after the upgrade, please try to clear the configuration file, which is in the `~/.chatgpt/*` directory.
|
||||
|
||||
### Out of sync login status between multiple windows
|
||||
|
||||
If you have already logged in in the main window, but the system tray window shows that you are not logged in, you can fix it by restarting the application (`Menu -> Preferences -> Restart ChatGPT`).
|
||||
|
||||
### Is it safe?
|
||||
|
||||
It's safe, just a wrapper for [OpenAI ChatGPT](https://chat.openai.com) website, no other data transfer exists (you can check the source code).
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# UPDATE LOG
|
||||
|
||||
## v0.3.0
|
||||
|
||||
fix: can't open ChatGPT
|
||||
|
||||
feat: menu enhancement
|
||||
- the control center of ChatGPT application
|
||||
- open the configuration file directory
|
||||
|
||||
## v0.2.2
|
||||
|
||||
feat:
|
||||
- menu: go to config
|
||||
|
||||
## v0.2.1
|
||||
|
||||
feat: menu optimization
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 773 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 391 KiB |
BIN
assets/chat.png
BIN
assets/chat.png
Binary file not shown.
|
Before Width: | Height: | Size: 653 KiB |
BIN
assets/control-center.png
Normal file
BIN
assets/control-center.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 500 KiB |
BIN
assets/tray-login.png
Normal file
BIN
assets/tray-login.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 989 KiB |
0
dist/.gitkeep
vendored
0
dist/.gitkeep
vendored
13
index.html
Normal file
13
index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ChatGPT</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
23
package.json
23
package.json
@@ -2,6 +2,8 @@
|
||||
"name": "chatgpt",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev:fe": "vite",
|
||||
"build:fe": "tsc && vite build",
|
||||
"dev": "yarn tauri dev",
|
||||
"build": "yarn tauri build",
|
||||
"updater": "tr updater",
|
||||
@@ -27,8 +29,25 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/lencx/ChatGPT"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^1.2.1",
|
||||
"@tauri-release/cli": "^0.2.3"
|
||||
"@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",
|
||||
"@vitejs/plugin-react": "^3.0.0",
|
||||
"sass": "^1.56.2",
|
||||
"typescript": "^4.9.4",
|
||||
"vite": "^4.0.0",
|
||||
"vite-tsconfig-paths": "^4.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
11
scripts/download.js
vendored
11
scripts/download.js
vendored
@@ -2,8 +2,8 @@ const fs = require('fs');
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
|
||||
async function init() {
|
||||
const content = fs.readFileSync('README.md', 'utf8').split('\n');
|
||||
async function rewrite(filename) {
|
||||
const content = fs.readFileSync(filename, 'utf8').split('\n');
|
||||
const startRe = /<!-- download start -->/;
|
||||
const endRe = /<!-- download end -->/;
|
||||
|
||||
@@ -20,7 +20,12 @@ async function init() {
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync('README.md', content.join('\n'), 'utf8');
|
||||
fs.writeFileSync(filename, content.join('\n'), 'utf8');
|
||||
}
|
||||
|
||||
async function init() {
|
||||
rewrite('README.md');
|
||||
rewrite('README-ZH.md');
|
||||
}
|
||||
|
||||
init().catch(console.error);
|
||||
@@ -35,9 +35,8 @@ pub fn get_chat_conf() -> ChatConfJson {
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn form_confirm(app: AppHandle, data: serde_json::Value) {
|
||||
ChatConfJson::amend(&serde_json::json!(data)).unwrap();
|
||||
tauri::api::process::restart(&app.env());
|
||||
pub fn form_confirm(_app: AppHandle, data: serde_json::Value) {
|
||||
ChatConfJson::amend(&serde_json::json!(data), None).unwrap();
|
||||
}
|
||||
|
||||
#[command]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
app::window,
|
||||
conf::{self, ChatConfJson},
|
||||
utils,
|
||||
};
|
||||
@@ -26,7 +25,7 @@ pub fn init(context: &Context<EmbeddedAssets>) -> Menu {
|
||||
.add_native_item(MenuItem::Quit),
|
||||
);
|
||||
|
||||
let always_on_top = CustomMenuItem::new("always_on_top".to_string(), "Always On Top")
|
||||
let always_on_top = CustomMenuItem::new("always_on_top".to_string(), "Always on Top")
|
||||
.accelerator("CmdOrCtrl+T");
|
||||
let titlebar =
|
||||
CustomMenuItem::new("titlebar".to_string(), "Titlebar").accelerator("CmdOrCtrl+B");
|
||||
@@ -67,27 +66,25 @@ pub fn init(context: &Context<EmbeddedAssets>) -> 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(),
|
||||
CustomMenuItem::new("control_center".to_string(), "Control Center")
|
||||
.accelerator("CmdOrCtrl+Shift+P")
|
||||
.into(),
|
||||
MenuItem::Separator.into(),
|
||||
CustomMenuItem::new("go_conf".to_string(), "Go to Config")
|
||||
.accelerator("CmdOrCtrl+Shift+G")
|
||||
.into(),
|
||||
CustomMenuItem::new("clear_conf".to_string(), "Clear Config")
|
||||
.accelerator("CmdOrCtrl+D")
|
||||
.accelerator("CmdOrCtrl+Shift+D")
|
||||
.into(),
|
||||
CustomMenuItem::new("restart".to_string(), "Restart ChatGPT")
|
||||
.accelerator("CmdOrCtrl+Shift+R")
|
||||
.into(),
|
||||
MenuItem::Separator.into(),
|
||||
CustomMenuItem::new("awesome".to_string(), "Awesome ChatGPT")
|
||||
.accelerator("CmdOrCtrl+Z")
|
||||
.accelerator("CmdOrCtrl+Shift+A")
|
||||
.into(),
|
||||
]),
|
||||
);
|
||||
@@ -162,15 +159,19 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
|
||||
match menu_id {
|
||||
// Preferences
|
||||
"control_center" => app.get_window("main").unwrap().show().unwrap(),
|
||||
"restart" => tauri::api::process::restart(&app.env()),
|
||||
"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();
|
||||
ChatConfJson::amend(&serde_json::json!({ "titlebar": !chat_conf.titlebar })).unwrap();
|
||||
ChatConfJson::amend(
|
||||
&serde_json::json!({ "titlebar": !chat_conf.titlebar }),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
tauri::api::process::restart(&app.env());
|
||||
}
|
||||
"theme_light" | "theme_dark" => {
|
||||
@@ -179,8 +180,7 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
} else {
|
||||
"Light"
|
||||
};
|
||||
ChatConfJson::amend(&serde_json::json!({ "theme": theme })).unwrap();
|
||||
tauri::api::process::restart(&app.env());
|
||||
ChatConfJson::amend(&serde_json::json!({ "theme": theme }), Some(app)).unwrap();
|
||||
}
|
||||
"always_on_top" => {
|
||||
let mut always_on_top = state.always_on_top.lock().unwrap();
|
||||
@@ -190,7 +190,11 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
.set_selected(*always_on_top)
|
||||
.unwrap();
|
||||
win.set_always_on_top(*always_on_top).unwrap();
|
||||
ChatConfJson::amend(&serde_json::json!({ "always_on_top": *always_on_top })).unwrap();
|
||||
ChatConfJson::amend(
|
||||
&serde_json::json!({ "always_on_top": *always_on_top }),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
// View
|
||||
"reload" => win.eval("window.location.reload()").unwrap(),
|
||||
|
||||
@@ -21,7 +21,7 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
|
||||
.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_pc)
|
||||
.user_agent(&chat_conf.ua_window)
|
||||
.build()?;
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
@@ -37,7 +37,7 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
|
||||
.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_pc)
|
||||
.user_agent(&chat_conf.ua_window)
|
||||
.build()?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -17,37 +17,9 @@ pub fn mini_window(handle: &tauri::AppHandle) {
|
||||
.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_phone)
|
||||
.user_agent(&chat_conf.ua_tray)
|
||||
.build()
|
||||
.unwrap()
|
||||
.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();
|
||||
}
|
||||
|
||||
1
src-tauri/src/assets/core.js
vendored
1
src-tauri/src/assets/core.js
vendored
@@ -1,4 +1,5 @@
|
||||
// *** Core Script - IPC ***
|
||||
|
||||
const uid = () => window.crypto.getRandomValues(new Uint32Array(1))[0];
|
||||
function transformCallback(callback = () => {}, once = false) {
|
||||
const identifier = uid();
|
||||
|
||||
20
src-tauri/src/assets/export.js
vendored
20
src-tauri/src/assets/export.js
vendored
@@ -1,5 +1,5 @@
|
||||
// *** Core Script - Export ***
|
||||
// @ref: https://github.com/liady/ChatGPT-pdf/blob/main/src/content_script.js
|
||||
// @ref: https://github.com/liady/ChatGPT-pdf
|
||||
|
||||
async function init() {
|
||||
const chatConf = await invoke('get_chat_conf') || {};
|
||||
@@ -78,22 +78,9 @@ function downloadThread({ as = Format.PNG } = {}) {
|
||||
const pixelRatio = window.devicePixelRatio;
|
||||
const minRatio = as === Format.PDF ? 2 : 2.5;
|
||||
window.devicePixelRatio = Math.max(pixelRatio, minRatio);
|
||||
|
||||
html2canvas(elements.thread, {
|
||||
letterRendering: true,
|
||||
onclone: function (cloneDoc) {
|
||||
//Make small fix of position to all the text containers
|
||||
let listOfTexts = cloneDoc.getElementsByClassName("min-h-[20px]");
|
||||
Array.from(listOfTexts).forEach((text) => {
|
||||
text.style.position = "relative";
|
||||
text.style.top = "-8px";
|
||||
});
|
||||
|
||||
//Delete copy button from code blocks
|
||||
let listOfCopyBtns = cloneDoc.querySelectorAll("button.flex");
|
||||
Array.from(listOfCopyBtns).forEach(
|
||||
(btn) => (btn.style.visibility = "hidden")
|
||||
);
|
||||
},
|
||||
}).then(async function (canvas) {
|
||||
elements.restoreLocation();
|
||||
window.devicePixelRatio = pixelRatio;
|
||||
@@ -166,6 +153,8 @@ class Elements {
|
||||
img.setAttribute("srcset_old", srcset);
|
||||
img.setAttribute("srcset", "");
|
||||
});
|
||||
//Fix to the text shifting down when generating the canvas
|
||||
document.body.style.lineHeight = "0.5";
|
||||
}
|
||||
restoreLocation() {
|
||||
this.hiddens.forEach((el) => {
|
||||
@@ -182,6 +171,7 @@ class Elements {
|
||||
img.setAttribute("srcset", srcset);
|
||||
img.setAttribute("srcset_old", "");
|
||||
});
|
||||
document.body.style.lineHeight = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
77
src-tauri/src/assets/origin.js
vendored
77
src-tauri/src/assets/origin.js
vendored
@@ -1,77 +0,0 @@
|
||||
function init() {
|
||||
document.body.innerHTML = `<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
input {
|
||||
all: unset;
|
||||
width: 280px;
|
||||
height: 30px;
|
||||
margin-bottom: 10px;
|
||||
padding: 0 5px;
|
||||
border: solid 2px #d8d8d8;
|
||||
background-color: #fff;
|
||||
border-radius: 5px !important;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
button {
|
||||
all: unset;
|
||||
height: 30px;
|
||||
font-size: 16px;
|
||||
padding: 0 10px;
|
||||
line-height: 30px;
|
||||
margin: 0 5px;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#cancel {
|
||||
background-color: #999;
|
||||
}
|
||||
#confirm {
|
||||
background-color: #10a37f;
|
||||
}
|
||||
</style>
|
||||
<h3>Switch Origin</h3>
|
||||
<input id="input" type="text" autocapitalize="off" autocomplete="off" spellcheck="false" autofocus placeholder="https://chat.openai.com" />
|
||||
<div class="btns">
|
||||
<button id="cancel">Cancel</button>
|
||||
<button id="confirm">Confirm</button>
|
||||
</div>`;
|
||||
|
||||
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);
|
||||
}
|
||||
89
src-tauri/src/assets/ua.js
vendored
89
src-tauri/src/assets/ua.js
vendored
@@ -1,89 +0,0 @@
|
||||
function init() {
|
||||
document.body.innerHTML = `<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
textarea {
|
||||
all: unset;
|
||||
width: 300px;
|
||||
height: 60px;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
border: solid 2px #d8d8d8;
|
||||
background-color: #fff;
|
||||
border-radius: 5px !important;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
button {
|
||||
all: unset;
|
||||
height: 30px;
|
||||
font-size: 16px;
|
||||
padding: 0 10px;
|
||||
line-height: 30px;
|
||||
margin: 0 5px;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#cancel {
|
||||
background-color: #999;
|
||||
}
|
||||
#confirm {
|
||||
background-color: #10a37f;
|
||||
}
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
label {
|
||||
width: 120px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
<h3>User Agent</h3>
|
||||
<div class="item">
|
||||
<label>Main Window (PC)</label>
|
||||
<textarea id="ua_pc" type="text" autocapitalize="off" autocomplete="off" spellcheck="false" autofocus placeholder="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ..."></textarea>
|
||||
</div>
|
||||
<div class="item">
|
||||
<label>Tray Window (Phone)</label>
|
||||
<textarea id="ua_phone" type="text" autocapitalize="off" autocomplete="off" spellcheck="false" autofocus placeholder="Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS ..."></textarea>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button id="cancel">Cancel</button>
|
||||
<button id="confirm">Confirm</button>
|
||||
</div>`;
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use crate::utils::{chat_root, create_file, exists};
|
||||
use anyhow::Result;
|
||||
use serde_json::Value;
|
||||
use std::{collections::BTreeMap, fs, path::PathBuf, sync::Mutex};
|
||||
use tauri::Theme;
|
||||
use tauri::{Manager, Theme};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::TitleBarStyle;
|
||||
@@ -19,8 +19,8 @@ pub const DEFAULT_CHAT_CONF: &str = r#"{
|
||||
"titlebar": true,
|
||||
"default_origin": "https://chat.openai.com",
|
||||
"origin": "https://chat.openai.com",
|
||||
"ua_pc": "",
|
||||
"ua_phone": ""
|
||||
"ua_window": "",
|
||||
"ua_tray": ""
|
||||
}"#;
|
||||
pub const DEFAULT_CHAT_CONF_MAC: &str = r#"{
|
||||
"always_on_top": false,
|
||||
@@ -28,8 +28,8 @@ pub const DEFAULT_CHAT_CONF_MAC: &str = r#"{
|
||||
"titlebar": false,
|
||||
"default_origin": "https://chat.openai.com",
|
||||
"origin": "https://chat.openai.com",
|
||||
"ua_pc": "",
|
||||
"ua_phone": ""
|
||||
"ua_window": "",
|
||||
"ua_tray": ""
|
||||
}"#;
|
||||
|
||||
pub struct ChatState {
|
||||
@@ -51,8 +51,8 @@ pub struct ChatConfJson {
|
||||
pub theme: String,
|
||||
pub default_origin: String,
|
||||
pub origin: String,
|
||||
pub ua_pc: String,
|
||||
pub ua_phone: String,
|
||||
pub ua_window: String,
|
||||
pub ua_tray: String,
|
||||
}
|
||||
|
||||
impl ChatConfJson {
|
||||
@@ -60,15 +60,30 @@ impl ChatConfJson {
|
||||
/// path: ~/.chatgpt/chat.conf.json
|
||||
pub fn init() -> PathBuf {
|
||||
let conf_file = ChatConfJson::conf_path();
|
||||
let content = if cfg!(target_os = "macos") {
|
||||
DEFAULT_CHAT_CONF_MAC
|
||||
} else {
|
||||
DEFAULT_CHAT_CONF
|
||||
};
|
||||
|
||||
if !exists(&conf_file) {
|
||||
create_file(&conf_file).unwrap();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fs::write(&conf_file, DEFAULT_CHAT_CONF_MAC).unwrap();
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fs::write(&conf_file, DEFAULT_CHAT_CONF).unwrap();
|
||||
fs::write(&conf_file, content).unwrap();
|
||||
return conf_file;
|
||||
}
|
||||
|
||||
let conf_file = ChatConfJson::conf_path();
|
||||
let file_content = fs::read_to_string(&conf_file).unwrap();
|
||||
match serde_json::from_str(&file_content) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
if err.to_string() == "invalid type: map, expected unit at line 1 column 0" {
|
||||
return conf_file;
|
||||
}
|
||||
fs::write(&conf_file, content).unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
conf_file
|
||||
}
|
||||
|
||||
@@ -77,15 +92,31 @@ impl ChatConfJson {
|
||||
}
|
||||
|
||||
pub fn get_chat_conf() -> Self {
|
||||
let config_file = fs::read_to_string(ChatConfJson::conf_path())
|
||||
.unwrap_or_else(|_| DEFAULT_CHAT_CONF.to_string());
|
||||
let config: Value =
|
||||
serde_json::from_str(&config_file).expect("failed to parse chat.conf.json");
|
||||
serde_json::from_value(config).unwrap_or_else(|_| ChatConfJson::chat_conf_default())
|
||||
let conf_file = ChatConfJson::conf_path();
|
||||
let file_content = fs::read_to_string(&conf_file).unwrap();
|
||||
let content = if cfg!(target_os = "macos") {
|
||||
DEFAULT_CHAT_CONF_MAC
|
||||
} else {
|
||||
DEFAULT_CHAT_CONF
|
||||
};
|
||||
|
||||
match serde_json::from_value(match serde_json::from_str(&file_content) {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
fs::write(&conf_file, content).unwrap();
|
||||
serde_json::from_str(content).unwrap()
|
||||
}
|
||||
}) {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
fs::write(&conf_file, content).unwrap();
|
||||
serde_json::from_value(serde_json::from_str(content).unwrap()).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://users.rust-lang.org/t/updating-object-fields-given-dynamic-json/39049/3
|
||||
pub fn amend(new_rules: &Value) -> Result<()> {
|
||||
pub fn amend(new_rules: &Value, app: Option<tauri::AppHandle>) -> Result<()> {
|
||||
let config = ChatConfJson::get_chat_conf();
|
||||
let config: Value = serde_json::to_value(&config)?;
|
||||
let mut config: BTreeMap<String, Value> = serde_json::from_value(config)?;
|
||||
@@ -99,6 +130,20 @@ impl ChatConfJson {
|
||||
ChatConfJson::conf_path(),
|
||||
serde_json::to_string_pretty(&config)?,
|
||||
)?;
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -120,8 +165,4 @@ impl ChatConfJson {
|
||||
TitleBarStyle::Overlay
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_conf_default() -> Self {
|
||||
serde_json::from_value(serde_json::json!(DEFAULT_CHAT_CONF)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,10 +36,15 @@ fn main() {
|
||||
.on_window_event(|event| {
|
||||
// https://github.com/tauri-apps/tauri/discussions/2684
|
||||
if let tauri::WindowEvent::CloseRequested { api, .. } = event.event() {
|
||||
// TODO: https://github.com/tauri-apps/tauri/issues/3084
|
||||
// event.window().hide().unwrap();
|
||||
// https://github.com/tauri-apps/tao/pull/517
|
||||
event.window().minimize().unwrap();
|
||||
let win = event.window();
|
||||
if win.label() == "main" {
|
||||
win.hide().unwrap();
|
||||
} else {
|
||||
// TODO: https://github.com/tauri-apps/tauri/issues/3084
|
||||
// event.window().hide().unwrap();
|
||||
// https://github.com/tauri-apps/tao/pull/517
|
||||
event.window().minimize().unwrap();
|
||||
}
|
||||
api.prevent_close();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"build": {
|
||||
"beforeDevCommand": "",
|
||||
"beforeBuildCommand": "",
|
||||
"devPath": "../dist",
|
||||
"beforeDevCommand": "npm run dev:fe",
|
||||
"beforeBuildCommand": "npm run build:fe",
|
||||
"devPath": "http://localhost:1420",
|
||||
"distDir": "../dist"
|
||||
},
|
||||
"package": {
|
||||
"productName": "ChatGPT",
|
||||
"version": "0.2.1"
|
||||
"version": "0.3.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
@@ -63,6 +63,16 @@
|
||||
"https://lencx.github.io/ChatGPT/install.json"
|
||||
],
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEIxMjY4OUI5MTVFNjBEMDUKUldRRkRlWVZ1WWttc1NGWEE0RFNSb0RqdnhsekRJZTkwK2hVLzhBZTZnaHExSEZ1ZEdzWkpXTHkK"
|
||||
}
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"label": "main",
|
||||
"url": "index.html",
|
||||
"title": "ChatGPT",
|
||||
"visible": false,
|
||||
"width": 800,
|
||||
"height": 600
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
24
src/layout/index.scss
vendored
Normal file
24
src/layout/index.scss
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
.chat-logo {
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
|
||||
img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.ant-menu {
|
||||
.ant-menu-item {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-layout-footer {
|
||||
color: #666 !important;
|
||||
opacity: 0.7;
|
||||
}
|
||||
36
src/layout/index.tsx
vendored
Normal file
36
src/layout/index.tsx
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import { FC, useState } from 'react';
|
||||
import { Layout, Menu } from 'antd';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import Routes, { menuItems } from '@/routes';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const { Content, Footer, Sider } = Layout;
|
||||
|
||||
interface ChatLayoutProps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const ChatLayout: FC<ChatLayoutProps> = ({ children }) => {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const go = useNavigate();
|
||||
|
||||
return (
|
||||
<Layout style={{ minHeight: '100vh' }}>
|
||||
<Sider theme="light" collapsible collapsed={collapsed} onCollapse={(value) => setCollapsed(value)}>
|
||||
<div className="chat-logo"><img src="/logo.png" /></div>
|
||||
<Menu defaultSelectedKeys={['/']} mode="vertical" items={menuItems} onClick={(i) => go(i.key)} />
|
||||
</Sider>
|
||||
<Layout className="chat-layout">
|
||||
<Content className="chat-container">
|
||||
<Routes />
|
||||
</Content>
|
||||
<Footer style={{ textAlign: 'center' }}>
|
||||
<a href="https://github.com/lencx/chatgpt" target="_blank">ChatGPT Desktop Application</a> ©2022 Created by lencx</Footer>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatLayout;
|
||||
20
src/main.scss
vendored
Normal file
20
src/main.scss
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
:root {
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
color: #2a2a2a;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
html, body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
16
src/main.tsx
vendored
Normal file
16
src/main.tsx
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import { StrictMode, Suspense } from 'react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import Layout from '@/layout';
|
||||
import './main.scss';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<StrictMode>
|
||||
<Suspense fallback={null}>
|
||||
<BrowserRouter>
|
||||
<Layout/>
|
||||
</BrowserRouter>
|
||||
</Suspense>
|
||||
</StrictMode>
|
||||
);
|
||||
44
src/routes.tsx
vendored
Normal file
44
src/routes.tsx
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
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 General from '@view/General';
|
||||
import ChatGPTPrompts from '@view/ChatGPTPrompts';
|
||||
|
||||
export type ChatRouteObject = {
|
||||
label: string;
|
||||
icon?: React.ReactNode,
|
||||
};
|
||||
|
||||
export const routes: Array<RouteObject & { meta: ChatRouteObject }> = [
|
||||
{
|
||||
path: '/',
|
||||
element: <General />,
|
||||
meta: {
|
||||
label: 'General',
|
||||
icon: <DesktopOutlined />,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/chatgpt-prompts',
|
||||
element: <ChatGPTPrompts />,
|
||||
meta: {
|
||||
label: 'ChatGPT Prompts',
|
||||
icon: <BulbOutlined />,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
type MenuItem = Required<MenuProps>['items'][number];
|
||||
export const menuItems: MenuItem[] = routes.map(i => ({
|
||||
...i.meta,
|
||||
key: i.path || '',
|
||||
}));
|
||||
|
||||
export default () => {
|
||||
return useRoutes(routes);
|
||||
};
|
||||
7
src/view/ChatGPTPrompts.tsx
vendored
Normal file
7
src/view/ChatGPTPrompts.tsx
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<div>
|
||||
TODO: ChatGPT Prompts
|
||||
</div>
|
||||
)
|
||||
}
|
||||
102
src/view/General.tsx
vendored
Normal file
102
src/view/General.tsx
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Form, Radio, Switch, Input, Button, Space, message, Tooltip } from 'antd';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
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, omit, isEqual } from 'lodash';
|
||||
|
||||
const OriginLabel = ({ url }: { url: string }) => {
|
||||
return (
|
||||
<span>
|
||||
Switch Origin <Tooltip title={`Default: ${url}`}><QuestionCircleOutlined /></Tooltip>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
const disableAuto = {
|
||||
autoCapitalize: 'off',
|
||||
autoComplete: 'off',
|
||||
spellCheck: false
|
||||
}
|
||||
|
||||
export default function General() {
|
||||
const [form] = Form.useForm();
|
||||
const [platformInfo, setPlatform] = useState<string>('');
|
||||
const [chatConf, setChatConf] = useState<any>(null);
|
||||
|
||||
const init = async () => {
|
||||
setPlatform(await platform());
|
||||
const chatData = await invoke('get_chat_conf');
|
||||
setChatConf(chatData);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue(clone(chatConf));
|
||||
}, [chatConf])
|
||||
|
||||
const onCancel = () => {
|
||||
form.setFieldsValue(chatConf);
|
||||
};
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
if (!isEqual(omit(chatConf, ['default_origin']), values)) {
|
||||
await invoke('form_confirm', { data: values, label: 'main' });
|
||||
const isOk = await ask(`Configuration saved successfully, whether to restart?`, {
|
||||
title: 'ChatGPT Preferences'
|
||||
});
|
||||
if (isOk) {
|
||||
relaunch();
|
||||
return;
|
||||
}
|
||||
message.success('Configuration saved successfully');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
style={{ maxWidth: 500 }}
|
||||
onFinish={onFinish}
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 15, offset: 1 }}
|
||||
>
|
||||
<Form.Item label="Theme" name="theme">
|
||||
<Radio.Group>
|
||||
<Radio value="Light">Light</Radio>
|
||||
<Radio value="Dark">Dark</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="Always on Top" name="always_on_top" valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
{platformInfo === 'darwin' && (
|
||||
<Form.Item label="Titlebar" name="titlebar" valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label={<OriginLabel url={chatConf?.default_origin} />} name="origin">
|
||||
<Input placeholder="https://chat.openai.com" {...disableAuto} />
|
||||
</Form.Item>
|
||||
<Form.Item label="User Agent (Window)" name="ua_window">
|
||||
<Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} {...disableAuto} placeholder="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" />
|
||||
</Form.Item>
|
||||
<Form.Item label="User Agent (SystemTray)" name="ua_tray">
|
||||
<Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} {...disableAuto} placeholder="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space size={20}>
|
||||
<Button onClick={onCancel}>Cancel</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
28
tsconfig.json
Normal file
28
tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@view/*": ["src/view/*"],
|
||||
"@comps/*": ["src/components/*"],
|
||||
"@layout/*": ["src/layout/*"],
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
9
tsconfig.node.json
Normal file
9
tsconfig.node.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
28
vite.config.ts
Normal file
28
vite.config.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [tsconfigPaths(), react()],
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
// prevent vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
},
|
||||
// to make use of `TAURI_DEBUG` and other env variables
|
||||
// https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
|
||||
envPrefix: ["VITE_", "TAURI_"],
|
||||
build: {
|
||||
// Tauri supports es2021
|
||||
target: ["es2021", "chrome100", "safari13"],
|
||||
// don't minify for debug builds
|
||||
minify: !process.env.TAURI_DEBUG ? "esbuild" : false,
|
||||
// produce sourcemaps for debug builds
|
||||
sourcemap: !!process.env.TAURI_DEBUG,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user