@workflow/web 5.0.0-beta.3 → 5.0.0-beta.31

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 (38) hide show
  1. package/build/client/assets/arrow-up-right-UTJqZa58.js +15 -0
  2. package/build/client/assets/encryption-CLVP4klT.js +51 -0
  3. package/build/client/assets/{entry.client-BWsSsWQm.js → entry.client-Q4eUIqMI.js} +25 -25
  4. package/build/client/assets/{highlighted-body-B3W2YXNL-Be0uLJd8.js → highlighted-body-B3W2YXNL-BJU4jA-M.js} +2 -2
  5. package/build/client/assets/{home-Ce97yLJ7.js → home-1xisMAzt.js} +1704 -257
  6. package/build/client/assets/index-HqNj_XIX.js +200 -0
  7. package/build/client/assets/{index-DQa-BExo.js → index-Q81MFBxC.js} +26 -26
  8. package/build/client/assets/index-STTN0-2V.js +306 -0
  9. package/build/client/assets/manifest-37d4814f.js +1 -0
  10. package/build/client/assets/{mermaid-3ZIDBTTL-BSJR0c5A.js → mermaid-3ZIDBTTL-BxUZpfmG.js} +4914 -3226
  11. package/build/client/assets/mermaid-3ZIDBTTL-DDxQhcP4.css +1 -0
  12. package/build/client/assets/root-CQggqXow.css +1 -0
  13. package/build/client/assets/{root-DdOfCnSG.js → root-mjlqO83r.js} +5 -15
  14. package/build/client/assets/run-detail-CWoGxu_0.css +1 -0
  15. package/build/client/assets/{run-detail-JQ32aJru.js → run-detail-ysogMwZA.js} +1811 -1014
  16. package/build/client/assets/server-build-C5ehvmav.css +1 -0
  17. package/build/client/assets/{workflow-graph-viewer-DYhMo9jl.js → workflow-graph-viewer-DpvUticH.js} +5093 -5076
  18. package/build/client/assets/zstd-B5R2yJjB.wasm +0 -0
  19. package/build/client/assets/zstd-browser-decoder-BFS0fmpR.js +27 -0
  20. package/build/server/assets/{app-Bh_7yjpl.js → app-BgN_e9S7.js} +48 -44
  21. package/build/server/assets/{highlighted-body-B3W2YXNL-Cdb-ypB4.js → highlighted-body-B3W2YXNL-CsR2wqX2.js} +10 -7
  22. package/build/server/assets/index-BbpsBsGO.js +132 -0
  23. package/build/server/assets/index-BfLBLRRi.js +204 -0
  24. package/build/server/assets/index-DIXsjgfZ.js +1121 -0
  25. package/build/server/assets/{index-meg9qk8y.js → index-Dq5jTBN4.js} +4 -0
  26. package/build/server/assets/{mermaid-3ZIDBTTL-CE8Volk5.js → mermaid-3ZIDBTTL-B-ddv9lk.js} +10 -7
  27. package/build/server/assets/{server-build-DztRAoMd.js → server-build-CQLvkWL5.js} +55524 -47343
  28. package/build/server/assets/{token-DMEdoEUo.js → token-Batv2rdg.js} +6 -2
  29. package/build/server/assets/{token-util-DsMisyo3.js → token-util-C03rrkmJ.js} +6 -2
  30. package/build/server/assets/zstd-browser-decoder-vLGtEQW6.js +68 -0
  31. package/build/server/index.js +5 -1
  32. package/package.json +14 -11
  33. package/build/client/assets/encryption-80GMP4r0.js +0 -26
  34. package/build/client/assets/manifest-923d96a0.js +0 -1
  35. package/build/client/assets/mermaid-3ZIDBTTL-DKxHcEOp.css +0 -1
  36. package/build/client/assets/root-DXDFWibD.css +0 -1
  37. package/build/client/assets/server-build-pZ5ucXET.css +0 -1
  38. package/build/server/assets/index-CNvEtg5c.js +0 -82
@@ -1,26 +1,28 @@
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-DYhMo9jl.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 getHighResInMs, a8 as ActiveSpanProvider, a9 as useActiveSpan, aa as useSidebarDataOptional, ab as computeRootBounds, ac as buildTimeCompression, ad as Timeline, ae as TimelineHeader, af as SidebarDataProvider, ag as hydrateResourceIOWithKey, ah as hydrateResourceIO, ai as fetchHook, l as fetchEvents, F as fetchRun, aj as fetchStep, ak as hasEncryptedFields, al as fetchStreams, am as Slot, E as useComposedRefs, an as Slottable, d as cn$1, ao as buttonVariants, ap as isEncryptedData, aq as hydrateData, ar as getWebRevivers, T as Tooltip, a as TooltipTrigger, b as TooltipContent, as as LIVE_UPDATE_INTERVAL_MS$1, u as useServerConfig, at as fetchEvent, au as getEncryptionKeyForRun, t as toast, L as LoaderCircle, av as StreamViewer } from "./mermaid-3ZIDBTTL-BSJR0c5A.js";
4
- import { decrypt, importKey } from "./encryption-80GMP4r0.js";
1
+ import { r as reactExports, j as jsxRuntimeExports, p as reactDomExports, G as ReactDOM, d as useNavigate, e as useSearchParams, c as Link, w as withComponentProps, H as useParams } from "./index-Q81MFBxC.js";
2
+ import { C as Check, a as Copy, d as CircleAlert, U as parseStepName, G as parseWorkflowName, V as ErrorCard, Y as format, Z as Zap, S as Send, R as ResolveHookModal, t as ChevronUp, X, Q as ErrorBoundary, _ as Circle, u as unwrapOrThrow, W as WorkflowWebAPIError, K as unwrapServerActionResult, $ as useEscapeKeydown, w as composeEventHandlers, a0 as useCallbackRef, a1 as useLayoutEffect2, v as useControllableState, a2 as useId, a3 as createContext2, x as Presence, z as createContextScope, p as hideOthers, q as ReactRemoveScroll, a4 as useFocusGuards, a5 as LAYOUT, a6 as wakeUpRun, n as resumeHook, A as Alert, e as AlertTitle, g as getErrorTitle, h as AlertDescription, i as getErrorMessage, b as Skeleton$1, a7 as RunActionsButtons, H as StatusBadge, j as CopyableText, c as RelativeTime, T as Tabs, M as TabsList, N as TabsTrigger, O as TabsContent, B as cancelRun, a8 as recreateRun } from "./workflow-graph-viewer-DpvUticH.js";
3
+ import { c as createLucideIcon, W as cn, X as DataInspector, Y as DecryptClickContext, Z as Button, $ as Spinner, a0 as Lock, a1 as CollapsibleRoot, a2 as CollapsibleTrigger, a3 as CollapsibleContent, M as cva, a4 as RESERVED_ATTRIBUTE_KEY_PREFIX, a5 as ContextCardProvider, h as useToast, a6 as Skeleton, a7 as Yr, a8 as isEncryptedMarker, C as ChevronRight, a9 as TimestampTooltip, aa as formatDuration, ab as isHookLifecycleEventType, ac as isTerminalStepEventType, ad as isWaitEventType, ae as isStepEventType, af as _r, ag as RunClickContext, ah as StreamClickContext, ai as Collapsible, aj as isExpiredMarker, ak as isDoStreamStep, al as extractConversation, am as getEventDataRefFields, an as hasEncryptedFields, ao as useSidebarDataOptional, ap as isTerminalWorkflowRunStatus, aq as clsx, ar as useActiveSpan, as as useSidebarData, at as Tooltip, au as TooltipTrigger, av as IconButton, aw as TooltipContent, H as ChevronDown, ax as useRowWindow, ay as isSpanDimmedBySearch, az as ROW_HEIGHT_PX, aA as getSpanDurationMs, aB as isSpanErrored, aC as formatDurationPrecise, aD as TooltipProvider, aE as ActiveSpanProvider, aF as useReducedMotion, aG as searchSpans, aH as computeRootBounds, aI as computeTimeMarkers, aJ as clampViewportToRoot, aK as getHighResInMs, aL as scrollRowIntoView, aM as TIMELINE_PADDING_PX, aN as Minimap, aO as Timeline, aP as TimelineHeader, aQ as wheelZoomScaleFactor, aR as wheelDeltaToPixels, aS as SidebarDataProvider, aT as fetchHook, l as fetchEvents, aU as hydrateResourceIOAsync, J as fetchRun, aV as fetchStep, aW as hydrateResourceIO, aX as fetchStreams, aY as Slot, I as useComposedRefs, aZ as Slottable, e as cn$1, a_ as buttonVariants, a$ as fetchEvent, b0 as fetchEventsByCorrelationId, b1 as isEncryptedData, b2 as hydrateData, b3 as getWebRevivers, T as Tooltip$1, a as TooltipTrigger$1, b as TooltipContent$1, b4 as LIVE_UPDATE_INTERVAL_MS$1, u as useServerConfig, b5 as getEncryptionKeyForRun, t as toast, L as LoaderCircle, b6 as StreamViewer } from "./mermaid-3ZIDBTTL-BxUZpfmG.js";
4
+ import { A as ArrowUpRight } from "./arrow-up-right-UTJqZa58.js";
5
+ import { decrypt, importKey } from "./encryption-CLVP4klT.js";
6
+ import "./index-STTN0-2V.js";
5
7
  /**
6
8
  * @license lucide-react v0.575.0 - ISC
7
9
  *
8
10
  * This source code is licensed under the ISC license.
9
11
  * See the LICENSE file in the root directory of this source tree.
10
12
  */
11
- const __iconNode$2 = [
13
+ const __iconNode$5 = [
12
14
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
13
15
  ["path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3", key: "1u773s" }],
14
16
  ["path", { d: "M12 17h.01", key: "p32p05" }]
15
17
  ];
16
- const CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode$2);
18
+ const CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode$5);
17
19
  /**
18
20
  * @license lucide-react v0.575.0 - ISC
19
21
  *
20
22
  * This source code is licensed under the ISC license.
21
23
  * See the LICENSE file in the root directory of this source tree.
22
24
  */
23
- const __iconNode$1 = [
25
+ const __iconNode$4 = [
24
26
  ["path", { d: "M3 5h.01", key: "18ugdj" }],
25
27
  ["path", { d: "M3 12h.01", key: "nlz23k" }],
26
28
  ["path", { d: "M3 19h.01", key: "noohij" }],
@@ -28,52 +30,549 @@ const __iconNode$1 = [
28
30
  ["path", { d: "M8 12h13", key: "1za7za" }],
29
31
  ["path", { d: "M8 19h13", key: "m83p4d" }]
30
32
  ];
31
- const List = createLucideIcon("list", __iconNode$1);
33
+ const List = createLucideIcon("list", __iconNode$4);
32
34
  /**
33
35
  * @license lucide-react v0.575.0 - ISC
34
36
  *
35
37
  * This source code is licensed under the ISC license.
36
38
  * See the LICENSE file in the root directory of this source tree.
37
39
  */
38
- const __iconNode = [
40
+ const __iconNode$3 = [
41
+ ["path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8", key: "1357e3" }],
42
+ ["path", { d: "M3 3v5h5", key: "1xhq8a" }]
43
+ ];
44
+ const RotateCcw = createLucideIcon("rotate-ccw", __iconNode$3);
45
+ /**
46
+ * @license lucide-react v0.575.0 - ISC
47
+ *
48
+ * This source code is licensed under the ISC license.
49
+ * See the LICENSE file in the root directory of this source tree.
50
+ */
51
+ const __iconNode$2 = [
39
52
  ["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
40
53
  ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
41
54
  ];
42
- const Search = createLucideIcon("search", __iconNode);
55
+ const Search = createLucideIcon("search", __iconNode$2);
56
+ /**
57
+ * @license lucide-react v0.575.0 - ISC
58
+ *
59
+ * This source code is licensed under the ISC license.
60
+ * See the LICENSE file in the root directory of this source tree.
61
+ */
62
+ const __iconNode$1 = [
63
+ ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }],
64
+ ["line", { x1: "21", x2: "16.65", y1: "21", y2: "16.65", key: "13gj7c" }],
65
+ ["line", { x1: "11", x2: "11", y1: "8", y2: "14", key: "1vmskp" }],
66
+ ["line", { x1: "8", x2: "14", y1: "11", y2: "11", key: "durymu" }]
67
+ ];
68
+ const ZoomIn = createLucideIcon("zoom-in", __iconNode$1);
69
+ /**
70
+ * @license lucide-react v0.575.0 - ISC
71
+ *
72
+ * This source code is licensed under the ISC license.
73
+ * See the LICENSE file in the root directory of this source tree.
74
+ */
75
+ const __iconNode = [
76
+ ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }],
77
+ ["line", { x1: "21", x2: "16.65", y1: "21", y2: "16.65", key: "13gj7c" }],
78
+ ["line", { x1: "8", x2: "14", y1: "11", y2: "11", key: "durymu" }]
79
+ ];
80
+ const ZoomOut = createLucideIcon("zoom-out", __iconNode);
81
+ const WORKFLOW_ULID_BODY = "[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}";
82
+ const STEP_ID_PATTERN = new RegExp(`^step_(${WORKFLOW_ULID_BODY})$`, "i");
83
+ const WAIT_ID_PATTERN = new RegExp(`^wait_(${WORKFLOW_ULID_BODY})$`, "i");
84
+ const HOOK_ID_PATTERN = new RegExp(`^hook_(${WORKFLOW_ULID_BODY})$`, "i");
85
+ const EVENT_ID_PATTERN = new RegExp(`^evnt_(${WORKFLOW_ULID_BODY})$`, "i");
86
+ const WORKFLOW_ID_PREFIX_PATTERN = /^(step_|wait_|hook_|evnt_|wrun_)/i;
87
+ function matchPrefixedId(pattern, prefix, kind, query) {
88
+ const match = query.match(pattern);
89
+ if (!match) {
90
+ return null;
91
+ }
92
+ return { kind, id: `${prefix}_${match[1].toUpperCase()}` };
93
+ }
94
+ function parseExactWorkflowSearchId(query) {
95
+ const trimmed = query.trim();
96
+ if (!trimmed) {
97
+ return null;
98
+ }
99
+ 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);
100
+ }
101
+ function looksLikeWorkflowIdSearchInput(query) {
102
+ const trimmed = query.trim();
103
+ if (!WORKFLOW_ID_PREFIX_PATTERN.test(trimmed)) {
104
+ return false;
105
+ }
106
+ return /\d/.test(trimmed);
107
+ }
108
+ function CopyButton({ copyText, ariaLabel, className }) {
109
+ const [copied, setCopied] = reactExports.useState(false);
110
+ const timeoutRef = reactExports.useRef(null);
111
+ reactExports.useEffect(() => {
112
+ return () => {
113
+ if (timeoutRef.current) {
114
+ clearTimeout(timeoutRef.current);
115
+ }
116
+ };
117
+ }, []);
118
+ return jsxRuntimeExports.jsx("button", { type: "button", "aria-label": ariaLabel, className: cn("cursor-pointer text-gray-800 hover:text-gray-1000 bg-transparent border-0 p-1 m-0", className), onClick: (e) => {
119
+ e.stopPropagation();
120
+ if (timeoutRef.current) {
121
+ clearTimeout(timeoutRef.current);
122
+ }
123
+ void navigator.clipboard.writeText(copyText).then(() => {
124
+ setCopied(true);
125
+ timeoutRef.current = setTimeout(() => setCopied(false), 1e3);
126
+ });
127
+ }, 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" }) })] }) });
128
+ }
129
+ const ELLIPSIS = "…";
130
+ const MIN_START = 3;
131
+ const MIN_END = 3;
132
+ const MIN_KEPT = MIN_START + MIN_END;
133
+ const graphemeSegmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
134
+ function toGraphemes(text) {
135
+ if (graphemeSegmenter) {
136
+ return [...graphemeSegmenter.segment(text)].map((s) => s.segment);
137
+ }
138
+ return Array.from(text);
139
+ }
140
+ function buildCandidate(graphemes, kept) {
141
+ if (kept <= 0) {
142
+ return {
143
+ prefixText: "",
144
+ prefixGraphemeCount: 0,
145
+ suffixText: "",
146
+ suffixGraphemeCount: 0,
147
+ text: ELLIPSIS,
148
+ truncated: true
149
+ };
150
+ }
151
+ const suffixGraphemeCount = kept >= MIN_KEPT ? Math.max(MIN_END, Math.floor(kept / 2)) : Math.floor(kept / 2);
152
+ const prefixGraphemeCount = kept - suffixGraphemeCount;
153
+ const prefixText = graphemes.slice(0, prefixGraphemeCount).join("");
154
+ const suffixText = suffixGraphemeCount > 0 ? graphemes.slice(-suffixGraphemeCount).join("") : "";
155
+ return {
156
+ prefixText,
157
+ prefixGraphemeCount,
158
+ suffixText,
159
+ suffixGraphemeCount,
160
+ text: prefixText + ELLIPSIS + suffixText,
161
+ truncated: true
162
+ };
163
+ }
164
+ function middleTruncate(graphemes, availableWidth, measure, fullWidth) {
165
+ const fullText = graphemes.join("");
166
+ const resolvedFullWidth = fullWidth ?? measure(fullText);
167
+ if (availableWidth <= 0 || graphemes.length === 0) {
168
+ return {
169
+ prefixText: fullText,
170
+ prefixGraphemeCount: graphemes.length,
171
+ suffixText: "",
172
+ suffixGraphemeCount: 0,
173
+ text: fullText,
174
+ truncated: false
175
+ };
176
+ }
177
+ if (resolvedFullWidth <= availableWidth) {
178
+ return {
179
+ prefixText: fullText,
180
+ prefixGraphemeCount: graphemes.length,
181
+ suffixText: "",
182
+ suffixGraphemeCount: 0,
183
+ text: fullText,
184
+ truncated: false
185
+ };
186
+ }
187
+ let lo = 0;
188
+ let hi = graphemes.length - 1;
189
+ let best = -1;
190
+ while (lo <= hi) {
191
+ const mid = lo + hi >>> 1;
192
+ const candidate = buildCandidate(graphemes, mid);
193
+ if (measure(candidate.text) <= availableWidth) {
194
+ best = mid;
195
+ lo = mid + 1;
196
+ } else {
197
+ hi = mid - 1;
198
+ }
199
+ }
200
+ if (best === -1) {
201
+ return {
202
+ prefixText: "",
203
+ prefixGraphemeCount: 0,
204
+ suffixText: "",
205
+ suffixGraphemeCount: 0,
206
+ text: "",
207
+ truncated: true
208
+ };
209
+ }
210
+ return buildCandidate(graphemes, best);
211
+ }
212
+ function getMiddleTruncateCopyText({ prefixText, selectionEnd, selectionStart, suffixText, value }) {
213
+ const visibleText = prefixText + ELLIPSIS + suffixText;
214
+ if (selectionStart < 0 || selectionEnd > visibleText.length || selectionStart >= selectionEnd) {
215
+ return null;
216
+ }
217
+ if (selectionStart === 0 && selectionEnd === visibleText.length) {
218
+ return value;
219
+ }
220
+ const ellipsisStart = prefixText.length;
221
+ const ellipsisEnd = ellipsisStart + ELLIPSIS.length;
222
+ if (selectionStart > ellipsisStart || selectionEnd < ellipsisEnd) {
223
+ return null;
224
+ }
225
+ const originalGraphemes = toGraphemes(value);
226
+ const selectedPrefixGraphemeCount = toGraphemes(visibleText.slice(0, selectionStart)).length;
227
+ const selectedSuffixGraphemeCount = toGraphemes(visibleText.slice(ellipsisEnd, selectionEnd)).length;
228
+ const suffixStart = originalGraphemes.length - toGraphemes(suffixText).length;
229
+ return originalGraphemes.slice(selectedPrefixGraphemeCount, suffixStart + selectedSuffixGraphemeCount).join("");
230
+ }
231
+ function getMiddleTruncateCopyTextFromSelectionText({ prefixText, selectionText, suffixText, value }) {
232
+ const visibleText = prefixText + ELLIPSIS + suffixText;
233
+ const trimmedSelectionText = selectionText.trim();
234
+ if (!trimmedSelectionText) {
235
+ return null;
236
+ }
237
+ const leading = selectionText.slice(0, selectionText.length - selectionText.trimStart().length);
238
+ const trailing = selectionText.slice(selectionText.trimEnd().length);
239
+ if (trimmedSelectionText === visibleText) {
240
+ return leading + value + trailing;
241
+ }
242
+ if (!trimmedSelectionText.includes(ELLIPSIS)) {
243
+ return null;
244
+ }
245
+ const selectionStart = visibleText.indexOf(trimmedSelectionText);
246
+ if (selectionStart === -1) {
247
+ return null;
248
+ }
249
+ const selectionEnd = selectionStart + trimmedSelectionText.length;
250
+ const mappedText = getMiddleTruncateCopyText({
251
+ prefixText,
252
+ selectionEnd,
253
+ selectionStart,
254
+ suffixText,
255
+ value
256
+ });
257
+ return mappedText === null ? null : leading + mappedText + trailing;
258
+ }
259
+ const useIsomorphicLayoutEffect = typeof window === "undefined" ? reactExports.useEffect : reactExports.useLayoutEffect;
260
+ function createFullState(value, graphemes) {
261
+ return {
262
+ displayText: value,
263
+ isTruncated: false,
264
+ prefixGraphemeCount: graphemes.length,
265
+ prefixText: value,
266
+ suffixGraphemeCount: 0,
267
+ suffixText: ""
268
+ };
269
+ }
270
+ function useMiddleTruncate(value) {
271
+ const graphemes = reactExports.useMemo(() => toGraphemes(value), [value]);
272
+ const fullState = reactExports.useMemo(() => createFullState(value, graphemes), [graphemes, value]);
273
+ const ref = reactExports.useRef(null);
274
+ const measureRef = reactExports.useRef(null);
275
+ const [state, setState] = reactExports.useState(() => fullState);
276
+ const rafRef = reactExports.useRef(0);
277
+ const updateState = reactExports.useCallback((nextState) => {
278
+ setState((currentState) => {
279
+ 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) {
280
+ return currentState;
281
+ }
282
+ return nextState;
283
+ });
284
+ }, []);
285
+ const recalculate = reactExports.useCallback(() => {
286
+ const el = ref.current;
287
+ const measureEl = measureRef.current;
288
+ if (!el || !measureEl)
289
+ return;
290
+ const available = el.clientWidth;
291
+ if (available <= 0) {
292
+ updateState(fullState);
293
+ return;
294
+ }
295
+ const measure = (text) => {
296
+ measureEl.textContent = text;
297
+ return measureEl.scrollWidth;
298
+ };
299
+ const fullWidth = measure(value);
300
+ if (fullWidth <= available) {
301
+ updateState(fullState);
302
+ return;
303
+ }
304
+ const result = middleTruncate(graphemes, available, measure, fullWidth);
305
+ updateState({
306
+ displayText: result.text,
307
+ isTruncated: result.truncated,
308
+ prefixGraphemeCount: result.prefixGraphemeCount,
309
+ prefixText: result.prefixText,
310
+ suffixGraphemeCount: result.suffixGraphemeCount,
311
+ suffixText: result.suffixText
312
+ });
313
+ }, [fullState, graphemes, updateState, value]);
314
+ useIsomorphicLayoutEffect(() => {
315
+ recalculate();
316
+ }, [recalculate]);
317
+ reactExports.useEffect(() => {
318
+ var _a;
319
+ const el = ref.current;
320
+ if (!el)
321
+ return;
322
+ const debouncedRecalc = () => {
323
+ cancelAnimationFrame(rafRef.current);
324
+ rafRef.current = requestAnimationFrame(recalculate);
325
+ };
326
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(debouncedRecalc) : null;
327
+ ro == null ? void 0 : ro.observe(el);
328
+ window.addEventListener("resize", debouncedRecalc);
329
+ const onFontsLoaded = () => {
330
+ debouncedRecalc();
331
+ };
332
+ const fontSet = "fonts" in document ? document.fonts : null;
333
+ (_a = fontSet == null ? void 0 : fontSet.addEventListener) == null ? void 0 : _a.call(fontSet, "loadingdone", onFontsLoaded);
334
+ return () => {
335
+ var _a2;
336
+ ro == null ? void 0 : ro.disconnect();
337
+ window.removeEventListener("resize", debouncedRecalc);
338
+ cancelAnimationFrame(rafRef.current);
339
+ (_a2 = fontSet == null ? void 0 : fontSet.removeEventListener) == null ? void 0 : _a2.call(fontSet, "loadingdone", onFontsLoaded);
340
+ };
341
+ }, [recalculate]);
342
+ return {
343
+ ref,
344
+ measureRef,
345
+ displayText: state.displayText,
346
+ isTruncated: state.isTruncated,
347
+ prefixGraphemeCount: state.prefixGraphemeCount,
348
+ prefixText: state.prefixText,
349
+ suffixGraphemeCount: state.suffixGraphemeCount,
350
+ suffixText: state.suffixText
351
+ };
352
+ }
353
+ function getRangeOffsets(container, range) {
354
+ if (!container.contains(range.startContainer) || !container.contains(range.endContainer)) {
355
+ return null;
356
+ }
357
+ const startRange = document.createRange();
358
+ startRange.selectNodeContents(container);
359
+ startRange.setEnd(range.startContainer, range.startOffset);
360
+ const endRange = document.createRange();
361
+ endRange.selectNodeContents(container);
362
+ endRange.setEnd(range.endContainer, range.endOffset);
363
+ return {
364
+ end: endRange.toString().length,
365
+ start: startRange.toString().length
366
+ };
367
+ }
368
+ function MiddleTruncate({ value, className, onCopy: onCopyProp, ...props }) {
369
+ const { ref, measureRef, displayText, isTruncated, prefixText, suffixText } = useMiddleTruncate(value);
370
+ const visibleRef = reactExports.useRef(null);
371
+ const handleCopy = reactExports.useCallback((e) => {
372
+ onCopyProp == null ? void 0 : onCopyProp(e);
373
+ if (e.defaultPrevented || !isTruncated)
374
+ return;
375
+ const selection = window.getSelection();
376
+ if (!selection || selection.rangeCount === 0)
377
+ return;
378
+ const selectionText = selection.toString();
379
+ if (!selectionText)
380
+ return;
381
+ const range = selection.getRangeAt(0);
382
+ const visibleEl = visibleRef.current;
383
+ let copyText = null;
384
+ if (e.currentTarget.contains(range.startContainer) && e.currentTarget.contains(range.endContainer) && visibleEl) {
385
+ const offsets = getRangeOffsets(visibleEl, range);
386
+ if (offsets) {
387
+ copyText = getMiddleTruncateCopyText({
388
+ prefixText,
389
+ selectionEnd: offsets.end,
390
+ selectionStart: offsets.start,
391
+ suffixText,
392
+ value
393
+ });
394
+ }
395
+ }
396
+ copyText ?? (copyText = getMiddleTruncateCopyTextFromSelectionText({
397
+ prefixText,
398
+ selectionText,
399
+ suffixText,
400
+ value
401
+ }));
402
+ if (copyText === null)
403
+ return;
404
+ e.preventDefault();
405
+ e.clipboardData.setData("text/plain", copyText);
406
+ }, [onCopyProp, isTruncated, prefixText, suffixText, value]);
407
+ 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 })] });
408
+ }
409
+ const encryptedPlaceholderPreview = `{
410
+ "input": "[encrypted]",
411
+ "result": "[encrypted]"
412
+ }`;
413
+ function EncryptedDataBlock() {
414
+ const ctx = reactExports.useContext(DecryptClickContext);
415
+ return jsxRuntimeExports.jsxs("div", { className: "relative min-h-20 overflow-hidden rounded-md border border-gray-alpha-400 bg-background-100", children: [jsxRuntimeExports.jsx("pre", { "aria-hidden": "true", className: "pointer-events-none m-0 select-none p-3 font-mono text-label-12 text-gray-900 blur-[4px]", children: encryptedPlaceholderPreview }), jsxRuntimeExports.jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: ctx ? jsxRuntimeExports.jsxs(Button, { onClick: ctx.onDecrypt, disabled: ctx.isDecrypting, size: "xs", children: [ctx.isDecrypting ? jsxRuntimeExports.jsx(Spinner, { size: 10 }) : jsxRuntimeExports.jsx(Lock, { className: "h-3 w-3" }), jsxRuntimeExports.jsx("span", { children: "Decrypt" })] }) : jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 rounded border border-gray-alpha-400 bg-gray-100 px-1.5 py-0.5 text-button-12 font-medium text-gray-700", children: [jsxRuntimeExports.jsx(Lock, { className: "h-3 w-3" }), "Encrypted"] }) })] });
416
+ }
417
+ const serializeForClipboard = (value) => {
418
+ if (typeof value === "string")
419
+ return value;
420
+ if (typeof value === "number" || typeof value === "boolean" || value === null) {
421
+ return String(value);
422
+ }
423
+ try {
424
+ return JSON.stringify(value, null, 2);
425
+ } catch {
426
+ return String(value);
427
+ }
428
+ };
429
+ function CopyableDataBlock({ data }) {
430
+ return jsxRuntimeExports.jsxs("div", { className: "relative overflow-x-auto rounded-md border border-gray-alpha-400 p-3", children: [jsxRuntimeExports.jsx(CopyButton, { copyText: serializeForClipboard(data), ariaLabel: "Copy data", className: "absolute right-2 top-2 z-10 flex h-6 w-6 items-center justify-center rounded-md border border-gray-alpha-400 !bg-background-100 p-0 text-gray-900 transition-transform transition-colors duration-100 hover:bg-gray-200 active:scale-95 active:bg-gray-300" }), jsxRuntimeExports.jsx(DataInspector, { data })] });
431
+ }
432
+ function isReservedAttributeKey(key) {
433
+ return key.startsWith(RESERVED_ATTRIBUTE_KEY_PREFIX);
434
+ }
435
+ const rowValueVariants = cva("min-w-0 flex-1 truncate text-right text-copy-13 text-gray-1000", {
436
+ variants: {
437
+ variant: {
438
+ default: "",
439
+ mono: "font-mono"
440
+ }
441
+ },
442
+ defaultVariants: {
443
+ variant: "default"
444
+ }
445
+ });
446
+ const rowCopyValueVariants = cva("flex min-w-0 items-center justify-end gap-1 text-copy-13 text-gray-1000", {
447
+ variants: {
448
+ variant: {
449
+ default: "",
450
+ mono: "font-mono"
451
+ }
452
+ },
453
+ defaultVariants: {
454
+ variant: "default"
455
+ }
456
+ });
457
+ function DetailKeyValueRowBase({ label, value, copyText, href, removed = false, variant }) {
458
+ const stringValue = typeof value === "string" ? value : void 0;
459
+ 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 })] });
460
+ }
461
+ function DetailKeyValueRow(props) {
462
+ return jsxRuntimeExports.jsx(DetailKeyValueRowBase, { ...props });
463
+ }
464
+ function DetailMonoKeyValueRow(props) {
465
+ return jsxRuntimeExports.jsx(DetailKeyValueRowBase, { ...props, variant: "mono" });
466
+ }
467
+ function AttributeRow({ attributeKey, value, removed = false }) {
468
+ return jsxRuntimeExports.jsx(DetailKeyValueRow, { label: attributeKey, value, removed });
469
+ }
470
+ function sortAttributeKeys(keys) {
471
+ return [...keys].sort((a, b) => {
472
+ const aReserved = isReservedAttributeKey(a);
473
+ const bReserved = isReservedAttributeKey(b);
474
+ if (aReserved !== bReserved)
475
+ return aReserved ? 1 : -1;
476
+ return a.localeCompare(b);
477
+ });
478
+ }
479
+ function RunAttributesCard({ attributes }) {
480
+ const keys = sortAttributeKeys(Object.keys(attributes));
481
+ if (keys.length === 0)
482
+ return null;
483
+ 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)) }) })] });
484
+ }
485
+ function formatMetadataValue(value) {
486
+ if (value == null) {
487
+ return String(value);
488
+ }
489
+ if (typeof value === "string") {
490
+ return value;
491
+ }
492
+ if (typeof value === "number" || typeof value === "boolean") {
493
+ return String(value);
494
+ }
495
+ try {
496
+ return JSON.stringify(value);
497
+ } catch {
498
+ return String(value);
499
+ }
500
+ }
501
+ function isMetadataRecord(value) {
502
+ return value !== null && typeof value === "object" && !Array.isArray(value);
503
+ }
504
+ function RunMetadataCard({ metadata }) {
505
+ if (!isMetadataRecord(metadata)) {
506
+ return jsxRuntimeExports.jsxs(CollapsibleRoot, { defaultOpen: true, children: [jsxRuntimeExports.jsx(CollapsibleTrigger, { children: "Metadata" }), jsxRuntimeExports.jsx(CollapsibleContent, { className: "mb-4", children: jsxRuntimeExports.jsx(CopyableDataBlock, { data: metadata }) })] });
507
+ }
508
+ const keys = Object.keys(metadata).sort((a, b) => a.localeCompare(b));
509
+ if (keys.length === 0)
510
+ return null;
511
+ 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)) }) })] });
512
+ }
513
+ function isAttrSetEventData(data) {
514
+ if (data === null || typeof data !== "object")
515
+ return false;
516
+ const record = data;
517
+ return Array.isArray(record.changes) && record.changes.every((c) => c !== null && typeof c === "object" && typeof c.key === "string");
518
+ }
519
+ function describeWriter(writer) {
520
+ if (!writer)
521
+ return null;
522
+ if (writer.type === "workflow")
523
+ return "Set by workflow";
524
+ if (writer.type === "step") {
525
+ return `Set by step ${writer.stepId} (attempt ${writer.attempt})`;
526
+ }
527
+ return null;
528
+ }
529
+ function AttrSetEventBlock({ data }) {
530
+ if (!isAttrSetEventData(data)) {
531
+ return jsxRuntimeExports.jsx(CopyableDataBlock, { data });
532
+ }
533
+ const writerLabel = describeWriter(data.writer);
534
+ 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: {
535
+ borderColor: "var(--ds-gray-alpha-400)",
536
+ color: "var(--ds-gray-700)"
537
+ }, children: writerLabel })] });
538
+ }
43
539
  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}`;
44
540
  function DecryptButton({ decrypted = false, loading = false, onClick }) {
45
541
  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"] })] });
46
542
  }
47
- function isStructuredErrorWithStack(value) {
48
- return value != null && typeof value === "object" && "stack" in value && typeof value.stack === "string";
543
+ function isStructuredError(value) {
544
+ return value != null && typeof value === "object" && (typeof value.message === "string" || typeof value.stack === "string");
545
+ }
546
+ function deriveTitle(message) {
547
+ const firstLine = message.split("\n").find((line) => line.trim().length > 0) ?? message;
548
+ return firstLine.trim();
49
549
  }
50
550
  function ErrorStackBlock({ value }) {
51
- const toast2 = useToast();
52
- const stack = value.stack;
551
+ const stack = typeof value.stack === "string" ? value.stack : void 0;
53
552
  const message = typeof value.message === "string" ? value.message : void 0;
54
- const copyText = message ? `${message}
553
+ const body = stack ?? message ?? "";
554
+ const title = message ? deriveTitle(message) : deriveTitle(body);
555
+ const copyText = message && stack && !stack.includes(message) ? `${message}
55
556
 
56
- ${stack}` : stack;
557
+ ${stack}` : body;
57
558
  return jsxRuntimeExports.jsxs("div", { className: "relative overflow-hidden rounded-md border", style: {
58
559
  borderColor: "var(--ds-red-400)",
59
560
  background: "var(--ds-red-100)"
60
- }, 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: {
61
- borderColor: "var(--ds-red-400)",
62
- background: "var(--ds-red-100)",
63
- color: "var(--ds-red-900)"
64
- }, onClick: () => {
65
- navigator.clipboard.writeText(copyText).then(() => {
66
- toast2.success("Copied to clipboard");
67
- }).catch(() => {
68
- toast2.error("Failed to copy");
69
- });
70
- }, children: jsxRuntimeExports.jsx(Copy, { size: 12 }) }), message && jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2 px-3 py-2.5 pr-10", style: {
561
+ }, 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: {
71
562
  color: "var(--ds-red-900)",
72
563
  borderBottom: "1px solid var(--ds-red-400)"
73
- }, 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: {
564
+ }, children: [jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4 shrink-0" }), jsxRuntimeExports.jsx("p", {
565
+ className: "text-xs font-semibold m-0 truncate",
566
+ // The full message or stack is in the body below; the header just
567
+ // shows the first line, single-line, with overflow
568
+ // ellipsised so a long title doesn't push the copy button or
569
+ // wrap into the framed hint/docs lines.
570
+ title: message,
571
+ children: title
572
+ })] }), body && jsxRuntimeExports.jsx("pre", { className: "px-3 py-2.5 text-xs font-mono whitespace-pre-wrap break-words overflow-auto m-0", style: {
74
573
  color: "var(--ds-red-900)",
75
574
  background: "var(--ds-red-200)"
76
- }, children: stack })] });
575
+ }, children: body })] });
77
576
  }
78
577
  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)}`;
79
578
  function LoadMoreButton({ loading = false, onClick, label = "Load more", loadingLabel = "Loading..." }) {
@@ -118,190 +617,7 @@ function MenuDropdown({ options, value, onChange }) {
118
617
  setOpen(false);
119
618
  }, children: option.label }, option.value)) })] });
120
619
  }
121
- const TIME_UNITS = [
122
- { unit: "year", ms: 31536e6 },
123
- { unit: "month", ms: 2628e6 },
124
- { unit: "day", ms: 864e5 },
125
- { unit: "hour", ms: 36e5 },
126
- { unit: "minute", ms: 6e4 },
127
- { unit: "second", ms: 1e3 }
128
- ];
129
- function formatTimeDifference(diff) {
130
- let remaining = Math.abs(diff);
131
- const result = [];
132
- for (const { unit, ms } of TIME_UNITS) {
133
- const value = Math.floor(remaining / ms);
134
- if (value > 0 || result.length > 0) {
135
- result.push(`${value} ${unit}${value !== 1 ? "s" : ""}`);
136
- remaining %= ms;
137
- }
138
- if (result.length === 3)
139
- break;
140
- }
141
- return result.join(", ");
142
- }
143
- function useTimeAgo(date) {
144
- const [timeAgo, setTimeAgo] = reactExports.useState("");
145
- reactExports.useEffect(() => {
146
- const update = () => {
147
- const diff = Date.now() - date;
148
- const formatted = formatTimeDifference(diff);
149
- setTimeAgo(formatted ? `${formatted} ago` : "Just now");
150
- };
151
- update();
152
- const timer = setInterval(update, 1e3);
153
- return () => clearInterval(timer);
154
- }, [date]);
155
- return timeAgo;
156
- }
157
- function ZoneDateTimeRow({ date, zone }) {
158
- var _a;
159
- const dateObj = new Date(date);
160
- const formattedZone = ((_a = new Intl.DateTimeFormat("en-US", {
161
- timeZone: zone,
162
- timeZoneName: "short"
163
- }).formatToParts(dateObj).find((part) => part.type === "timeZoneName")) == null ? void 0 : _a.value) || zone;
164
- const formattedDate = dateObj.toLocaleString("en-US", {
165
- timeZone: zone,
166
- year: "numeric",
167
- month: "long",
168
- day: "numeric"
169
- });
170
- const formattedTime = dateObj.toLocaleTimeString("en-US", {
171
- timeZone: zone,
172
- hour: "2-digit",
173
- minute: "2-digit",
174
- second: "2-digit"
175
- });
176
- return jsxRuntimeExports.jsxs("div", { style: {
177
- display: "flex",
178
- alignItems: "center",
179
- justifyContent: "space-between",
180
- gap: 12
181
- }, children: [jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [jsxRuntimeExports.jsx("div", { style: {
182
- display: "inline-flex",
183
- alignItems: "center",
184
- justifyContent: "center",
185
- height: 16,
186
- padding: "0 6px",
187
- backgroundColor: "var(--ds-gray-200)",
188
- borderRadius: 3,
189
- fontSize: 11,
190
- fontFamily: "var(--font-mono, monospace)",
191
- fontWeight: 500,
192
- color: "var(--ds-gray-900)",
193
- whiteSpace: "nowrap"
194
- }, children: formattedZone }), jsxRuntimeExports.jsx("span", { style: {
195
- fontSize: 13,
196
- color: "var(--ds-gray-1000)",
197
- whiteSpace: "nowrap"
198
- }, children: formattedDate })] }), jsxRuntimeExports.jsx("span", { style: {
199
- fontSize: 11,
200
- fontFamily: "var(--font-mono, monospace)",
201
- fontVariantNumeric: "tabular-nums",
202
- color: "var(--ds-gray-900)",
203
- whiteSpace: "nowrap"
204
- }, children: formattedTime })] });
205
- }
206
- function TimestampTooltipContent({ date }) {
207
- const localTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
208
- const timeAgo = useTimeAgo(date);
209
- return jsxRuntimeExports.jsxs("div", { style: {
210
- display: "flex",
211
- flexDirection: "column",
212
- gap: 12,
213
- minWidth: 300,
214
- padding: "12px 14px"
215
- }, children: [jsxRuntimeExports.jsx("span", { style: {
216
- fontSize: 13,
217
- fontVariantNumeric: "tabular-nums",
218
- color: "var(--ds-gray-900)"
219
- }, 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 })] })] });
220
- }
221
- const TOOLTIP_WIDTH = 330;
222
- const VIEWPORT_PAD = 8;
223
- function TooltipPortal({ triggerRect, onMouseEnter, onMouseLeave, date }) {
224
- const tooltipRef = reactExports.useRef(null);
225
- const [style, setStyle] = reactExports.useState({
226
- position: "fixed",
227
- zIndex: 9999,
228
- visibility: "hidden"
229
- });
230
- reactExports.useEffect(() => {
231
- const placement = triggerRect.top > 240 ? "above" : "below";
232
- const centerX = triggerRect.left + triggerRect.width / 2;
233
- const el = tooltipRef.current;
234
- const w = el ? el.offsetWidth : TOOLTIP_WIDTH;
235
- const h = el ? el.offsetHeight : 100;
236
- let left = centerX - w / 2;
237
- left = Math.max(VIEWPORT_PAD, Math.min(left, window.innerWidth - w - VIEWPORT_PAD));
238
- let top;
239
- if (placement === "above") {
240
- top = triggerRect.top - h - 6;
241
- if (top < VIEWPORT_PAD) {
242
- top = triggerRect.bottom + 6;
243
- }
244
- } else {
245
- top = triggerRect.bottom + 6;
246
- if (top + h > window.innerHeight - VIEWPORT_PAD) {
247
- top = triggerRect.top - h - 6;
248
- }
249
- }
250
- setStyle({
251
- position: "fixed",
252
- left,
253
- top,
254
- zIndex: 9999,
255
- borderRadius: 10,
256
- border: "1px solid var(--ds-gray-alpha-200)",
257
- backgroundColor: "var(--ds-background-100)",
258
- boxShadow: "0 4px 12px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.06)",
259
- visibility: "visible"
260
- });
261
- }, [triggerRect]);
262
- return reactDomExports.createPortal(
263
- // biome-ignore lint/a11y/noStaticElementInteractions: tooltip hover zone
264
- jsxRuntimeExports.jsx("div", { ref: tooltipRef, onMouseEnter, onMouseLeave, style, children: jsxRuntimeExports.jsx(TimestampTooltipContent, { date }) }),
265
- document.body
266
- );
267
- }
268
- function TimestampTooltip({ date, children }) {
269
- const [open, setOpen] = reactExports.useState(false);
270
- const [triggerRect, setTriggerRect] = reactExports.useState(null);
271
- const triggerRef = reactExports.useRef(null);
272
- const closeTimer = reactExports.useRef(null);
273
- reactExports.useEffect(() => {
274
- return () => {
275
- if (closeTimer.current)
276
- clearTimeout(closeTimer.current);
277
- };
278
- }, []);
279
- const ts = date == null ? null : typeof date === "number" ? date : new Date(date).getTime();
280
- if (ts == null || Number.isNaN(ts))
281
- return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children });
282
- const cancelClose = () => {
283
- if (closeTimer.current) {
284
- clearTimeout(closeTimer.current);
285
- closeTimer.current = null;
286
- }
287
- };
288
- const scheduleClose = () => {
289
- cancelClose();
290
- closeTimer.current = setTimeout(() => setOpen(false), 120);
291
- };
292
- const handleOpen = () => {
293
- cancelClose();
294
- if (triggerRef.current) {
295
- setTriggerRect(triggerRef.current.getBoundingClientRect());
296
- }
297
- setOpen(true);
298
- };
299
- return (
300
- // biome-ignore lint/a11y/noStaticElementInteractions: tooltip trigger
301
- 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 })] })
302
- );
303
- }
304
- const ERROR_EVENT_TYPES$1 = /* @__PURE__ */ new Set([
620
+ const ERROR_EVENT_TYPES = /* @__PURE__ */ new Set([
305
621
  "step_failed",
306
622
  "step_retrying",
307
623
  "run_failed",
@@ -322,6 +638,18 @@ function formatEventTime(date) {
322
638
  hour12: false
323
639
  }) + "." + date.getMilliseconds().toString().padStart(3, "0");
324
640
  }
641
+ function parseEventDate(value) {
642
+ if (value == null)
643
+ return null;
644
+ const date = value instanceof Date ? value : new Date(String(value));
645
+ return Number.isNaN(date.getTime()) ? null : date;
646
+ }
647
+ function getEffectiveEventDate$1(event) {
648
+ return parseEventDate(event.occurredAt) ?? new Date(event.createdAt);
649
+ }
650
+ function getEffectiveEventTime(event) {
651
+ return getEffectiveEventDate$1(event).getTime();
652
+ }
325
653
  function formatEventType(eventType) {
326
654
  return eventType.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
327
655
  }
@@ -335,6 +663,9 @@ function getStatusDotColor(eventType) {
335
663
  if (eventType === "step_retrying") {
336
664
  return "var(--ds-amber-700)";
337
665
  }
666
+ if (eventType === "attr_set") {
667
+ return "var(--ds-teal-900)";
668
+ }
338
669
  if (eventType === "step_completed" || eventType === "run_completed" || eventType === "workflow_completed" || eventType === "hook_disposed" || eventType === "wait_completed") {
339
670
  return "var(--ds-green-700)";
340
671
  }
@@ -359,27 +690,34 @@ function buildNameMaps(events, run) {
359
690
  return { correlationNameMap, workflowName };
360
691
  }
361
692
  function buildDurationMap(events) {
693
+ const chronological = [...events].sort((a, b) => getEffectiveEventTime(a) - getEffectiveEventTime(b));
362
694
  const createdTimes = /* @__PURE__ */ new Map();
695
+ const firstStartedTimes = /* @__PURE__ */ new Map();
363
696
  const startedTimes = /* @__PURE__ */ new Map();
364
697
  const durations = /* @__PURE__ */ new Map();
365
- for (const event of events) {
366
- const ts = new Date(event.createdAt).getTime();
698
+ for (const event of chronological) {
699
+ const ts = getEffectiveEventTime(event);
367
700
  const key = event.correlationId ?? "__run__";
368
701
  const type = event.eventType;
369
702
  if (type === "step_created" || type === "run_created") {
370
- createdTimes.set(key, ts);
371
- }
372
- if (type === "step_started" || type === "run_started" || type === "workflow_started") {
373
- startedTimes.set(key, ts);
374
703
  if (!createdTimes.has(key)) {
375
704
  createdTimes.set(key, ts);
376
705
  }
377
- const createdAt = createdTimes.get(key);
378
- const info = durations.get(key) ?? {};
379
- if (createdAt !== void 0) {
380
- info.queued = ts - createdAt;
706
+ }
707
+ if (type === "step_started" || type === "run_started" || type === "workflow_started") {
708
+ startedTimes.set(key, ts);
709
+ if (!firstStartedTimes.has(key)) {
710
+ firstStartedTimes.set(key, ts);
711
+ if (!createdTimes.has(key)) {
712
+ createdTimes.set(key, ts);
713
+ }
714
+ const createdAt = createdTimes.get(key);
715
+ const info = durations.get(key) ?? {};
716
+ if (createdAt !== void 0) {
717
+ info.queued = ts - createdAt;
718
+ }
719
+ durations.set(key, info);
381
720
  }
382
- durations.set(key, info);
383
721
  }
384
722
  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") {
385
723
  const startedAt = startedTimes.get(key);
@@ -402,7 +740,9 @@ function hasEncryptedValues(data) {
402
740
  return false;
403
741
  }
404
742
  function isRunLevel(eventType) {
405
- 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";
743
+ 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,
744
+ // so it groups and labels with the run itself.
745
+ eventType === "attr_set";
406
746
  }
407
747
  const GUTTER_WIDTH = 36;
408
748
  const LANE_X = 20;
@@ -524,14 +864,14 @@ function deepParseJson(value) {
524
864
  return value;
525
865
  }
526
866
  function extractStructuredError(data, eventType) {
527
- if (!eventType || !ERROR_EVENT_TYPES$1.has(eventType))
867
+ if (!eventType || !ERROR_EVENT_TYPES.has(eventType))
528
868
  return null;
529
869
  if (data == null || typeof data !== "object")
530
870
  return null;
531
871
  const record = data;
532
- if (isStructuredErrorWithStack(record.error))
872
+ if (isStructuredError(record.error))
533
873
  return record.error;
534
- if (isStructuredErrorWithStack(record))
874
+ if (isStructuredError(record))
535
875
  return record;
536
876
  return null;
537
877
  }
@@ -570,13 +910,22 @@ function PayloadBlock({ data, eventType }) {
570
910
  if (structuredError) {
571
911
  return jsxRuntimeExports.jsx("div", { className: "p-2", children: jsxRuntimeExports.jsx(ErrorStackBlock, { value: structuredError }) });
572
912
  }
913
+ if (eventType === "attr_set") {
914
+ return jsxRuntimeExports.jsx(AttrSetEventBlock, { data: cleaned });
915
+ }
916
+ if (eventType === "run_cancelled") {
917
+ const cancelReason = cleaned != null && typeof cleaned === "object" && typeof cleaned.cancelReason === "string" ? cleaned.cancelReason : null;
918
+ if (cancelReason) {
919
+ return jsxRuntimeExports.jsxs("div", { className: "p-2 text-xs", 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 })] });
920
+ }
921
+ }
573
922
  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" })] }) })] });
574
923
  }
575
924
  const SORT_OPTIONS = [
576
925
  { value: "desc", label: "Newest" },
577
926
  { value: "asc", label: "Oldest" }
578
927
  ];
579
- function RowsSkeleton() {
928
+ function RowsSkeleton({ showSeparateEventOccurrenceTimestamps = false }) {
580
929
  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: {
581
930
  position: "absolute",
582
931
  left: 8,
@@ -588,9 +937,9 @@ function RowsSkeleton() {
588
937
  height: i % 4 === 0 ? 8 : 6,
589
938
  borderRadius: "50%",
590
939
  marginLeft: i % 4 === 0 ? 5 : 6
591
- } })] }), 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)) });
940
+ } })] }), 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)) });
592
941
  }
593
- function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand, activeGroupKey, selectedGroupKey, selectedGroupRange, correlationNameMap, workflowName, durationMap, onSelectGroup, onHoverGroup, onLoadEventData, cachedEventData, onCacheEventData, encryptionKey, onEncryptedDataDetected }) {
942
+ 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 }) {
594
943
  const [isLoading, setIsLoading] = reactExports.useState(false);
595
944
  const [loadedEventData, setLoadedEventData] = reactExports.useState(cachedEventData);
596
945
  const [loadError, setLoadError] = reactExports.useState(null);
@@ -603,6 +952,8 @@ function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand,
603
952
  const rowGroupKey = isRunLevel(event.eventType) ? "__run__" : event.correlationId ?? void 0;
604
953
  const statusDotColor = getStatusDotColor(event.eventType);
605
954
  const createdAt = new Date(event.createdAt);
955
+ const occurredAt = parseEventDate(event.occurredAt);
956
+ const displayedCreatedAt = showSeparateEventOccurrenceTimestamps ? createdAt : getEffectiveEventDate$1(event);
606
957
  const hasExistingEventData = "eventData" in event && event.eventData != null;
607
958
  const isRun = isRunLevel(event.eventType);
608
959
  const eventName = isRun ? workflowName ?? "-" : event.correlationId ? correlationNameMap.get(event.correlationId) ?? "-" : "-";
@@ -610,7 +961,7 @@ function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand,
610
961
  const durationInfo = durationKey ? durationMap.get(durationKey) : void 0;
611
962
  const hasActive = activeGroupKey !== void 0;
612
963
  const isRelated = rowGroupKey !== void 0 && rowGroupKey === activeGroupKey;
613
- const isDimmed = hasActive && !isRelated;
964
+ const isDimmed = hasActive && !isRelated && !suppressGroupDimming;
614
965
  const isPulsing = hasActive && isRelated;
615
966
  const showBranch = hasActive && isRelated && !isRun;
616
967
  const showLaneLine = selectedGroupRange !== null && index >= selectedGroupRange.first && index <= selectedGroupRange.last;
@@ -706,7 +1057,7 @@ function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand,
706
1057
  }, children: jsxRuntimeExports.jsx(ChevronRight, { className: "h-3 w-3 transition-transform", style: {
707
1058
  color: "var(--ds-gray-900)",
708
1059
  transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)"
709
- } }) }), 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: {
1060
+ } }) }), 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: {
710
1061
  position: "relative",
711
1062
  display: "inline-flex",
712
1063
  width: 6,
@@ -735,7 +1086,8 @@ function EventRow$1({ event, index, isFirst, isLast, isExpanded, onToggleExpand,
735
1086
  color: "var(--ds-red-900)"
736
1087
  }, 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" })] })] })] });
737
1088
  }
738
- function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, isLoadingMoreEvents = false, onLoadMoreEvents, encryptionKey, isLoading = false, sortOrder: sortOrderProp, onSortOrderChange, onDecrypt, isDecrypting = false, hasEncryptedData: hasEncryptedDataProp = false }) {
1089
+ 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 }) {
1090
+ const toast2 = useToast();
739
1091
  const [internalSortOrder, setInternalSortOrder] = reactExports.useState("asc");
740
1092
  const effectiveSortOrder = sortOrderProp ?? internalSortOrder;
741
1093
  const handleSortOrderChange = reactExports.useCallback((order) => {
@@ -745,28 +1097,40 @@ function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, is
745
1097
  setInternalSortOrder(order);
746
1098
  }
747
1099
  }, [onSortOrderChange]);
1100
+ const [searchQuery, setSearchQuery] = reactExports.useState("");
1101
+ const [searchResults, setSearchResults] = reactExports.useState(null);
1102
+ const [searchResultsTruncated, setSearchResultsTruncated] = reactExports.useState(false);
1103
+ const [searchError, setSearchError] = reactExports.useState(null);
1104
+ const [searchLoading, setSearchLoading] = reactExports.useState(false);
1105
+ const [searchNotFound, setSearchNotFound] = reactExports.useState(false);
1106
+ const searchRequestRef = reactExports.useRef(0);
1107
+ const virtuosoRef = reactExports.useRef(null);
1108
+ const parsedSearchId = reactExports.useMemo(() => parseExactWorkflowSearchId(searchQuery), [searchQuery]);
1109
+ const isExactSearchActive = searchResults !== null;
748
1110
  const sortedEvents = reactExports.useMemo(() => {
749
- if (!events || events.length === 0)
1111
+ const sourceEvents = isExactSearchActive ? searchResults : events ?? [];
1112
+ if (sourceEvents.length === 0)
750
1113
  return [];
751
1114
  const dir = effectiveSortOrder === "desc" ? -1 : 1;
752
- return [...events].sort((a, b) => dir * (new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()));
753
- }, [events, effectiveSortOrder]);
1115
+ return [...sourceEvents].sort((a, b) => dir * (getEffectiveEventTime(a) - getEffectiveEventTime(b)));
1116
+ }, [events, effectiveSortOrder, isExactSearchActive, searchResults]);
754
1117
  const hasEncryptedInlineData = reactExports.useMemo(() => {
755
- if (!events)
1118
+ const sourceEvents = isExactSearchActive ? searchResults : events;
1119
+ if (!sourceEvents)
756
1120
  return false;
757
- for (const event of events) {
1121
+ for (const event of sourceEvents) {
758
1122
  const ed = event.eventData;
759
1123
  if (hasEncryptedValues(ed))
760
1124
  return true;
761
1125
  }
762
1126
  return false;
763
- }, [events]);
1127
+ }, [events, isExactSearchActive, searchResults]);
764
1128
  const [foundEncryptedInLazyData, setFoundEncryptedInLazyData] = reactExports.useState(false);
765
1129
  const handleEncryptedDataDetected = reactExports.useCallback(() => {
766
1130
  setFoundEncryptedInLazyData(true);
767
1131
  }, []);
768
1132
  const hasEncryptedData = hasEncryptedDataProp || hasEncryptedInlineData || foundEncryptedInLazyData;
769
- const { correlationNameMap, workflowName } = reactExports.useMemo(() => buildNameMaps(events ?? null, run ?? null), [events, run]);
1133
+ const { correlationNameMap, workflowName } = reactExports.useMemo(() => buildNameMaps(isExactSearchActive ? searchResults : events ?? null, run ?? null), [events, isExactSearchActive, run, searchResults]);
770
1134
  const durationMap = reactExports.useMemo(() => buildDurationMap(sortedEvents), [sortedEvents]);
771
1135
  const [selectedGroupKey, setSelectedGroupKey] = reactExports.useState(void 0);
772
1136
  const [hoveredGroupKey, setHoveredGroupKey] = reactExports.useState(void 0);
@@ -834,58 +1198,100 @@ function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, is
834
1198
  }
835
1199
  return first >= 0 ? { first, last } : null;
836
1200
  }, [activeGroupKey, sortedEvents]);
837
- const [searchQuery, setSearchQuery] = reactExports.useState("");
838
- const virtuosoRef = reactExports.useRef(null);
839
- const searchIndex = reactExports.useMemo(() => {
840
- const entries = [];
841
- for (let i = 0; i < sortedEvents.length; i++) {
842
- const ev = sortedEvents[i];
843
- const isRun = isRunLevel(ev.eventType);
844
- const name = isRun ? workflowName ?? "" : ev.correlationId ? correlationNameMap.get(ev.correlationId) ?? "" : "";
845
- entries.push({
846
- fields: [
847
- ev.eventId,
848
- ev.correlationId ?? "",
849
- ev.eventType,
850
- formatEventType(ev.eventType),
851
- name
852
- ].map((f) => f.toLowerCase()),
853
- groupKey: ev.correlationId ?? (isRun ? "__run__" : void 0),
854
- eventId: ev.eventId,
855
- index: i
856
- });
857
- }
858
- return entries;
859
- }, [sortedEvents, correlationNameMap, workflowName]);
860
1201
  reactExports.useEffect(() => {
861
- var _a;
862
- const q = searchQuery.trim().toLowerCase();
863
- if (!q) {
1202
+ const trimmed = searchQuery.trim();
1203
+ if (!trimmed) {
1204
+ searchRequestRef.current += 1;
1205
+ setSearchResults(null);
1206
+ setSearchResultsTruncated(false);
1207
+ setSearchError(null);
1208
+ setSearchLoading(false);
1209
+ setSearchNotFound(false);
864
1210
  setSelectedGroupKey(void 0);
865
1211
  return;
866
1212
  }
867
- let bestMatch = null;
868
- let bestScore = 0;
869
- for (const entry of searchIndex) {
870
- for (const field of entry.fields) {
871
- if (field && field.includes(q)) {
872
- const score = q.length / field.length;
873
- if (score > bestScore) {
874
- bestScore = score;
875
- bestMatch = entry;
1213
+ const parsed = parseExactWorkflowSearchId(trimmed);
1214
+ if (!parsed || !onExactIdSearch) {
1215
+ setSearchResults(null);
1216
+ setSearchLoading(false);
1217
+ setSearchNotFound(false);
1218
+ return;
1219
+ }
1220
+ const requestId = ++searchRequestRef.current;
1221
+ setSearchLoading(true);
1222
+ setSearchNotFound(false);
1223
+ setSearchError(null);
1224
+ const abortController = new AbortController();
1225
+ const timer = setTimeout(() => {
1226
+ void (async () => {
1227
+ var _a;
1228
+ try {
1229
+ const results = await onExactIdSearch(parsed.id, parsed.kind, abortController.signal);
1230
+ if (abortController.signal.aborted || searchRequestRef.current !== requestId) {
1231
+ return;
1232
+ }
1233
+ if (results.status === "error") {
1234
+ setSearchResults([]);
1235
+ setSearchResultsTruncated(false);
1236
+ setSearchNotFound(false);
1237
+ setSearchError(results.message);
1238
+ setSelectedGroupKey(void 0);
1239
+ return;
1240
+ }
1241
+ if (results.status === "not_found" || results.status === "ok" && results.events.length === 0) {
1242
+ setSearchResults([]);
1243
+ setSearchResultsTruncated(false);
1244
+ setSearchNotFound(true);
1245
+ setSearchError(null);
1246
+ setSelectedGroupKey(void 0);
1247
+ return;
1248
+ }
1249
+ setSearchResults(results.events);
1250
+ setSearchResultsTruncated(Boolean(results.truncated));
1251
+ setSearchNotFound(false);
1252
+ setSearchError(null);
1253
+ setSelectedGroupKey(parsed.kind === "event" ? (() => {
1254
+ const first = results.events[0];
1255
+ if (!first)
1256
+ return void 0;
1257
+ return isRunLevel(first.eventType) ? "__run__" : first.correlationId ?? void 0;
1258
+ })() : parsed.id);
1259
+ (_a = virtuosoRef.current) == null ? void 0 : _a.scrollToIndex({
1260
+ index: 0,
1261
+ align: "start",
1262
+ behavior: "smooth"
1263
+ });
1264
+ } catch {
1265
+ if (abortController.signal.aborted || searchRequestRef.current !== requestId) {
1266
+ return;
1267
+ }
1268
+ setSearchResults([]);
1269
+ setSearchResultsTruncated(false);
1270
+ setSearchNotFound(false);
1271
+ setSearchError("Failed to search events. Try again.");
1272
+ setSelectedGroupKey(void 0);
1273
+ } finally {
1274
+ if (searchRequestRef.current === requestId && !abortController.signal.aborted) {
1275
+ setSearchLoading(false);
876
1276
  }
877
1277
  }
878
- }
1278
+ })();
1279
+ }, 300);
1280
+ return () => {
1281
+ clearTimeout(timer);
1282
+ abortController.abort();
1283
+ };
1284
+ }, [searchQuery, onExactIdSearch]);
1285
+ const handleSearchKeyDown = reactExports.useCallback((event) => {
1286
+ if (event.key !== "Enter") {
1287
+ return;
879
1288
  }
880
- if (bestMatch) {
881
- setSelectedGroupKey(bestMatch.groupKey);
882
- (_a = virtuosoRef.current) == null ? void 0 : _a.scrollToIndex({
883
- index: bestMatch.index,
884
- align: "center",
885
- behavior: "smooth"
886
- });
1289
+ const trimmed = searchQuery.trim();
1290
+ if (!trimmed || parseExactWorkflowSearchId(trimmed) || !onExactIdSearch || !looksLikeWorkflowIdSearchInput(trimmed)) {
1291
+ return;
887
1292
  }
888
- }, [searchQuery, searchIndex]);
1293
+ toast2.info("Enter a full step ID, wait ID, hook ID, or event ID");
1294
+ }, [searchQuery, onExactIdSearch, toast2]);
889
1295
  const hasHadEventsRef = reactExports.useRef(false);
890
1296
  if (sortedEvents.length > 0) {
891
1297
  hasHadEventsRef.current = true;
@@ -895,9 +1301,6 @@ function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, is
895
1301
  if (isInitialLoad) {
896
1302
  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, {})] });
897
1303
  }
898
- if (!isLoading && (!events || events.length === 0)) {
899
- return jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-full text-sm", style: { color: "var(--ds-gray-700)" }, children: "No events found" });
900
- }
901
1304
  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: {
902
1305
  padding: 6,
903
1306
  backgroundColor: "var(--ds-background-100)",
@@ -921,7 +1324,7 @@ function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, is
921
1324
  justifyContent: "center",
922
1325
  color: "var(--ds-gray-800)",
923
1326
  flexShrink: 0
924
- }, 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: {
1327
+ }, 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: {
925
1328
  marginLeft: -16,
926
1329
  paddingInline: 12,
927
1330
  fontFamily: "inherit",
@@ -930,27 +1333,58 @@ function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, is
930
1333
  border: "none",
931
1334
  outline: "none",
932
1335
  height: 40,
933
- width: "100%"
1336
+ width: "100%",
1337
+ opacity: onExactIdSearch ? 1 : 0.5,
1338
+ cursor: onExactIdSearch ? "text" : "not-allowed"
934
1339
  } })] }), 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: {
935
1340
  borderColor: "var(--ds-gray-alpha-200)",
936
1341
  color: "var(--ds-gray-900)",
937
1342
  backgroundColor: "var(--ds-background-100)"
938
- }, 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: () => {
939
- if (!hasMoreEvents || isLoadingMoreEvents) {
1343
+ }, 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-sm", 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: () => {
1344
+ if (isExactSearchActive || !hasMoreEvents || isLoadingMoreEvents) {
940
1345
  return;
941
1346
  }
942
1347
  void (onLoadMoreEvents == null ? void 0 : onLoadMoreEvents());
943
1348
  }, itemContent: (index) => {
944
1349
  const ev = sortedEvents[index];
945
- 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 });
1350
+ 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 });
946
1351
  }, 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: {
947
1352
  borderColor: "var(--ds-gray-alpha-200)",
948
1353
  color: "var(--ds-gray-900)",
949
1354
  backgroundColor: "var(--ds-background-100)"
950
- }, 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()) }) }) })] })] }) });
1355
+ }, 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()) }) }) })] })] }) });
1356
+ }
1357
+ function EventListView(props) {
1358
+ return jsxRuntimeExports.jsx(ContextCardProvider, { children: jsxRuntimeExports.jsx(EventListViewInner, { ...props }) });
1359
+ }
1360
+ const GUTTER_PX = 1;
1361
+ const MIN_PX = 50;
1362
+ const DEFAULT_START_PX = 340;
1363
+ function paneColTemplate(startPx) {
1364
+ return `${startPx}px ${GUTTER_PX}px minmax(${MIN_PX}px, 1fr)`;
1365
+ }
1366
+ const COL_TEMPLATE = paneColTemplate(DEFAULT_START_PX);
1367
+ const ROWS = [
1368
+ { id: "r0", name: 62, off: 0, bar: 72 },
1369
+ { id: "r1", name: 78, off: 6, bar: 48 },
1370
+ { id: "r2", name: 50, off: 10, bar: 55 },
1371
+ { id: "r3", name: 84, off: 18, bar: 30 },
1372
+ { id: "r4", name: 45, off: 18, bar: 42 },
1373
+ { id: "r5", name: 66, off: 34, bar: 38 },
1374
+ { id: "r6", name: 55, off: 41, bar: 25 },
1375
+ { id: "r7", name: 40, off: 50, bar: 30 }
1376
+ ];
1377
+ const HEADER_MARKERS = ["m0", "m1", "m2", "m3"];
1378
+ 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" }) });
1379
+ function TraceViewerSkeleton() {
1380
+ 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: {
1381
+ left: `${row.off}%`,
1382
+ width: `${row.bar}%`,
1383
+ top: index * 3.5
1384
+ } }, 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)) })] })] });
951
1385
  }
952
1386
  function shouldShowVerticalLine(eventType) {
953
- if (eventType === "hook_created" || eventType === "hook_received" || eventType === "hook_disposed") {
1387
+ if (isHookLifecycleEventType(eventType)) {
954
1388
  return true;
955
1389
  }
956
1390
  return false;
@@ -1001,12 +1435,20 @@ const MARKER_EVENT_TYPES = /* @__PURE__ */ new Set([
1001
1435
  "step_failed",
1002
1436
  "run_failed",
1003
1437
  "wait_created",
1004
- "wait_completed"
1438
+ "wait_completed",
1439
+ "attr_set"
1005
1440
  ]);
1006
- function convertEventsToSpanEvents(events, filterTypes = true) {
1441
+ const getEventTimestamp = (event) => {
1442
+ const value = (event == null ? void 0 : event.occurredAt) ?? (event == null ? void 0 : event.createdAt);
1443
+ if (!value)
1444
+ return void 0;
1445
+ const date = value instanceof Date ? value : new Date(value);
1446
+ return Number.isNaN(date.getTime()) ? void 0 : date;
1447
+ };
1448
+ function convertEventsToSpanEvents(events, filterTypes = true, options = {}) {
1007
1449
  return events.filter((event) => filterTypes ? MARKER_EVENT_TYPES.has(event.eventType) : true).map((event) => ({
1008
1450
  name: event.eventType,
1009
- timestamp: dateToOtelTime(event.createdAt),
1451
+ timestamp: dateToOtelTime(options.preferOccurredAt ? getEventTimestamp(event) ?? event.createdAt : event.createdAt),
1010
1452
  attributes: {
1011
1453
  eventId: event.eventId,
1012
1454
  correlationId: event.correlationId,
@@ -1026,18 +1468,23 @@ const waitEventsToWaitEntity = (events) => {
1026
1468
  return {
1027
1469
  waitId: startEvent.correlationId,
1028
1470
  runId: startEvent.runId,
1029
- createdAt: startEvent.createdAt,
1030
- resumeAt: (_a = startEvent.eventData) == null ? void 0 : _a.resumeAt,
1031
- completedAt: completedEvent == null ? void 0 : completedEvent.createdAt
1471
+ createdAt: getEventTimestamp(startEvent) ?? startEvent.createdAt,
1472
+ resumeAt: ((_a = startEvent.eventData) == null ? void 0 : _a.resumeAt) ? new Date(startEvent.eventData.resumeAt) : void 0,
1473
+ completedAt: getEventTimestamp(completedEvent)
1032
1474
  };
1033
1475
  };
1034
- function waitToSpan(events, maxEndTime) {
1476
+ function waitToSpan(events, maxEndTime, fallbackEndTime = maxEndTime) {
1035
1477
  const wait = waitEventsToWaitEntity(events);
1036
1478
  if (!wait) {
1037
1479
  return null;
1038
1480
  }
1039
1481
  const startTime = wait.createdAt;
1040
- const endTime = wait.completedAt ?? maxEndTime;
1482
+ const startMs = startTime.getTime();
1483
+ let endTime = wait.completedAt;
1484
+ if (!endTime) {
1485
+ const fallbackCap = wait.resumeAt && wait.resumeAt.getTime() < fallbackEndTime.getTime() ? wait.resumeAt : fallbackEndTime;
1486
+ endTime = maxEndTime.getTime() > startMs && maxEndTime.getTime() < fallbackCap.getTime() ? maxEndTime : fallbackCap;
1487
+ }
1041
1488
  const start = dateToOtelTime(startTime);
1042
1489
  const end = dateToOtelTime(endTime);
1043
1490
  const duration = calculateDuration(startTime, endTime);
@@ -1080,16 +1527,16 @@ const stepEventsToStepEntity = (events) => {
1080
1527
  status = "running";
1081
1528
  attempt += 1;
1082
1529
  if (!startedAt)
1083
- startedAt = e.createdAt;
1530
+ startedAt = getEventTimestamp(e) ?? e.createdAt;
1084
1531
  completedAt = void 0;
1085
1532
  break;
1086
1533
  case "step_completed":
1087
1534
  status = "completed";
1088
- completedAt = e.createdAt;
1535
+ completedAt = getEventTimestamp(e) ?? e.createdAt;
1089
1536
  break;
1090
1537
  case "step_failed":
1091
1538
  status = "failed";
1092
- completedAt = e.createdAt;
1539
+ completedAt = getEventTimestamp(e) ?? e.createdAt;
1093
1540
  break;
1094
1541
  case "step_retrying":
1095
1542
  status = "pending";
@@ -1106,8 +1553,8 @@ const stepEventsToStepEntity = (events) => {
1106
1553
  stepName: ((_a = createdEvent == null ? void 0 : createdEvent.eventData) == null ? void 0 : _a.stepName) ?? "",
1107
1554
  status,
1108
1555
  attempt,
1109
- createdAt: anchorEvent.createdAt,
1110
- updatedAt: (lastEvent == null ? void 0 : lastEvent.createdAt) ?? anchorEvent.createdAt,
1556
+ createdAt: getEventTimestamp(anchorEvent) ?? anchorEvent.createdAt,
1557
+ updatedAt: getEventTimestamp(lastEvent) ?? (lastEvent == null ? void 0 : lastEvent.createdAt) ?? anchorEvent.createdAt,
1111
1558
  startedAt,
1112
1559
  completedAt,
1113
1560
  specVersion: anchorEvent.specVersion
@@ -1124,13 +1571,20 @@ function stepToSpan(stepEvents, maxEndTime) {
1124
1571
  data: step
1125
1572
  };
1126
1573
  const resource = "step";
1127
- const endTime = new Date(step.completedAt ?? maxEndTime);
1128
- const events = convertEventsToSpanEvents(stepEvents, false);
1129
- const spanStartTime = new Date(step.createdAt);
1574
+ const events = convertEventsToSpanEvents(stepEvents, false, {
1575
+ preferOccurredAt: true
1576
+ });
1577
+ const spanStartEvent = stepEvents.find((event) => event.eventType === "step_created") ?? stepEvents[0];
1578
+ const spanStartTime = getEventTimestamp(spanStartEvent) ?? new Date(step.createdAt);
1130
1579
  let activeStartTime = step.startedAt ? new Date(step.startedAt) : void 0;
1131
1580
  const firstStartEvent = stepEvents.find((event) => event.eventType === "step_started");
1132
1581
  if (firstStartEvent) {
1133
- activeStartTime = new Date(firstStartEvent.createdAt);
1582
+ activeStartTime = getEventTimestamp(firstStartEvent) ?? new Date(firstStartEvent.createdAt);
1583
+ }
1584
+ let endTime = new Date(maxEndTime);
1585
+ if (step.completedAt) {
1586
+ const completedEvent = stepEvents.slice().reverse().find((event) => isTerminalStepEventType(event.eventType));
1587
+ endTime = getEventTimestamp(completedEvent) ?? new Date(step.completedAt);
1134
1588
  }
1135
1589
  return {
1136
1590
  spanId: String(step.stepId),
@@ -1152,7 +1606,7 @@ function stepToSpan(stepEvents, maxEndTime) {
1152
1606
  };
1153
1607
  }
1154
1608
  const hookEventsToHookEntity = (events) => {
1155
- var _a, _b;
1609
+ var _a;
1156
1610
  const createdEvent = events.find((event) => event.eventType === "hook_created");
1157
1611
  if (!createdEvent) {
1158
1612
  return null;
@@ -1164,10 +1618,10 @@ const hookEventsToHookEntity = (events) => {
1164
1618
  hookId: createdEvent.correlationId,
1165
1619
  runId: createdEvent.runId,
1166
1620
  token: (_a = createdEvent.eventData) == null ? void 0 : _a.token,
1167
- createdAt: createdEvent.createdAt,
1621
+ createdAt: getEventTimestamp(createdEvent) ?? createdEvent.createdAt,
1168
1622
  receivedCount: receivedEvents.length,
1169
- lastReceivedAt: (lastReceivedEvent == null ? void 0 : lastReceivedEvent.createdAt) || void 0,
1170
- disposedAt: ((_b = disposedEvents.at(-1)) == null ? void 0 : _b.createdAt) || void 0
1623
+ lastReceivedAt: getEventTimestamp(lastReceivedEvent),
1624
+ disposedAt: getEventTimestamp(disposedEvents.at(-1))
1171
1625
  };
1172
1626
  };
1173
1627
  function hookToSpan(hookEvents, maxEndTime) {
@@ -1179,7 +1633,7 @@ function hookToSpan(hookEvents, maxEndTime) {
1179
1633
  const endTime = hook.disposedAt || maxEndTime;
1180
1634
  return {
1181
1635
  spanId: String(hook.hookId),
1182
- name: String(hook.hookId),
1636
+ name: hook.token ?? String(hook.hookId),
1183
1637
  kind: 1,
1184
1638
  // INTERNAL span kind
1185
1639
  resource: "hook",
@@ -1200,15 +1654,26 @@ function hookToSpan(hookEvents, maxEndTime) {
1200
1654
  function runToSpan(run, runEvents, nowTime) {
1201
1655
  var _a;
1202
1656
  const now = nowTime ?? /* @__PURE__ */ new Date();
1657
+ const runCreatedEvent = runEvents.find((event) => event.eventType === "run_created");
1658
+ const runStartedEvent = runEvents.find((event) => event.eventType === "run_started");
1659
+ const terminalEvent = runEvents.slice().reverse().find((event) => event.eventType === "run_completed" || event.eventType === "run_failed" || event.eventType === "run_cancelled");
1660
+ const spanStartTime = getEventTimestamp(runCreatedEvent) ?? new Date(run.createdAt);
1661
+ const activeStartTime = getEventTimestamp(runStartedEvent) ?? (run.startedAt ? new Date(run.startedAt) : void 0);
1662
+ const completedAt = getEventTimestamp(terminalEvent) ?? run.completedAt ?? void 0;
1663
+ const endTime = completedAt ?? now;
1203
1664
  const { input: _i, output: _o, error: _e, ...runIdentity } = run;
1204
1665
  const attributes = {
1205
1666
  resource: "run",
1206
- data: runIdentity
1667
+ data: {
1668
+ ...runIdentity,
1669
+ createdAt: spanStartTime,
1670
+ startedAt: activeStartTime,
1671
+ completedAt
1672
+ }
1207
1673
  };
1208
- const spanStartTime = new Date(run.createdAt);
1209
- const activeStartTime = run.startedAt ? new Date(run.startedAt) : void 0;
1210
- const endTime = run.completedAt ?? now;
1211
- const events = convertEventsToSpanEvents(runEvents, false);
1674
+ const events = convertEventsToSpanEvents(runEvents, false, {
1675
+ preferOccurredAt: true
1676
+ });
1212
1677
  return {
1213
1678
  spanId: String(run.runId),
1214
1679
  name: String(((_a = parseWorkflowName(run.workflowName)) == null ? void 0 : _a.shortName) ?? "?"),
@@ -1228,9 +1693,15 @@ function runToSpan(run, runEvents, nowTime) {
1228
1693
  activeStartTime: activeStartTime && activeStartTime.getTime() > spanStartTime.getTime() ? dateToOtelTime(activeStartTime) : void 0
1229
1694
  };
1230
1695
  }
1231
- const isStepEvent = (eventType) => eventType.startsWith("step_");
1232
- const isTimerEvent = (eventType) => eventType === "wait_created" || eventType === "wait_completed";
1233
- const isHookLifecycleEvent = (eventType) => eventType === "hook_received" || eventType === "hook_created" || eventType === "hook_disposed";
1696
+ const isRunLevelEvent = (event) => !event.correlationId || !isWaitEventType(event.eventType) && !isHookLifecycleEventType(event.eventType) && !isStepEventType(event.eventType);
1697
+ function filterSpanRawEvents(events, resource, spanId) {
1698
+ if (resource === "run") {
1699
+ return events.filter(isRunLevelEvent);
1700
+ }
1701
+ if (!spanId)
1702
+ return [];
1703
+ return events.filter((e) => e.correlationId === spanId);
1704
+ }
1234
1705
  function pushEvent(map, correlationId, event) {
1235
1706
  const existing = map.get(correlationId);
1236
1707
  if (existing) {
@@ -1250,15 +1721,15 @@ function groupEventsByCorrelation(events) {
1250
1721
  runLevelEvents.push(event);
1251
1722
  continue;
1252
1723
  }
1253
- if (isTimerEvent(event.eventType)) {
1724
+ if (isWaitEventType(event.eventType)) {
1254
1725
  pushEvent(timerEvents, correlationId, event);
1255
1726
  continue;
1256
1727
  }
1257
- if (isHookLifecycleEvent(event.eventType)) {
1728
+ if (isHookLifecycleEventType(event.eventType)) {
1258
1729
  pushEvent(hookEvents, correlationId, event);
1259
1730
  continue;
1260
1731
  }
1261
- if (isStepEvent(event.eventType)) {
1732
+ if (isStepEventType(event.eventType)) {
1262
1733
  pushEvent(eventsByStepId, correlationId, event);
1263
1734
  continue;
1264
1735
  }
@@ -1269,7 +1740,7 @@ function groupEventsByCorrelation(events) {
1269
1740
  function computeLatestKnownTime(events, run) {
1270
1741
  let latest = new Date(run.createdAt).getTime();
1271
1742
  for (const event of events) {
1272
- const t = new Date(event.createdAt).getTime();
1743
+ const t = (getEventTimestamp(event) ?? new Date(event.createdAt)).getTime();
1273
1744
  if (t > latest)
1274
1745
  latest = t;
1275
1746
  }
@@ -1277,9 +1748,10 @@ function computeLatestKnownTime(events, run) {
1277
1748
  }
1278
1749
  function buildSpans(run, groupedEvents, now, latestKnownTime) {
1279
1750
  const childMaxEnd = latestKnownTime;
1751
+ const runMaxEnd = run.completedAt ?? now;
1280
1752
  const stepSpans = Array.from(groupedEvents.eventsByStepId.values()).map((events) => stepToSpan(events, childMaxEnd)).filter((span) => span !== null);
1281
1753
  const hookSpans = Array.from(groupedEvents.hookEvents.values()).map((events) => hookToSpan(events, childMaxEnd)).filter((span) => span !== null);
1282
- const waitSpans = Array.from(groupedEvents.timerEvents.values()).map((events) => waitToSpan(events, childMaxEnd)).filter((span) => span !== null);
1754
+ const waitSpans = Array.from(groupedEvents.timerEvents.values()).map((events) => waitToSpan(events, childMaxEnd, runMaxEnd)).filter((span) => span !== null);
1283
1755
  return {
1284
1756
  runSpan: runToSpan(run, groupedEvents.runLevelEvents, now),
1285
1757
  spans: [...stepSpans, ...hookSpans, ...waitSpans]
@@ -1425,49 +1897,10 @@ function parseContent(content) {
1425
1897
  output: part.output
1426
1898
  };
1427
1899
  }
1428
- return { type: "text", text: "" };
1429
- });
1430
- }
1431
- return [];
1432
- }
1433
- const serializeForClipboard = (value) => {
1434
- if (typeof value === "string")
1435
- return value;
1436
- if (typeof value === "number" || typeof value === "boolean" || value === null) {
1437
- return String(value);
1438
- }
1439
- try {
1440
- return JSON.stringify(value, null, 2);
1441
- } catch {
1442
- return String(value);
1443
- }
1444
- };
1445
- function CopyableDataBlock({ data }) {
1446
- const toast2 = useToast();
1447
- 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: () => {
1448
- navigator.clipboard.writeText(serializeForClipboard(data)).then(() => {
1449
- toast2.success("Copied to clipboard");
1450
- }).catch(() => {
1451
- toast2.error("Failed to copy");
1900
+ return { type: "text", text: "" };
1452
1901
  });
1453
- }, children: jsxRuntimeExports.jsx(Copy, { size: 12 }) }), jsxRuntimeExports.jsx(DataInspector, { data })] });
1454
- }
1455
- function DetailCard({ summary, children, onToggle, disabled = false, summaryClassName, contentClassName }) {
1456
- if (disabled) {
1457
- return jsxRuntimeExports.jsx("div", { className: `rounded-md border px-2.5 py-1.5 text-xs ${summaryClassName ?? ""}`, style: {
1458
- borderColor: "var(--ds-gray-300)",
1459
- backgroundColor: "var(--ds-gray-100)",
1460
- color: "var(--ds-gray-700)",
1461
- cursor: "not-allowed",
1462
- opacity: 0.8
1463
- }, children: summary });
1464
1902
  }
1465
- 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: {
1466
- borderColor: "var(--ds-gray-300)",
1467
- backgroundColor: "var(--ds-gray-100)",
1468
- color: "var(--ds-gray-900)",
1469
- listStyle: "none"
1470
- }, 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 })] });
1903
+ return [];
1471
1904
  }
1472
1905
  function TabButton({ active, onClick, children }) {
1473
1906
  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: {
@@ -1506,25 +1939,12 @@ const conversationTabs = [
1506
1939
  { id: "conversation", label: "Conversation" },
1507
1940
  { id: "json", label: "Raw JSON" }
1508
1941
  ];
1509
- function ConversationWithTabs({ conversation, args }) {
1942
+ function ConversationWithTabs({ conversation, args, defaultOpen, onOpenChange }) {
1510
1943
  const [activeTab, setActiveTab] = reactExports.useState("conversation");
1511
- 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) }) }) });
1944
+ 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) }) }) });
1512
1945
  }
1513
1946
  function EncryptedFieldBlock() {
1514
- const ctx = reactExports.useContext(DecryptClickContext);
1515
- if (ctx) {
1516
- 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: {
1517
- borderColor: "var(--ds-gray-400)",
1518
- backgroundColor: "var(--ds-gray-100)",
1519
- color: "var(--ds-gray-700)",
1520
- opacity: ctx.isDecrypting ? 0.6 : 1
1521
- }, 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" })] });
1522
- }
1523
- 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: {
1524
- borderColor: "var(--ds-gray-300)",
1525
- backgroundColor: "var(--ds-gray-100)",
1526
- color: "var(--ds-gray-700)"
1527
- }, children: [jsxRuntimeExports.jsx(Lock, { className: "h-3 w-3" }), jsxRuntimeExports.jsx("span", { className: "font-medium", children: "Encrypted" })] });
1947
+ return jsxRuntimeExports.jsx(EncryptedDataBlock, {});
1528
1948
  }
1529
1949
  function ExpiredFieldBlock() {
1530
1950
  return jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1.5 rounded-md border px-3 py-2 text-xs", style: {
@@ -1570,6 +1990,7 @@ const attributeOrder = [
1570
1990
  "projectId",
1571
1991
  "environment",
1572
1992
  "executionContext",
1993
+ "occurredAt",
1573
1994
  "createdAt",
1574
1995
  "startedAt",
1575
1996
  "updatedAt",
@@ -1581,6 +2002,7 @@ const attributeOrder = [
1581
2002
  "eventData",
1582
2003
  "input",
1583
2004
  "output",
2005
+ "attributes",
1584
2006
  "resumeAt"
1585
2007
  ];
1586
2008
  const sortByAttributeOrder = (a, b) => {
@@ -1597,11 +2019,14 @@ const attributeDisplayNames = {
1597
2019
  attempt: "Attempts",
1598
2020
  eventId: "Event ID",
1599
2021
  runId: "Run ID",
2022
+ token: "Token",
1600
2023
  eventType: "Event Type",
2024
+ errorCode: "Error Code",
1601
2025
  correlationId: "Correlation ID",
1602
2026
  deploymentId: "Deployment ID",
1603
2027
  specVersion: "Spec Version",
1604
2028
  workflowCoreVersion: "@workflow/core version",
2029
+ occurredAt: "Occurred",
1605
2030
  createdAt: "Created",
1606
2031
  startedAt: "Started",
1607
2032
  updatedAt: "Updated",
@@ -1628,7 +2053,7 @@ const getModuleSpecifierFromName = (value) => {
1628
2053
  }
1629
2054
  return raw;
1630
2055
  };
1631
- const parseDateValue = (value) => {
2056
+ const parseDateValue$1 = (value) => {
1632
2057
  if (value == null) {
1633
2058
  return null;
1634
2059
  }
@@ -1641,31 +2066,16 @@ const parseDateValue = (value) => {
1641
2066
  const date = typeof value === "number" ? new Date(value) : new Date(String(value));
1642
2067
  return Number.isNaN(date.getTime()) ? null : date;
1643
2068
  };
1644
- const formatLocalMillisecondTime = (date) => date.toLocaleString(void 0, {
1645
- year: "numeric",
1646
- month: "numeric",
1647
- day: "numeric",
1648
- hour: "numeric",
1649
- minute: "numeric",
1650
- second: "numeric",
1651
- fractionalSecondDigits: 3
1652
- });
1653
- const localMillisecondTime = (value) => {
1654
- const date = parseDateValue(value);
1655
- if (!date) {
1656
- return "-";
1657
- }
1658
- return formatLocalMillisecondTime(date);
1659
- };
2069
+ const formatLocalMillisecondTime = (date) => format(date, "MMM dd HH:mm:ss.SS OO").toUpperCase();
1660
2070
  const localMillisecondTimeOrNull = (value) => {
1661
- const date = parseDateValue(value);
2071
+ const date = parseDateValue$1(value);
1662
2072
  if (!date) {
1663
2073
  return null;
1664
2074
  }
1665
2075
  return formatLocalMillisecondTime(date);
1666
2076
  };
1667
2077
  const timestampWithTooltipOrNull = (value) => {
1668
- const date = parseDateValue(value);
2078
+ const date = parseDateValue$1(value);
1669
2079
  if (!date)
1670
2080
  return null;
1671
2081
  return jsxRuntimeExports.jsx(TimestampTooltip, { date, children: jsxRuntimeExports.jsx("span", { children: formatLocalMillisecondTime(date) }) });
@@ -1677,7 +2087,7 @@ const attributeToDisplayFn = {
1677
2087
  stepName: (_value) => null,
1678
2088
  // IDs
1679
2089
  runId: (_value) => null,
1680
- stepId: (_value) => null,
2090
+ stepId: (value) => String(value),
1681
2091
  hookId: (value) => String(value),
1682
2092
  eventId: (value) => String(value),
1683
2093
  // Run/step details
@@ -1686,6 +2096,7 @@ const attributeToDisplayFn = {
1686
2096
  // Hook details
1687
2097
  token: (value) => String(value),
1688
2098
  isWebhook: (value) => String(value),
2099
+ isSystem: (value) => String(value),
1689
2100
  receivedCount: (value) => String(value),
1690
2101
  lastReceivedAt: localMillisecondTimeOrNull,
1691
2102
  disposedAt: localMillisecondTimeOrNull,
@@ -1701,7 +2112,16 @@ const attributeToDisplayFn = {
1701
2112
  projectId: (_value) => null,
1702
2113
  environment: (_value) => null,
1703
2114
  executionContext: (_value) => null,
2115
+ // Attributes — string-string metadata attached to the run.
2116
+ // Rendered as key-value rows in its own collapsible section;
2117
+ // if empty/missing, hidden by the hasDisplayContent gate.
2118
+ attributes: (value) => {
2119
+ if (!hasDisplayContent(value))
2120
+ return null;
2121
+ return jsxRuntimeExports.jsx(RunAttributesCard, { attributes: value });
2122
+ },
1704
2123
  // Dates — wrapped with TimestampTooltip showing UTC/local + relative time
2124
+ occurredAt: timestampWithTooltipOrNull,
1705
2125
  createdAt: timestampWithTooltipOrNull,
1706
2126
  startedAt: timestampWithTooltipOrNull,
1707
2127
  updatedAt: (_value) => null,
@@ -1713,71 +2133,78 @@ const attributeToDisplayFn = {
1713
2133
  metadata: (value) => {
1714
2134
  if (!hasDisplayContent(value))
1715
2135
  return null;
1716
- if (isEncryptedMarker(value))
1717
- return jsxRuntimeExports.jsx(EncryptedFieldBlock, {});
1718
- if (isExpiredMarker(value))
1719
- return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
1720
- return JsonBlock(value);
2136
+ if (isEncryptedMarker(value)) {
2137
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Metadata", children: jsxRuntimeExports.jsx(EncryptedDataBlock, {}) });
2138
+ }
2139
+ if (isExpiredMarker(value)) {
2140
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Metadata", children: jsxRuntimeExports.jsx(ExpiredFieldBlock, {}) });
2141
+ }
2142
+ return jsxRuntimeExports.jsx(RunMetadataCard, { metadata: value });
1721
2143
  },
1722
2144
  input: (value, context) => {
1723
- if (isEncryptedMarker(value))
1724
- return jsxRuntimeExports.jsx(EncryptedFieldBlock, {});
2145
+ if (isEncryptedMarker(value)) {
2146
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input", defaultOpen: context == null ? void 0 : context.sectionOpen, onOpenChange: context == null ? void 0 : context.onSectionOpenChange, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2147
+ }
1725
2148
  if (isExpiredMarker(value))
1726
2149
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
1727
2150
  if (value && typeof value === "object" && "args" in value) {
1728
2151
  const { args, closureVars, thisVal } = value;
1729
- const argCount2 = Array.isArray(args) ? args.length : 0;
1730
- const argLabel2 = argCount2 === 1 ? "argument" : "arguments";
1731
2152
  const hasClosureVars = hasDisplayContent(closureVars);
1732
2153
  const hasThisVal = hasDisplayContent(thisVal);
1733
2154
  const hasArgs = hasDisplayContent(args);
1734
2155
  if ((context == null ? void 0 : context.stepName) && isDoStreamStep(context.stepName)) {
1735
2156
  const conversation = extractConversation(args);
1736
2157
  if (conversation && conversation.length > 0) {
1737
- 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) })] });
2158
+ 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) })] });
1738
2159
  }
1739
2160
  }
1740
2161
  if (!hasArgs && !hasClosureVars && !hasThisVal) {
1741
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Input (no data)", disabled: true, summaryClassName: "text-label-14 font-medium py-2" });
2162
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input (no data)", disabled: true });
1742
2163
  }
1743
- 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) })] });
2164
+ 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) })] });
1744
2165
  }
1745
- const argCount = Array.isArray(value) ? value.length : 0;
1746
- const argLabel = argCount === 1 ? "argument" : "arguments";
1747
2166
  if (!hasDisplayContent(value)) {
1748
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Input (no data)", disabled: true, summaryClassName: "text-label-14 font-medium py-2" });
2167
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input (no data)", disabled: true });
1749
2168
  }
1750
- 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) });
2169
+ 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) });
1751
2170
  },
1752
- output: (value) => {
2171
+ output: (value, context) => {
2172
+ if (isEncryptedMarker(value)) {
2173
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Output", defaultOpen: context == null ? void 0 : context.sectionOpen, onOpenChange: context == null ? void 0 : context.onSectionOpenChange, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2174
+ }
1753
2175
  if (!hasDisplayContent(value))
1754
2176
  return null;
1755
- if (isEncryptedMarker(value))
1756
- return jsxRuntimeExports.jsx(EncryptedFieldBlock, {});
1757
2177
  if (isExpiredMarker(value))
1758
2178
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
1759
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Output", summaryClassName: "text-label-14 font-medium py-2", contentClassName: "mt-0", children: JsonBlock(value) });
2179
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Output", defaultOpen: context == null ? void 0 : context.sectionOpen, onOpenChange: context == null ? void 0 : context.onSectionOpenChange, children: JsonBlock(value) });
1760
2180
  },
1761
2181
  error: (value) => {
1762
- if (isEncryptedMarker(value))
1763
- return jsxRuntimeExports.jsx(EncryptedFieldBlock, {});
2182
+ if (isEncryptedMarker(value)) {
2183
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Error", defaultOpen: true, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2184
+ }
1764
2185
  if (isExpiredMarker(value))
1765
2186
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
1766
2187
  if (!hasDisplayContent(value))
1767
2188
  return null;
1768
- if (isStructuredErrorWithStack(value)) {
1769
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Error", summaryClassName: "text-label-14 font-medium py-2", contentClassName: "mt-0", children: jsxRuntimeExports.jsx(ErrorStackBlock, { value }) });
2189
+ if (isStructuredError(value)) {
2190
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Error", defaultOpen: true, children: jsxRuntimeExports.jsx(ErrorStackBlock, { value }) });
1770
2191
  }
1771
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Error", summaryClassName: "text-label-14 font-medium py-2", contentClassName: "mt-0", children: JsonBlock(value) });
2192
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Error", defaultOpen: true, children: JsonBlock(value) });
1772
2193
  },
1773
2194
  eventData: (value) => {
1774
- if (isEncryptedMarker(value))
1775
- return jsxRuntimeExports.jsx(EncryptedFieldBlock, {});
2195
+ if (isEncryptedMarker(value)) {
2196
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Event Data", defaultOpen: true, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2197
+ }
1776
2198
  if (isExpiredMarker(value))
1777
2199
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
1778
2200
  if (!hasDisplayContent(value))
1779
2201
  return null;
1780
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Event Data", children: JsonBlock(value) });
2202
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Event Data", defaultOpen: true, children: JsonBlock(value) });
2203
+ },
2204
+ errorCode: (value) => {
2205
+ if (typeof value !== "string" || value.length === 0)
2206
+ return null;
2207
+ return String(value);
1781
2208
  }
1782
2209
  };
1783
2210
  const resolvableAttributes = [
@@ -1785,33 +2212,68 @@ const resolvableAttributes = [
1785
2212
  "output",
1786
2213
  "error",
1787
2214
  "metadata",
2215
+ "attributes",
1788
2216
  "eventData"
1789
2217
  ];
2218
+ const selfHeaderedAttributes = /* @__PURE__ */ new Set([
2219
+ "input",
2220
+ "output",
2221
+ "error",
2222
+ "metadata",
2223
+ "attributes",
2224
+ "eventData"
2225
+ ]);
1790
2226
  const ExpiredDataMessage = () => jsxRuntimeExports.jsx("div", { className: "text-copy-12 rounded-md border p-4 my-2", style: {
1791
2227
  borderColor: "var(--ds-gray-300)",
1792
2228
  backgroundColor: "var(--ds-gray-100)",
1793
2229
  color: "var(--ds-gray-700)"
1794
2230
  }, children: jsxRuntimeExports.jsx("span", { children: "The data for this run has expired and is no longer available." }) });
2231
+ const copyableBasicAttributes = /* @__PURE__ */ new Set([
2232
+ "stepId",
2233
+ "hookId",
2234
+ "eventId",
2235
+ "deploymentId",
2236
+ "moduleSpecifier"
2237
+ ]);
2238
+ const loadingSectionLabels = {
2239
+ input: "Input",
2240
+ output: "Output",
2241
+ eventData: "Event Data"
2242
+ };
1795
2243
  const AttributeBlock = ({ attribute, value, isLoading, inline = false, context }) => {
1796
- const isExpandableLoadingTarget = attribute === "input" || attribute === "eventData";
1797
- if (isLoading && isExpandableLoadingTarget && !hasDisplayContent(value)) {
1798
- 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" })] });
2244
+ const decryptCtx = reactExports.useContext(DecryptClickContext);
2245
+ const sectionOpenRef = reactExports.useRef(false);
2246
+ const handleSectionOpenChange = reactExports.useCallback((open) => {
2247
+ sectionOpenRef.current = open;
2248
+ }, []);
2249
+ const label = loadingSectionLabels[attribute];
2250
+ if (isLoading && label && !hasDisplayContent(value)) {
2251
+ if (decryptCtx == null ? void 0 : decryptCtx.hasEncryptedData) {
2252
+ return jsxRuntimeExports.jsx(Collapsible, { label, defaultOpen: attribute === "eventData" || sectionOpenRef.current, onOpenChange: handleSectionOpenChange, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2253
+ }
2254
+ return jsxRuntimeExports.jsx(Collapsible, { label });
1799
2255
  }
1800
2256
  const displayFn = attributeToDisplayFn[attribute];
1801
2257
  if (!displayFn) {
1802
2258
  return null;
1803
2259
  }
1804
- const displayValue = displayFn(value, context);
2260
+ const displayValue = displayFn(value, {
2261
+ ...context,
2262
+ sectionOpen: sectionOpenRef.current,
2263
+ onSectionOpenChange: handleSectionOpenChange
2264
+ });
1805
2265
  if (!displayValue) {
1806
2266
  return null;
1807
2267
  }
1808
2268
  if (inline) {
1809
2269
  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 })] });
1810
2270
  }
1811
- 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)] });
2271
+ if (selfHeaderedAttributes.has(attribute)) {
2272
+ return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: displayValue });
2273
+ }
2274
+ 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-xs", style: { color: "var(--ds-gray-1000)" }, children: displayValue })] }, attribute)] });
1812
2275
  };
1813
- const AttributePanel = ({ data, moduleSpecifier, isLoading, error, expiredAt, onStreamClick, onRunClick, onDecrypt, isDecrypting = false, resource }) => {
1814
- const toast2 = useToast();
2276
+ const AttributePanel = ({ data, moduleSpecifier, moduleSourceUrl, isLoading, error, expiredAt, onStreamClick, onRunClick, onDecrypt, isDecrypting = false, resource }) => {
1815
2277
  const displayData = reactExports.useMemo(() => {
1816
2278
  const result = { ...data };
1817
2279
  const execCtx = data.executionContext;
@@ -1833,14 +2295,16 @@ const AttributePanel = ({ data, moduleSpecifier, isLoading, error, expiredAt, on
1833
2295
  const present = Object.keys(displayData).filter((key) => resolvableAttributes.includes(key)).sort(sortByAttributeOrder);
1834
2296
  if (!isLoading)
1835
2297
  return present;
1836
- const loadingDefaults = ["input"];
1837
- for (const key of loadingDefaults) {
2298
+ if (resource === "sleep")
2299
+ return present;
2300
+ const loadingPlaceholders = ["input", "output"];
2301
+ for (const key of loadingPlaceholders) {
1838
2302
  if (!present.includes(key)) {
1839
2303
  present.push(key);
1840
2304
  }
1841
2305
  }
1842
2306
  return present.sort(sortByAttributeOrder);
1843
- }, [displayData, isLoading]);
2307
+ }, [displayData, isLoading, resource]);
1844
2308
  const visibleBasicAttributes = basicAttributes.filter((attribute) => {
1845
2309
  const displayFn = attributeToDisplayFn[attribute];
1846
2310
  if (!displayFn)
@@ -1871,46 +2335,37 @@ const AttributePanel = ({ data, moduleSpecifier, isLoading, error, expiredAt, on
1871
2335
  const displayContext = reactExports.useMemo(() => ({
1872
2336
  stepName: displayData.stepName
1873
2337
  }), [displayData.stepName]);
1874
- const handleCopyModuleSpecifier = reactExports.useCallback((value) => {
1875
- navigator.clipboard.writeText(value).then(() => {
1876
- toast2.success("moduleSpecifier copied");
1877
- }).catch(() => {
1878
- toast2.error("Failed to copy moduleSpecifier");
1879
- });
1880
- }, []);
1881
- 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: {
1882
- borderColor: "var(--ds-gray-300)"
1883
- }, children: [orderedBasicAttributes.map((attribute, index) => {
2338
+ const outerDecryptCtx = reactExports.useContext(DecryptClickContext);
2339
+ const decryptValue = onDecrypt ? {
2340
+ onDecrypt,
2341
+ isDecrypting,
2342
+ hasEncryptedData: outerDecryptCtx == null ? void 0 : outerDecryptCtx.hasEncryptedData
2343
+ } : outerDecryptCtx;
2344
+ 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) => {
1884
2345
  var _a;
1885
2346
  const displayValue = (_a = attributeToDisplayFn[attribute]) == null ? void 0 : _a.call(attributeToDisplayFn, displayData[attribute]);
1886
- const isModuleSpecifier = attribute === "moduleSpecifier";
1887
- const moduleSpecifierValue = typeof displayValue === "string" ? displayValue : String(displayValue ?? displayData.moduleSpecifier ?? "");
1888
- const shouldCapitalizeLabel = attribute !== "workflowCoreVersion";
1889
- const showResumeAtSkeleton = isLoading && resource === "sleep" && !displayData.resumeAt;
1890
- const showDivider = index < orderedBasicAttributes.length - 1 || showResumeAtSkeleton;
1891
- 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: {
1892
- color: "var(--ds-gray-1000)",
1893
- background: "transparent",
1894
- border: "none",
1895
- padding: 0
1896
- }, 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);
1897
- }), 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)) })] }) }) }) });
2347
+ const isCopyableBasicAttribute = copyableBasicAttributes.has(attribute) && typeof displayValue === "string";
2348
+ const label = getAttributeDisplayName(attribute);
2349
+ return jsxRuntimeExports.jsx(DetailMonoKeyValueRow, { label, value: displayValue, copyText: isCopyableBasicAttribute ? displayValue : void 0, href: attribute === "moduleSpecifier" ? moduleSourceUrl : void 0 }, attribute);
2350
+ }), 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] }) }) }) });
1898
2351
  };
1899
- const ERROR_EVENT_TYPES = /* @__PURE__ */ new Set(["step_failed", "step_retrying"]);
1900
- const DATA_EVENT_TYPES = /* @__PURE__ */ new Set([
1901
- "step_created",
1902
- "step_completed",
1903
- "step_failed",
1904
- "step_retrying",
1905
- "hook_created",
1906
- "hook_received",
1907
- "run_created",
1908
- "run_completed",
1909
- "run_failed",
1910
- "wait_created",
1911
- "wait_completed"
1912
- ]);
1913
- function EventItem({ event, onLoadEventData, encryptionKey }) {
2352
+ const parseDateValue = (value) => {
2353
+ if (value == null)
2354
+ return null;
2355
+ const date = value instanceof Date ? value : new Date(String(value));
2356
+ return Number.isNaN(date.getTime()) ? null : date;
2357
+ };
2358
+ const getEffectiveEventDate = (event) => parseDateValue(event.occurredAt) ?? new Date(event.createdAt);
2359
+ const formatEventTimestamp = (date) => date.toLocaleString(void 0, {
2360
+ month: "short",
2361
+ day: "numeric",
2362
+ hour: "numeric",
2363
+ minute: "numeric",
2364
+ second: "numeric",
2365
+ fractionalSecondDigits: 3,
2366
+ timeZoneName: "short"
2367
+ });
2368
+ function EventItem({ event, onLoadEventData, encryptionKey, showSeparateEventOccurrenceTimestamps = false }) {
1914
2369
  const [loadedData, setLoadedData] = reactExports.useState(null);
1915
2370
  const [isLoading, setIsLoading] = reactExports.useState(false);
1916
2371
  const [loadError, setLoadError] = reactExports.useState(null);
@@ -1918,17 +2373,17 @@ function EventItem({ event, onLoadEventData, encryptionKey }) {
1918
2373
  const loadedDataRef = reactExports.useRef(null);
1919
2374
  const existingData = "eventData" in event && event.eventData != null ? event.eventData : null;
1920
2375
  const mergedDisplay = loadedData ?? existingData;
1921
- const canHaveData = DATA_EVENT_TYPES.has(event.eventType);
1922
- const loadEventData = reactExports.useCallback(async (options) => {
1923
- if (!onLoadEventData || !event.correlationId || !event.eventId)
2376
+ const canHaveData = existingData !== null || getEventDataRefFields(event.eventType).length > 0;
2377
+ const loadEventData = reactExports.useCallback(async (force) => {
2378
+ if (!onLoadEventData)
1924
2379
  return;
1925
- if (!(options == null ? void 0 : options.force) && loadedDataRef.current !== null) {
2380
+ if (!force && loadedDataRef.current !== null) {
1926
2381
  return;
1927
2382
  }
1928
2383
  try {
1929
2384
  setIsLoading(true);
1930
2385
  setLoadError(null);
1931
- const data = await onLoadEventData(event.correlationId, event.eventId);
2386
+ const data = await onLoadEventData(event);
1932
2387
  loadedDataRef.current = data;
1933
2388
  setLoadedData(data);
1934
2389
  } catch (err) {
@@ -1936,38 +2391,40 @@ function EventItem({ event, onLoadEventData, encryptionKey }) {
1936
2391
  } finally {
1937
2392
  setIsLoading(false);
1938
2393
  }
1939
- }, [onLoadEventData, event.correlationId, event.eventId]);
1940
- const handleExpand = reactExports.useCallback(async () => {
2394
+ }, [onLoadEventData, event]);
2395
+ const handleExpand = reactExports.useCallback(() => {
1941
2396
  if (isLoading)
1942
2397
  return;
1943
2398
  wasExpandedRef.current = true;
1944
- await loadEventData();
2399
+ void loadEventData(false);
1945
2400
  }, [isLoading, loadEventData]);
1946
- reactExports.useEffect(() => {
2401
+ reactExports.useLayoutEffect(() => {
1947
2402
  if (!encryptionKey || !wasExpandedRef.current)
1948
2403
  return;
1949
2404
  loadedDataRef.current = null;
1950
2405
  setLoadedData(null);
1951
- void loadEventData({ force: true });
2406
+ void loadEventData(true);
1952
2407
  }, [encryptionKey, loadEventData]);
1953
2408
  const createdAt = new Date(event.createdAt);
2409
+ const occurredAt = parseDateValue(event.occurredAt);
2410
+ const displayedCreatedAt = showSeparateEventOccurrenceTimestamps ? createdAt : getEffectiveEventDate(event);
2411
+ const displayedCreatedAtTime = displayedCreatedAt.toLocaleTimeString(void 0, {
2412
+ hour: "numeric",
2413
+ minute: "numeric",
2414
+ second: "numeric"
2415
+ });
1954
2416
  const displayPayload = isLoading ? loadedData : mergedDisplay;
1955
- 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) => {
2417
+ return jsxRuntimeExports.jsxs(CollapsibleRoot, { variant: "card", onOpenChange: canHaveData ? (open) => {
1956
2418
  if (open)
1957
2419
  handleExpand();
1958
- } : void 0, children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col divide-y rounded-md border overflow-hidden", style: {
1959
- borderColor: "var(--ds-gray-300)",
1960
- backgroundColor: "var(--ds-gray-100)"
1961
- }, 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: {
1962
- borderColor: "var(--ds-gray-300)"
1963
- }, 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 }) })] });
2420
+ } : 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 }) })] })] });
1964
2421
  }
1965
2422
  function hasOnlyExpiredFields(data, eventType) {
1966
2423
  if (data === null || typeof data !== "object" || Array.isArray(data)) {
1967
2424
  return false;
1968
2425
  }
1969
2426
  const record = data;
1970
- const refKeys = EVENT_DATA_REF_FIELDS[eventType] ?? [];
2427
+ const refKeys = getEventDataRefFields(eventType);
1971
2428
  const presentKeys = refKeys.filter((k) => k in record);
1972
2429
  return presentKeys.length > 0 && presentKeys.every((k) => isExpiredMarker(record[k]));
1973
2430
  }
@@ -1979,79 +2436,199 @@ function EventDataBlock({ eventType, data }) {
1979
2436
  color: "var(--ds-gray-700)"
1980
2437
  }, children: jsxRuntimeExports.jsx("span", { className: "font-medium", children: "Data expired" }) });
1981
2438
  }
1982
- if (ERROR_EVENT_TYPES.has(eventType) && data != null && typeof data === "object") {
2439
+ if (hasEncryptedFields({ eventType, eventData: data })) {
2440
+ return jsxRuntimeExports.jsx(EncryptedDataBlock, {});
2441
+ }
2442
+ if (eventType === "attr_set") {
2443
+ return jsxRuntimeExports.jsx(AttrSetEventBlock, { data });
2444
+ }
2445
+ if (getEventDataRefFields(eventType).includes("error") && data != null && typeof data === "object") {
1983
2446
  const record = data;
1984
- if (isStructuredErrorWithStack(record.error)) {
2447
+ if (isStructuredError(record.error)) {
1985
2448
  return jsxRuntimeExports.jsx(ErrorStackBlock, { value: record.error });
1986
2449
  }
1987
- if (isStructuredErrorWithStack(record)) {
2450
+ if (isStructuredError(record)) {
1988
2451
  return jsxRuntimeExports.jsx(ErrorStackBlock, { value: record });
1989
2452
  }
1990
2453
  }
1991
2454
  return jsxRuntimeExports.jsx(CopyableDataBlock, { data });
1992
2455
  }
1993
- function EventsList({ events, isLoading = false, error, onLoadEventData, encryptionKey }) {
1994
- const sortedEvents = reactExports.useMemo(() => [...events].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()), [events]);
1995
- 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] });
2456
+ function EventsList({ events, isLoading = false, error, onLoadEventData, onStreamClick, onRunClick, encryptionKey, showSeparateEventOccurrenceTimestamps = false }) {
2457
+ const sortedEvents = reactExports.useMemo(() => [...events].sort((a, b) => getEffectiveEventDate(a).getTime() - getEffectiveEventDate(b).getTime()), [events]);
2458
+ const hasEvents = sortedEvents.length > 0 && !error;
2459
+ if (!hasEvents && !isLoading) {
2460
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Events", disabled: true });
2461
+ }
2462
+ 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)) }) })] }) }) });
2463
+ }
2464
+ const hasField = (value, key) => key in value;
2465
+ const RESOURCES_WITH_FETCHED_DETAIL = /* @__PURE__ */ new Set(["run", "step", "sleep"]);
2466
+ function resourceNeedsFetchedDetail(resource) {
2467
+ return resource !== void 0 && RESOURCES_WITH_FETCHED_DETAIL.has(resource);
2468
+ }
2469
+ function deriveSpanDetailView(args) {
2470
+ const { resource, resourceId, inlineData, fetchedDetail, fetchedError } = args;
2471
+ const matchedDetail = spanDetailMatchesSelection(fetchedDetail, resource, resourceId) ? fetchedDetail : null;
2472
+ const displayData = mergeSpanDetail(inlineData, matchedDetail);
2473
+ if (!resource || !resourceId) {
2474
+ return { status: "idle", displayData, detail: null, error: void 0 };
2475
+ }
2476
+ if (fetchedError) {
2477
+ return {
2478
+ status: "error",
2479
+ displayData,
2480
+ detail: matchedDetail,
2481
+ error: fetchedError
2482
+ };
2483
+ }
2484
+ if (!resourceNeedsFetchedDetail(resource)) {
2485
+ return {
2486
+ status: "ready",
2487
+ displayData,
2488
+ detail: matchedDetail,
2489
+ error: void 0
2490
+ };
2491
+ }
2492
+ if (matchedDetail !== null) {
2493
+ return {
2494
+ status: "ready",
2495
+ displayData,
2496
+ detail: matchedDetail,
2497
+ error: void 0
2498
+ };
2499
+ }
2500
+ return { status: "loading", displayData, detail: null, error: void 0 };
2501
+ }
2502
+ function spanDetailMatchesSelection(detail, resource, resourceId) {
2503
+ if (!detail || typeof detail !== "object" || !resource || !resourceId) {
2504
+ return false;
2505
+ }
2506
+ switch (resource) {
2507
+ case "step":
2508
+ return hasField(detail, "stepId") && detail.stepId === resourceId;
2509
+ case "hook":
2510
+ return hasField(detail, "hookId") && detail.hookId === resourceId;
2511
+ case "sleep":
2512
+ return hasField(detail, "waitId") && detail.waitId === resourceId;
2513
+ case "run":
2514
+ return hasField(detail, "runId") && !("stepId" in detail) && !("hookId" in detail) && !("waitId" in detail) && detail.runId === resourceId;
2515
+ default:
2516
+ return false;
2517
+ }
2518
+ }
2519
+ function mergeSpanDetail(spanData, detail) {
2520
+ if (!detail || typeof detail !== "object") {
2521
+ return spanData;
2522
+ }
2523
+ if (!spanData || typeof spanData !== "object") {
2524
+ return detail;
2525
+ }
2526
+ const merged = { ...detail };
2527
+ for (const [key, value] of Object.entries(spanData)) {
2528
+ if (value !== void 0) {
2529
+ merged[key] = value;
2530
+ }
2531
+ }
2532
+ return merged;
1996
2533
  }
1997
- function isStep(data) {
1998
- return data !== null && typeof data === "object" && "stepId" in data;
2534
+ function deriveSpanSelection(selectedSpan) {
2535
+ if (!selectedSpan)
2536
+ return null;
2537
+ const { resource, data } = selectedSpan;
2538
+ if (resource === "step" && data && typeof data === "object" && "stepId" in data) {
2539
+ const step = data;
2540
+ return { resource: "step", resourceId: step.stepId, runId: step.runId };
2541
+ }
2542
+ if (resource === "run" && data && typeof data === "object" && "runId" in data) {
2543
+ return { resource: "run", resourceId: data.runId };
2544
+ }
2545
+ if (resource === "hook" && data && typeof data === "object" && "hookId" in data) {
2546
+ return { resource: "hook", resourceId: data.hookId };
2547
+ }
2548
+ if (resource === "sleep") {
2549
+ if (!selectedSpan.spanId)
2550
+ return null;
2551
+ const waitData = data;
2552
+ return {
2553
+ resource: "sleep",
2554
+ resourceId: selectedSpan.spanId,
2555
+ runId: waitData == null ? void 0 : waitData.runId
2556
+ };
2557
+ }
2558
+ return null;
1999
2559
  }
2000
- function isWorkflowRun(data) {
2001
- return data !== null && typeof data === "object" && "runId" in data;
2560
+ function useSelectedSpanDetail(selectedSpan, fetchSpanDetail) {
2561
+ const selection = reactExports.useMemo(() => deriveSpanSelection(selectedSpan), [selectedSpan]);
2562
+ const resource = selection == null ? void 0 : selection.resource;
2563
+ const resourceId = selection == null ? void 0 : selection.resourceId;
2564
+ const runId = selection == null ? void 0 : selection.runId;
2565
+ const selectionKey = resource && resourceId ? `${resource}:${resourceId}` : null;
2566
+ const needsFetch = resourceNeedsFetchedDetail(resource);
2567
+ const [fetched, setFetched] = reactExports.useState({ detail: null, error: null, errorKey: null });
2568
+ const tokenRef = reactExports.useRef(0);
2569
+ const selectionRef = reactExports.useRef(selection);
2570
+ selectionRef.current = selection;
2571
+ reactExports.useEffect(() => {
2572
+ if (!selectionKey || !needsFetch) {
2573
+ return;
2574
+ }
2575
+ const activeSelection = selectionRef.current;
2576
+ if (!activeSelection) {
2577
+ return;
2578
+ }
2579
+ const token = ++tokenRef.current;
2580
+ fetchSpanDetail(activeSelection).then((detail) => {
2581
+ if (tokenRef.current !== token)
2582
+ return;
2583
+ setFetched({ detail, error: null, errorKey: null });
2584
+ }).catch((err) => {
2585
+ if (tokenRef.current !== token)
2586
+ return;
2587
+ setFetched({
2588
+ detail: null,
2589
+ error: err instanceof Error ? err : new Error(String(err)),
2590
+ errorKey: selectionKey
2591
+ });
2592
+ });
2593
+ }, [selectionKey, runId, needsFetch, fetchSpanDetail]);
2594
+ const scopedError = fetched.errorKey === selectionKey ? fetched.error : null;
2595
+ const view = reactExports.useMemo(() => deriveSpanDetailView({
2596
+ resource,
2597
+ resourceId,
2598
+ inlineData: selectedSpan == null ? void 0 : selectedSpan.data,
2599
+ fetchedDetail: fetched.detail,
2600
+ fetchedError: scopedError
2601
+ }), [resource, resourceId, selectedSpan == null ? void 0 : selectedSpan.data, fetched.detail, scopedError]);
2602
+ return {
2603
+ status: view.status,
2604
+ resource,
2605
+ resourceId,
2606
+ runId,
2607
+ displayData: view.displayData,
2608
+ detail: view.detail,
2609
+ error: view.error
2610
+ };
2002
2611
  }
2003
2612
  function isHook(data) {
2004
2613
  return data !== null && typeof data === "object" && "hookId" in data;
2005
2614
  }
2006
- function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spanDetailError, spanDetailLoading, onSpanSelect, onWakeUpSleep, onLoadEventData, onResolveHook, encryptionKey, onDecrypt, isDecrypting = false, selectedSpan }) {
2615
+ function EntityDetailPanel({ run, onStreamClick, onRunClick, fetchSpanDetail, onWakeUpSleep, onLoadEventData, onResolveHook, encryptionKey, onDecrypt, isDecrypting = false, selectedSpan, showSeparateEventOccurrenceTimestamps = false, getModuleSourceUrl }) {
2007
2616
  const toast2 = useToast();
2008
2617
  const [stoppingSleep, setStoppingSleep] = reactExports.useState(false);
2009
2618
  const [showResolveHookModal, setShowResolveHookModal] = reactExports.useState(false);
2010
2619
  const [resolvingHook, setResolvingHook] = reactExports.useState(false);
2011
2620
  const [resolvedHookIds, setResolvedHookIds] = reactExports.useState(/* @__PURE__ */ new Set());
2621
+ const sidebar = useSidebarDataOptional();
2622
+ const hasEncryptedData = Boolean((sidebar == null ? void 0 : sidebar.hasEncryptedData) && !encryptionKey);
2012
2623
  const data = selectedSpan == null ? void 0 : selectedSpan.data;
2013
2624
  const rawEvents = selectedSpan == null ? void 0 : selectedSpan.rawEvents;
2014
2625
  const rawEventsLength = (rawEvents == null ? void 0 : rawEvents.length) ?? 0;
2015
- const { resource, resourceId, runId } = reactExports.useMemo(() => {
2016
- if (!selectedSpan) {
2017
- return { resource: void 0, resourceId: void 0, runId: void 0 };
2018
- }
2019
- const res = selectedSpan.resource;
2020
- if (res === "step" && isStep(data)) {
2021
- return { resource: "step", resourceId: data.stepId, runId: data.runId };
2022
- }
2023
- if (res === "run" && isWorkflowRun(data)) {
2024
- return { resource: "run", resourceId: data.runId, runId: void 0 };
2025
- }
2026
- if (res === "hook" && isHook(data)) {
2027
- return { resource: "hook", resourceId: data.hookId, runId: void 0 };
2028
- }
2029
- if (res === "sleep") {
2030
- const waitData = data;
2031
- return {
2032
- resource: "sleep",
2033
- resourceId: selectedSpan.spanId,
2034
- runId: waitData == null ? void 0 : waitData.runId
2035
- };
2036
- }
2037
- return { resource: void 0, resourceId: void 0, runId: void 0 };
2038
- }, [selectedSpan, data]);
2039
- const onSpanSelectRef = reactExports.useRef(onSpanSelect);
2040
- onSpanSelectRef.current = onSpanSelect;
2041
- reactExports.useEffect(() => {
2042
- if (resource && resourceId && ["run", "step", "hook", "sleep"].includes(resource)) {
2043
- onSpanSelectRef.current({
2044
- resource,
2045
- resourceId,
2046
- runId
2047
- });
2048
- }
2049
- }, [resource, resourceId, runId]);
2626
+ const { status, resource, resourceId, displayData, detail: matchedSpanDetailData, error } = useSelectedSpanDetail(selectedSpan, fetchSpanDetail);
2627
+ const loading = status === "loading";
2050
2628
  const canWakeUp = reactExports.useMemo(() => {
2051
2629
  if (resource !== "sleep" || !rawEvents)
2052
2630
  return false;
2053
- const terminalStates = ["completed", "failed", "cancelled"];
2054
- if (terminalStates.includes(run.status))
2631
+ if (isTerminalWorkflowRunStatus(run.status))
2055
2632
  return false;
2056
2633
  const hasWaitCreated = rawEvents.some((e) => e.eventType === "wait_created");
2057
2634
  if (!hasWaitCreated)
@@ -2064,8 +2641,7 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2064
2641
  return false;
2065
2642
  if (resolvedHookIds.has(resourceId))
2066
2643
  return false;
2067
- const terminalStates = ["completed", "failed", "cancelled"];
2068
- if (terminalStates.includes(run.status))
2644
+ if (isTerminalWorkflowRunStatus(run.status))
2069
2645
  return false;
2070
2646
  const hasHookDisposed = rawEvents.some((e) => e.eventType === "hook_disposed");
2071
2647
  if (hasHookDisposed)
@@ -2079,19 +2655,17 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2079
2655
  run.status,
2080
2656
  resolvedHookIds
2081
2657
  ]);
2082
- const error = spanDetailError ?? void 0;
2083
- const loading = spanDetailLoading ?? false;
2084
2658
  const hookToken = reactExports.useMemo(() => {
2085
2659
  if (resource !== "hook" || !resourceId)
2086
2660
  return void 0;
2087
- if (isHook(spanDetailData) && spanDetailData.token) {
2088
- return spanDetailData.token;
2661
+ if (isHook(matchedSpanDetailData) && matchedSpanDetailData.token) {
2662
+ return matchedSpanDetailData.token;
2089
2663
  }
2090
2664
  if (isHook(data) && data.token) {
2091
2665
  return data.token;
2092
2666
  }
2093
2667
  return void 0;
2094
- }, [resource, resourceId, spanDetailData, data]);
2668
+ }, [resource, resourceId, matchedSpanDetailData, data]);
2095
2669
  reactExports.useEffect(() => {
2096
2670
  if (error && selectedSpan && resource) {
2097
2671
  toast2.error(`Failed to load ${resource} details`, {
@@ -2146,7 +2720,7 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2146
2720
  }
2147
2721
  try {
2148
2722
  setResolvingHook(true);
2149
- const candidate = spanDetailData ?? data;
2723
+ const candidate = matchedSpanDetailData ?? data;
2150
2724
  const hook = isHook(candidate) ? candidate : void 0;
2151
2725
  await onResolveHook(hookToken, payload, hook);
2152
2726
  toast2.success("Hook resolved", {
@@ -2164,8 +2738,7 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2164
2738
  } finally {
2165
2739
  setResolvingHook(false);
2166
2740
  }
2167
- }, [onResolveHook, hookToken, resolvingHook, spanDetailData, data]);
2168
- const displayData = spanDetailData ?? data;
2741
+ }, [onResolveHook, hookToken, resolvingHook, matchedSpanDetailData, data]);
2169
2742
  const moduleSpecifier = reactExports.useMemo(() => {
2170
2743
  const displayRecord = displayData;
2171
2744
  const displayStepName = displayRecord.stepName;
@@ -2181,11 +2754,23 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2181
2754
  }
2182
2755
  return void 0;
2183
2756
  }, [displayData, run.workflowName]);
2757
+ const moduleSourceUrl = reactExports.useMemo(() => {
2758
+ if (!getModuleSourceUrl || !moduleSpecifier)
2759
+ return void 0;
2760
+ const parsed = parseStepName(moduleSpecifier) ?? parseWorkflowName(moduleSpecifier);
2761
+ if (!parsed)
2762
+ return void 0;
2763
+ const dataDeploymentId = displayData.deploymentId;
2764
+ return getModuleSourceUrl({
2765
+ moduleSpecifier: parsed.moduleSpecifier,
2766
+ deploymentId: typeof dataDeploymentId === "string" ? dataDeploymentId : run.deploymentId
2767
+ });
2768
+ }, [getModuleSourceUrl, moduleSpecifier, displayData, run.deploymentId]);
2184
2769
  if (!selectedSpan || !resource || !resourceId) {
2185
2770
  return null;
2186
2771
  }
2187
2772
  const hasPendingActions = resource === "sleep" && canWakeUp || resource === "hook" && canResolveHook;
2188
- 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: {
2773
+ 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: {
2189
2774
  borderColor: "var(--ds-gray-300)",
2190
2775
  backgroundColor: "var(--ds-gray-100)"
2191
2776
  }, 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: {
@@ -2194,125 +2779,319 @@ function EntityDetailPanel({ run, onStreamClick, onRunClick, spanDetailData, spa
2194
2779
  }, 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: {
2195
2780
  background: "var(--ds-gray-1000)",
2196
2781
  color: "var(--ds-background-100)"
2197
- }, 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 })] });
2782
+ }, 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 })] });
2198
2783
  }
2199
- function CopyButton({ copyText, ariaLabel, className }) {
2200
- const [copied, setCopied] = reactExports.useState(false);
2201
- const timeoutRef = reactExports.useRef(null);
2784
+ function useIntersectionObserver(elementRef, { threshold = 0, rootRef, rootMargin = "0%", freezeOnceVisible = false } = {}) {
2785
+ const [entry, setEntry] = reactExports.useState();
2786
+ const frozen = (entry == null ? void 0 : entry.isIntersecting) && freezeOnceVisible;
2202
2787
  reactExports.useEffect(() => {
2203
- return () => {
2204
- if (timeoutRef.current) {
2205
- clearTimeout(timeoutRef.current);
2206
- }
2788
+ const node = elementRef == null ? void 0 : elementRef.current;
2789
+ const hasIOSupport = !!window.IntersectionObserver;
2790
+ if (!hasIOSupport || frozen || !node)
2791
+ return;
2792
+ const updateEntry = ([entry2]) => {
2793
+ setEntry(entry2);
2207
2794
  };
2208
- }, []);
2209
- 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) => {
2210
- e.stopPropagation();
2211
- if (timeoutRef.current) {
2212
- clearTimeout(timeoutRef.current);
2213
- }
2214
- void navigator.clipboard.writeText(copyText).then(() => {
2215
- setCopied(true);
2216
- timeoutRef.current = setTimeout(() => setCopied(false), 1e3);
2795
+ const observer = new IntersectionObserver(updateEntry, {
2796
+ threshold,
2797
+ root: (rootRef == null ? void 0 : rootRef.current) ?? null,
2798
+ rootMargin
2217
2799
  });
2218
- }, 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" }) })] }) });
2800
+ observer.observe(node);
2801
+ return () => observer.disconnect();
2802
+ }, [elementRef, threshold, rootRef, rootMargin, frozen]);
2803
+ return entry;
2219
2804
  }
2220
- const GUTTER_PX = 9;
2221
- const MIN_PX = 50;
2222
- const DEFAULT_START_PX = 340;
2223
- function SplitPane({ children, className, defaultStartWidth = DEFAULT_START_PX, startHeader, endHeader }) {
2224
- const parts = reactExports.Children.toArray(children);
2225
- if (parts.length !== 2) {
2226
- throw new Error("SplitPane expects exactly two children");
2805
+ function useLoadMoreOnScroll(loadMore, { hasMore, isLoadingMore, rootRef, rootMargin = "400px" }) {
2806
+ const sentinelRef = reactExports.useRef(null);
2807
+ const entry = useIntersectionObserver(sentinelRef, { rootRef, rootMargin });
2808
+ const loadMoreRef = reactExports.useRef(loadMore);
2809
+ reactExports.useEffect(() => {
2810
+ loadMoreRef.current = loadMore;
2811
+ }, [loadMore]);
2812
+ reactExports.useEffect(() => {
2813
+ if ((entry == null ? void 0 : entry.isIntersecting) && hasMore && !isLoadingMore) {
2814
+ loadMoreRef.current();
2815
+ }
2816
+ }, [entry == null ? void 0 : entry.isIntersecting, hasMore, isLoadingMore]);
2817
+ return sentinelRef;
2818
+ }
2819
+ const kbdVariants = cva("inline-flex items-center justify-center rounded px-1 text-center font-sans", {
2820
+ variants: {
2821
+ variant: {
2822
+ subtle: "bg-background-100/20",
2823
+ outline: "border border-gray-alpha-400 bg-background-100 font-medium text-gray-900"
2824
+ },
2825
+ size: {
2826
+ default: "h-5 min-w-5 text-xs leading-none",
2827
+ compact: "h-4 min-h-4 min-w-4 text-[11px] leading-none",
2828
+ search: "h-5 min-h-5 min-w-5 text-[13px] leading-[1.7em]"
2829
+ }
2830
+ },
2831
+ defaultVariants: {
2832
+ variant: "subtle",
2833
+ size: "default"
2227
2834
  }
2228
- const [start, end] = parts;
2229
- const [startPx, setStartPx] = reactExports.useState(defaultStartWidth);
2835
+ });
2836
+ function Kbd({ children, className, variant, size, ...props }) {
2837
+ return jsxRuntimeExports.jsx("kbd", { className: cn(kbdVariants({ variant, size, className })), ...props, children });
2838
+ }
2839
+ const PANEL_MIN_WIDTH = 280;
2840
+ const PANEL_HARD_MAX_WIDTH = 1300;
2841
+ const PANEL_DEFAULT_WIDTH = 360;
2842
+ const MAIN_MIN_WIDTH = 400;
2843
+ const PANEL_WIDTH_STORAGE_KEY = "workflow-trace-detail-panel-width";
2844
+ function clamp(value, min, max) {
2845
+ return Math.min(Math.max(value, min), max);
2846
+ }
2847
+ function computeMaxPanelWidth(availableWidth) {
2848
+ if (availableWidth <= 0)
2849
+ return PANEL_HARD_MAX_WIDTH;
2850
+ return Math.min(Math.max(availableWidth - MAIN_MIN_WIDTH, 0), PANEL_HARD_MAX_WIDTH);
2851
+ }
2852
+ function clampPanelWidth(width, availableWidth) {
2853
+ return clamp(width, PANEL_MIN_WIDTH, computeMaxPanelWidth(availableWidth));
2854
+ }
2855
+ function readStoredPanelWidth() {
2856
+ if (typeof window === "undefined")
2857
+ return PANEL_DEFAULT_WIDTH;
2858
+ try {
2859
+ const raw = window.localStorage.getItem(PANEL_WIDTH_STORAGE_KEY);
2860
+ const value = raw ? Number(raw) : Number.NaN;
2861
+ if (!Number.isFinite(value))
2862
+ return PANEL_DEFAULT_WIDTH;
2863
+ return clamp(value, PANEL_MIN_WIDTH, PANEL_HARD_MAX_WIDTH);
2864
+ } catch {
2865
+ return PANEL_DEFAULT_WIDTH;
2866
+ }
2867
+ }
2868
+ function writeStoredPanelWidth(width) {
2869
+ if (typeof window === "undefined")
2870
+ return;
2871
+ try {
2872
+ window.localStorage.setItem(PANEL_WIDTH_STORAGE_KEY, String(Math.round(width)));
2873
+ } catch {
2874
+ }
2875
+ }
2876
+ function DraggableBorder({ element, position, onWidthChange, onReset, "aria-label": ariaLabel, "aria-controls": ariaControls, "aria-valuemin": ariaValueMin, "aria-valuemax": ariaValueMax, "aria-valuenow": ariaValueNow }) {
2230
2877
  const [isDragging, setIsDragging] = reactExports.useState(false);
2231
- const containerRef = reactExports.useRef(null);
2232
- const gutterRef = reactExports.useRef(null);
2233
- const rafRef = reactExports.useRef(null);
2234
- const pendingPx = reactExports.useRef(defaultStartWidth);
2235
2878
  const pointerIdRef = reactExports.useRef(null);
2879
+ const startXRef = reactExports.useRef(0);
2880
+ const startWidthRef = reactExports.useRef(0);
2881
+ const pendingWidthRef = reactExports.useRef(0);
2882
+ const rafRef = reactExports.useRef(null);
2883
+ const movedRef = reactExports.useRef(false);
2884
+ const lastDragEndAtRef = reactExports.useRef(0);
2236
2885
  reactExports.useEffect(() => {
2237
2886
  return () => {
2238
2887
  if (rafRef.current)
2239
2888
  cancelAnimationFrame(rafRef.current);
2240
2889
  };
2241
- }, []);
2242
- const clampPx = reactExports.useCallback((px) => {
2243
- const el = containerRef.current;
2244
- if (!el)
2245
- return px;
2246
- const maxPx = el.getBoundingClientRect().width - MIN_PX - GUTTER_PX;
2247
- return Math.min(maxPx, Math.max(MIN_PX, px));
2248
- }, []);
2890
+ }, []);
2891
+ reactExports.useEffect(() => {
2892
+ if (!isDragging)
2893
+ return;
2894
+ const previous = document.body.style.userSelect;
2895
+ document.body.style.userSelect = "none";
2896
+ return () => {
2897
+ document.body.style.userSelect = previous;
2898
+ };
2899
+ }, [isDragging]);
2900
+ const stopDrag = () => {
2901
+ pointerIdRef.current = null;
2902
+ setIsDragging(false);
2903
+ if (rafRef.current) {
2904
+ cancelAnimationFrame(rafRef.current);
2905
+ rafRef.current = null;
2906
+ if (movedRef.current)
2907
+ onWidthChange(pendingWidthRef.current);
2908
+ }
2909
+ if (movedRef.current) {
2910
+ movedRef.current = false;
2911
+ lastDragEndAtRef.current = performance.now();
2912
+ }
2913
+ };
2914
+ const handlePointerDown = (e) => {
2915
+ if (pointerIdRef.current !== null)
2916
+ return;
2917
+ const el = element.current;
2918
+ if (!el)
2919
+ return;
2920
+ if (e.pointerType === "mouse" && e.button !== 0)
2921
+ return;
2922
+ e.currentTarget.setPointerCapture(e.pointerId);
2923
+ pointerIdRef.current = e.pointerId;
2924
+ startXRef.current = e.clientX;
2925
+ startWidthRef.current = el.offsetWidth;
2926
+ movedRef.current = false;
2927
+ setIsDragging(true);
2928
+ };
2929
+ const handlePointerMove = (e) => {
2930
+ if (pointerIdRef.current !== e.pointerId)
2931
+ return;
2932
+ if (Math.abs(e.clientX - startXRef.current) > 4) {
2933
+ movedRef.current = true;
2934
+ }
2935
+ const delta = position === "left" ? startXRef.current - e.clientX : e.clientX - startXRef.current;
2936
+ pendingWidthRef.current = startWidthRef.current + delta;
2937
+ if (!rafRef.current) {
2938
+ rafRef.current = requestAnimationFrame(() => {
2939
+ rafRef.current = null;
2940
+ onWidthChange(pendingWidthRef.current);
2941
+ });
2942
+ }
2943
+ };
2944
+ const handlePointerEnd = (e) => {
2945
+ if (pointerIdRef.current !== e.pointerId)
2946
+ return;
2947
+ stopDrag();
2948
+ };
2949
+ const handleLostPointerCapture = (e) => {
2950
+ if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current) {
2951
+ return;
2952
+ }
2953
+ stopDrag();
2954
+ };
2955
+ const handleDoubleClick = () => {
2956
+ if (movedRef.current || performance.now() - lastDragEndAtRef.current < 500) {
2957
+ return;
2958
+ }
2959
+ onReset == null ? void 0 : onReset();
2960
+ };
2961
+ const handleKeyDown = (e) => {
2962
+ const el = element.current;
2963
+ if (!el)
2964
+ return;
2965
+ const step = e.shiftKey ? 64 : 16;
2966
+ const grow = position === "left" ? 1 : -1;
2967
+ let next = null;
2968
+ if (e.key === "ArrowLeft") {
2969
+ next = el.offsetWidth + grow * step;
2970
+ } else if (e.key === "ArrowRight") {
2971
+ next = el.offsetWidth - grow * step;
2972
+ } else if (e.key === "Home" && ariaValueMin !== void 0) {
2973
+ next = ariaValueMin;
2974
+ } else if (e.key === "End" && ariaValueMax !== void 0) {
2975
+ next = ariaValueMax;
2976
+ }
2977
+ if (next === null)
2978
+ return;
2979
+ e.preventDefault();
2980
+ onWidthChange(next);
2981
+ };
2982
+ 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") }) });
2983
+ }
2984
+ function useElementWidth(ref, enabled = true) {
2985
+ const [width, setWidth] = reactExports.useState(0);
2986
+ reactExports.useLayoutEffect(() => {
2987
+ if (!enabled)
2988
+ return;
2989
+ const el = ref.current;
2990
+ if (!el)
2991
+ return;
2992
+ setWidth(Math.round(el.getBoundingClientRect().width));
2993
+ const observer = new ResizeObserver(([entry]) => {
2994
+ setWidth(Math.round((entry == null ? void 0 : entry.contentRect.width) ?? 0));
2995
+ });
2996
+ observer.observe(el);
2997
+ return () => observer.disconnect();
2998
+ }, [ref, enabled]);
2999
+ return width;
3000
+ }
3001
+ const DETAIL_PANEL_ID = "trace-detail-panel";
3002
+ function useSelectedSpanInfo() {
3003
+ const { activeSpan } = useActiveSpan();
3004
+ const sidebar = useSidebarData();
3005
+ return reactExports.useMemo(() => {
3006
+ var _a, _b;
3007
+ if (!activeSpan)
3008
+ return null;
3009
+ const resource = (_a = activeSpan.attributes) == null ? void 0 : _a.resource;
3010
+ const rawEvents = filterSpanRawEvents(sidebar.events, resource, activeSpan.spanId);
3011
+ return {
3012
+ data: (_b = activeSpan.attributes) == null ? void 0 : _b.data,
3013
+ resource,
3014
+ spanId: activeSpan.spanId,
3015
+ rawEvents
3016
+ };
3017
+ }, [activeSpan, sidebar]);
3018
+ }
3019
+ function TraceDetailPanel({ containerRef, onNavigateToSpan, onClose }) {
3020
+ const { activeSpan, activeSpanId, spans } = useActiveSpan();
3021
+ const sidebar = useSidebarData();
3022
+ const selectedSpan = useSelectedSpanInfo();
3023
+ const isOpen = Boolean(activeSpan);
3024
+ const [storedWidth, setStoredWidth] = reactExports.useState(() => readStoredPanelWidth());
3025
+ const containerWidth = useElementWidth(containerRef, isOpen);
3026
+ const asideRef = reactExports.useRef(null);
3027
+ const handleResize = reactExports.useCallback((next) => {
3028
+ const clamped = clampPanelWidth(next, containerWidth);
3029
+ setStoredWidth(clamped);
3030
+ writeStoredPanelWidth(clamped);
3031
+ }, [containerWidth]);
3032
+ const panelWidth = clampPanelWidth(storedWidth, containerWidth);
3033
+ const panelMaxWidth = Math.max(PANEL_MIN_WIDTH, computeMaxPanelWidth(containerWidth));
3034
+ const { prevSpanId, nextSpanId } = reactExports.useMemo(() => {
3035
+ var _a, _b;
3036
+ if (!activeSpanId)
3037
+ return { prevSpanId: null, nextSpanId: null };
3038
+ const i = spans.findIndex((s) => s.spanId === activeSpanId);
3039
+ if (i === -1)
3040
+ return { prevSpanId: null, nextSpanId: null };
3041
+ return {
3042
+ prevSpanId: ((_a = spans[i - 1]) == null ? void 0 : _a.spanId) ?? null,
3043
+ nextSpanId: ((_b = spans[i + 1]) == null ? void 0 : _b.spanId) ?? null
3044
+ };
3045
+ }, [activeSpanId, spans]);
3046
+ const handleSelectPrev = reactExports.useCallback(() => {
3047
+ if (prevSpanId)
3048
+ onNavigateToSpan(prevSpanId);
3049
+ }, [prevSpanId, onNavigateToSpan]);
3050
+ const handleSelectNext = reactExports.useCallback(() => {
3051
+ if (nextSpanId)
3052
+ onNavigateToSpan(nextSpanId);
3053
+ }, [nextSpanId, onNavigateToSpan]);
2249
3054
  reactExports.useEffect(() => {
2250
- if (!isDragging)
3055
+ if (!isOpen)
2251
3056
  return;
2252
- const onPointerMove = (e) => {
2253
- if (e.pointerId !== pointerIdRef.current)
2254
- return;
2255
- const container = containerRef.current;
2256
- if (!container)
3057
+ const onKeyDown = (e) => {
3058
+ if (e.key !== "j" && e.key !== "k" && e.key !== "ArrowDown" && e.key !== "ArrowUp") {
2257
3059
  return;
2258
- const rect = container.getBoundingClientRect();
2259
- pendingPx.current = clampPx(e.clientX - rect.left);
2260
- if (!rafRef.current) {
2261
- rafRef.current = requestAnimationFrame(() => {
2262
- rafRef.current = null;
2263
- setStartPx(pendingPx.current);
2264
- });
2265
3060
  }
2266
- };
2267
- const onPointerUp = (e) => {
2268
- if (e.pointerId !== pointerIdRef.current)
3061
+ const target = e.target;
3062
+ if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || (target == null ? void 0 : target.isContentEditable)) {
2269
3063
  return;
2270
- const gutter2 = gutterRef.current;
2271
- if (gutter2 == null ? void 0 : gutter2.hasPointerCapture(e.pointerId)) {
2272
- gutter2.releasePointerCapture(e.pointerId);
2273
3064
  }
2274
- if (rafRef.current) {
2275
- cancelAnimationFrame(rafRef.current);
2276
- rafRef.current = null;
3065
+ const targetId = e.key === "k" || e.key === "ArrowUp" ? prevSpanId : nextSpanId;
3066
+ if (targetId) {
3067
+ e.preventDefault();
3068
+ onNavigateToSpan(targetId);
2277
3069
  }
2278
- pointerIdRef.current = null;
2279
- setIsDragging(false);
2280
3070
  };
2281
- document.addEventListener("pointermove", onPointerMove);
2282
- document.addEventListener("pointerup", onPointerUp);
2283
- document.addEventListener("pointercancel", onPointerUp);
2284
- return () => {
2285
- document.removeEventListener("pointermove", onPointerMove);
2286
- document.removeEventListener("pointerup", onPointerUp);
2287
- document.removeEventListener("pointercancel", onPointerUp);
2288
- };
2289
- }, [isDragging, clampPx]);
2290
- const handlePointerDown = (e) => {
2291
- e.currentTarget.setPointerCapture(e.pointerId);
2292
- pointerIdRef.current = e.pointerId;
2293
- setIsDragging(true);
2294
- };
2295
- const handleLostPointerCapture = () => {
2296
- pointerIdRef.current = null;
2297
- setIsDragging(false);
2298
- if (rafRef.current) {
2299
- cancelAnimationFrame(rafRef.current);
2300
- rafRef.current = null;
3071
+ window.addEventListener("keydown", onKeyDown);
3072
+ return () => window.removeEventListener("keydown", onKeyDown);
3073
+ }, [isOpen, prevSpanId, nextSpanId, onNavigateToSpan]);
3074
+ const selectedSpanName = reactExports.useMemo(() => {
3075
+ var _a, _b;
3076
+ if (!(selectedSpan == null ? void 0 : selectedSpan.data))
3077
+ return "Details";
3078
+ const data = selectedSpan.data;
3079
+ if (selectedSpan.resource === "hook") {
3080
+ return data.token ?? data.hookId;
2301
3081
  }
2302
- };
2303
- const colTemplate = `${startPx}px ${GUTTER_PX}px minmax(${MIN_PX}px, 1fr)`;
2304
- const hasHeaders = startHeader != null || endHeader != null;
2305
- 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 }) });
2306
- if (hasHeaders) {
2307
- 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 content-start overflow-x-hidden overflow-y-auto", isDragging && "select-none"), style: { gridTemplateColumns: colTemplate }, children: [start, gutter, end] })] });
2308
- }
2309
- 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] });
3082
+ const stepName = data.stepName;
3083
+ const workflowName = data.workflowName;
3084
+ 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";
3085
+ }, [selectedSpan == null ? void 0 : selectedSpan.data, selectedSpan == null ? void 0 : selectedSpan.resource]);
3086
+ if (!activeSpan)
3087
+ return null;
3088
+ 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 }) }) })] });
2310
3089
  }
2311
3090
  const WorkflowIcon = () => {
2312
- 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" }) });
3091
+ 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" }) });
2313
3092
  };
2314
3093
  const WebhookIcon = () => {
2315
- 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" }) });
3094
+ 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" }) });
2316
3095
  };
2317
3096
  const SleepIcon = () => {
2318
3097
  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" }) });
@@ -2323,13 +3102,14 @@ const StepForwardIcon = () => {
2323
3102
  const eventStyles = {
2324
3103
  run: { icon: WorkflowIcon, className: "text-blue-900" },
2325
3104
  step: { icon: StepForwardIcon, className: "text-green-900" },
2326
- hook: { icon: WebhookIcon, className: "text-yellow-900" },
3105
+ hook: { icon: WebhookIcon, className: "text-gray-900" },
2327
3106
  sleep: { icon: SleepIcon, className: "text-gray-900" }
2328
3107
  };
2329
3108
  const defaultStyle = {
2330
3109
  icon: Circle,
2331
3110
  className: "text-gray-900"
2332
3111
  };
3112
+ const ROW_HEIGHT_CLASS = "h-10";
2333
3113
  function getEventStyle(resource, isErrored) {
2334
3114
  const style = eventStyles[resource] ?? defaultStyle;
2335
3115
  return {
@@ -2337,30 +3117,110 @@ function getEventStyle(resource, isErrored) {
2337
3117
  className: cn(isErrored ? "text-red-900" : style.className)
2338
3118
  };
2339
3119
  }
2340
- const EventRow = ({ span, isSelected, onSelectSpan }) => {
2341
- const durationMs = getHighResInMs(span.duration);
2342
- const isErrored = span.attributes.data.status === "failed";
3120
+ const EventRow = ({ span, isSelected, isDimmed, onSelectSpan }) => {
3121
+ const durationMs = getSpanDurationMs(span);
3122
+ const isErrored = isSpanErrored(span);
2343
3123
  const { icon: Icon, className: tagClassName } = getEventStyle(span.resource, isErrored);
2344
- return jsxRuntimeExports.jsx("li", { className: "overflow-clip group", role: "treeitem", "aria-selected": isSelected, "aria-expanded": isSelected, "aria-level": 1, onClick: () => onSelectSpan(span.spanId), children: jsxRuntimeExports.jsxs("div", { className: "hover:bg-gray-100 group-aria-selected:bg-gray-100 group-aria-selected:hover:bg-gray-200 hover:aria-selected:bg-gray-100 rounded-sm px-2 h-[34px] py-1.5 flex", children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntimeExports.jsx("span", { className: tagClassName, children: jsxRuntimeExports.jsx(Icon, { className: "w-4 h-4" }) }), jsxRuntimeExports.jsx("span", { className: "text-label-14", children: span.name })] }), jsxRuntimeExports.jsx("div", { className: "ml-auto", children: jsxRuntimeExports.jsx("span", { className: "text-label-14 text-gray-900 tabular-nums", children: formatDuration(durationMs) }) })] }) });
3124
+ 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.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: formatDurationPrecise(durationMs) }) })] }) }) });
2345
3125
  };
2346
- const EventList = ({ spans, activeSpanId, onSelectSpan }) => {
2347
- return jsxRuntimeExports.jsx("ul", { id: "event-list", role: "tree", className: "block min-h-0 overflow-visible px-2 py-2", children: spans.map((span) => {
2348
- return jsxRuntimeExports.jsx(EventRow, { span, isSelected: span.spanId === activeSpanId, onSelectSpan }, span.spanId);
2349
- }) });
3126
+ const EventList = ({ spans, activeSpanId, searchResult, onSelectSpan }) => {
3127
+ const listRef = reactExports.useRef(null);
3128
+ const { start, end } = useRowWindow(listRef, spans.length, ROW_HEIGHT_PX);
3129
+ 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: {
3130
+ paddingTop: start * ROW_HEIGHT_PX,
3131
+ paddingBottom: (spans.length - end) * ROW_HEIGHT_PX
3132
+ }, children: spans.slice(start, end).map((span) => jsxRuntimeExports.jsx(EventRow, { span, isSelected: span.spanId === activeSpanId, isDimmed: isSpanDimmedBySearch(span.spanId, searchResult), onSelectSpan }, span.spanId)) });
3133
+ };
3134
+ const SPLIT_PANE_START_ID = "trace-split-start";
3135
+ function SplitPane({ children, className, startHeader, endHeader, scrollContainerRef }) {
3136
+ const parts = reactExports.Children.toArray(children);
3137
+ if (parts.length !== 2) {
3138
+ throw new Error("SplitPane expects exactly two children");
3139
+ }
3140
+ const [start, end] = parts;
3141
+ const [startPx, setStartPx] = reactExports.useState(DEFAULT_START_PX);
3142
+ const containerRef = reactExports.useRef(null);
3143
+ const startRef = reactExports.useRef(null);
3144
+ const containerWidth = useElementWidth(containerRef);
3145
+ const setContainerRef = reactExports.useCallback((node) => {
3146
+ containerRef.current = node;
3147
+ if (scrollContainerRef) {
3148
+ scrollContainerRef.current = node;
3149
+ }
3150
+ }, [scrollContainerRef]);
3151
+ const clampPx = reactExports.useCallback((px) => {
3152
+ const el = containerRef.current;
3153
+ if (!el)
3154
+ return px;
3155
+ const maxPx = el.getBoundingClientRect().width - MIN_PX - GUTTER_PX;
3156
+ return Math.min(maxPx, Math.max(MIN_PX, px));
3157
+ }, []);
3158
+ const handleWidthChange = reactExports.useCallback((next) => setStartPx(clampPx(next)), [clampPx]);
3159
+ const handleReset = reactExports.useCallback(() => setStartPx(clampPx(DEFAULT_START_PX)), [clampPx]);
3160
+ const effectiveStartPx = containerWidth > 0 ? Math.max(MIN_PX, Math.min(containerWidth - MIN_PX - GUTTER_PX, startPx)) : startPx;
3161
+ const colTemplate = paneColTemplate(effectiveStartPx);
3162
+ const maxStartPx = Math.max(MIN_PX, containerWidth > 0 ? containerWidth - MIN_PX - GUTTER_PX : startPx);
3163
+ 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: 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] })] });
3164
+ }
3165
+ const hint = "_hint_1dmsq_3";
3166
+ const styles = {
3167
+ hint
2350
3168
  };
2351
- function DetailPanel({ span, rootStart, onClose }) {
2352
- const startMs = getHighResInMs(span.startTime);
2353
- const durationMs = getHighResInMs(span.duration);
2354
- const offsetMs = startMs - rootStart;
2355
- 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" })] }) })] });
3169
+ const DISMISSALS_KEY = "workflow-step-shortcut-helper-dismissals";
3170
+ const DISMISSAL_LIMIT = 3;
3171
+ const HINT_ROTATION_MS = 8e3;
3172
+ function getAltKeyLabel() {
3173
+ if (typeof navigator === "undefined")
3174
+ return "Alt";
3175
+ return navigator.platform.toLowerCase().includes("mac") ? "Option" : "Alt";
3176
+ }
3177
+ function readDismissals() {
3178
+ try {
3179
+ const dismissals = Number.parseInt(window.localStorage.getItem(DISMISSALS_KEY) ?? "0", 10);
3180
+ return Number.isNaN(dismissals) ? 0 : dismissals;
3181
+ } catch {
3182
+ return 0;
3183
+ }
3184
+ }
3185
+ function AltHint() {
3186
+ return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: ["Hold", jsxRuntimeExports.jsx(Kbd, { variant: "outline", size: "compact", className: "mx-1", children: getAltKeyLabel() }), "to see delta between spans"] });
3187
+ }
3188
+ function NavHint() {
3189
+ 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"] });
3190
+ }
3191
+ function TraceShortcutHelper({ hasMultipleSpans, reducedMotion }) {
3192
+ const [visible, setVisible] = reactExports.useState(false);
3193
+ const [index, setIndex] = reactExports.useState(0);
3194
+ reactExports.useEffect(() => {
3195
+ setVisible(readDismissals() < DISMISSAL_LIMIT);
3196
+ }, []);
3197
+ reactExports.useEffect(() => {
3198
+ if (reducedMotion)
3199
+ return;
3200
+ const id = setInterval(() => {
3201
+ setIndex((i) => i === 0 ? 1 : 0);
3202
+ }, HINT_ROTATION_MS);
3203
+ return () => clearInterval(id);
3204
+ }, [reducedMotion]);
3205
+ if (!visible || !hasMultipleSpans)
3206
+ return null;
3207
+ const dismiss = () => {
3208
+ try {
3209
+ window.localStorage.setItem(DISMISSALS_KEY, String(readDismissals() + 1));
3210
+ } catch {
3211
+ }
3212
+ setVisible(false);
3213
+ };
3214
+ return jsxRuntimeExports.jsxs("div", { className: "group sticky bottom-3 z-10 ml-4 mb-3 hidden h-8 w-fit items-center gap-1 text-xs 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" }) })] });
2356
3215
  }
2357
3216
  const MIN_VIEWPORT_MS = 1e-3;
2358
- const TIMELINE_PADDING = 16;
3217
+ const ZOOM_DEBOUNCE_MS = 150;
2359
3218
  function useAnimatedViewport(initial) {
2360
3219
  const [viewport, setViewportState] = reactExports.useState(initial);
2361
3220
  const animRef = reactExports.useRef(null);
2362
3221
  const currentRef = reactExports.useRef(initial);
2363
3222
  currentRef.current = viewport;
3223
+ const reducedMotion = useReducedMotion();
2364
3224
  const cancel = reactExports.useCallback(() => {
2365
3225
  if (animRef.current) {
2366
3226
  cancelAnimationFrame(animRef.current.raf);
@@ -2369,11 +3229,16 @@ function useAnimatedViewport(initial) {
2369
3229
  }, []);
2370
3230
  const animateTo = reactExports.useCallback((target) => {
2371
3231
  cancel();
3232
+ if (reducedMotion) {
3233
+ currentRef.current = target;
3234
+ setViewportState(target);
3235
+ return;
3236
+ }
2372
3237
  const from = currentRef.current;
2373
3238
  const anim = { raf: 0, from, to: target, start: performance.now() };
2374
3239
  const tick = () => {
2375
- const t = Math.min((performance.now() - anim.start) / 150, 1);
2376
- const e = 1 - (1 - t) * (1 - t);
3240
+ const t = Math.min((performance.now() - anim.start) / 240, 1);
3241
+ const e = t < 0.5 ? 8 * t * t * t * t : 1 - (-2 * t + 2) ** 4 / 2;
2377
3242
  setViewportState({
2378
3243
  start: anim.from.start + (anim.to.start - anim.from.start) * e,
2379
3244
  end: anim.from.end + (anim.to.end - anim.from.end) * e
@@ -2385,7 +3250,7 @@ function useAnimatedViewport(initial) {
2385
3250
  };
2386
3251
  animRef.current = anim;
2387
3252
  anim.raf = requestAnimationFrame(tick);
2388
- }, [cancel]);
3253
+ }, [cancel, reducedMotion]);
2389
3254
  const setViewport = reactExports.useCallback((update) => {
2390
3255
  cancel();
2391
3256
  if (typeof update === "function") {
@@ -2402,38 +3267,26 @@ function useAnimatedViewport(initial) {
2402
3267
  reactExports.useEffect(() => cancel, [cancel]);
2403
3268
  return { viewport, setViewport, animateTo };
2404
3269
  }
2405
- function useSelectedSpanInfo() {
2406
- const { activeSpan } = useActiveSpan();
2407
- const sidebar = useSidebarDataOptional();
2408
- return reactExports.useMemo(() => {
2409
- var _a, _b;
2410
- if (!activeSpan || !sidebar)
2411
- return null;
2412
- const correlationId = activeSpan.spanId;
2413
- const rawEvents = correlationId ? sidebar.events.filter((e) => e.correlationId === correlationId) : [];
2414
- return {
2415
- data: (_a = activeSpan.attributes) == null ? void 0 : _a.data,
2416
- resource: (_b = activeSpan.attributes) == null ? void 0 : _b.resource,
2417
- spanId: activeSpan.spanId,
2418
- rawEvents
2419
- };
2420
- }, [activeSpan, sidebar == null ? void 0 : sidebar.events]);
2421
- }
2422
- function NewTraceViewer$1({ trace }) {
2423
- return jsxRuntimeExports.jsx(ActiveSpanProvider, { spans: trace.spans, children: jsxRuntimeExports.jsx(NewTraceViewerContent, { trace }) });
3270
+ function NewTraceViewer$1({ trace, onLoadMore, hasMore, isLoadingMore }) {
3271
+ return jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 300, children: jsxRuntimeExports.jsx(ActiveSpanProvider, { spans: trace.spans, children: jsxRuntimeExports.jsx(NewTraceViewerContent, { trace, onLoadMore, hasMore, isLoadingMore }) }) });
2424
3272
  }
2425
- function NewTraceViewerContent({ trace }) {
2426
- const { activeSpan, activeSpanId, setActiveSpan, clearActiveSpan } = useActiveSpan();
2427
- const sidebar = useSidebarDataOptional();
2428
- const selectedSpan = useSelectedSpanInfo();
3273
+ function NewTraceViewerContent({ trace, onLoadMore, hasMore, isLoadingMore }) {
3274
+ const { activeSpanId, setActiveSpan, clearActiveSpan } = useActiveSpan();
3275
+ const reducedMotion = useReducedMotion();
2429
3276
  const [searchQuery, setSearchQuery] = reactExports.useState("");
2430
- const filteredSpans = reactExports.useMemo(() => {
2431
- const q = searchQuery.trim().toLowerCase();
2432
- if (!q)
2433
- return trace.spans;
2434
- return trace.spans.filter((s) => s.name.toLowerCase().includes(q) || s.resource.toLowerCase().includes(q));
2435
- }, [trace.spans, searchQuery]);
3277
+ const deferredSearchQuery = reactExports.useDeferredValue(searchQuery);
3278
+ const paneRootRef = reactExports.useRef(null);
3279
+ const searchResult = reactExports.useMemo(() => searchSpans(trace.spans, deferredSearchQuery), [trace.spans, deferredSearchQuery]);
2436
3280
  const root = reactExports.useMemo(() => computeRootBounds(trace.spans), [trace.spans]);
3281
+ const scrollContainerRef = reactExports.useRef(null);
3282
+ const loadMore = reactExports.useCallback(() => {
3283
+ void (onLoadMore == null ? void 0 : onLoadMore());
3284
+ }, [onLoadMore]);
3285
+ const loadMoreSentinelRef = useLoadMoreOnScroll(loadMore, {
3286
+ hasMore: Boolean(onLoadMore && hasMore),
3287
+ isLoadingMore: Boolean(isLoadingMore),
3288
+ rootRef: scrollContainerRef
3289
+ });
2437
3290
  const { viewport, setViewport, animateTo } = useAnimatedViewport({
2438
3291
  start: root.startTime,
2439
3292
  end: root.startTime + root.duration
@@ -2454,30 +3307,47 @@ function NewTraceViewerContent({ trace }) {
2454
3307
  return prev;
2455
3308
  });
2456
3309
  prevRootRef.current = { start: newStart, end: newEnd };
2457
- }, [root.startTime, root.duration]);
3310
+ }, [root.startTime, root.duration, setViewport]);
2458
3311
  const viewDuration = viewport.end - viewport.start;
2459
- const compression = reactExports.useMemo(() => buildTimeCompression(trace.spans, viewport.start, viewport.end), [trace.spans, viewport.start, viewport.end]);
2460
- const isZoomed = viewport.start > root.startTime + 0.01 || viewport.end < root.startTime + root.duration - 0.01;
3312
+ const viewportRef = reactExports.useRef(viewport);
3313
+ viewportRef.current = viewport;
3314
+ const timeMarkers = reactExports.useMemo(() => computeTimeMarkers(viewDuration, viewport.start - root.startTime), [viewDuration, viewport.start, root.startTime]);
2461
3315
  const resetZoom = reactExports.useCallback(() => {
2462
3316
  animateTo({ start: root.startTime, end: root.startTime + root.duration });
2463
3317
  }, [animateTo, root.startTime, root.duration]);
2464
- const handleSelectSpan = reactExports.useCallback((spanId) => {
2465
- if (spanId === activeSpanId) {
2466
- clearActiveSpan();
2467
- return;
2468
- }
2469
- setActiveSpan(spanId);
3318
+ const clampToRoot = reactExports.useCallback((next) => clampViewportToRoot(next, root.startTime, root.endTime, MIN_VIEWPORT_MS), [root.startTime, root.endTime]);
3319
+ const handleRevealTime = reactExports.useCallback((timeMs) => {
3320
+ const { start, end } = viewportRef.current;
3321
+ const duration = end - start;
3322
+ animateTo(clampToRoot({
3323
+ start: timeMs - duration / 2,
3324
+ end: timeMs + duration / 2
3325
+ }));
3326
+ }, [animateTo, clampToRoot]);
3327
+ const ZOOM_FACTOR = 0.5;
3328
+ const zoomBy = reactExports.useCallback((factor) => {
3329
+ setViewport((prev) => {
3330
+ const center = (prev.start + prev.end) / 2;
3331
+ const newDuration = Math.max(MIN_VIEWPORT_MS, (prev.end - prev.start) * factor);
3332
+ return clampToRoot({
3333
+ start: center - newDuration / 2,
3334
+ end: center + newDuration / 2
3335
+ });
3336
+ });
3337
+ }, [setViewport, clampToRoot]);
3338
+ const zoomIn = reactExports.useCallback(() => zoomBy(ZOOM_FACTOR), [zoomBy]);
3339
+ const zoomOut = reactExports.useCallback(() => zoomBy(1 / ZOOM_FACTOR), [zoomBy]);
3340
+ const isAtMinZoom = viewDuration >= root.duration;
3341
+ const isAtMaxZoom = viewDuration <= MIN_VIEWPORT_MS;
3342
+ const focusViewportOnSpan = reactExports.useCallback((spanId) => {
2470
3343
  const span = trace.spans.find((s) => s.spanId === spanId);
2471
3344
  if (!span)
2472
3345
  return;
2473
3346
  const spanStart = getHighResInMs(span.startTime);
2474
3347
  const spanEnd = getHighResInMs(span.endTime);
2475
3348
  const spanDuration = spanEnd - spanStart;
2476
- const rootS = root.startTime;
2477
- const rootE = root.startTime + root.duration;
2478
- const rootD = root.duration;
2479
- if (spanDuration > rootD * 0.8) {
2480
- animateTo({ start: rootS, end: rootE });
3349
+ if (spanDuration > root.duration * 0.8) {
3350
+ animateTo({ start: root.startTime, end: root.endTime });
2481
3351
  return;
2482
3352
  }
2483
3353
  const padding = Math.max(spanDuration * 0.2, MIN_VIEWPORT_MS / 2);
@@ -2488,34 +3358,67 @@ function NewTraceViewerContent({ trace }) {
2488
3358
  newStart = center - MIN_VIEWPORT_MS / 2;
2489
3359
  newEnd = center + MIN_VIEWPORT_MS / 2;
2490
3360
  }
2491
- if (newStart < rootS) {
2492
- const duration = newEnd - newStart;
2493
- newStart = rootS;
2494
- newEnd = Math.min(rootE, rootS + duration);
2495
- }
2496
- if (newEnd > rootE) {
2497
- const duration = newEnd - newStart;
2498
- newEnd = rootE;
2499
- newStart = Math.max(rootS, rootE - duration);
2500
- }
2501
- animateTo({ start: newStart, end: newEnd });
3361
+ animateTo(clampToRoot({ start: newStart, end: newEnd }));
2502
3362
  }, [
2503
3363
  animateTo,
2504
- setActiveSpan,
2505
- clearActiveSpan,
2506
- activeSpanId,
2507
3364
  trace.spans,
2508
3365
  root.startTime,
2509
- root.duration
3366
+ root.endTime,
3367
+ root.duration,
3368
+ clampToRoot
3369
+ ]);
3370
+ const scrollSpanIntoView = reactExports.useCallback((spanId) => {
3371
+ var _a;
3372
+ const index = trace.spans.findIndex((s) => s.spanId === spanId);
3373
+ if (index === -1)
3374
+ return;
3375
+ const list = ((_a = scrollContainerRef.current) == null ? void 0 : _a.querySelector("#event-list")) ?? null;
3376
+ scrollRowIntoView(list, index, ROW_HEIGHT_PX, {
3377
+ behavior: reducedMotion ? "auto" : "smooth"
3378
+ });
3379
+ }, [trace.spans, reducedMotion]);
3380
+ const zoomTimerRef = reactExports.useRef(null);
3381
+ const cancelPendingZoom = reactExports.useCallback(() => {
3382
+ if (zoomTimerRef.current !== null) {
3383
+ clearTimeout(zoomTimerRef.current);
3384
+ zoomTimerRef.current = null;
3385
+ }
3386
+ }, []);
3387
+ reactExports.useEffect(() => cancelPendingZoom, [cancelPendingZoom]);
3388
+ const handleClearActiveSpan = reactExports.useCallback(() => {
3389
+ cancelPendingZoom();
3390
+ clearActiveSpan();
3391
+ }, [cancelPendingZoom, clearActiveSpan]);
3392
+ const handleSelectSpan = reactExports.useCallback((spanId) => {
3393
+ cancelPendingZoom();
3394
+ if (spanId === activeSpanId) {
3395
+ clearActiveSpan();
3396
+ return;
3397
+ }
3398
+ setActiveSpan(spanId);
3399
+ focusViewportOnSpan(spanId);
3400
+ }, [
3401
+ cancelPendingZoom,
3402
+ activeSpanId,
3403
+ clearActiveSpan,
3404
+ setActiveSpan,
3405
+ focusViewportOnSpan
2510
3406
  ]);
3407
+ const navigateToSpan = reactExports.useCallback((spanId) => {
3408
+ setActiveSpan(spanId);
3409
+ scrollSpanIntoView(spanId);
3410
+ cancelPendingZoom();
3411
+ zoomTimerRef.current = setTimeout(() => {
3412
+ zoomTimerRef.current = null;
3413
+ focusViewportOnSpan(spanId);
3414
+ }, ZOOM_DEBOUNCE_MS);
3415
+ }, [setActiveSpan, scrollSpanIntoView, cancelPendingZoom, focusViewportOnSpan]);
2511
3416
  const [altHeld, setAltHeld] = reactExports.useState(false);
2512
3417
  reactExports.useEffect(() => {
2513
3418
  const onKeyDown = (e) => {
2514
3419
  if (e.key === "Escape") {
2515
- clearActiveSpan();
2516
- }
2517
- if (e.key === "Alt") {
2518
- e.preventDefault();
3420
+ handleClearActiveSpan();
3421
+ } else if (e.key === "Alt") {
2519
3422
  setAltHeld(true);
2520
3423
  }
2521
3424
  };
@@ -2532,26 +3435,25 @@ function NewTraceViewerContent({ trace }) {
2532
3435
  window.removeEventListener("keyup", onKeyUp);
2533
3436
  window.removeEventListener("blur", onBlur);
2534
3437
  };
2535
- }, [clearActiveSpan]);
3438
+ }, [handleClearActiveSpan]);
2536
3439
  const timelineRef = reactExports.useRef(null);
2537
3440
  const [hoverFraction, setHoverFraction] = reactExports.useState(null);
2538
3441
  const hoverInfo = reactExports.useMemo(() => {
2539
3442
  if (hoverFraction == null)
2540
3443
  return null;
2541
- const absTime = compression.fromVisual(hoverFraction);
3444
+ const absTime = viewport.start + hoverFraction * viewDuration;
2542
3445
  const offset = absTime - root.startTime;
2543
- return { fraction: hoverFraction, label: formatDuration(offset, true) };
2544
- }, [hoverFraction, compression, root.startTime]);
3446
+ return { fraction: hoverFraction, label: formatDurationPrecise(offset) };
3447
+ }, [hoverFraction, viewport.start, viewDuration, root.startTime]);
2545
3448
  const handleTimelineMouseMove = reactExports.useCallback((e) => {
2546
3449
  const el = timelineRef.current;
2547
3450
  if (!el)
2548
3451
  return;
2549
3452
  const rect = el.getBoundingClientRect();
2550
- const padding = 8;
2551
- const contentWidth = rect.width - padding * 2;
3453
+ const contentWidth = rect.width - TIMELINE_PADDING_PX * 2;
2552
3454
  if (contentWidth <= 0)
2553
3455
  return;
2554
- const fraction = Math.max(0, Math.min(1, (e.clientX - rect.left - padding) / contentWidth));
3456
+ const fraction = Math.max(0, Math.min(1, (e.clientX - rect.left - TIMELINE_PADDING_PX) / contentWidth));
2555
3457
  setHoverFraction(fraction);
2556
3458
  }, []);
2557
3459
  const handleTimelineMouseLeave = reactExports.useCallback(() => {
@@ -2559,12 +3461,7 @@ function NewTraceViewerContent({ trace }) {
2559
3461
  }, []);
2560
3462
  reactExports.useEffect(() => {
2561
3463
  const el = timelineRef.current;
2562
- if (!el)
2563
- return;
2564
- const rootS = root.startTime;
2565
- const rootE = root.startTime + root.duration;
2566
- const rootD = root.duration;
2567
- if (rootD <= 0)
3464
+ if (!el || root.duration <= 0)
2568
3465
  return;
2569
3466
  const onWheel = (e) => {
2570
3467
  const isZoomGesture = e.ctrlKey || e.metaKey;
@@ -2573,85 +3470,54 @@ function NewTraceViewerContent({ trace }) {
2573
3470
  return;
2574
3471
  e.preventDefault();
2575
3472
  const rect = el.getBoundingClientRect();
2576
- const contentWidth = rect.width - TIMELINE_PADDING * 2;
3473
+ const contentWidth = rect.width - TIMELINE_PADDING_PX * 2;
2577
3474
  if (contentWidth <= 0)
2578
3475
  return;
2579
3476
  if (isZoomGesture) {
2580
- let dy = e.deltaY;
2581
- if (e.deltaMode === 1)
2582
- dy *= 16;
2583
- const cursorFraction = Math.max(0, Math.min(1, (e.clientX - rect.left - TIMELINE_PADDING) / contentWidth));
2584
- const scaleFactor = Math.pow(2, dy / 200);
3477
+ const cursorFraction = Math.max(0, Math.min(1, (e.clientX - rect.left - TIMELINE_PADDING_PX) / contentWidth));
3478
+ const scaleFactor = wheelZoomScaleFactor(e);
2585
3479
  setViewport((prev) => {
2586
3480
  const prevDuration = prev.end - prev.start;
2587
3481
  const cursorTime = prev.start + cursorFraction * prevDuration;
2588
- const newDuration = Math.max(MIN_VIEWPORT_MS, Math.min(rootD, prevDuration * scaleFactor));
2589
- let newStart = cursorTime - cursorFraction * newDuration;
2590
- let newEnd = newStart + newDuration;
2591
- if (newStart < rootS) {
2592
- newStart = rootS;
2593
- newEnd = rootS + newDuration;
2594
- }
2595
- if (newEnd > rootE) {
2596
- newEnd = rootE;
2597
- newStart = Math.max(rootS, rootE - newDuration);
2598
- }
2599
- return { start: newStart, end: newEnd };
3482
+ const newDuration = Math.max(MIN_VIEWPORT_MS, Math.min(root.duration, prevDuration * scaleFactor));
3483
+ return clampToRoot({
3484
+ start: cursorTime - cursorFraction * newDuration,
3485
+ end: cursorTime + (1 - cursorFraction) * newDuration
3486
+ });
2600
3487
  });
2601
3488
  } else {
2602
- let dx = e.deltaX;
2603
- if (e.deltaMode === 1)
2604
- dx *= 16;
3489
+ const dx = wheelDeltaToPixels(e.deltaX, e.deltaMode);
2605
3490
  setViewport((prev) => {
2606
- const prevDuration = prev.end - prev.start;
2607
- const panAmount = dx / contentWidth * prevDuration;
2608
- let newStart = prev.start + panAmount;
2609
- let newEnd = prev.end + panAmount;
2610
- if (newStart < rootS) {
2611
- newStart = rootS;
2612
- newEnd = rootS + prevDuration;
2613
- }
2614
- if (newEnd > rootE) {
2615
- newEnd = rootE;
2616
- newStart = Math.max(rootS, rootE - prevDuration);
2617
- }
2618
- return { start: newStart, end: newEnd };
3491
+ const panAmount = dx / contentWidth * (prev.end - prev.start);
3492
+ return clampToRoot({
3493
+ start: prev.start + panAmount,
3494
+ end: prev.end + panAmount
3495
+ });
2619
3496
  });
2620
3497
  }
2621
3498
  };
2622
3499
  el.addEventListener("wheel", onWheel, { passive: false });
2623
3500
  return () => el.removeEventListener("wheel", onWheel);
2624
- }, [root.startTime, root.duration]);
2625
- const selectedSpanName = reactExports.useMemo(() => {
2626
- var _a, _b;
2627
- if (!(selectedSpan == null ? void 0 : selectedSpan.data))
2628
- return "Details";
2629
- const data = selectedSpan.data;
2630
- const stepName = data.stepName;
2631
- const workflowName = data.workflowName;
2632
- 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";
2633
- }, [selectedSpan == null ? void 0 : selectedSpan.data]);
2634
- const selectedResource = selectedSpan == null ? void 0 : selectedSpan.resource;
2635
- const selectedResourceId = reactExports.useMemo(() => {
2636
- if (!(selectedSpan == null ? void 0 : selectedSpan.data))
2637
- return void 0;
2638
- const data = selectedSpan.data;
2639
- return data.stepId ?? data.runId ?? data.hookId ?? selectedSpan.spanId;
2640
- }, [selectedSpan == null ? void 0 : selectedSpan.data, selectedSpan == null ? void 0 : selectedSpan.spanId]);
2641
- 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.jsx("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, { viewStart: viewport.start, viewDuration, rootStart: root.startTime, compression, isZoomed, onResetZoom: resetZoom, 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 px-2", onDoubleClick: resetZoom, onMouseMove: handleTimelineMouseMove, onMouseLeave: handleTimelineMouseLeave, children: jsxRuntimeExports.jsx(Timeline, { spans: filteredSpans, compression, selectedId: activeSpanId, onSelect: handleSelectSpan, hoverFraction, altHeld }) })] }) }), 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] });
3501
+ }, [root.duration, setViewport, clampToRoot]);
3502
+ 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: "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) => {
3503
+ if (e.key === "Escape" && searchQuery) {
3504
+ e.preventDefault();
3505
+ e.stopPropagation();
3506
+ setSearchQuery("");
3507
+ }
3508
+ }, 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", "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-base 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-sm text-gray-800", children: [jsxRuntimeExports.jsx(Spinner, { size: 14 }), jsxRuntimeExports.jsx("span", { children: "Loading spans…" })] }) : null })] }), jsxRuntimeExports.jsxs("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, hoverFraction, altHeld }), jsxRuntimeExports.jsx(TraceShortcutHelper, { hasMultipleSpans: trace.spans.length > 1, reducedMotion })] })] }), jsxRuntimeExports.jsxs("div", { className: "absolute right-3 bottom-3 z-[5] flex items-center border border-gray-alpha-400 rounded-md bg-background-100 shadow-sm overflow-hidden divide-x divide-gray-alpha-400", 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 })] });
2642
3509
  }
2643
- const NewTraceViewer = ({ run, events, sidebarData }) => {
2644
- const traceWithMeta = reactExports.useMemo(() => {
3510
+ const NewTraceViewer = ({ run, events, sidebarData, onLoadMore, hasMore, isLoadingMore, loading = false }) => {
3511
+ const trace = reactExports.useMemo(() => {
2645
3512
  if (!(run == null ? void 0 : run.runId)) {
2646
3513
  return void 0;
2647
3514
  }
2648
3515
  return buildTrace(run, events, /* @__PURE__ */ new Date());
2649
3516
  }, [run, events]);
2650
- const trace = traceWithMeta;
2651
- if (!trace) {
2652
- 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…" }) });
3517
+ if (!trace || loading && events.length === 0) {
3518
+ return jsxRuntimeExports.jsx(TraceViewerSkeleton, {});
2653
3519
  }
2654
- return jsxRuntimeExports.jsx(SidebarDataProvider, { value: sidebarData, children: jsxRuntimeExports.jsx("div", { className: "relative w-full h-full flex", children: jsxRuntimeExports.jsx(NewTraceViewer$1, { trace }) }) });
3520
+ return jsxRuntimeExports.jsx(SidebarDataProvider, { value: sidebarData, children: jsxRuntimeExports.jsx("div", { className: "relative w-full h-full flex", children: jsxRuntimeExports.jsx(NewTraceViewer$1, { trace, onLoadMore, hasMore, isLoadingMore }) }) });
2655
3521
  };
2656
3522
  async function fetchResourceWithCorrelationId(env, resource, resourceId, options = {}) {
2657
3523
  const resolveData = options.resolveData ?? "all";
@@ -2681,130 +3547,45 @@ async function fetchResourceWithCorrelationId(env, resource, resourceId, options
2681
3547
  }
2682
3548
  return { data: resourceData, correlationId };
2683
3549
  }
2684
- function useWorkflowResourceData(env, resource, resourceId, options = {}) {
2685
- const { refreshInterval = 0, runId, enabled = true, encryptionKey } = options;
2686
- const [data, setData] = reactExports.useState(
2687
- null
2688
- );
2689
- const [loading, setLoading] = reactExports.useState(enabled);
2690
- const [error, setError] = reactExports.useState(null);
2691
- const hydrate = reactExports.useCallback(
2692
- async (resource2) => encryptionKey ? hydrateResourceIOWithKey(resource2, encryptionKey) : hydrateResourceIO(resource2),
2693
- [encryptionKey]
2694
- );
2695
- const prevSelectionRef = reactExports.useRef("");
2696
- const fetchData = reactExports.useCallback(async () => {
2697
- if (!enabled) {
2698
- setLoading(false);
2699
- return;
2700
- }
2701
- const selectionKey = `${resource}:${resourceId}`;
2702
- if (selectionKey !== prevSelectionRef.current) {
2703
- setData(null);
2704
- prevSelectionRef.current = selectionKey;
2705
- }
2706
- setError(null);
2707
- setLoading(true);
2708
- if (resource === "hook") {
2709
- try {
2710
- const { error: error2, result } = await unwrapServerActionResult(
2711
- fetchHook(env, resourceId, "all")
2712
- );
2713
- if (error2) {
2714
- setError(error2);
2715
- return;
2716
- }
2717
- try {
2718
- setData(await hydrate(result));
2719
- } catch (hydrateError) {
2720
- setError(
2721
- hydrateError instanceof Error ? hydrateError : new Error(String(hydrateError))
2722
- );
2723
- }
2724
- } finally {
2725
- setLoading(false);
2726
- }
2727
- return;
2728
- }
2729
- if (resource === "sleep") {
2730
- try {
2731
- if (!runId) {
2732
- setError(new Error("runId is required for loading sleep details"));
2733
- return;
2734
- }
2735
- const { error: error2, result } = await unwrapServerActionResult(
2736
- fetchEvents(env, runId, {
2737
- sortOrder: "asc",
2738
- limit: 1e3,
2739
- withData: true
2740
- })
2741
- );
2742
- if (error2) {
2743
- setError(error2);
2744
- return;
2745
- }
2746
- try {
2747
- const allEvents = result.data.map(
2748
- hydrateResourceIO
2749
- );
2750
- const waitEvents = await Promise.all(
2751
- allEvents.filter((e) => e.correlationId === resourceId).map(hydrate)
2752
- );
2753
- const data2 = waitEventsToWaitEntity(waitEvents);
2754
- if (data2 === null) {
2755
- setError(
2756
- new Error(
2757
- `Failed to load ${resource} details: missing required event data`
2758
- )
2759
- );
2760
- return;
2761
- }
2762
- setData(data2);
2763
- } catch (hydrateError) {
2764
- setError(
2765
- hydrateError instanceof Error ? hydrateError : new Error(String(hydrateError))
2766
- );
2767
- }
2768
- } finally {
2769
- setLoading(false);
2770
- }
2771
- return;
3550
+ async function fetchSpanDetailResource(env, selection, options = {}) {
3551
+ const { resource, resourceId, runId } = selection;
3552
+ const { encryptionKey } = options;
3553
+ const hydrate = async (value) => hydrateResourceIOAsync(value, encryptionKey);
3554
+ if (resource === "hook") {
3555
+ const result = await unwrapOrThrow(fetchHook(env, resourceId, "all"));
3556
+ return hydrate(result);
3557
+ }
3558
+ if (resource === "sleep") {
3559
+ if (!runId) {
3560
+ throw new WorkflowWebAPIError(
3561
+ "runId is required for loading sleep details",
3562
+ { layer: "client" }
3563
+ );
2772
3564
  }
2773
- try {
2774
- const { data: resourceData } = await fetchResourceWithCorrelationId(
2775
- env,
2776
- resource,
2777
- resourceId,
2778
- { runId }
3565
+ const result = await unwrapOrThrow(
3566
+ fetchEvents(env, runId, { sortOrder: "asc", limit: 1e3, withData: true })
3567
+ );
3568
+ const waitEvents = await Promise.all(
3569
+ result.data.filter((e) => e.correlationId === resourceId).map(hydrate)
3570
+ );
3571
+ const data2 = waitEventsToWaitEntity(waitEvents);
3572
+ if (data2 === null) {
3573
+ throw new WorkflowWebAPIError(
3574
+ `Failed to load ${resource} details: missing required event data`,
3575
+ { layer: "client" }
2779
3576
  );
2780
- setData(await hydrate(resourceData));
2781
- } catch (error2) {
2782
- if (error2 instanceof Error) {
2783
- setError(error2);
2784
- } else {
2785
- setError(new Error(String(error2)));
2786
- }
2787
- return;
2788
- } finally {
2789
- setLoading(false);
2790
3577
  }
2791
- }, [env, resource, resourceId, runId, enabled, hydrate]);
2792
- reactExports.useEffect(() => {
2793
- fetchData();
2794
- }, [fetchData]);
2795
- reactExports.useEffect(() => {
2796
- if (!refreshInterval || refreshInterval <= 0) {
2797
- return;
3578
+ return data2;
3579
+ }
3580
+ const { data } = await fetchResourceWithCorrelationId(
3581
+ env,
3582
+ resource,
3583
+ resourceId,
3584
+ {
3585
+ runId
2798
3586
  }
2799
- const interval = setInterval(fetchData, refreshInterval);
2800
- return () => clearInterval(interval);
2801
- }, [refreshInterval, fetchData]);
2802
- return {
2803
- data,
2804
- loading,
2805
- error,
2806
- refresh: fetchData
2807
- };
3587
+ );
3588
+ return hydrate(data);
2808
3589
  }
2809
3590
  function mergeById(prev, next, idKey) {
2810
3591
  const combined = [...prev, ...next];
@@ -2843,6 +3624,7 @@ const LIVE_POLL_LIMIT = 100;
2843
3624
  const INITIAL_PAGE_SIZE$1 = 500;
2844
3625
  const LOAD_MORE_PAGE_SIZE$1 = 100;
2845
3626
  const LIVE_UPDATE_INTERVAL_MS = 5e3;
3627
+ const AUTO_LOAD_MAX_EVENTS = 500;
2846
3628
  function useWorkflowTraceViewerData(env, runId, options = {}) {
2847
3629
  const { live = false } = options;
2848
3630
  const [run, setRun] = reactExports.useState(null);
@@ -2951,6 +3733,12 @@ function useWorkflowTraceViewerData(env, runId, options = {}) {
2951
3733
  eventsHasMore,
2952
3734
  eventsCursor
2953
3735
  ]);
3736
+ reactExports.useEffect(() => {
3737
+ if (events.length >= AUTO_LOAD_MAX_EVENTS) {
3738
+ return;
3739
+ }
3740
+ loadMoreTraceData();
3741
+ }, [events.length, loadMoreTraceData]);
2954
3742
  const pollRun = reactExports.useCallback(async () => {
2955
3743
  if (run == null ? void 0 : run.completedAt) {
2956
3744
  return false;
@@ -4211,6 +4999,7 @@ function BreadcrumbSeparator({
4211
4999
  }
4212
5000
  const INITIAL_PAGE_SIZE = 100;
4213
5001
  const LOAD_MORE_PAGE_SIZE = 100;
5002
+ const MAX_CORRELATION_SEARCH_PAGES = 30;
4214
5003
  function useEventsListData(env, runId, options = {}) {
4215
5004
  const { sortOrder = "asc", encryptionKey, enabled = true } = options;
4216
5005
  const [events, setEvents] = reactExports.useState([]);
@@ -4222,6 +5011,10 @@ function useEventsListData(env, runId, options = {}) {
4222
5011
  const isFetchingRef = reactExports.useRef(false);
4223
5012
  const encryptionKeyRef = reactExports.useRef(encryptionKey);
4224
5013
  encryptionKeyRef.current = encryptionKey;
5014
+ const hydrateEvents = reactExports.useCallback(async (rawEvents) => {
5015
+ const key = encryptionKeyRef.current;
5016
+ return Promise.all(rawEvents.map((ev) => hydrateResourceIOAsync(ev, key)));
5017
+ }, []);
4225
5018
  const fetchInitial = reactExports.useCallback(async () => {
4226
5019
  if (isFetchingRef.current) return;
4227
5020
  isFetchingRef.current = true;
@@ -4241,16 +5034,7 @@ function useEventsListData(env, runId, options = {}) {
4241
5034
  if (fetchError) {
4242
5035
  setError(fetchError);
4243
5036
  } else {
4244
- const hydrated = result.data.map(hydrateResourceIO);
4245
- const key = encryptionKeyRef.current;
4246
- if (key) {
4247
- const decrypted = await Promise.all(
4248
- hydrated.map((ev) => hydrateResourceIOWithKey(ev, key))
4249
- );
4250
- setEvents(decrypted);
4251
- } else {
4252
- setEvents(hydrated);
4253
- }
5037
+ setEvents(await hydrateEvents(result.data));
4254
5038
  setCursor(result.hasMore ? result.cursor : void 0);
4255
5039
  setHasMore(Boolean(result.hasMore));
4256
5040
  }
@@ -4260,14 +5044,14 @@ function useEventsListData(env, runId, options = {}) {
4260
5044
  setLoading(false);
4261
5045
  isFetchingRef.current = false;
4262
5046
  }
4263
- }, [env, runId, sortOrder]);
5047
+ }, [env, runId, sortOrder, hydrateEvents]);
4264
5048
  reactExports.useEffect(() => {
4265
5049
  if (enabled) fetchInitial();
4266
5050
  }, [fetchInitial, enabled]);
4267
5051
  reactExports.useEffect(() => {
4268
5052
  if (!encryptionKey || events.length === 0) return;
4269
5053
  let cancelled = false;
4270
- Promise.all(events.map((ev) => hydrateResourceIOWithKey(ev, encryptionKey))).then((decrypted) => {
5054
+ Promise.all(events.map((ev) => hydrateResourceIOAsync(ev, encryptionKey))).then((decrypted) => {
4271
5055
  if (!cancelled) setEvents(decrypted);
4272
5056
  }).catch(() => {
4273
5057
  });
@@ -4291,16 +5075,8 @@ function useEventsListData(env, runId, options = {}) {
4291
5075
  setError(fetchError);
4292
5076
  } else {
4293
5077
  if (result.data.length > 0) {
4294
- const hydrated = result.data.map(hydrateResourceIO);
4295
- const key = encryptionKeyRef.current;
4296
- if (key) {
4297
- const decrypted = await Promise.all(
4298
- hydrated.map((ev) => hydrateResourceIOWithKey(ev, key))
4299
- );
4300
- setEvents((prev) => [...prev, ...decrypted]);
4301
- } else {
4302
- setEvents((prev) => [...prev, ...hydrated]);
4303
- }
5078
+ const hydrated = await hydrateEvents(result.data);
5079
+ setEvents((prev) => [...prev, ...hydrated]);
4304
5080
  }
4305
5081
  setCursor(result.hasMore ? result.cursor : void 0);
4306
5082
  setHasMore(Boolean(result.hasMore));
@@ -4310,14 +5086,65 @@ function useEventsListData(env, runId, options = {}) {
4310
5086
  } finally {
4311
5087
  setLoadingMore(false);
4312
5088
  }
4313
- }, [env, runId, sortOrder, cursor, loadingMore]);
5089
+ }, [env, runId, sortOrder, cursor, loadingMore, hydrateEvents]);
5090
+ const searchByExactId = reactExports.useCallback(
5091
+ async (id, kind, signal) => {
5092
+ if (signal == null ? void 0 : signal.aborted) {
5093
+ throw new DOMException("Aborted", "AbortError");
5094
+ }
5095
+ if (kind === "event") {
5096
+ const { error: fetchError, result } = await unwrapServerActionResult(
5097
+ fetchEvent(env, runId, id, "none")
5098
+ );
5099
+ if (fetchError || (signal == null ? void 0 : signal.aborted)) {
5100
+ return fetchError ? { status: "error", message: fetchError.message } : (() => {
5101
+ throw new DOMException("Aborted", "AbortError");
5102
+ })();
5103
+ }
5104
+ const [event] = await hydrateEvents([result]);
5105
+ return (event == null ? void 0 : event.runId) === runId ? { status: "ok", events: [event] } : { status: "not_found" };
5106
+ }
5107
+ const matched = [];
5108
+ let nextCursor;
5109
+ let pagesFetched = 0;
5110
+ let truncated = false;
5111
+ do {
5112
+ if (signal == null ? void 0 : signal.aborted) {
5113
+ throw new DOMException("Aborted", "AbortError");
5114
+ }
5115
+ const { error: fetchError, result } = await unwrapServerActionResult(
5116
+ fetchEventsByCorrelationId(env, id, {
5117
+ cursor: nextCursor,
5118
+ sortOrder,
5119
+ limit: 100,
5120
+ withData: false
5121
+ })
5122
+ );
5123
+ if (fetchError) {
5124
+ return { status: "error", message: fetchError.message };
5125
+ }
5126
+ if (signal == null ? void 0 : signal.aborted) {
5127
+ throw new DOMException("Aborted", "AbortError");
5128
+ }
5129
+ pagesFetched += 1;
5130
+ const hydrated = await hydrateEvents(result.data);
5131
+ matched.push(...hydrated.filter((event) => event.runId === runId));
5132
+ const hitPageCap = pagesFetched >= MAX_CORRELATION_SEARCH_PAGES;
5133
+ truncated = truncated || hitPageCap && Boolean(result.hasMore && result.cursor);
5134
+ nextCursor = !hitPageCap && result.hasMore && result.cursor ? result.cursor : void 0;
5135
+ } while (nextCursor);
5136
+ return matched.length > 0 ? { status: "ok", events: matched, truncated: truncated || void 0 } : { status: "not_found" };
5137
+ },
5138
+ [env, runId, sortOrder, hydrateEvents]
5139
+ );
4314
5140
  return {
4315
5141
  events,
4316
5142
  loading,
4317
5143
  error,
4318
5144
  hasMore,
4319
5145
  loadingMore,
4320
- loadMore
5146
+ loadMore,
5147
+ searchByExactId
4321
5148
  };
4322
5149
  }
4323
5150
  const FRAME_HEADER_SIZE = 4;
@@ -4600,8 +5427,8 @@ function useStreamReader(env, streamId, runId, encryptionKey, runStatus) {
4600
5427
  return { chunks, isLive, error };
4601
5428
  }
4602
5429
  function LiveStatus({ hasError, errorMessage }) {
4603
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
4604
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
5430
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip$1, { children: [
5431
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger$1, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
4605
5432
  /* @__PURE__ */ jsxRuntimeExports.jsx(
4606
5433
  "div",
4607
5434
  {
@@ -4619,7 +5446,7 @@ function LiveStatus({ hasError, errorMessage }) {
4619
5446
  }
4620
5447
  )
4621
5448
  ] }) }),
4622
- /* @__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` }) })
5449
+ /* @__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` }) })
4623
5450
  ] }) });
4624
5451
  }
4625
5452
  ({
@@ -4627,7 +5454,6 @@ function LiveStatus({ hasError, errorMessage }) {
4627
5454
  });
4628
5455
  function RunDetailView({
4629
5456
  runId,
4630
- // TODO: This should open the right sidebar within the trace viewer
4631
5457
  selectedId: _selectedId
4632
5458
  }) {
4633
5459
  var _a;
@@ -4705,29 +5531,16 @@ function RunDetailView({
4705
5531
  if (error2) {
4706
5532
  throw error2;
4707
5533
  }
4708
- const fullEvent = encryptionKeyRef.current ? await hydrateResourceIOWithKey(result, encryptionKeyRef.current) : hydrateResourceIO(result);
4709
- if ("eventData" in fullEvent) {
4710
- return fullEvent.eventData;
4711
- }
4712
- return null;
4713
- },
4714
- [env]
4715
- );
4716
- const handleLoadSidebarEventData = reactExports.useCallback(
4717
- async (_correlationId, eventId) => {
4718
- const { error: error2, result } = await unwrapServerActionResult(
4719
- fetchEvent(env, runId, eventId, "all")
5534
+ const fullEvent = await hydrateResourceIOAsync(
5535
+ result,
5536
+ encryptionKeyRef.current ?? void 0
4720
5537
  );
4721
- if (error2) {
4722
- throw error2;
4723
- }
4724
- const fullEvent = encryptionKeyRef.current ? await hydrateResourceIOWithKey(result, encryptionKeyRef.current) : hydrateResourceIO(result);
4725
5538
  if ("eventData" in fullEvent) {
4726
5539
  return fullEvent.eventData;
4727
5540
  }
4728
5541
  return null;
4729
5542
  },
4730
- [env, runId]
5543
+ [env]
4731
5544
  );
4732
5545
  serverConfig.backendId === "local" || serverConfig.backendId === "@workflow/world-local";
4733
5546
  const {
@@ -4736,7 +5549,10 @@ function RunDetailView({
4736
5549
  loading,
4737
5550
  error,
4738
5551
  update,
4739
- hasEncryptedData
5552
+ hasEncryptedData,
5553
+ loadMoreTraceData,
5554
+ hasMoreTraceData,
5555
+ isLoadingMoreTraceData
4740
5556
  } = useWorkflowTraceViewerData(env, runId, { live: true });
4741
5557
  const run = runData ?? {};
4742
5558
  const [encryptionKey, setEncryptionKey] = reactExports.useState(null);
@@ -4747,36 +5563,22 @@ function RunDetailView({
4747
5563
  loading: eventsListLoading,
4748
5564
  hasMore: hasMoreEventsTab,
4749
5565
  loadingMore: loadingMoreEventsTab,
4750
- loadMore: loadMoreEventsTab
5566
+ loadMore: loadMoreEventsTab,
5567
+ searchByExactId
4751
5568
  } = useEventsListData(env, runId, {
4752
5569
  sortOrder: eventsSortOrder,
4753
5570
  encryptionKey: encryptionKey ?? void 0,
4754
5571
  enabled: activeTab === "events"
4755
5572
  });
4756
- const [spanSelection, setSpanSelection] = reactExports.useState(
4757
- null
4758
- );
4759
- const {
4760
- data: spanDetailData,
4761
- loading: spanDetailLoading,
4762
- error: spanDetailError,
4763
- refresh: refreshSpanDetail
4764
- } = useWorkflowResourceData(
4765
- env,
4766
- (spanSelection == null ? void 0 : spanSelection.resource) ?? "run",
4767
- (spanSelection == null ? void 0 : spanSelection.resourceId) ?? "",
4768
- {
4769
- runId: spanSelection == null ? void 0 : spanSelection.runId,
4770
- enabled: Boolean(
4771
- (spanSelection == null ? void 0 : spanSelection.resource) && (spanSelection == null ? void 0 : spanSelection.resourceId) && spanSelection.resource !== "hook"
4772
- ),
5573
+ const fetchSpanDetail = reactExports.useCallback(
5574
+ (selection) => fetchSpanDetailResource(env, selection, {
4773
5575
  encryptionKey: encryptionKey ?? void 0
4774
- }
5576
+ }),
5577
+ [env, encryptionKey]
4775
5578
  );
4776
5579
  const [isDecrypting, setIsDecrypting] = reactExports.useState(false);
4777
5580
  const handleDecrypt = reactExports.useCallback(async () => {
4778
5581
  if (encryptionKey) {
4779
- refreshSpanDetail();
4780
5582
  return;
4781
5583
  }
4782
5584
  setIsDecrypting(true);
@@ -4791,26 +5593,19 @@ function RunDetailView({
4791
5593
  return;
4792
5594
  }
4793
5595
  setEncryptionKey(keyResult);
4794
- toast.success("Run data decrypted successfully");
4795
5596
  } finally {
4796
5597
  setIsDecrypting(false);
4797
5598
  }
4798
- }, [encryptionKey, env, runId, refreshSpanDetail]);
4799
- const handleSpanSelect = reactExports.useCallback((info) => {
4800
- setSpanSelection(info);
4801
- }, []);
5599
+ }, [encryptionKey, env, runId]);
4802
5600
  const sidebarData = reactExports.useMemo(
4803
5601
  () => ({
4804
5602
  run,
4805
5603
  events: allEvents ?? [],
4806
- spanDetailData: spanDetailData ?? null,
4807
- spanDetailError,
4808
- spanDetailLoading,
4809
- onSpanSelect: handleSpanSelect,
5604
+ fetchSpanDetail,
4810
5605
  onStreamClick: handleStreamClick,
4811
5606
  onRunClick: handleRunRefClick,
4812
5607
  onWakeUpSleep: handleWakeUpSleep,
4813
- onLoadEventData: handleLoadSidebarEventData,
5608
+ onLoadEventData: handleLoadEventData,
4814
5609
  onResolveHook: handleResolveHook,
4815
5610
  encryptionKey: encryptionKey ?? void 0,
4816
5611
  onDecrypt: handleDecrypt,
@@ -4820,14 +5615,11 @@ function RunDetailView({
4820
5615
  [
4821
5616
  run,
4822
5617
  allEvents,
4823
- spanDetailData,
4824
- spanDetailError,
4825
- spanDetailLoading,
4826
- handleSpanSelect,
5618
+ fetchSpanDetail,
4827
5619
  handleStreamClick,
4828
5620
  handleRunRefClick,
4829
5621
  handleWakeUpSleep,
4830
- handleLoadSidebarEventData,
5622
+ handleLoadEventData,
4831
5623
  handleResolveHook,
4832
5624
  encryptionKey,
4833
5625
  handleDecrypt,
@@ -4855,7 +5647,7 @@ function RunDetailView({
4855
5647
  setShowCancelDialog(false);
4856
5648
  await cancelRun(env, runId);
4857
5649
  await update();
4858
- toast.success("Run cancelled successfully");
5650
+ toast.success("Run cancelled");
4859
5651
  } catch (err) {
4860
5652
  console.error("Failed to cancel run:", err);
4861
5653
  toast.error("Failed to cancel run", {
@@ -4874,7 +5666,7 @@ function RunDetailView({
4874
5666
  setRerunning(true);
4875
5667
  setShowRerunDialog(false);
4876
5668
  const newRunId = await recreateRun(env, run.runId);
4877
- toast.success("New run started successfully", {
5669
+ toast.success("New run started", {
4878
5670
  description: `Run ID: ${newRunId}`
4879
5671
  });
4880
5672
  navigate(`/run/${newRunId}`);
@@ -4891,8 +5683,8 @@ function RunDetailView({
4891
5683
  if (error && !runData) {
4892
5684
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Alert, { variant: "destructive", className: "m-4", children: [
4893
5685
  /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4" }),
4894
- /* @__PURE__ */ jsxRuntimeExports.jsx(AlertTitle, { children: "Error loading workflow run" }),
4895
- /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDescription, { children: error.message })
5686
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AlertTitle, { children: getErrorTitle(error, "Error loading workflow run") }),
5687
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDescription, { children: getErrorMessage(error) })
4896
5688
  ] });
4897
5689
  }
4898
5690
  const workflowName = (_a = parseWorkflowName(run.workflowName)) == null ? void 0 : _a.shortName;
@@ -4992,12 +5784,12 @@ function RunDetailView({
4992
5784
  /* @__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]" }) })
4993
5785
  ] }),
4994
5786
  run.expiredAt != null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
4995
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
4996
- /* @__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: [
5787
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip$1, { children: [
5788
+ /* @__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: [
4997
5789
  "Expired",
4998
5790
  /* @__PURE__ */ jsxRuntimeExports.jsx(CircleQuestionMark, { className: "w-3 h-3" })
4999
5791
  ] }) }),
5000
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "The storage data for this run has expired and is no longer available." }) })
5792
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "The storage data for this run has expired and is no longer available." }) })
5001
5793
  ] }),
5002
5794
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(RelativeTime, { date: run.expiredAt }) })
5003
5795
  ] })
@@ -5026,12 +5818,16 @@ function RunDetailView({
5026
5818
  "Streams"
5027
5819
  ] })
5028
5820
  ] }),
5029
- /* @__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 pl-6 bg-background-100 border-t border-gray-alpha-400 overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5821
+ /* @__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(
5030
5822
  NewTraceViewer,
5031
5823
  {
5032
5824
  run,
5033
5825
  events: allEvents ?? [],
5034
- sidebarData
5826
+ loading,
5827
+ sidebarData,
5828
+ onLoadMore: loadMoreTraceData,
5829
+ hasMore: hasMoreTraceData,
5830
+ isLoadingMore: isLoadingMoreTraceData
5035
5831
  }
5036
5832
  ) }) }) }),
5037
5833
  /* @__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(
@@ -5049,7 +5845,8 @@ function RunDetailView({
5049
5845
  onSortOrderChange: setEventsSortOrder,
5050
5846
  onDecrypt: handleDecrypt,
5051
5847
  isDecrypting,
5052
- hasEncryptedData
5848
+ hasEncryptedData,
5849
+ onExactIdSearch: searchByExactId
5053
5850
  }
5054
5851
  ) }) }) }),
5055
5852
  /* @__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: [