diff --git a/webview-ui/src/components/chat/ModeSelector.tsx b/webview-ui/src/components/chat/ModeSelector.tsx index 336e9f8357..f48d0e42dc 100644 --- a/webview-ui/src/components/chat/ModeSelector.tsx +++ b/webview-ui/src/components/chat/ModeSelector.tsx @@ -1,8 +1,19 @@ -import React from "react" -import { ChevronUp, Check } from "lucide-react" +import React, { useState, useRef, useCallback } from "react" +import { ChevronUp, Check, X } from "lucide-react" import { cn } from "@/lib/utils" import { useRooPortal } from "@/components/ui/hooks/useRooPortal" -import { Popover, PopoverContent, PopoverTrigger, StandardTooltip } from "@/components/ui" +import { + Popover, + PopoverContent, + PopoverTrigger, + StandardTooltip, + Command, + CommandInput, + CommandList, + CommandEmpty, + CommandItem, + CommandGroup, +} from "@/components/ui" import { IconButton } from "./IconButton" import { vscode } from "@/utils/vscode" import { useExtensionState } from "@/context/ExtensionStateContext" @@ -33,7 +44,9 @@ export const ModeSelector = ({ customModes, customModePrompts, }: ModeSelectorProps) => { - const [open, setOpen] = React.useState(false) + const [open, setOpen] = useState(false) + const [searchValue, setSearchValue] = useState("") + const searchInputRef = useRef(null) const portalContainer = useRooPortal("roo-portal") const { hasOpenedModeSelector, setHasOpenedModeSelector } = useExtensionState() const { t } = useAppTranslation() @@ -61,6 +74,32 @@ export const ModeSelector = ({ // Find the selected mode const selectedMode = React.useMemo(() => modes.find((mode) => mode.slug === value), [modes, value]) + // Filter modes based on search + const filteredModes = React.useMemo(() => { + if (!searchValue) return modes + const searchLower = searchValue.toLowerCase() + return modes.filter( + (mode) => mode.name.toLowerCase().includes(searchLower) || mode.slug.toLowerCase().includes(searchLower), + ) + }, [modes, searchValue]) + + // Handler for clearing search input + const onClearSearch = useCallback(() => { + setSearchValue("") + searchInputRef.current?.focus() + }, []) + + // Handler for mode selection + const handleModeSelect = useCallback( + (modeSlug: string) => { + onChange(modeSlug as Mode) + setOpen(false) + // Clear search after selection + setTimeout(() => setSearchValue(""), 100) + }, + [onChange], + ) + const trigger = ( { if (isOpen) trackModeSelectorOpened() setOpen(isOpen) + // Clear search when closing + if (!isOpen) { + setTimeout(() => setSearchValue(""), 100) + } }} data-testid="mode-selector-root"> {title ? {trigger} : trigger} @@ -97,81 +140,116 @@ export const ModeSelector = ({ sideOffset={4} container={portalContainer} className="p-0 overflow-hidden min-w-80 max-w-9/10"> -
-
-
-

{t("chat:modeSelector.title")}

-
- { - window.postMessage( - { - type: "action", - action: "marketplaceButtonClicked", - values: { marketplaceTab: "mode" }, - }, - "*", - ) - - setOpen(false) - }} - /> - { - vscode.postMessage({ - type: "switchTab", - tab: "modes", - }) - setOpen(false) - }} - /> -
+ + {/* Header with title and info icon */} +
+
+

{t("chat:modeSelector.title")}

+ + {t("chat:modeSelector.description")} +
+ {modeShortcutText} +
+ } + side="left" + maxWidth={300}> + + +
+ + {/* Search input */} +
+ + {searchValue.length > 0 && ( +
+ +
+ )}
-

- {t("chat:modeSelector.description")} -
- {modeShortcutText} -

{/* Mode List */} -
- {modes.map((mode) => ( -
{ - onChange(mode.slug as Mode) - setOpen(false) - }} - data-testid="mode-selector-item"> -
-

{mode.name}

- {mode.description && ( -

- {mode.description} -

- )} + + + {searchValue && ( +
+ {t("chat:modeSelector.noMatchFound")}
- {mode.slug === value ? ( - - ) : ( -
- )} -
- ))} + )} +
+ + {filteredModes.map((mode) => ( + handleModeSelect(mode.slug)} + className={cn( + "p-2 cursor-pointer", + mode.slug === value && "bg-vscode-list-activeSelectionBackground", + )} + data-testid="mode-selector-item"> +
+
+

{mode.name}

+ {mode.description && ( +

+ {mode.description} +

+ )} +
+ {mode.slug === value ? ( + + ) : ( +
+ )} +
+ + ))} + + + + {/* Footer with marketplace and settings buttons */} +
+ { + window.postMessage( + { + type: "action", + action: "marketplaceButtonClicked", + values: { marketplaceTab: "mode" }, + }, + "*", + ) + setOpen(false) + }} + /> + { + vscode.postMessage({ + type: "switchTab", + tab: "modes", + }) + setOpen(false) + }} + />
-
+ ) diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index 10f7ad3dba..a5749a1714 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -116,7 +116,9 @@ "title": "Modes", "marketplace": "Marketplace de Modes", "settings": "Configuració de Modes", - "description": "Personalitats especialitzades que adapten el comportament de Roo." + "description": "Personalitats especialitzades que adapten el comportament de Roo.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "Error en llegir el fitxer:", "noValidImages": "No s'ha processat cap imatge vàlida", diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index 95620eefdf..a238172ddd 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -116,7 +116,9 @@ "title": "Modi", "marketplace": "Modus-Marketplace", "settings": "Modus-Einstellungen", - "description": "Spezialisierte Personas, die Roos Verhalten anpassen." + "description": "Spezialisierte Personas, die Roos Verhalten anpassen.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "Fehler beim Lesen der Datei:", "noValidImages": "Keine gültigen Bilder wurden verarbeitet", diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index aed3bcfdc5..71a8861a0d 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -118,7 +118,9 @@ "title": "Modes", "marketplace": "Mode Marketplace", "settings": "Mode Settings", - "description": "Specialized personas that tailor Roo's behavior." + "description": "Specialized personas that tailor Roo's behavior.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "enhancePromptDescription": "The 'Enhance Prompt' button helps improve your prompt by providing additional context, clarification, or rephrasing. Try typing a prompt in here and clicking the button again to see how it works.", "addImages": "Add images to message", diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index a091aed1a6..6cf776bcc1 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -116,7 +116,9 @@ "title": "Modos", "marketplace": "Marketplace de Modos", "settings": "Configuración de Modos", - "description": "Personalidades especializadas que adaptan el comportamiento de Roo." + "description": "Personalidades especializadas que adaptan el comportamiento de Roo.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "Error al leer el archivo:", "noValidImages": "No se procesaron imágenes válidas", diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 59b22149fb..34fb5d3785 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -116,7 +116,9 @@ "title": "Modes", "marketplace": "Marketplace de Modes", "settings": "Paramètres des Modes", - "description": "Personas spécialisés qui adaptent le comportement de Roo." + "description": "Personas spécialisés qui adaptent le comportement de Roo.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "Erreur lors de la lecture du fichier :", "noValidImages": "Aucune image valide n'a été traitée", diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 08c7cc030a..eb18ea45d9 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -116,7 +116,9 @@ "title": "मोड्स", "marketplace": "मोड मार्केटप्लेस", "settings": "मोड सेटिंग्स", - "description": "विशेष व्यक्तित्व जो Roo के व्यवहार को अनुकूलित करते हैं।" + "description": "विशेष व्यक्तित्व जो Roo के व्यवहार को अनुकूलित करते हैं।", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "फ़ाइल पढ़ने में त्रुटि:", "noValidImages": "कोई मान्य चित्र प्रोसेस नहीं किया गया", diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index e242ebcf6c..d92d1be91b 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -122,7 +122,9 @@ "title": "Mode", "marketplace": "Marketplace Mode", "settings": "Pengaturan Mode", - "description": "Persona khusus yang menyesuaikan perilaku Roo." + "description": "Persona khusus yang menyesuaikan perilaku Roo.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "addImages": "Tambahkan gambar ke pesan", "sendMessage": "Kirim pesan", diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 9f2d1b523a..6ac1f018f3 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -116,7 +116,9 @@ "title": "Modalità", "marketplace": "Marketplace delle Modalità", "settings": "Impostazioni Modalità", - "description": "Personalità specializzate che adattano il comportamento di Roo." + "description": "Personalità specializzate che adattano il comportamento di Roo.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "Errore nella lettura del file:", "noValidImages": "Nessuna immagine valida è stata elaborata", diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index 986b1d48bf..80df75e109 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -116,7 +116,9 @@ "title": "モード", "marketplace": "モードマーケットプレイス", "settings": "モード設定", - "description": "Rooの動作をカスタマイズする専門的なペルソナ。" + "description": "Rooの動作をカスタマイズする専門的なペルソナ。", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "ファイル読み込みエラー:", "noValidImages": "有効な画像が処理されませんでした", diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 295f11584c..f726869190 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -116,7 +116,9 @@ "title": "모드", "marketplace": "모드 마켓플레이스", "settings": "모드 설정", - "description": "Roo의 행동을 맞춤화하는 전문화된 페르소나." + "description": "Roo의 행동을 맞춤화하는 전문화된 페르소나.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "파일 읽기 오류:", "noValidImages": "처리된 유효한 이미지가 없습니다", diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index 9d8bd6b202..0642cec665 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -108,7 +108,9 @@ "title": "Modi", "marketplace": "Modus Marktplaats", "settings": "Modus Instellingen", - "description": "Gespecialiseerde persona's die het gedrag van Roo aanpassen." + "description": "Gespecialiseerde persona's die het gedrag van Roo aanpassen.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "addImages": "Afbeeldingen toevoegen aan bericht", "sendMessage": "Bericht verzenden", diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index a5460909fb..2aa5212f66 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -116,7 +116,9 @@ "title": "Tryby", "marketplace": "Marketplace Trybów", "settings": "Ustawienia Trybów", - "description": "Wyspecjalizowane persony, które dostosowują zachowanie Roo." + "description": "Wyspecjalizowane persony, które dostosowują zachowanie Roo.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "Błąd odczytu pliku:", "noValidImages": "Nie przetworzono żadnych prawidłowych obrazów", diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 96cb0c7660..1950b879ad 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -116,7 +116,9 @@ "title": "Modos", "marketplace": "Marketplace de Modos", "settings": "Configurações de Modos", - "description": "Personas especializadas que adaptam o comportamento do Roo." + "description": "Personas especializadas que adaptam o comportamento do Roo.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "Erro ao ler arquivo:", "noValidImages": "Nenhuma imagem válida foi processada", diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 90387a620c..8ea0505f04 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -108,7 +108,9 @@ "title": "Режимы", "marketplace": "Маркетплейс режимов", "settings": "Настройки режимов", - "description": "Специализированные персоны, которые настраивают поведение Roo." + "description": "Специализированные персоны, которые настраивают поведение Roo.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "addImages": "Добавить изображения к сообщению", "sendMessage": "Отправить сообщение", diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 2188756f7f..c5d7fc61a8 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -116,7 +116,9 @@ "title": "Modlar", "marketplace": "Mod Pazaryeri", "settings": "Mod Ayarları", - "description": "Roo'nun davranışını özelleştiren uzmanlaşmış kişilikler." + "description": "Roo'nun davranışını özelleştiren uzmanlaşmış kişilikler.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "Dosya okuma hatası:", "noValidImages": "Hiçbir geçerli resim işlenmedi", diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index cdab5bf1e5..af0db1c198 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -116,7 +116,9 @@ "title": "Chế độ", "marketplace": "Chợ Chế độ", "settings": "Cài đặt Chế độ", - "description": "Các nhân cách chuyên biệt điều chỉnh hành vi của Roo." + "description": "Các nhân cách chuyên biệt điều chỉnh hành vi của Roo.", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "Lỗi khi đọc tệp:", "noValidImages": "Không có hình ảnh hợp lệ nào được xử lý", diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 58945ddf12..1aff1c6faa 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -116,7 +116,9 @@ "title": "模式", "marketplace": "模式市场", "settings": "模式设置", - "description": "专门定制Roo行为的角色。" + "description": "专门定制Roo行为的角色。", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "读取文件时出错:", "noValidImages": "没有处理有效图片", diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index fab99e69ae..4e0c0a966c 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -116,7 +116,9 @@ "title": "模式", "marketplace": "模式市集", "settings": "模式設定", - "description": "專門定制Roo行為的角色。" + "description": "專門定制Roo行為的角色。", + "searchPlaceholder": "Search modes...", + "noMatchFound": "No modes found" }, "errorReadingFile": "讀取檔案時發生錯誤:", "noValidImages": "未處理到任何有效圖片",