@workflow/web-shared 5.0.0-beta.3 → 5.0.0-beta.30

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 (402) hide show
  1. package/README.md +2 -10
  2. package/dist/components/error-boundary.js +1 -1
  3. package/dist/components/event-list-view.d.ts +46 -1
  4. package/dist/components/event-list-view.d.ts.map +1 -1
  5. package/dist/components/event-list-view.js +228 -90
  6. package/dist/components/hook-actions.js +1 -1
  7. package/dist/components/index.d.ts +6 -2
  8. package/dist/components/index.d.ts.map +1 -1
  9. package/dist/components/index.js +5 -2
  10. package/dist/components/new-trace-viewer/components/copy-button.js +3 -3
  11. package/dist/components/new-trace-viewer/components/detail-panel-width.d.ts +34 -0
  12. package/dist/components/new-trace-viewer/components/detail-panel-width.d.ts.map +1 -0
  13. package/dist/components/new-trace-viewer/components/detail-panel-width.js +67 -0
  14. package/dist/components/new-trace-viewer/components/detail-panel.d.ts +14 -0
  15. package/dist/components/new-trace-viewer/components/detail-panel.d.ts.map +1 -0
  16. package/dist/components/new-trace-viewer/components/detail-panel.js +122 -0
  17. package/dist/components/new-trace-viewer/components/draggable-border.d.ts +31 -0
  18. package/dist/components/new-trace-viewer/components/draggable-border.d.ts.map +1 -0
  19. package/dist/components/new-trace-viewer/components/draggable-border.js +140 -0
  20. package/dist/components/new-trace-viewer/components/event-list.d.ts +4 -2
  21. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -1
  22. package/dist/components/new-trace-viewer/components/event-list.js +22 -13
  23. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts +18 -0
  24. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
  25. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +54 -0
  26. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts +20 -0
  27. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
  28. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
  29. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +25 -0
  30. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
  31. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +101 -0
  32. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts +20 -0
  33. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +1 -0
  34. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js +114 -0
  35. package/dist/components/new-trace-viewer/components/minimap.d.ts +14 -0
  36. package/dist/components/new-trace-viewer/components/minimap.d.ts.map +1 -0
  37. package/dist/components/new-trace-viewer/components/minimap.js +337 -0
  38. package/dist/components/new-trace-viewer/components/pane-constants.d.ts +14 -0
  39. package/dist/components/new-trace-viewer/components/pane-constants.d.ts.map +1 -0
  40. package/dist/components/new-trace-viewer/components/pane-constants.js +16 -0
  41. package/dist/components/new-trace-viewer/components/span-markers.d.ts +46 -0
  42. package/dist/components/new-trace-viewer/components/span-markers.d.ts.map +1 -0
  43. package/dist/components/new-trace-viewer/components/span-markers.js +85 -0
  44. package/dist/components/new-trace-viewer/components/split-pane.d.ts +3 -7
  45. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -1
  46. package/dist/components/new-trace-viewer/components/split-pane.js +25 -84
  47. package/dist/components/new-trace-viewer/components/timeline.d.ts +19 -15
  48. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -1
  49. package/dist/components/new-trace-viewer/components/timeline.js +229 -111
  50. package/dist/components/new-trace-viewer/components/timeline.module.css +46 -0
  51. package/dist/components/new-trace-viewer/components/trace-shortcut-helper.d.ts +5 -0
  52. package/dist/components/new-trace-viewer/components/trace-shortcut-helper.d.ts.map +1 -0
  53. package/dist/components/new-trace-viewer/components/trace-shortcut-helper.js +55 -0
  54. package/dist/components/new-trace-viewer/components/trace-shortcut-helper.module.css +20 -0
  55. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts +2 -0
  56. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -0
  57. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.js +26 -0
  58. package/dist/components/new-trace-viewer/components/use-element-width.d.ts +8 -0
  59. package/dist/components/new-trace-viewer/components/use-element-width.d.ts.map +1 -0
  60. package/dist/components/new-trace-viewer/components/use-element-width.js +25 -0
  61. package/dist/components/new-trace-viewer/components/use-row-window.d.ts +34 -0
  62. package/dist/components/new-trace-viewer/components/use-row-window.d.ts.map +1 -0
  63. package/dist/components/new-trace-viewer/components/use-row-window.js +107 -0
  64. package/dist/components/new-trace-viewer/context.d.ts +3 -1
  65. package/dist/components/new-trace-viewer/context.d.ts.map +1 -1
  66. package/dist/components/new-trace-viewer/context.js +9 -10
  67. package/dist/components/new-trace-viewer/icons.js +3 -3
  68. package/dist/components/new-trace-viewer/search.d.ts +19 -0
  69. package/dist/components/new-trace-viewer/search.d.ts.map +1 -0
  70. package/dist/components/new-trace-viewer/search.js +134 -0
  71. package/dist/components/new-trace-viewer/trace-viewer.d.ts +6 -3
  72. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -1
  73. package/dist/components/new-trace-viewer/trace-viewer.js +167 -162
  74. package/dist/components/new-trace-viewer/types.d.ts +2 -0
  75. package/dist/components/new-trace-viewer/types.d.ts.map +1 -1
  76. package/dist/components/new-trace-viewer/types.js +2 -2
  77. package/dist/components/new-trace-viewer/utils.d.ts +48 -13
  78. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -1
  79. package/dist/components/new-trace-viewer/utils.js +135 -107
  80. package/dist/components/run-trace-view.d.ts +5 -7
  81. package/dist/components/run-trace-view.d.ts.map +1 -1
  82. package/dist/components/run-trace-view.js +3 -3
  83. package/dist/components/sidebar/attribute-panel.d.ts +4 -1
  84. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  85. package/dist/components/sidebar/attribute-panel.js +133 -113
  86. package/dist/components/sidebar/attributes-block.d.ts +40 -0
  87. package/dist/components/sidebar/attributes-block.d.ts.map +1 -0
  88. package/dist/components/sidebar/attributes-block.js +140 -0
  89. package/dist/components/sidebar/conversation-view.js +1 -1
  90. package/dist/components/sidebar/copyable-data-block.d.ts +2 -0
  91. package/dist/components/sidebar/copyable-data-block.d.ts.map +1 -1
  92. package/dist/components/sidebar/copyable-data-block.js +17 -16
  93. package/dist/components/sidebar/entity-detail-panel.d.ts +11 -11
  94. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  95. package/dist/components/sidebar/entity-detail-panel.js +35 -69
  96. package/dist/components/sidebar/events-list.d.ts +6 -2
  97. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  98. package/dist/components/sidebar/events-list.js +76 -62
  99. package/dist/components/sidebar/resolve-hook-modal.js +1 -1
  100. package/dist/components/sidebar/sidebar-data-context.d.ts +10 -7
  101. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -1
  102. package/dist/components/sidebar/sidebar-data-context.js +1 -1
  103. package/dist/components/sidebar/span-detail-merge.d.ts +31 -0
  104. package/dist/components/sidebar/span-detail-merge.d.ts.map +1 -0
  105. package/dist/components/sidebar/span-detail-merge.js +92 -0
  106. package/dist/components/sidebar/use-selected-span-detail.d.ts +16 -0
  107. package/dist/components/sidebar/use-selected-span-detail.d.ts.map +1 -0
  108. package/dist/components/sidebar/use-selected-span-detail.js +93 -0
  109. package/dist/components/stream-viewer.js +1 -1
  110. package/dist/components/trace-viewer/components/map.d.ts +2 -2
  111. package/dist/components/trace-viewer/components/map.d.ts.map +1 -1
  112. package/dist/components/trace-viewer/components/map.js +1 -1
  113. package/dist/components/trace-viewer/components/markers.js +1 -1
  114. package/dist/components/trace-viewer/components/node.d.ts +7 -7
  115. package/dist/components/trace-viewer/components/node.d.ts.map +1 -1
  116. package/dist/components/trace-viewer/components/node.js +2 -1
  117. package/dist/components/trace-viewer/components/search-input.js +1 -1
  118. package/dist/components/trace-viewer/components/search.js +1 -1
  119. package/dist/components/trace-viewer/components/span-content.js +1 -1
  120. package/dist/components/trace-viewer/components/span-detail-panel.js +1 -1
  121. package/dist/components/trace-viewer/components/span-segments.js +1 -1
  122. package/dist/components/trace-viewer/components/span-strategies.js +1 -1
  123. package/dist/components/trace-viewer/components/ui.js +1 -1
  124. package/dist/components/trace-viewer/components/zoom-button.js +1 -1
  125. package/dist/components/trace-viewer/components/zoom-icons.js +1 -1
  126. package/dist/components/trace-viewer/context.js +1 -1
  127. package/dist/components/trace-viewer/index.js +1 -1
  128. package/dist/components/trace-viewer/trace-viewer.js +1 -1
  129. package/dist/components/trace-viewer/trace-viewer.module.css +4 -4
  130. package/dist/components/trace-viewer/types.js +1 -1
  131. package/dist/components/trace-viewer/util/constants.js +1 -1
  132. package/dist/components/trace-viewer/util/scrollbar-width.js +1 -1
  133. package/dist/components/trace-viewer/util/timing.d.ts +3 -3
  134. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
  135. package/dist/components/trace-viewer/util/timing.js +8 -11
  136. package/dist/components/trace-viewer/util/tree.js +1 -1
  137. package/dist/components/trace-viewer/util/use-immediate-style.js +1 -1
  138. package/dist/components/trace-viewer/util/use-streaming-spans.js +1 -1
  139. package/dist/components/trace-viewer/util/use-trackpad-zoom.js +1 -1
  140. package/dist/components/trace-viewer/worker.js +1 -1
  141. package/dist/components/trace-viewer-new.d.ts +5 -1
  142. package/dist/components/trace-viewer-new.d.ts.map +1 -1
  143. package/dist/components/trace-viewer-new.js +8 -10
  144. package/dist/components/ui/alert.js +2 -2
  145. package/dist/components/ui/button.d.ts +10 -0
  146. package/dist/components/ui/button.d.ts.map +1 -0
  147. package/dist/components/ui/button.js +46 -0
  148. package/dist/components/ui/card.js +2 -2
  149. package/dist/components/ui/collapsible.d.ts +46 -0
  150. package/dist/components/ui/collapsible.d.ts.map +1 -0
  151. package/dist/components/ui/collapsible.js +66 -0
  152. package/dist/components/ui/context-card.d.ts +47 -0
  153. package/dist/components/ui/context-card.d.ts.map +1 -0
  154. package/dist/components/ui/context-card.js +471 -0
  155. package/dist/components/ui/data-inspector.d.ts +7 -6
  156. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  157. package/dist/components/ui/data-inspector.js +232 -99
  158. package/dist/components/ui/data-inspector.styles.d.ts +29 -0
  159. package/dist/components/ui/data-inspector.styles.d.ts.map +1 -0
  160. package/dist/components/ui/data-inspector.styles.js +95 -0
  161. package/dist/components/ui/decrypt-button.js +1 -1
  162. package/dist/components/ui/error-card.js +2 -2
  163. package/dist/components/ui/error-stack-block.d.ts +17 -10
  164. package/dist/components/ui/error-stack-block.d.ts.map +1 -1
  165. package/dist/components/ui/error-stack-block.js +49 -30
  166. package/dist/components/ui/icon-button.d.ts +19 -0
  167. package/dist/components/ui/icon-button.d.ts.map +1 -0
  168. package/dist/components/ui/icon-button.js +29 -0
  169. package/dist/components/ui/kbd.d.ts +10 -0
  170. package/dist/components/ui/kbd.d.ts.map +1 -0
  171. package/dist/components/ui/kbd.js +25 -0
  172. package/dist/components/ui/load-more-button.js +1 -1
  173. package/dist/components/ui/menu-dropdown.js +1 -1
  174. package/dist/components/ui/skeleton.js +2 -2
  175. package/dist/components/ui/spinner.js +1 -1
  176. package/dist/components/ui/timestamp-tooltip.d.ts +34 -1
  177. package/dist/components/ui/timestamp-tooltip.d.ts.map +1 -1
  178. package/dist/components/ui/timestamp-tooltip.js +80 -132
  179. package/dist/components/ui/tooltip.d.ts +10 -0
  180. package/dist/components/ui/tooltip.d.ts.map +1 -0
  181. package/dist/components/ui/tooltip.js +12 -0
  182. package/dist/components/workflow-trace-view.d.ts +7 -8
  183. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  184. package/dist/components/workflow-trace-view.js +11 -16
  185. package/dist/components/workflow-traces/event-colors.d.ts +2 -1
  186. package/dist/components/workflow-traces/event-colors.d.ts.map +1 -1
  187. package/dist/components/workflow-traces/event-colors.js +15 -7
  188. package/dist/components/workflow-traces/trace-colors.d.ts.map +1 -1
  189. package/dist/components/workflow-traces/trace-colors.js +3 -4
  190. package/dist/components/workflow-traces/trace-span-construction.d.ts +7 -4
  191. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  192. package/dist/components/workflow-traces/trace-span-construction.js +87 -33
  193. package/dist/components/workflow-traces/trace-time-utils.js +1 -1
  194. package/dist/hooks/use-dark-mode.js +1 -1
  195. package/dist/hooks/use-intersection-observer.d.ts +17 -0
  196. package/dist/hooks/use-intersection-observer.d.ts.map +1 -0
  197. package/dist/hooks/use-intersection-observer.js +29 -0
  198. package/dist/hooks/use-load-more-on-scroll.d.ts +21 -0
  199. package/dist/hooks/use-load-more-on-scroll.d.ts.map +1 -0
  200. package/dist/hooks/use-load-more-on-scroll.js +28 -0
  201. package/dist/hooks/use-reduced-motion.d.ts +10 -0
  202. package/dist/hooks/use-reduced-motion.d.ts.map +1 -0
  203. package/dist/hooks/use-reduced-motion.js +29 -0
  204. package/dist/index.d.ts +3 -2
  205. package/dist/index.d.ts.map +1 -1
  206. package/dist/index.js +4 -3
  207. package/dist/lib/cn.d.ts +10 -0
  208. package/dist/lib/cn.d.ts.map +1 -0
  209. package/dist/lib/cn.js +83 -0
  210. package/dist/lib/event-analysis.d.ts +1 -1
  211. package/dist/lib/event-analysis.d.ts.map +1 -1
  212. package/dist/lib/event-analysis.js +3 -2
  213. package/dist/lib/event-materialization.d.ts +1 -1
  214. package/dist/lib/event-materialization.d.ts.map +1 -1
  215. package/dist/lib/event-materialization.js +29 -21
  216. package/dist/lib/exact-event-search-id.d.ts +25 -0
  217. package/dist/lib/exact-event-search-id.d.ts.map +1 -0
  218. package/dist/lib/exact-event-search-id.js +39 -0
  219. package/dist/lib/hydration.d.ts +17 -2
  220. package/dist/lib/hydration.d.ts.map +1 -1
  221. package/dist/lib/hydration.js +185 -22
  222. package/dist/lib/stream-display.js +1 -1
  223. package/dist/lib/toast.js +1 -1
  224. package/dist/lib/trace-builder.d.ts +16 -4
  225. package/dist/lib/trace-builder.d.ts.map +1 -1
  226. package/dist/lib/trace-builder.js +34 -15
  227. package/dist/lib/utils.d.ts +23 -9
  228. package/dist/lib/utils.d.ts.map +1 -1
  229. package/dist/lib/utils.js +73 -33
  230. package/dist/lib/zstd-browser-decoder.d.ts +7 -0
  231. package/dist/lib/zstd-browser-decoder.d.ts.map +1 -0
  232. package/dist/lib/zstd-browser-decoder.js +41 -0
  233. package/dist/styles.css +14 -0
  234. package/package.json +10 -7
  235. package/src/components/event-list-view.tsx +375 -116
  236. package/src/components/index.ts +12 -5
  237. package/src/components/new-trace-viewer/components/copy-button.tsx +2 -2
  238. package/src/components/new-trace-viewer/components/detail-panel-width.test.ts +123 -0
  239. package/src/components/new-trace-viewer/components/detail-panel-width.ts +76 -0
  240. package/src/components/new-trace-viewer/components/detail-panel.tsx +273 -0
  241. package/src/components/new-trace-viewer/components/draggable-border.tsx +216 -0
  242. package/src/components/new-trace-viewer/components/event-list.tsx +57 -32
  243. package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
  244. package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
  245. package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
  246. package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
  247. package/src/components/new-trace-viewer/components/minimap.tsx +559 -0
  248. package/src/components/new-trace-viewer/components/pane-constants.ts +19 -0
  249. package/src/components/new-trace-viewer/components/span-markers.tsx +173 -0
  250. package/src/components/new-trace-viewer/components/split-pane.tsx +80 -135
  251. package/src/components/new-trace-viewer/components/timeline.module.css +46 -0
  252. package/src/components/new-trace-viewer/components/timeline.tsx +603 -239
  253. package/src/components/new-trace-viewer/components/trace-shortcut-helper.module.css +20 -0
  254. package/src/components/new-trace-viewer/components/trace-shortcut-helper.tsx +116 -0
  255. package/src/components/new-trace-viewer/components/trace-viewer-skeleton.tsx +114 -0
  256. package/src/components/new-trace-viewer/components/use-element-width.ts +29 -0
  257. package/src/components/new-trace-viewer/components/use-row-window.ts +140 -0
  258. package/src/components/new-trace-viewer/context.tsx +13 -14
  259. package/src/components/new-trace-viewer/icons.tsx +2 -2
  260. package/src/components/new-trace-viewer/search.ts +178 -0
  261. package/src/components/new-trace-viewer/trace-viewer.tsx +336 -293
  262. package/src/components/new-trace-viewer/types.ts +2 -0
  263. package/src/components/new-trace-viewer/utils.test.ts +247 -0
  264. package/src/components/new-trace-viewer/utils.ts +203 -134
  265. package/src/components/run-trace-view.tsx +10 -14
  266. package/src/components/sidebar/attribute-panel.tsx +276 -275
  267. package/src/components/sidebar/attributes-block.tsx +326 -0
  268. package/src/components/sidebar/copyable-data-block.tsx +50 -28
  269. package/src/components/sidebar/entity-detail-panel.tsx +89 -123
  270. package/src/components/sidebar/events-list.tsx +196 -178
  271. package/src/components/sidebar/sidebar-data-context.tsx +11 -11
  272. package/src/components/sidebar/span-detail-merge.ts +134 -0
  273. package/src/components/sidebar/use-selected-span-detail.ts +146 -0
  274. package/src/components/trace-viewer/components/node.tsx +3 -2
  275. package/src/components/trace-viewer/trace-viewer.module.css +4 -4
  276. package/src/components/trace-viewer/util/timing.ts +7 -11
  277. package/src/components/trace-viewer-new.tsx +21 -15
  278. package/src/components/ui/alert.tsx +1 -1
  279. package/src/components/ui/button.tsx +68 -0
  280. package/src/components/ui/card.tsx +1 -1
  281. package/src/components/ui/collapsible.tsx +219 -0
  282. package/src/components/ui/context-card.tsx +784 -0
  283. package/src/components/ui/data-inspector.styles.ts +96 -0
  284. package/src/components/ui/data-inspector.tsx +501 -204
  285. package/src/components/ui/error-card.tsx +1 -1
  286. package/src/components/ui/error-stack-block.tsx +84 -55
  287. package/src/components/ui/icon-button.tsx +53 -0
  288. package/src/components/ui/kbd.tsx +43 -0
  289. package/src/components/ui/skeleton.tsx +1 -1
  290. package/src/components/ui/timestamp-tooltip.tsx +132 -194
  291. package/src/components/ui/tooltip.tsx +47 -0
  292. package/src/components/workflow-trace-view.tsx +31 -36
  293. package/src/components/workflow-traces/event-colors.ts +15 -11
  294. package/src/components/workflow-traces/trace-colors.ts +6 -6
  295. package/src/components/workflow-traces/trace-span-construction.test.ts +85 -0
  296. package/src/components/workflow-traces/trace-span-construction.ts +118 -35
  297. package/src/hooks/use-intersection-observer.ts +57 -0
  298. package/src/hooks/use-load-more-on-scroll.ts +49 -0
  299. package/src/hooks/use-reduced-motion.ts +34 -0
  300. package/src/index.ts +9 -0
  301. package/src/lib/cn.ts +93 -0
  302. package/src/lib/event-analysis.ts +6 -4
  303. package/src/lib/event-materialization.ts +35 -21
  304. package/src/lib/exact-event-search-id.ts +67 -0
  305. package/src/lib/hydration.ts +203 -29
  306. package/src/lib/trace-builder.ts +44 -19
  307. package/src/lib/utils.test.ts +33 -0
  308. package/src/lib/utils.ts +82 -35
  309. package/src/lib/zstd-browser-decoder.ts +42 -0
  310. package/src/styles.css +14 -0
  311. package/dist/components/error-boundary.js.map +0 -1
  312. package/dist/components/event-list-view.js.map +0 -1
  313. package/dist/components/hook-actions.js.map +0 -1
  314. package/dist/components/index.js.map +0 -1
  315. package/dist/components/new-trace-viewer/components/copy-button.js.map +0 -1
  316. package/dist/components/new-trace-viewer/components/event-list.js.map +0 -1
  317. package/dist/components/new-trace-viewer/components/split-pane.js.map +0 -1
  318. package/dist/components/new-trace-viewer/components/timeline.js.map +0 -1
  319. package/dist/components/new-trace-viewer/context.js.map +0 -1
  320. package/dist/components/new-trace-viewer/detail-panel.d.ts +0 -10
  321. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +0 -1
  322. package/dist/components/new-trace-viewer/detail-panel.js +0 -11
  323. package/dist/components/new-trace-viewer/detail-panel.js.map +0 -1
  324. package/dist/components/new-trace-viewer/icons.js.map +0 -1
  325. package/dist/components/new-trace-viewer/trace-viewer.js.map +0 -1
  326. package/dist/components/new-trace-viewer/types.js.map +0 -1
  327. package/dist/components/new-trace-viewer/util/constants.d.ts +0 -1
  328. package/dist/components/new-trace-viewer/util/constants.d.ts.map +0 -1
  329. package/dist/components/new-trace-viewer/util/constants.js +0 -2
  330. package/dist/components/new-trace-viewer/util/constants.js.map +0 -1
  331. package/dist/components/new-trace-viewer/utils.js.map +0 -1
  332. package/dist/components/run-trace-view.js.map +0 -1
  333. package/dist/components/sidebar/attribute-panel.js.map +0 -1
  334. package/dist/components/sidebar/conversation-view.js.map +0 -1
  335. package/dist/components/sidebar/copyable-data-block.js.map +0 -1
  336. package/dist/components/sidebar/detail-card.d.ts +0 -14
  337. package/dist/components/sidebar/detail-card.d.ts.map +0 -1
  338. package/dist/components/sidebar/detail-card.js +0 -20
  339. package/dist/components/sidebar/detail-card.js.map +0 -1
  340. package/dist/components/sidebar/entity-detail-panel.js.map +0 -1
  341. package/dist/components/sidebar/events-list.js.map +0 -1
  342. package/dist/components/sidebar/resolve-hook-modal.js.map +0 -1
  343. package/dist/components/sidebar/sidebar-data-context.js.map +0 -1
  344. package/dist/components/stream-viewer.js.map +0 -1
  345. package/dist/components/trace-viewer/components/map.js.map +0 -1
  346. package/dist/components/trace-viewer/components/markers.js.map +0 -1
  347. package/dist/components/trace-viewer/components/node.js.map +0 -1
  348. package/dist/components/trace-viewer/components/search-input.js.map +0 -1
  349. package/dist/components/trace-viewer/components/search.js.map +0 -1
  350. package/dist/components/trace-viewer/components/span-content.js.map +0 -1
  351. package/dist/components/trace-viewer/components/span-detail-panel.js.map +0 -1
  352. package/dist/components/trace-viewer/components/span-segments.js.map +0 -1
  353. package/dist/components/trace-viewer/components/span-strategies.js.map +0 -1
  354. package/dist/components/trace-viewer/components/ui.js.map +0 -1
  355. package/dist/components/trace-viewer/components/zoom-button.js.map +0 -1
  356. package/dist/components/trace-viewer/components/zoom-icons.js.map +0 -1
  357. package/dist/components/trace-viewer/context.js.map +0 -1
  358. package/dist/components/trace-viewer/index.js.map +0 -1
  359. package/dist/components/trace-viewer/trace-viewer.js.map +0 -1
  360. package/dist/components/trace-viewer/types.js.map +0 -1
  361. package/dist/components/trace-viewer/util/constants.js.map +0 -1
  362. package/dist/components/trace-viewer/util/scrollbar-width.js.map +0 -1
  363. package/dist/components/trace-viewer/util/timing.js.map +0 -1
  364. package/dist/components/trace-viewer/util/tree.js.map +0 -1
  365. package/dist/components/trace-viewer/util/use-immediate-style.js.map +0 -1
  366. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +0 -1
  367. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
  368. package/dist/components/trace-viewer/worker.js.map +0 -1
  369. package/dist/components/trace-viewer-new.js.map +0 -1
  370. package/dist/components/ui/alert.js.map +0 -1
  371. package/dist/components/ui/card.js.map +0 -1
  372. package/dist/components/ui/data-inspector.js.map +0 -1
  373. package/dist/components/ui/decrypt-button.js.map +0 -1
  374. package/dist/components/ui/error-card.js.map +0 -1
  375. package/dist/components/ui/error-stack-block.js.map +0 -1
  376. package/dist/components/ui/inspector-theme.d.ts +0 -95
  377. package/dist/components/ui/inspector-theme.d.ts.map +0 -1
  378. package/dist/components/ui/inspector-theme.js +0 -82
  379. package/dist/components/ui/inspector-theme.js.map +0 -1
  380. package/dist/components/ui/load-more-button.js.map +0 -1
  381. package/dist/components/ui/menu-dropdown.js.map +0 -1
  382. package/dist/components/ui/skeleton.js.map +0 -1
  383. package/dist/components/ui/spinner.js.map +0 -1
  384. package/dist/components/ui/timestamp-tooltip.js.map +0 -1
  385. package/dist/components/workflow-trace-view.js.map +0 -1
  386. package/dist/components/workflow-traces/event-colors.js.map +0 -1
  387. package/dist/components/workflow-traces/trace-colors.js.map +0 -1
  388. package/dist/components/workflow-traces/trace-span-construction.js.map +0 -1
  389. package/dist/components/workflow-traces/trace-time-utils.js.map +0 -1
  390. package/dist/hooks/use-dark-mode.js.map +0 -1
  391. package/dist/index.js.map +0 -1
  392. package/dist/lib/event-analysis.js.map +0 -1
  393. package/dist/lib/event-materialization.js.map +0 -1
  394. package/dist/lib/hydration.js.map +0 -1
  395. package/dist/lib/stream-display.js.map +0 -1
  396. package/dist/lib/toast.js.map +0 -1
  397. package/dist/lib/trace-builder.js.map +0 -1
  398. package/dist/lib/utils.js.map +0 -1
  399. package/src/components/new-trace-viewer/detail-panel.tsx +0 -57
  400. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  401. package/src/components/sidebar/detail-card.tsx +0 -65
  402. package/src/components/ui/inspector-theme.ts +0 -110
@@ -3,16 +3,30 @@
3
3
  import { parseStepName, parseWorkflowName } from '@workflow/utils/parse-name';
4
4
  import type { Event, WorkflowRun } from '@workflow/world';
5
5
  import { Check, ChevronRight, Copy } from 'lucide-react';
6
- import type { MouseEvent as ReactMouseEvent, ReactNode } from 'react';
6
+ import type {
7
+ KeyboardEvent as ReactKeyboardEvent,
8
+ MouseEvent as ReactMouseEvent,
9
+ ReactNode,
10
+ } from 'react';
7
11
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
8
12
  import { Virtuoso, type VirtuosoHandle } from 'react-virtuoso';
13
+ import {
14
+ type ExactIdSearchResult,
15
+ type ExactWorkflowSearchIdKind,
16
+ looksLikeWorkflowIdSearchInput,
17
+ parseExactWorkflowSearchId,
18
+ } from '../lib/exact-event-search-id';
9
19
  import { isEncryptedMarker } from '../lib/hydration';
10
- import { DecryptButton } from './ui/decrypt-button';
20
+ import { useToast } from '../lib/toast';
11
21
  import { formatDuration } from '../lib/utils';
22
+ import { AttrSetEventBlock } from './sidebar/attributes-block';
23
+ import { ContextCardProvider } from './ui/context-card';
12
24
  import { DataInspector, DecryptClickContext } from './ui/data-inspector';
25
+ import { DecryptButton } from './ui/decrypt-button';
13
26
  import {
14
27
  ErrorStackBlock,
15
- isStructuredErrorWithStack,
28
+ isStructuredError,
29
+ type StructuredErrorRecord,
16
30
  } from './ui/error-stack-block';
17
31
  import { LoadMoreButton } from './ui/load-more-button';
18
32
  import { MenuDropdown } from './ui/menu-dropdown';
@@ -55,6 +69,25 @@ function formatEventTime(date: Date): string {
55
69
  );
56
70
  }
57
71
 
72
+ function parseEventDate(value: unknown): Date | null {
73
+ if (value == null) return null;
74
+
75
+ const date = value instanceof Date ? value : new Date(String(value));
76
+ return Number.isNaN(date.getTime()) ? null : date;
77
+ }
78
+
79
+ function getEffectiveEventDate(
80
+ event: Pick<Event, 'createdAt' | 'occurredAt'>
81
+ ): Date {
82
+ return parseEventDate(event.occurredAt) ?? new Date(event.createdAt);
83
+ }
84
+
85
+ function getEffectiveEventTime(
86
+ event: Pick<Event, 'createdAt' | 'occurredAt'>
87
+ ): number {
88
+ return getEffectiveEventDate(event).getTime();
89
+ }
90
+
58
91
  function formatEventType(eventType: Event['eventType']): string {
59
92
  return eventType
60
93
  .split('_')
@@ -84,6 +117,10 @@ function getStatusDotColor(eventType: string): string {
84
117
  if (eventType === 'step_retrying') {
85
118
  return 'var(--ds-amber-700)';
86
119
  }
120
+ // Attribute changes → teal
121
+ if (eventType === 'attr_set') {
122
+ return 'var(--ds-teal-900)';
123
+ }
87
124
  // Completed/succeeded → green
88
125
  if (
89
126
  eventType === 'step_completed' ||
@@ -142,7 +179,7 @@ function buildNameMaps(
142
179
  return { correlationNameMap, workflowName };
143
180
  }
144
181
 
145
- interface DurationInfo {
182
+ export interface DurationInfo {
146
183
  /** Time from created → started (ms) */
147
184
  queued?: number;
148
185
  /** Time from started → completed/failed/cancelled (ms) */
@@ -154,19 +191,30 @@ interface DurationInfo {
154
191
  * created ↔ started (queued) and started ↔ completed/failed/cancelled (ran).
155
192
  * Also computes run-level durations under the key '__run__'.
156
193
  */
157
- function buildDurationMap(events: Event[]): Map<string, DurationInfo> {
194
+ export function buildDurationMap(events: Event[]): Map<string, DurationInfo> {
195
+ // Process events in chronological order so the result doesn't depend on
196
+ // the caller's sort direction. Retried steps emit multiple `step_started`
197
+ // events for the same correlationId; the queued duration must be measured
198
+ // against the first one, not the last.
199
+ const chronological = [...events].sort(
200
+ (a, b) => getEffectiveEventTime(a) - getEffectiveEventTime(b)
201
+ );
202
+
158
203
  const createdTimes = new Map<string, number>();
204
+ const firstStartedTimes = new Map<string, number>();
159
205
  const startedTimes = new Map<string, number>();
160
206
  const durations = new Map<string, DurationInfo>();
161
207
 
162
- for (const event of events) {
163
- const ts = new Date(event.createdAt).getTime();
208
+ for (const event of chronological) {
209
+ const ts = getEffectiveEventTime(event);
164
210
  const key = event.correlationId ?? '__run__';
165
211
  const type: string = event.eventType;
166
212
 
167
213
  // Track created times (first event for each correlation)
168
214
  if (type === 'step_created' || type === 'run_created') {
169
- createdTimes.set(key, ts);
215
+ if (!createdTimes.has(key)) {
216
+ createdTimes.set(key, ts);
217
+ }
170
218
  }
171
219
 
172
220
  // Track started times & compute queued duration
@@ -176,16 +224,21 @@ function buildDurationMap(events: Event[]): Map<string, DurationInfo> {
176
224
  type === 'workflow_started'
177
225
  ) {
178
226
  startedTimes.set(key, ts);
179
- // If no explicit created event was seen, use the started time as created
180
- if (!createdTimes.has(key)) {
181
- createdTimes.set(key, ts);
182
- }
183
- const createdAt = createdTimes.get(key);
184
- const info = durations.get(key) ?? {};
185
- if (createdAt !== undefined) {
186
- info.queued = ts - createdAt;
227
+ // The queued duration is anchored on the first start event only
228
+ // subsequent step_started events come from retries.
229
+ if (!firstStartedTimes.has(key)) {
230
+ firstStartedTimes.set(key, ts);
231
+ // If no explicit created event was seen, use the started time as created
232
+ if (!createdTimes.has(key)) {
233
+ createdTimes.set(key, ts);
234
+ }
235
+ const createdAt = createdTimes.get(key);
236
+ const info = durations.get(key) ?? {};
237
+ if (createdAt !== undefined) {
238
+ info.queued = ts - createdAt;
239
+ }
240
+ durations.set(key, info);
187
241
  }
188
- durations.set(key, info);
189
242
  }
190
243
 
191
244
  // Compute ran duration on terminal events
@@ -230,7 +283,10 @@ function isRunLevel(eventType: string): boolean {
230
283
  eventType === 'run_cancelled' ||
231
284
  eventType === 'workflow_started' ||
232
285
  eventType === 'workflow_completed' ||
233
- eventType === 'workflow_failed'
286
+ eventType === 'workflow_failed' ||
287
+ // attr_set carries a dedup correlationId rather than a child entity ID,
288
+ // so it groups and labels with the run itself.
289
+ eventType === 'attr_set'
234
290
  );
235
291
  }
236
292
 
@@ -492,20 +548,20 @@ function deepParseJson(value: unknown): unknown {
492
548
  }
493
549
 
494
550
  /**
495
- * Extracts a structured error with a stack trace from event data, if present.
551
+ * Extracts a structured error from event data, if present.
496
552
  * Returns the error object to render with ErrorStackBlock, or null if not applicable.
497
553
  */
498
554
  function extractStructuredError(
499
555
  data: unknown,
500
556
  eventType?: string
501
- ): (Record<string, unknown> & { stack: string }) | null {
557
+ ): StructuredErrorRecord | null {
502
558
  if (!eventType || !ERROR_EVENT_TYPES.has(eventType)) return null;
503
559
  if (data == null || typeof data !== 'object') return null;
504
560
  const record = data as Record<string, unknown>;
505
561
  // Check the nested `error` field first (the StructuredError)
506
- if (isStructuredErrorWithStack(record.error)) return record.error;
507
- // Some error formats put the stack at the top level of eventData
508
- if (isStructuredErrorWithStack(record)) return record;
562
+ if (isStructuredError(record.error)) return record.error;
563
+ // Some error formats put the message/stack at the top level of eventData.
564
+ if (isStructuredError(record)) return record;
509
565
  return null;
510
566
  }
511
567
 
@@ -566,6 +622,33 @@ function PayloadBlock({
566
622
  );
567
623
  }
568
624
 
625
+ // Attribute changes — render the changed keys and the writer instead of
626
+ // the raw JSON payload.
627
+ if (eventType === 'attr_set') {
628
+ return <AttrSetEventBlock data={cleaned} />;
629
+ }
630
+
631
+ // Cancellation reason — render the free-text reason as a readable line
632
+ // instead of a raw JSON payload (the only field run_cancelled carries).
633
+ if (eventType === 'run_cancelled') {
634
+ const cancelReason =
635
+ cleaned != null &&
636
+ typeof cleaned === 'object' &&
637
+ typeof (cleaned as Record<string, unknown>).cancelReason === 'string'
638
+ ? ((cleaned as Record<string, unknown>).cancelReason as string)
639
+ : null;
640
+ if (cancelReason) {
641
+ return (
642
+ <div className="p-2 text-xs" style={{ color: 'var(--ds-gray-1000)' }}>
643
+ <span style={{ color: 'var(--ds-gray-900)' }}>Reason: </span>
644
+ <span className="whitespace-pre-wrap break-words">
645
+ {cancelReason}
646
+ </span>
647
+ </div>
648
+ );
649
+ }
650
+ }
651
+
569
652
  return (
570
653
  <div className="relative group/payload">
571
654
  <div
@@ -609,7 +692,11 @@ const SORT_OPTIONS = [
609
692
  { value: 'asc' as const, label: 'Oldest' },
610
693
  ];
611
694
 
612
- function RowsSkeleton() {
695
+ function RowsSkeleton({
696
+ showSeparateEventOccurrenceTimestamps = false,
697
+ }: {
698
+ showSeparateEventOccurrenceTimestamps?: boolean;
699
+ }) {
613
700
  return (
614
701
  <div className="flex-1 overflow-hidden">
615
702
  {Array.from({ length: 16 }, (_, i) => (
@@ -646,7 +733,12 @@ function RowsSkeleton() {
646
733
  <div className="w-5 flex-shrink-0 flex items-center justify-center">
647
734
  <Skeleton className="w-5 h-5" style={{ borderRadius: 4 }} />
648
735
  </div>
649
- {/* Time */}
736
+ {showSeparateEventOccurrenceTimestamps && (
737
+ <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
738
+ <Skeleton className="h-3" style={{ width: '70%' }} />
739
+ </div>
740
+ )}
741
+ {/* Created */}
650
742
  <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
651
743
  <Skeleton className="h-3" style={{ width: '70%' }} />
652
744
  </div>
@@ -705,9 +797,17 @@ interface EventsListProps {
705
797
  isDecrypting?: boolean;
706
798
  /** Run-level hint: the run contains encrypted data (from probe). */
707
799
  hasEncryptedData?: boolean;
800
+ /** Fetch events for an exact correlation or event ID. */
801
+ onExactIdSearch?: (
802
+ id: string,
803
+ kind: ExactWorkflowSearchIdKind,
804
+ signal?: AbortSignal
805
+ ) => Promise<ExactIdSearchResult>;
806
+ /** Show occurredAt separately instead of folding it into the Created timestamp. */
807
+ showSeparateEventOccurrenceTimestamps?: boolean;
708
808
  }
709
809
 
710
- function EventRow({
810
+ export function EventRow({
711
811
  event,
712
812
  index,
713
813
  isFirst,
@@ -727,6 +827,8 @@ function EventRow({
727
827
  onCacheEventData,
728
828
  encryptionKey,
729
829
  onEncryptedDataDetected,
830
+ suppressGroupDimming = false,
831
+ showSeparateEventOccurrenceTimestamps = false,
730
832
  }: {
731
833
  event: Event;
732
834
  index: number;
@@ -747,6 +849,10 @@ function EventRow({
747
849
  onCacheEventData: (eventId: string, data: unknown) => void;
748
850
  encryptionKey?: Uint8Array;
749
851
  onEncryptedDataDetected?: () => void;
852
+ /** Exact-ID search results should not dim unrelated rows. */
853
+ suppressGroupDimming?: boolean;
854
+ /** Show occurredAt separately instead of folding it into the Created timestamp. */
855
+ showSeparateEventOccurrenceTimestamps?: boolean;
750
856
  }) {
751
857
  const [isLoading, setIsLoading] = useState(false);
752
858
  const [loadedEventData, setLoadedEventData] = useState<unknown | null>(
@@ -775,6 +881,10 @@ function EventRow({
775
881
 
776
882
  const statusDotColor = getStatusDotColor(event.eventType);
777
883
  const createdAt = new Date(event.createdAt);
884
+ const occurredAt = parseEventDate(event.occurredAt);
885
+ const displayedCreatedAt = showSeparateEventOccurrenceTimestamps
886
+ ? createdAt
887
+ : getEffectiveEventDate(event);
778
888
  const hasExistingEventData = 'eventData' in event && event.eventData != null;
779
889
  const isRun = isRunLevel(event.eventType);
780
890
  const eventName = isRun
@@ -788,7 +898,7 @@ function EventRow({
788
898
 
789
899
  const hasActive = activeGroupKey !== undefined;
790
900
  const isRelated = rowGroupKey !== undefined && rowGroupKey === activeGroupKey;
791
- const isDimmed = hasActive && !isRelated;
901
+ const isDimmed = hasActive && !isRelated && !suppressGroupDimming;
792
902
  const isPulsing = hasActive && isRelated;
793
903
 
794
904
  // Gutter state derived from selectedGroupRange
@@ -955,13 +1065,28 @@ function EventRow({
955
1065
  />
956
1066
  </div>
957
1067
 
958
- {/* Time */}
1068
+ {showSeparateEventOccurrenceTimestamps && (
1069
+ <div
1070
+ className="tabular-nums min-w-0 px-4"
1071
+ style={{ color: 'var(--ds-gray-900)', flex: '2 1 0%' }}
1072
+ >
1073
+ {occurredAt ? (
1074
+ <TimestampTooltip date={occurredAt}>
1075
+ <span>{formatEventTime(occurredAt)}</span>
1076
+ </TimestampTooltip>
1077
+ ) : (
1078
+ '-'
1079
+ )}
1080
+ </div>
1081
+ )}
1082
+
1083
+ {/* Created */}
959
1084
  <div
960
1085
  className="tabular-nums min-w-0 px-4"
961
1086
  style={{ color: 'var(--ds-gray-900)', flex: '2 1 0%' }}
962
1087
  >
963
- <TimestampTooltip date={createdAt}>
964
- <span>{formatEventTime(createdAt)}</span>
1088
+ <TimestampTooltip date={displayedCreatedAt}>
1089
+ <span>{formatEventTime(displayedCreatedAt)}</span>
965
1090
  </TimestampTooltip>
966
1091
  </div>
967
1092
 
@@ -1125,7 +1250,7 @@ function EventRow({
1125
1250
  // Main component
1126
1251
  // ──────────────────────────────────────────────────────────────────────────
1127
1252
 
1128
- export function EventListView({
1253
+ function EventListViewInner({
1129
1254
  events,
1130
1255
  run,
1131
1256
  onLoadEventData,
@@ -1139,7 +1264,10 @@ export function EventListView({
1139
1264
  onDecrypt,
1140
1265
  isDecrypting = false,
1141
1266
  hasEncryptedData: hasEncryptedDataProp = false,
1267
+ onExactIdSearch,
1268
+ showSeparateEventOccurrenceTimestamps = false,
1142
1269
  }: EventsListProps) {
1270
+ const toast = useToast();
1143
1271
  const [internalSortOrder, setInternalSortOrder] = useState<'asc' | 'desc'>(
1144
1272
  'asc'
1145
1273
  );
@@ -1155,25 +1283,40 @@ export function EventListView({
1155
1283
  [onSortOrderChange]
1156
1284
  );
1157
1285
 
1286
+ const [searchQuery, setSearchQuery] = useState('');
1287
+ const [searchResults, setSearchResults] = useState<Event[] | null>(null);
1288
+ const [searchResultsTruncated, setSearchResultsTruncated] = useState(false);
1289
+ const [searchError, setSearchError] = useState<string | null>(null);
1290
+ const [searchLoading, setSearchLoading] = useState(false);
1291
+ const [searchNotFound, setSearchNotFound] = useState(false);
1292
+ const searchRequestRef = useRef(0);
1293
+ const virtuosoRef = useRef<VirtuosoHandle>(null);
1294
+
1295
+ const parsedSearchId = useMemo(
1296
+ () => parseExactWorkflowSearchId(searchQuery),
1297
+ [searchQuery]
1298
+ );
1299
+ const isExactSearchActive = searchResults !== null;
1300
+
1158
1301
  const sortedEvents = useMemo(() => {
1159
- if (!events || events.length === 0) return [];
1302
+ const sourceEvents = isExactSearchActive ? searchResults : (events ?? []);
1303
+ if (sourceEvents.length === 0) return [];
1160
1304
  const dir = effectiveSortOrder === 'desc' ? -1 : 1;
1161
- return [...events].sort(
1162
- (a, b) =>
1163
- dir *
1164
- (new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
1305
+ return [...sourceEvents].sort(
1306
+ (a, b) => dir * (getEffectiveEventTime(a) - getEffectiveEventTime(b))
1165
1307
  );
1166
- }, [events, effectiveSortOrder]);
1308
+ }, [events, effectiveSortOrder, isExactSearchActive, searchResults]);
1167
1309
 
1168
1310
  // Detect encrypted fields across all loaded events (inline eventData).
1169
1311
  const hasEncryptedInlineData = useMemo(() => {
1170
- if (!events) return false;
1171
- for (const event of events) {
1312
+ const sourceEvents = isExactSearchActive ? searchResults : events;
1313
+ if (!sourceEvents) return false;
1314
+ for (const event of sourceEvents) {
1172
1315
  const ed = (event as Record<string, unknown>).eventData;
1173
1316
  if (hasEncryptedValues(ed)) return true;
1174
1317
  }
1175
1318
  return false;
1176
- }, [events]);
1319
+ }, [events, isExactSearchActive, searchResults]);
1177
1320
 
1178
1321
  // Tracks whether any expanded row's lazy-loaded data contained encrypted markers.
1179
1322
  // Set to true by EventRow via onEncryptedDataDetected; never reset (sticky).
@@ -1187,8 +1330,12 @@ export function EventListView({
1187
1330
  hasEncryptedDataProp || hasEncryptedInlineData || foundEncryptedInLazyData;
1188
1331
 
1189
1332
  const { correlationNameMap, workflowName } = useMemo(
1190
- () => buildNameMaps(events ?? null, run ?? null),
1191
- [events, run]
1333
+ () =>
1334
+ buildNameMaps(
1335
+ isExactSearchActive ? searchResults : (events ?? null),
1336
+ run ?? null
1337
+ ),
1338
+ [events, isExactSearchActive, run, searchResults]
1192
1339
  );
1193
1340
 
1194
1341
  const durationMap = useMemo(
@@ -1278,68 +1425,139 @@ export function EventListView({
1278
1425
  return first >= 0 ? { first, last } : null;
1279
1426
  }, [activeGroupKey, sortedEvents]);
1280
1427
 
1281
- const [searchQuery, setSearchQuery] = useState('');
1282
- const virtuosoRef = useRef<VirtuosoHandle>(null);
1283
-
1284
- const searchIndex = useMemo(() => {
1285
- const entries: {
1286
- fields: string[];
1287
- groupKey?: string;
1288
- eventId: string;
1289
- index: number;
1290
- }[] = [];
1291
- for (let i = 0; i < sortedEvents.length; i++) {
1292
- const ev = sortedEvents[i];
1293
- const isRun = isRunLevel(ev.eventType);
1294
- const name = isRun
1295
- ? (workflowName ?? '')
1296
- : ev.correlationId
1297
- ? (correlationNameMap.get(ev.correlationId) ?? '')
1298
- : '';
1299
- entries.push({
1300
- fields: [
1301
- ev.eventId,
1302
- ev.correlationId ?? '',
1303
- ev.eventType,
1304
- formatEventType(ev.eventType),
1305
- name,
1306
- ].map((f) => f.toLowerCase()),
1307
- groupKey: ev.correlationId ?? (isRun ? '__run__' : undefined),
1308
- eventId: ev.eventId,
1309
- index: i,
1310
- });
1311
- }
1312
- return entries;
1313
- }, [sortedEvents, correlationNameMap, workflowName]);
1314
-
1315
1428
  useEffect(() => {
1316
- const q = searchQuery.trim().toLowerCase();
1317
- if (!q) {
1429
+ const trimmed = searchQuery.trim();
1430
+ if (!trimmed) {
1431
+ searchRequestRef.current += 1;
1432
+ setSearchResults(null);
1433
+ setSearchResultsTruncated(false);
1434
+ setSearchError(null);
1435
+ setSearchLoading(false);
1436
+ setSearchNotFound(false);
1318
1437
  setSelectedGroupKey(undefined);
1319
1438
  return;
1320
1439
  }
1321
- let bestMatch: (typeof searchIndex)[number] | null = null;
1322
- let bestScore = 0;
1323
- for (const entry of searchIndex) {
1324
- for (const field of entry.fields) {
1325
- if (field && field.includes(q)) {
1326
- const score = q.length / field.length;
1327
- if (score > bestScore) {
1328
- bestScore = score;
1329
- bestMatch = entry;
1440
+
1441
+ const parsed = parseExactWorkflowSearchId(trimmed);
1442
+ if (!parsed || !onExactIdSearch) {
1443
+ setSearchResults(null);
1444
+ setSearchLoading(false);
1445
+ setSearchNotFound(false);
1446
+ return;
1447
+ }
1448
+
1449
+ const requestId = ++searchRequestRef.current;
1450
+ setSearchLoading(true);
1451
+ setSearchNotFound(false);
1452
+ setSearchError(null);
1453
+
1454
+ const abortController = new AbortController();
1455
+
1456
+ const timer = setTimeout(() => {
1457
+ void (async () => {
1458
+ try {
1459
+ const results = await onExactIdSearch(
1460
+ parsed.id,
1461
+ parsed.kind,
1462
+ abortController.signal
1463
+ );
1464
+ if (
1465
+ abortController.signal.aborted ||
1466
+ searchRequestRef.current !== requestId
1467
+ ) {
1468
+ return;
1469
+ }
1470
+
1471
+ if (results.status === 'error') {
1472
+ setSearchResults([]);
1473
+ setSearchResultsTruncated(false);
1474
+ setSearchNotFound(false);
1475
+ setSearchError(results.message);
1476
+ setSelectedGroupKey(undefined);
1477
+ return;
1478
+ }
1479
+
1480
+ if (
1481
+ results.status === 'not_found' ||
1482
+ (results.status === 'ok' && results.events.length === 0)
1483
+ ) {
1484
+ setSearchResults([]);
1485
+ setSearchResultsTruncated(false);
1486
+ setSearchNotFound(true);
1487
+ setSearchError(null);
1488
+ setSelectedGroupKey(undefined);
1489
+ return;
1490
+ }
1491
+
1492
+ setSearchResults(results.events);
1493
+ setSearchResultsTruncated(Boolean(results.truncated));
1494
+ setSearchNotFound(false);
1495
+ setSearchError(null);
1496
+ setSelectedGroupKey(
1497
+ parsed.kind === 'event'
1498
+ ? (() => {
1499
+ const first = results.events[0];
1500
+ if (!first) return undefined;
1501
+ return isRunLevel(first.eventType)
1502
+ ? '__run__'
1503
+ : (first.correlationId ?? undefined);
1504
+ })()
1505
+ : parsed.id
1506
+ );
1507
+ virtuosoRef.current?.scrollToIndex({
1508
+ index: 0,
1509
+ align: 'start',
1510
+ behavior: 'smooth',
1511
+ });
1512
+ } catch {
1513
+ if (
1514
+ abortController.signal.aborted ||
1515
+ searchRequestRef.current !== requestId
1516
+ ) {
1517
+ return;
1518
+ }
1519
+ setSearchResults([]);
1520
+ setSearchResultsTruncated(false);
1521
+ setSearchNotFound(false);
1522
+ setSearchError('Failed to search events. Try again.');
1523
+ setSelectedGroupKey(undefined);
1524
+ } finally {
1525
+ if (
1526
+ searchRequestRef.current === requestId &&
1527
+ !abortController.signal.aborted
1528
+ ) {
1529
+ setSearchLoading(false);
1330
1530
  }
1331
1531
  }
1532
+ })();
1533
+ }, 300);
1534
+
1535
+ return () => {
1536
+ clearTimeout(timer);
1537
+ abortController.abort();
1538
+ };
1539
+ }, [searchQuery, onExactIdSearch]);
1540
+
1541
+ const handleSearchKeyDown = useCallback(
1542
+ (event: ReactKeyboardEvent<HTMLInputElement>) => {
1543
+ if (event.key !== 'Enter') {
1544
+ return;
1332
1545
  }
1333
- }
1334
- if (bestMatch) {
1335
- setSelectedGroupKey(bestMatch.groupKey);
1336
- virtuosoRef.current?.scrollToIndex({
1337
- index: bestMatch.index,
1338
- align: 'center',
1339
- behavior: 'smooth',
1340
- });
1341
- }
1342
- }, [searchQuery, searchIndex]);
1546
+
1547
+ const trimmed = searchQuery.trim();
1548
+ if (
1549
+ !trimmed ||
1550
+ parseExactWorkflowSearchId(trimmed) ||
1551
+ !onExactIdSearch ||
1552
+ !looksLikeWorkflowIdSearchInput(trimmed)
1553
+ ) {
1554
+ return;
1555
+ }
1556
+
1557
+ toast.info('Enter a full step ID, wait ID, hook ID, or event ID');
1558
+ },
1559
+ [searchQuery, onExactIdSearch, toast]
1560
+ );
1343
1561
 
1344
1562
  // Track whether we've ever had events to distinguish initial load from refetch
1345
1563
  const hasHadEventsRef = useRef(false);
@@ -1385,17 +1603,6 @@ export function EventListView({
1385
1603
  );
1386
1604
  }
1387
1605
 
1388
- if (!isLoading && (!events || events.length === 0)) {
1389
- return (
1390
- <div
1391
- className="flex items-center justify-center h-full text-sm"
1392
- style={{ color: 'var(--ds-gray-700)' }}
1393
- >
1394
- No events found
1395
- </div>
1396
- );
1397
- }
1398
-
1399
1606
  return (
1400
1607
  <DecryptClickContext.Provider
1401
1608
  value={onDecrypt ? { onDecrypt, isDecrypting } : undefined}
@@ -1460,9 +1667,16 @@ export function EventListView({
1460
1667
  </div>
1461
1668
  <input
1462
1669
  type="search"
1463
- placeholder="Search by name, event type, or ID…"
1670
+ placeholder="Search by step ID, wait ID, hook ID, or event ID…"
1464
1671
  value={searchQuery}
1465
1672
  onChange={(e) => setSearchQuery(e.target.value)}
1673
+ onKeyDown={handleSearchKeyDown}
1674
+ disabled={!onExactIdSearch}
1675
+ title={
1676
+ onExactIdSearch
1677
+ ? undefined
1678
+ : 'Exact ID search is unavailable in this view.'
1679
+ }
1466
1680
  style={{
1467
1681
  marginLeft: -16,
1468
1682
  paddingInline: 12,
@@ -1473,6 +1687,8 @@ export function EventListView({
1473
1687
  outline: 'none',
1474
1688
  height: 40,
1475
1689
  width: '100%',
1690
+ opacity: onExactIdSearch ? 1 : 0.5,
1691
+ cursor: onExactIdSearch ? 'text' : 'not-allowed',
1476
1692
  }}
1477
1693
  />
1478
1694
  </label>
@@ -1501,8 +1717,13 @@ export function EventListView({
1501
1717
  >
1502
1718
  <div className="flex-shrink-0" style={{ width: GUTTER_WIDTH }} />
1503
1719
  <div className="w-5 flex-shrink-0" />
1720
+ {showSeparateEventOccurrenceTimestamps && (
1721
+ <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1722
+ Occurred
1723
+ </div>
1724
+ )}
1504
1725
  <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1505
- Time
1726
+ Created
1506
1727
  </div>
1507
1728
  <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1508
1729
  Event Type
@@ -1519,8 +1740,25 @@ export function EventListView({
1519
1740
  </div>
1520
1741
 
1521
1742
  {/* Virtualized event rows or refetching skeleton */}
1522
- {isRefetching ? (
1523
- <RowsSkeleton />
1743
+ {isRefetching || searchLoading ? (
1744
+ <RowsSkeleton
1745
+ showSeparateEventOccurrenceTimestamps={
1746
+ showSeparateEventOccurrenceTimestamps
1747
+ }
1748
+ />
1749
+ ) : sortedEvents.length === 0 ? (
1750
+ <div
1751
+ className="flex flex-1 items-center justify-center px-6 text-center text-sm"
1752
+ style={{ color: 'var(--ds-gray-700)' }}
1753
+ >
1754
+ {searchNotFound && searchQuery.trim()
1755
+ ? `No events found for ${searchQuery.trim()}`
1756
+ : searchError
1757
+ ? searchError
1758
+ : parsedSearchId && searchQuery.trim() && !onExactIdSearch
1759
+ ? 'Exact ID search is unavailable in this view.'
1760
+ : 'No events found'}
1761
+ </div>
1524
1762
  ) : (
1525
1763
  <Virtuoso
1526
1764
  ref={virtuosoRef}
@@ -1528,7 +1766,11 @@ export function EventListView({
1528
1766
  overscan={20}
1529
1767
  defaultItemHeight={40}
1530
1768
  endReached={() => {
1531
- if (!hasMoreEvents || isLoadingMoreEvents) {
1769
+ if (
1770
+ isExactSearchActive ||
1771
+ !hasMoreEvents ||
1772
+ isLoadingMoreEvents
1773
+ ) {
1532
1774
  return;
1533
1775
  }
1534
1776
  void onLoadMoreEvents?.();
@@ -1558,6 +1800,10 @@ export function EventListView({
1558
1800
  onCacheEventData={cacheEventData}
1559
1801
  encryptionKey={encryptionKey}
1560
1802
  onEncryptedDataDetected={handleEncryptedDataDetected}
1803
+ suppressGroupDimming={isExactSearchActive}
1804
+ showSeparateEventOccurrenceTimestamps={
1805
+ showSeparateEventOccurrenceTimestamps
1806
+ }
1561
1807
  />
1562
1808
  );
1563
1809
  }}
@@ -1575,10 +1821,15 @@ export function EventListView({
1575
1821
  }}
1576
1822
  >
1577
1823
  <span>
1578
- {sortedEvents.length} event
1579
- {sortedEvents.length !== 1 ? 's' : ''} loaded
1824
+ {isExactSearchActive
1825
+ ? searchError
1826
+ ? searchError
1827
+ : searchNotFound
1828
+ ? `No events found for ${searchQuery.trim()}`
1829
+ : `${sortedEvents.length} event${sortedEvents.length !== 1 ? 's' : ''} for ${searchQuery.trim()}${searchResultsTruncated ? ' (results may be truncated)' : ''}`
1830
+ : `${sortedEvents.length} event${sortedEvents.length !== 1 ? 's' : ''} loaded`}
1580
1831
  </span>
1581
- {hasMoreEvents && (
1832
+ {!isExactSearchActive && hasMoreEvents && (
1582
1833
  <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
1583
1834
  <div className="pointer-events-auto">
1584
1835
  <LoadMoreButton
@@ -1593,3 +1844,11 @@ export function EventListView({
1593
1844
  </DecryptClickContext.Provider>
1594
1845
  );
1595
1846
  }
1847
+
1848
+ export function EventListView(props: EventsListProps) {
1849
+ return (
1850
+ <ContextCardProvider>
1851
+ <EventListViewInner {...props} />
1852
+ </ContextCardProvider>
1853
+ );
1854
+ }