diff --git a/ui/text/src/toolcall.tsx b/ui/text/src/toolcall.tsx
index e0fd61b8ee..91a532d6b1 100644
--- a/ui/text/src/toolcall.tsx
+++ b/ui/text/src/toolcall.tsx
@@ -171,7 +171,7 @@ export function buildToolCallCardLines(
const bodyRows: Array<{ text: string; color?: string; italic?: boolean }> = [];
const runningText = info.status === "in_progress" ? " running…" : "";
- const tabHint = hasTruncated && !expanded ? "tab ↔" : "";
+ const tabHint = "";
bodyRows.push({ text: "__HEADER__" });
if (hasLocations) {
@@ -252,28 +252,55 @@ export function ToolCallCompact({
info,
indent,
width,
+ keyPrefix = "compact",
+ showTabHint = false,
}: {
info: ToolCallInfo;
indent: number;
width: number;
-}) {
+ keyPrefix?: string;
+ showTabHint?: boolean;
+}): React.ReactNode[] {
const statusInfo = STATUS_INDICATORS[info.status] ?? STATUS_INDICATORS.pending!;
const kindIcon = KIND_ICONS[info.kind ?? "other"] ?? "⚙";
const summary = summarizeContent(info);
- const maxSummaryWidth = width - indent - 12 - info.title.length;
+ const borderColor = info.status === "failed" ? CRANBERRY : CEDAR;
+ const dimBorder = info.status !== "failed";
+
+ const cardWidth = Math.min(width - indent - 2, 72);
+ const innerWidth = cardWidth - 2;
+
+ const tabHintText = "tab ↔";
+ const maxSummaryWidth = innerWidth - info.title.length - 8 - (showTabHint ? tabHintText.length + 2 : 0);
const trimmedSummary =
summary.length > maxSummaryWidth && maxSummaryWidth > 3
? summary.slice(0, maxSummaryWidth - 1) + "…"
: summary;
- return (
-
- {statusInfo.icon}
- {kindIcon}
- {info.title}
- {trimmedSummary ? (
- — {trimmedSummary}
- ) : null}
-
- );
+ const topBorder = "╭" + "─".repeat(innerWidth) + "╮";
+ const botBorder = "╰" + "─".repeat(innerWidth) + "╯";
+
+ return [
+
+ {topBorder}
+ ,
+
+ │
+
+
+ {statusInfo.icon}
+ {kindIcon}
+ {info.title}
+ {trimmedSummary ? (
+ — {trimmedSummary}
+ ) : null}
+
+ {showTabHint && {tabHintText}}
+
+ │
+ ,
+
+ {botBorder}
+ ,
+ ];
}
diff --git a/ui/text/src/tui.tsx b/ui/text/src/tui.tsx
index b644cb0d85..57fc4d17d7 100644
--- a/ui/text/src/tui.tsx
+++ b/ui/text/src/tui.tsx
@@ -319,7 +319,7 @@ function buildTurnBodyLines({
spinIdx,
pendingPermission,
permissionIdx,
- expandedToolCall,
+ toolCallsExpanded,
}: {
turn: Turn;
width: number;
@@ -328,32 +328,32 @@ function buildTurnBodyLines({
spinIdx: number;
pendingPermission: PendingPermission | null;
permissionIdx: number;
- expandedToolCall: string | null;
+ toolCallsExpanded: boolean;
}): React.ReactNode[] {
const toolCallIds = turn.toolCallOrder;
const toolCalls = turn.toolCalls;
- const featuredId = findFeaturedToolCallId(toolCallIds, toolCalls);
const lines: React.ReactNode[] = [];
lines.push();
- for (const tcId of toolCallIds) {
+ for (let i = 0; i < toolCallIds.length; i++) {
+ const tcId = toolCallIds[i]!;
const tc = toolCalls.get(tcId);
if (!tc) continue;
- if (tcId === featuredId || expandedToolCall === tcId) {
- const cardLines = buildToolCallCardLines(tc, CONTENT_INDENT, width, expandedToolCall === tcId, `tc-${tcId}`);
+ if (toolCallsExpanded) {
+ const cardLines = buildToolCallCardLines(tc, CONTENT_INDENT, width, true, `tc-${tcId}`);
lines.push(...cardLines);
} else {
- lines.push(
- ,
- );
+ const compactLines = ToolCallCompact({
+ info: tc,
+ indent: CONTENT_INDENT,
+ width,
+ keyPrefix: `tc-${tcId}`,
+ showTabHint: i === 0,
+ });
+ lines.push(...compactLines);
}
}
@@ -566,7 +566,7 @@ function App({
const [queuedMessages, setQueuedMessages] = useState([]);
const [viewTurnIdx, setViewTurnIdx] = useState(-1);
- const [expandedToolCall, setExpandedToolCall] = useState(null);
+ const [toolCallsExpanded, setToolCallsExpanded] = useState(false);
const [scrollOffset, setScrollOffset] = useState(0);
const clientRef = useRef(null);
@@ -589,7 +589,7 @@ function App({
}, [turns]);
useEffect(() => {
- setExpandedToolCall(null);
+ setToolCallsExpanded(false);
setScrollOffset(0);
}, [viewTurnIdx, turns.length]);
@@ -684,7 +684,7 @@ function App({
},
]);
setViewTurnIdx(-1);
- setExpandedToolCall(null);
+ setToolCallsExpanded(false);
setScrollOffset(0);
}, []);
@@ -874,7 +874,7 @@ function App({
if (!trimmed) return;
setInput("");
setViewTurnIdx(-1);
- setExpandedToolCall(null);
+ setToolCallsExpanded(false);
setScrollOffset(0);
if (loading || isProcessingRef.current) {
@@ -933,10 +933,7 @@ function App({
const currentTurn = turns[effectiveIdx];
if (!currentTurn || currentTurn.toolCallOrder.length === 0) return;
- const featuredId = findFeaturedToolCallId(currentTurn.toolCallOrder, currentTurn.toolCalls);
- if (!featuredId) return;
-
- setExpandedToolCall((prev) => (prev === featuredId ? null : featuredId));
+ setToolCallsExpanded((prev) => !prev);
return;
}
@@ -1019,7 +1016,7 @@ function App({
spinIdx,
pendingPermission: isLatest ? pendingPermission : null,
permissionIdx,
- expandedToolCall,
+ toolCallsExpanded,
});
const allBodyLines = isLatest