@workflow/web-shared 5.0.0-beta.38 → 5.0.0-beta.39

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 (307) hide show
  1. package/README.md +3 -3
  2. package/dist/components/event-list-view.d.ts.map +1 -1
  3. package/dist/components/event-list-view.js +10 -10
  4. package/dist/components/index.d.ts +3 -5
  5. package/dist/components/index.d.ts.map +1 -1
  6. package/dist/components/index.js +3 -5
  7. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  8. package/dist/components/sidebar/attribute-panel.js +32 -8
  9. package/dist/components/sidebar/attributes-block.js +3 -3
  10. package/dist/components/sidebar/copyable-data-block.js +2 -2
  11. package/dist/components/sidebar/entity-detail-panel.js +3 -3
  12. package/dist/components/sidebar/events-list.js +2 -2
  13. package/dist/components/stream-viewer.js +2 -2
  14. package/dist/components/trace-viewer/components/copy-button.d.ts.map +1 -0
  15. package/dist/components/trace-viewer/components/copy-button.js +27 -0
  16. package/dist/components/trace-viewer/components/detail-panel-width.d.ts.map +1 -0
  17. package/dist/components/trace-viewer/components/detail-panel-width.js +67 -0
  18. package/dist/components/trace-viewer/components/detail-panel.d.ts.map +1 -0
  19. package/dist/components/trace-viewer/components/detail-panel.js +122 -0
  20. package/dist/components/trace-viewer/components/draggable-border.d.ts.map +1 -0
  21. package/dist/components/trace-viewer/components/draggable-border.js +140 -0
  22. package/dist/components/trace-viewer/components/event-list.d.ts.map +1 -0
  23. package/dist/components/trace-viewer/components/event-list.js +47 -0
  24. package/dist/components/trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
  25. package/dist/components/trace-viewer/components/middle-truncate/copy-selection.js +54 -0
  26. package/dist/components/trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
  27. package/dist/components/trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
  28. package/dist/components/trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
  29. package/dist/components/trace-viewer/components/middle-truncate/truncate.js +101 -0
  30. package/dist/components/trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +1 -0
  31. package/dist/components/trace-viewer/components/middle-truncate/use-middle-truncate.js +114 -0
  32. package/dist/components/trace-viewer/components/minimap.d.ts.map +1 -0
  33. package/dist/components/trace-viewer/components/minimap.js +337 -0
  34. package/dist/components/trace-viewer/components/pane-constants.d.ts.map +1 -0
  35. package/dist/components/trace-viewer/components/pane-constants.js +16 -0
  36. package/dist/components/trace-viewer/components/span-markers.d.ts.map +1 -0
  37. package/dist/components/trace-viewer/components/span-markers.js +85 -0
  38. package/dist/components/trace-viewer/components/split-pane.d.ts.map +1 -0
  39. package/dist/components/trace-viewer/components/split-pane.js +41 -0
  40. package/dist/components/trace-viewer/components/timeline.d.ts.map +1 -0
  41. package/dist/components/trace-viewer/components/timeline.js +321 -0
  42. package/dist/components/trace-viewer/components/trace-shortcut-helper.d.ts.map +1 -0
  43. package/dist/components/trace-viewer/components/trace-shortcut-helper.js +55 -0
  44. package/dist/components/trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -0
  45. package/dist/components/trace-viewer/components/trace-viewer-skeleton.js +26 -0
  46. package/dist/components/trace-viewer/components/use-element-width.d.ts.map +1 -0
  47. package/dist/components/trace-viewer/components/use-element-width.js +25 -0
  48. package/dist/components/trace-viewer/components/use-row-window.d.ts.map +1 -0
  49. package/dist/components/trace-viewer/components/use-row-window.js +107 -0
  50. package/dist/components/trace-viewer/context.d.ts +14 -182
  51. package/dist/components/trace-viewer/context.d.ts.map +1 -1
  52. package/dist/components/trace-viewer/context.js +44 -351
  53. package/dist/components/trace-viewer/icons.d.ts.map +1 -0
  54. package/dist/components/trace-viewer/icons.js +15 -0
  55. package/dist/components/trace-viewer/search.d.ts.map +1 -0
  56. package/dist/components/trace-viewer/search.js +134 -0
  57. package/dist/components/trace-viewer/trace-viewer.d.ts +7 -28
  58. package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -1
  59. package/dist/components/trace-viewer/trace-viewer.js +306 -356
  60. package/dist/components/trace-viewer/types.d.ts +2 -211
  61. package/dist/components/trace-viewer/types.d.ts.map +1 -1
  62. package/dist/components/trace-viewer/types.js +1 -1
  63. package/dist/components/trace-viewer/utils.d.ts.map +1 -0
  64. package/dist/components/trace-viewer/utils.js +430 -0
  65. package/dist/components/{trace-viewer-new.d.ts → trace-viewer.d.ts} +3 -3
  66. package/dist/components/trace-viewer.d.ts.map +1 -0
  67. package/dist/components/trace-viewer.js +21 -0
  68. package/dist/components/ui/alert.js +2 -2
  69. package/dist/components/ui/button.d.ts +1 -1
  70. package/dist/components/ui/button.d.ts.map +1 -1
  71. package/dist/components/ui/button.js +3 -3
  72. package/dist/components/ui/card.d.ts.map +1 -1
  73. package/dist/components/ui/card.js +3 -3
  74. package/dist/components/ui/error-card.js +2 -2
  75. package/dist/components/ui/error-stack-block.js +4 -4
  76. package/dist/components/ui/kbd.js +3 -3
  77. package/dist/components/ui/timestamp-tooltip.js +3 -3
  78. package/dist/components/workflow-traces/trace-span-construction.d.ts +1 -1
  79. package/dist/components/workflow-traces/trace-span-construction.js +1 -1
  80. package/dist/lib/trace-builder.d.ts +1 -1
  81. package/dist/lib/trace-builder.d.ts.map +1 -1
  82. package/dist/lib/trace-builder.js +1 -1
  83. package/dist/lib/trace-types.d.ts +54 -0
  84. package/dist/lib/trace-types.d.ts.map +1 -0
  85. package/dist/lib/trace-types.js +2 -0
  86. package/package.json +5 -5
  87. package/src/components/event-list-view.tsx +12 -9
  88. package/src/components/index.ts +3 -5
  89. package/src/components/sidebar/attribute-panel.tsx +46 -8
  90. package/src/components/sidebar/attributes-block.tsx +2 -2
  91. package/src/components/sidebar/copyable-data-block.tsx +1 -1
  92. package/src/components/sidebar/entity-detail-panel.tsx +3 -3
  93. package/src/components/sidebar/events-list.tsx +1 -1
  94. package/src/components/stream-viewer.tsx +1 -1
  95. package/src/components/{new-trace-viewer → trace-viewer}/components/event-list.tsx +1 -1
  96. package/src/components/{new-trace-viewer → trace-viewer}/components/minimap.tsx +1 -4
  97. package/src/components/{new-trace-viewer → trace-viewer}/components/timeline.tsx +2 -5
  98. package/src/components/{new-trace-viewer → trace-viewer}/components/trace-shortcut-helper.tsx +1 -1
  99. package/src/components/trace-viewer/context.tsx +63 -656
  100. package/src/components/trace-viewer/trace-viewer.tsx +563 -531
  101. package/src/components/trace-viewer/types.ts +2 -234
  102. package/src/components/{new-trace-viewer → trace-viewer}/utils.ts +2 -2
  103. package/src/components/{trace-viewer-new.tsx → trace-viewer.tsx} +5 -5
  104. package/src/components/ui/alert.tsx +1 -1
  105. package/src/components/ui/button.tsx +3 -3
  106. package/src/components/ui/card.tsx +2 -9
  107. package/src/components/ui/error-card.tsx +3 -3
  108. package/src/components/ui/error-stack-block.tsx +3 -3
  109. package/src/components/ui/kbd.tsx +2 -2
  110. package/src/components/ui/timestamp-tooltip.tsx +4 -4
  111. package/src/components/workflow-traces/trace-span-construction.ts +1 -1
  112. package/src/lib/trace-builder.ts +1 -1
  113. package/src/lib/trace-types.ts +56 -0
  114. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +0 -1
  115. package/dist/components/new-trace-viewer/components/copy-button.js +0 -27
  116. package/dist/components/new-trace-viewer/components/detail-panel-width.d.ts.map +0 -1
  117. package/dist/components/new-trace-viewer/components/detail-panel-width.js +0 -67
  118. package/dist/components/new-trace-viewer/components/detail-panel.d.ts.map +0 -1
  119. package/dist/components/new-trace-viewer/components/detail-panel.js +0 -122
  120. package/dist/components/new-trace-viewer/components/draggable-border.d.ts.map +0 -1
  121. package/dist/components/new-trace-viewer/components/draggable-border.js +0 -140
  122. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +0 -1
  123. package/dist/components/new-trace-viewer/components/event-list.js +0 -47
  124. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +0 -1
  125. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +0 -54
  126. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +0 -1
  127. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +0 -81
  128. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +0 -1
  129. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +0 -101
  130. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +0 -1
  131. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js +0 -114
  132. package/dist/components/new-trace-viewer/components/minimap.d.ts.map +0 -1
  133. package/dist/components/new-trace-viewer/components/minimap.js +0 -337
  134. package/dist/components/new-trace-viewer/components/pane-constants.d.ts.map +0 -1
  135. package/dist/components/new-trace-viewer/components/pane-constants.js +0 -16
  136. package/dist/components/new-trace-viewer/components/span-markers.d.ts.map +0 -1
  137. package/dist/components/new-trace-viewer/components/span-markers.js +0 -85
  138. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +0 -1
  139. package/dist/components/new-trace-viewer/components/split-pane.js +0 -41
  140. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +0 -1
  141. package/dist/components/new-trace-viewer/components/timeline.js +0 -321
  142. package/dist/components/new-trace-viewer/components/trace-shortcut-helper.d.ts.map +0 -1
  143. package/dist/components/new-trace-viewer/components/trace-shortcut-helper.js +0 -55
  144. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts.map +0 -1
  145. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.js +0 -26
  146. package/dist/components/new-trace-viewer/components/use-element-width.d.ts.map +0 -1
  147. package/dist/components/new-trace-viewer/components/use-element-width.js +0 -25
  148. package/dist/components/new-trace-viewer/components/use-row-window.d.ts.map +0 -1
  149. package/dist/components/new-trace-viewer/components/use-row-window.js +0 -107
  150. package/dist/components/new-trace-viewer/context.d.ts +0 -17
  151. package/dist/components/new-trace-viewer/context.d.ts.map +0 -1
  152. package/dist/components/new-trace-viewer/context.js +0 -48
  153. package/dist/components/new-trace-viewer/icons.d.ts.map +0 -1
  154. package/dist/components/new-trace-viewer/icons.js +0 -15
  155. package/dist/components/new-trace-viewer/search.d.ts.map +0 -1
  156. package/dist/components/new-trace-viewer/search.js +0 -134
  157. package/dist/components/new-trace-viewer/trace-viewer.d.ts +0 -11
  158. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +0 -1
  159. package/dist/components/new-trace-viewer/trace-viewer.js +0 -336
  160. package/dist/components/new-trace-viewer/types.d.ts +0 -3
  161. package/dist/components/new-trace-viewer/types.d.ts.map +0 -1
  162. package/dist/components/new-trace-viewer/types.js +0 -2
  163. package/dist/components/new-trace-viewer/utils.d.ts.map +0 -1
  164. package/dist/components/new-trace-viewer/utils.js +0 -430
  165. package/dist/components/run-trace-view.d.ts +0 -23
  166. package/dist/components/run-trace-view.d.ts.map +0 -1
  167. package/dist/components/run-trace-view.js +0 -11
  168. package/dist/components/trace-viewer/components/map.d.ts +0 -8
  169. package/dist/components/trace-viewer/components/map.d.ts.map +0 -1
  170. package/dist/components/trace-viewer/components/map.js +0 -162
  171. package/dist/components/trace-viewer/components/markers.d.ts +0 -23
  172. package/dist/components/trace-viewer/components/markers.d.ts.map +0 -1
  173. package/dist/components/trace-viewer/components/markers.js +0 -416
  174. package/dist/components/trace-viewer/components/node.d.ts +0 -35
  175. package/dist/components/trace-viewer/components/node.d.ts.map +0 -1
  176. package/dist/components/trace-viewer/components/node.js +0 -286
  177. package/dist/components/trace-viewer/components/search-input.d.ts +0 -9
  178. package/dist/components/trace-viewer/components/search-input.d.ts.map +0 -1
  179. package/dist/components/trace-viewer/components/search-input.js +0 -9
  180. package/dist/components/trace-viewer/components/search.d.ts +0 -3
  181. package/dist/components/trace-viewer/components/search.d.ts.map +0 -1
  182. package/dist/components/trace-viewer/components/search.js +0 -25
  183. package/dist/components/trace-viewer/components/span-content.d.ts +0 -19
  184. package/dist/components/trace-viewer/components/span-content.d.ts.map +0 -1
  185. package/dist/components/trace-viewer/components/span-content.js +0 -137
  186. package/dist/components/trace-viewer/components/span-detail-panel.d.ts +0 -10
  187. package/dist/components/trace-viewer/components/span-detail-panel.d.ts.map +0 -1
  188. package/dist/components/trace-viewer/components/span-detail-panel.js +0 -285
  189. package/dist/components/trace-viewer/components/span-segments.d.ts +0 -50
  190. package/dist/components/trace-viewer/components/span-segments.d.ts.map +0 -1
  191. package/dist/components/trace-viewer/components/span-segments.js +0 -445
  192. package/dist/components/trace-viewer/components/span-strategies.d.ts +0 -46
  193. package/dist/components/trace-viewer/components/span-strategies.d.ts.map +0 -1
  194. package/dist/components/trace-viewer/components/span-strategies.js +0 -108
  195. package/dist/components/trace-viewer/components/ui.d.ts +0 -28
  196. package/dist/components/trace-viewer/components/ui.d.ts.map +0 -1
  197. package/dist/components/trace-viewer/components/ui.js +0 -39
  198. package/dist/components/trace-viewer/components/zoom-button.d.ts +0 -3
  199. package/dist/components/trace-viewer/components/zoom-button.d.ts.map +0 -1
  200. package/dist/components/trace-viewer/components/zoom-button.js +0 -29
  201. package/dist/components/trace-viewer/components/zoom-icons.d.ts +0 -11
  202. package/dist/components/trace-viewer/components/zoom-icons.d.ts.map +0 -1
  203. package/dist/components/trace-viewer/components/zoom-icons.js +0 -11
  204. package/dist/components/trace-viewer/index.d.ts +0 -5
  205. package/dist/components/trace-viewer/index.d.ts.map +0 -1
  206. package/dist/components/trace-viewer/index.js +0 -4
  207. package/dist/components/trace-viewer/trace-viewer.module.css +0 -1465
  208. package/dist/components/trace-viewer/util/constants.d.ts +0 -9
  209. package/dist/components/trace-viewer/util/constants.d.ts.map +0 -1
  210. package/dist/components/trace-viewer/util/constants.js +0 -9
  211. package/dist/components/trace-viewer/util/scrollbar-width.d.ts +0 -2
  212. package/dist/components/trace-viewer/util/scrollbar-width.d.ts.map +0 -1
  213. package/dist/components/trace-viewer/util/scrollbar-width.js +0 -14
  214. package/dist/components/trace-viewer/util/tree.d.ts +0 -13
  215. package/dist/components/trace-viewer/util/tree.d.ts.map +0 -1
  216. package/dist/components/trace-viewer/util/tree.js +0 -227
  217. package/dist/components/trace-viewer/util/use-immediate-style.d.ts +0 -15
  218. package/dist/components/trace-viewer/util/use-immediate-style.d.ts.map +0 -1
  219. package/dist/components/trace-viewer/util/use-immediate-style.js +0 -23
  220. package/dist/components/trace-viewer/util/use-streaming-spans.d.ts +0 -8
  221. package/dist/components/trace-viewer/util/use-streaming-spans.d.ts.map +0 -1
  222. package/dist/components/trace-viewer/util/use-streaming-spans.js +0 -345
  223. package/dist/components/trace-viewer/util/use-trackpad-zoom.d.ts +0 -6
  224. package/dist/components/trace-viewer/util/use-trackpad-zoom.d.ts.map +0 -1
  225. package/dist/components/trace-viewer/util/use-trackpad-zoom.js +0 -39
  226. package/dist/components/trace-viewer/worker.d.ts +0 -2
  227. package/dist/components/trace-viewer/worker.d.ts.map +0 -1
  228. package/dist/components/trace-viewer/worker.js +0 -109
  229. package/dist/components/trace-viewer-new.d.ts.map +0 -1
  230. package/dist/components/trace-viewer-new.js +0 -21
  231. package/dist/components/workflow-trace-view.d.ts +0 -39
  232. package/dist/components/workflow-trace-view.d.ts.map +0 -1
  233. package/dist/components/workflow-trace-view.js +0 -690
  234. package/dist/components/workflow-traces/trace-colors.d.ts +0 -15
  235. package/dist/components/workflow-traces/trace-colors.d.ts.map +0 -1
  236. package/dist/components/workflow-traces/trace-colors.js +0 -86
  237. package/src/components/new-trace-viewer/context.tsx +0 -93
  238. package/src/components/new-trace-viewer/trace-viewer.tsx +0 -632
  239. package/src/components/new-trace-viewer/types.ts +0 -2
  240. package/src/components/run-trace-view.tsx +0 -80
  241. package/src/components/trace-viewer/components/map.tsx +0 -226
  242. package/src/components/trace-viewer/components/markers.tsx +0 -612
  243. package/src/components/trace-viewer/components/node.tsx +0 -506
  244. package/src/components/trace-viewer/components/search-input.tsx +0 -52
  245. package/src/components/trace-viewer/components/search.tsx +0 -47
  246. package/src/components/trace-viewer/components/span-content.tsx +0 -247
  247. package/src/components/trace-viewer/components/span-detail-panel.tsx +0 -655
  248. package/src/components/trace-viewer/components/span-segments.ts +0 -585
  249. package/src/components/trace-viewer/components/span-strategies.ts +0 -205
  250. package/src/components/trace-viewer/components/ui.tsx +0 -156
  251. package/src/components/trace-viewer/components/zoom-button.tsx +0 -61
  252. package/src/components/trace-viewer/components/zoom-icons.tsx +0 -65
  253. package/src/components/trace-viewer/index.tsx +0 -4
  254. package/src/components/trace-viewer/trace-viewer.module.css +0 -1465
  255. package/src/components/trace-viewer/util/constants.ts +0 -8
  256. package/src/components/trace-viewer/util/scrollbar-width.ts +0 -13
  257. package/src/components/trace-viewer/util/tree.ts +0 -277
  258. package/src/components/trace-viewer/util/use-immediate-style.ts +0 -38
  259. package/src/components/trace-viewer/util/use-streaming-spans.ts +0 -426
  260. package/src/components/trace-viewer/util/use-trackpad-zoom.tsx +0 -51
  261. package/src/components/trace-viewer/worker.ts +0 -131
  262. package/src/components/workflow-trace-view.tsx +0 -1166
  263. package/src/components/workflow-traces/trace-colors.ts +0 -112
  264. package/dist/components/{new-trace-viewer → trace-viewer}/components/copy-button.d.ts +0 -0
  265. package/dist/components/{new-trace-viewer → trace-viewer}/components/detail-panel-width.d.ts +0 -0
  266. package/dist/components/{new-trace-viewer → trace-viewer}/components/detail-panel.d.ts +0 -0
  267. package/dist/components/{new-trace-viewer → trace-viewer}/components/draggable-border.d.ts +0 -0
  268. package/dist/components/{new-trace-viewer → trace-viewer}/components/event-list.d.ts +0 -0
  269. package/dist/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/copy-selection.d.ts +0 -0
  270. package/dist/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/middle-truncate.d.ts +0 -0
  271. package/dist/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/truncate.d.ts +0 -0
  272. package/dist/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/use-middle-truncate.d.ts +0 -0
  273. package/dist/components/{new-trace-viewer → trace-viewer}/components/minimap.d.ts +0 -0
  274. package/dist/components/{new-trace-viewer → trace-viewer}/components/pane-constants.d.ts +0 -0
  275. package/dist/components/{new-trace-viewer → trace-viewer}/components/span-markers.d.ts +0 -0
  276. package/dist/components/{new-trace-viewer → trace-viewer}/components/split-pane.d.ts +0 -0
  277. package/dist/components/{new-trace-viewer → trace-viewer}/components/timeline.d.ts +0 -0
  278. package/dist/components/{new-trace-viewer → trace-viewer}/components/timeline.module.css +0 -0
  279. package/dist/components/{new-trace-viewer → trace-viewer}/components/trace-shortcut-helper.d.ts +0 -0
  280. package/dist/components/{new-trace-viewer → trace-viewer}/components/trace-shortcut-helper.module.css +0 -0
  281. package/dist/components/{new-trace-viewer → trace-viewer}/components/trace-viewer-skeleton.d.ts +0 -0
  282. package/dist/components/{new-trace-viewer → trace-viewer}/components/use-element-width.d.ts +0 -0
  283. package/dist/components/{new-trace-viewer → trace-viewer}/components/use-row-window.d.ts +0 -0
  284. package/dist/components/{new-trace-viewer → trace-viewer}/icons.d.ts +0 -0
  285. package/dist/components/{new-trace-viewer → trace-viewer}/search.d.ts +0 -0
  286. package/dist/components/{new-trace-viewer → trace-viewer}/utils.d.ts +0 -0
  287. package/src/components/{new-trace-viewer → trace-viewer}/components/copy-button.tsx +0 -0
  288. package/src/components/{new-trace-viewer → trace-viewer}/components/detail-panel-width.test.ts +0 -0
  289. package/src/components/{new-trace-viewer → trace-viewer}/components/detail-panel-width.ts +0 -0
  290. package/src/components/{new-trace-viewer → trace-viewer}/components/detail-panel.tsx +0 -0
  291. package/src/components/{new-trace-viewer → trace-viewer}/components/draggable-border.tsx +0 -0
  292. package/src/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/copy-selection.ts +0 -0
  293. package/src/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/middle-truncate.tsx +1 -1
  294. /package/src/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/truncate.ts +0 -0
  295. /package/src/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/use-middle-truncate.ts +0 -0
  296. /package/src/components/{new-trace-viewer → trace-viewer}/components/pane-constants.ts +0 -0
  297. /package/src/components/{new-trace-viewer → trace-viewer}/components/span-markers.tsx +0 -0
  298. /package/src/components/{new-trace-viewer → trace-viewer}/components/split-pane.tsx +0 -0
  299. /package/src/components/{new-trace-viewer → trace-viewer}/components/timeline.module.css +0 -0
  300. /package/src/components/{new-trace-viewer → trace-viewer}/components/trace-shortcut-helper.module.css +0 -0
  301. /package/src/components/{new-trace-viewer → trace-viewer}/components/trace-viewer-skeleton.tsx +0 -0
  302. /package/src/components/{new-trace-viewer → trace-viewer}/components/use-element-width.ts +0 -0
  303. /package/src/components/{new-trace-viewer → trace-viewer}/components/use-row-window.ts +0 -0
  304. /package/src/components/{new-trace-viewer → trace-viewer}/icons.tsx +0 -0
  305. /package/src/components/{new-trace-viewer → trace-viewer}/search.ts +0 -0
  306. /package/src/components/{new-trace-viewer → trace-viewer}/utils.test.ts +0 -0
  307. /package/src/{components/trace-viewer/modules.d.ts → modules.d.ts} +0 -0
@@ -1,1166 +0,0 @@
1
- 'use client';
2
-
3
- import { parseStepName, parseWorkflowName } from '@workflow/utils/parse-name';
4
- import type { Event, Hook, WorkflowRun } from '@workflow/world';
5
- import {
6
- ChevronDown,
7
- ChevronUp,
8
- Clock,
9
- Copy,
10
- Info,
11
- Send,
12
- Type,
13
- X,
14
- XCircle,
15
- } from 'lucide-react';
16
- import type { MouseEvent as ReactMouseEvent, ReactNode } from 'react';
17
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
18
- import { createPortal } from 'react-dom';
19
- import { useToast } from '../lib/toast';
20
- import {
21
- buildTrace,
22
- filterSpanRawEvents,
23
- type TraceWithMeta,
24
- } from '../lib/trace-builder';
25
- import { ErrorBoundary } from './error-boundary';
26
- import {
27
- EntityDetailPanel,
28
- type SelectedSpanInfo,
29
- type SpanSelectionInfo,
30
- } from './sidebar/entity-detail-panel';
31
- import { ResolveHookModal } from './sidebar/resolve-hook-modal';
32
- import type { FetchSpanDetail } from './sidebar/use-selected-span-detail';
33
- import {
34
- TraceViewerContextProvider,
35
- TraceViewerTimeline,
36
- useTraceViewer,
37
- } from './trace-viewer';
38
- import type { Span } from './trace-viewer/types';
39
- import { Skeleton } from './ui/skeleton';
40
- import { Spinner } from './ui/spinner';
41
- import {
42
- getCustomSpanClassName,
43
- getCustomSpanEventClassName,
44
- } from './workflow-traces/trace-colors';
45
-
46
- /**
47
- * While a run is live, continuously grow root.duration and rescale so the
48
- * trace always fits within the viewport. Individual span widths are grown
49
- * by each SpanComponent's own useEffect (see node.tsx).
50
- */
51
- function useLiveTick(isLive: boolean): void {
52
- const { state, dispatch } = useTraceViewer();
53
- const stateRef = useRef(state);
54
- stateRef.current = state;
55
-
56
- useEffect(() => {
57
- if (!isLive) return;
58
-
59
- // Grow root.duration on every frame so span rAFs see the latest time
60
- let rafId = 0;
61
- const tick = (): void => {
62
- const { root } = stateRef.current;
63
- if (root.startTime) {
64
- const nowMs = Date.now();
65
- if (nowMs > root.endTime) {
66
- root.endTime = nowMs;
67
- root.duration = root.endTime - root.startTime;
68
- }
69
- }
70
- rafId = requestAnimationFrame(tick);
71
- };
72
- rafId = requestAnimationFrame(tick);
73
-
74
- // Re-scale smoothly so the trace fits the viewport as it grows.
75
- // We dispatch detectBaseScale only when the computed baseScale has
76
- // changed enough to matter visually (>0.1% shift), avoiding
77
- // unnecessary React re-renders while keeping things smooth.
78
- let scaleRafId = 0;
79
- let lastBaseScale = 0;
80
- const scaleTick = (): void => {
81
- const s = stateRef.current;
82
- if (s.root.duration > 0) {
83
- const newBaseScale = (s.width - s.scrollbarWidth) / s.root.duration;
84
- const delta = Math.abs(newBaseScale - lastBaseScale);
85
- if (delta > lastBaseScale * 0.001 || lastBaseScale === 0) {
86
- lastBaseScale = newBaseScale;
87
- dispatch({ type: 'detectBaseScale' });
88
- }
89
- }
90
- scaleRafId = requestAnimationFrame(scaleTick);
91
- };
92
- scaleRafId = requestAnimationFrame(scaleTick);
93
-
94
- return () => {
95
- cancelAnimationFrame(rafId);
96
- cancelAnimationFrame(scaleRafId);
97
- };
98
- }, [isLive, dispatch]);
99
- }
100
-
101
- const DEFAULT_PANEL_WIDTH = 450;
102
- const MIN_PANEL_WIDTH = 240;
103
-
104
- // ──────────────────────────────────────────────────────────────────────────
105
- // Right-click context menu for spans
106
- // ──────────────────────────────────────────────────────────────────────────
107
-
108
- type ResourceType = 'sleep' | 'step' | 'hook' | 'run' | 'unknown';
109
-
110
- interface ContextMenuState {
111
- x: number;
112
- y: number;
113
- spanId: string;
114
- spanName: string;
115
- resourceType: ResourceType;
116
- /** Whether the span represents an active/pending resource (not yet completed) */
117
- isActive: boolean;
118
- }
119
-
120
- interface ContextMenuItem {
121
- label: string;
122
- icon?: ReactNode;
123
- action: () => void;
124
- destructive?: boolean;
125
- disabled?: boolean;
126
- }
127
-
128
- function SpanContextMenu({
129
- menu,
130
- items,
131
- onClose,
132
- }: {
133
- menu: ContextMenuState;
134
- items: ContextMenuItem[];
135
- onClose: () => void;
136
- }): ReactNode {
137
- const menuRef = useRef<HTMLDivElement>(null);
138
-
139
- // Close on outside click
140
- useEffect(() => {
141
- const handleClick = (e: MouseEvent): void => {
142
- if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
143
- onClose();
144
- }
145
- };
146
- const handleKeyDown = (e: KeyboardEvent): void => {
147
- if (e.key === 'Escape') {
148
- onClose();
149
- }
150
- };
151
- const handleScroll = (): void => {
152
- onClose();
153
- };
154
- // Use a timeout so we don't close immediately from the same event
155
- const timeout = setTimeout(() => {
156
- window.addEventListener('mousedown', handleClick);
157
- window.addEventListener('keydown', handleKeyDown);
158
- window.addEventListener('scroll', handleScroll, true);
159
- }, 0);
160
- return () => {
161
- clearTimeout(timeout);
162
- window.removeEventListener('mousedown', handleClick);
163
- window.removeEventListener('keydown', handleKeyDown);
164
- window.removeEventListener('scroll', handleScroll, true);
165
- };
166
- }, [onClose]);
167
-
168
- // Adjust position if menu would overflow viewport
169
- const [adjustedPos, setAdjustedPos] = useState({ x: menu.x, y: menu.y });
170
- useEffect(() => {
171
- const el = menuRef.current;
172
- if (!el) return;
173
- const rect = el.getBoundingClientRect();
174
- let { x, y } = menu;
175
- if (rect.right > window.innerWidth) {
176
- x = window.innerWidth - rect.width - 8;
177
- }
178
- if (rect.bottom > window.innerHeight) {
179
- y = window.innerHeight - rect.height - 8;
180
- }
181
- if (x !== menu.x || y !== menu.y) {
182
- setAdjustedPos({ x, y });
183
- }
184
- }, [menu]);
185
-
186
- if (items.length === 0) return null;
187
-
188
- return createPortal(
189
- <div
190
- ref={menuRef}
191
- style={{
192
- position: 'fixed',
193
- left: adjustedPos.x,
194
- top: adjustedPos.y,
195
- zIndex: 99999,
196
- boxShadow: 'var(--ds-shadow-menu)',
197
- borderRadius: 12,
198
- background: 'var(--ds-background-100)',
199
- padding: 'var(--geist-space-gap-quarter, 4px)',
200
- fontSize: 14,
201
- minWidth: 180,
202
- overflowX: 'hidden',
203
- overflowY: 'auto',
204
- }}
205
- >
206
- <div
207
- style={{
208
- display: 'block',
209
- color: 'var(--ds-gray-900)',
210
- fontSize: '0.75rem',
211
- padding: 'var(--geist-gap-quarter, 4px) var(--geist-space-2x, 8px)',
212
- fontWeight: 500,
213
- overflow: 'hidden',
214
- textOverflow: 'ellipsis',
215
- whiteSpace: 'nowrap',
216
- maxWidth: 240,
217
- borderBottom: '1px solid var(--ds-gray-alpha-400)',
218
- marginBottom: 4,
219
- }}
220
- >
221
- {menu.spanName}
222
- </div>
223
- {items.map((item) => (
224
- <button
225
- key={item.label}
226
- type="button"
227
- style={{
228
- outline: 'none',
229
- cursor: item.disabled ? 'not-allowed' : 'pointer',
230
- display: 'flex',
231
- alignItems: 'center',
232
- gap: 8,
233
- padding: '0 var(--geist-space-2x, 8px)',
234
- height: 40,
235
- textDecoration: 'none',
236
- borderRadius: 6,
237
- color: item.destructive
238
- ? 'var(--ds-red-900)'
239
- : 'var(--ds-gray-1000)',
240
- width: '100%',
241
- background: 'transparent',
242
- border: 'none',
243
- fontSize: 14,
244
- textAlign: 'left',
245
- opacity: item.disabled ? 0.4 : 1,
246
- transition: 'background 0.15s',
247
- }}
248
- disabled={item.disabled}
249
- onMouseEnter={(e) => {
250
- (e.currentTarget as HTMLButtonElement).style.background =
251
- 'var(--ds-gray-alpha-100)';
252
- }}
253
- onMouseLeave={(e) => {
254
- (e.currentTarget as HTMLButtonElement).style.background =
255
- 'transparent';
256
- }}
257
- onClick={() => {
258
- item.action();
259
- onClose();
260
- }}
261
- >
262
- {item.icon ?? null}
263
- {item.label}
264
- </button>
265
- ))}
266
- </div>,
267
- document.body
268
- );
269
- }
270
-
271
- /** Inner wrapper that has access to the TraceViewer context */
272
- function TraceViewerWithContextMenu({
273
- trace,
274
- run,
275
- isLive,
276
- onWakeUpSleep,
277
- onCancelRun,
278
- onResolveHook,
279
- onLoadMoreSpans,
280
- hasMoreSpans = false,
281
- isLoadingMoreSpans = false,
282
- children,
283
- }: {
284
- trace: { spans: Span[] };
285
- run: WorkflowRun;
286
- isLive: boolean;
287
- onWakeUpSleep?: (
288
- runId: string,
289
- correlationId: string
290
- ) => Promise<{ stoppedCount: number }>;
291
- onCancelRun?: (runId: string) => Promise<void>;
292
- onResolveHook?: (
293
- hookToken: string,
294
- payload: unknown,
295
- hook?: Hook
296
- ) => Promise<void>;
297
- onLoadMoreSpans?: () => void | Promise<void>;
298
- hasMoreSpans?: boolean;
299
- isLoadingMoreSpans?: boolean;
300
- children: ReactNode;
301
- }): ReactNode {
302
- const toast = useToast();
303
- const { state, dispatch } = useTraceViewer();
304
-
305
- // Drive active span widths at 60fps without React re-renders
306
- useLiveTick(isLive);
307
- const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
308
- const [resolveHookTarget, setResolveHookTarget] = useState<{
309
- hookId: string;
310
- token: string;
311
- } | null>(null);
312
- const [resolvingHook, setResolvingHook] = useState(false);
313
- // Track hooks resolved in this session so the context menu item hides immediately
314
- const [resolvedHookIds, setResolvedHookIds] = useState<Set<string>>(
315
- new Set()
316
- );
317
-
318
- // Build a lookup map: spanId -> span
319
- const spanLookup = useMemo(() => {
320
- const map = new Map<string, Span>();
321
- for (const span of trace.spans) {
322
- map.set(span.spanId, span);
323
- }
324
- return map;
325
- }, [trace.spans]);
326
-
327
- const handleResolveHook = useCallback(
328
- async (payload: unknown) => {
329
- if (resolvingHook || !resolveHookTarget || !onResolveHook) return;
330
- if (!resolveHookTarget.token) {
331
- toast.error('Unable to resolve hook', {
332
- description:
333
- 'Missing hook token. Try refreshing the run data and retry.',
334
- });
335
- return;
336
- }
337
- try {
338
- setResolvingHook(true);
339
- await onResolveHook(resolveHookTarget.token, payload);
340
- toast.success('Hook resolved', {
341
- description: 'The payload has been sent and the hook resolved.',
342
- });
343
- // Mark this hook as resolved locally so the menu item hides immediately
344
- setResolvedHookIds((prev) =>
345
- new Set(prev).add(resolveHookTarget.hookId)
346
- );
347
- setResolveHookTarget(null);
348
- } catch (err) {
349
- console.error('Failed to resolve hook:', err);
350
- toast.error('Failed to resolve hook', {
351
- description:
352
- err instanceof Error ? err.message : 'An unknown error occurred',
353
- });
354
- } finally {
355
- setResolvingHook(false);
356
- }
357
- },
358
- [onResolveHook, resolveHookTarget, resolvingHook]
359
- );
360
-
361
- const handleContextMenu = useCallback(
362
- (e: ReactMouseEvent | MouseEvent): void => {
363
- const target = e.target as HTMLElement;
364
- const $button = target.closest<HTMLButtonElement>('[data-span-id]');
365
- if (!$button) return;
366
-
367
- const spanId = $button.dataset.spanId;
368
- if (!spanId) return;
369
-
370
- e.preventDefault();
371
- e.stopPropagation();
372
-
373
- const span = spanLookup.get(spanId);
374
- if (!span) return;
375
-
376
- const resourceType =
377
- (span.attributes.resource as ResourceType) ?? 'unknown';
378
- const spanData = span.attributes.data as
379
- | Record<string, unknown>
380
- | undefined;
381
- const isActive = !spanData?.completedAt && !spanData?.disposedAt;
382
-
383
- setContextMenu({
384
- x: e.clientX,
385
- y: e.clientY,
386
- spanId,
387
- spanName: span.name,
388
- resourceType,
389
- isActive,
390
- });
391
- },
392
- [spanLookup]
393
- );
394
-
395
- const containerRef = useRef<HTMLDivElement>(null);
396
- useEffect(() => {
397
- const $container = containerRef.current;
398
- if (!$container) return;
399
-
400
- const onContextMenu = (event: MouseEvent): void => {
401
- handleContextMenu(event);
402
- };
403
- $container.addEventListener('contextmenu', onContextMenu);
404
-
405
- return () => {
406
- $container.removeEventListener('contextmenu', onContextMenu);
407
- };
408
- }, [handleContextMenu]);
409
-
410
- const loadingMoreRef = useRef(false);
411
- useEffect(() => {
412
- const timeline = state.timelineRef.current;
413
- if (!timeline || !onLoadMoreSpans || !hasMoreSpans) {
414
- return;
415
- }
416
-
417
- const thresholdPx = 200;
418
- const maybeLoadMore = () => {
419
- if (loadingMoreRef.current || isLoadingMoreSpans || !hasMoreSpans) {
420
- return;
421
- }
422
- const remaining =
423
- timeline.scrollHeight - timeline.scrollTop - timeline.clientHeight;
424
- if (remaining > thresholdPx) {
425
- return;
426
- }
427
-
428
- loadingMoreRef.current = true;
429
- Promise.resolve(onLoadMoreSpans()).finally(() => {
430
- loadingMoreRef.current = false;
431
- });
432
- };
433
-
434
- timeline.addEventListener('scroll', maybeLoadMore);
435
- maybeLoadMore();
436
- return () => {
437
- timeline.removeEventListener('scroll', maybeLoadMore);
438
- };
439
- }, [state.timelineRef, onLoadMoreSpans, hasMoreSpans, isLoadingMoreSpans]);
440
-
441
- const closeMenu = useCallback(() => {
442
- setContextMenu(null);
443
- }, []);
444
-
445
- const getMenuItems = useCallback(
446
- (menu: ContextMenuState): ContextMenuItem[] => {
447
- const items: ContextMenuItem[] = [];
448
-
449
- // Sleep-specific: Wake Up (only on active runs)
450
- const isRunActive = !run.completedAt;
451
- if (
452
- menu.resourceType === 'sleep' &&
453
- menu.isActive &&
454
- isRunActive &&
455
- onWakeUpSleep
456
- ) {
457
- items.push({
458
- label: 'Wake Up Sleep',
459
- icon: <Clock className="h-3.5 w-3.5" />,
460
- action: () => {
461
- onWakeUpSleep(run.runId, menu.spanId)
462
- .then((result) => {
463
- if (result.stoppedCount > 0) {
464
- toast.success('Sleep woken up', {
465
- description: `Woke up ${String(result.stoppedCount)} sleep${result.stoppedCount > 1 ? 's' : ''}`,
466
- });
467
- } else {
468
- toast.info('No active sleeps found', {
469
- description: 'The sleep may have already completed.',
470
- });
471
- }
472
- })
473
- .catch((err: unknown) => {
474
- toast.error('Failed to wake up sleep', {
475
- description:
476
- err instanceof Error
477
- ? err.message
478
- : 'An unknown error occurred',
479
- });
480
- });
481
- },
482
- });
483
- }
484
-
485
- // Hook-specific: Resolve Hook (only on active, unresolved hooks)
486
- if (menu.resourceType === 'hook' && isRunActive && onResolveHook) {
487
- const span = spanLookup.get(menu.spanId);
488
- const hookData = span?.attributes?.data as
489
- | { token?: string; disposedAt?: unknown }
490
- | undefined;
491
- // Check data-level disposedAt, span events, AND local resolved state
492
- const isDisposed =
493
- Boolean(hookData?.disposedAt) ||
494
- Boolean(span?.events?.some((e) => e.name === 'hook_disposed')) ||
495
- resolvedHookIds.has(menu.spanId);
496
- if (hookData?.token && !isDisposed) {
497
- items.push({
498
- label: 'Resolve Hook',
499
- icon: <Send className="h-3.5 w-3.5" />,
500
- action: () => {
501
- setResolveHookTarget({
502
- hookId: menu.spanId,
503
- token: hookData.token!,
504
- });
505
- },
506
- });
507
- }
508
- }
509
-
510
- // Run-specific: Cancel (only on active runs)
511
- if (menu.resourceType === 'run' && isRunActive && onCancelRun) {
512
- items.push({
513
- label: 'Cancel Run',
514
- icon: <XCircle className="h-3.5 w-3.5" />,
515
- destructive: true,
516
- action: () => {
517
- onCancelRun(run.runId).catch((err: unknown) => {
518
- toast.error('Failed to cancel run', {
519
- description:
520
- err instanceof Error
521
- ? err.message
522
- : 'An unknown error occurred',
523
- });
524
- });
525
- },
526
- });
527
- }
528
-
529
- // Common actions
530
- items.push({
531
- label: 'View Details',
532
- icon: <Info className="h-3.5 w-3.5" />,
533
- action: () => {
534
- dispatch({ type: 'select', id: menu.spanId });
535
- },
536
- });
537
-
538
- items.push({
539
- label: 'Copy Name',
540
- icon: <Type className="h-3.5 w-3.5" />,
541
- action: () => {
542
- navigator.clipboard
543
- .writeText(menu.spanName)
544
- .then(() => {
545
- toast.success('Name copied to clipboard');
546
- })
547
- .catch(() => {
548
- toast.error('Failed to copy name');
549
- });
550
- },
551
- });
552
-
553
- items.push({
554
- label: 'Copy ID',
555
- icon: <Copy className="h-3.5 w-3.5" />,
556
- action: () => {
557
- navigator.clipboard
558
- .writeText(menu.spanId)
559
- .then(() => {
560
- toast.success('ID copied to clipboard');
561
- })
562
- .catch(() => {
563
- toast.error('Failed to copy ID');
564
- });
565
- },
566
- });
567
-
568
- return items;
569
- },
570
- [
571
- dispatch,
572
- onWakeUpSleep,
573
- onCancelRun,
574
- onResolveHook,
575
- spanLookup,
576
- resolvedHookIds,
577
- run.runId,
578
- run.completedAt,
579
- ]
580
- );
581
-
582
- return (
583
- <div className="relative w-full h-full" ref={containerRef}>
584
- {children}
585
- {contextMenu ? (
586
- <SpanContextMenu
587
- menu={contextMenu}
588
- items={getMenuItems(contextMenu)}
589
- onClose={closeMenu}
590
- />
591
- ) : null}
592
- <ResolveHookModal
593
- isOpen={resolveHookTarget !== null}
594
- onClose={() => setResolveHookTarget(null)}
595
- onSubmit={handleResolveHook}
596
- isSubmitting={resolvingHook}
597
- />
598
- </div>
599
- );
600
- }
601
-
602
- /** Re-export SpanSelectionInfo for consumers */
603
- export type { SpanSelectionInfo };
604
-
605
- // ---------------------------------------------------------------------------
606
- // Bridge: reads selected span from trace viewer context and notifies parent
607
- // ---------------------------------------------------------------------------
608
-
609
- /**
610
- * Bridge component that lives INSIDE the TraceViewerContextProvider and
611
- * reads the selected span info, passing it up to the parent via callback.
612
- * This allows the parent to render the detail panel OUTSIDE the context.
613
- */
614
- function SelectionBridge({
615
- onSelectionChange,
616
- }: {
617
- onSelectionChange: (info: SelectedSpanInfo | null) => void;
618
- }) {
619
- const { state } = useTraceViewer();
620
- const { selected } = state;
621
- const onSelectionChangeRef = useRef(onSelectionChange);
622
- onSelectionChangeRef.current = onSelectionChange;
623
- const prevSpanIdRef = useRef<string | undefined>(undefined);
624
-
625
- useEffect(() => {
626
- const currentSpanId = selected?.span.spanId;
627
- if (currentSpanId === prevSpanIdRef.current) return;
628
- prevSpanIdRef.current = currentSpanId;
629
-
630
- if (selected) {
631
- onSelectionChangeRef.current({
632
- data: selected.span.attributes?.data,
633
- resource: selected.span.attributes?.resource as string | undefined,
634
- spanId: selected.span.spanId,
635
- });
636
- } else {
637
- onSelectionChangeRef.current(null);
638
- }
639
- }, [selected]);
640
-
641
- return null;
642
- }
643
-
644
- /**
645
- * Bridge component to deselect from outside the context.
646
- */
647
- function DeselectBridge({ triggerDeselect }: { triggerDeselect: number }) {
648
- const { dispatch } = useTraceViewer();
649
-
650
- useEffect(() => {
651
- if (triggerDeselect > 0) {
652
- dispatch({ type: 'deselect' });
653
- }
654
- }, [triggerDeselect, dispatch]);
655
-
656
- return null;
657
- }
658
-
659
- /**
660
- * Bridge component to select a span from outside the context.
661
- */
662
- function SelectBridge({
663
- selectRequest,
664
- }: {
665
- selectRequest: { id: string; token: number } | null;
666
- }) {
667
- const { dispatch } = useTraceViewer();
668
-
669
- useEffect(() => {
670
- if (selectRequest?.id) {
671
- dispatch({ type: 'select', id: selectRequest.id });
672
- }
673
- }, [dispatch, selectRequest]);
674
-
675
- return null;
676
- }
677
-
678
- // ---------------------------------------------------------------------------
679
- // Panel chrome (header with name/duration, close button)
680
- // ---------------------------------------------------------------------------
681
-
682
- function PanelResizeHandle({
683
- onResize,
684
- }: {
685
- onResize: (deltaX: number) => void;
686
- }) {
687
- const handlePointerDown = useCallback(
688
- (e: React.PointerEvent) => {
689
- e.preventDefault();
690
- let lastX = e.clientX;
691
- const onPointerMove = (moveEvent: PointerEvent) => {
692
- const deltaX = lastX - moveEvent.clientX;
693
- lastX = moveEvent.clientX;
694
- onResize(deltaX);
695
- };
696
- const onPointerUp = () => {
697
- document.removeEventListener('pointermove', onPointerMove);
698
- document.removeEventListener('pointerup', onPointerUp);
699
- };
700
- document.addEventListener('pointermove', onPointerMove);
701
- document.addEventListener('pointerup', onPointerUp);
702
- },
703
- [onResize]
704
- );
705
-
706
- return (
707
- <div
708
- className="absolute left-0 top-0 bottom-0 w-1 cursor-col-resize hover:bg-blue-400/50 z-10"
709
- onPointerDown={handlePointerDown}
710
- />
711
- );
712
- }
713
-
714
- // ---------------------------------------------------------------------------
715
- // Trace viewer footer — loading / waiting indicator
716
- // ---------------------------------------------------------------------------
717
-
718
- function TraceViewerFooter({
719
- hasMore,
720
- isLive,
721
- isInitialLoading,
722
- }: {
723
- hasMore: boolean;
724
- isLive: boolean;
725
- isInitialLoading: boolean;
726
- }): ReactNode {
727
- const style = { color: 'var(--ds-gray-900)' };
728
- if (hasMore || isInitialLoading) {
729
- return (
730
- <div
731
- className="flex items-center justify-center gap-2 py-3 text-xs"
732
- style={style}
733
- >
734
- <Spinner size={14} />
735
- Loading more events…
736
- </div>
737
- );
738
- }
739
- if (isLive) {
740
- return (
741
- <div
742
- className="flex items-center justify-center py-3 text-xs"
743
- style={style}
744
- >
745
- Waiting for more events…
746
- </div>
747
- );
748
- }
749
- return (
750
- <div
751
- className="flex items-center justify-center py-3 text-xs"
752
- style={style}
753
- >
754
- End of run
755
- </div>
756
- );
757
- }
758
-
759
- // ---------------------------------------------------------------------------
760
- // Main component
761
- // ---------------------------------------------------------------------------
762
-
763
- export const WorkflowTraceViewer = ({
764
- run,
765
- events,
766
- isLoading,
767
- error,
768
- fetchSpanDetail,
769
- onWakeUpSleep,
770
- onResolveHook,
771
- onCancelRun,
772
- onStreamClick,
773
- onRunClick,
774
- onLoadEventData,
775
- onLoadMoreSpans,
776
- hasMoreSpans = false,
777
- isLoadingMoreSpans = false,
778
- encryptionKey,
779
- onDecrypt,
780
- isDecrypting = false,
781
- showSeparateEventOccurrenceTimestamps = false,
782
- }: {
783
- run: WorkflowRun;
784
- events: Event[];
785
- isLoading?: boolean;
786
- error?: Error | null;
787
- fetchSpanDetail: FetchSpanDetail;
788
- onWakeUpSleep?: (
789
- runId: string,
790
- correlationId: string
791
- ) => Promise<{ stoppedCount: number }>;
792
- onResolveHook?: (
793
- hookToken: string,
794
- payload: unknown,
795
- hook?: Hook
796
- ) => Promise<void>;
797
- /** Callback to cancel the current run */
798
- onCancelRun?: (runId: string) => Promise<void>;
799
- /** Callback when a stream reference is clicked in the detail panel */
800
- onStreamClick?: (streamId: string) => void;
801
- /** Callback when a run reference is clicked in the detail panel */
802
- onRunClick?: (runId: string) => void;
803
- /** Callback to load event data for a specific event (lazy loading in sidebar) */
804
- onLoadEventData?: (event: Event) => Promise<unknown | null>;
805
- /** Load next trace page when vertical scroll reaches bottom. */
806
- onLoadMoreSpans?: () => void | Promise<void>;
807
- /** Whether trace pagination has more data to load. */
808
- hasMoreSpans?: boolean;
809
- /** Whether trace pagination is currently fetching another page. */
810
- isLoadingMoreSpans?: boolean;
811
- /** Encryption key (available after Decrypt), threaded to event list for re-loading */
812
- encryptionKey?: Uint8Array;
813
- /** Callback to initiate decryption of encrypted run data */
814
- onDecrypt?: () => void;
815
- /** Whether the encryption key is currently being fetched */
816
- isDecrypting?: boolean;
817
- /** Show occurredAt separately instead of folding it into the Created timestamp. */
818
- showSeparateEventOccurrenceTimestamps?: boolean;
819
- }) => {
820
- const toast = useToast();
821
- const [selectedSpan, setSelectedSpan] = useState<SelectedSpanInfo | null>(
822
- null
823
- );
824
- const [panelWidth, setPanelWidth] = useState(DEFAULT_PANEL_WIDTH);
825
- const [deselectTrigger, setDeselectTrigger] = useState(0);
826
- const [selectRequest, setSelectRequest] = useState<{
827
- id: string;
828
- token: number;
829
- } | null>(null);
830
-
831
- const isLive = Boolean(run && !run.completedAt);
832
-
833
- // Build trace only when actual data changes — no timer-driven rebuilds.
834
- // Active span widths are animated imperatively by useLiveTick at 60fps.
835
- const traceWithMeta: TraceWithMeta | undefined = useMemo(() => {
836
- if (!run?.runId) {
837
- return undefined;
838
- }
839
- return buildTrace(run, events, new Date());
840
- // eslint-disable-next-line react-hooks/exhaustive-deps -- `new Date()` is intentionally not a dep; useLiveTick handles live growth
841
- }, [run, events]);
842
- const trace = traceWithMeta;
843
-
844
- useEffect(() => {
845
- if (error && !isLoading) {
846
- console.error(error);
847
- toast.error('Error loading workflow trace data', {
848
- description: error.message,
849
- });
850
- }
851
- }, [error, isLoading]);
852
-
853
- const handleSelectionChange = useCallback(
854
- (info: SelectedSpanInfo | null) => {
855
- if (info) {
856
- // Filter raw events for the selected span: child spans match on
857
- // correlationId (stepId/hookId), the run root span gets run-level
858
- // events. This bypasses the trace worker pipeline entirely.
859
- const rawEvents = filterSpanRawEvents(
860
- events,
861
- info.resource,
862
- info.spanId
863
- );
864
- setSelectedSpan({ ...info, rawEvents });
865
- } else {
866
- setSelectedSpan(null);
867
- }
868
- },
869
- [events]
870
- );
871
-
872
- // Keep selected span raw events live as polling updates arrive.
873
- useEffect(() => {
874
- const correlationId = selectedSpan?.spanId;
875
- if (!correlationId) return;
876
-
877
- const nextRawEvents = filterSpanRawEvents(
878
- events,
879
- selectedSpan?.resource,
880
- correlationId
881
- );
882
- setSelectedSpan((prev) => {
883
- if (!prev || prev.spanId !== correlationId) {
884
- return prev;
885
- }
886
-
887
- const prevRawEvents = prev.rawEvents ?? [];
888
- const isSame =
889
- prevRawEvents.length === nextRawEvents.length &&
890
- prevRawEvents.every(
891
- (event, index) => event.eventId === nextRawEvents[index]?.eventId
892
- );
893
- if (isSame) {
894
- return prev;
895
- }
896
-
897
- return {
898
- ...prev,
899
- rawEvents: nextRawEvents,
900
- };
901
- });
902
- }, [events, selectedSpan?.spanId, selectedSpan?.resource]);
903
-
904
- // Reset selected span when navigating to a different run
905
- useEffect(() => {
906
- setSelectedSpan(null);
907
- setDeselectTrigger((n) => n + 1);
908
- }, [run?.runId]);
909
-
910
- const handleClose = useCallback(() => {
911
- setSelectedSpan(null);
912
- setDeselectTrigger((n) => n + 1);
913
- }, []);
914
-
915
- const handleResize = useCallback((deltaX: number) => {
916
- setPanelWidth((w) => Math.max(MIN_PANEL_WIDTH, w + deltaX));
917
- }, []);
918
- const selectedSpanIndex = useMemo(() => {
919
- if (!selectedSpan?.spanId || !trace) return -1;
920
- return trace.spans.findIndex((span) => span.spanId === selectedSpan.spanId);
921
- }, [selectedSpan?.spanId, trace]);
922
- const canSelectPrevSpan = selectedSpanIndex > 0;
923
- const canSelectNextSpan =
924
- selectedSpanIndex >= 0 &&
925
- trace != null &&
926
- selectedSpanIndex < trace.spans.length - 1;
927
- const handleSelectPrevSpan = useCallback(() => {
928
- if (!trace || selectedSpanIndex <= 0) return;
929
- const targetId = trace.spans[selectedSpanIndex - 1]?.spanId;
930
- if (!targetId) return;
931
- setSelectRequest({ id: targetId, token: Date.now() });
932
- }, [selectedSpanIndex, trace]);
933
- const handleSelectNextSpan = useCallback(() => {
934
- if (!trace || selectedSpanIndex < 0) return;
935
- const targetId = trace.spans[selectedSpanIndex + 1]?.spanId;
936
- if (!targetId) return;
937
- setSelectRequest({ id: targetId, token: Date.now() });
938
- }, [selectedSpanIndex, trace]);
939
-
940
- // Get the selected span name for the panel header
941
- const selectedSpanName = useMemo(() => {
942
- if (!selectedSpan?.data) return undefined;
943
- const data = selectedSpan.data as Record<string, unknown>;
944
- const stepName = data.stepName as string | undefined;
945
- const workflowName = data.workflowName as string | undefined;
946
- return (
947
- (stepName ? parseStepName(stepName)?.shortName : undefined) ??
948
- (workflowName ? parseWorkflowName(workflowName)?.shortName : undefined) ??
949
- stepName ??
950
- workflowName ??
951
- (data.hookId as string) ??
952
- 'Details'
953
- );
954
- }, [selectedSpan?.data]);
955
-
956
- if (!trace) {
957
- return (
958
- <div className="relative w-full h-full">
959
- <div className="border-b border-gray-alpha-400 w-full" />
960
- <Skeleton className="w-full ml-2 mt-1 mb-1 h-[56px]" />
961
- <div className="p-2 relative w-full">
962
- <Skeleton className="w-full mt-6 h-[20px]" />
963
- <Skeleton className="w-[10%] mt-2 ml-6 h-[20px]" />
964
- <Skeleton className="w-[10%] mt-2 ml-12 h-[20px]" />
965
- <Skeleton className="w-[20%] mt-2 ml-16 h-[20px]" />
966
- </div>
967
- </div>
968
- );
969
- }
970
-
971
- return (
972
- <div className="relative w-full h-full flex flex-row">
973
- {/* Timeline (takes remaining space) */}
974
- <div className="flex-1 min-w-0 relative">
975
- <TraceViewerContextProvider
976
- customSpanClassNameFunc={getCustomSpanClassName}
977
- customSpanEventClassNameFunc={getCustomSpanEventClassName}
978
- >
979
- <SelectionBridge onSelectionChange={handleSelectionChange} />
980
- <DeselectBridge triggerDeselect={deselectTrigger} />
981
- <SelectBridge selectRequest={selectRequest} />
982
- <TraceViewerWithContextMenu
983
- trace={trace}
984
- run={run}
985
- isLive={isLive}
986
- onWakeUpSleep={onWakeUpSleep}
987
- onCancelRun={onCancelRun}
988
- onResolveHook={onResolveHook}
989
- onLoadMoreSpans={onLoadMoreSpans}
990
- hasMoreSpans={hasMoreSpans}
991
- isLoadingMoreSpans={isLoadingMoreSpans}
992
- >
993
- <TraceViewerTimeline
994
- eagerRender
995
- height="100%"
996
- isLive={isLive}
997
- trace={trace}
998
- knownDurationMs={traceWithMeta?.knownDurationMs}
999
- hasMoreData={hasMoreSpans || Boolean(isLoading)}
1000
- footer={
1001
- <TraceViewerFooter
1002
- hasMore={hasMoreSpans}
1003
- isLive={isLive}
1004
- isInitialLoading={Boolean(isLoading)}
1005
- />
1006
- }
1007
- />
1008
- </TraceViewerWithContextMenu>
1009
- </TraceViewerContextProvider>
1010
- </div>
1011
-
1012
- {/* Detail panel (rendered outside the context, as a sibling) */}
1013
- {selectedSpan && (
1014
- <div
1015
- className="relative border-l flex-shrink-0 flex flex-col"
1016
- style={{
1017
- width: panelWidth,
1018
- borderColor: 'var(--ds-gray-200)',
1019
- backgroundColor: 'var(--ds-background-100)',
1020
- }}
1021
- >
1022
- <PanelResizeHandle onResize={handleResize} />
1023
- {/* Panel header */}
1024
- <div
1025
- className="flex items-center justify-between px-3 py-2 border-y flex-shrink-0"
1026
- style={{ borderColor: 'var(--ds-gray-200)' }}
1027
- >
1028
- <div className="min-w-0 flex-1">
1029
- <div
1030
- className="text-sm font-medium truncate"
1031
- style={{ color: 'var(--ds-gray-1000)' }}
1032
- title={selectedSpanName}
1033
- >
1034
- {selectedSpanName}
1035
- </div>
1036
- </div>
1037
- <div className="flex items-center">
1038
- <button
1039
- type="button"
1040
- aria-label="Previous span"
1041
- onClick={handleSelectPrevSpan}
1042
- disabled={!canSelectPrevSpan}
1043
- style={{
1044
- display: 'flex',
1045
- alignItems: 'center',
1046
- justifyContent: 'center',
1047
- marginLeft: 6,
1048
- width: 24,
1049
- height: 24,
1050
- padding: 0,
1051
- borderRadius: 6,
1052
- border: 'none',
1053
- background: 'transparent',
1054
- color: 'var(--ds-gray-700)',
1055
- cursor: canSelectPrevSpan ? 'pointer' : 'not-allowed',
1056
- opacity: canSelectPrevSpan ? 1 : 0.45,
1057
- flexShrink: 0,
1058
- transition: 'background 0.15s',
1059
- }}
1060
- onMouseEnter={(e) => {
1061
- if (!canSelectPrevSpan) return;
1062
- e.currentTarget.style.background = 'var(--ds-gray-alpha-100)';
1063
- }}
1064
- onMouseLeave={(e) => {
1065
- e.currentTarget.style.background = 'transparent';
1066
- }}
1067
- >
1068
- <ChevronUp size={16} />
1069
- </button>
1070
- <button
1071
- type="button"
1072
- aria-label="Next span"
1073
- onClick={handleSelectNextSpan}
1074
- disabled={!canSelectNextSpan}
1075
- style={{
1076
- display: 'flex',
1077
- alignItems: 'center',
1078
- justifyContent: 'center',
1079
- marginLeft: 2,
1080
- width: 24,
1081
- height: 24,
1082
- padding: 0,
1083
- borderRadius: 6,
1084
- border: 'none',
1085
- background: 'transparent',
1086
- color: 'var(--ds-gray-700)',
1087
- cursor: canSelectNextSpan ? 'pointer' : 'not-allowed',
1088
- opacity: canSelectNextSpan ? 1 : 0.45,
1089
- flexShrink: 0,
1090
- transition: 'background 0.15s',
1091
- }}
1092
- onMouseEnter={(e) => {
1093
- if (!canSelectNextSpan) return;
1094
- e.currentTarget.style.background = 'var(--ds-gray-alpha-100)';
1095
- }}
1096
- onMouseLeave={(e) => {
1097
- e.currentTarget.style.background = 'transparent';
1098
- }}
1099
- >
1100
- <ChevronDown size={16} />
1101
- </button>
1102
- </div>
1103
- <div
1104
- className="flex items-center"
1105
- style={{
1106
- borderLeft: '1px solid var(--ds-gray-300)',
1107
- marginLeft: 6,
1108
- }}
1109
- >
1110
- <button
1111
- type="button"
1112
- aria-label="Close panel"
1113
- onClick={handleClose}
1114
- style={{
1115
- display: 'flex',
1116
- alignItems: 'center',
1117
- justifyContent: 'center',
1118
- marginLeft: 6,
1119
- width: 24,
1120
- height: 24,
1121
- padding: 0,
1122
- borderRadius: 6,
1123
- border: 'none',
1124
- background: 'transparent',
1125
- color: 'var(--ds-gray-700)',
1126
- cursor: 'pointer',
1127
- flexShrink: 0,
1128
- transition: 'background 0.15s',
1129
- }}
1130
- onMouseEnter={(e) => {
1131
- e.currentTarget.style.background = 'var(--ds-gray-alpha-100)';
1132
- }}
1133
- onMouseLeave={(e) => {
1134
- e.currentTarget.style.background = 'transparent';
1135
- }}
1136
- >
1137
- <X size={16} />
1138
- </button>
1139
- </div>
1140
- </div>
1141
- {/* Panel body */}
1142
- <div className="flex-1 overflow-y-auto">
1143
- <ErrorBoundary title="Failed to load entity details">
1144
- <EntityDetailPanel
1145
- run={run}
1146
- onStreamClick={onStreamClick}
1147
- onRunClick={onRunClick}
1148
- fetchSpanDetail={fetchSpanDetail}
1149
- onWakeUpSleep={onWakeUpSleep}
1150
- onLoadEventData={onLoadEventData}
1151
- onResolveHook={onResolveHook}
1152
- encryptionKey={encryptionKey}
1153
- onDecrypt={onDecrypt}
1154
- isDecrypting={isDecrypting}
1155
- selectedSpan={selectedSpan}
1156
- showSeparateEventOccurrenceTimestamps={
1157
- showSeparateEventOccurrenceTimestamps
1158
- }
1159
- />
1160
- </ErrorBoundary>
1161
- </div>
1162
- </div>
1163
- )}
1164
- </div>
1165
- );
1166
- };