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>
  )
}