@workflow/web-shared 5.0.0-beta.28 → 5.0.0-beta.29
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/event-list-view.d.ts.map +1 -1
- package/dist/components/event-list-view.js +13 -1
- package/dist/components/new-trace-viewer/components/detail-panel.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/detail-panel.js +2 -2
- package/dist/components/new-trace-viewer/components/event-list.js +2 -2
- package/dist/components/new-trace-viewer/components/split-pane.d.ts +0 -2
- package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/split-pane.js +9 -75
- package/dist/components/new-trace-viewer/trace-viewer.js +3 -3
- package/dist/components/sidebar/attribute-panel.d.ts +4 -1
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
- package/dist/components/sidebar/attribute-panel.js +32 -24
- package/dist/components/sidebar/attributes-block.d.ts +1 -0
- package/dist/components/sidebar/attributes-block.d.ts.map +1 -1
- package/dist/components/sidebar/attributes-block.js +6 -5
- package/dist/components/sidebar/entity-detail-panel.d.ts +5 -1
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.js +18 -3
- package/dist/components/sidebar/sidebar-data-context.d.ts +4 -0
- package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -1
- package/dist/components/sidebar/sidebar-data-context.js +1 -1
- package/package.json +3 -3
- package/src/components/event-list-view.tsx +21 -0
- package/src/components/new-trace-viewer/components/detail-panel.tsx +1 -0
- package/src/components/new-trace-viewer/components/event-list.tsx +1 -1
- package/src/components/new-trace-viewer/components/split-pane.tsx +39 -95
- package/src/components/new-trace-viewer/trace-viewer.tsx +2 -2
- package/src/components/sidebar/attribute-panel.tsx +77 -22
- package/src/components/sidebar/attributes-block.tsx +43 -10
- package/src/components/sidebar/entity-detail-panel.tsx +22 -0
- package/src/components/sidebar/sidebar-data-context.tsx +4 -0
|
@@ -5,7 +5,7 @@ import type { Event, Hook, Step, WorkflowRun } from '@workflow/world';
|
|
|
5
5
|
import type { ModelMessage } from 'ai';
|
|
6
6
|
import { format } from 'date-fns';
|
|
7
7
|
import type { KeyboardEvent, ReactNode } from 'react';
|
|
8
|
-
import { useCallback, useContext, useMemo, useState } from 'react';
|
|
8
|
+
import { useCallback, useContext, useMemo, useRef, useState } from 'react';
|
|
9
9
|
import { isEncryptedMarker, isExpiredMarker } from '../../lib/hydration';
|
|
10
10
|
import { extractConversation, isDoStreamStep } from '../../lib/utils';
|
|
11
11
|
import {
|
|
@@ -148,16 +148,24 @@ const conversationTabs = [
|
|
|
148
148
|
function ConversationWithTabs({
|
|
149
149
|
conversation,
|
|
150
150
|
args,
|
|
151
|
+
defaultOpen,
|
|
152
|
+
onOpenChange,
|
|
151
153
|
}: {
|
|
152
154
|
conversation: ModelMessage[];
|
|
153
155
|
args: unknown[];
|
|
156
|
+
defaultOpen?: boolean;
|
|
157
|
+
onOpenChange?: (open: boolean) => void;
|
|
154
158
|
}) {
|
|
155
159
|
const [activeTab, setActiveTab] = useState<'conversation' | 'json'>(
|
|
156
160
|
'conversation'
|
|
157
161
|
);
|
|
158
162
|
|
|
159
163
|
return (
|
|
160
|
-
<Collapsible
|
|
164
|
+
<Collapsible
|
|
165
|
+
label="Input"
|
|
166
|
+
defaultOpen={defaultOpen}
|
|
167
|
+
onOpenChange={onOpenChange}
|
|
168
|
+
>
|
|
161
169
|
<TabbedContainer
|
|
162
170
|
tabs={conversationTabs}
|
|
163
171
|
activeTab={activeTab}
|
|
@@ -382,6 +390,8 @@ const timestampWithTooltipOrNull = (value: unknown): ReactNode | null => {
|
|
|
382
390
|
|
|
383
391
|
interface DisplayContext {
|
|
384
392
|
stepName?: string;
|
|
393
|
+
sectionOpen?: boolean;
|
|
394
|
+
onSectionOpenChange?: (open: boolean) => void;
|
|
385
395
|
}
|
|
386
396
|
|
|
387
397
|
const attributeToDisplayFn: Record<
|
|
@@ -457,7 +467,11 @@ const attributeToDisplayFn: Record<
|
|
|
457
467
|
input: (value: unknown, context?: DisplayContext) => {
|
|
458
468
|
if (isEncryptedMarker(value)) {
|
|
459
469
|
return (
|
|
460
|
-
<Collapsible
|
|
470
|
+
<Collapsible
|
|
471
|
+
label="Input"
|
|
472
|
+
defaultOpen={context?.sectionOpen}
|
|
473
|
+
onOpenChange={context?.onSectionOpenChange}
|
|
474
|
+
>
|
|
461
475
|
<EncryptedFieldBlock />
|
|
462
476
|
</Collapsible>
|
|
463
477
|
);
|
|
@@ -480,7 +494,12 @@ const attributeToDisplayFn: Record<
|
|
|
480
494
|
if (conversation && conversation.length > 0) {
|
|
481
495
|
return (
|
|
482
496
|
<>
|
|
483
|
-
<ConversationWithTabs
|
|
497
|
+
<ConversationWithTabs
|
|
498
|
+
conversation={conversation}
|
|
499
|
+
args={args}
|
|
500
|
+
defaultOpen={context?.sectionOpen}
|
|
501
|
+
onOpenChange={context?.onSectionOpenChange}
|
|
502
|
+
/>
|
|
484
503
|
{hasClosureVars && (
|
|
485
504
|
<Collapsible label="Closure Variables">
|
|
486
505
|
{JsonBlock(closureVars)}
|
|
@@ -503,7 +522,11 @@ const attributeToDisplayFn: Record<
|
|
|
503
522
|
|
|
504
523
|
return (
|
|
505
524
|
<>
|
|
506
|
-
<Collapsible
|
|
525
|
+
<Collapsible
|
|
526
|
+
label="Input"
|
|
527
|
+
defaultOpen={context?.sectionOpen}
|
|
528
|
+
onOpenChange={context?.onSectionOpenChange}
|
|
529
|
+
>
|
|
507
530
|
{Array.isArray(args)
|
|
508
531
|
? args.map((v, i) => (
|
|
509
532
|
<div className="mt-2 first:mt-0" key={i}>
|
|
@@ -529,7 +552,11 @@ const attributeToDisplayFn: Record<
|
|
|
529
552
|
return <Collapsible label="Input (no data)" disabled />;
|
|
530
553
|
}
|
|
531
554
|
return (
|
|
532
|
-
<Collapsible
|
|
555
|
+
<Collapsible
|
|
556
|
+
label="Input"
|
|
557
|
+
defaultOpen={context?.sectionOpen}
|
|
558
|
+
onOpenChange={context?.onSectionOpenChange}
|
|
559
|
+
>
|
|
533
560
|
{Array.isArray(value)
|
|
534
561
|
? value.map((v, i) => (
|
|
535
562
|
<div className="mt-2 first:mt-0" key={i}>
|
|
@@ -540,17 +567,29 @@ const attributeToDisplayFn: Record<
|
|
|
540
567
|
</Collapsible>
|
|
541
568
|
);
|
|
542
569
|
},
|
|
543
|
-
output: (value: unknown) => {
|
|
570
|
+
output: (value: unknown, context?: DisplayContext) => {
|
|
544
571
|
if (isEncryptedMarker(value)) {
|
|
545
572
|
return (
|
|
546
|
-
<Collapsible
|
|
573
|
+
<Collapsible
|
|
574
|
+
label="Output"
|
|
575
|
+
defaultOpen={context?.sectionOpen}
|
|
576
|
+
onOpenChange={context?.onSectionOpenChange}
|
|
577
|
+
>
|
|
547
578
|
<EncryptedFieldBlock />
|
|
548
579
|
</Collapsible>
|
|
549
580
|
);
|
|
550
581
|
}
|
|
551
582
|
if (!hasDisplayContent(value)) return null;
|
|
552
583
|
if (isExpiredMarker(value)) return <ExpiredFieldBlock />;
|
|
553
|
-
return
|
|
584
|
+
return (
|
|
585
|
+
<Collapsible
|
|
586
|
+
label="Output"
|
|
587
|
+
defaultOpen={context?.sectionOpen}
|
|
588
|
+
onOpenChange={context?.onSectionOpenChange}
|
|
589
|
+
>
|
|
590
|
+
{JsonBlock(value)}
|
|
591
|
+
</Collapsible>
|
|
592
|
+
);
|
|
554
593
|
},
|
|
555
594
|
error: (value: unknown) => {
|
|
556
595
|
if (isEncryptedMarker(value)) {
|
|
@@ -642,6 +681,12 @@ const copyableBasicAttributes = new Set<AttributeKey>([
|
|
|
642
681
|
'moduleSpecifier',
|
|
643
682
|
]);
|
|
644
683
|
|
|
684
|
+
const loadingSectionLabels: Partial<Record<AttributeKey, string>> = {
|
|
685
|
+
input: 'Input',
|
|
686
|
+
output: 'Output',
|
|
687
|
+
eventData: 'Event Data',
|
|
688
|
+
};
|
|
689
|
+
|
|
645
690
|
export const AttributeBlock = ({
|
|
646
691
|
attribute,
|
|
647
692
|
value,
|
|
@@ -656,20 +701,19 @@ export const AttributeBlock = ({
|
|
|
656
701
|
context?: DisplayContext;
|
|
657
702
|
}) => {
|
|
658
703
|
const decryptCtx = useContext(DecryptClickContext);
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
attribute === 'eventData'
|
|
666
|
-
? 'Event Data'
|
|
667
|
-
: attribute === 'output'
|
|
668
|
-
? 'Output'
|
|
669
|
-
: 'Input';
|
|
704
|
+
const sectionOpenRef = useRef(false);
|
|
705
|
+
const handleSectionOpenChange = useCallback((open: boolean) => {
|
|
706
|
+
sectionOpenRef.current = open;
|
|
707
|
+
}, []);
|
|
708
|
+
const label = loadingSectionLabels[attribute as AttributeKey];
|
|
709
|
+
if (isLoading && label && !hasDisplayContent(value)) {
|
|
670
710
|
if (decryptCtx?.hasEncryptedData) {
|
|
671
711
|
return (
|
|
672
|
-
<Collapsible
|
|
712
|
+
<Collapsible
|
|
713
|
+
label={label}
|
|
714
|
+
defaultOpen={attribute === 'eventData' || sectionOpenRef.current}
|
|
715
|
+
onOpenChange={handleSectionOpenChange}
|
|
716
|
+
>
|
|
673
717
|
<EncryptedFieldBlock />
|
|
674
718
|
</Collapsible>
|
|
675
719
|
);
|
|
@@ -682,7 +726,11 @@ export const AttributeBlock = ({
|
|
|
682
726
|
if (!displayFn) {
|
|
683
727
|
return null;
|
|
684
728
|
}
|
|
685
|
-
const displayValue = displayFn(value,
|
|
729
|
+
const displayValue = displayFn(value, {
|
|
730
|
+
...context,
|
|
731
|
+
sectionOpen: sectionOpenRef.current,
|
|
732
|
+
onSectionOpenChange: handleSectionOpenChange,
|
|
733
|
+
});
|
|
686
734
|
if (!displayValue) {
|
|
687
735
|
return null;
|
|
688
736
|
}
|
|
@@ -732,6 +780,7 @@ export const AttributeBlock = ({
|
|
|
732
780
|
export const AttributePanel = ({
|
|
733
781
|
data,
|
|
734
782
|
moduleSpecifier,
|
|
783
|
+
moduleSourceUrl,
|
|
735
784
|
isLoading,
|
|
736
785
|
error,
|
|
737
786
|
expiredAt,
|
|
@@ -743,6 +792,7 @@ export const AttributePanel = ({
|
|
|
743
792
|
}: {
|
|
744
793
|
data: Record<string, unknown>;
|
|
745
794
|
moduleSpecifier?: string;
|
|
795
|
+
moduleSourceUrl?: string;
|
|
746
796
|
isLoading?: boolean;
|
|
747
797
|
error?: Error;
|
|
748
798
|
expiredAt?: string | Date;
|
|
@@ -877,6 +927,11 @@ export const AttributePanel = ({
|
|
|
877
927
|
copyText={
|
|
878
928
|
isCopyableBasicAttribute ? displayValue : undefined
|
|
879
929
|
}
|
|
930
|
+
href={
|
|
931
|
+
attribute === 'moduleSpecifier'
|
|
932
|
+
? moduleSourceUrl
|
|
933
|
+
: undefined
|
|
934
|
+
}
|
|
880
935
|
/>
|
|
881
936
|
);
|
|
882
937
|
})}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
RESERVED_ATTRIBUTE_KEY_PREFIX,
|
|
6
6
|
} from '@workflow/world';
|
|
7
7
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
8
|
+
import { ArrowUpRight } from 'lucide-react';
|
|
8
9
|
import type { ReactNode } from 'react';
|
|
9
10
|
import { cn } from '../../lib/cn';
|
|
10
11
|
import { CopyButton } from '../new-trace-viewer/components/copy-button';
|
|
@@ -21,7 +22,7 @@ function isReservedAttributeKey(key: string): boolean {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
const rowValueVariants = cva(
|
|
24
|
-
'
|
|
25
|
+
'min-w-0 flex-1 truncate text-right text-copy-13 text-gray-1000',
|
|
25
26
|
{
|
|
26
27
|
variants: {
|
|
27
28
|
variant: {
|
|
@@ -36,7 +37,7 @@ const rowValueVariants = cva(
|
|
|
36
37
|
);
|
|
37
38
|
|
|
38
39
|
const rowCopyValueVariants = cva(
|
|
39
|
-
'flex min-w-0
|
|
40
|
+
'flex min-w-0 flex-1 items-center justify-end gap-1 text-copy-13 text-gray-1000',
|
|
40
41
|
{
|
|
41
42
|
variants: {
|
|
42
43
|
variant: {
|
|
@@ -54,6 +55,7 @@ type DetailKeyValueRowProps = {
|
|
|
54
55
|
label: string;
|
|
55
56
|
value?: ReactNode;
|
|
56
57
|
copyText?: string;
|
|
58
|
+
href?: string;
|
|
57
59
|
removed?: boolean;
|
|
58
60
|
};
|
|
59
61
|
|
|
@@ -61,6 +63,7 @@ function DetailKeyValueRowBase({
|
|
|
61
63
|
label,
|
|
62
64
|
value,
|
|
63
65
|
copyText,
|
|
66
|
+
href,
|
|
64
67
|
removed = false,
|
|
65
68
|
variant,
|
|
66
69
|
}: DetailKeyValueRowProps & VariantProps<typeof rowValueVariants>) {
|
|
@@ -77,13 +80,32 @@ function DetailKeyValueRowBase({
|
|
|
77
80
|
</span>
|
|
78
81
|
) : copyText ? (
|
|
79
82
|
<div className={cn(rowCopyValueVariants({ variant }))} title={copyText}>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
{href ? (
|
|
84
|
+
<a
|
|
85
|
+
href={href}
|
|
86
|
+
target="_blank"
|
|
87
|
+
rel="noreferrer"
|
|
88
|
+
className={cn(
|
|
89
|
+
rowValueVariants({ variant }),
|
|
90
|
+
'flex min-w-0 items-center gap-0.5 hover:underline'
|
|
91
|
+
)}
|
|
92
|
+
>
|
|
93
|
+
<MiddleTruncate
|
|
94
|
+
value={copyText}
|
|
95
|
+
className="min-w-0 text-right"
|
|
96
|
+
style={{ gridTemplateColumns: 'minmax(0, 1fr)' }}
|
|
97
|
+
/>
|
|
98
|
+
<ArrowUpRight aria-hidden className="h-3 w-3 shrink-0" />
|
|
99
|
+
</a>
|
|
100
|
+
) : (
|
|
101
|
+
<MiddleTruncate
|
|
102
|
+
value={copyText}
|
|
103
|
+
className={cn(
|
|
104
|
+
rowValueVariants({ variant, className: 'text-right' })
|
|
105
|
+
)}
|
|
106
|
+
style={{ gridTemplateColumns: 'minmax(0, 1fr)' }}
|
|
107
|
+
/>
|
|
108
|
+
)}
|
|
87
109
|
<CopyButton
|
|
88
110
|
copyText={copyText}
|
|
89
111
|
ariaLabel={`Copy ${label}`}
|
|
@@ -92,7 +114,18 @@ function DetailKeyValueRowBase({
|
|
|
92
114
|
</div>
|
|
93
115
|
) : (
|
|
94
116
|
<span className={cn(rowValueVariants({ variant }))} title={stringValue}>
|
|
95
|
-
{
|
|
117
|
+
{href ? (
|
|
118
|
+
<a
|
|
119
|
+
href={href}
|
|
120
|
+
target="_blank"
|
|
121
|
+
rel="noreferrer"
|
|
122
|
+
className="hover:underline"
|
|
123
|
+
>
|
|
124
|
+
{value}
|
|
125
|
+
</a>
|
|
126
|
+
) : (
|
|
127
|
+
value
|
|
128
|
+
)}
|
|
96
129
|
</span>
|
|
97
130
|
)}
|
|
98
131
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { parseStepName, parseWorkflowName } from '@workflow/utils/parse-name';
|
|
3
4
|
import type { Event, Hook, WorkflowRun } from '@workflow/world';
|
|
4
5
|
import clsx from 'clsx';
|
|
5
6
|
import { Send, Zap } from 'lucide-react';
|
|
@@ -62,6 +63,7 @@ export function EntityDetailPanel({
|
|
|
62
63
|
isDecrypting = false,
|
|
63
64
|
selectedSpan,
|
|
64
65
|
showSeparateEventOccurrenceTimestamps = false,
|
|
66
|
+
getModuleSourceUrl,
|
|
65
67
|
}: {
|
|
66
68
|
run: WorkflowRun;
|
|
67
69
|
/** Callback when a stream reference is clicked */
|
|
@@ -95,6 +97,10 @@ export function EntityDetailPanel({
|
|
|
95
97
|
selectedSpan: SelectedSpanInfo | null;
|
|
96
98
|
/** Show occurredAt separately instead of folding it into the Created timestamp. */
|
|
97
99
|
showSeparateEventOccurrenceTimestamps?: boolean;
|
|
100
|
+
getModuleSourceUrl?: (info: {
|
|
101
|
+
moduleSpecifier: string;
|
|
102
|
+
deploymentId: string;
|
|
103
|
+
}) => string | undefined;
|
|
98
104
|
}): React.JSX.Element | null {
|
|
99
105
|
const toast = useToast();
|
|
100
106
|
const [stoppingSleep, setStoppingSleep] = useState(false);
|
|
@@ -277,6 +283,21 @@ export function EntityDetailPanel({
|
|
|
277
283
|
return undefined;
|
|
278
284
|
}, [displayData, run.workflowName]);
|
|
279
285
|
|
|
286
|
+
const moduleSourceUrl = useMemo(() => {
|
|
287
|
+
if (!getModuleSourceUrl || !moduleSpecifier) return undefined;
|
|
288
|
+
const parsed =
|
|
289
|
+
parseStepName(moduleSpecifier) ?? parseWorkflowName(moduleSpecifier);
|
|
290
|
+
if (!parsed) return undefined;
|
|
291
|
+
const dataDeploymentId = displayData.deploymentId;
|
|
292
|
+
return getModuleSourceUrl({
|
|
293
|
+
moduleSpecifier: parsed.moduleSpecifier,
|
|
294
|
+
deploymentId:
|
|
295
|
+
typeof dataDeploymentId === 'string'
|
|
296
|
+
? dataDeploymentId
|
|
297
|
+
: run.deploymentId,
|
|
298
|
+
});
|
|
299
|
+
}, [getModuleSourceUrl, moduleSpecifier, displayData, run.deploymentId]);
|
|
300
|
+
|
|
280
301
|
if (!selectedSpan || !resource || !resourceId) {
|
|
281
302
|
return null;
|
|
282
303
|
}
|
|
@@ -360,6 +381,7 @@ export function EntityDetailPanel({
|
|
|
360
381
|
<AttributePanel
|
|
361
382
|
data={displayData}
|
|
362
383
|
moduleSpecifier={moduleSpecifier}
|
|
384
|
+
moduleSourceUrl={moduleSourceUrl}
|
|
363
385
|
expiredAt={run.expiredAt}
|
|
364
386
|
isLoading={loading}
|
|
365
387
|
error={error ?? undefined}
|
|
@@ -29,6 +29,10 @@ export interface SidebarDataContextValue {
|
|
|
29
29
|
hasEncryptedData?: boolean;
|
|
30
30
|
/** Show occurredAt separately instead of folding it into the Created timestamp. */
|
|
31
31
|
showSeparateEventOccurrenceTimestamps?: boolean;
|
|
32
|
+
getModuleSourceUrl?: (info: {
|
|
33
|
+
moduleSpecifier: string;
|
|
34
|
+
deploymentId: string;
|
|
35
|
+
}) => string | undefined;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
const SidebarDataContext = createContext<SidebarDataContextValue | null>(null);
|