1/** 2 * Context Panel â Causal Tab 3 * 4 * Renders a CSS-only vertical timeline of the causal DAG 5 * (root causes â propagation â counterfactuals). No external libraries. 6 */ 7(function () { 8 "use strict"; 9 10 var _currentEntityId = null; 11 12 function esc(s) { 13 return String(s ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); 14 } 15 16 function severityLabel(sev) { 17 var s = (sev || "").toLowerCase(); 18 var cls = s === "high" ? "color:#EF4444" : s === "medium" ? "color:#F59E0B" : "color:#10B981"; 19 return '<span class="hp-obs__causal-sev" style="' + cls + '">' + esc(sev) + '</span>'; 20 } 21 22 function renderTimeline(pane, dag) { 23 if (!dag || !dag.nodes || dag.nodes.length === 0) { 24 pane.innerHTML = '<div style="padding:24px;color:#94a3b8;font-size:.82rem;text-align:center">No causal chain data available for this entity.</div>'; 25 return; 26 } 27 28 var html = '<div class="hp-obs__causal-timeline">'; 29 30 // Nodes sorted: root_cause first, then propagation, then counterfactuals 31 var typeOrder = { root_cause: 0, propagation: 1, counterfactual: 2 }; 32 var sorted = dag.nodes.slice().sort(function (a, b) { 33 return (typeOrder[a.type] || 1) - (typeOrder[b.type] || 1); 34 }); 35 36 for (var i = 0; i < sorted.length; i++) { 37 var node = sorted[i]; 38 html += '<div class="hp-obs__causal-node hp-obs__causal-node--' + esc(node.type) + '">' 39 + '<div class="hp-obs__causal-event">' + esc(node.event) + severityLabel(node.severity) + '</div>' 40 + '</div>'; 41 } 42 43 // Counterfactuals 44 if (dag.counterfactuals && dag.counterfactuals.length > 0) { 45 html += '<div style="margin-top:12px;padding-top:8px;border-top:1px dashed rgba(15,23,42,.12)">' 46 + '<div style="font-weight:800;font-size:.72rem;color:#8B5CF6;text-transform:uppercase;letter-spacing:.03em;margin-bottom:6px">Counterfactuals</div>'; 47 for (var c = 0; c < dag.counterfactuals.length; c++) { 48 html += '<div class="hp-obs__causal-node hp-obs__causal-node--counterfactual">' 49 + '<div class="hp-obs__causal-event" style="font-style:italic">' + esc(dag.counterfactuals[c]) + '</div>' 50 + '</div>'; 51 } 52 html += '</div>'; 53 } 54 55 html += '</div>'; 56 pane.innerHTML = html; 57 } 58 59 function render(pane, entity, apiFetchJson) { 60 if (!pane || !entity) return; 61 62 if (_currentEntityId === entity.entity_id && pane.querySelector(".hp-obs__causal-timeline")) return; 63 _currentEntityId = entity.entity_id; 64 65 pane.innerHTML = '<div style="padding:24px;color:#94a3b8;font-size:.82rem;text-align:center">Loading causal chain…</div>'; 66 67 apiFetchJson("/v1/featured/entities/" + encodeURIComponent(entity.entity_id) + "/causal", function (err, data) { 68 if (err || !data) { 69 pane.innerHTML = '<div style="padding:24px;color:#94a3b8;font-size:.82rem;text-align:center">Causal data unavailable.</div>'; 70 return; 71 } 72 renderTimeline(pane, data.dag); 73 }); 74 } 75 76 window.HAIPHEN = window.HAIPHEN || {}; 77 window.HAIPHEN.Featured = window.HAIPHEN.Featured || {}; 78 window.HAIPHEN.Featured.ContextCausal = { render: render }; 79})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.