@@ -3,6 +3,7 @@ import { useAppContext } from '../utils/app.context';
3
3
import { CONFIG_DEFAULT , CONFIG_INFO } from '../Config' ;
4
4
import { isDev } from '../Config' ;
5
5
import StorageUtils from '../utils/storage' ;
6
+ import { isBoolean , isNumeric , isString } from '../utils/misc' ;
6
7
7
8
type SettKey = keyof typeof CONFIG_DEFAULT ;
8
9
@@ -52,7 +53,42 @@ export default function SettingDialog({
52
53
} ;
53
54
54
55
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 ) ;
56
92
onClose ( ) ;
57
93
} ;
58
94
@@ -66,6 +102,11 @@ export default function SettingDialog({
66
102
onClose ( ) ;
67
103
} ;
68
104
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
+
69
110
return (
70
111
< dialog className = { `modal ${ show ? 'modal-open' : '' } ` } >
71
112
< div className = "modal-box" >
@@ -79,9 +120,7 @@ export default function SettingDialog({
79
120
configKey = "apiKey"
80
121
configDefault = { CONFIG_DEFAULT }
81
122
value = { localConfig . apiKey }
82
- onChange = { ( value ) =>
83
- setLocalConfig ( { ...localConfig , apiKey : value } )
84
- }
123
+ onChange = { onChange ( 'apiKey' ) }
85
124
/>
86
125
87
126
< label className = "form-control mb-2" >
@@ -92,12 +131,7 @@ export default function SettingDialog({
92
131
className = "textarea textarea-bordered h-24"
93
132
placeholder = { `Default: ${ CONFIG_DEFAULT . systemMessage } ` }
94
133
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 ) }
101
135
/>
102
136
</ label >
103
137
@@ -107,9 +141,7 @@ export default function SettingDialog({
107
141
configKey = { key }
108
142
configDefault = { CONFIG_DEFAULT }
109
143
value = { localConfig [ key ] }
110
- onChange = { ( value ) =>
111
- setLocalConfig ( { ...localConfig , [ key ] : value } )
112
- }
144
+ onChange = { onChange ( key ) }
113
145
/>
114
146
) ) }
115
147
@@ -123,19 +155,15 @@ export default function SettingDialog({
123
155
configKey = "samplers"
124
156
configDefault = { CONFIG_DEFAULT }
125
157
value = { localConfig . samplers }
126
- onChange = { ( value ) =>
127
- setLocalConfig ( { ...localConfig , samplers : value } )
128
- }
158
+ onChange = { onChange ( 'samplers' ) }
129
159
/>
130
160
{ OTHER_SAMPLER_KEYS . map ( ( key ) => (
131
161
< SettingsModalShortInput
132
162
key = { key }
133
163
configKey = { key }
134
164
configDefault = { CONFIG_DEFAULT }
135
165
value = { localConfig [ key ] }
136
- onChange = { ( value ) =>
137
- setLocalConfig ( { ...localConfig , [ key ] : value } )
138
- }
166
+ onChange = { onChange ( key ) }
139
167
/>
140
168
) ) }
141
169
</ div >
@@ -152,9 +180,7 @@ export default function SettingDialog({
152
180
configKey = { key }
153
181
configDefault = { CONFIG_DEFAULT }
154
182
value = { localConfig [ key ] }
155
- onChange = { ( value ) =>
156
- setLocalConfig ( { ...localConfig , [ key ] : value } )
157
- }
183
+ onChange = { onChange ( key ) }
158
184
/>
159
185
) ) }
160
186
</ div >
@@ -171,10 +197,7 @@ export default function SettingDialog({
171
197
className = "checkbox"
172
198
checked = { localConfig . showThoughtInProgress }
173
199
onChange = { ( e ) =>
174
- setLocalConfig ( {
175
- ...localConfig ,
176
- showThoughtInProgress : e . target . checked ,
177
- } )
200
+ onChange ( 'showThoughtInProgress' ) ( e . target . checked )
178
201
}
179
202
/>
180
203
< span className = "ml-4" >
@@ -187,10 +210,7 @@ export default function SettingDialog({
187
210
className = "checkbox"
188
211
checked = { localConfig . excludeThoughtOnReq }
189
212
onChange = { ( e ) =>
190
- setLocalConfig ( {
191
- ...localConfig ,
192
- excludeThoughtOnReq : e . target . checked ,
193
- } )
213
+ onChange ( 'excludeThoughtOnReq' ) ( e . target . checked )
194
214
}
195
215
/>
196
216
< span className = "ml-4" >
@@ -220,10 +240,7 @@ export default function SettingDialog({
220
240
className = "checkbox"
221
241
checked = { localConfig . showTokensPerSecond }
222
242
onChange = { ( e ) =>
223
- setLocalConfig ( {
224
- ...localConfig ,
225
- showTokensPerSecond : e . target . checked ,
226
- } )
243
+ onChange ( 'showTokensPerSecond' ) ( e . target . checked )
227
244
}
228
245
/>
229
246
< span className = "ml-4" > Show tokens per second</ span >
@@ -245,9 +262,7 @@ export default function SettingDialog({
245
262
className = "textarea textarea-bordered h-24"
246
263
placeholder = 'Example: { "mirostat": 1, "min_p": 0.1 }'
247
264
value = { localConfig . custom }
248
- onChange = { ( e ) =>
249
- setLocalConfig ( { ...localConfig , custom : e . target . value } )
250
- }
265
+ onChange = { ( e ) => onChange ( 'custom' ) ( e . target . value ) }
251
266
/>
252
267
</ label >
253
268
</ div >
0 commit comments