@@ -33,7 +33,6 @@ import PlanView from "./plan";
3333import { McpServerSelector } from "../../features/McpServerSelector/McpServerSelector" ;
3434import { MCPAgentConfig , MCPServerInfo } from "../../features/McpServersConfig/types" ;
3535import { extractMcpServers } from "../../features/McpServersConfig/McpServersList" ;
36-
3736// Threshold for large text files (in characters)
3837const LARGE_TEXT_THRESHOLD = 1500 ;
3938
@@ -105,9 +104,7 @@ const ChatInput = React.forwardRef<{ focus: () => void }, ChatInputProps>(
105104 runStatus === "active" ||
106105 runStatus === "pausing" ||
107106 inputRequest ?. input_type === "approval" ;
108-
109107 const [ mcpServers , setMcpServers ] = React . useState < MCPServerInfo [ ] > ( [ ] ) ;
110-
111108 // Handle textarea auto-resize
112109 React . useEffect ( ( ) => {
113110 if ( textAreaRef . current ) {
@@ -156,19 +153,18 @@ const ChatInput = React.forwardRef<{ focus: () => void }, ChatInputProps>(
156153 setIsLoading ( false ) ;
157154 }
158155 } ;
159-
160156 const fetchMCPServers = async ( ) => {
161157 if ( ! user ?. email ) {
162158 console . error ( "User not authenticated" ) ;
163159 setIsLoading ( false ) ;
164160 return ;
165161 }
166-
162+
167163 try {
168164 setIsLoading ( true ) ;
169-
165+
170166 // Get user's latest settings from database
171- const settings = await settingsAPI . getSettings ( user . email ) ;
167+ const settings = await settingsAPI . getSettings ( user . email ) ;
172168 const mcpAgentConfigs : MCPAgentConfig [ ] = settings . mcp_agent_configs || [ ] ;
173169 const mcpServers = extractMcpServers ( mcpAgentConfigs ) ;
174170 setMcpServers ( mcpServers ) ;
@@ -178,10 +174,10 @@ const ChatInput = React.forwardRef<{ focus: () => void }, ChatInputProps>(
178174 setIsLoading ( false ) ;
179175 }
180176 } ;
181-
177+
182178 fetchMCPServers ( ) ;
183179 fetchAllPlans ( ) ;
184- } , [ userId , user ?. email ] ) ; // Added user?.email to properly track when user changes
180+ } , [ userId , user ?. email ] ) ;
185181
186182 // Add paste event listener for images and large text
187183 const handlePaste = ( e : React . ClipboardEvent < HTMLTextAreaElement > ) => {
@@ -604,13 +600,15 @@ const ChatInput = React.forwardRef<{ focus: () => void }, ChatInputProps>(
604600 } ;
605601 } , [ isRelevantPlansVisible ] ) ;
606602
603+ const goToMcpServersTab = React . useCallback ( ( ) => {
607604 onSubMenuChange ( "mcp_servers" ) ;
608- } , [ onSubMenuChange ] )
609-
605+ } , [ onSubMenuChange ] ) ;
606+
610607 const handleMcpServerSelectionChange = React . useCallback ( ( newSelection : string [ ] ) => {
611608 onSelectedMcpServersChange ( newSelection ) ;
612609 } , [ onSelectedMcpServersChange ] ) ;
613610
611+
614612 return (
615613 < div className = "mt-2 w-full relative" >
616614 { notificationContextHolder }
@@ -625,21 +623,52 @@ const ChatInput = React.forwardRef<{ focus: () => void }, ChatInputProps>(
625623 />
626624 ) }
627625
626+ { /* Selected MCP Tools Display */ }
627+ { selectedMcpServers . length > 0 && (
628+ < div
629+ className = { `-mb-2 mx-1 ${ darkMode === "dark" ? "bg-[#333333]" : "bg-gray-100"
630+ } rounded-t border-b-0 p-2 flex border flex-wrap gap-2`}
631+ >
632+ { selectedMcpServers . map ( ( serverName ) => (
633+ < div
634+ key = { serverName }
635+ className = { `flex items-center gap-1 ${ darkMode === "dark"
636+ ? "bg-[#444444] text-white"
637+ : "bg-white text-black"
638+ } rounded px-2 py-1 text-xs`}
639+ >
640+ < span className = "truncate max-w-[150px]" > 🔧 { serverName } </ span >
641+ { runStatus === "created" && (
642+ < Button
643+ type = "text"
644+ size = "small"
645+ className = "p-0 ml-1 flex items-center justify-center"
646+ onClick = { ( ) =>
647+ onSelectedMcpServersChange (
648+ selectedMcpServers . filter ( ( s ) => s !== serverName )
649+ )
650+ }
651+ icon = { < XIcon className = "w-3 h-3" /> }
652+ />
653+ ) }
654+ </ div >
655+ ) ) }
656+ </ div >
657+ ) }
658+
628659 { /* Attached Items Preview */ }
629660 { ( attachedPlan || fileList . length > 0 ) && (
630661 < div
631- className = { `-mb-2 mx-1 ${
632- darkMode === "dark" ? "bg-[#333333]" : "bg-gray-100"
633- } rounded-t border-b-0 p-2 flex border flex-wrap gap-2`}
662+ className = { `${ selectedMcpServers . length > 0 ? '-mt-2' : '-mb-2' } mx-1 ${ darkMode === "dark" ? "bg-[#333333]" : "bg-gray-100"
663+ } ${ selectedMcpServers . length > 0 ? 'rounded-b border-t-0' : 'rounded-t border-b-0' } p-2 flex border flex-wrap gap-2`}
634664 >
635665 { /* Attached Plan */ }
636666 { attachedPlan && (
637667 < div
638- className = { `flex items-center gap-1 ${
639- darkMode === "dark"
640- ? "bg-[#444444] text-white"
641- : "bg-white text-black"
642- } rounded px-2 py-1 text-xs cursor-pointer hover:opacity-80 transition-opacity`}
668+ className = { `flex items-center gap-1 ${ darkMode === "dark"
669+ ? "bg-[#444444] text-white"
670+ : "bg-white text-black"
671+ } rounded px-2 py-1 text-xs cursor-pointer hover:opacity-80 transition-opacity`}
643672 onClick = { handlePlanClick }
644673 >
645674 < span className = "truncate max-w-[150px]" >
@@ -662,11 +691,10 @@ const ChatInput = React.forwardRef<{ focus: () => void }, ChatInputProps>(
662691 { fileList . map ( ( file ) => (
663692 < div
664693 key = { file . uid }
665- className = { `flex items-center gap-1 ${
666- darkMode === "dark"
667- ? "bg-[#444444] text-white"
668- : "bg-white text-black"
669- } rounded px-2 py-1 text-xs`}
694+ className = { `flex items-center gap-1 ${ darkMode === "dark"
695+ ? "bg-[#444444] text-white"
696+ : "bg-white text-black"
697+ } rounded px-2 py-1 text-xs`}
670698 >
671699 { getFileIcon ( file ) }
672700 < span className = "truncate max-w-[150px]" > { file . name } </ span >
@@ -700,95 +728,80 @@ const ChatInput = React.forwardRef<{ focus: () => void }, ChatInputProps>(
700728 task = { attachedPlan . task || "" }
701729 plan = { attachedPlan . steps || [ ] }
702730 viewOnly = { true }
703- setPlan = { ( ) => { } }
731+ setPlan = { ( ) => { } }
704732 />
705733 ) }
706734 </ Modal >
707735
708- < div className = "mt-2 rounded shadow-sm" >
736+ < div className = "mt-2 rounded shadow-sm flex " >
709737 < div
710- className = { `flex flex-col w-full ${ dragOver ? "opacity-50" : "" } ` }
738+ className = { `flex w-full ${ dragOver ? "opacity-50" : "" } ` }
711739 onDragOver = { handleDragOver }
712740 onDragLeave = { handleDragLeave }
713741 onDrop = { handleDrop }
714742 >
715- { /* First row: Textarea full width */ }
716- < div className = { `w-full border-t border-l border-r border-accent rounded-t-lg ${
717- darkMode === "dark"
718- ? "bg-[#444444] text-white"
719- : "bg-white text-black"
720- } `} >
721- < form
722- onSubmit = { ( e ) => {
723- e . preventDefault ( ) ;
724- handleSubmit ( ) ;
725- } }
726- >
727- < textarea
728- id = "queryInput"
729- name = "queryInput"
730- onPaste = { handlePaste }
731- ref = { textAreaRef }
732- defaultValue = { "" }
733- onChange = { handleTextChange }
734- onKeyDown = { handleKeyDown }
735- className = { `flex items-center w-full resize-none p-2 ${
736- darkMode === "dark"
743+ < div className = "flex w-full" >
744+ < div className = "flex-1" >
745+ < form
746+ onSubmit = { ( e ) => {
747+ e . preventDefault ( ) ;
748+ handleSubmit ( ) ;
749+ } }
750+ >
751+ < textarea
752+ id = "queryInput"
753+ name = "queryInput"
754+ onPaste = { handlePaste }
755+ ref = { textAreaRef }
756+ defaultValue = { "" }
757+ onChange = { handleTextChange }
758+ onKeyDown = { handleKeyDown }
759+ className = { `flex items-center w-full resize-none border-l border-t border-b border-accent p-2 pl-5 rounded-l-lg ${ darkMode === "dark"
737760 ? "bg-[#444444] text-white"
738761 : "bg-white text-black"
739- } ${
740- isInputDisabled ? "cursor-not-allowed" : ""
741- } focus:outline-none rounded-t-lg`}
742- style = { {
743- maxHeight : "120px" ,
744- overflowY : "auto" ,
745- minHeight : "50px" ,
746- } }
747- placeholder = {
748- runStatus === "awaiting_input"
749- ? "Type your response here and let Magentic-UI know of any changes in the browser."
750- : enable_upload
751- ? dragOver
752- ? "Drop files here..."
753- : "Type your message here..."
754- : "Type your message here..."
755- }
756- disabled = { isInputDisabled }
757- />
758- </ form >
759- </ div >
760-
761- { /* Second row: MCP Selector (flexible) + Buttons (right-aligned) */ }
762- < div className = "flex w-full" >
763- { /* MCP Selector - flexible width */ }
764- < div className = { `flex-1 border-l border-b border-accent p-2 rounded-bl-lg flex justify-start items-center ${
765- darkMode === "dark"
766- ? "bg-[#444444] text-white"
767- : "bg-white text-black"
768- } `} >
769- < McpServerSelector
770- servers = { mcpServers }
771- onAddMcpServer = { goToMcpServersTab }
772- runStatus = { runStatus }
773- value = { selectedMcpServers }
774- onChange = { handleMcpServerSelectionChange }
775- />
762+ } ${ isInputDisabled ? "cursor-not-allowed" : ""
763+ } focus:outline-none`}
764+ style = { {
765+ maxHeight : "120px" ,
766+ overflowY : "auto" ,
767+ minHeight : "50px" ,
768+ } }
769+ placeholder = {
770+ runStatus === "awaiting_input"
771+ ? "Type your response here and let Magentic-UI know of any changes in the browser."
772+ : enable_upload
773+ ? dragOver
774+ ? "Drop files here..."
775+ : "Type your message here..."
776+ : "Type your message here..."
777+ }
778+ disabled = { isInputDisabled }
779+ />
780+ </ form >
776781 </ div >
777782
778- { /* Buttons - right-aligned */ }
779783 < div
780- className = { `flex items-center justify-center gap-2 border-r border-b border-accent px-2 rounded-br-lg ${
781- darkMode === "dark"
782- ? "bg-[#444444] text-white"
783- : "bg-white text-black"
784- } `}
784+ className = { `flex items-center justify-center gap-2 border-t border-r border-b border-accent px-2 rounded-r-lg ${ darkMode === "dark"
785+ ? "bg-[#444444] text-white"
786+ : "bg-white text-black"
787+ } `}
785788 >
789+
790+ { runStatus === "created" && (
791+ < McpServerSelector
792+ servers = { mcpServers }
793+ onAddMcpServer = { goToMcpServersTab }
794+ runStatus = { runStatus }
795+ value = { selectedMcpServers }
796+ onChange = { handleMcpServerSelectionChange }
797+ />
798+ ) }
799+
786800 { /* File upload button replaced with Dropdown */ }
787801 { enable_upload && (
788802 < div
789- className = { `${
790- isInputDisabled ? "pointer-events-none opacity-50" : ""
791- } `}
803+ className = { `${ isInputDisabled ? "pointer-events-none opacity-50" : ""
804+ } `}
792805 >
793806 < Dropdown
794807 overlay = {
@@ -857,11 +870,10 @@ const ChatInput = React.forwardRef<{ focus: () => void }, ChatInputProps>(
857870 type = "button"
858871 onClick = { handleSubmit }
859872 disabled = { isInputDisabled }
860- className = { `bg-magenta-800 transition duration-300 rounded flex justify-center items-center w-11 h-9 ${
861- isInputDisabled
862- ? "cursor-not-allowed"
863- : "hover:bg-magenta-900"
864- } `}
873+ className = { `bg-magenta-800 transition duration-300 rounded flex justify-center items-center w-11 h-9 ${ isInputDisabled
874+ ? "cursor-not-allowed"
875+ : "hover:bg-magenta-900"
876+ } `}
865877 >
866878 < PaperAirplaneIcon className = "h-6 w-6 text-white" />
867879 </ button >
@@ -882,4 +894,4 @@ const ChatInput = React.forwardRef<{ focus: () => void }, ChatInputProps>(
882894 }
883895) ;
884896
885- export default ChatInput ;
897+ export default ChatInput ;
0 commit comments