@workflow/web-shared 5.0.0-beta.5 → 5.0.0-beta.7

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.
@@ -8,7 +8,7 @@ import { MiddleTruncate } from './middle-truncate/middle-truncate';
8
8
  const eventStyles = {
9
9
  run: { icon: WorkflowIcon, className: 'text-blue-900' },
10
10
  step: { icon: StepForwardIcon, className: 'text-green-900' },
11
- hook: { icon: WebhookIcon, className: 'text-yellow-900' },
11
+ hook: { icon: WebhookIcon, className: 'text-gray-900' },
12
12
  sleep: { icon: SleepIcon, className: 'text-gray-900' },
13
13
  };
14
14
  const defaultStyle = {
@@ -1 +1 @@
1
- {"version":3,"file":"event-list.js","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/components/event-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Circle } from 'lucide-react';\nimport { cn } from '../../../lib/utils';\nimport type { Span } from '../../trace-viewer/types';\nimport { formatDuration } from '../../trace-viewer/util/timing';\nimport {\n WorkflowIcon,\n WebhookIcon,\n SleepIcon,\n StepForwardIcon,\n} from '../icons';\nimport { getSpanDurationMs } from '../utils';\nimport { MiddleTruncate } from './middle-truncate/middle-truncate';\n\ninterface EventStyle {\n icon: React.ComponentType<{ className?: string }>;\n className: string;\n}\n\nconst eventStyles: Record<string, EventStyle> = {\n run: { icon: WorkflowIcon, className: 'text-blue-900' },\n step: { icon: StepForwardIcon, className: 'text-green-900' },\n hook: { icon: WebhookIcon, className: 'text-yellow-900' },\n sleep: { icon: SleepIcon, className: 'text-gray-900' },\n};\n\nconst defaultStyle: EventStyle = {\n icon: Circle,\n className: 'text-gray-900',\n};\n\nconst ROW_HEIGHT_CLASS = 'h-10';\n\nfunction getEventStyle(resource: string, isErrored: boolean): EventStyle {\n const style = eventStyles[resource] ?? defaultStyle;\n return {\n icon: style.icon,\n className: cn(isErrored ? 'text-red-900' : style.className),\n };\n}\n\nconst EventRow = ({\n span,\n isSelected,\n onSelectSpan,\n}: {\n span: Span;\n isSelected: boolean;\n onSelectSpan: (spanId: string) => void;\n}) => {\n const durationMs = getSpanDurationMs(span);\n const isErrored =\n (span.attributes.data as Record<string, unknown>).status === 'failed';\n const { icon: Icon, className: tagClassName } = getEventStyle(\n span.resource,\n isErrored\n );\n\n return (\n <li\n className={cn(\n 'relative overflow-clip group after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-gray-alpha-400',\n ROW_HEIGHT_CLASS\n )}\n role=\"treeitem\"\n aria-selected={isSelected}\n aria-expanded={isSelected}\n aria-level={1}\n onClick={() => onSelectSpan(span.spanId)}\n >\n <div className=\"h-full hover:bg-gray-100 group-aria-selected:bg-gray-100 group-aria-selected:hover:bg-gray-200\">\n <div className=\"flex h-full min-w-0 items-center px-2\">\n <div className=\"flex min-w-0 flex-1 items-center gap-2\">\n <span className={cn('shrink-0', tagClassName)}>\n <Icon className=\"w-4 h-4\" />\n </span>\n <span className=\"min-w-0 text-label-14\">\n <MiddleTruncate value={span.name} />\n </span>\n </div>\n <div className=\"ml-2 shrink-0\">\n <span className=\"text-label-14 text-gray-900 tabular-nums\">\n {formatDuration(durationMs)}\n </span>\n </div>\n </div>\n </div>\n </li>\n );\n};\n\nconst EventList = ({\n spans,\n activeSpanId,\n onSelectSpan,\n}: {\n spans: Span[];\n activeSpanId: string | null;\n onSelectSpan: (spanId: string) => void;\n}) => {\n return (\n <ul id=\"event-list\" role=\"tree\" className=\"block min-h-0 overflow-visible\">\n {spans.map((span) => {\n return (\n <EventRow\n key={span.spanId}\n span={span}\n isSelected={span.spanId === activeSpanId}\n onSelectSpan={onSelectSpan}\n />\n );\n })}\n </ul>\n );\n};\n\nexport default EventList;\n"]}
1
+ {"version":3,"file":"event-list.js","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/components/event-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Circle } from 'lucide-react';\nimport { cn } from '../../../lib/utils';\nimport type { Span } from '../../trace-viewer/types';\nimport { formatDuration } from '../../trace-viewer/util/timing';\nimport {\n WorkflowIcon,\n WebhookIcon,\n SleepIcon,\n StepForwardIcon,\n} from '../icons';\nimport { getSpanDurationMs } from '../utils';\nimport { MiddleTruncate } from './middle-truncate/middle-truncate';\n\ninterface EventStyle {\n icon: React.ComponentType<{ className?: string }>;\n className: string;\n}\n\nconst eventStyles: Record<string, EventStyle> = {\n run: { icon: WorkflowIcon, className: 'text-blue-900' },\n step: { icon: StepForwardIcon, className: 'text-green-900' },\n hook: { icon: WebhookIcon, className: 'text-gray-900' },\n sleep: { icon: SleepIcon, className: 'text-gray-900' },\n};\n\nconst defaultStyle: EventStyle = {\n icon: Circle,\n className: 'text-gray-900',\n};\n\nconst ROW_HEIGHT_CLASS = 'h-10';\n\nfunction getEventStyle(resource: string, isErrored: boolean): EventStyle {\n const style = eventStyles[resource] ?? defaultStyle;\n return {\n icon: style.icon,\n className: cn(isErrored ? 'text-red-900' : style.className),\n };\n}\n\nconst EventRow = ({\n span,\n isSelected,\n onSelectSpan,\n}: {\n span: Span;\n isSelected: boolean;\n onSelectSpan: (spanId: string) => void;\n}) => {\n const durationMs = getSpanDurationMs(span);\n const isErrored =\n (span.attributes.data as Record<string, unknown>).status === 'failed';\n const { icon: Icon, className: tagClassName } = getEventStyle(\n span.resource,\n isErrored\n );\n\n return (\n <li\n className={cn(\n 'relative overflow-clip group after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-gray-alpha-400',\n ROW_HEIGHT_CLASS\n )}\n role=\"treeitem\"\n aria-selected={isSelected}\n aria-expanded={isSelected}\n aria-level={1}\n onClick={() => onSelectSpan(span.spanId)}\n >\n <div className=\"h-full hover:bg-gray-100 group-aria-selected:bg-gray-100 group-aria-selected:hover:bg-gray-200\">\n <div className=\"flex h-full min-w-0 items-center px-2\">\n <div className=\"flex min-w-0 flex-1 items-center gap-2\">\n <span className={cn('shrink-0', tagClassName)}>\n <Icon className=\"w-4 h-4\" />\n </span>\n <span className=\"min-w-0 text-label-14\">\n <MiddleTruncate value={span.name} />\n </span>\n </div>\n <div className=\"ml-2 shrink-0\">\n <span className=\"text-label-14 text-gray-900 tabular-nums\">\n {formatDuration(durationMs)}\n </span>\n </div>\n </div>\n </div>\n </li>\n );\n};\n\nconst EventList = ({\n spans,\n activeSpanId,\n onSelectSpan,\n}: {\n spans: Span[];\n activeSpanId: string | null;\n onSelectSpan: (spanId: string) => void;\n}) => {\n return (\n <ul id=\"event-list\" role=\"tree\" className=\"block min-h-0 overflow-visible\">\n {spans.map((span) => {\n return (\n <EventRow\n key={span.spanId}\n span={span}\n isSelected={span.spanId === activeSpanId}\n onSelectSpan={onSelectSpan}\n />\n );\n })}\n </ul>\n );\n};\n\nexport default EventList;\n"]}
@@ -8,7 +8,7 @@ declare const TimelineBar: import("react").NamedExoticComponent<{
8
8
  viewDuration: number;
9
9
  containerWidth: number;
10
10
  isSelected: boolean;
11
- onClick: () => void;
11
+ onSelect: (spanId: string) => void;
12
12
  }>;
13
13
  export { TimelineBar };
14
14
  export declare function TimelineHeader({ markers, hoverInfo, }: {
@@ -1 +1 @@
1
- {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/components/timeline.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,KAAK,EAAiB,UAAU,EAAE,MAAM,UAAU,CAAC;AAgC1D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAoCtC,QAAA,MAAM,WAAW;UAQT,IAAI;eACC,MAAM;kBACH,MAAM;oBACJ,MAAM;gBACV,OAAO;aACV,MAAM,IAAI;EAsJnB,CAAC;AAEH,OAAO,EAAE,WAAW,EAAE,CAAC;AAEvB,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,SAAS,GACV,EAAE;IACD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACxD,GAAG,SAAS,CAwBZ;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,SAAS,EACT,OAAO,EACP,OAAO,EACP,UAAU,EACV,QAAQ,EACR,aAAa,EACb,OAAe,GAChB,EAAE;IACD,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,SAAS,CAwFZ"}
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/components/timeline.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,KAAK,EAA0B,UAAU,EAAE,MAAM,UAAU,CAAC;AAcnE,eAAO,MAAM,mBAAmB,KAAK,CAAC;AA8OtC,QAAA,MAAM,WAAW;UAQT,IAAI;eACC,MAAM;kBACH,MAAM;oBACJ,MAAM;gBACV,OAAO;cACT,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI;EAgFlC,CAAC;AAEH,OAAO,EAAE,WAAW,EAAE,CAAC;AAqDvB,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,SAAS,GACV,EAAE;IACD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACxD,GAAG,SAAS,CAwBZ;AAMD,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,SAAS,EACT,OAAO,EACP,OAAO,EACP,UAAU,EACV,QAAQ,EACR,aAAa,EACb,OAAe,GAChB,EAAE;IACD,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,SAAS,CAgFZ"}
@@ -1,113 +1,204 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { ArrowLeft, ArrowRight } from 'lucide-react';
4
- import { memo, useEffect, useMemo, useRef, useState } from 'react';
4
+ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
5
5
  import { cn } from '../../../lib/utils';
6
6
  import { formatDuration, getHighResInMs } from '../../trace-viewer/util/timing';
7
- import { computeSpanGaps, computeSpanSegments, getSpanDurationMs, getResourceColor, } from '../utils';
8
- const SEGMENT_CONFIG = {
9
- queued: { className: 'bg-gray-500' },
10
- retrying: {
11
- className: 'box-border bg-gray-500',
12
- },
13
- waiting: { className: 'bg-gray-500' },
14
- running: { className: 'bg-blue-700' },
15
- failed: { className: 'bg-red-700' },
16
- succeeded: { className: 'bg-green-700' },
17
- sleeping: { className: 'bg-gray-500' },
18
- received: { className: 'bg-blue-700' },
19
- };
20
- const BAR_HEIGHT_PX = 24;
7
+ import { computeSpanGaps, computeSpanSegments, getResourceColor, getSpanDurationMs, } from '../utils';
8
+ // ---------------------------------------------------------------------------
9
+ // Constants
10
+ // ---------------------------------------------------------------------------
21
11
  const TINY_BAR_BOX_SIZE_PX = 24;
22
12
  const TINY_BAR_WIDTH_PX = 4;
23
- const SEGMENT_GAP_PX = 1;
24
- // Keep this in sync with the rendered row height in the timeline/event list.
25
- const ROW_HEIGHT = 40;
26
- const CONTAINER_PAD_Y = 8;
27
- const END_CAP_HEIGHT = 8;
28
13
  export const TIMELINE_PADDING_PX = 16;
29
- const ORIGIN_MARKER_EPSILON = 0.000001;
30
- const DeltaIndicator = memo(function DeltaIndicator({ leftFrac, rightFrac, label, rowIndex, }) {
31
- const centerY = CONTAINER_PAD_Y + (rowIndex + 1) * ROW_HEIGHT;
32
- return (_jsxs("div", { className: "absolute pointer-events-none", style: {
33
- left: `${leftFrac * 100}%`,
34
- width: `${(rightFrac - leftFrac) * 100}%`,
35
- top: centerY - END_CAP_HEIGHT / 2,
36
- height: END_CAP_HEIGHT,
37
- }, children: [_jsx("div", { className: "absolute left-0 top-0 w-px h-full bg-amber-800" }), _jsx("div", { className: "absolute left-0 right-0 top-1/2 h-px bg-amber-800" }), _jsx("div", { className: "absolute right-0 top-0 w-px h-full bg-amber-800" }), _jsx("span", { className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-label-12 leading-none whitespace-nowrap rounded-xs px-1 py-0.5 text-gray-100 bg-amber-800", children: label })] }));
38
- });
39
- const TimelineBar = memo(function TimelineBar({ span, viewStart, viewDuration, containerWidth, isSelected, onClick, }) {
40
- const startTime = getHighResInMs(span.startTime);
41
- const endTime = getHighResInMs(span.endTime);
14
+ const SEGMENT_CLASSES = {
15
+ queued: 'bg-gray-400 border border-gray-500',
16
+ retrying: 'bg-gray-400 border border-gray-500',
17
+ waiting: 'bg-gray-400 border border-gray-500',
18
+ running: 'bg-blue-200 border border-blue-500',
19
+ failed: 'bg-red-200 border border-red-500',
20
+ succeeded: 'bg-green-200 border border-green-500',
21
+ sleeping: 'bg-gray-400 border border-gray-500',
22
+ received: 'bg-blue-200 border border-blue-500',
23
+ };
24
+ const TIMELINE_INSET_STYLE = {
25
+ left: TIMELINE_PADDING_PX,
26
+ right: TIMELINE_PADDING_PX,
27
+ };
28
+ /**
29
+ * Compute the bar's geometry inside the timeline viewport. Percentages are
30
+ * always in [0, 100] so we never emit CSS values that exceed browser layout
31
+ * limits at extreme zoom.
32
+ */
33
+ function computeBarGeometry(startMs, endMs, viewStart, viewEnd, containerWidth) {
34
+ const viewDuration = viewEnd - viewStart;
35
+ const visibleStartMs = Math.max(startMs, viewStart);
36
+ const visibleEndMs = Math.min(endMs, viewEnd);
37
+ const visibleDurationMs = Math.max(0, visibleEndMs - visibleStartMs);
38
+ const widthFrac = viewDuration > 0 ? visibleDurationMs / viewDuration : 0;
39
+ const visiblePixelWidth = widthFrac * containerWidth;
40
+ const isTiny = containerWidth > 0 && visiblePixelWidth < TINY_BAR_BOX_SIZE_PX;
41
+ const extendsOffLeft = startMs < viewStart;
42
+ const extendsOffRight = endMs > viewEnd;
43
+ const mode = isTiny && (extendsOffLeft || extendsOffRight)
44
+ ? { kind: 'arrow', direction: extendsOffRight ? 'right' : 'left' }
45
+ : isTiny
46
+ ? { kind: 'tiny' }
47
+ : { kind: 'full' };
48
+ return {
49
+ mode,
50
+ leftPct: viewDuration > 0
51
+ ? ((visibleStartMs - viewStart) / viewDuration) * 100
52
+ : 0,
53
+ widthPct: widthFrac * 100,
54
+ visibleStartMs,
55
+ visibleEndMs,
56
+ visiblePixelWidth,
57
+ };
58
+ }
59
+ function getBarPositionStyle(geometry) {
60
+ switch (geometry.mode.kind) {
61
+ case 'arrow':
62
+ return {
63
+ left: geometry.mode.direction === 'right'
64
+ ? `calc(100% - ${TINY_BAR_BOX_SIZE_PX}px)`
65
+ : '0px',
66
+ width: `${TINY_BAR_BOX_SIZE_PX}px`,
67
+ };
68
+ case 'tiny':
69
+ return {
70
+ left: `min(${geometry.leftPct}%, calc(100% - ${TINY_BAR_WIDTH_PX}px))`,
71
+ width: `${TINY_BAR_WIDTH_PX}px`,
72
+ };
73
+ case 'full':
74
+ return {
75
+ left: `${geometry.leftPct}%`,
76
+ width: `max(${geometry.widthPct}%, 4px)`,
77
+ };
78
+ }
79
+ }
80
+ /**
81
+ * Project status segments onto the visible portion of a bar. Segments fully
82
+ * outside the visible window are dropped; segments crossing the edge are
83
+ * clipped to [0%, 100%] of the visible bar.
84
+ */
85
+ function projectSegments(segments, spanStartMs, spanDurationMs, geometry) {
86
+ const visibleDurationMs = geometry.visibleEndMs - geometry.visibleStartMs;
87
+ if (visibleDurationMs <= 0)
88
+ return [];
89
+ return segments.flatMap((seg) => {
90
+ const segStartMs = spanStartMs + seg.startFraction * spanDurationMs;
91
+ const segEndMs = spanStartMs + seg.endFraction * spanDurationMs;
92
+ const startMs = Math.max(segStartMs, geometry.visibleStartMs);
93
+ const endMs = Math.min(segEndMs, geometry.visibleEndMs);
94
+ if (endMs <= startMs)
95
+ return [];
96
+ const widthFrac = (endMs - startMs) / visibleDurationMs;
97
+ return [
98
+ {
99
+ status: seg.status,
100
+ leftPct: ((startMs - geometry.visibleStartMs) / visibleDurationMs) * 100,
101
+ widthPct: widthFrac * 100,
102
+ pixelWidth: widthFrac * geometry.visiblePixelWidth,
103
+ fullDurationMs: (seg.endFraction - seg.startFraction) * spanDurationMs,
104
+ },
105
+ ];
106
+ });
107
+ }
108
+ // ---------------------------------------------------------------------------
109
+ // Small render helpers
110
+ // ---------------------------------------------------------------------------
111
+ function DurationLabel({ label }) {
112
+ return (_jsx("span", { 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-gray-1000 tabular-nums opacity-0 group-hover/timeline-row:opacity-100", children: label }));
113
+ }
114
+ function BoundaryArrow({ direction, }) {
115
+ const Icon = direction === 'right' ? ArrowRight : ArrowLeft;
116
+ return (_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-[0.25rem]", children: _jsx(Icon, { className: "size-3 text-gray-900" }) }));
117
+ }
118
+ function PlainBar({ bg, border, label, }) {
119
+ return (_jsx("div", { className: "relative h-6 w-full min-w-1 rounded-[0.25rem] border", style: { background: bg, borderColor: border }, children: label ? _jsx(DurationLabel, { label: label }) : null }));
120
+ }
121
+ function SegmentBar({ segments }) {
122
+ return (_jsx("div", { className: "relative h-6 w-full", children: segments.map((seg, i) => {
123
+ const label = formatDuration(seg.fullDurationMs);
124
+ // Only render the label when there's enough room for it without clipping.
125
+ const showLabel = seg.pixelWidth >= Math.max(40, label.length * 6 + 12);
126
+ // Beef up the queued segment when it's too narrow to read.
127
+ const isNarrowQueued = seg.status === 'queued' && seg.pixelWidth < 20;
128
+ const overrideBg = isNarrowQueued ? 'var(--ds-gray-400)' : undefined;
129
+ const overrideBorder = isNarrowQueued
130
+ ? 'var(--ds-gray-500)'
131
+ : undefined;
132
+ return (_jsx("div", { className: cn('absolute h-full rounded-[0.25rem]', SEGMENT_CLASSES[seg.status]), style: {
133
+ // 1px gap between adjacent segments, distributed equally.
134
+ left: `calc(${seg.leftPct}% + 0.5px)`,
135
+ width: `calc(${seg.widthPct}% - 1px)`,
136
+ minWidth: 1,
137
+ background: overrideBg,
138
+ borderColor: overrideBorder,
139
+ }, children: showLabel ? _jsx(DurationLabel, { label: label }) : null }, `${seg.status}-${i}`));
140
+ }) }));
141
+ }
142
+ // ---------------------------------------------------------------------------
143
+ // TimelineBar
144
+ // ---------------------------------------------------------------------------
145
+ const TimelineBar = memo(function TimelineBar({ span, viewStart, viewDuration, containerWidth, isSelected, onSelect, }) {
146
+ const startMs = getHighResInMs(span.startTime);
147
+ const endMs = getHighResInMs(span.endTime);
42
148
  const totalDurationMs = getSpanDurationMs(span);
43
- const leftFracRaw = viewDuration > 0 ? (startTime - viewStart) / viewDuration : 0;
44
- const rightFracRaw = viewDuration > 0 ? (endTime - viewStart) / viewDuration : 0;
45
- const widthFrac = rightFracRaw - leftFracRaw;
46
- const leftPct = leftFracRaw * 100;
47
- const widthPct = widthFrac * 100;
48
- const pixelWidth = widthFrac * containerWidth;
49
- const visibleLeftFrac = Math.max(0, Math.min(1, leftFracRaw));
50
- const visibleRightFrac = Math.max(0, Math.min(1, rightFracRaw));
51
- const visiblePixelWidth = Math.max(0, visibleRightFrac - visibleLeftFrac) * containerWidth;
52
- const isTinyBar = containerWidth > 0 && visiblePixelWidth < TINY_BAR_BOX_SIZE_PX;
53
- const [isRowHovered, setIsRowHovered] = useState(false);
54
- const segments = useMemo(() => computeSpanSegments(span), [span]);
149
+ const geometry = useMemo(() => computeBarGeometry(startMs, endMs, viewStart, viewStart + viewDuration, containerWidth), [startMs, endMs, viewStart, viewDuration, containerWidth]);
150
+ const baseSegments = useMemo(() => computeSpanSegments(span), [span]);
151
+ const segments = useMemo(() => geometry.mode.kind === 'full'
152
+ ? projectSegments(baseSegments, startMs, totalDurationMs, geometry)
153
+ : [], [geometry, baseSegments, startMs, totalDurationMs]);
55
154
  const workflowStatus = span.attributes.data
56
155
  ?.status;
57
156
  const isErrored = span.status.code === 2 || workflowStatus === 'failed';
58
157
  const colors = getResourceColor(span.resource);
59
- const fallbackColor = isErrored
60
- ? (colors.errorBar ?? 'var(--ds-red-700)')
61
- : colors.bar;
62
- const renderDurationLabel = (label) => (_jsx("span", { 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", style: { textShadow: '0 1px 1px rgba(0, 0, 0, 0.45)' }, children: label }));
63
- const getMinDurationLabelWidthPx = (label) => Math.max(40, label.length * 6 + 12);
64
- const totalDurationLabel = formatDuration(totalDurationMs);
65
- const showBarDurationLabel = isRowHovered &&
66
- pixelWidth >= getMinDurationLabelWidthPx(totalDurationLabel);
67
- const showBoundaryArrow = isTinyBar && (leftFracRaw < 0 || rightFracRaw > 1);
68
- const BoundaryArrow = leftFracRaw < 0.5 ? ArrowLeft : ArrowRight;
69
- const barContent = showBoundaryArrow ? (_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-[0.25rem]", children: _jsx(BoundaryArrow, { className: "size-3 text-gray-900" }) })) : isTinyBar ? (_jsx("div", { className: "h-6 rounded-[0.25rem]", style: { background: fallbackColor } })) : segments.length > 0 ? (_jsx("div", { className: "relative h-6 w-full", children: segments.map((seg, i) => {
70
- const segPixelWidth = (seg.endFraction - seg.startFraction) * pixelWidth;
71
- const segDurationLabel = formatDuration((seg.endFraction - seg.startFraction) * totalDurationMs);
72
- const showSegmentDurationLabel = isRowHovered &&
73
- segPixelWidth >= getMinDurationLabelWidthPx(segDurationLabel);
74
- const segStyle = seg.status === 'queued' && segPixelWidth < 20
75
- ? { background: 'var(--ds-gray-500)' }
76
- : SEGMENT_CONFIG[seg.status].style;
77
- return (_jsx("div", { className: cn('absolute h-full rounded-[0.25rem]', SEGMENT_CONFIG[seg.status].className), style: {
78
- left: `calc(${seg.startFraction * 100}% + ${SEGMENT_GAP_PX / 2}px)`,
79
- width: `calc(${(seg.endFraction - seg.startFraction) * 100}% - ${SEGMENT_GAP_PX}px)`,
80
- minWidth: 1,
81
- ...segStyle,
82
- }, children: showSegmentDurationLabel
83
- ? renderDurationLabel(segDurationLabel)
84
- : null }, `${seg.status}-${i}`));
85
- }) })) : (_jsx("div", { className: "relative h-6 rounded-[0.25rem]", style: {
86
- width: '100%',
87
- minWidth: 4,
88
- background: fallbackColor,
89
- }, children: showBarDurationLabel ? renderDurationLabel(totalDurationLabel) : null }));
90
- return (_jsx("div", { role: "treeitem", "aria-selected": isSelected, "aria-expanded": isSelected, "aria-level": 1, className: cn('h-10 relative flex items-center hover:bg-gray-100 aria-selected:bg-gray-100 aria-selected:hover:bg-gray-200'), onMouseEnter: () => setIsRowHovered(true), onMouseLeave: () => setIsRowHovered(false), onClick: onClick, children: _jsx("div", { className: "absolute inset-y-0", style: {
91
- left: TIMELINE_PADDING_PX,
92
- right: TIMELINE_PADDING_PX,
93
- }, children: _jsx("div", { className: "absolute top-1/2 -translate-y-1/2", style: {
94
- left: showBoundaryArrow
95
- ? `min(max(${leftPct}%, 0px), calc(100% - ${TINY_BAR_BOX_SIZE_PX}px))`
96
- : isTinyBar
97
- ? `min(${leftPct}%, calc(100% - ${TINY_BAR_WIDTH_PX}px))`
98
- : `${leftPct}%`,
99
- width: showBoundaryArrow
100
- ? `${TINY_BAR_BOX_SIZE_PX}px`
101
- : isTinyBar
102
- ? `${TINY_BAR_WIDTH_PX}px`
103
- : `max(${widthPct}%, 4px)`,
104
- height: BAR_HEIGHT_PX,
105
- }, children: barContent }) }) }));
158
+ const fallbackBg = isErrored
159
+ ? (colors.errorBg ?? 'var(--ds-red-200)')
160
+ : colors.bg;
161
+ const fallbackBorder = isErrored
162
+ ? (colors.errorBorder ?? 'var(--ds-red-500)')
163
+ : colors.border;
164
+ const totalLabel = formatDuration(totalDurationMs);
165
+ const showTotalLabel = geometry.visiblePixelWidth >= Math.max(40, totalLabel.length * 6 + 12);
166
+ const handleClick = useCallback(() => {
167
+ onSelect(span.spanId);
168
+ }, [onSelect, span.spanId]);
169
+ return (_jsx("div", { role: "treeitem", "aria-selected": isSelected, "aria-expanded": isSelected, "aria-level": 1, className: "group/timeline-row h-10 relative flex items-center hover:bg-gray-100 aria-selected:bg-gray-100 aria-selected:hover:bg-gray-200", onClick: handleClick, children: _jsx("div", { className: "absolute inset-y-0", style: TIMELINE_INSET_STYLE, children: _jsx("div", { className: "absolute top-1/2 h-6 -translate-y-1/2", style: getBarPositionStyle(geometry), children: geometry.mode.kind === 'arrow' ? (_jsx(BoundaryArrow, { direction: geometry.mode.direction })) : geometry.mode.kind === 'tiny' ? (_jsx("div", { className: "h-6 rounded-[0.25rem] border", style: { background: fallbackBg, borderColor: fallbackBorder } })) : segments.length > 0 ? (_jsx(SegmentBar, { segments: segments })) : (_jsx(PlainBar, { bg: fallbackBg, border: fallbackBorder, label: showTotalLabel ? totalLabel : null })) }) }) }));
106
170
  });
107
171
  export { TimelineBar };
172
+ // ---------------------------------------------------------------------------
173
+ // DeltaIndicator (Alt-key gap overlay)
174
+ // ---------------------------------------------------------------------------
175
+ // Row and indicator sizes are local to DeltaIndicator since it's the only
176
+ // place that needs to compute Y positions from a row index. ROW_HEIGHT must
177
+ // match the `h-10` (40px) row used in TimelineBar.
178
+ const DELTA_ROW_HEIGHT_PX = 40;
179
+ const DELTA_CAP_HEIGHT_PX = 8;
180
+ // Vertical offset to sit the indicator inside the gap between row N and N+1,
181
+ // aligned with where the bar starts in the next row (rows center a 24px bar
182
+ // inside 40px, so bars start ~8px from the top of the row).
183
+ const DELTA_ROW_OFFSET_PX = 8;
184
+ const DeltaIndicator = memo(function DeltaIndicator({ leftFrac, rightFrac, label, rowIndex, }) {
185
+ const centerY = DELTA_ROW_OFFSET_PX + (rowIndex + 1) * DELTA_ROW_HEIGHT_PX;
186
+ return (_jsxs("div", { className: "absolute pointer-events-none", style: {
187
+ left: `${leftFrac * 100}%`,
188
+ width: `${(rightFrac - leftFrac) * 100}%`,
189
+ top: centerY - DELTA_CAP_HEIGHT_PX / 2,
190
+ height: DELTA_CAP_HEIGHT_PX,
191
+ }, children: [_jsx("div", { className: "absolute left-0 top-0 w-px h-full bg-amber-800" }), _jsx("div", { className: "absolute left-0 right-0 top-1/2 h-px bg-amber-800" }), _jsx("div", { className: "absolute right-0 top-0 w-px h-full bg-amber-800" }), _jsx("span", { className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-label-12 leading-none whitespace-nowrap rounded-xs px-1 py-0.5 text-gray-100 bg-amber-800", children: label })] }));
192
+ });
193
+ // ---------------------------------------------------------------------------
194
+ // TimelineHeader
195
+ // ---------------------------------------------------------------------------
108
196
  export function TimelineHeader({ markers, hoverInfo, }) {
109
197
  return (_jsx("div", { className: "relative bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-end px-4 pb-1", children: _jsxs("div", { className: "relative h-full flex-1", children: [markers.map((m) => (_jsx("span", { className: "absolute bottom-1 font-mono text-xs font-normal leading-4 text-gray-900 whitespace-nowrap", style: { left: `${m.position * 100}%` }, children: m.label }, `${m.position}-${m.label}`))), hoverInfo && (_jsx("span", { className: "absolute top-1 pointer-events-none z-10 font-mono text-[11px] leading-4 text-gray-1000 whitespace-nowrap bg-background-100 border border-gray-alpha-400 rounded px-1 -translate-x-1/2", style: { left: `${hoverInfo.fraction * 100}%` }, children: hoverInfo.label }))] }) }));
110
198
  }
199
+ // ---------------------------------------------------------------------------
200
+ // Timeline
201
+ // ---------------------------------------------------------------------------
111
202
  export function Timeline({ spans, viewStart, viewEnd, markers, selectedId, onSelect, hoverFraction, altHeld = false, }) {
112
203
  const containerRef = useRef(null);
113
204
  const [containerWidth, setContainerWidth] = useState(0);
@@ -124,15 +215,8 @@ export function Timeline({ spans, viewStart, viewEnd, markers, selectedId, onSel
124
215
  return () => ro.disconnect();
125
216
  }, []);
126
217
  const gaps = useMemo(() => computeSpanGaps(spans, viewStart, viewEnd), [spans, viewStart, viewEnd]);
127
- return (_jsxs("div", { ref: containerRef, className: "relative h-full overflow-hidden", children: [_jsx("div", { "aria-hidden": true, className: "absolute inset-y-0 pointer-events-none", style: {
128
- left: TIMELINE_PADDING_PX,
129
- right: TIMELINE_PADDING_PX,
130
- }, children: markers.map((marker) => Math.abs(marker.value) > ORIGIN_MARKER_EPSILON ? (_jsx("div", { className: "absolute top-0 bottom-0 w-px bg-gray-alpha-300", style: { left: `${marker.position * 100}%` } }, `${marker.position}-${marker.label}`)) : null) }), hoverFraction != null && (_jsx("div", { className: "absolute inset-y-0 pointer-events-none z-10", style: {
131
- left: TIMELINE_PADDING_PX,
132
- right: TIMELINE_PADDING_PX,
133
- }, children: _jsx("div", { className: "absolute top-0 bottom-0 w-px bg-gray-alpha-400", style: { left: `${hoverFraction * 100}%` } }) })), spans.map((span) => (_jsx(TimelineBar, { span: span, viewStart: viewStart, viewDuration: viewDuration, containerWidth: timelineWidth, isSelected: selectedId === span.spanId, onClick: () => onSelect(span.spanId) }, span.spanId))), altHeld && (_jsx("div", { "aria-hidden": true, className: "absolute inset-y-0 pointer-events-none", style: {
134
- left: TIMELINE_PADDING_PX,
135
- right: TIMELINE_PADDING_PX,
136
- }, children: gaps.map((gap) => (_jsx(DeltaIndicator, { leftFrac: gap.leftFrac, rightFrac: gap.rightFrac, label: formatDuration(gap.gapMs, true), rowIndex: gap.rowIndex }, gap.rowIndex))) }))] }));
218
+ return (_jsxs("div", { ref: containerRef, className: "relative h-full overflow-hidden", children: [_jsx("div", { "aria-hidden": true, className: "absolute inset-y-0 pointer-events-none", style: TIMELINE_INSET_STYLE, children: markers.map((marker) =>
219
+ // Skip the "0s" origin marker since the left edge already implies it.
220
+ Math.abs(marker.value) > 0.000001 ? (_jsx("div", { className: "absolute top-0 bottom-0 w-px bg-gray-alpha-300", style: { left: `${marker.position * 100}%` } }, `${marker.position}-${marker.label}`)) : null) }), hoverFraction != null && (_jsx("div", { className: "absolute inset-y-0 pointer-events-none z-10", style: TIMELINE_INSET_STYLE, children: _jsx("div", { className: "absolute top-0 bottom-0 w-px bg-gray-alpha-500", style: { left: `${hoverFraction * 100}%` } }) })), spans.map((span) => (_jsx(TimelineBar, { span: span, viewStart: viewStart, viewDuration: viewDuration, containerWidth: timelineWidth, isSelected: selectedId === span.spanId, onSelect: onSelect }, span.spanId))), altHeld && (_jsx("div", { "aria-hidden": true, className: "absolute inset-y-0 pointer-events-none", style: TIMELINE_INSET_STYLE, children: gaps.map((gap) => (_jsx(DeltaIndicator, { leftFrac: gap.leftFrac, rightFrac: gap.rightFrac, label: formatDuration(gap.gapMs, true), rowIndex: gap.rowIndex }, gap.rowIndex))) }))] }));
137
221
  }
138
222
  //# sourceMappingURL=timeline.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/components/timeline.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { ArrowLeft, ArrowRight } from 'lucide-react';\nimport type { ReactNode } from 'react';\nimport { memo, useEffect, useMemo, useRef, useState } from 'react';\nimport { cn } from '../../../lib/utils';\nimport type { Span } from '../../trace-viewer/types';\nimport { formatDuration, getHighResInMs } from '../../trace-viewer/util/timing';\nimport type { SegmentStatus, TimeMarker } from '../utils';\nimport {\n computeSpanGaps,\n computeSpanSegments,\n getSpanDurationMs,\n getResourceColor,\n} from '../utils';\n\nconst SEGMENT_CONFIG: Record<\n SegmentStatus,\n { className?: string; style?: React.CSSProperties }\n> = {\n queued: { className: 'bg-gray-500' },\n retrying: {\n className: 'box-border bg-gray-500',\n },\n waiting: { className: 'bg-gray-500' },\n running: { className: 'bg-blue-700' },\n failed: { className: 'bg-red-700' },\n succeeded: { className: 'bg-green-700' },\n sleeping: { className: 'bg-gray-500' },\n received: { className: 'bg-blue-700' },\n};\n\nconst BAR_HEIGHT_PX = 24;\nconst TINY_BAR_BOX_SIZE_PX = 24;\nconst TINY_BAR_WIDTH_PX = 4;\nconst SEGMENT_GAP_PX = 1;\n// Keep this in sync with the rendered row height in the timeline/event list.\nconst ROW_HEIGHT = 40;\nconst CONTAINER_PAD_Y = 8;\nconst END_CAP_HEIGHT = 8;\nexport const TIMELINE_PADDING_PX = 16;\nconst ORIGIN_MARKER_EPSILON = 0.000001;\n\nconst DeltaIndicator = memo(function DeltaIndicator({\n leftFrac,\n rightFrac,\n label,\n rowIndex,\n}: {\n leftFrac: number;\n rightFrac: number;\n label: string;\n rowIndex: number;\n}) {\n const centerY = CONTAINER_PAD_Y + (rowIndex + 1) * ROW_HEIGHT;\n\n return (\n <div\n className=\"absolute pointer-events-none\"\n style={{\n left: `${leftFrac * 100}%`,\n width: `${(rightFrac - leftFrac) * 100}%`,\n top: centerY - END_CAP_HEIGHT / 2,\n height: END_CAP_HEIGHT,\n }}\n >\n <div className=\"absolute left-0 top-0 w-px h-full bg-amber-800\" />\n <div className=\"absolute left-0 right-0 top-1/2 h-px bg-amber-800\" />\n <div className=\"absolute right-0 top-0 w-px h-full bg-amber-800\" />\n <span className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-label-12 leading-none whitespace-nowrap rounded-xs px-1 py-0.5 text-gray-100 bg-amber-800\">\n {label}\n </span>\n </div>\n );\n});\n\nconst TimelineBar = memo(function TimelineBar({\n span,\n viewStart,\n viewDuration,\n containerWidth,\n isSelected,\n onClick,\n}: {\n span: Span;\n viewStart: number;\n viewDuration: number;\n containerWidth: number;\n isSelected: boolean;\n onClick: () => void;\n}): ReactNode {\n const startTime = getHighResInMs(span.startTime);\n const endTime = getHighResInMs(span.endTime);\n const totalDurationMs = getSpanDurationMs(span);\n\n const leftFracRaw =\n viewDuration > 0 ? (startTime - viewStart) / viewDuration : 0;\n const rightFracRaw =\n viewDuration > 0 ? (endTime - viewStart) / viewDuration : 0;\n const widthFrac = rightFracRaw - leftFracRaw;\n\n const leftPct = leftFracRaw * 100;\n const widthPct = widthFrac * 100;\n\n const pixelWidth = widthFrac * containerWidth;\n const visibleLeftFrac = Math.max(0, Math.min(1, leftFracRaw));\n const visibleRightFrac = Math.max(0, Math.min(1, rightFracRaw));\n const visiblePixelWidth =\n Math.max(0, visibleRightFrac - visibleLeftFrac) * containerWidth;\n const isTinyBar =\n containerWidth > 0 && visiblePixelWidth < TINY_BAR_BOX_SIZE_PX;\n const [isRowHovered, setIsRowHovered] = useState(false);\n\n const segments = useMemo(() => computeSpanSegments(span), [span]);\n\n const workflowStatus = (span.attributes.data as Record<string, unknown>)\n ?.status as string | undefined;\n const isErrored = span.status.code === 2 || workflowStatus === 'failed';\n const colors = getResourceColor(span.resource);\n const fallbackColor = isErrored\n ? (colors.errorBar ?? 'var(--ds-red-700)')\n : colors.bar;\n const renderDurationLabel = (label: string) => (\n <span\n 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\"\n style={{ textShadow: '0 1px 1px rgba(0, 0, 0, 0.45)' }}\n >\n {label}\n </span>\n );\n const getMinDurationLabelWidthPx = (label: string) =>\n Math.max(40, label.length * 6 + 12);\n const totalDurationLabel = formatDuration(totalDurationMs);\n const showBarDurationLabel =\n isRowHovered &&\n pixelWidth >= getMinDurationLabelWidthPx(totalDurationLabel);\n\n const showBoundaryArrow = isTinyBar && (leftFracRaw < 0 || rightFracRaw > 1);\n const BoundaryArrow = leftFracRaw < 0.5 ? ArrowLeft : ArrowRight;\n const barContent = showBoundaryArrow ? (\n <div className=\"flex h-6 w-6 items-center justify-center rounded-[0.25rem]\">\n <BoundaryArrow className=\"size-3 text-gray-900\" />\n </div>\n ) : isTinyBar ? (\n <div\n className=\"h-6 rounded-[0.25rem]\"\n style={{ background: fallbackColor }}\n />\n ) : segments.length > 0 ? (\n <div className=\"relative h-6 w-full\">\n {segments.map((seg, i) => {\n const segPixelWidth =\n (seg.endFraction - seg.startFraction) * pixelWidth;\n const segDurationLabel = formatDuration(\n (seg.endFraction - seg.startFraction) * totalDurationMs\n );\n const showSegmentDurationLabel =\n isRowHovered &&\n segPixelWidth >= getMinDurationLabelWidthPx(segDurationLabel);\n const segStyle =\n seg.status === 'queued' && segPixelWidth < 20\n ? { background: 'var(--ds-gray-500)' }\n : SEGMENT_CONFIG[seg.status].style;\n\n return (\n <div\n key={`${seg.status}-${i}`}\n className={cn(\n 'absolute h-full rounded-[0.25rem]',\n SEGMENT_CONFIG[seg.status].className\n )}\n style={{\n left: `calc(${seg.startFraction * 100}% + ${SEGMENT_GAP_PX / 2}px)`,\n width: `calc(${(seg.endFraction - seg.startFraction) * 100}% - ${SEGMENT_GAP_PX}px)`,\n minWidth: 1,\n ...segStyle,\n }}\n >\n {showSegmentDurationLabel\n ? renderDurationLabel(segDurationLabel)\n : null}\n </div>\n );\n })}\n </div>\n ) : (\n <div\n className=\"relative h-6 rounded-[0.25rem]\"\n style={{\n width: '100%',\n minWidth: 4,\n background: fallbackColor,\n }}\n >\n {showBarDurationLabel ? renderDurationLabel(totalDurationLabel) : null}\n </div>\n );\n\n return (\n <div\n role=\"treeitem\"\n aria-selected={isSelected}\n aria-expanded={isSelected}\n aria-level={1}\n className={cn(\n 'h-10 relative flex items-center hover:bg-gray-100 aria-selected:bg-gray-100 aria-selected:hover:bg-gray-200'\n )}\n onMouseEnter={() => setIsRowHovered(true)}\n onMouseLeave={() => setIsRowHovered(false)}\n onClick={onClick}\n >\n <div\n className=\"absolute inset-y-0\"\n style={{\n left: TIMELINE_PADDING_PX,\n right: TIMELINE_PADDING_PX,\n }}\n >\n <div\n className=\"absolute top-1/2 -translate-y-1/2\"\n style={{\n left: showBoundaryArrow\n ? `min(max(${leftPct}%, 0px), calc(100% - ${TINY_BAR_BOX_SIZE_PX}px))`\n : isTinyBar\n ? `min(${leftPct}%, calc(100% - ${TINY_BAR_WIDTH_PX}px))`\n : `${leftPct}%`,\n width: showBoundaryArrow\n ? `${TINY_BAR_BOX_SIZE_PX}px`\n : isTinyBar\n ? `${TINY_BAR_WIDTH_PX}px`\n : `max(${widthPct}%, 4px)`,\n height: BAR_HEIGHT_PX,\n }}\n >\n {barContent}\n </div>\n </div>\n </div>\n );\n});\n\nexport { TimelineBar };\n\nexport function TimelineHeader({\n markers,\n hoverInfo,\n}: {\n markers: TimeMarker[];\n hoverInfo?: { fraction: number; label: string } | null;\n}): ReactNode {\n return (\n <div className=\"relative bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-end px-4 pb-1\">\n <div className=\"relative h-full flex-1\">\n {markers.map((m) => (\n <span\n key={`${m.position}-${m.label}`}\n className=\"absolute bottom-1 font-mono text-xs font-normal leading-4 text-gray-900 whitespace-nowrap\"\n style={{ left: `${m.position * 100}%` }}\n >\n {m.label}\n </span>\n ))}\n {hoverInfo && (\n <span\n className=\"absolute top-1 pointer-events-none z-10 font-mono text-[11px] leading-4 text-gray-1000 whitespace-nowrap bg-background-100 border border-gray-alpha-400 rounded px-1 -translate-x-1/2\"\n style={{ left: `${hoverInfo.fraction * 100}%` }}\n >\n {hoverInfo.label}\n </span>\n )}\n </div>\n </div>\n );\n}\n\nexport function Timeline({\n spans,\n viewStart,\n viewEnd,\n markers,\n selectedId,\n onSelect,\n hoverFraction,\n altHeld = false,\n}: {\n spans: Span[];\n viewStart: number;\n viewEnd: number;\n markers: TimeMarker[];\n selectedId: string | null;\n onSelect: (spanId: string) => void;\n hoverFraction?: number | null;\n altHeld?: boolean;\n}): ReactNode {\n const containerRef = useRef<HTMLDivElement>(null);\n const [containerWidth, setContainerWidth] = useState(0);\n const viewDuration = viewEnd - viewStart;\n const timelineWidth = Math.max(0, containerWidth - TIMELINE_PADDING_PX * 2);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n const ro = new ResizeObserver(([entry]) => {\n setContainerWidth(entry.contentRect.width);\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const gaps = useMemo(\n () => computeSpanGaps(spans, viewStart, viewEnd),\n [spans, viewStart, viewEnd]\n );\n\n return (\n <div ref={containerRef} className=\"relative h-full overflow-hidden\">\n <div\n aria-hidden\n className=\"absolute inset-y-0 pointer-events-none\"\n style={{\n left: TIMELINE_PADDING_PX,\n right: TIMELINE_PADDING_PX,\n }}\n >\n {markers.map((marker) =>\n Math.abs(marker.value) > ORIGIN_MARKER_EPSILON ? (\n <div\n key={`${marker.position}-${marker.label}`}\n className=\"absolute top-0 bottom-0 w-px bg-gray-alpha-300\"\n style={{ left: `${marker.position * 100}%` }}\n />\n ) : null\n )}\n </div>\n {hoverFraction != null && (\n <div\n className=\"absolute inset-y-0 pointer-events-none z-10\"\n style={{\n left: TIMELINE_PADDING_PX,\n right: TIMELINE_PADDING_PX,\n }}\n >\n <div\n className=\"absolute top-0 bottom-0 w-px bg-gray-alpha-400\"\n style={{ left: `${hoverFraction * 100}%` }}\n />\n </div>\n )}\n {spans.map((span) => (\n <TimelineBar\n key={span.spanId}\n span={span}\n viewStart={viewStart}\n viewDuration={viewDuration}\n containerWidth={timelineWidth}\n isSelected={selectedId === span.spanId}\n onClick={() => onSelect(span.spanId)}\n />\n ))}\n {altHeld && (\n <div\n aria-hidden\n className=\"absolute inset-y-0 pointer-events-none\"\n style={{\n left: TIMELINE_PADDING_PX,\n right: TIMELINE_PADDING_PX,\n }}\n >\n {gaps.map((gap) => (\n <DeltaIndicator\n key={gap.rowIndex}\n leftFrac={gap.leftFrac}\n rightFrac={gap.rightFrac}\n label={formatDuration(gap.gapMs, true)}\n rowIndex={gap.rowIndex}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/components/timeline.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { ArrowLeft, ArrowRight } from 'lucide-react';\nimport type { CSSProperties, ReactNode } from 'react';\nimport { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { cn } from '../../../lib/utils';\nimport type { Span } from '../../trace-viewer/types';\nimport { formatDuration, getHighResInMs } from '../../trace-viewer/util/timing';\nimport type { Segment, SegmentStatus, TimeMarker } from '../utils';\nimport {\n computeSpanGaps,\n computeSpanSegments,\n getResourceColor,\n getSpanDurationMs,\n} from '../utils';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst TINY_BAR_BOX_SIZE_PX = 24;\nconst TINY_BAR_WIDTH_PX = 4;\nexport const TIMELINE_PADDING_PX = 16;\n\nconst SEGMENT_CLASSES: Record<SegmentStatus, string> = {\n queued: 'bg-gray-400 border border-gray-500',\n retrying: 'bg-gray-400 border border-gray-500',\n waiting: 'bg-gray-400 border border-gray-500',\n running: 'bg-blue-200 border border-blue-500',\n failed: 'bg-red-200 border border-red-500',\n succeeded: 'bg-green-200 border border-green-500',\n sleeping: 'bg-gray-400 border border-gray-500',\n received: 'bg-blue-200 border border-blue-500',\n};\n\nconst TIMELINE_INSET_STYLE: CSSProperties = {\n left: TIMELINE_PADDING_PX,\n right: TIMELINE_PADDING_PX,\n};\n\n// ---------------------------------------------------------------------------\n// Bar geometry\n// ---------------------------------------------------------------------------\n\ntype BarMode =\n | { kind: 'arrow'; direction: 'left' | 'right' }\n | { kind: 'tiny' }\n | { kind: 'full' };\n\ninterface BarGeometry {\n mode: BarMode;\n leftPct: number;\n widthPct: number;\n visibleStartMs: number;\n visibleEndMs: number;\n visiblePixelWidth: number;\n}\n\n/**\n * Compute the bar's geometry inside the timeline viewport. Percentages are\n * always in [0, 100] so we never emit CSS values that exceed browser layout\n * limits at extreme zoom.\n */\nfunction computeBarGeometry(\n startMs: number,\n endMs: number,\n viewStart: number,\n viewEnd: number,\n containerWidth: number\n): BarGeometry {\n const viewDuration = viewEnd - viewStart;\n const visibleStartMs = Math.max(startMs, viewStart);\n const visibleEndMs = Math.min(endMs, viewEnd);\n const visibleDurationMs = Math.max(0, visibleEndMs - visibleStartMs);\n const widthFrac = viewDuration > 0 ? visibleDurationMs / viewDuration : 0;\n const visiblePixelWidth = widthFrac * containerWidth;\n\n const isTiny = containerWidth > 0 && visiblePixelWidth < TINY_BAR_BOX_SIZE_PX;\n const extendsOffLeft = startMs < viewStart;\n const extendsOffRight = endMs > viewEnd;\n\n const mode: BarMode =\n isTiny && (extendsOffLeft || extendsOffRight)\n ? { kind: 'arrow', direction: extendsOffRight ? 'right' : 'left' }\n : isTiny\n ? { kind: 'tiny' }\n : { kind: 'full' };\n\n return {\n mode,\n leftPct:\n viewDuration > 0\n ? ((visibleStartMs - viewStart) / viewDuration) * 100\n : 0,\n widthPct: widthFrac * 100,\n visibleStartMs,\n visibleEndMs,\n visiblePixelWidth,\n };\n}\n\nfunction getBarPositionStyle(geometry: BarGeometry): {\n left: string;\n width: string;\n} {\n switch (geometry.mode.kind) {\n case 'arrow':\n return {\n left:\n geometry.mode.direction === 'right'\n ? `calc(100% - ${TINY_BAR_BOX_SIZE_PX}px)`\n : '0px',\n width: `${TINY_BAR_BOX_SIZE_PX}px`,\n };\n case 'tiny':\n return {\n left: `min(${geometry.leftPct}%, calc(100% - ${TINY_BAR_WIDTH_PX}px))`,\n width: `${TINY_BAR_WIDTH_PX}px`,\n };\n case 'full':\n return {\n left: `${geometry.leftPct}%`,\n width: `max(${geometry.widthPct}%, 4px)`,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Segment projection\n// ---------------------------------------------------------------------------\n\ninterface VisibleSegment {\n status: SegmentStatus;\n leftPct: number;\n widthPct: number;\n pixelWidth: number;\n fullDurationMs: number;\n}\n\n/**\n * Project status segments onto the visible portion of a bar. Segments fully\n * outside the visible window are dropped; segments crossing the edge are\n * clipped to [0%, 100%] of the visible bar.\n */\nfunction projectSegments(\n segments: Segment[],\n spanStartMs: number,\n spanDurationMs: number,\n geometry: BarGeometry\n): VisibleSegment[] {\n const visibleDurationMs = geometry.visibleEndMs - geometry.visibleStartMs;\n if (visibleDurationMs <= 0) return [];\n\n return segments.flatMap((seg) => {\n const segStartMs = spanStartMs + seg.startFraction * spanDurationMs;\n const segEndMs = spanStartMs + seg.endFraction * spanDurationMs;\n const startMs = Math.max(segStartMs, geometry.visibleStartMs);\n const endMs = Math.min(segEndMs, geometry.visibleEndMs);\n if (endMs <= startMs) return [];\n\n const widthFrac = (endMs - startMs) / visibleDurationMs;\n return [\n {\n status: seg.status,\n leftPct:\n ((startMs - geometry.visibleStartMs) / visibleDurationMs) * 100,\n widthPct: widthFrac * 100,\n pixelWidth: widthFrac * geometry.visiblePixelWidth,\n fullDurationMs: (seg.endFraction - seg.startFraction) * spanDurationMs,\n },\n ];\n });\n}\n\n// ---------------------------------------------------------------------------\n// Small render helpers\n// ---------------------------------------------------------------------------\n\nfunction DurationLabel({ label }: { label: string }): ReactNode {\n return (\n <span 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-gray-1000 tabular-nums opacity-0 group-hover/timeline-row:opacity-100\">\n {label}\n </span>\n );\n}\n\nfunction BoundaryArrow({\n direction,\n}: {\n direction: 'left' | 'right';\n}): ReactNode {\n const Icon = direction === 'right' ? ArrowRight : ArrowLeft;\n return (\n <div className=\"flex h-6 w-6 items-center justify-center rounded-[0.25rem]\">\n <Icon className=\"size-3 text-gray-900\" />\n </div>\n );\n}\n\nfunction PlainBar({\n bg,\n border,\n label,\n}: {\n bg: string;\n border: string;\n label: string | null;\n}): ReactNode {\n return (\n <div\n className=\"relative h-6 w-full min-w-1 rounded-[0.25rem] border\"\n style={{ background: bg, borderColor: border }}\n >\n {label ? <DurationLabel label={label} /> : null}\n </div>\n );\n}\n\nfunction SegmentBar({ segments }: { segments: VisibleSegment[] }): ReactNode {\n return (\n <div className=\"relative h-6 w-full\">\n {segments.map((seg, i) => {\n const label = formatDuration(seg.fullDurationMs);\n // Only render the label when there's enough room for it without clipping.\n const showLabel = seg.pixelWidth >= Math.max(40, label.length * 6 + 12);\n // Beef up the queued segment when it's too narrow to read.\n const isNarrowQueued = seg.status === 'queued' && seg.pixelWidth < 20;\n const overrideBg = isNarrowQueued ? 'var(--ds-gray-400)' : undefined;\n const overrideBorder = isNarrowQueued\n ? 'var(--ds-gray-500)'\n : undefined;\n\n return (\n <div\n key={`${seg.status}-${i}`}\n className={cn(\n 'absolute h-full rounded-[0.25rem]',\n SEGMENT_CLASSES[seg.status]\n )}\n style={{\n // 1px gap between adjacent segments, distributed equally.\n left: `calc(${seg.leftPct}% + 0.5px)`,\n width: `calc(${seg.widthPct}% - 1px)`,\n minWidth: 1,\n background: overrideBg,\n borderColor: overrideBorder,\n }}\n >\n {showLabel ? <DurationLabel label={label} /> : null}\n </div>\n );\n })}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// TimelineBar\n// ---------------------------------------------------------------------------\n\nconst TimelineBar = memo(function TimelineBar({\n span,\n viewStart,\n viewDuration,\n containerWidth,\n isSelected,\n onSelect,\n}: {\n span: Span;\n viewStart: number;\n viewDuration: number;\n containerWidth: number;\n isSelected: boolean;\n onSelect: (spanId: string) => void;\n}): ReactNode {\n const startMs = getHighResInMs(span.startTime);\n const endMs = getHighResInMs(span.endTime);\n const totalDurationMs = getSpanDurationMs(span);\n\n const geometry = useMemo(\n () =>\n computeBarGeometry(\n startMs,\n endMs,\n viewStart,\n viewStart + viewDuration,\n containerWidth\n ),\n [startMs, endMs, viewStart, viewDuration, containerWidth]\n );\n\n const baseSegments = useMemo(() => computeSpanSegments(span), [span]);\n const segments = useMemo(\n () =>\n geometry.mode.kind === 'full'\n ? projectSegments(baseSegments, startMs, totalDurationMs, geometry)\n : [],\n [geometry, baseSegments, startMs, totalDurationMs]\n );\n\n const workflowStatus = (span.attributes.data as Record<string, unknown>)\n ?.status as string | undefined;\n const isErrored = span.status.code === 2 || workflowStatus === 'failed';\n const colors = getResourceColor(span.resource);\n const fallbackBg = isErrored\n ? (colors.errorBg ?? 'var(--ds-red-200)')\n : colors.bg;\n const fallbackBorder = isErrored\n ? (colors.errorBorder ?? 'var(--ds-red-500)')\n : colors.border;\n\n const totalLabel = formatDuration(totalDurationMs);\n const showTotalLabel =\n geometry.visiblePixelWidth >= Math.max(40, totalLabel.length * 6 + 12);\n\n const handleClick = useCallback(() => {\n onSelect(span.spanId);\n }, [onSelect, span.spanId]);\n\n return (\n <div\n role=\"treeitem\"\n aria-selected={isSelected}\n aria-expanded={isSelected}\n aria-level={1}\n className=\"group/timeline-row h-10 relative flex items-center hover:bg-gray-100 aria-selected:bg-gray-100 aria-selected:hover:bg-gray-200\"\n onClick={handleClick}\n >\n <div className=\"absolute inset-y-0\" style={TIMELINE_INSET_STYLE}>\n <div\n className=\"absolute top-1/2 h-6 -translate-y-1/2\"\n style={getBarPositionStyle(geometry)}\n >\n {geometry.mode.kind === 'arrow' ? (\n <BoundaryArrow direction={geometry.mode.direction} />\n ) : geometry.mode.kind === 'tiny' ? (\n <div\n className=\"h-6 rounded-[0.25rem] border\"\n style={{ background: fallbackBg, borderColor: fallbackBorder }}\n />\n ) : segments.length > 0 ? (\n <SegmentBar segments={segments} />\n ) : (\n <PlainBar\n bg={fallbackBg}\n border={fallbackBorder}\n label={showTotalLabel ? totalLabel : null}\n />\n )}\n </div>\n </div>\n </div>\n );\n});\n\nexport { TimelineBar };\n\n// ---------------------------------------------------------------------------\n// DeltaIndicator (Alt-key gap overlay)\n// ---------------------------------------------------------------------------\n\n// Row and indicator sizes are local to DeltaIndicator since it's the only\n// place that needs to compute Y positions from a row index. ROW_HEIGHT must\n// match the `h-10` (40px) row used in TimelineBar.\nconst DELTA_ROW_HEIGHT_PX = 40;\nconst DELTA_CAP_HEIGHT_PX = 8;\n// Vertical offset to sit the indicator inside the gap between row N and N+1,\n// aligned with where the bar starts in the next row (rows center a 24px bar\n// inside 40px, so bars start ~8px from the top of the row).\nconst DELTA_ROW_OFFSET_PX = 8;\n\nconst DeltaIndicator = memo(function DeltaIndicator({\n leftFrac,\n rightFrac,\n label,\n rowIndex,\n}: {\n leftFrac: number;\n rightFrac: number;\n label: string;\n rowIndex: number;\n}) {\n const centerY = DELTA_ROW_OFFSET_PX + (rowIndex + 1) * DELTA_ROW_HEIGHT_PX;\n\n return (\n <div\n className=\"absolute pointer-events-none\"\n style={{\n left: `${leftFrac * 100}%`,\n width: `${(rightFrac - leftFrac) * 100}%`,\n top: centerY - DELTA_CAP_HEIGHT_PX / 2,\n height: DELTA_CAP_HEIGHT_PX,\n }}\n >\n <div className=\"absolute left-0 top-0 w-px h-full bg-amber-800\" />\n <div className=\"absolute left-0 right-0 top-1/2 h-px bg-amber-800\" />\n <div className=\"absolute right-0 top-0 w-px h-full bg-amber-800\" />\n <span className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-label-12 leading-none whitespace-nowrap rounded-xs px-1 py-0.5 text-gray-100 bg-amber-800\">\n {label}\n </span>\n </div>\n );\n});\n\n// ---------------------------------------------------------------------------\n// TimelineHeader\n// ---------------------------------------------------------------------------\n\nexport function TimelineHeader({\n markers,\n hoverInfo,\n}: {\n markers: TimeMarker[];\n hoverInfo?: { fraction: number; label: string } | null;\n}): ReactNode {\n return (\n <div className=\"relative bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-end px-4 pb-1\">\n <div className=\"relative h-full flex-1\">\n {markers.map((m) => (\n <span\n key={`${m.position}-${m.label}`}\n className=\"absolute bottom-1 font-mono text-xs font-normal leading-4 text-gray-900 whitespace-nowrap\"\n style={{ left: `${m.position * 100}%` }}\n >\n {m.label}\n </span>\n ))}\n {hoverInfo && (\n <span\n className=\"absolute top-1 pointer-events-none z-10 font-mono text-[11px] leading-4 text-gray-1000 whitespace-nowrap bg-background-100 border border-gray-alpha-400 rounded px-1 -translate-x-1/2\"\n style={{ left: `${hoverInfo.fraction * 100}%` }}\n >\n {hoverInfo.label}\n </span>\n )}\n </div>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Timeline\n// ---------------------------------------------------------------------------\n\nexport function Timeline({\n spans,\n viewStart,\n viewEnd,\n markers,\n selectedId,\n onSelect,\n hoverFraction,\n altHeld = false,\n}: {\n spans: Span[];\n viewStart: number;\n viewEnd: number;\n markers: TimeMarker[];\n selectedId: string | null;\n onSelect: (spanId: string) => void;\n hoverFraction?: number | null;\n altHeld?: boolean;\n}): ReactNode {\n const containerRef = useRef<HTMLDivElement>(null);\n const [containerWidth, setContainerWidth] = useState(0);\n const viewDuration = viewEnd - viewStart;\n const timelineWidth = Math.max(0, containerWidth - TIMELINE_PADDING_PX * 2);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n const ro = new ResizeObserver(([entry]) => {\n setContainerWidth(entry.contentRect.width);\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const gaps = useMemo(\n () => computeSpanGaps(spans, viewStart, viewEnd),\n [spans, viewStart, viewEnd]\n );\n\n return (\n <div ref={containerRef} className=\"relative h-full overflow-hidden\">\n <div\n aria-hidden\n className=\"absolute inset-y-0 pointer-events-none\"\n style={TIMELINE_INSET_STYLE}\n >\n {markers.map((marker) =>\n // Skip the \"0s\" origin marker since the left edge already implies it.\n Math.abs(marker.value) > 0.000001 ? (\n <div\n key={`${marker.position}-${marker.label}`}\n className=\"absolute top-0 bottom-0 w-px bg-gray-alpha-300\"\n style={{ left: `${marker.position * 100}%` }}\n />\n ) : null\n )}\n </div>\n {hoverFraction != null && (\n <div\n className=\"absolute inset-y-0 pointer-events-none z-10\"\n style={TIMELINE_INSET_STYLE}\n >\n <div\n className=\"absolute top-0 bottom-0 w-px bg-gray-alpha-500\"\n style={{ left: `${hoverFraction * 100}%` }}\n />\n </div>\n )}\n {spans.map((span) => (\n <TimelineBar\n key={span.spanId}\n span={span}\n viewStart={viewStart}\n viewDuration={viewDuration}\n containerWidth={timelineWidth}\n isSelected={selectedId === span.spanId}\n onSelect={onSelect}\n />\n ))}\n {altHeld && (\n <div\n aria-hidden\n className=\"absolute inset-y-0 pointer-events-none\"\n style={TIMELINE_INSET_STYLE}\n >\n {gaps.map((gap) => (\n <DeltaIndicator\n key={gap.rowIndex}\n leftFrac={gap.leftFrac}\n rightFrac={gap.rightFrac}\n label={formatDuration(gap.gapMs, true)}\n rowIndex={gap.rowIndex}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"trace-viewer.d.ts","sourceRoot":"","sources":["../../../src/components/new-trace-viewer/trace-viewer.tsx"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAOf,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAcnD,UAAU,mBAAmB;IAC3B,KAAK,EAAE,KAAK,CAAC;CACd;AAqGD,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE,mBAAmB,GAAG,SAAS,CAMxE"}
1
+ {"version":3,"file":"trace-viewer.d.ts","sourceRoot":"","sources":["../../../src/components/new-trace-viewer/trace-viewer.tsx"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAQf,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAcnD,UAAU,mBAAmB;IAC3B,KAAK,EAAE,KAAK,CAAC;CACd;AA6GD,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE,mBAAmB,GAAG,SAAS,CAMxE"}
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { parseStepName, parseWorkflowName } from '@workflow/utils/parse-name';
4
4
  import { RotateCcw, Search, X, ZoomIn, ZoomOut } from 'lucide-react';
5
5
  import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
6
+ import { useReducedMotion } from '../../hooks/use-reduced-motion';
6
7
  import { ErrorBoundary } from '../error-boundary';
7
8
  import { EntityDetailPanel, } from '../sidebar/entity-detail-panel';
8
9
  import { useSidebarDataOptional } from '../sidebar/sidebar-data-context';
@@ -20,6 +21,7 @@ function useAnimatedViewport(initial) {
20
21
  const animRef = useRef(null);
21
22
  const currentRef = useRef(initial);
22
23
  currentRef.current = viewport;
24
+ const reducedMotion = useReducedMotion();
23
25
  const cancel = useCallback(() => {
24
26
  if (animRef.current) {
25
27
  cancelAnimationFrame(animRef.current.raf);
@@ -28,6 +30,11 @@ function useAnimatedViewport(initial) {
28
30
  }, []);
29
31
  const animateTo = useCallback((target) => {
30
32
  cancel();
33
+ if (reducedMotion) {
34
+ currentRef.current = target;
35
+ setViewportState(target);
36
+ return;
37
+ }
31
38
  const from = currentRef.current;
32
39
  const anim = { raf: 0, from, to: target, start: performance.now() };
33
40
  const tick = () => {
@@ -44,7 +51,7 @@ function useAnimatedViewport(initial) {
44
51
  };
45
52
  animRef.current = anim;
46
53
  anim.raf = requestAnimationFrame(tick);
47
- }, [cancel]);
54
+ }, [cancel, reducedMotion]);
48
55
  const setViewport = useCallback((update) => {
49
56
  cancel();
50
57
  if (typeof update === 'function') {
@@ -343,13 +350,11 @@ function NewTraceViewerContent({ trace }) {
343
350
  data.hookId ??
344
351
  selectedSpan.spanId);
345
352
  }, [selectedSpan?.data, selectedSpan?.resource, selectedSpan?.spanId]);
346
- return (_jsxs("div", { "data-pane": "pane-root", "data-has-detail": activeSpan ? '' : undefined, className: "grid w-full h-full max-h-full grid-cols-[minmax(100px,1fr)] data-[has-detail]:grid-cols-[minmax(100px,1fr)_clamp(280px,420px,100%)]", children: [_jsxs("div", { id: "trace-parent", className: "grid grid-rows-[1fr] h-full min-h-0 overflow-hidden relative bg-background-100", children: [_jsxs(SplitPane, { startHeader: _jsxs("div", { className: "bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-center px-2 gap-1.5", children: [_jsx(Search, { className: "w-3.5 h-3.5 shrink-0 text-gray-800" }), _jsx("input", { id: "trace-viewer-search", name: "trace-viewer-search", type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: "Search spans...", "aria-label": "Search spans", className: "flex-1 min-w-0 bg-transparent text-sm text-gray-1000 placeholder:text-gray-800 outline-none" }), searchQuery && (_jsx("button", { type: "button", onClick: () => setSearchQuery(''), className: "shrink-0 p-0.5 rounded-sm text-gray-800 hover:text-gray-1000 hover:bg-gray-200 transition-colors", children: _jsx(X, { className: "w-3 h-3" }) }))] }), endHeader: _jsx(TimelineHeader, { markers: timeMarkers, hoverInfo: hoverInfo }), children: [_jsx("div", { className: "block overflow-visible", children: _jsx(EventList, { spans: filteredSpans, activeSpanId: activeSpanId, onSelectSpan: handleSelectSpan }) }), _jsx("div", { ref: timelineRef, className: "block min-h-0 overflow-visible relative", onDoubleClick: resetZoom, onMouseMove: handleTimelineMouseMove, onMouseLeave: handleTimelineMouseLeave, children: _jsx(Timeline, { spans: filteredSpans, viewStart: viewport.start, viewEnd: viewport.end, markers: timeMarkers, selectedId: activeSpanId, onSelect: handleSelectSpan, hoverFraction: hoverFraction, altHeld: altHeld }) })] }), _jsxs("div", { className: "absolute right-3 bottom-3 z-[5] flex items-center border border-gray-alpha-400 rounded-lg bg-background-100 shadow-sm overflow-hidden divide-x divide-gray-alpha-400", children: [_jsx("button", { type: "button", className: "flex items-center justify-center w-8 h-8 text-gray-900 cursor-pointer transition-colors duration-[120ms] ease-in-out hover:text-gray-1000 hover:bg-gray-alpha-100", onClick: zoomOut, "aria-label": "Zoom out", children: _jsx(ZoomOut, { className: "w-4 h-4" }) }), _jsx("button", { type: "button", className: "flex items-center justify-center w-8 h-8 text-gray-900 cursor-pointer transition-colors duration-[120ms] ease-in-out hover:text-gray-1000 hover:bg-gray-alpha-100", onClick: resetZoom, "aria-label": "Reset zoom", children: _jsx(RotateCcw, { className: "w-3.5 h-3.5" }) }), _jsx("button", { type: "button", className: "flex items-center justify-center w-8 h-8 text-gray-900 cursor-pointer transition-colors duration-[120ms] ease-in-out hover:text-gray-1000 hover:bg-gray-alpha-100", onClick: zoomIn, "aria-label": "Zoom in", children: _jsx(ZoomIn, { className: "w-4 h-4" }) })] })] }), activeSpan && sidebar ? (_jsxs("aside", { className: "flex flex-col h-full max-h-full bg-background-100 border-l border-gray-alpha-400 overflow-auto", children: [_jsx("div", { className: "flex-shrink-0 px-4 pt-4 pb-3", children: _jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "text-[15px] font-semibold text-gray-1000 truncate block", children: selectedSpanName }), selectedResourceId && (_jsxs("div", { className: "mt-1 flex items-center gap-2", children: [selectedResource && (_jsx("span", { className: `inline-flex items-center rounded-md px-1.5 py-0.5 text-[11px] font-medium leading-none shrink-0 ${selectedResource === 'step'
353
+ return (_jsxs("div", { "data-pane": "pane-root", "data-has-detail": activeSpan ? '' : undefined, className: "grid w-full h-full max-h-full grid-cols-[minmax(100px,1fr)] data-[has-detail]:grid-cols-[minmax(100px,1fr)_clamp(280px,420px,100%)]", children: [_jsxs("div", { id: "trace-parent", className: "grid grid-rows-[1fr] h-full min-h-0 overflow-hidden relative bg-background-100", children: [_jsxs(SplitPane, { startHeader: _jsxs("div", { className: "bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-center px-2 gap-1.5", children: [_jsx(Search, { className: "w-3.5 h-3.5 shrink-0 text-gray-800" }), _jsx("input", { id: "trace-viewer-search", name: "trace-viewer-search", type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: "Search spans...", "aria-label": "Search spans", className: "flex-1 min-w-0 bg-transparent text-sm text-gray-1000 placeholder:text-gray-800 outline-none" }), searchQuery && (_jsx("button", { type: "button", onClick: () => setSearchQuery(''), className: "shrink-0 p-0.5 rounded-sm text-gray-800 hover:text-gray-1000 hover:bg-gray-200 transition-colors", children: _jsx(X, { className: "w-3 h-3" }) }))] }), endHeader: _jsx(TimelineHeader, { markers: timeMarkers, hoverInfo: hoverInfo }), children: [_jsx("div", { className: "block overflow-visible", children: _jsx(EventList, { spans: filteredSpans, activeSpanId: activeSpanId, onSelectSpan: handleSelectSpan }) }), _jsx("div", { ref: timelineRef, className: "block min-h-0 overflow-visible relative", onDoubleClick: resetZoom, onMouseMove: handleTimelineMouseMove, onMouseLeave: handleTimelineMouseLeave, children: _jsx(Timeline, { spans: filteredSpans, viewStart: viewport.start, viewEnd: viewport.end, markers: timeMarkers, selectedId: activeSpanId, onSelect: handleSelectSpan, hoverFraction: hoverFraction, altHeld: altHeld }) })] }), _jsxs("div", { className: "absolute right-3 bottom-3 z-[5] flex items-center border border-gray-alpha-400 rounded-lg bg-background-100 shadow-sm overflow-hidden divide-x divide-gray-alpha-400", children: [_jsx("button", { type: "button", className: "flex items-center justify-center w-8 h-8 text-gray-900 cursor-pointer transition-colors duration-[time:120ms] ease-in-out hover:text-gray-1000 hover:bg-gray-alpha-100", onClick: zoomOut, "aria-label": "Zoom out", children: _jsx(ZoomOut, { className: "w-4 h-4" }) }), _jsx("button", { type: "button", className: "flex items-center justify-center w-8 h-8 text-gray-900 cursor-pointer transition-colors duration-[time:120ms] ease-in-out hover:text-gray-1000 hover:bg-gray-alpha-100", onClick: resetZoom, "aria-label": "Reset zoom", children: _jsx(RotateCcw, { className: "w-3.5 h-3.5" }) }), _jsx("button", { type: "button", className: "flex items-center justify-center w-8 h-8 text-gray-900 cursor-pointer transition-colors duration-[time:120ms] ease-in-out hover:text-gray-1000 hover:bg-gray-alpha-100", onClick: zoomIn, "aria-label": "Zoom in", children: _jsx(ZoomIn, { className: "w-4 h-4" }) })] })] }), activeSpan && sidebar ? (_jsxs("aside", { className: "flex flex-col h-full max-h-full bg-background-100 border-l border-gray-alpha-400 overflow-auto", children: [_jsx("div", { className: "flex-shrink-0 px-4 pt-4 pb-3", children: _jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "text-[15px] font-semibold text-gray-1000 truncate block", children: selectedSpanName }), selectedResourceId && (_jsxs("div", { className: "mt-1 flex items-center gap-2", children: [selectedResource && (_jsx("span", { className: `inline-flex items-center rounded-md px-1.5 py-0.5 text-[11px] font-medium leading-none shrink-0 ${selectedResource === 'step'
347
354
  ? 'bg-green-200 text-green-900'
348
355
  : selectedResource === 'run'
349
356
  ? 'bg-blue-200 text-blue-900'
350
- : selectedResource === 'hook'
351
- ? 'bg-yellow-200 text-yellow-900'
352
- : 'bg-gray-200 text-gray-900'}`, children: selectedResource.charAt(0).toUpperCase() +
357
+ : 'bg-gray-200 text-gray-900'}`, children: selectedResource.charAt(0).toUpperCase() +
353
358
  selectedResource.slice(1) })), _jsxs("div", { className: "flex items-center gap-1 text-[13px] font-mono text-gray-700 min-w-0", title: selectedResourceId, children: [_jsx("span", { className: "truncate", children: selectedResourceId }), _jsx(CopyButton, { copyText: selectedResourceId, ariaLabel: "Copy ID", className: "shrink-0" })] })] }))] }), _jsx("button", { type: "button", className: "p-1 rounded-md text-gray-900 hover:text-gray-1000 hover:bg-gray-alpha-200 transition-colors shrink-0", onClick: clearActiveSpan, children: _jsx(X, { className: "w-4 h-4" }) })] }) }), _jsx("div", { className: "flex-1 overflow-y-auto", children: _jsx(ErrorBoundary, { children: _jsx(EntityDetailPanel, { run: sidebar.run, onStreamClick: sidebar.onStreamClick, onRunClick: sidebar.onRunClick, spanDetailData: sidebar.spanDetailData, spanDetailError: sidebar.spanDetailError, spanDetailLoading: sidebar.spanDetailLoading, onSpanSelect: sidebar.onSpanSelect, onWakeUpSleep: sidebar.onWakeUpSleep, onLoadEventData: sidebar.onLoadEventData, onResolveHook: sidebar.onResolveHook, encryptionKey: sidebar.encryptionKey, onDecrypt: sidebar.onDecrypt, isDecrypting: sidebar.isDecrypting, selectedSpan: selectedSpan }) }) })] })) : activeSpan ? (_jsx(DetailPanel, { span: activeSpan, rootStart: root.startTime, onClose: clearActiveSpan })) : null] }));
354
359
  }
355
360
  //# sourceMappingURL=trace-viewer.js.map