@lobehub/lobehub 2.0.0-next.66 → 2.0.0-next.67

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,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.67](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.66...v2.0.0-next.67)
6
+
7
+ <sup>Released on **2025-11-16**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Refactor to virtua.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Code refactoring
19
+
20
+ - **misc**: Refactor to virtua, closes [#10151](https://github.com/lobehub/lobe-chat/issues/10151) ([9ffb689](https://github.com/lobehub/lobe-chat/commit/9ffb689))
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
+
5
30
  ## [Version 2.0.0-next.66](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.65...v2.0.0-next.66)
6
31
 
7
32
  <sup>Released on **2025-11-16**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Refactor to virtua."
6
+ ]
7
+ },
8
+ "date": "2025-11-16",
9
+ "version": "2.0.0-next.67"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "features": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.66",
3
+ "version": "2.0.0-next.67",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent 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",
@@ -265,7 +265,7 @@
265
265
  "react-fast-marquee": "^1.6.5",
266
266
  "react-hotkeys-hook": "^5.2.1",
267
267
  "react-i18next": "^15.7.4",
268
- "react-layout-kit": "^2.0.0",
268
+ "react-layout-kit": "^2.0.1",
269
269
  "react-lazy-load": "^4.0.1",
270
270
  "react-pdf": "^9.2.1",
271
271
  "react-responsive": "^10.0.1",
@@ -295,6 +295,7 @@
295
295
  "url-join": "^5.0.0",
296
296
  "use-merge-value": "^1.2.0",
297
297
  "uuid": "^11.1.0",
298
+ "virtua": "^0.47.0",
298
299
  "word-extractor": "^1.0.4",
299
300
  "ws": "^8.18.3",
300
301
  "yaml": "^2.8.1",
@@ -75,6 +75,7 @@ const MainChatItem = memo<ThreadChatItemProps>(({ id, index }) => {
75
75
  endRender={showThread && <Thread id={id} placement={placement} />}
76
76
  id={id}
77
77
  index={index}
78
+ isLatestItem={isLatestItem}
78
79
  />
79
80
  {isLatestItem && <SupervisorThinkingTag />}
80
81
  </>
@@ -4,6 +4,7 @@ import { Icon } from '@lobehub/ui';
4
4
  import { Popover, Tooltip } from 'antd';
5
5
  import { createStyles, useTheme } from 'antd-style';
6
6
  import debug from 'debug';
7
+ import isEqual from 'fast-deep-equal';
7
8
  import { ChevronDown, ChevronUp } from 'lucide-react';
8
9
  import { markdownToTxt } from 'markdown-to-txt';
9
10
  import { memo, useCallback, useMemo, useState, useSyncExternalStore } from 'react';
@@ -11,13 +12,13 @@ import { useTranslation } from 'react-i18next';
11
12
  import { Flexbox } from 'react-layout-kit';
12
13
 
13
14
  import {
14
- getVirtuosoActiveIndex,
15
- getVirtuosoGlobalRef,
16
- subscribeVirtuosoActiveIndex,
17
- subscribeVirtuosoGlobalRef,
15
+ getVirtuaActiveIndex,
16
+ getVirtuaGlobalRef,
17
+ subscribeVirtuaActiveIndex,
18
+ subscribeVirtuaGlobalRef,
18
19
  } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
19
20
  import { useChatStore } from '@/store/chat';
20
- import { chatSelectors } from '@/store/chat/selectors';
21
+ import { displayMessageSelectors } from '@/store/chat/selectors';
21
22
 
22
23
  const log = debug('lobe-react:chat-minimap');
23
24
 
@@ -194,21 +195,17 @@ interface MinimapIndicator {
194
195
  width: number;
195
196
  }
196
197
 
197
- const ChatMinimap = () => {
198
+ const ChatMinimap = memo(() => {
198
199
  const { t } = useTranslation('chat');
199
200
  const { styles, cx } = useStyles();
200
201
  const [isHovered, setIsHovered] = useState(false);
201
- const virtuosoRef = useSyncExternalStore(
202
- subscribeVirtuosoGlobalRef,
203
- getVirtuosoGlobalRef,
204
- () => null,
205
- );
202
+ const virtuaRef = useSyncExternalStore(subscribeVirtuaGlobalRef, getVirtuaGlobalRef, () => null);
206
203
  const activeIndex = useSyncExternalStore(
207
- subscribeVirtuosoActiveIndex,
208
- getVirtuosoActiveIndex,
204
+ subscribeVirtuaActiveIndex,
205
+ getVirtuaActiveIndex,
209
206
  () => null,
210
207
  );
211
- const messages = useChatStore(chatSelectors.mainDisplayChats);
208
+ const messages = useChatStore(displayMessageSelectors.mainAIChats, isEqual);
212
209
 
213
210
  const theme = useTheme();
214
211
 
@@ -247,20 +244,19 @@ const ChatMinimap = () => {
247
244
 
248
245
  const handleJump = useCallback(
249
246
  (virtIndex: number) => {
250
- virtuosoRef?.current?.scrollToIndex({
247
+ virtuaRef?.current?.scrollToIndex(virtIndex, {
251
248
  align: 'start',
252
- behavior: 'smooth',
253
- index: virtIndex,
254
249
  // The current index detection will be off by 1, so we need to add 1 here
255
250
  offset: 6,
251
+ smooth: true,
256
252
  });
257
253
  },
258
- [virtuosoRef],
254
+ [virtuaRef],
259
255
  );
260
256
 
261
257
  const handleStep = useCallback(
262
258
  (direction: 'prev' | 'next') => {
263
- const ref = virtuosoRef?.current;
259
+ const ref = virtuaRef?.current;
264
260
  if (!ref || indicators.length === 0) return;
265
261
 
266
262
  let targetPosition: number;
@@ -303,14 +299,13 @@ const ChatMinimap = () => {
303
299
 
304
300
  if (!targetIndicator) return;
305
301
 
306
- ref.scrollToIndex({
302
+ ref.scrollToIndex(targetIndicator.virtuosoIndex, {
307
303
  align: 'start',
308
- behavior: 'smooth',
309
- index: targetIndicator.virtuosoIndex,
310
304
  offset: 6,
305
+ smooth: true,
311
306
  });
312
307
  },
313
- [activeIndex, activeIndicatorPosition, indicators, virtuosoRef],
308
+ [activeIndex, activeIndicatorPosition, indicators, virtuaRef],
314
309
  );
315
310
 
316
311
  if (indicators.length <= MIN_MESSAGES) return null;
@@ -353,9 +348,7 @@ const ChatMinimap = () => {
353
348
  aria-label={t('minimap.jumpToMessage', { index: position + 1 })}
354
349
  className={styles.indicator}
355
350
  onClick={() => handleJump(virtuosoIndex)}
356
- style={{
357
- width,
358
- }}
351
+ style={{ width }}
359
352
  type={'button'}
360
353
  >
361
354
  <div
@@ -382,6 +375,6 @@ const ChatMinimap = () => {
382
375
  </Flexbox>
383
376
  </Flexbox>
384
377
  );
385
- };
378
+ });
386
379
 
387
- export default memo(ChatMinimap);
380
+ export default ChatMinimap;
@@ -86,6 +86,10 @@ export const useStyles = createStyles(
86
86
  padding-block: 24px 12px;
87
87
  padding-inline: 12px;
88
88
 
89
+ @supports (content-visibility: auto) {
90
+ contain-intrinsic-size: auto 100lvh;
91
+ }
92
+
89
93
  time {
90
94
  display: inline-block;
91
95
  white-space: nowrap;
@@ -6,7 +6,7 @@ import { memo, use, useCallback, useContext, useMemo, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
8
  import ShareMessageModal from '@/features/Conversation/components/ShareMessageModal';
9
- import { VirtuosoContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
9
+ import { VirtuaContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
10
10
  import { useChatStore } from '@/store/chat';
11
11
  import { messageStateSelectors, threadSelectors } from '@/store/chat/selectors';
12
12
  import { useSessionStore } from '@/store/session';
@@ -90,7 +90,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
90
90
  s.toggleMessageCollapsed,
91
91
  ]);
92
92
  const { message } = App.useApp();
93
- const virtuosoRef = use(VirtuosoContext);
93
+ const virtuaRef = use(VirtuaContext);
94
94
 
95
95
  const onActionClick = useCallback(
96
96
  async (action: ActionIconGroupEvent) => {
@@ -98,7 +98,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
98
98
  case 'edit': {
99
99
  toggleMessageEditing(id, true);
100
100
 
101
- virtuosoRef?.current?.scrollIntoView({ align: 'start', behavior: 'auto', index });
101
+ virtuaRef?.current?.scrollToIndex(index, { align: 'start' });
102
102
  }
103
103
  }
104
104
  if (!data) return;