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
This commit is contained in:
Roo Code 2025-11-27 17:35:51 +00:00
parent 400d0cd183
commit 2cead44d79
5 changed files with 26 additions and 20 deletions

View file

@ -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<Record<NonNullable<ExtensionMessage["action"]>, 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" && <ModesView onDone={() => switchTab("chat")} />}
{tab === "mcp" && <McpView onDone={() => switchTab("chat")} />}
{tab === "history" && <HistoryView onDone={() => switchTab("chat")} />}
{tab === "settings" && (

View file

@ -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 (
<div data-testid="prompts-view" onClick={onDone}>
Modes View
</div>
)
},
}))
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(<AppWithProviders />)
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(<AppWithProviders />)
act(() => {

View file

@ -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)
}}
/>

View file

@ -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<SettingsViewRef, SettingsViewProps>(({ 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<SettingsViewRef, SettingsViewProps>(({ onDone, t
</div>
)}
{/* Modes Section */}
{activeTab === "modes" && <ModesView onDone={() => setActiveTab("providers")} />}
{/* Auto-Approve Section */}
{activeTab === "autoApprove" && (
<AutoApproveSettings

View file

@ -22,6 +22,7 @@
},
"sections": {
"providers": "Providers",
"modes": "Modes",
"autoApprove": "Auto-Approve",
"browser": "Browser",
"checkpoints": "Checkpoints",