@lobehub/chat 0.58.0 → 0.60.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,65 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 0.60.0](https://github.com/lobehub/lobe-chat/compare/v0.59.0...v0.60.0)
6
+
7
+ <sup>Released on **2023-08-26**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Refactor with new market index url.
12
+
13
+ #### ✨ Features
14
+
15
+ - **misc**: 支持插件 manifest 加载失败后重试.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### Code refactoring
23
+
24
+ - **misc**: Refactor with new market index url ([d2834b7](https://github.com/lobehub/lobe-chat/commit/d2834b7))
25
+
26
+ #### What's improved
27
+
28
+ - **misc**: 支持插件 manifest 加载失败后重试 ([f36378e](https://github.com/lobehub/lobe-chat/commit/f36378e))
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
+
38
+ ## [Version 0.59.0](https://github.com/lobehub/lobe-chat/compare/v0.58.0...v0.59.0)
39
+
40
+ <sup>Released on **2023-08-26**</sup>
41
+
42
+ #### ✨ Features
43
+
44
+ - **misc**: 支持展示插件插件状态, 支持插件 i18n 模式展示.
45
+
46
+ <br/>
47
+
48
+ <details>
49
+ <summary><kbd>Improvements and Fixes</kbd></summary>
50
+
51
+ #### What's improved
52
+
53
+ - **misc**: 支持展示插件插件状态 ([7e916ac](https://github.com/lobehub/lobe-chat/commit/7e916ac))
54
+ - **misc**: 支持插件 i18n 模式展示 ([8614734](https://github.com/lobehub/lobe-chat/commit/8614734))
55
+
56
+ </details>
57
+
58
+ <div align="right">
59
+
60
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
61
+
62
+ </div>
63
+
5
64
  ## [Version 0.58.0](https://github.com/lobehub/lobe-chat/compare/v0.57.0...v0.58.0)
6
65
 
7
66
  <sup>Released on **2023-08-26**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.58.0",
3
+ "version": "0.60.0",
4
4
  "description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
5
5
  "keywords": [
6
6
  "chatbot",
package/src/const/url.ts CHANGED
@@ -7,5 +7,4 @@ export const FEEDBACK = pkg.bugs.url;
7
7
  export const DISCORD = 'https://discord.gg/AYFPHvv2jT';
8
8
 
9
9
  export const PLUGINS_INDEX_URL =
10
- process.env.PLUGINS_INDEX_URL ??
11
- 'https://registry.npmmirror.com/@lobehub/lobe-chat-plugins/latest/files';
10
+ process.env.PLUGINS_INDEX_URL ?? 'https://chat-plugins-index.lobehub.com';
@@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
9
9
  import { FORM_STYLE } from '@/const/layoutTokens';
10
10
  import { transformPluginSettings } from '@/features/PluginSettings';
11
11
  import PluginSettingRender from '@/features/PluginSettings/PluginSettingRender';
12
- import { usePluginStore } from '@/store/plugin';
12
+ import { pluginHelpers, usePluginStore } from '@/store/plugin';
13
13
  import { useSessionStore } from '@/store/session';
14
14
  import { AgentAction, agentSelectors } from '@/store/session/slices/agentConfig';
15
15
  import { LobeAgentConfig } from '@/types/session';
@@ -98,8 +98,8 @@ const AgentPlugin = memo<AgentPluginProps>(({ config, updateConfig }) => {
98
98
  }}
99
99
  />
100
100
  ),
101
- desc: meta?.description,
102
- label: meta.title,
101
+ desc: pluginHelpers.getPluginDesc(meta),
102
+ label: pluginHelpers.getPluginTitle(meta),
103
103
  minWidth: undefined,
104
104
  tag: identifier,
105
105
  }));
@@ -131,7 +131,7 @@ const AgentPlugin = memo<AgentPluginProps>(({ config, updateConfig }) => {
131
131
  desc: item.desc && <Markdown className={styles.md}>{item.desc}</Markdown>,
132
132
  })),
133
133
  icon: ToyBrick,
134
- title: t('settingPlugin.config', { id: item.meta.title || item.identifier }),
134
+ title: t('settingPlugin.config', { id: pluginHelpers.getPluginTitle(item.meta) }),
135
135
  };
136
136
  })
137
137
  .filter(Boolean) as unknown as ItemGroup[]);
@@ -53,6 +53,7 @@ export default {
53
53
  regenerate: '重新生成',
54
54
  rename: '重命名',
55
55
  reset: '重置',
56
+ retry: '重试',
56
57
  roleAndArchive: '角色与记录',
57
58
  searchAgentPlaceholder: '搜索助手和对话...',
58
59
  send: '发送',
@@ -1,12 +1,13 @@
1
1
  import { Avatar } from '@lobehub/ui';
2
2
  import { Button, Divider } from 'antd';
3
3
  import { useTheme } from 'antd-style';
4
+ import isEqual from 'fast-deep-equal';
4
5
  import { memo } from 'react';
5
6
  import { useTranslation } from 'react-i18next';
6
7
  import { Center, Flexbox } from 'react-layout-kit';
7
8
 
8
9
  import PluginSettingsConfig from '@/features/PluginSettings';
9
- import { pluginSelectors, usePluginStore } from '@/store/plugin';
10
+ import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
10
11
  import { useSessionStore } from '@/store/session';
11
12
 
12
13
  import { ErrorActionContainer, useStyles } from '../style';
@@ -22,20 +23,21 @@ const PluginSettings = memo<PluginSettingsProps>(({ id, pluginIdentifier }) => {
22
23
  const { t } = useTranslation('error');
23
24
  const theme = useTheme();
24
25
  const [resend, deleteMessage] = useSessionStore((s) => [s.resendMessage, s.deleteMessage]);
25
- const plugin = usePluginStore(pluginSelectors.getPluginMetaById(pluginIdentifier));
26
- const manifest = usePluginStore(pluginSelectors.getPluginManifestSettingsById(pluginIdentifier));
26
+
27
+ const plugin = usePluginStore(pluginSelectors.getPluginMetaById(pluginIdentifier), isEqual);
28
+ const manifest = usePluginStore(pluginSelectors.getPluginManifestById(pluginIdentifier), isEqual);
27
29
 
28
30
  return (
29
31
  <ErrorActionContainer>
30
32
  <Center gap={16} style={{ maxWidth: 400 }}>
31
33
  <Avatar
32
- avatar={plugin?.meta.avatar || '⚙️'}
34
+ avatar={pluginHelpers.getPluginAvatar(plugin?.meta) || '⚙️'}
33
35
  background={theme.colorFillContent}
34
36
  gap={12}
35
37
  size={80}
36
38
  />
37
39
  <Flexbox style={{ fontSize: 20 }}>
38
- {t('pluginSettings.title', { name: plugin?.meta.title })}
40
+ {t('pluginSettings.title', { name: pluginHelpers.getPluginTitle(plugin?.meta) })}
39
41
  </Flexbox>
40
42
  <Flexbox className={styles.desc}>{t('pluginSettings.desc')}</Flexbox>
41
43
  <Divider style={{ margin: '0 16px' }} />
@@ -1,6 +1,6 @@
1
1
  import { PluginRender, PluginRenderProps } from '@lobehub/chat-plugin-sdk';
2
2
  import { Skeleton } from 'antd';
3
- import { memo, useState } from 'react';
3
+ import { memo, useEffect, useState } from 'react';
4
4
 
5
5
  import { system } from './dynamticLoader';
6
6
 
@@ -10,7 +10,7 @@ interface CustomRenderProps extends PluginRenderProps {
10
10
  const CustomRender = memo<CustomRenderProps>(({ url, ...props }) => {
11
11
  const [component, setComp] = useState<PluginRender | null>(null);
12
12
 
13
- if (!component) {
13
+ useEffect(() => {
14
14
  system
15
15
  .import(url)
16
16
  .then((module1) => {
@@ -21,12 +21,14 @@ const CustomRender = memo<CustomRenderProps>(({ url, ...props }) => {
21
21
  setComp(null);
22
22
  console.error(error);
23
23
  });
24
+ }, [url]);
24
25
 
26
+ if (!component) {
25
27
  return <Skeleton active style={{ width: 300 }} />;
26
28
  }
27
29
 
28
- const Comp = component;
30
+ const Render = component;
29
31
 
30
- return <Comp {...props} />;
32
+ return <Render {...props} />;
31
33
  });
32
34
  export default CustomRender;
@@ -1,33 +1,71 @@
1
- import { Avatar, Icon, Tag } from '@lobehub/ui';
1
+ import { ActionIcon, Avatar, Icon, Tag } from '@lobehub/ui';
2
2
  import type { MenuProps } from 'antd';
3
- import { Dropdown } from 'antd';
4
- import { LucideToyBrick } from 'lucide-react';
5
- import { memo } from 'react';
3
+ import { Badge, Dropdown } from 'antd';
4
+ import isEqual from 'fast-deep-equal';
5
+ import { LucideRotateCw, LucideToyBrick } from 'lucide-react';
6
+ import { memo, useMemo } from 'react';
6
7
  import { useTranslation } from 'react-i18next';
8
+ import { Flexbox } from 'react-layout-kit';
7
9
 
8
- import { usePluginStore } from '@/store/plugin';
10
+ import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
11
+
12
+ const PluginStatus = memo<{ id: string; title?: string }>(({ title, id }) => {
13
+ const { t } = useTranslation('common');
14
+ const [status, fetchPluginManifest] = usePluginStore((s) => [
15
+ pluginSelectors.getPluginManifestLoadingStatus(id)(s),
16
+ s.fetchPluginManifest,
17
+ ]);
18
+
19
+ const renderStatus = useMemo(() => {
20
+ switch (status) {
21
+ case 'loading': {
22
+ return <Badge color={'blue'} status={'processing'} />;
23
+ }
24
+ case 'error': {
25
+ return (
26
+ <ActionIcon
27
+ icon={LucideRotateCw}
28
+ onClick={() => {
29
+ fetchPluginManifest(id);
30
+ }}
31
+ size={'small'}
32
+ title={t('retry')}
33
+ />
34
+ );
35
+ }
36
+ case 'success': {
37
+ return <Badge status={status} />;
38
+ }
39
+ }
40
+ }, [status]);
41
+
42
+ return (
43
+ <Flexbox gap={12} horizontal justify={'space-between'}>
44
+ {title} {renderStatus}
45
+ </Flexbox>
46
+ );
47
+ });
9
48
 
10
49
  export interface PluginTagProps {
11
50
  plugins: string[];
12
51
  }
13
52
 
14
53
  const PluginTag = memo<PluginTagProps>(({ plugins }) => {
15
- const { t } = useTranslation('plugin');
16
-
17
- const list = usePluginStore((s) => s.pluginList);
54
+ const list = usePluginStore(pluginSelectors.displayPluginList);
55
+ const displayPlugin = usePluginStore(pluginSelectors.getPluginMetaById(plugins[0]), isEqual);
18
56
 
19
57
  if (plugins.length === 0) return null;
20
58
 
21
59
  const items: MenuProps['items'] = plugins.map((id) => {
22
- const item = list?.find((p) => p.identifier === id);
60
+ const item = list.find((i) => i.identifier === id);
61
+
23
62
  return {
24
- icon: (
25
- <Avatar avatar={item?.meta.avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />
26
- ),
63
+ icon: <Avatar avatar={item?.avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />,
27
64
  key: id,
28
- label: t(`plugins.${id}` as any),
65
+ label: <PluginStatus id={id} title={item?.title} />,
29
66
  };
30
67
  });
68
+
31
69
  const count = plugins.length;
32
70
 
33
71
  return (
@@ -35,7 +73,7 @@ const PluginTag = memo<PluginTagProps>(({ plugins }) => {
35
73
  <div>
36
74
  <Tag>
37
75
  {<Icon icon={LucideToyBrick} />}
38
- {t(`plugins.${plugins[0]}` as any)}
76
+ {pluginHelpers.getPluginTitle(displayPlugin?.meta)}
39
77
  {count > 1 && <div>({plugins.length - 1}+)</div>}
40
78
  </Tag>
41
79
  </div>
@@ -0,0 +1,26 @@
1
+ import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
2
+ import i18n from 'i18next';
3
+
4
+ const getI18nValue = (value: string | Record<string, string> | undefined) => {
5
+ if (!value) return;
6
+
7
+ if (typeof value === 'string') return value;
8
+
9
+ if (value[i18n.language]) return value[i18n.language];
10
+
11
+ return Object.values(value)[0];
12
+ };
13
+
14
+ const getPluginFormList = (pluginList: LobeChatPluginMeta[], id: string) =>
15
+ pluginList?.find((p) => p.identifier === id);
16
+
17
+ const getPluginTitle = (meta?: LobeChatPluginMeta['meta']) => getI18nValue(meta?.title);
18
+ const getPluginDesc = (meta?: LobeChatPluginMeta['meta']) => getI18nValue(meta?.description);
19
+ const getPluginAvatar = (meta?: LobeChatPluginMeta['meta']) => meta?.avatar;
20
+
21
+ export const pluginHelpers = {
22
+ getPluginAvatar,
23
+ getPluginDesc,
24
+ getPluginFormList,
25
+ getPluginTitle,
26
+ };
@@ -1,2 +1,3 @@
1
+ export * from './helpers';
1
2
  export * from './selectors';
2
3
  export { usePluginStore } from './store';
@@ -1,4 +1,5 @@
1
1
  import { PLUGIN_SCHEMA_SEPARATOR } from '@/const/plugin';
2
+ import { pluginHelpers } from '@/store/plugin/helpers';
2
3
 
3
4
  import { PluginStoreState } from './initialState';
4
5
 
@@ -23,13 +24,38 @@ const enabledSchema =
23
24
  };
24
25
 
25
26
  const getPluginMetaById = (id: string) => (s: PluginStoreState) =>
26
- s.pluginList?.find((p) => p.identifier === id);
27
+ pluginHelpers.getPluginFormList(s.pluginList, id);
27
28
 
28
- const getPluginManifestSettingsById = (id: string) => (s: PluginStoreState) =>
29
- s.pluginManifestMap[id];
29
+ const getPluginManifestById = (id: string) => (s: PluginStoreState) => s.pluginManifestMap[id];
30
+ const getPluginSettingsById = (id: string) => (s: PluginStoreState) => s.pluginsSettings[id];
31
+
32
+ // 获取插件 manifest 加载状态
33
+ const getPluginManifestLoadingStatus = (id: string) => (s: PluginStoreState) => {
34
+ const manifest = getPluginManifestById(id);
35
+
36
+ if (s.pluginManifestLoading[id]) return 'loading';
37
+
38
+ if (!manifest) return 'error';
39
+
40
+ if (!!manifest) return 'success';
41
+ };
42
+
43
+ const displayPluginList = (s: PluginStoreState) =>
44
+ s.pluginList.map((p) => ({
45
+ author: p.author,
46
+ avatar: p.meta?.avatar,
47
+ createAt: p.createAt,
48
+ desc: pluginHelpers.getPluginDesc(p.meta),
49
+ homepage: p.homepage,
50
+ identifier: p.identifier,
51
+ title: pluginHelpers.getPluginTitle(p.meta),
52
+ }));
30
53
 
31
54
  export const pluginSelectors = {
55
+ displayPluginList,
32
56
  enabledSchema,
33
- getPluginManifestSettingsById,
57
+ getPluginManifestById,
58
+ getPluginManifestLoadingStatus,
34
59
  getPluginMetaById,
60
+ getPluginSettingsById,
35
61
  };