Fix the save/discard/revert flow for Prompt Settings (#4623)

Add support for the save/discard flow for support prompt setting page

Normally when you edit things on the settings pages, the save button lights up,
allowing you to discard your changes. Currently the prompts page doesn't support this flow-
the prompts are immediately saved when they change.

With this change, we use the normal cachedState system in the SettingView, allowing users to dicard changes to their prompts like any other setting.
This removed the need for the resetSupportPrompt event since we send the entire state of the support prompts (same as before).

Test plan:
* Manually verified prompts can be saved/discarded for different types of support prompts.
This commit is contained in:
Chris Hasson 2025-06-14 07:36:14 -07:00 committed by GitHub
parent 69ffa43bab
commit 54edab571a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 38 additions and 49 deletions

View file

@ -829,13 +829,12 @@ export const webviewMessageHandler = async (
break
case "updateSupportPrompt":
try {
if (Object.keys(message?.values ?? {}).length === 0) {
if (!message?.values) {
return
}
const existingPrompts = getGlobalState("customSupportPrompts") ?? {}
const updatedPrompts = { ...existingPrompts, ...message.values }
await updateGlobalState("customSupportPrompts", updatedPrompts)
// Replace all prompts with the new values from the cached state
await updateGlobalState("customSupportPrompts", message.values)
await provider.postStateToWebview()
} catch (error) {
provider.log(
@ -844,24 +843,6 @@ export const webviewMessageHandler = async (
vscode.window.showErrorMessage(t("common:errors.update_support_prompt"))
}
break
case "resetSupportPrompt":
try {
if (!message?.text) {
return
}
const existingPrompts = getGlobalState("customSupportPrompts") ?? {}
const updatedPrompts = { ...existingPrompts }
updatedPrompts[message.text] = undefined
await updateGlobalState("customSupportPrompts", updatedPrompts)
await provider.postStateToWebview()
} catch (error) {
provider.log(
`Error reset support prompt: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
)
vscode.window.showErrorMessage(t("common:errors.reset_support_prompt"))
}
break
case "updatePrompt":
if (message.promptMode && message.customPrompt !== undefined) {
const existingPrompts = getGlobalState("customModePrompts") ?? {}

View file

@ -118,7 +118,6 @@ export interface WebviewMessage {
| "mode"
| "updatePrompt"
| "updateSupportPrompt"
| "resetSupportPrompt"
| "getSystemPrompt"
| "copySystemPrompt"
| "systemPrompt"

View file

@ -11,11 +11,14 @@ import { SectionHeader } from "./SectionHeader"
import { Section } from "./Section"
import { MessageSquare } from "lucide-react"
const PromptsSettings = () => {
const { t } = useAppTranslation()
interface PromptsSettingsProps {
customSupportPrompts: Record<string, string | undefined>
setCustomSupportPrompts: (prompts: Record<string, string | undefined>) => void
}
const { customSupportPrompts, listApiConfigMeta, enhancementApiConfigId, setEnhancementApiConfigId } =
useExtensionState()
const PromptsSettings = ({ customSupportPrompts, setCustomSupportPrompts }: PromptsSettingsProps) => {
const { t } = useAppTranslation()
const { listApiConfigMeta, enhancementApiConfigId, setEnhancementApiConfigId } = useExtensionState()
const [testPrompt, setTestPrompt] = useState("")
const [isEnhancing, setIsEnhancing] = useState(false)
@ -37,19 +40,14 @@ const PromptsSettings = () => {
}, [])
const updateSupportPrompt = (type: SupportPromptType, value: string | undefined) => {
vscode.postMessage({
type: "updateSupportPrompt",
values: {
[type]: value,
},
})
const updatedPrompts = { ...customSupportPrompts, [type]: value }
setCustomSupportPrompts(updatedPrompts)
}
const handleSupportReset = (type: SupportPromptType) => {
vscode.postMessage({
type: "resetSupportPrompt",
text: type,
})
const updatedPrompts = { ...customSupportPrompts }
delete updatedPrompts[type]
setCustomSupportPrompts(updatedPrompts)
}
const getSupportPromptValue = (type: SupportPromptType): string => {

View file

@ -171,6 +171,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
customCondensingPrompt,
codebaseIndexConfig,
codebaseIndexModels,
customSupportPrompts,
} = cachedState
const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration])
@ -242,6 +243,17 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
})
}, [])
const setCustomSupportPromptsField = useCallback((prompts: Record<string, string | undefined>) => {
setCachedState((prevState) => {
if (JSON.stringify(prevState.customSupportPrompts) === JSON.stringify(prompts)) {
return prevState
}
setChangeDetected(true)
return { ...prevState, customSupportPrompts: prompts }
})
}, [])
const isSettingValid = !errorMessage
const handleSubmit = () => {
@ -299,6 +311,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
vscode.postMessage({ type: "alwaysAllowSubtasks", bool: alwaysAllowSubtasks })
vscode.postMessage({ type: "condensingApiConfigId", text: condensingApiConfigId || "" })
vscode.postMessage({ type: "updateCondensingPrompt", text: customCondensingPrompt || "" })
vscode.postMessage({ type: "updateSupportPrompt", values: customSupportPrompts || {} })
vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration })
vscode.postMessage({ type: "telemetrySetting", text: telemetrySetting })
vscode.postMessage({ type: "codebaseIndexConfig", values: codebaseIndexConfig })
@ -653,7 +666,12 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
)}
{/* Prompts Section */}
{activeTab === "prompts" && <PromptsSettings />}
{activeTab === "prompts" && (
<PromptsSettings
customSupportPrompts={customSupportPrompts || {}}
setCustomSupportPrompts={setCustomSupportPromptsField}
/>
)}
{/* Experimental Section */}
{activeTab === "experimental" && (

View file

@ -123,29 +123,22 @@ export interface ExtensionStateContextType extends ExtensionState {
export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
export const mergeExtensionState = (prevState: ExtensionState, newState: ExtensionState) => {
const {
customModePrompts: prevCustomModePrompts,
customSupportPrompts: prevCustomSupportPrompts,
experiments: prevExperiments,
...prevRest
} = prevState
const { customModePrompts: prevCustomModePrompts, experiments: prevExperiments, ...prevRest } = prevState
const {
apiConfiguration,
customModePrompts: newCustomModePrompts,
customSupportPrompts: newCustomSupportPrompts,
customSupportPrompts,
experiments: newExperiments,
...newRest
} = newState
const customModePrompts = { ...prevCustomModePrompts, ...newCustomModePrompts }
const customSupportPrompts = { ...prevCustomSupportPrompts, ...newCustomSupportPrompts }
const experiments = { ...prevExperiments, ...newExperiments }
const rest = { ...prevRest, ...newRest }
// Note that we completely replace the previous apiConfiguration object with
// a new one since the state that is broadcast is the entire apiConfiguration
// and therefore merging is not necessary.
// Note that we completely replace the previous apiConfiguration and customSupportPrompts objects
// with new ones since the state that is broadcast is the entire objects so merging is not necessary.
return { ...rest, apiConfiguration, customModePrompts, customSupportPrompts, experiments }
}