Skip to content

Commit c093e96

Browse files
committed
Add file regex editing UI for custom modes in Modes tab
1 parent 02a4823 commit c093e96

File tree

3 files changed

+285
-14
lines changed

3 files changed

+285
-14
lines changed

FILE_REGEX_UI_IMPLEMENTATION.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# File Regex UI Implementation for Modes Tab
2+
3+
## Summary
4+
5+
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.
6+
7+
## Features Implemented
8+
9+
### 1. **Display Current File Regex**
10+
11+
- Shows the current file regex pattern and description for the edit tool group
12+
- Displays "all files" when no regex is configured
13+
- Shows either the description or the regex pattern (formatted as `/pattern/`)
14+
15+
### 2. **Edit Mode UI**
16+
17+
- **File Regex Input**: Text input field for entering regex patterns (e.g., `.*\.(js|ts|jsx|tsx)$`)
18+
- **Description Input**: Text input field for entering a human-readable description (e.g., "JavaScript/TypeScript files")
19+
- **Save/Cancel Buttons**: Action buttons to save or discard changes
20+
21+
### 3. **Edit Button**
22+
23+
- Small edit icon button that appears next to the file regex display
24+
- Only visible for custom modes (built-in modes cannot be edited)
25+
- Triggers the edit mode when clicked
26+
27+
### 4. **Input Validation**
28+
29+
- Handles both existing file regex configurations and new ones
30+
- Properly converts simple "edit" groups to array format with options
31+
- Preserves existing options when updating
32+
33+
## Files Modified
34+
35+
### 1. `webview-ui/src/components/modes/ModesView.tsx`
36+
37+
#### **State Added:**
38+
39+
```typescript
40+
const [editingFileRegex, setEditingFileRegex] = useState<string | null>(null)
41+
const [fileRegexValue, setFileRegexValue] = useState("")
42+
const [fileRegexDescription, setFileRegexDescription] = useState("")
43+
```
44+
45+
#### **Functions Added:**
46+
47+
- `startEditingFileRegex()`: Initializes edit mode with current values
48+
- `saveFileRegex()`: Saves changes to the mode configuration
49+
- `cancelEditingFileRegex()`: Cancels editing and resets state
50+
51+
#### **UI Changes:**
52+
53+
- Replaced static file regex display with conditional rendering
54+
- Added input fields and buttons that appear when editing
55+
- Added edit button that shows only for custom modes
56+
57+
### 2. `webview-ui/src/i18n/locales/en/prompts.json`
58+
59+
#### **Translation Keys Added:**
60+
61+
```json
62+
{
63+
"tools": {
64+
"fileRegex": "File Regex",
65+
"description": "Description",
66+
"save": "Save",
67+
"cancel": "Cancel",
68+
"editFileRegex": "Edit file regex"
69+
}
70+
}
71+
```
72+
73+
## Technical Implementation Details
74+
75+
### **Data Structure Handling**
76+
77+
The implementation properly handles the `GroupEntry` union type:
78+
79+
- Simple string groups: `"edit"`
80+
- Array groups with options: `["edit", { fileRegex: "pattern", description: "desc" }]`
81+
82+
### **Type Safety**
83+
84+
- Uses proper TypeScript types throughout
85+
- Handles the `GroupEntry` type correctly
86+
- Maintains type safety when updating group configurations
87+
88+
### **User Experience**
89+
90+
- Immediate visual feedback when entering edit mode
91+
- Clear labeling of input fields with placeholders
92+
- Non-destructive editing (changes only saved on explicit save)
93+
- Edit button only appears for modes that can be modified
94+
95+
## Usage
96+
97+
1. **Navigate to Modes Tab**: Open the prompts/modes configuration
98+
2. **Select Custom Mode**: Choose a custom mode (not built-in)
99+
3. **Find Edit Tool Group**: Look for the "Edit Files" tool group section
100+
4. **Click Edit Button**: Click the small edit icon next to the file regex display
101+
5. **Configure Regex**: Enter the desired file pattern and description
102+
6. **Save Changes**: Click "Save" to apply or "Cancel" to discard
103+
104+
## Example Use Cases
105+
106+
- **JavaScript Projects**: `.*\.(js|jsx|ts|tsx)$` - "JavaScript/TypeScript files"
107+
- **Python Projects**: `.*\.py$` - "Python files"
108+
- **Documentation**: `.*\.(md|txt|rst)$` - "Documentation files"
109+
- **Config Files**: `.*\.(json|yaml|yml|toml)$` - "Configuration files"
110+
111+
## Benefits
112+
113+
1. **Enhanced Control**: Users can precisely control which files modes can edit
114+
2. **Safety**: Prevents accidental modification of sensitive files
115+
3. **Workflow Optimization**: Modes can be tailored for specific file types
116+
4. **User-Friendly**: Intuitive UI that doesn't require manual JSON editing
117+
5. **Backward Compatible**: Works with existing mode configurations
118+
119+
The implementation follows the existing code patterns and design system used throughout the Roo Code extension, ensuring consistency and maintainability.

webview-ui/src/components/modes/ModesView.tsx

Lines changed: 161 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ const ModesView = ({ onDone }: ModesViewProps) => {
9595
const [searchValue, setSearchValue] = useState("")
9696
const searchInputRef = useRef<HTMLInputElement>(null)
9797

98+
// State for editing file regex
99+
const [editingFileRegex, setEditingFileRegex] = useState<string | null>(null)
100+
const [fileRegexValue, setFileRegexValue] = useState("")
101+
const [fileRegexDescription, setFileRegexDescription] = useState("")
102+
98103
// Direct update functions
99104
const updateAgentPrompt = useCallback(
100105
(mode: Mode, promptData: PromptComponent) => {
@@ -399,6 +404,82 @@ const ModesView = ({ onDone }: ModesViewProps) => {
399404
})
400405
}
401406

407+
// File regex editing functions
408+
const startEditingFileRegex = useCallback(
409+
(modeSlug: string) => {
410+
const currentMode = getCurrentMode()
411+
if (!currentMode) return
412+
413+
const editGroup = currentMode.groups?.find((g: GroupEntry) => Array.isArray(g) && g[0] === "edit")
414+
415+
if (Array.isArray(editGroup)) {
416+
setFileRegexValue(editGroup[1]?.fileRegex || "")
417+
setFileRegexDescription(editGroup[1]?.description || "")
418+
} else {
419+
setFileRegexValue("")
420+
setFileRegexDescription("")
421+
}
422+
setEditingFileRegex(modeSlug)
423+
},
424+
[getCurrentMode],
425+
)
426+
427+
const saveFileRegex = useCallback(() => {
428+
const currentMode = getCurrentMode()
429+
if (!currentMode || !editingFileRegex) return
430+
431+
const isCustomMode = Boolean(findModeBySlug(editingFileRegex, customModes))
432+
if (!isCustomMode) return
433+
434+
// Update the groups array with the new file regex
435+
const newGroups: GroupEntry[] = currentMode.groups.map((group: GroupEntry) => {
436+
if (Array.isArray(group) && group[0] === "edit") {
437+
// Update existing edit group with options
438+
return [
439+
"edit",
440+
{
441+
...(group[1] || {}),
442+
fileRegex: fileRegexValue.trim() || undefined,
443+
description: fileRegexDescription.trim() || undefined,
444+
},
445+
] as GroupEntry
446+
} else if (group === "edit") {
447+
// Convert simple edit group to array with options
448+
return [
449+
"edit",
450+
{
451+
fileRegex: fileRegexValue.trim() || undefined,
452+
description: fileRegexDescription.trim() || undefined,
453+
},
454+
] as GroupEntry
455+
}
456+
return group
457+
})
458+
459+
updateCustomMode(editingFileRegex, {
460+
...currentMode,
461+
groups: newGroups,
462+
})
463+
464+
setEditingFileRegex(null)
465+
setFileRegexValue("")
466+
setFileRegexDescription("")
467+
}, [
468+
getCurrentMode,
469+
editingFileRegex,
470+
fileRegexValue,
471+
fileRegexDescription,
472+
findModeBySlug,
473+
customModes,
474+
updateCustomMode,
475+
])
476+
477+
const cancelEditingFileRegex = useCallback(() => {
478+
setEditingFileRegex(null)
479+
setFileRegexValue("")
480+
setFileRegexDescription("")
481+
}, [])
482+
402483
return (
403484
<Tab>
404485
<TabHeader className="flex justify-between items-center">
@@ -806,20 +887,86 @@ const ModesView = ({ onDone }: ModesViewProps) => {
806887
{group === "edit" && (
807888
<div className="text-xs text-vscode-descriptionForeground mt-0.5">
808889
{t("prompts:tools.allowedFiles")}{" "}
809-
{(() => {
810-
const currentMode = getCurrentMode()
811-
const editGroup = currentMode?.groups?.find(
812-
(g) =>
813-
Array.isArray(g) &&
814-
g[0] === "edit" &&
815-
g[1]?.fileRegex,
816-
)
817-
if (!Array.isArray(editGroup)) return t("prompts:allFiles")
818-
return (
819-
editGroup[1].description ||
820-
`/${editGroup[1].fileRegex}/`
821-
)
822-
})()}
890+
{editingFileRegex === visualMode ? (
891+
<div className="mt-2 space-y-2">
892+
<div>
893+
<label className="block text-xs font-medium mb-1">
894+
{t("prompts:tools.fileRegex")}
895+
</label>
896+
<Input
897+
type="text"
898+
value={fileRegexValue}
899+
onChange={(e) =>
900+
setFileRegexValue(e.target.value)
901+
}
902+
placeholder="e.g., .*\\.(js|ts|jsx|tsx)$"
903+
className="text-xs h-6"
904+
/>
905+
</div>
906+
<div>
907+
<label className="block text-xs font-medium mb-1">
908+
{t("prompts:tools.description")}
909+
</label>
910+
<Input
911+
type="text"
912+
value={fileRegexDescription}
913+
onChange={(e) =>
914+
setFileRegexDescription(e.target.value)
915+
}
916+
placeholder="e.g., JavaScript/TypeScript files"
917+
className="text-xs h-6"
918+
/>
919+
</div>
920+
<div className="flex gap-1">
921+
<Button
922+
variant="default"
923+
size="sm"
924+
onClick={saveFileRegex}
925+
className="text-xs px-2 py-1 h-6">
926+
{t("prompts:tools.save")}
927+
</Button>
928+
<Button
929+
variant="ghost"
930+
size="sm"
931+
onClick={cancelEditingFileRegex}
932+
className="text-xs px-2 py-1 h-6">
933+
{t("prompts:tools.cancel")}
934+
</Button>
935+
</div>
936+
</div>
937+
) : (
938+
<div className="flex items-center gap-1">
939+
<span>
940+
{(() => {
941+
const currentMode = getCurrentMode()
942+
const editGroup = currentMode?.groups?.find(
943+
(g: GroupEntry) =>
944+
Array.isArray(g) &&
945+
g[0] === "edit" &&
946+
g[1]?.fileRegex,
947+
)
948+
if (!Array.isArray(editGroup))
949+
return t("prompts:allFiles")
950+
return (
951+
editGroup[1].description ||
952+
`/${editGroup[1].fileRegex}/`
953+
)
954+
})()}
955+
</span>
956+
{isCustomMode && (
957+
<Button
958+
variant="ghost"
959+
size="icon"
960+
onClick={() =>
961+
startEditingFileRegex(visualMode)
962+
}
963+
title={t("prompts:tools.editFileRegex")}
964+
className="h-4 w-4 p-0.5">
965+
<span className="codicon codicon-edit text-xs"></span>
966+
</Button>
967+
)}
968+
</div>
969+
)}
823970
</div>
824971
)}
825972
</VSCodeCheckbox>

webview-ui/src/i18n/locales/en/prompts.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
"editTools": "Edit tools",
2121
"doneEditing": "Done editing",
2222
"allowedFiles": "Allowed files:",
23+
"fileRegex": "File Regex",
24+
"description": "Description",
25+
"save": "Save",
26+
"cancel": "Cancel",
27+
"editFileRegex": "Edit file regex",
2328
"toolNames": {
2429
"read": "Read Files",
2530
"edit": "Edit Files",

0 commit comments

Comments
 (0)