SPB Git

spb/zyquo-cloud Public MIT

Native macOS AI chat client for 12 cloud providers — your keys, every cloud model, one beautiful chat.

Swift 97.4% Shell 1.7% Makefile 1%
4.2 KB · 128 lines swift
Raw Blame History
1//2//  ZyquoCloudApp.swift3//  Zyquo Cloud4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//89import SwiftUI1011struct ZyquoCloudApp: App {12    @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate13    @StateObject private var environment = AppEnvironment()14    @State private var quickChat: QuickChatController?15    @AppStorage("menuBarExtraEnabled") private var menuBarExtraEnabled = true1617    var body: some Scene {18        WindowGroup("Zyquo Cloud") {19            MainWindowView()20                .environmentObject(environment.conversations)21                .environmentObject(environment.catalog)22                .environmentObject(environment.vault)23                .environmentObject(environment.appearance)24                .onAppear {25                    if quickChat == nil {26                        quickChat = QuickChatController(environment: environment)27                    }28                }29        }30        .defaultSize(31            width: ZyquoMetrics.windowDefaultWidth,32            height: ZyquoMetrics.windowDefaultHeight33        )34        .commands {35            AppCommands(environment: environment, quickChat: { quickChat })36        }3738        Settings {39            SettingsView()40                .environmentObject(environment.conversations)41                .environmentObject(environment.catalog)42                .environmentObject(environment.vault)43                .environmentObject(environment.appearance)44        }4546        MenuBarExtra(isInserted: $menuBarExtraEnabled) {47            Button("New Chat") {48                _ = environment.conversations.newConversation()49                NSApp.activate(ignoringOtherApps: true)50            }51            Button("Quick Chat  ⌥Space") {52                quickChat?.show()53            }54            Divider()55            Button("Open Zyquo Cloud") {56                NSApp.activate(ignoringOtherApps: true)57            }58            Button("Quit") {59                NSApp.terminate(nil)60            }61        } label: {62            MenuBarIconView()63        }64    }65}6667/// Menu bar glyph: the shipped template PNG when bundled, SF Symbol fallback68/// during `swift run` (no bundle resources).69struct MenuBarIconView: View {70    var body: some View {71        if let image = Self.templateImage() {72            Image(nsImage: image)73        } else {74            Image(systemName: "cloud.fill")75        }76    }7778    private static func templateImage() -> NSImage? {79        guard let path = Bundle.main.path(forResource: "MenuBarIcon", ofType: "png"),80              let image = NSImage(contentsOfFile: path) else { return nil }81        image.isTemplate = true82        image.size = NSSize(width: 18, height: 18)83        return image84    }85}8687final class AppDelegate: NSObject, NSApplicationDelegate {88    func applicationDidFinishLaunching(_ notification: Notification) {89        // Ensure the app fronts correctly when launched outside Finder90        // (e.g. `swift run` or `open` from a terminal during development).91        NSApplication.shared.setActivationPolicy(.regular)92        NSApplication.shared.activate(ignoringOtherApps: true)93    }94}9596/// App-level menu commands and shortcuts.97struct AppCommands: Commands {98    let environment: AppEnvironment99    var quickChat: () -> QuickChatController?100101    var body: some Commands {102        CommandGroup(replacing: .newItem) {103            Button("New Chat") {104                _ = environment.conversations.newConversation()105            }106            .keyboardShortcut("n", modifiers: .command)107        }108        CommandMenu("Chat") {109            Button("Quick Chat") {110                quickChat()?.show()111            }112            Button("Stop Generating") {113                if let id = environment.conversations.selectedID {114                    environment.conversations.stopStreaming(id)115                }116            }117            .keyboardShortcut(".", modifiers: .command)118            Divider()119            Button("Export Conversation…") {120                if let conversation = environment.conversations.selected {121                    ConversationExporter.presentSavePanel(for: conversation)122                }123            }124            .keyboardShortcut("e", modifiers: [.command, .shift])125        }126    }127}128