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%
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