@lattices/cli 0.4.14 → 0.6.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.
Files changed (181) hide show
  1. package/README.md +5 -7
  2. package/apps/mac/Info.plist +4 -4
  3. package/apps/mac/Lattices.app/Contents/Info.plist +4 -12
  4. package/apps/mac/Lattices.app/Contents/MacOS/Lattices +0 -0
  5. package/bin/lattices-app.ts +110 -17
  6. package/bin/lattices-build +125 -0
  7. package/bin/lattices-dev +89 -16
  8. package/bin/lattices.ts +977 -16
  9. package/docs/agents.md +81 -4
  10. package/docs/ai-chat-ux-review.md +416 -0
  11. package/docs/api.md +135 -3
  12. package/docs/app.md +30 -8
  13. package/docs/config.md +4 -0
  14. package/docs/mouse-gestures.md +60 -1
  15. package/docs/proposals/LAT-004-interactive-overlay-actors.md +1 -1
  16. package/docs/proposals/LAT-005-action-runtime-product-spine.md +914 -0
  17. package/docs/proposals/LAT-006-mira-in-lattices.md +553 -0
  18. package/docs/proposals/LAT-007-unified-app-shell.md +128 -0
  19. package/docs/reference/dewey.config.ts +2 -2
  20. package/docs/release.md +171 -0
  21. package/docs/repo-structure.md +5 -5
  22. package/docs/voice.md +11 -27
  23. package/package.json +11 -10
  24. package/apps/mac/Package.swift +0 -27
  25. package/apps/mac/Sources/AppShell/App.swift +0 -26
  26. package/apps/mac/Sources/AppShell/AppActivationCoordinator.swift +0 -27
  27. package/apps/mac/Sources/AppShell/AppDelegate.swift +0 -189
  28. package/apps/mac/Sources/AppShell/AppServicesBootstrap.swift +0 -25
  29. package/apps/mac/Sources/AppShell/AppShellView.swift +0 -171
  30. package/apps/mac/Sources/AppShell/AppUpdater.swift +0 -305
  31. package/apps/mac/Sources/AppShell/CliActionLauncher.swift +0 -50
  32. package/apps/mac/Sources/AppShell/HomeDashboardView.swift +0 -133
  33. package/apps/mac/Sources/AppShell/HotkeyBootstrap.swift +0 -87
  34. package/apps/mac/Sources/AppShell/KeyRecorderView.swift +0 -210
  35. package/apps/mac/Sources/AppShell/LatticesRuntime.swift +0 -104
  36. package/apps/mac/Sources/AppShell/MainView.swift +0 -847
  37. package/apps/mac/Sources/AppShell/MainWindow.swift +0 -83
  38. package/apps/mac/Sources/AppShell/MenuBarController.swift +0 -177
  39. package/apps/mac/Sources/AppShell/OnboardingView.swift +0 -483
  40. package/apps/mac/Sources/AppShell/PermissionsAssistantView.swift +0 -366
  41. package/apps/mac/Sources/AppShell/PermissionsAssistantWindow.swift +0 -70
  42. package/apps/mac/Sources/AppShell/Preferences.swift +0 -297
  43. package/apps/mac/Sources/AppShell/SettingsView.swift +0 -3163
  44. package/apps/mac/Sources/AppShell/SettingsWindow.swift +0 -34
  45. package/apps/mac/Sources/AppShell/WorkspaceInspectorPresenter.swift +0 -13
  46. package/apps/mac/Sources/Core/Actions/HotkeyManager.swift +0 -256
  47. package/apps/mac/Sources/Core/Actions/HotkeyStore.swift +0 -399
  48. package/apps/mac/Sources/Core/Actions/IntentEngine.swift +0 -988
  49. package/apps/mac/Sources/Core/Actions/IntentSchema.swift +0 -94
  50. package/apps/mac/Sources/Core/Actions/Intents/CreateLayerIntent.swift +0 -54
  51. package/apps/mac/Sources/Core/Actions/Intents/DistributeIntent.swift +0 -56
  52. package/apps/mac/Sources/Core/Actions/Intents/FocusIntent.swift +0 -69
  53. package/apps/mac/Sources/Core/Actions/Intents/HelpIntent.swift +0 -41
  54. package/apps/mac/Sources/Core/Actions/Intents/KillIntent.swift +0 -47
  55. package/apps/mac/Sources/Core/Actions/Intents/LatticeIntent.swift +0 -53
  56. package/apps/mac/Sources/Core/Actions/Intents/LaunchIntent.swift +0 -67
  57. package/apps/mac/Sources/Core/Actions/Intents/ListSessionsIntent.swift +0 -32
  58. package/apps/mac/Sources/Core/Actions/Intents/ListWindowsIntent.swift +0 -30
  59. package/apps/mac/Sources/Core/Actions/Intents/ScanIntent.swift +0 -52
  60. package/apps/mac/Sources/Core/Actions/Intents/SearchIntent.swift +0 -190
  61. package/apps/mac/Sources/Core/Actions/Intents/SwitchLayerIntent.swift +0 -50
  62. package/apps/mac/Sources/Core/Actions/Intents/TileIntent.swift +0 -61
  63. package/apps/mac/Sources/Core/Actions/PaletteCommand.swift +0 -439
  64. package/apps/mac/Sources/Core/Actions/VoiceIntentResolver.swift +0 -713
  65. package/apps/mac/Sources/Core/Companion/CompanionActivityLog.swift +0 -70
  66. package/apps/mac/Sources/Core/Companion/CompanionKeyboardController.swift +0 -141
  67. package/apps/mac/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +0 -454
  68. package/apps/mac/Sources/Core/Companion/LatticesCompanionCockpit.swift +0 -555
  69. package/apps/mac/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +0 -629
  70. package/apps/mac/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +0 -204
  71. package/apps/mac/Sources/Core/Companion/LatticesDeckHost.swift +0 -1463
  72. package/apps/mac/Sources/Core/Daemon/DaemonProtocol.swift +0 -114
  73. package/apps/mac/Sources/Core/Daemon/DaemonServer.swift +0 -427
  74. package/apps/mac/Sources/Core/Daemon/LatticesApi.swift +0 -2965
  75. package/apps/mac/Sources/Core/Desktop/AccessibilityTextExtractor.swift +0 -111
  76. package/apps/mac/Sources/Core/Desktop/AppTypeClassifier.swift +0 -106
  77. package/apps/mac/Sources/Core/Desktop/DesktopModel.swift +0 -331
  78. package/apps/mac/Sources/Core/Desktop/DesktopModelTypes.swift +0 -73
  79. package/apps/mac/Sources/Core/Desktop/InventoryManager.swift +0 -35
  80. package/apps/mac/Sources/Core/Desktop/InventoryPath.swift +0 -43
  81. package/apps/mac/Sources/Core/Desktop/MouseFinder.swift +0 -527
  82. package/apps/mac/Sources/Core/Desktop/OcrModel.swift +0 -467
  83. package/apps/mac/Sources/Core/Desktop/OcrStore.swift +0 -329
  84. package/apps/mac/Sources/Core/Desktop/PlacementSpec.swift +0 -195
  85. package/apps/mac/Sources/Core/Desktop/SessionWindowLocator.swift +0 -139
  86. package/apps/mac/Sources/Core/Desktop/TilePickerView.swift +0 -209
  87. package/apps/mac/Sources/Core/Desktop/WindowCapture.swift +0 -33
  88. package/apps/mac/Sources/Core/Desktop/WindowDragSnapController.swift +0 -429
  89. package/apps/mac/Sources/Core/Desktop/WindowPreviewCard.swift +0 -100
  90. package/apps/mac/Sources/Core/Desktop/WindowPreviewStore.swift +0 -112
  91. package/apps/mac/Sources/Core/Desktop/WindowSelectionStore.swift +0 -76
  92. package/apps/mac/Sources/Core/Desktop/WindowTiler.swift +0 -2222
  93. package/apps/mac/Sources/Core/Input/EventTapBreaker.swift +0 -124
  94. package/apps/mac/Sources/Core/Input/EventTapThread.swift +0 -54
  95. package/apps/mac/Sources/Core/Input/InputCaptureResetCenter.swift +0 -20
  96. package/apps/mac/Sources/Core/Input/KeyboardRemapConfig.swift +0 -69
  97. package/apps/mac/Sources/Core/Input/KeyboardRemapController.swift +0 -346
  98. package/apps/mac/Sources/Core/Input/KeyboardRemapStore.swift +0 -141
  99. package/apps/mac/Sources/Core/Input/MouseGestureConfig.swift +0 -499
  100. package/apps/mac/Sources/Core/Input/MouseGestureController.swift +0 -2583
  101. package/apps/mac/Sources/Core/Input/MouseInputDeviceStore.swift +0 -98
  102. package/apps/mac/Sources/Core/Input/MouseInputEventViewer.swift +0 -272
  103. package/apps/mac/Sources/Core/Input/MouseShortcutStore.swift +0 -170
  104. package/apps/mac/Sources/Core/Input/SecureEventInputMonitor.swift +0 -39
  105. package/apps/mac/Sources/Core/Input/ShapeRecognizer.swift +0 -624
  106. package/apps/mac/Sources/Core/Input/TapBudgetMeter.swift +0 -56
  107. package/apps/mac/Sources/Core/Overlays/AppWindowShell.swift +0 -63
  108. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeState.swift +0 -1566
  109. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeView.swift +0 -1927
  110. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeWindow.swift +0 -196
  111. package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteView.swift +0 -307
  112. package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +0 -67
  113. package/apps/mac/Sources/Core/Overlays/HUD/CheatSheetHUD.swift +0 -576
  114. package/apps/mac/Sources/Core/Overlays/HUD/HUDBottomBar.swift +0 -279
  115. package/apps/mac/Sources/Core/Overlays/HUD/HUDController.swift +0 -1158
  116. package/apps/mac/Sources/Core/Overlays/HUD/HUDLeftBar.swift +0 -849
  117. package/apps/mac/Sources/Core/Overlays/HUD/HUDMinimap.swift +0 -179
  118. package/apps/mac/Sources/Core/Overlays/HUD/HUDRightBar.swift +0 -596
  119. package/apps/mac/Sources/Core/Overlays/HUD/HUDState.swift +0 -367
  120. package/apps/mac/Sources/Core/Overlays/HUD/HUDTopBar.swift +0 -243
  121. package/apps/mac/Sources/Core/Overlays/HUD/LauncherHUD.swift +0 -334
  122. package/apps/mac/Sources/Core/Overlays/HUD/LayerBezel.swift +0 -203
  123. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchState.swift +0 -280
  124. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchView.swift +0 -422
  125. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchWindow.swift +0 -94
  126. package/apps/mac/Sources/Core/Overlays/OverlayPanelShell.swift +0 -241
  127. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapState.swift +0 -3135
  128. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapView.swift +0 -3977
  129. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapWindowController.swift +0 -119
  130. package/apps/mac/Sources/Core/Overlays/ScreenOverlayCanvasController.swift +0 -1217
  131. package/apps/mac/Sources/Core/Overlays/Voice/VoiceCommandWindow.swift +0 -1575
  132. package/apps/mac/Sources/Core/Pi/PiAuthNextStepCard.swift +0 -148
  133. package/apps/mac/Sources/Core/Pi/PiAuthPromptCard.swift +0 -90
  134. package/apps/mac/Sources/Core/Pi/PiChatDock.swift +0 -564
  135. package/apps/mac/Sources/Core/Pi/PiChatSession.swift +0 -1948
  136. package/apps/mac/Sources/Core/Pi/PiInstallCallout.swift +0 -86
  137. package/apps/mac/Sources/Core/Pi/PiProviderSetupCallout.swift +0 -99
  138. package/apps/mac/Sources/Core/Pi/PiWorkspaceView.swift +0 -510
  139. package/apps/mac/Sources/Core/System/Capability.swift +0 -79
  140. package/apps/mac/Sources/Core/System/DiagnosticLog.swift +0 -373
  141. package/apps/mac/Sources/Core/System/EventBus.swift +0 -31
  142. package/apps/mac/Sources/Core/System/PermissionChecker.swift +0 -224
  143. package/apps/mac/Sources/Core/System/ProcessModel.swift +0 -199
  144. package/apps/mac/Sources/Core/System/ProcessQuery.swift +0 -151
  145. package/apps/mac/Sources/Core/System/SystemTelemetryMonitor.swift +0 -273
  146. package/apps/mac/Sources/Core/Voice/AdvisorLearningStore.swift +0 -90
  147. package/apps/mac/Sources/Core/Voice/AgentSession.swift +0 -377
  148. package/apps/mac/Sources/Core/Voice/AudioProvider.swift +0 -555
  149. package/apps/mac/Sources/Core/Voice/HandsOffSession.swift +0 -839
  150. package/apps/mac/Sources/Core/Voice/VoiceChatView.swift +0 -192
  151. package/apps/mac/Sources/Core/Voice/VoxClient.swift +0 -454
  152. package/apps/mac/Sources/Core/Workspace/Project.swift +0 -28
  153. package/apps/mac/Sources/Core/Workspace/ProjectScanner.swift +0 -141
  154. package/apps/mac/Sources/Core/Workspace/SessionLayerStore.swift +0 -285
  155. package/apps/mac/Sources/Core/Workspace/SessionManager.swift +0 -75
  156. package/apps/mac/Sources/Core/Workspace/Terminal/Terminal.swift +0 -259
  157. package/apps/mac/Sources/Core/Workspace/Terminal/TerminalQuery.swift +0 -156
  158. package/apps/mac/Sources/Core/Workspace/Terminal/TerminalSynthesizer.swift +0 -200
  159. package/apps/mac/Sources/Core/Workspace/Tmux/TmuxModel.swift +0 -60
  160. package/apps/mac/Sources/Core/Workspace/Tmux/TmuxQuery.swift +0 -105
  161. package/apps/mac/Sources/Core/Workspace/WorkspaceManager.swift +0 -1027
  162. package/apps/mac/Sources/UI/ActionRow.swift +0 -78
  163. package/apps/mac/Sources/UI/OrphanRow.swift +0 -129
  164. package/apps/mac/Sources/UI/ProjectRow.swift +0 -368
  165. package/apps/mac/Sources/UI/TabGroupRow.swift +0 -178
  166. package/apps/mac/Sources/UI/Theme.swift +0 -164
  167. package/apps/mac/Tests/StageDragTests.swift +0 -333
  168. package/apps/mac/Tests/StageJoinTests.swift +0 -313
  169. package/apps/mac/Tests/StageManagerTests.swift +0 -280
  170. package/apps/mac/Tests/StageTileTests.swift +0 -353
  171. package/swift/Package.swift +0 -20
  172. package/swift/Sources/DeckKit/DeckAction.swift +0 -51
  173. package/swift/Sources/DeckKit/DeckBridgeSecurity.swift +0 -152
  174. package/swift/Sources/DeckKit/DeckCockpit.swift +0 -82
  175. package/swift/Sources/DeckKit/DeckHost.swift +0 -7
  176. package/swift/Sources/DeckKit/DeckManifest.swift +0 -145
  177. package/swift/Sources/DeckKit/DeckRuntimeSnapshot.swift +0 -533
  178. package/swift/Sources/DeckKit/DeckTrackpad.swift +0 -63
  179. package/swift/Sources/DeckKit/DeckValue.swift +0 -93
  180. package/swift/Sources/DeckKit/DeckVoiceError.swift +0 -88
  181. package/swift/Tests/DeckKitTests/DeckKitTests.swift +0 -286
@@ -1,367 +0,0 @@
1
- import Combine
2
- import CoreGraphics
3
- import Foundation
4
-
5
- // MARK: - HUDItem
6
-
7
- enum HUDItem: Identifiable, Equatable {
8
- case project(Project)
9
- case window(WindowEntry)
10
-
11
- var id: String {
12
- switch self {
13
- case .project(let p): return "project-\(p.id)"
14
- case .window(let w): return "window-\(w.wid)"
15
- }
16
- }
17
-
18
- var displayName: String {
19
- switch self {
20
- case .project(let p): return p.name
21
- case .window(let w): return w.title
22
- }
23
- }
24
-
25
- var appName: String? {
26
- switch self {
27
- case .project: return nil
28
- case .window(let w): return w.app
29
- }
30
- }
31
-
32
- static func == (lhs: HUDItem, rhs: HUDItem) -> Bool {
33
- lhs.id == rhs.id
34
- }
35
- }
36
-
37
- // MARK: - Focus target
38
-
39
- enum HUDFocus: Equatable {
40
- case search // typing into the left bar search field
41
- case list // navigating the left bar item list
42
- case inspector // tabbed over to the right bar
43
- }
44
-
45
- // MARK: - HUDState
46
-
47
- final class HUDState: ObservableObject {
48
- private static let leftSidebarWidthKey = "hud.leftSidebarWidth"
49
- private static let defaultLeftSidebarWidth: CGFloat = 320
50
- private static let minLeftSidebarWidth: CGFloat = 260
51
- private static let maxLeftSidebarWidth: CGFloat = 420
52
-
53
- @Published var selectedItem: HUDItem?
54
- @Published var pinnedItem: HUDItem?
55
- @Published var hoveredPreviewItem: HUDItem?
56
- @Published var feedbackMessage: String?
57
- @Published var query: String = ""
58
- @Published var selectedIndex: Int = 0
59
- @Published var focus: HUDFocus = .search
60
- @Published var voiceActive: Bool = false
61
- @Published var expandedSections: Set<Int> = [2]
62
- @Published var leftSidebarWidth: CGFloat = {
63
- let saved = UserDefaults.standard.double(forKey: HUDState.leftSidebarWidthKey)
64
- if saved > 0 {
65
- return min(max(CGFloat(saved), HUDState.minLeftSidebarWidth), HUDState.maxLeftSidebarWidth)
66
- }
67
- return HUDState.defaultLeftSidebarWidth
68
- }()
69
-
70
- /// Multi-select for tiling — set of item IDs
71
- @Published var selectedItems: Set<String> = []
72
-
73
- enum MinimapMode: Equatable { case hidden, docked, expanded }
74
- @Published var minimapMode: MinimapMode = .docked
75
-
76
- // MARK: - Tile mode
77
-
78
- @Published var tileMode: Bool = false
79
-
80
- /// Snapshot of window positions taken when entering tile mode
81
- struct WindowSnapshot {
82
- let wid: UInt32
83
- let pid: Int32
84
- let frame: CGRect
85
- }
86
- var tileSnapshot: [WindowSnapshot] = []
87
- var tiledWindows: Set<UInt32> = []
88
-
89
- /// Pre-computed grid layout — calculated on HUD show, applied instantly on T press
90
- var precomputedGrid: [(wid: UInt32, pid: Int32, frame: CGRect)] = []
91
-
92
- /// Snapshot of the flat item list — set by HUDLeftBar so key handler can index into it
93
- var flatItems: [HUDItem] = []
94
-
95
- var hoverPreviewAnchorScreenY: CGFloat?
96
- var previewInteractionActive: Bool = false
97
-
98
- /// Section offsets for number-key jumping (1=Projects, 2=Terminals, 3=Chrome, 4=Claude)
99
- var sectionOffsets: [Int: Int] = [:]
100
-
101
- private var selectionAnchorID: String?
102
- private var touchedSections: Set<Int> = []
103
- private var feedbackClearWorkItem: DispatchWorkItem?
104
-
105
- var leftSidebarWidthRange: ClosedRange<CGFloat> {
106
- Self.minLeftSidebarWidth...Self.maxLeftSidebarWidth
107
- }
108
-
109
- func setLeftSidebarWidth(_ width: CGFloat) {
110
- let clamped = min(max(width, Self.minLeftSidebarWidth), Self.maxLeftSidebarWidth)
111
- guard abs(clamped - leftSidebarWidth) > 0.5 else { return }
112
- leftSidebarWidth = clamped
113
- UserDefaults.standard.set(Double(clamped), forKey: Self.leftSidebarWidthKey)
114
- }
115
-
116
- func isSectionExpanded(_ key: Int) -> Bool {
117
- expandedSections.contains(key)
118
- }
119
-
120
- func toggleSection(_ key: Int) {
121
- if expandedSections.contains(key) {
122
- expandedSections.remove(key)
123
- } else {
124
- expandedSections.insert(key)
125
- }
126
- touchedSections.insert(key)
127
- }
128
-
129
- func resetSectionDefaults(hasRunningProjects: Bool) {
130
- expandedSections = [2]
131
- if hasRunningProjects {
132
- expandedSections.insert(1)
133
- }
134
- touchedSections = []
135
- }
136
-
137
- func syncAutoSectionDefaults(hasRunningProjects: Bool) {
138
- if !touchedSections.contains(2) {
139
- expandedSections.insert(2)
140
- }
141
- if !touchedSections.contains(1) {
142
- if hasRunningProjects {
143
- expandedSections.insert(1)
144
- } else {
145
- expandedSections.remove(1)
146
- }
147
- }
148
- }
149
-
150
- var effectiveSelectionIDs: Set<String> {
151
- if selectedItems.isEmpty {
152
- if let selectedItem {
153
- return [selectedItem.id]
154
- }
155
- return []
156
- }
157
-
158
- var ids = selectedItems
159
- if let selectedItem {
160
- ids.insert(selectedItem.id)
161
- }
162
- return ids
163
- }
164
-
165
- var multiSelectionCount: Int {
166
- let count = effectiveSelectionIDs.count
167
- return count > 1 ? count : 0
168
- }
169
-
170
- var transientPreviewItem: HUDItem? {
171
- if let hoveredPreviewItem {
172
- return hoveredPreviewItem
173
- }
174
- if pinnedItem == nil && focus == .list {
175
- return selectedItem
176
- }
177
- return nil
178
- }
179
-
180
- var inspectorCandidateItem: HUDItem? {
181
- if let pinnedItem {
182
- return pinnedItem
183
- }
184
- if let hoveredPreviewItem {
185
- return hoveredPreviewItem
186
- }
187
- return selectedItem
188
- }
189
-
190
- func clearMultiSelection() {
191
- selectedItems = []
192
- if let selectedItem {
193
- selectionAnchorID = selectedItem.id
194
- } else {
195
- selectionAnchorID = nil
196
- }
197
- }
198
-
199
- func selectSingle(_ item: HUDItem, index: Int) {
200
- selectedItem = item
201
- selectedIndex = index
202
- selectedItems = []
203
- selectionAnchorID = item.id
204
- }
205
-
206
- func showFeedback(_ message: String, autoClearAfter delay: TimeInterval = 0.9) {
207
- feedbackClearWorkItem?.cancel()
208
- feedbackMessage = message
209
-
210
- let clearWorkItem = DispatchWorkItem { [weak self] in
211
- guard self?.feedbackMessage == message else { return }
212
- self?.feedbackMessage = nil
213
- }
214
- feedbackClearWorkItem = clearWorkItem
215
- DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: clearWorkItem)
216
- }
217
-
218
- func pinInspector(_ item: HUDItem, source: String = "selection") {
219
- let timed = AppFeedback.shared.beginTimed(
220
- "HUD inspect (\(source))",
221
- state: self,
222
- feedback: "Inspecting \(item.displayName)"
223
- )
224
- if let idx = flatItems.firstIndex(of: item) {
225
- selectedIndex = idx
226
- }
227
- selectedItem = item
228
- pinnedItem = item
229
- hoveredPreviewItem = nil
230
- selectedItems = []
231
- selectionAnchorID = item.id
232
- focus = .inspector
233
- DispatchQueue.main.async {
234
- AppFeedback.shared.finish(timed)
235
- }
236
- }
237
-
238
- func pinInspectorCandidate(source: String = "preview") {
239
- guard let item = inspectorCandidateItem else { return }
240
- pinInspector(item, source: source)
241
- }
242
-
243
- func toggleSelection(_ item: HUDItem, index: Int, in items: [HUDItem]) {
244
- let anchorID = selectionAnchorID ?? selectedItem?.id ?? item.id
245
-
246
- if selectedItems.isEmpty, let current = selectedItem {
247
- selectedItems = [current.id]
248
- }
249
-
250
- if selectedItems.contains(item.id) {
251
- selectedItems.remove(item.id)
252
- } else {
253
- selectedItems.insert(item.id)
254
- }
255
-
256
- if selectedItems.isEmpty {
257
- selectSingle(item, index: index)
258
- return
259
- }
260
-
261
- if selectedItems.count == 1,
262
- let remainingID = selectedItems.first,
263
- let remainingIndex = items.firstIndex(where: { $0.id == remainingID }),
264
- let remainingItem = items[safe: remainingIndex] {
265
- selectSingle(remainingItem, index: remainingIndex)
266
- return
267
- }
268
-
269
- selectedItem = item
270
- selectedIndex = index
271
- selectionAnchorID = anchorID
272
- }
273
-
274
- func selectRange(to item: HUDItem, index: Int, in items: [HUDItem]) {
275
- guard !items.isEmpty else { return }
276
-
277
- let anchorID = selectionAnchorID ?? selectedItem?.id ?? item.id
278
- guard let anchorIndex = items.firstIndex(where: { $0.id == anchorID }) else {
279
- selectSingle(item, index: index)
280
- return
281
- }
282
-
283
- let lower = min(anchorIndex, index)
284
- let upper = max(anchorIndex, index)
285
- let ids = Set(items[lower...upper].map(\.id))
286
-
287
- selectedItem = item
288
- selectedIndex = index
289
- selectedItems = ids.count > 1 ? ids : []
290
- selectionAnchorID = anchorID
291
- }
292
-
293
- func moveSelection(by delta: Int, extend: Bool) {
294
- let items = flatItems
295
- guard !items.isEmpty else { return }
296
-
297
- let currentIndex: Int
298
- if let selectedItem,
299
- let idx = items.firstIndex(of: selectedItem) {
300
- currentIndex = idx
301
- } else {
302
- currentIndex = max(0, min(items.count - 1, selectedIndex))
303
- }
304
-
305
- let nextIndex = max(0, min(items.count - 1, currentIndex + delta))
306
- guard let nextItem = items[safe: nextIndex] else { return }
307
-
308
- if extend {
309
- selectRange(to: nextItem, index: nextIndex, in: items)
310
- } else {
311
- selectSingle(nextItem, index: nextIndex)
312
- }
313
- }
314
-
315
- func reconcileSelection(with items: [HUDItem]) {
316
- let validIDs = Set(items.map(\.id))
317
-
318
- selectedItems = selectedItems.intersection(validIDs)
319
- if let selectedItem, !validIDs.contains(selectedItem.id) {
320
- self.selectedItem = nil
321
- }
322
- if let pinnedItem, !validIDs.contains(pinnedItem.id) {
323
- self.pinnedItem = nil
324
- }
325
- if let hoveredPreviewItem, !validIDs.contains(hoveredPreviewItem.id) {
326
- self.hoveredPreviewItem = nil
327
- }
328
- if let selectionAnchorID, !validIDs.contains(selectionAnchorID) {
329
- self.selectionAnchorID = selectedItem?.id
330
- }
331
-
332
- guard !items.isEmpty else {
333
- selectedItem = nil
334
- pinnedItem = nil
335
- hoveredPreviewItem = nil
336
- selectedIndex = 0
337
- selectedItems = []
338
- selectionAnchorID = nil
339
- return
340
- }
341
-
342
- if selectedItem == nil {
343
- let clampedIndex = max(0, min(items.count - 1, selectedIndex))
344
- if let fallback = items[safe: clampedIndex] {
345
- selectedItem = fallback
346
- selectedIndex = clampedIndex
347
- if selectionAnchorID == nil {
348
- selectionAnchorID = fallback.id
349
- }
350
- }
351
- return
352
- }
353
-
354
- if let selectedItem,
355
- let itemIndex = items.firstIndex(of: selectedItem) {
356
- selectedIndex = itemIndex
357
- } else {
358
- selectedIndex = max(0, min(items.count - 1, selectedIndex))
359
- }
360
- }
361
- }
362
-
363
- private extension Array {
364
- subscript(safe index: Int) -> Element? {
365
- indices.contains(index) ? self[index] : nil
366
- }
367
- }
@@ -1,243 +0,0 @@
1
- import SwiftUI
2
-
3
- // MARK: - HUDTopBar
4
-
5
- struct HUDTopBar: View {
6
- @ObservedObject var state: HUDState
7
- @ObservedObject private var handsOff = HandsOffSession.shared
8
- @ObservedObject private var workspace = WorkspaceManager.shared
9
- var onDismiss: () -> Void
10
-
11
- var body: some View {
12
- HStack(spacing: 0) {
13
- // Logo
14
- logo
15
- .padding(.leading, 16)
16
-
17
- // Voice status (when active)
18
- if state.voiceActive {
19
- voiceStatus
20
- .padding(.leading, 12)
21
- }
22
-
23
- Spacer()
24
-
25
- // Layer strip (Hyprland-style)
26
- if let layers = workspace.config?.layers, !layers.isEmpty {
27
- layerStrip(layers)
28
- .padding(.trailing, 12)
29
- }
30
-
31
- // Quick actions
32
- HStack(spacing: 6) {
33
- quickAction(icon: "rectangle.3.group", label: "Map", shortcut: "⌃⌥⇧⌘1") {
34
- onDismiss()
35
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
36
- ScreenMapWindowController.shared.toggle()
37
- }
38
- }
39
-
40
- quickAction(icon: "magnifyingglass", label: "Search", shortcut: "⌃⌥⇧⌘5") {
41
- onDismiss()
42
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
43
- OmniSearchWindow.shared.toggle()
44
- }
45
- }
46
-
47
- quickAction(icon: "text.justify.left", label: "Palette", shortcut: "⇧⌘M") {
48
- onDismiss()
49
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
50
- CommandPaletteWindow.shared.toggle()
51
- }
52
- }
53
- }
54
- .padding(.trailing, 16)
55
- }
56
- .frame(maxWidth: .infinity)
57
- .frame(height: 44)
58
- .background(Palette.bg)
59
- }
60
-
61
- // MARK: - Layer strip (Hyprland-style workspace bar)
62
-
63
- private func layerStrip(_ layers: [Layer]) -> some View {
64
- HStack(spacing: 3) {
65
- ForEach(Array(layers.enumerated()), id: \.element.id) { idx, layer in
66
- let isActive = idx == workspace.activeLayerIndex
67
-
68
- Button {
69
- workspace.focusLayer(index: idx)
70
- } label: {
71
- HStack(spacing: 4) {
72
- Circle()
73
- .fill(isActive ? Palette.running : Palette.textMuted.opacity(0.3))
74
- .frame(width: 5, height: 5)
75
-
76
- Text(layer.label)
77
- .font(Typo.monoBold(9))
78
- .foregroundColor(isActive ? Palette.text : Palette.textMuted)
79
- .lineLimit(1)
80
- }
81
- .padding(.horizontal, 8)
82
- .padding(.vertical, 4)
83
- .background(
84
- RoundedRectangle(cornerRadius: 4)
85
- .fill(isActive ? Palette.surfaceHov : Palette.surface.opacity(0.5))
86
- .overlay(
87
- RoundedRectangle(cornerRadius: 4)
88
- .strokeBorder(isActive ? Palette.running.opacity(0.4) : Palette.border.opacity(0.5), lineWidth: 0.5)
89
- )
90
- )
91
- }
92
- .buttonStyle(.plain)
93
- }
94
- }
95
- }
96
-
97
- // MARK: - Logo
98
-
99
- private var logo: some View {
100
- HStack(spacing: 7) {
101
- latticesGrid
102
- Text("lattices")
103
- .font(Typo.monoBold(11))
104
- .foregroundColor(Palette.textMuted.opacity(0.6))
105
- }
106
- }
107
-
108
- /// 3×3 grid matching the menu bar icon — L-shape bright, rest dim
109
- private var latticesGrid: some View {
110
- let cellSize: CGFloat = 3
111
- let gap: CGFloat = 1.5
112
- let solidCells: Set<Int> = [0, 3, 6, 7, 8]
113
-
114
- return Canvas { ctx, _ in
115
- for row in 0..<3 {
116
- for col in 0..<3 {
117
- let idx = row * 3 + col
118
- let x = CGFloat(col) * (cellSize + gap)
119
- let y = CGFloat(row) * (cellSize + gap)
120
- let rect = CGRect(x: x, y: y, width: cellSize, height: cellSize)
121
- let opacity: Double = solidCells.contains(idx) ? 0.5 : 0.15
122
- ctx.fill(
123
- Path(roundedRect: rect, cornerRadius: 0.5),
124
- with: .color(.white.opacity(opacity))
125
- )
126
- }
127
- }
128
- }
129
- .frame(width: 3 * 3 + 2 * 1.5, height: 3 * 3 + 2 * 1.5)
130
- }
131
-
132
- // MARK: - Voice status
133
-
134
- private var voiceStatus: some View {
135
- HStack(spacing: 8) {
136
- // Pulsing dot + state
137
- Circle()
138
- .fill(voiceColor)
139
- .frame(width: 6, height: 6)
140
- .overlay(
141
- Circle()
142
- .stroke(voiceColor.opacity(0.4), lineWidth: 1)
143
- .scaleEffect(handsOff.state == .listening ? 2.0 : 1.0)
144
- .opacity(handsOff.state == .listening ? 0 : 1)
145
- .animation(
146
- handsOff.state == .listening
147
- ? .easeOut(duration: 1.0).repeatForever(autoreverses: false)
148
- : .default,
149
- value: handsOff.state
150
- )
151
- )
152
-
153
- Text(voiceLabel)
154
- .font(Typo.monoBold(9))
155
- .foregroundColor(voiceColor)
156
-
157
- // Dialogue: last user message → last response
158
- if let transcript = handsOff.lastTranscript {
159
- Rectangle().fill(Palette.border).frame(width: 0.5, height: 16)
160
-
161
- // What user said
162
- HStack(spacing: 3) {
163
- Text("you")
164
- .font(Typo.monoBold(8))
165
- .foregroundColor(Palette.textMuted)
166
- Text(transcript)
167
- .font(Typo.mono(9))
168
- .foregroundColor(Palette.text)
169
- .lineLimit(1)
170
- }
171
- .frame(maxWidth: 300, alignment: .leading)
172
- }
173
-
174
- if let response = handsOff.lastResponse {
175
- // What Lattices said back
176
- HStack(spacing: 3) {
177
- Text("→")
178
- .font(Typo.mono(9))
179
- .foregroundColor(Palette.running)
180
- Text(response)
181
- .font(Typo.mono(9))
182
- .foregroundColor(Palette.textMuted)
183
- .lineLimit(1)
184
- }
185
- .frame(maxWidth: 350, alignment: .leading)
186
- }
187
- }
188
- .padding(.horizontal, 10)
189
- .padding(.vertical, 4)
190
- .background(
191
- RoundedRectangle(cornerRadius: 5)
192
- .fill(voiceColor.opacity(0.08))
193
- .overlay(
194
- RoundedRectangle(cornerRadius: 5)
195
- .strokeBorder(voiceColor.opacity(0.2), lineWidth: 0.5)
196
- )
197
- )
198
- }
199
-
200
- private var voiceColor: Color {
201
- switch handsOff.state {
202
- case .idle: return Palette.running
203
- case .connecting: return Palette.detach
204
- case .listening: return Palette.running
205
- case .thinking: return Palette.detach
206
- }
207
- }
208
-
209
- private var voiceLabel: String {
210
- switch handsOff.state {
211
- case .idle: return "ready"
212
- case .connecting: return "connecting"
213
- case .listening: return "listening"
214
- case .thinking: return "thinking"
215
- }
216
- }
217
-
218
- // MARK: - Quick action button
219
-
220
- private func quickAction(icon: String, label: String, shortcut: String, action: @escaping () -> Void) -> some View {
221
- Button(action: action) {
222
- HStack(spacing: 5) {
223
- Image(systemName: icon)
224
- .font(.system(size: 10))
225
- Text(label)
226
- .font(Typo.mono(10))
227
- }
228
- .foregroundColor(Palette.textMuted)
229
- .padding(.horizontal, 10)
230
- .padding(.vertical, 5)
231
- .background(
232
- RoundedRectangle(cornerRadius: 5)
233
- .fill(Palette.surface)
234
- .overlay(
235
- RoundedRectangle(cornerRadius: 5)
236
- .strokeBorder(Palette.border, lineWidth: 0.5)
237
- )
238
- )
239
- }
240
- .buttonStyle(.plain)
241
- .help("\(label) (\(shortcut))")
242
- }
243
- }