@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,216 @@
1
+ 'use client';
2
+
3
+ import {
4
+ type KeyboardEvent as ReactKeyboardEvent,
5
+ type PointerEvent as ReactPointerEvent,
6
+ type RefObject,
7
+ useEffect,
8
+ useRef,
9
+ useState,
10
+ } from 'react';
11
+ import { cn } from '../../../lib/cn';
12
+
13
+ interface DraggableBorderProps {
14
+ /** The panel whose width this border adjusts (measured on interaction). */
15
+ element: RefObject<HTMLElement | null>;
16
+ /** Which edge of the panel the border sits on. */
17
+ position: 'left' | 'right';
18
+ /**
19
+ * Called with the next width (px) while dragging or keyboard-resizing.
20
+ * The owner is responsible for clamping.
21
+ */
22
+ onWidthChange: (width: number) => void;
23
+ /** Double-click reset. */
24
+ onReset?: () => void;
25
+ 'aria-label': string;
26
+ 'aria-controls'?: string;
27
+ 'aria-valuemin'?: number;
28
+ 'aria-valuemax'?: number;
29
+ 'aria-valuenow'?: number;
30
+ }
31
+
32
+ /**
33
+ * Overlay drag handle straddling a panel edge: an invisible strip over the
34
+ * panel's border whose center line highlights on hover/drag, with
35
+ * double-click reset, pointer-capture dragging (works with touch/pen, no
36
+ * ghost image), and keyboard/ARIA window-splitter support.
37
+ *
38
+ * The panel's positioning ancestor must not clip overflow — the strip hangs
39
+ * ~8px past the panel edge.
40
+ */
41
+ export function DraggableBorder({
42
+ element,
43
+ position,
44
+ onWidthChange,
45
+ onReset,
46
+ 'aria-label': ariaLabel,
47
+ 'aria-controls': ariaControls,
48
+ 'aria-valuemin': ariaValueMin,
49
+ 'aria-valuemax': ariaValueMax,
50
+ 'aria-valuenow': ariaValueNow,
51
+ }: DraggableBorderProps) {
52
+ const [isDragging, setIsDragging] = useState(false);
53
+ const pointerIdRef = useRef<number | null>(null);
54
+ const startXRef = useRef(0);
55
+ const startWidthRef = useRef(0);
56
+ const pendingWidthRef = useRef(0);
57
+ const rafRef = useRef<number | null>(null);
58
+ const movedRef = useRef(false);
59
+ const lastDragEndAtRef = useRef(0);
60
+
61
+ useEffect(() => {
62
+ return () => {
63
+ if (rafRef.current) cancelAnimationFrame(rafRef.current);
64
+ };
65
+ }, []);
66
+
67
+ // Suppress text selection for the duration of a drag. Effect cleanup also
68
+ // covers the handle unmounting mid-drag (e.g. Escape closing the panel).
69
+ useEffect(() => {
70
+ if (!isDragging) return;
71
+ const previous = document.body.style.userSelect;
72
+ document.body.style.userSelect = 'none';
73
+ return () => {
74
+ document.body.style.userSelect = previous;
75
+ };
76
+ }, [isDragging]);
77
+
78
+ const stopDrag = () => {
79
+ pointerIdRef.current = null;
80
+ setIsDragging(false);
81
+ if (rafRef.current) {
82
+ cancelAnimationFrame(rafRef.current);
83
+ rafRef.current = null;
84
+ // Flush the final position that was still waiting on the next frame.
85
+ if (movedRef.current) onWidthChange(pendingWidthRef.current);
86
+ }
87
+ if (movedRef.current) {
88
+ movedRef.current = false;
89
+ lastDragEndAtRef.current = performance.now();
90
+ }
91
+ };
92
+
93
+ const handlePointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {
94
+ // Ignore secondary pointers (e.g. a second finger) while a drag is active
95
+ // so they can't re-baseline the gesture.
96
+ if (pointerIdRef.current !== null) return;
97
+ const el = element.current;
98
+ if (!el) return;
99
+ if (e.pointerType === 'mouse' && e.button !== 0) return;
100
+ e.currentTarget.setPointerCapture(e.pointerId);
101
+ pointerIdRef.current = e.pointerId;
102
+ startXRef.current = e.clientX;
103
+ startWidthRef.current = el.offsetWidth;
104
+ movedRef.current = false;
105
+ setIsDragging(true);
106
+ };
107
+
108
+ // Pointer capture routes move/up events to the strip itself, so no
109
+ // document-level listeners are needed.
110
+ const handlePointerMove = (e: ReactPointerEvent<HTMLDivElement>) => {
111
+ if (pointerIdRef.current !== e.pointerId) return;
112
+ if (Math.abs(e.clientX - startXRef.current) > 4) {
113
+ movedRef.current = true;
114
+ }
115
+ const delta =
116
+ position === 'left'
117
+ ? startXRef.current - e.clientX
118
+ : e.clientX - startXRef.current;
119
+ pendingWidthRef.current = startWidthRef.current + delta;
120
+ if (!rafRef.current) {
121
+ rafRef.current = requestAnimationFrame(() => {
122
+ rafRef.current = null;
123
+ onWidthChange(pendingWidthRef.current);
124
+ });
125
+ }
126
+ };
127
+
128
+ const handlePointerEnd = (e: ReactPointerEvent<HTMLDivElement>) => {
129
+ if (pointerIdRef.current !== e.pointerId) return;
130
+ stopDrag();
131
+ };
132
+
133
+ const handleLostPointerCapture = (e: ReactPointerEvent<HTMLDivElement>) => {
134
+ if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current) {
135
+ return;
136
+ }
137
+ stopDrag();
138
+ };
139
+
140
+ const handleDoubleClick = () => {
141
+ // Pointer-capture drags still emit click/dblclick (unlike HTML5 drag), so
142
+ // a dblclick during or right after a real drag is two resize nudges, not
143
+ // a reset request.
144
+ if (
145
+ movedRef.current ||
146
+ performance.now() - lastDragEndAtRef.current < 500
147
+ ) {
148
+ return;
149
+ }
150
+ onReset?.();
151
+ };
152
+
153
+ const handleKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>) => {
154
+ const el = element.current;
155
+ if (!el) return;
156
+ const step = e.shiftKey ? 64 : 16;
157
+ // Arrows move the border in screen direction: for a left-edge border,
158
+ // ArrowLeft grows the panel.
159
+ const grow = position === 'left' ? 1 : -1;
160
+ let next: number | null = null;
161
+ if (e.key === 'ArrowLeft') {
162
+ next = el.offsetWidth + grow * step;
163
+ } else if (e.key === 'ArrowRight') {
164
+ next = el.offsetWidth - grow * step;
165
+ } else if (e.key === 'Home' && ariaValueMin !== undefined) {
166
+ next = ariaValueMin;
167
+ } else if (e.key === 'End' && ariaValueMax !== undefined) {
168
+ next = ariaValueMax;
169
+ }
170
+ if (next === null) return;
171
+ e.preventDefault();
172
+ onWidthChange(next);
173
+ };
174
+
175
+ return (
176
+ // biome-ignore lint/a11y/useSemanticElements: the WAI-ARIA window-splitter pattern requires a focusable div with role=separator; <hr> cannot receive focus or drag interactions
177
+ <div
178
+ role="separator"
179
+ aria-orientation="vertical"
180
+ aria-label={ariaLabel}
181
+ aria-controls={ariaControls}
182
+ aria-valuemin={ariaValueMin}
183
+ aria-valuemax={ariaValueMax}
184
+ aria-valuenow={ariaValueNow}
185
+ tabIndex={0}
186
+ className={cn(
187
+ 'group absolute inset-y-0 z-10 w-[17px] cursor-col-resize touch-none outline-none',
188
+ position === 'left' ? '-left-2' : '-right-2'
189
+ )}
190
+ onPointerDown={handlePointerDown}
191
+ onPointerMove={handlePointerMove}
192
+ onPointerUp={handlePointerEnd}
193
+ onPointerCancel={handlePointerEnd}
194
+ onLostPointerCapture={handleLostPointerCapture}
195
+ onDoubleClick={handleDoubleClick}
196
+ onKeyDown={handleKeyDown}
197
+ >
198
+ {/* Center line: sits exactly over the panel's 1px border and highlights
199
+ on hover/drag/focus (delayed so incidental mouse-overs don't flash).
200
+ pointer-events-none is load-bearing: without it this 1px line is a
201
+ hit target, so a double-click aimed at the line lands one click on
202
+ the line and (after sub-pixel jitter) the other on the strip. A
203
+ native dblclick only fires when both clicks share a target, so the
204
+ reset silently fails on the line but works just beside it. Keeping
205
+ the line inert makes the separator div the sole target everywhere. */}
206
+ <span
207
+ aria-hidden
208
+ className={cn(
209
+ 'pointer-events-none absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-transparent transition-colors delay-75 duration-100',
210
+ 'group-hover:bg-gray-500 group-focus-visible:bg-[var(--ds-focus-color)]',
211
+ isDragging && 'bg-gray-500'
212
+ )}
213
+ />
214
+ </div>
215
+ );
216
+ }
@@ -1,13 +1,18 @@
1
1
  import { Circle } from 'lucide-react';
2
- import { cn } from '../../../lib/utils';
3
- import type { Span } from '../../trace-viewer/types';
4
- import { formatDuration, getHighResInMs } from '../../trace-viewer/util/timing';
2
+ import { useRef } from 'react';
3
+ import { cn } from '../../../lib/cn';
4
+ import { formatDurationPrecise } from '../../trace-viewer/util/timing';
5
5
  import {
6
- WorkflowIcon,
7
- WebhookIcon,
8
6
  SleepIcon,
9
7
  StepForwardIcon,
8
+ WebhookIcon,
9
+ WorkflowIcon,
10
10
  } from '../icons';
11
+ import { isSpanDimmedBySearch, type SpanSearchResult } from '../search';
12
+ import type { Span } from '../types';
13
+ import { getSpanDurationMs, isSpanErrored } from '../utils';
14
+ import { MiddleTruncate } from './middle-truncate/middle-truncate';
15
+ import { ROW_HEIGHT_PX, useRowWindow } from './use-row-window';
11
16
 
12
17
  interface EventStyle {
13
18
  icon: React.ComponentType<{ className?: string }>;
@@ -17,7 +22,7 @@ interface EventStyle {
17
22
  const eventStyles: Record<string, EventStyle> = {
18
23
  run: { icon: WorkflowIcon, className: 'text-blue-900' },
19
24
  step: { icon: StepForwardIcon, className: 'text-green-900' },
20
- hook: { icon: WebhookIcon, className: 'text-yellow-900' },
25
+ hook: { icon: WebhookIcon, className: 'text-gray-900' },
21
26
  sleep: { icon: SleepIcon, className: 'text-gray-900' },
22
27
  };
23
28
 
@@ -26,6 +31,8 @@ const defaultStyle: EventStyle = {
26
31
  className: 'text-gray-900',
27
32
  };
28
33
 
34
+ const ROW_HEIGHT_CLASS = 'h-10';
35
+
29
36
  function getEventStyle(resource: string, isErrored: boolean): EventStyle {
30
37
  const style = eventStyles[resource] ?? defaultStyle;
31
38
  return {
@@ -37,15 +44,16 @@ function getEventStyle(resource: string, isErrored: boolean): EventStyle {
37
44
  const EventRow = ({
38
45
  span,
39
46
  isSelected,
47
+ isDimmed,
40
48
  onSelectSpan,
41
49
  }: {
42
50
  span: Span;
43
51
  isSelected: boolean;
52
+ isDimmed?: boolean;
44
53
  onSelectSpan: (spanId: string) => void;
45
54
  }) => {
46
- const durationMs = getHighResInMs(span.duration);
47
- const isErrored =
48
- (span.attributes.data as Record<string, unknown>).status === 'failed';
55
+ const durationMs = getSpanDurationMs(span);
56
+ const isErrored = isSpanErrored(span);
49
57
  const { icon: Icon, className: tagClassName } = getEventStyle(
50
58
  span.resource,
51
59
  isErrored
@@ -53,24 +61,32 @@ const EventRow = ({
53
61
 
54
62
  return (
55
63
  <li
56
- className="overflow-clip group"
64
+ className={cn(
65
+ 'relative overflow-clip group transition-opacity',
66
+ ROW_HEIGHT_CLASS,
67
+ isDimmed && 'opacity-35'
68
+ )}
57
69
  role="treeitem"
58
70
  aria-selected={isSelected}
59
71
  aria-expanded={isSelected}
60
72
  aria-level={1}
61
73
  onClick={() => onSelectSpan(span.spanId)}
62
74
  >
63
- <div className="hover:bg-gray-100 group-aria-selected:bg-gray-100 group-aria-selected:hover:bg-gray-200 hover:aria-selected:bg-gray-100 rounded-sm px-2 h-[34px] py-1.5 flex">
64
- <div className="flex items-center gap-2">
65
- <span className={tagClassName}>
66
- <Icon className="w-4 h-4" />
67
- </span>
68
- <span className="text-label-14">{span.name}</span>
69
- </div>
70
- <div className="ml-auto">
71
- <span className="text-label-14 text-gray-900 tabular-nums">
72
- {formatDuration(durationMs)}
73
- </span>
75
+ <div className="h-full hover:bg-gray-100 group-aria-selected:bg-gray-100 group-aria-selected:hover:bg-gray-200">
76
+ <div className="flex h-full min-w-0 items-center pl-4 pr-2">
77
+ <div className="flex min-w-0 flex-1 items-center gap-2">
78
+ <span className={cn('shrink-0', tagClassName)}>
79
+ <Icon className="w-4 h-4" />
80
+ </span>
81
+ <span className="min-w-0 text-label-14">
82
+ <MiddleTruncate value={span.name} />
83
+ </span>
84
+ </div>
85
+ <div className="ml-2 shrink-0">
86
+ <span className="text-label-14 text-gray-900 tabular-nums">
87
+ {formatDurationPrecise(durationMs)}
88
+ </span>
89
+ </div>
74
90
  </div>
75
91
  </div>
76
92
  </li>
@@ -80,28 +96,37 @@ const EventRow = ({
80
96
  const EventList = ({
81
97
  spans,
82
98
  activeSpanId,
99
+ searchResult,
83
100
  onSelectSpan,
84
101
  }: {
85
102
  spans: Span[];
86
103
  activeSpanId: string | null;
104
+ searchResult: SpanSearchResult;
87
105
  onSelectSpan: (spanId: string) => void;
88
106
  }) => {
107
+ const listRef = useRef<HTMLUListElement>(null);
108
+ const { start, end } = useRowWindow(listRef, spans.length, ROW_HEIGHT_PX);
109
+
89
110
  return (
90
111
  <ul
112
+ ref={listRef}
91
113
  id="event-list"
92
114
  role="tree"
93
- className="block min-h-0 overflow-visible px-2 py-2"
115
+ className="block min-h-0 overflow-visible divide-y divide-gray-400 border-b border-gray-400"
116
+ style={{
117
+ paddingTop: start * ROW_HEIGHT_PX,
118
+ paddingBottom: (spans.length - end) * ROW_HEIGHT_PX,
119
+ }}
94
120
  >
95
- {spans.map((span) => {
96
- return (
97
- <EventRow
98
- key={span.spanId}
99
- span={span}
100
- isSelected={span.spanId === activeSpanId}
101
- onSelectSpan={onSelectSpan}
102
- />
103
- );
104
- })}
121
+ {spans.slice(start, end).map((span) => (
122
+ <EventRow
123
+ key={span.spanId}
124
+ span={span}
125
+ isSelected={span.spanId === activeSpanId}
126
+ isDimmed={isSpanDimmedBySearch(span.spanId, searchResult)}
127
+ onSelectSpan={onSelectSpan}
128
+ />
129
+ ))}
105
130
  </ul>
106
131
  );
107
132
  };
@@ -0,0 +1,114 @@
1
+ import { ELLIPSIS, toGraphemes } from './truncate';
2
+
3
+ interface GetMiddleTruncateCopyTextOptions {
4
+ prefixText: string;
5
+ selectionEnd: number;
6
+ selectionStart: number;
7
+ suffixText: string;
8
+ value: string;
9
+ }
10
+
11
+ interface GetMiddleTruncateCopyTextFromSelectionTextOptions {
12
+ prefixText: string;
13
+ selectionText: string;
14
+ suffixText: string;
15
+ value: string;
16
+ }
17
+
18
+ function getMiddleTruncateCopyText({
19
+ prefixText,
20
+ selectionEnd,
21
+ selectionStart,
22
+ suffixText,
23
+ value,
24
+ }: GetMiddleTruncateCopyTextOptions): string | null {
25
+ const visibleText = prefixText + ELLIPSIS + suffixText;
26
+
27
+ if (
28
+ selectionStart < 0 ||
29
+ selectionEnd > visibleText.length ||
30
+ selectionStart >= selectionEnd
31
+ ) {
32
+ return null;
33
+ }
34
+
35
+ if (selectionStart === 0 && selectionEnd === visibleText.length) {
36
+ return value;
37
+ }
38
+
39
+ const ellipsisStart = prefixText.length;
40
+ const ellipsisEnd = ellipsisStart + ELLIPSIS.length;
41
+
42
+ if (selectionStart > ellipsisStart || selectionEnd < ellipsisEnd) {
43
+ return null;
44
+ }
45
+
46
+ const originalGraphemes = toGraphemes(value);
47
+ const selectedPrefixGraphemeCount = toGraphemes(
48
+ visibleText.slice(0, selectionStart)
49
+ ).length;
50
+ const selectedSuffixGraphemeCount = toGraphemes(
51
+ visibleText.slice(ellipsisEnd, selectionEnd)
52
+ ).length;
53
+ const suffixStart = originalGraphemes.length - toGraphemes(suffixText).length;
54
+
55
+ return originalGraphemes
56
+ .slice(
57
+ selectedPrefixGraphemeCount,
58
+ suffixStart + selectedSuffixGraphemeCount
59
+ )
60
+ .join('');
61
+ }
62
+
63
+ function getMiddleTruncateCopyTextFromSelectionText({
64
+ prefixText,
65
+ selectionText,
66
+ suffixText,
67
+ value,
68
+ }: GetMiddleTruncateCopyTextFromSelectionTextOptions): string | null {
69
+ const visibleText = prefixText + ELLIPSIS + suffixText;
70
+ const trimmedSelectionText = selectionText.trim();
71
+
72
+ if (!trimmedSelectionText) {
73
+ return null;
74
+ }
75
+
76
+ const leading = selectionText.slice(
77
+ 0,
78
+ selectionText.length - selectionText.trimStart().length
79
+ );
80
+ const trailing = selectionText.slice(selectionText.trimEnd().length);
81
+
82
+ if (trimmedSelectionText === visibleText) {
83
+ return leading + value + trailing;
84
+ }
85
+
86
+ if (!trimmedSelectionText.includes(ELLIPSIS)) {
87
+ return null;
88
+ }
89
+
90
+ const selectionStart = visibleText.indexOf(trimmedSelectionText);
91
+ if (selectionStart === -1) {
92
+ return null;
93
+ }
94
+
95
+ const selectionEnd = selectionStart + trimmedSelectionText.length;
96
+ const mappedText = getMiddleTruncateCopyText({
97
+ prefixText,
98
+ selectionEnd,
99
+ selectionStart,
100
+ suffixText,
101
+ value,
102
+ });
103
+
104
+ return mappedText === null ? null : leading + mappedText + trailing;
105
+ }
106
+
107
+ export {
108
+ getMiddleTruncateCopyText,
109
+ getMiddleTruncateCopyTextFromSelectionText,
110
+ };
111
+ export type {
112
+ GetMiddleTruncateCopyTextFromSelectionTextOptions,
113
+ GetMiddleTruncateCopyTextOptions,
114
+ };
@@ -0,0 +1,157 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useRef } from 'react';
4
+ import type { ClipboardEvent, ComponentPropsWithoutRef, JSX } from 'react';
5
+ import { cn } from '../../../../lib/cn';
6
+ import {
7
+ getMiddleTruncateCopyText,
8
+ getMiddleTruncateCopyTextFromSelectionText,
9
+ } from './copy-selection';
10
+ import { ELLIPSIS } from './truncate';
11
+ import { useMiddleTruncate } from './use-middle-truncate';
12
+
13
+ /** Props for the {@link MiddleTruncate} component. */
14
+ interface MiddleTruncateProps
15
+ extends Omit<ComponentPropsWithoutRef<'span'>, 'children'> {
16
+ /** The full text string to display, truncated from the middle when it overflows. */
17
+ value: string;
18
+ }
19
+
20
+ function getRangeOffsets(
21
+ container: HTMLElement,
22
+ range: Range
23
+ ): { end: number; start: number } | null {
24
+ if (
25
+ !container.contains(range.startContainer) ||
26
+ !container.contains(range.endContainer)
27
+ ) {
28
+ return null;
29
+ }
30
+
31
+ const startRange = document.createRange();
32
+ startRange.selectNodeContents(container);
33
+ startRange.setEnd(range.startContainer, range.startOffset);
34
+
35
+ const endRange = document.createRange();
36
+ endRange.selectNodeContents(container);
37
+ endRange.setEnd(range.endContainer, range.endOffset);
38
+
39
+ return {
40
+ end: endRange.toString().length,
41
+ start: startRange.toString().length,
42
+ };
43
+ }
44
+
45
+ /**
46
+ * @gdoc
47
+ *
48
+ * Truncates text from the middle with an ellipsis when it overflows, preserving the beginning and end (e.g. file paths, URLs). Copy behavior restores the full untruncated text to the clipboard.
49
+ * Renders a `<span>` element.
50
+ *
51
+ * Documentation: [Geist Middle Truncate](https://vercel.com/geist/middle-truncate)
52
+ *
53
+ * @param value - Full text string to display.
54
+ */
55
+ function MiddleTruncate({
56
+ value,
57
+ className,
58
+ onCopy: onCopyProp,
59
+ ...props
60
+ }: MiddleTruncateProps): JSX.Element {
61
+ const { ref, measureRef, displayText, isTruncated, prefixText, suffixText } =
62
+ useMiddleTruncate(value);
63
+ const visibleRef = useRef<HTMLSpanElement | null>(null);
64
+
65
+ const handleCopy = useCallback(
66
+ (e: ClipboardEvent<HTMLSpanElement>) => {
67
+ onCopyProp?.(e);
68
+ if (e.defaultPrevented || !isTruncated) return;
69
+
70
+ const selection = window.getSelection();
71
+ if (!selection || selection.rangeCount === 0) return;
72
+ const selectionText = selection.toString();
73
+ if (!selectionText) return;
74
+
75
+ // Only intercept when the selection is fully within this element.
76
+ // If the user selected across elements (e.g. a table row), let
77
+ // the browser's default behavior preserve surrounding context.
78
+ const range = selection.getRangeAt(0);
79
+ const visibleEl = visibleRef.current;
80
+ let copyText: string | null = null;
81
+
82
+ if (
83
+ e.currentTarget.contains(range.startContainer) &&
84
+ e.currentTarget.contains(range.endContainer) &&
85
+ visibleEl
86
+ ) {
87
+ const offsets = getRangeOffsets(visibleEl, range);
88
+ if (offsets) {
89
+ copyText = getMiddleTruncateCopyText({
90
+ prefixText,
91
+ selectionEnd: offsets.end,
92
+ selectionStart: offsets.start,
93
+ suffixText,
94
+ value,
95
+ });
96
+ }
97
+ }
98
+
99
+ copyText ??= getMiddleTruncateCopyTextFromSelectionText({
100
+ prefixText,
101
+ selectionText,
102
+ suffixText,
103
+ value,
104
+ });
105
+
106
+ if (copyText === null) return;
107
+
108
+ e.preventDefault();
109
+ e.clipboardData.setData('text/plain', copyText);
110
+ },
111
+ [onCopyProp, isTruncated, prefixText, suffixText, value]
112
+ );
113
+
114
+ return (
115
+ <span
116
+ title={isTruncated ? value : undefined}
117
+ {...props}
118
+ ref={ref}
119
+ className={cn(
120
+ 'relative inline-grid min-w-0 max-w-full overflow-hidden whitespace-nowrap',
121
+ className
122
+ )}
123
+ onCopy={handleCopy}
124
+ >
125
+ {isTruncated && <span className="sr-only select-none">{value}</span>}
126
+ <span
127
+ aria-hidden="true"
128
+ className="pointer-events-none col-start-1 row-start-1 invisible select-none whitespace-nowrap"
129
+ >
130
+ {value}
131
+ </span>
132
+ <span
133
+ aria-hidden={isTruncated || undefined}
134
+ className="col-start-1 row-start-1 min-w-0 overflow-hidden"
135
+ ref={visibleRef}
136
+ >
137
+ {isTruncated ? (
138
+ <>
139
+ <span>{prefixText}</span>
140
+ <span>{ELLIPSIS}</span>
141
+ <span>{suffixText}</span>
142
+ </>
143
+ ) : (
144
+ displayText
145
+ )}
146
+ </span>
147
+ <span
148
+ aria-hidden="true"
149
+ className="pointer-events-none absolute left-0 top-0 inline-block invisible select-none whitespace-nowrap"
150
+ ref={measureRef}
151
+ />
152
+ </span>
153
+ );
154
+ }
155
+
156
+ export { MiddleTruncate };
157
+ export type { MiddleTruncateProps };