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

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 (194) hide show
  1. package/README.md +1 -1
  2. package/dist/components/error-boundary.js.map +1 -1
  3. package/dist/components/event-list-view.d.ts +3 -1
  4. package/dist/components/event-list-view.d.ts.map +1 -1
  5. package/dist/components/event-list-view.js +53 -53
  6. package/dist/components/event-list-view.js.map +1 -1
  7. package/dist/components/hook-actions.js.map +1 -1
  8. package/dist/components/index.d.ts +3 -1
  9. package/dist/components/index.d.ts.map +1 -1
  10. package/dist/components/index.js +3 -1
  11. package/dist/components/index.js.map +1 -1
  12. package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
  13. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
  14. package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
  15. package/dist/components/new-trace-viewer/components/copy-button.js.map +1 -0
  16. package/dist/components/new-trace-viewer/components/event-list.d.ts +8 -0
  17. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
  18. package/dist/components/new-trace-viewer/components/event-list.js +35 -0
  19. package/dist/components/new-trace-viewer/components/event-list.js.map +1 -0
  20. package/dist/components/new-trace-viewer/components/split-pane.d.ts +14 -0
  21. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
  22. package/dist/components/new-trace-viewer/components/split-pane.js +100 -0
  23. package/dist/components/new-trace-viewer/components/split-pane.js.map +1 -0
  24. package/dist/components/new-trace-viewer/components/timeline.d.ts +32 -0
  25. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
  26. package/dist/components/new-trace-viewer/components/timeline.js +134 -0
  27. package/dist/components/new-trace-viewer/components/timeline.js.map +1 -0
  28. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  29. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  30. package/dist/components/new-trace-viewer/context.js +49 -0
  31. package/dist/components/new-trace-viewer/context.js.map +1 -0
  32. package/dist/components/new-trace-viewer/detail-panel.d.ts +10 -0
  33. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -0
  34. package/dist/components/new-trace-viewer/detail-panel.js +11 -0
  35. package/dist/components/new-trace-viewer/detail-panel.js.map +1 -0
  36. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  37. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  38. package/dist/components/new-trace-viewer/icons.js +15 -0
  39. package/dist/components/new-trace-viewer/icons.js.map +1 -0
  40. package/dist/components/new-trace-viewer/trace-viewer.d.ts +8 -0
  41. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  42. package/dist/components/new-trace-viewer/trace-viewer.js +329 -0
  43. package/dist/components/new-trace-viewer/trace-viewer.js.map +1 -0
  44. package/dist/components/new-trace-viewer/types.d.ts +1 -0
  45. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  46. package/dist/components/new-trace-viewer/types.js +2 -0
  47. package/dist/components/new-trace-viewer/types.js.map +1 -0
  48. package/dist/components/new-trace-viewer/util/constants.d.ts +1 -0
  49. package/dist/components/new-trace-viewer/util/constants.d.ts.map +1 -0
  50. package/dist/components/new-trace-viewer/util/constants.js +2 -0
  51. package/dist/components/new-trace-viewer/util/constants.js.map +1 -0
  52. package/dist/components/new-trace-viewer/utils.d.ts +47 -0
  53. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  54. package/dist/components/new-trace-viewer/utils.js +358 -0
  55. package/dist/components/new-trace-viewer/utils.js.map +1 -0
  56. package/dist/components/run-trace-view.d.ts +2 -1
  57. package/dist/components/run-trace-view.d.ts.map +1 -1
  58. package/dist/components/run-trace-view.js +2 -2
  59. package/dist/components/run-trace-view.js.map +1 -1
  60. package/dist/components/sidebar/attribute-panel.d.ts +7 -1
  61. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  62. package/dist/components/sidebar/attribute-panel.js +76 -47
  63. package/dist/components/sidebar/attribute-panel.js.map +1 -1
  64. package/dist/components/sidebar/conversation-view.js.map +1 -1
  65. package/dist/components/sidebar/copyable-data-block.js +1 -1
  66. package/dist/components/sidebar/copyable-data-block.js.map +1 -1
  67. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  68. package/dist/components/sidebar/detail-card.js +1 -1
  69. package/dist/components/sidebar/detail-card.js.map +1 -1
  70. package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
  71. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  72. package/dist/components/sidebar/entity-detail-panel.js +16 -36
  73. package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
  74. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  75. package/dist/components/sidebar/events-list.js +2 -2
  76. package/dist/components/sidebar/events-list.js.map +1 -1
  77. package/dist/components/sidebar/resolve-hook-modal.js.map +1 -1
  78. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  79. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  80. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  81. package/dist/components/sidebar/sidebar-data-context.js.map +1 -0
  82. package/dist/components/stream-viewer.d.ts +1 -1
  83. package/dist/components/stream-viewer.d.ts.map +1 -1
  84. package/dist/components/stream-viewer.js +2 -26
  85. package/dist/components/stream-viewer.js.map +1 -1
  86. package/dist/components/trace-viewer/components/map.js.map +1 -1
  87. package/dist/components/trace-viewer/components/markers.js.map +1 -1
  88. package/dist/components/trace-viewer/components/node.js.map +1 -1
  89. package/dist/components/trace-viewer/components/search-input.js.map +1 -1
  90. package/dist/components/trace-viewer/components/search.js.map +1 -1
  91. package/dist/components/trace-viewer/components/span-content.js.map +1 -1
  92. package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -1
  93. package/dist/components/trace-viewer/components/span-segments.js.map +1 -1
  94. package/dist/components/trace-viewer/components/span-strategies.js.map +1 -1
  95. package/dist/components/trace-viewer/components/ui.js.map +1 -1
  96. package/dist/components/trace-viewer/components/zoom-button.js.map +1 -1
  97. package/dist/components/trace-viewer/components/zoom-icons.js.map +1 -1
  98. package/dist/components/trace-viewer/context.js.map +1 -1
  99. package/dist/components/trace-viewer/index.js.map +1 -1
  100. package/dist/components/trace-viewer/trace-viewer.js.map +1 -1
  101. package/dist/components/trace-viewer/types.js.map +1 -1
  102. package/dist/components/trace-viewer/util/constants.js.map +1 -1
  103. package/dist/components/trace-viewer/util/scrollbar-width.js.map +1 -1
  104. package/dist/components/trace-viewer/util/timing.js.map +1 -1
  105. package/dist/components/trace-viewer/util/tree.js.map +1 -1
  106. package/dist/components/trace-viewer/util/use-immediate-style.js.map +1 -1
  107. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -1
  108. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +1 -1
  109. package/dist/components/trace-viewer/worker.js.map +1 -1
  110. package/dist/components/trace-viewer-new.d.ts +10 -0
  111. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  112. package/dist/components/trace-viewer-new.js +23 -0
  113. package/dist/components/trace-viewer-new.js.map +1 -0
  114. package/dist/components/ui/alert.js.map +1 -1
  115. package/dist/components/ui/card.js.map +1 -1
  116. package/dist/components/ui/data-inspector.d.ts +38 -1
  117. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  118. package/dist/components/ui/data-inspector.js +211 -21
  119. package/dist/components/ui/data-inspector.js.map +1 -1
  120. package/dist/components/ui/decrypt-button.js.map +1 -1
  121. package/dist/components/ui/error-card.js.map +1 -1
  122. package/dist/components/ui/error-stack-block.js.map +1 -1
  123. package/dist/components/ui/inspector-theme.d.ts +9 -10
  124. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  125. package/dist/components/ui/inspector-theme.js +38 -71
  126. package/dist/components/ui/inspector-theme.js.map +1 -1
  127. package/dist/components/ui/load-more-button.js.map +1 -1
  128. package/dist/components/ui/menu-dropdown.js.map +1 -1
  129. package/dist/components/ui/skeleton.js.map +1 -1
  130. package/dist/components/ui/spinner.js.map +1 -1
  131. package/dist/components/ui/timestamp-tooltip.js.map +1 -1
  132. package/dist/components/workflow-trace-view.d.ts +3 -1
  133. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  134. package/dist/components/workflow-trace-view.js +9 -4
  135. package/dist/components/workflow-trace-view.js.map +1 -1
  136. package/dist/components/workflow-traces/event-colors.js.map +1 -1
  137. package/dist/components/workflow-traces/trace-colors.js.map +1 -1
  138. package/dist/components/workflow-traces/trace-span-construction.js +1 -1
  139. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
  140. package/dist/components/workflow-traces/trace-time-utils.js.map +1 -1
  141. package/dist/hooks/use-dark-mode.js.map +1 -1
  142. package/dist/index.d.ts +2 -1
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +1 -1
  145. package/dist/index.js.map +1 -1
  146. package/dist/lib/event-analysis.js.map +1 -1
  147. package/dist/lib/event-materialization.js.map +1 -1
  148. package/dist/lib/hydration.d.ts +8 -1
  149. package/dist/lib/hydration.d.ts.map +1 -1
  150. package/dist/lib/hydration.js +32 -2
  151. package/dist/lib/hydration.js.map +1 -1
  152. package/dist/lib/stream-display.d.ts +18 -0
  153. package/dist/lib/stream-display.d.ts.map +1 -0
  154. package/dist/lib/stream-display.js +30 -0
  155. package/dist/lib/stream-display.js.map +1 -0
  156. package/dist/lib/toast.js.map +1 -1
  157. package/dist/lib/trace-builder.js.map +1 -1
  158. package/dist/lib/utils.d.ts +7 -9
  159. package/dist/lib/utils.d.ts.map +1 -1
  160. package/dist/lib/utils.js +17 -22
  161. package/dist/lib/utils.js.map +1 -1
  162. package/dist/styles.css +470 -1
  163. package/package.json +4 -3
  164. package/src/components/event-list-view.tsx +181 -173
  165. package/src/components/index.ts +11 -1
  166. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  167. package/src/components/new-trace-viewer/components/event-list.tsx +109 -0
  168. package/src/components/new-trace-viewer/components/split-pane.tsx +186 -0
  169. package/src/components/new-trace-viewer/components/timeline.tsx +378 -0
  170. package/src/components/new-trace-viewer/context.tsx +94 -0
  171. package/src/components/new-trace-viewer/detail-panel.tsx +57 -0
  172. package/src/components/new-trace-viewer/icons.tsx +79 -0
  173. package/src/components/new-trace-viewer/trace-viewer.tsx +585 -0
  174. package/src/components/new-trace-viewer/types.ts +0 -0
  175. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  176. package/src/components/new-trace-viewer/utils.ts +524 -0
  177. package/src/components/run-trace-view.tsx +3 -0
  178. package/src/components/sidebar/attribute-panel.tsx +200 -138
  179. package/src/components/sidebar/copyable-data-block.tsx +1 -1
  180. package/src/components/sidebar/detail-card.tsx +1 -19
  181. package/src/components/sidebar/entity-detail-panel.tsx +91 -147
  182. package/src/components/sidebar/events-list.tsx +9 -6
  183. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  184. package/src/components/stream-viewer.tsx +18 -40
  185. package/src/components/trace-viewer-new.tsx +49 -0
  186. package/src/components/ui/data-inspector.tsx +436 -31
  187. package/src/components/ui/inspector-theme.ts +39 -81
  188. package/src/components/workflow-trace-view.tsx +12 -2
  189. package/src/components/workflow-traces/trace-span-construction.ts +1 -1
  190. package/src/index.ts +2 -0
  191. package/src/lib/hydration.ts +37 -0
  192. package/src/lib/stream-display.ts +48 -0
  193. package/src/lib/utils.ts +18 -23
  194. package/src/styles.css +470 -1
@@ -1,17 +1,16 @@
1
1
  /**
2
2
  * Shared theme configuration for react-inspector's ObjectInspector.
3
3
  *
4
- * Color choices are inspired by Node.js `util.inspect()` ANSI stylizing:
5
- * - property names: unstyled (default foreground dark text on light, light text on dark)
6
- * - strings / symbols: green
7
- * - numbers / booleans / bigint: yellow
8
- * - null: bold (foreground)
9
- * - undefined: grey
10
- * - regexp: red
11
- * - functions (special): cyan
12
- * - date: magenta
4
+ * Colors follow Geist's Shiki JSON palette so the inspector reads the same
5
+ * as highlighted code blocks across the product:
6
+ * - property names / punctuation: --ds-gray-1000 (default foreground)
7
+ * - strings / numbers / booleans: --ds-green-900
8
+ * - null / undefined: --ds-gray-900 (muted)
9
+ * - regexp / function: --ds-purple-900
10
+ * - date: --ds-pink-900
13
11
  *
14
- * See: https://github.com/nodejs/node/blob/main/lib/internal/util/inspect.js
12
+ * Because the `--ds-*` tokens adapt to theme automatically, the light and
13
+ * dark objects are intentionally identical.
15
14
  */
16
15
 
17
16
  // ---------------------------------------------------------------------------
@@ -25,11 +24,11 @@ export interface InspectorThemeExtended {
25
24
  }
26
25
 
27
26
  export const inspectorThemeExtendedLight: InspectorThemeExtended = {
28
- OBJECT_VALUE_DATE_COLOR: '#a21caf', // fuchsia-700
27
+ OBJECT_VALUE_DATE_COLOR: 'var(--ds-pink-900)',
29
28
  };
30
29
 
31
30
  export const inspectorThemeExtendedDark: InspectorThemeExtended = {
32
- OBJECT_VALUE_DATE_COLOR: '#e879f9', // fuchsia-400
31
+ OBJECT_VALUE_DATE_COLOR: 'var(--ds-pink-900)',
33
32
  };
34
33
 
35
34
  // ---------------------------------------------------------------------------
@@ -57,96 +56,55 @@ const shared = {
57
56
  // Light theme
58
57
  // ---------------------------------------------------------------------------
59
58
 
60
- export const inspectorThemeLight = {
59
+ const geistTheme = {
61
60
  ...shared,
62
61
 
63
62
  // Base text
64
63
  BASE_COLOR: 'var(--ds-gray-1000)',
65
64
 
66
- // Property names — unstyled, same as base foreground (Node: no style)
67
- OBJECT_NAME_COLOR: 'var(--ds-gray-900)',
65
+ // Property names — default foreground (matches JSON key color in Geist Shiki)
66
+ OBJECT_NAME_COLOR: 'var(--ds-gray-1000)',
68
67
 
69
- // Strings & symbols — green (Node: 'green')
70
- OBJECT_VALUE_STRING_COLOR: '#16a34a', // green-600
71
- OBJECT_VALUE_SYMBOL_COLOR: '#16a34a',
68
+ // Strings & symbols — green
69
+ OBJECT_VALUE_STRING_COLOR: 'var(--ds-green-900)',
70
+ OBJECT_VALUE_SYMBOL_COLOR: 'var(--ds-green-900)',
72
71
 
73
- // Numbers & booleans — yellow/amber (Node: 'yellow')
74
- OBJECT_VALUE_NUMBER_COLOR: '#b45309', // amber-700 (readable on white)
75
- OBJECT_VALUE_BOOLEAN_COLOR: '#b45309',
72
+ // Numbers & booleans — green (Geist JSON tokens)
73
+ OBJECT_VALUE_NUMBER_COLOR: 'var(--ds-green-900)',
74
+ OBJECT_VALUE_BOOLEAN_COLOR: 'var(--ds-green-900)',
76
75
 
77
- // null — bold foreground (Node: 'bold')
76
+ // null — muted foreground
78
77
  OBJECT_VALUE_NULL_COLOR: 'var(--ds-gray-900)',
79
78
 
80
- // undefined — grey (Node: 'grey')
81
- OBJECT_VALUE_UNDEFINED_COLOR: 'var(--ds-gray-500)',
79
+ // undefined — muted foreground
80
+ OBJECT_VALUE_UNDEFINED_COLOR: 'var(--ds-gray-900)',
82
81
 
83
- // RegExp — red (Node regexp base uses green/red/yellow palette)
84
- OBJECT_VALUE_REGEXP_COLOR: '#dc2626', // red-600
82
+ // RegExp — purple
83
+ OBJECT_VALUE_REGEXP_COLOR: 'var(--ds-purple-900)',
85
84
 
86
- // Functions — cyan (Node: 'special' → 'cyan')
87
- OBJECT_VALUE_FUNCTION_PREFIX_COLOR: '#0891b2', // cyan-600
85
+ // Functions — purple
86
+ OBJECT_VALUE_FUNCTION_PREFIX_COLOR: 'var(--ds-purple-900)',
88
87
 
89
- // HTML (less relevant for data inspection, but reasonable defaults)
90
- HTML_TAG_COLOR: 'var(--ds-gray-500)',
91
- HTML_TAGNAME_COLOR: '#0891b2',
92
- HTML_ATTRIBUTE_NAME_COLOR: '#b45309',
93
- HTML_ATTRIBUTE_VALUE_COLOR: '#16a34a',
94
- HTML_COMMENT_COLOR: 'var(--ds-gray-400)',
95
- HTML_DOCTYPE_COLOR: 'var(--ds-gray-400)',
88
+ // HTML (rarely used here, kept consistent with the palette)
89
+ HTML_TAG_COLOR: 'var(--ds-gray-900)',
90
+ HTML_TAGNAME_COLOR: 'var(--ds-blue-900)',
91
+ HTML_ATTRIBUTE_NAME_COLOR: 'var(--ds-amber-900)',
92
+ HTML_ATTRIBUTE_VALUE_COLOR: 'var(--ds-green-900)',
93
+ HTML_COMMENT_COLOR: 'var(--ds-gray-700)',
94
+ HTML_DOCTYPE_COLOR: 'var(--ds-gray-700)',
96
95
 
97
96
  // Structural
98
- ARROW_COLOR: 'var(--ds-gray-500)',
97
+ ARROW_COLOR: 'var(--ds-gray-700)',
99
98
  TABLE_BORDER_COLOR: 'var(--ds-gray-300)',
100
99
  TABLE_TH_BACKGROUND_COLOR: 'var(--ds-gray-100)',
101
100
  TABLE_TH_HOVER_COLOR: 'var(--ds-gray-200)',
102
- TABLE_SORT_ICON_COLOR: 'var(--ds-gray-500)',
101
+ TABLE_SORT_ICON_COLOR: 'var(--ds-gray-700)',
103
102
  };
104
103
 
104
+ export const inspectorThemeLight = geistTheme;
105
+
105
106
  // ---------------------------------------------------------------------------
106
107
  // Dark theme
107
108
  // ---------------------------------------------------------------------------
108
109
 
109
- export const inspectorThemeDark = {
110
- ...shared,
111
-
112
- // Base text
113
- BASE_COLOR: 'var(--ds-gray-1000)',
114
-
115
- // Property names — white/light foreground (Node: unstyled = white in dark terminal)
116
- OBJECT_NAME_COLOR: 'var(--ds-gray-900)',
117
-
118
- // Strings & symbols — green (Node: 'green')
119
- OBJECT_VALUE_STRING_COLOR: '#4ade80', // green-400
120
- OBJECT_VALUE_SYMBOL_COLOR: '#4ade80',
121
-
122
- // Numbers & booleans — yellow (Node: 'yellow')
123
- OBJECT_VALUE_NUMBER_COLOR: '#facc15', // yellow-400
124
- OBJECT_VALUE_BOOLEAN_COLOR: '#facc15',
125
-
126
- // null — bold foreground / white (Node: 'bold')
127
- OBJECT_VALUE_NULL_COLOR: 'var(--ds-gray-1000)',
128
-
129
- // undefined — grey (Node: 'grey')
130
- OBJECT_VALUE_UNDEFINED_COLOR: 'var(--ds-gray-500)',
131
-
132
- // RegExp — red (Node regexp palette)
133
- OBJECT_VALUE_REGEXP_COLOR: '#f87171', // red-400
134
-
135
- // Functions — cyan (Node: 'special' → 'cyan')
136
- OBJECT_VALUE_FUNCTION_PREFIX_COLOR: '#22d3ee', // cyan-400
137
-
138
- // HTML
139
- HTML_TAG_COLOR: 'var(--ds-gray-500)',
140
- HTML_TAGNAME_COLOR: '#22d3ee',
141
- HTML_ATTRIBUTE_NAME_COLOR: '#facc15',
142
- HTML_ATTRIBUTE_VALUE_COLOR: '#4ade80',
143
- HTML_COMMENT_COLOR: 'var(--ds-gray-500)',
144
- HTML_DOCTYPE_COLOR: 'var(--ds-gray-500)',
145
-
146
- // Structural
147
- ARROW_COLOR: 'var(--ds-gray-500)',
148
- TABLE_BORDER_COLOR: 'var(--ds-gray-300)',
149
- TABLE_TH_BACKGROUND_COLOR: 'var(--ds-gray-100)',
150
- TABLE_TH_HOVER_COLOR: 'var(--ds-gray-200)',
151
- TABLE_SORT_ICON_COLOR: 'var(--ds-gray-500)',
152
- };
110
+ export const inspectorThemeDark = geistTheme;
@@ -17,6 +17,7 @@ import type { MouseEvent as ReactMouseEvent, ReactNode } from 'react';
17
17
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
18
18
  import { createPortal } from 'react-dom';
19
19
  import { useToast } from '../lib/toast';
20
+ import { buildTrace, type TraceWithMeta } from '../lib/trace-builder';
20
21
  import { ErrorBoundary } from './error-boundary';
21
22
  import {
22
23
  EntityDetailPanel,
@@ -36,7 +37,6 @@ import {
36
37
  getCustomSpanClassName,
37
38
  getCustomSpanEventClassName,
38
39
  } from './workflow-traces/trace-colors';
39
- import { buildTrace, type TraceWithMeta } from '../lib/trace-builder';
40
40
 
41
41
  /**
42
42
  * While a run is live, continuously grow root.duration and rescale so the
@@ -767,6 +767,7 @@ export const WorkflowTraceViewer = ({
767
767
  onResolveHook,
768
768
  onCancelRun,
769
769
  onStreamClick,
770
+ onRunClick,
770
771
  onSpanSelect,
771
772
  onLoadEventData,
772
773
  onLoadMoreSpans,
@@ -796,6 +797,8 @@ export const WorkflowTraceViewer = ({
796
797
  onCancelRun?: (runId: string) => Promise<void>;
797
798
  /** Callback when a stream reference is clicked in the detail panel */
798
799
  onStreamClick?: (streamId: string) => void;
800
+ /** Callback when a run reference is clicked in the detail panel */
801
+ onRunClick?: (runId: string) => void;
799
802
  /** Callback when a span is selected. */
800
803
  onSpanSelect?: (info: SpanSelectionInfo) => void;
801
804
  /** Callback to load event data for a specific event (lazy loading in sidebar) */
@@ -903,6 +906,12 @@ export const WorkflowTraceViewer = ({
903
906
  });
904
907
  }, [events, selectedSpan?.spanId]);
905
908
 
909
+ // Reset selected span when navigating to a different run
910
+ useEffect(() => {
911
+ setSelectedSpan(null);
912
+ setDeselectTrigger((n) => n + 1);
913
+ }, [run?.runId]);
914
+
906
915
  const handleClose = useCallback(() => {
907
916
  setSelectedSpan(null);
908
917
  setDeselectTrigger((n) => n + 1);
@@ -965,7 +974,7 @@ export const WorkflowTraceViewer = ({
965
974
  }
966
975
 
967
976
  return (
968
- <div className="relative w-full h-full flex">
977
+ <div className="relative w-full h-full flex flex-col">
969
978
  {/* Timeline (takes remaining space) */}
970
979
  <div className="flex-1 min-w-0 relative">
971
980
  <TraceViewerContextProvider
@@ -1140,6 +1149,7 @@ export const WorkflowTraceViewer = ({
1140
1149
  <EntityDetailPanel
1141
1150
  run={run}
1142
1151
  onStreamClick={onStreamClick}
1152
+ onRunClick={onRunClick}
1143
1153
  spanDetailData={spanDetailData ?? null}
1144
1154
  spanDetailError={spanDetailError}
1145
1155
  spanDetailLoading={spanDetailLoading}
@@ -225,7 +225,7 @@ export function stepToSpan(stepEvents: Event[], maxEndTime: Date): Span | null {
225
225
 
226
226
  return {
227
227
  spanId: String(step.stepId),
228
- name: parsedName?.shortName ?? '',
228
+ name: parsedName?.shortName ?? String(step.stepName),
229
229
  kind: 1, // INTERNAL span kind
230
230
  resource,
231
231
  library: WORKFLOW_LIBRARY,
package/src/index.ts CHANGED
@@ -40,6 +40,7 @@ export {
40
40
  ENCRYPTED_PLACEHOLDER,
41
41
  extractStreamIds,
42
42
  getWebRevivers,
43
+ hasEncryptedFields,
43
44
  hydrateResourceIO,
44
45
  hydrateResourceIOWithKey,
45
46
  isClassInstanceRef,
@@ -49,6 +50,7 @@ export {
49
50
  STREAM_REF_TYPE,
50
51
  truncateId,
51
52
  } from './lib/hydration';
53
+ export type { DecodedStreamChunkSource } from './lib/stream-display';
52
54
  export type { ToastAdapter } from './lib/toast';
53
55
  export { ToastProvider, useToast } from './lib/toast';
54
56
  export type { StreamStep } from './lib/utils';
@@ -11,8 +11,10 @@ import {
11
11
  hydrateResourceIO as hydrateResourceIOGeneric,
12
12
  isEncryptedData,
13
13
  isExpiredStub,
14
+ isRunRef,
14
15
  observabilityRevivers,
15
16
  type Revivers,
17
+ serializedInstanceToRef,
16
18
  } from '@workflow/core/serialization-format';
17
19
  import { EVENT_DATA_REF_FIELDS } from '@workflow/world';
18
20
 
@@ -24,11 +26,15 @@ export {
24
26
  extractStreamIds,
25
27
  isClassInstanceRef,
26
28
  isEncryptedData,
29
+ isRunRef,
27
30
  isStreamId,
28
31
  isStreamRef,
29
32
  type Revivers,
33
+ RUN_REF_TYPE,
34
+ type RunRef,
30
35
  STREAM_REF_TYPE,
31
36
  type StreamRef,
37
+ serializedInstanceToRef,
32
38
  truncateId,
33
39
  } from '@workflow/core/serialization-format';
34
40
 
@@ -137,6 +143,11 @@ export function getWebRevivers(): Revivers {
137
143
  // react-inspector shows the class name (it reads constructor.name).
138
144
  Class: (value) => `<class:${extractClassName(value.classId)}>`,
139
145
  Instance: (value) => {
146
+ // Run instances are rendered as clickable RunRef badges
147
+ const runRef = serializedInstanceToRef(value);
148
+ if (isRunRef(runRef)) {
149
+ return runRef;
150
+ }
140
151
  const className = extractClassName(value.classId);
141
152
  const data = value.data;
142
153
  const props =
@@ -359,3 +370,29 @@ export async function hydrateResourceIOWithKey<T>(
359
370
 
360
371
  return result as T;
361
372
  }
373
+
374
+ /**
375
+ * Check whether a hydrated resource (event, step, run, etc.) contains any
376
+ * encrypted display markers. Inspects the standard top-level fields
377
+ * (`input`, `output`, `error`, `metadata`) as well as the event-type-specific
378
+ * `eventData` ref fields.
379
+ */
380
+ export function hasEncryptedFields(resource: unknown): boolean {
381
+ if (!resource || typeof resource !== 'object') return false;
382
+ const r = resource as Record<string, unknown>;
383
+
384
+ for (const key of ['input', 'output', 'metadata', 'error']) {
385
+ if (isEncryptedMarker(r[key])) return true;
386
+ }
387
+
388
+ if (r.eventData && typeof r.eventData === 'object') {
389
+ const eventType = typeof r.eventType === 'string' ? r.eventType : '';
390
+ const refKeys = EVENT_DATA_REF_FIELDS[eventType] ?? [];
391
+ const ed = r.eventData as Record<string, unknown>;
392
+ for (const key of refKeys) {
393
+ if (key in ed && isEncryptedMarker(ed[key])) return true;
394
+ }
395
+ }
396
+
397
+ return false;
398
+ }
@@ -0,0 +1,48 @@
1
+ export interface DecodedStreamChunkSource {
2
+ type: string;
3
+ encoding: 'utf-8';
4
+ rawSummary: string;
5
+ }
6
+
7
+ export interface FormattedStreamChunkDisplay {
8
+ text: string;
9
+ decodedFrom?: DecodedStreamChunkSource;
10
+ }
11
+
12
+ export function summarizeArrayBufferView(value: ArrayBufferView): string {
13
+ const ta = value as unknown as {
14
+ length: number;
15
+ constructor: { name: string };
16
+ } & ArrayLike<number>;
17
+ const name = ta.constructor.name;
18
+ const preview = Array.from(
19
+ { length: Math.min(ta.length, 8) },
20
+ (_, i) => ta[i]
21
+ );
22
+ const suffix = ta.length > 8 ? ', …' : '';
23
+ return `${name}(${ta.length}) [${preview.join(', ')}${suffix}]`;
24
+ }
25
+
26
+ /**
27
+ * Decode a typed array as UTF-8 text when valid, otherwise return a compact
28
+ * raw-byte summary. Used by `DataInspector`'s `collapseRefs` pipeline so
29
+ * hydrated `Uint8Array` chunks (e.g. AI SDK text deltas) render as readable
30
+ * text while still exposing the underlying byte layout.
31
+ */
32
+ export function formatArrayBufferViewForDisplay(
33
+ value: ArrayBufferView
34
+ ): FormattedStreamChunkDisplay {
35
+ try {
36
+ const text = new TextDecoder('utf-8', { fatal: true }).decode(value);
37
+ return {
38
+ text,
39
+ decodedFrom: {
40
+ type: value.constructor.name,
41
+ encoding: 'utf-8',
42
+ rawSummary: summarizeArrayBufferView(value),
43
+ },
44
+ };
45
+ } catch {
46
+ return { text: summarizeArrayBufferView(value) };
47
+ }
48
+ }
package/src/lib/utils.ts CHANGED
@@ -7,7 +7,12 @@ export function cn(...inputs: ClassValue[]) {
7
7
  return twMerge(clsx(inputs));
8
8
  }
9
9
 
10
- const durationFormatter = new Intl.NumberFormat(undefined, {
10
+ // const compactFormatter = new Intl.NumberFormat(undefined, {
11
+ // maximumFractionDigits: 2,
12
+ // });
13
+
14
+ const secondsFormatter = new Intl.NumberFormat(undefined, {
15
+ minimumFractionDigits: 2,
11
16
  maximumFractionDigits: 2,
12
17
  });
13
18
 
@@ -20,33 +25,25 @@ const MS_IN_DAY = 24 * MS_IN_HOUR;
20
25
  * Formats a duration in milliseconds to a human-readable string.
21
26
  *
22
27
  * @param ms - Duration in milliseconds
23
- * @param compact - If true, returns a single-unit format (e.g., "45s", "2.5m").
24
- * If false (default), returns multi-part format (e.g., "1h 30m", "2d 5h").
28
+ * @param compact - If true, returns a single-unit format (e.g., "0.38s", "2.5m").
29
+ * If false (default), returns multi-part format (e.g., "1m 13.21s", "2d 5h 3m 12.00s").
25
30
  *
26
- * Compact format:
27
- * - < 1s: shows milliseconds (e.g., "500ms")
28
- * - < 1m: shows seconds (e.g., "45s")
29
- * - < 1h: shows minutes (e.g., "45m")
31
+ * Compact format (timeline markers):
32
+ * - < 1m: shows seconds (e.g., "0.38s", "45s")
33
+ * - < 1h: shows minutes (e.g., "2.5m")
30
34
  * - >= 1h: shows hours (e.g., "2.5h")
31
35
  *
32
36
  * Full format:
33
- * - < 1s: shows milliseconds (e.g., "500ms")
34
- * - < 1m: shows seconds (e.g., "45.5s")
35
- * - >= 1m: shows human-readable format (e.g., "1h 30m", "2d 5h")
37
+ * - < 1m: shows seconds (e.g., "0.38s", "2.71s")
38
+ * - >= 1m: shows decomposed format with fractional seconds (e.g., "1m 13.21s", "2m 13.04s")
36
39
  */
37
40
  export function formatDuration(ms: number, compact = false): string {
38
41
  if (ms === 0) {
39
- return '0';
40
- }
41
-
42
- // For durations less than 1 second, show milliseconds
43
- if (ms < MS_IN_SECOND) {
44
- return `${durationFormatter.format(ms)}ms`;
42
+ return '0s';
45
43
  }
46
44
 
47
- // For durations less than 1 minute, show seconds
48
45
  if (ms < MS_IN_MINUTE) {
49
- return `${durationFormatter.format(ms / MS_IN_SECOND)}s`;
46
+ return `${secondsFormatter.format(ms / MS_IN_SECOND)}s`;
50
47
  }
51
48
 
52
49
  // Compact format: multi-unit without decimals (e.g. "8m 20s", "2h 30m")
@@ -61,11 +58,11 @@ export function formatDuration(ms: number, compact = false): string {
61
58
  return m > 0 ? `${h}h ${m}m` : `${h}h`;
62
59
  }
63
60
 
64
- // Full format: human-readable multi-part
61
+ // Full format: decompose into larger units + fractional seconds
65
62
  const days = Math.floor(ms / MS_IN_DAY);
66
63
  const hours = Math.floor((ms % MS_IN_DAY) / MS_IN_HOUR);
67
64
  const minutes = Math.floor((ms % MS_IN_HOUR) / MS_IN_MINUTE);
68
- const seconds = Math.floor((ms % MS_IN_MINUTE) / MS_IN_SECOND);
65
+ const seconds = (ms % MS_IN_MINUTE) / MS_IN_SECOND;
69
66
 
70
67
  const parts: string[] = [];
71
68
 
@@ -78,9 +75,7 @@ export function formatDuration(ms: number, compact = false): string {
78
75
  if (minutes > 0) {
79
76
  parts.push(`${minutes}m`);
80
77
  }
81
- if (hours <= 1 && (seconds > 0 || parts.length === 0)) {
82
- parts.push(`${seconds}s`);
83
- }
78
+ parts.push(`${secondsFormatter.format(seconds)}s`);
84
79
 
85
80
  return parts.join(' ');
86
81
  }