From 10a2ea2afd4847f23a1b240ab751ba6017d885e6 Mon Sep 17 00:00:00 2001 From: liangfung <1098486429@qq.com> Date: Sun, 5 Nov 2023 19:32:19 +0800 Subject: [PATCH] feat: Adding an auto-completion component --- ee/tabby-ui/components/prompt-form.tsx | 206 +++++++++++++---- ee/tabby-ui/components/ui/combobox.tsx | 216 ++++++++++++++++++ ee/tabby-ui/components/ui/popover.tsx | 41 ++++ ee/tabby-ui/lib/{types.ts => types/common.ts} | 0 ee/tabby-ui/lib/types/index.ts | 2 + ee/tabby-ui/lib/types/search.ts | 15 ++ ee/tabby-ui/lib/utils.ts | 15 ++ ee/tabby-ui/package.json | 4 + ee/tabby-ui/yarn.lock | 59 ++++- 9 files changed, 511 insertions(+), 47 deletions(-) create mode 100644 ee/tabby-ui/components/ui/combobox.tsx create mode 100644 ee/tabby-ui/components/ui/popover.tsx rename ee/tabby-ui/lib/{types.ts => types/common.ts} (100%) create mode 100644 ee/tabby-ui/lib/types/index.ts create mode 100644 ee/tabby-ui/lib/types/search.ts 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} > -
- - - -