@lattices/cli 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/app/Info.plist +13 -2
- package/app/Lattices.app/Contents/Info.plist +13 -2
- package/app/Lattices.app/Contents/MacOS/Lattices +0 -0
- package/app/Sources/AppShell/App.swift +7 -1
- package/app/Sources/AppShell/AppDelegate.swift +65 -1
- package/app/Sources/AppShell/AppShellView.swift +10 -0
- package/app/Sources/AppShell/CliActionLauncher.swift +2 -2
- package/app/Sources/AppShell/KeyRecorderView.swift +1 -1
- package/app/Sources/AppShell/MainView.swift +1 -1
- package/app/Sources/AppShell/Preferences.swift +29 -3
- package/app/Sources/AppShell/SettingsView.swift +525 -60
- package/app/Sources/AppShell/SettingsWindow.swift +4 -0
- package/app/Sources/Core/Actions/HotkeyStore.swift +13 -1
- package/app/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +23 -7
- package/app/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +35 -0
- package/app/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +1 -1
- package/app/Sources/Core/Desktop/WindowTiler.swift +0 -2
- package/app/Sources/Core/Input/KeyboardRemapConfig.swift +69 -0
- package/app/Sources/Core/Input/KeyboardRemapController.swift +184 -0
- package/app/Sources/Core/Input/KeyboardRemapStore.swift +84 -0
- package/app/Sources/Core/Overlays/CommandMode/CommandModeState.swift +101 -0
- package/app/Sources/Core/Overlays/CommandMode/CommandModeView.swift +113 -4
- package/app/Sources/Core/Overlays/CommandMode/CommandModeWindow.swift +9 -5
- package/app/Sources/Core/Overlays/HUD/CheatSheetHUD.swift +1 -0
- package/app/Sources/Core/Overlays/ScreenMap/ScreenMapState.swift +0 -1
- package/app/Sources/Core/Overlays/ScreenMap/ScreenMapView.swift +20 -7
- package/app/Sources/Core/Workspace/SessionManager.swift +1 -1
- package/app/Sources/Core/Workspace/WorkspaceManager.swift +62 -7
- package/bin/lattices-app.ts +11 -0
- package/bin/lattices-dev +11 -0
- package/bin/lattices.ts +57 -17
- package/docs/app.md +30 -2
- package/docs/companion-deck.md +29 -0
- package/docs/concepts.md +5 -5
- package/docs/config.md +34 -9
- package/docs/layers.md +1 -1
- package/docs/overview.md +1 -1
- package/docs/quickstart.md +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,11 +81,11 @@ Close your laptop, reboot, come back a week later — your editor, dev
|
|
|
81
81
|
server, and test runner are exactly where you left them.
|
|
82
82
|
|
|
83
83
|
```sh
|
|
84
|
-
cd my-project && lattices
|
|
84
|
+
cd my-project && lattices start
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
No config? It
|
|
88
|
-
|
|
87
|
+
No config? It opens a shell in the project and, when it can, starts your
|
|
88
|
+
detected dev command in a second pane.
|
|
89
89
|
|
|
90
90
|
### Configuration
|
|
91
91
|
|
|
@@ -95,7 +95,7 @@ Drop a `.lattices.json` in your project root:
|
|
|
95
95
|
{
|
|
96
96
|
"ensure": true,
|
|
97
97
|
"panes": [
|
|
98
|
-
{ "name": "
|
|
98
|
+
{ "name": "shell", "size": 60 },
|
|
99
99
|
{ "name": "server", "cmd": "pnpm dev" },
|
|
100
100
|
{ "name": "tests", "cmd": "pnpm test --watch" }
|
|
101
101
|
]
|
|
@@ -108,7 +108,7 @@ Drop a `.lattices.json` in your project root:
|
|
|
108
108
|
2 panes 3+ panes
|
|
109
109
|
|
|
110
110
|
┌──────────┬───────┐ ┌──────────┬───────┐
|
|
111
|
-
│
|
|
111
|
+
│ shell │server │ │ shell │server │
|
|
112
112
|
│ (60%) │(40%) │ │ (60%) ├───────┤
|
|
113
113
|
└──────────┴───────┘ │ │tests │
|
|
114
114
|
└──────────┴───────┘
|
|
@@ -193,7 +193,9 @@ desktop the same way you do.
|
|
|
193
193
|
## CLI
|
|
194
194
|
|
|
195
195
|
```
|
|
196
|
-
lattices
|
|
196
|
+
lattices Show workspace status and common commands
|
|
197
|
+
lattices start Create or reattach to current project session
|
|
198
|
+
lattices tmux Alias for lattices start
|
|
197
199
|
lattices init Generate .lattices.json
|
|
198
200
|
lattices ls List active sessions
|
|
199
201
|
lattices kill [name] Kill a session
|
package/app/Info.plist
CHANGED
|
@@ -14,10 +14,21 @@
|
|
|
14
14
|
<string>AppIcon</string>
|
|
15
15
|
<key>CFBundlePackageType</key>
|
|
16
16
|
<string>APPL</string>
|
|
17
|
+
<key>CFBundleURLTypes</key>
|
|
18
|
+
<array>
|
|
19
|
+
<dict>
|
|
20
|
+
<key>CFBundleURLName</key>
|
|
21
|
+
<string>com.arach.lattices</string>
|
|
22
|
+
<key>CFBundleURLSchemes</key>
|
|
23
|
+
<array>
|
|
24
|
+
<string>lattices</string>
|
|
25
|
+
</array>
|
|
26
|
+
</dict>
|
|
27
|
+
</array>
|
|
17
28
|
<key>CFBundleVersion</key>
|
|
18
|
-
<string>0.4.
|
|
29
|
+
<string>0.4.8</string>
|
|
19
30
|
<key>CFBundleShortVersionString</key>
|
|
20
|
-
<string>0.4.
|
|
31
|
+
<string>0.4.8</string>
|
|
21
32
|
<key>LSMinimumSystemVersion</key>
|
|
22
33
|
<string>13.0</string>
|
|
23
34
|
<key>LSUIElement</key>
|
|
@@ -14,10 +14,21 @@
|
|
|
14
14
|
<string>AppIcon</string>
|
|
15
15
|
<key>CFBundlePackageType</key>
|
|
16
16
|
<string>APPL</string>
|
|
17
|
+
<key>CFBundleURLTypes</key>
|
|
18
|
+
<array>
|
|
19
|
+
<dict>
|
|
20
|
+
<key>CFBundleURLName</key>
|
|
21
|
+
<string>com.arach.lattices</string>
|
|
22
|
+
<key>CFBundleURLSchemes</key>
|
|
23
|
+
<array>
|
|
24
|
+
<string>lattices</string>
|
|
25
|
+
</array>
|
|
26
|
+
</dict>
|
|
27
|
+
</array>
|
|
17
28
|
<key>CFBundleVersion</key>
|
|
18
|
-
<string>0.4.
|
|
29
|
+
<string>0.4.8</string>
|
|
19
30
|
<key>CFBundleShortVersionString</key>
|
|
20
|
-
<string>0.4.
|
|
31
|
+
<string>0.4.8</string>
|
|
21
32
|
<key>LSMinimumSystemVersion</key>
|
|
22
33
|
<string>13.0</string>
|
|
23
34
|
<key>LSUIElement</key>
|
|
Binary file
|
|
@@ -5,7 +5,13 @@ struct LatticesApp: App {
|
|
|
5
5
|
@NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
|
|
6
6
|
|
|
7
7
|
var body: some Scene {
|
|
8
|
-
Settings {
|
|
8
|
+
Settings {
|
|
9
|
+
SettingsContentView(
|
|
10
|
+
prefs: Preferences.shared,
|
|
11
|
+
scanner: ProjectScanner.shared
|
|
12
|
+
)
|
|
13
|
+
.frame(width: 900, height: 640)
|
|
14
|
+
}
|
|
9
15
|
.commands {
|
|
10
16
|
CommandGroup(after: .appInfo) {
|
|
11
17
|
Button("Update Lattices…") {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import AppKit
|
|
2
|
+
import Carbon
|
|
2
3
|
import SwiftUI
|
|
3
4
|
|
|
4
5
|
extension Notification.Name {
|
|
@@ -69,6 +70,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|
|
69
70
|
Self.shared = self
|
|
70
71
|
NSApp.setActivationPolicy(.accessory)
|
|
71
72
|
NSApp.appearance = NSAppearance(named: .darkAqua)
|
|
73
|
+
registerDeepLinkHandler()
|
|
72
74
|
|
|
73
75
|
// --- Status item ---
|
|
74
76
|
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
|
|
@@ -124,6 +126,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|
|
124
126
|
store.register(action: .omniSearch) { OmniSearchWindow.shared.toggle() }
|
|
125
127
|
WindowDragSnapController.shared.start()
|
|
126
128
|
MouseGestureController.shared.start()
|
|
129
|
+
KeyboardRemapController.shared.start()
|
|
127
130
|
|
|
128
131
|
// Session layer cycling
|
|
129
132
|
store.register(action: .layerNext) { SessionLayerStore.shared.cycleNext() }
|
|
@@ -162,6 +165,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|
|
162
165
|
}
|
|
163
166
|
store.register(action: .tileDistribute) { WindowTiler.distributeVisible(reactivateLattices: false) }
|
|
164
167
|
store.register(action: .tileTypeGrid) { WindowTiler.distributeVisibleByFrontmostType(reactivateLattices: false) }
|
|
168
|
+
store.register(action: .tileOrganize) {
|
|
169
|
+
let appName = DesktopModel.shared.frontmostWindow()?.app
|
|
170
|
+
?? NSWorkspace.shared.frontmostApplication?.localizedName
|
|
171
|
+
CommandModeWindow.shared.show(launchMode: .organize(appName: appName))
|
|
172
|
+
}
|
|
165
173
|
|
|
166
174
|
// Onboarding on first launch; otherwise just check permissions
|
|
167
175
|
if !OnboardingWindowController.shared.showIfNeeded() {
|
|
@@ -178,7 +186,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|
|
178
186
|
ProcessModel.shared.start()
|
|
179
187
|
LatticesApi.setup()
|
|
180
188
|
DaemonServer.shared.start()
|
|
181
|
-
|
|
189
|
+
if Preferences.shared.companionBridgeEnabled {
|
|
190
|
+
LatticesCompanionBridgeServer.shared.start()
|
|
191
|
+
} else {
|
|
192
|
+
diag.info("CompanionBridge: disabled by preference")
|
|
193
|
+
}
|
|
182
194
|
AgentPool.shared.start()
|
|
183
195
|
diag.finish(tBoot)
|
|
184
196
|
|
|
@@ -198,6 +210,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|
|
198
210
|
}
|
|
199
211
|
|
|
200
212
|
func applicationWillTerminate(_ notification: Notification) {
|
|
213
|
+
KeyboardRemapController.shared.stop()
|
|
201
214
|
LatticesCompanionBridgeServer.shared.stop()
|
|
202
215
|
DaemonServer.shared.stop()
|
|
203
216
|
}
|
|
@@ -328,6 +341,57 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|
|
328
341
|
@objc private func menuSettings() { SettingsWindowController.shared.show() }
|
|
329
342
|
@objc private func menuQuit() { NSApp.terminate(nil) }
|
|
330
343
|
|
|
344
|
+
// MARK: - Deep Links
|
|
345
|
+
|
|
346
|
+
private func registerDeepLinkHandler() {
|
|
347
|
+
NSAppleEventManager.shared().setEventHandler(
|
|
348
|
+
self,
|
|
349
|
+
andSelector: #selector(handleGetURLEvent(_:withReplyEvent:)),
|
|
350
|
+
forEventClass: AEEventClass(kInternetEventClass),
|
|
351
|
+
andEventID: AEEventID(kAEGetURL)
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
@objc private func handleGetURLEvent(
|
|
356
|
+
_ event: NSAppleEventDescriptor,
|
|
357
|
+
withReplyEvent replyEvent: NSAppleEventDescriptor
|
|
358
|
+
) {
|
|
359
|
+
guard
|
|
360
|
+
let value = event.paramDescriptor(forKeyword: keyDirectObject)?.stringValue,
|
|
361
|
+
let url = URL(string: value)
|
|
362
|
+
else {
|
|
363
|
+
return
|
|
364
|
+
}
|
|
365
|
+
handleDeepLink(url)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private func handleDeepLink(_ url: URL) {
|
|
369
|
+
guard url.scheme?.localizedCaseInsensitiveCompare("lattices") == .orderedSame else {
|
|
370
|
+
return
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
let host = url.host?.lowercased()
|
|
374
|
+
let action = url.pathComponents
|
|
375
|
+
.first { $0 != "/" }?
|
|
376
|
+
.lowercased()
|
|
377
|
+
|
|
378
|
+
guard host == "companion" else {
|
|
379
|
+
SettingsWindowController.shared.show()
|
|
380
|
+
return
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
switch action {
|
|
384
|
+
case "enable", "start":
|
|
385
|
+
Preferences.shared.companionBridgeEnabled = true
|
|
386
|
+
SettingsWindowController.shared.showCompanion()
|
|
387
|
+
case "disable", "stop":
|
|
388
|
+
Preferences.shared.companionBridgeEnabled = false
|
|
389
|
+
SettingsWindowController.shared.showCompanion()
|
|
390
|
+
default:
|
|
391
|
+
SettingsWindowController.shared.showCompanion()
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
331
395
|
private static func showWorkspaceInspector() {
|
|
332
396
|
guard let entry = DesktopModel.shared.frontmostWindow(),
|
|
333
397
|
entry.app != "Lattices" else {
|
|
@@ -8,6 +8,7 @@ enum AppPage: String, CaseIterable {
|
|
|
8
8
|
case desktopInventory
|
|
9
9
|
case pi
|
|
10
10
|
case settings
|
|
11
|
+
case companionSettings
|
|
11
12
|
case docs
|
|
12
13
|
|
|
13
14
|
var label: String {
|
|
@@ -17,6 +18,7 @@ enum AppPage: String, CaseIterable {
|
|
|
17
18
|
case .desktopInventory: return "Desktop Inventory"
|
|
18
19
|
case .pi: return "Pi"
|
|
19
20
|
case .settings: return "Settings"
|
|
21
|
+
case .companionSettings:return "Settings"
|
|
20
22
|
case .docs: return "Docs"
|
|
21
23
|
}
|
|
22
24
|
}
|
|
@@ -28,6 +30,7 @@ enum AppPage: String, CaseIterable {
|
|
|
28
30
|
case .desktopInventory: return "macwindow.on.rectangle"
|
|
29
31
|
case .pi: return "terminal"
|
|
30
32
|
case .settings: return "gearshape"
|
|
33
|
+
case .companionSettings:return "ipad.and.iphone"
|
|
31
34
|
case .docs: return "book"
|
|
32
35
|
}
|
|
33
36
|
}
|
|
@@ -129,6 +132,13 @@ struct AppShellView: View {
|
|
|
129
132
|
scanner: ProjectScanner.shared,
|
|
130
133
|
onBack: { windowController.activePage = .screenMap; controller.enter() }
|
|
131
134
|
)
|
|
135
|
+
case .companionSettings:
|
|
136
|
+
SettingsContentView(
|
|
137
|
+
page: .companionSettings,
|
|
138
|
+
prefs: Preferences.shared,
|
|
139
|
+
scanner: ProjectScanner.shared,
|
|
140
|
+
onBack: { windowController.activePage = .screenMap; controller.enter() }
|
|
141
|
+
)
|
|
132
142
|
case .docs:
|
|
133
143
|
SettingsContentView(
|
|
134
144
|
page: .docs,
|
|
@@ -24,7 +24,7 @@ enum CliActionLauncher {
|
|
|
24
24
|
) else { return }
|
|
25
25
|
|
|
26
26
|
Preferences.shared.terminal.launch(
|
|
27
|
-
command: "lattices init && lattices",
|
|
27
|
+
command: "lattices init && lattices start",
|
|
28
28
|
in: directory
|
|
29
29
|
)
|
|
30
30
|
}
|
|
@@ -36,7 +36,7 @@ enum CliActionLauncher {
|
|
|
36
36
|
) else { return }
|
|
37
37
|
|
|
38
38
|
Preferences.shared.terminal.launch(
|
|
39
|
-
command: "lattices",
|
|
39
|
+
command: "lattices start",
|
|
40
40
|
in: directory
|
|
41
41
|
)
|
|
42
42
|
}
|
|
@@ -34,7 +34,7 @@ struct KeyRecorderView: View {
|
|
|
34
34
|
.frame(minWidth: 80, alignment: .leading)
|
|
35
35
|
} else if let binding = binding {
|
|
36
36
|
HStack(spacing: 4) {
|
|
37
|
-
ForEach(binding.
|
|
37
|
+
ForEach(binding.compactDisplayParts, id: \.self) { part in
|
|
38
38
|
keyBadge(part)
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -490,7 +490,7 @@ struct MainView: View {
|
|
|
490
490
|
.buttonStyle(.plain)
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
-
Text("Initialize runs lattices init && lattices in the folder you choose.")
|
|
493
|
+
Text("Initialize runs lattices init && lattices start in the folder you choose.")
|
|
494
494
|
.font(Typo.mono(9))
|
|
495
495
|
.foregroundColor(Palette.textMuted)
|
|
496
496
|
.multilineTextAlignment(.center)
|
|
@@ -10,6 +10,7 @@ class Preferences: ObservableObject {
|
|
|
10
10
|
static let shared = Preferences()
|
|
11
11
|
|
|
12
12
|
private enum CompanionDefaultsKey {
|
|
13
|
+
static let bridgeEnabled = "companion.bridge.enabled"
|
|
13
14
|
static let trackpadEnabled = "companion.trackpad.enabled"
|
|
14
15
|
static let cockpitLayout = "companion.cockpit.layout"
|
|
15
16
|
}
|
|
@@ -30,6 +31,17 @@ class Preferences: ObservableObject {
|
|
|
30
31
|
didSet { UserDefaults.standard.set(dragSnapEnabled, forKey: "windowSnap.enabled") }
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
@Published var companionBridgeEnabled: Bool {
|
|
35
|
+
didSet {
|
|
36
|
+
UserDefaults.standard.set(companionBridgeEnabled, forKey: CompanionDefaultsKey.bridgeEnabled)
|
|
37
|
+
if companionBridgeEnabled {
|
|
38
|
+
LatticesCompanionBridgeServer.shared.start()
|
|
39
|
+
} else {
|
|
40
|
+
LatticesCompanionBridgeServer.shared.stop()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
33
45
|
@Published var companionTrackpadEnabled: Bool {
|
|
34
46
|
didSet { UserDefaults.standard.set(companionTrackpadEnabled, forKey: CompanionDefaultsKey.trackpadEnabled) }
|
|
35
47
|
}
|
|
@@ -37,11 +49,14 @@ class Preferences: ObservableObject {
|
|
|
37
49
|
@Published var companionCockpitLayout: LatticesCompanionCockpitLayout {
|
|
38
50
|
didSet { persistCompanionCockpitLayout() }
|
|
39
51
|
}
|
|
40
|
-
|
|
41
52
|
@Published var mouseGesturesEnabled: Bool {
|
|
42
53
|
didSet { UserDefaults.standard.set(mouseGesturesEnabled, forKey: "mouseGestures.enabled") }
|
|
43
54
|
}
|
|
44
55
|
|
|
56
|
+
@Published var keyboardRemapsEnabled: Bool {
|
|
57
|
+
didSet { UserDefaults.standard.set(keyboardRemapsEnabled, forKey: "keyboardRemaps.enabled") }
|
|
58
|
+
}
|
|
59
|
+
|
|
45
60
|
// MARK: - AI / Claude
|
|
46
61
|
|
|
47
62
|
@Published var claudePath: String {
|
|
@@ -156,19 +171,30 @@ class Preferences: ObservableObject {
|
|
|
156
171
|
self.dragSnapEnabled = true
|
|
157
172
|
}
|
|
158
173
|
|
|
174
|
+
if UserDefaults.standard.object(forKey: CompanionDefaultsKey.bridgeEnabled) != nil {
|
|
175
|
+
self.companionBridgeEnabled = UserDefaults.standard.bool(forKey: CompanionDefaultsKey.bridgeEnabled)
|
|
176
|
+
} else {
|
|
177
|
+
self.companionBridgeEnabled = false
|
|
178
|
+
}
|
|
179
|
+
|
|
159
180
|
if UserDefaults.standard.object(forKey: CompanionDefaultsKey.trackpadEnabled) != nil {
|
|
160
181
|
self.companionTrackpadEnabled = UserDefaults.standard.bool(forKey: CompanionDefaultsKey.trackpadEnabled)
|
|
161
182
|
} else {
|
|
162
|
-
self.companionTrackpadEnabled =
|
|
183
|
+
self.companionTrackpadEnabled = false
|
|
163
184
|
}
|
|
164
185
|
|
|
165
186
|
self.companionCockpitLayout = Self.loadCompanionCockpitLayout()
|
|
166
|
-
|
|
167
187
|
if UserDefaults.standard.object(forKey: "mouseGestures.enabled") != nil {
|
|
168
188
|
self.mouseGesturesEnabled = UserDefaults.standard.bool(forKey: "mouseGestures.enabled")
|
|
169
189
|
} else {
|
|
170
190
|
self.mouseGesturesEnabled = false
|
|
171
191
|
}
|
|
192
|
+
|
|
193
|
+
if UserDefaults.standard.object(forKey: "keyboardRemaps.enabled") != nil {
|
|
194
|
+
self.keyboardRemapsEnabled = UserDefaults.standard.bool(forKey: "keyboardRemaps.enabled")
|
|
195
|
+
} else {
|
|
196
|
+
self.keyboardRemapsEnabled = true
|
|
197
|
+
}
|
|
172
198
|
// AI / Claude
|
|
173
199
|
self.claudePath = UserDefaults.standard.string(forKey: "claude.path") ?? ""
|
|
174
200
|
self.advisorModel = UserDefaults.standard.string(forKey: "claude.advisorModel") ?? "haiku"
|