@soederpop/luca 0.0.29 → 0.0.30
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/commands/try-all-challenges.ts +1 -1
- package/docs/TABLE-OF-CONTENTS.md +0 -3
- package/docs/tutorials/20-browser-esm.md +234 -0
- package/package.json +1 -1
- package/src/agi/container.server.ts +4 -0
- package/src/agi/features/assistant.ts +62 -1
- package/src/agi/features/browser-use.ts +623 -0
- package/src/bootstrap/generated.ts +236 -308
- package/src/cli/build-info.ts +2 -2
- package/src/clients/rest.ts +7 -7
- package/src/commands/chat.ts +22 -0
- package/src/commands/describe.ts +67 -2
- package/src/commands/prompt.ts +23 -3
- package/src/container.ts +411 -113
- package/src/helper.ts +189 -5
- package/src/introspection/generated.agi.ts +17148 -11148
- package/src/introspection/generated.node.ts +5179 -2200
- package/src/introspection/generated.web.ts +379 -291
- package/src/introspection/index.ts +7 -0
- package/src/introspection/scan.ts +224 -7
- package/src/node/container.ts +31 -10
- package/src/node/features/content-db.ts +7 -7
- package/src/node/features/disk-cache.ts +11 -11
- package/src/node/features/esbuild.ts +3 -3
- package/src/node/features/file-manager.ts +15 -15
- package/src/node/features/fs.ts +23 -22
- package/src/node/features/git.ts +10 -10
- package/src/node/features/ink.ts +13 -13
- package/src/node/features/ipc-socket.ts +8 -8
- package/src/node/features/networking.ts +3 -3
- package/src/node/features/os.ts +7 -7
- package/src/node/features/package-finder.ts +15 -15
- package/src/node/features/proc.ts +1 -1
- package/src/node/features/ui.ts +13 -13
- package/src/node/features/vm.ts +4 -4
- package/src/scaffolds/generated.ts +1 -1
- package/src/servers/express.ts +6 -6
- package/src/servers/mcp.ts +4 -4
- package/src/servers/socket.ts +6 -6
- package/docs/apis/features/node/window-manager.md +0 -445
- package/docs/examples/window-manager-layouts.md +0 -180
- package/docs/examples/window-manager.md +0 -125
- package/docs/window-manager-fix.md +0 -249
- package/scripts/test-window-manager-lifecycle.ts +0 -86
- package/scripts/test-window-manager.ts +0 -43
- package/src/node/features/window-manager.ts +0 -1603
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
# WindowManager (features.windowManager)
|
|
2
|
-
|
|
3
|
-
WindowManager Feature — Native window control via LucaVoiceLauncher Acts as an IPC server that the native macOS launcher app connects to. Communicates over a Unix domain socket using NDJSON (newline-delimited JSON). **Protocol:** - Bun listens on a Unix domain socket; the native app connects as a client - Window dispatch commands are sent as NDJSON with a `window` field - The app executes window commands and sends back `windowAck` messages - Any non-windowAck message from the app is emitted as a `message` event - Other features can use `send()` to write arbitrary NDJSON to the app **Capabilities:** - Spawn native browser windows with configurable chrome - Navigate, focus, close, and eval JavaScript in windows - Automatic socket file cleanup and fallback paths
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
container.feature('windowManager', {
|
|
9
|
-
// Path to the Unix domain socket the server listens on
|
|
10
|
-
socketPath,
|
|
11
|
-
// Automatically start listening when the feature is enabled
|
|
12
|
-
autoListen,
|
|
13
|
-
// Per-request timeout in milliseconds for window operations
|
|
14
|
-
requestTimeoutMs,
|
|
15
|
-
})
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Options (Zod v4 schema)
|
|
19
|
-
|
|
20
|
-
| Property | Type | Description |
|
|
21
|
-
|----------|------|-------------|
|
|
22
|
-
| `socketPath` | `string` | Path to the Unix domain socket the server listens on |
|
|
23
|
-
| `autoListen` | `boolean` | Automatically start listening when the feature is enabled |
|
|
24
|
-
| `requestTimeoutMs` | `number` | Per-request timeout in milliseconds for window operations |
|
|
25
|
-
|
|
26
|
-
## Methods
|
|
27
|
-
|
|
28
|
-
### enable
|
|
29
|
-
|
|
30
|
-
**Parameters:**
|
|
31
|
-
|
|
32
|
-
| Name | Type | Required | Description |
|
|
33
|
-
|------|------|----------|-------------|
|
|
34
|
-
| `options` | `any` | | Parameter options |
|
|
35
|
-
|
|
36
|
-
**Returns:** `Promise<this>`
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
### listen
|
|
41
|
-
|
|
42
|
-
Start listening on the Unix domain socket for the native app to connect. Fire-and-forget — binds the socket and returns immediately. Sits quietly until the native app connects; does nothing visible if it never does.
|
|
43
|
-
|
|
44
|
-
**Parameters:**
|
|
45
|
-
|
|
46
|
-
| Name | Type | Required | Description |
|
|
47
|
-
|------|------|----------|-------------|
|
|
48
|
-
| `socketPath` | `string` | | Override the configured socket path |
|
|
49
|
-
|
|
50
|
-
**Returns:** `this`
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
### stop
|
|
55
|
-
|
|
56
|
-
Stop the IPC server and clean up all connections. Rejects any pending window operation requests.
|
|
57
|
-
|
|
58
|
-
**Returns:** `Promise<this>`
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
### spawn
|
|
63
|
-
|
|
64
|
-
Spawn a new native browser window. Sends a window dispatch to the app and waits for the ack.
|
|
65
|
-
|
|
66
|
-
**Parameters:**
|
|
67
|
-
|
|
68
|
-
| Name | Type | Required | Description |
|
|
69
|
-
|------|------|----------|-------------|
|
|
70
|
-
| `opts` | `SpawnOptions` | | Window configuration (url, dimensions, chrome options) |
|
|
71
|
-
|
|
72
|
-
`SpawnOptions` properties:
|
|
73
|
-
|
|
74
|
-
| Property | Type | Description |
|
|
75
|
-
|----------|------|-------------|
|
|
76
|
-
| `url` | `string` | |
|
|
77
|
-
| `width` | `DimensionValue` | |
|
|
78
|
-
| `height` | `DimensionValue` | |
|
|
79
|
-
| `x` | `DimensionValue` | |
|
|
80
|
-
| `y` | `DimensionValue` | |
|
|
81
|
-
| `alwaysOnTop` | `boolean` | |
|
|
82
|
-
| `window` | `{
|
|
83
|
-
decorations?: 'normal' | 'hiddenTitleBar' | 'none'
|
|
84
|
-
transparent?: boolean
|
|
85
|
-
shadow?: boolean
|
|
86
|
-
alwaysOnTop?: boolean
|
|
87
|
-
opacity?: number
|
|
88
|
-
clickThrough?: boolean
|
|
89
|
-
}` | |
|
|
90
|
-
|
|
91
|
-
**Returns:** `Promise<WindowHandle>`
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
### spawnTTY
|
|
96
|
-
|
|
97
|
-
Spawn a native terminal window running a command. The terminal is read-only — stdout/stderr are rendered with ANSI support. Closing the window terminates the process.
|
|
98
|
-
|
|
99
|
-
**Parameters:**
|
|
100
|
-
|
|
101
|
-
| Name | Type | Required | Description |
|
|
102
|
-
|------|------|----------|-------------|
|
|
103
|
-
| `opts` | `SpawnTTYOptions` | ✓ | Terminal configuration (command, args, cwd, dimensions, etc.) |
|
|
104
|
-
|
|
105
|
-
`SpawnTTYOptions` properties:
|
|
106
|
-
|
|
107
|
-
| Property | Type | Description |
|
|
108
|
-
|----------|------|-------------|
|
|
109
|
-
| `command` | `string` | Executable name or path (required). |
|
|
110
|
-
| `args` | `string[]` | Arguments passed after the command. |
|
|
111
|
-
| `cwd` | `string` | Working directory for the process. |
|
|
112
|
-
| `env` | `Record<string, string>` | Environment variable overrides. |
|
|
113
|
-
| `cols` | `number` | Initial terminal columns. |
|
|
114
|
-
| `rows` | `number` | Initial terminal rows. |
|
|
115
|
-
| `title` | `string` | Window title. |
|
|
116
|
-
| `width` | `DimensionValue` | Window width in points. |
|
|
117
|
-
| `height` | `DimensionValue` | Window height in points. |
|
|
118
|
-
| `x` | `DimensionValue` | Window x position. |
|
|
119
|
-
| `y` | `DimensionValue` | Window y position. |
|
|
120
|
-
| `window` | `SpawnOptions['window']` | Chrome options (decorations, alwaysOnTop, etc.) |
|
|
121
|
-
|
|
122
|
-
**Returns:** `Promise<WindowHandle>`
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
### focus
|
|
127
|
-
|
|
128
|
-
Bring a window to the front.
|
|
129
|
-
|
|
130
|
-
**Parameters:**
|
|
131
|
-
|
|
132
|
-
| Name | Type | Required | Description |
|
|
133
|
-
|------|------|----------|-------------|
|
|
134
|
-
| `windowId` | `string` | | The window ID. If omitted, the app uses the most recent window. |
|
|
135
|
-
|
|
136
|
-
**Returns:** `Promise<WindowAckResult>`
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
### close
|
|
141
|
-
|
|
142
|
-
Close a window.
|
|
143
|
-
|
|
144
|
-
**Parameters:**
|
|
145
|
-
|
|
146
|
-
| Name | Type | Required | Description |
|
|
147
|
-
|------|------|----------|-------------|
|
|
148
|
-
| `windowId` | `string` | | The window ID. If omitted, the app closes the most recent window. |
|
|
149
|
-
|
|
150
|
-
**Returns:** `Promise<WindowAckResult>`
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
### navigate
|
|
155
|
-
|
|
156
|
-
Navigate a window to a new URL.
|
|
157
|
-
|
|
158
|
-
**Parameters:**
|
|
159
|
-
|
|
160
|
-
| Name | Type | Required | Description |
|
|
161
|
-
|------|------|----------|-------------|
|
|
162
|
-
| `windowId` | `string` | ✓ | The window ID |
|
|
163
|
-
| `url` | `string` | ✓ | The URL to navigate to |
|
|
164
|
-
|
|
165
|
-
**Returns:** `Promise<WindowAckResult>`
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
### eval
|
|
170
|
-
|
|
171
|
-
Evaluate JavaScript in a window's web view.
|
|
172
|
-
|
|
173
|
-
**Parameters:**
|
|
174
|
-
|
|
175
|
-
| Name | Type | Required | Description |
|
|
176
|
-
|------|------|----------|-------------|
|
|
177
|
-
| `windowId` | `string` | ✓ | The window ID |
|
|
178
|
-
| `code` | `string` | ✓ | JavaScript code to evaluate |
|
|
179
|
-
| `opts` | `{ timeoutMs?: number; returnJson?: boolean }` | | timeoutMs (default 5000), returnJson (default true) |
|
|
180
|
-
|
|
181
|
-
**Returns:** `Promise<WindowAckResult>`
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
### screengrab
|
|
186
|
-
|
|
187
|
-
Capture a PNG screenshot from a window.
|
|
188
|
-
|
|
189
|
-
**Parameters:**
|
|
190
|
-
|
|
191
|
-
| Name | Type | Required | Description |
|
|
192
|
-
|------|------|----------|-------------|
|
|
193
|
-
| `opts` | `WindowScreenGrabOptions` | ✓ | Window target and output path |
|
|
194
|
-
|
|
195
|
-
`WindowScreenGrabOptions` properties:
|
|
196
|
-
|
|
197
|
-
| Property | Type | Description |
|
|
198
|
-
|----------|------|-------------|
|
|
199
|
-
| `windowId` | `string` | Window ID. If omitted, the launcher uses the most recent window. |
|
|
200
|
-
| `path` | `string` | Output file path for the PNG image. |
|
|
201
|
-
|
|
202
|
-
**Returns:** `Promise<WindowAckResult>`
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
### video
|
|
207
|
-
|
|
208
|
-
Record a video from a window to disk.
|
|
209
|
-
|
|
210
|
-
**Parameters:**
|
|
211
|
-
|
|
212
|
-
| Name | Type | Required | Description |
|
|
213
|
-
|------|------|----------|-------------|
|
|
214
|
-
| `opts` | `WindowVideoOptions` | ✓ | Window target, output path, and optional duration |
|
|
215
|
-
|
|
216
|
-
`WindowVideoOptions` properties:
|
|
217
|
-
|
|
218
|
-
| Property | Type | Description |
|
|
219
|
-
|----------|------|-------------|
|
|
220
|
-
| `windowId` | `string` | Window ID. If omitted, the launcher uses the most recent window. |
|
|
221
|
-
| `path` | `string` | Output file path for the video file. |
|
|
222
|
-
| `durationMs` | `number` | Recording duration in milliseconds. |
|
|
223
|
-
|
|
224
|
-
**Returns:** `Promise<WindowAckResult>`
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
### window
|
|
229
|
-
|
|
230
|
-
Get a WindowHandle for chainable operations on a specific window. Returns the tracked handle if one exists, otherwise creates a new one.
|
|
231
|
-
|
|
232
|
-
**Parameters:**
|
|
233
|
-
|
|
234
|
-
| Name | Type | Required | Description |
|
|
235
|
-
|------|------|----------|-------------|
|
|
236
|
-
| `windowId` | `string` | ✓ | The window ID |
|
|
237
|
-
|
|
238
|
-
**Returns:** `WindowHandle`
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
### spawnLayout
|
|
243
|
-
|
|
244
|
-
Spawn multiple windows in parallel from a layout configuration. Returns handles in the same order as the config entries.
|
|
245
|
-
|
|
246
|
-
**Parameters:**
|
|
247
|
-
|
|
248
|
-
| Name | Type | Required | Description |
|
|
249
|
-
|------|------|----------|-------------|
|
|
250
|
-
| `config` | `LayoutEntry[]` | ✓ | Array of layout entries (window or tty) |
|
|
251
|
-
|
|
252
|
-
**Returns:** `Promise<WindowHandle[]>`
|
|
253
|
-
|
|
254
|
-
```ts
|
|
255
|
-
const handles = await wm.spawnLayout([
|
|
256
|
-
{ type: 'window', url: 'https://google.com', width: 800, height: 600 },
|
|
257
|
-
{ type: 'tty', command: 'htop' },
|
|
258
|
-
{ url: 'https://github.com' }, // defaults to window
|
|
259
|
-
])
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
### spawnLayouts
|
|
265
|
-
|
|
266
|
-
Spawn multiple layouts sequentially. Each layout's windows spawn in parallel, but the next layout waits for the previous one to fully complete.
|
|
267
|
-
|
|
268
|
-
**Parameters:**
|
|
269
|
-
|
|
270
|
-
| Name | Type | Required | Description |
|
|
271
|
-
|------|------|----------|-------------|
|
|
272
|
-
| `configs` | `LayoutEntry[][]` | ✓ | Array of layout configurations |
|
|
273
|
-
|
|
274
|
-
**Returns:** `Promise<WindowHandle[][]>`
|
|
275
|
-
|
|
276
|
-
```ts
|
|
277
|
-
const [firstBatch, secondBatch] = await wm.spawnLayouts([
|
|
278
|
-
[{ url: 'https://google.com' }, { url: 'https://github.com' }],
|
|
279
|
-
[{ type: 'tty', command: 'htop' }],
|
|
280
|
-
])
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
### send
|
|
286
|
-
|
|
287
|
-
Write an NDJSON message to the connected app client. Public so other features can send arbitrary protocol messages over the same socket.
|
|
288
|
-
|
|
289
|
-
**Parameters:**
|
|
290
|
-
|
|
291
|
-
| Name | Type | Required | Description |
|
|
292
|
-
|------|------|----------|-------------|
|
|
293
|
-
| `msg` | `Record<string, any>` | ✓ | The message object to send (will be JSON-serialized + newline) |
|
|
294
|
-
|
|
295
|
-
**Returns:** `boolean`
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
## Getters
|
|
300
|
-
|
|
301
|
-
| Property | Type | Description |
|
|
302
|
-
|----------|------|-------------|
|
|
303
|
-
| `isListening` | `boolean` | Whether the IPC server is currently listening. |
|
|
304
|
-
| `isClientConnected` | `boolean` | Whether the native app client is currently connected. |
|
|
305
|
-
|
|
306
|
-
## Events (Zod v4 schema)
|
|
307
|
-
|
|
308
|
-
### listening
|
|
309
|
-
|
|
310
|
-
Emitted when the IPC server starts listening
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
### clientConnected
|
|
315
|
-
|
|
316
|
-
Emitted when the native app connects
|
|
317
|
-
|
|
318
|
-
**Event Arguments:**
|
|
319
|
-
|
|
320
|
-
| Name | Type | Description |
|
|
321
|
-
|------|------|-------------|
|
|
322
|
-
| `arg0` | `any` | The client socket |
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
### clientDisconnected
|
|
327
|
-
|
|
328
|
-
Emitted when the native app disconnects
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
### windowAck
|
|
333
|
-
|
|
334
|
-
Emitted when a window ack is received from the app
|
|
335
|
-
|
|
336
|
-
**Event Arguments:**
|
|
337
|
-
|
|
338
|
-
| Name | Type | Description |
|
|
339
|
-
|------|------|-------------|
|
|
340
|
-
| `arg0` | `any` | The window ack payload |
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
### windowClosed
|
|
345
|
-
|
|
346
|
-
Emitted when the native app reports a window closed event
|
|
347
|
-
|
|
348
|
-
**Event Arguments:**
|
|
349
|
-
|
|
350
|
-
| Name | Type | Description |
|
|
351
|
-
|------|------|-------------|
|
|
352
|
-
| `arg0` | `any` | Window lifecycle payload emitted when a window closes |
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
### terminalExited
|
|
357
|
-
|
|
358
|
-
Emitted when the native app reports a terminal process exit event
|
|
359
|
-
|
|
360
|
-
**Event Arguments:**
|
|
361
|
-
|
|
362
|
-
| Name | Type | Description |
|
|
363
|
-
|------|------|-------------|
|
|
364
|
-
| `arg0` | `any` | Terminal lifecycle payload emitted when a terminal process exits |
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
### message
|
|
369
|
-
|
|
370
|
-
Emitted for any incoming message that is not a windowAck
|
|
371
|
-
|
|
372
|
-
**Event Arguments:**
|
|
373
|
-
|
|
374
|
-
| Name | Type | Description |
|
|
375
|
-
|------|------|-------------|
|
|
376
|
-
| `arg0` | `any` | The parsed message object |
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
### error
|
|
381
|
-
|
|
382
|
-
Emitted on error
|
|
383
|
-
|
|
384
|
-
**Event Arguments:**
|
|
385
|
-
|
|
386
|
-
| Name | Type | Description |
|
|
387
|
-
|------|------|-------------|
|
|
388
|
-
| `arg0` | `any` | The error |
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
## State (Zod v4 schema)
|
|
393
|
-
|
|
394
|
-
| Property | Type | Description |
|
|
395
|
-
|----------|------|-------------|
|
|
396
|
-
| `enabled` | `boolean` | Whether this feature is currently enabled |
|
|
397
|
-
| `listening` | `boolean` | Whether the IPC server is listening |
|
|
398
|
-
| `clientConnected` | `boolean` | Whether the native launcher app is connected |
|
|
399
|
-
| `socketPath` | `string` | The socket path in use |
|
|
400
|
-
| `windowCount` | `number` | Number of tracked windows |
|
|
401
|
-
| `lastError` | `string` | Last error message |
|
|
402
|
-
|
|
403
|
-
## Examples
|
|
404
|
-
|
|
405
|
-
**features.windowManager**
|
|
406
|
-
|
|
407
|
-
```ts
|
|
408
|
-
const wm = container.feature('windowManager', { enable: true, autoListen: true })
|
|
409
|
-
|
|
410
|
-
const handle = await wm.spawn({ url: 'https://google.com', width: 800, height: 600 })
|
|
411
|
-
handle.on('close', (msg) => console.log('window closed'))
|
|
412
|
-
await handle.navigate('https://news.ycombinator.com')
|
|
413
|
-
const title = await handle.eval('document.title')
|
|
414
|
-
await handle.close()
|
|
415
|
-
|
|
416
|
-
// Other features can listen for non-window messages
|
|
417
|
-
wm.on('message', (msg) => console.log('App says:', msg))
|
|
418
|
-
|
|
419
|
-
// Other features can write raw NDJSON to the app
|
|
420
|
-
wm.send({ id: 'abc', status: 'processing', speech: 'Working on it' })
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
**spawnLayout**
|
|
426
|
-
|
|
427
|
-
```ts
|
|
428
|
-
const handles = await wm.spawnLayout([
|
|
429
|
-
{ type: 'window', url: 'https://google.com', width: 800, height: 600 },
|
|
430
|
-
{ type: 'tty', command: 'htop' },
|
|
431
|
-
{ url: 'https://github.com' }, // defaults to window
|
|
432
|
-
])
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
**spawnLayouts**
|
|
438
|
-
|
|
439
|
-
```ts
|
|
440
|
-
const [firstBatch, secondBatch] = await wm.spawnLayouts([
|
|
441
|
-
[{ url: 'https://google.com' }, { url: 'https://github.com' }],
|
|
442
|
-
[{ type: 'tty', command: 'htop' }],
|
|
443
|
-
])
|
|
444
|
-
```
|
|
445
|
-
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "Window Manager Layouts"
|
|
3
|
-
tags: [windowManager, layout, native, ipc, macos, multi-window]
|
|
4
|
-
lastTested: null
|
|
5
|
-
lastTestPassed: null
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Window Manager Layouts
|
|
9
|
-
|
|
10
|
-
Spawn and manage multiple windows at once using the layout API. Layouts let you declare groups of browser and terminal windows that open in parallel, or sequence multiple groups so each batch waits for the previous one to finish.
|
|
11
|
-
|
|
12
|
-
## Overview
|
|
13
|
-
|
|
14
|
-
The `windowManager` feature exposes two layout methods:
|
|
15
|
-
|
|
16
|
-
- **`spawnLayout(config)`** — spawns all entries in parallel, returns `WindowHandle[]`
|
|
17
|
-
- **`spawnLayouts(configs)`** — spawns multiple layouts sequentially (each layout's windows still spawn in parallel), returns `WindowHandle[][]`
|
|
18
|
-
|
|
19
|
-
Each entry in a layout is a `LayoutEntry` — either a browser window or a TTY window. Type detection is automatic: if the entry has a `command` field or `type: 'tty'`, it's a terminal. Otherwise it's a browser window.
|
|
20
|
-
|
|
21
|
-
## Setup
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
const wm = container.feature('windowManager', {
|
|
25
|
-
autoListen: true,
|
|
26
|
-
requestTimeoutMs: 10000
|
|
27
|
-
})
|
|
28
|
-
console.log('Window Manager ready')
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Single Layout — Parallel Windows
|
|
32
|
-
|
|
33
|
-
Spawn a mix of browser and terminal windows that all open at the same time.
|
|
34
|
-
|
|
35
|
-
```ts
|
|
36
|
-
const handles = await wm.spawnLayout([
|
|
37
|
-
{ url: 'https://github.com', width: '50%', height: '100%', x: 0, y: 0 },
|
|
38
|
-
{ url: 'https://soederpop.com', width: '50%', height: '100%', x: '50%', y: 0 },
|
|
39
|
-
{ command: 'top', title: 'System Monitor', width: 900, height: 400, x: 0, y: 720 },
|
|
40
|
-
])
|
|
41
|
-
|
|
42
|
-
console.log('Spawned', handles.length, 'windows')
|
|
43
|
-
handles.forEach((h, i) => console.log(` [${i}] windowId: ${h.windowId}`))
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
All three windows open simultaneously. The returned `handles` array preserves the same order as the config entries, so `handles[0]` corresponds to the GitHub window, `handles[1]` to HN, and `handles[2]` to htop.
|
|
47
|
-
|
|
48
|
-
## Percentage-Based Dimensions
|
|
49
|
-
|
|
50
|
-
Dimensions (`width`, `height`, `x`, `y`) accept percentage strings resolved against the primary display. This makes layouts portable across different screen resolutions.
|
|
51
|
-
|
|
52
|
-
```ts skip
|
|
53
|
-
// Side-by-side: two windows each taking half the screen
|
|
54
|
-
const handles = await wm.spawnLayout([
|
|
55
|
-
{ url: 'https://github.com', width: '50%', height: '100%', x: '0%', y: '0%' },
|
|
56
|
-
{ url: 'https://news.ycombinator.com', width: '50%', height: '100%', x: '50%', y: '0%' },
|
|
57
|
-
])
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
You can mix absolute and percentage values freely:
|
|
61
|
-
|
|
62
|
-
```ts skip
|
|
63
|
-
const handles = await wm.spawnLayout([
|
|
64
|
-
{ url: 'https://example.com', width: '75%', height: 600, x: '12.5%', y: 50 },
|
|
65
|
-
{ command: 'htop', width: '100%', height: '30%', x: '0%', y: '70%' },
|
|
66
|
-
])
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Explicit Type Field
|
|
70
|
-
|
|
71
|
-
You can be explicit about entry types using the `type` field. This is equivalent to the implicit detection but more readable when mixing window types.
|
|
72
|
-
|
|
73
|
-
```ts skip
|
|
74
|
-
const handles = await wm.spawnLayout([
|
|
75
|
-
{ type: 'window', url: 'https://example.com', width: 800, height: 600 },
|
|
76
|
-
{ type: 'tty', command: 'tail -f /var/log/system.log', title: 'Logs' },
|
|
77
|
-
])
|
|
78
|
-
|
|
79
|
-
console.log('Browser window:', handles[0].windowId)
|
|
80
|
-
console.log('TTY window:', handles[1].windowId)
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Sequential Layouts
|
|
84
|
-
|
|
85
|
-
Use `spawnLayouts()` when you need windows to appear in stages. Each layout batch spawns in parallel, but the next batch waits until the previous one is fully ready.
|
|
86
|
-
|
|
87
|
-
```ts skip
|
|
88
|
-
const [dashboards, tools] = await wm.spawnLayouts([
|
|
89
|
-
// First batch: main content
|
|
90
|
-
[
|
|
91
|
-
{ url: 'https://grafana.internal/d/api-latency', width: 960, height: 800, x: 0, y: 0 },
|
|
92
|
-
{ url: 'https://grafana.internal/d/error-rate', width: 960, height: 800, x: 970, y: 0 },
|
|
93
|
-
],
|
|
94
|
-
// Second batch: supporting tools (opens after dashboards are ready)
|
|
95
|
-
[
|
|
96
|
-
{ command: 'htop', title: 'CPU', width: 640, height: 400, x: 0, y: 820 },
|
|
97
|
-
{ command: 'tail -f /var/log/system.log', title: 'Logs', width: 640, height: 400, x: 650, y: 820 },
|
|
98
|
-
],
|
|
99
|
-
])
|
|
100
|
-
|
|
101
|
-
console.log('Dashboards:', dashboards.map(h => h.windowId))
|
|
102
|
-
console.log('Tools:', tools.map(h => h.windowId))
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
This is useful when the second batch depends on the first being visible — for example, positioning tool windows below dashboard windows.
|
|
106
|
-
|
|
107
|
-
## Lifecycle Events on Layout Handles
|
|
108
|
-
|
|
109
|
-
Every handle returned from a layout supports the same event API as a single `spawn()` call. You can listen for `close` and `terminalExited` events on each handle independently.
|
|
110
|
-
|
|
111
|
-
```ts skip
|
|
112
|
-
const handles = await wm.spawnLayout([
|
|
113
|
-
{ url: 'https://example.com', width: 800, height: 600 },
|
|
114
|
-
{ command: 'sleep 5 && echo done', title: 'Short Task' },
|
|
115
|
-
])
|
|
116
|
-
|
|
117
|
-
const [browser, terminal] = handles
|
|
118
|
-
|
|
119
|
-
browser.on('close', () => console.log('Browser window closed'))
|
|
120
|
-
|
|
121
|
-
terminal.on('terminalExited', (info) => {
|
|
122
|
-
console.log('Terminal process finished:', info)
|
|
123
|
-
})
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Window Chrome Options
|
|
127
|
-
|
|
128
|
-
Layout entries support all the same window chrome options as regular `spawn()` calls.
|
|
129
|
-
|
|
130
|
-
```ts skip
|
|
131
|
-
const handles = await wm.spawnLayout([
|
|
132
|
-
{
|
|
133
|
-
url: 'https://example.com',
|
|
134
|
-
width: 400,
|
|
135
|
-
height: 300,
|
|
136
|
-
alwaysOnTop: true,
|
|
137
|
-
window: {
|
|
138
|
-
decorations: 'hiddenTitleBar',
|
|
139
|
-
transparent: true,
|
|
140
|
-
shadow: true,
|
|
141
|
-
opacity: 0.9,
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
url: 'https://example.com/overlay',
|
|
146
|
-
width: 200,
|
|
147
|
-
height: 100,
|
|
148
|
-
window: {
|
|
149
|
-
decorations: 'none',
|
|
150
|
-
clickThrough: true,
|
|
151
|
-
transparent: true,
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
])
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## Operating on All Handles
|
|
158
|
-
|
|
159
|
-
Since layouts return arrays of `WindowHandle`, you can easily batch operations across all windows.
|
|
160
|
-
|
|
161
|
-
```ts skip
|
|
162
|
-
const handles = await wm.spawnLayout([
|
|
163
|
-
{ url: 'https://example.com/a', width: 600, height: 400 },
|
|
164
|
-
{ url: 'https://example.com/b', width: 600, height: 400 },
|
|
165
|
-
{ url: 'https://example.com/c', width: 600, height: 400 },
|
|
166
|
-
])
|
|
167
|
-
|
|
168
|
-
// Navigate all windows to the same URL
|
|
169
|
-
await Promise.all(handles.map(h => h.navigate('https://example.com/updated')))
|
|
170
|
-
|
|
171
|
-
// Screenshot all windows
|
|
172
|
-
await Promise.all(handles.map((h, i) => h.screengrab(`./layout-${i}.png`)))
|
|
173
|
-
|
|
174
|
-
// Close all windows
|
|
175
|
-
await Promise.all(handles.map(h => h.close()))
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
## Summary
|
|
179
|
-
|
|
180
|
-
The layout API builds on top of `spawn()` and `spawnTTY()` to orchestrate multi-window setups. Use `spawnLayout()` for a single batch of parallel windows, and `spawnLayouts()` when you need staged sequences. Every returned handle supports the full `WindowHandle` API — events, navigation, eval, screenshots, and more.
|