@@ -87,15 +87,15 @@ export function CustomToolModal({
8787 const [ codeError , setCodeError ] = useState < string | null > ( null )
8888 const [ isEditing , setIsEditing ] = useState ( false )
8989 const [ toolId , setToolId ] = useState < string | undefined > ( undefined )
90+ const [ initialJsonSchema , setInitialJsonSchema ] = useState ( '' )
91+ const [ initialFunctionCode , setInitialFunctionCode ] = useState ( '' )
9092 const [ showDeleteConfirm , setShowDeleteConfirm ] = useState ( false )
9193 const [ isSchemaPromptActive , setIsSchemaPromptActive ] = useState ( false )
9294 const [ schemaPromptInput , setSchemaPromptInput ] = useState ( '' )
93- const [ schemaPromptSummary , setSchemaPromptSummary ] = useState < string | null > ( null )
9495 const schemaPromptInputRef = useRef < HTMLInputElement | null > ( null )
9596
9697 const [ isCodePromptActive , setIsCodePromptActive ] = useState ( false )
9798 const [ codePromptInput , setCodePromptInput ] = useState ( '' )
98- const [ codePromptSummary , setCodePromptSummary ] = useState < string | null > ( null )
9999 const codePromptInputRef = useRef < HTMLInputElement | null > ( null )
100100
101101 const schemaGeneration = useWand ( {
@@ -272,12 +272,15 @@ try {
272272
273273 if ( initialValues ) {
274274 try {
275- setJsonSchema (
275+ const schemaValue =
276276 typeof initialValues . schema === 'string'
277277 ? initialValues . schema
278278 : JSON . stringify ( initialValues . schema , null , 2 )
279- )
280- setFunctionCode ( initialValues . code || '' )
279+ const codeValue = initialValues . code || ''
280+ setJsonSchema ( schemaValue )
281+ setFunctionCode ( codeValue )
282+ setInitialJsonSchema ( schemaValue )
283+ setInitialFunctionCode ( codeValue )
281284 setIsEditing ( true )
282285 setToolId ( initialValues . id )
283286 } catch ( error ) {
@@ -304,13 +307,13 @@ try {
304307 const resetForm = ( ) => {
305308 setJsonSchema ( '' )
306309 setFunctionCode ( '' )
310+ setInitialJsonSchema ( '' )
311+ setInitialFunctionCode ( '' )
307312 setSchemaError ( null )
308313 setCodeError ( null )
309314 setActiveSection ( 'schema' )
310315 setIsEditing ( false )
311316 setToolId ( undefined )
312- setSchemaPromptSummary ( null )
313- setCodePromptSummary ( null )
314317 setIsSchemaPromptActive ( false )
315318 setIsCodePromptActive ( false )
316319 setSchemaPromptInput ( '' )
@@ -376,6 +379,11 @@ try {
376379
377380 const isSchemaValid = useMemo ( ( ) => validateJsonSchema ( jsonSchema ) , [ jsonSchema ] )
378381
382+ const hasChanges = useMemo ( ( ) => {
383+ if ( ! isEditing ) return true
384+ return jsonSchema !== initialJsonSchema || functionCode !== initialFunctionCode
385+ } , [ isEditing , jsonSchema , initialJsonSchema , functionCode , initialFunctionCode ] )
386+
379387 const handleSave = async ( ) => {
380388 try {
381389 if ( ! jsonSchema ) {
@@ -483,17 +491,9 @@ try {
483491 }
484492
485493 onSave ( customTool )
486-
487- setSchemaPromptSummary ( null )
488- setCodePromptSummary ( null )
489-
490494 handleClose ( )
491495 } catch ( error ) {
492496 logger . error ( 'Error saving custom tool:' , { error } )
493-
494- setSchemaPromptSummary ( null )
495- setCodePromptSummary ( null )
496-
497497 const errorMessage = error instanceof Error ? error . message : 'Failed to save custom tool'
498498
499499 if ( errorMessage . includes ( 'Cannot change function name' ) ) {
@@ -667,6 +667,41 @@ try {
667667 }
668668 }
669669
670+ // Global keyboard handler for schema params dropdown (like EnvVarDropdown)
671+ useEffect ( ( ) => {
672+ if ( ! showSchemaParams || schemaParameters . length === 0 ) return
673+
674+ const handleKeyboardEvent = ( e : KeyboardEvent ) => {
675+ switch ( e . key ) {
676+ case 'ArrowDown' :
677+ e . preventDefault ( )
678+ e . stopPropagation ( )
679+ setSchemaParamSelectedIndex ( ( prev ) => Math . min ( prev + 1 , schemaParameters . length - 1 ) )
680+ break
681+ case 'ArrowUp' :
682+ e . preventDefault ( )
683+ e . stopPropagation ( )
684+ setSchemaParamSelectedIndex ( ( prev ) => Math . max ( prev - 1 , 0 ) )
685+ break
686+ case 'Enter' :
687+ e . preventDefault ( )
688+ e . stopPropagation ( )
689+ if ( schemaParamSelectedIndex >= 0 && schemaParamSelectedIndex < schemaParameters . length ) {
690+ handleSchemaParamSelect ( schemaParameters [ schemaParamSelectedIndex ] . name )
691+ }
692+ break
693+ case 'Escape' :
694+ e . preventDefault ( )
695+ e . stopPropagation ( )
696+ setShowSchemaParams ( false )
697+ break
698+ }
699+ }
700+
701+ window . addEventListener ( 'keydown' , handleKeyboardEvent , true )
702+ return ( ) => window . removeEventListener ( 'keydown' , handleKeyboardEvent , true )
703+ } , [ showSchemaParams , schemaParamSelectedIndex , schemaParameters ] )
704+
670705 const handleKeyDown = ( e : React . KeyboardEvent ) => {
671706 const isSchemaPromptVisible = activeSection === 'schema' && schemaGeneration . isPromptVisible
672707 const isCodePromptVisible = activeSection === 'code' && codeGeneration . isPromptVisible
@@ -709,27 +744,32 @@ try {
709744 e . preventDefault ( )
710745 e . stopPropagation ( )
711746 setSchemaParamSelectedIndex ( ( prev ) => Math . min ( prev + 1 , schemaParameters . length - 1 ) )
712- break
747+ return
713748 case 'ArrowUp' :
714749 e . preventDefault ( )
715750 e . stopPropagation ( )
716751 setSchemaParamSelectedIndex ( ( prev ) => Math . max ( prev - 1 , 0 ) )
717- break
752+ return
718753 case 'Enter' :
719754 e . preventDefault ( )
720755 e . stopPropagation ( )
721756 if ( schemaParamSelectedIndex >= 0 && schemaParamSelectedIndex < schemaParameters . length ) {
722757 const selectedParam = schemaParameters [ schemaParamSelectedIndex ]
723758 handleSchemaParamSelect ( selectedParam . name )
724759 }
725- break
760+ return
726761 case 'Escape' :
727762 e . preventDefault ( )
728763 e . stopPropagation ( )
729764 setShowSchemaParams ( false )
765+ return
766+ case ' ' :
767+ case 'Tab' :
768+ // Close dropdown but let the key event continue (don't prevent default)
769+ // This allows the space/tab character to be typed normally
770+ setShowSchemaParams ( false )
730771 break
731772 }
732- return
733773 }
734774
735775 if ( showEnvVars || showTags ) {
@@ -743,7 +783,7 @@ try {
743783 const handleSchemaWandClick = ( ) => {
744784 if ( schemaGeneration . isLoading || schemaGeneration . isStreaming ) return
745785 setIsSchemaPromptActive ( true )
746- setSchemaPromptInput ( schemaPromptSummary ?? '' )
786+ setSchemaPromptInput ( '' )
747787 setTimeout ( ( ) => {
748788 schemaPromptInputRef . current ?. focus ( )
749789 } , 0 )
@@ -762,7 +802,6 @@ try {
762802 const handleSchemaPromptSubmit = ( ) => {
763803 const trimmedPrompt = schemaPromptInput . trim ( )
764804 if ( ! trimmedPrompt || schemaGeneration . isLoading || schemaGeneration . isStreaming ) return
765- setSchemaPromptSummary ( trimmedPrompt )
766805 schemaGeneration . generateStream ( { prompt : trimmedPrompt } )
767806 setSchemaPromptInput ( '' )
768807 setIsSchemaPromptActive ( false )
@@ -782,7 +821,7 @@ try {
782821 const handleCodeWandClick = ( ) => {
783822 if ( codeGeneration . isLoading || codeGeneration . isStreaming ) return
784823 setIsCodePromptActive ( true )
785- setCodePromptInput ( codePromptSummary ?? '' )
824+ setCodePromptInput ( '' )
786825 setTimeout ( ( ) => {
787826 codePromptInputRef . current ?. focus ( )
788827 } , 0 )
@@ -801,7 +840,6 @@ try {
801840 const handleCodePromptSubmit = ( ) => {
802841 const trimmedPrompt = codePromptInput . trim ( )
803842 if ( ! trimmedPrompt || codeGeneration . isLoading || codeGeneration . isStreaming ) return
804- setCodePromptSummary ( trimmedPrompt )
805843 codeGeneration . generateStream ( { prompt : trimmedPrompt } )
806844 setCodePromptInput ( '' )
807845 setIsCodePromptActive ( false )
@@ -1130,6 +1168,30 @@ try {
11301168 collisionPadding = { 6 }
11311169 onOpenAutoFocus = { ( e ) => e . preventDefault ( ) }
11321170 onCloseAutoFocus = { ( e ) => e . preventDefault ( ) }
1171+ onKeyDown = { ( e ) => {
1172+ if ( e . key === 'ArrowDown' ) {
1173+ e . preventDefault ( )
1174+ setSchemaParamSelectedIndex ( ( prev ) =>
1175+ Math . min ( prev + 1 , schemaParameters . length - 1 )
1176+ )
1177+ } else if ( e . key === 'ArrowUp' ) {
1178+ e . preventDefault ( )
1179+ setSchemaParamSelectedIndex ( ( prev ) => Math . max ( prev - 1 , 0 ) )
1180+ } else if ( e . key === 'Enter' ) {
1181+ e . preventDefault ( )
1182+ if (
1183+ schemaParamSelectedIndex >= 0 &&
1184+ schemaParamSelectedIndex < schemaParameters . length
1185+ ) {
1186+ handleSchemaParamSelect (
1187+ schemaParameters [ schemaParamSelectedIndex ] . name
1188+ )
1189+ }
1190+ } else if ( e . key === 'Escape' ) {
1191+ e . preventDefault ( )
1192+ setShowSchemaParams ( false )
1193+ }
1194+ } }
11331195 >
11341196 < PopoverScrollArea >
11351197 < PopoverSection > Available Parameters</ PopoverSection >
@@ -1211,7 +1273,7 @@ try {
12111273 < Button
12121274 variant = 'tertiary'
12131275 onClick = { handleSave }
1214- disabled = { ! isSchemaValid || ! ! schemaError }
1276+ disabled = { ! isSchemaValid || ! ! schemaError || ! hasChanges }
12151277 >
12161278 { isEditing ? 'Update Tool' : 'Save Tool' }
12171279 </ Button >
0 commit comments