Fixes unsaved changes prompt on unchanged settings (#8230).

This commit is contained in:
Robert McIntyre 2026-02-07 18:21:12 -08:00
parent cfb6041648
commit cc47997ca7
3 changed files with 28 additions and 5 deletions

View file

@ -171,7 +171,8 @@ const ApiOptions = ({
// Only update if the processed object is different from the current config.
if (JSON.stringify(currentConfigHeaders) !== JSON.stringify(newHeadersObject)) {
setApiConfigurationField("openAiHeaders", newHeadersObject)
// Pass false to indicate this is automatic sync, not a user action
setApiConfigurationField("openAiHeaders", newHeadersObject, false)
}
},
300,

View file

@ -256,9 +256,23 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
const previousValue = prevState.apiConfiguration?.[field]
// Helper to check if two values are semantically equal
const areValuesEqual = (a: any, b: any): boolean => {
// Same reference
if (a === b) return true
// Both null/undefined
if (a == null && b == null) return true
// Different types
if (typeof a !== typeof b) return false
// For objects/arrays, do deep comparison via JSON (good enough for settings)
if (typeof a === "object" && typeof b === "object") {
return JSON.stringify(a) === JSON.stringify(b)
}
return false
}
// Only skip change detection for automatic initialization (not user actions)
// This prevents the dirty state when the component initializes and auto-syncs values
// Treat undefined, null, and empty string as uninitialized states
const isInitialSync =
!isUserAction &&
(previousValue === undefined || previousValue === "" || previousValue === null) &&
@ -266,7 +280,10 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
value !== "" &&
value !== null
if (!isInitialSync) {
// Also skip if it's an automatic sync with semantically equal values
const isAutomaticNoOpSync = !isUserAction && areValuesEqual(previousValue, value)
if (!isInitialSync && !isAutomaticNoOpSync) {
setChangeDetected(true)
}
return { ...prevState, apiConfiguration: { ...prevState.apiConfiguration, [field]: value } }

View file

@ -24,7 +24,11 @@ import { ThinkingBudget } from "../ThinkingBudget"
type OpenAICompatibleProps = {
apiConfiguration: ProviderSettings
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
setApiConfigurationField: <K extends keyof ProviderSettings>(
field: K,
value: ProviderSettings[K],
isUserAction?: boolean,
) => void
organizationAllowList: OrganizationAllowList
modelValidationError?: string
simplifySettings?: boolean
@ -88,7 +92,8 @@ export const OpenAICompatible = ({
useEffect(() => {
const timer = setTimeout(() => {
const headerObject = convertHeadersToObject(customHeaders)
setApiConfigurationField("openAiHeaders", headerObject)
// Pass false to indicate this is automatic sync, not a user action
setApiConfigurationField("openAiHeaders", headerObject, false)
}, 300)
return () => clearTimeout(timer)