mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
✨ feat: Add new components and features for AgentMeta
The changes include renaming a file and creating two new files. The code also adds new components and updates existing components in the "AgentMeta" feature. These changes are related to the configuration and metadata of an agent in a chat application. Autocomplete functionality is added to some input fields. The code includes React components for agent settings, such as metadata, configuration, and plugins. The code snippet shows the implementation of a chat editing page with various components and features.
This commit is contained in:
48
src/components/EmojiPicker/index.tsx
Normal file
48
src/components/EmojiPicker/index.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import data from '@emoji-mart/data';
|
||||
import i18n from '@emoji-mart/data/i18n/zh.json';
|
||||
import Picker from '@emoji-mart/react';
|
||||
import { Avatar } from '@lobehub/ui';
|
||||
import { Popover } from 'antd';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { DEFAULT_AVATAR, DEFAULT_BACKGROUND_COLOR } from '@/const/meta';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
export interface EmojiPickerProps {
|
||||
avatar?: string;
|
||||
backgroundColor?: string;
|
||||
onChange: (emoji: string) => void;
|
||||
}
|
||||
|
||||
const EmojiPicker = memo<EmojiPickerProps>(
|
||||
({ avatar = DEFAULT_AVATAR, backgroundColor = DEFAULT_BACKGROUND_COLOR, onChange }) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
return (
|
||||
<Popover
|
||||
content={
|
||||
<div className={styles.picker}>
|
||||
<Picker
|
||||
data={data}
|
||||
i18n={i18n}
|
||||
locale={'zh'}
|
||||
onEmojiSelect={(e: any) => onChange(e.native)}
|
||||
skinTonePosition={'none'}
|
||||
theme={'auto'}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
placement={'left'}
|
||||
rootClassName={styles.popover}
|
||||
trigger={'click'}
|
||||
>
|
||||
<div className={styles.avatar} style={{ width: 'fit-content' }}>
|
||||
<Avatar avatar={avatar} background={backgroundColor} size={44} />
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default EmojiPicker;
|
||||
28
src/components/EmojiPicker/style.ts
Normal file
28
src/components/EmojiPicker/style.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
import chroma from 'chroma-js';
|
||||
|
||||
export const useStyles = createStyles(({ css, token, prefixCls }) => ({
|
||||
avatar: css`
|
||||
border-radius: 50%;
|
||||
transition: scale 400ms ${token.motionEaseOut}, box-shadow 100ms ${token.motionEaseOut};
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 3px ${token.colorText};
|
||||
}
|
||||
|
||||
&:active {
|
||||
scale: 0.8;
|
||||
}
|
||||
`,
|
||||
picker: css`
|
||||
em-emoji-picker {
|
||||
--rgb-accent: ${chroma(token.colorPrimary) .rgb() .join(',')};
|
||||
--shadow: none;
|
||||
}
|
||||
`,
|
||||
popover: css`
|
||||
.${prefixCls}-popover-inner {
|
||||
padding: 0;
|
||||
}
|
||||
`,
|
||||
}));
|
||||
@@ -1,16 +1,14 @@
|
||||
import { Form, ItemGroup } from '@lobehub/ui';
|
||||
import { ConfigProvider, Input, Segmented, Select, Switch } from 'antd';
|
||||
import { useTheme } from 'antd-style';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { BrainCog, MessagesSquare } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SliderWithInput from 'src/components/SliderWithInput';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
import { AgentAction } from '@/store/session/slices/agentConfig';
|
||||
import { LanguageModel } from '@/types/llm';
|
||||
import type { LobeAgentConfig } from '@/types/session';
|
||||
|
||||
@@ -20,14 +18,15 @@ type SettingItemGroup = ItemGroup & {
|
||||
}[];
|
||||
};
|
||||
|
||||
const AgentConfig = () => {
|
||||
export interface AgentConfigProps {
|
||||
config: LobeAgentConfig;
|
||||
updateConfig: AgentAction['updateAgentConfig'];
|
||||
}
|
||||
|
||||
const AgentConfig = memo<AgentConfigProps>(({ config, updateConfig }) => {
|
||||
const { t } = useTranslation('setting');
|
||||
const theme = useTheme();
|
||||
|
||||
const config = useSessionStore(agentSelectors.currentAgentConfigSafe, isEqual);
|
||||
|
||||
const [updateAgentConfig] = useSessionStore((s) => [s.updateAgentConfig], shallow);
|
||||
|
||||
const chat: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
@@ -165,11 +164,11 @@ const AgentConfig = () => {
|
||||
<Form
|
||||
initialValues={config}
|
||||
items={[chat, model]}
|
||||
onValuesChange={debounce(updateAgentConfig, 100)}
|
||||
onValuesChange={debounce(updateConfig, 100)}
|
||||
{...FORM_STYLE}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default AgentConfig;
|
||||
@@ -6,8 +6,8 @@ import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface AutoGenerateInputProps extends InputProps {
|
||||
loading: boolean;
|
||||
onGenerate: () => void;
|
||||
loading?: boolean;
|
||||
onGenerate?: () => void;
|
||||
}
|
||||
|
||||
const AutoGenerateInput = memo<AutoGenerateInputProps>(({ loading, onGenerate, ...props }) => {
|
||||
@@ -17,18 +17,20 @@ const AutoGenerateInput = memo<AutoGenerateInputProps>(({ loading, onGenerate, .
|
||||
return (
|
||||
<Input
|
||||
suffix={
|
||||
<ActionIcon
|
||||
active
|
||||
icon={Wand2}
|
||||
loading={loading}
|
||||
onClick={onGenerate}
|
||||
size={'small'}
|
||||
style={{
|
||||
color: theme.colorInfo,
|
||||
marginRight: -4,
|
||||
}}
|
||||
title={t('autoGenerate')}
|
||||
/>
|
||||
onGenerate && (
|
||||
<ActionIcon
|
||||
active
|
||||
icon={Wand2}
|
||||
loading={loading}
|
||||
onClick={onGenerate}
|
||||
size={'small'}
|
||||
style={{
|
||||
color: theme.colorInfo,
|
||||
marginRight: -4,
|
||||
}}
|
||||
title={t('autoGenerate')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
type={'block'}
|
||||
{...props}
|
||||
27
src/features/AgentSetting/AgentMeta/BackgroundSwatches.tsx
Normal file
27
src/features/AgentSetting/AgentMeta/BackgroundSwatches.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Swatches, primaryColorsSwatches } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { DEFAULT_BACKGROUND_COLOR } from '@/const/meta';
|
||||
|
||||
interface BackgroundSwatchesProps {
|
||||
backgroundColor?: string;
|
||||
onChange: (color: string) => void;
|
||||
}
|
||||
|
||||
const BackgroundSwatches = memo<BackgroundSwatchesProps>(
|
||||
({ backgroundColor = DEFAULT_BACKGROUND_COLOR, onChange }) => {
|
||||
const handleSelect = (v: any) => {
|
||||
onChange(v || DEFAULT_BACKGROUND_COLOR);
|
||||
};
|
||||
|
||||
return (
|
||||
<Swatches
|
||||
activeColor={backgroundColor}
|
||||
colors={primaryColorsSwatches}
|
||||
onSelect={handleSelect}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default BackgroundSwatches;
|
||||
124
src/features/AgentSetting/AgentMeta/index.tsx
Normal file
124
src/features/AgentSetting/AgentMeta/index.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import { Form, type FormItemProps, Icon, type ItemGroup, Tooltip } from '@lobehub/ui';
|
||||
import { Button } from 'antd';
|
||||
import { UserCircle, Wand2 } from 'lucide-react';
|
||||
import { ReactNode, memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import EmojiPicker from '@/components/EmojiPicker';
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import { AgentAction, SessionLoadingState } from '@/store/session/slices/agentConfig';
|
||||
import { MetaData } from '@/types/meta';
|
||||
import { LobeAgentConfig } from '@/types/session';
|
||||
|
||||
import AutoGenerateInput from './AutoGenerateInput';
|
||||
import BackgroundSwatches from './BackgroundSwatches';
|
||||
|
||||
export interface AgentMetaProps {
|
||||
autocomplete?: {
|
||||
autocompleteMeta: AgentAction['autocompleteMeta'];
|
||||
autocompleteSessionAgentMeta: AgentAction['autocompleteSessionAgentMeta'];
|
||||
id: string | null;
|
||||
loading: SessionLoadingState;
|
||||
};
|
||||
config: LobeAgentConfig;
|
||||
meta: MetaData;
|
||||
updateMeta: AgentAction['updateAgentMeta'];
|
||||
}
|
||||
|
||||
const AgentMeta = memo<AgentMetaProps>(({ config, meta, updateMeta, autocomplete }) => {
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const hasSystemRole = useMemo(() => !!config.systemRole, [config]);
|
||||
|
||||
let extra: ReactNode | undefined;
|
||||
|
||||
const basic = [
|
||||
{
|
||||
key: 'title',
|
||||
label: t('settingAgent.name.title'),
|
||||
placeholder: t('settingAgent.name.placeholder'),
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: t('settingAgent.description.title'),
|
||||
placeholder: t('settingAgent.description.placeholder'),
|
||||
},
|
||||
// { key: 'tag', label: t('agentTag'), placeholder: t('agentTagPlaceholder') },
|
||||
];
|
||||
|
||||
const autocompleteItems: FormItemProps[] = basic.map((item) => ({
|
||||
children: (
|
||||
<AutoGenerateInput
|
||||
loading={autocomplete?.loading[item.key as keyof SessionLoadingState]}
|
||||
onChange={(e) => {
|
||||
updateMeta({ [item.key]: e.target.value });
|
||||
}}
|
||||
onGenerate={() => {
|
||||
autocomplete?.autocompleteMeta(item.key as keyof typeof meta);
|
||||
}}
|
||||
placeholder={item.placeholder}
|
||||
value={meta[item.key as keyof typeof meta]}
|
||||
/>
|
||||
),
|
||||
label: item.label,
|
||||
}));
|
||||
|
||||
if (autocomplete) {
|
||||
const { autocompleteSessionAgentMeta, loading, id } = autocomplete;
|
||||
|
||||
extra = (
|
||||
<Tooltip title={t('autoGenerateTooltip', { ns: 'common' })}>
|
||||
<Button
|
||||
disabled={!hasSystemRole}
|
||||
icon={<Icon icon={Wand2} />}
|
||||
loading={Object.values(loading).some((i) => !!i)}
|
||||
onClick={(e: any) => {
|
||||
e.stopPropagation();
|
||||
if (!id) return;
|
||||
autocompleteSessionAgentMeta(id, true);
|
||||
}}
|
||||
size={'small'}
|
||||
>
|
||||
{t('autoGenerate', { ns: 'common' })}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
const metaData: ItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: (
|
||||
<EmojiPicker
|
||||
avatar={meta.avatar}
|
||||
backgroundColor={meta.backgroundColor}
|
||||
onChange={(avatar) => updateMeta({ avatar })}
|
||||
/>
|
||||
),
|
||||
label: t('settingAgent.avatar.title'),
|
||||
minWidth: undefined,
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<BackgroundSwatches
|
||||
backgroundColor={meta.backgroundColor}
|
||||
onChange={(backgroundColor) => updateMeta({ backgroundColor })}
|
||||
/>
|
||||
),
|
||||
label: t('settingAgent.backgroundColor.title'),
|
||||
minWidth: undefined,
|
||||
},
|
||||
...autocompleteItems,
|
||||
],
|
||||
extra: extra,
|
||||
icon: UserCircle,
|
||||
title: t('settingAgent.title'),
|
||||
}),
|
||||
[autocompleteItems, extra, meta],
|
||||
);
|
||||
|
||||
return <Form items={[metaData]} {...FORM_STYLE} />;
|
||||
});
|
||||
|
||||
export default AgentMeta;
|
||||
@@ -1,22 +1,22 @@
|
||||
import { Avatar, Form, ItemGroup } from '@lobehub/ui';
|
||||
import { Switch } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { ToyBrick } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import { PluginsMap } from '@/plugins';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
import { AgentAction } from '@/store/session/slices/agentConfig';
|
||||
import { LobeAgentConfig } from '@/types/session';
|
||||
|
||||
const PluginList = () => {
|
||||
export interface AgentPluginProps {
|
||||
config: LobeAgentConfig;
|
||||
updateConfig: AgentAction['toggleAgentPlugin'];
|
||||
}
|
||||
|
||||
const AgentPlugin = memo<AgentPluginProps>(({ config, updateConfig }) => {
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const config = useSessionStore(agentSelectors.currentAgentConfigSafe, isEqual);
|
||||
|
||||
const toggleAgentPlugin = useSessionStore((s) => s.toggleAgentPlugin, shallow);
|
||||
|
||||
const plugin: ItemGroup = useMemo(
|
||||
() => ({
|
||||
children: Object.values(PluginsMap).map((item) => ({
|
||||
@@ -24,7 +24,7 @@ const PluginList = () => {
|
||||
children: (
|
||||
<Switch
|
||||
checked={!config.plugins ? false : config.plugins.includes(item.name)}
|
||||
onChange={() => toggleAgentPlugin(item.name)}
|
||||
onChange={() => updateConfig(item.name)}
|
||||
/>
|
||||
),
|
||||
desc: item.schema.description,
|
||||
@@ -39,6 +39,6 @@ const PluginList = () => {
|
||||
);
|
||||
|
||||
return <Form items={[plugin]} {...FORM_STYLE} />;
|
||||
};
|
||||
});
|
||||
|
||||
export default PluginList;
|
||||
export default AgentPlugin;
|
||||
@@ -1,13 +1,14 @@
|
||||
import { CodeEditor, FormGroup, TokenTag } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { encode } from 'gpt-tokenizer';
|
||||
import { Bot } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import { ModelTokens } from '@/const/modelTokens';
|
||||
import { agentSelectors, chatSelectors, useSessionStore } from '@/store/session';
|
||||
import { AgentAction } from '@/store/session/slices/agentConfig';
|
||||
import { LobeAgentConfig } from '@/types/session';
|
||||
|
||||
export const useStyles = createStyles(({ css, token }) => ({
|
||||
input: css`
|
||||
@@ -22,18 +23,16 @@ export const useStyles = createStyles(({ css, token }) => ({
|
||||
`,
|
||||
}));
|
||||
|
||||
const AgentPrompt = memo(() => {
|
||||
export interface AgentPromptProps {
|
||||
config: LobeAgentConfig;
|
||||
updateConfig: AgentAction['updateAgentConfig'];
|
||||
}
|
||||
|
||||
const AgentPrompt = memo<AgentPromptProps>(({ config, updateConfig }) => {
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const [systemRole, model, systemTokenCount, updateAgentConfig] = useSessionStore(
|
||||
(s) => [
|
||||
agentSelectors.currentAgentSystemRole(s),
|
||||
agentSelectors.currentAgentModel(s),
|
||||
chatSelectors.systemRoleTokenCount(s),
|
||||
s.updateAgentConfig,
|
||||
],
|
||||
shallow,
|
||||
);
|
||||
const { systemRole, model } = config;
|
||||
const systemTokenCount = useMemo(() => encode(systemRole || '').length, [systemRole]);
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
@@ -46,9 +45,7 @@ const AgentPrompt = memo(() => {
|
||||
>
|
||||
<CodeEditor
|
||||
language={'md'}
|
||||
onValueChange={(e) => {
|
||||
updateAgentConfig({ systemRole: e });
|
||||
}}
|
||||
onValueChange={(e) => updateConfig({ systemRole: e })}
|
||||
placeholder={t('settingAgent.name.placeholder')}
|
||||
resize={false}
|
||||
style={{ marginTop: 16 }}
|
||||
4
src/features/AgentSetting/index.ts
Normal file
4
src/features/AgentSetting/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { default as AgentConfig } from './AgentConfig';
|
||||
export { default as AgentMeta } from './AgentMeta';
|
||||
export { default as AgentPlugin } from './AgentPlugin';
|
||||
export { default as AgentPrompt } from './AgentPrompt';
|
||||
40
src/features/AgentSetting/test.json
Normal file
40
src/features/AgentSetting/test.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"exportType": "agents",
|
||||
"state": {
|
||||
"sessions": {
|
||||
"9a2def55-ff50-4f7f-ad6c-b97dac817193": {
|
||||
"chats": {},
|
||||
"config": {
|
||||
"model": "gpt-3.5-turbo",
|
||||
"params": { "temperature": 0.6 },
|
||||
"systemRole": "你是一名专业的前端。擅长书写 Typescript JSDoc 代码,代码的示例如下:\n\n```ts\ninterface Props {\n/**\n* 尺寸\n* */\nloading: boolean;\n/**\n* 头像形状\n* @default 'square'\n* @enum [\"square\",\"circle\"]\n* @enumNames [ \"方形\", \"圆形\"]\n*/\nshape?: 'square' | 'circle';\n/**\n* 返回事件\n* @ignore\n*/\nonBack: () => void;\n/**\n* 选择路由的回调函数\n* @param key - 选中的路由\n* @ignore\n*/\nonSelect?: (key: string) => any;\n/**\n* 点击事件回调函数\n* @ignore\n*/\nonClick?: () => void;\n/**\n* 引用\n* @ignore\n*/\nref: any;\n/**\n* Tooltip 提示框位置\n* @enum ['top', 'left', 'right', 'bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight', 'leftTop', 'leftBottom', 'rightTop', 'rightBottom']\n* @enumNames ['上', '左', '右', '下', '左上', '右上', '左下', '右下', '左上', '左下', '右上', '右下']\n* @default 'top'\n*/\nplacement?: TooltipPlacement;\n}\n```\n\n\n接下来用户会输入一串 interface 代码,需要你补全 jsdoc。其中接口的类型不可改变"
|
||||
},
|
||||
"createAt": 1690184262713,
|
||||
"id": "9a2def55-ff50-4f7f-ad6c-b97dac817193",
|
||||
"meta": { "backgroundColor": "#ec5e41", "avatar": "🥲" },
|
||||
"type": "agent",
|
||||
"updateAt": 1690184262713,
|
||||
"topics": {}
|
||||
},
|
||||
"744fe028-c8a2-410c-8b0a-66cfa00d647b": {
|
||||
"chats": {},
|
||||
"config": {
|
||||
"model": "gpt-3.5-turbo",
|
||||
"params": { "temperature": 0.6 },
|
||||
"systemRole": "- dafdsafa\n- da- dafdsafa\n- da- dafdsafa\n- da- dafdsafa\n- da- dafdsafa\n- da"
|
||||
},
|
||||
"createAt": 1690184730185,
|
||||
"id": "744fe028-c8a2-410c-8b0a-66cfa00d647b",
|
||||
"meta": {
|
||||
"backgroundColor": "#ffef5c",
|
||||
"title": "dafaa",
|
||||
"description": "safsafsafsafsafsafsfasfas"
|
||||
},
|
||||
"type": "agent",
|
||||
"updateAt": 1690184730185,
|
||||
"topics": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { Swatches, primaryColorsSwatches } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { DEFAULT_BACKGROUND_COLOR } from '@/const/meta';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
|
||||
const BackgroundSwatches = memo(() => {
|
||||
const [backgroundColor, updateAgentMeta] = useSessionStore(
|
||||
(s) => [agentSelectors.currentAgentBackgroundColor(s), s.updateAgentMeta],
|
||||
shallow,
|
||||
);
|
||||
|
||||
const handleSelect = (v: any) => {
|
||||
if (v) {
|
||||
updateAgentMeta({ backgroundColor: v });
|
||||
} else {
|
||||
updateAgentMeta({ backgroundColor: DEFAULT_BACKGROUND_COLOR });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Swatches
|
||||
activeColor={backgroundColor}
|
||||
colors={primaryColorsSwatches}
|
||||
onSelect={handleSelect}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default BackgroundSwatches;
|
||||
@@ -1,78 +0,0 @@
|
||||
import data from '@emoji-mart/data';
|
||||
import i18n from '@emoji-mart/data/i18n/zh.json';
|
||||
import Picker from '@emoji-mart/react';
|
||||
import { Avatar } from '@lobehub/ui';
|
||||
import { Popover } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import chroma from 'chroma-js';
|
||||
import { memo } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useSessionStore } from '@/store/session';
|
||||
import { agentSelectors } from '@/store/session/selectors';
|
||||
|
||||
const useStyles = createStyles(({ css, token, prefixCls }) => ({
|
||||
avatar: css`
|
||||
border-radius: 50%;
|
||||
transition: scale 400ms ${token.motionEaseOut}, box-shadow 100ms ${token.motionEaseOut};
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 3px ${token.colorText};
|
||||
}
|
||||
|
||||
&:active {
|
||||
scale: 0.8;
|
||||
}
|
||||
`,
|
||||
picker: css`
|
||||
em-emoji-picker {
|
||||
--rgb-accent: ${chroma(token.colorPrimary) .rgb() .join(',')};
|
||||
--shadow: none;
|
||||
}
|
||||
`,
|
||||
popover: css`
|
||||
.${prefixCls}-popover-inner {
|
||||
padding: 0;
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
||||
const EmojiPicker = () => {
|
||||
const { styles } = useStyles();
|
||||
const [avatar, backgroundColor, updateAgentMeta] = useSessionStore(
|
||||
(s) => [
|
||||
agentSelectors.currentAgentAvatar(s),
|
||||
agentSelectors.currentAgentBackgroundColor(s),
|
||||
s.updateAgentMeta,
|
||||
],
|
||||
shallow,
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
content={
|
||||
<div className={styles.picker}>
|
||||
<Picker
|
||||
data={data}
|
||||
i18n={i18n}
|
||||
locale={'zh'}
|
||||
onEmojiSelect={(e: any) => {
|
||||
updateAgentMeta({ avatar: e.native });
|
||||
}}
|
||||
skinTonePosition={'none'}
|
||||
theme={'auto'}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
placement={'left'}
|
||||
rootClassName={styles.popover}
|
||||
trigger={'click'}
|
||||
>
|
||||
<div className={styles.avatar} style={{ width: 'fit-content' }}>
|
||||
<Avatar avatar={avatar} background={backgroundColor} size={44} />
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(EmojiPicker);
|
||||
@@ -1,111 +0,0 @@
|
||||
import { Form, Icon, type ItemGroup, Tooltip } from '@lobehub/ui';
|
||||
import { Button } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { UserCircle, Wand2 } from 'lucide-react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
|
||||
import AutoGenerateInput from './AutoGenerateInput';
|
||||
import BackgroundSwatches from './BackgroundSwatches';
|
||||
import EmojiPicker from './EmojiPicker';
|
||||
|
||||
const AgentMeta = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const metaData = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
|
||||
|
||||
const [
|
||||
autocompleteMeta,
|
||||
autocompleteSessionAgentMeta,
|
||||
loading,
|
||||
updateAgentMeta,
|
||||
id,
|
||||
hasSystemRole,
|
||||
] = useSessionStore(
|
||||
(s) => [
|
||||
s.autocompleteMeta,
|
||||
s.autocompleteSessionAgentMeta,
|
||||
s.autocompleteLoading,
|
||||
s.updateAgentMeta,
|
||||
s.activeId,
|
||||
agentSelectors.hasSystemRole(s),
|
||||
],
|
||||
shallow,
|
||||
);
|
||||
|
||||
const basic = [
|
||||
{
|
||||
key: 'title',
|
||||
label: t('settingAgent.name.title'),
|
||||
placeholder: t('settingAgent.name.placeholder'),
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: t('settingAgent.description.title'),
|
||||
placeholder: t('settingAgent.description.placeholder'),
|
||||
},
|
||||
// { key: 'tag', label: t('agentTag'), placeholder: t('agentTagPlaceholder') },
|
||||
];
|
||||
|
||||
const meta: ItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: <EmojiPicker />,
|
||||
label: t('settingAgent.avatar.title'),
|
||||
minWidth: undefined,
|
||||
},
|
||||
{
|
||||
children: <BackgroundSwatches />,
|
||||
label: t('settingAgent.backgroundColor.title'),
|
||||
minWidth: undefined,
|
||||
},
|
||||
...basic.map((item) => ({
|
||||
children: (
|
||||
<AutoGenerateInput
|
||||
loading={loading[item.key as keyof typeof loading]}
|
||||
onChange={(e) => {
|
||||
updateAgentMeta({ [item.key]: e.target.value });
|
||||
}}
|
||||
onGenerate={() => {
|
||||
autocompleteMeta(item.key as keyof typeof metaData);
|
||||
}}
|
||||
placeholder={item.placeholder}
|
||||
value={metaData[item.key as keyof typeof metaData]}
|
||||
/>
|
||||
),
|
||||
label: item.label,
|
||||
})),
|
||||
],
|
||||
extra: (
|
||||
<Tooltip title={t('autoGenerateTooltip', { ns: 'common' })}>
|
||||
<Button
|
||||
disabled={!hasSystemRole}
|
||||
icon={<Icon icon={Wand2} />}
|
||||
loading={Object.values(loading).some((i) => !!i)}
|
||||
onClick={(e: any) => {
|
||||
e.stopPropagation();
|
||||
console.log(id);
|
||||
if (!id) return;
|
||||
autocompleteSessionAgentMeta(id, true);
|
||||
}}
|
||||
size={'small'}
|
||||
>
|
||||
{t('autoGenerate', { ns: 'common' })}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
),
|
||||
icon: UserCircle,
|
||||
title: t('settingAgent.title'),
|
||||
}),
|
||||
[basic, metaData],
|
||||
);
|
||||
|
||||
return <Form items={[meta]} {...FORM_STYLE} />;
|
||||
});
|
||||
|
||||
export default AgentMeta;
|
||||
@@ -1,3 +1,4 @@
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import Head from 'next/head';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -6,20 +7,40 @@ import { shallow } from 'zustand/shallow';
|
||||
|
||||
import HeaderSpacing from '@/components/HeaderSpacing';
|
||||
import { HEADER_HEIGHT } from '@/const/layoutTokens';
|
||||
import { AgentConfig, AgentMeta, AgentPlugin, AgentPrompt } from '@/features/AgentSetting';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
||||
|
||||
import ChatLayout from '../../layout';
|
||||
import AgentConfig from './AgentConfig';
|
||||
import AgentMeta from './AgentMeta';
|
||||
import AgentPlugin from './AgentPlugin';
|
||||
import AgentPrompt from './AgentPrompt';
|
||||
import Header from './Header';
|
||||
|
||||
const EditPage = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const config = useSessionStore(agentSelectors.currentAgentConfigSafe, isEqual);
|
||||
const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
|
||||
const [
|
||||
updateAgentConfig,
|
||||
toggleAgentPlugin,
|
||||
autocompleteMeta,
|
||||
autocompleteSessionAgentMeta,
|
||||
loading,
|
||||
updateAgentMeta,
|
||||
id,
|
||||
title,
|
||||
] = useSessionStore(
|
||||
(s) => [
|
||||
s.updateAgentConfig,
|
||||
s.toggleAgentPlugin,
|
||||
s.autocompleteMeta,
|
||||
s.autocompleteSessionAgentMeta,
|
||||
s.autocompleteLoading,
|
||||
s.updateAgentMeta,
|
||||
s.activeId,
|
||||
agentSelectors.currentAgentTitle(s),
|
||||
],
|
||||
shallow,
|
||||
);
|
||||
|
||||
const title = useSessionStore(agentSelectors.currentAgentTitle, shallow);
|
||||
const pageTitle = genSiteHeadTitle(t('header.sessionWithName', { name: title }));
|
||||
|
||||
return (
|
||||
@@ -31,10 +52,15 @@ const EditPage = memo(() => {
|
||||
<Header />
|
||||
<Flexbox align={'center'} flex={1} gap={16} padding={24} style={{ overflow: 'auto' }}>
|
||||
<HeaderSpacing height={HEADER_HEIGHT - 16} />
|
||||
<AgentPrompt />
|
||||
<AgentMeta />
|
||||
<AgentConfig />
|
||||
<AgentPlugin />
|
||||
<AgentPrompt config={config} updateConfig={updateAgentConfig} />
|
||||
<AgentMeta
|
||||
autocomplete={{ autocompleteMeta, autocompleteSessionAgentMeta, id, loading }}
|
||||
config={config}
|
||||
meta={meta}
|
||||
updateMeta={updateAgentMeta}
|
||||
/>
|
||||
<AgentConfig config={config} updateConfig={updateAgentConfig} />
|
||||
<AgentPlugin config={config} updateConfig={toggleAgentPlugin} />
|
||||
</Flexbox>
|
||||
</ChatLayout>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user