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

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
@@ -0,0 +1,784 @@
1
+ 'use client';
2
+
3
+ import { Root as SlotRoot, Slottable } from '@radix-ui/react-slot';
4
+ import type { JSX, MutableRefObject, ReactNode, RefObject } from 'react';
5
+ import {
6
+ createContext,
7
+ useContext,
8
+ useEffect,
9
+ useId,
10
+ useMemo,
11
+ useRef,
12
+ useState,
13
+ } from 'react';
14
+ import { createPortal } from 'react-dom';
15
+ import useMeasure from 'react-use-measure';
16
+ import { useReducedMotion } from '../../hooks/use-reduced-motion';
17
+ import { cn } from '../../lib/cn';
18
+
19
+ const INACTIVE_TIMEOUT_MS = 250;
20
+ const ENTER_DELAY_MS = 0;
21
+ const OPEN_DELAY_MS = 150;
22
+ const MAX_TRANSITION_DISTANCE_PX = 150;
23
+ const MAX_OVERLAP_FOR_TRANSITION_PERCENT = 100;
24
+ const PERSIST_TIMEOUT_MS = 1000;
25
+
26
+ interface Point {
27
+ x: number;
28
+ y: number;
29
+ }
30
+
31
+ interface ContextCardData {
32
+ id: string;
33
+ origin: Point;
34
+ contentSize: { width: number; height: number };
35
+ side: Side;
36
+ arrowOffset: Point;
37
+ }
38
+
39
+ type NullString = string | null;
40
+
41
+ interface ContextCardContextType {
42
+ rootOrigin: Point;
43
+ rootBounds: { width: number; height: number };
44
+ portalRef: RefObject<HTMLDivElement | null>;
45
+ activeId: RefObject<NullString | null>;
46
+ setActiveId: (id: NullString) => void;
47
+ hoveredId: NullString;
48
+ setHoveredId: (id: NullString) => void;
49
+ updateActiveContextCard: (update: ContextCardData | null) => void;
50
+ skipTransition: boolean;
51
+ rootVisible: boolean;
52
+ distanceFromLast: number;
53
+ lastOrigin: Point | null;
54
+ /** Whether a real {@link ContextCardProvider} is mounted above this consumer. */
55
+ isProvided: boolean;
56
+ }
57
+
58
+ const ContextCardContext = createContext<ContextCardContextType>({
59
+ rootOrigin: { x: 0, y: 0 },
60
+ rootBounds: { width: 0, height: 0 },
61
+ portalRef: null as unknown as RefObject<HTMLDivElement | null>,
62
+ activeId: null as unknown as MutableRefObject<string | null>,
63
+ setActiveId: () => void 0,
64
+ hoveredId: null,
65
+ setHoveredId: () => void 0,
66
+ updateActiveContextCard: () => void 0,
67
+ skipTransition: false,
68
+ rootVisible: false,
69
+ distanceFromLast: 0,
70
+ lastOrigin: null,
71
+ isProvided: false,
72
+ });
73
+ ContextCardContext.displayName = 'ContextCardContext';
74
+
75
+ /**
76
+ * Returns whether the current subtree is wrapped in a {@link ContextCardProvider}.
77
+ */
78
+ export function useHasContextCardProvider(): boolean {
79
+ return useContext(ContextCardContext).isProvided;
80
+ }
81
+
82
+ function usePrevious<T>(value: T): T | null {
83
+ const ref = useRef<T | null>(null);
84
+ useEffect(() => {
85
+ ref.current = value;
86
+ });
87
+ return ref.current;
88
+ }
89
+
90
+ function isRefObject(
91
+ value: HTMLElement | RefObject<HTMLElement | null> | null | undefined
92
+ ): value is RefObject<HTMLElement | null> {
93
+ return value !== null && value !== undefined && 'current' in value;
94
+ }
95
+
96
+ /**
97
+ * Provides shared state and a portal container for {@link ContextCardTrigger}
98
+ * components. Must wrap any tree containing triggers; manages positioning,
99
+ * visibility transitions, and the animated arrow tip across all cards.
100
+ */
101
+ export function ContextCardProvider({
102
+ children,
103
+ portalTarget,
104
+ }: {
105
+ children: ReactNode;
106
+ /** Custom container for the portal. Accepts either an element or a ref. */
107
+ portalTarget?: HTMLElement | RefObject<HTMLElement | null> | null;
108
+ }): ReactNode {
109
+ const [isMounted, setIsMounted] = useState(false);
110
+ useEffect(() => setIsMounted(true), []);
111
+ const resolvedPortalTarget = isRefObject(portalTarget)
112
+ ? portalTarget.current
113
+ : portalTarget;
114
+ const [ref, bounds] = useMeasure();
115
+ const portalRef = useRef<HTMLDivElement | null>(null);
116
+ const [activeContextCard, setActiveContextCard] =
117
+ useState<ContextCardData | null>(null);
118
+ const [visible, setVisible] = useState(false);
119
+ const [hoveredId, setHoveredId] = useState<NullString>(null);
120
+ const activeId = useRef<NullString>(null);
121
+
122
+ function setActiveId(id: NullString): void {
123
+ activeId.current = id;
124
+ setVisible(id !== null);
125
+ }
126
+ const updateActiveContextCard = (update: ContextCardData | null): void => {
127
+ if (update?.id !== activeId.current && update !== null) return;
128
+ setActiveContextCard(update);
129
+ };
130
+
131
+ const BORDER_WIDTH = 1;
132
+
133
+ const activeBounds = useMemo(() => {
134
+ if (!activeContextCard)
135
+ return {
136
+ width: 0,
137
+ height: 0,
138
+ x: 0,
139
+ y: 0,
140
+ side: 'top' as Side,
141
+ arrowOffset: { x: 0, y: 0 },
142
+ };
143
+ return {
144
+ width: activeContextCard.contentSize.width + BORDER_WIDTH * 2,
145
+ height: activeContextCard.contentSize.height + BORDER_WIDTH * 2,
146
+ x: activeContextCard.origin.x,
147
+ y: activeContextCard.origin.y,
148
+ side: activeContextCard.side,
149
+ arrowOffset: activeContextCard.arrowOffset,
150
+ };
151
+ }, [activeContextCard]);
152
+
153
+ const lastBounds = usePrevious(activeBounds);
154
+ const lastId = usePrevious(activeId.current);
155
+
156
+ const percentOverlapFromLast = useMemo(() => {
157
+ if (!lastBounds) return 0;
158
+
159
+ const overlapLeft = Math.max(activeBounds.x, lastBounds.x);
160
+ const overlapRight = Math.min(
161
+ activeBounds.x + activeBounds.width,
162
+ lastBounds.x + lastBounds.width
163
+ );
164
+ const overlapTop = Math.max(activeBounds.y, lastBounds.y);
165
+ const overlapBottom = Math.min(
166
+ activeBounds.y + activeBounds.height,
167
+ lastBounds.y + lastBounds.height
168
+ );
169
+
170
+ if (overlapLeft < overlapRight && overlapTop < overlapBottom) {
171
+ const overlapArea =
172
+ (overlapRight - overlapLeft) * (overlapBottom - overlapTop);
173
+
174
+ const activeBoundsArea = activeBounds.width * activeBounds.height;
175
+
176
+ if (activeBoundsArea === 0) return 0;
177
+
178
+ return (overlapArea / activeBoundsArea) * 100;
179
+ }
180
+
181
+ return 0;
182
+ }, [activeBounds, lastBounds]);
183
+
184
+ const distanceFromLast = useMemo(
185
+ () =>
186
+ Math.sqrt(
187
+ ((lastBounds?.x ?? 0) - activeBounds.x) ** 2 +
188
+ ((lastBounds?.y ?? 0) - activeBounds.y) ** 2
189
+ ),
190
+ [activeBounds.x, activeBounds.y, lastBounds?.x, lastBounds?.y]
191
+ );
192
+
193
+ const lastOrigin = useMemo(() => {
194
+ if (!lastBounds) return null;
195
+ return {
196
+ x: lastBounds.x - activeBounds.x,
197
+ y: lastBounds.y - activeBounds.y,
198
+ };
199
+ }, [activeBounds, lastBounds]);
200
+
201
+ const [isScrolling, setIsScrolling] = useState(false);
202
+ const skipTransition = useMemo(
203
+ () =>
204
+ distanceFromLast > MAX_TRANSITION_DISTANCE_PX ||
205
+ percentOverlapFromLast > MAX_OVERLAP_FOR_TRANSITION_PERCENT ||
206
+ lastId === null ||
207
+ isScrolling,
208
+ [distanceFromLast, isScrolling, lastId, percentOverlapFromLast]
209
+ );
210
+
211
+ useEffect(() => {
212
+ let isScrollingTimeout: ReturnType<typeof setTimeout> | null = null;
213
+
214
+ const onScroll = (): void => {
215
+ if (isScrollingTimeout) window.clearTimeout(isScrollingTimeout);
216
+
217
+ isScrollingTimeout = setTimeout(() => {
218
+ setIsScrolling(false);
219
+ }, 66);
220
+
221
+ if (!isScrolling) {
222
+ setIsScrolling(true);
223
+ }
224
+ };
225
+
226
+ document.addEventListener('scroll', onScroll, true);
227
+
228
+ return () => {
229
+ document.removeEventListener('scroll', onScroll, true);
230
+ };
231
+ }, [isScrolling]);
232
+
233
+ const contextValue = useMemo(
234
+ () => ({
235
+ updateActiveContextCard,
236
+ rootOrigin: { ...activeBounds },
237
+ rootBounds: bounds,
238
+ portalRef,
239
+ setActiveId,
240
+ activeId,
241
+ setHoveredId,
242
+ hoveredId,
243
+ skipTransition,
244
+ rootVisible: visible,
245
+ distanceFromLast,
246
+ lastOrigin,
247
+ isProvided: true,
248
+ }),
249
+ [
250
+ activeBounds,
251
+ bounds,
252
+ hoveredId,
253
+ skipTransition,
254
+ visible,
255
+ distanceFromLast,
256
+ lastOrigin,
257
+ ]
258
+ );
259
+
260
+ return (
261
+ <ContextCardContext.Provider value={contextValue}>
262
+ {children}
263
+ {isMounted
264
+ ? createPortal(
265
+ <div
266
+ className="w-full h-full inset-0 fixed pointer-events-none z-[100000]"
267
+ ref={ref}
268
+ >
269
+ <div
270
+ className="min-w-max transition-all duration-150 ease-[easing-function:cubic-bezier(0.3,_0.57,_0.07,_0.95)] will-change-[transform,width,height] motion-reduce:!transition-none"
271
+ style={{
272
+ opacity: visible ? 1 : 0,
273
+ }}
274
+ >
275
+ <div
276
+ className={cn(
277
+ 'absolute bg-background-100 bg-clip-padding rounded-[6px] overflow-visible pointer-events-none z-[1000000] shadow-[var(--ds-shadow-tooltip),0_0_0_1px_var(--ds-background-100)] w-fit [--context-card-tip-stroke:#DBDBDB] dark-theme:[--context-card-tip-stroke:#252525]',
278
+ skipTransition
279
+ ? '!transition-none'
280
+ : 'transition-[transform,width,height] duration-250 ease-[easing-function:cubic-bezier(0.29,0.31,0.05,0.96)] will-change-[transform,width,height] motion-reduce:!transition-none'
281
+ )}
282
+ data-skip-transition={skipTransition}
283
+ style={{
284
+ transform: `translate(${activeBounds.x}px,${activeBounds.y}px)`,
285
+ width: activeBounds.width,
286
+ height: activeBounds.height,
287
+ }}
288
+ >
289
+ <div
290
+ className={cn(
291
+ 'w-[14px] h-[7px] absolute grid place-content-center z-[999999999] origin-center',
292
+ {
293
+ 'top-full -translate-x-1/2 rotate-0':
294
+ activeBounds.side === 'top',
295
+ 'right-full -translate-y-1/2 rotate-90 -mr-[3.5px]':
296
+ activeBounds.side === 'right',
297
+ 'bottom-full -translate-x-1/2 rotate-180':
298
+ activeBounds.side === 'bottom',
299
+ 'left-full -translate-y-1/2 rotate-[270deg] -ml-[3.5px]':
300
+ activeBounds.side === 'left',
301
+ },
302
+ skipTransition
303
+ ? '!transition-none'
304
+ : 'transition-[transform,width,height] duration-250 ease-[easing-function:cubic-bezier(0.29,0.31,0.05,0.96)] will-change-[transform,width,height] motion-reduce:!transition-none'
305
+ )}
306
+ style={{
307
+ ...(activeBounds.side === 'top' ||
308
+ activeBounds.side === 'bottom'
309
+ ? {
310
+ left: `calc(50% + ${activeBounds.arrowOffset.x}px)`,
311
+ }
312
+ : {
313
+ top: `calc(50% + ${activeBounds.arrowOffset.y}px)`,
314
+ }),
315
+ }}
316
+ data-skip-transition={skipTransition}
317
+ data-side={activeBounds.side}
318
+ >
319
+ <svg
320
+ width="14"
321
+ height="7"
322
+ viewBox="0 0 14 7"
323
+ fill="none"
324
+ xmlns="http://www.w3.org/2000/svg"
325
+ aria-hidden="true"
326
+ >
327
+ <g clipPath="url(#context-card-tip-clip)">
328
+ <path
329
+ d="M15 -0.5V0.5H12.9834L12.8184 0.508789C12.4377 0.550822 12.0853 0.738056 11.8359 1.03418L8.53027 4.95996C7.73114 5.90893 6.26886 5.90892 5.46973 4.95996L2.16406 1.03418C1.87905 0.695733 1.45907 0.5 1.0166 0.5H-1V-0.5H15Z"
330
+ fill="var(--ds-background-100)"
331
+ style={{
332
+ fill: 'var(--ds-background-100)',
333
+ fillOpacity: 1,
334
+ stroke: 'var(--context-card-tip-stroke)',
335
+ strokeOpacity: 1,
336
+ }}
337
+ />
338
+ </g>
339
+ <defs>
340
+ <clipPath id="context-card-tip-clip">
341
+ <rect
342
+ width="14"
343
+ height="7"
344
+ fill="white"
345
+ style={{ fill: 'white', fillOpacity: 1 }}
346
+ />
347
+ </clipPath>
348
+ </defs>
349
+ </svg>
350
+ </div>
351
+ <div ref={portalRef} />
352
+ </div>
353
+ </div>
354
+ </div>,
355
+ resolvedPortalTarget ?? document.body
356
+ )
357
+ : null}
358
+ </ContextCardContext.Provider>
359
+ );
360
+ }
361
+
362
+ export type Side = 'top' | 'bottom' | 'left' | 'right';
363
+ export type Align = 'start' | 'center' | 'end';
364
+
365
+ /** Props for the {@link ContextCardTrigger} component. */
366
+ export interface ContextCardTriggerProps
367
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, 'content'> {
368
+ /** The content rendered inside the context card popup. */
369
+ content: ReactNode;
370
+ /** Which side of the trigger to display the card. Defaults to `"right"`. */
371
+ side?: Side;
372
+ /** Alignment of the card relative to the trigger. Defaults to `"center"`. */
373
+ align?: Align;
374
+ /** Distance in pixels between the trigger and the card. Defaults to `16`. */
375
+ sideOffset?: number;
376
+ /** Offset in pixels along the alignment axis. Defaults to `0`. */
377
+ alignOffset?: number;
378
+ /** Whether to disable pointer events on the card content. */
379
+ ignoreCardPointerEvents?: boolean;
380
+ /** Whether to remove default padding from the card content. */
381
+ noPadding?: boolean;
382
+ /** Whether the trigger uses the Slot pattern to merge props onto its child. */
383
+ asChild?: boolean;
384
+ /** Can be used to hide the card when needed. */
385
+ hide?: boolean;
386
+ /** Time in milliseconds before the card is dismissed after mouse leaves. */
387
+ inactiveTimeoutMs?: number;
388
+ }
389
+
390
+ /**
391
+ * Displays a floating context card on hover with viewport-aware collision
392
+ * detection and animated position transitions between triggers in the same
393
+ * {@link ContextCardProvider}. Persists briefly after mouse leave.
394
+ */
395
+ export function ContextCardTrigger({
396
+ content,
397
+ children,
398
+ side = 'right',
399
+ align = 'center',
400
+ sideOffset = 16,
401
+ alignOffset = 0,
402
+ ignoreCardPointerEvents = false,
403
+ noPadding,
404
+ asChild,
405
+ className,
406
+ hide,
407
+ inactiveTimeoutMs = INACTIVE_TIMEOUT_MS,
408
+ }: ContextCardTriggerProps): JSX.Element {
409
+ const triggerRef = useRef<HTMLDivElement>(null);
410
+ const contentRef = useRef<HTMLDivElement>(null);
411
+ const [contentMeasureRef, contentMeasureBounds] = useMeasure();
412
+ const {
413
+ portalRef,
414
+ updateActiveContextCard,
415
+ activeId,
416
+ setActiveId,
417
+ hoveredId,
418
+ setHoveredId,
419
+ skipTransition,
420
+ rootVisible,
421
+ } = useContext(ContextCardContext);
422
+ const id = useId();
423
+
424
+ useEffect(() => {
425
+ return () => {
426
+ // Only clear the shared active card if this trigger was the active one;
427
+ // otherwise an unmounting inactive trigger would hide another trigger's card.
428
+ if (activeId.current === id) {
429
+ setActiveId(null);
430
+ }
431
+ };
432
+ }, []);
433
+
434
+ const reduceMotion = useReducedMotion();
435
+
436
+ const [localHover, setLocalHover] = useState(false);
437
+ const [persisting, setPersisting] = useState(false);
438
+ const [visible, setVisible] = useState(false);
439
+
440
+ const activeTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
441
+ const persistingTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
442
+ const openTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
443
+
444
+ useEffect(() => {
445
+ if (
446
+ localHover &&
447
+ contentMeasureBounds.width > 0 &&
448
+ contentMeasureBounds.height > 0
449
+ ) {
450
+ if (openTimeout.current) clearTimeout(openTimeout.current);
451
+ openTimeout.current = setTimeout(
452
+ () => {
453
+ setActiveId(id);
454
+ updateBounds();
455
+ },
456
+ rootVisible ? 0 : OPEN_DELAY_MS
457
+ );
458
+ }
459
+ return () => {
460
+ if (openTimeout.current) clearTimeout(openTimeout.current);
461
+ };
462
+ }, [localHover, contentMeasureBounds]);
463
+
464
+ const enterTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
465
+
466
+ useEffect(() => {
467
+ if (enterTimeout.current) clearTimeout(enterTimeout.current);
468
+ if (hoveredId !== id) {
469
+ // Exits are instant
470
+ setVisible(false);
471
+ } else {
472
+ // Enters can be delayed unless we wish to script the transition
473
+ enterTimeout.current = setTimeout(
474
+ () => {
475
+ setVisible(true);
476
+ },
477
+ reduceMotion || skipTransition ? 0 : ENTER_DELAY_MS
478
+ );
479
+ }
480
+ }, [hoveredId, id, reduceMotion, skipTransition]);
481
+
482
+ function updateBounds(): void {
483
+ if (!contentRef.current) return;
484
+ const bounds = computeBounds();
485
+ updateActiveContextCard({ id, ...bounds });
486
+ }
487
+
488
+ function computeBounds(sideOverride?: Side): {
489
+ origin: Point;
490
+ contentSize: { width: number; height: number };
491
+ side: Side;
492
+ arrowOffset: Point;
493
+ } {
494
+ if (!triggerRef.current) throw new Error('Trigger not found');
495
+ if (!contentRef.current) throw new Error('Content not found');
496
+ const appliedSide = sideOverride ?? side;
497
+ const triggerRect = triggerRef.current.getBoundingClientRect();
498
+ const contentRect = contentRef.current.getBoundingClientRect();
499
+ contentRef.current.style.width = 'max-content';
500
+ contentRef.current.style.position = 'absolute';
501
+ const contentSize = {
502
+ width: Math.max(contentRect.width, contentRef.current.offsetWidth),
503
+ height: contentRect.height,
504
+ };
505
+
506
+ let origin: Point;
507
+
508
+ const arrowWidth = 14;
509
+ const halfArrowWidth = arrowWidth / 2;
510
+ const halfTriggerWidth = triggerRect.width / 2;
511
+ const halfTriggerHeight = triggerRect.height / 2;
512
+ const halfContentWidth = contentSize.width / 2;
513
+ const halfContentHeight = contentSize.height / 2;
514
+
515
+ const top = triggerRect.top;
516
+ const left = triggerRect.left;
517
+
518
+ let alignX = left + halfTriggerWidth - halfContentWidth + alignOffset;
519
+ if (align === 'start') alignX = left + alignOffset;
520
+ if (align === 'end')
521
+ alignX = left + triggerRect.width - contentRect.width + alignOffset;
522
+
523
+ let alignY = top + halfTriggerHeight - halfContentHeight + alignOffset;
524
+ if (align === 'start') alignY = top + alignOffset;
525
+ if (align === 'end')
526
+ alignY = top + triggerRect.height - contentRect.height + alignOffset;
527
+
528
+ switch (appliedSide) {
529
+ case 'top':
530
+ origin = {
531
+ x: alignX,
532
+ y: top - contentRect.height - sideOffset,
533
+ };
534
+ break;
535
+ case 'right':
536
+ origin = {
537
+ x: left + triggerRect.width + sideOffset,
538
+ y: alignY,
539
+ };
540
+ break;
541
+ case 'bottom':
542
+ origin = {
543
+ x: alignX,
544
+ y: top + triggerRect.height + sideOffset,
545
+ };
546
+ break;
547
+ case 'left':
548
+ origin = {
549
+ x: left - contentRect.width - sideOffset,
550
+ y: alignY,
551
+ };
552
+ break;
553
+ default: {
554
+ const _exhaustive: never = appliedSide;
555
+ throw new Error(`Unknown side: ${String(_exhaustive)}`);
556
+ }
557
+ }
558
+
559
+ const viewportWidth = document.documentElement.clientWidth;
560
+ const viewportHeight = document.documentElement.clientHeight;
561
+
562
+ const isOffscreenLeft = origin.x < 0;
563
+ const isOffscreenRight = origin.x + contentRect.width > viewportWidth;
564
+ const isOffscreenTop = origin.y < 0;
565
+ const isOffscreenBottom = origin.y + contentRect.height > viewportHeight;
566
+
567
+ if (!sideOverride) {
568
+ const spaceAbove = top;
569
+ const spaceBelow = viewportHeight - (top + triggerRect.height);
570
+ const spaceLeft = left;
571
+ const spaceRight = viewportWidth - (left + triggerRect.width);
572
+
573
+ if (side === 'top' && isOffscreenTop && spaceBelow > spaceAbove) {
574
+ return computeBounds('bottom');
575
+ }
576
+ if (side === 'bottom' && isOffscreenBottom && spaceAbove > spaceBelow) {
577
+ return computeBounds('top');
578
+ }
579
+
580
+ if (side === 'right' && isOffscreenRight && spaceLeft > spaceRight) {
581
+ return computeBounds('left');
582
+ }
583
+ if (side === 'left' && isOffscreenLeft && spaceRight > spaceLeft) {
584
+ return computeBounds('right');
585
+ }
586
+
587
+ if (
588
+ (side === 'top' || side === 'bottom') &&
589
+ isOffscreenLeft &&
590
+ spaceRight > contentRect.width
591
+ ) {
592
+ return computeBounds('right');
593
+ }
594
+ if (
595
+ (side === 'top' || side === 'bottom') &&
596
+ isOffscreenRight &&
597
+ spaceLeft > contentRect.width
598
+ ) {
599
+ return computeBounds('left');
600
+ }
601
+
602
+ if (
603
+ (side === 'left' || side === 'right') &&
604
+ isOffscreenTop &&
605
+ spaceBelow > contentRect.height
606
+ ) {
607
+ return computeBounds('bottom');
608
+ }
609
+ if (
610
+ (side === 'left' || side === 'right') &&
611
+ isOffscreenBottom &&
612
+ spaceAbove > contentRect.height
613
+ ) {
614
+ return computeBounds('top');
615
+ }
616
+ }
617
+
618
+ const viewportPadding = 8;
619
+ if (appliedSide === 'left' || appliedSide === 'right') {
620
+ origin.y = Math.max(
621
+ viewportPadding,
622
+ Math.min(
623
+ origin.y,
624
+ viewportHeight - contentSize.height - viewportPadding
625
+ )
626
+ );
627
+ } else {
628
+ origin.x = Math.max(
629
+ viewportPadding,
630
+ Math.min(origin.x, viewportWidth - contentSize.width - viewportPadding)
631
+ );
632
+ }
633
+
634
+ // Always constrain Y position within viewport bounds
635
+ origin.y = Math.max(
636
+ viewportPadding,
637
+ Math.min(origin.y, viewportHeight - contentSize.height - viewportPadding)
638
+ );
639
+
640
+ const triggerCenterX = left + halfTriggerWidth;
641
+ const triggerCenterY = top + halfTriggerHeight;
642
+ const cardCenterX = origin.x + contentSize.width / 2;
643
+ const cardCenterY = origin.y + contentSize.height / 2;
644
+
645
+ let arrowOffsetX = triggerCenterX - cardCenterX;
646
+ let arrowOffsetY = triggerCenterY - cardCenterY;
647
+
648
+ // Clamp arrow offset to ensure it stays within card boundaries.
649
+ // Arrow should be at least halfArrowWidth (7px) from the card edges.
650
+ const maxArrowOffsetX = halfContentWidth - halfArrowWidth;
651
+ const maxArrowOffsetY = halfContentHeight - halfArrowWidth;
652
+
653
+ if (appliedSide === 'top' || appliedSide === 'bottom') {
654
+ arrowOffsetX = Math.max(
655
+ -maxArrowOffsetX,
656
+ Math.min(arrowOffsetX, maxArrowOffsetX)
657
+ );
658
+ }
659
+
660
+ if (appliedSide === 'left' || appliedSide === 'right') {
661
+ arrowOffsetY = Math.max(
662
+ -maxArrowOffsetY,
663
+ Math.min(arrowOffsetY, maxArrowOffsetY)
664
+ );
665
+ }
666
+
667
+ const arrowOffset = {
668
+ x: arrowOffsetX,
669
+ y: arrowOffsetY,
670
+ };
671
+
672
+ return { origin, contentSize, side: appliedSide, arrowOffset };
673
+ }
674
+
675
+ useEffect(() => {
676
+ document.addEventListener('scroll', updateBounds, true);
677
+ window.addEventListener('resize', updateBounds);
678
+ return () => {
679
+ document.removeEventListener('scroll', updateBounds, true);
680
+ window.removeEventListener('resize', updateBounds);
681
+ };
682
+ }, []);
683
+
684
+ function resetDestructiveTimeouts(): void {
685
+ if (activeTimeout.current) clearTimeout(activeTimeout.current);
686
+ if (persistingTimeout.current) clearTimeout(persistingTimeout.current);
687
+ if (openTimeout.current) clearTimeout(openTimeout.current);
688
+ }
689
+
690
+ function attemptDeactivate(): void {
691
+ if (activeTimeout.current) clearTimeout(activeTimeout.current);
692
+ activeTimeout.current = setTimeout(() => {
693
+ if (activeId.current !== id) return;
694
+ setActiveId(null);
695
+ }, inactiveTimeoutMs);
696
+ }
697
+
698
+ function attemptPersist(): void {
699
+ setPersisting(true);
700
+ if (persistingTimeout.current) clearTimeout(persistingTimeout.current);
701
+ persistingTimeout.current = setTimeout(() => {
702
+ setPersisting(false);
703
+ }, PERSIST_TIMEOUT_MS);
704
+ }
705
+
706
+ function onMouseEnter(): void {
707
+ resetDestructiveTimeouts();
708
+ setHoveredId(id);
709
+ setLocalHover(true);
710
+ }
711
+
712
+ function onMouseLeave(): void {
713
+ setLocalHover(false);
714
+ attemptDeactivate();
715
+ attemptPersist();
716
+ }
717
+
718
+ useEffect(() => {
719
+ // close instantly
720
+ if (hide) {
721
+ setLocalHover(false);
722
+ setActiveId(null);
723
+ }
724
+ }, [hide, setActiveId]);
725
+
726
+ function handleClick(e: React.MouseEvent): void {
727
+ // Close if clicking a link. We assume we want the card to disappear if navigating.
728
+ if (e.target instanceof HTMLElement && e.target.closest('a[href]')) {
729
+ onMouseLeave();
730
+ }
731
+ }
732
+
733
+ const Comp = asChild ? SlotRoot : 'div';
734
+
735
+ return (
736
+ <Comp
737
+ className={cn(
738
+ 'inline-flex cursor-pointer flex-[0_1_auto] overflow-hidden',
739
+ className
740
+ )}
741
+ onMouseEnter={onMouseEnter}
742
+ onMouseLeave={onMouseLeave}
743
+ onClick={handleClick}
744
+ ref={triggerRef}
745
+ >
746
+ <Slottable>{children}</Slottable>
747
+ {portalRef?.current && (localHover || persisting)
748
+ ? createPortal(
749
+ <div
750
+ className={cn(
751
+ 'absolute top-0 p-3 left-0 max-w-max transition-[transform,width,height] duration-250 ease-[easing-function:cubic-bezier(0.29,0.31,0.05,0.96)] will-change-[transform,width,height] motion-reduce:!transition-none',
752
+ skipTransition && '!transition-none'
753
+ )}
754
+ key={id}
755
+ ref={contentRef}
756
+ style={{
757
+ ...(noPadding ? { padding: 0 } : {}),
758
+ pointerEvents:
759
+ rootVisible && visible && !ignoreCardPointerEvents
760
+ ? 'all'
761
+ : 'none',
762
+ }}
763
+ >
764
+ <div
765
+ className={cn(
766
+ 'min-w-max transition-all duration-150 ease-[easing-function:cubic-bezier(0.3,_0.57,_0.07,_0.95)] will-change-[transform,width,height] motion-reduce:!transition-none',
767
+ skipTransition
768
+ ? '!transition-none'
769
+ : 'transition-[transform,width,height] duration-250 ease-[easing-function:cubic-bezier(0.29,0.31,0.05,0.96)] will-change-[transform,width,height] motion-reduce:!transition-none'
770
+ )}
771
+ ref={contentMeasureRef}
772
+ style={{
773
+ opacity: Number(visible),
774
+ }}
775
+ >
776
+ {content}
777
+ </div>
778
+ </div>,
779
+ portalRef.current
780
+ )
781
+ : null}
782
+ </Comp>
783
+ );
784
+ }