feat: make enhance prompt button optional through experimental setting

- Add SHOW_ENHANCE_PROMPT_BUTTON experiment ID
- Update ChatTextArea to conditionally render button based on experiment
- Add translation keys for the new experimental setting
- Update tests to handle the new experiment configuration
- Default the setting to enabled to maintain current behavior

Fixes #6207
This commit is contained in:
Roo Code 2025-07-25 09:26:13 +00:00
parent d62a260576
commit 0e1c968fba
6 changed files with 127 additions and 45 deletions

View file

@ -6,7 +6,7 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js"
* ExperimentId
*/
export const experimentIds = ["powerSteering", "multiFileApplyDiff"] as const
export const experimentIds = ["powerSteering", "multiFileApplyDiff", "showEnhancePromptButton"] as const
export const experimentIdsSchema = z.enum(experimentIds)
@ -19,6 +19,7 @@ export type ExperimentId = z.infer<typeof experimentIdsSchema>
export const experimentsSchema = z.object({
powerSteering: z.boolean().optional(),
multiFileApplyDiff: z.boolean().optional(),
showEnhancePromptButton: z.boolean().optional(),
})
export type Experiments = z.infer<typeof experimentsSchema>

View file

@ -3,6 +3,7 @@ import type { AssertEqual, Equals, Keys, Values, ExperimentId, Experiments } fro
export const EXPERIMENT_IDS = {
MULTI_FILE_APPLY_DIFF: "multiFileApplyDiff",
POWER_STEERING: "powerSteering",
SHOW_ENHANCE_PROMPT_BUTTON: "showEnhancePromptButton",
} as const satisfies Record<string, ExperimentId>
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
@ -16,6 +17,7 @@ interface ExperimentConfig {
export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
MULTI_FILE_APPLY_DIFF: { enabled: false },
POWER_STEERING: { enabled: false },
SHOW_ENHANCE_PROMPT_BUTTON: { enabled: true },
}
export const experimentDefault = Object.fromEntries(

View file

@ -10,6 +10,7 @@ import { ExtensionMessage } from "@roo/ExtensionMessage"
import { vscode } from "@/utils/vscode"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useAppTranslation } from "@/i18n/TranslationContext"
import { EXPERIMENT_IDS, experiments } from "@roo/experiments"
import {
ContextMenuOptionType,
getContextMenuOptions,
@ -86,6 +87,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
togglePinnedApiConfig,
taskHistory,
clineMessages,
experiments: experimentsConfig,
} = useExtensionState()
// Find the ID and display text for the currently selected API configuration
@ -1109,29 +1111,31 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
onScroll={() => updateHighlights()}
/>
<div className="absolute top-1 right-1 z-30">
<StandardTooltip content={t("chat:enhancePrompt")}>
<button
aria-label={t("chat:enhancePrompt")}
disabled={sendingDisabled}
onClick={!sendingDisabled ? handleEnhancePrompt : undefined}
className={cn(
"relative inline-flex items-center justify-center",
"bg-transparent border-none p-1.5",
"rounded-md min-w-[28px] min-h-[28px]",
"opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground",
"transition-all duration-150",
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
!sendingDisabled && "cursor-pointer",
sendingDisabled &&
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
)}>
<WandSparkles className={cn("w-4 h-4", isEnhancingPrompt && "animate-spin")} />
</button>
</StandardTooltip>
</div>
{experiments.isEnabled(experimentsConfig, EXPERIMENT_IDS.SHOW_ENHANCE_PROMPT_BUTTON) && (
<div className="absolute top-1 right-1 z-30">
<StandardTooltip content={t("chat:enhancePrompt")}>
<button
aria-label={t("chat:enhancePrompt")}
disabled={sendingDisabled}
onClick={!sendingDisabled ? handleEnhancePrompt : undefined}
className={cn(
"relative inline-flex items-center justify-center",
"bg-transparent border-none p-1.5",
"rounded-md min-w-[28px] min-h-[28px]",
"opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground",
"transition-all duration-150",
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
!sendingDisabled && "cursor-pointer",
sendingDisabled &&
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
)}>
<WandSparkles className={cn("w-4 h-4", isEnhancingPrompt && "animate-spin")} />
</button>
</StandardTooltip>
</div>
)}
{!isEditMode && (
<div className="absolute bottom-1 right-1 z-30">

View file

@ -73,6 +73,9 @@ describe("ChatTextArea", () => {
},
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
})
@ -83,11 +86,50 @@ describe("ChatTextArea", () => {
openedTabs: [],
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
render(<ChatTextArea {...defaultProps} sendingDisabled={true} />)
const enhanceButton = getEnhancePromptButton()
expect(enhanceButton).toHaveClass("cursor-not-allowed")
})
it("should not be visible when experiment is disabled", () => {
;(useExtensionState as ReturnType<typeof vi.fn>).mockReturnValue({
filePaths: [],
openedTabs: [],
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: false,
},
})
render(<ChatTextArea {...defaultProps} />)
// The button should not exist in the DOM
const enhanceButton = screen.queryByRole("button", {
name: (_, element) => {
return element.querySelector(".lucide-wand-sparkles") !== null
},
})
expect(enhanceButton).not.toBeInTheDocument()
})
it("should be visible when experiment is enabled", () => {
;(useExtensionState as ReturnType<typeof vi.fn>).mockReturnValue({
filePaths: [],
openedTabs: [],
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
render(<ChatTextArea {...defaultProps} />)
const enhanceButton = getEnhancePromptButton()
expect(enhanceButton).toBeInTheDocument()
})
})
describe("handleEnhancePrompt", () => {
@ -103,6 +145,9 @@ describe("ChatTextArea", () => {
apiConfiguration,
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
render(<ChatTextArea {...defaultProps} inputValue="Test prompt" />)
@ -125,6 +170,9 @@ describe("ChatTextArea", () => {
},
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
render(<ChatTextArea {...defaultProps} inputValue="" />)
@ -147,6 +195,9 @@ describe("ChatTextArea", () => {
},
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
render(<ChatTextArea {...defaultProps} inputValue="Test prompt" />)
@ -174,6 +225,9 @@ describe("ChatTextArea", () => {
},
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
rerender(<ChatTextArea {...defaultProps} />)
@ -275,6 +329,9 @@ describe("ChatTextArea", () => {
filePaths: [],
openedTabs: [],
cwd: mockCwd,
experiments: {
showEnhancePromptButton: true,
},
})
mockConvertToMentionPath.mockClear()
})
@ -506,6 +563,9 @@ describe("ChatTextArea", () => {
taskHistory: [],
clineMessages: mockClineMessages,
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
})
@ -659,6 +719,9 @@ describe("ChatTextArea", () => {
taskHistory: [],
clineMessages: mixedClineMessages,
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
const setInputValue = vi.fn()
@ -687,6 +750,9 @@ describe("ChatTextArea", () => {
taskHistory: [],
clineMessages: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
const setInputValue = vi.fn()
@ -718,6 +784,9 @@ describe("ChatTextArea", () => {
taskHistory: [],
clineMessages: clineMessagesWithEmpty,
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
const setInputValue = vi.fn()
@ -752,6 +821,9 @@ describe("ChatTextArea", () => {
taskHistory: mockTaskHistory,
clineMessages: [], // No conversation messages
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
const setInputValue = vi.fn()
@ -789,6 +861,9 @@ describe("ChatTextArea", () => {
],
clineMessages: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
rerender(<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="" />)
@ -812,6 +887,9 @@ describe("ChatTextArea", () => {
{ type: "say", say: "user_feedback", text: "Message 2", ts: 2000 },
],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
setInputValue.mockClear()
@ -929,6 +1007,9 @@ describe("ChatTextArea", () => {
cwd: "/test/workspace",
customModes: [],
customModePrompts: {},
experiments: {
showEnhancePromptButton: true,
},
})
render(<ChatTextArea {...defaultProps} isEditMode={true} />)
@ -953,6 +1034,9 @@ describe("ChatTextArea", () => {
openedTabs: [],
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
render(<ChatTextArea {...defaultProps} isEditMode={false} />)

View file

@ -39,29 +39,16 @@ export const ExperimentalSettings = ({
{Object.entries(experimentConfigsMap)
.filter(([key]) => key in EXPERIMENT_IDS)
.map((config) => {
if (config[0] === "MULTI_FILE_APPLY_DIFF") {
return (
<ExperimentalFeature
key={config[0]}
experimentKey={config[0]}
enabled={experiments[EXPERIMENT_IDS.MULTI_FILE_APPLY_DIFF] ?? false}
onChange={(enabled) =>
setExperimentEnabled(EXPERIMENT_IDS.MULTI_FILE_APPLY_DIFF, enabled)
}
/>
)
}
const experimentKey = config[0] as keyof typeof EXPERIMENT_IDS
const experimentId = EXPERIMENT_IDS[experimentKey]
const defaultEnabled = experimentConfigsMap[experimentKey].enabled
return (
<ExperimentalFeature
key={config[0]}
experimentKey={config[0]}
enabled={experiments[EXPERIMENT_IDS[config[0] as keyof typeof EXPERIMENT_IDS]] ?? false}
onChange={(enabled) =>
setExperimentEnabled(
EXPERIMENT_IDS[config[0] as keyof typeof EXPERIMENT_IDS],
enabled,
)
}
key={experimentKey}
experimentKey={experimentKey}
enabled={experiments[experimentId] ?? defaultEnabled}
onChange={(enabled) => setExperimentEnabled(experimentId, enabled)}
/>
)
})}

View file

@ -648,6 +648,10 @@
"MULTI_FILE_APPLY_DIFF": {
"name": "Enable concurrent file edits",
"description": "When enabled, Roo can edit multiple files in a single request. When disabled, Roo must edit files one at a time. Disabling this can help when working with less capable models or when you want more control over file modifications."
},
"SHOW_ENHANCE_PROMPT_BUTTON": {
"name": "Show enhance prompt button",
"description": "When enabled, displays the enhance prompt button in the chat input area. This button allows you to improve your prompts using AI assistance."
}
},
"promptCaching": {