@workflow/web-shared 5.0.0-beta.35 → 5.0.0-beta.37
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 +10 -7
- package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/split-pane.js +5 -5
- package/dist/components/new-trace-viewer/components/timeline.d.ts +8 -2
- package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/timeline.js +95 -26
- package/dist/components/new-trace-viewer/components/trace-shortcut-helper.js +2 -2
- package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/trace-viewer.js +24 -22
- package/dist/components/new-trace-viewer/utils.d.ts +29 -0
- package/dist/components/new-trace-viewer/utils.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/utils.js +46 -3
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
- package/dist/components/sidebar/attribute-panel.js +7 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/hydration.d.ts.map +1 -1
- package/dist/lib/hydration.js +13 -5
- package/package.json +5 -5
- package/src/components/new-trace-viewer/components/event-list.tsx +21 -11
- package/src/components/new-trace-viewer/components/split-pane.tsx +14 -4
- package/src/components/new-trace-viewer/components/timeline.tsx +158 -47
- package/src/components/new-trace-viewer/components/trace-shortcut-helper.tsx +1 -1
- package/src/components/new-trace-viewer/trace-viewer.tsx +54 -50
- package/src/components/new-trace-viewer/utils.test.ts +199 -0
- package/src/components/new-trace-viewer/utils.ts +79 -2
- package/src/components/sidebar/attribute-panel.tsx +6 -0
- package/src/index.ts +1 -4
- package/src/lib/hydration.ts +12 -6
|
@@ -22,10 +22,12 @@ import type {
|
|
|
22
22
|
OffscreenMarkers,
|
|
23
23
|
Segment,
|
|
24
24
|
SegmentStatus,
|
|
25
|
+
SpanDelta,
|
|
25
26
|
TimeMarker,
|
|
26
27
|
} from '../utils';
|
|
27
28
|
import {
|
|
28
29
|
computeOffscreenMarkers,
|
|
30
|
+
computeSpanDelta,
|
|
29
31
|
computeSpanGaps,
|
|
30
32
|
computeSpanMarkers,
|
|
31
33
|
computeSpanSegments,
|
|
@@ -223,6 +225,11 @@ function projectSegments(
|
|
|
223
225
|
// Small render helpers
|
|
224
226
|
// ---------------------------------------------------------------------------
|
|
225
227
|
|
|
228
|
+
/** Estimated rendered width of a 10px-mono duration label (6px/glyph + padding). */
|
|
229
|
+
function estimateLabelWidthPx(label: string): number {
|
|
230
|
+
return label.length * 6 + 12;
|
|
231
|
+
}
|
|
232
|
+
|
|
226
233
|
function DurationLabel({
|
|
227
234
|
label,
|
|
228
235
|
className,
|
|
@@ -317,7 +324,7 @@ function SegmentBar({
|
|
|
317
324
|
const leadInLabel = formatDurationPrecise(seg.fullDurationMs);
|
|
318
325
|
const showLeadInLabel =
|
|
319
326
|
showLabels &&
|
|
320
|
-
seg.pixelWidth >= Math.max(40, leadInLabel
|
|
327
|
+
seg.pixelWidth >= Math.max(40, estimateLabelWidthPx(leadInLabel));
|
|
321
328
|
const isFullWidthQueued = segments.length === 1;
|
|
322
329
|
return (
|
|
323
330
|
<Fragment key={i}>
|
|
@@ -341,7 +348,8 @@ function SegmentBar({
|
|
|
341
348
|
const label = formatDurationPrecise(seg.fullDurationMs);
|
|
342
349
|
// Only render the label when there's enough room for it without clipping.
|
|
343
350
|
const showLabel =
|
|
344
|
-
showLabels &&
|
|
351
|
+
showLabels &&
|
|
352
|
+
seg.pixelWidth >= Math.max(40, estimateLabelWidthPx(label));
|
|
345
353
|
|
|
346
354
|
return (
|
|
347
355
|
<div
|
|
@@ -462,7 +470,8 @@ const TimelineBar = memo(function TimelineBar({
|
|
|
462
470
|
|
|
463
471
|
const totalLabel = formatDurationPrecise(totalDurationMs);
|
|
464
472
|
const showTotalLabel =
|
|
465
|
-
geometry.visiblePixelWidth >=
|
|
473
|
+
geometry.visiblePixelWidth >=
|
|
474
|
+
Math.max(40, estimateLabelWidthPx(totalLabel));
|
|
466
475
|
|
|
467
476
|
const handleClick = useCallback(() => {
|
|
468
477
|
onSelect(span.spanId);
|
|
@@ -542,45 +551,99 @@ const TimelineBar = memo(function TimelineBar({
|
|
|
542
551
|
export { TimelineBar };
|
|
543
552
|
|
|
544
553
|
// ---------------------------------------------------------------------------
|
|
545
|
-
//
|
|
554
|
+
// DeltaMeasureLine (Alt-key measurement overlays: the selected ↔ hovered
|
|
555
|
+
// measurement, and the ambient consecutive-gap indicators shown without a
|
|
556
|
+
// selection)
|
|
546
557
|
// ---------------------------------------------------------------------------
|
|
547
558
|
|
|
548
|
-
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
// inside 40px, so bars start ~8px from the top of the row).
|
|
552
|
-
const DELTA_ROW_OFFSET_PX = 8;
|
|
559
|
+
// Horizontal distance between the anchor bar's measured edge and the vertical
|
|
560
|
+
// guide — also the width of the connector stub bridging the two.
|
|
561
|
+
const MEASURE_GUIDE_OUTSET_PX = 4;
|
|
553
562
|
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
563
|
+
const DeltaMeasureLine = memo(function DeltaMeasureLine({
|
|
564
|
+
delta,
|
|
565
|
+
anchorRowIndex,
|
|
566
|
+
hoveredRowIndex,
|
|
567
|
+
timelineWidth,
|
|
559
568
|
}: {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
569
|
+
delta: SpanDelta;
|
|
570
|
+
anchorRowIndex: number;
|
|
571
|
+
hoveredRowIndex: number;
|
|
572
|
+
timelineWidth: number;
|
|
564
573
|
}) {
|
|
565
|
-
|
|
574
|
+
// Both ends of the measurement align with the vertical middle of the bars
|
|
575
|
+
// (bars are centered in their rows, so bar center == row center).
|
|
576
|
+
const anchorCenterY = anchorRowIndex * ROW_HEIGHT_PX + ROW_HEIGHT_PX / 2;
|
|
577
|
+
const lineY = hoveredRowIndex * ROW_HEIGHT_PX + ROW_HEIGHT_PX / 2;
|
|
578
|
+
|
|
579
|
+
// Guide connecting the middle of the anchor bar down/up to the line, so
|
|
580
|
+
// the measurement's origin stays legible when the rows are far apart.
|
|
581
|
+
// It sits just outside the anchor bar's measured edge (so it doesn't blend
|
|
582
|
+
// into the bar's border), joined to the bar by a short horizontal stub. The
|
|
583
|
+
// line runs from the elbow corner (the guide's x) to the hovered span's
|
|
584
|
+
// measured edge — pulled short of the edge arrow when the hovered span is
|
|
585
|
+
// fully off-screen.
|
|
586
|
+
const guideTop = Math.min(anchorCenterY, lineY);
|
|
587
|
+
const guideBottom = Math.max(anchorCenterY, lineY);
|
|
588
|
+
const anchorX = delta.anchorFrac * timelineWidth;
|
|
589
|
+
const guideX =
|
|
590
|
+
anchorX +
|
|
591
|
+
(delta.anchorEdge === 'end'
|
|
592
|
+
? MEASURE_GUIDE_OUTSET_PX
|
|
593
|
+
: -MEASURE_GUIDE_OUTSET_PX);
|
|
594
|
+
const arrowClearance =
|
|
595
|
+
delta.hoveredOffscreen === 'right'
|
|
596
|
+
? -(TINY_BAR_BOX_SIZE_PX + 4)
|
|
597
|
+
: delta.hoveredOffscreen === 'left'
|
|
598
|
+
? TINY_BAR_BOX_SIZE_PX + 4
|
|
599
|
+
: 0;
|
|
600
|
+
const hoveredX = delta.hoveredFrac * timelineWidth + arrowClearance;
|
|
601
|
+
const startX = Math.min(guideX, hoveredX);
|
|
602
|
+
const endX = Math.max(guideX, hoveredX);
|
|
603
|
+
|
|
604
|
+
const label = formatDurationPrecise(delta.deltaMs);
|
|
605
|
+
const labelWidthPx = estimateLabelWidthPx(label);
|
|
606
|
+
// Center the label on the line; when the line is too short, place it beside
|
|
607
|
+
// the right endpoint, flipping left near the viewport's right edge.
|
|
608
|
+
const labelPlacement =
|
|
609
|
+
endX - startX >= labelWidthPx
|
|
610
|
+
? { left: (startX + endX) / 2, translate: '-translate-x-1/2' }
|
|
611
|
+
: endX + 4 + labelWidthPx <= timelineWidth
|
|
612
|
+
? { left: endX + 4, translate: '' }
|
|
613
|
+
: { left: startX - 4, translate: '-translate-x-full' };
|
|
566
614
|
|
|
567
615
|
return (
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
<div
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
616
|
+
<>
|
|
617
|
+
<div
|
|
618
|
+
className="absolute h-px bg-amber-800"
|
|
619
|
+
style={{
|
|
620
|
+
left: Math.min(anchorX, guideX),
|
|
621
|
+
width: MEASURE_GUIDE_OUTSET_PX,
|
|
622
|
+
top: anchorCenterY,
|
|
623
|
+
}}
|
|
624
|
+
/>
|
|
625
|
+
<div
|
|
626
|
+
className="absolute w-px bg-amber-800"
|
|
627
|
+
style={{
|
|
628
|
+
left: guideX,
|
|
629
|
+
top: guideTop,
|
|
630
|
+
height: guideBottom - guideTop,
|
|
631
|
+
}}
|
|
632
|
+
/>
|
|
633
|
+
<div
|
|
634
|
+
className="absolute h-px bg-amber-800"
|
|
635
|
+
style={{ left: startX, width: Math.max(endX - startX, 1), top: lineY }}
|
|
636
|
+
/>
|
|
637
|
+
<span
|
|
638
|
+
className={cn(
|
|
639
|
+
'absolute -translate-y-1/2 font-mono text-[10px] font-medium leading-none tabular-nums whitespace-nowrap rounded-xs bg-background-100 px-1 py-0.5 text-amber-800',
|
|
640
|
+
labelPlacement.translate
|
|
641
|
+
)}
|
|
642
|
+
style={{ left: labelPlacement.left, top: lineY }}
|
|
643
|
+
>
|
|
581
644
|
{label}
|
|
582
645
|
</span>
|
|
583
|
-
|
|
646
|
+
</>
|
|
584
647
|
);
|
|
585
648
|
});
|
|
586
649
|
|
|
@@ -624,6 +687,13 @@ export function TimelineHeader({
|
|
|
624
687
|
// Timeline
|
|
625
688
|
// ---------------------------------------------------------------------------
|
|
626
689
|
|
|
690
|
+
export interface TimelineHover {
|
|
691
|
+
/** Pointer x as a fraction of the timeline's content width, in [0, 1]. */
|
|
692
|
+
fraction: number;
|
|
693
|
+
/** Row index under the pointer; may be past the last row — not validated. */
|
|
694
|
+
rowIndex: number;
|
|
695
|
+
}
|
|
696
|
+
|
|
627
697
|
export function Timeline({
|
|
628
698
|
spans,
|
|
629
699
|
viewStart,
|
|
@@ -633,7 +703,7 @@ export function Timeline({
|
|
|
633
703
|
searchResult,
|
|
634
704
|
onSelect,
|
|
635
705
|
onRevealTime,
|
|
636
|
-
|
|
706
|
+
hover,
|
|
637
707
|
altHeld = false,
|
|
638
708
|
}: {
|
|
639
709
|
spans: Span[];
|
|
@@ -644,7 +714,7 @@ export function Timeline({
|
|
|
644
714
|
searchResult: SpanSearchResult;
|
|
645
715
|
onSelect: (spanId: string) => void;
|
|
646
716
|
onRevealTime?: (timeMs: number) => void;
|
|
647
|
-
|
|
717
|
+
hover?: TimelineHover | null;
|
|
648
718
|
altHeld?: boolean;
|
|
649
719
|
}): ReactNode {
|
|
650
720
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -667,11 +737,45 @@ export function Timeline({
|
|
|
667
737
|
return () => ro.disconnect();
|
|
668
738
|
}, []);
|
|
669
739
|
|
|
670
|
-
|
|
671
|
-
|
|
740
|
+
// Each consecutive gap renders as a measurement from the earlier span's end
|
|
741
|
+
// edge to the next span's start, in the same visual language as the
|
|
742
|
+
// selected ↔ hovered measurement.
|
|
743
|
+
const gapMeasurements = useMemo(
|
|
744
|
+
() =>
|
|
745
|
+
computeSpanGaps(spans, viewStart, viewEnd).map((gap) => ({
|
|
746
|
+
delta: {
|
|
747
|
+
deltaMs: gap.gapMs,
|
|
748
|
+
anchorFrac: gap.leftFrac,
|
|
749
|
+
hoveredFrac: gap.rightFrac,
|
|
750
|
+
anchorEdge: 'end',
|
|
751
|
+
hoveredOffscreen: null,
|
|
752
|
+
} satisfies SpanDelta,
|
|
753
|
+
anchorRowIndex: gap.rowIndex,
|
|
754
|
+
hoveredRowIndex: gap.rowIndex + 1,
|
|
755
|
+
})),
|
|
672
756
|
[spans, viewStart, viewEnd]
|
|
673
757
|
);
|
|
674
758
|
|
|
759
|
+
// With a span selected, Alt+hover measures selected ↔ hovered instead of
|
|
760
|
+
// showing the all-sibling-gaps overlay.
|
|
761
|
+
const anchorIndex = useMemo(
|
|
762
|
+
() => (selectedId ? spans.findIndex((s) => s.spanId === selectedId) : -1),
|
|
763
|
+
[spans, selectedId]
|
|
764
|
+
);
|
|
765
|
+
|
|
766
|
+
const measurement = useMemo(() => {
|
|
767
|
+
if (!altHeld || hover == null || hover.rowIndex === anchorIndex) {
|
|
768
|
+
return null;
|
|
769
|
+
}
|
|
770
|
+
const anchorSpan = spans[anchorIndex];
|
|
771
|
+
const hoveredSpan = spans[hover.rowIndex];
|
|
772
|
+
if (!anchorSpan || !hoveredSpan) return null;
|
|
773
|
+
const delta = computeSpanDelta(anchorSpan, hoveredSpan, viewStart, viewEnd);
|
|
774
|
+
return delta
|
|
775
|
+
? { delta, anchorRowIndex: anchorIndex, hoveredRowIndex: hover.rowIndex }
|
|
776
|
+
: null;
|
|
777
|
+
}, [altHeld, anchorIndex, hover, spans, viewStart, viewEnd]);
|
|
778
|
+
|
|
675
779
|
return (
|
|
676
780
|
<div
|
|
677
781
|
ref={containerRef}
|
|
@@ -694,14 +798,14 @@ export function Timeline({
|
|
|
694
798
|
) : null
|
|
695
799
|
)}
|
|
696
800
|
</div>
|
|
697
|
-
{
|
|
801
|
+
{hover != null && (
|
|
698
802
|
<div
|
|
699
803
|
className="absolute inset-y-0 pointer-events-none z-10"
|
|
700
804
|
style={TIMELINE_INSET_STYLE}
|
|
701
805
|
>
|
|
702
806
|
<div
|
|
703
807
|
className="absolute top-0 bottom-0 w-px bg-gray-alpha-500"
|
|
704
|
-
style={{ left: `${
|
|
808
|
+
style={{ left: `${hover.fraction * 100}%` }}
|
|
705
809
|
/>
|
|
706
810
|
</div>
|
|
707
811
|
)}
|
|
@@ -720,23 +824,30 @@ export function Timeline({
|
|
|
720
824
|
/>
|
|
721
825
|
))}
|
|
722
826
|
</div>
|
|
723
|
-
{altHeld && (
|
|
827
|
+
{altHeld && anchorIndex < 0 && (
|
|
724
828
|
<div
|
|
725
829
|
aria-hidden
|
|
726
830
|
className="absolute inset-y-0 pointer-events-none"
|
|
727
831
|
style={TIMELINE_INSET_STYLE}
|
|
728
832
|
>
|
|
729
|
-
{
|
|
730
|
-
<
|
|
731
|
-
key={gap.
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
label={formatDurationPrecise(gap.gapMs)}
|
|
735
|
-
rowIndex={gap.rowIndex}
|
|
833
|
+
{gapMeasurements.map((gap) => (
|
|
834
|
+
<DeltaMeasureLine
|
|
835
|
+
key={gap.anchorRowIndex}
|
|
836
|
+
{...gap}
|
|
837
|
+
timelineWidth={timelineWidth}
|
|
736
838
|
/>
|
|
737
839
|
))}
|
|
738
840
|
</div>
|
|
739
841
|
)}
|
|
842
|
+
{measurement && (
|
|
843
|
+
<div
|
|
844
|
+
aria-hidden
|
|
845
|
+
className="absolute inset-y-0 pointer-events-none z-20"
|
|
846
|
+
style={TIMELINE_INSET_STYLE}
|
|
847
|
+
>
|
|
848
|
+
<DeltaMeasureLine {...measurement} timelineWidth={timelineWidth} />
|
|
849
|
+
</div>
|
|
850
|
+
)}
|
|
740
851
|
</div>
|
|
741
852
|
);
|
|
742
853
|
}
|
|
@@ -86,7 +86,7 @@ export function TraceShortcutHelper({
|
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
return (
|
|
89
|
-
<div className="group
|
|
89
|
+
<div className="group pointer-events-auto hidden h-8 w-fit items-center gap-1 text-xs leading-none text-gray-900 @min-[480px]:flex">
|
|
90
90
|
<span
|
|
91
91
|
aria-live="polite"
|
|
92
92
|
aria-atomic="true"
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
TIMELINE_PADDING_PX,
|
|
29
29
|
Timeline,
|
|
30
30
|
TimelineHeader,
|
|
31
|
+
type TimelineHover,
|
|
31
32
|
} from './components/timeline';
|
|
32
33
|
import { TraceShortcutHelper } from './components/trace-shortcut-helper';
|
|
33
34
|
import { ROW_HEIGHT_PX, scrollRowIntoView } from './components/use-row-window';
|
|
@@ -212,8 +213,8 @@ function NewTraceViewerContent({
|
|
|
212
213
|
|
|
213
214
|
const viewDuration = viewport.end - viewport.start;
|
|
214
215
|
|
|
215
|
-
// Keep a ref to the live viewport so
|
|
216
|
-
//
|
|
216
|
+
// Keep a ref to the live viewport so zoom callbacks can read the current
|
|
217
|
+
// range without being recreated on every pan (which would bust TimelineBar's
|
|
217
218
|
// memo and re-render every row each animation frame).
|
|
218
219
|
const viewportRef = useRef(viewport);
|
|
219
220
|
viewportRef.current = viewport;
|
|
@@ -253,19 +254,17 @@ function NewTraceViewerContent({
|
|
|
253
254
|
|
|
254
255
|
const zoomBy = useCallback(
|
|
255
256
|
(factor: number) => {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
);
|
|
262
|
-
return clampToRoot({
|
|
257
|
+
const { start, end } = viewportRef.current;
|
|
258
|
+
const center = (start + end) / 2;
|
|
259
|
+
const newDuration = Math.max(MIN_VIEWPORT_MS, (end - start) * factor);
|
|
260
|
+
animateTo(
|
|
261
|
+
clampToRoot({
|
|
263
262
|
start: center - newDuration / 2,
|
|
264
263
|
end: center + newDuration / 2,
|
|
265
|
-
})
|
|
266
|
-
|
|
264
|
+
})
|
|
265
|
+
);
|
|
267
266
|
},
|
|
268
|
-
[
|
|
267
|
+
[animateTo, clampToRoot]
|
|
269
268
|
);
|
|
270
269
|
|
|
271
270
|
const zoomIn = useCallback(() => zoomBy(ZOOM_FACTOR), [zoomBy]);
|
|
@@ -400,14 +399,14 @@ function NewTraceViewerContent({
|
|
|
400
399
|
}, [handleClearActiveSpan]);
|
|
401
400
|
|
|
402
401
|
const timelineRef = useRef<HTMLDivElement>(null);
|
|
403
|
-
const [
|
|
402
|
+
const [hover, setHover] = useState<TimelineHover | null>(null);
|
|
404
403
|
|
|
405
404
|
const hoverInfo = useMemo(() => {
|
|
406
|
-
if (
|
|
407
|
-
const absTime = viewport.start +
|
|
405
|
+
if (hover == null) return null;
|
|
406
|
+
const absTime = viewport.start + hover.fraction * viewDuration;
|
|
408
407
|
const offset = absTime - root.startTime;
|
|
409
|
-
return { fraction:
|
|
410
|
-
}, [
|
|
408
|
+
return { fraction: hover.fraction, label: formatDurationPrecise(offset) };
|
|
409
|
+
}, [hover, viewport.start, viewDuration, root.startTime]);
|
|
411
410
|
|
|
412
411
|
const handleTimelineMouseMove = useCallback(
|
|
413
412
|
(e: React.MouseEvent<HTMLDivElement>) => {
|
|
@@ -423,13 +422,16 @@ function NewTraceViewerContent({
|
|
|
423
422
|
(e.clientX - rect.left - TIMELINE_PADDING_PX) / contentWidth
|
|
424
423
|
)
|
|
425
424
|
);
|
|
426
|
-
|
|
425
|
+
setHover({
|
|
426
|
+
fraction,
|
|
427
|
+
rowIndex: Math.floor((e.clientY - rect.top) / ROW_HEIGHT_PX),
|
|
428
|
+
});
|
|
427
429
|
},
|
|
428
430
|
[]
|
|
429
431
|
);
|
|
430
432
|
|
|
431
433
|
const handleTimelineMouseLeave = useCallback(() => {
|
|
432
|
-
|
|
434
|
+
setHover(null);
|
|
433
435
|
}, []);
|
|
434
436
|
|
|
435
437
|
useEffect(() => {
|
|
@@ -495,7 +497,7 @@ function NewTraceViewerContent({
|
|
|
495
497
|
>
|
|
496
498
|
<div
|
|
497
499
|
id="trace-parent"
|
|
498
|
-
className="flex-1 min-w-0 grid grid-rows-[auto_1fr] h-full min-h-0 overflow-hidden relative bg-background-100"
|
|
500
|
+
className="@container flex-1 min-w-0 grid grid-rows-[auto_1fr] h-full min-h-0 overflow-hidden relative bg-background-100"
|
|
499
501
|
>
|
|
500
502
|
<Minimap
|
|
501
503
|
spans={trace.spans}
|
|
@@ -579,43 +581,45 @@ function NewTraceViewerContent({
|
|
|
579
581
|
searchResult={searchResult}
|
|
580
582
|
onSelect={handleSelectSpan}
|
|
581
583
|
onRevealTime={handleRevealTime}
|
|
582
|
-
|
|
584
|
+
hover={hover}
|
|
583
585
|
altHeld={altHeld}
|
|
584
586
|
/>
|
|
587
|
+
</div>
|
|
588
|
+
<>
|
|
585
589
|
<TraceShortcutHelper
|
|
586
590
|
hasMultipleSpans={trace.spans.length > 1}
|
|
587
591
|
reducedMotion={reducedMotion}
|
|
588
592
|
/>
|
|
589
|
-
|
|
593
|
+
<div className="pointer-events-auto flex items-center border border-gray-alpha-400 rounded-md bg-background-100 shadow-sm overflow-hidden divide-x divide-gray-alpha-400">
|
|
594
|
+
<IconButton
|
|
595
|
+
variant="muted"
|
|
596
|
+
size="small"
|
|
597
|
+
onClick={zoomOut}
|
|
598
|
+
disabled={isAtMinZoom}
|
|
599
|
+
aria-label="Zoom out"
|
|
600
|
+
>
|
|
601
|
+
<ZoomOut className="w-4 h-4" />
|
|
602
|
+
</IconButton>
|
|
603
|
+
<IconButton
|
|
604
|
+
variant="muted"
|
|
605
|
+
size="small"
|
|
606
|
+
onClick={resetZoom}
|
|
607
|
+
aria-label="Reset zoom"
|
|
608
|
+
>
|
|
609
|
+
<RotateCcw className="w-3.5 h-3.5" />
|
|
610
|
+
</IconButton>
|
|
611
|
+
<IconButton
|
|
612
|
+
variant="muted"
|
|
613
|
+
size="small"
|
|
614
|
+
onClick={zoomIn}
|
|
615
|
+
disabled={isAtMaxZoom}
|
|
616
|
+
aria-label="Zoom in"
|
|
617
|
+
>
|
|
618
|
+
<ZoomIn className="w-4 h-4" />
|
|
619
|
+
</IconButton>
|
|
620
|
+
</div>
|
|
621
|
+
</>
|
|
590
622
|
</SplitPane>
|
|
591
|
-
<div className="absolute right-3 bottom-3 z-[5] flex items-center border border-gray-alpha-400 rounded-md bg-background-100 shadow-sm overflow-hidden divide-x divide-gray-alpha-400">
|
|
592
|
-
<IconButton
|
|
593
|
-
variant="muted"
|
|
594
|
-
size="small"
|
|
595
|
-
onClick={zoomOut}
|
|
596
|
-
disabled={isAtMinZoom}
|
|
597
|
-
aria-label="Zoom out"
|
|
598
|
-
>
|
|
599
|
-
<ZoomOut className="w-4 h-4" />
|
|
600
|
-
</IconButton>
|
|
601
|
-
<IconButton
|
|
602
|
-
variant="muted"
|
|
603
|
-
size="small"
|
|
604
|
-
onClick={resetZoom}
|
|
605
|
-
aria-label="Reset zoom"
|
|
606
|
-
>
|
|
607
|
-
<RotateCcw className="w-3.5 h-3.5" />
|
|
608
|
-
</IconButton>
|
|
609
|
-
<IconButton
|
|
610
|
-
variant="muted"
|
|
611
|
-
size="small"
|
|
612
|
-
onClick={zoomIn}
|
|
613
|
-
disabled={isAtMaxZoom}
|
|
614
|
-
aria-label="Zoom in"
|
|
615
|
-
>
|
|
616
|
-
<ZoomIn className="w-4 h-4" />
|
|
617
|
-
</IconButton>
|
|
618
|
-
</div>
|
|
619
623
|
</div>
|
|
620
624
|
|
|
621
625
|
<TraceDetailPanel
|