From 2cead44d790f37f5cc34acd95ec256cdd4cc6dad Mon Sep 17 00:00:00 2001 From: Roo Code Date: Thu, 27 Nov 2025 17:35:51 +0000 Subject: [PATCH] feat: move ModesView from top-level tab to settings subtab - Remove ModesView from top-level tabs in App.tsx - Add modes as a subtab in SettingsView after Providers and before Auto-Approve - Use Users lucide icon for the modes tab - Update ModeSelector gear button to navigate to settings/modes section - Update promptsButtonClicked action to navigate to settings with modes section - Add translation key for modes section - Update tests to reflect new behavior --- webview-ui/src/App.tsx | 10 ++++++---- webview-ui/src/__tests__/App.spec.tsx | 20 +++++-------------- .../src/components/chat/ModeSelector.tsx | 8 +++++++- .../src/components/settings/SettingsView.tsx | 7 +++++++ webview-ui/src/i18n/locales/en/settings.json | 1 + 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index f9eb5605c2..bd8c2e2f5a 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -19,7 +19,6 @@ import WelcomeView from "./components/welcome/WelcomeView" import WelcomeViewProvider from "./components/welcome/WelcomeViewProvider" import McpView from "./components/mcp/McpView" import { MarketplaceView } from "./components/marketplace/MarketplaceView" -import ModesView from "./components/modes/ModesView" import { HumanRelayDialog } from "./components/human-relay/HumanRelayDialog" import { CheckpointRestoreDialog } from "./components/chat/CheckpointRestoreDialog" import { DeleteMessageDialog, EditMessageDialog } from "./components/chat/MessageModificationConfirmationDialog" @@ -29,7 +28,7 @@ import { useAddNonInteractiveClickListener } from "./components/ui/hooks/useNonI import { TooltipProvider } from "./components/ui/tooltip" import { STANDARD_TOOLTIP_DELAY } from "./components/ui/standard-tooltip" -type Tab = "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "cloud" +type Tab = "settings" | "history" | "mcp" | "chat" | "marketplace" | "cloud" interface HumanRelayDialogState { isOpen: boolean @@ -60,7 +59,6 @@ const MemoizedHumanRelayDialog = React.memo(HumanRelayDialog) const tabsByMessageAction: Partial, Tab>> = { chatButtonClicked: "chat", settingsButtonClicked: "settings", - promptsButtonClicked: "modes", mcpButtonClicked: "mcp", historyButtonClicked: "history", marketplaceButtonClicked: "marketplace", @@ -165,6 +163,11 @@ const App = () => { const targetSection = message.values?.section as string | undefined setCurrentSection(targetSection) setCurrentMarketplaceTab(undefined) + } else if (message.action === "promptsButtonClicked") { + // Navigate to settings with modes section + switchTab("settings") + setCurrentSection("modes") + setCurrentMarketplaceTab(undefined) } else { // Handle other actions using the mapping const newTab = tabsByMessageAction[message.action] @@ -271,7 +274,6 @@ const App = () => { ) ) : ( <> - {tab === "modes" && switchTab("chat")} />} {tab === "mcp" && switchTab("chat")} />} {tab === "history" && switchTab("chat")} />} {tab === "settings" && ( diff --git a/webview-ui/src/__tests__/App.spec.tsx b/webview-ui/src/__tests__/App.spec.tsx index 09db361c12..d2c63e9283 100644 --- a/webview-ui/src/__tests__/App.spec.tsx +++ b/webview-ui/src/__tests__/App.spec.tsx @@ -78,17 +78,6 @@ vi.mock("@src/components/mcp/McpView", () => ({ }, })) -vi.mock("@src/components/modes/ModesView", () => ({ - __esModule: true, - default: function ModesView({ onDone }: { onDone: () => void }) { - return ( -
- Modes View -
- ) - }, -})) - vi.mock("@src/components/marketplace/MarketplaceView", () => ({ MarketplaceView: function MarketplaceView({ onDone }: { onDone: () => void }) { return ( @@ -267,15 +256,16 @@ describe("App", () => { expect(chatView.getAttribute("data-hidden")).toBe("true") }) - it("switches to prompts view when receiving promptsButtonClicked action", async () => { + it("switches to settings view with modes section when receiving promptsButtonClicked action", async () => { render() act(() => { triggerMessage("promptsButtonClicked") }) - const promptsView = await screen.findByTestId("prompts-view") - expect(promptsView).toBeInTheDocument() + // promptsButtonClicked now navigates to settings view (with modes section) + const settingsView = await screen.findByTestId("settings-view") + expect(settingsView).toBeInTheDocument() const chatView = screen.getByTestId("chat-view") expect(chatView.getAttribute("data-hidden")).toBe("true") @@ -299,7 +289,7 @@ describe("App", () => { expect(screen.queryByTestId("settings-view")).not.toBeInTheDocument() }) - it.each(["history", "mcp", "prompts"])("returns to chat view when clicking done in %s view", async (view) => { + it.each(["history", "mcp"])("returns to chat view when clicking done in %s view", async (view) => { render() act(() => { diff --git a/webview-ui/src/components/chat/ModeSelector.tsx b/webview-ui/src/components/chat/ModeSelector.tsx index 3f843344a2..19a288b4d6 100644 --- a/webview-ui/src/components/chat/ModeSelector.tsx +++ b/webview-ui/src/components/chat/ModeSelector.tsx @@ -306,7 +306,13 @@ export const ModeSelector = ({ iconClass="codicon-settings-gear" title={t("chat:modeSelector.settings")} onClick={() => { - vscode.postMessage({ type: "switchTab", tab: "modes" }) + window.postMessage( + { + type: "action", + action: "promptsButtonClicked", + }, + "*", + ) setOpen(false) }} /> diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index dc1f1ae5ae..8790dfc720 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -25,6 +25,7 @@ import { LucideIcon, SquareSlash, Glasses, + Users, } from "lucide-react" import { @@ -74,6 +75,7 @@ import { Section } from "./Section" import PromptsSettings from "./PromptsSettings" import { SlashCommandsSettings } from "./SlashCommandsSettings" import { UISettings } from "./UISettings" +import ModesView from "../modes/ModesView" export const settingsTabsContainer = "flex flex-1 overflow-hidden [&.narrow_.tab-label]:hidden" export const settingsTabList = @@ -88,6 +90,7 @@ export interface SettingsViewRef { const sectionNames = [ "providers", + "modes", "autoApprove", "slashCommands", "browser", @@ -502,6 +505,7 @@ const SettingsView = forwardRef(({ onDone, t const sections: { id: SectionName; icon: LucideIcon }[] = useMemo( () => [ { id: "providers", icon: Webhook }, + { id: "modes", icon: Users }, { id: "autoApprove", icon: CheckCheck }, { id: "slashCommands", icon: SquareSlash }, { id: "browser", icon: SquareMousePointer }, @@ -699,6 +703,9 @@ const SettingsView = forwardRef(({ onDone, t )} + {/* Modes Section */} + {activeTab === "modes" && setActiveTab("providers")} />} + {/* Auto-Approve Section */} {activeTab === "autoApprove" && (