UNPKG

rasengan

Version:

The modern React Framework

61 lines (60 loc) 6.91 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { useEffect, useState, useSyncExternalStore } from 'react'; import { createPortal } from 'react-dom'; import { errorStore } from './error-store.js'; import { parseStackFrame, fetchSourceSnippet } from './stack-utils.js'; import './error-overlay.css'; export function ErrorOverlay() { const errors = useSyncExternalStore(errorStore.subscribe.bind(errorStore), () => errorStore.getErrors(), () => errorStore.getErrors()); const minimized = useSyncExternalStore(errorStore.subscribe.bind(errorStore), () => errorStore.isMinimized(), () => errorStore.isMinimized()); const [activeTab, setActiveTab] = useState(0); const [codePreview, setCodePreview] = useState(null); useEffect(() => { if (errors.length > 0 && activeTab >= errors.length) { setActiveTab(errors.length - 1); } }, [errors.length, activeTab]); useEffect(() => { const error = errors[activeTab]; if (!error || !error.error.stack) { setCodePreview(null); return; } const frame = parseStackFrame(error.error.stack); if (!frame) { setCodePreview(null); return; } fetchSourceSnippet(frame.file, frame.line).then((result) => { if (result) { setCodePreview({ snippet: result.snippet, errorLineIndex: result.errorLineIndex, file: frame.file, line: frame.line, }); } else { setCodePreview(null); } }); }, [activeTab, errors]); if (errors.length === 0) return null; if (minimized) return createPortal(_jsxs("button", { className: "rasengan-error-fab", onClick: () => errorStore.toggleMinimize(), title: `${errors.length} error${errors.length > 1 ? 's' : ''} — click to view`, children: [_jsx("svg", { className: "rasengan-error-fab-icon", viewBox: "0 0 16 16", width: "18", height: "18", fill: "currentColor", children: _jsx("path", { d: "M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l5.782 10.832c.659 1.234-.165 2.771-1.543 2.771H2.218c-1.378 0-2.202-1.537-1.543-2.771L6.457 1.047zM8 4a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 018 4zm0 7a1 1 0 100-2 1 1 0 000 2z" }) }), _jsx("span", { className: "rasengan-error-fab-count", children: errors.length })] }), document.body); const activeError = errors[activeTab]; if (!activeError) return null; const totalErrors = errors.length; const isFirst = activeTab === 0; const isLast = activeTab === totalErrors - 1; const snippetLines = codePreview ? codePreview.snippet.split('\n') : []; const relativeLineStart = codePreview ? codePreview.line - codePreview.errorLineIndex : 0; return createPortal(_jsxs(_Fragment, { children: [_jsx("div", { className: "rasengan-error-backdrop" }), _jsxs("div", { className: "rasengan-error-overlay", children: [_jsxs("div", { className: "rasengan-error-overlay-header", children: [_jsxs("div", { className: "rasengan-error-pagination", children: [_jsx("button", { className: "rasengan-error-pagination-btn", disabled: isFirst, onClick: () => setActiveTab(activeTab - 1), title: "Previous error", children: "\u2039" }), _jsxs("span", { className: "rasengan-error-pagination-text", children: [activeTab + 1, " of ", totalErrors] }), _jsx("button", { className: "rasengan-error-pagination-btn", disabled: isLast, onClick: () => setActiveTab(activeTab + 1), title: "Next error", children: "\u203A" })] }), _jsx("div", { className: "rasengan-error-overlay-actions", children: _jsx("button", { className: "rasengan-error-overlay-close", onClick: () => errorStore.toggleMinimize(), title: "Minimize", children: "\u00D7" }) })] }), _jsxs("div", { className: "rasengan-error-overlay-body", children: [_jsxs("div", { className: "rasengan-error-overlay-source", children: [_jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", style: { marginRight: 4, verticalAlign: -2 }, children: _jsx("path", { d: "M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l5.782 10.832c.659 1.234-.165 2.771-1.543 2.771H2.218c-1.378 0-2.202-1.537-1.543-2.771L6.457 1.047zM8 4a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 018 4zm0 7a1 1 0 100-2 1 1 0 000 2z" }) }), _jsx("p", { children: activeError.source })] }), _jsx("div", { className: "rasengan-error-overlay-message", children: activeError.error.message || 'Unknown error' }), codePreview && (_jsxs("div", { className: "rasengan-error-code-preview", children: [_jsxs("div", { className: "rasengan-error-code-preview-header", children: [_jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", style: { marginRight: 4, flexShrink: 0 }, children: _jsx("path", { d: "M2 1.75C2 .784 2.784 0 3.75 0h5.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0112.25 16h-8.5A1.75 1.75 0 012 14.25V1.75zM3.75 1.5a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h8.5a.25.25 0 00.25-.25V5.5h-2.75A1.75 1.75 0 018 3.75V1.5H3.75zm5.75.56v2.19c0 .138.112.25.25.25h2.19L9.5 2.06z" }) }), _jsx("span", { className: "rasengan-error-code-preview-file", children: codePreview.file }), _jsxs("span", { className: "rasengan-error-code-preview-line", children: ["line ", codePreview.line] })] }), _jsx("div", { className: "rasengan-error-code-preview-content", children: snippetLines.map((lineContent, idx) => { const isErrorLine = idx === codePreview.errorLineIndex; return (_jsxs("div", { className: `rasengan-error-code-line${isErrorLine ? ' error' : ''}`, children: [_jsx("span", { className: "rasengan-error-code-line-number", children: relativeLineStart + idx }), isErrorLine && (_jsx("span", { className: "rasengan-error-code-line-arrow", children: "\u25B8" })), !isErrorLine && (_jsx("span", { className: "rasengan-error-code-line-arrow" })), _jsx("span", { className: "rasengan-error-code-line-content", children: lineContent })] }, idx)); }) })] })), activeError.componentStack && (_jsxs("details", { className: "rasengan-error-overlay-section", open: true, children: [_jsx("summary", { className: "rasengan-error-overlay-section-title", children: "Component Stack" }), _jsx("pre", { className: "rasengan-error-overlay-stack", children: activeError.componentStack })] })), activeError.error.stack && (_jsxs("details", { className: "rasengan-error-overlay-section", open: true, children: [_jsx("summary", { className: "rasengan-error-overlay-section-title", children: "Stack Trace" }), _jsx("pre", { className: "rasengan-error-overlay-stack", children: activeError.error.stack })] }))] })] })] }), document.body); }