import { UseChatHelpers } from 'ai/react' import * as React from 'react' import { Button, buttonVariants } from '@/components/ui/button' 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, 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 { onSubmit: (value: string) => Promise isLoading: boolean } export function PromptForm({ onSubmit, input, setInput, isLoading }: PromptProps) { const { formRef, onKeyDown } = useEnterSubmit() const [queryCompletionUrl, setQueryCompletionUrl] = React.useState< string | null >(null) const latestFetchKey = React.useRef('') 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 (
{ e.preventDefault() if (!input?.trim()) { return } setInput('') await onSubmit(input) }} ref={formRef} > {({ open, inputRef, highlightedIndex }) => { return ( <>
} onChange={e => { if (has(e, 'target.value')) { let event = e as React.ChangeEvent setInput(event.target.value) onSearch?.(event) } }} onKeyDown={onKeyDown} />
Send message
e.preventDefault()} // popupMatchAnchorWidth className="w-1/2 max-w-xl" >
{open && !!options?.length && options.map((item, index) => (
{item?.doc?.name}
{item?.doc?.body}
))}
e.preventDefault()} onFocus={e => e.preventDefault()} onClick={e => e.preventDefault()} className="max-w-xl rounded-none" >
{options?.[highlightedIndex]?.doc?.name}
{options?.[highlightedIndex]?.doc?.body}
) }}
) }