@workflow/web-shared 5.0.0-beta.3 → 5.0.0-beta.4
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/new-trace-viewer/components/event-list.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/event-list.js +7 -4
- package/dist/components/new-trace-viewer/components/event-list.js.map +1 -1
- package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts +18 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +54 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts +20 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +25 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +101 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts +20 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js +114 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js.map +1 -0
- package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/split-pane.js +2 -3
- package/dist/components/new-trace-viewer/components/split-pane.js.map +1 -1
- package/dist/components/new-trace-viewer/components/timeline.d.ts +10 -11
- package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/timeline.js +57 -53
- package/dist/components/new-trace-viewer/components/timeline.js.map +1 -1
- package/dist/components/new-trace-viewer/context.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/context.js +5 -7
- package/dist/components/new-trace-viewer/context.js.map +1 -1
- package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/detail-panel.js +2 -1
- package/dist/components/new-trace-viewer/detail-panel.js.map +1 -1
- package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/trace-viewer.js +37 -17
- package/dist/components/new-trace-viewer/trace-viewer.js.map +1 -1
- package/dist/components/new-trace-viewer/utils.d.ts +3 -8
- package/dist/components/new-trace-viewer/utils.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/utils.js +12 -65
- package/dist/components/new-trace-viewer/utils.js.map +1 -1
- package/dist/components/trace-viewer/util/timing.d.ts +1 -1
- package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
- package/dist/components/trace-viewer/util/timing.js +5 -8
- package/dist/components/trace-viewer/util/timing.js.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.d.ts +2 -2
- package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.js +16 -3
- package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
- package/dist/lib/trace-builder.d.ts.map +1 -1
- package/dist/lib/trace-builder.js +2 -1
- package/dist/lib/trace-builder.js.map +1 -1
- package/dist/lib/utils.d.ts +9 -7
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +27 -27
- package/dist/lib/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/components/new-trace-viewer/components/event-list.tsx +27 -20
- package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
- package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
- package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
- package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
- package/src/components/new-trace-viewer/components/split-pane.tsx +2 -3
- package/src/components/new-trace-viewer/components/timeline.tsx +119 -115
- package/src/components/new-trace-viewer/context.tsx +7 -9
- package/src/components/new-trace-viewer/detail-panel.tsx +2 -1
- package/src/components/new-trace-viewer/trace-viewer.tsx +120 -60
- package/src/components/new-trace-viewer/utils.ts +19 -93
- package/src/components/trace-viewer/util/timing.ts +5 -9
- package/src/components/workflow-traces/trace-span-construction.ts +22 -4
- package/src/lib/trace-builder.ts +2 -1
- package/src/lib/utils.ts +29 -29
|
@@ -1,47 +1,45 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { ArrowLeft, ArrowRight } from 'lucide-react';
|
|
3
4
|
import type { ReactNode } from 'react';
|
|
4
5
|
import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
6
|
import { cn } from '../../../lib/utils';
|
|
6
7
|
import type { Span } from '../../trace-viewer/types';
|
|
7
8
|
import { formatDuration, getHighResInMs } from '../../trace-viewer/util/timing';
|
|
8
|
-
import type { SegmentStatus,
|
|
9
|
+
import type { SegmentStatus, TimeMarker } from '../utils';
|
|
9
10
|
import {
|
|
10
|
-
computeCompressedTimeMarkers,
|
|
11
11
|
computeSpanGaps,
|
|
12
12
|
computeSpanSegments,
|
|
13
|
-
|
|
13
|
+
getSpanDurationMs,
|
|
14
14
|
getResourceColor,
|
|
15
15
|
} from '../utils';
|
|
16
16
|
|
|
17
|
-
const QUEUED_BACKGROUND =
|
|
18
|
-
'radial-gradient(circle, var(--ds-gray-400) 2px, transparent 2px) center / 20px 20px space no-repeat, var(--ds-gray-500)';
|
|
19
|
-
|
|
20
|
-
const HATCHED_BACKGROUND =
|
|
21
|
-
'repeating-linear-gradient(-45deg, var(--ds-gray-400) 0px, var(--ds-gray-400) 3px, var(--ds-gray-500) 3px, var(--ds-gray-500) 6px)';
|
|
22
|
-
|
|
23
17
|
const SEGMENT_CONFIG: Record<
|
|
24
18
|
SegmentStatus,
|
|
25
19
|
{ className?: string; style?: React.CSSProperties }
|
|
26
20
|
> = {
|
|
27
|
-
queued: {
|
|
21
|
+
queued: { className: 'bg-gray-500' },
|
|
28
22
|
retrying: {
|
|
29
23
|
className: 'box-border bg-gray-500',
|
|
30
24
|
},
|
|
31
|
-
waiting: {
|
|
25
|
+
waiting: { className: 'bg-gray-500' },
|
|
32
26
|
running: { className: 'bg-blue-700' },
|
|
33
27
|
failed: { className: 'bg-red-700' },
|
|
34
28
|
succeeded: { className: 'bg-green-700' },
|
|
35
|
-
sleeping: { className: 'bg-
|
|
29
|
+
sleeping: { className: 'bg-gray-500' },
|
|
36
30
|
received: { className: 'bg-blue-700' },
|
|
37
31
|
};
|
|
38
32
|
|
|
39
|
-
const
|
|
33
|
+
const BAR_HEIGHT_PX = 24;
|
|
34
|
+
const TINY_BAR_BOX_SIZE_PX = 24;
|
|
35
|
+
const TINY_BAR_WIDTH_PX = 4;
|
|
40
36
|
const SEGMENT_GAP_PX = 1;
|
|
41
37
|
// Keep this in sync with the rendered row height in the timeline/event list.
|
|
42
|
-
const ROW_HEIGHT =
|
|
38
|
+
const ROW_HEIGHT = 40;
|
|
43
39
|
const CONTAINER_PAD_Y = 8;
|
|
44
40
|
const END_CAP_HEIGHT = 8;
|
|
41
|
+
export const TIMELINE_PADDING_PX = 16;
|
|
42
|
+
const ORIGIN_MARKER_EPSILON = 0.000001;
|
|
45
43
|
|
|
46
44
|
const DeltaIndicator = memo(function DeltaIndicator({
|
|
47
45
|
leftFrac,
|
|
@@ -78,34 +76,42 @@ const DeltaIndicator = memo(function DeltaIndicator({
|
|
|
78
76
|
|
|
79
77
|
const TimelineBar = memo(function TimelineBar({
|
|
80
78
|
span,
|
|
81
|
-
|
|
79
|
+
viewStart,
|
|
80
|
+
viewDuration,
|
|
82
81
|
containerWidth,
|
|
83
82
|
isSelected,
|
|
84
83
|
onClick,
|
|
85
84
|
}: {
|
|
86
85
|
span: Span;
|
|
87
|
-
|
|
86
|
+
viewStart: number;
|
|
87
|
+
viewDuration: number;
|
|
88
88
|
containerWidth: number;
|
|
89
89
|
isSelected: boolean;
|
|
90
90
|
onClick: () => void;
|
|
91
91
|
}): ReactNode {
|
|
92
92
|
const startTime = getHighResInMs(span.startTime);
|
|
93
93
|
const endTime = getHighResInMs(span.endTime);
|
|
94
|
-
const totalDurationMs =
|
|
94
|
+
const totalDurationMs = getSpanDurationMs(span);
|
|
95
95
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
const
|
|
96
|
+
const leftFracRaw =
|
|
97
|
+
viewDuration > 0 ? (startTime - viewStart) / viewDuration : 0;
|
|
98
|
+
const rightFracRaw =
|
|
99
|
+
viewDuration > 0 ? (endTime - viewStart) / viewDuration : 0;
|
|
100
|
+
const widthFrac = rightFracRaw - leftFracRaw;
|
|
99
101
|
|
|
100
|
-
const leftPct =
|
|
102
|
+
const leftPct = leftFracRaw * 100;
|
|
101
103
|
const widthPct = widthFrac * 100;
|
|
102
104
|
|
|
103
105
|
const pixelWidth = widthFrac * containerWidth;
|
|
104
|
-
const
|
|
106
|
+
const visibleLeftFrac = Math.max(0, Math.min(1, leftFracRaw));
|
|
107
|
+
const visibleRightFrac = Math.max(0, Math.min(1, rightFracRaw));
|
|
108
|
+
const visiblePixelWidth =
|
|
109
|
+
Math.max(0, visibleRightFrac - visibleLeftFrac) * containerWidth;
|
|
110
|
+
const isTinyBar =
|
|
111
|
+
containerWidth > 0 && visiblePixelWidth < TINY_BAR_BOX_SIZE_PX;
|
|
105
112
|
const [isRowHovered, setIsRowHovered] = useState(false);
|
|
106
113
|
|
|
107
114
|
const segments = useMemo(() => computeSpanSegments(span), [span]);
|
|
108
|
-
const finalSegment = segments[segments.length - 1];
|
|
109
115
|
|
|
110
116
|
const workflowStatus = (span.attributes.data as Record<string, unknown>)
|
|
111
117
|
?.status as string | undefined;
|
|
@@ -114,24 +120,6 @@ const TimelineBar = memo(function TimelineBar({
|
|
|
114
120
|
const fallbackColor = isErrored
|
|
115
121
|
? (colors.errorBar ?? 'var(--ds-red-700)')
|
|
116
122
|
: colors.bar;
|
|
117
|
-
const compressedSegmentStatus = isErrored
|
|
118
|
-
? 'failed'
|
|
119
|
-
: span.resource === 'hook'
|
|
120
|
-
? 'received'
|
|
121
|
-
: finalSegment?.status;
|
|
122
|
-
const compressedSegmentStyle =
|
|
123
|
-
compressedSegmentStatus === 'queued'
|
|
124
|
-
? { background: 'var(--ds-gray-500)' }
|
|
125
|
-
: compressedSegmentStatus
|
|
126
|
-
? SEGMENT_CONFIG[compressedSegmentStatus].style
|
|
127
|
-
: undefined;
|
|
128
|
-
const compressedSegmentClassName = compressedSegmentStatus
|
|
129
|
-
? SEGMENT_CONFIG[compressedSegmentStatus].className
|
|
130
|
-
: undefined;
|
|
131
|
-
|
|
132
|
-
const hasCompressedStatus = Boolean(
|
|
133
|
-
compressedSegmentClassName || compressedSegmentStyle
|
|
134
|
-
);
|
|
135
123
|
const renderDurationLabel = (label: string) => (
|
|
136
124
|
<span
|
|
137
125
|
className="pointer-events-none absolute inset-0 flex items-center justify-start overflow-hidden px-1 text-[10px] font-mono font-medium leading-none whitespace-nowrap text-left text-white tabular-nums"
|
|
@@ -147,22 +135,19 @@ const TimelineBar = memo(function TimelineBar({
|
|
|
147
135
|
isRowHovered &&
|
|
148
136
|
pixelWidth >= getMinDurationLabelWidthPx(totalDurationLabel);
|
|
149
137
|
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)}
|
|
156
|
-
style={{
|
|
157
|
-
width: '100%',
|
|
158
|
-
background: hasCompressedStatus ? undefined : fallbackColor,
|
|
159
|
-
...compressedSegmentStyle,
|
|
160
|
-
}}
|
|
161
|
-
>
|
|
162
|
-
{showBarDurationLabel ? renderDurationLabel(totalDurationLabel) : null}
|
|
138
|
+
const showBoundaryArrow = isTinyBar && (leftFracRaw < 0 || rightFracRaw > 1);
|
|
139
|
+
const BoundaryArrow = leftFracRaw < 0.5 ? ArrowLeft : ArrowRight;
|
|
140
|
+
const barContent = showBoundaryArrow ? (
|
|
141
|
+
<div className="flex h-6 w-6 items-center justify-center rounded-[0.25rem]">
|
|
142
|
+
<BoundaryArrow className="size-3 text-gray-900" />
|
|
163
143
|
</div>
|
|
144
|
+
) : isTinyBar ? (
|
|
145
|
+
<div
|
|
146
|
+
className="h-6 rounded-[0.25rem]"
|
|
147
|
+
style={{ background: fallbackColor }}
|
|
148
|
+
/>
|
|
164
149
|
) : segments.length > 0 ? (
|
|
165
|
-
<div className="relative
|
|
150
|
+
<div className="relative h-6 w-full">
|
|
166
151
|
{segments.map((seg, i) => {
|
|
167
152
|
const segPixelWidth =
|
|
168
153
|
(seg.endFraction - seg.startFraction) * pixelWidth;
|
|
@@ -200,7 +185,7 @@ const TimelineBar = memo(function TimelineBar({
|
|
|
200
185
|
</div>
|
|
201
186
|
) : (
|
|
202
187
|
<div
|
|
203
|
-
className="relative h-
|
|
188
|
+
className="relative h-6 rounded-[0.25rem]"
|
|
204
189
|
style={{
|
|
205
190
|
width: '100%',
|
|
206
191
|
minWidth: 4,
|
|
@@ -218,24 +203,37 @@ const TimelineBar = memo(function TimelineBar({
|
|
|
218
203
|
aria-expanded={isSelected}
|
|
219
204
|
aria-level={1}
|
|
220
205
|
className={cn(
|
|
221
|
-
'h-
|
|
206
|
+
'h-10 relative flex items-center hover:bg-gray-100 aria-selected:bg-gray-100 aria-selected:hover:bg-gray-200'
|
|
222
207
|
)}
|
|
223
208
|
onMouseEnter={() => setIsRowHovered(true)}
|
|
224
209
|
onMouseLeave={() => setIsRowHovered(false)}
|
|
225
210
|
onClick={onClick}
|
|
226
211
|
>
|
|
227
212
|
<div
|
|
228
|
-
className="absolute
|
|
213
|
+
className="absolute inset-y-0"
|
|
229
214
|
style={{
|
|
230
|
-
left:
|
|
231
|
-
|
|
232
|
-
: `${leftPct}%`,
|
|
233
|
-
width: isCompressed
|
|
234
|
-
? `${FIXED_BAR_WIDTH_PX}px`
|
|
235
|
-
: `max(${widthPct}%, 4px)`,
|
|
215
|
+
left: TIMELINE_PADDING_PX,
|
|
216
|
+
right: TIMELINE_PADDING_PX,
|
|
236
217
|
}}
|
|
237
218
|
>
|
|
238
|
-
|
|
219
|
+
<div
|
|
220
|
+
className="absolute top-1/2 -translate-y-1/2"
|
|
221
|
+
style={{
|
|
222
|
+
left: showBoundaryArrow
|
|
223
|
+
? `min(max(${leftPct}%, 0px), calc(100% - ${TINY_BAR_BOX_SIZE_PX}px))`
|
|
224
|
+
: isTinyBar
|
|
225
|
+
? `min(${leftPct}%, calc(100% - ${TINY_BAR_WIDTH_PX}px))`
|
|
226
|
+
: `${leftPct}%`,
|
|
227
|
+
width: showBoundaryArrow
|
|
228
|
+
? `${TINY_BAR_BOX_SIZE_PX}px`
|
|
229
|
+
: isTinyBar
|
|
230
|
+
? `${TINY_BAR_WIDTH_PX}px`
|
|
231
|
+
: `max(${widthPct}%, 4px)`,
|
|
232
|
+
height: BAR_HEIGHT_PX,
|
|
233
|
+
}}
|
|
234
|
+
>
|
|
235
|
+
{barContent}
|
|
236
|
+
</div>
|
|
239
237
|
</div>
|
|
240
238
|
</div>
|
|
241
239
|
);
|
|
@@ -244,43 +242,18 @@ const TimelineBar = memo(function TimelineBar({
|
|
|
244
242
|
export { TimelineBar };
|
|
245
243
|
|
|
246
244
|
export function TimelineHeader({
|
|
247
|
-
|
|
248
|
-
viewDuration,
|
|
249
|
-
rootStart,
|
|
250
|
-
compression,
|
|
251
|
-
isZoomed,
|
|
252
|
-
onResetZoom,
|
|
245
|
+
markers,
|
|
253
246
|
hoverInfo,
|
|
254
247
|
}: {
|
|
255
|
-
|
|
256
|
-
viewDuration: number;
|
|
257
|
-
rootStart: number;
|
|
258
|
-
compression: TimeCompression;
|
|
259
|
-
isZoomed: boolean;
|
|
260
|
-
onResetZoom: () => void;
|
|
248
|
+
markers: TimeMarker[];
|
|
261
249
|
hoverInfo?: { fraction: number; label: string } | null;
|
|
262
250
|
}): ReactNode {
|
|
263
|
-
const viewEnd = viewStart + viewDuration;
|
|
264
|
-
|
|
265
|
-
const markers = useMemo(
|
|
266
|
-
() =>
|
|
267
|
-
compression.isCompressed
|
|
268
|
-
? computeCompressedTimeMarkers(
|
|
269
|
-
compression,
|
|
270
|
-
viewStart,
|
|
271
|
-
viewEnd,
|
|
272
|
-
rootStart
|
|
273
|
-
)
|
|
274
|
-
: computeTimeMarkers(viewDuration, viewStart - rootStart),
|
|
275
|
-
[compression, viewStart, viewEnd, viewDuration, rootStart]
|
|
276
|
-
);
|
|
277
|
-
|
|
278
251
|
return (
|
|
279
|
-
<div className="relative bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-end px-
|
|
252
|
+
<div className="relative bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-end px-4 pb-1">
|
|
280
253
|
<div className="relative h-full flex-1">
|
|
281
|
-
{markers.map((m
|
|
254
|
+
{markers.map((m) => (
|
|
282
255
|
<span
|
|
283
|
-
key={
|
|
256
|
+
key={`${m.position}-${m.label}`}
|
|
284
257
|
className="absolute bottom-1 font-mono text-xs font-normal leading-4 text-gray-900 whitespace-nowrap"
|
|
285
258
|
style={{ left: `${m.position * 100}%` }}
|
|
286
259
|
>
|
|
@@ -296,29 +269,24 @@ export function TimelineHeader({
|
|
|
296
269
|
</span>
|
|
297
270
|
)}
|
|
298
271
|
</div>
|
|
299
|
-
{isZoomed && (
|
|
300
|
-
<button
|
|
301
|
-
type="button"
|
|
302
|
-
className="absolute right-2 top-1/2 -translate-y-1/2 z-[5] flex items-center py-0.5 px-2 border border-gray-alpha-400 rounded-md bg-background-100 font-sans text-[11px] font-medium text-gray-900 cursor-pointer whitespace-nowrap transition-[color,border-color] duration-[120ms] ease-in-out hover:text-gray-1000 hover:border-gray-600"
|
|
303
|
-
onClick={onResetZoom}
|
|
304
|
-
>
|
|
305
|
-
Reset zoom
|
|
306
|
-
</button>
|
|
307
|
-
)}
|
|
308
272
|
</div>
|
|
309
273
|
);
|
|
310
274
|
}
|
|
311
275
|
|
|
312
276
|
export function Timeline({
|
|
313
277
|
spans,
|
|
314
|
-
|
|
278
|
+
viewStart,
|
|
279
|
+
viewEnd,
|
|
280
|
+
markers,
|
|
315
281
|
selectedId,
|
|
316
282
|
onSelect,
|
|
317
283
|
hoverFraction,
|
|
318
284
|
altHeld = false,
|
|
319
285
|
}: {
|
|
320
286
|
spans: Span[];
|
|
321
|
-
|
|
287
|
+
viewStart: number;
|
|
288
|
+
viewEnd: number;
|
|
289
|
+
markers: TimeMarker[];
|
|
322
290
|
selectedId: string | null;
|
|
323
291
|
onSelect: (spanId: string) => void;
|
|
324
292
|
hoverFraction?: number | null;
|
|
@@ -326,6 +294,8 @@ export function Timeline({
|
|
|
326
294
|
}): ReactNode {
|
|
327
295
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
328
296
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
297
|
+
const viewDuration = viewEnd - viewStart;
|
|
298
|
+
const timelineWidth = Math.max(0, containerWidth - TIMELINE_PADDING_PX * 2);
|
|
329
299
|
|
|
330
300
|
useEffect(() => {
|
|
331
301
|
const el = containerRef.current;
|
|
@@ -338,30 +308,64 @@ export function Timeline({
|
|
|
338
308
|
}, []);
|
|
339
309
|
|
|
340
310
|
const gaps = useMemo(
|
|
341
|
-
() => computeSpanGaps(spans,
|
|
342
|
-
[spans,
|
|
311
|
+
() => computeSpanGaps(spans, viewStart, viewEnd),
|
|
312
|
+
[spans, viewStart, viewEnd]
|
|
343
313
|
);
|
|
344
314
|
|
|
345
315
|
return (
|
|
346
|
-
<div ref={containerRef} className="relative
|
|
316
|
+
<div ref={containerRef} className="relative h-full overflow-hidden">
|
|
317
|
+
<div
|
|
318
|
+
aria-hidden
|
|
319
|
+
className="absolute inset-y-0 pointer-events-none"
|
|
320
|
+
style={{
|
|
321
|
+
left: TIMELINE_PADDING_PX,
|
|
322
|
+
right: TIMELINE_PADDING_PX,
|
|
323
|
+
}}
|
|
324
|
+
>
|
|
325
|
+
{markers.map((marker) =>
|
|
326
|
+
Math.abs(marker.value) > ORIGIN_MARKER_EPSILON ? (
|
|
327
|
+
<div
|
|
328
|
+
key={`${marker.position}-${marker.label}`}
|
|
329
|
+
className="absolute top-0 bottom-0 w-px bg-gray-alpha-300"
|
|
330
|
+
style={{ left: `${marker.position * 100}%` }}
|
|
331
|
+
/>
|
|
332
|
+
) : null
|
|
333
|
+
)}
|
|
334
|
+
</div>
|
|
347
335
|
{hoverFraction != null && (
|
|
348
336
|
<div
|
|
349
|
-
className="absolute
|
|
350
|
-
style={{
|
|
351
|
-
|
|
337
|
+
className="absolute inset-y-0 pointer-events-none z-10"
|
|
338
|
+
style={{
|
|
339
|
+
left: TIMELINE_PADDING_PX,
|
|
340
|
+
right: TIMELINE_PADDING_PX,
|
|
341
|
+
}}
|
|
342
|
+
>
|
|
343
|
+
<div
|
|
344
|
+
className="absolute top-0 bottom-0 w-px bg-gray-alpha-400"
|
|
345
|
+
style={{ left: `${hoverFraction * 100}%` }}
|
|
346
|
+
/>
|
|
347
|
+
</div>
|
|
352
348
|
)}
|
|
353
349
|
{spans.map((span) => (
|
|
354
350
|
<TimelineBar
|
|
355
351
|
key={span.spanId}
|
|
356
352
|
span={span}
|
|
357
|
-
|
|
358
|
-
|
|
353
|
+
viewStart={viewStart}
|
|
354
|
+
viewDuration={viewDuration}
|
|
355
|
+
containerWidth={timelineWidth}
|
|
359
356
|
isSelected={selectedId === span.spanId}
|
|
360
357
|
onClick={() => onSelect(span.spanId)}
|
|
361
358
|
/>
|
|
362
359
|
))}
|
|
363
360
|
{altHeld && (
|
|
364
|
-
<div
|
|
361
|
+
<div
|
|
362
|
+
aria-hidden
|
|
363
|
+
className="absolute inset-y-0 pointer-events-none"
|
|
364
|
+
style={{
|
|
365
|
+
left: TIMELINE_PADDING_PX,
|
|
366
|
+
right: TIMELINE_PADDING_PX,
|
|
367
|
+
}}
|
|
368
|
+
>
|
|
365
369
|
{gaps.map((gap) => (
|
|
366
370
|
<DeltaIndicator
|
|
367
371
|
key={gap.rowIndex}
|
|
@@ -34,20 +34,18 @@ export function ActiveSpanProvider({
|
|
|
34
34
|
|
|
35
35
|
useEffect(() => {
|
|
36
36
|
setActiveSpanId((currentSpanId) => {
|
|
37
|
-
if (
|
|
37
|
+
if (!currentSpanId) {
|
|
38
38
|
return null;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return currentSpanId;
|
|
47
|
-
}
|
|
41
|
+
const hasCurrentSpan = spans.some(
|
|
42
|
+
(span) => span.spanId === currentSpanId
|
|
43
|
+
);
|
|
44
|
+
if (hasCurrentSpan) {
|
|
45
|
+
return currentSpanId;
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
return
|
|
48
|
+
return null;
|
|
51
49
|
});
|
|
52
50
|
}, [spans]);
|
|
53
51
|
|
|
@@ -4,6 +4,7 @@ import { X } from 'lucide-react';
|
|
|
4
4
|
import type { ReactNode } from 'react';
|
|
5
5
|
import type { Span } from '../trace-viewer/types';
|
|
6
6
|
import { formatDuration, getHighResInMs } from '../trace-viewer/util/timing';
|
|
7
|
+
import { getSpanDurationMs } from './utils';
|
|
7
8
|
|
|
8
9
|
interface DetailPanelProps {
|
|
9
10
|
span: Span;
|
|
@@ -17,7 +18,7 @@ export function DetailPanel({
|
|
|
17
18
|
onClose,
|
|
18
19
|
}: DetailPanelProps): ReactNode {
|
|
19
20
|
const startMs = getHighResInMs(span.startTime);
|
|
20
|
-
const durationMs =
|
|
21
|
+
const durationMs = getSpanDurationMs(span);
|
|
21
22
|
const offsetMs = startMs - rootStart;
|
|
22
23
|
|
|
23
24
|
return (
|