Skip to content

Commit 0a0c0da

Browse files
committed
feat: enhance ModeSelector with custom mode prompts and add tests
1 parent 4452bdc commit 0a0c0da

File tree

3 files changed

+80
-10
lines changed

3 files changed

+80
-10
lines changed

webview-ui/src/components/chat/ChatTextArea.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
7575
currentApiConfigName,
7676
listApiConfigMeta,
7777
customModes,
78+
customModePrompts,
7879
cwd,
7980
pinnedApiConfigs,
8081
togglePinnedApiConfig,
@@ -194,6 +195,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
194195
}
195196
}, [inputValue, sendingDisabled, setInputValue, t])
196197

198+
const allModes = useMemo(() => getAllModes(customModes), [customModes])
199+
197200
const queryItems = useMemo(() => {
198201
return [
199202
{ type: ContextMenuOptionType.Problems, value: "problems" },
@@ -323,7 +326,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
323326
selectedType,
324327
queryItems,
325328
fileSearchResults,
326-
getAllModes(customModes),
329+
allModes,
327330
)
328331
const optionsLength = options.length
329332

@@ -360,7 +363,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
360363
selectedType,
361364
queryItems,
362365
fileSearchResults,
363-
getAllModes(customModes),
366+
allModes,
364367
)[selectedMenuIndex]
365368
if (
366369
selectedOption &&
@@ -447,7 +450,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
447450
setInputValue,
448451
justDeletedSpaceAfterMention,
449452
queryItems,
450-
customModes,
453+
allModes,
451454
fileSearchResults,
452455
handleHistoryNavigation,
453456
resetHistoryNavigation,
@@ -846,7 +849,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
846849
setSelectedIndex={setSelectedMenuIndex}
847850
selectedType={selectedType}
848851
queryItems={queryItems}
849-
modes={getAllModes(customModes)}
852+
modes={allModes}
850853
loading={searchLoading}
851854
dynamicSearchResults={fileSearchResults}
852855
/>
@@ -1008,6 +1011,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
10081011
triggerClassName="w-full"
10091012
modeShortcutText={modeShortcutText}
10101013
customModes={customModes}
1014+
customModePrompts={customModePrompts}
10111015
/>
10121016
</div>
10131017

webview-ui/src/components/chat/ModeSelector.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import React from "react"
22
import { ChevronUp, Check } from "lucide-react"
3-
import { Mode, getAllModes } from "@roo/modes"
43
import { cn } from "@/lib/utils"
54
import { useRooPortal } from "@/components/ui/hooks/useRooPortal"
65
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui"
76
import { IconButton } from "./IconButton"
87
import { vscode } from "@/utils/vscode"
98
import { useExtensionState } from "@/context/ExtensionStateContext"
109
import { useAppTranslation } from "@/i18n/TranslationContext"
11-
import { ModeConfig } from "@roo-code/types"
10+
import { Mode, getAllModes } from "@roo/modes"
11+
import { ModeConfig, CustomModePrompts } from "@roo-code/types"
1212

1313
interface ModeSelectorProps {
1414
value: Mode
@@ -18,17 +18,19 @@ interface ModeSelectorProps {
1818
triggerClassName?: string
1919
modeShortcutText: string
2020
customModes?: ModeConfig[]
21+
customModePrompts?: CustomModePrompts
2122
}
2223

23-
export const ModeSelector: React.FC<ModeSelectorProps> = ({
24+
export const ModeSelector = ({
2425
value,
2526
onChange,
2627
disabled = false,
2728
title = "",
2829
triggerClassName = "",
2930
modeShortcutText,
3031
customModes,
31-
}) => {
32+
customModePrompts,
33+
}: ModeSelectorProps) => {
3234
const [open, setOpen] = React.useState(false)
3335
const portalContainer = useRooPortal("roo-portal")
3436
const { hasOpenedModeSelector, setHasOpenedModeSelector } = useExtensionState()
@@ -41,8 +43,14 @@ export const ModeSelector: React.FC<ModeSelectorProps> = ({
4143
}
4244
}
4345

44-
// Get all available modes
45-
const modes = React.useMemo(() => getAllModes(customModes), [customModes])
46+
// Get all modes including custom modes and merge custom prompt descriptions
47+
const modes = React.useMemo(() => {
48+
const allModes = getAllModes(customModes)
49+
return allModes.map((mode) => ({
50+
...mode,
51+
description: customModePrompts?.[mode.slug]?.description ?? mode.description,
52+
}))
53+
}, [customModes, customModePrompts])
4654

4755
// Find the selected mode
4856
const selectedMode = React.useMemo(() => modes.find((mode) => mode.slug === value), [modes, value])
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from "react"
2+
import { render, screen } from "@testing-library/react"
3+
import { describe, test, expect, vi } from "vitest"
4+
import ModeSelector from "../ModeSelector"
5+
import { Mode } from "@roo/modes"
6+
7+
// Mock the dependencies
8+
vi.mock("@/utils/vscode", () => ({
9+
vscode: {
10+
postMessage: vi.fn(),
11+
},
12+
}))
13+
14+
vi.mock("@/context/ExtensionStateContext", () => ({
15+
useExtensionState: () => ({
16+
hasOpenedModeSelector: false,
17+
setHasOpenedModeSelector: vi.fn(),
18+
}),
19+
}))
20+
21+
vi.mock("@/i18n/TranslationContext", () => ({
22+
useAppTranslation: () => ({
23+
t: (key: string) => key,
24+
}),
25+
}))
26+
27+
vi.mock("@/components/ui/hooks/useRooPortal", () => ({
28+
useRooPortal: () => document.body,
29+
}))
30+
31+
describe("ModeSelector", () => {
32+
test("shows custom description from customModePrompts", () => {
33+
const customModePrompts = {
34+
code: {
35+
description: "Custom code mode description",
36+
},
37+
}
38+
39+
render(
40+
<ModeSelector
41+
value={"code" as Mode}
42+
onChange={vi.fn()}
43+
modeShortcutText="Ctrl+M"
44+
customModePrompts={customModePrompts}
45+
/>,
46+
)
47+
48+
// The component should be rendered
49+
expect(screen.getByTestId("mode-selector-trigger")).toBeInTheDocument()
50+
})
51+
52+
test("falls back to default description when no custom prompt", () => {
53+
render(<ModeSelector value={"code" as Mode} onChange={vi.fn()} modeShortcutText="Ctrl+M" />)
54+
55+
// The component should be rendered
56+
expect(screen.getByTestId("mode-selector-trigger")).toBeInTheDocument()
57+
})
58+
})

0 commit comments

Comments
 (0)