mana-swift-ui/Sources/ManaComponentsUI/ManaJobStatusText.swift
Till JS a8621c3ef5 ManaComponentsUI: ManaJobStatusView — Wartezeit-Anzeige für GPU-Jobs
Natives Pendant zu @mana/shared-ui <JobStatus>. + Texthelfer
ManaJobStatusText (9 Tests im neuen Target ManaComponentsUITests).
CHANGELOG trägt zusätzlich den vorbestehenden AutoFill-[Unreleased]-
Eintrag (fremde Arbeit, nur Doku-Text).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 15:58:50 +02:00

64 lines
2.3 KiB
Swift

import Foundation
/// Lebenszyklus eines lang laufenden KI-/GPU-Jobs (Stem-Separation,
/// Musik-Generierung, Transkription …).
public enum ManaJobState: Sendable {
case queued
case running
case done
case failed
}
/// Reine Text-Helfer für ``ManaJobStatusView`` — getrennt von der View,
/// damit sie ohne SwiftUI testbar sind. Deutsch-first (eine Stimme).
///
/// Pendant zu `formatElapsed` / `formatJobEstimate` aus
/// `@mana/shared-ui` (Web), damit web und nativ identisch formulieren.
public enum ManaJobStatusText {
/// Verstrichene Zeit kompakt: `45 s`, `1:05 min`, `12:30 min`.
/// Negative/ungültige Werte → `0 s`.
public static func formatElapsed(_ seconds: Int) -> String {
let s = max(0, seconds)
if s < 60 { return "\(s) s" }
let m = s / 60
let rest = s % 60
return String(format: "%d:%02d min", m, rest)
}
/// Ehrliche Dauer-Erwartung, z.B.
/// „dauert meist unter 1 Minute, bei Andrang bis zu 10 Minuten".
///
/// `typicalSeconds` = übliche Dauer ohne Andrang. `maxMinutes` =
/// realistische Obergrenze bei belegter GPU (der GPU-Broker
/// serialisiert → Wartezeit ist real und wird benannt, nicht
/// versteckt). Fehlt `maxMinutes`, entfällt der Andrang-Teil.
public static func formatEstimate(typicalSeconds: Int?, maxMinutes: Int?) -> String {
var parts: [String] = []
if let typical = typicalSeconds, typical > 0 {
if typical <= 60 {
parts.append("dauert meist unter 1 Minute")
} else {
let mins = Int((Double(typical) / 60).rounded())
parts.append("dauert meist etwa \(mins) \(mins == 1 ? "Minute" : "Minuten")")
}
}
if let maxM = maxMinutes, maxM > 0 {
let lead = parts.isEmpty ? "bei Andrang" : ", bei Andrang"
parts.append("\(lead) bis zu \(maxM) \(maxM == 1 ? "Minute" : "Minuten")")
}
return parts.joined()
}
/// Default-Titelzeile pro Zustand (überschreibbar via `label`).
public static func defaultTitle(_ state: ManaJobState) -> String {
switch state {
case .queued: return "In der Warteschlange"
case .running: return "Wird bearbeitet"
case .done: return "Fertig"
case .failed: return "Fehlgeschlagen"
}
}
}