@lobehub/chat 0.57.0 → 0.59.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 (45) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/package.json +1 -1
  3. package/src/components/SafeSpacing/index.tsx +27 -0
  4. package/src/const/layoutTokens.ts +2 -0
  5. package/src/features/AgentSetting/AgentPlugin/index.tsx +4 -4
  6. package/src/features/AvatarWithUpload/index.tsx +4 -3
  7. package/src/features/MobileTabBar/index.tsx +60 -0
  8. package/src/features/SideBar/TopActions.tsx +1 -1
  9. package/src/layout/AppMobileLayout.tsx +76 -0
  10. package/src/locales/default/common.ts +4 -1
  11. package/src/pages/chat/SessionList/Header.tsx +26 -2
  12. package/src/pages/chat/SessionList/index.tsx +10 -1
  13. package/src/pages/chat/[id]/index.page.tsx +19 -7
  14. package/src/pages/chat/[id]/mobile.tsx +31 -0
  15. package/src/pages/chat/[id]/setting/Header.tsx +25 -2
  16. package/src/pages/chat/[id]/setting/index.page.tsx +33 -16
  17. package/src/pages/chat/[id]/setting/mobile.tsx +11 -0
  18. package/src/pages/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +7 -5
  19. package/src/pages/chat/features/Conversation/ChatList/Plugins/CustomRender.tsx +6 -4
  20. package/src/pages/chat/features/Conversation/index.tsx +5 -5
  21. package/src/pages/chat/features/Header/Mobile.tsx +47 -0
  22. package/src/pages/chat/features/Header/PC.tsx +101 -0
  23. package/src/pages/chat/features/Header/PluginTag.tsx +40 -14
  24. package/src/pages/chat/features/Header/index.tsx +7 -93
  25. package/src/pages/chat/features/Sidebar/Header.tsx +6 -4
  26. package/src/pages/chat/features/Sidebar/Mobile.tsx +36 -0
  27. package/src/pages/chat/features/Sidebar/SystemRole.tsx +2 -2
  28. package/src/pages/chat/features/Sidebar/index.tsx +33 -15
  29. package/src/pages/chat/index.page.tsx +14 -8
  30. package/src/pages/chat/layout.tsx +12 -8
  31. package/src/pages/chat/mobile.tsx +25 -0
  32. package/src/pages/settings/index.page.tsx +40 -8
  33. package/src/pages/settings/layout.tsx +5 -27
  34. package/src/pages/settings/mobile.tsx +20 -0
  35. package/src/pages/welcome/Banner/Hero.tsx +3 -5
  36. package/src/pages/welcome/Banner/index.tsx +5 -5
  37. package/src/pages/welcome/index.page.tsx +13 -4
  38. package/src/pages/welcome/mobile.tsx +20 -0
  39. package/src/pages/welcome/style.ts +2 -2
  40. package/src/store/global/initialState.ts +2 -0
  41. package/src/store/global/slices/common.ts +7 -0
  42. package/src/store/plugin/helpers.ts +26 -0
  43. package/src/store/plugin/index.ts +1 -0
  44. package/src/store/plugin/selectors.ts +30 -4
  45. package/src/components/HeaderSpacing/index.tsx +0 -9
@@ -0,0 +1,25 @@
1
+ import { useTheme } from 'antd-style';
2
+ import { memo } from 'react';
3
+
4
+ import AppMobileLayout from '@/layout/AppMobileLayout';
5
+ import Header from '@/pages/chat/SessionList/Header';
6
+ import { useSwitchSideBarOnInit } from '@/store/global';
7
+
8
+ import { Sessions } from './SessionList';
9
+
10
+ const ChatMobileLayout = memo(() => {
11
+ useSwitchSideBarOnInit('chat');
12
+ const theme = useTheme();
13
+
14
+ return (
15
+ <AppMobileLayout
16
+ navBar={<Header mobile={'navbar'} />}
17
+ showTabBar
18
+ style={{ background: theme.colorBgContainer }}
19
+ >
20
+ <Sessions mobile />
21
+ </AppMobileLayout>
22
+ );
23
+ });
24
+
25
+ export default ChatMobileLayout;
@@ -1,21 +1,53 @@
1
+ import { useResponsive } from 'antd-style';
2
+ import Head from 'next/head';
1
3
  import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
2
5
  import { Flexbox } from 'react-layout-kit';
3
6
 
4
- import HeaderSpacing from '@/components/HeaderSpacing';
7
+ import SafeSpacing from '@/components/SafeSpacing';
8
+ import { createI18nNext } from '@/locales/create';
9
+ import { useOnFinishHydrationGlobal, useSwitchSideBarOnInit } from '@/store/global';
10
+ import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
5
11
 
6
12
  import Header from './Header';
7
13
  import Settings from './Settings';
8
14
  import SettingLayout from './layout';
15
+ import Mobile from './mobile';
16
+
17
+ const initI18n = createI18nNext({ namespace: 'setting' });
9
18
 
10
19
  const Setting = memo(() => {
20
+ const { mobile } = useResponsive();
21
+ const { t } = useTranslation('setting');
22
+ const pageTitle = genSiteHeadTitle(t('header.global'));
23
+
24
+ useSwitchSideBarOnInit('settings');
25
+
26
+ useOnFinishHydrationGlobal(() => {
27
+ initI18n.then(() => {});
28
+ });
29
+
11
30
  return (
12
- <SettingLayout>
13
- <Header />
14
- <Flexbox align={'center'} flex={1} padding={24} style={{ overflow: 'auto' }}>
15
- <HeaderSpacing />
16
- <Settings />
17
- </Flexbox>
18
- </SettingLayout>
31
+ <>
32
+ <Head>
33
+ <title>{pageTitle}</title>
34
+ </Head>
35
+ {mobile ? (
36
+ <Mobile>
37
+ <Flexbox align={'center'} padding={16} style={{ overflow: 'auto' }}>
38
+ <Settings />
39
+ </Flexbox>
40
+ </Mobile>
41
+ ) : (
42
+ <SettingLayout>
43
+ <Header />
44
+ <Flexbox align={'center'} flex={1} padding={24} style={{ overflow: 'auto' }}>
45
+ <SafeSpacing />
46
+ <Settings />
47
+ </Flexbox>
48
+ </SettingLayout>
49
+ )}
50
+ </>
19
51
  );
20
52
  });
21
53
 
@@ -1,37 +1,15 @@
1
- import Head from 'next/head';
2
1
  import { ReactNode, memo } from 'react';
3
- import { useTranslation } from 'react-i18next';
4
2
  import { Flexbox } from 'react-layout-kit';
5
3
 
6
- import SideBar from '@/features/SideBar';
7
- import { createI18nNext } from '@/locales/create';
8
- import { useOnFinishHydrationGlobal, useSwitchSideBarOnInit } from '@/store/global';
9
- import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
10
-
11
- const initI18n = createI18nNext({ namespace: 'setting' });
4
+ import AppLayout from '@/layout/AppLayout';
12
5
 
13
6
  const SettingLayout = memo<{ children: ReactNode }>(({ children }) => {
14
- const { t } = useTranslation('setting');
15
- const pageTitle = genSiteHeadTitle(t('header.global'));
16
-
17
- useSwitchSideBarOnInit('settings');
18
-
19
- useOnFinishHydrationGlobal(() => {
20
- initI18n.then(() => {});
21
- });
22
-
23
7
  return (
24
- <>
25
- <Head>
26
- <title>{pageTitle}</title>
27
- </Head>
28
- <Flexbox horizontal width={'100%'}>
29
- <SideBar />
30
- <Flexbox flex={1} height={'100vh'} style={{ position: 'relative' }}>
31
- {children}
32
- </Flexbox>
8
+ <AppLayout>
9
+ <Flexbox flex={1} height={'100vh'} style={{ position: 'relative' }}>
10
+ {children}
33
11
  </Flexbox>
34
- </>
12
+ </AppLayout>
35
13
  );
36
14
  });
37
15
 
@@ -0,0 +1,20 @@
1
+ import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
2
+ import { ReactNode, memo } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+
5
+ import AppMobileLayout from '@/layout/AppMobileLayout';
6
+
7
+ const SettingLayout = memo<{ children: ReactNode }>(({ children }) => {
8
+ const { t } = useTranslation('setting');
9
+
10
+ return (
11
+ <AppMobileLayout
12
+ navBar={<MobileNavBar center={<MobileNavBarTitle title={t('header.global')} />} />}
13
+ showTabBar
14
+ >
15
+ {children}
16
+ </AppMobileLayout>
17
+ );
18
+ });
19
+
20
+ export default SettingLayout;
@@ -21,12 +21,10 @@ const Hero = memo<{ width: number }>(({ width }) => {
21
21
 
22
22
  return (
23
23
  <>
24
- <LogoThree
25
- size={size.logo}
26
- style={{ marginBottom: -size.logo / 40, marginTop: -size.logo / 5 }}
27
- />
24
+ <LogoThree size={size.logo} style={{ marginTop: -size.logo / 5 }} />
28
25
  <div className={styles.title} style={{ fontSize: size.title }}>
29
- LobeChat{mobile ? <br /> : ' '}
26
+ <span style={mobile ? { fontSize: '1.2em' } : {}}>LobeChat</span>
27
+ {mobile ? <br /> : ' '}
30
28
  {t('slogan.title')}
31
29
  </div>
32
30
  <div className={styles.desc} style={{ fontSize: size.desc }}>
@@ -13,7 +13,7 @@ import { useImportConfig } from '@/hooks/useImportConfig';
13
13
  import { useStyles } from '../style';
14
14
  import Hero from './Hero';
15
15
 
16
- const Banner = memo(() => {
16
+ const Banner = memo<{ mobile?: boolean }>(() => {
17
17
  const { importConfig } = useImportConfig();
18
18
  const ref = useRef(null);
19
19
  const domSize = useSize(ref);
@@ -28,12 +28,15 @@ const Banner = memo(() => {
28
28
  }, []);
29
29
 
30
30
  return (
31
- <Flexbox height={'80vh'} style={{ marginTop: '10vh' }}>
31
+ <Flexbox height={'80vh'} style={{ paddingTop: '10vh' }}>
32
32
  <GridShowcase>
33
33
  <div className={styles.container} ref={ref}>
34
34
  <Hero width={width} />
35
35
  </div>
36
36
  <Flexbox gap={16} horizontal style={{ marginTop: 16 }}>
37
+ <Upload maxCount={1} onChange={handleImport} showUploadList={false}>
38
+ <Button size={'large'}>{t('button.import')}</Button>
39
+ </Upload>
37
40
  <Link href={'/chat'}>
38
41
  <Button size={'large'} type={'primary'}>
39
42
  <Flexbox align={'center'} gap={4} horizontal>
@@ -42,9 +45,6 @@ const Banner = memo(() => {
42
45
  </Flexbox>
43
46
  </Button>
44
47
  </Link>
45
- <Upload maxCount={1} onChange={handleImport} showUploadList={false}>
46
- <Button size={'large'}>{t('button.import')}</Button>
47
- </Upload>
48
48
  </Flexbox>
49
49
  </GridShowcase>
50
50
  {/*TODO:暂时隐藏,待模板完成后再补回*/}
@@ -1,3 +1,4 @@
1
+ import { useResponsive } from 'antd-style';
1
2
  import Head from 'next/head';
2
3
  import { memo } from 'react';
3
4
  import { useTranslation } from 'react-i18next';
@@ -7,8 +8,10 @@ import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
7
8
  import Banner from './Banner';
8
9
  import Footer from './Footer';
9
10
  import Layout from './layout';
11
+ import Mobile from './mobile';
10
12
 
11
13
  const Welcome = memo(() => {
14
+ const { mobile } = useResponsive();
12
15
  const { t } = useTranslation('welcome');
13
16
  const pageTitle = genSiteHeadTitle(t('header'));
14
17
 
@@ -17,10 +20,16 @@ const Welcome = memo(() => {
17
20
  <Head>
18
21
  <title>{pageTitle}</title>
19
22
  </Head>
20
- <Layout>
21
- <Banner />
22
- <Footer />
23
- </Layout>
23
+ {mobile ? (
24
+ <Mobile>
25
+ <Banner />
26
+ </Mobile>
27
+ ) : (
28
+ <Layout>
29
+ <Banner />
30
+ <Footer />
31
+ </Layout>
32
+ )}
24
33
  </>
25
34
  );
26
35
  });
@@ -0,0 +1,20 @@
1
+ import { Logo, MobileNavBar } from '@lobehub/ui';
2
+ import { useTheme } from 'antd-style';
3
+ import { PropsWithChildren, memo } from 'react';
4
+
5
+ import AppMobileLayout from '../../layout/AppMobileLayout';
6
+
7
+ const WelcomeLayout = memo<PropsWithChildren>(({ children }) => {
8
+ const theme = useTheme();
9
+ return (
10
+ <AppMobileLayout
11
+ navBar={<MobileNavBar center={<Logo type={'text'} />} />}
12
+ showTabBar
13
+ style={{ background: theme.colorBgContainer }}
14
+ >
15
+ {children}
16
+ </AppMobileLayout>
17
+ );
18
+ });
19
+
20
+ export default WelcomeLayout;
@@ -41,9 +41,9 @@ export const useStyles = createStyles(({ css, token, stylish, cx }) => {
41
41
  padding: 16px;
42
42
  `,
43
43
  title: css`
44
- margin-bottom: 0.2em;
44
+ margin-bottom: 0.25em;
45
45
  font-weight: 800;
46
- line-height: 1.2;
46
+ line-height: 1.4;
47
47
  text-align: center;
48
48
  `,
49
49
  view: cx(
@@ -25,6 +25,7 @@ export interface GlobalState {
25
25
  export interface GlobalPreference {
26
26
  guide?: Guide;
27
27
  inputHeight: number;
28
+ mobileShowTopic?: boolean;
28
29
  sessionsWidth: number;
29
30
  showChatSideBar?: boolean;
30
31
  showSessionPanel?: boolean;
@@ -34,6 +35,7 @@ export const initialState: GlobalState = {
34
35
  preference: {
35
36
  guide: {},
36
37
  inputHeight: 200,
38
+ mobileShowTopic: false,
37
39
  sessionsWidth: 320,
38
40
  showChatSideBar: true,
39
41
  showSessionPanel: true,
@@ -19,6 +19,7 @@ export interface CommonAction {
19
19
  */
20
20
  switchSideBar: (key: SidebarTabKey) => void;
21
21
  toggleChatSideBar: (visible?: boolean) => void;
22
+ toggleMobileTopic: (visible?: boolean) => void;
22
23
  updateGuideState: (guide: Partial<Guide>) => void;
23
24
  updatePreference: (preference: Partial<GlobalPreference>, action?: string) => void;
24
25
  }
@@ -38,6 +39,12 @@ export const createCommonSlice: StateCreator<
38
39
 
39
40
  get().updatePreference({ showChatSideBar }, t('toggleAgentPanel', newValue) as string);
40
41
  },
42
+ toggleMobileTopic: (newValue) => {
43
+ const mobileShowTopic =
44
+ typeof newValue === 'boolean' ? newValue : !get().preference.mobileShowTopic;
45
+
46
+ get().updatePreference({ mobileShowTopic }, t('toggleMobileTopic', newValue) as string);
47
+ },
41
48
  updateGuideState: (guide) => {
42
49
  const { updatePreference } = get();
43
50
  const nextGuide = merge({}, get().preference.guide, guide);
@@ -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
  };
@@ -1,9 +0,0 @@
1
- import { memo } from 'react';
2
-
3
- import { HEADER_HEIGHT } from '@/const/layoutTokens';
4
-
5
- const HeaderSpacing = memo<{ height?: number }>(({ height = HEADER_HEIGHT }) => (
6
- <div style={{ flex: 'none', height }} />
7
- ));
8
-
9
- export default HeaderSpacing;