mirror of
https://github.com/lencx/ChatGPT.git
synced 2024-10-01 01:06:13 -04:00
chore: sync
This commit is contained in:
parent
2be560e69a
commit
389e00a5e0
@ -19,11 +19,12 @@ serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.2.2", features = ["api-all", "devtools", "system-tray", "updater"] }
|
||||
tauri-plugin-positioner = { version = "1.0.4", features = ["system-tray"] }
|
||||
tokio = { version = "1.23.0", features = ["macros"] }
|
||||
log = "0.4.17"
|
||||
csv = "1.1.6"
|
||||
thiserror = "1.0.38"
|
||||
reqwest = "0.11.13"
|
||||
walkdir = "2.3.2"
|
||||
# tokio = { version = "1.23.0", features = ["macros"] }
|
||||
# reqwest = "0.11.13"
|
||||
|
||||
[dependencies.tauri-plugin-log]
|
||||
git = "https://github.com/tauri-apps/tauri-plugin-log"
|
||||
|
@ -66,8 +66,8 @@ pub fn open_file(path: PathBuf) {
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn get_chat_model() -> serde_json::Value {
|
||||
let path = utils::chat_root().join("chat.model.json");
|
||||
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()
|
||||
}
|
||||
@ -98,3 +98,33 @@ pub fn window_reload(app: AppHandle, label: &str) {
|
||||
.eval("window.location.reload()")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
||||
use walkdir::WalkDir;
|
||||
use utils::chat_root;
|
||||
|
||||
#[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(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
|
||||
}
|
||||
|
51
src-tauri/src/assets/cmd.js
vendored
51
src-tauri/src/assets/cmd.js
vendored
@ -61,10 +61,8 @@ function init() {
|
||||
}
|
||||
|
||||
async function cmdTip() {
|
||||
const chatModelJson = await invoke('get_chat_model') || {};
|
||||
const user_custom = chatModelJson.user_custom || [];
|
||||
const sys_sync_prompts = chatModelJson.sys_sync_prompts || [];
|
||||
const data = [...user_custom, ...sys_sync_prompts];
|
||||
const chatModelJson = await invoke('get_chat_model_cmd') || {};
|
||||
const data = chatModelJson.data;
|
||||
if (data.length <= 0) return;
|
||||
|
||||
const modelDom = document.createElement('div');
|
||||
@ -82,18 +80,43 @@ async function cmdTip() {
|
||||
// Enter a command starting with `/` and press a space to automatically fill `chatgpt prompt`.
|
||||
// If more than one command appears in the search results, the first one will be used by default.
|
||||
searchInput.addEventListener('keydown', (event) => {
|
||||
if (!window.__CHAT_MODEL_CMD__) {
|
||||
if (!window.__CHAT_MODEL_CMD_PROMPT__) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.keyCode === 13 && window.__CHAT_MODEL_CMD__) {
|
||||
searchInput.value = window.__CHAT_MODEL_CMD__;
|
||||
// feat: https://github.com/lencx/ChatGPT/issues/54
|
||||
if (event.keyCode === 9) {
|
||||
const strGroup = window.__CHAT_MODEL_CMD_PROMPT__.match(/\{([^{}]*)\}/) || [];
|
||||
|
||||
if (strGroup[1]) {
|
||||
searchInput.value = `/${window.__CHAT_MODEL_CMD__}` + `{${strGroup[1]}}` + ' |-> ';
|
||||
window.__CHAT_MODEL_VAR__ = true;
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
if (window.__CHAT_MODEL_VAR__ && event.keyCode === 9) {
|
||||
const data = searchInput.value.split('|->');
|
||||
if (data[1]) {
|
||||
window.__CHAT_MODEL_CMD_PROMPT__ = window.__CHAT_MODEL_CMD_PROMPT__?.replace(/\{([^{}]*)\}/, `{${data[1]?.trim()}}`);
|
||||
// searchInput.value = window.__CHAT_MODEL_CMD_PROMPT__;
|
||||
}
|
||||
// event.preventDefault();
|
||||
}
|
||||
|
||||
// send
|
||||
if (event.keyCode === 13 && window.__CHAT_MODEL_CMD_PROMPT__) {
|
||||
searchInput.value = window.__CHAT_MODEL_CMD_PROMPT__;
|
||||
modelDom.innerHTML = '';
|
||||
delete window.__CHAT_MODEL_CMD_PROMPT__;
|
||||
delete window.__CHAT_MODEL_CMD__;
|
||||
delete window.__CHAT_MODEL_VAR__;
|
||||
}
|
||||
});
|
||||
|
||||
searchInput.addEventListener('input', (event) => {
|
||||
if (window.__CHAT_MODEL_VAR__) return;
|
||||
|
||||
const query = searchInput.value;
|
||||
if (!query || !/^\//.test(query)) {
|
||||
modelDom.innerHTML = '';
|
||||
@ -102,18 +125,20 @@ async function cmdTip() {
|
||||
|
||||
// all cmd result
|
||||
if (query === '/') {
|
||||
const result = data.filter(i => i.enable);
|
||||
modelDom.innerHTML = `<div>${result.map(itemDom).join('')}</div>`;
|
||||
window.__CHAT_MODEL_CMD__ = result[0]?.prompt.trim();
|
||||
modelDom.innerHTML = `<div>${data.map(itemDom).join('')}</div>`;
|
||||
window.__CHAT_MODEL_CMD_PROMPT__ = data[0]?.prompt.trim();
|
||||
window.__CHAT_MODEL_CMD__ = data[0]?.cmd.trim();
|
||||
return;
|
||||
}
|
||||
|
||||
const result = data.filter(i => i.enable && new RegExp(query.substring(1)).test(i.cmd));
|
||||
const result = data.filter(i => new RegExp(query.substring(1)).test(i.cmd));
|
||||
if (result.length > 0) {
|
||||
modelDom.innerHTML = `<div>${result.map(itemDom).join('')}</div>`;
|
||||
window.__CHAT_MODEL_CMD__ = result[0]?.prompt.trim();
|
||||
window.__CHAT_MODEL_CMD_PROMPT__ = result[0]?.prompt.trim();
|
||||
window.__CHAT_MODEL_CMD__ = result[0]?.cmd.trim();
|
||||
} else {
|
||||
modelDom.innerHTML = '';
|
||||
delete window.__CHAT_MODEL_CMD_PROMPT__;
|
||||
delete window.__CHAT_MODEL_CMD__;
|
||||
}
|
||||
}, {
|
||||
@ -136,7 +161,7 @@ async function cmdTip() {
|
||||
const val = decodeURIComponent(item.getAttribute('data-prompt'));
|
||||
searchInput.value = val;
|
||||
document.querySelector('form textarea').focus();
|
||||
window.__CHAT_MODEL_CMD__ = val;
|
||||
window.__CHAT_MODEL_CMD_PROMPT__ = val;
|
||||
modelDom.innerHTML = '';
|
||||
}
|
||||
}, {
|
||||
|
@ -15,8 +15,7 @@ use tauri_plugin_log::{
|
||||
LogTarget, LoggerBuilder,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
fn main() {
|
||||
ChatConfJson::init();
|
||||
let chat_conf = ChatConfJson::get_chat_conf();
|
||||
let context = tauri::generate_context!();
|
||||
@ -58,9 +57,10 @@ async fn main() {
|
||||
cmd::form_confirm,
|
||||
cmd::form_msg,
|
||||
cmd::open_file,
|
||||
cmd::get_chat_model,
|
||||
cmd::get_chat_model_cmd,
|
||||
cmd::parse_prompt,
|
||||
cmd::window_reload,
|
||||
cmd::cmd_list,
|
||||
fs_extra::metadata,
|
||||
])
|
||||
.setup(setup::init)
|
||||
|
34
src/hooks/useChatModel.ts
vendored
34
src/hooks/useChatModel.ts
vendored
@ -2,46 +2,52 @@ import { useState, useEffect } from 'react';
|
||||
import { clone } from 'lodash';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
|
||||
import { CHAT_MODEL_JSON, readJSON, writeJSON } from '@/utils';
|
||||
import { CHAT_MODEL_JSON, CHAT_MODEL_CMD_JSON, readJSON, writeJSON } from '@/utils';
|
||||
import useInit from '@/hooks/useInit';
|
||||
|
||||
export default function useChatModel(key: string, file = CHAT_MODEL_JSON) {
|
||||
const [modelJson, setModelJson] = useState<Record<string, any>>([]);
|
||||
const [modelJson, setModelJson] = useState<Record<string, any>>({});
|
||||
|
||||
useInit(async () => {
|
||||
const data = await readJSON(file, {
|
||||
defaultVal: { name: 'ChatGPT Model', [key]: [] },
|
||||
defaultVal: { name: 'ChatGPT Model', [key]: null },
|
||||
});
|
||||
setModelJson(data);
|
||||
});
|
||||
|
||||
const modelSet = async (data: Record<string, any>[]) => {
|
||||
const modelSet = async (data: Record<string, any>[]|Record<string, any>) => {
|
||||
const oData = clone(modelJson);
|
||||
oData[key] = data;
|
||||
await writeJSON(file, oData);
|
||||
await invoke('window_reload', { label: 'core' });
|
||||
setModelJson(oData);
|
||||
}
|
||||
|
||||
return { modelJson, modelSet, modelData: modelJson?.[key] || [] };
|
||||
}
|
||||
|
||||
export function useCacheModel(file: string) {
|
||||
const [modelJson, setModelJson] = useState<Record<string, any>[]>([]);
|
||||
export function useCacheModel(file = '') {
|
||||
const [modelCacheJson, setModelCacheJson] = useState<Record<string, any>[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!file) return;
|
||||
(async () => {
|
||||
const data = await readJSON(file, { isRoot: true });
|
||||
setModelJson(data);
|
||||
const data = await readJSON(file, { isRoot: true, isList: true });
|
||||
setModelCacheJson(data);
|
||||
})();
|
||||
}, [file]);
|
||||
|
||||
const modelSet = async (data: Record<string, any>[]) => {
|
||||
await writeJSON(file, data, { isRoot: true });
|
||||
await invoke('window_reload', { label: 'core' });
|
||||
setModelJson(data);
|
||||
const modelCacheSet = async (data: Record<string, any>[], newFile = '') => {
|
||||
await writeJSON(newFile ? newFile : file, data, { isRoot: true });
|
||||
setModelCacheJson(data);
|
||||
await modelCacheCmd();
|
||||
}
|
||||
|
||||
return { modelJson, modelSet };
|
||||
const modelCacheCmd = async () => {
|
||||
// Generate the `chat.model.cmd.json` file and refresh the page for the slash command to take effect.
|
||||
const list = await invoke('cmd_list');
|
||||
await writeJSON(CHAT_MODEL_CMD_JSON, { name: 'ChatGPT CMD', last_updated: Date.now(), data: list });
|
||||
await invoke('window_reload', { label: 'core' });
|
||||
};
|
||||
|
||||
return { modelCacheJson, modelCacheSet, modelCacheCmd };
|
||||
}
|
3
src/hooks/useData.ts
vendored
3
src/hooks/useData.ts
vendored
@ -17,6 +17,9 @@ export default function useData(oData: any[]) {
|
||||
};
|
||||
|
||||
const opInit = (val: any[] = []) => {
|
||||
if (!val || !Array.isArray(val)) return;
|
||||
console.log('«20» /src/hooks/useData.ts ~> ', val);
|
||||
|
||||
const nData = val.map(i => ({ [safeKey]: v4(), ...i }));
|
||||
setData(nData);
|
||||
};
|
||||
|
2
src/hooks/useEvent.ts
vendored
2
src/hooks/useEvent.ts
vendored
@ -5,7 +5,7 @@ import useChatModel from '@/hooks/useChatModel';
|
||||
import { GITHUB_PROMPTS_CSV_URL, chatPromptsPath, genCmd } from '@/utils';
|
||||
|
||||
export default function useEvent() {
|
||||
const { modelSet } = useChatModel('sys_sync_prompts');
|
||||
const { modelSet } = useChatModel('sync_prompts');
|
||||
// Using `emit` and `listen` will be triggered multiple times in development mode.
|
||||
// So here we use `eval` to call `__sync_prompt`
|
||||
useInit(() => {
|
||||
|
41
src/main.scss
vendored
41
src/main.scss
vendored
@ -23,3 +23,44 @@ html, body {
|
||||
width: 50px !important;
|
||||
min-width: 50px !important;
|
||||
}
|
||||
|
||||
.chat-prompts-val {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.chat-add-btn {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.chat-prompts-tags {
|
||||
.ant-tag {
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-sync-path {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #888;
|
||||
margin-bottom: 5px;
|
||||
line-height: 16px;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
// background-color: #d8d8d8;
|
||||
color: #4096ff;
|
||||
padding: 0 8px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
15
src/utils.ts
vendored
15
src/utils.ts
vendored
@ -3,7 +3,7 @@ import { homeDir, join, dirname } from '@tauri-apps/api/path';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export const CHAT_MODEL_JSON = 'chat.model.json';
|
||||
export const CHAT_MODEL_SYNC_JSON = 'chat.model.sync.json';
|
||||
export const CHAT_MODEL_CMD_JSON = 'chat.model.cmd.json';
|
||||
export const CHAT_PROMPTS_CSV = 'chat.prompts.csv';
|
||||
export const GITHUB_PROMPTS_CSV_URL = 'https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv';
|
||||
export const DISABLE_AUTO_COMPLETE = {
|
||||
@ -20,22 +20,23 @@ export const chatModelPath = async (): Promise<string> => {
|
||||
return join(await chatRoot(), CHAT_MODEL_JSON);
|
||||
}
|
||||
|
||||
export const chatModelSyncPath = async (): Promise<string> => {
|
||||
return join(await chatRoot(), CHAT_MODEL_SYNC_JSON);
|
||||
}
|
||||
// export const chatModelSyncPath = async (): Promise<string> => {
|
||||
// return join(await chatRoot(), CHAT_MODEL_SYNC_JSON);
|
||||
// }
|
||||
|
||||
export const chatPromptsPath = async (): Promise<string> => {
|
||||
return join(await chatRoot(), CHAT_PROMPTS_CSV);
|
||||
}
|
||||
|
||||
type readJSONOpts = { defaultVal?: Record<string, any>, isRoot?: boolean };
|
||||
type readJSONOpts = { defaultVal?: Record<string, any>, isRoot?: boolean, isList?: boolean };
|
||||
export const readJSON = async (path: string, opts: readJSONOpts = {}) => {
|
||||
const { defaultVal = {}, isRoot = false } = opts;
|
||||
const { defaultVal = {}, isRoot = false, isList = false } = opts;
|
||||
const root = await chatRoot();
|
||||
const file = await join(isRoot ? '' : root, path);
|
||||
|
||||
if (!await exists(file)) {
|
||||
writeTextFile(file, JSON.stringify({
|
||||
await createDir(await dirname(file), { recursive: true });
|
||||
await writeTextFile(file, isList ? '[]' : JSON.stringify({
|
||||
name: 'ChatGPT',
|
||||
link: 'https://github.com/lencx/ChatGPT',
|
||||
...defaultVal,
|
||||
|
28
src/view/model/SyncCustom/index.scss
vendored
28
src/view/model/SyncCustom/index.scss
vendored
@ -1,28 +0,0 @@
|
||||
.chat-prompts-tags {
|
||||
.ant-tag {
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.chat-model-path {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #888;
|
||||
margin-bottom: 5px;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
// background-color: #d8d8d8;
|
||||
color: #4096ff;
|
||||
padding: 0 8px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
22
src/view/model/SyncCustom/index.tsx
vendored
22
src/view/model/SyncCustom/index.tsx
vendored
@ -3,19 +3,19 @@ import { Table, Modal, Button, message } from 'antd';
|
||||
import { invoke, http, path, fs } from '@tauri-apps/api';
|
||||
|
||||
import useData from '@/hooks/useData';
|
||||
import useChatModel from '@/hooks/useChatModel';
|
||||
import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
|
||||
import useColumns from '@/hooks/useColumns';
|
||||
import { TABLE_PAGINATION } from '@/hooks/useTable';
|
||||
import { CHAT_MODEL_SYNC_JSON, chatRoot, writeJSON, readJSON, genCmd } from '@/utils';
|
||||
import { CHAT_MODEL_JSON, chatRoot, readJSON, genCmd } from '@/utils';
|
||||
import { syncColumns, getPath } from './config';
|
||||
import SyncForm from './Form';
|
||||
import './index.scss';
|
||||
|
||||
const setTag = (data: Record<string, any>[]) => data.map((i) => ({ ...i, tags: ['user-sync'], enable: true }))
|
||||
|
||||
export default function SyncCustom() {
|
||||
const [isVisible, setVisible] = useState(false);
|
||||
const { modelData, modelSet } = useChatModel('sync_url', CHAT_MODEL_SYNC_JSON);
|
||||
const { modelData, modelSet } = useChatModel('sync_custom', CHAT_MODEL_JSON);
|
||||
const { modelCacheCmd, modelCacheSet } = useCacheModel();
|
||||
const { opData, opInit, opAdd, opRemove, opReplace, opSafeKey } = useData([]);
|
||||
const { columns, ...opInfo } = useColumns(syncColumns());
|
||||
const formRef = useRef<any>(null);
|
||||
@ -53,7 +53,7 @@ export default function SyncCustom() {
|
||||
const handleSync = async (filename: string) => {
|
||||
const record = opInfo?.opRecord;
|
||||
const isJson = /json$/.test(record?.ext);
|
||||
const file = await path.join(await chatRoot(), 'cache_sync', filename);
|
||||
const file = await path.join(await chatRoot(), 'cache_model', filename);
|
||||
const filePath = await getPath(record);
|
||||
|
||||
// https or http
|
||||
@ -65,13 +65,14 @@ export default function SyncCustom() {
|
||||
if (res.ok) {
|
||||
if (isJson) {
|
||||
// parse json
|
||||
writeJSON(file, setTag(Array.isArray(res?.data) ? res?.data : []), { isRoot: true, dir: 'cache_sync' });
|
||||
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 writeJSON(file, fmtList, { isRoot: true, dir: 'cache_sync' });
|
||||
await modelCacheSet(fmtList, file);
|
||||
}
|
||||
await modelCacheCmd();
|
||||
message.success('ChatGPT Prompts data has been synchronized!');
|
||||
} else {
|
||||
message.error('ChatGPT Prompts data sync failed, please try again!');
|
||||
@ -82,14 +83,15 @@ export default function SyncCustom() {
|
||||
if (isJson) {
|
||||
// parse json
|
||||
const data = await readJSON(filePath, { isRoot: true });
|
||||
await writeJSON(file, setTag(Array.isArray(data) ? data : []), { isRoot: true, dir: 'cache_sync' });
|
||||
await modelCacheSet(setTag(Array.isArray(data) ? data : []), file);
|
||||
} else {
|
||||
// parse csv
|
||||
const data = await fs.readTextFile(filePath);
|
||||
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 writeJSON(file, fmtList, { isRoot: true, dir: 'cache_sync' });
|
||||
await modelCacheSet(fmtList, file);
|
||||
}
|
||||
await modelCacheCmd();
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
@ -109,7 +111,7 @@ export default function SyncCustom() {
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
className="add-btn"
|
||||
className="chat-add-btn"
|
||||
type="primary"
|
||||
onClick={opInfo.opNew}
|
||||
>
|
||||
|
29
src/view/model/SyncPrompts/index.scss
vendored
29
src/view/model/SyncPrompts/index.scss
vendored
@ -1,13 +1,3 @@
|
||||
.chat-prompts-tags {
|
||||
.ant-tag {
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.chat-table-tip, .chat-table-btns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@ -20,22 +10,3 @@
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-model-path {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #888;
|
||||
margin-bottom: 5px;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
// background-color: #d8d8d8;
|
||||
color: #4096ff;
|
||||
padding: 0 8px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
57
src/view/model/SyncPrompts/index.tsx
vendored
57
src/view/model/SyncPrompts/index.tsx
vendored
@ -1,14 +1,13 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Table, Button, message, Popconfirm } from 'antd';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { fetch, ResponseType } from '@tauri-apps/api/http';
|
||||
import { writeTextFile } from '@tauri-apps/api/fs';
|
||||
import { invoke, http, path, shell } from '@tauri-apps/api';
|
||||
|
||||
import useColumns from '@/hooks/useColumns';
|
||||
import useInit from '@/hooks/useInit';
|
||||
import useData from '@/hooks/useData';
|
||||
import useChatModel from '@/hooks/useChatModel';
|
||||
import useColumns from '@/hooks/useColumns';
|
||||
import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
|
||||
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
|
||||
import { fmtDate, chatPromptsPath, GITHUB_PROMPTS_CSV_URL, genCmd } from '@/utils';
|
||||
import { fmtDate, chatRoot, GITHUB_PROMPTS_CSV_URL, genCmd } from '@/utils';
|
||||
import { syncColumns } from './config';
|
||||
import './index.scss';
|
||||
|
||||
@ -16,36 +15,39 @@ const promptsURL = 'https://github.com/f/awesome-chatgpt-prompts/blob/main/promp
|
||||
|
||||
export default function SyncPrompts() {
|
||||
const { rowSelection, selectedRowIDs } = useTable();
|
||||
const [lastUpdated, setLastUpdated] = useState();
|
||||
const { modelJson, modelSet } = useChatModel('sys_sync_prompts');
|
||||
const [jsonPath, setJsonPath] = useState('');
|
||||
const { modelJson, modelSet } = useChatModel('sync_prompts');
|
||||
const { modelCacheJson, modelCacheSet } = useCacheModel(jsonPath);
|
||||
const { opData, opInit, opReplace, opReplaceItems, opSafeKey } = useData([]);
|
||||
const { columns, ...opInfo } = useColumns(syncColumns());
|
||||
|
||||
const lastUpdated = modelJson?.sync_prompts?.last_updated;
|
||||
const selectedItems = rowSelection.selectedRowKeys || [];
|
||||
|
||||
useInit(async () => {
|
||||
setJsonPath(await path.join(await chatRoot(), 'cache_model', 'chatgpt_prompts.json'));
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!modelJson?.sys_sync_prompts) return;
|
||||
opInit(modelJson?.sys_sync_prompts);
|
||||
if (lastUpdated) return;
|
||||
(async () => {
|
||||
const fileData: Record<string, any> = await invoke('metadata', { path: await chatPromptsPath() });
|
||||
setLastUpdated(fileData.accessedAtMs);
|
||||
})();
|
||||
}, [modelJson?.sys_sync_prompts])
|
||||
if (modelCacheJson.length <= 0) return;
|
||||
opInit(modelCacheJson);
|
||||
}, [modelCacheJson.length]);
|
||||
|
||||
const handleSync = async () => {
|
||||
const res = await fetch(GITHUB_PROMPTS_CSV_URL, {
|
||||
const res = await http.fetch(GITHUB_PROMPTS_CSV_URL, {
|
||||
method: 'GET',
|
||||
responseType: ResponseType.Text,
|
||||
responseType: http.ResponseType.Text,
|
||||
});
|
||||
const data = (res.data || '') as string;
|
||||
if (res.ok) {
|
||||
// const content = data.replace(/"(\s+)?,(\s+)?"/g, '","');
|
||||
await writeTextFile(await chatPromptsPath(), data);
|
||||
const list: Record<string, string>[] = await invoke('parse_prompt', { data });
|
||||
opInit(list);
|
||||
modelSet(list.map(i => ({ ...i, cmd: i.cmd ? i.cmd : genCmd(i.act), enable: true, tags: ['chatgpt-prompts'] })));
|
||||
setLastUpdated(fmtDate(Date.now()) as any);
|
||||
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({
|
||||
id: 'chatgpt_prompts',
|
||||
last_updated: Date.now(),
|
||||
});
|
||||
message.success('ChatGPT Prompts data has been synchronized!');
|
||||
} else {
|
||||
message.error('ChatGPT Prompts data sync failed, please try again!');
|
||||
@ -55,13 +57,13 @@ export default function SyncPrompts() {
|
||||
useEffect(() => {
|
||||
if (opInfo.opType === 'enable') {
|
||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord);
|
||||
modelSet(data);
|
||||
modelCacheSet(data);
|
||||
}
|
||||
}, [opInfo.opTime]);
|
||||
|
||||
const handleEnable = (isEnable: boolean) => {
|
||||
const data = opReplaceItems(selectedRowIDs, { enable: isEnable })
|
||||
modelSet(data);
|
||||
modelCacheSet(data);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -87,7 +89,10 @@ export default function SyncPrompts() {
|
||||
</Popconfirm>
|
||||
</div>
|
||||
<div className="chat-table-tip">
|
||||
<span className="chat-model-path">URL: <a href={promptsURL} target="_blank" title={promptsURL}>f/awesome-chatgpt-prompts/prompts.csv</a></span>
|
||||
<div className="chat-sync-path">
|
||||
<div>PATH: <a onClick={() => shell.open(promptsURL)} target="_blank" title={promptsURL}>f/awesome-chatgpt-prompts/prompts.csv</a></div>
|
||||
<div>CACHE: <a onClick={() => shell.open(jsonPath)} target="_blank" title={jsonPath}>{jsonPath}</a></div>
|
||||
</div>
|
||||
{lastUpdated && <span style={{ marginLeft: 10, color: '#888', fontSize: 12 }}>Last updated on {fmtDate(lastUpdated)}</span>}
|
||||
</div>
|
||||
<Table
|
||||
|
42
src/view/model/SyncRecord/index.scss
vendored
42
src/view/model/SyncRecord/index.scss
vendored
@ -1,42 +0,0 @@
|
||||
// .chat-prompts-tags {
|
||||
// .ant-tag {
|
||||
// margin: 2px;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .add-btn {
|
||||
// margin-bottom: 5px;
|
||||
// }
|
||||
|
||||
// .chat-table-tip, .chat-table-btns {
|
||||
// display: flex;
|
||||
// justify-content: space-between;
|
||||
// }
|
||||
|
||||
// .chat-table-btns {
|
||||
// margin-bottom: 5px;
|
||||
|
||||
// .num {
|
||||
// margin-left: 10px;
|
||||
// }
|
||||
// }
|
||||
|
||||
.chat-sync-path {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #888;
|
||||
margin-bottom: 5px;
|
||||
line-height: 16px;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
// background-color: #d8d8d8;
|
||||
color: #4096ff;
|
||||
padding: 0 8px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
15
src/view/model/SyncRecord/index.tsx
vendored
15
src/view/model/SyncRecord/index.tsx
vendored
@ -12,7 +12,6 @@ import { fmtDate, chatRoot } from '@/utils';
|
||||
import { getPath } from '@/view/model/SyncCustom/config';
|
||||
import { syncColumns } from './config';
|
||||
import useInit from '@/hooks/useInit';
|
||||
import './index.scss';
|
||||
|
||||
export default function SyncRecord() {
|
||||
const location = useLocation();
|
||||
@ -21,7 +20,7 @@ export default function SyncRecord() {
|
||||
const state = location?.state;
|
||||
|
||||
const { rowSelection, selectedRowIDs } = useTable();
|
||||
const { modelJson, modelSet } = useCacheModel(jsonPath);
|
||||
const { modelCacheJson, modelCacheSet } = useCacheModel(jsonPath);
|
||||
const { opData, opInit, opReplace, opReplaceItems, opSafeKey } = useData([]);
|
||||
const { columns, ...opInfo } = useColumns(syncColumns());
|
||||
|
||||
@ -29,24 +28,24 @@ export default function SyncRecord() {
|
||||
|
||||
useInit(async () => {
|
||||
setFilePath(await getPath(state));
|
||||
setJsonPath(await path.join(await chatRoot(), 'cache_sync', `${state?.id}.json`));
|
||||
setJsonPath(await path.join(await chatRoot(), 'cache_model', `${state?.id}.json`));
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (modelJson.length <= 0) return;
|
||||
opInit(modelJson);
|
||||
}, [modelJson.length]);
|
||||
if (modelCacheJson.length <= 0) return;
|
||||
opInit(modelCacheJson);
|
||||
}, [modelCacheJson.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (opInfo.opType === 'enable') {
|
||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord);
|
||||
modelSet(data);
|
||||
modelCacheSet(data);
|
||||
}
|
||||
}, [opInfo.opTime]);
|
||||
|
||||
const handleEnable = (isEnable: boolean) => {
|
||||
const data = opReplaceItems(selectedRowIDs, { enable: isEnable })
|
||||
modelSet(data);
|
||||
modelCacheSet(data);
|
||||
};
|
||||
|
||||
return (
|
||||
|
39
src/view/model/UserCustom/index.scss
vendored
39
src/view/model/UserCustom/index.scss
vendored
@ -1,39 +0,0 @@
|
||||
.chat-prompts-val {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.chat-prompts-tags {
|
||||
.ant-tag {
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.chat-model-path {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #888;
|
||||
margin-bottom: 5px;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
// background-color: #d8d8d8;
|
||||
color: #4096ff;
|
||||
padding: 0 8px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
89
src/view/model/UserCustom/index.tsx
vendored
89
src/view/model/UserCustom/index.tsx
vendored
@ -1,29 +1,36 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { Table, Button, Modal, message } from 'antd';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { shell, path } from '@tauri-apps/api';
|
||||
|
||||
import useInit from '@/hooks/useInit';
|
||||
import useData from '@/hooks/useData';
|
||||
import useChatModel from '@/hooks/useChatModel';
|
||||
import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
|
||||
import useColumns from '@/hooks/useColumns';
|
||||
import { TABLE_PAGINATION } from '@/hooks/useTable';
|
||||
import { chatModelPath } from '@/utils';
|
||||
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
|
||||
import { chatRoot, fmtDate } from '@/utils';
|
||||
import { modelColumns } from './config';
|
||||
import UserCustomForm from './Form';
|
||||
import './index.scss';
|
||||
|
||||
export default function LanguageModel() {
|
||||
const { rowSelection, selectedRowIDs } = useTable();
|
||||
const [isVisible, setVisible] = useState(false);
|
||||
const [modelPath, setChatModelPath] = useState('');
|
||||
const { modelData, modelSet } = useChatModel('user_custom');
|
||||
const { opData, opInit, opAdd, opRemove, opReplace, opSafeKey } = useData([]);
|
||||
const [jsonPath, setJsonPath] = useState('');
|
||||
const { modelJson, modelSet } = useChatModel('user_custom');
|
||||
const { modelCacheJson, modelCacheSet } = useCacheModel(jsonPath);
|
||||
const { opData, opInit, opReplaceItems, opAdd, opRemove, opReplace, opSafeKey } = useData([]);
|
||||
const { columns, ...opInfo } = useColumns(modelColumns());
|
||||
const lastUpdated = modelJson?.user_custom?.last_updated;
|
||||
const selectedItems = rowSelection.selectedRowKeys || [];
|
||||
const formRef = useRef<any>(null);
|
||||
|
||||
useInit(async () => {
|
||||
setJsonPath(await path.join(await chatRoot(), 'cache_model', 'user_custom.json'));
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (modelData.length <= 0) return;
|
||||
opInit(modelData);
|
||||
}, [modelData]);
|
||||
if (modelCacheJson.length <= 0) return;
|
||||
opInit(modelCacheJson);
|
||||
}, [modelCacheJson.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!opInfo.opType) return;
|
||||
@ -32,7 +39,7 @@ export default function LanguageModel() {
|
||||
}
|
||||
if (['delete'].includes(opInfo.opType)) {
|
||||
const data = opRemove(opInfo?.opRecord?.[opSafeKey]);
|
||||
modelSet(data);
|
||||
modelCacheSet(data);
|
||||
opInfo.resetRecord();
|
||||
}
|
||||
}, [opInfo.opType, formRef]);
|
||||
@ -40,14 +47,22 @@ export default function LanguageModel() {
|
||||
useEffect(() => {
|
||||
if (opInfo.opType === 'enable') {
|
||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord);
|
||||
modelSet(data);
|
||||
modelCacheSet(data);
|
||||
}
|
||||
}, [opInfo.opTime])
|
||||
|
||||
useInit(async () => {
|
||||
const path = await chatModelPath();
|
||||
setChatModelPath(path);
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (opInfo.opType === 'enable') {
|
||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord);
|
||||
modelCacheSet(data);
|
||||
}
|
||||
}, [opInfo.opTime]);
|
||||
|
||||
const handleEnable = (isEnable: boolean) => {
|
||||
const data = opReplaceItems(selectedRowIDs, { enable: isEnable })
|
||||
modelCacheSet(data);
|
||||
};
|
||||
|
||||
const hide = () => {
|
||||
setVisible(false);
|
||||
@ -56,8 +71,8 @@ export default function LanguageModel() {
|
||||
|
||||
const handleOk = () => {
|
||||
formRef.current?.form?.validateFields()
|
||||
.then((vals: Record<string, any>) => {
|
||||
if (modelData.map((i: any) => i.cmd).includes(vals.cmd) && opInfo?.opRecord?.cmd !== vals.cmd) {
|
||||
.then(async (vals: Record<string, any>) => {
|
||||
if (modelCacheJson.map((i: any) => i.cmd).includes(vals.cmd) && opInfo?.opRecord?.cmd !== vals.cmd) {
|
||||
message.warning(`"cmd: /${vals.cmd}" already exists, please change the "${vals.cmd}" name and resubmit.`);
|
||||
return;
|
||||
}
|
||||
@ -67,28 +82,46 @@ export default function LanguageModel() {
|
||||
case 'edit': data = opReplace(opInfo?.opRecord?.[opSafeKey], vals); break;
|
||||
default: break;
|
||||
}
|
||||
modelSet(data);
|
||||
opInfo.setExtra(Date.now());
|
||||
await modelCacheSet(data);
|
||||
opInit(data);
|
||||
modelSet({
|
||||
id: 'user_custom',
|
||||
last_updated: Date.now(),
|
||||
});
|
||||
hide();
|
||||
})
|
||||
};
|
||||
|
||||
const handleOpenFile = () => {
|
||||
invoke('open_file', { path: modelPath });
|
||||
};
|
||||
|
||||
const modalTitle = `${({ new: 'Create', edit: 'Edit' })[opInfo.opType]} Model`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button className="add-btn" type="primary" onClick={opInfo.opNew}>Add Model</Button>
|
||||
<div className="chat-model-path">PATH: <span onClick={handleOpenFile}>{modelPath}</span></div>
|
||||
<div className="chat-table-btns">
|
||||
<Button className="chat-add-btn" type="primary" onClick={opInfo.opNew}>Add Model</Button>
|
||||
<div>
|
||||
{selectedItems.length > 0 && (
|
||||
<>
|
||||
<Button type="primary" onClick={() => handleEnable(true)}>Enable</Button>
|
||||
<Button onClick={() => handleEnable(false)}>Disable</Button>
|
||||
<span className="num">Selected {selectedItems.length} items</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="chat-model-path">PATH: <span onClick={handleOpenFile}>{modelPath}</span></div> */}
|
||||
<div className="chat-table-tip">
|
||||
<div className="chat-sync-path">
|
||||
<div>CACHE: <a onClick={() => shell.open(jsonPath)} title={jsonPath}>{jsonPath}</a></div>
|
||||
</div>
|
||||
{lastUpdated && <span style={{ marginLeft: 10, color: '#888', fontSize: 12 }}>Last updated on {fmtDate(lastUpdated)}</span>}
|
||||
</div>
|
||||
<Table
|
||||
key={opInfo.opExtra}
|
||||
key={lastUpdated}
|
||||
rowKey="cmd"
|
||||
columns={columns}
|
||||
scroll={{ x: 'auto' }}
|
||||
dataSource={opData}
|
||||
rowSelection={rowSelection}
|
||||
pagination={TABLE_PAGINATION}
|
||||
/>
|
||||
<Modal
|
||||
|
Loading…
Reference in New Issue
Block a user