@@ -48,7 +48,7 @@ public static void OpenInBrowser(string htmlPath)
4848
4949 private static string BuildHtml ( string jsonData , bool isTerminal , string workspaceRoot , string ? title = null )
5050 {
51- var refreshTag = isTerminal ? "" : """<meta http-equiv="refresh" content="2">""" ;
51+ var refreshTag = "" ; // Refresh handled by JavaScript below
5252 var escapedJson = HttpUtility . HtmlEncode ( jsonData ) ;
5353 var escapedRoot = HttpUtility . HtmlEncode ( workspaceRoot . Replace ( '\\ ' , '/' ) ) ;
5454
@@ -295,6 +295,99 @@ .breakdown h3 {
295295 color: var(--color-primary);
296296 }
297297
298+ /* Verification progress */
299+ .verification-section {
300+ background: var(--color-card);
301+ border-radius: 12px;
302+ padding: 1.5rem;
303+ border: 1px solid var(--color-border);
304+ margin-bottom: 1.5rem;
305+ }
306+ .verification-section h3 {
307+ font-size: 0.9rem;
308+ font-weight: 600;
309+ margin-bottom: 1rem;
310+ }
311+ .verification-item {
312+ font-size: 0.8rem;
313+ padding: 8px 12px;
314+ border-radius: 6px;
315+ margin-bottom: 4px;
316+ display: flex;
317+ flex-wrap: wrap;
318+ align-items: center;
319+ gap: 8px;
320+ border-left: 3px solid transparent;
321+ }
322+ .verdict-grounded { background: var(--color-passed-bg); border-left-color: var(--color-passed); }
323+ .verdict-partial { background: #fef9c3; border-left-color: #ca8a04; }
324+ .verdict-hallucinated { background: var(--color-failed-bg); border-left-color: var(--color-failed); }
325+ .verdict-icon { font-weight: 700; font-size: 0.9rem; }
326+ .verdict-grounded .verdict-icon { color: var(--color-passed); }
327+ .verdict-partial .verdict-icon { color: #ca8a04; }
328+ .verdict-hallucinated .verdict-icon { color: var(--color-failed); }
329+ .verdict-id {
330+ font-family: 'JetBrains Mono', monospace;
331+ font-weight: 600;
332+ color: var(--color-text);
333+ }
334+ .verdict-title { color: var(--color-text); flex: 1; min-width: 0; }
335+ .verdict-badge {
336+ font-size: 0.7rem;
337+ font-weight: 600;
338+ text-transform: uppercase;
339+ padding: 2px 8px;
340+ border-radius: 10px;
341+ letter-spacing: 0.03em;
342+ }
343+ .verdict-grounded .verdict-badge { background: var(--color-passed); color: white; }
344+ .verdict-partial .verdict-badge { background: #ca8a04; color: white; }
345+ .verdict-hallucinated .verdict-badge { background: var(--color-failed); color: white; }
346+ .verdict-reason {
347+ width: 100%;
348+ font-size: 0.75rem;
349+ color: var(--color-text-muted);
350+ font-style: italic;
351+ padding-left: 24px;
352+ }
353+
354+ /* Rejected tests */
355+ .rejected-section {
356+ background: var(--color-card);
357+ border-radius: 12px;
358+ padding: 1.5rem;
359+ border: 1px solid var(--color-border);
360+ margin-bottom: 1.5rem;
361+ }
362+ .rejected-section h3 {
363+ font-size: 0.9rem;
364+ font-weight: 600;
365+ margin-bottom: 1rem;
366+ color: var(--color-failed);
367+ }
368+ .rejected-item {
369+ font-size: 0.8rem;
370+ padding: 8px 10px;
371+ background: var(--color-failed-bg);
372+ border-radius: 6px;
373+ margin-bottom: 4px;
374+ border-left: 3px solid var(--color-failed);
375+ }
376+ .rejected-id {
377+ font-family: 'JetBrains Mono', monospace;
378+ font-weight: 600;
379+ color: var(--color-failed);
380+ }
381+ .rejected-title {
382+ color: var(--color-text);
383+ }
384+ .rejected-reason {
385+ font-size: 0.75rem;
386+ color: var(--color-text-muted);
387+ margin-top: 4px;
388+ font-style: italic;
389+ }
390+
298391 /* Error */
299392 .error-card {
300393 background: var(--color-failed-bg);
@@ -378,8 +471,26 @@ .breakdown h3 {
378471 {{ BuildBody ( jsonData , isTerminal , workspaceRoot ) }}
379472 </div>
380473 <script>
381- // VS Code Live Preview auto-reloads when the file changes on disk.
382- // No custom polling needed — the CLI rewrites this HTML on every progress update.
474+ // Auto-refresh: reload page every 1.5s while status is not terminal.
475+ // Uses JavaScript instead of <meta refresh> for reliable file:// support.
476+ (function() {
477+ var isTerminal = {{ ( isTerminal ? "true" : "false" ) }} ;
478+ if (!isTerminal) {
479+ setInterval(function() {
480+ window.location.reload();
481+ }, 1500);
482+ }
483+ })();
484+
485+ // File links: open vscode:// URIs via JavaScript click handler
486+ // (browsers block direct <a href="vscode://..."> from file:// pages)
487+ document.addEventListener('click', function(e) {
488+ var link = e.target.closest('[data-vscode-uri]');
489+ if (link) {
490+ e.preventDefault();
491+ window.location.href = link.getAttribute('data-vscode-uri');
492+ }
493+ });
383494 </script>
384495 </body>
385496 </html>
@@ -429,6 +540,12 @@ private static string BuildBody(string jsonData, bool isTerminal, string workspa
429540 }
430541 }
431542
543+ // Verification progress
544+ if ( root . TryGetProperty ( "verification" , out var verification ) && verification . GetArrayLength ( ) > 0 )
545+ {
546+ sb . Append ( BuildVerificationSection ( verification ) ) ;
547+ }
548+
432549 // Docs index summary cards
433550 if ( root . TryGetProperty ( "documents_total" , out var dt2 ) )
434551 {
@@ -518,6 +635,12 @@ private static string BuildBody(string jsonData, bool isTerminal, string workspa
518635 """ ) ;
519636 }
520637
638+ // Rejected tests
639+ if ( root . TryGetProperty ( "rejected_tests" , out var rejectedTests ) && rejectedTests . GetArrayLength ( ) > 0 )
640+ {
641+ sb . Append ( BuildRejectedTestsSection ( rejectedTests ) ) ;
642+ }
643+
521644 // Files created
522645 if ( root . TryGetProperty ( "files_created" , out var files ) && files . GetArrayLength ( ) > 0 )
523646 {
@@ -531,7 +654,7 @@ private static string BuildBody(string jsonData, bool isTerminal, string workspa
531654 <div class="footer">
532655 <span class="refresh-indicator">
533656 <span class="refresh-dot"></span>
534- Auto-refreshing every 2 seconds
657+ Auto-refreshing every 1.5 seconds
535658 </span>
536659 </div>
537660 """ ) ;
@@ -759,9 +882,74 @@ private static string BuildFilesSection(System.Text.Json.JsonElement files, stri
759882 {
760883 var relativePath = file . GetString ( ) ?? "" ;
761884 var fullPath = Path . GetFullPath ( Path . Combine ( workspaceRoot , relativePath ) ) . Replace ( '\\ ' , '/' ) ;
762- var vscodeUri = $ "vscode://file/{ fullPath } ";
885+ var vscodeUri = $ "vscode://file/{ Uri . EscapeDataString ( fullPath ) . Replace ( "%2F" , "/" ) } ";
886+
887+ sb . Append ( $ """ <a class="file-item file-link" href="{ Escape ( vscodeUri ) } " data-vscode-uri="{ Escape ( vscodeUri ) } " title="Open in VS Code"><span class="file-icon">📄</span>{ Escape ( relativePath ) } </a>""" ) ;
888+ }
889+
890+ sb . Append ( "</div>" ) ;
891+ return sb . ToString ( ) ;
892+ }
893+
894+ private static string BuildVerificationSection ( System . Text . Json . JsonElement verification )
895+ {
896+ var sb = new StringBuilder ( ) ;
897+ sb . Append ( """<div class="verification-section"><h3>Critic Verification</h3><div class="verification-list">""" ) ;
898+
899+ foreach ( var test in verification . EnumerateArray ( ) )
900+ {
901+ var id = test . TryGetProperty ( "id" , out var idProp ) ? idProp . GetString ( ) ?? "" : "" ;
902+ var title = test . TryGetProperty ( "title" , out var titleProp ) ? titleProp . GetString ( ) ?? "" : "" ;
903+ var verdict = test . TryGetProperty ( "verdict" , out var verdictProp ) ? verdictProp . GetString ( ) ?? "" : "" ;
904+ var score = test . TryGetProperty ( "score" , out var scoreProp ) ? scoreProp . GetDouble ( ) : 0 ;
905+ var reason = test . TryGetProperty ( "reason" , out var reasonProp ) ? reasonProp . GetString ( ) : null ;
763906
764- sb . Append ( $ """ <a class="file-item file-link" href="{ Escape ( vscodeUri ) } " title="Open in VS Code"><span class="file-icon">📄</span>{ Escape ( relativePath ) } </a>""" ) ;
907+ var verdictClass = verdict switch
908+ {
909+ "grounded" => "verdict-grounded" ,
910+ "partial" => "verdict-partial" ,
911+ "hallucinated" => "verdict-hallucinated" ,
912+ _ => "verdict-unknown"
913+ } ;
914+
915+ var verdictIcon = verdict switch
916+ {
917+ "grounded" => "✓" , // checkmark
918+ "partial" => "○" , // circle
919+ "hallucinated" => "✗" , // cross
920+ _ => "?"
921+ } ;
922+
923+ sb . Append ( $ """ <div class="verification-item { verdictClass } ">""" ) ;
924+ sb . Append ( $ """ <span class="verdict-icon">{ verdictIcon } </span>""" ) ;
925+ sb . Append ( $ """ <span class="verdict-id">{ Escape ( id ) } </span>""" ) ;
926+ sb . Append ( $ """ <span class="verdict-title">{ Escape ( title ) } </span>""" ) ;
927+ sb . Append ( $ """ <span class="verdict-badge">{ Escape ( verdict ) } </span>""" ) ;
928+ if ( ! string . IsNullOrEmpty ( reason ) )
929+ sb . Append ( $ """ <div class="verdict-reason">{ Escape ( reason ) } </div>""" ) ;
930+ sb . Append ( "</div>" ) ;
931+ }
932+
933+ sb . Append ( "</div></div>" ) ;
934+ return sb . ToString ( ) ;
935+ }
936+
937+ private static string BuildRejectedTestsSection ( System . Text . Json . JsonElement rejected )
938+ {
939+ var sb = new StringBuilder ( ) ;
940+ sb . Append ( """<div class="rejected-section"><h3>Rejected by Critic</h3>""" ) ;
941+
942+ foreach ( var test in rejected . EnumerateArray ( ) )
943+ {
944+ var id = test . TryGetProperty ( "id" , out var idProp ) ? idProp . GetString ( ) ?? "" : "" ;
945+ var title = test . TryGetProperty ( "title" , out var titleProp ) ? titleProp . GetString ( ) ?? "" : "" ;
946+ var verdict = test . TryGetProperty ( "verdict" , out var verdictProp ) ? verdictProp . GetString ( ) ?? "" : "" ;
947+ var reason = test . TryGetProperty ( "reason" , out var reasonProp ) ? reasonProp . GetString ( ) : null ;
948+
949+ sb . Append ( $ """ <div class="rejected-item"><span class="rejected-id">{ Escape ( id ) } </span> <span class="rejected-title">{ Escape ( title ) } </span>""" ) ;
950+ if ( ! string . IsNullOrEmpty ( reason ) )
951+ sb . Append ( $ """ <div class="rejected-reason">{ Escape ( reason ) } </div>""" ) ;
952+ sb . Append ( "</div>" ) ;
765953 }
766954
767955 sb . Append ( "</div>" ) ;
0 commit comments