pilotswarm-cli 0.1.11 → 0.1.13

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/src/app.js ADDED
@@ -0,0 +1,595 @@
1
+ import React from "react";
2
+ import { useInput, useStdin } from "ink";
3
+ import { UiPlatformProvider, SharedPilotSwarmApp } from "pilotswarm-ui-react";
4
+ import { UI_COMMANDS } from "pilotswarm-ui-core";
5
+
6
+ const MOUSE_INPUT_PATTERN = /\u001b\[<(\d+);(\d+);(\d+)([mM])/gu;
7
+ const MOUSE_INPUT_FRAGMENT_PATTERN = /(?:\u001b)?\[<\d+;\d+;\d+[mM]/u;
8
+ const MOUSE_INPUT_CHUNK_PATTERN = /(?:\u001b?\[<|<)?\d+;\d+;\d+[mM]?/u;
9
+
10
+ export function isMouseInputSequence(input = "") {
11
+ const value = String(input || "");
12
+ return /^\u001b\[<\d+;\d+;\d+[mM]$/u.test(value)
13
+ || MOUSE_INPUT_FRAGMENT_PATTERN.test(value);
14
+ }
15
+
16
+ function looksLikeMouseInputFragment(input = "") {
17
+ const value = String(input || "");
18
+ if (!value) return false;
19
+ return value.includes("[<")
20
+ || value.startsWith("<")
21
+ || MOUSE_INPUT_CHUNK_PATTERN.test(value);
22
+ }
23
+
24
+ export function parseMouseInputSequences(input = "") {
25
+ const events = [];
26
+ MOUSE_INPUT_PATTERN.lastIndex = 0;
27
+ for (const match of String(input || "").matchAll(MOUSE_INPUT_PATTERN)) {
28
+ const code = Number(match[1]);
29
+ if (!Number.isFinite(code) || code >= 64) continue;
30
+ const buttonCode = code & 3;
31
+ const isRelease = match[4] === "m";
32
+ const isMotion = Boolean(code & 32);
33
+ const button = buttonCode === 0
34
+ ? "left"
35
+ : buttonCode === 1
36
+ ? "middle"
37
+ : buttonCode === 2
38
+ ? "right"
39
+ : "none";
40
+ events.push({
41
+ button,
42
+ type: isRelease ? "up" : (isMotion ? "drag" : "down"),
43
+ x: Math.max(0, Number(match[2]) - 1),
44
+ y: Math.max(0, Number(match[3]) - 1),
45
+ });
46
+ }
47
+ return events;
48
+ }
49
+
50
+ function formatCopyStatus(result) {
51
+ if (!result?.attempted) return null;
52
+ return result.copied ? "Copied to the clipboard" : "Clipboard copy failed";
53
+ }
54
+
55
+ export function getQuitPendingStatus(mode) {
56
+ return mode === "remote"
57
+ ? "Still quitting... disconnecting remote client"
58
+ : "Still quitting... stopping local workers";
59
+ }
60
+
61
+ export function isPlainShortcutKey(key = {}) {
62
+ return !key.ctrl && !key.meta;
63
+ }
64
+
65
+ export function PilotSwarmTuiApp({ controller, platform, onRequestExit }) {
66
+ const { stdin } = useStdin();
67
+ const mouseInputRef = React.useRef({ ignoreUntil: 0 });
68
+ const quitStateRef = React.useRef({
69
+ armedUntil: 0,
70
+ timer: null,
71
+ previousStatus: "",
72
+ });
73
+ const transientStatusRef = React.useRef({
74
+ timer: null,
75
+ previousStatus: "",
76
+ });
77
+ const exitingRef = React.useRef(false);
78
+ const [, forceRender] = React.useState(0);
79
+
80
+ const clearTransientStatus = React.useCallback((restoreStatus = false) => {
81
+ const transientStatus = transientStatusRef.current;
82
+ if (transientStatus.timer) {
83
+ clearTimeout(transientStatus.timer);
84
+ transientStatus.timer = null;
85
+ }
86
+ if (restoreStatus) {
87
+ controller.setStatus(transientStatus.previousStatus || "Connected");
88
+ }
89
+ }, [controller]);
90
+
91
+ const flashTransientStatus = React.useCallback((text, durationMs = 2400) => {
92
+ if (!text) return;
93
+ const transientStatus = transientStatusRef.current;
94
+ const hadActiveFlash = Boolean(transientStatus.timer);
95
+ const previousStatus = hadActiveFlash
96
+ ? transientStatus.previousStatus
97
+ : controller.getState().ui.statusText;
98
+ clearTransientStatus(false);
99
+ transientStatus.previousStatus = previousStatus;
100
+ controller.setStatus(text);
101
+ transientStatus.timer = setTimeout(() => {
102
+ transientStatus.timer = null;
103
+ if (controller.getState().ui.statusText === text) {
104
+ controller.setStatus(transientStatus.previousStatus || "Connected");
105
+ }
106
+ }, durationMs);
107
+ }, [clearTransientStatus, controller]);
108
+
109
+ const clearQuitArm = React.useCallback((restoreStatus = false) => {
110
+ const quitState = quitStateRef.current;
111
+ if (quitState.timer) {
112
+ clearTimeout(quitState.timer);
113
+ quitState.timer = null;
114
+ }
115
+ const wasArmed = quitState.armedUntil > Date.now();
116
+ quitState.armedUntil = 0;
117
+ if (restoreStatus && wasArmed) {
118
+ const currentStatus = controller.getState().ui.statusText;
119
+ if (currentStatus === "Press q to quit, or continue navigating") {
120
+ controller.setStatus(quitState.previousStatus || "Connected");
121
+ }
122
+ }
123
+ }, [controller]);
124
+
125
+ const armQuit = React.useCallback(() => {
126
+ clearQuitArm(false);
127
+ const quitState = quitStateRef.current;
128
+ quitState.previousStatus = controller.getState().ui.statusText;
129
+ quitState.armedUntil = Date.now() + 1500;
130
+ controller.setStatus("Press q to quit, or continue navigating");
131
+ quitState.timer = setTimeout(() => {
132
+ clearQuitArm(true);
133
+ }, 1500);
134
+ }, [clearQuitArm, controller]);
135
+
136
+ const requestExit = React.useCallback(() => {
137
+ if (exitingRef.current) return;
138
+ exitingRef.current = true;
139
+ clearQuitArm(false);
140
+ controller.setStatus("Quitting...");
141
+
142
+ const slowTimer = setTimeout(() => {
143
+ controller.setStatus(getQuitPendingStatus(controller.getState().connection.mode));
144
+ }, 300);
145
+
146
+ Promise.resolve(onRequestExit?.())
147
+ .catch(() => {})
148
+ .finally(() => {
149
+ clearTimeout(slowTimer);
150
+ });
151
+ }, [clearQuitArm, controller, onRequestExit]);
152
+
153
+ React.useEffect(() => {
154
+ let mounted = true;
155
+ controller.start().catch((error) => {
156
+ if (mounted) {
157
+ const message = error?.message || String(error);
158
+ controller.dispatch({
159
+ type: "connection/error",
160
+ error: message,
161
+ statusText: `Startup failed: ${message}`,
162
+ });
163
+ }
164
+ });
165
+ return () => {
166
+ mounted = false;
167
+ if (exitingRef.current) return;
168
+ clearQuitArm(false);
169
+ clearTransientStatus(false);
170
+ controller.stop().catch(() => {});
171
+ };
172
+ }, [clearQuitArm, clearTransientStatus, controller]);
173
+
174
+ React.useEffect(() => {
175
+ if (typeof platform.setRenderInvalidator !== "function") return undefined;
176
+ platform.setRenderInvalidator(() => {
177
+ forceRender((value) => value + 1);
178
+ });
179
+ return () => {
180
+ platform.setRenderInvalidator(null);
181
+ };
182
+ }, [platform]);
183
+
184
+ React.useEffect(() => {
185
+ if (!stdin || !process.stdout?.isTTY) return undefined;
186
+ const enableMouse = "\u001b[?1000h\u001b[?1002h\u001b[?1006h";
187
+ const disableMouse = "\u001b[?1000l\u001b[?1002l\u001b[?1006l";
188
+
189
+ try {
190
+ process.stdout.write(enableMouse);
191
+ } catch {}
192
+
193
+ const onData = (chunk) => {
194
+ const events = parseMouseInputSequences(chunk?.toString?.("utf8") || "");
195
+ if (events.length === 0) return;
196
+ mouseInputRef.current.ignoreUntil = Date.now() + 120;
197
+ if (controller.getState().ui.modal) {
198
+ platform.clearPointerSelection?.();
199
+ return;
200
+ }
201
+ for (const event of events) {
202
+ if (event.type === "down" && event.button === "left") {
203
+ platform.beginPointerSelection?.(event.x, event.y);
204
+ continue;
205
+ }
206
+ if (event.type === "drag") {
207
+ platform.updatePointerSelection?.(event.x, event.y);
208
+ continue;
209
+ }
210
+ if (event.type === "up") {
211
+ const result = platform.finalizePointerSelection?.() || null;
212
+ const status = formatCopyStatus(result);
213
+ if (status) flashTransientStatus(status);
214
+ }
215
+ }
216
+ };
217
+
218
+ stdin.on("data", onData);
219
+ return () => {
220
+ try {
221
+ stdin.off("data", onData);
222
+ } catch {}
223
+ platform.clearPointerSelection?.();
224
+ try {
225
+ process.stdout.write(disableMouse);
226
+ } catch {}
227
+ };
228
+ }, [controller, flashTransientStatus, platform, stdin]);
229
+
230
+ useInput((input, key) => {
231
+ const focus = controller.getState().ui.focusRegion;
232
+ const modal = controller.getState().ui.modal;
233
+ const inspectorTab = controller.getState().ui.inspectorTab;
234
+ const plainShortcut = isPlainShortcutKey(key);
235
+ const matchesCtrlKey = (name, controlChar) => key.ctrl
236
+ && (key.name === name || input === name || input === controlChar);
237
+ const isCtrlU = matchesCtrlKey("u", "\u0015");
238
+ const isCtrlD = matchesCtrlKey("d", "\u0004");
239
+ const isCtrlE = matchesCtrlKey("e", "\u0005");
240
+ const isCtrlJ = matchesCtrlKey("j", "\n");
241
+ const isCtrlA = matchesCtrlKey("a", "\u0001");
242
+ const isShiftN = input === "N" || (key.shift && key.name === "n");
243
+ const isShiftD = input === "D" || (key.shift && key.name === "d");
244
+ const isAltBackspace = key.meta && (key.backspace || key.delete || key.name === "backspace" || key.name === "delete");
245
+ const isAltLeftWord = key.meta && (key.leftArrow || key.name === "left" || input === "b" || input === "B");
246
+ const isAltRightWord = key.meta && (key.rightArrow || key.name === "right" || input === "f" || input === "F");
247
+
248
+ if (
249
+ isMouseInputSequence(input)
250
+ || (
251
+ mouseInputRef.current.ignoreUntil > Date.now()
252
+ && looksLikeMouseInputFragment(input)
253
+ )
254
+ ) {
255
+ return;
256
+ }
257
+
258
+ if (key.ctrl && input === "c") {
259
+ requestExit();
260
+ return;
261
+ }
262
+ if (focus !== "prompt" && input === "q" && quitStateRef.current.armedUntil > Date.now()) {
263
+ clearQuitArm(false);
264
+ requestExit();
265
+ return;
266
+ }
267
+ if (focus !== "prompt" && input === "q") {
268
+ clearQuitArm(false);
269
+ requestExit();
270
+ return;
271
+ }
272
+
273
+ if (key.name !== "escape") {
274
+ clearQuitArm(true);
275
+ }
276
+
277
+ if (modal) {
278
+ if (modal.type === "renameSession" || modal.type === "artifactUpload") {
279
+ if (key.escape) {
280
+ controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
281
+ return;
282
+ }
283
+ if (key.return) {
284
+ controller.handleCommand(UI_COMMANDS.MODAL_CONFIRM).catch(() => {});
285
+ return;
286
+ }
287
+ if (key.leftArrow) {
288
+ if (modal.type === "renameSession") controller.moveRenameSessionCursor(-1);
289
+ else controller.moveArtifactUploadCursor(-1);
290
+ return;
291
+ }
292
+ if (key.rightArrow) {
293
+ if (modal.type === "renameSession") controller.moveRenameSessionCursor(1);
294
+ else controller.moveArtifactUploadCursor(1);
295
+ return;
296
+ }
297
+ if (key.home) {
298
+ if (modal.type === "renameSession") controller.moveRenameSessionCursorToBoundary("start");
299
+ else controller.moveArtifactUploadCursorToBoundary("start");
300
+ return;
301
+ }
302
+ if (key.end) {
303
+ if (modal.type === "renameSession") controller.moveRenameSessionCursorToBoundary("end");
304
+ else controller.moveArtifactUploadCursorToBoundary("end");
305
+ return;
306
+ }
307
+ if (key.backspace || key.delete) {
308
+ if (modal.type === "renameSession") controller.deleteRenameSessionChar();
309
+ else controller.deleteArtifactUploadChar();
310
+ return;
311
+ }
312
+ if (!key.ctrl && !key.meta && input) {
313
+ if (modal.type === "renameSession") controller.insertRenameSessionText(input);
314
+ else controller.insertArtifactUploadText(input);
315
+ }
316
+ return;
317
+ }
318
+ if (key.escape || input === "q" || (modal.type === "artifactPicker" && input === "a")) {
319
+ controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
320
+ return;
321
+ }
322
+ if (key.tab && key.shift) {
323
+ controller.handleCommand(UI_COMMANDS.MODAL_PANE_PREV).catch(() => {});
324
+ return;
325
+ }
326
+ if (key.tab) {
327
+ controller.handleCommand(UI_COMMANDS.MODAL_PANE_NEXT).catch(() => {});
328
+ return;
329
+ }
330
+ if (key.return) {
331
+ controller.handleCommand(UI_COMMANDS.MODAL_CONFIRM).catch(() => {});
332
+ return;
333
+ }
334
+ if (key.upArrow || input === "k") {
335
+ controller.handleCommand(UI_COMMANDS.MODAL_PREV).catch(() => {});
336
+ return;
337
+ }
338
+ if (key.downArrow || input === "j") {
339
+ controller.handleCommand(UI_COMMANDS.MODAL_NEXT).catch(() => {});
340
+ return;
341
+ }
342
+ return;
343
+ }
344
+
345
+ if (key.tab && key.shift) {
346
+ controller.handleCommand(UI_COMMANDS.FOCUS_PREV).catch(() => {});
347
+ return;
348
+ }
349
+ if (key.tab) {
350
+ controller.handleCommand(UI_COMMANDS.FOCUS_NEXT).catch(() => {});
351
+ return;
352
+ }
353
+ if (key.escape && focus === "inspector" && inspectorTab === "files" && controller.getState().files.fullscreen) {
354
+ controller.handleCommand(UI_COMMANDS.TOGGLE_FILE_PREVIEW_FULLSCREEN).catch(() => {});
355
+ return;
356
+ }
357
+ if (key.escape) {
358
+ if (focus === "prompt") {
359
+ controller.setPrompt("");
360
+ controller.handleCommand(UI_COMMANDS.FOCUS_SESSIONS).catch(() => {});
361
+ return;
362
+ }
363
+ controller.handleCommand(UI_COMMANDS.FOCUS_SESSIONS).catch(() => {});
364
+ armQuit();
365
+ return;
366
+ }
367
+
368
+ if (focus !== "prompt" && isShiftN) {
369
+ controller.handleCommand(UI_COMMANDS.OPEN_MODEL_PICKER).catch(() => {});
370
+ return;
371
+ }
372
+
373
+ if (focus === "chat" && (input === "e" || isCtrlE)) {
374
+ controller.handleCommand(UI_COMMANDS.EXPAND_HISTORY).catch(() => {});
375
+ return;
376
+ }
377
+
378
+ if (focus === "inspector" && inspectorTab === "logs" && input === "t") {
379
+ controller.handleCommand(UI_COMMANDS.TOGGLE_LOG_TAIL).catch(() => {});
380
+ return;
381
+ }
382
+ if (focus === "inspector" && inspectorTab === "logs" && input === "f") {
383
+ controller.handleCommand(UI_COMMANDS.OPEN_LOG_FILTER).catch(() => {});
384
+ return;
385
+ }
386
+ if (focus === "inspector" && inspectorTab === "files" && input === "f") {
387
+ controller.handleCommand(UI_COMMANDS.OPEN_FILES_FILTER).catch(() => {});
388
+ return;
389
+ }
390
+ if (focus === "inspector" && inspectorTab === "files" && input === "v") {
391
+ controller.handleCommand(UI_COMMANDS.TOGGLE_FILE_PREVIEW_FULLSCREEN).catch(() => {});
392
+ return;
393
+ }
394
+ if (focus === "inspector" && inspectorTab === "files" && plainShortcut && input === "o") {
395
+ controller.handleCommand(UI_COMMANDS.OPEN_SELECTED_FILE).catch(() => {});
396
+ return;
397
+ }
398
+ if (focus === "inspector" && inspectorTab === "files" && !controller.getState().files.fullscreen) {
399
+ if (key.upArrow || input === "k") {
400
+ controller.handleCommand(UI_COMMANDS.MOVE_FILE_UP).catch(() => {});
401
+ return;
402
+ }
403
+ if (key.downArrow || input === "j") {
404
+ controller.handleCommand(UI_COMMANDS.MOVE_FILE_DOWN).catch(() => {});
405
+ return;
406
+ }
407
+ }
408
+
409
+ if (focus !== "prompt" && input === "[") {
410
+ controller.handleCommand(UI_COMMANDS.GROW_LEFT_PANE).catch(() => {});
411
+ return;
412
+ }
413
+ if (focus !== "prompt" && input === "]") {
414
+ controller.handleCommand(UI_COMMANDS.GROW_RIGHT_PANE).catch(() => {});
415
+ return;
416
+ }
417
+
418
+ if (focus === "prompt") {
419
+ if (isCtrlA) {
420
+ controller.handleCommand(UI_COMMANDS.OPEN_ARTIFACT_UPLOAD).catch(() => {});
421
+ return;
422
+ }
423
+ if (key.return && key.meta) {
424
+ controller.insertPromptText("\n");
425
+ return;
426
+ }
427
+ if (key.return) {
428
+ controller.handleCommand(UI_COMMANDS.SEND_PROMPT).catch(() => {});
429
+ return;
430
+ }
431
+ if (isAltLeftWord) {
432
+ controller.movePromptCursorWord(-1);
433
+ return;
434
+ }
435
+ if (isAltRightWord) {
436
+ controller.movePromptCursorWord(1);
437
+ return;
438
+ }
439
+ if (key.leftArrow) {
440
+ controller.movePromptCursor(-1);
441
+ return;
442
+ }
443
+ if (key.rightArrow) {
444
+ controller.movePromptCursor(1);
445
+ return;
446
+ }
447
+ if (key.upArrow) {
448
+ controller.movePromptCursorVertical(-1);
449
+ return;
450
+ }
451
+ if (key.downArrow) {
452
+ controller.movePromptCursorVertical(1);
453
+ return;
454
+ }
455
+ if (isAltBackspace) {
456
+ controller.deletePromptWordBackward();
457
+ return;
458
+ }
459
+ if (key.backspace || key.delete) {
460
+ controller.deletePromptChar();
461
+ return;
462
+ }
463
+ if (isCtrlJ) {
464
+ controller.insertPromptText("\n");
465
+ return;
466
+ }
467
+ if (!key.ctrl && !key.meta && input) {
468
+ controller.insertPromptText(input);
469
+ }
470
+ return;
471
+ }
472
+
473
+ if (plainShortcut && input === "p") {
474
+ controller.handleCommand(UI_COMMANDS.FOCUS_PROMPT).catch(() => {});
475
+ return;
476
+ }
477
+ if (plainShortcut && input === "n") {
478
+ controller.handleCommand(UI_COMMANDS.NEW_SESSION).catch(() => {});
479
+ return;
480
+ }
481
+ if (plainShortcut && input === "r") {
482
+ controller.handleCommand(UI_COMMANDS.REFRESH).catch(() => {});
483
+ return;
484
+ }
485
+ if (plainShortcut && input === "a") {
486
+ controller.handleCommand(UI_COMMANDS.OPEN_ARTIFACT_PICKER).catch(() => {});
487
+ return;
488
+ }
489
+ if (plainShortcut && input === "m") {
490
+ controller.handleCommand(UI_COMMANDS.CYCLE_INSPECTOR_TAB).catch(() => {});
491
+ return;
492
+ }
493
+ if (plainShortcut && input === "c") {
494
+ controller.handleCommand(UI_COMMANDS.CANCEL_SESSION).catch(() => {});
495
+ return;
496
+ }
497
+ if (plainShortcut && input === "d") {
498
+ controller.handleCommand(UI_COMMANDS.DONE_SESSION).catch(() => {});
499
+ return;
500
+ }
501
+ if (plainShortcut && isShiftD) {
502
+ controller.handleCommand(UI_COMMANDS.DELETE_SESSION).catch(() => {});
503
+ return;
504
+ }
505
+ if (focus !== "prompt" && (input === "h" || key.leftArrow)) {
506
+ if (focus === "inspector" && key.leftArrow) {
507
+ controller.handleCommand(UI_COMMANDS.PREV_INSPECTOR_TAB).catch(() => {});
508
+ return;
509
+ }
510
+ controller.handleCommand(UI_COMMANDS.FOCUS_LEFT).catch(() => {});
511
+ return;
512
+ }
513
+ if (focus !== "prompt" && (input === "l" || key.rightArrow)) {
514
+ if (focus === "inspector" && key.rightArrow) {
515
+ controller.handleCommand(UI_COMMANDS.NEXT_INSPECTOR_TAB).catch(() => {});
516
+ return;
517
+ }
518
+ controller.handleCommand(UI_COMMANDS.FOCUS_RIGHT).catch(() => {});
519
+ return;
520
+ }
521
+
522
+ if (focus === "sessions") {
523
+ if (key.upArrow || input === "k") {
524
+ controller.handleCommand(UI_COMMANDS.MOVE_SESSION_UP).catch(() => {});
525
+ return;
526
+ }
527
+ if (key.downArrow || input === "j") {
528
+ controller.handleCommand(UI_COMMANDS.MOVE_SESSION_DOWN).catch(() => {});
529
+ return;
530
+ }
531
+ if (isCtrlU || key.pageUp) {
532
+ controller.handleCommand(UI_COMMANDS.PAGE_UP).catch(() => {});
533
+ return;
534
+ }
535
+ if (isCtrlD || key.pageDown) {
536
+ controller.handleCommand(UI_COMMANDS.PAGE_DOWN).catch(() => {});
537
+ return;
538
+ }
539
+ if (input === "+" || input === "=") {
540
+ controller.handleCommand(UI_COMMANDS.EXPAND_SESSION).catch(() => {});
541
+ return;
542
+ }
543
+ if (input === "-") {
544
+ controller.handleCommand(UI_COMMANDS.COLLAPSE_SESSION).catch(() => {});
545
+ return;
546
+ }
547
+ if (plainShortcut && input === "t") {
548
+ controller.handleCommand(UI_COMMANDS.OPEN_RENAME_SESSION).catch(() => {});
549
+ return;
550
+ }
551
+ }
552
+
553
+ if (focus === "chat" || focus === "inspector" || focus === "activity") {
554
+ if (key.upArrow || input === "k") {
555
+ controller.handleCommand(UI_COMMANDS.SCROLL_UP).catch(() => {});
556
+ return;
557
+ }
558
+ if (key.downArrow || input === "j") {
559
+ controller.handleCommand(UI_COMMANDS.SCROLL_DOWN).catch(() => {});
560
+ return;
561
+ }
562
+ if (isCtrlU || key.pageUp) {
563
+ controller.handleCommand(UI_COMMANDS.PAGE_UP).catch(() => {});
564
+ return;
565
+ }
566
+ if (isCtrlD || key.pageDown) {
567
+ controller.handleCommand(UI_COMMANDS.PAGE_DOWN).catch(() => {});
568
+ return;
569
+ }
570
+ if (input === "g") {
571
+ controller.handleCommand(UI_COMMANDS.SCROLL_TOP).catch(() => {});
572
+ return;
573
+ }
574
+ if (input === "G" || (key.shift && input === "g")) {
575
+ controller.handleCommand(UI_COMMANDS.SCROLL_BOTTOM).catch(() => {});
576
+ return;
577
+ }
578
+ }
579
+
580
+ if (focus === "inspector") {
581
+ if (key.leftArrow) {
582
+ controller.handleCommand(UI_COMMANDS.PREV_INSPECTOR_TAB).catch(() => {});
583
+ return;
584
+ }
585
+ if (key.rightArrow) {
586
+ controller.handleCommand(UI_COMMANDS.NEXT_INSPECTOR_TAB).catch(() => {});
587
+ return;
588
+ }
589
+ }
590
+
591
+ });
592
+
593
+ return React.createElement(UiPlatformProvider, { platform },
594
+ React.createElement(SharedPilotSwarmApp, { controller }));
595
+ }