Skip to content

Commit 8de0ffc

Browse files
committed
Refactor to use Emotion CSS and reduce default limit to 64
- Replace CSS modules with Emotion styled components (matches project pattern) - Use shared ToolPrimitives and toolUtils for consistent styling - Reduce DEFAULT_MAX_ENTRIES from 100 to 64 - Update tests to reflect new limit
1 parent ed6782d commit 8de0ffc

File tree

4 files changed

+161
-170
lines changed

4 files changed

+161
-170
lines changed

src/components/tools/FileListToolCall.module.css

Lines changed: 0 additions & 122 deletions
This file was deleted.
Lines changed: 156 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,101 @@
11
import React from "react";
2+
import styled from "@emotion/styled";
23
import type { FileListToolArgs, FileListToolResult, FileEntry } from "@/types/tools";
34
import { 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

6100
interface 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+
};

src/constants/toolLimits.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const BASH_MAX_TOTAL_BYTES = 16 * 1024; // 16KB total output
66

77
export const FILE_LIST_DEFAULT_DEPTH = 1; // Non-recursive by default
88
export const FILE_LIST_MAX_DEPTH = 10; // Allow deep traversal when needed
9-
export const FILE_LIST_DEFAULT_MAX_ENTRIES = 100; // Reasonable default
9+
export const FILE_LIST_DEFAULT_MAX_ENTRIES = 64; // Reasonable default
1010
export const FILE_LIST_HARD_MAX_ENTRIES = 128; // Absolute limit (prevent context overload)
1111

1212
export const MAX_TODOS = 7; // Maximum number of TODO items in a list

src/services/tools/file_list.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ describe("file_list tool", () => {
223223

224224
describe("limit enforcement", () => {
225225
test("returns error when exceeding default limit", async () => {
226-
// Create 101 files (exceeds default limit of 100)
227-
for (let i = 0; i < 101; i++) {
226+
// Create 65 files (exceeds default limit of 64)
227+
for (let i = 0; i < 65; i++) {
228228
await fs.writeFile(path.join(tempDir, `file${i}.txt`), `content${i}`);
229229
}
230230

@@ -237,8 +237,8 @@ describe("file_list tool", () => {
237237

238238
expect(result.success).toBe(false);
239239
expect(result.error).toContain("exceed limit");
240-
expect(result.total_found).toBeGreaterThan(100);
241-
expect(result.limit_requested).toBe(100);
240+
expect(result.total_found).toBeGreaterThan(64);
241+
expect(result.limit_requested).toBe(64);
242242
});
243243

244244
test("respects custom max_entries", async () => {

0 commit comments

Comments
 (0)