diff --git a/client/src/components/Chat/Input/ArtifactsSubMenu.tsx b/client/src/components/Chat/Input/ArtifactsSubMenu.tsx index 654c129fba..e27fa43c0e 100644 --- a/client/src/components/Chat/Input/ArtifactsSubMenu.tsx +++ b/client/src/components/Chat/Input/ArtifactsSubMenu.tsx @@ -15,133 +15,142 @@ interface ArtifactsSubMenuProps { handleCustomToggle: () => void; } -const ArtifactsSubMenu = ({ - isArtifactsPinned, - setIsArtifactsPinned, - artifactsMode, - handleArtifactsToggle, - handleShadcnToggle, - handleCustomToggle, - ...props -}: ArtifactsSubMenuProps) => { - const localize = useLocalize(); +const ArtifactsSubMenu = React.forwardRef( + ( + { + isArtifactsPinned, + setIsArtifactsPinned, + artifactsMode, + handleArtifactsToggle, + handleShadcnToggle, + handleCustomToggle, + ...props + }, + ref, + ) => { + const localize = useLocalize(); - const menuStore = Ariakit.useMenuStore({ - focusLoop: true, - showTimeout: 100, - placement: 'right', - }); + const menuStore = Ariakit.useMenuStore({ + focusLoop: true, + showTimeout: 100, + placement: 'right', + }); - const isEnabled = artifactsMode !== '' && artifactsMode !== undefined; - const isShadcnEnabled = artifactsMode === ArtifactModes.SHADCNUI; - const isCustomEnabled = artifactsMode === ArtifactModes.CUSTOM; + const isEnabled = artifactsMode !== '' && artifactsMode !== undefined; + const isShadcnEnabled = artifactsMode === ArtifactModes.SHADCNUI; + const isCustomEnabled = artifactsMode === ArtifactModes.CUSTOM; - return ( - - ) => { - e.stopPropagation(); - handleArtifactsToggle(); - }} - onMouseEnter={() => { - if (isEnabled) { - menuStore.show(); - } - }} - className="flex w-full cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-surface-hover" - /> - } - > -
- - {localize('com_ui_artifacts')} - {isEnabled && } -
- -
- - {isEnabled && ( - -
-
- {localize('com_ui_artifacts_options')} + return ( +
+ + ) => { + e.stopPropagation(); + handleArtifactsToggle(); + }} + onMouseEnter={() => { + if (isEnabled) { + menuStore.show(); + } + }} + className="flex w-full cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-surface-hover" + /> + } + > +
+ + {localize('com_ui_artifacts')} + {isEnabled && }
- - {/* Include shadcn/ui Option */} - { - event.preventDefault(); - event.stopPropagation(); - handleShadcnToggle(); - }} - disabled={isCustomEnabled} - className={cn( - 'mb-1 flex items-center justify-between rounded-lg px-2 py-2', - 'cursor-pointer text-text-primary 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', - isCustomEnabled && 'cursor-not-allowed opacity-50', - )} - > -
- - {localize('com_ui_include_shadcnui' as any)} -
-
- - {/* Custom Prompt Mode Option */} - { - event.preventDefault(); - event.stopPropagation(); - handleCustomToggle(); + + + + {isEnabled && ( + -
- - {localize('com_ui_custom_prompt_mode' as any)} +
+
+ {localize('com_ui_artifacts_options')} +
+ + {/* Include shadcn/ui Option */} + { + event.preventDefault(); + event.stopPropagation(); + handleShadcnToggle(); + }} + disabled={isCustomEnabled} + className={cn( + 'mb-1 flex items-center justify-between rounded-lg px-2 py-2', + 'cursor-pointer text-text-primary 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', + isCustomEnabled && 'cursor-not-allowed opacity-50', + )} + > +
+ + {localize('com_ui_include_shadcnui' as any)} +
+
+ + {/* Custom Prompt Mode Option */} + { + event.preventDefault(); + event.stopPropagation(); + handleCustomToggle(); + }} + className={cn( + 'flex items-center justify-between rounded-lg px-2 py-2', + 'cursor-pointer text-text-primary 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', + )} + > +
+ + {localize('com_ui_custom_prompt_mode' as any)} +
+
- -
-
- )} -
- ); -}; + + )} + +
+ ); + }, +); + +ArtifactsSubMenu.displayName = 'ArtifactsSubMenu'; export default React.memo(ArtifactsSubMenu); diff --git a/client/src/components/Chat/Input/MCPSubMenu.tsx b/client/src/components/Chat/Input/MCPSubMenu.tsx index 1d704f2a6d..3628b732a1 100644 --- a/client/src/components/Chat/Input/MCPSubMenu.tsx +++ b/client/src/components/Chat/Input/MCPSubMenu.tsx @@ -11,115 +11,115 @@ interface MCPSubMenuProps { placeholder?: string; } -const MCPSubMenu = ({ placeholder, ...props }: MCPSubMenuProps) => { - const { - configuredServers, - mcpValues, - isPinned, - setIsPinned, - placeholderText, - toggleServerSelection, - getServerStatusIconProps, - getConfigDialogProps, - } = useMCPServerManager(); +const MCPSubMenu = React.forwardRef( + ({ placeholder, ...props }, ref) => { + const { + configuredServers, + mcpValues, + isPinned, + setIsPinned, + placeholderText, + toggleServerSelection, + getServerStatusIconProps, + getConfigDialogProps, + } = useMCPServerManager(); - const menuStore = Ariakit.useMenuStore({ - focusLoop: true, - showTimeout: 100, - placement: 'right', - }); + const menuStore = Ariakit.useMenuStore({ + focusLoop: true, + showTimeout: 100, + placement: 'right', + }); - // Don't render if no MCP servers are configured - if (!configuredServers || configuredServers.length === 0) { - return null; - } + // Don't render if no MCP servers are configured + if (!configuredServers || configuredServers.length === 0) { + return null; + } - const configDialogProps = getConfigDialogProps(); + const configDialogProps = getConfigDialogProps(); - return ( - <> - - ) => { - e.stopPropagation(); - menuStore.toggle(); - }} - className="flex w-full cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-surface-hover" - /> - } - > -
- - {placeholder || placeholderText} - -
- -
- - {configuredServers.map((serverName) => { - const statusIconProps = getServerStatusIconProps(serverName); - const isSelected = mcpValues?.includes(serverName) ?? false; - - const statusIcon = statusIconProps && ; - - return ( - { - event.preventDefault(); - toggleServerSelection(serverName); + return ( +
+ + ) => { + e.stopPropagation(); + menuStore.toggle(); }} - 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 justify-between text-sm', - )} - > - + + + {configuredServers.map((serverName) => { + const statusIconProps = getServerStatusIconProps(serverName); + const isSelected = mcpValues?.includes(serverName) ?? false; + + const statusIcon = statusIconProps && ; + + return ( + { + event.preventDefault(); + toggleServerSelection(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 justify-between text-sm', + )} > - - {serverName} - - {statusIcon &&
{statusIcon}
} -
- ); - })} -
-
- {configDialogProps && } - - ); -}; +
+ + {serverName} +
+ {statusIcon &&
{statusIcon}
} + + ); + })} + + + {configDialogProps && } +
+ ); + }, +); + +MCPSubMenu.displayName = 'MCPSubMenu'; export default React.memo(MCPSubMenu);