SPB Git

spb/forge-studio

Public

The Instruments of LLM training — a native macOS cockpit for Forge. Train language models from scratch on Apple Silicon without a terminal.

3.6 KB · 119 lines swift
1// Author: Simon-Pierre Boucher — contact@spboucher.ai2//3// Navigation shell: runs in the sidebar, live dashboard in the detail pane.4import SwiftUI56struct ContentView: View {7    @Environment(AppModel.self) private var app8    @State private var selection: UUID?9    @State private var showNewRun = false1011    var body: some View {12        NavigationSplitView {13            List(selection: $selection) {14                Section("Runs") {15                    ForEach(app.store.runs.sorted { $0.createdAt > $1.createdAt }) { run in16                        RunRow(run: run).tag(run.id)17                    }18                }19            }20            .navigationSplitViewColumnWidth(min: 220, ideal: 260)21            .toolbar {22                ToolbarItem(placement: .primaryAction) {23                    Button {24                        showNewRun = true25                    } label: {26                        Label("Nouveau run", systemImage: "plus")27                    }28                    .disabled(app.forgeError != nil)29                }30            }31        } detail: {32            if let id = selection,33               let run = app.store.runs.first(where: { $0.id == id }) {34                RunDetailView(run: run)35            } else {36                EmptyStateView()37            }38        }39        .sheet(isPresented: $showNewRun) {40            NewRunSheet()41        }42    }43}4445struct RunRow: View {46    let run: Run4748    var body: some View {49        VStack(alignment: .leading, spacing: 2) {50            HStack {51                Text(run.name).font(.headline)52                Spacer()53                StateBadge(state: run.state)54            }55            HStack(spacing: 8) {56                Text("\(run.config.model.paramCount.formatted(.number.notation(.compactName))) params")57                if let loss = run.lastTrainLoss {58                    Text(String(format: "loss %.3f", loss))59                }60                if run.state.isActive {61                    ProgressView(value: run.progress).frame(width: 60)62                }63            }64            .font(.caption)65            .foregroundStyle(.secondary)66        }67        .padding(.vertical, 2)68    }69}7071struct StateBadge: View {72    let state: RunState7374    var color: Color {75        switch state {76        case .running, .launching: return .green77        case .finished: return .blue78        case .failed: return .red79        case .stopped: return .orange80        case .queued, .finishing: return .gray81        }82    }8384    var body: some View {85        Text(state.rawValue)86            .font(.caption2.weight(.semibold))87            .padding(.horizontal, 6)88            .padding(.vertical, 2)89            .background(color.opacity(0.18), in: Capsule())90            .foregroundStyle(color)91    }92}9394struct EmptyStateView: View {95    @Environment(AppModel.self) private var app9697    var body: some View {98        VStack(spacing: 12) {99            Image(systemName: "flame")100                .font(.system(size: 48))101                .foregroundStyle(.orange)102            Text("Forge Studio").font(.largeTitle.weight(.semibold))103            if let device = app.forgeDevice {104                Label(device, systemImage: "cpu")105                    .foregroundStyle(.secondary)106            }107            if let error = app.forgeError {108                Label(error, systemImage: "exclamationmark.triangle")109                    .foregroundStyle(.red)110                SettingsLink { Text("Ouvrir les Réglages…") }111            } else {112                Text("Sélectionnez un run, ou créez-en un nouveau (+).")113                    .foregroundStyle(.secondary)114            }115        }116        .frame(maxWidth: .infinity, maxHeight: .infinity)117    }118}119
Files · 94570c75210a2acf467858b3c58005f0730d3bb4
Menu

Theme