File size: 2,529 Bytes
4fc4790
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import Foundation

public struct SwabbleConfig: Codable, Sendable {
    public struct Audio: Codable, Sendable {
        public var deviceName: String = ""
        public var deviceIndex: Int = -1
        public var sampleRate: Double = 16000
        public var channels: Int = 1
    }

    public struct Wake: Codable, Sendable {
        public var enabled: Bool = true
        public var word: String = "clawd"
        public var aliases: [String] = ["claude"]
    }

    public struct Hook: Codable, Sendable {
        public var command: String = ""
        public var args: [String] = []
        public var prefix: String = "Voice swabble from ${hostname}: "
        public var cooldownSeconds: Double = 1
        public var minCharacters: Int = 24
        public var timeoutSeconds: Double = 5
        public var env: [String: String] = [:]
    }

    public struct Logging: Codable, Sendable {
        public var level: String = "info"
        public var format: String = "text" // text|json placeholder
    }

    public struct Transcripts: Codable, Sendable {
        public var enabled: Bool = true
        public var maxEntries: Int = 50
    }

    public struct Speech: Codable, Sendable {
        public var localeIdentifier: String = Locale.current.identifier
        public var etiquetteReplacements: Bool = false
    }

    public var audio = Audio()
    public var wake = Wake()
    public var hook = Hook()
    public var logging = Logging()
    public var transcripts = Transcripts()
    public var speech = Speech()

    public static let defaultPath = FileManager.default
        .homeDirectoryForCurrentUser
        .appendingPathComponent(".config/swabble/config.json")

    public init() {}
}

public enum ConfigError: Error {
    case missingConfig
}

public enum ConfigLoader {
    public static func load(at path: URL?) throws -> SwabbleConfig {
        let url = path ?? SwabbleConfig.defaultPath
        if !FileManager.default.fileExists(atPath: url.path) {
            throw ConfigError.missingConfig
        }
        let data = try Data(contentsOf: url)
        return try JSONDecoder().decode(SwabbleConfig.self, from: data)
    }

    public static func save(_ config: SwabbleConfig, at path: URL?) throws {
        let url = path ?? SwabbleConfig.defaultPath
        let dir = url.deletingLastPathComponent()
        try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
        let data = try JSONEncoder().encode(config)
        try data.write(to: url)
    }
}