Skip to content

Commit 4930f72

Browse files
committed
feat: expose converter settings manifest
1 parent d5c6a76 commit 4930f72

17 files changed

Lines changed: 582 additions & 29 deletions

Core/Sources/ConverterServer/main.swift

Lines changed: 315 additions & 1 deletion
Large diffs are not rendered by default.

Core/Sources/Core/Configs/BoolConfigItem.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ protocol BoolConfigItem: ConfigItem<Bool> {
77
extension BoolConfigItem {
88
public var value: Bool {
99
get {
10-
if let value = UserDefaults.standard.object(forKey: Self.key) {
10+
if let value = Config.object(forKey: Self.key) {
1111
value as? Bool ?? Self.default
1212
} else {
1313
Self.default
1414
}
1515
}
1616
nonmutating set {
17-
UserDefaults.standard.set(newValue, forKey: Self.key)
17+
Config.set(newValue, forKey: Self.key)
1818
}
1919
}
2020
}

Core/Sources/Core/Configs/Config.swift

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
11
import Foundation
22

33
/// namespace for `Config`
4-
public enum Config {}
4+
public enum Config {
5+
nonisolated(unsafe) public static let userDefaults: UserDefaults = {
6+
#if os(macOS)
7+
UserDefaults(suiteName: AppGroup.azooKeyMacIdentifier) ?? .standard
8+
#else
9+
.standard
10+
#endif
11+
}()
12+
13+
public static func object(forKey key: String) -> Any? {
14+
if let value = Self.userDefaults.object(forKey: key) {
15+
return value
16+
}
17+
return UserDefaults.standard.object(forKey: key)
18+
}
19+
20+
public static func data(forKey key: String) -> Data? {
21+
if let value = Self.userDefaults.data(forKey: key) {
22+
return value
23+
}
24+
return UserDefaults.standard.data(forKey: key)
25+
}
26+
27+
public static func string(forKey key: String) -> String? {
28+
if let value = Self.userDefaults.string(forKey: key) {
29+
return value
30+
}
31+
return UserDefaults.standard.string(forKey: key)
32+
}
33+
34+
public static func set(_ value: Any?, forKey key: String) {
35+
Self.userDefaults.set(value, forKey: key)
36+
}
37+
}
538

639
public protocol ConfigItem<Value> {
740
static var key: String { get }

Core/Sources/Core/Configs/CustomCodableConfigItem.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ protocol CustomCodableConfigItem: ConfigItem {
1616
extension CustomCodableConfigItem {
1717
public var value: Value {
1818
get {
19-
guard let data = UserDefaults.standard.data(forKey: Self.key) else {
19+
guard let data = Config.data(forKey: Self.key) else {
2020
print(#file, #line, "data is not set yet")
2121
return Self.default
2222
}
@@ -31,7 +31,7 @@ extension CustomCodableConfigItem {
3131
nonmutating set {
3232
do {
3333
let encoded = try JSONEncoder().encode(newValue)
34-
UserDefaults.standard.set(encoded, forKey: Self.key)
34+
Config.set(encoded, forKey: Self.key)
3535
} catch {
3636
print(#file, #line, error)
3737
}
@@ -218,7 +218,7 @@ extension Config {
218218
public static var `default`: Value {
219219
// Migration: If user had OpenAI API enabled, preserve that setting
220220
let legacyKey = Config.Deprecated.EnableOpenAiApiKey.key
221-
if let legacyValue = UserDefaults.standard.object(forKey: legacyKey) as? Bool,
221+
if let legacyValue = Config.object(forKey: legacyKey) as? Bool,
222222
legacyValue {
223223
return .openAI
224224
}

Core/Sources/Core/Configs/IntConfigItem.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ protocol IntConfigItem: ConfigItem<Int> {
77
extension IntConfigItem {
88
public var value: Int {
99
get {
10-
if let value = UserDefaults.standard.object(forKey: Self.key) {
10+
if let value = Config.object(forKey: Self.key) {
1111
value as? Int ?? Self.default
1212
} else {
1313
Self.default
1414
}
1515
}
1616
nonmutating set {
17-
UserDefaults.standard.set(newValue, forKey: Self.key)
17+
Config.set(newValue, forKey: Self.key)
1818
}
1919
}
2020
}

Core/Sources/Core/Configs/KeyboardShortcutConfigItem.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ protocol KeyboardShortcutConfigItem: ConfigItem<KeyboardShortcut> {
4545
extension KeyboardShortcutConfigItem {
4646
public var value: KeyboardShortcut {
4747
get {
48-
guard let data = UserDefaults.standard.data(forKey: Self.key) else {
48+
guard let data = Config.data(forKey: Self.key) else {
4949
return Self.default
5050
}
5151
do {
@@ -58,7 +58,7 @@ extension KeyboardShortcutConfigItem {
5858
nonmutating set {
5959
do {
6060
let encoded = try JSONEncoder().encode(newValue)
61-
UserDefaults.standard.set(encoded, forKey: Self.key)
61+
Config.set(encoded, forKey: Self.key)
6262
} catch {
6363
// エンコード失敗時は何もしない
6464
}

Core/Sources/Core/Configs/PuntuationConfigItem.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ extension Config {
1818
extension Config.PunctuationStyle {
1919
public var value: Value {
2020
get {
21-
guard let data = UserDefaults.standard.data(forKey: Self.key) else {
21+
guard let data = Config.data(forKey: Self.key) else {
2222
print(#file, #line, "data is not set yet")
2323
// この場合、過去の設定を反映する
2424
return if Config.Deprecated.TypeCommaAndPeriod().value {
@@ -38,7 +38,7 @@ extension Config.PunctuationStyle {
3838
nonmutating set {
3939
do {
4040
let encoded = try JSONEncoder().encode(newValue)
41-
UserDefaults.standard.set(encoded, forKey: Self.key)
41+
Config.set(encoded, forKey: Self.key)
4242
} catch {
4343
print(#file, #line, error)
4444
}

Core/Sources/Core/Configs/StringConfigItem.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ protocol StringConfigItem: ConfigItem<String> {}
1212
extension StringConfigItem {
1313
public var value: String {
1414
get {
15-
UserDefaults.standard.string(forKey: Self.key) ?? ""
15+
Config.string(forKey: Self.key) ?? ""
1616
}
1717
nonmutating set {
18-
UserDefaults.standard.set(newValue, forKey: Self.key)
18+
Config.set(newValue, forKey: Self.key)
1919
}
2020
}
2121
}
@@ -46,11 +46,11 @@ extension Config {
4646

4747
public var value: String {
4848
get {
49-
let stored = UserDefaults.standard.string(forKey: Self.key) ?? ""
49+
let stored = Config.string(forKey: Self.key) ?? ""
5050
return stored.isEmpty ? Self.default : stored
5151
}
5252
nonmutating set {
53-
UserDefaults.standard.set(newValue, forKey: Self.key)
53+
Config.set(newValue, forKey: Self.key)
5454
}
5555
}
5656
}

Core/Sources/Core/InputUtils/Actions/UserAction.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,12 @@ public enum UserAction {
107107

108108
// この種のコードは複雑にしかならないので、lintを無効にする
109109
// swiftlint:disable:next cyclomatic_complexity
110-
public static func getUserAction(eventCore: KeyEventCore, inputLanguage: InputLanguage) -> UserAction {
110+
public static func getUserAction(
111+
eventCore: KeyEventCore,
112+
inputLanguage: InputLanguage,
113+
typeBackSlash: Bool? = nil
114+
) -> UserAction {
115+
let typeBackSlash = typeBackSlash ?? Config.TypeBackSlash().value
111116
// see: https://developer.mozilla.org/ja/docs/Web/API/UI_Events/Keyboard_event_code_values#mac_%E3%81%A7%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E5%80%A4
112117
func keyMap(_ string: String, invertPunctuation: Bool = false) -> [InputPiece] {
113118
switch inputLanguage {
@@ -161,13 +166,13 @@ public enum UserAction {
161166
case ("¥", [.shift, .option]), ("¥", [.shift]), ("\\", [.shift, .option]), ("\\", [.shift]):
162167
return .input(keyMap("|"))
163168
case ("¥", []), ("\\", []):
164-
return if Config.TypeBackSlash().value {
169+
return if typeBackSlash {
165170
.input(keyMap("\\"))
166171
} else {
167172
.input(keyMap("¥"))
168173
}
169174
case ("¥", [.option]), ("\\", [.option]):
170-
return if Config.TypeBackSlash().value {
175+
return if typeBackSlash {
171176
.input(keyMap("¥"))
172177
} else {
173178
.input(keyMap("\\"))

Core/Sources/Core/XPC/ConverterServerXPCProtocol.swift

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public enum ConverterServerCodec {
2525
public enum ConverterServerCommand: Codable, Sendable {
2626
case activate(sessionID: String)
2727
case deactivate(sessionID: String)
28+
case listSettings(sessionID: String, capabilities: ConverterSettingClientCapabilities)
29+
case updateSetting(sessionID: String, key: String, value: ConverterSettingValue)
2830
case snapshot(sessionID: String, inputState: ConverterInputState)
2931
case stopComposition(sessionID: String)
3032
case forgetMemory(sessionID: String)
@@ -38,6 +40,102 @@ public enum ConverterServerCommand: Codable, Sendable {
3840
case submitSelectedReplaceSuggestion(sessionID: String)
3941
}
4042

43+
public struct ConverterSettingClientCapabilities: Codable, Sendable, Equatable {
44+
public var supportedKinds: Set<ConverterSettingKindIdentifier>
45+
public var supportedActions: Set<String>
46+
public var supportedCustomSurfaces: Set<String>
47+
48+
public init(
49+
supportedKinds: Set<ConverterSettingKindIdentifier> = [],
50+
supportedActions: Set<String> = [],
51+
supportedCustomSurfaces: Set<String> = []
52+
) {
53+
self.supportedKinds = supportedKinds
54+
self.supportedActions = supportedActions
55+
self.supportedCustomSurfaces = supportedCustomSurfaces
56+
}
57+
}
58+
59+
public enum ConverterSettingKindIdentifier: String, Codable, Sendable, Hashable {
60+
case toggle
61+
case selector
62+
case textField
63+
case number
64+
case button
65+
case custom
66+
}
67+
68+
public enum ConverterSettingKind: Codable, Sendable, Equatable {
69+
case toggle
70+
case selector(options: [ConverterSettingOption])
71+
case textField(secure: Bool)
72+
case number(min: Double?, max: Double?, step: Double?)
73+
case button(action: String)
74+
case custom(surface: String)
75+
76+
public var identifier: ConverterSettingKindIdentifier {
77+
switch self {
78+
case .toggle:
79+
.toggle
80+
case .selector:
81+
.selector
82+
case .textField:
83+
.textField
84+
case .number:
85+
.number
86+
case .button:
87+
.button
88+
case .custom:
89+
.custom
90+
}
91+
}
92+
}
93+
94+
public enum ConverterSettingValue: Codable, Sendable, Equatable {
95+
case bool(Bool)
96+
case string(String)
97+
case int(Int)
98+
case double(Double)
99+
}
100+
101+
public struct ConverterSettingOption: Codable, Sendable, Equatable {
102+
public var title: String
103+
public var value: ConverterSettingValue
104+
105+
public init(title: String, value: ConverterSettingValue) {
106+
self.title = title
107+
self.value = value
108+
}
109+
}
110+
111+
public struct ConverterSettingDescriptor: Codable, Sendable, Equatable {
112+
public var key: String
113+
public var title: String
114+
public var section: String
115+
public var kind: ConverterSettingKind
116+
public var value: ConverterSettingValue?
117+
public var isEnabled: Bool
118+
public var requiresClientUpdate: Bool
119+
120+
public init(
121+
key: String,
122+
title: String,
123+
section: String,
124+
kind: ConverterSettingKind,
125+
value: ConverterSettingValue? = nil,
126+
isEnabled: Bool = true,
127+
requiresClientUpdate: Bool = false
128+
) {
129+
self.key = key
130+
self.title = title
131+
self.section = section
132+
self.kind = kind
133+
self.value = value
134+
self.isEnabled = isEnabled
135+
self.requiresClientUpdate = requiresClientUpdate
136+
}
137+
}
138+
41139
public struct ConverterSessionConfig: Codable, Sendable {
42140
public var aiBackendPreference: Config.AIBackendPreference.Value
43141
public var openAIModelName: String
@@ -144,19 +242,22 @@ public struct ConverterServerResponse: Codable, Sendable {
144242
public var effects: [ConverterClientEffect]
145243
public var inputState: ConverterInputState
146244
public var inputLanguage: InputLanguage?
245+
public var settings: [ConverterSettingDescriptor]
147246
public var snapshot: ConverterSessionSnapshot
148247

149248
public init(
150249
handled: Bool = true,
151250
effects: [ConverterClientEffect] = [],
152251
inputState: ConverterInputState = .none,
153252
inputLanguage: InputLanguage? = nil,
253+
settings: [ConverterSettingDescriptor] = [],
154254
snapshot: ConverterSessionSnapshot
155255
) {
156256
self.handled = handled
157257
self.effects = effects
158258
self.inputState = inputState
159259
self.inputLanguage = inputLanguage
260+
self.settings = settings
160261
self.snapshot = snapshot
161262
}
162263
}

0 commit comments

Comments
 (0)