@@ -18,6 +18,7 @@ struct MainView: View {
1818 @State private var showCleanSelectedCachesConfirm = false
1919 @State private var projectFilterMode : ProjectFilterMode = . all
2020 @State private var isCleaning = false
21+ @State private var isExpanded = false
2122
2223 enum Tab : String , CaseIterable {
2324 case developer = " Developer "
@@ -59,7 +60,7 @@ struct MainView: View {
5960 mainScreen
6061 }
6162 }
62- . frame ( width: 380 , height: 540 )
63+ . frame ( width: 380 , height: 700 )
6364 . alert ( " Clean Cache " , isPresented: $showCleanConfirm) {
6465 Button ( " Cancel " , role: . cancel) { }
6566 Button ( " Move to Trash " , role: . destructive) {
@@ -156,49 +157,130 @@ struct MainView: View {
156157 // MARK: - Main Screen
157158 var mainScreen : some View {
158159 ZStack {
159- VStack ( spacing: 0 ) {
160- // Header
161- headerView
162-
163- Divider ( )
164-
165- // Permission warnings (if any)
166- if diskMonitor. notificationPermission == . denied {
167- permissionBanner
160+ if isExpanded {
161+ // Expanded view: full-height content with back button
162+ VStack ( spacing: 0 ) {
163+ HStack {
164+ Button ( action: { withAnimation ( . spring( response: 0.4 , dampingFraction: 0.85 ) ) { isExpanded = false } } ) {
165+ HStack ( spacing: 4 ) {
166+ Image ( systemName: " chevron.left " )
167+ . font ( . system( size: 11 ) )
168+ Text ( " Back " )
169+ . font ( . system( size: 12 ) )
170+ }
171+ . padding ( . horizontal, 8 )
172+ . padding ( . vertical, 6 )
173+ . contentShape ( Rectangle ( ) )
174+ }
175+ . buttonStyle ( . plain)
176+ . foregroundColor ( . blue)
177+
178+ Spacer ( )
179+
180+ Text ( selectedTab. rawValue)
181+ . font ( . system( size: 13 , weight: . semibold) )
182+
183+ Spacer ( )
184+
185+ if diskMonitor. isScanning {
186+ ProgressView ( )
187+ . scaleEffect ( 0.7 )
188+ }
189+ Button ( action: { diskMonitor. scan ( ) } ) {
190+ Image ( systemName: " arrow.clockwise " )
191+ . font ( . system( size: 12 ) )
192+ }
193+ . buttonStyle ( . plain)
194+ . help ( " Refresh " )
195+ }
196+ . padding ( . horizontal, 12 )
197+ . padding ( . vertical, 8 )
198+
168199 Divider ( )
169- }
170-
171- // Cleanable summary card (only when there's stuff to clean)
172- let artifactTotal = diskMonitor. projectArtifacts. reduce ( Int64 ( 0 ) ) { $0 + $1. size }
173- if diskMonitor. safeCleanable > 10_485_760 || diskMonitor. riskyCleanable > 10_485_760 || artifactTotal > 10_485_760 {
174- cleanableSummary
200+
201+ ScrollView {
202+ switch selectedTab {
203+ case . overview:
204+ overviewContent
205+ case . developer:
206+ developerContent
207+ case . projects:
208+ projectsContent
209+ case . largeFiles:
210+ largeFilesContent
211+ }
212+ }
213+ . frame ( maxHeight: . infinity)
214+
175215 Divider ( )
216+
217+ footerView
176218 }
177-
178- // Tab bar
179- tabBar
180-
181- Divider ( )
182-
183- // Content
184- ScrollView {
185- switch selectedTab {
186- case . overview:
187- overviewContent
188- case . developer:
189- developerContent
190- case . projects:
191- projectsContent
192- case . largeFiles:
193- largeFilesContent
219+ . transition ( . asymmetric(
220+ insertion: . move( edge: . bottom) . combined ( with: . opacity) ,
221+ removal: . move( edge: . bottom) . combined ( with: . opacity)
222+ ) )
223+ } else {
224+ // Normal view
225+ VStack ( spacing: 0 ) {
226+ // Header
227+ headerView
228+
229+ Divider ( )
230+
231+ // Permission warnings (if any)
232+ if diskMonitor. notificationPermission == . denied {
233+ permissionBanner
234+ Divider ( )
235+ }
236+
237+ // Cleanable summary card (only when there's stuff to clean)
238+ let artifactTotal = diskMonitor. projectArtifacts. reduce ( Int64 ( 0 ) ) { $0 + $1. size }
239+ if diskMonitor. safeCleanable > 10_485_760 || diskMonitor. riskyCleanable > 10_485_760 || artifactTotal > 10_485_760 {
240+ cleanableSummary
241+ Divider ( )
194242 }
243+
244+ // Tab bar with expand button
245+ HStack ( spacing: 0 ) {
246+ tabBar
247+
248+ Button ( action: { withAnimation ( . spring( response: 0.4 , dampingFraction: 0.85 ) ) { isExpanded = true } } ) {
249+ Image ( systemName: " arrow.up.left.and.arrow.down.right " )
250+ . font ( . system( size: 11 ) )
251+ . foregroundColor ( . secondary)
252+ . frame ( width: 32 , height: 28 )
253+ . contentShape ( Rectangle ( ) )
254+ }
255+ . buttonStyle ( . plain)
256+ }
257+
258+ Divider ( )
259+
260+ // Content
261+ ScrollView {
262+ switch selectedTab {
263+ case . overview:
264+ overviewContent
265+ case . developer:
266+ developerContent
267+ case . projects:
268+ projectsContent
269+ case . largeFiles:
270+ largeFilesContent
271+ }
272+ }
273+ . frame ( maxHeight: . infinity)
274+
275+ Divider ( )
276+
277+ // Footer
278+ footerView
195279 }
196- . frame ( maxHeight: . infinity)
197-
198- Divider ( )
199-
200- // Footer
201- footerView
280+ . transition ( . asymmetric(
281+ insertion: . move( edge: . top) . combined ( with: . opacity) ,
282+ removal: . move( edge: . top) . combined ( with: . opacity)
283+ ) )
202284 }
203285
204286 // Onboarding overlay (first launch)
0 commit comments