Skip to content

Commit cb699a0

Browse files
committed
Enhance ModelDisplay component with infinite scrolling for model list
- Introduced pagination to the ModelDisplay component, allowing users to load models in increments of 50. - Implemented IntersectionObserver to automatically load more models when the user scrolls to the bottom of the list. - Added a loading indicator using CircularProgress to enhance user experience during model loading. - Ensured that the visible model count resets when the model list changes, maintaining a smooth interaction.
1 parent 11d7192 commit cb699a0

File tree

1 file changed

+67
-17
lines changed

1 file changed

+67
-17
lines changed

web/src/components/hugging_face/model_list/ModelDisplay.tsx

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from "react";
2-
import { List, Typography } from "@mui/material";
2+
import { Box, CircularProgress, List, Typography } from "@mui/material";
33
import ModelListItem from "./ModelListItem";
44
import { UnifiedModel } from "../../../stores/ApiTypes";
55
import { useModelManagerStore } from "../../../stores/ModelManagerStore";
@@ -19,6 +19,44 @@ const ModelDisplay: React.FC<ModelDisplayProps> = ({
1919
const { handleShowInExplorer, ollamaBasePath } = useModels();
2020
const downloadStore = useModelDownloadStore();
2121

22+
const PAGE_SIZE = 50;
23+
const [visibleCount, setVisibleCount] = React.useState<number>(PAGE_SIZE);
24+
const sentinelRef = React.useRef<HTMLDivElement | null>(null);
25+
26+
const visibleModels = React.useMemo(
27+
() => models.slice(0, Math.min(visibleCount, models.length)),
28+
[models, visibleCount]
29+
);
30+
31+
const hasMore = visibleModels.length < models.length;
32+
33+
const loadMore = React.useCallback(() => {
34+
if (!hasMore) return;
35+
setVisibleCount((prev) => Math.min(prev + PAGE_SIZE, models.length));
36+
}, [hasMore, models.length]);
37+
38+
// Reset pagination when the models set changes
39+
React.useEffect(() => {
40+
setVisibleCount(PAGE_SIZE);
41+
}, [models]);
42+
43+
// IntersectionObserver to trigger loading more when sentinel enters view
44+
React.useEffect(() => {
45+
const node = sentinelRef.current;
46+
if (!node) return;
47+
const observer = new IntersectionObserver(
48+
(entries) => {
49+
const entry = entries[0];
50+
if (entry.isIntersecting) {
51+
loadMore();
52+
}
53+
},
54+
{ root: null, rootMargin: "200px 0px", threshold: 0 }
55+
);
56+
observer.observe(node);
57+
return () => observer.unobserve(node);
58+
}, [loadMore, visibleModels.length]);
59+
2260
const startDownload = React.useCallback(
2361
(model: UnifiedModel) => {
2462
const repoId = model.repo_id || model.id;
@@ -45,22 +83,34 @@ const ModelDisplay: React.FC<ModelDisplayProps> = ({
4583
const allowShowInExplorer = modelSource === "downloaded";
4684

4785
return (
48-
<List>
49-
{models.map((model: UnifiedModel, idx: number) => (
50-
<ModelListItem
51-
key={`${model.id}-${idx}`}
52-
model={model}
53-
handleModelDelete={allowDelete ? handleDeleteClick : undefined}
54-
onDownload={allowDownload ? () => startDownload(model) : undefined}
55-
handleShowInExplorer={
56-
allowShowInExplorer ? handleShowInExplorer : undefined
57-
}
58-
// hideMissingInfo={modelSource === "recommended"}
59-
showModelStats={modelSource === "recommended"}
60-
ollamaBasePath={ollamaBasePath}
61-
/>
62-
))}
63-
</List>
86+
<>
87+
<List>
88+
{visibleModels.map((model: UnifiedModel, idx: number) => (
89+
<ModelListItem
90+
key={`${model.id}-${idx}`}
91+
model={model}
92+
handleModelDelete={allowDelete ? handleDeleteClick : undefined}
93+
onDownload={allowDownload ? () => startDownload(model) : undefined}
94+
handleShowInExplorer={
95+
allowShowInExplorer ? handleShowInExplorer : undefined
96+
}
97+
// hideMissingInfo={modelSource === "recommended"}
98+
showModelStats={modelSource === "recommended"}
99+
ollamaBasePath={ollamaBasePath}
100+
/>
101+
))}
102+
</List>
103+
<Box
104+
ref={sentinelRef}
105+
sx={{
106+
display: hasMore ? "flex" : "none",
107+
justifyContent: "center",
108+
py: 2
109+
}}
110+
>
111+
<CircularProgress size={20} />
112+
</Box>
113+
</>
64114
);
65115
};
66116

0 commit comments

Comments
 (0)