@lobehub/chat 1.26.21 → 1.27.1

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 (84) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/locales/ar/chat.json +1 -21
  3. package/locales/ar/error.json +4 -1
  4. package/locales/ar/topic.json +37 -0
  5. package/locales/bg-BG/chat.json +1 -21
  6. package/locales/bg-BG/error.json +4 -1
  7. package/locales/bg-BG/topic.json +37 -0
  8. package/locales/de-DE/chat.json +1 -21
  9. package/locales/de-DE/error.json +4 -1
  10. package/locales/de-DE/topic.json +37 -0
  11. package/locales/en-US/chat.json +1 -21
  12. package/locales/en-US/error.json +4 -1
  13. package/locales/en-US/topic.json +37 -0
  14. package/locales/es-ES/chat.json +1 -21
  15. package/locales/es-ES/error.json +4 -1
  16. package/locales/es-ES/topic.json +37 -0
  17. package/locales/fa-IR/chat.json +1 -21
  18. package/locales/fa-IR/components.json +1 -0
  19. package/locales/fa-IR/error.json +4 -1
  20. package/locales/fa-IR/topic.json +37 -0
  21. package/locales/fr-FR/chat.json +1 -21
  22. package/locales/fr-FR/error.json +4 -1
  23. package/locales/fr-FR/topic.json +37 -0
  24. package/locales/it-IT/chat.json +1 -21
  25. package/locales/it-IT/error.json +4 -1
  26. package/locales/it-IT/topic.json +37 -0
  27. package/locales/ja-JP/chat.json +1 -21
  28. package/locales/ja-JP/error.json +4 -1
  29. package/locales/ja-JP/topic.json +37 -0
  30. package/locales/ko-KR/chat.json +1 -21
  31. package/locales/ko-KR/error.json +4 -1
  32. package/locales/ko-KR/topic.json +37 -0
  33. package/locales/nl-NL/chat.json +1 -21
  34. package/locales/nl-NL/error.json +4 -1
  35. package/locales/nl-NL/topic.json +37 -0
  36. package/locales/pl-PL/chat.json +1 -21
  37. package/locales/pl-PL/error.json +4 -1
  38. package/locales/pl-PL/topic.json +37 -0
  39. package/locales/pt-BR/chat.json +1 -21
  40. package/locales/pt-BR/error.json +4 -1
  41. package/locales/pt-BR/topic.json +37 -0
  42. package/locales/ru-RU/chat.json +1 -21
  43. package/locales/ru-RU/error.json +4 -1
  44. package/locales/ru-RU/topic.json +37 -0
  45. package/locales/tr-TR/chat.json +1 -21
  46. package/locales/tr-TR/error.json +4 -1
  47. package/locales/tr-TR/topic.json +37 -0
  48. package/locales/vi-VN/chat.json +1 -21
  49. package/locales/vi-VN/error.json +4 -1
  50. package/locales/vi-VN/topic.json +37 -0
  51. package/locales/zh-CN/chat.json +1 -21
  52. package/locales/zh-CN/topic.json +37 -0
  53. package/locales/zh-TW/chat.json +1 -21
  54. package/locales/zh-TW/error.json +4 -1
  55. package/locales/zh-TW/topic.json +37 -0
  56. package/package.json +2 -2
  57. package/src/app/(backend)/webapi/plugin/store/route.ts +22 -8
  58. package/src/app/(main)/@nav/_layout/Desktop/TopActions.tsx +3 -2
  59. package/src/app/(main)/@nav/_layout/Desktop/index.tsx +4 -1
  60. package/src/app/(main)/chat/(workspace)/@topic/features/Header.tsx +27 -9
  61. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ByTimeMode/GroupItem.tsx +30 -0
  62. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ByTimeMode/index.tsx +72 -0
  63. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/DefaultContent.tsx +2 -2
  64. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/FlatMode/index.tsx +60 -0
  65. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/TopicContent.tsx +7 -7
  66. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/index.tsx +17 -53
  67. package/src/app/(main)/chat/(workspace)/@topic/features/TopicSearchBar/index.tsx +2 -2
  68. package/src/app/(main)/chat/(workspace)/_layout/Mobile/ChatHeader/ChatHeaderTitle.tsx +2 -2
  69. package/src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx +5 -2
  70. package/src/const/user.ts +2 -0
  71. package/src/hooks/useFetchTopics.ts +11 -0
  72. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +7 -1
  73. package/src/locales/default/chat.ts +0 -20
  74. package/src/locales/default/index.ts +3 -1
  75. package/src/locales/default/topic.ts +37 -0
  76. package/src/store/chat/slices/topic/action.test.ts +1 -1
  77. package/src/store/chat/slices/topic/action.ts +4 -4
  78. package/src/store/chat/slices/topic/selectors.test.ts +78 -1
  79. package/src/store/chat/slices/topic/selectors.ts +28 -1
  80. package/src/store/user/slices/preference/selectors.ts +4 -0
  81. package/src/types/topic.ts +24 -0
  82. package/src/types/user/index.ts +2 -0
  83. package/src/utils/client/topic.test.ts +142 -0
  84. package/src/utils/client/topic.ts +86 -0
@@ -0,0 +1,30 @@
1
+ import { createStyles } from 'antd-style';
2
+ import dayjs from 'dayjs';
3
+ import React, { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { GroupedTopic } from '@/types/topic';
8
+
9
+ const preformat = (id: string) =>
10
+ id.startsWith('20') ? (id.includes('-') ? dayjs(id).format('MMMM') : id) : undefined;
11
+
12
+ const useStyles = createStyles(({ css, token, stylish }) => ({
13
+ container: css`
14
+ color: ${token.colorTextQuaternary};
15
+ ${stylish.blur}
16
+ `,
17
+ }));
18
+
19
+ const TopicGroupItem = memo<Omit<GroupedTopic, 'children'>>(({ id, title }) => {
20
+ const { t } = useTranslation('topic');
21
+ const { styles } = useStyles();
22
+ const timeTitle = preformat(id) ?? t(`groupTitle.byTime.${id}` as any);
23
+
24
+ return (
25
+ <Flexbox className={styles.container} paddingBlock={8} paddingInline={12}>
26
+ {title ? title : timeTitle}
27
+ </Flexbox>
28
+ );
29
+ });
30
+ export default TopicGroupItem;
@@ -0,0 +1,72 @@
1
+ 'use client';
2
+
3
+ import isEqual from 'fast-deep-equal';
4
+ import React, { memo, useCallback, useMemo, useRef } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { GroupedVirtuoso, VirtuosoHandle } from 'react-virtuoso';
7
+
8
+ import { useChatStore } from '@/store/chat';
9
+ import { topicSelectors } from '@/store/chat/selectors';
10
+ import { ChatTopic } from '@/types/topic';
11
+
12
+ import TopicItem from '../TopicItem';
13
+ import TopicGroupItem from './GroupItem';
14
+
15
+ const ByTimeMode = memo(() => {
16
+ const { t } = useTranslation('topic');
17
+ const virtuosoRef = useRef<VirtuosoHandle>(null);
18
+ const [activeTopicId] = useChatStore((s) => [s.activeTopicId]);
19
+ const groupTopics = useChatStore(topicSelectors.groupedTopicsSelector, isEqual);
20
+
21
+ const { groups, groupCounts, topics } = useMemo(() => {
22
+ return {
23
+ groupCounts: [1, ...groupTopics.map((group) => group.children.length)],
24
+ groups: [
25
+ { id: 'default' },
26
+ ...groupTopics.map((group) => ({ id: group.id, title: group.title })),
27
+ ],
28
+ topics: [
29
+ { favorite: false, id: 'default', title: t('defaultTitle') } as ChatTopic,
30
+ ...groupTopics.flatMap((group) => group.children),
31
+ ],
32
+ };
33
+ }, [groupTopics]);
34
+
35
+ const itemContent = useCallback(
36
+ (index: number) => {
37
+ const { id, favorite, title } = topics[index];
38
+
39
+ return index === 0 ? (
40
+ <TopicItem active={!activeTopicId} fav={favorite} title={title} />
41
+ ) : (
42
+ <TopicItem active={activeTopicId === id} fav={favorite} id={id} key={id} title={title} />
43
+ );
44
+ },
45
+ [activeTopicId, topics],
46
+ );
47
+
48
+ const groupContent = useCallback(
49
+ (index: number) => {
50
+ if (index === 0) return undefined;
51
+
52
+ const topicGroup = groups[index];
53
+ return <TopicGroupItem {...topicGroup} />;
54
+ },
55
+ [groups],
56
+ );
57
+
58
+ // const activeIndex = topics.findIndex((topic) => topic.id === activeTopicId);
59
+
60
+ return (
61
+ <GroupedVirtuoso
62
+ groupContent={groupContent}
63
+ groupCounts={groupCounts}
64
+ itemContent={itemContent}
65
+ ref={virtuosoRef}
66
+ />
67
+ );
68
+ });
69
+
70
+ ByTimeMode.displayName = 'ByTimeMode';
71
+
72
+ export default ByTimeMode;
@@ -9,7 +9,7 @@ import { Flexbox } from 'react-layout-kit';
9
9
  const { Paragraph } = Typography;
10
10
 
11
11
  const DefaultContent = memo(() => {
12
- const { t } = useTranslation('common');
12
+ const { t } = useTranslation('topic');
13
13
 
14
14
  const theme = useTheme();
15
15
 
@@ -19,7 +19,7 @@ const DefaultContent = memo(() => {
19
19
  <Icon color={theme.colorTextDescription} icon={MessageSquareDashed} />
20
20
  </Flexbox>
21
21
  <Paragraph ellipsis={{ rows: 1 }} style={{ margin: 0 }}>
22
- {t('topic.defaultTitle', { ns: 'chat' })}
22
+ {t('defaultTitle')}
23
23
  </Paragraph>
24
24
  <Tag>{t('temp')}</Tag>
25
25
  </Flexbox>
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+
3
+ import isEqual from 'fast-deep-equal';
4
+ import React, { memo, useCallback, useMemo, useRef } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
7
+
8
+ import { useChatStore } from '@/store/chat';
9
+ import { topicSelectors } from '@/store/chat/selectors';
10
+ import { ChatTopic } from '@/types/topic';
11
+
12
+ import TopicItem from '../TopicItem';
13
+
14
+ const FlatMode = memo(() => {
15
+ const { t } = useTranslation('topic');
16
+ const virtuosoRef = useRef<VirtuosoHandle>(null);
17
+ const [activeTopicId] = useChatStore((s) => [s.activeTopicId]);
18
+ const activeTopicList = useChatStore(topicSelectors.displayTopics, isEqual);
19
+
20
+ const topics = useMemo(
21
+ () => [
22
+ { favorite: false, id: 'default', title: t('defaultTitle') } as ChatTopic,
23
+ ...(activeTopicList || []),
24
+ ],
25
+ [activeTopicList],
26
+ );
27
+
28
+ const itemContent = useCallback(
29
+ (index: number, { id, favorite, title }: ChatTopic) =>
30
+ index === 0 ? (
31
+ <TopicItem active={!activeTopicId} fav={favorite} title={title} />
32
+ ) : (
33
+ <TopicItem active={activeTopicId === id} fav={favorite} id={id} key={id} title={title} />
34
+ ),
35
+ [activeTopicId],
36
+ );
37
+
38
+ const activeIndex = topics.findIndex((topic) => topic.id === activeTopicId);
39
+
40
+ return (
41
+ <Virtuoso
42
+ // components={{ ScrollSeekPlaceholder: Placeholder }}
43
+ computeItemKey={(_, item) => item.id}
44
+ data={topics}
45
+ fixedItemHeight={44}
46
+ initialTopMostItemIndex={Math.max(activeIndex, 0)}
47
+ itemContent={itemContent}
48
+ overscan={44 * 10}
49
+ ref={virtuosoRef}
50
+ // scrollSeekConfiguration={{
51
+ // enter: (velocity) => Math.abs(velocity) > 350,
52
+ // exit: (velocity) => Math.abs(velocity) < 10,
53
+ // }}
54
+ />
55
+ );
56
+ });
57
+
58
+ FlatMode.displayName = 'FlatMode';
59
+
60
+ export default FlatMode;
@@ -42,7 +42,7 @@ interface TopicContentProps {
42
42
  }
43
43
 
44
44
  const TopicContent = memo<TopicContentProps>(({ id, title, fav, showMore }) => {
45
- const { t } = useTranslation('common');
45
+ const { t } = useTranslation(['topic', 'common']);
46
46
 
47
47
  const mobile = useIsMobile();
48
48
 
@@ -76,7 +76,7 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav, showMore }) => {
76
76
  {
77
77
  icon: <Icon icon={Wand2} />,
78
78
  key: 'autoRename',
79
- label: t('topic.actions.autoRename', { ns: 'chat' }),
79
+ label: t('actions.autoRename'),
80
80
  onClick: () => {
81
81
  autoRenameTopicTitle(id);
82
82
  },
@@ -84,7 +84,7 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav, showMore }) => {
84
84
  {
85
85
  icon: <Icon icon={PencilLine} />,
86
86
  key: 'rename',
87
- label: t('rename'),
87
+ label: t('rename', { ns: 'common' }),
88
88
  onClick: () => {
89
89
  toggleEditing(true);
90
90
  },
@@ -95,7 +95,7 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav, showMore }) => {
95
95
  {
96
96
  icon: <Icon icon={LucideCopy} />,
97
97
  key: 'duplicate',
98
- label: t('topic.actions.duplicate', { ns: 'chat' }),
98
+ label: t('actions.duplicate'),
99
99
  onClick: () => {
100
100
  duplicateTopic(id);
101
101
  },
@@ -103,7 +103,7 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav, showMore }) => {
103
103
  // {
104
104
  // icon: <Icon icon={LucideDownload} />,
105
105
  // key: 'export',
106
- // label: t('topic.actions.export', { ns: 'chat' }),
106
+ // label: t('topic.actions.export'),
107
107
  // onClick: () => {
108
108
  // configService.exportSingleTopic(sessionId, id);
109
109
  // },
@@ -120,7 +120,7 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav, showMore }) => {
120
120
  danger: true,
121
121
  icon: <Icon icon={Trash} />,
122
122
  key: 'delete',
123
- label: t('delete'),
123
+ label: t('delete', { ns: 'common' }),
124
124
  onClick: () => {
125
125
  if (!id) return;
126
126
 
@@ -130,7 +130,7 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav, showMore }) => {
130
130
  onOk: async () => {
131
131
  await removeTopic(id);
132
132
  },
133
- title: t('topic.confirmRemoveTopic', { ns: 'chat' }),
133
+ title: t('actions.confirmRemoveTopic'),
134
134
  });
135
135
  },
136
136
  },
@@ -3,61 +3,38 @@
3
3
  import { EmptyCard } from '@lobehub/ui';
4
4
  import { useThemeMode } from 'antd-style';
5
5
  import isEqual from 'fast-deep-equal';
6
- import React, { memo, useCallback, useMemo, useRef } from 'react';
6
+ import React, { memo } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { Flexbox } from 'react-layout-kit';
9
- import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
10
9
 
11
10
  import { imageUrl } from '@/const/url';
11
+ import { useFetchTopics } from '@/hooks/useFetchTopics';
12
12
  import { useChatStore } from '@/store/chat';
13
13
  import { topicSelectors } from '@/store/chat/selectors';
14
- import { useSessionStore } from '@/store/session';
15
14
  import { useUserStore } from '@/store/user';
16
- import { ChatTopic } from '@/types/topic';
15
+ import { preferenceSelectors } from '@/store/user/selectors';
16
+ import { TopicDisplayMode } from '@/types/topic';
17
17
 
18
18
  import { SkeletonList } from '../SkeletonList';
19
- import TopicItem from './TopicItem';
19
+ import ByTimeMode from './ByTimeMode';
20
+ import FlatMode from './FlatMode';
20
21
 
21
22
  const TopicListContent = memo(() => {
22
- const { t } = useTranslation('chat');
23
- const virtuosoRef = useRef<VirtuosoHandle>(null);
23
+ const { t } = useTranslation('topic');
24
24
  const { isDarkMode } = useThemeMode();
25
- const [topicsInit, activeTopicId, topicLength, useFetchTopics] = useChatStore((s) => [
25
+ const [topicsInit, topicLength] = useChatStore((s) => [
26
26
  s.topicsInit,
27
- s.activeTopicId,
28
27
  topicSelectors.currentTopicLength(s),
29
- s.useFetchTopics,
30
28
  ]);
31
- const [visible, updateGuideState] = useUserStore((s) => [
29
+ const activeTopicList = useChatStore(topicSelectors.displayTopics, isEqual);
30
+
31
+ const [visible, updateGuideState, topicDisplayMode] = useUserStore((s) => [
32
32
  s.preference.guide?.topic,
33
33
  s.updateGuideState,
34
+ preferenceSelectors.topicDisplayMode(s),
34
35
  ]);
35
36
 
36
- const activeTopicList = useChatStore(topicSelectors.displayTopics, isEqual);
37
-
38
- const topics = useMemo(
39
- () => [
40
- { favorite: false, id: 'default', title: t('topic.defaultTitle') } as ChatTopic,
41
- ...(activeTopicList || []),
42
- ],
43
- [activeTopicList],
44
- );
45
-
46
- const [sessionId] = useSessionStore((s) => [s.activeId]);
47
-
48
- useFetchTopics(sessionId);
49
-
50
- const itemContent = useCallback(
51
- (index: number, { id, favorite, title }: ChatTopic) =>
52
- index === 0 ? (
53
- <TopicItem active={!activeTopicId} fav={favorite} title={title} />
54
- ) : (
55
- <TopicItem active={activeTopicId === id} fav={favorite} id={id} key={id} title={title} />
56
- ),
57
- [activeTopicId],
58
- );
59
-
60
- const activeIndex = topics.findIndex((topic) => topic.id === activeTopicId);
37
+ useFetchTopics();
61
38
 
62
39
  // first time loading or has no data
63
40
  if (!topicsInit || !activeTopicList) return <SkeletonList />;
@@ -67,9 +44,9 @@ const TopicListContent = memo(() => {
67
44
  {topicLength === 0 && visible && (
68
45
  <Flexbox paddingInline={8}>
69
46
  <EmptyCard
70
- alt={t('topic.guide.desc')}
47
+ alt={t('guide.desc')}
71
48
  cover={imageUrl(`empty_topic_${isDarkMode ? 'dark' : 'light'}.webp`)}
72
- desc={t('topic.guide.desc')}
49
+ desc={t('guide.desc')}
73
50
  height={120}
74
51
  imageProps={{
75
52
  priority: true,
@@ -78,26 +55,13 @@ const TopicListContent = memo(() => {
78
55
  updateGuideState({ topic: visible });
79
56
  }}
80
57
  style={{ flex: 'none', marginBottom: 12 }}
81
- title={t('topic.guide.title')}
58
+ title={t('guide.title')}
82
59
  visible={visible}
83
60
  width={200}
84
61
  />
85
62
  </Flexbox>
86
63
  )}
87
- <Virtuoso
88
- // components={{ ScrollSeekPlaceholder: Placeholder }}
89
- computeItemKey={(_, item) => item.id}
90
- data={topics}
91
- fixedItemHeight={44}
92
- initialTopMostItemIndex={Math.max(activeIndex, 0)}
93
- itemContent={itemContent}
94
- overscan={44 * 10}
95
- ref={virtuosoRef}
96
- // scrollSeekConfiguration={{
97
- // enter: (velocity) => Math.abs(velocity) > 350,
98
- // exit: (velocity) => Math.abs(velocity) < 10,
99
- // }}
100
- />
64
+ {topicDisplayMode === TopicDisplayMode.ByTime ? <ByTimeMode /> : <FlatMode />}
101
65
  </>
102
66
  );
103
67
  });
@@ -9,7 +9,7 @@ import { useChatStore } from '@/store/chat';
9
9
  import { useServerConfigStore } from '@/store/serverConfig';
10
10
 
11
11
  const TopicSearchBar = memo<{ onClear?: () => void }>(({ onClear }) => {
12
- const { t } = useTranslation('chat');
12
+ const { t } = useTranslation('topic');
13
13
 
14
14
  const [keywords, setKeywords] = useState('');
15
15
  const mobile = useServerConfigStore((s) => s.isMobile);
@@ -31,7 +31,7 @@ const TopicSearchBar = memo<{ onClear?: () => void }>(({ onClear }) => {
31
31
  setKeywords(value);
32
32
  useChatStore.setState({ isSearchingTopic: !!value });
33
33
  }}
34
- placeholder={t('topic.searchPlaceholder')}
34
+ placeholder={t('searchPlaceholder')}
35
35
  spotlight={!mobile}
36
36
  type={mobile ? 'block' : 'ghost'}
37
37
  value={keywords}
@@ -12,7 +12,7 @@ import { useSessionStore } from '@/store/session';
12
12
  import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
13
13
 
14
14
  const ChatHeaderTitle = memo(() => {
15
- const { t } = useTranslation('chat');
15
+ const { t } = useTranslation(['chat', 'topic']);
16
16
  const toggleConfig = useGlobalStore((s) => s.toggleMobileTopic);
17
17
  const [topicLength, topic] = useChatStore((s) => [
18
18
  topicSelectors.currentTopicLength(s),
@@ -30,7 +30,7 @@ const ChatHeaderTitle = memo(() => {
30
30
  <MobileNavBarTitle
31
31
  desc={
32
32
  <Flexbox align={'center'} gap={4} horizontal onClick={() => toggleConfig()}>
33
- <span>{topic?.title || t('topic.title')}</span>
33
+ <span>{topic?.title || t('title', { ns: 'topic' })}</span>
34
34
  <ActionIcon
35
35
  active
36
36
  icon={ChevronDown}
@@ -4,6 +4,7 @@ import { Modal } from '@lobehub/ui';
4
4
  import { PropsWithChildren, memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
+ import { useFetchTopics } from '@/hooks/useFetchTopics';
7
8
  import { useGlobalStore } from '@/store/global';
8
9
  import { systemStatusSelectors } from '@/store/global/selectors';
9
10
 
@@ -15,7 +16,9 @@ const Topics = memo(({ children }: PropsWithChildren) => {
15
16
  s.toggleMobileTopic,
16
17
  ]);
17
18
  const [open, setOpen] = useWorkspaceModal(showAgentSettings, toggleConfig);
18
- const { t } = useTranslation('chat');
19
+ const { t } = useTranslation('topic');
20
+
21
+ useFetchTopics();
19
22
 
20
23
  return (
21
24
  <Modal
@@ -25,7 +28,7 @@ const Topics = memo(({ children }: PropsWithChildren) => {
25
28
  styles={{
26
29
  body: { padding: 0 },
27
30
  }}
28
- title={t('topic.title')}
31
+ title={t('title')}
29
32
  >
30
33
  {children}
31
34
  </Modal>
package/src/const/user.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { TopicDisplayMode } from '@/types/topic';
1
2
  import { UserPreference } from '@/types/user';
2
3
 
3
4
  export const DEFAULT_PREFERENCE: UserPreference = {
@@ -6,5 +7,6 @@ export const DEFAULT_PREFERENCE: UserPreference = {
6
7
  topic: true,
7
8
  },
8
9
  telemetry: null,
10
+ topicDisplayMode: TopicDisplayMode.ByTime,
9
11
  useCmdEnterToSend: false,
10
12
  };
@@ -0,0 +1,11 @@
1
+ import { useChatStore } from '@/store/chat';
2
+ import { useSessionStore } from '@/store/session';
3
+
4
+ /**
5
+ * Fetch topics for the current session
6
+ */
7
+ export const useFetchTopics = () => {
8
+ const [sessionId] = useSessionStore((s) => [s.activeId]);
9
+ const [useFetchTopics] = useChatStore((s) => [s.useFetchTopics]);
10
+ useFetchTopics(sessionId);
11
+ };
@@ -50,6 +50,7 @@ export interface CustomClientOptions<T extends Record<string, any> = any> {
50
50
  }
51
51
 
52
52
  interface OpenAICompatibleFactoryOptions<T extends Record<string, any> = any> {
53
+ apiKey?: string;
53
54
  baseURL?: string;
54
55
  chatCompletion?: {
55
56
  handleError?: (
@@ -139,6 +140,7 @@ export function transformResponseToStream(data: OpenAI.ChatCompletion) {
139
140
  export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>({
140
141
  provider,
141
142
  baseURL: DEFAULT_BASE_URL,
143
+ apiKey: DEFAULT_API_LEY,
142
144
  errorType,
143
145
  debug,
144
146
  constructorOptions,
@@ -158,7 +160,11 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
158
160
  private _options: ConstructorOptions<T>;
159
161
 
160
162
  constructor(options: ClientOptions & Record<string, any> = {}) {
161
- const _options = { ...options, baseURL: options.baseURL?.trim() || DEFAULT_BASE_URL };
163
+ const _options = {
164
+ ...options,
165
+ apiKey: options.apiKey?.trim() || DEFAULT_API_LEY,
166
+ baseURL: options.baseURL?.trim() || DEFAULT_BASE_URL,
167
+ };
162
168
  const { apiKey, baseURL = DEFAULT_BASE_URL, ...res } = _options;
163
169
  this._options = _options as ConstructorOptions<T>;
164
170
 
@@ -117,7 +117,6 @@ export default {
117
117
  loading: '识别中...',
118
118
  prettifying: '润色中...',
119
119
  },
120
- temp: '临时',
121
120
  tokenDetails: {
122
121
  chats: '会话消息',
123
122
  rest: '剩余可用',
@@ -133,29 +132,10 @@ export default {
133
132
  used: '使用',
134
133
  },
135
134
  topic: {
136
- actions: {
137
- autoRename: '智能重命名',
138
- duplicate: '创建副本',
139
- export: '导出话题',
140
- },
141
135
  checkOpenNewTopic: '是否开启新话题?',
142
136
  checkSaveCurrentMessages: '是否保存当前会话为话题?',
143
- confirmRemoveAll: '即将删除全部话题,删除后将不可恢复,请谨慎操作。',
144
- confirmRemoveTopic: '即将删除该话题,删除后将不可恢复,请谨慎操作。',
145
- confirmRemoveUnstarred: '即将删除未收藏话题,删除后将不可恢复,请谨慎操作。',
146
- defaultTitle: '默认话题',
147
- duplicateLoading: '话题复制中...',
148
- duplicateSuccess: '话题复制成功',
149
- guide: {
150
- desc: '点击发送左侧按钮可将当前会话保存为历史话题,并开启新一轮会话',
151
- title: '话题列表',
152
- },
153
137
  openNewTopic: '开启新话题',
154
- removeAll: '删除全部话题',
155
- removeUnstarred: '删除未收藏话题',
156
138
  saveCurrentMessages: '将当前会话保存为话题',
157
- searchPlaceholder: '搜索话题...',
158
- title: '话题',
159
139
  },
160
140
  translate: {
161
141
  action: '翻译',
@@ -1,4 +1,3 @@
1
- import tool from '../default/tool';
2
1
  import auth from './auth';
3
2
  import chat from './chat';
4
3
  import clerk from './clerk';
@@ -17,6 +16,8 @@ import portal from './portal';
17
16
  import providers from './providers';
18
17
  import ragEval from './ragEval';
19
18
  import setting from './setting';
19
+ import tool from './tool';
20
+ import topic from './topic';
20
21
  import welcome from './welcome';
21
22
 
22
23
  const resources = {
@@ -39,6 +40,7 @@ const resources = {
39
40
  ragEval,
40
41
  setting,
41
42
  tool,
43
+ topic,
42
44
  welcome,
43
45
  } as const;
44
46
 
@@ -0,0 +1,37 @@
1
+ export default {
2
+ actions: {
3
+ autoRename: '智能重命名',
4
+ confirmRemoveAll: '即将删除全部话题,删除后将不可恢复,请谨慎操作。',
5
+ confirmRemoveTopic: '即将删除该话题,删除后将不可恢复,请谨慎操作。',
6
+ confirmRemoveUnstarred: '即将删除未收藏话题,删除后将不可恢复,请谨慎操作。',
7
+ duplicate: '创建副本',
8
+ export: '导出话题',
9
+ removeAll: '删除全部话题',
10
+ removeUnstarred: '删除未收藏话题',
11
+ },
12
+ defaultTitle: '默认话题',
13
+ duplicateLoading: '话题复制中...',
14
+ duplicateSuccess: '话题复制成功',
15
+ favorite: '收藏',
16
+ groupMode: {
17
+ ascMessages: '按消息总数顺序',
18
+ byTime: '按时间分组',
19
+ descMessages: '按消息总数倒序',
20
+ flat: '不分组',
21
+ },
22
+ groupTitle: {
23
+ byTime: {
24
+ month: '本月',
25
+ today: '今天',
26
+ week: '本周',
27
+ yesterday: '昨天',
28
+ },
29
+ },
30
+ guide: {
31
+ desc: '点击发送左侧按钮可将当前会话保存为历史话题,并开启新一轮会话',
32
+ title: '话题列表',
33
+ },
34
+ searchPlaceholder: '搜索话题...',
35
+ temp: '临时',
36
+ title: '话题',
37
+ };
@@ -487,7 +487,7 @@ describe('topic action', () => {
487
487
  expect(createTopicSpy).toHaveBeenCalledWith({
488
488
  sessionId: activeId,
489
489
  messages: messages.map((m) => m.id),
490
- title: 'topic.defaultTitle',
490
+ title: 'defaultTitle',
491
491
  });
492
492
  expect(refreshTopicSpy).toHaveBeenCalled();
493
493
  });
@@ -84,7 +84,7 @@ export const chatTopic: StateCreator<
84
84
  set({ creatingTopic: true }, false, n('creatingTopic/start'));
85
85
  const topicId = await internal_createTopic({
86
86
  sessionId: activeId,
87
- title: t('topic.defaultTitle', { ns: 'chat' }),
87
+ title: t('defaultTitle', { ns: 'topic' }),
88
88
  messages: messages.map((m) => m.id),
89
89
  });
90
90
  set({ creatingTopic: false }, false, n('creatingTopic/end'));
@@ -102,7 +102,7 @@ export const chatTopic: StateCreator<
102
102
  // 1. create topic and bind these messages
103
103
  const topicId = await internal_createTopic({
104
104
  sessionId: activeId,
105
- title: t('topic.defaultTitle', { ns: 'chat' }),
105
+ title: t('defaultTitle', { ns: 'topic' }),
106
106
  messages: messages.map((m) => m.id),
107
107
  });
108
108
 
@@ -122,7 +122,7 @@ export const chatTopic: StateCreator<
122
122
  const newTitle = t('duplicateTitle', { ns: 'chat', title: topic?.title });
123
123
 
124
124
  message.loading({
125
- content: t('topic.duplicateLoading', { ns: 'chat' }),
125
+ content: t('duplicateLoading', { ns: 'topic' }),
126
126
  key: 'duplicateTopic',
127
127
  duration: 0,
128
128
  });
@@ -130,7 +130,7 @@ export const chatTopic: StateCreator<
130
130
  const newTopicId = await topicService.cloneTopic(id, newTitle);
131
131
  await refreshTopic();
132
132
  message.destroy('duplicateTopic');
133
- message.success(t('topic.duplicateSuccess', { ns: 'chat' }));
133
+ message.success(t('duplicateSuccess', { ns: 'topic' }));
134
134
 
135
135
  await switchTopic(newTopicId);
136
136
  },