mirror of
https://github.com/FranP-code/ChatGPT.git
synced 2025-10-13 00:13:25 +00:00
chore: dashboard
This commit is contained in:
@@ -1,29 +1,16 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::{fs_extra, window},
|
conf::ChatConfJson,
|
||||||
conf::{ChatConfJson, GITHUB_PROMPTS_CSV_URL},
|
|
||||||
utils::{self, chat_root, create_file},
|
utils::{self, chat_root, create_file},
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use regex::Regex;
|
use std::{fs, path::PathBuf};
|
||||||
use std::{collections::HashMap, fs, path::PathBuf, vec};
|
|
||||||
use tauri::{api, command, AppHandle, Manager, Theme};
|
use tauri::{api, command, AppHandle, Manager, Theme};
|
||||||
use walkdir::WalkDir;
|
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub fn drag_window(app: AppHandle) {
|
pub fn drag_window(app: AppHandle) {
|
||||||
app.get_window("core").unwrap().start_dragging().unwrap();
|
app.get_window("core").unwrap().start_dragging().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
|
||||||
pub fn dalle2_window(app: AppHandle, query: String) {
|
|
||||||
window::dalle2_window(
|
|
||||||
&app.app_handle(),
|
|
||||||
Some(query),
|
|
||||||
Some("ChatGPT & DALL·E 2".to_string()),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub fn fullscreen(app: AppHandle) {
|
pub fn fullscreen(app: AppHandle) {
|
||||||
let win = app.get_window("core").unwrap();
|
let win = app.get_window("core").unwrap();
|
||||||
@@ -61,13 +48,13 @@ pub fn get_chat_conf() -> ChatConfJson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub fn get_theme() -> String {
|
pub fn reset_chat_conf() -> ChatConfJson {
|
||||||
ChatConfJson::theme().unwrap_or(Theme::Light).to_string()
|
ChatConfJson::reset_chat_conf()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub fn reset_chat_conf() -> ChatConfJson {
|
pub fn get_theme() -> String {
|
||||||
ChatConfJson::reset_chat_conf()
|
ChatConfJson::theme().unwrap_or(Theme::Light).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
@@ -106,244 +93,6 @@ pub fn open_file(path: PathBuf) {
|
|||||||
utils::open_file(path);
|
utils::open_file(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
|
||||||
pub fn get_chat_model_cmd() -> serde_json::Value {
|
|
||||||
let path = utils::chat_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()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct PromptRecord {
|
|
||||||
pub cmd: Option<String>,
|
|
||||||
pub act: String,
|
|
||||||
pub prompt: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
pub fn parse_prompt(data: String) -> Vec<PromptRecord> {
|
|
||||||
let mut rdr = csv::Reader::from_reader(data.as_bytes());
|
|
||||||
let mut list = vec![];
|
|
||||||
for result in rdr.deserialize() {
|
|
||||||
let record: PromptRecord = result.unwrap_or_else(|err| {
|
|
||||||
info!("parse_prompt_error: {}", err);
|
|
||||||
PromptRecord {
|
|
||||||
cmd: None,
|
|
||||||
act: "".to_string(),
|
|
||||||
prompt: "".to_string(),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if !record.act.is_empty() {
|
|
||||||
list.push(record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
list
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
|
||||||
pub struct ModelRecord {
|
|
||||||
pub cmd: String,
|
|
||||||
pub act: String,
|
|
||||||
pub prompt: String,
|
|
||||||
pub tags: Vec<String>,
|
|
||||||
pub enable: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
pub fn cmd_list() -> Vec<ModelRecord> {
|
|
||||||
let mut list = vec![];
|
|
||||||
for entry in WalkDir::new(utils::chat_root().join("cache_model"))
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|e| e.ok())
|
|
||||||
{
|
|
||||||
let file = fs::read_to_string(entry.path().display().to_string());
|
|
||||||
if let Ok(v) = file {
|
|
||||||
let data: Vec<ModelRecord> = serde_json::from_str(&v).unwrap_or_else(|_| vec![]);
|
|
||||||
let enable_list = data.into_iter().filter(|v| v.enable);
|
|
||||||
list.extend(enable_list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// dbg!(&list);
|
|
||||||
list.sort_by(|a, b| a.cmd.len().cmp(&b.cmd.len()));
|
|
||||||
list
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
|
||||||
pub struct FileMetadata {
|
|
||||||
pub name: String,
|
|
||||||
pub ext: String,
|
|
||||||
pub created: u64,
|
|
||||||
pub id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 content = fs::read_to_string(&download_path).unwrap_or_else(|err| {
|
|
||||||
info!("download_list_error: {}", 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);
|
|
||||||
vec![]
|
|
||||||
});
|
|
||||||
|
|
||||||
(list, download_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
pub fn download_list(pathname: &str, dir: &str, filename: Option<String>, id: Option<String>) {
|
|
||||||
info!("download_list: {}", pathname);
|
|
||||||
let data = get_download_list(pathname);
|
|
||||||
let mut list = vec![];
|
|
||||||
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))
|
|
||||||
.into_iter()
|
|
||||||
.filter_entry(|e| !utils::is_hidden(e))
|
|
||||||
.filter_map(|e| e.ok())
|
|
||||||
{
|
|
||||||
let metadata = entry.metadata().unwrap();
|
|
||||||
if metadata.is_file() {
|
|
||||||
let file_path = entry.path().display().to_string();
|
|
||||||
let re = Regex::new(r"(?P<id>[\d\w]+).(?P<ext>\w+)$").unwrap();
|
|
||||||
let caps = re.captures(&file_path).unwrap();
|
|
||||||
let fid = &caps["id"];
|
|
||||||
let fext = &caps["ext"];
|
|
||||||
|
|
||||||
let mut file_data = FileMetadata {
|
|
||||||
name: fid.to_string(),
|
|
||||||
id: fid.to_string(),
|
|
||||||
ext: fext.to_string(),
|
|
||||||
created: fs_extra::system_time_to_ms(metadata.created()),
|
|
||||||
};
|
|
||||||
|
|
||||||
if idmap.get(fid).is_some() {
|
|
||||||
let name = idmap.get(fid).unwrap().get("name").unwrap().clone();
|
|
||||||
match name {
|
|
||||||
serde_json::Value::String(v) => {
|
|
||||||
file_data.name = v.clone();
|
|
||||||
v
|
|
||||||
}
|
|
||||||
_ => "".to_string(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if filename.is_some() && id.is_some() {
|
|
||||||
if let Some(ref v) = id {
|
|
||||||
if fid == v {
|
|
||||||
if let Some(ref v2) = filename {
|
|
||||||
file_data.name = v2.to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
list.push(serde_json::to_value(file_data).unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dbg!(&list);
|
|
||||||
list.sort_by(|a, b| {
|
|
||||||
let a1 = a.get("created").unwrap().as_u64().unwrap();
|
|
||||||
let b1 = b.get("created").unwrap().as_u64().unwrap();
|
|
||||||
a1.cmp(&b1).reverse()
|
|
||||||
});
|
|
||||||
|
|
||||||
fs::write(data.1, serde_json::to_string_pretty(&list).unwrap()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
pub async fn sync_prompts(app: AppHandle, time: u64) -> Option<Vec<ModelRecord>> {
|
|
||||||
let res = utils::get_data(GITHUB_PROMPTS_CSV_URL, Some(&app))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if let Some(v) = res {
|
|
||||||
let data = parse_prompt(v)
|
|
||||||
.iter()
|
|
||||||
.map(move |i| ModelRecord {
|
|
||||||
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 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()
|
|
||||||
.join("cache_model")
|
|
||||||
.join("chatgpt_prompts.json");
|
|
||||||
|
|
||||||
if !utils::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();
|
|
||||||
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::window_reload(app.clone(), "core");
|
|
||||||
window::window_reload(app, "tray");
|
|
||||||
|
|
||||||
return Some(data2);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub async fn get_data(app: AppHandle, url: String, is_msg: Option<bool>) -> Option<String> {
|
pub async fn get_data(app: AppHandle, url: String, is_msg: Option<bool>) -> Option<String> {
|
||||||
let is_msg = is_msg.unwrap_or(false);
|
let is_msg = is_msg.unwrap_or(false);
|
||||||
@@ -357,49 +106,3 @@ pub async fn get_data(app: AppHandle, url: String, is_msg: Option<bool>) -> Opti
|
|||||||
None
|
None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
|
||||||
pub async fn sync_user_prompts(url: String, data_type: String) -> Option<Vec<ModelRecord>> {
|
|
||||||
let res = utils::get_data(&url, None).await.unwrap_or_else(|err| {
|
|
||||||
info!("chatgpt_http_error: {}", err);
|
|
||||||
None
|
|
||||||
});
|
|
||||||
|
|
||||||
info!("chatgpt_http_url: {}", url);
|
|
||||||
|
|
||||||
if let Some(v) = res {
|
|
||||||
let data;
|
|
||||||
if data_type == "csv" {
|
|
||||||
info!("chatgpt_http_csv_parse");
|
|
||||||
data = parse_prompt(v);
|
|
||||||
} 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);
|
|
||||||
vec![]
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
info!("chatgpt_http_unknown_type");
|
|
||||||
data = vec![];
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = data
|
|
||||||
.iter()
|
|
||||||
.map(move |i| ModelRecord {
|
|
||||||
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!["user-sync".to_string()],
|
|
||||||
enable: true,
|
|
||||||
})
|
|
||||||
.collect::<Vec<ModelRecord>>();
|
|
||||||
|
|
||||||
return Some(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|||||||
294
src-tauri/src/app/gpt.rs
Normal file
294
src-tauri/src/app/gpt.rs
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
use crate::{
|
||||||
|
app::{fs_extra, window},
|
||||||
|
conf::GITHUB_PROMPTS_CSV_URL,
|
||||||
|
utils::{self, chat_root},
|
||||||
|
};
|
||||||
|
use log::info;
|
||||||
|
use regex::Regex;
|
||||||
|
use std::{collections::HashMap, fs, path::PathBuf, vec};
|
||||||
|
use tauri::{api, command, AppHandle, Manager};
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub fn get_chat_model_cmd() -> serde_json::Value {
|
||||||
|
let path = utils::chat_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()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct PromptRecord {
|
||||||
|
pub cmd: Option<String>,
|
||||||
|
pub act: String,
|
||||||
|
pub prompt: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub fn parse_prompt(data: String) -> Vec<PromptRecord> {
|
||||||
|
let mut rdr = csv::Reader::from_reader(data.as_bytes());
|
||||||
|
let mut list = vec![];
|
||||||
|
for result in rdr.deserialize() {
|
||||||
|
let record: PromptRecord = result.unwrap_or_else(|err| {
|
||||||
|
info!("parse_prompt_error: {}", err);
|
||||||
|
PromptRecord {
|
||||||
|
cmd: None,
|
||||||
|
act: "".to_string(),
|
||||||
|
prompt: "".to_string(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if !record.act.is_empty() {
|
||||||
|
list.push(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||||
|
pub struct ModelRecord {
|
||||||
|
pub cmd: String,
|
||||||
|
pub act: String,
|
||||||
|
pub prompt: String,
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub fn cmd_list() -> Vec<ModelRecord> {
|
||||||
|
let mut list = vec![];
|
||||||
|
for entry in WalkDir::new(utils::chat_root().join("cache_model"))
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
{
|
||||||
|
let file = fs::read_to_string(entry.path().display().to_string());
|
||||||
|
if let Ok(v) = file {
|
||||||
|
let data: Vec<ModelRecord> = serde_json::from_str(&v).unwrap_or_else(|_| vec![]);
|
||||||
|
let enable_list = data.into_iter().filter(|v| v.enable);
|
||||||
|
list.extend(enable_list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dbg!(&list);
|
||||||
|
list.sort_by(|a, b| a.cmd.len().cmp(&b.cmd.len()));
|
||||||
|
list
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||||
|
pub struct FileMetadata {
|
||||||
|
pub name: String,
|
||||||
|
pub ext: String,
|
||||||
|
pub created: u64,
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 content = fs::read_to_string(&download_path).unwrap_or_else(|err| {
|
||||||
|
info!("download_list_error: {}", 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);
|
||||||
|
vec![]
|
||||||
|
});
|
||||||
|
|
||||||
|
(list, download_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub fn download_list(pathname: &str, dir: &str, filename: Option<String>, id: Option<String>) {
|
||||||
|
info!("download_list: {}", pathname);
|
||||||
|
let data = get_download_list(pathname);
|
||||||
|
let mut list = vec![];
|
||||||
|
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))
|
||||||
|
.into_iter()
|
||||||
|
.filter_entry(|e| !utils::is_hidden(e))
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
{
|
||||||
|
let metadata = entry.metadata().unwrap();
|
||||||
|
if metadata.is_file() {
|
||||||
|
let file_path = entry.path().display().to_string();
|
||||||
|
let re = Regex::new(r"(?P<id>[\d\w]+).(?P<ext>\w+)$").unwrap();
|
||||||
|
let caps = re.captures(&file_path).unwrap();
|
||||||
|
let fid = &caps["id"];
|
||||||
|
let fext = &caps["ext"];
|
||||||
|
|
||||||
|
let mut file_data = FileMetadata {
|
||||||
|
name: fid.to_string(),
|
||||||
|
id: fid.to_string(),
|
||||||
|
ext: fext.to_string(),
|
||||||
|
created: fs_extra::system_time_to_ms(metadata.created()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if idmap.get(fid).is_some() {
|
||||||
|
let name = idmap.get(fid).unwrap().get("name").unwrap().clone();
|
||||||
|
match name {
|
||||||
|
serde_json::Value::String(v) => {
|
||||||
|
file_data.name = v.clone();
|
||||||
|
v
|
||||||
|
}
|
||||||
|
_ => "".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if filename.is_some() && id.is_some() {
|
||||||
|
if let Some(ref v) = id {
|
||||||
|
if fid == v {
|
||||||
|
if let Some(ref v2) = filename {
|
||||||
|
file_data.name = v2.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list.push(serde_json::to_value(file_data).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbg!(&list);
|
||||||
|
list.sort_by(|a, b| {
|
||||||
|
let a1 = a.get("created").unwrap().as_u64().unwrap();
|
||||||
|
let b1 = b.get("created").unwrap().as_u64().unwrap();
|
||||||
|
a1.cmp(&b1).reverse()
|
||||||
|
});
|
||||||
|
|
||||||
|
fs::write(data.1, serde_json::to_string_pretty(&list).unwrap()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub async fn sync_prompts(app: AppHandle, time: u64) -> Option<Vec<ModelRecord>> {
|
||||||
|
let res = utils::get_data(GITHUB_PROMPTS_CSV_URL, Some(&app))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if let Some(v) = res {
|
||||||
|
let data = parse_prompt(v)
|
||||||
|
.iter()
|
||||||
|
.map(move |i| ModelRecord {
|
||||||
|
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 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()
|
||||||
|
.join("cache_model")
|
||||||
|
.join("chatgpt_prompts.json");
|
||||||
|
|
||||||
|
if !utils::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();
|
||||||
|
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::window_reload(app.clone(), "core");
|
||||||
|
window::window_reload(app, "tray");
|
||||||
|
|
||||||
|
return Some(data2);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub async fn sync_user_prompts(url: String, data_type: String) -> Option<Vec<ModelRecord>> {
|
||||||
|
let res = utils::get_data(&url, None).await.unwrap_or_else(|err| {
|
||||||
|
info!("chatgpt_http_error: {}", err);
|
||||||
|
None
|
||||||
|
});
|
||||||
|
|
||||||
|
info!("chatgpt_http_url: {}", url);
|
||||||
|
|
||||||
|
if let Some(v) = res {
|
||||||
|
let data;
|
||||||
|
if data_type == "csv" {
|
||||||
|
info!("chatgpt_http_csv_parse");
|
||||||
|
data = parse_prompt(v);
|
||||||
|
} 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);
|
||||||
|
vec![]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
info!("chatgpt_http_unknown_type");
|
||||||
|
data = vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = data
|
||||||
|
.iter()
|
||||||
|
.map(move |i| ModelRecord {
|
||||||
|
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!["user-sync".to_string()],
|
||||||
|
enable: true,
|
||||||
|
})
|
||||||
|
.collect::<Vec<ModelRecord>>();
|
||||||
|
|
||||||
|
return Some(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
@@ -235,7 +235,7 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
|||||||
utils::run_check_update(app, false, None);
|
utils::run_check_update(app, false, None);
|
||||||
}
|
}
|
||||||
// Preferences
|
// Preferences
|
||||||
"control_center" => window::control_window(app.clone()),
|
"control_center" => window::control_window(app),
|
||||||
"restart" => tauri::api::process::restart(&app.env()),
|
"restart" => tauri::api::process::restart(&app.env()),
|
||||||
"inject_script" => open(&app, script_path),
|
"inject_script" => open(&app, script_path),
|
||||||
"go_conf" => utils::open_file(utils::chat_root()),
|
"go_conf" => utils::open_file(utils::chat_root()),
|
||||||
@@ -340,7 +340,6 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
|||||||
"reload" => win.eval("window.location.reload()").unwrap(),
|
"reload" => win.eval("window.location.reload()").unwrap(),
|
||||||
"go_back" => win.eval("window.history.go(-1)").unwrap(),
|
"go_back" => win.eval("window.history.go(-1)").unwrap(),
|
||||||
"go_forward" => win.eval("window.history.go(1)").unwrap(),
|
"go_forward" => win.eval("window.history.go(1)").unwrap(),
|
||||||
// core: document.querySelector('main .overflow-y-auto')
|
|
||||||
"scroll_top" => win
|
"scroll_top" => win
|
||||||
.eval(
|
.eval(
|
||||||
r#"window.scroll({
|
r#"window.scroll({
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pub mod cmd;
|
pub mod cmd;
|
||||||
pub mod fs_extra;
|
pub mod fs_extra;
|
||||||
|
pub mod gpt;
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
|
|||||||
@@ -93,6 +93,16 @@ 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn control_window(handle: tauri::AppHandle) {
|
pub fn control_window(handle: tauri::AppHandle) {
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
@@ -105,8 +115,8 @@ pub fn control_window(handle: tauri::AppHandle) {
|
|||||||
.title("Control Center")
|
.title("Control Center")
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.fullscreen(false)
|
.fullscreen(false)
|
||||||
.inner_size(1000.0, 700.0)
|
.inner_size(1200.0, 700.0)
|
||||||
.min_inner_size(800.0, 600.0)
|
.min_inner_size(1000.0, 600.0)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
@@ -140,6 +150,11 @@ pub async fn wa_window(
|
|||||||
if !win.clone().unwrap().is_visible().unwrap() {
|
if !win.clone().unwrap().is_visible().unwrap() {
|
||||||
win.clone().unwrap().show().unwrap();
|
win.clone().unwrap().show().unwrap();
|
||||||
}
|
}
|
||||||
|
win
|
||||||
|
.clone()
|
||||||
|
.unwrap()
|
||||||
|
.eval("window.location.reload()")
|
||||||
|
.unwrap();
|
||||||
win.unwrap().set_focus().unwrap();
|
win.unwrap().set_focus().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ mod app;
|
|||||||
mod conf;
|
mod conf;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use app::{cmd, fs_extra, menu, setup, window};
|
use app::{cmd, fs_extra, gpt, menu, setup, window};
|
||||||
use conf::ChatConfJson;
|
use conf::ChatConfJson;
|
||||||
use tauri::api::path;
|
use tauri::api::path;
|
||||||
use tauri_plugin_autostart::MacosLauncher;
|
use tauri_plugin_autostart::MacosLauncher;
|
||||||
@@ -30,8 +30,8 @@ async fn main() {
|
|||||||
trace: Color::Cyan,
|
trace: Color::Cyan,
|
||||||
};
|
};
|
||||||
|
|
||||||
cmd::download_list("chat.download.json", "download", None, None);
|
gpt::download_list("chat.download.json", "download", None, None);
|
||||||
cmd::download_list("chat.notes.json", "notes", None, None);
|
gpt::download_list("chat.notes.json", "notes", None, None);
|
||||||
|
|
||||||
let chat_conf = ChatConfJson::get_chat_conf();
|
let chat_conf = ChatConfJson::get_chat_conf();
|
||||||
|
|
||||||
@@ -69,19 +69,19 @@ async fn main() {
|
|||||||
cmd::form_confirm,
|
cmd::form_confirm,
|
||||||
cmd::form_msg,
|
cmd::form_msg,
|
||||||
cmd::open_file,
|
cmd::open_file,
|
||||||
cmd::get_chat_model_cmd,
|
|
||||||
cmd::parse_prompt,
|
|
||||||
cmd::sync_prompts,
|
|
||||||
cmd::sync_user_prompts,
|
|
||||||
cmd::dalle2_window,
|
|
||||||
cmd::cmd_list,
|
|
||||||
cmd::download_list,
|
|
||||||
cmd::get_download_list,
|
|
||||||
cmd::get_data,
|
cmd::get_data,
|
||||||
fs_extra::metadata,
|
gpt::get_chat_model_cmd,
|
||||||
window::window_reload,
|
gpt::parse_prompt,
|
||||||
|
gpt::sync_prompts,
|
||||||
|
gpt::sync_user_prompts,
|
||||||
|
gpt::cmd_list,
|
||||||
|
gpt::download_list,
|
||||||
|
gpt::get_download_list,
|
||||||
window::wa_window,
|
window::wa_window,
|
||||||
window::control_window,
|
window::control_window,
|
||||||
|
window::window_reload,
|
||||||
|
window::dalle2_search_window,
|
||||||
|
fs_extra::metadata,
|
||||||
])
|
])
|
||||||
.setup(setup::init)
|
.setup(setup::init)
|
||||||
.menu(menu::init());
|
.menu(menu::init());
|
||||||
|
|||||||
2
src-tauri/src/scripts/popup.core.js
vendored
2
src-tauri/src/scripts/popup.core.js
vendored
@@ -33,7 +33,7 @@ async function init() {
|
|||||||
document.body.addEventListener('mousedown', async (e) => {
|
document.body.addEventListener('mousedown', async (e) => {
|
||||||
selectionMenu.style.display = 'none';
|
selectionMenu.style.display = 'none';
|
||||||
if (e.target.id === 'chagpt-selection-menu') {
|
if (e.target.id === 'chagpt-selection-menu') {
|
||||||
await invoke('dalle2_window', { query: encodeURIComponent(window.__DALLE2_CONTENT__) });
|
await invoke('dalle2_search_window', { query: encodeURIComponent(window.__DALLE2_CONTENT__) });
|
||||||
} else {
|
} else {
|
||||||
delete window.__DALLE2_CONTENT__;
|
delete window.__DALLE2_CONTENT__;
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/components/Markdown/index.scss
vendored
6
src/components/Markdown/index.scss
vendored
@@ -13,12 +13,6 @@
|
|||||||
code {
|
code {
|
||||||
font-family: monospace, monospace;
|
font-family: monospace, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: rgba(200, 200, 200, 0.4);
|
|
||||||
padding: 2px 4px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-main {
|
.md-main {
|
||||||
|
|||||||
4
src/components/SwitchOrigin/index.tsx
vendored
4
src/components/SwitchOrigin/index.tsx
vendored
@@ -28,7 +28,7 @@ const SwitchOrigin: FC<SwitchOriginProps> = ({ name }) => {
|
|||||||
title={
|
title={
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
<b>Set the URL dashboard as an application window.</b>
|
<b>Set Dashboard as the application default window.</b>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If this is enabled, the <Tag color="blue">Switch Origin {labelName}</Tag>{' '}
|
If this is enabled, the <Tag color="blue">Switch Origin {labelName}</Tag>{' '}
|
||||||
@@ -58,7 +58,7 @@ const SwitchOrigin: FC<SwitchOriginProps> = ({ name }) => {
|
|||||||
title={
|
title={
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
<b>Set a single URL as an application window.</b>
|
<b>Set a single URL as the application default window.</b>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If you need to set a new URL as the application loading window, please add the
|
If you need to set a new URL as the application loading window, please add the
|
||||||
|
|||||||
17
src/hooks/useData.ts
vendored
17
src/hooks/useData.ts
vendored
@@ -28,6 +28,12 @@ export default function useData(oData: any[]) {
|
|||||||
return nData;
|
return nData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const opRemoveItems = (ids: string[]) => {
|
||||||
|
const nData = opData.filter((i) => !ids.includes(i[safeKey]));
|
||||||
|
setData(nData);
|
||||||
|
return nData;
|
||||||
|
};
|
||||||
|
|
||||||
const opReplace = (id: string, data: any) => {
|
const opReplace = (id: string, data: any) => {
|
||||||
const nData = [...opData];
|
const nData = [...opData];
|
||||||
const idx = opData.findIndex((v) => v[safeKey] === id);
|
const idx = opData.findIndex((v) => v[safeKey] === id);
|
||||||
@@ -51,5 +57,14 @@ export default function useData(oData: any[]) {
|
|||||||
return nData;
|
return nData;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { opSafeKey: safeKey, opInit, opReplace, opAdd, opRemove, opData, opReplaceItems };
|
return {
|
||||||
|
opSafeKey: safeKey,
|
||||||
|
opInit,
|
||||||
|
opReplace,
|
||||||
|
opAdd,
|
||||||
|
opRemove,
|
||||||
|
opRemoveItems,
|
||||||
|
opData,
|
||||||
|
opReplaceItems,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/layout/index.tsx
vendored
5
src/layout/index.tsx
vendored
@@ -21,7 +21,10 @@ export default function ChatLayout() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (location.search === '?type=control') {
|
if (location.search === '?type=control') {
|
||||||
go('/awesome');
|
go('/settings');
|
||||||
|
}
|
||||||
|
if (location.search === '?type=preview') {
|
||||||
|
go('/?type=preview');
|
||||||
}
|
}
|
||||||
setMenuKey(location.pathname);
|
setMenuKey(location.pathname);
|
||||||
setDashboard(location.pathname === '/');
|
setDashboard(location.pathname === '/');
|
||||||
|
|||||||
6
src/main.scss
vendored
6
src/main.scss
vendored
@@ -103,3 +103,9 @@ body,
|
|||||||
.chatico {
|
.chatico {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.awesome-tips {
|
||||||
|
.ant-tag {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
16
src/routes.tsx
vendored
16
src/routes.tsx
vendored
@@ -38,6 +38,14 @@ type ChatRouteObject = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const routes: Array<ChatRouteObject> = [
|
export const routes: Array<ChatRouteObject> = [
|
||||||
|
{
|
||||||
|
path: '/settings',
|
||||||
|
element: <Settings />,
|
||||||
|
meta: {
|
||||||
|
label: 'Settings',
|
||||||
|
icon: <SettingOutlined />,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/awesome',
|
path: '/awesome',
|
||||||
element: <Awesome />,
|
element: <Awesome />,
|
||||||
@@ -106,14 +114,6 @@ export const routes: Array<ChatRouteObject> = [
|
|||||||
icon: <DownloadOutlined />,
|
icon: <DownloadOutlined />,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/settings',
|
|
||||||
element: <Settings />,
|
|
||||||
meta: {
|
|
||||||
label: 'Settings',
|
|
||||||
icon: <SettingOutlined />,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/about',
|
path: '/about',
|
||||||
element: <About />,
|
element: <About />,
|
||||||
|
|||||||
6
src/view/about/index.scss
vendored
6
src/view/about/index.scss
vendored
@@ -15,4 +15,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: rgba(200, 200, 200, 0.4);
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/view/awesome/Form.tsx
vendored
6
src/view/awesome/Form.tsx
vendored
@@ -43,7 +43,11 @@ const AwesomeForm: ForwardRefRenderFunction<FormProps, AwesomeFormProps> = ({ re
|
|||||||
>
|
>
|
||||||
<Input placeholder="Please enter the URL" {...DISABLE_AUTO_COMPLETE} />
|
<Input placeholder="Please enter the URL" {...DISABLE_AUTO_COMPLETE} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Category" name="category">
|
<Form.Item
|
||||||
|
label="Category"
|
||||||
|
name="category"
|
||||||
|
rules={[{ required: true, message: 'Please enter a category' }]}
|
||||||
|
>
|
||||||
<Input placeholder="Please enter a category" {...DISABLE_AUTO_COMPLETE} />
|
<Input placeholder="Please enter a category" {...DISABLE_AUTO_COMPLETE} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Tags" name="tags">
|
<Form.Item label="Tags" name="tags">
|
||||||
|
|||||||
72
src/view/awesome/index.tsx
vendored
72
src/view/awesome/index.tsx
vendored
@@ -1,5 +1,8 @@
|
|||||||
import { useRef, useEffect, useState } from 'react';
|
import { useRef, useEffect, useState } from 'react';
|
||||||
import { Table, Modal, Popconfirm, Button, message } from 'antd';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
|
import { Table, Modal, Popconfirm, Button, Tooltip, Tag, message } from 'antd';
|
||||||
|
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { invoke } from '@tauri-apps/api';
|
||||||
|
|
||||||
import useJson from '@/hooks/useJson';
|
import useJson from '@/hooks/useJson';
|
||||||
import useData from '@/hooks/useData';
|
import useData from '@/hooks/useData';
|
||||||
@@ -13,9 +16,10 @@ import AwesomeForm from './Form';
|
|||||||
export default function Awesome() {
|
export default function Awesome() {
|
||||||
const formRef = useRef<any>(null);
|
const formRef = useRef<any>(null);
|
||||||
const [isVisible, setVisible] = useState(false);
|
const [isVisible, setVisible] = useState(false);
|
||||||
const { opData, opInit, opAdd, opReplace, opReplaceItems, opRemove, opSafeKey } = useData([]);
|
const { opData, opInit, opAdd, opReplace, opReplaceItems, opRemove, opRemoveItems, opSafeKey } =
|
||||||
|
useData([]);
|
||||||
const { columns, ...opInfo } = useColumns(awesomeColumns());
|
const { columns, ...opInfo } = useColumns(awesomeColumns());
|
||||||
const { rowSelection, selectedRowIDs } = useTableRowSelection();
|
const { rowSelection, selectedRowIDs, rowReset } = useTableRowSelection();
|
||||||
const { json, updateJson } = useJson<any[]>(CHAT_AWESOME_JSON);
|
const { json, updateJson } = useJson<any[]>(CHAT_AWESOME_JSON);
|
||||||
const selectedItems = rowSelection.selectedRowKeys || [];
|
const selectedItems = rowSelection.selectedRowKeys || [];
|
||||||
|
|
||||||
@@ -48,11 +52,19 @@ export default function Awesome() {
|
|||||||
}
|
}
|
||||||
}, [opInfo.opTime]);
|
}, [opInfo.opTime]);
|
||||||
|
|
||||||
const handleDelete = () => {};
|
const handleDelete = () => {
|
||||||
|
const data = opRemoveItems(selectedRowIDs);
|
||||||
|
updateJson(data);
|
||||||
|
rowReset();
|
||||||
|
message.success('All selected URLs have been deleted');
|
||||||
|
};
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
formRef.current?.form?.validateFields().then(async (vals: Record<string, any>) => {
|
formRef.current?.form?.validateFields().then(async (vals: Record<string, any>) => {
|
||||||
const idx = opData.findIndex((i) => i.url === vals.url);
|
let idx = opData.findIndex((i) => i.url === vals.url);
|
||||||
|
if (vals.url === opInfo?.opRecord?.url) {
|
||||||
|
idx = -1;
|
||||||
|
}
|
||||||
if (idx === -1) {
|
if (idx === -1) {
|
||||||
if (opInfo.opType === 'new') {
|
if (opInfo.opType === 'new') {
|
||||||
const data = opAdd(vals);
|
const data = opAdd(vals);
|
||||||
@@ -87,14 +99,28 @@ export default function Awesome() {
|
|||||||
updateJson(data);
|
updateJson(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePreview = () => {
|
||||||
|
invoke('wa_window', {
|
||||||
|
label: 'awesome_preview',
|
||||||
|
url: 'index.html?type=preview',
|
||||||
|
title: 'Preview Dashboard',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const modalTitle = `${{ new: 'Create', edit: 'Edit' }[opInfo.opType]} URL`;
|
const modalTitle = `${{ new: 'Create', edit: 'Edit' }[opInfo.opType]} URL`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="chat-table-btns">
|
<div className="chat-table-btns">
|
||||||
<Button className="chat-add-btn" type="primary" onClick={opInfo.opNew}>
|
<div>
|
||||||
Add URL
|
<Button className="chat-add-btn" type="primary" onClick={opInfo.opNew}>
|
||||||
</Button>
|
Add URL
|
||||||
|
</Button>
|
||||||
|
<Button type="dashed" onClick={handlePreview}>
|
||||||
|
Preview Dashboard
|
||||||
|
</Button>
|
||||||
|
<PreviewTip />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{selectedItems.length > 0 && (
|
{selectedItems.length > 0 && (
|
||||||
<>
|
<>
|
||||||
@@ -139,3 +165,33 @@ export default function Awesome() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PreviewTip = () => {
|
||||||
|
const go = useNavigate();
|
||||||
|
const handleGo = (v: string) => {
|
||||||
|
go(`/settings?type=${v}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
overlayInnerStyle={{ width: 400 }}
|
||||||
|
title={
|
||||||
|
<div className="awesome-tips">
|
||||||
|
Click the button to preview, and in
|
||||||
|
<Link to="/settings"> Settings </Link>
|
||||||
|
you can set a single URL or Dashboard as the default window for the app.
|
||||||
|
<br />
|
||||||
|
<Tag onClick={() => handleGo('main_window')} color="blue">
|
||||||
|
Main Window
|
||||||
|
</Tag>
|
||||||
|
{'or '}
|
||||||
|
<Tag onClick={() => handleGo('tray_window')} color="blue">
|
||||||
|
SystemTray Window
|
||||||
|
</Tag>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<QuestionCircleOutlined style={{ marginLeft: 5, color: '#1677ff' }} />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
6
src/view/dashboard/index.scss
vendored
6
src/view/dashboard/index.scss
vendored
@@ -20,7 +20,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.txt {
|
.txt {
|
||||||
|
padding: 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #1677ff;
|
color: #1677ff;
|
||||||
@@ -37,6 +39,10 @@
|
|||||||
padding-top: 30px;
|
padding-top: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.preview {
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.group-item {
|
.group-item {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
|||||||
11
src/view/dashboard/index.tsx
vendored
11
src/view/dashboard/index.tsx
vendored
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { Row, Col, Card } from 'antd';
|
import { Row, Col, Card } from 'antd';
|
||||||
import { InboxOutlined } from '@ant-design/icons';
|
import { InboxOutlined } from '@ant-design/icons';
|
||||||
import { os, invoke } from '@tauri-apps/api';
|
import { os, invoke } from '@tauri-apps/api';
|
||||||
@@ -10,6 +11,7 @@ import { CHAT_AWESOME_JSON, CHAT_CONF_JSON, readJSON } from '@/utils';
|
|||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
|
const [params] = useSearchParams();
|
||||||
const { json } = useJson<Record<string, any>[]>(CHAT_AWESOME_JSON);
|
const { json } = useJson<Record<string, any>[]>(CHAT_AWESOME_JSON);
|
||||||
const [list, setList] = useState<Array<[string, Record<string, any>[]]>>();
|
const [list, setList] = useState<Array<[string, Record<string, any>[]]>>();
|
||||||
const [hasClass, setClass] = useState(false);
|
const [hasClass, setClass] = useState(false);
|
||||||
@@ -56,14 +58,19 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="txt">
|
<div className="txt">
|
||||||
Go to <a onClick={() => invoke('control_window')}>{'Control Center -> Awesome'}</a> to add
|
Go to <a onClick={() => invoke('control_window')}>{'Control Center -> Awesome'}</a> to add
|
||||||
data
|
data and make sure they are enabled.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx('dashboard', theme, { 'has-top-dom': hasClass })}>
|
<div
|
||||||
|
className={clsx('dashboard', theme, {
|
||||||
|
'has-top-dom': hasClass,
|
||||||
|
preview: params.get('type') === 'preview',
|
||||||
|
})}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
{list.map((i) => {
|
{list.map((i) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
14
src/view/settings/index.tsx
vendored
14
src/view/settings/index.tsx
vendored
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { Form, Tabs, Space, Button, Popconfirm, message } from 'antd';
|
import { Form, Tabs, Space, Button, Popconfirm, message } from 'antd';
|
||||||
import { invoke, dialog, process, path, shell } from '@tauri-apps/api';
|
import { invoke, dialog, process, path, shell } from '@tauri-apps/api';
|
||||||
import { clone, omit, isEqual } from 'lodash';
|
import { clone, omit, isEqual } from 'lodash';
|
||||||
@@ -11,9 +12,16 @@ import MainWindow from './MainWindow';
|
|||||||
import TrayWindow from './TrayWindow';
|
import TrayWindow from './TrayWindow';
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
|
const [params] = useSearchParams();
|
||||||
|
const [activeKey, setActiveKey] = useState('general');
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [chatConf, setChatConf] = useState<any>(null);
|
const [chatConf, setChatConf] = useState<any>(null);
|
||||||
const [filePath, setPath] = useState('');
|
const [filePath, setPath] = useState('');
|
||||||
|
const key = params.get('type');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setActiveKey(key ? key : 'general');
|
||||||
|
}, [key]);
|
||||||
|
|
||||||
useInit(async () => {
|
useInit(async () => {
|
||||||
setChatConf(await invoke('get_chat_conf'));
|
setChatConf(await invoke('get_chat_conf'));
|
||||||
@@ -55,6 +63,10 @@ export default function Settings() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleTab = (v: string) => {
|
||||||
|
setActiveKey(v);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FilePath paths={CHAT_CONF_JSON} />
|
<FilePath paths={CHAT_CONF_JSON} />
|
||||||
@@ -66,6 +78,8 @@ export default function Settings() {
|
|||||||
wrapperCol={{ span: 13, offset: 1 }}
|
wrapperCol={{ span: 13, offset: 1 }}
|
||||||
>
|
>
|
||||||
<Tabs
|
<Tabs
|
||||||
|
activeKey={activeKey}
|
||||||
|
onChange={handleTab}
|
||||||
items={[
|
items={[
|
||||||
{ label: 'General', key: 'general', children: <General /> },
|
{ label: 'General', key: 'general', children: <General /> },
|
||||||
{ label: 'Main Window', key: 'main_window', children: <MainWindow /> },
|
{ label: 'Main Window', key: 'main_window', children: <MainWindow /> },
|
||||||
|
|||||||
Reference in New Issue
Block a user