@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
@@ -1,53 +1,70 @@
1
1
  'use client';
2
2
 
3
- import { parseStepName, parseWorkflowName } from '@workflow/utils/parse-name';
4
- import { Search, X } from 'lucide-react';
3
+ import { RotateCcw, Search, ZoomIn, ZoomOut } from 'lucide-react';
5
4
  import {
6
5
  type ReactNode,
7
6
  useCallback,
7
+ useDeferredValue,
8
8
  useEffect,
9
9
  useMemo,
10
10
  useRef,
11
11
  useState,
12
12
  } from 'react';
13
- import { ErrorBoundary } from '../error-boundary';
13
+ import { useLoadMoreOnScroll } from '../../hooks/use-load-more-on-scroll';
14
+ import { useReducedMotion } from '../../hooks/use-reduced-motion';
14
15
  import {
15
- EntityDetailPanel,
16
- type SelectedSpanInfo,
17
- } from '../sidebar/entity-detail-panel';
18
- import { useSidebarDataOptional } from '../sidebar/sidebar-data-context';
19
- import type { Trace } from '../trace-viewer/types';
20
- import { formatDuration, getHighResInMs } from '../trace-viewer/util/timing';
21
- import { CopyButton } from './components/copy-button';
22
- import { SplitPane } from './components/split-pane';
16
+ formatDurationPrecise,
17
+ getHighResInMs,
18
+ } from '../trace-viewer/util/timing';
19
+ import { IconButton } from '../ui/icon-button';
20
+ import { Kbd } from '../ui/kbd';
21
+ import { Spinner } from '../ui/spinner';
22
+ import { TooltipProvider } from '../ui/tooltip';
23
+ import { TraceDetailPanel } from './components/detail-panel';
23
24
  import EventList from './components/event-list';
24
- import { Timeline, TimelineHeader } from './components/timeline';
25
+ import { Minimap } from './components/minimap';
26
+ import { SplitPane } from './components/split-pane';
27
+ import {
28
+ TIMELINE_PADDING_PX,
29
+ Timeline,
30
+ TimelineHeader,
31
+ } from './components/timeline';
32
+ import { TraceShortcutHelper } from './components/trace-shortcut-helper';
33
+ import { ROW_HEIGHT_PX, scrollRowIntoView } from './components/use-row-window';
25
34
  import { ActiveSpanProvider, useActiveSpan } from './context';
26
- import { DetailPanel } from './detail-panel';
27
- import { buildTimeCompression, computeRootBounds } from './utils';
35
+ import { searchSpans } from './search';
36
+ import type { TraceWithMeta } from './types';
37
+ import {
38
+ clampViewportToRoot,
39
+ computeRootBounds,
40
+ computeTimeMarkers,
41
+ type ViewportRange,
42
+ wheelDeltaToPixels,
43
+ wheelZoomScaleFactor,
44
+ } from './utils';
28
45
 
29
46
  interface NewTraceViewerProps {
30
- trace: Trace;
47
+ trace: TraceWithMeta;
48
+ onLoadMore?: () => void | Promise<void>;
49
+ hasMore?: boolean;
50
+ isLoadingMore?: boolean;
31
51
  }
32
52
 
33
53
  const MIN_VIEWPORT_MS = 0.001;
34
- const TIMELINE_PADDING = 16;
35
54
 
36
- interface Viewport {
37
- start: number;
38
- end: number;
39
- }
55
+ const ZOOM_DEBOUNCE_MS = 150;
40
56
 
41
- function useAnimatedViewport(initial: Viewport) {
42
- const [viewport, setViewportState] = useState<Viewport>(initial);
57
+ function useAnimatedViewport(initial: ViewportRange) {
58
+ const [viewport, setViewportState] = useState<ViewportRange>(initial);
43
59
  const animRef = useRef<{
44
60
  raf: number;
45
- from: Viewport;
46
- to: Viewport;
61
+ from: ViewportRange;
62
+ to: ViewportRange;
47
63
  start: number;
48
64
  } | null>(null);
49
65
  const currentRef = useRef(initial);
50
66
  currentRef.current = viewport;
67
+ const reducedMotion = useReducedMotion();
51
68
 
52
69
  const cancel = useCallback(() => {
53
70
  if (animRef.current) {
@@ -57,14 +74,21 @@ function useAnimatedViewport(initial: Viewport) {
57
74
  }, []);
58
75
 
59
76
  const animateTo = useCallback(
60
- (target: Viewport) => {
77
+ (target: ViewportRange) => {
61
78
  cancel();
79
+
80
+ if (reducedMotion) {
81
+ currentRef.current = target;
82
+ setViewportState(target);
83
+ return;
84
+ }
85
+
62
86
  const from = currentRef.current;
63
87
  const anim = { raf: 0, from, to: target, start: performance.now() };
64
88
 
65
89
  const tick = () => {
66
- const t = Math.min((performance.now() - anim.start) / 150, 1);
67
- const e = 1 - (1 - t) * (1 - t);
90
+ const t = Math.min((performance.now() - anim.start) / 240, 1);
91
+ const e = t < 0.5 ? 8 * t * t * t * t : 1 - (-2 * t + 2) ** 4 / 2;
68
92
  setViewportState({
69
93
  start: anim.from.start + (anim.to.start - anim.from.start) * e,
70
94
  end: anim.from.end + (anim.to.end - anim.from.end) * e,
@@ -76,11 +100,11 @@ function useAnimatedViewport(initial: Viewport) {
76
100
  animRef.current = anim;
77
101
  anim.raf = requestAnimationFrame(tick);
78
102
  },
79
- [cancel]
103
+ [cancel, reducedMotion]
80
104
  );
81
105
 
82
106
  const setViewport = useCallback(
83
- (update: Viewport | ((prev: Viewport) => Viewport)) => {
107
+ (update: ViewportRange | ((prev: ViewportRange) => ViewportRange)) => {
84
108
  cancel();
85
109
  if (typeof update === 'function') {
86
110
  setViewportState((prev) => {
@@ -101,69 +125,68 @@ function useAnimatedViewport(initial: Viewport) {
101
125
  return { viewport, setViewport, animateTo };
102
126
  }
103
127
 
104
- // ---------------------------------------------------------------------------
105
- // Hook: bridge ActiveSpanContext + SidebarDataContext → SelectedSpanInfo
106
- // ---------------------------------------------------------------------------
107
-
108
- function useSelectedSpanInfo(): SelectedSpanInfo | null {
109
- const { activeSpan } = useActiveSpan();
110
- const sidebar = useSidebarDataOptional();
111
-
112
- return useMemo(() => {
113
- if (!activeSpan || !sidebar) return null;
114
-
115
- const correlationId = activeSpan.spanId;
116
- const rawEvents = correlationId
117
- ? sidebar.events.filter((e) => e.correlationId === correlationId)
118
- : [];
119
-
120
- return {
121
- data: activeSpan.attributes?.data,
122
- resource: activeSpan.attributes?.resource as string | undefined,
123
- spanId: activeSpan.spanId,
124
- rawEvents,
125
- };
126
- }, [activeSpan, sidebar?.events]);
127
- }
128
-
129
128
  // ---------------------------------------------------------------------------
130
129
  // Root component
131
130
  // ---------------------------------------------------------------------------
132
131
 
133
- export function NewTraceViewer({ trace }: NewTraceViewerProps): ReactNode {
132
+ export function NewTraceViewer({
133
+ trace,
134
+ onLoadMore,
135
+ hasMore,
136
+ isLoadingMore,
137
+ }: NewTraceViewerProps): ReactNode {
134
138
  return (
135
- <ActiveSpanProvider spans={trace.spans}>
136
- <NewTraceViewerContent trace={trace} />
137
- </ActiveSpanProvider>
139
+ <TooltipProvider delayDuration={300}>
140
+ <ActiveSpanProvider spans={trace.spans}>
141
+ <NewTraceViewerContent
142
+ trace={trace}
143
+ onLoadMore={onLoadMore}
144
+ hasMore={hasMore}
145
+ isLoadingMore={isLoadingMore}
146
+ />
147
+ </ActiveSpanProvider>
148
+ </TooltipProvider>
138
149
  );
139
150
  }
140
151
 
141
- function NewTraceViewerContent({ trace }: NewTraceViewerProps): ReactNode {
142
- const { activeSpan, activeSpanId, setActiveSpan, clearActiveSpan } =
143
- useActiveSpan();
152
+ function NewTraceViewerContent({
153
+ trace,
154
+ onLoadMore,
155
+ hasMore,
156
+ isLoadingMore,
157
+ }: NewTraceViewerProps): ReactNode {
158
+ const { activeSpanId, setActiveSpan, clearActiveSpan } = useActiveSpan();
144
159
 
145
- const sidebar = useSidebarDataOptional();
146
- const selectedSpan = useSelectedSpanInfo();
160
+ const reducedMotion = useReducedMotion();
147
161
 
148
162
  const [searchQuery, setSearchQuery] = useState('');
163
+ const deferredSearchQuery = useDeferredValue(searchQuery);
164
+
165
+ const paneRootRef = useRef<HTMLDivElement>(null);
149
166
 
150
- const filteredSpans = useMemo(() => {
151
- const q = searchQuery.trim().toLowerCase();
152
- if (!q) return trace.spans;
153
- return trace.spans.filter(
154
- (s) =>
155
- s.name.toLowerCase().includes(q) || s.resource.toLowerCase().includes(q)
156
- );
157
- }, [trace.spans, searchQuery]);
167
+ const searchResult = useMemo(
168
+ () => searchSpans(trace.spans, deferredSearchQuery),
169
+ [trace.spans, deferredSearchQuery]
170
+ );
158
171
 
159
172
  const root = useMemo(() => computeRootBounds(trace.spans), [trace.spans]);
160
173
 
174
+ const scrollContainerRef = useRef<HTMLDivElement>(null);
175
+ const loadMore = useCallback(() => {
176
+ void onLoadMore?.();
177
+ }, [onLoadMore]);
178
+ const loadMoreSentinelRef = useLoadMoreOnScroll(loadMore, {
179
+ hasMore: Boolean(onLoadMore && hasMore),
180
+ isLoadingMore: Boolean(isLoadingMore),
181
+ rootRef: scrollContainerRef,
182
+ });
183
+
161
184
  const { viewport, setViewport, animateTo } = useAnimatedViewport({
162
185
  start: root.startTime,
163
186
  end: root.startTime + root.duration,
164
187
  });
165
188
 
166
- const prevRootRef = useRef<Viewport>({
189
+ const prevRootRef = useRef<ViewportRange>({
167
190
  start: root.startTime,
168
191
  end: root.startTime + root.duration,
169
192
  });
@@ -185,31 +208,73 @@ function NewTraceViewerContent({ trace }: NewTraceViewerProps): ReactNode {
185
208
  });
186
209
 
187
210
  prevRootRef.current = { start: newStart, end: newEnd };
188
- }, [root.startTime, root.duration]);
211
+ }, [root.startTime, root.duration, setViewport]);
189
212
 
190
213
  const viewDuration = viewport.end - viewport.start;
191
214
 
192
- const compression = useMemo(
193
- () => buildTimeCompression(trace.spans, viewport.start, viewport.end),
194
- [trace.spans, viewport.start, viewport.end]
195
- );
215
+ // Keep a ref to the live viewport so the reveal callback can read the current
216
+ // zoom without being recreated on every pan (which would bust TimelineBar's
217
+ // memo and re-render every row each animation frame).
218
+ const viewportRef = useRef(viewport);
219
+ viewportRef.current = viewport;
196
220
 
197
- const isZoomed =
198
- viewport.start > root.startTime + 0.01 ||
199
- viewport.end < root.startTime + root.duration - 0.01;
221
+ const timeMarkers = useMemo(
222
+ () => computeTimeMarkers(viewDuration, viewport.start - root.startTime),
223
+ [viewDuration, viewport.start, root.startTime]
224
+ );
200
225
 
201
226
  const resetZoom = useCallback(() => {
202
227
  animateTo({ start: root.startTime, end: root.startTime + root.duration });
203
228
  }, [animateTo, root.startTime, root.duration]);
204
229
 
205
- const handleSelectSpan = useCallback(
206
- (spanId: string) => {
207
- if (spanId === activeSpanId) {
208
- clearActiveSpan();
209
- return;
210
- }
211
- setActiveSpan(spanId);
230
+ const clampToRoot = useCallback(
231
+ (next: ViewportRange): ViewportRange =>
232
+ clampViewportToRoot(next, root.startTime, root.endTime, MIN_VIEWPORT_MS),
233
+ [root.startTime, root.endTime]
234
+ );
235
+
236
+ // Pan (keeping the current zoom) so `timeMs` is centered in view — used by the
237
+ // off-screen marker indicators to scroll their marker into view.
238
+ const handleRevealTime = useCallback(
239
+ (timeMs: number) => {
240
+ const { start, end } = viewportRef.current;
241
+ const duration = end - start;
242
+ animateTo(
243
+ clampToRoot({
244
+ start: timeMs - duration / 2,
245
+ end: timeMs + duration / 2,
246
+ })
247
+ );
248
+ },
249
+ [animateTo, clampToRoot]
250
+ );
251
+
252
+ const ZOOM_FACTOR = 0.5;
253
+
254
+ const zoomBy = useCallback(
255
+ (factor: number) => {
256
+ setViewport((prev) => {
257
+ const center = (prev.start + prev.end) / 2;
258
+ const newDuration = Math.max(
259
+ MIN_VIEWPORT_MS,
260
+ (prev.end - prev.start) * factor
261
+ );
262
+ return clampToRoot({
263
+ start: center - newDuration / 2,
264
+ end: center + newDuration / 2,
265
+ });
266
+ });
267
+ },
268
+ [setViewport, clampToRoot]
269
+ );
212
270
 
271
+ const zoomIn = useCallback(() => zoomBy(ZOOM_FACTOR), [zoomBy]);
272
+ const zoomOut = useCallback(() => zoomBy(1 / ZOOM_FACTOR), [zoomBy]);
273
+ const isAtMinZoom = viewDuration >= root.duration;
274
+ const isAtMaxZoom = viewDuration <= MIN_VIEWPORT_MS;
275
+
276
+ const focusViewportOnSpan = useCallback(
277
+ (spanId: string) => {
213
278
  const span = trace.spans.find((s) => s.spanId === spanId);
214
279
  if (!span) return;
215
280
 
@@ -217,12 +282,8 @@ function NewTraceViewerContent({ trace }: NewTraceViewerProps): ReactNode {
217
282
  const spanEnd = getHighResInMs(span.endTime);
218
283
  const spanDuration = spanEnd - spanStart;
219
284
 
220
- const rootS = root.startTime;
221
- const rootE = root.startTime + root.duration;
222
- const rootD = root.duration;
223
-
224
- if (spanDuration > rootD * 0.8) {
225
- animateTo({ start: rootS, end: rootE });
285
+ if (spanDuration > root.duration * 0.8) {
286
+ animateTo({ start: root.startTime, end: root.endTime });
226
287
  return;
227
288
  }
228
289
 
@@ -236,39 +297,90 @@ function NewTraceViewerContent({ trace }: NewTraceViewerProps): ReactNode {
236
297
  newEnd = center + MIN_VIEWPORT_MS / 2;
237
298
  }
238
299
 
239
- if (newStart < rootS) {
240
- const duration = newEnd - newStart;
241
- newStart = rootS;
242
- newEnd = Math.min(rootE, rootS + duration);
243
- }
244
- if (newEnd > rootE) {
245
- const duration = newEnd - newStart;
246
- newEnd = rootE;
247
- newStart = Math.max(rootS, rootE - duration);
248
- }
249
-
250
- animateTo({ start: newStart, end: newEnd });
300
+ animateTo(clampToRoot({ start: newStart, end: newEnd }));
251
301
  },
252
302
  [
253
303
  animateTo,
254
- setActiveSpan,
255
- clearActiveSpan,
256
- activeSpanId,
257
304
  trace.spans,
258
305
  root.startTime,
306
+ root.endTime,
259
307
  root.duration,
308
+ clampToRoot,
260
309
  ]
261
310
  );
262
311
 
312
+ // Bring a row into view when keyboard/button navigation lands on a span that
313
+ // sits outside the shared scroll container's visible area. The list is
314
+ // windowed, so an off-screen row has no DOM node to `scrollIntoView` —
315
+ // `scrollRowIntoView` computes the target offset from the span's index.
316
+ const scrollSpanIntoView = useCallback(
317
+ (spanId: string) => {
318
+ const index = trace.spans.findIndex((s) => s.spanId === spanId);
319
+ if (index === -1) return;
320
+
321
+ const list =
322
+ scrollContainerRef.current?.querySelector<HTMLElement>('#event-list') ??
323
+ null;
324
+ scrollRowIntoView(list, index, ROW_HEIGHT_PX, {
325
+ behavior: reducedMotion ? 'auto' : 'smooth',
326
+ });
327
+ },
328
+ [trace.spans, reducedMotion]
329
+ );
330
+
331
+ const zoomTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
332
+ const cancelPendingZoom = useCallback(() => {
333
+ if (zoomTimerRef.current !== null) {
334
+ clearTimeout(zoomTimerRef.current);
335
+ zoomTimerRef.current = null;
336
+ }
337
+ }, []);
338
+ useEffect(() => cancelPendingZoom, [cancelPendingZoom]);
339
+
340
+ const handleClearActiveSpan = useCallback(() => {
341
+ cancelPendingZoom();
342
+ clearActiveSpan();
343
+ }, [cancelPendingZoom, clearActiveSpan]);
344
+
345
+ const handleSelectSpan = useCallback(
346
+ (spanId: string) => {
347
+ cancelPendingZoom();
348
+ if (spanId === activeSpanId) {
349
+ clearActiveSpan();
350
+ return;
351
+ }
352
+ setActiveSpan(spanId);
353
+ focusViewportOnSpan(spanId);
354
+ },
355
+ [
356
+ cancelPendingZoom,
357
+ activeSpanId,
358
+ clearActiveSpan,
359
+ setActiveSpan,
360
+ focusViewportOnSpan,
361
+ ]
362
+ );
363
+
364
+ const navigateToSpan = useCallback(
365
+ (spanId: string) => {
366
+ setActiveSpan(spanId);
367
+ scrollSpanIntoView(spanId);
368
+ cancelPendingZoom();
369
+ zoomTimerRef.current = setTimeout(() => {
370
+ zoomTimerRef.current = null;
371
+ focusViewportOnSpan(spanId);
372
+ }, ZOOM_DEBOUNCE_MS);
373
+ },
374
+ [setActiveSpan, scrollSpanIntoView, cancelPendingZoom, focusViewportOnSpan]
375
+ );
376
+
263
377
  const [altHeld, setAltHeld] = useState(false);
264
378
 
265
379
  useEffect(() => {
266
380
  const onKeyDown = (e: KeyboardEvent): void => {
267
381
  if (e.key === 'Escape') {
268
- clearActiveSpan();
269
- }
270
- if (e.key === 'Alt') {
271
- e.preventDefault();
382
+ handleClearActiveSpan();
383
+ } else if (e.key === 'Alt') {
272
384
  setAltHeld(true);
273
385
  }
274
386
  };
@@ -285,29 +397,31 @@ function NewTraceViewerContent({ trace }: NewTraceViewerProps): ReactNode {
285
397
  window.removeEventListener('keyup', onKeyUp);
286
398
  window.removeEventListener('blur', onBlur);
287
399
  };
288
- }, [clearActiveSpan]);
400
+ }, [handleClearActiveSpan]);
289
401
 
290
402
  const timelineRef = useRef<HTMLDivElement>(null);
291
403
  const [hoverFraction, setHoverFraction] = useState<number | null>(null);
292
404
 
293
405
  const hoverInfo = useMemo(() => {
294
406
  if (hoverFraction == null) return null;
295
- const absTime = compression.fromVisual(hoverFraction);
407
+ const absTime = viewport.start + hoverFraction * viewDuration;
296
408
  const offset = absTime - root.startTime;
297
- return { fraction: hoverFraction, label: formatDuration(offset, true) };
298
- }, [hoverFraction, compression, root.startTime]);
409
+ return { fraction: hoverFraction, label: formatDurationPrecise(offset) };
410
+ }, [hoverFraction, viewport.start, viewDuration, root.startTime]);
299
411
 
300
412
  const handleTimelineMouseMove = useCallback(
301
413
  (e: React.MouseEvent<HTMLDivElement>) => {
302
414
  const el = timelineRef.current;
303
415
  if (!el) return;
304
416
  const rect = el.getBoundingClientRect();
305
- const padding = 8;
306
- const contentWidth = rect.width - padding * 2;
417
+ const contentWidth = rect.width - TIMELINE_PADDING_PX * 2;
307
418
  if (contentWidth <= 0) return;
308
419
  const fraction = Math.max(
309
420
  0,
310
- Math.min(1, (e.clientX - rect.left - padding) / contentWidth)
421
+ Math.min(
422
+ 1,
423
+ (e.clientX - rect.left - TIMELINE_PADDING_PX) / contentWidth
424
+ )
311
425
  );
312
426
  setHoverFraction(fraction);
313
427
  },
@@ -320,12 +434,7 @@ function NewTraceViewerContent({ trace }: NewTraceViewerProps): ReactNode {
320
434
 
321
435
  useEffect(() => {
322
436
  const el = timelineRef.current;
323
- if (!el) return;
324
-
325
- const rootS = root.startTime;
326
- const rootE = root.startTime + root.duration;
327
- const rootD = root.duration;
328
- if (rootD <= 0) return;
437
+ if (!el || root.duration <= 0) return;
329
438
 
330
439
  const onWheel = (e: WheelEvent): void => {
331
440
  const isZoomGesture = e.ctrlKey || e.metaKey;
@@ -335,111 +444,71 @@ function NewTraceViewerContent({ trace }: NewTraceViewerProps): ReactNode {
335
444
  e.preventDefault();
336
445
 
337
446
  const rect = el.getBoundingClientRect();
338
- const contentWidth = rect.width - TIMELINE_PADDING * 2;
447
+ const contentWidth = rect.width - TIMELINE_PADDING_PX * 2;
339
448
  if (contentWidth <= 0) return;
340
449
 
341
450
  if (isZoomGesture) {
342
- let dy = e.deltaY;
343
- if (e.deltaMode === 1) dy *= 16;
344
-
345
451
  const cursorFraction = Math.max(
346
452
  0,
347
- Math.min(1, (e.clientX - rect.left - TIMELINE_PADDING) / contentWidth)
453
+ Math.min(
454
+ 1,
455
+ (e.clientX - rect.left - TIMELINE_PADDING_PX) / contentWidth
456
+ )
348
457
  );
349
- const scaleFactor = Math.pow(2, dy / 200);
458
+ const scaleFactor = wheelZoomScaleFactor(e);
350
459
 
351
460
  setViewport((prev) => {
352
461
  const prevDuration = prev.end - prev.start;
353
462
  const cursorTime = prev.start + cursorFraction * prevDuration;
463
+ // Clamp the duration before anchoring so the cursor keeps its
464
+ // fraction even when the zoom hits the min/max bounds.
354
465
  const newDuration = Math.max(
355
466
  MIN_VIEWPORT_MS,
356
- Math.min(rootD, prevDuration * scaleFactor)
467
+ Math.min(root.duration, prevDuration * scaleFactor)
357
468
  );
358
-
359
- let newStart = cursorTime - cursorFraction * newDuration;
360
- let newEnd = newStart + newDuration;
361
-
362
- if (newStart < rootS) {
363
- newStart = rootS;
364
- newEnd = rootS + newDuration;
365
- }
366
- if (newEnd > rootE) {
367
- newEnd = rootE;
368
- newStart = Math.max(rootS, rootE - newDuration);
369
- }
370
-
371
- return { start: newStart, end: newEnd };
469
+ return clampToRoot({
470
+ start: cursorTime - cursorFraction * newDuration,
471
+ end: cursorTime + (1 - cursorFraction) * newDuration,
472
+ });
372
473
  });
373
474
  } else {
374
- let dx = e.deltaX;
375
- if (e.deltaMode === 1) dx *= 16;
376
-
475
+ const dx = wheelDeltaToPixels(e.deltaX, e.deltaMode);
377
476
  setViewport((prev) => {
378
- const prevDuration = prev.end - prev.start;
379
- const panAmount = (dx / contentWidth) * prevDuration;
380
-
381
- let newStart = prev.start + panAmount;
382
- let newEnd = prev.end + panAmount;
383
-
384
- if (newStart < rootS) {
385
- newStart = rootS;
386
- newEnd = rootS + prevDuration;
387
- }
388
- if (newEnd > rootE) {
389
- newEnd = rootE;
390
- newStart = Math.max(rootS, rootE - prevDuration);
391
- }
392
-
393
- return { start: newStart, end: newEnd };
477
+ const panAmount = (dx / contentWidth) * (prev.end - prev.start);
478
+ return clampToRoot({
479
+ start: prev.start + panAmount,
480
+ end: prev.end + panAmount,
481
+ });
394
482
  });
395
483
  }
396
484
  };
397
485
 
398
486
  el.addEventListener('wheel', onWheel, { passive: false });
399
487
  return () => el.removeEventListener('wheel', onWheel);
400
- }, [root.startTime, root.duration]);
401
-
402
- // Derive the selected span name and metadata for the panel header
403
- const selectedSpanName = useMemo(() => {
404
- if (!selectedSpan?.data) return 'Details';
405
- const data = selectedSpan.data as Record<string, unknown>;
406
- const stepName = data.stepName as string | undefined;
407
- const workflowName = data.workflowName as string | undefined;
408
- return (
409
- (stepName ? parseStepName(stepName)?.shortName : undefined) ??
410
- (workflowName ? parseWorkflowName(workflowName)?.shortName : undefined) ??
411
- stepName ??
412
- workflowName ??
413
- (data.hookId as string) ??
414
- 'Details'
415
- );
416
- }, [selectedSpan?.data]);
417
-
418
- const selectedResource = selectedSpan?.resource as string | undefined;
419
- const selectedResourceId = useMemo(() => {
420
- if (!selectedSpan?.data) return undefined;
421
- const data = selectedSpan.data as Record<string, unknown>;
422
- return (
423
- (data.stepId as string) ??
424
- (data.runId as string) ??
425
- (data.hookId as string) ??
426
- selectedSpan.spanId
427
- );
428
- }, [selectedSpan?.data, selectedSpan?.spanId]);
488
+ }, [root.duration, setViewport, clampToRoot]);
429
489
 
430
490
  return (
431
491
  <div
492
+ ref={paneRootRef}
432
493
  data-pane="pane-root"
433
- data-has-detail={activeSpan ? '' : undefined}
434
- className="grid w-full h-full max-h-full grid-cols-[minmax(100px,1fr)] data-[has-detail]:grid-cols-[minmax(100px,1fr)_clamp(280px,420px,100%)]"
494
+ className="relative flex w-full h-full max-h-full"
435
495
  >
436
496
  <div
437
497
  id="trace-parent"
438
- className="grid grid-rows-[1fr] h-full min-h-0 overflow-hidden relative bg-background-100"
498
+ className="flex-1 min-w-0 grid grid-rows-[auto_1fr] h-full min-h-0 overflow-hidden relative bg-background-100"
439
499
  >
500
+ <Minimap
501
+ spans={trace.spans}
502
+ root={root}
503
+ viewport={viewport}
504
+ minViewportMs={MIN_VIEWPORT_MS}
505
+ onViewportChange={setViewport}
506
+ onAnimateTo={animateTo}
507
+ />
440
508
  <SplitPane
509
+ scrollContainerRef={scrollContainerRef}
441
510
  startHeader={
442
- <div className="bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-center px-2 gap-1.5">
511
+ <div className="bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-center pl-4 pr-2 gap-1.5">
443
512
  <Search className="w-3.5 h-3.5 shrink-0 text-gray-800" />
444
513
  <input
445
514
  id="trace-viewer-search"
@@ -447,6 +516,13 @@ function NewTraceViewerContent({ trace }: NewTraceViewerProps): ReactNode {
447
516
  type="text"
448
517
  value={searchQuery}
449
518
  onChange={(e) => setSearchQuery(e.target.value)}
519
+ onKeyDown={(e) => {
520
+ if (e.key === 'Escape' && searchQuery) {
521
+ e.preventDefault();
522
+ e.stopPropagation();
523
+ setSearchQuery('');
524
+ }
525
+ }}
450
526
  placeholder="Search spans..."
451
527
  aria-label="Search spans"
452
528
  className="flex-1 min-w-0 bg-transparent text-sm text-gray-1000 placeholder:text-gray-800 outline-none"
@@ -454,132 +530,99 @@ function NewTraceViewerContent({ trace }: NewTraceViewerProps): ReactNode {
454
530
  {searchQuery && (
455
531
  <button
456
532
  type="button"
533
+ aria-label="Clear search"
457
534
  onClick={() => setSearchQuery('')}
458
- className="shrink-0 p-0.5 rounded-sm text-gray-800 hover:text-gray-1000 hover:bg-gray-200 transition-colors"
535
+ className="-mr-2 hidden h-full max-w-full shrink-0 cursor-pointer items-center rounded-r-md border-0 bg-transparent px-2.5 font-inherit text-base text-gray-900 no-underline transition-colors duration-150 ease-in hover:text-gray-1000 focus-visible:-outline-offset-1 focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--ds-focus-color)] min-[961px]:flex"
459
536
  >
460
- <X className="w-3 h-3" />
537
+ <Kbd variant="outline" size="search">
538
+ Esc
539
+ </Kbd>
461
540
  </button>
462
541
  )}
463
542
  </div>
464
543
  }
465
544
  endHeader={
466
- <TimelineHeader
467
- viewStart={viewport.start}
468
- viewDuration={viewDuration}
469
- rootStart={root.startTime}
470
- compression={compression}
471
- isZoomed={isZoomed}
472
- onResetZoom={resetZoom}
473
- hoverInfo={hoverInfo}
474
- />
545
+ <TimelineHeader markers={timeMarkers} hoverInfo={hoverInfo} />
475
546
  }
476
547
  >
477
548
  <div className="block overflow-visible">
478
549
  <EventList
479
- spans={filteredSpans}
550
+ spans={trace.spans}
480
551
  activeSpanId={activeSpanId}
552
+ searchResult={searchResult}
481
553
  onSelectSpan={handleSelectSpan}
482
554
  />
555
+ <div ref={loadMoreSentinelRef} className="flex justify-center">
556
+ {isLoadingMore ? (
557
+ <div className="flex items-center justify-center gap-2 py-3 text-sm text-gray-800">
558
+ <Spinner size={14} />
559
+ <span>Loading spans…</span>
560
+ </div>
561
+ ) : null}
562
+ </div>
483
563
  </div>
564
+ {/* biome-ignore lint/a11y/noStaticElementInteractions: timeline hover and wheel gestures are pointer-only annotations */}
484
565
  <div
485
566
  ref={timelineRef}
486
- className="block min-h-0 overflow-visible relative px-2"
567
+ id="trace-timeline"
568
+ className="@container block min-h-0 overflow-visible relative"
487
569
  onDoubleClick={resetZoom}
488
570
  onMouseMove={handleTimelineMouseMove}
489
571
  onMouseLeave={handleTimelineMouseLeave}
490
572
  >
491
573
  <Timeline
492
- spans={filteredSpans}
493
- compression={compression}
574
+ spans={trace.spans}
575
+ viewStart={viewport.start}
576
+ viewEnd={viewport.end}
577
+ markers={timeMarkers}
494
578
  selectedId={activeSpanId}
579
+ searchResult={searchResult}
495
580
  onSelect={handleSelectSpan}
581
+ onRevealTime={handleRevealTime}
496
582
  hoverFraction={hoverFraction}
497
583
  altHeld={altHeld}
498
584
  />
585
+ <TraceShortcutHelper
586
+ hasMultipleSpans={trace.spans.length > 1}
587
+ reducedMotion={reducedMotion}
588
+ />
499
589
  </div>
500
590
  </SplitPane>
591
+ <div className="absolute right-3 bottom-3 z-[5] flex items-center border border-gray-alpha-400 rounded-md bg-background-100 shadow-sm overflow-hidden divide-x divide-gray-alpha-400">
592
+ <IconButton
593
+ variant="muted"
594
+ size="small"
595
+ onClick={zoomOut}
596
+ disabled={isAtMinZoom}
597
+ aria-label="Zoom out"
598
+ >
599
+ <ZoomOut className="w-4 h-4" />
600
+ </IconButton>
601
+ <IconButton
602
+ variant="muted"
603
+ size="small"
604
+ onClick={resetZoom}
605
+ aria-label="Reset zoom"
606
+ >
607
+ <RotateCcw className="w-3.5 h-3.5" />
608
+ </IconButton>
609
+ <IconButton
610
+ variant="muted"
611
+ size="small"
612
+ onClick={zoomIn}
613
+ disabled={isAtMaxZoom}
614
+ aria-label="Zoom in"
615
+ >
616
+ <ZoomIn className="w-4 h-4" />
617
+ </IconButton>
618
+ </div>
501
619
  </div>
502
620
 
503
- {/* Detail panel */}
504
- {activeSpan && sidebar ? (
505
- <aside className="flex flex-col h-full max-h-full bg-background-100 border-l border-gray-alpha-400 overflow-auto">
506
- {/* Panel header */}
507
- <div className="flex-shrink-0 px-4 pt-4 pb-3">
508
- <div className="flex items-start justify-between gap-2">
509
- <div className="min-w-0 flex-1">
510
- <span className="text-[15px] font-semibold text-gray-1000 truncate block">
511
- {selectedSpanName}
512
- </span>
513
- {selectedResourceId && (
514
- <div className="mt-1 flex items-center gap-2">
515
- {selectedResource && (
516
- <span
517
- className={`inline-flex items-center rounded-md px-1.5 py-0.5 text-[11px] font-medium leading-none shrink-0 ${
518
- selectedResource === 'step'
519
- ? 'bg-green-200 text-green-900'
520
- : selectedResource === 'run'
521
- ? 'bg-blue-200 text-blue-900'
522
- : selectedResource === 'hook'
523
- ? 'bg-yellow-200 text-yellow-900'
524
- : 'bg-gray-200 text-gray-900'
525
- }`}
526
- >
527
- {selectedResource.charAt(0).toUpperCase() +
528
- selectedResource.slice(1)}
529
- </span>
530
- )}
531
- <div
532
- className="flex items-center gap-1 text-[13px] font-mono text-gray-700 min-w-0"
533
- title={selectedResourceId}
534
- >
535
- <span className="truncate">{selectedResourceId}</span>
536
- <CopyButton
537
- copyText={selectedResourceId}
538
- ariaLabel="Copy ID"
539
- className="shrink-0"
540
- />
541
- </div>
542
- </div>
543
- )}
544
- </div>
545
- <button
546
- type="button"
547
- className="p-1 rounded-md text-gray-900 hover:text-gray-1000 hover:bg-gray-alpha-200 transition-colors shrink-0"
548
- onClick={clearActiveSpan}
549
- >
550
- <X className="w-4 h-4" />
551
- </button>
552
- </div>
553
- </div>
554
- {/* Panel body */}
555
- <div className="flex-1 overflow-y-auto">
556
- <ErrorBoundary>
557
- <EntityDetailPanel
558
- run={sidebar.run}
559
- onStreamClick={sidebar.onStreamClick}
560
- onRunClick={sidebar.onRunClick}
561
- spanDetailData={sidebar.spanDetailData}
562
- spanDetailError={sidebar.spanDetailError}
563
- spanDetailLoading={sidebar.spanDetailLoading}
564
- onSpanSelect={sidebar.onSpanSelect}
565
- onWakeUpSleep={sidebar.onWakeUpSleep}
566
- onLoadEventData={sidebar.onLoadEventData}
567
- onResolveHook={sidebar.onResolveHook}
568
- encryptionKey={sidebar.encryptionKey}
569
- onDecrypt={sidebar.onDecrypt}
570
- isDecrypting={sidebar.isDecrypting}
571
- selectedSpan={selectedSpan}
572
- />
573
- </ErrorBoundary>
574
- </div>
575
- </aside>
576
- ) : activeSpan ? (
577
- <DetailPanel
578
- span={activeSpan}
579
- rootStart={root.startTime}
580
- onClose={clearActiveSpan}
581
- />
582
- ) : null}
621
+ <TraceDetailPanel
622
+ containerRef={paneRootRef}
623
+ onNavigateToSpan={navigateToSpan}
624
+ onClose={handleClearActiveSpan}
625
+ />
583
626
  </div>
584
627
  );
585
628
  }