@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,235 @@
1
+ import type { ModelMessage } from 'ai';
2
+ import { Streamdown } from 'streamdown';
3
+
4
+ interface ConversationViewProps {
5
+ messages: ModelMessage[];
6
+ }
7
+
8
+ export function ConversationView({ messages }: ConversationViewProps) {
9
+ if (messages.length === 0) {
10
+ return (
11
+ <div
12
+ className="text-center py-8 text-[11px]"
13
+ style={{ color: 'var(--ds-gray-600)' }}
14
+ >
15
+ No messages
16
+ </div>
17
+ );
18
+ }
19
+
20
+ return (
21
+ <div className="flex flex-col gap-3 p-3">
22
+ {messages.map((message, index) => (
23
+ <MessageBubble key={index} message={message} />
24
+ ))}
25
+ </div>
26
+ );
27
+ }
28
+
29
+ function MessageBubble({ message }: { message: ModelMessage }) {
30
+ const role = message.role;
31
+ const parts = parseContent(message.content);
32
+ const style = getRoleStyle(role);
33
+
34
+ return (
35
+ <div
36
+ className="rounded-md border text-[11px]"
37
+ style={{
38
+ backgroundColor: style.bg,
39
+ borderColor: style.border,
40
+ }}
41
+ >
42
+ {/* Role header */}
43
+ <div
44
+ className="px-2.5 py-1 border-b text-[10px] font-medium uppercase tracking-wide"
45
+ style={{
46
+ borderColor: style.border,
47
+ color: style.label,
48
+ }}
49
+ >
50
+ {role}
51
+ </div>
52
+
53
+ {/* Content */}
54
+ <div className="px-2.5 py-2 space-y-2">
55
+ {parts.map((part, i) => (
56
+ <ContentPart key={i} part={part} role={role} />
57
+ ))}
58
+ </div>
59
+ </div>
60
+ );
61
+ }
62
+
63
+ interface ParsedPart {
64
+ type: 'text' | 'tool-call' | 'tool-result';
65
+ text?: string;
66
+ toolName?: string;
67
+ input?: unknown;
68
+ output?: unknown;
69
+ }
70
+
71
+ function ContentPart({ part, role }: { part: ParsedPart; role: string }) {
72
+ if (part.type === 'text') {
73
+ if (!part.text) return null;
74
+
75
+ // Use Streamdown for assistant messages (they often contain markdown)
76
+ if (role === 'assistant') {
77
+ return (
78
+ <div
79
+ className="prose prose-sm max-w-none text-[11px] [&>*:first-child]:mt-0 [&>*:last-child]:mb-0"
80
+ style={{ color: 'var(--ds-gray-1000)' }}
81
+ >
82
+ <Streamdown>{part.text}</Streamdown>
83
+ </div>
84
+ );
85
+ }
86
+
87
+ return (
88
+ <div
89
+ className="whitespace-pre-wrap break-words"
90
+ style={{ color: 'var(--ds-gray-1000)' }}
91
+ >
92
+ {part.text}
93
+ </div>
94
+ );
95
+ }
96
+
97
+ if (part.type === 'tool-call') {
98
+ return (
99
+ <div
100
+ className="rounded border px-2 py-1.5"
101
+ style={{
102
+ backgroundColor: 'var(--ds-purple-100)',
103
+ borderColor: 'var(--ds-purple-300)',
104
+ }}
105
+ >
106
+ <div className="flex items-center gap-1.5 text-[10px] font-medium">
107
+ <span>🔧</span>
108
+ <span style={{ color: 'var(--ds-purple-900)' }}>{part.toolName}</span>
109
+ </div>
110
+ {part.input != null && (
111
+ <pre
112
+ className="mt-1.5 text-[10px] overflow-x-auto p-1.5 rounded"
113
+ style={{
114
+ backgroundColor: 'var(--ds-gray-100)',
115
+ color: 'var(--ds-gray-800)',
116
+ }}
117
+ >
118
+ {typeof part.input === 'string'
119
+ ? part.input
120
+ : JSON.stringify(part.input, null, 2)}
121
+ </pre>
122
+ )}
123
+ </div>
124
+ );
125
+ }
126
+
127
+ if (part.type === 'tool-result') {
128
+ const outputText = formatOutput(part.output);
129
+ return (
130
+ <div
131
+ className="rounded border px-2 py-1.5"
132
+ style={{
133
+ backgroundColor: 'var(--ds-green-100)',
134
+ borderColor: 'var(--ds-green-300)',
135
+ }}
136
+ >
137
+ <div className="flex items-center gap-1.5 text-[10px] font-medium">
138
+ <span>✓</span>
139
+ <span style={{ color: 'var(--ds-green-900)' }}>
140
+ {part.toolName} result
141
+ </span>
142
+ </div>
143
+ {outputText && (
144
+ <pre
145
+ className="mt-1.5 text-[10px] overflow-x-auto max-h-[80px] p-1.5 rounded"
146
+ style={{
147
+ backgroundColor: 'var(--ds-gray-100)',
148
+ color: 'var(--ds-gray-800)',
149
+ }}
150
+ >
151
+ {outputText}
152
+ </pre>
153
+ )}
154
+ </div>
155
+ );
156
+ }
157
+
158
+ return null;
159
+ }
160
+
161
+ function getRoleStyle(role: string) {
162
+ switch (role) {
163
+ case 'user':
164
+ return {
165
+ bg: 'var(--ds-blue-100)',
166
+ border: 'var(--ds-blue-300)',
167
+ label: 'var(--ds-blue-700)',
168
+ };
169
+ case 'assistant':
170
+ return {
171
+ bg: 'var(--ds-gray-100)',
172
+ border: 'var(--ds-gray-300)',
173
+ label: 'var(--ds-gray-700)',
174
+ };
175
+ case 'system':
176
+ return {
177
+ bg: 'var(--ds-amber-100)',
178
+ border: 'var(--ds-amber-300)',
179
+ label: 'var(--ds-amber-700)',
180
+ };
181
+ case 'tool':
182
+ return {
183
+ bg: 'var(--ds-green-50)',
184
+ border: 'var(--ds-green-300)',
185
+ label: 'var(--ds-green-700)',
186
+ };
187
+ default:
188
+ return {
189
+ bg: 'var(--ds-gray-100)',
190
+ border: 'var(--ds-gray-300)',
191
+ label: 'var(--ds-gray-700)',
192
+ };
193
+ }
194
+ }
195
+
196
+ function parseContent(content: unknown): ParsedPart[] {
197
+ if (typeof content === 'string') {
198
+ return [{ type: 'text', text: content }];
199
+ }
200
+
201
+ if (Array.isArray(content)) {
202
+ return content.map((part): ParsedPart => {
203
+ if (typeof part === 'string') {
204
+ return { type: 'text', text: part };
205
+ }
206
+ if (part?.type === 'text') {
207
+ return { type: 'text', text: String(part.text ?? '') };
208
+ }
209
+ if (part?.type === 'tool-call') {
210
+ return {
211
+ type: 'tool-call',
212
+ toolName: part.toolName,
213
+ input: part.input,
214
+ };
215
+ }
216
+ if (part?.type === 'tool-result') {
217
+ return {
218
+ type: 'tool-result',
219
+ toolName: part.toolName,
220
+ output: part.output,
221
+ };
222
+ }
223
+ return { type: 'text', text: '' };
224
+ });
225
+ }
226
+
227
+ return [];
228
+ }
229
+
230
+ function formatOutput(output: unknown): string | null {
231
+ if (output == null) return null;
232
+ const text =
233
+ typeof output === 'string' ? output : JSON.stringify(output, null, 2);
234
+ return text.length > 500 ? `${text.slice(0, 500)}...` : text;
235
+ }
@@ -0,0 +1,43 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export function DetailCard({
4
+ summary,
5
+ children,
6
+ }: {
7
+ summary: ReactNode;
8
+ children?: ReactNode;
9
+ }) {
10
+ return (
11
+ <details className="group">
12
+ <summary
13
+ className="cursor-pointer rounded-md border px-2.5 py-1.5 text-xs hover:brightness-95"
14
+ style={{
15
+ borderColor: 'var(--ds-gray-300)',
16
+ backgroundColor: 'var(--ds-gray-100)',
17
+ color: 'var(--ds-gray-900)',
18
+ }}
19
+ >
20
+ {summary}
21
+ </summary>
22
+ {/* Expanded content with connecting line */}
23
+ <div className="relative pl-6 mt-3">
24
+ {/* Curved connecting line - vertical part from summary */}
25
+ <div
26
+ className="absolute left-3 -top-3 w-px h-3"
27
+ style={{ backgroundColor: 'var(--ds-gray-400)' }}
28
+ />
29
+ {/* Curved corner */}
30
+ <div
31
+ className="absolute left-3 top-0 w-3 h-3 border-l border-b rounded-bl-lg"
32
+ style={{ borderColor: 'var(--ds-gray-400)' }}
33
+ />
34
+ {/* Horizontal part to content */}
35
+ <div
36
+ className="absolute left-6 top-3 w-0 h-px -translate-y-px"
37
+ style={{ backgroundColor: 'var(--ds-gray-400)' }}
38
+ />
39
+ <div>{children}</div>
40
+ </div>
41
+ </details>
42
+ );
43
+ }
@@ -0,0 +1,338 @@
1
+ 'use client';
2
+
3
+ import type { Event, Hook, Step, WorkflowRun } from '@workflow/world';
4
+ import clsx from 'clsx';
5
+ import { Send, Zap } from 'lucide-react';
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
+ import { toast } from 'sonner';
8
+ import { useTraceViewer } from '../trace-viewer';
9
+ import { AttributePanel } from './attribute-panel';
10
+ import { EventsList } from './events-list';
11
+ import { ResolveHookModal } from './resolve-hook-modal';
12
+
13
+ // Type guards for runtime validation of span attribute data
14
+ function isStep(data: unknown): data is Step {
15
+ return data !== null && typeof data === 'object' && 'stepId' in data;
16
+ }
17
+
18
+ function isWorkflowRun(data: unknown): data is WorkflowRun {
19
+ return data !== null && typeof data === 'object' && 'runId' in data;
20
+ }
21
+
22
+ function isHook(data: unknown): data is Hook {
23
+ return data !== null && typeof data === 'object' && 'hookId' in data;
24
+ }
25
+
26
+ /**
27
+ * Info about the currently selected span
28
+ */
29
+ export type SpanSelectionInfo = {
30
+ resource: 'run' | 'step' | 'hook' | 'sleep';
31
+ resourceId: string;
32
+ runId?: string;
33
+ };
34
+
35
+ /**
36
+ * Custom panel component for workflow traces that displays entity details
37
+ */
38
+ export function EntityDetailPanel({
39
+ run,
40
+ onStreamClick,
41
+ spanDetailData,
42
+ spanDetailError,
43
+ spanDetailLoading,
44
+ onSpanSelect,
45
+ onWakeUpSleep,
46
+ onResolveHook,
47
+ }: {
48
+ run: WorkflowRun;
49
+ /** Callback when a stream reference is clicked */
50
+ onStreamClick?: (streamId: string) => void;
51
+ /** Pre-fetched span detail data for the selected span. */
52
+ spanDetailData: WorkflowRun | Step | Hook | Event | null;
53
+ /** Error from external span detail fetch. */
54
+ spanDetailError?: Error | null;
55
+ /** Loading state from external span detail fetch. */
56
+ spanDetailLoading?: boolean;
57
+ /** Callback when a span is selected. Use this to fetch data externally and pass via spanDetailData. */
58
+ onSpanSelect: (info: SpanSelectionInfo) => void;
59
+ /** Callback to wake up a pending sleep call. */
60
+ onWakeUpSleep?: (
61
+ runId: string,
62
+ correlationId: string
63
+ ) => Promise<{ stoppedCount: number }>;
64
+ /** Callback to resolve a hook with a payload. */
65
+ onResolveHook?: (
66
+ hookToken: string,
67
+ payload: unknown,
68
+ hook?: Hook
69
+ ) => Promise<void>;
70
+ }): React.JSX.Element | null {
71
+ const { state } = useTraceViewer();
72
+ const { selected } = state;
73
+ const [stoppingSleep, setStoppingSleep] = useState(false);
74
+ const [showResolveHookModal, setShowResolveHookModal] = useState(false);
75
+ const [resolvingHook, setResolvingHook] = useState(false);
76
+
77
+ const data = selected?.span.attributes?.data;
78
+
79
+ // Stable ref for onSpanSelect to avoid re-render loops when parent
80
+ // doesn't memoize the callback with useCallback.
81
+ const onSpanSelectRef = useRef(onSpanSelect);
82
+ useEffect(() => {
83
+ onSpanSelectRef.current = onSpanSelect;
84
+ });
85
+
86
+ // Determine resource ID and runId (needed for steps)
87
+ // Uses type guards to validate the data shape matches the expected resource type
88
+ const { resource, resourceId, runId } = useMemo(() => {
89
+ const resource = selected?.span.attributes?.resource;
90
+ if (resource === 'step' && isStep(data)) {
91
+ return { resource: 'step', resourceId: data.stepId, runId: data.runId };
92
+ } else if (resource === 'run' && isWorkflowRun(data)) {
93
+ return { resource: 'run', resourceId: data.runId, runId: undefined };
94
+ } else if (resource === 'hook' && isHook(data)) {
95
+ return { resource: 'hook', resourceId: data.hookId, runId: undefined };
96
+ } else if (resource === 'sleep') {
97
+ return {
98
+ resource: 'sleep',
99
+ resourceId: selected?.span?.spanId,
100
+ runId: undefined,
101
+ };
102
+ }
103
+ return { resource: undefined, resourceId: undefined, runId: undefined };
104
+ }, [selected, data]);
105
+
106
+ // Notify parent when span selection changes
107
+ useEffect(() => {
108
+ if (
109
+ resource &&
110
+ resourceId &&
111
+ ['run', 'step', 'hook', 'sleep'].includes(resource)
112
+ ) {
113
+ onSpanSelectRef.current({
114
+ resource: resource as 'run' | 'step' | 'hook' | 'sleep',
115
+ resourceId,
116
+ runId,
117
+ });
118
+ }
119
+ }, [resource, resourceId, runId]);
120
+
121
+ // Check if this sleep is still pending and can be woken up
122
+ // Requirements: no wait_completed event, resumeAt is in the future, run is not terminal
123
+ const spanEvents = selected?.span.events;
124
+ const spanEventsLength = spanEvents?.length ?? 0;
125
+ const canWakeUp = useMemo(() => {
126
+ void spanEventsLength; // Force dependency on length for reactivity
127
+ if (resource !== 'sleep' || !spanEvents) return false;
128
+
129
+ // Check run is not in a terminal state
130
+ const terminalStates = ['completed', 'failed', 'cancelled'];
131
+ if (terminalStates.includes(run.status)) return false;
132
+
133
+ // Check if wait has already completed
134
+ const hasWaitCompleted = spanEvents.some(
135
+ (e) => e.name === 'wait_completed'
136
+ );
137
+ if (hasWaitCompleted) return false;
138
+
139
+ // Check if resumeAt is in the future
140
+ const waitCreatedEvent = spanEvents.find((e) => e.name === 'wait_created');
141
+ const eventData = waitCreatedEvent?.attributes?.eventData as
142
+ | { resumeAt?: string | Date }
143
+ | undefined;
144
+ const resumeAt = eventData?.resumeAt;
145
+ if (!resumeAt) return false;
146
+
147
+ const resumeAtDate = new Date(resumeAt);
148
+ return resumeAtDate.getTime() > Date.now();
149
+ }, [resource, spanEvents, spanEventsLength, run.status]);
150
+
151
+ // Check if this hook can be resolved (not yet resolved, run is not terminal)
152
+ const canResolveHook = useMemo(() => {
153
+ void spanEventsLength; // Force dependency on length for reactivity
154
+ if (resource !== 'hook' || !spanEvents) return false;
155
+
156
+ // Check run is not in a terminal state
157
+ const terminalStates = ['completed', 'failed', 'cancelled'];
158
+ if (terminalStates.includes(run.status)) return false;
159
+
160
+ // Check if hook has already been disposed
161
+ const hasHookDisposed = spanEvents.some((e) => e.name === 'hook_disposed');
162
+ if (hasHookDisposed) return false;
163
+
164
+ // Hook can be resolved
165
+ return true;
166
+ }, [resource, spanEvents, spanEventsLength, run.status]);
167
+
168
+ const error = spanDetailError ?? undefined;
169
+ const loading = spanDetailLoading ?? false;
170
+
171
+ // Get the hook token for resolving (prefer fetched data when available)
172
+ const hookToken = useMemo(() => {
173
+ if (resource !== 'hook') return undefined;
174
+ const candidate = spanDetailData ?? data;
175
+ return isHook(candidate) ? candidate.token : undefined;
176
+ }, [resource, spanDetailData, data]);
177
+
178
+ useEffect(() => {
179
+ if (error && selected && resource) {
180
+ toast.error(`Failed to load ${resource} details`, {
181
+ description: error.message,
182
+ });
183
+ }
184
+ }, [error, resource, selected]);
185
+
186
+ const handleWakeUp = async () => {
187
+ if (stoppingSleep || !resourceId) return;
188
+ if (!onWakeUpSleep) {
189
+ toast.error('Unable to wake up sleep', {
190
+ description: 'No wake-up handler provided.',
191
+ });
192
+ return;
193
+ }
194
+
195
+ try {
196
+ setStoppingSleep(true);
197
+ const result = await onWakeUpSleep(run.runId, resourceId);
198
+ if (result.stoppedCount > 0) {
199
+ toast.success('Run woken up', {
200
+ description:
201
+ 'The sleep call has been interrupted and the run woken up.',
202
+ });
203
+ } else {
204
+ toast.info('Sleep already completed', {
205
+ description: 'This sleep call has already finished.',
206
+ });
207
+ }
208
+ } catch (err) {
209
+ console.error('Failed to wake up run:', err);
210
+ toast.error('Failed to wake up run', {
211
+ description:
212
+ err instanceof Error ? err.message : 'An unknown error occurred',
213
+ });
214
+ } finally {
215
+ setStoppingSleep(false);
216
+ }
217
+ };
218
+
219
+ const handleResolveHook = useCallback(
220
+ async (payload: unknown) => {
221
+ if (resolvingHook) return;
222
+ if (!onResolveHook) {
223
+ toast.error('Unable to resolve hook', {
224
+ description: 'No resolve handler provided.',
225
+ });
226
+ return;
227
+ }
228
+ if (!hookToken) {
229
+ toast.error('Unable to resolve hook', {
230
+ description:
231
+ 'Missing hook token. Try refreshing the run data and retry.',
232
+ });
233
+ return;
234
+ }
235
+
236
+ try {
237
+ setResolvingHook(true);
238
+ const candidate = spanDetailData ?? data;
239
+ const hook = isHook(candidate) ? candidate : undefined;
240
+ await onResolveHook(hookToken, payload, hook);
241
+ toast.success('Hook resolved', {
242
+ description: 'The payload has been sent and the hook resolved.',
243
+ });
244
+ setShowResolveHookModal(false);
245
+ } catch (err) {
246
+ console.error('Failed to resolve hook:', err);
247
+ toast.error('Failed to resolve hook', {
248
+ description:
249
+ err instanceof Error ? err.message : 'An unknown error occurred',
250
+ });
251
+ } finally {
252
+ setResolvingHook(false);
253
+ }
254
+ },
255
+ [onResolveHook, hookToken, resolvingHook, spanDetailData, data]
256
+ );
257
+
258
+ if (!selected || !resource || !resourceId) {
259
+ return null;
260
+ }
261
+
262
+ const displayData = (spanDetailData ?? data) as
263
+ | WorkflowRun
264
+ | Step
265
+ | Hook
266
+ | Event;
267
+
268
+ return (
269
+ <div className={clsx('flex flex-col px-2')}>
270
+ {/* Wake up button for pending sleep calls */}
271
+ {resource === 'sleep' && canWakeUp && (
272
+ <div className="mb-3 pb-3 border-b border-gray-200 dark:border-gray-700">
273
+ <button
274
+ type="button"
275
+ onClick={handleWakeUp}
276
+ disabled={stoppingSleep}
277
+ className={clsx(
278
+ 'flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md w-full',
279
+ 'bg-amber-100 dark:bg-amber-900/30 text-amber-800 dark:text-amber-200',
280
+ 'hover:bg-amber-200 dark:hover:bg-amber-900/50',
281
+ 'disabled:opacity-50 disabled:cursor-not-allowed',
282
+ 'transition-colors',
283
+ stoppingSleep ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
284
+ )}
285
+ >
286
+ <Zap className="h-4 w-4" />
287
+ {stoppingSleep ? 'Waking up...' : 'Wake up'}
288
+ </button>
289
+ <p className="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
290
+ Interrupt this sleep call and wake up the run.
291
+ </p>
292
+ </div>
293
+ )}
294
+
295
+ {/* Resolve hook button for pending hooks */}
296
+ {resource === 'hook' && canResolveHook && (
297
+ <div className="mb-3 pb-3 border-b border-gray-200 dark:border-gray-700">
298
+ <button
299
+ type="button"
300
+ onClick={() => setShowResolveHookModal(true)}
301
+ disabled={resolvingHook}
302
+ className={clsx(
303
+ 'flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md w-full',
304
+ 'bg-primary text-primary-foreground hover:bg-primary/90',
305
+ 'disabled:opacity-50 disabled:cursor-not-allowed',
306
+ 'transition-colors',
307
+ resolvingHook ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
308
+ )}
309
+ >
310
+ <Send className="h-4 w-4" />
311
+ Resolve Hook
312
+ </button>
313
+ <p className="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
314
+ Send a JSON payload to resolve this hook.
315
+ </p>
316
+ </div>
317
+ )}
318
+
319
+ {/* Resolve Hook Modal */}
320
+ <ResolveHookModal
321
+ isOpen={showResolveHookModal}
322
+ onClose={() => setShowResolveHookModal(false)}
323
+ onSubmit={handleResolveHook}
324
+ isSubmitting={resolvingHook}
325
+ />
326
+
327
+ {/* Content display */}
328
+ <AttributePanel
329
+ data={displayData}
330
+ expiredAt={run.expiredAt}
331
+ isLoading={loading}
332
+ error={error ?? undefined}
333
+ onStreamClick={onStreamClick}
334
+ />
335
+ {resource !== 'run' && <EventsList events={selected.span.events} />}
336
+ </div>
337
+ );
338
+ }