@workflow/web-shared 4.1.0-beta.47 → 4.1.0-beta.49

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 (301) hide show
  1. package/README.md +26 -52
  2. package/dist/components/error-boundary.d.ts.map +1 -0
  3. package/dist/{error-boundary.js → components/error-boundary.js} +1 -1
  4. package/dist/components/error-boundary.js.map +1 -0
  5. package/dist/{event-list-view.d.ts → components/event-list-view.d.ts} +2 -3
  6. package/dist/components/event-list-view.d.ts.map +1 -0
  7. package/dist/{event-list-view.js → components/event-list-view.js} +9 -17
  8. package/dist/components/event-list-view.js.map +1 -0
  9. package/dist/{hook-actions.d.ts → components/hook-actions.d.ts} +2 -3
  10. package/dist/components/hook-actions.d.ts.map +1 -0
  11. package/dist/{hook-actions.js → components/hook-actions.js} +3 -4
  12. package/dist/components/hook-actions.js.map +1 -0
  13. package/dist/components/index.d.ts +10 -0
  14. package/dist/components/index.d.ts.map +1 -0
  15. package/dist/components/index.js +8 -0
  16. package/dist/components/index.js.map +1 -0
  17. package/dist/components/run-trace-view.d.ts +22 -0
  18. package/dist/components/run-trace-view.d.ts.map +1 -0
  19. package/dist/components/run-trace-view.js +11 -0
  20. package/dist/components/run-trace-view.js.map +1 -0
  21. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -0
  22. package/dist/{sidebar → components/sidebar}/attribute-panel.js +3 -3
  23. package/dist/components/sidebar/attribute-panel.js.map +1 -0
  24. package/dist/components/sidebar/conversation-view.d.ts.map +1 -0
  25. package/dist/components/sidebar/conversation-view.js.map +1 -0
  26. package/dist/components/sidebar/detail-card.d.ts.map +1 -0
  27. package/dist/components/sidebar/detail-card.js.map +1 -0
  28. package/dist/components/sidebar/entity-detail-panel.d.ts +32 -0
  29. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -0
  30. package/dist/{sidebar → components/sidebar}/entity-detail-panel.js +61 -49
  31. package/dist/components/sidebar/entity-detail-panel.js.map +1 -0
  32. package/dist/components/sidebar/events-list.d.ts +8 -0
  33. package/dist/components/sidebar/events-list.d.ts.map +1 -0
  34. package/dist/components/sidebar/events-list.js +16 -0
  35. package/dist/components/sidebar/events-list.js.map +1 -0
  36. package/dist/components/sidebar/resolve-hook-modal.d.ts.map +1 -0
  37. package/dist/components/sidebar/resolve-hook-modal.js.map +1 -0
  38. package/dist/components/stream-viewer.d.ts +18 -0
  39. package/dist/components/stream-viewer.d.ts.map +1 -0
  40. package/dist/{stream-viewer.js → components/stream-viewer.js} +1 -59
  41. package/dist/components/stream-viewer.js.map +1 -0
  42. package/dist/components/trace-viewer/components/map.d.ts.map +1 -0
  43. package/dist/components/trace-viewer/components/map.js.map +1 -0
  44. package/dist/components/trace-viewer/components/markers.d.ts.map +1 -0
  45. package/dist/components/trace-viewer/components/markers.js.map +1 -0
  46. package/dist/components/trace-viewer/components/node.d.ts.map +1 -0
  47. package/dist/components/trace-viewer/components/node.js.map +1 -0
  48. package/dist/components/trace-viewer/components/search-input.d.ts.map +1 -0
  49. package/dist/components/trace-viewer/components/search-input.js.map +1 -0
  50. package/dist/components/trace-viewer/components/search.d.ts.map +1 -0
  51. package/dist/components/trace-viewer/components/search.js.map +1 -0
  52. package/dist/components/trace-viewer/components/span-detail-panel.d.ts.map +1 -0
  53. package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -0
  54. package/dist/components/trace-viewer/components/ui.d.ts.map +1 -0
  55. package/dist/components/trace-viewer/components/ui.js.map +1 -0
  56. package/dist/components/trace-viewer/components/zoom-button.d.ts.map +1 -0
  57. package/dist/components/trace-viewer/components/zoom-button.js.map +1 -0
  58. package/dist/components/trace-viewer/components/zoom-icons.d.ts.map +1 -0
  59. package/dist/components/trace-viewer/components/zoom-icons.js.map +1 -0
  60. package/dist/components/trace-viewer/context.d.ts.map +1 -0
  61. package/dist/components/trace-viewer/context.js.map +1 -0
  62. package/dist/components/trace-viewer/index.d.ts.map +1 -0
  63. package/dist/components/trace-viewer/index.js.map +1 -0
  64. package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -0
  65. package/dist/components/trace-viewer/trace-viewer.js.map +1 -0
  66. package/dist/components/trace-viewer/types.d.ts.map +1 -0
  67. package/dist/components/trace-viewer/types.js.map +1 -0
  68. package/dist/components/trace-viewer/util/constants.d.ts.map +1 -0
  69. package/dist/components/trace-viewer/util/constants.js.map +1 -0
  70. package/dist/components/trace-viewer/util/scrollbar-width.d.ts.map +1 -0
  71. package/dist/components/trace-viewer/util/scrollbar-width.js.map +1 -0
  72. package/dist/{trace-viewer → components/trace-viewer}/util/timing.d.ts +1 -1
  73. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -0
  74. package/dist/{trace-viewer → components/trace-viewer}/util/timing.js +1 -1
  75. package/dist/components/trace-viewer/util/timing.js.map +1 -0
  76. package/dist/components/trace-viewer/util/tree.d.ts.map +1 -0
  77. package/dist/components/trace-viewer/util/tree.js.map +1 -0
  78. package/dist/components/trace-viewer/util/use-immediate-style.d.ts.map +1 -0
  79. package/dist/components/trace-viewer/util/use-immediate-style.js.map +1 -0
  80. package/dist/components/trace-viewer/util/use-streaming-spans.d.ts.map +1 -0
  81. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -0
  82. package/dist/components/trace-viewer/util/use-trackpad-zoom.d.ts.map +1 -0
  83. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +1 -0
  84. package/dist/components/trace-viewer/worker.d.ts.map +1 -0
  85. package/dist/components/trace-viewer/worker.js.map +1 -0
  86. package/dist/components/workflow-trace-view.d.ts +24 -0
  87. package/dist/components/workflow-trace-view.d.ts.map +1 -0
  88. package/dist/components/workflow-trace-view.js +152 -0
  89. package/dist/components/workflow-trace-view.js.map +1 -0
  90. package/dist/components/workflow-traces/event-colors.d.ts.map +1 -0
  91. package/dist/components/workflow-traces/event-colors.js.map +1 -0
  92. package/dist/components/workflow-traces/trace-colors.d.ts.map +1 -0
  93. package/dist/components/workflow-traces/trace-colors.js.map +1 -0
  94. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -0
  95. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -0
  96. package/dist/components/workflow-traces/trace-time-utils.d.ts.map +1 -0
  97. package/dist/components/workflow-traces/trace-time-utils.js.map +1 -0
  98. package/dist/index.d.ts +3 -13
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +2 -9
  101. package/dist/index.js.map +1 -1
  102. package/package.json +15 -11
  103. package/src/components/error-boundary.tsx +79 -0
  104. package/src/components/event-list-view.tsx +429 -0
  105. package/src/components/hook-actions.tsx +167 -0
  106. package/src/components/index.d.ts +1 -0
  107. package/src/components/index.ts +23 -0
  108. package/src/components/run-trace-view.tsx +75 -0
  109. package/src/components/sidebar/attribute-panel.tsx +938 -0
  110. package/src/components/sidebar/conversation-view.tsx +235 -0
  111. package/src/components/sidebar/detail-card.tsx +43 -0
  112. package/src/components/sidebar/entity-detail-panel.tsx +338 -0
  113. package/src/components/sidebar/events-list.tsx +119 -0
  114. package/src/components/sidebar/resolve-hook-modal.tsx +219 -0
  115. package/src/components/stream-viewer.tsx +143 -0
  116. package/src/components/trace-viewer/components/map.tsx +226 -0
  117. package/src/components/trace-viewer/components/markers.tsx +564 -0
  118. package/src/components/trace-viewer/components/node.tsx +259 -0
  119. package/src/components/trace-viewer/components/search-input.tsx +52 -0
  120. package/src/components/trace-viewer/components/search.tsx +47 -0
  121. package/src/components/trace-viewer/components/span-detail-panel.tsx +650 -0
  122. package/src/components/trace-viewer/components/ui.tsx +156 -0
  123. package/src/components/trace-viewer/components/zoom-button.tsx +61 -0
  124. package/src/components/trace-viewer/components/zoom-icons.tsx +65 -0
  125. package/src/components/trace-viewer/context.tsx +633 -0
  126. package/src/components/trace-viewer/index.tsx +4 -0
  127. package/src/components/trace-viewer/modules.d.ts +16 -0
  128. package/src/components/trace-viewer/trace-viewer.module.css +1292 -0
  129. package/src/components/trace-viewer/trace-viewer.tsx +448 -0
  130. package/src/components/trace-viewer/types.ts +234 -0
  131. package/src/components/trace-viewer/util/constants.ts +8 -0
  132. package/src/components/trace-viewer/util/scrollbar-width.ts +13 -0
  133. package/src/components/trace-viewer/util/timing.ts +33 -0
  134. package/src/components/trace-viewer/util/tree.ts +277 -0
  135. package/src/components/trace-viewer/util/use-immediate-style.ts +38 -0
  136. package/src/components/trace-viewer/util/use-streaming-spans.ts +415 -0
  137. package/src/components/trace-viewer/util/use-trackpad-zoom.tsx +51 -0
  138. package/src/components/trace-viewer/worker.ts +128 -0
  139. package/src/components/ui/alert.tsx +59 -0
  140. package/src/components/ui/card.tsx +88 -0
  141. package/src/components/ui/error-card.tsx +65 -0
  142. package/src/components/ui/skeleton.tsx +15 -0
  143. package/src/components/workflow-trace-view.tsx +306 -0
  144. package/src/components/workflow-traces/event-colors.ts +94 -0
  145. package/src/components/workflow-traces/trace-colors.ts +112 -0
  146. package/src/components/workflow-traces/trace-span-construction.ts +299 -0
  147. package/src/components/workflow-traces/trace-time-utils.ts +50 -0
  148. package/src/hooks/use-dark-mode.ts +34 -0
  149. package/src/index.d.ts +1 -0
  150. package/src/index.ts +29 -0
  151. package/src/lib/event-analysis.ts +231 -0
  152. package/src/lib/utils.ts +166 -0
  153. package/dist/api/workflow-api-client.d.ts +0 -543
  154. package/dist/api/workflow-api-client.d.ts.map +0 -1
  155. package/dist/api/workflow-api-client.js +0 -953
  156. package/dist/api/workflow-api-client.js.map +0 -1
  157. package/dist/api/workflow-server-actions.d.ts +0 -230
  158. package/dist/api/workflow-server-actions.d.ts.map +0 -1
  159. package/dist/api/workflow-server-actions.js +0 -861
  160. package/dist/api/workflow-server-actions.js.map +0 -1
  161. package/dist/error-boundary.d.ts.map +0 -1
  162. package/dist/error-boundary.js.map +0 -1
  163. package/dist/event-list-view.d.ts.map +0 -1
  164. package/dist/event-list-view.js.map +0 -1
  165. package/dist/hook-actions.d.ts.map +0 -1
  166. package/dist/hook-actions.js.map +0 -1
  167. package/dist/run-trace-view.d.ts +0 -8
  168. package/dist/run-trace-view.d.ts.map +0 -1
  169. package/dist/run-trace-view.js +0 -15
  170. package/dist/run-trace-view.js.map +0 -1
  171. package/dist/sidebar/attribute-panel.d.ts.map +0 -1
  172. package/dist/sidebar/attribute-panel.js.map +0 -1
  173. package/dist/sidebar/conversation-view.d.ts.map +0 -1
  174. package/dist/sidebar/conversation-view.js.map +0 -1
  175. package/dist/sidebar/detail-card.d.ts.map +0 -1
  176. package/dist/sidebar/detail-card.js.map +0 -1
  177. package/dist/sidebar/entity-detail-panel.d.ts +0 -12
  178. package/dist/sidebar/entity-detail-panel.d.ts.map +0 -1
  179. package/dist/sidebar/entity-detail-panel.js.map +0 -1
  180. package/dist/sidebar/events-list.d.ts +0 -9
  181. package/dist/sidebar/events-list.d.ts.map +0 -1
  182. package/dist/sidebar/events-list.js +0 -36
  183. package/dist/sidebar/events-list.js.map +0 -1
  184. package/dist/sidebar/resolve-hook-modal.d.ts.map +0 -1
  185. package/dist/sidebar/resolve-hook-modal.js.map +0 -1
  186. package/dist/stream-viewer.d.ts +0 -13
  187. package/dist/stream-viewer.d.ts.map +0 -1
  188. package/dist/stream-viewer.js.map +0 -1
  189. package/dist/trace-viewer/components/map.d.ts.map +0 -1
  190. package/dist/trace-viewer/components/map.js.map +0 -1
  191. package/dist/trace-viewer/components/markers.d.ts.map +0 -1
  192. package/dist/trace-viewer/components/markers.js.map +0 -1
  193. package/dist/trace-viewer/components/node.d.ts.map +0 -1
  194. package/dist/trace-viewer/components/node.js.map +0 -1
  195. package/dist/trace-viewer/components/search-input.d.ts.map +0 -1
  196. package/dist/trace-viewer/components/search-input.js.map +0 -1
  197. package/dist/trace-viewer/components/search.d.ts.map +0 -1
  198. package/dist/trace-viewer/components/search.js.map +0 -1
  199. package/dist/trace-viewer/components/span-detail-panel.d.ts.map +0 -1
  200. package/dist/trace-viewer/components/span-detail-panel.js.map +0 -1
  201. package/dist/trace-viewer/components/ui.d.ts.map +0 -1
  202. package/dist/trace-viewer/components/ui.js.map +0 -1
  203. package/dist/trace-viewer/components/zoom-button.d.ts.map +0 -1
  204. package/dist/trace-viewer/components/zoom-button.js.map +0 -1
  205. package/dist/trace-viewer/components/zoom-icons.d.ts.map +0 -1
  206. package/dist/trace-viewer/components/zoom-icons.js.map +0 -1
  207. package/dist/trace-viewer/context.d.ts.map +0 -1
  208. package/dist/trace-viewer/context.js.map +0 -1
  209. package/dist/trace-viewer/index.d.ts.map +0 -1
  210. package/dist/trace-viewer/index.js.map +0 -1
  211. package/dist/trace-viewer/trace-viewer.d.ts.map +0 -1
  212. package/dist/trace-viewer/trace-viewer.js.map +0 -1
  213. package/dist/trace-viewer/types.d.ts.map +0 -1
  214. package/dist/trace-viewer/types.js.map +0 -1
  215. package/dist/trace-viewer/util/constants.d.ts.map +0 -1
  216. package/dist/trace-viewer/util/constants.js.map +0 -1
  217. package/dist/trace-viewer/util/scrollbar-width.d.ts.map +0 -1
  218. package/dist/trace-viewer/util/scrollbar-width.js.map +0 -1
  219. package/dist/trace-viewer/util/timing.d.ts.map +0 -1
  220. package/dist/trace-viewer/util/timing.js.map +0 -1
  221. package/dist/trace-viewer/util/tree.d.ts.map +0 -1
  222. package/dist/trace-viewer/util/tree.js.map +0 -1
  223. package/dist/trace-viewer/util/use-immediate-style.d.ts.map +0 -1
  224. package/dist/trace-viewer/util/use-immediate-style.js.map +0 -1
  225. package/dist/trace-viewer/util/use-streaming-spans.d.ts.map +0 -1
  226. package/dist/trace-viewer/util/use-streaming-spans.js.map +0 -1
  227. package/dist/trace-viewer/util/use-trackpad-zoom.d.ts.map +0 -1
  228. package/dist/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
  229. package/dist/trace-viewer/worker.d.ts.map +0 -1
  230. package/dist/trace-viewer/worker.js.map +0 -1
  231. package/dist/workflow-trace-view.d.ts +0 -14
  232. package/dist/workflow-trace-view.d.ts.map +0 -1
  233. package/dist/workflow-trace-view.js +0 -135
  234. package/dist/workflow-trace-view.js.map +0 -1
  235. package/dist/workflow-traces/event-colors.d.ts.map +0 -1
  236. package/dist/workflow-traces/event-colors.js.map +0 -1
  237. package/dist/workflow-traces/trace-colors.d.ts.map +0 -1
  238. package/dist/workflow-traces/trace-colors.js.map +0 -1
  239. package/dist/workflow-traces/trace-span-construction.d.ts.map +0 -1
  240. package/dist/workflow-traces/trace-span-construction.js.map +0 -1
  241. package/dist/workflow-traces/trace-time-utils.d.ts.map +0 -1
  242. package/dist/workflow-traces/trace-time-utils.js.map +0 -1
  243. package/server/README.md +0 -1
  244. package/server/package.json +0 -4
  245. /package/dist/{error-boundary.d.ts → components/error-boundary.d.ts} +0 -0
  246. /package/dist/{sidebar → components/sidebar}/attribute-panel.d.ts +0 -0
  247. /package/dist/{sidebar → components/sidebar}/conversation-view.d.ts +0 -0
  248. /package/dist/{sidebar → components/sidebar}/conversation-view.js +0 -0
  249. /package/dist/{sidebar → components/sidebar}/detail-card.d.ts +0 -0
  250. /package/dist/{sidebar → components/sidebar}/detail-card.js +0 -0
  251. /package/dist/{sidebar → components/sidebar}/resolve-hook-modal.d.ts +0 -0
  252. /package/dist/{sidebar → components/sidebar}/resolve-hook-modal.js +0 -0
  253. /package/dist/{trace-viewer → components/trace-viewer}/components/map.d.ts +0 -0
  254. /package/dist/{trace-viewer → components/trace-viewer}/components/map.js +0 -0
  255. /package/dist/{trace-viewer → components/trace-viewer}/components/markers.d.ts +0 -0
  256. /package/dist/{trace-viewer → components/trace-viewer}/components/markers.js +0 -0
  257. /package/dist/{trace-viewer → components/trace-viewer}/components/node.d.ts +0 -0
  258. /package/dist/{trace-viewer → components/trace-viewer}/components/node.js +0 -0
  259. /package/dist/{trace-viewer → components/trace-viewer}/components/search-input.d.ts +0 -0
  260. /package/dist/{trace-viewer → components/trace-viewer}/components/search-input.js +0 -0
  261. /package/dist/{trace-viewer → components/trace-viewer}/components/search.d.ts +0 -0
  262. /package/dist/{trace-viewer → components/trace-viewer}/components/search.js +0 -0
  263. /package/dist/{trace-viewer → components/trace-viewer}/components/span-detail-panel.d.ts +0 -0
  264. /package/dist/{trace-viewer → components/trace-viewer}/components/span-detail-panel.js +0 -0
  265. /package/dist/{trace-viewer → components/trace-viewer}/components/ui.d.ts +0 -0
  266. /package/dist/{trace-viewer → components/trace-viewer}/components/ui.js +0 -0
  267. /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-button.d.ts +0 -0
  268. /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-button.js +0 -0
  269. /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-icons.d.ts +0 -0
  270. /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-icons.js +0 -0
  271. /package/dist/{trace-viewer → components/trace-viewer}/context.d.ts +0 -0
  272. /package/dist/{trace-viewer → components/trace-viewer}/context.js +0 -0
  273. /package/dist/{trace-viewer → components/trace-viewer}/index.d.ts +0 -0
  274. /package/dist/{trace-viewer → components/trace-viewer}/index.js +0 -0
  275. /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.d.ts +0 -0
  276. /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.js +0 -0
  277. /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.module.css +0 -0
  278. /package/dist/{trace-viewer → components/trace-viewer}/types.d.ts +0 -0
  279. /package/dist/{trace-viewer → components/trace-viewer}/types.js +0 -0
  280. /package/dist/{trace-viewer → components/trace-viewer}/util/constants.d.ts +0 -0
  281. /package/dist/{trace-viewer → components/trace-viewer}/util/constants.js +0 -0
  282. /package/dist/{trace-viewer → components/trace-viewer}/util/scrollbar-width.d.ts +0 -0
  283. /package/dist/{trace-viewer → components/trace-viewer}/util/scrollbar-width.js +0 -0
  284. /package/dist/{trace-viewer → components/trace-viewer}/util/tree.d.ts +0 -0
  285. /package/dist/{trace-viewer → components/trace-viewer}/util/tree.js +0 -0
  286. /package/dist/{trace-viewer → components/trace-viewer}/util/use-immediate-style.d.ts +0 -0
  287. /package/dist/{trace-viewer → components/trace-viewer}/util/use-immediate-style.js +0 -0
  288. /package/dist/{trace-viewer → components/trace-viewer}/util/use-streaming-spans.d.ts +0 -0
  289. /package/dist/{trace-viewer → components/trace-viewer}/util/use-streaming-spans.js +0 -0
  290. /package/dist/{trace-viewer → components/trace-viewer}/util/use-trackpad-zoom.d.ts +0 -0
  291. /package/dist/{trace-viewer → components/trace-viewer}/util/use-trackpad-zoom.js +0 -0
  292. /package/dist/{trace-viewer → components/trace-viewer}/worker.d.ts +0 -0
  293. /package/dist/{trace-viewer → components/trace-viewer}/worker.js +0 -0
  294. /package/dist/{workflow-traces → components/workflow-traces}/event-colors.d.ts +0 -0
  295. /package/dist/{workflow-traces → components/workflow-traces}/event-colors.js +0 -0
  296. /package/dist/{workflow-traces → components/workflow-traces}/trace-colors.d.ts +0 -0
  297. /package/dist/{workflow-traces → components/workflow-traces}/trace-colors.js +0 -0
  298. /package/dist/{workflow-traces → components/workflow-traces}/trace-span-construction.d.ts +0 -0
  299. /package/dist/{workflow-traces → components/workflow-traces}/trace-span-construction.js +0 -0
  300. /package/dist/{workflow-traces → components/workflow-traces}/trace-time-utils.d.ts +0 -0
  301. /package/dist/{workflow-traces → components/workflow-traces}/trace-time-utils.js +0 -0
@@ -0,0 +1,119 @@
1
+ 'use client';
2
+
3
+ import { useMemo } from 'react';
4
+ import { ErrorCard } from '../ui/error-card';
5
+ import type { SpanEvent } from '../trace-viewer/types.js';
6
+ import { AttributeBlock, localMillisecondTime } from './attribute-panel';
7
+ import { DetailCard } from './detail-card';
8
+
9
+ export function EventsList({
10
+ events,
11
+ fullEvents,
12
+ isLoading = false,
13
+ error,
14
+ }: {
15
+ events: SpanEvent[];
16
+ fullEvents?: SpanEvent[] | null;
17
+ isLoading?: boolean;
18
+ error?: Error | null;
19
+ }) {
20
+ const displayData = useMemo(
21
+ () => (fullEvents?.length ? fullEvents : events) || [],
22
+ [events, fullEvents]
23
+ );
24
+
25
+ return (
26
+ <div className="mt-2" style={{ color: 'var(--ds-gray-1000)' }}>
27
+ <h3
28
+ className="text-heading-16 font-medium mt-4 mb-2"
29
+ style={{ color: 'var(--ds-gray-1000)' }}
30
+ >
31
+ Events {!isLoading && `(${displayData.length})`}
32
+ </h3>
33
+ {error ? (
34
+ <ErrorCard
35
+ title="Failed to load full event list"
36
+ details={error?.message}
37
+ className="my-4"
38
+ />
39
+ ) : null}
40
+ {isLoading ? <div>Loading events...</div> : null}
41
+ {!isLoading && !error && displayData.length === 0 && (
42
+ <div className="text-sm">No events found</div>
43
+ )}
44
+ {displayData.length > 0 && !error ? (
45
+ <div className="flex flex-col gap-2">
46
+ {displayData.map((event, index) => (
47
+ <DetailCard
48
+ key={`${event.name}-${index}`}
49
+ summary={
50
+ <>
51
+ <span
52
+ className="font-medium"
53
+ style={{ color: 'var(--ds-gray-1000)' }}
54
+ >
55
+ {event.name}
56
+ </span>{' '}
57
+ -{' '}
58
+ <span style={{ color: 'var(--ds-gray-700)' }}>
59
+ {localMillisecondTime(
60
+ event.timestamp[0] * 1000 + event.timestamp[1] / 1e6
61
+ )}
62
+ </span>
63
+ </>
64
+ }
65
+ >
66
+ {/* Bordered container with separator */}
67
+ <div
68
+ className="flex flex-col divide-y rounded-md border overflow-hidden"
69
+ style={{
70
+ borderColor: 'var(--ds-gray-300)',
71
+ backgroundColor: 'var(--ds-gray-100)',
72
+ }}
73
+ >
74
+ {Object.entries(event.attributes)
75
+ .filter(([key]) => key !== 'eventData')
76
+ .map(([key, value]) => (
77
+ <div
78
+ key={key}
79
+ className="flex items-center justify-between px-2.5 py-1.5"
80
+ style={{ borderColor: 'var(--ds-gray-300)' }}
81
+ >
82
+ <span
83
+ className="text-[11px] font-medium"
84
+ style={{ color: 'var(--ds-gray-700)' }}
85
+ >
86
+ {key}
87
+ </span>
88
+ <span
89
+ className="text-[11px] font-mono"
90
+ style={{ color: 'var(--ds-gray-1000)' }}
91
+ >
92
+ {String(value)}
93
+ </span>
94
+ </div>
95
+ ))}
96
+ </div>
97
+ {error ? (
98
+ <ErrorCard
99
+ title="Failed to load event data"
100
+ details={String(error)}
101
+ className="my-4"
102
+ />
103
+ ) : null}
104
+ {!error && !isLoading && event.attributes.eventData != null && (
105
+ <div className="mt-2">
106
+ <AttributeBlock
107
+ isLoading={isLoading}
108
+ attribute="eventData"
109
+ value={event.attributes.eventData}
110
+ />
111
+ </div>
112
+ )}
113
+ </DetailCard>
114
+ ))}
115
+ </div>
116
+ ) : null}
117
+ </div>
118
+ );
119
+ }
@@ -0,0 +1,219 @@
1
+ 'use client';
2
+
3
+ import clsx from 'clsx';
4
+ import { Send, X } from 'lucide-react';
5
+ import { useCallback, useEffect, useRef, useState } from 'react';
6
+
7
+ interface ResolveHookModalProps {
8
+ /** Whether the modal is open */
9
+ isOpen: boolean;
10
+ /** Callback when the modal should be closed */
11
+ onClose: () => void;
12
+ /** Callback when the form is submitted with the parsed JSON payload */
13
+ onSubmit: (payload: unknown) => Promise<void>;
14
+ /** Whether the submission is in progress */
15
+ isSubmitting?: boolean;
16
+ }
17
+
18
+ /**
19
+ * Modal component for resolving a hook by entering a JSON payload.
20
+ */
21
+ export function ResolveHookModal({
22
+ isOpen,
23
+ onClose,
24
+ onSubmit,
25
+ isSubmitting = false,
26
+ }: ResolveHookModalProps): React.JSX.Element | null {
27
+ const [jsonInput, setJsonInput] = useState('');
28
+ const [parseError, setParseError] = useState<string | null>(null);
29
+ const textareaRef = useRef<HTMLTextAreaElement>(null);
30
+
31
+ // Focus the textarea when the modal opens
32
+ useEffect(() => {
33
+ if (isOpen && textareaRef.current) {
34
+ textareaRef.current.focus();
35
+ }
36
+ }, [isOpen]);
37
+
38
+ // Reset state when modal closes
39
+ useEffect(() => {
40
+ if (!isOpen) {
41
+ setJsonInput('');
42
+ setParseError(null);
43
+ }
44
+ }, [isOpen]);
45
+
46
+ // Handle escape key to close
47
+ useEffect(() => {
48
+ const handleKeyDown = (e: KeyboardEvent) => {
49
+ if (e.key === 'Escape' && isOpen && !isSubmitting) {
50
+ onClose();
51
+ }
52
+ };
53
+ document.addEventListener('keydown', handleKeyDown);
54
+ return () => document.removeEventListener('keydown', handleKeyDown);
55
+ }, [isOpen, isSubmitting, onClose]);
56
+
57
+ const submitPayload = useCallback(async () => {
58
+ setParseError(null);
59
+
60
+ // Parse the JSON input
61
+ let payload: unknown;
62
+ try {
63
+ // Allow empty string as null payload
64
+ if (jsonInput.trim() === '') {
65
+ payload = null;
66
+ } else {
67
+ payload = JSON.parse(jsonInput);
68
+ }
69
+ } catch {
70
+ setParseError('Invalid JSON. Please check your input.');
71
+ return;
72
+ }
73
+
74
+ await onSubmit(payload);
75
+ }, [jsonInput, onSubmit]);
76
+
77
+ const handleSubmit = useCallback(
78
+ (e: React.FormEvent) => {
79
+ e.preventDefault();
80
+ void submitPayload();
81
+ },
82
+ [submitPayload]
83
+ );
84
+
85
+ // Handle Cmd/Ctrl + Enter to submit
86
+ const handleKeyDown = useCallback(
87
+ (e: React.KeyboardEvent) => {
88
+ if ((e.metaKey || e.ctrlKey) && e.key === 'Enter' && !isSubmitting) {
89
+ e.preventDefault();
90
+ handleSubmit(e as unknown as React.FormEvent);
91
+ }
92
+ },
93
+ [handleSubmit, isSubmitting]
94
+ );
95
+
96
+ if (!isOpen) {
97
+ return null;
98
+ }
99
+
100
+ return (
101
+ <div
102
+ className="fixed inset-0 z-50 flex items-center justify-center"
103
+ role="dialog"
104
+ aria-modal="true"
105
+ aria-labelledby="resolve-hook-modal-title"
106
+ >
107
+ {/* Backdrop */}
108
+ <div
109
+ className="absolute inset-0 bg-black/50 backdrop-blur-sm"
110
+ onClick={isSubmitting ? undefined : onClose}
111
+ />
112
+
113
+ {/* Modal content */}
114
+ <div
115
+ className={clsx(
116
+ 'relative z-10 w-full max-w-lg mx-4',
117
+ 'bg-background text-foreground rounded-lg shadow-xl',
118
+ 'border border-border'
119
+ )}
120
+ >
121
+ {/* Header */}
122
+ <div className="flex items-center justify-between px-4 py-3 border-b border-border">
123
+ <h2
124
+ id="resolve-hook-modal-title"
125
+ className="text-lg font-semibold text-foreground"
126
+ >
127
+ Resolve Hook
128
+ </h2>
129
+ <button
130
+ type="button"
131
+ onClick={onClose}
132
+ disabled={isSubmitting}
133
+ className={clsx(
134
+ 'p-1 rounded-md transition-colors',
135
+ 'text-muted-foreground hover:text-foreground',
136
+ 'hover:bg-muted',
137
+ 'disabled:opacity-50 disabled:cursor-not-allowed'
138
+ )}
139
+ aria-label="Close modal"
140
+ >
141
+ <X className="h-5 w-5" />
142
+ </button>
143
+ </div>
144
+
145
+ {/* Body */}
146
+ <form onSubmit={handleSubmit}>
147
+ <div className="px-4 py-4">
148
+ <label
149
+ htmlFor="json-payload"
150
+ className="block text-sm font-medium text-foreground mb-2"
151
+ >
152
+ JSON Payload
153
+ </label>
154
+ <p className="text-xs text-muted-foreground mb-3">
155
+ Enter a JSON value to send to the hook. Leave empty to send{' '}
156
+ <code className="px-1 py-0.5 bg-muted rounded text-xs">null</code>
157
+ .
158
+ </p>
159
+ <textarea
160
+ ref={textareaRef}
161
+ id="json-payload"
162
+ value={jsonInput}
163
+ onChange={(e) => {
164
+ setJsonInput(e.target.value);
165
+ setParseError(null);
166
+ }}
167
+ onKeyDown={handleKeyDown}
168
+ disabled={isSubmitting}
169
+ placeholder='{"key": "value"}'
170
+ className={clsx(
171
+ 'w-full h-40 px-3 py-2 font-mono text-sm',
172
+ 'text-foreground',
173
+ 'bg-background',
174
+ 'border rounded-md',
175
+ 'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background',
176
+ 'disabled:opacity-50 disabled:cursor-not-allowed',
177
+ 'placeholder:text-muted-foreground',
178
+ parseError ? 'border-destructive' : 'border-input'
179
+ )}
180
+ />
181
+ {parseError && (
182
+ <p className="mt-2 text-sm text-destructive">{parseError}</p>
183
+ )}
184
+ </div>
185
+
186
+ {/* Footer */}
187
+ <div className="flex items-center justify-end gap-2 px-4 py-3 border-t border-border">
188
+ <button
189
+ type="button"
190
+ onClick={onClose}
191
+ disabled={isSubmitting}
192
+ className={clsx(
193
+ 'px-4 py-2 text-sm font-medium rounded-md transition-colors',
194
+ 'bg-secondary text-secondary-foreground',
195
+ 'hover:bg-secondary/80',
196
+ 'disabled:opacity-50 disabled:cursor-not-allowed'
197
+ )}
198
+ >
199
+ Cancel
200
+ </button>
201
+ <button
202
+ type="button"
203
+ onClick={() => void submitPayload()}
204
+ disabled={isSubmitting}
205
+ className={clsx(
206
+ 'flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md transition-colors',
207
+ 'bg-primary text-primary-foreground hover:bg-primary/90',
208
+ 'disabled:opacity-50 disabled:cursor-not-allowed'
209
+ )}
210
+ >
211
+ <Send className="h-4 w-4" />
212
+ {isSubmitting ? 'Sending...' : 'Send Payload'}
213
+ </button>
214
+ </div>
215
+ </form>
216
+ </div>
217
+ </div>
218
+ );
219
+ }
@@ -0,0 +1,143 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+
5
+ interface StreamViewerProps {
6
+ streamId: string;
7
+ chunks: Chunk[];
8
+ isLive: boolean;
9
+ error?: string | null;
10
+ }
11
+
12
+ interface Chunk {
13
+ id: number;
14
+ text: string;
15
+ }
16
+
17
+ /**
18
+ * StreamViewer component that displays real-time stream data.
19
+ * It connects to a stream and displays chunks as they arrive,
20
+ * with auto-scroll functionality.
21
+ */
22
+ export function StreamViewer({
23
+ streamId,
24
+ chunks,
25
+ isLive,
26
+ error,
27
+ }: StreamViewerProps) {
28
+ // TODO: Handle 410 error specifically (stream expired)
29
+ const [hasMoreBelow, setHasMoreBelow] = useState(false);
30
+ const scrollRef = useRef<HTMLDivElement>(null);
31
+
32
+ const checkScrollPosition = useCallback(() => {
33
+ if (scrollRef.current) {
34
+ const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
35
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < 10;
36
+ setHasMoreBelow(!isAtBottom && scrollHeight > clientHeight);
37
+ }
38
+ }, []);
39
+
40
+ // biome-ignore lint/correctness/useExhaustiveDependencies: chunks.length triggers scroll on new chunks
41
+ useEffect(() => {
42
+ // Auto-scroll to bottom when new content arrives
43
+ if (scrollRef.current) {
44
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
45
+ }
46
+ // Check scroll position after content changes
47
+ checkScrollPosition();
48
+ }, [chunks.length, checkScrollPosition]);
49
+
50
+ return (
51
+ <div className="flex flex-col h-full pb-4">
52
+ <div className="flex items-center justify-between mb-3 px-1">
53
+ <code
54
+ className="text-xs font-mono truncate max-w-[80%]"
55
+ style={{ color: 'var(--ds-gray-900)' }}
56
+ title={streamId}
57
+ >
58
+ {streamId}
59
+ </code>
60
+ <span
61
+ className="text-xs flex items-center gap-1.5"
62
+ style={{
63
+ color: isLive ? 'var(--ds-green-700)' : 'var(--ds-gray-600)',
64
+ }}
65
+ >
66
+ <span
67
+ className="inline-block w-2 h-2 rounded-full"
68
+ style={{
69
+ backgroundColor: isLive
70
+ ? 'var(--ds-green-600)'
71
+ : 'var(--ds-gray-500)',
72
+ }}
73
+ />
74
+ {isLive ? 'Live' : 'Closed'}
75
+ </span>
76
+ </div>
77
+
78
+ <div className="relative flex-1 min-h-[200px]">
79
+ <div
80
+ ref={scrollRef}
81
+ onScroll={checkScrollPosition}
82
+ className="absolute inset-0 overflow-auto flex flex-col gap-2"
83
+ >
84
+ {error ? (
85
+ <div
86
+ className="text-[11px] rounded-md border p-3"
87
+ style={{
88
+ borderColor: 'var(--ds-red-300)',
89
+ backgroundColor: 'var(--ds-red-100)',
90
+ color: 'var(--ds-red-700)',
91
+ }}
92
+ >
93
+ <div>Error reading stream:</div>
94
+ <div>{error}</div>
95
+ </div>
96
+ ) : chunks.length === 0 ? (
97
+ <div
98
+ className="text-[11px] rounded-md border p-3"
99
+ style={{
100
+ borderColor: 'var(--ds-gray-300)',
101
+ backgroundColor: 'var(--ds-gray-100)',
102
+ color: 'var(--ds-gray-600)',
103
+ }}
104
+ >
105
+ {isLive ? 'Waiting for stream data...' : 'Stream is empty'}
106
+ </div>
107
+ ) : (
108
+ chunks.map((chunk, index) => (
109
+ <pre
110
+ key={`${streamId}-chunk-${chunk.id}`}
111
+ className="text-[11px] rounded-md border p-3 m-0 whitespace-pre-wrap break-words"
112
+ style={{
113
+ borderColor: 'var(--ds-gray-300)',
114
+ backgroundColor: 'var(--ds-gray-100)',
115
+ color: 'var(--ds-gray-1000)',
116
+ }}
117
+ >
118
+ <code>
119
+ <span
120
+ className="select-none mr-2"
121
+ style={{ color: 'var(--ds-gray-500)' }}
122
+ >
123
+ [{index}]
124
+ </span>
125
+ {chunk.text}
126
+ </code>
127
+ </pre>
128
+ ))
129
+ )}
130
+ </div>
131
+ {hasMoreBelow && (
132
+ <div
133
+ className="absolute bottom-0 left-0 right-0 h-8 pointer-events-none"
134
+ style={{
135
+ background:
136
+ 'linear-gradient(to top, var(--ds-background-100), transparent)',
137
+ }}
138
+ />
139
+ )}
140
+ </div>
141
+ </div>
142
+ );
143
+ }
@@ -0,0 +1,226 @@
1
+ import {
2
+ type MutableRefObject,
3
+ memo,
4
+ useEffect,
5
+ useLayoutEffect,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
9
+ import { useTraceViewer } from '../context';
10
+ import styles from '../trace-viewer.module.css';
11
+ import type { VisibleSpan } from '../types';
12
+ import { MAP_HEIGHT, TIMELINE_PADDING } from '../util/constants';
13
+ import { useImmediateStyle } from '../util/use-immediate-style';
14
+
15
+ const getDpi = (): number => {
16
+ if ('devicePixelRatio' in globalThis) {
17
+ return globalThis.devicePixelRatio || 1;
18
+ }
19
+ return 1;
20
+ };
21
+
22
+ const useDpi = (): number => {
23
+ const [dpi, setDpi] = useState(1);
24
+ useLayoutEffect(() => {
25
+ const onChange = (): void => setDpi(getDpi());
26
+ const media = matchMedia(`(resolution: ${dpi}dppx)`);
27
+ media.addEventListener('change', onChange);
28
+ onChange();
29
+
30
+ return () => {
31
+ media.removeEventListener('change', onChange);
32
+ };
33
+ }, [dpi]);
34
+ return dpi;
35
+ };
36
+
37
+ const padding = TIMELINE_PADDING;
38
+
39
+ export const MiniMap = memo(function MiniMap({
40
+ timelineRef,
41
+ rows,
42
+ scale,
43
+ }: {
44
+ timelineRef: MutableRefObject<HTMLDivElement | null>;
45
+ rows: VisibleSpan[][];
46
+ scale: number;
47
+ }) {
48
+ const {
49
+ state: { root, baseScale, timelineWidth, width: fullWidth, scrollbarWidth },
50
+ } = useTraceViewer();
51
+ const mapWidth = fullWidth + TIMELINE_PADDING * 2;
52
+ const dpi = useDpi();
53
+ const containerRef = useRef<HTMLDivElement>(null);
54
+ const canvasRef = useRef<HTMLCanvasElement>(null);
55
+
56
+ const thumbRatio =
57
+ (fullWidth / (fullWidth - scrollbarWidth)) *
58
+ Math.min(fullWidth / timelineWidth, baseScale / scale);
59
+ const thumbWidth = Math.min(
60
+ fullWidth,
61
+ (timelineWidth - scrollbarWidth) * thumbRatio
62
+ );
63
+ useEffect(() => {
64
+ let isHeld = false;
65
+ let clientX = 0;
66
+ let rect: DOMRect | undefined;
67
+
68
+ const isValidEventTarget = (target: EventTarget | null): boolean => {
69
+ return Boolean(containerRef.current?.contains(target as HTMLElement));
70
+ };
71
+
72
+ let nextFrame = 0;
73
+ const onFrame = (): void => {
74
+ if (!isHeld || !rect) return;
75
+ const $timeline = timelineRef.current;
76
+ if (!$timeline) return;
77
+
78
+ const x = clientX - thumbWidth * 0.5 - rect.left;
79
+ $timeline.scrollLeft = x / thumbRatio;
80
+ };
81
+
82
+ const onPointerMove = (event: PointerEvent): void => {
83
+ ({ clientX } = event);
84
+ cancelAnimationFrame(nextFrame);
85
+ nextFrame = requestAnimationFrame(onFrame);
86
+ };
87
+
88
+ const onPointerDown = (event: PointerEvent): void => {
89
+ if (!isValidEventTarget(event.target)) return;
90
+ isHeld = true;
91
+ rect = containerRef.current?.getBoundingClientRect();
92
+ onPointerMove(event);
93
+ };
94
+
95
+ const onPointerUp = (): void => {
96
+ isHeld = false;
97
+ rect = undefined;
98
+ };
99
+
100
+ const onTouchStart = (event: TouchEvent): void => {
101
+ if (!isValidEventTarget(event.target)) return;
102
+ if (event.cancelable) {
103
+ event.preventDefault();
104
+ }
105
+ };
106
+ const onTouchMove = (event: TouchEvent): void => {
107
+ if (!isHeld) return;
108
+ if (event.cancelable) {
109
+ event.preventDefault();
110
+ }
111
+ };
112
+
113
+ window.addEventListener('pointerdown', onPointerDown);
114
+ window.addEventListener('pointermove', onPointerMove);
115
+ window.addEventListener('pointerup', onPointerUp);
116
+ window.addEventListener('blur', onPointerUp);
117
+ window.addEventListener('touchstart', onTouchStart, { passive: false });
118
+ window.addEventListener('touchmove', onTouchMove, { passive: false });
119
+
120
+ return () => {
121
+ window.removeEventListener('pointerdown', onPointerDown);
122
+ window.removeEventListener('pointermove', onPointerMove);
123
+ window.removeEventListener('pointerup', onPointerUp);
124
+ window.removeEventListener('blur', onPointerUp);
125
+ window.removeEventListener('touchstart', onTouchStart);
126
+ window.removeEventListener('touchmove', onTouchMove);
127
+ cancelAnimationFrame(nextFrame);
128
+ };
129
+ }, [timelineRef, thumbRatio, thumbWidth]);
130
+
131
+ useEffect(() => {
132
+ const canvas = canvasRef.current;
133
+ const ctx = canvas?.getContext('2d');
134
+ if (!canvas || !ctx) return;
135
+
136
+ const render = (): void => {
137
+ ctx.resetTransform();
138
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
139
+ ctx.scale(dpi, dpi);
140
+
141
+ const gap = 1;
142
+ const height = 3;
143
+ const ratio = (mapWidth - padding * 2) / root.duration;
144
+
145
+ // TODO: fill spans based on depth
146
+ ctx.fillStyle = '#ccc';
147
+ ctx.beginPath();
148
+ for (const row of rows) {
149
+ for (const span of row) {
150
+ const y = padding - gap + (gap + height) * span.row;
151
+ if (y >= MAP_HEIGHT - height - padding) break;
152
+
153
+ const x = padding + (span.startTime - root.startTime) * ratio;
154
+ const width = span.duration * ratio;
155
+
156
+ ctx.roundRect(x, y, width, height, height);
157
+ }
158
+ }
159
+ ctx.fill();
160
+ };
161
+
162
+ const nextFrame = requestAnimationFrame(render);
163
+
164
+ return () => cancelAnimationFrame(nextFrame);
165
+ }, [dpi, root, mapWidth, rows]);
166
+
167
+ const thumbRef = useRef<HTMLDivElement>(null);
168
+ const { style: thumbStyle, setStyle: setThumbStyle } =
169
+ useImmediateStyle(thumbRef);
170
+ useLayoutEffect(() => {
171
+ const $timeline = timelineRef.current;
172
+ if (!$timeline) return;
173
+
174
+ let nextFrame = 0;
175
+ const onFrame = (): void => {
176
+ setThumbStyle(
177
+ 'transform',
178
+ `translateX(${padding + $timeline.scrollLeft * thumbRatio}px)`
179
+ );
180
+ };
181
+ onFrame();
182
+
183
+ const onScroll = (): void => {
184
+ cancelAnimationFrame(nextFrame);
185
+ nextFrame = requestAnimationFrame(onFrame);
186
+ };
187
+ $timeline.addEventListener('scroll', onScroll);
188
+
189
+ return () => {
190
+ cancelAnimationFrame(nextFrame);
191
+ $timeline.removeEventListener('scroll', onScroll);
192
+ };
193
+ }, [timelineRef, thumbRatio, setThumbStyle]);
194
+
195
+ return (
196
+ <div
197
+ className={styles.mapContainer}
198
+ ref={containerRef}
199
+ style={{
200
+ width: thumbWidth > 0 ? mapWidth : '100%',
201
+ height: MAP_HEIGHT,
202
+ }}
203
+ >
204
+ <canvas
205
+ className={styles.mapCanvas}
206
+ height={MAP_HEIGHT * dpi}
207
+ ref={canvasRef}
208
+ style={{ width: mapWidth, height: MAP_HEIGHT }}
209
+ width={mapWidth * dpi}
210
+ />
211
+ <div
212
+ className={styles.mapThumb}
213
+ ref={thumbRef}
214
+ style={{
215
+ transform: thumbStyle.transform,
216
+ width:
217
+ thumbWidth > 0
218
+ ? thumbWidth
219
+ : `calc(100% - ${TIMELINE_PADDING * 2}px)`,
220
+ top: 2,
221
+ height: MAP_HEIGHT - 4,
222
+ }}
223
+ />
224
+ </div>
225
+ );
226
+ });