Skip to content

Commit 0282de7

Browse files
igorcostaAutohand Evolve
andcommitted
Render live shell output in a compact body
Co-authored-by: Autohand Evolve <code-noreply@autohand.ai>
1 parent 18e07ee commit 0282de7

2 files changed

Lines changed: 98 additions & 17 deletions

File tree

src/ui/ink/ToolOutput.tsx

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export interface LiveCommandEntry {
2828
isExpanded: boolean;
2929
}
3030

31-
const LIVE_COMMAND_COLLAPSED_LINES = 12;
31+
const LIVE_COMMAND_COLLAPSED_LINES = 5;
3232

3333
function getVisibleTail(text: string, maxLines: number): { lines: string[]; hiddenLineCount: number } {
3434
const normalized = text.trimEnd();
@@ -47,6 +47,51 @@ function getVisibleTail(text: string, maxLines: number): { lines: string[]; hidd
4747
};
4848
}
4949

50+
function getLines(text: string): string[] {
51+
const normalized = text.trimEnd();
52+
return normalized ? normalized.split('\n') : [];
53+
}
54+
55+
function getCollapsedLiveCommandViews(
56+
stdout: string,
57+
stderr: string,
58+
maxLines: number
59+
): {
60+
stdoutView: { lines: string[]; hiddenLineCount: number };
61+
stderrView: { lines: string[]; hiddenLineCount: number };
62+
} {
63+
const stdoutLines = getLines(stdout);
64+
const stderrLines = getLines(stderr);
65+
66+
if (stdoutLines.length === 0) {
67+
return {
68+
stdoutView: { lines: [], hiddenLineCount: 0 },
69+
stderrView: getVisibleTail(stderr, maxLines),
70+
};
71+
}
72+
73+
if (stderrLines.length === 0) {
74+
return {
75+
stdoutView: getVisibleTail(stdout, maxLines),
76+
stderrView: { lines: [], hiddenLineCount: 0 },
77+
};
78+
}
79+
80+
const stderrBudget = Math.min(stderrLines.length, Math.max(1, Math.floor(maxLines / 3)));
81+
const stdoutBudget = Math.max(0, maxLines - stderrBudget);
82+
83+
return {
84+
stdoutView: {
85+
lines: stdoutBudget > 0 ? stdoutLines.slice(-stdoutBudget) : [],
86+
hiddenLineCount: Math.max(0, stdoutLines.length - stdoutBudget),
87+
},
88+
stderrView: {
89+
lines: stderrLines.slice(-stderrBudget),
90+
hiddenLineCount: Math.max(0, stderrLines.length - stderrBudget),
91+
},
92+
};
93+
}
94+
5095
/** A single tool call within a batch group */
5196
export interface BatchToolItem {
5297
tool: string;
@@ -242,14 +287,15 @@ export function ToolOutputList({ entries, maxVisible = 50 }: ToolOutputListProps
242287

243288
export function LiveCommandBlock({ entry }: { entry: LiveCommandEntry }) {
244289
const { colors } = useTheme();
245-
const stdoutView = entry.isExpanded
246-
? { lines: entry.stdout.trimEnd() ? entry.stdout.trimEnd().split('\n') : [], hiddenLineCount: 0 }
247-
: getVisibleTail(entry.stdout, LIVE_COMMAND_COLLAPSED_LINES);
248-
const stderrView = entry.isExpanded
249-
? { lines: entry.stderr.trimEnd() ? entry.stderr.trimEnd().split('\n') : [], hiddenLineCount: 0 }
250-
: getVisibleTail(entry.stderr, Math.max(4, Math.floor(LIVE_COMMAND_COLLAPSED_LINES / 3)));
290+
const { stdoutView, stderrView } = entry.isExpanded
291+
? {
292+
stdoutView: { lines: getLines(entry.stdout), hiddenLineCount: 0 },
293+
stderrView: { lines: getLines(entry.stderr), hiddenLineCount: 0 },
294+
}
295+
: getCollapsedLiveCommandViews(entry.stdout, entry.stderr, LIVE_COMMAND_COLLAPSED_LINES);
251296
const hiddenLineCount = stdoutView.hiddenLineCount + stderrView.hiddenLineCount;
252297
const hint = entry.isExpanded ? 'Ctrl+O collapse' : 'Ctrl+O expand';
298+
const hasVisibleOutput = stdoutView.lines.length > 0 || stderrView.lines.length > 0;
253299

254300
return (
255301
<Box flexDirection="column" marginBottom={1}>
@@ -262,15 +308,23 @@ export function LiveCommandBlock({ entry }: { entry: LiveCommandEntry }) {
262308
) : (
263309
<Text color={colors.muted}>{hint}</Text>
264310
)}
265-
{stdoutView.lines.length > 0 ? (
266-
<Text color={colors.toolOutput}>{renderTerminalMarkdown(stdoutView.lines.join('\n'))}</Text>
267-
) : null}
268-
{stderrView.lines.length > 0 ? (
269-
<Box flexDirection="column">
270-
<Text color={colors.error}>stderr</Text>
271-
<Text color={colors.error}>{renderTerminalMarkdown(stderrView.lines.join('\n'))}</Text>
272-
</Box>
273-
) : null}
311+
<Box flexDirection="column" borderStyle="single" borderColor={colors.borderMuted} paddingX={1}>
312+
{hasVisibleOutput ? (
313+
<>
314+
{stdoutView.lines.length > 0 ? (
315+
<Text color={colors.toolOutput}>{renderTerminalMarkdown(stdoutView.lines.join('\n'))}</Text>
316+
) : null}
317+
{stderrView.lines.length > 0 ? (
318+
<Box flexDirection="column">
319+
<Text color={colors.error}>stderr</Text>
320+
<Text color={colors.error}>{renderTerminalMarkdown(stderrView.lines.join('\n'))}</Text>
321+
</Box>
322+
) : null}
323+
</>
324+
) : (
325+
<Text color={colors.muted}>No output yet</Text>
326+
)}
327+
</Box>
274328
</Box>
275329
);
276330
}

tests/ui/ink/LiveCommandBlock.test.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,37 @@ describe('AgentUI live command block', () => {
143143

144144
const output = stripAnsi(lastFrame());
145145
expect(output).toContain('line 16');
146-
expect(output).not.toContain('line 4');
146+
expect(output).toContain('line 12');
147+
expect(output).not.toContain('line 11');
147148
expect(output).toContain('Ctrl+O expand');
148149
});
149150

151+
it('renders an empty live command body while waiting for output', () => {
152+
const entry = {
153+
id: 'cmd-1',
154+
command: '! node --check tetris.js',
155+
stdout: '',
156+
stderr: '',
157+
startedAt: Date.now(),
158+
isExpanded: false,
159+
};
160+
161+
const { lastFrame } = render(
162+
<I18nProvider>
163+
<ThemeProvider>
164+
<LiveCommandBlock entry={entry} />
165+
</ThemeProvider>
166+
</I18nProvider>
167+
);
168+
169+
const output = stripAnsi(lastFrame());
170+
expect(output).toContain('Running ! node --check tetris.js');
171+
expect(output).toContain('No output yet');
172+
expect(output).toContain('Ctrl+O expand');
173+
expect(output).toContain('┌');
174+
expect(output).toContain('└');
175+
});
176+
150177
it('shows full live command output when expanded', () => {
151178
const entry = {
152179
id: 'cmd-1',

0 commit comments

Comments
 (0)