native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152import AppKitimport Combineimport ComposableArchitectureimport ProwlCLISharedimport SwiftUI
struct NotificationsSettingsView: View { @Bindable var store: StoreOf<SettingsFeature> @State private var thresholdText = ""
var body: some View { VStack(alignment: .leading) { Form { Section("Notifications") { Toggle( "Show bell icon next to worktree", isOn: $store.inAppNotificationsEnabled ) .help("Show bell icon next to worktree") Picker(selection: $store.notificationSound) { Text(NotificationSound.never.displayName).tag(NotificationSound.never) Divider() ForEach(NotificationSound.systemCases) { sound in NotificationSoundLabel(sound: sound).tag(sound) } Divider() NotificationSoundLabel(sound: .prowlClassic).tag(NotificationSound.prowlClassic) } label: { Text("Play notification sound") Text( """ Ignored when system notifications are enabled, as they play sounds\ according to your settings. """ ) } .help("Choose the sound played when a notification is received") .disabled(store.systemNotificationsEnabled) Toggle( "Mute notifications for the active pane", isOn: $store.muteNotificationsForActiveSurface ) .help( """ Skip the banner, sound, and dock bounce when the notification comes from \ the pane you're already looking at. """ ) Toggle( "Move notified worktree to top", isOn: $store.moveNotifiedWorktreeToTop ) .help("Bring the worktree to the top when the terminal receives a notification") Picker( "Bounce dock icon", selection: $store.dockBounceMode ) { ForEach(DockBounceMode.allCases) { mode in Text(mode.title).tag(mode) } } .help("Bounce the Prowl app icon in the Dock when a notification is received.") } Section("System") { Toggle( "System notifications", isOn: $store.systemNotificationsEnabled ) .help("Show macOS system notification banners") Toggle( "Show notification badge on dock icon", isOn: $store.showNotificationDotOnDock ) .help("Show the unread worktree count on the Prowl dock icon.") .disabled(!dockBadgeAvailable) if let dockBadgeCaption { Text(dockBadgeCaption) .font(.callout) .foregroundStyle(.secondary) } } Section("Command Finished") { Toggle( "Notify when long-running commands finish", isOn: $store.commandFinishedNotificationEnabled ) .help("Show a notification when a command exceeds the duration threshold") if store.commandFinishedNotificationEnabled { LabeledContent("Duration threshold") { HStack(spacing: 4) { TextField("", text: $thresholdText) .frame(width: 40) .multilineTextAlignment(.trailing) .onSubmit { store.send(.setCommandFinishedNotificationThreshold(thresholdText)) } .onChange(of: store.commandFinishedNotificationThreshold) { _, newValue in thresholdText = String(newValue) } Text("seconds") .foregroundStyle(.secondary) } } .help("Minimum command duration in seconds before a notification is shown") .onAppear { thresholdText = String(store.commandFinishedNotificationThreshold) } } } } .formStyle(.grouped) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .task { store.send(.refreshDockBadgeAuthorization) } // Changing the notification permission or "Badge app icon" switch happens // in System Settings, so the moment Prowl regains focus is when to re-read // the macOS state and update the toggle live (no restart needed). .onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in store.send(.refreshDockBadgeAuthorization) } }
private var dockBadgeAvailable: Bool { store.dockBadgeAuthorization == .available }
private var dockBadgeCaption: String? { switch store.dockBadgeAuthorization { case .available: return nil case .notificationsDenied: return String(localized: "Allow notifications for Prowl in System Settings to show the Dock badge.") case .badgeDisabled: return String(localized: "Turn on “Badge app icon” for Prowl in System Settings > Notifications.") } }}
private struct NotificationSoundLabel: View { let sound: NotificationSound
var body: some View { if sound == GlobalSettings.default.notificationSound { Text("\(sound.displayName) \(Text("Default").foregroundStyle(.secondary))") } else { Text(sound.displayName) } }}