8787              <mail-text-preview  :mail =" log.extra.mail_preview" 
8888            </tab-content >
8989
90-             <tab-content  v-if =" hasLaravelStackTrace(log)" tab-value =" stack_trace" 
91-               <div  class =" p-4 lg:p-8" 
92-                 <!--  Exception Header --> 
93-                 <div  v-if =" getStackTraceData(log).header" class =" mb-6 pb-4 border-b border-gray-200 dark:border-gray-600" 
94-                   <div  class =" text-red-600 dark:text-red-400 font-semibold text-lg mb-2" 
95-                     {{ getStackTraceData(log).header.type }}
96-                   </div >
97-                   <div  class =" text-gray-800 dark:text-gray-200 text-base mb-2" 
98-                     {{ getStackTraceData(log).header.message }}
99-                   </div >
100-                   <div  class =" text-sm text-gray-600 dark:text-gray-400 font-mono" 
101-                     in {{ getStackTraceData(log).header.file }}:{{ getStackTraceData(log).header.line }}
102-                   </div >
103-                 </div >
104- 
105-                 <!--  Stack Trace Frames --> 
106-                 <div  class =" space-y-2" 
107-                   <div  v-for =" (frame, frameIndex) in getStackTraceData(log).frames" :key =" frameIndex" 
108-                        class =" mb-2 border-b border-gray-100 dark:border-gray-700 pb-2 last:border-b-0" 
109-                     <div  class =" flex items-start gap-3" 
110-                       <div  class =" text-xs text-gray-500 dark:text-gray-400 font-mono w-8 flex-shrink-0 pt-1" 
111-                         #{{ frame.number }}
112-                       </div >
113-                       <div  class =" flex-1 min-w-0" 
114-                         <div  v-if =" frame.file" class =" text-sm mb-1" 
115-                           <span  class =" font-mono text-blue-600 dark:text-blue-400 break-all" span >
116-                           <span  class =" text-gray-500 dark:text-gray-400" span >
117-                           <span  class =" font-mono text-orange-600 dark:text-orange-400" span >
118-                         </div >
119-                         <div  class =" text-sm text-gray-800 dark:text-gray-200 font-mono break-all" 
120-                           {{ frame.call }}
121-                         </div >
122-                       </div >
123-                     </div >
124-                   </div >
125-                 </div >
126-               </div >
90+             <tab-content  v-if =" hasLaravelStackTrace(log)" tab-value =" laravel_stack_trace" 
91+               <LaravelStackTraceDisplay  :log =" log" 
12792            </tab-content >
12893
12994            <tab-content  tab-value =" raw" 
@@ -190,6 +155,7 @@ import TabContainer from "./TabContainer.vue";
190155import  TabContent  from  " ./TabContent.vue" 
191156import  MailHtmlPreview  from  " ./MailHtmlPreview.vue" 
192157import  MailTextPreview  from  " ./MailTextPreview.vue" 
158+ import  LaravelStackTraceDisplay  from  " ./LaravelStackTraceDisplay.vue" 
193159import  {computed } from  " vue" 
194160
195161const  fileStore  =  useFileStore ();
@@ -218,6 +184,10 @@ const hasContext = (log) => {
218184const  getExtraTabsForLog  =  (log ) =>  {
219185  let  tabs =  []; 
220186
187+   if  (hasLaravelStackTrace (log)) { 
188+     tabs .push ({ name:  ' Stack Trace' :  ' laravel_stack_trace'  
189+   } 
190+ 
221191  if  (!  log .extra  ||  !  log .extra .mail_preview ) { 
222192    return  tabs; 
223193  } 
@@ -238,11 +208,6 @@ const getTabsForLog = (log) => {
238208
239209  tabs .push ({ name:  ' Raw' :  ' raw'  
240210
241-   //  Add Stack Trace tab for Laravel logs with stack traces 
242-   if  (hasLaravelStackTrace (log)) { 
243-     tabs .push ({ name:  ' Stack Trace' :  ' stack_trace'  
244-   } 
245- 
246211  return  tabs .filter (Boolean ); 
247212} 
248213
@@ -263,50 +228,6 @@ const hasLaravelStackTrace = (log) => {
263228  return  exception &&  typeof  exception ===  ' string' &&  exception .includes (' [stacktrace]'  
264229} 
265230
266- const  getStackTraceData  =  (log ) =>  {
267-   const  exception  =  Array .isArray (log .context ) 
268-     ?  log .context .find (item  =>  item .exception )? .exception  
269-     :  log .context .exception ; 
270- 
271-   if  (! exception ||  typeof  exception !==  ' string'  
272-     return  { header:  null , frames :  [] }; 
273-   } 
274- 
275-   //  Parse exception header 
276-   const  headerMatch  =  exception .match (/ ^ \[ object\] \s * \( ([^ (] + )\( code:\s * \d + \) :\s * (. +? )\s + at\s + (. +? ):(\d + )\) /  
277-   const  header  =  headerMatch ?  { 
278-     type:  headerMatch[1 ].trim (), 
279-     message:  headerMatch[2 ].trim (), 
280-     file:  headerMatch[3 ].trim (), 
281-     line:  parseInt (headerMatch[4 ]) 
282-   } :  null ; 
283- 
284-   //  Parse stack trace frames 
285-   const  stacktraceMatch  =  exception .match (/ \[ stacktrace\] ([\s\S ] *? )(?:\n\n | \n $ | $ )/  
286-   const  frames  =  []; 
287-   if  (stacktraceMatch) { 
288-     const  frameRegex  =  / #(\d + )\s + (. +? )(?:\n | $ )/ g ; 
289-     let  match; 
290-     while  ((match =  frameRegex .exec (stacktraceMatch[1 ])) !==  null ) { 
291-       const  frameLine  =  match[2 ].trim (); 
292-       const  fileMatch  =  frameLine .match (/ ^ (. +? )\( (\d + )\) :\s * (. + )$ /  
293-       frames .push (fileMatch ?  { 
294-         number:  parseInt (match[1 ]), 
295-         file:  fileMatch[1 ], 
296-         line:  parseInt (fileMatch[2 ]), 
297-         call:  fileMatch[3 ] 
298-       } :  { 
299-         number:  parseInt (match[1 ]), 
300-         file:  ' '  
301-         line:  0 , 
302-         call:  frameLine 
303-       }); 
304-     } 
305-   } 
306- 
307-   return  { header, frames  }; 
308- } 
309- 
310231const  tableColumns  =  computed (() =>  {
311232  //  the extra two columns are for the expand/collapse and log index columns 
312233  return  logViewerStore .columns .length  +  2 ; 
0 commit comments