/* Stack trace viewer for debug mode — parses error stacks, shows frames + fix suggestions */ import { useMemo, useState } from "react"; interface StackFrame { file: string; line: number; column: number; function: string; source?: string; } interface FixSuggestion { title: string; description: string; code?: string; file?: string; line?: number; } interface StackTraceViewerProps { errorMessage?: string; errorType?: string; rawStack?: string; fixSuggestions?: FixSuggestion[]; onFileSelect?: (file: string, line: number) => void; } function parseStackFrames(raw: string): StackFrame[] { const frames: StackFrame[] = []; const lines = raw.split("\n"); for (const line of lines) { /* Node stack: at fn (/path/file.js:line:col) */ const nodeMatch = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/); if (nodeMatch) { frames.push({ function: nodeMatch[1], file: nodeMatch[2], line: parseInt(nodeMatch[3]), column: parseInt(nodeMatch[4]), }); continue; } /* V8 stack: at /path/file.js:line:col */ const v8Match = line.match(/at\s+(.+?):(\d+):(\d+)/); if (v8Match) { frames.push({ function: "", file: v8Match[1], line: parseInt(v8Match[2]), column: parseInt(v8Match[3]), }); continue; } /* Python traceback: File "/path/file.py", line N, in fn */ const pyMatch = line.match( /File\s+"(.+?)",\s+line\s+(\d+)(?:,\s+in\s+(.+))?/, ); if (pyMatch) { frames.push({ file: pyMatch[1], line: parseInt(pyMatch[2]), column: 0, function: pyMatch[3] || "", }); continue; } } return frames; } function FrameRow({ frame, isLast, onFileSelect, }: { frame: StackFrame; isLast: boolean; onFileSelect?: (file: string, line: number) => void; }) { const [expanded, setExpanded] = useState(false); return (
{frame.function} {frame.file}:{frame.line}:{frame.column}
{frame.function !== "" && !isLast && ( )} {onFileSelect && ( )}
); } export function StackTraceViewer({ errorMessage, errorType, rawStack, fixSuggestions = [], onFileSelect, }: StackTraceViewerProps) { const frames = useMemo( () => (rawStack ? parseStackFrames(rawStack) : []), [rawStack], ); const [showAllFrames, setShowAllFrames] = useState(false); const visibleFrames = showAllFrames ? frames : frames.slice(0, 5); return (
{/* Error header */}
{errorType && ( {errorType} )}

{errorMessage || "Unknown error"}

{/* Stack frames */} {frames.length > 0 && (
Stack Trace {frames.length} frame{frames.length !== 1 ? "s" : ""}
{/* Source frame (first) */} {visibleFrames.map((frame, i) => ( ))} {frames.length > 5 && ( )}
)} {/* Fix suggestions */} {fixSuggestions.length > 0 && (
Fix Suggestions
{fixSuggestions.map((s, i) => (
{s.title} {s.file && ( {s.file} {s.line ? `:${s.line}` : ""} )}

{s.description}

{s.code && (
                    {s.code}
                  
)}
))}
)} {/* Empty state */} {!errorMessage && frames.length === 0 && (

No error data yet. Run your code in debug mode to see stack traces here.

)}
); } export type { StackFrame, FixSuggestion };