From 28b6926831c8e8e7409197d123cc6f2f080e3c55 Mon Sep 17 00:00:00 2001 From: euvre <93761161+euvre@users.noreply.github.com> Date: Wed, 22 Jul 2026 19:39:39 +0800 Subject: [PATCH] Fix: search input cannot expand to show long queries (#17166) --- web/src/hooks/use-auto-resize-textarea.ts | 20 ++++++++++++++++++++ web/src/pages/next-search/search-home.tsx | 23 +++++++++++++++++------ web/src/pages/next-search/search-view.tsx | 22 ++++++++++++++++------ 3 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 web/src/hooks/use-auto-resize-textarea.ts diff --git a/web/src/hooks/use-auto-resize-textarea.ts b/web/src/hooks/use-auto-resize-textarea.ts new file mode 100644 index 0000000000..cd9f0fcfe2 --- /dev/null +++ b/web/src/hooks/use-auto-resize-textarea.ts @@ -0,0 +1,20 @@ +import { useLayoutEffect, type RefObject } from 'react'; + +/** + * Auto-resize a textarea to fit its content, clamped to a maximum height. + * + * useLayoutEffect runs synchronously after DOM mutation but before the + * browser paints, so the height adjustment never produces a visible flicker. + */ +export function useAutoResizeTextarea( + ref: RefObject, + value: string, + maxHeight = 160, +) { + useLayoutEffect(() => { + const el = ref.current; + if (!el) return; + el.style.height = 'auto'; + el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`; + }, [ref, value, maxHeight]); +} diff --git a/web/src/pages/next-search/search-home.tsx b/web/src/pages/next-search/search-home.tsx index 7ef6e9ed97..317cca2d50 100644 --- a/web/src/pages/next-search/search-home.tsx +++ b/web/src/pages/next-search/search-home.tsx @@ -1,9 +1,9 @@ -import { Input } from '@/components/originui/input'; import Spotlight from '@/components/spotlight'; import message from '@/components/ui/message'; import { IUserInfo } from '@/interfaces/database/user-setting'; +import { useAutoResizeTextarea } from '@/hooks/use-auto-resize-textarea'; import { Search } from 'lucide-react'; -import { Dispatch, SetStateAction } from 'react'; +import { Dispatch, SetStateAction, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import './index.less'; import { RAGFlowLogo } from './ragflow-logo'; @@ -27,6 +27,10 @@ export default function SearchHome({ }) { // const { data: userInfo } = useFetchUserInfo(); const { t } = useTranslation(); + const searchInputRef = useRef(null); + + useAutoResizeTextarea(searchInputRef, searchText); + return (
@@ -48,12 +52,19 @@ export default function SearchHome({ )}
- { - if (e.key === 'Enter') { + onKeyDown={(e) => { + if ( + e.key === 'Enter' && + !e.shiftKey && + !e.nativeEvent.isComposing + ) { + e.preventDefault(); if (canSearch === false) { message.warning(t('search.chooseDataset')); return; diff --git a/web/src/pages/next-search/search-view.tsx b/web/src/pages/next-search/search-view.tsx index 3325fadaaa..8c0aa34250 100644 --- a/web/src/pages/next-search/search-view.tsx +++ b/web/src/pages/next-search/search-view.tsx @@ -3,7 +3,6 @@ import Empty from '@/components/empty/empty'; import HighLightMarkdown from '@/components/highlight-markdown'; import { FileIcon } from '@/components/icon-font'; import { ImageWithPopover } from '@/components/image'; -import { Input } from '@/components/originui/input'; import { SkeletonCard } from '@/components/skeleton-card'; import { TopSelect } from '@/components/top-select'; import { Button } from '@/components/ui/button'; @@ -13,10 +12,11 @@ import { PopoverTrigger, } from '@/components/ui/popover'; import { IReference } from '@/interfaces/database/chat'; +import { useAutoResizeTextarea } from '@/hooks/use-auto-resize-textarea'; import { cn } from '@/lib/utils'; import { isEmpty } from 'lodash'; import { ListTree, Search, X } from 'lucide-react'; -import { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ISearchAppDetailProps } from '../next-searches/hooks'; import PdfDrawer from './document-preview-modal'; @@ -73,11 +73,14 @@ export default function SearchingView({ const [searchText, setSearchText] = useState(''); const [retrievalLoading, setRetrievalLoading] = useState(false); + const searchInputRef = useRef(null); useEffect(() => { setSearchText(searchStr); }, [searchStr, setSearchText]); + useAutoResizeTextarea(searchInputRef, searchText); + return (
- { setSearchText(e.target.value); }} disabled={sendingLoading} - onKeyUp={(e) => { - if (e.key === 'Enter') { + onKeyDown={(e) => { + if ( + e.key === 'Enter' && + !e.shiftKey && + !e.nativeEvent.isComposing + ) { + e.preventDefault(); handleSearch(searchText); } }}