// // AppModel.swift // Zyquo MLX // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // import Foundation import Observation import SwiftUI /// Workbench sections (left navigator, charter §4.2). enum WorkbenchSection: String, CaseIterable, Identifiable { case models case datasets case train case convert case playground case evaluate var id: String { rawValue } var title: String { switch self { case .models: "Models" case .datasets: "Datasets" case .train: "Train" case .convert: "Convert" case .playground: "Playground" case .evaluate: "Evaluate" } } var icon: String { switch self { case .models: "shippingbox" case .datasets: "tablecells" case .train: "flame" case .convert: "arrow.triangle.2.circlepath" case .playground: "bubble.left.and.text.bubble.right" case .evaluate: "checkmark.seal" } } /// ⌘-shortcuts (charter Phase 6: ⌘L Models, ⌘D Datasets, ⌘R Train, /// ⌘I Playground; Convert/Evaluate get the next free letters). var shortcut: KeyEquivalent { switch self { case .models: "l" case .datasets: "d" case .train: "r" case .convert: "u" case .playground: "i" case .evaluate: "e" } } } extension Notification.Name { /// Posted by the Foundry menu commands to switch workbench sections. static let zyquoNavigate = Notification.Name("zyquo.navigate") } /// Root observable state for the workbench: library contents, run history, /// active jobs, and the live memory readout in the navigator footer. @Observable @MainActor final class AppModel { var selectedSection: WorkbenchSection = .models var models: [LocalModel] = [] var datasets: [Dataset] = [] var runs: [TrainingRun] = [] var jobs: [Job] = [] var activeJobCount: Int { jobs.filter { $0.state == .running }.count } /// GPU active memory, refreshed by the footer. var memoryUsedBytes: Int64 = 0 var memoryTotalBytes: Int64 = MemoryAdvisor.physicalMemory var lastError: String? /// Refresh all library-backed lists from disk. func refresh() async { do { try PersistenceService.ensureDirectories() models = try await ModelStore.shared.scan() datasets = try await DatasetService.shared.scan() runs = try await RunStore.shared.scan() let snapshot = await InferenceEngine.shared.memorySnapshot() memoryUsedBytes = Int64(snapshot.active) } catch { lastError = error.localizedDescription } } }