@workflow/web-shared 5.0.0-beta.40 → 5.0.0-beta.42
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/event-list-view.d.ts +10 -2
- package/dist/components/event-list-view.d.ts.map +1 -1
- package/dist/components/event-list-view.js +52 -26
- package/dist/components/sidebar/attributes-block.d.ts.map +1 -1
- package/dist/components/sidebar/attributes-block.js +3 -6
- package/dist/components/sidebar/conversation-view.d.ts.map +1 -1
- package/dist/components/sidebar/conversation-view.js +19 -36
- package/dist/components/sidebar/entity-detail-panel.d.ts +5 -0
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.js +2 -2
- package/dist/components/sidebar/events-list.d.ts +7 -1
- package/dist/components/sidebar/events-list.d.ts.map +1 -1
- package/dist/components/sidebar/events-list.js +6 -5
- package/dist/components/sidebar/resolve-hook-modal.d.ts.map +1 -1
- package/dist/components/sidebar/resolve-hook-modal.js +4 -145
- package/dist/components/sidebar/sidebar-data-context.d.ts +6 -0
- package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -1
- package/dist/components/sidebar/sidebar-data-context.js +1 -1
- package/dist/components/stream-viewer-skeleton.js +6 -6
- package/dist/components/stream-viewer.js +2 -2
- package/dist/components/trace-viewer/components/detail-panel.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/detail-panel.js +2 -1
- package/dist/components/trace-viewer/components/timeline.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/timeline.js +13 -22
- package/dist/components/trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/trace-viewer-skeleton.js +58 -18
- package/dist/components/trace-viewer/icons.js +5 -5
- package/dist/components/trace-viewer/utils.d.ts +6 -10
- package/dist/components/trace-viewer/utils.d.ts.map +1 -1
- package/dist/components/trace-viewer/utils.js +14 -24
- package/dist/components/trace-viewer.d.ts.map +1 -1
- package/dist/components/trace-viewer.js +12 -4
- package/dist/components/ui/context-card.d.ts.map +1 -1
- package/dist/components/ui/context-card.js +9 -21
- package/dist/components/ui/data-inspector.d.ts.map +1 -1
- package/dist/components/ui/data-inspector.js +8 -35
- package/dist/components/ui/duplicate-event-tooltip.d.ts +15 -0
- package/dist/components/ui/duplicate-event-tooltip.d.ts.map +1 -0
- package/dist/components/ui/duplicate-event-tooltip.js +19 -0
- package/dist/components/ui/menu-dropdown.d.ts.map +1 -1
- package/dist/components/ui/menu-dropdown.js +3 -19
- package/dist/components/ui/skeleton.js +2 -2
- package/dist/components/ui/spinner.d.ts.map +1 -1
- package/dist/components/ui/spinner.js +5 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/lib/duplicate-events.d.ts +27 -0
- package/dist/lib/duplicate-events.d.ts.map +1 -0
- package/dist/lib/duplicate-events.js +166 -0
- package/dist/lib/event-materialization.d.ts +15 -2
- package/dist/lib/event-materialization.d.ts.map +1 -1
- package/dist/lib/event-materialization.js +19 -4
- package/dist/lib/trace-builder.d.ts +16 -1
- package/dist/lib/trace-builder.d.ts.map +1 -1
- package/dist/lib/trace-builder.js +23 -4
- package/package.json +4 -4
- package/src/components/event-list-view.tsx +70 -32
- package/src/components/sidebar/attributes-block.tsx +4 -11
- package/src/components/sidebar/conversation-view.tsx +25 -79
- package/src/components/sidebar/entity-detail-panel.tsx +6 -0
- package/src/components/sidebar/events-list.tsx +21 -3
- package/src/components/sidebar/resolve-hook-modal.tsx +23 -186
- package/src/components/sidebar/sidebar-data-context.tsx +6 -0
- package/src/components/stream-viewer-skeleton.tsx +5 -5
- package/src/components/stream-viewer.tsx +1 -1
- package/src/components/trace-viewer/components/detail-panel.tsx +1 -0
- package/src/components/trace-viewer/components/timeline.tsx +21 -39
- package/src/components/trace-viewer/components/trace-viewer-skeleton.tsx +63 -35
- package/src/components/trace-viewer/icons.tsx +4 -4
- package/src/components/trace-viewer/utils.test.ts +5 -7
- package/src/components/trace-viewer/utils.ts +17 -31
- package/src/components/trace-viewer.tsx +15 -3
- package/src/components/ui/context-card.tsx +16 -26
- package/src/components/ui/data-inspector.tsx +20 -65
- package/src/components/ui/duplicate-event-tooltip.tsx +44 -0
- package/src/components/ui/menu-dropdown.tsx +8 -22
- package/src/components/ui/skeleton.tsx +2 -2
- package/src/components/ui/spinner.tsx +4 -8
- package/src/index.ts +4 -0
- package/src/lib/duplicate-events.test.ts +339 -0
- package/src/lib/duplicate-events.ts +188 -0
- package/src/lib/event-materialization.test.ts +117 -0
- package/src/lib/event-materialization.ts +23 -3
- package/src/lib/trace-builder.test.ts +74 -0
- package/src/lib/trace-builder.ts +30 -3
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const WorkflowIcon = () => {
|
|
2
2
|
return (
|
|
3
3
|
<svg
|
|
4
|
+
aria-hidden="true"
|
|
4
5
|
data-testid="geist-icon"
|
|
5
6
|
height="16"
|
|
6
7
|
strokeLinejoin="round"
|
|
7
|
-
style={{ color: 'currentColor' }}
|
|
8
8
|
viewBox="0 0 16 16"
|
|
9
9
|
width="16"
|
|
10
10
|
>
|
|
@@ -19,12 +19,12 @@ const WorkflowIcon = () => {
|
|
|
19
19
|
const WebhookIcon = () => {
|
|
20
20
|
return (
|
|
21
21
|
<svg
|
|
22
|
+
aria-hidden="true"
|
|
22
23
|
data-testid="geist-icon"
|
|
23
24
|
height="16"
|
|
24
25
|
strokeLinejoin="round"
|
|
25
26
|
viewBox="0 0 16 16"
|
|
26
27
|
width="16"
|
|
27
|
-
style={{ color: 'currentColor' }}
|
|
28
28
|
>
|
|
29
29
|
<path
|
|
30
30
|
fillRule="evenodd"
|
|
@@ -39,12 +39,12 @@ const WebhookIcon = () => {
|
|
|
39
39
|
const SleepIcon = () => {
|
|
40
40
|
return (
|
|
41
41
|
<svg
|
|
42
|
+
aria-hidden="true"
|
|
42
43
|
data-testid="geist-icon"
|
|
43
44
|
height="16"
|
|
44
45
|
strokeLinejoin="round"
|
|
45
46
|
viewBox="0 0 16 16"
|
|
46
47
|
width="16"
|
|
47
|
-
style={{ color: 'currentColor' }}
|
|
48
48
|
>
|
|
49
49
|
<path
|
|
50
50
|
fillRule="evenodd"
|
|
@@ -59,12 +59,12 @@ const SleepIcon = () => {
|
|
|
59
59
|
const StepForwardIcon = () => {
|
|
60
60
|
return (
|
|
61
61
|
<svg
|
|
62
|
+
aria-hidden="true"
|
|
62
63
|
data-testid="geist-icon"
|
|
63
64
|
height="16"
|
|
64
65
|
strokeLinejoin="round"
|
|
65
66
|
viewBox="0 0 16 16"
|
|
66
67
|
width="16"
|
|
67
|
-
style={{ color: 'currentColor' }}
|
|
68
68
|
>
|
|
69
69
|
<path
|
|
70
70
|
fillRule="evenodd"
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
computeSpanMarkers,
|
|
8
8
|
computeSpanSegments,
|
|
9
9
|
computeTimeMarkers,
|
|
10
|
-
|
|
10
|
+
getResourceClassNames,
|
|
11
11
|
} from './utils';
|
|
12
12
|
|
|
13
13
|
/** Build a high-res timestamp tuple ([seconds, nanoseconds]) for a given ms. */
|
|
@@ -248,13 +248,11 @@ describe('computeTimeMarkers', () => {
|
|
|
248
248
|
});
|
|
249
249
|
});
|
|
250
250
|
|
|
251
|
-
describe('
|
|
251
|
+
describe('getResourceClassNames', () => {
|
|
252
252
|
it('uses gray for hooks (passive spans), not amber', () => {
|
|
253
|
-
expect(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
errorBg: 'var(--ds-red-200)',
|
|
257
|
-
errorBorder: 'var(--ds-red-500)',
|
|
253
|
+
expect(getResourceClassNames('hook')).toEqual({
|
|
254
|
+
className: 'border-gray-500 bg-gray-200',
|
|
255
|
+
errorClassName: 'border-red-500 bg-red-200',
|
|
258
256
|
});
|
|
259
257
|
});
|
|
260
258
|
});
|
|
@@ -279,55 +279,41 @@ export function computeSpanDelta(
|
|
|
279
279
|
// Resource colors
|
|
280
280
|
// ---------------------------------------------------------------------------
|
|
281
281
|
|
|
282
|
-
export const
|
|
282
|
+
export const RESOURCE_CLASS_NAMES: Record<
|
|
283
283
|
string,
|
|
284
284
|
{
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
errorBg?: string;
|
|
288
|
-
errorBorder?: string;
|
|
285
|
+
className: string;
|
|
286
|
+
errorClassName?: string;
|
|
289
287
|
}
|
|
290
288
|
> = {
|
|
291
289
|
run: {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
errorBg: 'var(--ds-red-200)',
|
|
295
|
-
errorBorder: 'var(--ds-red-500)',
|
|
290
|
+
className: 'border-blue-500 bg-blue-200',
|
|
291
|
+
errorClassName: 'border-red-500 bg-red-200',
|
|
296
292
|
},
|
|
297
293
|
step: {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
errorBg: 'var(--ds-red-200)',
|
|
301
|
-
errorBorder: 'var(--ds-red-500)',
|
|
294
|
+
className: 'border-green-500 bg-green-200',
|
|
295
|
+
errorClassName: 'border-red-500 bg-red-200',
|
|
302
296
|
},
|
|
303
297
|
// Passive spans (hooks) stay gray — matches event-list icons and the minimap.
|
|
304
298
|
hook: {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
errorBg: 'var(--ds-red-200)',
|
|
308
|
-
errorBorder: 'var(--ds-red-500)',
|
|
299
|
+
className: 'border-gray-500 bg-gray-200',
|
|
300
|
+
errorClassName: 'border-red-500 bg-red-200',
|
|
309
301
|
},
|
|
310
302
|
sleep: {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
errorBg: 'var(--ds-red-200)',
|
|
314
|
-
errorBorder: 'var(--ds-red-500)',
|
|
303
|
+
className: 'border-purple-500 bg-purple-200',
|
|
304
|
+
errorClassName: 'border-red-500 bg-red-200',
|
|
315
305
|
},
|
|
316
306
|
default: {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
errorBg: 'var(--ds-red-200)',
|
|
320
|
-
errorBorder: 'var(--ds-red-500)',
|
|
307
|
+
className: 'border-gray-500 bg-gray-200',
|
|
308
|
+
errorClassName: 'border-red-500 bg-red-200',
|
|
321
309
|
},
|
|
322
310
|
};
|
|
323
311
|
|
|
324
|
-
export function
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
errorBg?: string;
|
|
328
|
-
errorBorder?: string;
|
|
312
|
+
export function getResourceClassNames(resource: string): {
|
|
313
|
+
className: string;
|
|
314
|
+
errorClassName?: string;
|
|
329
315
|
} {
|
|
330
|
-
return
|
|
316
|
+
return RESOURCE_CLASS_NAMES[resource] ?? RESOURCE_CLASS_NAMES.default;
|
|
331
317
|
}
|
|
332
318
|
|
|
333
319
|
// ---------------------------------------------------------------------------
|
|
@@ -30,16 +30,28 @@ const TraceViewer = ({
|
|
|
30
30
|
if (!run?.runId) {
|
|
31
31
|
return undefined;
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
// `hasMore` is the only place that knows whether more of the log is still
|
|
34
|
+
// to be fetched, and a repeat can only be told apart from the event it
|
|
35
|
+
// repeats with the whole log in hand.
|
|
36
|
+
return buildTrace(run, events, new Date(), {
|
|
37
|
+
isCompleteHistory: !hasMore,
|
|
38
|
+
});
|
|
34
39
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- `new Date()` is intentionally not a dep
|
|
35
|
-
}, [run, events]);
|
|
40
|
+
}, [run, events, hasMore]);
|
|
41
|
+
|
|
42
|
+
// The sidebar shows one entity's slice of the log, so it takes the trace's
|
|
43
|
+
// answer rather than recomputing one from the slice.
|
|
44
|
+
const sidebarValue = useMemo(
|
|
45
|
+
() => ({ ...sidebarData, duplicateEventIds: trace?.duplicateEventIds }),
|
|
46
|
+
[sidebarData, trace]
|
|
47
|
+
);
|
|
36
48
|
|
|
37
49
|
if (!trace || (loading && events.length === 0)) {
|
|
38
50
|
return <TraceViewerSkeleton />;
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
return (
|
|
42
|
-
<SidebarDataProvider value={
|
|
54
|
+
<SidebarDataProvider value={sidebarValue}>
|
|
43
55
|
<div className="relative w-full h-full flex">
|
|
44
56
|
<TraceViewerComponent
|
|
45
57
|
trace={trace}
|
|
@@ -267,17 +267,17 @@ export function ContextCardProvider({
|
|
|
267
267
|
ref={ref}
|
|
268
268
|
>
|
|
269
269
|
<div
|
|
270
|
-
className=
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
270
|
+
className={cn(
|
|
271
|
+
'min-w-max transition-all duration-150 ease-[easing-function:cubic-bezier(0.3,_0.57,_0.07,_0.95)] will-change-[transform,width,height] motion-reduce:!transition-none',
|
|
272
|
+
visible ? 'opacity-100' : 'opacity-0'
|
|
273
|
+
)}
|
|
274
274
|
>
|
|
275
275
|
<div
|
|
276
276
|
className={cn(
|
|
277
277
|
'absolute bg-background-100 bg-clip-padding rounded-[6px] overflow-visible pointer-events-none z-[1000000] shadow-[var(--ds-shadow-tooltip),0_0_0_1px_var(--ds-background-100)] w-fit [--context-card-tip-stroke:#DBDBDB] dark-theme:[--context-card-tip-stroke:#252525]',
|
|
278
278
|
skipTransition
|
|
279
279
|
? '!transition-none'
|
|
280
|
-
: 'transition-[transform,width,height] duration-
|
|
280
|
+
: 'transition-[transform,width,height] duration-[250ms] ease-[easing-function:cubic-bezier(0.29,0.31,0.05,0.96)] will-change-[transform,width,height] motion-reduce:!transition-none'
|
|
281
281
|
)}
|
|
282
282
|
data-skip-transition={skipTransition}
|
|
283
283
|
style={{
|
|
@@ -301,7 +301,7 @@ export function ContextCardProvider({
|
|
|
301
301
|
},
|
|
302
302
|
skipTransition
|
|
303
303
|
? '!transition-none'
|
|
304
|
-
: 'transition-[transform,width,height] duration-
|
|
304
|
+
: 'transition-[transform,width,height] duration-[250ms] ease-[easing-function:cubic-bezier(0.29,0.31,0.05,0.96)] will-change-[transform,width,height] motion-reduce:!transition-none'
|
|
305
305
|
)}
|
|
306
306
|
style={{
|
|
307
307
|
...(activeBounds.side === 'top' ||
|
|
@@ -328,12 +328,7 @@ export function ContextCardProvider({
|
|
|
328
328
|
<path
|
|
329
329
|
d="M15 -0.5V0.5H12.9834L12.8184 0.508789C12.4377 0.550822 12.0853 0.738056 11.8359 1.03418L8.53027 4.95996C7.73114 5.90893 6.26886 5.90892 5.46973 4.95996L2.16406 1.03418C1.87905 0.695733 1.45907 0.5 1.0166 0.5H-1V-0.5H15Z"
|
|
330
330
|
fill="var(--ds-background-100)"
|
|
331
|
-
|
|
332
|
-
fill: 'var(--ds-background-100)',
|
|
333
|
-
fillOpacity: 1,
|
|
334
|
-
stroke: 'var(--context-card-tip-stroke)',
|
|
335
|
-
strokeOpacity: 1,
|
|
336
|
-
}}
|
|
331
|
+
className="fill-background-100 stroke-[var(--context-card-tip-stroke)]"
|
|
337
332
|
/>
|
|
338
333
|
</g>
|
|
339
334
|
<defs>
|
|
@@ -342,7 +337,7 @@ export function ContextCardProvider({
|
|
|
342
337
|
width="14"
|
|
343
338
|
height="7"
|
|
344
339
|
fill="white"
|
|
345
|
-
|
|
340
|
+
className="fill-background-100"
|
|
346
341
|
/>
|
|
347
342
|
</clipPath>
|
|
348
343
|
</defs>
|
|
@@ -748,30 +743,25 @@ export function ContextCardTrigger({
|
|
|
748
743
|
? createPortal(
|
|
749
744
|
<div
|
|
750
745
|
className={cn(
|
|
751
|
-
'absolute top-0 p-3 left-0 max-w-max transition-[transform,width,height] duration-
|
|
752
|
-
skipTransition && '!transition-none'
|
|
746
|
+
'absolute top-0 p-3 left-0 max-w-max transition-[transform,width,height] duration-[250ms] ease-[easing-function:cubic-bezier(0.29,0.31,0.05,0.96)] will-change-[transform,width,height] motion-reduce:!transition-none',
|
|
747
|
+
skipTransition && '!transition-none',
|
|
748
|
+
noPadding && 'p-0',
|
|
749
|
+
rootVisible && visible && !ignoreCardPointerEvents
|
|
750
|
+
? 'pointer-events-auto'
|
|
751
|
+
: 'pointer-events-none'
|
|
753
752
|
)}
|
|
754
753
|
key={id}
|
|
755
754
|
ref={contentRef}
|
|
756
|
-
style={{
|
|
757
|
-
...(noPadding ? { padding: 0 } : {}),
|
|
758
|
-
pointerEvents:
|
|
759
|
-
rootVisible && visible && !ignoreCardPointerEvents
|
|
760
|
-
? 'all'
|
|
761
|
-
: 'none',
|
|
762
|
-
}}
|
|
763
755
|
>
|
|
764
756
|
<div
|
|
765
757
|
className={cn(
|
|
766
758
|
'min-w-max transition-all duration-150 ease-[easing-function:cubic-bezier(0.3,_0.57,_0.07,_0.95)] will-change-[transform,width,height] motion-reduce:!transition-none',
|
|
767
759
|
skipTransition
|
|
768
760
|
? '!transition-none'
|
|
769
|
-
: 'transition-[transform,width,height] duration-
|
|
761
|
+
: 'transition-[transform,width,height] duration-[250ms] ease-[easing-function:cubic-bezier(0.29,0.31,0.05,0.96)] will-change-[transform,width,height] motion-reduce:!transition-none',
|
|
762
|
+
visible ? 'opacity-100' : 'opacity-0'
|
|
770
763
|
)}
|
|
771
764
|
ref={contentMeasureRef}
|
|
772
|
-
style={{
|
|
773
|
-
opacity: Number(visible),
|
|
774
|
-
}}
|
|
775
765
|
>
|
|
776
766
|
{content}
|
|
777
767
|
</div>
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
useRef,
|
|
26
26
|
useState,
|
|
27
27
|
} from 'react';
|
|
28
|
+
import { cn } from '../../lib/cn';
|
|
28
29
|
import { ENCRYPTED_DISPLAY_NAME } from '../../lib/hydration';
|
|
29
30
|
import {
|
|
30
31
|
type DecodedStreamChunkSource,
|
|
@@ -182,16 +183,8 @@ function EncryptedInlineLabel() {
|
|
|
182
183
|
);
|
|
183
184
|
}
|
|
184
185
|
return (
|
|
185
|
-
<span
|
|
186
|
-
<Lock
|
|
187
|
-
className="h-3 w-3"
|
|
188
|
-
style={{
|
|
189
|
-
display: 'inline',
|
|
190
|
-
verticalAlign: 'middle',
|
|
191
|
-
marginRight: '3px',
|
|
192
|
-
marginTop: '-1px',
|
|
193
|
-
}}
|
|
194
|
-
/>
|
|
186
|
+
<span className="text-gray-600 italic">
|
|
187
|
+
<Lock className="-mt-px mr-[3px] inline h-3 w-3 align-middle" />
|
|
195
188
|
Encrypted
|
|
196
189
|
</span>
|
|
197
190
|
);
|
|
@@ -199,18 +192,10 @@ function EncryptedInlineLabel() {
|
|
|
199
192
|
|
|
200
193
|
function StreamRefInline({ streamRef }: { streamRef: StreamRef }) {
|
|
201
194
|
const onStreamClick = useContext(StreamClickContext);
|
|
202
|
-
const [hovered, setHovered] = useState(false);
|
|
203
195
|
return (
|
|
204
196
|
<button
|
|
205
197
|
type="button"
|
|
206
|
-
className="inline-flex items-center gap-1 px-1.5 py-0.5
|
|
207
|
-
style={{
|
|
208
|
-
backgroundColor: hovered ? 'var(--ds-blue-200)' : 'var(--ds-blue-100)',
|
|
209
|
-
color: 'var(--ds-blue-900)',
|
|
210
|
-
border: '1px solid var(--ds-blue-300)',
|
|
211
|
-
}}
|
|
212
|
-
onMouseEnter={() => setHovered(true)}
|
|
213
|
-
onMouseLeave={() => setHovered(false)}
|
|
198
|
+
className="inline-flex cursor-pointer items-center gap-1 rounded border border-blue-300 bg-blue-100 px-1.5 py-0.5 font-mono text-[10px] text-blue-900 underline decoration-transparent transition-colors hover:bg-blue-200"
|
|
214
199
|
onClick={(e) => {
|
|
215
200
|
e.stopPropagation();
|
|
216
201
|
onStreamClick?.(streamRef.streamId);
|
|
@@ -225,20 +210,10 @@ function StreamRefInline({ streamRef }: { streamRef: StreamRef }) {
|
|
|
225
210
|
|
|
226
211
|
function RunRefInline({ runRef }: { runRef: RunRef }) {
|
|
227
212
|
const onRunClick = useContext(RunClickContext);
|
|
228
|
-
const [hovered, setHovered] = useState(false);
|
|
229
213
|
return (
|
|
230
214
|
<button
|
|
231
215
|
type="button"
|
|
232
|
-
className="inline-flex items-center gap-1 px-1.5 py-0.5
|
|
233
|
-
style={{
|
|
234
|
-
backgroundColor: hovered
|
|
235
|
-
? 'var(--ds-purple-200)'
|
|
236
|
-
: 'var(--ds-purple-100)',
|
|
237
|
-
color: 'var(--ds-purple-900)',
|
|
238
|
-
border: '1px solid var(--ds-purple-300)',
|
|
239
|
-
}}
|
|
240
|
-
onMouseEnter={() => setHovered(true)}
|
|
241
|
-
onMouseLeave={() => setHovered(false)}
|
|
216
|
+
className="inline-flex cursor-pointer items-center gap-1 rounded border border-purple-300 bg-purple-100 px-1.5 py-0.5 font-mono text-[10px] text-purple-900 underline decoration-transparent transition-colors hover:bg-purple-200"
|
|
242
217
|
onClick={(e) => {
|
|
243
218
|
e.stopPropagation();
|
|
244
219
|
onRunClick?.(runRef.runId);
|
|
@@ -267,10 +242,7 @@ function DecodedBytesChunk({
|
|
|
267
242
|
{selectedView === 'decoded' ? (
|
|
268
243
|
<div className="min-w-0">
|
|
269
244
|
{typeof parsed === 'string' ? (
|
|
270
|
-
<span
|
|
271
|
-
className="whitespace-pre-wrap break-words"
|
|
272
|
-
style={{ color: 'var(--ds-gray-1000)' }}
|
|
273
|
-
>
|
|
245
|
+
<span className="whitespace-pre-wrap break-words text-gray-1000">
|
|
274
246
|
{deserializeChunkText(parsed)}
|
|
275
247
|
</span>
|
|
276
248
|
) : (
|
|
@@ -282,20 +254,15 @@ function DecodedBytesChunk({
|
|
|
282
254
|
)}
|
|
283
255
|
<div className="mt-2 flex">
|
|
284
256
|
<div
|
|
285
|
-
className="inline-flex overflow-hidden rounded border"
|
|
286
|
-
style={{ borderColor: 'var(--ds-gray-400)' }}
|
|
257
|
+
className="inline-flex overflow-hidden rounded border border-gray-400"
|
|
287
258
|
title={`${source.type} decoded as ${source.encoding.toUpperCase()} text. Switch to Bytes to inspect the summarized raw value.`}
|
|
288
259
|
>
|
|
289
260
|
<button
|
|
290
261
|
type="button"
|
|
291
|
-
className=
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
? 'var(--ds-gray-200)'
|
|
296
|
-
: 'var(--ds-gray-100)',
|
|
297
|
-
color: 'var(--ds-gray-900)',
|
|
298
|
-
}}
|
|
262
|
+
className={cn(
|
|
263
|
+
'h-5 px-1.5 font-medium text-[10px] text-gray-900',
|
|
264
|
+
selectedView === 'decoded' ? 'bg-gray-200' : 'bg-gray-100'
|
|
265
|
+
)}
|
|
299
266
|
onClick={() => setSelectedView('decoded')}
|
|
300
267
|
aria-pressed={selectedView === 'decoded'}
|
|
301
268
|
aria-label="Show decoded text"
|
|
@@ -304,15 +271,10 @@ function DecodedBytesChunk({
|
|
|
304
271
|
</button>
|
|
305
272
|
<button
|
|
306
273
|
type="button"
|
|
307
|
-
className=
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
selectedView === 'bytes'
|
|
312
|
-
? 'var(--ds-gray-200)'
|
|
313
|
-
: 'var(--ds-gray-100)',
|
|
314
|
-
color: 'var(--ds-gray-900)',
|
|
315
|
-
}}
|
|
274
|
+
className={cn(
|
|
275
|
+
'h-5 border-gray-400 border-l px-1.5 font-medium text-[10px] text-gray-900',
|
|
276
|
+
selectedView === 'bytes' ? 'bg-gray-200' : 'bg-gray-100'
|
|
277
|
+
)}
|
|
316
278
|
onClick={() => setSelectedView('bytes')}
|
|
317
279
|
aria-pressed={selectedView === 'bytes'}
|
|
318
280
|
aria-label="Show raw bytes summary"
|
|
@@ -338,23 +300,19 @@ function DecodedBytesInspector({
|
|
|
338
300
|
<div className="font-mono">
|
|
339
301
|
<button
|
|
340
302
|
type="button"
|
|
341
|
-
className="flex max-w-full items-start gap-1 text-left"
|
|
342
|
-
style={{ color: 'var(--ds-gray-1000)' }}
|
|
303
|
+
className="flex max-w-full items-start gap-1 text-left text-gray-1000"
|
|
343
304
|
onClick={() => setExpanded((value) => !value)}
|
|
344
305
|
title={`${source.type} decoded as ${source.encoding.toUpperCase()} text`}
|
|
345
306
|
>
|
|
346
|
-
<span className="select-none
|
|
307
|
+
<span className="select-none text-gray-700">
|
|
347
308
|
{expanded ? '▼' : '▶'}
|
|
348
309
|
</span>
|
|
349
310
|
<span className="min-w-0 break-words">{source.rawSummary}</span>
|
|
350
311
|
</button>
|
|
351
312
|
{expanded && (
|
|
352
313
|
<div className="mt-1 pl-5">
|
|
353
|
-
<span
|
|
354
|
-
<span
|
|
355
|
-
className="whitespace-pre-wrap break-words"
|
|
356
|
-
style={{ color: 'var(--ds-green-900)' }}
|
|
357
|
-
>
|
|
314
|
+
<span className="text-gray-700">decoded: </span>
|
|
315
|
+
<span className="whitespace-pre-wrap break-words text-green-900">
|
|
358
316
|
{JSON.stringify(decodedText)}
|
|
359
317
|
</span>
|
|
360
318
|
</div>
|
|
@@ -373,10 +331,7 @@ function BytesDisplayValue({ display }: { display: BytesDisplay }) {
|
|
|
373
331
|
);
|
|
374
332
|
}
|
|
375
333
|
return (
|
|
376
|
-
<span
|
|
377
|
-
className="whitespace-pre-wrap break-words"
|
|
378
|
-
style={{ color: 'var(--ds-gray-1000)' }}
|
|
379
|
-
>
|
|
334
|
+
<span className="whitespace-pre-wrap break-words text-gray-1000">
|
|
380
335
|
{display.text}
|
|
381
336
|
</span>
|
|
382
337
|
);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
import { DUPLICATE_EVENT_MESSAGE } from '../../lib/duplicate-events';
|
|
5
|
+
import {
|
|
6
|
+
Tooltip,
|
|
7
|
+
TooltipContent,
|
|
8
|
+
TooltipProvider,
|
|
9
|
+
TooltipTrigger,
|
|
10
|
+
} from './tooltip';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Explains why an event is shown greyed out: it repeats a class the log
|
|
14
|
+
* already records for the same entity, after that entity finished.
|
|
15
|
+
*
|
|
16
|
+
* Renders `children` untouched when `isDuplicate` is false, so a call site can
|
|
17
|
+
* wrap an event label unconditionally. Mounts its own {@link TooltipProvider}
|
|
18
|
+
* so it works in the sidebar and the events table alike; nesting one inside an
|
|
19
|
+
* existing provider is harmless.
|
|
20
|
+
*/
|
|
21
|
+
export function DuplicateEventTooltip({
|
|
22
|
+
isDuplicate = false,
|
|
23
|
+
children,
|
|
24
|
+
}: {
|
|
25
|
+
isDuplicate?: boolean;
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
}): ReactNode {
|
|
28
|
+
if (!isDuplicate) return children;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<TooltipProvider delayDuration={300}>
|
|
32
|
+
<Tooltip>
|
|
33
|
+
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
|
34
|
+
<TooltipContent
|
|
35
|
+
className="max-w-[264px] leading-snug"
|
|
36
|
+
collisionPadding={8}
|
|
37
|
+
side="top"
|
|
38
|
+
>
|
|
39
|
+
{DUPLICATE_EVENT_MESSAGE}
|
|
40
|
+
</TooltipContent>
|
|
41
|
+
</Tooltip>
|
|
42
|
+
</TooltipProvider>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { cn } from '../../lib/cn';
|
|
4
5
|
|
|
5
6
|
const STYLES = `.wf-menu-btn{appearance:none;-webkit-appearance:none;border:none;display:inline-flex;align-items:center;justify-content:center;height:40px;padding:0 12px;border-radius:6px;font-size:14px;font-weight:500;line-height:20px;color:var(--ds-gray-1000);background:var(--ds-background-100);box-shadow:0 0 0 1px var(--ds-gray-400);cursor:pointer;white-space:nowrap;transition:background 150ms}.wf-menu-btn:hover{background:var(--ds-gray-alpha-200)}.wf-menu-item{appearance:none;-webkit-appearance:none;border:none;display:flex;align-items:center;width:100%;height:40px;padding:0 8px;border-radius:6px;font-size:14px;color:var(--ds-gray-1000);background:transparent;cursor:pointer;transition:background 150ms}.wf-menu-item:hover{background:var(--ds-gray-alpha-100)}`;
|
|
6
7
|
|
|
@@ -41,7 +42,7 @@ export function MenuDropdown<T extends string = string>({
|
|
|
41
42
|
}, [open]);
|
|
42
43
|
|
|
43
44
|
return (
|
|
44
|
-
<div ref={ref}
|
|
45
|
+
<div ref={ref} className="relative shrink-0">
|
|
45
46
|
<style dangerouslySetInnerHTML={{ __html: STYLES }} />
|
|
46
47
|
|
|
47
48
|
<button
|
|
@@ -55,11 +56,7 @@ export function MenuDropdown<T extends string = string>({
|
|
|
55
56
|
height={16}
|
|
56
57
|
viewBox="0 0 16 16"
|
|
57
58
|
fill="none"
|
|
58
|
-
|
|
59
|
-
marginLeft: 16,
|
|
60
|
-
marginRight: -4,
|
|
61
|
-
color: 'var(--ds-gray-900)',
|
|
62
|
-
}}
|
|
59
|
+
className="-mr-1 ml-4 text-gray-900"
|
|
63
60
|
>
|
|
64
61
|
<path
|
|
65
62
|
d="M4.5 6L8 9.5L11.5 6"
|
|
@@ -73,18 +70,7 @@ export function MenuDropdown<T extends string = string>({
|
|
|
73
70
|
|
|
74
71
|
{open && (
|
|
75
72
|
<div
|
|
76
|
-
|
|
77
|
-
position: 'absolute',
|
|
78
|
-
right: 0,
|
|
79
|
-
top: '100%',
|
|
80
|
-
marginTop: 4,
|
|
81
|
-
minWidth: 140,
|
|
82
|
-
padding: 4,
|
|
83
|
-
borderRadius: 12,
|
|
84
|
-
background: 'var(--ds-background-100)',
|
|
85
|
-
boxShadow: 'var(--ds-shadow-menu, var(--ds-shadow-medium))',
|
|
86
|
-
zIndex: 2001,
|
|
87
|
-
}}
|
|
73
|
+
className="absolute top-full right-0 z-[2001] mt-1 min-w-[140px] bg-background-100 p-1 material-menu"
|
|
88
74
|
role="menu"
|
|
89
75
|
>
|
|
90
76
|
{options.map((option) => (
|
|
@@ -92,10 +78,10 @@ export function MenuDropdown<T extends string = string>({
|
|
|
92
78
|
key={option.value}
|
|
93
79
|
type="button"
|
|
94
80
|
role="menuitem"
|
|
95
|
-
className=
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
81
|
+
className={cn(
|
|
82
|
+
'wf-menu-item',
|
|
83
|
+
option.value === value ? 'font-medium' : 'font-normal'
|
|
84
|
+
)}
|
|
99
85
|
onClick={() => {
|
|
100
86
|
onChange(option.value);
|
|
101
87
|
setOpen(false);
|
|
@@ -44,9 +44,8 @@ export function Spinner({
|
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
46
|
<span
|
|
47
|
+
className="relative inline-flex"
|
|
47
48
|
style={{
|
|
48
|
-
display: 'inline-flex',
|
|
49
|
-
position: 'relative',
|
|
50
49
|
width: size,
|
|
51
50
|
height: size,
|
|
52
51
|
}}
|
|
@@ -55,18 +54,15 @@ export function Spinner({
|
|
|
55
54
|
{config.delays.map((delay, i) => (
|
|
56
55
|
<span
|
|
57
56
|
key={delay}
|
|
57
|
+
className="absolute top-1/2 left-1/2 rounded-[1px] bg-gray-700 [animation-iteration-count:infinite] [animation-name:wf-spinner-fade] [animation-timing-function:linear]"
|
|
58
58
|
style={{
|
|
59
|
-
position: 'absolute',
|
|
60
|
-
left: '50%',
|
|
61
|
-
top: '50%',
|
|
62
59
|
width: config.lineW,
|
|
63
60
|
height: config.lineH,
|
|
64
61
|
marginLeft: -config.lineW / 2,
|
|
65
62
|
marginTop: -config.lineH / 2,
|
|
66
|
-
|
|
67
|
-
backgroundColor: color ?? 'var(--ds-gray-700)',
|
|
63
|
+
backgroundColor: color,
|
|
68
64
|
transform: `rotate(${i * config.angle}deg) translate(${size * 0.36}px)`,
|
|
69
|
-
|
|
65
|
+
animationDuration: `${config.duration}ms`,
|
|
70
66
|
animationDelay: `${delay}ms`,
|
|
71
67
|
}}
|
|
72
68
|
/>
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,10 @@ export {
|
|
|
10
10
|
stepEventsToStepEntity,
|
|
11
11
|
waitEventsToWaitEntity,
|
|
12
12
|
} from './components/workflow-traces/trace-span-construction';
|
|
13
|
+
export {
|
|
14
|
+
DUPLICATE_EVENT_MESSAGE,
|
|
15
|
+
findDuplicateEventIds,
|
|
16
|
+
} from './lib/duplicate-events';
|
|
13
17
|
export type { EventAnalysis } from './lib/event-analysis';
|
|
14
18
|
export {
|
|
15
19
|
analyzeEvents,
|