@lobehub/chat 0.22.2 → 0.23.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ # [0.23.0](https://github.com/lobehub/lobe-chat/compare/v0.22.2...v0.23.0) (2023-07-26)
6
+
7
+ ### ✨ Features
8
+
9
+ - Add new features, update URLs, customize appearance, and implement components ([4b61bf4](https://github.com/lobehub/lobe-chat/commit/4b61bf4))
10
+
5
11
  ### [Version 0.22.2](https://github.com/lobehub/lobe-chat/compare/v0.22.1...v0.22.2)
6
12
 
7
13
  <sup>Released on **2023-07-26**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.22.2",
3
+ "version": "0.23.0",
4
4
  "description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -11,7 +11,7 @@
11
11
  ],
12
12
  "homepage": "https://github.com/lobehub/lobe-chat",
13
13
  "bugs": {
14
- "url": "https://github.com/lobehub/lobe-chat/issues/new"
14
+ "url": "https://github.com/lobehub/lobe-chat/issues/new/choose"
15
15
  },
16
16
  "repository": {
17
17
  "type": "git",
@@ -30,6 +30,7 @@
30
30
  "lint:style": "stylelint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
31
31
  "prepare": "husky install",
32
32
  "prettier": "prettier -c --write \"**/**\"",
33
+ "pull": "git pull",
33
34
  "release": "semantic-release",
34
35
  "start": "next start",
35
36
  "stylelint": "stylelint \"src/**/*.{js,jsx,ts,tsx}\" --fix",
@@ -141,5 +142,6 @@
141
142
  "publishConfig": {
142
143
  "access": "public",
143
144
  "registry": "https://registry.npmjs.org"
144
- }
145
+ },
146
+ "changelog": "https://github.com/lobehub/lobe-chat"
145
147
  }
@@ -36,7 +36,12 @@ const HeaderTitle = memo<HeaderTitleProps>(({ title, desc, tag }) => {
36
36
  </Flexbox>
37
37
  </Flexbox>
38
38
  );
39
- return <div className={styles.title}>{title}</div>;
39
+ return (
40
+ <Flexbox align={'center'} className={styles.title} gap={8} horizontal>
41
+ {title}
42
+ {tag}
43
+ </Flexbox>
44
+ );
40
45
  });
41
46
 
42
47
  export default HeaderTitle;
@@ -1,36 +1,61 @@
1
- import { InputNumber, Slider } from 'antd';
1
+ import { InputNumber, type InputNumberProps, Slider } from 'antd';
2
2
  import { SliderSingleProps } from 'antd/es/slider';
3
3
  import { isNull } from 'lodash-es';
4
4
  import { memo, useCallback } from 'react';
5
5
  import { Flexbox } from 'react-layout-kit';
6
6
 
7
- const SliderWithInput = memo<SliderSingleProps>(
8
- ({ step, value, onChange, max, min, defaultValue, ...props }) => {
7
+ export interface SliderWithInputProps extends SliderSingleProps {
8
+ controls?: InputNumberProps['controls'];
9
+ size?: InputNumberProps['size'];
10
+ }
11
+
12
+ const SliderWithInput = memo<SliderWithInputProps>(
13
+ ({
14
+ step,
15
+ value,
16
+ onChange,
17
+ max,
18
+ min,
19
+ defaultValue,
20
+ size,
21
+ controls,
22
+ style,
23
+ className,
24
+ ...props
25
+ }) => {
9
26
  const handleOnchange = useCallback((value: number | null) => {
10
27
  if (Number.isNaN(value) || isNull(value)) return;
11
28
  onChange?.(value);
12
29
  }, []);
13
30
 
14
31
  return (
15
- <Flexbox direction={'horizontal'} gap={8}>
32
+ <Flexbox
33
+ align={'center'}
34
+ className={className}
35
+ direction={'horizontal'}
36
+ gap={8}
37
+ style={style}
38
+ >
16
39
  <Slider
17
40
  defaultValue={defaultValue}
18
41
  max={max}
19
42
  min={min}
20
43
  onChange={handleOnchange}
21
44
  step={step}
22
- style={{ flex: 1 }}
23
- tooltip={{ getPopupContainer: (triggerNode) => triggerNode }}
45
+ style={size === 'small' ? { flex: 1, margin: 0 } : { flex: 1 }}
46
+ tooltip={{ open: false }}
24
47
  value={typeof value === 'number' ? value : 0}
25
48
  {...props}
26
49
  />
27
50
  <InputNumber
51
+ controls={size !== 'small' || controls}
28
52
  defaultValue={defaultValue}
29
53
  max={max}
30
54
  min={min}
31
55
  onChange={handleOnchange}
56
+ size={size}
32
57
  step={Number.isNaN(step) || isNull(step) ? undefined : step}
33
- style={{ flex: 1, maxWidth: 64 }}
58
+ style={{ flex: 1, maxWidth: size === 'small' ? 40 : 64 }}
34
59
  value={typeof value === 'number' ? value : 0}
35
60
  />
36
61
  </Flexbox>
@@ -1,18 +1,117 @@
1
- import { ActionIcon, SideNav } from '@lobehub/ui';
2
- import { MessageSquare, Settings2, Sticker } from 'lucide-react';
1
+ import { ActionIcon, Icon, SideNav } from '@lobehub/ui';
2
+ import { Dropdown, type MenuProps } from 'antd';
3
+ import {
4
+ Feather,
5
+ FileClock,
6
+ FolderInput,
7
+ FolderOutput,
8
+ Github,
9
+ Heart,
10
+ MessageSquare,
11
+ Settings,
12
+ Settings2,
13
+ Sticker,
14
+ } from 'lucide-react';
3
15
  import Router from 'next/router';
4
- import { memo } from 'react';
16
+ import { memo, useMemo } from 'react';
17
+ import { useTranslation } from 'react-i18next';
5
18
  import { shallow } from 'zustand/shallow';
6
19
 
20
+ import { bugs, homepage } from '@/../package.json';
7
21
  import AvatarWithUpload from '@/features/AvatarWithUpload';
8
22
  import { useSettings } from '@/store/settings';
9
23
 
10
24
  export default memo(() => {
11
25
  const [tab, setTab] = useSettings((s) => [s.sidebarKey, s.switchSideBar], shallow);
26
+ const { t } = useTranslation('common');
27
+ const items: MenuProps['items'] = useMemo(
28
+ () => [
29
+ {
30
+ icon: <Icon icon={FolderInput} />,
31
+ key: 'import',
32
+ label: <div>{t('import')}</div>,
33
+ },
34
+ {
35
+ children: [
36
+ {
37
+ key: 'allAgent',
38
+ label: <div>{t('exportType.allAgent')}</div>,
39
+ },
40
+ {
41
+ key: 'allAgentWithMessage',
42
+ label: <div>{t('exportType.allAgentWithMessage')}</div>,
43
+ },
44
+ {
45
+ key: 'globalSetting',
46
+ label: <div>{t('exportType.globalSetting')}</div>,
47
+ },
48
+ {
49
+ type: 'divider',
50
+ },
51
+ {
52
+ key: 'all',
53
+ label: <div>{t('exportType.all')}</div>,
54
+ },
55
+ ],
56
+ icon: <Icon icon={FolderOutput} />,
57
+ key: 'export',
58
+ label: t('export'),
59
+ },
60
+ {
61
+ type: 'divider',
62
+ },
63
+ {
64
+ icon: <Icon icon={Feather} />,
65
+ key: 'feedback',
66
+ label: (
67
+ <a href={bugs.url} rel="noreferrer" target={'_blank'}>
68
+ {t('feedback')}
69
+ </a>
70
+ ),
71
+ },
72
+ {
73
+ icon: <Icon icon={FileClock} />,
74
+ key: 'changelog',
75
+ label: (
76
+ <a href={`${homepage}/blob/master/CHANGELOG.md`} rel="noreferrer" target={'_blank'}>
77
+ {t('changelog')}
78
+ </a>
79
+ ),
80
+ },
81
+ {
82
+ icon: <Icon icon={Heart} />,
83
+ key: 'about',
84
+ label: (
85
+ <a href={homepage} rel="noreferrer" target={'_blank'}>
86
+ {t('about')}
87
+ </a>
88
+ ),
89
+ },
90
+ {
91
+ type: 'divider',
92
+ },
93
+ {
94
+ icon: <Icon icon={Settings} />,
95
+ key: 'setting',
96
+ label: <div onClick={() => Router.push('/setting')}>{t('setting')}</div>,
97
+ },
98
+ ],
99
+ [],
100
+ );
101
+
12
102
  return (
13
103
  <SideNav
14
104
  avatar={<AvatarWithUpload />}
15
- bottomActions={<ActionIcon icon={Settings2} onClick={() => Router.push('/setting')} />}
105
+ bottomActions={
106
+ <>
107
+ <a href={homepage} rel="noreferrer" target={'_blank'}>
108
+ <ActionIcon icon={Github} />
109
+ </a>
110
+ <Dropdown arrow={false} menu={{ items }} trigger={['click']}>
111
+ <ActionIcon icon={Settings2} />
112
+ </Dropdown>
113
+ </>
114
+ }
16
115
  style={{ height: '100vh' }}
17
116
  topActions={
18
117
  <>
@@ -1,4 +1,5 @@
1
1
  export default {
2
+ about: '关于',
2
3
  advanceSettings: '高级设置',
3
4
  agentMaxToken: '会话最大长度',
4
5
  agentModel: '模型',
@@ -7,6 +8,7 @@ export default {
7
8
  autoGenerate: '自动补全',
8
9
  autoGenerateTooltip: '基于提示词自动补全助手描述',
9
10
  cancel: '取消',
11
+ changelog: '更新日志',
10
12
  clearCurrentMessages: '清空当前会话消息',
11
13
  close: '关闭',
12
14
  confirmClearCurrentMessages: '即将清空当前会话消息,清空后将无法找回,请确认你的操作',
@@ -14,7 +16,17 @@ export default {
14
16
  defaultAgent: '默认助手',
15
17
  defaultSession: '默认对话',
16
18
  edit: '编辑',
17
- export: '导出',
19
+ export: '导出配置',
20
+ exportType: {
21
+ agent: '导出助手设定',
22
+ agentWithMessage: '导出助手和消息',
23
+ all: '导出全局设置和所有助手数据',
24
+ allAgent: '导出所有助手设定',
25
+ allAgentWithMessage: '导出所有助手和消息',
26
+ globalSetting: '导出全局设置',
27
+ },
28
+ feedback: '反馈与建议',
29
+ import: '导入配置',
18
30
  newAgent: '新建助手',
19
31
  noDescription: '暂无描述',
20
32
  ok: '确定',
@@ -1,7 +1,8 @@
1
1
  import { ActionIcon, Avatar, List } from '@lobehub/ui';
2
+ import { useHover } from 'ahooks';
2
3
  import { Popconfirm, Tag } from 'antd';
3
4
  import { X } from 'lucide-react';
4
- import { FC, memo } from 'react';
5
+ import { FC, memo, useRef } from 'react';
5
6
  import { useTranslation } from 'react-i18next';
6
7
  import { Flexbox } from 'react-layout-kit';
7
8
  import { shallow } from 'zustand/shallow';
@@ -20,6 +21,8 @@ interface SessionItemProps {
20
21
  }
21
22
 
22
23
  const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading }) => {
24
+ const ref = useRef(null);
25
+ const isHovering = useHover(ref);
23
26
  const { t } = useTranslation('common');
24
27
  const { styles, theme, cx } = useStyles();
25
28
  const [defaultModel] = useSettings((s) => [s.settings.model], shallow);
@@ -55,71 +58,74 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading })
55
58
  const showChatLength = chatLength > 0;
56
59
 
57
60
  return (
58
- <Flexbox className={styles.container} style={{ position: 'relative' }}>
59
- <Item
60
- active={active}
61
- avatar={
62
- <Avatar
63
- avatar={avatar}
64
- background={avatarBackground}
65
- shape="circle"
66
- size={46}
67
- title={title}
68
- />
69
- }
70
- classNames={{ time: cx('session-time', styles.time) }}
71
- date={updateAt}
72
- description={
73
- <Flexbox gap={4}>
74
- <Flexbox>{description || systemRole}</Flexbox>
61
+ <div ref={ref}>
62
+ <Flexbox className={styles.container} style={{ position: 'relative' }}>
63
+ <Item
64
+ active={active}
65
+ avatar={
66
+ <Avatar
67
+ animation={isHovering}
68
+ avatar={avatar}
69
+ background={avatarBackground}
70
+ shape="circle"
71
+ size={46}
72
+ title={title}
73
+ />
74
+ }
75
+ classNames={{ time: cx('session-time', styles.time) }}
76
+ date={updateAt}
77
+ description={
78
+ <Flexbox gap={4}>
79
+ <Flexbox>{description || systemRole}</Flexbox>
75
80
 
76
- {!(showModel || showChatLength) ? undefined : (
77
- <Flexbox horizontal>
78
- {showModel && (
79
- <Tag bordered={false} style={{ color: theme.colorTextSecondary }}>
80
- {model}
81
- </Tag>
82
- )}
83
- {/*{showChatLength && (*/}
84
- {/* <Tag*/}
85
- {/* bordered={false}*/}
86
- {/* style={{ color: theme.colorTextSecondary, display: 'flex', gap: 4 }}*/}
87
- {/* >*/}
88
- {/* <Icon icon={LucideMessageCircle} />*/}
89
- {/* {chatLength}*/}
90
- {/* </Tag>*/}
91
- {/*)}*/}
92
- </Flexbox>
93
- )}
94
- </Flexbox>
95
- }
96
- loading={loading}
97
- style={{ color: theme.colorText }}
98
- title={title}
99
- />
100
- <Popconfirm
101
- arrow={false}
102
- cancelText={t('cancel')}
103
- okButtonProps={{ danger: true }}
104
- okText={t('ok')}
105
- onConfirm={(e) => {
106
- e?.stopPropagation();
107
- removeSession(id);
108
- }}
109
- overlayStyle={{ width: 280 }}
110
- title={t('confirmRemoveSessionItemAlert')}
111
- >
112
- <ActionIcon
113
- className="session-remove"
114
- icon={X}
115
- onClick={(e) => {
116
- e.preventDefault();
117
- e.stopPropagation();
118
- }}
119
- size={'small'}
81
+ {!(showModel || showChatLength) ? undefined : (
82
+ <Flexbox horizontal>
83
+ {showModel && (
84
+ <Tag bordered={false} style={{ color: theme.colorTextSecondary }}>
85
+ {model}
86
+ </Tag>
87
+ )}
88
+ {/*{showChatLength && (*/}
89
+ {/* <Tag*/}
90
+ {/* bordered={false}*/}
91
+ {/* style={{ color: theme.colorTextSecondary, display: 'flex', gap: 4 }}*/}
92
+ {/* >*/}
93
+ {/* <Icon icon={LucideMessageCircle} />*/}
94
+ {/* {chatLength}*/}
95
+ {/* </Tag>*/}
96
+ {/*)}*/}
97
+ </Flexbox>
98
+ )}
99
+ </Flexbox>
100
+ }
101
+ loading={loading}
102
+ style={{ color: theme.colorText }}
103
+ title={title}
120
104
  />
121
- </Popconfirm>
122
- </Flexbox>
105
+ <Popconfirm
106
+ arrow={false}
107
+ cancelText={t('cancel')}
108
+ okButtonProps={{ danger: true }}
109
+ okText={t('ok')}
110
+ onConfirm={(e) => {
111
+ e?.stopPropagation();
112
+ removeSession(id);
113
+ }}
114
+ overlayStyle={{ width: 280 }}
115
+ title={t('confirmRemoveSessionItemAlert')}
116
+ >
117
+ <ActionIcon
118
+ className="session-remove"
119
+ icon={X}
120
+ onClick={(e) => {
121
+ e.preventDefault();
122
+ e.stopPropagation();
123
+ }}
124
+ size={'small'}
125
+ />
126
+ </Popconfirm>
127
+ </Flexbox>
128
+ </div>
123
129
  );
124
130
  }, shallow);
125
131
 
@@ -39,15 +39,18 @@ const InputActions = memo(() => {
39
39
  <ActionIcon icon={BrainCog} placement={'bottom'} title={t('settingModel.model.title')} />
40
40
  </Dropdown>
41
41
  <Popover
42
+ arrow={false}
42
43
  content={
43
44
  <SliderWithInput
45
+ controls={false}
44
46
  max={1}
45
47
  min={0}
46
48
  onChange={(v) => {
47
49
  updateAgentConfig({ params: { temperature: v } });
48
50
  }}
51
+ size={'small'}
49
52
  step={0.1}
50
- style={{ width: 120 }}
53
+ style={{ width: 160 }}
51
54
  value={temperature}
52
55
  />
53
56
  }
@@ -1,7 +1,8 @@
1
1
  import { ActionIcon, ChatHeader } from '@lobehub/ui';
2
- import { Download, Share2 } from 'lucide-react';
2
+ import { Dropdown, MenuProps } from 'antd';
3
+ import { FolderOutput, Share2 } from 'lucide-react';
3
4
  import Router from 'next/router';
4
- import { memo } from 'react';
5
+ import { memo, useMemo } from 'react';
5
6
  import { useTranslation } from 'react-i18next';
6
7
 
7
8
  import HeaderTitle from '@/components/HeaderTitle';
@@ -9,6 +10,20 @@ import HeaderTitle from '@/components/HeaderTitle';
9
10
  const Header = memo(() => {
10
11
  const { t } = useTranslation('setting');
11
12
 
13
+ const items: MenuProps['items'] = useMemo(
14
+ () => [
15
+ {
16
+ key: 'agent',
17
+ label: <div>{t('exportType.agent', { ns: 'common' })}</div>,
18
+ },
19
+ {
20
+ key: 'agentWithMessage',
21
+ label: <div>{t('exportType.agentWithMessage', { ns: 'common' })}</div>,
22
+ },
23
+ ],
24
+ [],
25
+ );
26
+
12
27
  return (
13
28
  <ChatHeader
14
29
  left={<HeaderTitle title={t('header.session')} />}
@@ -16,11 +31,13 @@ const Header = memo(() => {
16
31
  right={
17
32
  <>
18
33
  <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
- />
34
+ <Dropdown arrow={false} menu={{ items }} trigger={['click']}>
35
+ <ActionIcon
36
+ icon={FolderOutput}
37
+ size={{ fontSize: 24 }}
38
+ title={t('export', { ns: 'common' })}
39
+ />
40
+ </Dropdown>
24
41
  </>
25
42
  }
26
43
  showBackButton
@@ -1,8 +1,10 @@
1
1
  import { ChatHeader } from '@lobehub/ui';
2
+ import { Tag } from 'antd';
2
3
  import Router from 'next/router';
3
4
  import { memo } from 'react';
4
5
  import { useTranslation } from 'react-i18next';
5
6
 
7
+ import { version } from '@/../package.json';
6
8
  import HeaderTitle from '@/components/HeaderTitle';
7
9
 
8
10
  const Header = memo(() => {
@@ -10,7 +12,7 @@ const Header = memo(() => {
10
12
 
11
13
  return (
12
14
  <ChatHeader
13
- left={<HeaderTitle title={t('header.global')} />}
15
+ left={<HeaderTitle tag={<Tag>{`v${version}`}</Tag>} title={t('header.global')} />}
14
16
  onBackClick={() => Router.back()}
15
17
  showBackButton
16
18
  />