diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 04ce341..52086f4 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -10,19 +10,32 @@ mod utils; use app::{cmd, fs_extra, menu, setup}; use conf::{ChatConfJson, ChatState}; use tauri::api::path; -use tauri_plugin_log::{fern::colors::ColoredLevelConfig, LogTarget, LoggerBuilder}; +use tauri_plugin_log::{ + fern::colors::{Color, ColoredLevelConfig}, + LogTarget, LoggerBuilder, +}; fn main() { ChatConfJson::init(); let chat_conf = ChatConfJson::get_chat_conf(); let context = tauri::generate_context!(); - let colors = ColoredLevelConfig::default(); + let colors = ColoredLevelConfig { + error: Color::Red, + warn: Color::Yellow, + debug: Color::Blue, + info: Color::BrightGreen, + trace: Color::Cyan, + }; tauri::Builder::default() // https://github.com/tauri-apps/tauri/pull/2736 .plugin( LoggerBuilder::new() - // .level(log::LevelFilter::Error) + .level(if cfg!(debug_assertions) { + log::LevelFilter::Debug + } else { + log::LevelFilter::Trace + }) .with_colors(colors) .targets([ // LogTarget::LogDir, diff --git a/src/hooks/useData.ts b/src/hooks/useData.ts index be70c6e..38c7630 100644 --- a/src/hooks/useData.ts +++ b/src/hooks/useData.ts @@ -7,9 +7,8 @@ export default function useData(oData: any[]) { const [opData, setData] = useState([]); useEffect(() => { - const nData = oData.map(i => ({ [safeKey]: v4(), ...i })); - setData(nData); - }, [oData]) + opInit(oData); + }, []) const opAdd = (val: any) => { const v = [val, ...opData]; @@ -17,6 +16,11 @@ export default function useData(oData: any[]) { return v; }; + const opInit = (val: any[] = []) => { + const nData = val.map(i => ({ [safeKey]: v4(), ...i })); + setData(nData); + }; + const opRemove = (id: string) => { const nData = opData.filter(i => i[safeKey] !== id); setData(nData); @@ -31,5 +35,5 @@ export default function useData(oData: any[]) { return nData; }; - return { opSafeKey: safeKey, opReplace, opAdd, opRemove, opData }; + return { opSafeKey: safeKey, opInit, opReplace, opAdd, opRemove, opData }; } \ No newline at end of file diff --git a/src/layout/index.scss b/src/layout/index.scss index 338149f..9c0a917 100644 --- a/src/layout/index.scss +++ b/src/layout/index.scss @@ -19,6 +19,8 @@ } .ant-menu { + user-select: none; + -webkit-user-select: none; .ant-menu-item { background-color: #f8f8f8; } diff --git a/src/layout/index.tsx b/src/layout/index.tsx index e799981..a617f89 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -34,7 +34,13 @@ const ChatLayout: FC = ({ children }) => { }} >
- go(i.key)} /> + go(i.key)} + /> = [ +type ChatRouteObject = { + path: string; + element?: JSX.Element; + meta: ChatRouteMetaObject; + children?: ChatRouteObject[]; +} + +export const routes: Array = [ { path: '/', element: , @@ -27,19 +34,28 @@ export const routes: Array = [ }, { path: '/language-model', - element: , meta: { label: 'Language Model', icon: , }, - }, - { - path: '/sync-prompts', - element: , - meta: { - label: 'Sync Prompts', - icon: , - }, + children: [ + { + path: 'user-custom', + element: , + meta: { + label: 'User Custom', + icon: , + }, + }, + { + path: 'sync-prompts', + element: , + meta: { + label: 'Sync Prompts', + icon: , + }, + }, + ] }, ]; @@ -47,6 +63,8 @@ type MenuItem = Required['items'][number]; export const menuItems: MenuItem[] = routes.map(i => ({ ...i.meta, key: i.path || '', + children: i?.children?.map((j) => + ({ ...j.meta, key: `${i.path}/${j.path}` || ''})), })); export default () => { diff --git a/src/view/LanguageModel/config.tsx b/src/view/LanguageModel/config.tsx index 4667f96..c023079 100644 --- a/src/view/LanguageModel/config.tsx +++ b/src/view/LanguageModel/config.tsx @@ -1,4 +1,4 @@ -import { Tag, Switch, Tooltip, Space } from 'antd'; +import { Tag, Switch, Tooltip, Space, Popconfirm } from 'antd'; export const modelColumns = () => [ { @@ -29,7 +29,9 @@ export const modelColumns = () => [ dataIndex: 'enable', key: 'enable', width: 80, - render: (v: boolean = false) => , + render: (v: boolean = false, row: Record, action: Record) => ( + action.setRecord({ ...row, enable: v }, 'enable')} /> + ), }, { title: 'Prompt', @@ -48,7 +50,14 @@ export const modelColumns = () => [ render: (_: any, row: any, actions: any) => ( actions.setRecord(row, 'edit')}>Edit - actions.setRecord(row, 'delete')}>Delete + actions.setRecord(row, 'delete')} + okText="Yes" + cancelText="No" + > + Delete + ), } diff --git a/src/view/LanguageModel/index.tsx b/src/view/LanguageModel/index.tsx index 871f19a..7c50279 100644 --- a/src/view/LanguageModel/index.tsx +++ b/src/view/LanguageModel/index.tsx @@ -3,9 +3,9 @@ import { Table, Button, Modal, message } from 'antd'; import { invoke } from '@tauri-apps/api'; import useInit from '@/hooks/useInit'; +import useData from '@/hooks/useData'; import useChatModel from '@/hooks/useChatModel'; import useColumns from '@/hooks/useColumns'; -import useData from '@/hooks/useData'; import { chatModelPath } from '@/utils'; import { modelColumns } from './config'; import LanguageModelForm from './Form'; @@ -15,10 +15,15 @@ export default function LanguageModel() { const [isVisible, setVisible] = useState(false); const [modelPath, setChatModelPath] = useState(''); const { modelData, modelSet } = useChatModel('user_custom'); - const { opData, opAdd, opRemove, opReplace, opSafeKey } = useData(modelData); + const { opData, opInit, opAdd, opRemove, opReplace, opSafeKey } = useData([]); const { columns, ...opInfo } = useColumns(modelColumns()); const formRef = useRef(null); + useEffect(() => { + if (modelData.length <= 0) return; + opInit(modelData); + }, [modelData]) + useEffect(() => { if (!opInfo.opType) return; if (['edit', 'new'].includes(opInfo.opType)) { @@ -31,6 +36,13 @@ export default function LanguageModel() { } }, [opInfo.opType, formRef]); + useEffect(() => { + if (opInfo.opType === 'enable') { + const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord); + modelSet(data); + } + }, [opInfo.opTime]) + useInit(async () => { const path = await chatModelPath(); setChatModelPath(path); diff --git a/src/view/SyncPrompts/config.tsx b/src/view/SyncPrompts/config.tsx index 8f12a4a..a53248a 100644 --- a/src/view/SyncPrompts/config.tsx +++ b/src/view/SyncPrompts/config.tsx @@ -1,4 +1,4 @@ -import { Tag } from 'antd'; +import { Switch, Tag, Tooltip } from 'antd'; export const genCmd = (act: string) => act.replace(/\s+|\/+/g, '_').replace(/[^\d\w]/g, '').toLocaleLowerCase(); @@ -26,20 +26,22 @@ export const modelColumns = () => [ // width: 150, render: () => chatgpt-prompts, }, - // { - // title: 'Enable', - // dataIndex: 'enable', - // key: 'enable', - // width: 80, - // render: (v: boolean = false) => , - // }, + { + title: 'Enable', + dataIndex: 'enable', + key: 'enable', + // width: 80, + render: (v: boolean = false, row: Record, action: Record) => ( + action.setRecord({ ...row, enable: v }, 'enable')} /> + ), + }, { title: 'Prompt', dataIndex: 'prompt', key: 'prompt', // width: 300, - // render: (v: string) => ( - // {v} - // ), + render: (v: string) => ( + {v} + ), }, ]; diff --git a/src/view/SyncPrompts/index.tsx b/src/view/SyncPrompts/index.tsx index 5452e53..6c49338 100644 --- a/src/view/SyncPrompts/index.tsx +++ b/src/view/SyncPrompts/index.tsx @@ -1,33 +1,42 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { Table, Button, message } from 'antd'; import { invoke } from '@tauri-apps/api'; import { fetch, ResponseType } from '@tauri-apps/api/http'; import { writeTextFile, readTextFile } from '@tauri-apps/api/fs'; +import useInit from '@/hooks/useInit'; import useColumns from '@/hooks/useColumns'; +import useData from '@/hooks/useData'; import useChatModel from '@/hooks/useChatModel'; import { fmtDate, chatPromptsPath, GITHUB_PROMPTS_CSV_URL } from '@/utils'; import { modelColumns, genCmd } from './config'; import './index.scss'; -import useInit from '@/hooks/useInit'; const promptsURL = 'https://github.com/f/awesome-chatgpt-prompts/blob/main/prompts.csv'; export default function LanguageModel() { const [loading, setLoading] = useState(false); const [lastUpdated, setLastUpdated] = useState(); - const { modelSet } = useChatModel('sys_sync_prompts'); - const [tableData, setTableData] = useState[]>([]); + const { modelJson, modelSet } = useChatModel('sys_sync_prompts'); + const { opData, opInit, opReplace, opSafeKey } = useData([]); const { columns, ...opInfo } = useColumns(modelColumns()); - useInit(async () => { - const filename = await chatPromptsPath(); - const data = await readTextFile(filename); - const list: Record[] = await invoke('parse_prompt', { data }); - const fileData: Record = await invoke('metadata', { path: filename }); - setLastUpdated(fileData.accessedAtMs); - setTableData(list); - }) + // useInit(async () => { + // // const filename = await chatPromptsPath(); + // // const data = await readTextFile(filename); + // // const list: Record[] = await invoke('parse_prompt', { data }); + // // const fileData: Record = await invoke('metadata', { path: filename }); + // // setLastUpdated(fileData.accessedAtMs); + // // opInit(list); + // console.log('«31» /view/SyncPrompts/index.tsx ~> ', modelJson); + + // opInit([]); + // }) + + useEffect(() => { + if (!modelJson?.sys_sync_prompts) return; + opInit(modelJson?.sys_sync_prompts) + }, [modelJson?.sys_sync_prompts]) const handleSync = async () => { setLoading(true); @@ -36,16 +45,27 @@ export default function LanguageModel() { responseType: ResponseType.Text, }); const data = (res.data || '') as string; - // const content = data.replace(/"(\s+)?,(\s+)?"/g, '","'); - await writeTextFile(await chatPromptsPath(), data); - const list: Record[] = await invoke('parse_prompt', { data }); - setTableData(list); - modelSet(list.map(i => ({ cmd: genCmd(i.act), enable: true, tags: ['chatgpt-prompts'], ...i }))); + if (res.ok) { + // const content = data.replace(/"(\s+)?,(\s+)?"/g, '","'); + await writeTextFile(await chatPromptsPath(), data); + const list: Record[] = await invoke('parse_prompt', { data }); + opInit(list); + modelSet(list.map(i => ({ cmd: genCmd(i.act), enable: true, tags: ['chatgpt-prompts'], ...i }))); + setLastUpdated(fmtDate(Date.now()) as any); + message.success('ChatGPT Prompts data has been synchronized!'); + } else { + message.error('ChatGPT Prompts data sync failed, please try again!'); + } setLoading(false); - setLastUpdated(fmtDate(Date.now()) as any); - message.success('ChatGPT Prompts data synchronization completed!'); }; + useEffect(() => { + if (opInfo.opType === 'enable') { + const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord); + modelSet(data); + } + }, [opInfo.opTime]); + return (
@@ -56,7 +76,7 @@ export default function LanguageModel() { rowKey="act" columns={columns} scroll={{ x: 'auto' }} - dataSource={tableData} + dataSource={opData} pagination={{ hideOnSinglePage: true, showSizeChanger: true,