causal-inspector 0.1.6 → 0.2.0
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 +20 -447
- package/dist/CausalInspector.d.ts +8 -1
- package/dist/CausalInspector.js +32 -9
- package/dist/components/CopyablePayload.js +8 -8
- package/dist/components/EffectList.d.ts +4 -0
- package/dist/components/EffectList.js +15 -0
- package/dist/components/FilterBar.js +7 -10
- package/dist/components/GlobalScrubber.js +6 -6
- package/dist/components/JsonSyntax.js +8 -8
- package/dist/engines/query.js +131 -52
- package/dist/engines/scrubber.js +1 -1
- package/dist/engines/url.d.ts +5 -2
- package/dist/engines/url.js +50 -22
- package/dist/events.d.ts +38 -13
- package/dist/index.d.ts +5 -3
- package/dist/index.js +4 -2
- package/dist/panes/AggregateTimelinePane.js +4 -4
- package/dist/panes/CausalFlowPane.js +19 -19
- package/dist/panes/CausalTreePane.js +43 -34
- package/dist/panes/CorrelationExplorerPane.d.ts +2 -2
- package/dist/panes/CorrelationExplorerPane.js +10 -15
- package/dist/panes/LogsPane.js +8 -17
- package/dist/panes/SubjectChainPane.d.ts +1 -0
- package/dist/panes/SubjectChainPane.js +50 -0
- package/dist/panes/TimelinePane.js +33 -19
- package/dist/panes/WaterfallPane.js +5 -5
- package/dist/panes/WorkflowExplorerPane.d.ts +2 -0
- package/dist/panes/WorkflowExplorerPane.js +46 -0
- package/dist/queries.d.ts +16 -12
- package/dist/queries.js +103 -27
- package/dist/reducer.js +99 -38
- package/dist/state.d.ts +15 -5
- package/dist/state.js +16 -8
- package/dist/theme.js +4 -4
- package/dist/types.d.ts +52 -12
- package/dist/utils.js +1 -1
- package/package.json +1 -1
package/dist/events.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BaseEvent } from "./machine";
|
|
2
|
-
import type { InspectorEvent,
|
|
2
|
+
import type { InspectorEvent, WorkflowSummary, ReactorDependency, AggregateLifecycleEntry, FilterState, FlowSelection, ReactorDescription, ReactorDescriptionSnapshot, AggregateTimelineEntry, ReactorLog, ReactorOutcome, ReactorAttempt, PaneLayout, SubjectChainEvent, SubjectChainMode, InspectorEffect } 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", {
|
|
@@ -16,27 +16,27 @@ type CausalTreeLoaded = BaseEvent<"events/causal_tree_loaded", {
|
|
|
16
16
|
type FlowLoaded = BaseEvent<"events/flow_loaded", InspectorEvent[]>;
|
|
17
17
|
type LogsLoaded = BaseEvent<"events/logs_loaded", ReactorLog[]>;
|
|
18
18
|
type DescriptionsLoaded = BaseEvent<"events/descriptions_loaded", {
|
|
19
|
-
|
|
19
|
+
workflowId: string;
|
|
20
20
|
descriptions: ReactorDescription[];
|
|
21
21
|
}>;
|
|
22
22
|
type DescriptionSnapshotsLoaded = BaseEvent<"events/description_snapshots_loaded", {
|
|
23
|
-
|
|
23
|
+
workflowId: string;
|
|
24
24
|
snapshots: ReactorDescriptionSnapshot[];
|
|
25
25
|
}>;
|
|
26
26
|
type AggregateTimelineLoaded = BaseEvent<"events/aggregate_timeline_loaded", {
|
|
27
|
-
|
|
27
|
+
workflowId: string;
|
|
28
28
|
entries: AggregateTimelineEntry[];
|
|
29
29
|
}>;
|
|
30
30
|
type OutcomesLoaded = BaseEvent<"events/outcomes_loaded", {
|
|
31
|
-
|
|
31
|
+
workflowId: string;
|
|
32
32
|
outcomes: ReactorOutcome[];
|
|
33
33
|
}>;
|
|
34
34
|
type AttemptsLoaded = BaseEvent<"events/attempts_loaded", {
|
|
35
|
-
|
|
35
|
+
workflowId: string;
|
|
36
36
|
attempts: ReactorAttempt[];
|
|
37
37
|
}>;
|
|
38
|
-
type
|
|
39
|
-
|
|
38
|
+
type WorkflowsLoaded = BaseEvent<"events/workflows_loaded", {
|
|
39
|
+
workflows: WorkflowSummary[];
|
|
40
40
|
hasMore: boolean;
|
|
41
41
|
append: boolean;
|
|
42
42
|
}>;
|
|
@@ -51,7 +51,7 @@ type EventSelected = BaseEvent<"ui/event_selected", {
|
|
|
51
51
|
}>;
|
|
52
52
|
type EventDeselected = BaseEvent<"ui/event_deselected">;
|
|
53
53
|
type FlowOpened = BaseEvent<"ui/flow_opened", {
|
|
54
|
-
|
|
54
|
+
workflowId: string;
|
|
55
55
|
}>;
|
|
56
56
|
type FlowClosed = BaseEvent<"ui/flow_closed">;
|
|
57
57
|
type FlowNodeSelected = BaseEvent<"ui/flow_node_selected", FlowSelection>;
|
|
@@ -68,7 +68,7 @@ type ScrubberPlayToggled = BaseEvent<"ui/scrubber_play_toggled">;
|
|
|
68
68
|
type ScrubberSpeedChanged = BaseEvent<"ui/scrubber_speed_changed", {
|
|
69
69
|
speed: number;
|
|
70
70
|
}>;
|
|
71
|
-
type
|
|
71
|
+
type WorkflowsRequested = BaseEvent<"ui/workflows_requested", {
|
|
72
72
|
search?: string;
|
|
73
73
|
}>;
|
|
74
74
|
type HandlerSelected = BaseEvent<"ui/handler_selected", {
|
|
@@ -77,10 +77,35 @@ type HandlerSelected = BaseEvent<"ui/handler_selected", {
|
|
|
77
77
|
type AggregateLifecycleRequested = BaseEvent<"ui/aggregate_lifecycle_requested", {
|
|
78
78
|
aggregateKey: string;
|
|
79
79
|
}>;
|
|
80
|
-
type
|
|
80
|
+
type LoadMoreWorkflowsRequested = BaseEvent<"ui/load_more_workflows_requested">;
|
|
81
81
|
type LocationChanged = BaseEvent<"location/changed", {
|
|
82
|
-
|
|
82
|
+
workflowId: string | null;
|
|
83
83
|
handler: string | null;
|
|
84
|
+
subject: string | null;
|
|
85
|
+
subjectMode: SubjectChainMode | null;
|
|
84
86
|
}>;
|
|
85
|
-
|
|
87
|
+
type SubjectSelected = BaseEvent<"ui/subject_selected", {
|
|
88
|
+
aggregateType: string;
|
|
89
|
+
aggregateId: string;
|
|
90
|
+
mode?: SubjectChainMode;
|
|
91
|
+
}>;
|
|
92
|
+
type SubjectModeChanged = BaseEvent<"ui/subject_mode_changed", {
|
|
93
|
+
mode: SubjectChainMode;
|
|
94
|
+
}>;
|
|
95
|
+
type SubjectChainLoadMore = BaseEvent<"ui/subject_chain_load_more">;
|
|
96
|
+
type EventEffectsRequested = BaseEvent<"ui/event_effects_requested", {
|
|
97
|
+
eventId: string;
|
|
98
|
+
}>;
|
|
99
|
+
type SubjectChainLoaded = BaseEvent<"events/subject_chain_loaded", {
|
|
100
|
+
events: SubjectChainEvent[];
|
|
101
|
+
hasMore: boolean;
|
|
102
|
+
cursor: number | null;
|
|
103
|
+
depthCapped: boolean;
|
|
104
|
+
append: boolean;
|
|
105
|
+
}>;
|
|
106
|
+
type EventEffectsLoaded = BaseEvent<"events/event_effects_loaded", {
|
|
107
|
+
eventId: string;
|
|
108
|
+
effects: InspectorEffect[];
|
|
109
|
+
}>;
|
|
110
|
+
export type InspectorMachineEvent = EventsReceived | SubscriptionConnected | SubscriptionError | PageLoaded | CausalTreeLoaded | FlowLoaded | LogsLoaded | DescriptionsLoaded | DescriptionSnapshotsLoaded | AggregateTimelineLoaded | OutcomesLoaded | AttemptsLoaded | WorkflowsLoaded | EventSelected | EventDeselected | FlowOpened | FlowClosed | FlowNodeSelected | FilterChanged | LoadMoreRequested | LayoutChanged | ScrubberStartChanged | ScrubberEndChanged | ScrubberPlayToggled | ScrubberSpeedChanged | WorkflowsRequested | ReactorDependenciesLoaded | AggregateKeysLoaded | HandlerSelected | LocationChanged | AggregateLifecycleLoaded | AggregateLifecycleRequested | LoadMoreWorkflowsRequested | SubjectSelected | SubjectModeChanged | SubjectChainLoadMore | EventEffectsRequested | SubjectChainLoaded | EventEffectsLoaded;
|
|
86
111
|
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,
|
|
8
|
+
export type { InspectorEvent, InspectorEventsPage, InspectorCausalTree, InspectorCausalFlow, WorkflowSummary, ReactorDependency, AggregateLifecycleEntry, Block, ReactorLog, ReactorDescription, ReactorDescriptionSnapshot, AggregateStateEntry, AggregateTimelineEntry, ReactorOutcome, ReactorAttempt, FilterState, LogsFilter, FlowSelection, PaneLayout, SubjectChainSourceMode, SubjectChainMode, SubjectChainEvent, InspectorEffect, AggregateKeyEntry, AggregateKeysPage, } 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,
|
|
11
|
+
export { EVENTS_SUBSCRIPTION, INSPECTOR_EVENTS, INSPECTOR_CAUSAL_TREE, INSPECTOR_CAUSAL_FLOW, INSPECTOR_WORKFLOWS, INSPECTOR_REACTOR_LOGS, INSPECTOR_REACTOR_LOGS_BY_WORKFLOW, 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, INSPECTOR_SUBJECT_CHAIN, INSPECTOR_EFFECTS_FOR_EVENT, INSPECTOR_AGGREGATE_TYPES, INSPECTOR_AGGREGATE_KEYS_BY_TYPE, } 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";
|
|
@@ -17,6 +17,7 @@ export { FilterBar } from "./components/FilterBar";
|
|
|
17
17
|
export { CopyablePayload } from "./components/CopyablePayload";
|
|
18
18
|
export { JsonSyntax } from "./components/JsonSyntax";
|
|
19
19
|
export { GlobalScrubber } from "./components/GlobalScrubber";
|
|
20
|
+
export { EffectList } from "./components/EffectList";
|
|
20
21
|
export { TimelinePane } from "./panes/TimelinePane";
|
|
21
22
|
export type { TimelinePaneProps } from "./panes/TimelinePane";
|
|
22
23
|
export { CausalTreePane } from "./panes/CausalTreePane";
|
|
@@ -27,4 +28,5 @@ export { CausalFlowPane } from "./panes/CausalFlowPane";
|
|
|
27
28
|
export type { CausalFlowPaneProps } from "./panes/CausalFlowPane";
|
|
28
29
|
export { AggregateTimelinePane } from "./panes/AggregateTimelinePane";
|
|
29
30
|
export { WaterfallPane } from "./panes/WaterfallPane";
|
|
30
|
-
export {
|
|
31
|
+
export { WorkflowExplorerPane } from "./panes/WorkflowExplorerPane";
|
|
32
|
+
export { SubjectChainPane } from "./panes/SubjectChainPane";
|
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,
|
|
10
|
+
export { EVENTS_SUBSCRIPTION, INSPECTOR_EVENTS, INSPECTOR_CAUSAL_TREE, INSPECTOR_CAUSAL_FLOW, INSPECTOR_WORKFLOWS, INSPECTOR_REACTOR_LOGS, INSPECTOR_REACTOR_LOGS_BY_WORKFLOW, 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, INSPECTOR_SUBJECT_CHAIN, INSPECTOR_EFFECTS_FOR_EVENT, INSPECTOR_AGGREGATE_TYPES, INSPECTOR_AGGREGATE_KEYS_BY_TYPE, } from "./queries";
|
|
11
11
|
// ── Theme ──
|
|
12
12
|
export { eventHue, eventBg, eventBorder, eventTextColor, LOG_LEVEL_COLORS, } from "./theme";
|
|
13
13
|
// ── Utilities ──
|
|
@@ -19,6 +19,7 @@ export { FilterBar } from "./components/FilterBar";
|
|
|
19
19
|
export { CopyablePayload } from "./components/CopyablePayload";
|
|
20
20
|
export { JsonSyntax } from "./components/JsonSyntax";
|
|
21
21
|
export { GlobalScrubber } from "./components/GlobalScrubber";
|
|
22
|
+
export { EffectList } from "./components/EffectList";
|
|
22
23
|
// ── Panes ──
|
|
23
24
|
export { TimelinePane } from "./panes/TimelinePane";
|
|
24
25
|
export { CausalTreePane } from "./panes/CausalTreePane";
|
|
@@ -26,4 +27,5 @@ export { LogsPane } from "./panes/LogsPane";
|
|
|
26
27
|
export { CausalFlowPane } from "./panes/CausalFlowPane";
|
|
27
28
|
export { AggregateTimelinePane } from "./panes/AggregateTimelinePane";
|
|
28
29
|
export { WaterfallPane } from "./panes/WaterfallPane";
|
|
29
|
-
export {
|
|
30
|
+
export { WorkflowExplorerPane } from "./panes/WorkflowExplorerPane";
|
|
31
|
+
export { SubjectChainPane } from "./panes/SubjectChainPane";
|
|
@@ -144,8 +144,8 @@ function AggregateCard({ aggregateKey, state, prevState, diffMode, }) {
|
|
|
144
144
|
}, children: [_jsx("span", { style: { fontSize: 9, color: "#52525b", transform: collapsed ? "rotate(-90deg)" : "rotate(0deg)", transition: "transform 100ms" }, children: "\u25BC" }), _jsx("span", { style: { fontSize: 11, fontWeight: 500, color: "#e4e4e7" }, children: aggType }), aggId && (_jsx("span", { style: { fontSize: 10, color: "#71717a", fontFamily: "monospace" }, children: aggId })), showDiff && diffs.length > 0 && (_jsxs("span", { style: { fontSize: 9, color: "#fbbf24", marginLeft: "auto" }, children: [diffs.length, " change", diffs.length !== 1 ? "s" : ""] })), showDiff && diffs.length === 0 && (_jsx("span", { style: { fontSize: 9, color: "#52525b", marginLeft: "auto" }, children: "unchanged" }))] }), !collapsed && (_jsx("div", { style: { padding: "4px 8px 6px" }, children: showDiff ? _jsx(DiffView, { diffs: diffs }) : _jsx(InlineJson, { value: state }) }))] }));
|
|
145
145
|
}
|
|
146
146
|
export function AggregateTimelinePane() {
|
|
147
|
-
const
|
|
148
|
-
const entries = useSelector((s) =>
|
|
147
|
+
const workflowId = useSelector((s) => s.flowWorkflowId);
|
|
148
|
+
const entries = useSelector((s) => workflowId ? s.aggregateTimeline[workflowId] ?? [] : []);
|
|
149
149
|
const scrubberStart = useSelector((s) => s.scrubberStart);
|
|
150
150
|
const scrubberEnd = useSelector((s) => s.scrubberEnd);
|
|
151
151
|
const logsFilter = useSelector((s) => s.logsFilter);
|
|
@@ -185,11 +185,11 @@ export function AggregateTimelinePane() {
|
|
|
185
185
|
const handleRowClick = useCallback((seq) => {
|
|
186
186
|
dispatch({ type: "ui/scrubber_end_changed", payload: { end: seq } });
|
|
187
187
|
}, [dispatch]);
|
|
188
|
-
if (!
|
|
188
|
+
if (!workflowId) {
|
|
189
189
|
return (_jsx("div", { style: { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "#50506a", fontSize: 12, letterSpacing: "0.03em" }, children: "Open a flow to see the aggregate state timeline" }));
|
|
190
190
|
}
|
|
191
191
|
if (entries.length === 0) {
|
|
192
|
-
return (_jsx("div", { style: { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "#50506a", fontSize: 12, letterSpacing: "0.03em" }, children: "No aggregate state snapshots for this
|
|
192
|
+
return (_jsx("div", { style: { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "#50506a", fontSize: 12, letterSpacing: "0.03em" }, children: "No aggregate state snapshots for this workflow" }));
|
|
193
193
|
}
|
|
194
194
|
return (_jsxs("div", { style: { height: "100%", display: "flex", flexDirection: "column" }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 4, padding: "6px 12px", borderBottom: "1px solid rgba(255,255,255,0.06)", flexShrink: 0, background: "rgba(15, 15, 20, 0.6)", backdropFilter: "blur(8px)" }, children: [_jsx("button", { onClick: () => setDiffMode(false), style: {
|
|
195
195
|
fontSize: 10,
|
|
@@ -126,10 +126,10 @@ function buildFlowGraph(events, descriptions, outcomes, hiddenReactors, directio
|
|
|
126
126
|
children.add(groupKey);
|
|
127
127
|
reactorToChildTypes.set(evt.reactorId, children);
|
|
128
128
|
}
|
|
129
|
-
if (evt.
|
|
130
|
-
const reactors = parentToReactor.get(evt.
|
|
129
|
+
if (evt.causationId && evt.reactorId) {
|
|
130
|
+
const reactors = parentToReactor.get(evt.causationId) ?? new Set();
|
|
131
131
|
reactors.add(evt.reactorId);
|
|
132
|
-
parentToReactor.set(evt.
|
|
132
|
+
parentToReactor.set(evt.causationId, reactors);
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
const eventIdToGroup = new Map();
|
|
@@ -334,8 +334,8 @@ function computeVisibleIds(allEvents, start, end) {
|
|
|
334
334
|
edgeIds.add(`hdl:${evt.reactorId}->evt:${evt.name}`);
|
|
335
335
|
}
|
|
336
336
|
// Parent event -> reactor edge
|
|
337
|
-
if (evt.
|
|
338
|
-
const parentGroup = eventIdToGroup.get(evt.
|
|
337
|
+
if (evt.causationId && evt.reactorId) {
|
|
338
|
+
const parentGroup = eventIdToGroup.get(evt.causationId);
|
|
339
339
|
if (parentGroup) {
|
|
340
340
|
edgeIds.add(`evt:${parentGroup}->hdl:${evt.reactorId}`);
|
|
341
341
|
}
|
|
@@ -343,7 +343,7 @@ function computeVisibleIds(allEvents, start, end) {
|
|
|
343
343
|
// Root event -> reactor edges
|
|
344
344
|
if (!evt.reactorId && evt.id) {
|
|
345
345
|
for (const child of visible) {
|
|
346
|
-
if (child.
|
|
346
|
+
if (child.causationId === evt.id && child.reactorId) {
|
|
347
347
|
const rootGroup = eventIdToGroup.get(evt.id);
|
|
348
348
|
if (rootGroup) {
|
|
349
349
|
edgeIds.add(`evt:${rootGroup}->hdl:${child.reactorId}`);
|
|
@@ -427,10 +427,10 @@ 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, 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" }))] })] }))] }));
|
|
431
431
|
}
|
|
432
432
|
export function CausalFlowPane({ defaultHiddenReactors, headerExtra } = {}) {
|
|
433
|
-
const
|
|
433
|
+
const flowWorkflowId = useSelector((s) => s.flowWorkflowId);
|
|
434
434
|
const flowData = useSelector((s) => s.flowData);
|
|
435
435
|
const flowSelection = useSelector((s) => s.flowSelection);
|
|
436
436
|
const descriptionsMap = useSelector((s) => s.descriptions);
|
|
@@ -438,30 +438,30 @@ export function CausalFlowPane({ defaultHiddenReactors, headerExtra } = {}) {
|
|
|
438
438
|
const scrubberStart = useSelector((s) => s.scrubberStart);
|
|
439
439
|
const scrubberEnd = useSelector((s) => s.scrubberEnd);
|
|
440
440
|
const dispatch = useDispatch();
|
|
441
|
-
const flowLoading =
|
|
441
|
+
const flowLoading = flowWorkflowId != null && flowData.length === 0;
|
|
442
442
|
// Build typed maps from state
|
|
443
443
|
const descriptions = useMemo(() => {
|
|
444
|
-
if (!
|
|
444
|
+
if (!flowWorkflowId)
|
|
445
445
|
return undefined;
|
|
446
|
-
const raw = descriptionsMap[
|
|
446
|
+
const raw = descriptionsMap[flowWorkflowId];
|
|
447
447
|
if (!raw)
|
|
448
448
|
return undefined;
|
|
449
449
|
const map = new Map();
|
|
450
450
|
for (const d of raw)
|
|
451
451
|
map.set(d.reactorId, d.blocks);
|
|
452
452
|
return map;
|
|
453
|
-
}, [descriptionsMap,
|
|
453
|
+
}, [descriptionsMap, flowWorkflowId]);
|
|
454
454
|
const outcomes = useMemo(() => {
|
|
455
|
-
if (!
|
|
455
|
+
if (!flowWorkflowId)
|
|
456
456
|
return undefined;
|
|
457
|
-
const raw = outcomesMap[
|
|
457
|
+
const raw = outcomesMap[flowWorkflowId];
|
|
458
458
|
if (!raw)
|
|
459
459
|
return undefined;
|
|
460
460
|
const map = new Map();
|
|
461
461
|
for (const o of raw)
|
|
462
462
|
map.set(o.reactorId, o);
|
|
463
463
|
return map;
|
|
464
|
-
}, [outcomesMap,
|
|
464
|
+
}, [outcomesMap, flowWorkflowId]);
|
|
465
465
|
const [hiddenReactors, setHiddenReactors] = useState(() => defaultHiddenReactors ?? new Set());
|
|
466
466
|
const [direction, setDirection] = useState("LR");
|
|
467
467
|
const allReactorIds = useMemo(() => {
|
|
@@ -590,11 +590,11 @@ export function CausalFlowPane({ defaultHiddenReactors, headerExtra } = {}) {
|
|
|
590
590
|
dispatch({ type: "ui/flow_node_selected", payload: null });
|
|
591
591
|
}, [dispatch]);
|
|
592
592
|
const onNodesChange = useCallback((_changes) => { }, []);
|
|
593
|
-
if (!
|
|
594
|
-
return (_jsx("div", { className: "
|
|
593
|
+
if (!flowWorkflowId) {
|
|
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" }));
|
|
595
595
|
}
|
|
596
596
|
if (flowLoading) {
|
|
597
|
-
return (_jsxs("div", { className: "
|
|
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" })] })] })] }) })] }));
|
|
598
598
|
}
|
|
599
|
-
return (_jsxs("div", { className: "
|
|
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: flowWorkflowId }), _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 })] }) })] }));
|
|
600
600
|
}
|
|
@@ -2,9 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useState, useMemo, useRef, useEffect, useCallback } from "react";
|
|
3
3
|
import { useSelector, useDispatch } from "../machine";
|
|
4
4
|
import { CopyablePayload } from "../components/CopyablePayload";
|
|
5
|
+
import { EffectList } from "../components/EffectList";
|
|
5
6
|
import { eventTextColor } from "../theme";
|
|
6
7
|
import { formatTs, compactPayload, copyToClipboard, inScrubberRange } from "../utils";
|
|
7
|
-
import { Copy, Check, Search, X, ChevronRight, ChevronDown, AlertTriangle } from "lucide-react";
|
|
8
|
+
import { Copy, Check, Search, X, ChevronRight, ChevronDown, AlertTriangle, Zap } from "lucide-react";
|
|
8
9
|
function buildTreeJson(roots, childrenMap) {
|
|
9
10
|
function toNode(evt) {
|
|
10
11
|
const children = evt.id ? (childrenMap.get(evt.id) ?? []) : [];
|
|
@@ -36,17 +37,11 @@ function ReactorNode({ reactorId, parentEventId, children, childrenMap, depth, i
|
|
|
36
37
|
}, [onClickReactor, parentEventId, reactorId]);
|
|
37
38
|
const isError = outcome?.status === "error";
|
|
38
39
|
const isRunning = outcome?.status === "running";
|
|
39
|
-
return (_jsxs("div", {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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)))] }));
|
|
40
|
+
return (_jsxs("div", { className: depth > 0 ? "pl-6" : "", children: [_jsxs("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" : ""} ${isError ? "bg-red-500/8" : ""}`, 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 italic border ${isError
|
|
41
|
+
? "bg-red-500/10 text-red-400/80 border-red-500/20"
|
|
42
|
+
: isRunning
|
|
43
|
+
? "bg-yellow-500/10 text-yellow-400/80 border-yellow-500/20"
|
|
44
|
+
: "bg-white/[0.04] text-muted-foreground/60 border-border"}`, children: "reactor" }), _jsx("span", { className: "text-[10px] font-mono text-foreground/60 shrink-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", { className: "text-[10px] text-muted-foreground shrink-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)))] }));
|
|
50
45
|
}
|
|
51
46
|
// ---------------------------------------------------------------------------
|
|
52
47
|
// TreeNode (recursive)
|
|
@@ -54,8 +49,11 @@ function ReactorNode({ reactorId, parentEventId, children, childrenMap, depth, i
|
|
|
54
49
|
function TreeNode({ event, childrenMap, depth, onClickReactor, onInvestigate, outcomesByReactor, }) {
|
|
55
50
|
const selectedSeq = useSelector((s) => s.selectedSeq);
|
|
56
51
|
const flowSelection = useSelector((s) => s.flowSelection);
|
|
52
|
+
const expandedEffects = useSelector((s) => s.expandedEffects);
|
|
53
|
+
const loadingEffectsIds = useSelector((s) => s.loadingEffects);
|
|
57
54
|
const dispatch = useDispatch();
|
|
58
55
|
const [payloadOpen, setPayloadOpen] = useState(false);
|
|
56
|
+
const [effectsOpen, setEffectsOpen] = useState(false);
|
|
59
57
|
const [collapsed, setCollapsed] = useState(false);
|
|
60
58
|
const [copied, setCopied] = useState(false);
|
|
61
59
|
const isSelected = event.seq === selectedSeq;
|
|
@@ -84,19 +82,30 @@ function TreeNode({ event, childrenMap, depth, onClickReactor, onInvestigate, ou
|
|
|
84
82
|
return { reactorGroups: groups, directChildren: direct };
|
|
85
83
|
}, [children]);
|
|
86
84
|
const highlightedReactorId = flowSelection?.kind === "reactor" ? flowSelection.reactorId : null;
|
|
87
|
-
return (_jsxs("div", {
|
|
85
|
+
return (_jsxs("div", { className: depth > 0 ? "pl-6" : "", children: [_jsxs("div", { ref: isSelected ? nodeRef : undefined, onClick: () => {
|
|
88
86
|
dispatch({ type: "ui/event_selected", payload: { seq: event.seq } });
|
|
89
|
-
if (event.
|
|
90
|
-
dispatch({ type: "ui/flow_opened", payload: {
|
|
87
|
+
if (event.workflowId) {
|
|
88
|
+
dispatch({ type: "ui/flow_opened", payload: { workflowId: event.workflowId } });
|
|
91
89
|
}
|
|
92
|
-
}, className: `
|
|
90
|
+
}, 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, ")"] })), event.aggregateType && event.aggregateId && (_jsxs("button", { onClick: (e) => {
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
dispatch({ type: "ui/subject_selected", payload: { aggregateType: event.aggregateType, aggregateId: event.aggregateId, mode: "both" } });
|
|
93
|
+
}, className: "px-1.5 py-0.5 rounded-full text-[9px] font-mono bg-teal-500/8 text-teal-400/80 hover:bg-teal-500/15 hover:text-teal-400 shrink-0 transition-all border border-teal-500/10", title: `View subject ${event.aggregateType}:${event.aggregateId}`, children: [event.aggregateType, ":", event.aggregateId.slice(0, 8)] })), _jsx("span", { className: "text-[10px] text-muted-foreground shrink-0", children: formatTs(event.ts) }), event.id && (_jsxs("button", { onClick: (e) => {
|
|
94
|
+
e.stopPropagation();
|
|
95
|
+
if (!effectsOpen && expandedEffects[event.id] === undefined) {
|
|
96
|
+
dispatch({ type: "ui/event_effects_requested", payload: { eventId: event.id } });
|
|
97
|
+
}
|
|
98
|
+
setEffectsOpen((v) => !v);
|
|
99
|
+
}, className: `opacity-0 group-hover/tree:opacity-100 transition-all duration-150 flex items-center gap-1 px-1.5 py-0.5 rounded text-[9px] ${effectsOpen
|
|
100
|
+
? "opacity-100 bg-indigo-500/10 text-indigo-400/70 border border-indigo-500/20"
|
|
101
|
+
: "hover:bg-white/[0.05] text-muted-foreground/50 border border-transparent"}`, title: "Show effects", children: [_jsx(Zap, { size: 9 }), expandedEffects[event.id] !== undefined && expandedEffects[event.id].length > 0 && (_jsx("span", { children: expandedEffects[event.id].length }))] })), _jsx("button", { onClick: (e) => {
|
|
93
102
|
e.stopPropagation();
|
|
94
103
|
const json = buildTreeJson([event], childrenMap);
|
|
95
104
|
const text = JSON.stringify(json[0], null, 2);
|
|
96
105
|
copyToClipboard(text);
|
|
97
106
|
setCopied(true);
|
|
98
107
|
setTimeout(() => setCopied(false), 1500);
|
|
99
|
-
}, className: "
|
|
108
|
+
}, 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" })), effectsOpen && (_jsx("div", { className: "mt-1 ml-3", children: loadingEffectsIds.includes(event.id ?? "") ? (_jsx("div", { className: "text-[9px] text-muted-foreground/40 italic py-1", children: "Loading\u2026" })) : (_jsx(EffectList, { effects: event.id ? (expandedEffects[event.id] ?? []) : [] })) }))] }), !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)))] }))] }));
|
|
100
109
|
}
|
|
101
110
|
// ---------------------------------------------------------------------------
|
|
102
111
|
// matchesFlowSelection
|
|
@@ -112,23 +121,23 @@ export function CausalTreePane({ onInvestigate } = {}) {
|
|
|
112
121
|
const causalTree = useSelector((s) => s.causalTree);
|
|
113
122
|
const selectedSeq = useSelector((s) => s.selectedSeq);
|
|
114
123
|
const flowSelection = useSelector((s) => s.flowSelection);
|
|
115
|
-
const
|
|
124
|
+
const flowWorkflowId = useSelector((s) => s.flowWorkflowId);
|
|
116
125
|
const scrubberStart = useSelector((s) => s.scrubberStart);
|
|
117
126
|
const scrubberEnd = useSelector((s) => s.scrubberEnd);
|
|
118
127
|
const outcomesMap = useSelector((s) => s.outcomes);
|
|
119
128
|
const dispatch = useDispatch();
|
|
120
129
|
// Build reactor outcome lookup for current flow
|
|
121
130
|
const outcomesByReactor = useMemo(() => {
|
|
122
|
-
if (!
|
|
131
|
+
if (!flowWorkflowId)
|
|
123
132
|
return new Map();
|
|
124
|
-
const raw = outcomesMap[
|
|
133
|
+
const raw = outcomesMap[flowWorkflowId];
|
|
125
134
|
if (!raw)
|
|
126
135
|
return new Map();
|
|
127
136
|
const map = new Map();
|
|
128
137
|
for (const o of raw)
|
|
129
138
|
map.set(o.reactorId, o);
|
|
130
139
|
return map;
|
|
131
|
-
}, [outcomesMap,
|
|
140
|
+
}, [outcomesMap, flowWorkflowId]);
|
|
132
141
|
const treeEvents = useMemo(() => {
|
|
133
142
|
const all = causalTree?.events ?? null;
|
|
134
143
|
if (all == null || (scrubberStart == null && scrubberEnd == null))
|
|
@@ -137,47 +146,47 @@ export function CausalTreePane({ onInvestigate } = {}) {
|
|
|
137
146
|
}, [causalTree?.events, scrubberStart, scrubberEnd]);
|
|
138
147
|
const treeLoading = selectedSeq != null && causalTree == null;
|
|
139
148
|
const onClickReactor = useCallback((reactorId, _parentEventId) => {
|
|
140
|
-
if (
|
|
149
|
+
if (flowWorkflowId) {
|
|
141
150
|
dispatch({ type: "ui/flow_node_selected", payload: { kind: "reactor", reactorId } });
|
|
142
151
|
}
|
|
143
152
|
dispatch({ type: "ui/handler_selected", payload: { reactorId } });
|
|
144
|
-
}, [
|
|
153
|
+
}, [flowWorkflowId, dispatch]);
|
|
145
154
|
const { roots, childrenMap, totalCount, filteredCount } = useMemo(() => {
|
|
146
155
|
if (!treeEvents || treeEvents.length === 0)
|
|
147
156
|
return { roots: [], childrenMap: new Map(), totalCount: 0, filteredCount: 0 };
|
|
148
157
|
const total = treeEvents.length;
|
|
149
|
-
const events = (
|
|
158
|
+
const events = (flowWorkflowId && flowSelection)
|
|
150
159
|
? treeEvents.filter(e => matchesFlowSelection(e, flowSelection))
|
|
151
160
|
: treeEvents;
|
|
152
161
|
const idSet = new Set(events.map(e => e.id).filter(Boolean));
|
|
153
162
|
const cMap = new Map();
|
|
154
163
|
const rootList = [];
|
|
155
164
|
for (const evt of events) {
|
|
156
|
-
if (evt.
|
|
165
|
+
if (evt.causationId == null || !idSet.has(evt.causationId)) {
|
|
157
166
|
rootList.push(evt);
|
|
158
167
|
}
|
|
159
168
|
else {
|
|
160
|
-
const siblings = cMap.get(evt.
|
|
169
|
+
const siblings = cMap.get(evt.causationId) ?? [];
|
|
161
170
|
siblings.push(evt);
|
|
162
|
-
cMap.set(evt.
|
|
171
|
+
cMap.set(evt.causationId, siblings);
|
|
163
172
|
}
|
|
164
173
|
}
|
|
165
174
|
rootList.sort((a, b) => a.seq - b.seq);
|
|
166
175
|
const filtered = rootList.length + [...cMap.values()].reduce((s, a) => s + a.length, 0);
|
|
167
176
|
return { roots: rootList, childrenMap: cMap, totalCount: total, filteredCount: filtered };
|
|
168
|
-
}, [treeEvents,
|
|
177
|
+
}, [treeEvents, flowWorkflowId, flowSelection]);
|
|
169
178
|
if (treeLoading) {
|
|
170
|
-
return (_jsxs("div", { className: "
|
|
179
|
+
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" })] })] })] }));
|
|
171
180
|
}
|
|
172
181
|
if (!treeEvents) {
|
|
173
|
-
return (_jsx("div", { className: "
|
|
182
|
+
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" }));
|
|
174
183
|
}
|
|
175
184
|
if (roots.length === 0 && flowSelection) {
|
|
176
|
-
return (_jsxs("div", {
|
|
185
|
+
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"
|
|
177
186
|
? flowSelection.name
|
|
178
|
-
: `outputs of ${flowSelection.reactorId}` }), _jsx("button", { onClick: () => dispatch({ type: "ui/flow_node_selected", payload: null }),
|
|
187
|
+
: `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" })] }));
|
|
179
188
|
}
|
|
180
|
-
return (_jsxs("div", {
|
|
189
|
+
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"
|
|
181
190
|
? flowSelection.name
|
|
182
|
-
: `outputs of ${flowSelection.reactorId}` }), _jsx("button", { onClick: () => dispatch({ type: "ui/flow_node_selected", payload: null }),
|
|
191
|
+
: `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, outcomesByReactor: outcomesByReactor }, root.seq)))] }));
|
|
183
192
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type
|
|
2
|
-
export declare function
|
|
1
|
+
export type WorkflowExplorerPaneProps = Record<string, never>;
|
|
2
|
+
export declare function WorkflowExplorerPane(): import("react/jsx-runtime").JSX.Element;
|