@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 +30 -0
- package/app/Lattices.app/Contents/Info.plist +8 -2
- package/app/Lattices.app/Contents/MacOS/Lattices +0 -0
- package/app/Lattices.app/Contents/Resources/AppIcon.icns +0 -0
- package/app/Lattices.app/Contents/Resources/tap.wav +0 -0
- package/app/Lattices.app/Contents/_CodeSignature/CodeResources +139 -0
- package/app/Lattices.entitlements +15 -0
- package/app/Resources/tap.wav +0 -0
- package/app/Sources/ActionRow.swift +43 -26
- package/app/Sources/AppDelegate.swift +13 -7
- package/app/Sources/DesktopModel.swift +26 -2
- package/app/Sources/HandsOffSession.swift +121 -26
- package/app/Sources/HotkeyStore.swift +5 -1
- package/app/Sources/IntentEngine.swift +37 -0
- package/app/Sources/LatticesApi.swift +40 -0
- package/app/Sources/MainView.swift +73 -21
- package/app/Sources/MouseFinder.swift +222 -0
- package/app/Sources/ProjectScanner.swift +57 -44
- package/app/Tests/StageDragTests.swift +333 -0
- package/app/Tests/StageJoinTests.swift +313 -0
- package/app/Tests/StageManagerTests.swift +280 -0
- package/app/Tests/StageTileTests.swift +353 -0
- package/assets/AppIcon.icns +0 -0
- package/bin/handsoff-worker.ts +10 -1
- package/bin/lattices-app.ts +123 -39
- package/bin/lattices-dev +51 -3
- package/bin/lattices.ts +181 -7
- package/docs/agent-layer-guide.md +207 -0
- package/package.json +12 -4
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>
|
|
18
|
+
<string>0.4.2</string>
|
|
17
19
|
<key>CFBundleShortVersionString</key>
|
|
18
|
-
<string>0.
|
|
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>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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,
|
|
51
|
-
.padding(.vertical,
|
|
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
|
-
("
|
|
232
|
-
("
|
|
233
|
-
("
|
|
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
|
|
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
|
-
|
|
229
|
-
|
|
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 }
|