Compare commits

...

8 Commits

Author SHA1 Message Date
lencx
dc769235a7 v0.7.4 2022-12-31 20:11:21 +08:00
lencx
52cc029b01 readme 2022-12-31 20:11:15 +08:00
lencx
39dc007513 readme 2022-12-31 20:08:11 +08:00
lencx
ba1fe9a603 refactor: global shortcut 2022-12-31 20:07:09 +08:00
lencx
e1f8030009 refactor: prompts sync 2022-12-31 18:01:31 +08:00
lencx
9a392a71f6 chore: conf path 2022-12-31 12:43:47 +08:00
lencx
dc0c78fee2 fix: customize global shortcuts (#108) 2022-12-30 23:46:30 +08:00
lencx
b3bd54ce81 Merge pull request #101 from lencx/dev 2022-12-29 21:31:43 +08:00
16 changed files with 315 additions and 212 deletions

View File

@@ -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))
### 菜单项 ### 菜单项

View File

@@ -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

View File

@@ -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:

View File

@@ -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"

View File

@@ -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
} }

View File

@@ -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")]

View File

@@ -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)

View File

@@ -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'
}
})
}
} }
} }

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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)
}
}

View File

@@ -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
View File

@@ -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>
</>
) )
} }

View File

@@ -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 = () => {

View File

@@ -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!');
} }
}; };

View File

@@ -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',