diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index 7c22ad60f85..bacb960c225 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -29,8 +29,6 @@ import { buildDocLink } from "@src/utils/docLinks" import { Section } from "@src/components/settings/Section" import { SectionHeader } from "@src/components/settings/SectionHeader" -import { TabContent } from "../common/Tab" - import McpToolRow from "./McpToolRow" import McpResourceRow from "./McpResourceRow" import McpEnabledToggle from "./McpEnabledToggle" @@ -57,145 +55,141 @@ const McpView = () => {
- -
- - - Learn More - - -
+
+ + + Learn More + + +
- + - {mcpEnabled && ( - <> -
- { - setEnableMcpServerCreation(e.target.checked) - vscode.postMessage({ type: "enableMcpServerCreation", bool: e.target.checked }) - }}> - {t("mcp:enableServerCreation.title")} - -
- - - Learn about server creation - - new - -

{t("mcp:enableServerCreation.hint")}

-
+ {mcpEnabled && ( + <> +
+ { + setEnableMcpServerCreation(e.target.checked) + vscode.postMessage({ type: "enableMcpServerCreation", bool: e.target.checked }) + }}> + {t("mcp:enableServerCreation.title")} + +
+ + + Learn about server creation + + new + +

{t("mcp:enableServerCreation.hint")}

+
- {/* Server List */} - {servers.length > 0 && ( -
- {servers.map((server) => ( - - ))} -
- )} + {/* Server List */} + {servers.length > 0 && ( +
+ {servers.map((server) => ( + + ))} +
+ )} - {/* Edit Settings Buttons */} -
+ + + + - - - - - -
-
- - {t("mcp:learnMoreEditingSettings")} - -
- - )} - + +
+
+ + {t("mcp:learnMoreEditingSettings")} + +
+ + )}
) diff --git a/webview-ui/src/components/modes/ModesView.tsx b/webview-ui/src/components/modes/ModesView.tsx index dc9522bc44e..09cd237f195 100644 --- a/webview-ui/src/components/modes/ModesView.tsx +++ b/webview-ui/src/components/modes/ModesView.tsx @@ -30,7 +30,6 @@ import { useAppTranslation } from "@src/i18n/TranslationContext" import { useExtensionState } from "@src/context/ExtensionStateContext" import { Section } from "@src/components/settings/Section" import { SectionHeader } from "@src/components/settings/SectionHeader" -import { TabContent } from "@src/components/common/Tab" import { Button, Select, @@ -602,880 +601,866 @@ const ModesView = () => {
- -
-
e.stopPropagation()} className="flex justify-between items-center mb-3"> -

{t("prompts:modes.title")}

-
-
- - + + {showConfigMenu && ( +
e.stopPropagation()} + onMouseDown={(e) => e.stopPropagation()} + className="absolute top-full right-0 w-[200px] mt-1 bg-vscode-editor-background border border-vscode-input-border rounded shadow-md z-[1000]"> +
{ + e.preventDefault() // Prevent blur + vscode.postMessage({ + type: "openCustomModesSettings", + }) + setShowConfigMenu(false) }} - onBlur={() => { - // Add slight delay to allow menu item clicks to register - setTimeout(() => setShowConfigMenu(false), 200) - }}> - - - - {showConfigMenu && ( + onClick={(e) => e.preventDefault()}> + {t("prompts:modes.editGlobalModes")} +
e.stopPropagation()} - onMouseDown={(e) => e.stopPropagation()} - className="absolute top-full right-0 w-[200px] mt-1 bg-vscode-editor-background border border-vscode-input-border rounded shadow-md z-[1000]"> -
{ - e.preventDefault() // Prevent blur - vscode.postMessage({ - type: "openCustomModesSettings", - }) - setShowConfigMenu(false) - }} - onClick={(e) => e.preventDefault()}> - {t("prompts:modes.editGlobalModes")} -
-
{ - e.preventDefault() // Prevent blur - vscode.postMessage({ - type: "openFile", - text: "./.roomodes", - values: { - create: true, - content: JSON.stringify({ customModes: [] }, null, 2), - }, - }) - setShowConfigMenu(false) - }} - onClick={(e) => e.preventDefault()}> - {t("prompts:modes.editProjectModes")} -
+ className="p-2 cursor-pointer text-vscode-foreground text-sm border-t border-vscode-input-border" + onMouseDown={(e) => { + e.preventDefault() // Prevent blur + vscode.postMessage({ + type: "openFile", + text: "./.roomodes", + values: { + create: true, + content: JSON.stringify({ customModes: [] }, null, 2), + }, + }) + setShowConfigMenu(false) + }} + onClick={(e) => e.preventDefault()}> + {t("prompts:modes.editProjectModes")}
- )} -
- +
+ )} +
+ + + + + + + +
+
+ +
+ + + + +
+ +
+ {isRenamingMode ? ( + <> + { + const target = e as { target: { value: string } } + setRenameInputValue(target.target.value) + }} + className="grow" + placeholder={t("prompts:createModeDialog.name.placeholder")} + /> + - - + -
- - -
- - - - -
- -
- {isRenamingMode ? ( - <> - { - const target = e as { target: { value: string } } - setRenameInputValue(target.target.value) - }} - className="grow" - placeholder={t("prompts:createModeDialog.name.placeholder")} - /> - + + ) : ( + <> + + - - - - - - ) : ( - <> - - - - - - -
- - {searchValue.length > 0 && ( -
- + + + +
+ + {searchValue.length > 0 && ( +
+ +
+ )} +
+ + + {searchValue && ( +
+ {t("prompts:modes.noMatchFound")}
)} -
- - - {searchValue && ( -
- {t("prompts:modes.noMatchFound")} -
- )} -
- - {displayModes - .filter((modeConfig) => - searchValue - ? modeConfig.name - .toLowerCase() - .includes(searchValue.toLowerCase()) - : true, - ) - .map((modeConfig) => ( - { - handleModeSwitch(modeConfig) - setOpen(false) - }} - data-testid={`mode-option-${modeConfig.slug}`}> -
- - {modeConfig.name} - - - {modeConfig.slug} - -
-
- ))} -
-
- - - - - {/* New mode (+) moved here from the top bar */} - - - - - {/* Edit (rename) mode - only enabled for custom modes */} - - - - - {/* Delete mode - disabled for built-in modes */} - - - - - {/* Export mode (kept here to the right of the dropdown) */} - - - - - )} -
- - {/* API Configuration - Moved Here */} -
-
{t("prompts:apiConfiguration.title")}
-
- {t("prompts:apiConfiguration.select")} -
-
- -
-
-
+ + + {displayModes + .filter((modeConfig) => + searchValue + ? modeConfig.name + .toLowerCase() + .includes(searchValue.toLowerCase()) + : true, + ) + .map((modeConfig) => ( + { + handleModeSwitch(modeConfig) + setOpen(false) + }} + data-testid={`mode-option-${modeConfig.slug}`}> +
+ + {modeConfig.name} + + + {modeConfig.slug} + +
+
+ ))} +
+ + + + + + {/* New mode (+) moved here from the top bar */} + + + - {/* Role Definition section */} -
-
-
{t("prompts:roleDefinition.title")}
- {!findModeBySlug(visualMode, customModes) && ( - + {/* Edit (rename) mode - only enabled for custom modes */} + - )} -
-
- {t("prompts:roleDefinition.description")} -
- { - const customMode = findModeBySlug(visualMode, customModes) - const prompt = customModePrompts?.[visualMode] as PromptComponent - return ( - customMode?.roleDefinition ?? - prompt?.roleDefinition ?? - getRoleDefinition(visualMode) - ) - })()} - onChange={(e) => { - const value = - (e as unknown as CustomEvent)?.detail?.target?.value ?? - ((e as any).target as HTMLTextAreaElement).value - const customMode = findModeBySlug(visualMode, customModes) - if (customMode) { - // For custom modes, update the JSON file - updateCustomMode(visualMode, { - ...customMode, - roleDefinition: value.trim() || "", - source: customMode.source || "global", - }) - } else { - // For built-in modes, update the prompts - updateAgentPrompt(visualMode, { - roleDefinition: value.trim() || undefined, - }) - } - }} - className="w-full" - rows={5} - data-testid={`${getCurrentMode()?.slug || "code"}-prompt-textarea`} - /> -
- {/* Description section */} -
-
-
{t("prompts:description.title")}
- {!findModeBySlug(visualMode, customModes) && ( - + {/* Delete mode - disabled for built-in modes */} + - )} -
-
- {t("prompts:description.description")} -
- { - const customMode = findModeBySlug(visualMode, customModes) - const prompt = customModePrompts?.[visualMode] as PromptComponent - return customMode?.description ?? prompt?.description ?? getDescription(visualMode) - })()} - onChange={(e) => { - const value = - (e as unknown as CustomEvent)?.detail?.target?.value ?? - ((e as any).target as HTMLTextAreaElement).value - const customMode = findModeBySlug(visualMode, customModes) - if (customMode) { - // For custom modes, update the JSON file - updateCustomMode(visualMode, { - ...customMode, - description: value.trim() || undefined, - source: customMode.source || "global", - }) - } else { - // For built-in modes, update the prompts - updateAgentPrompt(visualMode, { - description: value.trim() || undefined, - }) - } - }} - className="w-full" - data-testid={`${getCurrentMode()?.slug || "code"}-description-textfield`} - /> -
- {/* When to Use section */} -
-
-
{t("prompts:whenToUse.title")}
- {!findModeBySlug(visualMode, customModes) && ( - + {/* Export mode (kept here to the right of the dropdown) */} + - )} -
+ + )} +
+ + {/* API Configuration - Moved Here */} +
+
{t("prompts:apiConfiguration.title")}
- {t("prompts:whenToUse.description")} + {t("prompts:apiConfiguration.select")}
- { - const customMode = findModeBySlug(visualMode, customModes) - const prompt = customModePrompts?.[visualMode] as PromptComponent - return customMode?.whenToUse ?? prompt?.whenToUse ?? getWhenToUse(visualMode) - })()} - onChange={(e) => { - const value = - (e as unknown as CustomEvent)?.detail?.target?.value ?? - ((e as any).target as HTMLTextAreaElement).value - const customMode = findModeBySlug(visualMode, customModes) - if (customMode) { - // For custom modes, update the JSON file - updateCustomMode(visualMode, { - ...customMode, - whenToUse: value.trim() || undefined, - source: customMode.source || "global", +
+ +
+ - {/* Mode settings */} - <> - {/* Show tools for all modes */} -
-
-
{t("prompts:tools.title")}
- {findModeBySlug(visualMode, customModes) && ( - - - - )} -
- {!findModeBySlug(visualMode, customModes) && ( -
- {t("prompts:tools.builtInModesText")} -
- )} - {isToolsEditMode && findModeBySlug(visualMode, customModes) ? ( -
- {availableGroups.map((group) => { - const currentMode = getCurrentMode() - const isCustomMode = findModeBySlug(visualMode, customModes) - const customMode = isCustomMode - const isGroupEnabled = isCustomMode - ? customMode?.groups?.some((g) => getGroupName(g) === group) - : currentMode?.groups?.some((g) => getGroupName(g) === group) - - return ( - - {t(`prompts:tools.toolNames.${group}`)} - {group === "edit" && ( -
- {t("prompts:tools.allowedFiles")}{" "} - {(() => { - const currentMode = getCurrentMode() - const editGroup = currentMode?.groups?.find( - (g) => - Array.isArray(g) && - g[0] === "edit" && - g[1]?.fileRegex, - ) - if (!Array.isArray(editGroup)) return t("prompts:allFiles") - return ( - editGroup[1].description || - `/${editGroup[1].fileRegex}/` - ) - })()} -
- )} -
- ) - })} -
- ) : ( -
- {(() => { + {/* Role Definition section */} +
+
+
{t("prompts:roleDefinition.title")}
+ {!findModeBySlug(visualMode, customModes) && ( + + + + )} +
+
+ {t("prompts:roleDefinition.description")} +
+ { + const customMode = findModeBySlug(visualMode, customModes) + const prompt = customModePrompts?.[visualMode] as PromptComponent + return customMode?.roleDefinition ?? prompt?.roleDefinition ?? getRoleDefinition(visualMode) + })()} + onChange={(e) => { + const value = + (e as unknown as CustomEvent)?.detail?.target?.value ?? + ((e as any).target as HTMLTextAreaElement).value + const customMode = findModeBySlug(visualMode, customModes) + if (customMode) { + // For custom modes, update the JSON file + updateCustomMode(visualMode, { + ...customMode, + roleDefinition: value.trim() || "", + source: customMode.source || "global", + }) + } else { + // For built-in modes, update the prompts + updateAgentPrompt(visualMode, { + roleDefinition: value.trim() || undefined, + }) + } + }} + className="w-full" + rows={5} + data-testid={`${getCurrentMode()?.slug || "code"}-prompt-textarea`} + /> +
- // If there are no enabled groups, display translated "None" - if (enabledGroups.length === 0) { - return t("prompts:tools.noTools") + {/* Description section */} +
+
+
{t("prompts:description.title")}
+ {!findModeBySlug(visualMode, customModes) && ( + + + + )} +
+
+ {t("prompts:description.description")} +
+ { + const customMode = findModeBySlug(visualMode, customModes) + const prompt = customModePrompts?.[visualMode] as PromptComponent + return customMode?.description ?? prompt?.description ?? getDescription(visualMode) + })()} + onChange={(e) => { + const value = + (e as unknown as CustomEvent)?.detail?.target?.value ?? + ((e as any).target as HTMLTextAreaElement).value + const customMode = findModeBySlug(visualMode, customModes) + if (customMode) { + // For custom modes, update the JSON file + updateCustomMode(visualMode, { + ...customMode, + description: value.trim() || undefined, + source: customMode.source || "global", + }) + } else { + // For built-in modes, update the prompts + updateAgentPrompt(visualMode, { + description: value.trim() || undefined, + }) + } + }} + className="w-full" + data-testid={`${getCurrentMode()?.slug || "code"}-description-textfield`} + /> +
- return enabledGroups - .map((group) => { - const groupName = getGroupName(group) - const displayName = t(`prompts:tools.toolNames.${groupName}`) - if (Array.isArray(group) && group[1]?.fileRegex) { - const description = - group[1].description || `/${group[1].fileRegex}/` - return `${displayName} (${description})` - } - return displayName - }) - .join(", ") - })()} -
- )} -
- + {/* When to Use section */} +
+
+
{t("prompts:whenToUse.title")}
+ {!findModeBySlug(visualMode, customModes) && ( + + + + )} +
+
+ {t("prompts:whenToUse.description")} +
+ { + const customMode = findModeBySlug(visualMode, customModes) + const prompt = customModePrompts?.[visualMode] as PromptComponent + return customMode?.whenToUse ?? prompt?.whenToUse ?? getWhenToUse(visualMode) + })()} + onChange={(e) => { + const value = + (e as unknown as CustomEvent)?.detail?.target?.value ?? + ((e as any).target as HTMLTextAreaElement).value + const customMode = findModeBySlug(visualMode, customModes) + if (customMode) { + // For custom modes, update the JSON file + updateCustomMode(visualMode, { + ...customMode, + whenToUse: value.trim() || undefined, + source: customMode.source || "global", + }) + } else { + // For built-in modes, update the prompts + updateAgentPrompt(visualMode, { + whenToUse: value.trim() || undefined, + }) + } + }} + className="w-full" + rows={4} + data-testid={`${getCurrentMode()?.slug || "code"}-when-to-use-textarea`} + /> +
- {/* Role definition for both built-in and custom modes */} -
+ {/* Mode settings */} + <> + {/* Show tools for all modes */} +
-
{t("prompts:customInstructions.title")}
- {!findModeBySlug(visualMode, customModes) && ( - +
{t("prompts:tools.title")}
+ {findModeBySlug(visualMode, customModes) && ( + )}
-
- {t("prompts:customInstructions.description", { - modeName: getCurrentMode()?.name || "Code", - })} -
- { - const customMode = findModeBySlug(visualMode, customModes) - const prompt = customModePrompts?.[visualMode] as PromptComponent - return ( - customMode?.customInstructions ?? - prompt?.customInstructions ?? - getCustomInstructions(visualMode, customModes) - ) - })()} - onChange={(e) => { - const value = - (e as unknown as CustomEvent)?.detail?.target?.value ?? - ((e as any).target as HTMLTextAreaElement).value - const customMode = findModeBySlug(visualMode, customModes) - if (customMode) { - // For custom modes, update the JSON file - updateCustomMode(visualMode, { - ...customMode, - // Preserve empty string; only treat null/undefined as unset - customInstructions: value ?? undefined, - source: customMode.source || "global", - }) - } else { - // For built-in modes, update the prompts - const existingPrompt = customModePrompts?.[visualMode] as PromptComponent - updateAgentPrompt(visualMode, { - ...existingPrompt, - customInstructions: value.trim() || undefined, - }) - } - }} - rows={10} - className="w-full" - data-testid={`${getCurrentMode()?.slug || "code"}-custom-instructions-textarea`} - /> -
- { - const currentMode = getCurrentMode() - if (!currentMode) return + {!findModeBySlug(visualMode, customModes) && ( +
+ {t("prompts:tools.builtInModesText")} +
+ )} + {isToolsEditMode && findModeBySlug(visualMode, customModes) ? ( +
+ {availableGroups.map((group) => { + const currentMode = getCurrentMode() + const isCustomMode = findModeBySlug(visualMode, customModes) + const customMode = isCustomMode + const isGroupEnabled = isCustomMode + ? customMode?.groups?.some((g) => getGroupName(g) === group) + : currentMode?.groups?.some((g) => getGroupName(g) === group) - // Open or create an empty file - vscode.postMessage({ - type: "openFile", - text: `./.roo/rules-${currentMode.slug}/rules.md`, - values: { - create: true, - content: "", - }, - }) - }} - /> - ), - "0": ( - + {t(`prompts:tools.toolNames.${group}`)} + {group === "edit" && ( +
+ {t("prompts:tools.allowedFiles")}{" "} + {(() => { + const currentMode = getCurrentMode() + const editGroup = currentMode?.groups?.find( + (g) => + Array.isArray(g) && g[0] === "edit" && g[1]?.fileRegex, + ) + if (!Array.isArray(editGroup)) return t("prompts:allFiles") + return editGroup[1].description || `/${editGroup[1].fileRegex}/` + })()} +
)} - style={{ display: "inline" }} - aria-label="Learn about global custom instructions for modes" - /> - ), - }} - /> -
+ + ) + })} +
+ ) : ( +
+ {(() => { + const currentMode = getCurrentMode() + const enabledGroups = currentMode?.groups || [] + + // If there are no enabled groups, display translated "None" + if (enabledGroups.length === 0) { + return t("prompts:tools.noTools") + } + + return enabledGroups + .map((group) => { + const groupName = getGroupName(group) + const displayName = t(`prompts:tools.toolNames.${groupName}`) + if (Array.isArray(group) && group[1]?.fileRegex) { + const description = group[1].description || `/${group[1].fileRegex}/` + return `${displayName} (${description})` + } + return displayName + }) + .join(", ") + })()} +
+ )}
+ + + {/* Role definition for both built-in and custom modes */} +
+
+
{t("prompts:customInstructions.title")}
+ {!findModeBySlug(visualMode, customModes) && ( + + + + )} +
+
+ {t("prompts:customInstructions.description", { + modeName: getCurrentMode()?.name || "Code", + })} +
+ { + const customMode = findModeBySlug(visualMode, customModes) + const prompt = customModePrompts?.[visualMode] as PromptComponent + return ( + customMode?.customInstructions ?? + prompt?.customInstructions ?? + getCustomInstructions(visualMode, customModes) + ) + })()} + onChange={(e) => { + const value = + (e as unknown as CustomEvent)?.detail?.target?.value ?? + ((e as any).target as HTMLTextAreaElement).value + const customMode = findModeBySlug(visualMode, customModes) + if (customMode) { + // For custom modes, update the JSON file + updateCustomMode(visualMode, { + ...customMode, + // Preserve empty string; only treat null/undefined as unset + customInstructions: value ?? undefined, + source: customMode.source || "global", + }) + } else { + // For built-in modes, update the prompts + const existingPrompt = customModePrompts?.[visualMode] as PromptComponent + updateAgentPrompt(visualMode, { + ...existingPrompt, + customInstructions: value.trim() || undefined, + }) + } + }} + rows={10} + className="w-full" + data-testid={`${getCurrentMode()?.slug || "code"}-custom-instructions-textarea`} + /> +
+ { + const currentMode = getCurrentMode() + if (!currentMode) return + + // Open or create an empty file + vscode.postMessage({ + type: "openFile", + text: `./.roo/rules-${currentMode.slug}/rules.md`, + values: { + create: true, + content: "", + }, + }) + }} + /> + ), + "0": ( + + ), + }} + /> +
+
-
-
+
+
+ + - - - -
+ +
- {/* Advanced Features Disclosure */} -
- - - {isSystemPromptDisclosureOpen && ( -
- {/* Override System Prompt Section */} -
-

- Override System Prompt -

-
- { - const currentMode = getCurrentMode() - if (!currentMode) return - - vscode.postMessage({ - type: "openFile", - text: `./.roo/system-prompt-${currentMode.slug}`, - values: { - create: true, - content: "", - }, - }) - }} - /> - ), - "1": ( - - ), - "2": , - }} - /> -
+ {/* Advanced Features Disclosure */} +
+ + + {isSystemPromptDisclosureOpen && ( +
+ {/* Override System Prompt Section */} +
+

+ Override System Prompt +

+
+ { + const currentMode = getCurrentMode() + if (!currentMode) return + + vscode.postMessage({ + type: "openFile", + text: `./.roo/system-prompt-${currentMode.slug}`, + values: { + create: true, + content: "", + }, + }) + }} + /> + ), + "1": ( + + ), + "2": , + }} + />
- )} -
+
+ )}
+
-
-

{t("prompts:globalCustomInstructions.title")}

+
+

{t("prompts:globalCustomInstructions.title")}

-
- - - -
- { - const value = - (e as unknown as CustomEvent)?.detail?.target?.value ?? - ((e as any).target as HTMLTextAreaElement).value - setCustomInstructions(value ?? undefined) - vscode.postMessage({ - type: "customInstructions", - text: value ?? undefined, - }) +
+ + + +
+ { + const value = + (e as unknown as CustomEvent)?.detail?.target?.value ?? + ((e as any).target as HTMLTextAreaElement).value + setCustomInstructions(value ?? undefined) + vscode.postMessage({ + type: "customInstructions", + text: value ?? undefined, + }) + }} + rows={4} + className="w-full" + data-testid="global-custom-instructions-textarea" + /> +
+ + vscode.postMessage({ + type: "openFile", + text: "./.roo/rules/rules.md", + values: { + create: true, + content: "", + }, + }) + } + /> + ), + "0": ( + + ), }} - rows={4} - className="w-full" - data-testid="global-custom-instructions-textarea" /> -
- - vscode.postMessage({ - type: "openFile", - text: "./.roo/rules/rules.md", - values: { - create: true, - content: "", - }, - }) - } - /> - ), - "0": ( - - ), - }} - /> -
- +
{isCreateModeDialogOpen && (