UNPKG

murmuraba

Version:

Real-time audio noise reduction with advanced chunked processing for web applications

177 lines (176 loc) 7.35 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { Component } from 'react'; export class ErrorBoundary extends Component { constructor() { super(...arguments); this.resetTimeoutId = null; this.state = { hasError: false, error: null, errorInfo: null }; this.resetErrorBoundary = () => { this.setState({ hasError: false, error: null, errorInfo: null }); }; this.handleReload = () => { this.resetErrorBoundary(); // If still erroring after reset, reload the page this.resetTimeoutId = window.setTimeout(() => { window.location.reload(); }, 100); }; this.handleKeyDown = (event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.handleReload(); } }; } static getDerivedStateFromError(error) { return { hasError: true, error }; } componentDidCatch(error, errorInfo) { this.setState({ errorInfo }); // Call custom error handler if provided this.props.onError?.(error, errorInfo); // Log error for debugging console.error('🚨 Murmuraba ErrorBoundary caught an error:', error, errorInfo); } componentDidUpdate(prevProps) { const { resetOnPropsChange } = this.props; const { hasError } = this.state; // Reset error state if props change and resetOnPropsChange is enabled if (resetOnPropsChange && hasError && prevProps.children !== this.props.children) { this.resetErrorBoundary(); } } componentWillUnmount() { if (this.resetTimeoutId) { window.clearTimeout(this.resetTimeoutId); } } render() { const { hasError, error, errorInfo } = this.state; const { children, fallback, className = '', 'aria-label': ariaLabel } = this.props; if (hasError && error) { // Use custom fallback if provided if (fallback) { return fallback(error, errorInfo); } // Default error UI return (_jsx("div", { className: `murmuraba-error-boundary ${className}`, role: "alert", "aria-label": ariaLabel || 'Application error occurred', style: defaultStyles.container, children: _jsxs("div", { style: defaultStyles.card, children: [_jsx("div", { style: defaultStyles.icon, "aria-hidden": "true", children: "\uD83C\uDF3E" }), _jsx("h1", { style: defaultStyles.title, children: "Oops! Something went wrong" }), _jsx("p", { style: defaultStyles.message, children: "The audio processing application encountered an unexpected error. This might be due to browser compatibility or a temporary issue." }), _jsxs("button", { onClick: this.handleReload, onKeyDown: this.handleKeyDown, style: defaultStyles.button, "aria-label": "Reload application to recover from error", children: [_jsx("span", { style: defaultStyles.buttonIcon, "aria-hidden": "true", children: "\uD83D\uDD04" }), _jsx("span", { children: "Reload Application" })] }), process.env.NODE_ENV === 'development' && error && (_jsxs("details", { style: defaultStyles.details, children: [_jsx("summary", { style: defaultStyles.summary, children: "\uD83D\uDD0D Error Details (Development)" }), _jsxs("div", { style: defaultStyles.errorContent, children: [_jsx("h4", { style: defaultStyles.errorTitle, children: "Error:" }), _jsx("pre", { style: defaultStyles.errorText, children: error.toString() }), error.stack && (_jsxs(_Fragment, { children: [_jsx("h4", { style: defaultStyles.errorTitle, children: "Stack Trace:" }), _jsx("pre", { style: defaultStyles.errorText, children: error.stack })] })), errorInfo?.componentStack && (_jsxs(_Fragment, { children: [_jsx("h4", { style: defaultStyles.errorTitle, children: "Component Stack:" }), _jsx("pre", { style: defaultStyles.errorText, children: errorInfo.componentStack })] }))] })] }))] }) })); } return children; } } // Default styles for the error boundary const defaultStyles = { container: { minHeight: '400px', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '2rem', backgroundColor: 'var(--dark-bg-primary, #0A0B0E)', fontFamily: 'system-ui, -apple-system, sans-serif', }, card: { maxWidth: '500px', width: '100%', padding: '3rem 2rem', backgroundColor: 'var(--dark-surface, #1F2028)', borderRadius: '12px', boxShadow: '0 10px 25px rgba(0, 0, 0, 0.1)', textAlign: 'center', border: '1px solid var(--neutral-400, #4E5165)', }, icon: { fontSize: '4rem', marginBottom: '1.5rem', display: 'block', }, title: { fontSize: '1.75rem', fontWeight: 'bold', color: 'var(--dark-text-primary, #CACBDA)', marginBottom: '1rem', margin: '0 0 1rem 0', }, message: { fontSize: '1rem', color: 'var(--dark-text-secondary, #A0A1B3)', lineHeight: '1.6', marginBottom: '2rem', margin: '0 0 2rem 0', }, button: { display: 'inline-flex', alignItems: 'center', gap: '0.5rem', padding: '0.75rem 1.5rem', backgroundColor: 'var(--accent-green, #52A32F)', color: 'var(--dark-text-primary, #CACBDA)', border: 'none', borderRadius: '6px', fontSize: '1rem', fontWeight: '500', cursor: 'pointer', transition: 'all 0.2s ease', ':hover': { backgroundColor: 'var(--accent-green-light, #6BC748)', }, }, buttonIcon: { fontSize: '1rem', }, details: { marginTop: '2rem', textAlign: 'left', backgroundColor: 'var(--dark-bg-tertiary, #1A1B23)', borderRadius: '6px', padding: '1rem', }, summary: { cursor: 'pointer', fontWeight: '500', color: 'var(--dark-text-secondary, #A0A1B3)', marginBottom: '1rem', fontSize: '0.9rem', }, errorContent: { marginTop: '1rem', }, errorTitle: { fontSize: '0.875rem', fontWeight: '600', color: 'var(--dark-text-primary, #CACBDA)', marginBottom: '0.5rem', margin: '1rem 0 0.5rem 0', }, errorText: { fontSize: '0.75rem', fontFamily: 'Monaco, Consolas, "Courier New", monospace', backgroundColor: '#edf2f7', padding: '0.75rem', borderRadius: '4px', overflow: 'auto', whiteSpace: 'pre-wrap', wordBreak: 'break-word', color: 'var(--dark-text-primary, #CACBDA)', border: '1px solid #cbd5e0', margin: '0 0 0.5rem 0', }, }; // Export a HOC version for easier usage export function withErrorBoundary(Component, errorBoundaryProps) { const WrappedComponent = (props) => (_jsx(ErrorBoundary, { ...errorBoundaryProps, children: _jsx(Component, { ...props }) })); WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name})`; return WrappedComponent; }