From e6052152ea26d406f37f7d8e0110bdf174dd8385 Mon Sep 17 00:00:00 2001 From: lencx Date: Tue, 24 Jan 2023 23:23:52 +0800 Subject: [PATCH] chore: dashboard --- src-tauri/src/app/cmd.rs | 309 +------------------------- src-tauri/src/app/gpt.rs | 294 ++++++++++++++++++++++++ src-tauri/src/app/menu.rs | 3 +- src-tauri/src/app/mod.rs | 1 + src-tauri/src/app/window.rs | 19 +- src-tauri/src/main.rs | 26 +-- src-tauri/src/scripts/popup.core.js | 2 +- src/components/Markdown/index.scss | 6 - src/components/SwitchOrigin/index.tsx | 4 +- src/hooks/useData.ts | 17 +- src/layout/index.tsx | 5 +- src/main.scss | 6 + src/routes.tsx | 16 +- src/view/about/index.scss | 6 + src/view/awesome/Form.tsx | 6 +- src/view/awesome/index.tsx | 72 +++++- src/view/dashboard/index.scss | 6 + src/view/dashboard/index.tsx | 11 +- src/view/settings/index.tsx | 14 ++ 19 files changed, 473 insertions(+), 350 deletions(-) create mode 100644 src-tauri/src/app/gpt.rs diff --git a/src-tauri/src/app/cmd.rs b/src-tauri/src/app/cmd.rs index 0a4c655..64493cf 100644 --- a/src-tauri/src/app/cmd.rs +++ b/src-tauri/src/app/cmd.rs @@ -1,29 +1,16 @@ use crate::{ - app::{fs_extra, window}, - conf::{ChatConfJson, GITHUB_PROMPTS_CSV_URL}, + conf::ChatConfJson, utils::{self, chat_root, create_file}, }; use log::info; -use regex::Regex; -use std::{collections::HashMap, fs, path::PathBuf, vec}; +use std::{fs, path::PathBuf}; use tauri::{api, command, AppHandle, Manager, Theme}; -use walkdir::WalkDir; #[command] pub fn drag_window(app: AppHandle) { 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] pub fn fullscreen(app: AppHandle) { let win = app.get_window("core").unwrap(); @@ -61,13 +48,13 @@ pub fn get_chat_conf() -> ChatConfJson { } #[command] -pub fn get_theme() -> String { - ChatConfJson::theme().unwrap_or(Theme::Light).to_string() +pub fn reset_chat_conf() -> ChatConfJson { + ChatConfJson::reset_chat_conf() } #[command] -pub fn reset_chat_conf() -> ChatConfJson { - ChatConfJson::reset_chat_conf() +pub fn get_theme() -> String { + ChatConfJson::theme().unwrap_or(Theme::Light).to_string() } #[command] @@ -106,244 +93,6 @@ pub fn open_file(path: PathBuf) { 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, - pub act: String, - pub prompt: String, -} - -#[command] -pub fn parse_prompt(data: String) -> Vec { - 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, - pub enable: bool, -} - -#[command] -pub fn cmd_list() -> Vec { - 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 = 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, 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::>(&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, id: Option) { - 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[\d\w]+).(?P\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> { - 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::>(); - - 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 get_data(app: AppHandle, url: String, is_msg: Option) -> Option { let is_msg = is_msg.unwrap_or(false); @@ -357,49 +106,3 @@ pub async fn get_data(app: AppHandle, url: String, is_msg: Option) -> Opti None }) } - -#[command] -pub async fn sync_user_prompts(url: String, data_type: String) -> Option> { - 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::>(); - - return Some(data); - } - - None -} diff --git a/src-tauri/src/app/gpt.rs b/src-tauri/src/app/gpt.rs new file mode 100644 index 0000000..392c15f --- /dev/null +++ b/src-tauri/src/app/gpt.rs @@ -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, + pub act: String, + pub prompt: String, +} + +#[command] +pub fn parse_prompt(data: String) -> Vec { + 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, + pub enable: bool, +} + +#[command] +pub fn cmd_list() -> Vec { + 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 = 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, 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::>(&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, id: Option) { + 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[\d\w]+).(?P\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> { + 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::>(); + + 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> { + 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::>(); + + return Some(data); + } + + None +} diff --git a/src-tauri/src/app/menu.rs b/src-tauri/src/app/menu.rs index 9e1aece..e507613 100644 --- a/src-tauri/src/app/menu.rs +++ b/src-tauri/src/app/menu.rs @@ -235,7 +235,7 @@ pub fn menu_handler(event: WindowMenuEvent) { utils::run_check_update(app, false, None); } // Preferences - "control_center" => window::control_window(app.clone()), + "control_center" => window::control_window(app), "restart" => tauri::api::process::restart(&app.env()), "inject_script" => open(&app, script_path), "go_conf" => utils::open_file(utils::chat_root()), @@ -340,7 +340,6 @@ pub fn menu_handler(event: WindowMenuEvent) { "reload" => win.eval("window.location.reload()").unwrap(), "go_back" => win.eval("window.history.go(-1)").unwrap(), "go_forward" => win.eval("window.history.go(1)").unwrap(), - // core: document.querySelector('main .overflow-y-auto') "scroll_top" => win .eval( r#"window.scroll({ diff --git a/src-tauri/src/app/mod.rs b/src-tauri/src/app/mod.rs index 46d47f9..39fac31 100644 --- a/src-tauri/src/app/mod.rs +++ b/src-tauri/src/app/mod.rs @@ -1,5 +1,6 @@ pub mod cmd; pub mod fs_extra; +pub mod gpt; pub mod menu; pub mod setup; pub mod window; diff --git a/src-tauri/src/app/window.rs b/src-tauri/src/app/window.rs index 8a912e3..e23daa8 100644 --- a/src-tauri/src/app/window.rs +++ b/src-tauri/src/app/window.rs @@ -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] pub fn control_window(handle: tauri::AppHandle) { tauri::async_runtime::spawn(async move { @@ -105,8 +115,8 @@ pub fn control_window(handle: tauri::AppHandle) { .title("Control Center") .resizable(true) .fullscreen(false) - .inner_size(1000.0, 700.0) - .min_inner_size(800.0, 600.0) + .inner_size(1200.0, 700.0) + .min_inner_size(1000.0, 600.0) .build() .unwrap(); } else { @@ -140,6 +150,11 @@ pub async fn wa_window( if !win.clone().unwrap().is_visible().unwrap() { win.clone().unwrap().show().unwrap(); } + win + .clone() + .unwrap() + .eval("window.location.reload()") + .unwrap(); win.unwrap().set_focus().unwrap(); } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 71def99..601d4dc 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,7 +7,7 @@ mod app; mod conf; mod utils; -use app::{cmd, fs_extra, menu, setup, window}; +use app::{cmd, fs_extra, gpt, menu, setup, window}; use conf::ChatConfJson; use tauri::api::path; use tauri_plugin_autostart::MacosLauncher; @@ -30,8 +30,8 @@ async fn main() { trace: Color::Cyan, }; - cmd::download_list("chat.download.json", "download", None, None); - cmd::download_list("chat.notes.json", "notes", None, None); + gpt::download_list("chat.download.json", "download", None, None); + gpt::download_list("chat.notes.json", "notes", None, None); let chat_conf = ChatConfJson::get_chat_conf(); @@ -69,19 +69,19 @@ async fn main() { cmd::form_confirm, cmd::form_msg, 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, - fs_extra::metadata, - window::window_reload, + gpt::get_chat_model_cmd, + gpt::parse_prompt, + gpt::sync_prompts, + gpt::sync_user_prompts, + gpt::cmd_list, + gpt::download_list, + gpt::get_download_list, window::wa_window, window::control_window, + window::window_reload, + window::dalle2_search_window, + fs_extra::metadata, ]) .setup(setup::init) .menu(menu::init()); diff --git a/src-tauri/src/scripts/popup.core.js b/src-tauri/src/scripts/popup.core.js index f45db80..3cbbe18 100644 --- a/src-tauri/src/scripts/popup.core.js +++ b/src-tauri/src/scripts/popup.core.js @@ -33,7 +33,7 @@ async function init() { document.body.addEventListener('mousedown', async (e) => { selectionMenu.style.display = 'none'; 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 { delete window.__DALLE2_CONTENT__; } diff --git a/src/components/Markdown/index.scss b/src/components/Markdown/index.scss index 64745c0..5c0bc57 100644 --- a/src/components/Markdown/index.scss +++ b/src/components/Markdown/index.scss @@ -13,12 +13,6 @@ code { font-family: monospace, monospace; } - - code { - background-color: rgba(200, 200, 200, 0.4); - padding: 2px 4px; - border-radius: 5px; - } } .md-main { diff --git a/src/components/SwitchOrigin/index.tsx b/src/components/SwitchOrigin/index.tsx index e07b87a..d51c510 100644 --- a/src/components/SwitchOrigin/index.tsx +++ b/src/components/SwitchOrigin/index.tsx @@ -28,7 +28,7 @@ const SwitchOrigin: FC = ({ name }) => { title={

- Set the URL dashboard as an application window. + Set Dashboard as the application default window.

If this is enabled, the Switch Origin {labelName}{' '} @@ -58,7 +58,7 @@ const SwitchOrigin: FC = ({ name }) => { title={

- Set a single URL as an application window. + Set a single URL as the application default window.

If you need to set a new URL as the application loading window, please add the diff --git a/src/hooks/useData.ts b/src/hooks/useData.ts index de82cce..3a58498 100644 --- a/src/hooks/useData.ts +++ b/src/hooks/useData.ts @@ -28,6 +28,12 @@ export default function useData(oData: any[]) { 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 nData = [...opData]; const idx = opData.findIndex((v) => v[safeKey] === id); @@ -51,5 +57,14 @@ export default function useData(oData: any[]) { return nData; }; - return { opSafeKey: safeKey, opInit, opReplace, opAdd, opRemove, opData, opReplaceItems }; + return { + opSafeKey: safeKey, + opInit, + opReplace, + opAdd, + opRemove, + opRemoveItems, + opData, + opReplaceItems, + }; } diff --git a/src/layout/index.tsx b/src/layout/index.tsx index b5bdf49..f1a331d 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -21,7 +21,10 @@ export default function ChatLayout() { useEffect(() => { if (location.search === '?type=control') { - go('/awesome'); + go('/settings'); + } + if (location.search === '?type=preview') { + go('/?type=preview'); } setMenuKey(location.pathname); setDashboard(location.pathname === '/'); diff --git a/src/main.scss b/src/main.scss index 03f6aec..c7c6441 100644 --- a/src/main.scss +++ b/src/main.scss @@ -103,3 +103,9 @@ body, .chatico { cursor: pointer; } + +.awesome-tips { + .ant-tag { + cursor: pointer; + } +} diff --git a/src/routes.tsx b/src/routes.tsx index bb4881a..66910fb 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -38,6 +38,14 @@ type ChatRouteObject = { }; export const routes: Array = [ + { + path: '/settings', + element: , + meta: { + label: 'Settings', + icon: , + }, + }, { path: '/awesome', element: , @@ -106,14 +114,6 @@ export const routes: Array = [ icon: , }, }, - { - path: '/settings', - element: , - meta: { - label: 'Settings', - icon: , - }, - }, { path: '/about', element: , diff --git a/src/view/about/index.scss b/src/view/about/index.scss index 53cd372..408924f 100644 --- a/src/view/about/index.scss +++ b/src/view/about/index.scss @@ -15,4 +15,10 @@ } } } + + code { + background-color: rgba(200, 200, 200, 0.4); + padding: 2px 4px; + border-radius: 5px; + } } diff --git a/src/view/awesome/Form.tsx b/src/view/awesome/Form.tsx index 89054ce..baee30b 100644 --- a/src/view/awesome/Form.tsx +++ b/src/view/awesome/Form.tsx @@ -43,7 +43,11 @@ const AwesomeForm: ForwardRefRenderFunction = ({ re > - + diff --git a/src/view/awesome/index.tsx b/src/view/awesome/index.tsx index ba59b85..654cc1a 100644 --- a/src/view/awesome/index.tsx +++ b/src/view/awesome/index.tsx @@ -1,5 +1,8 @@ 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 useData from '@/hooks/useData'; @@ -13,9 +16,10 @@ import AwesomeForm from './Form'; export default function Awesome() { const formRef = useRef(null); 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 { rowSelection, selectedRowIDs } = useTableRowSelection(); + const { rowSelection, selectedRowIDs, rowReset } = useTableRowSelection(); const { json, updateJson } = useJson(CHAT_AWESOME_JSON); const selectedItems = rowSelection.selectedRowKeys || []; @@ -48,11 +52,19 @@ export default function Awesome() { } }, [opInfo.opTime]); - const handleDelete = () => {}; + const handleDelete = () => { + const data = opRemoveItems(selectedRowIDs); + updateJson(data); + rowReset(); + message.success('All selected URLs have been deleted'); + }; const handleOk = () => { formRef.current?.form?.validateFields().then(async (vals: Record) => { - 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 (opInfo.opType === 'new') { const data = opAdd(vals); @@ -87,14 +99,28 @@ export default function Awesome() { 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`; return (

- +
+ + + +
{selectedItems.length > 0 && ( <> @@ -139,3 +165,33 @@ export default function Awesome() {
); } + +const PreviewTip = () => { + const go = useNavigate(); + const handleGo = (v: string) => { + go(`/settings?type=${v}`); + }; + + return ( + + Click the button to preview, and in + Settings + you can set a single URL or Dashboard as the default window for the app. +
+ handleGo('main_window')} color="blue"> + Main Window + + {'or '} + handleGo('tray_window')} color="blue"> + SystemTray Window + +
+ } + > + + + ); +}; diff --git a/src/view/dashboard/index.scss b/src/view/dashboard/index.scss index 7b66b9e..96c8a7a 100644 --- a/src/view/dashboard/index.scss +++ b/src/view/dashboard/index.scss @@ -20,7 +20,9 @@ } .txt { + padding: 10px; font-size: 12px; + line-height: 16px; a { color: #1677ff; @@ -37,6 +39,10 @@ padding-top: 30px; } + &.preview { + padding-top: 15px; + } + .group-item { margin-bottom: 20px; diff --git a/src/view/dashboard/index.tsx b/src/view/dashboard/index.tsx index 00a68f0..1b97d41 100644 --- a/src/view/dashboard/index.tsx +++ b/src/view/dashboard/index.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; import clsx from 'clsx'; +import { useSearchParams } from 'react-router-dom'; import { Row, Col, Card } from 'antd'; import { InboxOutlined } from '@ant-design/icons'; import { os, invoke } from '@tauri-apps/api'; @@ -10,6 +11,7 @@ import { CHAT_AWESOME_JSON, CHAT_CONF_JSON, readJSON } from '@/utils'; import './index.scss'; export default function Dashboard() { + const [params] = useSearchParams(); const { json } = useJson[]>(CHAT_AWESOME_JSON); const [list, setList] = useState[]]>>(); const [hasClass, setClass] = useState(false); @@ -56,14 +58,19 @@ export default function Dashboard() {
Go to invoke('control_window')}>{'Control Center -> Awesome'} to add - data + data and make sure they are enabled.
); } return ( -
+
{list.map((i) => { return ( diff --git a/src/view/settings/index.tsx b/src/view/settings/index.tsx index 6d7ce83..1b41f15 100644 --- a/src/view/settings/index.tsx +++ b/src/view/settings/index.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; import { Form, Tabs, Space, Button, Popconfirm, message } from 'antd'; import { invoke, dialog, process, path, shell } from '@tauri-apps/api'; import { clone, omit, isEqual } from 'lodash'; @@ -11,9 +12,16 @@ import MainWindow from './MainWindow'; import TrayWindow from './TrayWindow'; export default function Settings() { + const [params] = useSearchParams(); + const [activeKey, setActiveKey] = useState('general'); const [form] = Form.useForm(); const [chatConf, setChatConf] = useState(null); const [filePath, setPath] = useState(''); + const key = params.get('type'); + + useEffect(() => { + setActiveKey(key ? key : 'general'); + }, [key]); useInit(async () => { setChatConf(await invoke('get_chat_conf')); @@ -55,6 +63,10 @@ export default function Settings() { } }; + const handleTab = (v: string) => { + setActiveKey(v); + }; + return (
@@ -66,6 +78,8 @@ export default function Settings() { wrapperCol={{ span: 13, offset: 1 }} > }, { label: 'Main Window', key: 'main_window', children: },