@@ -2747,6 +2747,44 @@ def _enhance_comparison_to_unified_viewer(
27472747 window.comparisonData = comparisonData;
27482748 }}
27492749
2750+ // Parse model output for SoM actions
2751+ window.parseModelOutput = function(rawOutput) {{
2752+ if (!rawOutput) return {{ html: '<em style="color:var(--text-muted);">No prediction</em>' }};
2753+
2754+ // Try to extract SoM actions: CLICK([N]), TYPE([N], "text"), TYPE("text")
2755+ const clickSomMatch = rawOutput.match(/CLICK\\ s*\\ (\\ s*\\ [\\ s*(\\ d+)\\ s*\\ ]\\ s*\\ )/);
2756+ const typeSomMatch = rawOutput.match(/TYPE\\ s*\\ (\\ s*\\ [\\ s*(\\ d+)\\ s*\\ ]\\ s*,\\ s*["']([^"']*)["']\\ s*\\ )/);
2757+ const typeSimpleMatch = rawOutput.match(/TYPE\\ s*\\ (\\ s*["']([^"']*)["']\\ s*\\ )/);
2758+ const clickCoordMatch = rawOutput.match(/CLICK\\ s*\\ (\\ s*x\\ s*=\\ s*([\\ d.]+)\\ s*,\\ s*y\\ s*=\\ s*([\\ d.]+)\\ s*\\ )/);
2759+
2760+ let html = '';
2761+
2762+ if (clickSomMatch) {{
2763+ html = `<div style="font-weight:600;color:#00d4aa;">CLICK([${{clickSomMatch[1]}}])</div>`;
2764+ }} else if (typeSomMatch) {{
2765+ html = `<div style="font-weight:600;color:#00d4aa;">TYPE([${{typeSomMatch[1]}}], "${{typeSomMatch[2]}}")</div>`;
2766+ }} else if (typeSimpleMatch) {{
2767+ html = `<div style="font-weight:600;color:#00d4aa;">TYPE("${{typeSimpleMatch[1]}}")</div>`;
2768+ }} else if (clickCoordMatch) {{
2769+ html = `<div style="font-weight:600;color:#00d4aa;">CLICK(x=${{clickCoordMatch[1]}}, y=${{clickCoordMatch[2]}})</div>`;
2770+ }} else {{
2771+ // No structured action - show truncated output
2772+ const truncated = rawOutput.replace(/\\ n/g, ' ').substring(0, 150);
2773+ html = `<div style="font-size:0.85rem;color:var(--text-muted);max-height:60px;overflow:hidden;">${{truncated}}${{rawOutput.length > 150 ? '...' : ''}}</div>`;
2774+ }}
2775+
2776+ return {{ html }};
2777+ }};
2778+
2779+ // Override prediction display in comparison viewer
2780+ window.formatPrediction = function(pred) {{
2781+ if (!pred) return '<em style="color:var(--text-muted);">No prediction</em>';
2782+ if (pred.x !== undefined) {{
2783+ return `<div>Type: ${{pred.type || 'click'}}</div><div>Position: (${{(pred.x * 100).toFixed(1)}}%, ${{(pred.y * 100).toFixed(1)}}%)</div>`;
2784+ }}
2785+ return window.parseModelOutput(pred.raw_output || JSON.stringify(pred)).html;
2786+ }};
2787+
27502788 // Use window. prefix for cross-script variable access
27512789 window.predictionsByCheckpoint = { predictions_json } ;
27522790 window.availableCaptures = { captures_json } ;
@@ -2813,6 +2851,18 @@ def _enhance_comparison_to_unified_viewer(
28132851 updateComparison(idx);
28142852 }}
28152853
2854+ // Reformat prediction display after original updateComparison runs
2855+ setTimeout(() => {{
2856+ const predEl = document.getElementById('predicted-action') ||
2857+ document.querySelector('.action-box.predicted .action-details');
2858+ if (predEl && window.comparisonData && window.comparisonData[idx]) {{
2859+ const pred = window.comparisonData[idx].predicted_action;
2860+ if (pred) {{
2861+ predEl.innerHTML = window.formatPrediction(pred);
2862+ }}
2863+ }}
2864+ }}, 50);
2865+
28162866 // Update metrics if setupMetricsSummary exists
28172867 if (typeof window.setupMetricsSummary === 'function') {{
28182868 window.setupMetricsSummary();
0 commit comments