@@ -904,6 +904,144 @@ describe("ChatTextArea", () => {
904904 } )
905905 } )
906906
907+ describe ( "slash command highlighting" , ( ) => {
908+ const mockCommands = [
909+ { name : "setup" , source : "project" , description : "Setup the project" } ,
910+ { name : "deploy" , source : "global" , description : "Deploy the application" } ,
911+ { name : "test-command" , source : "project" , description : "Test command with dash" } ,
912+ ]
913+
914+ beforeEach ( ( ) => {
915+ ; ( useExtensionState as ReturnType < typeof vi . fn > ) . mockReturnValue ( {
916+ filePaths : [ ] ,
917+ openedTabs : [ ] ,
918+ taskHistory : [ ] ,
919+ cwd : "/test/workspace" ,
920+ commands : mockCommands ,
921+ } )
922+ } )
923+
924+ it ( "should highlight valid slash commands" , ( ) => {
925+ const { getByTestId } = render ( < ChatTextArea { ...defaultProps } inputValue = "/setup the project" /> )
926+
927+ const highlightLayer = getByTestId ( "highlight-layer" )
928+ expect ( highlightLayer ) . toBeInTheDocument ( )
929+
930+ // The highlighting is applied via innerHTML, so we need to check the content
931+ // The valid command "/setup" should be highlighted
932+ expect ( highlightLayer . innerHTML ) . toContain ( '<mark class="mention-context-textarea-highlight">/setup</mark>' )
933+ } )
934+
935+ it ( "should not highlight invalid slash commands" , ( ) => {
936+ const { getByTestId } = render ( < ChatTextArea { ...defaultProps } inputValue = "/invalid command" /> )
937+
938+ const highlightLayer = getByTestId ( "highlight-layer" )
939+ expect ( highlightLayer ) . toBeInTheDocument ( )
940+
941+ // The invalid command "/invalid" should not be highlighted
942+ expect ( highlightLayer . innerHTML ) . not . toContain (
943+ '<mark class="mention-context-textarea-highlight">/invalid</mark>' ,
944+ )
945+ // But it should still contain the text without highlighting
946+ expect ( highlightLayer . innerHTML ) . toContain ( "/invalid" )
947+ } )
948+
949+ it ( "should highlight only the command portion, not arguments" , ( ) => {
950+ const { getByTestId } = render ( < ChatTextArea { ...defaultProps } inputValue = "/deploy to production" /> )
951+
952+ const highlightLayer = getByTestId ( "highlight-layer" )
953+ expect ( highlightLayer ) . toBeInTheDocument ( )
954+
955+ // Only "/deploy" should be highlighted, not "to production"
956+ expect ( highlightLayer . innerHTML ) . toContain (
957+ '<mark class="mention-context-textarea-highlight">/deploy</mark>' ,
958+ )
959+ expect ( highlightLayer . innerHTML ) . not . toContain (
960+ '<mark class="mention-context-textarea-highlight">/deploy to production</mark>' ,
961+ )
962+ } )
963+
964+ it ( "should handle commands with dashes and underscores" , ( ) => {
965+ const { getByTestId } = render ( < ChatTextArea { ...defaultProps } inputValue = "/test-command with args" /> )
966+
967+ const highlightLayer = getByTestId ( "highlight-layer" )
968+ expect ( highlightLayer ) . toBeInTheDocument ( )
969+
970+ // The command with dash should be highlighted
971+ expect ( highlightLayer . innerHTML ) . toContain (
972+ '<mark class="mention-context-textarea-highlight">/test-command</mark>' ,
973+ )
974+ } )
975+
976+ it ( "should be case-sensitive when matching commands" , ( ) => {
977+ const { getByTestId } = render ( < ChatTextArea { ...defaultProps } inputValue = "/Setup the project" /> )
978+
979+ const highlightLayer = getByTestId ( "highlight-layer" )
980+ expect ( highlightLayer ) . toBeInTheDocument ( )
981+
982+ // "/Setup" (capital S) should not be highlighted since the command is "setup" (lowercase)
983+ expect ( highlightLayer . innerHTML ) . not . toContain (
984+ '<mark class="mention-context-textarea-highlight">/Setup</mark>' ,
985+ )
986+ expect ( highlightLayer . innerHTML ) . toContain ( "/Setup" )
987+ } )
988+
989+ it ( "should highlight multiple valid commands in the same text" , ( ) => {
990+ const { getByTestId } = render ( < ChatTextArea { ...defaultProps } inputValue = "/setup first then /deploy" /> )
991+
992+ const highlightLayer = getByTestId ( "highlight-layer" )
993+ expect ( highlightLayer ) . toBeInTheDocument ( )
994+
995+ // Both valid commands should be highlighted
996+ expect ( highlightLayer . innerHTML ) . toContain ( '<mark class="mention-context-textarea-highlight">/setup</mark>' )
997+ expect ( highlightLayer . innerHTML ) . toContain (
998+ '<mark class="mention-context-textarea-highlight">/deploy</mark>' ,
999+ )
1000+ } )
1001+
1002+ it ( "should handle mixed valid and invalid commands" , ( ) => {
1003+ const { getByTestId } = render (
1004+ < ChatTextArea { ...defaultProps } inputValue = "/setup first then /invalid then /deploy" /> ,
1005+ )
1006+
1007+ const highlightLayer = getByTestId ( "highlight-layer" )
1008+ expect ( highlightLayer ) . toBeInTheDocument ( )
1009+
1010+ // Valid commands should be highlighted
1011+ expect ( highlightLayer . innerHTML ) . toContain ( '<mark class="mention-context-textarea-highlight">/setup</mark>' )
1012+ expect ( highlightLayer . innerHTML ) . toContain (
1013+ '<mark class="mention-context-textarea-highlight">/deploy</mark>' ,
1014+ )
1015+
1016+ // Invalid command should not be highlighted
1017+ expect ( highlightLayer . innerHTML ) . not . toContain (
1018+ '<mark class="mention-context-textarea-highlight">/invalid</mark>' ,
1019+ )
1020+ expect ( highlightLayer . innerHTML ) . toContain ( "/invalid" )
1021+ } )
1022+
1023+ it ( "should work when no commands are available" , ( ) => {
1024+ ; ( useExtensionState as ReturnType < typeof vi . fn > ) . mockReturnValue ( {
1025+ filePaths : [ ] ,
1026+ openedTabs : [ ] ,
1027+ taskHistory : [ ] ,
1028+ cwd : "/test/workspace" ,
1029+ commands : undefined ,
1030+ } )
1031+
1032+ const { getByTestId } = render ( < ChatTextArea { ...defaultProps } inputValue = "/setup the project" /> )
1033+
1034+ const highlightLayer = getByTestId ( "highlight-layer" )
1035+ expect ( highlightLayer ) . toBeInTheDocument ( )
1036+
1037+ // No commands should be highlighted when commands array is undefined
1038+ expect ( highlightLayer . innerHTML ) . not . toContain (
1039+ '<mark class="mention-context-textarea-highlight">/setup</mark>' ,
1040+ )
1041+ expect ( highlightLayer . innerHTML ) . toContain ( "/setup" )
1042+ } )
1043+ } )
1044+
9071045 describe ( "selectApiConfig" , ( ) => {
9081046 // Helper function to get the API config dropdown
9091047 const getApiConfigDropdown = ( ) => {
0 commit comments