@lobehub/chat 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +3 -3
  3. package/locales/en_US/common.json +19 -27
  4. package/locales/en_US/plugin.json +10 -0
  5. package/locales/en_US/setting.json +42 -6
  6. package/locales/zh_CN/common.json +8 -17
  7. package/locales/zh_CN/plugin.json +10 -0
  8. package/locales/zh_CN/setting.json +36 -0
  9. package/package.json +1 -1
  10. package/src/components/HeaderSpacing/index.tsx +3 -1
  11. package/src/components/HeaderTitle/index.tsx +42 -0
  12. package/src/const/layoutTokens.ts +6 -1
  13. package/src/const/meta.ts +3 -0
  14. package/src/locales/default/common.ts +31 -49
  15. package/src/locales/default/plugin.ts +10 -0
  16. package/src/locales/default/setting.ts +46 -1
  17. package/src/locales/resources/en_US.ts +2 -0
  18. package/src/locales/resources/zh_CN.ts +2 -0
  19. package/src/pages/chat/SessionList/List/SessionItem.tsx +1 -2
  20. package/src/pages/chat/[id]/Config/Header.tsx +26 -0
  21. package/src/pages/chat/[id]/Config/SideBar.tsx +86 -0
  22. package/src/pages/chat/[id]/Config/index.tsx +5 -45
  23. package/src/pages/chat/[id]/Conversation/FunctionMessage.tsx +1 -1
  24. package/src/pages/chat/[id]/Conversation/Input/Action.tsx +13 -5
  25. package/src/pages/chat/[id]/Conversation/Input/index.tsx +11 -3
  26. package/src/pages/chat/[id]/Conversation/MessageExtra.tsx +1 -1
  27. package/src/pages/chat/[id]/Header.tsx +34 -43
  28. package/src/pages/chat/[id]/edit/AgentConfig/index.tsx +154 -95
  29. package/src/pages/chat/[id]/edit/AgentMeta/AutoGenerateInput.tsx +39 -0
  30. package/src/pages/chat/[id]/edit/AgentMeta/BackgroundSwatches.tsx +31 -0
  31. package/src/pages/chat/[id]/edit/AgentMeta/EmojiPicker.tsx +7 -3
  32. package/src/pages/chat/[id]/edit/AgentMeta/index.tsx +68 -75
  33. package/src/pages/chat/[id]/edit/AgentPlugin/index.tsx +44 -0
  34. package/src/pages/chat/[id]/edit/AgentPrompt/index.tsx +48 -0
  35. package/src/pages/chat/[id]/edit/Header.tsx +31 -0
  36. package/src/pages/chat/[id]/edit/index.page.tsx +12 -40
  37. package/src/pages/chat/[id]/index.page.tsx +2 -1
  38. package/src/pages/setting/Header.tsx +4 -11
  39. package/src/pages/setting/SettingForm.tsx +9 -9
  40. package/src/pages/setting/index.page.tsx +2 -1
  41. package/src/plugins/index.ts +2 -1
  42. package/src/plugins/webCrawler/index.ts +20 -0
  43. package/src/plugins/webCrawler/runner.ts +75 -0
  44. package/src/store/session/slices/agentConfig/initialState.ts +1 -5
  45. package/src/store/session/slices/agentConfig/selectors.ts +15 -5
  46. package/src/store/session/slices/chat/selectors/chat.ts +2 -2
  47. package/src/store/session/slices/session/selectors/chat.ts +1 -1
  48. package/src/utils/genSiteHeadTitle.ts +1 -0
  49. package/src/pages/chat/[id]/Config/ReadMode.tsx +0 -64
  50. package/src/pages/chat/[id]/edit/AgentConfig/Plugin.tsx +0 -46
  51. package/src/pages/chat/[id]/edit/AgentConfig/Prompt.tsx +0 -69
@@ -1,113 +1,172 @@
1
- import { Collapse, ConfigProvider, InputNumber, Segmented, Slider } from 'antd';
1
+ import { Form, ItemGroup } from '@lobehub/ui';
2
+ import { Input, Segmented, Select, Switch } from 'antd';
2
3
  import isEqual from 'fast-deep-equal';
4
+ import { debounce } from 'lodash-es';
5
+ import { BrainCog, MessagesSquare } from 'lucide-react';
6
+ import { useMemo } from 'react';
3
7
  import { useTranslation } from 'react-i18next';
4
- import { Flexbox } from 'react-layout-kit';
5
8
  import { shallow } from 'zustand/shallow';
6
9
 
10
+ import { FORM_STYLE } from '@/const/layoutTokens';
11
+ import SliderWithInput from '@/features/SliderWithInput';
7
12
  import { agentSelectors, useSessionStore } from '@/store/session';
8
13
  import { LanguageModel } from '@/types/llm';
14
+ import type { LobeAgentConfig } from '@/types/session';
9
15
 
10
- import { FormItem } from '../FormItem';
11
- import { useStyles } from '../style';
12
- import Plugin from './Plugin';
13
- import Prompt from './Prompt';
16
+ type SettingItemGroup = ItemGroup & {
17
+ children: {
18
+ name?: keyof LobeAgentConfig;
19
+ }[];
20
+ };
14
21
 
15
22
  const AgentConfig = () => {
16
- const { t } = useTranslation('common');
17
-
18
- const { styles, theme } = useStyles();
23
+ const { t } = useTranslation('setting');
19
24
 
20
25
  const config = useSessionStore(agentSelectors.currentAgentConfigSafe, isEqual);
21
26
 
22
27
  const [updateAgentConfig] = useSessionStore((s) => [s.updateAgentConfig], shallow);
23
28
 
24
- return (
25
- <ConfigProvider
26
- theme={{
27
- components: {
28
- Segmented: {
29
- colorBgLayout: theme.isDarkMode ? '#111' : '#f1f1f1',
30
- },
29
+ // TODO: setting 结构嵌套,现在是扁平的 params: { temperature: 0.6 }
30
+ // @ts-ignore
31
+ const chat: SettingItemGroup = useMemo(
32
+ () => ({
33
+ children: [
34
+ {
35
+ children: (
36
+ <Segmented
37
+ options={[
38
+ { label: t('settingChat.chatStyleType.type.bubble'), value: 'chat' },
39
+ { label: t('settingChat.chatStyleType.type.docs'), value: 'docs' },
40
+ ]}
41
+ />
42
+ ),
43
+ label: t('settingChat.chatStyleType.title'),
44
+ minWidth: undefined,
45
+ name: 'chatStyleType',
46
+ },
47
+ {
48
+ children: <Input placeholder={t('settingChat.inputTemplate.placeholder')} />,
49
+ desc: t('settingChat.inputTemplate.desc'),
50
+ label: t('settingChat.inputTemplate.title'),
51
+ name: 'enableHistoryCount',
52
+ },
53
+ {
54
+ children: <Switch />,
55
+ label: t('settingChat.enableHistoryCount.title'),
56
+ minWidth: undefined,
57
+ name: 'enableHistoryCount',
58
+ valuePropName: 'checked',
59
+ },
60
+ {
61
+ children: <SliderWithInput max={32} min={0} />,
62
+ desc: t('settingChat.historyCount.desc'),
63
+ // @ts-ignore
64
+ hidden: !config.params.enableHistoryCount,
65
+ label: t('settingChat.historyCount.title'),
66
+ name: 'historyCount',
67
+ },
68
+ {
69
+ children: <Switch />,
70
+ label: t('settingChat.enableCompressThreshold.title'),
71
+ minWidth: undefined,
72
+ name: 'enableCompressThreshold',
73
+ valuePropName: 'checked',
74
+ },
75
+ {
76
+ children: <SliderWithInput max={32} min={0} />,
77
+ desc: t('settingChat.compressThreshold.desc'),
78
+ // @ts-ignore
79
+ hidden: !config.params.enableCompressThreshold,
80
+ label: t('settingChat.compressThreshold.title'),
81
+ name: 'compressThreshold',
82
+ },
83
+ ],
84
+ icon: MessagesSquare,
85
+ title: t('settingChat.title'),
86
+ }),
87
+ [config],
88
+ );
89
+
90
+ // TODO: setting 结构嵌套,现在是扁平的 params: { temperature: 0.6 }
91
+ // @ts-ignore
92
+ const model: SettingItemGroup = useMemo(
93
+ () => ({
94
+ children: [
95
+ {
96
+ children: (
97
+ <Select
98
+ options={Object.values(LanguageModel).map((value) => ({
99
+ label: value,
100
+ value,
101
+ }))}
102
+ />
103
+ ),
104
+ desc: t('settingModel.model.desc'),
105
+ label: t('settingModel.model.title'),
106
+ name: 'model',
107
+ tag: 'model',
31
108
  },
32
- }}
33
- >
34
- <Flexbox
35
- align={'center'}
36
- distribution={'space-between'}
37
- horizontal
38
- paddingBlock={12}
39
- style={{
40
- borderBottom: `1px solid ${theme.colorBorder}`,
41
- }}
42
- >
43
- <Flexbox className={styles.profile}> {t('modelConfig')}</Flexbox>
44
- </Flexbox>
45
- <Flexbox gap={24}>
46
- <FormItem label={t('agentModel')}>
47
- <Segmented
48
- block
49
- onChange={(value) => {
50
- updateAgentConfig({ model: value as LanguageModel });
51
- }}
52
- options={Object.values(LanguageModel).map((value) => ({
53
- label: t(value),
54
- value,
55
- }))}
56
- size={'large'}
57
- value={config.model}
58
- />
59
- </FormItem>
60
- <Prompt />
61
- <Collapse
62
- className={styles.title}
63
- expandIconPosition={'end'}
64
- items={[
65
- {
66
- children: (
67
- <Flexbox paddingBlock={16}>
68
- <FormItem label={t('modelTemperature')}>
69
- <Flexbox gap={16} horizontal>
70
- <Slider
71
- max={1}
72
- min={0}
73
- onChange={(value) => {
74
- updateAgentConfig({ params: { temperature: value } });
75
- }}
76
- step={0.1}
77
- style={{ flex: 1 }}
78
- value={Number(config.params.temperature)}
79
- />
80
- <InputNumber
81
- max={1}
82
- min={0}
83
- onChange={(value) => {
84
- if (value) updateAgentConfig({ params: { temperature: value } });
85
- }}
86
- value={config.params.temperature}
87
- />
88
- </Flexbox>
89
- </FormItem>
90
- </Flexbox>
91
- ),
92
- key: 'advanceSettings',
93
- label: t('advanceSettings'),
94
- },
95
- ]}
96
- />
97
- <Flexbox
98
- align={'center'}
99
- distribution={'space-between'}
100
- horizontal
101
- paddingBlock={12}
102
- style={{
103
- borderBottom: `1px solid ${theme.colorBorder}`,
104
- }}
105
- >
106
- <Flexbox className={styles.profile}> {t('pluginList')}</Flexbox>
107
- </Flexbox>
108
- <Plugin />
109
- </Flexbox>
110
- </ConfigProvider>
109
+ {
110
+ children: <SliderWithInput max={1} min={0} step={0.1} />,
111
+ desc: t('settingModel.temperature.desc'),
112
+ label: t('settingModel.temperature.title'),
113
+ name: 'temperature',
114
+ tag: 'temperature',
115
+ },
116
+ {
117
+ children: <SliderWithInput max={1} min={0} step={0.1} />,
118
+ desc: t('settingModel.topP.desc'),
119
+ label: t('settingModel.topP.title'),
120
+ name: 'topP',
121
+ tag: 'top_p',
122
+ },
123
+ {
124
+ children: <SliderWithInput max={2} min={-2} step={0.1} />,
125
+ desc: t('settingModel.presencePenalty.desc'),
126
+ label: t('settingModel.presencePenalty.title'),
127
+ name: 'presencePenalty',
128
+ tag: 'presence_penalty',
129
+ },
130
+ {
131
+ children: <SliderWithInput max={2} min={-2} step={0.1} />,
132
+ desc: t('settingModel.frequencyPenalty.desc'),
133
+ label: t('settingModel.frequencyPenalty.title'),
134
+ name: 'frequencyPenalty',
135
+ tag: 'frequency_penalty',
136
+ },
137
+ {
138
+ children: <Switch />,
139
+ label: t('settingModel.enableMaxTokens.title'),
140
+ minWidth: undefined,
141
+ name: 'enableMaxTokens',
142
+ valuePropName: 'checked',
143
+ },
144
+ {
145
+ children: <SliderWithInput max={32_000} min={0} step={100} />,
146
+ desc: t('settingModel.maxTokens.desc'),
147
+ // TODO
148
+ // @ts-ignore
149
+ hidden: !config?.params?.enableMaxTokens,
150
+ label: t('settingModel.maxTokens.title'),
151
+ name: 'params.maxTokens',
152
+ tag: 'max_tokens',
153
+ },
154
+ ],
155
+ icon: BrainCog,
156
+ title: t('settingModel.title'),
157
+ }),
158
+ [config],
159
+ );
160
+
161
+ const items = useMemo(() => [chat, model], [config]);
162
+
163
+ return (
164
+ <Form
165
+ initialValues={config}
166
+ items={items}
167
+ onValuesChange={debounce(updateAgentConfig, 100)}
168
+ {...FORM_STYLE}
169
+ />
111
170
  );
112
171
  };
113
172
 
@@ -0,0 +1,39 @@
1
+ import { ActionIcon } from '@lobehub/ui';
2
+ import { Input, InputProps } from 'antd';
3
+ import { useTheme } from 'antd-style';
4
+ import { Wand2 } from 'lucide-react';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+
8
+ export interface AutoGenerateInputProps extends InputProps {
9
+ loading: boolean;
10
+ onGenerate: () => void;
11
+ }
12
+
13
+ const AutoGenerateInput = memo<AutoGenerateInputProps>(({ loading, onGenerate, ...props }) => {
14
+ const { t } = useTranslation('common');
15
+ const theme = useTheme();
16
+
17
+ return (
18
+ <Input
19
+ suffix={
20
+ <ActionIcon
21
+ active
22
+ icon={Wand2}
23
+ loading={loading}
24
+ onClick={onGenerate}
25
+ size={'small'}
26
+ style={{
27
+ color: theme.colorInfo,
28
+ marginRight: -4,
29
+ }}
30
+ title={t('autoGenerate')}
31
+ />
32
+ }
33
+ type={'block'}
34
+ {...props}
35
+ />
36
+ );
37
+ });
38
+
39
+ export default AutoGenerateInput;
@@ -0,0 +1,31 @@
1
+ import { Swatches, primaryColorsSwatches } from '@lobehub/ui';
2
+ import { memo } from 'react';
3
+ import { shallow } from 'zustand/shallow';
4
+
5
+ import { DEFAULT_BACKGROUND_COLOR } from '@/const/meta';
6
+ import { agentSelectors, useSessionStore } from '@/store/session';
7
+
8
+ const BackgroundSwatches = memo(() => {
9
+ const [backgroundColor, updateAgentMeta] = useSessionStore(
10
+ (s) => [agentSelectors.currentAgentBackgroundColor(s), s.updateAgentMeta],
11
+ shallow,
12
+ );
13
+
14
+ const handleSelect = (v: any) => {
15
+ if (v) {
16
+ updateAgentMeta({ backgroundColor: v });
17
+ } else {
18
+ updateAgentMeta({ backgroundColor: DEFAULT_BACKGROUND_COLOR });
19
+ }
20
+ };
21
+
22
+ return (
23
+ <Swatches
24
+ activeColor={backgroundColor}
25
+ colors={primaryColorsSwatches}
26
+ onSelect={handleSelect}
27
+ />
28
+ );
29
+ });
30
+
31
+ export default BackgroundSwatches;
@@ -39,8 +39,12 @@ const useStyles = createStyles(({ css, token, prefixCls }) => ({
39
39
 
40
40
  const EmojiPicker = () => {
41
41
  const { styles } = useStyles();
42
- const [avatar, updateAgentMeta] = useSessionStore(
43
- (s) => [agentSelectors.currentAgentAvatar(s), s.updateAgentMeta],
42
+ const [avatar, backgroundColor, updateAgentMeta] = useSessionStore(
43
+ (s) => [
44
+ agentSelectors.currentAgentAvatar(s),
45
+ agentSelectors.currentAgentBackgroundColor(s),
46
+ s.updateAgentMeta,
47
+ ],
44
48
  shallow,
45
49
  );
46
50
 
@@ -65,7 +69,7 @@ const EmojiPicker = () => {
65
69
  trigger={'click'}
66
70
  >
67
71
  <div className={styles.avatar} style={{ width: 'fit-content' }}>
68
- <Avatar avatar={avatar} size={200} />
72
+ <Avatar avatar={avatar} background={backgroundColor} size={44} />
69
73
  </div>
70
74
  </Popover>
71
75
  );
@@ -1,21 +1,20 @@
1
- import { ActionIcon, Input, Tooltip } from '@lobehub/ui';
2
- import { Button, Collapse } from 'antd';
1
+ import { Form, Icon, type ItemGroup, Tooltip } from '@lobehub/ui';
2
+ import { Button } from 'antd';
3
3
  import isEqual from 'fast-deep-equal';
4
- import { LucideSparkles } from 'lucide-react';
4
+ import { UserCircle, Wand2 } from 'lucide-react';
5
+ import { memo, useMemo } from 'react';
5
6
  import { useTranslation } from 'react-i18next';
6
- import { Flexbox } from 'react-layout-kit';
7
7
  import { shallow } from 'zustand/shallow';
8
8
 
9
+ import { FORM_STYLE } from '@/const/layoutTokens';
9
10
  import { agentSelectors, useSessionStore } from '@/store/session';
10
11
 
11
- import { FormItem } from '../FormItem';
12
- import { useStyles } from '../style';
12
+ import AutoGenerateInput from './AutoGenerateInput';
13
+ import BackgroundSwatches from './BackgroundSwatches';
13
14
  import EmojiPicker from './EmojiPicker';
14
15
 
15
- const AgentMeta = () => {
16
- const { t } = useTranslation('common');
17
-
18
- const { styles, theme } = useStyles();
16
+ const AgentMeta = memo(() => {
17
+ const { t } = useTranslation('setting');
19
18
 
20
19
  const metaData = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
21
20
 
@@ -39,80 +38,74 @@ const AgentMeta = () => {
39
38
  );
40
39
 
41
40
  const basic = [
42
- { key: 'title', label: t('agentName'), placeholder: t('agentNamePlaceholder') },
41
+ {
42
+ key: 'title',
43
+ label: t('settingAgent.name.title'),
44
+ placeholder: t('settingAgent.name.placeholder'),
45
+ },
43
46
  {
44
47
  key: 'description',
45
- label: t('agentDescription'),
46
- placeholder: t('agentDescriptionPlaceholder'),
48
+ label: t('settingAgent.description.title'),
49
+ placeholder: t('settingAgent.description.placeholder'),
47
50
  },
48
51
  // { key: 'tag', label: t('agentTag'), placeholder: t('agentTagPlaceholder') },
49
52
  ];
50
53
 
51
- return (
52
- <Collapse
53
- defaultActiveKey={hasSystemRole ? ['meta'] : []}
54
- items={[
54
+ const meta: ItemGroup = useMemo(
55
+ () => ({
56
+ children: [
57
+ {
58
+ children: <EmojiPicker />,
59
+ label: t('settingAgent.avatar.title'),
60
+ minWidth: undefined,
61
+ },
55
62
  {
63
+ children: <BackgroundSwatches />,
64
+ label: t('settingAgent.backgroundColor.title'),
65
+ minWidth: undefined,
66
+ },
67
+ ...basic.map((item) => ({
56
68
  children: (
57
- <Flexbox gap={80} horizontal style={{ marginTop: 16 }}>
58
- <Flexbox flex={1} gap={24}>
59
- {basic.map((item) => (
60
- <FormItem key={item.key} label={item.label}>
61
- <Input
62
- onChange={(e) => {
63
- updateAgentMeta({ [item.key]: e.target.value });
64
- }}
65
- placeholder={item.placeholder}
66
- suffix={
67
- <ActionIcon
68
- icon={LucideSparkles}
69
- loading={loading[item.key as keyof typeof loading]}
70
- onClick={() => {
71
- autocompleteMeta(item.key as keyof typeof metaData);
72
- }}
73
- size={'small'}
74
- style={{
75
- color: theme.purple,
76
- }}
77
- title={t('autoGenerate')}
78
- />
79
- }
80
- type={'block'}
81
- value={metaData[item.key as keyof typeof metaData]}
82
- />
83
- </FormItem>
84
- ))}
85
- </Flexbox>
86
- <FormItem label={t('agentAvatar')}>
87
- <EmojiPicker />
88
- </FormItem>
89
- </Flexbox>
90
- ),
91
- className: styles.collapseHeader,
92
- extra: (
93
- <Tooltip title={t('autoGenerateTooltip')}>
94
- <Button
95
- disabled={!hasSystemRole}
96
- loading={Object.values(loading).some((i) => !!i)}
97
- onClick={(e: any) => {
98
- e.stopPropagation();
99
- console.log(id);
100
- if (!id) return;
101
-
102
- autocompleteSessionAgentMeta(id, true);
103
- }}
104
- size={'large'}
105
- >
106
- {t('autoGenerate')}
107
- </Button>
108
- </Tooltip>
69
+ <AutoGenerateInput
70
+ loading={loading[item.key as keyof typeof loading]}
71
+ onChange={(e) => {
72
+ updateAgentMeta({ [item.key]: e.target.value });
73
+ }}
74
+ onGenerate={() => {
75
+ autocompleteMeta(item.key as keyof typeof metaData);
76
+ }}
77
+ placeholder={item.placeholder}
78
+ value={metaData[item.key as keyof typeof metaData]}
79
+ />
109
80
  ),
110
- key: 'meta',
111
- label: <Flexbox className={styles.profile}>{t('profile')}</Flexbox>,
112
- },
113
- ]}
114
- />
81
+ label: item.label,
82
+ })),
83
+ ],
84
+ extra: (
85
+ <Tooltip title={t('autoGenerateTooltip', { ns: 'common' })}>
86
+ <Button
87
+ disabled={!hasSystemRole}
88
+ icon={<Icon icon={Wand2} />}
89
+ loading={Object.values(loading).some((i) => !!i)}
90
+ onClick={(e: any) => {
91
+ e.stopPropagation();
92
+ console.log(id);
93
+ if (!id) return;
94
+ autocompleteSessionAgentMeta(id, true);
95
+ }}
96
+ size={'small'}
97
+ >
98
+ {t('autoGenerate', { ns: 'common' })}
99
+ </Button>
100
+ </Tooltip>
101
+ ),
102
+ icon: UserCircle,
103
+ title: t('settingAgent.title'),
104
+ }),
105
+ [basic, metaData],
115
106
  );
116
- };
107
+
108
+ return <Form items={[meta]} {...FORM_STYLE} />;
109
+ });
117
110
 
118
111
  export default AgentMeta;
@@ -0,0 +1,44 @@
1
+ import { Avatar, Form, ItemGroup } from '@lobehub/ui';
2
+ import { Switch } from 'antd';
3
+ import isEqual from 'fast-deep-equal';
4
+ import { ToyBrick } from 'lucide-react';
5
+ import { useMemo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { shallow } from 'zustand/shallow';
8
+
9
+ import { FORM_STYLE } from '@/const/layoutTokens';
10
+ import pluginList from '@/plugins';
11
+ import { agentSelectors, useSessionStore } from '@/store/session';
12
+
13
+ const PluginList = () => {
14
+ const { t } = useTranslation('setting');
15
+
16
+ const config = useSessionStore(agentSelectors.currentAgentConfigSafe, isEqual);
17
+
18
+ const toggleAgentPlugin = useSessionStore((s) => s.toggleAgentPlugin, shallow);
19
+
20
+ const plugin: ItemGroup = useMemo(
21
+ () => ({
22
+ children: pluginList.map((item) => ({
23
+ avatar: <Avatar avatar={item.avatar} />,
24
+ children: (
25
+ <Switch
26
+ checked={!config.plugins ? false : config.plugins.includes(item.name)}
27
+ onChange={() => toggleAgentPlugin(item.name)}
28
+ />
29
+ ),
30
+ desc: item.schema.description,
31
+ label: t(`plugins.${item.name}` as any, { ns: 'plugin' }),
32
+ minWidth: undefined,
33
+ tag: item.name,
34
+ })),
35
+ icon: ToyBrick,
36
+ title: t('settingPlugin.title'),
37
+ }),
38
+ [config],
39
+ );
40
+
41
+ return <Form items={[plugin]} {...FORM_STYLE} />;
42
+ };
43
+
44
+ export default PluginList;
@@ -0,0 +1,48 @@
1
+ import { CodeEditor, FormGroup } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { Bot } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { shallow } from 'zustand/shallow';
7
+
8
+ import { FORM_STYLE } from '@/const/layoutTokens';
9
+ import { agentSelectors, useSessionStore } from '@/store/session';
10
+
11
+ export const useStyles = createStyles(({ css, token }) => ({
12
+ input: css`
13
+ padding: 12px;
14
+ background: ${token.colorFillTertiary};
15
+ border: 1px solid ${token.colorPrimaryBorder};
16
+ border-radius: 8px;
17
+ `,
18
+ markdown: css`
19
+ padding: 12px;
20
+ background: ${token.colorFillTertiary};
21
+ `,
22
+ }));
23
+
24
+ const AgentPrompt = memo(() => {
25
+ const { t } = useTranslation('setting');
26
+
27
+ const systemRole = useSessionStore(agentSelectors.currentAgentSystemRole, shallow);
28
+
29
+ const [updateAgentConfig] = useSessionStore((s) => [s.updateAgentConfig], shallow);
30
+
31
+ return (
32
+ <FormGroup icon={Bot} style={FORM_STYLE.style} title={t('settingAgent.prompt.title')}>
33
+ <CodeEditor
34
+ language={'md'}
35
+ onValueChange={(e) => {
36
+ updateAgentConfig({ systemRole: e });
37
+ }}
38
+ placeholder={t('settingAgent.name.placeholder')}
39
+ resize={false}
40
+ style={{ marginBottom: 16, marginTop: 16 }}
41
+ type={'pure'}
42
+ value={systemRole}
43
+ />
44
+ </FormGroup>
45
+ );
46
+ });
47
+
48
+ export default AgentPrompt;
@@ -0,0 +1,31 @@
1
+ import { ActionIcon, ChatHeader } from '@lobehub/ui';
2
+ import { Download, Share2 } from 'lucide-react';
3
+ import Router from 'next/router';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import HeaderTitle from '@/components/HeaderTitle';
8
+
9
+ const Header = memo(() => {
10
+ const { t } = useTranslation('setting');
11
+
12
+ return (
13
+ <ChatHeader
14
+ left={<HeaderTitle title={t('header.session')} />}
15
+ onBackClick={() => Router.back()}
16
+ right={
17
+ <>
18
+ <ActionIcon icon={Share2} size={{ fontSize: 24 }} title={t('share', { ns: 'common' })} />
19
+ <ActionIcon
20
+ icon={Download}
21
+ size={{ fontSize: 24 }}
22
+ title={t('export', { ns: 'common' })}
23
+ />
24
+ </>
25
+ }
26
+ showBackButton
27
+ />
28
+ );
29
+ });
30
+
31
+ export default Header;