diff --git a/packages/app/src/components/code-insets.ts b/packages/app/src/components/code-insets.ts new file mode 100644 index 000000000..d0d3b8884 --- /dev/null +++ b/packages/app/src/components/code-insets.ts @@ -0,0 +1,9 @@ +export function getCodeInsets(theme: any) { + const paddingX = theme.spacing[3] + theme.spacing[2]; + const paddingY = theme.spacing[1]; + const extraRight = theme.spacing[4]; + const extraBottom = theme.spacing[3]; + + return { paddingX, paddingY, extraRight, extraBottom }; +} + diff --git a/packages/app/src/components/diff-viewer.tsx b/packages/app/src/components/diff-viewer.tsx index 629418ef1..32c3a4e71 100644 --- a/packages/app/src/components/diff-viewer.tsx +++ b/packages/app/src/components/diff-viewer.tsx @@ -4,6 +4,7 @@ import { ScrollView } from "react-native-gesture-handler"; import { StyleSheet } from "react-native-unistyles"; import { Fonts } from "@/constants/theme"; import type { DiffLine, DiffSegment } from "@/utils/tool-call-parsers"; +import { getCodeInsets } from "./code-insets"; interface DiffViewerProps { diffLines: DiffLine[]; @@ -86,64 +87,70 @@ export function DiffViewer({ diffLines, maxHeight = 280, emptyLabel = "No change ); } -const styles = StyleSheet.create((theme) => ({ - verticalScroll: {}, - verticalContent: { - flexGrow: 1, - }, - horizontalContent: { - flexDirection: "column" as const, - }, - linesContainer: { - alignSelf: "flex-start", - }, - line: { - minWidth: "100%", - paddingHorizontal: theme.spacing[3], - paddingVertical: theme.spacing[1], - }, - lineText: { - fontFamily: Fonts.mono, - fontSize: theme.fontSize.xs, - color: theme.colors.foreground, - }, - headerLine: { - backgroundColor: theme.colors.surface2, - }, - headerText: { - color: theme.colors.foregroundMuted, - }, - addLine: { - backgroundColor: "rgba(46, 160, 67, 0.15)", - }, - addText: { - color: theme.colors.foreground, - }, - removeLine: { - backgroundColor: "rgba(248, 81, 73, 0.1)", - }, - removeText: { - color: theme.colors.foreground, - }, - addHighlight: { - backgroundColor: "rgba(46, 160, 67, 0.4)", - }, - removeHighlight: { - backgroundColor: "rgba(248, 81, 73, 0.35)", - }, - contextLine: { - backgroundColor: theme.colors.surface2, - }, - contextText: { - color: theme.colors.foregroundMuted, - }, - emptyState: { - padding: theme.spacing[4], - alignItems: "center" as const, - justifyContent: "center" as const, - }, - emptyText: { - fontSize: theme.fontSize.sm, - color: theme.colors.foregroundMuted, - }, -})); +const styles = StyleSheet.create((theme) => { + const insets = getCodeInsets(theme); + + return { + verticalScroll: {}, + verticalContent: { + flexGrow: 1, + paddingBottom: insets.extraBottom, + }, + horizontalContent: { + flexDirection: "column" as const, + paddingRight: insets.extraRight, + }, + linesContainer: { + alignSelf: "flex-start", + }, + line: { + minWidth: "100%", + paddingHorizontal: insets.paddingX, + paddingVertical: insets.paddingY, + }, + lineText: { + fontFamily: Fonts.mono, + fontSize: theme.fontSize.xs, + color: theme.colors.foreground, + }, + headerLine: { + backgroundColor: theme.colors.surface2, + }, + headerText: { + color: theme.colors.foregroundMuted, + }, + addLine: { + backgroundColor: "rgba(46, 160, 67, 0.15)", + }, + addText: { + color: theme.colors.foreground, + }, + removeLine: { + backgroundColor: "rgba(248, 81, 73, 0.1)", + }, + removeText: { + color: theme.colors.foreground, + }, + addHighlight: { + backgroundColor: "rgba(46, 160, 67, 0.4)", + }, + removeHighlight: { + backgroundColor: "rgba(248, 81, 73, 0.35)", + }, + contextLine: { + backgroundColor: theme.colors.surface2, + }, + contextText: { + color: theme.colors.foregroundMuted, + }, + emptyState: { + padding: theme.spacing[4], + alignItems: "center" as const, + justifyContent: "center" as const, + }, + emptyText: { + fontSize: theme.fontSize.sm, + color: theme.colors.foregroundMuted, + }, + }; +}); diff --git a/packages/app/src/components/message.tsx b/packages/app/src/components/message.tsx index 08bb8ba6c..315e0cab3 100644 --- a/packages/app/src/components/message.tsx +++ b/packages/app/src/components/message.tsx @@ -389,8 +389,8 @@ const expandableBadgeStylesheet = StyleSheet.create((theme) => ({ borderTopWidth: 0, borderColor: theme.colors.borderAccent, backgroundColor: theme.colors.surface0, - padding: theme.spacing[2], - gap: theme.spacing[2], + padding: 0, + gap: 0, flexShrink: 1, minWidth: 0, }, @@ -781,6 +781,9 @@ interface TodoListCardProps { } const todoListCardStylesheet = StyleSheet.create((theme) => ({ + detailsWrapper: { + padding: theme.spacing[2], + }, list: { gap: theme.spacing[1], }, @@ -832,26 +835,28 @@ export const TodoListCard = memo(function TodoListCard({ const renderDetails = useCallback(() => { return ( - - {items.length === 0 ? ( - No tasks yet. - ) : ( - items.map((item, idx) => ( - - - {item.completed ? : null} + + + {items.length === 0 ? ( + No tasks yet. + ) : ( + items.map((item, idx) => ( + + + {item.completed ? : null} + + + {item.text} + - - {item.text} - - - )) - )} + )) + )} + ); }, [items]); @@ -1099,12 +1104,13 @@ export const ToolCall = memo(function ToolCall({ args, result, error, + cwd, }); } else { // Desktop: toggle inline expansion setIsExpanded((prev) => !prev); } - }, [isMobile, openToolCall, toolName, kind, status, args, result, error]); + }, [isMobile, openToolCall, toolName, kind, status, args, result, error, cwd]); useEffect(() => { if (isMobile || !isPerfLoggingEnabled()) { @@ -1136,15 +1142,7 @@ export const ToolCall = memo(function ToolCall({ // Render inline details for desktop const renderDetails = useCallback(() => { if (isMobile) return null; - return ( - - - - ); + return ; }, [isMobile, display, errorText]); return ( @@ -1169,10 +1167,3 @@ export const ToolCall = memo(function ToolCall({ /> ); }); - -const toolCallInlineStyles = StyleSheet.create((theme) => ({ - detailsContainer: { - paddingTop: theme.spacing[3], - paddingBottom: theme.spacing[2], - }, -})); diff --git a/packages/app/src/components/tool-call-details.tsx b/packages/app/src/components/tool-call-details.tsx index 16a46c8a2..1ab017e31 100644 --- a/packages/app/src/components/tool-call-details.tsx +++ b/packages/app/src/components/tool-call-details.tsx @@ -11,6 +11,7 @@ import { type ToolCallDisplay, } from "@/utils/tool-call-parsers"; import { DiffViewer } from "./diff-viewer"; +import { getCodeInsets } from "./code-insets"; // ---- Types ---- @@ -157,46 +158,37 @@ export function ToolCallDetailsContent({ }, [display]); const sections: ReactNode[] = []; + const isFullBleed = display.type === "edit" || display.type === "shell"; if (display.type === "shell") { + const combinedText = `$ ${display.command}${display.output ? `\n${display.output}` : ""}`; sections.push( - Command - - {display.command} - - {display.output ? ( + - {display.output} + + {combinedText} + - ) : null} + ); } else if (display.type === "edit") { sections.push( - File - - {display.filePath} - - {diffLines && diffLines.length > 0 ? ( + {diffLines ? ( @@ -206,10 +198,6 @@ export function ToolCallDetailsContent({ } else if (display.type === "read") { sections.push( - File - - {display.filePath} - {(display.offset !== undefined || display.limit !== undefined) ? ( {display.offset !== undefined ? `Offset: ${display.offset}` : ""} @@ -326,7 +314,11 @@ export function ToolCallDetailsContent({ ); } - return {sections}; + return ( + + {sections} + + ); } // ---- Hook for parsing tool call data ---- @@ -362,10 +354,18 @@ export function useToolCallDetails(data: ToolCallDetailsData) { // ---- Styles ---- -const styles = StyleSheet.create((theme) => ({ - container: { - gap: theme.spacing[4], - }, +const styles = StyleSheet.create((theme) => { + const insets = getCodeInsets(theme); + + return { + paddedContainer: { + gap: theme.spacing[4], + padding: theme.spacing[2], + }, + fullBleedContainer: { + gap: theme.spacing[2], + padding: 0, + }, groupHeader: { flexDirection: "row", alignItems: "center", @@ -391,20 +391,6 @@ const styles = StyleSheet.create((theme) => ({ textTransform: "uppercase", letterSpacing: 0.5, }, - fileBadge: { - alignSelf: "flex-start", - paddingHorizontal: theme.spacing[2], - paddingVertical: theme.spacing[1], - borderRadius: theme.borderRadius.base, - borderWidth: theme.borderWidth[1], - borderColor: theme.colors.border, - backgroundColor: theme.colors.surface2, - }, - fileBadgeText: { - color: theme.colors.foreground, - fontFamily: Fonts.mono, - fontSize: theme.fontSize.xs, - }, rangeText: { color: theme.colors.foregroundMuted, fontSize: theme.fontSize.xs, @@ -416,6 +402,19 @@ const styles = StyleSheet.create((theme) => ({ overflow: "hidden", backgroundColor: theme.colors.surface2, }, + codeVerticalScroll: {}, + codeVerticalContent: { + flexGrow: 1, + paddingBottom: insets.extraBottom, + }, + codeHorizontalContent: { + paddingRight: insets.extraRight, + }, + codeLine: { + minWidth: "100%", + paddingHorizontal: insets.paddingX, + paddingVertical: insets.paddingY, + }, scrollArea: { borderWidth: theme.borderWidth[1], borderColor: theme.colors.border, @@ -451,4 +450,5 @@ const styles = StyleSheet.create((theme) => ({ fontSize: theme.fontSize.sm, fontStyle: "italic", }, -})); + }; +}); diff --git a/packages/app/src/components/tool-call-sheet.tsx b/packages/app/src/components/tool-call-sheet.tsx index e03895aed..699f95422 100644 --- a/packages/app/src/components/tool-call-sheet.tsx +++ b/packages/app/src/components/tool-call-sheet.tsx @@ -16,6 +16,7 @@ import { BottomSheetBackgroundProps, } from "@gorhom/bottom-sheet"; import { Pencil, Eye, SquareTerminal, Search, Wrench, X } from "lucide-react-native"; +import { extractPrincipalParam } from "@/utils/tool-call-parsers"; import { ToolCallDetailsContent, useToolCallDetails } from "./tool-call-details"; // ----- Types ----- @@ -24,6 +25,7 @@ export interface ToolCallSheetData { toolName: string; kind?: string; status?: "executing" | "completed" | "failed"; + cwd?: string; args?: unknown; result?: unknown; error?: unknown; @@ -138,13 +140,14 @@ interface ToolCallSheetContentProps { } function ToolCallSheetContent({ data, onClose }: ToolCallSheetContentProps) { - const { toolName, kind, args, result, error } = data; + const { toolName, kind, cwd, args, result, error } = data; const IconComponent = kind ? toolKindIcons[kind.toLowerCase()] || Wrench : Wrench; const { display, errorText } = useToolCallDetails({ toolName, args, result, error }); + const principalParam = useMemo(() => extractPrincipalParam(args, cwd), [args, cwd]); return ( @@ -152,9 +155,16 @@ function ToolCallSheetContent({ data, onClose }: ToolCallSheetContentProps) { - - {display.toolName} - + + + {display.toolName} + + {principalParam ? ( + + {principalParam} + + ) : null} + @@ -200,6 +210,10 @@ const styles = StyleSheet.create((theme) => ({ gap: theme.spacing[2], flex: 1, }, + headerTextColumn: { + flex: 1, + minWidth: 0, + }, headerIcon: { color: theme.colors.foreground, }, @@ -209,6 +223,11 @@ const styles = StyleSheet.create((theme) => ({ color: theme.colors.foreground, flex: 1, }, + headerSubtitle: { + marginTop: theme.spacing[1], + fontSize: theme.fontSize.sm, + color: theme.colors.foregroundMuted, + }, closeButton: { padding: theme.spacing[2], }, @@ -220,8 +239,6 @@ const styles = StyleSheet.create((theme) => ({ backgroundColor: theme.colors.surface2, }, contentContainer: { - paddingHorizontal: theme.spacing[4], - paddingTop: theme.spacing[4], - paddingBottom: theme.spacing[8], + padding: 0, }, }));