@@ -9,6 +9,8 @@ import { StartHere } from './pages/StartHere';
99import { Overview } from './pages/Overview' ;
1010
1111// 懒加载的页面
12+ const EndToEndWalkthrough = lazy ( ( ) => import ( './pages/EndToEndWalkthrough' ) . then ( m => ( { default : m . EndToEndWalkthrough } ) ) ) ;
13+ const UpstreamDiffOverview = lazy ( ( ) => import ( './pages/UpstreamDiffOverview' ) . then ( m => ( { default : m . UpstreamDiffOverview } ) ) ) ;
1214const StartupFlow = lazy ( ( ) => import ( './pages/StartupFlow' ) . then ( m => ( { default : m . StartupFlow } ) ) ) ;
1315const StartupChain = lazy ( ( ) => import ( './pages/StartupChain' ) . then ( m => ( { default : m . StartupChain } ) ) ) ;
1416const ConfigSystem = lazy ( ( ) => import ( './pages/ConfigSystem' ) . then ( m => ( { default : m . ConfigSystem } ) ) ) ;
@@ -64,6 +66,7 @@ function PageLoading() {
6466}
6567
6668function App ( ) {
69+ const [ isMobileSidebarOpen , setIsMobileSidebarOpen ] = useState ( false ) ;
6770 const validTabIds = useMemo ( ( ) => new Set ( flatNavItems . map ( ( i ) => i . id ) ) , [ ] ) ;
6871
6972 const getTabFromUrl = useCallback ( ( ) => {
@@ -92,6 +95,29 @@ function App() {
9295 [ validTabIds ]
9396 ) ;
9497
98+ const activeTabLabel = useMemo ( ( ) => {
99+ const item = flatNavItems . find ( ( i ) => i . id === activeTab ) ;
100+ return item ?. label ?? 'Innies CLI' ;
101+ } , [ activeTab ] ) ;
102+
103+ useEffect ( ( ) => {
104+ if ( ! isMobileSidebarOpen ) return ;
105+ const onKeyDown = ( e : KeyboardEvent ) => {
106+ if ( e . key === 'Escape' ) setIsMobileSidebarOpen ( false ) ;
107+ } ;
108+ window . addEventListener ( 'keydown' , onKeyDown ) ;
109+ return ( ) => window . removeEventListener ( 'keydown' , onKeyDown ) ;
110+ } , [ isMobileSidebarOpen ] ) ;
111+
112+ useEffect ( ( ) => {
113+ if ( ! isMobileSidebarOpen ) return ;
114+ const prevOverflow = document . body . style . overflow ;
115+ document . body . style . overflow = 'hidden' ;
116+ return ( ) => {
117+ document . body . style . overflow = prevOverflow ;
118+ } ;
119+ } , [ isMobileSidebarOpen ] ) ;
120+
95121 useEffect ( ( ) => {
96122 const onPopState = ( ) => {
97123 setActiveTab ( getTabFromUrl ( ) ) ;
@@ -112,6 +138,10 @@ function App() {
112138 switch ( activeTab ) {
113139 case 'start-here' :
114140 return < StartHere onNavigate = { ( tab ) => navigateToTab ( tab ) } /> ;
141+ case 'e2e' :
142+ return < EndToEndWalkthrough /> ;
143+ case 'upstream-diff' :
144+ return < UpstreamDiffOverview /> ;
115145 case 'overview' :
116146 return < Overview /> ;
117147 case 'startup' :
@@ -203,12 +233,60 @@ function App() {
203233
204234 return (
205235 < div className = "flex min-h-screen bg-gray-950" >
206- { /* Sidebar */ }
207- < Sidebar activeTab = { activeTab } onTabChange = { ( tab ) => navigateToTab ( tab ) } />
236+ { /* Desktop Sidebar */ }
237+ < div className = "hidden md:block" >
238+ < Sidebar activeTab = { activeTab } onTabChange = { ( tab ) => navigateToTab ( tab ) } />
239+ </ div >
240+
241+ { /* Mobile Sidebar Drawer */ }
242+ { isMobileSidebarOpen && (
243+ < div className = "md:hidden fixed inset-0 z-50" >
244+ < button
245+ aria-label = "Close sidebar overlay"
246+ onClick = { ( ) => setIsMobileSidebarOpen ( false ) }
247+ className = "absolute inset-0 bg-black/60"
248+ />
249+ < div className = "absolute inset-y-0 left-0 w-72 max-w-[85vw] shadow-2xl" >
250+ < Sidebar
251+ activeTab = { activeTab }
252+ onTabChange = { ( tab ) => {
253+ navigateToTab ( tab ) ;
254+ setIsMobileSidebarOpen ( false ) ;
255+ } }
256+ />
257+ </ div >
258+ </ div >
259+ ) }
208260
209261 { /* Main Content */ }
210262 < main className = "flex-1 overflow-y-auto" >
211- < div className = "max-w-5xl mx-auto p-8" >
263+ { /* Mobile Top Bar */ }
264+ < div className = "md:hidden sticky top-0 z-20 bg-gray-950/80 backdrop-blur border-b border-gray-800" >
265+ < div className = "px-4 py-3 flex items-center gap-3" >
266+ < button
267+ onClick = { ( ) => setIsMobileSidebarOpen ( true ) }
268+ className = "px-3 py-2 rounded-lg bg-gray-900/30 border border-gray-800 text-gray-200 hover:bg-gray-800/40"
269+ aria-label = "Open sidebar"
270+ >
271+ ☰
272+ </ button >
273+ < div className = "flex-1 min-w-0" >
274+ < div className = "text-sm text-gray-200 truncate" > { activeTabLabel } </ div >
275+ < div className = "text-xs text-gray-500 truncate" >
276+ 搜索页面:Ctrl/⌘ + K
277+ </ div >
278+ </ div >
279+ < button
280+ onClick = { ( ) => navigateToTab ( 'start-here' ) }
281+ className = "px-3 py-2 rounded-lg bg-gray-900/30 border border-gray-800 text-gray-200 hover:bg-gray-800/40"
282+ aria-label = "Go to start"
283+ >
284+ ⌂
285+ </ button >
286+ </ div >
287+ </ div >
288+
289+ < div className = "max-w-5xl mx-auto p-4 md:p-8" >
212290 < div className = "animate-fadeIn" >
213291 < PageLayout activeTab = { activeTab } onNavigate = { navigateToTab } >
214292 < Suspense fallback = { < PageLoading /> } >
0 commit comments