@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
@@ -1,8 +1,48 @@
1
1
  'use client';
2
2
 
3
- import { Copy } from 'lucide-react';
4
- import { useToast } from '../../lib/toast';
5
- import { DataInspector } from '../ui/data-inspector';
3
+ import { Lock } from 'lucide-react';
4
+ import { useContext } from 'react';
5
+ import { CopyButton } from '../new-trace-viewer/components/copy-button';
6
+ import { Button } from '../ui/button';
7
+ import { DataInspector, DecryptClickContext } from '../ui/data-inspector';
8
+ import { Spinner } from '../ui/spinner';
9
+
10
+ const encryptedPlaceholderPreview = `{
11
+ "input": "[encrypted]",
12
+ "result": "[encrypted]"
13
+ }`;
14
+
15
+ export function EncryptedDataBlock() {
16
+ const ctx = useContext(DecryptClickContext);
17
+
18
+ return (
19
+ <div className="relative min-h-20 overflow-hidden rounded-md border border-gray-alpha-400 bg-background-100">
20
+ <pre
21
+ aria-hidden="true"
22
+ className="pointer-events-none m-0 select-none p-3 font-mono text-label-12 text-gray-900 blur-[4px]"
23
+ >
24
+ {encryptedPlaceholderPreview}
25
+ </pre>
26
+ <div className="absolute inset-0 flex items-center justify-center">
27
+ {ctx ? (
28
+ <Button onClick={ctx.onDecrypt} disabled={ctx.isDecrypting} size="xs">
29
+ {ctx.isDecrypting ? (
30
+ <Spinner size={10} />
31
+ ) : (
32
+ <Lock className="h-3 w-3" />
33
+ )}
34
+ <span>Decrypt</span>
35
+ </Button>
36
+ ) : (
37
+ <span className="inline-flex items-center gap-1 rounded border border-gray-alpha-400 bg-gray-100 px-1.5 py-0.5 text-button-12 font-medium text-gray-700">
38
+ <Lock className="h-3 w-3" />
39
+ Encrypted
40
+ </span>
41
+ )}
42
+ </div>
43
+ </div>
44
+ );
45
+ }
6
46
 
7
47
  const serializeForClipboard = (value: unknown): string => {
8
48
  if (typeof value === 'string') return value;
@@ -21,31 +61,13 @@ const serializeForClipboard = (value: unknown): string => {
21
61
  };
22
62
 
23
63
  export function CopyableDataBlock({ data }: { data: unknown }) {
24
- const toast = useToast();
25
64
  return (
26
- <div
27
- className="relative overflow-x-auto rounded-md border p-3 pt-9"
28
- style={{ borderColor: 'var(--ds-gray-300)' }}
29
- >
30
- <button
31
- type="button"
32
- aria-label="Copy data"
33
- title="Copy"
34
- className="!absolute !right-2 !top-2 !flex !h-6 !w-6 !items-center !justify-center !rounded-md !border !bg-[var(--ds-background-100)] !text-[var(--ds-gray-800)] transition-transform transition-colors duration-100 hover:!bg-[var(--ds-gray-alpha-200)] active:!scale-95 active:!bg-[var(--ds-gray-alpha-300)]"
35
- style={{ borderColor: 'var(--ds-gray-300)' }}
36
- onClick={() => {
37
- navigator.clipboard
38
- .writeText(serializeForClipboard(data))
39
- .then(() => {
40
- toast.success('Copied to clipboard');
41
- })
42
- .catch(() => {
43
- toast.error('Failed to copy');
44
- });
45
- }}
46
- >
47
- <Copy size={12} />
48
- </button>
65
+ <div className="relative overflow-x-auto rounded-md border border-gray-alpha-400 p-3">
66
+ <CopyButton
67
+ copyText={serializeForClipboard(data)}
68
+ ariaLabel="Copy data"
69
+ className="absolute right-2 top-2 z-10 flex h-6 w-6 items-center justify-center rounded-md border border-gray-alpha-400 !bg-background-100 p-0 text-gray-900 transition-transform transition-colors duration-100 hover:bg-gray-200 active:scale-95 active:bg-gray-300"
70
+ />
49
71
  <DataInspector data={data} />
50
72
  </div>
51
73
  );
@@ -1,83 +1,143 @@
1
- import { ChevronRight } from 'lucide-react';
2
- import type { ReactNode } from 'react';
1
+ import { ChevronDown, ChevronRight } from 'lucide-react';
2
+ import { type ReactNode, useState } from 'react';
3
+ import { cn } from '../../lib/utils';
3
4
 
4
5
  export function DetailCard({
5
6
  summary,
6
7
  children,
7
8
  onToggle,
8
9
  disabled = false,
10
+ defaultOpen = false,
11
+ variant = 'section',
12
+ trailing,
9
13
  summaryClassName,
10
14
  contentClassName,
11
15
  }: {
12
16
  summary: ReactNode;
13
17
  children?: ReactNode;
14
- /** Called when the detail card is expanded/collapsed */
15
18
  onToggle?: (open: boolean) => void;
16
- /** Renders a non-expandable summary card when true. */
17
19
  disabled?: boolean;
18
- /** Extra classes for the summary row. */
20
+ defaultOpen?: boolean;
21
+ variant?: 'section' | 'card';
22
+ trailing?: ReactNode;
19
23
  summaryClassName?: string;
20
- /** Extra classes for expanded content wrapper. */
21
24
  contentClassName?: string;
22
25
  }) {
23
- if (disabled) {
26
+ const [open, setOpen] = useState(defaultOpen);
27
+
28
+ const handleToggle = (e: React.SyntheticEvent<HTMLDetailsElement>) => {
29
+ // React surfaces `toggle` as a bubbling synthetic event even though the
30
+ // native event doesn't bubble. Without this guard, a nested <details>
31
+ // (e.g. an event card inside the Events section) collapsing would flip
32
+ // the outer DetailCard's state. Only react to direct toggles.
33
+ if (e.target !== e.currentTarget) return;
34
+ const next = e.currentTarget.open;
35
+ setOpen(next);
36
+ onToggle?.(next);
37
+ };
38
+
39
+ if (variant === 'card') {
40
+ if (disabled) {
41
+ return (
42
+ <div
43
+ className={cn(
44
+ 'list-none px-3 py-4 bg-background-200 [&::-webkit-details-marker]:hidden',
45
+ summaryClassName
46
+ )}
47
+ style={{ cursor: 'not-allowed', opacity: 0.8 }}
48
+ >
49
+ {summary}
50
+ </div>
51
+ );
52
+ }
24
53
  return (
25
- <div
26
- className={`rounded-md border px-2.5 py-1.5 text-xs ${summaryClassName ?? ''}`}
27
- style={{
28
- borderColor: 'var(--ds-gray-300)',
29
- backgroundColor: 'var(--ds-gray-100)',
30
- color: 'var(--ds-gray-700)',
31
- cursor: 'not-allowed',
32
- opacity: 0.8,
33
- }}
54
+ <details
55
+ className="group/card last:border-b border-gray-alpha-400"
56
+ open={open}
57
+ onToggle={handleToggle}
34
58
  >
35
- {summary}
36
- </div>
59
+ <summary
60
+ className={cn(
61
+ 'list-none cursor-pointer px-3 py-4 border-t border-gray-alpha-400 bg-background-200 hover:bg-gray-100 [&::-webkit-details-marker]:hidden',
62
+ summaryClassName
63
+ )}
64
+ >
65
+ <span className="flex items-center gap-1.5">
66
+ <ChevronRight
67
+ size={14}
68
+ className={cn(
69
+ 'shrink-0 text-gray-700 group-hover/card:text-gray-1000',
70
+ open && 'rotate-90'
71
+ )}
72
+ />
73
+ {summary}
74
+ </span>
75
+ </summary>
76
+ <div className={contentClassName}>{children}</div>
77
+ </details>
37
78
  );
38
79
  }
39
80
 
40
- return (
41
- <details
42
- className="group"
43
- onToggle={(e) => onToggle?.((e.target as HTMLDetailsElement).open)}
44
- >
45
- <summary
46
- className={`cursor-pointer rounded-md border px-2.5 py-1.5 text-xs hover:brightness-95 [&::-webkit-details-marker]:hidden ${summaryClassName ?? ''}`}
47
- style={{
48
- borderColor: 'var(--ds-gray-300)',
49
- backgroundColor: 'var(--ds-gray-100)',
50
- color: 'var(--ds-gray-900)',
51
- listStyle: 'none',
52
- }}
53
- >
54
- <span className="flex items-center gap-1.5">
55
- <ChevronRight
56
- size={14}
57
- className="shrink-0 transition-transform group-open:rotate-90"
58
- />
59
- {summary}
60
- </span>
61
- </summary>
62
- {/* Expanded content with connecting line */}
63
- <div className={`relative pl-6 mt-3 ${contentClassName ?? ''}`}>
64
- {/* Curved connecting line - vertical part from summary */}
65
- <div
66
- className="absolute left-3 -top-3 w-px h-3"
67
- style={{ backgroundColor: 'var(--ds-gray-400)' }}
68
- />
69
- {/* Curved corner */}
70
- <div
71
- className="absolute left-3 top-0 w-3 h-3 border-l border-b rounded-bl-lg"
72
- style={{ borderColor: 'var(--ds-gray-400)' }}
73
- />
74
- {/* Horizontal part to content */}
81
+ // Shared height with the expandable summary row. Keeps the trailing /
82
+ // disabled / chevron variants visually identical in height regardless of
83
+ // what's in the trailing slot.
84
+ const rowClasses =
85
+ 'flex h-9 items-center gap-2 px-2 -mx-2 text-heading-14 font-medium my-2';
86
+
87
+ if (trailing) {
88
+ return (
89
+ <section className="-mx-4 border-t px-4 border-gray-alpha-400">
90
+ <div className={cn(rowClasses, summaryClassName)}>
91
+ <div className="isolate relative shrink-0 w-3.5 h-3.5 text-gray-700">
92
+ <ChevronRight
93
+ size={14}
94
+ className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
95
+ />
96
+ </div>
97
+ <span className="min-w-0 flex-1">{summary}</span>
98
+ <div className="shrink-0 pr-1">{trailing}</div>
99
+ </div>
100
+ </section>
101
+ );
102
+ }
103
+
104
+ if (disabled) {
105
+ return (
106
+ <section className="-mx-4 border-t px-4 border-gray-alpha-400">
75
107
  <div
76
- className="absolute left-6 top-3 w-0 h-px -translate-y-px"
77
- style={{ backgroundColor: 'var(--ds-gray-400)' }}
78
- />
79
- <div>{children}</div>
80
- </div>
81
- </details>
108
+ className={cn(rowClasses, summaryClassName)}
109
+ style={{ color: 'var(--ds-gray-700)', cursor: 'not-allowed' }}
110
+ >
111
+ <span className="min-w-0 flex-1">{summary}</span>
112
+ </div>
113
+ </section>
114
+ );
115
+ }
116
+
117
+ return (
118
+ <section className="-mx-4 border-t px-4 border-gray-alpha-400">
119
+ <details className="group" open={open} onToggle={handleToggle}>
120
+ <summary
121
+ className={cn(
122
+ 'group/trigger list-none cursor-pointer rounded hover:bg-gray-alpha-100 [&::-webkit-details-marker]:hidden',
123
+ rowClasses,
124
+ summaryClassName
125
+ )}
126
+ >
127
+ <div className="isolate relative shrink-0 text-gray-700 group-hover/trigger:text-gray-1000 w-3.5 h-3.5">
128
+ <ChevronRight
129
+ size={14}
130
+ className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-100 group-open:opacity-0"
131
+ />
132
+ <ChevronDown
133
+ size={14}
134
+ className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-0 group-open:opacity-100"
135
+ />
136
+ </div>
137
+ <span className="min-w-0 flex-1">{summary}</span>
138
+ </summary>
139
+ <div className={cn('mb-3', contentClassName)}>{children}</div>
140
+ </details>
141
+ </section>
82
142
  );
83
143
  }
@@ -5,11 +5,11 @@ import clsx from 'clsx';
5
5
  import { Send, Zap } from 'lucide-react';
6
6
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
7
  import { useToast } from '../../lib/toast';
8
- import { isEncryptedMarker } from '../../lib/hydration';
9
- import { DecryptButton } from '../ui/decrypt-button';
8
+ import { DecryptClickContext } from '../ui/data-inspector';
10
9
  import { AttributePanel } from './attribute-panel';
11
10
  import { EventsList } from './events-list';
12
11
  import { ResolveHookModal } from './resolve-hook-modal';
12
+ import { useSidebarDataOptional } from './sidebar-data-context';
13
13
 
14
14
  // Type guards for runtime validation of span attribute data
15
15
  function isStep(data: unknown): data is Step {
@@ -56,6 +56,7 @@ export interface SelectedSpanInfo {
56
56
  export function EntityDetailPanel({
57
57
  run,
58
58
  onStreamClick,
59
+ onRunClick,
59
60
  spanDetailData,
60
61
  spanDetailError,
61
62
  spanDetailLoading,
@@ -71,6 +72,8 @@ export function EntityDetailPanel({
71
72
  run: WorkflowRun;
72
73
  /** Callback when a stream reference is clicked */
73
74
  onStreamClick?: (streamId: string) => void;
75
+ /** Callback when a run reference is clicked */
76
+ onRunClick?: (runId: string) => void;
74
77
  /** Pre-fetched span detail data for the selected span. */
75
78
  spanDetailData: WorkflowRun | Step | Hook | Event | null;
76
79
  /** Error from external span detail fetch. */
@@ -114,6 +117,9 @@ export function EntityDetailPanel({
114
117
  new Set()
115
118
  );
116
119
 
120
+ const sidebar = useSidebarDataOptional();
121
+ const hasEncryptedData = Boolean(sidebar?.hasEncryptedData && !encryptionKey);
122
+
117
123
  const data = selectedSpan?.data;
118
124
  const rawEvents = selectedSpan?.rawEvents;
119
125
  const rawEventsLength = rawEvents?.length ?? 0;
@@ -209,17 +215,6 @@ export function EntityDetailPanel({
209
215
  const error = spanDetailError ?? undefined;
210
216
  const loading = spanDetailLoading ?? false;
211
217
 
212
- const hasEncryptedFields = useMemo(() => {
213
- if (!spanDetailData) return false;
214
- const d = spanDetailData as Record<string, unknown>;
215
- return (
216
- isEncryptedMarker(d.input) ||
217
- isEncryptedMarker(d.output) ||
218
- isEncryptedMarker(d.error) ||
219
- isEncryptedMarker(d.metadata)
220
- );
221
- }, [spanDetailData]);
222
-
223
218
  // Get the hook token for resolving (prefer fetched data, then hooks array fallback)
224
219
  const hookToken = useMemo(() => {
225
220
  if (resource !== 'hook' || !resourceId) return undefined;
@@ -347,155 +342,104 @@ export function EntityDetailPanel({
347
342
  return null;
348
343
  }
349
344
 
350
- const resourceLabel = resource.charAt(0).toUpperCase() + resource.slice(1);
351
345
  const hasPendingActions =
352
346
  (resource === 'sleep' && canWakeUp) ||
353
347
  (resource === 'hook' && canResolveHook);
354
- const runStateLabel = run.completedAt ? 'Completed' : 'Live';
355
348
 
356
349
  return (
357
350
  <div className="flex h-full flex-col">
358
- <div
359
- className="border-b px-3 py-3"
360
- style={{ borderColor: 'var(--ds-gray-200)' }}
351
+ <DecryptClickContext.Provider
352
+ value={
353
+ onDecrypt ? { onDecrypt, isDecrypting, hasEncryptedData } : undefined
354
+ }
361
355
  >
362
- <div className="flex items-start justify-between gap-2">
363
- <div className="min-w-0">
364
- <div className="flex items-center gap-2">
365
- <span
366
- className="inline-flex items-center rounded-full border px-2 py-0.5 text-[13px] font-medium"
367
- style={{
368
- borderColor: 'var(--ds-gray-300)',
369
- color: 'var(--ds-gray-900)',
370
- backgroundColor: 'var(--ds-background-100)',
371
- }}
372
- >
373
- {resourceLabel}
374
- </span>
375
- <span
376
- className="text-[13px]"
377
- style={{
378
- color: run.completedAt
379
- ? 'var(--ds-gray-700)'
380
- : 'var(--ds-green-800)',
381
- }}
356
+ <div className="flex-1 overflow-y-auto px-4 pb-8">
357
+ {hasPendingActions && (
358
+ <div
359
+ className="mb-4 rounded-lg border p-2"
360
+ style={{
361
+ borderColor: 'var(--ds-gray-300)',
362
+ backgroundColor: 'var(--ds-gray-100)',
363
+ }}
364
+ >
365
+ <p
366
+ className="mb-2 px-1 text-[13px] font-medium uppercase tracking-wide"
367
+ style={{ color: 'var(--ds-gray-700)' }}
382
368
  >
383
- {runStateLabel}
384
- </span>
369
+ Actions
370
+ </p>
371
+ <div className="flex flex-col gap-2">
372
+ {/* Wake up button for pending sleep calls */}
373
+ {resource === 'sleep' && canWakeUp && (
374
+ <button
375
+ type="button"
376
+ onClick={handleWakeUp}
377
+ disabled={stoppingSleep}
378
+ className={clsx(
379
+ 'flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
380
+ 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
381
+ stoppingSleep
382
+ ? 'opacity-50 cursor-not-allowed'
383
+ : 'cursor-pointer'
384
+ )}
385
+ style={{
386
+ background: 'var(--ds-amber-200)',
387
+ color: 'var(--ds-amber-900)',
388
+ }}
389
+ >
390
+ <Zap className="h-4 w-4" />
391
+ {stoppingSleep ? 'Waking up...' : 'Wake Up Sleep'}
392
+ </button>
393
+ )}
394
+
395
+ {/* Resolve hook button for pending hooks */}
396
+ {resource === 'hook' && canResolveHook && (
397
+ <button
398
+ type="button"
399
+ onClick={() => setShowResolveHookModal(true)}
400
+ disabled={resolvingHook}
401
+ className={clsx(
402
+ 'flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
403
+ 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
404
+ resolvingHook
405
+ ? 'opacity-50 cursor-not-allowed'
406
+ : 'cursor-pointer'
407
+ )}
408
+ style={{
409
+ background: 'var(--ds-gray-1000)',
410
+ color: 'var(--ds-background-100)',
411
+ }}
412
+ >
413
+ <Send className="h-4 w-4" />
414
+ Resolve Hook
415
+ </button>
416
+ )}
417
+ </div>
385
418
  </div>
386
- <p
387
- className="mt-1 truncate font-mono text-[13px]"
388
- style={{ color: 'var(--ds-gray-700)' }}
389
- title={resourceId}
390
- >
391
- {resourceId}
392
- </p>
393
- </div>
394
- {(hasEncryptedFields || encryptionKey) && onDecrypt && (
395
- <DecryptButton
396
- decrypted={!!encryptionKey}
397
- loading={isDecrypting}
398
- onClick={onDecrypt}
399
- />
400
419
  )}
401
- </div>
402
- </div>
403
-
404
- <div className="flex-1 overflow-y-auto px-3 pt-3 pb-8">
405
- {hasPendingActions && (
406
- <div
407
- className="mb-4 rounded-lg border p-2"
408
- style={{
409
- borderColor: 'var(--ds-gray-300)',
410
- backgroundColor: 'var(--ds-gray-100)',
411
- }}
412
- >
413
- <p
414
- className="mb-2 px-1 text-[13px] font-medium uppercase tracking-wide"
415
- style={{ color: 'var(--ds-gray-700)' }}
416
- >
417
- Actions
418
- </p>
419
- <div className="flex flex-col gap-2">
420
- {/* Wake up button for pending sleep calls */}
421
- {resource === 'sleep' && canWakeUp && (
422
- <button
423
- type="button"
424
- onClick={handleWakeUp}
425
- disabled={stoppingSleep}
426
- className={clsx(
427
- 'flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
428
- 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
429
- stoppingSleep
430
- ? 'opacity-50 cursor-not-allowed'
431
- : 'cursor-pointer'
432
- )}
433
- style={{
434
- background: 'var(--ds-amber-200)',
435
- color: 'var(--ds-amber-900)',
436
- }}
437
- >
438
- <Zap className="h-4 w-4" />
439
- {stoppingSleep ? 'Waking up...' : 'Wake Up Sleep'}
440
- </button>
441
- )}
442
-
443
- {/* Resolve hook button for pending hooks */}
444
- {resource === 'hook' && canResolveHook && (
445
- <button
446
- type="button"
447
- onClick={() => setShowResolveHookModal(true)}
448
- disabled={resolvingHook}
449
- className={clsx(
450
- 'flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
451
- 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
452
- resolvingHook
453
- ? 'opacity-50 cursor-not-allowed'
454
- : 'cursor-pointer'
455
- )}
456
- style={{
457
- background: 'var(--ds-gray-1000)',
458
- color: 'var(--ds-background-100)',
459
- }}
460
- >
461
- <Send className="h-4 w-4" />
462
- Resolve Hook
463
- </button>
464
- )}
465
- </div>
466
- </div>
467
- )}
468
-
469
- <div className="space-y-4">
470
- <section>
471
- <h3
472
- className="mb-2 text-[13px] font-medium uppercase tracking-wide"
473
- style={{ color: 'var(--ds-gray-700)' }}
474
- >
475
- Details
476
- </h3>
477
- <AttributePanel
478
- data={displayData}
479
- moduleSpecifier={moduleSpecifier}
480
- expiredAt={run.expiredAt}
481
- isLoading={loading}
482
- error={error ?? undefined}
483
- onStreamClick={onStreamClick}
484
- resource={resource}
485
- />
486
- </section>
420
+
421
+ <AttributePanel
422
+ data={displayData}
423
+ moduleSpecifier={moduleSpecifier}
424
+ expiredAt={run.expiredAt}
425
+ isLoading={loading}
426
+ error={error ?? undefined}
427
+ onStreamClick={onStreamClick}
428
+ onRunClick={onRunClick}
429
+ onDecrypt={onDecrypt}
430
+ isDecrypting={isDecrypting}
431
+ resource={resource}
432
+ />
487
433
 
488
434
  {resource !== 'run' && rawEvents && (
489
- <section>
490
- <EventsList
491
- events={rawEvents}
492
- onLoadEventData={onLoadEventData}
493
- encryptionKey={encryptionKey}
494
- />
495
- </section>
435
+ <EventsList
436
+ events={rawEvents}
437
+ onLoadEventData={onLoadEventData}
438
+ encryptionKey={encryptionKey}
439
+ />
496
440
  )}
497
441
  </div>
498
- </div>
442
+ </DecryptClickContext.Provider>
499
443
 
500
444
  {/* Resolve Hook Modal */}
501
445
  <ResolveHookModal