From 54edab571a7c317d9309aa24b5ec4444f34d88c6 Mon Sep 17 00:00:00 2001 From: Chris Hasson Date: Sat, 14 Jun 2025 07:36:14 -0700 Subject: [PATCH] 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. --- src/core/webview/webviewMessageHandler.ts | 25 +++--------------- src/shared/WebviewMessage.ts | 1 - .../components/settings/PromptsSettings.tsx | 26 +++++++++---------- .../src/components/settings/SettingsView.tsx | 20 +++++++++++++- .../src/context/ExtensionStateContext.tsx | 15 +++-------- 5 files changed, 38 insertions(+), 49 deletions(-) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 673f1bc17b..a4d9dafecf 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -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") ?? {} diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 7574959e14..5186c716b9 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -118,7 +118,6 @@ export interface WebviewMessage { | "mode" | "updatePrompt" | "updateSupportPrompt" - | "resetSupportPrompt" | "getSystemPrompt" | "copySystemPrompt" | "systemPrompt" diff --git a/webview-ui/src/components/settings/PromptsSettings.tsx b/webview-ui/src/components/settings/PromptsSettings.tsx index ffeffca8ea..568b8eeee1 100644 --- a/webview-ui/src/components/settings/PromptsSettings.tsx +++ b/webview-ui/src/components/settings/PromptsSettings.tsx @@ -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 + setCustomSupportPrompts: (prompts: Record) => 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 => { diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index aea9457994..5a330c8996 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -171,6 +171,7 @@ const SettingsView = forwardRef(({ onDone, t customCondensingPrompt, codebaseIndexConfig, codebaseIndexModels, + customSupportPrompts, } = cachedState const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration]) @@ -242,6 +243,17 @@ const SettingsView = forwardRef(({ onDone, t }) }, []) + const setCustomSupportPromptsField = useCallback((prompts: Record) => { + 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(({ 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(({ onDone, t )} {/* Prompts Section */} - {activeTab === "prompts" && } + {activeTab === "prompts" && ( + + )} {/* Experimental Section */} {activeTab === "experimental" && ( diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index e15c247603..ab79f63df8 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -123,29 +123,22 @@ export interface ExtensionStateContextType extends ExtensionState { export const ExtensionStateContext = createContext(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 } }