refactor: simplify tool call display and path handling
- Remove displayName from tool calls, use name directly with icon - Add medium font weight for tool call badges - Strip workspace root from file paths at parsing level using Zod schema - Add accentBorder to tool call badges and git diff file blocks - Fix user message alignment and vertical spacing - Deduplicate stripCwdPrefix into tool-call-parsers.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
4f3b2317ab
commit
66881511b6
@@ -283,15 +283,11 @@ export function AgentStreamView({
|
||||
case "tool_call": {
|
||||
const { payload } = item;
|
||||
|
||||
console.log("[TOOL_CALL_DEBUG]", JSON.stringify(payload, null, 2));
|
||||
|
||||
if (payload.source === "agent") {
|
||||
const data = payload.data;
|
||||
const toolLabel = data.displayName ?? `${data.server}/${data.tool}`;
|
||||
content = (
|
||||
<ToolCall
|
||||
toolName={toolLabel}
|
||||
kind={data.kind}
|
||||
toolName={data.name}
|
||||
args={data.input}
|
||||
result={data.result}
|
||||
error={data.error}
|
||||
@@ -299,6 +295,7 @@ export function AgentStreamView({
|
||||
parsedEditEntries={data.parsedEdits}
|
||||
parsedReadEntries={data.parsedReads}
|
||||
parsedCommandDetails={data.parsedCommand ?? null}
|
||||
cwd={agent.cwd}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -315,7 +315,7 @@ const styles = StyleSheet.create((theme) => ({
|
||||
overflow: "hidden",
|
||||
backgroundColor: theme.colors.muted,
|
||||
borderWidth: theme.borderWidth[1],
|
||||
borderColor: theme.colors.border,
|
||||
borderColor: theme.colors.accentBorder,
|
||||
marginBottom: theme.spacing[2],
|
||||
},
|
||||
fileHeader: {
|
||||
|
||||
@@ -29,6 +29,7 @@ import { Colors } from "@/constants/theme";
|
||||
import * as Clipboard from "expo-clipboard";
|
||||
import type { TodoEntry, ThoughtStatus } from "@/types/stream";
|
||||
import type { CommandDetails, EditEntry, ReadEntry } from "@/utils/tool-call-parsers";
|
||||
import { extractPrincipalParam } from "@/utils/tool-call-parsers";
|
||||
import { resolveToolCallPreview } from "./tool-call-preview";
|
||||
import { useToolCallSheet } from "./tool-call-sheet";
|
||||
|
||||
@@ -41,15 +42,16 @@ const userMessageStylesheet = StyleSheet.create((theme) => ({
|
||||
container: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
marginBottom: theme.spacing[3],
|
||||
paddingHorizontal: theme.spacing[4],
|
||||
marginBottom: theme.spacing[4],
|
||||
marginTop: theme.spacing[4],
|
||||
paddingHorizontal: theme.spacing[2],
|
||||
},
|
||||
bubble: {
|
||||
backgroundColor: theme.colors.muted,
|
||||
borderRadius: theme.borderRadius["2xl"],
|
||||
borderTopRightRadius: theme.borderRadius.sm,
|
||||
paddingHorizontal: theme.spacing[4],
|
||||
paddingVertical: theme.spacing[3],
|
||||
paddingVertical: theme.spacing[4],
|
||||
maxWidth: "80%",
|
||||
},
|
||||
text: {
|
||||
@@ -187,7 +189,7 @@ const expandableBadgeStylesheet = StyleSheet.create((theme) => ({
|
||||
pressable: {
|
||||
borderRadius: theme.borderRadius.lg,
|
||||
borderWidth: theme.borderWidth[1],
|
||||
borderColor: theme.colors.border,
|
||||
borderColor: theme.colors.accentBorder,
|
||||
backgroundColor: theme.colors.secondary,
|
||||
paddingHorizontal: theme.spacing[2],
|
||||
paddingVertical: theme.spacing[1],
|
||||
@@ -209,13 +211,24 @@ const expandableBadgeStylesheet = StyleSheet.create((theme) => ({
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
label: {
|
||||
color: theme.colors.foreground,
|
||||
fontSize: theme.fontSize.base,
|
||||
fontWeight: theme.fontWeight.medium,
|
||||
flexShrink: 0,
|
||||
},
|
||||
secondaryLabel: {
|
||||
flex: 1,
|
||||
color: theme.colors.mutedForeground,
|
||||
fontSize: theme.fontSize.base,
|
||||
fontWeight: theme.fontWeight.normal,
|
||||
marginLeft: theme.spacing[2],
|
||||
},
|
||||
spacer: {
|
||||
flex: 1,
|
||||
},
|
||||
chevron: {
|
||||
marginLeft: theme.spacing[1],
|
||||
flexShrink: 0,
|
||||
},
|
||||
detailWrapper: {
|
||||
marginTop: theme.spacing[2],
|
||||
@@ -829,6 +842,7 @@ interface AgentThoughtMessageProps {
|
||||
|
||||
interface ExpandableBadgeProps {
|
||||
label: string;
|
||||
secondaryLabel?: string;
|
||||
icon?: ComponentType<{ size?: number; color?: string }>;
|
||||
isExpanded: boolean;
|
||||
onToggle: () => void;
|
||||
@@ -839,6 +853,7 @@ interface ExpandableBadgeProps {
|
||||
|
||||
const ExpandableBadge = memo(function ExpandableBadge({
|
||||
label,
|
||||
secondaryLabel,
|
||||
icon,
|
||||
isExpanded,
|
||||
onToggle,
|
||||
@@ -914,6 +929,13 @@ const ExpandableBadge = memo(function ExpandableBadge({
|
||||
<Text style={expandableBadgeStylesheet.label} numberOfLines={1}>
|
||||
{label}
|
||||
</Text>
|
||||
{secondaryLabel ? (
|
||||
<Text style={expandableBadgeStylesheet.secondaryLabel} numberOfLines={1}>
|
||||
{secondaryLabel}
|
||||
</Text>
|
||||
) : (
|
||||
<View style={expandableBadgeStylesheet.spacer} />
|
||||
)}
|
||||
{hasDetails ? (
|
||||
<ChevronRight
|
||||
size={14}
|
||||
@@ -1084,7 +1106,6 @@ export function AgentThoughtMessage({ message, status = "ready" }: AgentThoughtM
|
||||
|
||||
interface ToolCallProps {
|
||||
toolName: string;
|
||||
kind?: string; // Optional kind for ACP tool calls
|
||||
args: any;
|
||||
result?: any;
|
||||
error?: any;
|
||||
@@ -1092,6 +1113,7 @@ interface ToolCallProps {
|
||||
parsedEditEntries?: EditEntry[];
|
||||
parsedReadEntries?: ReadEntry[];
|
||||
parsedCommandDetails?: CommandDetails | null;
|
||||
cwd?: string;
|
||||
}
|
||||
|
||||
// Icon mapping for tool kinds
|
||||
@@ -1102,9 +1124,19 @@ const toolKindIcons: Record<string, any> = {
|
||||
search: Search,
|
||||
};
|
||||
|
||||
// Derive tool kind from tool name for icon selection
|
||||
function getToolKindFromName(toolName: string): string {
|
||||
const lower = toolName.toLowerCase();
|
||||
if (lower === "read" || lower === "read_file" || lower.startsWith("read")) return "read";
|
||||
if (lower === "edit" || lower === "write" || lower === "apply_patch") return "edit";
|
||||
if (lower === "bash" || lower === "shell") return "execute";
|
||||
if (lower === "grep" || lower === "glob" || lower === "web_search") return "search";
|
||||
return "tool";
|
||||
}
|
||||
|
||||
|
||||
export const ToolCall = memo(function ToolCall({
|
||||
toolName,
|
||||
kind,
|
||||
args,
|
||||
result,
|
||||
error,
|
||||
@@ -1112,12 +1144,18 @@ export const ToolCall = memo(function ToolCall({
|
||||
parsedEditEntries,
|
||||
parsedReadEntries,
|
||||
parsedCommandDetails,
|
||||
cwd,
|
||||
}: ToolCallProps) {
|
||||
const { openToolCall } = useToolCallSheet();
|
||||
|
||||
const IconComponent = kind
|
||||
? toolKindIcons[kind.toLowerCase()] || Wrench
|
||||
: Wrench;
|
||||
const kind = getToolKindFromName(toolName);
|
||||
const IconComponent = toolKindIcons[kind] || Wrench;
|
||||
|
||||
// Extract principal param for secondary label (memoized)
|
||||
const principalParam = useMemo(
|
||||
() => extractPrincipalParam(args, cwd),
|
||||
[args, cwd]
|
||||
);
|
||||
|
||||
// Check if there's any content to display in the sheet
|
||||
const hasDetails = args !== undefined || result !== undefined || error !== undefined;
|
||||
@@ -1133,8 +1171,9 @@ export const ToolCall = memo(function ToolCall({
|
||||
parsedEditEntries,
|
||||
parsedReadEntries,
|
||||
parsedCommandDetails,
|
||||
cwd,
|
||||
});
|
||||
}, [openToolCall, toolName, kind, status, args, result, error, parsedEditEntries, parsedReadEntries, parsedCommandDetails]);
|
||||
}, [openToolCall, toolName, kind, status, args, result, error, parsedEditEntries, parsedReadEntries, parsedCommandDetails, cwd]);
|
||||
|
||||
// Dummy renderDetails to make badge tappable - actual content is rendered in the sheet
|
||||
const dummyRenderDetails = useCallback(() => null, []);
|
||||
@@ -1142,6 +1181,7 @@ export const ToolCall = memo(function ToolCall({
|
||||
return (
|
||||
<ExpandableBadge
|
||||
label={toolName}
|
||||
secondaryLabel={principalParam}
|
||||
icon={IconComponent}
|
||||
isExpanded={false}
|
||||
onToggle={handleOpenSheet}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { Pencil, Eye, SquareTerminal, Search, Wrench, X } from "lucide-react-nat
|
||||
import { DiffViewer } from "./diff-viewer";
|
||||
import {
|
||||
extractKeyValuePairs,
|
||||
stripCwdPrefix,
|
||||
type EditEntry,
|
||||
type ReadEntry,
|
||||
type CommandDetails,
|
||||
@@ -36,6 +37,7 @@ export interface ToolCallSheetData {
|
||||
parsedEditEntries?: EditEntry[];
|
||||
parsedReadEntries?: ReadEntry[];
|
||||
parsedCommandDetails?: CommandDetails | null;
|
||||
cwd?: string;
|
||||
}
|
||||
|
||||
interface ToolCallSheetContextValue {
|
||||
@@ -234,6 +236,7 @@ function ToolCallSheetContent({ data, onClose }: ToolCallSheetContentProps) {
|
||||
parsedEditEntries,
|
||||
parsedReadEntries,
|
||||
parsedCommandDetails,
|
||||
cwd,
|
||||
} = data;
|
||||
|
||||
const IconComponent = kind
|
||||
@@ -453,7 +456,7 @@ function ToolCallSheetContent({ data, onClose }: ToolCallSheetContentProps) {
|
||||
<Text style={styles.sectionTitle}>Diff</Text>
|
||||
{entry.filePath ? (
|
||||
<View style={styles.fileBadge}>
|
||||
<Text style={styles.fileBadgeText}>{entry.filePath}</Text>
|
||||
<Text style={styles.fileBadgeText}>{stripCwdPrefix(entry.filePath, cwd)}</Text>
|
||||
</View>
|
||||
) : null}
|
||||
<View style={styles.diffContainer}>
|
||||
@@ -478,7 +481,7 @@ function ToolCallSheetContent({ data, onClose }: ToolCallSheetContentProps) {
|
||||
<Text style={styles.sectionTitle}>Read Result</Text>
|
||||
{entry.filePath ? (
|
||||
<View style={styles.fileBadge}>
|
||||
<Text style={styles.fileBadgeText}>{entry.filePath}</Text>
|
||||
<Text style={styles.fileBadgeText}>{stripCwdPrefix(entry.filePath, cwd)}</Text>
|
||||
</View>
|
||||
) : null}
|
||||
<ScrollView
|
||||
|
||||
Reference in New Issue
Block a user