@workflow/web 5.0.0-beta.25 → 5.0.0-beta.27

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 (25) hide show
  1. package/build/client/assets/{encryption-g14N5vQl.js → encryption-BQTnudEZ.js} +1 -1
  2. package/build/client/assets/{highlighted-body-B3W2YXNL-QrddlyZu.js → highlighted-body-B3W2YXNL-B-EF8R0t.js} +1 -1
  3. package/build/client/assets/{home-BNApVOct.js → home-DQkixJk4.js} +3 -3
  4. package/build/client/assets/{index-B6-SzLmT.js → index-ConSkUmc.js} +0 -2
  5. package/build/client/assets/{manifest-379f050f.js → manifest-ce6dd06d.js} +1 -1
  6. package/build/client/assets/{mermaid-3ZIDBTTL-Cg-Mp5Kj.js → mermaid-3ZIDBTTL-aixonSAh.js} +445 -260
  7. package/build/client/assets/root-BlaSucTH.css +1 -0
  8. package/build/client/assets/{root-BsPxYsbK.js → root-CsCdj08f.js} +1 -1
  9. package/build/client/assets/{run-detail-sr7StYc6.js → run-detail-CCsVqid_.js} +975 -760
  10. package/build/client/assets/server-build-DPrKlhgf.css +1 -0
  11. package/build/client/assets/{workflow-graph-viewer-CcCwLmnF.js → workflow-graph-viewer-BlPvo_5I.js} +44 -62
  12. package/build/client/assets/{zstd-browser-decoder-OOeWOMJw.js → zstd-browser-decoder-RyHgsNO3.js} +1 -1
  13. package/build/server/assets/{app-e2FYIgWU.js → app-kYNGZFkW.js} +1 -1
  14. package/build/server/assets/{highlighted-body-B3W2YXNL-B7xcG7gl.js → highlighted-body-B3W2YXNL-D1QWoqJB.js} +3 -3
  15. package/build/server/assets/index-fWQcXKhC.js +115 -0
  16. package/build/server/assets/{mermaid-3ZIDBTTL-DQqBhQ9Y.js → mermaid-3ZIDBTTL-BOh-zwwU.js} +3 -3
  17. package/build/server/assets/{server-build-D7EgG_DH.js → server-build-B21fZEey.js} +2565 -1341
  18. package/build/server/assets/{token-guLCf66r.js → token-DmIqmbeU.js} +2 -2
  19. package/build/server/assets/{token-util-CCeU4-vR.js → token-util-D4JYdrFc.js} +2 -2
  20. package/build/server/assets/{zstd-browser-decoder-CcinVfMP.js → zstd-browser-decoder-DgO7NCG6.js} +3 -3
  21. package/build/server/index.js +1 -1
  22. package/package.json +8 -8
  23. package/build/client/assets/root-F_dqJDC7.css +0 -1
  24. package/build/client/assets/server-build-DVBDkRDS.css +0 -1
  25. package/build/server/assets/index-BELZxbAO.js +0 -100
@@ -1,8 +1,8 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, f as reactDomExports, G as ReactDOM, B as useNavigate, C as useSearchParams, A as Link, w as withComponentProps, H as useParams } from "./index-BXZSIDEp.js";
2
- import { q as Check, O as Copy, j as ChevronRight, o as ChevronDown, C as CircleAlert, Q as parseStepName, B as parseWorkflowName, U as ErrorCard, V as format, Z as Zap, S as Send, R as ResolveHookModal, Y as Circle, X, p as ChevronUp, N as ErrorBoundary, u as unwrapOrThrow, W as WorkflowWebAPIError, I as unwrapServerActionResult, _ as useEscapeKeydown, t as composeEventHandlers, $ as useCallbackRef, a0 as useLayoutEffect2, s as useControllableState, a1 as useId, a2 as createContext2, v as Presence, x as createContextScope, l as hideOthers, m as ReactRemoveScroll, a3 as useFocusGuards, a4 as LAYOUT, a5 as wakeUpRun, r as resumeHook, A as Alert, c as AlertTitle, d as AlertDescription, a as Skeleton$1, a6 as RunActionsButtons, F as StatusBadge, e as CopyableText, b as RelativeTime, T as Tabs, K as TabsList, L as TabsTrigger, M as TabsContent, y as cancelRun, a7 as recreateRun } from "./workflow-graph-viewer-CcCwLmnF.js";
3
- import { c as createLucideIcon, O as cn, U as DataInspector, W as DecryptClickContext, X as Button, Y as Spinner, Z as Lock, $ as RESERVED_ATTRIBUTE_KEY_PREFIX, a0 as ContextCardProvider, h as useToast, a1 as Skeleton, a2 as Yr, a3 as isEncryptedMarker, a4 as TimestampTooltip, a5 as formatDuration, a6 as _r, a7 as RunClickContext, a8 as StreamClickContext, a9 as isExpiredMarker, aa as isDoStreamStep, ab as extractConversation, ac as hasEncryptedFields, ad as EVENT_DATA_REF_FIELDS, ae as useSidebarDataOptional, af as clsx, I as cva, ag as useRowWindow, ah as isSpanDimmedBySearch, ai as ROW_HEIGHT_PX, aj as getSpanDurationMs, ak as formatDurationPrecise, al as TooltipProvider, am as ActiveSpanProvider, an as useActiveSpan, ao as useSidebarData, ap as useReducedMotion, aq as searchSpans, ar as computeRootBounds, as as computeTimeMarkers, at as getHighResInMs, au as scrollRowIntoView, av as TIMELINE_PADDING_PX, aw as Timeline, ax as TimelineHeader, ay as IconButton, az as Tooltip, aA as TooltipTrigger, aB as TooltipContent, aC as SidebarDataProvider, aD as fetchHook, l as fetchEvents, aE as hydrateResourceIO, aF as hydrateResourceIOWithKey, H as fetchRun, aG as fetchStep, aH as fetchStreams, aI as Slot, G as useComposedRefs, aJ as Slottable, e as cn$1, aK as buttonVariants, aL as fetchEvent, aM as fetchEventsByCorrelationId, aN as isEncryptedData, aO as hydrateData, aP as getWebRevivers, T as Tooltip$1, a as TooltipTrigger$1, b as TooltipContent$1, aQ as LIVE_UPDATE_INTERVAL_MS$1, u as useServerConfig, aR as getEncryptionKeyForRun, t as toast, L as LoaderCircle, aS as StreamViewer } from "./mermaid-3ZIDBTTL-Cg-Mp5Kj.js";
4
- import { decrypt, importKey } from "./encryption-g14N5vQl.js";
5
- import "./index-B6-SzLmT.js";
2
+ import { o as Check, M as Copy, C as CircleAlert, N as parseStepName, y as parseWorkflowName, O as ErrorCard, Q as format, Z as Zap, S as Send, R as ResolveHookModal, n as ChevronUp, X, L as ErrorBoundary, U as Circle, u as unwrapOrThrow, W as WorkflowWebAPIError, G as unwrapServerActionResult, V as useEscapeKeydown, q as composeEventHandlers, Y as useCallbackRef, _ as useLayoutEffect2, p as useControllableState, $ as useId, a0 as createContext2, s as Presence, v as createContextScope, k as hideOthers, l 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, z as StatusBadge, e as CopyableText, b as RelativeTime, T as Tabs, I as TabsList, J as TabsTrigger, K as TabsContent, w as cancelRun, a5 as recreateRun } from "./workflow-graph-viewer-BlPvo_5I.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 _r, ac as RunClickContext, ad as StreamClickContext, ae as Collapsible, af as isExpiredMarker, ag as isDoStreamStep, ah as extractConversation, ai as hasEncryptedFields, aj as getEventDataRefFields, ak as useSidebarDataOptional, al as clsx, am as useActiveSpan, an as useSidebarData, ao as Tooltip, ap as TooltipTrigger, aq as IconButton, ar as TooltipContent, H as ChevronDown, as as useRowWindow, at as isSpanDimmedBySearch, au as ROW_HEIGHT_PX, av as getSpanDurationMs, aw as formatDurationPrecise, ax as TooltipProvider, ay as ActiveSpanProvider, az as useReducedMotion, aA as searchSpans, aB as computeRootBounds, aC as computeTimeMarkers, aD as getHighResInMs, aE as scrollRowIntoView, aF as TIMELINE_PADDING_PX, aG as Timeline, aH as TimelineHeader, aI as SidebarDataProvider, aJ as fetchHook, l as fetchEvents, aK as hydrateResourceIOAsync, J as fetchRun, aL as fetchStep, aM as hydrateResourceIO, aN as fetchStreams, aO as Slot, I as useComposedRefs, aP as Slottable, e as cn$1, aQ as buttonVariants, aR as fetchEvent, aS as fetchEventsByCorrelationId, aT as isEncryptedData, aU as hydrateData, aV as getWebRevivers, T as Tooltip$1, a as TooltipTrigger$1, b as TooltipContent$1, aW as LIVE_UPDATE_INTERVAL_MS$1, u as useServerConfig, aX as getEncryptionKeyForRun, t as toast, L as LoaderCircle, aY as StreamViewer } from "./mermaid-3ZIDBTTL-aixonSAh.js";
4
+ import { decrypt, importKey } from "./encryption-BQTnudEZ.js";
5
+ import "./index-ConSkUmc.js";
6
6
  /**
7
7
  * @license lucide-react v0.575.0 - ISC
8
8
  *
@@ -125,166 +125,476 @@ function CopyButton({ copyText, ariaLabel, className }) {
125
125
  });
126
126
  }, 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" }) })] }) });
127
127
  }
128
- const encryptedPlaceholderPreview = `{
129
- "input": "[encrypted]",
130
- "result": "[encrypted]"
131
- }`;
132
- function EncryptedDataBlock() {
133
- const ctx = reactExports.useContext(DecryptClickContext);
134
- 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"] }) })] });
135
- }
136
- const serializeForClipboard = (value) => {
137
- if (typeof value === "string")
138
- return value;
139
- if (typeof value === "number" || typeof value === "boolean" || value === null) {
140
- return String(value);
141
- }
142
- try {
143
- return JSON.stringify(value, null, 2);
144
- } catch {
145
- return String(value);
128
+ const ELLIPSIS = "…";
129
+ const MIN_START = 3;
130
+ const MIN_END = 3;
131
+ const MIN_KEPT = MIN_START + MIN_END;
132
+ const graphemeSegmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
133
+ function toGraphemes(text) {
134
+ if (graphemeSegmenter) {
135
+ return [...graphemeSegmenter.segment(text)].map((s) => s.segment);
146
136
  }
147
- };
148
- function CopyableDataBlock({ data }) {
149
- 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 })] });
137
+ return Array.from(text);
150
138
  }
151
- function DetailCard({ summary, children, onToggle, disabled = false, defaultOpen = false, variant = "section", trailing, summaryClassName, contentClassName }) {
152
- const [open, setOpen] = reactExports.useState(defaultOpen);
153
- const handleToggle = (e) => {
154
- if (e.target !== e.currentTarget)
155
- return;
156
- const next = e.currentTarget.open;
157
- setOpen(next);
158
- onToggle == null ? void 0 : onToggle(next);
139
+ function buildCandidate(graphemes, kept) {
140
+ if (kept <= 0) {
141
+ return {
142
+ prefixText: "",
143
+ prefixGraphemeCount: 0,
144
+ suffixText: "",
145
+ suffixGraphemeCount: 0,
146
+ text: ELLIPSIS,
147
+ truncated: true
148
+ };
149
+ }
150
+ const suffixGraphemeCount = kept >= MIN_KEPT ? Math.max(MIN_END, Math.floor(kept / 2)) : Math.floor(kept / 2);
151
+ const prefixGraphemeCount = kept - suffixGraphemeCount;
152
+ const prefixText = graphemes.slice(0, prefixGraphemeCount).join("");
153
+ const suffixText = suffixGraphemeCount > 0 ? graphemes.slice(-suffixGraphemeCount).join("") : "";
154
+ return {
155
+ prefixText,
156
+ prefixGraphemeCount,
157
+ suffixText,
158
+ suffixGraphemeCount,
159
+ text: prefixText + ELLIPSIS + suffixText,
160
+ truncated: true
159
161
  };
160
- if (variant === "card") {
161
- if (disabled) {
162
- return jsxRuntimeExports.jsx("div", { className: cn("list-none px-3 py-4 bg-background-200 [&::-webkit-details-marker]:hidden", summaryClassName), style: { cursor: "not-allowed", opacity: 0.8 }, children: summary });
163
- }
164
- return jsxRuntimeExports.jsxs("details", { className: "group/card last:border-b border-gray-alpha-400", open, onToggle: handleToggle, children: [jsxRuntimeExports.jsx("summary", { className: cn("list-none cursor-pointer px-3 py-4 border-t border-gray-alpha-400 bg-background-200 hover:bg-gray-100 [&::-webkit-details-marker]:hidden", summaryClassName), children: jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5", children: [jsxRuntimeExports.jsx(ChevronRight, { size: 14, className: cn("shrink-0 text-gray-700 group-hover/card:text-gray-1000", open && "rotate-90") }), summary] }) }), jsxRuntimeExports.jsx("div", { className: contentClassName, children })] });
162
+ }
163
+ function middleTruncate(graphemes, availableWidth, measure, fullWidth) {
164
+ const fullText = graphemes.join("");
165
+ const resolvedFullWidth = fullWidth ?? measure(fullText);
166
+ if (availableWidth <= 0 || graphemes.length === 0) {
167
+ return {
168
+ prefixText: fullText,
169
+ prefixGraphemeCount: graphemes.length,
170
+ suffixText: "",
171
+ suffixGraphemeCount: 0,
172
+ text: fullText,
173
+ truncated: false
174
+ };
165
175
  }
166
- const rowClasses = "flex h-9 items-center gap-2 px-2 -mx-2 text-heading-14 font-medium my-2";
167
- if (trailing) {
168
- return jsxRuntimeExports.jsx("section", { className: "-mx-4 border-t px-4 border-gray-alpha-400", children: jsxRuntimeExports.jsxs("div", { className: cn(rowClasses, summaryClassName), children: [jsxRuntimeExports.jsx("div", { className: "isolate relative shrink-0 w-3.5 h-3.5 text-gray-700", children: jsxRuntimeExports.jsx(ChevronRight, { size: 14, className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2" }) }), jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1", children: summary }), jsxRuntimeExports.jsx("div", { className: "shrink-0 pr-1", children: trailing })] }) });
176
+ if (resolvedFullWidth <= availableWidth) {
177
+ return {
178
+ prefixText: fullText,
179
+ prefixGraphemeCount: graphemes.length,
180
+ suffixText: "",
181
+ suffixGraphemeCount: 0,
182
+ text: fullText,
183
+ truncated: false
184
+ };
169
185
  }
170
- if (disabled) {
171
- return jsxRuntimeExports.jsx("section", { className: "-mx-4 border-t px-4 border-gray-alpha-400", children: jsxRuntimeExports.jsx("div", { className: cn(rowClasses, summaryClassName), style: { color: "var(--ds-gray-700)", cursor: "not-allowed" }, children: jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1", children: summary }) }) });
186
+ let lo = 0;
187
+ let hi = graphemes.length - 1;
188
+ let best = -1;
189
+ while (lo <= hi) {
190
+ const mid = lo + hi >>> 1;
191
+ const candidate = buildCandidate(graphemes, mid);
192
+ if (measure(candidate.text) <= availableWidth) {
193
+ best = mid;
194
+ lo = mid + 1;
195
+ } else {
196
+ hi = mid - 1;
197
+ }
172
198
  }
173
- return jsxRuntimeExports.jsx("section", { className: "-mx-4 border-t px-4 border-gray-alpha-400", children: jsxRuntimeExports.jsxs("details", { className: "group", open, onToggle: handleToggle, children: [jsxRuntimeExports.jsxs("summary", { className: cn("group/trigger list-none cursor-pointer rounded hover:bg-gray-alpha-100 [&::-webkit-details-marker]:hidden", rowClasses, summaryClassName), children: [jsxRuntimeExports.jsxs("div", { className: "isolate relative shrink-0 text-gray-700 group-hover/trigger:text-gray-1000 w-3.5 h-3.5", children: [jsxRuntimeExports.jsx(ChevronRight, { size: 14, className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-100 group-open:opacity-0" }), jsxRuntimeExports.jsx(ChevronDown, { size: 14, className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-0 group-open:opacity-100" })] }), jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1", children: summary })] }), jsxRuntimeExports.jsx("div", { className: cn("mb-3", contentClassName), children })] }) });
174
- }
175
- const isReservedAttributeKey = (key) => key.startsWith(RESERVED_ATTRIBUTE_KEY_PREFIX);
176
- function ReservedBadge() {
177
- return jsxRuntimeExports.jsx("span", { className: "shrink-0 rounded border px-1 py-px text-[10px] font-medium leading-3", style: {
178
- borderColor: "var(--ds-gray-alpha-400)",
179
- backgroundColor: "var(--ds-gray-100)",
180
- color: "var(--ds-gray-700)"
181
- }, title: `Keys prefixed with "${RESERVED_ATTRIBUTE_KEY_PREFIX}" are reserved for framework and library code`, children: "Reserved" });
182
- }
183
- function AttributeRow({ attributeKey, value, removed = false }) {
184
- const reserved = isReservedAttributeKey(attributeKey);
185
- return jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-3 py-0.5", children: [jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-center gap-1.5 text-label-13 text-gray-900", style: reserved ? { color: "var(--ds-gray-700)" } : void 0, children: [jsxRuntimeExports.jsx("span", { className: "truncate", children: attributeKey }), reserved && jsxRuntimeExports.jsx(ReservedBadge, {})] }), removed ? jsxRuntimeExports.jsx("span", { className: "shrink-0 text-copy-13 italic text-gray-700", children: "removed" }) : jsxRuntimeExports.jsx("span", { className: "max-w-[60%] truncate text-copy-13 text-gray-1000", title: value, children: value })] });
186
- }
187
- function sortAttributeKeys(keys) {
188
- return [...keys].sort((a, b) => {
189
- const aReserved = isReservedAttributeKey(a);
190
- const bReserved = isReservedAttributeKey(b);
191
- if (aReserved !== bReserved)
192
- return aReserved ? 1 : -1;
193
- return a.localeCompare(b);
194
- });
199
+ if (best === -1) {
200
+ return {
201
+ prefixText: "",
202
+ prefixGraphemeCount: 0,
203
+ suffixText: "",
204
+ suffixGraphemeCount: 0,
205
+ text: "",
206
+ truncated: true
207
+ };
208
+ }
209
+ return buildCandidate(graphemes, best);
195
210
  }
196
- function RunAttributesCard({ attributes }) {
197
- const keys = sortAttributeKeys(Object.keys(attributes));
198
- if (keys.length === 0)
211
+ function getMiddleTruncateCopyText({ prefixText, selectionEnd, selectionStart, suffixText, value }) {
212
+ const visibleText = prefixText + ELLIPSIS + suffixText;
213
+ if (selectionStart < 0 || selectionEnd > visibleText.length || selectionStart >= selectionEnd) {
199
214
  return null;
200
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Attributes", defaultOpen: true, contentClassName: "mb-4", children: jsxRuntimeExports.jsx("div", { className: "flex flex-col", children: keys.map((key) => jsxRuntimeExports.jsx(AttributeRow, { attributeKey: key, value: attributes[key] }, key)) }) });
201
- }
202
- function isAttrSetEventData(data) {
203
- if (data === null || typeof data !== "object")
204
- return false;
205
- const record = data;
206
- return Array.isArray(record.changes) && record.changes.every((c) => c !== null && typeof c === "object" && typeof c.key === "string");
207
- }
208
- function describeWriter(writer) {
209
- if (!writer)
215
+ }
216
+ if (selectionStart === 0 && selectionEnd === visibleText.length) {
217
+ return value;
218
+ }
219
+ const ellipsisStart = prefixText.length;
220
+ const ellipsisEnd = ellipsisStart + ELLIPSIS.length;
221
+ if (selectionStart > ellipsisStart || selectionEnd < ellipsisEnd) {
210
222
  return null;
211
- if (writer.type === "workflow")
212
- return "Set by workflow";
213
- if (writer.type === "step") {
214
- return `Set by step ${writer.stepId} (attempt ${writer.attempt})`;
215
223
  }
216
- return null;
224
+ const originalGraphemes = toGraphemes(value);
225
+ const selectedPrefixGraphemeCount = toGraphemes(visibleText.slice(0, selectionStart)).length;
226
+ const selectedSuffixGraphemeCount = toGraphemes(visibleText.slice(ellipsisEnd, selectionEnd)).length;
227
+ const suffixStart = originalGraphemes.length - toGraphemes(suffixText).length;
228
+ return originalGraphemes.slice(selectedPrefixGraphemeCount, suffixStart + selectedSuffixGraphemeCount).join("");
217
229
  }
218
- function AttrSetEventBlock({ data }) {
219
- if (!isAttrSetEventData(data)) {
220
- return jsxRuntimeExports.jsx(CopyableDataBlock, { data });
230
+ function getMiddleTruncateCopyTextFromSelectionText({ prefixText, selectionText, suffixText, value }) {
231
+ const visibleText = prefixText + ELLIPSIS + suffixText;
232
+ const trimmedSelectionText = selectionText.trim();
233
+ if (!trimmedSelectionText) {
234
+ return null;
221
235
  }
222
- const writerLabel = describeWriter(data.writer);
223
- 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: {
224
- borderColor: "var(--ds-gray-alpha-400)",
225
- color: "var(--ds-gray-700)"
226
- }, children: writerLabel })] });
227
- }
228
- 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}`;
229
- function DecryptButton({ decrypted = false, loading = false, onClick }) {
230
- 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"] })] });
231
- }
232
- function isStructuredError(value) {
233
- return value != null && typeof value === "object" && (typeof value.message === "string" || typeof value.stack === "string");
234
- }
235
- function deriveTitle(message) {
236
- const firstLine = message.split("\n").find((line) => line.trim().length > 0) ?? message;
237
- return firstLine.trim();
238
- }
239
- function ErrorStackBlock({ value }) {
240
- const stack = typeof value.stack === "string" ? value.stack : void 0;
241
- const message = typeof value.message === "string" ? value.message : void 0;
242
- const body = stack ?? message ?? "";
243
- const title = message ? deriveTitle(message) : deriveTitle(body);
244
- const copyText = message && stack && !stack.includes(message) ? `${message}
245
-
246
- ${stack}` : body;
247
- return jsxRuntimeExports.jsxs("div", { className: "relative overflow-hidden rounded-md border", style: {
248
- borderColor: "var(--ds-red-400)",
249
- background: "var(--ds-red-100)"
250
- }, 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: {
251
- color: "var(--ds-red-900)",
252
- borderBottom: "1px solid var(--ds-red-400)"
253
- }, children: [jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4 shrink-0" }), jsxRuntimeExports.jsx("p", {
254
- className: "text-xs font-semibold m-0 truncate",
255
- // The full message or stack is in the body below; the header just
256
- // shows the first line, single-line, with overflow
257
- // ellipsised so a long title doesn't push the copy button or
258
- // wrap into the framed hint/docs lines.
259
- title: message,
260
- children: title
261
- })] }), body && jsxRuntimeExports.jsx("pre", { className: "px-3 py-2.5 text-xs font-mono whitespace-pre-wrap break-words overflow-auto m-0", style: {
262
- color: "var(--ds-red-900)",
263
- background: "var(--ds-red-200)"
264
- }, children: body })] });
236
+ const leading = selectionText.slice(0, selectionText.length - selectionText.trimStart().length);
237
+ const trailing = selectionText.slice(selectionText.trimEnd().length);
238
+ if (trimmedSelectionText === visibleText) {
239
+ return leading + value + trailing;
240
+ }
241
+ if (!trimmedSelectionText.includes(ELLIPSIS)) {
242
+ return null;
243
+ }
244
+ const selectionStart = visibleText.indexOf(trimmedSelectionText);
245
+ if (selectionStart === -1) {
246
+ return null;
247
+ }
248
+ const selectionEnd = selectionStart + trimmedSelectionText.length;
249
+ const mappedText = getMiddleTruncateCopyText({
250
+ prefixText,
251
+ selectionEnd,
252
+ selectionStart,
253
+ suffixText,
254
+ value
255
+ });
256
+ return mappedText === null ? null : leading + mappedText + trailing;
265
257
  }
266
- 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)}`;
267
- function LoadMoreButton({ loading = false, onClick, label = "Load more", loadingLabel = "Loading..." }) {
268
- return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("style", { dangerouslySetInnerHTML: { __html: STYLES$1 } }), jsxRuntimeExports.jsxs("button", { type: "button", onClick, disabled: loading, className: "wf-load-more", children: [loading && jsxRuntimeExports.jsx(Spinner, { size: 14 }), loading ? loadingLabel : label] })] });
258
+ const useIsomorphicLayoutEffect = typeof window === "undefined" ? reactExports.useEffect : reactExports.useLayoutEffect;
259
+ function createFullState(value, graphemes) {
260
+ return {
261
+ displayText: value,
262
+ isTruncated: false,
263
+ prefixGraphemeCount: graphemes.length,
264
+ prefixText: value,
265
+ suffixGraphemeCount: 0,
266
+ suffixText: ""
267
+ };
269
268
  }
270
- const STYLES = `.wf-menu-btn{appearance:none;-webkit-appearance:none;border:none;display:inline-flex;align-items:center;justify-content:center;height:40px;padding:0 12px;border-radius:6px;font-size:14px;font-weight:500;line-height:20px;color:var(--ds-gray-1000);background:var(--ds-background-100);box-shadow:0 0 0 1px var(--ds-gray-400);cursor:pointer;white-space:nowrap;transition:background 150ms}.wf-menu-btn:hover{background:var(--ds-gray-alpha-200)}.wf-menu-item{appearance:none;-webkit-appearance:none;border:none;display:flex;align-items:center;width:100%;height:40px;padding:0 8px;border-radius:6px;font-size:14px;color:var(--ds-gray-1000);background:transparent;cursor:pointer;transition:background 150ms}.wf-menu-item:hover{background:var(--ds-gray-alpha-100)}`;
271
- function MenuDropdown({ options, value, onChange }) {
272
- var _a, _b;
273
- const [open, setOpen] = reactExports.useState(false);
269
+ function useMiddleTruncate(value) {
270
+ const graphemes = reactExports.useMemo(() => toGraphemes(value), [value]);
271
+ const fullState = reactExports.useMemo(() => createFullState(value, graphemes), [graphemes, value]);
274
272
  const ref = reactExports.useRef(null);
275
- const label = ((_a = options.find((o) => o.value === value)) == null ? void 0 : _a.label) ?? ((_b = options[0]) == null ? void 0 : _b.label) ?? "";
276
- reactExports.useEffect(() => {
277
- if (!open)
278
- return;
279
- function handleClickOutside(e) {
280
- if (ref.current && !ref.current.contains(e.target)) {
281
- setOpen(false);
273
+ const measureRef = reactExports.useRef(null);
274
+ const [state, setState] = reactExports.useState(() => fullState);
275
+ const rafRef = reactExports.useRef(0);
276
+ const updateState = reactExports.useCallback((nextState) => {
277
+ setState((currentState) => {
278
+ 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) {
279
+ return currentState;
282
280
  }
283
- }
284
- document.addEventListener("mousedown", handleClickOutside);
285
- return () => document.removeEventListener("mousedown", handleClickOutside);
286
- }, [open]);
287
- return jsxRuntimeExports.jsxs("div", { ref, style: { position: "relative", flexShrink: 0 }, children: [jsxRuntimeExports.jsx("style", { dangerouslySetInnerHTML: { __html: STYLES } }), jsxRuntimeExports.jsxs("button", { type: "button", className: "wf-menu-btn", onClick: () => setOpen(!open), children: [jsxRuntimeExports.jsx("span", { children: label }), jsxRuntimeExports.jsx("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", style: {
281
+ return nextState;
282
+ });
283
+ }, []);
284
+ const recalculate = reactExports.useCallback(() => {
285
+ const el = ref.current;
286
+ const measureEl = measureRef.current;
287
+ if (!el || !measureEl)
288
+ return;
289
+ const available = el.clientWidth;
290
+ if (available <= 0) {
291
+ updateState(fullState);
292
+ return;
293
+ }
294
+ const measure = (text) => {
295
+ measureEl.textContent = text;
296
+ return measureEl.scrollWidth;
297
+ };
298
+ const fullWidth = measure(value);
299
+ if (fullWidth <= available) {
300
+ updateState(fullState);
301
+ return;
302
+ }
303
+ const result = middleTruncate(graphemes, available, measure, fullWidth);
304
+ updateState({
305
+ displayText: result.text,
306
+ isTruncated: result.truncated,
307
+ prefixGraphemeCount: result.prefixGraphemeCount,
308
+ prefixText: result.prefixText,
309
+ suffixGraphemeCount: result.suffixGraphemeCount,
310
+ suffixText: result.suffixText
311
+ });
312
+ }, [fullState, graphemes, updateState, value]);
313
+ useIsomorphicLayoutEffect(() => {
314
+ recalculate();
315
+ }, [recalculate]);
316
+ reactExports.useEffect(() => {
317
+ var _a;
318
+ const el = ref.current;
319
+ if (!el)
320
+ return;
321
+ const debouncedRecalc = () => {
322
+ cancelAnimationFrame(rafRef.current);
323
+ rafRef.current = requestAnimationFrame(recalculate);
324
+ };
325
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(debouncedRecalc) : null;
326
+ ro == null ? void 0 : ro.observe(el);
327
+ window.addEventListener("resize", debouncedRecalc);
328
+ const onFontsLoaded = () => {
329
+ debouncedRecalc();
330
+ };
331
+ const fontSet = "fonts" in document ? document.fonts : null;
332
+ (_a = fontSet == null ? void 0 : fontSet.addEventListener) == null ? void 0 : _a.call(fontSet, "loadingdone", onFontsLoaded);
333
+ return () => {
334
+ var _a2;
335
+ ro == null ? void 0 : ro.disconnect();
336
+ window.removeEventListener("resize", debouncedRecalc);
337
+ cancelAnimationFrame(rafRef.current);
338
+ (_a2 = fontSet == null ? void 0 : fontSet.removeEventListener) == null ? void 0 : _a2.call(fontSet, "loadingdone", onFontsLoaded);
339
+ };
340
+ }, [recalculate]);
341
+ return {
342
+ ref,
343
+ measureRef,
344
+ displayText: state.displayText,
345
+ isTruncated: state.isTruncated,
346
+ prefixGraphemeCount: state.prefixGraphemeCount,
347
+ prefixText: state.prefixText,
348
+ suffixGraphemeCount: state.suffixGraphemeCount,
349
+ suffixText: state.suffixText
350
+ };
351
+ }
352
+ function getRangeOffsets(container, range) {
353
+ if (!container.contains(range.startContainer) || !container.contains(range.endContainer)) {
354
+ return null;
355
+ }
356
+ const startRange = document.createRange();
357
+ startRange.selectNodeContents(container);
358
+ startRange.setEnd(range.startContainer, range.startOffset);
359
+ const endRange = document.createRange();
360
+ endRange.selectNodeContents(container);
361
+ endRange.setEnd(range.endContainer, range.endOffset);
362
+ return {
363
+ end: endRange.toString().length,
364
+ start: startRange.toString().length
365
+ };
366
+ }
367
+ function MiddleTruncate({ value, className, onCopy: onCopyProp, ...props }) {
368
+ const { ref, measureRef, displayText, isTruncated, prefixText, suffixText } = useMiddleTruncate(value);
369
+ const visibleRef = reactExports.useRef(null);
370
+ const handleCopy = reactExports.useCallback((e) => {
371
+ onCopyProp == null ? void 0 : onCopyProp(e);
372
+ if (e.defaultPrevented || !isTruncated)
373
+ return;
374
+ const selection = window.getSelection();
375
+ if (!selection || selection.rangeCount === 0)
376
+ return;
377
+ const selectionText = selection.toString();
378
+ if (!selectionText)
379
+ return;
380
+ const range = selection.getRangeAt(0);
381
+ const visibleEl = visibleRef.current;
382
+ let copyText = null;
383
+ if (e.currentTarget.contains(range.startContainer) && e.currentTarget.contains(range.endContainer) && visibleEl) {
384
+ const offsets = getRangeOffsets(visibleEl, range);
385
+ if (offsets) {
386
+ copyText = getMiddleTruncateCopyText({
387
+ prefixText,
388
+ selectionEnd: offsets.end,
389
+ selectionStart: offsets.start,
390
+ suffixText,
391
+ value
392
+ });
393
+ }
394
+ }
395
+ copyText ?? (copyText = getMiddleTruncateCopyTextFromSelectionText({
396
+ prefixText,
397
+ selectionText,
398
+ suffixText,
399
+ value
400
+ }));
401
+ if (copyText === null)
402
+ return;
403
+ e.preventDefault();
404
+ e.clipboardData.setData("text/plain", copyText);
405
+ }, [onCopyProp, isTruncated, prefixText, suffixText, value]);
406
+ 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 })] });
407
+ }
408
+ const encryptedPlaceholderPreview = `{
409
+ "input": "[encrypted]",
410
+ "result": "[encrypted]"
411
+ }`;
412
+ function EncryptedDataBlock() {
413
+ const ctx = reactExports.useContext(DecryptClickContext);
414
+ 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"] }) })] });
415
+ }
416
+ const serializeForClipboard = (value) => {
417
+ if (typeof value === "string")
418
+ return value;
419
+ if (typeof value === "number" || typeof value === "boolean" || value === null) {
420
+ return String(value);
421
+ }
422
+ try {
423
+ return JSON.stringify(value, null, 2);
424
+ } catch {
425
+ return String(value);
426
+ }
427
+ };
428
+ function CopyableDataBlock({ data }) {
429
+ 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 })] });
430
+ }
431
+ function isReservedAttributeKey(key) {
432
+ return key.startsWith(RESERVED_ATTRIBUTE_KEY_PREFIX);
433
+ }
434
+ const rowValueVariants = cva("max-w-[60%] truncate text-right text-copy-13 text-gray-1000", {
435
+ variants: {
436
+ variant: {
437
+ default: "",
438
+ mono: "font-mono"
439
+ }
440
+ },
441
+ defaultVariants: {
442
+ variant: "default"
443
+ }
444
+ });
445
+ const rowCopyValueVariants = cva("flex min-w-0 max-w-[60%] items-center justify-end gap-1 text-copy-13 text-gray-1000", {
446
+ variants: {
447
+ variant: {
448
+ default: "",
449
+ mono: "font-mono"
450
+ }
451
+ },
452
+ defaultVariants: {
453
+ variant: "default"
454
+ }
455
+ });
456
+ function DetailKeyValueRowBase({ label, value, copyText, removed = false, variant }) {
457
+ const stringValue = typeof value === "string" ? value : void 0;
458
+ 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: [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: value })] });
459
+ }
460
+ function DetailKeyValueRow(props) {
461
+ return jsxRuntimeExports.jsx(DetailKeyValueRowBase, { ...props });
462
+ }
463
+ function DetailMonoKeyValueRow(props) {
464
+ return jsxRuntimeExports.jsx(DetailKeyValueRowBase, { ...props, variant: "mono" });
465
+ }
466
+ function AttributeRow({ attributeKey, value, removed = false }) {
467
+ return jsxRuntimeExports.jsx(DetailKeyValueRow, { label: attributeKey, value, removed });
468
+ }
469
+ function sortAttributeKeys(keys) {
470
+ return [...keys].sort((a, b) => {
471
+ const aReserved = isReservedAttributeKey(a);
472
+ const bReserved = isReservedAttributeKey(b);
473
+ if (aReserved !== bReserved)
474
+ return aReserved ? 1 : -1;
475
+ return a.localeCompare(b);
476
+ });
477
+ }
478
+ function RunAttributesCard({ attributes }) {
479
+ const keys = sortAttributeKeys(Object.keys(attributes));
480
+ if (keys.length === 0)
481
+ return null;
482
+ 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)) }) })] });
483
+ }
484
+ function formatMetadataValue(value) {
485
+ if (value == null) {
486
+ return String(value);
487
+ }
488
+ if (typeof value === "string") {
489
+ return value;
490
+ }
491
+ if (typeof value === "number" || typeof value === "boolean") {
492
+ return String(value);
493
+ }
494
+ try {
495
+ return JSON.stringify(value);
496
+ } catch {
497
+ return String(value);
498
+ }
499
+ }
500
+ function isMetadataRecord(value) {
501
+ return value !== null && typeof value === "object" && !Array.isArray(value);
502
+ }
503
+ function RunMetadataCard({ metadata }) {
504
+ if (!isMetadataRecord(metadata)) {
505
+ return jsxRuntimeExports.jsxs(CollapsibleRoot, { defaultOpen: true, children: [jsxRuntimeExports.jsx(CollapsibleTrigger, { children: "Metadata" }), jsxRuntimeExports.jsx(CollapsibleContent, { className: "mb-4", children: jsxRuntimeExports.jsx(CopyableDataBlock, { data: metadata }) })] });
506
+ }
507
+ const keys = Object.keys(metadata).sort((a, b) => a.localeCompare(b));
508
+ if (keys.length === 0)
509
+ return null;
510
+ 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)) }) })] });
511
+ }
512
+ function isAttrSetEventData(data) {
513
+ if (data === null || typeof data !== "object")
514
+ return false;
515
+ const record = data;
516
+ return Array.isArray(record.changes) && record.changes.every((c) => c !== null && typeof c === "object" && typeof c.key === "string");
517
+ }
518
+ function describeWriter(writer) {
519
+ if (!writer)
520
+ return null;
521
+ if (writer.type === "workflow")
522
+ return "Set by workflow";
523
+ if (writer.type === "step") {
524
+ return `Set by step ${writer.stepId} (attempt ${writer.attempt})`;
525
+ }
526
+ return null;
527
+ }
528
+ function AttrSetEventBlock({ data }) {
529
+ if (!isAttrSetEventData(data)) {
530
+ return jsxRuntimeExports.jsx(CopyableDataBlock, { data });
531
+ }
532
+ const writerLabel = describeWriter(data.writer);
533
+ 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: {
534
+ borderColor: "var(--ds-gray-alpha-400)",
535
+ color: "var(--ds-gray-700)"
536
+ }, children: writerLabel })] });
537
+ }
538
+ 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}`;
539
+ function DecryptButton({ decrypted = false, loading = false, onClick }) {
540
+ 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"] })] });
541
+ }
542
+ function isStructuredError(value) {
543
+ return value != null && typeof value === "object" && (typeof value.message === "string" || typeof value.stack === "string");
544
+ }
545
+ function deriveTitle(message) {
546
+ const firstLine = message.split("\n").find((line) => line.trim().length > 0) ?? message;
547
+ return firstLine.trim();
548
+ }
549
+ function ErrorStackBlock({ value }) {
550
+ const stack = typeof value.stack === "string" ? value.stack : void 0;
551
+ const message = typeof value.message === "string" ? value.message : void 0;
552
+ const body = stack ?? message ?? "";
553
+ const title = message ? deriveTitle(message) : deriveTitle(body);
554
+ const copyText = message && stack && !stack.includes(message) ? `${message}
555
+
556
+ ${stack}` : body;
557
+ return jsxRuntimeExports.jsxs("div", { className: "relative overflow-hidden rounded-md border", style: {
558
+ borderColor: "var(--ds-red-400)",
559
+ background: "var(--ds-red-100)"
560
+ }, 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: {
561
+ color: "var(--ds-red-900)",
562
+ borderBottom: "1px solid var(--ds-red-400)"
563
+ }, children: [jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4 shrink-0" }), jsxRuntimeExports.jsx("p", {
564
+ className: "text-xs font-semibold m-0 truncate",
565
+ // The full message or stack is in the body below; the header just
566
+ // shows the first line, single-line, with overflow
567
+ // ellipsised so a long title doesn't push the copy button or
568
+ // wrap into the framed hint/docs lines.
569
+ title: message,
570
+ children: title
571
+ })] }), body && jsxRuntimeExports.jsx("pre", { className: "px-3 py-2.5 text-xs font-mono whitespace-pre-wrap break-words overflow-auto m-0", style: {
572
+ color: "var(--ds-red-900)",
573
+ background: "var(--ds-red-200)"
574
+ }, children: body })] });
575
+ }
576
+ 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)}`;
577
+ function LoadMoreButton({ loading = false, onClick, label = "Load more", loadingLabel = "Loading..." }) {
578
+ return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("style", { dangerouslySetInnerHTML: { __html: STYLES$1 } }), jsxRuntimeExports.jsxs("button", { type: "button", onClick, disabled: loading, className: "wf-load-more", children: [loading && jsxRuntimeExports.jsx(Spinner, { size: 14 }), loading ? loadingLabel : label] })] });
579
+ }
580
+ const STYLES = `.wf-menu-btn{appearance:none;-webkit-appearance:none;border:none;display:inline-flex;align-items:center;justify-content:center;height:40px;padding:0 12px;border-radius:6px;font-size:14px;font-weight:500;line-height:20px;color:var(--ds-gray-1000);background:var(--ds-background-100);box-shadow:0 0 0 1px var(--ds-gray-400);cursor:pointer;white-space:nowrap;transition:background 150ms}.wf-menu-btn:hover{background:var(--ds-gray-alpha-200)}.wf-menu-item{appearance:none;-webkit-appearance:none;border:none;display:flex;align-items:center;width:100%;height:40px;padding:0 8px;border-radius:6px;font-size:14px;color:var(--ds-gray-1000);background:transparent;cursor:pointer;transition:background 150ms}.wf-menu-item:hover{background:var(--ds-gray-alpha-100)}`;
581
+ function MenuDropdown({ options, value, onChange }) {
582
+ var _a, _b;
583
+ const [open, setOpen] = reactExports.useState(false);
584
+ const ref = reactExports.useRef(null);
585
+ const label = ((_a = options.find((o) => o.value === value)) == null ? void 0 : _a.label) ?? ((_b = options[0]) == null ? void 0 : _b.label) ?? "";
586
+ reactExports.useEffect(() => {
587
+ if (!open)
588
+ return;
589
+ function handleClickOutside(e) {
590
+ if (ref.current && !ref.current.contains(e.target)) {
591
+ setOpen(false);
592
+ }
593
+ }
594
+ document.addEventListener("mousedown", handleClickOutside);
595
+ return () => document.removeEventListener("mousedown", handleClickOutside);
596
+ }, [open]);
597
+ return jsxRuntimeExports.jsxs("div", { ref, style: { position: "relative", flexShrink: 0 }, children: [jsxRuntimeExports.jsx("style", { dangerouslySetInnerHTML: { __html: STYLES } }), jsxRuntimeExports.jsxs("button", { type: "button", className: "wf-menu-btn", onClick: () => setOpen(!open), children: [jsxRuntimeExports.jsx("span", { children: label }), jsxRuntimeExports.jsx("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", style: {
288
598
  marginLeft: 16,
289
599
  marginRight: -4,
290
600
  color: "var(--ds-gray-900)"
@@ -1040,7 +1350,13 @@ function EventListViewInner({ events, run, onLoadEventData, hasMoreEvents = fals
1040
1350
  function EventListView(props) {
1041
1351
  return jsxRuntimeExports.jsx(ContextCardProvider, { children: jsxRuntimeExports.jsx(EventListViewInner, { ...props }) });
1042
1352
  }
1043
- const COL_TEMPLATE = "340px 1px minmax(50px, 1fr)";
1353
+ const GUTTER_PX = 1;
1354
+ const MIN_PX = 50;
1355
+ const DEFAULT_START_PX = 340;
1356
+ function paneColTemplate(startPx) {
1357
+ return `${startPx}px ${GUTTER_PX}px minmax(${MIN_PX}px, 1fr)`;
1358
+ }
1359
+ const COL_TEMPLATE = paneColTemplate(DEFAULT_START_PX);
1044
1360
  const ROWS = [
1045
1361
  { id: "r0", name: 62, off: 0, bar: 72 },
1046
1362
  { id: "r1", name: 78, off: 6, bar: 48 },
@@ -1235,124 +1551,36 @@ const stepEventsToStepEntity = (events) => {
1235
1551
  };
1236
1552
  function stepToSpan(stepEvents, maxEndTime) {
1237
1553
  const step = stepEventsToStepEntity(stepEvents);
1238
- if (!step) {
1239
- return null;
1240
- }
1241
- const parsedName = parseStepName(String(step.stepName));
1242
- const attributes = {
1243
- resource: "step",
1244
- data: step
1245
- };
1246
- const resource = "step";
1247
- const events = convertEventsToSpanEvents(stepEvents, false, {
1248
- preferOccurredAt: true
1249
- });
1250
- const spanStartEvent = stepEvents.find((event) => event.eventType === "step_created") ?? stepEvents[0];
1251
- const spanStartTime = getEventTimestamp(spanStartEvent) ?? new Date(step.createdAt);
1252
- let activeStartTime = step.startedAt ? new Date(step.startedAt) : void 0;
1253
- const firstStartEvent = stepEvents.find((event) => event.eventType === "step_started");
1254
- if (firstStartEvent) {
1255
- activeStartTime = getEventTimestamp(firstStartEvent) ?? new Date(firstStartEvent.createdAt);
1256
- }
1257
- let endTime = new Date(maxEndTime);
1258
- if (step.completedAt) {
1259
- const completedEvent = stepEvents.slice().reverse().find((event) => event.eventType === "step_completed" || event.eventType === "step_failed");
1260
- endTime = getEventTimestamp(completedEvent) ?? new Date(step.completedAt);
1261
- }
1262
- return {
1263
- spanId: String(step.stepId),
1264
- name: (parsedName == null ? void 0 : parsedName.shortName) ?? String(step.stepName),
1265
- kind: 1,
1266
- // INTERNAL span kind
1267
- resource,
1268
- library: WORKFLOW_LIBRARY,
1269
- status: { code: 0 },
1270
- traceFlags: 1,
1271
- attributes,
1272
- links: [],
1273
- events,
1274
- startTime: dateToOtelTime(spanStartTime),
1275
- endTime: dateToOtelTime(endTime),
1276
- duration: calculateDuration(spanStartTime, endTime),
1277
- // Only set activeStartTime if it differs from startTime (i.e., there was a queued period)
1278
- activeStartTime: activeStartTime && activeStartTime.getTime() > spanStartTime.getTime() ? dateToOtelTime(activeStartTime) : void 0
1279
- };
1280
- }
1281
- const hookEventsToHookEntity = (events) => {
1282
- var _a;
1283
- const createdEvent = events.find((event) => event.eventType === "hook_created");
1284
- if (!createdEvent) {
1285
- return null;
1286
- }
1287
- const receivedEvents = events.filter((event) => event.eventType === "hook_received");
1288
- const disposedEvents = events.filter((event) => event.eventType === "hook_disposed");
1289
- const lastReceivedEvent = receivedEvents.at(-1);
1290
- return {
1291
- hookId: createdEvent.correlationId,
1292
- runId: createdEvent.runId,
1293
- token: (_a = createdEvent.eventData) == null ? void 0 : _a.token,
1294
- createdAt: getEventTimestamp(createdEvent) ?? createdEvent.createdAt,
1295
- receivedCount: receivedEvents.length,
1296
- lastReceivedAt: getEventTimestamp(lastReceivedEvent),
1297
- disposedAt: getEventTimestamp(disposedEvents.at(-1))
1298
- };
1299
- };
1300
- function hookToSpan(hookEvents, maxEndTime) {
1301
- const hook = hookEventsToHookEntity(hookEvents);
1302
- if (!hook) {
1303
- return null;
1304
- }
1305
- const events = convertEventsToSpanEvents(hookEvents, false);
1306
- const endTime = hook.disposedAt || maxEndTime;
1307
- return {
1308
- spanId: String(hook.hookId),
1309
- name: hook.token ?? String(hook.hookId),
1310
- kind: 1,
1311
- // INTERNAL span kind
1312
- resource: "hook",
1313
- library: WORKFLOW_LIBRARY,
1314
- status: { code: 1 },
1315
- traceFlags: 1,
1316
- attributes: {
1317
- resource: "hook",
1318
- data: hook
1319
- },
1320
- links: [],
1321
- events,
1322
- startTime: dateToOtelTime(hook.createdAt),
1323
- endTime: dateToOtelTime(endTime),
1324
- duration: calculateDuration(hook.createdAt, endTime)
1325
- };
1326
- }
1327
- function runToSpan(run, runEvents, nowTime) {
1328
- var _a;
1329
- const now = nowTime ?? /* @__PURE__ */ new Date();
1330
- const runCreatedEvent = runEvents.find((event) => event.eventType === "run_created");
1331
- const runStartedEvent = runEvents.find((event) => event.eventType === "run_started");
1332
- const terminalEvent = runEvents.slice().reverse().find((event) => event.eventType === "run_completed" || event.eventType === "run_failed" || event.eventType === "run_cancelled");
1333
- const spanStartTime = getEventTimestamp(runCreatedEvent) ?? new Date(run.createdAt);
1334
- const activeStartTime = getEventTimestamp(runStartedEvent) ?? (run.startedAt ? new Date(run.startedAt) : void 0);
1335
- const completedAt = getEventTimestamp(terminalEvent) ?? run.completedAt ?? void 0;
1336
- const endTime = completedAt ?? now;
1337
- const { input: _i, output: _o, error: _e, ...runIdentity } = run;
1554
+ if (!step) {
1555
+ return null;
1556
+ }
1557
+ const parsedName = parseStepName(String(step.stepName));
1338
1558
  const attributes = {
1339
- resource: "run",
1340
- data: {
1341
- ...runIdentity,
1342
- createdAt: spanStartTime,
1343
- startedAt: activeStartTime,
1344
- completedAt
1345
- }
1559
+ resource: "step",
1560
+ data: step
1346
1561
  };
1347
- const events = convertEventsToSpanEvents(runEvents, false, {
1562
+ const resource = "step";
1563
+ const events = convertEventsToSpanEvents(stepEvents, false, {
1348
1564
  preferOccurredAt: true
1349
1565
  });
1566
+ const spanStartEvent = stepEvents.find((event) => event.eventType === "step_created") ?? stepEvents[0];
1567
+ const spanStartTime = getEventTimestamp(spanStartEvent) ?? new Date(step.createdAt);
1568
+ let activeStartTime = step.startedAt ? new Date(step.startedAt) : void 0;
1569
+ const firstStartEvent = stepEvents.find((event) => event.eventType === "step_started");
1570
+ if (firstStartEvent) {
1571
+ activeStartTime = getEventTimestamp(firstStartEvent) ?? new Date(firstStartEvent.createdAt);
1572
+ }
1573
+ let endTime = new Date(maxEndTime);
1574
+ if (step.completedAt) {
1575
+ const completedEvent = stepEvents.slice().reverse().find((event) => event.eventType === "step_completed" || event.eventType === "step_failed");
1576
+ endTime = getEventTimestamp(completedEvent) ?? new Date(step.completedAt);
1577
+ }
1350
1578
  return {
1351
- spanId: String(run.runId),
1352
- name: String(((_a = parseWorkflowName(run.workflowName)) == null ? void 0 : _a.shortName) ?? "?"),
1579
+ spanId: String(step.stepId),
1580
+ name: (parsedName == null ? void 0 : parsedName.shortName) ?? String(step.stepName),
1353
1581
  kind: 1,
1354
1582
  // INTERNAL span kind
1355
- resource: "run",
1583
+ resource,
1356
1584
  library: WORKFLOW_LIBRARY,
1357
1585
  status: { code: 0 },
1358
1586
  traceFlags: 1,
@@ -1366,391 +1594,199 @@ function runToSpan(run, runEvents, nowTime) {
1366
1594
  activeStartTime: activeStartTime && activeStartTime.getTime() > spanStartTime.getTime() ? dateToOtelTime(activeStartTime) : void 0
1367
1595
  };
1368
1596
  }
1369
- const isStepEvent = (eventType) => eventType.startsWith("step_");
1370
- const isTimerEvent = (eventType) => eventType === "wait_created" || eventType === "wait_completed";
1371
- const isHookLifecycleEvent = (eventType) => eventType === "hook_received" || eventType === "hook_created" || eventType === "hook_disposed";
1372
- const isRunLevelEvent = (event) => !event.correlationId || !isTimerEvent(event.eventType) && !isHookLifecycleEvent(event.eventType) && !isStepEvent(event.eventType);
1373
- function filterSpanRawEvents(events, resource, spanId) {
1374
- if (resource === "run") {
1375
- return events.filter(isRunLevelEvent);
1376
- }
1377
- if (!spanId)
1378
- return [];
1379
- return events.filter((e) => e.correlationId === spanId);
1380
- }
1381
- function pushEvent(map, correlationId, event) {
1382
- const existing = map.get(correlationId);
1383
- if (existing) {
1384
- existing.push(event);
1385
- return;
1386
- }
1387
- map.set(correlationId, [event]);
1388
- }
1389
- function groupEventsByCorrelation(events) {
1390
- const eventsByStepId = /* @__PURE__ */ new Map();
1391
- const runLevelEvents = [];
1392
- const timerEvents = /* @__PURE__ */ new Map();
1393
- const hookEvents = /* @__PURE__ */ new Map();
1394
- for (const event of events) {
1395
- const correlationId = event.correlationId;
1396
- if (!correlationId) {
1397
- runLevelEvents.push(event);
1398
- continue;
1399
- }
1400
- if (isTimerEvent(event.eventType)) {
1401
- pushEvent(timerEvents, correlationId, event);
1402
- continue;
1403
- }
1404
- if (isHookLifecycleEvent(event.eventType)) {
1405
- pushEvent(hookEvents, correlationId, event);
1406
- continue;
1407
- }
1408
- if (isStepEvent(event.eventType)) {
1409
- pushEvent(eventsByStepId, correlationId, event);
1410
- continue;
1411
- }
1412
- runLevelEvents.push(event);
1413
- }
1414
- return { eventsByStepId, runLevelEvents, timerEvents, hookEvents };
1415
- }
1416
- function computeLatestKnownTime(events, run) {
1417
- let latest = new Date(run.createdAt).getTime();
1418
- for (const event of events) {
1419
- const t = (getEventTimestamp(event) ?? new Date(event.createdAt)).getTime();
1420
- if (t > latest)
1421
- latest = t;
1422
- }
1423
- return new Date(latest);
1424
- }
1425
- function buildSpans(run, groupedEvents, now, latestKnownTime) {
1426
- const childMaxEnd = latestKnownTime;
1427
- const runMaxEnd = run.completedAt ?? now;
1428
- const stepSpans = Array.from(groupedEvents.eventsByStepId.values()).map((events) => stepToSpan(events, childMaxEnd)).filter((span) => span !== null);
1429
- const hookSpans = Array.from(groupedEvents.hookEvents.values()).map((events) => hookToSpan(events, childMaxEnd)).filter((span) => span !== null);
1430
- const waitSpans = Array.from(groupedEvents.timerEvents.values()).map((events) => waitToSpan(events, childMaxEnd, runMaxEnd)).filter((span) => span !== null);
1431
- return {
1432
- runSpan: runToSpan(run, groupedEvents.runLevelEvents, now),
1433
- spans: [...stepSpans, ...hookSpans, ...waitSpans]
1434
- };
1435
- }
1436
- function cascadeSpans(runSpan, spans) {
1437
- const sortedSpans = [
1438
- runSpan,
1439
- ...spans.slice().sort((a, b) => {
1440
- const aStart = otelTimeToMs(a.startTime);
1441
- const bStart = otelTimeToMs(b.startTime);
1442
- return aStart - bStart;
1443
- })
1444
- ];
1445
- return sortedSpans.map((span, index) => {
1446
- const parentSpanId = index === 0 ? void 0 : String(sortedSpans[index - 1].spanId);
1447
- return {
1448
- ...span,
1449
- parentSpanId
1450
- };
1451
- });
1452
- }
1453
- function buildTrace(run, events, now) {
1454
- const groupedEvents = groupEventsByCorrelation(events);
1455
- const latestKnownTime = computeLatestKnownTime(events, run);
1456
- const { runSpan, spans } = buildSpans(run, groupedEvents, now, latestKnownTime);
1457
- const sortedCascadingSpans = cascadeSpans(runSpan, spans);
1458
- const traceStartMs = otelTimeToMs(runSpan.startTime);
1459
- const knownDurationMs = latestKnownTime.getTime() - traceStartMs;
1460
- return {
1461
- traceId: run.runId,
1462
- rootSpanId: run.runId,
1463
- spans: sortedCascadingSpans,
1464
- resources: [
1465
- {
1466
- name: "workflow",
1467
- attributes: {
1468
- "service.name": WORKFLOW_LIBRARY.name
1469
- }
1470
- }
1471
- ],
1472
- knownDurationMs: Math.max(0, knownDurationMs)
1473
- };
1474
- }
1475
- const ELLIPSIS = "...";
1476
- const MIN_START = 3;
1477
- const MIN_END = 3;
1478
- const MIN_KEPT = MIN_START + MIN_END;
1479
- const graphemeSegmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
1480
- function toGraphemes(text) {
1481
- if (graphemeSegmenter) {
1482
- return [...graphemeSegmenter.segment(text)].map((s) => s.segment);
1483
- }
1484
- return Array.from(text);
1485
- }
1486
- function buildCandidate(graphemes, kept) {
1487
- if (kept <= 0) {
1488
- return {
1489
- prefixText: "",
1490
- prefixGraphemeCount: 0,
1491
- suffixText: "",
1492
- suffixGraphemeCount: 0,
1493
- text: ELLIPSIS,
1494
- truncated: true
1495
- };
1496
- }
1497
- const suffixGraphemeCount = kept >= MIN_KEPT ? Math.max(MIN_END, Math.floor(kept / 2)) : Math.floor(kept / 2);
1498
- const prefixGraphemeCount = kept - suffixGraphemeCount;
1499
- const prefixText = graphemes.slice(0, prefixGraphemeCount).join("");
1500
- const suffixText = suffixGraphemeCount > 0 ? graphemes.slice(-suffixGraphemeCount).join("") : "";
1501
- return {
1502
- prefixText,
1503
- prefixGraphemeCount,
1504
- suffixText,
1505
- suffixGraphemeCount,
1506
- text: prefixText + ELLIPSIS + suffixText,
1507
- truncated: true
1508
- };
1509
- }
1510
- function middleTruncate(graphemes, availableWidth, measure, fullWidth) {
1511
- const fullText = graphemes.join("");
1512
- const resolvedFullWidth = fullWidth ?? measure(fullText);
1513
- if (availableWidth <= 0 || graphemes.length === 0) {
1514
- return {
1515
- prefixText: fullText,
1516
- prefixGraphemeCount: graphemes.length,
1517
- suffixText: "",
1518
- suffixGraphemeCount: 0,
1519
- text: fullText,
1520
- truncated: false
1521
- };
1522
- }
1523
- if (resolvedFullWidth <= availableWidth) {
1524
- return {
1525
- prefixText: fullText,
1526
- prefixGraphemeCount: graphemes.length,
1527
- suffixText: "",
1528
- suffixGraphemeCount: 0,
1529
- text: fullText,
1530
- truncated: false
1531
- };
1532
- }
1533
- let lo = 0;
1534
- let hi = graphemes.length - 1;
1535
- let best = -1;
1536
- while (lo <= hi) {
1537
- const mid = lo + hi >>> 1;
1538
- const candidate = buildCandidate(graphemes, mid);
1539
- if (measure(candidate.text) <= availableWidth) {
1540
- best = mid;
1541
- lo = mid + 1;
1542
- } else {
1543
- hi = mid - 1;
1544
- }
1545
- }
1546
- if (best === -1) {
1547
- return {
1548
- prefixText: "",
1549
- prefixGraphemeCount: 0,
1550
- suffixText: "",
1551
- suffixGraphemeCount: 0,
1552
- text: "",
1553
- truncated: true
1554
- };
1555
- }
1556
- return buildCandidate(graphemes, best);
1557
- }
1558
- function getMiddleTruncateCopyText({ prefixText, selectionEnd, selectionStart, suffixText, value }) {
1559
- const visibleText = prefixText + ELLIPSIS + suffixText;
1560
- if (selectionStart < 0 || selectionEnd > visibleText.length || selectionStart >= selectionEnd) {
1561
- return null;
1562
- }
1563
- if (selectionStart === 0 && selectionEnd === visibleText.length) {
1564
- return value;
1565
- }
1566
- const ellipsisStart = prefixText.length;
1567
- const ellipsisEnd = ellipsisStart + ELLIPSIS.length;
1568
- if (selectionStart > ellipsisStart || selectionEnd < ellipsisEnd) {
1569
- return null;
1570
- }
1571
- const originalGraphemes = toGraphemes(value);
1572
- const selectedPrefixGraphemeCount = toGraphemes(visibleText.slice(0, selectionStart)).length;
1573
- const selectedSuffixGraphemeCount = toGraphemes(visibleText.slice(ellipsisEnd, selectionEnd)).length;
1574
- const suffixStart = originalGraphemes.length - toGraphemes(suffixText).length;
1575
- return originalGraphemes.slice(selectedPrefixGraphemeCount, suffixStart + selectedSuffixGraphemeCount).join("");
1576
- }
1577
- function getMiddleTruncateCopyTextFromSelectionText({ prefixText, selectionText, suffixText, value }) {
1578
- const visibleText = prefixText + ELLIPSIS + suffixText;
1579
- const trimmedSelectionText = selectionText.trim();
1580
- if (!trimmedSelectionText) {
1581
- return null;
1582
- }
1583
- const leading = selectionText.slice(0, selectionText.length - selectionText.trimStart().length);
1584
- const trailing = selectionText.slice(selectionText.trimEnd().length);
1585
- if (trimmedSelectionText === visibleText) {
1586
- return leading + value + trailing;
1587
- }
1588
- if (!trimmedSelectionText.includes(ELLIPSIS)) {
1597
+ const hookEventsToHookEntity = (events) => {
1598
+ var _a;
1599
+ const createdEvent = events.find((event) => event.eventType === "hook_created");
1600
+ if (!createdEvent) {
1589
1601
  return null;
1590
1602
  }
1591
- const selectionStart = visibleText.indexOf(trimmedSelectionText);
1592
- if (selectionStart === -1) {
1603
+ const receivedEvents = events.filter((event) => event.eventType === "hook_received");
1604
+ const disposedEvents = events.filter((event) => event.eventType === "hook_disposed");
1605
+ const lastReceivedEvent = receivedEvents.at(-1);
1606
+ return {
1607
+ hookId: createdEvent.correlationId,
1608
+ runId: createdEvent.runId,
1609
+ token: (_a = createdEvent.eventData) == null ? void 0 : _a.token,
1610
+ createdAt: getEventTimestamp(createdEvent) ?? createdEvent.createdAt,
1611
+ receivedCount: receivedEvents.length,
1612
+ lastReceivedAt: getEventTimestamp(lastReceivedEvent),
1613
+ disposedAt: getEventTimestamp(disposedEvents.at(-1))
1614
+ };
1615
+ };
1616
+ function hookToSpan(hookEvents, maxEndTime) {
1617
+ const hook = hookEventsToHookEntity(hookEvents);
1618
+ if (!hook) {
1593
1619
  return null;
1594
1620
  }
1595
- const selectionEnd = selectionStart + trimmedSelectionText.length;
1596
- const mappedText = getMiddleTruncateCopyText({
1597
- prefixText,
1598
- selectionEnd,
1599
- selectionStart,
1600
- suffixText,
1601
- value
1602
- });
1603
- return mappedText === null ? null : leading + mappedText + trailing;
1604
- }
1605
- const useIsomorphicLayoutEffect = typeof window === "undefined" ? reactExports.useEffect : reactExports.useLayoutEffect;
1606
- function createFullState(value, graphemes) {
1621
+ const events = convertEventsToSpanEvents(hookEvents, false);
1622
+ const endTime = hook.disposedAt || maxEndTime;
1607
1623
  return {
1608
- displayText: value,
1609
- isTruncated: false,
1610
- prefixGraphemeCount: graphemes.length,
1611
- prefixText: value,
1612
- suffixGraphemeCount: 0,
1613
- suffixText: ""
1624
+ spanId: String(hook.hookId),
1625
+ name: hook.token ?? String(hook.hookId),
1626
+ kind: 1,
1627
+ // INTERNAL span kind
1628
+ resource: "hook",
1629
+ library: WORKFLOW_LIBRARY,
1630
+ status: { code: 1 },
1631
+ traceFlags: 1,
1632
+ attributes: {
1633
+ resource: "hook",
1634
+ data: hook
1635
+ },
1636
+ links: [],
1637
+ events,
1638
+ startTime: dateToOtelTime(hook.createdAt),
1639
+ endTime: dateToOtelTime(endTime),
1640
+ duration: calculateDuration(hook.createdAt, endTime)
1614
1641
  };
1615
1642
  }
1616
- function useMiddleTruncate(value) {
1617
- const graphemes = reactExports.useMemo(() => toGraphemes(value), [value]);
1618
- const fullState = reactExports.useMemo(() => createFullState(value, graphemes), [graphemes, value]);
1619
- const ref = reactExports.useRef(null);
1620
- const measureRef = reactExports.useRef(null);
1621
- const [state, setState] = reactExports.useState(() => fullState);
1622
- const rafRef = reactExports.useRef(0);
1623
- const updateState = reactExports.useCallback((nextState) => {
1624
- setState((currentState) => {
1625
- 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) {
1626
- return currentState;
1627
- }
1628
- return nextState;
1629
- });
1630
- }, []);
1631
- const recalculate = reactExports.useCallback(() => {
1632
- const el = ref.current;
1633
- const measureEl = measureRef.current;
1634
- if (!el || !measureEl)
1635
- return;
1636
- const available = el.clientWidth;
1637
- if (available <= 0) {
1638
- updateState(fullState);
1639
- return;
1640
- }
1641
- const measure = (text) => {
1642
- measureEl.textContent = text;
1643
- return measureEl.scrollWidth;
1644
- };
1645
- const fullWidth = measure(value);
1646
- if (fullWidth <= available) {
1647
- updateState(fullState);
1648
- return;
1643
+ function runToSpan(run, runEvents, nowTime) {
1644
+ var _a;
1645
+ const now = nowTime ?? /* @__PURE__ */ new Date();
1646
+ const runCreatedEvent = runEvents.find((event) => event.eventType === "run_created");
1647
+ const runStartedEvent = runEvents.find((event) => event.eventType === "run_started");
1648
+ const terminalEvent = runEvents.slice().reverse().find((event) => event.eventType === "run_completed" || event.eventType === "run_failed" || event.eventType === "run_cancelled");
1649
+ const spanStartTime = getEventTimestamp(runCreatedEvent) ?? new Date(run.createdAt);
1650
+ const activeStartTime = getEventTimestamp(runStartedEvent) ?? (run.startedAt ? new Date(run.startedAt) : void 0);
1651
+ const completedAt = getEventTimestamp(terminalEvent) ?? run.completedAt ?? void 0;
1652
+ const endTime = completedAt ?? now;
1653
+ const { input: _i, output: _o, error: _e, ...runIdentity } = run;
1654
+ const attributes = {
1655
+ resource: "run",
1656
+ data: {
1657
+ ...runIdentity,
1658
+ createdAt: spanStartTime,
1659
+ startedAt: activeStartTime,
1660
+ completedAt
1649
1661
  }
1650
- const result = middleTruncate(graphemes, available, measure, fullWidth);
1651
- updateState({
1652
- displayText: result.text,
1653
- isTruncated: result.truncated,
1654
- prefixGraphemeCount: result.prefixGraphemeCount,
1655
- prefixText: result.prefixText,
1656
- suffixGraphemeCount: result.suffixGraphemeCount,
1657
- suffixText: result.suffixText
1658
- });
1659
- }, [fullState, graphemes, updateState, value]);
1660
- useIsomorphicLayoutEffect(() => {
1661
- recalculate();
1662
- }, [recalculate]);
1663
- reactExports.useEffect(() => {
1664
- var _a;
1665
- const el = ref.current;
1666
- if (!el)
1667
- return;
1668
- const debouncedRecalc = () => {
1669
- cancelAnimationFrame(rafRef.current);
1670
- rafRef.current = requestAnimationFrame(recalculate);
1671
- };
1672
- const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(debouncedRecalc) : null;
1673
- ro == null ? void 0 : ro.observe(el);
1674
- window.addEventListener("resize", debouncedRecalc);
1675
- const onFontsLoaded = () => {
1676
- debouncedRecalc();
1677
- };
1678
- const fontSet = "fonts" in document ? document.fonts : null;
1679
- (_a = fontSet == null ? void 0 : fontSet.addEventListener) == null ? void 0 : _a.call(fontSet, "loadingdone", onFontsLoaded);
1680
- return () => {
1681
- var _a2;
1682
- ro == null ? void 0 : ro.disconnect();
1683
- window.removeEventListener("resize", debouncedRecalc);
1684
- cancelAnimationFrame(rafRef.current);
1685
- (_a2 = fontSet == null ? void 0 : fontSet.removeEventListener) == null ? void 0 : _a2.call(fontSet, "loadingdone", onFontsLoaded);
1686
- };
1687
- }, [recalculate]);
1662
+ };
1663
+ const events = convertEventsToSpanEvents(runEvents, false, {
1664
+ preferOccurredAt: true
1665
+ });
1688
1666
  return {
1689
- ref,
1690
- measureRef,
1691
- displayText: state.displayText,
1692
- isTruncated: state.isTruncated,
1693
- prefixGraphemeCount: state.prefixGraphemeCount,
1694
- prefixText: state.prefixText,
1695
- suffixGraphemeCount: state.suffixGraphemeCount,
1696
- suffixText: state.suffixText
1667
+ spanId: String(run.runId),
1668
+ name: String(((_a = parseWorkflowName(run.workflowName)) == null ? void 0 : _a.shortName) ?? "?"),
1669
+ kind: 1,
1670
+ // INTERNAL span kind
1671
+ resource: "run",
1672
+ library: WORKFLOW_LIBRARY,
1673
+ status: { code: 0 },
1674
+ traceFlags: 1,
1675
+ attributes,
1676
+ links: [],
1677
+ events,
1678
+ startTime: dateToOtelTime(spanStartTime),
1679
+ endTime: dateToOtelTime(endTime),
1680
+ duration: calculateDuration(spanStartTime, endTime),
1681
+ // Only set activeStartTime if it differs from startTime (i.e., there was a queued period)
1682
+ activeStartTime: activeStartTime && activeStartTime.getTime() > spanStartTime.getTime() ? dateToOtelTime(activeStartTime) : void 0
1697
1683
  };
1698
1684
  }
1699
- function getRangeOffsets(container, range) {
1700
- if (!container.contains(range.startContainer) || !container.contains(range.endContainer)) {
1701
- return null;
1685
+ const isStepEvent = (eventType) => eventType.startsWith("step_");
1686
+ const isTimerEvent = (eventType) => eventType === "wait_created" || eventType === "wait_completed";
1687
+ const isHookLifecycleEvent = (eventType) => eventType === "hook_received" || eventType === "hook_created" || eventType === "hook_disposed";
1688
+ const isRunLevelEvent = (event) => !event.correlationId || !isTimerEvent(event.eventType) && !isHookLifecycleEvent(event.eventType) && !isStepEvent(event.eventType);
1689
+ function filterSpanRawEvents(events, resource, spanId) {
1690
+ if (resource === "run") {
1691
+ return events.filter(isRunLevelEvent);
1702
1692
  }
1703
- const startRange = document.createRange();
1704
- startRange.selectNodeContents(container);
1705
- startRange.setEnd(range.startContainer, range.startOffset);
1706
- const endRange = document.createRange();
1707
- endRange.selectNodeContents(container);
1708
- endRange.setEnd(range.endContainer, range.endOffset);
1693
+ if (!spanId)
1694
+ return [];
1695
+ return events.filter((e) => e.correlationId === spanId);
1696
+ }
1697
+ function pushEvent(map, correlationId, event) {
1698
+ const existing = map.get(correlationId);
1699
+ if (existing) {
1700
+ existing.push(event);
1701
+ return;
1702
+ }
1703
+ map.set(correlationId, [event]);
1704
+ }
1705
+ function groupEventsByCorrelation(events) {
1706
+ const eventsByStepId = /* @__PURE__ */ new Map();
1707
+ const runLevelEvents = [];
1708
+ const timerEvents = /* @__PURE__ */ new Map();
1709
+ const hookEvents = /* @__PURE__ */ new Map();
1710
+ for (const event of events) {
1711
+ const correlationId = event.correlationId;
1712
+ if (!correlationId) {
1713
+ runLevelEvents.push(event);
1714
+ continue;
1715
+ }
1716
+ if (isTimerEvent(event.eventType)) {
1717
+ pushEvent(timerEvents, correlationId, event);
1718
+ continue;
1719
+ }
1720
+ if (isHookLifecycleEvent(event.eventType)) {
1721
+ pushEvent(hookEvents, correlationId, event);
1722
+ continue;
1723
+ }
1724
+ if (isStepEvent(event.eventType)) {
1725
+ pushEvent(eventsByStepId, correlationId, event);
1726
+ continue;
1727
+ }
1728
+ runLevelEvents.push(event);
1729
+ }
1730
+ return { eventsByStepId, runLevelEvents, timerEvents, hookEvents };
1731
+ }
1732
+ function computeLatestKnownTime(events, run) {
1733
+ let latest = new Date(run.createdAt).getTime();
1734
+ for (const event of events) {
1735
+ const t = (getEventTimestamp(event) ?? new Date(event.createdAt)).getTime();
1736
+ if (t > latest)
1737
+ latest = t;
1738
+ }
1739
+ return new Date(latest);
1740
+ }
1741
+ function buildSpans(run, groupedEvents, now, latestKnownTime) {
1742
+ const childMaxEnd = latestKnownTime;
1743
+ const runMaxEnd = run.completedAt ?? now;
1744
+ const stepSpans = Array.from(groupedEvents.eventsByStepId.values()).map((events) => stepToSpan(events, childMaxEnd)).filter((span) => span !== null);
1745
+ const hookSpans = Array.from(groupedEvents.hookEvents.values()).map((events) => hookToSpan(events, childMaxEnd)).filter((span) => span !== null);
1746
+ const waitSpans = Array.from(groupedEvents.timerEvents.values()).map((events) => waitToSpan(events, childMaxEnd, runMaxEnd)).filter((span) => span !== null);
1709
1747
  return {
1710
- end: endRange.toString().length,
1711
- start: startRange.toString().length
1748
+ runSpan: runToSpan(run, groupedEvents.runLevelEvents, now),
1749
+ spans: [...stepSpans, ...hookSpans, ...waitSpans]
1712
1750
  };
1713
1751
  }
1714
- function MiddleTruncate({ value, className, onCopy: onCopyProp, ...props }) {
1715
- const { ref, measureRef, displayText, isTruncated, prefixText, suffixText } = useMiddleTruncate(value);
1716
- const visibleRef = reactExports.useRef(null);
1717
- const handleCopy = reactExports.useCallback((e) => {
1718
- onCopyProp == null ? void 0 : onCopyProp(e);
1719
- if (e.defaultPrevented || !isTruncated)
1720
- return;
1721
- const selection = window.getSelection();
1722
- if (!selection || selection.rangeCount === 0)
1723
- return;
1724
- const selectionText = selection.toString();
1725
- if (!selectionText)
1726
- return;
1727
- const range = selection.getRangeAt(0);
1728
- const visibleEl = visibleRef.current;
1729
- let copyText = null;
1730
- if (e.currentTarget.contains(range.startContainer) && e.currentTarget.contains(range.endContainer) && visibleEl) {
1731
- const offsets = getRangeOffsets(visibleEl, range);
1732
- if (offsets) {
1733
- copyText = getMiddleTruncateCopyText({
1734
- prefixText,
1735
- selectionEnd: offsets.end,
1736
- selectionStart: offsets.start,
1737
- suffixText,
1738
- value
1739
- });
1752
+ function cascadeSpans(runSpan, spans) {
1753
+ const sortedSpans = [
1754
+ runSpan,
1755
+ ...spans.slice().sort((a, b) => {
1756
+ const aStart = otelTimeToMs(a.startTime);
1757
+ const bStart = otelTimeToMs(b.startTime);
1758
+ return aStart - bStart;
1759
+ })
1760
+ ];
1761
+ return sortedSpans.map((span, index) => {
1762
+ const parentSpanId = index === 0 ? void 0 : String(sortedSpans[index - 1].spanId);
1763
+ return {
1764
+ ...span,
1765
+ parentSpanId
1766
+ };
1767
+ });
1768
+ }
1769
+ function buildTrace(run, events, now) {
1770
+ const groupedEvents = groupEventsByCorrelation(events);
1771
+ const latestKnownTime = computeLatestKnownTime(events, run);
1772
+ const { runSpan, spans } = buildSpans(run, groupedEvents, now, latestKnownTime);
1773
+ const sortedCascadingSpans = cascadeSpans(runSpan, spans);
1774
+ const traceStartMs = otelTimeToMs(runSpan.startTime);
1775
+ const knownDurationMs = latestKnownTime.getTime() - traceStartMs;
1776
+ return {
1777
+ traceId: run.runId,
1778
+ rootSpanId: run.runId,
1779
+ spans: sortedCascadingSpans,
1780
+ resources: [
1781
+ {
1782
+ name: "workflow",
1783
+ attributes: {
1784
+ "service.name": WORKFLOW_LIBRARY.name
1785
+ }
1740
1786
  }
1741
- }
1742
- copyText ?? (copyText = getMiddleTruncateCopyTextFromSelectionText({
1743
- prefixText,
1744
- selectionText,
1745
- suffixText,
1746
- value
1747
- }));
1748
- if (copyText === null)
1749
- return;
1750
- e.preventDefault();
1751
- e.clipboardData.setData("text/plain", copyText);
1752
- }, [onCopyProp, isTruncated, prefixText, suffixText, value]);
1753
- 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 })] });
1787
+ ],
1788
+ knownDurationMs: Math.max(0, knownDurationMs)
1789
+ };
1754
1790
  }
1755
1791
  function ConversationView({ messages }) {
1756
1792
  if (messages.length === 0) {
@@ -1897,7 +1933,7 @@ const conversationTabs = [
1897
1933
  ];
1898
1934
  function ConversationWithTabs({ conversation, args }) {
1899
1935
  const [activeTab, setActiveTab] = reactExports.useState("conversation");
1900
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Input", 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) }) }) });
1936
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input", 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) }) }) });
1901
1937
  }
1902
1938
  function EncryptedFieldBlock() {
1903
1939
  return jsxRuntimeExports.jsx(EncryptedDataBlock, {});
@@ -2069,7 +2105,7 @@ const attributeToDisplayFn = {
2069
2105
  environment: (_value) => null,
2070
2106
  executionContext: (_value) => null,
2071
2107
  // Attributes — string-string metadata attached to the run.
2072
- // Rendered as key-value rows in its own collapsible DetailCard;
2108
+ // Rendered as key-value rows in its own collapsible section;
2073
2109
  // if empty/missing, hidden by the hasDisplayContent gate.
2074
2110
  attributes: (value) => {
2075
2111
  if (!hasDisplayContent(value))
@@ -2089,15 +2125,17 @@ const attributeToDisplayFn = {
2089
2125
  metadata: (value) => {
2090
2126
  if (!hasDisplayContent(value))
2091
2127
  return null;
2092
- if (isEncryptedMarker(value))
2093
- return jsxRuntimeExports.jsx(EncryptedDataBlock, {});
2094
- if (isExpiredMarker(value))
2095
- return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
2096
- return JsonBlock(value);
2128
+ if (isEncryptedMarker(value)) {
2129
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Metadata", children: jsxRuntimeExports.jsx(EncryptedDataBlock, {}) });
2130
+ }
2131
+ if (isExpiredMarker(value)) {
2132
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Metadata", children: jsxRuntimeExports.jsx(ExpiredFieldBlock, {}) });
2133
+ }
2134
+ return jsxRuntimeExports.jsx(RunMetadataCard, { metadata: value });
2097
2135
  },
2098
2136
  input: (value, context) => {
2099
2137
  if (isEncryptedMarker(value)) {
2100
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Input", children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2138
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input", children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2101
2139
  }
2102
2140
  if (isExpiredMarker(value))
2103
2141
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
@@ -2109,51 +2147,51 @@ const attributeToDisplayFn = {
2109
2147
  if ((context == null ? void 0 : context.stepName) && isDoStreamStep(context.stepName)) {
2110
2148
  const conversation = extractConversation(args);
2111
2149
  if (conversation && conversation.length > 0) {
2112
- 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) })] });
2150
+ return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(ConversationWithTabs, { conversation, args }), hasClosureVars && jsxRuntimeExports.jsx(Collapsible, { label: "Closure Variables", children: JsonBlock(closureVars) }), hasThisVal && jsxRuntimeExports.jsx(Collapsible, { label: "This Value", children: JsonBlock(thisVal) })] });
2113
2151
  }
2114
2152
  }
2115
2153
  if (!hasArgs && !hasClosureVars && !hasThisVal) {
2116
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Input (no data)", disabled: true });
2154
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input (no data)", disabled: true });
2117
2155
  }
2118
- return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(DetailCard, { summary: "Input", 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: "Context", children: JsonBlock(thisVal) })] });
2156
+ return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(Collapsible, { label: "Input", 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) })] });
2119
2157
  }
2120
2158
  if (!hasDisplayContent(value)) {
2121
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Input (no data)", disabled: true });
2159
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input (no data)", disabled: true });
2122
2160
  }
2123
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Input", children: Array.isArray(value) ? value.map((v, i) => jsxRuntimeExports.jsx("div", { className: "mt-2 first:mt-0", children: JsonBlock(v) }, i)) : JsonBlock(value) });
2161
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Input", children: Array.isArray(value) ? value.map((v, i) => jsxRuntimeExports.jsx("div", { className: "mt-2 first:mt-0", children: JsonBlock(v) }, i)) : JsonBlock(value) });
2124
2162
  },
2125
2163
  output: (value) => {
2126
2164
  if (isEncryptedMarker(value)) {
2127
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Output", children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2165
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Output", children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2128
2166
  }
2129
2167
  if (!hasDisplayContent(value))
2130
2168
  return null;
2131
2169
  if (isExpiredMarker(value))
2132
2170
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
2133
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Output", children: JsonBlock(value) });
2171
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Output", children: JsonBlock(value) });
2134
2172
  },
2135
2173
  error: (value) => {
2136
2174
  if (isEncryptedMarker(value)) {
2137
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Error", defaultOpen: true, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2175
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Error", defaultOpen: true, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2138
2176
  }
2139
2177
  if (isExpiredMarker(value))
2140
2178
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
2141
2179
  if (!hasDisplayContent(value))
2142
2180
  return null;
2143
2181
  if (isStructuredError(value)) {
2144
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Error", defaultOpen: true, children: jsxRuntimeExports.jsx(ErrorStackBlock, { value }) });
2182
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Error", defaultOpen: true, children: jsxRuntimeExports.jsx(ErrorStackBlock, { value }) });
2145
2183
  }
2146
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Error", defaultOpen: true, children: JsonBlock(value) });
2184
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Error", defaultOpen: true, children: JsonBlock(value) });
2147
2185
  },
2148
2186
  eventData: (value) => {
2149
2187
  if (isEncryptedMarker(value)) {
2150
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Event Data", defaultOpen: true, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2188
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Event Data", defaultOpen: true, children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2151
2189
  }
2152
2190
  if (isExpiredMarker(value))
2153
2191
  return jsxRuntimeExports.jsx(ExpiredFieldBlock, {});
2154
2192
  if (!hasDisplayContent(value))
2155
2193
  return null;
2156
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Event Data", defaultOpen: true, children: JsonBlock(value) });
2194
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Event Data", defaultOpen: true, children: JsonBlock(value) });
2157
2195
  },
2158
2196
  errorCode: (value) => {
2159
2197
  if (typeof value !== "string" || value.length === 0)
@@ -2173,6 +2211,7 @@ const selfHeaderedAttributes = /* @__PURE__ */ new Set([
2173
2211
  "input",
2174
2212
  "output",
2175
2213
  "error",
2214
+ "metadata",
2176
2215
  "attributes",
2177
2216
  "eventData"
2178
2217
  ]);
@@ -2194,9 +2233,9 @@ const AttributeBlock = ({ attribute, value, isLoading, inline = false, context }
2194
2233
  if (isLoading && isExpandableLoadingTarget && !hasDisplayContent(value)) {
2195
2234
  const label = attribute === "eventData" ? "Event Data" : attribute === "output" ? "Output" : "Input";
2196
2235
  if (decryptCtx == null ? void 0 : decryptCtx.hasEncryptedData) {
2197
- return jsxRuntimeExports.jsx(DetailCard, { summary: label, defaultOpen: attribute === "eventData", children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2236
+ return jsxRuntimeExports.jsx(Collapsible, { label, defaultOpen: attribute === "eventData", children: jsxRuntimeExports.jsx(EncryptedFieldBlock, {}) });
2198
2237
  }
2199
- return jsxRuntimeExports.jsx(DetailCard, { summary: label });
2238
+ return jsxRuntimeExports.jsx(Collapsible, { label });
2200
2239
  }
2201
2240
  const displayFn = attributeToDisplayFn[attribute];
2202
2241
  if (!displayFn) {
@@ -2282,12 +2321,13 @@ const AttributePanel = ({ data, moduleSpecifier, isLoading, error, expiredAt, on
2282
2321
  isDecrypting,
2283
2322
  hasEncryptedData: outerDecryptCtx == null ? void 0 : outerDecryptCtx.hasEncryptedData
2284
2323
  } : outerDecryptCtx;
2285
- 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("div", { className: "flex flex-col overflow-hidden divide-y divide-gray-alpha-400 mb-3", children: [orderedBasicAttributes.map((attribute) => {
2324
+ 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) => {
2286
2325
  var _a;
2287
2326
  const displayValue = (_a = attributeToDisplayFn[attribute]) == null ? void 0 : _a.call(attributeToDisplayFn, displayData[attribute]);
2288
2327
  const isCopyableBasicAttribute = copyableBasicAttributes.has(attribute) && typeof displayValue === "string";
2289
- return jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between py-2", children: [jsxRuntimeExports.jsx("span", { className: "text-label-14 text-gray-900", children: getAttributeDisplayName(attribute) }), isCopyableBasicAttribute ? jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 max-w-[70%] items-center justify-end gap-1 text-[13px] font-mono text-gray-1000", title: displayValue, children: [jsxRuntimeExports.jsx(MiddleTruncate, { value: displayValue, className: "text-right", style: { gridTemplateColumns: "minmax(0, 1fr)" } }), jsxRuntimeExports.jsx(CopyButton, { copyText: displayValue, ariaLabel: `Copy ${getAttributeDisplayName(attribute)}`, className: "shrink-0 -mr-1" })] }) : jsxRuntimeExports.jsx("span", { className: "text-right text-label-13 font-mono", children: displayValue })] }, attribute);
2290
- }), 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, {}) : resolvedAttributes.length > 0 ? jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: resolvedAttributes.map((attribute) => jsxRuntimeExports.jsx(AttributeBlock, { isLoading, attribute, value: displayData[attribute], context: displayContext }, attribute)) }) : null] }) }) }) });
2328
+ const label = getAttributeDisplayName(attribute);
2329
+ return jsxRuntimeExports.jsx(DetailMonoKeyValueRow, { label, value: displayValue, copyText: isCopyableBasicAttribute ? displayValue : void 0 }, attribute);
2330
+ }), 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] }) }) }) });
2291
2331
  };
2292
2332
  const ERROR_EVENT_TYPES = /* @__PURE__ */ new Set(["step_failed", "step_retrying"]);
2293
2333
  const DATA_EVENT_TYPES = /* @__PURE__ */ new Set([
@@ -2369,17 +2409,17 @@ function EventItem({ event, onLoadEventData, encryptionKey, showSeparateEventOcc
2369
2409
  second: "numeric"
2370
2410
  });
2371
2411
  const displayPayload = isLoading ? loadedData : mergedDisplay;
2372
- return jsxRuntimeExports.jsxs(DetailCard, { variant: "card", summaryClassName: "px-3 py-2", summary: 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 })] }), onToggle: canHaveData ? (open) => {
2412
+ return jsxRuntimeExports.jsxs(CollapsibleRoot, { variant: "card", onOpenChange: canHaveData ? (open) => {
2373
2413
  if (open)
2374
2414
  handleExpand();
2375
- } : void 0, 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 }) })] });
2415
+ } : 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 }) })] })] });
2376
2416
  }
2377
2417
  function hasOnlyExpiredFields(data, eventType) {
2378
2418
  if (data === null || typeof data !== "object" || Array.isArray(data)) {
2379
2419
  return false;
2380
2420
  }
2381
2421
  const record = data;
2382
- const refKeys = EVENT_DATA_REF_FIELDS[eventType] ?? [];
2422
+ const refKeys = getEventDataRefFields(eventType);
2383
2423
  const presentKeys = refKeys.filter((k) => k in record);
2384
2424
  return presentKeys.length > 0 && presentKeys.every((k) => isExpiredMarker(record[k]));
2385
2425
  }
@@ -2412,9 +2452,9 @@ function EventsList({ events, isLoading = false, error, onLoadEventData, onStrea
2412
2452
  const sortedEvents = reactExports.useMemo(() => [...events].sort((a, b) => getEffectiveEventDate(a).getTime() - getEffectiveEventDate(b).getTime()), [events]);
2413
2453
  const hasEvents = sortedEvents.length > 0 && !error;
2414
2454
  if (!hasEvents && !isLoading) {
2415
- return jsxRuntimeExports.jsx(DetailCard, { summary: "Events", disabled: true });
2455
+ return jsxRuntimeExports.jsx(Collapsible, { label: "Events", disabled: true });
2416
2456
  }
2417
- return jsxRuntimeExports.jsx(RunClickContext.Provider, { value: onRunClick, children: jsxRuntimeExports.jsx(StreamClickContext.Provider, { value: onStreamClick, children: jsxRuntimeExports.jsx(DetailCard, { summary: "Events", contentClassName: "mb-0", defaultOpen: true, 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)) }) }) }) });
2457
+ 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)) }) })] }) }) });
2418
2458
  }
2419
2459
  const hasField = (value, key) => key in value;
2420
2460
  const RESOURCES_WITH_FETCHED_DETAIL = /* @__PURE__ */ new Set(["run", "step", "sleep"]);
@@ -2781,6 +2821,257 @@ const kbdVariants = cva("inline-flex items-center justify-center rounded px-1 te
2781
2821
  function Kbd({ children, className, variant, size, ...props }) {
2782
2822
  return jsxRuntimeExports.jsx("kbd", { className: cn(kbdVariants({ variant, size, className })), ...props, children });
2783
2823
  }
2824
+ const PANEL_MIN_WIDTH = 280;
2825
+ const PANEL_HARD_MAX_WIDTH = 1300;
2826
+ const PANEL_DEFAULT_WIDTH = 360;
2827
+ const MAIN_MIN_WIDTH = 400;
2828
+ const PANEL_WIDTH_STORAGE_KEY = "workflow-trace-detail-panel-width";
2829
+ function clamp(value, min, max) {
2830
+ return Math.min(Math.max(value, min), max);
2831
+ }
2832
+ function computeMaxPanelWidth(availableWidth) {
2833
+ if (availableWidth <= 0)
2834
+ return PANEL_HARD_MAX_WIDTH;
2835
+ return Math.min(Math.max(availableWidth - MAIN_MIN_WIDTH, 0), PANEL_HARD_MAX_WIDTH);
2836
+ }
2837
+ function clampPanelWidth(width, availableWidth) {
2838
+ return clamp(width, PANEL_MIN_WIDTH, computeMaxPanelWidth(availableWidth));
2839
+ }
2840
+ function readStoredPanelWidth() {
2841
+ if (typeof window === "undefined")
2842
+ return PANEL_DEFAULT_WIDTH;
2843
+ try {
2844
+ const raw = window.localStorage.getItem(PANEL_WIDTH_STORAGE_KEY);
2845
+ const value = raw ? Number(raw) : Number.NaN;
2846
+ if (!Number.isFinite(value))
2847
+ return PANEL_DEFAULT_WIDTH;
2848
+ return clamp(value, PANEL_MIN_WIDTH, PANEL_HARD_MAX_WIDTH);
2849
+ } catch {
2850
+ return PANEL_DEFAULT_WIDTH;
2851
+ }
2852
+ }
2853
+ function writeStoredPanelWidth(width) {
2854
+ if (typeof window === "undefined")
2855
+ return;
2856
+ try {
2857
+ window.localStorage.setItem(PANEL_WIDTH_STORAGE_KEY, String(Math.round(width)));
2858
+ } catch {
2859
+ }
2860
+ }
2861
+ function DraggableBorder({ element, position, onWidthChange, onReset, "aria-label": ariaLabel, "aria-controls": ariaControls, "aria-valuemin": ariaValueMin, "aria-valuemax": ariaValueMax, "aria-valuenow": ariaValueNow }) {
2862
+ const [isDragging, setIsDragging] = reactExports.useState(false);
2863
+ const pointerIdRef = reactExports.useRef(null);
2864
+ const startXRef = reactExports.useRef(0);
2865
+ const startWidthRef = reactExports.useRef(0);
2866
+ const pendingWidthRef = reactExports.useRef(0);
2867
+ const rafRef = reactExports.useRef(null);
2868
+ const movedRef = reactExports.useRef(false);
2869
+ const lastDragEndAtRef = reactExports.useRef(0);
2870
+ reactExports.useEffect(() => {
2871
+ return () => {
2872
+ if (rafRef.current)
2873
+ cancelAnimationFrame(rafRef.current);
2874
+ };
2875
+ }, []);
2876
+ reactExports.useEffect(() => {
2877
+ if (!isDragging)
2878
+ return;
2879
+ const previous = document.body.style.userSelect;
2880
+ document.body.style.userSelect = "none";
2881
+ return () => {
2882
+ document.body.style.userSelect = previous;
2883
+ };
2884
+ }, [isDragging]);
2885
+ const stopDrag = () => {
2886
+ pointerIdRef.current = null;
2887
+ setIsDragging(false);
2888
+ if (rafRef.current) {
2889
+ cancelAnimationFrame(rafRef.current);
2890
+ rafRef.current = null;
2891
+ if (movedRef.current)
2892
+ onWidthChange(pendingWidthRef.current);
2893
+ }
2894
+ if (movedRef.current) {
2895
+ movedRef.current = false;
2896
+ lastDragEndAtRef.current = performance.now();
2897
+ }
2898
+ };
2899
+ const handlePointerDown = (e) => {
2900
+ if (pointerIdRef.current !== null)
2901
+ return;
2902
+ const el = element.current;
2903
+ if (!el)
2904
+ return;
2905
+ if (e.pointerType === "mouse" && e.button !== 0)
2906
+ return;
2907
+ e.currentTarget.setPointerCapture(e.pointerId);
2908
+ pointerIdRef.current = e.pointerId;
2909
+ startXRef.current = e.clientX;
2910
+ startWidthRef.current = el.offsetWidth;
2911
+ movedRef.current = false;
2912
+ setIsDragging(true);
2913
+ };
2914
+ const handlePointerMove = (e) => {
2915
+ if (pointerIdRef.current !== e.pointerId)
2916
+ return;
2917
+ if (Math.abs(e.clientX - startXRef.current) > 4) {
2918
+ movedRef.current = true;
2919
+ }
2920
+ const delta = position === "left" ? startXRef.current - e.clientX : e.clientX - startXRef.current;
2921
+ pendingWidthRef.current = startWidthRef.current + delta;
2922
+ if (!rafRef.current) {
2923
+ rafRef.current = requestAnimationFrame(() => {
2924
+ rafRef.current = null;
2925
+ onWidthChange(pendingWidthRef.current);
2926
+ });
2927
+ }
2928
+ };
2929
+ const handlePointerEnd = (e) => {
2930
+ if (pointerIdRef.current !== e.pointerId)
2931
+ return;
2932
+ stopDrag();
2933
+ };
2934
+ const handleLostPointerCapture = (e) => {
2935
+ if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current) {
2936
+ return;
2937
+ }
2938
+ stopDrag();
2939
+ };
2940
+ const handleDoubleClick = () => {
2941
+ if (movedRef.current || performance.now() - lastDragEndAtRef.current < 500) {
2942
+ return;
2943
+ }
2944
+ onReset == null ? void 0 : onReset();
2945
+ };
2946
+ const handleKeyDown = (e) => {
2947
+ const el = element.current;
2948
+ if (!el)
2949
+ return;
2950
+ const step = e.shiftKey ? 64 : 16;
2951
+ const grow = position === "left" ? 1 : -1;
2952
+ let next = null;
2953
+ if (e.key === "ArrowLeft") {
2954
+ next = el.offsetWidth + grow * step;
2955
+ } else if (e.key === "ArrowRight") {
2956
+ next = el.offsetWidth - grow * step;
2957
+ } else if (e.key === "Home" && ariaValueMin !== void 0) {
2958
+ next = ariaValueMin;
2959
+ } else if (e.key === "End" && ariaValueMax !== void 0) {
2960
+ next = ariaValueMax;
2961
+ }
2962
+ if (next === null)
2963
+ return;
2964
+ e.preventDefault();
2965
+ onWidthChange(next);
2966
+ };
2967
+ 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") }) });
2968
+ }
2969
+ function useElementWidth(ref, enabled = true) {
2970
+ const [width, setWidth] = reactExports.useState(0);
2971
+ reactExports.useLayoutEffect(() => {
2972
+ if (!enabled)
2973
+ return;
2974
+ const el = ref.current;
2975
+ if (!el)
2976
+ return;
2977
+ setWidth(Math.round(el.getBoundingClientRect().width));
2978
+ const observer = new ResizeObserver(([entry]) => {
2979
+ setWidth(Math.round((entry == null ? void 0 : entry.contentRect.width) ?? 0));
2980
+ });
2981
+ observer.observe(el);
2982
+ return () => observer.disconnect();
2983
+ }, [ref, enabled]);
2984
+ return width;
2985
+ }
2986
+ const DETAIL_PANEL_ID = "trace-detail-panel";
2987
+ function useSelectedSpanInfo() {
2988
+ const { activeSpan } = useActiveSpan();
2989
+ const sidebar = useSidebarData();
2990
+ return reactExports.useMemo(() => {
2991
+ var _a, _b;
2992
+ if (!activeSpan)
2993
+ return null;
2994
+ const resource = (_a = activeSpan.attributes) == null ? void 0 : _a.resource;
2995
+ const rawEvents = filterSpanRawEvents(sidebar.events, resource, activeSpan.spanId);
2996
+ return {
2997
+ data: (_b = activeSpan.attributes) == null ? void 0 : _b.data,
2998
+ resource,
2999
+ spanId: activeSpan.spanId,
3000
+ rawEvents
3001
+ };
3002
+ }, [activeSpan, sidebar]);
3003
+ }
3004
+ function TraceDetailPanel({ containerRef, onNavigateToSpan, onClose }) {
3005
+ const { activeSpan, activeSpanId, spans } = useActiveSpan();
3006
+ const sidebar = useSidebarData();
3007
+ const selectedSpan = useSelectedSpanInfo();
3008
+ const isOpen = Boolean(activeSpan);
3009
+ const [storedWidth, setStoredWidth] = reactExports.useState(() => readStoredPanelWidth());
3010
+ const containerWidth = useElementWidth(containerRef, isOpen);
3011
+ const asideRef = reactExports.useRef(null);
3012
+ const handleResize = reactExports.useCallback((next) => {
3013
+ const clamped = clampPanelWidth(next, containerWidth);
3014
+ setStoredWidth(clamped);
3015
+ writeStoredPanelWidth(clamped);
3016
+ }, [containerWidth]);
3017
+ const panelWidth = clampPanelWidth(storedWidth, containerWidth);
3018
+ const panelMaxWidth = Math.max(PANEL_MIN_WIDTH, computeMaxPanelWidth(containerWidth));
3019
+ const { prevSpanId, nextSpanId } = reactExports.useMemo(() => {
3020
+ var _a, _b;
3021
+ if (!activeSpanId)
3022
+ return { prevSpanId: null, nextSpanId: null };
3023
+ const i = spans.findIndex((s) => s.spanId === activeSpanId);
3024
+ if (i === -1)
3025
+ return { prevSpanId: null, nextSpanId: null };
3026
+ return {
3027
+ prevSpanId: ((_a = spans[i - 1]) == null ? void 0 : _a.spanId) ?? null,
3028
+ nextSpanId: ((_b = spans[i + 1]) == null ? void 0 : _b.spanId) ?? null
3029
+ };
3030
+ }, [activeSpanId, spans]);
3031
+ const handleSelectPrev = reactExports.useCallback(() => {
3032
+ if (prevSpanId)
3033
+ onNavigateToSpan(prevSpanId);
3034
+ }, [prevSpanId, onNavigateToSpan]);
3035
+ const handleSelectNext = reactExports.useCallback(() => {
3036
+ if (nextSpanId)
3037
+ onNavigateToSpan(nextSpanId);
3038
+ }, [nextSpanId, onNavigateToSpan]);
3039
+ reactExports.useEffect(() => {
3040
+ if (!isOpen)
3041
+ return;
3042
+ const onKeyDown = (e) => {
3043
+ if (e.key !== "j" && e.key !== "k" && e.key !== "ArrowDown" && e.key !== "ArrowUp") {
3044
+ return;
3045
+ }
3046
+ const target = e.target;
3047
+ if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || (target == null ? void 0 : target.isContentEditable)) {
3048
+ return;
3049
+ }
3050
+ const targetId = e.key === "k" || e.key === "ArrowUp" ? prevSpanId : nextSpanId;
3051
+ if (targetId) {
3052
+ e.preventDefault();
3053
+ onNavigateToSpan(targetId);
3054
+ }
3055
+ };
3056
+ window.addEventListener("keydown", onKeyDown);
3057
+ return () => window.removeEventListener("keydown", onKeyDown);
3058
+ }, [isOpen, prevSpanId, nextSpanId, onNavigateToSpan]);
3059
+ const selectedSpanName = reactExports.useMemo(() => {
3060
+ var _a, _b;
3061
+ if (!(selectedSpan == null ? void 0 : selectedSpan.data))
3062
+ return "Details";
3063
+ const data = selectedSpan.data;
3064
+ if (selectedSpan.resource === "hook") {
3065
+ return data.token ?? data.hookId;
3066
+ }
3067
+ const stepName = data.stepName;
3068
+ const workflowName = data.workflowName;
3069
+ 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";
3070
+ }, [selectedSpan == null ? void 0 : selectedSpan.data, selectedSpan == null ? void 0 : selectedSpan.resource]);
3071
+ if (!activeSpan)
3072
+ return null;
3073
+ 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 }) }) })] });
3074
+ }
2784
3075
  const WorkflowIcon = () => {
2785
3076
  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" }) });
2786
3077
  };
@@ -2827,22 +3118,20 @@ const EventList = ({ spans, activeSpanId, searchResult, onSelectSpan }) => {
2827
3118
  paddingBottom: (spans.length - end) * ROW_HEIGHT_PX
2828
3119
  }, children: spans.slice(start, end).map((span) => jsxRuntimeExports.jsx(EventRow, { span, isSelected: span.spanId === activeSpanId, isDimmed: isSpanDimmedBySearch(span.spanId, searchResult), onSelectSpan }, span.spanId)) });
2829
3120
  };
2830
- const GUTTER_PX = 1;
2831
- const MIN_PX = 50;
2832
- const DEFAULT_START_PX = 340;
2833
- function SplitPane({ children, className, defaultStartWidth = DEFAULT_START_PX, startHeader, endHeader, scrollContainerRef }) {
3121
+ function SplitPane({ children, className, startHeader, endHeader, scrollContainerRef }) {
2834
3122
  const parts = reactExports.Children.toArray(children);
2835
3123
  if (parts.length !== 2) {
2836
3124
  throw new Error("SplitPane expects exactly two children");
2837
3125
  }
2838
3126
  const [start, end] = parts;
2839
- const [startPx, setStartPx] = reactExports.useState(defaultStartWidth);
3127
+ const [startPx, setStartPx] = reactExports.useState(DEFAULT_START_PX);
2840
3128
  const [isDragging, setIsDragging] = reactExports.useState(false);
2841
3129
  const containerRef = reactExports.useRef(null);
2842
3130
  const gutterRef = reactExports.useRef(null);
2843
3131
  const rafRef = reactExports.useRef(null);
2844
- const pendingPx = reactExports.useRef(defaultStartWidth);
3132
+ const pendingPx = reactExports.useRef(DEFAULT_START_PX);
2845
3133
  const pointerIdRef = reactExports.useRef(null);
3134
+ const containerWidth = useElementWidth(containerRef);
2846
3135
  reactExports.useEffect(() => {
2847
3136
  return () => {
2848
3137
  if (rafRef.current)
@@ -2916,13 +3205,10 @@ function SplitPane({ children, className, defaultStartWidth = DEFAULT_START_PX,
2916
3205
  rafRef.current = null;
2917
3206
  }
2918
3207
  };
2919
- const colTemplate = `${startPx}px ${GUTTER_PX}px minmax(${MIN_PX}px, 1fr)`;
2920
- const hasHeaders = startHeader != null || endHeader != null;
3208
+ const effectiveStartPx = containerWidth > 0 ? Math.max(MIN_PX, Math.min(containerWidth - MIN_PX - GUTTER_PX, startPx)) : startPx;
3209
+ const colTemplate = paneColTemplate(effectiveStartPx);
2921
3210
  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 }) });
2922
- if (hasHeaders) {
2923
- 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: cn("grid flex-1 min-h-0 overflow-x-hidden overflow-y-auto", isDragging && "select-none"), style: { gridTemplateColumns: colTemplate }, children: [start, gutter, end] })] });
2924
- }
2925
- return jsxRuntimeExports.jsxs("div", { ref: setContainerRef, 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] });
3211
+ 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: cn("grid flex-1 min-h-0 overflow-x-hidden overflow-y-auto", isDragging && "select-none"), style: { gridTemplateColumns: colTemplate }, children: [start, gutter, end] })] });
2926
3212
  }
2927
3213
  const hint = "_hint_1dmsq_3";
2928
3214
  const styles = {
@@ -3029,33 +3315,15 @@ function useAnimatedViewport(initial) {
3029
3315
  reactExports.useEffect(() => cancel, [cancel]);
3030
3316
  return { viewport, setViewport, animateTo };
3031
3317
  }
3032
- function useSelectedSpanInfo() {
3033
- const { activeSpan } = useActiveSpan();
3034
- const sidebar = useSidebarData();
3035
- return reactExports.useMemo(() => {
3036
- var _a, _b;
3037
- if (!activeSpan)
3038
- return null;
3039
- const resource = (_a = activeSpan.attributes) == null ? void 0 : _a.resource;
3040
- const rawEvents = filterSpanRawEvents(sidebar.events, resource, activeSpan.spanId);
3041
- return {
3042
- data: (_b = activeSpan.attributes) == null ? void 0 : _b.data,
3043
- resource,
3044
- spanId: activeSpan.spanId,
3045
- rawEvents
3046
- };
3047
- }, [activeSpan, sidebar]);
3048
- }
3049
3318
  function NewTraceViewer$1({ trace, onLoadMore, hasMore, isLoadingMore }) {
3050
3319
  return jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 300, children: jsxRuntimeExports.jsx(ActiveSpanProvider, { spans: trace.spans, children: jsxRuntimeExports.jsx(NewTraceViewerContent, { trace, onLoadMore, hasMore, isLoadingMore }) }) });
3051
3320
  }
3052
3321
  function NewTraceViewerContent({ trace, onLoadMore, hasMore, isLoadingMore }) {
3053
- const { activeSpan, activeSpanId, setActiveSpan, clearActiveSpan } = useActiveSpan();
3054
- const sidebar = useSidebarData();
3055
- const selectedSpan = useSelectedSpanInfo();
3322
+ const { activeSpanId, setActiveSpan, clearActiveSpan } = useActiveSpan();
3056
3323
  const reducedMotion = useReducedMotion();
3057
3324
  const [searchQuery, setSearchQuery] = reactExports.useState("");
3058
3325
  const deferredSearchQuery = reactExports.useDeferredValue(searchQuery);
3326
+ const paneRootRef = reactExports.useRef(null);
3059
3327
  const searchResult = reactExports.useMemo(() => searchSpans(trace.spans, deferredSearchQuery), [trace.spans, deferredSearchQuery]);
3060
3328
  const root = reactExports.useMemo(() => computeRootBounds(trace.spans), [trace.spans]);
3061
3329
  const scrollContainerRef = reactExports.useRef(null);
@@ -3136,6 +3404,8 @@ function NewTraceViewerContent({ trace, onLoadMore, hasMore, isLoadingMore }) {
3136
3404
  }, [setViewport, root.startTime, root.duration]);
3137
3405
  const zoomIn = reactExports.useCallback(() => zoomBy(ZOOM_FACTOR), [zoomBy]);
3138
3406
  const zoomOut = reactExports.useCallback(() => zoomBy(1 / ZOOM_FACTOR), [zoomBy]);
3407
+ const isAtMinZoom = viewDuration >= root.duration;
3408
+ const isAtMaxZoom = viewDuration <= MIN_VIEWPORT_MS;
3139
3409
  const focusViewportOnSpan = reactExports.useCallback((spanId) => {
3140
3410
  const span = trace.spans.find((s) => s.spanId === spanId);
3141
3411
  if (!span)
@@ -3218,24 +3488,11 @@ function NewTraceViewerContent({ trace, onLoadMore, hasMore, isLoadingMore }) {
3218
3488
  }, [setActiveSpan, scrollSpanIntoView, cancelPendingZoom, focusViewportOnSpan]);
3219
3489
  const [altHeld, setAltHeld] = reactExports.useState(false);
3220
3490
  reactExports.useEffect(() => {
3221
- const handleSidebarNavKey = (e) => {
3222
- const target = e.target;
3223
- if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || (target == null ? void 0 : target.isContentEditable)) {
3224
- return;
3225
- }
3226
- const targetId = e.key === "k" ? prevSpanIdRef.current : nextSpanIdRef.current;
3227
- if (targetId) {
3228
- e.preventDefault();
3229
- navigateToSpanRef.current(targetId);
3230
- }
3231
- };
3232
3491
  const onKeyDown = (e) => {
3233
3492
  if (e.key === "Escape") {
3234
3493
  handleClearActiveSpan();
3235
3494
  } else if (e.key === "Alt") {
3236
3495
  setAltHeld(true);
3237
- } else if (e.key === "j" || e.key === "k") {
3238
- handleSidebarNavKey(e);
3239
3496
  }
3240
3497
  };
3241
3498
  const onKeyUp = (e) => {
@@ -3341,51 +3598,13 @@ function NewTraceViewerContent({ trace, onLoadMore, hasMore, isLoadingMore }) {
3341
3598
  el.addEventListener("wheel", onWheel, { passive: false });
3342
3599
  return () => el.removeEventListener("wheel", onWheel);
3343
3600
  }, [root.startTime, root.duration, setViewport]);
3344
- const selectedSpanName = reactExports.useMemo(() => {
3345
- var _a, _b;
3346
- if (!(selectedSpan == null ? void 0 : selectedSpan.data))
3347
- return "Details";
3348
- const data = selectedSpan.data;
3349
- if (selectedSpan.resource === "hook") {
3350
- return data.token ?? data.hookId;
3351
- }
3352
- const stepName = data.stepName;
3353
- const workflowName = data.workflowName;
3354
- 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";
3355
- }, [selectedSpan == null ? void 0 : selectedSpan.data, selectedSpan == null ? void 0 : selectedSpan.resource]);
3356
- const { prevSpanId, nextSpanId } = reactExports.useMemo(() => {
3357
- var _a, _b;
3358
- if (!activeSpanId)
3359
- return { prevSpanId: null, nextSpanId: null };
3360
- const i = trace.spans.findIndex((s) => s.spanId === activeSpanId);
3361
- if (i === -1)
3362
- return { prevSpanId: null, nextSpanId: null };
3363
- return {
3364
- prevSpanId: ((_a = trace.spans[i - 1]) == null ? void 0 : _a.spanId) ?? null,
3365
- nextSpanId: ((_b = trace.spans[i + 1]) == null ? void 0 : _b.spanId) ?? null
3366
- };
3367
- }, [activeSpanId, trace.spans]);
3368
- const handleSelectPrevSpan = reactExports.useCallback(() => {
3369
- if (prevSpanId)
3370
- navigateToSpan(prevSpanId);
3371
- }, [prevSpanId, navigateToSpan]);
3372
- const handleSelectNextSpan = reactExports.useCallback(() => {
3373
- if (nextSpanId)
3374
- navigateToSpan(nextSpanId);
3375
- }, [nextSpanId, navigateToSpan]);
3376
- const prevSpanIdRef = reactExports.useRef(prevSpanId);
3377
- const nextSpanIdRef = reactExports.useRef(nextSpanId);
3378
- const navigateToSpanRef = reactExports.useRef(navigateToSpan);
3379
- prevSpanIdRef.current = prevSpanId;
3380
- nextSpanIdRef.current = nextSpanId;
3381
- navigateToSpanRef.current = navigateToSpan;
3382
- return jsxRuntimeExports.jsxs("div", { "data-pane": "pane-root", "data-has-detail": activeSpan ? "" : void 0, className: "relative grid w-full h-full max-h-full grid-cols-[minmax(100px,1fr)] data-[has-detail]:grid-cols-[minmax(100px,1fr)_clamp(280px,360px,100%)]", children: [jsxRuntimeExports.jsxs("div", { id: "trace-parent", className: "grid grid-rows-[1fr] h-full min-h-0 overflow-hidden relative bg-background-100", children: [jsxRuntimeExports.jsxs(SplitPane, { 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) => {
3601
+ 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-[1fr] h-full min-h-0 overflow-hidden relative bg-background-100", children: [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) => {
3383
3602
  if (e.key === "Escape" && searchQuery) {
3384
3603
  e.preventDefault();
3385
3604
  e.stopPropagation();
3386
3605
  setSearchQuery("");
3387
3606
  }
3388
- }, 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.jsx("div", { ref: timelineRef, className: "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.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, "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, "aria-label": "Zoom in", children: jsxRuntimeExports.jsx(ZoomIn, { className: "w-4 h-4" }) })] })] }), activeSpan ? 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.jsxs("div", { className: "flex items-center justify-between gap-2 shrink-0 px-4 pt-3 pb-3", 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: handleSelectPrevSpan, 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: handleSelectNextSpan, 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: handleClearActiveSpan, 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 }) }) })] }) : null, jsxRuntimeExports.jsx(TraceShortcutHelper, { hasMultipleSpans: trace.spans.length > 1, reducedMotion })] });
3607
+ }, 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.jsx("div", { ref: timelineRef, className: "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.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 }), jsxRuntimeExports.jsx(TraceShortcutHelper, { hasMultipleSpans: trace.spans.length > 1, reducedMotion })] });
3389
3608
  }
3390
3609
  const NewTraceViewer = ({ run, events, sidebarData, onLoadMore, hasMore, isLoadingMore, loading = false }) => {
3391
3610
  const trace = reactExports.useMemo(() => {
@@ -3430,7 +3649,7 @@ async function fetchResourceWithCorrelationId(env, resource, resourceId, options
3430
3649
  async function fetchSpanDetailResource(env, selection, options = {}) {
3431
3650
  const { resource, resourceId, runId } = selection;
3432
3651
  const { encryptionKey } = options;
3433
- const hydrate = async (value) => encryptionKey ? hydrateResourceIOWithKey(value, encryptionKey) : hydrateResourceIO(value);
3652
+ const hydrate = async (value) => hydrateResourceIOAsync(value, encryptionKey);
3434
3653
  if (resource === "hook") {
3435
3654
  const result = await unwrapOrThrow(fetchHook(env, resourceId, "all"));
3436
3655
  return hydrate(result);
@@ -3445,11 +3664,8 @@ async function fetchSpanDetailResource(env, selection, options = {}) {
3445
3664
  const result = await unwrapOrThrow(
3446
3665
  fetchEvents(env, runId, { sortOrder: "asc", limit: 1e3, withData: true })
3447
3666
  );
3448
- const allEvents = result.data.map(
3449
- hydrateResourceIO
3450
- );
3451
3667
  const waitEvents = await Promise.all(
3452
- allEvents.filter((e) => e.correlationId === resourceId).map(hydrate)
3668
+ result.data.filter((e) => e.correlationId === resourceId).map(hydrate)
3453
3669
  );
3454
3670
  const data2 = waitEventsToWaitEntity(waitEvents);
3455
3671
  if (data2 === null) {
@@ -4895,14 +5111,8 @@ function useEventsListData(env, runId, options = {}) {
4895
5111
  const encryptionKeyRef = reactExports.useRef(encryptionKey);
4896
5112
  encryptionKeyRef.current = encryptionKey;
4897
5113
  const hydrateEvents = reactExports.useCallback(async (rawEvents) => {
4898
- const hydrated = rawEvents.map(hydrateResourceIO);
4899
5114
  const key = encryptionKeyRef.current;
4900
- if (key) {
4901
- return Promise.all(
4902
- hydrated.map((ev) => hydrateResourceIOWithKey(ev, key))
4903
- );
4904
- }
4905
- return hydrated;
5115
+ return Promise.all(rawEvents.map((ev) => hydrateResourceIOAsync(ev, key)));
4906
5116
  }, []);
4907
5117
  const fetchInitial = reactExports.useCallback(async () => {
4908
5118
  if (isFetchingRef.current) return;
@@ -4940,7 +5150,7 @@ function useEventsListData(env, runId, options = {}) {
4940
5150
  reactExports.useEffect(() => {
4941
5151
  if (!encryptionKey || events.length === 0) return;
4942
5152
  let cancelled = false;
4943
- Promise.all(events.map((ev) => hydrateResourceIOWithKey(ev, encryptionKey))).then((decrypted) => {
5153
+ Promise.all(events.map((ev) => hydrateResourceIOAsync(ev, encryptionKey))).then((decrypted) => {
4944
5154
  if (!cancelled) setEvents(decrypted);
4945
5155
  }).catch(() => {
4946
5156
  });
@@ -5343,7 +5553,6 @@ function LiveStatus({ hasError, errorMessage }) {
5343
5553
  });
5344
5554
  function RunDetailView({
5345
5555
  runId,
5346
- // TODO: This should open the right sidebar within the trace viewer
5347
5556
  selectedId: _selectedId
5348
5557
  }) {
5349
5558
  var _a;
@@ -5421,7 +5630,10 @@ function RunDetailView({
5421
5630
  if (error2) {
5422
5631
  throw error2;
5423
5632
  }
5424
- const fullEvent = encryptionKeyRef.current ? await hydrateResourceIOWithKey(result, encryptionKeyRef.current) : hydrateResourceIO(result);
5633
+ const fullEvent = await hydrateResourceIOAsync(
5634
+ result,
5635
+ encryptionKeyRef.current ?? void 0
5636
+ );
5425
5637
  if ("eventData" in fullEvent) {
5426
5638
  return fullEvent.eventData;
5427
5639
  }
@@ -5437,7 +5649,10 @@ function RunDetailView({
5437
5649
  if (error2) {
5438
5650
  throw error2;
5439
5651
  }
5440
- const fullEvent = encryptionKeyRef.current ? await hydrateResourceIOWithKey(result, encryptionKeyRef.current) : hydrateResourceIO(result);
5652
+ const fullEvent = await hydrateResourceIOAsync(
5653
+ result,
5654
+ encryptionKeyRef.current ?? void 0
5655
+ );
5441
5656
  if ("eventData" in fullEvent) {
5442
5657
  return fullEvent.eventData;
5443
5658
  }