@workflow/web-shared 5.0.0-beta.1 → 5.0.0-beta.11

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 (298) hide show
  1. package/README.md +1 -1
  2. package/dist/components/error-boundary.js +1 -1
  3. package/dist/components/event-list-view.d.ts +18 -1
  4. package/dist/components/event-list-view.d.ts.map +1 -1
  5. package/dist/components/event-list-view.js +219 -125
  6. package/dist/components/hook-actions.js +1 -1
  7. package/dist/components/index.d.ts +5 -1
  8. package/dist/components/index.d.ts.map +1 -1
  9. package/dist/components/index.js +6 -2
  10. package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
  11. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
  12. package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
  13. package/dist/components/new-trace-viewer/components/event-list.d.ts +10 -0
  14. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
  15. package/dist/components/new-trace-viewer/components/event-list.js +44 -0
  16. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts +18 -0
  17. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
  18. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +54 -0
  19. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts +20 -0
  20. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
  21. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
  22. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +25 -0
  23. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
  24. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +101 -0
  25. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts +20 -0
  26. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +1 -0
  27. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js +114 -0
  28. package/dist/components/new-trace-viewer/components/split-pane.d.ts +15 -0
  29. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
  30. package/dist/components/new-trace-viewer/components/split-pane.js +105 -0
  31. package/dist/components/new-trace-viewer/components/timeline.d.ts +34 -0
  32. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
  33. package/dist/components/new-trace-viewer/components/timeline.js +221 -0
  34. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts +2 -0
  35. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -0
  36. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.js +19 -0
  37. package/dist/components/new-trace-viewer/components/use-row-window.d.ts +20 -0
  38. package/dist/components/new-trace-viewer/components/use-row-window.d.ts.map +1 -0
  39. package/dist/components/new-trace-viewer/components/use-row-window.js +69 -0
  40. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  41. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  42. package/dist/components/new-trace-viewer/context.js +47 -0
  43. package/dist/components/new-trace-viewer/detail-panel.d.ts +10 -0
  44. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -0
  45. package/dist/components/new-trace-viewer/detail-panel.js +13 -0
  46. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  47. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  48. package/dist/components/new-trace-viewer/icons.js +15 -0
  49. package/dist/components/new-trace-viewer/search.d.ts +19 -0
  50. package/dist/components/new-trace-viewer/search.d.ts.map +1 -0
  51. package/dist/components/new-trace-viewer/search.js +134 -0
  52. package/dist/components/new-trace-viewer/trace-viewer.d.ts +11 -0
  53. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  54. package/dist/components/new-trace-viewer/trace-viewer.js +400 -0
  55. package/dist/components/new-trace-viewer/types.d.ts +1 -0
  56. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  57. package/dist/components/new-trace-viewer/types.js +2 -0
  58. package/dist/components/new-trace-viewer/util/constants.d.ts +1 -0
  59. package/dist/components/new-trace-viewer/util/constants.d.ts.map +1 -0
  60. package/dist/components/new-trace-viewer/util/constants.js +2 -0
  61. package/dist/components/new-trace-viewer/utils.d.ts +46 -0
  62. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  63. package/dist/components/new-trace-viewer/utils.js +330 -0
  64. package/dist/components/run-trace-view.d.ts +2 -1
  65. package/dist/components/run-trace-view.d.ts.map +1 -1
  66. package/dist/components/run-trace-view.js +3 -3
  67. package/dist/components/sidebar/attribute-panel.d.ts +7 -1
  68. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  69. package/dist/components/sidebar/attribute-panel.js +131 -70
  70. package/dist/components/sidebar/conversation-view.js +1 -1
  71. package/dist/components/sidebar/copyable-data-block.d.ts +1 -0
  72. package/dist/components/sidebar/copyable-data-block.d.ts.map +1 -1
  73. package/dist/components/sidebar/copyable-data-block.js +16 -15
  74. package/dist/components/sidebar/detail-card.d.ts +5 -6
  75. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  76. package/dist/components/sidebar/detail-card.js +32 -16
  77. package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
  78. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  79. package/dist/components/sidebar/entity-detail-panel.js +20 -37
  80. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  81. package/dist/components/sidebar/events-list.js +21 -15
  82. package/dist/components/sidebar/resolve-hook-modal.js +1 -1
  83. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  84. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  85. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  86. package/dist/components/stream-viewer.d.ts +1 -1
  87. package/dist/components/stream-viewer.d.ts.map +1 -1
  88. package/dist/components/stream-viewer.js +3 -27
  89. package/dist/components/trace-viewer/components/map.js +1 -1
  90. package/dist/components/trace-viewer/components/markers.js +1 -1
  91. package/dist/components/trace-viewer/components/node.js +1 -1
  92. package/dist/components/trace-viewer/components/search-input.js +1 -1
  93. package/dist/components/trace-viewer/components/search.js +1 -1
  94. package/dist/components/trace-viewer/components/span-content.js +1 -1
  95. package/dist/components/trace-viewer/components/span-detail-panel.js +1 -1
  96. package/dist/components/trace-viewer/components/span-segments.js +1 -1
  97. package/dist/components/trace-viewer/components/span-strategies.js +1 -1
  98. package/dist/components/trace-viewer/components/ui.js +1 -1
  99. package/dist/components/trace-viewer/components/zoom-button.js +1 -1
  100. package/dist/components/trace-viewer/components/zoom-icons.js +1 -1
  101. package/dist/components/trace-viewer/context.js +1 -1
  102. package/dist/components/trace-viewer/index.js +1 -1
  103. package/dist/components/trace-viewer/trace-viewer.js +1 -1
  104. package/dist/components/trace-viewer/types.js +1 -1
  105. package/dist/components/trace-viewer/util/constants.js +1 -1
  106. package/dist/components/trace-viewer/util/scrollbar-width.js +1 -1
  107. package/dist/components/trace-viewer/util/timing.d.ts +1 -1
  108. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
  109. package/dist/components/trace-viewer/util/timing.js +6 -9
  110. package/dist/components/trace-viewer/util/tree.js +1 -1
  111. package/dist/components/trace-viewer/util/use-immediate-style.js +1 -1
  112. package/dist/components/trace-viewer/util/use-streaming-spans.js +1 -1
  113. package/dist/components/trace-viewer/util/use-trackpad-zoom.js +1 -1
  114. package/dist/components/trace-viewer/worker.js +1 -1
  115. package/dist/components/trace-viewer-new.d.ts +14 -0
  116. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  117. package/dist/components/trace-viewer-new.js +22 -0
  118. package/dist/components/ui/alert.js +1 -1
  119. package/dist/components/ui/button.d.ts +10 -0
  120. package/dist/components/ui/button.d.ts.map +1 -0
  121. package/dist/components/ui/button.js +46 -0
  122. package/dist/components/ui/card.js +1 -1
  123. package/dist/components/ui/data-inspector.d.ts +39 -1
  124. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  125. package/dist/components/ui/data-inspector.js +207 -22
  126. package/dist/components/ui/decrypt-button.js +1 -1
  127. package/dist/components/ui/error-card.js +1 -1
  128. package/dist/components/ui/error-stack-block.d.ts.map +1 -1
  129. package/dist/components/ui/error-stack-block.js +27 -19
  130. package/dist/components/ui/icon-button.d.ts +19 -0
  131. package/dist/components/ui/icon-button.d.ts.map +1 -0
  132. package/dist/components/ui/icon-button.js +29 -0
  133. package/dist/components/ui/inspector-theme.d.ts +9 -10
  134. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  135. package/dist/components/ui/inspector-theme.js +39 -72
  136. package/dist/components/ui/kbd.d.ts +6 -0
  137. package/dist/components/ui/kbd.d.ts.map +1 -0
  138. package/dist/components/ui/kbd.js +6 -0
  139. package/dist/components/ui/load-more-button.js +1 -1
  140. package/dist/components/ui/menu-dropdown.js +1 -1
  141. package/dist/components/ui/skeleton.js +1 -1
  142. package/dist/components/ui/spinner.js +1 -1
  143. package/dist/components/ui/timestamp-tooltip.js +1 -1
  144. package/dist/components/ui/tooltip.d.ts +10 -0
  145. package/dist/components/ui/tooltip.d.ts.map +1 -0
  146. package/dist/components/ui/tooltip.js +12 -0
  147. package/dist/components/workflow-trace-view.d.ts +3 -1
  148. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  149. package/dist/components/workflow-trace-view.js +10 -5
  150. package/dist/components/workflow-traces/event-colors.js +1 -1
  151. package/dist/components/workflow-traces/trace-colors.js +1 -1
  152. package/dist/components/workflow-traces/trace-span-construction.d.ts +2 -2
  153. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  154. package/dist/components/workflow-traces/trace-span-construction.js +19 -6
  155. package/dist/components/workflow-traces/trace-time-utils.js +1 -1
  156. package/dist/hooks/use-dark-mode.js +1 -1
  157. package/dist/hooks/use-intersection-observer.d.ts +17 -0
  158. package/dist/hooks/use-intersection-observer.d.ts.map +1 -0
  159. package/dist/hooks/use-intersection-observer.js +29 -0
  160. package/dist/hooks/use-load-more-on-scroll.d.ts +21 -0
  161. package/dist/hooks/use-load-more-on-scroll.d.ts.map +1 -0
  162. package/dist/hooks/use-load-more-on-scroll.js +28 -0
  163. package/dist/hooks/use-reduced-motion.d.ts +10 -0
  164. package/dist/hooks/use-reduced-motion.d.ts.map +1 -0
  165. package/dist/hooks/use-reduced-motion.js +29 -0
  166. package/dist/index.d.ts +3 -1
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +3 -2
  169. package/dist/lib/event-analysis.js +1 -1
  170. package/dist/lib/event-materialization.js +1 -1
  171. package/dist/lib/exact-event-search-id.d.ts +25 -0
  172. package/dist/lib/exact-event-search-id.d.ts.map +1 -0
  173. package/dist/lib/exact-event-search-id.js +39 -0
  174. package/dist/lib/hydration.d.ts +14 -1
  175. package/dist/lib/hydration.d.ts.map +1 -1
  176. package/dist/lib/hydration.js +157 -4
  177. package/dist/lib/stream-display.d.ts +18 -0
  178. package/dist/lib/stream-display.d.ts.map +1 -0
  179. package/dist/lib/stream-display.js +30 -0
  180. package/dist/lib/toast.js +1 -1
  181. package/dist/lib/trace-builder.d.ts.map +1 -1
  182. package/dist/lib/trace-builder.js +3 -2
  183. package/dist/lib/utils.d.ts +9 -9
  184. package/dist/lib/utils.d.ts.map +1 -1
  185. package/dist/lib/utils.js +27 -32
  186. package/dist/styles.css +475 -1
  187. package/package.json +6 -4
  188. package/src/components/event-list-view.tsx +421 -262
  189. package/src/components/index.ts +13 -1
  190. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  191. package/src/components/new-trace-viewer/components/event-list.tsx +135 -0
  192. package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
  193. package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
  194. package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
  195. package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
  196. package/src/components/new-trace-viewer/components/split-pane.tsx +198 -0
  197. package/src/components/new-trace-viewer/components/timeline.tsx +560 -0
  198. package/src/components/new-trace-viewer/components/trace-viewer-skeleton.tsx +94 -0
  199. package/src/components/new-trace-viewer/components/use-row-window.ts +93 -0
  200. package/src/components/new-trace-viewer/context.tsx +90 -0
  201. package/src/components/new-trace-viewer/detail-panel.tsx +55 -0
  202. package/src/components/new-trace-viewer/icons.tsx +79 -0
  203. package/src/components/new-trace-viewer/search.ts +178 -0
  204. package/src/components/new-trace-viewer/trace-viewer.tsx +762 -0
  205. package/src/components/new-trace-viewer/types.ts +0 -0
  206. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  207. package/src/components/new-trace-viewer/utils.ts +482 -0
  208. package/src/components/run-trace-view.tsx +3 -0
  209. package/src/components/sidebar/attribute-panel.tsx +240 -196
  210. package/src/components/sidebar/copyable-data-block.tsx +49 -27
  211. package/src/components/sidebar/detail-card.tsx +119 -59
  212. package/src/components/sidebar/entity-detail-panel.tsx +92 -148
  213. package/src/components/sidebar/events-list.tsx +51 -76
  214. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  215. package/src/components/stream-viewer.tsx +18 -40
  216. package/src/components/trace-viewer/util/timing.ts +5 -9
  217. package/src/components/trace-viewer-new.tsx +57 -0
  218. package/src/components/ui/button.tsx +68 -0
  219. package/src/components/ui/data-inspector.tsx +427 -31
  220. package/src/components/ui/error-stack-block.tsx +39 -30
  221. package/src/components/ui/icon-button.tsx +53 -0
  222. package/src/components/ui/inspector-theme.ts +39 -81
  223. package/src/components/ui/kbd.tsx +21 -0
  224. package/src/components/ui/tooltip.tsx +47 -0
  225. package/src/components/workflow-trace-view.tsx +12 -2
  226. package/src/components/workflow-traces/trace-span-construction.ts +24 -6
  227. package/src/hooks/use-intersection-observer.ts +57 -0
  228. package/src/hooks/use-load-more-on-scroll.ts +49 -0
  229. package/src/hooks/use-reduced-motion.ts +34 -0
  230. package/src/index.ts +9 -0
  231. package/src/lib/exact-event-search-id.ts +67 -0
  232. package/src/lib/hydration.ts +169 -1
  233. package/src/lib/stream-display.ts +48 -0
  234. package/src/lib/trace-builder.ts +2 -1
  235. package/src/lib/utils.ts +27 -32
  236. package/src/styles.css +475 -1
  237. package/dist/components/error-boundary.js.map +0 -1
  238. package/dist/components/event-list-view.js.map +0 -1
  239. package/dist/components/hook-actions.js.map +0 -1
  240. package/dist/components/index.js.map +0 -1
  241. package/dist/components/run-trace-view.js.map +0 -1
  242. package/dist/components/sidebar/attribute-panel.js.map +0 -1
  243. package/dist/components/sidebar/conversation-view.js.map +0 -1
  244. package/dist/components/sidebar/copyable-data-block.js.map +0 -1
  245. package/dist/components/sidebar/detail-card.js.map +0 -1
  246. package/dist/components/sidebar/entity-detail-panel.js.map +0 -1
  247. package/dist/components/sidebar/events-list.js.map +0 -1
  248. package/dist/components/sidebar/resolve-hook-modal.js.map +0 -1
  249. package/dist/components/stream-viewer.js.map +0 -1
  250. package/dist/components/trace-viewer/components/map.js.map +0 -1
  251. package/dist/components/trace-viewer/components/markers.js.map +0 -1
  252. package/dist/components/trace-viewer/components/node.js.map +0 -1
  253. package/dist/components/trace-viewer/components/search-input.js.map +0 -1
  254. package/dist/components/trace-viewer/components/search.js.map +0 -1
  255. package/dist/components/trace-viewer/components/span-content.js.map +0 -1
  256. package/dist/components/trace-viewer/components/span-detail-panel.js.map +0 -1
  257. package/dist/components/trace-viewer/components/span-segments.js.map +0 -1
  258. package/dist/components/trace-viewer/components/span-strategies.js.map +0 -1
  259. package/dist/components/trace-viewer/components/ui.js.map +0 -1
  260. package/dist/components/trace-viewer/components/zoom-button.js.map +0 -1
  261. package/dist/components/trace-viewer/components/zoom-icons.js.map +0 -1
  262. package/dist/components/trace-viewer/context.js.map +0 -1
  263. package/dist/components/trace-viewer/index.js.map +0 -1
  264. package/dist/components/trace-viewer/trace-viewer.js.map +0 -1
  265. package/dist/components/trace-viewer/types.js.map +0 -1
  266. package/dist/components/trace-viewer/util/constants.js.map +0 -1
  267. package/dist/components/trace-viewer/util/scrollbar-width.js.map +0 -1
  268. package/dist/components/trace-viewer/util/timing.js.map +0 -1
  269. package/dist/components/trace-viewer/util/tree.js.map +0 -1
  270. package/dist/components/trace-viewer/util/use-immediate-style.js.map +0 -1
  271. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +0 -1
  272. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
  273. package/dist/components/trace-viewer/worker.js.map +0 -1
  274. package/dist/components/ui/alert.js.map +0 -1
  275. package/dist/components/ui/card.js.map +0 -1
  276. package/dist/components/ui/data-inspector.js.map +0 -1
  277. package/dist/components/ui/decrypt-button.js.map +0 -1
  278. package/dist/components/ui/error-card.js.map +0 -1
  279. package/dist/components/ui/error-stack-block.js.map +0 -1
  280. package/dist/components/ui/inspector-theme.js.map +0 -1
  281. package/dist/components/ui/load-more-button.js.map +0 -1
  282. package/dist/components/ui/menu-dropdown.js.map +0 -1
  283. package/dist/components/ui/skeleton.js.map +0 -1
  284. package/dist/components/ui/spinner.js.map +0 -1
  285. package/dist/components/ui/timestamp-tooltip.js.map +0 -1
  286. package/dist/components/workflow-trace-view.js.map +0 -1
  287. package/dist/components/workflow-traces/event-colors.js.map +0 -1
  288. package/dist/components/workflow-traces/trace-colors.js.map +0 -1
  289. package/dist/components/workflow-traces/trace-span-construction.js.map +0 -1
  290. package/dist/components/workflow-traces/trace-time-utils.js.map +0 -1
  291. package/dist/hooks/use-dark-mode.js.map +0 -1
  292. package/dist/index.js.map +0 -1
  293. package/dist/lib/event-analysis.js.map +0 -1
  294. package/dist/lib/event-materialization.js.map +0 -1
  295. package/dist/lib/hydration.js.map +0 -1
  296. package/dist/lib/toast.js.map +0 -1
  297. package/dist/lib/trace-builder.js.map +0 -1
  298. package/dist/lib/utils.js.map +0 -1
@@ -3,13 +3,16 @@
3
3
  import { parseStepName, parseWorkflowName } from '@workflow/utils/parse-name';
4
4
  import type { Event, Hook, Step, WorkflowRun } from '@workflow/world';
5
5
  import type { ModelMessage } from 'ai';
6
- import { Lock } from 'lucide-react';
7
6
  import type { KeyboardEvent, ReactNode } from 'react';
8
- import { useCallback, useMemo, useState } from 'react';
7
+ import { useCallback, useContext, useMemo, useState } from 'react';
9
8
  import { isEncryptedMarker, isExpiredMarker } from '../../lib/hydration';
10
9
  import { useToast } from '../../lib/toast';
11
10
  import { extractConversation, isDoStreamStep } from '../../lib/utils';
12
- import { StreamClickContext } from '../ui/data-inspector';
11
+ import {
12
+ DecryptClickContext,
13
+ RunClickContext,
14
+ StreamClickContext,
15
+ } from '../ui/data-inspector';
13
16
  import { ErrorCard } from '../ui/error-card';
14
17
  import {
15
18
  ErrorStackBlock,
@@ -17,8 +20,10 @@ import {
17
20
  } from '../ui/error-stack-block';
18
21
  import { Skeleton } from '../ui/skeleton';
19
22
  import { TimestampTooltip } from '../ui/timestamp-tooltip';
23
+ import { CopyButton } from '../new-trace-viewer/components/copy-button';
24
+ import { MiddleTruncate } from '../new-trace-viewer/components/middle-truncate/middle-truncate';
20
25
  import { ConversationView } from './conversation-view';
21
- import { CopyableDataBlock } from './copyable-data-block';
26
+ import { CopyableDataBlock, EncryptedDataBlock } from './copyable-data-block';
22
27
  import { DetailCard } from './detail-card';
23
28
 
24
29
  /**
@@ -146,7 +151,7 @@ function ConversationWithTabs({
146
151
  );
147
152
 
148
153
  return (
149
- <DetailCard summary={`Input (${conversation.length} messages)`}>
154
+ <DetailCard summary="Input">
150
155
  <TabbedContainer
151
156
  tabs={conversationTabs}
152
157
  activeTab={activeTab}
@@ -175,24 +180,8 @@ function ConversationWithTabs({
175
180
  * Render a value with the shared DataInspector (ObjectInspector with
176
181
  * custom theming, nodeRenderer for StreamRef/ClassInstanceRef, etc.)
177
182
  */
178
- /**
179
- * Inline display for an encrypted field — no expand, just a flat label
180
- * with the lucide Lock icon matching the title bar Decrypt button.
181
- */
182
183
  function EncryptedFieldBlock() {
183
- return (
184
- <div
185
- className="flex items-center gap-1.5 rounded-md border px-3 py-2 text-xs"
186
- style={{
187
- borderColor: 'var(--ds-gray-300)',
188
- backgroundColor: 'var(--ds-gray-100)',
189
- color: 'var(--ds-gray-700)',
190
- }}
191
- >
192
- <Lock className="h-3 w-3" />
193
- <span className="font-medium">Encrypted</span>
194
- </div>
195
- );
184
+ return <EncryptedDataBlock />;
196
185
  }
197
186
 
198
187
  /**
@@ -237,7 +226,9 @@ type AttributeKey =
237
226
  | 'workflowCoreVersion'
238
227
  | 'receivedCount'
239
228
  | 'lastReceivedAt'
240
- | 'disposedAt';
229
+ | 'disposedAt'
230
+ | 'isSystem'
231
+ | 'errorCode';
241
232
 
242
233
  const attributeOrder: AttributeKey[] = [
243
234
  'workflowName',
@@ -286,8 +277,30 @@ const sortByAttributeOrder = (a: string, b: string): number => {
286
277
  * Display names for attributes that should render differently from their key.
287
278
  */
288
279
  const attributeDisplayNames: Partial<Record<AttributeKey, string>> = {
280
+ moduleSpecifier: 'Module',
281
+ workflowName: 'Workflow Name',
282
+ stepName: 'Step Name',
283
+ stepId: 'Step ID',
284
+ hookId: 'Hook ID',
285
+ attempt: 'Attempts',
286
+ eventId: 'Event ID',
287
+ runId: 'Run ID',
288
+ token: 'Token',
289
+ eventType: 'Event Type',
290
+ correlationId: 'Correlation ID',
291
+ deploymentId: 'Deployment ID',
292
+ specVersion: 'Spec Version',
289
293
  workflowCoreVersion: '@workflow/core version',
290
- receivedCount: 'times resolved',
294
+ createdAt: 'Created',
295
+ startedAt: 'Started',
296
+ updatedAt: 'Updated',
297
+ completedAt: 'Completed',
298
+ expiredAt: 'Expired',
299
+ retryAfter: 'Retry After',
300
+ resumeAt: 'Resume',
301
+ lastReceivedAt: 'Last Received',
302
+ disposedAt: 'Disposed',
303
+ receivedCount: 'Times Resolved',
291
304
  };
292
305
 
293
306
  /**
@@ -374,21 +387,21 @@ const attributeToDisplayFn: Record<
374
387
  (value: unknown, context?: DisplayContext) => null | string | ReactNode
375
388
  > = {
376
389
  // Names that need pretty-printing
377
- workflowName: (value: unknown) =>
378
- parseWorkflowName(String(value))?.shortName ?? '?',
390
+ workflowName: (_value: unknown) => null,
379
391
  moduleSpecifier: (value: unknown) => getModuleSpecifierFromName(value),
380
- stepName: (value: unknown) => parseStepName(String(value))?.shortName ?? '?',
392
+ stepName: (_value: unknown) => null,
381
393
  // IDs
382
- runId: (value: unknown) => String(value),
394
+ runId: (_value: unknown) => null,
383
395
  stepId: (value: unknown) => String(value),
384
396
  hookId: (value: unknown) => String(value),
385
397
  eventId: (value: unknown) => String(value),
386
398
  // Run/step details
387
- status: (value: unknown) => String(value),
399
+ status: (_value: unknown) => null,
388
400
  attempt: (value: unknown) => String(value),
389
401
  // Hook details
390
402
  token: (value: unknown) => String(value),
391
403
  isWebhook: (value: unknown) => String(value),
404
+ isSystem: (value: unknown) => String(value),
392
405
  receivedCount: (value: unknown) => String(value),
393
406
  lastReceivedAt: localMillisecondTimeOrNull,
394
407
  disposedAt: localMillisecondTimeOrNull,
@@ -404,23 +417,36 @@ const attributeToDisplayFn: Record<
404
417
  projectId: (_value: unknown) => null,
405
418
  environment: (_value: unknown) => null,
406
419
  executionContext: (_value: unknown) => null,
420
+ // Attributes MVP — string-string metadata attached to the run.
421
+ // Rendered as a JSON block; if empty/missing, hidden by the
422
+ // hasDisplayContent gate above.
423
+ attributes: (value: unknown) => {
424
+ if (!hasDisplayContent(value)) return null;
425
+ return JsonBlock(value);
426
+ },
407
427
  // Dates — wrapped with TimestampTooltip showing UTC/local + relative time
408
428
  createdAt: timestampWithTooltipOrNull,
409
429
  startedAt: timestampWithTooltipOrNull,
410
- updatedAt: timestampWithTooltipOrNull,
430
+ updatedAt: (_value: unknown) => null,
411
431
  completedAt: timestampWithTooltipOrNull,
412
- expiredAt: timestampWithTooltipOrNull,
432
+ expiredAt: (_value: unknown) => null,
413
433
  retryAfter: timestampWithTooltipOrNull,
414
434
  resumeAt: timestampWithTooltipOrNull,
415
435
  // Resolved attributes, won't actually use this function
416
436
  metadata: (value: unknown) => {
417
437
  if (!hasDisplayContent(value)) return null;
418
- if (isEncryptedMarker(value)) return <EncryptedFieldBlock />;
438
+ if (isEncryptedMarker(value)) return <EncryptedDataBlock />;
419
439
  if (isExpiredMarker(value)) return <ExpiredFieldBlock />;
420
440
  return JsonBlock(value);
421
441
  },
422
442
  input: (value: unknown, context?: DisplayContext) => {
423
- if (isEncryptedMarker(value)) return <EncryptedFieldBlock />;
443
+ if (isEncryptedMarker(value)) {
444
+ return (
445
+ <DetailCard summary="Input">
446
+ <EncryptedFieldBlock />
447
+ </DetailCard>
448
+ );
449
+ }
424
450
  if (isExpiredMarker(value)) return <ExpiredFieldBlock />;
425
451
  // Check if input has args + closure vars structure
426
452
  if (value && typeof value === 'object' && 'args' in value) {
@@ -429,8 +455,6 @@ const attributeToDisplayFn: Record<
429
455
  closureVars?: Record<string, unknown>;
430
456
  thisVal?: unknown;
431
457
  };
432
- const argCount = Array.isArray(args) ? args.length : 0;
433
- const argLabel = argCount === 1 ? 'argument' : 'arguments';
434
458
  const hasClosureVars = hasDisplayContent(closureVars);
435
459
  const hasThisVal = hasDisplayContent(thisVal);
436
460
  const hasArgs = hasDisplayContent(args);
@@ -459,22 +483,12 @@ const attributeToDisplayFn: Record<
459
483
 
460
484
  // Don't render an empty "Input (0 arguments)" card when no input exists.
461
485
  if (!hasArgs && !hasClosureVars && !hasThisVal) {
462
- return (
463
- <DetailCard
464
- summary="Input (no data)"
465
- disabled
466
- summaryClassName="text-base py-2"
467
- />
468
- );
486
+ return <DetailCard summary="Input (no data)" disabled />;
469
487
  }
470
488
 
471
489
  return (
472
490
  <>
473
- <DetailCard
474
- summary={`Input (${argCount} ${argLabel})`}
475
- summaryClassName="text-base py-2"
476
- contentClassName="mt-0"
477
- >
491
+ <DetailCard summary="Input">
478
492
  {Array.isArray(args)
479
493
  ? args.map((v, i) => (
480
494
  <div className="mt-2 first:mt-0" key={i}>
@@ -489,30 +503,18 @@ const attributeToDisplayFn: Record<
489
503
  </DetailCard>
490
504
  )}
491
505
  {hasThisVal && (
492
- <DetailCard summary="this">{JsonBlock(thisVal)}</DetailCard>
506
+ <DetailCard summary="Context">{JsonBlock(thisVal)}</DetailCard>
493
507
  )}
494
508
  </>
495
509
  );
496
510
  }
497
511
 
498
512
  // Fallback: treat as plain array or object
499
- const argCount = Array.isArray(value) ? value.length : 0;
500
- const argLabel = argCount === 1 ? 'argument' : 'arguments';
501
513
  if (!hasDisplayContent(value)) {
502
- return (
503
- <DetailCard
504
- summary="Input (no data)"
505
- disabled
506
- summaryClassName="text-base py-2"
507
- />
508
- );
514
+ return <DetailCard summary="Input (no data)" disabled />;
509
515
  }
510
516
  return (
511
- <DetailCard
512
- summary={`Input (${argCount} ${argLabel})`}
513
- summaryClassName="text-base py-2"
514
- contentClassName="mt-0"
515
- >
517
+ <DetailCard summary="Input">
516
518
  {Array.isArray(value)
517
519
  ? value.map((v, i) => (
518
520
  <div className="mt-2 first:mt-0" key={i}>
@@ -524,21 +526,25 @@ const attributeToDisplayFn: Record<
524
526
  );
525
527
  },
526
528
  output: (value: unknown) => {
529
+ if (isEncryptedMarker(value)) {
530
+ return (
531
+ <DetailCard summary="Output">
532
+ <EncryptedFieldBlock />
533
+ </DetailCard>
534
+ );
535
+ }
527
536
  if (!hasDisplayContent(value)) return null;
528
- if (isEncryptedMarker(value)) return <EncryptedFieldBlock />;
529
537
  if (isExpiredMarker(value)) return <ExpiredFieldBlock />;
530
- return (
531
- <DetailCard
532
- summary="Output"
533
- summaryClassName="text-base py-2"
534
- contentClassName="mt-0"
535
- >
536
- {JsonBlock(value)}
537
- </DetailCard>
538
- );
538
+ return <DetailCard summary="Output">{JsonBlock(value)}</DetailCard>;
539
539
  },
540
540
  error: (value: unknown) => {
541
- if (isEncryptedMarker(value)) return <EncryptedFieldBlock />;
541
+ if (isEncryptedMarker(value)) {
542
+ return (
543
+ <DetailCard summary="Error" defaultOpen>
544
+ <EncryptedFieldBlock />
545
+ </DetailCard>
546
+ );
547
+ }
542
548
  if (isExpiredMarker(value)) return <ExpiredFieldBlock />;
543
549
  if (!hasDisplayContent(value)) return null;
544
550
 
@@ -546,31 +552,37 @@ const attributeToDisplayFn: Record<
546
552
  // pre-formatted text. Otherwise fall back to the raw JSON viewer.
547
553
  if (isStructuredErrorWithStack(value)) {
548
554
  return (
549
- <DetailCard
550
- summary="Error"
551
- summaryClassName="text-base py-2"
552
- contentClassName="mt-0"
553
- >
555
+ <DetailCard summary="Error" defaultOpen>
554
556
  <ErrorStackBlock value={value} />
555
557
  </DetailCard>
556
558
  );
557
559
  }
558
560
 
559
561
  return (
560
- <DetailCard
561
- summary="Error"
562
- summaryClassName="text-base py-2"
563
- contentClassName="mt-0"
564
- >
562
+ <DetailCard summary="Error" defaultOpen>
565
563
  {JsonBlock(value)}
566
564
  </DetailCard>
567
565
  );
568
566
  },
569
567
  eventData: (value: unknown) => {
570
- if (isEncryptedMarker(value)) return <EncryptedFieldBlock />;
568
+ if (isEncryptedMarker(value)) {
569
+ return (
570
+ <DetailCard summary="Event Data" defaultOpen>
571
+ <EncryptedFieldBlock />
572
+ </DetailCard>
573
+ );
574
+ }
571
575
  if (isExpiredMarker(value)) return <ExpiredFieldBlock />;
572
576
  if (!hasDisplayContent(value)) return null;
573
- return <DetailCard summary="Event Data">{JsonBlock(value)}</DetailCard>;
577
+ return (
578
+ <DetailCard summary="Event Data" defaultOpen>
579
+ {JsonBlock(value)}
580
+ </DetailCard>
581
+ );
582
+ },
583
+ errorCode: (value: unknown) => {
584
+ if (typeof value !== 'string' || value.length === 0) return null;
585
+ return String(value);
574
586
  },
575
587
  };
576
588
 
@@ -582,6 +594,15 @@ const resolvableAttributes = [
582
594
  'eventData',
583
595
  ];
584
596
 
597
+ // Attributes whose displayFn renders its own section header via DetailCard,
598
+ // so the outer AttributeBlock should not duplicate the label.
599
+ const selfHeaderedAttributes = new Set([
600
+ 'input',
601
+ 'output',
602
+ 'error',
603
+ 'eventData',
604
+ ]);
605
+
585
606
  const ExpiredDataMessage = () => (
586
607
  <div
587
608
  className="text-copy-12 rounded-md border p-4 my-2"
@@ -595,6 +616,13 @@ const ExpiredDataMessage = () => (
595
616
  </div>
596
617
  );
597
618
 
619
+ const copyableBasicAttributes = new Set<AttributeKey>([
620
+ 'stepId',
621
+ 'hookId',
622
+ 'eventId',
623
+ 'deploymentId',
624
+ ]);
625
+
598
626
  export const AttributeBlock = ({
599
627
  attribute,
600
628
  value,
@@ -608,24 +636,26 @@ export const AttributeBlock = ({
608
636
  inline?: boolean;
609
637
  context?: DisplayContext;
610
638
  }) => {
639
+ const decryptCtx = useContext(DecryptClickContext);
611
640
  const isExpandableLoadingTarget =
612
641
  attribute === 'input' ||
613
642
  attribute === 'output' ||
614
643
  attribute === 'eventData';
615
644
  if (isLoading && isExpandableLoadingTarget && !hasDisplayContent(value)) {
616
- return (
617
- <div
618
- className={`my-2 flex flex-col ${attribute === 'input' || attribute === 'output' ? 'gap-2 my-3.5' : 'gap-0'}`}
619
- >
620
- <span
621
- className={`${attribute === 'input' || attribute === 'output' ? 'text-base' : 'text-xs'} font-medium first-letter:uppercase`}
622
- style={{ color: 'var(--ds-gray-700)' }}
623
- >
624
- {attribute}
625
- </span>
626
- <Skeleton className="h-9 w-full rounded-md" />
627
- </div>
628
- );
645
+ const label =
646
+ attribute === 'eventData'
647
+ ? 'Event Data'
648
+ : attribute === 'output'
649
+ ? 'Output'
650
+ : 'Input';
651
+ if (decryptCtx?.hasEncryptedData) {
652
+ return (
653
+ <DetailCard summary={label} defaultOpen={attribute === 'eventData'}>
654
+ <EncryptedFieldBlock />
655
+ </DetailCard>
656
+ );
657
+ }
658
+ return <DetailCard summary={label} />;
629
659
  }
630
660
 
631
661
  const displayFn =
@@ -654,6 +684,10 @@ export const AttributeBlock = ({
654
684
  );
655
685
  }
656
686
 
687
+ if (selfHeaderedAttributes.has(attribute)) {
688
+ return <>{displayValue}</>;
689
+ }
690
+
657
691
  return (
658
692
  <div className="relative">
659
693
  {typeof isLoading === 'boolean' && isLoading && (
@@ -664,14 +698,8 @@ export const AttributeBlock = ({
664
698
  />
665
699
  </div>
666
700
  )}
667
- <div
668
- key={attribute}
669
- className={`my-2 flex flex-col ${attribute === 'input' || attribute === 'output' || attribute === 'error' ? 'gap-2 my-3.5' : 'gap-0'}`}
670
- >
671
- <span
672
- className={`${attribute === 'input' || attribute === 'output' || attribute === 'error' ? 'text-base' : 'text-xs'} font-medium first-letter:uppercase`}
673
- style={{ color: 'var(--ds-gray-700)' }}
674
- >
701
+ <div key={attribute} className="my-2 flex flex-col gap-0">
702
+ <span className="text-label-14 text-gray-1000 font-medium first-letter:uppercase">
675
703
  {attribute}
676
704
  </span>
677
705
  <span className="text-xs" style={{ color: 'var(--ds-gray-1000)' }}>
@@ -689,6 +717,9 @@ export const AttributePanel = ({
689
717
  error,
690
718
  expiredAt,
691
719
  onStreamClick,
720
+ onRunClick,
721
+ onDecrypt,
722
+ isDecrypting = false,
692
723
  resource,
693
724
  }: {
694
725
  data: Record<string, unknown>;
@@ -698,6 +729,12 @@ export const AttributePanel = ({
698
729
  expiredAt?: string | Date;
699
730
  /** Callback when a stream reference is clicked */
700
731
  onStreamClick?: (streamId: string) => void;
732
+ /** Callback when a run reference is clicked */
733
+ onRunClick?: (runId: string) => void;
734
+ /** Callback when an encrypted marker is clicked (triggers decryption) */
735
+ onDecrypt?: () => void;
736
+ /** Whether decryption is currently in progress */
737
+ isDecrypting?: boolean;
701
738
  /** Resource type of the selected span — used to show targeted loading skeletons. */
702
739
  resource?: string;
703
740
  }) => {
@@ -731,7 +768,9 @@ export const AttributePanel = ({
731
768
 
732
769
  if (!isLoading) return present;
733
770
 
734
- // During loading, ensure input/output appear so their skeletons render
771
+ if (resource === 'sleep') return present;
772
+
773
+ // During loading, ensure sections appear so their skeletons render
735
774
  // in the correct position (above the events section).
736
775
  const loadingDefaults = ['input', 'output'];
737
776
  for (const key of loadingDefaults) {
@@ -740,7 +779,7 @@ export const AttributePanel = ({
740
779
  }
741
780
  }
742
781
  return present.sort(sortByAttributeOrder);
743
- }, [displayData, isLoading]);
782
+ }, [displayData, isLoading, resource]);
744
783
 
745
784
  // Filter out attributes that return null
746
785
  const visibleBasicAttributes = basicAttributes.filter((attribute) => {
@@ -796,50 +835,46 @@ export const AttributePanel = ({
796
835
  });
797
836
  }, []);
798
837
 
838
+ const outerDecryptCtx = useContext(DecryptClickContext);
839
+ const decryptValue = onDecrypt
840
+ ? {
841
+ onDecrypt,
842
+ isDecrypting,
843
+ hasEncryptedData: outerDecryptCtx?.hasEncryptedData,
844
+ }
845
+ : outerDecryptCtx;
846
+
799
847
  return (
800
- <StreamClickContext.Provider value={onStreamClick}>
801
- <div>
802
- {/* Basic attributes in a vertical layout with border */}
803
- {visibleBasicAttributes.length > 0 && (
804
- <div
805
- className="mb-3 flex flex-col overflow-hidden rounded-lg border"
806
- style={{
807
- borderColor: 'var(--ds-gray-300)',
808
- }}
809
- >
810
- {orderedBasicAttributes.map((attribute, index) => {
811
- const displayValue = attributeToDisplayFn[
812
- attribute as keyof typeof attributeToDisplayFn
813
- ]?.(displayData[attribute as keyof typeof displayData]);
814
- const isModuleSpecifier = attribute === 'moduleSpecifier';
815
- const moduleSpecifierValue =
816
- typeof displayValue === 'string'
817
- ? displayValue
818
- : String(displayValue ?? displayData.moduleSpecifier ?? '');
819
- const shouldCapitalizeLabel = attribute !== 'workflowCoreVersion';
820
- const showResumeAtSkeleton =
821
- isLoading && resource === 'sleep' && !displayData.resumeAt;
822
- const showDivider =
823
- index < orderedBasicAttributes.length - 1 ||
824
- showResumeAtSkeleton;
825
-
826
- return (
827
- <div key={attribute} className="py-1">
828
- <div className="flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1">
829
- <span
830
- className={
831
- shouldCapitalizeLabel
832
- ? 'text-[14px] first-letter:uppercase'
833
- : 'text-[14px]'
834
- }
835
- style={{ color: 'var(--ds-gray-700)' }}
836
- >
848
+ <RunClickContext.Provider value={onRunClick}>
849
+ <StreamClickContext.Provider value={onStreamClick}>
850
+ <DecryptClickContext.Provider value={decryptValue}>
851
+ {visibleBasicAttributes.length > 0 && (
852
+ <div className="flex flex-col overflow-hidden divide-y divide-gray-alpha-400 mb-3">
853
+ {orderedBasicAttributes.map((attribute) => {
854
+ const displayValue = attributeToDisplayFn[
855
+ attribute as keyof typeof attributeToDisplayFn
856
+ ]?.(displayData[attribute as keyof typeof displayData]);
857
+ const isModuleSpecifier = attribute === 'moduleSpecifier';
858
+ const isCopyableBasicAttribute =
859
+ copyableBasicAttributes.has(attribute as AttributeKey) &&
860
+ typeof displayValue === 'string';
861
+ const moduleSpecifierValue =
862
+ typeof displayValue === 'string'
863
+ ? displayValue
864
+ : String(displayValue ?? displayData.moduleSpecifier ?? '');
865
+
866
+ return (
867
+ <div
868
+ className="flex items-center justify-between py-2"
869
+ key={attribute}
870
+ >
871
+ <span className="text-label-14 text-gray-900">
837
872
  {getAttributeDisplayName(attribute)}
838
873
  </span>
839
874
  {isModuleSpecifier ? (
840
875
  <button
841
876
  type="button"
842
- className="min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono"
877
+ className="min-w-0 max-w-[70%] truncate text-right text-label-13 font-mono"
843
878
  style={{
844
879
  color: 'var(--ds-gray-1000)',
845
880
  background: 'transparent',
@@ -853,61 +888,70 @@ export const AttributePanel = ({
853
888
  >
854
889
  {moduleSpecifierValue}
855
890
  </button>
856
- ) : (
857
- <span
858
- className="min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono"
859
- style={{ color: 'var(--ds-gray-1000)' }}
891
+ ) : isCopyableBasicAttribute ? (
892
+ <div
893
+ className="flex min-w-0 max-w-[70%] items-center justify-end gap-1 text-right text-[13px] font-mono"
894
+ style={{
895
+ color: 'var(--ds-gray-1000)',
896
+ }}
897
+ title={displayValue}
860
898
  >
899
+ <MiddleTruncate
900
+ value={displayValue}
901
+ className="flex-1"
902
+ />
903
+ <CopyButton
904
+ copyText={displayValue}
905
+ ariaLabel={`Copy ${getAttributeDisplayName(attribute)}`}
906
+ className="shrink-0 -mr-1"
907
+ />
908
+ </div>
909
+ ) : (
910
+ <span className="text-right text-label-13 font-mono">
861
911
  {displayValue}
862
912
  </span>
863
913
  )}
864
914
  </div>
865
- {showDivider ? (
866
- <div
867
- className="mx-2.5 border-b"
868
- style={{ borderColor: 'var(--ds-gray-300)' }}
869
- />
870
- ) : null}
871
- </div>
872
- );
873
- })}
874
- {isLoading && resource === 'sleep' && !displayData.resumeAt && (
875
- <div className="py-1">
876
- <div className="flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1">
877
- <span
878
- className="text-[14px] first-letter:uppercase"
879
- style={{ color: 'var(--ds-gray-700)' }}
880
- >
881
- resumeAt
882
- </span>
883
- <Skeleton className="h-4 w-[55%]" />
915
+ );
916
+ })}
917
+ {isLoading && resource === 'sleep' && !displayData.resumeAt && (
918
+ <div className="py-1">
919
+ <div className="flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1">
920
+ <span
921
+ className="text-[14px] first-letter:uppercase"
922
+ style={{ color: 'var(--ds-gray-700)' }}
923
+ >
924
+ resumeAt
925
+ </span>
926
+ <Skeleton className="h-4 w-[55%]" />
927
+ </div>
884
928
  </div>
885
- </div>
886
- )}
887
- </div>
888
- )}
889
- {error ? (
890
- <ErrorCard
891
- title="Failed to load resource details"
892
- details={error.message}
893
- className="my-4"
894
- />
895
- ) : hasExpired ? (
896
- <ExpiredDataMessage />
897
- ) : (
898
- <>
899
- {resolvedAttributes.map((attribute) => (
900
- <AttributeBlock
901
- isLoading={isLoading}
902
- key={attribute}
903
- attribute={attribute}
904
- value={displayData[attribute as keyof typeof displayData]}
905
- context={displayContext}
906
- />
907
- ))}
908
- </>
909
- )}
910
- </div>
911
- </StreamClickContext.Provider>
929
+ )}
930
+ </div>
931
+ )}
932
+ {error ? (
933
+ <ErrorCard
934
+ title="Failed to load resource details"
935
+ details={error.message}
936
+ className="my-4"
937
+ />
938
+ ) : hasExpired ? (
939
+ <ExpiredDataMessage />
940
+ ) : resolvedAttributes.length > 0 ? (
941
+ <>
942
+ {resolvedAttributes.map((attribute) => (
943
+ <AttributeBlock
944
+ isLoading={isLoading}
945
+ key={attribute}
946
+ attribute={attribute}
947
+ value={displayData[attribute as keyof typeof displayData]}
948
+ context={displayContext}
949
+ />
950
+ ))}
951
+ </>
952
+ ) : null}
953
+ </DecryptClickContext.Provider>
954
+ </StreamClickContext.Provider>
955
+ </RunClickContext.Provider>
912
956
  );
913
957
  };