pilotswarm-cli 0.1.12 → 0.1.14

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,617 @@
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 isShiftT = !key.ctrl && !key.meta && !key.alt && (input === "T" || (key.shift && key.name === "t"));
245
+ const isAltBackspace = key.meta && (key.backspace || key.delete || key.name === "backspace" || key.name === "delete");
246
+ const isAltLeftWord = key.meta && (key.leftArrow || key.name === "left" || input === "b" || input === "B");
247
+ const isAltRightWord = key.meta && (key.rightArrow || key.name === "right" || input === "f" || input === "F");
248
+
249
+ if (
250
+ isMouseInputSequence(input)
251
+ || (
252
+ mouseInputRef.current.ignoreUntil > Date.now()
253
+ && looksLikeMouseInputFragment(input)
254
+ )
255
+ ) {
256
+ return;
257
+ }
258
+
259
+ if (key.ctrl && input === "c") {
260
+ requestExit();
261
+ return;
262
+ }
263
+ if (focus !== "prompt" && input === "q" && quitStateRef.current.armedUntil > Date.now()) {
264
+ clearQuitArm(false);
265
+ requestExit();
266
+ return;
267
+ }
268
+ if (focus !== "prompt" && input === "q") {
269
+ clearQuitArm(false);
270
+ requestExit();
271
+ return;
272
+ }
273
+
274
+ if (key.name !== "escape") {
275
+ clearQuitArm(true);
276
+ }
277
+
278
+ if (modal) {
279
+ if (isShiftT && modal.type === "themePicker") {
280
+ controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
281
+ return;
282
+ }
283
+ if (modal.type === "renameSession" || modal.type === "artifactUpload") {
284
+ if (key.escape) {
285
+ controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
286
+ return;
287
+ }
288
+ if (key.return) {
289
+ controller.handleCommand(UI_COMMANDS.MODAL_CONFIRM).catch(() => {});
290
+ return;
291
+ }
292
+ if (key.leftArrow) {
293
+ if (modal.type === "renameSession") controller.moveRenameSessionCursor(-1);
294
+ else controller.moveArtifactUploadCursor(-1);
295
+ return;
296
+ }
297
+ if (key.rightArrow) {
298
+ if (modal.type === "renameSession") controller.moveRenameSessionCursor(1);
299
+ else controller.moveArtifactUploadCursor(1);
300
+ return;
301
+ }
302
+ if (key.home) {
303
+ if (modal.type === "renameSession") controller.moveRenameSessionCursorToBoundary("start");
304
+ else controller.moveArtifactUploadCursorToBoundary("start");
305
+ return;
306
+ }
307
+ if (key.end) {
308
+ if (modal.type === "renameSession") controller.moveRenameSessionCursorToBoundary("end");
309
+ else controller.moveArtifactUploadCursorToBoundary("end");
310
+ return;
311
+ }
312
+ if (key.backspace || key.delete) {
313
+ if (modal.type === "renameSession") controller.deleteRenameSessionChar();
314
+ else controller.deleteArtifactUploadChar();
315
+ return;
316
+ }
317
+ if (!key.ctrl && !key.meta && input) {
318
+ if (modal.type === "renameSession") controller.insertRenameSessionText(input);
319
+ else controller.insertArtifactUploadText(input);
320
+ }
321
+ return;
322
+ }
323
+ if (key.escape || input === "q" || (modal.type === "artifactPicker" && input === "a")) {
324
+ controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
325
+ return;
326
+ }
327
+ if (key.tab && key.shift) {
328
+ controller.handleCommand(UI_COMMANDS.MODAL_PANE_PREV).catch(() => {});
329
+ return;
330
+ }
331
+ if (key.tab) {
332
+ controller.handleCommand(UI_COMMANDS.MODAL_PANE_NEXT).catch(() => {});
333
+ return;
334
+ }
335
+ if (key.return) {
336
+ controller.handleCommand(UI_COMMANDS.MODAL_CONFIRM).catch(() => {});
337
+ return;
338
+ }
339
+ if (key.upArrow || input === "k") {
340
+ controller.handleCommand(UI_COMMANDS.MODAL_PREV).catch(() => {});
341
+ return;
342
+ }
343
+ if (key.downArrow || input === "j") {
344
+ controller.handleCommand(UI_COMMANDS.MODAL_NEXT).catch(() => {});
345
+ return;
346
+ }
347
+ return;
348
+ }
349
+
350
+ if (focus !== "prompt" && isShiftT) {
351
+ controller.handleCommand(UI_COMMANDS.OPEN_THEME_PICKER).catch(() => {});
352
+ return;
353
+ }
354
+
355
+ if (key.tab && key.shift) {
356
+ controller.handleCommand(UI_COMMANDS.FOCUS_PREV).catch(() => {});
357
+ return;
358
+ }
359
+ if (key.tab) {
360
+ controller.handleCommand(UI_COMMANDS.FOCUS_NEXT).catch(() => {});
361
+ return;
362
+ }
363
+ if (key.escape && focus === "inspector" && inspectorTab === "files" && controller.getState().files.fullscreen) {
364
+ controller.handleCommand(UI_COMMANDS.TOGGLE_FILE_PREVIEW_FULLSCREEN).catch(() => {});
365
+ return;
366
+ }
367
+ if (key.escape) {
368
+ if (focus === "prompt") {
369
+ controller.setPrompt("");
370
+ controller.handleCommand(UI_COMMANDS.FOCUS_SESSIONS).catch(() => {});
371
+ return;
372
+ }
373
+ controller.handleCommand(UI_COMMANDS.FOCUS_SESSIONS).catch(() => {});
374
+ armQuit();
375
+ return;
376
+ }
377
+
378
+ if (focus !== "prompt" && isShiftN) {
379
+ controller.handleCommand(UI_COMMANDS.OPEN_MODEL_PICKER).catch(() => {});
380
+ return;
381
+ }
382
+
383
+ if (focus === "chat" && (input === "e" || isCtrlE)) {
384
+ controller.handleCommand(UI_COMMANDS.EXPAND_HISTORY).catch(() => {});
385
+ return;
386
+ }
387
+
388
+ if (focus === "inspector" && inspectorTab === "logs" && input === "t") {
389
+ controller.handleCommand(UI_COMMANDS.TOGGLE_LOG_TAIL).catch(() => {});
390
+ return;
391
+ }
392
+ if (focus === "inspector" && inspectorTab === "logs" && input === "f") {
393
+ controller.handleCommand(UI_COMMANDS.OPEN_LOG_FILTER).catch(() => {});
394
+ return;
395
+ }
396
+ if (focus === "inspector" && inspectorTab === "files" && input === "f") {
397
+ controller.handleCommand(UI_COMMANDS.OPEN_FILES_FILTER).catch(() => {});
398
+ return;
399
+ }
400
+ if (focus === "inspector" && inspectorTab === "history" && input === "f") {
401
+ controller.handleCommand(UI_COMMANDS.OPEN_HISTORY_FORMAT).catch(() => {});
402
+ return;
403
+ }
404
+ if (focus === "inspector" && inspectorTab === "history" && input === "r") {
405
+ controller.handleCommand(UI_COMMANDS.REFRESH_EXECUTION_HISTORY).catch(() => {});
406
+ return;
407
+ }
408
+ if (focus === "inspector" && inspectorTab === "history" && input === "a") {
409
+ controller.handleCommand(UI_COMMANDS.EXPORT_EXECUTION_HISTORY).catch(() => {});
410
+ return;
411
+ }
412
+ if (focus === "inspector" && inspectorTab === "files" && input === "v") {
413
+ controller.handleCommand(UI_COMMANDS.TOGGLE_FILE_PREVIEW_FULLSCREEN).catch(() => {});
414
+ return;
415
+ }
416
+ if (focus === "inspector" && inspectorTab === "files" && plainShortcut && input === "o") {
417
+ controller.handleCommand(UI_COMMANDS.OPEN_SELECTED_FILE).catch(() => {});
418
+ return;
419
+ }
420
+ if (focus === "inspector" && inspectorTab === "files" && !controller.getState().files.fullscreen) {
421
+ if (key.upArrow || input === "k") {
422
+ controller.handleCommand(UI_COMMANDS.MOVE_FILE_UP).catch(() => {});
423
+ return;
424
+ }
425
+ if (key.downArrow || input === "j") {
426
+ controller.handleCommand(UI_COMMANDS.MOVE_FILE_DOWN).catch(() => {});
427
+ return;
428
+ }
429
+ }
430
+
431
+ if (focus !== "prompt" && input === "[") {
432
+ controller.handleCommand(UI_COMMANDS.GROW_LEFT_PANE).catch(() => {});
433
+ return;
434
+ }
435
+ if (focus !== "prompt" && input === "]") {
436
+ controller.handleCommand(UI_COMMANDS.GROW_RIGHT_PANE).catch(() => {});
437
+ return;
438
+ }
439
+
440
+ if (focus === "prompt") {
441
+ if (isCtrlA) {
442
+ controller.handleCommand(UI_COMMANDS.OPEN_ARTIFACT_UPLOAD).catch(() => {});
443
+ return;
444
+ }
445
+ if (key.return && key.meta) {
446
+ controller.insertPromptText("\n");
447
+ return;
448
+ }
449
+ if (key.return) {
450
+ controller.handleCommand(UI_COMMANDS.SEND_PROMPT).catch(() => {});
451
+ return;
452
+ }
453
+ if (isAltLeftWord) {
454
+ controller.movePromptCursorWord(-1);
455
+ return;
456
+ }
457
+ if (isAltRightWord) {
458
+ controller.movePromptCursorWord(1);
459
+ return;
460
+ }
461
+ if (key.leftArrow) {
462
+ controller.movePromptCursor(-1);
463
+ return;
464
+ }
465
+ if (key.rightArrow) {
466
+ controller.movePromptCursor(1);
467
+ return;
468
+ }
469
+ if (key.upArrow) {
470
+ controller.movePromptCursorVertical(-1);
471
+ return;
472
+ }
473
+ if (key.downArrow) {
474
+ controller.movePromptCursorVertical(1);
475
+ return;
476
+ }
477
+ if (isAltBackspace) {
478
+ controller.deletePromptWordBackward();
479
+ return;
480
+ }
481
+ if (key.backspace || key.delete) {
482
+ controller.deletePromptChar();
483
+ return;
484
+ }
485
+ if (isCtrlJ) {
486
+ controller.insertPromptText("\n");
487
+ return;
488
+ }
489
+ if (!key.ctrl && !key.meta && input) {
490
+ controller.insertPromptText(input);
491
+ }
492
+ return;
493
+ }
494
+
495
+ if (plainShortcut && input === "p") {
496
+ controller.handleCommand(UI_COMMANDS.FOCUS_PROMPT).catch(() => {});
497
+ return;
498
+ }
499
+ if (plainShortcut && input === "n") {
500
+ controller.handleCommand(UI_COMMANDS.NEW_SESSION).catch(() => {});
501
+ return;
502
+ }
503
+ if (plainShortcut && input === "r") {
504
+ controller.handleCommand(UI_COMMANDS.REFRESH).catch(() => {});
505
+ return;
506
+ }
507
+ if (plainShortcut && input === "a") {
508
+ controller.handleCommand(UI_COMMANDS.OPEN_ARTIFACT_PICKER).catch(() => {});
509
+ return;
510
+ }
511
+ if (plainShortcut && input === "m") {
512
+ controller.handleCommand(UI_COMMANDS.CYCLE_INSPECTOR_TAB).catch(() => {});
513
+ return;
514
+ }
515
+ if (plainShortcut && input === "c") {
516
+ controller.handleCommand(UI_COMMANDS.CANCEL_SESSION).catch(() => {});
517
+ return;
518
+ }
519
+ if (plainShortcut && input === "d") {
520
+ controller.handleCommand(UI_COMMANDS.DONE_SESSION).catch(() => {});
521
+ return;
522
+ }
523
+ if (plainShortcut && isShiftD) {
524
+ controller.handleCommand(UI_COMMANDS.DELETE_SESSION).catch(() => {});
525
+ return;
526
+ }
527
+ if (focus !== "prompt" && (input === "h" || key.leftArrow)) {
528
+ if (focus === "inspector" && key.leftArrow) {
529
+ controller.handleCommand(UI_COMMANDS.PREV_INSPECTOR_TAB).catch(() => {});
530
+ return;
531
+ }
532
+ controller.handleCommand(UI_COMMANDS.FOCUS_LEFT).catch(() => {});
533
+ return;
534
+ }
535
+ if (focus !== "prompt" && (input === "l" || key.rightArrow)) {
536
+ if (focus === "inspector" && key.rightArrow) {
537
+ controller.handleCommand(UI_COMMANDS.NEXT_INSPECTOR_TAB).catch(() => {});
538
+ return;
539
+ }
540
+ controller.handleCommand(UI_COMMANDS.FOCUS_RIGHT).catch(() => {});
541
+ return;
542
+ }
543
+
544
+ if (focus === "sessions") {
545
+ if (key.upArrow || input === "k") {
546
+ controller.handleCommand(UI_COMMANDS.MOVE_SESSION_UP).catch(() => {});
547
+ return;
548
+ }
549
+ if (key.downArrow || input === "j") {
550
+ controller.handleCommand(UI_COMMANDS.MOVE_SESSION_DOWN).catch(() => {});
551
+ return;
552
+ }
553
+ if (isCtrlU || key.pageUp) {
554
+ controller.handleCommand(UI_COMMANDS.PAGE_UP).catch(() => {});
555
+ return;
556
+ }
557
+ if (isCtrlD || key.pageDown) {
558
+ controller.handleCommand(UI_COMMANDS.PAGE_DOWN).catch(() => {});
559
+ return;
560
+ }
561
+ if (input === "+" || input === "=") {
562
+ controller.handleCommand(UI_COMMANDS.EXPAND_SESSION).catch(() => {});
563
+ return;
564
+ }
565
+ if (input === "-") {
566
+ controller.handleCommand(UI_COMMANDS.COLLAPSE_SESSION).catch(() => {});
567
+ return;
568
+ }
569
+ if (plainShortcut && input === "t") {
570
+ controller.handleCommand(UI_COMMANDS.OPEN_RENAME_SESSION).catch(() => {});
571
+ return;
572
+ }
573
+ }
574
+
575
+ if (focus === "chat" || focus === "inspector" || focus === "activity") {
576
+ if (key.upArrow || input === "k") {
577
+ controller.handleCommand(UI_COMMANDS.SCROLL_UP).catch(() => {});
578
+ return;
579
+ }
580
+ if (key.downArrow || input === "j") {
581
+ controller.handleCommand(UI_COMMANDS.SCROLL_DOWN).catch(() => {});
582
+ return;
583
+ }
584
+ if (isCtrlU || key.pageUp) {
585
+ controller.handleCommand(UI_COMMANDS.PAGE_UP).catch(() => {});
586
+ return;
587
+ }
588
+ if (isCtrlD || key.pageDown) {
589
+ controller.handleCommand(UI_COMMANDS.PAGE_DOWN).catch(() => {});
590
+ return;
591
+ }
592
+ if (input === "g") {
593
+ controller.handleCommand(UI_COMMANDS.SCROLL_TOP).catch(() => {});
594
+ return;
595
+ }
596
+ if (input === "G" || (key.shift && input === "g")) {
597
+ controller.handleCommand(UI_COMMANDS.SCROLL_BOTTOM).catch(() => {});
598
+ return;
599
+ }
600
+ }
601
+
602
+ if (focus === "inspector") {
603
+ if (key.leftArrow) {
604
+ controller.handleCommand(UI_COMMANDS.PREV_INSPECTOR_TAB).catch(() => {});
605
+ return;
606
+ }
607
+ if (key.rightArrow) {
608
+ controller.handleCommand(UI_COMMANDS.NEXT_INSPECTOR_TAB).catch(() => {});
609
+ return;
610
+ }
611
+ }
612
+
613
+ });
614
+
615
+ return React.createElement(UiPlatformProvider, { platform },
616
+ React.createElement(SharedPilotSwarmApp, { controller }));
617
+ }