Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/types/src/experiment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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>
Expand Down
30 changes: 30 additions & 0 deletions src/shared/__tests__/experiments.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,21 @@ describe("experiments", () => {
})
})

describe("SHOW_ENHANCE_PROMPT_BUTTON", () => {
it("is configured correctly", () => {
expect(EXPERIMENT_IDS.SHOW_ENHANCE_PROMPT_BUTTON).toBe("showEnhancePromptButton")
expect(experimentConfigsMap.SHOW_ENHANCE_PROMPT_BUTTON).toMatchObject({
enabled: true,
})
})
})

describe("isEnabled", () => {
it("returns false when POWER_STEERING experiment is not enabled", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: false,
multiFileApplyDiff: false,
showEnhancePromptButton: true,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
Expand All @@ -36,6 +46,7 @@ describe("experiments", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: true,
multiFileApplyDiff: false,
showEnhancePromptButton: true,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
})
Expand All @@ -44,8 +55,27 @@ describe("experiments", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: false,
multiFileApplyDiff: false,
showEnhancePromptButton: true,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})

it("returns true when SHOW_ENHANCE_PROMPT_BUTTON is enabled", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: false,
multiFileApplyDiff: false,
showEnhancePromptButton: true,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.SHOW_ENHANCE_PROMPT_BUTTON)).toBe(true)
})

it("returns false when SHOW_ENHANCE_PROMPT_BUTTON is disabled", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: false,
multiFileApplyDiff: false,
showEnhancePromptButton: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.SHOW_ENHANCE_PROMPT_BUTTON)).toBe(false)
})
})
})
2 changes: 2 additions & 0 deletions src/shared/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>>>
Expand All @@ -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(
Expand Down
50 changes: 27 additions & 23 deletions webview-ui/src/components/chat/ChatTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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">
Expand Down
84 changes: 84 additions & 0 deletions webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ describe("ChatTextArea", () => {
},
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
})

Expand All @@ -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", () => {
Expand All @@ -103,6 +145,9 @@ describe("ChatTextArea", () => {
apiConfiguration,
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

render(<ChatTextArea {...defaultProps} inputValue="Test prompt" />)
Expand All @@ -125,6 +170,9 @@ describe("ChatTextArea", () => {
},
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

render(<ChatTextArea {...defaultProps} inputValue="" />)
Expand All @@ -147,6 +195,9 @@ describe("ChatTextArea", () => {
},
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

render(<ChatTextArea {...defaultProps} inputValue="Test prompt" />)
Expand Down Expand Up @@ -174,6 +225,9 @@ describe("ChatTextArea", () => {
},
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

rerender(<ChatTextArea {...defaultProps} />)
Expand Down Expand Up @@ -275,6 +329,9 @@ describe("ChatTextArea", () => {
filePaths: [],
openedTabs: [],
cwd: mockCwd,
experiments: {
showEnhancePromptButton: true,
},
})
mockConvertToMentionPath.mockClear()
})
Expand Down Expand Up @@ -506,6 +563,9 @@ describe("ChatTextArea", () => {
taskHistory: [],
clineMessages: mockClineMessages,
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})
})

Expand Down Expand Up @@ -659,6 +719,9 @@ describe("ChatTextArea", () => {
taskHistory: [],
clineMessages: mixedClineMessages,
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

const setInputValue = vi.fn()
Expand Down Expand Up @@ -687,6 +750,9 @@ describe("ChatTextArea", () => {
taskHistory: [],
clineMessages: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

const setInputValue = vi.fn()
Expand Down Expand Up @@ -718,6 +784,9 @@ describe("ChatTextArea", () => {
taskHistory: [],
clineMessages: clineMessagesWithEmpty,
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

const setInputValue = vi.fn()
Expand Down Expand Up @@ -752,6 +821,9 @@ describe("ChatTextArea", () => {
taskHistory: mockTaskHistory,
clineMessages: [], // No conversation messages
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

const setInputValue = vi.fn()
Expand Down Expand Up @@ -789,6 +861,9 @@ describe("ChatTextArea", () => {
],
clineMessages: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

rerender(<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="" />)
Expand All @@ -812,6 +887,9 @@ describe("ChatTextArea", () => {
{ type: "say", say: "user_feedback", text: "Message 2", ts: 2000 },
],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

setInputValue.mockClear()
Expand Down Expand Up @@ -929,6 +1007,9 @@ describe("ChatTextArea", () => {
cwd: "/test/workspace",
customModes: [],
customModePrompts: {},
experiments: {
showEnhancePromptButton: true,
},
})

render(<ChatTextArea {...defaultProps} isEditMode={true} />)
Expand All @@ -953,6 +1034,9 @@ describe("ChatTextArea", () => {
openedTabs: [],
taskHistory: [],
cwd: "/test/workspace",
experiments: {
showEnhancePromptButton: true,
},
})

render(<ChatTextArea {...defaultProps} isEditMode={false} />)
Expand Down
Loading