2222 .controls-area { display : flex; gap : .5rem ; align-items : center; }
2323 .empty-state { height : 62vh ; display : flex; align-items : center; justify-content : center; color : # 6c757d ; }
2424 .btn-sm-icon { padding : .25rem .5rem ; font-size : .85rem ; }
25+ /* Progress area styles */
26+ # indexProgress { margin-bottom : 0.75rem ; min-height : 2rem ; }
27+ .progress-info { font-size : .95rem ; color : # 333 ; display : flex; align-items : center; gap : 0.5rem ; }
2528 </ style >
2629</ head >
2730< body >
@@ -54,7 +57,7 @@ <h5 class="card-title">Analyses</h5>
5457 {% if analyses %}
5558 < ul class ="list-group list-group-flush ">
5659 {% for a in analyses %}
57- < li class ="list-group-item d-flex justify-content-between align-items-start ">
60+ < li class ="list-group-item d-flex justify-content-between align-items-start analysis-item " data-analysis-id =" {{ a.id }} ">
5861 < div class ="ms-2 me-auto ">
5962 < div class ="fw-bold "> {{ a.name }} — ID: {{ a.id }}</ div >
6063 < div class ="small text-muted "> {{ a.path }} — < em > {{ a.created_at }}</ em > </ div >
@@ -97,6 +100,11 @@ <h5 class="card-title mb-0">Chat</h5>
97100 </ div >
98101 </ div >
99102
103+ <!-- Indexing verification / progress area (English) -->
104+ < div id ="indexProgress " class ="mb-2 " aria-live ="polite ">
105+ < span class ="text-muted "> Indexing status not available.</ span >
106+ </ div >
107+
100108 < div id ="chatWindow " class ="chat-window mb-3 " aria-live ="polite "> </ div >
101109
102110 < div class ="mt-auto ">
@@ -370,18 +378,96 @@ <h5 class="card-title mb-0">Chat</h5>
370378 renderChat ( ) ;
371379 }
372380
381+ // --- Analysis status polling and UI update (English text) ---
382+ const POLL_INTERVAL = 5000 ;
383+
384+ async function fetchAnalysesStatus ( ) {
385+ try {
386+ const resp = await fetch ( "/analyses/status" ) ;
387+ if ( ! resp . ok ) return null ;
388+ return await resp . json ( ) ;
389+ } catch ( e ) {
390+ console . warn ( "Failed to fetch analyses status:" , e ) ;
391+ return null ;
392+ }
393+ }
394+
395+ function updateAnalysesListUI ( analyses ) {
396+ document . querySelectorAll ( ".analysis-item" ) . forEach ( li => {
397+ const aid = parseInt ( li . getAttribute ( "data-analysis-id" ) ) ;
398+ const a = ( analyses || [ ] ) . find ( x => x . id === aid ) ;
399+ if ( a ) {
400+ // update badge and counts in the left list (if present)
401+ const statusBadge = li . querySelector ( ".badge" ) ;
402+ if ( statusBadge ) {
403+ statusBadge . textContent = a . status ;
404+ }
405+ const fileCountSpan = li . querySelector ( ".text-muted" ) ;
406+ // we won't try to be too invasive; ensure the left panel reflects counts by reloading page for complex updates
407+ }
408+ } ) ;
409+ }
410+
411+ function renderProgressForSelectedAnalysis ( analyses ) {
412+ const aidInput = document . getElementById ( "analysis_id" ) ;
413+ const out = document . getElementById ( "indexProgress" ) ;
414+ if ( ! aidInput || ! out ) return ;
415+ const aid = parseInt ( aidInput . value || 0 ) || null ;
416+ if ( ! aid ) {
417+ out . innerHTML = '<span class="text-muted">Select an Analysis ID to view progress.</span>' ;
418+ return ;
419+ }
420+ const a = ( analyses || [ ] ) . find ( x => x . id === aid ) ;
421+ if ( ! a ) {
422+ out . innerHTML = '<span class="text-muted">Analysis not found.</span>' ;
423+ return ;
424+ }
425+
426+ if ( a . status === "running" ) {
427+ const fc = a . file_count || 0 ;
428+ const ec = a . embedding_count || 0 ;
429+ out . innerHTML = `<div class="progress-info"><strong>Indexing in progress</strong> — ${ fc } files processed <small class="text-muted">(${ ec } embeddings)</small><div class="spinner-small ms-2" aria-hidden="true"></div></div>` ;
430+ } else if ( a . status === "pending" ) {
431+ out . innerHTML = `<span class="text-muted">Analysis queued (status: ${ a . status } ).</span>` ;
432+ } else if ( a . status === "completed" ) {
433+ out . innerHTML = `<span class="text-success">Indexing completed — ${ a . file_count || 0 } files processed, ${ a . embedding_count || 0 } embeddings.</span>` ;
434+ } else if ( a . status === "failed" ) {
435+ out . innerHTML = `<span class="text-danger">Indexing failed.</span>` ;
436+ } else {
437+ out . innerHTML = `<span class="text-muted">Status: ${ a . status } — ${ a . file_count || 0 } files processed.</span>` ;
438+ }
439+ }
440+
441+ let _pollHandle = null ;
442+ async function startPolling ( ) {
443+ const analyses = await fetchAnalysesStatus ( ) ;
444+ if ( analyses ) {
445+ updateAnalysesListUI ( analyses ) ;
446+ renderProgressForSelectedAnalysis ( analyses ) ;
447+ }
448+ if ( _pollHandle ) clearInterval ( _pollHandle ) ;
449+ _pollHandle = setInterval ( async ( ) => {
450+ const analyses = await fetchAnalysesStatus ( ) ;
451+ if ( analyses ) {
452+ updateAnalysesListUI ( analyses ) ;
453+ renderProgressForSelectedAnalysis ( analyses ) ;
454+ }
455+ } , POLL_INTERVAL ) ;
456+ }
457+
458+ // --- Search only: show results in chat window (transient) ---
373459 async function searchOnly ( ) {
374460 const text = document . getElementById ( "chatInput" ) . value . trim ( ) ;
375461 if ( ! text ) return ;
376462 const aid = parseInt ( document . getElementById ( "analysis_id" ) . value || 0 ) || 0 ;
377463 const top_k = parseInt ( document . getElementById ( "top_k" ) . value || 5 ) ;
378464 try {
379465 const win = document . getElementById ( "chatWindow" ) ;
380- // clear previous transient search results (keep history preserved)
381466 const header = document . createElement ( "div" ) ;
382467 header . className = "mb-2 muted-small" ;
383468 header . textContent = "Search results" ;
384469 win . appendChild ( header ) ;
470+
385471 const resp = await fetch ( "/search" , {
386472 method : "POST" ,
387473 headers : { "Content-Type" :"application/json" } ,
@@ -401,7 +487,9 @@ <h5 class="card-title mb-0">Chat</h5>
401487 data . forEach ( d => {
402488 const item = document . createElement ( "div" ) ;
403489 item . className = "card mb-2 p-2" ;
404- item . innerHTML = `<div class="small"><strong>${ DOMPurify . sanitize ( d . path ) } </strong></div><div class="muted-small">score: ${ d . score !== undefined && d . score !== null ? parseFloat ( d . score ) . toFixed ( 4 ) : "n/a" } </div>` ;
490+ const path = DOMPurify . sanitize ( d . path || "(unknown)" ) ;
491+ const score = ( d . score !== undefined && d . score !== null ) ? Number ( d . score ) . toFixed ( 4 ) : "n/a" ;
492+ item . innerHTML = `<div class="small"><strong>${ path } </strong></div><div class="muted-small">score: ${ score } </div>` ;
405493 win . appendChild ( item ) ;
406494 } ) ;
407495 } else {
@@ -421,17 +509,28 @@ <h5 class="card-title mb-0">Chat</h5>
421509 }
422510 }
423511
424- document . getElementById ( "sendBtn" ) . addEventListener ( "click" , sendMessage ) ;
425- document . getElementById ( "clearBtn" ) . addEventListener ( "click" , clearHistory ) ;
426- document . getElementById ( "searchBtn" ) . addEventListener ( "click" , searchOnly ) ;
427- document . getElementById ( "chatInput" ) . addEventListener ( "keydown" , function ( e ) {
428- if ( e . key === "Enter" && ! e . shiftKey ) {
429- e . preventDefault ( ) ;
430- sendMessage ( ) ;
431- }
432- } ) ;
512+ // Wire up event handlers after DOM loaded
513+ window . addEventListener ( "load" , ( ) => {
514+ document . getElementById ( "sendBtn" ) . addEventListener ( "click" , sendMessage ) ;
515+ document . getElementById ( "clearBtn" ) . addEventListener ( "click" , clearHistory ) ;
516+ document . getElementById ( "searchBtn" ) . addEventListener ( "click" , searchOnly ) ;
517+ document . getElementById ( "chatInput" ) . addEventListener ( "keydown" , function ( e ) {
518+ if ( e . key === "Enter" && ! e . shiftKey ) {
519+ e . preventDefault ( ) ;
520+ sendMessage ( ) ;
521+ }
522+ } ) ;
523+ // click on analysis items sets analysis id
524+ document . querySelectorAll ( ".analysis-item" ) . forEach ( li => {
525+ li . addEventListener ( "click" , ( ) => {
526+ const aid = li . getAttribute ( "data-analysis-id" ) ;
527+ document . getElementById ( "analysis_id" ) . value = aid ;
528+ } ) ;
529+ } ) ;
433530
434- renderChat ( ) ;
531+ renderChat ( ) ;
532+ startPolling ( ) ;
533+ } ) ;
435534 </ script >
436535</ body >
437536</ html >
0 commit comments