PageSourceSearch

https://haiphen.io/components/featured/context-causal.js?v=1

js haiphen.io collected 2026-10-01 12:02:18 UTC 3,158 bytes, 79 lines download raw bytes

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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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&hellip;</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.