diff --git a/webview-ui/src/components/modes/ModesView.tsx b/webview-ui/src/components/modes/ModesView.tsx index 170d03b0e4..cc8bd7389c 100644 --- a/webview-ui/src/components/modes/ModesView.tsx +++ b/webview-ui/src/components/modes/ModesView.tsx @@ -833,7 +833,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) { @@ -888,7 +888,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) { @@ -943,7 +943,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) { @@ -1102,14 +1102,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 { @@ -1307,12 +1308,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 e202114bbb..7e85616613 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 a71132d62b..e0f208f1e2 100644 --- a/webview-ui/src/components/settings/PromptsSettings.tsx +++ b/webview-ui/src/components/settings/PromptsSettings.tsx @@ -56,14 +56,31 @@ const PromptsSettings = ({ customSupportPrompts, setCustomSupportPrompts }: Prom }, []) 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) } } @@ -75,6 +92,10 @@ const PromptsSettings = ({ customSupportPrompts, setCustomSupportPrompts }: Prom 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] @@ -84,7 +105,8 @@ const PromptsSettings = ({ customSupportPrompts, setCustomSupportPrompts }: Prom 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) } @@ -145,12 +167,11 @@ const PromptsSettings = ({ customSupportPrompts, setCustomSupportPrompts }: Prom { + 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"