import { FileX, Search, SearchX } from 'lucide-react'; import { useMemo, useState } from 'react'; import type { KnowledgeBaseView, SessionKnowledgeConfig } from '@/api'; import { PanelEmpty } from '@/components/panel/PanelEmpty'; import { Badge } from '@/components/ui/badge'; import { Checkbox } from '@/components/ui/checkbox'; import { InputGroup, InputGroupAddon, InputGroupInput } from '@/components/ui/input-group'; import { Item, ItemContent, ItemDescription, ItemTitle } from '@/components/ui/item'; import { useTranslation } from '@/i18n/useI18n'; interface KnowledgeBasePanelProps { /** The user's knowledge bases. */ knowledgeBases: KnowledgeBaseView[]; /** Whether the KB list is still loading. */ loading?: boolean; /** * Current attachment for this session. `null` means no KBs attached * and the panel renders with an empty selection. */ value: SessionKnowledgeConfig | null; /** * Persist a new attachment to the session. The owner is responsible * for awaiting the backend round-trip and refreshing session state. * Pass `null` to detach every KB. */ onChange: (next: SessionKnowledgeConfig | null) => void; /** Disable the entire panel — e.g. when no session is selected. */ disabled?: boolean; } /** * Pure content body for the Knowledge Base dock panel: a search box and * a checkbox list of the user's KBs. * * Middleware parameter editing lives in * {@link KnowledgeBaseParametersPopover}, which the owner mounts in the * panel header's `actions` slot — that keeps this body focused on * picking KBs rather than mixing two unrelated forms in the same scroll * area. */ export function KnowledgeBasePanel({ knowledgeBases, loading = false, value, onChange, disabled = false, }: KnowledgeBasePanelProps) { const { t } = useTranslation(); const [search, setSearch] = useState(''); const selectedIds = useMemo(() => new Set(value?.knowledge_base_ids ?? []), [value]); const filtered = search ? knowledgeBases.filter((kb) => kb.name.toLowerCase().includes(search.toLowerCase())) : knowledgeBases; const toggleKb = (kbId: string, checked: boolean) => { const next = new Set(selectedIds); if (checked) next.add(kbId); else next.delete(kbId); const ids = Array.from(next); if (ids.length === 0) { onChange(null); return; } onChange({ knowledge_base_ids: ids, parameters: value?.parameters ?? {}, }); }; return (
{t('panel.knowledge.description')} setSearch(e.target.value)} disabled={disabled} />
{loading ? (

{t('panel.loading')}

) : filtered.length === 0 ? ( ) : (
{filtered.map((kb) => { const isSelected = selectedIds.has(kb.id); const inputId = `kb-${kb.id}`; return ( toggleKb(kb.id, !!checked)} /> {kb.description ? ( {kb.description} ) : null}
{kb.embedding_model_config.model} {kb.embedding_model_config.dimensions}d
); })}
)}
); }