causal-inspector 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { INSPECTOR_EVENTS, INSPECTOR_CAUSAL_TREE, INSPECTOR_CAUSAL_FLOW, INSPECTOR_CORRELATIONS, INSPECTOR_REACTOR_DEPENDENCIES, INSPECTOR_AGGREGATE_KEYS, INSPECTOR_AGGREGATE_LIFECYCLE, INSPECTOR_REACTOR_LOGS_BY_CORRELATION, INSPECTOR_REACTOR_DESCRIPTIONS, INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS, INSPECTOR_AGGREGATE_TIMELINE, INSPECTOR_REACTOR_OUTCOMES, } from "../queries";
1
+ import { INSPECTOR_EVENTS, INSPECTOR_CAUSAL_TREE, INSPECTOR_CAUSAL_FLOW, INSPECTOR_CORRELATIONS, INSPECTOR_REACTOR_DEPENDENCIES, INSPECTOR_AGGREGATE_KEYS, INSPECTOR_AGGREGATE_LIFECYCLE, INSPECTOR_REACTOR_LOGS_BY_CORRELATION, INSPECTOR_REACTOR_DESCRIPTIONS, INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS, INSPECTOR_AGGREGATE_TIMELINE, INSPECTOR_REACTOR_OUTCOMES, INSPECTOR_REACTOR_ATTEMPTS, } from "../queries";
2
2
  /**
3
3
  * Query engine — fetches data in response to state transitions.
4
4
  *
@@ -69,11 +69,12 @@ export const createQueryEngine = (transport) => {
69
69
  };
70
70
  const fetchFlowMetadata = async (correlationId) => {
71
71
  try {
72
- const [descData, snapshotData, aggTimelineData, outcomeData] = await Promise.all([
72
+ const [descData, snapshotData, aggTimelineData, outcomeData, attemptData] = await Promise.all([
73
73
  transport.query(INSPECTOR_REACTOR_DESCRIPTIONS, { correlationId }),
74
74
  transport.query(INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS, { correlationId }),
75
75
  transport.query(INSPECTOR_AGGREGATE_TIMELINE, { correlationId }),
76
76
  transport.query(INSPECTOR_REACTOR_OUTCOMES, { correlationId }),
77
+ transport.query(INSPECTOR_REACTOR_ATTEMPTS, { correlationId }),
77
78
  ]);
78
79
  if (activeFlowCorrelationId !== correlationId)
79
80
  return; // stale
@@ -105,6 +106,13 @@ export const createQueryEngine = (transport) => {
105
106
  outcomes: outcomeData.inspectorReactorOutcomes,
106
107
  },
107
108
  });
109
+ dispatch({
110
+ type: "events/attempts_loaded",
111
+ payload: {
112
+ correlationId,
113
+ attempts: attemptData.inspectorReactorAttempts,
114
+ },
115
+ });
108
116
  }
109
117
  catch (e) {
110
118
  console.error("[causal-inspector] fetch flow metadata failed:", e);
@@ -119,23 +127,18 @@ export const createQueryEngine = (transport) => {
119
127
  console.error("[causal-inspector] fetch logs failed:", e);
120
128
  }
121
129
  };
122
- let correlationCursor = null;
123
130
  const fetchCorrelations = async (opts) => {
124
- const append = opts?.append ?? false;
125
- const cursor = append ? correlationCursor : undefined;
126
131
  try {
127
132
  const data = await transport.query(INSPECTOR_CORRELATIONS, {
128
133
  search: opts?.search || undefined,
129
- limit: 50,
130
- cursor: cursor || undefined,
134
+ limit: 100,
131
135
  });
132
- correlationCursor = data.inspectorCorrelations.nextCursor;
133
136
  dispatch({
134
137
  type: "events/correlations_loaded",
135
138
  payload: {
136
- correlations: data.inspectorCorrelations.correlations,
137
- hasMore: data.inspectorCorrelations.nextCursor != null,
138
- append,
139
+ correlations: data.inspectorCorrelations,
140
+ hasMore: false,
141
+ append: false,
139
142
  },
140
143
  });
141
144
  }
@@ -229,10 +232,9 @@ export const createQueryEngine = (transport) => {
229
232
  fetchEvents();
230
233
  break;
231
234
  case "ui/load_more_correlations_requested":
232
- fetchCorrelations({ append: true });
235
+ fetchCorrelations();
233
236
  break;
234
237
  case "ui/correlations_requested":
235
- correlationCursor = null;
236
238
  fetchCorrelations({ search: event.payload.search });
237
239
  stopCorrelationPolling();
238
240
  correlationPollTimer = setInterval(() => fetchCorrelations({ search: event.payload.search }), 5000);
package/dist/events.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { BaseEvent } from "./machine";
2
- import type { InspectorEvent, CorrelationSummary, ReactorDependency, AggregateLifecycleEntry, FilterState, FlowSelection, ReactorDescription, ReactorDescriptionSnapshot, AggregateTimelineEntry, ReactorLog, ReactorOutcome, PaneLayout } from "./types";
2
+ import type { InspectorEvent, CorrelationSummary, ReactorDependency, AggregateLifecycleEntry, FilterState, FlowSelection, ReactorDescription, ReactorDescriptionSnapshot, AggregateTimelineEntry, ReactorLog, ReactorOutcome, ReactorAttempt, PaneLayout } from "./types";
3
3
  type EventsReceived = BaseEvent<"events/received", InspectorEvent[]>;
4
4
  type SubscriptionConnected = BaseEvent<"events/subscription_connected">;
5
5
  type SubscriptionError = BaseEvent<"events/subscription_error", {
@@ -31,6 +31,10 @@ type OutcomesLoaded = BaseEvent<"events/outcomes_loaded", {
31
31
  correlationId: string;
32
32
  outcomes: ReactorOutcome[];
33
33
  }>;
34
+ type AttemptsLoaded = BaseEvent<"events/attempts_loaded", {
35
+ correlationId: string;
36
+ attempts: ReactorAttempt[];
37
+ }>;
34
38
  type CorrelationsLoaded = BaseEvent<"events/correlations_loaded", {
35
39
  correlations: CorrelationSummary[];
36
40
  hasMore: boolean;
@@ -78,5 +82,5 @@ type LocationChanged = BaseEvent<"location/changed", {
78
82
  correlationId: string | null;
79
83
  handler: string | null;
80
84
  }>;
81
- export type InspectorMachineEvent = EventsReceived | SubscriptionConnected | SubscriptionError | PageLoaded | CausalTreeLoaded | FlowLoaded | LogsLoaded | DescriptionsLoaded | DescriptionSnapshotsLoaded | AggregateTimelineLoaded | OutcomesLoaded | CorrelationsLoaded | EventSelected | EventDeselected | FlowOpened | FlowClosed | FlowNodeSelected | FilterChanged | LoadMoreRequested | LayoutChanged | ScrubberStartChanged | ScrubberEndChanged | ScrubberPlayToggled | ScrubberSpeedChanged | CorrelationsRequested | ReactorDependenciesLoaded | AggregateKeysLoaded | HandlerSelected | LocationChanged | AggregateLifecycleLoaded | AggregateLifecycleRequested | LoadMoreCorrelationsRequested;
85
+ export type InspectorMachineEvent = EventsReceived | SubscriptionConnected | SubscriptionError | PageLoaded | CausalTreeLoaded | FlowLoaded | LogsLoaded | DescriptionsLoaded | DescriptionSnapshotsLoaded | AggregateTimelineLoaded | OutcomesLoaded | AttemptsLoaded | CorrelationsLoaded | EventSelected | EventDeselected | FlowOpened | FlowClosed | FlowNodeSelected | FilterChanged | LoadMoreRequested | LayoutChanged | ScrubberStartChanged | ScrubberEndChanged | ScrubberPlayToggled | ScrubberSpeedChanged | CorrelationsRequested | ReactorDependenciesLoaded | AggregateKeysLoaded | HandlerSelected | LocationChanged | AggregateLifecycleLoaded | AggregateLifecycleRequested | LoadMoreCorrelationsRequested;
82
86
  export {};
package/dist/index.d.ts CHANGED
@@ -5,10 +5,10 @@ export { reducer } from "./reducer";
5
5
  export { initialState } from "./state";
6
6
  export type { InspectorState } from "./state";
7
7
  export type { InspectorMachineEvent } from "./events";
8
- export type { InspectorEvent, InspectorEventsPage, InspectorCausalTree, InspectorCausalFlow, CorrelationSummary, ReactorDependency, AggregateLifecycleEntry, Block, ReactorLog, ReactorDescription, ReactorDescriptionSnapshot, AggregateStateEntry, AggregateTimelineEntry, ReactorOutcome, FilterState, LogsFilter, FlowSelection, PaneLayout, } from "./types";
8
+ export type { InspectorEvent, InspectorEventsPage, InspectorCausalTree, InspectorCausalFlow, CorrelationSummary, ReactorDependency, AggregateLifecycleEntry, Block, ReactorLog, ReactorDescription, ReactorDescriptionSnapshot, AggregateStateEntry, AggregateTimelineEntry, ReactorOutcome, ReactorAttempt, FilterState, LogsFilter, FlowSelection, PaneLayout, } from "./types";
9
9
  export { createInspectorEngine, createSubscriptionEngine, createQueryEngine, createStorageEngine, } from "./engines";
10
10
  export type { InspectorTransport, SubscriptionTransport, QueryTransport, StorageTransport, } from "./engines";
11
- export { EVENTS_SUBSCRIPTION, INSPECTOR_EVENTS, INSPECTOR_CAUSAL_TREE, INSPECTOR_CAUSAL_FLOW, INSPECTOR_CORRELATIONS, INSPECTOR_REACTOR_LOGS, INSPECTOR_REACTOR_LOGS_BY_CORRELATION, INSPECTOR_REACTOR_DESCRIPTIONS, INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS, INSPECTOR_AGGREGATE_TIMELINE, INSPECTOR_REACTOR_OUTCOMES, INSPECTOR_REACTOR_DEPENDENCIES, INSPECTOR_AGGREGATE_KEYS, INSPECTOR_AGGREGATE_LIFECYCLE, } from "./queries";
11
+ export { EVENTS_SUBSCRIPTION, INSPECTOR_EVENTS, INSPECTOR_CAUSAL_TREE, INSPECTOR_CAUSAL_FLOW, INSPECTOR_CORRELATIONS, INSPECTOR_REACTOR_LOGS, INSPECTOR_REACTOR_LOGS_BY_CORRELATION, INSPECTOR_REACTOR_DESCRIPTIONS, INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS, INSPECTOR_AGGREGATE_TIMELINE, INSPECTOR_REACTOR_OUTCOMES, INSPECTOR_REACTOR_ATTEMPTS, INSPECTOR_REACTOR_DEPENDENCIES, INSPECTOR_AGGREGATE_KEYS, INSPECTOR_AGGREGATE_LIFECYCLE, } from "./queries";
12
12
  export { eventHue, eventBg, eventBorder, eventTextColor, LOG_LEVEL_COLORS, } from "./theme";
13
13
  export { formatTs, compactPayload, copyToClipboard } from "./utils";
14
14
  export { CausalInspector } from "./CausalInspector";
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ export { initialState } from "./state";
7
7
  // ── Engines ──
8
8
  export { createInspectorEngine, createSubscriptionEngine, createQueryEngine, createStorageEngine, } from "./engines";
9
9
  // ── Queries ──
10
- export { EVENTS_SUBSCRIPTION, INSPECTOR_EVENTS, INSPECTOR_CAUSAL_TREE, INSPECTOR_CAUSAL_FLOW, INSPECTOR_CORRELATIONS, INSPECTOR_REACTOR_LOGS, INSPECTOR_REACTOR_LOGS_BY_CORRELATION, INSPECTOR_REACTOR_DESCRIPTIONS, INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS, INSPECTOR_AGGREGATE_TIMELINE, INSPECTOR_REACTOR_OUTCOMES, INSPECTOR_REACTOR_DEPENDENCIES, INSPECTOR_AGGREGATE_KEYS, INSPECTOR_AGGREGATE_LIFECYCLE, } from "./queries";
10
+ export { EVENTS_SUBSCRIPTION, INSPECTOR_EVENTS, INSPECTOR_CAUSAL_TREE, INSPECTOR_CAUSAL_FLOW, INSPECTOR_CORRELATIONS, INSPECTOR_REACTOR_LOGS, INSPECTOR_REACTOR_LOGS_BY_CORRELATION, INSPECTOR_REACTOR_DESCRIPTIONS, INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS, INSPECTOR_AGGREGATE_TIMELINE, INSPECTOR_REACTOR_OUTCOMES, INSPECTOR_REACTOR_ATTEMPTS, INSPECTOR_REACTOR_DEPENDENCIES, INSPECTOR_AGGREGATE_KEYS, INSPECTOR_AGGREGATE_LIFECYCLE, } from "./queries";
11
11
  // ── Theme ──
12
12
  export { eventHue, eventBg, eventBorder, eventTextColor, LOG_LEVEL_COLORS, } from "./theme";
13
13
  // ── Utilities ──
@@ -427,7 +427,7 @@ function ReactorFilter({ allReactorIds, hiddenReactors, setHiddenReactors }) {
427
427
  ? allReactorIds.filter(id => id.toLowerCase().includes(filter.toLowerCase()))
428
428
  : allReactorIds;
429
429
  const hiddenCount = hiddenReactors.size;
430
- return (_jsxs("div", { ref: containerRef, className: "relative", children: [_jsxs("button", { onClick: () => setOpen(v => !v), className: "text-[10px] text-muted-foreground/60 hover:text-foreground px-2 py-1 rounded-md border border-border hover:border-indigo-500/30 transition-all duration-150", children: [_jsx(Filter, { size: 11, className: "inline mr-1 -mt-px" }), hiddenCount > 0 ? `${hiddenCount} hidden` : "Filter"] }), open && (_jsxs("div", { className: "absolute top-full right-0 mt-1 z-50 border border-border rounded-lg min-w-[240px]", style: { background: "rgba(17, 17, 22, 0.95)", backdropFilter: "blur(12px)", boxShadow: "0 8px 32px rgba(0, 0, 0, 0.5)" }, children: [_jsx("div", { className: "px-3 py-2 border-b border-border", children: _jsx("input", { autoFocus: true, type: "text", value: filter, onChange: e => setFilter(e.target.value), placeholder: "Search reactors...", className: "w-full text-xs bg-transparent border-none outline-none text-foreground placeholder:text-muted-foreground/50" }) }), _jsxs("div", { className: "max-h-64 overflow-y-auto py-1", children: [filtered.map(id => (_jsxs("label", { className: "flex items-center gap-2 px-3 py-1.5 hover:bg-white/[0.03] cursor-pointer transition-colors", children: [_jsx("input", { type: "checkbox", checked: !hiddenReactors.has(id), onChange: () => toggle(id), className: "rounded border-border accent-indigo-500" }), _jsx("span", { className: "text-[11px] font-mono text-foreground/80 truncate", children: id })] }, id))), filtered.length === 0 && (_jsx("div", { className: "text-xs text-muted-foreground/50 px-3 py-2", children: "No matches" }))] })] }))] }));
430
+ return (_jsxs("div", { ref: containerRef, style: { position: "relative" }, children: [_jsxs("button", { onClick: () => setOpen(v => !v), className: "ci-toolbar-btn", children: [_jsx(Filter, { size: 11, style: { display: "inline", marginRight: 4, verticalAlign: "-1px" } }), hiddenCount > 0 ? `${hiddenCount} hidden` : "Filter"] }), open && (_jsxs("div", { className: "ci-dropdown", style: { position: "absolute", top: "100%", right: 0, marginTop: 4, zIndex: 50, minWidth: 240 }, children: [_jsx("div", { style: { padding: "8px 12px", borderBottom: "1px solid var(--ci-border)" }, children: _jsx("input", { autoFocus: true, type: "text", value: filter, onChange: e => setFilter(e.target.value), placeholder: "Search reactors...", style: { width: "100%", fontSize: 12, background: "transparent", border: "none", outline: "none", color: "var(--ci-text)" } }) }), _jsxs("div", { style: { maxHeight: 256, overflowY: "auto", padding: "4px 0" }, children: [filtered.map(id => (_jsxs("label", { className: "ci-row", style: { gap: 8, padding: "6px 12px", cursor: "pointer", transition: "background 150ms" }, children: [_jsx("input", { type: "checkbox", checked: !hiddenReactors.has(id), onChange: () => toggle(id), style: { accentColor: "var(--ci-accent)" } }), _jsx("span", { className: "ci-mono ci-truncate", style: { fontSize: 11, color: "var(--ci-text)", opacity: 0.8 }, children: id })] }, id))), filtered.length === 0 && (_jsx("div", { style: { fontSize: 12, color: "var(--ci-text-muted)", opacity: 0.5, padding: "8px 12px" }, children: "No matches" }))] })] }))] }));
431
431
  }
432
432
  export function CausalFlowPane({ defaultHiddenReactors, headerExtra } = {}) {
433
433
  const flowCorrelationId = useSelector((s) => s.flowCorrelationId);
@@ -591,10 +591,10 @@ export function CausalFlowPane({ defaultHiddenReactors, headerExtra } = {}) {
591
591
  }, [dispatch]);
592
592
  const onNodesChange = useCallback((_changes) => { }, []);
593
593
  if (!flowCorrelationId) {
594
- return (_jsx("div", { className: "flex items-center justify-center h-full text-xs text-muted-foreground/50 tracking-wide", children: "Select an event to visualize its causal flow" }));
594
+ return (_jsx("div", { className: "ci-empty", children: "Select an event to visualize its causal flow" }));
595
595
  }
596
596
  if (flowLoading) {
597
- return (_jsxs("div", { className: "h-full flex flex-col", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 border-b border-border shrink-0", children: [_jsx("div", { className: "h-3 w-10 bg-muted rounded animate-pulse" }), _jsx("div", { className: "h-3 w-48 bg-muted rounded animate-pulse" })] }), _jsx("div", { className: "flex-1 flex items-center justify-center", children: _jsxs("div", { className: "animate-pulse flex flex-col items-center gap-3", children: [_jsx("div", { className: "h-8 w-40 bg-muted rounded-md" }), _jsx("div", { className: "h-6 w-px bg-muted" }), _jsx("div", { className: "h-6 w-28 bg-muted rounded-full" }), _jsxs("div", { className: "flex items-start gap-8", children: [_jsxs("div", { className: "flex flex-col items-center gap-3", children: [_jsx("div", { className: "h-6 w-px bg-muted" }), _jsx("div", { className: "h-8 w-36 bg-muted rounded-md" })] }), _jsxs("div", { className: "flex flex-col items-center gap-3", children: [_jsx("div", { className: "h-6 w-px bg-muted" }), _jsx("div", { className: "h-8 w-36 bg-muted rounded-md" })] })] })] }) })] }));
597
+ return (_jsxs("div", { className: "ci-col", children: [_jsxs("div", { className: "ci-row", style: { gap: 8, padding: "6px 12px", borderBottom: "1px solid var(--ci-border)", flexShrink: 0 }, children: [_jsx("div", { className: "ci-muted-bg ci-pulse", style: { height: 12, width: 40 } }), _jsx("div", { className: "ci-muted-bg ci-pulse", style: { height: 12, width: 192 } })] }), _jsx("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center" }, children: _jsxs("div", { className: "ci-pulse", style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [_jsx("div", { className: "ci-muted-bg", style: { height: 32, width: 160, borderRadius: "var(--ci-radius-md)" } }), _jsx("div", { className: "ci-muted-bg", style: { height: 24, width: 1 } }), _jsx("div", { className: "ci-muted-bg", style: { height: 24, width: 112, borderRadius: "var(--ci-radius-full)" } }), _jsxs("div", { style: { display: "flex", alignItems: "start", gap: 32 }, children: [_jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [_jsx("div", { className: "ci-muted-bg", style: { height: 24, width: 1 } }), _jsx("div", { className: "ci-muted-bg", style: { height: 32, width: 144, borderRadius: "var(--ci-radius-md)" } })] }), _jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [_jsx("div", { className: "ci-muted-bg", style: { height: 24, width: 1 } }), _jsx("div", { className: "ci-muted-bg", style: { height: 32, width: 144, borderRadius: "var(--ci-radius-md)" } })] })] })] }) })] }));
598
598
  }
599
- return (_jsxs("div", { className: "h-full flex flex-col", children: [_jsxs("div", { className: "flex items-center gap-2.5 px-3 py-2 border-b border-border shrink-0", style: { background: "rgba(15, 15, 20, 0.6)", backdropFilter: "blur(8px)" }, children: [_jsx("h3", { className: "text-[10px] font-semibold text-muted-foreground/60 uppercase tracking-widest", children: "Flow" }), _jsx("span", { className: "text-[10px] font-mono text-foreground/80 truncate px-1.5 py-0.5 rounded bg-white/[0.03] border border-border", children: flowCorrelationId }), _jsxs("span", { className: "text-[10px] text-muted-foreground/50 tabular-nums", children: [flowData.length, " events \u00B7 ", nodes.length, " nodes"] }), headerExtra, _jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [_jsx("button", { onClick: () => setDirection(d => d === "LR" ? "TB" : "LR"), className: "text-[10px] text-muted-foreground/60 hover:text-foreground px-2 py-1 rounded-md border border-border hover:border-indigo-500/30 transition-all duration-150", title: direction === "LR" ? "Switch to vertical layout" : "Switch to horizontal layout", children: direction === "LR" ? _jsx(ArrowDown, { size: 11, className: "inline" }) : _jsx(ArrowRight, { size: 11, className: "inline" }) }), _jsx(ReactorFilter, { allReactorIds: allReactorIds, hiddenReactors: hiddenReactors, setHiddenReactors: setHiddenReactors })] })] }), _jsx("div", { className: "flex-1 relative", children: _jsxs(ReactFlow, { nodes: nodes, edges: edges, nodeTypes: nodeTypes, defaultEdgeOptions: { type: "smoothstep" }, onNodesChange: onNodesChange, onNodeClick: onNodeClick, onPaneClick: onPaneClick, minZoom: 0.25, proOptions: { hideAttribution: true }, nodesDraggable: false, nodesConnectable: false, elevateNodesOnSelect: false, colorMode: "dark", children: [_jsx(FitOnLoad, {}), _jsx(FocusOnSelection, { nodes: nodes, flowData: flowData }), _jsx(Background, { color: "rgba(255,255,255,0.03)", gap: 24, size: 1 }), _jsx(Controls, { showInteractive: false })] }) })] }));
599
+ return (_jsxs("div", { className: "ci-col", children: [_jsxs("div", { className: "ci-row ci-surface", style: { gap: 10, padding: "8px 12px", borderBottom: "1px solid var(--ci-border)", flexShrink: 0 }, children: [_jsx("h3", { className: "ci-header", children: "Flow" }), _jsx("span", { className: "ci-mono ci-truncate", style: { fontSize: 10, color: "var(--ci-text)", opacity: 0.8, padding: "2px 6px", borderRadius: "var(--ci-radius-sm)", background: "rgba(255, 255, 255, 0.03)", border: "1px solid var(--ci-border)" }, children: flowCorrelationId }), _jsxs("span", { className: "ci-tabular", style: { fontSize: 10, color: "var(--ci-text-muted)", opacity: 0.5 }, children: [flowData.length, " events \u00B7 ", nodes.length, " nodes"] }), headerExtra, _jsxs("div", { className: "ci-row", style: { marginLeft: "auto", gap: 6 }, children: [_jsx("button", { onClick: () => setDirection(d => d === "LR" ? "TB" : "LR"), className: "ci-toolbar-btn", title: direction === "LR" ? "Switch to vertical layout" : "Switch to horizontal layout", children: direction === "LR" ? _jsx(ArrowDown, { size: 11, style: { display: "inline", verticalAlign: "-1px" } }) : _jsx(ArrowRight, { size: 11, style: { display: "inline", verticalAlign: "-1px" } }) }), _jsx(ReactorFilter, { allReactorIds: allReactorIds, hiddenReactors: hiddenReactors, setHiddenReactors: setHiddenReactors })] })] }), _jsx("div", { style: { flex: 1, position: "relative" }, children: _jsxs(ReactFlow, { nodes: nodes, edges: edges, nodeTypes: nodeTypes, defaultEdgeOptions: { type: "smoothstep" }, onNodesChange: onNodesChange, onNodeClick: onNodeClick, onPaneClick: onPaneClick, minZoom: 0.25, proOptions: { hideAttribution: true }, nodesDraggable: false, nodesConnectable: false, elevateNodesOnSelect: false, colorMode: "dark", children: [_jsx(FitOnLoad, {}), _jsx(FocusOnSelection, { nodes: nodes, flowData: flowData }), _jsx(Background, { color: "rgba(255,255,255,0.03)", gap: 24, size: 1 }), _jsx(Controls, { showInteractive: false })] }) })] }));
600
600
  }
@@ -4,7 +4,7 @@ import { useSelector, useDispatch } from "../machine";
4
4
  import { CopyablePayload } from "../components/CopyablePayload";
5
5
  import { eventTextColor } from "../theme";
6
6
  import { formatTs, compactPayload, copyToClipboard, inScrubberRange } from "../utils";
7
- import { Copy, Check, Search, X, ChevronRight, ChevronDown } from "lucide-react";
7
+ import { Copy, Check, Search, X, ChevronRight, ChevronDown, AlertTriangle } from "lucide-react";
8
8
  function buildTreeJson(roots, childrenMap) {
9
9
  function toNode(evt) {
10
10
  const children = evt.id ? (childrenMap.get(evt.id) ?? []) : [];
@@ -23,7 +23,7 @@ function buildTreeJson(roots, childrenMap) {
23
23
  // ---------------------------------------------------------------------------
24
24
  // ReactorNode — intermediate node grouping children by reactor_id
25
25
  // ---------------------------------------------------------------------------
26
- function ReactorNode({ reactorId, parentEventId, children, childrenMap, depth, isHighlighted, onClickReactor, }) {
26
+ function ReactorNode({ reactorId, parentEventId, children, childrenMap, depth, isHighlighted, onClickReactor, outcome, outcomesByReactor, }) {
27
27
  const [collapsed, setCollapsed] = useState(false);
28
28
  const nodeRef = useRef(null);
29
29
  useEffect(() => {
@@ -34,12 +34,24 @@ function ReactorNode({ reactorId, parentEventId, children, childrenMap, depth, i
34
34
  const handleClick = useCallback(() => {
35
35
  onClickReactor(reactorId, parentEventId);
36
36
  }, [onClickReactor, parentEventId, reactorId]);
37
- return (_jsxs("div", { className: depth > 0 ? "pl-6" : "", children: [_jsx("div", { ref: isHighlighted ? nodeRef : undefined, className: `group/tree w-full text-left px-2 py-1.5 rounded-md transition-all duration-150 hover:bg-white/[0.03] ${isHighlighted ? "bg-indigo-500/15 ring-1 ring-indigo-500/25" : ""}`, children: _jsxs("div", { className: "flex items-center gap-1.5 min-w-0", children: [_jsx("button", { onClick: (e) => { e.stopPropagation(); setCollapsed(v => !v); }, className: "text-[10px] text-muted-foreground hover:text-foreground shrink-0 w-3 text-center", children: collapsed ? _jsx(ChevronRight, { size: 10 }) : _jsx(ChevronDown, { size: 10 }) }), _jsxs("button", { onClick: handleClick, className: "flex items-center gap-1.5 min-w-0", children: [_jsx("span", { className: "px-1.5 py-0.5 rounded text-[9px] font-medium shrink-0 bg-white/[0.04] text-muted-foreground/60 italic border border-border", children: "reactor" }), _jsx("span", { className: "text-[10px] font-mono text-foreground/60 shrink-0", children: reactorId }), collapsed && (_jsxs("span", { className: "text-[10px] text-muted-foreground shrink-0", children: ["(", children.length, ")"] }))] })] }) }), !collapsed && children.map((child) => (_jsx(TreeNode, { event: child, childrenMap: childrenMap, depth: depth + 1, onClickReactor: onClickReactor }, child.seq)))] }));
37
+ const isError = outcome?.status === "error";
38
+ const isRunning = outcome?.status === "running";
39
+ return (_jsxs("div", { style: depth > 0 ? { paddingLeft: 24 } : undefined, children: [_jsxs("div", { ref: isHighlighted ? nodeRef : undefined, className: `ci-tree-node${isHighlighted ? " ci-selected" : ""}`, style: isError ? { background: "rgba(239, 68, 68, 0.08)" } : undefined, children: [_jsxs("div", { className: "ci-row", style: { gap: 6, minWidth: 0 }, children: [_jsx("button", { onClick: (e) => { e.stopPropagation(); setCollapsed(v => !v); }, style: { fontSize: 10, color: "var(--ci-text-muted)", background: "none", border: "none", cursor: "pointer", flexShrink: 0, width: 12, textAlign: "center", padding: 0, transition: "color 150ms" }, children: collapsed ? _jsx(ChevronRight, { size: 10 }) : _jsx(ChevronDown, { size: 10 }) }), _jsxs("button", { onClick: handleClick, className: "ci-row", style: { gap: 6, minWidth: 0, background: "none", border: "none", cursor: "pointer", padding: 0 }, children: [_jsx("span", { style: {
40
+ padding: "2px 6px",
41
+ borderRadius: "var(--ci-radius-sm)",
42
+ fontSize: 9,
43
+ fontWeight: 500,
44
+ flexShrink: 0,
45
+ fontStyle: "italic",
46
+ background: isError ? "rgba(239, 68, 68, 0.1)" : isRunning ? "rgba(234, 179, 8, 0.1)" : "rgba(255, 255, 255, 0.04)",
47
+ color: isError ? "rgba(248, 113, 113, 0.8)" : isRunning ? "rgba(234, 179, 8, 0.8)" : "var(--ci-text-dim)",
48
+ border: `1px solid ${isError ? "rgba(239, 68, 68, 0.2)" : isRunning ? "rgba(234, 179, 8, 0.2)" : "var(--ci-border)"}`,
49
+ }, children: "reactor" }), _jsx("span", { className: "ci-mono", style: { fontSize: 10, color: "var(--ci-text)", opacity: 0.6, flexShrink: 0 }, children: reactorId }), isError && (_jsxs("span", { className: "flex items-center gap-1 text-[9px] text-red-400/80 shrink-0", title: outcome.error ?? "Error", children: [_jsx(AlertTriangle, { size: 10 }), outcome.attempts > 1 && _jsxs("span", { children: ["x", outcome.attempts] })] })), collapsed && (_jsxs("span", { style: { fontSize: 10, color: "var(--ci-text-muted)", flexShrink: 0 }, children: ["(", children.length, ")"] }))] })] }), isError && outcome.error && (_jsx("div", { className: "mt-1 ml-7 text-[9px] text-red-400/70 truncate", title: outcome.error, children: outcome.error }))] }), !collapsed && children.map((child) => (_jsx(TreeNode, { event: child, childrenMap: childrenMap, depth: depth + 1, onClickReactor: onClickReactor, outcomesByReactor: outcomesByReactor }, child.seq)))] }));
38
50
  }
39
51
  // ---------------------------------------------------------------------------
40
52
  // TreeNode (recursive)
41
53
  // ---------------------------------------------------------------------------
42
- function TreeNode({ event, childrenMap, depth, onClickReactor, onInvestigate, }) {
54
+ function TreeNode({ event, childrenMap, depth, onClickReactor, onInvestigate, outcomesByReactor, }) {
43
55
  const selectedSeq = useSelector((s) => s.selectedSeq);
44
56
  const flowSelection = useSelector((s) => s.flowSelection);
45
57
  const dispatch = useDispatch();
@@ -72,19 +84,19 @@ function TreeNode({ event, childrenMap, depth, onClickReactor, onInvestigate, })
72
84
  return { reactorGroups: groups, directChildren: direct };
73
85
  }, [children]);
74
86
  const highlightedReactorId = flowSelection?.kind === "reactor" ? flowSelection.reactorId : null;
75
- return (_jsxs("div", { className: depth > 0 ? "pl-6" : "", children: [_jsxs("div", { ref: isSelected ? nodeRef : undefined, onClick: () => {
87
+ return (_jsxs("div", { style: depth > 0 ? { paddingLeft: 24 } : undefined, children: [_jsxs("div", { ref: isSelected ? nodeRef : undefined, onClick: () => {
76
88
  dispatch({ type: "ui/event_selected", payload: { seq: event.seq } });
77
89
  if (event.correlationId) {
78
90
  dispatch({ type: "ui/flow_opened", payload: { correlationId: event.correlationId } });
79
91
  }
80
- }, className: `group/tree w-full text-left px-2 py-1.5 rounded-md transition-all duration-150 cursor-pointer hover:bg-white/[0.03] ${isSelected ? "bg-indigo-500/15 ring-1 ring-indigo-500/25" : ""}`, children: [_jsxs("div", { className: "flex items-center gap-1.5 min-w-0", children: [hasChildren ? (_jsx("button", { onClick: (e) => { e.stopPropagation(); setCollapsed((v) => !v); }, className: "text-[10px] text-muted-foreground hover:text-foreground shrink-0 w-3 text-center", children: collapsed ? _jsx(ChevronRight, { size: 10 }) : _jsx(ChevronDown, { size: 10 }) })) : (_jsx("span", { className: "w-3 shrink-0" })), _jsx("span", { className: "text-[10px] font-mono shrink-0", style: { color: eventTextColor(event.name) }, children: event.name }), collapsed && hasChildren && (_jsxs("span", { className: "text-[10px] text-muted-foreground shrink-0", children: ["(", children.length, ")"] })), _jsx("span", { className: "text-[10px] text-muted-foreground shrink-0", children: formatTs(event.ts) }), _jsx("button", { onClick: (e) => {
92
+ }, className: `ci-tree-node${isSelected ? " ci-selected" : ""}`, style: { cursor: "pointer" }, children: [_jsxs("div", { className: "ci-row", style: { gap: 6, minWidth: 0 }, children: [hasChildren ? (_jsx("button", { onClick: (e) => { e.stopPropagation(); setCollapsed((v) => !v); }, style: { fontSize: 10, color: "var(--ci-text-muted)", background: "none", border: "none", cursor: "pointer", flexShrink: 0, width: 12, textAlign: "center", padding: 0, transition: "color 150ms" }, children: collapsed ? _jsx(ChevronRight, { size: 10 }) : _jsx(ChevronDown, { size: 10 }) })) : (_jsx("span", { style: { width: 12, flexShrink: 0 } })), _jsx("span", { className: "ci-mono", style: { fontSize: 10, flexShrink: 0, color: eventTextColor(event.name) }, children: event.name }), collapsed && hasChildren && (_jsxs("span", { style: { fontSize: 10, color: "var(--ci-text-muted)", flexShrink: 0 }, children: ["(", children.length, ")"] })), _jsx("span", { style: { fontSize: 10, color: "var(--ci-text-muted)", flexShrink: 0 }, children: formatTs(event.ts) }), _jsx("button", { onClick: (e) => {
81
93
  e.stopPropagation();
82
94
  const json = buildTreeJson([event], childrenMap);
83
95
  const text = JSON.stringify(json[0], null, 2);
84
96
  copyToClipboard(text);
85
97
  setCopied(true);
86
98
  setTimeout(() => setCopied(false), 1500);
87
- }, className: "opacity-0 group-hover/tree:opacity-100 transition-all duration-150 ml-auto p-1 rounded-md hover:bg-white/[0.05] shrink-0 text-[10px] text-muted-foreground/50", title: "Copy subtree as JSON", children: copied ? _jsx(Check, { size: 12 }) : _jsx(Copy, { size: 12 }) }), onInvestigate && (_jsx("button", { onClick: (e) => { e.stopPropagation(); onInvestigate(event); }, className: "opacity-0 group-hover/tree:opacity-100 transition-all duration-150 p-1 rounded-md hover:bg-white/[0.05] shrink-0 text-muted-foreground/50", title: "Investigate", children: _jsx(Search, { size: 12 }) }))] }), _jsx("button", { onClick: (e) => { e.stopPropagation(); setPayloadOpen((v) => !v); }, className: "mt-0.5 ml-3 text-[10px] font-mono text-muted-foreground hover:text-foreground truncate text-left max-w-full block", title: "Click to expand payload", children: event.summary ?? compactPayload(event.payload) }), payloadOpen && (_jsx(CopyablePayload, { payload: event.payload, className: "mt-1 ml-3 max-h-48" }))] }), !collapsed && (_jsxs(_Fragment, { children: [directChildren.map((child) => (_jsx(TreeNode, { event: child, childrenMap: childrenMap, depth: depth + 1, onClickReactor: onClickReactor, onInvestigate: onInvestigate }, child.seq))), [...reactorGroups.entries()].map(([hid, group]) => (_jsx(ReactorNode, { reactorId: hid, parentEventId: event.id, children: group, childrenMap: childrenMap, depth: depth + 1, isHighlighted: hid === highlightedReactorId, onClickReactor: onClickReactor }, hid)))] }))] }));
99
+ }, className: "ci-tree-actions ci-btn", style: { marginLeft: "auto", flexShrink: 0, fontSize: 10 }, title: "Copy subtree as JSON", children: copied ? _jsx(Check, { size: 12 }) : _jsx(Copy, { size: 12 }) }), onInvestigate && (_jsx("button", { onClick: (e) => { e.stopPropagation(); onInvestigate(event); }, className: "ci-tree-actions ci-btn", style: { flexShrink: 0 }, title: "Investigate", children: _jsx(Search, { size: 12 }) }))] }), _jsx("button", { onClick: (e) => { e.stopPropagation(); setPayloadOpen((v) => !v); }, className: "ci-mono ci-truncate", style: { marginTop: 2, marginLeft: 12, fontSize: 10, color: "var(--ci-text-muted)", textAlign: "left", maxWidth: "100%", display: "block", background: "none", border: "none", cursor: "pointer", padding: 0, transition: "color 150ms" }, title: "Click to expand payload", children: event.summary ?? compactPayload(event.payload) }), payloadOpen && (_jsx("div", { style: { marginTop: 4, marginLeft: 12, maxHeight: 192 }, children: _jsx(CopyablePayload, { payload: event.payload }) }))] }), !collapsed && (_jsxs(_Fragment, { children: [directChildren.map((child) => (_jsx(TreeNode, { event: child, childrenMap: childrenMap, depth: depth + 1, onClickReactor: onClickReactor, onInvestigate: onInvestigate, outcomesByReactor: outcomesByReactor }, child.seq))), [...reactorGroups.entries()].map(([hid, group]) => (_jsx(ReactorNode, { reactorId: hid, parentEventId: event.id, children: group, childrenMap: childrenMap, depth: depth + 1, isHighlighted: hid === highlightedReactorId, onClickReactor: onClickReactor, outcome: outcomesByReactor?.get(hid), outcomesByReactor: outcomesByReactor }, hid)))] }))] }));
88
100
  }
89
101
  // ---------------------------------------------------------------------------
90
102
  // matchesFlowSelection
@@ -103,7 +115,20 @@ export function CausalTreePane({ onInvestigate } = {}) {
103
115
  const flowCorrelationId = useSelector((s) => s.flowCorrelationId);
104
116
  const scrubberStart = useSelector((s) => s.scrubberStart);
105
117
  const scrubberEnd = useSelector((s) => s.scrubberEnd);
118
+ const outcomesMap = useSelector((s) => s.outcomes);
106
119
  const dispatch = useDispatch();
120
+ // Build reactor outcome lookup for current flow
121
+ const outcomesByReactor = useMemo(() => {
122
+ if (!flowCorrelationId)
123
+ return new Map();
124
+ const raw = outcomesMap[flowCorrelationId];
125
+ if (!raw)
126
+ return new Map();
127
+ const map = new Map();
128
+ for (const o of raw)
129
+ map.set(o.reactorId, o);
130
+ return map;
131
+ }, [outcomesMap, flowCorrelationId]);
107
132
  const treeEvents = useMemo(() => {
108
133
  const all = causalTree?.events ?? null;
109
134
  if (all == null || (scrubberStart == null && scrubberEnd == null))
@@ -142,17 +167,17 @@ export function CausalTreePane({ onInvestigate } = {}) {
142
167
  return { roots: rootList, childrenMap: cMap, totalCount: total, filteredCount: filtered };
143
168
  }, [treeEvents, flowCorrelationId, flowSelection]);
144
169
  if (treeLoading) {
145
- return (_jsxs("div", { className: "p-3 space-y-1.5 animate-pulse", children: [_jsx("div", { className: "h-3 w-32 bg-muted rounded mb-3" }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-4 w-12 bg-muted rounded" }), _jsx("div", { className: "h-4 w-36 bg-muted rounded" }), _jsx("div", { className: "h-3 w-24 bg-muted rounded" })] }), _jsxs("div", { className: "pl-6 space-y-1.5", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-4 w-14 bg-muted rounded" }), _jsx("div", { className: "h-4 w-44 bg-muted rounded" }), _jsx("div", { className: "h-3 w-24 bg-muted rounded" })] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-4 w-10 bg-muted rounded" }), _jsx("div", { className: "h-4 w-32 bg-muted rounded" }), _jsx("div", { className: "h-3 w-24 bg-muted rounded" })] })] })] }));
170
+ return (_jsxs("div", { className: "ci-pulse", style: { padding: 12, display: "flex", flexDirection: "column", gap: 6 }, children: [_jsx("div", { className: "ci-muted-bg", style: { height: 12, width: 128, marginBottom: 12 } }), _jsxs("div", { className: "ci-row", style: { gap: 6 }, children: [_jsx("div", { className: "ci-muted-bg", style: { height: 16, width: 48 } }), _jsx("div", { className: "ci-muted-bg", style: { height: 16, width: 144 } }), _jsx("div", { className: "ci-muted-bg", style: { height: 12, width: 96 } })] }), _jsxs("div", { style: { paddingLeft: 24, display: "flex", flexDirection: "column", gap: 6 }, children: [_jsxs("div", { className: "ci-row", style: { gap: 6 }, children: [_jsx("div", { className: "ci-muted-bg", style: { height: 16, width: 56 } }), _jsx("div", { className: "ci-muted-bg", style: { height: 16, width: 176 } }), _jsx("div", { className: "ci-muted-bg", style: { height: 12, width: 96 } })] }), _jsxs("div", { className: "ci-row", style: { gap: 6 }, children: [_jsx("div", { className: "ci-muted-bg", style: { height: 16, width: 40 } }), _jsx("div", { className: "ci-muted-bg", style: { height: 16, width: 128 } }), _jsx("div", { className: "ci-muted-bg", style: { height: 12, width: 96 } })] })] })] }));
146
171
  }
147
172
  if (!treeEvents) {
148
- return (_jsx("div", { className: "flex items-center justify-center h-full text-xs text-muted-foreground/50 tracking-wide", children: "Select an event to view its causal tree" }));
173
+ return (_jsx("div", { className: "ci-empty", children: "Select an event to view its causal tree" }));
149
174
  }
150
175
  if (roots.length === 0 && flowSelection) {
151
- return (_jsxs("div", { className: "h-full overflow-y-auto p-3", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2 px-2.5 py-1.5 rounded-md bg-indigo-500/8 border border-indigo-500/15 text-xs text-indigo-400", children: [_jsx("span", { children: flowSelection.kind === "event-type"
176
+ return (_jsxs("div", { style: { height: "100%", overflow: "auto", padding: 12 }, children: [_jsxs("div", { className: "ci-flow-filter ci-row", style: { gap: 8, marginBottom: 8 }, children: [_jsx("span", { children: flowSelection.kind === "event-type"
152
177
  ? flowSelection.name
153
- : `outputs of ${flowSelection.reactorId}` }), _jsx("button", { onClick: () => dispatch({ type: "ui/flow_node_selected", payload: null }), className: "ml-auto hover:text-foreground", children: _jsx(X, { size: 12 }) })] }), _jsx("div", { className: "flex items-center justify-center h-32 text-sm text-muted-foreground", children: "No events match the current filter" })] }));
178
+ : `outputs of ${flowSelection.reactorId}` }), _jsx("button", { onClick: () => dispatch({ type: "ui/flow_node_selected", payload: null }), style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", color: "inherit", padding: 0, transition: "color 150ms" }, children: _jsx(X, { size: 12 }) })] }), _jsx("div", { className: "ci-empty", style: { height: 128, fontSize: 14 }, children: "No events match the current filter" })] }));
154
179
  }
155
- return (_jsxs("div", { className: "h-full overflow-y-auto p-3", children: [flowSelection && (_jsxs("div", { className: "flex items-center gap-2 mb-2 px-2.5 py-1.5 rounded-md bg-indigo-500/8 border border-indigo-500/15 text-xs text-indigo-400", children: [_jsx("span", { children: flowSelection.kind === "event-type"
180
+ return (_jsxs("div", { style: { height: "100%", overflow: "auto", padding: 12 }, children: [flowSelection && (_jsxs("div", { className: "ci-flow-filter ci-row", style: { gap: 8, marginBottom: 8 }, children: [_jsx("span", { children: flowSelection.kind === "event-type"
156
181
  ? flowSelection.name
157
- : `outputs of ${flowSelection.reactorId}` }), _jsx("button", { onClick: () => dispatch({ type: "ui/flow_node_selected", payload: null }), className: "ml-auto hover:text-foreground", children: _jsx(X, { size: 12 }) })] })), _jsxs("h3", { className: "text-[10px] font-semibold text-muted-foreground/50 mb-2 uppercase tracking-widest", children: ["Causal Tree (", flowSelection ? `${filteredCount} of ${totalCount}` : totalCount, " events)"] }), roots.map(root => (_jsx(TreeNode, { event: root, childrenMap: childrenMap, depth: 0, onClickReactor: onClickReactor, onInvestigate: onInvestigate }, root.seq)))] }));
182
+ : `outputs of ${flowSelection.reactorId}` }), _jsx("button", { onClick: () => dispatch({ type: "ui/flow_node_selected", payload: null }), style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", color: "inherit", padding: 0, transition: "color 150ms" }, children: _jsx(X, { size: 12 }) })] })), _jsxs("h3", { className: "ci-header", style: { marginBottom: 8 }, children: ["Causal Tree (", flowSelection ? `${filteredCount} of ${totalCount}` : totalCount, " events)"] }), roots.map(root => (_jsx(TreeNode, { event: root, childrenMap: childrenMap, depth: 0, onClickReactor: onClickReactor, onInvestigate: onInvestigate, outcomesByReactor: outcomesByReactor }, root.seq)))] }));
158
183
  }
@@ -39,8 +39,13 @@ export function CorrelationExplorerPane() {
39
39
  const handleCopy = useCallback((text) => {
40
40
  navigator.clipboard.writeText(text).catch(() => { });
41
41
  }, []);
42
- return (_jsxs("div", { className: "flex flex-col h-full", children: [_jsx("div", { className: "px-3 py-2.5 border-b border-border", style: { background: "rgba(15, 15, 20, 0.6)", backdropFilter: "blur(8px)" }, children: _jsx("input", { type: "text", placeholder: "Search by correlation ID or event type...", value: search, onChange: (e) => handleSearchChange(e.target.value), className: "w-full px-3 py-1.5 text-xs bg-background/50 border border-border rounded-md text-foreground placeholder:text-muted-foreground/40 focus:outline-none focus:ring-1 focus:ring-indigo-500/40 focus:border-indigo-500/30 transition-all" }) }), _jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border text-[9px] font-semibold text-muted-foreground/40 uppercase tracking-widest", children: [_jsx("span", { className: "w-28 shrink-0", children: "Root Event" }), _jsx("span", { className: "w-24 shrink-0", children: "Correlation" }), _jsx("span", { className: "w-12 shrink-0 text-right", children: "Events" }), _jsx("span", { className: "w-20 shrink-0 text-right", children: "Duration" }), _jsx("span", { className: "flex-1", children: "Last Activity" })] }), loading && correlations.length === 0 ? (_jsx("div", { className: "animate-pulse p-3", children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "flex items-center gap-2 py-2.5", children: [_jsx("div", { className: "h-3 w-28 bg-white/[0.03] rounded" }), _jsx("div", { className: "h-3 w-24 bg-white/[0.03] rounded" }), _jsx("div", { className: "h-3 w-12 bg-white/[0.03] rounded" })] }, i))) })) : correlations.length === 0 ? (_jsx("div", { className: "flex items-center justify-center h-32 text-xs text-muted-foreground/50 tracking-wide", children: "No correlations found" })) : (_jsx("div", { className: "flex-1 overflow-y-auto", children: correlations.map((corr) => (_jsxs("button", { onClick: () => handleRowClick(corr.correlationId), className: "group w-full text-left flex items-center gap-2 px-3 py-2.5 border-b border-border hover:bg-indigo-500/8 transition-all duration-150", children: [_jsx("span", { className: "text-[10px] font-mono shrink-0 w-28 truncate px-1.5 py-0.5 rounded", style: {
42
+ return (_jsxs("div", { className: "ci-col", children: [_jsx("div", { className: "ci-surface", style: { padding: "10px 12px", borderBottom: "1px solid var(--ci-border)" }, children: _jsx("input", { type: "text", placeholder: "Search by correlation ID or event type...", value: search, onChange: (e) => handleSearchChange(e.target.value), className: "ci-input", style: { width: "100%", padding: "6px 12px", fontSize: 12 } }) }), _jsxs("div", { className: "ci-row ci-header", style: { gap: 8, padding: "8px 12px", borderBottom: "1px solid var(--ci-border)", letterSpacing: "0.08em", fontSize: 9 }, children: [_jsx("span", { style: { width: 112, flexShrink: 0 }, children: "Root Event" }), _jsx("span", { style: { width: 96, flexShrink: 0 }, children: "Correlation" }), _jsx("span", { style: { width: 48, flexShrink: 0, textAlign: "right" }, children: "Events" }), _jsx("span", { style: { width: 80, flexShrink: 0, textAlign: "right" }, children: "Duration" }), _jsx("span", { style: { flex: 1 }, children: "Last Activity" })] }), loading && correlations.length === 0 ? (_jsx("div", { className: "ci-pulse", style: { padding: 12 }, children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "ci-row", style: { gap: 8, padding: "10px 0" }, children: [_jsx("div", { className: "ci-skeleton", style: { height: 12, width: 112 } }), _jsx("div", { className: "ci-skeleton", style: { height: 12, width: 96 } }), _jsx("div", { className: "ci-skeleton", style: { height: 12, width: 48 } })] }, i))) })) : correlations.length === 0 ? (_jsx("div", { className: "ci-empty", style: { height: 128 }, children: "No correlations found" })) : (_jsx("div", { className: "ci-scroll", children: correlations.map((corr) => (_jsxs("button", { onClick: () => handleRowClick(corr.correlationId), className: "ci-list-row ci-row", style: { width: "100%", textAlign: "left", gap: 8, padding: "10px 12px", borderBottom: "1px solid var(--ci-border)", background: "none", border: "none", borderBottomStyle: "solid", cursor: "pointer" }, children: [_jsx("span", { className: "ci-mono ci-truncate", style: {
43
+ fontSize: 10,
44
+ flexShrink: 0,
45
+ width: 112,
46
+ padding: "2px 6px",
47
+ borderRadius: "var(--ci-radius-sm)",
43
48
  color: eventTextColor(corr.rootEventType),
44
49
  background: eventBg(corr.rootEventType),
45
- }, title: corr.rootEventType, children: corr.rootEventType }), _jsx("span", { className: "text-[10px] font-mono text-purple-400/70 w-24 shrink-0 truncate cursor-pointer hover:text-purple-400 transition-colors", title: `Click to copy: ${corr.correlationId}`, onClick: (e) => { e.stopPropagation(); handleCopy(corr.correlationId); }, children: corr.correlationId.slice(0, 8) }), _jsx("span", { className: "text-[11px] font-mono text-foreground/70 w-12 shrink-0 text-right tabular-nums", children: corr.eventCount }), _jsx("span", { className: "text-[10px] text-muted-foreground/50 w-20 shrink-0 text-right font-mono tabular-nums", children: _jsx(RelativeDuration, { firstTs: corr.firstTs, lastTs: corr.lastTs }) }), _jsx("span", { className: "text-[10px] text-muted-foreground/40 flex-1 truncate tabular-nums", children: formatTs(corr.lastTs) }), corr.hasErrors && (_jsx("span", { className: "w-2 h-2 rounded-full bg-red-500/80 shrink-0", style: { boxShadow: "0 0 6px rgba(239, 68, 68, 0.3)" }, title: "This correlation has errors" }))] }, corr.correlationId))) }))] }));
50
+ }, title: corr.rootEventType, children: corr.rootEventType }), _jsx("span", { className: "ci-mono ci-truncate", style: { fontSize: 10, color: "var(--ci-purple-dim)", width: 96, flexShrink: 0, cursor: "pointer", transition: "color 150ms" }, title: `Click to copy: ${corr.correlationId}`, onClick: (e) => { e.stopPropagation(); handleCopy(corr.correlationId); }, children: corr.correlationId.slice(0, 8) }), _jsx("span", { className: "ci-mono ci-tabular", style: { fontSize: 11, color: "var(--ci-text)", opacity: 0.7, width: 48, flexShrink: 0, textAlign: "right" }, children: corr.eventCount }), _jsx("span", { className: "ci-mono ci-tabular", style: { fontSize: 10, color: "var(--ci-text-muted)", opacity: 0.5, width: 80, flexShrink: 0, textAlign: "right" }, children: _jsx(RelativeDuration, { firstTs: corr.firstTs, lastTs: corr.lastTs }) }), _jsx("span", { className: "ci-tabular ci-truncate", style: { fontSize: 10, color: "var(--ci-text-dimmer)", flex: 1 }, children: formatTs(corr.lastTs) }), corr.hasErrors && (_jsx("span", { className: "ci-error-dot", title: "This correlation has errors" }))] }, corr.correlationId))) }))] }));
46
51
  }
@@ -9,8 +9,8 @@ import { Search, ChevronRight, ChevronDown } from "lucide-react";
9
9
  // ---------------------------------------------------------------------------
10
10
  function LogRow({ log, showReactor }) {
11
11
  const [expanded, setExpanded] = useState(false);
12
- const levelColor = LOG_LEVEL_COLORS[log.level] ?? "bg-zinc-600/20 text-zinc-400";
13
- return (_jsxs("div", { className: "px-2 py-1.5 hover:bg-white/[0.02] rounded-md transition-colors duration-100", children: [_jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [_jsx("span", { className: `px-1.5 py-0.5 rounded text-[9px] font-semibold uppercase shrink-0 ${levelColor}`, children: log.level }), _jsx("span", { className: "text-[10px] text-muted-foreground/50 shrink-0 tabular-nums", children: formatTs(log.loggedAt) }), showReactor && (_jsx("span", { className: "text-[10px] font-mono text-muted-foreground/40 shrink-0", children: log.reactorId })), _jsx("span", { className: "text-[11px] text-foreground/80 truncate", children: log.message }), log.data != null && (_jsx("button", { onClick: () => setExpanded((v) => !v), className: "ml-auto text-[10px] p-1 rounded-md hover:bg-white/[0.05] shrink-0 text-muted-foreground/50 hover:text-foreground transition-colors", children: expanded ? _jsx(ChevronDown, { size: 10 }) : _jsx(ChevronRight, { size: 10 }) }))] }), expanded && log.data != null && (_jsx("pre", { className: "mt-1.5 ml-4 text-[10px] font-mono text-muted-foreground/70 bg-white/[0.02] rounded-md p-2.5 max-h-32 overflow-auto whitespace-pre-wrap border border-border", children: typeof log.data === "string" ? log.data : JSON.stringify(log.data, null, 2) }))] }));
12
+ const levelColor = LOG_LEVEL_COLORS[log.level] ?? "ci-log-debug";
13
+ return (_jsxs("div", { style: { padding: "6px 8px", borderRadius: "var(--ci-radius-md)", transition: "background 100ms" }, className: "ci-list-row", children: [_jsxs("div", { className: "ci-row", style: { gap: 8, minWidth: 0 }, children: [_jsx("span", { className: levelColor, style: { padding: "2px 6px", borderRadius: "var(--ci-radius-sm)", fontSize: 9, fontWeight: 600, textTransform: "uppercase", flexShrink: 0 }, children: log.level }), _jsx("span", { className: "ci-tabular", style: { fontSize: 10, color: "var(--ci-text-muted)", opacity: 0.5, flexShrink: 0 }, children: formatTs(log.loggedAt) }), showReactor && (_jsx("span", { className: "ci-mono", style: { fontSize: 10, color: "var(--ci-text-dimmer)", flexShrink: 0 }, children: log.reactorId })), _jsx("span", { className: "ci-truncate", style: { fontSize: 11, color: "var(--ci-text)", opacity: 0.8 }, children: log.message }), log.data != null && (_jsx("button", { onClick: () => setExpanded((v) => !v), className: "ci-btn", style: { marginLeft: "auto", flexShrink: 0, fontSize: 10 }, children: expanded ? _jsx(ChevronDown, { size: 10 }) : _jsx(ChevronRight, { size: 10 }) }))] }), expanded && log.data != null && (_jsx("pre", { className: "ci-mono", style: { marginTop: 6, marginLeft: 16, fontSize: 10, color: "var(--ci-text-muted)", opacity: 0.7, background: "rgba(255, 255, 255, 0.02)", borderRadius: "var(--ci-radius-md)", padding: 10, maxHeight: 128, overflow: "auto", whiteSpace: "pre-wrap", border: "1px solid var(--ci-border)" }, children: typeof log.data === "string" ? log.data : JSON.stringify(log.data, null, 2) }))] }));
14
14
  }
15
15
  export function LogsPane({ onInvestigate } = {}) {
16
16
  const logs = useSelector((s) => s.logs);
@@ -18,7 +18,7 @@ export function LogsPane({ onInvestigate } = {}) {
18
18
  const flowData = useSelector((s) => s.flowData);
19
19
  const scrubberStart = useSelector((s) => s.scrubberStart);
20
20
  const scrubberEnd = useSelector((s) => s.scrubberEnd);
21
- const [levelFilter, setLevelFilter] = useState(new Set(["debug", "info", "warn"]));
21
+ const [levelFilter, setLevelFilter] = useState(new Set(["debug", "info", "warn", "error"]));
22
22
  const [searchText, setSearchText] = useState("");
23
23
  const isCorrelationScope = logsFilter.scope === "correlation" && logsFilter.correlationId != null;
24
24
  const hasFilter = logsFilter.reactorId != null || isCorrelationScope;
@@ -47,7 +47,7 @@ export function LogsPane({ onInvestigate } = {}) {
47
47
  return filtered;
48
48
  }, [logs, logsFilter, levelFilter, searchText, visibleEventIds]);
49
49
  if (!hasFilter) {
50
- return (_jsx("div", { className: "flex items-center justify-center h-full text-xs text-muted-foreground/50 tracking-wide", children: "Click a reactor node in the causal tree to view logs" }));
50
+ return (_jsx("div", { className: "ci-empty", children: "Click a reactor node in the causal tree to view logs" }));
51
51
  }
52
52
  const toggleLevel = (level) => {
53
53
  setLevelFilter((prev) => {
@@ -59,7 +59,16 @@ export function LogsPane({ onInvestigate } = {}) {
59
59
  return next;
60
60
  });
61
61
  };
62
- return (_jsxs("div", { className: "h-full flex flex-col", children: [_jsxs("div", { className: "px-3 py-2 border-b border-border flex items-center gap-3 flex-wrap", style: { background: "rgba(15, 15, 20, 0.6)", backdropFilter: "blur(8px)" }, children: [_jsx("div", { className: "flex items-center gap-1 text-[10px]", children: ["debug", "info", "warn"].map((level) => (_jsx("button", { onClick: () => toggleLevel(level), className: `px-2 py-0.5 rounded-md uppercase font-semibold transition-all duration-150 ${levelFilter.has(level)
63
- ? LOG_LEVEL_COLORS[level]
64
- : "text-muted-foreground/30 line-through"}`, children: level }, level))) }), _jsxs("div", { className: "relative flex items-center", children: [_jsx(Search, { size: 10, className: "absolute left-2.5 text-muted-foreground/40 pointer-events-none" }), _jsx("input", { type: "text", placeholder: "Search logs...", value: searchText, onChange: (e) => setSearchText(e.target.value), className: "pl-7 pr-2 text-[11px] bg-background/50 border border-border rounded-md py-1 w-44 focus:outline-none focus:ring-1 focus:ring-indigo-500/40 focus:border-indigo-500/30 text-foreground placeholder:text-muted-foreground/40 transition-all" })] }), onInvestigate && (_jsx("button", { onClick: () => onInvestigate(logsFilter), className: "flex items-center gap-1 px-2 py-1 rounded-md text-[11px] text-muted-foreground/50 hover:text-foreground hover:bg-white/[0.04] transition-all duration-150", title: "Investigate logs", children: _jsx(Search, { size: 12 }) }))] }), _jsxs("div", { className: "px-3 py-1.5 text-[10px] text-muted-foreground/50", children: [logsFilter.reactorId ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-mono text-foreground/60", children: logsFilter.reactorId }), isCorrelationScope && _jsx("span", { className: "ml-1", children: "(all reactors in correlation)" })] })) : (_jsx("span", { children: "All reactors in correlation" })), _jsxs("span", { className: "ml-2 tabular-nums", children: [filteredLogs.length, " logs"] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-1", children: [filteredLogs.length === 0 && (_jsx("div", { className: "p-3 text-[11px] text-muted-foreground/40", children: "No logs match filters" })), filteredLogs.map((log, i) => (_jsx(LogRow, { log: log, showReactor: isCorrelationScope }, i)))] })] }));
62
+ return (_jsxs("div", { className: "ci-col", children: [_jsxs("div", { className: "ci-row ci-surface", style: { padding: "8px 12px", borderBottom: "1px solid var(--ci-border)", gap: 12, flexWrap: "wrap" }, children: [_jsx("div", { className: "ci-row", style: { gap: 4, fontSize: 10 }, children: ["debug", "info", "warn", "error"].map((level) => (_jsx("button", { onClick: () => toggleLevel(level), className: levelFilter.has(level) ? LOG_LEVEL_COLORS[level] : "", style: {
63
+ padding: "2px 8px",
64
+ borderRadius: "var(--ci-radius-md)",
65
+ textTransform: "uppercase",
66
+ fontWeight: 600,
67
+ border: "none",
68
+ cursor: "pointer",
69
+ background: levelFilter.has(level) ? undefined : "transparent",
70
+ color: levelFilter.has(level) ? undefined : "var(--ci-text-dimmer)",
71
+ textDecoration: levelFilter.has(level) ? undefined : "line-through",
72
+ transition: "all 150ms",
73
+ }, children: level }, level))) }), _jsxs("div", { className: "ci-row", style: { position: "relative", marginLeft: "auto" }, children: [_jsx(Search, { size: 10, style: { position: "absolute", left: 10, color: "var(--ci-text-dimmer)", pointerEvents: "none" } }), _jsx("input", { type: "text", placeholder: "Search logs...", value: searchText, onChange: (e) => setSearchText(e.target.value), className: "ci-input", style: { paddingLeft: 28, paddingRight: 8, fontSize: 11, paddingTop: 4, paddingBottom: 4, width: 176 } })] }), onInvestigate && (_jsx("button", { onClick: () => onInvestigate(logsFilter), className: "ci-btn ci-row", style: { gap: 4, fontSize: 11 }, title: "Investigate logs", children: _jsx(Search, { size: 12 }) }))] }), _jsxs("div", { style: { padding: "6px 12px", fontSize: 10, color: "var(--ci-text-muted)", opacity: 0.5 }, children: [logsFilter.reactorId ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "ci-mono", style: { color: "var(--ci-text)", opacity: 0.6 }, children: logsFilter.reactorId }), isCorrelationScope && _jsx("span", { style: { marginLeft: 4 }, children: "(all reactors in correlation)" })] })) : (_jsx("span", { children: "All reactors in correlation" })), _jsxs("span", { className: "ci-tabular", style: { marginLeft: 8 }, children: [filteredLogs.length, " logs"] })] }), _jsxs("div", { className: "ci-scroll", style: { padding: "0 4px" }, children: [filteredLogs.length === 0 && (_jsx("div", { style: { padding: 12, fontSize: 11, color: "var(--ci-text-dimmer)" }, children: "No logs match filters" })), filteredLogs.map((log, i) => (_jsx(LogRow, { log: log, showReactor: isCorrelationScope }, i)))] })] }));
65
74
  }
@@ -8,12 +8,14 @@ import { formatTs, compactPayload, inScrubberRange } from "../utils";
8
8
  import { Search, ChevronRight } from "lucide-react";
9
9
  function EventRow({ event, isSelected, onClick, onFilterCorrelation, onInvestigate, }) {
10
10
  const [payloadOpen, setPayloadOpen] = useState(false);
11
- return (_jsxs("div", { className: `group w-full text-left px-3 py-2 border-b border-border transition-all duration-150 ${isSelected
12
- ? "bg-indigo-500/15"
13
- : "hover:bg-white/[0.02]"}`, children: [_jsx("div", { onClick: onClick, role: "button", tabIndex: 0, className: "w-full text-left cursor-pointer", children: _jsxs("div", { className: "flex items-center gap-2.5 min-w-0", children: [_jsx("span", { className: "text-[10px] font-mono text-muted-foreground/60 w-10 shrink-0 text-right tabular-nums", children: event.seq }), _jsx("span", { className: "text-[10px] text-muted-foreground/70 shrink-0 w-32 tabular-nums", children: formatTs(event.ts) }), event.correlationId && (_jsx("button", { onClick: (e) => { e.stopPropagation(); onFilterCorrelation(event.correlationId); }, className: "px-1.5 py-0.5 rounded-full text-[9px] font-mono bg-purple-500/8 text-purple-400/80 hover:bg-purple-500/15 hover:text-purple-400 shrink-0 transition-all border border-purple-500/10", title: `Filter by correlation ${event.correlationId}`, children: event.correlationId.slice(0, 8) })), _jsx("span", { className: "text-xs font-mono shrink-0 px-1.5 py-0.5 rounded", style: {
11
+ return (_jsxs("div", { className: `ci-event-row${isSelected ? " ci-selected" : ""}`, children: [_jsx("div", { onClick: onClick, role: "button", tabIndex: 0, style: { width: "100%", textAlign: "left", cursor: "pointer" }, children: _jsxs("div", { className: "ci-row", style: { gap: 10, minWidth: 0 }, children: [_jsx("span", { className: "ci-mono ci-tabular", style: { fontSize: 10, color: "var(--ci-text-dim)", width: 40, flexShrink: 0, textAlign: "right" }, children: event.seq }), _jsx("span", { className: "ci-tabular", style: { fontSize: 10, color: "var(--ci-text-muted)", opacity: 0.7, flexShrink: 0, width: 128 }, children: formatTs(event.ts) }), event.correlationId && (_jsx("button", { onClick: (e) => { e.stopPropagation(); onFilterCorrelation(event.correlationId); }, className: "ci-corr-pill", title: `Filter by correlation ${event.correlationId}`, style: { flexShrink: 0 }, children: event.correlationId.slice(0, 8) })), _jsx("span", { className: "ci-mono", style: {
12
+ fontSize: 12,
13
+ flexShrink: 0,
14
+ padding: "2px 6px",
15
+ borderRadius: "var(--ci-radius-sm)",
14
16
  color: eventTextColor(event.name),
15
17
  background: eventBg(event.name),
16
- }, children: event.name }), _jsxs("button", { onClick: (e) => { e.stopPropagation(); setPayloadOpen((v) => !v); }, className: "flex items-center gap-1 text-[10px] font-mono text-muted-foreground/60 hover:text-muted-foreground truncate text-left min-w-0 transition-colors", title: "Click to expand payload", children: [_jsx(ChevronRight, { size: 10, className: `shrink-0 transition-transform duration-150 ${payloadOpen ? "rotate-90" : ""}` }), _jsx("span", { className: "truncate", children: event.summary ?? compactPayload(event.payload) })] }), onInvestigate && (_jsx("button", { onClick: (e) => { e.stopPropagation(); onInvestigate(); }, className: "opacity-0 group-hover:opacity-100 transition-opacity duration-150 ml-auto p-1 rounded-md hover:bg-white/[0.05] shrink-0 text-muted-foreground", title: "Investigate", children: _jsx(Search, { size: 12 }) }))] }) }), payloadOpen && (_jsx(CopyablePayload, { payload: event.payload, className: "mt-2 ml-12 max-h-64" }))] }));
18
+ }, children: event.name }), _jsxs("button", { onClick: (e) => { e.stopPropagation(); setPayloadOpen((v) => !v); }, className: "ci-mono ci-truncate", style: { display: "flex", alignItems: "center", gap: 4, fontSize: 10, color: "var(--ci-text-dim)", textAlign: "left", minWidth: 0, background: "none", border: "none", cursor: "pointer", transition: "color 150ms" }, title: "Click to expand payload", children: [_jsx(ChevronRight, { size: 10, style: { flexShrink: 0, transition: "transform 150ms", transform: payloadOpen ? "rotate(90deg)" : undefined } }), _jsx("span", { className: "ci-truncate", children: event.summary ?? compactPayload(event.payload) })] }), onInvestigate && (_jsx("button", { onClick: (e) => { e.stopPropagation(); onInvestigate(); }, className: "ci-copy-btn ci-btn", style: { marginLeft: "auto", flexShrink: 0 }, title: "Investigate", children: _jsx(Search, { size: 12 }) }))] }) }), payloadOpen && (_jsx("div", { style: { marginTop: 8, marginLeft: 48, maxHeight: 256 }, children: _jsx(CopyablePayload, { payload: event.payload }) }))] }));
17
19
  }
18
20
  function InfiniteScrollSentinel({ onVisible, loading }) {
19
21
  const ref = useRef(null);
@@ -30,7 +32,7 @@ function InfiniteScrollSentinel({ onVisible, loading }) {
30
32
  observer.observe(el);
31
33
  return () => observer.disconnect();
32
34
  }, [loading]);
33
- return (_jsx("div", { ref: ref, className: "flex items-center justify-center py-4", children: loading && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-indigo-500/50 animate-pulse" }), _jsx("span", { className: "text-[10px] text-muted-foreground/60", children: "Loading" })] })) }));
35
+ return (_jsx("div", { ref: ref, className: "ci-row", style: { justifyContent: "center", padding: "16px 0" }, children: loading && (_jsxs("div", { className: "ci-row", style: { gap: 8 }, children: [_jsx("div", { className: "ci-pulse", style: { width: 6, height: 6, borderRadius: "50%", background: "rgba(99, 102, 241, 0.5)" } }), _jsx("span", { style: { fontSize: 10, color: "var(--ci-text-dim)" }, children: "Loading" })] })) }));
34
36
  }
35
37
  export function TimelinePane({ onInvestigate } = {}) {
36
38
  const events = useSelector((s) => {
@@ -69,5 +71,5 @@ export function TimelinePane({ onInvestigate } = {}) {
69
71
  const handleLoadMore = useCallback(() => {
70
72
  dispatch({ type: "ui/load_more_requested" });
71
73
  }, [dispatch]);
72
- return (_jsxs("div", { className: "flex flex-col h-full", children: [_jsx(FilterBar, {}), loading && events.length === 0 ? (_jsx("div", { className: "animate-pulse p-1", children: Array.from({ length: 12 }).map((_, i) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-2.5 border-b border-border", children: [_jsx("div", { className: "h-3 w-10 bg-white/[0.03] rounded shrink-0" }), _jsx("div", { className: "h-3 w-32 bg-white/[0.03] rounded shrink-0" }), _jsx("div", { className: "h-3 bg-white/[0.03] rounded flex-1", style: { maxWidth: `${150 + (i * 37) % 200}px` } })] }, i))) })) : events.length === 0 ? (_jsx("div", { className: "flex items-center justify-center h-32 text-sm text-muted-foreground/60", children: "No events found" })) : (_jsxs("div", { className: "flex-1 overflow-y-auto", children: [displayedEvents.map((event) => (_jsx(EventRow, { event: event, isSelected: event.seq === selectedSeq, onClick: () => handleSelect(event), onFilterCorrelation: handleFilterCorrelation, onInvestigate: onInvestigate ? () => onInvestigate(event) : undefined }, event.seq))), hasMore && (_jsx(InfiniteScrollSentinel, { onVisible: handleLoadMore, loading: loading }))] }))] }));
74
+ return (_jsxs("div", { className: "ci-col", children: [_jsx(FilterBar, {}), loading && events.length === 0 ? (_jsx("div", { className: "ci-pulse", style: { padding: 4 }, children: Array.from({ length: 12 }).map((_, i) => (_jsxs("div", { className: "ci-row", style: { gap: 8, padding: "10px 12px", borderBottom: "1px solid var(--ci-border)" }, children: [_jsx("div", { className: "ci-skeleton", style: { height: 12, width: 40, flexShrink: 0 } }), _jsx("div", { className: "ci-skeleton", style: { height: 12, width: 128, flexShrink: 0 } }), _jsx("div", { className: "ci-skeleton", style: { height: 12, flex: 1, maxWidth: `${150 + (i * 37) % 200}px` } })] }, i))) })) : events.length === 0 ? (_jsx("div", { className: "ci-empty", style: { height: 128, fontSize: 14 }, children: "No events found" })) : (_jsxs("div", { className: "ci-scroll", children: [displayedEvents.map((event) => (_jsx(EventRow, { event: event, isSelected: event.seq === selectedSeq, onClick: () => handleSelect(event), onFilterCorrelation: handleFilterCorrelation, onInvestigate: onInvestigate ? () => onInvestigate(event) : undefined }, event.seq))), hasMore && (_jsx(InfiniteScrollSentinel, { onVisible: handleLoadMore, loading: loading }))] }))] }));
73
75
  }