@lobehub/chat 0.39.3 → 0.40.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,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 0.40.0](https://github.com/lobehub/lobe-chat/compare/v0.39.4...v0.40.0)
6
+
7
+ <sup>Released on **2023-08-05**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Add new dependency, add Tag and PluginTag components, update HeaderTitle.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Add new dependency, add Tag and PluginTag components, update HeaderTitle, closes [#56](https://github.com/lobehub/lobe-chat/issues/56) [#55](https://github.com/lobehub/lobe-chat/issues/55) [#54](https://github.com/lobehub/lobe-chat/issues/54) ([2812ea2](https://github.com/lobehub/lobe-chat/commit/2812ea2))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 0.39.4](https://github.com/lobehub/lobe-chat/compare/v0.39.3...v0.39.4)
31
+
32
+ <sup>Released on **2023-08-05**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: 修正 assistant 消息没有 background 的问题.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: 修正 assistant 消息没有 background 的问题, closes [#42](https://github.com/lobehub/lobe-chat/issues/42) ([812e976](https://github.com/lobehub/lobe-chat/commit/812e976))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 0.39.3](https://github.com/lobehub/lobe-chat/compare/v0.39.2...v0.39.3)
6
56
 
7
57
  <sup>Released on **2023-08-04**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.39.3",
3
+ "version": "0.40.0",
4
4
  "description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -64,6 +64,7 @@
64
64
  "@commitlint/cli": "^17",
65
65
  "@emoji-mart/data": "^1",
66
66
  "@emoji-mart/react": "^1",
67
+ "@icons-pack/react-simple-icons": "^8",
67
68
  "@lobehub/ui": "latest",
68
69
  "@vercel/analytics": "^1",
69
70
  "ahooks": "^3",
@@ -1,21 +1,21 @@
1
- import { ActionIcon } from '@lobehub/ui';
1
+ import { ActionIcon, DivProps } from '@lobehub/ui';
2
2
  import { X } from 'lucide-react';
3
3
  import { memo, useState } from 'react';
4
4
 
5
5
  import { useStyles } from './style';
6
6
 
7
- interface EmptyProps {
7
+ interface EmptyProps extends DivProps {
8
8
  cover: string;
9
9
  desc: string;
10
10
  title: string;
11
11
  }
12
12
 
13
- const Empty = memo<EmptyProps>(({ cover, title, desc }) => {
13
+ const Empty = memo<EmptyProps>(({ cover, title, desc, ...props }) => {
14
14
  const [visiable, setVisiable] = useState(true);
15
15
  const { styles } = useStyles();
16
16
  if (!visiable) return null;
17
17
  return (
18
- <div className={styles.container}>
18
+ <div className={styles.container} {...props}>
19
19
  <ActionIcon
20
20
  className={styles.close}
21
21
  icon={X}
@@ -3,16 +3,41 @@ import { ReactNode, memo } from 'react';
3
3
  import { Flexbox } from 'react-layout-kit';
4
4
 
5
5
  const useStyles = createStyles(({ css, token }) => ({
6
+ container: css`
7
+ position: relative;
8
+ overflow: hidden;
9
+ flex: 1;
10
+ max-width: 100%;
11
+ `,
6
12
  desc: css`
13
+ overflow: hidden;
14
+
15
+ width: 100%;
16
+
7
17
  font-size: 12px;
8
18
  color: ${token.colorTextTertiary};
19
+ text-overflow: ellipsis;
20
+ white-space: nowrap;
21
+ `,
22
+ tag: css`
23
+ flex: none;
9
24
  `,
10
25
  title: css`
26
+ overflow: hidden;
27
+
11
28
  font-size: 16px;
12
29
  font-weight: bold;
30
+ text-overflow: ellipsis;
31
+ white-space: nowrap;
32
+ `,
33
+ titleContainer: css`
34
+ flex: 1;
13
35
  `,
14
36
  titleWithDesc: css`
37
+ overflow: hidden;
15
38
  font-weight: bold;
39
+ text-overflow: ellipsis;
40
+ white-space: nowrap;
16
41
  `,
17
42
  }));
18
43
 
@@ -26,20 +51,24 @@ const HeaderTitle = memo<HeaderTitleProps>(({ title, desc, tag }) => {
26
51
  const { styles } = useStyles();
27
52
  if (desc)
28
53
  return (
29
- <Flexbox>
30
- <Flexbox align={'center'} className={styles.titleWithDesc} gap={8} horizontal>
31
- {title}
32
- {tag}
54
+ <Flexbox className={styles.container}>
55
+ <Flexbox align={'center'} className={styles.titleContainer} gap={8} horizontal>
56
+ <div className={styles.titleWithDesc}>{title}</div>
57
+ <Flexbox className={styles.tag} horizontal>
58
+ {tag}
59
+ </Flexbox>
33
60
  </Flexbox>
34
- <Flexbox align={'center'} className={styles.desc} horizontal>
35
- {desc}
61
+ <Flexbox align={'center'} horizontal>
62
+ <div className={styles.desc}>{desc}</div>
36
63
  </Flexbox>
37
64
  </Flexbox>
38
65
  );
39
66
  return (
40
- <Flexbox align={'center'} className={styles.title} gap={8} horizontal>
41
- {title}
42
- {tag}
67
+ <Flexbox align={'center'} className={styles.container} gap={8} horizontal>
68
+ <div className={styles.title}>{title}</div>
69
+ <Flexbox className={styles.tag} horizontal>
70
+ {tag}
71
+ </Flexbox>
43
72
  </Flexbox>
44
73
  );
45
74
  });
@@ -0,0 +1,38 @@
1
+ import { Avatar } from '@lobehub/ui';
2
+ import type { MenuProps } from 'antd';
3
+ import { Dropdown } from 'antd';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import { PluginsMap } from '@/plugins';
8
+
9
+ import Tag from './index';
10
+
11
+ export interface PluginTagProps {
12
+ plugins: string[];
13
+ }
14
+
15
+ const PluginTag = memo<PluginTagProps>(({ plugins }) => {
16
+ const { t } = useTranslation('plugin');
17
+
18
+ if (plugins.length === 0) return <Tag type={'plugin'}>{t(`plugins.${plugins[0]}` as any)}</Tag>;
19
+
20
+ const items: MenuProps['items'] = plugins.map((id) => ({
21
+ icon: (
22
+ <Avatar avatar={PluginsMap[id].avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />
23
+ ),
24
+ key: id,
25
+ label: t(`plugins.${id}` as any),
26
+ }));
27
+ return (
28
+ <Dropdown menu={{ items }}>
29
+ <div style={{ height: 20 }}>
30
+ <Tag count={plugins.length} type={'plugin'}>
31
+ {t(`plugins.${plugins[0]}` as any)}
32
+ </Tag>
33
+ </div>
34
+ </Dropdown>
35
+ );
36
+ });
37
+
38
+ export default PluginTag;
@@ -0,0 +1,79 @@
1
+ import { SiOpenai } from '@icons-pack/react-simple-icons';
2
+ import { Tag as AntTag, type TagProps as AntTagProps } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import { capitalize as cap } from 'lodash-es';
5
+ import { type LucideIcon, Milestone, ToyBrick } from 'lucide-react';
6
+ import { ReactNode, memo } from 'react';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ const useStyles = createStyles(({ cx, css, token }, hasCount?: boolean) => ({
10
+ count: css`
11
+ display: flex;
12
+ align-items: center;
13
+
14
+ height: 20px;
15
+ padding: 0 8px;
16
+
17
+ font-size: 12px;
18
+ line-height: 1;
19
+
20
+ background: ${token.colorFillTertiary};
21
+ border: ${token.borderRadius}px;
22
+ `,
23
+ tag: cx(
24
+ css`
25
+ color: ${token.colorTextSecondary} !important;
26
+ background: ${token.colorFillSecondary};
27
+ border: ${token.borderRadius}px;
28
+
29
+ &:hover {
30
+ color: ${token.colorText};
31
+ background: ${token.colorFill};
32
+ }
33
+ `,
34
+ hasCount &&
35
+ css`
36
+ padding-right: 0 !important;
37
+ `,
38
+ ),
39
+ }));
40
+
41
+ export interface TagProps extends AntTagProps {
42
+ capitalize?: boolean;
43
+ children: string;
44
+ count?: number;
45
+ icon?: ReactNode;
46
+ type?: 'openai' | 'plugin' | 'version';
47
+ }
48
+
49
+ const Tag = memo<TagProps>(({ type = 'openai', icon, capitalize, count, children, ...props }) => {
50
+ const { styles } = useStyles(Boolean(count && count > 1));
51
+ let IconRender: LucideIcon | any;
52
+
53
+ switch (type) {
54
+ case 'openai': {
55
+ IconRender = SiOpenai;
56
+ break;
57
+ }
58
+ case 'version': {
59
+ IconRender = Milestone;
60
+ break;
61
+ }
62
+ case 'plugin': {
63
+ IconRender = ToyBrick;
64
+ break;
65
+ }
66
+ }
67
+
68
+ return (
69
+ <AntTag bordered={false} className={styles.tag} {...props}>
70
+ <Flexbox align={'center'} gap={4} horizontal>
71
+ {icon ? icon : <IconRender size={'1em'} />}
72
+ {capitalize ? cap(children) : children}
73
+ {count && <div className={styles.count}>{`${count}+`}</div>}
74
+ </Flexbox>
75
+ </AntTag>
76
+ );
77
+ });
78
+
79
+ export default Tag;
@@ -0,0 +1,2 @@
1
+ export { default as PluginTag } from './PluginTag';
2
+ export { default } from './Tag';
@@ -1,12 +1,13 @@
1
1
  import { ActionIcon, Avatar, Icon, List } from '@lobehub/ui';
2
2
  import { useHover } from 'ahooks';
3
- import { App, Dropdown, type MenuProps, Tag } from 'antd';
3
+ import { App, Dropdown, type MenuProps } from 'antd';
4
4
  import { FolderOutput, MoreVertical, Pin, PinOff, Trash } from 'lucide-react';
5
5
  import { FC, memo, useMemo, useRef, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
  import { shallow } from 'zustand/shallow';
9
9
 
10
+ import Tag from '@/components/Tag';
10
11
  import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
11
12
  import { agentSelectors, chatSelectors, sessionSelectors, useSessionStore } from '@/store/session';
12
13
  import { useSettings } from '@/store/settings';
@@ -28,7 +29,7 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading })
28
29
 
29
30
  const { t } = useTranslation('common');
30
31
 
31
- const { styles, theme } = useStyles();
32
+ const { styles } = useStyles();
32
33
  const [defaultModel] = useSettings((s) => [s.settings.defaultAgent.config?.model], shallow);
33
34
 
34
35
  const [
@@ -146,6 +147,17 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading })
146
147
  </Dropdown>
147
148
  }
148
149
  active={active}
150
+ addon={
151
+ !(showModel || showChatLength) ? undefined : (
152
+ <Flexbox gap={4} horizontal style={{ flexWrap: 'wrap' }}>
153
+ {showModel && (
154
+ <Tag capitalize type={'openai'}>
155
+ {model}
156
+ </Tag>
157
+ )}
158
+ </Flexbox>
159
+ )
160
+ }
149
161
  avatar={
150
162
  <Avatar
151
163
  animation={isHovering}
@@ -158,21 +170,7 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading })
158
170
  }
159
171
  className={styles.container}
160
172
  date={updateAt}
161
- description={
162
- <Flexbox gap={4}>
163
- <Flexbox>{description || systemRole}</Flexbox>
164
-
165
- {!(showModel || showChatLength) ? undefined : (
166
- <Flexbox horizontal>
167
- {showModel && (
168
- <Tag bordered={false} style={{ color: theme.colorTextSecondary }}>
169
- {model}
170
- </Tag>
171
- )}
172
- </Flexbox>
173
- )}
174
- </Flexbox>
175
- }
173
+ description={description || systemRole}
176
174
  loading={loading}
177
175
  pin={pin}
178
176
  ref={ref}
@@ -1,9 +1,9 @@
1
- import { Tag } from 'antd';
2
1
  import { createStyles } from 'antd-style';
3
2
  import { ReactNode } from 'react';
4
3
  import { Flexbox } from 'react-layout-kit';
5
4
  import { shallow } from 'zustand/shallow';
6
5
 
6
+ import Tag from '@/components/Tag';
7
7
  import { agentSelectors, useSessionStore } from '@/store/session';
8
8
  import { ChatMessage } from '@/types/chatMessage';
9
9
 
@@ -45,8 +45,8 @@ const MessageExtra = ({
45
45
 
46
46
  const modelTag = (
47
47
  <div>
48
- <Tag bordered={false} style={{ borderRadius: 6 }}>
49
- {extra?.fromModel}
48
+ <Tag capitalize type={'openai'}>
49
+ {extra?.fromModel as string}
50
50
  </Tag>
51
51
  </div>
52
52
  );
@@ -1,5 +1,4 @@
1
1
  import { ActionIcon, Avatar, ChatHeader } from '@lobehub/ui';
2
- import { Tag } from 'antd';
3
2
  import { PanelRightClose, PanelRightOpen, Settings, Share2 } from 'lucide-react';
4
3
  import Router from 'next/router';
5
4
  import { memo } from 'react';
@@ -7,13 +6,14 @@ import { useTranslation } from 'react-i18next';
7
6
  import { shallow } from 'zustand/shallow';
8
7
 
9
8
  import HeaderTitle from '@/components/HeaderTitle';
9
+ import Tag, { PluginTag } from '@/components/Tag';
10
10
  import { agentSelectors, useSessionStore } from '@/store/session';
11
11
  import { useSettings } from '@/store/settings';
12
12
 
13
13
  const Header = memo(() => {
14
14
  const { t } = useTranslation('common');
15
15
 
16
- const [title, description, avatar, backgroundColor, id, model] = useSessionStore(
16
+ const [title, description, avatar, backgroundColor, id, model, plugins] = useSessionStore(
17
17
  (s) => [
18
18
  agentSelectors.currentAgentTitle(s),
19
19
  agentSelectors.currentAgentDescription(s),
@@ -21,6 +21,7 @@ const Header = memo(() => {
21
21
  agentSelectors.currentAgentBackgroundColor(s),
22
22
  s.activeId,
23
23
  agentSelectors.currentAgentModel(s),
24
+ agentSelectors.currentAgentPlugins(s),
24
25
  ],
25
26
  shallow,
26
27
  );
@@ -38,13 +39,24 @@ const Header = memo(() => {
38
39
  avatar={avatar}
39
40
  background={backgroundColor}
40
41
  onClick={() => {
41
- Router.push(`/chat/${id}/edit`);
42
+ Router.push(`/chat/${id}/setting`);
42
43
  }}
43
44
  size={40}
44
- style={{ cursor: 'pointer' }}
45
+ style={{ cursor: 'pointer', flex: 'none' }}
46
+ title={title}
47
+ />
48
+ <HeaderTitle
49
+ desc={description}
50
+ tag={
51
+ <>
52
+ <Tag capitalize type={'openai'}>
53
+ {model}
54
+ </Tag>
55
+ {plugins?.length > 1 && <PluginTag plugins={plugins} />}
56
+ </>
57
+ }
45
58
  title={title}
46
59
  />
47
- <HeaderTitle desc={description} tag={<Tag>{model}</Tag>} title={title} />
48
60
  </>
49
61
  }
50
62
  right={
@@ -19,6 +19,7 @@ export const Topic = () => {
19
19
  <Empty
20
20
  cover={`/images/empty_topic_${isDarkMode ? 'dark' : 'light'}.webp`}
21
21
  desc={t('topic.desc')}
22
+ style={{ marginBottom: 6 }}
22
23
  title={t('topic.title')}
23
24
  />
24
25
  )}
@@ -1,18 +1,23 @@
1
1
  import { ChatHeader } from '@lobehub/ui';
2
- import { Tag } from 'antd';
3
2
  import Router from 'next/router';
4
3
  import { memo } from 'react';
5
4
  import { useTranslation } from 'react-i18next';
6
5
 
7
6
  import pkg from '@/../package.json';
8
7
  import HeaderTitle from '@/components/HeaderTitle';
8
+ import Tag from '@/components/Tag';
9
9
 
10
10
  const Header = memo(() => {
11
11
  const { t } = useTranslation('setting');
12
12
 
13
13
  return (
14
14
  <ChatHeader
15
- left={<HeaderTitle tag={<Tag>{`v${pkg.version}`}</Tag>} title={t('header.global')} />}
15
+ left={
16
+ <HeaderTitle
17
+ tag={<Tag type={'version'}>{`v${pkg.version}`}</Tag>}
18
+ title={t('header.global')}
19
+ />
20
+ }
16
21
  onBackClick={() => Router.back()}
17
22
  showBackButton
18
23
  />
@@ -55,6 +55,12 @@ const currentAgentModel = (s: SessionStore): LanguageModel => {
55
55
  return config?.model || LanguageModel.GPT3_5;
56
56
  };
57
57
 
58
+ const currentAgentPlugins = (s: SessionStore) => {
59
+ const config = currentAgentConfig(s);
60
+
61
+ return config?.plugins || [];
62
+ };
63
+
58
64
  const hasSystemRole = (s: SessionStore) => {
59
65
  const config = currentAgentConfig(s);
60
66
 
@@ -74,6 +80,7 @@ export const agentSelectors = {
74
80
  currentAgentDescription,
75
81
  currentAgentMeta,
76
82
  currentAgentModel,
83
+ currentAgentPlugins,
77
84
  currentAgentSystemRole,
78
85
  currentAgentTitle,
79
86
  currentAutocomplete,
@@ -19,6 +19,7 @@ export const getChatsById =
19
19
  session,
20
20
  {
21
21
  assistant: agentSelectors.currentAgentAvatar(s),
22
+ assistantBackground: agentSelectors.currentAgentBackgroundColor(s),
22
23
  user: useSettings.getState().settings.avatar || DEFAULT_USER_AVATAR,
23
24
  },
24
25
  s.activeTopicId,
@@ -4,7 +4,7 @@ import { LobeAgentSession } from '@/types/session';
4
4
 
5
5
  export const organizeChats = (
6
6
  session: LobeAgentSession,
7
- avatar: { assistant: string; user: string },
7
+ avatar: { assistant: string; assistantBackground?: string; user: string },
8
8
  topicId?: string,
9
9
  ) => {
10
10
  const getMeta = (message: ChatMessage) => {
@@ -22,6 +22,7 @@ export const organizeChats = (
22
22
  case 'assistant': {
23
23
  return {
24
24
  avatar: avatar.assistant,
25
+ backgroundColor: avatar.assistantBackground,
25
26
  title: session.meta.title,
26
27
  };
27
28
  }