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>
64 lines
2.3 KiB
Swift
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"
|
|
}
|
|
}
|
|
}
|