Spaces:
Running
Running
File size: 5,093 Bytes
3b780fb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
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>
)
}
|