forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNotchSystemControlsView.swift
More file actions
172 lines (156 loc) · 6.46 KB
/
Copy pathNotchSystemControlsView.swift
File metadata and controls
172 lines (156 loc) · 6.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import AppKit
import OmiTheme
import SwiftUI
/// The control cluster revealed on the trailing side of the notch hover surface.
///
/// Shows what Omi is looking at right now and lets the user cut its two inputs — screen
/// and audio — without going to the menu bar. Those are the same two toggles the status
/// item owns; both surfaces route through `SystemCaptureControls` so the paywall and
/// permission gates cannot drift between them.
struct NotchSystemControlsView: View {
let progress: CGFloat
@ObservedObject private var shortcuts = ShortcutSettings.shared
/// Toggle state lives in UserDefaults and plugin state rather than in an observable, so
/// it is sampled when the surface opens and after each toggle instead of being bound.
@State private var screenCaptureOn = false
@State private var audioRecordingOn = false
/// The frontmost app the capture loop is currently on. Sampled with the toggles rather than
/// observed: it used to be read off the retired Focus feature's storage, and the plugin that
/// actually drives capture is the honest owner of "what Omi is looking at".
@State private var currentAppName: String?
var body: some View {
VStack(alignment: .trailing, spacing: OmiSpacing.xs) {
if let currentAppName {
currentAppRow(currentAppName)
}
shortcutLegend
HStack(spacing: OmiSpacing.xs) {
controlButton(
icon: "rectangle.dashed.badge.record",
label: "Screen",
isOn: screenCaptureOn,
action: toggleScreenCapture
)
controlButton(
icon: "mic.fill",
label: "Audio",
isOn: audioRecordingOn,
action: toggleAudioRecording
)
}
}
.opacity(progress)
.allowsHitTesting(progress > 0.6)
.onAppear(perform: refresh)
.onChange(of: progress) { _, newValue in
// Re-sample when the surface opens: the menu bar or Settings may have changed
// either toggle while the notch was collapsed.
if newValue > 0.6 { refresh() }
}
}
/// The frontmost app as the capture loop sees it, so the notch and the assistants can never
/// disagree about the active app.
private static func frontmostCaptureApp() -> String? {
let name = ProactiveAssistantsPlugin.shared.currentStatus.currentApp
guard let name, !name.trimmingCharacters(in: .whitespaces).isEmpty else { return nil }
return name
}
/// What Omi is currently looking at.
private func currentAppRow(_ name: String) -> some View {
HStack(spacing: OmiSpacing.xxs) {
Circle()
.fill(screenCaptureOn ? NotchGlass.ink(.w55) : NotchGlass.ink(.w25))
.frame(width: 5, height: 5)
Text(name)
.scaledFont(size: OmiType.micro, weight: .medium)
.foregroundColor(NotchGlass.secondary)
.lineLimit(1)
.truncationMode(.tail)
}
.accessibilityElement(children: .combine)
.accessibilityLabel(screenCaptureOn ? "Omi is watching \(name)" : "Omi is not watching \(name)")
}
// MARK: - Shortcut legend
/// What you can say/press and the keys that do it. Read-only by design: this is a
/// legend, not a second route into chat or settings — the duplicate entry points that
/// used to live here were removed for exactly that reason (FC-split-mutation-authority).
private var shortcutLegend: some View {
// One row per configurable shortcut, named the way Settings advertises it
// ("Open Omi Shortcut"). The fixed window-summon chord is deliberately not
// listed: two near-identical "open" rows read as a bug, not a feature.
VStack(alignment: .trailing, spacing: 3) {
if shortcuts.pttEnabled {
shortcutRow("Talk", keys: shortcuts.pttShortcut.displayTokens)
}
if shortcuts.askOmiEnabled {
shortcutRow("Open", keys: shortcuts.askOmiShortcut.displayTokens)
}
}
.accessibilityElement(children: .contain)
.accessibilityLabel("Keyboard shortcuts")
}
private func shortcutRow(_ title: String, keys: [String]) -> some View {
HStack(spacing: 3) {
Text(title)
.scaledFont(size: 8, weight: .semibold)
.foregroundStyle(NotchGlass.ink(.w55))
shortcutKeys(keys)
}
.accessibilityElement(children: .combine)
.accessibilityLabel("\(title): \(keys.joined(separator: " "))")
}
private func shortcutKeys(_ keys: [String]) -> some View {
ForEach(ShortcutHintLayout.visibleTokens(for: keys), id: \.self) { key in
Text(key)
.scaledFont(size: 8, weight: .medium)
.foregroundStyle(NotchGlass.ink(.w75))
.lineLimit(1)
.minimumScaleFactor(0.7)
.padding(.horizontal, key.count > 1 ? 3 : 0)
.frame(minWidth: 12, minHeight: 12)
.background(NotchGlass.fillHover)
.cornerRadius(OmiChrome.stripRadius)
}
.fixedSize(horizontal: true, vertical: false)
}
private func controlButton(icon: String, label: String, isOn: Bool, action: @escaping () -> Void) -> some View {
Button(action: action) {
HStack(spacing: OmiSpacing.xxs) {
Image(systemName: icon)
.font(.system(size: 9, weight: .semibold))
Text(label)
.scaledFont(size: OmiType.micro, weight: .semibold)
}
.foregroundColor(NotchGlass.controlLabel(isActive: isOn))
.padding(.horizontal, OmiSpacing.sm)
.padding(.vertical, OmiSpacing.xxs)
.background(
Capsule().fill(NotchGlass.controlFill(isActive: isOn, isHovering: false))
)
.contentShape(Capsule())
}
.buttonStyle(.plain)
.accessibilityLabel("\(label) capture")
.accessibilityValue(isOn ? "On" : "Off")
.accessibilityHint("Toggles whether Omi captures \(label.lowercased())")
.help("\(label) capture is \(isOn ? "on" : "off")")
}
// MARK: - Actions
private func refresh() {
screenCaptureOn = SystemCaptureControls.isScreenCaptureOn
currentAppName = Self.frontmostCaptureApp()
audioRecordingOn = SystemCaptureControls.isAudioRecordingOn
}
private func toggleScreenCapture() {
// Reflect the outcome, not the request: a refused enable (paywall, missing Screen
// Recording permission) must leave the control reading OFF.
let outcome = SystemCaptureControls.setScreenCapture(!screenCaptureOn) { started in
screenCaptureOn = started && SystemCaptureControls.isScreenCaptureOn
}
screenCaptureOn = outcome.resultingIsOn
}
private func toggleAudioRecording() {
let outcome = SystemCaptureControls.setAudioRecording(!audioRecordingOn)
audioRecordingOn = outcome.resultingIsOn
}
}