// // ZyquoAgentApp.swift // Zyquo Agent // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // // The SwiftUI app shell: builds the shared environment (task store, run // hub, model catalog, key vault, appearance, agent settings, templates, // personas, UI signals), presents the command-center window (1320×860 // default, 1040×680 min), the Settings scene (760×560, 7 tabs), the // toggleable menu bar extra with running-task status, the ⌥Space Quick Task // panel, and the full Phase 6 command set (⌘N, ⌘K, ⌘⇧A, ⌘⇧E, ⌥Space; // ⌘⏎/⌘./⌘F live in the detail/sidebar views). // import SwiftUI /// Shared object graph for the app session. @MainActor final class AppEnvironment: ObservableObject { let tasks: TaskStore let hub: RunHub let catalog: ModelCatalog let vault: KeyVaultStore let appearance: AppearanceStore let settings: AgentSettingsStore let templates: TemplateStore let personas: PersonaStore let uiState: AppUIState init() { let tasks = TaskStore() let settings = AgentSettingsStore() self.tasks = tasks self.settings = settings self.hub = RunHub(store: tasks, settings: settings) self.catalog = ModelCatalog() self.vault = KeyVaultStore() self.appearance = AppearanceStore() self.templates = TemplateStore() self.personas = PersonaStore() self.uiState = AppUIState() } /// The model new tasks start with (persisted default, catalog fallback). var newTaskModel: AIModel? { settings.defaultAgentModel(in: catalog) } } struct ZyquoAgentApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate @StateObject private var environment = AppEnvironment() @State private var quickTask: QuickTaskController? @AppStorage("menuBarExtraEnabled") private var menuBarExtraEnabled = true var body: some Scene { WindowGroup("Zyquo Agent") { MainWindowView() .modifier(EnvironmentInjector(environment: environment)) .frame( minWidth: ZyquoMetrics.windowMinWidth, minHeight: ZyquoMetrics.windowMinHeight ) .onAppear { if quickTask == nil { quickTask = QuickTaskController(environment: environment) } } } .defaultSize( width: ZyquoMetrics.windowDefaultWidth, height: ZyquoMetrics.windowDefaultHeight ) .commands { AppCommands(environment: environment, quickTask: { quickTask }) } Settings { SettingsView() .modifier(EnvironmentInjector(environment: environment)) } MenuBarExtra(isInserted: $menuBarExtraEnabled) { MenuBarContent(store: environment.tasks, environment: environment, quickTask: { quickTask }) } label: { MenuBarIconView() } } } /// Injects the full shared object graph (all scenes get the same set). private struct EnvironmentInjector: ViewModifier { let environment: AppEnvironment func body(content: Content) -> some View { content .environmentObject(environment.tasks) .environmentObject(environment.hub) .environmentObject(environment.catalog) .environmentObject(environment.vault) .environmentObject(environment.appearance) .environmentObject(environment.settings) .environmentObject(environment.templates) .environmentObject(environment.personas) .environmentObject(environment.uiState) } } // MARK: - Commands /// App-level menu commands and shortcuts (⌘N, ⌘K, ⌘⇧A, ⌘⇧E, Quick Task). struct AppCommands: Commands { let environment: AppEnvironment var quickTask: () -> QuickTaskController? var body: some Commands { CommandGroup(replacing: .newItem) { Button("New Task") { environment.tasks.newTask( model: environment.newTaskModel, safetyMode: environment.settings.settings.defaultSafetyMode ) } .keyboardShortcut("n", modifiers: .command) } CommandMenu("Task") { Button("Command Palette") { environment.uiState.showCommandPalette.toggle() } .keyboardShortcut("k", modifiers: .command) Button("Quick Task") { quickTask()?.show() } Button("Browse Templates…") { environment.uiState.showTemplateBrowser = true } Divider() Button("Open Audit Log") { environment.uiState.requestAuditLog() } .keyboardShortcut("a", modifiers: [.command, .shift]) Divider() Button("Export Transcript as Markdown…") { if let id = environment.tasks.selectedID, let task = environment.tasks.task(id: id) { TaskTranscriptExporter.presentSavePanel(for: task, format: .markdown) } } .keyboardShortcut("e", modifiers: [.command, .shift]) .disabled(environment.tasks.selectedID == nil) Button("Export Transcript as PDF…") { if let id = environment.tasks.selectedID, let task = environment.tasks.task(id: id) { TaskTranscriptExporter.presentSavePanel(for: task, format: .pdf) } } .disabled(environment.tasks.selectedID == nil) } } } // MARK: - Menu bar extra /// Menu bar glyph: the shipped template PNG when bundled, SF Symbol fallback /// during `swift run` (no bundle resources). struct MenuBarIconView: View { var body: some View { if let image = Self.templateImage() { Image(nsImage: image) } else { Image(systemName: "bolt.circle") } } private static func templateImage() -> NSImage? { guard let path = Bundle.main.path(forResource: "MenuBarIcon", ofType: "png"), let image = NSImage(contentsOfFile: path) else { return nil } image.isTemplate = true image.size = NSSize(width: 18, height: 18) return image } } /// Menu content: running-task status, New Task, Quick Task, open, quit. struct MenuBarContent: View { @ObservedObject var store: TaskStore let environment: AppEnvironment var quickTask: () -> QuickTaskController? var body: some View { let running = store.tasks.filter { $0.status.isActive } if running.isEmpty { Text("No running tasks") } else { Text("\(running.count) running task\(running.count == 1 ? "" : "s")") ForEach(running) { task in Button("\(task.title) — \(task.status.displayName)") { store.selectedID = task.id NSApp.activate(ignoringOtherApps: true) } } } Divider() Button("New Task") { store.newTask( model: environment.newTaskModel, safetyMode: environment.settings.settings.defaultSafetyMode ) NSApp.activate(ignoringOtherApps: true) } Button("Quick Task ⌥Space") { quickTask()?.show() } Divider() Button("Open Zyquo Agent") { NSApp.activate(ignoringOtherApps: true) } Button("Quit") { NSApp.terminate(nil) } } } final class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { // Ensure the app fronts correctly when launched outside Finder // (e.g. `swift run` or `open` from a terminal during development). NSApp.setActivationPolicy(.regular) NSApp.activate(ignoringOtherApps: true) } }