update file ops

This commit is contained in:
Adam Cohen Hillel 2025-04-18 18:38:55 +01:00
parent 67f81d10a0
commit d876086d21
6 changed files with 358 additions and 362 deletions

View File

@ -9,9 +9,7 @@ import { Slider } from "@/components/ui/slider";
import { CommandToolView } from "./tool-views/CommandToolView";
import { StrReplaceToolView } from "./tool-views/StrReplaceToolView";
import { GenericToolView } from "./tool-views/GenericToolView";
import { CreateFileToolView } from "./tool-views/CreateFileToolView";
import { FileRewriteToolView } from "./tool-views/FileRewriteToolView";
import { DeleteFileToolView } from "./tool-views/DeleteFileToolView";
import { FileOperationToolView } from "./tool-views/FileOperationToolView";
import { BrowserToolView } from "./tool-views/BrowserToolView";
import { WebSearchToolView } from "./tool-views/WebSearchToolView";
import { WebCrawlToolView } from "./tool-views/WebCrawlToolView";
@ -66,33 +64,32 @@ function getToolView(
/>
);
case 'create-file':
return (
<CreateFileToolView
assistantContent={assistantContent}
toolContent={toolContent}
assistantTimestamp={assistantTimestamp}
toolTimestamp={toolTimestamp}
isSuccess={isSuccess}
/>
);
case 'full-file-rewrite':
return (
<FileRewriteToolView
assistantContent={assistantContent}
toolContent={toolContent}
assistantTimestamp={assistantTimestamp}
toolTimestamp={toolTimestamp}
isSuccess={isSuccess}
/>
);
case 'delete-file':
return (
<DeleteFileToolView
<FileOperationToolView
assistantContent={assistantContent}
toolContent={toolContent}
assistantTimestamp={assistantTimestamp}
toolTimestamp={toolTimestamp}
isSuccess={isSuccess}
name={normalizedToolName}
/>
);
case 'browser-navigate':
case 'browser-click':
case 'browser-extract':
case 'browser-fill':
case 'browser-wait':
return (
<BrowserToolView
name={normalizedToolName}
assistantContent={assistantContent}
toolContent={toolContent}
assistantTimestamp={assistantTimestamp}
toolTimestamp={toolTimestamp}
isSuccess={isSuccess}
project={project}
/>
);
case 'web-search':
@ -105,7 +102,7 @@ function getToolView(
isSuccess={isSuccess}
/>
);
case 'crawl-webpage':
case 'web-crawl':
return (
<WebCrawlToolView
assistantContent={assistantContent}

View File

@ -1,122 +0,0 @@
import React from "react";
import { FileCode, FileSymlink, FolderPlus, CheckCircle, AlertTriangle } from "lucide-react";
import { ToolViewProps } from "./types";
import { extractFilePath, extractFileContent, getFileType, formatTimestamp } from "./utils";
import { GenericToolView } from "./GenericToolView";
export function CreateFileToolView({
assistantContent,
toolContent,
assistantTimestamp,
toolTimestamp,
isSuccess = true
}: ToolViewProps) {
const filePath = extractFilePath(assistantContent);
const fileContent = extractFileContent(assistantContent, 'create-file');
if (!filePath || !fileContent) {
return (
<GenericToolView
name="create-file"
assistantContent={assistantContent}
toolContent={toolContent}
assistantTimestamp={assistantTimestamp}
toolTimestamp={toolTimestamp}
isSuccess={isSuccess}
/>
);
}
// Split content into lines for line numbering
const contentLines = fileContent.split('\n');
const fileType = getFileType(filePath);
const fileName = filePath.split('/').pop() || filePath;
const isMarkdown = fileName.endsWith('.md');
return (
<div className="space-y-4 p-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div className="h-8 w-8 rounded-full bg-muted flex items-center justify-center">
<FileCode className="h-4 w-4" />
</div>
<div>
<h4 className="text-sm font-medium">Create File</h4>
</div>
</div>
{toolContent && (
<div className={`px-2 py-1 rounded-full text-xs ${
isSuccess ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'
}`}>
{isSuccess ? 'Success' : 'Failed'}
</div>
)}
</div>
<div className="border rounded-md overflow-hidden shadow-sm">
{/* IDE Header */}
<div className="flex items-center p-2 bg-gray-800 text-white justify-between">
<div className="flex items-center">
{isMarkdown ?
<FileCode className="h-4 w-4 mr-2 text-blue-400" /> :
<FileSymlink className="h-4 w-4 mr-2 text-blue-400" />
}
<span className="text-sm font-medium">{fileName}</span>
</div>
<span className="text-xs text-gray-400 bg-gray-700 px-2 py-0.5 rounded">
{fileType}
</span>
</div>
{/* File Path Bar */}
<div className="px-3 py-1.5 border-t border-gray-700 bg-gray-700 flex items-center">
<div className="flex items-center space-x-1 text-gray-300">
<FolderPlus className="h-3.5 w-3.5" />
<code className="text-xs font-mono">{filePath}</code>
</div>
</div>
{/* IDE Content Area */}
<div className="overflow-auto bg-gray-900 max-h-[500px] text-gray-200">
<div className="min-w-full table">
{contentLines.map((line, idx) => (
<div key={idx} className="table-row hover:bg-gray-800/50 group">
<div className="table-cell text-right pr-4 py-0.5 text-xs font-mono text-gray-500 select-none w-12 border-r border-gray-700">
{idx + 1}
</div>
<div className="table-cell pl-4 py-0.5 text-xs font-mono whitespace-pre">
{line || ' '}
</div>
</div>
))}
{/* Add an empty line at the end */}
<div className="table-row h-16"></div>
</div>
</div>
{/* Status Footer */}
{isSuccess ? (
<div className="border-t border-gray-700 px-4 py-2 bg-green-800/20 flex items-center text-green-400">
<CheckCircle className="h-4 w-4 mr-2" />
<span className="text-xs">{fileName} created successfully</span>
</div>
) : (
<div className="border-t border-gray-700 px-4 py-2 bg-red-800/20 flex items-center text-red-400">
<AlertTriangle className="h-4 w-4 mr-2" />
<span className="text-xs">Failed to create file</span>
</div>
)}
</div>
<div className="flex justify-between items-center text-xs text-muted-foreground">
{assistantTimestamp && (
<div>Called: {formatTimestamp(assistantTimestamp)}</div>
)}
{toolTimestamp && (
<div>Result: {formatTimestamp(toolTimestamp)}</div>
)}
</div>
</div>
);
}

View File

@ -1,90 +0,0 @@
import React from "react";
import { FileX, CheckCircle, AlertTriangle } from "lucide-react";
import { ToolViewProps } from "./types";
import { extractFilePath, formatTimestamp } from "./utils";
import { GenericToolView } from "./GenericToolView";
export function DeleteFileToolView({
assistantContent,
toolContent,
assistantTimestamp,
toolTimestamp,
isSuccess = true
}: ToolViewProps) {
const filePath = extractFilePath(assistantContent);
if (!filePath) {
return (
<GenericToolView
name="delete-file"
assistantContent={assistantContent}
toolContent={toolContent}
assistantTimestamp={assistantTimestamp}
toolTimestamp={toolTimestamp}
isSuccess={isSuccess}
/>
);
}
return (
<div className="space-y-4 p-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div className="h-8 w-8 rounded-full bg-muted flex items-center justify-center">
<FileX className="h-4 w-4" />
</div>
<div>
<h4 className="text-sm font-medium">Delete File</h4>
</div>
</div>
{toolContent && (
<div className={`px-2 py-1 rounded-full text-xs ${
isSuccess ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'
}`}>
{isSuccess ? 'Success' : 'Failed'}
</div>
)}
</div>
<div className="border rounded-md overflow-hidden">
<div className="flex items-center p-2 bg-red-50 text-red-700 justify-between">
<div className="flex items-center">
<FileX className="h-4 w-4 mr-2" />
<span className="text-sm font-medium">File Deleted</span>
</div>
</div>
<div className="p-4 flex items-center justify-center">
<div className="bg-red-50 rounded-md p-3 text-center text-red-700">
<code className="text-sm font-mono">{filePath}</code>
<p className="text-xs mt-1">This file has been deleted</p>
</div>
</div>
{isSuccess && (
<div className="border-t px-4 py-2 bg-green-50 flex items-center">
<CheckCircle className="h-4 w-4 text-green-600 mr-2" />
<span className="text-xs text-green-700">File deleted successfully</span>
</div>
)}
{!isSuccess && (
<div className="border-t px-4 py-2 bg-red-50 flex items-center">
<AlertTriangle className="h-4 w-4 text-red-600 mr-2" />
<span className="text-xs text-red-700">Failed to delete file</span>
</div>
)}
</div>
<div className="flex justify-between items-center text-xs text-muted-foreground">
{assistantTimestamp && (
<div>Called: {formatTimestamp(assistantTimestamp)}</div>
)}
{toolTimestamp && (
<div>Result: {formatTimestamp(toolTimestamp)}</div>
)}
</div>
</div>
);
}

View File

@ -0,0 +1,277 @@
import React from "react";
import { FileCode, FileSymlink, FolderPlus, FileX, Replace, CheckCircle, AlertTriangle } from "lucide-react";
import { ToolViewProps } from "./types";
import { extractFilePath, extractFileContent, getFileType, formatTimestamp } from "./utils";
import { GenericToolView } from "./GenericToolView";
// Type for operation type
type FileOperation = "create" | "rewrite" | "delete";
export function FileOperationToolView({
assistantContent,
toolContent,
assistantTimestamp,
toolTimestamp,
isSuccess = true,
name
}: ToolViewProps & { name?: string }) {
// Determine operation type from content or name
const getOperationType = (): FileOperation => {
// First check tool name if available
if (name) {
if (name.includes("create")) return "create";
if (name.includes("rewrite")) return "rewrite";
if (name.includes("delete")) return "delete";
}
if (!assistantContent) return "create"; // default fallback
if (assistantContent.includes("<create-file>")) return "create";
if (assistantContent.includes("<full-file-rewrite>")) return "rewrite";
if (assistantContent.includes("delete-file") || assistantContent.includes("<delete>")) return "delete";
// Check for tool names as a fallback
if (assistantContent.toLowerCase().includes("create file")) return "create";
if (assistantContent.toLowerCase().includes("rewrite file")) return "rewrite";
if (assistantContent.toLowerCase().includes("delete file")) return "delete";
// Default to create if we can't determine
return "create";
};
const operation = getOperationType();
const filePath = extractFilePath(assistantContent);
// Only extract content for create and rewrite operations
const fileContent = operation !== "delete"
? extractFileContent(assistantContent, operation === "create" ? 'create-file' : 'full-file-rewrite')
: null;
// For debugging - show raw content if file path can't be extracted for delete operations
const showDebugInfo = !filePath && operation === "delete";
// Fall back to generic view if file path is missing or if content is missing for non-delete operations
if ((!filePath && !showDebugInfo) || (operation !== "delete" && !fileContent)) {
return (
<GenericToolView
name={`file-${operation}`}
assistantContent={assistantContent}
toolContent={toolContent}
assistantTimestamp={assistantTimestamp}
toolTimestamp={toolTimestamp}
isSuccess={isSuccess}
/>
);
}
// Operation-specific configs
const configs = {
create: {
title: "Create File",
icon: FolderPlus,
color: "text-blue-500",
bgColor: "bg-blue-50",
successMessage: "File created successfully"
},
rewrite: {
title: "Rewrite File",
icon: Replace,
color: "text-amber-500",
bgColor: "bg-amber-50",
successMessage: "File rewritten successfully"
},
delete: {
title: "Delete File",
icon: FileX,
color: "text-red-500",
bgColor: "bg-red-50",
successMessage: "File deleted successfully"
}
};
const config = configs[operation];
// Process file path - handle potential newlines and clean up
const processedFilePath = filePath ? filePath.trim().replace(/\\n/g, '\n').split('\n')[0] : null;
// For create and rewrite, prepare content for display
const contentLines = fileContent ? fileContent.replace(/\\n/g, '\n').split('\n') : [];
const fileName = processedFilePath ? processedFilePath.split('/').pop() || processedFilePath : '';
const fileType = processedFilePath ? getFileType(processedFilePath) : '';
const isMarkdown = fileName.endsWith('.md');
const Icon = config.icon;
return (
<div className="space-y-4 p-4">
{/* Header */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div className={`h-8 w-8 rounded-full ${config.bgColor} flex items-center justify-center`}>
<Icon className={`h-4 w-4 ${config.color}`} />
</div>
<div>
<h4 className="text-sm font-medium">{config.title}</h4>
<p className="text-xs text-gray-500 break-all">{processedFilePath}</p>
</div>
</div>
{toolContent && (
<div className={`px-2 py-1 rounded-full text-xs ${
isSuccess ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'
}`}>
{isSuccess ? 'Success' : 'Failed'}
</div>
)}
</div>
{/* File Content for create and rewrite operations */}
{operation !== "delete" && fileContent && (
<div className="border rounded-lg overflow-hidden shadow-sm bg-slate-900">
{/* IDE Header */}
<div className="flex items-center p-2 bg-slate-800 text-white justify-between border-b border-slate-700">
<div className="flex items-center">
{isMarkdown ?
<FileCode className="h-4 w-4 mr-2 text-blue-400" /> :
<FileSymlink className="h-4 w-4 mr-2 text-blue-400" />
}
<span className="text-sm font-medium">{fileName}</span>
</div>
<span className="text-xs text-gray-300 bg-slate-700 px-2 py-0.5 rounded">
{fileType}
</span>
</div>
{/* File Content */}
<div className="overflow-auto max-h-[400px] bg-slate-900 text-slate-200">
<div className="min-w-full table">
{contentLines.map((line, idx) => (
<div key={idx} className="table-row hover:bg-slate-800 transition-colors">
<div className="table-cell text-right pr-3 py-0.5 text-xs font-mono text-slate-500 select-none w-12 border-r border-slate-700">
{idx + 1}
</div>
<div className="table-cell pl-3 py-0.5 text-xs font-mono whitespace-pre">
{line || ' '}
</div>
</div>
))}
<div className="table-row h-4"></div>
</div>
</div>
</div>
)}
{/* Debug info for delete operations with missing file path */}
{showDebugInfo && (
<div className="border rounded-lg overflow-hidden shadow-sm mb-4">
<div className="p-4 bg-yellow-50 border-b border-yellow-200">
<h3 className="text-sm font-medium text-yellow-800">Debug Info: Unable to extract file path</h3>
</div>
<div className="p-4 bg-white">
<h4 className="text-xs font-semibold mb-2">Raw Assistant Content:</h4>
<pre className="text-xs bg-gray-50 p-3 rounded border overflow-auto max-h-[200px]">
{assistantContent || "No content"}
</pre>
</div>
</div>
)}
{/* Delete view with unknown path */}
{operation === "delete" && !processedFilePath && (
<div className="border rounded-lg overflow-hidden shadow-sm">
<div className="p-6 flex flex-col items-center justify-center bg-gray-900 text-white">
<div className="w-14 h-14 rounded-full bg-red-900/30 flex items-center justify-center mb-4">
<FileX className="h-7 w-7 text-red-400" />
</div>
<h3 className="text-lg font-medium mb-4 text-red-300">File Deleted</h3>
<div className="bg-gray-800 border border-gray-700 rounded-md p-4 w-full max-w-md text-center mb-2">
<p className="text-sm text-gray-300">Unknown file path</p>
</div>
<p className="text-sm text-gray-400 mt-2">A file has been deleted but the path could not be determined</p>
</div>
{/* Status footer */}
<div className={`p-3 border-t ${
isSuccess ? 'border-green-800 bg-green-900/20 text-green-400' : 'border-red-800 bg-red-900/20 text-red-400'
}`}>
<div className="flex items-center">
{isSuccess ? (
<CheckCircle className="h-4 w-4 mr-2 flex-shrink-0" />
) : (
<AlertTriangle className="h-4 w-4 mr-2 flex-shrink-0" />
)}
<span className="text-sm">
{isSuccess ? config.successMessage : `Failed to delete file`}
</span>
</div>
</div>
</div>
)}
{/* Delete view */}
{operation === "delete" && processedFilePath && (
<div className="border rounded-lg overflow-hidden shadow-sm">
<div className="p-6 flex flex-col items-center justify-center bg-gray-900 text-white">
<div className="w-14 h-14 rounded-full bg-red-900/30 flex items-center justify-center mb-4">
<FileX className="h-7 w-7 text-red-400" />
</div>
<h3 className="text-lg font-medium mb-4 text-red-300">File Deleted</h3>
<div className="bg-gray-800 border border-gray-700 rounded-md p-4 w-full max-w-md text-center mb-2">
<code className="text-sm font-mono text-gray-300 break-all">{processedFilePath}</code>
</div>
<p className="text-sm text-gray-400 mt-2">This file has been permanently removed</p>
</div>
{/* Status footer */}
<div className={`p-3 border-t ${
isSuccess ? 'border-green-800 bg-green-900/20 text-green-400' : 'border-red-800 bg-red-900/20 text-red-400'
}`}>
<div className="flex items-center">
{isSuccess ? (
<CheckCircle className="h-4 w-4 mr-2 flex-shrink-0" />
) : (
<AlertTriangle className="h-4 w-4 mr-2 flex-shrink-0" />
)}
<span className="text-sm">
{isSuccess ? config.successMessage : `Failed to delete file`}
</span>
</div>
</div>
</div>
)}
{/* Status footer - only show for non-delete operations as delete has its own */}
{operation !== "delete" && (
<div className={`mt-2 p-3 rounded-md border ${
isSuccess ? 'border-green-200 bg-green-50' : 'border-red-200 bg-red-50'
}`}>
<div className="flex items-center">
{isSuccess ? (
<CheckCircle className="h-4 w-4 text-green-600 mr-2 flex-shrink-0" />
) : (
<AlertTriangle className="h-4 w-4 text-red-600 mr-2 flex-shrink-0" />
)}
<span className={`text-sm ${isSuccess ? 'text-green-700' : 'text-red-700'}`}>
{isSuccess ? config.successMessage : `Failed to ${operation} file`}
</span>
</div>
</div>
)}
{/* Timestamps */}
<div className="flex justify-between items-center text-xs text-gray-500 pt-1">
{assistantTimestamp && (
<div className="flex items-center">
<span className="font-medium mr-1">Requested:</span>
{formatTimestamp(assistantTimestamp)}
</div>
)}
{toolTimestamp && (
<div className="flex items-center">
<span className="font-medium mr-1">Completed:</span>
{formatTimestamp(toolTimestamp)}
</div>
)}
</div>
</div>
);
}

View File

@ -1,122 +0,0 @@
import React from "react";
import { FileCode, FileSymlink, Replace, CheckCircle, AlertTriangle } from "lucide-react";
import { ToolViewProps } from "./types";
import { extractFilePath, extractFileContent, getFileType, formatTimestamp } from "./utils";
import { GenericToolView } from "./GenericToolView";
export function FileRewriteToolView({
assistantContent,
toolContent,
assistantTimestamp,
toolTimestamp,
isSuccess = true
}: ToolViewProps) {
const filePath = extractFilePath(assistantContent);
const fileContent = extractFileContent(assistantContent, 'full-file-rewrite');
if (!filePath || !fileContent) {
return (
<GenericToolView
name="file-rewrite"
assistantContent={assistantContent}
toolContent={toolContent}
assistantTimestamp={assistantTimestamp}
toolTimestamp={toolTimestamp}
isSuccess={isSuccess}
/>
);
}
// Split content into lines for line numbering
const contentLines = fileContent.split('\n');
const fileType = getFileType(filePath);
const fileName = filePath.split('/').pop() || filePath;
const isMarkdown = fileName.endsWith('.md');
return (
<div className="space-y-4 p-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div className="h-8 w-8 rounded-full bg-muted flex items-center justify-center">
<Replace className="h-4 w-4" />
</div>
<div>
<h4 className="text-sm font-medium">File Rewrite</h4>
</div>
</div>
{toolContent && (
<div className={`px-2 py-1 rounded-full text-xs ${
isSuccess ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'
}`}>
{isSuccess ? 'Success' : 'Failed'}
</div>
)}
</div>
<div className="border rounded-md overflow-hidden shadow-sm">
{/* IDE Header */}
<div className="flex items-center p-2 bg-gray-800 text-white justify-between">
<div className="flex items-center">
{isMarkdown ?
<FileCode className="h-4 w-4 mr-2 text-blue-400" /> :
<FileSymlink className="h-4 w-4 mr-2 text-blue-400" />
}
<span className="text-sm font-medium">{fileName}</span>
</div>
<span className="text-xs text-gray-400 bg-gray-700 px-2 py-0.5 rounded">
{fileType}
</span>
</div>
{/* File Path Bar */}
<div className="px-3 py-1.5 border-t border-gray-700 bg-gray-700 flex items-center">
<div className="flex items-center space-x-1 text-gray-300">
<Replace className="h-3.5 w-3.5" />
<code className="text-xs font-mono">{filePath}</code>
</div>
</div>
{/* IDE Content Area */}
<div className="overflow-auto bg-gray-900 max-h-[500px] text-gray-200">
<div className="min-w-full table">
{contentLines.map((line, idx) => (
<div key={idx} className="table-row hover:bg-gray-800/50 group">
<div className="table-cell text-right pr-4 py-0.5 text-xs font-mono text-gray-500 select-none w-12 border-r border-gray-700">
{idx + 1}
</div>
<div className="table-cell pl-4 py-0.5 text-xs font-mono whitespace-pre">
{line || ' '}
</div>
</div>
))}
{/* Add an empty line at the end */}
<div className="table-row h-16"></div>
</div>
</div>
{/* Status Footer */}
{isSuccess ? (
<div className="border-t border-gray-700 px-4 py-2 bg-green-800/20 flex items-center text-green-400">
<CheckCircle className="h-4 w-4 mr-2" />
<span className="text-xs">{fileName} rewritten successfully</span>
</div>
) : (
<div className="border-t border-gray-700 px-4 py-2 bg-red-800/20 flex items-center text-red-400">
<AlertTriangle className="h-4 w-4 mr-2" />
<span className="text-xs">Failed to rewrite file</span>
</div>
)}
</div>
<div className="flex justify-between items-center text-xs text-muted-foreground">
{assistantTimestamp && (
<div>Called: {formatTimestamp(assistantTimestamp)}</div>
)}
{toolTimestamp && (
<div>Result: {formatTimestamp(toolTimestamp)}</div>
)}
</div>
</div>
);
}

View File

@ -85,16 +85,53 @@ export function extractFilePath(content: string | undefined): string | null {
}
// Look for file_path in different formats
const filePathMatch = content.match(/file_path=["']([\s\S]*?)["']/);
if (filePathMatch) return filePathMatch[1];
const filePathMatch = content.match(/file_path=["']([\s\S]*?)["']/i) ||
content.match(/target_file=["']([\s\S]*?)["']/i) ||
content.match(/path=["']([\s\S]*?)["']/i);
if (filePathMatch) {
const path = filePathMatch[1].trim();
// Handle newlines and return first line if multiple lines
return cleanFilePath(path);
}
// Look for file_path in XML-like tags
const xmlFilePathMatch = content.match(/<str-replace\s+file_path=["']([\s\S]*?)["']/);
if (xmlFilePathMatch) return xmlFilePathMatch[1];
const xmlFilePathMatch = content.match(/<str-replace\s+file_path=["']([\s\S]*?)["']/i) ||
content.match(/<delete[^>]*file_path=["']([\s\S]*?)["']/i) ||
content.match(/<delete-file[^>]*>([^<]+)<\/delete-file>/i);
if (xmlFilePathMatch) {
return cleanFilePath(xmlFilePathMatch[1]);
}
// Look for file paths in delete operations in particular
if (content.toLowerCase().includes('delete') || content.includes('delete-file')) {
// Look for patterns like "Deleting file: path/to/file.txt"
const deletePathMatch = content.match(/(?:delete|remove|deleting)\s+(?:file|the file)?:?\s+["']?([\w\-./\\]+\.\w+)["']?/i);
if (deletePathMatch) return cleanFilePath(deletePathMatch[1]);
// Look for isolated file paths with extensions
const fileMatch = content.match(/["']?([\w\-./\\]+\.\w+)["']?/);
if (fileMatch) return cleanFilePath(fileMatch[1]);
}
return null;
}
// Helper to clean and process a file path string, handling escaped chars
function cleanFilePath(path: string): string {
if (!path) return path;
// Handle escaped newlines and other escaped characters
return path
.replace(/\\n/g, '\n') // Replace \n with actual newlines
.replace(/\\t/g, '\t') // Replace \t with actual tabs
.replace(/\\r/g, '') // Remove \r
.replace(/\\\\/g, '\\') // Replace \\ with \
.replace(/\\"/g, '"') // Replace \" with "
.replace(/\\'/g, "'") // Replace \' with '
.split('\n')[0] // Take only the first line if multiline
.trim(); // Trim whitespace
}
// Helper to extract str-replace old and new strings
export function extractStrReplaceContent(content: string | undefined): { oldStr: string | null, newStr: string | null } {
if (!content) return { oldStr: null, newStr: null };
@ -114,7 +151,26 @@ export function extractFileContent(content: string | undefined, toolType: 'creat
const tagName = toolType === 'create-file' ? 'create-file' : 'full-file-rewrite';
const contentMatch = content.match(new RegExp(`<${tagName}[^>]*>([\\s\\S]*?)<\\/${tagName}>`, 'i'));
return contentMatch ? contentMatch[1] : null;
if (contentMatch && contentMatch[1]) {
return processFileContent(contentMatch[1]);
}
return null;
}
// Helper to process and clean file content
function processFileContent(content: string): string {
if (!content) return content;
// Handle escaped characters
return content
.replace(/\\n/g, '\n') // Replace \n with actual newlines
.replace(/\\t/g, '\t') // Replace \t with actual tabs
.replace(/\\r/g, '') // Remove \r
.replace(/\\\\/g, '\\') // Replace \\ with \
.replace(/\\"/g, '"') // Replace \" with "
.replace(/\\'/g, "'"); // Replace \' with '
}
// Helper to determine file type (for syntax highlighting)