diff --git a/apps/app/src/components/LogsView.tsx b/apps/app/src/components/LogsView.tsx index dcc3899f..5f5cd631 100644 --- a/apps/app/src/components/LogsView.tsx +++ b/apps/app/src/components/LogsView.tsx @@ -2,7 +2,7 @@ * Logs view component — logs viewer with filtering. */ -import { useEffect, useMemo, useState } from "react"; +import { memo, useEffect, useMemo, useState } from "react"; import { useApp } from "../AppContext"; import type { LogEntry } from "../api-client"; import { formatTime } from "./shared/format"; @@ -18,6 +18,69 @@ const TAG_COLORS: Record = { websocket: { bg: "rgba(20, 184, 166, 0.15)", fg: "rgb(20, 184, 166)" }, }; +// ⚡ Bolt Performance Optimization +// What: Extract and memoize individual log entries. +// Why: When typing in the search filter, the entire list of logs re-rendered on every keystroke. +// Impact: Prevents O(N) re-renders of DOM nodes for unaffected log lines, significantly improving text input latency. +const LogEntryItem = memo(function LogEntryItem({ + entry, +}: { + entry: LogEntry; +}) { + return ( +
+ {/* Timestamp */} + + {formatTime(entry.timestamp, { fallback: "—" })} + + + {/* Level */} + + {entry.level} + + + {/* Source */} + + [{entry.source}] + + + {/* Tag badges */} + + {(entry.tags ?? []).map((t: string) => { + const c = TAG_COLORS[t]; + return ( + + {t} + + ); + })} + + + {/* Message */} + {entry.message} +
+ ); +}); + export function LogsView() { const [searchQuery, setSearchQuery] = useState(""); @@ -163,57 +226,10 @@ export function LogsView() { ) : ( filteredLogs.map((entry: LogEntry) => ( -
- {/* Timestamp */} - - {formatTime(entry.timestamp, { fallback: "—" })} - - - {/* Level */} - - {entry.level} - - - {/* Source */} - - [{entry.source}] - - - {/* Tag badges */} - - {(entry.tags ?? []).map((t: string) => { - const c = TAG_COLORS[t]; - return ( - - {t} - - ); - })} - - - {/* Message */} - {entry.message} -
+ entry={entry} + /> )) )}