jbilcke-hf's picture
jbilcke-hf HF staff
wip
3b780fb
raw
history blame
5.09 kB
import { useRef, useState, useTransition } from "react"
import Link from "next/link"
import { encode, decode } from 'js-base64'
// import throttle from "@jcoreio/async-throttle"
import debounce from "lodash.debounce"
import { GoSearch } from "react-icons/go"
import { useStore } from "@/app/state/useStore"
import { cn } from "@/lib/utils/cn"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
import { MediaInfo } from "@/types/general"
import { search } from "@/app/api/v1/search"
export function LatentSearchInput() {
const [_pending, startTransition] = useTransition()
const setSearchAutocompleteQuery = useStore(s => s.setSearchAutocompleteQuery)
const showAutocompleteBox = useStore(s => s.showAutocompleteBox)
const setShowAutocompleteBox = useStore(s => s.setShowAutocompleteBox)
const searchAutocompleteResults = useStore(s => s.searchAutocompleteResults)
const setSearchAutocompleteResults = useStore(s => s.setSearchAutocompleteResults)
const setSearchQuery = useStore(s => s.setSearchQuery)
const [searchDraft, setSearchDraft] = useState("")
const ref = useRef<HTMLInputElement>(null)
// called when pressing enter or clicking on search
const debouncedSearch = debounce((query: string) => {
startTransition(async () => {
console.log(`searching the latent space for "${query}"..`)
if (query.length < 2) { console.log("search term is too short") }
console.log("imaginating medias..")
const imaginedMedias = await search({
prompt: query,
nbResults: 4
})
console.log(`imagined ${imaginedMedias.length} results:`, imaginedMedias)
setSearchAutocompleteResults(imaginedMedias.map(item => ({
title: item.title,
tags: item.tags,
})))
// TODO: only close the show autocomplete box if we found something
// setShowAutocompleteBox(false)
})
}, 500)
// called when pressing enter or clicking on search
const handleSearch = () => {
ref.current?.focus()
setSearchQuery(searchDraft)
setShowAutocompleteBox(true)
debouncedSearch(searchDraft)
}
return (
<div className="flex flex-row flex-grow w-[380px] lg:w-[600px]">
<div className="flex flex-row w-full">
<Input
ref={ref}
placeholder="Search the latent space"
className={cn(
`bg-neutral-900 text-neutral-200 dark:bg-neutral-900 dark:text-neutral-200`,
`rounded-l-full rounded-r-none`,
// we increase the line height to better catch the clicks
`py-0 h-10 leading-7`,
`border-neutral-700 dark:border-neutral-700 border-r-0`,
)}
onFocus={() => {
handleSearch()
}}
onChange={(e) => {
setSearchDraft(e.target.value)
setShowAutocompleteBox(true)
// handleSearch()
}}
onKeyDown={({ key }) => {
if (key === 'Enter') {
handleSearch()
}
}}
value={searchDraft}
/>
<Button
className={cn(
`rounded-l-none rounded-r-full border border-neutral-700 dark:border-neutral-700`,
`cursor-pointer`,
`transition-all duration-200 ease-in-out`,
`text-neutral-200 dark:text-neutral-200 bg-neutral-800 dark:bg-neutral-800 hover:bg-neutral-700 disabled:bg-neutral-900`
)}
onClick={() => {
handleSearch()
// console.log("submit")
// setShowAutocompleteBox(false)
// setSearchDraft("")
}}
disabled={false}
>
<GoSearch className="w-6 h-6" />
</Button>
</div>
<div
className={cn(
`absolute z-50 ml-1`,
// please keep this in sync with the parent
`w-[320px] lg:w-[540px]`,
`text-neutral-200 dark:text-neutral-200 bg-neutral-900 dark:bg-neutral-900`,
`border border-neutral-800 dark:border-neutral-800`,
`rounded-xl shadow-2xl`,
`flex flex-col p-2 space-y-1`,
`transition-all duration-200 ease-in-out`,
showAutocompleteBox
? `opacity-100 scale-100 mt-11 pointer-events-auto`
: `opacity-0 scale-95 mt-6 pointer-events-none`
)}
>
{searchAutocompleteResults.length === 0 ? <div>Nothing to show, type something and press enter</div> : null}
{searchAutocompleteResults.map(item => (
<Link key={item.id} href={
item.id
? `${process.env.NEXT_PUBLIC_DOMAIN}/watch?v=${item.id}`
: `${process.env.NEXT_PUBLIC_DOMAIN}/latent/watch?p=${encode(JSON.stringify(item))}`
}>
<div
className={cn(
`dark:hover:bg-neutral-800 hover:bg-neutral-800`,
`text-sm`,
`px-3 py-2`,
`rounded-xl`
)}
>
{item.title}
</div>
</Link>
))}
</div>
</div>
)
}