@workflow/web-shared 4.1.0-beta.52 → 4.1.0-beta.53
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.
- package/dist/components/error-boundary.d.ts +15 -20
- package/dist/components/error-boundary.d.ts.map +1 -1
- package/dist/components/error-boundary.js +17 -31
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/event-list-view.d.ts +7 -6
- package/dist/components/event-list-view.d.ts.map +1 -1
- package/dist/components/event-list-view.js +492 -109
- package/dist/components/event-list-view.js.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/run-trace-view.d.ts +2 -1
- package/dist/components/run-trace-view.d.ts.map +1 -1
- package/dist/components/run-trace-view.js +2 -2
- package/dist/components/run-trace-view.js.map +1 -1
- package/dist/components/sidebar/attribute-panel.d.ts +2 -1
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
- package/dist/components/sidebar/attribute-panel.js +53 -142
- package/dist/components/sidebar/attribute-panel.js.map +1 -1
- package/dist/components/sidebar/conversation-view.d.ts.map +1 -1
- package/dist/components/sidebar/conversation-view.js +3 -17
- package/dist/components/sidebar/conversation-view.js.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.js +63 -10
- package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
- package/dist/components/sidebar/events-list.d.ts.map +1 -1
- package/dist/components/sidebar/events-list.js +4 -8
- package/dist/components/sidebar/events-list.js.map +1 -1
- package/dist/components/sidebar/resolve-hook-modal.d.ts +3 -0
- package/dist/components/sidebar/resolve-hook-modal.d.ts.map +1 -1
- package/dist/components/sidebar/resolve-hook-modal.js +152 -3
- package/dist/components/sidebar/resolve-hook-modal.js.map +1 -1
- package/dist/components/stream-viewer.d.ts +7 -5
- package/dist/components/stream-viewer.d.ts.map +1 -1
- package/dist/components/stream-viewer.js +54 -22
- package/dist/components/stream-viewer.js.map +1 -1
- package/dist/components/trace-viewer/components/markers.d.ts +2 -1
- package/dist/components/trace-viewer/components/markers.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/markers.js +59 -20
- package/dist/components/trace-viewer/components/markers.js.map +1 -1
- package/dist/components/trace-viewer/components/node.d.ts +5 -1
- package/dist/components/trace-viewer/components/node.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/node.js +250 -68
- package/dist/components/trace-viewer/components/node.js.map +1 -1
- package/dist/components/trace-viewer/components/span-content.d.ts +19 -0
- package/dist/components/trace-viewer/components/span-content.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-content.js +137 -0
- package/dist/components/trace-viewer/components/span-content.js.map +1 -0
- package/dist/components/trace-viewer/components/span-detail-panel.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/span-detail-panel.js +3 -2
- package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -1
- package/dist/components/trace-viewer/components/span-segments.d.ts +50 -0
- package/dist/components/trace-viewer/components/span-segments.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-segments.js +392 -0
- package/dist/components/trace-viewer/components/span-segments.js.map +1 -0
- package/dist/components/trace-viewer/components/span-strategies.d.ts +46 -0
- package/dist/components/trace-viewer/components/span-strategies.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-strategies.js +108 -0
- package/dist/components/trace-viewer/components/span-strategies.js.map +1 -0
- package/dist/components/trace-viewer/context.d.ts +7 -6
- package/dist/components/trace-viewer/context.d.ts.map +1 -1
- package/dist/components/trace-viewer/context.js +47 -18
- package/dist/components/trace-viewer/context.js.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.d.ts +5 -1
- package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.js +87 -11
- package/dist/components/trace-viewer/trace-viewer.js.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.module.css +179 -6
- package/dist/components/trace-viewer/util/timing.d.ts +5 -0
- package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
- package/dist/components/trace-viewer/util/timing.js +12 -0
- package/dist/components/trace-viewer/util/timing.js.map +1 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.d.ts +1 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.d.ts.map +1 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.js +29 -17
- package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -1
- package/dist/components/trace-viewer/worker.js +3 -1
- package/dist/components/trace-viewer/worker.js.map +1 -1
- package/dist/components/ui/alert.js +3 -3
- package/dist/components/ui/alert.js.map +1 -1
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/card.js +2 -2
- package/dist/components/ui/card.js.map +1 -1
- package/dist/components/ui/data-inspector.d.ts +17 -0
- package/dist/components/ui/data-inspector.d.ts.map +1 -0
- package/dist/components/ui/data-inspector.js +184 -0
- package/dist/components/ui/data-inspector.js.map +1 -0
- package/dist/components/ui/error-card.d.ts.map +1 -1
- package/dist/components/ui/error-card.js +4 -1
- package/dist/components/ui/error-card.js.map +1 -1
- package/dist/components/ui/inspector-theme.d.ts +39 -24
- package/dist/components/ui/inspector-theme.d.ts.map +1 -1
- package/dist/components/ui/inspector-theme.js +90 -38
- package/dist/components/ui/inspector-theme.js.map +1 -1
- package/dist/components/ui/skeleton.d.ts +1 -1
- package/dist/components/ui/skeleton.d.ts.map +1 -1
- package/dist/components/ui/skeleton.js +2 -2
- package/dist/components/ui/skeleton.js.map +1 -1
- package/dist/components/workflow-trace-view.d.ts +3 -1
- package/dist/components/workflow-trace-view.d.ts.map +1 -1
- package/dist/components/workflow-trace-view.js +435 -21
- package/dist/components/workflow-trace-view.js.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.d.ts +1 -1
- package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.js +2 -2
- package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
- package/dist/lib/hydration.d.ts.map +1 -1
- package/dist/lib/hydration.js +17 -3
- package/dist/lib/hydration.js.map +1 -1
- package/dist/styles.css +186 -0
- package/package.json +8 -7
- package/src/components/error-boundary.tsx +29 -40
- package/src/components/event-list-view.tsx +1000 -287
- package/src/components/index.ts +1 -0
- package/src/components/run-trace-view.tsx +3 -0
- package/src/components/sidebar/attribute-panel.tsx +58 -258
- package/src/components/sidebar/conversation-view.tsx +30 -27
- package/src/components/sidebar/entity-detail-panel.tsx +86 -20
- package/src/components/sidebar/events-list.tsx +4 -11
- package/src/components/sidebar/resolve-hook-modal.tsx +206 -47
- package/src/components/stream-viewer.tsx +138 -61
- package/src/components/trace-viewer/components/markers.tsx +69 -21
- package/src/components/trace-viewer/components/node.tsx +346 -100
- package/src/components/trace-viewer/components/span-content.tsx +247 -0
- package/src/components/trace-viewer/components/span-detail-panel.tsx +7 -2
- package/src/components/trace-viewer/components/span-segments.ts +516 -0
- package/src/components/trace-viewer/components/span-strategies.ts +205 -0
- package/src/components/trace-viewer/context.tsx +92 -40
- package/src/components/trace-viewer/trace-viewer.module.css +179 -6
- package/src/components/trace-viewer/trace-viewer.tsx +115 -11
- package/src/components/trace-viewer/util/timing.ts +13 -0
- package/src/components/trace-viewer/util/use-streaming-spans.ts +28 -17
- package/src/components/trace-viewer/worker.ts +4 -1
- package/src/components/ui/alert.tsx +3 -3
- package/src/components/ui/card.tsx +3 -5
- package/src/components/ui/data-inspector.tsx +318 -0
- package/src/components/ui/error-card.tsx +17 -6
- package/src/components/ui/inspector-theme.ts +127 -39
- package/src/components/ui/skeleton.tsx +3 -1
- package/src/components/workflow-trace-view.tsx +625 -26
- package/src/components/workflow-traces/trace-span-construction.ts +3 -2
- package/src/lib/hydration.ts +17 -8
- package/src/styles.css +186 -0
package/src/components/index.ts
CHANGED
|
@@ -21,4 +21,5 @@ export type {
|
|
|
21
21
|
} from './sidebar/entity-detail-panel';
|
|
22
22
|
export { type StreamChunk, StreamViewer } from './stream-viewer';
|
|
23
23
|
export type { Span, SpanEvent } from './trace-viewer/types';
|
|
24
|
+
export { DataInspector, type DataInspectorProps } from './ui/data-inspector';
|
|
24
25
|
export { WorkflowTraceViewer } from './workflow-trace-view';
|
|
@@ -24,6 +24,7 @@ interface RunTraceViewProps {
|
|
|
24
24
|
payload: unknown,
|
|
25
25
|
hook?: Hook
|
|
26
26
|
) => Promise<void>;
|
|
27
|
+
onCancelRun?: (runId: string) => Promise<void>;
|
|
27
28
|
onStreamClick?: (streamId: string) => void;
|
|
28
29
|
onSpanSelect?: (info: SpanSelectionInfo) => void;
|
|
29
30
|
}
|
|
@@ -40,6 +41,7 @@ export function RunTraceView({
|
|
|
40
41
|
spanDetailError,
|
|
41
42
|
onWakeUpSleep,
|
|
42
43
|
onResolveHook,
|
|
44
|
+
onCancelRun,
|
|
43
45
|
onStreamClick,
|
|
44
46
|
onSpanSelect,
|
|
45
47
|
}: RunTraceViewProps) {
|
|
@@ -67,6 +69,7 @@ export function RunTraceView({
|
|
|
67
69
|
spanDetailError={spanDetailError}
|
|
68
70
|
onWakeUpSleep={onWakeUpSleep}
|
|
69
71
|
onResolveHook={onResolveHook}
|
|
72
|
+
onCancelRun={onCancelRun}
|
|
70
73
|
onStreamClick={onStreamClick}
|
|
71
74
|
onSpanSelect={onSpanSelect}
|
|
72
75
|
/>
|
|
@@ -4,10 +4,9 @@ import { parseStepName, parseWorkflowName } from '@workflow/utils/parse-name';
|
|
|
4
4
|
import type { Event, Hook, Step, WorkflowRun } from '@workflow/world';
|
|
5
5
|
import type { ModelMessage } from 'ai';
|
|
6
6
|
import type { ReactNode } from 'react';
|
|
7
|
-
import {
|
|
8
|
-
import { ObjectInspector } from 'react-inspector';
|
|
9
|
-
import { useDarkMode } from '../../hooks/use-dark-mode';
|
|
7
|
+
import { useMemo, useState } from 'react';
|
|
10
8
|
import { extractConversation, isDoStreamStep } from '../../lib/utils';
|
|
9
|
+
import { DataInspector, StreamClickContext } from '../ui/data-inspector';
|
|
11
10
|
import { ErrorCard } from '../ui/error-card';
|
|
12
11
|
import { ConversationView } from './conversation-view';
|
|
13
12
|
import { DetailCard } from './detail-card';
|
|
@@ -112,265 +111,16 @@ function ConversationWithTabs({
|
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
/**
|
|
115
|
-
*
|
|
114
|
+
* Render a value with the shared DataInspector (ObjectInspector with
|
|
115
|
+
* custom theming, nodeRenderer for StreamRef/ClassInstanceRef, etc.)
|
|
116
116
|
*/
|
|
117
|
-
|
|
118
|
-
((streamId: string) => void) | undefined
|
|
119
|
-
>(undefined);
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Marker for stream reference objects that can be rendered as links
|
|
123
|
-
* This is duplicated from @workflow/core/observability to avoid pulling in
|
|
124
|
-
* Node.js dependencies into the client bundle.
|
|
125
|
-
*/
|
|
126
|
-
const STREAM_REF_TYPE = '__workflow_stream_ref__';
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* A stream reference object that contains the stream ID and can be
|
|
130
|
-
* detected in the UI to render as a clickable link
|
|
131
|
-
*/
|
|
132
|
-
interface StreamRef {
|
|
133
|
-
__type: typeof STREAM_REF_TYPE;
|
|
134
|
-
streamId: string;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Check if a value is a StreamRef object
|
|
139
|
-
*
|
|
140
|
-
*/
|
|
141
|
-
const isStreamRef = (value: unknown): value is StreamRef => {
|
|
142
|
-
// TODO: This is duplicated from @workflow/core/observability, but can't be pulled
|
|
143
|
-
// in client-side code because it's a Node.js dependency.
|
|
144
|
-
return (
|
|
145
|
-
value !== null &&
|
|
146
|
-
typeof value === 'object' &&
|
|
147
|
-
'__type' in value &&
|
|
148
|
-
value.__type === STREAM_REF_TYPE &&
|
|
149
|
-
'streamId' in value &&
|
|
150
|
-
typeof value.streamId === 'string'
|
|
151
|
-
);
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Marker for custom class instance references.
|
|
156
|
-
* This is duplicated from @workflow/core/observability to avoid pulling in
|
|
157
|
-
* Node.js dependencies into the client bundle.
|
|
158
|
-
*/
|
|
159
|
-
const CLASS_INSTANCE_REF_TYPE = '__workflow_class_instance_ref__';
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* A class instance reference object that contains the class name and serialized data.
|
|
163
|
-
* Used in o11y when a custom class instance is encountered but the class is not
|
|
164
|
-
* registered for deserialization.
|
|
165
|
-
*/
|
|
166
|
-
interface ClassInstanceRef {
|
|
167
|
-
__type: typeof CLASS_INSTANCE_REF_TYPE;
|
|
168
|
-
className: string;
|
|
169
|
-
classId: string;
|
|
170
|
-
data: unknown;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Check if a value is a ClassInstanceRef object
|
|
175
|
-
*/
|
|
176
|
-
const isClassInstanceRef = (value: unknown): value is ClassInstanceRef => {
|
|
177
|
-
return (
|
|
178
|
-
value !== null &&
|
|
179
|
-
typeof value === 'object' &&
|
|
180
|
-
'__type' in value &&
|
|
181
|
-
value.__type === CLASS_INSTANCE_REF_TYPE &&
|
|
182
|
-
'className' in value &&
|
|
183
|
-
typeof value.className === 'string'
|
|
184
|
-
);
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
import ColorHash from 'color-hash';
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Color hash instance configured for nice saturation and lightness.
|
|
191
|
-
* Returns HSL values which we can transform for different use cases.
|
|
192
|
-
*/
|
|
193
|
-
const colorHash = new ColorHash({
|
|
194
|
-
saturation: [0.5, 0.6, 0.7],
|
|
195
|
-
lightness: [0.4, 0.5, 0.6],
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Convert HSL to CSS hsl() string
|
|
200
|
-
*/
|
|
201
|
-
const hslToString = (h: number, s: number, l: number): string => {
|
|
202
|
-
return `hsl(${h}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`;
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Get consistent colors for a class ID using color-hash and HSL transformations.
|
|
207
|
-
* Adjusts colors based on light/dark mode for optimal appearance.
|
|
208
|
-
*/
|
|
209
|
-
const getClassColors = (
|
|
210
|
-
classId: string,
|
|
211
|
-
isDark: boolean
|
|
212
|
-
): { header: string; body: string; text: string } => {
|
|
213
|
-
const [h, s, l] = colorHash.hsl(classId);
|
|
214
|
-
|
|
215
|
-
if (isDark) {
|
|
216
|
-
// Dark mode: vibrant header, dark body, light text
|
|
217
|
-
return {
|
|
218
|
-
header: hslToString(h, s, Math.min(l + 0.1, 0.6)), // Slightly brighter header
|
|
219
|
-
body: hslToString(h, s * 0.8, 0.15), // Very dark, slightly desaturated body
|
|
220
|
-
text: hslToString(h, s * 0.6, 0.8), // Light, slightly desaturated text
|
|
221
|
-
};
|
|
222
|
-
} else {
|
|
223
|
-
// Light mode: vibrant header, light body, dark text
|
|
224
|
-
return {
|
|
225
|
-
header: hslToString(h, s, l), // Use base color for header
|
|
226
|
-
body: hslToString(h, s * 0.4, 0.95), // Very light, desaturated body
|
|
227
|
-
text: hslToString(h, s * 0.8, 0.25), // Dark, saturated text
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Renders a ClassInstanceRef as a styled card showing the class name and serialized data.
|
|
234
|
-
* The header color is determined by hashing the classId for visual distinction.
|
|
235
|
-
* Reacts to theme changes for proper dark/light mode support.
|
|
236
|
-
*/
|
|
237
|
-
const ClassInstanceRefDisplay = ({
|
|
238
|
-
classInstanceRef,
|
|
239
|
-
}: {
|
|
240
|
-
classInstanceRef: ClassInstanceRef;
|
|
241
|
-
}) => {
|
|
242
|
-
const isDark = useDarkMode();
|
|
243
|
-
const colors = getClassColors(classInstanceRef.classId, isDark);
|
|
244
|
-
|
|
245
|
-
return (
|
|
246
|
-
<div
|
|
247
|
-
className="inline-flex flex-col rounded text-[11px] font-mono my-1"
|
|
248
|
-
style={{
|
|
249
|
-
backgroundColor: colors.body,
|
|
250
|
-
border: `1px solid ${colors.header}`,
|
|
251
|
-
}}
|
|
252
|
-
>
|
|
253
|
-
<div
|
|
254
|
-
className="flex items-center gap-1.5 px-2 py-1 rounded-t"
|
|
255
|
-
style={{
|
|
256
|
-
backgroundColor: colors.header,
|
|
257
|
-
color: '#FFFFFF',
|
|
258
|
-
}}
|
|
259
|
-
title={`Custom class: ${classInstanceRef.classId}`}
|
|
260
|
-
>
|
|
261
|
-
<svg
|
|
262
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
263
|
-
width="12"
|
|
264
|
-
height="12"
|
|
265
|
-
viewBox="0 0 24 24"
|
|
266
|
-
fill="none"
|
|
267
|
-
stroke="currentColor"
|
|
268
|
-
strokeWidth="2"
|
|
269
|
-
strokeLinecap="round"
|
|
270
|
-
strokeLinejoin="round"
|
|
271
|
-
>
|
|
272
|
-
<title>Class instance</title>
|
|
273
|
-
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
|
|
274
|
-
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
|
|
275
|
-
<line x1="12" y1="22.08" x2="12" y2="12" />
|
|
276
|
-
</svg>
|
|
277
|
-
<span className="font-semibold">{classInstanceRef.className}</span>
|
|
278
|
-
</div>
|
|
279
|
-
<pre
|
|
280
|
-
className="px-2 py-1.5 overflow-x-auto whitespace-pre-wrap"
|
|
281
|
-
style={{ color: colors.text }}
|
|
282
|
-
>
|
|
283
|
-
{JSON.stringify(classInstanceRef.data, null, 2)}
|
|
284
|
-
</pre>
|
|
285
|
-
</div>
|
|
286
|
-
);
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Renders a StreamRef as a styled link/badge
|
|
291
|
-
*/
|
|
292
|
-
const StreamRefDisplay = ({ streamRef }: { streamRef: StreamRef }) => {
|
|
293
|
-
const onStreamClick = useContext(StreamClickContext);
|
|
294
|
-
|
|
295
|
-
const handleClick = () => {
|
|
296
|
-
if (onStreamClick) {
|
|
297
|
-
onStreamClick(streamRef.streamId);
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
return (
|
|
302
|
-
<button
|
|
303
|
-
type="button"
|
|
304
|
-
onClick={handleClick}
|
|
305
|
-
className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-mono cursor-pointer hover:opacity-80 transition-opacity"
|
|
306
|
-
style={{
|
|
307
|
-
backgroundColor: 'var(--ds-blue-200)',
|
|
308
|
-
color: 'var(--ds-blue-900)',
|
|
309
|
-
border: '1px solid var(--ds-blue-400)',
|
|
310
|
-
}}
|
|
311
|
-
title={`Click to view stream: ${streamRef.streamId}`}
|
|
312
|
-
>
|
|
313
|
-
<svg
|
|
314
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
315
|
-
width="10"
|
|
316
|
-
height="10"
|
|
317
|
-
viewBox="0 0 24 24"
|
|
318
|
-
fill="none"
|
|
319
|
-
stroke="currentColor"
|
|
320
|
-
strokeWidth="2"
|
|
321
|
-
strokeLinecap="round"
|
|
322
|
-
strokeLinejoin="round"
|
|
323
|
-
>
|
|
324
|
-
<title>Stream icon</title>
|
|
325
|
-
<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" />
|
|
326
|
-
<circle cx="12" cy="12" r="3" />
|
|
327
|
-
</svg>
|
|
328
|
-
{streamRef.streamId.length > 40
|
|
329
|
-
? `${streamRef.streamId.slice(0, 20)}...${streamRef.streamId.slice(
|
|
330
|
-
-15
|
|
331
|
-
)}`
|
|
332
|
-
: streamRef.streamId}
|
|
333
|
-
</button>
|
|
334
|
-
);
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Renders a value using react-inspector's ObjectInspector for proper
|
|
339
|
-
* display of Map, Set, URLSearchParams, Date, Error, RegExp, typed
|
|
340
|
-
* arrays, and other non-plain-object types.
|
|
341
|
-
*
|
|
342
|
-
* StreamRef and ClassInstanceRef objects are rendered inline as
|
|
343
|
-
* custom components (clickable stream links and class cards).
|
|
344
|
-
*/
|
|
345
|
-
const JsonBlock = (value: unknown) => {
|
|
346
|
-
return <DataInspector data={value} />;
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
import { inspectorThemeDark, inspectorThemeLight } from '../ui/inspector-theme';
|
|
350
|
-
|
|
351
|
-
function DataInspector({ data }: { data: unknown }) {
|
|
352
|
-
const isDark = useDarkMode();
|
|
353
|
-
|
|
354
|
-
// Render top-level StreamRef/ClassInstanceRef as full custom components
|
|
355
|
-
if (isStreamRef(data)) {
|
|
356
|
-
return <StreamRefDisplay streamRef={data} />;
|
|
357
|
-
}
|
|
358
|
-
if (isClassInstanceRef(data)) {
|
|
359
|
-
return <ClassInstanceRefDisplay classInstanceRef={data} />;
|
|
360
|
-
}
|
|
361
|
-
|
|
117
|
+
function JsonBlock(value: unknown) {
|
|
362
118
|
return (
|
|
363
119
|
<div
|
|
364
120
|
className="overflow-x-auto rounded-md border p-3"
|
|
365
121
|
style={{ borderColor: 'var(--ds-gray-300)' }}
|
|
366
122
|
>
|
|
367
|
-
<
|
|
368
|
-
data={data}
|
|
369
|
-
// @ts-expect-error react-inspector accepts theme objects at runtime despite
|
|
370
|
-
// types declaring string only — see https://github.com/storybookjs/react-inspector/blob/main/README.md#theme
|
|
371
|
-
theme={isDark ? inspectorThemeDark : inspectorThemeLight}
|
|
372
|
-
expandLevel={2}
|
|
373
|
-
/>
|
|
123
|
+
<DataInspector data={value} />
|
|
374
124
|
</div>
|
|
375
125
|
);
|
|
376
126
|
}
|
|
@@ -380,6 +130,7 @@ type AttributeKey =
|
|
|
380
130
|
| keyof WorkflowRun
|
|
381
131
|
| keyof Hook
|
|
382
132
|
| keyof Event
|
|
133
|
+
| 'moduleSpecifier'
|
|
383
134
|
| 'eventData'
|
|
384
135
|
| 'resumeAt'
|
|
385
136
|
| 'expiredAt'
|
|
@@ -387,6 +138,7 @@ type AttributeKey =
|
|
|
387
138
|
|
|
388
139
|
const attributeOrder: AttributeKey[] = [
|
|
389
140
|
'workflowName',
|
|
141
|
+
'moduleSpecifier',
|
|
390
142
|
'stepName',
|
|
391
143
|
'status',
|
|
392
144
|
'stepId',
|
|
@@ -438,6 +190,19 @@ const getAttributeDisplayName = (attribute: string): string => {
|
|
|
438
190
|
return attributeDisplayNames[attribute as AttributeKey] ?? attribute;
|
|
439
191
|
};
|
|
440
192
|
|
|
193
|
+
const getModuleSpecifierFromName = (value: unknown): string => {
|
|
194
|
+
const raw = String(value);
|
|
195
|
+
const parsedStep = parseStepName(raw);
|
|
196
|
+
if (parsedStep) {
|
|
197
|
+
return parsedStep.moduleSpecifier;
|
|
198
|
+
}
|
|
199
|
+
const parsedWorkflow = parseWorkflowName(raw);
|
|
200
|
+
if (parsedWorkflow) {
|
|
201
|
+
return parsedWorkflow.moduleSpecifier;
|
|
202
|
+
}
|
|
203
|
+
return raw;
|
|
204
|
+
};
|
|
205
|
+
|
|
441
206
|
export const localMillisecondTime = (value: unknown): string => {
|
|
442
207
|
let date: Date;
|
|
443
208
|
if (value instanceof Date) {
|
|
@@ -473,6 +238,7 @@ const attributeToDisplayFn: Record<
|
|
|
473
238
|
// Names that need pretty-printing
|
|
474
239
|
workflowName: (value: unknown) =>
|
|
475
240
|
parseWorkflowName(String(value))?.shortName ?? '?',
|
|
241
|
+
moduleSpecifier: (value: unknown) => getModuleSpecifierFromName(value),
|
|
476
242
|
stepName: (value: unknown) => parseStepName(String(value))?.shortName ?? '?',
|
|
477
243
|
// IDs
|
|
478
244
|
runId: (value: unknown) => String(value),
|
|
@@ -726,12 +492,14 @@ export const AttributeBlock = ({
|
|
|
726
492
|
|
|
727
493
|
export const AttributePanel = ({
|
|
728
494
|
data,
|
|
495
|
+
moduleSpecifier,
|
|
729
496
|
isLoading,
|
|
730
497
|
error,
|
|
731
498
|
expiredAt,
|
|
732
499
|
onStreamClick,
|
|
733
500
|
}: {
|
|
734
501
|
data: Record<string, unknown>;
|
|
502
|
+
moduleSpecifier?: string;
|
|
735
503
|
isLoading?: boolean;
|
|
736
504
|
error?: Error;
|
|
737
505
|
expiredAt?: string | Date;
|
|
@@ -747,8 +515,15 @@ export const AttributePanel = ({
|
|
|
747
515
|
if (execCtx?.workflowCoreVersion) {
|
|
748
516
|
result.workflowCoreVersion = execCtx.workflowCoreVersion;
|
|
749
517
|
}
|
|
518
|
+
if (moduleSpecifier) {
|
|
519
|
+
result.moduleSpecifier = moduleSpecifier;
|
|
520
|
+
} else if (typeof data.stepName === 'string') {
|
|
521
|
+
result.moduleSpecifier = data.stepName;
|
|
522
|
+
} else if (typeof data.workflowName === 'string') {
|
|
523
|
+
result.moduleSpecifier = data.workflowName;
|
|
524
|
+
}
|
|
750
525
|
return result;
|
|
751
|
-
}, [data]);
|
|
526
|
+
}, [data, moduleSpecifier]);
|
|
752
527
|
const hasExpired = expiredAt != null && new Date(expiredAt) < new Date();
|
|
753
528
|
const basicAttributes = Object.keys(displayData)
|
|
754
529
|
.filter((key) => !resolvableAttributes.includes(key))
|
|
@@ -768,6 +543,31 @@ export const AttributePanel = ({
|
|
|
768
543
|
return displayValue !== null;
|
|
769
544
|
});
|
|
770
545
|
|
|
546
|
+
// Keep `moduleSpecifier` immediately after `workflowName` or `stepName`.
|
|
547
|
+
const orderedBasicAttributes = useMemo(() => {
|
|
548
|
+
const attributes = [...visibleBasicAttributes];
|
|
549
|
+
const moduleSpecifierIndex = attributes.indexOf('moduleSpecifier');
|
|
550
|
+
if (moduleSpecifierIndex === -1) {
|
|
551
|
+
return attributes;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
attributes.splice(moduleSpecifierIndex, 1);
|
|
555
|
+
const workflowNameIndex = attributes.indexOf('workflowName');
|
|
556
|
+
if (workflowNameIndex !== -1) {
|
|
557
|
+
attributes.splice(workflowNameIndex + 1, 0, 'moduleSpecifier');
|
|
558
|
+
return attributes;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const stepNameIndex = attributes.indexOf('stepName');
|
|
562
|
+
if (stepNameIndex !== -1) {
|
|
563
|
+
attributes.splice(stepNameIndex + 1, 0, 'moduleSpecifier');
|
|
564
|
+
return attributes;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
attributes.unshift('moduleSpecifier');
|
|
568
|
+
return attributes;
|
|
569
|
+
}, [visibleBasicAttributes]);
|
|
570
|
+
|
|
771
571
|
// Memoize context object to avoid object reconstruction on render
|
|
772
572
|
const displayContext = useMemo(
|
|
773
573
|
() => ({
|
|
@@ -788,7 +588,7 @@ export const AttributePanel = ({
|
|
|
788
588
|
backgroundColor: 'var(--ds-gray-100)',
|
|
789
589
|
}}
|
|
790
590
|
>
|
|
791
|
-
{
|
|
591
|
+
{orderedBasicAttributes.map((attribute) => (
|
|
792
592
|
<div
|
|
793
593
|
key={attribute}
|
|
794
594
|
className="flex items-center justify-between px-3 py-1.5"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ModelMessage } from 'ai';
|
|
2
2
|
import { Streamdown } from 'streamdown';
|
|
3
|
+
import { DataInspector } from '../ui/data-inspector';
|
|
3
4
|
|
|
4
5
|
interface ConversationViewProps {
|
|
5
6
|
messages: ModelMessage[];
|
|
@@ -108,24 +109,27 @@ function ContentPart({ part, role }: { part: ParsedPart; role: string }) {
|
|
|
108
109
|
<span style={{ color: 'var(--ds-purple-900)' }}>{part.toolName}</span>
|
|
109
110
|
</div>
|
|
110
111
|
{part.input != null && (
|
|
111
|
-
<
|
|
112
|
-
className="mt-1.5
|
|
113
|
-
style={{
|
|
114
|
-
backgroundColor: 'var(--ds-gray-100)',
|
|
115
|
-
color: 'var(--ds-gray-800)',
|
|
116
|
-
}}
|
|
112
|
+
<div
|
|
113
|
+
className="mt-1.5 overflow-x-auto p-1.5 rounded"
|
|
114
|
+
style={{ backgroundColor: 'var(--ds-gray-100)' }}
|
|
117
115
|
>
|
|
118
|
-
{typeof part.input === 'string'
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
{typeof part.input === 'string' ? (
|
|
117
|
+
<pre
|
|
118
|
+
className="text-[10px]"
|
|
119
|
+
style={{ color: 'var(--ds-gray-800)' }}
|
|
120
|
+
>
|
|
121
|
+
{part.input}
|
|
122
|
+
</pre>
|
|
123
|
+
) : (
|
|
124
|
+
<DataInspector data={part.input} />
|
|
125
|
+
)}
|
|
126
|
+
</div>
|
|
122
127
|
)}
|
|
123
128
|
</div>
|
|
124
129
|
);
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
if (part.type === 'tool-result') {
|
|
128
|
-
const outputText = formatOutput(part.output);
|
|
129
133
|
return (
|
|
130
134
|
<div
|
|
131
135
|
className="rounded border px-2 py-1.5"
|
|
@@ -140,16 +144,22 @@ function ContentPart({ part, role }: { part: ParsedPart; role: string }) {
|
|
|
140
144
|
{part.toolName} result
|
|
141
145
|
</span>
|
|
142
146
|
</div>
|
|
143
|
-
{
|
|
144
|
-
<
|
|
145
|
-
className="mt-1.5
|
|
146
|
-
style={{
|
|
147
|
-
backgroundColor: 'var(--ds-gray-100)',
|
|
148
|
-
color: 'var(--ds-gray-800)',
|
|
149
|
-
}}
|
|
147
|
+
{part.output != null && (
|
|
148
|
+
<div
|
|
149
|
+
className="mt-1.5 overflow-x-auto max-h-[200px] overflow-y-auto p-1.5 rounded"
|
|
150
|
+
style={{ backgroundColor: 'var(--ds-gray-100)' }}
|
|
150
151
|
>
|
|
151
|
-
{
|
|
152
|
-
|
|
152
|
+
{typeof part.output === 'string' ? (
|
|
153
|
+
<pre
|
|
154
|
+
className="text-[10px]"
|
|
155
|
+
style={{ color: 'var(--ds-gray-800)' }}
|
|
156
|
+
>
|
|
157
|
+
{part.output}
|
|
158
|
+
</pre>
|
|
159
|
+
) : (
|
|
160
|
+
<DataInspector data={part.output} expandLevel={1} />
|
|
161
|
+
)}
|
|
162
|
+
</div>
|
|
153
163
|
)}
|
|
154
164
|
</div>
|
|
155
165
|
);
|
|
@@ -226,10 +236,3 @@ function parseContent(content: unknown): ParsedPart[] {
|
|
|
226
236
|
|
|
227
237
|
return [];
|
|
228
238
|
}
|
|
229
|
-
|
|
230
|
-
function formatOutput(output: unknown): string | null {
|
|
231
|
-
if (output == null) return null;
|
|
232
|
-
const text =
|
|
233
|
-
typeof output === 'string' ? output : JSON.stringify(output, null, 2);
|
|
234
|
-
return text.length > 500 ? `${text.slice(0, 500)}...` : text;
|
|
235
|
-
}
|