From 5fac78e0ca615506ff2948a6eabed8fb601762dd Mon Sep 17 00:00:00 2001 From: Infinite-Null Date: Tue, 23 Jun 2026 15:29:18 +0530 Subject: [PATCH 1/3] feat: implement draft-based settings updates with a dedicated save button in DeveloperSettings --- .../ai-home/components/DeveloperSettings.tsx | 112 +++++++++++++----- routes/ai-home/style.scss | 4 +- 2 files changed, 86 insertions(+), 30 deletions(-) diff --git a/routes/ai-home/components/DeveloperSettings.tsx b/routes/ai-home/components/DeveloperSettings.tsx index 582be4253..9e70a3604 100644 --- a/routes/ai-home/components/DeveloperSettings.tsx +++ b/routes/ai-home/components/DeveloperSettings.tsx @@ -4,7 +4,7 @@ import { Button, Spinner } from '@wordpress/components'; import { DataForm } from '@wordpress/dataviews'; import type { Field, Form } from '@wordpress/dataviews'; -import { useCallback, useMemo, useRef } from '@wordpress/element'; +import { useCallback, useEffect, useMemo, useRef, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { Notice } from '@wordpress/ui'; @@ -46,8 +46,23 @@ export function DeveloperSettings( { const { settings, update, clear, isSaving } = useDeveloperFeatureSettings( featureId ); + const [ draftSettings, setDraftSettings ] = useState< DeveloperSelection | null >( null ); + const [ isSavingThis, setIsSavingThis ] = useState( false ); + + useEffect( () => { + if ( ! isSaving ) { + setIsSavingThis( false ); + } + }, [ isSaving ] ); + + useEffect( () => { + setDraftSettings( null ); + }, [ settings.provider, settings.model ] ); + + const currentSettings = draftSettings ?? settings; + const getModelElements = useCallback( () => { - const provider = providers.find( ( p ) => p.id === settings.provider ); + const provider = providers.find( ( p ) => p.id === currentSettings.provider ); if ( ! provider ) { return Promise.resolve( [] ); } @@ -58,7 +73,7 @@ export function DeveloperSettings( { label: m.name, } ) ), ] ); - }, [ settings.provider, providers ] ); + }, [ currentSettings.provider, providers ] ); const fields = useMemo< Field< DeveloperSelection >[] >( () => [ @@ -97,18 +112,34 @@ export function DeveloperSettings( { const handleChange = useCallback( ( changes: Partial< DeveloperSelection > ) => { if ( 'provider' in changes ) { - void update( { provider: changes.provider ?? '', model: '' } ); + setDraftSettings( { ...currentSettings, provider: changes.provider ?? '', model: '' } ); } else { - void update( { ...settings, ...changes } ); + setDraftSettings( { ...currentSettings, ...changes } ); } }, - [ update, settings ] + [ currentSettings ] ); - const hasSavedSelection = settings.provider !== '' || settings.model !== ''; + const handleSave = useCallback( () => { + if ( draftSettings ) { + setIsSavingThis( true ); + if ( draftSettings.provider === '' && draftSettings.model === '' ) { + void clear(); + } else { + void update( draftSettings ); + } + } + }, [ draftSettings, update, clear ] ); + + const hasSavedSelection = currentSettings.provider !== '' || currentSettings.model !== ''; + const hasUnsavedChanges = + draftSettings !== null && + ( draftSettings.provider !== settings.provider || + draftSettings.model !== settings.model ); + const hasStaleProvider = - !! settings.provider && - ! providers.find( ( p ) => p.id === settings.provider ); + !! currentSettings.provider && + ! providers.find( ( p ) => p.id === currentSettings.provider ); if ( capability === 'none' ) { return ( @@ -153,30 +184,55 @@ export function DeveloperSettings( { ) }
- data={ settings } + data={ currentSettings } fields={ fields } form={ form } onChange={ handleChange } />
- { hasSavedSelection && ( - - ) } +
+ { ( hasUnsavedChanges || isSavingThis ) && ( + + ) } + { hasSavedSelection && ( + + ) } +
) } diff --git a/routes/ai-home/style.scss b/routes/ai-home/style.scss index 906f93b81..a4415a2c7 100644 --- a/routes/ai-home/style.scss +++ b/routes/ai-home/style.scss @@ -88,7 +88,7 @@ body:has(.ai-settings-page) .components-popover { margin-bottom: var(--wpds-dimension-gap-lg, 12px); } - .ai-developer-mode-fields__reset-button { - margin-top: var(--wpds-dimension-gap-lg, 12px); + .ai-developer-mode-fields__actions { + padding-bottom: var(--wpds-dimension-gap-sm, 8px); } } From a7100ad1f229674f39358f3b9fecf44a7b78950b Mon Sep 17 00:00:00 2001 From: Infinite-Null Date: Tue, 23 Jun 2026 15:41:45 +0530 Subject: [PATCH 2/3] refactor: apply linting fixes and optimize state updates in DeveloperSettings component --- .../ai-home/components/DeveloperSettings.tsx | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/routes/ai-home/components/DeveloperSettings.tsx b/routes/ai-home/components/DeveloperSettings.tsx index 9e70a3604..95fdba768 100644 --- a/routes/ai-home/components/DeveloperSettings.tsx +++ b/routes/ai-home/components/DeveloperSettings.tsx @@ -4,7 +4,13 @@ import { Button, Spinner } from '@wordpress/components'; import { DataForm } from '@wordpress/dataviews'; import type { Field, Form } from '@wordpress/dataviews'; -import { useCallback, useEffect, useMemo, useRef, useState } from '@wordpress/element'; +import { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { Notice } from '@wordpress/ui'; @@ -46,7 +52,8 @@ export function DeveloperSettings( { const { settings, update, clear, isSaving } = useDeveloperFeatureSettings( featureId ); - const [ draftSettings, setDraftSettings ] = useState< DeveloperSelection | null >( null ); + const [ draftSettings, setDraftSettings ] = + useState< DeveloperSelection | null >( null ); const [ isSavingThis, setIsSavingThis ] = useState( false ); useEffect( () => { @@ -62,7 +69,9 @@ export function DeveloperSettings( { const currentSettings = draftSettings ?? settings; const getModelElements = useCallback( () => { - const provider = providers.find( ( p ) => p.id === currentSettings.provider ); + const provider = providers.find( + ( p ) => p.id === currentSettings.provider + ); if ( ! provider ) { return Promise.resolve( [] ); } @@ -112,7 +121,10 @@ export function DeveloperSettings( { const handleChange = useCallback( ( changes: Partial< DeveloperSelection > ) => { if ( 'provider' in changes ) { - setDraftSettings( { ...currentSettings, provider: changes.provider ?? '', model: '' } ); + setDraftSettings( { + provider: changes.provider ?? '', + model: '', + } ); } else { setDraftSettings( { ...currentSettings, ...changes } ); } @@ -131,7 +143,8 @@ export function DeveloperSettings( { } }, [ draftSettings, update, clear ] ); - const hasSavedSelection = currentSettings.provider !== '' || currentSettings.model !== ''; + const hasSavedSelection = + currentSettings.provider !== '' || currentSettings.model !== ''; const hasUnsavedChanges = draftSettings !== null && ( draftSettings.provider !== settings.provider || @@ -203,9 +216,10 @@ export function DeveloperSettings( { @@ -221,12 +235,11 @@ export function DeveloperSettings( { ) ?.focus(); setDraftSettings( { - ...currentSettings, provider: '', model: '', } ); } } - disabled={ isSaving } + disabled={ isSavingThis } accessibleWhenDisabled > { __( 'Reset to default', 'ai' ) } From 8e33f570734c375a9ae24948e12e19f74d70417a Mon Sep 17 00:00:00 2001 From: Infinite-Null Date: Tue, 23 Jun 2026 15:46:21 +0530 Subject: [PATCH 3/3] refactor: migrate inline styles for developer settings actions to scss file --- routes/ai-home/components/DeveloperSettings.tsx | 10 +--------- routes/ai-home/style.scss | 4 ++++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/routes/ai-home/components/DeveloperSettings.tsx b/routes/ai-home/components/DeveloperSettings.tsx index 95fdba768..f6a2736c9 100644 --- a/routes/ai-home/components/DeveloperSettings.tsx +++ b/routes/ai-home/components/DeveloperSettings.tsx @@ -203,15 +203,7 @@ export function DeveloperSettings( { onChange={ handleChange } /> -
+
{ ( hasUnsavedChanges || isSavingThis ) && (