import React, { CSSProperties, MouseEventHandler, ReactNode, useState } from 'react'; import cls from 'classnames'; import BaseComponent from '../../_base/baseComponent'; import PropTypes from 'prop-types'; import { cssClasses, strings } from '@douyinfe/semi-foundation/sidebar/constants'; import { RadioGroup, Radio, Input, Tooltip, Button, Empty } from '../../index'; import { IconSearch, IconPlus, IconSetting, IconMinus, IconEdit } from '@douyinfe/semi-icons'; import { IllustrationNoContent, IllustrationNoContentDark } from '@douyinfe/semi-illustrations'; import MCPConfigureContentFoundation, { MCPOption, MCPConfigureContentProps, MCPConfigureMode, MCPConfigureContentState, MCPConfigureContentAdapter } from '@douyinfe/semi-foundation/sidebar/mcpCofContentFoundation'; import LocaleConsumer from '../../locale/localeConsumer'; import { Locale } from '../../locale/interface'; import { getFilterResult } from '@douyinfe/semi-foundation/sidebar/utils'; const prefixCls = cssClasses.MCP_CONFIGURE_CONTENT; export interface MCPReactOption extends MCPOption { icon?: ReactNode; desc?: ReactNode } export interface MCPConfigureContentReactProps extends MCPConfigureContentProps { options?: MCPReactOption[]; customOptions?: MCPReactOption[]; filter?: (inputValue: string, option: MCPReactOption) => boolean; style?: CSSProperties; onStatusChange?: (options: MCPReactOption[], custom: boolean) => void; onAddClick?: (e: React.MouseEvent) => void; onConfigureClick?: (e: React.MouseEvent, option: MCPReactOption) => void; onEditClick?: (e: React.MouseEvent, option: MCPReactOption) => void; renderItem?: (props: { option: MCPReactOption; custom: boolean }) => ReactNode } interface MCPConfigureContentReactState extends MCPConfigureContentState{ showOptions: MCPReactOption[]; cachedOptions: MCPReactOption[]; cachedCustomOptions: MCPReactOption[] } class MCPConfigureContent extends BaseComponent { static propTypes = { className: PropTypes.string, options: PropTypes.array, customOptions: PropTypes.array, filter: PropTypes.func, placeholder: PropTypes.string, style: PropTypes.object, onStatusChange: PropTypes.func, onSearch: PropTypes.func, onAddClick: PropTypes.func, onConfigureClick: PropTypes.func, onEditClick: PropTypes.func, renderItem: PropTypes.func, }; static __SemiComponentName__ = "MCPConfigureContent"; static defaultProps = {}; foundation: MCPConfigureContentFoundation; constructor(props: MCPConfigureContentReactProps) { super(props); this.state = { mode: strings.MCP_MODE.INNER, inputValue: '', showOptions: props.options ?? [], cachedOptions: props.options ?? [], cachedCustomOptions: props.customOptions ?? [], }; this.foundation = new MCPConfigureContentFoundation(this.adapter); } get adapter(): MCPConfigureContentAdapter { return { ...super.adapter, notifyConfigureClick: (e: React.MouseEvent, option: MCPOption) => { this.props.onConfigureClick?.(e, option); }, notifyEditClick: (e: React.MouseEvent, option: MCPOption) => { this.props.onEditClick?.(e, option); }, notifyStatusChange: (options: MCPOption[], custom: boolean) => { this.props.onStatusChange?.(options, custom); }, notifyAddClick: (e: any) => { this.props.onAddClick?.(e); } }; } static getDerivedStateFromProps(nextProps: MCPConfigureContentReactProps, prevState: MCPConfigureContentReactState) { const newState = {} as Partial; const { options = [], customOptions = [], filter } = nextProps; const { cachedOptions = [], cachedCustomOptions = [] } = prevState; if (options !== cachedOptions) { newState.cachedOptions = options; if (prevState.mode === strings.MCP_MODE.INNER) { if (!prevState.inputValue) { newState.showOptions = options; } else { newState.showOptions = getFilterResult(prevState.inputValue, options, filter); } } } if (customOptions !== cachedCustomOptions) { newState.cachedCustomOptions = customOptions; if (prevState.mode === strings.MCP_MODE.CUSTOM) { if (!prevState.inputValue) { newState.showOptions = customOptions; } else { newState.showOptions = getFilterResult(prevState.inputValue, customOptions, filter); } } } return newState; } renderContent = () => { const { showOptions, mode } = this.state; const { renderItem } = this.props; return
{showOptions.map(option => { if (renderItem) { return renderItem({ custom: mode === strings.MCP_MODE.CUSTOM, option: option, }); } return (
{typeof option.icon === 'string' ? {option.label} :
{option.icon}
}
{option.label}
{option.desc}
{option.configure &&
); })}
; } renderStatusButton = (option: MCPOption) => { const buttonNode = )} ; } return
{(locale: Locale['Sidebar']) => (<> } value={this.state.inputValue} placeholder={placeholder ?? locale.searchPlaceholder} onChange={this.foundation.handleSearch} /> )}
; } } render() { const { options = [], customOptions = [], className, style } = this.props; const activatedCount = options.filter(option => option.active).length + customOptions.filter(option => option.active).length; return
{/* header */} {(locale: Locale['Sidebar']) => (
MCP Servers {locale.newMcpAdd} {locale.activeMCPNumber} {activatedCount}/{options.length + customOptions.length}
)}
{/* search */} {this.renderSearch()} {/* content */} {this.renderContent()}
; } } export default MCPConfigureContent;