SPB Git

spb/zyquo-agent Public MIT

The autonomous agent that actually operates your Mac — plans, runs real commands, verifies its own work.

Swift 94.7% Shell 4.1% Python 0.7% Makefile 0.5%
6.6 KB · 168 lines swift
Raw Blame History
1//2//  MainWindowView.swift3//  Zyquo Agent4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//8//  The command-center root: translucent 260pt sidebar (NavigationSplitView's9//  native sidebar material) + the task detail. With no task selected, the10//  "What should I do on your Mac?" hero creates one. Hosts the ⌘K command11//  palette overlay and the template browser sheet; the root re-identifies on12//  accent change so every ZyquoColor.accent token redraws immediately.13//1415import SwiftUI1617struct MainWindowView: View {18    @EnvironmentObject private var store: TaskStore19    @EnvironmentObject private var catalog: ModelCatalog20    @EnvironmentObject private var appearance: AppearanceStore21    @EnvironmentObject private var settings: AgentSettingsStore22    @EnvironmentObject private var templates: TemplateStore23    @EnvironmentObject private var personas: PersonaStore24    @EnvironmentObject private var uiState: AppUIState2526    var body: some View {27        ZStack(alignment: .top) {28            NavigationSplitView {29                SidebarView()30                    .navigationSplitViewColumnWidth(31                        min: ZyquoMetrics.sidebarWidth,32                        ideal: ZyquoMetrics.sidebarWidth,33                        max: 36034                    )35            } detail: {36                if let id = store.selectedID, store.task(id: id) != nil {37                    TaskDetailView(taskID: id)38                } else {39                    AgentEmptyStateView(40                        model: defaultModel,41                        safetyMode: settings.settings.defaultSafetyMode,42                        personaName: nil,43                        onSelectModel: { model in44                            store.newTask(model: model, safetyMode: settings.settings.defaultSafetyMode)45                        },46                        onSelectSafetyMode: { mode in47                            store.newTask(model: defaultModel, safetyMode: mode)48                        },49                        onSelectPersona: { persona in50                            newTask(with: persona)51                        },52                        onBrowseTemplates: {53                            uiState.showTemplateBrowser = true54                        },55                        onSuggestion: { prompt in56                            store.pendingDraft = prompt57                            store.newTask(model: defaultModel, safetyMode: settings.settings.defaultSafetyMode)58                        }59                    )60                }61            }62            if uiState.showCommandPalette {63                paletteOverlay64            }65        }66        .frame(67            minWidth: ZyquoMetrics.windowMinWidth,68            minHeight: ZyquoMetrics.windowMinHeight69        )70        .sheet(isPresented: $uiState.showTemplateBrowser) {71            TemplateBrowserView { prompt, safetyMode in72                store.pendingDraft = prompt73                store.newTask(model: defaultModel, safetyMode: safetyMode)74            }75        }76        .preferredColorScheme(appearance.themeMode.colorScheme)77        .tint(appearance.accentColor)78        .id(appearance.accent)79    }8081    /// New tasks start with the persisted default agent model.82    private var defaultModel: AIModel? {83        settings.defaultAgentModel(in: catalog)84    }8586    private func newTask(with persona: Persona?) {87        let model: AIModel?88        if let persona, let id = persona.modelID, let provider = persona.provider,89           let preferred = catalog.model(id: id, provider: provider) {90            model = preferred91        } else {92            model = defaultModel93        }94        store.newTask(95            model: model,96            safetyMode: persona?.safetyMode ?? settings.settings.defaultSafetyMode,97            personaID: persona?.id98        )99    }100101    // MARK: - ⌘K palette102103    private var paletteOverlay: some View {104        ZStack(alignment: .top) {105            // Click-away scrim (transparent, keeps the window visible).106            Color.black.opacity(0.001)107                .onTapGesture { uiState.showCommandPalette = false }108            CommandPaletteView(items: paletteItems) {109                uiState.showCommandPalette = false110            }111            .padding(.top, ZyquoSpacing.xxl * 2)112        }113        .ignoresSafeArea()114        .transition(.opacity)115    }116117    private var paletteItems: [PaletteItem] {118        var items: [PaletteItem] = [119            PaletteItem(id: "action-new-task", kind: .action, title: "New Task", subtitle: "⌘N", symbolName: "plus.circle") {120                store.newTask(model: defaultModel, safetyMode: settings.settings.defaultSafetyMode)121            },122            PaletteItem(id: "action-settings", kind: .action, title: "Open Settings", subtitle: "⌘,", symbolName: "gearshape") {123                SettingsOpener.open()124            },125            PaletteItem(id: "action-templates", kind: .action, title: "Browse Templates", subtitle: nil, symbolName: "square.grid.2x2") {126                uiState.showTemplateBrowser = true127            },128            PaletteItem(id: "action-audit", kind: .action, title: "Open Audit Log", subtitle: "⌘⇧A", symbolName: "list.bullet.rectangle") {129                uiState.requestAuditLog()130            },131            PaletteItem(id: "action-drawer", kind: .action, title: "Toggle Activity Drawer", subtitle: nil, symbolName: "terminal") {132                uiState.requestDrawerToggle()133            },134            PaletteItem(id: "action-plan", kind: .action, title: "Toggle Plan Panel", subtitle: nil, symbolName: "sidebar.right") {135                uiState.requestPlanToggle()136            },137        ]138        for template in templates.all {139            items.append(PaletteItem(140                id: "template-\(template.id.uuidString)",141                kind: .template,142                title: template.title,143                subtitle: template.category.displayName,144                symbolName: template.displaySymbol145            ) {146                if template.variables.isEmpty {147                    store.pendingDraft = template.prompt148                    store.newTask(model: defaultModel, safetyMode: template.suggestedSafetyMode)149                } else {150                    uiState.showTemplateBrowser = true151                }152            })153        }154        for task in store.tasks.prefix(60) {155            items.append(PaletteItem(156                id: "task-\(task.id.uuidString)",157                kind: .task,158                title: task.title,159                subtitle: task.status.displayName,160                symbolName: "checklist"161            ) {162                store.selectedID = task.id163            })164        }165        return items166    }167}168