@@ -34,8 +34,9 @@ import { MentionNode } from "./lexical/MentionNode"
3434import { LexicalMentionPlugin , type MentionInfo , type LexicalMentionPluginRef } from "./lexical/LexicalMentionPlugin"
3535import { LexicalSelectAllPlugin } from "./lexical/LexicalSelectAllPlugin"
3636import { LexicalPromptHistoryPlugin } from "./lexical/LexicalPromptHistoryPlugin"
37+ import { LexicalContextMenuPlugin } from "./lexical/LexicalContextMenuPlugin"
3738import ContextMenu from "./ContextMenu"
38- import { ContextMenuOptionType , getContextMenuOptions , SearchResult } from "@/utils/context-mentions"
39+ import { ContextMenuOptionType , SearchResult } from "@/utils/context-mentions"
3940import { MAX_IMAGES_PER_MESSAGE } from "./ChatView"
4041import { CloudAccountSwitcher } from "../cloud/CloudAccountSwitcher"
4142import { ChatContextBar } from "./ChatContextBar"
@@ -112,7 +113,6 @@ export const ChatLexicalTextArea = forwardRef<LexicalEditor, ChatTextAreaProps>(
112113 const [ searchLoading , setSearchLoading ] = useState ( false )
113114 const [ searchRequestId , setSearchRequestId ] = useState < string > ( "" )
114115 const [ gitCommits , setGitCommits ] = useState < any [ ] > ( [ ] )
115- const contextMenuContainerRef = useRef < HTMLDivElement > ( null )
116116 const searchTimeoutRef = useRef < NodeJS . Timeout | null > ( null )
117117 const [ isEnhancingPrompt , setIsEnhancingPrompt ] = useState ( false )
118118 const [ isMouseDownOnMenu , setIsMouseDownOnMenu ] = useState ( false )
@@ -530,102 +530,10 @@ export const ChatLexicalTextArea = forwardRef<LexicalEditor, ChatTextAreaProps>(
530530 [ searchQuery , setMode , setInputValue , mentionPluginRef ] ,
531531 )
532532
533- // Handle keyboard navigation for context menu
534- useEffect ( ( ) => {
535- const handleKeyDown = ( event : KeyboardEvent ) => {
536- if ( ! showContextMenu ) return
537-
538- if ( event . key === "Escape" ) {
539- setSelectedType ( null )
540- setSelectedMenuIndex ( 3 ) // File by default
541- return
542- }
543-
544- if ( event . key === "ArrowUp" || event . key === "ArrowDown" ) {
545- event . preventDefault ( )
546- setSelectedMenuIndex ( ( prevIndex ) => {
547- const direction = event . key === "ArrowUp" ? - 1 : 1
548- const options = getContextMenuOptions (
549- searchQuery ,
550- selectedType ,
551- queryItems ,
552- fileSearchResults ,
553- customModes ,
554- commands ,
555- )
556- const optionsLength = options . length
557-
558- if ( optionsLength === 0 ) return prevIndex
559-
560- // Find selectable options (non-URL types)
561- const selectableOptions = options . filter (
562- ( option ) =>
563- option . type !== ContextMenuOptionType . URL &&
564- option . type !== ContextMenuOptionType . NoResults &&
565- option . type !== ContextMenuOptionType . SectionHeader ,
566- )
567-
568- if ( selectableOptions . length === 0 ) return - 1 // No selectable options
569-
570- // Find the index of the next selectable option
571- const currentSelectableIndex = selectableOptions . findIndex (
572- ( option ) => option === options [ prevIndex ] ,
573- )
574-
575- const newSelectableIndex =
576- ( currentSelectableIndex + direction + selectableOptions . length ) % selectableOptions . length
577-
578- // Find the index of the selected option in the original options array
579- return options . findIndex ( ( option ) => option === selectableOptions [ newSelectableIndex ] )
580- } )
581- return
582- }
583-
584- if ( ( event . key === "Enter" || event . key === "Tab" ) && selectedMenuIndex !== - 1 ) {
585- event . preventDefault ( )
586- event . stopPropagation ( )
587- const selectedOption = getContextMenuOptions (
588- searchQuery ,
589- selectedType ,
590- queryItems ,
591- fileSearchResults ,
592- customModes ,
593- commands ,
594- ) [ selectedMenuIndex ]
595- if (
596- selectedOption &&
597- selectedOption . type !== ContextMenuOptionType . URL &&
598- selectedOption . type !== ContextMenuOptionType . NoResults &&
599- selectedOption . type !== ContextMenuOptionType . SectionHeader
600- ) {
601- handleMentionSelect ( selectedOption . type , selectedOption . value )
602- }
603- return
604- }
605- }
606-
607- document . addEventListener ( "keydown" , handleKeyDown , { capture : true } )
608- return ( ) => document . removeEventListener ( "keydown" , handleKeyDown , { capture : true } )
609- } , [
610- showContextMenu ,
611- searchQuery ,
612- selectedMenuIndex ,
613- selectedType ,
614- queryItems ,
615- fileSearchResults ,
616- customModes ,
617- commands ,
618- handleMentionSelect ,
619- ] )
620-
621533 // Effect to handle clicks outside the context menu to close it
622534 useEffect ( ( ) => {
623- const handleClickOutside = ( event : MouseEvent ) => {
624- if (
625- contextMenuContainerRef . current &&
626- ! contextMenuContainerRef . current . contains ( event . target as Node ) &&
627- ! isMouseDownOnMenu
628- ) {
535+ const handleClickOutside = ( ) => {
536+ if ( ! isMouseDownOnMenu ) {
629537 setShowContextMenu ( false )
630538 }
631539 }
@@ -708,7 +616,6 @@ export const ChatLexicalTextArea = forwardRef<LexicalEditor, ChatTextAreaProps>(
708616 } } >
709617 { showContextMenu && (
710618 < div
711- ref = { contextMenuContainerRef }
712619 onMouseDown = { handleMenuMouseDown }
713620 className = { cn (
714621 "absolute" ,
@@ -837,6 +744,19 @@ export const ChatLexicalTextArea = forwardRef<LexicalEditor, ChatTextAreaProps>(
837744 promptHistory = { promptHistory }
838745 showContextMenu = { showContextMenu }
839746 />
747+ < LexicalContextMenuPlugin
748+ showContextMenu = { showContextMenu }
749+ searchQuery = { searchQuery }
750+ selectedMenuIndex = { selectedMenuIndex }
751+ setSelectedMenuIndex = { setSelectedMenuIndex }
752+ selectedType = { selectedType }
753+ setSelectedType = { setSelectedType }
754+ queryItems = { queryItems }
755+ fileSearchResults = { fileSearchResults }
756+ customModes = { customModes }
757+ commands = { commands }
758+ onMentionSelect = { handleMentionSelect }
759+ />
840760 < LexicalSelectAllPlugin />
841761 </ LexicalComposer >
842762
0 commit comments