mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
✨ feat: agent profile
This commit is contained in:
@@ -1,16 +1,33 @@
|
||||
{
|
||||
"advanceSettings": "高级设置",
|
||||
"agentAvatar": "头像",
|
||||
"agentDescription": "描述",
|
||||
"agentDescriptionPlaceholder": "请输入描述",
|
||||
"agentName": "名称",
|
||||
"agentNamePlaceholder": "请输入名称",
|
||||
"agentProfile": "助手信息",
|
||||
"agentPrompt": "提示词",
|
||||
"agentPromptPlaceholder": "请输入 AI 提示词",
|
||||
"agentTag": "标签",
|
||||
"agentTagPlaceholder": "请输入标签",
|
||||
"archive": "归档",
|
||||
"autoGenerate": "自动补全",
|
||||
"cancel": "取消",
|
||||
"close": "关闭",
|
||||
"confirmRemoveSessionItemAlert": "即将删除该助手,删除后该将无法找回,请确认你的操作",
|
||||
"defaultAgent": "默认助手",
|
||||
"edit": "编辑",
|
||||
"editAgentProfile": "编辑助手信息",
|
||||
|
||||
"modelTemperature": "发散度",
|
||||
"newAgent": "新建助手",
|
||||
"noDescription": "暂无描述",
|
||||
"ok": "确定",
|
||||
"profile": "身份卡",
|
||||
"reset": "重置",
|
||||
"searchAgentPlaceholder": "搜索助手和对话...",
|
||||
"sessionSetting": "会话设置",
|
||||
"setting": "设置",
|
||||
"share": "分享"
|
||||
"share": "分享",
|
||||
"updateAgent": "更新助理信息"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ActionIcon, DraggablePanel, DraggablePanelContainer } from '@lobehub/ui
|
||||
import { createStyles } from 'antd-style';
|
||||
import { LucideEdit, LucideX } from 'lucide-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Router from 'next/router';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
@@ -23,8 +24,8 @@ const useStyles = createStyles(({ css, token }) => ({
|
||||
const Config = () => {
|
||||
const { t } = useTranslation('common');
|
||||
const { styles } = useStyles();
|
||||
const [showAgentSettings, toggleConfig] = useChatStore(
|
||||
(s) => [s.showAgentSettings, s.toggleConfig],
|
||||
const [showAgentSettings, toggleConfig, id] = useChatStore(
|
||||
(s) => [s.showAgentSettings, s.toggleConfig, s.activeId],
|
||||
shallow,
|
||||
);
|
||||
|
||||
@@ -53,6 +54,9 @@ const Config = () => {
|
||||
<Flexbox gap={4} horizontal>
|
||||
<ActionIcon
|
||||
icon={LucideEdit}
|
||||
onClick={() => {
|
||||
Router.push(`/chat/${id}/edit`);
|
||||
}}
|
||||
size={{ blockSize: 32, fontSize: 20 }}
|
||||
title={t('edit')}
|
||||
/>
|
||||
@@ -58,18 +58,11 @@ const Header = memo(() => {
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
<Flexbox gap={8} horizontal>
|
||||
<ActionIcon
|
||||
icon={Share2Icon}
|
||||
onClick={() => {
|
||||
// genShareUrl();
|
||||
}}
|
||||
size={{ fontSize: 24 }}
|
||||
title={t('share')}
|
||||
/>
|
||||
<ActionIcon icon={Share2Icon} size={{ fontSize: 24 }} title={t('share')} />
|
||||
<ActionIcon icon={ArchiveIcon} size={{ fontSize: 24 }} title={t('archive')} />
|
||||
<ActionIcon
|
||||
icon={MoreVerticalIcon}
|
||||
onClick={toggleConfig}
|
||||
onClick={() => toggleConfig()}
|
||||
size={{ fontSize: 24 }}
|
||||
title={t('sessionSetting')}
|
||||
/>
|
||||
27
src/pages/chat/[id]/edit/Form.tsx
Normal file
27
src/pages/chat/[id]/edit/Form.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
import { ReactNode, memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
header: css``,
|
||||
title: css`
|
||||
color: ${token.colorTextSecondary};
|
||||
`,
|
||||
}));
|
||||
|
||||
interface FormItemProps {
|
||||
children: ReactNode;
|
||||
label: string;
|
||||
}
|
||||
const FormItem = memo<FormItemProps>(({ label, children }) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
return (
|
||||
<Flexbox className={styles.header} gap={12}>
|
||||
<Flexbox className={styles.title}>{label}</Flexbox>
|
||||
<Flexbox>{children}</Flexbox>
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
||||
export default FormItem;
|
||||
130
src/pages/chat/[id]/edit/index.page.tsx
Normal file
130
src/pages/chat/[id]/edit/index.page.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import { ActionIcon, Avatar, Input, TextArea } from '@lobehub/ui';
|
||||
import { Button, Slider } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { LucideChevronLeft } from 'lucide-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import Router from 'next/router';
|
||||
import { memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import ChatLayout from '../../layout';
|
||||
import FormItem from './Form';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
footer: css`
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
border-top: 1px solid ${token.colorBorder};
|
||||
`,
|
||||
form: css`
|
||||
overflow-y: auto;
|
||||
`,
|
||||
header: css`
|
||||
background: ${token.colorBgContainer};
|
||||
border-bottom: 1px solid ${token.colorSplit};
|
||||
`,
|
||||
profile: css`
|
||||
font-size: 20px;
|
||||
color: ${token.colorTextTertiary};
|
||||
`,
|
||||
title: css`
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
`,
|
||||
}));
|
||||
|
||||
const EditPage = memo(() => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const { styles, theme } = useStyles();
|
||||
|
||||
const basic = [
|
||||
{ label: t('agentName'), placeholder: t('agentNamePlaceholder') },
|
||||
{ label: t('agentDescription'), placeholder: t('agentDescriptionPlaceholder') },
|
||||
{ label: t('agentTag'), placeholder: t('agentTagPlaceholder') },
|
||||
];
|
||||
return (
|
||||
<ChatLayout>
|
||||
<Flexbox height={'100vh'} style={{ position: 'relative' }} width={'100%'}>
|
||||
{/*header*/}
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
className={styles.header}
|
||||
gap={8}
|
||||
horizontal
|
||||
paddingBlock={8}
|
||||
paddingInline={16}
|
||||
>
|
||||
<ActionIcon
|
||||
icon={LucideChevronLeft}
|
||||
onClick={() => {
|
||||
Router.back();
|
||||
}}
|
||||
title={'返回'}
|
||||
/>
|
||||
<Flexbox className={styles.title}>{t('editAgentProfile')}</Flexbox>
|
||||
</Flexbox>
|
||||
|
||||
{/*form*/}
|
||||
<Flexbox className={styles.form} flex={1} gap={10} padding={24}>
|
||||
<FormItem label={t('agentPrompt')}>
|
||||
<TextArea
|
||||
placeholder={t('agentPromptPlaceholder')}
|
||||
// style={{ minHeight: 64 }}
|
||||
type={'block'}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
distribution={'space-between'}
|
||||
horizontal
|
||||
paddingBlock={12}
|
||||
style={{
|
||||
borderBottom: `1px solid ${theme.colorBorder}`,
|
||||
}}
|
||||
>
|
||||
<Flexbox className={styles.profile}> {t('profile')}</Flexbox>
|
||||
<Button size={'large'}>{t('autoGenerate')}</Button>
|
||||
</Flexbox>
|
||||
<Flexbox gap={80} horizontal style={{ marginTop: 16 }}>
|
||||
<Flexbox flex={1} gap={24}>
|
||||
{basic.map((item) => (
|
||||
<FormItem key={item.label} label={item.label}>
|
||||
<Input placeholder={item.placeholder} type={'block'} />
|
||||
</FormItem>
|
||||
))}
|
||||
|
||||
<Flexbox className={styles.profile}> {t('advanceSettings')}</Flexbox>
|
||||
|
||||
<FormItem label={t('modelTemperature')}>
|
||||
<Slider />
|
||||
</FormItem>
|
||||
</Flexbox>
|
||||
<FormItem label={t('agentAvatar')}>
|
||||
<Avatar size={200} />
|
||||
</FormItem>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
|
||||
{/*bottom*/}
|
||||
<Flexbox
|
||||
className={styles.footer}
|
||||
direction={'horizontal-reverse'}
|
||||
gap={8}
|
||||
padding={'16px 24px'}
|
||||
>
|
||||
<Button type={'primary'}>{t('updateAgent')}</Button>
|
||||
<Button>{t('reset')}</Button>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
</ChatLayout>
|
||||
);
|
||||
});
|
||||
|
||||
export default EditPage;
|
||||
|
||||
export const getServerSideProps = async (context: any) => ({
|
||||
props: await serverSideTranslations(context.locale),
|
||||
});
|
||||
@@ -1,70 +1,44 @@
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { memo, useEffect } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import { sessionSelectors, useChatStore } from '@/store/session';
|
||||
import { useSettings } from '@/store/settings';
|
||||
|
||||
import Config from '../Config';
|
||||
import Conversation from '../Conversation';
|
||||
import Header from '../Header';
|
||||
import { Sessions } from '../SessionList';
|
||||
import Sidebar from '../Sidebar';
|
||||
import Layout from '../layout';
|
||||
import Config from './Config';
|
||||
import Conversation from './Conversation';
|
||||
import Header from './Header';
|
||||
|
||||
const ChatLayout = memo(() => {
|
||||
const Chat = memo(() => {
|
||||
const [title] = useChatStore((s) => {
|
||||
const context = sessionSelectors.currentSession(s);
|
||||
return [context?.meta.title];
|
||||
}, isEqual);
|
||||
|
||||
useEffect(() => {
|
||||
useSettings.persist.rehydrate();
|
||||
useSettings.setState({ sidebarKey: 'chat' });
|
||||
}, []);
|
||||
|
||||
const router = useRouter();
|
||||
const { id } = router.query;
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof id === 'string') {
|
||||
useChatStore.setState({ activeId: id });
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Layout>
|
||||
<Head>
|
||||
<title>{title ? `${title} - LobeChat` : 'LobeChat'}</title>
|
||||
</Head>
|
||||
<Flexbox horizontal width={'100%'}>
|
||||
<Sidebar />
|
||||
<Sessions />
|
||||
<Flexbox flex={1}>
|
||||
<Header />
|
||||
<Flexbox
|
||||
id={'lobe-conversion-container'}
|
||||
style={{ height: 'calc(100vh - 64px)', position: 'relative' }}
|
||||
>
|
||||
<Conversation />
|
||||
<Config />
|
||||
</Flexbox>
|
||||
|
||||
<Flexbox flex={1}>
|
||||
<Header />
|
||||
<Flexbox
|
||||
id={'lobe-conversion-container'}
|
||||
style={{ height: 'calc(100vh - 64px)', position: 'relative' }}
|
||||
>
|
||||
<Conversation />
|
||||
<Config />
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
});
|
||||
export default Chat;
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
const { locale } = context;
|
||||
return {
|
||||
props: {
|
||||
// pass the translation props to the page component
|
||||
...(await serverSideTranslations(locale)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default ChatLayout;
|
||||
// pass the translation props to the page component
|
||||
export const getServerSideProps = async (context: any) => ({
|
||||
props: await serverSideTranslations(context.locale),
|
||||
});
|
||||
|
||||
47
src/pages/chat/layout.tsx
Normal file
47
src/pages/chat/layout.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { PropsWithChildren, memo, useEffect } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import { sessionSelectors, useChatStore } from '@/store/session';
|
||||
import { useSettings } from '@/store/settings';
|
||||
|
||||
import { Sessions } from './SessionList';
|
||||
import Sidebar from './Sidebar';
|
||||
|
||||
const ChatLayout = memo<PropsWithChildren>(({ children }) => {
|
||||
const [title] = useChatStore((s) => {
|
||||
const context = sessionSelectors.currentSession(s);
|
||||
return [context?.meta.title];
|
||||
}, isEqual);
|
||||
|
||||
useEffect(() => {
|
||||
useSettings.persist.rehydrate();
|
||||
useSettings.setState({ sidebarKey: 'chat' });
|
||||
}, []);
|
||||
|
||||
const router = useRouter();
|
||||
const { id } = router.query;
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof id === 'string') {
|
||||
useChatStore.setState({ activeId: id });
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title ? `${title} - LobeChat` : 'LobeChat'}</title>
|
||||
</Head>
|
||||
<Flexbox horizontal width={'100%'}>
|
||||
<Sidebar />
|
||||
<Sessions />
|
||||
{children}
|
||||
</Flexbox>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default ChatLayout;
|
||||
@@ -6,16 +6,6 @@ export default (token: Theme) => css`
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ant-btn-default:not(:disabled):not(.ant-btn-dangerous) {
|
||||
border-color: transparent;
|
||||
|
||||
&:hover {
|
||||
color: ${token.colorText};
|
||||
background: ${token.isDarkMode ? token.colorFill : token.colorFillTertiary};
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-popover {
|
||||
z-index: 1100;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user