feat(input): 附件引用弹窗新增搜索过滤框
- DropdownMenu 内新增 Input 搜索框,autoFocus - filterMentionCandidates 同时受 mentionQuery 和 mentionSearchText 双重过滤 - 搜索时重置高亮索引避免越界 - 上/下箭头将焦点交还给候选列表复用键盘导航 - 所有关闭路径统一重置搜索文字 - 弹窗打开时自动 refetch 最新文件列表
This commit is contained in:
parent
f3c160f103
commit
7d5e25e325
@ -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<PromptInputReference[]>([]);
|
||||
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;
|
||||
}
|
||||
return mentionCandidates.filter((candidate) =>
|
||||
const search = mentionSearchText.trim().toLowerCase();
|
||||
let result = mentionCandidates;
|
||||
if (query) {
|
||||
result = result.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({
|
||||
<DropdownMenuLabel className="p-0 text-sm text-ws-fg-primary">
|
||||
{t.inputBox.addReference}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator className="mx-0 mt-[20px] mb-0" />
|
||||
<Input
|
||||
className="mt-3 h-8 text-sm"
|
||||
placeholder="搜索文件..."
|
||||
value={mentionSearchText}
|
||||
autoFocus
|
||||
onChange={(e) => {
|
||||
setMentionSearchText(e.target.value);
|
||||
setActiveMentionIndex(0);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
// 将焦点交还给 dropdown,让现有的键盘导航逻辑处理
|
||||
const items =
|
||||
document.querySelectorAll<HTMLElement>(
|
||||
'[data-testid="mention-candidate-item"]',
|
||||
);
|
||||
if (items.length > 0) {
|
||||
(items[0] as HTMLElement).focus();
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<DropdownMenuSeparator className="mx-0 mt-3 mb-0" />
|
||||
<DropdownMenuGroup className="flex min-h-0 flex-col gap-[10px] px-0">
|
||||
<ScrollArea className="h-[320px] pt-[20px]" hideScrollbar={false}>
|
||||
{filteredMentionCandidates.map((candidate, index) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user