({
value,
onChange,
disabled = false,
title,
triggerClassName = "",
modeShortcutText,
customModes,
customModePrompts,
disableSearch = false,
}: ModeSelectorProps)
| 31 | } |
| 32 | |
| 33 | export const ModeSelector = ({ |
| 34 | value, |
| 35 | onChange, |
| 36 | disabled = false, |
| 37 | title, |
| 38 | triggerClassName = "", |
| 39 | modeShortcutText, |
| 40 | customModes, |
| 41 | customModePrompts, |
| 42 | disableSearch = false, |
| 43 | }: ModeSelectorProps) => { |
| 44 | const [open, setOpen] = React.useState(false) |
| 45 | const [searchValue, setSearchValue] = React.useState("") |
| 46 | const searchInputRef = React.useRef<HTMLInputElement>(null) |
| 47 | const selectedItemRef = React.useRef<HTMLDivElement>(null) |
| 48 | const scrollContainerRef = React.useRef<HTMLDivElement>(null) |
| 49 | const lastNotifiedInvalidModeRef = React.useRef<string | null>(null) |
| 50 | const portalContainer = useRooPortal("roo-portal") |
| 51 | const { hasOpenedModeSelector, setHasOpenedModeSelector } = useExtensionState() |
| 52 | const { t } = useAppTranslation() |
| 53 | |
| 54 | const trackModeSelectorOpened = React.useCallback(() => { |
| 55 | // Track telemetry every time the mode selector is opened. |
| 56 | telemetryClient.capture(TelemetryEventName.MODE_SELECTOR_OPENED) |
| 57 | |
| 58 | // Track first-time usage for UI purposes. |
| 59 | if (!hasOpenedModeSelector) { |
| 60 | setHasOpenedModeSelector(true) |
| 61 | vscode.postMessage({ type: "hasOpenedModeSelector", bool: true }) |
| 62 | } |
| 63 | }, [hasOpenedModeSelector, setHasOpenedModeSelector]) |
| 64 | |
| 65 | // Get all modes including custom modes and merge custom prompt descriptions. |
| 66 | const modes = React.useMemo(() => { |
| 67 | const allModes = getAllModes(customModes) |
| 68 | |
| 69 | return allModes.map((mode) => ({ |
| 70 | ...mode, |
| 71 | description: customModePrompts?.[mode.slug]?.description ?? mode.description, |
| 72 | })) |
| 73 | }, [customModes, customModePrompts]) |
| 74 | |
| 75 | // Find the selected mode, falling back to default if current mode doesn't exist (e.g., after workspace switch) |
| 76 | const selectedMode = React.useMemo(() => { |
| 77 | return modes.find((mode) => mode.slug === value) ?? modes.find((mode) => mode.slug === defaultModeSlug) |
| 78 | }, [modes, value]) |
| 79 | |
| 80 | // Notify parent when current mode is invalid so it can update its state |
| 81 | React.useEffect(() => { |
| 82 | const isValidMode = modes.some((mode) => mode.slug === value) |
| 83 | |
| 84 | if (isValidMode) { |
| 85 | lastNotifiedInvalidModeRef.current = null |
| 86 | return |
| 87 | } |
| 88 | |
| 89 | if (lastNotifiedInvalidModeRef.current === value) { |
| 90 | return |
nothing calls this directly
no test coverage detected