diff --git a/frontend/src/components/workspace/input-box.tsx b/frontend/src/components/workspace/input-box.tsx index c64b3deb..ac3c73c9 100644 --- a/frontend/src/components/workspace/input-box.tsx +++ b/frontend/src/components/workspace/input-box.tsx @@ -70,6 +70,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; import { Tag } from "@/components/ui/tag"; import { useReferenceFiles } from "@/core/artifacts/references"; import { urlOfArtifact } from "@/core/artifacts/utils"; @@ -286,6 +287,7 @@ export function InputBox({ const [memoryPanelOpen, setMemoryPanelOpen] = useState(false); const [references, setReferences] = useState([]); const [mentionQuery, setMentionQuery] = useState(""); + const [mentionSearchText, setMentionSearchText] = useState(""); const [mentionOpen, setMentionOpen] = useState(false); const [activeMentionIndex, setActiveMentionIndex] = useState(0); const [mentionRange, setMentionRange] = useState<{ @@ -294,7 +296,15 @@ export function InputBox({ } | null>(null); const [isInputToolsTourOpen, setIsInputToolsTourOpen] = useState(false); const [isInputToolsTourReady, setIsInputToolsTourReady] = useState(false); - const { data: referenceFilesData } = useReferenceFiles(threadIdFromProps); + const { data: referenceFilesData, refetch: refetchReferenceFiles } = + useReferenceFiles(threadIdFromProps); + + // 打开附件引用弹窗时刷新数据 + useEffect(() => { + if (mentionOpen) { + refetchReferenceFiles(); + } + }, [mentionOpen, refetchReferenceFiles]); // Welcome 态下禁用收缩,始终保持展开 const effectiveIsFocused = @@ -478,15 +488,24 @@ export function InputBox({ const filteredMentionCandidates = useMemo(() => { const query = mentionQuery.trim().toLowerCase(); - if (!query) { - return mentionCandidates; + const search = mentionSearchText.trim().toLowerCase(); + let result = mentionCandidates; + if (query) { + result = result.filter((candidate) => + `${candidate.filename} ${candidate.typeLabel} ${candidate.pathTail}` + .toLowerCase() + .includes(query), + ); } - return mentionCandidates.filter((candidate) => - `${candidate.filename} ${candidate.typeLabel} ${candidate.pathTail}` - .toLowerCase() - .includes(query), - ); - }, [mentionCandidates, mentionQuery]); + if (search) { + result = result.filter((candidate) => + `${candidate.filename} ${candidate.typeLabel} ${candidate.pathTail}` + .toLowerCase() + .includes(search), + ); + } + return result; + }, [mentionCandidates, mentionQuery, mentionSearchText]); const handleModelSelect = useCallback( (model_name: string) => { onContextChange?.({ @@ -594,6 +613,7 @@ export function InputBox({ }); } setMentionQuery(""); + setMentionSearchText(""); setMentionOpen(false); setActiveMentionIndex(0); setMentionRange(null); @@ -634,6 +654,7 @@ export function InputBox({ if (!token) { setMentionOpen(false); setMentionQuery(""); + setMentionSearchText(""); setActiveMentionIndex(0); setMentionRange(null); return; @@ -683,6 +704,7 @@ export function InputBox({ } } else if (event.key === "Escape") { event.preventDefault(); + setMentionSearchText(""); setMentionOpen(false); setMentionRange(null); } @@ -861,6 +883,7 @@ export function InputBox({ onOpenChange={(open) => { setMentionOpen(open); if (!open) { + setMentionSearchText(""); setMentionRange(null); } }} @@ -888,7 +911,30 @@ export function InputBox({ {t.inputBox.addReference} - + { + setMentionSearchText(e.target.value); + setActiveMentionIndex(0); + }} + onKeyDown={(e) => { + if (e.key === "ArrowDown" || e.key === "ArrowUp") { + e.preventDefault(); + // 将焦点交还给 dropdown,让现有的键盘导航逻辑处理 + const items = + document.querySelectorAll( + '[data-testid="mention-candidate-item"]', + ); + if (items.length > 0) { + (items[0] as HTMLElement).focus(); + } + } + }} + /> + {filteredMentionCandidates.map((candidate, index) => {