@@ -2,6 +2,7 @@ import React, { Component } from "react"
22import { telemetryClient } from "@src/utils/TelemetryClient"
33import { withTranslation , WithTranslation } from "react-i18next"
44import { enhanceErrorWithSourceMaps } from "@src/utils/sourceMapUtils"
5+ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
56
67type ErrorProps = {
78 children : React . ReactNode
@@ -11,12 +12,19 @@ type ErrorState = {
1112 error ?: string
1213 componentStack ?: string | null
1314 timestamp ?: number
15+ hasError : boolean
16+ errorCount : number
1417}
1518
1619class ErrorBoundary extends Component < ErrorProps , ErrorState > {
20+ private retryTimeoutId : NodeJS . Timeout | null = null
21+
1722 constructor ( props : ErrorProps ) {
1823 super ( props )
19- this . state = { }
24+ this . state = {
25+ hasError : false ,
26+ errorCount : 0 ,
27+ }
2028 }
2129
2230 static getDerivedStateFromError ( error : unknown ) {
@@ -31,31 +39,71 @@ class ErrorBoundary extends Component<ErrorProps, ErrorState> {
3139 return {
3240 error : errorMessage ,
3341 timestamp : Date . now ( ) ,
42+ hasError : true ,
3443 }
3544 }
3645
3746 async componentDidCatch ( error : Error , errorInfo : React . ErrorInfo ) {
3847 const componentStack = errorInfo . componentStack || ""
3948 const enhancedError = await enhanceErrorWithSourceMaps ( error , componentStack )
4049
50+ // Increment error count
51+ this . setState ( ( prevState ) => ( {
52+ errorCount : prevState . errorCount + 1 ,
53+ } ) )
54+
4155 telemetryClient . capture ( "error_boundary_caught_error" , {
4256 error : enhancedError . message ,
4357 stack : enhancedError . sourceMappedStack || enhancedError . stack ,
4458 componentStack : enhancedError . sourceMappedComponentStack || componentStack ,
4559 timestamp : Date . now ( ) ,
4660 errorType : enhancedError . name ,
61+ errorCount : this . state . errorCount + 1 ,
4762 } )
4863
4964 this . setState ( {
5065 error : enhancedError . sourceMappedStack || enhancedError . stack ,
5166 componentStack : enhancedError . sourceMappedComponentStack || componentStack ,
5267 } )
68+
69+ // Auto-retry after 5 seconds if this is the first error
70+ if ( this . state . errorCount === 0 && ! this . retryTimeoutId ) {
71+ this . retryTimeoutId = setTimeout ( ( ) => {
72+ this . handleReset ( )
73+ } , 5000 )
74+ }
75+ }
76+
77+ componentWillUnmount ( ) {
78+ if ( this . retryTimeoutId ) {
79+ clearTimeout ( this . retryTimeoutId )
80+ this . retryTimeoutId = null
81+ }
82+ }
83+
84+ handleReset = ( ) => {
85+ if ( this . retryTimeoutId ) {
86+ clearTimeout ( this . retryTimeoutId )
87+ this . retryTimeoutId = null
88+ }
89+
90+ this . setState ( {
91+ error : undefined ,
92+ componentStack : undefined ,
93+ timestamp : undefined ,
94+ hasError : false ,
95+ // Don't reset errorCount to track total errors in session
96+ } )
97+ }
98+
99+ handleReload = ( ) => {
100+ window . location . reload ( )
53101 }
54102
55103 render ( ) {
56104 const { t } = this . props
57105
58- if ( ! this . state . error ) {
106+ if ( ! this . state . hasError || ! this . state . error ) {
59107 return this . props . children
60108 }
61109
@@ -64,30 +112,65 @@ class ErrorBoundary extends Component<ErrorProps, ErrorState> {
64112
65113 const version = process . env . PKG_VERSION || "unknown"
66114
115+ // Use a white background to ensure visibility and prevent gray screen
67116 return (
68- < div >
69- < h2 className = "text-lg font-bold mt-0 mb-2" >
70- { t ( "errorBoundary.title" ) } (v{ version } )
71- </ h2 >
72- < p className = "mb-4" >
73- { t ( "errorBoundary.reportText" ) } { " " }
74- < a href = "https://github.com/RooCodeInc/Roo-Code/issues" target = "_blank" rel = "noreferrer" >
75- { t ( "errorBoundary.githubText" ) }
76- </ a >
77- </ p >
78- < p className = "mb-2" > { t ( "errorBoundary.copyInstructions" ) } </ p >
79-
80- < div className = "mb-4" >
81- < h3 className = "text-md font-bold mb-1" > { t ( "errorBoundary.errorStack" ) } </ h3 >
82- < pre className = "p-2 border rounded text-sm overflow-auto" > { errorDisplay } </ pre >
83- </ div >
84-
85- { componentStackDisplay && (
86- < div >
87- < h3 className = "text-md font-bold mb-1" > { t ( "errorBoundary.componentStack" ) } </ h3 >
88- < pre className = "p-2 border rounded text-sm overflow-auto" > { componentStackDisplay } </ pre >
117+ < div
118+ className = "fixed inset-0 bg-vscode-editor-background overflow-auto p-4"
119+ style = { { backgroundColor : "var(--vscode-editor-background, white)" , zIndex : 9999 } } >
120+ < div className = "max-w-4xl mx-auto" >
121+ < h2 className = "text-lg font-bold mt-0 mb-2 text-vscode-editor-foreground" >
122+ { t ( "errorBoundary.title" ) } (v{ version } )
123+ </ h2 >
124+
125+ { this . state . errorCount === 1 && (
126+ < div className = "mb-4 p-3 bg-vscode-notifications-background border border-vscode-notifications-border rounded" >
127+ < p className = "text-vscode-notifications-foreground" >
128+ The application will attempt to recover automatically in a few seconds...
129+ </ p >
130+ </ div >
131+ ) }
132+
133+ < div className = "flex gap-2 mb-4" >
134+ < VSCodeButton appearance = "primary" onClick = { this . handleReset } >
135+ Try Again
136+ </ VSCodeButton >
137+ < VSCodeButton appearance = "secondary" onClick = { this . handleReload } >
138+ Reload Window
139+ </ VSCodeButton >
89140 </ div >
90- ) }
141+
142+ < p className = "mb-4 text-vscode-editor-foreground" >
143+ { t ( "errorBoundary.reportText" ) } { " " }
144+ < a
145+ href = "https://github.com/RooCodeInc/Roo-Code/issues"
146+ target = "_blank"
147+ rel = "noreferrer"
148+ className = "text-vscode-textLink-foreground hover:text-vscode-textLink-activeForeground" >
149+ { t ( "errorBoundary.githubText" ) }
150+ </ a >
151+ </ p >
152+ < p className = "mb-2 text-vscode-editor-foreground" > { t ( "errorBoundary.copyInstructions" ) } </ p >
153+
154+ < details className = "mb-4" >
155+ < summary className = "cursor-pointer text-vscode-editor-foreground font-bold mb-2" >
156+ { t ( "errorBoundary.errorStack" ) } (Click to expand)
157+ </ summary >
158+ < pre className = "p-2 border border-vscode-panel-border rounded text-sm overflow-auto bg-vscode-editor-background text-vscode-editor-foreground mt-2" >
159+ { errorDisplay }
160+ </ pre >
161+ </ details >
162+
163+ { componentStackDisplay && (
164+ < details >
165+ < summary className = "cursor-pointer text-vscode-editor-foreground font-bold mb-2" >
166+ { t ( "errorBoundary.componentStack" ) } (Click to expand)
167+ </ summary >
168+ < pre className = "p-2 border border-vscode-panel-border rounded text-sm overflow-auto bg-vscode-editor-background text-vscode-editor-foreground mt-2" >
169+ { componentStackDisplay }
170+ </ pre >
171+ </ details >
172+ ) }
173+ </ div >
91174 </ div >
92175 )
93176 }
0 commit comments