@workflow/web-shared 5.0.0-beta.2 → 5.0.0-beta.4

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 (161) hide show
  1. package/dist/components/index.d.ts +2 -0
  2. package/dist/components/index.d.ts.map +1 -1
  3. package/dist/components/index.js +2 -0
  4. package/dist/components/index.js.map +1 -1
  5. package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
  6. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
  7. package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
  8. package/dist/components/new-trace-viewer/components/copy-button.js.map +1 -0
  9. package/dist/components/new-trace-viewer/components/event-list.d.ts +8 -0
  10. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
  11. package/dist/components/new-trace-viewer/components/event-list.js +38 -0
  12. package/dist/components/new-trace-viewer/components/event-list.js.map +1 -0
  13. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts +18 -0
  14. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
  15. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +54 -0
  16. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js.map +1 -0
  17. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts +20 -0
  18. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
  19. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
  20. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js.map +1 -0
  21. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +25 -0
  22. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
  23. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +101 -0
  24. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js.map +1 -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/middle-truncate/use-middle-truncate.js.map +1 -0
  29. package/dist/components/new-trace-viewer/components/split-pane.d.ts +14 -0
  30. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
  31. package/dist/components/new-trace-viewer/components/split-pane.js +99 -0
  32. package/dist/components/new-trace-viewer/components/split-pane.js.map +1 -0
  33. package/dist/components/new-trace-viewer/components/timeline.d.ts +31 -0
  34. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
  35. package/dist/components/new-trace-viewer/components/timeline.js +138 -0
  36. package/dist/components/new-trace-viewer/components/timeline.js.map +1 -0
  37. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  38. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  39. package/dist/components/new-trace-viewer/context.js +47 -0
  40. package/dist/components/new-trace-viewer/context.js.map +1 -0
  41. package/dist/components/new-trace-viewer/detail-panel.d.ts +10 -0
  42. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -0
  43. package/dist/components/new-trace-viewer/detail-panel.js +12 -0
  44. package/dist/components/new-trace-viewer/detail-panel.js.map +1 -0
  45. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  46. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  47. package/dist/components/new-trace-viewer/icons.js +15 -0
  48. package/dist/components/new-trace-viewer/icons.js.map +1 -0
  49. package/dist/components/new-trace-viewer/trace-viewer.d.ts +8 -0
  50. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  51. package/dist/components/new-trace-viewer/trace-viewer.js +349 -0
  52. package/dist/components/new-trace-viewer/trace-viewer.js.map +1 -0
  53. package/dist/components/new-trace-viewer/types.d.ts +1 -0
  54. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  55. package/dist/components/new-trace-viewer/types.js +2 -0
  56. package/dist/components/new-trace-viewer/types.js.map +1 -0
  57. package/dist/components/new-trace-viewer/util/constants.d.ts +1 -0
  58. package/dist/components/new-trace-viewer/util/constants.d.ts.map +1 -0
  59. package/dist/components/new-trace-viewer/util/constants.js +2 -0
  60. package/dist/components/new-trace-viewer/util/constants.js.map +1 -0
  61. package/dist/components/new-trace-viewer/utils.d.ts +42 -0
  62. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  63. package/dist/components/new-trace-viewer/utils.js +305 -0
  64. package/dist/components/new-trace-viewer/utils.js.map +1 -0
  65. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  66. package/dist/components/sidebar/attribute-panel.js +42 -23
  67. package/dist/components/sidebar/attribute-panel.js.map +1 -1
  68. package/dist/components/sidebar/copyable-data-block.js +1 -1
  69. package/dist/components/sidebar/copyable-data-block.js.map +1 -1
  70. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  71. package/dist/components/sidebar/detail-card.js +1 -1
  72. package/dist/components/sidebar/detail-card.js.map +1 -1
  73. package/dist/components/sidebar/entity-detail-panel.d.ts +1 -3
  74. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  75. package/dist/components/sidebar/entity-detail-panel.js +3 -25
  76. package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
  77. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  78. package/dist/components/sidebar/events-list.js +2 -2
  79. package/dist/components/sidebar/events-list.js.map +1 -1
  80. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  81. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  82. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  83. package/dist/components/sidebar/sidebar-data-context.js.map +1 -0
  84. package/dist/components/stream-viewer.d.ts +1 -1
  85. package/dist/components/stream-viewer.d.ts.map +1 -1
  86. package/dist/components/stream-viewer.js +2 -26
  87. package/dist/components/stream-viewer.js.map +1 -1
  88. package/dist/components/trace-viewer/util/timing.d.ts +1 -1
  89. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
  90. package/dist/components/trace-viewer/util/timing.js +5 -8
  91. package/dist/components/trace-viewer/util/timing.js.map +1 -1
  92. package/dist/components/trace-viewer-new.d.ts +10 -0
  93. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  94. package/dist/components/trace-viewer-new.js +23 -0
  95. package/dist/components/trace-viewer-new.js.map +1 -0
  96. package/dist/components/ui/data-inspector.d.ts +21 -0
  97. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  98. package/dist/components/ui/data-inspector.js +99 -4
  99. package/dist/components/ui/data-inspector.js.map +1 -1
  100. package/dist/components/ui/inspector-theme.d.ts +9 -10
  101. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  102. package/dist/components/ui/inspector-theme.js +38 -71
  103. package/dist/components/ui/inspector-theme.js.map +1 -1
  104. package/dist/components/workflow-trace-view.d.ts +1 -3
  105. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  106. package/dist/components/workflow-trace-view.js +4 -4
  107. package/dist/components/workflow-trace-view.js.map +1 -1
  108. package/dist/components/workflow-traces/trace-span-construction.d.ts +2 -2
  109. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  110. package/dist/components/workflow-traces/trace-span-construction.js +16 -3
  111. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
  112. package/dist/index.d.ts +1 -0
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js.map +1 -1
  115. package/dist/lib/stream-display.d.ts +18 -0
  116. package/dist/lib/stream-display.d.ts.map +1 -0
  117. package/dist/lib/stream-display.js +30 -0
  118. package/dist/lib/stream-display.js.map +1 -0
  119. package/dist/lib/trace-builder.d.ts.map +1 -1
  120. package/dist/lib/trace-builder.js +2 -1
  121. package/dist/lib/trace-builder.js.map +1 -1
  122. package/dist/lib/utils.d.ts +9 -9
  123. package/dist/lib/utils.d.ts.map +1 -1
  124. package/dist/lib/utils.js +26 -31
  125. package/dist/lib/utils.js.map +1 -1
  126. package/dist/styles.css +470 -1
  127. package/package.json +3 -2
  128. package/src/components/index.ts +5 -0
  129. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  130. package/src/components/new-trace-viewer/components/event-list.tsx +116 -0
  131. package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
  132. package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
  133. package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
  134. package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
  135. package/src/components/new-trace-viewer/components/split-pane.tsx +185 -0
  136. package/src/components/new-trace-viewer/components/timeline.tsx +382 -0
  137. package/src/components/new-trace-viewer/context.tsx +92 -0
  138. package/src/components/new-trace-viewer/detail-panel.tsx +58 -0
  139. package/src/components/new-trace-viewer/icons.tsx +79 -0
  140. package/src/components/new-trace-viewer/trace-viewer.tsx +645 -0
  141. package/src/components/new-trace-viewer/types.ts +0 -0
  142. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  143. package/src/components/new-trace-viewer/utils.ts +450 -0
  144. package/src/components/sidebar/attribute-panel.tsx +43 -32
  145. package/src/components/sidebar/copyable-data-block.tsx +1 -1
  146. package/src/components/sidebar/detail-card.tsx +1 -19
  147. package/src/components/sidebar/entity-detail-panel.tsx +0 -71
  148. package/src/components/sidebar/events-list.tsx +9 -6
  149. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  150. package/src/components/stream-viewer.tsx +18 -40
  151. package/src/components/trace-viewer/util/timing.ts +5 -9
  152. package/src/components/trace-viewer-new.tsx +49 -0
  153. package/src/components/ui/data-inspector.tsx +235 -4
  154. package/src/components/ui/inspector-theme.ts +39 -81
  155. package/src/components/workflow-trace-view.tsx +2 -6
  156. package/src/components/workflow-traces/trace-span-construction.ts +22 -4
  157. package/src/index.ts +1 -0
  158. package/src/lib/stream-display.ts +48 -0
  159. package/src/lib/trace-builder.ts +2 -1
  160. package/src/lib/utils.ts +27 -32
  161. 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
@@ -776,7 +776,6 @@ export const WorkflowTraceViewer = ({
776
776
  encryptionKey,
777
777
  onDecrypt,
778
778
  isDecrypting = false,
779
- hasEncryptedData = false,
780
779
  }: {
781
780
  run: WorkflowRun;
782
781
  events: Event[];
@@ -819,8 +818,6 @@ export const WorkflowTraceViewer = ({
819
818
  onDecrypt?: () => void;
820
819
  /** Whether the encryption key is currently being fetched */
821
820
  isDecrypting?: boolean;
822
- /** Run-level hint: the run contains encrypted data (from probe). */
823
- hasEncryptedData?: boolean;
824
821
  }) => {
825
822
  const toast = useToast();
826
823
  const [selectedSpan, setSelectedSpan] = useState<SelectedSpanInfo | null>(
@@ -977,7 +974,7 @@ export const WorkflowTraceViewer = ({
977
974
  }
978
975
 
979
976
  return (
980
- <div className="relative w-full h-full flex">
977
+ <div className="relative w-full h-full flex flex-col">
981
978
  {/* Timeline (takes remaining space) */}
982
979
  <div className="flex-1 min-w-0 relative">
983
980
  <TraceViewerContextProvider
@@ -1164,7 +1161,6 @@ export const WorkflowTraceViewer = ({
1164
1161
  onDecrypt={onDecrypt}
1165
1162
  isDecrypting={isDecrypting}
1166
1163
  selectedSpan={selectedSpan}
1167
- hasEncryptedData={hasEncryptedData}
1168
1164
  />
1169
1165
  </ErrorBoundary>
1170
1166
  </div>
@@ -59,7 +59,7 @@ export const waitEventsToWaitEntity = (
59
59
  waitId: string;
60
60
  runId: string;
61
61
  createdAt: Date;
62
- resumeAt: Date;
62
+ resumeAt?: Date;
63
63
  completedAt?: Date;
64
64
  } | null => {
65
65
  const startEvent = events.find((event) => event.eventType === 'wait_created');
@@ -73,7 +73,9 @@ export const waitEventsToWaitEntity = (
73
73
  waitId: startEvent.correlationId,
74
74
  runId: startEvent.runId,
75
75
  createdAt: startEvent.createdAt,
76
- resumeAt: startEvent.eventData?.resumeAt,
76
+ resumeAt: startEvent.eventData?.resumeAt
77
+ ? new Date(startEvent.eventData.resumeAt)
78
+ : undefined,
77
79
  completedAt: completedEvent?.createdAt,
78
80
  };
79
81
  };
@@ -81,13 +83,29 @@ export const waitEventsToWaitEntity = (
81
83
  /**
82
84
  * Converts a workflow Wait to an OpenTelemetry Span
83
85
  */
84
- export function waitToSpan(events: Event[], maxEndTime: Date): Span | null {
86
+ export function waitToSpan(
87
+ events: Event[],
88
+ maxEndTime: Date,
89
+ fallbackEndTime = maxEndTime
90
+ ): Span | null {
85
91
  const wait = waitEventsToWaitEntity(events);
86
92
  if (!wait) {
87
93
  return null;
88
94
  }
89
95
  const startTime = wait.createdAt;
90
- const endTime = wait.completedAt ?? maxEndTime;
96
+ const startMs = startTime.getTime();
97
+ let endTime = wait.completedAt;
98
+ if (!endTime) {
99
+ const fallbackCap =
100
+ wait.resumeAt && wait.resumeAt.getTime() < fallbackEndTime.getTime()
101
+ ? wait.resumeAt
102
+ : fallbackEndTime;
103
+ endTime =
104
+ maxEndTime.getTime() > startMs &&
105
+ maxEndTime.getTime() < fallbackCap.getTime()
106
+ ? maxEndTime
107
+ : fallbackCap;
108
+ }
91
109
  const start = dateToOtelTime(startTime);
92
110
  const end = dateToOtelTime(endTime);
93
111
  const duration = calculateDuration(startTime, endTime);
package/src/index.ts CHANGED
@@ -50,6 +50,7 @@ export {
50
50
  STREAM_REF_TYPE,
51
51
  truncateId,
52
52
  } from './lib/hydration';
53
+ export type { DecodedStreamChunkSource } from './lib/stream-display';
53
54
  export type { ToastAdapter } from './lib/toast';
54
55
  export { ToastProvider, useToast } from './lib/toast';
55
56
  export type { StreamStep } from './lib/utils';
@@ -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
+ }
@@ -117,6 +117,7 @@ function buildSpans(
117
117
  // unknown territory. Even when the run is completed, we may not have loaded
118
118
  // all events yet, so always use the latest event we've actually seen.
119
119
  const childMaxEnd = latestKnownTime;
120
+ const runMaxEnd = run.completedAt ?? now;
120
121
 
121
122
  const stepSpans = Array.from(groupedEvents.eventsByStepId.values())
122
123
  .map((events) => stepToSpan(events, childMaxEnd))
@@ -127,7 +128,7 @@ function buildSpans(
127
128
  .filter((span): span is Span => span !== null);
128
129
 
129
130
  const waitSpans = Array.from(groupedEvents.timerEvents.values())
130
- .map((events) => waitToSpan(events, childMaxEnd))
131
+ .map((events) => waitToSpan(events, childMaxEnd, runMaxEnd))
131
132
  .filter((span): span is Span => span !== null);
132
133
 
133
134
  return {
package/src/lib/utils.ts CHANGED
@@ -7,10 +7,6 @@ export function cn(...inputs: ClassValue[]) {
7
7
  return twMerge(clsx(inputs));
8
8
  }
9
9
 
10
- const durationFormatter = new Intl.NumberFormat(undefined, {
11
- maximumFractionDigits: 2,
12
- });
13
-
14
10
  const MS_IN_SECOND = 1000;
15
11
  const MS_IN_MINUTE = 60 * MS_IN_SECOND;
16
12
  const MS_IN_HOUR = 60 * MS_IN_MINUTE;
@@ -20,52 +16,53 @@ const MS_IN_DAY = 24 * MS_IN_HOUR;
20
16
  * Formats a duration in milliseconds to a human-readable string.
21
17
  *
22
18
  * @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").
19
+ * @param compact - If true, returns a compact format (e.g., "380ms", "2m 30s").
20
+ * If false (default), returns multi-part format (e.g., "1m 13s", "2d 5h 3m 12s").
25
21
  *
26
- * Compact format:
27
- * - < 1s: shows milliseconds (e.g., "500ms")
22
+ * Compact format (timeline markers):
23
+ * - < 1s: shows milliseconds (e.g., "380ms")
28
24
  * - < 1m: shows seconds (e.g., "45s")
29
- * - < 1h: shows minutes (e.g., "45m")
30
- * - >= 1h: shows hours (e.g., "2.5h")
25
+ * - < 1h: shows minutes and seconds (e.g., "2m 30s")
26
+ * - >= 1h: shows hours and minutes (e.g., "2h 30m")
31
27
  *
32
28
  * 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")
29
+ * - < 1s: shows milliseconds (e.g., "380ms")
30
+ * - < 1m: shows seconds (e.g., "45s")
31
+ * - >= 1m: shows decomposed format with whole seconds (e.g., "1m 13s", "2m 13s")
36
32
  */
37
33
  export function formatDuration(ms: number, compact = false): string {
38
34
  if (ms === 0) {
39
- return '0';
35
+ return '0s';
40
36
  }
41
37
 
42
- // For durations less than 1 second, show milliseconds
43
38
  if (ms < MS_IN_SECOND) {
44
- return `${durationFormatter.format(ms)}ms`;
39
+ const roundedMs = Math.round(ms);
40
+ return roundedMs < MS_IN_SECOND ? `${roundedMs}ms` : '1s';
45
41
  }
46
42
 
47
- // For durations less than 1 minute, show seconds
48
- if (ms < MS_IN_MINUTE) {
49
- return `${durationFormatter.format(ms / MS_IN_SECOND)}s`;
43
+ const roundedMs = Math.round(ms / MS_IN_SECOND) * MS_IN_SECOND;
44
+
45
+ if (roundedMs < MS_IN_MINUTE) {
46
+ return `${Math.floor(roundedMs / MS_IN_SECOND)}s`;
50
47
  }
51
48
 
52
49
  // Compact format: multi-unit without decimals (e.g. "8m 20s", "2h 30m")
53
50
  if (compact) {
54
- if (ms < MS_IN_HOUR) {
55
- const m = Math.floor(ms / MS_IN_MINUTE);
56
- const s = Math.floor((ms % MS_IN_MINUTE) / MS_IN_SECOND);
51
+ if (roundedMs < MS_IN_HOUR) {
52
+ const m = Math.floor(roundedMs / MS_IN_MINUTE);
53
+ const s = Math.floor((roundedMs % MS_IN_MINUTE) / MS_IN_SECOND);
57
54
  return s > 0 ? `${m}m ${s}s` : `${m}m`;
58
55
  }
59
- const h = Math.floor(ms / MS_IN_HOUR);
60
- const m = Math.floor((ms % MS_IN_HOUR) / MS_IN_MINUTE);
56
+ const h = Math.floor(roundedMs / MS_IN_HOUR);
57
+ const m = Math.floor((roundedMs % MS_IN_HOUR) / MS_IN_MINUTE);
61
58
  return m > 0 ? `${h}h ${m}m` : `${h}h`;
62
59
  }
63
60
 
64
- // Full format: human-readable multi-part
65
- const days = Math.floor(ms / MS_IN_DAY);
66
- const hours = Math.floor((ms % MS_IN_DAY) / MS_IN_HOUR);
67
- const minutes = Math.floor((ms % MS_IN_HOUR) / MS_IN_MINUTE);
68
- const seconds = Math.floor((ms % MS_IN_MINUTE) / MS_IN_SECOND);
61
+ // Full format: decompose into larger units + whole seconds.
62
+ const days = Math.floor(roundedMs / MS_IN_DAY);
63
+ const hours = Math.floor((roundedMs % MS_IN_DAY) / MS_IN_HOUR);
64
+ const minutes = Math.floor((roundedMs % MS_IN_HOUR) / MS_IN_MINUTE);
65
+ const seconds = Math.floor((roundedMs % 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(`${seconds}s`);
84
79
 
85
80
  return parts.join(' ');
86
81
  }