@workflow/web 5.0.0-beta.4 → 5.0.0-beta.41

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.
Files changed (47) hide show
  1. package/README.md +1 -0
  2. package/build/client/assets/app-BkGXg64e.css +1 -0
  3. package/build/client/assets/arrow-up-right-jWH-UQH9.js +15 -0
  4. package/build/client/assets/{entry.client-BWsSsWQm.js → entry.client-LwI3HNYl.js} +25 -25
  5. package/build/client/assets/{highlighted-body-B3W2YXNL-B7T9pqeb.js → highlighted-body-B3W2YXNL-DmmWu7VM.js} +2 -2
  6. package/build/client/assets/{home-BIQJMe_Q.js → home-BwYHJJJr.js} +1762 -290
  7. package/build/client/assets/index-HqNj_XIX.js +200 -0
  8. package/build/client/assets/{index-DQa-BExo.js → index-PFjW8YjQ.js} +24 -24
  9. package/build/client/assets/loader-circle-fFQM94Ej.js +12 -0
  10. package/build/client/assets/manifest-f6a9dca2.js +1 -0
  11. package/build/client/assets/mermaid-3ZIDBTTL-D8CjRQta.css +1 -0
  12. package/build/client/assets/{mermaid-3ZIDBTTL-w46Kr90n.js → mermaid-3ZIDBTTL-DchhEfRL.js} +6169 -4615
  13. package/build/client/assets/{root-sXuXrryl.js → root-D6p4Hb4F.js} +17 -42
  14. package/build/client/assets/root-ZRjqDhEN.css +1 -0
  15. package/build/client/assets/run-detail-CWoGxu_0.css +1 -0
  16. package/build/client/assets/{run-detail-7Pj2mQLs.js → run-detail-vPCbZIXx.js} +1970 -1587
  17. package/build/client/assets/{workflow-graph-viewer-De9JQkZc.js → workflow-graph-viewer-t_fSwuwz.js} +5109 -5110
  18. package/build/client/assets/zstd-B5R2yJjB.wasm +0 -0
  19. package/build/client/assets/zstd-browser-decoder-BTw1glGN.js +27 -0
  20. package/build/server/assets/{server-build-DT0Hg0Yf.js → app-D8GiFFR6.js} +71220 -49560
  21. package/build/server/assets/{highlighted-body-B3W2YXNL-uaucauLI.js → highlighted-body-B3W2YXNL-BZVc2guG.js} +13 -8
  22. package/build/server/assets/index-BByEr1g6.js +154 -0
  23. package/build/server/assets/index-BfLBLRRi.js +204 -0
  24. package/build/server/assets/index-CETb4fkq.js +1164 -0
  25. package/build/server/assets/index-DTci39F3.js +1262 -0
  26. package/build/server/assets/{mermaid-3ZIDBTTL-DrZ5V6uv.js → mermaid-3ZIDBTTL-DOefrphg.js} +14 -9
  27. package/build/server/assets/{token-D7_Lld85.js → token-c3byIJRC.js} +5 -2
  28. package/build/server/assets/{token-util-CfY4anoc.js → token-util-C2yENsqE.js} +5 -2
  29. package/build/server/assets/websocket-server-DagS_hnJ.js +3669 -0
  30. package/build/server/assets/wrapper-Cnumc4u1.js +67 -0
  31. package/build/server/assets/ws-transport-2gJL6hC2.js +493 -0
  32. package/build/server/assets/zstd-browser-decoder-BCVvr1WT.js +70 -0
  33. package/build/server/index.js +48 -2
  34. package/handler.d.ts +22 -0
  35. package/handler.js +186 -0
  36. package/package.json +27 -12
  37. package/registry.d.ts +29 -0
  38. package/registry.js +89 -0
  39. package/build/client/assets/encryption-80GMP4r0.js +0 -26
  40. package/build/client/assets/manifest-77894282.js +0 -1
  41. package/build/client/assets/mermaid-3ZIDBTTL-DKxHcEOp.css +0 -1
  42. package/build/client/assets/root-C0Tu5TVN.css +0 -1
  43. package/build/client/assets/server-build-DqaxIrF0.css +0 -1
  44. package/build/client/assets/worker-CqHKTA7D.js +0 -115
  45. package/build/server/assets/app-CtOg8_v5.js +0 -9213
  46. package/build/server/assets/index-D02biwxV.js +0 -82
  47. package/build/server/assets/index-meg9qk8y.js +0 -1282
@@ -1,7 +1,7 @@
1
- import { j as jsxRuntimeExports, r as reactExports, f as reactDomExports, C as ReactDOM, A as useNavigate, B as useSearchParams, z as Link, w as withComponentProps, G as useParams } from "./index-DQa-BExo.js";
2
- import { N as Copy, C as CircleAlert, O as parseStepName, z as parseWorkflowName, j as ChevronRight, p as Check, Q as ErrorCard, Z as Zap, S as Send, R as ResolveHookModal, U as Circle, X, M as ErrorBoundary, H as unwrapServerActionResult, u as unwrapOrThrow, W as WorkflowWebAPIError, V as useEscapeKeydown, s as composeEventHandlers, Y as useCallbackRef, _ as useLayoutEffect2, q as useControllableState, $ as useId, a0 as createContext2, t as Presence, w as createContextScope, l as hideOthers, m as ReactRemoveScroll, a1 as useFocusGuards, a2 as LAYOUT, a3 as wakeUpRun, r as resumeHook, A as Alert, c as AlertTitle, d as AlertDescription, a as Skeleton$1, a4 as RunActionsButtons, B as StatusBadge, e as CopyableText, b as RelativeTime, T as Tabs, J as TabsList, K as TabsTrigger, L as TabsContent, x as cancelRun, a5 as recreateRun } from "./workflow-graph-viewer-De9JQkZc.js";
3
- import { c as createLucideIcon, M as Spinner, h as useToast, N as Skeleton, O as DecryptClickContext, Y as Yr, U as isEncryptedMarker, W as formatDuration, X as DataInspector, _ as _r, Z as RunClickContext, $ as StreamClickContext, a0 as isExpiredMarker, a1 as isDoStreamStep, a2 as extractConversation, a3 as Lock, a4 as EVENT_DATA_REF_FIELDS, a5 as clsx, a6 as cn, a7 as getSpanDurationMs, a8 as getHighResInMs, a9 as ActiveSpanProvider, aa as useActiveSpan, ab as useSidebarDataOptional, ac as computeRootBounds, ad as computeTimeMarkers, ae as TIMELINE_PADDING_PX, af as Timeline, ag as TimelineHeader, ah as SidebarDataProvider, ai as hydrateResourceIOWithKey, aj as hydrateResourceIO, ak as fetchHook, l as fetchEvents, F as fetchRun, al as fetchStep, am as hasEncryptedFields, an as fetchStreams, ao as Slot, E as useComposedRefs, ap as Slottable, d as cn$1, aq as buttonVariants, ar as isEncryptedData, as as hydrateData, at as getWebRevivers, T as Tooltip, a as TooltipTrigger, b as TooltipContent, au as LIVE_UPDATE_INTERVAL_MS$1, u as useServerConfig, av as fetchEvent, aw as getEncryptionKeyForRun, t as toast, L as LoaderCircle, ax as StreamViewer } from "./mermaid-3ZIDBTTL-w46Kr90n.js";
4
- import { decrypt, importKey } from "./encryption-80GMP4r0.js";
1
+ import { r as reactExports, j as jsxRuntimeExports, o as reactDomExports, D as ReactDOM, d as useNavigate, e as useSearchParams, c as Link, w as withComponentProps, G as useParams } from "./index-PFjW8YjQ.js";
2
+ import { C as CircleAlert, O as parseStepName, B as parseWorkflowName, Q as ErrorCard, U as format, Z as Zap, S as Send, R as ResolveHookModal, q as ChevronUp, X, N as ErrorBoundary, V as Circle, u as unwrapOrThrow, W as WorkflowWebAPIError, I as unwrapServerActionResult, Y as useEscapeKeydown, t as composeEventHandlers, _ as useCallbackRef, $ as useLayoutEffect2, s as useControllableState, a0 as useId, a1 as createContext2, v as Presence, x as createContextScope, n as hideOthers, o as ReactRemoveScroll, a2 as useFocusGuards, a3 as LAYOUT, a4 as wakeUpRun, l as resumeHook, A as Alert, c as AlertTitle, g as getErrorTitle, d as AlertDescription, e as getErrorMessage, a as Skeleton$1, a5 as RunActionsButtons, F as StatusBadge, h as CopyableText, b as RelativeTime, T as Tabs, K as TabsList, L as TabsTrigger, M as TabsContent, a6 as cancelRun, a7 as recreateRun } from "./workflow-graph-viewer-t_fSwuwz.js";
3
+ import { c as createLucideIcon, Y as cn, Z as CopyableDataBlock, $ as CollapsibleRoot, a0 as CollapsibleTrigger, a1 as CollapsibleContent, a2 as CopyButton, O as cva, a3 as RESERVED_ATTRIBUTE_KEY_PREFIX, a4 as Spinner, a5 as ContextCardProvider, h as useToast, a6 as Skeleton, a7 as DecryptClickContext, a8 as Yr, a9 as isEncryptedMarker, m as ChevronRight, aa as TimestampTooltip, ab as formatDuration, C as Check, l as Copy, ac as DataInspector, ad as _r, ae as isHookLifecycleEventType, af as isTerminalStepEventType, ag as isWaitEventType, ah as isStepEventType, ai as RunClickContext, aj as StreamClickContext, ak as Collapsible, al as isExpiredMarker, am as isDoStreamStep, an as extractConversation, ao as EncryptedDataBlock, ap as getEventDataRefFields, aq as hasEncryptedFields, ar as useSidebarDataOptional, as as isTerminalWorkflowRunStatus, at as clsx, au as useActiveSpan, av as useSidebarData, aw as Tooltip, ax as TooltipTrigger, ay as IconButton, az as TooltipContent, J as ChevronDown, aA as useRowWindow, aB as isSpanDimmedBySearch, aC as ROW_HEIGHT_PX, aD as getSpanDurationMs, aE as isSpanErrored, aF as formatDurationPrecise, aG as TooltipProvider, aH as ActiveSpanProvider, aI as useReducedMotion, aJ as searchSpans, aK as computeRootBounds, aL as computeTimeMarkers, aM as clampViewportToRoot, aN as getHighResInMs, aO as scrollRowIntoView, aP as TIMELINE_PADDING_PX, aQ as Minimap, aR as Timeline, aS as TimelineHeader, aT as wheelZoomScaleFactor, aU as wheelDeltaToPixels, aV as SidebarDataProvider, aW as fetchHook, n as fetchEvents, aX as hydrateResourceIOAsync, N as fetchRun, aY as fetchStep, aZ as hydrateResourceIO, a_ as fetchStreams, a$ as apiBase, b0 as Slot, L as useComposedRefs, b1 as Slottable, e as cn$1, b2 as buttonVariants, b3 as fetchEvent, b4 as fetchEventsByCorrelationId, b5 as isEncryptedData, b6 as hydrateData, b7 as decrypt, b8 as deriveRunPayloadKeys, b9 as getWebRevivers, T as Tooltip$1, a as TooltipTrigger$1, b as TooltipContent$1, ba as LIVE_UPDATE_INTERVAL_MS$1, u as useServerConfig, bb as getEncryptionKeyForRun, t as toast, bc as StreamViewerSkeleton, bd as Lock, be as StreamViewer } from "./mermaid-3ZIDBTTL-DchhEfRL.js";
4
+ import { A as ArrowUpRight } from "./arrow-up-right-jWH-UQH9.js";
5
5
  /**
6
6
  * @license lucide-react v0.575.0 - ISC
7
7
  *
@@ -76,40 +76,457 @@ const __iconNode = [
76
76
  ["line", { x1: "8", x2: "14", y1: "11", y2: "11", key: "durymu" }]
77
77
  ];
78
78
  const ZoomOut = createLucideIcon("zoom-out", __iconNode);
79
+ const WORKFLOW_ULID_BODY = "[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}";
80
+ const STEP_ID_PATTERN = new RegExp(`^step_(${WORKFLOW_ULID_BODY})$`, "i");
81
+ const WAIT_ID_PATTERN = new RegExp(`^wait_(${WORKFLOW_ULID_BODY})$`, "i");
82
+ const HOOK_ID_PATTERN = new RegExp(`^hook_(${WORKFLOW_ULID_BODY})$`, "i");
83
+ const EVENT_ID_PATTERN = new RegExp(`^evnt_(${WORKFLOW_ULID_BODY})$`, "i");
84
+ const WORKFLOW_ID_PREFIX_PATTERN = /^(step_|wait_|hook_|evnt_|wrun_)/i;
85
+ function matchPrefixedId(pattern, prefix, kind, query) {
86
+ const match = query.match(pattern);
87
+ if (!match) {
88
+ return null;
89
+ }
90
+ return { kind, id: `${prefix}_${match[1].toUpperCase()}` };
91
+ }
92
+ function parseExactWorkflowSearchId(query) {
93
+ const trimmed = query.trim();
94
+ if (!trimmed) {
95
+ return null;
96
+ }
97
+ return matchPrefixedId(STEP_ID_PATTERN, "step", "step", trimmed) ?? matchPrefixedId(WAIT_ID_PATTERN, "wait", "wait", trimmed) ?? matchPrefixedId(HOOK_ID_PATTERN, "hook", "hook", trimmed) ?? matchPrefixedId(EVENT_ID_PATTERN, "evnt", "event", trimmed);
98
+ }
99
+ function looksLikeWorkflowIdSearchInput(query) {
100
+ const trimmed = query.trim();
101
+ if (!WORKFLOW_ID_PREFIX_PATTERN.test(trimmed)) {
102
+ return false;
103
+ }
104
+ return /\d/.test(trimmed);
105
+ }
106
+ const ELLIPSIS = "…";
107
+ const MIN_START = 3;
108
+ const MIN_END = 3;
109
+ const MIN_KEPT = MIN_START + MIN_END;
110
+ const graphemeSegmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
111
+ function toGraphemes(text) {
112
+ if (graphemeSegmenter) {
113
+ return [...graphemeSegmenter.segment(text)].map((s) => s.segment);
114
+ }
115
+ return Array.from(text);
116
+ }
117
+ function buildCandidate(graphemes, kept) {
118
+ if (kept <= 0) {
119
+ return {
120
+ prefixText: "",
121
+ prefixGraphemeCount: 0,
122
+ suffixText: "",
123
+ suffixGraphemeCount: 0,
124
+ text: ELLIPSIS,
125
+ truncated: true
126
+ };
127
+ }
128
+ const suffixGraphemeCount = kept >= MIN_KEPT ? Math.max(MIN_END, Math.floor(kept / 2)) : Math.floor(kept / 2);
129
+ const prefixGraphemeCount = kept - suffixGraphemeCount;
130
+ const prefixText = graphemes.slice(0, prefixGraphemeCount).join("");
131
+ const suffixText = suffixGraphemeCount > 0 ? graphemes.slice(-suffixGraphemeCount).join("") : "";
132
+ return {
133
+ prefixText,
134
+ prefixGraphemeCount,
135
+ suffixText,
136
+ suffixGraphemeCount,
137
+ text: prefixText + ELLIPSIS + suffixText,
138
+ truncated: true
139
+ };
140
+ }
141
+ function middleTruncate(graphemes, availableWidth, measure, fullWidth) {
142
+ const fullText = graphemes.join("");
143
+ const resolvedFullWidth = fullWidth ?? measure(fullText);
144
+ if (availableWidth <= 0 || graphemes.length === 0) {
145
+ return {
146
+ prefixText: fullText,
147
+ prefixGraphemeCount: graphemes.length,
148
+ suffixText: "",
149
+ suffixGraphemeCount: 0,
150
+ text: fullText,
151
+ truncated: false
152
+ };
153
+ }
154
+ if (resolvedFullWidth <= availableWidth) {
155
+ return {
156
+ prefixText: fullText,
157
+ prefixGraphemeCount: graphemes.length,
158
+ suffixText: "",
159
+ suffixGraphemeCount: 0,
160
+ text: fullText,
161
+ truncated: false
162
+ };
163
+ }
164
+ let lo = 0;
165
+ let hi = graphemes.length - 1;
166
+ let best = -1;
167
+ while (lo <= hi) {
168
+ const mid = lo + hi >>> 1;
169
+ const candidate = buildCandidate(graphemes, mid);
170
+ if (measure(candidate.text) <= availableWidth) {
171
+ best = mid;
172
+ lo = mid + 1;
173
+ } else {
174
+ hi = mid - 1;
175
+ }
176
+ }
177
+ if (best === -1) {
178
+ return {
179
+ prefixText: "",
180
+ prefixGraphemeCount: 0,
181
+ suffixText: "",
182
+ suffixGraphemeCount: 0,
183
+ text: "",
184
+ truncated: true
185
+ };
186
+ }
187
+ return buildCandidate(graphemes, best);
188
+ }
189
+ function getMiddleTruncateCopyText({ prefixText, selectionEnd, selectionStart, suffixText, value }) {
190
+ const visibleText = prefixText + ELLIPSIS + suffixText;
191
+ if (selectionStart < 0 || selectionEnd > visibleText.length || selectionStart >= selectionEnd) {
192
+ return null;
193
+ }
194
+ if (selectionStart === 0 && selectionEnd === visibleText.length) {
195
+ return value;
196
+ }
197
+ const ellipsisStart = prefixText.length;
198
+ const ellipsisEnd = ellipsisStart + ELLIPSIS.length;
199
+ if (selectionStart > ellipsisStart || selectionEnd < ellipsisEnd) {
200
+ return null;
201
+ }
202
+ const originalGraphemes = toGraphemes(value);
203
+ const selectedPrefixGraphemeCount = toGraphemes(visibleText.slice(0, selectionStart)).length;
204
+ const selectedSuffixGraphemeCount = toGraphemes(visibleText.slice(ellipsisEnd, selectionEnd)).length;
205
+ const suffixStart = originalGraphemes.length - toGraphemes(suffixText).length;
206
+ return originalGraphemes.slice(selectedPrefixGraphemeCount, suffixStart + selectedSuffixGraphemeCount).join("");
207
+ }
208
+ function getMiddleTruncateCopyTextFromSelectionText({ prefixText, selectionText, suffixText, value }) {
209
+ const visibleText = prefixText + ELLIPSIS + suffixText;
210
+ const trimmedSelectionText = selectionText.trim();
211
+ if (!trimmedSelectionText) {
212
+ return null;
213
+ }
214
+ const leading = selectionText.slice(0, selectionText.length - selectionText.trimStart().length);
215
+ const trailing = selectionText.slice(selectionText.trimEnd().length);
216
+ if (trimmedSelectionText === visibleText) {
217
+ return leading + value + trailing;
218
+ }
219
+ if (!trimmedSelectionText.includes(ELLIPSIS)) {
220
+ return null;
221
+ }
222
+ const selectionStart = visibleText.indexOf(trimmedSelectionText);
223
+ if (selectionStart === -1) {
224
+ return null;
225
+ }
226
+ const selectionEnd = selectionStart + trimmedSelectionText.length;
227
+ const mappedText = getMiddleTruncateCopyText({
228
+ prefixText,
229
+ selectionEnd,
230
+ selectionStart,
231
+ suffixText,
232
+ value
233
+ });
234
+ return mappedText === null ? null : leading + mappedText + trailing;
235
+ }
236
+ const useIsomorphicLayoutEffect = typeof window === "undefined" ? reactExports.useEffect : reactExports.useLayoutEffect;
237
+ function createFullState(value, graphemes) {
238
+ return {
239
+ displayText: value,
240
+ isTruncated: false,
241
+ prefixGraphemeCount: graphemes.length,
242
+ prefixText: value,
243
+ suffixGraphemeCount: 0,
244
+ suffixText: ""
245
+ };
246
+ }
247
+ function useMiddleTruncate(value) {
248
+ const graphemes = reactExports.useMemo(() => toGraphemes(value), [value]);
249
+ const fullState = reactExports.useMemo(() => createFullState(value, graphemes), [graphemes, value]);
250
+ const ref = reactExports.useRef(null);
251
+ const measureRef = reactExports.useRef(null);
252
+ const [state, setState] = reactExports.useState(() => fullState);
253
+ const rafRef = reactExports.useRef(0);
254
+ const updateState = reactExports.useCallback((nextState) => {
255
+ setState((currentState) => {
256
+ if (currentState.displayText === nextState.displayText && currentState.isTruncated === nextState.isTruncated && currentState.prefixText === nextState.prefixText && currentState.prefixGraphemeCount === nextState.prefixGraphemeCount && currentState.suffixText === nextState.suffixText && currentState.suffixGraphemeCount === nextState.suffixGraphemeCount) {
257
+ return currentState;
258
+ }
259
+ return nextState;
260
+ });
261
+ }, []);
262
+ const recalculate = reactExports.useCallback(() => {
263
+ const el = ref.current;
264
+ const measureEl = measureRef.current;
265
+ if (!el || !measureEl)
266
+ return;
267
+ const available = el.clientWidth;
268
+ if (available <= 0) {
269
+ updateState(fullState);
270
+ return;
271
+ }
272
+ const measure = (text) => {
273
+ measureEl.textContent = text;
274
+ return measureEl.scrollWidth;
275
+ };
276
+ const fullWidth = measure(value);
277
+ if (fullWidth <= available) {
278
+ updateState(fullState);
279
+ return;
280
+ }
281
+ const result = middleTruncate(graphemes, available, measure, fullWidth);
282
+ updateState({
283
+ displayText: result.text,
284
+ isTruncated: result.truncated,
285
+ prefixGraphemeCount: result.prefixGraphemeCount,
286
+ prefixText: result.prefixText,
287
+ suffixGraphemeCount: result.suffixGraphemeCount,
288
+ suffixText: result.suffixText
289
+ });
290
+ }, [fullState, graphemes, updateState, value]);
291
+ useIsomorphicLayoutEffect(() => {
292
+ recalculate();
293
+ }, [recalculate]);
294
+ reactExports.useEffect(() => {
295
+ var _a;
296
+ const el = ref.current;
297
+ if (!el)
298
+ return;
299
+ const debouncedRecalc = () => {
300
+ cancelAnimationFrame(rafRef.current);
301
+ rafRef.current = requestAnimationFrame(recalculate);
302
+ };
303
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(debouncedRecalc) : null;
304
+ ro == null ? void 0 : ro.observe(el);
305
+ window.addEventListener("resize", debouncedRecalc);
306
+ const onFontsLoaded = () => {
307
+ debouncedRecalc();
308
+ };
309
+ const fontSet = "fonts" in document ? document.fonts : null;
310
+ (_a = fontSet == null ? void 0 : fontSet.addEventListener) == null ? void 0 : _a.call(fontSet, "loadingdone", onFontsLoaded);
311
+ return () => {
312
+ var _a2;
313
+ ro == null ? void 0 : ro.disconnect();
314
+ window.removeEventListener("resize", debouncedRecalc);
315
+ cancelAnimationFrame(rafRef.current);
316
+ (_a2 = fontSet == null ? void 0 : fontSet.removeEventListener) == null ? void 0 : _a2.call(fontSet, "loadingdone", onFontsLoaded);
317
+ };
318
+ }, [recalculate]);
319
+ return {
320
+ ref,
321
+ measureRef,
322
+ displayText: state.displayText,
323
+ isTruncated: state.isTruncated,
324
+ prefixGraphemeCount: state.prefixGraphemeCount,
325
+ prefixText: state.prefixText,
326
+ suffixGraphemeCount: state.suffixGraphemeCount,
327
+ suffixText: state.suffixText
328
+ };
329
+ }
330
+ function getRangeOffsets(container, range) {
331
+ if (!container.contains(range.startContainer) || !container.contains(range.endContainer)) {
332
+ return null;
333
+ }
334
+ const startRange = document.createRange();
335
+ startRange.selectNodeContents(container);
336
+ startRange.setEnd(range.startContainer, range.startOffset);
337
+ const endRange = document.createRange();
338
+ endRange.selectNodeContents(container);
339
+ endRange.setEnd(range.endContainer, range.endOffset);
340
+ return {
341
+ end: endRange.toString().length,
342
+ start: startRange.toString().length
343
+ };
344
+ }
345
+ function MiddleTruncate({ value, className, onCopy: onCopyProp, ...props }) {
346
+ const { ref, measureRef, displayText, isTruncated, prefixText, suffixText } = useMiddleTruncate(value);
347
+ const visibleRef = reactExports.useRef(null);
348
+ const handleCopy = reactExports.useCallback((e) => {
349
+ onCopyProp == null ? void 0 : onCopyProp(e);
350
+ if (e.defaultPrevented || !isTruncated)
351
+ return;
352
+ const selection = window.getSelection();
353
+ if (!selection || selection.rangeCount === 0)
354
+ return;
355
+ const selectionText = selection.toString();
356
+ if (!selectionText)
357
+ return;
358
+ const range = selection.getRangeAt(0);
359
+ const visibleEl = visibleRef.current;
360
+ let copyText = null;
361
+ if (e.currentTarget.contains(range.startContainer) && e.currentTarget.contains(range.endContainer) && visibleEl) {
362
+ const offsets = getRangeOffsets(visibleEl, range);
363
+ if (offsets) {
364
+ copyText = getMiddleTruncateCopyText({
365
+ prefixText,
366
+ selectionEnd: offsets.end,
367
+ selectionStart: offsets.start,
368
+ suffixText,
369
+ value
370
+ });
371
+ }
372
+ }
373
+ copyText ?? (copyText = getMiddleTruncateCopyTextFromSelectionText({
374
+ prefixText,
375
+ selectionText,
376
+ suffixText,
377
+ value
378
+ }));
379
+ if (copyText === null)
380
+ return;
381
+ e.preventDefault();
382
+ e.clipboardData.setData("text/plain", copyText);
383
+ }, [onCopyProp, isTruncated, prefixText, suffixText, value]);
384
+ return jsxRuntimeExports.jsxs("span", { title: isTruncated ? value : void 0, ...props, ref, className: cn("relative inline-grid min-w-0 max-w-full overflow-hidden whitespace-nowrap", className), onCopy: handleCopy, children: [isTruncated && jsxRuntimeExports.jsx("span", { className: "sr-only select-none", children: value }), jsxRuntimeExports.jsx("span", { "aria-hidden": "true", className: "pointer-events-none col-start-1 row-start-1 invisible select-none whitespace-nowrap", children: value }), jsxRuntimeExports.jsx("span", { "aria-hidden": isTruncated || void 0, className: "col-start-1 row-start-1 min-w-0 overflow-hidden", ref: visibleRef, children: isTruncated ? jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("span", { children: prefixText }), jsxRuntimeExports.jsx("span", { children: ELLIPSIS }), jsxRuntimeExports.jsx("span", { children: suffixText })] }) : displayText }), jsxRuntimeExports.jsx("span", { "aria-hidden": "true", className: "pointer-events-none absolute left-0 top-0 inline-block invisible select-none whitespace-nowrap", ref: measureRef })] });
385
+ }
386
+ function isReservedAttributeKey(key) {
387
+ return key.startsWith(RESERVED_ATTRIBUTE_KEY_PREFIX);
388
+ }
389
+ const rowValueVariants = cva("min-w-0 flex-1 truncate text-right text-copy-13 text-gray-1000", {
390
+ variants: {
391
+ variant: {
392
+ default: "",
393
+ mono: "font-mono"
394
+ }
395
+ },
396
+ defaultVariants: {
397
+ variant: "default"
398
+ }
399
+ });
400
+ const rowCopyValueVariants = cva("flex min-w-0 flex-1 items-center justify-end gap-1 overflow-hidden text-copy-13 text-gray-1000", {
401
+ variants: {
402
+ variant: {
403
+ default: "",
404
+ mono: "font-mono"
405
+ }
406
+ },
407
+ defaultVariants: {
408
+ variant: "default"
409
+ }
410
+ });
411
+ function DetailKeyValueRowBase({ label, value, copyText, href, removed = false, variant }) {
412
+ const stringValue = typeof value === "string" ? value : void 0;
413
+ return jsxRuntimeExports.jsxs("div", { className: "px-1.5 hover:bg-gray-100 flex justify-between gap-3 -mx-1.5 py-0.5 rounded items-center", children: [jsxRuntimeExports.jsx("span", { className: "flex min-w-0 items-center gap-1.5 truncate text-label-13 text-gray-900", children: label }), removed ? jsxRuntimeExports.jsx("span", { className: "shrink-0 text-copy-13 italic text-gray-700", children: "removed" }) : copyText ? jsxRuntimeExports.jsxs("div", { className: cn(rowCopyValueVariants({ variant })), title: copyText, children: [href ? jsxRuntimeExports.jsxs("a", { href, target: "_blank", rel: "noreferrer", className: cn(rowValueVariants({ variant }), "flex min-w-0 items-center gap-0.5 hover:underline"), children: [jsxRuntimeExports.jsx(MiddleTruncate, { value: copyText, className: "min-w-0 text-right", style: { gridTemplateColumns: "minmax(0, 1fr)" } }), jsxRuntimeExports.jsx(ArrowUpRight, { "aria-hidden": true, className: "h-3 w-3 shrink-0" })] }) : jsxRuntimeExports.jsx(MiddleTruncate, { value: copyText, className: cn(rowValueVariants({ variant, className: "text-right" })), style: { gridTemplateColumns: "minmax(0, 1fr)" } }), jsxRuntimeExports.jsx(CopyButton, { copyText, ariaLabel: `Copy ${label}`, className: "shrink-0 -mr-1" })] }) : jsxRuntimeExports.jsx("span", { className: cn(rowValueVariants({ variant })), title: stringValue, children: href ? jsxRuntimeExports.jsx("a", { href, target: "_blank", rel: "noreferrer", className: "hover:underline", children: value }) : value })] });
414
+ }
415
+ function DetailKeyValueRow(props) {
416
+ return jsxRuntimeExports.jsx(DetailKeyValueRowBase, { ...props });
417
+ }
418
+ function DetailMonoKeyValueRow(props) {
419
+ return jsxRuntimeExports.jsx(DetailKeyValueRowBase, { ...props, variant: "mono" });
420
+ }
421
+ function AttributeRow({ attributeKey, value, removed = false }) {
422
+ return jsxRuntimeExports.jsx(DetailKeyValueRow, { label: attributeKey, value, removed });
423
+ }
424
+ function sortAttributeKeys(keys) {
425
+ return [...keys].sort((a, b) => {
426
+ const aReserved = isReservedAttributeKey(a);
427
+ const bReserved = isReservedAttributeKey(b);
428
+ if (aReserved !== bReserved)
429
+ return aReserved ? 1 : -1;
430
+ return a.localeCompare(b);
431
+ });
432
+ }
433
+ function RunAttributesCard({ attributes }) {
434
+ const keys = sortAttributeKeys(Object.keys(attributes));
435
+ if (keys.length === 0)
436
+ return null;
437
+ return jsxRuntimeExports.jsxs(CollapsibleRoot, { defaultOpen: true, children: [jsxRuntimeExports.jsx(CollapsibleTrigger, { children: "Attributes" }), jsxRuntimeExports.jsx(CollapsibleContent, { className: "mb-4", children: jsxRuntimeExports.jsx("div", { className: "flex flex-col", children: keys.map((key) => jsxRuntimeExports.jsx(AttributeRow, { attributeKey: key, value: attributes[key] }, key)) }) })] });
438
+ }
439
+ function formatMetadataValue(value) {
440
+ if (value == null) {
441
+ return String(value);
442
+ }
443
+ if (typeof value === "string") {
444
+ return value;
445
+ }
446
+ if (typeof value === "number" || typeof value === "boolean") {
447
+ return String(value);
448
+ }
449
+ try {
450
+ return JSON.stringify(value);
451
+ } catch {
452
+ return String(value);
453
+ }
454
+ }
455
+ function isMetadataRecord(value) {
456
+ return value !== null && typeof value === "object" && !Array.isArray(value);
457
+ }
458
+ function RunMetadataCard({ metadata }) {
459
+ if (!isMetadataRecord(metadata)) {
460
+ return jsxRuntimeExports.jsxs(CollapsibleRoot, { defaultOpen: true, children: [jsxRuntimeExports.jsx(CollapsibleTrigger, { children: "Metadata" }), jsxRuntimeExports.jsx(CollapsibleContent, { className: "mb-4", children: jsxRuntimeExports.jsx(CopyableDataBlock, { data: metadata }) })] });
461
+ }
462
+ const keys = Object.keys(metadata).sort((a, b) => a.localeCompare(b));
463
+ if (keys.length === 0)
464
+ return null;
465
+ return jsxRuntimeExports.jsxs(CollapsibleRoot, { defaultOpen: true, children: [jsxRuntimeExports.jsx(CollapsibleTrigger, { children: "Metadata" }), jsxRuntimeExports.jsx(CollapsibleContent, { className: "mb-4", children: jsxRuntimeExports.jsx("div", { className: "flex flex-col", children: keys.map((key) => jsxRuntimeExports.jsx(AttributeRow, { attributeKey: key, value: formatMetadataValue(metadata[key]) }, key)) }) })] });
466
+ }
467
+ function isAttrSetEventData(data) {
468
+ if (data === null || typeof data !== "object")
469
+ return false;
470
+ const record = data;
471
+ return Array.isArray(record.changes) && record.changes.every((c) => c !== null && typeof c === "object" && typeof c.key === "string");
472
+ }
473
+ function describeWriter(writer) {
474
+ if (!writer)
475
+ return null;
476
+ if (writer.type === "workflow")
477
+ return "Set by workflow";
478
+ if (writer.type === "step") {
479
+ return `Set by step ${writer.stepId} (attempt ${writer.attempt})`;
480
+ }
481
+ return null;
482
+ }
483
+ function AttrSetEventBlock({ data }) {
484
+ if (!isAttrSetEventData(data)) {
485
+ return jsxRuntimeExports.jsx(CopyableDataBlock, { data });
486
+ }
487
+ const writerLabel = describeWriter(data.writer);
488
+ return jsxRuntimeExports.jsxs("div", { className: "flex flex-col px-3 py-1", children: [jsxRuntimeExports.jsx("div", { className: "flex flex-col", children: data.changes.map((change, index) => jsxRuntimeExports.jsx(AttributeRow, { attributeKey: change.key, removed: change.value === null, value: change.value ?? void 0 }, `${change.key}:${index}`)) }), writerLabel && jsxRuntimeExports.jsx("div", { className: "border-t py-1.5 text-label-12", style: {
489
+ borderColor: "var(--ds-gray-alpha-400)",
490
+ color: "var(--ds-gray-700)"
491
+ }, children: writerLabel })] });
492
+ }
79
493
  const STYLES$2 = `.wf-decrypt-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;cursor:pointer;white-space:nowrap;gap:6px;transition:background 150ms}.wf-decrypt-idle{color:var(--ds-gray-1000);background:var(--ds-background-100);box-shadow:0 0 0 1px var(--ds-gray-400)}.wf-decrypt-idle:hover{background:var(--ds-gray-alpha-200)}.wf-decrypt-done{color:var(--ds-green-900);background:var(--ds-green-100);box-shadow:0 0 0 1px var(--ds-green-400);cursor:default}`;
80
494
  function DecryptButton({ decrypted = false, loading = false, onClick }) {
81
495
  return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("style", { dangerouslySetInnerHTML: { __html: STYLES$2 } }), jsxRuntimeExports.jsxs("button", { type: "button", onClick: decrypted ? void 0 : onClick, disabled: decrypted || loading, className: `wf-decrypt-btn ${decrypted ? "wf-decrypt-done" : "wf-decrypt-idle"}`, children: [loading ? jsxRuntimeExports.jsx(Spinner, { size: 14 }) : decrypted ? jsxRuntimeExports.jsxs("svg", { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }), jsxRuntimeExports.jsx("path", { d: "M7 11V7a5 5 0 0 1 9.9-1" })] }) : jsxRuntimeExports.jsxs("svg", { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }), jsxRuntimeExports.jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })] }), loading ? "Decrypting…" : decrypted ? "Decrypted" : "Decrypt"] })] });
82
496
  }
83
- function isStructuredErrorWithStack(value) {
84
- return value != null && typeof value === "object" && "stack" in value && typeof value.stack === "string";
497
+ function isStructuredError(value) {
498
+ return value != null && typeof value === "object" && (typeof value.message === "string" || typeof value.stack === "string");
499
+ }
500
+ function deriveTitle(message) {
501
+ const firstLine = message.split("\n").find((line) => line.trim().length > 0) ?? message;
502
+ return firstLine.trim();
85
503
  }
86
504
  function ErrorStackBlock({ value }) {
87
- const toast2 = useToast();
88
- const stack = value.stack;
505
+ const stack = typeof value.stack === "string" ? value.stack : void 0;
89
506
  const message = typeof value.message === "string" ? value.message : void 0;
90
- const copyText = message ? `${message}
507
+ const body = stack ?? message ?? "";
508
+ const title = message ? deriveTitle(message) : deriveTitle(body);
509
+ const copyText = message && stack && !stack.includes(message) ? `${message}
91
510
 
92
- ${stack}` : stack;
511
+ ${stack}` : body;
93
512
  return jsxRuntimeExports.jsxs("div", { className: "relative overflow-hidden rounded-md border", style: {
94
513
  borderColor: "var(--ds-red-400)",
95
514
  background: "var(--ds-red-100)"
96
- }, children: [jsxRuntimeExports.jsx("button", { type: "button", "aria-label": "Copy error", title: "Copy", className: "!absolute !right-2 !top-2 !flex !h-6 !w-6 !items-center !justify-center !rounded-md !border transition-transform transition-colors duration-100 hover:!bg-[var(--ds-red-200)] active:!scale-95", style: {
97
- borderColor: "var(--ds-red-400)",
98
- background: "var(--ds-red-100)",
99
- color: "var(--ds-red-900)"
100
- }, onClick: () => {
101
- navigator.clipboard.writeText(copyText).then(() => {
102
- toast2.success("Copied to clipboard");
103
- }).catch(() => {
104
- toast2.error("Failed to copy");
105
- });
106
- }, children: jsxRuntimeExports.jsx(Copy, { size: 12 }) }), message && jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2 px-3 py-2.5 pr-10", style: {
515
+ }, children: [jsxRuntimeExports.jsx(CopyButton, { copyText, ariaLabel: "Copy error", className: "absolute right-2 top-2 flex h-6 w-6 items-center justify-center rounded-md border border-red-400 bg-red-100 p-0 text-red-900 transition-transform transition-colors duration-100 hover:bg-red-200 active:scale-95" }), title && jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-3 py-2.5 pr-10", style: {
107
516
  color: "var(--ds-red-900)",
108
517
  borderBottom: "1px solid var(--ds-red-400)"
109
- }, children: [jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4 shrink-0", style: { marginTop: 1 } }), jsxRuntimeExports.jsx("p", { className: "text-xs font-semibold m-0 break-words", children: message })] }), jsxRuntimeExports.jsx("pre", { className: "px-3 py-2.5 text-xs font-mono whitespace-pre-wrap break-words overflow-auto m-0", style: {
518
+ }, children: [jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4 shrink-0" }), jsxRuntimeExports.jsx("p", {
519
+ className: "text-label-12 font-semibold m-0 truncate",
520
+ // The full message or stack is in the body below; the header just
521
+ // shows the first line, single-line, with overflow
522
+ // ellipsised so a long title doesn't push the copy button or
523
+ // wrap into the framed hint/docs lines.
524
+ title: message,
525
+ children: title
526
+ })] }), body && jsxRuntimeExports.jsx("pre", { className: "px-3 py-2.5 text-label-12-mono whitespace-pre-wrap break-words overflow-auto m-0", style: {
110
527
  color: "var(--ds-red-900)",
111
528
  background: "var(--ds-red-200)"
112
- }, children: stack })] });
529
+ }, children: body })] });
113
530
  }
114
531
  const STYLES$1 = `.wf-load-more{appearance:none;-webkit-appearance:none;border:none;display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;border-radius:6px;font-size:13px;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;gap:6px;transition:background 150ms}.wf-load-more:hover{background:var(--ds-gray-alpha-200)}.wf-load-more:disabled{opacity:.6;cursor:default}.wf-load-more:disabled:hover{background:var(--ds-background-100)}`;
115
532
  function LoadMoreButton({ loading = false, onClick, label = "Load more", loadingLabel = "Loading..." }) {
@@ -154,190 +571,7 @@ function MenuDropdown({ options, value, onChange }) {
154
571
  setOpen(false);
155
572
  }, children: option.label }, option.value)) })] });
156
573
  }
157
- const TIME_UNITS = [
158
- { unit: "year", ms: 31536e6 },
159
- { unit: "month", ms: 2628e6 },
160
- { unit: "day", ms: 864e5 },
161
- { unit: "hour", ms: 36e5 },
162
- { unit: "minute", ms: 6e4 },
163
- { unit: "second", ms: 1e3 }
164
- ];
165
- function formatTimeDifference(diff) {
166
- let remaining = Math.abs(diff);
167
- const result = [];
168
- for (const { unit, ms } of TIME_UNITS) {
169
- const value = Math.floor(remaining / ms);
170
- if (value > 0 || result.length > 0) {
171
- result.push(`${value} ${unit}${value !== 1 ? "s" : ""}`);
172
- remaining %= ms;
173
- }
174
- if (result.length === 3)
175
- break;
176
- }
177
- return result.join(", ");
178
- }
179
- function useTimeAgo(date) {
180
- const [timeAgo, setTimeAgo] = reactExports.useState("");
181
- reactExports.useEffect(() => {
182
- const update = () => {
183
- const diff = Date.now() - date;
184
- const formatted = formatTimeDifference(diff);
185
- setTimeAgo(formatted ? `${formatted} ago` : "Just now");
186
- };
187
- update();
188
- const timer = setInterval(update, 1e3);
189
- return () => clearInterval(timer);
190
- }, [date]);
191
- return timeAgo;
192
- }
193
- function ZoneDateTimeRow({ date, zone }) {
194
- var _a;
195
- const dateObj = new Date(date);
196
- const formattedZone = ((_a = new Intl.DateTimeFormat("en-US", {
197
- timeZone: zone,
198
- timeZoneName: "short"
199
- }).formatToParts(dateObj).find((part) => part.type === "timeZoneName")) == null ? void 0 : _a.value) || zone;
200
- const formattedDate = dateObj.toLocaleString("en-US", {
201
- timeZone: zone,
202
- year: "numeric",
203
- month: "long",
204
- day: "numeric"
205
- });
206
- const formattedTime = dateObj.toLocaleTimeString("en-US", {
207
- timeZone: zone,
208
- hour: "2-digit",
209
- minute: "2-digit",
210
- second: "2-digit"
211
- });
212
- return jsxRuntimeExports.jsxs("div", { style: {
213
- display: "flex",
214
- alignItems: "center",
215
- justifyContent: "space-between",
216
- gap: 12
217
- }, children: [jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [jsxRuntimeExports.jsx("div", { style: {
218
- display: "inline-flex",
219
- alignItems: "center",
220
- justifyContent: "center",
221
- height: 16,
222
- padding: "0 6px",
223
- backgroundColor: "var(--ds-gray-200)",
224
- borderRadius: 3,
225
- fontSize: 11,
226
- fontFamily: "var(--font-mono, monospace)",
227
- fontWeight: 500,
228
- color: "var(--ds-gray-900)",
229
- whiteSpace: "nowrap"
230
- }, children: formattedZone }), jsxRuntimeExports.jsx("span", { style: {
231
- fontSize: 13,
232
- color: "var(--ds-gray-1000)",
233
- whiteSpace: "nowrap"
234
- }, children: formattedDate })] }), jsxRuntimeExports.jsx("span", { style: {
235
- fontSize: 11,
236
- fontFamily: "var(--font-mono, monospace)",
237
- fontVariantNumeric: "tabular-nums",
238
- color: "var(--ds-gray-900)",
239
- whiteSpace: "nowrap"
240
- }, children: formattedTime })] });
241
- }
242
- function TimestampTooltipContent({ date }) {
243
- const localTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
244
- const timeAgo = useTimeAgo(date);
245
- return jsxRuntimeExports.jsxs("div", { style: {
246
- display: "flex",
247
- flexDirection: "column",
248
- gap: 12,
249
- minWidth: 300,
250
- padding: "12px 14px"
251
- }, children: [jsxRuntimeExports.jsx("span", { style: {
252
- fontSize: 13,
253
- fontVariantNumeric: "tabular-nums",
254
- color: "var(--ds-gray-900)"
255
- }, children: timeAgo }), jsxRuntimeExports.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [jsxRuntimeExports.jsx(ZoneDateTimeRow, { date, zone: "UTC" }), jsxRuntimeExports.jsx(ZoneDateTimeRow, { date, zone: localTimezone })] })] });
256
- }
257
- const TOOLTIP_WIDTH = 330;
258
- const VIEWPORT_PAD = 8;
259
- function TooltipPortal({ triggerRect, onMouseEnter, onMouseLeave, date }) {
260
- const tooltipRef = reactExports.useRef(null);
261
- const [style, setStyle] = reactExports.useState({
262
- position: "fixed",
263
- zIndex: 9999,
264
- visibility: "hidden"
265
- });
266
- reactExports.useEffect(() => {
267
- const placement = triggerRect.top > 240 ? "above" : "below";
268
- const centerX = triggerRect.left + triggerRect.width / 2;
269
- const el = tooltipRef.current;
270
- const w = el ? el.offsetWidth : TOOLTIP_WIDTH;
271
- const h = el ? el.offsetHeight : 100;
272
- let left = centerX - w / 2;
273
- left = Math.max(VIEWPORT_PAD, Math.min(left, window.innerWidth - w - VIEWPORT_PAD));
274
- let top;
275
- if (placement === "above") {
276
- top = triggerRect.top - h - 6;
277
- if (top < VIEWPORT_PAD) {
278
- top = triggerRect.bottom + 6;
279
- }
280
- } else {
281
- top = triggerRect.bottom + 6;
282
- if (top + h > window.innerHeight - VIEWPORT_PAD) {
283
- top = triggerRect.top - h - 6;
284
- }
285
- }
286
- setStyle({
287
- position: "fixed",
288
- left,
289
- top,
290
- zIndex: 9999,
291
- borderRadius: 10,
292
- border: "1px solid var(--ds-gray-alpha-200)",
293
- backgroundColor: "var(--ds-background-100)",
294
- boxShadow: "0 4px 12px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.06)",
295
- visibility: "visible"
296
- });
297
- }, [triggerRect]);
298
- return reactDomExports.createPortal(
299
- // biome-ignore lint/a11y/noStaticElementInteractions: tooltip hover zone
300
- jsxRuntimeExports.jsx("div", { ref: tooltipRef, onMouseEnter, onMouseLeave, style, children: jsxRuntimeExports.jsx(TimestampTooltipContent, { date }) }),
301
- document.body
302
- );
303
- }
304
- function TimestampTooltip({ date, children }) {
305
- const [open, setOpen] = reactExports.useState(false);
306
- const [triggerRect, setTriggerRect] = reactExports.useState(null);
307
- const triggerRef = reactExports.useRef(null);
308
- const closeTimer = reactExports.useRef(null);
309
- reactExports.useEffect(() => {
310
- return () => {
311
- if (closeTimer.current)
312
- clearTimeout(closeTimer.current);
313
- };
314
- }, []);
315
- const ts = date == null ? null : typeof date === "number" ? date : new Date(date).getTime();
316
- if (ts == null || Number.isNaN(ts))
317
- return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children });
318
- const cancelClose = () => {
319
- if (closeTimer.current) {
320
- clearTimeout(closeTimer.current);
321
- closeTimer.current = null;
322
- }
323
- };
324
- const scheduleClose = () => {
325
- cancelClose();
326
- closeTimer.current = setTimeout(() => setOpen(false), 120);
327
- };
328
- const handleOpen = () => {
329
- cancelClose();
330
- if (triggerRef.current) {
331
- setTriggerRect(triggerRef.current.getBoundingClientRect());
332
- }
333
- setOpen(true);
334
- };
335
- return (
336
- // biome-ignore lint/a11y/noStaticElementInteractions: tooltip trigger
337
- jsxRuntimeExports.jsxs("span", { ref: triggerRef, onMouseEnter: handleOpen, onMouseLeave: scheduleClose, style: { display: "inline-flex" }, children: [children, open && triggerRect && jsxRuntimeExports.jsx(TooltipPortal, { triggerRect, onMouseEnter: cancelClose, onMouseLeave: scheduleClose, date: ts })] })
338
- );
339
- }
340
- const ERROR_EVENT_TYPES$1 = /* @__PURE__ */ new Set([
574
+ const ERROR_EVENT_TYPES = /* @__PURE__ */ new Set([
341
575
  "step_failed",
342
576
  "step_retrying",
343
577
  "run_failed",
@@ -358,6 +592,18 @@ function formatEventTime(date) {
358
592
  hour12: false
359
593
  }) + "." + date.getMilliseconds().toString().padStart(3, "0");
360
594
  }
595
+ function parseEventDate(value) {
596
+ if (value == null)
597
+ return null;
598
+ const date = value instanceof Date ? value : new Date(String(value));
599
+ return Number.isNaN(date.getTime()) ? null : date;
600
+ }
601
+ function getEffectiveEventDate$1(event) {
602
+ return parseEventDate(event.occurredAt) ?? new Date(event.createdAt);
603
+ }
604
+ function getEffectiveEventTime(event) {
605
+ return getEffectiveEventDate$1(event).getTime();
606
+ }
361
607
  function formatEventType(eventType) {
362
608
  return eventType.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
363
609
  }
@@ -371,6 +617,9 @@ function getStatusDotColor(eventType) {
371
617
  if (eventType === "step_retrying") {
372
618
  return "var(--ds-amber-700)";
373
619
  }
620
+ if (eventType === "attr_set") {
621
+ return "var(--ds-teal-900)";
622
+ }
374
623
  if (eventType === "step_completed" || eventType === "run_completed" || eventType === "workflow_completed" || eventType === "hook_disposed" || eventType === "wait_completed") {
375
624
  return "var(--ds-green-700)";
376
625
  }
@@ -395,27 +644,34 @@ function buildNameMaps(events, run) {
395
644
  return { correlationNameMap, workflowName };
396
645
  }
397
646
  function buildDurationMap(events) {
647
+ const chronological = [...events].sort((a, b) => getEffectiveEventTime(a) - getEffectiveEventTime(b));
398
648
  const createdTimes = /* @__PURE__ */ new Map();
649
+ const firstStartedTimes = /* @__PURE__ */ new Map();
399
650
  const startedTimes = /* @__PURE__ */ new Map();
400
651
  const durations = /* @__PURE__ */ new Map();
401
- for (const event of events) {
402
- const ts = new Date(event.createdAt).getTime();
652
+ for (const event of chronological) {
653
+ const ts = getEffectiveEventTime(event);
403
654
  const key = event.correlationId ?? "__run__";
404
655
  const type = event.eventType;
405
656
  if (type === "step_created" || type === "run_created") {
406
- createdTimes.set(key, ts);
407
- }
408
- if (type === "step_started" || type === "run_started" || type === "workflow_started") {
409
- startedTimes.set(key, ts);
410
657
  if (!createdTimes.has(key)) {
411
658
  createdTimes.set(key, ts);
412
659
  }
413
- const createdAt = createdTimes.get(key);
414
- const info = durations.get(key) ?? {};
415
- if (createdAt !== void 0) {
416
- info.queued = ts - createdAt;
660
+ }
661
+ if (type === "step_started" || type === "run_started" || type === "workflow_started") {
662
+ startedTimes.set(key, ts);
663
+ if (!firstStartedTimes.has(key)) {
664
+ firstStartedTimes.set(key, ts);
665
+ if (!createdTimes.has(key)) {
666
+ createdTimes.set(key, ts);
667
+ }
668
+ const createdAt = createdTimes.get(key);
669
+ const info = durations.get(key) ?? {};
670
+ if (createdAt !== void 0) {
671
+ info.queued = ts - createdAt;
672
+ }
673
+ durations.set(key, info);
417
674
  }
418
- durations.set(key, info);
419
675
  }
420
676
  if (type === "step_completed" || type === "step_failed" || type === "run_completed" || type === "run_failed" || type === "run_cancelled" || type === "workflow_completed" || type === "workflow_failed" || type === "wait_completed" || type === "hook_disposed") {
421
677
  const startedAt = startedTimes.get(key);
@@ -438,7 +694,9 @@ function hasEncryptedValues(data) {
438
694
  return false;
439
695
  }
440
696
  function isRunLevel(eventType) {
441
- return eventType === "run_created" || eventType === "run_started" || eventType === "run_completed" || eventType === "run_failed" || eventType === "run_cancelled" || eventType === "workflow_started" || eventType === "workflow_completed" || eventType === "workflow_failed";
697
+ return eventType === "run_created" || eventType === "run_started" || eventType === "run_completed" || eventType === "run_failed" || eventType === "run_cancelled" || eventType === "workflow_started" || eventType === "workflow_completed" || eventType === "workflow_failed" || // attr_set carries a dedup correlationId rather than a child entity ID,
698
+ // so it groups and labels with the run itself.
699
+ eventType === "attr_set";
442
700
  }
443
701
  const GUTTER_WIDTH = 36;
444
702
  const LANE_X = 20;
@@ -560,14 +818,14 @@ function deepParseJson(value) {
560
818
  return value;
561
819
  }
562
820
  function extractStructuredError(data, eventType) {
563
- if (!eventType || !ERROR_EVENT_TYPES$1.has(eventType))
821
+ if (!eventType || !ERROR_EVENT_TYPES.has(eventType))
564
822
  return null;
565
823
  if (data == null || typeof data !== "object")
566
824
  return null;
567
825
  const record = data;
568
- if (isStructuredErrorWithStack(record.error))
826
+ if (isStructuredError(record.error))
569
827
  return record.error;
570
- if (isStructuredErrorWithStack(record))
828
+ if (isStructuredError(record))
571
829
  return record;
572
830
  return null;
573
831
  }
@@ -606,13 +864,22 @@ function PayloadBlock({ data, eventType }) {
606
864
  if (structuredError) {
607
865
  return jsxRuntimeExports.jsx("div", { className: "p-2", children: jsxRuntimeExports.jsx(ErrorStackBlock, { value: structuredError }) });
608
866
  }
609
- return jsxRuntimeExports.jsxs("div", { className: "relative group/payload", children: [jsxRuntimeExports.jsx("div", { className: "overflow-x-auto p-2 text-[11px]", style: { color: "var(--ds-gray-1000)" }, children: jsxRuntimeExports.jsx(DataInspector, { data: cleaned, expandLevel: 2 }) }), jsxRuntimeExports.jsx("button", { type: "button", onClick: handleCopy, className: "absolute bottom-2 right-2 opacity-0 group-hover/payload:opacity-100 transition-opacity flex items-center gap-1 px-2 py-1 rounded-md text-xs hover:bg-[var(--ds-gray-alpha-200)]", style: { ...BUTTON_RESET_STYLE, color: "var(--ds-gray-700)" }, "aria-label": "Copy payload", children: copied ? jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(Check, { className: "h-3 w-3", style: { color: "var(--ds-green-700)" } }), jsxRuntimeExports.jsx("span", { style: { color: "var(--ds-green-700)" }, children: "Copied" })] }) : jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(Copy, { className: "h-3 w-3" }), jsxRuntimeExports.jsx("span", { children: "Copy" })] }) })] });
867
+ if (eventType === "attr_set") {
868
+ return jsxRuntimeExports.jsx(AttrSetEventBlock, { data: cleaned });
869
+ }
870
+ if (eventType === "run_cancelled") {
871
+ const cancelReason = cleaned != null && typeof cleaned === "object" && typeof cleaned.cancelReason === "string" ? cleaned.cancelReason : null;
872
+ if (cancelReason) {
873
+ return jsxRuntimeExports.jsxs("div", { className: "p-2 text-label-12", style: { color: "var(--ds-gray-1000)" }, children: [jsxRuntimeExports.jsx("span", { style: { color: "var(--ds-gray-900)" }, children: "Reason: " }), jsxRuntimeExports.jsx("span", { className: "whitespace-pre-wrap break-words", children: cancelReason })] });
874
+ }
875
+ }
876
+ return jsxRuntimeExports.jsxs("div", { className: "relative group/payload", children: [jsxRuntimeExports.jsx("div", { className: "overflow-x-auto p-2 text-[11px]", style: { color: "var(--ds-gray-1000)" }, children: jsxRuntimeExports.jsx(DataInspector, { data: cleaned, expandLevel: 2 }) }), jsxRuntimeExports.jsx("button", { type: "button", onClick: handleCopy, className: "absolute bottom-2 right-2 opacity-0 group-hover/payload:opacity-100 transition-opacity flex items-center gap-1 px-2 py-1 rounded-md text-button-12 hover:bg-[var(--ds-gray-alpha-200)]", style: { ...BUTTON_RESET_STYLE, color: "var(--ds-gray-700)" }, "aria-label": "Copy payload", children: copied ? jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(Check, { className: "h-3 w-3", style: { color: "var(--ds-green-700)" } }), jsxRuntimeExports.jsx("span", { style: { color: "var(--ds-green-700)" }, children: "Copied" })] }) : jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(Copy, { className: "h-3 w-3" }), jsxRuntimeExports.jsx("span", { children: "Copy" })] }) })] });
610
877
  }
611
878
  const SORT_OPTIONS = [
612
879
  { value: "desc", label: "Newest" },
613
880
  { value: "asc", label: "Oldest" }
614
881
  ];
615
- function RowsSkeleton() {
882
+ function RowsSkeleton({ showSeparateEventOccurrenceTimestamps = false }) {
616
883
  return jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-hidden", children: Array.from({ length: 16 }, (_, i) => jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-0", style: { height: 40 }, children: [jsxRuntimeExports.jsxs("div", { className: "relative flex-shrink-0 self-stretch flex items-center", style: { width: GUTTER_WIDTH }, children: [jsxRuntimeExports.jsx("div", { style: {
617
884
  position: "absolute",
618
885
  left: 8,
@@ -624,9 +891,9 @@ function RowsSkeleton() {
624
891
  height: i % 4 === 0 ? 8 : 6,
625
892
  borderRadius: "50%",
626
893
  marginLeft: i % 4 === 0 ? 5 : 6
627
- } })] }), jsxRuntimeExports.jsx("div", { className: "w-5 flex-shrink-0 flex items-center justify-center", children: jsxRuntimeExports.jsx(Skeleton, { className: "w-5 h-5", style: { borderRadius: 4 } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "70%" } }) }), jsxRuntimeExports.jsxs("div", { className: "min-w-0 px-4 flex items-center gap-1.5", style: { flex: "2 1 0%" }, children: [jsxRuntimeExports.jsx(Skeleton, { className: "flex-shrink-0", style: { width: 6, height: 6, borderRadius: "50%" } }), jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "60%" } })] }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "50%" } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "75%" } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "75%" } }) })] }, i)) });
894
+ } })] }), jsxRuntimeExports.jsx("div", { className: "w-5 flex-shrink-0 flex items-center justify-center", children: jsxRuntimeExports.jsx(Skeleton, { className: "w-5 h-5", style: { borderRadius: 4 } }) }), showSeparateEventOccurrenceTimestamps && jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "70%" } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "70%" } }) }), jsxRuntimeExports.jsxs("div", { className: "min-w-0 px-4 flex items-center gap-1.5", style: { flex: "2 1 0%" }, children: [jsxRuntimeExports.jsx(Skeleton, { className: "flex-shrink-0", style: { width: 6, height: 6, borderRadius: "50%" } }), jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "60%" } })] }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "50%" } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "75%" } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "75%" } }) })] }, i)) });
628
895
  }
629
- function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand, activeGroupKey, selectedGroupKey, selectedGroupRange, correlationNameMap, workflowName, durationMap, onSelectGroup, onHoverGroup, onLoadEventData, cachedEventData, onCacheEventData, encryptionKey, onEncryptedDataDetected }) {
896
+ function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand, activeGroupKey, selectedGroupKey, selectedGroupRange, correlationNameMap, workflowName, durationMap, onSelectGroup, onHoverGroup, onLoadEventData, cachedEventData, onCacheEventData, encryptionKey, onEncryptedDataDetected, suppressGroupDimming = false, showSeparateEventOccurrenceTimestamps = false }) {
630
897
  const [isLoading, setIsLoading] = reactExports.useState(false);
631
898
  const [loadedEventData, setLoadedEventData] = reactExports.useState(cachedEventData);
632
899
  const [loadError, setLoadError] = reactExports.useState(null);
@@ -639,6 +906,8 @@ function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand,
639
906
  const rowGroupKey = isRunLevel(event.eventType) ? "__run__" : event.correlationId ?? void 0;
640
907
  const statusDotColor = getStatusDotColor(event.eventType);
641
908
  const createdAt = new Date(event.createdAt);
909
+ const occurredAt = parseEventDate(event.occurredAt);
910
+ const displayedCreatedAt = showSeparateEventOccurrenceTimestamps ? createdAt : getEffectiveEventDate$1(event);
642
911
  const hasExistingEventData = "eventData" in event && event.eventData != null;
643
912
  const isRun = isRunLevel(event.eventType);
644
913
  const eventName = isRun ? workflowName ?? "-" : event.correlationId ? correlationNameMap.get(event.correlationId) ?? "-" : "-";
@@ -646,7 +915,7 @@ function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand,
646
915
  const durationInfo = durationKey ? durationMap.get(durationKey) : void 0;
647
916
  const hasActive = activeGroupKey !== void 0;
648
917
  const isRelated = rowGroupKey !== void 0 && rowGroupKey === activeGroupKey;
649
- const isDimmed = hasActive && !isRelated;
918
+ const isDimmed = hasActive && !isRelated && !suppressGroupDimming;
650
919
  const isPulsing = hasActive && isRelated;
651
920
  const showBranch = hasActive && isRelated && !isRun;
652
921
  const showLaneLine = selectedGroupRange !== null && index >= selectedGroupRange.first && index <= selectedGroupRange.last;
@@ -737,12 +1006,12 @@ function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand,
737
1006
  return jsxRuntimeExports.jsxs("div", { "data-event-id": event.eventId, onMouseEnter: () => onHoverGroup(rowGroupKey), onMouseLeave: () => onHoverGroup(void 0), children: [jsxRuntimeExports.jsxs("div", { role: "button", tabIndex: 0, onClick: handleRowClick, onKeyDown: (e) => {
738
1007
  if (e.key === "Enter" || e.key === " ")
739
1008
  handleRowClick();
740
- }, className: "w-full text-left flex items-center gap-0 text-[13px] hover:bg-[var(--ds-gray-alpha-100)] transition-colors cursor-pointer", style: { minHeight: 40 }, children: [jsxRuntimeExports.jsx(TreeGutter, { isFirst, isLast: isLast && !isExpanded, isRunLevel: isRun, statusDotColor, pulse: isPulsing, hasSelection: hasActive, showBranch, showLaneLine, isLaneStart, isLaneEnd }), jsxRuntimeExports.jsxs("div", { className: "flex items-center flex-1 min-w-0", style: { opacity: contentOpacity, transition: "opacity 150ms" }, children: [jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center w-5 h-5 flex-shrink-0 rounded", style: {
1009
+ }, className: "w-full text-left flex items-center gap-0 text-label-13 hover:bg-[var(--ds-gray-alpha-100)] transition-colors cursor-pointer", style: { minHeight: 40 }, children: [jsxRuntimeExports.jsx(TreeGutter, { isFirst, isLast: isLast && !isExpanded, isRunLevel: isRun, statusDotColor, pulse: isPulsing, hasSelection: hasActive, showBranch, showLaneLine, isLaneStart, isLaneEnd }), jsxRuntimeExports.jsxs("div", { className: "flex items-center flex-1 min-w-0", style: { opacity: contentOpacity, transition: "opacity 150ms" }, children: [jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center w-5 h-5 flex-shrink-0 rounded", style: {
741
1010
  border: "1px solid var(--ds-gray-400)"
742
1011
  }, children: jsxRuntimeExports.jsx(ChevronRight, { className: "h-3 w-3 transition-transform", style: {
743
1012
  color: "var(--ds-gray-900)",
744
1013
  transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)"
745
- } }) }), jsxRuntimeExports.jsx("div", { className: "tabular-nums min-w-0 px-4", style: { color: "var(--ds-gray-900)", flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(TimestampTooltip, { date: createdAt, children: jsxRuntimeExports.jsx("span", { children: formatEventTime(createdAt) }) }) }), jsxRuntimeExports.jsx("div", { className: "font-medium min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5", style: { color: "var(--ds-gray-900)" }, children: [jsxRuntimeExports.jsxs("span", { style: {
1014
+ } }) }), showSeparateEventOccurrenceTimestamps && jsxRuntimeExports.jsx("div", { className: "tabular-nums min-w-0 px-4", style: { color: "var(--ds-gray-900)", flex: "2 1 0%" }, children: occurredAt ? jsxRuntimeExports.jsx(TimestampTooltip, { date: occurredAt, children: jsxRuntimeExports.jsx("span", { children: formatEventTime(occurredAt) }) }) : "-" }), jsxRuntimeExports.jsx("div", { className: "tabular-nums min-w-0 px-4", style: { color: "var(--ds-gray-900)", flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(TimestampTooltip, { date: displayedCreatedAt, children: jsxRuntimeExports.jsx("span", { children: formatEventTime(displayedCreatedAt) }) }) }), jsxRuntimeExports.jsx("div", { className: "font-medium min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5", style: { color: "var(--ds-gray-900)" }, children: [jsxRuntimeExports.jsxs("span", { style: {
746
1015
  position: "relative",
747
1016
  display: "inline-flex",
748
1017
  width: 6,
@@ -765,13 +1034,14 @@ function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand,
765
1034
  borderColor: "var(--ds-gray-alpha-200)",
766
1035
  opacity: contentOpacity,
767
1036
  transition: "opacity 150ms"
768
- }, children: [((durationInfo == null ? void 0 : durationInfo.queued) !== void 0 || (durationInfo == null ? void 0 : durationInfo.ran) !== void 0) && jsxRuntimeExports.jsxs("div", { className: "px-2 pb-1.5 text-xs flex gap-3", style: { color: "var(--ds-gray-900)" }, children: [durationInfo.queued !== void 0 && durationInfo.queued > 0 && jsxRuntimeExports.jsxs("span", { children: ["Queued for", " ", jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatDuration(durationInfo.queued) })] }), durationInfo.ran !== void 0 && jsxRuntimeExports.jsxs("span", { children: ["Ran for", " ", jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatDuration(durationInfo.ran) })] })] }), displayPayload != null ? jsxRuntimeExports.jsx(PayloadBlock, { data: displayPayload, eventType: event.eventType }) : loadError ? jsxRuntimeExports.jsx("div", { className: "rounded-md border p-3 text-xs", style: {
1037
+ }, children: [((durationInfo == null ? void 0 : durationInfo.queued) !== void 0 || (durationInfo == null ? void 0 : durationInfo.ran) !== void 0) && jsxRuntimeExports.jsxs("div", { className: "px-2 pb-1.5 text-label-12 flex gap-3", style: { color: "var(--ds-gray-900)" }, children: [durationInfo.queued !== void 0 && durationInfo.queued > 0 && jsxRuntimeExports.jsxs("span", { children: ["Queued for", " ", jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatDuration(durationInfo.queued) })] }), durationInfo.ran !== void 0 && jsxRuntimeExports.jsxs("span", { children: ["Ran for", " ", jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatDuration(durationInfo.ran) })] })] }), displayPayload != null ? jsxRuntimeExports.jsx(PayloadBlock, { data: displayPayload, eventType: event.eventType }) : loadError ? jsxRuntimeExports.jsx("div", { className: "rounded-md border p-3 text-label-12", style: {
769
1038
  borderColor: "var(--ds-red-400)",
770
1039
  backgroundColor: "var(--ds-red-100)",
771
1040
  color: "var(--ds-red-900)"
772
- }, children: loadError }) : isLoading || loadedEventData === null && !hasAttemptedLoad && event.correlationId ? jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 p-3", children: [jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "75%" } }), jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "50%" } }), jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "60%" } })] }) : jsxRuntimeExports.jsx("div", { className: "p-2 text-xs", style: { color: "var(--ds-gray-900)" }, children: "No data" })] })] })] });
1041
+ }, children: loadError }) : isLoading || loadedEventData === null && !hasAttemptedLoad && event.correlationId ? jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 p-3", children: [jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "75%" } }), jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "50%" } }), jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: "60%" } })] }) : jsxRuntimeExports.jsx("div", { className: "p-2 text-label-12", style: { color: "var(--ds-gray-900)" }, children: "No data" })] })] })] });
773
1042
  }
774
- function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, isLoadingMoreEvents = false, onLoadMoreEvents, encryptionKey, isLoading = false, sortOrder: sortOrderProp, onSortOrderChange, onDecrypt, isDecrypting = false, hasEncryptedData: hasEncryptedDataProp = false }) {
1043
+ function EventListViewInner({ events, run, onLoadEventData, hasMoreEvents = false, isLoadingMoreEvents = false, onLoadMoreEvents, encryptionKey, isLoading = false, sortOrder: sortOrderProp, onSortOrderChange, onDecrypt, isDecrypting = false, hasEncryptedData: hasEncryptedDataProp = false, onExactIdSearch, showSeparateEventOccurrenceTimestamps = false }) {
1044
+ const toast2 = useToast();
775
1045
  const [internalSortOrder, setInternalSortOrder] = reactExports.useState("asc");
776
1046
  const effectiveSortOrder = sortOrderProp ?? internalSortOrder;
777
1047
  const handleSortOrderChange = reactExports.useCallback((order) => {
@@ -781,28 +1051,40 @@ function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, is
781
1051
  setInternalSortOrder(order);
782
1052
  }
783
1053
  }, [onSortOrderChange]);
1054
+ const [searchQuery, setSearchQuery] = reactExports.useState("");
1055
+ const [searchResults, setSearchResults] = reactExports.useState(null);
1056
+ const [searchResultsTruncated, setSearchResultsTruncated] = reactExports.useState(false);
1057
+ const [searchError, setSearchError] = reactExports.useState(null);
1058
+ const [searchLoading, setSearchLoading] = reactExports.useState(false);
1059
+ const [searchNotFound, setSearchNotFound] = reactExports.useState(false);
1060
+ const searchRequestRef = reactExports.useRef(0);
1061
+ const virtuosoRef = reactExports.useRef(null);
1062
+ const parsedSearchId = reactExports.useMemo(() => parseExactWorkflowSearchId(searchQuery), [searchQuery]);
1063
+ const isExactSearchActive = searchResults !== null;
784
1064
  const sortedEvents = reactExports.useMemo(() => {
785
- if (!events || events.length === 0)
1065
+ const sourceEvents = isExactSearchActive ? searchResults : events ?? [];
1066
+ if (sourceEvents.length === 0)
786
1067
  return [];
787
1068
  const dir = effectiveSortOrder === "desc" ? -1 : 1;
788
- return [...events].sort((a, b) => dir * (new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()));
789
- }, [events, effectiveSortOrder]);
1069
+ return [...sourceEvents].sort((a, b) => dir * (getEffectiveEventTime(a) - getEffectiveEventTime(b)));
1070
+ }, [events, effectiveSortOrder, isExactSearchActive, searchResults]);
790
1071
  const hasEncryptedInlineData = reactExports.useMemo(() => {
791
- if (!events)
1072
+ const sourceEvents = isExactSearchActive ? searchResults : events;
1073
+ if (!sourceEvents)
792
1074
  return false;
793
- for (const event of events) {
1075
+ for (const event of sourceEvents) {
794
1076
  const ed = event.eventData;
795
1077
  if (hasEncryptedValues(ed))
796
1078
  return true;
797
1079
  }
798
1080
  return false;
799
- }, [events]);
1081
+ }, [events, isExactSearchActive, searchResults]);
800
1082
  const [foundEncryptedInLazyData, setFoundEncryptedInLazyData] = reactExports.useState(false);
801
1083
  const handleEncryptedDataDetected = reactExports.useCallback(() => {
802
1084
  setFoundEncryptedInLazyData(true);
803
1085
  }, []);
804
1086
  const hasEncryptedData = hasEncryptedDataProp || hasEncryptedInlineData || foundEncryptedInLazyData;
805
- const { correlationNameMap, workflowName } = reactExports.useMemo(() => buildNameMaps(events ?? null, run ?? null), [events, run]);
1087
+ const { correlationNameMap, workflowName } = reactExports.useMemo(() => buildNameMaps(isExactSearchActive ? searchResults : events ?? null, run ?? null), [events, isExactSearchActive, run, searchResults]);
806
1088
  const durationMap = reactExports.useMemo(() => buildDurationMap(sortedEvents), [sortedEvents]);
807
1089
  const [selectedGroupKey, setSelectedGroupKey] = reactExports.useState(void 0);
808
1090
  const [hoveredGroupKey, setHoveredGroupKey] = reactExports.useState(void 0);
@@ -870,123 +1152,273 @@ function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, is
870
1152
  }
871
1153
  return first >= 0 ? { first, last } : null;
872
1154
  }, [activeGroupKey, sortedEvents]);
873
- const [searchQuery, setSearchQuery] = reactExports.useState("");
874
- const virtuosoRef = reactExports.useRef(null);
875
- const searchIndex = reactExports.useMemo(() => {
876
- const entries = [];
877
- for (let i = 0; i < sortedEvents.length; i++) {
878
- const ev = sortedEvents[i];
879
- const isRun = isRunLevel(ev.eventType);
880
- const name = isRun ? workflowName ?? "" : ev.correlationId ? correlationNameMap.get(ev.correlationId) ?? "" : "";
881
- entries.push({
882
- fields: [
883
- ev.eventId,
884
- ev.correlationId ?? "",
885
- ev.eventType,
886
- formatEventType(ev.eventType),
887
- name
888
- ].map((f) => f.toLowerCase()),
889
- groupKey: ev.correlationId ?? (isRun ? "__run__" : void 0),
890
- eventId: ev.eventId,
891
- index: i
892
- });
893
- }
894
- return entries;
895
- }, [sortedEvents, correlationNameMap, workflowName]);
896
1155
  reactExports.useEffect(() => {
897
- var _a;
898
- const q = searchQuery.trim().toLowerCase();
899
- if (!q) {
1156
+ const trimmed = searchQuery.trim();
1157
+ if (!trimmed) {
1158
+ searchRequestRef.current += 1;
1159
+ setSearchResults(null);
1160
+ setSearchResultsTruncated(false);
1161
+ setSearchError(null);
1162
+ setSearchLoading(false);
1163
+ setSearchNotFound(false);
900
1164
  setSelectedGroupKey(void 0);
901
1165
  return;
902
1166
  }
903
- let bestMatch = null;
904
- let bestScore = 0;
905
- for (const entry of searchIndex) {
906
- for (const field of entry.fields) {
907
- if (field && field.includes(q)) {
908
- const score = q.length / field.length;
909
- if (score > bestScore) {
910
- bestScore = score;
911
- bestMatch = entry;
1167
+ const parsed = parseExactWorkflowSearchId(trimmed);
1168
+ if (!parsed || !onExactIdSearch) {
1169
+ setSearchResults(null);
1170
+ setSearchLoading(false);
1171
+ setSearchNotFound(false);
1172
+ return;
1173
+ }
1174
+ const requestId = ++searchRequestRef.current;
1175
+ setSearchLoading(true);
1176
+ setSearchNotFound(false);
1177
+ setSearchError(null);
1178
+ const abortController = new AbortController();
1179
+ const timer = setTimeout(() => {
1180
+ void (async () => {
1181
+ var _a;
1182
+ try {
1183
+ const results = await onExactIdSearch(parsed.id, parsed.kind, abortController.signal);
1184
+ if (abortController.signal.aborted || searchRequestRef.current !== requestId) {
1185
+ return;
1186
+ }
1187
+ if (results.status === "error") {
1188
+ setSearchResults([]);
1189
+ setSearchResultsTruncated(false);
1190
+ setSearchNotFound(false);
1191
+ setSearchError(results.message);
1192
+ setSelectedGroupKey(void 0);
1193
+ return;
1194
+ }
1195
+ if (results.status === "not_found" || results.status === "ok" && results.events.length === 0) {
1196
+ setSearchResults([]);
1197
+ setSearchResultsTruncated(false);
1198
+ setSearchNotFound(true);
1199
+ setSearchError(null);
1200
+ setSelectedGroupKey(void 0);
1201
+ return;
1202
+ }
1203
+ setSearchResults(results.events);
1204
+ setSearchResultsTruncated(Boolean(results.truncated));
1205
+ setSearchNotFound(false);
1206
+ setSearchError(null);
1207
+ setSelectedGroupKey(parsed.kind === "event" ? (() => {
1208
+ const first = results.events[0];
1209
+ if (!first)
1210
+ return void 0;
1211
+ return isRunLevel(first.eventType) ? "__run__" : first.correlationId ?? void 0;
1212
+ })() : parsed.id);
1213
+ (_a = virtuosoRef.current) == null ? void 0 : _a.scrollToIndex({
1214
+ index: 0,
1215
+ align: "start",
1216
+ behavior: "smooth"
1217
+ });
1218
+ } catch {
1219
+ if (abortController.signal.aborted || searchRequestRef.current !== requestId) {
1220
+ return;
1221
+ }
1222
+ setSearchResults([]);
1223
+ setSearchResultsTruncated(false);
1224
+ setSearchNotFound(false);
1225
+ setSearchError("Failed to search events. Try again.");
1226
+ setSelectedGroupKey(void 0);
1227
+ } finally {
1228
+ if (searchRequestRef.current === requestId && !abortController.signal.aborted) {
1229
+ setSearchLoading(false);
912
1230
  }
913
1231
  }
914
- }
1232
+ })();
1233
+ }, 300);
1234
+ return () => {
1235
+ clearTimeout(timer);
1236
+ abortController.abort();
1237
+ };
1238
+ }, [searchQuery, onExactIdSearch]);
1239
+ const handleSearchKeyDown = reactExports.useCallback((event) => {
1240
+ if (event.key !== "Enter") {
1241
+ return;
915
1242
  }
916
- if (bestMatch) {
917
- setSelectedGroupKey(bestMatch.groupKey);
918
- (_a = virtuosoRef.current) == null ? void 0 : _a.scrollToIndex({
919
- index: bestMatch.index,
920
- align: "center",
921
- behavior: "smooth"
922
- });
1243
+ const trimmed = searchQuery.trim();
1244
+ if (!trimmed || parseExactWorkflowSearchId(trimmed) || !onExactIdSearch || !looksLikeWorkflowIdSearchInput(trimmed)) {
1245
+ return;
923
1246
  }
924
- }, [searchQuery, searchIndex]);
1247
+ toast2.info("Enter a full step ID, wait ID, hook ID, or event ID");
1248
+ }, [searchQuery, onExactIdSearch, toast2]);
925
1249
  const hasHadEventsRef = reactExports.useRef(false);
926
1250
  if (sortedEvents.length > 0) {
927
1251
  hasHadEventsRef.current = true;
928
1252
  }
929
- const isInitialLoad = isLoading && !hasHadEventsRef.current;
930
- const isRefetching = isLoading && hasHadEventsRef.current && sortedEvents.length === 0;
931
- if (isInitialLoad) {
932
- return jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col overflow-hidden", children: [jsxRuntimeExports.jsx("div", { style: { padding: 6 }, children: jsxRuntimeExports.jsx(Skeleton, { style: { height: 40, borderRadius: 6 } }) }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-0 h-10 border-b flex-shrink-0", style: { borderColor: "var(--ds-gray-alpha-200)" }, children: [jsxRuntimeExports.jsx("div", { className: "flex-shrink-0", style: { width: GUTTER_WIDTH } }), jsxRuntimeExports.jsx("div", { className: "w-5 flex-shrink-0" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: 40 } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: 72 } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: 44 } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: 92 } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: 60 } }) })] }), jsxRuntimeExports.jsx(RowsSkeleton, {})] });
1253
+ const isInitialLoad = isLoading && !hasHadEventsRef.current;
1254
+ const isRefetching = isLoading && hasHadEventsRef.current && sortedEvents.length === 0;
1255
+ if (isInitialLoad) {
1256
+ return jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col overflow-hidden", children: [jsxRuntimeExports.jsx("div", { style: { padding: 6 }, children: jsxRuntimeExports.jsx(Skeleton, { style: { height: 40, borderRadius: 6 } }) }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-0 h-10 border-b flex-shrink-0", style: { borderColor: "var(--ds-gray-alpha-200)" }, children: [jsxRuntimeExports.jsx("div", { className: "flex-shrink-0", style: { width: GUTTER_WIDTH } }), jsxRuntimeExports.jsx("div", { className: "w-5 flex-shrink-0" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: 40 } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: 72 } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: 44 } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: 92 } }) }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: jsxRuntimeExports.jsx(Skeleton, { className: "h-3", style: { width: 60 } }) })] }), jsxRuntimeExports.jsx(RowsSkeleton, {})] });
1257
+ }
1258
+ return jsxRuntimeExports.jsx(DecryptClickContext.Provider, { value: onDecrypt ? { onDecrypt, isDecrypting } : void 0, children: jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col overflow-hidden", children: [jsxRuntimeExports.jsx("style", { children: `@keyframes workflow-dot-pulse{0%{transform:scale(1);opacity:.7}70%,100%{transform:scale(2.2);opacity:0}}` }), jsxRuntimeExports.jsxs("div", { style: {
1259
+ padding: 6,
1260
+ backgroundColor: "var(--ds-background-100)",
1261
+ display: "flex",
1262
+ gap: 6
1263
+ }, children: [jsxRuntimeExports.jsxs("label", { style: {
1264
+ display: "flex",
1265
+ alignItems: "center",
1266
+ justifyContent: "center",
1267
+ borderRadius: 6,
1268
+ boxShadow: "0 0 0 1px var(--ds-gray-alpha-400)",
1269
+ background: "var(--ds-background-100)",
1270
+ height: 40,
1271
+ flex: 1,
1272
+ minWidth: 0
1273
+ }, children: [jsxRuntimeExports.jsx("div", { style: {
1274
+ width: 40,
1275
+ height: 40,
1276
+ display: "flex",
1277
+ alignItems: "center",
1278
+ justifyContent: "center",
1279
+ color: "var(--ds-gray-800)",
1280
+ flexShrink: 0
1281
+ }, children: jsxRuntimeExports.jsxs("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", focusable: "false", children: [jsxRuntimeExports.jsx("circle", { cx: "7", cy: "7", r: "4.5", stroke: "currentColor", strokeWidth: "1.5" }), jsxRuntimeExports.jsx("path", { d: "M11.5 11.5L14 14", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })] }) }), jsxRuntimeExports.jsx("input", { type: "search", placeholder: "Search by step ID, wait ID, hook ID, or event ID…", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onKeyDown: handleSearchKeyDown, disabled: !onExactIdSearch, title: onExactIdSearch ? void 0 : "Exact ID search is unavailable in this view.", style: {
1282
+ marginLeft: -16,
1283
+ paddingInline: 12,
1284
+ fontFamily: "inherit",
1285
+ fontSize: 14,
1286
+ background: "transparent",
1287
+ border: "none",
1288
+ outline: "none",
1289
+ height: 40,
1290
+ width: "100%",
1291
+ opacity: onExactIdSearch ? 1 : 0.5,
1292
+ cursor: onExactIdSearch ? "text" : "not-allowed"
1293
+ } })] }), jsxRuntimeExports.jsx(MenuDropdown, { options: SORT_OPTIONS, value: effectiveSortOrder, onChange: handleSortOrderChange }), (hasEncryptedData || encryptionKey) && onDecrypt && jsxRuntimeExports.jsx(DecryptButton, { decrypted: !!encryptionKey, loading: isDecrypting, onClick: onDecrypt })] }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-0 text-label-13 font-medium h-10 border-b flex-shrink-0", style: {
1294
+ borderColor: "var(--ds-gray-alpha-200)",
1295
+ color: "var(--ds-gray-900)",
1296
+ backgroundColor: "var(--ds-background-100)"
1297
+ }, children: [jsxRuntimeExports.jsx("div", { className: "flex-shrink-0", style: { width: GUTTER_WIDTH } }), jsxRuntimeExports.jsx("div", { className: "w-5 flex-shrink-0" }), showSeparateEventOccurrenceTimestamps && jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: "Occurred" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: "Created" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: "Event Type" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: "Name" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: "Correlation ID" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: "Event ID" })] }), isRefetching || searchLoading ? jsxRuntimeExports.jsx(RowsSkeleton, { showSeparateEventOccurrenceTimestamps }) : sortedEvents.length === 0 ? jsxRuntimeExports.jsx("div", { className: "flex flex-1 items-center justify-center px-6 text-center text-copy-14", style: { color: "var(--ds-gray-700)" }, children: searchNotFound && searchQuery.trim() ? `No events found for ${searchQuery.trim()}` : searchError ? searchError : parsedSearchId && searchQuery.trim() && !onExactIdSearch ? "Exact ID search is unavailable in this view." : "No events found" }) : jsxRuntimeExports.jsx(Yr, { ref: virtuosoRef, totalCount: sortedEvents.length, overscan: 20, defaultItemHeight: 40, endReached: () => {
1298
+ if (isExactSearchActive || !hasMoreEvents || isLoadingMoreEvents) {
1299
+ return;
1300
+ }
1301
+ void (onLoadMoreEvents == null ? void 0 : onLoadMoreEvents());
1302
+ }, itemContent: (index) => {
1303
+ const ev = sortedEvents[index];
1304
+ return jsxRuntimeExports.jsx(EventRow$1, { event: ev, index, isFirst: index === 0, isLast: index === sortedEvents.length - 1, isExpanded: expandedEventIds.has(ev.eventId), onToggleExpand: toggleEventExpanded, activeGroupKey, selectedGroupKey, selectedGroupRange, correlationNameMap, workflowName, durationMap, onSelectGroup, onHoverGroup, onLoadEventData, cachedEventData: eventDataCacheRef.current.get(ev.eventId) ?? null, onCacheEventData: cacheEventData, encryptionKey, onEncryptedDataDetected: handleEncryptedDataDetected, suppressGroupDimming: isExactSearchActive, showSeparateEventOccurrenceTimestamps });
1305
+ }, style: { flex: 1, minHeight: 0 } }), jsxRuntimeExports.jsxs("div", { className: "relative flex-shrink-0 flex items-center h-10 border-t px-4 text-label-12", style: {
1306
+ borderColor: "var(--ds-gray-alpha-200)",
1307
+ color: "var(--ds-gray-900)",
1308
+ backgroundColor: "var(--ds-background-100)"
1309
+ }, children: [jsxRuntimeExports.jsx("span", { children: isExactSearchActive ? searchError ? searchError : searchNotFound ? `No events found for ${searchQuery.trim()}` : `${sortedEvents.length} event${sortedEvents.length !== 1 ? "s" : ""} for ${searchQuery.trim()}${searchResultsTruncated ? " (results may be truncated)" : ""}` : `${sortedEvents.length} event${sortedEvents.length !== 1 ? "s" : ""} loaded` }), !isExactSearchActive && hasMoreEvents && jsxRuntimeExports.jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: jsxRuntimeExports.jsx("div", { className: "pointer-events-auto", children: jsxRuntimeExports.jsx(LoadMoreButton, { loading: isLoadingMoreEvents, onClick: () => void (onLoadMoreEvents == null ? void 0 : onLoadMoreEvents()) }) }) })] })] }) });
1310
+ }
1311
+ function EventListView(props) {
1312
+ return jsxRuntimeExports.jsx(ContextCardProvider, { children: jsxRuntimeExports.jsx(EventListViewInner, { ...props }) });
1313
+ }
1314
+ function ConversationView({ messages }) {
1315
+ if (messages.length === 0) {
1316
+ return jsxRuntimeExports.jsx("div", { className: "text-center py-8 text-[11px]", style: { color: "var(--ds-gray-600)" }, children: "No messages" });
1317
+ }
1318
+ return jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-3 p-3", children: messages.map((message, index) => jsxRuntimeExports.jsx(MessageBubble, { message }, index)) });
1319
+ }
1320
+ function MessageBubble({ message }) {
1321
+ const role = message.role;
1322
+ const parts = parseContent(message.content);
1323
+ const style = getRoleStyle(role);
1324
+ return jsxRuntimeExports.jsxs("div", { className: "rounded-md border text-[11px]", style: {
1325
+ backgroundColor: style.bg,
1326
+ borderColor: style.border
1327
+ }, children: [jsxRuntimeExports.jsx("div", { className: "px-2.5 py-1 border-b text-[10px] font-medium uppercase tracking-wide", style: {
1328
+ borderColor: style.border,
1329
+ color: style.label
1330
+ }, children: role }), jsxRuntimeExports.jsx("div", { className: "px-2.5 py-2 space-y-2", children: parts.map((part, i) => jsxRuntimeExports.jsx(ContentPart, { part, role }, i)) })] });
1331
+ }
1332
+ function ContentPart({ part, role }) {
1333
+ if (part.type === "text") {
1334
+ if (!part.text)
1335
+ return null;
1336
+ if (role === "assistant") {
1337
+ return jsxRuntimeExports.jsx("div", { className: "prose prose-sm max-w-none text-[11px] [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", style: { color: "var(--ds-gray-1000)" }, children: jsxRuntimeExports.jsx(_r, { children: part.text }) });
1338
+ }
1339
+ return jsxRuntimeExports.jsx("div", { className: "whitespace-pre-wrap break-words", style: { color: "var(--ds-gray-1000)" }, children: part.text });
1340
+ }
1341
+ if (part.type === "tool-call") {
1342
+ return jsxRuntimeExports.jsxs("div", { className: "rounded border px-2 py-1.5", style: {
1343
+ backgroundColor: "var(--ds-purple-100)",
1344
+ borderColor: "var(--ds-purple-300)"
1345
+ }, children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-[10px] font-medium", children: [jsxRuntimeExports.jsx("span", { children: "🔧" }), jsxRuntimeExports.jsx("span", { style: { color: "var(--ds-purple-900)" }, children: part.toolName })] }), part.input != null && jsxRuntimeExports.jsx("div", { className: "mt-1.5 overflow-x-auto p-1.5 rounded", style: { backgroundColor: "var(--ds-gray-100)" }, children: typeof part.input === "string" ? jsxRuntimeExports.jsx("pre", { className: "text-[10px]", style: { color: "var(--ds-gray-800)" }, children: part.input }) : jsxRuntimeExports.jsx(DataInspector, { data: part.input }) })] });
1346
+ }
1347
+ if (part.type === "tool-result") {
1348
+ return jsxRuntimeExports.jsxs("div", { className: "rounded border px-2 py-1.5", style: {
1349
+ backgroundColor: "var(--ds-green-100)",
1350
+ borderColor: "var(--ds-green-300)"
1351
+ }, children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-[10px] font-medium", children: [jsxRuntimeExports.jsx("span", { children: "✓" }), jsxRuntimeExports.jsxs("span", { style: { color: "var(--ds-green-900)" }, children: [part.toolName, " result"] })] }), part.output != null && jsxRuntimeExports.jsx("div", { className: "mt-1.5 overflow-x-auto max-h-[200px] overflow-y-auto p-1.5 rounded", style: { backgroundColor: "var(--ds-gray-100)" }, children: typeof part.output === "string" ? jsxRuntimeExports.jsx("pre", { className: "text-[10px]", style: { color: "var(--ds-gray-800)" }, children: part.output }) : jsxRuntimeExports.jsx(DataInspector, { data: part.output, expandLevel: 1 }) })] });
1352
+ }
1353
+ return null;
1354
+ }
1355
+ function getRoleStyle(role) {
1356
+ switch (role) {
1357
+ case "user":
1358
+ return {
1359
+ bg: "var(--ds-blue-100)",
1360
+ border: "var(--ds-blue-300)",
1361
+ label: "var(--ds-blue-700)"
1362
+ };
1363
+ case "assistant":
1364
+ return {
1365
+ bg: "var(--ds-gray-100)",
1366
+ border: "var(--ds-gray-300)",
1367
+ label: "var(--ds-gray-700)"
1368
+ };
1369
+ case "system":
1370
+ return {
1371
+ bg: "var(--ds-amber-100)",
1372
+ border: "var(--ds-amber-300)",
1373
+ label: "var(--ds-amber-700)"
1374
+ };
1375
+ case "tool":
1376
+ return {
1377
+ bg: "var(--ds-green-50)",
1378
+ border: "var(--ds-green-300)",
1379
+ label: "var(--ds-green-700)"
1380
+ };
1381
+ default:
1382
+ return {
1383
+ bg: "var(--ds-gray-100)",
1384
+ border: "var(--ds-gray-300)",
1385
+ label: "var(--ds-gray-700)"
1386
+ };
1387
+ }
1388
+ }
1389
+ function parseContent(content) {
1390
+ if (typeof content === "string") {
1391
+ return [{ type: "text", text: content }];
933
1392
  }
934
- if (!isLoading && (!events || events.length === 0)) {
935
- return jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-full text-sm", style: { color: "var(--ds-gray-700)" }, children: "No events found" });
1393
+ if (Array.isArray(content)) {
1394
+ return content.map((part) => {
1395
+ if (typeof part === "string") {
1396
+ return { type: "text", text: part };
1397
+ }
1398
+ if ((part == null ? void 0 : part.type) === "text") {
1399
+ return { type: "text", text: String(part.text ?? "") };
1400
+ }
1401
+ if ((part == null ? void 0 : part.type) === "tool-call") {
1402
+ return {
1403
+ type: "tool-call",
1404
+ toolName: part.toolName,
1405
+ input: part.input
1406
+ };
1407
+ }
1408
+ if ((part == null ? void 0 : part.type) === "tool-result") {
1409
+ return {
1410
+ type: "tool-result",
1411
+ toolName: part.toolName,
1412
+ output: part.output
1413
+ };
1414
+ }
1415
+ return { type: "text", text: "" };
1416
+ });
936
1417
  }
937
- return jsxRuntimeExports.jsx(DecryptClickContext.Provider, { value: onDecrypt ? { onDecrypt, isDecrypting } : void 0, children: jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col overflow-hidden", children: [jsxRuntimeExports.jsx("style", { children: `@keyframes workflow-dot-pulse{0%{transform:scale(1);opacity:.7}70%,100%{transform:scale(2.2);opacity:0}}` }), jsxRuntimeExports.jsxs("div", { style: {
938
- padding: 6,
939
- backgroundColor: "var(--ds-background-100)",
940
- display: "flex",
941
- gap: 6
942
- }, children: [jsxRuntimeExports.jsxs("label", { style: {
943
- display: "flex",
944
- alignItems: "center",
945
- justifyContent: "center",
946
- borderRadius: 6,
947
- boxShadow: "0 0 0 1px var(--ds-gray-alpha-400)",
948
- background: "var(--ds-background-100)",
949
- height: 40,
950
- flex: 1,
951
- minWidth: 0
952
- }, children: [jsxRuntimeExports.jsx("div", { style: {
953
- width: 40,
954
- height: 40,
955
- display: "flex",
956
- alignItems: "center",
957
- justifyContent: "center",
958
- color: "var(--ds-gray-800)",
959
- flexShrink: 0
960
- }, children: jsxRuntimeExports.jsxs("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", focusable: "false", children: [jsxRuntimeExports.jsx("circle", { cx: "7", cy: "7", r: "4.5", stroke: "currentColor", strokeWidth: "1.5" }), jsxRuntimeExports.jsx("path", { d: "M11.5 11.5L14 14", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })] }) }), jsxRuntimeExports.jsx("input", { type: "search", placeholder: "Search by name, event type, or ID…", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: {
961
- marginLeft: -16,
962
- paddingInline: 12,
963
- fontFamily: "inherit",
964
- fontSize: 14,
965
- background: "transparent",
966
- border: "none",
967
- outline: "none",
968
- height: 40,
969
- width: "100%"
970
- } })] }), jsxRuntimeExports.jsx(MenuDropdown, { options: SORT_OPTIONS, value: effectiveSortOrder, onChange: handleSortOrderChange }), (hasEncryptedData || encryptionKey) && onDecrypt && jsxRuntimeExports.jsx(DecryptButton, { decrypted: !!encryptionKey, loading: isDecrypting, onClick: onDecrypt })] }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-0 text-[13px] font-medium h-10 border-b flex-shrink-0", style: {
971
- borderColor: "var(--ds-gray-alpha-200)",
972
- color: "var(--ds-gray-900)",
973
- backgroundColor: "var(--ds-background-100)"
974
- }, children: [jsxRuntimeExports.jsx("div", { className: "flex-shrink-0", style: { width: GUTTER_WIDTH } }), jsxRuntimeExports.jsx("div", { className: "w-5 flex-shrink-0" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: "Time" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: "Event Type" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "2 1 0%" }, children: "Name" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: "Correlation ID" }), jsxRuntimeExports.jsx("div", { className: "min-w-0 px-4", style: { flex: "3 1 0%" }, children: "Event ID" })] }), isRefetching ? jsxRuntimeExports.jsx(RowsSkeleton, {}) : jsxRuntimeExports.jsx(Yr, { ref: virtuosoRef, totalCount: sortedEvents.length, overscan: 20, defaultItemHeight: 40, endReached: () => {
975
- if (!hasMoreEvents || isLoadingMoreEvents) {
976
- return;
977
- }
978
- void (onLoadMoreEvents == null ? void 0 : onLoadMoreEvents());
979
- }, itemContent: (index) => {
980
- const ev = sortedEvents[index];
981
- return jsxRuntimeExports.jsx(EventRow$1, { event: ev, index, isFirst: index === 0, isLast: index === sortedEvents.length - 1, isExpanded: expandedEventIds.has(ev.eventId), onToggleExpand: toggleEventExpanded, activeGroupKey, selectedGroupKey, selectedGroupRange, correlationNameMap, workflowName, durationMap, onSelectGroup, onHoverGroup, onLoadEventData, cachedEventData: eventDataCacheRef.current.get(ev.eventId) ?? null, onCacheEventData: cacheEventData, encryptionKey, onEncryptedDataDetected: handleEncryptedDataDetected });
982
- }, style: { flex: 1, minHeight: 0 } }), jsxRuntimeExports.jsxs("div", { className: "relative flex-shrink-0 flex items-center h-10 border-t px-4 text-xs", style: {
983
- borderColor: "var(--ds-gray-alpha-200)",
984
- color: "var(--ds-gray-900)",
985
- backgroundColor: "var(--ds-background-100)"
986
- }, children: [jsxRuntimeExports.jsxs("span", { children: [sortedEvents.length, " event", sortedEvents.length !== 1 ? "s" : "", " loaded"] }), hasMoreEvents && jsxRuntimeExports.jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: jsxRuntimeExports.jsx("div", { className: "pointer-events-auto", children: jsxRuntimeExports.jsx(LoadMoreButton, { loading: isLoadingMoreEvents, onClick: () => void (onLoadMoreEvents == null ? void 0 : onLoadMoreEvents()) }) }) })] })] }) });
1418
+ return [];
987
1419
  }
988
1420
  function shouldShowVerticalLine(eventType) {
989
- if (eventType === "hook_created" || eventType === "hook_received" || eventType === "hook_disposed") {
1421
+ if (isHookLifecycleEventType(eventType)) {
990
1422
  return true;
991
1423
  }
992
1424
  return false;
@@ -1037,12 +1469,20 @@ const MARKER_EVENT_TYPES = /* @__PURE__ */ new Set([
1037
1469
  "step_failed",
1038
1470
  "run_failed",
1039
1471
  "wait_created",
1040
- "wait_completed"
1472
+ "wait_completed",
1473
+ "attr_set"
1041
1474
  ]);
1042
- function convertEventsToSpanEvents(events, filterTypes = true) {
1475
+ const getEventTimestamp = (event) => {
1476
+ const value = (event == null ? void 0 : event.occurredAt) ?? (event == null ? void 0 : event.createdAt);
1477
+ if (!value)
1478
+ return void 0;
1479
+ const date = value instanceof Date ? value : new Date(value);
1480
+ return Number.isNaN(date.getTime()) ? void 0 : date;
1481
+ };
1482
+ function convertEventsToSpanEvents(events, filterTypes = true, options = {}) {
1043
1483
  return events.filter((event) => filterTypes ? MARKER_EVENT_TYPES.has(event.eventType) : true).map((event) => ({
1044
1484
  name: event.eventType,
1045
- timestamp: dateToOtelTime(event.createdAt),
1485
+ timestamp: dateToOtelTime(options.preferOccurredAt ? getEventTimestamp(event) ?? event.createdAt : event.createdAt),
1046
1486
  attributes: {
1047
1487
  eventId: event.eventId,
1048
1488
  correlationId: event.correlationId,
@@ -1062,9 +1502,9 @@ const waitEventsToWaitEntity = (events) => {
1062
1502
  return {
1063
1503
  waitId: startEvent.correlationId,
1064
1504
  runId: startEvent.runId,
1065
- createdAt: startEvent.createdAt,
1505
+ createdAt: getEventTimestamp(startEvent) ?? startEvent.createdAt,
1066
1506
  resumeAt: ((_a = startEvent.eventData) == null ? void 0 : _a.resumeAt) ? new Date(startEvent.eventData.resumeAt) : void 0,
1067
- completedAt: completedEvent == null ? void 0 : completedEvent.createdAt
1507
+ completedAt: getEventTimestamp(completedEvent)
1068
1508
  };
1069
1509
  };
1070
1510
  function waitToSpan(events, maxEndTime, fallbackEndTime = maxEndTime) {
@@ -1121,16 +1561,16 @@ const stepEventsToStepEntity = (events) => {
1121
1561
  status = "running";
1122
1562
  attempt += 1;
1123
1563
  if (!startedAt)
1124
- startedAt = e.createdAt;
1564
+ startedAt = getEventTimestamp(e) ?? e.createdAt;
1125
1565
  completedAt = void 0;
1126
1566
  break;
1127
1567
  case "step_completed":
1128
1568
  status = "completed";
1129
- completedAt = e.createdAt;
1569
+ completedAt = getEventTimestamp(e) ?? e.createdAt;
1130
1570
  break;
1131
1571
  case "step_failed":
1132
1572
  status = "failed";
1133
- completedAt = e.createdAt;
1573
+ completedAt = getEventTimestamp(e) ?? e.createdAt;
1134
1574
  break;
1135
1575
  case "step_retrying":
1136
1576
  status = "pending";
@@ -1147,8 +1587,8 @@ const stepEventsToStepEntity = (events) => {
1147
1587
  stepName: ((_a = createdEvent == null ? void 0 : createdEvent.eventData) == null ? void 0 : _a.stepName) ?? "",
1148
1588
  status,
1149
1589
  attempt,
1150
- createdAt: anchorEvent.createdAt,
1151
- updatedAt: (lastEvent == null ? void 0 : lastEvent.createdAt) ?? anchorEvent.createdAt,
1590
+ createdAt: getEventTimestamp(anchorEvent) ?? anchorEvent.createdAt,
1591
+ updatedAt: getEventTimestamp(lastEvent) ?? (lastEvent == null ? void 0 : lastEvent.createdAt) ?? anchorEvent.createdAt,
1152
1592
  startedAt,
1153
1593
  completedAt,
1154
1594
  specVersion: anchorEvent.specVersion
@@ -1165,13 +1605,20 @@ function stepToSpan(stepEvents, maxEndTime) {
1165
1605
  data: step
1166
1606
  };
1167
1607
  const resource = "step";
1168
- const endTime = new Date(step.completedAt ?? maxEndTime);
1169
- const events = convertEventsToSpanEvents(stepEvents, false);
1170
- const spanStartTime = new Date(step.createdAt);
1608
+ const events = convertEventsToSpanEvents(stepEvents, false, {
1609
+ preferOccurredAt: true
1610
+ });
1611
+ const spanStartEvent = stepEvents.find((event) => event.eventType === "step_created") ?? stepEvents[0];
1612
+ const spanStartTime = getEventTimestamp(spanStartEvent) ?? new Date(step.createdAt);
1171
1613
  let activeStartTime = step.startedAt ? new Date(step.startedAt) : void 0;
1172
1614
  const firstStartEvent = stepEvents.find((event) => event.eventType === "step_started");
1173
1615
  if (firstStartEvent) {
1174
- activeStartTime = new Date(firstStartEvent.createdAt);
1616
+ activeStartTime = getEventTimestamp(firstStartEvent) ?? new Date(firstStartEvent.createdAt);
1617
+ }
1618
+ let endTime = new Date(maxEndTime);
1619
+ if (step.completedAt) {
1620
+ const completedEvent = stepEvents.slice().reverse().find((event) => isTerminalStepEventType(event.eventType));
1621
+ endTime = getEventTimestamp(completedEvent) ?? new Date(step.completedAt);
1175
1622
  }
1176
1623
  return {
1177
1624
  spanId: String(step.stepId),
@@ -1193,7 +1640,7 @@ function stepToSpan(stepEvents, maxEndTime) {
1193
1640
  };
1194
1641
  }
1195
1642
  const hookEventsToHookEntity = (events) => {
1196
- var _a, _b;
1643
+ var _a;
1197
1644
  const createdEvent = events.find((event) => event.eventType === "hook_created");
1198
1645
  if (!createdEvent) {
1199
1646
  return null;
@@ -1205,10 +1652,10 @@ const hookEventsToHookEntity = (events) => {
1205
1652
  hookId: createdEvent.correlationId,
1206
1653
  runId: createdEvent.runId,
1207
1654
  token: (_a = createdEvent.eventData) == null ? void 0 : _a.token,
1208
- createdAt: createdEvent.createdAt,
1655
+ createdAt: getEventTimestamp(createdEvent) ?? createdEvent.createdAt,
1209
1656
  receivedCount: receivedEvents.length,
1210
- lastReceivedAt: (lastReceivedEvent == null ? void 0 : lastReceivedEvent.createdAt) || void 0,
1211
- disposedAt: ((_b = disposedEvents.at(-1)) == null ? void 0 : _b.createdAt) || void 0
1657
+ lastReceivedAt: getEventTimestamp(lastReceivedEvent),
1658
+ disposedAt: getEventTimestamp(disposedEvents.at(-1))
1212
1659
  };
1213
1660
  };
1214
1661
  function hookToSpan(hookEvents, maxEndTime) {
@@ -1220,7 +1667,7 @@ function hookToSpan(hookEvents, maxEndTime) {
1220
1667
  const endTime = hook.disposedAt || maxEndTime;
1221
1668
  return {
1222
1669
  spanId: String(hook.hookId),
1223
- name: String(hook.hookId),
1670
+ name: hook.token ?? String(hook.hookId),
1224
1671
  kind: 1,
1225
1672
  // INTERNAL span kind
1226
1673
  resource: "hook",
@@ -1241,15 +1688,26 @@ function hookToSpan(hookEvents, maxEndTime) {
1241
1688
  function runToSpan(run, runEvents, nowTime) {
1242
1689
  var _a;
1243
1690
  const now = nowTime ?? /* @__PURE__ */ new Date();
1691
+ const runCreatedEvent = runEvents.find((event) => event.eventType === "run_created");
1692
+ const runStartedEvent = runEvents.find((event) => event.eventType === "run_started");
1693
+ const terminalEvent = runEvents.slice().reverse().find((event) => event.eventType === "run_completed" || event.eventType === "run_failed" || event.eventType === "run_cancelled");
1694
+ const spanStartTime = getEventTimestamp(runCreatedEvent) ?? new Date(run.createdAt);
1695
+ const activeStartTime = getEventTimestamp(runStartedEvent) ?? (run.startedAt ? new Date(run.startedAt) : void 0);
1696
+ const completedAt = getEventTimestamp(terminalEvent) ?? run.completedAt ?? void 0;
1697
+ const endTime = completedAt ?? now;
1244
1698
  const { input: _i, output: _o, error: _e, ...runIdentity } = run;
1245
1699
  const attributes = {
1246
1700
  resource: "run",
1247
- data: runIdentity
1701
+ data: {
1702
+ ...runIdentity,
1703
+ createdAt: spanStartTime,
1704
+ startedAt: activeStartTime,
1705
+ completedAt
1706
+ }
1248
1707
  };
1249
- const spanStartTime = new Date(run.createdAt);
1250
- const activeStartTime = run.startedAt ? new Date(run.startedAt) : void 0;
1251
- const endTime = run.completedAt ?? now;
1252
- const events = convertEventsToSpanEvents(runEvents, false);
1708
+ const events = convertEventsToSpanEvents(runEvents, false, {
1709
+ preferOccurredAt: true
1710
+ });
1253
1711
  return {
1254
1712
  spanId: String(run.runId),
1255
1713
  name: String(((_a = parseWorkflowName(run.workflowName)) == null ? void 0 : _a.shortName) ?? "?"),
@@ -1269,9 +1727,15 @@ function runToSpan(run, runEvents, nowTime) {
1269
1727
  activeStartTime: activeStartTime && activeStartTime.getTime() > spanStartTime.getTime() ? dateToOtelTime(activeStartTime) : void 0
1270
1728
  };
1271
1729
  }
1272
- const isStepEvent = (eventType) => eventType.startsWith("step_");
1273
- const isTimerEvent = (eventType) => eventType === "wait_created" || eventType === "wait_completed";
1274
- const isHookLifecycleEvent = (eventType) => eventType === "hook_received" || eventType === "hook_created" || eventType === "hook_disposed";
1730
+ const isRunLevelEvent = (event) => !event.correlationId || !isWaitEventType(event.eventType) && !isHookLifecycleEventType(event.eventType) && !isStepEventType(event.eventType);
1731
+ function filterSpanRawEvents(events, resource, spanId) {
1732
+ if (resource === "run") {
1733
+ return events.filter(isRunLevelEvent);
1734
+ }
1735
+ if (!spanId)
1736
+ return [];
1737
+ return events.filter((e) => e.correlationId === spanId);
1738
+ }
1275
1739
  function pushEvent(map, correlationId, event) {
1276
1740
  const existing = map.get(correlationId);
1277
1741
  if (existing) {
@@ -1291,15 +1755,15 @@ function groupEventsByCorrelation(events) {
1291
1755
  runLevelEvents.push(event);
1292
1756
  continue;
1293
1757
  }
1294
- if (isTimerEvent(event.eventType)) {
1758
+ if (isWaitEventType(event.eventType)) {
1295
1759
  pushEvent(timerEvents, correlationId, event);
1296
1760
  continue;
1297
1761
  }
1298
- if (isHookLifecycleEvent(event.eventType)) {
1762
+ if (isHookLifecycleEventType(event.eventType)) {
1299
1763
  pushEvent(hookEvents, correlationId, event);
1300
1764
  continue;
1301
1765
  }
1302
- if (isStepEvent(event.eventType)) {
1766
+ if (isStepEventType(event.eventType)) {
1303
1767
  pushEvent(eventsByStepId, correlationId, event);
1304
1768
  continue;
1305
1769
  }
@@ -1310,7 +1774,7 @@ function groupEventsByCorrelation(events) {
1310
1774
  function computeLatestKnownTime(events, run) {
1311
1775
  let latest = new Date(run.createdAt).getTime();
1312
1776
  for (const event of events) {
1313
- const t = new Date(event.createdAt).getTime();
1777
+ const t = (getEventTimestamp(event) ?? new Date(event.createdAt)).getTime();
1314
1778
  if (t > latest)
1315
1779
  latest = t;
1316
1780
  }
@@ -1366,150 +1830,86 @@ function buildTrace(run, events, now) {
1366
1830
  knownDurationMs: Math.max(0, knownDurationMs)
1367
1831
  };
1368
1832
  }
1369
- function ConversationView({ messages }) {
1370
- if (messages.length === 0) {
1371
- return jsxRuntimeExports.jsx("div", { className: "text-center py-8 text-[11px]", style: { color: "var(--ds-gray-600)" }, children: "No messages" });
1372
- }
1373
- return jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-3 p-3", children: messages.map((message, index) => jsxRuntimeExports.jsx(MessageBubble, { message }, index)) });
1374
- }
1375
- function MessageBubble({ message }) {
1376
- const role = message.role;
1377
- const parts = parseContent(message.content);
1378
- const style = getRoleStyle(role);
1379
- return jsxRuntimeExports.jsxs("div", { className: "rounded-md border text-[11px]", style: {
1380
- backgroundColor: style.bg,
1381
- borderColor: style.border
1382
- }, children: [jsxRuntimeExports.jsx("div", { className: "px-2.5 py-1 border-b text-[10px] font-medium uppercase tracking-wide", style: {
1383
- borderColor: style.border,
1384
- color: style.label
1385
- }, children: role }), jsxRuntimeExports.jsx("div", { className: "px-2.5 py-2 space-y-2", children: parts.map((part, i) => jsxRuntimeExports.jsx(ContentPart, { part, role }, i)) })] });
1386
- }
1387
- function ContentPart({ part, role }) {
1388
- if (part.type === "text") {
1389
- if (!part.text)
1390
- return null;
1391
- if (role === "assistant") {
1392
- return jsxRuntimeExports.jsx("div", { className: "prose prose-sm max-w-none text-[11px] [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", style: { color: "var(--ds-gray-1000)" }, children: jsxRuntimeExports.jsx(_r, { children: part.text }) });
1393
- }
1394
- return jsxRuntimeExports.jsx("div", { className: "whitespace-pre-wrap break-words", style: { color: "var(--ds-gray-1000)" }, children: part.text });
1395
- }
1396
- if (part.type === "tool-call") {
1397
- return jsxRuntimeExports.jsxs("div", { className: "rounded border px-2 py-1.5", style: {
1398
- backgroundColor: "var(--ds-purple-100)",
1399
- borderColor: "var(--ds-purple-300)"
1400
- }, children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-[10px] font-medium", children: [jsxRuntimeExports.jsx("span", { children: "🔧" }), jsxRuntimeExports.jsx("span", { style: { color: "var(--ds-purple-900)" }, children: part.toolName })] }), part.input != null && jsxRuntimeExports.jsx("div", { className: "mt-1.5 overflow-x-auto p-1.5 rounded", style: { backgroundColor: "var(--ds-gray-100)" }, children: typeof part.input === "string" ? jsxRuntimeExports.jsx("pre", { className: "text-[10px]", style: { color: "var(--ds-gray-800)" }, children: part.input }) : jsxRuntimeExports.jsx(DataInspector, { data: part.input }) })] });
1401
- }
1402
- if (part.type === "tool-result") {
1403
- return jsxRuntimeExports.jsxs("div", { className: "rounded border px-2 py-1.5", style: {
1404
- backgroundColor: "var(--ds-green-100)",
1405
- borderColor: "var(--ds-green-300)"
1406
- }, children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-[10px] font-medium", children: [jsxRuntimeExports.jsx("span", { children: "✓" }), jsxRuntimeExports.jsxs("span", { style: { color: "var(--ds-green-900)" }, children: [part.toolName, " result"] })] }), part.output != null && jsxRuntimeExports.jsx("div", { className: "mt-1.5 overflow-x-auto max-h-[200px] overflow-y-auto p-1.5 rounded", style: { backgroundColor: "var(--ds-gray-100)" }, children: typeof part.output === "string" ? jsxRuntimeExports.jsx("pre", { className: "text-[10px]", style: { color: "var(--ds-gray-800)" }, children: part.output }) : jsxRuntimeExports.jsx(DataInspector, { data: part.output, expandLevel: 1 }) })] });
1407
- }
1408
- return null;
1833
+ const GUTTER_PX = 1;
1834
+ const MIN_PX = 50;
1835
+ const DEFAULT_START_PX = 340;
1836
+ function paneColTemplate(startPx) {
1837
+ return `${startPx}px ${GUTTER_PX}px minmax(${MIN_PX}px, 1fr)`;
1409
1838
  }
1410
- function getRoleStyle(role) {
1411
- switch (role) {
1412
- case "user":
1413
- return {
1414
- bg: "var(--ds-blue-100)",
1415
- border: "var(--ds-blue-300)",
1416
- label: "var(--ds-blue-700)"
1417
- };
1418
- case "assistant":
1419
- return {
1420
- bg: "var(--ds-gray-100)",
1421
- border: "var(--ds-gray-300)",
1422
- label: "var(--ds-gray-700)"
1423
- };
1424
- case "system":
1425
- return {
1426
- bg: "var(--ds-amber-100)",
1427
- border: "var(--ds-amber-300)",
1428
- label: "var(--ds-amber-700)"
1429
- };
1430
- case "tool":
1431
- return {
1432
- bg: "var(--ds-green-50)",
1433
- border: "var(--ds-green-300)",
1434
- label: "var(--ds-green-700)"
1435
- };
1436
- default:
1437
- return {
1438
- bg: "var(--ds-gray-100)",
1439
- border: "var(--ds-gray-300)",
1440
- label: "var(--ds-gray-700)"
1441
- };
1442
- }
1839
+ const COL_TEMPLATE = paneColTemplate(DEFAULT_START_PX);
1840
+ const ROWS = [
1841
+ { id: "r0", name: 62, off: 0, bar: 72 },
1842
+ { id: "r1", name: 78, off: 6, bar: 48 },
1843
+ { id: "r2", name: 50, off: 10, bar: 55 },
1844
+ { id: "r3", name: 84, off: 18, bar: 30 },
1845
+ { id: "r4", name: 45, off: 18, bar: 42 },
1846
+ { id: "r5", name: 66, off: 34, bar: 38 },
1847
+ { id: "r6", name: 55, off: 41, bar: 25 },
1848
+ { id: "r7", name: 40, off: 50, bar: 30 }
1849
+ ];
1850
+ const HEADER_MARKERS = ["m0", "m1", "m2", "m3"];
1851
+ const HeaderDivider = () => jsxRuntimeExports.jsx("div", { className: "flex justify-center", children: jsxRuntimeExports.jsx("span", { "aria-hidden": true, className: "h-full w-px bg-gray-alpha-400" }) });
1852
+ function TraceViewerSkeleton() {
1853
+ return jsxRuntimeExports.jsxs("output", { "aria-busy": "true", "aria-label": "Loading trace", className: "flex flex-col w-full h-full min-h-0 bg-background-100", children: [jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Loading trace…" }), jsxRuntimeExports.jsx("div", { className: "relative h-10 min-h-10 shrink-0 border-b border-gray-alpha-400", children: jsxRuntimeExports.jsx("div", { className: "absolute inset-x-4 top-[6px]", children: ROWS.map((row, index) => jsxRuntimeExports.jsx(Skeleton, { className: "absolute h-[3px] rounded-full", style: {
1854
+ left: `${row.off}%`,
1855
+ width: `${row.bar}%`,
1856
+ top: index * 3.5
1857
+ } }, row.id)) }) }), jsxRuntimeExports.jsxs("div", { className: "shrink-0 grid", style: { gridTemplateColumns: COL_TEMPLATE }, children: [jsxRuntimeExports.jsxs("div", { className: "h-10 min-h-10 flex items-center border-b border-gray-alpha-400 pl-4 pr-2 gap-1.5", children: [jsxRuntimeExports.jsx(Skeleton, { className: "w-3.5 h-3.5 shrink-0 rounded-sm" }), jsxRuntimeExports.jsx(Skeleton, { className: "h-3.5 w-40" })] }), jsxRuntimeExports.jsx(HeaderDivider, {}), jsxRuntimeExports.jsx("div", { className: "h-10 min-h-10 flex items-end border-b border-gray-alpha-400 px-4 pb-1 gap-2", children: jsxRuntimeExports.jsx("div", { className: "relative flex-1 flex items-end justify-between", children: HEADER_MARKERS.map((id) => jsxRuntimeExports.jsx(Skeleton, { className: "h-3.5 w-9" }, id)) }) })] }), jsxRuntimeExports.jsxs("div", { className: "grid flex-1 min-h-0 overflow-hidden", style: { gridTemplateColumns: COL_TEMPLATE }, children: [jsxRuntimeExports.jsx("div", { className: "block overflow-visible", children: jsxRuntimeExports.jsx("ul", { className: "block divide-y divide-gray-alpha-400 border-b border-gray-alpha-400", children: ROWS.map((row) => jsxRuntimeExports.jsxs("li", { className: "h-10 flex items-center pl-4 pr-2", children: [jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [jsxRuntimeExports.jsx(Skeleton, { className: "w-4 h-4 shrink-0 rounded-sm" }), jsxRuntimeExports.jsx(Skeleton, { className: "h-3.5", style: { width: `${row.name}%` } })] }), jsxRuntimeExports.jsx(Skeleton, { className: "ml-2 h-3.5 w-10 shrink-0" })] }, row.id)) }) }), jsxRuntimeExports.jsx("span", { "aria-hidden": true, className: "h-full w-px bg-gray-alpha-400" }), jsxRuntimeExports.jsx("div", { className: "relative min-h-0", children: ROWS.map((row) => jsxRuntimeExports.jsx("div", { className: "relative h-10", children: jsxRuntimeExports.jsx("div", { className: "absolute inset-x-4 inset-y-0", children: jsxRuntimeExports.jsx(Skeleton, { className: "absolute top-1/2 -translate-y-1/2 h-6 rounded-[0.25rem]", style: { left: `${row.off}%`, width: `${row.bar}%` } }) }) }, row.id)) })] })] });
1443
1858
  }
1444
- function parseContent(content) {
1445
- if (typeof content === "string") {
1446
- return [{ type: "text", text: content }];
1447
- }
1448
- if (Array.isArray(content)) {
1449
- return content.map((part) => {
1450
- if (typeof part === "string") {
1451
- return { type: "text", text: part };
1452
- }
1453
- if ((part == null ? void 0 : part.type) === "text") {
1454
- return { type: "text", text: String(part.text ?? "") };
1455
- }
1456
- if ((part == null ? void 0 : part.type) === "tool-call") {
1457
- return {
1458
- type: "tool-call",
1459
- toolName: part.toolName,
1460
- input: part.input
1461
- };
1462
- }
1463
- if ((part == null ? void 0 : part.type) === "tool-result") {
1464
- return {
1465
- type: "tool-result",
1466
- toolName: part.toolName,
1467
- output: part.output
1468
- };
1469
- }
1470
- return { type: "text", text: "" };
1859
+ function useIntersectionObserver(elementRef, { threshold = 0, rootRef, rootMargin = "0%", freezeOnceVisible = false } = {}) {
1860
+ const [entry, setEntry] = reactExports.useState();
1861
+ const frozen = (entry == null ? void 0 : entry.isIntersecting) && freezeOnceVisible;
1862
+ reactExports.useEffect(() => {
1863
+ const node = elementRef == null ? void 0 : elementRef.current;
1864
+ const hasIOSupport = !!window.IntersectionObserver;
1865
+ if (!hasIOSupport || frozen || !node)
1866
+ return;
1867
+ const updateEntry = ([entry2]) => {
1868
+ setEntry(entry2);
1869
+ };
1870
+ const observer = new IntersectionObserver(updateEntry, {
1871
+ threshold,
1872
+ root: (rootRef == null ? void 0 : rootRef.current) ?? null,
1873
+ rootMargin
1471
1874
  });
1472
- }
1473
- return [];
1875
+ observer.observe(node);
1876
+ return () => observer.disconnect();
1877
+ }, [elementRef, threshold, rootRef, rootMargin, frozen]);
1878
+ return entry;
1474
1879
  }
1475
- const serializeForClipboard = (value) => {
1476
- if (typeof value === "string")
1477
- return value;
1478
- if (typeof value === "number" || typeof value === "boolean" || value === null) {
1479
- return String(value);
1480
- }
1481
- try {
1482
- return JSON.stringify(value, null, 2);
1483
- } catch {
1484
- return String(value);
1485
- }
1486
- };
1487
- function CopyableDataBlock({ data }) {
1488
- const toast2 = useToast();
1489
- return jsxRuntimeExports.jsxs("div", { className: "relative overflow-x-auto rounded-md border p-3", style: { borderColor: "var(--ds-gray-300)" }, children: [jsxRuntimeExports.jsx("button", { type: "button", "aria-label": "Copy data", title: "Copy", className: "!absolute !right-2 !top-2 !flex !h-6 !w-6 !items-center !justify-center !rounded-md !border !bg-[var(--ds-background-100)] !text-[var(--ds-gray-800)] transition-transform transition-colors duration-100 hover:!bg-[var(--ds-gray-alpha-200)] active:!scale-95 active:!bg-[var(--ds-gray-alpha-300)]", style: { borderColor: "var(--ds-gray-300)" }, onClick: () => {
1490
- navigator.clipboard.writeText(serializeForClipboard(data)).then(() => {
1491
- toast2.success("Copied to clipboard");
1492
- }).catch(() => {
1493
- toast2.error("Failed to copy");
1494
- });
1495
- }, children: jsxRuntimeExports.jsx(Copy, { size: 12 }) }), jsxRuntimeExports.jsx(DataInspector, { data })] });
1880
+ function useLoadMoreOnScroll(loadMore, { hasMore, isLoadingMore, rootRef, rootMargin = "400px" }) {
1881
+ const sentinelRef = reactExports.useRef(null);
1882
+ const entry = useIntersectionObserver(sentinelRef, { rootRef, rootMargin });
1883
+ const loadMoreRef = reactExports.useRef(loadMore);
1884
+ reactExports.useEffect(() => {
1885
+ loadMoreRef.current = loadMore;
1886
+ }, [loadMore]);
1887
+ reactExports.useEffect(() => {
1888
+ if ((entry == null ? void 0 : entry.isIntersecting) && hasMore && !isLoadingMore) {
1889
+ loadMoreRef.current();
1890
+ }
1891
+ }, [entry == null ? void 0 : entry.isIntersecting, hasMore, isLoadingMore]);
1892
+ return sentinelRef;
1496
1893
  }
1497
- function DetailCard({ summary, children, onToggle, disabled = false, summaryClassName, contentClassName }) {
1498
- if (disabled) {
1499
- return jsxRuntimeExports.jsx("div", { className: `rounded-md border px-2.5 py-1.5 text-xs ${summaryClassName ?? ""}`, style: {
1500
- borderColor: "var(--ds-gray-300)",
1501
- backgroundColor: "var(--ds-gray-100)",
1502
- color: "var(--ds-gray-700)",
1503
- cursor: "not-allowed",
1504
- opacity: 0.8
1505
- }, children: summary });
1894
+ const kbdVariants = cva("inline-flex items-center justify-center rounded px-1 text-center font-sans", {
1895
+ variants: {
1896
+ variant: {
1897
+ subtle: "bg-background-100/20",
1898
+ outline: "border border-gray-alpha-400 bg-background-100 font-medium text-gray-900"
1899
+ },
1900
+ size: {
1901
+ default: "h-5 min-w-5 text-label-12 leading-none",
1902
+ compact: "h-4 min-h-4 min-w-4 text-[11px] leading-none",
1903
+ search: "h-5 min-h-5 min-w-5 text-label-13 leading-[1.7em]"
1904
+ }
1905
+ },
1906
+ defaultVariants: {
1907
+ variant: "subtle",
1908
+ size: "default"
1506
1909
  }
1507
- return jsxRuntimeExports.jsxs("details", { className: "group", onToggle: (e) => onToggle == null ? void 0 : onToggle(e.target.open), children: [jsxRuntimeExports.jsx("summary", { className: `cursor-pointer rounded-md border px-2.5 py-1.5 text-xs hover:brightness-95 [&::-webkit-details-marker]:hidden ${summaryClassName ?? ""}`, style: {
1508
- borderColor: "var(--ds-gray-300)",
1509
- backgroundColor: "var(--ds-gray-100)",
1510
- color: "var(--ds-gray-900)",
1511
- listStyle: "none"
1512
- }, children: jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5", children: [jsxRuntimeExports.jsx(ChevronRight, { size: 14, className: "shrink-0 transition-transform group-open:rotate-90" }), summary] }) }), jsxRuntimeExports.jsx("div", { className: `mt-2 ${contentClassName ?? ""}`, children })] });
1910
+ });
1911
+ function Kbd({ children, className, variant, size, ...props }) {
1912
+ return jsxRuntimeExports.jsx("kbd", { className: cn(kbdVariants({ variant, size, className })), ...props, children });
1513
1913
  }
1514
1914
  function TabButton({ active, onClick, children }) {
1515
1915
  return jsxRuntimeExports.jsx("button", { type: "button", role: "tab", "aria-selected": active, tabIndex: active ? 0 : -1, onClick, className: "px-3 py-1.5 text-[11px] font-medium transition-colors -mb-px", style: {
@@ -1548,28 +1948,15 @@ const conversationTabs = [
1548
1948
  { id: "conversation", label: "Conversation" },
1549
1949
  { id: "json", label: "Raw JSON" }
1550
1950
  ];
1551
- function ConversationWithTabs({ conversation, args }) {
1951
+ function ConversationWithTabs({ conversation, args, defaultOpen, onOpenChange }) {
1552
1952
  const [activeTab, setActiveTab] = reactExports.useState("conversation");
1553
- return jsxRuntimeExports.jsx(DetailCard, { summary: `Input (${conversation.length} messages)`, children: jsxRuntimeExports.jsx(TabbedContainer, { tabs: conversationTabs, activeTab, onTabChange: setActiveTab, ariaLabel: "Conversation view", children: activeTab === "conversation" ? jsxRuntimeExports.jsx(ConversationView, { messages: conversation }) : jsxRuntimeExports.jsx("div", { className: "p-3", children: Array.isArray(args) ? args.map((v, i) => jsxRuntimeExports.jsx("div", { className: "mt-2 first:mt-0", children: JsonBlock(v) }, i)) : JsonBlock(args) }) }) });
1953
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input", defaultOpen, onOpenChange, children: jsxRuntimeExports.jsx(TabbedContainer, { tabs: conversationTabs, activeTab, onTabChange: setActiveTab, ariaLabel: "Conversation view", children: activeTab === "conversation" ? jsxRuntimeExports.jsx(ConversationView, { messages: conversation }) : jsxRuntimeExports.jsx("div", { className: "p-3", children: Array.isArray(args) ? args.map((v, i) => jsxRuntimeExports.jsx("div", { className: "mt-2 first:mt-0", children: JsonBlock(v) }, i)) : JsonBlock(args) }) }) });
1554
1954
  }
1555
1955
  function EncryptedFieldBlock() {
1556
- const ctx = reactExports.useContext(DecryptClickContext);
1557
- if (ctx) {
1558
- return jsxRuntimeExports.jsxs("button", { type: "button", onClick: ctx.onDecrypt, disabled: ctx.isDecrypting, className: "flex w-full items-center justify-center gap-1.5 rounded-md border px-3 py-2 text-xs cursor-pointer transition-colors", style: {
1559
- borderColor: "var(--ds-gray-400)",
1560
- backgroundColor: "var(--ds-gray-100)",
1561
- color: "var(--ds-gray-700)",
1562
- opacity: ctx.isDecrypting ? 0.6 : 1
1563
- }, title: "Click to decrypt", children: [ctx.isDecrypting ? jsxRuntimeExports.jsx(Spinner, { size: 12 }) : jsxRuntimeExports.jsx(Lock, { className: "h-3 w-3" }), jsxRuntimeExports.jsx("span", { className: "font-medium", children: ctx.isDecrypting ? "Decrypting…" : "Decrypt" })] });
1564
- }
1565
- return jsxRuntimeExports.jsxs("div", { className: "flex w-full items-center justify-center gap-1.5 rounded-md border px-3 py-2 text-xs", style: {
1566
- borderColor: "var(--ds-gray-300)",
1567
- backgroundColor: "var(--ds-gray-100)",
1568
- color: "var(--ds-gray-700)"
1569
- }, children: [jsxRuntimeExports.jsx(Lock, { className: "h-3 w-3" }), jsxRuntimeExports.jsx("span", { className: "font-medium", children: "Encrypted" })] });
1956
+ return jsxRuntimeExports.jsx(EncryptedDataBlock, {});
1570
1957
  }
1571
1958
  function ExpiredFieldBlock() {
1572
- return jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1.5 rounded-md border px-3 py-2 text-xs", style: {
1959
+ return jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1.5 rounded-md border px-3 py-2 text-label-12", style: {
1573
1960
  borderColor: "var(--ds-gray-300)",
1574
1961
  backgroundColor: "var(--ds-gray-100)",
1575
1962
  color: "var(--ds-gray-700)"
@@ -1600,36 +1987,44 @@ const attributeOrder = [
1600
1987
  "runId",
1601
1988
  "attempt",
1602
1989
  "token",
1990
+ "tokenRetentionUntil",
1991
+ "isWebhook",
1992
+ "isSystem",
1603
1993
  "receivedCount",
1604
1994
  "lastReceivedAt",
1605
1995
  "disposedAt",
1606
1996
  "correlationId",
1607
1997
  "eventType",
1608
1998
  "deploymentId",
1999
+ "vercelId",
2000
+ "computeInstanceId",
1609
2001
  "specVersion",
1610
2002
  "workflowCoreVersion",
1611
2003
  "ownerId",
1612
2004
  "projectId",
1613
2005
  "environment",
1614
2006
  "executionContext",
2007
+ "occurredAt",
1615
2008
  "createdAt",
1616
2009
  "startedAt",
1617
2010
  "updatedAt",
1618
2011
  "completedAt",
1619
2012
  "expiredAt",
1620
2013
  "retryAfter",
2014
+ "errorCode",
1621
2015
  "error",
1622
2016
  "metadata",
1623
2017
  "eventData",
1624
2018
  "input",
1625
2019
  "output",
2020
+ "attributes",
1626
2021
  "resumeAt"
1627
2022
  ];
1628
- const sortByAttributeOrder = (a, b) => {
1629
- const aIndex = attributeOrder.indexOf(a) || 0;
1630
- const bIndex = attributeOrder.indexOf(b) || 0;
1631
- return aIndex - bIndex;
2023
+ const attributeOrderIndex = (attribute) => {
2024
+ const index = attributeOrder.indexOf(attribute);
2025
+ return index === -1 ? attributeOrder.length : index;
1632
2026
  };
2027
+ const sortByAttributeOrder = (a, b) => attributeOrderIndex(a) - attributeOrderIndex(b);
1633
2028
  const attributeDisplayNames = {
1634
2029
  moduleSpecifier: "Module",
1635
2030
  workflowName: "Workflow Name",
@@ -1639,11 +2034,17 @@ const attributeDisplayNames = {
1639
2034
  attempt: "Attempts",
1640
2035
  eventId: "Event ID",
1641
2036
  runId: "Run ID",
2037
+ token: "Token",
2038
+ tokenRetentionUntil: "Minimum Retention Until",
1642
2039
  eventType: "Event Type",
2040
+ errorCode: "Error Code",
1643
2041
  correlationId: "Correlation ID",
1644
2042
  deploymentId: "Deployment ID",
2043
+ vercelId: "Request ID",
2044
+ computeInstanceId: "Compute Instance ID",
1645
2045
  specVersion: "Spec Version",
1646
2046
  workflowCoreVersion: "@workflow/core version",
2047
+ occurredAt: "Occurred",
1647
2048
  createdAt: "Created",
1648
2049
  startedAt: "Started",
1649
2050
  updatedAt: "Updated",
@@ -1670,7 +2071,7 @@ const getModuleSpecifierFromName = (value) => {
1670
2071
  }
1671
2072
  return raw;
1672
2073
  };
1673
- const parseDateValue = (value) => {
2074
+ const parseDateValue$1 = (value) => {
1674
2075
  if (value == null) {
1675
2076
  return null;
1676
2077
  }
@@ -1683,35 +2084,21 @@ const parseDateValue = (value) => {
1683
2084
  const date = typeof value === "number" ? new Date(value) : new Date(String(value));
1684
2085
  return Number.isNaN(date.getTime()) ? null : date;
1685
2086
  };
1686
- const formatLocalMillisecondTime = (date) => date.toLocaleString(void 0, {
1687
- year: "numeric",
1688
- month: "numeric",
1689
- day: "numeric",
1690
- hour: "numeric",
1691
- minute: "numeric",
1692
- second: "numeric",
1693
- fractionalSecondDigits: 3
1694
- });
1695
- const localMillisecondTime = (value) => {
1696
- const date = parseDateValue(value);
1697
- if (!date) {
1698
- return "-";
1699
- }
1700
- return formatLocalMillisecondTime(date);
1701
- };
2087
+ const formatLocalMillisecondTime = (date) => format(date, "MMM dd HH:mm:ss.SS OO").toUpperCase();
1702
2088
  const localMillisecondTimeOrNull = (value) => {
1703
- const date = parseDateValue(value);
2089
+ const date = parseDateValue$1(value);
1704
2090
  if (!date) {
1705
2091
  return null;
1706
2092
  }
1707
2093
  return formatLocalMillisecondTime(date);
1708
2094
  };
1709
2095
  const timestampWithTooltipOrNull = (value) => {
1710
- const date = parseDateValue(value);
2096
+ const date = parseDateValue$1(value);
1711
2097
  if (!date)
1712
2098
  return null;
1713
2099
  return jsxRuntimeExports.jsx(TimestampTooltip, { date, children: jsxRuntimeExports.jsx("span", { children: formatLocalMillisecondTime(date) }) });
1714
2100
  };
2101
+ const opaqueIdOrNull = (value) => hasDisplayContent(value) ? String(value) : null;
1715
2102
  const attributeToDisplayFn = {
1716
2103
  // Names that need pretty-printing
1717
2104
  workflowName: (_value) => null,
@@ -1719,7 +2106,7 @@ const attributeToDisplayFn = {
1719
2106
  stepName: (_value) => null,
1720
2107
  // IDs
1721
2108
  runId: (_value) => null,
1722
- stepId: (_value) => null,
2109
+ stepId: (value) => String(value),
1723
2110
  hookId: (value) => String(value),
1724
2111
  eventId: (value) => String(value),
1725
2112
  // Run/step details
@@ -1727,15 +2114,23 @@ const attributeToDisplayFn = {
1727
2114
  attempt: (value) => String(value),
1728
2115
  // Hook details
1729
2116
  token: (value) => String(value),
2117
+ tokenRetentionUntil: timestampWithTooltipOrNull,
1730
2118
  isWebhook: (value) => String(value),
2119
+ isSystem: (value) => String(value),
1731
2120
  receivedCount: (value) => String(value),
1732
2121
  lastReceivedAt: localMillisecondTimeOrNull,
1733
2122
  disposedAt: localMillisecondTimeOrNull,
2123
+ // Internal resume plumbing — not surfaced in the UI
2124
+ resumeContext: (_value) => null,
2125
+ resumeId: (_value) => null,
2126
+ resumeCapabilities: (_value) => null,
1734
2127
  // Event details
1735
2128
  eventType: (value) => String(value),
1736
2129
  correlationId: (value) => String(value),
1737
2130
  // Project details
1738
2131
  deploymentId: (value) => String(value),
2132
+ vercelId: opaqueIdOrNull,
2133
+ computeInstanceId: opaqueIdOrNull,
1739
2134
  specVersion: (value) => String(value),
1740
2135
  workflowCoreVersion: (value) => String(value),
1741
2136
  // Tenancy (we don't show these)
@@ -1743,7 +2138,16 @@ const attributeToDisplayFn = {
1743
2138
  projectId: (_value) => null,
1744
2139
  environment: (_value) => null,
1745
2140
  executionContext: (_value) => null,
2141
+ // Attributes — string-string metadata attached to the run.
2142
+ // Rendered as key-value rows in its own collapsible section;
2143
+ // if empty/missing, hidden by the hasDisplayContent gate.
2144
+ attributes: (value) => {
2145
+ if (!hasDisplayContent(value))
2146
+ return null;
2147
+ return jsxRuntimeExports.jsx(RunAttributesCard, { attributes: value });
2148
+ },
1746
2149
  // Dates — wrapped with TimestampTooltip showing UTC/local + relative time
2150
+ occurredAt: timestampWithTooltipOrNull,
1747
2151
  createdAt: timestampWithTooltipOrNull,
1748
2152
  startedAt: timestampWithTooltipOrNull,
1749
2153
  updatedAt: (_value) => null,
@@ -1755,105 +2159,154 @@ const attributeToDisplayFn = {
1755
2159
  metadata: (value) => {
1756
2160
  if (!hasDisplayContent(value))
1757
2161
  return null;
1758
- if (isEncryptedMarker(value))
1759
- return jsxRuntimeExports.jsx(EncryptedFieldBlock, {});
1760
- if (isExpiredMarker(value))
1761
- return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
1762
- return JsonBlock(value);
2162
+ if (isEncryptedMarker(value)) {
2163
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Metadata", children: jsxRuntimeExports.jsx(EncryptedDataBlock, {}) });
2164
+ }
2165
+ if (isExpiredMarker(value)) {
2166
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Metadata", children: jsxRuntimeExports.jsx(ExpiredFieldBlock, {}) });
2167
+ }
2168
+ return jsxRuntimeExports.jsx(RunMetadataCard, { metadata: value });
1763
2169
  },
1764
2170
  input: (value, context) => {
1765
- if (isEncryptedMarker(value))
1766
- return jsxRuntimeExports.jsx(EncryptedFieldBlock, {});
2171
+ if (isEncryptedMarker(value)) {
2172
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input", defaultOpen: context == null ? void 0 : context.sectionOpen, onOpenChange: context == null ? void 0 : context.onSectionOpenChange, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2173
+ }
1767
2174
  if (isExpiredMarker(value))
1768
2175
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
1769
2176
  if (value && typeof value === "object" && "args" in value) {
1770
2177
  const { args, closureVars, thisVal } = value;
1771
- const argCount2 = Array.isArray(args) ? args.length : 0;
1772
- const argLabel2 = argCount2 === 1 ? "argument" : "arguments";
1773
2178
  const hasClosureVars = hasDisplayContent(closureVars);
1774
2179
  const hasThisVal = hasDisplayContent(thisVal);
1775
2180
  const hasArgs = hasDisplayContent(args);
1776
2181
  if ((context == null ? void 0 : context.stepName) && isDoStreamStep(context.stepName)) {
1777
2182
  const conversation = extractConversation(args);
1778
2183
  if (conversation && conversation.length > 0) {
1779
- return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(ConversationWithTabs, { conversation, args }), hasClosureVars && jsxRuntimeExports.jsx(DetailCard, { summary: "Closure Variables", children: JsonBlock(closureVars) }), hasThisVal && jsxRuntimeExports.jsx(DetailCard, { summary: "This Value", children: JsonBlock(thisVal) })] });
2184
+ return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(ConversationWithTabs, { conversation, args, defaultOpen: context == null ? void 0 : context.sectionOpen, onOpenChange: context == null ? void 0 : context.onSectionOpenChange }), hasClosureVars && jsxRuntimeExports.jsx(Collapsible, { label: "Closure Variables", children: JsonBlock(closureVars) }), hasThisVal && jsxRuntimeExports.jsx(Collapsible, { label: "This Value", children: JsonBlock(thisVal) })] });
1780
2185
  }
1781
2186
  }
1782
2187
  if (!hasArgs && !hasClosureVars && !hasThisVal) {
1783
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Input (no data)", disabled: true, summaryClassName: "text-label-14 font-medium py-2" });
2188
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input (no data)", disabled: true });
1784
2189
  }
1785
- return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(DetailCard, { summary: `Input (${argCount2} ${argLabel2})`, summaryClassName: "text-label-14 font-medium py-2", contentClassName: "mt-0", children: Array.isArray(args) ? args.map((v, i) => jsxRuntimeExports.jsx("div", { className: "mt-2 first:mt-0", children: JsonBlock(v) }, i)) : JsonBlock(args) }), hasClosureVars && jsxRuntimeExports.jsx(DetailCard, { summary: "Closure Variables", children: JsonBlock(closureVars) }), hasThisVal && jsxRuntimeExports.jsx(DetailCard, { summary: "this", children: JsonBlock(thisVal) })] });
2190
+ return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(Collapsible, { label: "Input", defaultOpen: context == null ? void 0 : context.sectionOpen, onOpenChange: context == null ? void 0 : context.onSectionOpenChange, children: Array.isArray(args) ? args.map((v, i) => jsxRuntimeExports.jsx("div", { className: "mt-2 first:mt-0", children: JsonBlock(v) }, i)) : JsonBlock(args) }), hasClosureVars && jsxRuntimeExports.jsx(Collapsible, { label: "Closure Variables", children: JsonBlock(closureVars) }), hasThisVal && jsxRuntimeExports.jsx(Collapsible, { label: "Context", children: JsonBlock(thisVal) })] });
1786
2191
  }
1787
- const argCount = Array.isArray(value) ? value.length : 0;
1788
- const argLabel = argCount === 1 ? "argument" : "arguments";
1789
2192
  if (!hasDisplayContent(value)) {
1790
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Input (no data)", disabled: true, summaryClassName: "text-label-14 font-medium py-2" });
2193
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input (no data)", disabled: true });
1791
2194
  }
1792
- return jsxRuntimeExports.jsx(DetailCard, { summary: `Input (${argCount} ${argLabel})`, summaryClassName: "text-label-14 font-medium py-2", contentClassName: "mt-0", children: Array.isArray(value) ? value.map((v, i) => jsxRuntimeExports.jsx("div", { className: "mt-2 first:mt-0", children: JsonBlock(v) }, i)) : JsonBlock(value) });
2195
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input", defaultOpen: context == null ? void 0 : context.sectionOpen, onOpenChange: context == null ? void 0 : context.onSectionOpenChange, children: Array.isArray(value) ? value.map((v, i) => jsxRuntimeExports.jsx("div", { className: "mt-2 first:mt-0", children: JsonBlock(v) }, i)) : JsonBlock(value) });
1793
2196
  },
1794
- output: (value) => {
2197
+ output: (value, context) => {
2198
+ if (isEncryptedMarker(value)) {
2199
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Output", defaultOpen: context == null ? void 0 : context.sectionOpen, onOpenChange: context == null ? void 0 : context.onSectionOpenChange, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2200
+ }
1795
2201
  if (!hasDisplayContent(value))
1796
2202
  return null;
1797
- if (isEncryptedMarker(value))
1798
- return jsxRuntimeExports.jsx(EncryptedFieldBlock, {});
1799
2203
  if (isExpiredMarker(value))
1800
2204
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
1801
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Output", summaryClassName: "text-label-14 font-medium py-2", contentClassName: "mt-0", children: JsonBlock(value) });
2205
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Output", defaultOpen: context == null ? void 0 : context.sectionOpen, onOpenChange: context == null ? void 0 : context.onSectionOpenChange, children: JsonBlock(value) });
1802
2206
  },
1803
2207
  error: (value) => {
1804
- if (isEncryptedMarker(value))
1805
- return jsxRuntimeExports.jsx(EncryptedFieldBlock, {});
2208
+ if (isEncryptedMarker(value)) {
2209
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Error", defaultOpen: true, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2210
+ }
1806
2211
  if (isExpiredMarker(value))
1807
2212
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
1808
2213
  if (!hasDisplayContent(value))
1809
2214
  return null;
1810
- if (isStructuredErrorWithStack(value)) {
1811
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Error", summaryClassName: "text-label-14 font-medium py-2", contentClassName: "mt-0", children: jsxRuntimeExports.jsx(ErrorStackBlock, { value }) });
2215
+ if (isStructuredError(value)) {
2216
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Error", defaultOpen: true, children: jsxRuntimeExports.jsx(ErrorStackBlock, { value }) });
1812
2217
  }
1813
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Error", summaryClassName: "text-label-14 font-medium py-2", contentClassName: "mt-0", children: JsonBlock(value) });
2218
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Error", defaultOpen: true, children: JsonBlock(value) });
1814
2219
  },
1815
2220
  eventData: (value) => {
1816
- if (isEncryptedMarker(value))
1817
- return jsxRuntimeExports.jsx(EncryptedFieldBlock, {});
2221
+ if (isEncryptedMarker(value)) {
2222
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Event Data", defaultOpen: true, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2223
+ }
1818
2224
  if (isExpiredMarker(value))
1819
2225
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
1820
2226
  if (!hasDisplayContent(value))
1821
2227
  return null;
1822
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Event Data", children: JsonBlock(value) });
1823
- }
2228
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Event Data", defaultOpen: true, children: JsonBlock(value) });
2229
+ },
2230
+ errorCode: (value) => {
2231
+ if (typeof value !== "string" || value.length === 0)
2232
+ return null;
2233
+ return String(value);
2234
+ },
2235
+ // Internal encryption plumbing: the run's X25519 public key, used by
2236
+ // cross-run writers to seal payloads to this run. Not actionable for users
2237
+ // and not secret — hidden rather than rendered as 44 opaque base64 chars.
2238
+ encryptionPublicKey: (_value) => null
1824
2239
  };
1825
2240
  const resolvableAttributes = [
1826
2241
  "input",
1827
2242
  "output",
1828
2243
  "error",
1829
2244
  "metadata",
2245
+ "attributes",
2246
+ "eventData"
2247
+ ];
2248
+ const selfHeaderedAttributes = /* @__PURE__ */ new Set([
2249
+ "input",
2250
+ "output",
2251
+ "error",
2252
+ "metadata",
2253
+ "attributes",
1830
2254
  "eventData"
1831
- ];
1832
- const ExpiredDataMessage = () => jsxRuntimeExports.jsx("div", { className: "text-copy-12 rounded-md border p-4 my-2", style: {
2255
+ ]);
2256
+ const ExpiredDataMessage = () => jsxRuntimeExports.jsx("div", { className: "text-label-12 rounded-md border p-4 my-2", style: {
1833
2257
  borderColor: "var(--ds-gray-300)",
1834
2258
  backgroundColor: "var(--ds-gray-100)",
1835
2259
  color: "var(--ds-gray-700)"
1836
2260
  }, children: jsxRuntimeExports.jsx("span", { children: "The data for this run has expired and is no longer available." }) });
2261
+ const copyableBasicAttributes = /* @__PURE__ */ new Set([
2262
+ "stepId",
2263
+ "hookId",
2264
+ "eventId",
2265
+ "deploymentId",
2266
+ "vercelId",
2267
+ "computeInstanceId",
2268
+ "moduleSpecifier",
2269
+ "token"
2270
+ ]);
2271
+ const loadingSectionLabels = {
2272
+ input: "Input",
2273
+ output: "Output",
2274
+ eventData: "Event Data"
2275
+ };
1837
2276
  const AttributeBlock = ({ attribute, value, isLoading, inline = false, context }) => {
1838
- const isExpandableLoadingTarget = attribute === "input" || attribute === "eventData";
1839
- if (isLoading && isExpandableLoadingTarget && !hasDisplayContent(value)) {
1840
- return jsxRuntimeExports.jsxs("div", { className: `my-2 flex flex-col ${attribute === "input" ? "gap-2 my-3.5" : "gap-0"}`, children: [jsxRuntimeExports.jsx("span", { className: "text-label-14 text-gray-1000 font-medium first-letter:uppercase", children: attribute }), jsxRuntimeExports.jsx(Skeleton, { className: "h-9 w-full rounded-md" })] });
2277
+ const decryptCtx = reactExports.useContext(DecryptClickContext);
2278
+ const sectionOpenRef = reactExports.useRef(false);
2279
+ const handleSectionOpenChange = reactExports.useCallback((open) => {
2280
+ sectionOpenRef.current = open;
2281
+ }, []);
2282
+ const label = loadingSectionLabels[attribute];
2283
+ if (isLoading && label && !hasDisplayContent(value)) {
2284
+ if (decryptCtx == null ? void 0 : decryptCtx.hasEncryptedData) {
2285
+ return jsxRuntimeExports.jsx(Collapsible, { label, defaultOpen: attribute === "eventData" || sectionOpenRef.current, onOpenChange: handleSectionOpenChange, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2286
+ }
2287
+ return jsxRuntimeExports.jsx(Collapsible, { label });
1841
2288
  }
1842
2289
  const displayFn = attributeToDisplayFn[attribute];
1843
2290
  if (!displayFn) {
1844
2291
  return null;
1845
2292
  }
1846
- const displayValue = displayFn(value, context);
2293
+ const displayValue = displayFn(value, {
2294
+ ...context,
2295
+ sectionOpen: sectionOpenRef.current,
2296
+ onSectionOpenChange: handleSectionOpenChange
2297
+ });
1847
2298
  if (!displayValue) {
1848
2299
  return null;
1849
2300
  }
1850
2301
  if (inline) {
1851
2302
  return jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [jsxRuntimeExports.jsx("span", { className: "text-[11px] font-medium", style: { color: "var(--ds-gray-700)" }, children: attribute }), jsxRuntimeExports.jsx("span", { className: "text-[11px]", style: { color: "var(--ds-gray-1000)" }, children: displayValue })] });
1852
2303
  }
1853
- return jsxRuntimeExports.jsxs("div", { className: "relative", children: [typeof isLoading === "boolean" && isLoading && jsxRuntimeExports.jsx("div", { className: "absolute top-9 right-4", children: jsxRuntimeExports.jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2", style: { borderColor: "var(--ds-gray-900)" } }) }), jsxRuntimeExports.jsxs("div", { className: `my-2 flex flex-col ${attribute === "input" || attribute === "output" || attribute === "error" ? "gap-2 my-3.5" : "gap-0"}`, children: [jsxRuntimeExports.jsx("span", { className: "text-label-14 text-gray-1000 font-medium first-letter:uppercase", children: attribute }), jsxRuntimeExports.jsx("span", { className: "text-xs", style: { color: "var(--ds-gray-1000)" }, children: displayValue })] }, attribute)] });
2304
+ if (selfHeaderedAttributes.has(attribute)) {
2305
+ return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: displayValue });
2306
+ }
2307
+ return jsxRuntimeExports.jsxs("div", { className: "relative", children: [typeof isLoading === "boolean" && isLoading && jsxRuntimeExports.jsx("div", { className: "absolute top-9 right-4", children: jsxRuntimeExports.jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2", style: { borderColor: "var(--ds-gray-900)" } }) }), jsxRuntimeExports.jsxs("div", { className: "my-2 flex flex-col gap-0", children: [jsxRuntimeExports.jsx("span", { className: "text-label-14 text-gray-1000 font-medium first-letter:uppercase", children: attribute }), jsxRuntimeExports.jsx("span", { className: "text-label-12", style: { color: "var(--ds-gray-1000)" }, children: displayValue })] }, attribute)] });
1854
2308
  };
1855
- const AttributePanel = ({ data, moduleSpecifier, isLoading, error, expiredAt, onStreamClick, onRunClick, onDecrypt, isDecrypting = false, resource }) => {
1856
- const toast2 = useToast();
2309
+ const AttributePanel = ({ data, moduleSpecifier, moduleSourceUrl, isLoading, error, expiredAt, onStreamClick, onRunClick, onDecrypt, isDecrypting = false, resource }) => {
1857
2310
  const displayData = reactExports.useMemo(() => {
1858
2311
  const result = { ...data };
1859
2312
  const execCtx = data.executionContext;
@@ -1875,14 +2328,16 @@ const AttributePanel = ({ data, moduleSpecifier, isLoading, error, expiredAt, on
1875
2328
  const present = Object.keys(displayData).filter((key) => resolvableAttributes.includes(key)).sort(sortByAttributeOrder);
1876
2329
  if (!isLoading)
1877
2330
  return present;
1878
- const loadingDefaults = ["input"];
1879
- for (const key of loadingDefaults) {
2331
+ if (resource === "sleep")
2332
+ return present;
2333
+ const loadingPlaceholders = ["input", "output"];
2334
+ for (const key of loadingPlaceholders) {
1880
2335
  if (!present.includes(key)) {
1881
2336
  present.push(key);
1882
2337
  }
1883
2338
  }
1884
2339
  return present.sort(sortByAttributeOrder);
1885
- }, [displayData, isLoading]);
2340
+ }, [displayData, isLoading, resource]);
1886
2341
  const visibleBasicAttributes = basicAttributes.filter((attribute) => {
1887
2342
  const displayFn = attributeToDisplayFn[attribute];
1888
2343
  if (!displayFn)
@@ -1913,46 +2368,37 @@ const AttributePanel = ({ data, moduleSpecifier, isLoading, error, expiredAt, on
1913
2368
  const displayContext = reactExports.useMemo(() => ({
1914
2369
  stepName: displayData.stepName
1915
2370
  }), [displayData.stepName]);
1916
- const handleCopyModuleSpecifier = reactExports.useCallback((value) => {
1917
- navigator.clipboard.writeText(value).then(() => {
1918
- toast2.success("moduleSpecifier copied");
1919
- }).catch(() => {
1920
- toast2.error("Failed to copy moduleSpecifier");
1921
- });
1922
- }, []);
1923
- return jsxRuntimeExports.jsx(RunClickContext.Provider, { value: onRunClick, children: jsxRuntimeExports.jsx(StreamClickContext.Provider, { value: onStreamClick, children: jsxRuntimeExports.jsx(DecryptClickContext.Provider, { value: onDecrypt ? { onDecrypt, isDecrypting } : void 0, children: jsxRuntimeExports.jsxs("div", { children: [visibleBasicAttributes.length > 0 && jsxRuntimeExports.jsxs("div", { className: "mb-3 flex flex-col overflow-hidden rounded-md border", style: {
1924
- borderColor: "var(--ds-gray-300)"
1925
- }, children: [orderedBasicAttributes.map((attribute, index) => {
2371
+ const outerDecryptCtx = reactExports.useContext(DecryptClickContext);
2372
+ const decryptValue = onDecrypt ? {
2373
+ onDecrypt,
2374
+ isDecrypting,
2375
+ hasEncryptedData: outerDecryptCtx == null ? void 0 : outerDecryptCtx.hasEncryptedData
2376
+ } : outerDecryptCtx;
2377
+ return jsxRuntimeExports.jsx(ContextCardProvider, { children: jsxRuntimeExports.jsx(RunClickContext.Provider, { value: onRunClick, children: jsxRuntimeExports.jsx(StreamClickContext.Provider, { value: onStreamClick, children: jsxRuntimeExports.jsxs(DecryptClickContext.Provider, { value: decryptValue, children: [visibleBasicAttributes.length > 0 && jsxRuntimeExports.jsxs(CollapsibleRoot, { defaultOpen: true, children: [jsxRuntimeExports.jsx(CollapsibleTrigger, { children: "Metadata" }), jsxRuntimeExports.jsx(CollapsibleContent, { className: "mt-0 mb-2", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [orderedBasicAttributes.map((attribute) => {
1926
2378
  var _a;
1927
2379
  const displayValue = (_a = attributeToDisplayFn[attribute]) == null ? void 0 : _a.call(attributeToDisplayFn, displayData[attribute]);
1928
- const isModuleSpecifier = attribute === "moduleSpecifier";
1929
- const moduleSpecifierValue = typeof displayValue === "string" ? displayValue : String(displayValue ?? displayData.moduleSpecifier ?? "");
1930
- const shouldCapitalizeLabel = attribute !== "workflowCoreVersion";
1931
- const showResumeAtSkeleton = isLoading && resource === "sleep" && !displayData.resumeAt;
1932
- const showDivider = index < orderedBasicAttributes.length - 1 || showResumeAtSkeleton;
1933
- return jsxRuntimeExports.jsxs("div", { className: "py-1", children: [jsxRuntimeExports.jsxs("div", { className: "flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1", children: [jsxRuntimeExports.jsx("span", { className: shouldCapitalizeLabel ? "text-[14px] first-letter:uppercase" : "text-[14px]", style: { color: "var(--ds-gray-700)" }, children: getAttributeDisplayName(attribute) }), isModuleSpecifier ? jsxRuntimeExports.jsx("button", { type: "button", className: "min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono", style: {
1934
- color: "var(--ds-gray-1000)",
1935
- background: "transparent",
1936
- border: "none",
1937
- padding: 0
1938
- }, title: moduleSpecifierValue, onClick: () => handleCopyModuleSpecifier(moduleSpecifierValue), children: moduleSpecifierValue }) : jsxRuntimeExports.jsx("span", { className: "min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono", style: { color: "var(--ds-gray-1000)" }, children: displayValue })] }), showDivider ? jsxRuntimeExports.jsx("div", { className: "mx-2.5 border-b", style: { borderColor: "var(--ds-gray-300)" } }) : null] }, attribute);
1939
- }), isLoading && resource === "sleep" && !displayData.resumeAt && jsxRuntimeExports.jsx("div", { className: "py-1", children: jsxRuntimeExports.jsxs("div", { className: "flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1", children: [jsxRuntimeExports.jsx("span", { className: "text-[14px] first-letter:uppercase", style: { color: "var(--ds-gray-700)" }, children: "resumeAt" }), jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-[55%]" })] }) })] }), error ? jsxRuntimeExports.jsx(ErrorCard, { title: "Failed to load resource details", details: error.message, className: "my-4" }) : hasExpired ? jsxRuntimeExports.jsx(ExpiredDataMessage, {}) : jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: resolvedAttributes.map((attribute) => jsxRuntimeExports.jsx(AttributeBlock, { isLoading, attribute, value: displayData[attribute], context: displayContext }, attribute)) })] }) }) }) });
2380
+ const isCopyableBasicAttribute = copyableBasicAttributes.has(attribute) && typeof displayValue === "string";
2381
+ const label = getAttributeDisplayName(attribute);
2382
+ return jsxRuntimeExports.jsx(DetailMonoKeyValueRow, { label, value: displayValue, copyText: isCopyableBasicAttribute ? displayValue : void 0, href: attribute === "moduleSpecifier" ? moduleSourceUrl : void 0 }, attribute);
2383
+ }), isLoading && resource === "sleep" && !displayData.resumeAt && jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-3 py-0.5", children: [jsxRuntimeExports.jsx("span", { className: "text-label-13 text-gray-900", children: "Resume" }), jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-[55%]" })] })] }) })] }), error ? jsxRuntimeExports.jsx(ErrorCard, { title: "Failed to load resource details", details: error.message, className: "my-4" }) : hasExpired ? jsxRuntimeExports.jsx(ExpiredDataMessage, {}) : resolvedAttributes.length > 0 ? resolvedAttributes.map((attribute) => jsxRuntimeExports.jsx(AttributeBlock, { isLoading, attribute, value: displayData[attribute], context: displayContext }, attribute)) : null] }) }) }) });
1940
2384
  };
1941
- const ERROR_EVENT_TYPES = /* @__PURE__ */ new Set(["step_failed", "step_retrying"]);
1942
- const DATA_EVENT_TYPES = /* @__PURE__ */ new Set([
1943
- "step_created",
1944
- "step_completed",
1945
- "step_failed",
1946
- "step_retrying",
1947
- "hook_created",
1948
- "hook_received",
1949
- "run_created",
1950
- "run_completed",
1951
- "run_failed",
1952
- "wait_created",
1953
- "wait_completed"
1954
- ]);
1955
- function EventItem({ event, onLoadEventData, encryptionKey }) {
2385
+ const parseDateValue = (value) => {
2386
+ if (value == null)
2387
+ return null;
2388
+ const date = value instanceof Date ? value : new Date(String(value));
2389
+ return Number.isNaN(date.getTime()) ? null : date;
2390
+ };
2391
+ const getEffectiveEventDate = (event) => parseDateValue(event.occurredAt) ?? new Date(event.createdAt);
2392
+ const formatEventTimestamp = (date) => date.toLocaleString(void 0, {
2393
+ month: "short",
2394
+ day: "numeric",
2395
+ hour: "numeric",
2396
+ minute: "numeric",
2397
+ second: "numeric",
2398
+ fractionalSecondDigits: 3,
2399
+ timeZoneName: "short"
2400
+ });
2401
+ function EventItem({ event, onLoadEventData, encryptionKey, showSeparateEventOccurrenceTimestamps = false }) {
1956
2402
  const [loadedData, setLoadedData] = reactExports.useState(null);
1957
2403
  const [isLoading, setIsLoading] = reactExports.useState(false);
1958
2404
  const [loadError, setLoadError] = reactExports.useState(null);
@@ -1960,17 +2406,17 @@ function EventItem({ event, onLoadEventData, encryptionKey }) {
1960
2406
  const loadedDataRef = reactExports.useRef(null);
1961
2407
  const existingData = "eventData" in event && event.eventData != null ? event.eventData : null;
1962
2408
  const mergedDisplay = loadedData ?? existingData;
1963
- const canHaveData = DATA_EVENT_TYPES.has(event.eventType);
1964
- const loadEventData = reactExports.useCallback(async (options) => {
1965
- if (!onLoadEventData || !event.correlationId || !event.eventId)
2409
+ const canHaveData = existingData !== null || getEventDataRefFields(event.eventType).length > 0;
2410
+ const loadEventData = reactExports.useCallback(async (force) => {
2411
+ if (!onLoadEventData)
1966
2412
  return;
1967
- if (!(options == null ? void 0 : options.force) && loadedDataRef.current !== null) {
2413
+ if (!force && loadedDataRef.current !== null) {
1968
2414
  return;
1969
2415
  }
1970
2416
  try {
1971
2417
  setIsLoading(true);
1972
2418
  setLoadError(null);
1973
- const data = await onLoadEventData(event.correlationId, event.eventId);
2419
+ const data = await onLoadEventData(event);
1974
2420
  loadedDataRef.current = data;
1975
2421
  setLoadedData(data);
1976
2422
  } catch (err) {
@@ -1978,122 +2424,244 @@ function EventItem({ event, onLoadEventData, encryptionKey }) {
1978
2424
  } finally {
1979
2425
  setIsLoading(false);
1980
2426
  }
1981
- }, [onLoadEventData, event.correlationId, event.eventId]);
1982
- const handleExpand = reactExports.useCallback(async () => {
2427
+ }, [onLoadEventData, event]);
2428
+ const handleExpand = reactExports.useCallback(() => {
1983
2429
  if (isLoading)
1984
2430
  return;
1985
2431
  wasExpandedRef.current = true;
1986
- await loadEventData();
2432
+ void loadEventData(false);
1987
2433
  }, [isLoading, loadEventData]);
1988
- reactExports.useEffect(() => {
2434
+ reactExports.useLayoutEffect(() => {
1989
2435
  if (!encryptionKey || !wasExpandedRef.current)
1990
2436
  return;
1991
2437
  loadedDataRef.current = null;
1992
2438
  setLoadedData(null);
1993
- void loadEventData({ force: true });
2439
+ void loadEventData(true);
1994
2440
  }, [encryptionKey, loadEventData]);
1995
2441
  const createdAt = new Date(event.createdAt);
2442
+ const occurredAt = parseDateValue(event.occurredAt);
2443
+ const displayedCreatedAt = showSeparateEventOccurrenceTimestamps ? createdAt : getEffectiveEventDate(event);
2444
+ const displayedCreatedAtTime = displayedCreatedAt.toLocaleTimeString(void 0, {
2445
+ hour: "numeric",
2446
+ minute: "numeric",
2447
+ second: "numeric"
2448
+ });
1996
2449
  const displayPayload = isLoading ? loadedData : mergedDisplay;
1997
- return jsxRuntimeExports.jsxs(DetailCard, { summaryClassName: "text-base py-2", summary: jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("span", { className: "font-medium", style: { color: "var(--ds-gray-1000)" }, children: event.eventType }), " ", "-", " ", jsxRuntimeExports.jsx("span", { style: { color: "var(--ds-gray-700)" }, children: localMillisecondTime(createdAt.getTime()) })] }), onToggle: canHaveData ? (open) => {
2450
+ return jsxRuntimeExports.jsxs(CollapsibleRoot, { variant: "card", onOpenChange: canHaveData ? (open) => {
1998
2451
  if (open)
1999
2452
  handleExpand();
2000
- } : void 0, children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col divide-y rounded-md border overflow-hidden", style: {
2001
- borderColor: "var(--ds-gray-300)",
2002
- backgroundColor: "var(--ds-gray-100)"
2003
- }, children: [jsxRuntimeExports.jsxs("div", { className: "flex min-h-[32px] items-center justify-between gap-4 px-2.5 py-1.5", style: { borderColor: "var(--ds-gray-300)" }, children: [jsxRuntimeExports.jsx("span", { className: "text-[13px] font-medium", style: { color: "var(--ds-gray-700)" }, children: "Event ID" }), jsxRuntimeExports.jsx("span", { className: "max-w-[70%] truncate text-right text-[13px] font-mono", style: { color: "var(--ds-gray-1000)" }, title: event.eventId, children: event.eventId })] }), event.correlationId && jsxRuntimeExports.jsxs("div", { className: "flex min-h-[32px] items-center justify-between gap-4 px-2.5 py-1.5", style: { borderColor: "var(--ds-gray-300)" }, children: [jsxRuntimeExports.jsx("span", { className: "text-[13px] font-medium", style: { color: "var(--ds-gray-700)" }, children: "Correlation ID" }), jsxRuntimeExports.jsx("span", { className: "max-w-[70%] truncate text-right text-[13px] font-mono", style: { color: "var(--ds-gray-1000)" }, title: event.correlationId, children: event.correlationId })] })] }), isLoading && jsxRuntimeExports.jsxs("div", { className: "mt-2 rounded-md border p-3", style: {
2004
- borderColor: "var(--ds-gray-300)"
2005
- }, children: [jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-[35%]" }), jsxRuntimeExports.jsx(Skeleton, { className: "mt-2 h-4 w-[90%]" }), jsxRuntimeExports.jsx(Skeleton, { className: "mt-2 h-4 w-[75%]" })] }), loadError && jsxRuntimeExports.jsx(ErrorCard, { title: "Failed to load event data", details: loadError, className: "mt-2" }), displayPayload != null && jsxRuntimeExports.jsx("div", { className: "mt-2", children: jsxRuntimeExports.jsx(EventDataBlock, { eventType: event.eventType, data: displayPayload }) })] });
2453
+ } : void 0, children: [jsxRuntimeExports.jsx(CollapsibleTrigger, { className: "px-3 py-2", children: jsxRuntimeExports.jsxs("div", { className: "flex w-full items-center justify-between gap-3", children: [jsxRuntimeExports.jsx("span", { className: "text-gray-1000 text-label-12 font-mono", children: event.eventType }), jsxRuntimeExports.jsx("span", { className: "shrink-0 text-label-13 text-gray-900", children: displayedCreatedAtTime })] }) }), jsxRuntimeExports.jsxs(CollapsibleContent, { children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col bg-background-200 [&:has(+_*)]:border-b [&:has(+_*)]:border-gray-alpha-400", children: [showSeparateEventOccurrenceTimestamps && occurredAt && jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2 py-2 px-3", children: [jsxRuntimeExports.jsx("span", { className: "text-label-12 text-gray-900", children: "Occurred" }), jsxRuntimeExports.jsx(TimestampTooltip, { date: occurredAt, children: jsxRuntimeExports.jsx("span", { className: "max-w-[70%] truncate text-right text-label-12 font-mono", children: formatEventTimestamp(occurredAt) }) })] }), jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2 py-2 px-3", children: [jsxRuntimeExports.jsx("span", { className: "text-label-12 text-gray-900", children: "Event ID" }), jsxRuntimeExports.jsx("span", { className: "max-w-[70%] truncate text-right text-label-12 font-mono", children: event.eventId })] }), event.correlationId && jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2 py-2 px-3", children: [jsxRuntimeExports.jsx("span", { className: "text-label-12 text-gray-900", children: "Correlation ID" }), jsxRuntimeExports.jsx("span", { className: "max-w-[70%] truncate text-right text-label-12 font-mono", children: event.correlationId })] })] }), isLoading && jsxRuntimeExports.jsxs("div", { className: "p-3", children: [jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-[35%]" }), jsxRuntimeExports.jsx(Skeleton, { className: "mt-2 h-4 w-[90%]" }), jsxRuntimeExports.jsx(Skeleton, { className: "mt-2 h-4 w-[75%]" })] }), loadError && jsxRuntimeExports.jsx(ErrorCard, { title: "Failed to load event data", details: loadError, className: "mt-2" }), displayPayload != null && jsxRuntimeExports.jsx("div", { className: "[&>div]:border-none [&>div]:rounded-none", children: jsxRuntimeExports.jsx(EventDataBlock, { eventType: event.eventType, data: displayPayload }) })] })] });
2006
2454
  }
2007
2455
  function hasOnlyExpiredFields(data, eventType) {
2008
2456
  if (data === null || typeof data !== "object" || Array.isArray(data)) {
2009
2457
  return false;
2010
2458
  }
2011
2459
  const record = data;
2012
- const refKeys = EVENT_DATA_REF_FIELDS[eventType] ?? [];
2460
+ const refKeys = getEventDataRefFields(eventType);
2013
2461
  const presentKeys = refKeys.filter((k) => k in record);
2014
2462
  return presentKeys.length > 0 && presentKeys.every((k) => isExpiredMarker(record[k]));
2015
2463
  }
2016
2464
  function EventDataBlock({ eventType, data }) {
2017
2465
  if (isExpiredMarker(data) || hasOnlyExpiredFields(data, eventType)) {
2018
- return jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1.5 rounded-md border px-3 py-2 text-xs", style: {
2466
+ return jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1.5 rounded-md border px-3 py-2 text-label-12", style: {
2019
2467
  borderColor: "var(--ds-gray-300)",
2020
2468
  backgroundColor: "var(--ds-gray-100)",
2021
2469
  color: "var(--ds-gray-700)"
2022
2470
  }, children: jsxRuntimeExports.jsx("span", { className: "font-medium", children: "Data expired" }) });
2023
2471
  }
2024
- if (ERROR_EVENT_TYPES.has(eventType) && data != null && typeof data === "object") {
2472
+ if (hasEncryptedFields({ eventType, eventData: data })) {
2473
+ return jsxRuntimeExports.jsx(EncryptedDataBlock, {});
2474
+ }
2475
+ if (eventType === "attr_set") {
2476
+ return jsxRuntimeExports.jsx(AttrSetEventBlock, { data });
2477
+ }
2478
+ if (getEventDataRefFields(eventType).includes("error") && data != null && typeof data === "object") {
2025
2479
  const record = data;
2026
- if (isStructuredErrorWithStack(record.error)) {
2480
+ if (isStructuredError(record.error)) {
2027
2481
  return jsxRuntimeExports.jsx(ErrorStackBlock, { value: record.error });
2028
2482
  }
2029
- if (isStructuredErrorWithStack(record)) {
2483
+ if (isStructuredError(record)) {
2030
2484
  return jsxRuntimeExports.jsx(ErrorStackBlock, { value: record });
2031
2485
  }
2032
2486
  }
2033
2487
  return jsxRuntimeExports.jsx(CopyableDataBlock, { data });
2034
2488
  }
2035
- function EventsList({ events, isLoading = false, error, onLoadEventData, encryptionKey }) {
2036
- const sortedEvents = reactExports.useMemo(() => [...events].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()), [events]);
2037
- return jsxRuntimeExports.jsxs("div", { className: "mt-2", style: { color: "var(--ds-gray-1000)" }, children: [jsxRuntimeExports.jsx("h3", { className: "text-label-14 font-medium mt-4 mb-2", style: { color: "var(--ds-gray-1000)" }, children: "Events" }), isLoading ? jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-4", children: [jsxRuntimeExports.jsx(Skeleton, { className: "h-9 w-full rounded-md" }), jsxRuntimeExports.jsx(Skeleton, { className: "h-9 w-full rounded-md" }), jsxRuntimeExports.jsx(Skeleton, { className: "h-9 w-full rounded-md" })] }) : null, !isLoading && !error && sortedEvents.length === 0 && jsxRuntimeExports.jsx("div", { className: "text-sm", children: "No events found" }), sortedEvents.length > 0 && !error ? jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-4", children: sortedEvents.map((event) => jsxRuntimeExports.jsx(EventItem, { event, onLoadEventData, encryptionKey }, event.eventId)) }) : null] });
2489
+ function EventsList({ events, isLoading = false, error, onLoadEventData, onStreamClick, onRunClick, encryptionKey, showSeparateEventOccurrenceTimestamps = false }) {
2490
+ const sortedEvents = reactExports.useMemo(() => [...events].sort((a, b) => getEffectiveEventDate(a).getTime() - getEffectiveEventDate(b).getTime()), [events]);
2491
+ const hasEvents = sortedEvents.length > 0 && !error;
2492
+ if (!hasEvents && !isLoading) {
2493
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Events", disabled: true });
2494
+ }
2495
+ return jsxRuntimeExports.jsx(RunClickContext.Provider, { value: onRunClick, children: jsxRuntimeExports.jsx(StreamClickContext.Provider, { value: onStreamClick, children: jsxRuntimeExports.jsxs(CollapsibleRoot, { defaultOpen: true, children: [jsxRuntimeExports.jsx(CollapsibleTrigger, { children: "Events" }), jsxRuntimeExports.jsx(CollapsibleContent, { className: "mb-0", children: isLoading ? jsxRuntimeExports.jsx("div", { className: "flex flex-col -mx-4", children: [0, 1, 2].map((i) => jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-3 bg-background-200 px-4 py-2", children: [jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-32 rounded" }), jsxRuntimeExports.jsx(Skeleton, { className: "h-3 w-16 rounded" })] }, i)) }) : jsxRuntimeExports.jsx("div", { className: "flex flex-col -mx-4", children: sortedEvents.map((event) => jsxRuntimeExports.jsx(EventItem, { event, onLoadEventData, encryptionKey, showSeparateEventOccurrenceTimestamps }, event.eventId)) }) })] }) }) });
2496
+ }
2497
+ const hasField = (value, key) => key in value;
2498
+ const RESOURCES_WITH_FETCHED_DETAIL = /* @__PURE__ */ new Set(["run", "step", "sleep"]);
2499
+ function resourceNeedsFetchedDetail(resource) {
2500
+ return resource !== void 0 && RESOURCES_WITH_FETCHED_DETAIL.has(resource);
2501
+ }
2502
+ function deriveSpanDetailView(args) {
2503
+ const { resource, resourceId, inlineData, fetchedDetail, fetchedError } = args;
2504
+ const matchedDetail = spanDetailMatchesSelection(fetchedDetail, resource, resourceId) ? fetchedDetail : null;
2505
+ const displayData = mergeSpanDetail(inlineData, matchedDetail);
2506
+ if (!resource || !resourceId) {
2507
+ return { status: "idle", displayData, detail: null, error: void 0 };
2508
+ }
2509
+ if (fetchedError) {
2510
+ return {
2511
+ status: "error",
2512
+ displayData,
2513
+ detail: matchedDetail,
2514
+ error: fetchedError
2515
+ };
2516
+ }
2517
+ if (!resourceNeedsFetchedDetail(resource)) {
2518
+ return {
2519
+ status: "ready",
2520
+ displayData,
2521
+ detail: matchedDetail,
2522
+ error: void 0
2523
+ };
2524
+ }
2525
+ if (matchedDetail !== null) {
2526
+ return {
2527
+ status: "ready",
2528
+ displayData,
2529
+ detail: matchedDetail,
2530
+ error: void 0
2531
+ };
2532
+ }
2533
+ return { status: "loading", displayData, detail: null, error: void 0 };
2534
+ }
2535
+ function spanDetailMatchesSelection(detail, resource, resourceId) {
2536
+ if (!detail || typeof detail !== "object" || !resource || !resourceId) {
2537
+ return false;
2538
+ }
2539
+ switch (resource) {
2540
+ case "step":
2541
+ return hasField(detail, "stepId") && detail.stepId === resourceId;
2542
+ case "hook":
2543
+ return hasField(detail, "hookId") && detail.hookId === resourceId;
2544
+ case "sleep":
2545
+ return hasField(detail, "waitId") && detail.waitId === resourceId;
2546
+ case "run":
2547
+ return hasField(detail, "runId") && !("stepId" in detail) && !("hookId" in detail) && !("waitId" in detail) && detail.runId === resourceId;
2548
+ default:
2549
+ return false;
2550
+ }
2551
+ }
2552
+ function mergeSpanDetail(spanData, detail) {
2553
+ if (!detail || typeof detail !== "object") {
2554
+ return spanData;
2555
+ }
2556
+ if (!spanData || typeof spanData !== "object") {
2557
+ return detail;
2558
+ }
2559
+ const merged = { ...detail };
2560
+ for (const [key, value] of Object.entries(spanData)) {
2561
+ if (value !== void 0) {
2562
+ merged[key] = value;
2563
+ }
2564
+ }
2565
+ return merged;
2038
2566
  }
2039
- function isStep(data) {
2040
- return data !== null && typeof data === "object" && "stepId" in data;
2567
+ function deriveSpanSelection(selectedSpan) {
2568
+ if (!selectedSpan)
2569
+ return null;
2570
+ const { resource, data } = selectedSpan;
2571
+ if (resource === "step" && data && typeof data === "object" && "stepId" in data) {
2572
+ const step = data;
2573
+ return { resource: "step", resourceId: step.stepId, runId: step.runId };
2574
+ }
2575
+ if (resource === "run" && data && typeof data === "object" && "runId" in data) {
2576
+ return { resource: "run", resourceId: data.runId };
2577
+ }
2578
+ if (resource === "hook" && data && typeof data === "object" && "hookId" in data) {
2579
+ return { resource: "hook", resourceId: data.hookId };
2580
+ }
2581
+ if (resource === "sleep") {
2582
+ if (!selectedSpan.spanId)
2583
+ return null;
2584
+ const waitData = data;
2585
+ return {
2586
+ resource: "sleep",
2587
+ resourceId: selectedSpan.spanId,
2588
+ runId: waitData == null ? void 0 : waitData.runId
2589
+ };
2590
+ }
2591
+ return null;
2041
2592
  }
2042
- function isWorkflowRun(data) {
2043
- return data !== null && typeof data === "object" && "runId" in data;
2593
+ function useSelectedSpanDetail(selectedSpan, fetchSpanDetail) {
2594
+ const selection = reactExports.useMemo(() => deriveSpanSelection(selectedSpan), [selectedSpan]);
2595
+ const resource = selection == null ? void 0 : selection.resource;
2596
+ const resourceId = selection == null ? void 0 : selection.resourceId;
2597
+ const runId = selection == null ? void 0 : selection.runId;
2598
+ const selectionKey = resource && resourceId ? `${resource}:${resourceId}` : null;
2599
+ const needsFetch = resourceNeedsFetchedDetail(resource);
2600
+ const [fetched, setFetched] = reactExports.useState({ detail: null, error: null, errorKey: null });
2601
+ const tokenRef = reactExports.useRef(0);
2602
+ const selectionRef = reactExports.useRef(selection);
2603
+ selectionRef.current = selection;
2604
+ reactExports.useEffect(() => {
2605
+ if (!selectionKey || !needsFetch) {
2606
+ return;
2607
+ }
2608
+ const activeSelection = selectionRef.current;
2609
+ if (!activeSelection) {
2610
+ return;
2611
+ }
2612
+ const token = ++tokenRef.current;
2613
+ fetchSpanDetail(activeSelection).then((detail) => {
2614
+ if (tokenRef.current !== token)
2615
+ return;
2616
+ setFetched({ detail, error: null, errorKey: null });
2617
+ }).catch((err) => {
2618
+ if (tokenRef.current !== token)
2619
+ return;
2620
+ setFetched({
2621
+ detail: null,
2622
+ error: err instanceof Error ? err : new Error(String(err)),
2623
+ errorKey: selectionKey
2624
+ });
2625
+ });
2626
+ }, [selectionKey, runId, needsFetch, fetchSpanDetail]);
2627
+ const scopedError = fetched.errorKey === selectionKey ? fetched.error : null;
2628
+ const view = reactExports.useMemo(() => deriveSpanDetailView({
2629
+ resource,
2630
+ resourceId,
2631
+ inlineData: selectedSpan == null ? void 0 : selectedSpan.data,
2632
+ fetchedDetail: fetched.detail,
2633
+ fetchedError: scopedError
2634
+ }), [resource, resourceId, selectedSpan == null ? void 0 : selectedSpan.data, fetched.detail, scopedError]);
2635
+ return {
2636
+ status: view.status,
2637
+ resource,
2638
+ resourceId,
2639
+ runId,
2640
+ displayData: view.displayData,
2641
+ detail: view.detail,
2642
+ error: view.error
2643
+ };
2044
2644
  }
2045
2645
  function isHook(data) {
2046
2646
  return data !== null && typeof data === "object" && "hookId" in data;
2047
2647
  }
2048
- function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spanDetailError, spanDetailLoading, onSpanSelect, onWakeUpSleep, onLoadEventData, onResolveHook, encryptionKey, onDecrypt, isDecrypting = false, selectedSpan }) {
2648
+ function EntityDetailPanel({ run, onStreamClick, onRunClick, fetchSpanDetail, onWakeUpSleep, onLoadEventData, onResolveHook, encryptionKey, onDecrypt, isDecrypting = false, selectedSpan, showSeparateEventOccurrenceTimestamps = false, getModuleSourceUrl }) {
2049
2649
  const toast2 = useToast();
2050
2650
  const [stoppingSleep, setStoppingSleep] = reactExports.useState(false);
2051
2651
  const [showResolveHookModal, setShowResolveHookModal] = reactExports.useState(false);
2052
2652
  const [resolvingHook, setResolvingHook] = reactExports.useState(false);
2053
2653
  const [resolvedHookIds, setResolvedHookIds] = reactExports.useState(/* @__PURE__ */ new Set());
2654
+ const sidebar = useSidebarDataOptional();
2655
+ const hasEncryptedData = Boolean((sidebar == null ? void 0 : sidebar.hasEncryptedData) && !encryptionKey);
2054
2656
  const data = selectedSpan == null ? void 0 : selectedSpan.data;
2055
2657
  const rawEvents = selectedSpan == null ? void 0 : selectedSpan.rawEvents;
2056
2658
  const rawEventsLength = (rawEvents == null ? void 0 : rawEvents.length) ?? 0;
2057
- const { resource, resourceId, runId } = reactExports.useMemo(() => {
2058
- if (!selectedSpan) {
2059
- return { resource: void 0, resourceId: void 0, runId: void 0 };
2060
- }
2061
- const res = selectedSpan.resource;
2062
- if (res === "step" && isStep(data)) {
2063
- return { resource: "step", resourceId: data.stepId, runId: data.runId };
2064
- }
2065
- if (res === "run" && isWorkflowRun(data)) {
2066
- return { resource: "run", resourceId: data.runId, runId: void 0 };
2067
- }
2068
- if (res === "hook" && isHook(data)) {
2069
- return { resource: "hook", resourceId: data.hookId, runId: void 0 };
2070
- }
2071
- if (res === "sleep") {
2072
- const waitData = data;
2073
- return {
2074
- resource: "sleep",
2075
- resourceId: selectedSpan.spanId,
2076
- runId: waitData == null ? void 0 : waitData.runId
2077
- };
2078
- }
2079
- return { resource: void 0, resourceId: void 0, runId: void 0 };
2080
- }, [selectedSpan, data]);
2081
- const onSpanSelectRef = reactExports.useRef(onSpanSelect);
2082
- onSpanSelectRef.current = onSpanSelect;
2083
- reactExports.useEffect(() => {
2084
- if (resource && resourceId && ["run", "step", "hook", "sleep"].includes(resource)) {
2085
- onSpanSelectRef.current({
2086
- resource,
2087
- resourceId,
2088
- runId
2089
- });
2090
- }
2091
- }, [resource, resourceId, runId]);
2659
+ const { status, resource, resourceId, displayData, detail: matchedSpanDetailData, error } = useSelectedSpanDetail(selectedSpan, fetchSpanDetail);
2660
+ const loading = status === "loading";
2092
2661
  const canWakeUp = reactExports.useMemo(() => {
2093
2662
  if (resource !== "sleep" || !rawEvents)
2094
2663
  return false;
2095
- const terminalStates = ["completed", "failed", "cancelled"];
2096
- if (terminalStates.includes(run.status))
2664
+ if (isTerminalWorkflowRunStatus(run.status))
2097
2665
  return false;
2098
2666
  const hasWaitCreated = rawEvents.some((e) => e.eventType === "wait_created");
2099
2667
  if (!hasWaitCreated)
@@ -2106,8 +2674,7 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2106
2674
  return false;
2107
2675
  if (resolvedHookIds.has(resourceId))
2108
2676
  return false;
2109
- const terminalStates = ["completed", "failed", "cancelled"];
2110
- if (terminalStates.includes(run.status))
2677
+ if (isTerminalWorkflowRunStatus(run.status))
2111
2678
  return false;
2112
2679
  const hasHookDisposed = rawEvents.some((e) => e.eventType === "hook_disposed");
2113
2680
  if (hasHookDisposed)
@@ -2121,19 +2688,17 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2121
2688
  run.status,
2122
2689
  resolvedHookIds
2123
2690
  ]);
2124
- const error = spanDetailError ?? void 0;
2125
- const loading = spanDetailLoading ?? false;
2126
2691
  const hookToken = reactExports.useMemo(() => {
2127
2692
  if (resource !== "hook" || !resourceId)
2128
2693
  return void 0;
2129
- if (isHook(spanDetailData) && spanDetailData.token) {
2130
- return spanDetailData.token;
2694
+ if (isHook(matchedSpanDetailData) && matchedSpanDetailData.token) {
2695
+ return matchedSpanDetailData.token;
2131
2696
  }
2132
2697
  if (isHook(data) && data.token) {
2133
2698
  return data.token;
2134
2699
  }
2135
2700
  return void 0;
2136
- }, [resource, resourceId, spanDetailData, data]);
2701
+ }, [resource, resourceId, matchedSpanDetailData, data]);
2137
2702
  reactExports.useEffect(() => {
2138
2703
  if (error && selectedSpan && resource) {
2139
2704
  toast2.error(`Failed to load ${resource} details`, {
@@ -2188,7 +2753,7 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2188
2753
  }
2189
2754
  try {
2190
2755
  setResolvingHook(true);
2191
- const candidate = spanDetailData ?? data;
2756
+ const candidate = matchedSpanDetailData ?? data;
2192
2757
  const hook = isHook(candidate) ? candidate : void 0;
2193
2758
  await onResolveHook(hookToken, payload, hook);
2194
2759
  toast2.success("Hook resolved", {
@@ -2206,8 +2771,7 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2206
2771
  } finally {
2207
2772
  setResolvingHook(false);
2208
2773
  }
2209
- }, [onResolveHook, hookToken, resolvingHook, spanDetailData, data]);
2210
- const displayData = spanDetailData ?? data;
2774
+ }, [onResolveHook, hookToken, resolvingHook, matchedSpanDetailData, data]);
2211
2775
  const moduleSpecifier = reactExports.useMemo(() => {
2212
2776
  const displayRecord = displayData;
2213
2777
  const displayStepName = displayRecord.stepName;
@@ -2217,391 +2781,353 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2217
2781
  }
2218
2782
  if (typeof displayWorkflowName === "string") {
2219
2783
  return displayWorkflowName;
2220
- }
2221
- if (typeof run.workflowName === "string") {
2222
- return run.workflowName;
2223
- }
2224
- return void 0;
2225
- }, [displayData, run.workflowName]);
2226
- if (!selectedSpan || !resource || !resourceId) {
2227
- return null;
2228
- }
2229
- const hasPendingActions = resource === "sleep" && canWakeUp || resource === "hook" && canResolveHook;
2230
- return jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col", children: [jsxRuntimeExports.jsx(DecryptClickContext.Provider, { value: onDecrypt ? { onDecrypt, isDecrypting } : void 0, children: jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto px-3 pt-3 pb-8", children: [hasPendingActions && jsxRuntimeExports.jsxs("div", { className: "mb-4 rounded-lg border p-2", style: {
2231
- borderColor: "var(--ds-gray-300)",
2232
- backgroundColor: "var(--ds-gray-100)"
2233
- }, children: [jsxRuntimeExports.jsx("p", { className: "mb-2 px-1 text-[13px] font-medium uppercase tracking-wide", style: { color: "var(--ds-gray-700)" }, children: "Actions" }), jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [resource === "sleep" && canWakeUp && jsxRuntimeExports.jsxs("button", { type: "button", onClick: handleWakeUp, disabled: stoppingSleep, className: clsx("flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium", "disabled:opacity-50 disabled:cursor-not-allowed transition-colors", stoppingSleep ? "opacity-50 cursor-not-allowed" : "cursor-pointer"), style: {
2234
- background: "var(--ds-amber-200)",
2235
- color: "var(--ds-amber-900)"
2236
- }, children: [jsxRuntimeExports.jsx(Zap, { className: "h-4 w-4" }), stoppingSleep ? "Waking up..." : "Wake Up Sleep"] }), resource === "hook" && canResolveHook && jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setShowResolveHookModal(true), disabled: resolvingHook, className: clsx("flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium", "disabled:opacity-50 disabled:cursor-not-allowed transition-colors", resolvingHook ? "opacity-50 cursor-not-allowed" : "cursor-pointer"), style: {
2237
- background: "var(--ds-gray-1000)",
2238
- color: "var(--ds-background-100)"
2239
- }, children: [jsxRuntimeExports.jsx(Send, { className: "h-4 w-4" }), "Resolve Hook"] })] })] }), jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [jsxRuntimeExports.jsx("section", { children: jsxRuntimeExports.jsx(AttributePanel, { data: displayData, moduleSpecifier, expiredAt: run.expiredAt, isLoading: loading, error: error ?? void 0, onStreamClick, onRunClick, onDecrypt, isDecrypting, resource }) }), resource !== "run" && rawEvents && jsxRuntimeExports.jsx("section", { children: jsxRuntimeExports.jsx(EventsList, { events: rawEvents, onLoadEventData, encryptionKey }) })] })] }) }), jsxRuntimeExports.jsx(ResolveHookModal, { isOpen: showResolveHookModal, onClose: () => setShowResolveHookModal(false), onSubmit: handleResolveHook, isSubmitting: resolvingHook })] });
2240
- }
2241
- function CopyButton({ copyText, ariaLabel, className }) {
2242
- const [copied, setCopied] = reactExports.useState(false);
2243
- const timeoutRef = reactExports.useRef(null);
2244
- reactExports.useEffect(() => {
2245
- return () => {
2246
- if (timeoutRef.current) {
2247
- clearTimeout(timeoutRef.current);
2248
- }
2249
- };
2250
- }, []);
2251
- return jsxRuntimeExports.jsx("button", { type: "button", "aria-label": ariaLabel, className: cn("cursor-pointer text-gray-800 hover:text-gray-1000 bg-transparent border-none p-1 m-0", className), onClick: (e) => {
2252
- e.stopPropagation();
2253
- if (timeoutRef.current) {
2254
- clearTimeout(timeoutRef.current);
2255
- }
2256
- void navigator.clipboard.writeText(copyText).then(() => {
2257
- setCopied(true);
2258
- timeoutRef.current = setTimeout(() => setCopied(false), 1e3);
2259
- });
2260
- }, children: jsxRuntimeExports.jsxs("div", { className: "relative w-3 h-3", children: [jsxRuntimeExports.jsx("div", { className: cn("absolute inset-0 flex items-center justify-center transition-all duration-150 ease-out", copied ? "scale-100 opacity-100" : "scale-0 opacity-0"), children: jsxRuntimeExports.jsx(Check, { className: "w-3 h-3" }) }), jsxRuntimeExports.jsx("div", { className: cn("absolute inset-0 flex items-center justify-center transition-all duration-150 ease-out", copied ? "scale-0 opacity-0" : "scale-100 opacity-100"), children: jsxRuntimeExports.jsx(Copy, { className: "w-3 h-3" }) })] }) });
2261
- }
2262
- const WorkflowIcon = () => {
2263
- return jsxRuntimeExports.jsx("svg", { "data-testid": "geist-icon", height: "16", "stroke-linejoin": "round", style: { color: "currentColor" }, viewBox: "0 0 16 16", width: "16", children: jsxRuntimeExports.jsx("path", { d: "M5.75 7L6.4209 7.33594L4.85254 10.4717C5.38789 10.771 5.75 11.3431 5.75 12V14C5.75 14.9665 4.96648 15.75 4 15.75H2C1.03352 15.75 0.25 14.9665 0.25 14V12C0.25 11.0335 1.03352 10.25 2 10.25H3.28711L5.0791 6.66504L5.75 7ZM14 10.25C14.9665 10.25 15.75 11.0335 15.75 12V14C15.75 14.9665 14.9665 15.75 14 15.75H12C11.1185 15.75 10.3909 15.098 10.2695 14.25H7V12.75H10.25V12C10.25 11.0335 11.0335 10.25 12 10.25H14ZM2 11.75C1.86192 11.75 1.75 11.8619 1.75 12V14C1.75 14.1381 1.86192 14.25 2 14.25H4C4.13808 14.25 4.25 14.1381 4.25 14V12C4.25 11.8619 4.13808 11.75 4 11.75H2ZM12 11.75C11.8619 11.75 11.75 11.8619 11.75 12V14C11.75 14.1381 11.8619 14.25 12 14.25H14C14.1381 14.25 14.25 14.1381 14.25 14V12C14.25 11.8619 14.1381 11.75 14 11.75H12ZM9 0.25C9.96649 0.25 10.75 1.03351 10.75 2V4C10.75 4.44642 10.5808 4.85197 10.3057 5.16113L12.2041 8.6416L10.8877 9.36035L8.91895 5.75H7C6.03351 5.75 5.25 4.96649 5.25 4V2C5.25 1.03351 6.03351 0.25 7 0.25H9ZM7 1.75C6.86193 1.75 6.75 1.86193 6.75 2V4C6.75 4.13807 6.86193 4.25 7 4.25H9C9.13807 4.25 9.25 4.13807 9.25 4V2C9.25 1.86193 9.13807 1.75 9 1.75H7Z", fill: "currentColor" }) });
2264
- };
2265
- const WebhookIcon = () => {
2266
- return jsxRuntimeExports.jsx("svg", { "data-testid": "geist-icon", height: "16", "stroke-linejoin": "round", viewBox: "0 0 16 16", width: "16", style: { color: "currentColor" }, children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M5.8349 3.24994C5.14454 4.44567 5.55423 5.97464 6.74996 6.665L7.39465 7.03721L7.02726 7.68466L5.14599 11C5.21289 11.1531 5.25 11.3222 5.25 11.5C5.25 12.1904 4.69036 12.75 4 12.75C3.30964 12.75 2.75 12.1904 2.75 11.5C2.75 10.8634 3.22584 10.338 3.84123 10.26L5.38944 7.5316C3.95909 6.30274 3.55857 4.19264 4.53586 2.49994C5.64043 0.586765 8.08679 -0.0687357 9.99996 1.03583C11.836 2.09584 12.5137 4.39156 11.5892 6.26591L10.4949 4.33754C10.4418 3.53274 10.0009 2.76843 9.24996 2.33487C8.05423 1.64452 6.52525 2.0542 5.8349 3.24994ZM9.146 4.99996C9.21289 4.84686 9.25 4.67776 9.25 4.5C9.25 3.80964 8.69036 3.25 8 3.25C7.30964 3.25 6.75 3.80964 6.75 4.5C6.75 5.13659 7.22586 5.66203 7.84127 5.74002L9.72266 9.05556L10.0956 9.71274L10.75 9.33493C11.9457 8.64458 13.4747 9.05427 14.165 10.25C14.8554 11.4457 14.4457 12.9747 13.25 13.6651C12.5047 14.0953 11.63 14.0983 10.9096 13.75H8.69252C9.85895 15.4685 12.1727 16.0191 14 14.9641C15.9131 13.8595 16.5686 11.4132 15.4641 9.5C14.4886 7.81038 12.4665 7.10169 10.6895 7.71998L9.146 4.99996ZM13.25 11.5C13.25 12.1904 12.6904 12.75 12 12.75C11.591 12.75 11.228 12.5536 10.9999 12.25H7.9298C7.57875 14.1006 5.95279 15.5 4 15.5C1.79086 15.5 0 13.7091 0 11.5C0 9.40018 1.61801 7.67826 3.67529 7.51299L2.58098 9.44147C1.92792 9.89251 1.5 10.6463 1.5 11.5C1.5 12.8807 2.61929 14 4 14C5.38071 14 6.5 12.8807 6.5 11.5V10.75H7.25H10.9999C11.228 10.4464 11.591 10.25 12 10.25C12.6904 10.25 13.25 10.8096 13.25 11.5Z", fill: "currentColor" }) });
2267
- };
2268
- const SleepIcon = () => {
2269
- return jsxRuntimeExports.jsx("svg", { "data-testid": "geist-icon", height: "16", strokeLinejoin: "round", viewBox: "0 0 16 16", width: "16", style: { color: "currentColor" }, children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M7.25 1.25V2.03971C5.87928 2.18571 4.62678 2.72736 3.6089 3.54824L3.03033 2.96967L2.5 2.43934L1.43934 3.5L1.96967 4.03033L2.54824 4.6089C1.57979 5.80976 1 7.33717 1 9C1 12.866 4.13401 16 8 16C11.866 16 15 12.866 15 9C15 7.33717 14.4202 5.80976 13.4518 4.6089L14.0303 4.03033L14.5607 3.5L13.5 2.43934L12.9697 2.96967L12.3911 3.54824C11.3732 2.72736 10.1207 2.18571 8.75 2.03971V1.25H9.25H10V-0.25H9.25H8.75H7.25H6.75H6V1.25H6.75H7.25ZM2.5 9C2.5 5.96243 4.96243 3.5 8 3.5C11.0376 3.5 13.5 5.96243 13.5 9C13.5 12.0376 11.0376 14.5 8 14.5C4.96243 14.5 2.5 12.0376 2.5 9ZM8.75 6.75V6H7.25V6.75V9V9.75H8.75V9V6.75Z", fill: "currentColor" }) });
2270
- };
2271
- const StepForwardIcon = () => {
2272
- return jsxRuntimeExports.jsx("svg", { "data-testid": "geist-icon", height: "16", strokeLinejoin: "round", viewBox: "0 0 16 16", width: "16", style: { color: "currentColor" }, children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4.21969 12.5303L4.75002 13.0607L5.81068 12L5.28035 11.4697L1.81068 7.99999L5.28035 4.53032L5.81068 3.99999L4.75002 2.93933L4.21969 3.46966L0.39647 7.29289C0.00594562 7.68341 0.00594562 8.31658 0.39647 8.7071L4.21969 12.5303ZM11.7804 12.5303L11.25 13.0607L10.1894 12L10.7197 11.4697L14.1894 7.99999L10.7197 4.53032L10.1894 3.99999L11.25 2.93933L11.7804 3.46966L15.6036 7.29289C15.9941 7.68341 15.9941 8.31658 15.6036 8.7071L11.7804 12.5303Z", fill: "currentColor" }) });
2273
- };
2274
- const ELLIPSIS = "...";
2275
- const MIN_START = 3;
2276
- const MIN_END = 3;
2277
- const MIN_KEPT = MIN_START + MIN_END;
2278
- const graphemeSegmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
2279
- function toGraphemes(text) {
2280
- if (graphemeSegmenter) {
2281
- return [...graphemeSegmenter.segment(text)].map((s) => s.segment);
2282
- }
2283
- return Array.from(text);
2284
- }
2285
- function buildCandidate(graphemes, kept) {
2286
- if (kept <= 0) {
2287
- return {
2288
- prefixText: "",
2289
- prefixGraphemeCount: 0,
2290
- suffixText: "",
2291
- suffixGraphemeCount: 0,
2292
- text: ELLIPSIS,
2293
- truncated: true
2294
- };
2295
- }
2296
- const suffixGraphemeCount = kept >= MIN_KEPT ? Math.max(MIN_END, Math.floor(kept / 2)) : Math.floor(kept / 2);
2297
- const prefixGraphemeCount = kept - suffixGraphemeCount;
2298
- const prefixText = graphemes.slice(0, prefixGraphemeCount).join("");
2299
- const suffixText = suffixGraphemeCount > 0 ? graphemes.slice(-suffixGraphemeCount).join("") : "";
2300
- return {
2301
- prefixText,
2302
- prefixGraphemeCount,
2303
- suffixText,
2304
- suffixGraphemeCount,
2305
- text: prefixText + ELLIPSIS + suffixText,
2306
- truncated: true
2307
- };
2308
- }
2309
- function middleTruncate(graphemes, availableWidth, measure, fullWidth) {
2310
- const fullText = graphemes.join("");
2311
- const resolvedFullWidth = fullWidth ?? measure(fullText);
2312
- if (availableWidth <= 0 || graphemes.length === 0) {
2313
- return {
2314
- prefixText: fullText,
2315
- prefixGraphemeCount: graphemes.length,
2316
- suffixText: "",
2317
- suffixGraphemeCount: 0,
2318
- text: fullText,
2319
- truncated: false
2320
- };
2321
- }
2322
- if (resolvedFullWidth <= availableWidth) {
2323
- return {
2324
- prefixText: fullText,
2325
- prefixGraphemeCount: graphemes.length,
2326
- suffixText: "",
2327
- suffixGraphemeCount: 0,
2328
- text: fullText,
2329
- truncated: false
2330
- };
2331
- }
2332
- let lo = 0;
2333
- let hi = graphemes.length - 1;
2334
- let best = -1;
2335
- while (lo <= hi) {
2336
- const mid = lo + hi >>> 1;
2337
- const candidate = buildCandidate(graphemes, mid);
2338
- if (measure(candidate.text) <= availableWidth) {
2339
- best = mid;
2340
- lo = mid + 1;
2341
- } else {
2342
- hi = mid - 1;
2343
- }
2344
- }
2345
- if (best === -1) {
2346
- return {
2347
- prefixText: "",
2348
- prefixGraphemeCount: 0,
2349
- suffixText: "",
2350
- suffixGraphemeCount: 0,
2351
- text: "",
2352
- truncated: true
2353
- };
2354
- }
2355
- return buildCandidate(graphemes, best);
2356
- }
2357
- function getMiddleTruncateCopyText({ prefixText, selectionEnd, selectionStart, suffixText, value }) {
2358
- const visibleText = prefixText + ELLIPSIS + suffixText;
2359
- if (selectionStart < 0 || selectionEnd > visibleText.length || selectionStart >= selectionEnd) {
2360
- return null;
2361
- }
2362
- if (selectionStart === 0 && selectionEnd === visibleText.length) {
2363
- return value;
2364
- }
2365
- const ellipsisStart = prefixText.length;
2366
- const ellipsisEnd = ellipsisStart + ELLIPSIS.length;
2367
- if (selectionStart > ellipsisStart || selectionEnd < ellipsisEnd) {
2368
- return null;
2369
- }
2370
- const originalGraphemes = toGraphemes(value);
2371
- const selectedPrefixGraphemeCount = toGraphemes(visibleText.slice(0, selectionStart)).length;
2372
- const selectedSuffixGraphemeCount = toGraphemes(visibleText.slice(ellipsisEnd, selectionEnd)).length;
2373
- const suffixStart = originalGraphemes.length - toGraphemes(suffixText).length;
2374
- return originalGraphemes.slice(selectedPrefixGraphemeCount, suffixStart + selectedSuffixGraphemeCount).join("");
2375
- }
2376
- function getMiddleTruncateCopyTextFromSelectionText({ prefixText, selectionText, suffixText, value }) {
2377
- const visibleText = prefixText + ELLIPSIS + suffixText;
2378
- const trimmedSelectionText = selectionText.trim();
2379
- if (!trimmedSelectionText) {
2380
- return null;
2381
- }
2382
- const leading = selectionText.slice(0, selectionText.length - selectionText.trimStart().length);
2383
- const trailing = selectionText.slice(selectionText.trimEnd().length);
2384
- if (trimmedSelectionText === visibleText) {
2385
- return leading + value + trailing;
2386
- }
2387
- if (!trimmedSelectionText.includes(ELLIPSIS)) {
2784
+ }
2785
+ if (typeof run.workflowName === "string") {
2786
+ return run.workflowName;
2787
+ }
2788
+ return void 0;
2789
+ }, [displayData, run.workflowName]);
2790
+ const moduleSourceUrl = reactExports.useMemo(() => {
2791
+ if (!getModuleSourceUrl || !moduleSpecifier)
2792
+ return void 0;
2793
+ const parsed = parseStepName(moduleSpecifier) ?? parseWorkflowName(moduleSpecifier);
2794
+ if (!parsed)
2795
+ return void 0;
2796
+ const dataDeploymentId = displayData.deploymentId;
2797
+ return getModuleSourceUrl({
2798
+ moduleSpecifier: parsed.moduleSpecifier,
2799
+ deploymentId: typeof dataDeploymentId === "string" ? dataDeploymentId : run.deploymentId
2800
+ });
2801
+ }, [getModuleSourceUrl, moduleSpecifier, displayData, run.deploymentId]);
2802
+ if (!selectedSpan || !resource || !resourceId) {
2388
2803
  return null;
2389
2804
  }
2390
- const selectionStart = visibleText.indexOf(trimmedSelectionText);
2391
- if (selectionStart === -1) {
2392
- return null;
2805
+ const hasPendingActions = resource === "sleep" && canWakeUp || resource === "hook" && canResolveHook;
2806
+ return jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col", children: [jsxRuntimeExports.jsx(DecryptClickContext.Provider, { value: onDecrypt ? { onDecrypt, isDecrypting, hasEncryptedData } : void 0, children: jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto px-4 pb-8", children: [hasPendingActions && jsxRuntimeExports.jsxs("div", { className: "mb-4 rounded-lg border p-2", style: {
2807
+ borderColor: "var(--ds-gray-300)",
2808
+ backgroundColor: "var(--ds-gray-100)"
2809
+ }, children: [jsxRuntimeExports.jsx("p", { className: "mb-2 px-1 text-label-13 font-medium uppercase tracking-wide", style: { color: "var(--ds-gray-700)" }, children: "Actions" }), jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [resource === "sleep" && canWakeUp && jsxRuntimeExports.jsxs("button", { type: "button", onClick: handleWakeUp, disabled: stoppingSleep, className: clsx("flex items-center justify-center gap-2 rounded-md px-3 py-2 text-button-14", "disabled:opacity-50 disabled:cursor-not-allowed transition-colors", stoppingSleep ? "opacity-50 cursor-not-allowed" : "cursor-pointer"), style: {
2810
+ background: "var(--ds-amber-200)",
2811
+ color: "var(--ds-amber-900)"
2812
+ }, children: [jsxRuntimeExports.jsx(Zap, { className: "h-4 w-4" }), stoppingSleep ? "Waking up..." : "Wake Up Sleep"] }), resource === "hook" && canResolveHook && jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setShowResolveHookModal(true), disabled: resolvingHook, className: clsx("flex items-center justify-center gap-2 rounded-md px-3 py-2 text-button-14", "disabled:opacity-50 disabled:cursor-not-allowed transition-colors", resolvingHook ? "opacity-50 cursor-not-allowed" : "cursor-pointer"), style: {
2813
+ background: "var(--ds-gray-1000)",
2814
+ color: "var(--ds-background-100)"
2815
+ }, children: [jsxRuntimeExports.jsx(Send, { className: "h-4 w-4" }), "Resolve Hook"] })] })] }), jsxRuntimeExports.jsx(AttributePanel, { data: displayData, moduleSpecifier, moduleSourceUrl, expiredAt: run.expiredAt, isLoading: loading, error: error ?? void 0, onStreamClick, onRunClick, onDecrypt, isDecrypting, resource }), rawEvents && jsxRuntimeExports.jsx(EventsList, { events: rawEvents, onLoadEventData, onStreamClick, onRunClick, encryptionKey, showSeparateEventOccurrenceTimestamps })] }) }), jsxRuntimeExports.jsx(ResolveHookModal, { isOpen: showResolveHookModal, onClose: () => setShowResolveHookModal(false), onSubmit: handleResolveHook, isSubmitting: resolvingHook })] });
2816
+ }
2817
+ const PANEL_MIN_WIDTH = 280;
2818
+ const PANEL_HARD_MAX_WIDTH = 1300;
2819
+ const PANEL_DEFAULT_WIDTH = 360;
2820
+ const MAIN_MIN_WIDTH = 400;
2821
+ const PANEL_WIDTH_STORAGE_KEY = "workflow-trace-detail-panel-width";
2822
+ function clamp(value, min, max) {
2823
+ return Math.min(Math.max(value, min), max);
2824
+ }
2825
+ function computeMaxPanelWidth(availableWidth) {
2826
+ if (availableWidth <= 0)
2827
+ return PANEL_HARD_MAX_WIDTH;
2828
+ return Math.min(Math.max(availableWidth - MAIN_MIN_WIDTH, 0), PANEL_HARD_MAX_WIDTH);
2829
+ }
2830
+ function clampPanelWidth(width, availableWidth) {
2831
+ return clamp(width, PANEL_MIN_WIDTH, computeMaxPanelWidth(availableWidth));
2832
+ }
2833
+ function readStoredPanelWidth() {
2834
+ if (typeof window === "undefined")
2835
+ return PANEL_DEFAULT_WIDTH;
2836
+ try {
2837
+ const raw = window.localStorage.getItem(PANEL_WIDTH_STORAGE_KEY);
2838
+ const value = raw ? Number(raw) : Number.NaN;
2839
+ if (!Number.isFinite(value))
2840
+ return PANEL_DEFAULT_WIDTH;
2841
+ return clamp(value, PANEL_MIN_WIDTH, PANEL_HARD_MAX_WIDTH);
2842
+ } catch {
2843
+ return PANEL_DEFAULT_WIDTH;
2393
2844
  }
2394
- const selectionEnd = selectionStart + trimmedSelectionText.length;
2395
- const mappedText = getMiddleTruncateCopyText({
2396
- prefixText,
2397
- selectionEnd,
2398
- selectionStart,
2399
- suffixText,
2400
- value
2401
- });
2402
- return mappedText === null ? null : leading + mappedText + trailing;
2403
2845
  }
2404
- const useIsomorphicLayoutEffect = typeof window === "undefined" ? reactExports.useEffect : reactExports.useLayoutEffect;
2405
- function createFullState(value, graphemes) {
2406
- return {
2407
- displayText: value,
2408
- isTruncated: false,
2409
- prefixGraphemeCount: graphemes.length,
2410
- prefixText: value,
2411
- suffixGraphemeCount: 0,
2412
- suffixText: ""
2413
- };
2846
+ function writeStoredPanelWidth(width) {
2847
+ if (typeof window === "undefined")
2848
+ return;
2849
+ try {
2850
+ window.localStorage.setItem(PANEL_WIDTH_STORAGE_KEY, String(Math.round(width)));
2851
+ } catch {
2852
+ }
2414
2853
  }
2415
- function useMiddleTruncate(value) {
2416
- const graphemes = reactExports.useMemo(() => toGraphemes(value), [value]);
2417
- const fullState = reactExports.useMemo(() => createFullState(value, graphemes), [graphemes, value]);
2418
- const ref = reactExports.useRef(null);
2419
- const measureRef = reactExports.useRef(null);
2420
- const [state, setState] = reactExports.useState(() => fullState);
2421
- const rafRef = reactExports.useRef(0);
2422
- const updateState = reactExports.useCallback((nextState) => {
2423
- setState((currentState) => {
2424
- if (currentState.displayText === nextState.displayText && currentState.isTruncated === nextState.isTruncated && currentState.prefixText === nextState.prefixText && currentState.prefixGraphemeCount === nextState.prefixGraphemeCount && currentState.suffixText === nextState.suffixText && currentState.suffixGraphemeCount === nextState.suffixGraphemeCount) {
2425
- return currentState;
2426
- }
2427
- return nextState;
2428
- });
2854
+ function DraggableBorder({ element, position, onWidthChange, onReset, "aria-label": ariaLabel, "aria-controls": ariaControls, "aria-valuemin": ariaValueMin, "aria-valuemax": ariaValueMax, "aria-valuenow": ariaValueNow }) {
2855
+ const [isDragging, setIsDragging] = reactExports.useState(false);
2856
+ const pointerIdRef = reactExports.useRef(null);
2857
+ const startXRef = reactExports.useRef(0);
2858
+ const startWidthRef = reactExports.useRef(0);
2859
+ const pendingWidthRef = reactExports.useRef(0);
2860
+ const rafRef = reactExports.useRef(null);
2861
+ const movedRef = reactExports.useRef(false);
2862
+ const lastDragEndAtRef = reactExports.useRef(0);
2863
+ reactExports.useEffect(() => {
2864
+ return () => {
2865
+ if (rafRef.current)
2866
+ cancelAnimationFrame(rafRef.current);
2867
+ };
2429
2868
  }, []);
2430
- const recalculate = reactExports.useCallback(() => {
2431
- const el = ref.current;
2432
- const measureEl = measureRef.current;
2433
- if (!el || !measureEl)
2869
+ reactExports.useEffect(() => {
2870
+ if (!isDragging)
2434
2871
  return;
2435
- const available = el.clientWidth;
2436
- if (available <= 0) {
2437
- updateState(fullState);
2872
+ const previous = document.body.style.userSelect;
2873
+ document.body.style.userSelect = "none";
2874
+ return () => {
2875
+ document.body.style.userSelect = previous;
2876
+ };
2877
+ }, [isDragging]);
2878
+ const stopDrag = () => {
2879
+ pointerIdRef.current = null;
2880
+ setIsDragging(false);
2881
+ if (rafRef.current) {
2882
+ cancelAnimationFrame(rafRef.current);
2883
+ rafRef.current = null;
2884
+ if (movedRef.current)
2885
+ onWidthChange(pendingWidthRef.current);
2886
+ }
2887
+ if (movedRef.current) {
2888
+ movedRef.current = false;
2889
+ lastDragEndAtRef.current = performance.now();
2890
+ }
2891
+ };
2892
+ const handlePointerDown = (e) => {
2893
+ if (pointerIdRef.current !== null)
2894
+ return;
2895
+ const el = element.current;
2896
+ if (!el)
2897
+ return;
2898
+ if (e.pointerType === "mouse" && e.button !== 0)
2899
+ return;
2900
+ e.currentTarget.setPointerCapture(e.pointerId);
2901
+ pointerIdRef.current = e.pointerId;
2902
+ startXRef.current = e.clientX;
2903
+ startWidthRef.current = el.offsetWidth;
2904
+ movedRef.current = false;
2905
+ setIsDragging(true);
2906
+ };
2907
+ const handlePointerMove = (e) => {
2908
+ if (pointerIdRef.current !== e.pointerId)
2438
2909
  return;
2910
+ if (Math.abs(e.clientX - startXRef.current) > 4) {
2911
+ movedRef.current = true;
2439
2912
  }
2440
- const measure = (text) => {
2441
- measureEl.textContent = text;
2442
- return measureEl.scrollWidth;
2443
- };
2444
- const fullWidth = measure(value);
2445
- if (fullWidth <= available) {
2446
- updateState(fullState);
2913
+ const delta = position === "left" ? startXRef.current - e.clientX : e.clientX - startXRef.current;
2914
+ pendingWidthRef.current = startWidthRef.current + delta;
2915
+ if (!rafRef.current) {
2916
+ rafRef.current = requestAnimationFrame(() => {
2917
+ rafRef.current = null;
2918
+ onWidthChange(pendingWidthRef.current);
2919
+ });
2920
+ }
2921
+ };
2922
+ const handlePointerEnd = (e) => {
2923
+ if (pointerIdRef.current !== e.pointerId)
2924
+ return;
2925
+ stopDrag();
2926
+ };
2927
+ const handleLostPointerCapture = (e) => {
2928
+ if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current) {
2447
2929
  return;
2448
2930
  }
2449
- const result = middleTruncate(graphemes, available, measure, fullWidth);
2450
- updateState({
2451
- displayText: result.text,
2452
- isTruncated: result.truncated,
2453
- prefixGraphemeCount: result.prefixGraphemeCount,
2454
- prefixText: result.prefixText,
2455
- suffixGraphemeCount: result.suffixGraphemeCount,
2456
- suffixText: result.suffixText
2457
- });
2458
- }, [fullState, graphemes, updateState, value]);
2459
- useIsomorphicLayoutEffect(() => {
2460
- recalculate();
2461
- }, [recalculate]);
2462
- reactExports.useEffect(() => {
2463
- var _a;
2464
- const el = ref.current;
2465
- if (!el)
2931
+ stopDrag();
2932
+ };
2933
+ const handleDoubleClick = () => {
2934
+ if (movedRef.current || performance.now() - lastDragEndAtRef.current < 500) {
2466
2935
  return;
2467
- const debouncedRecalc = () => {
2468
- cancelAnimationFrame(rafRef.current);
2469
- rafRef.current = requestAnimationFrame(recalculate);
2470
- };
2471
- const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(debouncedRecalc) : null;
2472
- ro == null ? void 0 : ro.observe(el);
2473
- window.addEventListener("resize", debouncedRecalc);
2474
- const onFontsLoaded = () => {
2475
- debouncedRecalc();
2476
- };
2477
- const fontSet = "fonts" in document ? document.fonts : null;
2478
- (_a = fontSet == null ? void 0 : fontSet.addEventListener) == null ? void 0 : _a.call(fontSet, "loadingdone", onFontsLoaded);
2479
- return () => {
2480
- var _a2;
2481
- ro == null ? void 0 : ro.disconnect();
2482
- window.removeEventListener("resize", debouncedRecalc);
2483
- cancelAnimationFrame(rafRef.current);
2484
- (_a2 = fontSet == null ? void 0 : fontSet.removeEventListener) == null ? void 0 : _a2.call(fontSet, "loadingdone", onFontsLoaded);
2485
- };
2486
- }, [recalculate]);
2487
- return {
2488
- ref,
2489
- measureRef,
2490
- displayText: state.displayText,
2491
- isTruncated: state.isTruncated,
2492
- prefixGraphemeCount: state.prefixGraphemeCount,
2493
- prefixText: state.prefixText,
2494
- suffixGraphemeCount: state.suffixGraphemeCount,
2495
- suffixText: state.suffixText
2936
+ }
2937
+ onReset == null ? void 0 : onReset();
2496
2938
  };
2497
- }
2498
- function getRangeOffsets(container, range) {
2499
- if (!container.contains(range.startContainer) || !container.contains(range.endContainer)) {
2500
- return null;
2501
- }
2502
- const startRange = document.createRange();
2503
- startRange.selectNodeContents(container);
2504
- startRange.setEnd(range.startContainer, range.startOffset);
2505
- const endRange = document.createRange();
2506
- endRange.selectNodeContents(container);
2507
- endRange.setEnd(range.endContainer, range.endOffset);
2508
- return {
2509
- end: endRange.toString().length,
2510
- start: startRange.toString().length
2939
+ const handleKeyDown = (e) => {
2940
+ const el = element.current;
2941
+ if (!el)
2942
+ return;
2943
+ const step = e.shiftKey ? 64 : 16;
2944
+ const grow = position === "left" ? 1 : -1;
2945
+ let next = null;
2946
+ if (e.key === "ArrowLeft") {
2947
+ next = el.offsetWidth + grow * step;
2948
+ } else if (e.key === "ArrowRight") {
2949
+ next = el.offsetWidth - grow * step;
2950
+ } else if (e.key === "Home" && ariaValueMin !== void 0) {
2951
+ next = ariaValueMin;
2952
+ } else if (e.key === "End" && ariaValueMax !== void 0) {
2953
+ next = ariaValueMax;
2954
+ }
2955
+ if (next === null)
2956
+ return;
2957
+ e.preventDefault();
2958
+ onWidthChange(next);
2511
2959
  };
2960
+ return jsxRuntimeExports.jsx("div", { role: "separator", "aria-orientation": "vertical", "aria-label": ariaLabel, "aria-controls": ariaControls, "aria-valuemin": ariaValueMin, "aria-valuemax": ariaValueMax, "aria-valuenow": ariaValueNow, tabIndex: 0, className: cn("group absolute inset-y-0 z-10 w-[17px] cursor-col-resize touch-none outline-none", position === "left" ? "-left-2" : "-right-2"), onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerEnd, onPointerCancel: handlePointerEnd, onLostPointerCapture: handleLostPointerCapture, onDoubleClick: handleDoubleClick, onKeyDown: handleKeyDown, children: jsxRuntimeExports.jsx("span", { "aria-hidden": true, className: cn("pointer-events-none absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-transparent transition-colors delay-75 duration-100", "group-hover:bg-gray-500 group-focus-visible:bg-[var(--ds-focus-color)]", isDragging && "bg-gray-500") }) });
2512
2961
  }
2513
- function MiddleTruncate({ value, className, onCopy: onCopyProp, ...props }) {
2514
- const { ref, measureRef, displayText, isTruncated, prefixText, suffixText } = useMiddleTruncate(value);
2515
- const visibleRef = reactExports.useRef(null);
2516
- const handleCopy = reactExports.useCallback((e) => {
2517
- onCopyProp == null ? void 0 : onCopyProp(e);
2518
- if (e.defaultPrevented || !isTruncated)
2962
+ function useElementWidth(ref, enabled = true) {
2963
+ const [width, setWidth] = reactExports.useState(0);
2964
+ reactExports.useLayoutEffect(() => {
2965
+ if (!enabled)
2519
2966
  return;
2520
- const selection = window.getSelection();
2521
- if (!selection || selection.rangeCount === 0)
2967
+ const el = ref.current;
2968
+ if (!el)
2522
2969
  return;
2523
- const selectionText = selection.toString();
2524
- if (!selectionText)
2970
+ setWidth(Math.round(el.getBoundingClientRect().width));
2971
+ const observer = new ResizeObserver(([entry]) => {
2972
+ setWidth(Math.round((entry == null ? void 0 : entry.contentRect.width) ?? 0));
2973
+ });
2974
+ observer.observe(el);
2975
+ return () => observer.disconnect();
2976
+ }, [ref, enabled]);
2977
+ return width;
2978
+ }
2979
+ const DETAIL_PANEL_ID = "trace-detail-panel";
2980
+ function useSelectedSpanInfo() {
2981
+ const { activeSpan } = useActiveSpan();
2982
+ const sidebar = useSidebarData();
2983
+ return reactExports.useMemo(() => {
2984
+ var _a, _b;
2985
+ if (!activeSpan)
2986
+ return null;
2987
+ const resource = (_a = activeSpan.attributes) == null ? void 0 : _a.resource;
2988
+ const rawEvents = filterSpanRawEvents(sidebar.events, resource, activeSpan.spanId);
2989
+ return {
2990
+ data: (_b = activeSpan.attributes) == null ? void 0 : _b.data,
2991
+ resource,
2992
+ spanId: activeSpan.spanId,
2993
+ rawEvents
2994
+ };
2995
+ }, [activeSpan, sidebar]);
2996
+ }
2997
+ function TraceDetailPanel({ containerRef, onNavigateToSpan, onClose }) {
2998
+ const { activeSpan, activeSpanId, spans } = useActiveSpan();
2999
+ const sidebar = useSidebarData();
3000
+ const selectedSpan = useSelectedSpanInfo();
3001
+ const isOpen = Boolean(activeSpan);
3002
+ const [storedWidth, setStoredWidth] = reactExports.useState(() => readStoredPanelWidth());
3003
+ const containerWidth = useElementWidth(containerRef, isOpen);
3004
+ const asideRef = reactExports.useRef(null);
3005
+ const handleResize = reactExports.useCallback((next) => {
3006
+ const clamped = clampPanelWidth(next, containerWidth);
3007
+ setStoredWidth(clamped);
3008
+ writeStoredPanelWidth(clamped);
3009
+ }, [containerWidth]);
3010
+ const panelWidth = clampPanelWidth(storedWidth, containerWidth);
3011
+ const panelMaxWidth = Math.max(PANEL_MIN_WIDTH, computeMaxPanelWidth(containerWidth));
3012
+ const { prevSpanId, nextSpanId } = reactExports.useMemo(() => {
3013
+ var _a, _b;
3014
+ if (!activeSpanId)
3015
+ return { prevSpanId: null, nextSpanId: null };
3016
+ const i = spans.findIndex((s) => s.spanId === activeSpanId);
3017
+ if (i === -1)
3018
+ return { prevSpanId: null, nextSpanId: null };
3019
+ return {
3020
+ prevSpanId: ((_a = spans[i - 1]) == null ? void 0 : _a.spanId) ?? null,
3021
+ nextSpanId: ((_b = spans[i + 1]) == null ? void 0 : _b.spanId) ?? null
3022
+ };
3023
+ }, [activeSpanId, spans]);
3024
+ const handleSelectPrev = reactExports.useCallback(() => {
3025
+ if (prevSpanId)
3026
+ onNavigateToSpan(prevSpanId);
3027
+ }, [prevSpanId, onNavigateToSpan]);
3028
+ const handleSelectNext = reactExports.useCallback(() => {
3029
+ if (nextSpanId)
3030
+ onNavigateToSpan(nextSpanId);
3031
+ }, [nextSpanId, onNavigateToSpan]);
3032
+ reactExports.useEffect(() => {
3033
+ if (!isOpen)
2525
3034
  return;
2526
- const range = selection.getRangeAt(0);
2527
- const visibleEl = visibleRef.current;
2528
- let copyText = null;
2529
- if (e.currentTarget.contains(range.startContainer) && e.currentTarget.contains(range.endContainer) && visibleEl) {
2530
- const offsets = getRangeOffsets(visibleEl, range);
2531
- if (offsets) {
2532
- copyText = getMiddleTruncateCopyText({
2533
- prefixText,
2534
- selectionEnd: offsets.end,
2535
- selectionStart: offsets.start,
2536
- suffixText,
2537
- value
2538
- });
3035
+ const onKeyDown = (e) => {
3036
+ if (e.key !== "j" && e.key !== "k" && e.key !== "ArrowDown" && e.key !== "ArrowUp") {
3037
+ return;
3038
+ }
3039
+ const target = e.target;
3040
+ if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || (target == null ? void 0 : target.isContentEditable)) {
3041
+ return;
2539
3042
  }
3043
+ const targetId = e.key === "k" || e.key === "ArrowUp" ? prevSpanId : nextSpanId;
3044
+ if (targetId) {
3045
+ e.preventDefault();
3046
+ onNavigateToSpan(targetId);
3047
+ }
3048
+ };
3049
+ window.addEventListener("keydown", onKeyDown);
3050
+ return () => window.removeEventListener("keydown", onKeyDown);
3051
+ }, [isOpen, prevSpanId, nextSpanId, onNavigateToSpan]);
3052
+ const selectedSpanName = reactExports.useMemo(() => {
3053
+ var _a, _b;
3054
+ if (!(selectedSpan == null ? void 0 : selectedSpan.data))
3055
+ return "Details";
3056
+ const data = selectedSpan.data;
3057
+ if (selectedSpan.resource === "hook") {
3058
+ return data.token ?? data.hookId;
2540
3059
  }
2541
- copyText ?? (copyText = getMiddleTruncateCopyTextFromSelectionText({
2542
- prefixText,
2543
- selectionText,
2544
- suffixText,
2545
- value
2546
- }));
2547
- if (copyText === null)
2548
- return;
2549
- e.preventDefault();
2550
- e.clipboardData.setData("text/plain", copyText);
2551
- }, [onCopyProp, isTruncated, prefixText, suffixText, value]);
2552
- return jsxRuntimeExports.jsxs("span", { title: isTruncated ? value : void 0, ...props, ref, className: cn("relative inline-grid min-w-0 max-w-full overflow-hidden whitespace-nowrap", className), onCopy: handleCopy, children: [isTruncated && jsxRuntimeExports.jsx("span", { className: "sr-only select-none", children: value }), jsxRuntimeExports.jsx("span", { "aria-hidden": "true", className: "pointer-events-none col-start-1 row-start-1 invisible select-none whitespace-nowrap", children: value }), jsxRuntimeExports.jsx("span", { "aria-hidden": isTruncated || void 0, className: "col-start-1 row-start-1 min-w-0 overflow-hidden", ref: visibleRef, children: isTruncated ? jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("span", { children: prefixText }), jsxRuntimeExports.jsx("span", { children: ELLIPSIS }), jsxRuntimeExports.jsx("span", { children: suffixText })] }) : displayText }), jsxRuntimeExports.jsx("span", { "aria-hidden": "true", className: "pointer-events-none absolute left-0 top-0 inline-block invisible select-none whitespace-nowrap", ref: measureRef })] });
3060
+ const stepName = data.stepName;
3061
+ const workflowName = data.workflowName;
3062
+ return (stepName ? (_a = parseStepName(stepName)) == null ? void 0 : _a.shortName : void 0) ?? (workflowName ? (_b = parseWorkflowName(workflowName)) == null ? void 0 : _b.shortName : void 0) ?? stepName ?? workflowName ?? data.hookId ?? "Details";
3063
+ }, [selectedSpan == null ? void 0 : selectedSpan.data, selectedSpan == null ? void 0 : selectedSpan.resource]);
3064
+ if (!activeSpan)
3065
+ return null;
3066
+ return jsxRuntimeExports.jsxs("aside", { ref: asideRef, id: DETAIL_PANEL_ID, className: "relative flex flex-col h-full max-h-full shrink-0 bg-background-100 border-l border-gray-alpha-400", style: { width: panelWidth }, children: [jsxRuntimeExports.jsx(DraggableBorder, { element: asideRef, position: "left", onWidthChange: handleResize, onReset: () => handleResize(PANEL_DEFAULT_WIDTH), "aria-label": "Resize span details panel", "aria-controls": DETAIL_PANEL_ID, "aria-valuemin": PANEL_MIN_WIDTH, "aria-valuemax": panelMaxWidth, "aria-valuenow": Math.min(Math.max(Math.round(panelWidth), PANEL_MIN_WIDTH), panelMaxWidth) }), jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2 shrink-0 px-4 py-[7.5px]", children: [jsxRuntimeExports.jsx("span", { className: "text-label-14 font-medium text-gray-1000 truncate block", children: selectedSpanName }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-0.5 shrink-0", children: [jsxRuntimeExports.jsxs(Tooltip, { children: [jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: jsxRuntimeExports.jsx(IconButton, { "aria-label": "Navigate up", "aria-keyshortcuts": "K", onClick: handleSelectPrev, disabled: !prevSpanId, children: jsxRuntimeExports.jsx(ChevronUp, { className: "w-4 h-4" }) }) }), prevSpanId ? jsxRuntimeExports.jsxs(TooltipContent, { children: ["Navigate up", jsxRuntimeExports.jsx(Kbd, { children: "K" })] }) : null] }), jsxRuntimeExports.jsxs(Tooltip, { children: [jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: jsxRuntimeExports.jsx(IconButton, { "aria-label": "Navigate down", "aria-keyshortcuts": "J", onClick: handleSelectNext, disabled: !nextSpanId, children: jsxRuntimeExports.jsx(ChevronDown, { className: "w-4 h-4" }) }) }), nextSpanId ? jsxRuntimeExports.jsxs(TooltipContent, { children: ["Navigate down", jsxRuntimeExports.jsx(Kbd, { children: "J" })] }) : null] }), jsxRuntimeExports.jsx("div", { "aria-hidden": true, className: "w-px h-4 bg-gray-alpha-400 mx-1" }), jsxRuntimeExports.jsx(IconButton, { "aria-label": "Close span details", "aria-keyshortcuts": "Escape", onClick: onClose, children: jsxRuntimeExports.jsx(X, { className: "w-4 h-4" }) })] })] }), jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto", children: jsxRuntimeExports.jsx(ErrorBoundary, { children: jsxRuntimeExports.jsx(EntityDetailPanel, { run: sidebar.run, onStreamClick: sidebar.onStreamClick, onRunClick: sidebar.onRunClick, fetchSpanDetail: sidebar.fetchSpanDetail, onWakeUpSleep: sidebar.onWakeUpSleep, onLoadEventData: sidebar.onLoadEventData, onResolveHook: sidebar.onResolveHook, encryptionKey: sidebar.encryptionKey, onDecrypt: sidebar.onDecrypt, isDecrypting: sidebar.isDecrypting, selectedSpan, showSeparateEventOccurrenceTimestamps: sidebar.showSeparateEventOccurrenceTimestamps, getModuleSourceUrl: sidebar.getModuleSourceUrl }) }) })] });
2553
3067
  }
3068
+ const WorkflowIcon = () => {
3069
+ return jsxRuntimeExports.jsx("svg", { "data-testid": "geist-icon", height: "16", strokeLinejoin: "round", style: { color: "currentColor" }, viewBox: "0 0 16 16", width: "16", children: jsxRuntimeExports.jsx("path", { d: "M5.75 7L6.4209 7.33594L4.85254 10.4717C5.38789 10.771 5.75 11.3431 5.75 12V14C5.75 14.9665 4.96648 15.75 4 15.75H2C1.03352 15.75 0.25 14.9665 0.25 14V12C0.25 11.0335 1.03352 10.25 2 10.25H3.28711L5.0791 6.66504L5.75 7ZM14 10.25C14.9665 10.25 15.75 11.0335 15.75 12V14C15.75 14.9665 14.9665 15.75 14 15.75H12C11.1185 15.75 10.3909 15.098 10.2695 14.25H7V12.75H10.25V12C10.25 11.0335 11.0335 10.25 12 10.25H14ZM2 11.75C1.86192 11.75 1.75 11.8619 1.75 12V14C1.75 14.1381 1.86192 14.25 2 14.25H4C4.13808 14.25 4.25 14.1381 4.25 14V12C4.25 11.8619 4.13808 11.75 4 11.75H2ZM12 11.75C11.8619 11.75 11.75 11.8619 11.75 12V14C11.75 14.1381 11.8619 14.25 12 14.25H14C14.1381 14.25 14.25 14.1381 14.25 14V12C14.25 11.8619 14.1381 11.75 14 11.75H12ZM9 0.25C9.96649 0.25 10.75 1.03351 10.75 2V4C10.75 4.44642 10.5808 4.85197 10.3057 5.16113L12.2041 8.6416L10.8877 9.36035L8.91895 5.75H7C6.03351 5.75 5.25 4.96649 5.25 4V2C5.25 1.03351 6.03351 0.25 7 0.25H9ZM7 1.75C6.86193 1.75 6.75 1.86193 6.75 2V4C6.75 4.13807 6.86193 4.25 7 4.25H9C9.13807 4.25 9.25 4.13807 9.25 4V2C9.25 1.86193 9.13807 1.75 9 1.75H7Z", fill: "currentColor" }) });
3070
+ };
3071
+ const WebhookIcon = () => {
3072
+ return jsxRuntimeExports.jsx("svg", { "data-testid": "geist-icon", height: "16", strokeLinejoin: "round", viewBox: "0 0 16 16", width: "16", style: { color: "currentColor" }, children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M5.8349 3.24994C5.14454 4.44567 5.55423 5.97464 6.74996 6.665L7.39465 7.03721L7.02726 7.68466L5.14599 11C5.21289 11.1531 5.25 11.3222 5.25 11.5C5.25 12.1904 4.69036 12.75 4 12.75C3.30964 12.75 2.75 12.1904 2.75 11.5C2.75 10.8634 3.22584 10.338 3.84123 10.26L5.38944 7.5316C3.95909 6.30274 3.55857 4.19264 4.53586 2.49994C5.64043 0.586765 8.08679 -0.0687357 9.99996 1.03583C11.836 2.09584 12.5137 4.39156 11.5892 6.26591L10.4949 4.33754C10.4418 3.53274 10.0009 2.76843 9.24996 2.33487C8.05423 1.64452 6.52525 2.0542 5.8349 3.24994ZM9.146 4.99996C9.21289 4.84686 9.25 4.67776 9.25 4.5C9.25 3.80964 8.69036 3.25 8 3.25C7.30964 3.25 6.75 3.80964 6.75 4.5C6.75 5.13659 7.22586 5.66203 7.84127 5.74002L9.72266 9.05556L10.0956 9.71274L10.75 9.33493C11.9457 8.64458 13.4747 9.05427 14.165 10.25C14.8554 11.4457 14.4457 12.9747 13.25 13.6651C12.5047 14.0953 11.63 14.0983 10.9096 13.75H8.69252C9.85895 15.4685 12.1727 16.0191 14 14.9641C15.9131 13.8595 16.5686 11.4132 15.4641 9.5C14.4886 7.81038 12.4665 7.10169 10.6895 7.71998L9.146 4.99996ZM13.25 11.5C13.25 12.1904 12.6904 12.75 12 12.75C11.591 12.75 11.228 12.5536 10.9999 12.25H7.9298C7.57875 14.1006 5.95279 15.5 4 15.5C1.79086 15.5 0 13.7091 0 11.5C0 9.40018 1.61801 7.67826 3.67529 7.51299L2.58098 9.44147C1.92792 9.89251 1.5 10.6463 1.5 11.5C1.5 12.8807 2.61929 14 4 14C5.38071 14 6.5 12.8807 6.5 11.5V10.75H7.25H10.9999C11.228 10.4464 11.591 10.25 12 10.25C12.6904 10.25 13.25 10.8096 13.25 11.5Z", fill: "currentColor" }) });
3073
+ };
3074
+ const SleepIcon = () => {
3075
+ return jsxRuntimeExports.jsx("svg", { "data-testid": "geist-icon", height: "16", strokeLinejoin: "round", viewBox: "0 0 16 16", width: "16", style: { color: "currentColor" }, children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M7.25 1.25V2.03971C5.87928 2.18571 4.62678 2.72736 3.6089 3.54824L3.03033 2.96967L2.5 2.43934L1.43934 3.5L1.96967 4.03033L2.54824 4.6089C1.57979 5.80976 1 7.33717 1 9C1 12.866 4.13401 16 8 16C11.866 16 15 12.866 15 9C15 7.33717 14.4202 5.80976 13.4518 4.6089L14.0303 4.03033L14.5607 3.5L13.5 2.43934L12.9697 2.96967L12.3911 3.54824C11.3732 2.72736 10.1207 2.18571 8.75 2.03971V1.25H9.25H10V-0.25H9.25H8.75H7.25H6.75H6V1.25H6.75H7.25ZM2.5 9C2.5 5.96243 4.96243 3.5 8 3.5C11.0376 3.5 13.5 5.96243 13.5 9C13.5 12.0376 11.0376 14.5 8 14.5C4.96243 14.5 2.5 12.0376 2.5 9ZM8.75 6.75V6H7.25V6.75V9V9.75H8.75V9V6.75Z", fill: "currentColor" }) });
3076
+ };
3077
+ const StepForwardIcon = () => {
3078
+ return jsxRuntimeExports.jsx("svg", { "data-testid": "geist-icon", height: "16", strokeLinejoin: "round", viewBox: "0 0 16 16", width: "16", style: { color: "currentColor" }, children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4.21969 12.5303L4.75002 13.0607L5.81068 12L5.28035 11.4697L1.81068 7.99999L5.28035 4.53032L5.81068 3.99999L4.75002 2.93933L4.21969 3.46966L0.39647 7.29289C0.00594562 7.68341 0.00594562 8.31658 0.39647 8.7071L4.21969 12.5303ZM11.7804 12.5303L11.25 13.0607L10.1894 12L10.7197 11.4697L14.1894 7.99999L10.7197 4.53032L10.1894 3.99999L11.25 2.93933L11.7804 3.46966L15.6036 7.29289C15.9941 7.68341 15.9941 8.31658 15.6036 8.7071L11.7804 12.5303Z", fill: "currentColor" }) });
3079
+ };
2554
3080
  const eventStyles = {
2555
- run: { icon: WorkflowIcon, className: "text-blue-900" },
2556
- step: { icon: StepForwardIcon, className: "text-green-900" },
2557
- hook: { icon: WebhookIcon, className: "text-yellow-900" },
2558
- sleep: { icon: SleepIcon, className: "text-gray-900" }
3081
+ run: { icon: WorkflowIcon, className: "text-blue-900", label: "Workflow" },
3082
+ step: { icon: StepForwardIcon, className: "text-green-900", label: "Step" },
3083
+ hook: { icon: WebhookIcon, className: "text-gray-900", label: "Hook" },
3084
+ sleep: { icon: SleepIcon, className: "text-gray-900", label: "Sleep" }
2559
3085
  };
2560
3086
  const defaultStyle = {
2561
3087
  icon: Circle,
2562
- className: "text-gray-900"
3088
+ className: "text-gray-900",
3089
+ label: "Event"
2563
3090
  };
2564
3091
  const ROW_HEIGHT_CLASS = "h-10";
2565
3092
  function getEventStyle(resource, isErrored) {
2566
3093
  const style = eventStyles[resource] ?? defaultStyle;
2567
3094
  return {
2568
3095
  icon: style.icon,
2569
- className: cn(isErrored ? "text-red-900" : style.className)
3096
+ className: cn(isErrored ? "text-red-900" : style.className),
3097
+ label: style.label
2570
3098
  };
2571
3099
  }
2572
- const EventRow = ({ span, isSelected, onSelectSpan }) => {
3100
+ const EventRow = ({ span, isSelected, isDimmed, onSelectSpan }) => {
2573
3101
  const durationMs = getSpanDurationMs(span);
2574
- const isErrored = span.attributes.data.status === "failed";
2575
- const { icon: Icon, className: tagClassName } = getEventStyle(span.resource, isErrored);
2576
- return jsxRuntimeExports.jsx("li", { className: cn("relative overflow-clip group after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-gray-alpha-400", ROW_HEIGHT_CLASS), role: "treeitem", "aria-selected": isSelected, "aria-expanded": isSelected, "aria-level": 1, onClick: () => onSelectSpan(span.spanId), children: jsxRuntimeExports.jsx("div", { className: "h-full hover:bg-gray-100 group-aria-selected:bg-gray-100 group-aria-selected:hover:bg-gray-200", children: jsxRuntimeExports.jsxs("div", { className: "flex h-full min-w-0 items-center px-2", children: [jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [jsxRuntimeExports.jsx("span", { className: cn("shrink-0", tagClassName), children: jsxRuntimeExports.jsx(Icon, { className: "w-4 h-4" }) }), jsxRuntimeExports.jsx("span", { className: "min-w-0 text-label-14", children: jsxRuntimeExports.jsx(MiddleTruncate, { value: span.name }) })] }), jsxRuntimeExports.jsx("div", { className: "ml-2 shrink-0", children: jsxRuntimeExports.jsx("span", { className: "text-label-14 text-gray-900 tabular-nums", children: formatDuration(durationMs) }) })] }) }) });
3102
+ const isErrored = isSpanErrored(span);
3103
+ const { icon: Icon, className: tagClassName, label: iconLabel } = getEventStyle(span.resource, isErrored);
3104
+ return jsxRuntimeExports.jsx("li", { className: cn("relative overflow-clip group transition-opacity", ROW_HEIGHT_CLASS, isDimmed && "opacity-35"), role: "treeitem", "aria-selected": isSelected, "aria-expanded": isSelected, "aria-level": 1, onClick: () => onSelectSpan(span.spanId), children: jsxRuntimeExports.jsx("div", { className: "h-full hover:bg-gray-100 group-aria-selected:bg-gray-100 group-aria-selected:hover:bg-gray-200", children: jsxRuntimeExports.jsxs("div", { className: "flex h-full min-w-0 items-center pl-4 pr-2", children: [jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [jsxRuntimeExports.jsxs(Tooltip, { delayDuration: 500, children: [jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: jsxRuntimeExports.jsx("span", { className: cn("shrink-0", tagClassName), children: jsxRuntimeExports.jsx(Icon, { className: "w-4 h-4" }) }) }), jsxRuntimeExports.jsx(TooltipContent, { side: "top", children: iconLabel })] }), jsxRuntimeExports.jsx("span", { className: "min-w-0 text-label-14", children: jsxRuntimeExports.jsx(MiddleTruncate, { value: span.name }) })] }), jsxRuntimeExports.jsx("div", { className: "ml-2 shrink-0", children: jsxRuntimeExports.jsx("span", { className: "text-label-14 text-gray-900 tabular-nums", children: formatDurationPrecise(durationMs) }) })] }) }) });
2577
3105
  };
2578
- const EventList = ({ spans, activeSpanId, onSelectSpan }) => {
2579
- return jsxRuntimeExports.jsx("ul", { id: "event-list", role: "tree", className: "block min-h-0 overflow-visible", children: spans.map((span) => {
2580
- return jsxRuntimeExports.jsx(EventRow, { span, isSelected: span.spanId === activeSpanId, onSelectSpan }, span.spanId);
2581
- }) });
3106
+ const EventList = ({ spans, activeSpanId, searchResult, onSelectSpan }) => {
3107
+ const listRef = reactExports.useRef(null);
3108
+ const { start, end } = useRowWindow(listRef, spans.length, ROW_HEIGHT_PX);
3109
+ return jsxRuntimeExports.jsx("ul", { ref: listRef, id: "event-list", role: "tree", className: "block min-h-0 overflow-visible divide-y divide-gray-400 border-b border-gray-400", style: {
3110
+ paddingTop: start * ROW_HEIGHT_PX,
3111
+ paddingBottom: (spans.length - end) * ROW_HEIGHT_PX
3112
+ }, children: spans.slice(start, end).map((span) => jsxRuntimeExports.jsx(EventRow, { span, isSelected: span.spanId === activeSpanId, isDimmed: isSpanDimmedBySearch(span.spanId, searchResult), onSelectSpan }, span.spanId)) });
2582
3113
  };
2583
- const GUTTER_PX = 1;
2584
- const MIN_PX = 50;
2585
- const DEFAULT_START_PX = 340;
2586
- function SplitPane({ children, className, defaultStartWidth = DEFAULT_START_PX, startHeader, endHeader }) {
3114
+ const SPLIT_PANE_START_ID = "trace-split-start";
3115
+ function SplitPane({ children, className, startHeader, endHeader, scrollContainerRef }) {
2587
3116
  const parts = reactExports.Children.toArray(children);
2588
- if (parts.length !== 2) {
2589
- throw new Error("SplitPane expects exactly two children");
3117
+ if (parts.length !== 3) {
3118
+ throw new Error("SplitPane expects exactly three children");
2590
3119
  }
2591
- const [start, end] = parts;
2592
- const [startPx, setStartPx] = reactExports.useState(defaultStartWidth);
2593
- const [isDragging, setIsDragging] = reactExports.useState(false);
3120
+ const [start, end, endOverlay] = parts;
3121
+ const [startPx, setStartPx] = reactExports.useState(DEFAULT_START_PX);
2594
3122
  const containerRef = reactExports.useRef(null);
2595
- const gutterRef = reactExports.useRef(null);
2596
- const rafRef = reactExports.useRef(null);
2597
- const pendingPx = reactExports.useRef(defaultStartWidth);
2598
- const pointerIdRef = reactExports.useRef(null);
2599
- reactExports.useEffect(() => {
2600
- return () => {
2601
- if (rafRef.current)
2602
- cancelAnimationFrame(rafRef.current);
2603
- };
2604
- }, []);
3123
+ const startRef = reactExports.useRef(null);
3124
+ const containerWidth = useElementWidth(containerRef);
3125
+ const setContainerRef = reactExports.useCallback((node) => {
3126
+ containerRef.current = node;
3127
+ if (scrollContainerRef) {
3128
+ scrollContainerRef.current = node;
3129
+ }
3130
+ }, [scrollContainerRef]);
2605
3131
  const clampPx = reactExports.useCallback((px) => {
2606
3132
  const el = containerRef.current;
2607
3133
  if (!el)
@@ -2609,80 +3135,72 @@ function SplitPane({ children, className, defaultStartWidth = DEFAULT_START_PX,
2609
3135
  const maxPx = el.getBoundingClientRect().width - MIN_PX - GUTTER_PX;
2610
3136
  return Math.min(maxPx, Math.max(MIN_PX, px));
2611
3137
  }, []);
3138
+ const handleWidthChange = reactExports.useCallback((next) => setStartPx(clampPx(next)), [clampPx]);
3139
+ const handleReset = reactExports.useCallback(() => setStartPx(clampPx(DEFAULT_START_PX)), [clampPx]);
3140
+ const effectiveStartPx = containerWidth > 0 ? Math.max(MIN_PX, Math.min(containerWidth - MIN_PX - GUTTER_PX, startPx)) : startPx;
3141
+ const colTemplate = paneColTemplate(effectiveStartPx);
3142
+ const maxStartPx = Math.max(MIN_PX, containerWidth > 0 ? containerWidth - MIN_PX - GUTTER_PX : startPx);
3143
+ return jsxRuntimeExports.jsxs("div", { className: cn("relative flex flex-col h-full min-h-0", className), children: [jsxRuntimeExports.jsxs("div", { className: "shrink-0 grid", style: { gridTemplateColumns: colTemplate }, children: [jsxRuntimeExports.jsx("div", { children: startHeader }), jsxRuntimeExports.jsx("div", { className: "flex justify-center", children: jsxRuntimeExports.jsx("span", { className: "h-full w-px bg-gray-alpha-400", "aria-hidden": true }) }), jsxRuntimeExports.jsx("div", { children: endHeader })] }), jsxRuntimeExports.jsxs("div", { ref: setContainerRef, className: "grid flex-1 min-h-0 overflow-x-hidden overflow-y-auto", style: { gridTemplateColumns: colTemplate }, children: [jsxRuntimeExports.jsx("div", { ref: startRef, id: SPLIT_PANE_START_ID, className: "min-w-0", children: start }), jsxRuntimeExports.jsxs("div", { className: "relative z-20 isolate flex shrink-0 justify-center", children: [jsxRuntimeExports.jsx("span", { className: "pointer-events-none relative z-10 h-full w-px shrink-0 bg-gray-alpha-400", "aria-hidden": true }), jsxRuntimeExports.jsx(DraggableBorder, { element: startRef, position: "right", onWidthChange: handleWidthChange, onReset: handleReset, "aria-label": "Resize event list", "aria-controls": SPLIT_PANE_START_ID, "aria-valuemin": MIN_PX, "aria-valuemax": maxStartPx, "aria-valuenow": Math.min(Math.max(Math.round(effectiveStartPx), MIN_PX), maxStartPx) })] }), end] }), jsxRuntimeExports.jsxs("div", { className: "pointer-events-none absolute inset-x-0 bottom-3 z-[5] grid", style: { gridTemplateColumns: colTemplate }, children: [jsxRuntimeExports.jsx("div", {}), jsxRuntimeExports.jsx("div", {}), jsxRuntimeExports.jsx("div", { className: "flex min-w-0 items-center justify-between gap-3 pl-4 pr-3", children: endOverlay })] })] });
3144
+ }
3145
+ const hint = "_hint_1dmsq_3";
3146
+ const styles = {
3147
+ hint
3148
+ };
3149
+ const DISMISSALS_KEY = "workflow-step-shortcut-helper-dismissals";
3150
+ const DISMISSAL_LIMIT = 3;
3151
+ const HINT_ROTATION_MS = 8e3;
3152
+ function getAltKeyLabel() {
3153
+ if (typeof navigator === "undefined")
3154
+ return "Alt";
3155
+ return navigator.platform.toLowerCase().includes("mac") ? "Option" : "Alt";
3156
+ }
3157
+ function readDismissals() {
3158
+ try {
3159
+ const dismissals = Number.parseInt(window.localStorage.getItem(DISMISSALS_KEY) ?? "0", 10);
3160
+ return Number.isNaN(dismissals) ? 0 : dismissals;
3161
+ } catch {
3162
+ return 0;
3163
+ }
3164
+ }
3165
+ function AltHint() {
3166
+ return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: ["Hold", jsxRuntimeExports.jsx(Kbd, { variant: "outline", size: "compact", className: "mx-1", children: getAltKeyLabel() }), "to see delta between spans"] });
3167
+ }
3168
+ function NavHint() {
3169
+ return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: ["Use", jsxRuntimeExports.jsx(Kbd, { variant: "outline", size: "compact", className: "ml-1", children: "J" }), jsxRuntimeExports.jsx("span", { className: "mx-1", children: "/" }), jsxRuntimeExports.jsx(Kbd, { variant: "outline", size: "compact", className: "mr-1", children: "K" }), "to move between spans"] });
3170
+ }
3171
+ function TraceShortcutHelper({ hasMultipleSpans, reducedMotion }) {
3172
+ const [visible, setVisible] = reactExports.useState(false);
3173
+ const [index, setIndex] = reactExports.useState(0);
3174
+ reactExports.useEffect(() => {
3175
+ setVisible(readDismissals() < DISMISSAL_LIMIT);
3176
+ }, []);
2612
3177
  reactExports.useEffect(() => {
2613
- if (!isDragging)
3178
+ if (reducedMotion)
2614
3179
  return;
2615
- const onPointerMove = (e) => {
2616
- if (e.pointerId !== pointerIdRef.current)
2617
- return;
2618
- const container = containerRef.current;
2619
- if (!container)
2620
- return;
2621
- const rect = container.getBoundingClientRect();
2622
- pendingPx.current = clampPx(e.clientX - rect.left);
2623
- if (!rafRef.current) {
2624
- rafRef.current = requestAnimationFrame(() => {
2625
- rafRef.current = null;
2626
- setStartPx(pendingPx.current);
2627
- });
2628
- }
2629
- };
2630
- const onPointerUp = (e) => {
2631
- if (e.pointerId !== pointerIdRef.current)
2632
- return;
2633
- const gutter2 = gutterRef.current;
2634
- if (gutter2 == null ? void 0 : gutter2.hasPointerCapture(e.pointerId)) {
2635
- gutter2.releasePointerCapture(e.pointerId);
2636
- }
2637
- if (rafRef.current) {
2638
- cancelAnimationFrame(rafRef.current);
2639
- rafRef.current = null;
2640
- }
2641
- pointerIdRef.current = null;
2642
- setIsDragging(false);
2643
- };
2644
- document.addEventListener("pointermove", onPointerMove);
2645
- document.addEventListener("pointerup", onPointerUp);
2646
- document.addEventListener("pointercancel", onPointerUp);
2647
- return () => {
2648
- document.removeEventListener("pointermove", onPointerMove);
2649
- document.removeEventListener("pointerup", onPointerUp);
2650
- document.removeEventListener("pointercancel", onPointerUp);
2651
- };
2652
- }, [isDragging, clampPx]);
2653
- const handlePointerDown = (e) => {
2654
- e.currentTarget.setPointerCapture(e.pointerId);
2655
- pointerIdRef.current = e.pointerId;
2656
- setIsDragging(true);
2657
- };
2658
- const handleLostPointerCapture = () => {
2659
- pointerIdRef.current = null;
2660
- setIsDragging(false);
2661
- if (rafRef.current) {
2662
- cancelAnimationFrame(rafRef.current);
2663
- rafRef.current = null;
3180
+ const id = setInterval(() => {
3181
+ setIndex((i) => i === 0 ? 1 : 0);
3182
+ }, HINT_ROTATION_MS);
3183
+ return () => clearInterval(id);
3184
+ }, [reducedMotion]);
3185
+ if (!visible || !hasMultipleSpans)
3186
+ return null;
3187
+ const dismiss = () => {
3188
+ try {
3189
+ window.localStorage.setItem(DISMISSALS_KEY, String(readDismissals() + 1));
3190
+ } catch {
2664
3191
  }
3192
+ setVisible(false);
2665
3193
  };
2666
- const colTemplate = `${startPx}px ${GUTTER_PX}px minmax(${MIN_PX}px, 1fr)`;
2667
- const hasHeaders = startHeader != null || endHeader != null;
2668
- const gutter = jsxRuntimeExports.jsx("div", { ref: gutterRef, className: "relative z-20 isolate flex shrink-0 cursor-col-resize justify-center", onPointerDown: handlePointerDown, onLostPointerCapture: handleLostPointerCapture, children: jsxRuntimeExports.jsx("span", { className: "pointer-events-none relative z-10 h-full w-px shrink-0 bg-gray-alpha-400", "aria-hidden": true }) });
2669
- if (hasHeaders) {
2670
- return jsxRuntimeExports.jsxs("div", { className: cn("flex flex-col h-full min-h-0", className), children: [jsxRuntimeExports.jsxs("div", { className: "shrink-0 grid", style: { gridTemplateColumns: colTemplate }, children: [jsxRuntimeExports.jsx("div", { children: startHeader }), jsxRuntimeExports.jsx("div", { className: "flex justify-center", children: jsxRuntimeExports.jsx("span", { className: "h-full w-px bg-gray-alpha-400", "aria-hidden": true }) }), jsxRuntimeExports.jsx("div", { children: endHeader })] }), jsxRuntimeExports.jsxs("div", { ref: containerRef, className: cn("grid flex-1 min-h-0 overflow-x-hidden overflow-y-auto", isDragging && "select-none"), style: { gridTemplateColumns: colTemplate }, children: [start, gutter, end] })] });
2671
- }
2672
- return jsxRuntimeExports.jsxs("div", { ref: containerRef, className: cn("grid h-full min-h-0 content-start overflow-x-hidden overflow-y-auto", isDragging && "select-none", className), style: { gridTemplateColumns: colTemplate, height: "100%" }, children: [start, gutter, end] });
2673
- }
2674
- function DetailPanel({ span, rootStart, onClose }) {
2675
- const startMs = getHighResInMs(span.startTime);
2676
- const durationMs = getSpanDurationMs(span);
2677
- const offsetMs = startMs - rootStart;
2678
- return jsxRuntimeExports.jsxs("aside", { className: "grid h-full max-h-full grid-rows-[2.5rem_1fr] bg-background-200", children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-3 border-b border-gray-alpha-400", children: [jsxRuntimeExports.jsx("span", { className: "text-sm font-medium text-gray-1000 truncate", children: span.name }), jsxRuntimeExports.jsx("button", { type: "button", className: "p-1 rounded-md text-gray-900 hover:text-gray-1000 hover:bg-gray-alpha-200 transition-colors", onClick: onClose, children: jsxRuntimeExports.jsx(X, { className: "w-4 h-4" }) })] }), jsxRuntimeExports.jsx("div", { className: "overflow-y-auto p-3 space-y-3", children: jsxRuntimeExports.jsxs("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 text-sm", children: [jsxRuntimeExports.jsx("dt", { className: "text-gray-900", children: "Resource" }), jsxRuntimeExports.jsx("dd", { className: "text-gray-1000 font-mono", children: span.resource }), jsxRuntimeExports.jsx("dt", { className: "text-gray-900", children: "Duration" }), jsxRuntimeExports.jsx("dd", { className: "text-gray-1000 tabular-nums font-mono", children: formatDuration(durationMs) }), jsxRuntimeExports.jsx("dt", { className: "text-gray-900", children: "Offset" }), jsxRuntimeExports.jsxs("dd", { className: "text-gray-1000 tabular-nums font-mono", children: ["+", formatDuration(offsetMs)] }), jsxRuntimeExports.jsx("dt", { className: "text-gray-900", children: "Status" }), jsxRuntimeExports.jsx("dd", { className: "text-gray-1000 font-mono", children: span.status.code === 2 ? "Error" : "OK" })] }) })] });
3194
+ return jsxRuntimeExports.jsxs("div", { className: "group pointer-events-auto hidden h-8 w-fit items-center gap-1 text-label-12 leading-none text-gray-900 @min-[480px]:flex", children: [jsxRuntimeExports.jsx("span", { "aria-live": "polite", "aria-atomic": "true", className: "inline-flex items-center whitespace-nowrap", children: reducedMotion ? jsxRuntimeExports.jsx(AltHint, {}) : jsxRuntimeExports.jsx("span", { className: `inline-flex items-center ${styles.hint}`, children: index === 0 ? jsxRuntimeExports.jsx(AltHint, {}) : jsxRuntimeExports.jsx(NavHint, {}) }, index) }), jsxRuntimeExports.jsx("button", { type: "button", className: "inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-accent hover:text-foreground group-hover:opacity-100 focus-visible:opacity-100 motion-reduce:transition-none", onClick: dismiss, "aria-label": "Dismiss trace shortcuts helper", children: jsxRuntimeExports.jsx(X, { className: "h-3 w-3" }) })] });
2679
3195
  }
2680
3196
  const MIN_VIEWPORT_MS = 1e-3;
3197
+ const ZOOM_DEBOUNCE_MS = 150;
2681
3198
  function useAnimatedViewport(initial) {
2682
3199
  const [viewport, setViewportState] = reactExports.useState(initial);
2683
3200
  const animRef = reactExports.useRef(null);
2684
3201
  const currentRef = reactExports.useRef(initial);
2685
3202
  currentRef.current = viewport;
3203
+ const reducedMotion = useReducedMotion();
2686
3204
  const cancel = reactExports.useCallback(() => {
2687
3205
  if (animRef.current) {
2688
3206
  cancelAnimationFrame(animRef.current.raf);
@@ -2691,11 +3209,16 @@ function useAnimatedViewport(initial) {
2691
3209
  }, []);
2692
3210
  const animateTo = reactExports.useCallback((target) => {
2693
3211
  cancel();
3212
+ if (reducedMotion) {
3213
+ currentRef.current = target;
3214
+ setViewportState(target);
3215
+ return;
3216
+ }
2694
3217
  const from = currentRef.current;
2695
3218
  const anim = { raf: 0, from, to: target, start: performance.now() };
2696
3219
  const tick = () => {
2697
- const t = Math.min((performance.now() - anim.start) / 150, 1);
2698
- const e = 1 - (1 - t) * (1 - t);
3220
+ const t = Math.min((performance.now() - anim.start) / 240, 1);
3221
+ const e = t < 0.5 ? 8 * t * t * t * t : 1 - (-2 * t + 2) ** 4 / 2;
2699
3222
  setViewportState({
2700
3223
  start: anim.from.start + (anim.to.start - anim.from.start) * e,
2701
3224
  end: anim.from.end + (anim.to.end - anim.from.end) * e
@@ -2707,7 +3230,7 @@ function useAnimatedViewport(initial) {
2707
3230
  };
2708
3231
  animRef.current = anim;
2709
3232
  anim.raf = requestAnimationFrame(tick);
2710
- }, [cancel]);
3233
+ }, [cancel, reducedMotion]);
2711
3234
  const setViewport = reactExports.useCallback((update) => {
2712
3235
  cancel();
2713
3236
  if (typeof update === "function") {
@@ -2724,38 +3247,26 @@ function useAnimatedViewport(initial) {
2724
3247
  reactExports.useEffect(() => cancel, [cancel]);
2725
3248
  return { viewport, setViewport, animateTo };
2726
3249
  }
2727
- function useSelectedSpanInfo() {
2728
- const { activeSpan } = useActiveSpan();
2729
- const sidebar = useSidebarDataOptional();
2730
- return reactExports.useMemo(() => {
2731
- var _a, _b;
2732
- if (!activeSpan || !sidebar)
2733
- return null;
2734
- const correlationId = activeSpan.spanId;
2735
- const rawEvents = correlationId ? sidebar.events.filter((e) => e.correlationId === correlationId) : [];
2736
- return {
2737
- data: (_a = activeSpan.attributes) == null ? void 0 : _a.data,
2738
- resource: (_b = activeSpan.attributes) == null ? void 0 : _b.resource,
2739
- spanId: activeSpan.spanId,
2740
- rawEvents
2741
- };
2742
- }, [activeSpan, sidebar == null ? void 0 : sidebar.events]);
2743
- }
2744
- function NewTraceViewer$1({ trace }) {
2745
- return jsxRuntimeExports.jsx(ActiveSpanProvider, { spans: trace.spans, children: jsxRuntimeExports.jsx(NewTraceViewerContent, { trace }) });
3250
+ function TraceViewer$1({ trace, onLoadMore, hasMore, isLoadingMore }) {
3251
+ return jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 300, children: jsxRuntimeExports.jsx(ActiveSpanProvider, { spans: trace.spans, children: jsxRuntimeExports.jsx(TraceViewerContent, { trace, onLoadMore, hasMore, isLoadingMore }) }) });
2746
3252
  }
2747
- function NewTraceViewerContent({ trace }) {
2748
- const { activeSpan, activeSpanId, setActiveSpan, clearActiveSpan } = useActiveSpan();
2749
- const sidebar = useSidebarDataOptional();
2750
- const selectedSpan = useSelectedSpanInfo();
3253
+ function TraceViewerContent({ trace, onLoadMore, hasMore, isLoadingMore }) {
3254
+ const { activeSpanId, setActiveSpan, clearActiveSpan } = useActiveSpan();
3255
+ const reducedMotion = useReducedMotion();
2751
3256
  const [searchQuery, setSearchQuery] = reactExports.useState("");
2752
- const filteredSpans = reactExports.useMemo(() => {
2753
- const q = searchQuery.trim().toLowerCase();
2754
- if (!q)
2755
- return trace.spans;
2756
- return trace.spans.filter((s) => s.name.toLowerCase().includes(q) || s.resource.toLowerCase().includes(q));
2757
- }, [trace.spans, searchQuery]);
3257
+ const deferredSearchQuery = reactExports.useDeferredValue(searchQuery);
3258
+ const paneRootRef = reactExports.useRef(null);
3259
+ const searchResult = reactExports.useMemo(() => searchSpans(trace.spans, deferredSearchQuery), [trace.spans, deferredSearchQuery]);
2758
3260
  const root = reactExports.useMemo(() => computeRootBounds(trace.spans), [trace.spans]);
3261
+ const scrollContainerRef = reactExports.useRef(null);
3262
+ const loadMore = reactExports.useCallback(() => {
3263
+ void (onLoadMore == null ? void 0 : onLoadMore());
3264
+ }, [onLoadMore]);
3265
+ const loadMoreSentinelRef = useLoadMoreOnScroll(loadMore, {
3266
+ hasMore: Boolean(onLoadMore && hasMore),
3267
+ isLoadingMore: Boolean(isLoadingMore),
3268
+ rootRef: scrollContainerRef
3269
+ });
2759
3270
  const { viewport, setViewport, animateTo } = useAnimatedViewport({
2760
3271
  start: root.startTime,
2761
3272
  end: root.startTime + root.duration
@@ -2776,53 +3287,46 @@ function NewTraceViewerContent({ trace }) {
2776
3287
  return prev;
2777
3288
  });
2778
3289
  prevRootRef.current = { start: newStart, end: newEnd };
2779
- }, [root.startTime, root.duration]);
3290
+ }, [root.startTime, root.duration, setViewport]);
2780
3291
  const viewDuration = viewport.end - viewport.start;
3292
+ const viewportRef = reactExports.useRef(viewport);
3293
+ viewportRef.current = viewport;
2781
3294
  const timeMarkers = reactExports.useMemo(() => computeTimeMarkers(viewDuration, viewport.start - root.startTime), [viewDuration, viewport.start, root.startTime]);
2782
3295
  const resetZoom = reactExports.useCallback(() => {
2783
3296
  animateTo({ start: root.startTime, end: root.startTime + root.duration });
2784
3297
  }, [animateTo, root.startTime, root.duration]);
3298
+ const clampToRoot = reactExports.useCallback((next) => clampViewportToRoot(next, root.startTime, root.endTime, MIN_VIEWPORT_MS), [root.startTime, root.endTime]);
3299
+ const handleRevealTime = reactExports.useCallback((timeMs) => {
3300
+ const { start, end } = viewportRef.current;
3301
+ const duration = end - start;
3302
+ animateTo(clampToRoot({
3303
+ start: timeMs - duration / 2,
3304
+ end: timeMs + duration / 2
3305
+ }));
3306
+ }, [animateTo, clampToRoot]);
2785
3307
  const ZOOM_FACTOR = 0.5;
2786
3308
  const zoomBy = reactExports.useCallback((factor) => {
2787
- const rootS = root.startTime;
2788
- const rootE = root.startTime + root.duration;
2789
- const rootD = root.duration;
2790
- setViewport((prev) => {
2791
- const prevDuration = prev.end - prev.start;
2792
- const center = (prev.start + prev.end) / 2;
2793
- const newDuration = Math.max(MIN_VIEWPORT_MS, Math.min(rootD, prevDuration * factor));
2794
- let newStart = center - newDuration / 2;
2795
- let newEnd = center + newDuration / 2;
2796
- if (newStart < rootS) {
2797
- newStart = rootS;
2798
- newEnd = rootS + newDuration;
2799
- }
2800
- if (newEnd > rootE) {
2801
- newEnd = rootE;
2802
- newStart = Math.max(rootS, rootE - newDuration);
2803
- }
2804
- return { start: newStart, end: newEnd };
2805
- });
2806
- }, [setViewport, root.startTime, root.duration]);
3309
+ const { start, end } = viewportRef.current;
3310
+ const center = (start + end) / 2;
3311
+ const newDuration = Math.max(MIN_VIEWPORT_MS, (end - start) * factor);
3312
+ animateTo(clampToRoot({
3313
+ start: center - newDuration / 2,
3314
+ end: center + newDuration / 2
3315
+ }));
3316
+ }, [animateTo, clampToRoot]);
2807
3317
  const zoomIn = reactExports.useCallback(() => zoomBy(ZOOM_FACTOR), [zoomBy]);
2808
3318
  const zoomOut = reactExports.useCallback(() => zoomBy(1 / ZOOM_FACTOR), [zoomBy]);
2809
- const handleSelectSpan = reactExports.useCallback((spanId) => {
2810
- if (spanId === activeSpanId) {
2811
- clearActiveSpan();
2812
- return;
2813
- }
2814
- setActiveSpan(spanId);
3319
+ const isAtMinZoom = viewDuration >= root.duration;
3320
+ const isAtMaxZoom = viewDuration <= MIN_VIEWPORT_MS;
3321
+ const focusViewportOnSpan = reactExports.useCallback((spanId) => {
2815
3322
  const span = trace.spans.find((s) => s.spanId === spanId);
2816
3323
  if (!span)
2817
3324
  return;
2818
3325
  const spanStart = getHighResInMs(span.startTime);
2819
3326
  const spanEnd = getHighResInMs(span.endTime);
2820
3327
  const spanDuration = spanEnd - spanStart;
2821
- const rootS = root.startTime;
2822
- const rootE = root.startTime + root.duration;
2823
- const rootD = root.duration;
2824
- if (spanDuration > rootD * 0.8) {
2825
- animateTo({ start: rootS, end: rootE });
3328
+ if (spanDuration > root.duration * 0.8) {
3329
+ animateTo({ start: root.startTime, end: root.endTime });
2826
3330
  return;
2827
3331
  }
2828
3332
  const padding = Math.max(spanDuration * 0.2, MIN_VIEWPORT_MS / 2);
@@ -2833,34 +3337,67 @@ function NewTraceViewerContent({ trace }) {
2833
3337
  newStart = center - MIN_VIEWPORT_MS / 2;
2834
3338
  newEnd = center + MIN_VIEWPORT_MS / 2;
2835
3339
  }
2836
- if (newStart < rootS) {
2837
- const duration = newEnd - newStart;
2838
- newStart = rootS;
2839
- newEnd = Math.min(rootE, rootS + duration);
2840
- }
2841
- if (newEnd > rootE) {
2842
- const duration = newEnd - newStart;
2843
- newEnd = rootE;
2844
- newStart = Math.max(rootS, rootE - duration);
2845
- }
2846
- animateTo({ start: newStart, end: newEnd });
3340
+ animateTo(clampToRoot({ start: newStart, end: newEnd }));
2847
3341
  }, [
2848
3342
  animateTo,
2849
- setActiveSpan,
2850
- clearActiveSpan,
2851
- activeSpanId,
2852
3343
  trace.spans,
2853
3344
  root.startTime,
2854
- root.duration
3345
+ root.endTime,
3346
+ root.duration,
3347
+ clampToRoot
3348
+ ]);
3349
+ const scrollSpanIntoView = reactExports.useCallback((spanId) => {
3350
+ var _a;
3351
+ const index = trace.spans.findIndex((s) => s.spanId === spanId);
3352
+ if (index === -1)
3353
+ return;
3354
+ const list = ((_a = scrollContainerRef.current) == null ? void 0 : _a.querySelector("#event-list")) ?? null;
3355
+ scrollRowIntoView(list, index, ROW_HEIGHT_PX, {
3356
+ behavior: reducedMotion ? "auto" : "smooth"
3357
+ });
3358
+ }, [trace.spans, reducedMotion]);
3359
+ const zoomTimerRef = reactExports.useRef(null);
3360
+ const cancelPendingZoom = reactExports.useCallback(() => {
3361
+ if (zoomTimerRef.current !== null) {
3362
+ clearTimeout(zoomTimerRef.current);
3363
+ zoomTimerRef.current = null;
3364
+ }
3365
+ }, []);
3366
+ reactExports.useEffect(() => cancelPendingZoom, [cancelPendingZoom]);
3367
+ const handleClearActiveSpan = reactExports.useCallback(() => {
3368
+ cancelPendingZoom();
3369
+ clearActiveSpan();
3370
+ }, [cancelPendingZoom, clearActiveSpan]);
3371
+ const handleSelectSpan = reactExports.useCallback((spanId) => {
3372
+ cancelPendingZoom();
3373
+ if (spanId === activeSpanId) {
3374
+ clearActiveSpan();
3375
+ return;
3376
+ }
3377
+ setActiveSpan(spanId);
3378
+ focusViewportOnSpan(spanId);
3379
+ }, [
3380
+ cancelPendingZoom,
3381
+ activeSpanId,
3382
+ clearActiveSpan,
3383
+ setActiveSpan,
3384
+ focusViewportOnSpan
2855
3385
  ]);
3386
+ const navigateToSpan = reactExports.useCallback((spanId) => {
3387
+ setActiveSpan(spanId);
3388
+ scrollSpanIntoView(spanId);
3389
+ cancelPendingZoom();
3390
+ zoomTimerRef.current = setTimeout(() => {
3391
+ zoomTimerRef.current = null;
3392
+ focusViewportOnSpan(spanId);
3393
+ }, ZOOM_DEBOUNCE_MS);
3394
+ }, [setActiveSpan, scrollSpanIntoView, cancelPendingZoom, focusViewportOnSpan]);
2856
3395
  const [altHeld, setAltHeld] = reactExports.useState(false);
2857
3396
  reactExports.useEffect(() => {
2858
3397
  const onKeyDown = (e) => {
2859
3398
  if (e.key === "Escape") {
2860
- clearActiveSpan();
2861
- }
2862
- if (e.key === "Alt") {
2863
- e.preventDefault();
3399
+ handleClearActiveSpan();
3400
+ } else if (e.key === "Alt") {
2864
3401
  setAltHeld(true);
2865
3402
  }
2866
3403
  };
@@ -2877,16 +3414,16 @@ function NewTraceViewerContent({ trace }) {
2877
3414
  window.removeEventListener("keyup", onKeyUp);
2878
3415
  window.removeEventListener("blur", onBlur);
2879
3416
  };
2880
- }, [clearActiveSpan]);
3417
+ }, [handleClearActiveSpan]);
2881
3418
  const timelineRef = reactExports.useRef(null);
2882
- const [hoverFraction, setHoverFraction] = reactExports.useState(null);
3419
+ const [hover, setHover] = reactExports.useState(null);
2883
3420
  const hoverInfo = reactExports.useMemo(() => {
2884
- if (hoverFraction == null)
3421
+ if (hover == null)
2885
3422
  return null;
2886
- const absTime = viewport.start + hoverFraction * viewDuration;
3423
+ const absTime = viewport.start + hover.fraction * viewDuration;
2887
3424
  const offset = absTime - root.startTime;
2888
- return { fraction: hoverFraction, label: formatDuration(offset, true) };
2889
- }, [hoverFraction, viewport.start, viewDuration, root.startTime]);
3425
+ return { fraction: hover.fraction, label: formatDurationPrecise(offset) };
3426
+ }, [hover, viewport.start, viewDuration, root.startTime]);
2890
3427
  const handleTimelineMouseMove = reactExports.useCallback((e) => {
2891
3428
  const el = timelineRef.current;
2892
3429
  if (!el)
@@ -2896,19 +3433,17 @@ function NewTraceViewerContent({ trace }) {
2896
3433
  if (contentWidth <= 0)
2897
3434
  return;
2898
3435
  const fraction = Math.max(0, Math.min(1, (e.clientX - rect.left - TIMELINE_PADDING_PX) / contentWidth));
2899
- setHoverFraction(fraction);
3436
+ setHover({
3437
+ fraction,
3438
+ rowIndex: Math.floor((e.clientY - rect.top) / ROW_HEIGHT_PX)
3439
+ });
2900
3440
  }, []);
2901
3441
  const handleTimelineMouseLeave = reactExports.useCallback(() => {
2902
- setHoverFraction(null);
3442
+ setHover(null);
2903
3443
  }, []);
2904
3444
  reactExports.useEffect(() => {
2905
3445
  const el = timelineRef.current;
2906
- if (!el)
2907
- return;
2908
- const rootS = root.startTime;
2909
- const rootE = root.startTime + root.duration;
2910
- const rootD = root.duration;
2911
- if (rootD <= 0)
3446
+ if (!el || root.duration <= 0)
2912
3447
  return;
2913
3448
  const onWheel = (e) => {
2914
3449
  const isZoomGesture = e.ctrlKey || e.metaKey;
@@ -2921,81 +3456,50 @@ function NewTraceViewerContent({ trace }) {
2921
3456
  if (contentWidth <= 0)
2922
3457
  return;
2923
3458
  if (isZoomGesture) {
2924
- let dy = e.deltaY;
2925
- if (e.deltaMode === 1)
2926
- dy *= 16;
2927
3459
  const cursorFraction = Math.max(0, Math.min(1, (e.clientX - rect.left - TIMELINE_PADDING_PX) / contentWidth));
2928
- const scaleFactor = Math.pow(2, dy / 200);
3460
+ const scaleFactor = wheelZoomScaleFactor(e);
2929
3461
  setViewport((prev) => {
2930
3462
  const prevDuration = prev.end - prev.start;
2931
3463
  const cursorTime = prev.start + cursorFraction * prevDuration;
2932
- const newDuration = Math.max(MIN_VIEWPORT_MS, Math.min(rootD, prevDuration * scaleFactor));
2933
- let newStart = cursorTime - cursorFraction * newDuration;
2934
- let newEnd = newStart + newDuration;
2935
- if (newStart < rootS) {
2936
- newStart = rootS;
2937
- newEnd = rootS + newDuration;
2938
- }
2939
- if (newEnd > rootE) {
2940
- newEnd = rootE;
2941
- newStart = Math.max(rootS, rootE - newDuration);
2942
- }
2943
- return { start: newStart, end: newEnd };
3464
+ const newDuration = Math.max(MIN_VIEWPORT_MS, Math.min(root.duration, prevDuration * scaleFactor));
3465
+ return clampToRoot({
3466
+ start: cursorTime - cursorFraction * newDuration,
3467
+ end: cursorTime + (1 - cursorFraction) * newDuration
3468
+ });
2944
3469
  });
2945
3470
  } else {
2946
- let dx = e.deltaX;
2947
- if (e.deltaMode === 1)
2948
- dx *= 16;
3471
+ const dx = wheelDeltaToPixels(e.deltaX, e.deltaMode);
2949
3472
  setViewport((prev) => {
2950
- const prevDuration = prev.end - prev.start;
2951
- const panAmount = dx / contentWidth * prevDuration;
2952
- let newStart = prev.start + panAmount;
2953
- let newEnd = prev.end + panAmount;
2954
- if (newStart < rootS) {
2955
- newStart = rootS;
2956
- newEnd = rootS + prevDuration;
2957
- }
2958
- if (newEnd > rootE) {
2959
- newEnd = rootE;
2960
- newStart = Math.max(rootS, rootE - prevDuration);
2961
- }
2962
- return { start: newStart, end: newEnd };
3473
+ const panAmount = dx / contentWidth * (prev.end - prev.start);
3474
+ return clampToRoot({
3475
+ start: prev.start + panAmount,
3476
+ end: prev.end + panAmount
3477
+ });
2963
3478
  });
2964
3479
  }
2965
3480
  };
2966
3481
  el.addEventListener("wheel", onWheel, { passive: false });
2967
3482
  return () => el.removeEventListener("wheel", onWheel);
2968
- }, [root.startTime, root.duration]);
2969
- const selectedSpanName = reactExports.useMemo(() => {
2970
- var _a, _b;
2971
- if (!(selectedSpan == null ? void 0 : selectedSpan.data))
2972
- return "Details";
2973
- const data = selectedSpan.data;
2974
- const stepName = data.stepName;
2975
- const workflowName = data.workflowName;
2976
- return (stepName ? (_a = parseStepName(stepName)) == null ? void 0 : _a.shortName : void 0) ?? (workflowName ? (_b = parseWorkflowName(workflowName)) == null ? void 0 : _b.shortName : void 0) ?? stepName ?? workflowName ?? data.hookId ?? "Details";
2977
- }, [selectedSpan == null ? void 0 : selectedSpan.data]);
2978
- const selectedResource = selectedSpan == null ? void 0 : selectedSpan.resource;
2979
- const selectedResourceId = reactExports.useMemo(() => {
2980
- if (!(selectedSpan == null ? void 0 : selectedSpan.data))
2981
- return void 0;
2982
- const data = selectedSpan.data;
2983
- return data.stepId ?? data.runId ?? data.hookId ?? selectedSpan.spanId;
2984
- }, [selectedSpan == null ? void 0 : selectedSpan.data, selectedSpan == null ? void 0 : selectedSpan.spanId]);
2985
- return jsxRuntimeExports.jsxs("div", { "data-pane": "pane-root", "data-has-detail": activeSpan ? "" : void 0, 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: [jsxRuntimeExports.jsxs("div", { id: "trace-parent", className: "grid grid-rows-[1fr] h-full min-h-0 overflow-hidden relative bg-background-100", children: [jsxRuntimeExports.jsxs(SplitPane, { startHeader: jsxRuntimeExports.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: [jsxRuntimeExports.jsx(Search, { className: "w-3.5 h-3.5 shrink-0 text-gray-800" }), jsxRuntimeExports.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 && jsxRuntimeExports.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: jsxRuntimeExports.jsx(X, { className: "w-3 h-3" }) })] }), endHeader: jsxRuntimeExports.jsx(TimelineHeader, { markers: timeMarkers, hoverInfo }), children: [jsxRuntimeExports.jsx("div", { className: "block overflow-visible", children: jsxRuntimeExports.jsx(EventList, { spans: filteredSpans, activeSpanId, onSelectSpan: handleSelectSpan }) }), jsxRuntimeExports.jsx("div", { ref: timelineRef, className: "block min-h-0 overflow-visible relative", onDoubleClick: resetZoom, onMouseMove: handleTimelineMouseMove, onMouseLeave: handleTimelineMouseLeave, children: jsxRuntimeExports.jsx(Timeline, { spans: filteredSpans, viewStart: viewport.start, viewEnd: viewport.end, markers: timeMarkers, selectedId: activeSpanId, onSelect: handleSelectSpan, hoverFraction, altHeld }) })] }), jsxRuntimeExports.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: [jsxRuntimeExports.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: jsxRuntimeExports.jsx(ZoomOut, { className: "w-4 h-4" }) }), jsxRuntimeExports.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: jsxRuntimeExports.jsx(RotateCcw, { className: "w-3.5 h-3.5" }) }), jsxRuntimeExports.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: jsxRuntimeExports.jsx(ZoomIn, { className: "w-4 h-4" }) })] })] }), activeSpan && sidebar ? jsxRuntimeExports.jsxs("aside", { className: "flex flex-col h-full max-h-full bg-background-100 border-l border-gray-alpha-400 overflow-auto", children: [jsxRuntimeExports.jsx("div", { className: "flex-shrink-0 px-4 pt-4 pb-3", children: jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between gap-2", children: [jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [jsxRuntimeExports.jsx("span", { className: "text-[15px] font-semibold text-gray-1000 truncate block", children: selectedSpanName }), selectedResourceId && jsxRuntimeExports.jsxs("div", { className: "mt-1 flex items-center gap-2", children: [selectedResource && jsxRuntimeExports.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" ? "bg-green-200 text-green-900" : selectedResource === "run" ? "bg-blue-200 text-blue-900" : selectedResource === "hook" ? "bg-yellow-200 text-yellow-900" : "bg-gray-200 text-gray-900"}`, children: selectedResource.charAt(0).toUpperCase() + selectedResource.slice(1) }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-[13px] font-mono text-gray-700 min-w-0", title: selectedResourceId, children: [jsxRuntimeExports.jsx("span", { className: "truncate", children: selectedResourceId }), jsxRuntimeExports.jsx(CopyButton, { copyText: selectedResourceId, ariaLabel: "Copy ID", className: "shrink-0" })] })] })] }), jsxRuntimeExports.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: jsxRuntimeExports.jsx(X, { className: "w-4 h-4" }) })] }) }), jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto", children: jsxRuntimeExports.jsx(ErrorBoundary, { children: jsxRuntimeExports.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 }) }) })] }) : activeSpan ? jsxRuntimeExports.jsx(DetailPanel, { span: activeSpan, rootStart: root.startTime, onClose: clearActiveSpan }) : null] });
3483
+ }, [root.duration, setViewport, clampToRoot]);
3484
+ return jsxRuntimeExports.jsxs("div", { ref: paneRootRef, "data-pane": "pane-root", className: "relative flex w-full h-full max-h-full", children: [jsxRuntimeExports.jsxs("div", { id: "trace-parent", className: "@container flex-1 min-w-0 grid grid-rows-[auto_1fr] h-full min-h-0 overflow-hidden relative bg-background-100", children: [jsxRuntimeExports.jsx(Minimap, { spans: trace.spans, root, viewport, minViewportMs: MIN_VIEWPORT_MS, onViewportChange: setViewport, onAnimateTo: animateTo }), jsxRuntimeExports.jsxs(SplitPane, { scrollContainerRef, startHeader: jsxRuntimeExports.jsxs("div", { className: "bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-center pl-4 pr-2 gap-1.5", children: [jsxRuntimeExports.jsx(Search, { className: "w-3.5 h-3.5 shrink-0 text-gray-800" }), jsxRuntimeExports.jsx("input", { id: "trace-viewer-search", name: "trace-viewer-search", type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onKeyDown: (e) => {
3485
+ if (e.key === "Escape" && searchQuery) {
3486
+ e.preventDefault();
3487
+ e.stopPropagation();
3488
+ setSearchQuery("");
3489
+ }
3490
+ }, placeholder: "Search spans...", "aria-label": "Search spans", className: "flex-1 min-w-0 bg-transparent text-label-14 text-gray-1000 placeholder:text-gray-800 outline-none" }), searchQuery && jsxRuntimeExports.jsx("button", { type: "button", "aria-label": "Clear search", onClick: () => setSearchQuery(""), className: "-mr-2 hidden h-full max-w-full shrink-0 cursor-pointer items-center rounded-r-md border-0 bg-transparent px-2.5 font-inherit text-label-16 text-gray-900 no-underline transition-colors duration-150 ease-in hover:text-gray-1000 focus-visible:-outline-offset-1 focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--ds-focus-color)] min-[961px]:flex", children: jsxRuntimeExports.jsx(Kbd, { variant: "outline", size: "search", children: "Esc" }) })] }), endHeader: jsxRuntimeExports.jsx(TimelineHeader, { markers: timeMarkers, hoverInfo }), children: [jsxRuntimeExports.jsxs("div", { className: "block overflow-visible", children: [jsxRuntimeExports.jsx(EventList, { spans: trace.spans, activeSpanId, searchResult, onSelectSpan: handleSelectSpan }), jsxRuntimeExports.jsx("div", { ref: loadMoreSentinelRef, className: "flex justify-center", children: isLoadingMore ? jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center gap-2 py-3 text-label-14 text-gray-800", children: [jsxRuntimeExports.jsx(Spinner, { size: 14 }), jsxRuntimeExports.jsx("span", { children: "Loading spans…" })] }) : null })] }), jsxRuntimeExports.jsx("div", { ref: timelineRef, id: "trace-timeline", className: "@container block min-h-0 overflow-visible relative", onDoubleClick: resetZoom, onMouseMove: handleTimelineMouseMove, onMouseLeave: handleTimelineMouseLeave, children: jsxRuntimeExports.jsx(Timeline, { spans: trace.spans, viewStart: viewport.start, viewEnd: viewport.end, markers: timeMarkers, selectedId: activeSpanId, searchResult, onSelect: handleSelectSpan, onRevealTime: handleRevealTime, hover, altHeld }) }), jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(TraceShortcutHelper, { hasMultipleSpans: trace.spans.length > 1, reducedMotion }), jsxRuntimeExports.jsxs("div", { className: "pointer-events-auto flex items-center border border-gray-alpha-400 rounded-md bg-background-100 shadow-sm overflow-hidden divide-x divide-gray-alpha-400", children: [jsxRuntimeExports.jsx(IconButton, { variant: "muted", size: "small", onClick: zoomOut, disabled: isAtMinZoom, "aria-label": "Zoom out", children: jsxRuntimeExports.jsx(ZoomOut, { className: "w-4 h-4" }) }), jsxRuntimeExports.jsx(IconButton, { variant: "muted", size: "small", onClick: resetZoom, "aria-label": "Reset zoom", children: jsxRuntimeExports.jsx(RotateCcw, { className: "w-3.5 h-3.5" }) }), jsxRuntimeExports.jsx(IconButton, { variant: "muted", size: "small", onClick: zoomIn, disabled: isAtMaxZoom, "aria-label": "Zoom in", children: jsxRuntimeExports.jsx(ZoomIn, { className: "w-4 h-4" }) })] })] })] })] }), jsxRuntimeExports.jsx(TraceDetailPanel, { containerRef: paneRootRef, onNavigateToSpan: navigateToSpan, onClose: handleClearActiveSpan })] });
2986
3491
  }
2987
- const NewTraceViewer = ({ run, events, sidebarData }) => {
2988
- const traceWithMeta = reactExports.useMemo(() => {
3492
+ const TraceViewer = ({ run, events, sidebarData, onLoadMore, hasMore, isLoadingMore, loading = false }) => {
3493
+ const trace = reactExports.useMemo(() => {
2989
3494
  if (!(run == null ? void 0 : run.runId)) {
2990
3495
  return void 0;
2991
3496
  }
2992
3497
  return buildTrace(run, events, /* @__PURE__ */ new Date());
2993
3498
  }, [run, events]);
2994
- const trace = traceWithMeta;
2995
- if (!trace) {
2996
- return jsxRuntimeExports.jsx("div", { className: "relative w-full h-full flex items-center justify-center", children: jsxRuntimeExports.jsx("div", { className: "text-gray-500 text-sm", children: "Loading trace…" }) });
3499
+ if (!trace || loading && events.length === 0) {
3500
+ return jsxRuntimeExports.jsx(TraceViewerSkeleton, {});
2997
3501
  }
2998
- return jsxRuntimeExports.jsx(SidebarDataProvider, { value: sidebarData, children: jsxRuntimeExports.jsx("div", { className: "relative w-full h-full flex", children: jsxRuntimeExports.jsx(NewTraceViewer$1, { trace }) }) });
3502
+ return jsxRuntimeExports.jsx(SidebarDataProvider, { value: sidebarData, children: jsxRuntimeExports.jsx("div", { className: "relative w-full h-full flex", children: jsxRuntimeExports.jsx(TraceViewer$1, { trace, onLoadMore, hasMore, isLoadingMore }) }) });
2999
3503
  };
3000
3504
  async function fetchResourceWithCorrelationId(env, resource, resourceId, options = {}) {
3001
3505
  const resolveData = options.resolveData ?? "all";
@@ -3025,130 +3529,45 @@ async function fetchResourceWithCorrelationId(env, resource, resourceId, options
3025
3529
  }
3026
3530
  return { data: resourceData, correlationId };
3027
3531
  }
3028
- function useWorkflowResourceData(env, resource, resourceId, options = {}) {
3029
- const { refreshInterval = 0, runId, enabled = true, encryptionKey } = options;
3030
- const [data, setData] = reactExports.useState(
3031
- null
3032
- );
3033
- const [loading, setLoading] = reactExports.useState(enabled);
3034
- const [error, setError] = reactExports.useState(null);
3035
- const hydrate = reactExports.useCallback(
3036
- async (resource2) => encryptionKey ? hydrateResourceIOWithKey(resource2, encryptionKey) : hydrateResourceIO(resource2),
3037
- [encryptionKey]
3038
- );
3039
- const prevSelectionRef = reactExports.useRef("");
3040
- const fetchData = reactExports.useCallback(async () => {
3041
- if (!enabled) {
3042
- setLoading(false);
3043
- return;
3044
- }
3045
- const selectionKey = `${resource}:${resourceId}`;
3046
- if (selectionKey !== prevSelectionRef.current) {
3047
- setData(null);
3048
- prevSelectionRef.current = selectionKey;
3049
- }
3050
- setError(null);
3051
- setLoading(true);
3052
- if (resource === "hook") {
3053
- try {
3054
- const { error: error2, result } = await unwrapServerActionResult(
3055
- fetchHook(env, resourceId, "all")
3056
- );
3057
- if (error2) {
3058
- setError(error2);
3059
- return;
3060
- }
3061
- try {
3062
- setData(await hydrate(result));
3063
- } catch (hydrateError) {
3064
- setError(
3065
- hydrateError instanceof Error ? hydrateError : new Error(String(hydrateError))
3066
- );
3067
- }
3068
- } finally {
3069
- setLoading(false);
3070
- }
3071
- return;
3072
- }
3073
- if (resource === "sleep") {
3074
- try {
3075
- if (!runId) {
3076
- setError(new Error("runId is required for loading sleep details"));
3077
- return;
3078
- }
3079
- const { error: error2, result } = await unwrapServerActionResult(
3080
- fetchEvents(env, runId, {
3081
- sortOrder: "asc",
3082
- limit: 1e3,
3083
- withData: true
3084
- })
3085
- );
3086
- if (error2) {
3087
- setError(error2);
3088
- return;
3089
- }
3090
- try {
3091
- const allEvents = result.data.map(
3092
- hydrateResourceIO
3093
- );
3094
- const waitEvents = await Promise.all(
3095
- allEvents.filter((e) => e.correlationId === resourceId).map(hydrate)
3096
- );
3097
- const data2 = waitEventsToWaitEntity(waitEvents);
3098
- if (data2 === null) {
3099
- setError(
3100
- new Error(
3101
- `Failed to load ${resource} details: missing required event data`
3102
- )
3103
- );
3104
- return;
3105
- }
3106
- setData(data2);
3107
- } catch (hydrateError) {
3108
- setError(
3109
- hydrateError instanceof Error ? hydrateError : new Error(String(hydrateError))
3110
- );
3111
- }
3112
- } finally {
3113
- setLoading(false);
3114
- }
3115
- return;
3532
+ async function fetchSpanDetailResource(env, selection, options = {}) {
3533
+ const { resource, resourceId, runId } = selection;
3534
+ const { encryptionKey } = options;
3535
+ const hydrate = async (value) => hydrateResourceIOAsync(value, encryptionKey);
3536
+ if (resource === "hook") {
3537
+ const result = await unwrapOrThrow(fetchHook(env, resourceId, "all"));
3538
+ return hydrate(result);
3539
+ }
3540
+ if (resource === "sleep") {
3541
+ if (!runId) {
3542
+ throw new WorkflowWebAPIError(
3543
+ "runId is required for loading sleep details",
3544
+ { layer: "client" }
3545
+ );
3116
3546
  }
3117
- try {
3118
- const { data: resourceData } = await fetchResourceWithCorrelationId(
3119
- env,
3120
- resource,
3121
- resourceId,
3122
- { runId }
3547
+ const result = await unwrapOrThrow(
3548
+ fetchEvents(env, runId, { sortOrder: "asc", limit: 1e3, withData: true })
3549
+ );
3550
+ const waitEvents = await Promise.all(
3551
+ result.data.filter((e) => e.correlationId === resourceId).map(hydrate)
3552
+ );
3553
+ const data2 = waitEventsToWaitEntity(waitEvents);
3554
+ if (data2 === null) {
3555
+ throw new WorkflowWebAPIError(
3556
+ `Failed to load ${resource} details: missing required event data`,
3557
+ { layer: "client" }
3123
3558
  );
3124
- setData(await hydrate(resourceData));
3125
- } catch (error2) {
3126
- if (error2 instanceof Error) {
3127
- setError(error2);
3128
- } else {
3129
- setError(new Error(String(error2)));
3130
- }
3131
- return;
3132
- } finally {
3133
- setLoading(false);
3134
3559
  }
3135
- }, [env, resource, resourceId, runId, enabled, hydrate]);
3136
- reactExports.useEffect(() => {
3137
- fetchData();
3138
- }, [fetchData]);
3139
- reactExports.useEffect(() => {
3140
- if (!refreshInterval || refreshInterval <= 0) {
3141
- return;
3560
+ return data2;
3561
+ }
3562
+ const { data } = await fetchResourceWithCorrelationId(
3563
+ env,
3564
+ resource,
3565
+ resourceId,
3566
+ {
3567
+ runId
3142
3568
  }
3143
- const interval = setInterval(fetchData, refreshInterval);
3144
- return () => clearInterval(interval);
3145
- }, [refreshInterval, fetchData]);
3146
- return {
3147
- data,
3148
- loading,
3149
- error,
3150
- refresh: fetchData
3151
- };
3569
+ );
3570
+ return hydrate(data);
3152
3571
  }
3153
3572
  function mergeById(prev, next, idKey) {
3154
3573
  const combined = [...prev, ...next];
@@ -3187,6 +3606,7 @@ const LIVE_POLL_LIMIT = 100;
3187
3606
  const INITIAL_PAGE_SIZE$1 = 500;
3188
3607
  const LOAD_MORE_PAGE_SIZE$1 = 100;
3189
3608
  const LIVE_UPDATE_INTERVAL_MS = 5e3;
3609
+ const AUTO_LOAD_MAX_EVENTS = 500;
3190
3610
  function useWorkflowTraceViewerData(env, runId, options = {}) {
3191
3611
  const { live = false } = options;
3192
3612
  const [run, setRun] = reactExports.useState(null);
@@ -3295,6 +3715,12 @@ function useWorkflowTraceViewerData(env, runId, options = {}) {
3295
3715
  eventsHasMore,
3296
3716
  eventsCursor
3297
3717
  ]);
3718
+ reactExports.useEffect(() => {
3719
+ if (events.length >= AUTO_LOAD_MAX_EVENTS) {
3720
+ return;
3721
+ }
3722
+ loadMoreTraceData();
3723
+ }, [events.length, loadMoreTraceData]);
3298
3724
  const pollRun = reactExports.useCallback(async () => {
3299
3725
  if (run == null ? void 0 : run.completedAt) {
3300
3726
  return false;
@@ -3382,7 +3808,7 @@ async function readStream(_env, streamId, runId, signal, cursor) {
3382
3808
  if (cursor) {
3383
3809
  params.set("cursor", cursor);
3384
3810
  }
3385
- const url = `/api/stream/${encodeURIComponent(streamId)}?${params.toString()}`;
3811
+ const url = `${apiBase()}/api/stream/${encodeURIComponent(streamId)}?${params.toString()}`;
3386
3812
  const response = await fetch(url, { signal });
3387
3813
  if (!response.ok) {
3388
3814
  const errorData = await response.json().catch(() => null);
@@ -4555,6 +4981,7 @@ function BreadcrumbSeparator({
4555
4981
  }
4556
4982
  const INITIAL_PAGE_SIZE = 100;
4557
4983
  const LOAD_MORE_PAGE_SIZE = 100;
4984
+ const MAX_CORRELATION_SEARCH_PAGES = 30;
4558
4985
  function useEventsListData(env, runId, options = {}) {
4559
4986
  const { sortOrder = "asc", encryptionKey, enabled = true } = options;
4560
4987
  const [events, setEvents] = reactExports.useState([]);
@@ -4566,6 +4993,10 @@ function useEventsListData(env, runId, options = {}) {
4566
4993
  const isFetchingRef = reactExports.useRef(false);
4567
4994
  const encryptionKeyRef = reactExports.useRef(encryptionKey);
4568
4995
  encryptionKeyRef.current = encryptionKey;
4996
+ const hydrateEvents = reactExports.useCallback(async (rawEvents) => {
4997
+ const key = encryptionKeyRef.current;
4998
+ return Promise.all(rawEvents.map((ev) => hydrateResourceIOAsync(ev, key)));
4999
+ }, []);
4569
5000
  const fetchInitial = reactExports.useCallback(async () => {
4570
5001
  if (isFetchingRef.current) return;
4571
5002
  isFetchingRef.current = true;
@@ -4585,16 +5016,7 @@ function useEventsListData(env, runId, options = {}) {
4585
5016
  if (fetchError) {
4586
5017
  setError(fetchError);
4587
5018
  } else {
4588
- const hydrated = result.data.map(hydrateResourceIO);
4589
- const key = encryptionKeyRef.current;
4590
- if (key) {
4591
- const decrypted = await Promise.all(
4592
- hydrated.map((ev) => hydrateResourceIOWithKey(ev, key))
4593
- );
4594
- setEvents(decrypted);
4595
- } else {
4596
- setEvents(hydrated);
4597
- }
5019
+ setEvents(await hydrateEvents(result.data));
4598
5020
  setCursor(result.hasMore ? result.cursor : void 0);
4599
5021
  setHasMore(Boolean(result.hasMore));
4600
5022
  }
@@ -4604,14 +5026,14 @@ function useEventsListData(env, runId, options = {}) {
4604
5026
  setLoading(false);
4605
5027
  isFetchingRef.current = false;
4606
5028
  }
4607
- }, [env, runId, sortOrder]);
5029
+ }, [env, runId, sortOrder, hydrateEvents]);
4608
5030
  reactExports.useEffect(() => {
4609
5031
  if (enabled) fetchInitial();
4610
5032
  }, [fetchInitial, enabled]);
4611
5033
  reactExports.useEffect(() => {
4612
5034
  if (!encryptionKey || events.length === 0) return;
4613
5035
  let cancelled = false;
4614
- Promise.all(events.map((ev) => hydrateResourceIOWithKey(ev, encryptionKey))).then((decrypted) => {
5036
+ Promise.all(events.map((ev) => hydrateResourceIOAsync(ev, encryptionKey))).then((decrypted) => {
4615
5037
  if (!cancelled) setEvents(decrypted);
4616
5038
  }).catch(() => {
4617
5039
  });
@@ -4635,16 +5057,8 @@ function useEventsListData(env, runId, options = {}) {
4635
5057
  setError(fetchError);
4636
5058
  } else {
4637
5059
  if (result.data.length > 0) {
4638
- const hydrated = result.data.map(hydrateResourceIO);
4639
- const key = encryptionKeyRef.current;
4640
- if (key) {
4641
- const decrypted = await Promise.all(
4642
- hydrated.map((ev) => hydrateResourceIOWithKey(ev, key))
4643
- );
4644
- setEvents((prev) => [...prev, ...decrypted]);
4645
- } else {
4646
- setEvents((prev) => [...prev, ...hydrated]);
4647
- }
5060
+ const hydrated = await hydrateEvents(result.data);
5061
+ setEvents((prev) => [...prev, ...hydrated]);
4648
5062
  }
4649
5063
  setCursor(result.hasMore ? result.cursor : void 0);
4650
5064
  setHasMore(Boolean(result.hasMore));
@@ -4654,14 +5068,66 @@ function useEventsListData(env, runId, options = {}) {
4654
5068
  } finally {
4655
5069
  setLoadingMore(false);
4656
5070
  }
4657
- }, [env, runId, sortOrder, cursor, loadingMore]);
5071
+ }, [env, runId, sortOrder, cursor, loadingMore, hydrateEvents]);
5072
+ const searchByExactId = reactExports.useCallback(
5073
+ async (id, kind, signal) => {
5074
+ if (signal == null ? void 0 : signal.aborted) {
5075
+ throw new DOMException("Aborted", "AbortError");
5076
+ }
5077
+ if (kind === "event") {
5078
+ const { error: fetchError, result } = await unwrapServerActionResult(
5079
+ fetchEvent(env, runId, id, "none")
5080
+ );
5081
+ if (fetchError || (signal == null ? void 0 : signal.aborted)) {
5082
+ return fetchError ? { status: "error", message: fetchError.message } : (() => {
5083
+ throw new DOMException("Aborted", "AbortError");
5084
+ })();
5085
+ }
5086
+ const [event] = await hydrateEvents([result]);
5087
+ return (event == null ? void 0 : event.runId) === runId ? { status: "ok", events: [event] } : { status: "not_found" };
5088
+ }
5089
+ const matched = [];
5090
+ let nextCursor;
5091
+ let pagesFetched = 0;
5092
+ let truncated = false;
5093
+ do {
5094
+ if (signal == null ? void 0 : signal.aborted) {
5095
+ throw new DOMException("Aborted", "AbortError");
5096
+ }
5097
+ const { error: fetchError, result } = await unwrapServerActionResult(
5098
+ fetchEventsByCorrelationId(env, id, {
5099
+ cursor: nextCursor,
5100
+ sortOrder,
5101
+ limit: 100,
5102
+ withData: false,
5103
+ runId
5104
+ })
5105
+ );
5106
+ if (fetchError) {
5107
+ return { status: "error", message: fetchError.message };
5108
+ }
5109
+ if (signal == null ? void 0 : signal.aborted) {
5110
+ throw new DOMException("Aborted", "AbortError");
5111
+ }
5112
+ pagesFetched += 1;
5113
+ const hydrated = await hydrateEvents(result.data);
5114
+ matched.push(...hydrated.filter((event) => event.runId === runId));
5115
+ const hitPageCap = pagesFetched >= MAX_CORRELATION_SEARCH_PAGES;
5116
+ truncated = truncated || hitPageCap && Boolean(result.hasMore && result.cursor);
5117
+ nextCursor = !hitPageCap && result.hasMore && result.cursor ? result.cursor : void 0;
5118
+ } while (nextCursor);
5119
+ return matched.length > 0 ? { status: "ok", events: matched, truncated: truncated || void 0 } : { status: "not_found" };
5120
+ },
5121
+ [env, runId, sortOrder, hydrateEvents]
5122
+ );
4658
5123
  return {
4659
5124
  events,
4660
5125
  loading,
4661
5126
  error,
4662
5127
  hasMore,
4663
5128
  loadingMore,
4664
- loadMore
5129
+ loadMore,
5130
+ searchByExactId
4665
5131
  };
4666
5132
  }
4667
5133
  const FRAME_HEADER_SIZE = 4;
@@ -4682,6 +5148,9 @@ function detectEncoding(data) {
4682
5148
  function useStreamReader(env, streamId, runId, encryptionKey, runStatus) {
4683
5149
  const [chunks, setChunks] = reactExports.useState([]);
4684
5150
  const [isLive, setIsLive] = reactExports.useState(false);
5151
+ const [isInitialLoading, setIsInitialLoading] = reactExports.useState(
5152
+ Boolean(streamId && runId)
5153
+ );
4685
5154
  const [error, setError] = reactExports.useState(null);
4686
5155
  const abortControllerRef = reactExports.useRef(null);
4687
5156
  const chunkIdRef = reactExports.useRef(0);
@@ -4710,9 +5179,8 @@ function useStreamReader(env, streamId, runId, encryptionKey, runStatus) {
4710
5179
  if (!cryptoKey) {
4711
5180
  return { encrypted: true };
4712
5181
  }
4713
- const payload = frameData.slice(4);
4714
5182
  hydrated = hydrateData(
4715
- await decrypt(cryptoKey, payload),
5183
+ await decrypt(frameData, cryptoKey),
4716
5184
  revivers
4717
5185
  );
4718
5186
  } else {
@@ -4729,6 +5197,8 @@ function useStreamReader(env, streamId, runId, encryptionKey, runStatus) {
4729
5197
  reactExports.useEffect(() => {
4730
5198
  setChunks([]);
4731
5199
  setError(null);
5200
+ setIsLive(false);
5201
+ setIsInitialLoading(Boolean(streamId && runId));
4732
5202
  chunkIdRef.current = 0;
4733
5203
  frameCountRef.current = 0;
4734
5204
  serverCursorRef.current = null;
@@ -4737,13 +5207,12 @@ function useStreamReader(env, streamId, runId, encryptionKey, runStatus) {
4737
5207
  pollTimerRef.current = null;
4738
5208
  }
4739
5209
  if (!streamId || !runId) {
4740
- setIsLive(false);
5210
+ setIsInitialLoading(false);
4741
5211
  return;
4742
5212
  }
4743
5213
  let mounted = true;
4744
5214
  const abortController = new AbortController();
4745
5215
  abortControllerRef.current = abortController;
4746
- setIsLive(true);
4747
5216
  const revivers = getWebRevivers();
4748
5217
  const parseLegacyLine = (line) => {
4749
5218
  const chunkId = chunkIdRef.current++;
@@ -4865,13 +5334,14 @@ function useStreamReader(env, streamId, runId, encryptionKey, runStatus) {
4865
5334
  };
4866
5335
  const readStreamData = async () => {
4867
5336
  try {
4868
- const cryptoKey = encryptionKey ? await importKey(encryptionKey) : void 0;
5337
+ const cryptoKey = encryptionKey ? await deriveRunPayloadKeys(encryptionKey) : void 0;
4869
5338
  const initialChunks = [];
4870
5339
  const result = await fetchAndParse(initialChunks, cryptoKey);
4871
5340
  if (result.encrypted) {
4872
5341
  if (mounted) {
4873
5342
  setError("This stream is encrypted. Click Decrypt to view.");
4874
5343
  setIsLive(false);
5344
+ setIsInitialLoading(false);
4875
5345
  }
4876
5346
  return;
4877
5347
  }
@@ -4879,11 +5349,13 @@ function useStreamReader(env, streamId, runId, encryptionKey, runStatus) {
4879
5349
  serverCursorRef.current = result.cursor;
4880
5350
  if (!mounted || abortController.signal.aborted) return;
4881
5351
  setChunks(initialChunks);
5352
+ setIsInitialLoading(false);
4882
5353
  if (result.done) {
4883
5354
  setIsLive(false);
4884
5355
  return;
4885
5356
  }
4886
5357
  if (isRunActive(runStatusRef.current)) {
5358
+ setIsLive(true);
4887
5359
  const poll = async () => {
4888
5360
  if (!mounted || abortController.signal.aborted) return;
4889
5361
  try {
@@ -4921,6 +5393,7 @@ function useStreamReader(env, streamId, runId, encryptionKey, runStatus) {
4921
5393
  if (mounted) {
4922
5394
  setError(err instanceof Error ? err.message : String(err));
4923
5395
  setIsLive(false);
5396
+ setIsInitialLoading(false);
4924
5397
  }
4925
5398
  }
4926
5399
  };
@@ -4941,11 +5414,11 @@ function useStreamReader(env, streamId, runId, encryptionKey, runStatus) {
4941
5414
  setIsLive(false);
4942
5415
  }
4943
5416
  }, [runStatus]);
4944
- return { chunks, isLive, error };
5417
+ return { chunks, isLive, isInitialLoading, error };
4945
5418
  }
4946
5419
  function LiveStatus({ hasError, errorMessage }) {
4947
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
4948
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
5420
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip$1, { children: [
5421
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger$1, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
4949
5422
  /* @__PURE__ */ jsxRuntimeExports.jsx(
4950
5423
  "div",
4951
5424
  {
@@ -4963,7 +5436,7 @@ function LiveStatus({ hasError, errorMessage }) {
4963
5436
  }
4964
5437
  )
4965
5438
  ] }) }),
4966
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: hasError ? `Error updating data: ${errorMessage}` : `Content updates every ${LIVE_UPDATE_INTERVAL_MS$1 / 1e3} seconds` }) })
5439
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: hasError ? `Error updating data: ${errorMessage}` : `Content updates every ${LIVE_UPDATE_INTERVAL_MS$1 / 1e3} seconds` }) })
4967
5440
  ] }) });
4968
5441
  }
4969
5442
  ({
@@ -4971,7 +5444,6 @@ function LiveStatus({ hasError, errorMessage }) {
4971
5444
  });
4972
5445
  function RunDetailView({
4973
5446
  runId,
4974
- // TODO: This should open the right sidebar within the trace viewer
4975
5447
  selectedId: _selectedId
4976
5448
  }) {
4977
5449
  var _a;
@@ -5049,29 +5521,16 @@ function RunDetailView({
5049
5521
  if (error2) {
5050
5522
  throw error2;
5051
5523
  }
5052
- const fullEvent = encryptionKeyRef.current ? await hydrateResourceIOWithKey(result, encryptionKeyRef.current) : hydrateResourceIO(result);
5053
- if ("eventData" in fullEvent) {
5054
- return fullEvent.eventData;
5055
- }
5056
- return null;
5057
- },
5058
- [env]
5059
- );
5060
- const handleLoadSidebarEventData = reactExports.useCallback(
5061
- async (_correlationId, eventId) => {
5062
- const { error: error2, result } = await unwrapServerActionResult(
5063
- fetchEvent(env, runId, eventId, "all")
5524
+ const fullEvent = await hydrateResourceIOAsync(
5525
+ result,
5526
+ encryptionKeyRef.current ?? void 0
5064
5527
  );
5065
- if (error2) {
5066
- throw error2;
5067
- }
5068
- const fullEvent = encryptionKeyRef.current ? await hydrateResourceIOWithKey(result, encryptionKeyRef.current) : hydrateResourceIO(result);
5069
5528
  if ("eventData" in fullEvent) {
5070
5529
  return fullEvent.eventData;
5071
5530
  }
5072
5531
  return null;
5073
5532
  },
5074
- [env, runId]
5533
+ [env]
5075
5534
  );
5076
5535
  serverConfig.backendId === "local" || serverConfig.backendId === "@workflow/world-local";
5077
5536
  const {
@@ -5080,7 +5539,10 @@ function RunDetailView({
5080
5539
  loading,
5081
5540
  error,
5082
5541
  update,
5083
- hasEncryptedData
5542
+ hasEncryptedData,
5543
+ loadMoreTraceData,
5544
+ hasMoreTraceData,
5545
+ isLoadingMoreTraceData
5084
5546
  } = useWorkflowTraceViewerData(env, runId, { live: true });
5085
5547
  const run = runData ?? {};
5086
5548
  const [encryptionKey, setEncryptionKey] = reactExports.useState(null);
@@ -5091,36 +5553,22 @@ function RunDetailView({
5091
5553
  loading: eventsListLoading,
5092
5554
  hasMore: hasMoreEventsTab,
5093
5555
  loadingMore: loadingMoreEventsTab,
5094
- loadMore: loadMoreEventsTab
5556
+ loadMore: loadMoreEventsTab,
5557
+ searchByExactId
5095
5558
  } = useEventsListData(env, runId, {
5096
5559
  sortOrder: eventsSortOrder,
5097
5560
  encryptionKey: encryptionKey ?? void 0,
5098
5561
  enabled: activeTab === "events"
5099
5562
  });
5100
- const [spanSelection, setSpanSelection] = reactExports.useState(
5101
- null
5102
- );
5103
- const {
5104
- data: spanDetailData,
5105
- loading: spanDetailLoading,
5106
- error: spanDetailError,
5107
- refresh: refreshSpanDetail
5108
- } = useWorkflowResourceData(
5109
- env,
5110
- (spanSelection == null ? void 0 : spanSelection.resource) ?? "run",
5111
- (spanSelection == null ? void 0 : spanSelection.resourceId) ?? "",
5112
- {
5113
- runId: spanSelection == null ? void 0 : spanSelection.runId,
5114
- enabled: Boolean(
5115
- (spanSelection == null ? void 0 : spanSelection.resource) && (spanSelection == null ? void 0 : spanSelection.resourceId) && spanSelection.resource !== "hook"
5116
- ),
5563
+ const fetchSpanDetail = reactExports.useCallback(
5564
+ (selection) => fetchSpanDetailResource(env, selection, {
5117
5565
  encryptionKey: encryptionKey ?? void 0
5118
- }
5566
+ }),
5567
+ [env, encryptionKey]
5119
5568
  );
5120
5569
  const [isDecrypting, setIsDecrypting] = reactExports.useState(false);
5121
5570
  const handleDecrypt = reactExports.useCallback(async () => {
5122
5571
  if (encryptionKey) {
5123
- refreshSpanDetail();
5124
5572
  return;
5125
5573
  }
5126
5574
  setIsDecrypting(true);
@@ -5135,26 +5583,19 @@ function RunDetailView({
5135
5583
  return;
5136
5584
  }
5137
5585
  setEncryptionKey(keyResult);
5138
- toast.success("Run data decrypted successfully");
5139
5586
  } finally {
5140
5587
  setIsDecrypting(false);
5141
5588
  }
5142
- }, [encryptionKey, env, runId, refreshSpanDetail]);
5143
- const handleSpanSelect = reactExports.useCallback((info) => {
5144
- setSpanSelection(info);
5145
- }, []);
5589
+ }, [encryptionKey, env, runId]);
5146
5590
  const sidebarData = reactExports.useMemo(
5147
5591
  () => ({
5148
5592
  run,
5149
5593
  events: allEvents ?? [],
5150
- spanDetailData: spanDetailData ?? null,
5151
- spanDetailError,
5152
- spanDetailLoading,
5153
- onSpanSelect: handleSpanSelect,
5594
+ fetchSpanDetail,
5154
5595
  onStreamClick: handleStreamClick,
5155
5596
  onRunClick: handleRunRefClick,
5156
5597
  onWakeUpSleep: handleWakeUpSleep,
5157
- onLoadEventData: handleLoadSidebarEventData,
5598
+ onLoadEventData: handleLoadEventData,
5158
5599
  onResolveHook: handleResolveHook,
5159
5600
  encryptionKey: encryptionKey ?? void 0,
5160
5601
  onDecrypt: handleDecrypt,
@@ -5164,14 +5605,11 @@ function RunDetailView({
5164
5605
  [
5165
5606
  run,
5166
5607
  allEvents,
5167
- spanDetailData,
5168
- spanDetailError,
5169
- spanDetailLoading,
5170
- handleSpanSelect,
5608
+ fetchSpanDetail,
5171
5609
  handleStreamClick,
5172
5610
  handleRunRefClick,
5173
5611
  handleWakeUpSleep,
5174
- handleLoadSidebarEventData,
5612
+ handleLoadEventData,
5175
5613
  handleResolveHook,
5176
5614
  encryptionKey,
5177
5615
  handleDecrypt,
@@ -5187,6 +5625,7 @@ function RunDetailView({
5187
5625
  const {
5188
5626
  chunks: streamChunks,
5189
5627
  isLive: streamIsLive,
5628
+ isInitialLoading: streamIsInitialLoading,
5190
5629
  error: streamError
5191
5630
  } = useStreamReader(env, selectedStreamId, runId, encryptionKey, run.status);
5192
5631
  const handleCancelClick = () => {
@@ -5199,7 +5638,7 @@ function RunDetailView({
5199
5638
  setShowCancelDialog(false);
5200
5639
  await cancelRun(env, runId);
5201
5640
  await update();
5202
- toast.success("Run cancelled successfully");
5641
+ toast.success("Run cancelled");
5203
5642
  } catch (err) {
5204
5643
  console.error("Failed to cancel run:", err);
5205
5644
  toast.error("Failed to cancel run", {
@@ -5218,7 +5657,7 @@ function RunDetailView({
5218
5657
  setRerunning(true);
5219
5658
  setShowRerunDialog(false);
5220
5659
  const newRunId = await recreateRun(env, run.runId);
5221
- toast.success("New run started successfully", {
5660
+ toast.success("New run started", {
5222
5661
  description: `Run ID: ${newRunId}`
5223
5662
  });
5224
5663
  navigate(`/run/${newRunId}`);
@@ -5235,8 +5674,8 @@ function RunDetailView({
5235
5674
  if (error && !runData) {
5236
5675
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Alert, { variant: "destructive", className: "m-4", children: [
5237
5676
  /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4" }),
5238
- /* @__PURE__ */ jsxRuntimeExports.jsx(AlertTitle, { children: "Error loading workflow run" }),
5239
- /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDescription, { children: error.message })
5677
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AlertTitle, { children: getErrorTitle(error, "Error loading workflow run") }),
5678
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDescription, { children: getErrorMessage(error) })
5240
5679
  ] });
5241
5680
  }
5242
5681
  const workflowName = (_a = parseWorkflowName(run.workflowName)) == null ? void 0 : _a.shortName;
@@ -5336,12 +5775,12 @@ function RunDetailView({
5336
5775
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs", children: run.runId ? run.completedAt ? /* @__PURE__ */ jsxRuntimeExports.jsx(RelativeTime, { date: run.completedAt }) : "-" : /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton$1, { className: "w-[110px] h-[20px]" }) })
5337
5776
  ] }),
5338
5777
  run.expiredAt != null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
5339
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
5340
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground cursor-help flex items-center gap-1", children: [
5778
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip$1, { children: [
5779
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger$1, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground cursor-help flex items-center gap-1", children: [
5341
5780
  "Expired",
5342
5781
  /* @__PURE__ */ jsxRuntimeExports.jsx(CircleQuestionMark, { className: "w-3 h-3" })
5343
5782
  ] }) }),
5344
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "The storage data for this run has expired and is no longer available." }) })
5783
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "The storage data for this run has expired and is no longer available." }) })
5345
5784
  ] }),
5346
5785
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(RelativeTime, { date: run.expiredAt }) })
5347
5786
  ] })
@@ -5370,12 +5809,16 @@ function RunDetailView({
5370
5809
  "Streams"
5371
5810
  ] })
5372
5811
  ] }),
5373
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "trace", className: "mt-0 flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { title: "Failed to load trace viewer", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full -mx-6 bg-background-100 border-t border-gray-alpha-400 overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5374
- NewTraceViewer,
5812
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "trace", className: "mt-0 flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { title: "Failed to load trace viewer", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative h-full -mx-6 bg-background-100 border-t border-gray-alpha-400 overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5813
+ TraceViewer,
5375
5814
  {
5376
5815
  run,
5377
5816
  events: allEvents ?? [],
5378
- sidebarData
5817
+ loading,
5818
+ sidebarData,
5819
+ onLoadMore: loadMoreTraceData,
5820
+ hasMore: hasMoreTraceData,
5821
+ isLoadingMore: isLoadingMoreTraceData
5379
5822
  }
5380
5823
  ) }) }) }),
5381
5824
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "events", className: "mt-0 flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { title: "Failed to load events list", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -5393,118 +5836,58 @@ function RunDetailView({
5393
5836
  onSortOrderChange: setEventsSortOrder,
5394
5837
  onDecrypt: handleDecrypt,
5395
5838
  isDecrypting,
5396
- hasEncryptedData
5839
+ hasEncryptedData,
5840
+ onExactIdSearch: searchByExactId
5397
5841
  }
5398
5842
  ) }) }) }),
5399
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "streams", className: "mt-0 flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { title: "Failed to load stream data", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex gap-4", children: [
5400
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
5401
- "div",
5402
- {
5403
- className: "w-64 flex-shrink-0 border rounded-lg overflow-hidden",
5404
- style: {
5405
- borderColor: "var(--ds-gray-300)",
5406
- backgroundColor: "var(--ds-background-100)"
5843
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "streams", className: "mt-0 flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { title: "Failed to load stream data", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative -mx-6 flex h-full min-h-0 overflow-hidden border-t border-gray-alpha-400 bg-background-100", children: [
5844
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex w-[340px] shrink-0 flex-col border-r border-gray-alpha-400 bg-background-100", children: [
5845
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5846
+ "div",
5847
+ {
5848
+ "aria-hidden": "true",
5849
+ className: "h-10 min-h-10 border-b border-gray-alpha-400"
5850
+ }
5851
+ ),
5852
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-h-0 flex-1 divide-y divide-gray-400 overflow-y-auto", children: streamsLoading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col divide-y divide-gray-400", children: [
5853
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-10 items-center pl-4 pr-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton$1, { className: "h-3.5 w-24" }) }),
5854
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-10 items-center pl-4 pr-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton$1, { className: "h-3.5 w-32" }) }),
5855
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-10 items-center pl-4 pr-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton$1, { className: "h-3.5 w-20" }) })
5856
+ ] }) : streamsError ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-2 text-label-12 text-red-900", children: streamsError.message }) : streams.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-10 items-center px-4 text-label-12 text-gray-900", children: "No streams found for this run" }) : streams.map((streamId) => /* @__PURE__ */ jsxRuntimeExports.jsx(
5857
+ "button",
5858
+ {
5859
+ type: "button",
5860
+ onClick: () => setSelectedStreamId(streamId),
5861
+ className: `flex h-10 w-full min-w-0 items-center truncate pl-4 pr-2 text-left text-label-14 transition-colors ${selectedStreamId === streamId ? "bg-gray-100 text-gray-1000 hover:bg-gray-200 active:bg-gray-200" : "bg-transparent text-gray-900 hover:bg-gray-100 hover:text-gray-1000 focus-visible:bg-gray-100 focus-visible:text-gray-1000"}`,
5862
+ title: streamId,
5863
+ children: streamId
5407
5864
  },
5408
- children: [
5409
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
5410
- "div",
5411
- {
5412
- className: "px-3 py-2 border-b text-xs font-medium",
5413
- style: {
5414
- borderColor: "var(--ds-gray-300)",
5415
- color: "var(--ds-gray-900)"
5416
- },
5417
- children: [
5418
- "Streams (",
5419
- streams.length,
5420
- ")"
5421
- ]
5422
- }
5423
- ),
5424
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-auto max-h-[calc(100vh-400px)]", children: streamsLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }) : streamsError ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 text-xs text-destructive", children: streamsError.message }) : streams.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(
5425
- "div",
5426
- {
5427
- className: "p-4 text-xs",
5428
- style: { color: "var(--ds-gray-600)" },
5429
- children: "No streams found for this run"
5430
- }
5431
- ) : streams.map((streamId) => /* @__PURE__ */ jsxRuntimeExports.jsx(
5432
- "button",
5433
- {
5434
- type: "button",
5435
- onClick: () => setSelectedStreamId(streamId),
5436
- className: "w-full text-left px-3 py-2 text-xs font-mono truncate hover:bg-accent transition-colors",
5437
- style: {
5438
- backgroundColor: selectedStreamId === streamId ? "var(--ds-gray-200)" : "transparent",
5439
- color: "var(--ds-gray-1000)"
5440
- },
5441
- title: streamId,
5442
- children: streamId
5443
- },
5444
- streamId
5445
- )) })
5446
- ]
5447
- }
5448
- ),
5449
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-w-0", children: selectedStreamId ? (streamError == null ? void 0 : streamError.includes("encrypted")) && !encryptionKey ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
5450
- "div",
5451
- {
5452
- className: "h-full flex flex-col items-center justify-center gap-3 rounded-lg border p-4",
5453
- style: {
5454
- borderColor: "var(--ds-gray-300)",
5455
- backgroundColor: "var(--ds-gray-100)"
5456
- },
5457
- children: [
5458
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5459
- Lock,
5460
- {
5461
- className: "h-6 w-6",
5462
- style: { color: "var(--ds-gray-700)" }
5463
- }
5464
- ),
5465
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5466
- "div",
5467
- {
5468
- className: "text-sm",
5469
- style: { color: "var(--ds-gray-900)" },
5470
- children: "This stream is encrypted."
5471
- }
5472
- ),
5473
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5474
- DecryptButton,
5475
- {
5476
- onClick: handleDecrypt,
5477
- loading: isDecrypting
5478
- }
5479
- )
5480
- ]
5481
- }
5482
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
5865
+ streamId
5866
+ )) })
5867
+ ] }),
5868
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex min-w-0 flex-1 flex-col overflow-hidden bg-background-100", children: streamsLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(StreamViewerSkeleton, {}) }) : selectedStreamId ? (streamError == null ? void 0 : streamError.includes("encrypted")) && !encryptionKey ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full items-center justify-center p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-3 text-center", children: [
5869
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Lock, { className: "h-8 w-8 text-gray-700" }),
5870
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
5871
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-label-14 font-medium text-gray-1000", children: "Encrypted Stream" }),
5872
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-label-12 text-gray-900", children: "Decrypt this stream to view its chunks." })
5873
+ ] }),
5874
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5875
+ DecryptButton,
5876
+ {
5877
+ onClick: handleDecrypt,
5878
+ loading: isDecrypting
5879
+ }
5880
+ )
5881
+ ] }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5483
5882
  StreamViewer,
5484
5883
  {
5485
5884
  streamId: selectedStreamId,
5486
5885
  chunks: streamChunks,
5487
5886
  isLive: streamIsLive,
5887
+ isLoading: streamIsInitialLoading,
5488
5888
  error: (streamError == null ? void 0 : streamError.includes("encrypted")) ? null : streamError
5489
5889
  }
5490
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
5491
- "div",
5492
- {
5493
- className: "h-full flex items-center justify-center rounded-lg border",
5494
- style: {
5495
- borderColor: "var(--ds-gray-300)",
5496
- backgroundColor: "var(--ds-gray-100)"
5497
- },
5498
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5499
- "div",
5500
- {
5501
- className: "text-sm",
5502
- style: { color: "var(--ds-gray-600)" },
5503
- children: streams.length > 0 ? "Select a stream to view its data" : "No streams available"
5504
- }
5505
- )
5506
- }
5507
- ) })
5890
+ ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-label-14 text-gray-600", children: streams.length > 0 ? "Select a stream to view its data" : "No streams available" }) }) })
5508
5891
  ] }) }) }),
5509
5892
  false
5510
5893
  ]