LibreChat/client/src/components/Chat/Input/MCPSubMenu.tsx

97 lines
2.9 KiB
TypeScript
Raw Normal View History

🤖 feat: Streamline Endpoints to Agent Framework (#8013) * refactor(buildEndpointOption): Improve error logging in middleware, consolidate `isAgents` builder logic, remove adding `modelsConfig` to `endpointOption` * refactor: parameter extraction and organization in agent services, minimize redundancy of shared fields across objects, make clear distinction of parameters processed uniquely by LibreChat vs LLM Provider Configs * refactor(createPayload): streamline all endpoints to agent route * fix: add `modelLabel` to response sender options for agent initialization * chore: correct log message context in EditController abort controller cleanup * chore: remove unused abortRequest hook * chore: remove unused addToCache module and its dependencies * refactor: remove AskController and related routes, update endpoint URLs (now all streamlined to agents route) * chore: remove unused bedrock route and its related imports * refactor: simplify response sender logic for Google endpoint * chore: add `modelDisplayLabel` handling for agents endpoint * feat: add file search capability to ephemeral agents, update code interpreter selection based of file upload, consolidate main upload menu for all endpoints * feat: implement useToolToggle hook for managing tool toggle state, refactor CodeInterpreter and WebSearch components to utilize new hook * feat: add ToolsDropdown component to BadgeRow for enhanced tool options * feat: introduce BadgeRowContext and BadgeRowProvider for managing conversation state, refactor related components to utilize context * feat: implement useMCPSelect hook for managing MCP selection state, refactor MCPSelect component to utilize new hook * feat: enhance BadgeRowContext with MCPSelect and tool toggle functionality, refactor related components to utilize updated context and hooks * refactor: streamline useToolToggle hook by integrating setEphemeralAgent directly into toggle logic and removing redundant setValue function * refactor: consolidate codeApiKeyForm and searchApiKeyForm from CodeInterpreter and WebSearch to utilize new context properties * refactor: update CheckboxButton to support controlled state and enhance ToolsDropdown with permission-based toggles for web search and code interpreter * refactor: conditionally render CheckboxButton in CodeInterpreter and WebSearch components for improved UI responsiveness * chore: add jotai dependency to package.json and package-lock.json * chore: update brace-expansion package to version 2.0.2 in package-lock.json due to CVE-2025-5889 * Revert "chore: add jotai dependency to package.json and package-lock.json" This reverts commit 69b699739671e740820f570ddb83d1caa0dbd6e4. * refactor: add pinning functionality to CodeInterpreter and WebSearch components, and enhance ToolsDropdown with pin toggle for web search and code interpreter * chore: move MCPIcon to correct location, remove duplicate * fix: update MCP import to use type-only import from librechat-data-provider * feat: implement MCPSubMenu component and integrate pinning functionality into ToolsDropdown * fix: cycling to submenu by using parent menu context * feat: add FileSearch component and integrate it into BadgeRow and ToolsDropdown * chore: import order * chore: remove agent specific logic that would block functionality for streamlined endpoints * chore: linting for `createContextHandlers` * chore: ensure ToolsDropdown doesn't show up for agents * chore: ensure tool resource is selected when dragged to UI * chore: update file search behavior to simulate legacy functionality * feat: ToolDialogs with multiple trigger references, add settings to tool dropdown * refactor: simplify web search and code interpreter settings checks * chore: simplify local storage key for pinned state in useToolToggle * refactor: reinstate agent check in AttachFileChat component, as individual providers will ahve different file configurations * ci: increase timeout for MongoDB connection in Agent tests
2025-06-23 09:59:05 -04:00
import React from 'react';
import * as Ariakit from '@ariakit/react';
import { ChevronRight } from 'lucide-react';
import { PinIcon, MCPIcon } from '~/components/svg';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
interface MCPSubMenuProps {
isMCPPinned: boolean;
setIsMCPPinned: (value: boolean) => void;
mcpValues?: string[];
mcpServerNames: string[];
handleMCPToggle: (serverName: string) => void;
}
const MCPSubMenu = ({
mcpValues,
isMCPPinned,
mcpServerNames,
setIsMCPPinned,
handleMCPToggle,
...props
}: MCPSubMenuProps) => {
const localize = useLocalize();
const menuStore = Ariakit.useMenuStore({
showTimeout: 100,
placement: 'right',
});
return (
<Ariakit.MenuProvider store={menuStore}>
<Ariakit.MenuItem
{...props}
render={
<Ariakit.MenuButton className="flex w-full cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-surface-hover" />
}
>
<div className="flex items-center gap-2">
<MCPIcon className="icon-md" />
<span>{localize('com_ui_mcp_servers')}</span>
<ChevronRight className="ml-auto h-3 w-3" />
</div>
<button
type="button"
onClick={(e) => {
e.stopPropagation();
setIsMCPPinned(!isMCPPinned);
}}
className={cn(
'rounded p-1 transition-all duration-200',
'hover:bg-surface-tertiary hover:shadow-sm',
!isMCPPinned && 'text-text-secondary hover:text-text-primary',
)}
aria-label={isMCPPinned ? 'Unpin' : 'Pin'}
>
<div className="h-4 w-4">
<PinIcon unpin={isMCPPinned} />
</div>
</button>
</Ariakit.MenuItem>
<Ariakit.Menu
gutter={-4}
shift={-8}
unmountOnHide
portal={true}
className={cn(
'animate-popover-left z-50 ml-3 flex min-w-[200px] flex-col rounded-xl',
'border border-border-light bg-surface-secondary p-1 shadow-lg',
)}
>
{mcpServerNames.map((serverName) => (
<Ariakit.MenuItem
key={serverName}
onClick={(event) => {
event.preventDefault();
handleMCPToggle(serverName);
}}
className={cn(
'flex items-center gap-2 rounded-lg px-2 py-1.5 text-text-primary hover:cursor-pointer',
'scroll-m-1 outline-none transition-colors',
'hover:bg-black/[0.075] dark:hover:bg-white/10',
'data-[active-item]:bg-black/[0.075] dark:data-[active-item]:bg-white/10',
'w-full min-w-0 text-sm',
)}
>
<Ariakit.MenuItemCheck checked={mcpValues?.includes(serverName) ?? false} />
<span>{serverName}</span>
</Ariakit.MenuItem>
))}
</Ariakit.Menu>
</Ariakit.MenuProvider>
);
};
export default React.memo(MCPSubMenu);