-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathConversionController.swift
More file actions
99 lines (83 loc) · 3.98 KB
/
Copy pathConversionController.swift
File metadata and controls
99 lines (83 loc) · 3.98 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
import Cocoa
/// Imperative shell for a conversion request.
///
/// Builds the pure transform, checks trust, asks `EditableField` to rewrite,
/// then runs the `ConversionPolicy` plan (notifications / success sound /
/// permission prompt). Routing rules live in the policy; this type only wires
/// effects.
final class ConversionController {
static let shared = ConversionController()
private init() {}
/// Trigger a conversion using the user's preferences for direction/scope.
func trigger() {
trigger(directionOverride: nil, scopeOverride: nil)
}
/// Trigger with explicit overrides (used by per-mode menu items).
func trigger(directionOverride: ConversionDirection?, scopeOverride: ConversionScope?) {
let prefs = PreferencesStore.shared
let direction = directionOverride ?? prefs.conversionDirection
let scope = scopeOverride ?? prefs.conversionScope
// Build the closure once so both edit paths convert with identical settings.
let convert: (String) -> String = { text in
ConversionEngine.convert(
text,
direction: direction,
scope: scope,
convertSpace: prefs.convertSpaceToIdeographic
)
}
// ── Gate: no Accessibility → plan is requireTrust; never touch the field ──
guard AccessibilityHelper.shared.isTrusted else {
execute(ConversionPolicy.plan(.untrusted))
return
}
let outcome = EditableField.apply(
convert,
options: EditableField.Options(restoreClipboard: prefs.restoreClipboard)
)
execute(ConversionPolicy.plan(.edited(Self.fieldResult(outcome))))
}
// MARK: - Shell: perform the plan
private func execute(_ plan: ConversionPolicy.Plan) {
switch plan {
case .requireTrust:
AccessibilityHelper.shared.ensureTrustedPrompt()
NotificationPresenter.shared.notify(
title: NSLocalizedString("notify.permissionTitle",
value: "Accessibility permission required", comment: ""),
body: NSLocalizedString("notify.permissionBody",
value: "Grant access in System Settings → Privacy & Security → Accessibility.", comment: ""))
case .applied:
ActionFeedback.playSuccessSoundIfEnabled()
case .noChange:
NotificationPresenter.shared.notify(
title: NSLocalizedString("notify.noChangeTitle",
value: "Nothing changed", comment: ""),
body: NSLocalizedString("notify.noChangeBody",
value: "Text already matches the target form.", comment: ""))
case .notFocused:
NotificationPresenter.shared.notify(
title: NSLocalizedString("notify.noFocusTitle",
value: "No text field in focus", comment: ""),
body: NSLocalizedString("notify.noFocusBody",
value: "Click into a text field, then try again.", comment: ""))
case .unreadable:
NotificationPresenter.shared.notify(
title: NSLocalizedString("notify.nothingTitle",
value: "Nothing to convert", comment: ""),
body: NSLocalizedString("notify.nothingBody",
value: "Couldn't read the focused field. Try selecting the text manually.", comment: ""))
case .silent:
return
}
}
private static func fieldResult(_ outcome: EditableField.Outcome) -> ConversionPolicy.FieldResult {
switch outcome {
case .applied: return .applied
case .noChange: return .noChange
case .notFocused: return .notFocused
case .unreadable: return .unreadable
case .busy: return .busy
}
}
}