@@ -27,18 +27,38 @@ import {
2727} from '@/constants/instructions'
2828import { default_system_instructions } from '@shared/constants/default-system-instructions'
2929
30- type NavItem =
30+ export type NavItem =
3131 | 'general'
32+ | 'checkpoints'
33+ | 'edit-format'
34+ | 'chatbots'
3235 | 'model-providers'
3336 | 'configurations'
37+ | 'defaults'
38+ | 'instructions'
3439
35- type NavConfigItem = { id : NavItem ; label : TranslationKey }
40+ type NavConfigItem = { id : NavItem ; label : TranslationKey ; is_nested ?: boolean }
3641
3742const NAV_ITEMS_CONFIG : NavConfigItem [ ] = [
3843 {
3944 id : 'general' ,
4045 label : 'sidebar.general'
4146 } ,
47+ {
48+ id : 'checkpoints' ,
49+ label : 'general.checkpoints.title' ,
50+ is_nested : true
51+ } ,
52+ {
53+ id : 'edit-format' ,
54+ label : 'general.edit-formats.title' ,
55+ is_nested : true
56+ } ,
57+ {
58+ id : 'chatbots' ,
59+ label : 'general.chatbots.title' ,
60+ is_nested : true
61+ } ,
4262 {
4363 id : 'model-providers' ,
4464 label : 'sidebar.model-providers'
@@ -47,6 +67,16 @@ const NAV_ITEMS_CONFIG: NavConfigItem[] = [
4767 id : 'configurations' ,
4868 label : 'sidebar.configurations'
4969 } ,
70+ {
71+ id : 'defaults' ,
72+ label : 'configurations.defaults.title' ,
73+ is_nested : true
74+ } ,
75+ {
76+ id : 'instructions' ,
77+ label : 'configurations.instructions.title' ,
78+ is_nested : true
79+ }
5080]
5181
5282type Props = {
@@ -129,43 +159,23 @@ export const Home: React.FC<Props> = (props) => {
129159 const scroll_container_ref = useRef < HTMLDivElement > ( null )
130160 const section_refs = useRef < Record < NavItem , HTMLDivElement | null > > ( {
131161 general : null ,
162+ checkpoints : null ,
163+ 'edit-format' : null ,
164+ chatbots : null ,
132165 'model-providers' : null ,
133- configurations : null
166+ configurations : null ,
167+ defaults : null ,
168+ instructions : null
134169 } )
170+
171+ const set_section_ref = useCallback ( ( id : NavItem , el : HTMLDivElement | null ) => {
172+ section_refs . current [ id ] = el
173+ } , [ ] )
174+
135175 const [ commit_instructions , set_commit_instructions ] = useState ( '' )
136176 const [ voice_input_instructions , set_voice_input_instructions ] = useState ( '' )
137177 const [ edit_context_instructions , set_edit_context_instructions ] =
138178 useState ( '' )
139- const [ stuck_sections , set_stuck_sections ] = useState ( new Set < NavItem > ( ) )
140-
141- const handle_stuck_change = useCallback ( ( id : NavItem , is_stuck : boolean ) => {
142- set_stuck_sections ( ( prev ) => {
143- const is_currently_stuck = prev . has ( id )
144- if ( is_stuck === is_currently_stuck ) {
145- return prev
146- }
147- const next = new Set ( prev )
148- if ( is_stuck ) {
149- next . add ( id )
150- } else {
151- next . delete ( id )
152- }
153- return next
154- } )
155- } , [ ] )
156-
157- const general_on_stuck_change = useCallback (
158- ( is_stuck : boolean ) => handle_stuck_change ( 'general' , is_stuck ) ,
159- [ handle_stuck_change ]
160- )
161- const model_providers_on_stuck_change = useCallback (
162- ( is_stuck : boolean ) => handle_stuck_change ( 'model-providers' , is_stuck ) ,
163- [ handle_stuck_change ]
164- )
165- const configurations_on_stuck_change = useCallback (
166- ( is_stuck : boolean ) => handle_stuck_change ( 'configurations' , is_stuck ) ,
167- [ handle_stuck_change ]
168- )
169179
170180 const get_has_warning = ( id : NavItem ) : boolean => {
171181 if ( id == 'model-providers' ) {
@@ -177,9 +187,37 @@ export const Home: React.FC<Props> = (props) => {
177187 }
178188 }
179189
180- const nav_item_ids = NAV_ITEMS_CONFIG . map ( ( item ) => item . id )
181- const active_nav_item_id =
182- nav_item_ids . filter ( ( id ) => stuck_sections . has ( id ) ) . pop ( ) || nav_item_ids [ 0 ]
190+ const [ active_nav_item_id , set_active_nav_item_id ] = useState < NavItem > ( NAV_ITEMS_CONFIG [ 0 ] . id )
191+
192+ useEffect ( ( ) => {
193+ const scroll_container = scroll_container_ref . current
194+ if ( ! scroll_container ) return
195+
196+ const handle_scroll = ( ) => {
197+ const container_rect = scroll_container . getBoundingClientRect ( )
198+ let new_active_id = NAV_ITEMS_CONFIG [ 0 ] . id
199+
200+ for ( const item of NAV_ITEMS_CONFIG ) {
201+ const el = section_refs . current [ item . id ]
202+ if ( el ) {
203+ const rect = el . getBoundingClientRect ( )
204+ if ( rect . top <= container_rect . top + 150 ) {
205+ new_active_id = item . id
206+ }
207+ }
208+ }
209+ set_active_nav_item_id ( new_active_id )
210+ }
211+
212+ scroll_container . addEventListener ( 'scroll' , handle_scroll )
213+ window . addEventListener ( 'resize' , handle_scroll )
214+ setTimeout ( handle_scroll , 50 )
215+
216+ return ( ) => {
217+ scroll_container . removeEventListener ( 'scroll' , handle_scroll )
218+ window . removeEventListener ( 'resize' , handle_scroll )
219+ }
220+ } , [ ] )
183221
184222 useEffect ( ( ) => {
185223 set_commit_instructions ( props . commit_message_instructions || '' )
@@ -202,7 +240,14 @@ export const Home: React.FC<Props> = (props) => {
202240 const section_rect = section . getBoundingClientRect ( )
203241
204242 const offset = section_rect . top - container_rect . top
205- const target_scroll_top = scroll_container . scrollTop + offset
243+
244+ let extra_offset = 0
245+ const is_subsection = NAV_ITEMS_CONFIG . find ( ( i ) => i . id === item_id ) ?. is_nested
246+ if ( is_subsection ) {
247+ extra_offset = - 112
248+ }
249+
250+ const target_scroll_top = scroll_container . scrollTop + offset + extra_offset
206251
207252 scroll_container . scrollTo ( {
208253 top : target_scroll_top ,
@@ -240,12 +285,15 @@ export const Home: React.FC<Props> = (props) => {
240285 is_active = { item . id === active_nav_item_id }
241286 has_warning = { get_has_warning ( item . id ) }
242287 on_click = { ( e ) => handle_nav_click ( e , item . id ) }
288+ is_nested = { item . is_nested }
289+ is_last_nested = { item . is_nested && ! NAV_ITEMS_CONFIG [ i + 1 ] ?. is_nested }
243290 />
244291 )
245292 } ) }
246293 >
247294 < GeneralSection
248- ref = { ( el ) => ( section_refs . current [ 'general' ] = el ) }
295+ ref = { ( el ) => set_section_ref ( 'general' , el ) }
296+ set_section_ref = { set_section_ref }
249297 context_size_warning_threshold = { props . context_size_warning_threshold }
250298 on_context_size_warning_threshold_change = {
251299 props . on_context_size_warning_threshold_change
@@ -288,7 +336,6 @@ export const Home: React.FC<Props> = (props) => {
288336 ai_studio_user_id = { props . ai_studio_user_id }
289337 on_gemini_user_id_change = { props . on_gemini_user_id_change }
290338 on_ai_studio_user_id_change = { props . on_ai_studio_user_id_change }
291- on_stuck_change = { general_on_stuck_change }
292339 auto_run_intelligent_update = { props . auto_run_intelligent_update }
293340 on_auto_run_intelligent_update_change = { props . on_auto_run_intelligent_update_change }
294341 include_prompts_in_commit_messages = { props . include_prompts_in_commit_messages }
@@ -298,10 +345,9 @@ export const Home: React.FC<Props> = (props) => {
298345 />
299346
300347 < UiSection
301- ref = { ( el ) => ( section_refs . current [ 'model-providers' ] = el ) }
348+ ref = { ( el ) => set_section_ref ( 'model-providers' , el ) }
302349 title = { t ( 'sidebar.model-providers' ) }
303350 subtitle = { t ( 'model-providers.subtitle' ) }
304- on_stuck_change = { model_providers_on_stuck_change }
305351 actions = {
306352 < UiButton on_click = { ( ) => props . on_add_provider ( ) } >
307353 { t ( 'action.add-new' ) }
@@ -381,7 +427,8 @@ export const Home: React.FC<Props> = (props) => {
381427 </ UiSection >
382428
383429 < ConfigurationsSection
384- ref = { ( el ) => ( section_refs . current [ 'configurations' ] = el ) }
430+ ref = { ( el ) => set_section_ref ( 'configurations' , el ) }
431+ set_section_ref = { set_section_ref }
385432 api_configurations = { props . api_configurations }
386433 defaults = { props . defaults }
387434 set_api_configurations = { props . set_api_configurations }
@@ -392,7 +439,6 @@ export const Home: React.FC<Props> = (props) => {
392439 on_delete_api_configuration = { props . on_delete_api_configuration }
393440 on_set_default_api_configuration = { props . on_set_default_api_configuration }
394441 on_select_default_api_configuration = { props . on_select_default_api_configuration }
395- on_stuck_change = { configurations_on_stuck_change }
396442 edit_context_instructions = { edit_context_instructions }
397443 commit_instructions = { commit_instructions }
398444 voice_input_instructions = { voice_input_instructions }
0 commit comments