import { AutocompletePlugin } from '@algolia/autocomplete-js'; import type { AgentDefinition } from '@services/agentDefinition/interface'; import { getI18n } from 'react-i18next'; import { TEMP_TAB_ID_PREFIX } from '../../../constants/tab'; import { useTabStore } from '../../../store/tabStore'; import { TabType } from '../../../types/tab'; interface AgentsPluginOptions { onSelect?: (agent: AgentDefinition) => void; sourceTitle?: string; /** If true, search agent templates instead of existing agent definitions */ searchTemplates?: boolean; } export const createAgentsPlugin = (options: AgentsPluginOptions = {}): AutocompletePlugin, unknown> => { // Get translation function, but fallback gracefully in test environment let t: (key: string) => string; try { t = getI18n().t; } catch { // Fallback for test environment t = (key: string) => key; } const plugin = { getSources({ query }) { return [ { sourceId: options.onSelect ? 'templateAgentsSource' : 'agentsSource', getItems: async () => { try { // Choose between agent definitions and templates based on options // Note: getAgentTemplates no longer performs server-side search filtering, // so always fetch the full list and apply client-side filtering here. const agents = options.searchTemplates ? await window.service.agentDefinition.getAgentTemplates() : await window.service.agentDefinition.getAgentDefs(); if (!query) { return agents as (AgentDefinition & Record)[]; } // Apply client-side filtering for both templates and definitions const lowerCaseQuery = query.toLowerCase(); return (agents as (AgentDefinition & Record)[]).filter(agent => (agent.name && agent.name.toLowerCase().includes(lowerCaseQuery)) || (agent.description && agent.description.toLowerCase().includes(lowerCaseQuery)) ); } catch (error) { console.error(t('Search.FailedToFetchAgents'), error); return []; } }, templates: { header() { return (
{options.sourceTitle || t('Search.AvailableAgents')}
); }, item({ item, state }) { return (
{item.avatarUrl ? ( {item.name} ) : (
🤖
)}
{item.description && (
)}
); }, noResults() { return (
{t('Search.NoAgentsFound')}
); }, }, onSelect: async ({ item }) => { try { // If custom onSelect callback is provided, use it if (options.onSelect) { options.onSelect(item as AgentDefinition); return; } // Default behavior: create chat tab const tabStore = useTabStore.getState(); const { addTab, closeTab, activeTabId, tabs } = tabStore; // Handle current active tab - close temp tabs or NEW_TAB type tabs if (activeTabId) { const activeTab = tabs.find(tab => tab.id === activeTabId); if (activeTab && (activeTab.id.startsWith(TEMP_TAB_ID_PREFIX) || activeTab.type === TabType.NEW_TAB)) { closeTab(activeTabId); } } // Create new chat tab directly using addTab await addTab(TabType.CHAT, { agentDefId: item.id, }); } catch (error) { console.error(t('Search.FailedToCreateChatWithAgent'), error); } }, }, ]; }, } satisfies AutocompletePlugin, unknown>; return plugin; }; function highlightHits({ hit, attribute, query, }: { hit: AgentDefinition & Record; attribute: string; query: string; }): string { // Get attribute value and convert to string const attributeValue = hit[attribute]; let value = ''; if (typeof attributeValue === 'string') { value = attributeValue; } else if (attributeValue === null || attributeValue === undefined) { // Handle empty value } else { // Try to safely convert to string try { // eslint-disable-next-line @typescript-eslint/no-base-to-string const stringValue = String(attributeValue); if (stringValue !== '[object Object]') { value = stringValue; } else { value = JSON.stringify(attributeValue); } } catch { // Conversion failed, keep empty string } } // If no query or value is empty, return value directly if (!query || !value) return value; // Perform search and highlighting const lowerCaseValue = value.toLowerCase(); const lowerCaseQuery = query.toLowerCase(); const startIndex = lowerCaseValue.indexOf(lowerCaseQuery); if (startIndex === -1) return value; const endIndex = startIndex + lowerCaseQuery.length; return value.substring(0, startIndex) + `${value.substring(startIndex, endIndex)}` + value.substring(endIndex); }