@lobehub/chat 0.111.1 → 0.111.2

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,39 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.111.2](https://github.com/lobehub/lobe-chat/compare/v0.111.1...v0.111.2)
6
+
7
+ <sup>Released on **2023-12-13**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Change topic-deletion hotkey.
12
+
13
+ #### 💄 Styles
14
+
15
+ - **misc**: Fix image display in safari (fix.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### What's fixed
23
+
24
+ - **misc**: Change topic-deletion hotkey, closes [#616](https://github.com/lobehub/lobe-chat/issues/616) ([912ff45](https://github.com/lobehub/lobe-chat/commit/912ff45))
25
+
26
+ #### Styles
27
+
28
+ - **misc**: Fix image display in safari (fix, closes [#571](https://github.com/lobehub/lobe-chat/issues/571) ([4beefa7](https://github.com/lobehub/lobe-chat/commit/4beefa7))
29
+
30
+ </details>
31
+
32
+ <div align="right">
33
+
34
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
35
+
36
+ </div>
37
+
5
38
  ### [Version 0.111.1](https://github.com/lobehub/lobe-chat/compare/v0.111.0...v0.111.1)
6
39
 
7
40
  <sup>Released on **2023-12-13**</sup>
@@ -123,6 +123,7 @@
123
123
  "installed": "Installed"
124
124
  },
125
125
  "title": "Plugin Store",
126
- "uninstall": "Uninstall"
126
+ "uninstall": "Uninstall",
127
+ "placeholder": "Search for plugin name, description, or keywords..."
127
128
  }
128
129
  }
@@ -123,6 +123,7 @@
123
123
  "installed": "Installés"
124
124
  },
125
125
  "title": "Boutique de plugins",
126
- "uninstall": "Désinstaller"
126
+ "uninstall": "Désinstaller",
127
+ "placeholder": "Rechercher le nom ou les mots-clés de l'extension..."
127
128
  }
128
129
  }
@@ -123,6 +123,7 @@
123
123
  "installed": "インストール済み"
124
124
  },
125
125
  "title": "プラグインストア",
126
- "uninstall": "アンインストール"
126
+ "uninstall": "アンインストール",
127
+ "placeholder": "プラグイン名、説明、またはキーワードで検索..."
127
128
  }
128
129
  }
@@ -123,6 +123,7 @@
123
123
  "installed": "설치됨"
124
124
  },
125
125
  "title": "플러그인 상점",
126
- "uninstall": "제거"
126
+ "uninstall": "제거",
127
+ "placeholder": "플러그인 이름 또는 키워드로 검색..."
127
128
  }
128
129
  }
@@ -123,6 +123,7 @@
123
123
  "installed": "Установленные"
124
124
  },
125
125
  "title": "Магазин плагинов",
126
- "uninstall": "Удалить"
126
+ "uninstall": "Удалить",
127
+ "placeholder": "Введите название или ключевое слово плагина..."
127
128
  }
128
129
  }
@@ -117,6 +117,7 @@
117
117
  "install": "安装",
118
118
  "installAllPlugins": "安装全部",
119
119
  "networkError": "获取插件商店失败,请检测网络连接后重试",
120
+ "placeholder": "搜索插件名称介绍或关键词...",
120
121
  "releasedAt": "发布于 {{createdAt}}",
121
122
  "tabs": {
122
123
  "all": "全部",
@@ -123,6 +123,7 @@
123
123
  "installed": "已安裝"
124
124
  },
125
125
  "title": "插件商店",
126
- "uninstall": "卸載"
126
+ "uninstall": "卸載",
127
+ "placeholder": "搜尋插件名稱介紹或關鍵字..."
127
128
  }
128
129
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.111.1",
3
+ "version": "0.111.2",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -6,7 +6,7 @@ import { useHotkeys } from 'react-hotkeys-hook';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
8
  import HotKeys from '@/components/HotKeys';
9
- import { CLEAN_MESSAGE_KEY, PREFIX_KEY } from '@/const/hotkeys';
9
+ import { CLEAN_MESSAGE_KEY, META_KEY, PREFIX_KEY } from '@/const/hotkeys';
10
10
  import { useChatStore } from '@/store/chat';
11
11
  import { useFileStore } from '@/store/file';
12
12
 
@@ -14,7 +14,7 @@ const Clear = memo(() => {
14
14
  const { t } = useTranslation('setting');
15
15
  const [clearMessage] = useChatStore((s) => [s.clearMessage]);
16
16
  const [clearImageList] = useFileStore((s) => [s.clearImageList]);
17
- const hotkeys = [PREFIX_KEY, CLEAN_MESSAGE_KEY].join('+');
17
+ const hotkeys = [META_KEY, PREFIX_KEY, CLEAN_MESSAGE_KEY].join('+');
18
18
 
19
19
  const resetConversation = useCallback(() => {
20
20
  clearMessage();
@@ -1,6 +1,6 @@
1
1
  import { Icon } from '@lobehub/ui';
2
2
  import { Button, Result } from 'antd';
3
- import { BadgeCheck, CpuIcon } from 'lucide-react';
3
+ import { CheckCircle, CpuIcon } from 'lucide-react';
4
4
  import { memo, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { Center } from 'react-layout-kit';
@@ -48,7 +48,7 @@ const MigrationModal = memo<MigrationModalProps>(({ setOpen, open, state: dbStat
48
48
  {t('dbV1.action.start')}
49
49
  </Button>
50
50
  }
51
- icon={<Icon icon={BadgeCheck} />}
51
+ icon={<Icon icon={CheckCircle} />}
52
52
  status={'success'}
53
53
  style={{ paddingBlock: 24 }}
54
54
  subTitle={t('dbV1.upgrade.success.subTitle')}
@@ -62,9 +62,11 @@ const FileItem = memo<FileItemProps>(({ editable, id, alwaysShowClose }) => {
62
62
  }
63
63
  alt={data?.name || id || ''}
64
64
  alwaysShowActions={alwaysShowClose}
65
+ height={'auto'}
65
66
  isLoading={isLoading}
66
67
  size={IMAGE_SIZE as any}
67
68
  src={data?.url}
69
+ style={{ height: 'auto' }}
68
70
  wrapperClassName={cx(styles.image, editable && styles.editableImage)}
69
71
  />
70
72
  );
@@ -1,10 +1,13 @@
1
1
  'use client';
2
2
 
3
+ import { Icon } from '@lobehub/ui';
3
4
  import { createStyles } from 'antd-style';
5
+ import { isString } from 'lodash-es';
6
+ import { Command, Delete, Option } from 'lucide-react';
4
7
  import { memo, useEffect, useState } from 'react';
5
8
  import { Flexbox } from 'react-layout-kit';
6
9
 
7
- import { CLEAN_MESSAGE_KEY, PREFIX_KEY } from '@/const/hotkeys';
10
+ import { CLEAN_MESSAGE_KEY, META_KEY, PREFIX_KEY } from '@/const/hotkeys';
8
11
  import { isApplePlatform } from '@/utils/platform';
9
12
 
10
13
  const useStyles = createStyles(
@@ -13,14 +16,15 @@ const useStyles = createStyles(
13
16
 
14
17
  span {
15
18
  font-weight: 600;
16
- opacity: 0.5;
17
19
  }
18
20
 
19
21
  kbd {
20
- padding: 3px 5px;
22
+ min-width: 16px;
23
+ padding: 3px 6px;
21
24
 
22
25
  line-height: 1;
23
- color: ${token.colorTextSecondary};
26
+ color: ${token.colorTextDescription};
27
+ text-align: center;
24
28
 
25
29
  background: ${token.colorBgContainer};
26
30
  border: 1px solid ${token.colorBorderSecondary};
@@ -42,9 +46,10 @@ const HotKeys = memo<HotKeysProps>(({ keys, desc }) => {
42
46
  const visibility = typeof window === 'undefined' ? 'hidden' : 'visible';
43
47
 
44
48
  useEffect(() => {
45
- const mapping: Record<string, string> = {
46
- [CLEAN_MESSAGE_KEY]: isApplePlatform() ? '⌫' : 'backspace',
47
- [PREFIX_KEY]: isApplePlatform() ? '⌥' : 'alt',
49
+ const mapping: Record<string, any> = {
50
+ [CLEAN_MESSAGE_KEY]: isApplePlatform() ? <Icon icon={Delete} /> : 'backspace',
51
+ [META_KEY]: isApplePlatform() ? <Icon icon={Command} /> : 'ctrl',
52
+ [PREFIX_KEY]: isApplePlatform() ? <Icon icon={Option} /> : 'alt',
48
53
  };
49
54
  const newValue = keys
50
55
  .split('+')
@@ -57,7 +62,7 @@ const HotKeys = memo<HotKeysProps>(({ keys, desc }) => {
57
62
  <Flexbox align={'center'} className={styles} gap={2} horizontal>
58
63
  {keysGroup.map((key, index) => (
59
64
  <kbd key={index}>
60
- <span style={{ visibility }}>{key.toUpperCase()}</span>
65
+ <span style={{ visibility }}>{isString(key) ? key.toUpperCase() : key}</span>
61
66
  </kbd>
62
67
  ))}
63
68
  </Flexbox>
@@ -1,4 +1,5 @@
1
1
  export const PREFIX_KEY = 'alt';
2
+ export const META_KEY = 'mod';
2
3
  export const SAVE_TOPIC_KEY = 'n';
3
4
  export const CLEAN_MESSAGE_KEY = 'backspace';
4
5
  export const REGENERATE_KEY = 'r';
@@ -1,7 +1,7 @@
1
1
  import { Icon } from '@lobehub/ui';
2
2
  import { Button, Result, Table, Upload } from 'antd';
3
3
  import { createStyles } from 'antd-style';
4
- import { BadgeCheck, ImportIcon } from 'lucide-react';
4
+ import { CheckCircle, ImportIcon } from 'lucide-react';
5
5
  import React, { ReactNode, memo, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Center, Flexbox } from 'react-layout-kit';
@@ -169,7 +169,7 @@ const DataImporter = memo<DataImporterProps>(({ children, onFinishImport }) => {
169
169
  {t('importModal.finish.start')}
170
170
  </Button>
171
171
  }
172
- icon={<Icon icon={BadgeCheck} />}
172
+ icon={<Icon icon={CheckCircle} />}
173
173
  status={'success'}
174
174
  style={{ paddingBlock: 24 }}
175
175
  subTitle={
@@ -25,6 +25,8 @@ const PluginSettingsModal = memo<PluginSettingsModalProps>(({ schema, onClose, i
25
25
  const theme = useTheme();
26
26
  return (
27
27
  <Modal
28
+ cancelText={t('cancel', { ns: 'common' })}
29
+ okText={t('ok', { ns: 'common' })}
28
30
  onCancel={onClose}
29
31
  onOk={() => {
30
32
  onClose();
@@ -1,7 +1,8 @@
1
1
  import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
2
- import { Avatar, Tag } from '@lobehub/ui';
2
+ import { Avatar, Icon, Tag } from '@lobehub/ui';
3
3
  import { Button } from 'antd';
4
- import { createStyles } from 'antd-style';
4
+ import { createStyles, useResponsive } from 'antd-style';
5
+ import { BadgeCheck, CircleUser } from 'lucide-react';
5
6
  import Link from 'next/link';
6
7
  import { memo } from 'react';
7
8
  import { useTranslation } from 'react-i18next';
@@ -14,13 +15,13 @@ import PluginSettings from './PluginSettings';
14
15
 
15
16
  const useStyles = createStyles(({ css, token }) => ({
16
17
  desc: css`
17
- color: ${token.colorTextSecondary};
18
+ color: ${token.colorTextDescription};
18
19
  `,
19
20
  link: css`
20
- color: ${token.colorTextDescription};
21
+ color: ${token.colorText};
21
22
  `,
22
23
  tag: css`
23
- font-weight: normal;
24
+ color: ${token.colorTextSecondary};
24
25
  `,
25
26
  title: css`
26
27
  font-size: ${token.fontSizeLG}px;
@@ -28,52 +29,52 @@ const useStyles = createStyles(({ css, token }) => ({
28
29
  line-height: 2;
29
30
  `,
30
31
  }));
31
- const PluginItem = memo<LobeChatPluginMeta>(({ identifier, createdAt, homepage, author, meta }) => {
32
+ const PluginItem = memo<LobeChatPluginMeta>(({ identifier, homepage, author, meta }) => {
32
33
  const [installed, installing, installPlugin, unInstallPlugin] = useToolStore((s) => [
33
34
  pluginSelectors.isPluginInstalled(identifier)(s),
34
35
  pluginStoreSelectors.isPluginInstallLoading(identifier)(s),
35
36
  s.installPlugin,
36
37
  s.uninstallPlugin,
37
38
  ]);
39
+ const { mobile } = useResponsive();
38
40
  const { styles } = useStyles();
39
41
 
40
42
  const { t } = useTranslation('plugin');
41
43
  return (
42
- <Flexbox gap={8} padding={16}>
43
- <Flexbox horizontal justify={'space-between'}>
44
- <Avatar avatar={meta.avatar} size={56} style={{ flex: 'none' }} />
45
-
46
- <Flexbox gap={8} horizontal>
47
- <PluginSettings identifier={identifier} />
48
- <Button
49
- loading={installing}
50
- onClick={() => {
51
- if (installed) {
52
- unInstallPlugin(identifier);
53
- } else installPlugin(identifier);
54
- }}
55
- type={installed ? 'default' : 'primary'}
56
- >
57
- {t(installed ? 'store.uninstall' : 'store.install')}
58
- </Button>
59
- </Flexbox>
60
- </Flexbox>
61
-
62
- <Flexbox gap={4}>
63
- <Flexbox align={'center'} gap={8} horizontal>
64
- <div className={styles.title}>{meta.title}</div>
65
- <Tag className={styles.tag}> {identifier}</Tag>
66
- </Flexbox>
67
- <div className={styles.desc}>{meta.description}</div>
68
- <Flexbox className={styles.link} gap={8} horizontal>
69
- {author && (
44
+ <Flexbox align={'center'} gap={8} horizontal justify={'space-between'}>
45
+ <Flexbox align={'center'} gap={8} horizontal>
46
+ <Avatar avatar={meta.avatar} size={mobile ? 40 : 56} style={{ flex: 'none' }} />
47
+ <Flexbox gap={4}>
48
+ <Flexbox align={'center'} gap={8} horizontal>
70
49
  <Link className={styles.link} href={homepage} target={'_blank'}>
71
- @{author}
50
+ <div className={styles.title}>{meta.title}</div>
72
51
  </Link>
73
- )}
74
- {t('store.releasedAt', { createdAt })}
52
+
53
+ <Tag
54
+ className={styles.tag}
55
+ icon={<Icon icon={author === 'LobeHub' ? BadgeCheck : CircleUser} />}
56
+ >
57
+ {author}
58
+ </Tag>
59
+ </Flexbox>
60
+ <div className={styles.desc}>{meta.description} </div>
75
61
  </Flexbox>
76
62
  </Flexbox>
63
+ <Flexbox align={'center'} gap={8} horizontal>
64
+ <PluginSettings identifier={identifier} />
65
+ <Button
66
+ loading={installing}
67
+ onClick={() => {
68
+ if (installed) {
69
+ unInstallPlugin(identifier);
70
+ } else installPlugin(identifier);
71
+ }}
72
+ size={mobile ? 'small' : undefined}
73
+ type={installed ? 'default' : 'primary'}
74
+ >
75
+ {t(installed ? 'store.uninstall' : 'store.install')}
76
+ </Button>
77
+ </Flexbox>
77
78
  </Flexbox>
78
79
  );
79
80
  });
@@ -1,5 +1,5 @@
1
1
  import { ActionIcon } from '@lobehub/ui';
2
- import { SettingsIcon } from 'lucide-react';
2
+ import { Settings2 } from 'lucide-react';
3
3
  import { memo, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
@@ -19,7 +19,7 @@ const PluginSettings = memo<{ identifier: string }>(({ identifier }) => {
19
19
  return (
20
20
  <>
21
21
  <ActionIcon
22
- icon={SettingsIcon}
22
+ icon={Settings2}
23
23
  onClick={() => {
24
24
  setOpen(true);
25
25
  }}
@@ -1,8 +1,9 @@
1
- import { Icon, Modal, SpotlightCard, TabsNav } from '@lobehub/ui';
2
- import { Button, Empty } from 'antd';
1
+ import { Icon, Modal, SearchBar } from '@lobehub/ui';
2
+ import { Button, Empty, Segmented } from 'antd';
3
+ import { createStyles } from 'antd-style';
3
4
  import isEqual from 'fast-deep-equal';
4
5
  import { ServerCrash } from 'lucide-react';
5
- import { memo } from 'react';
6
+ import { memo, useState } from 'react';
6
7
  import { useTranslation } from 'react-i18next';
7
8
  import { Center, Flexbox } from 'react-layout-kit';
8
9
 
@@ -13,13 +14,24 @@ import { pluginStoreSelectors } from '@/store/tool/selectors';
13
14
  import Loading from './Loading';
14
15
  import PluginItem from './PluginItem';
15
16
 
17
+ const useStyles = createStyles(({ css, token }) => ({
18
+ header: css`
19
+ color: ${token.colorTextDescription};
20
+
21
+ > div {
22
+ flex: 1;
23
+ }
24
+ `,
25
+ }));
26
+
16
27
  interface PluginStoreProps {
17
28
  open?: boolean;
18
29
  setOpen: (open: boolean) => void;
19
30
  }
20
31
  export const PluginStore = memo<PluginStoreProps>(({ setOpen, open }) => {
32
+ const [keywords, setKeywords] = useState<string>();
21
33
  const { t } = useTranslation('plugin');
22
-
34
+ const { styles } = useStyles();
23
35
  const [listType, useFetchPluginList, installPlugins] = useToolStore((s) => [
24
36
  s.listType,
25
37
  s.useFetchPluginStore,
@@ -43,28 +55,19 @@ export const PluginStore = memo<PluginStoreProps>(({ setOpen, open }) => {
43
55
  <MobilePadding>
44
56
  <Center>
45
57
  <Flexbox gap={24} width={'100%'}>
46
- <Flexbox align={'center'} horizontal justify={'space-between'}>
47
- <TabsNav
48
- activeKey={listType}
49
- items={[
50
- { key: 'all', label: t('store.tabs.all') },
51
- { key: 'installed', label: t('store.tabs.installed') },
52
- ]}
58
+ <Flexbox align={'center'} gap={8} horizontal justify={'space-between'}>
59
+ <Segmented
60
+ block
53
61
  onChange={(v) => {
54
62
  useToolStore.setState({ listType: v as any });
55
63
  }}
64
+ options={[
65
+ { label: t('store.tabs.all'), value: 'all' },
66
+ { label: t('store.tabs.installed'), value: 'installed' },
67
+ ]}
68
+ style={{ flex: 1 }}
69
+ value={listType}
56
70
  />
57
- <Flexbox gap={8} horizontal>
58
- {listType === 'all' && (
59
- <Button
60
- onClick={() => {
61
- installPlugins(pluginStoreList.map((item) => item.identifier));
62
- }}
63
- >
64
- {t('store.installAllPlugins')}
65
- </Button>
66
- )}
67
- </Flexbox>
68
71
  </Flexbox>
69
72
  {isLoading ? (
70
73
  <Loading />
@@ -83,7 +86,37 @@ export const PluginStore = memo<PluginStoreProps>(({ setOpen, open }) => {
83
86
  )}
84
87
  </Center>
85
88
  ) : (
86
- <SpotlightCard columns={2} gap={16} items={pluginStoreList} renderItem={PluginItem} />
89
+ <>
90
+ <Flexbox className={styles.header} gap={8} horizontal>
91
+ <SearchBar
92
+ allowClear
93
+ onChange={(e) => setKeywords(e.target.value)}
94
+ placeholder={t('store.placeholder')}
95
+ type={'block'}
96
+ value={keywords}
97
+ />
98
+ {listType === 'all' && (
99
+ <Button
100
+ onClick={() => {
101
+ installPlugins(pluginStoreList.map((item) => item.identifier));
102
+ }}
103
+ >
104
+ {t('store.installAllPlugins')}
105
+ </Button>
106
+ )}
107
+ </Flexbox>
108
+
109
+ {pluginStoreList
110
+ .filter((item) =>
111
+ [item.meta.title, item.meta.description, item.author, ...(item.meta.tags || [])]
112
+ .join('')
113
+ .toLowerCase()
114
+ .includes((keywords || '')?.toLowerCase()),
115
+ )
116
+ .map((item) => (
117
+ <PluginItem key={item.identifier} {...item} />
118
+ ))}
119
+ </>
87
120
  )}
88
121
  </Flexbox>
89
122
  </Center>
@@ -15,7 +15,7 @@ const InnerLocale = memo<LocaleLayoutProps>(({ children, lang }) => {
15
15
  const { data: locale } = useSWR(
16
16
  lang,
17
17
  async () =>
18
- await import(`antd/locale/${lang?.includes('-') ? lang?.replace('-', '_') : 'en-US'}.js`),
18
+ await import(`antd/locale/${lang?.includes('-') ? lang?.replace('-', '_') : 'en_US'}.js`),
19
19
  { revalidateOnFocus: false },
20
20
  );
21
21
  const [i18n] = useState(createI18nNext(lang));
@@ -118,6 +118,7 @@ export default {
118
118
  install: '安装',
119
119
  installAllPlugins: '安装全部',
120
120
  networkError: '获取插件商店失败,请检测网络连接后重试',
121
+ placeholder: '搜索插件名称介绍或关键词...',
121
122
  releasedAt: '发布于 {{createdAt}}',
122
123
  tabs: {
123
124
  all: '全部',