@workflow/web-shared 4.1.0-beta.47 → 4.1.0-beta.49

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 (301) hide show
  1. package/README.md +26 -52
  2. package/dist/components/error-boundary.d.ts.map +1 -0
  3. package/dist/{error-boundary.js → components/error-boundary.js} +1 -1
  4. package/dist/components/error-boundary.js.map +1 -0
  5. package/dist/{event-list-view.d.ts → components/event-list-view.d.ts} +2 -3
  6. package/dist/components/event-list-view.d.ts.map +1 -0
  7. package/dist/{event-list-view.js → components/event-list-view.js} +9 -17
  8. package/dist/components/event-list-view.js.map +1 -0
  9. package/dist/{hook-actions.d.ts → components/hook-actions.d.ts} +2 -3
  10. package/dist/components/hook-actions.d.ts.map +1 -0
  11. package/dist/{hook-actions.js → components/hook-actions.js} +3 -4
  12. package/dist/components/hook-actions.js.map +1 -0
  13. package/dist/components/index.d.ts +10 -0
  14. package/dist/components/index.d.ts.map +1 -0
  15. package/dist/components/index.js +8 -0
  16. package/dist/components/index.js.map +1 -0
  17. package/dist/components/run-trace-view.d.ts +22 -0
  18. package/dist/components/run-trace-view.d.ts.map +1 -0
  19. package/dist/components/run-trace-view.js +11 -0
  20. package/dist/components/run-trace-view.js.map +1 -0
  21. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -0
  22. package/dist/{sidebar → components/sidebar}/attribute-panel.js +3 -3
  23. package/dist/components/sidebar/attribute-panel.js.map +1 -0
  24. package/dist/components/sidebar/conversation-view.d.ts.map +1 -0
  25. package/dist/components/sidebar/conversation-view.js.map +1 -0
  26. package/dist/components/sidebar/detail-card.d.ts.map +1 -0
  27. package/dist/components/sidebar/detail-card.js.map +1 -0
  28. package/dist/components/sidebar/entity-detail-panel.d.ts +32 -0
  29. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -0
  30. package/dist/{sidebar → components/sidebar}/entity-detail-panel.js +61 -49
  31. package/dist/components/sidebar/entity-detail-panel.js.map +1 -0
  32. package/dist/components/sidebar/events-list.d.ts +8 -0
  33. package/dist/components/sidebar/events-list.d.ts.map +1 -0
  34. package/dist/components/sidebar/events-list.js +16 -0
  35. package/dist/components/sidebar/events-list.js.map +1 -0
  36. package/dist/components/sidebar/resolve-hook-modal.d.ts.map +1 -0
  37. package/dist/components/sidebar/resolve-hook-modal.js.map +1 -0
  38. package/dist/components/stream-viewer.d.ts +18 -0
  39. package/dist/components/stream-viewer.d.ts.map +1 -0
  40. package/dist/{stream-viewer.js → components/stream-viewer.js} +1 -59
  41. package/dist/components/stream-viewer.js.map +1 -0
  42. package/dist/components/trace-viewer/components/map.d.ts.map +1 -0
  43. package/dist/components/trace-viewer/components/map.js.map +1 -0
  44. package/dist/components/trace-viewer/components/markers.d.ts.map +1 -0
  45. package/dist/components/trace-viewer/components/markers.js.map +1 -0
  46. package/dist/components/trace-viewer/components/node.d.ts.map +1 -0
  47. package/dist/components/trace-viewer/components/node.js.map +1 -0
  48. package/dist/components/trace-viewer/components/search-input.d.ts.map +1 -0
  49. package/dist/components/trace-viewer/components/search-input.js.map +1 -0
  50. package/dist/components/trace-viewer/components/search.d.ts.map +1 -0
  51. package/dist/components/trace-viewer/components/search.js.map +1 -0
  52. package/dist/components/trace-viewer/components/span-detail-panel.d.ts.map +1 -0
  53. package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -0
  54. package/dist/components/trace-viewer/components/ui.d.ts.map +1 -0
  55. package/dist/components/trace-viewer/components/ui.js.map +1 -0
  56. package/dist/components/trace-viewer/components/zoom-button.d.ts.map +1 -0
  57. package/dist/components/trace-viewer/components/zoom-button.js.map +1 -0
  58. package/dist/components/trace-viewer/components/zoom-icons.d.ts.map +1 -0
  59. package/dist/components/trace-viewer/components/zoom-icons.js.map +1 -0
  60. package/dist/components/trace-viewer/context.d.ts.map +1 -0
  61. package/dist/components/trace-viewer/context.js.map +1 -0
  62. package/dist/components/trace-viewer/index.d.ts.map +1 -0
  63. package/dist/components/trace-viewer/index.js.map +1 -0
  64. package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -0
  65. package/dist/components/trace-viewer/trace-viewer.js.map +1 -0
  66. package/dist/components/trace-viewer/types.d.ts.map +1 -0
  67. package/dist/components/trace-viewer/types.js.map +1 -0
  68. package/dist/components/trace-viewer/util/constants.d.ts.map +1 -0
  69. package/dist/components/trace-viewer/util/constants.js.map +1 -0
  70. package/dist/components/trace-viewer/util/scrollbar-width.d.ts.map +1 -0
  71. package/dist/components/trace-viewer/util/scrollbar-width.js.map +1 -0
  72. package/dist/{trace-viewer → components/trace-viewer}/util/timing.d.ts +1 -1
  73. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -0
  74. package/dist/{trace-viewer → components/trace-viewer}/util/timing.js +1 -1
  75. package/dist/components/trace-viewer/util/timing.js.map +1 -0
  76. package/dist/components/trace-viewer/util/tree.d.ts.map +1 -0
  77. package/dist/components/trace-viewer/util/tree.js.map +1 -0
  78. package/dist/components/trace-viewer/util/use-immediate-style.d.ts.map +1 -0
  79. package/dist/components/trace-viewer/util/use-immediate-style.js.map +1 -0
  80. package/dist/components/trace-viewer/util/use-streaming-spans.d.ts.map +1 -0
  81. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -0
  82. package/dist/components/trace-viewer/util/use-trackpad-zoom.d.ts.map +1 -0
  83. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +1 -0
  84. package/dist/components/trace-viewer/worker.d.ts.map +1 -0
  85. package/dist/components/trace-viewer/worker.js.map +1 -0
  86. package/dist/components/workflow-trace-view.d.ts +24 -0
  87. package/dist/components/workflow-trace-view.d.ts.map +1 -0
  88. package/dist/components/workflow-trace-view.js +152 -0
  89. package/dist/components/workflow-trace-view.js.map +1 -0
  90. package/dist/components/workflow-traces/event-colors.d.ts.map +1 -0
  91. package/dist/components/workflow-traces/event-colors.js.map +1 -0
  92. package/dist/components/workflow-traces/trace-colors.d.ts.map +1 -0
  93. package/dist/components/workflow-traces/trace-colors.js.map +1 -0
  94. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -0
  95. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -0
  96. package/dist/components/workflow-traces/trace-time-utils.d.ts.map +1 -0
  97. package/dist/components/workflow-traces/trace-time-utils.js.map +1 -0
  98. package/dist/index.d.ts +3 -13
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +2 -9
  101. package/dist/index.js.map +1 -1
  102. package/package.json +15 -11
  103. package/src/components/error-boundary.tsx +79 -0
  104. package/src/components/event-list-view.tsx +429 -0
  105. package/src/components/hook-actions.tsx +167 -0
  106. package/src/components/index.d.ts +1 -0
  107. package/src/components/index.ts +23 -0
  108. package/src/components/run-trace-view.tsx +75 -0
  109. package/src/components/sidebar/attribute-panel.tsx +938 -0
  110. package/src/components/sidebar/conversation-view.tsx +235 -0
  111. package/src/components/sidebar/detail-card.tsx +43 -0
  112. package/src/components/sidebar/entity-detail-panel.tsx +338 -0
  113. package/src/components/sidebar/events-list.tsx +119 -0
  114. package/src/components/sidebar/resolve-hook-modal.tsx +219 -0
  115. package/src/components/stream-viewer.tsx +143 -0
  116. package/src/components/trace-viewer/components/map.tsx +226 -0
  117. package/src/components/trace-viewer/components/markers.tsx +564 -0
  118. package/src/components/trace-viewer/components/node.tsx +259 -0
  119. package/src/components/trace-viewer/components/search-input.tsx +52 -0
  120. package/src/components/trace-viewer/components/search.tsx +47 -0
  121. package/src/components/trace-viewer/components/span-detail-panel.tsx +650 -0
  122. package/src/components/trace-viewer/components/ui.tsx +156 -0
  123. package/src/components/trace-viewer/components/zoom-button.tsx +61 -0
  124. package/src/components/trace-viewer/components/zoom-icons.tsx +65 -0
  125. package/src/components/trace-viewer/context.tsx +633 -0
  126. package/src/components/trace-viewer/index.tsx +4 -0
  127. package/src/components/trace-viewer/modules.d.ts +16 -0
  128. package/src/components/trace-viewer/trace-viewer.module.css +1292 -0
  129. package/src/components/trace-viewer/trace-viewer.tsx +448 -0
  130. package/src/components/trace-viewer/types.ts +234 -0
  131. package/src/components/trace-viewer/util/constants.ts +8 -0
  132. package/src/components/trace-viewer/util/scrollbar-width.ts +13 -0
  133. package/src/components/trace-viewer/util/timing.ts +33 -0
  134. package/src/components/trace-viewer/util/tree.ts +277 -0
  135. package/src/components/trace-viewer/util/use-immediate-style.ts +38 -0
  136. package/src/components/trace-viewer/util/use-streaming-spans.ts +415 -0
  137. package/src/components/trace-viewer/util/use-trackpad-zoom.tsx +51 -0
  138. package/src/components/trace-viewer/worker.ts +128 -0
  139. package/src/components/ui/alert.tsx +59 -0
  140. package/src/components/ui/card.tsx +88 -0
  141. package/src/components/ui/error-card.tsx +65 -0
  142. package/src/components/ui/skeleton.tsx +15 -0
  143. package/src/components/workflow-trace-view.tsx +306 -0
  144. package/src/components/workflow-traces/event-colors.ts +94 -0
  145. package/src/components/workflow-traces/trace-colors.ts +112 -0
  146. package/src/components/workflow-traces/trace-span-construction.ts +299 -0
  147. package/src/components/workflow-traces/trace-time-utils.ts +50 -0
  148. package/src/hooks/use-dark-mode.ts +34 -0
  149. package/src/index.d.ts +1 -0
  150. package/src/index.ts +29 -0
  151. package/src/lib/event-analysis.ts +231 -0
  152. package/src/lib/utils.ts +166 -0
  153. package/dist/api/workflow-api-client.d.ts +0 -543
  154. package/dist/api/workflow-api-client.d.ts.map +0 -1
  155. package/dist/api/workflow-api-client.js +0 -953
  156. package/dist/api/workflow-api-client.js.map +0 -1
  157. package/dist/api/workflow-server-actions.d.ts +0 -230
  158. package/dist/api/workflow-server-actions.d.ts.map +0 -1
  159. package/dist/api/workflow-server-actions.js +0 -861
  160. package/dist/api/workflow-server-actions.js.map +0 -1
  161. package/dist/error-boundary.d.ts.map +0 -1
  162. package/dist/error-boundary.js.map +0 -1
  163. package/dist/event-list-view.d.ts.map +0 -1
  164. package/dist/event-list-view.js.map +0 -1
  165. package/dist/hook-actions.d.ts.map +0 -1
  166. package/dist/hook-actions.js.map +0 -1
  167. package/dist/run-trace-view.d.ts +0 -8
  168. package/dist/run-trace-view.d.ts.map +0 -1
  169. package/dist/run-trace-view.js +0 -15
  170. package/dist/run-trace-view.js.map +0 -1
  171. package/dist/sidebar/attribute-panel.d.ts.map +0 -1
  172. package/dist/sidebar/attribute-panel.js.map +0 -1
  173. package/dist/sidebar/conversation-view.d.ts.map +0 -1
  174. package/dist/sidebar/conversation-view.js.map +0 -1
  175. package/dist/sidebar/detail-card.d.ts.map +0 -1
  176. package/dist/sidebar/detail-card.js.map +0 -1
  177. package/dist/sidebar/entity-detail-panel.d.ts +0 -12
  178. package/dist/sidebar/entity-detail-panel.d.ts.map +0 -1
  179. package/dist/sidebar/entity-detail-panel.js.map +0 -1
  180. package/dist/sidebar/events-list.d.ts +0 -9
  181. package/dist/sidebar/events-list.d.ts.map +0 -1
  182. package/dist/sidebar/events-list.js +0 -36
  183. package/dist/sidebar/events-list.js.map +0 -1
  184. package/dist/sidebar/resolve-hook-modal.d.ts.map +0 -1
  185. package/dist/sidebar/resolve-hook-modal.js.map +0 -1
  186. package/dist/stream-viewer.d.ts +0 -13
  187. package/dist/stream-viewer.d.ts.map +0 -1
  188. package/dist/stream-viewer.js.map +0 -1
  189. package/dist/trace-viewer/components/map.d.ts.map +0 -1
  190. package/dist/trace-viewer/components/map.js.map +0 -1
  191. package/dist/trace-viewer/components/markers.d.ts.map +0 -1
  192. package/dist/trace-viewer/components/markers.js.map +0 -1
  193. package/dist/trace-viewer/components/node.d.ts.map +0 -1
  194. package/dist/trace-viewer/components/node.js.map +0 -1
  195. package/dist/trace-viewer/components/search-input.d.ts.map +0 -1
  196. package/dist/trace-viewer/components/search-input.js.map +0 -1
  197. package/dist/trace-viewer/components/search.d.ts.map +0 -1
  198. package/dist/trace-viewer/components/search.js.map +0 -1
  199. package/dist/trace-viewer/components/span-detail-panel.d.ts.map +0 -1
  200. package/dist/trace-viewer/components/span-detail-panel.js.map +0 -1
  201. package/dist/trace-viewer/components/ui.d.ts.map +0 -1
  202. package/dist/trace-viewer/components/ui.js.map +0 -1
  203. package/dist/trace-viewer/components/zoom-button.d.ts.map +0 -1
  204. package/dist/trace-viewer/components/zoom-button.js.map +0 -1
  205. package/dist/trace-viewer/components/zoom-icons.d.ts.map +0 -1
  206. package/dist/trace-viewer/components/zoom-icons.js.map +0 -1
  207. package/dist/trace-viewer/context.d.ts.map +0 -1
  208. package/dist/trace-viewer/context.js.map +0 -1
  209. package/dist/trace-viewer/index.d.ts.map +0 -1
  210. package/dist/trace-viewer/index.js.map +0 -1
  211. package/dist/trace-viewer/trace-viewer.d.ts.map +0 -1
  212. package/dist/trace-viewer/trace-viewer.js.map +0 -1
  213. package/dist/trace-viewer/types.d.ts.map +0 -1
  214. package/dist/trace-viewer/types.js.map +0 -1
  215. package/dist/trace-viewer/util/constants.d.ts.map +0 -1
  216. package/dist/trace-viewer/util/constants.js.map +0 -1
  217. package/dist/trace-viewer/util/scrollbar-width.d.ts.map +0 -1
  218. package/dist/trace-viewer/util/scrollbar-width.js.map +0 -1
  219. package/dist/trace-viewer/util/timing.d.ts.map +0 -1
  220. package/dist/trace-viewer/util/timing.js.map +0 -1
  221. package/dist/trace-viewer/util/tree.d.ts.map +0 -1
  222. package/dist/trace-viewer/util/tree.js.map +0 -1
  223. package/dist/trace-viewer/util/use-immediate-style.d.ts.map +0 -1
  224. package/dist/trace-viewer/util/use-immediate-style.js.map +0 -1
  225. package/dist/trace-viewer/util/use-streaming-spans.d.ts.map +0 -1
  226. package/dist/trace-viewer/util/use-streaming-spans.js.map +0 -1
  227. package/dist/trace-viewer/util/use-trackpad-zoom.d.ts.map +0 -1
  228. package/dist/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
  229. package/dist/trace-viewer/worker.d.ts.map +0 -1
  230. package/dist/trace-viewer/worker.js.map +0 -1
  231. package/dist/workflow-trace-view.d.ts +0 -14
  232. package/dist/workflow-trace-view.d.ts.map +0 -1
  233. package/dist/workflow-trace-view.js +0 -135
  234. package/dist/workflow-trace-view.js.map +0 -1
  235. package/dist/workflow-traces/event-colors.d.ts.map +0 -1
  236. package/dist/workflow-traces/event-colors.js.map +0 -1
  237. package/dist/workflow-traces/trace-colors.d.ts.map +0 -1
  238. package/dist/workflow-traces/trace-colors.js.map +0 -1
  239. package/dist/workflow-traces/trace-span-construction.d.ts.map +0 -1
  240. package/dist/workflow-traces/trace-span-construction.js.map +0 -1
  241. package/dist/workflow-traces/trace-time-utils.d.ts.map +0 -1
  242. package/dist/workflow-traces/trace-time-utils.js.map +0 -1
  243. package/server/README.md +0 -1
  244. package/server/package.json +0 -4
  245. /package/dist/{error-boundary.d.ts → components/error-boundary.d.ts} +0 -0
  246. /package/dist/{sidebar → components/sidebar}/attribute-panel.d.ts +0 -0
  247. /package/dist/{sidebar → components/sidebar}/conversation-view.d.ts +0 -0
  248. /package/dist/{sidebar → components/sidebar}/conversation-view.js +0 -0
  249. /package/dist/{sidebar → components/sidebar}/detail-card.d.ts +0 -0
  250. /package/dist/{sidebar → components/sidebar}/detail-card.js +0 -0
  251. /package/dist/{sidebar → components/sidebar}/resolve-hook-modal.d.ts +0 -0
  252. /package/dist/{sidebar → components/sidebar}/resolve-hook-modal.js +0 -0
  253. /package/dist/{trace-viewer → components/trace-viewer}/components/map.d.ts +0 -0
  254. /package/dist/{trace-viewer → components/trace-viewer}/components/map.js +0 -0
  255. /package/dist/{trace-viewer → components/trace-viewer}/components/markers.d.ts +0 -0
  256. /package/dist/{trace-viewer → components/trace-viewer}/components/markers.js +0 -0
  257. /package/dist/{trace-viewer → components/trace-viewer}/components/node.d.ts +0 -0
  258. /package/dist/{trace-viewer → components/trace-viewer}/components/node.js +0 -0
  259. /package/dist/{trace-viewer → components/trace-viewer}/components/search-input.d.ts +0 -0
  260. /package/dist/{trace-viewer → components/trace-viewer}/components/search-input.js +0 -0
  261. /package/dist/{trace-viewer → components/trace-viewer}/components/search.d.ts +0 -0
  262. /package/dist/{trace-viewer → components/trace-viewer}/components/search.js +0 -0
  263. /package/dist/{trace-viewer → components/trace-viewer}/components/span-detail-panel.d.ts +0 -0
  264. /package/dist/{trace-viewer → components/trace-viewer}/components/span-detail-panel.js +0 -0
  265. /package/dist/{trace-viewer → components/trace-viewer}/components/ui.d.ts +0 -0
  266. /package/dist/{trace-viewer → components/trace-viewer}/components/ui.js +0 -0
  267. /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-button.d.ts +0 -0
  268. /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-button.js +0 -0
  269. /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-icons.d.ts +0 -0
  270. /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-icons.js +0 -0
  271. /package/dist/{trace-viewer → components/trace-viewer}/context.d.ts +0 -0
  272. /package/dist/{trace-viewer → components/trace-viewer}/context.js +0 -0
  273. /package/dist/{trace-viewer → components/trace-viewer}/index.d.ts +0 -0
  274. /package/dist/{trace-viewer → components/trace-viewer}/index.js +0 -0
  275. /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.d.ts +0 -0
  276. /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.js +0 -0
  277. /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.module.css +0 -0
  278. /package/dist/{trace-viewer → components/trace-viewer}/types.d.ts +0 -0
  279. /package/dist/{trace-viewer → components/trace-viewer}/types.js +0 -0
  280. /package/dist/{trace-viewer → components/trace-viewer}/util/constants.d.ts +0 -0
  281. /package/dist/{trace-viewer → components/trace-viewer}/util/constants.js +0 -0
  282. /package/dist/{trace-viewer → components/trace-viewer}/util/scrollbar-width.d.ts +0 -0
  283. /package/dist/{trace-viewer → components/trace-viewer}/util/scrollbar-width.js +0 -0
  284. /package/dist/{trace-viewer → components/trace-viewer}/util/tree.d.ts +0 -0
  285. /package/dist/{trace-viewer → components/trace-viewer}/util/tree.js +0 -0
  286. /package/dist/{trace-viewer → components/trace-viewer}/util/use-immediate-style.d.ts +0 -0
  287. /package/dist/{trace-viewer → components/trace-viewer}/util/use-immediate-style.js +0 -0
  288. /package/dist/{trace-viewer → components/trace-viewer}/util/use-streaming-spans.d.ts +0 -0
  289. /package/dist/{trace-viewer → components/trace-viewer}/util/use-streaming-spans.js +0 -0
  290. /package/dist/{trace-viewer → components/trace-viewer}/util/use-trackpad-zoom.d.ts +0 -0
  291. /package/dist/{trace-viewer → components/trace-viewer}/util/use-trackpad-zoom.js +0 -0
  292. /package/dist/{trace-viewer → components/trace-viewer}/worker.d.ts +0 -0
  293. /package/dist/{trace-viewer → components/trace-viewer}/worker.js +0 -0
  294. /package/dist/{workflow-traces → components/workflow-traces}/event-colors.d.ts +0 -0
  295. /package/dist/{workflow-traces → components/workflow-traces}/event-colors.js +0 -0
  296. /package/dist/{workflow-traces → components/workflow-traces}/trace-colors.d.ts +0 -0
  297. /package/dist/{workflow-traces → components/workflow-traces}/trace-colors.js +0 -0
  298. /package/dist/{workflow-traces → components/workflow-traces}/trace-span-construction.d.ts +0 -0
  299. /package/dist/{workflow-traces → components/workflow-traces}/trace-span-construction.js +0 -0
  300. /package/dist/{workflow-traces → components/workflow-traces}/trace-time-utils.d.ts +0 -0
  301. /package/dist/{workflow-traces → components/workflow-traces}/trace-time-utils.js +0 -0
@@ -0,0 +1,448 @@
1
+ 'use client';
2
+
3
+ import { clsx } from 'clsx';
4
+ import type { CSSProperties, MouseEventHandler, ReactNode } from 'react';
5
+ import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';
6
+ import { MiniMap } from './components/map';
7
+ import { CursorMarker, EventMarkers, Markers } from './components/markers';
8
+ import { SpanNodes } from './components/node';
9
+ import { SearchBar } from './components/search';
10
+ import { SpanDetailPanel } from './components/span-detail-panel';
11
+ import { ZoomButton } from './components/zoom-button';
12
+ import { TraceViewerContextProvider, useTraceViewer } from './context';
13
+ import styles from './trace-viewer.module.css';
14
+ import type { GetQuickLinks, Trace } from './types';
15
+ import {
16
+ MAP_HEIGHT,
17
+ MARKER_HEIGHT,
18
+ MARKER_NOTCH_HEIGHT,
19
+ ROW_HEIGHT,
20
+ ROW_PADDING,
21
+ SEARCH_GAP,
22
+ SEARCH_HEIGHT,
23
+ TIMELINE_PADDING,
24
+ } from './util/constants';
25
+ import { parseTrace } from './util/tree';
26
+ import { useStreamingSpans } from './util/use-streaming-spans';
27
+
28
+ interface TraceViewerProps {
29
+ trace?: Trace;
30
+ className?: string;
31
+ scrollLock?: boolean;
32
+ height?: string | number;
33
+ withPanel?: boolean;
34
+ getQuickLinks?: GetQuickLinks;
35
+ highlightedSpans?: string[];
36
+ }
37
+
38
+ export function TraceViewerProvider({
39
+ getQuickLinks,
40
+ children,
41
+ }: Pick<TraceViewerProps, 'getQuickLinks'> & {
42
+ children: ReactNode;
43
+ }): ReactNode {
44
+ return (
45
+ <TraceViewerContextProvider getQuickLinks={getQuickLinks}>
46
+ {children}
47
+ </TraceViewerContextProvider>
48
+ );
49
+ }
50
+
51
+ interface LastClickRef {
52
+ x: number;
53
+ y: number;
54
+ t: number;
55
+ spanId: string;
56
+ }
57
+
58
+ const skeletonTrace: Trace = {
59
+ traceId: 'skeleton',
60
+ spans: [
61
+ {
62
+ parentSpanId: '',
63
+ spanId: 'root',
64
+ name: 'root span',
65
+ kind: 1,
66
+ resource: 'vercel.runtime',
67
+ startTime: [5000, 0],
68
+ endTime: [6000, 0],
69
+ duration: [1000, 0],
70
+ library: {
71
+ name: 'vercel-site',
72
+ },
73
+ status: {
74
+ code: 1,
75
+ },
76
+ attributes: {
77
+ 'vercel.ownerId': 'team_abc',
78
+ },
79
+ traceFlags: 1,
80
+ events: [],
81
+ links: [],
82
+ },
83
+ ],
84
+ resources: [
85
+ {
86
+ name: 'vercel.runtime',
87
+ attributes: {},
88
+ },
89
+ ],
90
+ rootSpanId: 'root',
91
+ };
92
+
93
+ export function TraceViewerTimeline({
94
+ trace = skeletonTrace,
95
+ className = '',
96
+ scrollLock = false,
97
+ height,
98
+ withPanel = false,
99
+ highlightedSpans,
100
+ }: Omit<TraceViewerProps, 'getQuickLinks'>): ReactNode {
101
+ const isSkeleton = trace === skeletonTrace;
102
+ const { state, dispatch } = useTraceViewer();
103
+ const { timelineRef, scrollSnapshotRef } = state;
104
+ const memoCache = state.memoCacheRef.current;
105
+ const hideSearchBar =
106
+ (highlightedSpans?.length ?? 0) > 0 || trace.spans.length <= 10;
107
+
108
+ useEffect(() => {
109
+ const { root, map: spanMap } = parseTrace(trace);
110
+ dispatch({
111
+ type: 'setRoot',
112
+ root,
113
+ spanMap,
114
+ resources: trace.resources || [],
115
+ });
116
+ }, [dispatch, trace]);
117
+
118
+ const { rows, spans, events, scale } = useStreamingSpans(highlightedSpans);
119
+
120
+ const ref = useRef<HTMLDivElement>(null);
121
+ useLayoutEffect(() => {
122
+ const $el = ref.current;
123
+ if (!$el) return;
124
+
125
+ const onResize = (): void => {
126
+ const padding = 2 * TIMELINE_PADDING;
127
+ const rect = $el.getBoundingClientRect();
128
+
129
+ dispatch({
130
+ type: 'setSize',
131
+ width: rect.width - padding,
132
+ height: rect.height,
133
+ });
134
+ };
135
+
136
+ onResize();
137
+
138
+ const observer = new ResizeObserver(onResize);
139
+ observer.observe($el);
140
+ window.addEventListener('resize', onResize);
141
+
142
+ return () => {
143
+ observer.disconnect();
144
+ window.removeEventListener('resize', onResize);
145
+ };
146
+ }, [dispatch]);
147
+
148
+ const lastClickRef = useRef<LastClickRef>({
149
+ t: 0,
150
+ x: -1,
151
+ y: -1,
152
+ spanId: '',
153
+ });
154
+ const onClick: MouseEventHandler = useCallback(
155
+ (event) => {
156
+ // NOTE(wits): We manually implement double-click logic here so that we can support double
157
+ // clicking a span even if the first click moves the span that the user is clicking on due
158
+ // to the panel opening. If we used a regular double click listener we would need to delay
159
+ // the opening of the panel artificially. This implementation allows us to always be
160
+ // FAST while also avoiding a FRUSTRATING situation.
161
+ const prev = lastClickRef.current;
162
+ const t = Date.now();
163
+ const { clientX: x, clientY: y } = event;
164
+ const d = Math.sqrt((x - prev.x) ** 2 + (y - prev.y) ** 2);
165
+ // double click
166
+ if (t - prev.t <= 500 && d <= 8) {
167
+ event.stopPropagation();
168
+ event.preventDefault();
169
+ if (!prev.spanId) {
170
+ dispatch({
171
+ type: 'resetScale',
172
+ });
173
+ return;
174
+ }
175
+ dispatch({
176
+ type: 'select',
177
+ id: prev.spanId,
178
+ });
179
+ dispatch({
180
+ type: 'scaleToNode',
181
+ id: prev.spanId,
182
+ });
183
+ return;
184
+ }
185
+ const target = event.target as HTMLElement;
186
+ if (!target.closest(`.${String(styles.timeline)}`)) return;
187
+ const $button = target.closest<HTMLButtonElement>('[data-span-id]');
188
+ const spanId = $button?.dataset.spanId || '';
189
+ lastClickRef.current = {
190
+ x,
191
+ y,
192
+ t,
193
+ spanId,
194
+ };
195
+ if (!spanId) return;
196
+ dispatch({
197
+ type: 'toggleSelection',
198
+ id: spanId,
199
+ });
200
+ event.stopPropagation();
201
+ },
202
+ [dispatch]
203
+ );
204
+
205
+ // Zoom helper
206
+ useLayoutEffect(() => {
207
+ const $timeline = timelineRef.current;
208
+ if (!$timeline) return;
209
+
210
+ const snapshot = scrollSnapshotRef.current;
211
+ if (snapshot) {
212
+ $timeline.scrollLeft = snapshot.anchorT * scale - snapshot.anchorX;
213
+ }
214
+ }, [scrollSnapshotRef, timelineRef, scale]);
215
+
216
+ // Selection helper
217
+ useEffect(() => {
218
+ const spanId = state.selected?.span.spanId;
219
+ if (!spanId) return;
220
+
221
+ const timeout = setTimeout(() => {
222
+ const $timeline = state.timelineRef.current;
223
+ const $span = $timeline?.querySelector(`[data-span-id="${spanId}"]`);
224
+ if (!$timeline || !$span) return;
225
+
226
+ const viewRect = $timeline.getBoundingClientRect();
227
+ const spanRect = $span.getBoundingClientRect();
228
+
229
+ // If the selected span is narrower than the timeline, scroll it into view
230
+ if (
231
+ spanRect.width < viewRect.width &&
232
+ (spanRect.left < viewRect.left ||
233
+ spanRect.right > viewRect.right ||
234
+ spanRect.top < viewRect.top ||
235
+ spanRect.bottom > viewRect.bottom)
236
+ ) {
237
+ $span.scrollIntoView({
238
+ block: 'nearest',
239
+ inline: 'center',
240
+ behavior: 'smooth',
241
+ });
242
+ }
243
+ }, 500);
244
+
245
+ return () => {
246
+ clearTimeout(timeout);
247
+ };
248
+ }, [state.selected, state.timelineRef]);
249
+
250
+ // Global keyboard shortcuts
251
+ useEffect(() => {
252
+ const onKeyDown = (e: KeyboardEvent): void => {
253
+ if (e.key === 'Escape') {
254
+ dispatch({
255
+ type: 'escape',
256
+ });
257
+ }
258
+ };
259
+ window.addEventListener('keydown', onKeyDown);
260
+
261
+ return () => {
262
+ window.removeEventListener('keydown', onKeyDown);
263
+ };
264
+ }, [dispatch]);
265
+
266
+ // Scroll locking
267
+ useEffect(() => {
268
+ if (!scrollLock) return;
269
+
270
+ const $html = document.documentElement;
271
+ const $body = document.body;
272
+
273
+ $html.style.overflow = 'clip';
274
+ $body.style.overflow = 'clip';
275
+
276
+ const onScroll = (event?: Event): void => {
277
+ if (event?.cancelable) {
278
+ event.preventDefault();
279
+ } else {
280
+ window.scrollTo({
281
+ left: 0,
282
+ top: 0,
283
+ behavior: 'instant',
284
+ });
285
+ }
286
+ };
287
+ onScroll();
288
+ window.addEventListener('scroll', onScroll, {
289
+ passive: false,
290
+ });
291
+
292
+ return () => {
293
+ $html.style.overflow = '';
294
+ $body.style.overflow = '';
295
+ window.removeEventListener('scroll', onScroll);
296
+ };
297
+ }, [scrollLock]);
298
+
299
+ const timelineHeight = Math.max(
300
+ state.timelineHeight - state.scrollbarWidth,
301
+ MARKER_HEIGHT +
302
+ ROW_PADDING +
303
+ rows.length * (ROW_HEIGHT + ROW_PADDING) -
304
+ ROW_PADDING +
305
+ // When there are enough spans to be near the bottom edge, add some extra padding
306
+ // to avoid overlapping with the zoom buttons, etc.
307
+ 84
308
+ );
309
+
310
+ const inert = Boolean(state.isMobile && state.selected);
311
+
312
+ return (
313
+ <div
314
+ className={clsx(
315
+ styles.traceViewer,
316
+ isSkeleton && styles.skeleton,
317
+ className
318
+ )}
319
+ onClickCapture={onClick}
320
+ ref={ref}
321
+ style={
322
+ {
323
+ height,
324
+ '--timeline-padding': `${TIMELINE_PADDING}px`,
325
+ '--row-height': `${ROW_HEIGHT}px`,
326
+ '--row-padding': `${ROW_PADDING}px`,
327
+ '--search-height': `${!hideSearchBar ? SEARCH_HEIGHT : 0}px`,
328
+ '--search-gap': `${!hideSearchBar ? SEARCH_GAP : 2}px`,
329
+ '--map-height': `${MAP_HEIGHT}px`,
330
+ '--timeline-width': `${state.timelineWidth}px`,
331
+ '--timeline-height': `${state.timelineHeight}px`,
332
+ '--timeline-scroll-width': `${Math.round(state.root.duration * state.scale)}px`,
333
+ '--panel-width': `${state.panelWidth}px`,
334
+ '--panel-height': `${state.panelHeight}px`,
335
+ '--height': `${state.height}px`,
336
+ '--scrollbar-width': `${state.scrollbarWidth}px`,
337
+ '--marker-height': `${MARKER_HEIGHT}px`,
338
+ '--marker-notch-height': `${MARKER_NOTCH_HEIGHT}px`,
339
+ } as CSSProperties
340
+ }
341
+ >
342
+ {!hideSearchBar ? <SearchBar /> : null}
343
+ <MiniMap rows={rows} scale={scale} timelineRef={timelineRef} />
344
+ <div className={clsx(styles.traceViewerContent, inert && styles.inert)}>
345
+ <div className={styles.timeline} ref={timelineRef}>
346
+ <div
347
+ style={{
348
+ position: 'relative',
349
+ width: state.timelineWidth,
350
+ height: state.timelineHeight - TIMELINE_PADDING * 2,
351
+ padding: TIMELINE_PADDING,
352
+ paddingBottom: 0,
353
+ }}
354
+ >
355
+ <div
356
+ className={styles.traceNode}
357
+ style={{
358
+ width: state.root.duration * scale || undefined,
359
+ height: timelineHeight - TIMELINE_PADDING * 2,
360
+ }}
361
+ >
362
+ <Markers scale={scale} />
363
+ <EventMarkers events={events} root={state.root} scale={scale} />
364
+ <CursorMarker
365
+ dispatch={dispatch}
366
+ events={events}
367
+ memoCacheRef={state.memoCacheRef}
368
+ root={state.root}
369
+ scale={scale}
370
+ scrollSnapshotRef={scrollSnapshotRef}
371
+ spans={spans}
372
+ timelineRef={timelineRef}
373
+ />
374
+ <SpanNodes
375
+ cacheKey={memoCache.get('')}
376
+ cache={memoCache}
377
+ customSpanClassNameFunc={state.customSpanClassNameFunc}
378
+ customSpanEventClassNameFunc={
379
+ state.customSpanEventClassNameFunc
380
+ }
381
+ root={state.root}
382
+ scale={scale}
383
+ scrollSnapshotRef={scrollSnapshotRef}
384
+ spans={spans}
385
+ />
386
+ </div>
387
+ </div>
388
+ </div>
389
+ <div className={styles.zoomButtonTraceViewer}>
390
+ <ZoomButton />
391
+ </div>
392
+ </div>
393
+ {withPanel ? (
394
+ <div
395
+ className={clsx(
396
+ styles.spanDetailPanelTraceViewer,
397
+ !state.selected && styles.hidden,
398
+ state.isMobile && styles.mobile
399
+ )}
400
+ >
401
+ <SpanDetailPanel attached />
402
+ </div>
403
+ ) : null}
404
+ </div>
405
+ );
406
+ }
407
+
408
+ export function TraceViewerPanel({
409
+ className = '',
410
+ children = null,
411
+ }: {
412
+ className?: string;
413
+ children?: ReactNode;
414
+ }): ReactNode {
415
+ const { state } = useTraceViewer();
416
+
417
+ if (!state.selected) {
418
+ return children;
419
+ }
420
+
421
+ return (
422
+ <div
423
+ className={clsx(styles.spanDetailPanelTraceViewer, className)}
424
+ style={
425
+ {
426
+ position: 'relative',
427
+ '--search-height': '0',
428
+ '--search-gap': '0',
429
+ '--map-height': '0',
430
+ '--panel-width': `100%`,
431
+ '--panel-height': `100%`,
432
+ '--height': `100%`,
433
+ '--scrollbar-width': `${state.scrollbarWidth}px`,
434
+ } as CSSProperties
435
+ }
436
+ >
437
+ <SpanDetailPanel />
438
+ </div>
439
+ );
440
+ }
441
+
442
+ export function TraceViewer(props: TraceViewerProps): ReactNode {
443
+ return (
444
+ <TraceViewerContextProvider getQuickLinks={props.getQuickLinks} withPanel>
445
+ <TraceViewerTimeline withPanel {...props} />
446
+ </TraceViewerContextProvider>
447
+ );
448
+ }
@@ -0,0 +1,234 @@
1
+ import type { MutableRefObject, ReactNode } from 'react';
2
+
3
+ export type MemoCacheKey = Record<string, never>;
4
+ export type MemoCache = Map<string, MemoCacheKey>;
5
+
6
+ export interface Span {
7
+ name: string;
8
+ kind: number;
9
+ resource: string;
10
+ library: {
11
+ name: string;
12
+ version?: string;
13
+ };
14
+ spanId: string;
15
+ parentSpanId?: string;
16
+ status: {
17
+ code: number;
18
+ };
19
+ traceFlags: number;
20
+ attributes: Record<string, unknown>;
21
+ links: Record<string, unknown>[];
22
+ events: SpanEvent[];
23
+ startTime: [number, number];
24
+ endTime: [number, number];
25
+ duration: [number, number];
26
+ /**
27
+ * The time when the span became active/started executing (optional).
28
+ * If provided and different from startTime, the portion between startTime
29
+ * and activeStartTime will be rendered as a "queued" period with different styling.
30
+ */
31
+ activeStartTime?: [number, number];
32
+ }
33
+
34
+ export interface SpanEvent {
35
+ name: string;
36
+ timestamp: [number, number];
37
+ attributes: Record<string, unknown>;
38
+ /**
39
+ * Optional custom color for this event marker (workflow-specific feature).
40
+ * If provided, this color will be used for the event marker line/diamond.
41
+ */
42
+ color?: string;
43
+ /**
44
+ * Whether to show a vertical line for this event in the timeline (workflow-specific feature).
45
+ * If false, only the diamond marker on the span will be shown.
46
+ * Defaults to true if not specified.
47
+ */
48
+ showVerticalLine?: boolean;
49
+ }
50
+
51
+ export interface Resource {
52
+ name: string;
53
+ attributes: Record<string, string>;
54
+ }
55
+
56
+ export interface Trace {
57
+ traceId: string;
58
+ resources?: Resource[];
59
+ spans: Span[];
60
+ rootSpanId?: string;
61
+ }
62
+
63
+ interface EveryNode {
64
+ startTime: number;
65
+ endTime: number;
66
+ duration: number;
67
+ /**
68
+ * How deep is this node within the tree
69
+ */
70
+ depth: number;
71
+ /**
72
+ * All direct child nodes of this node
73
+ */
74
+ children: SpanNode[];
75
+ }
76
+
77
+ export interface RootNode extends EveryNode {
78
+ /**
79
+ * The depth is always 0 for the root node
80
+ */
81
+ depth: 0;
82
+ }
83
+
84
+ export interface SpanNode extends EveryNode {
85
+ /**
86
+ * This original Span that this node is based on
87
+ */
88
+ span: Span;
89
+ /**
90
+ * The immediate parent of this node
91
+ */
92
+ parent: RootNode | SpanNode;
93
+ /**
94
+ * A generated label to use (overrides the span.name) if present
95
+ */
96
+ label?: string;
97
+ /**
98
+ * OTEL events that are present on this span (e.g. TTFB)
99
+ */
100
+ events?: SpanNodeEvent[];
101
+ /**
102
+ * Whether this span originated in Vercel's infrastructure.
103
+ */
104
+ isVercel: boolean;
105
+ /**
106
+ * Whether this span matches the current search filter
107
+ */
108
+ isHighlighted?: boolean;
109
+ /**
110
+ * Whether this span is a placeholder to hint at where user spans
111
+ * would appear if proper instrumentation were available
112
+ */
113
+ isInstrumentationHint?: boolean;
114
+ /**
115
+ * The index of the resource that this node belongs to (used for color)
116
+ */
117
+ resourceIndex: number;
118
+ /**
119
+ * The time when execution actually started (in milliseconds).
120
+ * If present and greater than startTime, represents a "queued" period.
121
+ */
122
+ activeStartTime?: number;
123
+ }
124
+
125
+ export interface SpanNodeEvent {
126
+ /** A unique key for this event to help with rendering in React */
127
+ key: string;
128
+ /** The timestamp of this event in milliseconds */
129
+ timestamp: number;
130
+ /** The original event model */
131
+ event: SpanEvent;
132
+ }
133
+
134
+ export type TraceNode = RootNode | SpanNode;
135
+
136
+ export interface VisibleSpan extends SpanNode {
137
+ /**
138
+ * The ref for this span's element in the DOM
139
+ */
140
+ ref?: MutableRefObject<HTMLButtonElement | null>;
141
+ /**
142
+ * The y position of this span (in # of rows)
143
+ */
144
+ row: number;
145
+ /**
146
+ * Whether the CursorMarker is intersecting this span
147
+ */
148
+ isHovered: boolean;
149
+ /**
150
+ * Whether this is the selected span
151
+ */
152
+ isSelected: boolean;
153
+ /**
154
+ * Whether this node is visible (based on its size on screen)
155
+ */
156
+ isVisible: boolean;
157
+ /**
158
+ * Events also need to be able to hold a ref to their element in the DOM
159
+ */
160
+ events?: VisibleSpanEvent[];
161
+ }
162
+
163
+ export interface VisibleSpanEvent extends SpanNodeEvent {
164
+ /**
165
+ * The ref for this span's element in the DOM
166
+ */
167
+ ref?: MutableRefObject<HTMLDivElement | null>;
168
+ /**
169
+ * Whether the CursorMarker is intersecting this span
170
+ */
171
+ isHovered: boolean;
172
+ }
173
+
174
+ export interface ScrollSnapshot {
175
+ anchorT: number;
176
+ anchorX: number;
177
+ scrollLeft: number;
178
+ scrollTop: number;
179
+ startTime: number;
180
+ endTime: number;
181
+ startRow: number;
182
+ endRow: number;
183
+ scale: number;
184
+ }
185
+
186
+ export type WorkerRequest = {
187
+ requestId: number;
188
+ } & (
189
+ | {
190
+ type: 'calculateSpanPositions';
191
+ root: RootNode;
192
+ }
193
+ | {
194
+ type: 'filterSpans';
195
+ root: RootNode;
196
+ filter: string;
197
+ }
198
+ );
199
+
200
+ export type WorkerResponse = {
201
+ requestId: number;
202
+ } & (
203
+ | {
204
+ type: 'setRowsResult';
205
+ rows: VisibleSpan[][];
206
+ isEnd: boolean;
207
+ }
208
+ | {
209
+ type: 'updateHighlight';
210
+ matches: Set<string>;
211
+ }
212
+ );
213
+
214
+ /**
215
+ * Represents one link to a page related to the selected
216
+ * span in the detail panel.
217
+ */
218
+ export interface QuickLink {
219
+ /** Used as a React key and the user-facing name for the link */
220
+ key: string;
221
+ /** Until this Promise is fulfilled, the row will render with a skeleton for the value. */
222
+ value: Promise<QuickLinkValue>;
223
+ }
224
+
225
+ export interface QuickLinkValue {
226
+ /** The visible components for the right side of this link */
227
+ label: ReactNode;
228
+ /** A special suffix to show for this link, defaults to the ExternalLink icon */
229
+ icon?: ReactNode;
230
+ /** The href that this link will open when clicked */
231
+ href: string;
232
+ }
233
+
234
+ export type GetQuickLinks = (node: Span) => QuickLink[];
@@ -0,0 +1,8 @@
1
+ export const SEARCH_HEIGHT = 40;
2
+ export const SEARCH_GAP = 8;
3
+ export const MAP_HEIGHT = 56;
4
+ export const TIMELINE_PADDING = 8;
5
+ export const ROW_PADDING = 2;
6
+ export const ROW_HEIGHT = 24;
7
+ export const MARKER_HEIGHT = 24;
8
+ export const MARKER_NOTCH_HEIGHT = 8;
@@ -0,0 +1,13 @@
1
+ export const detectScrollbarWidth = (): number => {
2
+ if (!('document' in globalThis)) return 0;
3
+ const size = 400;
4
+ const container = document.createElement('div');
5
+ container.style.width = `${size}px`;
6
+ container.style.height = `${size}px`;
7
+ container.style.overflow = 'scroll';
8
+ document.body.appendChild(container);
9
+ const scrollbarWidth = size - container.scrollWidth;
10
+ container.remove();
11
+
12
+ return scrollbarWidth;
13
+ };