diff --git a/web/containers/BlankState/index.tsx b/web/containers/BlankState/index.tsx
new file mode 100644
index 000000000..bcfa2b306
--- /dev/null
+++ b/web/containers/BlankState/index.tsx
@@ -0,0 +1,24 @@
+import { ReactNode } from 'react'
+
+import LogoMark from '@/containers/Brand/Logo/Mark'
+
+type Props = {
+ title: string
+ description?: string
+ action?: ReactNode
+}
+
+const BlankState = ({ title, description, action }: Props) => {
+ return (
+
+
+
{title}
+ {description && (
+
{description}
+ )}
+ {action && action}
+
+ )
+}
+
+export default BlankState
diff --git a/web/screens/Hub/index.tsx b/web/screens/Hub/index.tsx
index 190efa136..37adb717c 100644
--- a/web/screens/Hub/index.tsx
+++ b/web/screens/Hub/index.tsx
@@ -7,6 +7,7 @@ import { ScrollArea, Button, Select } from '@janhq/joi'
import { useAtomValue, useSetAtom } from 'jotai'
import { UploadIcon } from 'lucide-react'
+import BlankState from '@/containers/BlankState'
import CenterPanelContainer from '@/containers/CenterPanelContainer'
import ModelSearch from '@/containers/ModelSearch'
@@ -92,15 +93,19 @@ const HubScreen = () => {
-
-
+ {!filteredModels.length ? (
+
+ ) : (
+
+
+ )}
diff --git a/web/screens/Settings/MyModels/index.tsx b/web/screens/Settings/MyModels/index.tsx
index 80402a548..8dafd6e20 100644
--- a/web/screens/Settings/MyModels/index.tsx
+++ b/web/screens/Settings/MyModels/index.tsx
@@ -18,6 +18,7 @@ import {
import { twMerge } from 'tailwind-merge'
+import BlankState from '@/containers/BlankState'
import ModelSearch from '@/containers/ModelSearch'
import SetupRemoteModel from '@/containers/SetupRemoteModel'
@@ -183,72 +184,80 @@ const MyModels = () => {
- {groupByEngine.map((engine, i) => {
- const engineLogo = getLogoEngine(engine as InferenceEngine)
- const showModel = showEngineListModel.includes(engine)
- const onClickChevron = () => {
- if (showModel) {
- setShowEngineListModel((prev) =>
- prev.filter((item) => item !== engine)
- )
- } else {
- setShowEngineListModel((prev) => [...prev, engine])
+ {!groupByEngine.length ? (
+
+
+
+ ) : (
+ groupByEngine.map((engine, i) => {
+ const engineLogo = getLogoEngine(engine as InferenceEngine)
+ const showModel = showEngineListModel.includes(engine)
+ const onClickChevron = () => {
+ if (showModel) {
+ setShowEngineListModel((prev) =>
+ prev.filter((item) => item !== engine)
+ )
+ } else {
+ setShowEngineListModel((prev) => [...prev, engine])
+ }
}
- }
- return (
-
-
-
- {engineLogo && (
-
- )}
-
- {getTitleByEngine(engine)}
-
+ return (
+
+
+
+ {engineLogo && (
+
+ )}
+
+ {getTitleByEngine(engine)}
+
+
+
+ {!localEngines.includes(engine) && (
+
+ )}
+ {!showModel ? (
+
+ ) : (
+
+ )}
+
-
- {!localEngines.includes(engine) && (
-
- )}
- {!showModel ? (
-
- ) : (
-
- )}
+
+ {filteredDownloadedModels
+ ? filteredDownloadedModels
+ .filter((x) => x.engine === engine)
+ .map((model) => {
+ if (!showModel) return null
+ return (
+
+ )
+ })
+ : null}
-
- {filteredDownloadedModels
- ? filteredDownloadedModels
- .filter((x) => x.engine === engine)
- .map((model) => {
- if (!showModel) return null
- return
- })
- : null}
-
-
- )
- })}
+ )
+ })
+ )}