@lattices/cli 0.3.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.
- package/README.md +155 -0
- package/app/Lattices.app/Contents/Info.plist +24 -0
- package/app/Package.swift +13 -0
- package/app/Sources/AccessibilityTextExtractor.swift +111 -0
- package/app/Sources/ActionRow.swift +61 -0
- package/app/Sources/App.swift +10 -0
- package/app/Sources/AppDelegate.swift +242 -0
- package/app/Sources/AppShellView.swift +62 -0
- package/app/Sources/AppTypeClassifier.swift +70 -0
- package/app/Sources/AppWindowShell.swift +63 -0
- package/app/Sources/CheatSheetHUD.swift +332 -0
- package/app/Sources/CommandModeState.swift +1362 -0
- package/app/Sources/CommandModeView.swift +1405 -0
- package/app/Sources/CommandModeWindow.swift +192 -0
- package/app/Sources/CommandPaletteView.swift +307 -0
- package/app/Sources/CommandPaletteWindow.swift +134 -0
- package/app/Sources/DaemonProtocol.swift +101 -0
- package/app/Sources/DaemonServer.swift +414 -0
- package/app/Sources/DesktopModel.swift +149 -0
- package/app/Sources/DesktopModelTypes.swift +71 -0
- package/app/Sources/DiagnosticLog.swift +271 -0
- package/app/Sources/EventBus.swift +30 -0
- package/app/Sources/HotkeyManager.swift +254 -0
- package/app/Sources/HotkeyStore.swift +338 -0
- package/app/Sources/InventoryManager.swift +35 -0
- package/app/Sources/InventoryPath.swift +43 -0
- package/app/Sources/KeyRecorderView.swift +210 -0
- package/app/Sources/LatticesApi.swift +1234 -0
- package/app/Sources/LayerBezel.swift +203 -0
- package/app/Sources/MainView.swift +479 -0
- package/app/Sources/MainWindow.swift +83 -0
- package/app/Sources/OcrModel.swift +430 -0
- package/app/Sources/OcrStore.swift +329 -0
- package/app/Sources/OmniSearchState.swift +283 -0
- package/app/Sources/OmniSearchView.swift +288 -0
- package/app/Sources/OmniSearchWindow.swift +105 -0
- package/app/Sources/OrphanRow.swift +129 -0
- package/app/Sources/PaletteCommand.swift +419 -0
- package/app/Sources/PermissionChecker.swift +125 -0
- package/app/Sources/Preferences.swift +99 -0
- package/app/Sources/ProcessModel.swift +199 -0
- package/app/Sources/ProcessQuery.swift +151 -0
- package/app/Sources/Project.swift +28 -0
- package/app/Sources/ProjectRow.swift +368 -0
- package/app/Sources/ProjectScanner.swift +128 -0
- package/app/Sources/ScreenMapState.swift +2387 -0
- package/app/Sources/ScreenMapView.swift +2820 -0
- package/app/Sources/ScreenMapWindowController.swift +89 -0
- package/app/Sources/SessionManager.swift +72 -0
- package/app/Sources/SettingsView.swift +1064 -0
- package/app/Sources/SettingsWindow.swift +20 -0
- package/app/Sources/TabGroupRow.swift +178 -0
- package/app/Sources/Terminal.swift +259 -0
- package/app/Sources/TerminalQuery.swift +156 -0
- package/app/Sources/TerminalSynthesizer.swift +200 -0
- package/app/Sources/Theme.swift +163 -0
- package/app/Sources/TilePickerView.swift +209 -0
- package/app/Sources/TmuxModel.swift +53 -0
- package/app/Sources/TmuxQuery.swift +81 -0
- package/app/Sources/WindowTiler.swift +1778 -0
- package/app/Sources/WorkspaceManager.swift +575 -0
- package/bin/client.js +4 -0
- package/bin/daemon-client.js +187 -0
- package/bin/lattices-app.js +221 -0
- package/bin/lattices.js +1551 -0
- package/docs/api.md +924 -0
- package/docs/app.md +297 -0
- package/docs/concepts.md +135 -0
- package/docs/config.md +245 -0
- package/docs/layers.md +410 -0
- package/docs/ocr.md +185 -0
- package/docs/overview.md +94 -0
- package/docs/quickstart.md +75 -0
- package/package.json +42 -0
package/docs/layers.md
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Workspace Layers & Tab Groups
|
|
3
|
+
description: Group projects into switchable layers and tabbed groups
|
|
4
|
+
order: 4
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Two ways to organize related projects in `~/.lattices/workspace.json`:
|
|
8
|
+
|
|
9
|
+
- **Layers** — switchable contexts that focus and tile windows
|
|
10
|
+
- **Tab groups** — related projects as tabs within a single terminal window
|
|
11
|
+
|
|
12
|
+
Both features are configured in the same workspace config and
|
|
13
|
+
work together.
|
|
14
|
+
|
|
15
|
+
## Tab Groups
|
|
16
|
+
|
|
17
|
+
Tab groups let you bundle related projects as tabs within a single
|
|
18
|
+
terminal session (requires tmux). This is useful when you have a family
|
|
19
|
+
of projects — like an iOS app, macOS app, website, and API — that
|
|
20
|
+
you think of as one logical unit.
|
|
21
|
+
|
|
22
|
+
### Configuration
|
|
23
|
+
|
|
24
|
+
Add `groups` to `~/.lattices/workspace.json`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"name": "my-setup",
|
|
29
|
+
"groups": [
|
|
30
|
+
{
|
|
31
|
+
"id": "talkie",
|
|
32
|
+
"label": "Talkie",
|
|
33
|
+
"tabs": [
|
|
34
|
+
{ "path": "/Users/you/dev/talkie-ios", "label": "iOS" },
|
|
35
|
+
{ "path": "/Users/you/dev/talkie-macos", "label": "macOS" },
|
|
36
|
+
{ "path": "/Users/you/dev/talkie-web", "label": "Website" },
|
|
37
|
+
{ "path": "/Users/you/dev/talkie-api", "label": "API" }
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Each tab's pane layout comes from its own `.lattices.json` — no changes
|
|
45
|
+
to per-project configs.
|
|
46
|
+
|
|
47
|
+
### How it works
|
|
48
|
+
|
|
49
|
+
- Session name follows the pattern `lattices-group-<id>` (e.g. `lattices-group-talkie`)
|
|
50
|
+
- 1 group = 1 tmux session. Each tab is a tmux window, and each window
|
|
51
|
+
gets its own panes from that project's `.lattices.json`
|
|
52
|
+
- You can still launch projects independently: `cd talkie-ios && lattices`
|
|
53
|
+
creates its own standalone session as before
|
|
54
|
+
|
|
55
|
+
### Tab group fields
|
|
56
|
+
|
|
57
|
+
| Field | Type | Description |
|
|
58
|
+
|----------------|----------|--------------------------------------|
|
|
59
|
+
| `id` | string | Unique identifier for the group |
|
|
60
|
+
| `label` | string | Display name shown in the UI |
|
|
61
|
+
| `tabs` | array | List of tab definitions |
|
|
62
|
+
| `tabs[].path` | string | Absolute path to project directory |
|
|
63
|
+
| `tabs[].label` | string? | Tab name (defaults to directory name) |
|
|
64
|
+
|
|
65
|
+
### CLI commands
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
lattices groups # List all groups with status
|
|
69
|
+
lattices group <id> # Launch or attach to a group
|
|
70
|
+
lattices tab <group> [tab] # Switch tab by label or index
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Examples:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
lattices group talkie # Launch all Talkie tabs
|
|
77
|
+
lattices tab talkie iOS # Switch to the iOS tab
|
|
78
|
+
lattices tab talkie 0 # Switch to first tab (by index)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Menu bar app
|
|
82
|
+
|
|
83
|
+
Tab groups appear above the project list in the menu bar panel.
|
|
84
|
+
Each group row shows:
|
|
85
|
+
|
|
86
|
+
- Status indicator (running/stopped)
|
|
87
|
+
- Tab count badge
|
|
88
|
+
- Expand/collapse to see individual tabs
|
|
89
|
+
- Launch/Attach and Kill buttons
|
|
90
|
+
- Per-tab "Go" buttons to switch and focus a specific tab
|
|
91
|
+
|
|
92
|
+
The command palette also includes group commands:
|
|
93
|
+
|
|
94
|
+
| Command | Description |
|
|
95
|
+
|----------------------------|----------------------------------------|
|
|
96
|
+
| Launch *group* | Start the group session |
|
|
97
|
+
| Attach *group* | Focus the running group session |
|
|
98
|
+
| *Group*: *Tab* | Switch to a specific tab in a group |
|
|
99
|
+
| Kill *group* Group | Terminate the group session |
|
|
100
|
+
|
|
101
|
+
## Layers
|
|
102
|
+
|
|
103
|
+
Layers let you group projects into switchable contexts. Define two or
|
|
104
|
+
three layers and switch between them. The target layer's windows come
|
|
105
|
+
to the front and tile into position; the previous layer's windows fall
|
|
106
|
+
behind.
|
|
107
|
+
|
|
108
|
+
All tmux sessions stay alive across switches. Nothing is detached or
|
|
109
|
+
killed. Layers only control which windows are focused.
|
|
110
|
+
|
|
111
|
+
### Configuration
|
|
112
|
+
|
|
113
|
+
Add `layers` to `~/.lattices/workspace.json`:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"name": "my-setup",
|
|
118
|
+
"layers": [
|
|
119
|
+
{
|
|
120
|
+
"id": "web",
|
|
121
|
+
"label": "Web",
|
|
122
|
+
"projects": [
|
|
123
|
+
{ "path": "/Users/you/dev/frontend", "tile": "left" },
|
|
124
|
+
{ "path": "/Users/you/dev/api", "tile": "right" }
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"id": "mobile",
|
|
129
|
+
"label": "Mobile",
|
|
130
|
+
"projects": [
|
|
131
|
+
{ "path": "/Users/you/dev/ios-app", "tile": "left" },
|
|
132
|
+
{ "path": "/Users/you/dev/backend", "tile": "right" }
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### App windows in layers
|
|
140
|
+
|
|
141
|
+
Layers aren't limited to terminal sessions. You can include any
|
|
142
|
+
application window by using the `app`, `title`, `url`, and `launch`
|
|
143
|
+
fields instead of `path`:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"name": "hudson",
|
|
148
|
+
"layers": [
|
|
149
|
+
{
|
|
150
|
+
"id": "main",
|
|
151
|
+
"label": "Main",
|
|
152
|
+
"projects": [
|
|
153
|
+
{ "app": "Google Chrome", "title": "GitHub", "tile": "left" },
|
|
154
|
+
{ "app": "Talkie", "tile": "top-right", "launch": "open -a Talkie" },
|
|
155
|
+
{ "path": "/Users/you/dev/frontend", "tile": "bottom-right" }
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "docs",
|
|
160
|
+
"label": "Docs",
|
|
161
|
+
"projects": [
|
|
162
|
+
{ "app": "Google Chrome", "url": "https://docs.example.com", "tile": "left" },
|
|
163
|
+
{ "app": "Notes", "title": "Sprint Notes", "tile": "right" }
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
When switching to a layer, lattices matches windows by `app` name and
|
|
171
|
+
optionally filters by `title` substring or `url` prefix. If `launch`
|
|
172
|
+
is provided and no matching window is found, the command is executed
|
|
173
|
+
to open the app.
|
|
174
|
+
|
|
175
|
+
### Using groups in layers
|
|
176
|
+
|
|
177
|
+
Layer projects can reference a tab group instead of a single path.
|
|
178
|
+
This lets you tile a whole group into a screen position:
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"name": "my-setup",
|
|
183
|
+
"groups": [
|
|
184
|
+
{
|
|
185
|
+
"id": "talkie",
|
|
186
|
+
"label": "Talkie",
|
|
187
|
+
"tabs": [
|
|
188
|
+
{ "path": "/Users/you/dev/talkie-ios", "label": "iOS" },
|
|
189
|
+
{ "path": "/Users/you/dev/talkie-web", "label": "Website" }
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
],
|
|
193
|
+
"layers": [
|
|
194
|
+
{
|
|
195
|
+
"id": "main",
|
|
196
|
+
"label": "Main",
|
|
197
|
+
"projects": [
|
|
198
|
+
{ "group": "talkie", "tile": "top-left" },
|
|
199
|
+
{ "path": "/Users/you/dev/design-system", "tile": "right" }
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
When switching to this layer, lattices launches (or focuses) the
|
|
207
|
+
"talkie" group session and tiles it to the top-left quarter, alongside
|
|
208
|
+
the design-system project on the right.
|
|
209
|
+
|
|
210
|
+
### Layer fields
|
|
211
|
+
|
|
212
|
+
| Field | Type | Description |
|
|
213
|
+
|-------------------|----------|------------------------------------------|
|
|
214
|
+
| `name` | string | Workspace name (for your reference) |
|
|
215
|
+
| `layers` | array | List of layer definitions |
|
|
216
|
+
| `layers[].id` | string | Unique identifier (e.g. `"web"`) |
|
|
217
|
+
| `layers[].label` | string | Display name shown in the UI |
|
|
218
|
+
| `layers[].projects` | array | Projects in this layer |
|
|
219
|
+
| `projects[].path` | string? | Absolute path to project directory |
|
|
220
|
+
| `projects[].group`| string? | Group ID (alternative to `path`) |
|
|
221
|
+
| `projects[].app` | string? | Application name (for non-terminal windows) |
|
|
222
|
+
| `projects[].title`| string? | Window title substring to match |
|
|
223
|
+
| `projects[].url` | string? | URL prefix to match (browser windows) |
|
|
224
|
+
| `projects[].launch`| string? | Shell command to launch the app if not found |
|
|
225
|
+
| `projects[].tile` | string? | Tile position (optional, see below) |
|
|
226
|
+
|
|
227
|
+
Each project entry must have either `path`, `group`, or `app` — pick one.
|
|
228
|
+
|
|
229
|
+
### Tile values
|
|
230
|
+
|
|
231
|
+
Any tile position from the [config reference](/docs/config#tile-positions)
|
|
232
|
+
works: `left`, `right`, `top`, `bottom`, `top-left`, `top-right`,
|
|
233
|
+
`bottom-left`, `bottom-right`, `left-third`, `center-third`,
|
|
234
|
+
`right-third`, `maximize`, `center`.
|
|
235
|
+
|
|
236
|
+
### Switching layers
|
|
237
|
+
|
|
238
|
+
Four ways to switch:
|
|
239
|
+
|
|
240
|
+
| Method | How |
|
|
241
|
+
|----------------------|------------------------------------------|
|
|
242
|
+
| **Hotkey** | Cmd+Option+1, Cmd+Option+2, Cmd+Option+3... |
|
|
243
|
+
| **Layer bar** | Click a layer pill in the menu bar panel |
|
|
244
|
+
| **Command palette** | Search "Switch to Layer" in Cmd+Shift+M |
|
|
245
|
+
| **CLI** | `lattices layer <name\|index>` |
|
|
246
|
+
|
|
247
|
+
When you switch to a layer:
|
|
248
|
+
|
|
249
|
+
1. Each project's window is **raised and focused**
|
|
250
|
+
2. App windows are matched by `app` / `title` / `url`
|
|
251
|
+
3. If a project isn't running yet, it gets **launched** automatically
|
|
252
|
+
4. Windows with a `tile` value are **tiled** to that position
|
|
253
|
+
5. The previous layer's windows stay open behind the new ones
|
|
254
|
+
|
|
255
|
+
The app remembers which layer was last active across restarts.
|
|
256
|
+
|
|
257
|
+
### Named layer switching
|
|
258
|
+
|
|
259
|
+
You can switch layers by name from the CLI:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
lattices layer hudson # Switch to the layer named "hudson"
|
|
263
|
+
lattices layer 0 # Switch to the first layer (by index)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
This is useful for scripting — you don't need to know the index,
|
|
267
|
+
just the layer's `id` or `label`.
|
|
268
|
+
|
|
269
|
+
### Window tagging
|
|
270
|
+
|
|
271
|
+
You can manually assign any window to a layer, even if it's not
|
|
272
|
+
declared in `workspace.json`. This is useful for ad-hoc windows
|
|
273
|
+
that you want to move with a layer:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
lattices window assign <wid> <layer> # Tag a window to a layer
|
|
277
|
+
lattices window map # Show all window→layer assignments
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Tagged windows behave like declared ones — they're raised and tiled
|
|
281
|
+
when their layer activates. Remove a tag by reassigning or with:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Via the agent API
|
|
285
|
+
await daemonCall('window.removeLayer', { wid: 1234 })
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Layer bezel
|
|
289
|
+
|
|
290
|
+
When you switch layers via hotkey, a translucent HUD pill appears
|
|
291
|
+
briefly at the top of the screen showing the new layer's name.
|
|
292
|
+
This provides instant visual feedback without interrupting your flow.
|
|
293
|
+
|
|
294
|
+
### Programmatic switching
|
|
295
|
+
|
|
296
|
+
Agents and scripts can switch layers via the agent API:
|
|
297
|
+
|
|
298
|
+
```js
|
|
299
|
+
import { daemonCall } from '@lattices/cli'
|
|
300
|
+
|
|
301
|
+
// List available layers
|
|
302
|
+
const { layers, active } = await daemonCall('layers.list')
|
|
303
|
+
console.log(`Active: ${layers[active].label}`)
|
|
304
|
+
|
|
305
|
+
// Switch to a layer by index
|
|
306
|
+
await daemonCall('layer.switch', { index: 0 })
|
|
307
|
+
|
|
308
|
+
// Switch to a layer by name
|
|
309
|
+
await daemonCall('layer.switch', { name: 'hudson' })
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The `layer.switch` call focuses and tiles all windows in the target
|
|
313
|
+
layer, just like the hotkey or command palette. A `layer.switched`
|
|
314
|
+
event is broadcast to all connected clients.
|
|
315
|
+
|
|
316
|
+
More methods in the [Agent API reference](/docs/api).
|
|
317
|
+
|
|
318
|
+
### Layer bar
|
|
319
|
+
|
|
320
|
+
When a workspace config is loaded, a layer bar appears between the
|
|
321
|
+
header and search field in the menu bar panel:
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
lattices 2 sessions [↔] [⟳]
|
|
325
|
+
┌────────────────────────────────────────┐
|
|
326
|
+
│ ● Web ○ Mobile │
|
|
327
|
+
│ ⌥1 ⌥2 │
|
|
328
|
+
└────────────────────────────────────────┘
|
|
329
|
+
Search projects...
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
- Active layer: filled green dot
|
|
333
|
+
- Inactive layers: dim outline dot
|
|
334
|
+
- Hotkey hints shown below each label
|
|
335
|
+
|
|
336
|
+
## Layout examples
|
|
337
|
+
|
|
338
|
+
### Single project
|
|
339
|
+
|
|
340
|
+
```json
|
|
341
|
+
{
|
|
342
|
+
"projects": [
|
|
343
|
+
{ "path": "/Users/you/dev/talkie" }
|
|
344
|
+
]
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
No `tile` — just focuses the window wherever it is.
|
|
349
|
+
|
|
350
|
+
### Two-project split
|
|
351
|
+
|
|
352
|
+
```json
|
|
353
|
+
{
|
|
354
|
+
"projects": [
|
|
355
|
+
{ "path": "/Users/you/dev/app", "tile": "left" },
|
|
356
|
+
{ "path": "/Users/you/dev/api", "tile": "right" }
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Mixed: apps + terminals
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"projects": [
|
|
366
|
+
{ "app": "Google Chrome", "title": "GitHub", "tile": "left" },
|
|
367
|
+
{ "path": "/Users/you/dev/api", "tile": "right" }
|
|
368
|
+
]
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Group + project
|
|
373
|
+
|
|
374
|
+
```json
|
|
375
|
+
{
|
|
376
|
+
"projects": [
|
|
377
|
+
{ "group": "talkie", "tile": "left" },
|
|
378
|
+
{ "path": "/Users/you/dev/api", "tile": "right" }
|
|
379
|
+
]
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Four quadrants
|
|
384
|
+
|
|
385
|
+
```json
|
|
386
|
+
{
|
|
387
|
+
"projects": [
|
|
388
|
+
{ "path": "/Users/you/dev/frontend", "tile": "top-left" },
|
|
389
|
+
{ "path": "/Users/you/dev/backend", "tile": "top-right" },
|
|
390
|
+
{ "path": "/Users/you/dev/mobile", "tile": "bottom-left" },
|
|
391
|
+
{ "path": "/Users/you/dev/infra", "tile": "bottom-right" }
|
|
392
|
+
]
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Tips
|
|
397
|
+
|
|
398
|
+
- Projects don't need a `.lattices.json` config to be in a layer — any
|
|
399
|
+
directory path works. If the project has a config, lattices uses it; if
|
|
400
|
+
not, it opens a plain terminal in that directory.
|
|
401
|
+
- App windows don't need any config at all — just specify `app` and
|
|
402
|
+
optionally `title` or `url` to match the right window.
|
|
403
|
+
- You can have up to 9 layers (Cmd+Option+1 through Cmd+Option+9).
|
|
404
|
+
- Edit `workspace.json` by hand — the app re-reads it on launch. Use
|
|
405
|
+
the Refresh Projects button or restart the app to pick up changes.
|
|
406
|
+
- The `tile` field is optional. Omit it if you just want the window
|
|
407
|
+
focused without repositioning.
|
|
408
|
+
- Tab groups and standalone projects can coexist in the same workspace.
|
|
409
|
+
Use groups for related project families, standalone paths for
|
|
410
|
+
individual projects.
|
package/docs/ocr.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Screen OCR & Search
|
|
3
|
+
description: Vision-powered screen reading with full-text search for agents
|
|
4
|
+
order: 3.5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
The menu bar app reads text from visible windows using Apple's Vision
|
|
8
|
+
framework and stores results in a local SQLite database with FTS5
|
|
9
|
+
full-text search. Agents can use this to "see" what's on screen.
|
|
10
|
+
|
|
11
|
+
## Enabling OCR
|
|
12
|
+
|
|
13
|
+
Open **Settings** (via command palette or gear icon) and toggle
|
|
14
|
+
**Search & OCR** on. OCR is disabled by default.
|
|
15
|
+
|
|
16
|
+
### Accuracy modes
|
|
17
|
+
|
|
18
|
+
| Mode | Description |
|
|
19
|
+
|------|-------------|
|
|
20
|
+
| **Accurate** (default) | Higher quality recognition, slower processing |
|
|
21
|
+
| **Fast** | Lower latency, reduced accuracy |
|
|
22
|
+
|
|
23
|
+
Both modes use `VNRecognizeTextRequest` with language correction enabled.
|
|
24
|
+
|
|
25
|
+
## How scanning works
|
|
26
|
+
|
|
27
|
+
The app runs two scan schedules:
|
|
28
|
+
|
|
29
|
+
| Schedule | Interval | Window limit | Purpose |
|
|
30
|
+
|----------|----------|-------------|---------|
|
|
31
|
+
| **Quick scan** | 60 seconds | Top 5 windows | Keep recent content fresh |
|
|
32
|
+
| **Deep scan** | 2 hours | Up to 15 windows | Catch less-active windows |
|
|
33
|
+
|
|
34
|
+
Both intervals and limits are configurable in Settings.
|
|
35
|
+
|
|
36
|
+
### Change detection
|
|
37
|
+
|
|
38
|
+
Before running OCR on a window, the app captures the window image and
|
|
39
|
+
computes a SHA-256 hash of the pixel data. If the hash matches the
|
|
40
|
+
previous scan, the cached result is reused. No Vision processing needed.
|
|
41
|
+
|
|
42
|
+
This keeps CPU usage low when windows haven't changed. A 100ms throttle
|
|
43
|
+
between windows further limits processing bursts.
|
|
44
|
+
|
|
45
|
+
## Browsing results
|
|
46
|
+
|
|
47
|
+
The **Recent Captures** section in Settings shows OCR results grouped by
|
|
48
|
+
app. Each entry displays the window title, recognized text preview, and
|
|
49
|
+
timestamp.
|
|
50
|
+
|
|
51
|
+
## Searching
|
|
52
|
+
|
|
53
|
+
### From the command palette
|
|
54
|
+
|
|
55
|
+
The OmniSearch bar (Cmd+Shift+M) searches OCR content alongside windows,
|
|
56
|
+
projects, and sessions. Matches show as "Screen Text" results with
|
|
57
|
+
contextual snippets.
|
|
58
|
+
|
|
59
|
+
### From the CLI
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# View current OCR snapshot
|
|
63
|
+
lattices ocr
|
|
64
|
+
|
|
65
|
+
# Search OCR history
|
|
66
|
+
lattices ocr search "error OR failed"
|
|
67
|
+
|
|
68
|
+
# Trigger an immediate deep scan
|
|
69
|
+
lattices ocr scan
|
|
70
|
+
|
|
71
|
+
# View OCR history for a specific window ID
|
|
72
|
+
lattices ocr history 12345
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### From the agent API
|
|
76
|
+
|
|
77
|
+
Agents can query OCR data through four API methods:
|
|
78
|
+
|
|
79
|
+
| Method | Description |
|
|
80
|
+
|--------|-------------|
|
|
81
|
+
| `ocr.snapshot` | Current OCR results for all visible windows |
|
|
82
|
+
| `ocr.search` | Full-text search across history (FTS5 syntax) |
|
|
83
|
+
| `ocr.history` | Timeline of OCR results for a specific window |
|
|
84
|
+
| `ocr.scan` | Trigger an immediate deep scan |
|
|
85
|
+
|
|
86
|
+
#### `ocr.snapshot`
|
|
87
|
+
|
|
88
|
+
Returns the latest OCR results for all on-screen windows.
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
import { daemonCall } from '@lattices/cli'
|
|
92
|
+
|
|
93
|
+
const snapshot = await daemonCall('ocr.snapshot')
|
|
94
|
+
// [{ wid, app, title, frame, fullText, blocks, timestamp }, ...]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Each result includes:
|
|
98
|
+
- `wid` — window ID
|
|
99
|
+
- `app` — application name
|
|
100
|
+
- `title` — window title
|
|
101
|
+
- `frame` — `{ x, y, w, h }` screen position
|
|
102
|
+
- `fullText` — all recognized text concatenated
|
|
103
|
+
- `blocks` — individual text blocks with `{ text, confidence, x, y, w, h }`
|
|
104
|
+
- `timestamp` — Unix timestamp of the scan
|
|
105
|
+
|
|
106
|
+
#### `ocr.search`
|
|
107
|
+
|
|
108
|
+
Full-text search across OCR history using FTS5 query syntax.
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
const results = await daemonCall('ocr.search', {
|
|
112
|
+
query: 'error OR failed', // FTS5 query (required)
|
|
113
|
+
app: 'Terminal', // filter by app name (optional)
|
|
114
|
+
limit: 50, // max results (optional, default 50)
|
|
115
|
+
live: false // search in-memory snapshot instead of history (optional)
|
|
116
|
+
})
|
|
117
|
+
// [{ id, wid, app, title, frame, fullText, snippet, timestamp }, ...]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The `snippet` field contains FTS5-highlighted text with `«` and `»`
|
|
121
|
+
delimiters around matched terms.
|
|
122
|
+
|
|
123
|
+
#### `ocr.history`
|
|
124
|
+
|
|
125
|
+
Get the OCR content timeline for a specific window.
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
const history = await daemonCall('ocr.history', {
|
|
129
|
+
wid: 12345, // window ID (required)
|
|
130
|
+
limit: 50 // max results (optional, default 50)
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### `ocr.scan`
|
|
135
|
+
|
|
136
|
+
Trigger an immediate deep scan (all visible windows up to the deep limit).
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
await daemonCall('ocr.scan')
|
|
140
|
+
// { ok: true }
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Storage
|
|
144
|
+
|
|
145
|
+
OCR data is stored in `~/.lattices/ocr.db`, a SQLite database in WAL
|
|
146
|
+
(Write-Ahead Logging) mode for safe concurrent reads.
|
|
147
|
+
|
|
148
|
+
The schema uses two tables:
|
|
149
|
+
- `ocr_entry` — stores window ID, app, title, frame, full text, and timestamp
|
|
150
|
+
- `ocr_fts` — FTS5 virtual table indexing `full_text`, `app`, and `title`
|
|
151
|
+
|
|
152
|
+
Triggers keep the FTS index in sync with inserts, updates, and deletes.
|
|
153
|
+
|
|
154
|
+
Entries older than 3 days are automatically deleted.
|
|
155
|
+
|
|
156
|
+
## Agent usage
|
|
157
|
+
|
|
158
|
+
A typical agent workflow: trigger a scan, then search for relevant content.
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
import { daemonCall } from '@lattices/cli'
|
|
162
|
+
|
|
163
|
+
// Trigger a fresh scan
|
|
164
|
+
await daemonCall('ocr.scan')
|
|
165
|
+
|
|
166
|
+
// Search for compilation errors across all windows
|
|
167
|
+
const errors = await daemonCall('ocr.search', { query: 'error OR warning' })
|
|
168
|
+
|
|
169
|
+
for (const result of errors) {
|
|
170
|
+
console.log(`[${result.app}] ${result.title}`)
|
|
171
|
+
console.log(result.snippet)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Read everything currently visible
|
|
175
|
+
const snapshot = await daemonCall('ocr.snapshot')
|
|
176
|
+
for (const win of snapshot) {
|
|
177
|
+
console.log(`${win.app}: ${win.fullText.slice(0, 200)}`)
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Requirements
|
|
182
|
+
|
|
183
|
+
- **Screen Recording** permission — required to capture window images
|
|
184
|
+
- Grant via System Settings > Privacy & Security > Screen Recording
|
|
185
|
+
- Add the lattices menu bar app to the allowed list
|
package/docs/overview.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Overview
|
|
3
|
+
description: What lattices is and who it's for
|
|
4
|
+
order: 0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
lattices is an agentic window manager for macOS. It provides a programmable
|
|
8
|
+
workspace, smart layout management, and managed tmux sessions — all
|
|
9
|
+
controllable from the CLI or a 35+-method agent API.
|
|
10
|
+
|
|
11
|
+
## The problem
|
|
12
|
+
|
|
13
|
+
I kept losing track of windows. Terminal here, dev server there, browser
|
|
14
|
+
somewhere on the second monitor, three other projects buried under it all.
|
|
15
|
+
Every morning I'd rebuild the same layout. Half my context-switching was
|
|
16
|
+
just hunting for the right window.
|
|
17
|
+
|
|
18
|
+
AI agents have it worse. They're stuck inside a single shell with no way
|
|
19
|
+
to see what's on screen, arrange windows, or jump between projects.
|
|
20
|
+
|
|
21
|
+
## The solution
|
|
22
|
+
|
|
23
|
+
lattices addresses both sides with three layers:
|
|
24
|
+
|
|
25
|
+
### Programmable workspace
|
|
26
|
+
|
|
27
|
+
The CLI and agent API expose everything: query what's on screen, search
|
|
28
|
+
window text, tile layouts, manage sessions. Claude Code skills, MCP servers,
|
|
29
|
+
or your own scripts can drive your desktop the same way you do. Your
|
|
30
|
+
workspace becomes infrastructure you can observe and control programmatically.
|
|
31
|
+
|
|
32
|
+
### Smart layout manager
|
|
33
|
+
|
|
34
|
+
A native menu bar app tracks every window across all your monitors. Tile
|
|
35
|
+
with hotkeys, organize into switchable layers, snap to grids. It reads
|
|
36
|
+
your windows too — extracting text from UI elements every 60 seconds and
|
|
37
|
+
running Vision OCR on background windows every 2 hours. Everything is
|
|
38
|
+
searchable.
|
|
39
|
+
|
|
40
|
+
### Managed tmux sessions
|
|
41
|
+
|
|
42
|
+
We make tmux easy. Declare your dev environment in a `.lattices.json`:
|
|
43
|
+
which panes, which commands, what layout. lattices builds it, runs it,
|
|
44
|
+
and keeps it alive. Use your own terminal — sessions survive reboots
|
|
45
|
+
and you can reattach anytime.
|
|
46
|
+
|
|
47
|
+
## What's included
|
|
48
|
+
|
|
49
|
+
| Component | Description |
|
|
50
|
+
|-----------|-------------|
|
|
51
|
+
| **CLI** | The `lattices` command. Tile windows, manage sessions, scan screen text, control the workspace from your terminal |
|
|
52
|
+
| **Menu bar app** | Native macOS companion. Command palette, window tiling, project discovery, screen text indexing |
|
|
53
|
+
| **Agent API** | WebSocket server on `ws://127.0.0.1:9399`. 35+ methods, 5 real-time events |
|
|
54
|
+
| **Screen scanner** | Reads text from visible windows using Accessibility API (60s) and Apple Vision OCR (2h), indexes with FTS5 |
|
|
55
|
+
| **Node.js client** | Zero-dependency `daemonCall()` helper for scripting |
|
|
56
|
+
|
|
57
|
+
## Example
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
cd ~/my-project && lattices
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Agents get the same control programmatically:
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
import { daemonCall } from '@lattices/cli'
|
|
67
|
+
await daemonCall('session.launch', { path: '/Users/you/dev/frontend' })
|
|
68
|
+
await daemonCall('window.tile', { session: 'frontend-a1b2c3', position: 'left' })
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Who it's for
|
|
72
|
+
|
|
73
|
+
- Developers who juggle multiple projects and want faster window management
|
|
74
|
+
- People building AI agents that need to observe and control the desktop
|
|
75
|
+
- Power users who work across multiple macOS Spaces
|
|
76
|
+
- Anyone who wants persistent, auto-configured terminal sessions
|
|
77
|
+
|
|
78
|
+
## Requirements
|
|
79
|
+
|
|
80
|
+
- macOS 13.0+
|
|
81
|
+
- Node.js 18+
|
|
82
|
+
|
|
83
|
+
### Dev dependencies
|
|
84
|
+
|
|
85
|
+
- Swift 5.9+ — only to build the menu bar app from source
|
|
86
|
+
- tmux — needed for persistent terminal sessions (`brew install tmux`)
|
|
87
|
+
|
|
88
|
+
## Next steps
|
|
89
|
+
|
|
90
|
+
- [Quickstart](/docs/quickstart): install and run your first session in 2 minutes
|
|
91
|
+
- [Configuration](/docs/config): `.lattices.json` format and CLI commands
|
|
92
|
+
- [Screen scanning](/docs/ocr): AX text extraction, Vision OCR, and full-text search
|
|
93
|
+
- [Concepts](/docs/concepts): architecture, glossary, and how it all works
|
|
94
|
+
- [Agent API](/docs/api): RPC method reference for agents and scripts
|