@workflow/web-shared 5.0.0-beta.41 → 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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { Send, X } from 'lucide-react';
|
|
4
4
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { cn } from '../../lib/cn';
|
|
5
6
|
|
|
6
7
|
interface ResolveHookModalProps {
|
|
7
8
|
/** Whether the modal is open */
|
|
@@ -110,60 +111,24 @@ export function ResolveHookModal({
|
|
|
110
111
|
|
|
111
112
|
return (
|
|
112
113
|
<div
|
|
113
|
-
|
|
114
|
-
position: 'fixed',
|
|
115
|
-
inset: 0,
|
|
116
|
-
zIndex: 50,
|
|
117
|
-
display: 'flex',
|
|
118
|
-
alignItems: 'center',
|
|
119
|
-
justifyContent: 'center',
|
|
120
|
-
}}
|
|
114
|
+
className="fixed inset-0 z-50 flex items-center justify-center"
|
|
121
115
|
role="dialog"
|
|
122
116
|
aria-modal="true"
|
|
123
117
|
aria-labelledby="resolve-hook-modal-title"
|
|
124
118
|
>
|
|
125
119
|
{/* Backdrop — matches Geist dialog ::backdrop */}
|
|
126
120
|
<div
|
|
127
|
-
|
|
128
|
-
position: 'absolute',
|
|
129
|
-
inset: 0,
|
|
130
|
-
background: 'rgba(0, 0, 0, 0.7)',
|
|
131
|
-
}}
|
|
121
|
+
className="absolute inset-0 bg-black/70"
|
|
132
122
|
onClick={isSubmitting ? undefined : onClose}
|
|
133
123
|
/>
|
|
134
124
|
|
|
135
125
|
{/* Modal card — matches Geist dialog.geist-dialog */}
|
|
136
|
-
<div
|
|
137
|
-
style={{
|
|
138
|
-
position: 'relative',
|
|
139
|
-
zIndex: 10,
|
|
140
|
-
width: 480,
|
|
141
|
-
maxWidth: 'calc(100% - 32px)',
|
|
142
|
-
borderRadius: 12,
|
|
143
|
-
border: 'none',
|
|
144
|
-
boxShadow: 'var(--ds-shadow-menu)',
|
|
145
|
-
background: 'var(--ds-background-100)',
|
|
146
|
-
color: 'var(--ds-gray-1000)',
|
|
147
|
-
overflow: 'hidden',
|
|
148
|
-
}}
|
|
149
|
-
>
|
|
126
|
+
<div className="relative z-10 w-[480px] max-w-[calc(100%_-_32px)] overflow-hidden !border-none bg-background-100 text-gray-1000 material-menu">
|
|
150
127
|
{/* Header */}
|
|
151
|
-
<div
|
|
152
|
-
style={{
|
|
153
|
-
display: 'flex',
|
|
154
|
-
alignItems: 'center',
|
|
155
|
-
justifyContent: 'space-between',
|
|
156
|
-
padding: '16px 24px',
|
|
157
|
-
}}
|
|
158
|
-
>
|
|
128
|
+
<div className="flex items-center justify-between px-6 py-4">
|
|
159
129
|
<h2
|
|
160
130
|
id="resolve-hook-modal-title"
|
|
161
|
-
|
|
162
|
-
margin: 0,
|
|
163
|
-
fontSize: 16,
|
|
164
|
-
fontWeight: 600,
|
|
165
|
-
color: 'var(--ds-gray-1000)',
|
|
166
|
-
}}
|
|
131
|
+
className="m-0 font-semibold text-base text-gray-1000"
|
|
167
132
|
>
|
|
168
133
|
Resolve Hook
|
|
169
134
|
</h2>
|
|
@@ -172,66 +137,24 @@ export function ResolveHookModal({
|
|
|
172
137
|
onClick={onClose}
|
|
173
138
|
disabled={isSubmitting}
|
|
174
139
|
aria-label="Close modal"
|
|
175
|
-
|
|
176
|
-
display: 'flex',
|
|
177
|
-
alignItems: 'center',
|
|
178
|
-
justifyContent: 'center',
|
|
179
|
-
padding: 4,
|
|
180
|
-
borderRadius: 6,
|
|
181
|
-
border: 'none',
|
|
182
|
-
background: 'transparent',
|
|
183
|
-
color: 'var(--ds-gray-900)',
|
|
184
|
-
cursor: isSubmitting ? 'not-allowed' : 'pointer',
|
|
185
|
-
opacity: isSubmitting ? 0.5 : 1,
|
|
186
|
-
transition: 'background 0.15s',
|
|
187
|
-
}}
|
|
188
|
-
onMouseEnter={(e) => {
|
|
189
|
-
e.currentTarget.style.background = 'var(--ds-gray-alpha-200)';
|
|
190
|
-
}}
|
|
191
|
-
onMouseLeave={(e) => {
|
|
192
|
-
e.currentTarget.style.background = 'transparent';
|
|
193
|
-
}}
|
|
140
|
+
className="flex cursor-pointer items-center justify-center rounded-md !border-none !bg-transparent p-1 text-gray-900 transition-colors hover:!bg-gray-alpha-200 disabled:cursor-not-allowed disabled:opacity-50"
|
|
194
141
|
>
|
|
195
|
-
<X
|
|
142
|
+
<X className="size-4" />
|
|
196
143
|
</button>
|
|
197
144
|
</div>
|
|
198
145
|
|
|
199
146
|
{/* Body */}
|
|
200
147
|
<form onSubmit={handleSubmit}>
|
|
201
|
-
<div
|
|
148
|
+
<div className="px-6 pb-4">
|
|
202
149
|
<label
|
|
203
150
|
htmlFor="json-payload"
|
|
204
|
-
|
|
205
|
-
display: 'block',
|
|
206
|
-
fontSize: 14,
|
|
207
|
-
fontWeight: 500,
|
|
208
|
-
marginBottom: 6,
|
|
209
|
-
color: 'var(--ds-gray-1000)',
|
|
210
|
-
}}
|
|
151
|
+
className="mb-1.5 block font-medium text-gray-1000 text-sm"
|
|
211
152
|
>
|
|
212
153
|
JSON Payload
|
|
213
154
|
</label>
|
|
214
|
-
<p
|
|
215
|
-
style={{
|
|
216
|
-
fontSize: 13,
|
|
217
|
-
marginBottom: 12,
|
|
218
|
-
marginTop: 0,
|
|
219
|
-
color: 'var(--ds-gray-900)',
|
|
220
|
-
lineHeight: 1.5,
|
|
221
|
-
}}
|
|
222
|
-
>
|
|
155
|
+
<p className="mt-0 mb-3 text-[13px] text-gray-900 leading-[1.5]">
|
|
223
156
|
Enter a JSON value to send to the hook. Leave empty to send{' '}
|
|
224
|
-
<code
|
|
225
|
-
style={{
|
|
226
|
-
padding: '2px 6px',
|
|
227
|
-
borderRadius: 4,
|
|
228
|
-
fontSize: 12,
|
|
229
|
-
fontFamily:
|
|
230
|
-
'var(--font-mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace)',
|
|
231
|
-
background: 'var(--ds-gray-alpha-200)',
|
|
232
|
-
color: 'var(--ds-gray-1000)',
|
|
233
|
-
}}
|
|
234
|
-
>
|
|
157
|
+
<code className="rounded bg-gray-alpha-200 px-1.5 py-0.5 font-mono text-gray-1000 text-xs">
|
|
235
158
|
null
|
|
236
159
|
</code>
|
|
237
160
|
.
|
|
@@ -247,57 +170,17 @@ export function ResolveHookModal({
|
|
|
247
170
|
onKeyDown={handleKeyDown}
|
|
248
171
|
disabled={isSubmitting}
|
|
249
172
|
placeholder='{"key": "value"}'
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
fontFamily:
|
|
255
|
-
'var(--font-mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace)',
|
|
256
|
-
fontSize: 13,
|
|
257
|
-
lineHeight: 1.5,
|
|
258
|
-
borderRadius: 8,
|
|
259
|
-
border: `1px solid ${parseError ? 'var(--ds-red-700)' : 'var(--ds-gray-alpha-400)'}`,
|
|
260
|
-
color: 'var(--ds-gray-1000)',
|
|
261
|
-
background: 'var(--ds-background-100)',
|
|
262
|
-
outline: 'none',
|
|
263
|
-
resize: 'none',
|
|
264
|
-
opacity: isSubmitting ? 0.5 : 1,
|
|
265
|
-
cursor: isSubmitting ? 'not-allowed' : 'text',
|
|
266
|
-
boxSizing: 'border-box',
|
|
267
|
-
}}
|
|
173
|
+
className={cn(
|
|
174
|
+
'box-border h-40 w-full resize-none rounded-lg border bg-background-100 px-3 py-2 font-mono text-[13px] text-gray-1000 leading-[1.5] outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
175
|
+
parseError ? 'border-red-700' : 'border-gray-alpha-400'
|
|
176
|
+
)}
|
|
268
177
|
/>
|
|
269
178
|
{parseError && (
|
|
270
|
-
<p
|
|
271
|
-
style={{
|
|
272
|
-
marginTop: 8,
|
|
273
|
-
fontSize: 13,
|
|
274
|
-
color: 'var(--ds-red-900)',
|
|
275
|
-
margin: '8px 0 0',
|
|
276
|
-
}}
|
|
277
|
-
>
|
|
278
|
-
{parseError}
|
|
279
|
-
</p>
|
|
179
|
+
<p className="mt-2 mb-0 text-[13px] text-red-900">{parseError}</p>
|
|
280
180
|
)}
|
|
281
|
-
<p
|
|
282
|
-
style={{
|
|
283
|
-
marginTop: 8,
|
|
284
|
-
fontSize: 12,
|
|
285
|
-
color: 'var(--ds-gray-800)',
|
|
286
|
-
margin: '8px 0 0',
|
|
287
|
-
}}
|
|
288
|
-
>
|
|
181
|
+
<p className="mt-2 mb-0 text-gray-800 text-xs">
|
|
289
182
|
Press{' '}
|
|
290
|
-
<kbd
|
|
291
|
-
style={{
|
|
292
|
-
padding: '2px 5px',
|
|
293
|
-
borderRadius: 4,
|
|
294
|
-
fontSize: 11,
|
|
295
|
-
fontFamily:
|
|
296
|
-
'var(--font-mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace)',
|
|
297
|
-
background: 'var(--ds-gray-alpha-200)',
|
|
298
|
-
border: '1px solid var(--ds-gray-alpha-400)',
|
|
299
|
-
}}
|
|
300
|
-
>
|
|
183
|
+
<kbd className="rounded border border-gray-alpha-400 bg-gray-alpha-200 px-[5px] py-0.5 font-mono text-[11px]">
|
|
301
184
|
{isMacPlatform ? '⌘' : 'Ctrl'}
|
|
302
185
|
+Enter
|
|
303
186
|
</kbd>{' '}
|
|
@@ -306,38 +189,12 @@ export function ResolveHookModal({
|
|
|
306
189
|
</div>
|
|
307
190
|
|
|
308
191
|
{/* Footer */}
|
|
309
|
-
<div
|
|
310
|
-
style={{
|
|
311
|
-
display: 'flex',
|
|
312
|
-
alignItems: 'center',
|
|
313
|
-
justifyContent: 'flex-end',
|
|
314
|
-
gap: 8,
|
|
315
|
-
padding: '12px 24px',
|
|
316
|
-
borderTop: '1px solid var(--ds-gray-alpha-400)',
|
|
317
|
-
}}
|
|
318
|
-
>
|
|
192
|
+
<div className="flex items-center justify-end gap-2 border-gray-alpha-400 border-t px-6 py-3">
|
|
319
193
|
<button
|
|
320
194
|
type="button"
|
|
321
195
|
onClick={onClose}
|
|
322
196
|
disabled={isSubmitting}
|
|
323
|
-
|
|
324
|
-
padding: '8px 16px',
|
|
325
|
-
fontSize: 14,
|
|
326
|
-
fontWeight: 500,
|
|
327
|
-
borderRadius: 8,
|
|
328
|
-
border: '1px solid var(--ds-gray-alpha-400)',
|
|
329
|
-
background: 'var(--ds-background-100)',
|
|
330
|
-
color: 'var(--ds-gray-1000)',
|
|
331
|
-
cursor: isSubmitting ? 'not-allowed' : 'pointer',
|
|
332
|
-
opacity: isSubmitting ? 0.5 : 1,
|
|
333
|
-
transition: 'background 0.15s',
|
|
334
|
-
}}
|
|
335
|
-
onMouseEnter={(e) => {
|
|
336
|
-
e.currentTarget.style.background = 'var(--ds-gray-alpha-100)';
|
|
337
|
-
}}
|
|
338
|
-
onMouseLeave={(e) => {
|
|
339
|
-
e.currentTarget.style.background = 'var(--ds-background-100)';
|
|
340
|
-
}}
|
|
197
|
+
className="cursor-pointer rounded-lg border border-gray-alpha-400 bg-background-100 px-4 py-2 font-medium text-gray-1000 text-sm transition-colors hover:bg-gray-alpha-100 disabled:cursor-not-allowed disabled:opacity-50"
|
|
341
198
|
>
|
|
342
199
|
Cancel
|
|
343
200
|
</button>
|
|
@@ -345,29 +202,9 @@ export function ResolveHookModal({
|
|
|
345
202
|
type="button"
|
|
346
203
|
onClick={() => void submitPayload()}
|
|
347
204
|
disabled={isSubmitting}
|
|
348
|
-
|
|
349
|
-
display: 'flex',
|
|
350
|
-
alignItems: 'center',
|
|
351
|
-
gap: 6,
|
|
352
|
-
padding: '8px 16px',
|
|
353
|
-
fontSize: 14,
|
|
354
|
-
fontWeight: 500,
|
|
355
|
-
borderRadius: 8,
|
|
356
|
-
border: 'none',
|
|
357
|
-
background: 'var(--ds-gray-1000)',
|
|
358
|
-
color: 'var(--ds-background-100)',
|
|
359
|
-
cursor: isSubmitting ? 'not-allowed' : 'pointer',
|
|
360
|
-
opacity: isSubmitting ? 0.5 : 1,
|
|
361
|
-
transition: 'opacity 0.15s',
|
|
362
|
-
}}
|
|
363
|
-
onMouseEnter={(e) => {
|
|
364
|
-
if (!isSubmitting) e.currentTarget.style.opacity = '0.9';
|
|
365
|
-
}}
|
|
366
|
-
onMouseLeave={(e) => {
|
|
367
|
-
if (!isSubmitting) e.currentTarget.style.opacity = '1';
|
|
368
|
-
}}
|
|
205
|
+
className="flex cursor-pointer items-center gap-1.5 rounded-lg !border-none bg-gray-1000 px-4 py-2 font-medium text-background-100 text-sm transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:opacity-50"
|
|
369
206
|
>
|
|
370
|
-
<Send
|
|
207
|
+
<Send className="size-3.5" />
|
|
371
208
|
{isSubmitting ? 'Sending...' : 'Send Payload'}
|
|
372
209
|
</button>
|
|
373
210
|
</div>
|
|
@@ -7,6 +7,12 @@ import type { FetchSpanDetail } from './use-selected-span-detail';
|
|
|
7
7
|
export interface SidebarDataContextValue {
|
|
8
8
|
run: WorkflowRun;
|
|
9
9
|
events: Event[];
|
|
10
|
+
/**
|
|
11
|
+
* Events every replay reads past as repeats, computed once from the whole
|
|
12
|
+
* of `events` by whoever knows the list is complete. Absent when nobody
|
|
13
|
+
* could vouch for that, in which case nothing is marked.
|
|
14
|
+
*/
|
|
15
|
+
duplicateEventIds?: ReadonlySet<string>;
|
|
10
16
|
fetchSpanDetail: FetchSpanDetail;
|
|
11
17
|
onStreamClick?: (streamId: string) => void;
|
|
12
18
|
onRunClick?: (runId: string) => void;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Skeleton } from './ui/skeleton';
|
|
2
2
|
|
|
3
3
|
const STREAM_SKELETON_ROWS = [
|
|
4
|
-
{ id: 'stream-skeleton-row-1',
|
|
5
|
-
{ id: 'stream-skeleton-row-2',
|
|
6
|
-
{ id: 'stream-skeleton-row-3',
|
|
7
|
-
{ id: 'stream-skeleton-row-4',
|
|
4
|
+
{ id: 'stream-skeleton-row-1', widthClassName: 'w-[72%]' },
|
|
5
|
+
{ id: 'stream-skeleton-row-2', widthClassName: 'w-[58%]' },
|
|
6
|
+
{ id: 'stream-skeleton-row-3', widthClassName: 'w-4/5' },
|
|
7
|
+
{ id: 'stream-skeleton-row-4', widthClassName: 'w-[64%]' },
|
|
8
8
|
];
|
|
9
9
|
|
|
10
10
|
export function StreamViewerSkeleton() {
|
|
@@ -24,7 +24,7 @@ export function StreamViewerSkeleton() {
|
|
|
24
24
|
key={row.id}
|
|
25
25
|
className="flex h-10 items-center gap-2 border-b border-gray-alpha-400 px-3"
|
|
26
26
|
>
|
|
27
|
-
<Skeleton className=
|
|
27
|
+
<Skeleton className={`h-3 ${row.widthClassName}`} />
|
|
28
28
|
<Skeleton className="h-4 w-4 shrink-0 rounded-sm" />
|
|
29
29
|
</div>
|
|
30
30
|
))}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { ArrowLeft, ArrowRight } from 'lucide-react';
|
|
4
|
-
import type {
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
5
5
|
import {
|
|
6
6
|
Fragment,
|
|
7
7
|
memo,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
computeSpanGaps,
|
|
29
29
|
computeSpanMarkers,
|
|
30
30
|
computeSpanSegments,
|
|
31
|
-
|
|
31
|
+
getResourceClassNames,
|
|
32
32
|
getSpanDurationMs,
|
|
33
33
|
isSpanErrored,
|
|
34
34
|
} from '../utils';
|
|
@@ -62,11 +62,6 @@ const SEGMENT_CLASSES: Record<SegmentStatus, string> = {
|
|
|
62
62
|
received: 'bg-blue-200 border border-blue-500',
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
const TIMELINE_INSET_STYLE: CSSProperties = {
|
|
66
|
-
left: TIMELINE_PADDING_PX,
|
|
67
|
-
right: TIMELINE_PADDING_PX,
|
|
68
|
-
};
|
|
69
|
-
|
|
70
65
|
const STRIPED_SEGMENT_STATUSES: ReadonlySet<SegmentStatus> = new Set([
|
|
71
66
|
'pending',
|
|
72
67
|
'running',
|
|
@@ -260,18 +255,18 @@ function BoundaryArrow({
|
|
|
260
255
|
}
|
|
261
256
|
|
|
262
257
|
function PlainBar({
|
|
263
|
-
|
|
264
|
-
border,
|
|
258
|
+
className,
|
|
265
259
|
label,
|
|
266
260
|
}: {
|
|
267
|
-
|
|
268
|
-
border: string;
|
|
261
|
+
className: string;
|
|
269
262
|
label: string | null;
|
|
270
263
|
}): ReactNode {
|
|
271
264
|
return (
|
|
272
265
|
<div
|
|
273
|
-
className=
|
|
274
|
-
|
|
266
|
+
className={cn(
|
|
267
|
+
'relative h-6 w-full min-w-1 rounded-[0.25rem] border',
|
|
268
|
+
className
|
|
269
|
+
)}
|
|
275
270
|
>
|
|
276
271
|
{label ? <DurationLabel label={label} /> : null}
|
|
277
272
|
</div>
|
|
@@ -352,14 +347,13 @@ function SegmentBar({
|
|
|
352
347
|
<div
|
|
353
348
|
key={i}
|
|
354
349
|
className={cn(
|
|
355
|
-
'absolute h-full overflow-hidden rounded-[0.25rem]',
|
|
350
|
+
'absolute h-full min-w-px overflow-hidden rounded-[0.25rem]',
|
|
356
351
|
SEGMENT_CLASSES[seg.status]
|
|
357
352
|
)}
|
|
358
353
|
style={{
|
|
359
354
|
// 1px gap between adjacent segments, distributed equally.
|
|
360
355
|
left: `calc(${seg.leftPct}% + 0.5px)`,
|
|
361
356
|
width: `calc(${seg.widthPct}% - 1px)`,
|
|
362
|
-
minWidth: 1,
|
|
363
357
|
}}
|
|
364
358
|
>
|
|
365
359
|
{STRIPED_SEGMENT_STATUSES.has(seg.status) ? (
|
|
@@ -457,13 +451,10 @@ const TimelineBar = memo(function TimelineBar({
|
|
|
457
451
|
markers.length > 0 || offscreen.left !== null || offscreen.right !== null;
|
|
458
452
|
|
|
459
453
|
const isErrored = isSpanErrored(span);
|
|
460
|
-
const colors =
|
|
461
|
-
const
|
|
462
|
-
? (colors.
|
|
463
|
-
: colors.
|
|
464
|
-
const fallbackBorder = isErrored
|
|
465
|
-
? (colors.errorBorder ?? 'var(--ds-red-500)')
|
|
466
|
-
: colors.border;
|
|
454
|
+
const colors = getResourceClassNames(span.resource);
|
|
455
|
+
const fallbackClassName = isErrored
|
|
456
|
+
? (colors.errorClassName ?? 'border-red-500 bg-red-200')
|
|
457
|
+
: colors.className;
|
|
467
458
|
|
|
468
459
|
const totalLabel = formatDurationPrecise(totalDurationMs);
|
|
469
460
|
const showTotalLabel =
|
|
@@ -486,7 +477,7 @@ const TimelineBar = memo(function TimelineBar({
|
|
|
486
477
|
)}
|
|
487
478
|
onClick={handleClick}
|
|
488
479
|
>
|
|
489
|
-
<div className="absolute inset-y-0"
|
|
480
|
+
<div className="absolute inset-x-4 inset-y-0">
|
|
490
481
|
<div
|
|
491
482
|
className="absolute top-1/2 h-6 -translate-y-1/2 overflow-hidden rounded-[0.25rem]"
|
|
492
483
|
style={getBarPositionStyle(geometry)}
|
|
@@ -495,15 +486,13 @@ const TimelineBar = memo(function TimelineBar({
|
|
|
495
486
|
<BoundaryArrow direction={geometry.mode.direction} />
|
|
496
487
|
) : geometry.mode.kind === 'tiny' ? (
|
|
497
488
|
<div
|
|
498
|
-
className=
|
|
499
|
-
style={{ background: fallbackBg, borderColor: fallbackBorder }}
|
|
489
|
+
className={cn('h-6 rounded-[0.25rem] border', fallbackClassName)}
|
|
500
490
|
/>
|
|
501
491
|
) : segments.length > 0 ? (
|
|
502
492
|
<SegmentBar segments={segments} showLabels={!hasMarkers} />
|
|
503
493
|
) : (
|
|
504
494
|
<PlainBar
|
|
505
|
-
|
|
506
|
-
border={fallbackBorder}
|
|
495
|
+
className={fallbackClassName}
|
|
507
496
|
label={showTotalLabel ? totalLabel : null}
|
|
508
497
|
/>
|
|
509
498
|
)}
|
|
@@ -612,10 +601,9 @@ const DeltaMeasureLine = memo(function DeltaMeasureLine({
|
|
|
612
601
|
return (
|
|
613
602
|
<>
|
|
614
603
|
<div
|
|
615
|
-
className="absolute h-px bg-amber-800"
|
|
604
|
+
className="absolute h-px w-1 bg-amber-800"
|
|
616
605
|
style={{
|
|
617
606
|
left: Math.min(anchorX, guideX),
|
|
618
|
-
width: MEASURE_GUIDE_OUTSET_PX,
|
|
619
607
|
top: anchorCenterY,
|
|
620
608
|
}}
|
|
621
609
|
/>
|
|
@@ -781,8 +769,7 @@ export function Timeline({
|
|
|
781
769
|
>
|
|
782
770
|
<div
|
|
783
771
|
aria-hidden
|
|
784
|
-
className="absolute inset-
|
|
785
|
-
style={TIMELINE_INSET_STYLE}
|
|
772
|
+
className="pointer-events-none absolute inset-x-4 inset-y-0"
|
|
786
773
|
>
|
|
787
774
|
{markers.map((marker) =>
|
|
788
775
|
// Skip the "0s" origin marker since the left edge already implies it.
|
|
@@ -796,10 +783,7 @@ export function Timeline({
|
|
|
796
783
|
)}
|
|
797
784
|
</div>
|
|
798
785
|
{hover != null && (
|
|
799
|
-
<div
|
|
800
|
-
className="absolute inset-y-0 pointer-events-none z-10"
|
|
801
|
-
style={TIMELINE_INSET_STYLE}
|
|
802
|
-
>
|
|
786
|
+
<div className="pointer-events-none absolute inset-x-4 inset-y-0 z-10">
|
|
803
787
|
<div
|
|
804
788
|
className="absolute top-0 bottom-0 w-px bg-gray-alpha-500"
|
|
805
789
|
style={{ left: `${hover.fraction * 100}%` }}
|
|
@@ -824,8 +808,7 @@ export function Timeline({
|
|
|
824
808
|
{altHeld && anchorIndex < 0 && (
|
|
825
809
|
<div
|
|
826
810
|
aria-hidden
|
|
827
|
-
className="absolute inset-
|
|
828
|
-
style={TIMELINE_INSET_STYLE}
|
|
811
|
+
className="pointer-events-none absolute inset-x-4 inset-y-0"
|
|
829
812
|
>
|
|
830
813
|
{gapMeasurements.map((gap) => (
|
|
831
814
|
<DeltaMeasureLine
|
|
@@ -839,8 +822,7 @@ export function Timeline({
|
|
|
839
822
|
{measurement && (
|
|
840
823
|
<div
|
|
841
824
|
aria-hidden
|
|
842
|
-
className="absolute inset-
|
|
843
|
-
style={TIMELINE_INSET_STYLE}
|
|
825
|
+
className="pointer-events-none absolute inset-x-4 inset-y-0 z-20"
|
|
844
826
|
>
|
|
845
827
|
<DeltaMeasureLine {...measurement} timelineWidth={timelineWidth} />
|
|
846
828
|
</div>
|
|
@@ -1,19 +1,62 @@
|
|
|
1
1
|
import { Skeleton } from '../../ui/skeleton';
|
|
2
|
-
import { DEFAULT_START_PX, paneColTemplate } from './pane-constants';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
const ROWS = [
|
|
4
|
+
{
|
|
5
|
+
id: 'r0',
|
|
6
|
+
nameClassName: 'w-[62%]',
|
|
7
|
+
offsetClassName: 'left-0',
|
|
8
|
+
barClassName: 'w-[72%]',
|
|
9
|
+
topClassName: 'top-0',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: 'r1',
|
|
13
|
+
nameClassName: 'w-[78%]',
|
|
14
|
+
offsetClassName: 'left-[6%]',
|
|
15
|
+
barClassName: 'w-[48%]',
|
|
16
|
+
topClassName: 'top-[3.5px]',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'r2',
|
|
20
|
+
nameClassName: 'w-1/2',
|
|
21
|
+
offsetClassName: 'left-[10%]',
|
|
22
|
+
barClassName: 'w-[55%]',
|
|
23
|
+
topClassName: 'top-[7px]',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'r3',
|
|
27
|
+
nameClassName: 'w-[84%]',
|
|
28
|
+
offsetClassName: 'left-[18%]',
|
|
29
|
+
barClassName: 'w-[30%]',
|
|
30
|
+
topClassName: 'top-[10.5px]',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'r4',
|
|
34
|
+
nameClassName: 'w-[45%]',
|
|
35
|
+
offsetClassName: 'left-[18%]',
|
|
36
|
+
barClassName: 'w-[42%]',
|
|
37
|
+
topClassName: 'top-[14px]',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'r5',
|
|
41
|
+
nameClassName: 'w-[66%]',
|
|
42
|
+
offsetClassName: 'left-[34%]',
|
|
43
|
+
barClassName: 'w-[38%]',
|
|
44
|
+
topClassName: 'top-[17.5px]',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'r6',
|
|
48
|
+
nameClassName: 'w-[55%]',
|
|
49
|
+
offsetClassName: 'left-[41%]',
|
|
50
|
+
barClassName: 'w-1/4',
|
|
51
|
+
topClassName: 'top-[21px]',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'r7',
|
|
55
|
+
nameClassName: 'w-2/5',
|
|
56
|
+
offsetClassName: 'left-1/2',
|
|
57
|
+
barClassName: 'w-[30%]',
|
|
58
|
+
topClassName: 'top-[24.5px]',
|
|
59
|
+
},
|
|
17
60
|
];
|
|
18
61
|
|
|
19
62
|
const HEADER_MARKERS = ['m0', 'm1', 'm2', 'm3'];
|
|
@@ -36,25 +79,17 @@ export function TraceViewerSkeleton() {
|
|
|
36
79
|
{/* Minimap strip: thin density lines tracing the same shape as the bars */}
|
|
37
80
|
<div className="relative h-10 min-h-10 shrink-0 border-b border-gray-alpha-400">
|
|
38
81
|
<div className="absolute inset-x-4 top-[6px]">
|
|
39
|
-
{ROWS.map((row
|
|
82
|
+
{ROWS.map((row) => (
|
|
40
83
|
<Skeleton
|
|
41
84
|
key={row.id}
|
|
42
|
-
className=
|
|
43
|
-
style={{
|
|
44
|
-
left: `${row.off}%`,
|
|
45
|
-
width: `${row.bar}%`,
|
|
46
|
-
top: index * 3.5,
|
|
47
|
-
}}
|
|
85
|
+
className={`absolute h-[3px] rounded-full ${row.offsetClassName} ${row.barClassName} ${row.topClassName}`}
|
|
48
86
|
/>
|
|
49
87
|
))}
|
|
50
88
|
</div>
|
|
51
89
|
</div>
|
|
52
90
|
|
|
53
91
|
{/* Header row: search header | divider | timeline header */}
|
|
54
|
-
<div
|
|
55
|
-
className="shrink-0 grid"
|
|
56
|
-
style={{ gridTemplateColumns: COL_TEMPLATE }}
|
|
57
|
-
>
|
|
92
|
+
<div className="grid shrink-0 grid-cols-[340px_1px_minmax(50px,1fr)]">
|
|
58
93
|
<div className="h-10 min-h-10 flex items-center border-b border-gray-alpha-400 pl-4 pr-2 gap-1.5">
|
|
59
94
|
<Skeleton className="w-3.5 h-3.5 shrink-0 rounded-sm" />
|
|
60
95
|
<Skeleton className="h-3.5 w-40" />
|
|
@@ -70,10 +105,7 @@ export function TraceViewerSkeleton() {
|
|
|
70
105
|
</div>
|
|
71
106
|
|
|
72
107
|
{/* Content row: event list | gutter | timeline */}
|
|
73
|
-
<div
|
|
74
|
-
className="grid flex-1 min-h-0 overflow-hidden"
|
|
75
|
-
style={{ gridTemplateColumns: COL_TEMPLATE }}
|
|
76
|
-
>
|
|
108
|
+
<div className="grid min-h-0 flex-1 grid-cols-[340px_1px_minmax(50px,1fr)] overflow-hidden">
|
|
77
109
|
{/* Sidebar event rows */}
|
|
78
110
|
<div className="block overflow-visible">
|
|
79
111
|
<ul className="block divide-y divide-gray-alpha-400 border-b border-gray-alpha-400">
|
|
@@ -81,10 +113,7 @@ export function TraceViewerSkeleton() {
|
|
|
81
113
|
<li key={row.id} className="h-10 flex items-center pl-4 pr-2">
|
|
82
114
|
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
83
115
|
<Skeleton className="w-4 h-4 shrink-0 rounded-sm" />
|
|
84
|
-
<Skeleton
|
|
85
|
-
className="h-3.5"
|
|
86
|
-
style={{ width: `${row.name}%` }}
|
|
87
|
-
/>
|
|
116
|
+
<Skeleton className={`h-3.5 ${row.nameClassName}`} />
|
|
88
117
|
</div>
|
|
89
118
|
<Skeleton className="ml-2 h-3.5 w-10 shrink-0" />
|
|
90
119
|
</li>
|
|
@@ -101,8 +130,7 @@ export function TraceViewerSkeleton() {
|
|
|
101
130
|
<div key={row.id} className="relative h-10">
|
|
102
131
|
<div className="absolute inset-x-4 inset-y-0">
|
|
103
132
|
<Skeleton
|
|
104
|
-
className=
|
|
105
|
-
style={{ left: `${row.off}%`, width: `${row.bar}%` }}
|
|
133
|
+
className={`absolute top-1/2 h-6 -translate-y-1/2 rounded-[0.25rem] ${row.offsetClassName} ${row.barClassName}`}
|
|
106
134
|
/>
|
|
107
135
|
</div>
|
|
108
136
|
</div>
|