@lattices/cli 0.4.14 → 0.5.0
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 +5 -7
- package/apps/mac/Info.plist +2 -2
- package/apps/mac/Lattices.app/Contents/Info.plist +4 -12
- package/apps/mac/Lattices.app/Contents/MacOS/Lattices +0 -0
- package/bin/lattices-app.ts +110 -17
- package/bin/lattices-build +125 -0
- package/bin/lattices-dev +89 -16
- package/bin/lattices.ts +977 -16
- package/docs/agents.md +81 -4
- package/docs/ai-chat-ux-review.md +416 -0
- package/docs/api.md +135 -3
- package/docs/app.md +30 -8
- package/docs/config.md +4 -0
- package/docs/mouse-gestures.md +60 -1
- package/docs/proposals/LAT-004-interactive-overlay-actors.md +1 -1
- package/docs/proposals/LAT-005-action-runtime-product-spine.md +914 -0
- package/docs/proposals/LAT-006-mira-in-lattices.md +553 -0
- package/docs/reference/dewey.config.ts +2 -2
- package/docs/release.md +171 -0
- package/docs/repo-structure.md +4 -5
- package/docs/voice.md +11 -27
- package/package.json +9 -10
- package/apps/mac/Package.swift +0 -27
- package/apps/mac/Sources/AppShell/App.swift +0 -26
- package/apps/mac/Sources/AppShell/AppActivationCoordinator.swift +0 -27
- package/apps/mac/Sources/AppShell/AppDelegate.swift +0 -189
- package/apps/mac/Sources/AppShell/AppServicesBootstrap.swift +0 -25
- package/apps/mac/Sources/AppShell/AppShellView.swift +0 -171
- package/apps/mac/Sources/AppShell/AppUpdater.swift +0 -305
- package/apps/mac/Sources/AppShell/CliActionLauncher.swift +0 -50
- package/apps/mac/Sources/AppShell/HomeDashboardView.swift +0 -133
- package/apps/mac/Sources/AppShell/HotkeyBootstrap.swift +0 -87
- package/apps/mac/Sources/AppShell/KeyRecorderView.swift +0 -210
- package/apps/mac/Sources/AppShell/LatticesRuntime.swift +0 -104
- package/apps/mac/Sources/AppShell/MainView.swift +0 -847
- package/apps/mac/Sources/AppShell/MainWindow.swift +0 -83
- package/apps/mac/Sources/AppShell/MenuBarController.swift +0 -177
- package/apps/mac/Sources/AppShell/OnboardingView.swift +0 -483
- package/apps/mac/Sources/AppShell/PermissionsAssistantView.swift +0 -366
- package/apps/mac/Sources/AppShell/PermissionsAssistantWindow.swift +0 -70
- package/apps/mac/Sources/AppShell/Preferences.swift +0 -297
- package/apps/mac/Sources/AppShell/SettingsView.swift +0 -3163
- package/apps/mac/Sources/AppShell/SettingsWindow.swift +0 -34
- package/apps/mac/Sources/AppShell/WorkspaceInspectorPresenter.swift +0 -13
- package/apps/mac/Sources/Core/Actions/HotkeyManager.swift +0 -256
- package/apps/mac/Sources/Core/Actions/HotkeyStore.swift +0 -399
- package/apps/mac/Sources/Core/Actions/IntentEngine.swift +0 -988
- package/apps/mac/Sources/Core/Actions/IntentSchema.swift +0 -94
- package/apps/mac/Sources/Core/Actions/Intents/CreateLayerIntent.swift +0 -54
- package/apps/mac/Sources/Core/Actions/Intents/DistributeIntent.swift +0 -56
- package/apps/mac/Sources/Core/Actions/Intents/FocusIntent.swift +0 -69
- package/apps/mac/Sources/Core/Actions/Intents/HelpIntent.swift +0 -41
- package/apps/mac/Sources/Core/Actions/Intents/KillIntent.swift +0 -47
- package/apps/mac/Sources/Core/Actions/Intents/LatticeIntent.swift +0 -53
- package/apps/mac/Sources/Core/Actions/Intents/LaunchIntent.swift +0 -67
- package/apps/mac/Sources/Core/Actions/Intents/ListSessionsIntent.swift +0 -32
- package/apps/mac/Sources/Core/Actions/Intents/ListWindowsIntent.swift +0 -30
- package/apps/mac/Sources/Core/Actions/Intents/ScanIntent.swift +0 -52
- package/apps/mac/Sources/Core/Actions/Intents/SearchIntent.swift +0 -190
- package/apps/mac/Sources/Core/Actions/Intents/SwitchLayerIntent.swift +0 -50
- package/apps/mac/Sources/Core/Actions/Intents/TileIntent.swift +0 -61
- package/apps/mac/Sources/Core/Actions/PaletteCommand.swift +0 -439
- package/apps/mac/Sources/Core/Actions/VoiceIntentResolver.swift +0 -713
- package/apps/mac/Sources/Core/Companion/CompanionActivityLog.swift +0 -70
- package/apps/mac/Sources/Core/Companion/CompanionKeyboardController.swift +0 -141
- package/apps/mac/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +0 -454
- package/apps/mac/Sources/Core/Companion/LatticesCompanionCockpit.swift +0 -555
- package/apps/mac/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +0 -629
- package/apps/mac/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +0 -204
- package/apps/mac/Sources/Core/Companion/LatticesDeckHost.swift +0 -1463
- package/apps/mac/Sources/Core/Daemon/DaemonProtocol.swift +0 -114
- package/apps/mac/Sources/Core/Daemon/DaemonServer.swift +0 -427
- package/apps/mac/Sources/Core/Daemon/LatticesApi.swift +0 -2965
- package/apps/mac/Sources/Core/Desktop/AccessibilityTextExtractor.swift +0 -111
- package/apps/mac/Sources/Core/Desktop/AppTypeClassifier.swift +0 -106
- package/apps/mac/Sources/Core/Desktop/DesktopModel.swift +0 -331
- package/apps/mac/Sources/Core/Desktop/DesktopModelTypes.swift +0 -73
- package/apps/mac/Sources/Core/Desktop/InventoryManager.swift +0 -35
- package/apps/mac/Sources/Core/Desktop/InventoryPath.swift +0 -43
- package/apps/mac/Sources/Core/Desktop/MouseFinder.swift +0 -527
- package/apps/mac/Sources/Core/Desktop/OcrModel.swift +0 -467
- package/apps/mac/Sources/Core/Desktop/OcrStore.swift +0 -329
- package/apps/mac/Sources/Core/Desktop/PlacementSpec.swift +0 -195
- package/apps/mac/Sources/Core/Desktop/SessionWindowLocator.swift +0 -139
- package/apps/mac/Sources/Core/Desktop/TilePickerView.swift +0 -209
- package/apps/mac/Sources/Core/Desktop/WindowCapture.swift +0 -33
- package/apps/mac/Sources/Core/Desktop/WindowDragSnapController.swift +0 -429
- package/apps/mac/Sources/Core/Desktop/WindowPreviewCard.swift +0 -100
- package/apps/mac/Sources/Core/Desktop/WindowPreviewStore.swift +0 -112
- package/apps/mac/Sources/Core/Desktop/WindowSelectionStore.swift +0 -76
- package/apps/mac/Sources/Core/Desktop/WindowTiler.swift +0 -2222
- package/apps/mac/Sources/Core/Input/EventTapBreaker.swift +0 -124
- package/apps/mac/Sources/Core/Input/EventTapThread.swift +0 -54
- package/apps/mac/Sources/Core/Input/InputCaptureResetCenter.swift +0 -20
- package/apps/mac/Sources/Core/Input/KeyboardRemapConfig.swift +0 -69
- package/apps/mac/Sources/Core/Input/KeyboardRemapController.swift +0 -346
- package/apps/mac/Sources/Core/Input/KeyboardRemapStore.swift +0 -141
- package/apps/mac/Sources/Core/Input/MouseGestureConfig.swift +0 -499
- package/apps/mac/Sources/Core/Input/MouseGestureController.swift +0 -2583
- package/apps/mac/Sources/Core/Input/MouseInputDeviceStore.swift +0 -98
- package/apps/mac/Sources/Core/Input/MouseInputEventViewer.swift +0 -272
- package/apps/mac/Sources/Core/Input/MouseShortcutStore.swift +0 -170
- package/apps/mac/Sources/Core/Input/SecureEventInputMonitor.swift +0 -39
- package/apps/mac/Sources/Core/Input/ShapeRecognizer.swift +0 -624
- package/apps/mac/Sources/Core/Input/TapBudgetMeter.swift +0 -56
- package/apps/mac/Sources/Core/Overlays/AppWindowShell.swift +0 -63
- package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeState.swift +0 -1566
- package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeView.swift +0 -1927
- package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeWindow.swift +0 -196
- package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteView.swift +0 -307
- package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +0 -67
- package/apps/mac/Sources/Core/Overlays/HUD/CheatSheetHUD.swift +0 -576
- package/apps/mac/Sources/Core/Overlays/HUD/HUDBottomBar.swift +0 -279
- package/apps/mac/Sources/Core/Overlays/HUD/HUDController.swift +0 -1158
- package/apps/mac/Sources/Core/Overlays/HUD/HUDLeftBar.swift +0 -849
- package/apps/mac/Sources/Core/Overlays/HUD/HUDMinimap.swift +0 -179
- package/apps/mac/Sources/Core/Overlays/HUD/HUDRightBar.swift +0 -596
- package/apps/mac/Sources/Core/Overlays/HUD/HUDState.swift +0 -367
- package/apps/mac/Sources/Core/Overlays/HUD/HUDTopBar.swift +0 -243
- package/apps/mac/Sources/Core/Overlays/HUD/LauncherHUD.swift +0 -334
- package/apps/mac/Sources/Core/Overlays/HUD/LayerBezel.swift +0 -203
- package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchState.swift +0 -280
- package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchView.swift +0 -422
- package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchWindow.swift +0 -94
- package/apps/mac/Sources/Core/Overlays/OverlayPanelShell.swift +0 -241
- package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapState.swift +0 -3135
- package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapView.swift +0 -3977
- package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapWindowController.swift +0 -119
- package/apps/mac/Sources/Core/Overlays/ScreenOverlayCanvasController.swift +0 -1217
- package/apps/mac/Sources/Core/Overlays/Voice/VoiceCommandWindow.swift +0 -1575
- package/apps/mac/Sources/Core/Pi/PiAuthNextStepCard.swift +0 -148
- package/apps/mac/Sources/Core/Pi/PiAuthPromptCard.swift +0 -90
- package/apps/mac/Sources/Core/Pi/PiChatDock.swift +0 -564
- package/apps/mac/Sources/Core/Pi/PiChatSession.swift +0 -1948
- package/apps/mac/Sources/Core/Pi/PiInstallCallout.swift +0 -86
- package/apps/mac/Sources/Core/Pi/PiProviderSetupCallout.swift +0 -99
- package/apps/mac/Sources/Core/Pi/PiWorkspaceView.swift +0 -510
- package/apps/mac/Sources/Core/System/Capability.swift +0 -79
- package/apps/mac/Sources/Core/System/DiagnosticLog.swift +0 -373
- package/apps/mac/Sources/Core/System/EventBus.swift +0 -31
- package/apps/mac/Sources/Core/System/PermissionChecker.swift +0 -224
- package/apps/mac/Sources/Core/System/ProcessModel.swift +0 -199
- package/apps/mac/Sources/Core/System/ProcessQuery.swift +0 -151
- package/apps/mac/Sources/Core/System/SystemTelemetryMonitor.swift +0 -273
- package/apps/mac/Sources/Core/Voice/AdvisorLearningStore.swift +0 -90
- package/apps/mac/Sources/Core/Voice/AgentSession.swift +0 -377
- package/apps/mac/Sources/Core/Voice/AudioProvider.swift +0 -555
- package/apps/mac/Sources/Core/Voice/HandsOffSession.swift +0 -839
- package/apps/mac/Sources/Core/Voice/VoiceChatView.swift +0 -192
- package/apps/mac/Sources/Core/Voice/VoxClient.swift +0 -454
- package/apps/mac/Sources/Core/Workspace/Project.swift +0 -28
- package/apps/mac/Sources/Core/Workspace/ProjectScanner.swift +0 -141
- package/apps/mac/Sources/Core/Workspace/SessionLayerStore.swift +0 -285
- package/apps/mac/Sources/Core/Workspace/SessionManager.swift +0 -75
- package/apps/mac/Sources/Core/Workspace/Terminal/Terminal.swift +0 -259
- package/apps/mac/Sources/Core/Workspace/Terminal/TerminalQuery.swift +0 -156
- package/apps/mac/Sources/Core/Workspace/Terminal/TerminalSynthesizer.swift +0 -200
- package/apps/mac/Sources/Core/Workspace/Tmux/TmuxModel.swift +0 -60
- package/apps/mac/Sources/Core/Workspace/Tmux/TmuxQuery.swift +0 -105
- package/apps/mac/Sources/Core/Workspace/WorkspaceManager.swift +0 -1027
- package/apps/mac/Sources/UI/ActionRow.swift +0 -78
- package/apps/mac/Sources/UI/OrphanRow.swift +0 -129
- package/apps/mac/Sources/UI/ProjectRow.swift +0 -368
- package/apps/mac/Sources/UI/TabGroupRow.swift +0 -178
- package/apps/mac/Sources/UI/Theme.swift +0 -164
- package/apps/mac/Tests/StageDragTests.swift +0 -333
- package/apps/mac/Tests/StageJoinTests.swift +0 -313
- package/apps/mac/Tests/StageManagerTests.swift +0 -280
- package/apps/mac/Tests/StageTileTests.swift +0 -353
- package/swift/Package.swift +0 -20
- package/swift/Sources/DeckKit/DeckAction.swift +0 -51
- package/swift/Sources/DeckKit/DeckBridgeSecurity.swift +0 -152
- package/swift/Sources/DeckKit/DeckCockpit.swift +0 -82
- package/swift/Sources/DeckKit/DeckHost.swift +0 -7
- package/swift/Sources/DeckKit/DeckManifest.swift +0 -145
- package/swift/Sources/DeckKit/DeckRuntimeSnapshot.swift +0 -533
- package/swift/Sources/DeckKit/DeckTrackpad.swift +0 -63
- package/swift/Sources/DeckKit/DeckValue.swift +0 -93
- package/swift/Sources/DeckKit/DeckVoiceError.swift +0 -88
- package/swift/Tests/DeckKitTests/DeckKitTests.swift +0 -286
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import AppKit
|
|
2
|
-
import Combine
|
|
3
|
-
import Foundation
|
|
4
|
-
|
|
5
|
-
final class KeyboardRemapStore: ObservableObject {
|
|
6
|
-
static let shared = KeyboardRemapStore()
|
|
7
|
-
|
|
8
|
-
@Published private(set) var config: KeyboardRemapConfig
|
|
9
|
-
|
|
10
|
-
let configURL: URL
|
|
11
|
-
/// Lock-protected mirror of `config` for tap-thread reads. The keyboard
|
|
12
|
-
/// event tap runs on EventTapThread and must not read the @Published
|
|
13
|
-
/// SwiftUI-facing config directly while main may be mutating it.
|
|
14
|
-
private let stateLock = NSLock()
|
|
15
|
-
private var snapshot: KeyboardRemapConfig
|
|
16
|
-
private var lastLoadedModifiedDate: Date?
|
|
17
|
-
private var lastReloadCheckAt: Date = .distantPast
|
|
18
|
-
private var reloadCheckInFlight = false
|
|
19
|
-
private let reloadCheckInterval: TimeInterval = 1.0
|
|
20
|
-
|
|
21
|
-
private init() {
|
|
22
|
-
let dir = FileManager.default.homeDirectoryForCurrentUser
|
|
23
|
-
.appendingPathComponent(".lattices")
|
|
24
|
-
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
|
|
25
|
-
self.configURL = dir.appendingPathComponent("keyboard-remaps.json")
|
|
26
|
-
self.config = .defaults
|
|
27
|
-
self.snapshot = .defaults
|
|
28
|
-
ensureConfigFile()
|
|
29
|
-
reload()
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
var enabledRules: [KeyboardRemapRule] {
|
|
33
|
-
stateLock.lock(); defer { stateLock.unlock() }
|
|
34
|
-
return snapshot.rules.filter(\.enabled)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
var summaryLines: [String] {
|
|
38
|
-
enabledRules.map(\.summaryLine)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
var capsLockRule: KeyboardRemapRule? {
|
|
42
|
-
enabledRules.first { $0.from == .capsLock }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
func ensureConfigFile() {
|
|
46
|
-
guard !FileManager.default.fileExists(atPath: configURL.path) else { return }
|
|
47
|
-
write(config: .defaults)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
func reload() {
|
|
51
|
-
// @Published mutation must run on main; hop if called off-main.
|
|
52
|
-
if !Thread.isMainThread {
|
|
53
|
-
DispatchQueue.main.async { [weak self] in self?.reload() }
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
let newDate = modifiedDate()
|
|
57
|
-
let newConfig: KeyboardRemapConfig
|
|
58
|
-
guard let data = FileManager.default.contents(atPath: configURL.path) else {
|
|
59
|
-
newConfig = .defaults
|
|
60
|
-
stateLock.lock()
|
|
61
|
-
snapshot = newConfig
|
|
62
|
-
lastLoadedModifiedDate = newDate
|
|
63
|
-
stateLock.unlock()
|
|
64
|
-
config = newConfig
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
do {
|
|
69
|
-
newConfig = try JSONDecoder().decode(KeyboardRemapConfig.self, from: data)
|
|
70
|
-
} catch {
|
|
71
|
-
DiagnosticLog.shared.error("KeyboardRemapStore: failed to decode keyboard-remaps.json - \(error.localizedDescription)")
|
|
72
|
-
newConfig = .defaults
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
stateLock.lock()
|
|
76
|
-
snapshot = newConfig
|
|
77
|
-
lastLoadedModifiedDate = newDate
|
|
78
|
-
stateLock.unlock()
|
|
79
|
-
config = newConfig
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
func scheduleReloadCheckIfNeeded() {
|
|
83
|
-
// Called from the keyboard event-tap thread. Keep this path to memory
|
|
84
|
-
// bookkeeping only; filesystem work runs off the tap callback.
|
|
85
|
-
let now = Date()
|
|
86
|
-
stateLock.lock()
|
|
87
|
-
guard !reloadCheckInFlight,
|
|
88
|
-
now.timeIntervalSince(lastReloadCheckAt) >= reloadCheckInterval else {
|
|
89
|
-
stateLock.unlock()
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
reloadCheckInFlight = true
|
|
93
|
-
lastReloadCheckAt = now
|
|
94
|
-
stateLock.unlock()
|
|
95
|
-
|
|
96
|
-
DispatchQueue.global(qos: .utility).async { [weak self] in
|
|
97
|
-
self?.reloadIfNeeded()
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private func reloadIfNeeded() {
|
|
102
|
-
let currentModifiedDate = modifiedDate()
|
|
103
|
-
stateLock.lock()
|
|
104
|
-
let needsReload = currentModifiedDate != lastLoadedModifiedDate
|
|
105
|
-
if needsReload {
|
|
106
|
-
lastLoadedModifiedDate = currentModifiedDate
|
|
107
|
-
}
|
|
108
|
-
reloadCheckInFlight = false
|
|
109
|
-
stateLock.unlock()
|
|
110
|
-
guard needsReload else { return }
|
|
111
|
-
reload()
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
func restoreDefaults() {
|
|
115
|
-
write(config: .defaults)
|
|
116
|
-
reload()
|
|
117
|
-
DiagnosticLog.shared.info("Keyboard remaps restored to defaults")
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
func openConfiguration() {
|
|
121
|
-
ensureConfigFile()
|
|
122
|
-
NSWorkspace.shared.open(configURL)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private func write(config: KeyboardRemapConfig) {
|
|
126
|
-
let encoder = JSONEncoder()
|
|
127
|
-
encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
|
|
128
|
-
guard let data = try? encoder.encode(config) else { return }
|
|
129
|
-
try? data.write(to: configURL, options: .atomic)
|
|
130
|
-
let newDate = modifiedDate()
|
|
131
|
-
stateLock.lock()
|
|
132
|
-
snapshot = config
|
|
133
|
-
lastLoadedModifiedDate = newDate
|
|
134
|
-
stateLock.unlock()
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
private func modifiedDate() -> Date? {
|
|
138
|
-
let attrs = try? FileManager.default.attributesOfItem(atPath: configURL.path)
|
|
139
|
-
return attrs?[.modificationDate] as? Date
|
|
140
|
-
}
|
|
141
|
-
}
|
|
@@ -1,499 +0,0 @@
|
|
|
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
|
-
var displayLabel: String {
|
|
11
|
-
switch self {
|
|
12
|
-
case .left: return "Left"
|
|
13
|
-
case .right: return "Right"
|
|
14
|
-
case .up: return "Up"
|
|
15
|
-
case .down: return "Down"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
enum MouseShortcutTriggerKind: String, Codable, Equatable {
|
|
21
|
-
case drag
|
|
22
|
-
case click
|
|
23
|
-
case shape
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
enum MouseShortcutActionType: String, Codable, Equatable {
|
|
27
|
-
case spaceNext = "space.next"
|
|
28
|
-
case spacePrevious = "space.previous"
|
|
29
|
-
case screenMapToggle = "screenmap.toggle"
|
|
30
|
-
case dictationStart = "dictation.start"
|
|
31
|
-
case shortcutSend = "shortcut.send"
|
|
32
|
-
case appActivate = "app.activate"
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
enum MouseShortcutModifier: String, CaseIterable, Codable, Equatable {
|
|
36
|
-
case command
|
|
37
|
-
case control
|
|
38
|
-
case option
|
|
39
|
-
case shift
|
|
40
|
-
|
|
41
|
-
var appleScriptToken: String {
|
|
42
|
-
switch self {
|
|
43
|
-
case .command: return "command down"
|
|
44
|
-
case .control: return "control down"
|
|
45
|
-
case .option: return "option down"
|
|
46
|
-
case .shift: return "shift down"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
var displayLabel: String {
|
|
51
|
-
switch self {
|
|
52
|
-
case .command: return "Cmd"
|
|
53
|
-
case .control: return "Ctrl"
|
|
54
|
-
case .option: return "Option"
|
|
55
|
-
case .shift: return "Shift"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
enum MouseShortcutButton: Hashable, Codable, Equatable {
|
|
61
|
-
case right
|
|
62
|
-
case middle
|
|
63
|
-
case button4
|
|
64
|
-
case button5
|
|
65
|
-
case number(Int)
|
|
66
|
-
|
|
67
|
-
init(rawButtonNumber: Int) {
|
|
68
|
-
switch rawButtonNumber {
|
|
69
|
-
case 1: self = .right
|
|
70
|
-
case 2: self = .middle
|
|
71
|
-
case 3: self = .button4
|
|
72
|
-
case 4: self = .button5
|
|
73
|
-
default: self = .number(rawButtonNumber)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
init(from decoder: Decoder) throws {
|
|
78
|
-
let container = try decoder.singleValueContainer()
|
|
79
|
-
if let intValue = try? container.decode(Int.self) {
|
|
80
|
-
self = MouseShortcutButton(rawButtonNumber: intValue)
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
let stringValue = try container.decode(String.self)
|
|
85
|
-
switch stringValue.lowercased() {
|
|
86
|
-
case "right", "button.right":
|
|
87
|
-
self = .right
|
|
88
|
-
case "middle", "button.middle":
|
|
89
|
-
self = .middle
|
|
90
|
-
case "back", "button.back", "button4", "button.button4":
|
|
91
|
-
self = .button4
|
|
92
|
-
case "forward", "button.forward", "button5", "button.button5":
|
|
93
|
-
self = .button5
|
|
94
|
-
default:
|
|
95
|
-
if let raw = Int(stringValue.filter(\.isNumber)) {
|
|
96
|
-
self = MouseShortcutButton(rawButtonNumber: raw)
|
|
97
|
-
} else {
|
|
98
|
-
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported mouse button '\(stringValue)'")
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
func encode(to encoder: Encoder) throws {
|
|
104
|
-
var container = encoder.singleValueContainer()
|
|
105
|
-
try container.encode(configValue)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
var rawButtonNumber: Int {
|
|
109
|
-
switch self {
|
|
110
|
-
case .right: return 1
|
|
111
|
-
case .middle: return 2
|
|
112
|
-
case .button4: return 3
|
|
113
|
-
case .button5: return 4
|
|
114
|
-
case .number(let value): return value
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
var configValue: String {
|
|
119
|
-
switch self {
|
|
120
|
-
case .right: return "right"
|
|
121
|
-
case .middle: return "middle"
|
|
122
|
-
case .button4: return "back"
|
|
123
|
-
case .button5: return "forward"
|
|
124
|
-
case .number(let value): return "button\(value)"
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
var displayLabel: String {
|
|
129
|
-
switch self {
|
|
130
|
-
case .right: return "Right Click"
|
|
131
|
-
case .middle: return "Middle Click"
|
|
132
|
-
case .button4: return "Back Button"
|
|
133
|
-
case .button5: return "Forward Button"
|
|
134
|
-
case .number(let value): return "Button \(value)"
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
var triggerToken: String {
|
|
139
|
-
switch self {
|
|
140
|
-
case .right: return "button.right"
|
|
141
|
-
case .middle: return "button.middle"
|
|
142
|
-
case .button4: return "button.back"
|
|
143
|
-
case .button5: return "button.forward"
|
|
144
|
-
case .number(let value): return "button.\(value)"
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
struct MouseShortcutDeviceSelector: Codable, Equatable {
|
|
150
|
-
private enum CodingKeys: String, CodingKey {
|
|
151
|
-
case match
|
|
152
|
-
case vendorId
|
|
153
|
-
case productId
|
|
154
|
-
case locationId
|
|
155
|
-
case product
|
|
156
|
-
case manufacturer
|
|
157
|
-
case transport
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
var match: String?
|
|
161
|
-
var vendorId: Int?
|
|
162
|
-
var productId: Int?
|
|
163
|
-
var locationId: Int?
|
|
164
|
-
var product: String?
|
|
165
|
-
var manufacturer: String?
|
|
166
|
-
var transport: String?
|
|
167
|
-
|
|
168
|
-
static let any = MouseShortcutDeviceSelector(match: "any")
|
|
169
|
-
|
|
170
|
-
init(
|
|
171
|
-
match: String? = "any",
|
|
172
|
-
vendorId: Int? = nil,
|
|
173
|
-
productId: Int? = nil,
|
|
174
|
-
locationId: Int? = nil,
|
|
175
|
-
product: String? = nil,
|
|
176
|
-
manufacturer: String? = nil,
|
|
177
|
-
transport: String? = nil
|
|
178
|
-
) {
|
|
179
|
-
self.match = match
|
|
180
|
-
self.vendorId = vendorId
|
|
181
|
-
self.productId = productId
|
|
182
|
-
self.locationId = locationId
|
|
183
|
-
self.product = product
|
|
184
|
-
self.manufacturer = manufacturer
|
|
185
|
-
self.transport = transport
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
init(from decoder: Decoder) throws {
|
|
189
|
-
let container = try decoder.singleValueContainer()
|
|
190
|
-
if let stringValue = try? container.decode(String.self) {
|
|
191
|
-
self = MouseShortcutDeviceSelector(match: stringValue)
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
let object = try decoder.container(keyedBy: CodingKeys.self)
|
|
196
|
-
match = try object.decodeIfPresent(String.self, forKey: .match)
|
|
197
|
-
vendorId = try object.decodeIfPresent(Int.self, forKey: .vendorId)
|
|
198
|
-
productId = try object.decodeIfPresent(Int.self, forKey: .productId)
|
|
199
|
-
locationId = try object.decodeIfPresent(Int.self, forKey: .locationId)
|
|
200
|
-
product = try object.decodeIfPresent(String.self, forKey: .product)
|
|
201
|
-
manufacturer = try object.decodeIfPresent(String.self, forKey: .manufacturer)
|
|
202
|
-
transport = try object.decodeIfPresent(String.self, forKey: .transport)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
func encode(to encoder: Encoder) throws {
|
|
206
|
-
if isAny {
|
|
207
|
-
var container = encoder.singleValueContainer()
|
|
208
|
-
try container.encode("any")
|
|
209
|
-
return
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
213
|
-
try container.encodeIfPresent(match, forKey: .match)
|
|
214
|
-
try container.encodeIfPresent(vendorId, forKey: .vendorId)
|
|
215
|
-
try container.encodeIfPresent(productId, forKey: .productId)
|
|
216
|
-
try container.encodeIfPresent(locationId, forKey: .locationId)
|
|
217
|
-
try container.encodeIfPresent(product, forKey: .product)
|
|
218
|
-
try container.encodeIfPresent(manufacturer, forKey: .manufacturer)
|
|
219
|
-
try container.encodeIfPresent(transport, forKey: .transport)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
var isAny: Bool {
|
|
223
|
-
let normalized = (match ?? "any").lowercased()
|
|
224
|
-
return normalized == "any"
|
|
225
|
-
&& vendorId == nil
|
|
226
|
-
&& productId == nil
|
|
227
|
-
&& locationId == nil
|
|
228
|
-
&& product == nil
|
|
229
|
-
&& manufacturer == nil
|
|
230
|
-
&& transport == nil
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
func matches(_ device: MouseInputDeviceInfo?) -> Bool {
|
|
234
|
-
if isAny { return true }
|
|
235
|
-
guard let device else { return false }
|
|
236
|
-
if let vendorId, vendorId != device.vendorId { return false }
|
|
237
|
-
if let productId, productId != device.productId { return false }
|
|
238
|
-
if let locationId, locationId != device.locationId { return false }
|
|
239
|
-
if let product, device.product?.localizedCaseInsensitiveContains(product) != true { return false }
|
|
240
|
-
if let manufacturer, device.manufacturer?.localizedCaseInsensitiveContains(manufacturer) != true { return false }
|
|
241
|
-
if let transport, device.transport?.localizedCaseInsensitiveContains(transport) != true { return false }
|
|
242
|
-
return true
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
struct MouseShortcutTrigger: Codable, Equatable {
|
|
247
|
-
var button: MouseShortcutButton
|
|
248
|
-
var kind: MouseShortcutTriggerKind
|
|
249
|
-
var direction: MouseGestureDirection?
|
|
250
|
-
var shape: GestureShapeLabel?
|
|
251
|
-
|
|
252
|
-
var triggerName: String {
|
|
253
|
-
let detail: String?
|
|
254
|
-
switch kind {
|
|
255
|
-
case .drag:
|
|
256
|
-
detail = direction?.rawValue
|
|
257
|
-
case .shape:
|
|
258
|
-
detail = shape?.rawValue
|
|
259
|
-
case .click:
|
|
260
|
-
detail = nil
|
|
261
|
-
}
|
|
262
|
-
return ([button.triggerToken, kind.rawValue] + [detail].compactMap { $0 }).joined(separator: ".")
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
var displayLabel: String {
|
|
266
|
-
switch kind {
|
|
267
|
-
case .click:
|
|
268
|
-
return "\(button.displayLabel) click"
|
|
269
|
-
case .drag:
|
|
270
|
-
if let direction {
|
|
271
|
-
return "\(button.displayLabel) drag \(direction.displayLabel.lowercased())"
|
|
272
|
-
}
|
|
273
|
-
return "\(button.displayLabel) drag"
|
|
274
|
-
case .shape:
|
|
275
|
-
if let shape {
|
|
276
|
-
return "\(button.displayLabel) \(shape.displayName)"
|
|
277
|
-
}
|
|
278
|
-
return "\(button.displayLabel) shape"
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
struct MouseShortcutKeyStroke: Codable, Equatable {
|
|
284
|
-
var key: String?
|
|
285
|
-
var keyCode: Int?
|
|
286
|
-
var modifiers: [MouseShortcutModifier]
|
|
287
|
-
|
|
288
|
-
var displayLabel: String {
|
|
289
|
-
let keyLabel = key?.uppercased() ?? "KeyCode \(keyCode ?? -1)"
|
|
290
|
-
let parts = modifiers.map(\.displayLabel) + [keyLabel]
|
|
291
|
-
return parts.joined(separator: "+")
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
struct MouseShortcutActionDefinition: Codable, Equatable {
|
|
296
|
-
var type: MouseShortcutActionType
|
|
297
|
-
var shortcut: MouseShortcutKeyStroke?
|
|
298
|
-
var app: String?
|
|
299
|
-
|
|
300
|
-
init(type: MouseShortcutActionType, shortcut: MouseShortcutKeyStroke? = nil, app: String? = nil) {
|
|
301
|
-
self.type = type
|
|
302
|
-
self.shortcut = shortcut
|
|
303
|
-
self.app = app
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
var label: String {
|
|
307
|
-
switch type {
|
|
308
|
-
case .spaceNext:
|
|
309
|
-
return "Next Space"
|
|
310
|
-
case .spacePrevious:
|
|
311
|
-
return "Previous Space"
|
|
312
|
-
case .screenMapToggle:
|
|
313
|
-
return "Screen Map Overview"
|
|
314
|
-
case .dictationStart:
|
|
315
|
-
return "Dictation"
|
|
316
|
-
case .shortcutSend:
|
|
317
|
-
return shortcut?.displayLabel ?? "Send Shortcut"
|
|
318
|
-
case .appActivate:
|
|
319
|
-
return app.map { "Activate \($0)" } ?? "Activate App"
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
struct MouseShortcutVisualDefinition: Codable, Equatable {
|
|
325
|
-
var renderer: String
|
|
326
|
-
var theme: String?
|
|
327
|
-
var asset: String?
|
|
328
|
-
var character: String?
|
|
329
|
-
var markers: [String: String]?
|
|
330
|
-
var events: [String: String]?
|
|
331
|
-
|
|
332
|
-
init(
|
|
333
|
-
renderer: String = "native",
|
|
334
|
-
theme: String? = nil,
|
|
335
|
-
asset: String? = nil,
|
|
336
|
-
character: String? = nil,
|
|
337
|
-
markers: [String: String]? = nil,
|
|
338
|
-
events: [String: String]? = nil
|
|
339
|
-
) {
|
|
340
|
-
self.renderer = renderer
|
|
341
|
-
self.theme = theme
|
|
342
|
-
self.asset = asset
|
|
343
|
-
self.character = character
|
|
344
|
-
self.markers = markers
|
|
345
|
-
self.events = events
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
var isLottiePOC: Bool {
|
|
349
|
-
renderer.localizedCaseInsensitiveCompare("lottie") == .orderedSame
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
func marker(phase: String, shape: GestureShapeLabel?, success: Bool?) -> String? {
|
|
353
|
-
let keys: [String] = [
|
|
354
|
-
success.map { "\(phase).\($0 ? "success" : "failure")" },
|
|
355
|
-
shape.map { "recognized:\($0.rawValue)" },
|
|
356
|
-
phase,
|
|
357
|
-
].compactMap { $0 }
|
|
358
|
-
|
|
359
|
-
for key in keys {
|
|
360
|
-
if let marker = markers?[key] {
|
|
361
|
-
return marker
|
|
362
|
-
}
|
|
363
|
-
if let marker = events?[key] {
|
|
364
|
-
return marker
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return nil
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
struct MouseShortcutRule: Codable, Equatable, Identifiable {
|
|
372
|
-
var id: String
|
|
373
|
-
var enabled: Bool
|
|
374
|
-
var device: MouseShortcutDeviceSelector
|
|
375
|
-
var trigger: MouseShortcutTrigger
|
|
376
|
-
var action: MouseShortcutActionDefinition
|
|
377
|
-
var visual: MouseShortcutVisualDefinition?
|
|
378
|
-
|
|
379
|
-
init(
|
|
380
|
-
id: String,
|
|
381
|
-
enabled: Bool,
|
|
382
|
-
device: MouseShortcutDeviceSelector,
|
|
383
|
-
trigger: MouseShortcutTrigger,
|
|
384
|
-
action: MouseShortcutActionDefinition,
|
|
385
|
-
visual: MouseShortcutVisualDefinition? = nil
|
|
386
|
-
) {
|
|
387
|
-
self.id = id
|
|
388
|
-
self.enabled = enabled
|
|
389
|
-
self.device = device
|
|
390
|
-
self.trigger = trigger
|
|
391
|
-
self.action = action
|
|
392
|
-
self.visual = visual
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
var summary: String {
|
|
396
|
-
"\(trigger.triggerName) -> \(action.type.rawValue)"
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
struct MouseShortcutTuning: Codable, Equatable {
|
|
401
|
-
var dragThreshold: CGFloat
|
|
402
|
-
var holdTolerance: CGFloat
|
|
403
|
-
var axisBias: CGFloat
|
|
404
|
-
|
|
405
|
-
static let defaults = MouseShortcutTuning(
|
|
406
|
-
dragThreshold: 68,
|
|
407
|
-
holdTolerance: 10,
|
|
408
|
-
axisBias: 1.2
|
|
409
|
-
)
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
struct MouseShortcutConfig: Codable, Equatable {
|
|
413
|
-
var version: Int
|
|
414
|
-
var tuning: MouseShortcutTuning
|
|
415
|
-
var rules: [MouseShortcutRule]
|
|
416
|
-
|
|
417
|
-
static let defaults = MouseShortcutConfig(
|
|
418
|
-
version: 1,
|
|
419
|
-
tuning: .defaults,
|
|
420
|
-
rules: [
|
|
421
|
-
MouseShortcutRule(
|
|
422
|
-
id: "space-previous",
|
|
423
|
-
enabled: true,
|
|
424
|
-
device: .any,
|
|
425
|
-
trigger: MouseShortcutTrigger(button: .middle, kind: .drag, direction: .left, shape: nil),
|
|
426
|
-
action: MouseShortcutActionDefinition(type: .spacePrevious)
|
|
427
|
-
),
|
|
428
|
-
MouseShortcutRule(
|
|
429
|
-
id: "space-next",
|
|
430
|
-
enabled: true,
|
|
431
|
-
device: .any,
|
|
432
|
-
trigger: MouseShortcutTrigger(button: .middle, kind: .drag, direction: .right, shape: nil),
|
|
433
|
-
action: MouseShortcutActionDefinition(type: .spaceNext)
|
|
434
|
-
),
|
|
435
|
-
MouseShortcutRule(
|
|
436
|
-
id: "screenmap-overview",
|
|
437
|
-
enabled: true,
|
|
438
|
-
device: .any,
|
|
439
|
-
trigger: MouseShortcutTrigger(button: .middle, kind: .drag, direction: .down, shape: nil),
|
|
440
|
-
action: MouseShortcutActionDefinition(type: .screenMapToggle)
|
|
441
|
-
),
|
|
442
|
-
MouseShortcutRule(
|
|
443
|
-
id: "dictation",
|
|
444
|
-
enabled: true,
|
|
445
|
-
device: .any,
|
|
446
|
-
trigger: MouseShortcutTrigger(button: .middle, kind: .drag, direction: .up, shape: nil),
|
|
447
|
-
action: MouseShortcutActionDefinition(type: .dictationStart)
|
|
448
|
-
),
|
|
449
|
-
]
|
|
450
|
-
)
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
struct MouseShortcutMatchResult {
|
|
454
|
-
let rule: MouseShortcutRule
|
|
455
|
-
let action: MouseShortcutActionDefinition
|
|
456
|
-
let triggerName: String
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
struct MouseShortcutTriggerEvent {
|
|
460
|
-
let button: MouseShortcutButton
|
|
461
|
-
let kind: MouseShortcutTriggerKind
|
|
462
|
-
let direction: MouseGestureDirection?
|
|
463
|
-
let shape: GestureShapeLabel?
|
|
464
|
-
let device: MouseInputDeviceInfo?
|
|
465
|
-
|
|
466
|
-
init(
|
|
467
|
-
button: MouseShortcutButton,
|
|
468
|
-
kind: MouseShortcutTriggerKind,
|
|
469
|
-
direction: MouseGestureDirection? = nil,
|
|
470
|
-
shape: GestureShapeLabel? = nil,
|
|
471
|
-
device: MouseInputDeviceInfo? = nil
|
|
472
|
-
) {
|
|
473
|
-
self.button = button
|
|
474
|
-
self.kind = kind
|
|
475
|
-
self.direction = direction
|
|
476
|
-
self.shape = shape
|
|
477
|
-
self.device = device
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
var triggerName: String {
|
|
481
|
-
MouseShortcutTrigger(button: button, kind: kind, direction: direction, shape: shape).triggerName
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
struct MouseShortcutObservedEvent {
|
|
486
|
-
let timestamp: Date
|
|
487
|
-
let phase: String
|
|
488
|
-
let buttonNumber: Int
|
|
489
|
-
let location: CGPoint
|
|
490
|
-
let delta: CGPoint
|
|
491
|
-
let modifiers: NSEvent.ModifierFlags
|
|
492
|
-
let frontmostAppName: String?
|
|
493
|
-
let frontmostBundleId: String?
|
|
494
|
-
let candidateTrigger: String?
|
|
495
|
-
let device: MouseInputDeviceInfo?
|
|
496
|
-
let matchedRuleSummary: String?
|
|
497
|
-
let willFire: Bool
|
|
498
|
-
let note: String?
|
|
499
|
-
}
|