@lattices/cli 0.4.2 → 0.4.5
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 +3 -0
- package/app/Info.plist +2 -2
- package/app/Lattices.app/Contents/Info.plist +2 -2
- package/app/Lattices.app/Contents/MacOS/Lattices +0 -0
- package/app/Package.swift +6 -0
- package/app/Sources/App.swift +10 -0
- package/app/Sources/AppDelegate.swift +90 -34
- package/app/Sources/AppShellView.swift +2 -0
- package/app/Sources/AppTypeClassifier.swift +36 -0
- package/app/Sources/AppUpdater.swift +92 -0
- package/app/Sources/CheatSheetHUD.swift +1 -0
- package/app/Sources/CliActionLauncher.swift +50 -0
- package/app/Sources/CommandModeView.swift +4 -24
- package/app/Sources/CompanionActivityLog.swift +70 -0
- package/app/Sources/CompanionKeyboardController.swift +141 -0
- package/app/Sources/DesktopModel.swift +4 -0
- package/app/Sources/HandsOffSession.swift +15 -4
- package/app/Sources/HomeDashboardView.swift +18 -10
- package/app/Sources/HotkeyStore.swift +8 -5
- package/app/Sources/IntentEngine.swift +7 -1
- package/app/Sources/LatticesApi.swift +125 -4
- package/app/Sources/LatticesCompanionBridgeServer.swift +438 -0
- package/app/Sources/LatticesCompanionCockpit.swift +555 -0
- package/app/Sources/LatticesCompanionSecurityCoordinator.swift +594 -0
- package/app/Sources/LatticesCompanionTrackpadController.swift +204 -0
- package/app/Sources/LatticesDeckHost.swift +1463 -0
- package/app/Sources/LatticesRuntime.swift +61 -0
- package/app/Sources/MainView.swift +351 -191
- package/app/Sources/MouseFinder.swift +335 -30
- package/app/Sources/MouseGestureConfig.swift +364 -0
- package/app/Sources/MouseGestureController.swift +1203 -0
- package/app/Sources/MouseInputDeviceStore.swift +98 -0
- package/app/Sources/MouseInputEventViewer.swift +272 -0
- package/app/Sources/MouseShortcutStore.swift +107 -0
- package/app/Sources/OmniSearchView.swift +136 -2
- package/app/Sources/OmniSearchWindow.swift +65 -5
- package/app/Sources/OnboardingView.swift +30 -16
- package/app/Sources/PaletteCommand.swift +26 -6
- package/app/Sources/PermissionChecker.swift +76 -2
- package/app/Sources/PiAuthNextStepCard.swift +148 -0
- package/app/Sources/PiAuthPromptCard.swift +90 -0
- package/app/Sources/PiChatDock.swift +137 -74
- package/app/Sources/PiChatSession.swift +608 -108
- package/app/Sources/PiInstallCallout.swift +86 -0
- package/app/Sources/PiProviderSetupCallout.swift +99 -0
- package/app/Sources/PiWorkspaceView.swift +174 -77
- package/app/Sources/Preferences.swift +78 -0
- package/app/Sources/ScreenMapState.swift +91 -31
- package/app/Sources/ScreenMapView.swift +510 -524
- package/app/Sources/ScreenMapWindowController.swift +12 -4
- package/app/Sources/SettingsView.swift +869 -152
- package/app/Sources/SystemTelemetryMonitor.swift +273 -0
- package/app/Sources/VoiceCommandWindow.swift +23 -2
- package/app/Sources/WindowDragSnapController.swift +628 -0
- package/app/Sources/WindowTiler.swift +328 -65
- package/app/Sources/WorkspaceManager.swift +288 -0
- package/bin/assistant-intelligence.ts +874 -0
- package/bin/handsoff-infer.ts +16 -209
- package/bin/handsoff-worker.ts +45 -258
- package/bin/lattices-app.ts +62 -0
- package/bin/lattices-dev +4 -0
- package/bin/lattices.ts +125 -14
- package/docs/agents.md +14 -0
- package/docs/api.md +55 -0
- package/docs/app.md +3 -0
- package/docs/companion-deck.md +180 -0
- package/docs/config.md +25 -0
- package/docs/tiling-reference.md +55 -0
- package/docs/voice-error-model.md +73 -0
- package/package.json +2 -1
|
@@ -63,6 +63,273 @@ struct LayoutConfig: Codable {
|
|
|
63
63
|
struct GridFile: Codable {
|
|
64
64
|
let presets: [String: GridPreset]?
|
|
65
65
|
let layouts: [String: LayoutConfig]?
|
|
66
|
+
let snapZones: SnapZonesConfig?
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
enum SnapModifierKey: String, Codable, Equatable {
|
|
70
|
+
case command
|
|
71
|
+
case option
|
|
72
|
+
case control
|
|
73
|
+
case shift
|
|
74
|
+
|
|
75
|
+
var eventFlags: NSEvent.ModifierFlags {
|
|
76
|
+
switch self {
|
|
77
|
+
case .command:
|
|
78
|
+
return .command
|
|
79
|
+
case .option:
|
|
80
|
+
return .option
|
|
81
|
+
case .control:
|
|
82
|
+
return .control
|
|
83
|
+
case .shift:
|
|
84
|
+
return .shift
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
var cgEventFlags: CGEventFlags {
|
|
89
|
+
switch self {
|
|
90
|
+
case .command:
|
|
91
|
+
return .maskCommand
|
|
92
|
+
case .option:
|
|
93
|
+
return .maskAlternate
|
|
94
|
+
case .control:
|
|
95
|
+
return .maskControl
|
|
96
|
+
case .shift:
|
|
97
|
+
return .maskShift
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
var label: String {
|
|
102
|
+
rawValue.capitalized
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
enum SnapZoneTriggerSpec: Codable, Equatable {
|
|
107
|
+
case named(String)
|
|
108
|
+
case fractions(FractionalPlacement)
|
|
109
|
+
|
|
110
|
+
init(from decoder: Decoder) throws {
|
|
111
|
+
let container = try decoder.singleValueContainer()
|
|
112
|
+
if let named = try? container.decode(String.self) {
|
|
113
|
+
self = .named(named)
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let preset = try container.decode(GridPreset.self)
|
|
118
|
+
guard let placement = FractionalPlacement(x: preset.x, y: preset.y, w: preset.w, h: preset.h) else {
|
|
119
|
+
throw DecodingError.dataCorruptedError(
|
|
120
|
+
in: container,
|
|
121
|
+
debugDescription: "snap zone trigger fractions must stay within 0...1"
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
self = .fractions(placement)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func encode(to encoder: Encoder) throws {
|
|
128
|
+
var container = encoder.singleValueContainer()
|
|
129
|
+
switch self {
|
|
130
|
+
case .named(let name):
|
|
131
|
+
try container.encode(name)
|
|
132
|
+
case .fractions(let placement):
|
|
133
|
+
try container.encode(GridPreset(x: placement.x, y: placement.y, w: placement.w, h: placement.h))
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
enum SnapZonePlacementSpec: Codable, Equatable {
|
|
139
|
+
case named(String)
|
|
140
|
+
case fractions(FractionalPlacement)
|
|
141
|
+
|
|
142
|
+
init(from decoder: Decoder) throws {
|
|
143
|
+
let container = try decoder.singleValueContainer()
|
|
144
|
+
if let named = try? container.decode(String.self) {
|
|
145
|
+
self = .named(named)
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let preset = try container.decode(GridPreset.self)
|
|
150
|
+
guard let placement = FractionalPlacement(x: preset.x, y: preset.y, w: preset.w, h: preset.h) else {
|
|
151
|
+
throw DecodingError.dataCorruptedError(
|
|
152
|
+
in: container,
|
|
153
|
+
debugDescription: "snap zone placement fractions must stay within 0...1"
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
self = .fractions(placement)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
func encode(to encoder: Encoder) throws {
|
|
160
|
+
var container = encoder.singleValueContainer()
|
|
161
|
+
switch self {
|
|
162
|
+
case .named(let name):
|
|
163
|
+
try container.encode(name)
|
|
164
|
+
case .fractions(let placement):
|
|
165
|
+
try container.encode(GridPreset(x: placement.x, y: placement.y, w: placement.w, h: placement.h))
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
struct SnapZoneDefinition: Codable, Equatable, Identifiable {
|
|
171
|
+
let rawID: String?
|
|
172
|
+
let label: String?
|
|
173
|
+
let placement: SnapZonePlacementSpec
|
|
174
|
+
let trigger: SnapZoneTriggerSpec
|
|
175
|
+
let priority: Int?
|
|
176
|
+
|
|
177
|
+
enum CodingKeys: String, CodingKey {
|
|
178
|
+
case rawID = "id"
|
|
179
|
+
case label
|
|
180
|
+
case placement
|
|
181
|
+
case trigger
|
|
182
|
+
case priority
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
var id: String {
|
|
186
|
+
let trimmed = rawID?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
187
|
+
return trimmed.isEmpty ? fallbackID : trimmed
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private var fallbackID: String {
|
|
191
|
+
switch placement {
|
|
192
|
+
case .named(let name):
|
|
193
|
+
return name
|
|
194
|
+
case .fractions(let fractions):
|
|
195
|
+
return "fractions-\(fractions.x)-\(fractions.y)-\(fractions.w)-\(fractions.h)"
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
struct SnapZonesConfig: Codable, Equatable {
|
|
201
|
+
let enabled: Bool?
|
|
202
|
+
let modifier: SnapModifierKey?
|
|
203
|
+
let zoneOpacity: Double?
|
|
204
|
+
let highlightOpacity: Double?
|
|
205
|
+
let previewOpacity: Double?
|
|
206
|
+
let cornerRadius: CGFloat?
|
|
207
|
+
let rules: [SnapZoneDefinition]?
|
|
208
|
+
|
|
209
|
+
enum CodingKeys: String, CodingKey {
|
|
210
|
+
case enabled
|
|
211
|
+
case modifier
|
|
212
|
+
case zoneOpacity
|
|
213
|
+
case highlightOpacity
|
|
214
|
+
case previewOpacity
|
|
215
|
+
case cornerRadius
|
|
216
|
+
case rules
|
|
217
|
+
case zones
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
init(
|
|
221
|
+
enabled: Bool?,
|
|
222
|
+
modifier: SnapModifierKey?,
|
|
223
|
+
zoneOpacity: Double?,
|
|
224
|
+
highlightOpacity: Double?,
|
|
225
|
+
previewOpacity: Double?,
|
|
226
|
+
cornerRadius: CGFloat?,
|
|
227
|
+
rules: [SnapZoneDefinition]?
|
|
228
|
+
) {
|
|
229
|
+
self.enabled = enabled
|
|
230
|
+
self.modifier = modifier
|
|
231
|
+
self.zoneOpacity = zoneOpacity
|
|
232
|
+
self.highlightOpacity = highlightOpacity
|
|
233
|
+
self.previewOpacity = previewOpacity
|
|
234
|
+
self.cornerRadius = cornerRadius
|
|
235
|
+
self.rules = rules
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
init(from decoder: Decoder) throws {
|
|
239
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
240
|
+
enabled = try container.decodeIfPresent(Bool.self, forKey: .enabled)
|
|
241
|
+
modifier = try container.decodeIfPresent(SnapModifierKey.self, forKey: .modifier)
|
|
242
|
+
zoneOpacity = try container.decodeIfPresent(Double.self, forKey: .zoneOpacity)
|
|
243
|
+
highlightOpacity = try container.decodeIfPresent(Double.self, forKey: .highlightOpacity)
|
|
244
|
+
previewOpacity = try container.decodeIfPresent(Double.self, forKey: .previewOpacity)
|
|
245
|
+
cornerRadius = try container.decodeIfPresent(CGFloat.self, forKey: .cornerRadius)
|
|
246
|
+
let decodedRules = try container.decodeIfPresent([SnapZoneDefinition].self, forKey: .rules)
|
|
247
|
+
let decodedZones = try container.decodeIfPresent([SnapZoneDefinition].self, forKey: .zones)
|
|
248
|
+
rules = decodedRules ?? decodedZones
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
func encode(to encoder: Encoder) throws {
|
|
252
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
253
|
+
try container.encodeIfPresent(enabled, forKey: .enabled)
|
|
254
|
+
try container.encodeIfPresent(modifier, forKey: .modifier)
|
|
255
|
+
try container.encodeIfPresent(zoneOpacity, forKey: .zoneOpacity)
|
|
256
|
+
try container.encodeIfPresent(highlightOpacity, forKey: .highlightOpacity)
|
|
257
|
+
try container.encodeIfPresent(previewOpacity, forKey: .previewOpacity)
|
|
258
|
+
try container.encodeIfPresent(cornerRadius, forKey: .cornerRadius)
|
|
259
|
+
try container.encodeIfPresent(rules, forKey: .rules)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
static let defaults = SnapZonesConfig(
|
|
263
|
+
enabled: true,
|
|
264
|
+
modifier: .command,
|
|
265
|
+
zoneOpacity: 0.10,
|
|
266
|
+
highlightOpacity: 0.22,
|
|
267
|
+
previewOpacity: 0.18,
|
|
268
|
+
cornerRadius: 18,
|
|
269
|
+
rules: [
|
|
270
|
+
SnapZoneDefinition(
|
|
271
|
+
rawID: "top-left",
|
|
272
|
+
label: "Top Left",
|
|
273
|
+
placement: .named("top-left"),
|
|
274
|
+
trigger: .fractions(FractionalPlacement(x: 0.00, y: 0.00, w: 0.24, h: 0.18)!),
|
|
275
|
+
priority: 40
|
|
276
|
+
),
|
|
277
|
+
SnapZoneDefinition(
|
|
278
|
+
rawID: "maximize",
|
|
279
|
+
label: "Maximize",
|
|
280
|
+
placement: .named("maximize"),
|
|
281
|
+
trigger: .fractions(FractionalPlacement(x: 0.24, y: 0.00, w: 0.52, h: 0.12)!),
|
|
282
|
+
priority: 20
|
|
283
|
+
),
|
|
284
|
+
SnapZoneDefinition(
|
|
285
|
+
rawID: "top-right",
|
|
286
|
+
label: "Top Right",
|
|
287
|
+
placement: .named("top-right"),
|
|
288
|
+
trigger: .fractions(FractionalPlacement(x: 0.76, y: 0.00, w: 0.24, h: 0.18)!),
|
|
289
|
+
priority: 40
|
|
290
|
+
),
|
|
291
|
+
SnapZoneDefinition(
|
|
292
|
+
rawID: "left",
|
|
293
|
+
label: "Left",
|
|
294
|
+
placement: .named("left"),
|
|
295
|
+
trigger: .fractions(FractionalPlacement(x: 0.00, y: 0.18, w: 0.12, h: 0.64)!),
|
|
296
|
+
priority: 10
|
|
297
|
+
),
|
|
298
|
+
SnapZoneDefinition(
|
|
299
|
+
rawID: "right",
|
|
300
|
+
label: "Right",
|
|
301
|
+
placement: .named("right"),
|
|
302
|
+
trigger: .fractions(FractionalPlacement(x: 0.88, y: 0.18, w: 0.12, h: 0.64)!),
|
|
303
|
+
priority: 10
|
|
304
|
+
),
|
|
305
|
+
SnapZoneDefinition(
|
|
306
|
+
rawID: "bottom-left",
|
|
307
|
+
label: "Bottom Left",
|
|
308
|
+
placement: .named("bottom-left"),
|
|
309
|
+
trigger: .fractions(FractionalPlacement(x: 0.00, y: 0.82, w: 0.24, h: 0.18)!),
|
|
310
|
+
priority: 40
|
|
311
|
+
),
|
|
312
|
+
SnapZoneDefinition(
|
|
313
|
+
rawID: "bottom-right",
|
|
314
|
+
label: "Bottom Right",
|
|
315
|
+
placement: .named("bottom-right"),
|
|
316
|
+
trigger: .fractions(FractionalPlacement(x: 0.76, y: 0.82, w: 0.24, h: 0.18)!),
|
|
317
|
+
priority: 40
|
|
318
|
+
),
|
|
319
|
+
]
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
func merged(over defaults: SnapZonesConfig = .defaults) -> SnapZonesConfig {
|
|
323
|
+
SnapZonesConfig(
|
|
324
|
+
enabled: enabled ?? defaults.enabled,
|
|
325
|
+
modifier: modifier ?? defaults.modifier,
|
|
326
|
+
zoneOpacity: zoneOpacity ?? defaults.zoneOpacity,
|
|
327
|
+
highlightOpacity: highlightOpacity ?? defaults.highlightOpacity,
|
|
328
|
+
previewOpacity: previewOpacity ?? defaults.previewOpacity,
|
|
329
|
+
cornerRadius: cornerRadius ?? defaults.cornerRadius,
|
|
330
|
+
rules: rules ?? defaults.rules
|
|
331
|
+
)
|
|
332
|
+
}
|
|
66
333
|
}
|
|
67
334
|
|
|
68
335
|
// MARK: - Manager
|
|
@@ -75,9 +342,11 @@ class WorkspaceManager: ObservableObject {
|
|
|
75
342
|
@Published var isSwitching: Bool = false
|
|
76
343
|
@Published var gridPresets: [String: GridPreset] = [:]
|
|
77
344
|
@Published var gridLayouts: [String: LayoutConfig] = [:]
|
|
345
|
+
@Published var snapZonesConfig: SnapZonesConfig = .defaults
|
|
78
346
|
|
|
79
347
|
private let configPath: String
|
|
80
348
|
private let gridConfigPath: String
|
|
349
|
+
private let snapZonesConfigPath: String
|
|
81
350
|
private var tmuxPath: String { TmuxQuery.resolvedPath ?? "/opt/homebrew/bin/tmux" }
|
|
82
351
|
private let activeLayerKey = "lattices.activeLayerIndex"
|
|
83
352
|
|
|
@@ -85,6 +354,7 @@ class WorkspaceManager: ObservableObject {
|
|
|
85
354
|
let home = FileManager.default.homeDirectoryForCurrentUser.path
|
|
86
355
|
self.configPath = (home as NSString).appendingPathComponent(".lattices/workspace.json")
|
|
87
356
|
self.gridConfigPath = (home as NSString).appendingPathComponent(".lattices/grid.json")
|
|
357
|
+
self.snapZonesConfigPath = (home as NSString).appendingPathComponent(".lattices/snap-zones.json")
|
|
88
358
|
self.activeLayerIndex = UserDefaults.standard.integer(forKey: activeLayerKey)
|
|
89
359
|
loadConfig()
|
|
90
360
|
loadGridConfig()
|
|
@@ -137,6 +407,7 @@ class WorkspaceManager: ObservableObject {
|
|
|
137
407
|
func loadGridConfig() {
|
|
138
408
|
var presets: [String: GridPreset] = [:]
|
|
139
409
|
var layouts: [String: LayoutConfig] = [:]
|
|
410
|
+
var snapZones = SnapZonesConfig.defaults
|
|
140
411
|
|
|
141
412
|
// Load global ~/.lattices/grid.json
|
|
142
413
|
if FileManager.default.fileExists(atPath: gridConfigPath),
|
|
@@ -145,11 +416,24 @@ class WorkspaceManager: ObservableObject {
|
|
|
145
416
|
let gridFile = try JSONDecoder().decode(GridFile.self, from: data)
|
|
146
417
|
if let p = gridFile.presets { presets.merge(p) { _, new in new } }
|
|
147
418
|
if let l = gridFile.layouts { layouts.merge(l) { _, new in new } }
|
|
419
|
+
if let snap = gridFile.snapZones {
|
|
420
|
+
snapZones = snap.merged(over: snapZones)
|
|
421
|
+
}
|
|
148
422
|
} catch {
|
|
149
423
|
DiagnosticLog.shared.error("WorkspaceManager: failed to decode grid.json — \(error.localizedDescription)")
|
|
150
424
|
}
|
|
151
425
|
}
|
|
152
426
|
|
|
427
|
+
if FileManager.default.fileExists(atPath: snapZonesConfigPath),
|
|
428
|
+
let data = FileManager.default.contents(atPath: snapZonesConfigPath) {
|
|
429
|
+
do {
|
|
430
|
+
let config = try JSONDecoder().decode(SnapZonesConfig.self, from: data)
|
|
431
|
+
snapZones = config.merged(over: snapZones)
|
|
432
|
+
} catch {
|
|
433
|
+
DiagnosticLog.shared.error("WorkspaceManager: failed to decode snap-zones.json — \(error.localizedDescription)")
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
153
437
|
// Merge per-project .lattices.json "grid" section on top
|
|
154
438
|
let projectGridPath = ".lattices.json"
|
|
155
439
|
if FileManager.default.fileExists(atPath: projectGridPath),
|
|
@@ -161,6 +445,9 @@ class WorkspaceManager: ObservableObject {
|
|
|
161
445
|
let gridFile = try JSONDecoder().decode(GridFile.self, from: gridData)
|
|
162
446
|
if let p = gridFile.presets { presets.merge(p) { _, new in new } }
|
|
163
447
|
if let l = gridFile.layouts { layouts.merge(l) { _, new in new } }
|
|
448
|
+
if let snap = gridFile.snapZones {
|
|
449
|
+
snapZones = snap.merged(over: snapZones)
|
|
450
|
+
}
|
|
164
451
|
}
|
|
165
452
|
} catch {
|
|
166
453
|
DiagnosticLog.shared.error("WorkspaceManager: failed to decode .lattices.json grid — \(error.localizedDescription)")
|
|
@@ -169,6 +456,7 @@ class WorkspaceManager: ObservableObject {
|
|
|
169
456
|
|
|
170
457
|
self.gridPresets = presets
|
|
171
458
|
self.gridLayouts = layouts
|
|
459
|
+
self.snapZonesConfig = snapZones
|
|
172
460
|
}
|
|
173
461
|
|
|
174
462
|
/// Resolve a tile string to fractions: check user presets first, then built-in TilePosition
|