@@ -3,6 +3,7 @@ import { useAppContext } from '../utils/app.context';
33import { CONFIG_DEFAULT , CONFIG_INFO } from '../Config' ;
44import { isDev } from '../Config' ;
55import StorageUtils from '../utils/storage' ;
6+ import { isBoolean , isNumeric , isString } from '../utils/misc' ;
67
78type SettKey = keyof typeof CONFIG_DEFAULT ;
89
@@ -52,7 +53,42 @@ export default function SettingDialog({
5253 } ;
5354
5455 const handleSave = ( ) => {
55- saveConfig ( localConfig ) ;
56+ // copy the local config to prevent direct mutation
57+ const newConfig : typeof CONFIG_DEFAULT = JSON . parse (
58+ JSON . stringify ( localConfig )
59+ ) ;
60+ // validate the config
61+ for ( const key in newConfig ) {
62+ const value = newConfig [ key as SettKey ] ;
63+ const mustBeBoolean = isBoolean ( CONFIG_DEFAULT [ key as SettKey ] ) ;
64+ const mustBeString = isString ( CONFIG_DEFAULT [ key as SettKey ] ) ;
65+ const mustBeNumeric = isNumeric ( CONFIG_DEFAULT [ key as SettKey ] ) ;
66+ if ( mustBeString ) {
67+ if ( ! isString ( value ) ) {
68+ alert ( `Value for ${ key } must be string` ) ;
69+ return ;
70+ }
71+ } else if ( mustBeNumeric ) {
72+ const trimedValue = value . toString ( ) . trim ( ) ;
73+ const numVal = Number ( trimedValue ) ;
74+ if ( isNaN ( numVal ) || ! isNumeric ( numVal ) || trimedValue . length === 0 ) {
75+ alert ( `Value for ${ key } must be numeric` ) ;
76+ return ;
77+ }
78+ // force conversion to number
79+ // @ts -expect-error this is safe
80+ newConfig [ key ] = numVal ;
81+ } else if ( mustBeBoolean ) {
82+ if ( ! isBoolean ( value ) ) {
83+ alert ( `Value for ${ key } must be boolean` ) ;
84+ return ;
85+ }
86+ } else {
87+ console . error ( `Unknown default type for key ${ key } ` ) ;
88+ }
89+ }
90+ if ( isDev ) console . log ( 'Saving config' , newConfig ) ;
91+ saveConfig ( newConfig ) ;
5692 onClose ( ) ;
5793 } ;
5894
@@ -66,6 +102,11 @@ export default function SettingDialog({
66102 onClose ( ) ;
67103 } ;
68104
105+ const onChange = ( key : SettKey ) => ( value : string | boolean ) => {
106+ // note: we do not perform validation here, because we may get incomplete value as user is still typing it
107+ setLocalConfig ( { ...localConfig , [ key ] : value } ) ;
108+ } ;
109+
69110 return (
70111 < dialog className = { `modal ${ show ? 'modal-open' : '' } ` } >
71112 < div className = "modal-box" >
@@ -79,9 +120,7 @@ export default function SettingDialog({
79120 configKey = "apiKey"
80121 configDefault = { CONFIG_DEFAULT }
81122 value = { localConfig . apiKey }
82- onChange = { ( value ) =>
83- setLocalConfig ( { ...localConfig , apiKey : value } )
84- }
123+ onChange = { onChange ( 'apiKey' ) }
85124 />
86125
87126 < label className = "form-control mb-2" >
@@ -92,12 +131,7 @@ export default function SettingDialog({
92131 className = "textarea textarea-bordered h-24"
93132 placeholder = { `Default: ${ CONFIG_DEFAULT . systemMessage } ` }
94133 value = { localConfig . systemMessage }
95- onChange = { ( e ) =>
96- setLocalConfig ( {
97- ...localConfig ,
98- systemMessage : e . target . value ,
99- } )
100- }
134+ onChange = { ( e ) => onChange ( 'systemMessage' ) ( e . target . value ) }
101135 />
102136 </ label >
103137
@@ -107,9 +141,7 @@ export default function SettingDialog({
107141 configKey = { key }
108142 configDefault = { CONFIG_DEFAULT }
109143 value = { localConfig [ key ] }
110- onChange = { ( value ) =>
111- setLocalConfig ( { ...localConfig , [ key ] : value } )
112- }
144+ onChange = { onChange ( key ) }
113145 />
114146 ) ) }
115147
@@ -123,19 +155,15 @@ export default function SettingDialog({
123155 configKey = "samplers"
124156 configDefault = { CONFIG_DEFAULT }
125157 value = { localConfig . samplers }
126- onChange = { ( value ) =>
127- setLocalConfig ( { ...localConfig , samplers : value } )
128- }
158+ onChange = { onChange ( 'samplers' ) }
129159 />
130160 { OTHER_SAMPLER_KEYS . map ( ( key ) => (
131161 < SettingsModalShortInput
132162 key = { key }
133163 configKey = { key }
134164 configDefault = { CONFIG_DEFAULT }
135165 value = { localConfig [ key ] }
136- onChange = { ( value ) =>
137- setLocalConfig ( { ...localConfig , [ key ] : value } )
138- }
166+ onChange = { onChange ( key ) }
139167 />
140168 ) ) }
141169 </ div >
@@ -152,9 +180,7 @@ export default function SettingDialog({
152180 configKey = { key }
153181 configDefault = { CONFIG_DEFAULT }
154182 value = { localConfig [ key ] }
155- onChange = { ( value ) =>
156- setLocalConfig ( { ...localConfig , [ key ] : value } )
157- }
183+ onChange = { onChange ( key ) }
158184 />
159185 ) ) }
160186 </ div >
@@ -171,10 +197,7 @@ export default function SettingDialog({
171197 className = "checkbox"
172198 checked = { localConfig . showThoughtInProgress }
173199 onChange = { ( e ) =>
174- setLocalConfig ( {
175- ...localConfig ,
176- showThoughtInProgress : e . target . checked ,
177- } )
200+ onChange ( 'showThoughtInProgress' ) ( e . target . checked )
178201 }
179202 />
180203 < span className = "ml-4" >
@@ -187,10 +210,7 @@ export default function SettingDialog({
187210 className = "checkbox"
188211 checked = { localConfig . excludeThoughtOnReq }
189212 onChange = { ( e ) =>
190- setLocalConfig ( {
191- ...localConfig ,
192- excludeThoughtOnReq : e . target . checked ,
193- } )
213+ onChange ( 'excludeThoughtOnReq' ) ( e . target . checked )
194214 }
195215 />
196216 < span className = "ml-4" >
@@ -220,10 +240,7 @@ export default function SettingDialog({
220240 className = "checkbox"
221241 checked = { localConfig . showTokensPerSecond }
222242 onChange = { ( e ) =>
223- setLocalConfig ( {
224- ...localConfig ,
225- showTokensPerSecond : e . target . checked ,
226- } )
243+ onChange ( 'showTokensPerSecond' ) ( e . target . checked )
227244 }
228245 />
229246 < span className = "ml-4" > Show tokens per second</ span >
@@ -245,9 +262,7 @@ export default function SettingDialog({
245262 className = "textarea textarea-bordered h-24"
246263 placeholder = 'Example: { "mirostat": 1, "min_p": 0.1 }'
247264 value = { localConfig . custom }
248- onChange = { ( e ) =>
249- setLocalConfig ( { ...localConfig , custom : e . target . value } )
250- }
265+ onChange = { ( e ) => onChange ( 'custom' ) ( e . target . value ) }
251266 />
252267 </ label >
253268 </ div >
0 commit comments