@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,482 @@
1
+ import type { Span, SpanEvent } from '../trace-viewer/types';
2
+ import { formatDuration, getHighResInMs } from '../trace-viewer/util/timing';
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Root bounds
6
+ // ---------------------------------------------------------------------------
7
+
8
+ export interface RootBounds {
9
+ startTime: number;
10
+ endTime: number;
11
+ duration: number;
12
+ }
13
+
14
+ export function computeRootBounds(spans: Span[]): RootBounds {
15
+ let minStart = Number.POSITIVE_INFINITY;
16
+ let maxEnd = Number.NEGATIVE_INFINITY;
17
+
18
+ for (const span of spans) {
19
+ const s = getHighResInMs(span.startTime);
20
+ const e = getHighResInMs(span.endTime);
21
+ if (s < minStart) minStart = s;
22
+ if (e > maxEnd) maxEnd = e;
23
+ }
24
+
25
+ if (!Number.isFinite(minStart) || !Number.isFinite(maxEnd)) {
26
+ return { startTime: 0, endTime: 1, duration: 1 };
27
+ }
28
+
29
+ const duration = Math.max(maxEnd - minStart, 1);
30
+ return { startTime: minStart, endTime: maxEnd, duration };
31
+ }
32
+
33
+ export function getSpanDurationMs(span: Span): number {
34
+ return Math.max(
35
+ 0,
36
+ getHighResInMs(span.endTime) - getHighResInMs(span.startTime)
37
+ );
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Time markers
42
+ // ---------------------------------------------------------------------------
43
+
44
+ export interface TimeMarker {
45
+ position: number;
46
+ label: string;
47
+ value: number;
48
+ }
49
+
50
+ const NICE_INTERVALS = [
51
+ 1, 2, 5, 10, 20, 50, 100, 200, 500, 1_000, 2_000, 5_000, 10_000, 20_000,
52
+ 50_000, 100_000, 200_000, 500_000, 1_000_000, 2_000_000, 5_000_000,
53
+ 10_000_000, 20_000_000, 50_000_000, 100_000_000, 200_000_000, 500_000_000,
54
+ 1_000_000_000, 2_000_000_000, 5_000_000_000,
55
+ ];
56
+
57
+ const MAX_MARKERS = 8;
58
+
59
+ function pickInterval(viewDuration: number, maxTicks: number): number {
60
+ for (const interval of NICE_INTERVALS) {
61
+ if (viewDuration / interval <= maxTicks) return interval;
62
+ }
63
+ return NICE_INTERVALS[NICE_INTERVALS.length - 1];
64
+ }
65
+
66
+ export function computeTimeMarkers(
67
+ viewDuration: number,
68
+ offset: number
69
+ ): TimeMarker[] {
70
+ if (viewDuration <= 0) return [];
71
+
72
+ const maxTicks = 6;
73
+ const interval = pickInterval(viewDuration, maxTicks);
74
+
75
+ const firstTick = Math.ceil(offset / interval) * interval;
76
+ const markers: TimeMarker[] = [];
77
+
78
+ for (let t = firstTick; t <= offset + viewDuration; t += interval) {
79
+ const position = (t - offset) / viewDuration;
80
+ if (position < -0.01 || position > 1.01) continue;
81
+ markers.push({
82
+ position: Math.min(Math.max(position, 0), 1),
83
+ label: formatDuration(Math.abs(t), true),
84
+ value: t,
85
+ });
86
+ if (markers.length >= MAX_MARKERS) break;
87
+ }
88
+
89
+ return markers;
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // Span gaps — time deltas between consecutive spans (Alt-key overlay)
94
+ // ---------------------------------------------------------------------------
95
+
96
+ export interface SpanGap {
97
+ gapMs: number;
98
+ leftFrac: number;
99
+ rightFrac: number;
100
+ rowIndex: number;
101
+ }
102
+
103
+ export function computeSpanGaps(
104
+ spans: Span[],
105
+ viewStart: number,
106
+ viewEnd: number
107
+ ): SpanGap[] {
108
+ const range = viewEnd - viewStart;
109
+ if (range <= 0) return [];
110
+
111
+ const gaps: SpanGap[] = [];
112
+ for (let i = 0; i < spans.length - 1; i++) {
113
+ const endTime = getHighResInMs(spans[i].endTime);
114
+ const startTime = getHighResInMs(spans[i + 1].startTime);
115
+ const gapMs = startTime - endTime;
116
+ if (gapMs <= 0) continue;
117
+
118
+ const leftFrac = Math.min(Math.max((endTime - viewStart) / range, 0), 1);
119
+ const rightFrac = Math.min(Math.max((startTime - viewStart) / range, 0), 1);
120
+ if (rightFrac - leftFrac < 0.001) continue;
121
+
122
+ gaps.push({ gapMs, leftFrac, rightFrac, rowIndex: i });
123
+ }
124
+ return gaps;
125
+ }
126
+
127
+ // ---------------------------------------------------------------------------
128
+ // Resource colors
129
+ // ---------------------------------------------------------------------------
130
+
131
+ export const RESOURCE_COLORS: Record<
132
+ string,
133
+ {
134
+ bg: string;
135
+ border: string;
136
+ errorBg?: string;
137
+ errorBorder?: string;
138
+ }
139
+ > = {
140
+ run: {
141
+ bg: 'var(--ds-blue-200)',
142
+ border: 'var(--ds-blue-500)',
143
+ errorBg: 'var(--ds-red-200)',
144
+ errorBorder: 'var(--ds-red-500)',
145
+ },
146
+ step: {
147
+ bg: 'var(--ds-green-200)',
148
+ border: 'var(--ds-green-500)',
149
+ errorBg: 'var(--ds-red-200)',
150
+ errorBorder: 'var(--ds-red-500)',
151
+ },
152
+ hook: {
153
+ bg: 'var(--ds-amber-200)',
154
+ border: 'var(--ds-amber-500)',
155
+ errorBg: 'var(--ds-red-200)',
156
+ errorBorder: 'var(--ds-red-500)',
157
+ },
158
+ sleep: {
159
+ bg: 'var(--ds-purple-200)',
160
+ border: 'var(--ds-purple-500)',
161
+ errorBg: 'var(--ds-red-200)',
162
+ errorBorder: 'var(--ds-red-500)',
163
+ },
164
+ default: {
165
+ bg: 'var(--ds-gray-200)',
166
+ border: 'var(--ds-gray-500)',
167
+ errorBg: 'var(--ds-red-200)',
168
+ errorBorder: 'var(--ds-red-500)',
169
+ },
170
+ };
171
+
172
+ export function getResourceColor(resource: string): {
173
+ bg: string;
174
+ border: string;
175
+ errorBg?: string;
176
+ errorBorder?: string;
177
+ } {
178
+ return RESOURCE_COLORS[resource] ?? RESOURCE_COLORS.default;
179
+ }
180
+
181
+ // ---------------------------------------------------------------------------
182
+ // Span segments — split a timeline bar into colored sections by event state
183
+ // ---------------------------------------------------------------------------
184
+
185
+ export type SegmentStatus =
186
+ | 'queued'
187
+ | 'running'
188
+ | 'failed'
189
+ | 'retrying'
190
+ | 'succeeded'
191
+ | 'waiting'
192
+ | 'sleeping'
193
+ | 'received';
194
+
195
+ export interface Segment {
196
+ startFraction: number;
197
+ endFraction: number;
198
+ status: SegmentStatus;
199
+ }
200
+
201
+ function clampFraction(value: number): number {
202
+ return Math.max(0, Math.min(1, value));
203
+ }
204
+
205
+ function timeToFraction(
206
+ time: number,
207
+ spanStart: number,
208
+ spanDuration: number
209
+ ): number {
210
+ if (spanDuration <= 0) return 0;
211
+ return clampFraction((time - spanStart) / spanDuration);
212
+ }
213
+
214
+ interface EventMark {
215
+ time: number;
216
+ type: string;
217
+ }
218
+
219
+ function sortedEventMarks(
220
+ events: SpanEvent[],
221
+ relevantNames: string[]
222
+ ): EventMark[] {
223
+ return events
224
+ .filter((e) => relevantNames.includes(e.name))
225
+ .map((e) => ({ time: getHighResInMs(e.timestamp), type: e.name }))
226
+ .sort((a, b) => a.time - b.time);
227
+ }
228
+
229
+ function computeStepSegmentsFromSpan(
230
+ startMs: number,
231
+ duration: number,
232
+ events: SpanEvent[]
233
+ ): Segment[] {
234
+ const segments: Segment[] = [];
235
+ if (duration <= 0) return segments;
236
+
237
+ const marks = sortedEventMarks(events, [
238
+ 'step_started',
239
+ 'step_retrying',
240
+ 'step_failed',
241
+ 'step_completed',
242
+ ]);
243
+
244
+ if (marks.length === 0) {
245
+ segments.push({ startFraction: 0, endFraction: 1, status: 'running' });
246
+ return segments;
247
+ }
248
+
249
+ const firstFraction = timeToFraction(marks[0].time, startMs, duration);
250
+ if (firstFraction > 0.001) {
251
+ segments.push({
252
+ startFraction: 0,
253
+ endFraction: firstFraction,
254
+ status: 'queued',
255
+ });
256
+ }
257
+
258
+ for (let i = 0; i < marks.length; i++) {
259
+ const mark = marks[i];
260
+ const markFrac = timeToFraction(mark.time, startMs, duration);
261
+ const nextMark = marks[i + 1];
262
+ const nextFrac = nextMark
263
+ ? timeToFraction(nextMark.time, startMs, duration)
264
+ : 1;
265
+
266
+ if (mark.type === 'step_started') {
267
+ if (i === marks.length - 1) {
268
+ segments.push({
269
+ startFraction: markFrac,
270
+ endFraction: 1,
271
+ status: 'succeeded',
272
+ });
273
+ } else {
274
+ const nextType = nextMark.type;
275
+ const attemptStatus: SegmentStatus =
276
+ nextType === 'step_retrying' || nextType === 'step_failed'
277
+ ? 'failed'
278
+ : nextType === 'step_completed'
279
+ ? 'succeeded'
280
+ : 'running';
281
+ segments.push({
282
+ startFraction: markFrac,
283
+ endFraction: nextFrac,
284
+ status: attemptStatus,
285
+ });
286
+ }
287
+ } else if (mark.type === 'step_retrying') {
288
+ segments.push({
289
+ startFraction: markFrac,
290
+ endFraction: nextFrac,
291
+ status: 'retrying',
292
+ });
293
+ } else if (mark.type === 'step_failed') {
294
+ if (markFrac < 0.999) {
295
+ segments.push({
296
+ startFraction: markFrac,
297
+ endFraction: 1,
298
+ status: 'failed',
299
+ });
300
+ }
301
+ }
302
+ }
303
+
304
+ return segments;
305
+ }
306
+
307
+ function computeHookSegmentsFromSpan(
308
+ startMs: number,
309
+ duration: number,
310
+ events: SpanEvent[]
311
+ ): Segment[] {
312
+ const segments: Segment[] = [];
313
+ if (duration <= 0) return segments;
314
+
315
+ const sorted = [...events]
316
+ .map((e) => ({ name: e.name, time: getHighResInMs(e.timestamp) }))
317
+ .sort((a, b) => a.time - b.time);
318
+
319
+ const received = sorted.find((e) => e.name === 'hook_received');
320
+ const disposed = sorted.find((e) => e.name === 'hook_disposed');
321
+
322
+ if (!received && !disposed) {
323
+ segments.push({ startFraction: 0, endFraction: 1, status: 'waiting' });
324
+ return segments;
325
+ }
326
+
327
+ const receivedFrac = received
328
+ ? timeToFraction(received.time, startMs, duration)
329
+ : null;
330
+ const disposedFrac = disposed
331
+ ? timeToFraction(disposed.time, startMs, duration)
332
+ : null;
333
+
334
+ if (receivedFrac !== null && receivedFrac > 0.001) {
335
+ segments.push({
336
+ startFraction: 0,
337
+ endFraction: receivedFrac,
338
+ status: 'waiting',
339
+ });
340
+ } else if (receivedFrac === null && disposedFrac !== null) {
341
+ segments.push({
342
+ startFraction: 0,
343
+ endFraction: disposedFrac,
344
+ status: 'waiting',
345
+ });
346
+ }
347
+
348
+ if (receivedFrac !== null) {
349
+ segments.push({
350
+ startFraction: receivedFrac,
351
+ endFraction: disposedFrac ?? 1,
352
+ status: 'received',
353
+ });
354
+ }
355
+
356
+ if (disposedFrac !== null && disposedFrac < 0.999) {
357
+ segments.push({
358
+ startFraction: disposedFrac,
359
+ endFraction: 1,
360
+ status: 'succeeded',
361
+ });
362
+ }
363
+
364
+ return segments;
365
+ }
366
+
367
+ function computeSleepSegmentsFromSpan(
368
+ _startMs: number,
369
+ duration: number,
370
+ _events: SpanEvent[]
371
+ ): Segment[] {
372
+ if (duration <= 0) return [];
373
+ return [{ startFraction: 0, endFraction: 1, status: 'sleeping' }];
374
+ }
375
+
376
+ function computeRunSegmentsFromSpan(
377
+ startMs: number,
378
+ duration: number,
379
+ activeStartMs: number | undefined,
380
+ events: SpanEvent[],
381
+ attributes: Record<string, unknown>
382
+ ): Segment[] {
383
+ const segments: Segment[] = [];
384
+ if (duration <= 0) return segments;
385
+
386
+ const sorted = [...events]
387
+ .map((e) => ({ name: e.name, time: getHighResInMs(e.timestamp) }))
388
+ .sort((a, b) => a.time - b.time);
389
+
390
+ const hasRunCreated = sorted.some((e) => e.name === 'run_created');
391
+
392
+ if (!hasRunCreated) {
393
+ const runData = attributes?.data as Record<string, unknown> | undefined;
394
+ const runStatus = runData?.status as string | undefined;
395
+ return computeV1RunSegments(startMs, duration, activeStartMs, runStatus);
396
+ }
397
+
398
+ const failedEvent = sorted.find((e) => e.name === 'run_failed');
399
+
400
+ let cursor = 0;
401
+ if (activeStartMs !== undefined && activeStartMs > startMs) {
402
+ const queuedFrac = timeToFraction(activeStartMs, startMs, duration);
403
+ if (queuedFrac > 0.001) {
404
+ segments.push({
405
+ startFraction: 0,
406
+ endFraction: queuedFrac,
407
+ status: 'queued',
408
+ });
409
+ cursor = queuedFrac;
410
+ }
411
+ }
412
+
413
+ segments.push({
414
+ startFraction: cursor,
415
+ endFraction: 1,
416
+ status: failedEvent ? 'failed' : 'running',
417
+ });
418
+
419
+ return segments;
420
+ }
421
+
422
+ function computeV1RunSegments(
423
+ startMs: number,
424
+ duration: number,
425
+ activeStartMs: number | undefined,
426
+ runStatus: string | undefined
427
+ ): Segment[] {
428
+ const segments: Segment[] = [];
429
+
430
+ let cursor = 0;
431
+ if (activeStartMs !== undefined && activeStartMs > startMs) {
432
+ const queuedFrac = timeToFraction(activeStartMs, startMs, duration);
433
+ if (queuedFrac > 0.001) {
434
+ segments.push({
435
+ startFraction: 0,
436
+ endFraction: queuedFrac,
437
+ status: 'queued',
438
+ });
439
+ cursor = queuedFrac;
440
+ }
441
+ }
442
+
443
+ segments.push({
444
+ startFraction: cursor,
445
+ endFraction: 1,
446
+ status: runStatus === 'failed' ? 'failed' : 'running',
447
+ });
448
+
449
+ return segments;
450
+ }
451
+
452
+ /**
453
+ * Compute event-based segments for a span. Dispatches on `span.resource`
454
+ * to the appropriate resource-type-specific segment builder.
455
+ * Returns an empty array for generic (non-workflow) spans.
456
+ */
457
+ export function computeSpanSegments(span: Span): Segment[] {
458
+ const startMs = getHighResInMs(span.startTime);
459
+ const duration = getSpanDurationMs(span);
460
+ const activeStartMs = span.activeStartTime
461
+ ? getHighResInMs(span.activeStartTime)
462
+ : undefined;
463
+
464
+ switch (span.resource) {
465
+ case 'step':
466
+ return computeStepSegmentsFromSpan(startMs, duration, span.events);
467
+ case 'hook':
468
+ return computeHookSegmentsFromSpan(startMs, duration, span.events);
469
+ case 'sleep':
470
+ return computeSleepSegmentsFromSpan(startMs, duration, span.events);
471
+ case 'run':
472
+ return computeRunSegmentsFromSpan(
473
+ startMs,
474
+ duration,
475
+ activeStartMs,
476
+ span.events,
477
+ span.attributes
478
+ );
479
+ default:
480
+ return [];
481
+ }
482
+ }
@@ -24,6 +24,7 @@ interface RunTraceViewProps {
24
24
  ) => Promise<void>;
25
25
  onCancelRun?: (runId: string) => Promise<void>;
26
26
  onStreamClick?: (streamId: string) => void;
27
+ onRunClick?: (runId: string) => void;
27
28
  onSpanSelect?: (info: SpanSelectionInfo) => void;
28
29
  onLoadMoreSpans?: () => void | Promise<void>;
29
30
  hasMoreSpans?: boolean;
@@ -42,6 +43,7 @@ export function RunTraceView({
42
43
  onResolveHook,
43
44
  onCancelRun,
44
45
  onStreamClick,
46
+ onRunClick,
45
47
  onSpanSelect,
46
48
  onLoadMoreSpans,
47
49
  hasMoreSpans,
@@ -71,6 +73,7 @@ export function RunTraceView({
71
73
  onResolveHook={onResolveHook}
72
74
  onCancelRun={onCancelRun}
73
75
  onStreamClick={onStreamClick}
76
+ onRunClick={onRunClick}
74
77
  onSpanSelect={onSpanSelect}
75
78
  onLoadMoreSpans={onLoadMoreSpans}
76
79
  hasMoreSpans={hasMoreSpans}