@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.
Files changed (146) hide show
  1. package/README.md +3 -0
  2. package/app/Info.plist +2 -2
  3. package/app/Lattices.app/Contents/Info.plist +2 -2
  4. package/app/Lattices.app/Contents/MacOS/Lattices +0 -0
  5. package/app/Package.swift +6 -0
  6. package/app/Sources/AppShell/App.swift +20 -0
  7. package/app/Sources/{AppDelegate.swift → AppShell/AppDelegate.swift} +94 -34
  8. package/app/Sources/{AppShellView.swift → AppShell/AppShellView.swift} +12 -1
  9. package/app/Sources/AppShell/AppUpdater.swift +92 -0
  10. package/app/Sources/AppShell/CliActionLauncher.swift +50 -0
  11. package/app/Sources/{HomeDashboardView.swift → AppShell/HomeDashboardView.swift} +18 -10
  12. package/app/Sources/AppShell/LatticesRuntime.swift +61 -0
  13. package/app/Sources/{MainView.swift → AppShell/MainView.swift} +351 -191
  14. package/app/Sources/{OnboardingView.swift → AppShell/OnboardingView.swift} +30 -16
  15. package/app/Sources/{Preferences.swift → AppShell/Preferences.swift} +78 -0
  16. package/app/Sources/{SettingsView.swift → AppShell/SettingsView.swift} +869 -152
  17. package/app/Sources/{HotkeyStore.swift → Core/Actions/HotkeyStore.swift} +9 -5
  18. package/app/Sources/{IntentEngine.swift → Core/Actions/IntentEngine.swift} +51 -27
  19. package/app/Sources/Core/Actions/IntentSchema.swift +94 -0
  20. package/app/Sources/{Intents → Core/Actions/Intents}/LatticeIntent.swift +0 -25
  21. package/app/Sources/{PaletteCommand.swift → Core/Actions/PaletteCommand.swift} +26 -6
  22. package/app/Sources/{VoiceIntentResolver.swift → Core/Actions/VoiceIntentResolver.swift} +46 -4
  23. package/app/Sources/Core/Companion/CompanionActivityLog.swift +70 -0
  24. package/app/Sources/Core/Companion/CompanionKeyboardController.swift +141 -0
  25. package/app/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +438 -0
  26. package/app/Sources/Core/Companion/LatticesCompanionCockpit.swift +555 -0
  27. package/app/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +594 -0
  28. package/app/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +204 -0
  29. package/app/Sources/Core/Companion/LatticesDeckHost.swift +1463 -0
  30. package/app/Sources/{LatticesApi.swift → Core/Daemon/LatticesApi.swift} +125 -4
  31. package/app/Sources/{AppTypeClassifier.swift → Core/Desktop/AppTypeClassifier.swift} +36 -0
  32. package/app/Sources/{DesktopModel.swift → Core/Desktop/DesktopModel.swift} +6 -8
  33. package/app/Sources/Core/Desktop/MouseFinder.swift +527 -0
  34. package/app/Sources/Core/Desktop/SessionWindowLocator.swift +139 -0
  35. package/app/Sources/Core/Desktop/WindowDragSnapController.swift +628 -0
  36. package/app/Sources/Core/Desktop/WindowPreviewCard.swift +100 -0
  37. package/app/Sources/Core/Desktop/WindowPreviewStore.swift +113 -0
  38. package/app/Sources/Core/Desktop/WindowSelectionStore.swift +76 -0
  39. package/app/Sources/{WindowTiler.swift → Core/Desktop/WindowTiler.swift} +351 -172
  40. package/app/Sources/Core/Input/MouseGestureConfig.swift +364 -0
  41. package/app/Sources/Core/Input/MouseGestureController.swift +1203 -0
  42. package/app/Sources/Core/Input/MouseInputDeviceStore.swift +98 -0
  43. package/app/Sources/Core/Input/MouseInputEventViewer.swift +272 -0
  44. package/app/Sources/Core/Input/MouseShortcutStore.swift +107 -0
  45. package/app/Sources/{CommandModeState.swift → Core/Overlays/CommandMode/CommandModeState.swift} +127 -24
  46. package/app/Sources/{CommandModeView.swift → Core/Overlays/CommandMode/CommandModeView.swift} +492 -79
  47. package/app/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +67 -0
  48. package/app/Sources/{CheatSheetHUD.swift → Core/Overlays/HUD/CheatSheetHUD.swift} +1 -0
  49. package/app/Sources/{HUDRightBar.swift → Core/Overlays/HUD/HUDRightBar.swift} +23 -201
  50. package/app/Sources/{LauncherHUD.swift → Core/Overlays/HUD/LauncherHUD.swift} +12 -26
  51. package/app/Sources/{OmniSearchView.swift → Core/Overlays/OmniSearch/OmniSearchView.swift} +136 -2
  52. package/app/Sources/{OmniSearchWindow.swift → Core/Overlays/OmniSearch/OmniSearchWindow.swift} +21 -32
  53. package/app/Sources/Core/Overlays/OverlayPanelShell.swift +241 -0
  54. package/app/Sources/{ScreenMapState.swift → Core/Overlays/ScreenMap/ScreenMapState.swift} +116 -32
  55. package/app/Sources/{ScreenMapView.swift → Core/Overlays/ScreenMap/ScreenMapView.swift} +510 -524
  56. package/app/Sources/{ScreenMapWindowController.swift → Core/Overlays/ScreenMap/ScreenMapWindowController.swift} +12 -4
  57. package/app/Sources/{VoiceCommandWindow.swift → Core/Overlays/Voice/VoiceCommandWindow.swift} +46 -53
  58. package/app/Sources/Core/Pi/PiAuthNextStepCard.swift +148 -0
  59. package/app/Sources/Core/Pi/PiAuthPromptCard.swift +90 -0
  60. package/app/Sources/{PiChatDock.swift → Core/Pi/PiChatDock.swift} +137 -74
  61. package/app/Sources/{PiChatSession.swift → Core/Pi/PiChatSession.swift} +608 -108
  62. package/app/Sources/Core/Pi/PiInstallCallout.swift +86 -0
  63. package/app/Sources/Core/Pi/PiProviderSetupCallout.swift +99 -0
  64. package/app/Sources/{PiWorkspaceView.swift → Core/Pi/PiWorkspaceView.swift} +174 -77
  65. package/app/Sources/{PermissionChecker.swift → Core/System/PermissionChecker.swift} +76 -2
  66. package/app/Sources/Core/System/SystemTelemetryMonitor.swift +273 -0
  67. package/app/Sources/{HandsOffSession.swift → Core/Voice/HandsOffSession.swift} +15 -4
  68. package/app/Sources/{WorkspaceManager.swift → Core/Workspace/WorkspaceManager.swift} +288 -0
  69. package/bin/assistant-intelligence.ts +874 -0
  70. package/bin/handsoff-infer.ts +16 -209
  71. package/bin/handsoff-worker.ts +45 -258
  72. package/bin/lattices-app.ts +62 -0
  73. package/bin/lattices-dev +4 -0
  74. package/bin/lattices.ts +125 -14
  75. package/docs/agents.md +14 -0
  76. package/docs/api.md +55 -0
  77. package/docs/app.md +3 -0
  78. package/docs/companion-deck.md +180 -0
  79. package/docs/component-extraction-roadmap.md +392 -0
  80. package/docs/config.md +25 -0
  81. package/docs/tiling-reference.md +55 -0
  82. package/docs/voice-error-model.md +73 -0
  83. package/package.json +4 -1
  84. package/app/Sources/App.swift +0 -10
  85. package/app/Sources/CommandPaletteWindow.swift +0 -134
  86. package/app/Sources/MouseFinder.swift +0 -222
  87. /package/app/Sources/{KeyRecorderView.swift → AppShell/KeyRecorderView.swift} +0 -0
  88. /package/app/Sources/{MainWindow.swift → AppShell/MainWindow.swift} +0 -0
  89. /package/app/Sources/{SettingsWindow.swift → AppShell/SettingsWindow.swift} +0 -0
  90. /package/app/Sources/{HotkeyManager.swift → Core/Actions/HotkeyManager.swift} +0 -0
  91. /package/app/Sources/{Intents → Core/Actions/Intents}/CreateLayerIntent.swift +0 -0
  92. /package/app/Sources/{Intents → Core/Actions/Intents}/DistributeIntent.swift +0 -0
  93. /package/app/Sources/{Intents → Core/Actions/Intents}/FocusIntent.swift +0 -0
  94. /package/app/Sources/{Intents → Core/Actions/Intents}/HelpIntent.swift +0 -0
  95. /package/app/Sources/{Intents → Core/Actions/Intents}/KillIntent.swift +0 -0
  96. /package/app/Sources/{Intents → Core/Actions/Intents}/LaunchIntent.swift +0 -0
  97. /package/app/Sources/{Intents → Core/Actions/Intents}/ListSessionsIntent.swift +0 -0
  98. /package/app/Sources/{Intents → Core/Actions/Intents}/ListWindowsIntent.swift +0 -0
  99. /package/app/Sources/{Intents → Core/Actions/Intents}/ScanIntent.swift +0 -0
  100. /package/app/Sources/{Intents → Core/Actions/Intents}/SearchIntent.swift +0 -0
  101. /package/app/Sources/{Intents → Core/Actions/Intents}/SwitchLayerIntent.swift +0 -0
  102. /package/app/Sources/{Intents → Core/Actions/Intents}/TileIntent.swift +0 -0
  103. /package/app/Sources/{DaemonProtocol.swift → Core/Daemon/DaemonProtocol.swift} +0 -0
  104. /package/app/Sources/{DaemonServer.swift → Core/Daemon/DaemonServer.swift} +0 -0
  105. /package/app/Sources/{AccessibilityTextExtractor.swift → Core/Desktop/AccessibilityTextExtractor.swift} +0 -0
  106. /package/app/Sources/{DesktopModelTypes.swift → Core/Desktop/DesktopModelTypes.swift} +0 -0
  107. /package/app/Sources/{InventoryManager.swift → Core/Desktop/InventoryManager.swift} +0 -0
  108. /package/app/Sources/{InventoryPath.swift → Core/Desktop/InventoryPath.swift} +0 -0
  109. /package/app/Sources/{OcrModel.swift → Core/Desktop/OcrModel.swift} +0 -0
  110. /package/app/Sources/{OcrStore.swift → Core/Desktop/OcrStore.swift} +0 -0
  111. /package/app/Sources/{PlacementSpec.swift → Core/Desktop/PlacementSpec.swift} +0 -0
  112. /package/app/Sources/{TilePickerView.swift → Core/Desktop/TilePickerView.swift} +0 -0
  113. /package/app/Sources/{AppWindowShell.swift → Core/Overlays/AppWindowShell.swift} +0 -0
  114. /package/app/Sources/{CommandModeWindow.swift → Core/Overlays/CommandMode/CommandModeWindow.swift} +0 -0
  115. /package/app/Sources/{CommandPaletteView.swift → Core/Overlays/CommandPalette/CommandPaletteView.swift} +0 -0
  116. /package/app/Sources/{HUDBottomBar.swift → Core/Overlays/HUD/HUDBottomBar.swift} +0 -0
  117. /package/app/Sources/{HUDController.swift → Core/Overlays/HUD/HUDController.swift} +0 -0
  118. /package/app/Sources/{HUDLeftBar.swift → Core/Overlays/HUD/HUDLeftBar.swift} +0 -0
  119. /package/app/Sources/{HUDMinimap.swift → Core/Overlays/HUD/HUDMinimap.swift} +0 -0
  120. /package/app/Sources/{HUDState.swift → Core/Overlays/HUD/HUDState.swift} +0 -0
  121. /package/app/Sources/{HUDTopBar.swift → Core/Overlays/HUD/HUDTopBar.swift} +0 -0
  122. /package/app/Sources/{LayerBezel.swift → Core/Overlays/HUD/LayerBezel.swift} +0 -0
  123. /package/app/Sources/{OmniSearchState.swift → Core/Overlays/OmniSearch/OmniSearchState.swift} +0 -0
  124. /package/app/Sources/{DiagnosticLog.swift → Core/System/DiagnosticLog.swift} +0 -0
  125. /package/app/Sources/{EventBus.swift → Core/System/EventBus.swift} +0 -0
  126. /package/app/Sources/{ProcessModel.swift → Core/System/ProcessModel.swift} +0 -0
  127. /package/app/Sources/{ProcessQuery.swift → Core/System/ProcessQuery.swift} +0 -0
  128. /package/app/Sources/{AdvisorLearningStore.swift → Core/Voice/AdvisorLearningStore.swift} +0 -0
  129. /package/app/Sources/{AgentSession.swift → Core/Voice/AgentSession.swift} +0 -0
  130. /package/app/Sources/{AudioProvider.swift → Core/Voice/AudioProvider.swift} +0 -0
  131. /package/app/Sources/{VoiceChatView.swift → Core/Voice/VoiceChatView.swift} +0 -0
  132. /package/app/Sources/{VoxClient.swift → Core/Voice/VoxClient.swift} +0 -0
  133. /package/app/Sources/{Project.swift → Core/Workspace/Project.swift} +0 -0
  134. /package/app/Sources/{ProjectScanner.swift → Core/Workspace/ProjectScanner.swift} +0 -0
  135. /package/app/Sources/{SessionLayerStore.swift → Core/Workspace/SessionLayerStore.swift} +0 -0
  136. /package/app/Sources/{SessionManager.swift → Core/Workspace/SessionManager.swift} +0 -0
  137. /package/app/Sources/{Terminal.swift → Core/Workspace/Terminal/Terminal.swift} +0 -0
  138. /package/app/Sources/{TerminalQuery.swift → Core/Workspace/Terminal/TerminalQuery.swift} +0 -0
  139. /package/app/Sources/{TerminalSynthesizer.swift → Core/Workspace/Terminal/TerminalSynthesizer.swift} +0 -0
  140. /package/app/Sources/{TmuxModel.swift → Core/Workspace/Tmux/TmuxModel.swift} +0 -0
  141. /package/app/Sources/{TmuxQuery.swift → Core/Workspace/Tmux/TmuxQuery.swift} +0 -0
  142. /package/app/Sources/{ActionRow.swift → UI/ActionRow.swift} +0 -0
  143. /package/app/Sources/{OrphanRow.swift → UI/OrphanRow.swift} +0 -0
  144. /package/app/Sources/{ProjectRow.swift → UI/ProjectRow.swift} +0 -0
  145. /package/app/Sources/{TabGroupRow.swift → UI/TabGroupRow.swift} +0 -0
  146. /package/app/Sources/{Theme.swift → UI/Theme.swift} +0 -0
@@ -31,7 +31,7 @@ enum HotkeyAction: String, CaseIterable, Codable {
31
31
  // Tiling
32
32
  case tileLeft, tileRight, tileMaximize, tileCenter
33
33
  case tileTopLeft, tileTopRight, tileBottomLeft, tileBottomRight
34
- case tileTop, tileBottom, tileDistribute
34
+ case tileTop, tileBottom, tileDistribute, tileTypeGrid
35
35
  case tileLeftThird, tileCenterThird, tileRightThird
36
36
 
37
37
  var label: String {
@@ -40,11 +40,11 @@ enum HotkeyAction: String, CaseIterable, Codable {
40
40
  case .screenMap: return "Screen Map"
41
41
  case .bezel: return "Window Bezel"
42
42
  case .cheatSheet: return "Cheat Sheet"
43
- case .desktopInventory: return "Desktop Inventory"
44
- case .omniSearch: return "Omni Search"
43
+ case .desktopInventory: return "Window Selector"
44
+ case .omniSearch: return "Search"
45
45
  case .voiceCommand: return "Voice Command"
46
46
  case .handsOff: return "Hands-Off Mode"
47
- case .unifiedWindow: return "Unified Window"
47
+ case .unifiedWindow: return "Workspace Home"
48
48
  case .hud: return "HUD"
49
49
  case .mouseFinder: return "Find Mouse"
50
50
  case .layer1: return "Layer 1"
@@ -70,6 +70,7 @@ enum HotkeyAction: String, CaseIterable, Codable {
70
70
  case .tileTop: return "Top Half"
71
71
  case .tileBottom: return "Bottom Half"
72
72
  case .tileDistribute: return "Distribute"
73
+ case .tileTypeGrid: return "Grid Type"
73
74
  case .tileLeftThird: return "Left Third"
74
75
  case .tileCenterThird: return "Center Third"
75
76
  case .tileRightThird: return "Right Third"
@@ -122,6 +123,7 @@ enum HotkeyAction: String, CaseIterable, Codable {
122
123
  case .tileTop: return 308
123
124
  case .tileBottom: return 309
124
125
  case .tileDistribute: return 310
126
+ case .tileTypeGrid: return 314
125
127
  case .tileLeftThird: return 311
126
128
  case .tileCenterThird: return 312
127
129
  case .tileRightThird: return 313
@@ -226,9 +228,10 @@ class HotkeyStore: ObservableObject {
226
228
 
227
229
  // App
228
230
  bind(.palette, 46, cmdShift) // Cmd+Shift+M
229
- bind(.unifiedWindow, 18, hyper) // Hyper+1 (Screen Map + Desktop Inventory)
231
+ bind(.unifiedWindow, 18, hyper) // Hyper+1 (Workspace Home)
230
232
  bind(.bezel, 19, hyper) // Hyper+2
231
233
  bind(.hud, 20, hyper) // Hyper+3 (HUD overlay)
234
+ bind(.desktopInventory, 5, hyper) // Hyper+G
232
235
  bind(.voiceCommand, 21, hyper) // Hyper+4 (moved from Hyper+3)
233
236
  let cmdCtrl = UInt32(cmdKey | controlKey)
234
237
  bind(.handsOff, 46, cmdCtrl) // Ctrl+Cmd+M
@@ -259,6 +262,7 @@ class HotkeyStore: ObservableObject {
259
262
  bind(.tileBottomLeft, 38, ctrlOpt) // Ctrl+Opt+J
260
263
  bind(.tileBottomRight, 40, ctrlOpt) // Ctrl+Opt+K
261
264
  bind(.tileDistribute, 2, ctrlOpt) // Ctrl+Opt+D
265
+ bind(.tileTypeGrid, 5, hyper) // Hyper+G
262
266
  bind(.tileLeftThird, 18, ctrlOpt) // Ctrl+Opt+1
263
267
  bind(.tileCenterThird, 19, ctrlOpt) // Ctrl+Opt+2
264
268
  bind(.tileRightThird, 20, ctrlOpt) // Ctrl+Opt+3
@@ -1,31 +1,5 @@
1
1
  import AppKit
2
2
 
3
- // MARK: - Intent Definition
4
-
5
- struct IntentDef {
6
- let name: String
7
- let description: String
8
- let examples: [String] // Example phrases that map to this intent
9
- let slots: [IntentSlot] // Named parameters extracted from the utterance
10
- let handler: (IntentRequest) throws -> JSON
11
- }
12
-
13
- struct IntentSlot {
14
- let name: String
15
- let type: String // "string", "int", "position", "query"
16
- let required: Bool
17
- let description: String
18
- let enumValues: [String]? // For constrained slots like tile positions
19
- }
20
-
21
- struct IntentRequest {
22
- let intent: String
23
- let slots: [String: JSON]
24
- let rawText: String? // Original transcription, for fallback matching
25
- let confidence: Double? // Transcription confidence from voice service
26
- let source: String? // "vox", "siri", "cli", etc.
27
- }
28
-
29
3
  // MARK: - Intent Engine
30
4
 
31
5
  final class IntentEngine {
@@ -133,6 +107,8 @@ final class IntentEngine {
133
107
  description: "Target window ID", enumValues: nil),
134
108
  IntentSlot(name: "session", type: "string", required: false,
135
109
  description: "Target session name", enumValues: nil),
110
+ IntentSlot(name: "selection", type: "bool", required: false,
111
+ description: "Apply to the active multi-window selection instead of a single window", enumValues: nil),
136
112
  ],
137
113
  handler: { req in
138
114
  guard let posStr = req.slots["position"]?.stringValue else {
@@ -176,6 +152,34 @@ final class IntentEngine {
176
152
  throw IntentError.targetNotFound("No window found for app '\(app)'")
177
153
  }
178
154
 
155
+ if req.slots["selection"]?.boolValue == true {
156
+ let selectionIds = WindowSelectionStore.shared.windowIds
157
+ guard !selectionIds.isEmpty else {
158
+ throw IntentError.targetNotFound("No active window selection")
159
+ }
160
+
161
+ if selectionIds.count == 1,
162
+ let wid = selectionIds.first,
163
+ let entry = DesktopModel.shared.windows[wid] {
164
+ tileEntry(entry)
165
+ return .object([
166
+ "ok": .bool(true),
167
+ "target": .string("selection"),
168
+ "wid": .int(Int(wid)),
169
+ "position": .string(posStr)
170
+ ])
171
+ }
172
+
173
+ return try LatticesApi.shared.dispatch(
174
+ method: "space.optimize",
175
+ params: .object([
176
+ "scope": .string("selection"),
177
+ "windowIds": .array(selectionIds.map { .int(Int($0)) }),
178
+ "region": .string(posStr)
179
+ ])
180
+ )
181
+ }
182
+
179
183
  // Default: tile frontmost window
180
184
  DispatchQueue.main.async {
181
185
  WindowTiler.tileFrontmostViaAX(to: placement)
@@ -381,7 +385,7 @@ final class IntentEngine {
381
385
 
382
386
  register(IntentDef(
383
387
  name: "distribute",
384
- description: "Distribute windows evenly in a grid, optionally filtered by app and constrained to a screen region",
388
+ description: "Distribute windows evenly in a grid, optionally filtered by app or window type and constrained to a screen region",
385
389
  examples: [
386
390
  "spread out the windows",
387
391
  "distribute everything",
@@ -394,19 +398,39 @@ final class IntentEngine {
394
398
  slots: [
395
399
  IntentSlot(name: "app", type: "string", required: false,
396
400
  description: "Filter to windows of this app (e.g. 'iTerm2', 'Google Chrome')", enumValues: nil),
401
+ IntentSlot(name: "type", type: "string", required: false,
402
+ description: "Filter to a window type (e.g. 'terminal', 'browser', 'editor')",
403
+ enumValues: AppType.allCases.map(\.rawValue)),
397
404
  IntentSlot(name: "region", type: "position", required: false,
398
405
  description: "Constrain the grid to a screen region. Uses tile position names.",
399
406
  enumValues: ["left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right",
400
407
  "left-third", "center-third", "right-third"]),
408
+ IntentSlot(name: "selection", type: "bool", required: false,
409
+ description: "Use the active selected windows instead of all visible windows", enumValues: nil),
401
410
  ],
402
411
  handler: { req in
403
412
  var params: [String: JSON] = [:]
404
413
  if let app = req.slots["app"]?.stringValue {
405
414
  params["app"] = .string(app)
406
415
  }
416
+ if let type = req.slots["type"]?.stringValue {
417
+ params["type"] = .string(type)
418
+ }
407
419
  if let region = req.slots["region"]?.stringValue {
408
420
  params["region"] = .string(region)
409
421
  }
422
+ if req.slots["selection"]?.boolValue == true {
423
+ let selectionIds = WindowSelectionStore.shared.windowIds
424
+ guard !selectionIds.isEmpty else {
425
+ throw IntentError.targetNotFound("No active window selection")
426
+ }
427
+ params["scope"] = .string("selection")
428
+ params["windowIds"] = .array(selectionIds.map { .int(Int($0)) })
429
+ return try LatticesApi.shared.dispatch(
430
+ method: "space.optimize",
431
+ params: .object(params)
432
+ )
433
+ }
410
434
  return try LatticesApi.shared.dispatch(
411
435
  method: "layout.distribute",
412
436
  params: params.isEmpty ? nil : .object(params)
@@ -0,0 +1,94 @@
1
+ import Foundation
2
+
3
+ struct IntentDef {
4
+ let name: String
5
+ let description: String
6
+ let examples: [String]
7
+ let slots: [IntentSlot]
8
+ let handler: (IntentRequest) throws -> JSON
9
+ }
10
+
11
+ struct IntentSlot {
12
+ let name: String
13
+ let type: String
14
+ let required: Bool
15
+ let description: String
16
+ let enumValues: [String]?
17
+ let defaultValue: JSON?
18
+
19
+ init(
20
+ name: String,
21
+ type: String,
22
+ required: Bool,
23
+ description: String,
24
+ enumValues: [String]? = nil,
25
+ defaultValue: JSON? = nil
26
+ ) {
27
+ self.name = name
28
+ self.type = type
29
+ self.required = required
30
+ self.description = description
31
+ self.enumValues = enumValues
32
+ self.defaultValue = defaultValue
33
+ }
34
+ }
35
+
36
+ struct IntentRequest {
37
+ let intent: String
38
+ let slots: [String: JSON]
39
+ let rawText: String?
40
+ let confidence: Double?
41
+ let source: String?
42
+ }
43
+
44
+ enum SlotType {
45
+ case string
46
+ case int
47
+ case bool
48
+ case position
49
+ case query
50
+ case app
51
+ case session
52
+ case layer
53
+ case enumerated([String])
54
+
55
+ var typeLabel: String {
56
+ switch self {
57
+ case .string: return "string"
58
+ case .int: return "int"
59
+ case .bool: return "bool"
60
+ case .position: return "position"
61
+ case .query: return "query"
62
+ case .app: return "app"
63
+ case .session: return "session"
64
+ case .layer: return "layer"
65
+ case .enumerated: return "string"
66
+ }
67
+ }
68
+
69
+ var enumValues: [String]? {
70
+ guard case .enumerated(let values) = self else { return nil }
71
+ return values
72
+ }
73
+ }
74
+
75
+ typealias SlotDef = IntentSlot
76
+
77
+ extension IntentSlot {
78
+ init(
79
+ name: String,
80
+ type: SlotType,
81
+ required: Bool = true,
82
+ description: String = "",
83
+ defaultValue: JSON? = nil
84
+ ) {
85
+ self.init(
86
+ name: name,
87
+ type: type.typeLabel,
88
+ required: required,
89
+ description: description,
90
+ enumValues: type.enumValues,
91
+ defaultValue: defaultValue
92
+ )
93
+ }
94
+ }
@@ -12,31 +12,6 @@ protocol LatticeIntent {
12
12
  func perform(slots: [String: JSON]) throws -> JSON
13
13
  }
14
14
 
15
- // MARK: - Slot Definition
16
-
17
- enum SlotType {
18
- case string // Free-form text
19
- case position // Tile position (left, right, maximize, etc.)
20
- case app // Running app name
21
- case session // Active tmux session
22
- case layer // Layer name
23
- case enumerated([String]) // Fixed set of values
24
- }
25
-
26
- struct SlotDef {
27
- let name: String
28
- let type: SlotType
29
- let required: Bool
30
- let defaultValue: JSON?
31
-
32
- init(name: String, type: SlotType, required: Bool = true, defaultValue: JSON? = nil) {
33
- self.name = name
34
- self.type = type
35
- self.required = required
36
- self.defaultValue = defaultValue
37
- }
38
- }
39
-
40
15
  // MARK: - Compiled Phrase Template
41
16
 
42
17
  struct CompiledPhrase {
@@ -364,24 +364,44 @@ enum CommandBuilder {
364
364
  }
365
365
  ))
366
366
 
367
+ commands.append(PaletteCommand(
368
+ id: "app-home",
369
+ title: "Home",
370
+ subtitle: "Open the workspace home view",
371
+ icon: "house",
372
+ category: .app,
373
+ badge: nil,
374
+ action: { ScreenMapWindowController.shared.showPage(.home) }
375
+ ))
376
+
367
377
  commands.append(PaletteCommand(
368
378
  id: "app-windows-list",
369
- title: "Windows List",
370
- subtitle: "Browse all windows across displays",
371
- icon: "rectangle.split.2x1",
379
+ title: "Search",
380
+ subtitle: "Browse windows, displays, spaces, and screen text",
381
+ icon: "magnifyingglass",
372
382
  category: .app,
373
383
  badge: nil,
374
- action: { CommandModeWindow.shared.show() }
384
+ action: { ScreenMapWindowController.shared.showPage(.desktopInventory) }
375
385
  ))
376
386
 
377
387
  commands.append(PaletteCommand(
378
388
  id: "app-screen-map",
379
- title: "Window Map",
389
+ title: "Layout",
380
390
  subtitle: "Visual window editor",
381
391
  icon: "rectangle.3.group",
382
392
  category: .app,
383
393
  badge: nil,
384
- action: { ScreenMapWindowController.shared.show() }
394
+ action: { ScreenMapWindowController.shared.showPage(.screenMap) }
395
+ ))
396
+
397
+ commands.append(PaletteCommand(
398
+ id: "app-workspace-chat",
399
+ title: "Workspace Chat",
400
+ subtitle: "Open the longer-form assistant surface",
401
+ icon: "bubble.left.and.bubble.right",
402
+ category: .app,
403
+ badge: nil,
404
+ action: { ScreenMapWindowController.shared.showPage(.pi) }
385
405
  ))
386
406
 
387
407
  commands.append(PaletteCommand(
@@ -141,10 +141,43 @@ final class VoiceIntentResolver {
141
141
  private func extractSlots(for intentName: String, input: String) -> ExtractedSlots {
142
142
  switch intentName {
143
143
  case "tile_window":
144
- guard let position = resolvePosition(in: input) else {
144
+ var slots: [String: JSON] = [:]
145
+ var boost = 0.0
146
+
147
+ if let position = resolvePosition(in: input) {
148
+ slots["position"] = .string(position)
149
+ boost += 0.28
150
+ } else {
145
151
  return ExtractedSlots(slots: [:], boost: 0)
146
152
  }
147
- return ExtractedSlots(slots: ["position": .string(position)], boost: 0.28)
153
+
154
+ if refersToSelection(in: input) {
155
+ slots["selection"] = .bool(true)
156
+ boost += 0.08
157
+ }
158
+
159
+ return ExtractedSlots(slots: slots, boost: boost)
160
+
161
+ case "distribute":
162
+ var slots: [String: JSON] = [:]
163
+ var boost = 0.0
164
+
165
+ if let region = resolvePosition(in: input) {
166
+ slots["region"] = .string(region)
167
+ boost += 0.18
168
+ }
169
+
170
+ if let app = detectKnownApp(in: input) {
171
+ slots["app"] = .string(app)
172
+ boost += 0.14
173
+ }
174
+
175
+ if refersToSelection(in: input) {
176
+ slots["selection"] = .bool(true)
177
+ boost += 0.12
178
+ }
179
+
180
+ return ExtractedSlots(slots: slots, boost: boost)
148
181
 
149
182
  case "focus":
150
183
  if let app = detectKnownApp(in: input) ?? extractEntity(in: input, prefixes: focusPrefixes) {
@@ -426,6 +459,15 @@ final class VoiceIntentResolver {
426
459
  return nil
427
460
  }
428
461
 
462
+ private func refersToSelection(in input: String) -> Bool {
463
+ let markers = [
464
+ "grid that", "grid these", "grid those",
465
+ "tile that", "tile these", "tile those",
466
+ "selected windows", "selection", "selected", "these windows", "those windows", "them"
467
+ ]
468
+ return markers.contains(where: input.contains)
469
+ }
470
+
429
471
  private func detectKnownApp(in input: String) -> String? {
430
472
  for app in knownApps() {
431
473
  let lower = app.lowercased()
@@ -601,7 +643,7 @@ final class VoiceIntentResolver {
601
643
  "search": ["find", "search", "look for", "where is", "where d", "locate", "lost", "show me all", "windows"],
602
644
  "list_windows": ["what s open", "list windows", "which windows", "what do i have open"],
603
645
  "list_sessions": ["list sessions", "what s running", "which projects", "show my sessions"],
604
- "distribute": ["distribute", "spread", "organize", "arrange", "tidy", "clean up", "grid"],
646
+ "distribute": ["distribute", "spread", "organize", "arrange", "tidy", "clean up", "grid", "selected", "selection"],
605
647
  "create_layer": ["create layer", "save layout", "snapshot", "remember this layout"],
606
648
  "kill": ["kill", "stop", "shut down", "close", "terminate", "end"],
607
649
  "scan": ["scan", "rescan", "ocr", "read the screen", "what s on my screen", "screen text"],
@@ -662,7 +704,7 @@ final class VoiceIntentResolver {
662
704
  "search": ["where d my slack go", "pull up everything with dewey in it", "show me all the chrome windows", "dewey"],
663
705
  "list_windows": ["what do i have open", "what windows do i have"],
664
706
  "list_sessions": ["show me my sessions", "which projects are active"],
665
- "distribute": ["tidy up", "line everything up", "clean up the windows"],
707
+ "distribute": ["tidy up", "line everything up", "clean up the windows", "grid that in the bottom half", "arrange the selected windows"],
666
708
  "create_layer": ["snapshot this", "remember this layout"],
667
709
  "kill": ["close the dewey session", "stop my session"],
668
710
  "scan": ["what s on my screen", "read the screen", "give me a fresh scan"],
@@ -0,0 +1,70 @@
1
+ import DeckKit
2
+ import Foundation
3
+
4
+ final class CompanionActivityLog {
5
+ static let shared = CompanionActivityLog()
6
+
7
+ private let lock = NSLock()
8
+ private var entries: [DeckActivityLogEntry] = []
9
+ private let maxEntries = 120
10
+
11
+ private init() {
12
+ EventBus.shared.subscribe { [weak self] event in
13
+ self?.record(event)
14
+ }
15
+ }
16
+
17
+ func record(tag: String, tint: String?, text: String) {
18
+ let entry = DeckActivityLogEntry(
19
+ id: UUID().uuidString,
20
+ createdAt: Date(),
21
+ tag: tag,
22
+ tint: tint,
23
+ text: text
24
+ )
25
+
26
+ lock.lock()
27
+ entries.append(entry)
28
+ if entries.count > maxEntries {
29
+ entries.removeFirst(entries.count - maxEntries)
30
+ }
31
+ lock.unlock()
32
+ }
33
+
34
+ func snapshot(limit: Int = 80) -> [DeckActivityLogEntry] {
35
+ lock.lock()
36
+ let copy = entries
37
+ lock.unlock()
38
+
39
+ return Array(copy.suffix(limit).reversed())
40
+ }
41
+ }
42
+
43
+ private extension CompanionActivityLog {
44
+ func record(_ event: ModelEvent) {
45
+ switch event {
46
+ case .windowsChanged(let windows, let added, let removed):
47
+ let delta = [added.isEmpty ? nil : "+\(added.count)", removed.isEmpty ? nil : "-\(removed.count)"]
48
+ .compactMap { $0 }
49
+ .joined(separator: " ")
50
+ let suffix = delta.isEmpty ? "" : " (\(delta))"
51
+ record(tag: "WIN", tint: "blue", text: "\(windows.count) desktop windows\(suffix)")
52
+
53
+ case .tmuxChanged(let sessions):
54
+ record(tag: "TMUX", tint: "green", text: "\(sessions.count) tmux sessions indexed")
55
+
56
+ case .layerSwitched(let index):
57
+ record(tag: "LAYER", tint: "violet", text: "Switched workspace layer \(index + 1)")
58
+
59
+ case .processesChanged(let interesting):
60
+ record(tag: "PROC", tint: "amber", text: "\(interesting.count) terminal processes changed")
61
+
62
+ case .ocrScanComplete(let windowCount, let totalBlocks):
63
+ record(tag: "OCR", tint: "teal", text: "Scanned \(totalBlocks) text blocks across \(windowCount) windows")
64
+
65
+ case .voiceCommand(let text, let confidence):
66
+ let pct = Int((confidence * 100).rounded())
67
+ record(tag: "VOICE", tint: "red", text: "\"\(text)\" · \(pct)%")
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,141 @@
1
+ import AppKit
2
+ import Foundation
3
+
4
+ enum CompanionKeyboardError: LocalizedError {
5
+ case unknownKey(String)
6
+ case eventSourceUnavailable
7
+
8
+ var errorDescription: String? {
9
+ switch self {
10
+ case .unknownKey(let key):
11
+ return "Unsupported key for forwarding: \(key)"
12
+ case .eventSourceUnavailable:
13
+ return "Unable to create a keyboard event source."
14
+ }
15
+ }
16
+ }
17
+
18
+ final class CompanionKeyboardController {
19
+ static let shared = CompanionKeyboardController()
20
+
21
+ private init() {}
22
+
23
+ func send(key rawKey: String, modifiers rawModifiers: [String]) throws -> String {
24
+ let parsed = parse(key: rawKey, modifiers: rawModifiers)
25
+ guard let keyCode = keyCode(for: parsed.key) else {
26
+ throw CompanionKeyboardError.unknownKey(rawKey)
27
+ }
28
+ guard let source = CGEventSource(stateID: .combinedSessionState) else {
29
+ throw CompanionKeyboardError.eventSourceUnavailable
30
+ }
31
+
32
+ let flags = eventFlags(for: parsed.modifiers)
33
+ guard
34
+ let down = CGEvent(keyboardEventSource: source, virtualKey: keyCode, keyDown: true),
35
+ let up = CGEvent(keyboardEventSource: source, virtualKey: keyCode, keyDown: false)
36
+ else {
37
+ throw CompanionKeyboardError.eventSourceUnavailable
38
+ }
39
+
40
+ down.flags = flags
41
+ up.flags = flags
42
+ down.post(tap: .cghidEventTap)
43
+ usleep(12_000)
44
+ up.post(tap: .cghidEventTap)
45
+
46
+ return displayName(key: parsed.key, modifiers: parsed.modifiers)
47
+ }
48
+ }
49
+
50
+ private extension CompanionKeyboardController {
51
+ func parse(key rawKey: String, modifiers rawModifiers: [String]) -> (key: String, modifiers: Set<String>) {
52
+ var modifiers = Set(rawModifiers.map(normalizeModifier).filter { !$0.isEmpty })
53
+ var key = rawKey
54
+ .trimmingCharacters(in: .whitespacesAndNewlines)
55
+ .lowercased()
56
+
57
+ let symbolModifiers: [(String, String)] = [
58
+ ("⌘", "command"),
59
+ ("cmd", "command"),
60
+ ("command", "command"),
61
+ ("⌥", "option"),
62
+ ("option", "option"),
63
+ ("alt", "option"),
64
+ ("⌃", "control"),
65
+ ("ctrl", "control"),
66
+ ("control", "control"),
67
+ ("⇧", "shift"),
68
+ ("shift", "shift"),
69
+ ]
70
+
71
+ for (symbol, modifier) in symbolModifiers where key.contains(symbol) {
72
+ modifiers.insert(modifier)
73
+ key = key.replacingOccurrences(of: symbol, with: "")
74
+ }
75
+
76
+ key = key
77
+ .replacingOccurrences(of: "+", with: "")
78
+ .replacingOccurrences(of: " ", with: "")
79
+ .replacingOccurrences(of: "⎋", with: "escape")
80
+ .replacingOccurrences(of: "⇥", with: "tab")
81
+ .replacingOccurrences(of: "↩", with: "enter")
82
+ .replacingOccurrences(of: "⏎", with: "enter")
83
+ .replacingOccurrences(of: "return", with: "enter")
84
+ .replacingOccurrences(of: "←", with: "left")
85
+ .replacingOccurrences(of: "→", with: "right")
86
+ .replacingOccurrences(of: "↑", with: "up")
87
+ .replacingOccurrences(of: "↓", with: "down")
88
+
89
+ if key == "esc" {
90
+ key = "escape"
91
+ }
92
+
93
+ return (key.isEmpty ? rawKey.lowercased() : key, modifiers)
94
+ }
95
+
96
+ func normalizeModifier(_ modifier: String) -> String {
97
+ switch modifier.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() {
98
+ case "cmd", "command", "meta", "⌘":
99
+ return "command"
100
+ case "opt", "option", "alt", "⌥":
101
+ return "option"
102
+ case "ctrl", "control", "⌃":
103
+ return "control"
104
+ case "shift", "⇧":
105
+ return "shift"
106
+ default:
107
+ return ""
108
+ }
109
+ }
110
+
111
+ func eventFlags(for modifiers: Set<String>) -> CGEventFlags {
112
+ var flags: CGEventFlags = []
113
+ if modifiers.contains("command") { flags.insert(.maskCommand) }
114
+ if modifiers.contains("option") { flags.insert(.maskAlternate) }
115
+ if modifiers.contains("control") { flags.insert(.maskControl) }
116
+ if modifiers.contains("shift") { flags.insert(.maskShift) }
117
+ return flags
118
+ }
119
+
120
+ func keyCode(for key: String) -> CGKeyCode? {
121
+ let codes: [String: CGKeyCode] = [
122
+ "a": 0, "s": 1, "d": 2, "f": 3, "h": 4, "g": 5, "z": 6, "x": 7,
123
+ "c": 8, "v": 9, "b": 11, "q": 12, "w": 13, "e": 14, "r": 15,
124
+ "y": 16, "t": 17, "1": 18, "2": 19, "3": 20, "4": 21, "6": 22,
125
+ "5": 23, "=": 24, "9": 25, "7": 26, "-": 27, "8": 28, "0": 29,
126
+ "]": 30, "o": 31, "u": 32, "[": 33, "i": 34, "p": 35, "enter": 36,
127
+ "l": 37, "j": 38, "'": 39, "k": 40, ";": 41, "\\": 42, ",": 43,
128
+ "/": 44, "n": 45, "m": 46, ".": 47, "tab": 48, "space": 49,
129
+ "`": 50, "delete": 51, "backspace": 51, "escape": 53,
130
+ "command": 55, "cmd": 55, "shift": 56, "capslock": 57, "option": 58,
131
+ "alt": 58, "control": 59, "left": 123, "right": 124, "down": 125,
132
+ "up": 126,
133
+ ]
134
+ return codes[key]
135
+ }
136
+
137
+ func displayName(key: String, modifiers: Set<String>) -> String {
138
+ let ordered = ["control", "option", "shift", "command"].filter { modifiers.contains($0) }
139
+ return (ordered + [key]).joined(separator: "+")
140
+ }
141
+ }