@lobehub/chat 0.159.4 → 0.159.6

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 (24) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/package.json +1 -1
  3. package/src/app/(main)/chat/(workspace)/@topic/_layout/Desktop.tsx +17 -0
  4. package/src/app/(main)/chat/(workspace)/@topic/_layout/Mobile.tsx +21 -0
  5. package/src/app/(main)/chat/(workspace)/@topic/default.tsx +7 -1
  6. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{Topic/SkeletonList.tsx → SkeletonList.tsx} +1 -1
  7. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{Topic/TopicItem.tsx → TopicItem.tsx} +5 -0
  8. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/index.tsx +95 -13
  9. package/src/app/(main)/chat/(workspace)/_layout/Desktop/TopicPanel.tsx +12 -15
  10. package/src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx +9 -1
  11. package/src/app/(main)/chat/_layout/Desktop/SessionPanel.tsx +16 -25
  12. package/src/components/SidebarHeader/index.tsx +1 -2
  13. package/src/components/server/MobileNavLayout.tsx +1 -0
  14. package/src/features/Conversation/components/InboxWelcome/AgentsSuggest.tsx +18 -22
  15. package/src/features/User/UserPanel/PanelContent.tsx +5 -2
  16. package/src/features/User/__tests__/PanelContent.test.tsx +3 -6
  17. package/src/layout/GlobalProvider/AppTheme.tsx +13 -0
  18. package/src/store/user/slices/preference/initialState.ts +1 -0
  19. package/src/styles/global.ts +23 -0
  20. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/index.tsx +0 -101
  21. /package/src/app/(main)/chat/(workspace)/@topic/features/{TopicListContent/Header.tsx → Header.tsx} +0 -0
  22. /package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{Topic/DefaultContent.tsx → DefaultContent.tsx} +0 -0
  23. /package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{Topic/TopicContent.tsx → TopicContent.tsx} +0 -0
  24. /package/src/app/(main)/chat/(workspace)/@topic/features/{TopicListContent/TopicSearchBar → TopicSearchBar}/index.tsx +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.159.6](https://github.com/lobehub/lobe-chat/compare/v0.159.5...v0.159.6)
6
+
7
+ <sup>Released on **2024-05-14**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Login button not show on user panel.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Login button not show on user panel, closes [#2496](https://github.com/lobehub/lobe-chat/issues/2496) ([39637fb](https://github.com/lobehub/lobe-chat/commit/39637fb))
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.159.5](https://github.com/lobehub/lobe-chat/compare/v0.159.4...v0.159.5)
31
+
32
+ <sup>Released on **2024-05-14**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Fix scroll and expand.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Fix scroll and expand, closes [#2470](https://github.com/lobehub/lobe-chat/issues/2470) ([8b1202a](https://github.com/lobehub/lobe-chat/commit/8b1202a))
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.159.4](https://github.com/lobehub/lobe-chat/compare/v0.159.3...v0.159.4)
6
56
 
7
57
  <sup>Released on **2024-05-14**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.159.4",
3
+ "version": "0.159.6",
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",
@@ -0,0 +1,17 @@
1
+ import { PropsWithChildren } from 'react';
2
+ import { Flexbox } from 'react-layout-kit';
3
+
4
+ import Header from '../features/Header';
5
+
6
+ const Layout = ({ children }: PropsWithChildren) => {
7
+ return (
8
+ <>
9
+ <Header />
10
+ <Flexbox height={'100%'} style={{ overflow: 'hidden', position: 'relative' }} width={'100%'}>
11
+ {children}
12
+ </Flexbox>
13
+ </>
14
+ );
15
+ };
16
+
17
+ export default Layout;
@@ -0,0 +1,21 @@
1
+ import { PropsWithChildren } from 'react';
2
+ import { Flexbox } from 'react-layout-kit';
3
+
4
+ import TopicSearchBar from '../features/TopicSearchBar';
5
+
6
+ const Layout = ({ children }: PropsWithChildren) => {
7
+ return (
8
+ <Flexbox gap={8} height={'100%'} padding={'8px 8px 0'} style={{ overflow: 'hidden' }}>
9
+ <TopicSearchBar />
10
+ <Flexbox
11
+ height={'100%'}
12
+ style={{ marginInline: -8, overflow: 'hidden', position: 'relative' }}
13
+ width={'calc(100% + 16px)'}
14
+ >
15
+ {children}
16
+ </Flexbox>
17
+ </Flexbox>
18
+ );
19
+ };
20
+
21
+ export default Layout;
@@ -1,15 +1,21 @@
1
1
  import { isMobileDevice } from '@/utils/responsive';
2
2
 
3
+ import Desktop from './_layout/Desktop';
4
+ import Mobile from './_layout/Mobile';
3
5
  import SystemRole from './features/SystemRole';
4
6
  import TopicListContent from './features/TopicListContent';
5
7
 
6
8
  const Topic = () => {
7
9
  const mobile = isMobileDevice();
8
10
 
11
+ const Layout = mobile ? Mobile : Desktop;
12
+
9
13
  return (
10
14
  <>
11
15
  {!mobile && <SystemRole />}
12
- <TopicListContent mobile={mobile} />
16
+ <Layout>
17
+ <TopicListContent />
18
+ </Layout>
13
19
  </>
14
20
  );
15
21
  };
@@ -45,7 +45,7 @@ export const Placeholder = memo(() => {
45
45
  });
46
46
 
47
47
  export const SkeletonList = memo(() => (
48
- <Flexbox>
48
+ <Flexbox style={{ paddingTop: 6 }}>
49
49
  {Array.from({ length: 8 }).map((_, i) => (
50
50
  <Placeholder key={i} />
51
51
  ))}
@@ -19,7 +19,12 @@ const useStyles = createStyles(({ css, token, isDarkMode }) => ({
19
19
  `,
20
20
  container: css`
21
21
  cursor: pointer;
22
+
23
+ width: calc(100% - 16px);
24
+ margin-block: 2px;
25
+ margin-inline: 8px;
22
26
  padding: 8px;
27
+
23
28
  border-radius: ${token.borderRadius}px;
24
29
 
25
30
  &:hover {
@@ -1,18 +1,100 @@
1
+ 'use client';
2
+
3
+ import { EmptyCard } from '@lobehub/ui';
4
+ import { useThemeMode } from 'antd-style';
5
+ import isEqual from 'fast-deep-equal';
6
+ import React, { memo, useCallback, useRef } from 'react';
7
+ import { useTranslation } from 'react-i18next';
1
8
  import { Flexbox } from 'react-layout-kit';
9
+ import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
10
+
11
+ import { imageUrl } from '@/const/url';
12
+ import { useChatStore } from '@/store/chat';
13
+ import { topicSelectors } from '@/store/chat/selectors';
14
+ import { useUserStore } from '@/store/user';
15
+ import { ChatTopic } from '@/types/topic';
16
+
17
+ import { Placeholder, SkeletonList } from './SkeletonList';
18
+ import TopicItem from './TopicItem';
19
+
20
+ const TopicListContent = memo(() => {
21
+ const { t } = useTranslation('chat');
22
+ const virtuosoRef = useRef<VirtuosoHandle>(null);
23
+ const { isDarkMode } = useThemeMode();
24
+ const [topicsInit, activeTopicId, topicLength] = useChatStore((s) => [
25
+ s.topicsInit,
26
+ s.activeTopicId,
27
+ topicSelectors.currentTopicLength(s),
28
+ ]);
29
+ const [visible, updateGuideState] = useUserStore((s) => [
30
+ s.preference.guide?.topic,
31
+ s.updateGuideState,
32
+ ]);
33
+
34
+ const topics = useChatStore(
35
+ (s) => [
36
+ {
37
+ favorite: false,
38
+ id: 'default',
39
+ title: t('topic.defaultTitle'),
40
+ } as ChatTopic,
41
+ ...topicSelectors.displayTopics(s),
42
+ ],
43
+ isEqual,
44
+ );
45
+
46
+ const itemContent = useCallback(
47
+ (index: number, { id, favorite, title }: ChatTopic) =>
48
+ index === 0 ? (
49
+ <TopicItem active={!activeTopicId} fav={favorite} title={title} />
50
+ ) : (
51
+ <TopicItem active={activeTopicId === id} fav={favorite} id={id} key={id} title={title} />
52
+ ),
53
+ [activeTopicId],
54
+ );
55
+
56
+ const activeIndex = topics.findIndex((topic) => topic.id === activeTopicId);
2
57
 
3
- import Header from './Header';
4
- import { Topic } from './Topic';
5
- import TopicSearchBar from './TopicSearchBar';
6
-
7
- const TopicListContent = ({ mobile }: { mobile?: boolean }) => {
8
- return (
9
- <Flexbox gap={mobile ? 8 : 0} height={'100%'} style={{ overflow: 'hidden' }}>
10
- {mobile ? <TopicSearchBar /> : <Header />}
11
- <Flexbox gap={16} height={'100%'} style={{ paddingTop: 6, position: 'relative' }}>
12
- <Topic mobile={mobile} />
13
- </Flexbox>
14
- </Flexbox>
58
+ return !topicsInit ? (
59
+ <SkeletonList />
60
+ ) : (
61
+ <>
62
+ {topicLength === 0 && visible && (
63
+ <Flexbox paddingInline={8}>
64
+ <EmptyCard
65
+ alt={t('topic.guide.desc')}
66
+ cover={imageUrl(`empty_topic_${isDarkMode ? 'dark' : 'light'}.webp`)}
67
+ desc={t('topic.guide.desc')}
68
+ height={120}
69
+ imageProps={{
70
+ priority: true,
71
+ }}
72
+ onVisibleChange={(visible) => {
73
+ updateGuideState({ topic: visible });
74
+ }}
75
+ style={{ flex: 'none', marginBottom: 12 }}
76
+ title={t('topic.guide.title')}
77
+ visible={visible}
78
+ width={200}
79
+ />
80
+ </Flexbox>
81
+ )}
82
+ <Virtuoso
83
+ components={{ ScrollSeekPlaceholder: Placeholder }}
84
+ computeItemKey={(_, item) => item.id}
85
+ data={topics}
86
+ fixedItemHeight={44}
87
+ initialTopMostItemIndex={Math.max(activeIndex, 0)}
88
+ itemContent={itemContent}
89
+ overscan={44 * 10}
90
+ ref={virtuosoRef}
91
+ scrollSeekConfiguration={{
92
+ enter: (velocity) => Math.abs(velocity) > 350,
93
+ exit: (velocity) => Math.abs(velocity) < 10,
94
+ }}
95
+ />
96
+ </>
15
97
  );
16
- };
98
+ });
17
99
 
18
100
  export default TopicListContent;
@@ -2,7 +2,8 @@
2
2
 
3
3
  import { DraggablePanel, DraggablePanelContainer } from '@lobehub/ui';
4
4
  import { createStyles, useResponsive } from 'antd-style';
5
- import { PropsWithChildren, memo, useEffect, useLayoutEffect, useState } from 'react';
5
+ import isEqual from 'fast-deep-equal';
6
+ import { PropsWithChildren, memo, useEffect, useState } from 'react';
6
7
 
7
8
  import SafeSpacing from '@/components/SafeSpacing';
8
9
  import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens';
@@ -26,27 +27,23 @@ const useStyles = createStyles(({ css, token }) => ({
26
27
  const TopicPanel = memo(({ children }: PropsWithChildren) => {
27
28
  const { styles } = useStyles();
28
29
  const { md = true, lg = true } = useResponsive();
29
- const [showAgentSettings, toggleConfig, isPreferenceInit] = useGlobalStore((s) => [
30
+ const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
30
31
  s.preference.showChatSideBar,
31
32
  s.toggleChatSideBar,
32
33
  s.isPreferenceInit,
33
34
  ]);
34
- const [expand, setExpand] = useState(showAgentSettings);
35
+ const [cacheExpand, setCacheExpand] = useState<boolean>(Boolean(showAgentSettings));
35
36
 
36
- const handleExpand = (e: boolean) => {
37
- toggleConfig(e);
38
- setExpand(e);
37
+ const handleExpand = (expand: boolean) => {
38
+ if (isEqual(expand, Boolean(showAgentSettings))) return;
39
+ toggleConfig(expand);
40
+ setCacheExpand(expand);
39
41
  };
40
42
 
41
- useLayoutEffect(() => {
42
- if (!isPreferenceInit) return;
43
- setExpand(showAgentSettings);
44
- }, [isPreferenceInit, showAgentSettings]);
45
-
46
43
  useEffect(() => {
47
- if (lg && showAgentSettings) setExpand(true);
48
- if (!lg) setExpand(false);
49
- }, [lg, showAgentSettings]);
44
+ if (lg && cacheExpand) toggleConfig(true);
45
+ if (!lg) toggleConfig(false);
46
+ }, [lg, cacheExpand]);
50
47
 
51
48
  return (
52
49
  <DraggablePanel
@@ -54,7 +51,7 @@ const TopicPanel = memo(({ children }: PropsWithChildren) => {
54
51
  classNames={{
55
52
  content: styles.content,
56
53
  }}
57
- expand={expand}
54
+ expand={showAgentSettings}
58
55
  minWidth={CHAT_SIDEBAR_WIDTH}
59
56
  mode={md ? 'fixed' : 'float'}
60
57
  onExpandChange={handleExpand}
@@ -17,7 +17,15 @@ const Topics = memo(({ children }: PropsWithChildren) => {
17
17
  const { t } = useTranslation('chat');
18
18
 
19
19
  return (
20
- <Modal allowFullscreen onCancel={() => setOpen(false)} open={open} title={t('topic.title')}>
20
+ <Modal
21
+ allowFullscreen
22
+ onCancel={() => setOpen(false)}
23
+ open={open}
24
+ styles={{
25
+ body: { padding: 0 },
26
+ }}
27
+ title={t('topic.title')}
28
+ >
21
29
  {children}
22
30
  </Modal>
23
31
  );
@@ -3,7 +3,7 @@
3
3
  import { DraggablePanel, DraggablePanelContainer, type DraggablePanelProps } from '@lobehub/ui';
4
4
  import { createStyles, useResponsive } from 'antd-style';
5
5
  import isEqual from 'fast-deep-equal';
6
- import { PropsWithChildren, memo, useEffect, useLayoutEffect, useState } from 'react';
6
+ import { PropsWithChildren, memo, useEffect, useState } from 'react';
7
7
 
8
8
  import { FOLDER_WIDTH } from '@/const/layoutTokens';
9
9
  import { useGlobalStore } from '@/store/global';
@@ -18,25 +18,21 @@ export const useStyles = createStyles(({ css, token }) => ({
18
18
 
19
19
  const SessionPanel = memo<PropsWithChildren>(({ children }) => {
20
20
  const { md = true } = useResponsive();
21
+
21
22
  const { styles } = useStyles();
22
- const [sessionsWidth, sessionExpandable, updatePreference, isPreferenceInit] = useGlobalStore(
23
- (s) => [
24
- s.preference.sessionsWidth,
25
- s.preference.showSessionPanel,
26
- s.updatePreference,
27
- s.isPreferenceInit,
28
- ],
29
- );
30
- const [expand, setExpand] = useState(sessionExpandable);
23
+ const [sessionsWidth, sessionExpandable, updatePreference] = useGlobalStore((s) => [
24
+ s.preference.sessionsWidth,
25
+ s.preference.showSessionPanel,
26
+ s.updatePreference,
27
+ ]);
28
+ const [cacheExpand, setCacheExpand] = useState<boolean>(Boolean(sessionExpandable));
31
29
  const [tmpWidth, setWidth] = useState(sessionsWidth);
32
30
  if (tmpWidth !== sessionsWidth) setWidth(sessionsWidth);
33
31
 
34
- const handleExpand: DraggablePanelProps['onExpandChange'] = (e) => {
35
- updatePreference({
36
- sessionsWidth: e ? 320 : 0,
37
- showSessionPanel: e,
38
- });
39
- setExpand(e);
32
+ const handleExpand = (expand: boolean) => {
33
+ if (isEqual(expand, sessionExpandable)) return;
34
+ updatePreference({ showSessionPanel: expand });
35
+ setCacheExpand(expand);
40
36
  };
41
37
 
42
38
  const handleSizeChange: DraggablePanelProps['onSizeChange'] = (_, size) => {
@@ -47,21 +43,16 @@ const SessionPanel = memo<PropsWithChildren>(({ children }) => {
47
43
  updatePreference({ sessionsWidth: nextWidth });
48
44
  };
49
45
 
50
- useLayoutEffect(() => {
51
- if (!isPreferenceInit) return;
52
- setExpand(sessionExpandable);
53
- }, [isPreferenceInit, sessionExpandable]);
54
-
55
46
  useEffect(() => {
56
- if (md && sessionExpandable) setExpand(true);
57
- if (!md) setExpand(false);
58
- }, [md, sessionExpandable]);
47
+ if (md && cacheExpand) updatePreference({ showSessionPanel: true });
48
+ if (!md) updatePreference({ showSessionPanel: false });
49
+ }, [md, cacheExpand]);
59
50
 
60
51
  return (
61
52
  <DraggablePanel
62
53
  className={styles.panel}
63
54
  defaultSize={{ width: tmpWidth }}
64
- expand={expand}
55
+ expand={sessionExpandable}
65
56
  maxWidth={400}
66
57
  minWidth={FOLDER_WIDTH}
67
58
  mode={md ? 'fixed' : 'float'}
@@ -2,10 +2,9 @@ import { createStyles } from 'antd-style';
2
2
  import { type ReactNode, memo } from 'react';
3
3
  import { Flexbox } from 'react-layout-kit';
4
4
 
5
- const useStyles = createStyles(({ css, token }) => ({
5
+ const useStyles = createStyles(({ css }) => ({
6
6
  header: css`
7
7
  z-index: 10;
8
- box-shadow: 0 2px 6px ${token.colorBgLayout};
9
8
  `,
10
9
  }));
11
10
 
@@ -16,6 +16,7 @@ const MobileContentLayout = ({
16
16
  const content = (
17
17
  <Flexbox
18
18
  height="100%"
19
+ id={'lobe-mobile-scroll-container'}
19
20
  style={{
20
21
  overflowX: 'hidden',
21
22
  overflowY: 'auto',
@@ -6,7 +6,7 @@ import { createStyles } from 'antd-style';
6
6
  import isEqual from 'fast-deep-equal';
7
7
  import { RefreshCw } from 'lucide-react';
8
8
  import Link from 'next/link';
9
- import { memo, useMemo, useState } from 'react';
9
+ import { memo, useState } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import { Flexbox } from 'react-layout-kit';
12
12
 
@@ -71,26 +71,6 @@ const AgentsSuggest = memo<{ mobile?: boolean }>(({ mobile }) => {
71
71
  </Flexbox>
72
72
  ));
73
73
 
74
- const cards = useMemo(
75
- () =>
76
- agentList.slice(sliceStart, sliceStart + agentLength).map((agent) => (
77
- <Link href={`/market?agent=${agent.identifier}`} key={agent.identifier}>
78
- <Flexbox className={styles.card} gap={8} horizontal>
79
- <Avatar avatar={agent.meta.avatar} style={{ flex: 'none' }} />
80
- <Flexbox gap={mobile ? 2 : 8} style={{ overflow: 'hidden', width: '100%' }}>
81
- <Paragraph className={styles.cardTitle} ellipsis={{ rows: 1 }}>
82
- {agent.meta.title}
83
- </Paragraph>
84
- <Paragraph className={styles.cardDesc} ellipsis={{ rows: mobile ? 1 : 2 }}>
85
- {agent.meta.description}
86
- </Paragraph>
87
- </Flexbox>
88
- </Flexbox>
89
- </Link>
90
- )),
91
- [agentList, sliceStart],
92
- );
93
-
94
74
  const handleRefresh = () => {
95
75
  if (!agentList) return;
96
76
  setSliceStart(Math.floor((Math.random() * agentList.length) / 2));
@@ -108,7 +88,23 @@ const AgentsSuggest = memo<{ mobile?: boolean }>(({ mobile }) => {
108
88
  />
109
89
  </Flexbox>
110
90
  <Grid gap={8} rows={2}>
111
- {isLoading ? loadingCards : cards}
91
+ {isLoading
92
+ ? loadingCards
93
+ : agentList.slice(sliceStart, sliceStart + agentLength).map((agent) => (
94
+ <Link href={`/market?agent=${agent.identifier}`} key={agent.identifier}>
95
+ <Flexbox className={styles.card} gap={8} horizontal>
96
+ <Avatar avatar={agent.meta.avatar} style={{ flex: 'none' }} />
97
+ <Flexbox gap={mobile ? 2 : 8} style={{ overflow: 'hidden', width: '100%' }}>
98
+ <Paragraph className={styles.cardTitle} ellipsis={{ rows: 1 }}>
99
+ {agent.meta.title}
100
+ </Paragraph>
101
+ <Paragraph className={styles.cardDesc} ellipsis={{ rows: mobile ? 1 : 2 }}>
102
+ {agent.meta.description}
103
+ </Paragraph>
104
+ </Flexbox>
105
+ </Flexbox>
106
+ </Link>
107
+ ))}
112
108
  </Grid>
113
109
  </Flexbox>
114
110
  );
@@ -4,7 +4,6 @@ import { Flexbox } from 'react-layout-kit';
4
4
 
5
5
  import BrandWatermark from '@/components/BrandWatermark';
6
6
  import Menu from '@/components/Menu';
7
- import { enableAuth } from '@/const/auth';
8
7
  import { useUserStore } from '@/store/user';
9
8
  import { authSelectors } from '@/store/user/selectors';
10
9
 
@@ -18,10 +17,12 @@ import { useMenu } from './useMenu';
18
17
  const PanelContent = memo<{ closePopover: () => void }>(({ closePopover }) => {
19
18
  const router = useRouter();
20
19
  const isLoginWithAuth = useUserStore(authSelectors.isLoginWithAuth);
21
- const [openSignIn, signOut, openUserProfile] = useUserStore((s) => [
20
+ const [openSignIn, signOut, openUserProfile, enableAuth, enabledNextAuth] = useUserStore((s) => [
22
21
  s.openLogin,
23
22
  s.logout,
24
23
  s.openUserProfile,
24
+ s.enableAuth(),
25
+ s.enabledNextAuth(),
25
26
  ]);
26
27
  const { mainItems, logoutItems } = useMenu();
27
28
 
@@ -39,6 +40,8 @@ const PanelContent = memo<{ closePopover: () => void }>(({ closePopover }) => {
39
40
  const handleSignOut = () => {
40
41
  signOut();
41
42
  closePopover();
43
+ // NextAuth doesn't need to redirect to login page
44
+ if (enabledNextAuth) return;
42
45
  router.push('/login');
43
46
  };
44
47
 
@@ -64,12 +64,9 @@ vi.mock('../DataStatistics', () => ({
64
64
  // 定义一个变量来存储 enableAuth 的值
65
65
  let enableAuth = true;
66
66
 
67
- // 模拟 @/const/auth 模块
68
- vi.mock('@/const/auth', () => ({
69
- get enableAuth() {
70
- return enableAuth;
71
- },
72
- }));
67
+ beforeEach(() => {
68
+ useUserStore.setState({ enableAuth: () => true });
69
+ });
73
70
 
74
71
  afterEach(() => {
75
72
  enableAuth = true;
@@ -30,12 +30,25 @@ const useStyles = createStyles(({ css, token }) => ({
30
30
  height: 100%;
31
31
  min-height: 100dvh;
32
32
  max-height: 100dvh;
33
+
34
+ @media (min-device-width: 576px) {
35
+ overflow: hidden;
36
+ }
33
37
  `,
34
38
  // scrollbar-width and scrollbar-color are supported from Chrome 121
35
39
  // https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color
36
40
  scrollbar: css`
37
41
  scrollbar-color: ${token.colorFill} transparent;
38
42
  scrollbar-width: thin;
43
+
44
+ #lobe-mobile-scroll-container {
45
+ scrollbar-width: none;
46
+
47
+ ::-webkit-scrollbar {
48
+ width: 0;
49
+ height: 0;
50
+ }
51
+ }
39
52
  `,
40
53
 
41
54
  // so this is a polyfill for older browsers
@@ -32,6 +32,7 @@ export interface UserPreferenceState {
32
32
  export const DEFAULT_PREFERENCE: UserPreference = {
33
33
  guide: {
34
34
  moveSettingsToAvatar: true,
35
+ topic: true,
35
36
  },
36
37
  telemetry: null,
37
38
  useCmdEnterToSend: false,
@@ -16,10 +16,33 @@ export default ({ token }: { prefixCls: string; token: Theme }) => css`
16
16
  max-height: 100dvh;
17
17
 
18
18
  background: ${token.colorBgLayout};
19
+
20
+ @media (min-device-width: 576px) {
21
+ overflow: hidden;
22
+ }
19
23
  }
20
24
 
21
25
  * {
22
26
  scrollbar-color: ${token.colorFill} transparent;
23
27
  scrollbar-width: thin;
28
+
29
+ ::-webkit-scrollbar {
30
+ width: 0.75em;
31
+ height: 0.75em;
32
+ }
33
+
34
+ ::-webkit-scrollbar-thumb {
35
+ border-radius: 10px;
36
+ }
37
+
38
+ :hover::-webkit-scrollbar-thumb {
39
+ background-color: ${token.colorText};
40
+ background-clip: content-box;
41
+ border: 3px solid transparent;
42
+ }
43
+
44
+ ::-webkit-scrollbar-track {
45
+ background-color: transparent;
46
+ }
24
47
  }
25
48
  `;
@@ -1,101 +0,0 @@
1
- 'use client';
2
-
3
- import { EmptyCard } from '@lobehub/ui';
4
- import { useThemeMode } from 'antd-style';
5
- import isEqual from 'fast-deep-equal';
6
- import React, { memo, useCallback, useRef } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
- import { Flexbox } from 'react-layout-kit';
9
- import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
10
-
11
- import { imageUrl } from '@/const/url';
12
- import { useChatStore } from '@/store/chat';
13
- import { topicSelectors } from '@/store/chat/selectors';
14
- import { useUserStore } from '@/store/user';
15
- import { ChatTopic } from '@/types/topic';
16
-
17
- import { Placeholder, SkeletonList } from './SkeletonList';
18
- import TopicItem from './TopicItem';
19
-
20
- export const Topic = memo<{ mobile?: boolean }>(({ mobile }) => {
21
- const { t } = useTranslation('chat');
22
- const virtuosoRef = useRef<VirtuosoHandle>(null);
23
- const { isDarkMode } = useThemeMode();
24
- const [topicsInit, activeTopicId, topicLength] = useChatStore((s) => [
25
- s.topicsInit,
26
- s.activeTopicId,
27
- topicSelectors.currentTopicLength(s),
28
- ]);
29
- const [visible, updateGuideState] = useUserStore((s) => [
30
- s.preference.guide?.topic,
31
- s.updateGuideState,
32
- ]);
33
-
34
- const topics = useChatStore(
35
- (s) => [
36
- {
37
- favorite: false,
38
- id: 'default',
39
- title: t('topic.defaultTitle'),
40
- } as ChatTopic,
41
- ...topicSelectors.displayTopics(s),
42
- ],
43
- isEqual,
44
- );
45
-
46
- const itemContent = useCallback(
47
- (index: number, { id, favorite, title }: ChatTopic) =>
48
- index === 0 ? (
49
- <TopicItem active={!activeTopicId} fav={favorite} title={title} />
50
- ) : (
51
- <TopicItem active={activeTopicId === id} fav={favorite} id={id} key={id} title={title} />
52
- ),
53
- [activeTopicId],
54
- );
55
-
56
- const activeIndex = topics.findIndex((topic) => topic.id === activeTopicId);
57
-
58
- return !topicsInit ? (
59
- <SkeletonList />
60
- ) : (
61
- <Flexbox
62
- gap={2}
63
- height={'100%'}
64
- paddingInline={mobile ? undefined : 8}
65
- style={{ marginBottom: 12 }}
66
- >
67
- {topicLength === 0 && (
68
- <EmptyCard
69
- alt={t('topic.guide.desc')}
70
- cover={imageUrl(`empty_topic_${isDarkMode ? 'dark' : 'light'}.webp`)}
71
- desc={t('topic.guide.desc')}
72
- height={120}
73
- imageProps={{
74
- priority: true,
75
- }}
76
- onVisibleChange={(visible) => {
77
- updateGuideState({ topic: visible });
78
- }}
79
- style={{ flex: 'none', marginBottom: 12 }}
80
- title={t('topic.guide.title')}
81
- visible={visible}
82
- width={200}
83
- />
84
- )}
85
- <Virtuoso
86
- components={{ ScrollSeekPlaceholder: Placeholder }}
87
- computeItemKey={(_, item) => item.id}
88
- data={topics}
89
- fixedItemHeight={44}
90
- initialTopMostItemIndex={Math.max(activeIndex, 0)}
91
- itemContent={itemContent}
92
- overscan={44 * 10}
93
- ref={virtuosoRef}
94
- scrollSeekConfiguration={{
95
- enter: (velocity) => Math.abs(velocity) > 350,
96
- exit: (velocity) => Math.abs(velocity) < 10,
97
- }}
98
- />
99
- </Flexbox>
100
- );
101
- });