mirror of
https://github.com/FranP-code/ChatGPT.git
synced 2025-10-13 00:13:25 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc769235a7 | ||
|
|
52cc029b01 | ||
|
|
39dc007513 | ||
|
|
ba1fe9a603 | ||
|
|
e1f8030009 | ||
|
|
9a392a71f6 | ||
|
|
dc0c78fee2 | ||
|
|
b3bd54ce81 |
@@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
**最新版:**
|
**最新版:**
|
||||||
|
|
||||||
- `Mac`: [ChatGPT_0.7.3_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.7.3/ChatGPT_0.7.3_x64.dmg)
|
- `Mac`: [ChatGPT_0.7.4_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.7.4/ChatGPT_0.7.4_x64.dmg)
|
||||||
- `Linux`: [chat-gpt_0.7.3_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.7.3/chat-gpt_0.7.3_amd64.deb)
|
- `Linux`: [chat-gpt_0.7.4_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.7.4/chat-gpt_0.7.4_amd64.deb)
|
||||||
- `Windows`: [ChatGPT_0.7.3_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.7.3/ChatGPT_0.7.3_x64_en-US.msi)
|
- `Windows`: [ChatGPT_0.7.4_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.7.4/ChatGPT_0.7.4_x64_en-US.msi)
|
||||||
|
|
||||||
[其他版本...](https://github.com/lencx/ChatGPT/releases)
|
[其他版本...](https://github.com/lencx/ChatGPT/releases)
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ cask "popcorn-time", args: { "no-quarantine": true }
|
|||||||
- 系统托盘悬浮窗
|
- 系统托盘悬浮窗
|
||||||
- 应用菜单功能强大
|
- 应用菜单功能强大
|
||||||
- 支持斜杠命令及其配置(可手动配置或从文件同步 [#55](https://github.com/lencx/ChatGPT/issues/55))
|
- 支持斜杠命令及其配置(可手动配置或从文件同步 [#55](https://github.com/lencx/ChatGPT/issues/55))
|
||||||
- 进入应用的全局快捷键 (mac: `Command + Shift + O`, windows: `Ctrl + Shift + O`)
|
- 自定义全局快捷键 ([#108](https://github.com/lencx/ChatGPT/issues/108))
|
||||||
|
|
||||||
### 菜单项
|
### 菜单项
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,9 @@
|
|||||||
|
|
||||||
**Latest:**
|
**Latest:**
|
||||||
|
|
||||||
- `Mac`: [ChatGPT_0.7.3_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.7.3/ChatGPT_0.7.3_x64.dmg)
|
- `Mac`: [ChatGPT_0.7.4_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.7.4/ChatGPT_0.7.4_x64.dmg)
|
||||||
- `Linux`: [chat-gpt_0.7.3_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.7.3/chat-gpt_0.7.3_amd64.deb)
|
- `Linux`: [chat-gpt_0.7.4_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.7.4/chat-gpt_0.7.4_amd64.deb)
|
||||||
- `Windows`: [ChatGPT_0.7.3_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.7.3/ChatGPT_0.7.3_x64_en-US.msi)
|
- `Windows`: [ChatGPT_0.7.4_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.7.4/ChatGPT_0.7.4_x64_en-US.msi)
|
||||||
|
|
||||||
[Other version...](https://github.com/lencx/ChatGPT/releases)
|
[Other version...](https://github.com/lencx/ChatGPT/releases)
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ In the chatgpt text input area, type a character starting with `/` to bring up t
|
|||||||
- System tray hover window
|
- System tray hover window
|
||||||
- Powerful menu items
|
- Powerful menu items
|
||||||
- Support for slash commands and their configuration (can be configured manually or synchronized from a file [#55](https://github.com/lencx/ChatGPT/issues/55))
|
- Support for slash commands and their configuration (can be configured manually or synchronized from a file [#55](https://github.com/lencx/ChatGPT/issues/55))
|
||||||
- Global shortcuts to the chatgpt app (mac: `Command + Shift + O`, windows: `Ctrl + Shift + O`)
|
- Customize global shortcuts ([#108](https://github.com/lencx/ChatGPT/issues/108))
|
||||||
|
|
||||||
### MenuItem
|
### MenuItem
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# UPDATE LOG
|
# UPDATE LOG
|
||||||
|
|
||||||
|
## v0.7.4
|
||||||
|
|
||||||
|
fix:
|
||||||
|
- trying to resolve linux errors: `error while loading shared libraries`
|
||||||
|
- customize global shortcuts (`Menu -> Preferences -> Control Center -> General -> Global Shortcut`)
|
||||||
|
|
||||||
## v0.7.3
|
## v0.7.3
|
||||||
|
|
||||||
chore:
|
chore:
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ thiserror = "1.0.38"
|
|||||||
walkdir = "2.3.2"
|
walkdir = "2.3.2"
|
||||||
regex = "1.7.0"
|
regex = "1.7.0"
|
||||||
tokio = { version = "1.23.0", features = ["macros"] }
|
tokio = { version = "1.23.0", features = ["macros"] }
|
||||||
# reqwest = "0.11.13"
|
reqwest = "0.11.13"
|
||||||
|
wry = "0.23.4"
|
||||||
|
|
||||||
[dependencies.tauri-plugin-log]
|
[dependencies.tauri-plugin-log]
|
||||||
git = "https://github.com/tauri-apps/tauri-plugin-log"
|
git = "https://github.com/tauri-apps/tauri-plugin-log"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
conf::ChatConfJson,
|
conf::{ChatConfJson, GITHUB_PROMPTS_CSV_URL},
|
||||||
utils::{self, exists},
|
utils::{self, exists},
|
||||||
};
|
};
|
||||||
|
use log::info;
|
||||||
use std::{collections::HashMap, fs, path::PathBuf};
|
use std::{collections::HashMap, fs, path::PathBuf};
|
||||||
use tauri::{api, command, AppHandle, Manager};
|
use tauri::{api, command, AppHandle, Manager};
|
||||||
|
|
||||||
@@ -87,8 +88,17 @@ pub fn parse_prompt(data: String) -> Vec<PromptRecord> {
|
|||||||
let mut rdr = csv::Reader::from_reader(data.as_bytes());
|
let mut rdr = csv::Reader::from_reader(data.as_bytes());
|
||||||
let mut list = vec![];
|
let mut list = vec![];
|
||||||
for result in rdr.deserialize() {
|
for result in rdr.deserialize() {
|
||||||
let record: PromptRecord = result.unwrap();
|
let record: PromptRecord = result.unwrap_or_else(|err| {
|
||||||
list.push(record);
|
info!("parse_prompt_error: {}", err);
|
||||||
|
PromptRecord {
|
||||||
|
cmd: None,
|
||||||
|
act: "".to_string(),
|
||||||
|
prompt: "".to_string(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if !record.act.is_empty() {
|
||||||
|
list.push(record);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
@@ -134,75 +144,133 @@ pub fn cmd_list() -> Vec<ModelRecord> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub fn sync_prompts(app: AppHandle, data: String, time: u64) {
|
pub async fn sync_prompts(app: AppHandle, time: u64) -> Option<Vec<ModelRecord>> {
|
||||||
let data = parse_prompt(data)
|
let res = utils::get_data(GITHUB_PROMPTS_CSV_URL, Some(&app))
|
||||||
.iter()
|
.await
|
||||||
.map(move |i| ModelRecord {
|
.unwrap();
|
||||||
cmd: if i.cmd.is_some() {
|
|
||||||
i.cmd.clone().unwrap()
|
|
||||||
} else {
|
|
||||||
utils::gen_cmd(i.act.clone())
|
|
||||||
},
|
|
||||||
act: i.act.clone(),
|
|
||||||
prompt: i.prompt.clone(),
|
|
||||||
tags: vec!["chatgpt-prompts".to_string()],
|
|
||||||
enable: true,
|
|
||||||
})
|
|
||||||
.collect::<Vec<ModelRecord>>();
|
|
||||||
|
|
||||||
let model = chat_root().join("chat.model.json");
|
if let Some(v) = res {
|
||||||
let model_cmd = chat_root().join("chat.model.cmd.json");
|
let data = parse_prompt(v)
|
||||||
let chatgpt_prompts = chat_root().join("cache_model").join("chatgpt_prompts.json");
|
.iter()
|
||||||
|
.map(move |i| ModelRecord {
|
||||||
if !exists(&model) {
|
cmd: if i.cmd.is_some() {
|
||||||
fs::write(
|
i.cmd.clone().unwrap()
|
||||||
&model,
|
} else {
|
||||||
serde_json::json!({
|
utils::gen_cmd(i.act.clone())
|
||||||
"name": "ChatGPT Model",
|
},
|
||||||
"link": "https://github.com/lencx/ChatGPT"
|
act: i.act.clone(),
|
||||||
|
prompt: i.prompt.clone(),
|
||||||
|
tags: vec!["chatgpt-prompts".to_string()],
|
||||||
|
enable: true,
|
||||||
})
|
})
|
||||||
.to_string(),
|
.collect::<Vec<ModelRecord>>();
|
||||||
|
|
||||||
|
let data2 = data.clone();
|
||||||
|
|
||||||
|
let model = chat_root().join("chat.model.json");
|
||||||
|
let model_cmd = chat_root().join("chat.model.cmd.json");
|
||||||
|
let chatgpt_prompts = chat_root().join("cache_model").join("chatgpt_prompts.json");
|
||||||
|
|
||||||
|
if !exists(&model) {
|
||||||
|
fs::write(
|
||||||
|
&model,
|
||||||
|
serde_json::json!({
|
||||||
|
"name": "ChatGPT Model",
|
||||||
|
"link": "https://github.com/lencx/ChatGPT"
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// chatgpt_prompts.json
|
||||||
|
fs::write(
|
||||||
|
chatgpt_prompts,
|
||||||
|
serde_json::to_string_pretty(&data).unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let cmd_data = cmd_list();
|
||||||
|
|
||||||
|
// chat.model.cmd.json
|
||||||
|
fs::write(
|
||||||
|
model_cmd,
|
||||||
|
serde_json::to_string_pretty(&serde_json::json!({
|
||||||
|
"name": "ChatGPT CMD",
|
||||||
|
"last_updated": time,
|
||||||
|
"data": cmd_data,
|
||||||
|
}))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut kv = HashMap::new();
|
||||||
|
kv.insert(
|
||||||
|
"sync_prompts".to_string(),
|
||||||
|
serde_json::json!({ "id": "chatgpt_prompts", "last_updated": time }),
|
||||||
|
);
|
||||||
|
let model_data = utils::merge(
|
||||||
|
&serde_json::from_str(&fs::read_to_string(&model).unwrap()).unwrap(),
|
||||||
|
&kv,
|
||||||
|
);
|
||||||
|
|
||||||
|
// chat.model.json
|
||||||
|
fs::write(model, serde_json::to_string_pretty(&model_data).unwrap()).unwrap();
|
||||||
|
|
||||||
|
// refresh window
|
||||||
|
api::dialog::message(
|
||||||
|
app.get_window("core").as_ref(),
|
||||||
|
"Sync Prompts",
|
||||||
|
"ChatGPT Prompts data has been synchronized!",
|
||||||
|
);
|
||||||
|
window_reload(app, "core");
|
||||||
|
|
||||||
|
return Some(data2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// chatgpt_prompts.json
|
None
|
||||||
fs::write(
|
}
|
||||||
chatgpt_prompts,
|
|
||||||
serde_json::to_string_pretty(&data).unwrap(),
|
#[command]
|
||||||
)
|
pub async fn sync_user_prompts(url: String, data_type: String) -> Option<Vec<ModelRecord>> {
|
||||||
.unwrap();
|
let res = utils::get_data(&url, None).await.unwrap_or_else(|err| {
|
||||||
let cmd_data = cmd_list();
|
info!("chatgpt_http_error: {}", err);
|
||||||
|
None
|
||||||
// chat.model.cmd.json
|
});
|
||||||
fs::write(
|
|
||||||
model_cmd,
|
info!("chatgpt_http_url: {}", url);
|
||||||
serde_json::to_string_pretty(&serde_json::json!({
|
|
||||||
"name": "ChatGPT CMD",
|
if let Some(v) = res {
|
||||||
"last_updated": time,
|
let data;
|
||||||
"data": cmd_data,
|
if data_type == "csv" {
|
||||||
}))
|
info!("chatgpt_http_csv_parse");
|
||||||
.unwrap(),
|
data = parse_prompt(v);
|
||||||
)
|
} else if data_type == "json" {
|
||||||
.unwrap();
|
info!("chatgpt_http_json_parse");
|
||||||
let mut kv = HashMap::new();
|
data = serde_json::from_str(&v).unwrap_or_else(|err| {
|
||||||
kv.insert(
|
info!("chatgpt_http_json_parse_error: {}", err);
|
||||||
"sync_prompts".to_string(),
|
vec![]
|
||||||
serde_json::json!({ "id": "chatgpt_prompts", "last_updated": time }),
|
});
|
||||||
);
|
} else {
|
||||||
let model_data = utils::merge(
|
info!("chatgpt_http_unknown_type");
|
||||||
&serde_json::from_str(&fs::read_to_string(&model).unwrap()).unwrap(),
|
data = vec![];
|
||||||
&kv,
|
}
|
||||||
);
|
|
||||||
|
let data = data
|
||||||
// chat.model.json
|
.iter()
|
||||||
fs::write(model, serde_json::to_string_pretty(&model_data).unwrap()).unwrap();
|
.map(move |i| ModelRecord {
|
||||||
|
cmd: if i.cmd.is_some() {
|
||||||
// refresh window
|
i.cmd.clone().unwrap()
|
||||||
api::dialog::message(
|
} else {
|
||||||
app.get_window("core").as_ref(),
|
utils::gen_cmd(i.act.clone())
|
||||||
"Sync Prompts",
|
},
|
||||||
"ChatGPT Prompts data has been synchronized!",
|
act: i.act.clone(),
|
||||||
);
|
prompt: i.prompt.clone(),
|
||||||
window_reload(app, "core");
|
tags: vec!["user-sync".to_string()],
|
||||||
|
enable: true,
|
||||||
|
})
|
||||||
|
.collect::<Vec<ModelRecord>>();
|
||||||
|
|
||||||
|
return Some(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::{app::window, conf::ChatConfJson, utils};
|
use crate::{app::window, conf::ChatConfJson, utils};
|
||||||
use log::info;
|
use log::info;
|
||||||
use tauri::{utils::config::WindowUrl, window::WindowBuilder, App, GlobalShortcutManager, Manager};
|
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>> {
|
pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||||
info!("stepup");
|
info!("stepup");
|
||||||
@@ -13,30 +14,35 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
|
|||||||
window::tray_window(&handle);
|
window::tray_window(&handle);
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
if let Some(v) = chat_conf.global_shortcut {
|
||||||
info!("global_shortcut_start");
|
info!("global_shortcut: `{}`", v);
|
||||||
let handle = app.app_handle();
|
match v.parse::<Accelerator>() {
|
||||||
let mut shortcut = app.global_shortcut_manager();
|
Ok(_) => {
|
||||||
let core_shortcut = shortcut.is_registered("CmdOrCtrl+Shift+O");
|
info!("global_shortcut_register");
|
||||||
|
let handle = app.app_handle();
|
||||||
info!("is_registered: {}", core_shortcut.is_ok());
|
let mut shortcut = app.global_shortcut_manager();
|
||||||
|
shortcut
|
||||||
if core_shortcut.is_ok() {
|
.register(&v, move || {
|
||||||
shortcut
|
if let Some(w) = handle.get_window("core") {
|
||||||
.register("CmdOrCtrl+Shift+O", move || {
|
if w.is_visible().unwrap() {
|
||||||
if let Some(w) = handle.get_window("core") {
|
w.hide().unwrap();
|
||||||
if w.is_visible().unwrap() {
|
} else {
|
||||||
w.hide().unwrap();
|
w.show().unwrap();
|
||||||
} else {
|
w.set_focus().unwrap();
|
||||||
w.show().unwrap();
|
}
|
||||||
w.set_focus().unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
.unwrap_or_else(|err| {
|
||||||
.unwrap();
|
info!("global_shortcut_register_error: {}", err);
|
||||||
};
|
});
|
||||||
info!("global_shortcut_end");
|
}
|
||||||
}
|
Err(err) => {
|
||||||
|
info!("global_shortcut_parse_error: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("global_shortcut_unregister");
|
||||||
|
};
|
||||||
|
|
||||||
if chat_conf.hide_dock_icon {
|
if chat_conf.hide_dock_icon {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ pub fn control_window(handle: &tauri::AppHandle) {
|
|||||||
let app = handle.clone();
|
let app = handle.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
WindowBuilder::new(&app, "main", WindowUrl::App("index.html".into()))
|
WindowBuilder::new(&app, "main", WindowUrl::App("index.html".into()))
|
||||||
.title("ChatGPT")
|
.title("Control Center")
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.fullscreen(false)
|
.fullscreen(false)
|
||||||
.inner_size(800.0, 600.0)
|
.inner_size(800.0, 600.0)
|
||||||
|
|||||||
16
src-tauri/src/assets/core.js
vendored
16
src-tauri/src/assets/core.js
vendored
@@ -88,21 +88,7 @@ async function init() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.__sync_prompts = async function() {
|
window.__sync_prompts = async function() {
|
||||||
const res = await fetch('https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv');
|
await invoke('sync_prompts', { time: Date.now() });
|
||||||
if (res.ok) {
|
|
||||||
const data = await res.text();
|
|
||||||
await invoke('sync_prompts', { data, time: Date.now() });
|
|
||||||
} else {
|
|
||||||
invoke('messageDialog', {
|
|
||||||
__tauriModule: 'Dialog',
|
|
||||||
message: {
|
|
||||||
cmd: 'messageDialog',
|
|
||||||
message: 'ChatGPT Prompts data sync failed, please try again!'.toString(),
|
|
||||||
title: 'Sync Prompts'.toString(),
|
|
||||||
type: 'error'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,13 @@ use tauri::TitleBarStyle;
|
|||||||
pub const ISSUES_URL: &str = "https://github.com/lencx/ChatGPT/issues";
|
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 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 AWESOME_URL: &str = "https://github.com/lencx/ChatGPT/blob/main/AWESOME.md";
|
||||||
|
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#"{
|
pub const DEFAULT_CHAT_CONF: &str = r#"{
|
||||||
"stay_on_top": false,
|
"stay_on_top": false,
|
||||||
"theme": "Light",
|
"theme": "Light",
|
||||||
"titlebar": true,
|
"titlebar": true,
|
||||||
|
"global_shortcut": "",
|
||||||
"hide_dock_icon": false,
|
"hide_dock_icon": false,
|
||||||
"default_origin": "https://chat.openai.com",
|
"default_origin": "https://chat.openai.com",
|
||||||
"origin": "https://chat.openai.com",
|
"origin": "https://chat.openai.com",
|
||||||
@@ -28,6 +31,7 @@ pub const DEFAULT_CHAT_CONF_MAC: &str = r#"{
|
|||||||
"stay_on_top": false,
|
"stay_on_top": false,
|
||||||
"theme": "Light",
|
"theme": "Light",
|
||||||
"titlebar": false,
|
"titlebar": false,
|
||||||
|
"global_shortcut": "",
|
||||||
"hide_dock_icon": false,
|
"hide_dock_icon": false,
|
||||||
"default_origin": "https://chat.openai.com",
|
"default_origin": "https://chat.openai.com",
|
||||||
"origin": "https://chat.openai.com",
|
"origin": "https://chat.openai.com",
|
||||||
@@ -61,6 +65,7 @@ pub struct ChatConfJson {
|
|||||||
pub origin: String,
|
pub origin: String,
|
||||||
pub ua_window: String,
|
pub ua_window: String,
|
||||||
pub ua_tray: String,
|
pub ua_tray: String,
|
||||||
|
pub global_shortcut: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChatConfJson {
|
impl ChatConfJson {
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ async fn main() {
|
|||||||
cmd::get_chat_model_cmd,
|
cmd::get_chat_model_cmd,
|
||||||
cmd::parse_prompt,
|
cmd::parse_prompt,
|
||||||
cmd::sync_prompts,
|
cmd::sync_prompts,
|
||||||
|
cmd::sync_user_prompts,
|
||||||
cmd::window_reload,
|
cmd::window_reload,
|
||||||
cmd::cmd_list,
|
cmd::cmd_list,
|
||||||
fs_extra::metadata,
|
fs_extra::metadata,
|
||||||
|
|||||||
@@ -109,3 +109,22 @@ pub fn gen_cmd(name: String) -> String {
|
|||||||
let re = Regex::new(r"[^a-zA-Z0-9]").unwrap();
|
let re = Regex::new(r"[^a-zA-Z0-9]").unwrap();
|
||||||
re.replace_all(&name, "_").to_lowercase()
|
re.replace_all(&name, "_").to_lowercase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_data(
|
||||||
|
url: &str,
|
||||||
|
app: Option<&tauri::AppHandle>,
|
||||||
|
) -> Result<Option<String>, reqwest::Error> {
|
||||||
|
let res = reqwest::get(url).await?;
|
||||||
|
let is_ok = res.status() == 200;
|
||||||
|
let body = res.text().await?;
|
||||||
|
|
||||||
|
if is_ok {
|
||||||
|
Ok(Some(body))
|
||||||
|
} else {
|
||||||
|
info!("chatgpt_http_error: {}", body);
|
||||||
|
if let Some(v) = app {
|
||||||
|
tauri::api::dialog::message(v.get_window("core").as_ref(), "ChatGPT HTTP", body);
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "ChatGPT",
|
"productName": "ChatGPT",
|
||||||
"version": "0.7.3"
|
"version": "0.7.4"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
@@ -15,13 +15,6 @@
|
|||||||
"globalShortcut": {
|
"globalShortcut": {
|
||||||
"all": true
|
"all": true
|
||||||
},
|
},
|
||||||
"http": {
|
|
||||||
"all": true,
|
|
||||||
"scope": [
|
|
||||||
"https://**",
|
|
||||||
"http://**"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"fs": {
|
"fs": {
|
||||||
"all": true,
|
"all": true,
|
||||||
"scope": [
|
"scope": [
|
||||||
|
|||||||
120
src/view/General.tsx
vendored
120
src/view/General.tsx
vendored
@@ -1,36 +1,54 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Form, Radio, Switch, Input, Button, Space, message, Tooltip } from 'antd';
|
import { Form, Radio, Switch, Input, Button, Space, message, Tooltip } from 'antd';
|
||||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
import { invoke } from '@tauri-apps/api';
|
import { invoke, shell, path } from '@tauri-apps/api';
|
||||||
import { platform } from '@tauri-apps/api/os';
|
import { platform } from '@tauri-apps/api/os';
|
||||||
import { ask } from '@tauri-apps/api/dialog';
|
import { ask } from '@tauri-apps/api/dialog';
|
||||||
import { relaunch } from '@tauri-apps/api/process';
|
import { relaunch } from '@tauri-apps/api/process';
|
||||||
import { clone, omit, isEqual } from 'lodash';
|
import { clone, omit, isEqual } from 'lodash';
|
||||||
|
|
||||||
import { DISABLE_AUTO_COMPLETE } from '@/utils';
|
import useInit from '@/hooks/useInit';
|
||||||
|
import { DISABLE_AUTO_COMPLETE, chatRoot } from '@/utils';
|
||||||
|
|
||||||
const OriginLabel = ({ url }: { url: string }) => {
|
const OriginLabel = ({ url }: { url: string }) => {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
Switch Origin <Tooltip title={`Default: ${url}`}><QuestionCircleOutlined /></Tooltip>
|
Switch Origin <Tooltip title={`Default: ${url}`}><QuestionCircleOutlined style={{ color: '#1677ff' }} /></Tooltip>
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GlobalShortcut = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Global Shortcut
|
||||||
|
{' '}
|
||||||
|
<Tooltip title={(
|
||||||
|
<div>
|
||||||
|
<div>Shortcut definition, modifiers and key separated by "+" e.g. CmdOrControl+Q</div>
|
||||||
|
<div style={{ margin: '10px 0'}}>If empty, the shortcut is disabled.</div>
|
||||||
|
<a href="https://tauri.app/v1/api/js/globalshortcut" target="_blank">https://tauri.app/v1/api/js/globalshortcut</a>
|
||||||
|
</div>
|
||||||
|
)}>
|
||||||
|
<QuestionCircleOutlined style={{ color: '#1677ff' }} />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function General() {
|
export default function General() {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const [jsonPath, setJsonPath] = useState('');
|
||||||
const [platformInfo, setPlatform] = useState<string>('');
|
const [platformInfo, setPlatform] = useState<string>('');
|
||||||
const [chatConf, setChatConf] = useState<any>(null);
|
const [chatConf, setChatConf] = useState<any>(null);
|
||||||
|
|
||||||
const init = async () => {
|
useInit(async () => {
|
||||||
|
setJsonPath(await path.join(await chatRoot(), 'chat.conf.json'));
|
||||||
|
|
||||||
setPlatform(await platform());
|
setPlatform(await platform());
|
||||||
const chatData = await invoke('get_chat_conf');
|
const chatData = await invoke('get_chat_conf');
|
||||||
setChatConf(chatData);
|
setChatConf(chatData);
|
||||||
}
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
init();
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form.setFieldsValue(clone(chatConf));
|
form.setFieldsValue(clone(chatConf));
|
||||||
@@ -55,44 +73,54 @@ export default function General() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<>
|
||||||
form={form}
|
<div className="chat-table-tip">
|
||||||
style={{ maxWidth: 500 }}
|
<div className="chat-sync-path">
|
||||||
onFinish={onFinish}
|
<div>PATH: <a onClick={() => shell.open(jsonPath)} title={jsonPath}>{jsonPath}</a></div>
|
||||||
labelCol={{ span: 8 }}
|
</div>
|
||||||
wrapperCol={{ span: 15, offset: 1 }}
|
</div>
|
||||||
>
|
<Form
|
||||||
<Form.Item label="Theme" name="theme">
|
form={form}
|
||||||
<Radio.Group>
|
style={{ maxWidth: 500 }}
|
||||||
<Radio value="Light">Light</Radio>
|
onFinish={onFinish}
|
||||||
<Radio value="Dark">Dark</Radio>
|
labelCol={{ span: 8 }}
|
||||||
</Radio.Group>
|
wrapperCol={{ span: 15, offset: 1 }}
|
||||||
</Form.Item>
|
>
|
||||||
<Form.Item label="Stay On Top" name="stay_on_top" valuePropName="checked">
|
<Form.Item label="Theme" name="theme">
|
||||||
<Switch />
|
<Radio.Group>
|
||||||
</Form.Item>
|
<Radio value="Light">Light</Radio>
|
||||||
{platformInfo === 'darwin' && (
|
<Radio value="Dark">Dark</Radio>
|
||||||
<Form.Item label="Titlebar" name="titlebar" valuePropName="checked">
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Stay On Top" name="stay_on_top" valuePropName="checked">
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
<Form.Item label={<GlobalShortcut />} name="global_shortcut">
|
||||||
<Form.Item label={<OriginLabel url={chatConf?.default_origin} />} name="origin">
|
<Input placeholder="CmdOrCtrl+Shift+O" {...DISABLE_AUTO_COMPLETE} />
|
||||||
<Input placeholder="https://chat.openai.com" {...DISABLE_AUTO_COMPLETE} />
|
</Form.Item>
|
||||||
</Form.Item>
|
{platformInfo === 'darwin' && (
|
||||||
<Form.Item label="User Agent (Window)" name="ua_window">
|
<Form.Item label="Titlebar" name="titlebar" valuePropName="checked">
|
||||||
<Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} {...DISABLE_AUTO_COMPLETE} 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" />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="User Agent (SystemTray)" name="ua_tray">
|
)}
|
||||||
<Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} {...DISABLE_AUTO_COMPLETE} 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 label={<OriginLabel url={chatConf?.default_origin} />} name="origin">
|
||||||
</Form.Item>
|
<Input placeholder="https://chat.openai.com" {...DISABLE_AUTO_COMPLETE} />
|
||||||
<Form.Item>
|
</Form.Item>
|
||||||
<Space size={20}>
|
<Form.Item label="User Agent (Window)" name="ua_window">
|
||||||
<Button onClick={onCancel}>Cancel</Button>
|
<Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} {...DISABLE_AUTO_COMPLETE} 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" />
|
||||||
<Button type="primary" htmlType="submit">
|
</Form.Item>
|
||||||
Submit
|
<Form.Item label="User Agent (SystemTray)" name="ua_tray">
|
||||||
</Button>
|
<Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} {...DISABLE_AUTO_COMPLETE} 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" />
|
||||||
</Space>
|
</Form.Item>
|
||||||
</Form.Item>
|
<Form.Item>
|
||||||
</Form>
|
<Space size={20}>
|
||||||
|
<Button onClick={onCancel}>Cancel</Button>
|
||||||
|
<Button type="primary" htmlType="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
47
src/view/model/SyncCustom/index.tsx
vendored
47
src/view/model/SyncCustom/index.tsx
vendored
@@ -1,6 +1,6 @@
|
|||||||
import { useState, useRef, useEffect } from 'react';
|
import { useState, useRef, useEffect } from 'react';
|
||||||
import { Table, Modal, Button, message } from 'antd';
|
import { Table, Modal, Button, message } from 'antd';
|
||||||
import { invoke, http, path, fs } from '@tauri-apps/api';
|
import { invoke, path, fs } from '@tauri-apps/api';
|
||||||
|
|
||||||
import useData from '@/hooks/useData';
|
import useData from '@/hooks/useData';
|
||||||
import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
|
import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
|
||||||
@@ -10,7 +10,7 @@ import { CHAT_MODEL_JSON, chatRoot, readJSON, genCmd } from '@/utils';
|
|||||||
import { syncColumns, getPath } from './config';
|
import { syncColumns, getPath } from './config';
|
||||||
import SyncForm from './Form';
|
import SyncForm from './Form';
|
||||||
|
|
||||||
const setTag = (data: Record<string, any>[]) => data.map((i) => ({ ...i, tags: ['user-sync'], enable: true }))
|
const fmtData = (data: Record<string, any>[] = []) => (Array.isArray(data) ? data : []).map((i) => ({ ...i, cmd: i.cmd ? i.cmd : genCmd(i.act), tags: ['user-sync'], enable: true }));
|
||||||
|
|
||||||
export default function SyncCustom() {
|
export default function SyncCustom() {
|
||||||
const [isVisible, setVisible] = useState(false);
|
const [isVisible, setVisible] = useState(false);
|
||||||
@@ -34,7 +34,9 @@ export default function SyncCustom() {
|
|||||||
if (!opInfo.opType) return;
|
if (!opInfo.opType) return;
|
||||||
if (opInfo.opType === 'sync') {
|
if (opInfo.opType === 'sync') {
|
||||||
const filename = `${opInfo?.opRecord?.id}.json`;
|
const filename = `${opInfo?.opRecord?.id}.json`;
|
||||||
handleSync(filename).then(() => {
|
handleSync(filename).then((isOk: boolean) => {
|
||||||
|
opInfo.resetRecord();
|
||||||
|
if (!isOk) return;
|
||||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], { ...opInfo?.opRecord, last_updated: Date.now() });
|
const data = opReplace(opInfo?.opRecord?.[opSafeKey], { ...opInfo?.opRecord, last_updated: Date.now() });
|
||||||
modelSet(data);
|
modelSet(data);
|
||||||
opInfo.resetRecord();
|
opInfo.resetRecord();
|
||||||
@@ -44,9 +46,16 @@ export default function SyncCustom() {
|
|||||||
setVisible(true);
|
setVisible(true);
|
||||||
}
|
}
|
||||||
if (['delete'].includes(opInfo.opType)) {
|
if (['delete'].includes(opInfo.opType)) {
|
||||||
const data = opRemove(opInfo?.opRecord?.[opSafeKey]);
|
(async () => {
|
||||||
modelSet(data);
|
try {
|
||||||
opInfo.resetRecord();
|
const file = await path.join(await chatRoot(), 'cache_model', `${opInfo?.opRecord?.id}.json`);
|
||||||
|
await fs.removeFile(file);
|
||||||
|
} catch(e) {}
|
||||||
|
const data = opRemove(opInfo?.opRecord?.[opSafeKey]);
|
||||||
|
modelSet(data);
|
||||||
|
opInfo.resetRecord();
|
||||||
|
modelCacheCmd();
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
}, [opInfo.opType, formRef]);
|
}, [opInfo.opType, formRef]);
|
||||||
|
|
||||||
@@ -58,40 +67,30 @@ export default function SyncCustom() {
|
|||||||
|
|
||||||
// https or http
|
// https or http
|
||||||
if (/^http/.test(record?.protocol)) {
|
if (/^http/.test(record?.protocol)) {
|
||||||
const res = await http.fetch(filePath, {
|
const data = await invoke('sync_user_prompts', { url: filePath, dataType: record?.ext });
|
||||||
method: 'GET',
|
if (data) {
|
||||||
responseType: isJson ? 1 : 2,
|
await modelCacheSet(data as [], file);
|
||||||
});
|
|
||||||
if (res.ok) {
|
|
||||||
if (isJson) {
|
|
||||||
// parse json
|
|
||||||
await modelCacheSet(setTag(Array.isArray(res?.data) ? res?.data : []), file);
|
|
||||||
} else {
|
|
||||||
// parse csv
|
|
||||||
const list: Record<string, string>[] = await invoke('parse_prompt', { data: res?.data });
|
|
||||||
const fmtList = list.map(i => ({ ...i, cmd: i.cmd ? i.cmd : genCmd(i.act), enable: true, tags: ['user-sync'] }));
|
|
||||||
await modelCacheSet(fmtList, file);
|
|
||||||
}
|
|
||||||
await modelCacheCmd();
|
await modelCacheCmd();
|
||||||
message.success('ChatGPT Prompts data has been synchronized!');
|
message.success('ChatGPT Prompts data has been synchronized!');
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
message.error('ChatGPT Prompts data sync failed, please try again!');
|
message.error('ChatGPT Prompts data sync failed, please try again!');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// local
|
// local
|
||||||
if (isJson) {
|
if (isJson) {
|
||||||
// parse json
|
// parse json
|
||||||
const data = await readJSON(filePath, { isRoot: true });
|
const data = await readJSON(filePath, { isRoot: true });
|
||||||
await modelCacheSet(setTag(Array.isArray(data) ? data : []), file);
|
await modelCacheSet(fmtData(data), file);
|
||||||
} else {
|
} else {
|
||||||
// parse csv
|
// parse csv
|
||||||
const data = await fs.readTextFile(filePath);
|
const data = await fs.readTextFile(filePath);
|
||||||
const list: Record<string, string>[] = await invoke('parse_prompt', { data });
|
const list: Record<string, string>[] = await invoke('parse_prompt', { data });
|
||||||
const fmtList = list.map(i => ({ ...i, cmd: i.cmd ? i.cmd : genCmd(i.act), enable: true, tags: ['user-sync'] }));
|
await modelCacheSet(fmtData(list), file);
|
||||||
await modelCacheSet(fmtList, file);
|
|
||||||
}
|
}
|
||||||
await modelCacheCmd();
|
await modelCacheCmd();
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
|
|||||||
23
src/view/model/SyncPrompts/index.tsx
vendored
23
src/view/model/SyncPrompts/index.tsx
vendored
@@ -1,13 +1,13 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Table, Button, message, Popconfirm } from 'antd';
|
import { Table, Button, Popconfirm } from 'antd';
|
||||||
import { invoke, http, path, shell } from '@tauri-apps/api';
|
import { invoke, path, shell } from '@tauri-apps/api';
|
||||||
|
|
||||||
import useInit from '@/hooks/useInit';
|
import useInit from '@/hooks/useInit';
|
||||||
import useData from '@/hooks/useData';
|
import useData from '@/hooks/useData';
|
||||||
import useColumns from '@/hooks/useColumns';
|
import useColumns from '@/hooks/useColumns';
|
||||||
import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
|
import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
|
||||||
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
|
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
|
||||||
import { fmtDate, chatRoot, GITHUB_PROMPTS_CSV_URL, genCmd } from '@/utils';
|
import { fmtDate, chatRoot } from '@/utils';
|
||||||
import { syncColumns } from './config';
|
import { syncColumns } from './config';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
@@ -33,24 +33,13 @@ export default function SyncPrompts() {
|
|||||||
}, [modelCacheJson.length]);
|
}, [modelCacheJson.length]);
|
||||||
|
|
||||||
const handleSync = async () => {
|
const handleSync = async () => {
|
||||||
const res = await http.fetch(GITHUB_PROMPTS_CSV_URL, {
|
const data = await invoke('sync_prompts', { time: Date.now() });
|
||||||
method: 'GET',
|
if (data) {
|
||||||
responseType: http.ResponseType.Text,
|
opInit(data as any[]);
|
||||||
});
|
|
||||||
const data = (res.data || '') as string;
|
|
||||||
if (res.ok) {
|
|
||||||
// const content = data.replace(/"(\s+)?,(\s+)?"/g, '","');
|
|
||||||
const list: Record<string, string>[] = await invoke('parse_prompt', { data });
|
|
||||||
const fmtList = list.map(i => ({ ...i, cmd: i.cmd ? i.cmd : genCmd(i.act), enable: true, tags: ['chatgpt-prompts'] }));
|
|
||||||
await modelCacheSet(fmtList);
|
|
||||||
opInit(fmtList);
|
|
||||||
modelSet({
|
modelSet({
|
||||||
id: 'chatgpt_prompts',
|
id: 'chatgpt_prompts',
|
||||||
last_updated: Date.now(),
|
last_updated: Date.now(),
|
||||||
});
|
});
|
||||||
message.success('ChatGPT Prompts data has been synchronized!');
|
|
||||||
} else {
|
|
||||||
message.error('ChatGPT Prompts data sync failed, please try again!');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
4
src/view/model/SyncRecord/config.tsx
vendored
4
src/view/model/SyncRecord/config.tsx
vendored
@@ -24,7 +24,9 @@ export const syncColumns = () => [
|
|||||||
dataIndex: 'tags',
|
dataIndex: 'tags',
|
||||||
key: 'tags',
|
key: 'tags',
|
||||||
// width: 150,
|
// width: 150,
|
||||||
render: () => <Tag>chatgpt-prompts</Tag>,
|
render: (v: string[]) => (
|
||||||
|
<span className="chat-prompts-tags">{v?.map(i => <Tag key={i}>{i}</Tag>)}</span>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Enable',
|
title: 'Enable',
|
||||||
|
|||||||
Reference in New Issue
Block a user