@@ -3,15 +3,27 @@ import React from 'react';
33import { AnimatePresence } from 'framer-motion' ;
44import { useAppLogic } from './hooks/useAppLogic' ;
55import Toast from './components/Toast' ;
6- import SettingsDialog from './components/SettingsDialog' ;
76import Header from './components/Header' ;
87import MainContent from './components/MainContent' ;
98import StatusBar from './components/StatusBar' ;
10- import SearchDialog from './components/SearchDialog' ;
119import ConfirmationDialog from './components/ConfirmationDialog' ;
12- import AIChat from './components/AIChat' ;
13- import FileRankDialog from './components/FileRankDialog' ;
14- import KeyboardShortcutsDialog from './components/KeyboardShortcutsDialog' ;
10+
11+ // Lazy-load heavy components
12+ const SettingsDialog = React . lazy ( ( ) => import ( './components/SettingsDialog' ) ) ;
13+ const SearchDialog = React . lazy ( ( ) => import ( './components/SearchDialog' ) ) ;
14+ const FileRankDialog = React . lazy ( ( ) => import ( './components/FileRankDialog' ) ) ;
15+ const KeyboardShortcutsDialog = React . lazy ( ( ) => import ( './components/KeyboardShortcutsDialog' ) ) ;
16+
17+ const SuspenseFallback = ( ) => null ;
18+
19+ function getProgressWidth ( message : string | null ) : string {
20+ if ( ! message ) return '90%' ;
21+ const match = message . match ( / ( \d + ) \/ ( \d + ) / ) ;
22+ if ( ! match ) return '90%' ;
23+ const current = parseInt ( match [ 1 ] ) ;
24+ const total = Math . max ( 1 , parseInt ( match [ 2 ] ) ) ;
25+ return `${ Math . min ( 95 , ( current / total ) * 100 ) } %` ;
26+ }
1527
1628const App : React . FC = ( ) => {
1729 const codeViewRef = React . useRef < HTMLDivElement > ( null ) ;
@@ -21,17 +33,19 @@ const App: React.FC = () => {
2133 const { state, handlers, settings } = logic ;
2234
2335 return (
24- < div
36+ < div
2537 className = "flex flex-col h-full bg-light-bg dark:bg-dark-bg"
26- onDragEnter = { ( e ) => { e . preventDefault ( ) ; e . stopPropagation ( ) ; handlers . setIsDragging ( true ) ; } }
27- onDragOver = { ( e ) => { e . preventDefault ( ) ; e . stopPropagation ( ) ; } }
28- onDragLeave = { ( e ) => { e . preventDefault ( ) ; e . stopPropagation ( ) ; handlers . setIsDragging ( false ) ; } }
38+ onDragEnter = { ( e ) => { e . preventDefault ( ) ; e . stopPropagation ( ) ; handlers . setIsDragging ( true ) ; } }
39+ onDragOver = { ( e ) => { e . preventDefault ( ) ; e . stopPropagation ( ) ; } }
40+ onDragLeave = { ( e ) => { e . preventDefault ( ) ; e . stopPropagation ( ) ; handlers . setIsDragging ( false ) ; } }
2941 onDrop = { handlers . handleDrop }
42+ role = "application"
43+ aria-label = "Structure Insight 代码分析工具"
3044 >
3145 { /* Loading progress bar */ }
3246 { state . isLoading && (
33- < div className = "fixed top-0 left-0 right-0 z-50 h-0.5 bg-light-border dark:bg-dark-border overflow-hidden" >
34- < div className = "h-full bg-gradient-to-r from-primary to-indigo-500 animate-pulse " style = { { width : '90%' } } />
47+ < div className = "fixed top-0 left-0 right-0 z-50 h-1 bg-light-border dark:bg-dark-border overflow-hidden" >
48+ < div className = "h-full bg-gradient-to-r from-primary to-indigo-500 transition-all duration-300 ease-out " style = { { width : getProgressWidth ( state . progressMessage ) } } />
3549 </ div >
3650 ) }
3751 < Header
@@ -42,7 +56,6 @@ const App: React.FC = () => {
4256 onCancel = { handlers . handleCancel }
4357 onSettings = { ( ) => handlers . setIsSettingsOpen ( true ) }
4458 onToggleSearch = { ( ) => handlers . setIsSearchOpen ( true ) }
45- onToggleAiChat = { ( ) => handlers . setIsAiChatOpen ( true ) }
4659 onToggleFileRank = { ( ) => handlers . setIsFileRankOpen ( true ) }
4760 onShowStructure = { ( ) => handlers . setActiveView ( 'structure' ) }
4861 hasContent = { ! ! state . processedData }
@@ -57,8 +70,9 @@ const App: React.FC = () => {
5770 fileCount = { state . stats . fileCount }
5871 totalLines = { state . stats . totalLines }
5972 totalChars = { state . stats . totalChars }
60- selectedFileName = { state . selectedFile ?. name }
73+ selectedFileName = { state . selectedFile ?. path . split ( '/' ) . pop ( ) }
6174 isDark = { state . isDark }
75+ processedData = { state . processedData }
6276 />
6377 ) }
6478
@@ -72,18 +86,21 @@ const App: React.FC = () => {
7286
7387 < AnimatePresence >
7488 { state . isSearchOpen && (
75- < SearchDialog
89+ < React . Suspense fallback = { < SuspenseFallback /> } >
90+ < SearchDialog
7691 onClose = { ( ) => handlers . setIsSearchOpen ( false ) }
7792 onSearch = { handlers . handleSearch }
7893 onNavigate = { handlers . handleNavigate }
7994 resultsCount = { state . searchResults . length }
8095 currentIndex = { state . activeResultIndex }
8196 />
97+ </ React . Suspense >
8298 ) }
8399 </ AnimatePresence >
84-
100+
85101 < AnimatePresence >
86102 { state . isFileRankOpen && (
103+ < React . Suspense fallback = { < SuspenseFallback /> } >
87104 < FileRankDialog
88105 isOpen = { state . isFileRankOpen }
89106 onClose = { ( ) => handlers . setIsFileRankOpen ( false ) }
@@ -93,17 +110,13 @@ const App: React.FC = () => {
93110 onDeleteFile = { handlers . handleDeleteFile }
94111 onToggleExclude = { handlers . handleToggleExclude }
95112 />
113+ </ React . Suspense >
96114 ) }
97115 </ AnimatePresence >
98116
99- < AIChat
100- isOpen = { state . isAiChatOpen }
101- onClose = { ( ) => handlers . setIsAiChatOpen ( false ) }
102- projectData = { state . processedData }
103- />
104-
105117 < AnimatePresence >
106118 { state . isSettingsOpen && (
119+ < React . Suspense fallback = { < SuspenseFallback /> } >
107120 < SettingsDialog
108121 isOpen = { state . isSettingsOpen }
109122 onClose = { ( ) => handlers . setIsSettingsOpen ( false ) }
@@ -120,19 +133,32 @@ const App: React.FC = () => {
120133 onSetMaxCharsThreshold = { settings . setMaxCharsThreshold }
121134 wordWrap = { state . wordWrap }
122135 onToggleWordWrap = { ( ) => settings . setWordWrap ( ! state . wordWrap ) }
136+ includeFileSummary = { state . includeFileSummary }
137+ onToggleIncludeFileSummary = { ( ) => settings . setIncludeFileSummary ( ! state . includeFileSummary ) }
138+ includeDirectoryStructure = { state . includeDirectoryStructure }
139+ onToggleIncludeDirectoryStructure = { ( ) => settings . setIncludeDirectoryStructure ( ! state . includeDirectoryStructure ) }
140+ includeGitDiffs = { state . includeGitDiffs }
141+ onToggleIncludeGitDiffs = { ( ) => settings . setIncludeGitDiffs ( ! state . includeGitDiffs ) }
142+ exportHeaderText = { state . exportHeaderText }
143+ onSetExportHeaderText = { settings . setExportHeaderText }
144+ exportInstructionText = { state . exportInstructionText }
145+ onSetExportInstructionText = { settings . setExportInstructionText }
123146 />
147+ </ React . Suspense >
124148 ) }
125149 </ AnimatePresence >
126150 < AnimatePresence >
127151 { state . isShortcutsOpen && (
152+ < React . Suspense fallback = { < SuspenseFallback /> } >
128153 < KeyboardShortcutsDialog
129154 isOpen = { state . isShortcutsOpen }
130155 onClose = { ( ) => handlers . setIsShortcutsOpen ( false ) }
131156 />
157+ </ React . Suspense >
132158 ) }
133159 </ AnimatePresence >
134160 < AnimatePresence >
135- { state . toastMessage && < Toast message = { state . toastMessage } onDone = { ( ) => handlers . setToastMessage ( null ) } /> }
161+ { state . toastMessage && < Toast message = { state . toastMessage } onDone = { ( ) => handlers . setToastMessage ( null ) } type = { state . toastType } /> }
136162 </ AnimatePresence >
137163 </ div >
138164 ) ;
0 commit comments