@opencode-cockpit/shell 0.1.3 → 0.1.5
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/dist/connect.js +31 -0
- package/dist/server.js +152 -0
- package/dist/tools/find.js +66 -0
- package/dist/tools/format.js +63 -0
- package/dist/tools/index.js +433 -0
- package/{src/tools/keys.ts → dist/tools/keys.js} +11 -14
- package/dist/tui/badge.js +29 -0
- package/dist/tui/console.js +628 -0
- package/dist/tui/dock.js +214 -0
- package/dist/tui/index.js +264 -0
- package/dist/tui/keys.js +43 -0
- package/dist/tui/sidebar.js +121 -0
- package/dist/tui/store.js +156 -0
- package/dist/tui/view.js +154 -0
- package/package.json +23 -13
- package/types/connect.d.ts +9 -0
- package/types/server.d.ts +12 -0
- package/types/tools/find.d.ts +32 -0
- package/types/tools/format.d.ts +8 -0
- package/types/tools/index.d.ts +17 -0
- package/types/tools/keys.d.ts +3 -0
- package/types/tui/badge.d.ts +9 -0
- package/types/tui/console.d.ts +16 -0
- package/types/tui/dock.d.ts +12 -0
- package/types/tui/index.d.ts +19 -0
- package/types/tui/keys.d.ts +8 -0
- package/types/tui/sidebar.d.ts +14 -0
- package/types/tui/store.d.ts +46 -0
- package/types/tui/view.d.ts +67 -0
- package/src/connect.ts +0 -24
- package/src/server.ts +0 -156
- package/src/tools/find.ts +0 -80
- package/src/tools/format.ts +0 -78
- package/src/tools/index.ts +0 -467
- package/src/tui/badge.tsx +0 -17
- package/src/tui/console.tsx +0 -438
- package/src/tui/dock.tsx +0 -130
- package/src/tui/index.tsx +0 -286
- package/src/tui/keys.ts +0 -52
- package/src/tui/sidebar.tsx +0 -44
- package/src/tui/store.ts +0 -185
- package/src/tui/view.ts +0 -161
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
import { use as _$use } from "@opentui/solid";
|
|
2
|
+
import { memo as _$memo } from "@opentui/solid";
|
|
3
|
+
import { effect as _$effect } from "@opentui/solid";
|
|
4
|
+
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
5
|
+
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
6
|
+
import { insert as _$insert } from "@opentui/solid";
|
|
7
|
+
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
8
|
+
import { setProp as _$setProp } from "@opentui/solid";
|
|
9
|
+
import { createElement as _$createElement } from "@opentui/solid";
|
|
10
|
+
/** @jsxImportSource @opentui/solid */
|
|
11
|
+
|
|
12
|
+
import { useBindings } from "@opentui/keymap/solid";
|
|
13
|
+
import { useTerminalDimensions } from "@opentui/solid";
|
|
14
|
+
import { createEffect, createMemo, createSignal, For, on, onCleanup, Show } from "solid-js";
|
|
15
|
+
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
|
+
/** 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
|
+
const COMMAND_LINES = 3;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Keyboard-first shell console in an overlay. Normal mode: single-key actions. Typing mode:
|
|
31
|
+
* every key goes to the program (ctrl+c included); ctrl+] returns to normal mode.
|
|
32
|
+
*/
|
|
33
|
+
export function Console(props) {
|
|
34
|
+
const theme = () => props.api.theme.current;
|
|
35
|
+
const dims = useTerminalDimensions();
|
|
36
|
+
const shell = () => props.store.selected();
|
|
37
|
+
const [view, setView] = createSignal("screen");
|
|
38
|
+
const [typing, setTyping] = createSignal(props.typing ?? false);
|
|
39
|
+
const [log, setLog] = createSignal([]);
|
|
40
|
+
const [notice, setNotice] = createSignal();
|
|
41
|
+
const {
|
|
42
|
+
screen
|
|
43
|
+
} = useScreen(props.store, () => shell()?.id);
|
|
44
|
+
let scroll;
|
|
45
|
+
const running = () => shell()?.status === "running";
|
|
46
|
+
const client = props.store.client;
|
|
47
|
+
|
|
48
|
+
// Layout budget. The host dialog starts at 1/4 of the height and is 116 columns wide (xlarge),
|
|
49
|
+
// capped by the screen; the body grows with content up to what the header leaves.
|
|
50
|
+
const frameRows = () => Math.max(10, dims().height - Math.floor(dims().height / 4) - 3);
|
|
51
|
+
const bodyCols = () => Math.max(20, Math.min(116, dims().width - 2) - 7);
|
|
52
|
+
const commandLines = createMemo(() => {
|
|
53
|
+
const s = shell();
|
|
54
|
+
return s ? wrapText(displayCommand(s), bodyCols() - 2, COMMAND_LINES) : [];
|
|
55
|
+
});
|
|
56
|
+
const folder = createMemo(() => {
|
|
57
|
+
const s = shell();
|
|
58
|
+
return s ? relativeCwd(s.cwd, props.store.project()) : "";
|
|
59
|
+
});
|
|
60
|
+
const failure = createMemo(() => {
|
|
61
|
+
const s = shell();
|
|
62
|
+
if (!s?.summary) return undefined;
|
|
63
|
+
const kind = kindOf(s);
|
|
64
|
+
return kind === "fail" || kind === "stop" ? s.summary : undefined;
|
|
65
|
+
});
|
|
66
|
+
const bodyRows = () => {
|
|
67
|
+
const header = 1 + commandLines().length + (folder() ? 1 : 0) + (failure() ? 1 : 0);
|
|
68
|
+
return Math.max(3, frameRows() - header - 4); // margins (2) + footer (1) + bottom padding (1)
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Transient footer message. Replaces the key hints while shown, then clears itself.
|
|
72
|
+
let noticeTimer;
|
|
73
|
+
const flash = (text, tone, ms) => {
|
|
74
|
+
clearTimeout(noticeTimer);
|
|
75
|
+
setNotice({
|
|
76
|
+
text,
|
|
77
|
+
tone
|
|
78
|
+
});
|
|
79
|
+
if (ms) noticeTimer = setTimeout(() => setNotice(undefined), ms);
|
|
80
|
+
};
|
|
81
|
+
onCleanup(() => clearTimeout(noticeTimer));
|
|
82
|
+
const act = (label, fn) => {
|
|
83
|
+
flash(`${label}…`, "info");
|
|
84
|
+
fn().then(result => typeof result === "string" ? flash(result, "success", 2500) : setNotice(undefined)).catch(err => flash(`${label} failed: ${friendlyError(err)}`, "error", 5000));
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/** Actions that need a live process explain themselves instead of failing. */
|
|
88
|
+
const whileRunning = (label, fn) => () => {
|
|
89
|
+
const s = shell();
|
|
90
|
+
if (!s) return;
|
|
91
|
+
if (s.status !== "running") {
|
|
92
|
+
flash(`can't ${label}: this shell already ${kindOf(s) === "done" ? "finished" : "ended"} · r runs it again`, "info", 3000);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
fn(s.id);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Log view: reload on selection, view switch and new output.
|
|
99
|
+
createEffect(on([() => shell()?.id, view, () => screen()], () => {
|
|
100
|
+
const id = shell()?.id;
|
|
101
|
+
if (!id || view() !== "log") return;
|
|
102
|
+
void client.call("shell.read", {
|
|
103
|
+
id,
|
|
104
|
+
tail: 2000,
|
|
105
|
+
limit: 2000
|
|
106
|
+
}).then(page => setLog(page.lines)).catch(() => {});
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
// Typing mode: size the PTY to what we show, then forward every key.
|
|
110
|
+
createEffect(on(typing, on => {
|
|
111
|
+
const id = shell()?.id;
|
|
112
|
+
if (!on || !id || !running()) return;
|
|
113
|
+
setView("screen");
|
|
114
|
+
void client.call("shell.resize", {
|
|
115
|
+
id,
|
|
116
|
+
cols: bodyCols(),
|
|
117
|
+
rows: bodyRows()
|
|
118
|
+
}).catch(() => {});
|
|
119
|
+
}));
|
|
120
|
+
createEffect(() => {
|
|
121
|
+
if (typing() && !running()) setTyping(false);
|
|
122
|
+
});
|
|
123
|
+
const release = props.api.keymap.intercept("key", ctx => {
|
|
124
|
+
if (!typing()) return;
|
|
125
|
+
const event = ctx.event;
|
|
126
|
+
ctx.consume({
|
|
127
|
+
preventDefault: true,
|
|
128
|
+
stopPropagation: true
|
|
129
|
+
});
|
|
130
|
+
if (isReleaseKey(event)) {
|
|
131
|
+
setTyping(false);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const bytes = keyToBytes(event);
|
|
135
|
+
const id = shell()?.id;
|
|
136
|
+
if (bytes && id) void client.call("shell.write", {
|
|
137
|
+
id,
|
|
138
|
+
data: bytes
|
|
139
|
+
}).catch(() => setTyping(false));
|
|
140
|
+
}, {
|
|
141
|
+
priority: 10_000
|
|
142
|
+
});
|
|
143
|
+
onCleanup(release);
|
|
144
|
+
const withShell = fn => () => {
|
|
145
|
+
const id = shell()?.id;
|
|
146
|
+
if (id) fn(id);
|
|
147
|
+
};
|
|
148
|
+
const toggle = next => setView(v => v === next ? "screen" : next);
|
|
149
|
+
useBindings(() => ({
|
|
150
|
+
enabled: () => !typing(),
|
|
151
|
+
commands: [{
|
|
152
|
+
name: "cockpit.console.type",
|
|
153
|
+
title: "Type into shell",
|
|
154
|
+
run: whileRunning("type", () => setTyping(true))
|
|
155
|
+
}, {
|
|
156
|
+
name: "cockpit.console.interrupt",
|
|
157
|
+
title: "Send ctrl+c",
|
|
158
|
+
run: whileRunning("interrupt", id => act("interrupt", async () => {
|
|
159
|
+
await client.call("shell.write", {
|
|
160
|
+
id,
|
|
161
|
+
data: "\x03"
|
|
162
|
+
});
|
|
163
|
+
return "sent ctrl+c";
|
|
164
|
+
}))
|
|
165
|
+
}, {
|
|
166
|
+
name: "cockpit.console.restart",
|
|
167
|
+
title: "Restart shell",
|
|
168
|
+
run: withShell(id => act("restart", () => client.call("shell.restart", {
|
|
169
|
+
id
|
|
170
|
+
})))
|
|
171
|
+
}, {
|
|
172
|
+
name: "cockpit.console.stop",
|
|
173
|
+
title: "Stop shell",
|
|
174
|
+
run: whileRunning("stop", id => act("stop", async () => {
|
|
175
|
+
await client.call("shell.stop", {
|
|
176
|
+
id,
|
|
177
|
+
signal: "SIGTERM",
|
|
178
|
+
graceMs: 3000
|
|
179
|
+
});
|
|
180
|
+
return "stopped";
|
|
181
|
+
}))
|
|
182
|
+
}, {
|
|
183
|
+
name: "cockpit.console.remove",
|
|
184
|
+
title: "Remove shell",
|
|
185
|
+
run: withShell(id => act("remove", () => client.call("shell.remove", {
|
|
186
|
+
id
|
|
187
|
+
})))
|
|
188
|
+
}, {
|
|
189
|
+
name: "cockpit.console.clear",
|
|
190
|
+
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()
|
|
199
|
+
}, {
|
|
200
|
+
name: "cockpit.console.view",
|
|
201
|
+
title: "Toggle screen/log",
|
|
202
|
+
run: () => setView(v => v === "log" ? "screen" : "log")
|
|
203
|
+
}, {
|
|
204
|
+
name: "cockpit.console.details",
|
|
205
|
+
title: "Toggle details",
|
|
206
|
+
run: () => toggle("details")
|
|
207
|
+
}, {
|
|
208
|
+
name: "cockpit.console.next",
|
|
209
|
+
title: "Next shell",
|
|
210
|
+
run: () => props.store.step(1)
|
|
211
|
+
}, {
|
|
212
|
+
name: "cockpit.console.prev",
|
|
213
|
+
title: "Previous shell",
|
|
214
|
+
run: () => props.store.step(-1)
|
|
215
|
+
}, {
|
|
216
|
+
name: "cockpit.console.new",
|
|
217
|
+
title: "New shell",
|
|
218
|
+
run: () => props.onNewShell()
|
|
219
|
+
}, {
|
|
220
|
+
name: "cockpit.console.down",
|
|
221
|
+
title: "Scroll down",
|
|
222
|
+
run: () => scroll?.scrollBy(3)
|
|
223
|
+
}, {
|
|
224
|
+
name: "cockpit.console.up",
|
|
225
|
+
title: "Scroll up",
|
|
226
|
+
run: () => scroll?.scrollBy(-3)
|
|
227
|
+
}, {
|
|
228
|
+
name: "cockpit.console.bottom",
|
|
229
|
+
title: "Scroll to end",
|
|
230
|
+
run: () => scroll?.scrollTo(scroll.scrollHeight)
|
|
231
|
+
}, {
|
|
232
|
+
name: "cockpit.console.top",
|
|
233
|
+
title: "Scroll to top",
|
|
234
|
+
run: () => scroll?.scrollTo(0)
|
|
235
|
+
}, {
|
|
236
|
+
name: "cockpit.console.close",
|
|
237
|
+
title: "Close console",
|
|
238
|
+
run: () => props.onClose()
|
|
239
|
+
}],
|
|
240
|
+
bindings: [{
|
|
241
|
+
key: "i,return",
|
|
242
|
+
cmd: "cockpit.console.type",
|
|
243
|
+
desc: "Type"
|
|
244
|
+
}, {
|
|
245
|
+
key: "c",
|
|
246
|
+
cmd: "cockpit.console.interrupt",
|
|
247
|
+
desc: "^C"
|
|
248
|
+
}, {
|
|
249
|
+
key: "r",
|
|
250
|
+
cmd: "cockpit.console.restart",
|
|
251
|
+
desc: "Restart"
|
|
252
|
+
}, {
|
|
253
|
+
key: "x",
|
|
254
|
+
cmd: "cockpit.console.stop",
|
|
255
|
+
desc: "Stop"
|
|
256
|
+
}, {
|
|
257
|
+
key: "d",
|
|
258
|
+
cmd: "cockpit.console.remove",
|
|
259
|
+
desc: "Remove"
|
|
260
|
+
}, {
|
|
261
|
+
key: "shift+d",
|
|
262
|
+
cmd: "cockpit.console.clear",
|
|
263
|
+
desc: "Clear finished"
|
|
264
|
+
}, {
|
|
265
|
+
key: "a",
|
|
266
|
+
cmd: "cockpit.console.all",
|
|
267
|
+
desc: "All"
|
|
268
|
+
}, {
|
|
269
|
+
key: "tab",
|
|
270
|
+
cmd: "cockpit.console.view",
|
|
271
|
+
desc: "Screen/log"
|
|
272
|
+
}, {
|
|
273
|
+
key: "?,shift+/",
|
|
274
|
+
cmd: "cockpit.console.details",
|
|
275
|
+
desc: "Details"
|
|
276
|
+
}, {
|
|
277
|
+
key: "],l,right",
|
|
278
|
+
cmd: "cockpit.console.next",
|
|
279
|
+
desc: "Next"
|
|
280
|
+
}, {
|
|
281
|
+
key: "[,h,left",
|
|
282
|
+
cmd: "cockpit.console.prev",
|
|
283
|
+
desc: "Prev"
|
|
284
|
+
}, {
|
|
285
|
+
key: "n",
|
|
286
|
+
cmd: "cockpit.console.new",
|
|
287
|
+
desc: "New"
|
|
288
|
+
}, {
|
|
289
|
+
key: "j,down",
|
|
290
|
+
cmd: "cockpit.console.down",
|
|
291
|
+
desc: "Down"
|
|
292
|
+
}, {
|
|
293
|
+
key: "k,up",
|
|
294
|
+
cmd: "cockpit.console.up",
|
|
295
|
+
desc: "Up"
|
|
296
|
+
}, {
|
|
297
|
+
key: "shift+g,end",
|
|
298
|
+
cmd: "cockpit.console.bottom",
|
|
299
|
+
desc: "End"
|
|
300
|
+
}, {
|
|
301
|
+
key: "g,home",
|
|
302
|
+
cmd: "cockpit.console.top",
|
|
303
|
+
desc: "Top"
|
|
304
|
+
}, {
|
|
305
|
+
key: "q",
|
|
306
|
+
cmd: "cockpit.console.close",
|
|
307
|
+
desc: "Close"
|
|
308
|
+
}]
|
|
309
|
+
}));
|
|
310
|
+
const screenText = createMemo(() => tailLines(screen()?.text, bodyRows(), bodyCols()));
|
|
311
|
+
const details = createMemo(() => shell() ? detailRows(shell(), props.store.now(), bodyCols()) : []);
|
|
312
|
+
const bodyHeight = createMemo(() => {
|
|
313
|
+
if (typing()) return bodyRows();
|
|
314
|
+
const content = view() === "log" ? log().length : view() === "details" ? details().length : screenText().split("\n").length;
|
|
315
|
+
return Math.min(bodyRows(), Math.max(6, content));
|
|
316
|
+
});
|
|
317
|
+
const position = createMemo(() => {
|
|
318
|
+
const list = props.store.visible();
|
|
319
|
+
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}` : ""}` : "";
|
|
322
|
+
});
|
|
323
|
+
// Only the keys that do something for the selected shell.
|
|
324
|
+
const hint = createMemo(() => {
|
|
325
|
+
if (typing()) return "TYPING: keys go to the shell (ctrl+c included) · ctrl+] stop typing";
|
|
326
|
+
const next = view() === "log" ? "screen" : "log";
|
|
327
|
+
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(" · ");
|
|
331
|
+
});
|
|
332
|
+
return (() => {
|
|
333
|
+
var _el$ = _$createElement("box"),
|
|
334
|
+
_el$2 = _$createElement("box");
|
|
335
|
+
_$insertNode(_el$, _el$2);
|
|
336
|
+
_$setProp(_el$, "flexDirection", "column");
|
|
337
|
+
_$setProp(_el$, "paddingLeft", 2);
|
|
338
|
+
_$setProp(_el$, "paddingRight", 2);
|
|
339
|
+
_$setProp(_el$, "paddingBottom", 1);
|
|
340
|
+
_$setProp(_el$, "overflow", "hidden");
|
|
341
|
+
_$insert(_el$, _$createComponent(Show, {
|
|
342
|
+
get when() {
|
|
343
|
+
return shell();
|
|
344
|
+
},
|
|
345
|
+
get fallback() {
|
|
346
|
+
return (() => {
|
|
347
|
+
var _el$3 = _$createElement("box"),
|
|
348
|
+
_el$4 = _$createElement("text"),
|
|
349
|
+
_el$5 = _$createElement("b"),
|
|
350
|
+
_el$7 = _$createElement("text");
|
|
351
|
+
_$insertNode(_el$3, _el$4);
|
|
352
|
+
_$insertNode(_el$3, _el$7);
|
|
353
|
+
_$setProp(_el$3, "flexDirection", "column");
|
|
354
|
+
_$insertNode(_el$4, _el$5);
|
|
355
|
+
_$setProp(_el$4, "wrapMode", "none");
|
|
356
|
+
_$insertNode(_el$5, _$createTextNode(`No shells in this project`));
|
|
357
|
+
_$setProp(_el$7, "wrapMode", "none");
|
|
358
|
+
_$insert(_el$7, () => truncate("Press n to start one. The agent starts its own with shell_start.", bodyCols()));
|
|
359
|
+
_$effect(_p$ => {
|
|
360
|
+
var _v$ = theme().text,
|
|
361
|
+
_v$2 = theme().textMuted;
|
|
362
|
+
_v$ !== _p$.e && (_p$.e = _$setProp(_el$4, "fg", _v$, _p$.e));
|
|
363
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$7, "fg", _v$2, _p$.t));
|
|
364
|
+
return _p$;
|
|
365
|
+
}, {
|
|
366
|
+
e: undefined,
|
|
367
|
+
t: undefined
|
|
368
|
+
});
|
|
369
|
+
return _el$3;
|
|
370
|
+
})();
|
|
371
|
+
},
|
|
372
|
+
children: s => [(() => {
|
|
373
|
+
var _el$8 = _$createElement("box"),
|
|
374
|
+
_el$9 = _$createElement("text"),
|
|
375
|
+
_el$0 = _$createTextNode(` `),
|
|
376
|
+
_el$1 = _$createElement("b"),
|
|
377
|
+
_el$10 = _$createElement("box"),
|
|
378
|
+
_el$11 = _$createElement("text");
|
|
379
|
+
_$insertNode(_el$8, _el$9);
|
|
380
|
+
_$insertNode(_el$8, _el$10);
|
|
381
|
+
_$insertNode(_el$8, _el$11);
|
|
382
|
+
_$setProp(_el$8, "height", 1);
|
|
383
|
+
_$setProp(_el$8, "flexShrink", 0);
|
|
384
|
+
_$setProp(_el$8, "flexDirection", "row");
|
|
385
|
+
_$setProp(_el$8, "overflow", "hidden");
|
|
386
|
+
_$insert(_el$8, _$createComponent(Badge, {
|
|
387
|
+
get api() {
|
|
388
|
+
return props.api;
|
|
389
|
+
},
|
|
390
|
+
get shell() {
|
|
391
|
+
return s();
|
|
392
|
+
},
|
|
393
|
+
get frame() {
|
|
394
|
+
return props.store.frame();
|
|
395
|
+
}
|
|
396
|
+
}), _el$9);
|
|
397
|
+
_$insertNode(_el$9, _el$0);
|
|
398
|
+
_$insertNode(_el$9, _el$1);
|
|
399
|
+
_$setProp(_el$9, "wrapMode", "none");
|
|
400
|
+
_$setProp(_el$9, "flexShrink", 1);
|
|
401
|
+
_$insert(_el$1, () => truncate(s().title, Math.max(10, bodyCols() - 50)));
|
|
402
|
+
_$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(" · "));
|
|
406
|
+
_$effect(_p$ => {
|
|
407
|
+
var _v$3 = theme().text,
|
|
408
|
+
_v$4 = theme().textMuted;
|
|
409
|
+
_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));
|
|
411
|
+
return _p$;
|
|
412
|
+
}, {
|
|
413
|
+
e: undefined,
|
|
414
|
+
t: undefined
|
|
415
|
+
});
|
|
416
|
+
return _el$8;
|
|
417
|
+
})(), _$createComponent(For, {
|
|
418
|
+
get each() {
|
|
419
|
+
return commandLines();
|
|
420
|
+
},
|
|
421
|
+
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);
|
|
429
|
+
_$effect(_p$ => {
|
|
430
|
+
var _v$7 = theme().text,
|
|
431
|
+
_v$8 = {
|
|
432
|
+
fg: theme().textMuted
|
|
433
|
+
};
|
|
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));
|
|
436
|
+
return _p$;
|
|
437
|
+
}, {
|
|
438
|
+
e: undefined,
|
|
439
|
+
t: undefined
|
|
440
|
+
});
|
|
441
|
+
return _el$17;
|
|
442
|
+
})()
|
|
443
|
+
}), _$createComponent(Show, {
|
|
444
|
+
get when() {
|
|
445
|
+
return folder();
|
|
446
|
+
},
|
|
447
|
+
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;
|
|
454
|
+
}
|
|
455
|
+
}), _$createComponent(Show, {
|
|
456
|
+
get when() {
|
|
457
|
+
return failure();
|
|
458
|
+
},
|
|
459
|
+
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;
|
|
466
|
+
})()
|
|
467
|
+
}), (() => {
|
|
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, {
|
|
476
|
+
get when() {
|
|
477
|
+
return view() === "screen";
|
|
478
|
+
},
|
|
479
|
+
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;
|
|
485
|
+
}
|
|
486
|
+
}), null);
|
|
487
|
+
_$insert(_el$13, _$createComponent(Show, {
|
|
488
|
+
get when() {
|
|
489
|
+
return view() === "details";
|
|
490
|
+
},
|
|
491
|
+
get children() {
|
|
492
|
+
var _el$15 = _$createElement("box");
|
|
493
|
+
_$setProp(_el$15, "flexDirection", "column");
|
|
494
|
+
_$insert(_el$15, _$createComponent(For, {
|
|
495
|
+
get each() {
|
|
496
|
+
return details();
|
|
497
|
+
},
|
|
498
|
+
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);
|
|
505
|
+
_$effect(_p$ => {
|
|
506
|
+
var _v$9 = theme().text,
|
|
507
|
+
_v$0 = {
|
|
508
|
+
fg: theme().textMuted
|
|
509
|
+
};
|
|
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));
|
|
512
|
+
return _p$;
|
|
513
|
+
}, {
|
|
514
|
+
e: undefined,
|
|
515
|
+
t: undefined
|
|
516
|
+
});
|
|
517
|
+
return _el$20;
|
|
518
|
+
})()
|
|
519
|
+
}));
|
|
520
|
+
return _el$15;
|
|
521
|
+
}
|
|
522
|
+
}), null);
|
|
523
|
+
_$insert(_el$13, _$createComponent(Show, {
|
|
524
|
+
get when() {
|
|
525
|
+
return view() === "log";
|
|
526
|
+
},
|
|
527
|
+
get children() {
|
|
528
|
+
var _el$16 = _$createElement("scrollbox");
|
|
529
|
+
_$use(el => {
|
|
530
|
+
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", {
|
|
536
|
+
visible: false
|
|
537
|
+
});
|
|
538
|
+
_$setProp(_el$16, "horizontalScrollbarOptions", {
|
|
539
|
+
visible: false
|
|
540
|
+
});
|
|
541
|
+
_$insert(_el$16, _$createComponent(For, {
|
|
542
|
+
get each() {
|
|
543
|
+
return log();
|
|
544
|
+
},
|
|
545
|
+
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);
|
|
554
|
+
_$effect(_p$ => {
|
|
555
|
+
var _v$1 = theme().text,
|
|
556
|
+
_v$10 = {
|
|
557
|
+
fg: theme().textMuted
|
|
558
|
+
};
|
|
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));
|
|
561
|
+
return _p$;
|
|
562
|
+
}, {
|
|
563
|
+
e: undefined,
|
|
564
|
+
t: undefined
|
|
565
|
+
});
|
|
566
|
+
return _el$22;
|
|
567
|
+
})()
|
|
568
|
+
}));
|
|
569
|
+
return _el$16;
|
|
570
|
+
}
|
|
571
|
+
}), null);
|
|
572
|
+
_$effect(_p$ => {
|
|
573
|
+
var _v$5 = bodyHeight(),
|
|
574
|
+
_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));
|
|
577
|
+
return _p$;
|
|
578
|
+
}, {
|
|
579
|
+
e: undefined,
|
|
580
|
+
t: undefined
|
|
581
|
+
});
|
|
582
|
+
return _el$13;
|
|
583
|
+
})()]
|
|
584
|
+
}), _el$2);
|
|
585
|
+
_$setProp(_el$2, "height", 1);
|
|
586
|
+
_$setProp(_el$2, "flexShrink", 0);
|
|
587
|
+
_$setProp(_el$2, "flexDirection", "row");
|
|
588
|
+
_$setProp(_el$2, "overflow", "hidden");
|
|
589
|
+
_$insert(_el$2, _$createComponent(Show, {
|
|
590
|
+
get when() {
|
|
591
|
+
return notice();
|
|
592
|
+
},
|
|
593
|
+
get fallback() {
|
|
594
|
+
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;
|
|
600
|
+
})();
|
|
601
|
+
},
|
|
602
|
+
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;
|
|
608
|
+
})()
|
|
609
|
+
}));
|
|
610
|
+
return _el$;
|
|
611
|
+
})();
|
|
612
|
+
}
|
|
613
|
+
|
|
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
|
+
}
|