- {(
- [
- "All",
- "XHR",
- "JS",
- "CSS",
- "Img",
- "Media",
- "Other",
- "Errors",
- ] as FilterType[]
- ).map((type) => (
+
+ {/* Search Input */}
+
+
+
+ setSearchQuery(e.target.value)}
+ className="pl-9 pr-10"
+ />
+ {searchQuery && (
+
+ )}
+
+ {/* Results count */}
+ {(debouncedSearchQuery ||
+ activeFilter !== "All" ||
+ statusFilter.length > 0) && (
+
+ Showing {getFilteredCount()} of {harData.log.entries.length}{" "}
+ requests
+
+ )}
+
+
+ {/* Filters and View Mode */}
+
+
+ {(
+ [
+ "All",
+ "XHR",
+ "JS",
+ "CSS",
+ "Img",
+ "Media",
+ "Other",
+ "Errors",
+ ] as FilterType[]
+ ).map((type) => (
+
+ ))}
+
+
- ))}
-
-
-
-
+
+
@@ -291,11 +407,13 @@ export default function HARFileViewer() {
activeFilter={activeFilter}
statusFilter={statusFilter}
onStatusFilterChange={setStatusFilter}
+ searchQuery={debouncedSearchQuery}
/>
) : (
)}
@@ -318,6 +436,7 @@ interface HarTableComponentProps extends HarTableProps {
activeFilter: FilterType;
statusFilter: string[];
onStatusFilterChange: (statusCodes: string[]) => void;
+ searchQuery: string;
}
const HarTable = ({
@@ -325,6 +444,7 @@ const HarTable = ({
activeFilter,
statusFilter,
onStatusFilterChange,
+ searchQuery,
}: HarTableComponentProps) => {
const [expandedRow, setExpandedRow] = useState
(null);
const [sortField, setSortField] = useState(null);
@@ -332,7 +452,7 @@ const HarTable = ({
useEffect(() => {
setExpandedRow(null);
- }, [activeFilter]);
+ }, [activeFilter, searchQuery]);
// Helper function to get status text
const getStatusText = (statusCode: number): string => {
@@ -389,6 +509,53 @@ const HarTable = ({
);
}
+ // Apply search filter
+ if (searchQuery) {
+ const query = searchQuery.toLowerCase();
+ result = result.filter((entry) => {
+ // Search in URL
+ if (entry.request.url.toLowerCase().includes(query)) return true;
+
+ // Search in request headers
+ const requestHeaderMatch = entry.request.headers.some(
+ (header) =>
+ header.name.toLowerCase().includes(query) ||
+ header.value.toLowerCase().includes(query)
+ );
+ if (requestHeaderMatch) return true;
+
+ // Search in response headers
+ const responseHeaderMatch = entry.response.headers.some(
+ (header) =>
+ header.name.toLowerCase().includes(query) ||
+ header.value.toLowerCase().includes(query)
+ );
+ if (responseHeaderMatch) return true;
+
+ // Search in request payload
+ if (entry.request.postData?.text) {
+ if (entry.request.postData.text.toLowerCase().includes(query))
+ return true;
+ }
+
+ // Search in response content
+ if (entry.response.content.text) {
+ // For base64 content, try to decode and search
+ let contentToSearch = entry.response.content.text;
+ if (isBase64(contentToSearch)) {
+ try {
+ contentToSearch = atob(contentToSearch);
+ } catch (e) {
+ // If decode fails, search in original
+ }
+ }
+ if (contentToSearch.toLowerCase().includes(query)) return true;
+ }
+
+ return false;
+ });
+ }
+
// Apply sorting
if (sortField) {
result.sort((a, b) => {
@@ -405,7 +572,7 @@ const HarTable = ({
}
return result;
- }, [entries, activeFilter, statusFilter, sortField, sortOrder]);
+ }, [entries, activeFilter, statusFilter, searchQuery, sortField, sortOrder]);
const handleSort = (field: SortField) => {
if (sortField === field) {