Files
clawdbot/apps/macos/Sources/Clawdis/VoiceWakeChime.swift
2025-12-08 20:45:05 +01:00

83 lines
2.3 KiB
Swift

import AppKit
import Foundation
enum VoiceWakeChime: Codable, Equatable {
case system(name: String)
case custom(displayName: String, bookmark: Data)
var systemName: String? {
if case let .system(name) = self {
return name
}
return nil
}
var displayLabel: String {
switch self {
case let .system(name):
return VoiceWakeChimeCatalog.displayName(for: name)
case let .custom(displayName, _):
return displayName
}
}
}
struct VoiceWakeChimeCatalog {
/// Options shown in the picker; first entry is the default bundled tone.
static let systemOptions: [String] = [
defaultVoiceWakeChimeName,
"Ping",
"Pop",
"Glass",
"Frog",
"Submarine",
"Funk",
"Tink",
]
static func displayName(for raw: String) -> String {
if raw == defaultVoiceWakeChimeName { return "Startrek Computer" }
return raw
}
}
enum VoiceWakeChimePlayer {
@MainActor
static func play(_ chime: VoiceWakeChime) {
guard let sound = self.sound(for: chime) else { return }
sound.play()
}
private static func sound(for chime: VoiceWakeChime) -> NSSound? {
switch chime {
case let .system(name):
// Prefer bundled tone if present.
if let bundled = bundledSound(named: name) {
return bundled
}
return NSSound(named: NSSound.Name(name))
case let .custom(_, bookmark):
var stale = false
guard let url = try? URL(
resolvingBookmarkData: bookmark,
options: [.withoutUI, .withSecurityScope],
bookmarkDataIsStale: &stale)
else { return nil }
let scoped = url.startAccessingSecurityScopedResource()
defer { if scoped { url.stopAccessingSecurityScopedResource() } }
return NSSound(contentsOf: url, byReference: false)
}
}
private static func bundledSound(named name: String) -> NSSound? {
guard let url = Bundle.main.url(
forResource: name,
withExtension: defaultVoiceWakeChimeExtension,
subdirectory: "Resources/Sounds")
else { return nil }
return NSSound(contentsOf: url, byReference: false)
}
}