@workflow/web-shared 5.0.0-beta.1 → 5.0.0-beta.11

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 (298) hide show
  1. package/README.md +1 -1
  2. package/dist/components/error-boundary.js +1 -1
  3. package/dist/components/event-list-view.d.ts +18 -1
  4. package/dist/components/event-list-view.d.ts.map +1 -1
  5. package/dist/components/event-list-view.js +219 -125
  6. package/dist/components/hook-actions.js +1 -1
  7. package/dist/components/index.d.ts +5 -1
  8. package/dist/components/index.d.ts.map +1 -1
  9. package/dist/components/index.js +6 -2
  10. package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
  11. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
  12. package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
  13. package/dist/components/new-trace-viewer/components/event-list.d.ts +10 -0
  14. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
  15. package/dist/components/new-trace-viewer/components/event-list.js +44 -0
  16. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts +18 -0
  17. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
  18. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +54 -0
  19. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts +20 -0
  20. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
  21. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
  22. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +25 -0
  23. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
  24. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +101 -0
  25. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts +20 -0
  26. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +1 -0
  27. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js +114 -0
  28. package/dist/components/new-trace-viewer/components/split-pane.d.ts +15 -0
  29. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
  30. package/dist/components/new-trace-viewer/components/split-pane.js +105 -0
  31. package/dist/components/new-trace-viewer/components/timeline.d.ts +34 -0
  32. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
  33. package/dist/components/new-trace-viewer/components/timeline.js +221 -0
  34. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts +2 -0
  35. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -0
  36. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.js +19 -0
  37. package/dist/components/new-trace-viewer/components/use-row-window.d.ts +20 -0
  38. package/dist/components/new-trace-viewer/components/use-row-window.d.ts.map +1 -0
  39. package/dist/components/new-trace-viewer/components/use-row-window.js +69 -0
  40. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  41. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  42. package/dist/components/new-trace-viewer/context.js +47 -0
  43. package/dist/components/new-trace-viewer/detail-panel.d.ts +10 -0
  44. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -0
  45. package/dist/components/new-trace-viewer/detail-panel.js +13 -0
  46. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  47. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  48. package/dist/components/new-trace-viewer/icons.js +15 -0
  49. package/dist/components/new-trace-viewer/search.d.ts +19 -0
  50. package/dist/components/new-trace-viewer/search.d.ts.map +1 -0
  51. package/dist/components/new-trace-viewer/search.js +134 -0
  52. package/dist/components/new-trace-viewer/trace-viewer.d.ts +11 -0
  53. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  54. package/dist/components/new-trace-viewer/trace-viewer.js +400 -0
  55. package/dist/components/new-trace-viewer/types.d.ts +1 -0
  56. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  57. package/dist/components/new-trace-viewer/types.js +2 -0
  58. package/dist/components/new-trace-viewer/util/constants.d.ts +1 -0
  59. package/dist/components/new-trace-viewer/util/constants.d.ts.map +1 -0
  60. package/dist/components/new-trace-viewer/util/constants.js +2 -0
  61. package/dist/components/new-trace-viewer/utils.d.ts +46 -0
  62. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  63. package/dist/components/new-trace-viewer/utils.js +330 -0
  64. package/dist/components/run-trace-view.d.ts +2 -1
  65. package/dist/components/run-trace-view.d.ts.map +1 -1
  66. package/dist/components/run-trace-view.js +3 -3
  67. package/dist/components/sidebar/attribute-panel.d.ts +7 -1
  68. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  69. package/dist/components/sidebar/attribute-panel.js +131 -70
  70. package/dist/components/sidebar/conversation-view.js +1 -1
  71. package/dist/components/sidebar/copyable-data-block.d.ts +1 -0
  72. package/dist/components/sidebar/copyable-data-block.d.ts.map +1 -1
  73. package/dist/components/sidebar/copyable-data-block.js +16 -15
  74. package/dist/components/sidebar/detail-card.d.ts +5 -6
  75. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  76. package/dist/components/sidebar/detail-card.js +32 -16
  77. package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
  78. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  79. package/dist/components/sidebar/entity-detail-panel.js +20 -37
  80. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  81. package/dist/components/sidebar/events-list.js +21 -15
  82. package/dist/components/sidebar/resolve-hook-modal.js +1 -1
  83. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  84. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  85. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  86. package/dist/components/stream-viewer.d.ts +1 -1
  87. package/dist/components/stream-viewer.d.ts.map +1 -1
  88. package/dist/components/stream-viewer.js +3 -27
  89. package/dist/components/trace-viewer/components/map.js +1 -1
  90. package/dist/components/trace-viewer/components/markers.js +1 -1
  91. package/dist/components/trace-viewer/components/node.js +1 -1
  92. package/dist/components/trace-viewer/components/search-input.js +1 -1
  93. package/dist/components/trace-viewer/components/search.js +1 -1
  94. package/dist/components/trace-viewer/components/span-content.js +1 -1
  95. package/dist/components/trace-viewer/components/span-detail-panel.js +1 -1
  96. package/dist/components/trace-viewer/components/span-segments.js +1 -1
  97. package/dist/components/trace-viewer/components/span-strategies.js +1 -1
  98. package/dist/components/trace-viewer/components/ui.js +1 -1
  99. package/dist/components/trace-viewer/components/zoom-button.js +1 -1
  100. package/dist/components/trace-viewer/components/zoom-icons.js +1 -1
  101. package/dist/components/trace-viewer/context.js +1 -1
  102. package/dist/components/trace-viewer/index.js +1 -1
  103. package/dist/components/trace-viewer/trace-viewer.js +1 -1
  104. package/dist/components/trace-viewer/types.js +1 -1
  105. package/dist/components/trace-viewer/util/constants.js +1 -1
  106. package/dist/components/trace-viewer/util/scrollbar-width.js +1 -1
  107. package/dist/components/trace-viewer/util/timing.d.ts +1 -1
  108. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
  109. package/dist/components/trace-viewer/util/timing.js +6 -9
  110. package/dist/components/trace-viewer/util/tree.js +1 -1
  111. package/dist/components/trace-viewer/util/use-immediate-style.js +1 -1
  112. package/dist/components/trace-viewer/util/use-streaming-spans.js +1 -1
  113. package/dist/components/trace-viewer/util/use-trackpad-zoom.js +1 -1
  114. package/dist/components/trace-viewer/worker.js +1 -1
  115. package/dist/components/trace-viewer-new.d.ts +14 -0
  116. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  117. package/dist/components/trace-viewer-new.js +22 -0
  118. package/dist/components/ui/alert.js +1 -1
  119. package/dist/components/ui/button.d.ts +10 -0
  120. package/dist/components/ui/button.d.ts.map +1 -0
  121. package/dist/components/ui/button.js +46 -0
  122. package/dist/components/ui/card.js +1 -1
  123. package/dist/components/ui/data-inspector.d.ts +39 -1
  124. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  125. package/dist/components/ui/data-inspector.js +207 -22
  126. package/dist/components/ui/decrypt-button.js +1 -1
  127. package/dist/components/ui/error-card.js +1 -1
  128. package/dist/components/ui/error-stack-block.d.ts.map +1 -1
  129. package/dist/components/ui/error-stack-block.js +27 -19
  130. package/dist/components/ui/icon-button.d.ts +19 -0
  131. package/dist/components/ui/icon-button.d.ts.map +1 -0
  132. package/dist/components/ui/icon-button.js +29 -0
  133. package/dist/components/ui/inspector-theme.d.ts +9 -10
  134. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  135. package/dist/components/ui/inspector-theme.js +39 -72
  136. package/dist/components/ui/kbd.d.ts +6 -0
  137. package/dist/components/ui/kbd.d.ts.map +1 -0
  138. package/dist/components/ui/kbd.js +6 -0
  139. package/dist/components/ui/load-more-button.js +1 -1
  140. package/dist/components/ui/menu-dropdown.js +1 -1
  141. package/dist/components/ui/skeleton.js +1 -1
  142. package/dist/components/ui/spinner.js +1 -1
  143. package/dist/components/ui/timestamp-tooltip.js +1 -1
  144. package/dist/components/ui/tooltip.d.ts +10 -0
  145. package/dist/components/ui/tooltip.d.ts.map +1 -0
  146. package/dist/components/ui/tooltip.js +12 -0
  147. package/dist/components/workflow-trace-view.d.ts +3 -1
  148. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  149. package/dist/components/workflow-trace-view.js +10 -5
  150. package/dist/components/workflow-traces/event-colors.js +1 -1
  151. package/dist/components/workflow-traces/trace-colors.js +1 -1
  152. package/dist/components/workflow-traces/trace-span-construction.d.ts +2 -2
  153. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  154. package/dist/components/workflow-traces/trace-span-construction.js +19 -6
  155. package/dist/components/workflow-traces/trace-time-utils.js +1 -1
  156. package/dist/hooks/use-dark-mode.js +1 -1
  157. package/dist/hooks/use-intersection-observer.d.ts +17 -0
  158. package/dist/hooks/use-intersection-observer.d.ts.map +1 -0
  159. package/dist/hooks/use-intersection-observer.js +29 -0
  160. package/dist/hooks/use-load-more-on-scroll.d.ts +21 -0
  161. package/dist/hooks/use-load-more-on-scroll.d.ts.map +1 -0
  162. package/dist/hooks/use-load-more-on-scroll.js +28 -0
  163. package/dist/hooks/use-reduced-motion.d.ts +10 -0
  164. package/dist/hooks/use-reduced-motion.d.ts.map +1 -0
  165. package/dist/hooks/use-reduced-motion.js +29 -0
  166. package/dist/index.d.ts +3 -1
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +3 -2
  169. package/dist/lib/event-analysis.js +1 -1
  170. package/dist/lib/event-materialization.js +1 -1
  171. package/dist/lib/exact-event-search-id.d.ts +25 -0
  172. package/dist/lib/exact-event-search-id.d.ts.map +1 -0
  173. package/dist/lib/exact-event-search-id.js +39 -0
  174. package/dist/lib/hydration.d.ts +14 -1
  175. package/dist/lib/hydration.d.ts.map +1 -1
  176. package/dist/lib/hydration.js +157 -4
  177. package/dist/lib/stream-display.d.ts +18 -0
  178. package/dist/lib/stream-display.d.ts.map +1 -0
  179. package/dist/lib/stream-display.js +30 -0
  180. package/dist/lib/toast.js +1 -1
  181. package/dist/lib/trace-builder.d.ts.map +1 -1
  182. package/dist/lib/trace-builder.js +3 -2
  183. package/dist/lib/utils.d.ts +9 -9
  184. package/dist/lib/utils.d.ts.map +1 -1
  185. package/dist/lib/utils.js +27 -32
  186. package/dist/styles.css +475 -1
  187. package/package.json +6 -4
  188. package/src/components/event-list-view.tsx +421 -262
  189. package/src/components/index.ts +13 -1
  190. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  191. package/src/components/new-trace-viewer/components/event-list.tsx +135 -0
  192. package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
  193. package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
  194. package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
  195. package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
  196. package/src/components/new-trace-viewer/components/split-pane.tsx +198 -0
  197. package/src/components/new-trace-viewer/components/timeline.tsx +560 -0
  198. package/src/components/new-trace-viewer/components/trace-viewer-skeleton.tsx +94 -0
  199. package/src/components/new-trace-viewer/components/use-row-window.ts +93 -0
  200. package/src/components/new-trace-viewer/context.tsx +90 -0
  201. package/src/components/new-trace-viewer/detail-panel.tsx +55 -0
  202. package/src/components/new-trace-viewer/icons.tsx +79 -0
  203. package/src/components/new-trace-viewer/search.ts +178 -0
  204. package/src/components/new-trace-viewer/trace-viewer.tsx +762 -0
  205. package/src/components/new-trace-viewer/types.ts +0 -0
  206. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  207. package/src/components/new-trace-viewer/utils.ts +482 -0
  208. package/src/components/run-trace-view.tsx +3 -0
  209. package/src/components/sidebar/attribute-panel.tsx +240 -196
  210. package/src/components/sidebar/copyable-data-block.tsx +49 -27
  211. package/src/components/sidebar/detail-card.tsx +119 -59
  212. package/src/components/sidebar/entity-detail-panel.tsx +92 -148
  213. package/src/components/sidebar/events-list.tsx +51 -76
  214. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  215. package/src/components/stream-viewer.tsx +18 -40
  216. package/src/components/trace-viewer/util/timing.ts +5 -9
  217. package/src/components/trace-viewer-new.tsx +57 -0
  218. package/src/components/ui/button.tsx +68 -0
  219. package/src/components/ui/data-inspector.tsx +427 -31
  220. package/src/components/ui/error-stack-block.tsx +39 -30
  221. package/src/components/ui/icon-button.tsx +53 -0
  222. package/src/components/ui/inspector-theme.ts +39 -81
  223. package/src/components/ui/kbd.tsx +21 -0
  224. package/src/components/ui/tooltip.tsx +47 -0
  225. package/src/components/workflow-trace-view.tsx +12 -2
  226. package/src/components/workflow-traces/trace-span-construction.ts +24 -6
  227. package/src/hooks/use-intersection-observer.ts +57 -0
  228. package/src/hooks/use-load-more-on-scroll.ts +49 -0
  229. package/src/hooks/use-reduced-motion.ts +34 -0
  230. package/src/index.ts +9 -0
  231. package/src/lib/exact-event-search-id.ts +67 -0
  232. package/src/lib/hydration.ts +169 -1
  233. package/src/lib/stream-display.ts +48 -0
  234. package/src/lib/trace-builder.ts +2 -1
  235. package/src/lib/utils.ts +27 -32
  236. package/src/styles.css +475 -1
  237. package/dist/components/error-boundary.js.map +0 -1
  238. package/dist/components/event-list-view.js.map +0 -1
  239. package/dist/components/hook-actions.js.map +0 -1
  240. package/dist/components/index.js.map +0 -1
  241. package/dist/components/run-trace-view.js.map +0 -1
  242. package/dist/components/sidebar/attribute-panel.js.map +0 -1
  243. package/dist/components/sidebar/conversation-view.js.map +0 -1
  244. package/dist/components/sidebar/copyable-data-block.js.map +0 -1
  245. package/dist/components/sidebar/detail-card.js.map +0 -1
  246. package/dist/components/sidebar/entity-detail-panel.js.map +0 -1
  247. package/dist/components/sidebar/events-list.js.map +0 -1
  248. package/dist/components/sidebar/resolve-hook-modal.js.map +0 -1
  249. package/dist/components/stream-viewer.js.map +0 -1
  250. package/dist/components/trace-viewer/components/map.js.map +0 -1
  251. package/dist/components/trace-viewer/components/markers.js.map +0 -1
  252. package/dist/components/trace-viewer/components/node.js.map +0 -1
  253. package/dist/components/trace-viewer/components/search-input.js.map +0 -1
  254. package/dist/components/trace-viewer/components/search.js.map +0 -1
  255. package/dist/components/trace-viewer/components/span-content.js.map +0 -1
  256. package/dist/components/trace-viewer/components/span-detail-panel.js.map +0 -1
  257. package/dist/components/trace-viewer/components/span-segments.js.map +0 -1
  258. package/dist/components/trace-viewer/components/span-strategies.js.map +0 -1
  259. package/dist/components/trace-viewer/components/ui.js.map +0 -1
  260. package/dist/components/trace-viewer/components/zoom-button.js.map +0 -1
  261. package/dist/components/trace-viewer/components/zoom-icons.js.map +0 -1
  262. package/dist/components/trace-viewer/context.js.map +0 -1
  263. package/dist/components/trace-viewer/index.js.map +0 -1
  264. package/dist/components/trace-viewer/trace-viewer.js.map +0 -1
  265. package/dist/components/trace-viewer/types.js.map +0 -1
  266. package/dist/components/trace-viewer/util/constants.js.map +0 -1
  267. package/dist/components/trace-viewer/util/scrollbar-width.js.map +0 -1
  268. package/dist/components/trace-viewer/util/timing.js.map +0 -1
  269. package/dist/components/trace-viewer/util/tree.js.map +0 -1
  270. package/dist/components/trace-viewer/util/use-immediate-style.js.map +0 -1
  271. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +0 -1
  272. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
  273. package/dist/components/trace-viewer/worker.js.map +0 -1
  274. package/dist/components/ui/alert.js.map +0 -1
  275. package/dist/components/ui/card.js.map +0 -1
  276. package/dist/components/ui/data-inspector.js.map +0 -1
  277. package/dist/components/ui/decrypt-button.js.map +0 -1
  278. package/dist/components/ui/error-card.js.map +0 -1
  279. package/dist/components/ui/error-stack-block.js.map +0 -1
  280. package/dist/components/ui/inspector-theme.js.map +0 -1
  281. package/dist/components/ui/load-more-button.js.map +0 -1
  282. package/dist/components/ui/menu-dropdown.js.map +0 -1
  283. package/dist/components/ui/skeleton.js.map +0 -1
  284. package/dist/components/ui/spinner.js.map +0 -1
  285. package/dist/components/ui/timestamp-tooltip.js.map +0 -1
  286. package/dist/components/workflow-trace-view.js.map +0 -1
  287. package/dist/components/workflow-traces/event-colors.js.map +0 -1
  288. package/dist/components/workflow-traces/trace-colors.js.map +0 -1
  289. package/dist/components/workflow-traces/trace-span-construction.js.map +0 -1
  290. package/dist/components/workflow-traces/trace-time-utils.js.map +0 -1
  291. package/dist/hooks/use-dark-mode.js.map +0 -1
  292. package/dist/index.js.map +0 -1
  293. package/dist/lib/event-analysis.js.map +0 -1
  294. package/dist/lib/event-materialization.js.map +0 -1
  295. package/dist/lib/hydration.js.map +0 -1
  296. package/dist/lib/toast.js.map +0 -1
  297. package/dist/lib/trace-builder.js.map +0 -1
  298. package/dist/lib/utils.js.map +0 -1
@@ -0,0 +1,167 @@
1
+ 'use client';
2
+
3
+ import {
4
+ useCallback,
5
+ useEffect,
6
+ useLayoutEffect,
7
+ useMemo,
8
+ useRef,
9
+ useState,
10
+ } from 'react';
11
+ import { middleTruncate, toGraphemes } from './truncate';
12
+
13
+ const useIsomorphicLayoutEffect =
14
+ typeof window === 'undefined' ? useEffect : useLayoutEffect;
15
+
16
+ interface MiddleTruncateState {
17
+ displayText: string;
18
+ isTruncated: boolean;
19
+ prefixGraphemeCount: number;
20
+ prefixText: string;
21
+ suffixGraphemeCount: number;
22
+ suffixText: string;
23
+ }
24
+
25
+ function createFullState(
26
+ value: string,
27
+ graphemes: string[]
28
+ ): MiddleTruncateState {
29
+ return {
30
+ displayText: value,
31
+ isTruncated: false,
32
+ prefixGraphemeCount: graphemes.length,
33
+ prefixText: value,
34
+ suffixGraphemeCount: 0,
35
+ suffixText: '',
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Middle-truncation logic. Returns refs to attach to the container and measurement elements, plus the truncated display text. Recalculates on resize, font loading, and value changes.
41
+ * Powers the `<MiddleTruncate>` (`<span>`) component.
42
+ *
43
+ * Documentation: [Geist Middle Truncate](https://vercel.com/geist/middle-truncate)
44
+ *
45
+ * @param value - Full text string to truncate.
46
+ */
47
+ function useMiddleTruncate(value: string): {
48
+ ref: React.RefObject<HTMLSpanElement | null>;
49
+ measureRef: React.RefObject<HTMLSpanElement | null>;
50
+ displayText: string;
51
+ isTruncated: boolean;
52
+ prefixGraphemeCount: number;
53
+ prefixText: string;
54
+ suffixGraphemeCount: number;
55
+ suffixText: string;
56
+ } {
57
+ const graphemes = useMemo(() => toGraphemes(value), [value]);
58
+ const fullState = useMemo(
59
+ () => createFullState(value, graphemes),
60
+ [graphemes, value]
61
+ );
62
+ const ref = useRef<HTMLSpanElement | null>(null);
63
+ const measureRef = useRef<HTMLSpanElement | null>(null);
64
+ const [state, setState] = useState<MiddleTruncateState>(() => fullState);
65
+ const rafRef = useRef<number>(0);
66
+
67
+ const updateState = useCallback((nextState: MiddleTruncateState) => {
68
+ setState((currentState) => {
69
+ if (
70
+ currentState.displayText === nextState.displayText &&
71
+ currentState.isTruncated === nextState.isTruncated &&
72
+ currentState.prefixText === nextState.prefixText &&
73
+ currentState.prefixGraphemeCount === nextState.prefixGraphemeCount &&
74
+ currentState.suffixText === nextState.suffixText &&
75
+ currentState.suffixGraphemeCount === nextState.suffixGraphemeCount
76
+ ) {
77
+ return currentState;
78
+ }
79
+
80
+ return nextState;
81
+ });
82
+ }, []);
83
+
84
+ const recalculate = useCallback(() => {
85
+ const el = ref.current;
86
+ const measureEl = measureRef.current;
87
+ if (!el || !measureEl) return;
88
+
89
+ const available = el.clientWidth;
90
+
91
+ if (available <= 0) {
92
+ updateState(fullState);
93
+ return;
94
+ }
95
+
96
+ const measure = (text: string): number => {
97
+ measureEl.textContent = text;
98
+ return measureEl.scrollWidth;
99
+ };
100
+
101
+ const fullWidth = measure(value);
102
+
103
+ if (fullWidth <= available) {
104
+ updateState(fullState);
105
+ return;
106
+ }
107
+
108
+ const result = middleTruncate(graphemes, available, measure, fullWidth);
109
+ updateState({
110
+ displayText: result.text,
111
+ isTruncated: result.truncated,
112
+ prefixGraphemeCount: result.prefixGraphemeCount,
113
+ prefixText: result.prefixText,
114
+ suffixGraphemeCount: result.suffixGraphemeCount,
115
+ suffixText: result.suffixText,
116
+ });
117
+ }, [fullState, graphemes, updateState, value]);
118
+
119
+ // Measure on mount and when value changes - before paint
120
+ useIsomorphicLayoutEffect(() => {
121
+ recalculate();
122
+ }, [recalculate]);
123
+
124
+ // ResizeObserver + font loading for ongoing responsiveness
125
+ useEffect(() => {
126
+ const el = ref.current;
127
+ if (!el) return;
128
+
129
+ const debouncedRecalc = (): void => {
130
+ cancelAnimationFrame(rafRef.current);
131
+ rafRef.current = requestAnimationFrame(recalculate);
132
+ };
133
+
134
+ const ro =
135
+ typeof ResizeObserver !== 'undefined'
136
+ ? new ResizeObserver(debouncedRecalc)
137
+ : null;
138
+ ro?.observe(el);
139
+ window.addEventListener('resize', debouncedRecalc);
140
+
141
+ const onFontsLoaded = (): void => {
142
+ debouncedRecalc();
143
+ };
144
+ const fontSet = 'fonts' in document ? document.fonts : null;
145
+ fontSet?.addEventListener?.('loadingdone', onFontsLoaded);
146
+
147
+ return () => {
148
+ ro?.disconnect();
149
+ window.removeEventListener('resize', debouncedRecalc);
150
+ cancelAnimationFrame(rafRef.current);
151
+ fontSet?.removeEventListener?.('loadingdone', onFontsLoaded);
152
+ };
153
+ }, [recalculate]);
154
+
155
+ return {
156
+ ref,
157
+ measureRef,
158
+ displayText: state.displayText,
159
+ isTruncated: state.isTruncated,
160
+ prefixGraphemeCount: state.prefixGraphemeCount,
161
+ prefixText: state.prefixText,
162
+ suffixGraphemeCount: state.suffixGraphemeCount,
163
+ suffixText: state.suffixText,
164
+ };
165
+ }
166
+
167
+ export { useMiddleTruncate };
@@ -0,0 +1,198 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Children,
5
+ type ReactNode,
6
+ type PointerEvent as ReactPointerEvent,
7
+ type RefObject,
8
+ useCallback,
9
+ useEffect,
10
+ useRef,
11
+ useState,
12
+ } from 'react';
13
+ import { cn } from '../../../lib/utils';
14
+
15
+ const GUTTER_PX = 1;
16
+ const MIN_PX = 50;
17
+ const DEFAULT_START_PX = 340;
18
+
19
+ export function Divider() {
20
+ return <div aria-hidden className="h-full w-px shrink-0 bg-gray-alpha-400" />;
21
+ }
22
+
23
+ export interface SplitPaneProps {
24
+ children: ReactNode;
25
+ className?: string;
26
+ /** Fixed pixel width for the start (left) pane. Default 220. */
27
+ defaultStartWidth?: number;
28
+ /** Fixed (non-scrolling) header rendered above the start pane. */
29
+ startHeader?: ReactNode;
30
+ /** Fixed (non-scrolling) header rendered above the end pane. */
31
+ endHeader?: ReactNode;
32
+ scrollContainerRef?: RefObject<HTMLDivElement | null>;
33
+ }
34
+
35
+ export function SplitPane({
36
+ children,
37
+ className,
38
+ defaultStartWidth = DEFAULT_START_PX,
39
+ startHeader,
40
+ endHeader,
41
+ scrollContainerRef,
42
+ }: SplitPaneProps) {
43
+ const parts = Children.toArray(children);
44
+ if (parts.length !== 2) {
45
+ throw new Error('SplitPane expects exactly two children');
46
+ }
47
+ const [start, end] = parts;
48
+
49
+ const [startPx, setStartPx] = useState(defaultStartWidth);
50
+ const [isDragging, setIsDragging] = useState(false);
51
+ const containerRef = useRef<HTMLDivElement>(null);
52
+ const gutterRef = useRef<HTMLDivElement>(null);
53
+ const rafRef = useRef<number | null>(null);
54
+ const pendingPx = useRef(defaultStartWidth);
55
+ const pointerIdRef = useRef<number | null>(null);
56
+
57
+ useEffect(() => {
58
+ return () => {
59
+ if (rafRef.current) cancelAnimationFrame(rafRef.current);
60
+ };
61
+ }, []);
62
+
63
+ const setContainerRef = useCallback(
64
+ (node: HTMLDivElement | null) => {
65
+ containerRef.current = node;
66
+ if (scrollContainerRef) {
67
+ scrollContainerRef.current = node;
68
+ }
69
+ },
70
+ [scrollContainerRef]
71
+ );
72
+
73
+ const clampPx = useCallback((px: number) => {
74
+ const el = containerRef.current;
75
+ if (!el) return px;
76
+ const maxPx = el.getBoundingClientRect().width - MIN_PX - GUTTER_PX;
77
+ return Math.min(maxPx, Math.max(MIN_PX, px));
78
+ }, []);
79
+
80
+ useEffect(() => {
81
+ if (!isDragging) return;
82
+
83
+ const onPointerMove = (e: globalThis.PointerEvent) => {
84
+ if (e.pointerId !== pointerIdRef.current) return;
85
+ const container = containerRef.current;
86
+ if (!container) return;
87
+ const rect = container.getBoundingClientRect();
88
+ pendingPx.current = clampPx(e.clientX - rect.left);
89
+ if (!rafRef.current) {
90
+ rafRef.current = requestAnimationFrame(() => {
91
+ rafRef.current = null;
92
+ setStartPx(pendingPx.current);
93
+ });
94
+ }
95
+ };
96
+
97
+ const onPointerUp = (e: globalThis.PointerEvent) => {
98
+ if (e.pointerId !== pointerIdRef.current) return;
99
+ const gutter = gutterRef.current;
100
+ if (gutter?.hasPointerCapture(e.pointerId)) {
101
+ gutter.releasePointerCapture(e.pointerId);
102
+ }
103
+ if (rafRef.current) {
104
+ cancelAnimationFrame(rafRef.current);
105
+ rafRef.current = null;
106
+ }
107
+ pointerIdRef.current = null;
108
+ setIsDragging(false);
109
+ };
110
+
111
+ document.addEventListener('pointermove', onPointerMove);
112
+ document.addEventListener('pointerup', onPointerUp);
113
+ document.addEventListener('pointercancel', onPointerUp);
114
+
115
+ return () => {
116
+ document.removeEventListener('pointermove', onPointerMove);
117
+ document.removeEventListener('pointerup', onPointerUp);
118
+ document.removeEventListener('pointercancel', onPointerUp);
119
+ };
120
+ }, [isDragging, clampPx]);
121
+
122
+ const handlePointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {
123
+ e.currentTarget.setPointerCapture(e.pointerId);
124
+ pointerIdRef.current = e.pointerId;
125
+ setIsDragging(true);
126
+ };
127
+
128
+ const handleLostPointerCapture = () => {
129
+ pointerIdRef.current = null;
130
+ setIsDragging(false);
131
+ if (rafRef.current) {
132
+ cancelAnimationFrame(rafRef.current);
133
+ rafRef.current = null;
134
+ }
135
+ };
136
+
137
+ const colTemplate = `${startPx}px ${GUTTER_PX}px minmax(${MIN_PX}px, 1fr)`;
138
+ const hasHeaders = startHeader != null || endHeader != null;
139
+
140
+ const gutter = (
141
+ <div
142
+ ref={gutterRef}
143
+ className="relative z-20 isolate flex shrink-0 cursor-col-resize justify-center"
144
+ onPointerDown={handlePointerDown}
145
+ onLostPointerCapture={handleLostPointerCapture}
146
+ >
147
+ <span
148
+ className="pointer-events-none relative z-10 h-full w-px shrink-0 bg-gray-alpha-400"
149
+ aria-hidden
150
+ />
151
+ </div>
152
+ );
153
+
154
+ if (hasHeaders) {
155
+ return (
156
+ <div className={cn('flex flex-col h-full min-h-0', className)}>
157
+ <div
158
+ className="shrink-0 grid"
159
+ style={{ gridTemplateColumns: colTemplate }}
160
+ >
161
+ <div>{startHeader}</div>
162
+ <div className="flex justify-center">
163
+ <span className="h-full w-px bg-gray-alpha-400" aria-hidden />
164
+ </div>
165
+ <div>{endHeader}</div>
166
+ </div>
167
+ <div
168
+ ref={setContainerRef}
169
+ className={cn(
170
+ 'grid flex-1 min-h-0 overflow-x-hidden overflow-y-auto',
171
+ isDragging && 'select-none'
172
+ )}
173
+ style={{ gridTemplateColumns: colTemplate }}
174
+ >
175
+ {start}
176
+ {gutter}
177
+ {end}
178
+ </div>
179
+ </div>
180
+ );
181
+ }
182
+
183
+ return (
184
+ <div
185
+ ref={setContainerRef}
186
+ className={cn(
187
+ 'grid h-full min-h-0 content-start overflow-x-hidden overflow-y-auto',
188
+ isDragging && 'select-none',
189
+ className
190
+ )}
191
+ style={{ gridTemplateColumns: colTemplate, height: '100%' }}
192
+ >
193
+ {start}
194
+ {gutter}
195
+ {end}
196
+ </div>
197
+ );
198
+ }