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

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