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.
- package/dist/CausalInspector.css +447 -20
- package/dist/components/CopyablePayload.js +8 -8
- package/dist/components/FilterBar.js +9 -6
- package/dist/components/JsonSyntax.js +8 -8
- package/dist/engines/query.js +15 -13
- package/dist/events.d.ts +6 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/panes/CausalFlowPane.js +4 -4
- package/dist/panes/CausalTreePane.js +38 -13
- package/dist/panes/CorrelationExplorerPane.js +7 -2
- package/dist/panes/LogsPane.js +16 -7
- package/dist/panes/TimelinePane.js +8 -6
- package/dist/panes/WaterfallPane.js +181 -68
- package/dist/queries.d.ts +1 -0
- package/dist/queries.js +14 -0
- package/dist/reducer.js +5 -0
- package/dist/state.d.ts +2 -1
- package/dist/state.js +3 -0
- package/dist/theme.js +4 -3
- package/dist/types.d.ts +12 -4
- package/package.json +1 -1
package/dist/engines/query.js
CHANGED
|
@@ -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:
|
|
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
|
|
137
|
-
hasMore:
|
|
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(
|
|
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,
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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
|
-
|
|
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", {
|
|
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: `
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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", {
|
|
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 }),
|
|
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", {
|
|
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 }),
|
|
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: "
|
|
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: "
|
|
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
|
}
|
package/dist/panes/LogsPane.js
CHANGED
|
@@ -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] ?? "
|
|
13
|
-
return (_jsxs("div", {
|
|
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: "
|
|
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: "
|
|
63
|
-
|
|
64
|
-
: "
|
|
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: `
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
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: "
|
|
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: "
|
|
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
|
}
|