diff --git a/webview-ui/src/components/modes/ModesView.tsx b/webview-ui/src/components/modes/ModesView.tsx index 0283fbbec4..c50996585f 100644 --- a/webview-ui/src/components/modes/ModesView.tsx +++ b/webview-ui/src/components/modes/ModesView.tsx @@ -849,7 +849,7 @@ const ModesView = ({ onDone }: ModesViewProps) => { })()} onChange={(e) => { const value = - (e as unknown as CustomEvent)?.detail?.target?.value || + (e as unknown as CustomEvent)?.detail?.target?.value ?? ((e as any).target as HTMLTextAreaElement).value const customMode = findModeBySlug(visualMode, customModes) if (customMode) { @@ -904,7 +904,7 @@ const ModesView = ({ onDone }: ModesViewProps) => { })()} onChange={(e) => { const value = - (e as unknown as CustomEvent)?.detail?.target?.value || + (e as unknown as CustomEvent)?.detail?.target?.value ?? ((e as any).target as HTMLTextAreaElement).value const customMode = findModeBySlug(visualMode, customModes) if (customMode) { @@ -959,7 +959,7 @@ const ModesView = ({ onDone }: ModesViewProps) => { })()} onChange={(e) => { const value = - (e as unknown as CustomEvent)?.detail?.target?.value || + (e as unknown as CustomEvent)?.detail?.target?.value ?? ((e as any).target as HTMLTextAreaElement).value const customMode = findModeBySlug(visualMode, customModes) if (customMode) { @@ -1118,14 +1118,15 @@ const ModesView = ({ onDone }: ModesViewProps) => { })()} onChange={(e) => { const value = - (e as unknown as CustomEvent)?.detail?.target?.value || + (e as unknown as CustomEvent)?.detail?.target?.value ?? ((e as any).target as HTMLTextAreaElement).value const customMode = findModeBySlug(visualMode, customModes) if (customMode) { // For custom modes, update the JSON file updateCustomMode(visualMode, { ...customMode, - customInstructions: value.trim() || undefined, + // Preserve empty string; only treat null/undefined as unset + customInstructions: value ?? undefined, source: customMode.source || "global", }) } else { @@ -1335,12 +1336,12 @@ const ModesView = ({ onDone }: ModesViewProps) => { value={customInstructions || ""} onChange={(e) => { const value = - (e as unknown as CustomEvent)?.detail?.target?.value || + (e as unknown as CustomEvent)?.detail?.target?.value ?? ((e as any).target as HTMLTextAreaElement).value - setCustomInstructions(value || undefined) + setCustomInstructions(value ?? undefined) vscode.postMessage({ type: "customInstructions", - text: value.trim() || undefined, + text: value ?? undefined, }) }} rows={4} diff --git a/webview-ui/src/components/modes/__tests__/ModesView.spec.tsx b/webview-ui/src/components/modes/__tests__/ModesView.spec.tsx index 56ab791360..1a30ae079f 100644 --- a/webview-ui/src/components/modes/__tests__/ModesView.spec.tsx +++ b/webview-ui/src/components/modes/__tests__/ModesView.spec.tsx @@ -223,12 +223,13 @@ describe("PromptsView", () => { const changeEvent = new Event("change", { bubbles: true }) fireEvent(textarea, changeEvent) - // The component calls setCustomInstructions with value || undefined - // Since empty string is falsy, it should be undefined - expect(setCustomInstructions).toHaveBeenCalledWith(undefined) + // The component calls setCustomInstructions with value ?? undefined + // With nullish coalescing, empty string is preserved (not treated as nullish) + expect(setCustomInstructions).toHaveBeenCalledWith("") + // The postMessage call will have multiple calls, we need to check the right one expect(vscode.postMessage).toHaveBeenCalledWith({ type: "customInstructions", - text: undefined, + text: "", // empty string is now preserved with ?? operator }) }) diff --git a/webview-ui/src/components/settings/PromptsSettings.tsx b/webview-ui/src/components/settings/PromptsSettings.tsx index d09b53fe47..7aaa1bcc80 100644 --- a/webview-ui/src/components/settings/PromptsSettings.tsx +++ b/webview-ui/src/components/settings/PromptsSettings.tsx @@ -69,14 +69,31 @@ const PromptsSettings = ({ }, []) const updateSupportPrompt = (type: SupportPromptType, value: string | undefined) => { + // Don't trim during editing to preserve intentional whitespace + // Use nullish coalescing to preserve empty strings + const finalValue = value ?? undefined + if (type === "CONDENSE") { - setCustomCondensingPrompt(value || supportPrompt.default.CONDENSE) + setCustomCondensingPrompt(finalValue ?? supportPrompt.default.CONDENSE) vscode.postMessage({ type: "updateCondensingPrompt", - text: value || supportPrompt.default.CONDENSE, + text: finalValue ?? supportPrompt.default.CONDENSE, }) + // Also update the customSupportPrompts to trigger change detection + const updatedPrompts = { ...customSupportPrompts } + if (finalValue === undefined) { + delete updatedPrompts[type] + } else { + updatedPrompts[type] = finalValue + } + setCustomSupportPrompts(updatedPrompts) } else { - const updatedPrompts = { ...customSupportPrompts, [type]: value } + const updatedPrompts = { ...customSupportPrompts } + if (finalValue === undefined) { + delete updatedPrompts[type] + } else { + updatedPrompts[type] = finalValue + } setCustomSupportPrompts(updatedPrompts) } } @@ -88,6 +105,10 @@ const PromptsSettings = ({ type: "updateCondensingPrompt", text: supportPrompt.default.CONDENSE, }) + // Also update the customSupportPrompts to trigger change detection + const updatedPrompts = { ...customSupportPrompts } + delete updatedPrompts[type] + setCustomSupportPrompts(updatedPrompts) } else { const updatedPrompts = { ...customSupportPrompts } delete updatedPrompts[type] @@ -97,7 +118,8 @@ const PromptsSettings = ({ const getSupportPromptValue = (type: SupportPromptType): string => { if (type === "CONDENSE") { - return customCondensingPrompt || supportPrompt.default.CONDENSE + // Preserve empty string - only fall back to default when value is nullish + return customCondensingPrompt ?? supportPrompt.default.CONDENSE } return supportPrompt.get(customSupportPrompts, type) } @@ -158,12 +180,11 @@ const PromptsSettings = ({ { + onInput={(e) => { const value = - (e as unknown as CustomEvent)?.detail?.target?.value || + (e as unknown as CustomEvent)?.detail?.target?.value ?? ((e as any).target as HTMLTextAreaElement).value - const trimmedValue = value.trim() - updateSupportPrompt(activeSupportOption, trimmedValue || undefined) + updateSupportPrompt(activeSupportOption, value) }} rows={6} className="w-full"