// // ContentView.swift // oAI // // Root navigation container // import SwiftUI struct ContentView: View { @Environment(ChatViewModel.self) var chatViewModel var body: some View { @Bindable var vm = chatViewModel NavigationStack { ChatView( onModelSelect: { chatViewModel.showModelSelector = true }, onProviderChange: { newProvider in chatViewModel.changeProvider(newProvider) } ) .navigationTitle("") .toolbar { #if os(macOS) macOSToolbar #endif } } .frame(minWidth: 640, minHeight: 400) #if os(macOS) .onKeyPress(.return, phases: .down) { press in if press.modifiers.contains(.command) { chatViewModel.sendMessage() return .handled } return .ignored } #endif .sheet(isPresented: $vm.showModelSelector) { ModelSelectorView( models: chatViewModel.availableModels, selectedModel: chatViewModel.selectedModel, onSelect: { model in let oldModel = chatViewModel.selectedModel chatViewModel.selectedModel = model SettingsService.shared.defaultModel = model.id chatViewModel.showModelSelector = false // Trigger auto-save on model switch Task { await chatViewModel.onModelSwitch(from: oldModel, to: model) } } ) .task { if chatViewModel.availableModels.count <= 10 { await chatViewModel.loadAvailableModels() } } } .sheet(isPresented: $vm.showSettings, onDismiss: { chatViewModel.syncFromSettings() }) { SettingsView(chatViewModel: chatViewModel) } .sheet(isPresented: $vm.showStats) { StatsView( stats: chatViewModel.sessionStats, model: chatViewModel.selectedModel, provider: chatViewModel.currentProvider ) } .sheet(isPresented: $vm.showHelp) { HelpView() } .sheet(isPresented: $vm.showCredits) { CreditsView(provider: chatViewModel.currentProvider) } .sheet(isPresented: $vm.showConversations) { ConversationListView(onLoad: { conversation in chatViewModel.loadConversation(conversation) }) } .sheet(item: $vm.modelInfoTarget) { model in ModelInfoView(model: model) } .sheet(isPresented: $vm.showHistory) { HistoryView(onSelect: { input in chatViewModel.inputText = input }) } } #if os(macOS) @ToolbarContentBuilder private var macOSToolbar: some ToolbarContent { let settings = SettingsService.shared let showLabels = settings.showToolbarLabels let scale = iconScale(for: settings.toolbarIconSize) ToolbarItemGroup(placement: .automatic) { // New conversation Button(action: { chatViewModel.newConversation() }) { ToolbarLabel(title: "New Chat", systemImage: "square.and.pencil", showLabels: showLabels, scale: scale) } .keyboardShortcut("n", modifiers: .command) .help("New conversation") Button(action: { chatViewModel.showConversations = true }) { ToolbarLabel(title: "Conversations", systemImage: "clock.arrow.circlepath", showLabels: showLabels, scale: scale) } .keyboardShortcut("l", modifiers: .command) .help("Saved conversations (Cmd+L)") Button(action: { chatViewModel.showHistory = true }) { ToolbarLabel(title: "History", systemImage: "list.bullet", showLabels: showLabels, scale: scale) } .keyboardShortcut("h", modifiers: .command) .help("Command history (Cmd+H)") Spacer() Button(action: { chatViewModel.showModelSelector = true }) { ToolbarLabel(title: "Model", systemImage: "cpu", showLabels: showLabels, scale: scale) } .keyboardShortcut("m", modifiers: .command) .help("Select AI model (Cmd+M)") Button(action: { if let model = chatViewModel.selectedModel { chatViewModel.modelInfoTarget = model } }) { ToolbarLabel(title: "Info", systemImage: "info.circle", showLabels: showLabels, scale: scale) } .keyboardShortcut("i", modifiers: .command) .help("Model info (Cmd+I)") .disabled(chatViewModel.selectedModel == nil) Button(action: { chatViewModel.showStats = true }) { ToolbarLabel(title: "Stats", systemImage: "chart.bar", showLabels: showLabels, scale: scale) } .keyboardShortcut("s", modifiers: .command) .help("Session statistics (Cmd+S)") Button(action: { chatViewModel.showCredits = true }) { ToolbarLabel(title: "Credits", systemImage: "creditcard", showLabels: showLabels, scale: scale) } .help("Check API credits") Spacer() Button(action: { chatViewModel.showSettings = true }) { ToolbarLabel(title: "Settings", systemImage: "gearshape", showLabels: showLabels, scale: scale) } .keyboardShortcut(",", modifiers: .command) .help("Settings (Cmd+,)") Button(action: { chatViewModel.showHelp = true }) { ToolbarLabel(title: "Help", systemImage: "questionmark.circle", showLabels: showLabels, scale: scale) } .keyboardShortcut("/", modifiers: .command) .help("Help & commands (Cmd+/)") } } #endif // Helper function to convert icon size to imageScale private func iconScale(for size: Double) -> Image.Scale { switch size { case ...18: return .small case 19...24: return .medium default: return .large } } } // Helper view for toolbar labels struct ToolbarLabel: View { let title: String let systemImage: String let showLabels: Bool let scale: Image.Scale var body: some View { if showLabels { Label(title, systemImage: systemImage) .labelStyle(.titleAndIcon) .imageScale(scale) } else { Label(title, systemImage: systemImage) .labelStyle(.iconOnly) .imageScale(scale) } } } #Preview { ContentView() .environment(ChatViewModel()) }