@lattices/cli 0.4.0 → 0.4.2

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/app/Info.plist ADDED
@@ -0,0 +1,30 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleIdentifier</key>
6
+ <string>com.arach.lattices</string>
7
+ <key>CFBundleName</key>
8
+ <string>Lattices</string>
9
+ <key>CFBundleDisplayName</key>
10
+ <string>Lattices</string>
11
+ <key>CFBundleExecutable</key>
12
+ <string>Lattices</string>
13
+ <key>CFBundleIconFile</key>
14
+ <string>AppIcon</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>APPL</string>
17
+ <key>CFBundleVersion</key>
18
+ <string>0.4.2</string>
19
+ <key>CFBundleShortVersionString</key>
20
+ <string>0.4.2</string>
21
+ <key>LSMinimumSystemVersion</key>
22
+ <string>13.0</string>
23
+ <key>LSUIElement</key>
24
+ <true/>
25
+ <key>NSHighResolutionCapable</key>
26
+ <true/>
27
+ <key>NSSupportsAutomaticTermination</key>
28
+ <true/>
29
+ </dict>
30
+ </plist>
@@ -6,6 +6,8 @@
6
6
  <string>com.arach.lattices</string>
7
7
  <key>CFBundleName</key>
8
8
  <string>Lattices</string>
9
+ <key>CFBundleDisplayName</key>
10
+ <string>Lattices</string>
9
11
  <key>CFBundleExecutable</key>
10
12
  <string>Lattices</string>
11
13
  <key>CFBundleIconFile</key>
@@ -13,11 +15,15 @@
13
15
  <key>CFBundlePackageType</key>
14
16
  <string>APPL</string>
15
17
  <key>CFBundleVersion</key>
16
- <string>1</string>
18
+ <string>0.4.2</string>
17
19
  <key>CFBundleShortVersionString</key>
18
- <string>0.1.0</string>
20
+ <string>0.4.2</string>
21
+ <key>LSMinimumSystemVersion</key>
22
+ <string>13.0</string>
19
23
  <key>LSUIElement</key>
20
24
  <true/>
25
+ <key>NSHighResolutionCapable</key>
26
+ <true/>
21
27
  <key>NSSupportsAutomaticTermination</key>
22
28
  <true/>
23
29
  </dict>
@@ -0,0 +1,139 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>files</key>
6
+ <dict>
7
+ <key>Resources/AppIcon.icns</key>
8
+ <data>
9
+ 3sIZmtGJHMo2S/XvIMl46SOHcFY=
10
+ </data>
11
+ <key>Resources/tap.wav</key>
12
+ <data>
13
+ eOpp5td/ovQGMumXPpwy4Vyt/uc=
14
+ </data>
15
+ </dict>
16
+ <key>files2</key>
17
+ <dict>
18
+ <key>Resources/AppIcon.icns</key>
19
+ <dict>
20
+ <key>hash2</key>
21
+ <data>
22
+ LZsztS/9I1hmuQmDOk+anfxOpqVryB3y4a1kwSaUK4s=
23
+ </data>
24
+ </dict>
25
+ <key>Resources/tap.wav</key>
26
+ <dict>
27
+ <key>hash2</key>
28
+ <data>
29
+ K4QV08FuKEJR29hhgUbEG7Em3J6zHYpGKmGWdnZopzs=
30
+ </data>
31
+ </dict>
32
+ </dict>
33
+ <key>rules</key>
34
+ <dict>
35
+ <key>^Resources/</key>
36
+ <true/>
37
+ <key>^Resources/.*\.lproj/</key>
38
+ <dict>
39
+ <key>optional</key>
40
+ <true/>
41
+ <key>weight</key>
42
+ <real>1000</real>
43
+ </dict>
44
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
45
+ <dict>
46
+ <key>omit</key>
47
+ <true/>
48
+ <key>weight</key>
49
+ <real>1100</real>
50
+ </dict>
51
+ <key>^Resources/Base\.lproj/</key>
52
+ <dict>
53
+ <key>weight</key>
54
+ <real>1010</real>
55
+ </dict>
56
+ <key>^version.plist$</key>
57
+ <true/>
58
+ </dict>
59
+ <key>rules2</key>
60
+ <dict>
61
+ <key>.*\.dSYM($|/)</key>
62
+ <dict>
63
+ <key>weight</key>
64
+ <real>11</real>
65
+ </dict>
66
+ <key>^(.*/)?\.DS_Store$</key>
67
+ <dict>
68
+ <key>omit</key>
69
+ <true/>
70
+ <key>weight</key>
71
+ <real>2000</real>
72
+ </dict>
73
+ <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
74
+ <dict>
75
+ <key>nested</key>
76
+ <true/>
77
+ <key>weight</key>
78
+ <real>10</real>
79
+ </dict>
80
+ <key>^.*</key>
81
+ <true/>
82
+ <key>^Info\.plist$</key>
83
+ <dict>
84
+ <key>omit</key>
85
+ <true/>
86
+ <key>weight</key>
87
+ <real>20</real>
88
+ </dict>
89
+ <key>^PkgInfo$</key>
90
+ <dict>
91
+ <key>omit</key>
92
+ <true/>
93
+ <key>weight</key>
94
+ <real>20</real>
95
+ </dict>
96
+ <key>^Resources/</key>
97
+ <dict>
98
+ <key>weight</key>
99
+ <real>20</real>
100
+ </dict>
101
+ <key>^Resources/.*\.lproj/</key>
102
+ <dict>
103
+ <key>optional</key>
104
+ <true/>
105
+ <key>weight</key>
106
+ <real>1000</real>
107
+ </dict>
108
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
109
+ <dict>
110
+ <key>omit</key>
111
+ <true/>
112
+ <key>weight</key>
113
+ <real>1100</real>
114
+ </dict>
115
+ <key>^Resources/Base\.lproj/</key>
116
+ <dict>
117
+ <key>weight</key>
118
+ <real>1010</real>
119
+ </dict>
120
+ <key>^[^/]+$</key>
121
+ <dict>
122
+ <key>nested</key>
123
+ <true/>
124
+ <key>weight</key>
125
+ <real>10</real>
126
+ </dict>
127
+ <key>^embedded\.provisionprofile$</key>
128
+ <dict>
129
+ <key>weight</key>
130
+ <real>20</real>
131
+ </dict>
132
+ <key>^version\.plist$</key>
133
+ <dict>
134
+ <key>weight</key>
135
+ <real>20</real>
136
+ </dict>
137
+ </dict>
138
+ </dict>
139
+ </plist>
@@ -0,0 +1,15 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <!-- App sandbox disabled — Lattices needs direct access to tmux, processes, and the filesystem -->
6
+ <key>com.apple.security.app-sandbox</key>
7
+ <false/>
8
+
9
+ <!-- Network: localhost WebSocket daemon for CLI/agent communication -->
10
+ <key>com.apple.security.network.server</key>
11
+ <true/>
12
+ <key>com.apple.security.network.client</key>
13
+ <true/>
14
+ </dict>
15
+ </plist>
Binary file
@@ -2,9 +2,9 @@ import SwiftUI
2
2
 
3
3
  /// A single action row with shortcut badge, label, optional icon, and hotkey hint.
4
4
  struct ActionRow: View {
5
- let shortcut: String
6
5
  let label: String
7
- var hotkey: String? = nil
6
+ var detail: String? = nil
7
+ var hotkeyTokens: [String] = []
8
8
  var icon: String? = nil
9
9
  var accentColor: Color = Palette.textDim
10
10
  var action: () -> Void
@@ -14,41 +14,58 @@ struct ActionRow: View {
14
14
  var body: some View {
15
15
  Button(action: action) {
16
16
  HStack(spacing: 10) {
17
- // Shortcut badge
18
- Text(shortcut)
19
- .font(Typo.monoBold(10))
20
- .foregroundColor(accentColor)
21
- .frame(width: 18, height: 18)
22
- .background(
23
- RoundedRectangle(cornerRadius: 4)
24
- .fill(accentColor.opacity(0.12))
25
- )
26
-
27
17
  // Icon
28
18
  if let icon {
29
- Image(systemName: icon)
30
- .font(.system(size: 11, weight: .medium))
31
- .foregroundColor(isHovered ? Palette.text : Palette.textDim)
32
- .frame(width: 14)
19
+ ZStack {
20
+ RoundedRectangle(cornerRadius: 5)
21
+ .fill(accentColor.opacity(isHovered ? 0.18 : 0.12))
22
+ Image(systemName: icon)
23
+ .font(.system(size: 11, weight: .medium))
24
+ .foregroundColor(isHovered ? Palette.text : accentColor)
25
+ }
26
+ .frame(width: 22, height: 22)
33
27
  }
34
28
 
35
29
  // Label
36
- Text(label)
37
- .font(Typo.mono(12))
38
- .foregroundColor(isHovered ? Palette.text : Palette.textDim)
39
- .lineLimit(1)
30
+ VStack(alignment: .leading, spacing: detail == nil ? 0 : 2) {
31
+ Text(label)
32
+ .font(Typo.body(12))
33
+ .foregroundColor(isHovered ? Palette.text : Palette.textDim)
34
+ .lineLimit(1)
35
+
36
+ if let detail {
37
+ Text(detail)
38
+ .font(Typo.mono(9))
39
+ .foregroundColor(Palette.textMuted)
40
+ .lineLimit(1)
41
+ }
42
+ }
40
43
 
41
44
  Spacer()
42
45
 
43
46
  // Hotkey
44
- if let hotkey {
45
- Text(hotkey)
46
- .font(Typo.mono(10))
47
- .foregroundColor(Palette.textMuted)
47
+ if !hotkeyTokens.isEmpty {
48
+ HStack(spacing: 4) {
49
+ ForEach(hotkeyTokens, id: \.self) { token in
50
+ Text(token)
51
+ .font(Typo.monoBold(8))
52
+ .foregroundColor(Palette.textMuted)
53
+ .padding(.horizontal, token.count > 3 ? 6 : 5)
54
+ .padding(.vertical, 3)
55
+ .background(
56
+ RoundedRectangle(cornerRadius: 4)
57
+ .fill(Palette.surface)
58
+ .overlay(
59
+ RoundedRectangle(cornerRadius: 4)
60
+ .strokeBorder(Palette.border, lineWidth: 0.5)
61
+ )
62
+ )
63
+ }
64
+ }
48
65
  }
49
66
  }
50
- .padding(.horizontal, 10)
51
- .padding(.vertical, 6)
67
+ .padding(.horizontal, 12)
68
+ .padding(.vertical, 8)
52
69
  .background(
53
70
  RoundedRectangle(cornerRadius: 5)
54
71
  .fill(isHovered ? Palette.surfaceHov : Color.clear)
@@ -99,6 +99,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
99
99
  }
100
100
  }
101
101
  store.register(action: .hud) { HUDController.shared.toggle() }
102
+ store.register(action: .mouseFinder) { MouseFinder.shared.find() }
102
103
 
103
104
  // Pre-render HUD panels off-screen for instant first open
104
105
  DispatchQueue.main.async { HUDController.shared.warmUp() }
@@ -157,7 +158,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
157
158
  LatticesApi.setup()
158
159
  DaemonServer.shared.start()
159
160
  AgentPool.shared.start()
160
- HandsOffSession.shared.start()
161
161
  diag.finish(tBoot)
162
162
 
163
163
  // --diagnostics flag: auto-open diagnostics panel on launch
@@ -228,11 +228,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
228
228
 
229
229
  let actions: [(String, String, Selector)] = [
230
230
  ("Command Palette", "⌘⇧M", #selector(menuCommandPalette)),
231
- ("Unified Window", "", #selector(menuScreenMap)),
232
- ("HUD", "", #selector(menuHUD)),
233
- ("Window Bezel", "", #selector(menuWindowBezel)),
234
- ("Cheat Sheet", "", #selector(menuCheatSheet)),
235
- ("Omni Search", "", #selector(menuOmniSearch)),
231
+ ("Workspace", "", #selector(menuWorkspace)),
232
+ ("Assistant", "", #selector(menuAssistant)),
233
+ ("Help & Shortcuts", "", #selector(menuDocs)),
236
234
  ]
237
235
  for (title, shortcut, action) in actions {
238
236
  let item = NSMenuItem(title: title, action: action, keyEquivalent: "")
@@ -263,7 +261,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
263
261
  }
264
262
 
265
263
  @objc private func menuCommandPalette() { CommandPaletteWindow.shared.toggle() }
266
- @objc private func menuScreenMap() { ScreenMapWindowController.shared.toggle() }
264
+ @objc private func menuWorkspace() { ScreenMapWindowController.shared.showPage(.home) }
265
+ @objc private func menuAssistant() {
266
+ if AudioLayer.shared.isListening || VoiceCommandWindow.shared.isVisible {
267
+ VoiceCommandWindow.shared.toggle()
268
+ } else {
269
+ OmniSearchWindow.shared.show()
270
+ }
271
+ }
272
+ @objc private func menuDocs() { ScreenMapWindowController.shared.showPage(.docs) }
267
273
  @objc private func menuHUD() { HUDController.shared.toggle() }
268
274
  @objc private func menuWindowBezel() { WindowBezel.showBezelForFrontmostWindow() }
269
275
  @objc private func menuCheatSheet() { CheatSheetHUD.shared.toggle() }
@@ -134,7 +134,24 @@ final class DesktopModel: ObservableObject {
134
134
 
135
135
  // MARK: - Polling
136
136
 
137
+ private var lastPollTime: Date = .distantPast
138
+ private static let minPollInterval: TimeInterval = 1.0
139
+
140
+ /// Poll only if stale. Call `forcePoll()` to bypass the freshness check.
137
141
  func poll() {
142
+ let now = Date()
143
+ guard now.timeIntervalSince(lastPollTime) >= Self.minPollInterval else { return }
144
+ lastPollTime = now
145
+ performPoll()
146
+ }
147
+
148
+ /// Force a poll regardless of freshness — use sparingly.
149
+ func forcePoll() {
150
+ lastPollTime = Date()
151
+ performPoll()
152
+ }
153
+
154
+ private func performPoll() {
138
155
  guard let list = CGWindowListCopyWindowInfo(
139
156
  [.optionAll, .excludeDesktopElements],
140
157
  kCGNullWindowID
@@ -225,8 +242,11 @@ final class DesktopModel: ObservableObject {
225
242
  if markFrontmost, let frontmostWid {
226
243
  interactions[frontmostWid] = interactionTime
227
244
  }
228
- self.windows = fresh
229
- self.interactionDates = interactions
245
+ // Only publish if something actually changed — avoids unnecessary SwiftUI re-renders
246
+ if changed || markFrontmost {
247
+ self.windows = fresh
248
+ self.interactionDates = interactions
249
+ }
230
250
  self.lastFrontmostWid = frontmostWid
231
251
  }
232
252
 
@@ -255,6 +275,10 @@ final class DesktopModel: ObservableObject {
255
275
 
256
276
  for (pid, wids) in byPid {
257
277
  let axApp = AXUIElementCreateApplication(pid)
278
+
279
+ // Set a timeout so unresponsive apps (video calls, etc.) don't block the poll
280
+ AXUIElementSetMessagingTimeout(axApp, 0.3)
281
+
258
282
  var axWindowsRef: CFTypeRef?
259
283
  guard AXUIElementCopyAttributeValue(axApp, kAXWindowsAttribute as CFString, &axWindowsRef) == .success,
260
284
  let axWindows = axWindowsRef as? [AXUIElement] else { continue }