11import React from "react" ;
2+ import styled from "@emotion/styled" ;
23import type { FileListToolArgs , FileListToolResult , FileEntry } from "@/types/tools" ;
34import { formatSize } from "@/services/tools/fileCommon" ;
4- import styles from "./FileListToolCall.module.css" ;
5+ import {
6+ ToolContainer ,
7+ ToolHeader ,
8+ ExpandIcon ,
9+ StatusIndicator ,
10+ ToolDetails ,
11+ DetailSection ,
12+ DetailLabel ,
13+ LoadingDots ,
14+ } from "./shared/ToolPrimitives" ;
15+ import { useToolExpansion , getStatusDisplay } from "./shared/toolUtils" ;
16+
17+ // FileList-specific styled components
18+
19+ const PathText = styled . span `
20+ color: var(--color-text);
21+ font-family: var(--font-monospace);
22+ font-weight: 500;
23+ ` ;
24+
25+ const ParamsText = styled . span `
26+ color: var(--color-text-secondary);
27+ font-size: 10px;
28+ margin-left: 8px;
29+ ` ;
30+
31+ const CountBadge = styled . span `
32+ color: var(--color-text-secondary);
33+ font-size: 10px;
34+ margin-left: 8px;
35+ ` ;
36+
37+ const ErrorMessage = styled . div `
38+ color: #f44336;
39+ font-size: 11px;
40+ padding: 6px 8px;
41+ background: rgba(244, 67, 54, 0.1);
42+ border-radius: 3px;
43+ border-left: 2px solid #f44336;
44+ line-height: 1.5;
45+ white-space: pre-wrap;
46+ ` ;
47+
48+ const ErrorHint = styled . div `
49+ color: var(--color-text-secondary);
50+ font-size: 10px;
51+ margin-top: 6px;
52+ font-style: italic;
53+ ` ;
54+
55+ const TreeContainer = styled . div `
56+ margin-top: 8px;
57+ background: rgba(0, 0, 0, 0.2);
58+ border-radius: 3px;
59+ padding: 12px;
60+ overflow-x: auto;
61+ font-family: var(--font-monospace);
62+ line-height: 1.6;
63+ ` ;
64+
65+ const Entry = styled . div `
66+ display: flex;
67+ align-items: center;
68+ white-space: nowrap;
69+ font-size: 11px;
70+ ` ;
71+
72+ const Prefix = styled . span `
73+ color: var(--color-text-secondary);
74+ user-select: none;
75+ ` ;
76+
77+ const Icon = styled . span `
78+ margin-right: 6px;
79+ user-select: none;
80+ ` ;
81+
82+ const Name = styled . span `
83+ color: var(--color-text);
84+ font-weight: 500;
85+ ` ;
86+
87+ const Size = styled . span `
88+ color: var(--color-text-secondary);
89+ margin-left: 8px;
90+ font-size: 10px;
91+ ` ;
92+
93+ const EmptyMessage = styled . div `
94+ color: var(--color-text-secondary);
95+ font-style: italic;
96+ text-align: center;
97+ padding: 16px;
98+ ` ;
599
6100interface FileListToolCallProps {
7101 args : FileListToolArgs ;
@@ -24,15 +118,15 @@ function renderFileTree(entries: FileEntry[], depth: number = 0): JSX.Element[]
24118 const sizeInfo = entry . size !== undefined ? ` (${ formatSize ( entry . size ) } )` : "" ;
25119
26120 elements . push (
27- < div key = { `${ depth } -${ index } -${ entry . name } ` } className = { styles . entry } >
28- < span className = { styles . prefix } > { prefix } </ span >
29- < span className = { styles . icon } > { icon } </ span >
30- < span className = { styles . name } >
121+ < Entry key = { `${ depth } -${ index } -${ entry . name } ` } >
122+ < Prefix > { prefix } </ Prefix >
123+ < Icon > { icon } </ Icon >
124+ < Name >
31125 { entry . name }
32126 { suffix }
33- </ span >
34- { sizeInfo && < span className = { styles . size } > { sizeInfo } </ span > }
35- </ div >
127+ </ Name >
128+ { sizeInfo && < Size > { sizeInfo } </ Size > }
129+ </ Entry >
36130 ) ;
37131
38132 // Recursively render children if present
@@ -44,7 +138,8 @@ function renderFileTree(entries: FileEntry[], depth: number = 0): JSX.Element[]
44138 return elements ;
45139}
46140
47- export function FileListToolCall ( { args, result, status } : FileListToolCallProps ) : JSX . Element {
141+ export const FileListToolCall : React . FC < FileListToolCallProps > = ( { args, result, status } ) => {
142+ const { expanded, toggleExpanded } = useToolExpansion ( false ) ;
48143 const isError = status === "error" || ( result && ! result . success ) ;
49144 const isComplete = status === "complete" ;
50145 const isPending = status === "pending" || status === "streaming" ;
@@ -64,46 +159,64 @@ export function FileListToolCall({ args, result, status }: FileListToolCallProps
64159 params . push ( `max: ${ args . max_entries } ` ) ;
65160 }
66161
67- const paramStr = params . length > 0 ? ` (${ params . join ( ", " ) } )` : "" ;
162+ const paramStr = params . length > 0 ? `(${ params . join ( ", " ) } )` : "" ;
163+
164+ // Convert our status to shared ToolStatus type
165+ const toolStatus = isError ? "failed" : isPending ? "executing" : "completed" ;
68166
69167 return (
70- < div className = { ` ${ styles . container } ${ isError ? styles . error : "" } ` } >
71- { /* Header */ }
72- < div className = { styles . header } >
73- < span className = { styles . toolName } > 📋 file_list: </ span >
74- < span className = { styles . path } > { args . path } </ span >
75- < span className = { styles . params } > { paramStr } </ span >
168+ < ToolContainer expanded = { expanded } >
169+ < ToolHeader onClick = { toggleExpanded } >
170+ < ExpandIcon expanded = { expanded } > ▶ </ ExpandIcon >
171+ < span > 📋 file_list</ span >
172+ < PathText > { args . path } </ PathText >
173+ { paramStr && < ParamsText > { paramStr } </ ParamsText > }
76174 { isComplete && result && result . success && (
77- < span className = { styles . count } > { result . total_count } entries</ span >
175+ < CountBadge > { result . total_count } entries</ CountBadge >
78176 ) }
79- </ div >
80-
81- { /* Status */ }
82- { isPending && < div className = { styles . status } > ⏳ Listing directory...</ div > }
83-
84- { /* Error */ }
85- { isError && result && ! result . success && (
86- < div className = { styles . errorMessage } >
87- < div className = { styles . errorTitle } > ❌ Error</ div >
88- < div className = { styles . errorText } > { result . error } </ div >
89- { result . total_found !== undefined && (
90- < div className = { styles . errorHint } >
91- Found { result . total_found } + entries (limit: { result . limit_requested } )
92- </ div >
177+ < StatusIndicator status = { toolStatus } > { getStatusDisplay ( toolStatus ) } </ StatusIndicator >
178+ </ ToolHeader >
179+
180+ { expanded && (
181+ < ToolDetails >
182+ { /* Pending state */ }
183+ { isPending && (
184+ < DetailSection >
185+ Listing directory
186+ < LoadingDots />
187+ </ DetailSection >
188+ ) }
189+
190+ { /* Error state */ }
191+ { isError && result && ! result . success && (
192+ < DetailSection >
193+ < DetailLabel > Error</ DetailLabel >
194+ < ErrorMessage >
195+ { result . error }
196+ { result . total_found !== undefined && (
197+ < ErrorHint >
198+ Found { result . total_found } + entries (limit: { result . limit_requested } )
199+ </ ErrorHint >
200+ ) }
201+ </ ErrorMessage >
202+ </ DetailSection >
93203 ) }
94- </ div >
95- ) }
96204
97- { /* Success - Render tree */ }
98- { isComplete && result && result . success && (
99- < div className = { styles . treeContainer } >
100- { result . entries . length === 0 ? (
101- < div className = { styles . empty } > Empty directory</ div >
102- ) : (
103- < div className = { styles . tree } > { renderFileTree ( result . entries ) } </ div >
205+ { /* Success state */ }
206+ { isComplete && result && result . success && (
207+ < DetailSection >
208+ < DetailLabel > Contents ({ result . total_count } entries)</ DetailLabel >
209+ < TreeContainer >
210+ { result . entries . length === 0 ? (
211+ < EmptyMessage > Empty directory</ EmptyMessage >
212+ ) : (
213+ < > { renderFileTree ( result . entries ) } </ >
214+ ) }
215+ </ TreeContainer >
216+ </ DetailSection >
104217 ) }
105- </ div >
218+ </ ToolDetails >
106219 ) }
107- </ div >
220+ </ ToolContainer >
108221 ) ;
109- }
222+ } ;
0 commit comments