diff --git a/webview-ui/src/components/chat/AutoApproveMenu.tsx b/webview-ui/src/components/chat/AutoApproveMenu.tsx
index 0feafae15d..2ab3daa202 100644
--- a/webview-ui/src/components/chat/AutoApproveMenu.tsx
+++ b/webview-ui/src/components/chat/AutoApproveMenu.tsx
@@ -129,18 +129,8 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
setIsExpanded((prev) => !prev)
}, [])
- const enabledActionsList = Object.entries(toggles)
- .filter(([_key, value]) => !!value)
- .map(([key]) => t(autoApproveSettingsConfig[key as AutoApproveSetting].labelKey))
- .join(", ")
-
- // Update displayed text logic
- const displayText = useMemo(() => {
- if (!effectiveAutoApprovalEnabled || !hasEnabledOptions) {
- return t("chat:autoApprove.none")
- }
- return enabledActionsList || t("chat:autoApprove.none")
- }, [effectiveAutoApprovalEnabled, hasEnabledOptions, enabledActionsList, t])
+ // Get all icons for display and highlight based on toggle state
+ const allConfigs = useMemo(() => Object.values(autoApproveSettingsConfig), [])
const handleOpenSettings = useCallback(
() =>
@@ -213,8 +203,44 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
whiteSpace: "nowrap",
flex: 1,
minWidth: 0,
- }}>
- {displayText}
+ display: "flex",
+ alignItems: "center",
+ gap: "6px",
+ }}
+ onClick={(e) => e.stopPropagation()}>
+ {allConfigs.map(({ key, icon, labelKey }) => {
+ const isEnabled = !!toggles[key]
+ return (
+
+
+ )
+ })}
({
@@ -84,7 +85,7 @@ describe("AutoApproveMenu", () => {
})
describe("Master checkbox behavior", () => {
- it("should show 'None selected' when no sub-options are selected", () => {
+ it("should show all icons with none highlighted when no sub-options are selected", () => {
;(useExtensionState as ReturnType).mockReturnValue({
...defaultExtensionState,
autoApprovalEnabled: false,
@@ -97,11 +98,23 @@ describe("AutoApproveMenu", () => {
render()
- // Check that the text shows "None selected"
- expect(screen.getByText("None selected")).toBeInTheDocument()
+ const container = screen.getByText("Auto-approve").parentElement?.parentElement
+
+ // All primary icons are rendered
+ expect(container?.querySelector(".codicon-eye")).toBeInTheDocument()
+ expect(container?.querySelector(".codicon-edit")).toBeInTheDocument()
+ expect(container?.querySelector(".codicon-terminal")).toBeInTheDocument()
+
+ // None are active
+ expect(container?.querySelector(".codicon-eye")?.getAttribute("data-active")).toBe("false")
+ expect(container?.querySelector(".codicon-edit")?.getAttribute("data-active")).toBe("false")
+ expect(container?.querySelector(".codicon-terminal")?.getAttribute("data-active")).toBe("false")
+
+ // "None selected" helper text is not shown anymore
+ expect(screen.queryByText("None selected")).not.toBeInTheDocument()
})
- it("should show enabled options when sub-options are selected", () => {
+ it("should highlight the enabled icons when sub-options are selected", () => {
;(useExtensionState as ReturnType).mockReturnValue({
...defaultExtensionState,
autoApprovalEnabled: true,
@@ -111,8 +124,15 @@ describe("AutoApproveMenu", () => {
render()
- // Check that the text shows the enabled option
- expect(screen.getByText("Read-only operations")).toBeInTheDocument()
+ const container = screen.getByText("Auto-approve").parentElement?.parentElement as HTMLElement
+ const eyeIcon = container.querySelector(".codicon-eye") as HTMLElement
+ const editIcon = container.querySelector(".codicon-edit") as HTMLElement
+
+ expect(eyeIcon).toBeInTheDocument()
+ expect(editIcon).toBeInTheDocument()
+
+ expect(eyeIcon.getAttribute("data-active")).toBe("true")
+ expect(editIcon.getAttribute("data-active")).toBe("false")
})
it("should not allow toggling master checkbox when no options are selected", () => {
@@ -211,7 +231,62 @@ describe("AutoApproveMenu", () => {
})
describe("Complex scenarios", () => {
- it("should display multiple enabled options in summary text", () => {
+ it("should highlight multiple enabled icons in collapsed view", () => {
+ ;(useExtensionState as ReturnType).mockReturnValue({
+ ...defaultExtensionState,
+ autoApprovalEnabled: true,
+ alwaysAllowReadOnly: true,
+ alwaysAllowWrite: true,
+ alwaysAllowExecute: true,
+ })
+
+ render()
+
+ const container = screen.getByText("Auto-approve").parentElement?.parentElement as HTMLElement
+ expect(container.querySelector("button.codicon-eye")?.getAttribute("data-active")).toBe("true") // Read
+ expect(container.querySelector("button.codicon-edit")?.getAttribute("data-active")).toBe("true") // Write
+ expect(container.querySelector("button.codicon-terminal")?.getAttribute("data-active")).toBe("true") // Execute
+ })
+
+ it("should toggle options when clicking icons in collapsed view", () => {
+ const mockSetAlwaysAllowReadOnly = vi.fn()
+ const mockSetAlwaysAllowWrite = vi.fn()
+
+ ;(useExtensionState as ReturnType).mockReturnValue({
+ ...defaultExtensionState,
+ autoApprovalEnabled: false,
+ alwaysAllowReadOnly: false,
+ alwaysAllowWrite: false,
+ setAlwaysAllowReadOnly: mockSetAlwaysAllowReadOnly,
+ setAlwaysAllowWrite: mockSetAlwaysAllowWrite,
+ })
+
+ render()
+
+ const container = screen.getByText("Auto-approve").parentElement?.parentElement as HTMLElement
+ const eyeButton = container.querySelector("button.codicon-eye") as HTMLElement
+ const editButton = container.querySelector("button.codicon-edit") as HTMLElement
+
+ // Click the read-only icon
+ fireEvent.click(eyeButton)
+ expect(mockPostMessage).toHaveBeenCalledWith({
+ type: "alwaysAllowReadOnly",
+ bool: true,
+ })
+ expect(mockSetAlwaysAllowReadOnly).toHaveBeenCalledWith(true)
+
+ // Click the write icon
+ fireEvent.click(editButton)
+ expect(mockPostMessage).toHaveBeenCalledWith({
+ type: "alwaysAllowWrite",
+ bool: true,
+ })
+ expect(mockSetAlwaysAllowWrite).toHaveBeenCalledWith(true)
+ })
+
+ it("should display tooltips on icon buttons in collapsed view", async () => {
+ const user = userEvent.setup()
+
;(useExtensionState as ReturnType).mockReturnValue({
...defaultExtensionState,
autoApprovalEnabled: true,
@@ -222,8 +297,22 @@ describe("AutoApproveMenu", () => {
render()
- // Should show all enabled options in the summary
- expect(screen.getByText("Read-only operations, Write operations, Execute operations")).toBeInTheDocument()
+ // Find the icon buttons
+ const container = screen.getByText("Auto-approve").parentElement?.parentElement
+ const eyeButton = container?.querySelector("button.codicon-eye") as HTMLElement
+ const editButton = container?.querySelector("button.codicon-edit") as HTMLElement
+ const terminalButton = container?.querySelector("button.codicon-terminal") as HTMLElement
+
+ // Verify icon buttons are present
+ expect(eyeButton).toBeInTheDocument()
+ expect(editButton).toBeInTheDocument()
+ expect(terminalButton).toBeInTheDocument()
+
+ // Test read-only icon tooltip
+ await user.hover(eyeButton)
+ await waitFor(() => {
+ expect(screen.getByRole("tooltip")).toHaveTextContent("Read-only operations")
+ })
})
it("should handle enabling first option when none selected", async () => {