SPB Git

spb/zyquo-mlx Public MIT

The local MLX foundry for your Mac — run, fine-tune, quantize, and ship models. Nothing leaves your machine.

Swift 93.4% Python 3.8% Makefile 2.2% Shell 0.5%
3.8 KB · 122 lines swift
Raw Blame History
1//2//  ModelsView.swift3//  Zyquo MLX4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//89import SwiftUI1011/// Models section: installed local library + Discover (Hub search wired in12/// Phase 6).13struct ModelsView: View {14    @Bindable var model: AppModel15    @State private var tab: Tab = .installed1617    enum Tab: String, CaseIterable {18        case installed = "Installed"19        case discover = "Discover"20    }2122    var body: some View {23        VStack(spacing: 0) {24            Picker("", selection: $tab) {25                ForEach(Tab.allCases, id: \.self) { Text($0.rawValue) }26            }27            .pickerStyle(.segmented)28            .frame(width: 240)29            .padding(.vertical, ZyquoTheme.spacing12)3031            switch tab {32            case .installed: installed33            case .discover: discover34            }35        }36    }3738    @ViewBuilder39    private var installed: some View {40        if model.models.isEmpty {41            EmptyStateView(42                icon: "shippingbox",43                title: "No Models Yet",44                message: "Your foundry starts with a model. Discover curated MLX models from the community, or drop an existing MLX model folder into your library.",45                actionLabel: "Discover Models",46                action: { tab = .discover })47        } else {48            ScrollView {49                LazyVStack(spacing: ZyquoTheme.spacing8) {50                    ForEach(model.models) { item in51                        ModelRow(item: item)52                    }53                }54                .padding(ZyquoTheme.spacing20)55            }56        }57    }5859    private var discover: some View {60        DiscoverView(model: model)61    }62}6364struct ModelRow: View {65    let item: LocalModel66    @State private var isHovering = false6768    var body: some View {69        HStack(spacing: ZyquoTheme.spacing12) {70            Image(systemName: iconName)71                .font(.system(size: 18, weight: .light))72                .foregroundStyle(ZyquoTheme.accent)73                .frame(width: 32, height: 32)74                .background(ZyquoTheme.accentSubtle)75                .clipShape(RoundedRectangle(cornerRadius: ZyquoTheme.radiusSmall))7677            VStack(alignment: .leading, spacing: ZyquoTheme.spacing2) {78                Text(item.repoID ?? item.name)79                    .font(ZyquoTheme.bodyFont.weight(.medium))80                    .foregroundStyle(ZyquoTheme.textPrimary)81                    .lineLimit(1)82                HStack(spacing: ZyquoTheme.spacing8) {83                    Text(item.displayParams)84                    if let quant = item.quantization { Text(quant.label) }85                    Text(ByteCountFormatter.string(fromByteCount: item.diskSize, countStyle: .file))86                }87                .font(ZyquoTheme.captionFont)88                .foregroundStyle(ZyquoTheme.textSecondary)89            }9091            Spacer()9293            TypeBadge(type: item.type)94            VerdictBadge(verdict: MemoryAdvisor.inferenceVerdict(for: item))9596            Button {97                NSWorkspace.shared.activateFileViewerSelecting([item.directory])98            } label: {99                Image(systemName: "magnifyingglass")100                    .font(.system(size: 11))101                    .foregroundStyle(ZyquoTheme.textTertiary)102            }103            .buttonStyle(.plain)104            .help("Reveal in Finder")105            .opacity(isHovering ? 1 : 0)106        }107        .padding(ZyquoTheme.spacing12)108        .zyquoCard()109        .onHover { isHovering = $0 }110    }111112    private var iconName: String {113        switch item.type {114        case .llm: "text.alignleft"115        case .vlm: "photo.on.rectangle.angled"116        case .embedding: "point.3.connected.trianglepath.dotted"117        case .speech: "waveform"118        case .imageGeneration: "paintbrush"119        }120    }121}122