Skip to content

Commit 53ebedf

Browse files
authored
Refactor UI & Enhance Statistics/Controls for Moshi (#10)
* Improve the layout of the ModelView screen * Better handling of stats * Further improvements to the UI * Default to stats being expanded
1 parent 3c7936b commit 53ebedf

File tree

3 files changed

+387
-184
lines changed

3 files changed

+387
-184
lines changed

Moshi/ContentView.swift

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,52 +26,70 @@ enum ModelSelect: String, CaseIterable, Identifiable {
2626
case helium
2727

2828
var id: Self { return self }
29+
30+
var name: String {
31+
switch self {
32+
case .hibiki:
33+
return "Hibiki 1B"
34+
default:
35+
return rawValue.capitalized
36+
}
37+
}
38+
39+
var description: String {
40+
switch self {
41+
case .hibiki:
42+
return "A French to English simultaneous translation model designed for real-time speech translation."
43+
default:
44+
return ""
45+
}
46+
}
2947
}
3048

3149
struct ContentView: View {
3250
@State var model = Evaluator()
3351
@State var selectedModel: ModelSelect = .mimi
3452
@State var sendToSpeaker = false
3553
@Environment(DeviceStat.self) private var deviceStat
36-
@State private var displayStats = false
37-
54+
55+
// Currently available models
56+
private let availableModels: [ModelSelect] = [.hibiki]
3857
var body: some View {
39-
NavigationSplitView(
40-
sidebar: {
41-
VStack {
42-
List {
43-
ForEach([ModelSelect.hibiki]) { modelType in
44-
NavigationLink(
45-
modelType.rawValue,
46-
destination: {
47-
ModelView(
48-
model: $model, modelType: modelType, displayStats: $displayStats
58+
Group {
59+
if availableModels.count == 1 {
60+
// Skip navigation if only one model
61+
ModelView(
62+
model: $model,
63+
modelType: availableModels[0]
64+
)
65+
} else {
66+
NavigationSplitView(
67+
sidebar: {
68+
VStack {
69+
ForEach(availableModels) { modelType in
70+
NavigationLink(
71+
modelType.rawValue,
72+
destination: {
73+
ModelView(
74+
model: $model,
75+
modelType: modelType
76+
)
77+
}
4978
)
50-
})
51-
}
52-
}
53-
Toggle("speaker", isOn: $sendToSpeaker)
54-
.toggleStyle(.button)
55-
.padding()
56-
.onChange(of: sendToSpeaker) { newValue in
57-
if newValue {
58-
setDefaultToSpeaker()
59-
} else {
60-
setDefaultToStd()
79+
}
6180
}
81+
.navigationTitle("Available Models")
82+
#if os(iOS)
83+
.navigationBarTitleDisplayMode(.inline)
84+
#endif
85+
},
86+
detail: {
87+
Text("Please choose a model type")
6288
}
63-
}
64-
.navigationTitle("Available Models")
65-
#if os(iOS)
66-
.navigationBarTitleDisplayMode(.inline)
67-
#endif
68-
69-
},
70-
detail: {
71-
Text("Please choose a model type")
72-
})
89+
)
90+
}
91+
}
7392
}
74-
7593
}
7694

7795
#Preview {

Moshi/DeviceStat.swift

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import Foundation
22
import MLX
33

4+
enum ThermalState: String {
5+
case nominal = "Nominal"
6+
case fair = "Fair"
7+
case serious = "Serious"
8+
case critical = "Critical"
9+
case unknown = "Unknown"
10+
}
11+
412
@Observable
513
final class DeviceStat: @unchecked Sendable {
614

715
@MainActor
816
var gpuUsage = GPU.snapshot()
917
@MainActor
10-
var thermalState: ProcessInfo.ThermalState = .nominal
18+
var thermalState: ThermalState = .nominal
1119

1220
private let initialGPUSnapshot = GPU.snapshot()
1321
private var timer: Timer?
@@ -27,7 +35,18 @@ final class DeviceStat: @unchecked Sendable {
2735
let thermalState = ProcessInfo.processInfo.thermalState
2836
DispatchQueue.main.async { [weak self] in
2937
self?.gpuUsage = gpuSnapshotDelta
30-
self?.thermalState = thermalState
38+
switch thermalState {
39+
case .nominal:
40+
self?.thermalState = .nominal
41+
case .fair:
42+
self?.thermalState = .fair
43+
case .serious:
44+
self?.thermalState = .serious
45+
case .critical:
46+
self?.thermalState = .critical
47+
default:
48+
self?.thermalState = .unknown
49+
}
3150
}
3251
}
3352

0 commit comments

Comments
 (0)