From 4beee7358b8a5631d82a7e5ad69ed5eb62ec775b Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 27 Apr 2026 16:06:02 -0700 Subject: [PATCH] feat(web): colorize run logs by level and target Parse each tracing line in the run logs panel and tint the timestamp, level, target, and message separately. Errors and warnings now stand out at a glance (coral/amber) while debug/trace and the surrounding chrome recede. Original whitespace is preserved so the formatter's column alignment is intact. --- apps/fabro-web/app/routes/run-logs.tsx | 51 +++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/apps/fabro-web/app/routes/run-logs.tsx b/apps/fabro-web/app/routes/run-logs.tsx index 9bdf6cfb2..b363737fb 100644 --- a/apps/fabro-web/app/routes/run-logs.tsx +++ b/apps/fabro-web/app/routes/run-logs.tsx @@ -57,6 +57,7 @@ function renderBody(logsQuery: ReturnType) { function LogPanel({ text }: { text: string }) { const byteCount = new Blob([text]).size; + const lines = useMemo(() => text.split("\n"), [text]); return (
@@ -67,12 +68,60 @@ function LogPanel({ text }: { text: string }) {
-        {text}
+        {lines.map((line, i) => (
+          
+        ))}
       
); } +const LOG_LINE_RE = + /^(\S+)(\s+)(TRACE|DEBUG|INFO|WARN|ERROR)(\s+)(.*)$/; + +const LEVEL_COLOR: Record = { + ERROR: "text-coral", + WARN: "text-amber", + INFO: "text-teal-500", + DEBUG: "text-fg-3", + TRACE: "text-fg-muted", +}; + +function LogLine({ line, trailingNewline }: { line: string; trailingNewline: boolean }) { + const newline = trailingNewline ? "\n" : ""; + const match = LOG_LINE_RE.exec(line); + if (!match) { + return {line}{newline}; + } + const [, timestamp, gap1, level, gap2, rest] = match; + return ( + + {timestamp} + {gap1} + {level} + {gap2} + + {newline} + + ); +} + +const LOG_REST_RE = /^([a-zA-Z_][\w:]*):(\s+)(.*)$/; + +function LogRest({ text }: { text: string }) { + const match = LOG_REST_RE.exec(text); + if (!match) return <>{text}; + const [, target, gap, message] = match; + return ( + <> + {target} + : + {gap} + {message} + + ); +} + function errorMessage(error: unknown): string | undefined { return error instanceof Error ? error.message : undefined; }