chore: update hub progress download, added toaster
This commit is contained in:
parent
2345ff172d
commit
53f5729674
@ -10,10 +10,12 @@ import { abortDownload } from '@/services/models'
|
|||||||
import { getProviders } from '@/services/providers'
|
import { getProviders } from '@/services/providers'
|
||||||
import { DownloadEvent, DownloadState, events } from '@janhq/core'
|
import { DownloadEvent, DownloadState, events } from '@janhq/core'
|
||||||
import { IconX } from '@tabler/icons-react'
|
import { IconX } from '@tabler/icons-react'
|
||||||
import { useCallback, useEffect, useMemo } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
export function DownloadManagement() {
|
export function DownloadManagement() {
|
||||||
const { setProviders } = useModelProvider()
|
const { setProviders } = useModelProvider()
|
||||||
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
|
||||||
const { downloads, updateProgress, removeDownload } = useDownloadStore()
|
const { downloads, updateProgress, removeDownload } = useDownloadStore()
|
||||||
const downloadCount = useMemo(
|
const downloadCount = useMemo(
|
||||||
() => Object.keys(downloads).length,
|
() => Object.keys(downloads).length,
|
||||||
@ -22,7 +24,7 @@ export function DownloadManagement() {
|
|||||||
const downloadProcesses = useMemo(
|
const downloadProcesses = useMemo(
|
||||||
() =>
|
() =>
|
||||||
Object.values(downloads).map((download) => ({
|
Object.values(downloads).map((download) => ({
|
||||||
id: download.id,
|
id: download.name,
|
||||||
name: download.name,
|
name: download.name,
|
||||||
progress: download.progress,
|
progress: download.progress,
|
||||||
current: download.current,
|
current: download.current,
|
||||||
@ -76,6 +78,10 @@ export function DownloadManagement() {
|
|||||||
console.debug('onFileDownloadSuccess', state)
|
console.debug('onFileDownloadSuccess', state)
|
||||||
removeDownload(state.modelId)
|
removeDownload(state.modelId)
|
||||||
getProviders().then(setProviders)
|
getProviders().then(setProviders)
|
||||||
|
toast.success('Download Complete', {
|
||||||
|
id: 'download-complete',
|
||||||
|
description: `The model ${state.modelId} has been downloaded`,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
[removeDownload, setProviders]
|
[removeDownload, setProviders]
|
||||||
)
|
)
|
||||||
@ -107,8 +113,9 @@ export function DownloadManagement() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<>
|
||||||
{downloadCount > 0 && (
|
{downloadCount > 0 && (
|
||||||
|
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<div className="bg-left-panel-fg/10 hover:bg-left-panel-fg/12 p-2 rounded-md my-1 relative border border-left-panel-fg/10 cursor-pointer text-left">
|
<div className="bg-left-panel-fg/10 hover:bg-left-panel-fg/12 p-2 rounded-md my-1 relative border border-left-panel-fg/10 cursor-pointer text-left">
|
||||||
<div className="bg-primary font-bold size-5 rounded-full absolute -top-2 -right-1 flex items-center justify-center text-primary-fg">
|
<div className="bg-primary font-bold size-5 rounded-full absolute -top-2 -right-1 flex items-center justify-center text-primary-fg">
|
||||||
@ -123,12 +130,13 @@ export function DownloadManagement() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
)}
|
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
side="right"
|
side="right"
|
||||||
align="end"
|
align="end"
|
||||||
className="p-0 overflow-hidden text-sm select-none"
|
className="p-0 overflow-hidden text-sm select-none"
|
||||||
sideOffset={6}
|
sideOffset={6}
|
||||||
|
onFocusOutside={(e) => e.preventDefault}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="p-2 py-1.5 bg-main-view-fg/5 border-b border-main-view-fg/6">
|
<div className="p-2 py-1.5 bg-main-view-fg/5 border-b border-main-view-fg/6">
|
||||||
@ -142,20 +150,29 @@ export function DownloadManagement() {
|
|||||||
{download.name}
|
{download.name}
|
||||||
</p>
|
</p>
|
||||||
<div className="shrink-0 flex items-center space-x-0.5">
|
<div className="shrink-0 flex items-center space-x-0.5">
|
||||||
{/* <IconPlayerPauseFilled
|
|
||||||
size={16}
|
|
||||||
className="text-main-view-fg/70 cursor-pointer"
|
|
||||||
title="Pause download"
|
|
||||||
/> */}
|
|
||||||
<IconX
|
<IconX
|
||||||
size={16}
|
size={16}
|
||||||
className="text-main-view-fg/70 cursor-pointer"
|
className="text-main-view-fg/70 cursor-pointer"
|
||||||
title="Cancel download"
|
title="Cancel download"
|
||||||
onClick={() => abortDownload(download.name)}
|
onClick={() => {
|
||||||
|
abortDownload(download.name).then(() => {
|
||||||
|
toast.info('Download Cancelled', {
|
||||||
|
id: 'cancel-download',
|
||||||
|
description:
|
||||||
|
'The download process was cancelled',
|
||||||
|
})
|
||||||
|
if (downloadProcesses.length === 0) {
|
||||||
|
setIsPopoverOpen(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Progress value={download.progress * 100} className="my-2" />
|
<Progress
|
||||||
|
value={download.progress * 100}
|
||||||
|
className="my-2"
|
||||||
|
/>
|
||||||
<p className="text-main-view-fg/60 text-xs">
|
<p className="text-main-view-fg/60 text-xs">
|
||||||
{`${renderGB(download.current)} / ${renderGB(download.total)}`}{' '}
|
{`${renderGB(download.current)} / ${renderGB(download.total)}`}{' '}
|
||||||
GB ({download.progress.toFixed(2)}%)
|
GB ({download.progress.toFixed(2)}%)
|
||||||
@ -166,5 +183,7 @@ export function DownloadManagement() {
|
|||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,8 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu'
|
} from '@/components/ui/dropdown-menu'
|
||||||
import { downloadModel } from '@/services/models'
|
import { downloadModel } from '@/services/models'
|
||||||
|
import { useDownloadStore } from '@/hooks/useDownloadStore'
|
||||||
|
import { Progress } from '@/components/ui/progress'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const Route = createFileRoute(route.hub as any)({
|
export const Route = createFileRoute(route.hub as any)({
|
||||||
@ -77,6 +79,53 @@ function Hub() {
|
|||||||
setSearchValue(e.target.value)
|
setSearchValue(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { downloads } = useDownloadStore()
|
||||||
|
|
||||||
|
const downloadProcesses = useMemo(
|
||||||
|
() =>
|
||||||
|
Object.values(downloads).map((download) => ({
|
||||||
|
id: download.name,
|
||||||
|
name: download.name,
|
||||||
|
progress: download.progress,
|
||||||
|
current: download.current,
|
||||||
|
total: download.total,
|
||||||
|
})),
|
||||||
|
[downloads]
|
||||||
|
)
|
||||||
|
|
||||||
|
interface ModelProps {
|
||||||
|
model: {
|
||||||
|
id: string
|
||||||
|
models: {
|
||||||
|
id: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DownloadButtonPlaceholder = useMemo(() => {
|
||||||
|
return ({ model }: ModelProps) => {
|
||||||
|
const modelId = model.models[0]?.id
|
||||||
|
const isDownloading = downloadProcesses.some((e) => e.id === modelId)
|
||||||
|
const downloadProgress =
|
||||||
|
downloadProcesses.find((e) => e.id === modelId)?.progress || 0
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isDownloading ? (
|
||||||
|
<div className="flex items-center gap-2 w-20">
|
||||||
|
<Progress value={downloadProgress * 100} />
|
||||||
|
<span className="text-xs text-center text-main-view-fg/70">
|
||||||
|
{Math.round(downloadProgress * 100)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Button onClick={() => downloadModel(modelId)}>Download</Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [downloadProcesses])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full">
|
<div className="flex h-full w-full">
|
||||||
<div className="flex flex-col h-full w-full">
|
<div className="flex flex-col h-full w-full">
|
||||||
@ -134,33 +183,24 @@ function Hub() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col pb-2 mb-2 gap-2">
|
<div className="flex flex-col pb-2 mb-2 gap-2">
|
||||||
{filteredModels.map((model) => {
|
{filteredModels.map((model) => (
|
||||||
return (
|
|
||||||
<div key={model.id}>
|
<div key={model.id}>
|
||||||
<Card
|
<Card
|
||||||
header={
|
header={
|
||||||
<div className="flex items-center justify-between gap-x-2">
|
<div className="flex items-center justify-between gap-x-2">
|
||||||
<Link
|
<Link
|
||||||
to={
|
to={`https://huggingface.co/${model.id}` as string}
|
||||||
`https://huggingface.co/${model.id}` as string
|
|
||||||
}
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<h1 className="text-main-view-fg font-medium text-base capitalize truncate">
|
<h1 className="text-main-view-fg font-medium text-base capitalize truncate">
|
||||||
{extractModelName(model.id) || ''}
|
{extractModelName(model.id) || ''}
|
||||||
</h1>
|
</h1>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="shrink-0 space-x-3">
|
<div className="shrink-0 space-x-3 flex items-center">
|
||||||
<span className="text-main-view-fg/70 font-medium text-xs">
|
<span className="text-main-view-fg/70 font-medium text-xs">
|
||||||
{toGigabytes(model.models?.[0]?.size)}
|
{toGigabytes(model.models?.[0]?.size)}
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<DownloadButtonPlaceholder model={model} />
|
||||||
onClick={() =>
|
|
||||||
downloadModel(model.models[0]?.id)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Download
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -177,8 +217,7 @@ function Hub() {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
content={
|
content={
|
||||||
extractDescription(model.metadata.description) ||
|
extractDescription(model.metadata.description) || ''
|
||||||
''
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -222,11 +261,9 @@ function Hub() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{expandedModels[model.id] &&
|
{expandedModels[model.id] && model.models.length > 0 && (
|
||||||
model.models.length > 0 && (
|
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
{model.models.map((variant) => {
|
{model.models.slice(1).map((variant) => (
|
||||||
return (
|
|
||||||
<CardItem
|
<CardItem
|
||||||
key={variant.id}
|
key={variant.id}
|
||||||
title={variant.id}
|
title={variant.id}
|
||||||
@ -236,9 +273,32 @@ function Hub() {
|
|||||||
<p className="text-main-view-fg/70 font-medium text-xs">
|
<p className="text-main-view-fg/70 font-medium text-xs">
|
||||||
{toGigabytes(variant.size)}
|
{toGigabytes(variant.size)}
|
||||||
</p>
|
</p>
|
||||||
|
{(() => {
|
||||||
|
const isDownloading =
|
||||||
|
downloadProcesses.some(
|
||||||
|
(e) => e.id === variant.id
|
||||||
|
)
|
||||||
|
const downloadProgress =
|
||||||
|
downloadProcesses.find(
|
||||||
|
(e) => e.id === variant.id
|
||||||
|
)?.progress || 0
|
||||||
|
|
||||||
|
return isDownloading ? (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center gap-2 w-20">
|
||||||
|
<Progress
|
||||||
|
value={downloadProgress * 100}
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-center text-main-view-fg/70">
|
||||||
|
{Math.round(downloadProgress * 100)}
|
||||||
|
%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
<div
|
<div
|
||||||
className="size-6 cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/10 transition-all duration-200 ease-in-out"
|
className="size-6 cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/10 transition-all duration-200 ease-in-out"
|
||||||
title="Edit All Servers JSON"
|
title="Download model"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
downloadModel(variant.id)
|
downloadModel(variant.id)
|
||||||
}
|
}
|
||||||
@ -248,17 +308,17 @@ function Hub() {
|
|||||||
className="text-main-view-fg/80"
|
className="text-main-view-fg/80"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)
|
))}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)
|
))}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user