mirror of
https://github.com/FranP-code/ChatGPT.git
synced 2025-10-13 00:13:25 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11877a960d | ||
|
|
a107e2b21e | ||
|
|
62924975a6 | ||
|
|
8aeca251e6 | ||
|
|
272ef1cd37 | ||
|
|
8eade8e9b9 | ||
|
|
171ac94f77 | ||
|
|
ffef57e934 | ||
|
|
7e499b6b1e | ||
|
|
a38804139a | ||
|
|
a939b3442e | ||
|
|
074afb58c8 | ||
|
|
f9f173407e | ||
|
|
99877209a1 | ||
|
|
f0ff062b21 | ||
|
|
ad722b09cf | ||
|
|
d78f23d7e2 | ||
|
|
0c68599d35 | ||
|
|
ca171b7c1b | ||
|
|
7aa70c83de | ||
|
|
585582244d | ||
|
|
1c3b3c1f22 | ||
|
|
db7a4f0b01 | ||
|
|
655dda8efb |
20
.github/workflows/release.yml
vendored
20
.github/workflows/release.yml
vendored
@@ -90,13 +90,13 @@ jobs:
|
||||
publish_dir: ./updater
|
||||
force_orphan: true
|
||||
|
||||
publish-winget:
|
||||
# Action can only be run on windows
|
||||
runs-on: windows-latest
|
||||
needs: [create-release, build-tauri]
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@v1
|
||||
with:
|
||||
identifier: lencx.ChatGPT
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
version: ${{ env.version }}
|
||||
# publish-winget:
|
||||
# # Action can only be run on windows
|
||||
# runs-on: windows-latest
|
||||
# needs: [create-release, build-tauri]
|
||||
# steps:
|
||||
# - uses: vedantmgoyal2009/winget-releaser@v1
|
||||
# with:
|
||||
# identifier: lencx.ChatGPT
|
||||
# token: ${{ secrets.WINGET_TOKEN }}
|
||||
# version: ${{ env.version }}
|
||||
|
||||
26
AWESOME.md
26
AWESOME.md
@@ -1,26 +0,0 @@
|
||||
# Awesome ChatGPT
|
||||
|
||||
- [Awesome ChatGPT Prompts](https://github.com/f/awesome-chatgpt-prompts) - This repo includes ChatGPT prompt curation to use ChatGPT better.
|
||||
- [Awesome ChatGPT](https://github.com/humanloop/awesome-chatgpt) - Curated list of awesome tools, demos, docs for ChatGPT and GPT-3
|
||||
|
||||
## Extension
|
||||
|
||||
`Browser`
|
||||
|
||||
- [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`
|
||||
|
||||
- [ChatGPT Extension for VSCode](https://github.com/mpociot/chatgpt-vscode) - A VSCode extension that allows you to use ChatGPT
|
||||
|
||||
`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.
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
### Windows
|
||||
|
||||
- [ChatGPT_0.10.0_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.10.0/ChatGPT_0.10.0_x64_en-US.msi):
|
||||
- [ChatGPT_0.10.2_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.10.2/ChatGPT_0.10.2_x64_en-US.msi):
|
||||
- 使用 [winget](https://winstall.app/apps/lencx.ChatGPT):
|
||||
|
||||
```bash
|
||||
@@ -32,15 +32,15 @@
|
||||
winget install --id=lencx.ChatGPT -e
|
||||
|
||||
# install the specified version
|
||||
winget install --id=lencx.ChatGPT -e --version 0.9.0
|
||||
winget install --id=lencx.ChatGPT -e --version 0.10.0
|
||||
```
|
||||
|
||||
**注意:如果安装路径和应用名称相同,会导致冲突 ([#142](https://github.com/lencx/ChatGPT/issues/142#issuecomment-0.10.0))**
|
||||
**注意:如果安装路径和应用名称相同,会导致冲突 ([#142](https://github.com/lencx/ChatGPT/issues/142#issuecomment-0.10.2))**
|
||||
|
||||
### Mac
|
||||
|
||||
- [ChatGPT_0.10.0_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.10.0/ChatGPT_0.10.0_x64.dmg)
|
||||
- [ChatGPT.app.tar.gz](https://github.com/lencx/ChatGPT/releases/download/v0.10.0/ChatGPT.app.tar.gz)
|
||||
- [ChatGPT_0.10.2_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.10.2/ChatGPT_0.10.2_x64.dmg)
|
||||
- [ChatGPT.app.tar.gz](https://github.com/lencx/ChatGPT/releases/download/v0.10.2/ChatGPT.app.tar.gz)
|
||||
- Homebrew \
|
||||
_[Homebrew 快捷安装](https://brew.sh) ([Cask](https://docs.brew.sh/Cask-Cookbook)):_
|
||||
```sh
|
||||
@@ -56,8 +56,8 @@
|
||||
|
||||
### Linux
|
||||
|
||||
- [chat-gpt_0.10.0_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.10.0/chat-gpt_0.10.0_amd64.deb)
|
||||
- [chat-gpt_0.10.0_amd64.AppImage](https://github.com/lencx/ChatGPT/releases/download/v0.10.0/chat-gpt_0.10.0_amd64.AppImage): **工作可靠,`.deb` 运行失败时可以尝试它**
|
||||
- [chat-gpt_0.10.2_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.10.2/chat-gpt_0.10.2_amd64.deb)
|
||||
- [chat-gpt_0.10.2_amd64.AppImage](https://github.com/lencx/ChatGPT/releases/download/v0.10.2/chat-gpt_0.10.2_amd64.AppImage): **工作可靠,`.deb` 运行失败时可以尝试它**
|
||||
- 使用 [AUR](https://aur.archlinux.org/packages/chatgpt-desktop-bin):
|
||||
```bash
|
||||
yay -S chatgpt-desktop-bin
|
||||
@@ -87,6 +87,7 @@
|
||||
|
||||
- 跨平台: `macOS` `Linux` `Windows`
|
||||
- 导出 ChatGPT 聊天记录 (支持 PNG, PDF 和生成分享链接)
|
||||
- 主窗口和系统托盘支持自定义 URL,将任意网站包装成一个桌面应用
|
||||
- 应用自动升级通知
|
||||
- 丰富的快捷键
|
||||
- 系统托盘悬浮窗
|
||||
@@ -129,6 +130,7 @@
|
||||
|
||||
- `[.chatgpt]` - 应用配置根路径
|
||||
- `chat.conf.json` - 应用喜好配置
|
||||
- `chat.awesome.json` - 自定义 URL 列表,类似于浏览器书签。可以将任意 URL 作为主窗口或托盘窗口 (**Control Conter -> Awesome**)
|
||||
- `chat.model.json` - ChatGPT 输入提示,通过斜杠命令来快速完成输入,主要包含三部分:
|
||||
- `user_custom` - 需要手动录入 (**Control Conter -> Language Model -> User Custom**)
|
||||
- `sync_prompts` - 从 [f/awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts) 同步数据 (**Control Conter -> Language Model -> Sync Prompts**)
|
||||
|
||||
17
README.md
17
README.md
@@ -7,6 +7,7 @@
|
||||
[](./README.md)
|
||||
[](./README-ZH_CN.md)\
|
||||

|
||||

|
||||
[](https://github.com/lencx/ChatGPT/releases)
|
||||
[](https://discord.gg/aPhCRf4zZr)
|
||||
[](https://twitter.com/lencx_)
|
||||
@@ -26,7 +27,7 @@
|
||||
|
||||
### Windows
|
||||
|
||||
- [ChatGPT_0.10.0_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.10.0/ChatGPT_0.10.0_x64_en-US.msi): Direct download installer
|
||||
- [ChatGPT_0.10.2_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.10.2/ChatGPT_0.10.2_x64_en-US.msi): Direct download installer
|
||||
- Use [winget](https://winstall.app/apps/lencx.ChatGPT):
|
||||
|
||||
```bash
|
||||
@@ -34,15 +35,15 @@
|
||||
winget install --id=lencx.ChatGPT -e
|
||||
|
||||
# install the specified version
|
||||
winget install --id=lencx.ChatGPT -e --version 0.9.0
|
||||
winget install --id=lencx.ChatGPT -e --version 0.10.0
|
||||
```
|
||||
|
||||
**Note: If the installation path and application name are the same, it will lead to conflict ([#142](https://github.com/lencx/ChatGPT/issues/142#issuecomment-0.10.0))**
|
||||
**Note: If the installation path and application name are the same, it will lead to conflict ([#142](https://github.com/lencx/ChatGPT/issues/142#issuecomment-0.10.2))**
|
||||
|
||||
### Mac
|
||||
|
||||
- [ChatGPT_0.10.0_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.10.0/ChatGPT_0.10.0_x64.dmg): Direct download installer
|
||||
- [ChatGPT.app.tar.gz](https://github.com/lencx/ChatGPT/releases/download/v0.10.0/ChatGPT.app.tar.gz): Download the `.app` installer
|
||||
- [ChatGPT_0.10.2_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.10.2/ChatGPT_0.10.2_x64.dmg): Direct download installer
|
||||
- [ChatGPT.app.tar.gz](https://github.com/lencx/ChatGPT/releases/download/v0.10.2/ChatGPT.app.tar.gz): Download the `.app` installer
|
||||
- Homebrew \
|
||||
Or you can install with _[Homebrew](https://brew.sh) ([Cask](https://docs.brew.sh/Cask-Cookbook)):_
|
||||
```sh
|
||||
@@ -58,8 +59,8 @@
|
||||
|
||||
### Linux
|
||||
|
||||
- [chat-gpt_0.10.0_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.10.0/chat-gpt_0.10.0_amd64.deb): Download `.deb` installer, advantage small size, disadvantage poor compatibility
|
||||
- [chat-gpt_0.10.0_amd64.AppImage](https://github.com/lencx/ChatGPT/releases/download/v0.10.0/chat-gpt_0.10.0_amd64.AppImage): Works reliably, you can try it if `.deb` fails to run
|
||||
- [chat-gpt_0.10.2_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.10.2/chat-gpt_0.10.2_amd64.deb): Download `.deb` installer, advantage small size, disadvantage poor compatibility
|
||||
- [chat-gpt_0.10.2_amd64.AppImage](https://github.com/lencx/ChatGPT/releases/download/v0.10.2/chat-gpt_0.10.2_amd64.AppImage): Works reliably, you can try it if `.deb` fails to run
|
||||
- Available on [AUR](https://aur.archlinux.org/packages/chatgpt-desktop-bin) with the package name `chatgpt-desktop-bin`, and you can use your favourite AUR package manager to install it.
|
||||
|
||||
<!-- download end -->
|
||||
@@ -90,6 +91,7 @@ You can look at **[awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt
|
||||
|
||||
- Multi-platform: `macOS` `Linux` `Windows`
|
||||
- Export ChatGPT history (PNG, PDF and Markdown)
|
||||
- The main window and system tray support custom URLs to wrap any website into a desktop application
|
||||
- Automatic application upgrade notification
|
||||
- Common shortcut keys
|
||||
- System tray hover window
|
||||
@@ -132,6 +134,7 @@ You can look at **[awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt
|
||||
|
||||
- `[.chatgpt]` - application configuration root folder
|
||||
- `chat.conf.json` - preferences configuration
|
||||
- `chat.awesome.json` - Custom URL lists, similar to browser bookmarks. Any URL can be used as the main window or tray window (**Control Conter -> Awesome**)
|
||||
- `chat.model.json` - prompts configuration,contains three parts:
|
||||
- `user_custom` - Requires manual data entry (**Control Conter -> Language Model -> User Custom**)
|
||||
- `sync_prompts` - Synchronizing data from [f/awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts) (**Control Conter -> Language Model -> Sync Prompts**)
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# UPDATE LOG
|
||||
|
||||
## v0.10.2
|
||||
|
||||
Fix:
|
||||
|
||||
- PNG and PDF buttons do not work (https://github.com/lencx/ChatGPT/issues/274)
|
||||
- Change the window size and the Send button is obscured by the Export button (https://github.com/lencx/ChatGPT/issues/286)
|
||||
- Change forward and backward shortcuts (https://github.com/lencx/ChatGPT/issues/254)
|
||||
- MacOS: `Cmd [`, `Cmd ]`
|
||||
- Windows and Linux: `Ctrl [`, `Ctrl ]`
|
||||
|
||||
Feat:
|
||||
|
||||
- Copy a single record to the clipboard (https://github.com/lencx/ChatGPT/issues/191)
|
||||
|
||||
## v0.10.1
|
||||
|
||||
Fix:
|
||||
|
||||
- Program exception when `Awesome` data is empty (https://github.com/lencx/ChatGPT/issues/248)
|
||||
|
||||
Feat:
|
||||
|
||||
- New shortcut key to change zoom level (30% - 200%), `+` or `-` 10% each time, `0` will be reset to 100% (https://github.com/lencx/ChatGPT/issues/202)
|
||||
- Windows: `Ctrl +`, `Ctrl -`, `Ctrl 0`
|
||||
- MacOS: `Cmd +`, `Cmd -`, `Cmd 0`
|
||||
|
||||
## v0.10.0
|
||||
|
||||
Fix:
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# ChatGPT Model
|
||||
|
||||
- [Awesome ChatGPT Prompts](https://github.com/f/awesome-chatgpt-prompts)
|
||||
@@ -1,10 +1,7 @@
|
||||
use crate::{
|
||||
conf::ChatConfJson,
|
||||
utils::{self, chat_root, create_file},
|
||||
};
|
||||
use log::info;
|
||||
use crate::utils;
|
||||
use log::error;
|
||||
use std::{fs, path::PathBuf};
|
||||
use tauri::{api, command, AppHandle, Manager, Theme};
|
||||
use tauri::{api, command, AppHandle, Manager};
|
||||
|
||||
#[command]
|
||||
pub fn drag_window(app: AppHandle) {
|
||||
@@ -22,19 +19,29 @@ pub fn fullscreen(app: AppHandle) {
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn download(_app: AppHandle, name: String, blob: Vec<u8>) {
|
||||
let path = chat_root().join(PathBuf::from(name));
|
||||
create_file(&path).unwrap();
|
||||
pub fn download(app: AppHandle, name: String, blob: Vec<u8>) {
|
||||
let win = app.app_handle().get_window("core");
|
||||
let path = utils::app_root().join(PathBuf::from(name));
|
||||
utils::create_file(&path).unwrap();
|
||||
fs::write(&path, blob).unwrap();
|
||||
utils::open_file(path);
|
||||
tauri::api::dialog::message(
|
||||
win.as_ref(),
|
||||
"Save File",
|
||||
format!("PATH: {}", path.display()),
|
||||
);
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn save_file(_app: AppHandle, name: String, content: String) {
|
||||
let path = chat_root().join(PathBuf::from(name));
|
||||
create_file(&path).unwrap();
|
||||
pub fn save_file(app: AppHandle, name: String, content: String) {
|
||||
let win = app.app_handle().get_window("core");
|
||||
let path = utils::app_root().join(PathBuf::from(name));
|
||||
utils::create_file(&path).unwrap();
|
||||
fs::write(&path, content).unwrap();
|
||||
utils::open_file(path);
|
||||
tauri::api::dialog::message(
|
||||
win.as_ref(),
|
||||
"Save File",
|
||||
format!("PATH: {}", path.display()),
|
||||
);
|
||||
}
|
||||
|
||||
#[command]
|
||||
@@ -42,52 +49,11 @@ pub fn open_link(app: AppHandle, url: String) {
|
||||
api::shell::open(&app.shell_scope(), url, None).unwrap();
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn get_chat_conf() -> ChatConfJson {
|
||||
ChatConfJson::get_chat_conf()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn reset_chat_conf() -> ChatConfJson {
|
||||
ChatConfJson::reset_chat_conf()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn get_theme() -> String {
|
||||
ChatConfJson::theme().unwrap_or(Theme::Light).to_string()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn run_check_update(app: AppHandle, silent: bool, has_msg: Option<bool>) {
|
||||
utils::run_check_update(app, silent, has_msg);
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn form_confirm(_app: AppHandle, data: serde_json::Value) {
|
||||
ChatConfJson::amend(&serde_json::json!(data), None).unwrap();
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn form_cancel(app: AppHandle, label: &str, title: &str, msg: &str) {
|
||||
let win = app.app_handle().get_window(label).unwrap();
|
||||
tauri::api::dialog::ask(
|
||||
app.app_handle().get_window(label).as_ref(),
|
||||
title,
|
||||
msg,
|
||||
move |is_cancel| {
|
||||
if is_cancel {
|
||||
win.close().unwrap();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn form_msg(app: AppHandle, label: &str, title: &str, msg: &str) {
|
||||
let win = app.app_handle().get_window(label);
|
||||
tauri::api::dialog::message(win.as_ref(), title, msg);
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn open_file(path: PathBuf) {
|
||||
utils::open_file(path);
|
||||
@@ -102,7 +68,7 @@ pub async fn get_data(app: AppHandle, url: String, is_msg: Option<bool>) -> Opti
|
||||
utils::get_data(&url, None).await
|
||||
};
|
||||
res.unwrap_or_else(|err| {
|
||||
info!("chatgpt_client_http_error: {}", err);
|
||||
error!("chatgpt_client_http: {}", err);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
app::{fs_extra, window},
|
||||
conf::GITHUB_PROMPTS_CSV_URL,
|
||||
utils::{self, chat_root},
|
||||
utils,
|
||||
};
|
||||
use log::info;
|
||||
use log::{error, info};
|
||||
use regex::Regex;
|
||||
use std::{collections::HashMap, fs, path::PathBuf, vec};
|
||||
use tauri::{api, command, AppHandle, Manager};
|
||||
@@ -11,7 +11,7 @@ use walkdir::WalkDir;
|
||||
|
||||
#[command]
|
||||
pub fn get_chat_model_cmd() -> serde_json::Value {
|
||||
let path = utils::chat_root().join("chat.model.cmd.json");
|
||||
let path = utils::app_root().join("chat.model.cmd.json");
|
||||
let content = fs::read_to_string(path).unwrap_or_else(|_| r#"{"data":[]}"#.to_string());
|
||||
serde_json::from_str(&content).unwrap()
|
||||
}
|
||||
@@ -29,7 +29,7 @@ pub fn parse_prompt(data: String) -> Vec<PromptRecord> {
|
||||
let mut list = vec![];
|
||||
for result in rdr.deserialize() {
|
||||
let record: PromptRecord = result.unwrap_or_else(|err| {
|
||||
info!("parse_prompt_error: {}", err);
|
||||
error!("parse_prompt: {}", err);
|
||||
PromptRecord {
|
||||
cmd: None,
|
||||
act: "".to_string(),
|
||||
@@ -55,7 +55,7 @@ pub struct ModelRecord {
|
||||
#[command]
|
||||
pub fn cmd_list() -> Vec<ModelRecord> {
|
||||
let mut list = vec![];
|
||||
for entry in WalkDir::new(utils::chat_root().join("cache_model"))
|
||||
for entry in WalkDir::new(utils::app_root().join("cache_model"))
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
{
|
||||
@@ -82,14 +82,14 @@ pub struct FileMetadata {
|
||||
#[tauri::command]
|
||||
pub fn get_download_list(pathname: &str) -> (Vec<serde_json::Value>, PathBuf) {
|
||||
info!("get_download_list: {}", pathname);
|
||||
let download_path = chat_root().join(PathBuf::from(pathname));
|
||||
let download_path = utils::app_root().join(PathBuf::from(pathname));
|
||||
let content = fs::read_to_string(&download_path).unwrap_or_else(|err| {
|
||||
info!("download_list_error: {}", err);
|
||||
error!("download_list: {}", err);
|
||||
fs::write(&download_path, "[]").unwrap();
|
||||
"[]".to_string()
|
||||
});
|
||||
let list = serde_json::from_str::<Vec<serde_json::Value>>(&content).unwrap_or_else(|err| {
|
||||
info!("download_list_parse_error: {}", err);
|
||||
error!("download_list_parse: {}", err);
|
||||
vec![]
|
||||
});
|
||||
|
||||
@@ -104,7 +104,7 @@ pub fn download_list(pathname: &str, dir: &str, filename: Option<String>, id: Op
|
||||
let mut idmap = HashMap::new();
|
||||
utils::vec_to_hashmap(data.0.into_iter(), "id", &mut idmap);
|
||||
|
||||
for entry in WalkDir::new(utils::chat_root().join(dir))
|
||||
for entry in WalkDir::new(utils::app_root().join(dir))
|
||||
.into_iter()
|
||||
.filter_entry(|e| !utils::is_hidden(e))
|
||||
.filter_map(|e| e.ok())
|
||||
@@ -182,9 +182,9 @@ pub async fn sync_prompts(app: AppHandle, time: u64) -> Option<Vec<ModelRecord>>
|
||||
|
||||
let data2 = data.clone();
|
||||
|
||||
let model = utils::chat_root().join("chat.model.json");
|
||||
let model_cmd = utils::chat_root().join("chat.model.cmd.json");
|
||||
let chatgpt_prompts = utils::chat_root()
|
||||
let model = utils::app_root().join("chat.model.json");
|
||||
let model_cmd = utils::app_root().join("chat.model.cmd.json");
|
||||
let chatgpt_prompts = utils::app_root()
|
||||
.join("cache_model")
|
||||
.join("chatgpt_prompts.json");
|
||||
|
||||
@@ -238,8 +238,8 @@ pub async fn sync_prompts(app: AppHandle, time: u64) -> Option<Vec<ModelRecord>>
|
||||
"Sync Prompts",
|
||||
"ChatGPT Prompts data has been synchronized!",
|
||||
);
|
||||
window::window_reload(app.clone(), "core");
|
||||
window::window_reload(app, "tray");
|
||||
window::cmd::window_reload(app.clone(), "core");
|
||||
window::cmd::window_reload(app, "tray");
|
||||
|
||||
return Some(data2);
|
||||
}
|
||||
@@ -249,13 +249,12 @@ pub async fn sync_prompts(app: AppHandle, time: u64) -> Option<Vec<ModelRecord>>
|
||||
|
||||
#[command]
|
||||
pub async fn sync_user_prompts(url: String, data_type: String) -> Option<Vec<ModelRecord>> {
|
||||
info!("sync_user_prompts: url => {}", url);
|
||||
let res = utils::get_data(&url, None).await.unwrap_or_else(|err| {
|
||||
info!("chatgpt_http_error: {}", err);
|
||||
error!("chatgpt_http: {}", err);
|
||||
None
|
||||
});
|
||||
|
||||
info!("chatgpt_http_url: {}", url);
|
||||
|
||||
if let Some(v) = res {
|
||||
let data;
|
||||
if data_type == "csv" {
|
||||
@@ -264,11 +263,11 @@ pub async fn sync_user_prompts(url: String, data_type: String) -> Option<Vec<Mod
|
||||
} else if data_type == "json" {
|
||||
info!("chatgpt_http_json_parse");
|
||||
data = serde_json::from_str(&v).unwrap_or_else(|err| {
|
||||
info!("chatgpt_http_json_parse_error: {}", err);
|
||||
error!("chatgpt_http_json_parse: {}", err);
|
||||
vec![]
|
||||
});
|
||||
} else {
|
||||
info!("chatgpt_http_unknown_type");
|
||||
error!("chatgpt_http_unknown_type");
|
||||
data = vec![];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
app::window,
|
||||
conf::{self, ChatConfJson},
|
||||
conf::{self, AppConf},
|
||||
utils,
|
||||
};
|
||||
use tauri::{
|
||||
@@ -14,7 +14,7 @@ use tauri::AboutMetadata;
|
||||
|
||||
// --- Menu
|
||||
pub fn init() -> Menu {
|
||||
let chat_conf = ChatConfJson::get_chat_conf();
|
||||
let app_conf = AppConf::read();
|
||||
let name = "ChatGPT";
|
||||
let app_menu = Submenu::new(
|
||||
name,
|
||||
@@ -35,7 +35,7 @@ pub fn init() -> Menu {
|
||||
|
||||
let stay_on_top =
|
||||
CustomMenuItem::new("stay_on_top".to_string(), "Stay On Top").accelerator("CmdOrCtrl+T");
|
||||
let stay_on_top_menu = if chat_conf.stay_on_top {
|
||||
let stay_on_top_menu = if app_conf.stay_on_top {
|
||||
stay_on_top.selected()
|
||||
} else {
|
||||
stay_on_top
|
||||
@@ -44,15 +44,15 @@ pub fn init() -> Menu {
|
||||
let theme_light = CustomMenuItem::new("theme_light".to_string(), "Light");
|
||||
let theme_dark = CustomMenuItem::new("theme_dark".to_string(), "Dark");
|
||||
let theme_system = CustomMenuItem::new("theme_system".to_string(), "System");
|
||||
let is_dark = chat_conf.theme == "Dark";
|
||||
let is_system = chat_conf.theme == "System";
|
||||
let is_dark = app_conf.clone().theme_check("dark");
|
||||
let is_system = app_conf.clone().theme_check("system");
|
||||
|
||||
let update_prompt = CustomMenuItem::new("update_prompt".to_string(), "Prompt");
|
||||
let update_silent = CustomMenuItem::new("update_silent".to_string(), "Silent");
|
||||
let _update_disable = CustomMenuItem::new("update_disable".to_string(), "Disable");
|
||||
// let _update_disable = CustomMenuItem::new("update_disable".to_string(), "Disable");
|
||||
|
||||
let popup_search = CustomMenuItem::new("popup_search".to_string(), "Pop-up Search");
|
||||
let popup_search_menu = if chat_conf.popup_search {
|
||||
let popup_search_menu = if app_conf.popup_search {
|
||||
popup_search.selected()
|
||||
} else {
|
||||
popup_search
|
||||
@@ -61,19 +61,20 @@ pub fn init() -> Menu {
|
||||
#[cfg(target_os = "macos")]
|
||||
let titlebar = CustomMenuItem::new("titlebar".to_string(), "Titlebar").accelerator("CmdOrCtrl+B");
|
||||
#[cfg(target_os = "macos")]
|
||||
let titlebar_menu = if chat_conf.titlebar {
|
||||
let titlebar_menu = if app_conf.titlebar {
|
||||
titlebar.selected()
|
||||
} else {
|
||||
titlebar
|
||||
};
|
||||
|
||||
let system_tray = CustomMenuItem::new("system_tray".to_string(), "System Tray");
|
||||
let system_tray_menu = if chat_conf.tray {
|
||||
let system_tray_menu = if app_conf.tray {
|
||||
system_tray.selected()
|
||||
} else {
|
||||
system_tray
|
||||
};
|
||||
|
||||
let auto_update = app_conf.get_auto_update();
|
||||
let preferences_menu = Submenu::new(
|
||||
"Preferences",
|
||||
Menu::with_items([
|
||||
@@ -114,16 +115,16 @@ pub fn init() -> Menu {
|
||||
Submenu::new(
|
||||
"Auto Update",
|
||||
Menu::new()
|
||||
.add_item(if chat_conf.auto_update == "Prompt" {
|
||||
.add_item(if auto_update == "prompt" {
|
||||
update_prompt.selected()
|
||||
} else {
|
||||
update_prompt
|
||||
})
|
||||
.add_item(if chat_conf.auto_update == "Silent" {
|
||||
.add_item(if auto_update == "silent" {
|
||||
update_silent.selected()
|
||||
} else {
|
||||
update_silent
|
||||
}), // .add_item(if chat_conf.auto_update == "Disable" {
|
||||
}), // .add_item(if auto_update == "disable" {
|
||||
// update_disable.selected()
|
||||
// } else {
|
||||
// update_disable
|
||||
@@ -142,7 +143,6 @@ pub fn init() -> Menu {
|
||||
.into(),
|
||||
CustomMenuItem::new("clear_conf".to_string(), "Clear Config").into(),
|
||||
MenuItem::Separator.into(),
|
||||
CustomMenuItem::new("awesome".to_string(), "Awesome ChatGPT").into(),
|
||||
CustomMenuItem::new("buy_coffee".to_string(), "Buy lencx a coffee").into(),
|
||||
]),
|
||||
);
|
||||
@@ -162,9 +162,9 @@ pub fn init() -> Menu {
|
||||
let view_menu = Submenu::new(
|
||||
"View",
|
||||
Menu::new()
|
||||
.add_item(CustomMenuItem::new("go_back".to_string(), "Go Back").accelerator("CmdOrCtrl+Left"))
|
||||
.add_item(CustomMenuItem::new("go_back".to_string(), "Go Back").accelerator("CmdOrCtrl+["))
|
||||
.add_item(
|
||||
CustomMenuItem::new("go_forward".to_string(), "Go Forward").accelerator("CmdOrCtrl+Right"),
|
||||
CustomMenuItem::new("go_forward".to_string(), "Go Forward").accelerator("CmdOrCtrl+]"),
|
||||
)
|
||||
.add_item(
|
||||
CustomMenuItem::new("scroll_top".to_string(), "Scroll to Top of Screen")
|
||||
@@ -175,6 +175,12 @@ pub fn init() -> Menu {
|
||||
.accelerator("CmdOrCtrl+Down"),
|
||||
)
|
||||
.add_native_item(MenuItem::Separator)
|
||||
.add_item(
|
||||
CustomMenuItem::new("zoom_0".to_string(), "Zoom to Actual Size").accelerator("CmdOrCtrl+0"),
|
||||
)
|
||||
.add_item(CustomMenuItem::new("zoom_out".to_string(), "Zoom Out").accelerator("CmdOrCtrl+-"))
|
||||
.add_item(CustomMenuItem::new("zoom_in".to_string(), "Zoom In").accelerator("CmdOrCtrl+Plus"))
|
||||
.add_native_item(MenuItem::Separator)
|
||||
.add_item(
|
||||
CustomMenuItem::new("reload".to_string(), "Refresh the Screen").accelerator("CmdOrCtrl+R"),
|
||||
),
|
||||
@@ -183,6 +189,10 @@ pub fn init() -> Menu {
|
||||
let window_menu = Submenu::new(
|
||||
"Window",
|
||||
Menu::new()
|
||||
.add_item(CustomMenuItem::new(
|
||||
"app_website".to_string(),
|
||||
"ChatGPT User's Guide",
|
||||
))
|
||||
.add_item(CustomMenuItem::new("dalle2".to_string(), "DALL·E 2"))
|
||||
.add_native_item(MenuItem::Separator)
|
||||
.add_native_item(MenuItem::Minimize)
|
||||
@@ -235,23 +245,31 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
utils::run_check_update(app, false, None);
|
||||
}
|
||||
// Preferences
|
||||
"control_center" => window::control_window(app),
|
||||
"control_center" => window::cmd::control_window(app),
|
||||
"restart" => tauri::api::process::restart(&app.env()),
|
||||
"inject_script" => open(&app, script_path),
|
||||
"go_conf" => utils::open_file(utils::chat_root()),
|
||||
"go_conf" => utils::open_file(utils::app_root()),
|
||||
"clear_conf" => utils::clear_conf(&app),
|
||||
"awesome" => open(&app, conf::AWESOME_URL.to_string()),
|
||||
"app_website" => window::cmd::wa_window(
|
||||
app,
|
||||
"app_website".into(),
|
||||
"ChatGPT User's Guide".into(),
|
||||
conf::APP_WEBSITE.into(),
|
||||
None,
|
||||
),
|
||||
"buy_coffee" => open(&app, conf::BUY_COFFEE.to_string()),
|
||||
"popup_search" => {
|
||||
let chat_conf = conf::ChatConfJson::get_chat_conf();
|
||||
let popup_search = !chat_conf.popup_search;
|
||||
let app_conf = AppConf::read();
|
||||
let popup_search = !app_conf.popup_search;
|
||||
menu_handle
|
||||
.get_item(menu_id)
|
||||
.set_selected(popup_search)
|
||||
.unwrap();
|
||||
ChatConfJson::amend(&serde_json::json!({ "popup_search": popup_search }), None).unwrap();
|
||||
window::window_reload(app.clone(), "core");
|
||||
window::window_reload(app, "tray");
|
||||
app_conf
|
||||
.amend(serde_json::json!({ "popup_search": popup_search }))
|
||||
.write();
|
||||
window::cmd::window_reload(app.clone(), "core");
|
||||
window::cmd::window_reload(app, "tray");
|
||||
}
|
||||
"sync_prompts" => {
|
||||
tauri::api::dialog::ask(
|
||||
@@ -270,29 +288,37 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
);
|
||||
}
|
||||
"hide_dock_icon" => {
|
||||
ChatConfJson::amend(&serde_json::json!({ "hide_dock_icon": true }), Some(app)).unwrap()
|
||||
AppConf::read()
|
||||
.amend(serde_json::json!({ "hide_dock_icon": true }))
|
||||
.write()
|
||||
.restart(app);
|
||||
}
|
||||
"titlebar" => {
|
||||
let chat_conf = conf::ChatConfJson::get_chat_conf();
|
||||
ChatConfJson::amend(
|
||||
&serde_json::json!({ "titlebar": !chat_conf.titlebar }),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
tauri::api::process::restart(&app.env());
|
||||
let app_conf = AppConf::read();
|
||||
app_conf
|
||||
.clone()
|
||||
.amend(serde_json::json!({ "titlebar": !app_conf.titlebar }))
|
||||
.write()
|
||||
.restart(app);
|
||||
}
|
||||
"system_tray" => {
|
||||
let chat_conf = conf::ChatConfJson::get_chat_conf();
|
||||
ChatConfJson::amend(&serde_json::json!({ "tray": !chat_conf.tray }), None).unwrap();
|
||||
tauri::api::process::restart(&app.env());
|
||||
let app_conf = AppConf::read();
|
||||
app_conf
|
||||
.clone()
|
||||
.amend(serde_json::json!({ "tray": !app_conf.tray }))
|
||||
.write()
|
||||
.restart(app);
|
||||
}
|
||||
"theme_light" | "theme_dark" | "theme_system" => {
|
||||
let theme = match menu_id {
|
||||
"theme_dark" => "Dark",
|
||||
"theme_system" => "System",
|
||||
_ => "Light",
|
||||
"theme_dark" => "dark",
|
||||
"theme_system" => "system",
|
||||
_ => "light",
|
||||
};
|
||||
ChatConfJson::amend(&serde_json::json!({ "theme": theme }), Some(app)).unwrap();
|
||||
AppConf::read()
|
||||
.amend(serde_json::json!({ "theme": theme }))
|
||||
.write()
|
||||
.restart(app);
|
||||
}
|
||||
"update_prompt" | "update_silent" | "update_disable" => {
|
||||
// for id in ["update_prompt", "update_silent", "update_disable"] {
|
||||
@@ -305,38 +331,45 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
.get_item("update_silent")
|
||||
.set_selected(true)
|
||||
.unwrap();
|
||||
"Silent"
|
||||
"silent"
|
||||
}
|
||||
"update_disable" => {
|
||||
menu_handle
|
||||
.get_item("update_disable")
|
||||
.set_selected(true)
|
||||
.unwrap();
|
||||
"Disable"
|
||||
"disable"
|
||||
}
|
||||
_ => {
|
||||
menu_handle
|
||||
.get_item("update_prompt")
|
||||
.set_selected(true)
|
||||
.unwrap();
|
||||
"Prompt"
|
||||
"prompt"
|
||||
}
|
||||
};
|
||||
ChatConfJson::amend(&serde_json::json!({ "auto_update": auto_update }), None).unwrap();
|
||||
AppConf::read()
|
||||
.amend(serde_json::json!({ "auto_update": auto_update }))
|
||||
.write();
|
||||
}
|
||||
"stay_on_top" => {
|
||||
let chat_conf = conf::ChatConfJson::get_chat_conf();
|
||||
let stay_on_top = !chat_conf.stay_on_top;
|
||||
let app_conf = AppConf::read();
|
||||
let stay_on_top = !app_conf.stay_on_top;
|
||||
menu_handle
|
||||
.get_item(menu_id)
|
||||
.set_selected(stay_on_top)
|
||||
.unwrap();
|
||||
win.set_always_on_top(stay_on_top).unwrap();
|
||||
ChatConfJson::amend(&serde_json::json!({ "stay_on_top": stay_on_top }), None).unwrap();
|
||||
app_conf
|
||||
.amend(serde_json::json!({ "stay_on_top": stay_on_top }))
|
||||
.write();
|
||||
}
|
||||
// Window
|
||||
"dalle2" => window::dalle2_window(&app, None, None, Some(false)),
|
||||
// View
|
||||
"zoom_0" => win.eval("window.__zoom0 && window.__zoom0()").unwrap(),
|
||||
"zoom_out" => win.eval("window.__zoomOut && window.__zoomOut()").unwrap(),
|
||||
"zoom_in" => win.eval("window.__zoomIn && window.__zoomIn()").unwrap(),
|
||||
"reload" => win.eval("window.location.reload()").unwrap(),
|
||||
"go_back" => win.eval("window.history.go(-1)").unwrap(),
|
||||
"go_forward" => win.eval("window.history.go(1)").unwrap(),
|
||||
@@ -358,7 +391,7 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
)
|
||||
.unwrap(),
|
||||
// Help
|
||||
"chatgpt_log" => utils::open_file(utils::chat_root().join("chatgpt.log")),
|
||||
"chatgpt_log" => utils::open_file(utils::app_root().join("chatgpt.log")),
|
||||
"update_log" => open(&app, conf::UPDATE_LOG_URL.to_string()),
|
||||
"report_bug" => open(&app, conf::ISSUES_URL.to_string()),
|
||||
"dev_tools" => {
|
||||
@@ -372,22 +405,29 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
// --- SystemTray Menu
|
||||
pub fn tray_menu() -> SystemTray {
|
||||
if cfg!(target_os = "macos") {
|
||||
SystemTray::new().with_menu(
|
||||
SystemTrayMenu::new()
|
||||
.add_item(CustomMenuItem::new(
|
||||
"control_center".to_string(),
|
||||
"Control Center",
|
||||
))
|
||||
.add_native_item(SystemTrayMenuItem::Separator)
|
||||
.add_item(CustomMenuItem::new(
|
||||
"show_dock_icon".to_string(),
|
||||
"Show Dock Icon",
|
||||
))
|
||||
let mut tray_menu = SystemTrayMenu::new()
|
||||
.add_item(CustomMenuItem::new(
|
||||
"control_center".to_string(),
|
||||
"Control Center",
|
||||
))
|
||||
.add_native_item(SystemTrayMenuItem::Separator);
|
||||
|
||||
if AppConf::read().hide_dock_icon {
|
||||
tray_menu = tray_menu.add_item(CustomMenuItem::new(
|
||||
"show_dock_icon".to_string(),
|
||||
"Show Dock Icon",
|
||||
));
|
||||
} else {
|
||||
tray_menu = tray_menu
|
||||
.add_item(CustomMenuItem::new(
|
||||
"hide_dock_icon".to_string(),
|
||||
"Hide Dock Icon",
|
||||
))
|
||||
.add_item(CustomMenuItem::new("show_core".to_string(), "Show ChatGPT"))
|
||||
.add_item(CustomMenuItem::new("show_core".to_string(), "Show ChatGPT"));
|
||||
}
|
||||
|
||||
SystemTray::new().with_menu(
|
||||
tray_menu
|
||||
.add_native_item(SystemTrayMenuItem::Separator)
|
||||
.add_item(CustomMenuItem::new("quit".to_string(), "Quit ChatGPT")),
|
||||
)
|
||||
@@ -413,42 +453,51 @@ pub fn tray_handler(handle: &AppHandle, event: SystemTrayEvent) {
|
||||
|
||||
match event {
|
||||
SystemTrayEvent::LeftClick { .. } => {
|
||||
let chat_conf = conf::ChatConfJson::get_chat_conf();
|
||||
let app_conf = AppConf::read();
|
||||
|
||||
if !chat_conf.hide_dock_icon {
|
||||
let core_win = handle.get_window("core").unwrap();
|
||||
core_win.minimize().unwrap();
|
||||
if !app_conf.hide_dock_icon {
|
||||
if let Some(core_win) = handle.get_window("core") {
|
||||
core_win.minimize().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let tray_win = handle.get_window("tray").unwrap();
|
||||
tray_win.move_window(Position::TrayCenter).unwrap();
|
||||
if let Some(tray_win) = handle.get_window("tray") {
|
||||
tray_win.move_window(Position::TrayCenter).unwrap();
|
||||
|
||||
if tray_win.is_visible().unwrap() {
|
||||
tray_win.hide().unwrap();
|
||||
} else {
|
||||
tray_win.show().unwrap();
|
||||
if tray_win.is_visible().unwrap() {
|
||||
tray_win.hide().unwrap();
|
||||
} else {
|
||||
tray_win.show().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
|
||||
"control_center" => window::control_window(app),
|
||||
"control_center" => window::cmd::control_window(app),
|
||||
"restart" => tauri::api::process::restart(&handle.env()),
|
||||
"show_dock_icon" => {
|
||||
ChatConfJson::amend(&serde_json::json!({ "hide_dock_icon": false }), Some(app)).unwrap();
|
||||
AppConf::read()
|
||||
.amend(serde_json::json!({ "hide_dock_icon": false }))
|
||||
.write()
|
||||
.restart(app);
|
||||
}
|
||||
"hide_dock_icon" => {
|
||||
let chat_conf = conf::ChatConfJson::get_chat_conf();
|
||||
if !chat_conf.hide_dock_icon {
|
||||
ChatConfJson::amend(&serde_json::json!({ "hide_dock_icon": true }), Some(app)).unwrap();
|
||||
let app_conf = AppConf::read();
|
||||
if !app_conf.hide_dock_icon {
|
||||
app_conf
|
||||
.amend(serde_json::json!({ "hide_dock_icon": true }))
|
||||
.write()
|
||||
.restart(app);
|
||||
}
|
||||
}
|
||||
"show_core" => {
|
||||
let core_win = app.get_window("core").unwrap();
|
||||
let tray_win = app.get_window("tray").unwrap();
|
||||
if !core_win.is_visible().unwrap() {
|
||||
core_win.show().unwrap();
|
||||
core_win.set_focus().unwrap();
|
||||
tray_win.hide().unwrap();
|
||||
}
|
||||
if let Some(core_win) = app.get_window("core") {
|
||||
let tray_win = app.get_window("tray").unwrap();
|
||||
if !core_win.is_visible().unwrap() {
|
||||
core_win.show().unwrap();
|
||||
core_win.set_focus().unwrap();
|
||||
tray_win.hide().unwrap();
|
||||
}
|
||||
};
|
||||
}
|
||||
"quit" => std::process::exit(0),
|
||||
_ => (),
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
use crate::{app::window, conf::ChatConfJson, utils};
|
||||
use log::info;
|
||||
use crate::{app::window, conf::AppConf, utils};
|
||||
use log::{error, info};
|
||||
use tauri::{utils::config::WindowUrl, window::WindowBuilder, App, GlobalShortcutManager, Manager};
|
||||
use wry::application::accelerator::Accelerator;
|
||||
|
||||
pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
info!("stepup");
|
||||
let chat_conf = ChatConfJson::get_chat_conf();
|
||||
let url = chat_conf.main_origin.to_string();
|
||||
let theme = ChatConfJson::theme();
|
||||
let app_conf = AppConf::read();
|
||||
let url = app_conf.main_origin.to_string();
|
||||
let theme = AppConf::theme_mode();
|
||||
let handle = app.app_handle();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
info!("stepup_tray");
|
||||
window::tray_window(&handle);
|
||||
});
|
||||
|
||||
if let Some(v) = chat_conf.global_shortcut {
|
||||
if let Some(v) = app_conf.clone().global_shortcut {
|
||||
info!("global_shortcut: `{}`", v);
|
||||
match v.parse::<Accelerator>() {
|
||||
Ok(_) => {
|
||||
@@ -33,47 +34,49 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|err| {
|
||||
info!("global_shortcut_register_error: {}", err);
|
||||
error!("global_shortcut_register_error: {}", err);
|
||||
});
|
||||
}
|
||||
Err(err) => {
|
||||
info!("global_shortcut_parse_error: {}", err);
|
||||
error!("global_shortcut_parse_error: {}", err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!("global_shortcut_unregister");
|
||||
};
|
||||
|
||||
if chat_conf.hide_dock_icon {
|
||||
let app_conf2 = app_conf.clone();
|
||||
if app_conf.hide_dock_icon {
|
||||
#[cfg(target_os = "macos")]
|
||||
app.set_activation_policy(tauri::ActivationPolicy::Accessory);
|
||||
} else {
|
||||
let app = app.handle();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let link = if chat_conf.main_dashboard {
|
||||
let link = if app_conf2.main_dashboard {
|
||||
"index.html"
|
||||
} else {
|
||||
&url
|
||||
};
|
||||
info!("main_window: {}", link);
|
||||
let mut main_win = WindowBuilder::new(&app, "core", WindowUrl::App(link.into()))
|
||||
.title("ChatGPT")
|
||||
.resizable(true)
|
||||
.fullscreen(false)
|
||||
.inner_size(800.0, 600.0)
|
||||
.theme(theme)
|
||||
.always_on_top(chat_conf.stay_on_top)
|
||||
.theme(Some(theme))
|
||||
.always_on_top(app_conf2.stay_on_top)
|
||||
.initialization_script(&utils::user_script())
|
||||
.initialization_script(include_str!("../scripts/core.js"))
|
||||
.user_agent(&chat_conf.ua_window);
|
||||
.user_agent(&app_conf2.ua_window);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
main_win = main_win
|
||||
.title_bar_style(ChatConfJson::titlebar())
|
||||
.title_bar_style(app_conf2.clone().titlebar())
|
||||
.hidden_title(true);
|
||||
}
|
||||
|
||||
if url == "https://chat.openai.com" && !chat_conf.main_dashboard {
|
||||
if url == "https://chat.openai.com" && !app_conf2.main_dashboard {
|
||||
main_win = main_win
|
||||
.initialization_script(include_str!("../vendors/floating-ui-core.js"))
|
||||
.initialization_script(include_str!("../vendors/floating-ui-dom.js"))
|
||||
@@ -92,10 +95,11 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
|
||||
}
|
||||
|
||||
// auto_update
|
||||
if chat_conf.auto_update != "Disable" {
|
||||
info!("stepup::run_check_update");
|
||||
let auto_update = app_conf.get_auto_update();
|
||||
if auto_update != "disable" {
|
||||
info!("run_check_update");
|
||||
let app = app.handle();
|
||||
utils::run_check_update(app, chat_conf.auto_update == "Silent", None);
|
||||
utils::run_check_update(app, auto_update == "silent", None);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use crate::{conf, utils};
|
||||
use crate::{conf::AppConf, utils};
|
||||
use log::info;
|
||||
use std::time::SystemTime;
|
||||
use tauri::{utils::config::WindowUrl, window::WindowBuilder, Manager};
|
||||
|
||||
pub fn tray_window(handle: &tauri::AppHandle) {
|
||||
let chat_conf = conf::ChatConfJson::get_chat_conf();
|
||||
let theme = conf::ChatConfJson::theme();
|
||||
let app_conf = AppConf::read();
|
||||
let theme = AppConf::theme_mode();
|
||||
let app = handle.clone();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let link = if chat_conf.tray_dashboard {
|
||||
let link = if app_conf.tray_dashboard {
|
||||
"index.html"
|
||||
} else {
|
||||
&chat_conf.tray_origin
|
||||
&app_conf.tray_origin
|
||||
};
|
||||
let mut tray_win = WindowBuilder::new(&app, "tray", WindowUrl::App(link.into()))
|
||||
.title("ChatGPT")
|
||||
@@ -21,12 +21,12 @@ pub fn tray_window(handle: &tauri::AppHandle) {
|
||||
.inner_size(360.0, 540.0)
|
||||
.decorations(false)
|
||||
.always_on_top(true)
|
||||
.theme(theme)
|
||||
.theme(Some(theme))
|
||||
.initialization_script(&utils::user_script())
|
||||
.initialization_script(include_str!("../scripts/core.js"))
|
||||
.user_agent(&chat_conf.ua_tray);
|
||||
.user_agent(&app_conf.ua_tray);
|
||||
|
||||
if chat_conf.tray_origin == "https://chat.openai.com" && !chat_conf.tray_dashboard {
|
||||
if app_conf.tray_origin == "https://chat.openai.com" && !app_conf.tray_dashboard {
|
||||
tray_win = tray_win
|
||||
.initialization_script(include_str!("../vendors/floating-ui-core.js"))
|
||||
.initialization_script(include_str!("../vendors/floating-ui-dom.js"))
|
||||
@@ -45,7 +45,7 @@ pub fn dalle2_window(
|
||||
is_new: Option<bool>,
|
||||
) {
|
||||
info!("dalle2_query: {:?}", query);
|
||||
let theme = conf::ChatConfJson::theme();
|
||||
let theme = AppConf::theme_mode();
|
||||
let app = handle.clone();
|
||||
|
||||
let query = if query.is_some() {
|
||||
@@ -76,7 +76,7 @@ pub fn dalle2_window(
|
||||
.fullscreen(false)
|
||||
.inner_size(800.0, 600.0)
|
||||
.always_on_top(false)
|
||||
.theme(theme)
|
||||
.theme(Some(theme))
|
||||
.initialization_script(include_str!("../scripts/core.js"))
|
||||
.initialization_script(&query)
|
||||
.initialization_script(include_str!("../scripts/dalle2.js"))
|
||||
@@ -90,78 +90,83 @@ pub fn dalle2_window(
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn dalle2_search_window(app: tauri::AppHandle, query: String) {
|
||||
dalle2_window(
|
||||
&app.app_handle(),
|
||||
Some(query),
|
||||
Some("ChatGPT & DALL·E 2".to_string()),
|
||||
None,
|
||||
);
|
||||
}
|
||||
pub mod cmd {
|
||||
use super::*;
|
||||
use log::info;
|
||||
use tauri::{command, utils::config::WindowUrl, window::WindowBuilder, Manager};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn control_window(handle: tauri::AppHandle) {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
if handle.get_window("main").is_none() {
|
||||
WindowBuilder::new(
|
||||
&handle,
|
||||
"main",
|
||||
WindowUrl::App("index.html?type=control".into()),
|
||||
)
|
||||
.title("Control Center")
|
||||
.resizable(true)
|
||||
.fullscreen(false)
|
||||
.inner_size(1200.0, 700.0)
|
||||
.min_inner_size(1000.0, 600.0)
|
||||
.build()
|
||||
.unwrap();
|
||||
} else {
|
||||
let main_win = handle.get_window("main").unwrap();
|
||||
main_win.show().unwrap();
|
||||
main_win.set_focus().unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
#[tauri::command]
|
||||
pub fn dalle2_search_window(app: tauri::AppHandle, query: String) {
|
||||
dalle2_window(
|
||||
&app.app_handle(),
|
||||
Some(query),
|
||||
Some("ChatGPT & DALL·E 2".to_string()),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn wa_window(
|
||||
app: tauri::AppHandle,
|
||||
label: String,
|
||||
title: String,
|
||||
url: String,
|
||||
script: Option<String>,
|
||||
) {
|
||||
info!("wa_window: {} :=> {}", title, url);
|
||||
let win = app.get_window(&label);
|
||||
if win.is_none() {
|
||||
#[tauri::command]
|
||||
pub fn control_window(handle: tauri::AppHandle) {
|
||||
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)
|
||||
if handle.get_window("main").is_none() {
|
||||
WindowBuilder::new(
|
||||
&handle,
|
||||
"main",
|
||||
WindowUrl::App("index.html?type=control".into()),
|
||||
)
|
||||
.title("Control Center")
|
||||
.resizable(true)
|
||||
.fullscreen(false)
|
||||
.inner_size(1200.0, 700.0)
|
||||
.min_inner_size(1000.0, 600.0)
|
||||
.build()
|
||||
.unwrap();
|
||||
} else {
|
||||
let main_win = handle.get_window("main").unwrap();
|
||||
main_win.show().unwrap();
|
||||
main_win.set_focus().unwrap();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if !win.clone().unwrap().is_visible().unwrap() {
|
||||
win.clone().unwrap().show().unwrap();
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn wa_window(
|
||||
app: tauri::AppHandle,
|
||||
label: String,
|
||||
title: String,
|
||||
url: String,
|
||||
script: Option<String>,
|
||||
) {
|
||||
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)
|
||||
.inner_size(960.0, 700.0)
|
||||
.resizable(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
win
|
||||
.clone()
|
||||
if let Some(v) = win {
|
||||
if !v.is_visible().unwrap() {
|
||||
v.show().unwrap();
|
||||
}
|
||||
v.eval("window.location.reload()").unwrap();
|
||||
v.set_focus().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn window_reload(app: tauri::AppHandle, label: &str) {
|
||||
app
|
||||
.app_handle()
|
||||
.get_window(label)
|
||||
.unwrap()
|
||||
.eval("window.location.reload()")
|
||||
.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();
|
||||
}
|
||||
|
||||
@@ -1,179 +1,142 @@
|
||||
use crate::utils::{chat_root, create_file, exists};
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
use log::{error, info};
|
||||
use serde_json::Value;
|
||||
use std::{collections::BTreeMap, fs, path::PathBuf};
|
||||
use std::{collections::BTreeMap, path::PathBuf};
|
||||
use tauri::{Manager, Theme};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::TitleBarStyle;
|
||||
|
||||
// pub const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15";
|
||||
// pub const PHONE_USER_AGENT: &str = "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";
|
||||
use crate::utils::{app_root, create_file, exists};
|
||||
|
||||
pub const APP_WEBSITE: &str = "https://lencx.github.io/app/";
|
||||
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 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#"{
|
||||
"stay_on_top": false,
|
||||
"auto_update": "Prompt",
|
||||
"theme": "Light",
|
||||
"tray": true,
|
||||
"titlebar": true,
|
||||
"popup_search": false,
|
||||
"global_shortcut": "",
|
||||
"hide_dock_icon": false,
|
||||
"main_dashboard": false,
|
||||
"tray_dashboard": false,
|
||||
"main_origin": "https://chat.openai.com",
|
||||
"tray_origin": "https://chat.openai.com",
|
||||
"default_origin": "https://chat.openai.com",
|
||||
"ua_window": "",
|
||||
"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#"{
|
||||
"stay_on_top": false,
|
||||
"auto_update": "Prompt",
|
||||
"theme": "Light",
|
||||
"tray": true,
|
||||
"titlebar": false,
|
||||
"popup_search": false,
|
||||
"global_shortcut": "",
|
||||
"hide_dock_icon": false,
|
||||
"main_dashboard": false,
|
||||
"tray_dashboard": false,
|
||||
"main_origin": "https://chat.openai.com",
|
||||
"tray_origin": "https://chat.openai.com",
|
||||
"default_origin": "https://chat.openai.com",
|
||||
"ua_window": "",
|
||||
"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"
|
||||
}"#;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
pub struct ChatConfJson {
|
||||
// support macOS only
|
||||
pub titlebar: bool,
|
||||
pub hide_dock_icon: bool,
|
||||
pub const APP_CONF_PATH: &str = "chat.conf.json";
|
||||
pub const CHATGPT_URL: &str = "https://chat.openai.com";
|
||||
pub const UA_MOBILE: &str = "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";
|
||||
|
||||
// macOS and Windows, Light/Dark/System
|
||||
pub theme: String,
|
||||
// auto update policy, Prompt/Silent/Disable
|
||||
pub auto_update: String,
|
||||
pub tray: bool,
|
||||
pub popup_search: bool,
|
||||
pub stay_on_top: bool,
|
||||
pub main_dashboard: bool,
|
||||
pub tray_dashboard: bool,
|
||||
pub main_origin: String,
|
||||
pub tray_origin: String,
|
||||
pub default_origin: String,
|
||||
pub ua_window: String,
|
||||
pub ua_tray: String,
|
||||
pub global_shortcut: Option<String>,
|
||||
macro_rules! pub_struct {
|
||||
($name:ident {$($field:ident: $t:ty,)*}) => {
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
pub struct $name {
|
||||
$(pub $field: $t),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChatConfJson {
|
||||
/// init chat.conf.json
|
||||
/// path: ~/.chatgpt/chat.conf.json
|
||||
pub fn init() -> PathBuf {
|
||||
info!("chat_conf_init");
|
||||
let conf_file = ChatConfJson::conf_path();
|
||||
let content = if cfg!(target_os = "macos") {
|
||||
DEFAULT_CHAT_CONF_MAC
|
||||
} else {
|
||||
DEFAULT_CHAT_CONF
|
||||
};
|
||||
pub_struct!(AppConf {
|
||||
titlebar: bool,
|
||||
hide_dock_icon: bool,
|
||||
// macOS and Windows: light / dark / system
|
||||
theme: String,
|
||||
// auto update policy: prompt / silent / disable
|
||||
auto_update: String,
|
||||
tray: bool,
|
||||
popup_search: bool,
|
||||
stay_on_top: bool,
|
||||
main_dashboard: bool,
|
||||
tray_dashboard: bool,
|
||||
main_origin: String,
|
||||
tray_origin: String,
|
||||
default_origin: String,
|
||||
ua_window: String,
|
||||
ua_tray: String,
|
||||
global_shortcut: Option<String>,
|
||||
});
|
||||
|
||||
if !exists(&conf_file) {
|
||||
create_file(&conf_file).unwrap();
|
||||
fs::write(&conf_file, content).unwrap();
|
||||
return conf_file;
|
||||
impl AppConf {
|
||||
pub fn new() -> Self {
|
||||
info!("conf_init");
|
||||
Self {
|
||||
titlebar: !cfg!(target_os = "macos"),
|
||||
hide_dock_icon: false,
|
||||
theme: "light".into(),
|
||||
auto_update: "prompt".into(),
|
||||
tray: true,
|
||||
popup_search: false,
|
||||
stay_on_top: false,
|
||||
main_dashboard: false,
|
||||
tray_dashboard: false,
|
||||
main_origin: CHATGPT_URL.into(),
|
||||
tray_origin: CHATGPT_URL.into(),
|
||||
default_origin: CHATGPT_URL.into(),
|
||||
ua_tray: UA_MOBILE.into(),
|
||||
ua_window: "".into(),
|
||||
global_shortcut: None,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
pub fn file_path() -> PathBuf {
|
||||
app_root().join(APP_CONF_PATH)
|
||||
}
|
||||
|
||||
pub fn read() -> Self {
|
||||
match std::fs::read_to_string(Self::file_path()) {
|
||||
Ok(v) => serde_json::from_str::<AppConf>(&v).unwrap(),
|
||||
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
|
||||
}
|
||||
|
||||
pub fn conf_path() -> PathBuf {
|
||||
chat_root().join("chat.conf.json")
|
||||
}
|
||||
|
||||
pub fn get_chat_conf() -> Self {
|
||||
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()
|
||||
error!("conf_read_error: {}", err);
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_chat_conf() -> Self {
|
||||
let conf_file = ChatConfJson::conf_path();
|
||||
let content = if cfg!(target_os = "macos") {
|
||||
DEFAULT_CHAT_CONF_MAC
|
||||
pub fn write(self) -> Self {
|
||||
let path = &Self::file_path();
|
||||
if !exists(path) {
|
||||
create_file(path).unwrap();
|
||||
info!("conf_create");
|
||||
}
|
||||
if let Ok(v) = serde_json::to_string_pretty(&self) {
|
||||
std::fs::write(path, v).unwrap_or_else(|err| {
|
||||
error!("conf_write: {}", err);
|
||||
Self::default().write();
|
||||
});
|
||||
} else {
|
||||
DEFAULT_CHAT_CONF
|
||||
};
|
||||
fs::write(&conf_file, content).unwrap();
|
||||
serde_json::from_str(content).unwrap()
|
||||
error!("conf_ser");
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
// https://users.rust-lang.org/t/updating-object-fields-given-dynamic-json/39049/3
|
||||
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)?;
|
||||
let new_rules: BTreeMap<String, Value> = serde_json::from_value(new_rules.clone())?;
|
||||
pub fn amend(self, json: Value) -> Self {
|
||||
let val = serde_json::to_value(&self).unwrap();
|
||||
let mut config: BTreeMap<String, Value> = serde_json::from_value(val).unwrap();
|
||||
let new_json: BTreeMap<String, Value> = serde_json::from_value(json).unwrap();
|
||||
|
||||
for (k, v) in new_rules {
|
||||
for (k, v) in new_json {
|
||||
config.insert(k, v);
|
||||
}
|
||||
|
||||
fs::write(
|
||||
ChatConfJson::conf_path(),
|
||||
serde_json::to_string_pretty(&config)?,
|
||||
)?;
|
||||
|
||||
if let Some(handle) = app {
|
||||
tauri::api::process::restart(&handle.env());
|
||||
match serde_json::to_string_pretty(&config) {
|
||||
Ok(v) => match serde_json::from_str::<AppConf>(&v) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
error!("conf_amend_parse: {}", err);
|
||||
self
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
error!("conf_amend_str: {}", err);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn theme() -> Option<Theme> {
|
||||
let conf = ChatConfJson::get_chat_conf();
|
||||
let theme = match conf.theme.as_str() {
|
||||
"System" => match dark_light::detect() {
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn titlebar(self) -> TitleBarStyle {
|
||||
if self.titlebar {
|
||||
TitleBarStyle::Transparent
|
||||
} else {
|
||||
TitleBarStyle::Overlay
|
||||
}
|
||||
}
|
||||
|
||||
pub fn theme_mode() -> Theme {
|
||||
match Self::get_theme().as_str() {
|
||||
"system" => match dark_light::detect() {
|
||||
// Dark mode
|
||||
dark_light::Mode::Dark => Theme::Dark,
|
||||
// Light mode
|
||||
@@ -181,20 +144,76 @@ impl ChatConfJson {
|
||||
// Unspecified
|
||||
dark_light::Mode::Default => Theme::Light,
|
||||
},
|
||||
"Dark" => Theme::Dark,
|
||||
"dark" => Theme::Dark,
|
||||
_ => Theme::Light,
|
||||
};
|
||||
|
||||
Some(theme)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn titlebar() -> TitleBarStyle {
|
||||
let conf = ChatConfJson::get_chat_conf();
|
||||
if conf.titlebar {
|
||||
TitleBarStyle::Transparent
|
||||
} else {
|
||||
TitleBarStyle::Overlay
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_theme() -> String {
|
||||
Self::read().theme.to_lowercase()
|
||||
}
|
||||
|
||||
pub fn get_auto_update(self) -> String {
|
||||
self.auto_update.to_lowercase()
|
||||
}
|
||||
|
||||
pub fn theme_check(self, mode: &str) -> bool {
|
||||
self.theme.to_lowercase() == mode
|
||||
}
|
||||
|
||||
pub fn restart(self, app: tauri::AppHandle) {
|
||||
tauri::api::process::restart(&app.env());
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AppConf {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod cmd {
|
||||
use super::AppConf;
|
||||
use tauri::{command, AppHandle, Manager};
|
||||
|
||||
#[command]
|
||||
pub fn get_app_conf() -> AppConf {
|
||||
AppConf::read()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn reset_app_conf() -> AppConf {
|
||||
AppConf::default().write()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn get_theme() -> String {
|
||||
AppConf::get_theme()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn form_confirm(_app: AppHandle, data: serde_json::Value) {
|
||||
AppConf::read().amend(serde_json::json!(data)).write();
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn form_cancel(app: AppHandle, label: &str, title: &str, msg: &str) {
|
||||
let win = app.app_handle().get_window(label).unwrap();
|
||||
tauri::api::dialog::ask(
|
||||
app.app_handle().get_window(label).as_ref(),
|
||||
title,
|
||||
msg,
|
||||
move |is_cancel| {
|
||||
if is_cancel {
|
||||
win.close().unwrap();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn form_msg(app: AppHandle, label: &str, title: &str, msg: &str) {
|
||||
let win = app.app_handle().get_window(label);
|
||||
tauri::api::dialog::message(win.as_ref(), title, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ mod conf;
|
||||
mod utils;
|
||||
|
||||
use app::{cmd, fs_extra, gpt, menu, setup, window};
|
||||
use conf::ChatConfJson;
|
||||
use tauri::api::path;
|
||||
use conf::AppConf;
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_log::{
|
||||
fern::colors::{Color, ColoredLevelConfig},
|
||||
@@ -18,38 +17,36 @@ use tauri_plugin_log::{
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
ChatConfJson::init();
|
||||
let app_conf = AppConf::read().write();
|
||||
// If the file does not exist, creating the file will block menu synchronization
|
||||
utils::create_chatgpt_prompts();
|
||||
let context = tauri::generate_context!();
|
||||
let colors = ColoredLevelConfig {
|
||||
error: Color::Red,
|
||||
warn: Color::Yellow,
|
||||
debug: Color::Blue,
|
||||
info: Color::BrightGreen,
|
||||
trace: Color::Cyan,
|
||||
};
|
||||
|
||||
gpt::download_list("chat.download.json", "download", None, None);
|
||||
gpt::download_list("chat.notes.json", "notes", None, None);
|
||||
|
||||
let chat_conf = ChatConfJson::get_chat_conf();
|
||||
let mut log = tauri_plugin_log::Builder::default()
|
||||
.targets([
|
||||
// LogTarget::LogDir,
|
||||
// LOG PATH: ~/.chatgpt/ChatGPT.log
|
||||
LogTarget::Folder(utils::app_root()),
|
||||
LogTarget::Stdout,
|
||||
LogTarget::Webview,
|
||||
])
|
||||
.level(log::LevelFilter::Debug);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
log = log.with_colors(ColoredLevelConfig {
|
||||
error: Color::Red,
|
||||
warn: Color::Yellow,
|
||||
debug: Color::Blue,
|
||||
info: Color::BrightGreen,
|
||||
trace: Color::Cyan,
|
||||
});
|
||||
}
|
||||
|
||||
let mut builder = tauri::Builder::default()
|
||||
// https://github.com/tauri-apps/tauri/pull/2736
|
||||
.plugin(
|
||||
tauri_plugin_log::Builder::default()
|
||||
.targets([
|
||||
// LogTarget::LogDir,
|
||||
// LOG PATH: ~/.chatgpt/ChatGPT.log
|
||||
LogTarget::Folder(path::home_dir().unwrap().join(".chatgpt")),
|
||||
LogTarget::Stdout,
|
||||
LogTarget::Webview,
|
||||
])
|
||||
.level(log::LevelFilter::Debug)
|
||||
.with_colors(colors)
|
||||
.build(),
|
||||
)
|
||||
.plugin(log.build())
|
||||
.plugin(tauri_plugin_positioner::init())
|
||||
.plugin(tauri_plugin_autostart::init(
|
||||
MacosLauncher::LaunchAgent,
|
||||
@@ -61,13 +58,7 @@ async fn main() {
|
||||
cmd::download,
|
||||
cmd::save_file,
|
||||
cmd::open_link,
|
||||
cmd::get_chat_conf,
|
||||
cmd::get_theme,
|
||||
cmd::reset_chat_conf,
|
||||
cmd::run_check_update,
|
||||
cmd::form_cancel,
|
||||
cmd::form_confirm,
|
||||
cmd::form_msg,
|
||||
cmd::open_file,
|
||||
cmd::get_data,
|
||||
gpt::get_chat_model_cmd,
|
||||
@@ -77,16 +68,22 @@ async fn main() {
|
||||
gpt::cmd_list,
|
||||
gpt::download_list,
|
||||
gpt::get_download_list,
|
||||
window::wa_window,
|
||||
window::control_window,
|
||||
window::window_reload,
|
||||
window::dalle2_search_window,
|
||||
fs_extra::metadata,
|
||||
conf::cmd::get_app_conf,
|
||||
conf::cmd::reset_app_conf,
|
||||
conf::cmd::get_theme,
|
||||
conf::cmd::form_confirm,
|
||||
conf::cmd::form_cancel,
|
||||
conf::cmd::form_msg,
|
||||
window::cmd::wa_window,
|
||||
window::cmd::control_window,
|
||||
window::cmd::window_reload,
|
||||
window::cmd::dalle2_search_window,
|
||||
])
|
||||
.setup(setup::init)
|
||||
.menu(menu::init());
|
||||
|
||||
if chat_conf.tray {
|
||||
if app_conf.tray {
|
||||
builder = builder.system_tray(menu::tray_menu());
|
||||
}
|
||||
|
||||
@@ -94,19 +91,10 @@ async fn main() {
|
||||
.on_menu_event(menu::menu_handler)
|
||||
.on_system_tray_event(menu::tray_handler)
|
||||
.on_window_event(|event| {
|
||||
// https://github.com/tauri-apps/tauri/discussions/2684
|
||||
if let tauri::WindowEvent::CloseRequested { api, .. } = event.event() {
|
||||
let win = event.window();
|
||||
if win.label() == "core" {
|
||||
// TODO: https://github.com/tauri-apps/tauri/issues/3084
|
||||
// event.window().hide().unwrap();
|
||||
// https://github.com/tauri-apps/tao/pull/517
|
||||
#[cfg(target_os = "macos")]
|
||||
event.window().minimize().unwrap();
|
||||
|
||||
// fix: https://github.com/lencx/ChatGPT/issues/93
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
event.window().hide().unwrap();
|
||||
} else {
|
||||
win.close().unwrap();
|
||||
}
|
||||
|
||||
4
src-tauri/src/scripts/cmd.js
vendored
4
src-tauri/src/scripts/cmd.js
vendored
@@ -75,6 +75,10 @@ function init() {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.chatappico.copy {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
@media screen and (max-width: 767px) {
|
||||
#download-png-button, #download-pdf-button, #download-html-button {
|
||||
display: none;
|
||||
|
||||
138
src-tauri/src/scripts/core.js
vendored
138
src-tauri/src/scripts/core.js
vendored
@@ -36,8 +36,22 @@ async function invoke(cmd, args) {
|
||||
});
|
||||
}
|
||||
|
||||
async function message(message) {
|
||||
invoke('messageDialog', {
|
||||
__tauriModule: 'Dialog',
|
||||
message: {
|
||||
cmd: 'messageDialog',
|
||||
message: message.toString(),
|
||||
title: null,
|
||||
type: null,
|
||||
buttonLabel: null
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.uid = uid;
|
||||
window.invoke = invoke;
|
||||
window.message = message;
|
||||
window.transformCallback = transformCallback;
|
||||
|
||||
async function init() {
|
||||
@@ -45,8 +59,6 @@ async function init() {
|
||||
document.getElementsByTagName('html')[0].style['font-size'] = '70%';
|
||||
}
|
||||
|
||||
if (__TAURI_METADATA__.__currentWindow.label !== 'core') return;
|
||||
|
||||
async function platform() {
|
||||
return invoke('platform', {
|
||||
__tauriModule: 'Os',
|
||||
@@ -54,29 +66,31 @@ async function init() {
|
||||
});
|
||||
}
|
||||
|
||||
const _platform = await platform();
|
||||
const chatConf = await invoke('get_chat_conf') || {};
|
||||
if (/darwin/.test(_platform) && !chatConf.titlebar) {
|
||||
const topStyleDom = document.createElement("style");
|
||||
topStyleDom.innerHTML = `#chatgpt-app-window-top{position:fixed;top:0;z-index:999999999;width:100%;height:24px;background:transparent;cursor:grab;cursor:-webkit-grab;user-select:none;-webkit-user-select:none;}#chatgpt-app-window-top:active {cursor:grabbing;cursor:-webkit-grabbing;}`;
|
||||
document.head.appendChild(topStyleDom);
|
||||
const topDom = document.createElement("div");
|
||||
topDom.id = "chatgpt-app-window-top";
|
||||
document.body.appendChild(topDom);
|
||||
if (__TAURI_METADATA__.__currentWindow.label !== 'tray') {
|
||||
const _platform = await platform();
|
||||
const chatConf = await invoke('get_app_conf') || {};
|
||||
if (/darwin/.test(_platform) && !chatConf.titlebar) {
|
||||
const topStyleDom = document.createElement("style");
|
||||
topStyleDom.innerHTML = `#chatgpt-app-window-top{position:fixed;top:0;z-index:999999999;width:100%;height:24px;background:transparent;cursor:grab;cursor:-webkit-grab;user-select:none;-webkit-user-select:none;}#chatgpt-app-window-top:active {cursor:grabbing;cursor:-webkit-grabbing;}`;
|
||||
document.head.appendChild(topStyleDom);
|
||||
const topDom = document.createElement("div");
|
||||
topDom.id = "chatgpt-app-window-top";
|
||||
document.body.appendChild(topDom);
|
||||
|
||||
if (window.location.host === 'chat.openai.com') {
|
||||
const nav = document.body.querySelector('nav');
|
||||
if (nav) {
|
||||
const currentPaddingTop = parseInt(window.getComputedStyle(document.querySelector('nav'), null).getPropertyValue('padding-top').replace('px', ''), 10);
|
||||
const navStyleDom = document.createElement("style");
|
||||
navStyleDom.innerHTML = `nav{padding-top:${currentPaddingTop + topDom.clientHeight}px !important}`;
|
||||
document.head.appendChild(navStyleDom);
|
||||
if (window.location.host === 'chat.openai.com') {
|
||||
const nav = document.body.querySelector('nav');
|
||||
if (nav) {
|
||||
const currentPaddingTop = parseInt(window.getComputedStyle(document.querySelector('nav'), null).getPropertyValue('padding-top').replace('px', ''), 10);
|
||||
const navStyleDom = document.createElement("style");
|
||||
navStyleDom.innerHTML = `nav{padding-top:${currentPaddingTop + topDom.clientHeight}px !important}`;
|
||||
document.head.appendChild(navStyleDom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
topDom.addEventListener("mousedown", () => invoke("drag_window"));
|
||||
topDom.addEventListener("touchstart", () => invoke("drag_window"));
|
||||
topDom.addEventListener("dblclick", () => invoke("fullscreen"));
|
||||
topDom.addEventListener("mousedown", () => invoke("drag_window"));
|
||||
topDom.addEventListener("touchstart", () => invoke("drag_window"));
|
||||
topDom.addEventListener("dblclick", () => invoke("fullscreen"));
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
@@ -87,20 +101,76 @@ async function init() {
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('wheel', function(event) {
|
||||
const deltaX = event.wheelDeltaX;
|
||||
if (Math.abs(deltaX) >= 50) {
|
||||
if (deltaX > 0) {
|
||||
window.history.go(-1);
|
||||
} else {
|
||||
window.history.go(1);
|
||||
}
|
||||
if (window.location.host === 'chat.openai.com') {
|
||||
window.__sync_prompts = async function() {
|
||||
await invoke('sync_prompts', { time: Date.now() });
|
||||
}
|
||||
});
|
||||
|
||||
window.__sync_prompts = async function() {
|
||||
await invoke('sync_prompts', { time: Date.now() });
|
||||
}
|
||||
|
||||
coreZoom();
|
||||
}
|
||||
|
||||
function coreZoom() {
|
||||
const styleDom = document.createElement('style');
|
||||
styleDom.innerHTML = `
|
||||
#ZoomTopTip {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 20px;
|
||||
background: #2a2a2a;
|
||||
color: #fafafa;
|
||||
padding: 20px 15px;
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
z-index: 999999;
|
||||
box-shadow: 0 2px 2px 2px #d8d8d8;
|
||||
}
|
||||
.ZoomTopTipAni {
|
||||
transition: opacity 200ms, display 200ms;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
`;
|
||||
document.head.append(styleDom);
|
||||
const zoomTipDom = document.createElement('div');
|
||||
zoomTipDom.id = 'ZoomTopTip';
|
||||
document.body.appendChild(zoomTipDom);
|
||||
function zoom(callback) {
|
||||
if (window.zoomSetTimeout) clearTimeout(window.zoomSetTimeout);
|
||||
const htmlZoom = window.localStorage.getItem("htmlZoom") || "100%";
|
||||
const html = document.getElementsByTagName("html")[0];
|
||||
const zoom = callback(htmlZoom);
|
||||
html.style.zoom = zoom;
|
||||
window.localStorage.setItem("htmlZoom", zoom);
|
||||
zoomTipDom.innerHTML = zoom;
|
||||
zoomTipDom.style.display = 'block';
|
||||
zoomTipDom.classList.remove('ZoomTopTipAni');
|
||||
window.zoomSetTimeout = setTimeout(() => {
|
||||
zoomTipDom.classList.add('ZoomTopTipAni');
|
||||
}, 2500);
|
||||
}
|
||||
function zoomDefault() {
|
||||
const htmlZoom = window.localStorage.getItem("htmlZoom");
|
||||
if (htmlZoom) {
|
||||
document.getElementsByTagName("html")[0].style.zoom = htmlZoom;
|
||||
}
|
||||
}
|
||||
function zoomIn() {
|
||||
zoom((htmlZoom) => `${Math.min(parseInt(htmlZoom) + 10, 200)}%`);
|
||||
}
|
||||
function zoomOut() {
|
||||
zoom((htmlZoom) => `${Math.max(parseInt(htmlZoom) - 10, 30)}%`);
|
||||
}
|
||||
function zoom0() {
|
||||
zoom(() => `100%`);
|
||||
}
|
||||
zoomDefault();
|
||||
window.__zoomIn = zoomIn;
|
||||
window.__zoomOut = zoomOut;
|
||||
window.__zoom0 = zoom0;
|
||||
}
|
||||
|
||||
if (
|
||||
|
||||
47
src-tauri/src/scripts/export.js
vendored
47
src-tauri/src/scripts/export.js
vendored
@@ -2,11 +2,13 @@
|
||||
|
||||
async function init() {
|
||||
const buttonOuterHTMLFallback = `<button class="btn flex justify-center gap-2 btn-neutral" id="download-png-button">Try Again</button>`;
|
||||
if (window.innerWidth < 767) return;
|
||||
const chatConf = await invoke('get_chat_conf') || {};
|
||||
removeButtons();
|
||||
if (window.buttonsInterval) {
|
||||
clearInterval(window.buttonsInterval);
|
||||
}
|
||||
if (window.innerWidth < 767) return;
|
||||
|
||||
const chatConf = await invoke('get_app_conf') || {};
|
||||
window.buttonsInterval = setInterval(() => {
|
||||
const actionsArea = document.querySelector("form>div>div");
|
||||
if (!actionsArea) {
|
||||
@@ -20,12 +22,15 @@ async function init() {
|
||||
TryAgainButton = parentNode.querySelector("button");
|
||||
}
|
||||
addActionsButtons(actionsArea, TryAgainButton, chatConf);
|
||||
copyBtns();
|
||||
} else if (shouldRemoveButtons()) {
|
||||
removeButtons();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', init);
|
||||
|
||||
const Format = {
|
||||
PNG: "png",
|
||||
PDF: "pdf",
|
||||
@@ -200,7 +205,7 @@ class Elements {
|
||||
}
|
||||
init() {
|
||||
// this.threadWrapper = document.querySelector(".cdfdFe");
|
||||
this.spacer = document.querySelector(".w-full.h-48.flex-shrink-0");
|
||||
this.spacer = document.querySelector("[class*='h-48'].w-full.flex-shrink-0");
|
||||
this.thread = document.querySelector(
|
||||
"[class*='react-scroll-to-bottom']>[class*='react-scroll-to-bottom']>div"
|
||||
);
|
||||
@@ -268,10 +273,44 @@ function setIcon(type) {
|
||||
// link: `<svg class="chatappico" viewBox="0 0 1024 1024"><path d="M1007.382 379.672L655.374 75.702C624.562 49.092 576 70.694 576 112.03v160.106C254.742 275.814 0 340.2 0 644.652c0 122.882 79.162 244.618 166.666 308.264 27.306 19.862 66.222-5.066 56.154-37.262C132.132 625.628 265.834 548.632 576 544.17V720c0 41.4 48.6 62.906 79.374 36.328l352.008-304c22.142-19.124 22.172-53.506 0-72.656z" p-id="8506" fill="currentColor"></path></svg>`,
|
||||
png: `<svg class="chatappico" viewBox="0 0 1070 1024"><path d="M981.783273 0H85.224727C38.353455 0 0 35.374545 0 83.083636v844.893091c0 47.616 38.353455 86.574545 85.178182 86.574546h903.633454c46.917818 0 81.733818-38.958545 81.733819-86.574546V83.083636C1070.592 35.374545 1028.701091 0 981.783273 0zM335.825455 135.912727c74.193455 0 134.330182 60.974545 134.330181 136.285091 0 75.170909-60.136727 136.192-134.330181 136.192-74.286545 0-134.516364-61.021091-134.516364-136.192 0-75.264 60.229818-136.285091 134.516364-136.285091z m-161.512728 745.937455a41.890909 41.890909 0 0 1-27.648-10.379637 43.752727 43.752727 0 0 1-4.654545-61.067636l198.097454-255.162182a42.123636 42.123636 0 0 1 57.716364-6.702545l116.549818 128.139636 286.906182-352.814545c14.615273-18.711273 90.251636-106.775273 135.866182-6.935273 0.093091-0.093091 0.093091 112.965818 0.232727 247.761455 0.093091 140.8 0.093091 317.067636 0.093091 317.067636-1.024-0.093091-762.740364 0.093091-763.112727 0.093091z" fill="currentColor"></path></svg>`,
|
||||
pdf: `<svg class="chatappico pdf" viewBox="0 0 1024 1024"><path d="M821.457602 118.382249H205.725895c-48.378584 0-87.959995 39.583368-87.959996 87.963909v615.731707c0 48.378584 39.581411 87.959995 87.959996 87.959996h615.733664c48.380541 0 87.961952-39.581411 87.961952-87.959996V206.346158c-0.001957-48.378584-39.583368-87.963909-87.963909-87.963909zM493.962468 457.544987c-10.112054 32.545237-21.72487 82.872662-38.806571 124.248336-8.806957 22.378397-8.380404 18.480717-15.001764 32.609808l5.71738-1.851007c58.760658-16.443827 99.901532-20.519564 138.162194-27.561607-7.67796-6.06371-14.350194-10.751884-19.631237-15.586807-26.287817-29.101504-35.464584-34.570387-70.440002-111.862636v0.003913z m288.36767 186.413594c-7.476424 8.356924-20.670227 13.191847-40.019704 13.191847-33.427694 0-63.808858-9.229597-107.79277-31.660824-75.648648 8.356924-156.097 17.214754-201.399704 31.729308-2.199293 0.876587-4.832967 1.759043-7.916674 3.077836-54.536215 93.237125-95.031389 132.767663-130.621199 131.19646-11.286054-0.49895-27.694661-7.044-32.973748-10.11988l-6.52157-6.196764-2.29517-4.353583c-3.07588-7.91863-3.954423-15.395054-2.197337-23.751977 4.838837-23.309771 29.907651-60.251638 82.686779-93.237126 8.356924-6.159587 27.430511-15.897917 45.020944-24.25484 13.311204-21.177004 19.45905-34.744531 36.341171-72.259702 19.102937-45.324228 36.505531-99.492589 47.500041-138.191543v-0.44025c-16.267727-53.219378-25.945401-89.310095-9.67376-147.80856 3.958337-16.71189 18.46702-33.864031 34.748444-33.864031h10.552304c10.115967 0 19.791684 3.520043 26.829814 10.552304 29.029107 29.031064 15.39114 103.824649 0.8805 162.323113-0.8805 2.63563-1.322707 4.832967-1.761 6.153717 17.59239 49.697378 45.400538 98.774492 73.108895 121.647926 11.436717 8.791304 22.638634 18.899444 36.71098 26.814161 19.791684-2.20125 37.517128-4.11487 55.547812-4.11487 54.540128 0 87.525615 9.67963 100.279169 30.351814 4.400543 7.034217 6.595923 15.389184 5.281043 24.1844-0.44025 10.996467-4.39663 21.112434-12.31526 29.031064z m-27.796407-36.748157c-4.394673-4.398587-17.024957-16.936907-78.601259-16.936907-3.073923 0-10.622744-0.784623-14.57521 3.612007 32.104987 14.072347 62.830525 24.757704 83.058545 24.757703 3.083707 0 5.72325-0.442207 8.356923-0.876586h1.759044c2.20125-0.8805 3.520043-1.324663 3.960293-5.71738-0.87463-1.324663-1.757087-3.083707-3.958336-4.838837z m-387.124553 63.041845c-9.237424 5.27713-16.71189 10.112054-21.112433 13.634053-31.226444 28.586901-51.018128 57.616008-53.217422 74.331812 19.789727-6.59788 45.737084-35.626987 74.329855-87.961952v-0.003913z m125.574957-297.822284l2.197336-1.761c3.079793-14.072347 5.232127-29.189554 7.87167-38.869184l1.318794-7.036174c4.39663-25.070771 2.71781-39.720334-4.76057-50.272637l-6.59788-2.20125a57.381208 57.381208 0 0 0-3.079794 5.27713c-7.474467 18.47289-7.063567 55.283661 3.0524 94.865072l-0.001956-0.001957z" fill="currentColor"></path></svg>`,
|
||||
md: `<svg class="chatappico md" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1380" width="200" height="200"><path d="M128 128h768a42.666667 42.666667 0 0 1 42.666667 42.666667v682.666666a42.666667 42.666667 0 0 1-42.666667 42.666667H128a42.666667 42.666667 0 0 1-42.666667-42.666667V170.666667a42.666667 42.666667 0 0 1 42.666667-42.666667z m170.666667 533.333333v-170.666666l85.333333 85.333333 85.333333-85.333333v170.666666h85.333334v-298.666666h-85.333334l-85.333333 85.333333-85.333333-85.333333H213.333333v298.666666h85.333334z m469.333333-128v-170.666666h-85.333333v170.666666h-85.333334l128 128 128-128h-85.333333z" p-id="1381" fill="currentColor"></path></svg>`
|
||||
md: `<svg class="chatappico md" viewBox="0 0 1024 1024" width="200" height="200"><path d="M128 128h768a42.666667 42.666667 0 0 1 42.666667 42.666667v682.666666a42.666667 42.666667 0 0 1-42.666667 42.666667H128a42.666667 42.666667 0 0 1-42.666667-42.666667V170.666667a42.666667 42.666667 0 0 1 42.666667-42.666667z m170.666667 533.333333v-170.666666l85.333333 85.333333 85.333333-85.333333v170.666666h85.333334v-298.666666h-85.333334l-85.333333 85.333333-85.333333-85.333333H213.333333v298.666666h85.333334z m469.333333-128v-170.666666h-85.333333v170.666666h-85.333334l128 128 128-128h-85.333333z" p-id="1381" fill="currentColor"></path></svg>`,
|
||||
copy: `<svg class="chatappico copy" stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>`
|
||||
}[type];
|
||||
}
|
||||
|
||||
function copyBtns() {
|
||||
Array.from(document.querySelectorAll("main >div>div>div>div>div"))
|
||||
.forEach(i => {
|
||||
if (i.querySelector('.chat-item-copy')) return;
|
||||
if (!i.querySelector('button.rounded-md')) return;
|
||||
const btn = i.querySelector('button.rounded-md').cloneNode(true);
|
||||
btn.classList.add('chat-item-copy');
|
||||
btn.title = 'Copy to clipboard';
|
||||
btn.innerHTML = setIcon('copy');
|
||||
i.querySelector('.self-end').appendChild(btn);
|
||||
btn.onclick = () => {
|
||||
copyToClipboard(i?.innerText?.trim() || '');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(text);
|
||||
} else {
|
||||
var textarea = document.createElement('textarea');
|
||||
document.body.appendChild(textarea);
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.clip = 'rect(0 0 0 0)';
|
||||
textarea.style.top = '10px';
|
||||
textarea.value = text;
|
||||
textarea.select();
|
||||
document.execCommand('copy', true);
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
message('Copied to clipboard');
|
||||
}
|
||||
|
||||
if (
|
||||
document.readyState === "complete" ||
|
||||
document.readyState === "interactive"
|
||||
|
||||
2
src-tauri/src/scripts/popup.core.js
vendored
2
src-tauri/src/scripts/popup.core.js
vendored
@@ -1,7 +1,7 @@
|
||||
// *** Core Script - DALL·E 2 Core ***
|
||||
|
||||
async function init() {
|
||||
const chatConf = await invoke('get_chat_conf') || {};
|
||||
const chatConf = await invoke('get_app_conf') || {};
|
||||
if (!chatConf.popup_search) return;
|
||||
if (!window.FloatingUIDOM) return;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
use log::{error, info};
|
||||
use regex::Regex;
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
@@ -11,7 +11,7 @@ use std::{
|
||||
use tauri::updater::UpdateResponse;
|
||||
use tauri::{utils::config::Config, AppHandle, Manager, Wry};
|
||||
|
||||
pub fn chat_root() -> PathBuf {
|
||||
pub fn app_root() -> PathBuf {
|
||||
tauri::api::path::home_dir().unwrap().join(".chatgpt")
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn create_file(path: &Path) -> Result<File> {
|
||||
}
|
||||
|
||||
pub fn create_chatgpt_prompts() {
|
||||
let sync_file = chat_root().join("cache_model").join("chatgpt_prompts.json");
|
||||
let sync_file = app_root().join("cache_model").join("chatgpt_prompts.json");
|
||||
if !exists(&sync_file) {
|
||||
create_file(&sync_file).unwrap();
|
||||
fs::write(&sync_file, "[]").unwrap();
|
||||
@@ -41,7 +41,7 @@ pub fn create_chatgpt_prompts() {
|
||||
}
|
||||
|
||||
pub fn script_path() -> PathBuf {
|
||||
let script_file = chat_root().join("main.js");
|
||||
let script_file = app_root().join("main.js");
|
||||
if !exists(&script_file) {
|
||||
create_file(&script_file).unwrap();
|
||||
fs::write(
|
||||
@@ -96,7 +96,7 @@ pub fn convert_path(path_str: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn clear_conf(app: &tauri::AppHandle) {
|
||||
let root = chat_root();
|
||||
let root = app_root();
|
||||
let msg = format!(
|
||||
"Path: {}\n
|
||||
Are you sure you want to clear all ChatGPT configurations? Performing this operation data can not be restored, please back up in advance.\n
|
||||
@@ -145,7 +145,7 @@ pub async fn get_data(
|
||||
if is_ok {
|
||||
Ok(Some(body))
|
||||
} else {
|
||||
info!("chatgpt_http_error: {}", body);
|
||||
error!("chatgpt_http: {}", body);
|
||||
if let Some(v) = app {
|
||||
tauri::api::dialog::message(v.get_window("core").as_ref(), "ChatGPT HTTP", body);
|
||||
}
|
||||
@@ -156,25 +156,25 @@ pub async fn get_data(
|
||||
pub fn run_check_update(app: AppHandle<Wry>, silent: bool, has_msg: Option<bool>) {
|
||||
info!("run_check_update: silent={} has_msg={:?}", silent, has_msg);
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let result = app.updater().check().await;
|
||||
let update_resp = result.unwrap();
|
||||
if update_resp.is_update_available() {
|
||||
if silent {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
silent_install(app, update_resp).await.unwrap();
|
||||
});
|
||||
} else {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
prompt_for_install(app, update_resp).await.unwrap();
|
||||
});
|
||||
}
|
||||
} else if let Some(v) = has_msg {
|
||||
if v {
|
||||
tauri::api::dialog::message(
|
||||
app.app_handle().get_window("core").as_ref(),
|
||||
"ChatGPT",
|
||||
"Your ChatGPT is up to date",
|
||||
);
|
||||
if let Ok(update_resp) = app.updater().check().await {
|
||||
if update_resp.is_update_available() {
|
||||
if silent {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
silent_install(app, update_resp).await.unwrap();
|
||||
});
|
||||
} else {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
prompt_for_install(app, update_resp).await.unwrap();
|
||||
});
|
||||
}
|
||||
} else if let Some(v) = has_msg {
|
||||
if v {
|
||||
tauri::api::dialog::message(
|
||||
app.app_handle().get_window("core").as_ref(),
|
||||
"ChatGPT",
|
||||
"Your ChatGPT is up to date",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "ChatGPT",
|
||||
"version": "0.10.0"
|
||||
"version": "0.10.2"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
||||
27
src/components/SwitchOrigin/index.tsx
vendored
27
src/components/SwitchOrigin/index.tsx
vendored
@@ -18,6 +18,11 @@ const SwitchOrigin: FC<SwitchOriginProps> = ({ name }) => {
|
||||
const originName = `${name}_origin`;
|
||||
const isEnable = Form.useWatch(dashboardName, form);
|
||||
|
||||
let urlList = [{ title: 'ChatGPT', url: 'https://chat.openai.com', init: true }];
|
||||
if (Array.isArray(list)) {
|
||||
urlList = urlList.concat(list);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
@@ -74,18 +79,16 @@ const SwitchOrigin: FC<SwitchOriginProps> = ({ name }) => {
|
||||
name={originName}
|
||||
>
|
||||
<Select disabled={isEnable} showSearch {...DISABLE_AUTO_COMPLETE} optionLabelProp="url">
|
||||
{[{ title: 'ChatGPT', url: 'https://chat.openai.com', init: true }, ...list].map(
|
||||
(i, idx) => (
|
||||
<Select.Option
|
||||
key={`${idx}_${i.url}`}
|
||||
label={i.title}
|
||||
value={i.url}
|
||||
title={`${i.title}${i.init ? '(Built-in)' : ''}: ${i.url}`}
|
||||
>
|
||||
<Tag color={i.init ? 'orange' : 'geekblue'}>{i.title}</Tag> {i.url}
|
||||
</Select.Option>
|
||||
),
|
||||
)}
|
||||
{urlList.map((i, idx) => (
|
||||
<Select.Option
|
||||
key={`${idx}_${i.url}`}
|
||||
label={i.title}
|
||||
value={i.url}
|
||||
title={`${i.title}${i.init ? '(Built-in)' : ''}: ${i.url}`}
|
||||
>
|
||||
<Tag color={i.init ? 'orange' : 'geekblue'}>{i.title}</Tag> {i.url}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
||||
2
src/utils.ts
vendored
2
src/utils.ts
vendored
@@ -2,7 +2,7 @@ import { readTextFile, writeTextFile, exists, createDir } from '@tauri-apps/api/
|
||||
import { homeDir, join, dirname } from '@tauri-apps/api/path';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export const CHAT_CONF_JSON = 'chat.conf.json';
|
||||
export const APP_CONF_JSON = 'chat.conf.json';
|
||||
export const CHAT_MODEL_JSON = 'chat.model.json';
|
||||
export const CHAT_MODEL_CMD_JSON = 'chat.model.cmd.json';
|
||||
export const CHAT_DOWNLOAD_JSON = 'chat.download.json';
|
||||
|
||||
4
src/view/about/index.scss
vendored
4
src/view/about/index.scss
vendored
@@ -7,6 +7,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.about-tab {
|
||||
.imgs {
|
||||
img {
|
||||
|
||||
26
src/view/dashboard/index.tsx
vendored
26
src/view/dashboard/index.tsx
vendored
@@ -7,7 +7,7 @@ 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 { CHAT_AWESOME_JSON, APP_CONF_JSON, readJSON } from '@/utils';
|
||||
import './index.scss';
|
||||
|
||||
export default function Dashboard() {
|
||||
@@ -19,7 +19,7 @@ export default function Dashboard() {
|
||||
|
||||
useInit(async () => {
|
||||
const getOS = await os.platform();
|
||||
const conf = await readJSON(CHAT_CONF_JSON);
|
||||
const conf = await readJSON(APP_CONF_JSON);
|
||||
const appTheme = await invoke('get_theme');
|
||||
setTheme(appTheme as string);
|
||||
setClass(!conf?.titlebar && getOS === 'darwin');
|
||||
@@ -29,15 +29,19 @@ export default function Dashboard() {
|
||||
if (!json) return;
|
||||
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]);
|
||||
if (Array.isArray(json)) {
|
||||
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) || []);
|
||||
} else {
|
||||
setList([]);
|
||||
}
|
||||
}, [JSON.stringify(json)]);
|
||||
|
||||
const handleLink = async (item: Record<string, any>) => {
|
||||
await invoke('wa_window', {
|
||||
|
||||
2
src/view/model/UserCustom/index.tsx
vendored
2
src/view/model/UserCustom/index.tsx
vendored
@@ -12,7 +12,7 @@ import { chatRoot, fmtDate } from '@/utils';
|
||||
import { modelColumns } from './config';
|
||||
import UserCustomForm from './Form';
|
||||
|
||||
export default function LanguageModel() {
|
||||
export default function UserCustom() {
|
||||
const { rowSelection, selectedRowIDs } = useTableRowSelection();
|
||||
const [isVisible, setVisible] = useState(false);
|
||||
const [jsonPath, setJsonPath] = useState('');
|
||||
|
||||
1
src/view/notes/config.tsx
vendored
1
src/view/notes/config.tsx
vendored
@@ -67,6 +67,5 @@ const RenderPath = ({ row }: any) => {
|
||||
};
|
||||
|
||||
export const getPath = async (row: any) => {
|
||||
const isImg = ['png'].includes(row?.ext);
|
||||
return (await path.join(await chatRoot(), 'notes', row.id)) + `.${row.ext}`;
|
||||
};
|
||||
|
||||
10
src/view/settings/General.tsx
vendored
10
src/view/settings/General.tsx
vendored
@@ -30,16 +30,16 @@ export default function General() {
|
||||
)}
|
||||
<Form.Item label="Theme" name="theme">
|
||||
<Radio.Group>
|
||||
<Radio value="Light">Light</Radio>
|
||||
<Radio value="Dark">Dark</Radio>
|
||||
<Radio value="light">Light</Radio>
|
||||
<Radio value="dark">Dark</Radio>
|
||||
{['darwin', 'windows'].includes(platformInfo) && <Radio value="System">System</Radio>}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label={<AutoUpdateLabel />} name="auto_update">
|
||||
<Radio.Group>
|
||||
<Radio value="Prompt">Prompt</Radio>
|
||||
<Radio value="Silent">Silent</Radio>
|
||||
{/*<Radio value="Disable">Disable</Radio>*/}
|
||||
<Radio value="prompt">Prompt</Radio>
|
||||
<Radio value="silent">Silent</Radio>
|
||||
{/*<Radio value="disable">Disable</Radio>*/}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label={<GlobalShortcutLabel />} name="global_shortcut">
|
||||
|
||||
2
src/view/settings/MainWindow.tsx
vendored
2
src/view/settings/MainWindow.tsx
vendored
@@ -30,7 +30,7 @@ const PopupSearchLabel = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default function General() {
|
||||
export default function MainWindow() {
|
||||
return (
|
||||
<>
|
||||
<Form.Item label={<PopupSearchLabel />} name="popup_search" valuePropName="checked">
|
||||
|
||||
2
src/view/settings/TrayWindow.tsx
vendored
2
src/view/settings/TrayWindow.tsx
vendored
@@ -17,7 +17,7 @@ const UALabel = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default function General() {
|
||||
export default function TrayWindow() {
|
||||
return (
|
||||
<>
|
||||
<Form.Item label="Enable SystemTray" name="tray" valuePropName="checked">
|
||||
|
||||
10
src/view/settings/index.tsx
vendored
10
src/view/settings/index.tsx
vendored
@@ -6,7 +6,7 @@ import { clone, omit, isEqual } from 'lodash';
|
||||
|
||||
import useInit from '@/hooks/useInit';
|
||||
import FilePath from '@/components/FilePath';
|
||||
import { chatRoot, CHAT_CONF_JSON } from '@/utils';
|
||||
import { chatRoot, APP_CONF_JSON } from '@/utils';
|
||||
import General from './General';
|
||||
import MainWindow from './MainWindow';
|
||||
import TrayWindow from './TrayWindow';
|
||||
@@ -24,8 +24,8 @@ export default function Settings() {
|
||||
}, [key]);
|
||||
|
||||
useInit(async () => {
|
||||
setChatConf(await invoke('get_chat_conf'));
|
||||
setPath(await path.join(await chatRoot(), CHAT_CONF_JSON));
|
||||
setChatConf(await invoke('get_app_conf'));
|
||||
setPath(await path.join(await chatRoot(), APP_CONF_JSON));
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -37,7 +37,7 @@ export default function Settings() {
|
||||
};
|
||||
|
||||
const onReset = async () => {
|
||||
const chatData = await invoke('reset_chat_conf');
|
||||
const chatData = await invoke('reset_app_conf');
|
||||
setChatConf(chatData);
|
||||
const isOk = await dialog.ask(`Configuration reset successfully, whether to restart?`, {
|
||||
title: 'ChatGPT Preferences',
|
||||
@@ -69,7 +69,7 @@ export default function Settings() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FilePath paths={CHAT_CONF_JSON} />
|
||||
<FilePath paths={APP_CONF_JSON} />
|
||||
<Form
|
||||
form={form}
|
||||
style={{ maxWidth: 500 }}
|
||||
|
||||
Reference in New Issue
Block a user