| 
									
										
										
										
											2025-06-20 17:39:06 +08:00
										 |  |  | // | 
					
						
							|  |  |  | //  MNNLLMiOSApp.swift | 
					
						
							|  |  |  | //  MainTabView | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //  Created by 游薪渝(揽清) on 2025/06/20. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import SwiftUI | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct MainTabView: View { | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |     // MARK: - State Properties | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-23 16:48:23 +08:00
										 |  |  |     @State private var showHistory = false | 
					
						
							|  |  |  |     @State private var selectedHistory: ChatHistory? = nil | 
					
						
							|  |  |  |     @State private var histories: [ChatHistory] = ChatHistoryManager.shared.getAllHistory() | 
					
						
							|  |  |  |     @State private var showHistoryButton = true | 
					
						
							| 
									
										
										
										
											2025-06-24 17:11:07 +08:00
										 |  |  |     @State private var showSettings = false | 
					
						
							|  |  |  |     @State private var showWebView = false | 
					
						
							|  |  |  |     @State private var webViewURL: URL? | 
					
						
							| 
									
										
										
										
											2025-07-09 20:31:39 +08:00
										 |  |  |     @State private var navigateToSettings = false | 
					
						
							| 
									
										
										
										
											2025-07-10 11:16:25 +08:00
										 |  |  |     @StateObject private var modelListViewModel = ModelListViewModel() | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |     @State private var selectedTab: Int = 0 | 
					
						
							| 
									
										
										
										
											2025-07-21 16:06:09 +08:00
										 |  |  |      | 
					
						
							|  |  |  |     private var titles: [String] { | 
					
						
							|  |  |  |         [ | 
					
						
							|  |  |  |             NSLocalizedString("Local Model", comment: "本地模型标签"), | 
					
						
							|  |  |  |             NSLocalizedString("Model Market", comment: "模型市场标签"), | 
					
						
							|  |  |  |             NSLocalizedString("Benchmark", comment: "基准测试标签") | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-23 16:48:23 +08:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |     // MARK: - Body | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-20 17:39:06 +08:00
										 |  |  |     var body: some View { | 
					
						
							| 
									
										
										
										
											2025-06-25 17:12:04 +08:00
										 |  |  |         ZStack { | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |             // Main TabView for navigation between Local Model, Model Market, and Benchmark | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |             TabView(selection: $selectedTab) { | 
					
						
							|  |  |  |                 NavigationView { | 
					
						
							| 
									
										
										
										
											2025-07-10 11:16:25 +08:00
										 |  |  |                     LocalModelListView(viewModel: modelListViewModel) | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                         .navigationTitle(titles[0]) | 
					
						
							|  |  |  |                         .navigationBarTitleDisplayMode(.inline) | 
					
						
							|  |  |  |                         .navigationBarHidden(false) | 
					
						
							|  |  |  |                         .onAppear { | 
					
						
							|  |  |  |                             setupNavigationBarAppearance() | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |                         } | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                         .toolbar { | 
					
						
							|  |  |  |                             CommonToolbarView( | 
					
						
							|  |  |  |                                 showHistory: $showHistory, | 
					
						
							|  |  |  |                                 showHistoryButton: $showHistoryButton, | 
					
						
							|  |  |  |                             ) | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         .background( | 
					
						
							|  |  |  |                             ZStack { | 
					
						
							|  |  |  |                                 NavigationLink(destination: chatDestination, isActive: chatIsActiveBinding) { EmptyView() } | 
					
						
							|  |  |  |                                 NavigationLink(destination: SettingsView(), isActive: $navigateToSettings) { EmptyView() } | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |                         // Hide TabBar when entering chat or settings view | 
					
						
							|  |  |  |                         .toolbar((chatIsActiveBinding.wrappedValue || navigateToSettings) ? .hidden : .visible, for: .tabBar) | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                 } | 
					
						
							|  |  |  |                 .tabItem { | 
					
						
							|  |  |  |                     Image(systemName: "house.fill") | 
					
						
							|  |  |  |                     Text(titles[0]) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 .tag(0) | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 NavigationView { | 
					
						
							| 
									
										
										
										
											2025-07-10 11:16:25 +08:00
										 |  |  |                     ModelListView(viewModel: modelListViewModel) | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                         .navigationTitle(titles[1]) | 
					
						
							|  |  |  |                         .navigationBarTitleDisplayMode(.inline) | 
					
						
							|  |  |  |                         .navigationBarHidden(false) | 
					
						
							|  |  |  |                         .onAppear { | 
					
						
							|  |  |  |                             setupNavigationBarAppearance() | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |                         } | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                         .toolbar { | 
					
						
							|  |  |  |                             CommonToolbarView( | 
					
						
							|  |  |  |                                 showHistory: $showHistory, | 
					
						
							|  |  |  |                                 showHistoryButton: $showHistoryButton, | 
					
						
							|  |  |  |                             ) | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |                         } | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                         .background( | 
					
						
							|  |  |  |                             ZStack { | 
					
						
							|  |  |  |                                 NavigationLink(destination: chatDestination, isActive: chatIsActiveBinding) { EmptyView() } | 
					
						
							|  |  |  |                                 NavigationLink(destination: SettingsView(), isActive: $navigateToSettings) { EmptyView() } | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                 .tabItem { | 
					
						
							|  |  |  |                     Image(systemName: "doc.text.fill") | 
					
						
							|  |  |  |                     Text(titles[1]) | 
					
						
							| 
									
										
										
										
											2025-06-30 16:38:31 +08:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                 .tag(1) | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 NavigationView { | 
					
						
							|  |  |  |                     BenchmarkView() | 
					
						
							|  |  |  |                         .navigationTitle(titles[2]) | 
					
						
							|  |  |  |                         .navigationBarTitleDisplayMode(.inline) | 
					
						
							|  |  |  |                         .navigationBarHidden(false) | 
					
						
							|  |  |  |                         .onAppear { | 
					
						
							|  |  |  |                             setupNavigationBarAppearance() | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |                         } | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                         .toolbar { | 
					
						
							|  |  |  |                             CommonToolbarView( | 
					
						
							|  |  |  |                                 showHistory: $showHistory, | 
					
						
							|  |  |  |                                 showHistoryButton: $showHistoryButton, | 
					
						
							|  |  |  |                             ) | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |                         } | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                         .background( | 
					
						
							|  |  |  |                             ZStack { | 
					
						
							|  |  |  |                                 NavigationLink(destination: chatDestination, isActive: chatIsActiveBinding) { EmptyView() } | 
					
						
							|  |  |  |                                 NavigationLink(destination: SettingsView(), isActive: $navigateToSettings) { EmptyView() } | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                 .tabItem { | 
					
						
							|  |  |  |                     Image(systemName: "clock.fill") | 
					
						
							|  |  |  |                     Text(titles[2]) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 .tag(2) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             .onAppear { | 
					
						
							|  |  |  |                 setupTabBarAppearance() | 
					
						
							| 
									
										
										
										
											2025-06-23 16:48:23 +08:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-07-01 17:14:40 +08:00
										 |  |  |             .tint(.black) | 
					
						
							| 
									
										
										
										
											2025-06-25 17:12:04 +08:00
										 |  |  |              | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |             // Overlay for dimming the background when history is shown | 
					
						
							| 
									
										
										
										
											2025-06-25 17:12:04 +08:00
										 |  |  |             if showHistory { | 
					
						
							|  |  |  |                 Color.black.opacity(0.5) | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                 .edgesIgnoringSafeArea(.all) | 
					
						
							|  |  |  |                 .onTapGesture { | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |                     withAnimation(.easeInOut(duration: 0.2)) { | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                         showHistory = false | 
					
						
							| 
									
										
										
										
											2025-06-23 16:48:23 +08:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2025-07-18 17:18:45 +08:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-06-23 16:48:23 +08:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-06-25 17:12:04 +08:00
										 |  |  |              | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |             // Side menu for displaying chat history | 
					
						
							| 
									
										
										
										
											2025-07-01 17:14:40 +08:00
										 |  |  |             SideMenuView(isOpen: $showHistory,  | 
					
						
							|  |  |  |                         selectedHistory: $selectedHistory,  | 
					
						
							|  |  |  |                         histories: $histories, | 
					
						
							|  |  |  |                         navigateToMainSettings: $navigateToSettings) | 
					
						
							|  |  |  |                         .edgesIgnoringSafeArea(.all) | 
					
						
							| 
									
										
										
										
											2025-06-25 17:12:04 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-07-01 17:14:40 +08:00
										 |  |  |         .onChange(of: showHistory) { oldValue, newValue in | 
					
						
							| 
									
										
										
										
											2025-08-05 15:50:52 +08:00
										 |  |  |             if newValue { | 
					
						
							|  |  |  |                 // Refresh chat history when opening the side menu | 
					
						
							|  |  |  |                 histories = ChatHistoryManager.shared.getAllHistory() | 
					
						
							|  |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2025-06-25 17:12:04 +08:00
										 |  |  |                 DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { | 
					
						
							|  |  |  |                     withAnimation { | 
					
						
							|  |  |  |                         showHistoryButton = true | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2025-06-20 17:39:06 +08:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-06-23 16:48:23 +08:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-06-25 17:12:04 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |         .sheet(isPresented: $showWebView) { | 
					
						
							|  |  |  |             if let url = webViewURL { | 
					
						
							|  |  |  |                 WebView(url: url) | 
					
						
							| 
									
										
										
										
											2025-06-24 17:11:07 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |     // MARK: - View Builders | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     /// Destination view for chat, either from a new model or a history item. | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |     @ViewBuilder | 
					
						
							|  |  |  |     private var chatDestination: some View { | 
					
						
							| 
									
										
										
										
											2025-07-10 11:16:25 +08:00
										 |  |  |         if let model = modelListViewModel.selectedModel { | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |             LLMChatView(modelInfo: model) | 
					
						
							| 
									
										
										
										
											2025-07-09 20:31:39 +08:00
										 |  |  |                 .navigationBarHidden(false) | 
					
						
							|  |  |  |                 .navigationBarTitleDisplayMode(.inline) | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |         } else if let history = selectedHistory { | 
					
						
							| 
									
										
										
										
											2025-07-10 11:16:25 +08:00
										 |  |  |             let modelInfo = ModelInfo(modelId: history.modelId, isDownloaded: true) | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |             LLMChatView(modelInfo: modelInfo, history: history) | 
					
						
							| 
									
										
										
										
											2025-07-09 20:31:39 +08:00
										 |  |  |                 .navigationBarHidden(false) | 
					
						
							|  |  |  |                 .navigationBarTitleDisplayMode(.inline) | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             EmptyView() | 
					
						
							| 
									
										
										
										
											2025-06-24 17:11:07 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-06-20 17:39:06 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |     // MARK: - Bindings | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     /// Binding to control the activation of the chat view. | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |     private var chatIsActiveBinding: Binding<Bool> { | 
					
						
							|  |  |  |         Binding<Bool>( | 
					
						
							| 
									
										
										
										
											2025-07-09 20:31:39 +08:00
										 |  |  |             get: {  | 
					
						
							| 
									
										
										
										
											2025-07-10 11:16:25 +08:00
										 |  |  |                 return modelListViewModel.selectedModel != nil || selectedHistory != nil | 
					
						
							| 
									
										
										
										
											2025-07-09 20:31:39 +08:00
										 |  |  |             }, | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |             set: { isActive in | 
					
						
							|  |  |  |                 if !isActive { | 
					
						
							| 
									
										
										
										
											2025-07-09 20:31:39 +08:00
										 |  |  |                     // Record usage when returning from chat | 
					
						
							| 
									
										
										
										
											2025-07-10 11:16:25 +08:00
										 |  |  |                     if let model = modelListViewModel.selectedModel { | 
					
						
							|  |  |  |                         modelListViewModel.recordModelUsage(modelName: model.modelName) | 
					
						
							| 
									
										
										
										
											2025-06-26 16:06:25 +08:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2025-07-09 20:31:39 +08:00
										 |  |  |                      | 
					
						
							| 
									
										
										
										
											2025-08-05 15:50:52 +08:00
										 |  |  |                     // Refresh chat history when returning from chat | 
					
						
							|  |  |  |                     histories = ChatHistoryManager.shared.getAllHistory() | 
					
						
							|  |  |  |                      | 
					
						
							| 
									
										
										
										
											2025-07-09 20:31:39 +08:00
										 |  |  |                     // Clear selections | 
					
						
							| 
									
										
										
										
											2025-07-10 11:16:25 +08:00
										 |  |  |                     modelListViewModel.selectedModel = nil | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |                     selectedHistory = nil | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |     // MARK: - Private Methods | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     /// Configures the appearance of the navigation bar. | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |     private func setupNavigationBarAppearance() { | 
					
						
							|  |  |  |         let appearance = UINavigationBarAppearance() | 
					
						
							|  |  |  |         appearance.configureWithOpaqueBackground() | 
					
						
							|  |  |  |         appearance.backgroundColor = .white | 
					
						
							| 
									
										
										
										
											2025-07-01 17:14:40 +08:00
										 |  |  |         appearance.shadowColor = .clear | 
					
						
							| 
									
										
										
										
											2025-06-24 17:42:40 +08:00
										 |  |  |          | 
					
						
							|  |  |  |         UINavigationBar.appearance().standardAppearance = appearance | 
					
						
							|  |  |  |         UINavigationBar.appearance().compactAppearance = appearance | 
					
						
							|  |  |  |         UINavigationBar.appearance().scrollEdgeAppearance = appearance | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-30 16:38:31 +08:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-07-18 17:38:52 +08:00
										 |  |  |     /// Configures the appearance of the tab bar. | 
					
						
							| 
									
										
										
										
											2025-06-30 16:38:31 +08:00
										 |  |  |     private func setupTabBarAppearance() { | 
					
						
							|  |  |  |         let appearance = UITabBarAppearance() | 
					
						
							|  |  |  |         appearance.configureWithOpaqueBackground() | 
					
						
							| 
									
										
										
										
											2025-07-01 11:01:49 +08:00
										 |  |  |          | 
					
						
							|  |  |  |         let selectedColor = UIColor(Color.primaryPurple) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         appearance.stackedLayoutAppearance.selected.iconColor = selectedColor | 
					
						
							|  |  |  |         appearance.stackedLayoutAppearance.selected.titleTextAttributes = [.foregroundColor: selectedColor] | 
					
						
							| 
									
										
										
										
											2025-06-30 16:38:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         UITabBar.appearance().standardAppearance = appearance | 
					
						
							|  |  |  |         UITabBar.appearance().scrollEdgeAppearance = appearance | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-07-10 11:16:25 +08:00
										 |  |  | } |