mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Add file regex editing UI for custom modes in Modes tab
This commit is contained in:
parent
02a4823250
commit
c093e96882
3 changed files with 285 additions and 14 deletions
119
FILE_REGEX_UI_IMPLEMENTATION.md
Normal file
119
FILE_REGEX_UI_IMPLEMENTATION.md
Normal 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.
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue