@workflow/web-shared 5.0.0-beta.2 → 5.0.0-beta.21

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 (321) hide show
  1. package/dist/components/error-boundary.js +1 -1
  2. package/dist/components/event-list-view.d.ts +16 -1
  3. package/dist/components/event-list-view.d.ts.map +1 -1
  4. package/dist/components/event-list-view.js +196 -85
  5. package/dist/components/hook-actions.js +1 -1
  6. package/dist/components/index.d.ts +4 -0
  7. package/dist/components/index.d.ts.map +1 -1
  8. package/dist/components/index.js +5 -1
  9. package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
  10. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
  11. package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
  12. package/dist/components/new-trace-viewer/components/event-list.d.ts +10 -0
  13. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
  14. package/dist/components/new-trace-viewer/components/event-list.js +46 -0
  15. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts +18 -0
  16. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
  17. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +54 -0
  18. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts +20 -0
  19. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
  20. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
  21. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +25 -0
  22. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
  23. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +101 -0
  24. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts +20 -0
  25. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +1 -0
  26. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js +114 -0
  27. package/dist/components/new-trace-viewer/components/span-markers.d.ts +46 -0
  28. package/dist/components/new-trace-viewer/components/span-markers.d.ts.map +1 -0
  29. package/dist/components/new-trace-viewer/components/span-markers.js +85 -0
  30. package/dist/components/new-trace-viewer/components/split-pane.d.ts +15 -0
  31. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
  32. package/dist/components/new-trace-viewer/components/split-pane.js +105 -0
  33. package/dist/components/new-trace-viewer/components/timeline.d.ts +36 -0
  34. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
  35. package/dist/components/new-trace-viewer/components/timeline.js +254 -0
  36. package/dist/components/new-trace-viewer/components/timeline.module.css +46 -0
  37. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts +2 -0
  38. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -0
  39. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.js +19 -0
  40. package/dist/components/new-trace-viewer/components/use-row-window.d.ts +34 -0
  41. package/dist/components/new-trace-viewer/components/use-row-window.d.ts.map +1 -0
  42. package/dist/components/new-trace-viewer/components/use-row-window.js +107 -0
  43. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  44. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  45. package/dist/components/new-trace-viewer/context.js +47 -0
  46. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  47. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  48. package/dist/components/new-trace-viewer/icons.js +15 -0
  49. package/dist/components/new-trace-viewer/search.d.ts +19 -0
  50. package/dist/components/new-trace-viewer/search.d.ts.map +1 -0
  51. package/dist/components/new-trace-viewer/search.js +134 -0
  52. package/dist/components/new-trace-viewer/trace-viewer.d.ts +11 -0
  53. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  54. package/dist/components/new-trace-viewer/trace-viewer.js +462 -0
  55. package/dist/components/new-trace-viewer/types.d.ts +3 -0
  56. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  57. package/dist/components/new-trace-viewer/types.js +2 -0
  58. package/dist/components/new-trace-viewer/utils.d.ts +61 -0
  59. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  60. package/dist/components/new-trace-viewer/utils.js +345 -0
  61. package/dist/components/run-trace-view.js +1 -1
  62. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  63. package/dist/components/sidebar/attribute-panel.js +129 -102
  64. package/dist/components/sidebar/attributes-block.d.ts +28 -0
  65. package/dist/components/sidebar/attributes-block.d.ts.map +1 -0
  66. package/dist/components/sidebar/attributes-block.js +81 -0
  67. package/dist/components/sidebar/conversation-view.js +1 -1
  68. package/dist/components/sidebar/copyable-data-block.d.ts +1 -0
  69. package/dist/components/sidebar/copyable-data-block.d.ts.map +1 -1
  70. package/dist/components/sidebar/copyable-data-block.js +16 -15
  71. package/dist/components/sidebar/detail-card.d.ts +5 -6
  72. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  73. package/dist/components/sidebar/detail-card.js +32 -16
  74. package/dist/components/sidebar/entity-detail-panel.d.ts +1 -3
  75. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  76. package/dist/components/sidebar/entity-detail-panel.js +17 -35
  77. package/dist/components/sidebar/events-list.d.ts +3 -1
  78. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  79. package/dist/components/sidebar/events-list.js +36 -22
  80. package/dist/components/sidebar/resolve-hook-modal.js +1 -1
  81. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  82. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  83. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  84. package/dist/components/sidebar/span-detail-merge.d.ts +16 -0
  85. package/dist/components/sidebar/span-detail-merge.d.ts.map +1 -0
  86. package/dist/components/sidebar/span-detail-merge.js +53 -0
  87. package/dist/components/stream-viewer.d.ts +1 -1
  88. package/dist/components/stream-viewer.d.ts.map +1 -1
  89. package/dist/components/stream-viewer.js +3 -27
  90. package/dist/components/trace-viewer/components/map.js +1 -1
  91. package/dist/components/trace-viewer/components/markers.js +1 -1
  92. package/dist/components/trace-viewer/components/node.d.ts.map +1 -1
  93. package/dist/components/trace-viewer/components/node.js +2 -1
  94. package/dist/components/trace-viewer/components/search-input.js +1 -1
  95. package/dist/components/trace-viewer/components/search.js +1 -1
  96. package/dist/components/trace-viewer/components/span-content.js +1 -1
  97. package/dist/components/trace-viewer/components/span-detail-panel.js +1 -1
  98. package/dist/components/trace-viewer/components/span-segments.js +1 -1
  99. package/dist/components/trace-viewer/components/span-strategies.js +1 -1
  100. package/dist/components/trace-viewer/components/ui.js +1 -1
  101. package/dist/components/trace-viewer/components/zoom-button.js +1 -1
  102. package/dist/components/trace-viewer/components/zoom-icons.js +1 -1
  103. package/dist/components/trace-viewer/context.js +1 -1
  104. package/dist/components/trace-viewer/index.js +1 -1
  105. package/dist/components/trace-viewer/trace-viewer.js +1 -1
  106. package/dist/components/trace-viewer/trace-viewer.module.css +4 -4
  107. package/dist/components/trace-viewer/types.js +1 -1
  108. package/dist/components/trace-viewer/util/constants.js +1 -1
  109. package/dist/components/trace-viewer/util/scrollbar-width.js +1 -1
  110. package/dist/components/trace-viewer/util/timing.d.ts +3 -3
  111. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
  112. package/dist/components/trace-viewer/util/timing.js +8 -11
  113. package/dist/components/trace-viewer/util/tree.js +1 -1
  114. package/dist/components/trace-viewer/util/use-immediate-style.js +1 -1
  115. package/dist/components/trace-viewer/util/use-streaming-spans.js +1 -1
  116. package/dist/components/trace-viewer/util/use-trackpad-zoom.js +1 -1
  117. package/dist/components/trace-viewer/worker.js +1 -1
  118. package/dist/components/trace-viewer-new.d.ts +14 -0
  119. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  120. package/dist/components/trace-viewer-new.js +21 -0
  121. package/dist/components/ui/alert.js +1 -1
  122. package/dist/components/ui/button.d.ts +10 -0
  123. package/dist/components/ui/button.d.ts.map +1 -0
  124. package/dist/components/ui/button.js +46 -0
  125. package/dist/components/ui/card.js +1 -1
  126. package/dist/components/ui/context-card.d.ts +47 -0
  127. package/dist/components/ui/context-card.d.ts.map +1 -0
  128. package/dist/components/ui/context-card.js +471 -0
  129. package/dist/components/ui/data-inspector.d.ts +22 -0
  130. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  131. package/dist/components/ui/data-inspector.js +103 -13
  132. package/dist/components/ui/decrypt-button.js +1 -1
  133. package/dist/components/ui/error-card.js +1 -1
  134. package/dist/components/ui/error-stack-block.d.ts +17 -10
  135. package/dist/components/ui/error-stack-block.d.ts.map +1 -1
  136. package/dist/components/ui/error-stack-block.js +49 -30
  137. package/dist/components/ui/icon-button.d.ts +19 -0
  138. package/dist/components/ui/icon-button.d.ts.map +1 -0
  139. package/dist/components/ui/icon-button.js +29 -0
  140. package/dist/components/ui/inspector-theme.d.ts +9 -10
  141. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  142. package/dist/components/ui/inspector-theme.js +39 -72
  143. package/dist/components/ui/kbd.d.ts +6 -0
  144. package/dist/components/ui/kbd.d.ts.map +1 -0
  145. package/dist/components/ui/kbd.js +6 -0
  146. package/dist/components/ui/load-more-button.js +1 -1
  147. package/dist/components/ui/menu-dropdown.js +1 -1
  148. package/dist/components/ui/skeleton.js +1 -1
  149. package/dist/components/ui/spinner.js +1 -1
  150. package/dist/components/ui/timestamp-tooltip.d.ts +34 -1
  151. package/dist/components/ui/timestamp-tooltip.d.ts.map +1 -1
  152. package/dist/components/ui/timestamp-tooltip.js +80 -132
  153. package/dist/components/ui/tooltip.d.ts +10 -0
  154. package/dist/components/ui/tooltip.d.ts.map +1 -0
  155. package/dist/components/ui/tooltip.js +12 -0
  156. package/dist/components/workflow-trace-view.d.ts +1 -3
  157. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  158. package/dist/components/workflow-trace-view.js +11 -13
  159. package/dist/components/workflow-traces/event-colors.d.ts +1 -0
  160. package/dist/components/workflow-traces/event-colors.d.ts.map +1 -1
  161. package/dist/components/workflow-traces/event-colors.js +12 -1
  162. package/dist/components/workflow-traces/trace-colors.js +1 -1
  163. package/dist/components/workflow-traces/trace-span-construction.d.ts +2 -2
  164. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  165. package/dist/components/workflow-traces/trace-span-construction.js +19 -5
  166. package/dist/components/workflow-traces/trace-time-utils.js +1 -1
  167. package/dist/hooks/use-dark-mode.js +1 -1
  168. package/dist/hooks/use-intersection-observer.d.ts +17 -0
  169. package/dist/hooks/use-intersection-observer.d.ts.map +1 -0
  170. package/dist/hooks/use-intersection-observer.js +29 -0
  171. package/dist/hooks/use-load-more-on-scroll.d.ts +21 -0
  172. package/dist/hooks/use-load-more-on-scroll.d.ts.map +1 -0
  173. package/dist/hooks/use-load-more-on-scroll.js +28 -0
  174. package/dist/hooks/use-reduced-motion.d.ts +10 -0
  175. package/dist/hooks/use-reduced-motion.d.ts.map +1 -0
  176. package/dist/hooks/use-reduced-motion.js +29 -0
  177. package/dist/index.d.ts +3 -1
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +3 -2
  180. package/dist/lib/event-analysis.js +1 -1
  181. package/dist/lib/event-materialization.js +1 -1
  182. package/dist/lib/exact-event-search-id.d.ts +25 -0
  183. package/dist/lib/exact-event-search-id.d.ts.map +1 -0
  184. package/dist/lib/exact-event-search-id.js +39 -0
  185. package/dist/lib/hydration.d.ts +6 -0
  186. package/dist/lib/hydration.d.ts.map +1 -1
  187. package/dist/lib/hydration.js +154 -2
  188. package/dist/lib/stream-display.d.ts +18 -0
  189. package/dist/lib/stream-display.d.ts.map +1 -0
  190. package/dist/lib/stream-display.js +30 -0
  191. package/dist/lib/toast.js +1 -1
  192. package/dist/lib/trace-builder.d.ts +15 -0
  193. package/dist/lib/trace-builder.d.ts.map +1 -1
  194. package/dist/lib/trace-builder.js +29 -3
  195. package/dist/lib/utils.d.ts +20 -9
  196. package/dist/lib/utils.d.ts.map +1 -1
  197. package/dist/lib/utils.js +74 -31
  198. package/dist/lib/zstd-browser-decoder.d.ts +7 -0
  199. package/dist/lib/zstd-browser-decoder.d.ts.map +1 -0
  200. package/dist/lib/zstd-browser-decoder.js +41 -0
  201. package/dist/styles.css +484 -1
  202. package/package.json +10 -5
  203. package/src/components/event-list-view.tsx +279 -104
  204. package/src/components/index.ts +7 -0
  205. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  206. package/src/components/new-trace-viewer/components/event-list.tsx +136 -0
  207. package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
  208. package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
  209. package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
  210. package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
  211. package/src/components/new-trace-viewer/components/span-markers.tsx +173 -0
  212. package/src/components/new-trace-viewer/components/split-pane.tsx +198 -0
  213. package/src/components/new-trace-viewer/components/timeline.module.css +46 -0
  214. package/src/components/new-trace-viewer/components/timeline.tsx +744 -0
  215. package/src/components/new-trace-viewer/components/trace-viewer-skeleton.tsx +94 -0
  216. package/src/components/new-trace-viewer/components/use-row-window.ts +140 -0
  217. package/src/components/new-trace-viewer/context.tsx +90 -0
  218. package/src/components/new-trace-viewer/icons.tsx +79 -0
  219. package/src/components/new-trace-viewer/search.ts +178 -0
  220. package/src/components/new-trace-viewer/trace-viewer.tsx +843 -0
  221. package/src/components/new-trace-viewer/types.ts +2 -0
  222. package/src/components/new-trace-viewer/utils.test.ts +182 -0
  223. package/src/components/new-trace-viewer/utils.ts +519 -0
  224. package/src/components/sidebar/attribute-panel.tsx +194 -240
  225. package/src/components/sidebar/attributes-block.tsx +173 -0
  226. package/src/components/sidebar/copyable-data-block.tsx +49 -27
  227. package/src/components/sidebar/detail-card.tsx +119 -59
  228. package/src/components/sidebar/entity-detail-panel.tsx +55 -111
  229. package/src/components/sidebar/events-list.tsx +85 -96
  230. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  231. package/src/components/sidebar/span-detail-merge.ts +65 -0
  232. package/src/components/stream-viewer.tsx +18 -40
  233. package/src/components/trace-viewer/components/node.tsx +3 -2
  234. package/src/components/trace-viewer/trace-viewer.module.css +4 -4
  235. package/src/components/trace-viewer/util/timing.ts +7 -11
  236. package/src/components/trace-viewer-new.tsx +55 -0
  237. package/src/components/ui/button.tsx +68 -0
  238. package/src/components/ui/context-card.tsx +784 -0
  239. package/src/components/ui/data-inspector.tsx +244 -22
  240. package/src/components/ui/error-stack-block.tsx +84 -55
  241. package/src/components/ui/icon-button.tsx +53 -0
  242. package/src/components/ui/inspector-theme.ts +39 -81
  243. package/src/components/ui/kbd.tsx +21 -0
  244. package/src/components/ui/timestamp-tooltip.tsx +132 -194
  245. package/src/components/ui/tooltip.tsx +47 -0
  246. package/src/components/workflow-trace-view.tsx +19 -15
  247. package/src/components/workflow-traces/event-colors.ts +12 -0
  248. package/src/components/workflow-traces/trace-span-construction.ts +24 -5
  249. package/src/hooks/use-intersection-observer.ts +57 -0
  250. package/src/hooks/use-load-more-on-scroll.ts +49 -0
  251. package/src/hooks/use-reduced-motion.ts +34 -0
  252. package/src/index.ts +9 -0
  253. package/src/lib/exact-event-search-id.ts +67 -0
  254. package/src/lib/hydration.ts +166 -1
  255. package/src/lib/stream-display.ts +48 -0
  256. package/src/lib/trace-builder.ts +34 -2
  257. package/src/lib/utils.ts +83 -31
  258. package/src/lib/zstd-browser-decoder.ts +42 -0
  259. package/src/styles.css +484 -1
  260. package/dist/components/error-boundary.js.map +0 -1
  261. package/dist/components/event-list-view.js.map +0 -1
  262. package/dist/components/hook-actions.js.map +0 -1
  263. package/dist/components/index.js.map +0 -1
  264. package/dist/components/run-trace-view.js.map +0 -1
  265. package/dist/components/sidebar/attribute-panel.js.map +0 -1
  266. package/dist/components/sidebar/conversation-view.js.map +0 -1
  267. package/dist/components/sidebar/copyable-data-block.js.map +0 -1
  268. package/dist/components/sidebar/detail-card.js.map +0 -1
  269. package/dist/components/sidebar/entity-detail-panel.js.map +0 -1
  270. package/dist/components/sidebar/events-list.js.map +0 -1
  271. package/dist/components/sidebar/resolve-hook-modal.js.map +0 -1
  272. package/dist/components/stream-viewer.js.map +0 -1
  273. package/dist/components/trace-viewer/components/map.js.map +0 -1
  274. package/dist/components/trace-viewer/components/markers.js.map +0 -1
  275. package/dist/components/trace-viewer/components/node.js.map +0 -1
  276. package/dist/components/trace-viewer/components/search-input.js.map +0 -1
  277. package/dist/components/trace-viewer/components/search.js.map +0 -1
  278. package/dist/components/trace-viewer/components/span-content.js.map +0 -1
  279. package/dist/components/trace-viewer/components/span-detail-panel.js.map +0 -1
  280. package/dist/components/trace-viewer/components/span-segments.js.map +0 -1
  281. package/dist/components/trace-viewer/components/span-strategies.js.map +0 -1
  282. package/dist/components/trace-viewer/components/ui.js.map +0 -1
  283. package/dist/components/trace-viewer/components/zoom-button.js.map +0 -1
  284. package/dist/components/trace-viewer/components/zoom-icons.js.map +0 -1
  285. package/dist/components/trace-viewer/context.js.map +0 -1
  286. package/dist/components/trace-viewer/index.js.map +0 -1
  287. package/dist/components/trace-viewer/trace-viewer.js.map +0 -1
  288. package/dist/components/trace-viewer/types.js.map +0 -1
  289. package/dist/components/trace-viewer/util/constants.js.map +0 -1
  290. package/dist/components/trace-viewer/util/scrollbar-width.js.map +0 -1
  291. package/dist/components/trace-viewer/util/timing.js.map +0 -1
  292. package/dist/components/trace-viewer/util/tree.js.map +0 -1
  293. package/dist/components/trace-viewer/util/use-immediate-style.js.map +0 -1
  294. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +0 -1
  295. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
  296. package/dist/components/trace-viewer/worker.js.map +0 -1
  297. package/dist/components/ui/alert.js.map +0 -1
  298. package/dist/components/ui/card.js.map +0 -1
  299. package/dist/components/ui/data-inspector.js.map +0 -1
  300. package/dist/components/ui/decrypt-button.js.map +0 -1
  301. package/dist/components/ui/error-card.js.map +0 -1
  302. package/dist/components/ui/error-stack-block.js.map +0 -1
  303. package/dist/components/ui/inspector-theme.js.map +0 -1
  304. package/dist/components/ui/load-more-button.js.map +0 -1
  305. package/dist/components/ui/menu-dropdown.js.map +0 -1
  306. package/dist/components/ui/skeleton.js.map +0 -1
  307. package/dist/components/ui/spinner.js.map +0 -1
  308. package/dist/components/ui/timestamp-tooltip.js.map +0 -1
  309. package/dist/components/workflow-trace-view.js.map +0 -1
  310. package/dist/components/workflow-traces/event-colors.js.map +0 -1
  311. package/dist/components/workflow-traces/trace-colors.js.map +0 -1
  312. package/dist/components/workflow-traces/trace-span-construction.js.map +0 -1
  313. package/dist/components/workflow-traces/trace-time-utils.js.map +0 -1
  314. package/dist/hooks/use-dark-mode.js.map +0 -1
  315. package/dist/index.js.map +0 -1
  316. package/dist/lib/event-analysis.js.map +0 -1
  317. package/dist/lib/event-materialization.js.map +0 -1
  318. package/dist/lib/hydration.js.map +0 -1
  319. package/dist/lib/toast.js.map +0 -1
  320. package/dist/lib/trace-builder.js.map +0 -1
  321. package/dist/lib/utils.js.map +0 -1
@@ -0,0 +1,173 @@
1
+ 'use client';
2
+
3
+ import { ArrowLeft, ArrowRight } from 'lucide-react';
4
+ import type { ReactNode } from 'react';
5
+ import { cn } from '../../../lib/utils';
6
+ import { TimestampTooltip } from '../../ui/timestamp-tooltip';
7
+ import type { SpanMarker } from '../utils';
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Marker projection
11
+ // ---------------------------------------------------------------------------
12
+
13
+ export interface VisibleMarker {
14
+ leftPct: number;
15
+ /** Absolute (epoch) timestamp in ms — for the tooltip. */
16
+ timeMs: number;
17
+ }
18
+
19
+ /**
20
+ * Project point-in-time markers onto the visible window `[visibleStartMs,
21
+ * visibleEndMs]` of a bar, as percentages of that window. Markers outside the
22
+ * window are dropped.
23
+ */
24
+ export function projectMarkers(
25
+ markers: SpanMarker[],
26
+ visibleStartMs: number,
27
+ visibleEndMs: number
28
+ ): VisibleMarker[] {
29
+ const visibleDurationMs = visibleEndMs - visibleStartMs;
30
+ if (visibleDurationMs <= 0) return [];
31
+
32
+ return markers.flatMap((m) => {
33
+ if (m.timeMs < visibleStartMs || m.timeMs > visibleEndMs) {
34
+ return [];
35
+ }
36
+ return [
37
+ {
38
+ leftPct: ((m.timeMs - visibleStartMs) / visibleDurationMs) * 100,
39
+ timeMs: m.timeMs,
40
+ },
41
+ ];
42
+ });
43
+ }
44
+
45
+ /** Min pixel gap a tick needs from the last-kept one, else it's culled. */
46
+ const MARKER_MIN_GAP_PX = 16;
47
+
48
+ /**
49
+ * Thin out ticks that would visually collide: walk left-to-right and keep each
50
+ * one unless it sits within MARKER_MIN_GAP_PX of the last kept tick. Well-spaced
51
+ * markers survive even when a tight cluster elsewhere on the bar gets thinned —
52
+ * zoom in to resolve a cluster.
53
+ */
54
+ export function cullCollidingMarkers(
55
+ projected: VisibleMarker[],
56
+ pixelWidth: number
57
+ ): VisibleMarker[] {
58
+ const kept: VisibleMarker[] = [];
59
+ let lastKeptPct = Number.NEGATIVE_INFINITY;
60
+ for (const m of projected) {
61
+ const gapPx = ((m.leftPct - lastKeptPct) / 100) * pixelWidth;
62
+ if (gapPx < MARKER_MIN_GAP_PX) continue;
63
+ kept.push(m);
64
+ lastKeptPct = m.leftPct;
65
+ }
66
+ return kept;
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Rendering
71
+ // ---------------------------------------------------------------------------
72
+
73
+ /** Marker glyph: a rounded vertical tick (3px wide). */
74
+ function MarkerTick({ className }: { className?: string }): ReactNode {
75
+ return (
76
+ <span
77
+ className={cn('block w-[3px] rounded-full bg-gray-1000', className)}
78
+ />
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Point-in-time markers overlaid on a bar — one vertical tick per event (hook
84
+ * resumptions and attribute writes), centered on the bar. Each tick sits inside
85
+ * a larger hit target and, on hover, shows the shared `TimestampTooltip` card
86
+ * (the same light Geist card used for every other timestamp in the app, e.g. the
87
+ * attribute panel and event list) so the time reads consistently with the rest
88
+ * of the product. The position is clamped a hair inside the bar so an edge
89
+ * marker never jams the rounded corner.
90
+ */
91
+ export function MarkerLayer({
92
+ markers,
93
+ }: {
94
+ markers: VisibleMarker[];
95
+ }): ReactNode {
96
+ return (
97
+ <>
98
+ {markers.map((m) => (
99
+ <span
100
+ key={m.timeMs}
101
+ className="pointer-events-auto absolute top-0 bottom-0 z-10 flex w-8 -translate-x-1/2 items-center justify-center"
102
+ style={{ left: `clamp(8px, ${m.leftPct}%, calc(100% - 8px))` }}
103
+ >
104
+ <TimestampTooltip date={m.timeMs}>
105
+ <span className="flex h-6 w-8 items-center justify-center">
106
+ <MarkerTick className="h-3" />
107
+ </span>
108
+ </TimestampTooltip>
109
+ </span>
110
+ ))}
111
+ </>
112
+ );
113
+ }
114
+
115
+ /**
116
+ * Edge indicator for markers that have scrolled out of view while zoomed in.
117
+ * Pinned flush to the corresponding edge of the visible bar, it shows the marker
118
+ * glyph plus a chevron pointing the way to scroll. The chevron always sits on
119
+ * the outer (off-screen) side, in a box that lines up with the bare
120
+ * BoundaryArrow on adjacent rows.
121
+ */
122
+ export function OffscreenMarkerIndicator({
123
+ direction,
124
+ count,
125
+ targetMs,
126
+ onReveal,
127
+ }: {
128
+ direction: 'left' | 'right';
129
+ count: number;
130
+ targetMs: number;
131
+ onReveal?: (timeMs: number) => void;
132
+ }): ReactNode {
133
+ const Chevron = direction === 'right' ? ArrowRight : ArrowLeft;
134
+ const label = `${count} marker${count === 1 ? '' : 's'} ${
135
+ direction === 'right' ? 'ahead' : 'behind'
136
+ } — click to scroll to ${count === 1 ? 'it' : 'the nearest'}`;
137
+ const chevron = (
138
+ <span className="flex h-6 w-6 shrink-0 items-center justify-center">
139
+ <Chevron className="size-3 text-gray-900" />
140
+ </span>
141
+ );
142
+ const tick = <MarkerTick className="h-3.5 shrink-0" />;
143
+
144
+ return (
145
+ <button
146
+ type="button"
147
+ aria-label={label}
148
+ title={label}
149
+ onClick={(e) => {
150
+ // Don't let the row's onClick fire — revealing shouldn't also
151
+ // change the span selection.
152
+ e.stopPropagation();
153
+ onReveal?.(targetMs);
154
+ }}
155
+ className={cn(
156
+ 'pointer-events-auto absolute top-1/2 z-20 flex h-6 -translate-y-1/2 cursor-pointer items-center rounded-[0.25rem] border border-gray-alpha-400 bg-background-100 shadow-sm hover:bg-gray-100',
157
+ direction === 'right' ? 'right-0 pl-1.5' : 'left-0 pr-1.5'
158
+ )}
159
+ >
160
+ {direction === 'left' ? (
161
+ <>
162
+ {chevron}
163
+ {tick}
164
+ </>
165
+ ) : (
166
+ <>
167
+ {tick}
168
+ {chevron}
169
+ </>
170
+ )}
171
+ </button>
172
+ );
173
+ }
@@ -0,0 +1,198 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Children,
5
+ type ReactNode,
6
+ type PointerEvent as ReactPointerEvent,
7
+ type RefObject,
8
+ useCallback,
9
+ useEffect,
10
+ useRef,
11
+ useState,
12
+ } from 'react';
13
+ import { cn } from '../../../lib/utils';
14
+
15
+ const GUTTER_PX = 1;
16
+ const MIN_PX = 50;
17
+ const DEFAULT_START_PX = 340;
18
+
19
+ export function Divider() {
20
+ return <div aria-hidden className="h-full w-px shrink-0 bg-gray-alpha-400" />;
21
+ }
22
+
23
+ export interface SplitPaneProps {
24
+ children: ReactNode;
25
+ className?: string;
26
+ /** Fixed pixel width for the start (left) pane. Default 220. */
27
+ defaultStartWidth?: number;
28
+ /** Fixed (non-scrolling) header rendered above the start pane. */
29
+ startHeader?: ReactNode;
30
+ /** Fixed (non-scrolling) header rendered above the end pane. */
31
+ endHeader?: ReactNode;
32
+ scrollContainerRef?: RefObject<HTMLDivElement | null>;
33
+ }
34
+
35
+ export function SplitPane({
36
+ children,
37
+ className,
38
+ defaultStartWidth = DEFAULT_START_PX,
39
+ startHeader,
40
+ endHeader,
41
+ scrollContainerRef,
42
+ }: SplitPaneProps) {
43
+ const parts = Children.toArray(children);
44
+ if (parts.length !== 2) {
45
+ throw new Error('SplitPane expects exactly two children');
46
+ }
47
+ const [start, end] = parts;
48
+
49
+ const [startPx, setStartPx] = useState(defaultStartWidth);
50
+ const [isDragging, setIsDragging] = useState(false);
51
+ const containerRef = useRef<HTMLDivElement>(null);
52
+ const gutterRef = useRef<HTMLDivElement>(null);
53
+ const rafRef = useRef<number | null>(null);
54
+ const pendingPx = useRef(defaultStartWidth);
55
+ const pointerIdRef = useRef<number | null>(null);
56
+
57
+ useEffect(() => {
58
+ return () => {
59
+ if (rafRef.current) cancelAnimationFrame(rafRef.current);
60
+ };
61
+ }, []);
62
+
63
+ const setContainerRef = useCallback(
64
+ (node: HTMLDivElement | null) => {
65
+ containerRef.current = node;
66
+ if (scrollContainerRef) {
67
+ scrollContainerRef.current = node;
68
+ }
69
+ },
70
+ [scrollContainerRef]
71
+ );
72
+
73
+ const clampPx = useCallback((px: number) => {
74
+ const el = containerRef.current;
75
+ if (!el) return px;
76
+ const maxPx = el.getBoundingClientRect().width - MIN_PX - GUTTER_PX;
77
+ return Math.min(maxPx, Math.max(MIN_PX, px));
78
+ }, []);
79
+
80
+ useEffect(() => {
81
+ if (!isDragging) return;
82
+
83
+ const onPointerMove = (e: globalThis.PointerEvent) => {
84
+ if (e.pointerId !== pointerIdRef.current) return;
85
+ const container = containerRef.current;
86
+ if (!container) return;
87
+ const rect = container.getBoundingClientRect();
88
+ pendingPx.current = clampPx(e.clientX - rect.left);
89
+ if (!rafRef.current) {
90
+ rafRef.current = requestAnimationFrame(() => {
91
+ rafRef.current = null;
92
+ setStartPx(pendingPx.current);
93
+ });
94
+ }
95
+ };
96
+
97
+ const onPointerUp = (e: globalThis.PointerEvent) => {
98
+ if (e.pointerId !== pointerIdRef.current) return;
99
+ const gutter = gutterRef.current;
100
+ if (gutter?.hasPointerCapture(e.pointerId)) {
101
+ gutter.releasePointerCapture(e.pointerId);
102
+ }
103
+ if (rafRef.current) {
104
+ cancelAnimationFrame(rafRef.current);
105
+ rafRef.current = null;
106
+ }
107
+ pointerIdRef.current = null;
108
+ setIsDragging(false);
109
+ };
110
+
111
+ document.addEventListener('pointermove', onPointerMove);
112
+ document.addEventListener('pointerup', onPointerUp);
113
+ document.addEventListener('pointercancel', onPointerUp);
114
+
115
+ return () => {
116
+ document.removeEventListener('pointermove', onPointerMove);
117
+ document.removeEventListener('pointerup', onPointerUp);
118
+ document.removeEventListener('pointercancel', onPointerUp);
119
+ };
120
+ }, [isDragging, clampPx]);
121
+
122
+ const handlePointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {
123
+ e.currentTarget.setPointerCapture(e.pointerId);
124
+ pointerIdRef.current = e.pointerId;
125
+ setIsDragging(true);
126
+ };
127
+
128
+ const handleLostPointerCapture = () => {
129
+ pointerIdRef.current = null;
130
+ setIsDragging(false);
131
+ if (rafRef.current) {
132
+ cancelAnimationFrame(rafRef.current);
133
+ rafRef.current = null;
134
+ }
135
+ };
136
+
137
+ const colTemplate = `${startPx}px ${GUTTER_PX}px minmax(${MIN_PX}px, 1fr)`;
138
+ const hasHeaders = startHeader != null || endHeader != null;
139
+
140
+ const gutter = (
141
+ <div
142
+ ref={gutterRef}
143
+ className="relative z-20 isolate flex shrink-0 cursor-col-resize justify-center"
144
+ onPointerDown={handlePointerDown}
145
+ onLostPointerCapture={handleLostPointerCapture}
146
+ >
147
+ <span
148
+ className="pointer-events-none relative z-10 h-full w-px shrink-0 bg-gray-alpha-400"
149
+ aria-hidden
150
+ />
151
+ </div>
152
+ );
153
+
154
+ if (hasHeaders) {
155
+ return (
156
+ <div className={cn('flex flex-col h-full min-h-0', className)}>
157
+ <div
158
+ className="shrink-0 grid"
159
+ style={{ gridTemplateColumns: colTemplate }}
160
+ >
161
+ <div>{startHeader}</div>
162
+ <div className="flex justify-center">
163
+ <span className="h-full w-px bg-gray-alpha-400" aria-hidden />
164
+ </div>
165
+ <div>{endHeader}</div>
166
+ </div>
167
+ <div
168
+ ref={setContainerRef}
169
+ className={cn(
170
+ 'grid flex-1 min-h-0 overflow-x-hidden overflow-y-auto',
171
+ isDragging && 'select-none'
172
+ )}
173
+ style={{ gridTemplateColumns: colTemplate }}
174
+ >
175
+ {start}
176
+ {gutter}
177
+ {end}
178
+ </div>
179
+ </div>
180
+ );
181
+ }
182
+
183
+ return (
184
+ <div
185
+ ref={setContainerRef}
186
+ className={cn(
187
+ 'grid h-full min-h-0 content-start overflow-x-hidden overflow-y-auto',
188
+ isDragging && 'select-none',
189
+ className
190
+ )}
191
+ style={{ gridTemplateColumns: colTemplate, height: '100%' }}
192
+ >
193
+ {start}
194
+ {gutter}
195
+ {end}
196
+ </div>
197
+ );
198
+ }
@@ -0,0 +1,46 @@
1
+ .runningStripes,
2
+ .pendingStripes {
3
+ position: absolute;
4
+ inset: 0;
5
+ pointer-events: none;
6
+ border-radius: 0.25rem;
7
+ opacity: 0.5;
8
+ background-size: 11.314px 11.314px;
9
+ animation: running-stripes 0.6s linear infinite;
10
+ }
11
+
12
+ .runningStripes {
13
+ background-image: repeating-linear-gradient(
14
+ 45deg,
15
+ var(--ds-blue-500) 0,
16
+ var(--ds-blue-500) 1.5px,
17
+ transparent 1.5px,
18
+ transparent 8px
19
+ );
20
+ }
21
+
22
+ .pendingStripes {
23
+ background-image: repeating-linear-gradient(
24
+ 45deg,
25
+ var(--ds-gray-500) 0,
26
+ var(--ds-gray-500) 1.5px,
27
+ transparent 1.5px,
28
+ transparent 8px
29
+ );
30
+ }
31
+
32
+ @keyframes running-stripes {
33
+ from {
34
+ background-position: 0 0;
35
+ }
36
+ to {
37
+ background-position: 11.314px 0;
38
+ }
39
+ }
40
+
41
+ @media (prefers-reduced-motion: reduce) {
42
+ .runningStripes,
43
+ .pendingStripes {
44
+ animation: none;
45
+ }
46
+ }