Relayout hub screen
This commit is contained in:
parent
5d5139ad20
commit
47c15fd550
@ -183,6 +183,11 @@ export interface Model {
|
|||||||
*/
|
*/
|
||||||
version: number;
|
version: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The format of the model.
|
||||||
|
*/
|
||||||
|
format: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The model download source. It can be an external url or a local filepath.
|
* The model download source. It can be an external url or a local filepath.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -10,6 +10,7 @@ const badgeVariants = cva('badge', {
|
|||||||
secondary: 'badge-secondary',
|
secondary: 'badge-secondary',
|
||||||
danger: 'badge-danger',
|
danger: 'badge-danger',
|
||||||
outline: 'badge-outline',
|
outline: 'badge-outline',
|
||||||
|
pink: 'badge-pink',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
|
|||||||
@ -2,7 +2,11 @@
|
|||||||
@apply focus:ring-ring border-border inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2;
|
@apply focus:ring-ring border-border inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2;
|
||||||
|
|
||||||
&-primary {
|
&-primary {
|
||||||
@apply bg-primary text-primary-foreground hover:bg-primary/80 border-transparent;
|
@apply border-transparent bg-blue-100 text-blue-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-pink {
|
||||||
|
@apply border-transparent bg-pink-100 text-pink-700;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-success {
|
&-success {
|
||||||
@ -14,7 +18,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-danger {
|
&-danger {
|
||||||
@apply bg-danger text-danger-foreground hover:bg-danger/80 border-transparent;
|
@apply border-transparent bg-red-100 text-red-700;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-outline {
|
&-outline {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ const buttonVariants = cva('btn', {
|
|||||||
danger: 'btn-danger',
|
danger: 'btn-danger',
|
||||||
outline: 'btn-outline',
|
outline: 'btn-outline',
|
||||||
secondary: 'btn-secondary',
|
secondary: 'btn-secondary',
|
||||||
|
secondaryBlue: 'btn-secondary-blue',
|
||||||
ghost: 'btn-ghost',
|
ghost: 'btn-ghost',
|
||||||
success: 'btn-success',
|
success: 'btn-success',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
.btn {
|
.btn {
|
||||||
@apply inline-flex items-center justify-center whitespace-nowrap rounded-md font-semibold transition-colors;
|
@apply inline-flex items-center justify-center whitespace-nowrap rounded-lg font-semibold transition-colors;
|
||||||
@apply focus-visible:ring-ring cursor-pointer focus-visible:outline-none focus-visible:ring-1;
|
@apply focus-visible:ring-ring cursor-pointer focus-visible:outline-none focus-visible:ring-1;
|
||||||
@apply disabled:pointer-events-none disabled:opacity-50;
|
@apply disabled:pointer-events-none disabled:opacity-50;
|
||||||
|
|
||||||
@ -7,6 +7,10 @@
|
|||||||
@apply bg-primary hover:bg-primary/90 text-white;
|
@apply bg-primary hover:bg-primary/90 text-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-secondary-blue {
|
||||||
|
@apply bg-blue-200 text-blue-900 hover:bg-blue-500/80;
|
||||||
|
}
|
||||||
|
|
||||||
&-danger {
|
&-danger {
|
||||||
@apply bg-danger text-danger-foreground hover:bg-danger/90;
|
@apply bg-danger text-danger-foreground hover:bg-danger/90;
|
||||||
}
|
}
|
||||||
@ -62,6 +66,9 @@
|
|||||||
&.btn-secondary {
|
&.btn-secondary {
|
||||||
@apply bg-secondary hover:bg-secondary/80;
|
@apply bg-secondary hover:bg-secondary/80;
|
||||||
}
|
}
|
||||||
|
&.btn-secondary-blue {
|
||||||
|
@apply bg-blue-200 text-blue-900 hover:bg-blue-200/80;
|
||||||
|
}
|
||||||
&.btn-danger {
|
&.btn-danger {
|
||||||
@apply bg-danger hover:bg-danger/90;
|
@apply bg-danger hover:bg-danger/90;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
.select {
|
.select {
|
||||||
@apply ring-offset-background placeholder:text-muted-foreground border-border flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border bg-transparent px-3 py-2 text-sm shadow-sm disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1;
|
@apply placeholder:text-muted-foreground border-border flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border bg-transparent px-3 py-2 text-sm shadow-sm disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1;
|
||||||
|
|
||||||
&-caret {
|
&-caret {
|
||||||
@apply h-4 w-4 opacity-50;
|
@apply h-4 w-4 opacity-50;
|
||||||
@ -18,7 +18,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
@apply hover:bg-secondary relative my-1 block w-full cursor-pointer select-none items-center rounded-sm px-4 py-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50;
|
@apply hover:bg-secondary relative my-1 block w-full cursor-pointer select-none items-center rounded-sm px-4 py-2 text-sm data-[disabled]:pointer-events-none data-[disabled]:opacity-50;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-trigger-viewport {
|
&-trigger-viewport {
|
||||||
|
|||||||
@ -48,8 +48,8 @@ export default function CardSidebar({
|
|||||||
>
|
>
|
||||||
<ChevronDownIcon
|
<ChevronDownIcon
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'h-5 w-5 flex-none rotate-180 text-gray-400',
|
'h-5 w-5 flex-none text-gray-400',
|
||||||
show && 'rotate-0'
|
show && 'rotate-180'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<span className="font-bold">{title}</span>
|
<span className="font-bold">{title}</span>
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
ModalHeader,
|
ModalHeader,
|
||||||
Button,
|
Button,
|
||||||
ModalTitle,
|
ModalTitle,
|
||||||
|
Progress,
|
||||||
} from '@janhq/uikit'
|
} from '@janhq/uikit'
|
||||||
|
|
||||||
import { atom, useAtomValue } from 'jotai'
|
import { atom, useAtomValue } from 'jotai'
|
||||||
@ -21,7 +22,6 @@ import { useDownloadState } from '@/hooks/useDownloadState'
|
|||||||
import { formatDownloadPercentage } from '@/utils/converter'
|
import { formatDownloadPercentage } from '@/utils/converter'
|
||||||
|
|
||||||
import { extensionManager } from '@/extension'
|
import { extensionManager } from '@/extension'
|
||||||
import { downloadingModelsAtom } from '@/helpers/atoms/Model.atom'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
model: Model
|
model: Model
|
||||||
@ -46,7 +46,20 @@ export default function ModalCancelDownload({ model, isFromList }: Props) {
|
|||||||
{cancelText}
|
{cancelText}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button>{cancelText}</Button>
|
<Button themes="secondaryBlue">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<span className="inline-block">Cancel</span>
|
||||||
|
<Progress
|
||||||
|
className="inline-block h-2 w-[80px] bg-blue-100"
|
||||||
|
value={
|
||||||
|
formatDownloadPercentage(downloadState?.percent, {
|
||||||
|
hidePercentage: true,
|
||||||
|
}) as number
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span>{formatDownloadPercentage(downloadState.percent)}</span>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ModalTrigger>
|
</ModalTrigger>
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable react/display-name */
|
/* eslint-disable react/display-name */
|
||||||
|
|
||||||
import { forwardRef } from 'react'
|
import { forwardRef, useState } from 'react'
|
||||||
|
|
||||||
import { Model } from '@janhq/core'
|
import { Model } from '@janhq/core'
|
||||||
import { Badge } from '@janhq/uikit'
|
import { Badge } from '@janhq/uikit'
|
||||||
@ -12,51 +12,55 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ExploreModelItem = forwardRef<HTMLDivElement, Props>(({ model }, ref) => {
|
const ExploreModelItem = forwardRef<HTMLDivElement, Props>(({ model }, ref) => {
|
||||||
|
const [open, setOpen] = useState('')
|
||||||
|
|
||||||
|
const handleToggle = () => {
|
||||||
|
if (open === model.id) {
|
||||||
|
setOpen('')
|
||||||
|
} else {
|
||||||
|
setOpen(model.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="mb-6 flex flex-col overflow-hidden rounded-xl border border-border bg-background/60"
|
className="mb-6 flex flex-col overflow-hidden rounded-xl border border-border bg-background/60"
|
||||||
>
|
>
|
||||||
<ExploreModelItemHeader model={model} />
|
<ExploreModelItemHeader
|
||||||
<div className="flex flex-col p-4">
|
model={model}
|
||||||
<div className="mb-4 flex flex-col gap-1">
|
onClick={handleToggle}
|
||||||
|
open={open}
|
||||||
|
/>
|
||||||
|
{open === model.id && (
|
||||||
|
<div className="flex">
|
||||||
|
<div className="flex w-full flex-col border-t border-border p-4 ">
|
||||||
|
<div className="mb-6 flex flex-col gap-1">
|
||||||
<span className="font-semibold">About</span>
|
<span className="font-semibold">About</span>
|
||||||
<p>{model.description}</p>
|
<p className="text-muted-foreground">
|
||||||
|
{model.description || '-'}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex space-x-10">
|
||||||
<div className="mb-4 flex space-x-6 border-b border-border pb-4">
|
|
||||||
<div>
|
<div>
|
||||||
<span className="font-semibold">Author</span>
|
<span className="font-semibold text-muted-foreground">
|
||||||
<p className="mt-1 font-medium">{model.metadata.author}</p>
|
Author
|
||||||
|
</span>
|
||||||
|
<p className="mt-2 font-medium">{model.metadata.author}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="mb-1 font-semibold">Compatibility</span>
|
<span className="mb-1 font-semibold text-muted-foreground">
|
||||||
<div className="mt-1 flex gap-2">
|
Model ID
|
||||||
{/* <Badge
|
</span>
|
||||||
themes="secondary"
|
<p className="mt-2 font-medium">{model.id}</p>
|
||||||
className="line-clamp-1 lg:line-clamp-none"
|
|
||||||
title={`${toGigabytes(
|
|
||||||
model.metadata.maxRamRequired // TODO: check this
|
|
||||||
)} RAM required`}
|
|
||||||
>
|
|
||||||
{toGigabytes(model.metadata.maxRamRequired)} RAM required
|
|
||||||
</Badge> */}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-3 items-center gap-4">
|
|
||||||
<div>
|
|
||||||
<span className="font-semibold">Version</span>
|
|
||||||
<div className="mt-2 flex space-x-2">
|
|
||||||
<Badge themes="outline">v{model.version}</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="font-semibold">Tags</span>
|
<span className="mb-1 font-semibold text-muted-foreground">
|
||||||
|
Tags
|
||||||
|
</span>
|
||||||
<div className="mt-2 flex space-x-2">
|
<div className="mt-2 flex space-x-2">
|
||||||
{model.metadata.tags.map((tag, i) => (
|
{model.metadata.tags.map((tag, i) => (
|
||||||
<Badge key={i} themes="outline">
|
<Badge key={i} themes={i === 0 ? 'primary' : 'pink'}>
|
||||||
{tag}
|
{tag}
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
@ -64,6 +68,22 @@ const ExploreModelItem = forwardRef<HTMLDivElement, Props>(({ model }, ref) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="w-48 flex-shrink-0 border-l border-t border-border p-4">
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold text-muted-foreground">
|
||||||
|
Format
|
||||||
|
</span>
|
||||||
|
<p className="mt-2 font-medium uppercase">{model.format}</p>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<span className="font-semibold text-muted-foreground">
|
||||||
|
Compatibility
|
||||||
|
</span>
|
||||||
|
<p className="mt-2 font-medium">-</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,16 +1,20 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { useCallback, useMemo, useState } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
|
|
||||||
import { Model } from '@janhq/core'
|
import { Model } from '@janhq/core'
|
||||||
import { Badge, Button } from '@janhq/uikit'
|
import { Badge, Button } from '@janhq/uikit'
|
||||||
|
|
||||||
import { atom, useAtomValue } from 'jotai'
|
import { atom, useAtomValue } from 'jotai'
|
||||||
|
|
||||||
|
import { ChevronDownIcon } from 'lucide-react'
|
||||||
|
|
||||||
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
|
||||||
import ModalCancelDownload from '@/containers/ModalCancelDownload'
|
import ModalCancelDownload from '@/containers/ModalCancelDownload'
|
||||||
|
|
||||||
import { MainViewState } from '@/constants/screens'
|
import { MainViewState } from '@/constants/screens'
|
||||||
|
|
||||||
import { ModelPerformance, TagType } from '@/constants/tagType'
|
// import { ModelPerformance, TagType } from '@/constants/tagType'
|
||||||
|
|
||||||
import useDownloadModel from '@/hooks/useDownloadModel'
|
import useDownloadModel from '@/hooks/useDownloadModel'
|
||||||
import { useDownloadState } from '@/hooks/useDownloadState'
|
import { useDownloadState } from '@/hooks/useDownloadState'
|
||||||
@ -21,18 +25,20 @@ import { toGigabytes } from '@/utils/converter'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
model: Model
|
model: Model
|
||||||
|
onClick: () => void
|
||||||
|
open: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExploreModelItemHeader: React.FC<Props> = ({ model }) => {
|
const ExploreModelItemHeader: React.FC<Props> = ({ model, onClick, open }) => {
|
||||||
console.log(model)
|
|
||||||
const { downloadModel } = useDownloadModel()
|
const { downloadModel } = useDownloadModel()
|
||||||
const { downloadedModels } = useGetDownloadedModels()
|
const { downloadedModels } = useGetDownloadedModels()
|
||||||
const { modelDownloadStateAtom, downloadStates } = useDownloadState()
|
const { modelDownloadStateAtom, downloadStates } = useDownloadState()
|
||||||
const [title, setTitle] = useState<string>('Recommended')
|
// const [title, setTitle] = useState<string>('Recommended')
|
||||||
|
|
||||||
|
// const [performanceTag, setPerformanceTag] = useState<TagType>(
|
||||||
|
// ModelPerformance.PerformancePositive
|
||||||
|
// )
|
||||||
|
|
||||||
const [performanceTag, setPerformanceTag] = useState<TagType>(
|
|
||||||
ModelPerformance.PerformancePositive
|
|
||||||
)
|
|
||||||
const downloadAtom = useMemo(
|
const downloadAtom = useMemo(
|
||||||
() => atom((get) => get(modelDownloadStateAtom)[model.id]),
|
() => atom((get) => get(modelDownloadStateAtom)[model.id]),
|
||||||
[model.id]
|
[model.id]
|
||||||
@ -48,18 +54,14 @@ const ExploreModelItemHeader: React.FC<Props> = ({ model }) => {
|
|||||||
const isDownloaded = downloadedModels.find((md) => md.id === model.id) != null
|
const isDownloaded = downloadedModels.find((md) => md.id === model.id) != null
|
||||||
|
|
||||||
let downloadButton = (
|
let downloadButton = (
|
||||||
<Button onClick={() => onDownloadClick()}>
|
<Button onClick={() => onDownloadClick()}>Download</Button>
|
||||||
{model.metadata.size
|
|
||||||
? `Download (${toGigabytes(model.metadata.size)})`
|
|
||||||
: 'Download'}
|
|
||||||
</Button>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isDownloaded) {
|
if (isDownloaded) {
|
||||||
downloadButton = (
|
downloadButton = (
|
||||||
<Button
|
<Button
|
||||||
themes="success"
|
themes="success"
|
||||||
className="min-w-[80px]"
|
className="min-w-[98px]"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMainViewState(MainViewState.MyModels)
|
setMainViewState(MainViewState.MyModels)
|
||||||
}}
|
}}
|
||||||
@ -73,30 +75,42 @@ const ExploreModelItemHeader: React.FC<Props> = ({ model }) => {
|
|||||||
downloadButton = <ModalCancelDownload model={model} />
|
downloadButton = <ModalCancelDownload model={model} />
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderBadge = (performance: TagType) => {
|
// const renderBadge = (performance: TagType) => {
|
||||||
switch (performance) {
|
// switch (performance) {
|
||||||
case ModelPerformance.PerformancePositive:
|
// case ModelPerformance.PerformancePositive:
|
||||||
return <Badge themes="success">{title}</Badge>
|
// return <Badge themes="success">{title}</Badge>
|
||||||
|
|
||||||
case ModelPerformance.PerformanceNeutral:
|
// case ModelPerformance.PerformanceNeutral:
|
||||||
return <Badge themes="secondary">{title}</Badge>
|
// return <Badge themes="secondary">{title}</Badge>
|
||||||
|
|
||||||
case ModelPerformance.PerformanceNegative:
|
// case ModelPerformance.PerformanceNegative:
|
||||||
return <Badge themes="danger">{title}</Badge>
|
// return <Badge themes="danger">{title}</Badge>
|
||||||
|
|
||||||
default:
|
// default:
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between rounded-t-md border-b border-border bg-background/50 px-4 py-2">
|
<div
|
||||||
|
className="flex cursor-pointer items-center justify-between rounded-t-md bg-background/50 px-4 py-4"
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="font-medium">{model.name}</span>
|
<span className="font-bold">{model.name}</span>
|
||||||
|
<Badge>{model.metadata.tags[0]}</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-x-2">
|
<div className="inline-flex items-center space-x-2">
|
||||||
{performanceTag && renderBadge(performanceTag)}
|
<span className="font-semibold text-muted-foreground">
|
||||||
|
{toGigabytes(model.metadata.size)}
|
||||||
|
</span>
|
||||||
{downloadButton}
|
{downloadButton}
|
||||||
|
<ChevronDownIcon
|
||||||
|
className={twMerge(
|
||||||
|
'h-5 w-5 flex-none text-gray-400',
|
||||||
|
open === model.id && 'rotate-180'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -6,10 +6,14 @@ type Props = {
|
|||||||
models: Model[]
|
models: Model[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExploreModelList: React.FC<Props> = ({ models }) => (
|
const ExploreModelList: React.FC<Props> = ({ models }) => {
|
||||||
|
return (
|
||||||
<div className="relative h-full w-full flex-shrink-0">
|
<div className="relative h-full w-full flex-shrink-0">
|
||||||
{models?.map((model) => <ExploreModelItem key={model.id} model={model} />)}
|
{models
|
||||||
|
?.sort((a, b) => a.metadata.size - b.metadata.size)
|
||||||
|
?.map((model) => <ExploreModelItem key={model.id} model={model} />)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default ExploreModelList
|
export default ExploreModelList
|
||||||
|
|||||||
@ -1,6 +1,19 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
import { Input, ScrollArea } from '@janhq/uikit'
|
import {
|
||||||
|
Input,
|
||||||
|
ScrollArea,
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
TooltipArrow,
|
||||||
|
Select,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
} from '@janhq/uikit'
|
||||||
|
|
||||||
import { SearchIcon } from 'lucide-react'
|
import { SearchIcon } from 'lucide-react'
|
||||||
|
|
||||||
@ -12,12 +25,29 @@ import Loader from '@/containers/Loader'
|
|||||||
|
|
||||||
import { useGetConfiguredModels } from '@/hooks/useGetConfiguredModels'
|
import { useGetConfiguredModels } from '@/hooks/useGetConfiguredModels'
|
||||||
|
|
||||||
|
import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels'
|
||||||
|
|
||||||
import ExploreModelList from './ExploreModelList'
|
import ExploreModelList from './ExploreModelList'
|
||||||
|
|
||||||
const ExploreModelsScreen = () => {
|
const ExploreModelsScreen = () => {
|
||||||
const { loading, models } = useGetConfiguredModels()
|
const { loading, models } = useGetConfiguredModels()
|
||||||
const [searchValue, setsearchValue] = useState('')
|
const [searchValue, setsearchValue] = useState('')
|
||||||
const [tabActive, setTabActive] = useState('Model')
|
const [tabActive, setTabActive] = useState('Model')
|
||||||
|
const { downloadedModels } = useGetDownloadedModels()
|
||||||
|
const [sortSelected, setSortSelected] = useState('All Model')
|
||||||
|
const sortMenu = ['All Model', 'Downloaded']
|
||||||
|
|
||||||
|
const filteredModels = models.filter((x) => {
|
||||||
|
if (sortSelected === 'Downloaded') {
|
||||||
|
return (
|
||||||
|
x.name.toLowerCase().includes(searchValue.toLowerCase()) &&
|
||||||
|
downloadedModels.some((y) => y.id === x.id)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return x.name.toLowerCase().includes(searchValue.toLowerCase())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (loading) return <Loader description="loading ..." />
|
if (loading) return <Loader description="loading ..." />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -52,23 +82,53 @@ const ExploreModelsScreen = () => {
|
|||||||
onClick={() => setTabActive('Model')}
|
onClick={() => setTabActive('Model')}
|
||||||
>
|
>
|
||||||
<Code2Icon size={20} className="text-muted-foreground" />
|
<Code2Icon size={20} className="text-muted-foreground" />
|
||||||
<span>Model</span>
|
<span className="font-semibold">Model</span>
|
||||||
</div>
|
</div>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'pointer-events-none flex cursor-pointer items-center space-x-2 px-3 py-2',
|
'pointer-events-none flex cursor-pointer items-center space-x-2 px-3 py-2 text-muted-foreground',
|
||||||
tabActive === 'Assistant' && 'bg-secondary'
|
tabActive === 'Assistant' && 'bg-secondary'
|
||||||
)}
|
)}
|
||||||
onClick={() => setTabActive('Assistant')}
|
onClick={() => setTabActive('Assistant')}
|
||||||
>
|
>
|
||||||
<UserIcon size={20} className="text-muted-foreground" />
|
<UserIcon size={20} className="text-muted-foreground" />
|
||||||
<span>Assistant</span>
|
<span className="font-semibold">Assistant</span>
|
||||||
</div>
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="top" sideOffset={10}>
|
||||||
|
<span className="font-bold">Coming Soon</span>
|
||||||
|
<TooltipArrow />
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
value={sortSelected}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
setSortSelected(value)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-[200px]">
|
||||||
|
<SelectValue placeholder="Sort By"></SelectValue>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent className="right-0 block w-full min-w-[200px] pr-0">
|
||||||
|
<SelectGroup>
|
||||||
|
{sortMenu.map((x, i) => {
|
||||||
|
return (
|
||||||
|
<SelectItem key={i} value={x}>
|
||||||
|
<span className="line-clamp-1 block">{x}</span>
|
||||||
|
</SelectItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<ExploreModelList models={models} />
|
<ExploreModelList models={filteredModels} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user