diff --git a/FILE_REGEX_UI_IMPLEMENTATION.md b/FILE_REGEX_UI_IMPLEMENTATION.md new file mode 100644 index 0000000000..4a4278687c --- /dev/null +++ b/FILE_REGEX_UI_IMPLEMENTATION.md @@ -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(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. diff --git a/webview-ui/src/components/modes/ModesView.tsx b/webview-ui/src/components/modes/ModesView.tsx index 18f4bdf3d2..59e7503600 100644 --- a/webview-ui/src/components/modes/ModesView.tsx +++ b/webview-ui/src/components/modes/ModesView.tsx @@ -95,6 +95,11 @@ const ModesView = ({ onDone }: ModesViewProps) => { const [searchValue, setSearchValue] = useState("") const searchInputRef = useRef(null) + // State for editing file regex + const [editingFileRegex, setEditingFileRegex] = useState(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 ( @@ -806,20 +887,86 @@ const ModesView = ({ onDone }: ModesViewProps) => { {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}/` - ) - })()} + {editingFileRegex === visualMode ? ( +
+
+ + + setFileRegexValue(e.target.value) + } + placeholder="e.g., .*\\.(js|ts|jsx|tsx)$" + className="text-xs h-6" + /> +
+
+ + + setFileRegexDescription(e.target.value) + } + placeholder="e.g., JavaScript/TypeScript files" + className="text-xs h-6" + /> +
+
+ + +
+
+ ) : ( +
+ + {(() => { + 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}/` + ) + })()} + + {isCustomMode && ( + + )} +
+ )}
)} diff --git a/webview-ui/src/i18n/locales/en/prompts.json b/webview-ui/src/i18n/locales/en/prompts.json index f03d48ca92..9e92df6095 100644 --- a/webview-ui/src/i18n/locales/en/prompts.json +++ b/webview-ui/src/i18n/locales/en/prompts.json @@ -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",