Add file regex editing UI for custom modes in Modes tab

This commit is contained in:
Cursor Agent 2025-06-22 21:41:18 +00:00
parent 02a4823250
commit c093e96882
3 changed files with 285 additions and 14 deletions

View file

@ -0,0 +1,119 @@
# File Regex UI Implementation for Modes Tab
## Summary
I have successfully implemented a UI in the modes tab for displaying and editing the file regex for the edit files tool group. This allows users to configure which files can be edited when a specific mode is active.
## Features Implemented
### 1. **Display Current File Regex**
- Shows the current file regex pattern and description for the edit tool group
- Displays "all files" when no regex is configured
- Shows either the description or the regex pattern (formatted as `/pattern/`)
### 2. **Edit Mode UI**
- **File Regex Input**: Text input field for entering regex patterns (e.g., `.*\.(js|ts|jsx|tsx)$`)
- **Description Input**: Text input field for entering a human-readable description (e.g., "JavaScript/TypeScript files")
- **Save/Cancel Buttons**: Action buttons to save or discard changes
### 3. **Edit Button**
- Small edit icon button that appears next to the file regex display
- Only visible for custom modes (built-in modes cannot be edited)
- Triggers the edit mode when clicked
### 4. **Input Validation**
- Handles both existing file regex configurations and new ones
- Properly converts simple "edit" groups to array format with options
- Preserves existing options when updating
## Files Modified
### 1. `webview-ui/src/components/modes/ModesView.tsx`
#### **State Added:**
```typescript
const [editingFileRegex, setEditingFileRegex] = useState<string | null>(null)
const [fileRegexValue, setFileRegexValue] = useState("")
const [fileRegexDescription, setFileRegexDescription] = useState("")
```
#### **Functions Added:**
- `startEditingFileRegex()`: Initializes edit mode with current values
- `saveFileRegex()`: Saves changes to the mode configuration
- `cancelEditingFileRegex()`: Cancels editing and resets state
#### **UI Changes:**
- Replaced static file regex display with conditional rendering
- Added input fields and buttons that appear when editing
- Added edit button that shows only for custom modes
### 2. `webview-ui/src/i18n/locales/en/prompts.json`
#### **Translation Keys Added:**
```json
{
"tools": {
"fileRegex": "File Regex",
"description": "Description",
"save": "Save",
"cancel": "Cancel",
"editFileRegex": "Edit file regex"
}
}
```
## Technical Implementation Details
### **Data Structure Handling**
The implementation properly handles the `GroupEntry` union type:
- Simple string groups: `"edit"`
- Array groups with options: `["edit", { fileRegex: "pattern", description: "desc" }]`
### **Type Safety**
- Uses proper TypeScript types throughout
- Handles the `GroupEntry` type correctly
- Maintains type safety when updating group configurations
### **User Experience**
- Immediate visual feedback when entering edit mode
- Clear labeling of input fields with placeholders
- Non-destructive editing (changes only saved on explicit save)
- Edit button only appears for modes that can be modified
## Usage
1. **Navigate to Modes Tab**: Open the prompts/modes configuration
2. **Select Custom Mode**: Choose a custom mode (not built-in)
3. **Find Edit Tool Group**: Look for the "Edit Files" tool group section
4. **Click Edit Button**: Click the small edit icon next to the file regex display
5. **Configure Regex**: Enter the desired file pattern and description
6. **Save Changes**: Click "Save" to apply or "Cancel" to discard
## Example Use Cases
- **JavaScript Projects**: `.*\.(js|jsx|ts|tsx)$` - "JavaScript/TypeScript files"
- **Python Projects**: `.*\.py$` - "Python files"
- **Documentation**: `.*\.(md|txt|rst)$` - "Documentation files"
- **Config Files**: `.*\.(json|yaml|yml|toml)$` - "Configuration files"
## Benefits
1. **Enhanced Control**: Users can precisely control which files modes can edit
2. **Safety**: Prevents accidental modification of sensitive files
3. **Workflow Optimization**: Modes can be tailored for specific file types
4. **User-Friendly**: Intuitive UI that doesn't require manual JSON editing
5. **Backward Compatible**: Works with existing mode configurations
The implementation follows the existing code patterns and design system used throughout the Roo Code extension, ensuring consistency and maintainability.

View file

@ -95,6 +95,11 @@ const ModesView = ({ onDone }: ModesViewProps) => {
const [searchValue, setSearchValue] = useState("")
const searchInputRef = useRef<HTMLInputElement>(null)
// State for editing file regex
const [editingFileRegex, setEditingFileRegex] = useState<string | null>(null)
const [fileRegexValue, setFileRegexValue] = useState("")
const [fileRegexDescription, setFileRegexDescription] = useState("")
// Direct update functions
const updateAgentPrompt = useCallback(
(mode: Mode, promptData: PromptComponent) => {
@ -399,6 +404,82 @@ const ModesView = ({ onDone }: ModesViewProps) => {
})
}
// File regex editing functions
const startEditingFileRegex = useCallback(
(modeSlug: string) => {
const currentMode = getCurrentMode()
if (!currentMode) return
const editGroup = currentMode.groups?.find((g: GroupEntry) => Array.isArray(g) && g[0] === "edit")
if (Array.isArray(editGroup)) {
setFileRegexValue(editGroup[1]?.fileRegex || "")
setFileRegexDescription(editGroup[1]?.description || "")
} else {
setFileRegexValue("")
setFileRegexDescription("")
}
setEditingFileRegex(modeSlug)
},
[getCurrentMode],
)
const saveFileRegex = useCallback(() => {
const currentMode = getCurrentMode()
if (!currentMode || !editingFileRegex) return
const isCustomMode = Boolean(findModeBySlug(editingFileRegex, customModes))
if (!isCustomMode) return
// Update the groups array with the new file regex
const newGroups: GroupEntry[] = currentMode.groups.map((group: GroupEntry) => {
if (Array.isArray(group) && group[0] === "edit") {
// Update existing edit group with options
return [
"edit",
{
...(group[1] || {}),
fileRegex: fileRegexValue.trim() || undefined,
description: fileRegexDescription.trim() || undefined,
},
] as GroupEntry
} else if (group === "edit") {
// Convert simple edit group to array with options
return [
"edit",
{
fileRegex: fileRegexValue.trim() || undefined,
description: fileRegexDescription.trim() || undefined,
},
] as GroupEntry
}
return group
})
updateCustomMode(editingFileRegex, {
...currentMode,
groups: newGroups,
})
setEditingFileRegex(null)
setFileRegexValue("")
setFileRegexDescription("")
}, [
getCurrentMode,
editingFileRegex,
fileRegexValue,
fileRegexDescription,
findModeBySlug,
customModes,
updateCustomMode,
])
const cancelEditingFileRegex = useCallback(() => {
setEditingFileRegex(null)
setFileRegexValue("")
setFileRegexDescription("")
}, [])
return (
<Tab>
<TabHeader className="flex justify-between items-center">
@ -806,20 +887,86 @@ const ModesView = ({ onDone }: ModesViewProps) => {
{group === "edit" && (
<div className="text-xs text-vscode-descriptionForeground mt-0.5">
{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}/`
)
})()}
{editingFileRegex === visualMode ? (
<div className="mt-2 space-y-2">
<div>
<label className="block text-xs font-medium mb-1">
{t("prompts:tools.fileRegex")}
</label>
<Input
type="text"
value={fileRegexValue}
onChange={(e) =>
setFileRegexValue(e.target.value)
}
placeholder="e.g., .*\\.(js|ts|jsx|tsx)$"
className="text-xs h-6"
/>
</div>
<div>
<label className="block text-xs font-medium mb-1">
{t("prompts:tools.description")}
</label>
<Input
type="text"
value={fileRegexDescription}
onChange={(e) =>
setFileRegexDescription(e.target.value)
}
placeholder="e.g., JavaScript/TypeScript files"
className="text-xs h-6"
/>
</div>
<div className="flex gap-1">
<Button
variant="default"
size="sm"
onClick={saveFileRegex}
className="text-xs px-2 py-1 h-6">
{t("prompts:tools.save")}
</Button>
<Button
variant="ghost"
size="sm"
onClick={cancelEditingFileRegex}
className="text-xs px-2 py-1 h-6">
{t("prompts:tools.cancel")}
</Button>
</div>
</div>
) : (
<div className="flex items-center gap-1">
<span>
{(() => {
const currentMode = getCurrentMode()
const editGroup = currentMode?.groups?.find(
(g: GroupEntry) =>
Array.isArray(g) &&
g[0] === "edit" &&
g[1]?.fileRegex,
)
if (!Array.isArray(editGroup))
return t("prompts:allFiles")
return (
editGroup[1].description ||
`/${editGroup[1].fileRegex}/`
)
})()}
</span>
{isCustomMode && (
<Button
variant="ghost"
size="icon"
onClick={() =>
startEditingFileRegex(visualMode)
}
title={t("prompts:tools.editFileRegex")}
className="h-4 w-4 p-0.5">
<span className="codicon codicon-edit text-xs"></span>
</Button>
)}
</div>
)}
</div>
)}
</VSCodeCheckbox>

View file

@ -20,6 +20,11 @@
"editTools": "Edit tools",
"doneEditing": "Done editing",
"allowedFiles": "Allowed files:",
"fileRegex": "File Regex",
"description": "Description",
"save": "Save",
"cancel": "Cancel",
"editFileRegex": "Edit file regex",
"toolNames": {
"read": "Read Files",
"edit": "Edit Files",