@lattices/cli 0.4.2 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/app/Info.plist +2 -2
- package/app/Lattices.app/Contents/Info.plist +2 -2
- package/app/Lattices.app/Contents/MacOS/Lattices +0 -0
- package/app/Package.swift +6 -0
- package/app/Sources/AppShell/App.swift +20 -0
- package/app/Sources/{AppDelegate.swift → AppShell/AppDelegate.swift} +94 -34
- package/app/Sources/{AppShellView.swift → AppShell/AppShellView.swift} +12 -1
- package/app/Sources/AppShell/AppUpdater.swift +92 -0
- package/app/Sources/AppShell/CliActionLauncher.swift +50 -0
- package/app/Sources/{HomeDashboardView.swift → AppShell/HomeDashboardView.swift} +18 -10
- package/app/Sources/AppShell/LatticesRuntime.swift +61 -0
- package/app/Sources/{MainView.swift → AppShell/MainView.swift} +351 -191
- package/app/Sources/{OnboardingView.swift → AppShell/OnboardingView.swift} +30 -16
- package/app/Sources/{Preferences.swift → AppShell/Preferences.swift} +78 -0
- package/app/Sources/{SettingsView.swift → AppShell/SettingsView.swift} +869 -152
- package/app/Sources/{HotkeyStore.swift → Core/Actions/HotkeyStore.swift} +9 -5
- package/app/Sources/{IntentEngine.swift → Core/Actions/IntentEngine.swift} +51 -27
- package/app/Sources/Core/Actions/IntentSchema.swift +94 -0
- package/app/Sources/{Intents → Core/Actions/Intents}/LatticeIntent.swift +0 -25
- package/app/Sources/{PaletteCommand.swift → Core/Actions/PaletteCommand.swift} +26 -6
- package/app/Sources/{VoiceIntentResolver.swift → Core/Actions/VoiceIntentResolver.swift} +46 -4
- package/app/Sources/Core/Companion/CompanionActivityLog.swift +70 -0
- package/app/Sources/Core/Companion/CompanionKeyboardController.swift +141 -0
- package/app/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +438 -0
- package/app/Sources/Core/Companion/LatticesCompanionCockpit.swift +555 -0
- package/app/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +594 -0
- package/app/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +204 -0
- package/app/Sources/Core/Companion/LatticesDeckHost.swift +1463 -0
- package/app/Sources/{LatticesApi.swift → Core/Daemon/LatticesApi.swift} +125 -4
- package/app/Sources/{AppTypeClassifier.swift → Core/Desktop/AppTypeClassifier.swift} +36 -0
- package/app/Sources/{DesktopModel.swift → Core/Desktop/DesktopModel.swift} +6 -8
- package/app/Sources/Core/Desktop/MouseFinder.swift +527 -0
- package/app/Sources/Core/Desktop/SessionWindowLocator.swift +139 -0
- package/app/Sources/Core/Desktop/WindowDragSnapController.swift +628 -0
- package/app/Sources/Core/Desktop/WindowPreviewCard.swift +100 -0
- package/app/Sources/Core/Desktop/WindowPreviewStore.swift +113 -0
- package/app/Sources/Core/Desktop/WindowSelectionStore.swift +76 -0
- package/app/Sources/{WindowTiler.swift → Core/Desktop/WindowTiler.swift} +351 -172
- package/app/Sources/Core/Input/MouseGestureConfig.swift +364 -0
- package/app/Sources/Core/Input/MouseGestureController.swift +1203 -0
- package/app/Sources/Core/Input/MouseInputDeviceStore.swift +98 -0
- package/app/Sources/Core/Input/MouseInputEventViewer.swift +272 -0
- package/app/Sources/Core/Input/MouseShortcutStore.swift +107 -0
- package/app/Sources/{CommandModeState.swift → Core/Overlays/CommandMode/CommandModeState.swift} +127 -24
- package/app/Sources/{CommandModeView.swift → Core/Overlays/CommandMode/CommandModeView.swift} +492 -79
- package/app/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +67 -0
- package/app/Sources/{CheatSheetHUD.swift → Core/Overlays/HUD/CheatSheetHUD.swift} +1 -0
- package/app/Sources/{HUDRightBar.swift → Core/Overlays/HUD/HUDRightBar.swift} +23 -201
- package/app/Sources/{LauncherHUD.swift → Core/Overlays/HUD/LauncherHUD.swift} +12 -26
- package/app/Sources/{OmniSearchView.swift → Core/Overlays/OmniSearch/OmniSearchView.swift} +136 -2
- package/app/Sources/{OmniSearchWindow.swift → Core/Overlays/OmniSearch/OmniSearchWindow.swift} +21 -32
- package/app/Sources/Core/Overlays/OverlayPanelShell.swift +241 -0
- package/app/Sources/{ScreenMapState.swift → Core/Overlays/ScreenMap/ScreenMapState.swift} +116 -32
- package/app/Sources/{ScreenMapView.swift → Core/Overlays/ScreenMap/ScreenMapView.swift} +510 -524
- package/app/Sources/{ScreenMapWindowController.swift → Core/Overlays/ScreenMap/ScreenMapWindowController.swift} +12 -4
- package/app/Sources/{VoiceCommandWindow.swift → Core/Overlays/Voice/VoiceCommandWindow.swift} +46 -53
- package/app/Sources/Core/Pi/PiAuthNextStepCard.swift +148 -0
- package/app/Sources/Core/Pi/PiAuthPromptCard.swift +90 -0
- package/app/Sources/{PiChatDock.swift → Core/Pi/PiChatDock.swift} +137 -74
- package/app/Sources/{PiChatSession.swift → Core/Pi/PiChatSession.swift} +608 -108
- package/app/Sources/Core/Pi/PiInstallCallout.swift +86 -0
- package/app/Sources/Core/Pi/PiProviderSetupCallout.swift +99 -0
- package/app/Sources/{PiWorkspaceView.swift → Core/Pi/PiWorkspaceView.swift} +174 -77
- package/app/Sources/{PermissionChecker.swift → Core/System/PermissionChecker.swift} +76 -2
- package/app/Sources/Core/System/SystemTelemetryMonitor.swift +273 -0
- package/app/Sources/{HandsOffSession.swift → Core/Voice/HandsOffSession.swift} +15 -4
- package/app/Sources/{WorkspaceManager.swift → Core/Workspace/WorkspaceManager.swift} +288 -0
- package/bin/assistant-intelligence.ts +874 -0
- package/bin/handsoff-infer.ts +16 -209
- package/bin/handsoff-worker.ts +45 -258
- package/bin/lattices-app.ts +62 -0
- package/bin/lattices-dev +4 -0
- package/bin/lattices.ts +125 -14
- package/docs/agents.md +14 -0
- package/docs/api.md +55 -0
- package/docs/app.md +3 -0
- package/docs/companion-deck.md +180 -0
- package/docs/component-extraction-roadmap.md +392 -0
- package/docs/config.md +25 -0
- package/docs/tiling-reference.md +55 -0
- package/docs/voice-error-model.md +73 -0
- package/package.json +4 -1
- package/app/Sources/App.swift +0 -10
- package/app/Sources/CommandPaletteWindow.swift +0 -134
- package/app/Sources/MouseFinder.swift +0 -222
- /package/app/Sources/{KeyRecorderView.swift → AppShell/KeyRecorderView.swift} +0 -0
- /package/app/Sources/{MainWindow.swift → AppShell/MainWindow.swift} +0 -0
- /package/app/Sources/{SettingsWindow.swift → AppShell/SettingsWindow.swift} +0 -0
- /package/app/Sources/{HotkeyManager.swift → Core/Actions/HotkeyManager.swift} +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/CreateLayerIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/DistributeIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/FocusIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/HelpIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/KillIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/LaunchIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/ListSessionsIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/ListWindowsIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/ScanIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/SearchIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/SwitchLayerIntent.swift +0 -0
- /package/app/Sources/{Intents → Core/Actions/Intents}/TileIntent.swift +0 -0
- /package/app/Sources/{DaemonProtocol.swift → Core/Daemon/DaemonProtocol.swift} +0 -0
- /package/app/Sources/{DaemonServer.swift → Core/Daemon/DaemonServer.swift} +0 -0
- /package/app/Sources/{AccessibilityTextExtractor.swift → Core/Desktop/AccessibilityTextExtractor.swift} +0 -0
- /package/app/Sources/{DesktopModelTypes.swift → Core/Desktop/DesktopModelTypes.swift} +0 -0
- /package/app/Sources/{InventoryManager.swift → Core/Desktop/InventoryManager.swift} +0 -0
- /package/app/Sources/{InventoryPath.swift → Core/Desktop/InventoryPath.swift} +0 -0
- /package/app/Sources/{OcrModel.swift → Core/Desktop/OcrModel.swift} +0 -0
- /package/app/Sources/{OcrStore.swift → Core/Desktop/OcrStore.swift} +0 -0
- /package/app/Sources/{PlacementSpec.swift → Core/Desktop/PlacementSpec.swift} +0 -0
- /package/app/Sources/{TilePickerView.swift → Core/Desktop/TilePickerView.swift} +0 -0
- /package/app/Sources/{AppWindowShell.swift → Core/Overlays/AppWindowShell.swift} +0 -0
- /package/app/Sources/{CommandModeWindow.swift → Core/Overlays/CommandMode/CommandModeWindow.swift} +0 -0
- /package/app/Sources/{CommandPaletteView.swift → Core/Overlays/CommandPalette/CommandPaletteView.swift} +0 -0
- /package/app/Sources/{HUDBottomBar.swift → Core/Overlays/HUD/HUDBottomBar.swift} +0 -0
- /package/app/Sources/{HUDController.swift → Core/Overlays/HUD/HUDController.swift} +0 -0
- /package/app/Sources/{HUDLeftBar.swift → Core/Overlays/HUD/HUDLeftBar.swift} +0 -0
- /package/app/Sources/{HUDMinimap.swift → Core/Overlays/HUD/HUDMinimap.swift} +0 -0
- /package/app/Sources/{HUDState.swift → Core/Overlays/HUD/HUDState.swift} +0 -0
- /package/app/Sources/{HUDTopBar.swift → Core/Overlays/HUD/HUDTopBar.swift} +0 -0
- /package/app/Sources/{LayerBezel.swift → Core/Overlays/HUD/LayerBezel.swift} +0 -0
- /package/app/Sources/{OmniSearchState.swift → Core/Overlays/OmniSearch/OmniSearchState.swift} +0 -0
- /package/app/Sources/{DiagnosticLog.swift → Core/System/DiagnosticLog.swift} +0 -0
- /package/app/Sources/{EventBus.swift → Core/System/EventBus.swift} +0 -0
- /package/app/Sources/{ProcessModel.swift → Core/System/ProcessModel.swift} +0 -0
- /package/app/Sources/{ProcessQuery.swift → Core/System/ProcessQuery.swift} +0 -0
- /package/app/Sources/{AdvisorLearningStore.swift → Core/Voice/AdvisorLearningStore.swift} +0 -0
- /package/app/Sources/{AgentSession.swift → Core/Voice/AgentSession.swift} +0 -0
- /package/app/Sources/{AudioProvider.swift → Core/Voice/AudioProvider.swift} +0 -0
- /package/app/Sources/{VoiceChatView.swift → Core/Voice/VoiceChatView.swift} +0 -0
- /package/app/Sources/{VoxClient.swift → Core/Voice/VoxClient.swift} +0 -0
- /package/app/Sources/{Project.swift → Core/Workspace/Project.swift} +0 -0
- /package/app/Sources/{ProjectScanner.swift → Core/Workspace/ProjectScanner.swift} +0 -0
- /package/app/Sources/{SessionLayerStore.swift → Core/Workspace/SessionLayerStore.swift} +0 -0
- /package/app/Sources/{SessionManager.swift → Core/Workspace/SessionManager.swift} +0 -0
- /package/app/Sources/{Terminal.swift → Core/Workspace/Terminal/Terminal.swift} +0 -0
- /package/app/Sources/{TerminalQuery.swift → Core/Workspace/Terminal/TerminalQuery.swift} +0 -0
- /package/app/Sources/{TerminalSynthesizer.swift → Core/Workspace/Terminal/TerminalSynthesizer.swift} +0 -0
- /package/app/Sources/{TmuxModel.swift → Core/Workspace/Tmux/TmuxModel.swift} +0 -0
- /package/app/Sources/{TmuxQuery.swift → Core/Workspace/Tmux/TmuxQuery.swift} +0 -0
- /package/app/Sources/{ActionRow.swift → UI/ActionRow.swift} +0 -0
- /package/app/Sources/{OrphanRow.swift → UI/OrphanRow.swift} +0 -0
- /package/app/Sources/{ProjectRow.swift → UI/ProjectRow.swift} +0 -0
- /package/app/Sources/{TabGroupRow.swift → UI/TabGroupRow.swift} +0 -0
- /package/app/Sources/{Theme.swift → UI/Theme.swift} +0 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
enum MouseGestureDirection: String, CaseIterable, Codable, Equatable {
|
|
5
|
+
case left
|
|
6
|
+
case right
|
|
7
|
+
case up
|
|
8
|
+
case down
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
enum MouseShortcutTriggerKind: String, Codable, Equatable {
|
|
12
|
+
case drag
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
enum MouseShortcutActionType: String, Codable, Equatable {
|
|
16
|
+
case spaceNext = "space.next"
|
|
17
|
+
case spacePrevious = "space.previous"
|
|
18
|
+
case screenMapToggle = "screenmap.toggle"
|
|
19
|
+
case dictationStart = "dictation.start"
|
|
20
|
+
case shortcutSend = "shortcut.send"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
enum MouseShortcutModifier: String, CaseIterable, Codable, Equatable {
|
|
24
|
+
case command
|
|
25
|
+
case control
|
|
26
|
+
case option
|
|
27
|
+
case shift
|
|
28
|
+
|
|
29
|
+
var appleScriptToken: String {
|
|
30
|
+
switch self {
|
|
31
|
+
case .command: return "command down"
|
|
32
|
+
case .control: return "control down"
|
|
33
|
+
case .option: return "option down"
|
|
34
|
+
case .shift: return "shift down"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var displayLabel: String {
|
|
39
|
+
switch self {
|
|
40
|
+
case .command: return "Cmd"
|
|
41
|
+
case .control: return "Ctrl"
|
|
42
|
+
case .option: return "Option"
|
|
43
|
+
case .shift: return "Shift"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
enum MouseShortcutButton: Hashable, Codable, Equatable {
|
|
49
|
+
case middle
|
|
50
|
+
case button4
|
|
51
|
+
case button5
|
|
52
|
+
case number(Int)
|
|
53
|
+
|
|
54
|
+
init(rawButtonNumber: Int) {
|
|
55
|
+
switch rawButtonNumber {
|
|
56
|
+
case 2: self = .middle
|
|
57
|
+
case 3: self = .button4
|
|
58
|
+
case 4: self = .button5
|
|
59
|
+
default: self = .number(rawButtonNumber)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
init(from decoder: Decoder) throws {
|
|
64
|
+
let container = try decoder.singleValueContainer()
|
|
65
|
+
if let intValue = try? container.decode(Int.self) {
|
|
66
|
+
self = MouseShortcutButton(rawButtonNumber: intValue)
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let stringValue = try container.decode(String.self)
|
|
71
|
+
switch stringValue.lowercased() {
|
|
72
|
+
case "middle", "button.middle":
|
|
73
|
+
self = .middle
|
|
74
|
+
case "button4", "button.button4":
|
|
75
|
+
self = .button4
|
|
76
|
+
case "button5", "button.button5":
|
|
77
|
+
self = .button5
|
|
78
|
+
default:
|
|
79
|
+
if let raw = Int(stringValue.filter(\.isNumber)) {
|
|
80
|
+
self = MouseShortcutButton(rawButtonNumber: raw)
|
|
81
|
+
} else {
|
|
82
|
+
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported mouse button '\(stringValue)'")
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
func encode(to encoder: Encoder) throws {
|
|
88
|
+
var container = encoder.singleValueContainer()
|
|
89
|
+
try container.encode(configValue)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
var rawButtonNumber: Int {
|
|
93
|
+
switch self {
|
|
94
|
+
case .middle: return 2
|
|
95
|
+
case .button4: return 3
|
|
96
|
+
case .button5: return 4
|
|
97
|
+
case .number(let value): return value
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
var configValue: String {
|
|
102
|
+
switch self {
|
|
103
|
+
case .middle: return "middle"
|
|
104
|
+
case .button4: return "button4"
|
|
105
|
+
case .button5: return "button5"
|
|
106
|
+
case .number(let value): return "button\(value)"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
var displayLabel: String {
|
|
111
|
+
switch self {
|
|
112
|
+
case .middle: return "Middle Click"
|
|
113
|
+
case .button4: return "Button 4"
|
|
114
|
+
case .button5: return "Button 5"
|
|
115
|
+
case .number(let value): return "Button \(value)"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
var triggerToken: String {
|
|
120
|
+
switch self {
|
|
121
|
+
case .middle: return "button.middle"
|
|
122
|
+
case .button4: return "button.button4"
|
|
123
|
+
case .button5: return "button.button5"
|
|
124
|
+
case .number(let value): return "button.\(value)"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
struct MouseShortcutDeviceSelector: Codable, Equatable {
|
|
130
|
+
private enum CodingKeys: String, CodingKey {
|
|
131
|
+
case match
|
|
132
|
+
case vendorId
|
|
133
|
+
case productId
|
|
134
|
+
case locationId
|
|
135
|
+
case product
|
|
136
|
+
case manufacturer
|
|
137
|
+
case transport
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
var match: String?
|
|
141
|
+
var vendorId: Int?
|
|
142
|
+
var productId: Int?
|
|
143
|
+
var locationId: Int?
|
|
144
|
+
var product: String?
|
|
145
|
+
var manufacturer: String?
|
|
146
|
+
var transport: String?
|
|
147
|
+
|
|
148
|
+
static let any = MouseShortcutDeviceSelector(match: "any")
|
|
149
|
+
|
|
150
|
+
init(
|
|
151
|
+
match: String? = "any",
|
|
152
|
+
vendorId: Int? = nil,
|
|
153
|
+
productId: Int? = nil,
|
|
154
|
+
locationId: Int? = nil,
|
|
155
|
+
product: String? = nil,
|
|
156
|
+
manufacturer: String? = nil,
|
|
157
|
+
transport: String? = nil
|
|
158
|
+
) {
|
|
159
|
+
self.match = match
|
|
160
|
+
self.vendorId = vendorId
|
|
161
|
+
self.productId = productId
|
|
162
|
+
self.locationId = locationId
|
|
163
|
+
self.product = product
|
|
164
|
+
self.manufacturer = manufacturer
|
|
165
|
+
self.transport = transport
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
init(from decoder: Decoder) throws {
|
|
169
|
+
let container = try decoder.singleValueContainer()
|
|
170
|
+
if let stringValue = try? container.decode(String.self) {
|
|
171
|
+
self = MouseShortcutDeviceSelector(match: stringValue)
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let object = try decoder.container(keyedBy: CodingKeys.self)
|
|
176
|
+
match = try object.decodeIfPresent(String.self, forKey: .match)
|
|
177
|
+
vendorId = try object.decodeIfPresent(Int.self, forKey: .vendorId)
|
|
178
|
+
productId = try object.decodeIfPresent(Int.self, forKey: .productId)
|
|
179
|
+
locationId = try object.decodeIfPresent(Int.self, forKey: .locationId)
|
|
180
|
+
product = try object.decodeIfPresent(String.self, forKey: .product)
|
|
181
|
+
manufacturer = try object.decodeIfPresent(String.self, forKey: .manufacturer)
|
|
182
|
+
transport = try object.decodeIfPresent(String.self, forKey: .transport)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
func encode(to encoder: Encoder) throws {
|
|
186
|
+
if isAny {
|
|
187
|
+
var container = encoder.singleValueContainer()
|
|
188
|
+
try container.encode("any")
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
193
|
+
try container.encodeIfPresent(match, forKey: .match)
|
|
194
|
+
try container.encodeIfPresent(vendorId, forKey: .vendorId)
|
|
195
|
+
try container.encodeIfPresent(productId, forKey: .productId)
|
|
196
|
+
try container.encodeIfPresent(locationId, forKey: .locationId)
|
|
197
|
+
try container.encodeIfPresent(product, forKey: .product)
|
|
198
|
+
try container.encodeIfPresent(manufacturer, forKey: .manufacturer)
|
|
199
|
+
try container.encodeIfPresent(transport, forKey: .transport)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
var isAny: Bool {
|
|
203
|
+
let normalized = (match ?? "any").lowercased()
|
|
204
|
+
return normalized == "any"
|
|
205
|
+
&& vendorId == nil
|
|
206
|
+
&& productId == nil
|
|
207
|
+
&& locationId == nil
|
|
208
|
+
&& product == nil
|
|
209
|
+
&& manufacturer == nil
|
|
210
|
+
&& transport == nil
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
func matches(_ device: MouseInputDeviceInfo?) -> Bool {
|
|
214
|
+
if isAny { return true }
|
|
215
|
+
guard let device else { return false }
|
|
216
|
+
if let vendorId, vendorId != device.vendorId { return false }
|
|
217
|
+
if let productId, productId != device.productId { return false }
|
|
218
|
+
if let locationId, locationId != device.locationId { return false }
|
|
219
|
+
if let product, device.product?.localizedCaseInsensitiveContains(product) != true { return false }
|
|
220
|
+
if let manufacturer, device.manufacturer?.localizedCaseInsensitiveContains(manufacturer) != true { return false }
|
|
221
|
+
if let transport, device.transport?.localizedCaseInsensitiveContains(transport) != true { return false }
|
|
222
|
+
return true
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
struct MouseShortcutTrigger: Codable, Equatable {
|
|
227
|
+
var button: MouseShortcutButton
|
|
228
|
+
var kind: MouseShortcutTriggerKind
|
|
229
|
+
var direction: MouseGestureDirection
|
|
230
|
+
|
|
231
|
+
var triggerName: String {
|
|
232
|
+
"\(button.triggerToken).\(kind.rawValue).\(direction.rawValue)"
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
struct MouseShortcutKeyStroke: Codable, Equatable {
|
|
237
|
+
var key: String?
|
|
238
|
+
var keyCode: Int?
|
|
239
|
+
var modifiers: [MouseShortcutModifier]
|
|
240
|
+
|
|
241
|
+
var displayLabel: String {
|
|
242
|
+
let keyLabel = key?.uppercased() ?? "KeyCode \(keyCode ?? -1)"
|
|
243
|
+
let parts = modifiers.map(\.displayLabel) + [keyLabel]
|
|
244
|
+
return parts.joined(separator: "+")
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
struct MouseShortcutActionDefinition: Codable, Equatable {
|
|
249
|
+
var type: MouseShortcutActionType
|
|
250
|
+
var shortcut: MouseShortcutKeyStroke?
|
|
251
|
+
|
|
252
|
+
var label: String {
|
|
253
|
+
switch type {
|
|
254
|
+
case .spaceNext:
|
|
255
|
+
return "Next Space"
|
|
256
|
+
case .spacePrevious:
|
|
257
|
+
return "Previous Space"
|
|
258
|
+
case .screenMapToggle:
|
|
259
|
+
return "Screen Map Overview"
|
|
260
|
+
case .dictationStart:
|
|
261
|
+
return "Dictation"
|
|
262
|
+
case .shortcutSend:
|
|
263
|
+
return shortcut?.displayLabel ?? "Send Shortcut"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
struct MouseShortcutRule: Codable, Equatable, Identifiable {
|
|
269
|
+
var id: String
|
|
270
|
+
var enabled: Bool
|
|
271
|
+
var device: MouseShortcutDeviceSelector
|
|
272
|
+
var trigger: MouseShortcutTrigger
|
|
273
|
+
var action: MouseShortcutActionDefinition
|
|
274
|
+
|
|
275
|
+
var summary: String {
|
|
276
|
+
"\(trigger.triggerName) -> \(action.type.rawValue)"
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
struct MouseShortcutTuning: Codable, Equatable {
|
|
281
|
+
var dragThreshold: CGFloat
|
|
282
|
+
var holdTolerance: CGFloat
|
|
283
|
+
var axisBias: CGFloat
|
|
284
|
+
|
|
285
|
+
static let defaults = MouseShortcutTuning(
|
|
286
|
+
dragThreshold: 68,
|
|
287
|
+
holdTolerance: 10,
|
|
288
|
+
axisBias: 1.2
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
struct MouseShortcutConfig: Codable, Equatable {
|
|
293
|
+
var version: Int
|
|
294
|
+
var tuning: MouseShortcutTuning
|
|
295
|
+
var rules: [MouseShortcutRule]
|
|
296
|
+
|
|
297
|
+
static let defaults = MouseShortcutConfig(
|
|
298
|
+
version: 1,
|
|
299
|
+
tuning: .defaults,
|
|
300
|
+
rules: [
|
|
301
|
+
MouseShortcutRule(
|
|
302
|
+
id: "space-previous",
|
|
303
|
+
enabled: true,
|
|
304
|
+
device: .any,
|
|
305
|
+
trigger: MouseShortcutTrigger(button: .middle, kind: .drag, direction: .left),
|
|
306
|
+
action: MouseShortcutActionDefinition(type: .spacePrevious, shortcut: nil)
|
|
307
|
+
),
|
|
308
|
+
MouseShortcutRule(
|
|
309
|
+
id: "space-next",
|
|
310
|
+
enabled: true,
|
|
311
|
+
device: .any,
|
|
312
|
+
trigger: MouseShortcutTrigger(button: .middle, kind: .drag, direction: .right),
|
|
313
|
+
action: MouseShortcutActionDefinition(type: .spaceNext, shortcut: nil)
|
|
314
|
+
),
|
|
315
|
+
MouseShortcutRule(
|
|
316
|
+
id: "screenmap-overview",
|
|
317
|
+
enabled: true,
|
|
318
|
+
device: .any,
|
|
319
|
+
trigger: MouseShortcutTrigger(button: .middle, kind: .drag, direction: .down),
|
|
320
|
+
action: MouseShortcutActionDefinition(type: .screenMapToggle, shortcut: nil)
|
|
321
|
+
),
|
|
322
|
+
MouseShortcutRule(
|
|
323
|
+
id: "dictation",
|
|
324
|
+
enabled: true,
|
|
325
|
+
device: .any,
|
|
326
|
+
trigger: MouseShortcutTrigger(button: .middle, kind: .drag, direction: .up),
|
|
327
|
+
action: MouseShortcutActionDefinition(type: .dictationStart, shortcut: nil)
|
|
328
|
+
),
|
|
329
|
+
]
|
|
330
|
+
)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
struct MouseShortcutMatchResult {
|
|
334
|
+
let rule: MouseShortcutRule
|
|
335
|
+
let action: MouseShortcutActionDefinition
|
|
336
|
+
let triggerName: String
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
struct MouseShortcutTriggerEvent {
|
|
340
|
+
let button: MouseShortcutButton
|
|
341
|
+
let kind: MouseShortcutTriggerKind
|
|
342
|
+
let direction: MouseGestureDirection
|
|
343
|
+
let device: MouseInputDeviceInfo?
|
|
344
|
+
|
|
345
|
+
var triggerName: String {
|
|
346
|
+
MouseShortcutTrigger(button: button, kind: kind, direction: direction).triggerName
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
struct MouseShortcutObservedEvent {
|
|
351
|
+
let timestamp: Date
|
|
352
|
+
let phase: String
|
|
353
|
+
let buttonNumber: Int
|
|
354
|
+
let location: CGPoint
|
|
355
|
+
let delta: CGPoint
|
|
356
|
+
let modifiers: NSEvent.ModifierFlags
|
|
357
|
+
let frontmostAppName: String?
|
|
358
|
+
let frontmostBundleId: String?
|
|
359
|
+
let candidateTrigger: String?
|
|
360
|
+
let device: MouseInputDeviceInfo?
|
|
361
|
+
let matchedRuleSummary: String?
|
|
362
|
+
let willFire: Bool
|
|
363
|
+
let note: String?
|
|
364
|
+
}
|