@@ -179,6 +179,10 @@ <h2>Provider Metrics</h2>
179179 const escapeHtml = ( v ) => String ( v ?? '' ) . replace ( / & / g, '&' ) . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) ;
180180 const encodeCandidateId = ( v ) => encodeURIComponent ( String ( v ) ) . replace ( / ' / g, '%27' ) ;
181181 const projectName = ( p ) => p . split ( / [ \\ \/ ] / ) . filter ( Boolean ) . pop ( ) || 'ROOT' ;
182+ const selectProject = ( encodedProject ) => {
183+ currentProject = decodeURIComponent ( encodedProject ) ;
184+ refreshData ( ) ;
185+ } ;
182186
183187 function switchView ( viewId ) {
184188 document . querySelectorAll ( '.main-view' ) . forEach ( v => v . classList . add ( 'hidden' ) ) ;
@@ -199,13 +203,13 @@ <h2>Provider Metrics</h2>
199203
200204 document . getElementById ( 'project-badge' ) . textContent = projectName ( currentProject ) . toUpperCase ( ) ;
201205 document . getElementById ( 'project-dna' ) . innerHTML = `
202- <div class="stat-row"><span class="stat-label">STACK</span><span>${ state . stack } </span></div>
203- <div class="stat-row"><span class="stat-label">FRAMEWORK</span><span>${ state . framework } </span></div>
204- <div class="stat-row"><span class="stat-label">PATTERN</span><span>${ state . pattern } </span></div>
206+ <div class="stat-row"><span class="stat-label">STACK</span><span>${ escapeHtml ( state . stack ) } </span></div>
207+ <div class="stat-row"><span class="stat-label">FRAMEWORK</span><span>${ escapeHtml ( state . framework ) } </span></div>
208+ <div class="stat-row"><span class="stat-label">PATTERN</span><span>${ escapeHtml ( state . pattern ) } </span></div>
205209 ` ;
206210
207211 document . getElementById ( 'project-list' ) . innerHTML = state . projects . map ( p =>
208- `<div class="nav-item ${ p === currentProject ? 'active' : '' } " onclick="currentProject= '${ p } ';refreshData(); " style="font-size: 0.7rem; margin-left: 0.5rem;">📂 ${ projectName ( p ) } </div>`
212+ `<div class="nav-item ${ p === currentProject ? 'active' : '' } " onclick="selectProject( '${ encodeCandidateId ( p ) } ') " style="font-size: 0.7rem; margin-left: 0.5rem;">📂 ${ escapeHtml ( projectName ( p ) ) } </div>`
209213 ) . join ( '' ) ;
210214
211215 // 2. View Specific Refresh
@@ -214,14 +218,46 @@ <h2>Provider Metrics</h2>
214218 const data = await res . json ( ) ;
215219 document . getElementById ( 'timeline-terminal' ) . innerHTML = data . map ( e => {
216220 let icon = 'EVT' , color = 'var(--text)' ;
217- if ( e . type === 'prompt' ) { icon = 'PRM' ; color = 'var(--accent)' ; }
221+ let extraHtml = '' ;
222+
223+ if ( e . type === 'prompt' ) {
224+ icon = 'PRM' ; color = 'var(--accent)' ;
225+ if ( e . details && e . details . intent === 'self-heal' ) {
226+ icon = 'HEAL' ; color = '#10b981' ;
227+ extraHtml += `<div class="badge" style="background: rgba(16, 185, 129, 0.15); color: #6ee7b7; border-color: rgba(16, 185, 129, 0.3); margin-top: 8px; display: inline-block;">AUTONOMOUS SELF-HEALING RETRY</div>` ;
228+ }
229+ if ( e . details && e . details . normalized_prompt ) {
230+ extraHtml += `<div style="margin-top: 8px; padding: 10px; background: rgba(56, 189, 248, 0.05); border-left: 2px solid var(--accent); border-radius: 0 4px 4px 0; font-size: 0.75rem; white-space: pre-wrap; color: var(--dim);"><strong>IMPROVED PROMPT:</strong><br/>${ escapeHtml ( e . details . normalized_prompt ) } </div>` ;
231+ }
232+ }
218233 else if ( e . type === 'commit' ) { icon = 'GIT' ; color = '#f472b6' ; }
219234 else if ( e . type === 'log' ) { icon = 'LOG' ; color = 'var(--dim)' ; }
235+ else if ( e . type === 'execution' ) {
236+ icon = 'EXEC' ;
237+ color = e . event_type === 'failed' ? '#ef4444' : '#22c55e' ;
238+ let execBadge = e . event_type === 'failed'
239+ ? `<span class="badge" style="background: rgba(239, 68, 68, 0.15); color: #fca5a5; border-color: rgba(239, 68, 68, 0.3);">AI ERROR</span>`
240+ : `<span class="badge" style="background: rgba(34, 197, 94, 0.15); color: #86efac; border-color: rgba(34, 197, 94, 0.3);">SUCCESS</span>` ;
241+
242+ extraHtml = `<div style="margin-top: 8px;">
243+ ${ execBadge } <span style="color: var(--dim); font-size: 0.7rem; margin-left: 6px;">EXECUTOR: ${ escapeHtml ( e . author || 'unknown' ) } </span>
244+ </div>` ;
245+
246+ if ( e . event_type === 'failed' && e . details && e . details . error ) {
247+ extraHtml += `<div style="margin-top: 8px; padding: 10px; background: rgba(239, 68, 68, 0.05); border-left: 2px solid #ef4444; border-radius: 0 4px 4px 0; font-size: 0.75rem; white-space: pre-wrap; color: #fca5a5; font-family: monospace;"><strong>RAW ERROR:</strong><br/>${ escapeHtml ( typeof e . details . error === 'object' ? JSON . stringify ( e . details . error , null , 2 ) : String ( e . details . error ) ) } </div>` ;
248+ } else if ( e . event_type === 'completed' && e . details && e . details . healedResponse ) {
249+ extraHtml += `<div style="margin-top: 8px; padding: 10px; background: rgba(34, 197, 94, 0.05); border-left: 2px solid #22c55e; border-radius: 0 4px 4px 0; font-size: 0.75rem; white-space: pre-wrap; color: #86efac;"><strong>HEALED RESPONSE:</strong><br/>${ escapeHtml ( e . details . healedResponse ) } </div>` ;
250+ }
251+ }
252+
220253 return `
221254 <div class="log-line">
222255 <span class="log-ts">${ new Date ( e . timestamp ) . toLocaleTimeString ( ) } </span>
223256 <span class="log-icon" style="color:${ color } ">${ icon } </span>
224- <span>${ escapeHtml ( e . summary ) } </span>
257+ <div style="flex-grow: 1;">
258+ <div style="white-space: pre-wrap; font-family: monospace; font-size: 0.85rem;">${ escapeHtml ( e . summary ) } </div>
259+ ${ extraHtml }
260+ </div>
225261 </div>
226262 ` ;
227263 } ) . join ( '' ) ;
@@ -235,14 +271,14 @@ <h2>Provider Metrics</h2>
235271 <div style="display:flex; justify-content:space-between; align-items:flex-start;">
236272 <div>
237273 <code style="color:var(--accent)">${ c . sha . substring ( 0 , 7 ) } </code> <strong>${ escapeHtml ( c . message ) } </strong>
238- <div style="font-size:0.7rem; color:var(--dim); margin-top:4px;">by ${ c . author } • ${ new Date ( c . committed_at ) . toLocaleString ( ) } </div>
274+ <div style="font-size:0.7rem; color:var(--dim); margin-top:4px;">by ${ escapeHtml ( c . author ) } • ${ new Date ( c . committed_at ) . toLocaleString ( ) } </div>
239275 </div>
240276 <div style="text-align:right">
241277 <span class="diff-add">+${ JSON . parse ( c . diff_stats_json ) . insertions || 0 } </span>
242278 <span class="diff-del">-${ JSON . parse ( c . diff_stats_json ) . deletions || 0 } </span>
243279 </div>
244280 </div>
245- ${ c . prompt_id ? `<div class="badge" style="margin-top:10px; display:inline-block">Linked to Prompt: ${ c . prompt_id } </div>` : '' }
281+ ${ c . prompt_id ? `<div class="badge" style="margin-top:10px; display:inline-block">Linked to Prompt: ${ escapeHtml ( c . prompt_id ) } </div>` : '' }
246282 </div>
247283 ` ) . join ( '' ) || '<p style="color:var(--dim)">No commits ingested for this project.</p>' ;
248284 }
@@ -328,9 +364,13 @@ <h2>Provider Metrics</h2>
328364 }
329365 }
330366
367+ // Auto-poll every 3 seconds so the user never has to click anything
368+ setInterval ( ( ) => {
369+ refreshData ( ) . catch ( err => console . error ( "Auto-refresh failed:" , err ) ) ;
370+ } , 3000 ) ;
371+
331372 // Initial Load
332373 refreshData ( ) ;
333- setInterval ( refreshData , 10000 ) ;
334374 </ script >
335375</ body >
336376</ html >
0 commit comments