@wierdbytes/pi-facelift 0.1.0 → 0.1.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.
- package/bash-rendering.test.ts +165 -0
- package/index.ts +113 -6
- package/package.json +1 -1
- package/scripts/demo.ts +51 -0
package/bash-rendering.test.ts
CHANGED
|
@@ -166,4 +166,169 @@ describe("bash renderCall expansion", () => {
|
|
|
166
166
|
}
|
|
167
167
|
});
|
|
168
168
|
});
|
|
169
|
+
|
|
170
|
+
it("keeps trailing dashes on the first line for multi-line commands", () => {
|
|
171
|
+
withStdoutColumns(120, () => {
|
|
172
|
+
const bashTool = loadBashTool();
|
|
173
|
+
// User-typed multi-line command (line continuation with `\`)
|
|
174
|
+
const command = `cd /tmp && \\\n echo "hello"`;
|
|
175
|
+
|
|
176
|
+
const rendered = bashTool.renderCall({ command }, mockTheme, {
|
|
177
|
+
lastComponent: new MockText(),
|
|
178
|
+
isError: false,
|
|
179
|
+
state: {},
|
|
180
|
+
expanded: true,
|
|
181
|
+
invalidate: () => {},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const lines = rendered.getText().split("\n");
|
|
185
|
+
expect(lines.length).toBeGreaterThanOrEqual(2);
|
|
186
|
+
|
|
187
|
+
// First line: top border with `╭── bash cd /tmp && \` and trailing dashes.
|
|
188
|
+
expect(lines[0]).toMatch(/^.*╭──.*bash.*cd \/tmp && \\.*─/);
|
|
189
|
+
expect(lines[0]).toContain("\\");
|
|
190
|
+
expect(lines[0]).toMatch(/─{2,}/); // trailing dashes survive on first line
|
|
191
|
+
|
|
192
|
+
// Continuation line: outer rail + sub-tree connector + content (no trailing dashes).
|
|
193
|
+
const stripAnsi = (s: string) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
194
|
+
const plainCont = stripAnsi(lines[1]);
|
|
195
|
+
expect(plainCont).toMatch(/^│/);
|
|
196
|
+
expect(plainCont).toMatch(/[│╰] /); // sub-tree connector before content
|
|
197
|
+
expect(plainCont).toContain("echo");
|
|
198
|
+
expect(plainCont).not.toMatch(/─{2,}/);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("renders multi-line commands as a sub-tree with `│`/`╰` connectors", () => {
|
|
203
|
+
withStdoutColumns(200, () => {
|
|
204
|
+
const bashTool = loadBashTool();
|
|
205
|
+
const command = `cd /tmp && \\\n echo "line 1" \\\n echo "line 2"`;
|
|
206
|
+
|
|
207
|
+
const rendered = bashTool.renderCall({ command }, mockTheme, {
|
|
208
|
+
lastComponent: new MockText(),
|
|
209
|
+
isError: false,
|
|
210
|
+
state: {},
|
|
211
|
+
expanded: true,
|
|
212
|
+
invalidate: () => {},
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const stripAnsi = (s: string) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
216
|
+
const plain = rendered.getText().split("\n").map(stripAnsi);
|
|
217
|
+
|
|
218
|
+
expect(plain.length).toBe(3);
|
|
219
|
+
// Non-last continuation row uses `│`.
|
|
220
|
+
expect(plain[1]).toMatch(/^│ +│ echo "line 1" \\$/);
|
|
221
|
+
// Last continuation row uses `╰`.
|
|
222
|
+
expect(plain[2]).toMatch(/^│ +╰ echo "line 2"$/);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("aligns sub-tree continuations under the first arg, ignoring heredoc indent", () => {
|
|
227
|
+
withStdoutColumns(120, () => {
|
|
228
|
+
const bashTool = loadBashTool();
|
|
229
|
+
// Continuation has 4 leading spaces — those should be stripped so the
|
|
230
|
+
// sub-tree connector aligns the content under the first row's first arg.
|
|
231
|
+
const command = "cd /tmp && \\\n echo hi";
|
|
232
|
+
|
|
233
|
+
const rendered = bashTool.renderCall({ command }, mockTheme, {
|
|
234
|
+
lastComponent: new MockText(),
|
|
235
|
+
isError: false,
|
|
236
|
+
state: {},
|
|
237
|
+
expanded: true,
|
|
238
|
+
invalidate: () => {},
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const stripAnsi = (s: string) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
242
|
+
const [firstLine, secondLine] = rendered.getText().split("\n").map(stripAnsi);
|
|
243
|
+
|
|
244
|
+
// `╭── bash ` is 9 chars, so `c` of `cd` is at col 10 (0-indexed: 9).
|
|
245
|
+
const firstArgCol = firstLine.indexOf("cd /tmp");
|
|
246
|
+
expect(firstArgCol).toBe(9);
|
|
247
|
+
|
|
248
|
+
// Continuation: `│` (col 0) + 6 spaces + connector (col 7) + space (col 8) + content (col 9).
|
|
249
|
+
// Content col must equal the first row's first arg col, regardless of
|
|
250
|
+
// the user's heredoc indent (4 leading spaces above are stripped).
|
|
251
|
+
const echoCol = secondLine.indexOf("echo");
|
|
252
|
+
expect(echoCol).toBe(firstArgCol);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("colors continuation lines with the same accent as the first arg", () => {
|
|
257
|
+
withStdoutColumns(120, () => {
|
|
258
|
+
const bashTool = loadBashTool();
|
|
259
|
+
const accentTheme = {
|
|
260
|
+
fg: (key: string, text: string) => (key === "accent" ? `\x1b[36m${text}\x1b[0m` : `\x1b[31m${text}\x1b[0m`),
|
|
261
|
+
bold: (text: string) => `\x1b[1m${text}\x1b[22m`,
|
|
262
|
+
};
|
|
263
|
+
const command = "cd /tmp && \\\n echo hi";
|
|
264
|
+
|
|
265
|
+
const rendered = bashTool.renderCall({ command }, accentTheme, {
|
|
266
|
+
lastComponent: new MockText(),
|
|
267
|
+
isError: false,
|
|
268
|
+
state: {},
|
|
269
|
+
expanded: true,
|
|
270
|
+
invalidate: () => {},
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const lines = rendered.getText().split("\n");
|
|
274
|
+
// First-row args are wrapped in the accent open code (`\x1b[36m`); the
|
|
275
|
+
// continuation row must independently carry the same accent wrap so the
|
|
276
|
+
// rendered text doesn't fall back to the terminal's default fg.
|
|
277
|
+
expect(lines[0]).toContain("\x1b[36mcd /tmp");
|
|
278
|
+
expect(lines[1]).toContain("\x1b[36mecho hi");
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe("bash renderResult body rails", () => {
|
|
284
|
+
it("keeps the left rail on lines that contain a `\\r` (terminal-style overwrite)", () => {
|
|
285
|
+
withStdoutColumns(120, () => {
|
|
286
|
+
const bashTool = loadBashTool();
|
|
287
|
+
|
|
288
|
+
// Simulates `git rebase` output, which prints `Rebasing (1/1)\rSuccessfully …`
|
|
289
|
+
const text = [
|
|
290
|
+
"=== before ===",
|
|
291
|
+
"abc1234 some change",
|
|
292
|
+
"Rebasing (1/1)\rSuccessfully rebased and updated refs/heads/master.",
|
|
293
|
+
"=== after ===",
|
|
294
|
+
].join("\n");
|
|
295
|
+
|
|
296
|
+
const result = {
|
|
297
|
+
content: [{ type: "text", text }],
|
|
298
|
+
details: { _type: "bashResult", text, exitCode: 0, command: "git pull --rebase" },
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const rendered = bashTool.renderResult(
|
|
302
|
+
result as any,
|
|
303
|
+
{ isPartial: false } as any,
|
|
304
|
+
mockTheme,
|
|
305
|
+
{
|
|
306
|
+
lastComponent: new MockText(),
|
|
307
|
+
isError: false,
|
|
308
|
+
state: {},
|
|
309
|
+
expanded: true,
|
|
310
|
+
invalidate: () => {},
|
|
311
|
+
} as any,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const out = rendered.getText() as string;
|
|
315
|
+
const stripAnsi = (s: string) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
316
|
+
const lines = out.split("\n").map(stripAnsi);
|
|
317
|
+
|
|
318
|
+
// Find the line that ends with the success message — it must be rail-prefixed,
|
|
319
|
+
// not raw `Successfully…` (which would mean the rail got clobbered).
|
|
320
|
+
const successLine = lines.find((l) => l.includes("Successfully rebased"));
|
|
321
|
+
expect(successLine).toBeDefined();
|
|
322
|
+
expect(successLine!.startsWith("│")).toBe(true);
|
|
323
|
+
// `\r` and the discarded progress prefix are stripped from the rendered line.
|
|
324
|
+
expect(successLine!).not.toContain("\r");
|
|
325
|
+
expect(successLine!).not.toContain("Rebasing (1/1)");
|
|
326
|
+
|
|
327
|
+
// Sanity check: every body line keeps a rail prefix.
|
|
328
|
+
const bodyLines = lines.filter((l) => !l.startsWith("╭") && !l.startsWith("╰") && l.length > 0);
|
|
329
|
+
for (const line of bodyLines) {
|
|
330
|
+
expect(line.startsWith("│")).toBe(true);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
});
|
|
169
334
|
});
|
package/index.ts
CHANGED
|
@@ -250,27 +250,112 @@ function bodyW(): number {
|
|
|
250
250
|
return Math.max(1, termW() - 1);
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Find the visible column of the first space character in an ANSI-formatted
|
|
255
|
+
* string, ignoring ANSI escape sequences. Returns -1 if no space is found
|
|
256
|
+
* before a newline (or end of string).
|
|
257
|
+
*
|
|
258
|
+
* Used by `frameTop` to align continuation rows under the first argument of
|
|
259
|
+
* the tool title (e.g., the column where `cd` starts in `bash cd …`).
|
|
260
|
+
*/
|
|
261
|
+
function firstVisibleSpace(s: string): number {
|
|
262
|
+
let visiblePos = 0;
|
|
263
|
+
let i = 0;
|
|
264
|
+
while (i < s.length) {
|
|
265
|
+
const ch = s[i];
|
|
266
|
+
if (ch === "\x1b") {
|
|
267
|
+
const next = s[i + 1];
|
|
268
|
+
if (next === "[") {
|
|
269
|
+
const end = s.indexOf("m", i + 2);
|
|
270
|
+
if (end < 0) return -1;
|
|
271
|
+
i = end + 1;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
// Unknown escape — bail out
|
|
275
|
+
return -1;
|
|
276
|
+
}
|
|
277
|
+
if (ch === "\n") return -1;
|
|
278
|
+
if (ch === " ") return visiblePos;
|
|
279
|
+
visiblePos += 1;
|
|
280
|
+
i += 1;
|
|
281
|
+
}
|
|
282
|
+
return -1;
|
|
283
|
+
}
|
|
284
|
+
|
|
253
285
|
/**
|
|
254
286
|
* Render the top border of the frame: `╭── <title> ─────`.
|
|
255
287
|
* The title may contain ANSI codes; visible width is measured with pi-tui.
|
|
256
288
|
* Truncated with `…` if it would overflow the terminal width.
|
|
289
|
+
*
|
|
290
|
+
* Multi-line titles (e.g., a multi-line bash command) are rendered with the
|
|
291
|
+
* first line as the top border and remaining lines as rail-prefixed
|
|
292
|
+
* continuation rows in a sub-tree:
|
|
293
|
+
*
|
|
294
|
+
* ╭── bash cd /…/pi-wierd-stuff && \ ──────────────
|
|
295
|
+
* │ │ echo "line 1" \
|
|
296
|
+
* │ ╚ echo "line 2"
|
|
297
|
+
*
|
|
298
|
+
* Each continuation row is composed of:
|
|
299
|
+
* • the outer frame rail `│` (frame status color)
|
|
300
|
+
* • padding sized so the sub-tree connector lines up with the first arg
|
|
301
|
+
* character of the first row
|
|
302
|
+
* • a sub-tree connector (`│` for non-last rows, `╰` for the last)
|
|
303
|
+
* • a single separator space
|
|
304
|
+
* • the row's content (callers are expected to wrap each line in the
|
|
305
|
+
* same color as the first-row args so the sub-tree text matches)
|
|
306
|
+
*
|
|
307
|
+
* Trailing dashes are only emitted on the first row so continuation rows
|
|
308
|
+
* stay clean.
|
|
257
309
|
*/
|
|
258
310
|
function frameTop(titleAnsi: string, status: FrameStatus): string {
|
|
259
311
|
const color = getFrameColor(status);
|
|
260
312
|
const w = termW();
|
|
313
|
+
const titleLines = titleAnsi.split("\n");
|
|
314
|
+
const firstTitle = titleLines[0];
|
|
315
|
+
const continuations = titleLines.slice(1);
|
|
316
|
+
|
|
261
317
|
// Layout: `╭` (1) + `──` (2) + ` ` (1) + title + ` ` (1) + trailing dashes (≥1)
|
|
262
318
|
const minTrailing = 1;
|
|
263
319
|
const fixed = 1 + 2 + 1 + 1 + minTrailing;
|
|
264
320
|
const maxTitleW = Math.max(0, w - fixed);
|
|
265
321
|
|
|
266
|
-
let title =
|
|
267
|
-
let tw = visibleWidth(
|
|
322
|
+
let title = firstTitle;
|
|
323
|
+
let tw = visibleWidth(firstTitle);
|
|
268
324
|
if (tw > maxTitleW) {
|
|
269
|
-
title = truncateToWidth(
|
|
325
|
+
title = truncateToWidth(firstTitle, maxTitleW, "…");
|
|
270
326
|
tw = visibleWidth(title);
|
|
271
327
|
}
|
|
272
328
|
const trailing = Math.max(minTrailing, w - 1 - 2 - 1 - tw - 1);
|
|
273
|
-
|
|
329
|
+
const firstLine = `${color}╭──${RST} ${title} ${color}${"─".repeat(trailing)}${RST}`;
|
|
330
|
+
|
|
331
|
+
if (continuations.length === 0) return firstLine;
|
|
332
|
+
|
|
333
|
+
// Continuation rows: rail + padding + sub-tree connector + space + content.
|
|
334
|
+
// First line layout:
|
|
335
|
+
// col 1: `╭`
|
|
336
|
+
// cols 2-3: `──`
|
|
337
|
+
// col 4: literal space
|
|
338
|
+
// cols 5..: title (toolName + space + args…)
|
|
339
|
+
// If the toolName ends at visible-offset N (i.e., the space is at offset N),
|
|
340
|
+
// the first arg char sits at column N + 6 (1-indexed). We align the sub-tree
|
|
341
|
+
// connector two columns before it (at contentCol - 2) and put the content
|
|
342
|
+
// itself at the same column as the first row's first arg char, so the rows
|
|
343
|
+
// read as a tree hanging off the title.
|
|
344
|
+
const toolNameEnd = firstVisibleSpace(firstTitle);
|
|
345
|
+
const contentCol = toolNameEnd >= 0 ? toolNameEnd + 6 : 6;
|
|
346
|
+
const padBetween = " ".repeat(Math.max(0, contentCol - 4));
|
|
347
|
+
const rail = `${color}│${RST}`;
|
|
348
|
+
const innerW = Math.max(1, w - contentCol + 1);
|
|
349
|
+
const lastIdx = continuations.length - 1;
|
|
350
|
+
|
|
351
|
+
const contLines = continuations.map((line, idx) => {
|
|
352
|
+
const connectorChar = idx === lastIdx ? "╰" : "│";
|
|
353
|
+
const connector = `${color}${connectorChar}${RST}`;
|
|
354
|
+
const fitted = visibleWidth(line) > innerW ? truncateToWidth(line, innerW, "…") : line;
|
|
355
|
+
return `${rail}${padBetween}${connector} ${fitted}`;
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
return [firstLine, ...contLines].join("\n");
|
|
274
359
|
}
|
|
275
360
|
|
|
276
361
|
/** Render the bottom border of the frame: `╰─────────────`. */
|
|
@@ -306,6 +391,13 @@ function frameBottomWithLabel(labelAnsi: string, status: FrameStatus): string {
|
|
|
306
391
|
* Prefix every line in `text` with the frame's left rail (`│`).
|
|
307
392
|
* Each logical line is right-truncated to `bodyW()` so the visible width
|
|
308
393
|
* including the rail never exceeds `termW()`.
|
|
394
|
+
*
|
|
395
|
+
* Embedded carriage returns (`\r`) are collapsed using terminal-style
|
|
396
|
+
* overwrite semantics: only the content after the last `\r` on a line is
|
|
397
|
+
* kept. Without this, sequences like `git rebase`'s
|
|
398
|
+
* `Rebasing (1/1)\rSuccessfully rebased…` would clobber the rail when the
|
|
399
|
+
* terminal honors `\r` as cursor-reset, leaving the line visibly
|
|
400
|
+
* unprefixed.
|
|
309
401
|
*/
|
|
310
402
|
function frameBodyLines(text: string, status: FrameStatus): string {
|
|
311
403
|
const color = getFrameColor(status);
|
|
@@ -314,7 +406,11 @@ function frameBodyLines(text: string, status: FrameStatus): string {
|
|
|
314
406
|
return text
|
|
315
407
|
.split("\n")
|
|
316
408
|
.map((line) => {
|
|
317
|
-
|
|
409
|
+
// Terminal-style \r handling: keep only what survives after the last
|
|
410
|
+
// carriage return so the rail isn't overwritten by progress messages.
|
|
411
|
+
const lastCR = line.lastIndexOf("\r");
|
|
412
|
+
const safeLine = lastCR >= 0 ? line.slice(lastCR + 1) : line;
|
|
413
|
+
const fitted = safeLine && visibleWidth(safeLine) > w ? truncateToWidth(safeLine, w, "") : safeLine;
|
|
318
414
|
return `${rail}${fitted}`;
|
|
319
415
|
})
|
|
320
416
|
.join("\n");
|
|
@@ -1226,7 +1322,18 @@ export default function piFaceliftExtension(pi: PiFaceliftApi, deps?: PiFacelift
|
|
|
1226
1322
|
}
|
|
1227
1323
|
const timeout = args.timeout ? ` ${theme.fg("muted", `(${args.timeout}s timeout)`)}` : "";
|
|
1228
1324
|
const displayCmd = ctx.expanded || cmd.length <= 80 ? cmd : `${cmd.slice(0, 77)}…`;
|
|
1229
|
-
|
|
1325
|
+
|
|
1326
|
+
// Multi-line bash commands (shell line continuations with `\`, here-docs,
|
|
1327
|
+
// or embedded newlines) need each line wrapped in the accent color
|
|
1328
|
+
// separately so the color survives line splitting in `frameTop`. Leading
|
|
1329
|
+
// whitespace on continuation lines is dropped — the sub-tree connector
|
|
1330
|
+
// already provides visual indentation, so the user's heredoc indent
|
|
1331
|
+
// would just push content right and misalign the tree.
|
|
1332
|
+
const cmdLines = displayCmd.split("\n");
|
|
1333
|
+
const firstCmd = cmdLines[0];
|
|
1334
|
+
const restCmd = cmdLines.slice(1).map((line) => line.replace(/^\s+/, ""));
|
|
1335
|
+
const firstTitle = `${theme.fg("toolTitle", theme.bold("bash"))} ${theme.fg("accent", firstCmd)}${timeout}`;
|
|
1336
|
+
const title = [firstTitle, ...restCmd.map((line) => theme.fg("accent", line))].join("\n");
|
|
1230
1337
|
t.setText(frameTop(title, getFrameStatus(ctx)));
|
|
1231
1338
|
return t;
|
|
1232
1339
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wierdbytes/pi-facelift",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Cosmetic facelift for built-in pi tool output — open-right rounded frames, syntax-highlighted reads, status-aware bash summaries, find/grep.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
package/scripts/demo.ts
CHANGED
|
@@ -260,6 +260,56 @@ async function demoBashTimeout(): Promise<void> {
|
|
|
260
260
|
);
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
async function demoBashMultiline(): Promise<void> {
|
|
264
|
+
header("bash multi-line command — sub-tree title + \\r-safe body rails");
|
|
265
|
+
// Multi-line shell command (line continuations with `\`). The title
|
|
266
|
+
// renders as a sub-tree with `│`/`╰` connectors aligned under the first
|
|
267
|
+
// arg of the first row; continuation lines stay in the same accent
|
|
268
|
+
// color as the first row.
|
|
269
|
+
const command = [
|
|
270
|
+
"cd /Users/mentor/me/dev/pi-wierd-stuff && \\",
|
|
271
|
+
' echo "=== before ===" && \\',
|
|
272
|
+
" git log --oneline -3 origin/master && \\",
|
|
273
|
+
" git pull --rebase && \\",
|
|
274
|
+
' echo "=== after ==="',
|
|
275
|
+
].join("\n");
|
|
276
|
+
|
|
277
|
+
// Body intentionally contains `Rebasing (1/1)\rSuccessfully rebased…`,
|
|
278
|
+
// the exact pattern `git rebase` writes to stderr. Without terminal-style
|
|
279
|
+
// `\r` handling, the embedded carriage return would clobber the `│` rail
|
|
280
|
+
// when the terminal honors `\r` as cursor-reset.
|
|
281
|
+
const bodyText = [
|
|
282
|
+
"=== before ===",
|
|
283
|
+
"981c12d facelift: add @wierdbytes/pi-facelift v0.1.0",
|
|
284
|
+
"4517d9c README: fix stale npm package names",
|
|
285
|
+
"046a5c2 web: rename slash command from /wierd-web to /web, bump to 0.3.1",
|
|
286
|
+
"Rebasing (1/1)\rSuccessfully rebased and updated refs/heads/master.",
|
|
287
|
+
"=== after ===",
|
|
288
|
+
"8770a5e facelift: add @wierdbytes/pi-facelift v0.1.0",
|
|
289
|
+
"046a5c2 web: rename slash command from /wierd-web to /web, bump to 0.3.1",
|
|
290
|
+
"f87cbcf voice: rename slash command from /wierd-voice to /voice, bump to 0.4.1",
|
|
291
|
+
].join("\n");
|
|
292
|
+
|
|
293
|
+
const tools = loadTools({
|
|
294
|
+
bash: async () => ({
|
|
295
|
+
content: [{ type: "text", text: bodyText }],
|
|
296
|
+
details: { _type: "bashResult", text: bodyText, exitCode: 0, command },
|
|
297
|
+
}),
|
|
298
|
+
});
|
|
299
|
+
const bash = tools.get("bash");
|
|
300
|
+
const state: { startedAt?: number; endedAt?: number } = { startedAt: Date.now() - 1200 };
|
|
301
|
+
|
|
302
|
+
const callCtx = makeCtx({ state, expanded: true });
|
|
303
|
+
console.log(bash.renderCall({ command }, theme, callCtx).getText());
|
|
304
|
+
|
|
305
|
+
const result = await bash.execute("t1", { command }, undefined, undefined, {});
|
|
306
|
+
state.endedAt = Date.now();
|
|
307
|
+
const resCtx = makeCtx({ state, expanded: true });
|
|
308
|
+
console.log(
|
|
309
|
+
bash.renderResult(result, { isPartial: false, expanded: true }, theme, resCtx).getText(),
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
263
313
|
async function demoLs(): Promise<void> {
|
|
264
314
|
header("ls — tree view with Nerd Font icons");
|
|
265
315
|
const tools = loadTools({
|
|
@@ -344,6 +394,7 @@ async function main(): Promise<void> {
|
|
|
344
394
|
await demoBashError();
|
|
345
395
|
await demoBashStreaming();
|
|
346
396
|
await demoBashTimeout();
|
|
397
|
+
await demoBashMultiline();
|
|
347
398
|
await demoLs();
|
|
348
399
|
await demoFind();
|
|
349
400
|
await demoGrep();
|