import { Box, Text } from "ink"
import * as theme from "../../theme.js"
import { Icon } from "../Icon.js"
import type { ToolRendererProps } from "./types.js"
import { truncateText, sanitizeContent, getToolDisplayName, getToolIconName } from "./utils.js"
const MAX_PREVIEW_LINES = 12
/**
* Check if content looks like actual file content vs just path info
* File content typically has newlines or is longer than a typical path
*/
function isActualContent(content: string, path: string): boolean {
if (!content) return false
// If content equals path or is just the path, it's not actual content
if (content === path || content.endsWith(path)) return false
// Check if it looks like a plain path (no newlines, starts with / or drive letter)
if (!content.includes("\n") && (content.startsWith("/") || /^[A-Z]:\\/.test(content))) return false
// Has newlines or doesn't look like a path - treat as content
return content.includes("\n") || content.length > 200
}
export function FileReadTool({ toolData }: ToolRendererProps) {
const iconName = getToolIconName(toolData.tool)
const displayName = getToolDisplayName(toolData.tool)
const path = toolData.path || ""
const rawContent = toolData.content ? sanitizeContent(toolData.content) : ""
const isOutsideWorkspace = toolData.isOutsideWorkspace
const isList = toolData.tool.includes("list") || toolData.tool.includes("List")
// Only show content if it's actual file content, not just path info
const content = isActualContent(rawContent, path) ? rawContent : ""
// Handle batch file reads
if (toolData.batchFiles && toolData.batchFiles.length > 0) {
return (
{/* Header */}
{" "}
{displayName}
({toolData.batchFiles.length} files)
{/* File list */}
{toolData.batchFiles.slice(0, 10).map((file, index) => (
{file.path}
{file.lineSnippet && ({file.lineSnippet})}
{file.isOutsideWorkspace && (
{" "}
⚠ outside workspace
)}
))}
{toolData.batchFiles.length > 10 && (
... and {toolData.batchFiles.length - 10} more files
)}
)
}
// Single file read
const { text: previewContent, truncated, hiddenLines } = truncateText(content, MAX_PREVIEW_LINES)
return (
{/* Header with path on same line for single file */}
{displayName}
{path && (
<>
·
{path}
{isOutsideWorkspace && (
{" "}
⚠ outside workspace
)}
>
)}
{/* Content preview - only if we have actual file content */}
{previewContent && (
{isList ? (
// Directory listing - show as tree-like structure
{previewContent.split("\n").map((line, i) => (
{line}
))}
) : (
// File content - show in a box
{previewContent}
)}
{truncated && (
... ({hiddenLines} more lines)
)}
)}
)
}