@@ -89,6 +89,10 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
8989 commands,
9090 } = useExtensionState ( )
9191
92+ // Detect if we're on Mac for modifier key detection
93+ const isMac = typeof navigator !== "undefined" && navigator . platform . toUpperCase ( ) . indexOf ( "MAC" ) >= 0
94+ const [ isModifierPressed , setIsModifierPressed ] = useState ( false )
95+
9296 // Find the ID and display text for the currently selected API configuration.
9397 const { currentConfigId, displayName } = useMemo ( ( ) => {
9498 const currentConfig = listApiConfigMeta ?. find ( ( config ) => config . name === currentApiConfigName )
@@ -221,6 +225,54 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
221225 setInputValue,
222226 } )
223227
228+ // Handle modifier key detection for mention clicks
229+ const handleGlobalKeyDown = useCallback (
230+ ( e : KeyboardEvent ) => {
231+ if ( ( isMac && e . metaKey ) || ( ! isMac && e . ctrlKey ) ) {
232+ setIsModifierPressed ( true )
233+ }
234+ } ,
235+ [ isMac ] ,
236+ )
237+
238+ const handleGlobalKeyUp = useCallback (
239+ ( e : KeyboardEvent ) => {
240+ if ( ( isMac && ! e . metaKey ) || ( ! isMac && ! e . ctrlKey ) ) {
241+ setIsModifierPressed ( false )
242+ }
243+ } ,
244+ [ isMac ] ,
245+ )
246+
247+ // Add global event listeners for modifier key detection
248+ useEffect ( ( ) => {
249+ document . addEventListener ( "keydown" , handleGlobalKeyDown )
250+ document . addEventListener ( "keyup" , handleGlobalKeyUp )
251+
252+ return ( ) => {
253+ document . removeEventListener ( "keydown" , handleGlobalKeyDown )
254+ document . removeEventListener ( "keyup" , handleGlobalKeyUp )
255+ }
256+ } , [ handleGlobalKeyDown , handleGlobalKeyUp ] )
257+
258+ // Handle clicks on mentions in the highlight layer
259+ const handleMentionClick = useCallback (
260+ ( e : MouseEvent ) => {
261+ if ( ! isModifierPressed ) return
262+
263+ const target = e . target as HTMLElement
264+ if ( target . tagName === "MARK" && target . classList . contains ( "mention-context-textarea-highlight" ) ) {
265+ const mentionText = target . textContent
266+ if ( mentionText ) {
267+ // Remove @ symbol if present and send to VSCode
268+ const cleanText = mentionText . startsWith ( "@" ) ? mentionText . slice ( 1 ) : mentionText
269+ vscode . postMessage ( { type : "openMention" , text : cleanText } )
270+ }
271+ }
272+ } ,
273+ [ isModifierPressed ] ,
274+ )
275+
224276 // Fetch git commits when Git is selected or when typing a hash.
225277 useEffect ( ( ) => {
226278 if ( selectedType === ContextMenuOptionType . Git || / ^ [ a - f 0 - 9 ] + $ / i. test ( searchQuery ) ) {
@@ -717,11 +769,17 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
717769 return commands ?. some ( ( cmd ) => cmd . name === commandName ) || false
718770 }
719771
772+ // Determine the class to use based on modifier key state
773+ const mentionClass = `mention-context-textarea-highlight${ isModifierPressed ? " clickable" : "" } `
774+
720775 // Process the text to highlight mentions and valid commands
721776 let processedText = text
722777 . replace ( / \n $ / , "\n\n" )
723778 . replace ( / [ < > & ] / g, ( c ) => ( { "<" : "<" , ">" : ">" , "&" : "&" } ) [ c ] || c )
724- . replace ( mentionRegexGlobal , '<mark class="mention-context-textarea-highlight">$&</mark>' )
779+ . replace (
780+ mentionRegexGlobal ,
781+ `<mark class="${ mentionClass } " title="${ isModifierPressed ? `${ isMac ? "Cmd" : "Ctrl" } + Click to open` : `Hold ${ isMac ? "Cmd" : "Ctrl" } + Click to open` } ">$&</mark>` ,
782+ )
725783
726784 // Custom replacement for commands - only highlight valid ones
727785 processedText = processedText . replace ( commandRegexGlobal , ( match , commandName ) => {
@@ -733,10 +791,10 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
733791
734792 if ( startsWithSpace ) {
735793 // Keep the space but only highlight the command part
736- return ` <mark class="mention-context-textarea-highlight ">${ commandPart } </mark>`
794+ return ` <mark class="${ mentionClass } " title=" ${ isModifierPressed ? ` ${ isMac ? "Cmd" : "Ctrl" } + Click to open` : `Hold ${ isMac ? "Cmd" : "Ctrl" } + Click to open` } ">${ commandPart } </mark>`
737795 } else {
738796 // Highlight the entire command (starts at beginning of line)
739- return `<mark class="mention-context-textarea-highlight ">${ commandPart } </mark>`
797+ return `<mark class="${ mentionClass } " title=" ${ isModifierPressed ? ` ${ isMac ? "Cmd" : "Ctrl" } + Click to open` : `Hold ${ isMac ? "Cmd" : "Ctrl" } + Click to open` } ">${ commandPart } </mark>`
740798 }
741799 }
742800 return match // Return unhighlighted if command is not valid
@@ -746,12 +804,23 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
746804
747805 highlightLayerRef . current . scrollTop = textAreaRef . current . scrollTop
748806 highlightLayerRef . current . scrollLeft = textAreaRef . current . scrollLeft
749- } , [ commands ] )
807+ } , [ commands , isModifierPressed , isMac ] )
750808
751809 useLayoutEffect ( ( ) => {
752810 updateHighlights ( )
753811 } , [ inputValue , updateHighlights ] )
754812
813+ // Add click event listener to highlight layer for mention clicks
814+ useEffect ( ( ) => {
815+ const highlightLayer = highlightLayerRef . current
816+ if ( highlightLayer ) {
817+ highlightLayer . addEventListener ( "click" , handleMentionClick )
818+ return ( ) => {
819+ highlightLayer . removeEventListener ( "click" , handleMentionClick )
820+ }
821+ }
822+ } , [ handleMentionClick ] )
823+
755824 const updateCursorPosition = useCallback ( ( ) => {
756825 if ( textAreaRef . current ) {
757826 setCursorPosition ( textAreaRef . current . selectionStart )
0 commit comments