@@ -2,8 +2,6 @@ import React, { useState, useEffect, useMemo, useCallback } from "react"
22import { Button } from "@/components/ui/button"
33import {
44 VSCodeTextArea ,
5- VSCodeDropdown ,
6- VSCodeOption ,
75 VSCodeTextField ,
86 VSCodeCheckbox ,
97 VSCodeRadioGroup ,
@@ -29,6 +27,25 @@ import { Tab, TabContent, TabHeader } from "../common/Tab"
2927import i18next from "i18next"
3028import { useAppTranslation } from "@src/i18n/TranslationContext"
3129import { Trans } from "react-i18next"
30+ import {
31+ Command ,
32+ CommandEmpty ,
33+ CommandGroup ,
34+ CommandInput ,
35+ CommandItem ,
36+ CommandList ,
37+ Input ,
38+ Popover ,
39+ PopoverContent ,
40+ PopoverTrigger ,
41+ Select ,
42+ SelectContent ,
43+ SelectItem ,
44+ SelectTrigger ,
45+ SelectValue ,
46+ Textarea ,
47+ } from "../ui"
48+ import { ChevronsUpDown , X } from "lucide-react"
3249
3350// Get all available groups that should show in prompts view
3451const availableGroups = ( Object . keys ( TOOL_GROUPS ) as ToolGroup [ ] ) . filter ( ( group ) => ! TOOL_GROUPS [ group ] . alwaysAvailable )
@@ -73,6 +90,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
7390 const [ isCreateModeDialogOpen , setIsCreateModeDialogOpen ] = useState ( false )
7491 const [ activeSupportTab , setActiveSupportTab ] = useState < SupportPromptType > ( "ENHANCE" )
7592 const [ isSystemPromptDisclosureOpen , setIsSystemPromptDisclosureOpen ] = useState ( false )
93+ const [ searchValue , setSearchValue ] = useState ( "" )
94+ const [ open , onOpenChange ] = useState ( false )
7695
7796 // Direct update functions
7897 const updateAgentPrompt = useCallback (
@@ -471,23 +490,80 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
471490 </ div >
472491
473492 < div className = "flex gap-2 items-center mb-3 flex-wrap py-1" >
474- { modes . map ( ( modeConfig ) => {
475- const isActive = mode === modeConfig . slug
476- return (
477- < button
478- key = { modeConfig . slug }
479- data-testid = { `${ modeConfig . slug } -tab` }
480- data-active = { isActive ? "true" : "false" }
481- onClick = { ( ) => handleModeSwitch ( modeConfig ) }
482- className = { `px-2 py-1 border-none rounded cursor-pointer font-bold ${
483- isActive
484- ? "bg-vscode-button-background text-vscode-button-foreground opacity-100"
485- : "bg-transparent text-vscode-foreground opacity-80"
486- } `} >
487- { modeConfig . name }
488- </ button >
489- )
490- } ) }
493+ < Popover open = { open } onOpenChange = { onOpenChange } >
494+ < PopoverTrigger asChild >
495+ < Button
496+ variant = "combobox"
497+ role = "combobox"
498+ aria-expanded = { open }
499+ className = "grow justify-between"
500+ // Use select-component data-testid for test compatibility
501+ data-testid = "select-component" >
502+ < div > { getCurrentMode ( ) ?. name } </ div >
503+ < ChevronsUpDown className = "opacity-50" />
504+ </ Button >
505+ </ PopoverTrigger >
506+ < PopoverContent className = "p-0 w-[var(--radix-popover-trigger-width)]" >
507+ < Command >
508+ < div className = "relative" >
509+ < CommandInput
510+ value = { searchValue }
511+ onValueChange = { setSearchValue }
512+ placeholder = { t ( "settings:providers.searchPlaceholder" ) }
513+ className = "h-9 mr-4"
514+ data-testid = "profile-search-input"
515+ />
516+ { searchValue . length > 0 && (
517+ < div className = "absolute right-2 top-0 bottom-0 flex items-center justify-center" >
518+ < X
519+ className = "text-vscode-input-foreground opacity-50 hover:opacity-100 size-4 p-0.5 cursor-pointer"
520+ onClick = { ( ) => setSearchValue ( "" ) }
521+ />
522+ </ div >
523+ ) }
524+ </ div >
525+ < CommandList >
526+ < CommandEmpty >
527+ { searchValue && (
528+ < div className = "py-2 px-1 text-sm" >
529+ { t ( "settings:providers.noMatchFound" ) }
530+ </ div >
531+ ) }
532+ </ CommandEmpty >
533+ < CommandGroup >
534+ { modes
535+ . filter ( ( modeConfig ) =>
536+ searchValue
537+ ? modeConfig . name
538+ . toLowerCase ( )
539+ . includes ( searchValue . toLowerCase ( ) ) ||
540+ modeConfig . slug
541+ . toLowerCase ( )
542+ . includes ( searchValue . toLowerCase ( ) )
543+ : true ,
544+ )
545+ . map ( ( config ) => (
546+ < CommandItem
547+ key = { config . name }
548+ value = { config . slug }
549+ onSelect = { ( value ) => {
550+ handleModeSwitch (
551+ modes . find ( ( m ) => m . slug === value ) || modes [ 0 ] ,
552+ )
553+ onOpenChange ( false )
554+ } }
555+ data-testid = { `profile-option-${ config . slug } ` } >
556+ < div className = "flex-1 flex items-center justify-between" >
557+ { config . name }
558+ < span className = "text-foreground/70" > { config . slug } </ span >
559+ </ div >
560+ </ CommandItem >
561+ ) ) }
562+ </ CommandGroup >
563+ </ CommandList >
564+ </ Command >
565+ </ PopoverContent >
566+ </ Popover >
491567 </ div >
492568 </ div >
493569
@@ -498,7 +574,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
498574 < div className = "flex-1" >
499575 < div className = "font-bold mb-1" > { t ( "prompts:createModeDialog.name.label" ) } </ div >
500576 < div className = "flex gap-2" >
501- < VSCodeTextField
577+ < Input
502578 value = { getModeProperty ( findModeBySlug ( mode , customModes ) , "name" ) ?? "" }
503579 onChange = { ( e : Event | React . FormEvent < HTMLElement > ) => {
504580 const target =
@@ -553,15 +629,15 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
553629 < div className = "text-sm text-vscode-descriptionForeground mb-2" >
554630 { t ( "prompts:roleDefinition.description" ) }
555631 </ div >
556- < VSCodeTextArea
632+ < Textarea
557633 value = { ( ( ) => {
558634 const customMode = findModeBySlug ( mode , customModes )
559635 const prompt = customModePrompts ?. [ mode ] as PromptComponent
560636 return customMode ?. roleDefinition ?? prompt ?. roleDefinition ?? getRoleDefinition ( mode )
561637 } ) ( ) }
562638 onChange = { ( e ) => {
563639 const value =
564- ( e as CustomEvent ) ?. detail ?. target ?. value ||
640+ ( e as unknown as CustomEvent ) ?. detail ?. target ?. value ||
565641 ( ( e as any ) . target as HTMLTextAreaElement ) . value
566642 const customMode = findModeBySlug ( mode , customModes )
567643 if ( customMode ) {
@@ -578,8 +654,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
578654 } )
579655 }
580656 } }
657+ className = "resize-y"
581658 rows = { 4 }
582- resize = "vertical"
583659 style = { { width : "100%" } }
584660 data-testid = { `${ getCurrentMode ( ) ?. slug || "code" } -prompt-textarea` }
585661 />
@@ -591,22 +667,25 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
591667 { t ( "prompts:apiConfiguration.title" ) }
592668 </ div >
593669 < div style = { { marginBottom : "8px" } } >
594- < VSCodeDropdown
595- value = { currentApiConfigName || "" }
596- onChange = { ( e : any ) => {
597- const value = e . detail ?. target ?. value || e . target ?. value
670+ < Select
671+ value = { currentApiConfigName }
672+ onValueChange = { ( value ) => {
598673 vscode . postMessage ( {
599674 type : "loadApiConfiguration" ,
600675 text : value ,
601676 } )
602- } }
603- className = "w-full" >
604- { ( listApiConfigMeta || [ ] ) . map ( ( config ) => (
605- < VSCodeOption key = { config . id } value = { config . name } >
606- { config . name }
607- </ VSCodeOption >
608- ) ) }
609- </ VSCodeDropdown >
677+ } } >
678+ < SelectTrigger className = "w-full" >
679+ < SelectValue placeholder = { t ( "settings:common.select" ) } />
680+ </ SelectTrigger >
681+ < SelectContent >
682+ { ( listApiConfigMeta || [ ] ) . map ( ( config ) => (
683+ < SelectItem key = { config . id } value = { config . name } >
684+ { config . name }
685+ </ SelectItem >
686+ ) ) }
687+ </ SelectContent >
688+ </ Select >
610689 < div className = "text-xs mt-1.5 text-vscode-descriptionForeground" >
611690 { t ( "prompts:apiConfiguration.select" ) }
612691 </ div >
@@ -736,7 +815,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
736815 modeName : getCurrentMode ( ) ?. name || "Code" ,
737816 } ) }
738817 </ div >
739- < VSCodeTextArea
818+ < Textarea
740819 value = { ( ( ) => {
741820 const customMode = findModeBySlug ( mode , customModes )
742821 const prompt = customModePrompts ?. [ mode ] as PromptComponent
@@ -748,7 +827,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
748827 } ) ( ) }
749828 onChange = { ( e ) => {
750829 const value =
751- ( e as CustomEvent ) ?. detail ?. target ?. value ||
830+ ( e as unknown as CustomEvent ) ?. detail ?. target ?. value ||
752831 ( ( e as any ) . target as HTMLTextAreaElement ) . value
753832 const customMode = findModeBySlug ( mode , customModes )
754833 if ( customMode ) {
@@ -768,8 +847,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
768847 }
769848 } }
770849 rows = { 4 }
771- resize = "vertical"
772- style = { { width : "100%" } }
850+ className = "w-full resize-y"
773851 data-testid = { `${ getCurrentMode ( ) ?. slug || "code" } -custom-instructions-textarea` }
774852 />
775853 < div
@@ -905,11 +983,11 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
905983 < div className = "text-sm text-vscode-descriptionForeground mb-2" >
906984 { t ( "prompts:globalCustomInstructions.description" , { language : i18next . language } ) }
907985 </ div >
908- < VSCodeTextArea
986+ < Textarea
909987 value = { customInstructions ?? "" }
910988 onChange = { ( e ) => {
911989 const value =
912- ( e as CustomEvent ) ?. detail ?. target ?. value ||
990+ ( e as unknown as CustomEvent ) ?. detail ?. target ?. value ||
913991 ( ( e as any ) . target as HTMLTextAreaElement ) . value
914992 setCustomInstructions ( value || undefined )
915993 vscode . postMessage ( {
@@ -918,8 +996,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
918996 } )
919997 } }
920998 rows = { 4 }
921- resize = "vertical"
922- className = "w-full"
999+ className = "w-full resize-y"
9231000 data-testid = "global-custom-instructions-textarea"
9241001 />
9251002 < div className = "text-xs text-vscode-descriptionForeground mt-1.5 mb-10" >
@@ -968,28 +1045,20 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
9681045 flexWrap : "wrap" ,
9691046 padding : "4px 0" ,
9701047 } } >
971- { Object . keys ( supportPrompt . default ) . map ( ( type ) => (
972- < button
973- key = { type }
974- data-testid = { `${ type } -tab` }
975- data-active = { activeSupportTab === type ? "true" : "false" }
976- onClick = { ( ) => setActiveSupportTab ( type as SupportPromptType ) }
977- style = { {
978- padding : "4px 8px" ,
979- border : "none" ,
980- background : activeSupportTab === type ? "var(--vscode-button-background)" : "none" ,
981- color :
982- activeSupportTab === type
983- ? "var(--vscode-button-foreground)"
984- : "var(--vscode-foreground)" ,
985- cursor : "pointer" ,
986- opacity : activeSupportTab === type ? 1 : 0.8 ,
987- borderRadius : "3px" ,
988- fontWeight : "bold" ,
989- } } >
990- { t ( `prompts:supportPrompts.types.${ type } .label` ) }
991- </ button >
992- ) ) }
1048+ < Select
1049+ value = { activeSupportTab }
1050+ onValueChange = { ( type ) => setActiveSupportTab ( type as SupportPromptType ) } >
1051+ < SelectTrigger className = "w-full" >
1052+ < SelectValue placeholder = { t ( "settings:common.select" ) } />
1053+ </ SelectTrigger >
1054+ < SelectContent >
1055+ { Object . keys ( supportPrompt . default ) . map ( ( type ) => (
1056+ < SelectItem key = { type } value = { type } >
1057+ { t ( `prompts:supportPrompts.types.${ type } .label` ) }
1058+ </ SelectItem >
1059+ ) ) }
1060+ </ SelectContent >
1061+ </ Select >
9931062 </ div >
9941063
9951064 { /* Support prompt description */ }
@@ -1021,18 +1090,17 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
10211090 </ Button >
10221091 </ div >
10231092
1024- < VSCodeTextArea
1093+ < Textarea
10251094 value = { getSupportPromptValue ( activeSupportTab ) }
10261095 onChange = { ( e ) => {
10271096 const value =
1028- ( e as CustomEvent ) ?. detail ?. target ?. value ||
1097+ ( e as unknown as CustomEvent ) ?. detail ?. target ?. value ||
10291098 ( ( e as any ) . target as HTMLTextAreaElement ) . value
10301099 const trimmedValue = value . trim ( )
10311100 updateSupportPrompt ( activeSupportTab , trimmedValue || undefined )
10321101 } }
10331102 rows = { 6 }
1034- resize = "vertical"
1035- style = { { width : "100%" } }
1103+ className = "resize-y w-full"
10361104 />
10371105
10381106 { activeSupportTab === "ENHANCE" && (
@@ -1058,27 +1126,31 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
10581126 { t ( "prompts:supportPrompts.enhance.apiConfigDescription" ) }
10591127 </ div >
10601128 </ div >
1061- < VSCodeDropdown
1129+ < Select
10621130 value = { enhancementApiConfigId || "" }
1063- data-testid = "api-config-dropdown"
1064- onChange = { ( e : any ) => {
1065- const value = e . detail ?. target ?. value || e . target ?. value
1131+ onValueChange = { ( value ) => {
10661132 setEnhancementApiConfigId ( value )
10671133 vscode . postMessage ( {
10681134 type : "enhancementApiConfigId" ,
10691135 text : value ,
10701136 } )
1071- } }
1072- style = { { width : "300px" } } >
1073- < VSCodeOption value = "" >
1074- { t ( "prompts:supportPrompts.enhance.useCurrentConfig" ) }
1075- </ VSCodeOption >
1076- { ( listApiConfigMeta || [ ] ) . map ( ( config ) => (
1077- < VSCodeOption key = { config . id } value = { config . id } >
1078- { config . name }
1079- </ VSCodeOption >
1080- ) ) }
1081- </ VSCodeDropdown >
1137+ } } >
1138+ < SelectTrigger data-testid = "api-config-select" >
1139+ < SelectValue
1140+ placeholder = { t ( "prompts:supportPrompts.enhance.useCurrentConfig" ) }
1141+ />
1142+ </ SelectTrigger >
1143+ < SelectContent >
1144+ < SelectItem value = "" >
1145+ { t ( "prompts:supportPrompts.enhance.useCurrentConfig" ) }
1146+ </ SelectItem >
1147+ { ( listApiConfigMeta || [ ] ) . map ( ( config ) => (
1148+ < SelectItem key = { config . id } value = { config . id } >
1149+ { config . name }
1150+ </ SelectItem >
1151+ ) ) }
1152+ </ SelectContent >
1153+ </ Select >
10821154 </ div >
10831155 </ div >
10841156
0 commit comments