@@ -153,7 +153,7 @@ interface AppContentProps {
153
153
154
154
const AppContent : React . FC < AppContentProps > = ( { project, setProject } ) : React . JSX . Element => {
155
155
const { t, i18n } = useTranslation ( ) ;
156
- const { settings, updateLanguage, updateTheme, storage, isLoading } = useUserSettings ( ) ;
156
+ const { settings, updateLanguage, updateTheme, updateOpenTabs , getOpenTabs , storage, isLoading } = useUserSettings ( ) ;
157
157
158
158
const [ alertErrorMessage , setAlertErrorMessage ] = React . useState ( '' ) ;
159
159
const [ currentModule , setCurrentModule ] = React . useState < storageModule . Module | null > ( null ) ;
@@ -163,6 +163,7 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
163
163
const [ modulePathToContentText , setModulePathToContentText ] = React . useState < { [ modulePath : string ] : string } > ( { } ) ;
164
164
const [ tabItems , setTabItems ] = React . useState < Tabs . TabItem [ ] > ( [ ] ) ;
165
165
const [ activeTab , setActiveTab ] = React . useState ( '' ) ;
166
+ const [ isLoadingTabs , setIsLoadingTabs ] = React . useState ( false ) ;
166
167
const [ shownPythonToolboxCategories , setShownPythonToolboxCategories ] = React . useState < Set < string > > ( new Set ( ) ) ;
167
168
const [ triggerPythonRegeneration , setTriggerPythonRegeneration ] = React . useState ( 0 ) ;
168
169
const [ leftCollapsed , setLeftCollapsed ] = React . useState ( false ) ;
@@ -384,35 +385,6 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
384
385
handleToolboxSettingsOk ( updatedShownCategories ) ;
385
386
} ;
386
387
387
- /** Creates tab items from project data. */
388
- const createTabItemsFromProject = ( projectData : storageProject . Project ) : Tabs . TabItem [ ] => {
389
- const tabs : Tabs . TabItem [ ] = [
390
- {
391
- key : projectData . robot . modulePath ,
392
- title : t ( 'ROBOT' ) ,
393
- type : TabType . ROBOT ,
394
- } ,
395
- ] ;
396
-
397
- projectData . mechanisms . forEach ( ( mechanism ) => {
398
- tabs . push ( {
399
- key : mechanism . modulePath ,
400
- title : mechanism . className ,
401
- type : TabType . MECHANISM ,
402
- } ) ;
403
- } ) ;
404
-
405
- projectData . opModes . forEach ( ( opmode ) => {
406
- tabs . push ( {
407
- key : opmode . modulePath ,
408
- title : opmode . className ,
409
- type : TabType . OPMODE ,
410
- } ) ;
411
- } ) ;
412
-
413
- return tabs ;
414
- } ;
415
-
416
388
/** Handles toolbox update requests from blocks */
417
389
const handleToolboxUpdateRequest = React . useCallback ( ( e : Event ) => {
418
390
const workspaceId = ( e as CustomEvent ) . detail . workspaceId ;
@@ -581,20 +553,163 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
581
553
}
582
554
} , [ project ] ) ;
583
555
584
- // Update tab items when ever the modules in the project change.
556
+ // Load saved tabs when project changes
585
557
React . useEffect ( ( ) => {
586
- if ( project ) {
587
- const tabs = createTabItemsFromProject ( project ) ;
588
- setTabItems ( tabs ) ;
558
+ const loadSavedTabs = async ( ) => {
559
+ if ( project && ! isLoading ) {
560
+ setIsLoadingTabs ( true ) ;
561
+
562
+ // Add a small delay to ensure UserSettingsProvider context is updated
563
+ await new Promise ( resolve => setTimeout ( resolve , 0 ) ) ;
564
+
565
+ let tabsToSet : Tabs . TabItem [ ] = [ ] ;
566
+ let usedSavedTabs = false ;
567
+
568
+ // Try to load saved tabs first
569
+ try {
570
+ const savedTabPaths = await getOpenTabs ( project . projectName ) ;
571
+
572
+ if ( savedTabPaths . length > 0 ) {
573
+ // Filter saved tabs to only include those that still exist in the project
574
+ const validSavedTabs = savedTabPaths . filter ( ( tabPath : string ) => {
575
+ const module = storageProject . findModuleByModulePath ( project ! , tabPath ) ;
576
+ return module !== null ;
577
+ } ) ;
578
+
579
+ if ( validSavedTabs . length > 0 ) {
580
+ usedSavedTabs = true ;
581
+ // Convert paths back to TabItem objects
582
+ tabsToSet = validSavedTabs . map ( ( path : string ) => {
583
+ const module = storageProject . findModuleByModulePath ( project ! , path ) ;
584
+ if ( ! module ) return null ;
585
+
586
+ let type : TabType ;
587
+ let title : string ;
588
+
589
+ switch ( module . moduleType ) {
590
+ case storageModule . ModuleType . ROBOT :
591
+ type = TabType . ROBOT ;
592
+ title = t ( 'ROBOT' ) ;
593
+ break ;
594
+ case storageModule . ModuleType . MECHANISM :
595
+ type = TabType . MECHANISM ;
596
+ title = module . className ;
597
+ break ;
598
+ case storageModule . ModuleType . OPMODE :
599
+ type = TabType . OPMODE ;
600
+ title = module . className ;
601
+ break ;
602
+ default :
603
+ return null ;
604
+ }
605
+
606
+ return {
607
+ key : path ,
608
+ title,
609
+ type,
610
+ } ;
611
+ } ) . filter ( ( item ) : item is Tabs . TabItem => item !== null ) ;
612
+ }
613
+ }
614
+ } catch ( error ) {
615
+ console . error ( 'Failed to load saved tabs:' , error ) ;
616
+ }
617
+
618
+ // If no saved tabs or loading failed, create default tabs (all project files)
619
+ if ( tabsToSet . length === 0 ) {
620
+ tabsToSet = [
621
+ {
622
+ key : project . robot . modulePath ,
623
+ title : t ( 'ROBOT' ) ,
624
+ type : TabType . ROBOT ,
625
+ }
626
+ ] ;
627
+
628
+ // Add all mechanisms
629
+ project . mechanisms . forEach ( ( mechanism ) => {
630
+ tabsToSet . push ( {
631
+ key : mechanism . modulePath ,
632
+ title : mechanism . className ,
633
+ type : TabType . MECHANISM ,
634
+ } ) ;
635
+ } ) ;
636
+
637
+ // Add all opmodes
638
+ project . opModes . forEach ( ( opmode ) => {
639
+ tabsToSet . push ( {
640
+ key : opmode . modulePath ,
641
+ title : opmode . className ,
642
+ type : TabType . OPMODE ,
643
+ } ) ;
644
+ } ) ;
645
+ }
646
+
647
+ // Set the tabs
648
+ setTabItems ( tabsToSet ) ;
649
+
650
+ // Only set active tab to robot if no active tab is set or if the current active tab no longer exists
651
+ const currentActiveTabExists = tabsToSet . some ( tab => tab . key === activeTab ) ;
652
+ if ( ! activeTab || ! currentActiveTabExists ) {
653
+ setActiveTab ( project . robot . modulePath ) ;
654
+ }
655
+
656
+ // Only auto-save if we didn't use saved tabs (i.e., this is a new project or the first time)
657
+ if ( ! usedSavedTabs ) {
658
+ try {
659
+ const tabPaths = tabsToSet . map ( tab => tab . key ) ;
660
+ await updateOpenTabs ( project . projectName , tabPaths ) ;
661
+ } catch ( error ) {
662
+ console . error ( 'Failed to auto-save default tabs:' , error ) ;
663
+ }
664
+ }
665
+
666
+ setIsLoadingTabs ( false ) ;
667
+ }
668
+ } ;
669
+
670
+ loadSavedTabs ( ) ;
671
+ } , [ project ?. projectName , isLoading , getOpenTabs ] ) ;
672
+
673
+ // Update tab items when modules in project change (for title updates, etc)
674
+ React . useEffect ( ( ) => {
675
+ if ( project && tabItems . length > 0 ) {
676
+ // Update existing tab titles in case they changed
677
+ const updatedTabs = tabItems . map ( tab => {
678
+ const module = storageProject . findModuleByModulePath ( project , tab . key ) ;
679
+ if ( module && module . moduleType !== storageModule . ModuleType . ROBOT ) {
680
+ return { ...tab , title : module . className } ;
681
+ }
682
+ return tab ;
683
+ } ) ;
589
684
590
- // Only set active tab to robot if no active tab is set or if the current active tab no longer exists
591
- const currentActiveTabExists = tabs . some ( tab => tab . key === activeTab ) ;
592
- if ( ! activeTab || ! currentActiveTabExists ) {
593
- setActiveTab ( project . robot . modulePath ) ;
685
+ // Only update if something actually changed
686
+ const titlesChanged = updatedTabs . some ( ( tab , index ) => tab . title !== tabItems [ index ] ?. title ) ;
687
+ if ( titlesChanged ) {
688
+ setTabItems ( updatedTabs ) ;
594
689
}
595
690
}
596
691
} , [ modulePathToContentText ] ) ;
597
692
693
+ // Save tabs when tab list changes (but not during initial loading)
694
+ React . useEffect ( ( ) => {
695
+ const saveTabs = async ( ) => {
696
+ // Don't save tabs while we're in the process of loading them
697
+ if ( project ?. projectName && tabItems . length > 0 && ! isLoadingTabs ) {
698
+ try {
699
+ const tabPaths = tabItems . map ( tab => tab . key ) ;
700
+ await updateOpenTabs ( project . projectName , tabPaths ) ;
701
+ } catch ( error ) {
702
+ console . error ( 'Failed to save open tabs:' , error ) ;
703
+ // Don't show alert for save failures as they're not critical to user workflow
704
+ }
705
+ }
706
+ } ;
707
+
708
+ // Use a small delay to debounce rapid tab changes
709
+ const timeoutId = setTimeout ( saveTabs , 100 ) ;
710
+ return ( ) => clearTimeout ( timeoutId ) ;
711
+ } , [ tabItems , project ?. projectName , isLoadingTabs ] ) ;
712
+
598
713
const { Sider, Content } = Antd . Layout ;
599
714
600
715
return (
0 commit comments