@lobehub/chat 0.119.3 → 0.119.5

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 (56) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +3 -3
  3. package/README.zh-CN.md +3 -3
  4. package/locales/de-DE/chat.json +4 -0
  5. package/locales/en-US/chat.json +4 -0
  6. package/locales/es-ES/chat.json +4 -0
  7. package/locales/fr-FR/chat.json +4 -0
  8. package/locales/it-IT/chat.json +4 -0
  9. package/locales/ja-JP/chat.json +4 -0
  10. package/locales/ko-KR/chat.json +4 -0
  11. package/locales/nl-NL/chat.json +4 -0
  12. package/locales/pl-PL/chat.json +4 -0
  13. package/locales/pt-BR/chat.json +4 -0
  14. package/locales/ru-RU/chat.json +4 -0
  15. package/locales/tr-TR/chat.json +4 -0
  16. package/locales/vi-VN/chat.json +4 -0
  17. package/locales/zh-CN/chat.json +5 -1
  18. package/locales/zh-TW/chat.json +4 -0
  19. package/package.json +2 -1
  20. package/src/app/chat/features/TopicListContent/Topic/SkeletonList.tsx +46 -12
  21. package/src/app/chat/features/TopicListContent/Topic/index.tsx +67 -33
  22. package/src/app/chat/features/TopicListContent/index.tsx +2 -1
  23. package/src/app/settings/llm/LLM/index.tsx +7 -1
  24. package/src/features/Conversation/components/AutoScroll.tsx +25 -0
  25. package/src/features/Conversation/components/BackBottom/index.tsx +31 -0
  26. package/src/features/Conversation/components/BackBottom/style.ts +36 -0
  27. package/src/features/Conversation/components/ChatItem/index.tsx +63 -42
  28. package/src/features/Conversation/components/ChatList/index.tsx +6 -29
  29. package/src/features/Conversation/components/SkeletonList.tsx +5 -2
  30. package/src/features/Conversation/components/VirtualizedList/index.tsx +78 -0
  31. package/src/features/Conversation/index.tsx +8 -18
  32. package/src/locales/create.ts +1 -1
  33. package/src/locales/default/chat.ts +5 -1
  34. package/src/locales/default/index.ts +0 -2
  35. package/src/store/chat/slices/message/selectors.ts +6 -0
  36. package/vercel.json +1 -1
  37. package/locales/de-DE/empty.json +0 -6
  38. package/locales/en-US/empty.json +0 -6
  39. package/locales/es-ES/empty.json +0 -6
  40. package/locales/fr-FR/empty.json +0 -6
  41. package/locales/it-IT/empty.json +0 -6
  42. package/locales/ja-JP/empty.json +0 -6
  43. package/locales/ko-KR/empty.json +0 -6
  44. package/locales/nl-NL/empty.json +0 -6
  45. package/locales/pl-PL/empty.json +0 -6
  46. package/locales/pt-BR/empty.json +0 -6
  47. package/locales/ru-RU/empty.json +0 -6
  48. package/locales/tr-TR/empty.json +0 -6
  49. package/locales/vi-VN/empty.json +0 -6
  50. package/locales/zh-CN/empty.json +0 -6
  51. package/locales/zh-TW/empty.json +0 -6
  52. package/src/features/Conversation/components/ChatList/style.ts +0 -10
  53. package/src/features/Conversation/components/ScrollAnchor/index.tsx +0 -29
  54. package/src/features/Conversation/components/ScrollAnchor/useAtBottom.ts +0 -20
  55. package/src/locales/default/empty.ts +0 -6
  56. /package/src/features/Conversation/components/{ChatList → ChatItem}/HistoryDivider.tsx +0 -0
@@ -13,12 +13,14 @@ import { renderErrorMessages } from '../../Error';
13
13
  import { renderMessagesExtra } from '../../Extras';
14
14
  import { renderMessages, useAvatarsClick } from '../../Messages';
15
15
  import ActionsBar from './ActionsBar';
16
+ import HistoryDivider from './HistoryDivider';
16
17
 
17
18
  export interface ChatListItemProps {
19
+ id: string;
18
20
  index: number;
19
21
  }
20
22
 
21
- const Item = memo<ChatListItemProps>(({ index }) => {
23
+ const Item = memo<ChatListItemProps>(({ index, id }) => {
22
24
  const { t } = useTranslation('common');
23
25
 
24
26
  const [editing, setEditing] = useState(false);
@@ -28,13 +30,18 @@ const Item = memo<ChatListItemProps>(({ index }) => {
28
30
  });
29
31
 
30
32
  const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
31
- const item = useChatStore(
32
- (s) => chatSelectors.currentChatsWithGuideMessage(meta)(s)[index],
33
- isEqual,
34
- );
33
+ const item = useChatStore((s) => {
34
+ const chats = chatSelectors.currentChatsWithGuideMessage(meta)(s);
35
+
36
+ if (index >= chats.length) return;
37
+
38
+ return chatSelectors.currentChatsWithGuideMessage(meta)(s)[index];
39
+ }, isEqual);
40
+
41
+ const historyLength = useChatStore((s) => chatSelectors.currentChats(s).length);
35
42
 
36
43
  const [loading, onMessageChange] = useChatStore((s) => [
37
- s.chatLoadingId === item.id,
44
+ s.chatLoadingId === id,
38
45
  s.updateMessageContent,
39
46
  ]);
40
47
 
@@ -49,7 +56,7 @@ const Item = memo<ChatListItemProps>(({ index }) => {
49
56
 
50
57
  return <RenderFunction {...data} editableContent={editableContent} />;
51
58
  },
52
- [item.role],
59
+ [item?.role],
53
60
  );
54
61
 
55
62
  const MessageExtra = useCallback(
@@ -61,7 +68,7 @@ const Item = memo<ChatListItemProps>(({ index }) => {
61
68
  if (!RenderFunction) return;
62
69
  return <RenderFunction {...data} />;
63
70
  },
64
- [item.role],
71
+ [item?.role],
65
72
  );
66
73
 
67
74
  const ErrorMessage = useCallback(
@@ -75,51 +82,65 @@ const Item = memo<ChatListItemProps>(({ index }) => {
75
82
  if (!RenderFunction) return;
76
83
  return <RenderFunction {...data} />;
77
84
  },
78
- [item.error],
85
+ [item?.error],
79
86
  );
80
87
 
81
88
  const error = useMemo(() => {
82
- if (!item.error) return;
89
+ if (!item?.error) return;
83
90
  const message = item.error?.message;
84
91
  let alertConfig = {};
85
92
  if (item.error.type && renderErrorMessages?.[item.error.type]) {
86
93
  alertConfig = renderErrorMessages[item.error.type]?.config as AlertProps;
87
94
  }
88
95
  return { message, ...alertConfig };
89
- }, [item.error]);
96
+ }, [item?.error]);
97
+
98
+ const enableHistoryDivider = useSessionStore((s) => {
99
+ const config = agentSelectors.currentAgentConfig(s);
100
+ return (
101
+ config.enableHistoryCount &&
102
+ historyLength > (config.historyCount ?? 0) &&
103
+ config.historyCount === historyLength - index + 1
104
+ );
105
+ });
90
106
 
91
107
  return (
92
- <ChatItem
93
- actions={<ActionsBar index={index} setEditing={setEditing} />}
94
- avatar={item.meta}
95
- editing={editing}
96
- error={error}
97
- errorMessage={<ErrorMessage data={item} />}
98
- loading={loading}
99
- message={item.content}
100
- messageExtra={<MessageExtra data={item} />}
101
- onAvatarClick={onAvatarsClick?.(item.role)}
102
- onChange={(value) => onMessageChange(item.id, value)}
103
- onDoubleClick={(e) => {
104
- if (item.id === 'default' || item.error) return;
105
- if (item.role && ['assistant', 'user'].includes(item.role) && e.altKey) {
106
- setEditing(true);
107
- }
108
- }}
109
- onEditingChange={setEditing}
110
- placement={type === 'chat' ? (item.role === 'user' ? 'right' : 'left') : 'left'}
111
- primary={item.role === 'user'}
112
- renderMessage={(editableContent) => (
113
- <RenderMessage data={item} editableContent={editableContent} />
114
- )}
115
- text={{
116
- cancel: t('cancel'),
117
- confirm: t('ok'),
118
- edit: t('edit'),
119
- }}
120
- time={item.updatedAt || item.createdAt}
121
- type={type === 'chat' ? 'block' : 'pure'}
122
- />
108
+ item && (
109
+ <>
110
+ <HistoryDivider enable={enableHistoryDivider} />
111
+ <ChatItem
112
+ actions={<ActionsBar index={index} setEditing={setEditing} />}
113
+ avatar={item.meta}
114
+ editing={editing}
115
+ error={error}
116
+ errorMessage={<ErrorMessage data={item} />}
117
+ loading={loading}
118
+ message={item.content}
119
+ messageExtra={<MessageExtra data={item} />}
120
+ onAvatarClick={onAvatarsClick?.(item.role)}
121
+ onChange={(value) => onMessageChange(item.id, value)}
122
+ onDoubleClick={(e) => {
123
+ if (item.id === 'default' || item.error) return;
124
+ if (item.role && ['assistant', 'user'].includes(item.role) && e.altKey) {
125
+ setEditing(true);
126
+ }
127
+ }}
128
+ onEditingChange={setEditing}
129
+ placement={type === 'chat' ? (item.role === 'user' ? 'right' : 'left') : 'left'}
130
+ primary={item.role === 'user'}
131
+ renderMessage={(editableContent) => (
132
+ <RenderMessage data={item} editableContent={editableContent} />
133
+ )}
134
+ text={{
135
+ cancel: t('cancel'),
136
+ confirm: t('ok'),
137
+ edit: t('edit'),
138
+ }}
139
+ time={item.updatedAt || item.createdAt}
140
+ type={type === 'chat' ? 'block' : 'pure'}
141
+ />
142
+ </>
143
+ )
123
144
  );
124
145
  });
125
146
 
@@ -1,42 +1,19 @@
1
- import isEqual from 'fast-deep-equal';
2
- import { Fragment, memo } from 'react';
1
+ import { memo } from 'react';
3
2
  import { Flexbox } from 'react-layout-kit';
4
3
 
5
4
  import { useChatStore } from '@/store/chat';
6
5
  import { chatSelectors } from '@/store/chat/selectors';
7
- import { useSessionStore } from '@/store/session';
8
- import { agentSelectors } from '@/store/session/selectors';
9
6
 
10
7
  import Item from '../ChatItem';
11
- import HistoryDivider from './HistoryDivider';
12
- import { useStyles } from './style';
13
8
 
14
9
  const ChatList = memo(() => {
15
- const { styles } = useStyles();
16
-
17
- const data = useChatStore(chatSelectors.currentChatIDsWithGuideMessage, isEqual);
18
-
19
- const [enableHistoryCount, historyCount = 0] = useSessionStore((s) => {
20
- const config = agentSelectors.currentAgentConfig(s);
21
- return [config.enableHistoryCount, config.historyCount];
22
- });
10
+ const ids = useChatStore(chatSelectors.currentChatIDsWithGuideMessage);
23
11
 
24
12
  return (
25
- <Flexbox className={styles.container}>
26
- {data.map((item, index) => {
27
- const historyLength = data.length;
28
- const enableHistoryDivider =
29
- enableHistoryCount &&
30
- historyLength > historyCount &&
31
- historyCount === historyLength - index + 1;
32
-
33
- return (
34
- <Fragment key={item}>
35
- <HistoryDivider enable={enableHistoryDivider} />
36
- <Item index={index} />
37
- </Fragment>
38
- );
39
- })}
13
+ <Flexbox height={'100%'} style={{ position: 'relative' }}>
14
+ {ids.map((id, index) => (
15
+ <Item id={id} index={index} key={id} />
16
+ ))}
40
17
  </Flexbox>
41
18
  );
42
19
  });
@@ -16,11 +16,14 @@ const useStyles = createStyles(({ css, prefixCls }) => ({
16
16
  }
17
17
  `,
18
18
  }));
19
- const SkeletonList = memo(() => {
19
+ interface SkeletonListProps {
20
+ mobile?: boolean;
21
+ }
22
+ const SkeletonList = memo<SkeletonListProps>(({ mobile }) => {
20
23
  const { styles } = useStyles();
21
24
 
22
25
  return (
23
- <Flexbox gap={24} padding={12} style={{ marginTop: 24 }}>
26
+ <Flexbox gap={24} padding={12} style={{ marginTop: 24 + (mobile ? 0 : 64) }}>
24
27
  <Skeleton
25
28
  active
26
29
  avatar={{ size: 40 }}
@@ -0,0 +1,78 @@
1
+ import isEqual from 'fast-deep-equal';
2
+ import React, { memo, useEffect, useRef, useState } from 'react';
3
+ import { Flexbox } from 'react-layout-kit';
4
+ import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
5
+
6
+ import { useChatStore } from '@/store/chat';
7
+ import { chatSelectors } from '@/store/chat/selectors';
8
+ import { isMobileScreen } from '@/utils/screen';
9
+
10
+ import AutoScroll from '../AutoScroll';
11
+ import Item from '../ChatItem';
12
+
13
+ const itemContent = (index: number, id: string) => {
14
+ const isMobile = isMobileScreen();
15
+
16
+ return index === 0 ? (
17
+ <div style={{ height: isMobile ? 24 : 64 }} />
18
+ ) : (
19
+ <Item id={id} index={index - 1} />
20
+ );
21
+ };
22
+
23
+ interface VirtualizedListProps {
24
+ mobile?: boolean;
25
+ }
26
+ const VirtualizedList = memo<VirtualizedListProps>(({ mobile }) => {
27
+ const virtuosoRef = useRef<VirtuosoHandle>(null);
28
+ const [atBottom, setAtBottom] = useState(true);
29
+
30
+ const data = useChatStore(
31
+ (s) => ['empty', ...chatSelectors.currentChatIDsWithGuideMessage(s)],
32
+ isEqual,
33
+ );
34
+ const [id, chatLoading] = useChatStore((s) => [
35
+ chatSelectors.currentChatKey(s),
36
+ chatSelectors.currentChatLoadingState(s),
37
+ ]);
38
+
39
+ useEffect(() => {
40
+ if (virtuosoRef.current) {
41
+ virtuosoRef.current.scrollToIndex({ align: 'end', behavior: 'auto', index: 'LAST' });
42
+ }
43
+ }, [id]);
44
+
45
+ return chatLoading && data.length === 2 ? null : (
46
+ <Flexbox height={'100%'}>
47
+ <Virtuoso
48
+ atBottomStateChange={setAtBottom}
49
+ atBottomThreshold={60 * (mobile ? 2 : 1)}
50
+ computeItemKey={(_, item) => item}
51
+ data={data}
52
+ followOutput={'auto'}
53
+ initialTopMostItemIndex={data?.length - 1}
54
+ itemContent={itemContent}
55
+ overscan={70 * 10}
56
+ ref={virtuosoRef}
57
+ />
58
+ <AutoScroll
59
+ atBottom={atBottom}
60
+ onScrollToBottom={(type) => {
61
+ const virtuoso = virtuosoRef.current;
62
+ switch (type) {
63
+ case 'auto': {
64
+ virtuoso?.scrollToIndex({ align: 'end', behavior: 'auto', index: 'LAST' });
65
+ break;
66
+ }
67
+ case 'click': {
68
+ virtuoso?.scrollToIndex({ align: 'end', behavior: 'smooth', index: 'LAST' });
69
+ break;
70
+ }
71
+ }
72
+ }}
73
+ />
74
+ </Flexbox>
75
+ );
76
+ });
77
+
78
+ export default VirtualizedList;
@@ -1,22 +1,19 @@
1
- import { BackBottom } from '@lobehub/ui';
2
1
  import { createStyles } from 'antd-style';
3
- import { ReactNode, memo, useRef } from 'react';
4
- import { useTranslation } from 'react-i18next';
2
+ import { ReactNode, memo } from 'react';
5
3
  import { Flexbox } from 'react-layout-kit';
6
4
 
7
- import SafeSpacing from '@/components/SafeSpacing';
8
5
  import { useGlobalStore } from '@/store/global';
9
6
  import { settingsSelectors } from '@/store/global/selectors';
10
7
 
11
- import ChatList from './components/ChatList';
12
- import ChatScrollAnchor from './components/ScrollAnchor';
13
8
  import SkeletonList from './components/SkeletonList';
9
+ import ChatList from './components/VirtualizedList';
14
10
  import { useInitConversation } from './hooks/useInitConversation';
15
11
 
16
12
  const useStyles = createStyles(({ css, responsive, stylish, cx }, fontSize: number = 14) =>
17
13
  cx(
18
14
  css`
19
- overflow: hidden scroll;
15
+ position: relative;
16
+ overflow-y: auto;
20
17
  height: 100%;
21
18
 
22
19
  ${responsive.mobile} {
@@ -46,9 +43,7 @@ interface ConversationProps {
46
43
  mobile?: boolean;
47
44
  }
48
45
 
49
- const Conversation = memo<ConversationProps>(({ mobile, chatInput }) => {
50
- const ref = useRef(null);
51
- const { t } = useTranslation('chat');
46
+ const Conversation = memo<ConversationProps>(({ chatInput, mobile }) => {
52
47
  const fontSize = useGlobalStore((s) => settingsSelectors.currentSettings(s).fontSize);
53
48
  const { styles } = useStyles(fontSize);
54
49
 
@@ -56,14 +51,9 @@ const Conversation = memo<ConversationProps>(({ mobile, chatInput }) => {
56
51
  const init = useInitConversation();
57
52
 
58
53
  return (
59
- <Flexbox flex={1} style={{ position: 'relative' }}>
60
- <div style={{ flex: 1, overflow: 'hidden', position: 'relative' }}>
61
- <div className={styles} ref={ref}>
62
- {!mobile && <SafeSpacing />}
63
- {init ? <ChatList /> : <SkeletonList />}
64
- <ChatScrollAnchor />
65
- </div>
66
- <BackBottom target={ref} text={t('backToBottom')} />
54
+ <Flexbox data-id={'conversation'} flex={1}>
55
+ <div className={styles}>
56
+ {init ? <ChatList mobile={mobile} /> : <SkeletonList mobile={mobile} />}
67
57
  </div>
68
58
  {chatInput}
69
59
  </Flexbox>
@@ -28,7 +28,7 @@ export const createI18nNext = (lang?: string) => {
28
28
  init: () =>
29
29
  instance.init({
30
30
  debug: debugMode,
31
- defaultNS: ['error', 'common', 'empty', 'chat'],
31
+ defaultNS: ['error', 'common', 'chat'],
32
32
  detection: {
33
33
  caches: ['cookie'],
34
34
  cookieMinutes: 60 * 24 * COOKIE_CACHE_DAYS,
@@ -65,12 +65,16 @@ export default {
65
65
  confirmRemoveTopic: '即将删除该话题,删除后将不可恢复,请谨慎操作。',
66
66
  confirmRemoveUnstarred: '即将删除未收藏话题,删除后将不可恢复,请谨慎操作。',
67
67
  defaultTitle: '默认话题',
68
+ guide: {
69
+ desc: '点击发送左侧按钮可将当前会话保存为历史话题,并开启新一轮会话',
70
+ title: '话题列表',
71
+ },
68
72
  openNewTopic: '开启新话题',
69
73
  removeAll: '删除全部话题',
70
74
  removeUnstarred: '删除未收藏话题',
71
75
  saveCurrentMessages: '将当前会话保存为话题',
72
76
  searchPlaceholder: '搜索话题...',
73
- title: '话题列表',
77
+ title: '话题',
74
78
  },
75
79
  translate: {
76
80
  action: '翻译',
@@ -1,7 +1,6 @@
1
1
  import tool from '../default/tool';
2
2
  import chat from './chat';
3
3
  import common from './common';
4
- import empty from './empty';
5
4
  import error from './error';
6
5
  import market from './market';
7
6
  import migration from './migration';
@@ -12,7 +11,6 @@ import welcome from './welcome';
12
11
  const resources = {
13
12
  chat,
14
13
  common,
15
- empty,
16
14
  error,
17
15
  market,
18
16
  migration,
@@ -39,6 +39,8 @@ const getMeta = (message: ChatMessage) => {
39
39
  }
40
40
  };
41
41
 
42
+ const currentChatKey = (s: ChatStore) => `${s.activeId}_${s.activeTopicId}`;
43
+
42
44
  // 当前激活的消息列表
43
45
  const currentChats = (s: ChatStore): ChatMessage[] => {
44
46
  if (!s.activeId) return [];
@@ -117,9 +119,13 @@ const getMessageById = (id: string) => (s: ChatStore) => chatHelpers.getMessageB
117
119
 
118
120
  const latestMessage = (s: ChatStore) => currentChats(s).at(-1);
119
121
 
122
+ const currentChatLoadingState = (s: ChatStore) => !s.messagesInit;
123
+
120
124
  export const chatSelectors = {
121
125
  chatsMessageString,
122
126
  currentChatIDsWithGuideMessage,
127
+ currentChatKey,
128
+ currentChatLoadingState,
123
129
  currentChats,
124
130
  currentChatsWithGuideMessage,
125
131
  currentChatsWithHistoryConfig,
package/vercel.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "installCommand": "/bun1/bun install"
2
+ "installCommand": "npx bun@1.0.15 install"
3
3
  }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Klicken Sie auf die Schaltfläche links, um das aktuelle Gespräch als historisches Thema zu speichern und eine neue Gesprächsrunde zu starten",
4
- "title": "Themenliste"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Click the button on the left to save the current conversation as a historical topic and start a new conversation",
4
- "title": "Topic List"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Haz clic en el botón izquierdo para guardar la conversación actual como un tema histórico y comenzar una nueva sesión",
4
- "title": "Lista de temas"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Cliquez sur le bouton à gauche pour enregistrer la conversation actuelle comme un sujet précédent et commencer une nouvelle session",
4
- "title": "Liste des sujets"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Clicca sul pulsante a sinistra per salvare l'attuale sessione come argomento storico e avviare una nuova sessione",
4
- "title": "Elenco argomenti"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "左側のボタンをクリックして、現在の会話を履歴トピックとして保存し、新しい会話を開始します",
4
- "title": "トピックリスト"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "현재 대화를 히스토리 토픽으로 저장하고 새로운 대화를 시작하려면 왼쪽 버튼을 클릭하세요.",
4
- "title": "토픽 목록"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Klik op de knop aan de linkerkant om het huidige gesprek op te slaan als een historisch onderwerp en een nieuw gesprek te starten",
4
- "title": "Onderwerplijst"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Kliknij przycisk po lewej stronie, aby zapisać bieżącą rozmowę jako historię tematu i rozpocząć nową rundę rozmowy",
4
- "title": "Lista tematów"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Clique em enviar no botão esquerdo para salvar a conversa atual como um tópico histórico e iniciar uma nova rodada de conversa",
4
- "title": "Lista de Tópicos"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Нажмите кнопку слева, чтобы сохранить текущий разговор как историческую тему и начать новый разговор",
4
- "title": "Список тем"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Mevcut konuşmayı kaydetmek ve yeni bir konuşma başlatmak için soldaki düğmeye tıklayın.",
4
- "title": "Konular"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "Nhấn vào nút bên trái để lưu cuộc trò chuyện hiện tại như một chủ đề lịch sử và bắt đầu một cuộc trò chuyện mới",
4
- "title": "Danh sách chủ đề"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "点击发送左侧按钮可将当前会话保存为历史话题,并开启新一轮会话",
4
- "title": "话题列表"
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "topic": {
3
- "desc": "點選左側的按鈕,將目前的對話存成歷史主題,並開始新的對話",
4
- "title": "主題清單"
5
- }
6
- }
@@ -1,10 +0,0 @@
1
- import { createStyles } from 'antd-style';
2
-
3
- export const useStyles = createStyles(({ css }) => {
4
- return {
5
- container: css`
6
- position: relative;
7
- margin-top: 24px;
8
- `,
9
- };
10
- });
@@ -1,29 +0,0 @@
1
- import { memo, useEffect } from 'react';
2
- import { useInView } from 'react-intersection-observer';
3
-
4
- import { useChatStore } from '@/store/chat';
5
- import { chatSelectors } from '@/store/chat/selectors';
6
-
7
- import { useAtBottom } from './useAtBottom';
8
-
9
- const ChatScrollAnchor = memo(() => {
10
- const trackVisibility = useChatStore((s) => !!s.chatLoadingId);
11
- const str = useChatStore(chatSelectors.chatsMessageString);
12
-
13
- const isAtBottom = useAtBottom();
14
- const { ref, entry, inView } = useInView({
15
- delay: 100,
16
- rootMargin: '0px 0px -150px 0px',
17
- trackVisibility,
18
- });
19
-
20
- useEffect(() => {
21
- if (isAtBottom && trackVisibility && !inView) {
22
- entry?.target.scrollIntoView({ block: 'start' });
23
- }
24
- }, [inView, entry, isAtBottom, trackVisibility, str]);
25
-
26
- return <div ref={ref} style={{ height: 1, width: '100%' }} />;
27
- });
28
-
29
- export default ChatScrollAnchor;
@@ -1,20 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
-
3
- export const useAtBottom = (offset = 0) => {
4
- const [isAtBottom, setIsAtBottom] = useState(false);
5
-
6
- useEffect(() => {
7
- const handleScroll = () => {
8
- setIsAtBottom(window.innerHeight + window.scrollY >= document.body.offsetHeight - offset);
9
- };
10
-
11
- window.addEventListener('scroll', handleScroll, { passive: true });
12
- handleScroll();
13
-
14
- return () => {
15
- window.removeEventListener('scroll', handleScroll);
16
- };
17
- }, [offset]);
18
-
19
- return isAtBottom;
20
- };
@@ -1,6 +0,0 @@
1
- export default {
2
- topic: {
3
- desc: '点击发送左侧按钮可将当前会话保存为历史话题,并开启新一轮会话',
4
- title: '话题列表',
5
- },
6
- };