@workflow/web-shared 5.0.0-beta.14 → 5.0.0-beta.16

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 (57) hide show
  1. package/dist/components/event-list-view.d.ts +1 -1
  2. package/dist/components/event-list-view.d.ts.map +1 -1
  3. package/dist/components/event-list-view.js +20 -3
  4. package/dist/components/new-trace-viewer/components/timeline.d.ts +1 -1
  5. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -1
  6. package/dist/components/new-trace-viewer/components/timeline.js +61 -13
  7. package/dist/components/new-trace-viewer/components/timeline.module.css +31 -0
  8. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -1
  9. package/dist/components/new-trace-viewer/trace-viewer.js +8 -9
  10. package/dist/components/new-trace-viewer/utils.d.ts +1 -1
  11. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -1
  12. package/dist/components/new-trace-viewer/utils.js +12 -5
  13. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  14. package/dist/components/sidebar/attribute-panel.js +26 -32
  15. package/dist/components/sidebar/attributes-block.d.ts +28 -0
  16. package/dist/components/sidebar/attributes-block.d.ts.map +1 -0
  17. package/dist/components/sidebar/attributes-block.js +83 -0
  18. package/dist/components/sidebar/entity-detail-panel.js +2 -2
  19. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  20. package/dist/components/sidebar/events-list.js +8 -1
  21. package/dist/components/trace-viewer/components/node.d.ts.map +1 -1
  22. package/dist/components/trace-viewer/components/node.js +2 -1
  23. package/dist/components/ui/context-card.d.ts +47 -0
  24. package/dist/components/ui/context-card.d.ts.map +1 -0
  25. package/dist/components/ui/context-card.js +471 -0
  26. package/dist/components/ui/timestamp-tooltip.d.ts +34 -1
  27. package/dist/components/ui/timestamp-tooltip.d.ts.map +1 -1
  28. package/dist/components/ui/timestamp-tooltip.js +80 -132
  29. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  30. package/dist/components/workflow-trace-view.js +8 -10
  31. package/dist/components/workflow-traces/event-colors.d.ts +1 -0
  32. package/dist/components/workflow-traces/event-colors.d.ts.map +1 -1
  33. package/dist/components/workflow-traces/event-colors.js +12 -1
  34. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  35. package/dist/components/workflow-traces/trace-span-construction.js +2 -1
  36. package/dist/lib/trace-builder.d.ts +15 -0
  37. package/dist/lib/trace-builder.d.ts.map +1 -1
  38. package/dist/lib/trace-builder.js +27 -2
  39. package/dist/styles.css +9 -0
  40. package/package.json +6 -4
  41. package/src/components/event-list-view.tsx +25 -2
  42. package/src/components/new-trace-viewer/components/timeline.module.css +31 -0
  43. package/src/components/new-trace-viewer/components/timeline.tsx +134 -10
  44. package/src/components/new-trace-viewer/trace-viewer.tsx +17 -14
  45. package/src/components/new-trace-viewer/utils.ts +12 -4
  46. package/src/components/sidebar/attribute-panel.tsx +117 -119
  47. package/src/components/sidebar/attributes-block.tsx +182 -0
  48. package/src/components/sidebar/entity-detail-panel.tsx +1 -1
  49. package/src/components/sidebar/events-list.tsx +8 -0
  50. package/src/components/trace-viewer/components/node.tsx +3 -2
  51. package/src/components/ui/context-card.tsx +784 -0
  52. package/src/components/ui/timestamp-tooltip.tsx +132 -194
  53. package/src/components/workflow-trace-view.tsx +18 -10
  54. package/src/components/workflow-traces/event-colors.ts +12 -0
  55. package/src/components/workflow-traces/trace-span-construction.ts +1 -0
  56. package/src/lib/trace-builder.ts +32 -1
  57. package/src/styles.css +9 -0
@@ -0,0 +1,182 @@
1
+ 'use client';
2
+
3
+ import {
4
+ type AttributeChange,
5
+ RESERVED_ATTRIBUTE_KEY_PREFIX,
6
+ } from '@workflow/world';
7
+ import { CopyableDataBlock } from './copyable-data-block';
8
+ import { DetailCard } from './detail-card';
9
+
10
+ /**
11
+ * Check whether an attribute key is in the reserved (`$`-prefixed)
12
+ * namespace used by framework/library code.
13
+ */
14
+ export const isReservedAttributeKey = (key: string): boolean =>
15
+ key.startsWith(RESERVED_ATTRIBUTE_KEY_PREFIX);
16
+
17
+ function ReservedBadge() {
18
+ return (
19
+ <span
20
+ className="shrink-0 rounded border px-1 py-px text-[10px] font-medium leading-3"
21
+ style={{
22
+ borderColor: 'var(--ds-gray-alpha-400)',
23
+ backgroundColor: 'var(--ds-gray-100)',
24
+ color: 'var(--ds-gray-700)',
25
+ }}
26
+ title={`Keys prefixed with "${RESERVED_ATTRIBUTE_KEY_PREFIX}" are reserved for framework and library code`}
27
+ >
28
+ Reserved
29
+ </span>
30
+ );
31
+ }
32
+
33
+ function AttributeRow({
34
+ attributeKey,
35
+ value,
36
+ removed = false,
37
+ }: {
38
+ attributeKey: string;
39
+ value?: string;
40
+ removed?: boolean;
41
+ }) {
42
+ const reserved = isReservedAttributeKey(attributeKey);
43
+ return (
44
+ <div className="flex items-center justify-between gap-3 py-1.5">
45
+ <span
46
+ className="flex min-w-0 items-center gap-1.5 text-label-12 font-mono"
47
+ style={{
48
+ color: reserved ? 'var(--ds-gray-700)' : 'var(--ds-gray-900)',
49
+ }}
50
+ >
51
+ <span className="truncate">{attributeKey}</span>
52
+ {reserved && <ReservedBadge />}
53
+ </span>
54
+ {removed ? (
55
+ <span
56
+ className="shrink-0 text-label-12 italic"
57
+ style={{ color: 'var(--ds-gray-700)' }}
58
+ >
59
+ removed
60
+ </span>
61
+ ) : (
62
+ <span
63
+ className="max-w-[60%] truncate text-right text-label-12 font-mono text-gray-1000"
64
+ title={value}
65
+ >
66
+ {value}
67
+ </span>
68
+ )}
69
+ </div>
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Sort attribute keys for display: user keys first (alphabetical), then
75
+ * reserved `$`-prefixed keys (alphabetical), so framework metadata doesn't
76
+ * crowd out user-set attributes.
77
+ */
78
+ export function sortAttributeKeys(keys: string[]): string[] {
79
+ return [...keys].sort((a, b) => {
80
+ const aReserved = isReservedAttributeKey(a);
81
+ const bReserved = isReservedAttributeKey(b);
82
+ if (aReserved !== bReserved) return aReserved ? 1 : -1;
83
+ return a.localeCompare(b);
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Collapsible card showing a run's materialized attributes as key-value
89
+ * rows. Reserved (`$`-prefixed) keys are visually de-emphasized with a
90
+ * badge and sorted after user keys.
91
+ */
92
+ export function RunAttributesCard({
93
+ attributes,
94
+ }: {
95
+ attributes: Record<string, string>;
96
+ }) {
97
+ const keys = sortAttributeKeys(Object.keys(attributes));
98
+ if (keys.length === 0) return null;
99
+
100
+ return (
101
+ <DetailCard
102
+ summary={`Attributes (${keys.length})`}
103
+ defaultOpen
104
+ contentClassName="mb-0"
105
+ >
106
+ <div className="flex flex-col divide-y divide-gray-alpha-400">
107
+ {keys.map((key) => (
108
+ <AttributeRow attributeKey={key} key={key} value={attributes[key]} />
109
+ ))}
110
+ </div>
111
+ </DetailCard>
112
+ );
113
+ }
114
+
115
+ interface AttrSetEventData {
116
+ changes: AttributeChange[];
117
+ writer?:
118
+ | { type: 'workflow' }
119
+ | { type: 'step'; stepId: string; attempt: number };
120
+ }
121
+
122
+ function isAttrSetEventData(data: unknown): data is AttrSetEventData {
123
+ if (data === null || typeof data !== 'object') return false;
124
+ const record = data as Record<string, unknown>;
125
+ return (
126
+ Array.isArray(record.changes) &&
127
+ record.changes.every(
128
+ (c) =>
129
+ c !== null &&
130
+ typeof c === 'object' &&
131
+ typeof (c as Record<string, unknown>).key === 'string'
132
+ )
133
+ );
134
+ }
135
+
136
+ function describeWriter(writer: AttrSetEventData['writer']): string | null {
137
+ if (!writer) return null;
138
+ if (writer.type === 'workflow') return 'Set by workflow';
139
+ if (writer.type === 'step') {
140
+ return `Set by step ${writer.stepId} (attempt ${writer.attempt})`;
141
+ }
142
+ return null;
143
+ }
144
+
145
+ /**
146
+ * Renders the payload of an `attr_set` event: the list of attribute
147
+ * changes (sets and removals) plus which writer (workflow or step)
148
+ * made them. Falls back to the generic JSON block for unexpected shapes.
149
+ */
150
+ export function AttrSetEventBlock({ data }: { data: unknown }) {
151
+ if (!isAttrSetEventData(data)) {
152
+ return <CopyableDataBlock data={data} />;
153
+ }
154
+
155
+ const writerLabel = describeWriter(data.writer);
156
+
157
+ return (
158
+ <div className="flex flex-col px-3 py-1">
159
+ <div className="flex flex-col divide-y divide-gray-alpha-400">
160
+ {data.changes.map((change, index) => (
161
+ <AttributeRow
162
+ attributeKey={change.key}
163
+ key={`${change.key}:${index}`}
164
+ removed={change.value === null}
165
+ value={change.value ?? undefined}
166
+ />
167
+ ))}
168
+ </div>
169
+ {writerLabel && (
170
+ <div
171
+ className="border-t py-1.5 text-label-12"
172
+ style={{
173
+ borderColor: 'var(--ds-gray-alpha-400)',
174
+ color: 'var(--ds-gray-700)',
175
+ }}
176
+ >
177
+ {writerLabel}
178
+ </div>
179
+ )}
180
+ </div>
181
+ );
182
+ }
@@ -444,7 +444,7 @@ export function EntityDetailPanel({
444
444
  resource={resource}
445
445
  />
446
446
 
447
- {resource !== 'run' && rawEvents && (
447
+ {rawEvents && (
448
448
  <EventsList
449
449
  events={rawEvents}
450
450
  onLoadEventData={onLoadEventData}
@@ -7,6 +7,7 @@ import { RunClickContext, StreamClickContext } from '../ui/data-inspector';
7
7
  import { ErrorCard } from '../ui/error-card';
8
8
  import { ErrorStackBlock, isStructuredError } from '../ui/error-stack-block';
9
9
  import { Skeleton } from '../ui/skeleton';
10
+ import { AttrSetEventBlock } from './attributes-block';
10
11
  import { CopyableDataBlock, EncryptedDataBlock } from './copyable-data-block';
11
12
  import { DetailCard } from './detail-card';
12
13
 
@@ -30,6 +31,7 @@ const DATA_EVENT_TYPES = new Set([
30
31
  'run_failed',
31
32
  'wait_created',
32
33
  'wait_completed',
34
+ 'attr_set',
33
35
  ]);
34
36
 
35
37
  /**
@@ -226,6 +228,12 @@ function EventDataBlock({
226
228
  return <EncryptedDataBlock />;
227
229
  }
228
230
 
231
+ // Attribute changes — render the changed keys and the writer instead of
232
+ // the raw JSON payload.
233
+ if (eventType === 'attr_set') {
234
+ return <AttrSetEventBlock data={data} />;
235
+ }
236
+
229
237
  // For error events (step_failed, step_retrying), the eventData has the shape
230
238
  // { error: StructuredError, stack?: string, ... }. Check both the top-level
231
239
  // value and the nested `error` field for a stack trace.
@@ -18,10 +18,10 @@ import { formatDuration, getHighResInMs } from '../util/timing';
18
18
  import { SpanContent } from './span-content';
19
19
  import { computeSegments } from './span-segments';
20
20
  import {
21
- type ResourceType,
22
- type SpanLayout,
23
21
  getResourceType,
24
22
  getSpanLayout,
23
+ type ResourceType,
24
+ type SpanLayout,
25
25
  } from './span-strategies';
26
26
 
27
27
  export const getSpanColorClassName = (node: SpanNode): string => {
@@ -362,6 +362,7 @@ const BOUNDARY_LABELS: Record<string, string> = {
362
362
  run_completed: 'Completed',
363
363
  run_failed: 'Run failed',
364
364
  step_completed: 'Completed',
365
+ attr_set: 'Attributes set',
365
366
  };
366
367
 
367
368
  export const SpanEventComponent = memo(function SpanEventComponent({