chore: allow users to import hf repo (#5103)
* chore: allow users to input HF repo id / url * chore: allow users to search HuggingFace models * chore: normalize input * chore: normalize input from FE * chore: clean up * chore: clean up * fix: conflict * fix: model name from metada instead id * chore: enable ryhype raw for desc card hub * fix: broken link * chore: remove log --------- Co-authored-by: Faisal Amir <urmauur@gmail.com>
This commit is contained in:
parent
b8de48c9e9
commit
64f5703461
@ -36,7 +36,7 @@ export function CardItem({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<h1 className="font-medium">{title}</h1>
|
<h1 className="font-medium line-clamp-1">{title}</h1>
|
||||||
{description && (
|
{description && (
|
||||||
<span className="text-main-view-fg/70 leading-normal">
|
<span className="text-main-view-fg/70 leading-normal">
|
||||||
{description}
|
{description}
|
||||||
|
|||||||
@ -14,15 +14,18 @@ import { cn } from '@/lib/utils'
|
|||||||
import { useCodeblock } from '@/hooks/useCodeblock'
|
import { useCodeblock } from '@/hooks/useCodeblock'
|
||||||
import 'katex/dist/katex.min.css'
|
import 'katex/dist/katex.min.css'
|
||||||
import { IconCopy, IconCopyCheck } from '@tabler/icons-react'
|
import { IconCopy, IconCopyCheck } from '@tabler/icons-react'
|
||||||
|
import rehypeRaw from 'rehype-raw'
|
||||||
|
|
||||||
interface MarkdownProps {
|
interface MarkdownProps {
|
||||||
content: string
|
content: string
|
||||||
className?: string
|
className?: string
|
||||||
components?: Components
|
components?: Components
|
||||||
|
enableRawHtml?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function RenderMarkdownComponent({
|
function RenderMarkdownComponent({
|
||||||
content,
|
content,
|
||||||
|
enableRawHtml,
|
||||||
className,
|
className,
|
||||||
components,
|
components,
|
||||||
}: MarkdownProps) {
|
}: MarkdownProps) {
|
||||||
@ -147,7 +150,9 @@ function RenderMarkdownComponent({
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// Memoize the rehypePlugins to prevent unnecessary re-renders
|
// Memoize the rehypePlugins to prevent unnecessary re-renders
|
||||||
const rehypePlugins = useMemo(() => [rehypeKatex], [])
|
const rehypePlugins = useMemo(() => {
|
||||||
|
return enableRawHtml ? [rehypeKatex, rehypeRaw] : [rehypeKatex]
|
||||||
|
}, [enableRawHtml])
|
||||||
|
|
||||||
// Merge custom components with default components
|
// Merge custom components with default components
|
||||||
const mergedComponents = useMemo(
|
const mergedComponents = useMemo(
|
||||||
|
|||||||
@ -2,7 +2,14 @@ import { createFileRoute, Link, useNavigate } from '@tanstack/react-router'
|
|||||||
import { route } from '@/constants/routes'
|
import { route } from '@/constants/routes'
|
||||||
import { useModelSources } from '@/hooks/useModelSources'
|
import { useModelSources } from '@/hooks/useModelSources'
|
||||||
import { cn, fuzzySearch, toGigabytes } from '@/lib/utils'
|
import { cn, fuzzySearch, toGigabytes } from '@/lib/utils'
|
||||||
import { useState, useMemo, useEffect, ChangeEvent, useCallback } from 'react'
|
import {
|
||||||
|
useState,
|
||||||
|
useMemo,
|
||||||
|
useEffect,
|
||||||
|
ChangeEvent,
|
||||||
|
useCallback,
|
||||||
|
useRef,
|
||||||
|
} from 'react'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { useModelProvider } from '@/hooks/useModelProvider'
|
import { useModelProvider } from '@/hooks/useModelProvider'
|
||||||
import { Card, CardItem } from '@/containers/Card'
|
import { Card, CardItem } from '@/containers/Card'
|
||||||
@ -16,10 +23,11 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu'
|
} from '@/components/ui/dropdown-menu'
|
||||||
import { downloadModel } from '@/services/models'
|
import { addModelSource, downloadModel } from '@/services/models'
|
||||||
import { useDownloadStore } from '@/hooks/useDownloadStore'
|
import { useDownloadStore } from '@/hooks/useDownloadStore'
|
||||||
import { Progress } from '@/components/ui/progress'
|
import { Progress } from '@/components/ui/progress'
|
||||||
import HeaderPage from '@/containers/HeaderPage'
|
import HeaderPage from '@/containers/HeaderPage'
|
||||||
|
import { Loader } from 'lucide-react'
|
||||||
|
|
||||||
type ModelProps = {
|
type ModelProps = {
|
||||||
model: {
|
model: {
|
||||||
@ -47,6 +55,10 @@ function Hub() {
|
|||||||
const [expandedModels, setExpandedModels] = useState<Record<string, boolean>>(
|
const [expandedModels, setExpandedModels] = useState<Record<string, boolean>>(
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
|
const [isSearching, setIsSearching] = useState(false)
|
||||||
|
const addModelSourceTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
const toggleModelExpansion = (modelId: string) => {
|
const toggleModelExpansion = (modelId: string) => {
|
||||||
setExpandedModels((prev) => ({
|
setExpandedModels((prev) => ({
|
||||||
@ -87,7 +99,26 @@ function Hub() {
|
|||||||
}, [fetchSources])
|
}, [fetchSources])
|
||||||
|
|
||||||
const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
|
const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setIsSearching(false)
|
||||||
setSearchValue(e.target.value)
|
setSearchValue(e.target.value)
|
||||||
|
if (addModelSourceTimeoutRef.current) {
|
||||||
|
clearTimeout(addModelSourceTimeoutRef.current)
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
e.target.value.length &&
|
||||||
|
(e.target.value.includes('/') || e.target.value.startsWith('http'))
|
||||||
|
) {
|
||||||
|
setIsSearching(true)
|
||||||
|
addModelSourceTimeoutRef.current = setTimeout(() => {
|
||||||
|
addModelSource(e.target.value)
|
||||||
|
.then(() => {
|
||||||
|
fetchSources()
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsSearching(false)
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { downloads } = useDownloadStore()
|
const { downloads } = useDownloadStore()
|
||||||
@ -163,10 +194,14 @@ function Hub() {
|
|||||||
<div className="flex flex-col h-full w-full">
|
<div className="flex flex-col h-full w-full">
|
||||||
<HeaderPage>
|
<HeaderPage>
|
||||||
<div className="pr-4 py-3 border-b border-main-view-fg/5 h-10 w-full flex items-center justify-between relative z-20 ">
|
<div className="pr-4 py-3 border-b border-main-view-fg/5 h-10 w-full flex items-center justify-between relative z-20 ">
|
||||||
<div className="flex items-center gap-2 w-full pr-4">
|
<div className="flex items-center gap-2 w-full">
|
||||||
<IconSearch className="text-main-view-fg/60" size={14} />
|
{isSearching ? (
|
||||||
|
<Loader className="size-4 animate-spin text-main-view-fg/60" />
|
||||||
|
) : (
|
||||||
|
<IconSearch className="text-main-view-fg/60" size={14} />
|
||||||
|
)}
|
||||||
<input
|
<input
|
||||||
placeholder="Search models..."
|
placeholder="Search for models on Hugging Face..."
|
||||||
value={searchValue}
|
value={searchValue}
|
||||||
onChange={handleSearchChange}
|
onChange={handleSearchChange}
|
||||||
className="w-full focus:outline-none"
|
className="w-full focus:outline-none"
|
||||||
@ -223,11 +258,13 @@ function Hub() {
|
|||||||
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={`https://huggingface.co/${model.id}` as string}
|
to={
|
||||||
|
`https://huggingface.co/${model.metadata?.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.metadata?.id) || ''}
|
||||||
</h1>
|
</h1>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="shrink-0 space-x-3 flex items-center">
|
<div className="shrink-0 space-x-3 flex items-center">
|
||||||
@ -241,6 +278,8 @@ function Hub() {
|
|||||||
>
|
>
|
||||||
<div className="line-clamp-2 mt-3 text-main-view-fg/60">
|
<div className="line-clamp-2 mt-3 text-main-view-fg/60">
|
||||||
<RenderMarkdown
|
<RenderMarkdown
|
||||||
|
enableRawHtml={true}
|
||||||
|
className="select-none"
|
||||||
components={{
|
components={{
|
||||||
a: ({ ...props }) => (
|
a: ({ ...props }) => (
|
||||||
<a
|
<a
|
||||||
@ -251,7 +290,8 @@ function Hub() {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
content={
|
content={
|
||||||
extractDescription(model.metadata.description) || ''
|
extractDescription(model.metadata?.description) ||
|
||||||
|
''
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -303,7 +343,6 @@ function Hub() {
|
|||||||
title={variant.id}
|
title={variant.id}
|
||||||
actions={
|
actions={
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* {defaultVariant && <>test</>} */}
|
|
||||||
<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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user