mirror of
https://github.com/lencx/ChatGPT.git
synced 2024-10-01 01:06:13 -04:00
chore: sync record
This commit is contained in:
parent
6abe7c783e
commit
2be560e69a
26
src/hooks/useChatModel.ts
vendored
26
src/hooks/useChatModel.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { clone } from 'lodash';
|
import { clone } from 'lodash';
|
||||||
import { invoke } from '@tauri-apps/api';
|
import { invoke } from '@tauri-apps/api';
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ import { CHAT_MODEL_JSON, readJSON, writeJSON } from '@/utils';
|
|||||||
import useInit from '@/hooks/useInit';
|
import useInit from '@/hooks/useInit';
|
||||||
|
|
||||||
export default function useChatModel(key: string, file = CHAT_MODEL_JSON) {
|
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 () => {
|
useInit(async () => {
|
||||||
const data = await readJSON(file, {
|
const data = await readJSON(file, {
|
||||||
@ -23,5 +23,25 @@ export default function useChatModel(key: string, file = CHAT_MODEL_JSON) {
|
|||||||
setModelJson(oData);
|
setModelJson(oData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { modelJson, modelSet, modelData: modelJson?.[key] || [] }
|
return { modelJson, modelSet, modelData: modelJson?.[key] || [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCacheModel(file: string) {
|
||||||
|
const [modelJson, setModelJson] = useState<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!file) return;
|
||||||
|
(async () => {
|
||||||
|
const data = await readJSON(file, { isRoot: true });
|
||||||
|
setModelJson(data);
|
||||||
|
})();
|
||||||
|
}, [file]);
|
||||||
|
|
||||||
|
const modelSet = async (data: Record<string, any>[]) => {
|
||||||
|
await writeJSON(file, data, { isRoot: true });
|
||||||
|
await invoke('window_reload', { label: 'core' });
|
||||||
|
setModelJson(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { modelJson, modelSet };
|
||||||
}
|
}
|
26
src/routes.tsx
vendored
26
src/routes.tsx
vendored
@ -11,7 +11,8 @@ import type { MenuProps } from 'antd';
|
|||||||
import General from '@view/General';
|
import General from '@view/General';
|
||||||
import UserCustom from '@/view/model/UserCustom';
|
import UserCustom from '@/view/model/UserCustom';
|
||||||
import SyncPrompts from '@/view/model/SyncPrompts';
|
import SyncPrompts from '@/view/model/SyncPrompts';
|
||||||
import SyncMore from '@/view/model/SyncMore';
|
import SyncCustom from '@/view/model/SyncCustom';
|
||||||
|
import SyncRecord from '@/view/model/SyncRecord';
|
||||||
|
|
||||||
export type ChatRouteMetaObject = {
|
export type ChatRouteMetaObject = {
|
||||||
label: string;
|
label: string;
|
||||||
@ -21,7 +22,8 @@ export type ChatRouteMetaObject = {
|
|||||||
type ChatRouteObject = {
|
type ChatRouteObject = {
|
||||||
path: string;
|
path: string;
|
||||||
element?: JSX.Element;
|
element?: JSX.Element;
|
||||||
meta: ChatRouteMetaObject;
|
hideMenu?: boolean;
|
||||||
|
meta?: ChatRouteMetaObject;
|
||||||
children?: ChatRouteObject[];
|
children?: ChatRouteObject[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,23 +60,31 @@ export const routes: Array<ChatRouteObject> = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'sync-more',
|
path: 'sync-custom',
|
||||||
element: <SyncMore />,
|
element: <SyncCustom />,
|
||||||
meta: {
|
meta: {
|
||||||
label: 'Sync More',
|
label: 'Sync Custom',
|
||||||
icon: <FileSyncOutlined />,
|
icon: <FileSyncOutlined />,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'sync-custom/:id',
|
||||||
|
element: <SyncRecord />,
|
||||||
|
hideMenu: true,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
type MenuItem = Required<MenuProps>['items'][number];
|
type MenuItem = Required<MenuProps>['items'][number];
|
||||||
export const menuItems: MenuItem[] = routes.map(i => ({
|
export const menuItems: MenuItem[] = routes
|
||||||
|
.filter((j) => !j.hideMenu)
|
||||||
|
.map(i => ({
|
||||||
...i.meta,
|
...i.meta,
|
||||||
key: i.path || '',
|
key: i.path || '',
|
||||||
children: i?.children?.map((j) =>
|
children: i?.children
|
||||||
({ ...j.meta, key: `${i.path}/${j.path}` || ''})),
|
?.filter((j) => !j.hideMenu)
|
||||||
|
?.map((j) => ({ ...j.meta, key: `${i.path}/${j.path}` || ''})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Tag, Space, Popconfirm } from 'antd';
|
import { Tag, Space, Popconfirm } from 'antd';
|
||||||
|
import { HistoryOutlined } from '@ant-design/icons';
|
||||||
import { shell, path } from '@tauri-apps/api';
|
import { shell, path } from '@tauri-apps/api';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import useInit from '@/hooks/useInit';
|
import useInit from '@/hooks/useInit';
|
||||||
import { chatRoot, fmtDate } from '@/utils';
|
import { chatRoot, fmtDate } from '@/utils';
|
||||||
|
|
||||||
export const pathColumns = () => [
|
export const syncColumns = () => [
|
||||||
{
|
{
|
||||||
title: 'Name',
|
title: 'Name',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
@ -31,16 +33,22 @@ export const pathColumns = () => [
|
|||||||
dataIndex: 'last_updated',
|
dataIndex: 'last_updated',
|
||||||
key: 'last_updated',
|
key: 'last_updated',
|
||||||
width: 140,
|
width: 140,
|
||||||
render: fmtDate,
|
render: (v: number) => (
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<HistoryOutlined style={{ marginRight: 5, color: v ? '#52c41a' : '#ff4d4f' }} />
|
||||||
|
{ v ? fmtDate(v) : ''}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Action',
|
title: 'Action',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
width: 140,
|
width: 150,
|
||||||
render: (_: any, row: any, actions: any) => {
|
render: (_: any, row: any, actions: any) => {
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
<a onClick={() => actions.setRecord(row, 'sync')}>Sync</a>
|
<a onClick={() => actions.setRecord(row, 'sync')}>Sync</a>
|
||||||
|
{row.last_updated && <Link to={`${row.id}`} state={row}>View</Link>}
|
||||||
<a onClick={() => actions.setRecord(row, 'edit')}>Edit</a>
|
<a onClick={() => actions.setRecord(row, 'edit')}>Edit</a>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="Are you sure to delete this path?"
|
title="Are you sure to delete this path?"
|
@ -7,17 +7,17 @@ import useChatModel from '@/hooks/useChatModel';
|
|||||||
import useColumns from '@/hooks/useColumns';
|
import useColumns from '@/hooks/useColumns';
|
||||||
import { TABLE_PAGINATION } from '@/hooks/useTable';
|
import { TABLE_PAGINATION } from '@/hooks/useTable';
|
||||||
import { CHAT_MODEL_SYNC_JSON, chatRoot, writeJSON, readJSON, genCmd } from '@/utils';
|
import { CHAT_MODEL_SYNC_JSON, chatRoot, writeJSON, readJSON, genCmd } from '@/utils';
|
||||||
import { pathColumns, getPath } from './config';
|
import { syncColumns, getPath } from './config';
|
||||||
import SyncForm from './Form';
|
import SyncForm from './Form';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
const setTag = (data: Record<string, any>[]) => data.map((i) => ({ ...i, tags: ['user-sync'], enable: true }))
|
const setTag = (data: Record<string, any>[]) => data.map((i) => ({ ...i, tags: ['user-sync'], enable: true }))
|
||||||
|
|
||||||
export default function SyncMore() {
|
export default function SyncCustom() {
|
||||||
const [isVisible, setVisible] = useState(false);
|
const [isVisible, setVisible] = useState(false);
|
||||||
const { modelData, modelSet } = useChatModel('sync_url', CHAT_MODEL_SYNC_JSON);
|
const { modelData, modelSet } = useChatModel('sync_url', CHAT_MODEL_SYNC_JSON);
|
||||||
const { opData, opInit, opAdd, opRemove, opReplace, opSafeKey } = useData([]);
|
const { opData, opInit, opAdd, opRemove, opReplace, opSafeKey } = useData([]);
|
||||||
const { columns, ...opInfo } = useColumns(pathColumns());
|
const { columns, ...opInfo } = useColumns(syncColumns());
|
||||||
const formRef = useRef<any>(null);
|
const formRef = useRef<any>(null);
|
||||||
|
|
||||||
const hide = () => {
|
const hide = () => {
|
||||||
@ -36,8 +36,6 @@ export default function SyncMore() {
|
|||||||
const filename = `${opInfo?.opRecord?.id}.json`;
|
const filename = `${opInfo?.opRecord?.id}.json`;
|
||||||
handleSync(filename).then(() => {
|
handleSync(filename).then(() => {
|
||||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], { ...opInfo?.opRecord, last_updated: Date.now() });
|
const data = opReplace(opInfo?.opRecord?.[opSafeKey], { ...opInfo?.opRecord, last_updated: Date.now() });
|
||||||
console.log('«38» /model/SyncMore/index.tsx ~> ', data);
|
|
||||||
|
|
||||||
modelSet(data);
|
modelSet(data);
|
||||||
opInfo.resetRecord();
|
opInfo.resetRecord();
|
||||||
});
|
});
|
||||||
@ -103,10 +101,7 @@ export default function SyncMore() {
|
|||||||
case 'edit': data = opReplace(opInfo?.opRecord?.[opSafeKey], vals); break;
|
case 'edit': data = opReplace(opInfo?.opRecord?.[opSafeKey], vals); break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
console.log('«95» /model/SyncMore/index.tsx ~> ', data);
|
|
||||||
|
|
||||||
modelSet(data);
|
modelSet(data);
|
||||||
opInfo.setExtra(Date.now());
|
|
||||||
hide();
|
hide();
|
||||||
})
|
})
|
||||||
};
|
};
|
2
src/view/model/SyncPrompts/config.tsx
vendored
2
src/view/model/SyncPrompts/config.tsx
vendored
@ -2,7 +2,7 @@ import { Switch, Tag, Tooltip } from 'antd';
|
|||||||
|
|
||||||
import { genCmd } from '@/utils';
|
import { genCmd } from '@/utils';
|
||||||
|
|
||||||
export const modelColumns = () => [
|
export const syncColumns = () => [
|
||||||
{
|
{
|
||||||
title: '/{cmd}',
|
title: '/{cmd}',
|
||||||
dataIndex: 'cmd',
|
dataIndex: 'cmd',
|
||||||
|
6
src/view/model/SyncPrompts/index.tsx
vendored
6
src/view/model/SyncPrompts/index.tsx
vendored
@ -9,17 +9,17 @@ import useData from '@/hooks/useData';
|
|||||||
import useChatModel from '@/hooks/useChatModel';
|
import useChatModel from '@/hooks/useChatModel';
|
||||||
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
|
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
|
||||||
import { fmtDate, chatPromptsPath, GITHUB_PROMPTS_CSV_URL, genCmd } from '@/utils';
|
import { fmtDate, chatPromptsPath, GITHUB_PROMPTS_CSV_URL, genCmd } from '@/utils';
|
||||||
import { modelColumns } from './config';
|
import { syncColumns } from './config';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
const promptsURL = 'https://github.com/f/awesome-chatgpt-prompts/blob/main/prompts.csv';
|
const promptsURL = 'https://github.com/f/awesome-chatgpt-prompts/blob/main/prompts.csv';
|
||||||
|
|
||||||
export default function LanguageModel() {
|
export default function SyncPrompts() {
|
||||||
const { rowSelection, selectedRowIDs } = useTable();
|
const { rowSelection, selectedRowIDs } = useTable();
|
||||||
const [lastUpdated, setLastUpdated] = useState();
|
const [lastUpdated, setLastUpdated] = useState();
|
||||||
const { modelJson, modelSet } = useChatModel('sys_sync_prompts');
|
const { modelJson, modelSet } = useChatModel('sys_sync_prompts');
|
||||||
const { opData, opInit, opReplace, opReplaceItems, opSafeKey } = useData([]);
|
const { opData, opInit, opReplace, opReplaceItems, opSafeKey } = useData([]);
|
||||||
const { columns, ...opInfo } = useColumns(modelColumns());
|
const { columns, ...opInfo } = useColumns(syncColumns());
|
||||||
|
|
||||||
const selectedItems = rowSelection.selectedRowKeys || [];
|
const selectedItems = rowSelection.selectedRowKeys || [];
|
||||||
|
|
||||||
|
47
src/view/model/SyncRecord/config.tsx
vendored
Normal file
47
src/view/model/SyncRecord/config.tsx
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Switch, Tag, Tooltip } from 'antd';
|
||||||
|
|
||||||
|
import { genCmd } from '@/utils';
|
||||||
|
|
||||||
|
export const syncColumns = () => [
|
||||||
|
{
|
||||||
|
title: '/{cmd}',
|
||||||
|
dataIndex: 'cmd',
|
||||||
|
fixed: 'left',
|
||||||
|
// width: 120,
|
||||||
|
key: 'cmd',
|
||||||
|
render: (_: string, row: Record<string, string>) => (
|
||||||
|
<Tag color="#2a2a2a">/{genCmd(row.act)}</Tag>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Act',
|
||||||
|
dataIndex: 'act',
|
||||||
|
key: 'act',
|
||||||
|
// width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Tags',
|
||||||
|
dataIndex: 'tags',
|
||||||
|
key: 'tags',
|
||||||
|
// width: 150,
|
||||||
|
render: () => <Tag>chatgpt-prompts</Tag>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Enable',
|
||||||
|
dataIndex: 'enable',
|
||||||
|
key: 'enable',
|
||||||
|
// width: 80,
|
||||||
|
render: (v: boolean = false, row: Record<string, any>, action: Record<string, any>) => (
|
||||||
|
<Switch checked={v} onChange={(v) => action.setRecord({ ...row, enable: v }, 'enable')} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Prompt',
|
||||||
|
dataIndex: 'prompt',
|
||||||
|
key: 'prompt',
|
||||||
|
// width: 300,
|
||||||
|
render: (v: string) => (
|
||||||
|
<Tooltip overlayInnerStyle={{ width: 350 }} title={v}><span className="chat-prompts-val">{v}</span></Tooltip>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
42
src/view/model/SyncRecord/index.scss
vendored
Normal file
42
src/view/model/SyncRecord/index.scss
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// .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;
|
||||||
|
}
|
||||||
|
}
|
86
src/view/model/SyncRecord/index.tsx
vendored
Normal file
86
src/view/model/SyncRecord/index.tsx
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||||
|
import { Table, Button } from 'antd';
|
||||||
|
import { shell, path } from '@tauri-apps/api';
|
||||||
|
|
||||||
|
import useColumns from '@/hooks/useColumns';
|
||||||
|
import useData from '@/hooks/useData';
|
||||||
|
import { useCacheModel } from '@/hooks/useChatModel';
|
||||||
|
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
|
||||||
|
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();
|
||||||
|
const [filePath, setFilePath] = useState('');
|
||||||
|
const [jsonPath, setJsonPath] = useState('');
|
||||||
|
const state = location?.state;
|
||||||
|
|
||||||
|
const { rowSelection, selectedRowIDs } = useTable();
|
||||||
|
const { modelJson, modelSet } = useCacheModel(jsonPath);
|
||||||
|
const { opData, opInit, opReplace, opReplaceItems, opSafeKey } = useData([]);
|
||||||
|
const { columns, ...opInfo } = useColumns(syncColumns());
|
||||||
|
|
||||||
|
const selectedItems = rowSelection.selectedRowKeys || [];
|
||||||
|
|
||||||
|
useInit(async () => {
|
||||||
|
setFilePath(await getPath(state));
|
||||||
|
setJsonPath(await path.join(await chatRoot(), 'cache_sync', `${state?.id}.json`));
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (modelJson.length <= 0) return;
|
||||||
|
opInit(modelJson);
|
||||||
|
}, [modelJson.length]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (opInfo.opType === 'enable') {
|
||||||
|
const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord);
|
||||||
|
modelSet(data);
|
||||||
|
}
|
||||||
|
}, [opInfo.opTime]);
|
||||||
|
|
||||||
|
const handleEnable = (isEnable: boolean) => {
|
||||||
|
const data = opReplaceItems(selectedRowIDs, { enable: isEnable })
|
||||||
|
modelSet(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="chat-table-btns">
|
||||||
|
<div>
|
||||||
|
<Button shape="round" icon={<ArrowLeftOutlined />} onClick={() => history.back()} />
|
||||||
|
</div>
|
||||||
|
<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-table-tip">
|
||||||
|
<div className="chat-sync-path">
|
||||||
|
<div>PATH: <a onClick={() => shell.open(filePath)} target="_blank" title={filePath}>{filePath}</a></div>
|
||||||
|
<div>CACHE: <a onClick={() => shell.open(jsonPath)} target="_blank" title={jsonPath}>{jsonPath}</a></div>
|
||||||
|
</div>
|
||||||
|
{state?.last_updated && <span style={{ marginLeft: 10, color: '#888', fontSize: 12 }}>Last updated on {fmtDate(state?.last_updated)}</span>}
|
||||||
|
</div>
|
||||||
|
<Table
|
||||||
|
key="prompt"
|
||||||
|
rowKey="act"
|
||||||
|
columns={columns}
|
||||||
|
scroll={{ x: 'auto' }}
|
||||||
|
dataSource={opData}
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
pagination={TABLE_PAGINATION}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user