feat(cli): implement dense tool rendering and UI refinements

- Add DenseToolMessage component for single-line tool output.
- Update ToolGroupMessage to support dense layout and conditional borders.
- Improve padding and layout in ShellToolMessage and ToolMessage.
- Add padding to ShowMoreLines and update UI snapshots.
This commit is contained in:
Jarrod Whelan
2026-02-10 00:41:37 -08:00
parent e88d4ffe97
commit 5f99cbe560
6 changed files with 332 additions and 273 deletions

View File

@@ -31,7 +31,7 @@ export const ShowMoreLines = ({ constrainHeight }: ShowMoreLinesProps) => {
}
return (
<Box>
<Box paddingX={1}>
<Text color={theme.text.secondary} wrap="truncate">
Press ctrl-o to show more lines
</Text>

View File

@@ -25,6 +25,7 @@ export const DenseToolMessage: React.FC<DenseToolMessageProps> = ({
description,
status,
resultDisplay,
outputFile,
}) => {
let denseResult: string | undefined;
@@ -54,22 +55,31 @@ export const DenseToolMessage: React.FC<DenseToolMessageProps> = ({
}
return (
<Box marginLeft={3} flexDirection="row" flexWrap="wrap">
<ToolStatusIndicator status={status} name={name} />
<Box maxWidth={25} flexShrink={1} flexGrow={0}>
<Text color={theme.text.primary} bold wrap="truncate-end">
{name}
</Text>
<Box flexDirection="column">
<Box marginLeft={3} flexDirection="row" flexWrap="wrap">
<ToolStatusIndicator status={status} name={name} />
<Box maxWidth={25} flexShrink={1} flexGrow={0}>
<Text color={theme.text.primary} bold wrap="truncate-end">
{name}
</Text>
</Box>
<Box marginLeft={1} flexShrink={1} flexGrow={0}>
<Text color={theme.text.secondary} wrap="truncate-end">
{description}
</Text>
</Box>
{denseResult && (
<Box marginLeft={1} flexGrow={1}>
<Text color={theme.text.accent} wrap="wrap">
{denseResult}
</Text>
</Box>
)}
</Box>
<Box marginLeft={1} flexShrink={1} flexGrow={0}>
<Text color={theme.text.secondary} wrap="truncate-end">
{description}
</Text>
</Box>
{denseResult && (
<Box marginLeft={1} flexGrow={1}>
<Text color={theme.text.accent} wrap="wrap">
{denseResult}
{outputFile && (
<Box marginLeft={6}>
<Text color={theme.text.secondary}>
(Output saved to: {outputFile})
</Text>
</Box>
)}

View File

@@ -167,7 +167,7 @@ export const ShellToolMessage: React.FC<ShellToolMessageProps> = ({
)}
/>
{isThisShellFocused && config && (
<Box paddingLeft={STATUS_INDICATOR_WIDTH} marginTop={1}>
<Box paddingLeft={STATUS_INDICATOR_WIDTH}>
<ShellInputPrompt
activeShellPtyId={activeShellPtyId ?? null}
focus={embeddedShellFocused}

View File

@@ -165,7 +165,7 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
*/
width={terminalWidth}
paddingRight={TOOL_MESSAGE_HORIZONTAL_MARGIN}
marginBottom={1}
marginBottom={borderBottomOverride === false ? 0 : 1}
>
{visibleToolCalls.map((tool, index) => {
const isConfirming = toolAwaitingApproval?.callId === tool.callId;
@@ -218,106 +218,113 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
) : (
<ToolMessage {...commonProps} />
)}
<Box
borderLeft={true}
borderRight={true}
borderTop={false}
borderBottom={false}
borderColor={borderColor}
borderDimColor={borderDimColor}
flexDirection="column"
borderStyle="round"
paddingLeft={1}
paddingRight={1}
>
{tool.status === ToolCallStatus.Confirming &&
isConfirming &&
tool.confirmationDetails && (
<ToolConfirmationMessage
callId={tool.callId}
confirmationDetails={tool.confirmationDetails}
config={config}
isFocused={isFocused}
availableTerminalHeight={
availableTerminalHeightPerToolMessage
}
terminalWidth={
contentWidth - TOOL_CONFIRMATION_INTERNAL_PADDING
}
/>
{((tool.status === ToolCallStatus.Confirming &&
isConfirming &&
tool.confirmationDetails) ||
tool.outputFile) && (
<Box
borderLeft={true}
borderRight={true}
borderTop={false}
borderBottom={false}
borderColor={borderColor}
borderDimColor={borderDimColor}
flexDirection="column"
borderStyle="round"
paddingLeft={1}
paddingRight={1}
>
{tool.status === ToolCallStatus.Confirming &&
isConfirming &&
tool.confirmationDetails && (
<ToolConfirmationMessage
callId={tool.callId}
confirmationDetails={tool.confirmationDetails}
config={config}
isFocused={isFocused}
availableTerminalHeight={
availableTerminalHeightPerToolMessage
}
terminalWidth={
contentWidth - TOOL_CONFIRMATION_INTERNAL_PADDING
}
/>
)}
{tool.outputFile && (
<Box>
<Text color={theme.text.primary}>
Output too long and was saved to: {tool.outputFile}
</Text>
</Box>
)}
{tool.outputFile && (
<Box>
<Text color={theme.text.primary}>
Output too long and was saved to: {tool.outputFile}
</Text>
</Box>
)}
</Box>
</Box>
)}
</Box>
);
})}
{
/*
We have to keep the bottom border separate so it doesn't get
drawn over by the sticky header directly inside it.
Only render if the last tool was displayed in full/verbose mode,
or if explicitly overridden.
*/
(() => {
if (visibleToolCalls.length === 0) {
if (borderBottomOverride !== undefined) {
return (
<Box
height={0}
width={contentWidth}
borderLeft={true}
borderRight={true}
borderTop={false}
borderBottom={borderBottomOverride}
borderColor={borderColor}
borderDimColor={borderDimColor}
borderStyle="round"
/>
);
}
return null;
}
const lastTool = visibleToolCalls[visibleToolCalls.length - 1];
const isShell = isShellTool(lastTool.name);
const isConfirming = lastTool.status === ToolCallStatus.Confirming;
// Logic: If dense view (not verbose, not shell, not confirming), hide border by default
const isDense = !isVerboseMode && !isShell && !isConfirming;
let showBottomBorder = !isDense;
{(() => {
if (visibleToolCalls.length === 0) {
// If we are in dense mode, we generally don't want to draw a border for an empty group.
// HOWEVER, if borderBottomOverride is true, it means the scheduler explicitly
// wants to close a box. Since dense tools don't have boxes, this must be closing
// a non-dense (e.g. shell) tool box. So we must allow it.
if (!isVerboseMode && borderBottomOverride !== true) return null;
if (borderBottomOverride !== undefined) {
showBottomBorder = borderBottomOverride;
return (
<Box
height={0}
width={contentWidth}
borderLeft={true}
borderRight={true}
borderTop={false}
borderBottom={borderBottomOverride}
borderColor={borderColor}
borderDimColor={borderDimColor}
borderStyle="round"
/>
);
}
return null;
}
if (!showBottomBorder) return null;
const lastTool = visibleToolCalls[visibleToolCalls.length - 1];
const isShell = isShellTool(lastTool.name);
const isConfirming = lastTool.status === ToolCallStatus.Confirming;
return (
<Box
height={0}
width={contentWidth}
borderLeft={true}
borderRight={true}
borderTop={false}
borderBottom={true}
borderColor={borderColor}
borderDimColor={borderDimColor}
borderStyle="round"
/>
);
})()
}
{(borderBottomOverride ?? true) && visibleToolCalls.length > 0 && (
<Box paddingX={1}>
<ShowMoreLines constrainHeight={constrainHeight} />
</Box>
)}
// Logic: If dense view (not verbose, not shell, not confirming), hide border by default
const isDense = !isVerboseMode && !isShell && !isConfirming;
if (isDense) return null;
let showBottomBorder = true;
if (borderBottomOverride !== undefined) {
showBottomBorder = borderBottomOverride;
}
if (!showBottomBorder) return null;
return (
<Box
height={0}
width={contentWidth}
borderLeft={true}
borderRight={true}
borderTop={false}
borderBottom={true}
borderColor={borderColor}
borderDimColor={borderDimColor}
borderStyle="round"
/>
);
})()}
{!isVerboseMode
? null
: (borderBottomOverride ?? true) &&
visibleToolCalls.length > 0 && (
<ShowMoreLines constrainHeight={constrainHeight} />
)}
</Box>
);
};

View File

@@ -116,7 +116,7 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
hasFocus={isThisShellFocused}
/>
{isThisShellFocused && config && (
<Box paddingLeft={STATUS_INDICATOR_WIDTH} marginTop={1}>
<Box paddingLeft={STATUS_INDICATOR_WIDTH}>
<ShellInputPrompt
activeShellPtyId={activeShellPtyId ?? null}
focus={embeddedShellFocused}

View File

@@ -1,238 +1,280 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<ToolGroupMessage /> > Ask User Filtering > does NOT filter out ask_user when status is Error 1`] = `
"╭──────────────────────────────────────────────────────────────────────────╮
│ x Ask User │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Ask User Filtering > does NOT filter out ask_user when status is Success 1`] = `
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ Ask User │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Ask User Filtering > filters out ask_user when status is Confirming 1`] = `""`;
exports[`<ToolGroupMessage /> > Ask User Filtering > filters out ask_user when status is Executing 1`] = `""`;
exports[`<ToolGroupMessage /> > Ask User Filtering > filters out ask_user when status is Pending 1`] = `""`;
exports[`<ToolGroupMessage /> > Ask User Filtering > shows other tools when ask_user is filtered out 1`] = `
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ other-tool A tool for testing │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Border Color Logic > uses gray border when all tools are successful and no shell commands 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ test-tool A tool for testing
│ Test result
│ ✓ another-tool A tool for testing
│ Test result
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ test-tool A tool for testing │
│ │
│ Test result │
│ │
│ ✓ another-tool A tool for testing │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Border Color Logic > uses yellow border for shell commands even when successful 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ run_shell_command A tool for testing
│ Test result
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ run_shell_command A tool for testing │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Border Color Logic > uses yellow border when tools are pending 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ o test-tool A tool for testing
│ Test result
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ o test-tool A tool for testing │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Confirmation Handling > renders confirmation with permanent approval disabled 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ? confirm-tool A tool for testing ← │
│ Test result
│ Do you want to proceed?
Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. No, suggest changes (esc)
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
"╭──────────────────────────────────────────────────────────────────────────╮
│ ? confirm-tool A tool for testing ← │
│ │
│ Test result │
│ Do you want to proceed? │
Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. No, suggest changes (esc)
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Confirmation Handling > renders confirmation with permanent approval enabled 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ? confirm-tool A tool for testing ← │
│ Test result
│ Do you want to proceed?
Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. Allow for all future sessions
4. No, suggest changes (esc)
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
"╭──────────────────────────────────────────────────────────────────────────╮
│ ? confirm-tool A tool for testing ← │
│ │
│ Test result │
│ Do you want to proceed? │
Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. Allow for all future sessions
4. No, suggest changes (esc)
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Confirmation Handling > shows confirmation dialog for first confirming tool only 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ? first-confirm A tool for testing ← │
│ Test result
│ Confirm first tool
Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. No, suggest changes (esc)
? second-confirm A tool for testing
│ Test result │
╰──────────────────────────────────────────────────────────────────────────────╯
"╭──────────────────────────────────────────────────────────────────────────╮
│ ? first-confirm A tool for testing ← │
│ │
│ Test result │
│ Confirm first tool │
Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. No, suggest changes (esc)
│ │
? second-confirm A tool for testing
Test result
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Event-Driven Scheduler > hides confirming tools when event-driven scheduler is enabled 1`] = `""`;
exports[`<ToolGroupMessage /> > Event-Driven Scheduler > renders nothing when only tool is in-progress AskUser with borderBottom=false 1`] = `""`;
exports[`<ToolGroupMessage /> > Event-Driven Scheduler > shows only successful tools when mixed with confirming tools 1`] = `
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ success-tool A tool for testing │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders empty tool calls array 1`] = `""`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders header when scrolled 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ tool-1 Description 1. This is a long description that will need to be tr… │
│──────────────────────────────────────────────────────────────────────────────
│ ✓ tool-2 Description 2
│ line1
│ line2
╰──────────────────────────────────────────────────────────────────────────────╯ █
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ tool-1 Description 1. This is a long description that will need to b… │
│──────────────────────────────────────────────────────────────────────────│
│ ✓ tool-2 Description 2
│ line1
│ line2
╰──────────────────────────────────────────────────────────────────────────╯
█"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders mixed tool calls including shell command 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ read_file Read a file
│ Test result
│ ⊷ run_shell_command Run command
│ Test result
│ o write_file Write to file
│ Test result
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ read_file Read a file │
│ │
│ Test result │
│ │
│ ⊷ run_shell_command Run command │
│ │
│ Test result │
│ │
│ o write_file Write to file │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders multiple tool calls with different statuses 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ successful-tool This tool succeeded
│ Test result
│ o pending-tool This tool is pending
│ Test result
│ x error-tool This tool failed
│ Test result
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ successful-tool This tool succeeded │
│ │
│ Test result │
│ │
│ o pending-tool This tool is pending │
│ │
│ Test result │
│ │
│ x error-tool This tool failed │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders shell command with yellow border 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ run_shell_command Execute shell command
│ Test result
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ run_shell_command Execute shell command │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders single successful tool call 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ test-tool A tool for testing
│ Test result
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ test-tool A tool for testing │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders tool call awaiting confirmation 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ? confirmation-tool This tool needs confirmation ← │
│ Test result
│ Are you sure you want to proceed?
Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. No, suggest changes (esc)
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
"╭──────────────────────────────────────────────────────────────────────────╮
│ ? confirmation-tool This tool needs confirmation ← │
│ │
│ Test result │
│ Are you sure you want to proceed? │
Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. No, suggest changes (esc)
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders tool call with outputFile 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ tool-with-file Tool that saved output to file
│ Test result
│ Output too long and was saved to: /path/to/output.txt
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ tool-with-file Tool that saved output to file │
│ │
│ Test result │
│ Output too long and was saved to: /path/to/output.txt │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders two tool groups where only the last line of the previous group is visible 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ tool-2 Description 2
│ line1
╰──────────────────────────────────────────────────────────────────────────────╯ █
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ tool-2 Description 2 │
│ │
│ line1
╰──────────────────────────────────────────────────────────────────────────╯
█"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders when not focused 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ test-tool A tool for testing
│ Test result
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ test-tool A tool for testing │
│ │
│ Test result │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders with limited terminal height 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ tool-with-result Tool with output
│ This is a long result that might need height constraints
│ ✓ another-tool Another tool
│ More output here
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ tool-with-result Tool with output │
│ │
│ This is a long result that might need height constraints │
│ │
│ ✓ another-tool Another tool │
│ │
│ More output here │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders with narrow terminal width 1`] = `
"╭──────────────────────────────────────
│ ✓ very-long-tool-name-that-might-w… │
│ Test result
╰──────────────────────────────────────
"╭──────────────────────────────────╮
│ ✓ very-long-tool-name-that-mig… │
│ │
│ Test result │
╰──────────────────────────────────╯
"
`;
exports[`<ToolGroupMessage /> > Height Calculation > calculates available height correctly with multiple tools with results 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────
│ ✓ test-tool A tool for testing
│ Result 1
│ ✓ test-tool A tool for testing
│ Result 2
│ ✓ test-tool A tool for testing
╰──────────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ test-tool A tool for testing │
│ │
│ Result 1 │
│ │
│ ✓ test-tool A tool for testing │
│ │
│ Result 2 │
│ │
│ ✓ test-tool A tool for testing │
│ │
╰──────────────────────────────────────────────────────────────────────────╯
"
`;