@workflow/web-shared 4.1.0-beta.52 → 4.1.0-beta.54
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/components/error-boundary.d.ts +15 -20
- package/dist/components/error-boundary.d.ts.map +1 -1
- package/dist/components/error-boundary.js +17 -31
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/event-list-view.d.ts +7 -6
- package/dist/components/event-list-view.d.ts.map +1 -1
- package/dist/components/event-list-view.js +492 -109
- package/dist/components/event-list-view.js.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/run-trace-view.d.ts +2 -1
- package/dist/components/run-trace-view.d.ts.map +1 -1
- package/dist/components/run-trace-view.js +2 -2
- package/dist/components/run-trace-view.js.map +1 -1
- package/dist/components/sidebar/attribute-panel.d.ts +2 -1
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
- package/dist/components/sidebar/attribute-panel.js +53 -142
- package/dist/components/sidebar/attribute-panel.js.map +1 -1
- package/dist/components/sidebar/conversation-view.d.ts.map +1 -1
- package/dist/components/sidebar/conversation-view.js +3 -17
- package/dist/components/sidebar/conversation-view.js.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.js +63 -10
- package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
- package/dist/components/sidebar/events-list.d.ts.map +1 -1
- package/dist/components/sidebar/events-list.js +4 -8
- package/dist/components/sidebar/events-list.js.map +1 -1
- package/dist/components/sidebar/resolve-hook-modal.d.ts +3 -0
- package/dist/components/sidebar/resolve-hook-modal.d.ts.map +1 -1
- package/dist/components/sidebar/resolve-hook-modal.js +152 -3
- package/dist/components/sidebar/resolve-hook-modal.js.map +1 -1
- package/dist/components/stream-viewer.d.ts +7 -5
- package/dist/components/stream-viewer.d.ts.map +1 -1
- package/dist/components/stream-viewer.js +54 -22
- package/dist/components/stream-viewer.js.map +1 -1
- package/dist/components/trace-viewer/components/markers.d.ts +2 -1
- package/dist/components/trace-viewer/components/markers.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/markers.js +59 -20
- package/dist/components/trace-viewer/components/markers.js.map +1 -1
- package/dist/components/trace-viewer/components/node.d.ts +5 -1
- package/dist/components/trace-viewer/components/node.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/node.js +250 -68
- package/dist/components/trace-viewer/components/node.js.map +1 -1
- package/dist/components/trace-viewer/components/span-content.d.ts +19 -0
- package/dist/components/trace-viewer/components/span-content.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-content.js +137 -0
- package/dist/components/trace-viewer/components/span-content.js.map +1 -0
- package/dist/components/trace-viewer/components/span-detail-panel.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/span-detail-panel.js +3 -2
- package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -1
- package/dist/components/trace-viewer/components/span-segments.d.ts +50 -0
- package/dist/components/trace-viewer/components/span-segments.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-segments.js +392 -0
- package/dist/components/trace-viewer/components/span-segments.js.map +1 -0
- package/dist/components/trace-viewer/components/span-strategies.d.ts +46 -0
- package/dist/components/trace-viewer/components/span-strategies.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-strategies.js +108 -0
- package/dist/components/trace-viewer/components/span-strategies.js.map +1 -0
- package/dist/components/trace-viewer/context.d.ts +7 -6
- package/dist/components/trace-viewer/context.d.ts.map +1 -1
- package/dist/components/trace-viewer/context.js +47 -18
- package/dist/components/trace-viewer/context.js.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.d.ts +5 -1
- package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.js +87 -11
- package/dist/components/trace-viewer/trace-viewer.js.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.module.css +179 -6
- package/dist/components/trace-viewer/util/timing.d.ts +5 -0
- package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
- package/dist/components/trace-viewer/util/timing.js +12 -0
- package/dist/components/trace-viewer/util/timing.js.map +1 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.d.ts +1 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.d.ts.map +1 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.js +29 -17
- package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -1
- package/dist/components/trace-viewer/worker.js +3 -1
- package/dist/components/trace-viewer/worker.js.map +1 -1
- package/dist/components/ui/alert.js +3 -3
- package/dist/components/ui/alert.js.map +1 -1
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/card.js +2 -2
- package/dist/components/ui/card.js.map +1 -1
- package/dist/components/ui/data-inspector.d.ts +17 -0
- package/dist/components/ui/data-inspector.d.ts.map +1 -0
- package/dist/components/ui/data-inspector.js +184 -0
- package/dist/components/ui/data-inspector.js.map +1 -0
- package/dist/components/ui/error-card.d.ts.map +1 -1
- package/dist/components/ui/error-card.js +4 -1
- package/dist/components/ui/error-card.js.map +1 -1
- package/dist/components/ui/inspector-theme.d.ts +39 -24
- package/dist/components/ui/inspector-theme.d.ts.map +1 -1
- package/dist/components/ui/inspector-theme.js +90 -38
- package/dist/components/ui/inspector-theme.js.map +1 -1
- package/dist/components/ui/skeleton.d.ts +1 -1
- package/dist/components/ui/skeleton.d.ts.map +1 -1
- package/dist/components/ui/skeleton.js +2 -2
- package/dist/components/ui/skeleton.js.map +1 -1
- package/dist/components/workflow-trace-view.d.ts +3 -1
- package/dist/components/workflow-trace-view.d.ts.map +1 -1
- package/dist/components/workflow-trace-view.js +435 -21
- package/dist/components/workflow-trace-view.js.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.d.ts +1 -1
- package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.js +2 -2
- package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
- package/dist/lib/hydration.d.ts.map +1 -1
- package/dist/lib/hydration.js +17 -3
- package/dist/lib/hydration.js.map +1 -1
- package/dist/styles.css +186 -0
- package/package.json +8 -7
- package/src/components/error-boundary.tsx +29 -40
- package/src/components/event-list-view.tsx +1000 -287
- package/src/components/index.ts +1 -0
- package/src/components/run-trace-view.tsx +3 -0
- package/src/components/sidebar/attribute-panel.tsx +58 -258
- package/src/components/sidebar/conversation-view.tsx +30 -27
- package/src/components/sidebar/entity-detail-panel.tsx +86 -20
- package/src/components/sidebar/events-list.tsx +4 -11
- package/src/components/sidebar/resolve-hook-modal.tsx +206 -47
- package/src/components/stream-viewer.tsx +138 -61
- package/src/components/trace-viewer/components/markers.tsx +69 -21
- package/src/components/trace-viewer/components/node.tsx +346 -100
- package/src/components/trace-viewer/components/span-content.tsx +247 -0
- package/src/components/trace-viewer/components/span-detail-panel.tsx +7 -2
- package/src/components/trace-viewer/components/span-segments.ts +516 -0
- package/src/components/trace-viewer/components/span-strategies.ts +205 -0
- package/src/components/trace-viewer/context.tsx +92 -40
- package/src/components/trace-viewer/trace-viewer.module.css +179 -6
- package/src/components/trace-viewer/trace-viewer.tsx +115 -11
- package/src/components/trace-viewer/util/timing.ts +13 -0
- package/src/components/trace-viewer/util/use-streaming-spans.ts +28 -17
- package/src/components/trace-viewer/worker.ts +4 -1
- package/src/components/ui/alert.tsx +3 -3
- package/src/components/ui/card.tsx +3 -5
- package/src/components/ui/data-inspector.tsx +318 -0
- package/src/components/ui/error-card.tsx +17 -6
- package/src/components/ui/inspector-theme.ts +127 -39
- package/src/components/ui/skeleton.tsx +3 -1
- package/src/components/workflow-trace-view.tsx +625 -26
- package/src/components/workflow-traces/trace-span-construction.ts +3 -2
- package/src/lib/hydration.ts +17 -8
- package/src/styles.css +186 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
import type { RootNode, ScrollSnapshot, VisibleSpan } from '../types';
|
|
3
|
+
import { MARKER_HEIGHT, ROW_HEIGHT, ROW_PADDING } from '../util/constants';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The resource type of a workflow span, read from span.attributes.resource.
|
|
7
|
+
* Falls back to 'default' for non-workflow (generic OTEL) spans.
|
|
8
|
+
*/
|
|
9
|
+
export type ResourceType = 'run' | 'step' | 'hook' | 'sleep' | 'default';
|
|
10
|
+
|
|
11
|
+
/** Minimum rendered width so very short spans are always visible */
|
|
12
|
+
const MIN_SPAN_WIDTH = 4;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Layout result computed by a span strategy. Controls how the span
|
|
16
|
+
* is sized and positioned in the timeline.
|
|
17
|
+
*/
|
|
18
|
+
export interface SpanLayout {
|
|
19
|
+
/** The rendered width in pixels */
|
|
20
|
+
width: number;
|
|
21
|
+
/** The actual duration-based width (before min-width clamping) */
|
|
22
|
+
actualWidth: number;
|
|
23
|
+
/** The rendered height in pixels */
|
|
24
|
+
height: number;
|
|
25
|
+
/** The y position in pixels */
|
|
26
|
+
top: number;
|
|
27
|
+
/** The x position in pixels (from left) */
|
|
28
|
+
left: number;
|
|
29
|
+
/** Whether the span is considered "small" (< 64px actual width) */
|
|
30
|
+
isSmall: boolean;
|
|
31
|
+
/** Whether the span is considered "huge" (>= 500px actual width) */
|
|
32
|
+
isHuge: boolean;
|
|
33
|
+
/** Whether the span is currently hovered (with hover eligibility) */
|
|
34
|
+
isHovered: boolean;
|
|
35
|
+
/** Whether the span is expanded (hovered or selected) — controls sizing */
|
|
36
|
+
isExpanded: boolean;
|
|
37
|
+
/** Whether the span is near the right side of the visible area */
|
|
38
|
+
isNearRightSide: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Reads the resource type from a span's attributes.
|
|
43
|
+
*/
|
|
44
|
+
export function getResourceType(node: VisibleSpan): ResourceType {
|
|
45
|
+
const resource = node.span.attributes?.resource;
|
|
46
|
+
if (
|
|
47
|
+
resource === 'run' ||
|
|
48
|
+
resource === 'step' ||
|
|
49
|
+
resource === 'hook' ||
|
|
50
|
+
resource === 'sleep'
|
|
51
|
+
) {
|
|
52
|
+
return resource;
|
|
53
|
+
}
|
|
54
|
+
return 'default';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
58
|
+
// Shared helpers
|
|
59
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
function computeIsSmall(actualWidth: number): boolean {
|
|
62
|
+
return actualWidth < 64;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function computeIsHuge(actualWidth: number): boolean {
|
|
66
|
+
return actualWidth >= 500;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function computeIsHovered(node: VisibleSpan, isHuge: boolean): boolean {
|
|
70
|
+
return node.isHovered && !isHuge && node.isHighlighted !== false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function computeIsNearRightSide(
|
|
74
|
+
node: VisibleSpan,
|
|
75
|
+
root: RootNode,
|
|
76
|
+
scrollSnapshotRef: RefObject<ScrollSnapshot | undefined>,
|
|
77
|
+
isHovered: boolean
|
|
78
|
+
): boolean {
|
|
79
|
+
if (!isHovered) return false;
|
|
80
|
+
|
|
81
|
+
let visibleDuration = root.duration;
|
|
82
|
+
let visibleEndTime = root.endTime;
|
|
83
|
+
const snapshot = scrollSnapshotRef.current;
|
|
84
|
+
if (snapshot) {
|
|
85
|
+
visibleDuration = snapshot.endTime - snapshot.startTime;
|
|
86
|
+
visibleEndTime = snapshot.endTime;
|
|
87
|
+
}
|
|
88
|
+
return visibleEndTime - node.startTime < 0.25 * visibleDuration;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
92
|
+
// Default layout (used by step, hook, run, default — all behave the same today)
|
|
93
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
function computeDefaultLayout(
|
|
96
|
+
node: VisibleSpan,
|
|
97
|
+
root: RootNode,
|
|
98
|
+
scale: number,
|
|
99
|
+
scrollSnapshotRef: RefObject<ScrollSnapshot | undefined>
|
|
100
|
+
): SpanLayout {
|
|
101
|
+
const left = (node.startTime - root.startTime) * scale;
|
|
102
|
+
let top = MARKER_HEIGHT + (ROW_HEIGHT + ROW_PADDING) * node.row;
|
|
103
|
+
const actualWidth = node.duration * scale;
|
|
104
|
+
const width = Math.max(actualWidth, MIN_SPAN_WIDTH);
|
|
105
|
+
let height = ROW_HEIGHT;
|
|
106
|
+
|
|
107
|
+
const isHuge = computeIsHuge(actualWidth);
|
|
108
|
+
const isSmall = computeIsSmall(actualWidth);
|
|
109
|
+
const isHovered = computeIsHovered(node, isHuge);
|
|
110
|
+
const isExpanded = isHovered || Boolean(node.isSelected);
|
|
111
|
+
|
|
112
|
+
if (isSmall && !isExpanded) {
|
|
113
|
+
height *= 0.4;
|
|
114
|
+
top += (ROW_HEIGHT - height) * 0.5;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const isNearRightSide = computeIsNearRightSide(
|
|
118
|
+
node,
|
|
119
|
+
root,
|
|
120
|
+
scrollSnapshotRef,
|
|
121
|
+
isExpanded
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
width,
|
|
126
|
+
actualWidth,
|
|
127
|
+
height,
|
|
128
|
+
top,
|
|
129
|
+
left,
|
|
130
|
+
isSmall,
|
|
131
|
+
isHuge,
|
|
132
|
+
isHovered,
|
|
133
|
+
isExpanded,
|
|
134
|
+
isNearRightSide,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
139
|
+
// Per-type layout functions
|
|
140
|
+
// Each returns the same result today but provides a clear extension point.
|
|
141
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
export function getRunLayout(
|
|
144
|
+
node: VisibleSpan,
|
|
145
|
+
root: RootNode,
|
|
146
|
+
scale: number,
|
|
147
|
+
scrollSnapshotRef: RefObject<ScrollSnapshot | undefined>
|
|
148
|
+
): SpanLayout {
|
|
149
|
+
return computeDefaultLayout(node, root, scale, scrollSnapshotRef);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function getStepLayout(
|
|
153
|
+
node: VisibleSpan,
|
|
154
|
+
root: RootNode,
|
|
155
|
+
scale: number,
|
|
156
|
+
scrollSnapshotRef: RefObject<ScrollSnapshot | undefined>
|
|
157
|
+
): SpanLayout {
|
|
158
|
+
return computeDefaultLayout(node, root, scale, scrollSnapshotRef);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function getHookLayout(
|
|
162
|
+
node: VisibleSpan,
|
|
163
|
+
root: RootNode,
|
|
164
|
+
scale: number,
|
|
165
|
+
scrollSnapshotRef: RefObject<ScrollSnapshot | undefined>
|
|
166
|
+
): SpanLayout {
|
|
167
|
+
return computeDefaultLayout(node, root, scale, scrollSnapshotRef);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function getSleepLayout(
|
|
171
|
+
node: VisibleSpan,
|
|
172
|
+
root: RootNode,
|
|
173
|
+
scale: number,
|
|
174
|
+
scrollSnapshotRef: RefObject<ScrollSnapshot | undefined>
|
|
175
|
+
): SpanLayout {
|
|
176
|
+
return computeDefaultLayout(node, root, scale, scrollSnapshotRef);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
180
|
+
// Dispatcher
|
|
181
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Computes the layout for a span based on its resource type.
|
|
185
|
+
*/
|
|
186
|
+
export function getSpanLayout(
|
|
187
|
+
resourceType: ResourceType,
|
|
188
|
+
node: VisibleSpan,
|
|
189
|
+
root: RootNode,
|
|
190
|
+
scale: number,
|
|
191
|
+
scrollSnapshotRef: RefObject<ScrollSnapshot | undefined>
|
|
192
|
+
): SpanLayout {
|
|
193
|
+
switch (resourceType) {
|
|
194
|
+
case 'run':
|
|
195
|
+
return getRunLayout(node, root, scale, scrollSnapshotRef);
|
|
196
|
+
case 'step':
|
|
197
|
+
return getStepLayout(node, root, scale, scrollSnapshotRef);
|
|
198
|
+
case 'hook':
|
|
199
|
+
return getHookLayout(node, root, scale, scrollSnapshotRef);
|
|
200
|
+
case 'sleep':
|
|
201
|
+
return getSleepLayout(node, root, scale, scrollSnapshotRef);
|
|
202
|
+
default:
|
|
203
|
+
return computeDefaultLayout(node, root, scale, scrollSnapshotRef);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -117,12 +117,6 @@ export interface TraceViewerState {
|
|
|
117
117
|
* Whether the trace viewer is small enough that it should be in mobile mode
|
|
118
118
|
*/
|
|
119
119
|
isMobile: boolean;
|
|
120
|
-
/**
|
|
121
|
-
* @deprecated Panel rendering has been moved outside the context.
|
|
122
|
-
* This field is kept for backwards compatibility but is no longer
|
|
123
|
-
* used by the workflow trace viewer.
|
|
124
|
-
*/
|
|
125
|
-
customPanelComponent: ReactNode | null;
|
|
126
120
|
/**
|
|
127
121
|
* A function to provide custom class names for spans.
|
|
128
122
|
*/
|
|
@@ -216,6 +210,13 @@ export type TraceViewerAction =
|
|
|
216
210
|
}
|
|
217
211
|
| {
|
|
218
212
|
type: 'forceRender';
|
|
213
|
+
}
|
|
214
|
+
| {
|
|
215
|
+
/** Like setRoot but preserves scroll position and memo cache (for incremental data updates) */
|
|
216
|
+
type: 'updateRoot';
|
|
217
|
+
root: RootNode;
|
|
218
|
+
spanMap: Record<string, SpanNode>;
|
|
219
|
+
resources: Resource[];
|
|
219
220
|
};
|
|
220
221
|
|
|
221
222
|
export interface TraceViewerContextProps {
|
|
@@ -251,16 +252,10 @@ export const initialState: TraceViewerState = {
|
|
|
251
252
|
getQuickLinks: () => [],
|
|
252
253
|
withPanel: false,
|
|
253
254
|
isMobile: false,
|
|
254
|
-
customPanelComponent: null,
|
|
255
255
|
};
|
|
256
256
|
|
|
257
257
|
const getMinScale = (state: TraceViewerState): number => {
|
|
258
|
-
return (
|
|
259
|
-
(state.width -
|
|
260
|
-
state.scrollbarWidth -
|
|
261
|
-
Number(Boolean(state.selected && state.withPanel)) * state.panelWidth) /
|
|
262
|
-
state.root.duration
|
|
263
|
-
);
|
|
258
|
+
return (state.timelineWidth - state.scrollbarWidth) / state.root.duration;
|
|
264
259
|
};
|
|
265
260
|
|
|
266
261
|
export const TraceViewerContext = createContext<TraceViewerContextProps>({
|
|
@@ -324,11 +319,20 @@ const reducer: Reducer<TraceViewerState, TraceViewerAction> = (
|
|
|
324
319
|
240,
|
|
325
320
|
Math.min(action.width, state.width - 240)
|
|
326
321
|
);
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
322
|
+
const timelineWidth =
|
|
323
|
+
state.withPanel && state.selected && !state.isMobile
|
|
324
|
+
? state.width - panelWidth
|
|
325
|
+
: state.width;
|
|
326
|
+
return reducer(
|
|
327
|
+
{
|
|
328
|
+
...state,
|
|
329
|
+
timelineWidth,
|
|
330
|
+
panelWidth,
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
type: 'detectBaseScale',
|
|
334
|
+
}
|
|
335
|
+
);
|
|
332
336
|
}
|
|
333
337
|
case 'setFilter':
|
|
334
338
|
return {
|
|
@@ -343,7 +347,7 @@ const reducer: Reducer<TraceViewerState, TraceViewerAction> = (
|
|
|
343
347
|
timelineWidth: state.width,
|
|
344
348
|
},
|
|
345
349
|
{
|
|
346
|
-
type: '
|
|
350
|
+
type: 'detectBaseScale',
|
|
347
351
|
}
|
|
348
352
|
);
|
|
349
353
|
case 'select': {
|
|
@@ -352,13 +356,18 @@ const reducer: Reducer<TraceViewerState, TraceViewerAction> = (
|
|
|
352
356
|
return state;
|
|
353
357
|
}
|
|
354
358
|
|
|
355
|
-
return
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
359
|
+
return reducer(
|
|
360
|
+
{
|
|
361
|
+
...state,
|
|
362
|
+
selected: node,
|
|
363
|
+
timelineWidth:
|
|
364
|
+
state.width -
|
|
365
|
+
(state.withPanel && !state.isMobile ? state.panelWidth : 0),
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
type: 'detectBaseScale',
|
|
369
|
+
}
|
|
370
|
+
);
|
|
362
371
|
}
|
|
363
372
|
case 'escape': {
|
|
364
373
|
if (state.selected) {
|
|
@@ -387,13 +396,18 @@ const reducer: Reducer<TraceViewerState, TraceViewerAction> = (
|
|
|
387
396
|
}
|
|
388
397
|
case 'detectBaseScale': {
|
|
389
398
|
const baseScale =
|
|
390
|
-
(state.
|
|
399
|
+
(state.timelineWidth - state.scrollbarWidth) / state.root.duration;
|
|
391
400
|
|
|
392
|
-
return
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
401
|
+
return reducer(
|
|
402
|
+
{
|
|
403
|
+
...state,
|
|
404
|
+
baseScale,
|
|
405
|
+
scale: baseScale * state.scaleRatio,
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
type: 'minScale',
|
|
409
|
+
}
|
|
410
|
+
);
|
|
397
411
|
}
|
|
398
412
|
case 'setScale':
|
|
399
413
|
return {
|
|
@@ -552,16 +566,43 @@ const reducer: Reducer<TraceViewerState, TraceViewerAction> = (
|
|
|
552
566
|
}
|
|
553
567
|
case 'setWithPanel': {
|
|
554
568
|
if (state.withPanel === action.withPanel) return state;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
569
|
+
const timelineWidth =
|
|
570
|
+
action.withPanel && state.selected && !state.isMobile
|
|
571
|
+
? state.width - state.panelWidth
|
|
572
|
+
: state.width;
|
|
573
|
+
return reducer(
|
|
574
|
+
{
|
|
575
|
+
...state,
|
|
576
|
+
withPanel: action.withPanel,
|
|
577
|
+
timelineWidth,
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
type: 'detectBaseScale',
|
|
581
|
+
}
|
|
582
|
+
);
|
|
559
583
|
}
|
|
560
584
|
case 'forceRender':
|
|
561
585
|
state.memoCacheRef.current.set('', {});
|
|
562
586
|
return {
|
|
563
587
|
...state,
|
|
564
588
|
};
|
|
589
|
+
case 'updateRoot':
|
|
590
|
+
// Incremental update: preserve scroll snapshot and only invalidate
|
|
591
|
+
// memo cache for spans whose data may have changed
|
|
592
|
+
state.memoCacheRef.current.set('', {});
|
|
593
|
+
return reducer(
|
|
594
|
+
{
|
|
595
|
+
...state,
|
|
596
|
+
root: action.root,
|
|
597
|
+
spanMap: action.spanMap,
|
|
598
|
+
resourceMap: Object.fromEntries(
|
|
599
|
+
action.resources.map(({ name, attributes }) => [name, attributes])
|
|
600
|
+
),
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
type: 'detectBaseScale',
|
|
604
|
+
}
|
|
605
|
+
);
|
|
565
606
|
}
|
|
566
607
|
};
|
|
567
608
|
|
|
@@ -594,7 +635,6 @@ export function TraceViewerContextProvider({
|
|
|
594
635
|
scrollSnapshotRef,
|
|
595
636
|
customSpanClassNameFunc,
|
|
596
637
|
customSpanEventClassNameFunc,
|
|
597
|
-
customPanelComponent,
|
|
598
638
|
memoCacheRef,
|
|
599
639
|
withPanel,
|
|
600
640
|
getQuickLinks: (span) => {
|
|
@@ -624,11 +664,23 @@ export function TraceViewerContextProvider({
|
|
|
624
664
|
);
|
|
625
665
|
|
|
626
666
|
return (
|
|
627
|
-
<
|
|
628
|
-
{
|
|
629
|
-
|
|
667
|
+
<CustomPanelContext.Provider value={customPanelComponent}>
|
|
668
|
+
<TraceViewerContext.Provider value={value}>
|
|
669
|
+
{children}
|
|
670
|
+
</TraceViewerContext.Provider>
|
|
671
|
+
</CustomPanelContext.Provider>
|
|
630
672
|
);
|
|
631
673
|
}
|
|
632
674
|
|
|
633
675
|
export const useTraceViewer = (): TraceViewerContextProps =>
|
|
634
676
|
useContext(TraceViewerContext);
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Separate context for the custom panel component. This is intentionally
|
|
680
|
+
* outside the useReducer state so that the panel re-renders reactively
|
|
681
|
+
* when props like spanDetailData change.
|
|
682
|
+
*/
|
|
683
|
+
const CustomPanelContext = createContext<ReactNode | null>(null);
|
|
684
|
+
|
|
685
|
+
export const useCustomPanelComponent = (): ReactNode | null =>
|
|
686
|
+
useContext(CustomPanelContext);
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
top: -7px;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
&:hover:not(
|
|
66
|
+
&:hover:not([data-selected]) {
|
|
67
67
|
filter: brightness(0.95);
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
border-radius: 4px;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
&[data-selected] {
|
|
107
107
|
border-width: 2px;
|
|
108
108
|
outline: 2px solid var(--span-border);
|
|
109
109
|
outline-offset: -1px;
|
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
filter: brightness(0.95);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
+
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
.spanNodeEvent {
|
|
@@ -271,11 +272,11 @@
|
|
|
271
272
|
}
|
|
272
273
|
}
|
|
273
274
|
|
|
274
|
-
& .spanNode:hover:not(
|
|
275
|
+
& .spanNode:hover:not([data-selected]) {
|
|
275
276
|
filter: brightness(1.05);
|
|
276
277
|
}
|
|
277
278
|
|
|
278
|
-
& .spanNode
|
|
279
|
+
& .spanNode[data-selected]:hover {
|
|
279
280
|
filter: brightness(1.05);
|
|
280
281
|
}
|
|
281
282
|
}
|
|
@@ -295,11 +296,11 @@
|
|
|
295
296
|
}
|
|
296
297
|
}
|
|
297
298
|
|
|
298
|
-
& .spanNode:hover:not(
|
|
299
|
+
& .spanNode:hover:not([data-selected]) {
|
|
299
300
|
filter: brightness(1.05);
|
|
300
301
|
}
|
|
301
302
|
|
|
302
|
-
& .spanNode
|
|
303
|
+
& .spanNode[data-selected]:hover {
|
|
303
304
|
filter: brightness(1.05);
|
|
304
305
|
}
|
|
305
306
|
}
|
|
@@ -311,6 +312,8 @@
|
|
|
311
312
|
text-align: left;
|
|
312
313
|
overflow: hidden;
|
|
313
314
|
text-overflow: ellipsis;
|
|
315
|
+
position: relative;
|
|
316
|
+
z-index: 1;
|
|
314
317
|
}
|
|
315
318
|
|
|
316
319
|
.spanDuration {
|
|
@@ -318,6 +321,8 @@
|
|
|
318
321
|
padding-right: 6px;
|
|
319
322
|
font-variant-numeric: tabular-nums;
|
|
320
323
|
color: var(--span-secondary, var(--ds-gray-alpha-700));
|
|
324
|
+
position: relative;
|
|
325
|
+
z-index: 1;
|
|
321
326
|
}
|
|
322
327
|
|
|
323
328
|
/* SPAN DETAIL PANEL */
|
|
@@ -796,6 +801,16 @@
|
|
|
796
801
|
bottom: 6px;
|
|
797
802
|
color: var(--ds-gray-900);
|
|
798
803
|
user-select: none;
|
|
804
|
+
white-space: nowrap;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.markerClockTime {
|
|
808
|
+
display: block;
|
|
809
|
+
font-size: 9px;
|
|
810
|
+
color: var(--ds-gray-alpha-500);
|
|
811
|
+
margin-top: 1px;
|
|
812
|
+
letter-spacing: 0.02em;
|
|
813
|
+
white-space: nowrap;
|
|
799
814
|
}
|
|
800
815
|
|
|
801
816
|
.eventMarkersContainer {
|
|
@@ -1253,6 +1268,164 @@
|
|
|
1253
1268
|
--span-secondary: var(--ds-gray-900);
|
|
1254
1269
|
}
|
|
1255
1270
|
|
|
1271
|
+
/* ──────────────────────────────────────────────────────────────────────── */
|
|
1272
|
+
/* Span segments — colored sections overlaid inside span bars */
|
|
1273
|
+
/* ──────────────────────────────────────────────────────────────────────── */
|
|
1274
|
+
|
|
1275
|
+
.segmentLayer {
|
|
1276
|
+
position: absolute;
|
|
1277
|
+
inset: 0;
|
|
1278
|
+
z-index: 0;
|
|
1279
|
+
border-radius: inherit;
|
|
1280
|
+
pointer-events: none;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
.segment {
|
|
1284
|
+
position: absolute;
|
|
1285
|
+
top: 0;
|
|
1286
|
+
bottom: 0;
|
|
1287
|
+
pointer-events: auto;
|
|
1288
|
+
overflow: hidden;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
.segmentLabel {
|
|
1292
|
+
display: flex;
|
|
1293
|
+
align-items: center;
|
|
1294
|
+
height: 100%;
|
|
1295
|
+
padding: 0 4px;
|
|
1296
|
+
font-size: 9px;
|
|
1297
|
+
font-variant-numeric: tabular-nums;
|
|
1298
|
+
white-space: nowrap;
|
|
1299
|
+
color: var(--ds-gray-900);
|
|
1300
|
+
opacity: 0.7;
|
|
1301
|
+
pointer-events: none;
|
|
1302
|
+
user-select: none;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
/* Inline segment tags rendered next to the span name */
|
|
1306
|
+
.segmentTag {
|
|
1307
|
+
font-size: 10px;
|
|
1308
|
+
font-variant-numeric: tabular-nums;
|
|
1309
|
+
opacity: 0.6;
|
|
1310
|
+
white-space: nowrap;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/* Queued: hatched — waiting to start */
|
|
1314
|
+
.segQueued {
|
|
1315
|
+
background: repeating-linear-gradient(
|
|
1316
|
+
45deg,
|
|
1317
|
+
var(--ds-gray-alpha-200),
|
|
1318
|
+
var(--ds-gray-alpha-200) 6px,
|
|
1319
|
+
var(--ds-gray-alpha-100) 6px,
|
|
1320
|
+
var(--ds-gray-alpha-100) 12px
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/* Running: soft blue — actively executing */
|
|
1325
|
+
.segRunning {
|
|
1326
|
+
background-color: var(--ds-blue-200);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/* Failed: soft red — attempt ended in failure */
|
|
1330
|
+
.segFailed {
|
|
1331
|
+
background-color: var(--ds-red-200);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
/* Retrying: hatched — waiting between retry attempts */
|
|
1335
|
+
.segRetrying {
|
|
1336
|
+
background: repeating-linear-gradient(
|
|
1337
|
+
45deg,
|
|
1338
|
+
var(--ds-gray-alpha-200),
|
|
1339
|
+
var(--ds-gray-alpha-200) 6px,
|
|
1340
|
+
var(--ds-gray-alpha-100) 6px,
|
|
1341
|
+
var(--ds-gray-alpha-100) 12px
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/* Succeeded: soft green — final successful attempt */
|
|
1346
|
+
.segSucceeded {
|
|
1347
|
+
background-color: var(--ds-green-200);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/* Waiting: hatched — hook waiting for payload */
|
|
1351
|
+
.segWaiting {
|
|
1352
|
+
background: repeating-linear-gradient(
|
|
1353
|
+
45deg,
|
|
1354
|
+
var(--ds-gray-alpha-200),
|
|
1355
|
+
var(--ds-gray-alpha-200) 6px,
|
|
1356
|
+
var(--ds-gray-alpha-100) 6px,
|
|
1357
|
+
var(--ds-gray-alpha-100) 12px
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/* Sleeping: soft amber — sleep in progress */
|
|
1362
|
+
.segSleeping {
|
|
1363
|
+
background-color: var(--ds-amber-200);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
/* Received: soft blue — hook received payload */
|
|
1367
|
+
.segReceived {
|
|
1368
|
+
background-color: var(--ds-blue-200);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
/* Boundary markers — workflow events shown as thin vertical lines instead of diamonds */
|
|
1372
|
+
.boundaryMarker {
|
|
1373
|
+
& .boundaryLine {
|
|
1374
|
+
position: absolute;
|
|
1375
|
+
left: -0.5px;
|
|
1376
|
+
top: 0;
|
|
1377
|
+
bottom: 0;
|
|
1378
|
+
width: 1px;
|
|
1379
|
+
background-color: transparent;
|
|
1380
|
+
z-index: 0;
|
|
1381
|
+
transition: background-color 0.15s ease;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
&[data-hovered="true"] .boundaryLine {
|
|
1385
|
+
background-color: var(--ds-gray-alpha-400);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
/* Tooltip must sit above the boundary line */
|
|
1389
|
+
& .hoverInfo {
|
|
1390
|
+
z-index: 1;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
/* When segments are active, make span background transparent so segments show */
|
|
1395
|
+
.spanNode.hasSegments {
|
|
1396
|
+
background-color: transparent;
|
|
1397
|
+
|
|
1398
|
+
&.small:not(.xHover) {
|
|
1399
|
+
background-color: transparent;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/* Suppress hover expansion gradient — segments handle the background */
|
|
1403
|
+
&.xHover {
|
|
1404
|
+
background: none;
|
|
1405
|
+
min-width: var(--span-width);
|
|
1406
|
+
max-width: var(--span-width);
|
|
1407
|
+
|
|
1408
|
+
&[data-right-side="true"] {
|
|
1409
|
+
background: none;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
/* Remove duration label background bleed — segments provide the visual */
|
|
1414
|
+
& .spanDuration {
|
|
1415
|
+
background-color: transparent;
|
|
1416
|
+
box-shadow: none;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/* Let segment tooltips receive pointer events */
|
|
1420
|
+
&::after {
|
|
1421
|
+
pointer-events: none;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
& .segmentLayer {
|
|
1425
|
+
pointer-events: auto;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1256
1429
|
/* Event markers */
|
|
1257
1430
|
|
|
1258
1431
|
/* Failure events - Red */
|