diff --git a/ee/tabby-ui/components/prompt-form.tsx b/ee/tabby-ui/components/prompt-form.tsx index f930b54..16dcb49 100644 --- a/ee/tabby-ui/components/prompt-form.tsx +++ b/ee/tabby-ui/components/prompt-form.tsx @@ -1,17 +1,26 @@ import { UseChatHelpers } from 'ai/react' import * as React from 'react' -import Textarea from 'react-textarea-autosize' - import { Button, buttonVariants } from '@/components/ui/button' -import { IconArrowElbow, IconEdit, IconPlus } from '@/components/ui/icons' +import { IconArrowElbow, IconEdit } from '@/components/ui/icons' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { + Combobox, + ComboboxAnchor, + ComboboxContent, + ComboboxOption, + ComboboxTextarea +} from '@/components/ui/combobox' +import { Popover, PopoverAnchor, PopoverContent } from '@/components/ui/popover' import { useEnterSubmit } from '@/lib/hooks/use-enter-submit' -import { cn } from '@/lib/utils' -import { useRouter } from 'next/navigation' +import { cn, getSearchCompletionQueryName } from '@/lib/utils' +import useSWR from 'swr' +import fetcher from '@/lib/tabby-fetcher' +import { debounce, has } from 'lodash' +import { ISearchHit, SearchReponse } from '@/lib/types' export interface PromptProps extends Pick { @@ -26,15 +35,56 @@ export function PromptForm({ isLoading }: PromptProps) { const { formRef, onKeyDown } = useEnterSubmit() - const inputRef = React.useRef(null) - const router = useRouter() + const [queryCompletionUrl, setQueryCompletionUrl] = React.useState< + string | null + >(null) + const latestFetchKey = React.useRef('') - React.useEffect(() => { - if (inputRef.current) { - inputRef.current.focus() + useSWR(queryCompletionUrl, fetcher, { + revalidateOnFocus: false, + dedupingInterval: 500, + onSuccess: (data, key) => { + if (key !== latestFetchKey.current) return + + setOptions(data?.hits ?? []) } + }) + + const [options, setOptions] = React.useState([]) + const onSearch = React.useMemo(() => { + return debounce((e: React.ChangeEvent) => { + const value = e.target?.value ?? '' + const end = e.target?.selectionEnd ?? 0 + const queryname = getSearchCompletionQueryName(value, end) + if (queryname) { + let url = `/v1beta/search?q=name:${queryname}` + latestFetchKey.current = url + setQueryCompletionUrl(`/v1beta/search?q=name:${queryname}`) + } else { + setOptions([]) + } + }, 200) }, []) + const onSelectCompletion = ( + inputRef: React.MutableRefObject< + HTMLTextAreaElement | HTMLInputElement | null + >, + item: ISearchHit + ) => { + const replaceString = '`@' + item?.doc?.name + '` ' + const selectionEnd = inputRef.current?.selectionEnd ?? 0 + const queryname = getSearchCompletionQueryName(input, selectionEnd) + const prevInput = input + .substring(0, selectionEnd) + .replace(new RegExp(`@${queryname}$`), '') + if (queryname) { + setInput(prevInput + replaceString + input.substring(selectionEnd)) + } + + setOptions([]) + } + return (
{ @@ -47,42 +97,108 @@ export function PromptForm({ }} ref={formRef} > -
- - - -