Browse Source

fix: prevent empty mode names from being saved (fixes #5766) (#5767)

* fix: prevent empty mode names from being saved (fixes #5766)

- Add frontend validation in ModesView to prevent empty names from being saved
- Add onBlur handler to restore original name if field is left empty
- Add backend validation in CustomModesManager.updateCustomMode using modeConfigSchema
- Provide user feedback when validation fails
- Trim whitespace from mode names before validation

This prevents YAML parsing errors caused by empty mode name fields.

* fix: improve UX by allowing users to empty mode name field

- Remove restriction that prevented users from emptying the name field
- Remove onBlur handler that automatically restored original name
- Allow backend validation to handle empty names and show appropriate errors
- Users can now type freely but invalid saves are prevented by backend validation

Addresses feedback from @daniel-lxs in PR #5767

* fix: allow emptying mode name field but prevent saving when invalid

- Modified onBlur handler to check if name is empty before saving
- If empty, revert to original name instead of saving empty value
- This provides better UX as requested in PR review

* fix: add proper JSON formatting to source map writes for Windows compatibility

---------

Co-authored-by: Roo Code <[email protected]>
roomote[bot] 7 months ago
parent
commit
7ca4901024

+ 8 - 4
src/core/config/CustomModesManager.ts

@@ -405,9 +405,13 @@ export class CustomModesManager {
 			// Validate the mode configuration before saving
 			const validationResult = modeConfigSchema.safeParse(config)
 			if (!validationResult.success) {
-				const errors = validationResult.error.errors.map((e) => e.message).join(", ")
-				logger.error(`Invalid mode configuration for ${slug}`, { errors: validationResult.error.errors })
-				throw new Error(`Invalid mode configuration: ${errors}`)
+				const errorMessages = validationResult.error.errors
+					.map((err) => `${err.path.join(".")}: ${err.message}`)
+					.join(", ")
+				const errorMessage = `Invalid mode configuration: ${errorMessages}`
+				logger.error("Mode validation failed", { slug, errors: validationResult.error.errors })
+				vscode.window.showErrorMessage(t("common:customModes.errors.updateFailed", { error: errorMessage }))
+				return
 			}
 
 			const isProjectMode = config.source === "project"
@@ -786,7 +790,7 @@ export class CustomModesManager {
 								// This excludes the rules-{slug} folder from the path
 								const relativePath = path.relative(modeRulesDir, filePath)
 								// Normalize path to use forward slashes for cross-platform compatibility
-								const normalizedRelativePath = relativePath.replace(/\\/g, '/')
+								const normalizedRelativePath = relativePath.replace(/\\/g, "/")
 								rulesFiles.push({ relativePath: normalizedRelativePath, content: content.trim() })
 							}
 						}

+ 15 - 7
webview-ui/src/components/modes/ModesView.tsx

@@ -755,17 +755,25 @@ const ModesView = ({ onDone }: ModesViewProps) => {
 											}
 										}}
 										onChange={(e) => {
-											setLocalModeName(e.target.value)
+											const newName = e.target.value
+											// Allow users to type freely, including emptying the field
+											setLocalModeName(newName)
 										}}
 										onBlur={() => {
 											const customMode = findModeBySlug(visualMode, customModes)
-											if (customMode && localModeName.trim()) {
+											if (customMode) {
+												const trimmedName = localModeName.trim()
 												// Only update if the name is not empty
-												updateCustomMode(visualMode, {
-													...customMode,
-													name: localModeName,
-													source: customMode.source || "global",
-												})
+												if (trimmedName) {
+													updateCustomMode(visualMode, {
+														...customMode,
+														name: trimmedName,
+														source: customMode.source || "global",
+													})
+												} else {
+													// Revert to the original name if empty
+													setLocalModeName(customMode.name)
+												}
 											}
 											// Clear the editing state
 											setCurrentEditingModeSlug(null)

+ 2 - 2
webview-ui/src/vite-plugins/sourcemapPlugin.ts

@@ -88,8 +88,8 @@ export function sourcemapPlugin(): Plugin {
 								})
 							}
 
-							// Write back the updated source map
-							fs.writeFileSync(mapPath, JSON.stringify(mapContent))
+							// Write back the updated source map with proper formatting
+							fs.writeFileSync(mapPath, JSON.stringify(mapContent, null, 2))
 							console.log(`Updated source map for ${jsFile}`)
 						} catch (error) {
 							console.error(`Error processing source map for ${jsFile}:`, error)