@@ -38,6 +38,79 @@ function ImagePreloader() {
3838 )
3939}
4040
41+ // Capture console logs for debug reports
42+ const consoleLogs = [ ]
43+ const MAX_LOGS = 100
44+ const originalConsole = { log : console . log , warn : console . warn , error : console . error , info : console . info , debug : console . debug }
45+ ; [ 'log' , 'warn' , 'error' , 'info' , 'debug' ] . forEach ( level => {
46+ console [ level ] = ( ...args ) => {
47+ consoleLogs . push ( { level, time : new Date ( ) . toISOString ( ) , message : args . map ( a => typeof a === 'object' ? JSON . stringify ( a ) : String ( a ) ) . join ( ' ' ) } )
48+ if ( consoleLogs . length > MAX_LOGS ) consoleLogs . shift ( )
49+ originalConsole [ level ] ?. ( ...args )
50+ }
51+ } )
52+
53+ // Debug info component for error reporting
54+ function DebugInfo ( { error, step, selectedDevice, serial, message } ) {
55+ const [ copied , setCopied ] = useState ( false )
56+
57+ const getDebugReport = ( ) => {
58+ const deviceName = selectedDevice === DeviceType . COMMA_4 ? 'comma four' : selectedDevice === DeviceType . COMMA_3 ? 'comma 3/3X' : 'unknown'
59+ const errorName = Object . keys ( ErrorCode ) . find ( k => ErrorCode [ k ] === error ) || 'UNKNOWN'
60+ const stepName = Object . keys ( StepCode ) . find ( k => StepCode [ k ] === step ) || 'UNKNOWN'
61+ const platform = isWindows ? 'Windows' : isLinux ? 'Linux' : 'macOS'
62+
63+ return `## Bug Report - flash.comma.ai
64+
65+ **Device:** ${ deviceName }
66+ **Serial:** ${ serial || 'N/A' }
67+ **Error:** ${ errorName }
68+ **Step:** ${ stepName }
69+ **Last Message:** ${ message || 'N/A' }
70+
71+ **Browser:** ${ navigator . userAgent }
72+ **Platform:** ${ platform }
73+ **URL:** ${ window . location . href }
74+ **Time:** ${ new Date ( ) . toISOString ( ) }
75+
76+ <details>
77+ <summary>Console Logs</summary>
78+
79+ \`\`\`
80+ ${ consoleLogs . slice ( - 30 ) . map ( l => `[${ l . time } ] [${ l . level } ] ${ l . message } ` ) . join ( '\n' ) }
81+ \`\`\`
82+
83+ </details>
84+ `
85+ }
86+
87+ const handleCopy = ( ) => {
88+ navigator . clipboard . writeText ( getDebugReport ( ) )
89+ setCopied ( true )
90+ setTimeout ( ( ) => setCopied ( false ) , 2000 )
91+ }
92+
93+ return (
94+ < div className = "mt-6 w-full max-w-xl p-4 bg-gray-100 rounded-lg text-left text-sm" >
95+ < p className = "text-gray-600 mb-3" >
96+ Copy this debug info and paste it in{ ' ' }
97+ < a href = "https://discord.comma.ai" target = "_blank" rel = "noopener noreferrer" className = "text-blue-500 hover:underline" > Discord</ a >
98+ { ' ' } or{ ' ' }
99+ < a href = "https://github.com/commaai/flash/issues/new" target = "_blank" rel = "noopener noreferrer" className = "text-blue-500 hover:underline" > GitHub Issues</ a > .
100+ </ p >
101+ < pre className = "bg-gray-900 text-gray-100 p-3 rounded text-xs overflow-auto max-h-48 font-mono debug-scrollbar" >
102+ { getDebugReport ( ) }
103+ </ pre >
104+ < button
105+ onClick = { handleCopy }
106+ className = "mt-3 px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white text-sm rounded transition-colors"
107+ >
108+ { copied ? 'Copied!' : 'Copy Debug Info' }
109+ </ button >
110+ </ div >
111+ )
112+ }
113+
41114
42115const steps = {
43116 [ StepCode . INITIALIZING ] : {
@@ -740,6 +813,9 @@ export default function Flash() {
740813 </ button >
741814 ) }
742815 { connected && < DeviceState serial = { serial } /> }
816+ { error !== ErrorCode . NONE && (
817+ < DebugInfo error = { error } step = { step } selectedDevice = { selectedDevice } serial = { serial } message = { message } />
818+ ) }
743819 </ div >
744820 )
745821}
0 commit comments