@opencode-cockpit/shell 0.1.5 → 0.2.1

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.
Files changed (64) hide show
  1. package/README.md +105 -10
  2. package/dist/agent/plugin.js +180 -0
  3. package/dist/agent/tools/index.js +23 -0
  4. package/dist/agent/tools/list.js +68 -0
  5. package/dist/agent/tools/read.js +59 -0
  6. package/dist/agent/tools/restart.js +44 -0
  7. package/dist/agent/tools/send.js +71 -0
  8. package/dist/agent/tools/shared.js +94 -0
  9. package/dist/agent/tools/start.js +152 -0
  10. package/dist/agent/tools/stop.js +42 -0
  11. package/dist/agent/tools/wait.js +79 -0
  12. package/dist/agent/tools/watch-args.js +60 -0
  13. package/dist/agent/tools/watch.js +72 -0
  14. package/dist/core/config.js +97 -0
  15. package/dist/{tools → core}/find.js +3 -0
  16. package/dist/{tools → core}/format.js +27 -2
  17. package/dist/core/kind.js +31 -0
  18. package/dist/server.js +2 -152
  19. package/dist/tui/{badge.js → components/badge.js} +1 -1
  20. package/dist/tui/{console.js → components/console.js} +270 -139
  21. package/dist/tui/{dock.js → components/dock.js} +75 -19
  22. package/dist/tui/{sidebar.js → components/sidebar.js} +16 -1
  23. package/dist/tui/dialogs.js +163 -0
  24. package/dist/tui/index.js +35 -94
  25. package/dist/tui/lib/details.js +23 -0
  26. package/dist/tui/lib/search.js +39 -0
  27. package/dist/tui/lib/update.js +58 -0
  28. package/dist/tui/{view.js → lib/view.js} +55 -2
  29. package/dist/tui/{store.js → state/store.js} +4 -2
  30. package/package.json +4 -5
  31. package/types/agent/plugin.d.ts +12 -0
  32. package/types/agent/tools/index.d.ts +5 -0
  33. package/types/agent/tools/list.d.ts +3 -0
  34. package/types/agent/tools/read.d.ts +3 -0
  35. package/types/agent/tools/restart.d.ts +3 -0
  36. package/types/agent/tools/send.d.ts +3 -0
  37. package/types/agent/tools/shared.d.ts +40 -0
  38. package/types/agent/tools/start.d.ts +3 -0
  39. package/types/agent/tools/stop.d.ts +3 -0
  40. package/types/agent/tools/wait.d.ts +3 -0
  41. package/types/agent/tools/watch-args.d.ts +18 -0
  42. package/types/agent/tools/watch.d.ts +3 -0
  43. package/types/core/config.d.ts +60 -0
  44. package/types/{tools → core}/find.d.ts +5 -0
  45. package/types/core/kind.d.ts +9 -0
  46. package/types/server.d.ts +2 -12
  47. package/types/tui/{console.d.ts → components/console.d.ts} +7 -1
  48. package/types/tui/{dock.d.ts → components/dock.d.ts} +3 -1
  49. package/types/tui/{sidebar.d.ts → components/sidebar.d.ts} +1 -1
  50. package/types/tui/dialogs.d.ts +10 -0
  51. package/types/tui/index.d.ts +3 -9
  52. package/types/tui/lib/details.d.ts +3 -0
  53. package/types/tui/lib/search.d.ts +7 -0
  54. package/types/tui/lib/update.d.ts +25 -0
  55. package/types/tui/{view.d.ts → lib/view.d.ts} +16 -1
  56. package/types/tui/{store.d.ts → state/store.d.ts} +9 -0
  57. package/dist/tools/index.js +0 -433
  58. package/types/tools/index.d.ts +0 -17
  59. /package/dist/{tools → agent/tools}/keys.js +0 -0
  60. /package/dist/tui/{keys.js → lib/keys.js} +0 -0
  61. /package/types/{tools → agent/tools}/keys.d.ts +0 -0
  62. /package/types/{tools → core}/format.d.ts +0 -0
  63. /package/types/tui/{badge.d.ts → components/badge.d.ts} +0 -0
  64. /package/types/tui/{keys.d.ts → lib/keys.d.ts} +0 -0
@@ -1,5 +1,5 @@
1
- import { use as _$use } from "@opentui/solid";
2
1
  import { memo as _$memo } from "@opentui/solid";
2
+ import { use as _$use } from "@opentui/solid";
3
3
  import { effect as _$effect } from "@opentui/solid";
4
4
  import { createTextNode as _$createTextNode } from "@opentui/solid";
5
5
  import { insertNode as _$insertNode } from "@opentui/solid";
@@ -12,20 +12,17 @@ import { createElement as _$createElement } from "@opentui/solid";
12
12
  import { useBindings } from "@opentui/keymap/solid";
13
13
  import { useTerminalDimensions } from "@opentui/solid";
14
14
  import { createEffect, createMemo, createSignal, For, on, onCleanup, Show } from "solid-js";
15
+ import { detailRows } from "../lib/details.js";
16
+ import { isReleaseKey, keyToBytes } from "../lib/keys.js";
17
+ import { friendlyError, splitMatches } from "../lib/search.js";
18
+ import { displayCommand, kindColor, kindOf, relativeCwd, statusDetail, tailLines, tailRuns, truncate, watchColor, watchLabel, wrapText } from "../lib/view.js";
19
+ import { useScreen } from "../state/store.js";
15
20
  import { Badge } from "./badge.js";
16
- import { isReleaseKey, keyToBytes } from "./keys.js";
17
- import { useScreen } from "./store.js";
18
- import { displayCommand, kindColor, kindOf, relativeCwd, statusDetail, tailLines, truncate, wrapText } from "./view.js";
19
21
  /** Daemon errors name ids and internal states; say what happened instead. */
20
- function friendlyError(err) {
21
- const message = err instanceof Error ? err.message : String(err);
22
- if (/is (exited|killed|failed)$/.test(message)) return "the shell is no longer running";
23
- if (/not found$/.test(message)) return "that shell was already removed";
24
- if (/connection|not running|did not start/.test(message)) return "lost connection to cockpitd, retrying";
25
- return message;
26
- }
27
22
  const COMMAND_LINES = 3;
28
23
 
24
+ /** Splits a line into plain and matching parts so a filtered log can highlight what matched. */
25
+
29
26
  /**
30
27
  * Keyboard-first shell console in an overlay. Normal mode: single-key actions. Typing mode:
31
28
  * every key goes to the program (ctrl+c included); ctrl+] returns to normal mode.
@@ -34,10 +31,14 @@ export function Console(props) {
34
31
  const theme = () => props.api.theme.current;
35
32
  const dims = useTerminalDimensions();
36
33
  const shell = () => props.store.selected();
37
- const [view, setView] = createSignal("screen");
34
+ const [view, setView] = createSignal(props.defaultView ?? "screen");
38
35
  const [typing, setTyping] = createSignal(props.typing ?? false);
39
36
  const [log, setLog] = createSignal([]);
40
37
  const [notice, setNotice] = createSignal();
38
+ /** Applied log filter, and the query being typed for it. */
39
+ const [filter, setFilter] = createSignal("");
40
+ const [searching, setSearching] = createSignal(false);
41
+ const [draft, setDraft] = createSignal("");
41
42
  const {
42
43
  screen
43
44
  } = useScreen(props.store, () => shell()?.id);
@@ -96,14 +97,17 @@ export function Console(props) {
96
97
  };
97
98
 
98
99
  // Log view: reload on selection, view switch and new output.
99
- createEffect(on([() => shell()?.id, view, () => screen()], () => {
100
+ createEffect(on([() => shell()?.id, view, filter, () => screen()], () => {
100
101
  const id = shell()?.id;
101
102
  if (!id || view() !== "log") return;
103
+ // The daemon greps server-side, so filtering a 40k-line log costs one call, not a transfer.
102
104
  void client.call("shell.read", {
103
105
  id,
104
106
  tail: 2000,
105
- limit: 2000
106
- }).then(page => setLog(page.lines)).catch(() => {});
107
+ limit: 2000,
108
+ grep: filter() || undefined,
109
+ ignoreCase: true
110
+ }).then(page => setLog(page.lines)).catch(() => setLog([]));
107
111
  }));
108
112
 
109
113
  // Typing mode: size the PTY to what we show, then forward every key.
@@ -121,6 +125,24 @@ export function Console(props) {
121
125
  if (typing() && !running()) setTyping(false);
122
126
  });
123
127
  const release = props.api.keymap.intercept("key", ctx => {
128
+ // Search takes keys first: a typed query must reach neither the program nor the dialog.
129
+ if (searching()) {
130
+ const event = ctx.event;
131
+ ctx.consume({
132
+ preventDefault: true,
133
+ stopPropagation: true
134
+ });
135
+ if (event.name === "escape") return setSearching(false);
136
+ if (event.name === "return" || event.name === "enter") {
137
+ setFilter(draft().trim());
138
+ return setSearching(false);
139
+ }
140
+ if (event.name === "backspace") return setDraft(value => value.slice(0, -1));
141
+ if (event.sequence && !event.ctrl && !event.meta && event.sequence >= " ") {
142
+ setDraft(value => value + event.sequence);
143
+ }
144
+ return;
145
+ }
124
146
  if (!typing()) return;
125
147
  const event = ctx.event;
126
148
  ctx.consume({
@@ -188,18 +210,35 @@ export function Console(props) {
188
210
  }, {
189
211
  name: "cockpit.console.clear",
190
212
  title: "Clear finished shells",
191
- run: () => act("clear", async () => {
192
- const n = await props.store.clearFinished();
193
- return `cleared ${n} finished shell${n === 1 ? "" : "s"}`;
194
- })
195
- }, {
196
- name: "cockpit.console.all",
197
- title: "Show all / fewer shells",
198
- run: () => props.store.toggleAll()
213
+ run: () => {
214
+ if (finished() === 0) {
215
+ flash("nothing to clear: no finished shells", "info", 2500);
216
+ return;
217
+ }
218
+ act("clear", async () => {
219
+ const n = await props.store.clearFinished();
220
+ return `cleared ${n} finished shell${n === 1 ? "" : "s"}`;
221
+ });
222
+ }
199
223
  }, {
200
224
  name: "cockpit.console.view",
201
225
  title: "Toggle screen/log",
202
226
  run: () => setView(v => v === "log" ? "screen" : "log")
227
+ }, {
228
+ name: "cockpit.console.search",
229
+ title: "Search this shell's log",
230
+ run: () => {
231
+ setView("log");
232
+ setDraft(filter());
233
+ setSearching(true);
234
+ }
235
+ }, {
236
+ name: "cockpit.console.searchClear",
237
+ title: "Clear the log filter",
238
+ run: () => {
239
+ setFilter("");
240
+ setDraft("");
241
+ }
203
242
  }, {
204
243
  name: "cockpit.console.details",
205
244
  title: "Toggle details",
@@ -261,14 +300,18 @@ export function Console(props) {
261
300
  key: "shift+d",
262
301
  cmd: "cockpit.console.clear",
263
302
  desc: "Clear finished"
264
- }, {
265
- key: "a",
266
- cmd: "cockpit.console.all",
267
- desc: "All"
268
303
  }, {
269
304
  key: "tab",
270
305
  cmd: "cockpit.console.view",
271
306
  desc: "Screen/log"
307
+ }, {
308
+ key: "/",
309
+ cmd: "cockpit.console.search",
310
+ desc: "Search log"
311
+ }, {
312
+ key: "backspace",
313
+ cmd: "cockpit.console.searchClear",
314
+ desc: "Clear filter"
272
315
  }, {
273
316
  key: "?,shift+/",
274
317
  cmd: "cockpit.console.details",
@@ -308,26 +351,38 @@ export function Console(props) {
308
351
  }]
309
352
  }));
310
353
  const screenText = createMemo(() => tailLines(screen()?.text, bodyRows(), bodyCols()));
354
+ // Colour when the daemon sent styled rows; the plain text stays the fallback.
355
+ const screenRuns = createMemo(() => props.colors === false ? [] : tailRuns(screen()?.styled, bodyRows(), bodyCols()));
311
356
  const details = createMemo(() => shell() ? detailRows(shell(), props.store.now(), bodyCols()) : []);
312
357
  const bodyHeight = createMemo(() => {
313
358
  if (typing()) return bodyRows();
314
359
  const content = view() === "log" ? log().length : view() === "details" ? details().length : screenText().split("\n").length;
315
360
  return Math.min(bodyRows(), Math.max(6, content));
316
361
  });
362
+ const finished = createMemo(() => props.store.shells().filter(s => s.status !== "running").length);
317
363
  const position = createMemo(() => {
318
- const list = props.store.visible();
364
+ const list = props.store.shells();
319
365
  const index = list.findIndex(x => x.id === shell()?.id);
320
- const hidden = props.store.hidden().length;
321
- return list.length > 1 || hidden > 0 ? `${index + 1}/${list.length}${hidden ? ` +${hidden}` : ""}` : "";
366
+ return list.length > 1 ? `${index + 1}/${list.length}` : "";
322
367
  });
323
368
  // Only the keys that do something for the selected shell.
369
+ /** Only the keys that do something right now: no sidebar concepts, no actions with no target. */
324
370
  const hint = createMemo(() => {
371
+ if (searching()) return `search: ${draft()}▏· enter filters · esc cancels`;
325
372
  if (typing()) return "TYPING: keys go to the shell (ctrl+c included) · ctrl+] stop typing";
326
- const next = view() === "log" ? "screen" : "log";
327
373
  const wide = dims().width >= 110;
328
- const live = running() ? wide ? ["i type", "c ^C", "r restart", "x stop"] : ["i type", "c ^C", "r", "x"] : [wide ? "r run again" : "r rerun", wide ? "d remove" : "d"];
329
- const common = wide ? [`tab ${next}`, "? details", "[ ] switch", "D clear done", "a all", "esc"] : [`tab ${next}`, "?", "[ ]", "D", "a", "esc"];
330
- return [...(shell() ? live : ["n new"]), ...common].join(" · ");
374
+ const label = (long, short) => wide ? long : short;
375
+ const keys = [];
376
+ if (!shell()) return ["n new", "esc close"].join(" · ");
377
+ keys.push(...(running() ? [label("i type", "i"), label("c ^C", "c"), label("r restart", "r"), label("x stop", "x")] : [label("r run again", "r"), label("d remove", "d")]));
378
+ if (view() === "log" && filter()) keys.push(label("backspace clear filter", "⌫ filter"));
379
+ keys.push(label(`tab ${view() === "log" ? "screen" : "log"}`, "tab"));
380
+ if (view() !== "details") keys.push(label("/ search log", "/"));
381
+ keys.push(label("? details", "?"));
382
+ if (props.store.shells().length > 1) keys.push(label("[ ] switch", "[ ]"));
383
+ if (finished() > 0) keys.push(label("D clear done", "D"));
384
+ keys.push(label("n new", "n"), "esc");
385
+ return keys.join(" · ");
331
386
  });
332
387
  return (() => {
333
388
  var _el$ = _$createElement("box"),
@@ -375,10 +430,10 @@ export function Console(props) {
375
430
  _el$0 = _$createTextNode(` `),
376
431
  _el$1 = _$createElement("b"),
377
432
  _el$10 = _$createElement("box"),
378
- _el$11 = _$createElement("text");
433
+ _el$14 = _$createElement("text");
379
434
  _$insertNode(_el$8, _el$9);
380
435
  _$insertNode(_el$8, _el$10);
381
- _$insertNode(_el$8, _el$11);
436
+ _$insertNode(_el$8, _el$14);
382
437
  _$setProp(_el$8, "height", 1);
383
438
  _$setProp(_el$8, "flexShrink", 0);
384
439
  _$setProp(_el$8, "flexDirection", "row");
@@ -400,14 +455,31 @@ export function Console(props) {
400
455
  _$setProp(_el$9, "flexShrink", 1);
401
456
  _$insert(_el$1, () => truncate(s().title, Math.max(10, bodyCols() - 50)));
402
457
  _$setProp(_el$10, "flexGrow", 1);
403
- _$setProp(_el$11, "wrapMode", "none");
404
- _$setProp(_el$11, "flexShrink", 0);
405
- _$insert(_el$11, () => [statusDetail(s(), props.store.now()), s().run > 1 ? `run ${s().run}` : "", position()].filter(Boolean).join(" · "));
458
+ _$insert(_el$8, _$createComponent(Show, {
459
+ get when() {
460
+ return watchLabel(s());
461
+ },
462
+ get children() {
463
+ var _el$11 = _$createElement("text"),
464
+ _el$12 = _$createTextNode(` `),
465
+ _el$13 = _$createTextNode(` `);
466
+ _$insertNode(_el$11, _el$12);
467
+ _$insertNode(_el$11, _el$13);
468
+ _$setProp(_el$11, "wrapMode", "none");
469
+ _$setProp(_el$11, "flexShrink", 0);
470
+ _$insert(_el$11, () => watchLabel(s()), _el$13);
471
+ _$effect(_$p => _$setProp(_el$11, "fg", watchColor(theme(), s()), _$p));
472
+ return _el$11;
473
+ }
474
+ }), _el$14);
475
+ _$setProp(_el$14, "wrapMode", "none");
476
+ _$setProp(_el$14, "flexShrink", 0);
477
+ _$insert(_el$14, () => [statusDetail(s(), props.store.now()), s().run > 1 ? `run ${s().run}` : "", position()].filter(Boolean).join(" · "));
406
478
  _$effect(_p$ => {
407
479
  var _v$3 = theme().text,
408
480
  _v$4 = theme().textMuted;
409
481
  _v$3 !== _p$.e && (_p$.e = _$setProp(_el$9, "fg", _v$3, _p$.e));
410
- _v$4 !== _p$.t && (_p$.t = _$setProp(_el$11, "fg", _v$4, _p$.t));
482
+ _v$4 !== _p$.t && (_p$.t = _$setProp(_el$14, "fg", _v$4, _p$.t));
411
483
  return _p$;
412
484
  }, {
413
485
  e: undefined,
@@ -419,167 +491,240 @@ export function Console(props) {
419
491
  return commandLines();
420
492
  },
421
493
  children: (line, index) => (() => {
422
- var _el$17 = _$createElement("text"),
423
- _el$18 = _$createElement("span");
424
- _$insertNode(_el$17, _el$18);
425
- _$setProp(_el$17, "wrapMode", "none");
426
- _$setProp(_el$17, "flexShrink", 0);
427
- _$insert(_el$18, () => index() === 0 ? "$ " : " ");
428
- _$insert(_el$17, line, null);
494
+ var _el$23 = _$createElement("text"),
495
+ _el$24 = _$createElement("span");
496
+ _$insertNode(_el$23, _el$24);
497
+ _$setProp(_el$23, "wrapMode", "none");
498
+ _$setProp(_el$23, "flexShrink", 0);
499
+ _$insert(_el$24, () => index() === 0 ? "$ " : " ");
500
+ _$insert(_el$23, line, null);
429
501
  _$effect(_p$ => {
430
502
  var _v$7 = theme().text,
431
503
  _v$8 = {
432
504
  fg: theme().textMuted
433
505
  };
434
- _v$7 !== _p$.e && (_p$.e = _$setProp(_el$17, "fg", _v$7, _p$.e));
435
- _v$8 !== _p$.t && (_p$.t = _$setProp(_el$18, "style", _v$8, _p$.t));
506
+ _v$7 !== _p$.e && (_p$.e = _$setProp(_el$23, "fg", _v$7, _p$.e));
507
+ _v$8 !== _p$.t && (_p$.t = _$setProp(_el$24, "style", _v$8, _p$.t));
436
508
  return _p$;
437
509
  }, {
438
510
  e: undefined,
439
511
  t: undefined
440
512
  });
441
- return _el$17;
513
+ return _el$23;
442
514
  })()
443
515
  }), _$createComponent(Show, {
444
516
  get when() {
445
517
  return folder();
446
518
  },
447
519
  get children() {
448
- var _el$12 = _$createElement("text");
449
- _$setProp(_el$12, "wrapMode", "none");
450
- _$setProp(_el$12, "flexShrink", 0);
451
- _$insert(_el$12, () => truncate(`in ${folder()}`, bodyCols()));
452
- _$effect(_$p => _$setProp(_el$12, "fg", theme().textMuted, _$p));
453
- return _el$12;
520
+ var _el$15 = _$createElement("text");
521
+ _$setProp(_el$15, "wrapMode", "none");
522
+ _$setProp(_el$15, "flexShrink", 0);
523
+ _$insert(_el$15, () => truncate(`in ${folder()}`, bodyCols()));
524
+ _$effect(_$p => _$setProp(_el$15, "fg", theme().textMuted, _$p));
525
+ return _el$15;
454
526
  }
455
527
  }), _$createComponent(Show, {
456
528
  get when() {
457
529
  return failure();
458
530
  },
459
531
  children: summary => (() => {
460
- var _el$19 = _$createElement("text");
461
- _$setProp(_el$19, "wrapMode", "none");
462
- _$setProp(_el$19, "flexShrink", 0);
463
- _$insert(_el$19, () => truncate(`${kindOf(s()) === "fail" ? "error" : "last output"}: ${summary()}`, bodyCols()));
464
- _$effect(_$p => _$setProp(_el$19, "fg", kindColor(theme(), kindOf(s())), _$p));
465
- return _el$19;
532
+ var _el$25 = _$createElement("text");
533
+ _$setProp(_el$25, "wrapMode", "none");
534
+ _$setProp(_el$25, "flexShrink", 0);
535
+ _$insert(_el$25, () => truncate(`${kindOf(s()) === "fail" ? "error" : "last output"}: ${summary()}`, bodyCols()));
536
+ _$effect(_$p => _$setProp(_el$25, "fg", kindColor(theme(), kindOf(s())), _$p));
537
+ return _el$25;
466
538
  })()
467
539
  }), (() => {
468
- var _el$13 = _$createElement("box");
469
- _$setProp(_el$13, "flexShrink", 0);
470
- _$setProp(_el$13, "marginTop", 1);
471
- _$setProp(_el$13, "marginBottom", 1);
472
- _$setProp(_el$13, "border", ["left"]);
473
- _$setProp(_el$13, "paddingLeft", 1);
474
- _$setProp(_el$13, "overflow", "hidden");
475
- _$insert(_el$13, _$createComponent(Show, {
540
+ var _el$16 = _$createElement("box");
541
+ _$setProp(_el$16, "flexShrink", 0);
542
+ _$setProp(_el$16, "marginTop", 1);
543
+ _$setProp(_el$16, "marginBottom", 1);
544
+ _$setProp(_el$16, "border", ["left"]);
545
+ _$setProp(_el$16, "paddingLeft", 1);
546
+ _$setProp(_el$16, "overflow", "hidden");
547
+ _$insert(_el$16, _$createComponent(Show, {
476
548
  get when() {
477
549
  return view() === "screen";
478
550
  },
479
551
  get children() {
480
- var _el$14 = _$createElement("text");
481
- _$setProp(_el$14, "wrapMode", "none");
482
- _$insert(_el$14, () => screenText() || " ");
483
- _$effect(_$p => _$setProp(_el$14, "fg", theme().text, _$p));
484
- return _el$14;
552
+ return _$createComponent(Show, {
553
+ get when() {
554
+ return screenRuns().length > 0;
555
+ },
556
+ get fallback() {
557
+ return (() => {
558
+ var _el$26 = _$createElement("text");
559
+ _$setProp(_el$26, "wrapMode", "none");
560
+ _$insert(_el$26, () => screenText() || " ");
561
+ _$effect(_$p => _$setProp(_el$26, "fg", theme().text, _$p));
562
+ return _el$26;
563
+ })();
564
+ },
565
+ get children() {
566
+ var _el$17 = _$createElement("box");
567
+ _$setProp(_el$17, "flexDirection", "column");
568
+ _$insert(_el$17, _$createComponent(For, {
569
+ get each() {
570
+ return screenRuns();
571
+ },
572
+ children: row => (() => {
573
+ var _el$27 = _$createElement("text"),
574
+ _el$28 = _$createTextNode(` `);
575
+ _$insertNode(_el$27, _el$28);
576
+ _$setProp(_el$27, "wrapMode", "none");
577
+ _$insert(_el$27, _$createComponent(For, {
578
+ each: row,
579
+ children: run => (() => {
580
+ var _el$29 = _$createElement("span");
581
+ _$insert(_el$29, () => run.text);
582
+ _$effect(_$p => _$setProp(_el$29, "style", {
583
+ fg: run.fg,
584
+ bg: run.bg,
585
+ bold: run.bold,
586
+ italic: run.italic
587
+ }, _$p));
588
+ return _el$29;
589
+ })()
590
+ }), _el$28);
591
+ _$effect(_$p => _$setProp(_el$27, "fg", theme().text, _$p));
592
+ return _el$27;
593
+ })()
594
+ }));
595
+ return _el$17;
596
+ }
597
+ });
485
598
  }
486
599
  }), null);
487
- _$insert(_el$13, _$createComponent(Show, {
600
+ _$insert(_el$16, _$createComponent(Show, {
488
601
  get when() {
489
602
  return view() === "details";
490
603
  },
491
604
  get children() {
492
- var _el$15 = _$createElement("box");
493
- _$setProp(_el$15, "flexDirection", "column");
494
- _$insert(_el$15, _$createComponent(For, {
605
+ var _el$18 = _$createElement("box");
606
+ _$setProp(_el$18, "flexDirection", "column");
607
+ _$insert(_el$18, _$createComponent(For, {
495
608
  get each() {
496
609
  return details();
497
610
  },
498
611
  children: ([key, value]) => (() => {
499
- var _el$20 = _$createElement("text"),
500
- _el$21 = _$createElement("span");
501
- _$insertNode(_el$20, _el$21);
502
- _$setProp(_el$20, "wrapMode", "none");
503
- _$insert(_el$21, () => key.padEnd(10));
504
- _$insert(_el$20, value, null);
612
+ var _el$30 = _$createElement("text"),
613
+ _el$31 = _$createElement("span");
614
+ _$insertNode(_el$30, _el$31);
615
+ _$setProp(_el$30, "wrapMode", "none");
616
+ _$insert(_el$31, () => key.padEnd(10));
617
+ _$insert(_el$30, value, null);
505
618
  _$effect(_p$ => {
506
619
  var _v$9 = theme().text,
507
620
  _v$0 = {
508
621
  fg: theme().textMuted
509
622
  };
510
- _v$9 !== _p$.e && (_p$.e = _$setProp(_el$20, "fg", _v$9, _p$.e));
511
- _v$0 !== _p$.t && (_p$.t = _$setProp(_el$21, "style", _v$0, _p$.t));
623
+ _v$9 !== _p$.e && (_p$.e = _$setProp(_el$30, "fg", _v$9, _p$.e));
624
+ _v$0 !== _p$.t && (_p$.t = _$setProp(_el$31, "style", _v$0, _p$.t));
512
625
  return _p$;
513
626
  }, {
514
627
  e: undefined,
515
628
  t: undefined
516
629
  });
517
- return _el$20;
630
+ return _el$30;
518
631
  })()
519
632
  }));
520
- return _el$15;
633
+ return _el$18;
521
634
  }
522
635
  }), null);
523
- _$insert(_el$13, _$createComponent(Show, {
636
+ _$insert(_el$16, _$createComponent(Show, {
524
637
  get when() {
525
638
  return view() === "log";
526
639
  },
527
640
  get children() {
528
- var _el$16 = _$createElement("scrollbox");
641
+ var _el$19 = _$createElement("scrollbox");
529
642
  _$use(el => {
530
643
  scroll = el;
531
- }, _el$16);
532
- _$setProp(_el$16, "flexGrow", 1);
533
- _$setProp(_el$16, "stickyScroll", true);
534
- _$setProp(_el$16, "stickyStart", "bottom");
535
- _$setProp(_el$16, "verticalScrollbarOptions", {
644
+ }, _el$19);
645
+ _$setProp(_el$19, "flexGrow", 1);
646
+ _$setProp(_el$19, "stickyScroll", true);
647
+ _$setProp(_el$19, "stickyStart", "bottom");
648
+ _$setProp(_el$19, "verticalScrollbarOptions", {
536
649
  visible: false
537
650
  });
538
- _$setProp(_el$16, "horizontalScrollbarOptions", {
651
+ _$setProp(_el$19, "horizontalScrollbarOptions", {
539
652
  visible: false
540
653
  });
541
- _$insert(_el$16, _$createComponent(For, {
654
+ _$insert(_el$19, _$createComponent(For, {
542
655
  get each() {
543
656
  return log();
544
657
  },
545
658
  children: line => (() => {
546
- var _el$22 = _$createElement("text"),
547
- _el$23 = _$createElement("span"),
548
- _el$24 = _$createTextNode(` `);
549
- _$insertNode(_el$22, _el$23);
550
- _$setProp(_el$22, "wrapMode", "none");
551
- _$insertNode(_el$23, _el$24);
552
- _$insert(_el$23, () => String(line.n).padStart(5), _el$24);
553
- _$insert(_el$22, () => truncate(line.text, bodyCols() - 7), null);
659
+ var _el$32 = _$createElement("text"),
660
+ _el$33 = _$createElement("span"),
661
+ _el$34 = _$createTextNode(` `);
662
+ _$insertNode(_el$32, _el$33);
663
+ _$setProp(_el$32, "wrapMode", "none");
664
+ _$insertNode(_el$33, _el$34);
665
+ _$insert(_el$33, () => String(line.n).padStart(5), _el$34);
666
+ _$insert(_el$32, _$createComponent(For, {
667
+ get each() {
668
+ return splitMatches(truncate(line.text, bodyCols() - 7), filter());
669
+ },
670
+ children: part => part.match ? (() => {
671
+ var _el$35 = _$createElement("span");
672
+ _$insert(_el$35, () => part.text);
673
+ _$effect(_$p => _$setProp(_el$35, "style", {
674
+ fg: theme().background,
675
+ bg: theme().warning
676
+ }, _$p));
677
+ return _el$35;
678
+ })() : (() => {
679
+ var _el$36 = _$createElement("span");
680
+ _$insert(_el$36, () => part.text);
681
+ return _el$36;
682
+ })()
683
+ }), null);
554
684
  _$effect(_p$ => {
555
685
  var _v$1 = theme().text,
556
686
  _v$10 = {
557
687
  fg: theme().textMuted
558
688
  };
559
- _v$1 !== _p$.e && (_p$.e = _$setProp(_el$22, "fg", _v$1, _p$.e));
560
- _v$10 !== _p$.t && (_p$.t = _$setProp(_el$23, "style", _v$10, _p$.t));
689
+ _v$1 !== _p$.e && (_p$.e = _$setProp(_el$32, "fg", _v$1, _p$.e));
690
+ _v$10 !== _p$.t && (_p$.t = _$setProp(_el$33, "style", _v$10, _p$.t));
561
691
  return _p$;
562
692
  }, {
563
693
  e: undefined,
564
694
  t: undefined
565
695
  });
566
- return _el$22;
696
+ return _el$32;
567
697
  })()
568
- }));
569
- return _el$16;
698
+ }), null);
699
+ _$insert(_el$19, _$createComponent(Show, {
700
+ get when() {
701
+ return _$memo(() => !!filter())() && log().length === 0;
702
+ },
703
+ get children() {
704
+ var _el$20 = _$createElement("text"),
705
+ _el$21 = _$createTextNode(`no lines match "`),
706
+ _el$22 = _$createTextNode(`"`);
707
+ _$insertNode(_el$20, _el$21);
708
+ _$insertNode(_el$20, _el$22);
709
+ _$insert(_el$20, filter, _el$22);
710
+ _$effect(_$p => _$setProp(_el$20, "fg", theme().textMuted, _$p));
711
+ return _el$20;
712
+ }
713
+ }), null);
714
+ return _el$19;
570
715
  }
571
716
  }), null);
572
717
  _$effect(_p$ => {
573
718
  var _v$5 = bodyHeight(),
574
719
  _v$6 = typing() ? theme().accent : theme().border;
575
- _v$5 !== _p$.e && (_p$.e = _$setProp(_el$13, "height", _v$5, _p$.e));
576
- _v$6 !== _p$.t && (_p$.t = _$setProp(_el$13, "borderColor", _v$6, _p$.t));
720
+ _v$5 !== _p$.e && (_p$.e = _$setProp(_el$16, "height", _v$5, _p$.e));
721
+ _v$6 !== _p$.t && (_p$.t = _$setProp(_el$16, "borderColor", _v$6, _p$.t));
577
722
  return _p$;
578
723
  }, {
579
724
  e: undefined,
580
725
  t: undefined
581
726
  });
582
- return _el$13;
727
+ return _el$16;
583
728
  })()]
584
729
  }), _el$2);
585
730
  _$setProp(_el$2, "height", 1);
@@ -592,37 +737,23 @@ export function Console(props) {
592
737
  },
593
738
  get fallback() {
594
739
  return (() => {
595
- var _el$25 = _$createElement("text");
596
- _$setProp(_el$25, "wrapMode", "none");
597
- _$insert(_el$25, () => truncate(hint(), bodyCols()));
598
- _$effect(_$p => _$setProp(_el$25, "fg", typing() ? theme().accent : theme().textMuted, _$p));
599
- return _el$25;
740
+ var _el$37 = _$createElement("text");
741
+ _$setProp(_el$37, "wrapMode", "none");
742
+ _$insert(_el$37, () => truncate(hint(), bodyCols()));
743
+ _$effect(_$p => _$setProp(_el$37, "fg", typing() ? theme().accent : theme().textMuted, _$p));
744
+ return _el$37;
600
745
  })();
601
746
  },
602
747
  children: n => (() => {
603
- var _el$26 = _$createElement("text");
604
- _$setProp(_el$26, "wrapMode", "none");
605
- _$insert(_el$26, () => truncate(n().text, bodyCols()));
606
- _$effect(_$p => _$setProp(_el$26, "fg", n().tone === "error" ? theme().error : n().tone === "success" ? theme().success : theme().warning, _$p));
607
- return _el$26;
748
+ var _el$38 = _$createElement("text");
749
+ _$setProp(_el$38, "wrapMode", "none");
750
+ _$insert(_el$38, () => truncate(n().text, bodyCols()));
751
+ _$effect(_$p => _$setProp(_el$38, "fg", n().tone === "error" ? theme().error : n().tone === "success" ? theme().success : theme().warning, _$p));
752
+ return _el$38;
608
753
  })()
609
754
  }));
610
755
  return _el$;
611
756
  })();
612
757
  }
613
758
 
614
- /** Everything about a shell, with the full command wrapped rather than cut. */
615
- function detailRows(s, now, cols) {
616
- const width = Math.max(10, cols - 11);
617
- const rows = [];
618
- for (const [i, line] of wrapText(displayCommand(s), width, 12).entries()) rows.push([i === 0 ? "command" : "", line]);
619
- rows.push(["folder", s.cwd]);
620
- rows.push(["status", `${s.status}${s.exitCode !== undefined ? ` (exit ${s.exitCode})` : ""}${s.signal ? ` (${s.signal})` : ""}`]);
621
- rows.push(["timing", statusDetail(s, now)]);
622
- rows.push(["started", new Date(s.startedAt).toLocaleString()]);
623
- if (s.summary) rows.push(["summary", truncate(s.summary, width)]);
624
- rows.push(["id", `${s.id} · run ${s.run}${s.pid ? ` · pid ${s.pid}` : ""}`]);
625
- rows.push(["owner", s.owner.session ? `agent session ${s.owner.session}` : "you"]);
626
- rows.push(["output", `${s.lines.last} lines · ${Math.round(s.bytes / 1024)} KiB`]);
627
- return rows;
628
- }
759
+ /** Everything about a shell, with the full command wrapped rather than cut. */