Skip to content
17 changes: 9 additions & 8 deletions webview-ui/src/components/modes/ModesView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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("")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Coverage Gap: While the test correctly verifies the onChange behavior with nullish coalescing, it doesn't test the actual save functionality or the trimming that should happen on save. Consider adding tests for the complete save flow.

// 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
})
})
})
37 changes: 29 additions & 8 deletions webview-ui/src/components/settings/PromptsSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Using value ?? undefined is redundant since the nullish coalescing operator already handles null/undefined. This should just be const finalValue = value.


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)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical Issue: This state update for CONDENSE type is redundant. The parent component already manages customSupportPrompts state through the setCustomSupportPrompts prop. This duplicate state management could cause synchronization issues and unnecessary re-renders.

Consider removing lines 69-76 since the state is already being updated properly through the parent's setter function.

} else {
const updatedPrompts = { ...customSupportPrompts, [type]: value }
const updatedPrompts = { ...customSupportPrompts }
if (finalValue === undefined) {
delete updatedPrompts[type]
} else {
updatedPrompts[type] = finalValue
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: This pattern of updating customSupportPrompts by either deleting or setting keys is repeated multiple times (lines 71-75, 79-83, 96-98, 100-102). Consider extracting this into a helper function:

const updatePromptsObject = (prompts: Record<string, string | undefined>, key: string, value: string | undefined) => {
  const updated = { ...prompts };
  if (value === undefined) {
    delete updated[key];
  } else {
    updated[key] = value;
  }
  return updated;
}

setCustomSupportPrompts(updatedPrompts)
}
}
Expand All @@ -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]
Expand All @@ -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)
}
Expand Down Expand Up @@ -145,12 +167,11 @@ const PromptsSettings = ({ customSupportPrompts, setCustomSupportPrompts }: Prom
<VSCodeTextArea
resize="vertical"
value={getSupportPromptValue(activeSupportOption)}
onChange={(e) => {
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)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical Issue: The PR description mentions "Trimming only happens on save, not on every keystroke", but I don't see any trimming logic when the Save button is actually clicked. The values should be trimmed before being persisted to storage.

Consider adding a save handler that trims values before persisting them.

}}
rows={6}
className="w-full"
Expand Down
Loading