@workflow/web-shared 4.1.0-beta.51 → 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.
Files changed (145) hide show
  1. package/dist/components/error-boundary.d.ts +15 -20
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/error-boundary.js +17 -31
  4. package/dist/components/error-boundary.js.map +1 -1
  5. package/dist/components/event-list-view.d.ts +7 -6
  6. package/dist/components/event-list-view.d.ts.map +1 -1
  7. package/dist/components/event-list-view.js +492 -109
  8. package/dist/components/event-list-view.js.map +1 -1
  9. package/dist/components/index.d.ts +1 -0
  10. package/dist/components/index.d.ts.map +1 -1
  11. package/dist/components/index.js +1 -0
  12. package/dist/components/index.js.map +1 -1
  13. package/dist/components/run-trace-view.d.ts +2 -1
  14. package/dist/components/run-trace-view.d.ts.map +1 -1
  15. package/dist/components/run-trace-view.js +2 -2
  16. package/dist/components/run-trace-view.js.map +1 -1
  17. package/dist/components/sidebar/attribute-panel.d.ts +2 -1
  18. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  19. package/dist/components/sidebar/attribute-panel.js +53 -142
  20. package/dist/components/sidebar/attribute-panel.js.map +1 -1
  21. package/dist/components/sidebar/conversation-view.d.ts.map +1 -1
  22. package/dist/components/sidebar/conversation-view.js +3 -17
  23. package/dist/components/sidebar/conversation-view.js.map +1 -1
  24. package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
  25. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  26. package/dist/components/sidebar/entity-detail-panel.js +63 -10
  27. package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
  28. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  29. package/dist/components/sidebar/events-list.js +4 -8
  30. package/dist/components/sidebar/events-list.js.map +1 -1
  31. package/dist/components/sidebar/resolve-hook-modal.d.ts +3 -0
  32. package/dist/components/sidebar/resolve-hook-modal.d.ts.map +1 -1
  33. package/dist/components/sidebar/resolve-hook-modal.js +152 -3
  34. package/dist/components/sidebar/resolve-hook-modal.js.map +1 -1
  35. package/dist/components/stream-viewer.d.ts +7 -5
  36. package/dist/components/stream-viewer.d.ts.map +1 -1
  37. package/dist/components/stream-viewer.js +54 -22
  38. package/dist/components/stream-viewer.js.map +1 -1
  39. package/dist/components/trace-viewer/components/markers.d.ts +2 -1
  40. package/dist/components/trace-viewer/components/markers.d.ts.map +1 -1
  41. package/dist/components/trace-viewer/components/markers.js +59 -20
  42. package/dist/components/trace-viewer/components/markers.js.map +1 -1
  43. package/dist/components/trace-viewer/components/node.d.ts +5 -1
  44. package/dist/components/trace-viewer/components/node.d.ts.map +1 -1
  45. package/dist/components/trace-viewer/components/node.js +250 -68
  46. package/dist/components/trace-viewer/components/node.js.map +1 -1
  47. package/dist/components/trace-viewer/components/span-content.d.ts +19 -0
  48. package/dist/components/trace-viewer/components/span-content.d.ts.map +1 -0
  49. package/dist/components/trace-viewer/components/span-content.js +137 -0
  50. package/dist/components/trace-viewer/components/span-content.js.map +1 -0
  51. package/dist/components/trace-viewer/components/span-detail-panel.d.ts.map +1 -1
  52. package/dist/components/trace-viewer/components/span-detail-panel.js +3 -2
  53. package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -1
  54. package/dist/components/trace-viewer/components/span-segments.d.ts +50 -0
  55. package/dist/components/trace-viewer/components/span-segments.d.ts.map +1 -0
  56. package/dist/components/trace-viewer/components/span-segments.js +392 -0
  57. package/dist/components/trace-viewer/components/span-segments.js.map +1 -0
  58. package/dist/components/trace-viewer/components/span-strategies.d.ts +46 -0
  59. package/dist/components/trace-viewer/components/span-strategies.d.ts.map +1 -0
  60. package/dist/components/trace-viewer/components/span-strategies.js +108 -0
  61. package/dist/components/trace-viewer/components/span-strategies.js.map +1 -0
  62. package/dist/components/trace-viewer/context.d.ts +7 -6
  63. package/dist/components/trace-viewer/context.d.ts.map +1 -1
  64. package/dist/components/trace-viewer/context.js +47 -18
  65. package/dist/components/trace-viewer/context.js.map +1 -1
  66. package/dist/components/trace-viewer/trace-viewer.d.ts +5 -1
  67. package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -1
  68. package/dist/components/trace-viewer/trace-viewer.js +87 -11
  69. package/dist/components/trace-viewer/trace-viewer.js.map +1 -1
  70. package/dist/components/trace-viewer/trace-viewer.module.css +179 -6
  71. package/dist/components/trace-viewer/util/timing.d.ts +5 -0
  72. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
  73. package/dist/components/trace-viewer/util/timing.js +12 -0
  74. package/dist/components/trace-viewer/util/timing.js.map +1 -1
  75. package/dist/components/trace-viewer/util/use-streaming-spans.d.ts +1 -1
  76. package/dist/components/trace-viewer/util/use-streaming-spans.d.ts.map +1 -1
  77. package/dist/components/trace-viewer/util/use-streaming-spans.js +29 -17
  78. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -1
  79. package/dist/components/trace-viewer/worker.js +3 -1
  80. package/dist/components/trace-viewer/worker.js.map +1 -1
  81. package/dist/components/ui/alert.js +3 -3
  82. package/dist/components/ui/alert.js.map +1 -1
  83. package/dist/components/ui/card.d.ts.map +1 -1
  84. package/dist/components/ui/card.js +2 -2
  85. package/dist/components/ui/card.js.map +1 -1
  86. package/dist/components/ui/data-inspector.d.ts +17 -0
  87. package/dist/components/ui/data-inspector.d.ts.map +1 -0
  88. package/dist/components/ui/data-inspector.js +184 -0
  89. package/dist/components/ui/data-inspector.js.map +1 -0
  90. package/dist/components/ui/error-card.d.ts.map +1 -1
  91. package/dist/components/ui/error-card.js +4 -1
  92. package/dist/components/ui/error-card.js.map +1 -1
  93. package/dist/components/ui/inspector-theme.d.ts +39 -24
  94. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  95. package/dist/components/ui/inspector-theme.js +90 -38
  96. package/dist/components/ui/inspector-theme.js.map +1 -1
  97. package/dist/components/ui/skeleton.d.ts +1 -1
  98. package/dist/components/ui/skeleton.d.ts.map +1 -1
  99. package/dist/components/ui/skeleton.js +2 -2
  100. package/dist/components/ui/skeleton.js.map +1 -1
  101. package/dist/components/workflow-trace-view.d.ts +3 -1
  102. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  103. package/dist/components/workflow-trace-view.js +435 -21
  104. package/dist/components/workflow-trace-view.js.map +1 -1
  105. package/dist/components/workflow-traces/trace-span-construction.d.ts +1 -1
  106. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  107. package/dist/components/workflow-traces/trace-span-construction.js +2 -2
  108. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
  109. package/dist/lib/hydration.d.ts.map +1 -1
  110. package/dist/lib/hydration.js +17 -3
  111. package/dist/lib/hydration.js.map +1 -1
  112. package/dist/styles.css +186 -0
  113. package/package.json +8 -7
  114. package/src/components/error-boundary.tsx +29 -40
  115. package/src/components/event-list-view.tsx +1000 -287
  116. package/src/components/index.ts +1 -0
  117. package/src/components/run-trace-view.tsx +3 -0
  118. package/src/components/sidebar/attribute-panel.tsx +58 -258
  119. package/src/components/sidebar/conversation-view.tsx +30 -27
  120. package/src/components/sidebar/entity-detail-panel.tsx +86 -20
  121. package/src/components/sidebar/events-list.tsx +4 -11
  122. package/src/components/sidebar/resolve-hook-modal.tsx +206 -47
  123. package/src/components/stream-viewer.tsx +138 -61
  124. package/src/components/trace-viewer/components/markers.tsx +69 -21
  125. package/src/components/trace-viewer/components/node.tsx +346 -100
  126. package/src/components/trace-viewer/components/span-content.tsx +247 -0
  127. package/src/components/trace-viewer/components/span-detail-panel.tsx +7 -2
  128. package/src/components/trace-viewer/components/span-segments.ts +516 -0
  129. package/src/components/trace-viewer/components/span-strategies.ts +205 -0
  130. package/src/components/trace-viewer/context.tsx +92 -40
  131. package/src/components/trace-viewer/trace-viewer.module.css +179 -6
  132. package/src/components/trace-viewer/trace-viewer.tsx +115 -11
  133. package/src/components/trace-viewer/util/timing.ts +13 -0
  134. package/src/components/trace-viewer/util/use-streaming-spans.ts +28 -17
  135. package/src/components/trace-viewer/worker.ts +4 -1
  136. package/src/components/ui/alert.tsx +3 -3
  137. package/src/components/ui/card.tsx +3 -5
  138. package/src/components/ui/data-inspector.tsx +318 -0
  139. package/src/components/ui/error-card.tsx +17 -6
  140. package/src/components/ui/inspector-theme.ts +127 -39
  141. package/src/components/ui/skeleton.tsx +3 -1
  142. package/src/components/workflow-trace-view.tsx +625 -26
  143. package/src/components/workflow-traces/trace-span-construction.ts +3 -2
  144. package/src/lib/hydration.ts +17 -8
  145. package/src/styles.css +186 -0
@@ -1,14 +1,22 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { ChevronRight, Loader2 } from 'lucide-react';
4
- import { useCallback, useMemo, useState } from 'react';
5
- import { ObjectInspector } from 'react-inspector';
6
- import { useDarkMode } from '../hooks/use-dark-mode';
7
- import { inspectorThemeDark, inspectorThemeLight } from './ui/inspector-theme';
8
- import { getEventColor } from './workflow-traces/event-colors';
9
- /**
10
- * Format a date to a human-readable local time string with milliseconds
11
- */
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { parseStepName, parseWorkflowName } from '@workflow/utils/parse-name';
4
+ import { Check, ChevronRight, Copy } from 'lucide-react';
5
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
+ import { Virtuoso } from 'react-virtuoso';
7
+ import { formatDuration } from '../lib/utils';
8
+ import { DataInspector } from './ui/data-inspector';
9
+ import { Skeleton } from './ui/skeleton';
10
+ const BUTTON_RESET_STYLE = {
11
+ appearance: 'none',
12
+ WebkitAppearance: 'none',
13
+ border: 'none',
14
+ background: 'transparent',
15
+ };
16
+ const DOT_PULSE_ANIMATION = 'workflow-dot-pulse 1.25s cubic-bezier(0, 0, 0.2, 1) infinite';
17
+ // ──────────────────────────────────────────────────────────────────────────
18
+ // Helpers
19
+ // ──────────────────────────────────────────────────────────────────────────
12
20
  function formatEventTime(date) {
13
21
  return (date.toLocaleTimeString('en-US', {
14
22
  hour: '2-digit',
@@ -19,48 +27,331 @@ function formatEventTime(date) {
19
27
  '.' +
20
28
  date.getMilliseconds().toString().padStart(3, '0'));
21
29
  }
22
- /**
23
- * Format a date to full local datetime string with milliseconds
24
- */
25
- function formatEventDateTime(date) {
26
- return date.toLocaleString(undefined, {
27
- year: 'numeric',
28
- month: 'numeric',
29
- day: 'numeric',
30
- hour: 'numeric',
31
- minute: 'numeric',
32
- second: 'numeric',
33
- fractionalSecondDigits: 3,
34
- });
35
- }
36
- /**
37
- * Format event type to a more readable label
38
- */
39
30
  function formatEventType(eventType) {
40
31
  return eventType
41
32
  .split('_')
42
33
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
43
34
  .join(' ');
44
35
  }
36
+ // ──────────────────────────────────────────────────────────────────────────
37
+ // Event type → status color (small dot only)
38
+ // ──────────────────────────────────────────────────────────────────────────
39
+ /** Returns a CSS color using Geist design tokens for the status dot. */
40
+ function getStatusDotColor(eventType) {
41
+ // Failed → red
42
+ if (eventType === 'step_failed' ||
43
+ eventType === 'run_failed' ||
44
+ eventType === 'workflow_failed') {
45
+ return 'var(--ds-red-700)';
46
+ }
47
+ // Cancelled → amber
48
+ if (eventType === 'run_cancelled') {
49
+ return 'var(--ds-amber-700)';
50
+ }
51
+ // Retrying → amber
52
+ if (eventType === 'step_retrying') {
53
+ return 'var(--ds-amber-700)';
54
+ }
55
+ // Completed/succeeded → green
56
+ if (eventType === 'step_completed' ||
57
+ eventType === 'run_completed' ||
58
+ eventType === 'workflow_completed' ||
59
+ eventType === 'hook_disposed' ||
60
+ eventType === 'wait_completed') {
61
+ return 'var(--ds-green-700)';
62
+ }
63
+ // Started/running → blue
64
+ if (eventType === 'step_started' ||
65
+ eventType === 'run_started' ||
66
+ eventType === 'workflow_started' ||
67
+ eventType === 'hook_received') {
68
+ return 'var(--ds-blue-700)';
69
+ }
70
+ // Created/pending → gray
71
+ return 'var(--ds-gray-600)';
72
+ }
45
73
  /**
46
- * Single event row component with expandable details
74
+ * Build a map from correlationId (stepId) → display name using step entities,
75
+ * and parse the workflow name from the run.
47
76
  */
48
- function EventRow({ event, onLoadEventData, }) {
49
- const isDark = useDarkMode();
77
+ function buildNameMaps(steps, run) {
78
+ const correlationNameMap = new Map();
79
+ // Map step correlationId (= stepId) → parsed step name
80
+ if (steps) {
81
+ for (const step of steps) {
82
+ const parsed = parseStepName(String(step.stepName));
83
+ correlationNameMap.set(step.stepId, parsed?.shortName ?? step.stepName);
84
+ }
85
+ }
86
+ // Parse workflow name from run
87
+ const workflowName = run?.workflowName
88
+ ? (parseWorkflowName(run.workflowName)?.shortName ?? run.workflowName)
89
+ : null;
90
+ return { correlationNameMap, workflowName };
91
+ }
92
+ /**
93
+ * Build a map from correlationId → duration info by diffing
94
+ * created ↔ started (queued) and started ↔ completed/failed/cancelled (ran).
95
+ * Also computes run-level durations under the key '__run__'.
96
+ */
97
+ function buildDurationMap(events) {
98
+ const createdTimes = new Map();
99
+ const startedTimes = new Map();
100
+ const durations = new Map();
101
+ for (const event of events) {
102
+ const ts = new Date(event.createdAt).getTime();
103
+ const key = event.correlationId ?? '__run__';
104
+ const type = event.eventType;
105
+ // Track created times (first event for each correlation)
106
+ if (type === 'step_created' || type === 'run_created') {
107
+ createdTimes.set(key, ts);
108
+ }
109
+ // Track started times & compute queued duration
110
+ if (type === 'step_started' ||
111
+ type === 'run_started' ||
112
+ type === 'workflow_started') {
113
+ startedTimes.set(key, ts);
114
+ // If no explicit created event was seen, use the started time as created
115
+ if (!createdTimes.has(key)) {
116
+ createdTimes.set(key, ts);
117
+ }
118
+ const createdAt = createdTimes.get(key);
119
+ const info = durations.get(key) ?? {};
120
+ if (createdAt !== undefined) {
121
+ info.queued = ts - createdAt;
122
+ }
123
+ durations.set(key, info);
124
+ }
125
+ // Compute ran duration on terminal events
126
+ if (type === 'step_completed' ||
127
+ type === 'step_failed' ||
128
+ type === 'run_completed' ||
129
+ type === 'run_failed' ||
130
+ type === 'run_cancelled' ||
131
+ type === 'workflow_completed' ||
132
+ type === 'workflow_failed' ||
133
+ type === 'wait_completed' ||
134
+ type === 'hook_disposed') {
135
+ const startedAt = startedTimes.get(key);
136
+ const info = durations.get(key) ?? {};
137
+ if (startedAt !== undefined) {
138
+ info.ran = ts - startedAt;
139
+ }
140
+ durations.set(key, info);
141
+ }
142
+ }
143
+ return durations;
144
+ }
145
+ function isRunLevel(eventType) {
146
+ return (eventType === 'run_created' ||
147
+ eventType === 'run_started' ||
148
+ eventType === 'run_completed' ||
149
+ eventType === 'run_failed' ||
150
+ eventType === 'run_cancelled' ||
151
+ eventType === 'workflow_started' ||
152
+ eventType === 'workflow_completed' ||
153
+ eventType === 'workflow_failed');
154
+ }
155
+ // ──────────────────────────────────────────────────────────────────────────
156
+ // Tree gutter — fixed-width, shows branch lines only for the selected group
157
+ // ──────────────────────────────────────────────────────────────────────────
158
+ /** Fixed gutter width: 20px root area + 16px for one branch lane */
159
+ const GUTTER_WIDTH = 36;
160
+ /** X position of the single branch lane line */
161
+ const LANE_X = 20;
162
+ const ROOT_LINE_COLOR = 'var(--ds-gray-500)';
163
+ function TreeGutter({ isFirst, isLast, isRunLevel: isRun, statusDotColor, pulse = false, hasSelection, showBranch, showLaneLine, isLaneStart, isLaneEnd, continuationOnly = false, }) {
164
+ const dotSize = isRun ? 8 : 6;
165
+ const dotLeft = isRun ? 5 : 6;
166
+ const dotOpacity = hasSelection && !showBranch && !isRun ? 0.3 : 1;
167
+ return (_jsxs("div", { className: "relative flex-shrink-0 self-stretch", style: {
168
+ width: GUTTER_WIDTH,
169
+ minHeight: continuationOnly ? 0 : undefined,
170
+ }, children: [_jsx("div", { style: {
171
+ position: 'absolute',
172
+ left: 8,
173
+ top: continuationOnly ? 0 : isFirst ? '50%' : 0,
174
+ bottom: continuationOnly ? 0 : isLast ? '50%' : 0,
175
+ width: 2,
176
+ backgroundColor: ROOT_LINE_COLOR,
177
+ zIndex: 0,
178
+ } }), !continuationOnly && (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
179
+ position: 'absolute',
180
+ left: dotLeft,
181
+ top: '50%',
182
+ transform: 'translateY(-50%)',
183
+ width: dotSize,
184
+ height: dotSize,
185
+ zIndex: 2,
186
+ }, children: [_jsx("div", { style: {
187
+ position: 'absolute',
188
+ inset: 0,
189
+ borderRadius: '50%',
190
+ backgroundColor: 'var(--ds-background-100)',
191
+ zIndex: 0,
192
+ } }), pulse && (_jsx("div", { style: {
193
+ position: 'absolute',
194
+ inset: 0,
195
+ borderRadius: '50%',
196
+ backgroundColor: statusDotColor,
197
+ opacity: 0.75 * dotOpacity,
198
+ animation: DOT_PULSE_ANIMATION,
199
+ zIndex: 1,
200
+ } })), _jsx("div", { style: {
201
+ position: 'relative',
202
+ width: '100%',
203
+ height: '100%',
204
+ borderRadius: '50%',
205
+ backgroundColor: statusDotColor,
206
+ opacity: dotOpacity,
207
+ transition: 'opacity 150ms',
208
+ zIndex: 2,
209
+ } })] }), showBranch && (_jsx("div", { style: {
210
+ position: 'absolute',
211
+ left: 9,
212
+ top: '50%',
213
+ width: GUTTER_WIDTH - 9,
214
+ height: 2,
215
+ backgroundColor: ROOT_LINE_COLOR,
216
+ zIndex: 0,
217
+ } }))] })), showLaneLine && (_jsx("div", { style: {
218
+ position: 'absolute',
219
+ left: LANE_X,
220
+ top: continuationOnly ? 0 : isLaneStart ? '50%' : 0,
221
+ bottom: continuationOnly ? 0 : isLaneEnd ? '50%' : 0,
222
+ width: 2,
223
+ backgroundColor: ROOT_LINE_COLOR,
224
+ zIndex: 0,
225
+ } }))] }));
226
+ }
227
+ // ──────────────────────────────────────────────────────────────────────────
228
+ // Copyable cell — shows a copy button on hover
229
+ // ──────────────────────────────────────────────────────────────────────────
230
+ function CopyableCell({ value, className, }) {
231
+ const [copied, setCopied] = useState(false);
232
+ const resetCopiedTimeoutRef = useRef(null);
233
+ useEffect(() => {
234
+ return () => {
235
+ if (resetCopiedTimeoutRef.current !== null) {
236
+ window.clearTimeout(resetCopiedTimeoutRef.current);
237
+ }
238
+ };
239
+ }, []);
240
+ const handleCopy = useCallback((e) => {
241
+ e.stopPropagation();
242
+ navigator.clipboard.writeText(value).then(() => {
243
+ setCopied(true);
244
+ if (resetCopiedTimeoutRef.current !== null) {
245
+ window.clearTimeout(resetCopiedTimeoutRef.current);
246
+ }
247
+ resetCopiedTimeoutRef.current = window.setTimeout(() => {
248
+ setCopied(false);
249
+ resetCopiedTimeoutRef.current = null;
250
+ }, 1500);
251
+ });
252
+ }, [value]);
253
+ return (_jsxs("div", { className: `group/copy flex items-center gap-1 flex-1 min-w-0 px-4 ${className ?? ''}`, children: [_jsx("span", { className: "overflow-hidden text-ellipsis whitespace-nowrap", children: value || '-' }), value ? (_jsx("button", { type: "button", onClick: handleCopy, className: "flex-shrink-0 opacity-0 group-hover/copy:opacity-100 transition-opacity p-0.5 rounded hover:bg-[var(--ds-gray-alpha-200)]", style: BUTTON_RESET_STYLE, "aria-label": `Copy ${value}`, children: copied ? (_jsx(Check, { className: "h-3 w-3", style: { color: 'var(--ds-green-700)' } })) : (_jsx(Copy, { className: "h-3 w-3", style: { color: 'var(--ds-gray-700)' } })) })) : null] }));
254
+ }
255
+ /** Recursively parse stringified JSON values so escaped slashes / quotes are cleaned up */
256
+ function deepParseJson(value) {
257
+ if (typeof value === 'string') {
258
+ const trimmed = value.trim();
259
+ if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
260
+ (trimmed.startsWith('[') && trimmed.endsWith(']')) ||
261
+ (trimmed.startsWith('"') && trimmed.endsWith('"'))) {
262
+ try {
263
+ return deepParseJson(JSON.parse(trimmed));
264
+ }
265
+ catch {
266
+ return value;
267
+ }
268
+ }
269
+ return value;
270
+ }
271
+ if (Array.isArray(value)) {
272
+ return value.map(deepParseJson);
273
+ }
274
+ if (value !== null && typeof value === 'object') {
275
+ const result = {};
276
+ for (const [k, v] of Object.entries(value)) {
277
+ result[k] = deepParseJson(v);
278
+ }
279
+ return result;
280
+ }
281
+ return value;
282
+ }
283
+ function PayloadBlock({ data }) {
284
+ const [copied, setCopied] = useState(false);
285
+ const resetCopiedTimeoutRef = useRef(null);
286
+ const cleaned = useMemo(() => deepParseJson(data), [data]);
287
+ useEffect(() => {
288
+ return () => {
289
+ if (resetCopiedTimeoutRef.current !== null) {
290
+ window.clearTimeout(resetCopiedTimeoutRef.current);
291
+ }
292
+ };
293
+ }, []);
294
+ const formatted = useMemo(() => {
295
+ try {
296
+ return JSON.stringify(cleaned, null, 2);
297
+ }
298
+ catch {
299
+ return String(cleaned);
300
+ }
301
+ }, [cleaned]);
302
+ const handleCopy = useCallback((e) => {
303
+ e.stopPropagation();
304
+ navigator.clipboard.writeText(formatted).then(() => {
305
+ setCopied(true);
306
+ if (resetCopiedTimeoutRef.current !== null) {
307
+ window.clearTimeout(resetCopiedTimeoutRef.current);
308
+ }
309
+ resetCopiedTimeoutRef.current = window.setTimeout(() => {
310
+ setCopied(false);
311
+ resetCopiedTimeoutRef.current = null;
312
+ }, 1500);
313
+ });
314
+ }, [formatted]);
315
+ return (_jsxs("div", { className: "relative group/payload", children: [_jsx("div", { className: "overflow-x-auto p-2 text-[11px]", style: { color: 'var(--ds-gray-1000)' }, children: _jsx(DataInspector, { data: cleaned, expandLevel: 2 }) }), _jsx("button", { type: "button", onClick: handleCopy, className: "absolute bottom-2 right-2 opacity-0 group-hover/payload:opacity-100 transition-opacity flex items-center gap-1 px-2 py-1 rounded-md text-xs hover:bg-[var(--ds-gray-alpha-200)]", style: { ...BUTTON_RESET_STYLE, color: 'var(--ds-gray-700)' }, "aria-label": "Copy payload", children: copied ? (_jsxs(_Fragment, { children: [_jsx(Check, { className: "h-3 w-3", style: { color: 'var(--ds-green-700)' } }), _jsx("span", { style: { color: 'var(--ds-green-700)' }, children: "Copied" })] })) : (_jsxs(_Fragment, { children: [_jsx(Copy, { className: "h-3 w-3" }), _jsx("span", { children: "Copy" })] })) })] }));
316
+ }
317
+ function EventRow({ event, index, isFirst, isLast, activeGroupKey, selectedGroupKey, selectedGroupRange, correlationNameMap, workflowName, durationMap, onSelectGroup, onHoverGroup, onLoadEventData, }) {
50
318
  const [isExpanded, setIsExpanded] = useState(false);
51
319
  const [isLoading, setIsLoading] = useState(false);
52
320
  const [loadedEventData, setLoadedEventData] = useState(null);
53
321
  const [loadError, setLoadError] = useState(null);
54
- const colors = getEventColor(event.eventType);
322
+ const [hasAttemptedLoad, setHasAttemptedLoad] = useState(false);
323
+ const rowGroupKey = event.correlationId ??
324
+ (isRunLevel(event.eventType) ? '__run__' : undefined);
325
+ // Collapse when a different group gets selected
326
+ useEffect(() => {
327
+ if (selectedGroupKey !== undefined && selectedGroupKey !== rowGroupKey) {
328
+ setIsExpanded(false);
329
+ }
330
+ }, [selectedGroupKey, rowGroupKey]);
331
+ const statusDotColor = getStatusDotColor(event.eventType);
55
332
  const createdAt = new Date(event.createdAt);
56
- // Check if event already has eventData (from initial fetch)
57
333
  const hasExistingEventData = 'eventData' in event && event.eventData != null;
58
- // Load full event details when expanding
334
+ const isRun = isRunLevel(event.eventType);
335
+ const eventName = isRun
336
+ ? (workflowName ?? '-')
337
+ : event.correlationId
338
+ ? (correlationNameMap.get(event.correlationId) ?? '-')
339
+ : '-';
340
+ const durationKey = event.correlationId ?? (isRun ? '__run__' : '');
341
+ const durationInfo = durationKey ? durationMap.get(durationKey) : undefined;
342
+ const hasActive = activeGroupKey !== undefined;
343
+ const isRelated = rowGroupKey !== undefined && rowGroupKey === activeGroupKey;
344
+ const isDimmed = hasActive && !isRelated;
345
+ const isPulsing = hasActive && isRelated;
346
+ // Gutter state derived from selectedGroupRange
347
+ const showBranch = hasActive && isRelated && !isRun;
348
+ const showLaneLine = selectedGroupRange !== null &&
349
+ index >= selectedGroupRange.first &&
350
+ index <= selectedGroupRange.last;
351
+ const isLaneStart = selectedGroupRange !== null && index === selectedGroupRange.first;
352
+ const isLaneEnd = selectedGroupRange !== null && index === selectedGroupRange.last;
59
353
  const loadEventDetails = useCallback(async () => {
60
- // Skip if we already have data or no correlationId
61
- if (loadedEventData !== null ||
62
- hasExistingEventData ||
63
- !event.correlationId) {
354
+ if (loadedEventData !== null || hasExistingEventData) {
64
355
  return;
65
356
  }
66
357
  setIsLoading(true);
@@ -80,99 +371,191 @@ function EventRow({ event, onLoadEventData, }) {
80
371
  }
81
372
  finally {
82
373
  setIsLoading(false);
374
+ setHasAttemptedLoad(true);
83
375
  }
84
- }, [
85
- event.correlationId,
86
- loadedEventData,
87
- hasExistingEventData,
88
- onLoadEventData,
89
- ]);
90
- // Handle expand/collapse
91
- const handleToggle = useCallback(() => {
376
+ }, [event, loadedEventData, hasExistingEventData, onLoadEventData]);
377
+ const handleExpandToggle = useCallback((e) => {
378
+ e.stopPropagation();
92
379
  const newExpanded = !isExpanded;
93
380
  setIsExpanded(newExpanded);
94
- // Load details when expanding for the first time
95
381
  if (newExpanded && loadedEventData === null && !hasExistingEventData) {
96
382
  loadEventDetails();
97
383
  }
98
384
  }, [isExpanded, loadedEventData, hasExistingEventData, loadEventDetails]);
99
- // Get the event data to display (either from initial fetch, loaded data, or null)
385
+ const handleRowClick = useCallback(() => {
386
+ if (selectedGroupKey === rowGroupKey) {
387
+ onSelectGroup(undefined);
388
+ }
389
+ else {
390
+ onSelectGroup(rowGroupKey);
391
+ }
392
+ }, [selectedGroupKey, rowGroupKey, onSelectGroup]);
100
393
  const eventData = hasExistingEventData
101
394
  ? event.eventData
102
395
  : loadedEventData;
103
- return (_jsxs("div", { className: "rounded-lg border overflow-hidden transition-all", style: {
104
- backgroundColor: 'var(--ds-background-100)',
105
- borderColor: colors.border,
106
- borderLeftWidth: '1px',
107
- borderLeftColor: colors.color,
108
- }, children: [_jsxs("button", { type: "button", onClick: handleToggle, className: "w-full text-left grid gap-3 items-center px-0 py-2 text-xs hover:brightness-[0.98] transition-all cursor-pointer", style: {
109
- backgroundColor: 'var(--ds-background-100)',
110
- gridTemplateColumns: '24px 100px minmax(120px, auto) 1fr 1fr',
111
- }, children: [_jsx("div", { className: "flex justify-center", children: _jsx(ChevronRight, { className: "h-3.5 w-3.5 transition-transform", style: {
112
- color: colors.secondary,
113
- transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
114
- } }) }), _jsx("div", { className: "font-mono tabular-nums", style: { color: colors.secondary }, children: formatEventTime(createdAt) }), _jsx("div", { className: "font-medium", style: { color: colors.text }, children: _jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx("span", { className: "w-2 h-2 rounded-full flex-shrink-0", style: { backgroundColor: colors.color } }), formatEventType(event.eventType)] }) }), _jsx("div", { className: "font-mono text-[11px] overflow-hidden text-ellipsis whitespace-nowrap", style: { color: colors.secondary }, title: event.correlationId || '-', children: event.correlationId || '-' }), _jsx("div", { className: "font-mono text-[11px] pr-3 overflow-hidden text-ellipsis whitespace-nowrap", style: { color: colors.secondary }, title: event.eventId, children: event.eventId })] }), isExpanded && (_jsxs("div", { className: "border-t px-4 py-3", style: {
115
- borderColor: colors.border,
116
- backgroundColor: 'var(--ds-background-100)',
117
- }, children: [_jsxs("div", { className: "flex flex-col divide-y rounded-md border overflow-hidden", style: {
118
- borderColor: 'var(--ds-gray-300)',
119
- backgroundColor: 'var(--ds-gray-100)',
120
- }, children: [_jsx(AttributeRow, { label: "Event ID", value: event.eventId, mono: true }), _jsx(AttributeRow, { label: "Event Type", value: event.eventType }), _jsx(AttributeRow, { label: "Correlation ID", value: event.correlationId || '-', mono: true }), _jsx(AttributeRow, { label: "Run ID", value: event.runId, mono: true }), _jsx(AttributeRow, { label: "Created At", value: formatEventDateTime(createdAt) })] }), _jsxs("div", { className: "mt-3", children: [_jsx("div", { className: "text-xs font-medium mb-1.5", style: { color: 'var(--ds-gray-700)' }, children: "Event Data" }), isLoading && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border p-3", style: {
121
- borderColor: 'var(--ds-gray-300)',
122
- backgroundColor: 'var(--ds-gray-100)',
123
- }, children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin", style: { color: 'var(--ds-gray-700)' } }), _jsx("span", { className: "text-xs", style: { color: 'var(--ds-gray-700)' }, children: "Loading event details..." })] })), loadError && !isLoading && (_jsx("div", { className: "rounded-md border p-3 text-xs", style: {
396
+ const contentOpacity = isDimmed ? 0.3 : 1;
397
+ return (_jsxs("div", { "data-event-id": event.eventId, onMouseEnter: () => onHoverGroup(rowGroupKey), onMouseLeave: () => onHoverGroup(undefined), children: [_jsxs("div", { role: "button", tabIndex: 0, onClick: handleRowClick, onKeyDown: (e) => {
398
+ if (e.key === 'Enter' || e.key === ' ')
399
+ handleRowClick();
400
+ }, className: "w-full text-left flex items-center gap-0 text-sm hover:bg-[var(--ds-gray-alpha-100)] transition-colors cursor-pointer", style: { minHeight: 40 }, children: [_jsx(TreeGutter, { isFirst: isFirst, isLast: isLast && !isExpanded, isRunLevel: isRun, statusDotColor: statusDotColor, pulse: isPulsing, hasSelection: hasActive, showBranch: showBranch, showLaneLine: showLaneLine, isLaneStart: isLaneStart, isLaneEnd: isLaneEnd }), _jsxs("div", { className: "flex items-center flex-1 min-w-0", style: { opacity: contentOpacity, transition: 'opacity 150ms' }, children: [_jsx("button", { type: "button", onClick: handleExpandToggle, className: "flex items-center justify-center w-5 h-5 flex-shrink-0 rounded hover:bg-[var(--ds-gray-alpha-200)] transition-colors", style: {
401
+ ...BUTTON_RESET_STYLE,
402
+ border: '1px solid var(--ds-gray-alpha-400)',
403
+ }, "aria-label": isExpanded ? 'Collapse details' : 'Expand details', children: _jsx(ChevronRight, { className: "h-3 w-3 transition-transform", style: {
404
+ color: 'var(--ds-gray-700)',
405
+ transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
406
+ } }) }), _jsx("div", { className: "text-xs tabular-nums flex-1 min-w-0 px-4", style: { color: 'var(--ds-gray-900)' }, children: formatEventTime(createdAt) }), _jsx("div", { className: "text-xs font-medium flex-1 min-w-0 px-4", children: _jsxs("span", { className: "inline-flex items-center gap-1.5", style: { color: 'var(--ds-gray-900)' }, children: [_jsxs("span", { style: {
407
+ position: 'relative',
408
+ display: 'inline-flex',
409
+ width: 6,
410
+ height: 6,
411
+ flexShrink: 0,
412
+ }, children: [isPulsing && (_jsx("span", { style: {
413
+ position: 'absolute',
414
+ inset: 0,
415
+ borderRadius: '50%',
416
+ backgroundColor: statusDotColor,
417
+ opacity: 0.75,
418
+ animation: DOT_PULSE_ANIMATION,
419
+ } })), _jsx("span", { style: {
420
+ position: 'relative',
421
+ width: 6,
422
+ height: 6,
423
+ borderRadius: '50%',
424
+ backgroundColor: statusDotColor,
425
+ } })] }), formatEventType(event.eventType)] }) }), _jsx("div", { className: "text-xs flex-1 min-w-0 px-4 overflow-hidden text-ellipsis whitespace-nowrap", title: eventName !== '-' ? eventName : undefined, children: eventName }), _jsx(CopyableCell, { value: event.correlationId || '', className: "font-mono text-xs" }), _jsx(CopyableCell, { value: event.eventId, className: "font-mono text-xs" })] })] }), isExpanded && (_jsxs("div", { className: "flex", children: [_jsx(TreeGutter, { isFirst: false, isLast: isLast, isRunLevel: isRun, hasSelection: hasActive, showBranch: false, showLaneLine: showLaneLine && !isLaneEnd, isLaneStart: false, isLaneEnd: false, continuationOnly: true }), _jsx("div", { className: "w-5 flex-shrink-0" }), _jsxs("div", { className: "flex-1 my-1.5 mr-3 ml-2 py-2 rounded-md border overflow-hidden", style: {
426
+ borderColor: 'var(--ds-gray-alpha-200)',
427
+ opacity: contentOpacity,
428
+ transition: 'opacity 150ms',
429
+ }, children: [(durationInfo?.queued !== undefined ||
430
+ durationInfo?.ran !== undefined) && (_jsxs("div", { className: "px-2 pb-1.5 text-xs flex gap-3", style: { color: 'var(--ds-gray-900)' }, children: [durationInfo.queued !== undefined &&
431
+ durationInfo.queued > 0 && (_jsxs("span", { children: ["Queued for", ' ', _jsx("span", { className: "font-mono tabular-nums", children: formatDuration(durationInfo.queued) })] })), durationInfo.ran !== undefined && (_jsxs("span", { children: ["Ran for", ' ', _jsx("span", { className: "font-mono tabular-nums", children: formatDuration(durationInfo.ran) })] }))] })), eventData != null ? (_jsx(PayloadBlock, { data: eventData })) : loadError ? (_jsx("div", { className: "rounded-md border p-3 text-xs", style: {
124
432
  borderColor: 'var(--ds-red-400)',
125
433
  backgroundColor: 'var(--ds-red-100)',
126
434
  color: 'var(--ds-red-900)',
127
- }, children: loadError })), !isLoading && !loadError && eventData != null && (_jsx("div", { className: "overflow-x-auto rounded-md border p-3", style: { borderColor: 'var(--ds-gray-300)' }, children: _jsx(ObjectInspector, { data: eventData,
128
- // @ts-expect-error react-inspector accepts theme objects at runtime
129
- // see https://github.com/storybookjs/react-inspector/blob/main/README.md#theme
130
- theme: isDark ? inspectorThemeDark : inspectorThemeLight, expandLevel: 2 }) })), !isLoading &&
131
- !loadError &&
132
- eventData == null &&
133
- !event.correlationId && (_jsx("div", { className: "rounded-md border p-3 text-xs", style: {
134
- borderColor: 'var(--ds-gray-300)',
135
- backgroundColor: 'var(--ds-gray-100)',
136
- color: 'var(--ds-gray-700)',
137
- }, children: "No event data available" })), !isLoading &&
138
- !loadError &&
139
- eventData == null &&
140
- event.correlationId &&
141
- !hasExistingEventData &&
142
- loadedEventData === null && (_jsx("div", { className: "rounded-md border p-3 text-xs", style: {
143
- borderColor: 'var(--ds-gray-300)',
144
- backgroundColor: 'var(--ds-gray-100)',
145
- color: 'var(--ds-gray-700)',
146
- }, children: "No event data for this event type" }))] })] }))] }));
147
- }
148
- /**
149
- * Helper component for attribute rows in the expanded details
150
- */
151
- function AttributeRow({ label, value, mono = false, }) {
152
- return (_jsxs("div", { className: "flex items-center justify-between px-2.5 py-1.5", style: { borderColor: 'var(--ds-gray-300)' }, children: [_jsx("span", { className: "text-[11px] font-medium", style: { color: 'var(--ds-gray-700)' }, children: label }), _jsx("span", { className: `text-[11px] ${mono ? 'font-mono' : ''} text-right max-w-[70%] break-all`, style: { color: 'var(--ds-gray-1000)' }, children: value })] }));
435
+ }, children: loadError })) : isLoading ||
436
+ (!hasExistingEventData &&
437
+ !hasAttemptedLoad &&
438
+ event.correlationId) ? (_jsxs("div", { className: "flex flex-col gap-2 p-3", children: [_jsx(Skeleton, { className: "h-3", style: { width: '75%' } }), _jsx(Skeleton, { className: "h-3", style: { width: '50%' } }), _jsx(Skeleton, { className: "h-3", style: { width: '60%' } })] })) : (_jsx("div", { className: "p-2 text-xs", style: { color: 'var(--ds-gray-900)' }, children: "No data" }))] })] }))] }));
153
439
  }
154
- /**
155
- * Displays a list of all events for a workflow run as colored cards in a pseudo-table.
156
- * Events are sorted by createdAt (oldest first).
157
- */
158
- export function EventListView({ events, onLoadEventData }) {
159
- // Sort events by createdAt (oldest first)
440
+ // ──────────────────────────────────────────────────────────────────────────
441
+ // Main component
442
+ // ──────────────────────────────────────────────────────────────────────────
443
+ export function EventListView({ events, steps, run, onLoadEventData, hasMoreEvents = false, isLoadingMoreEvents = false, onLoadMoreEvents, }) {
160
444
  const sortedEvents = useMemo(() => {
161
445
  if (!events || events.length === 0)
162
446
  return [];
163
447
  return [...events].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
164
448
  }, [events]);
449
+ const { correlationNameMap, workflowName } = useMemo(() => buildNameMaps(steps ?? null, run ?? null), [steps, run]);
450
+ const durationMap = useMemo(() => buildDurationMap(sortedEvents), [sortedEvents]);
451
+ const [selectedGroupKey, setSelectedGroupKey] = useState(undefined);
452
+ const [hoveredGroupKey, setHoveredGroupKey] = useState(undefined);
453
+ const onSelectGroup = useCallback((groupKey) => {
454
+ setSelectedGroupKey(groupKey);
455
+ }, []);
456
+ const onHoverGroup = useCallback((groupKey) => {
457
+ setHoveredGroupKey(groupKey);
458
+ }, []);
459
+ const activeGroupKey = selectedGroupKey ?? hoveredGroupKey;
460
+ // Compute the row-index range for the active group's connecting lane line.
461
+ // Only applies to non-run groups (step/hook/wait correlations).
462
+ const selectedGroupRange = useMemo(() => {
463
+ if (!activeGroupKey || activeGroupKey === '__run__')
464
+ return null;
465
+ let first = -1;
466
+ let last = -1;
467
+ for (let i = 0; i < sortedEvents.length; i++) {
468
+ if (sortedEvents[i].correlationId === activeGroupKey) {
469
+ if (first === -1)
470
+ first = i;
471
+ last = i;
472
+ }
473
+ }
474
+ return first >= 0 ? { first, last } : null;
475
+ }, [activeGroupKey, sortedEvents]);
476
+ const [searchQuery, setSearchQuery] = useState('');
477
+ const virtuosoRef = useRef(null);
478
+ const searchIndex = useMemo(() => {
479
+ const entries = [];
480
+ for (let i = 0; i < sortedEvents.length; i++) {
481
+ const ev = sortedEvents[i];
482
+ entries.push({
483
+ text: [ev.eventId, ev.correlationId ?? ''].join(' ').toLowerCase(),
484
+ groupKey: ev.correlationId ??
485
+ (isRunLevel(ev.eventType) ? '__run__' : undefined),
486
+ eventId: ev.eventId,
487
+ index: i,
488
+ });
489
+ }
490
+ return entries;
491
+ }, [sortedEvents]);
492
+ useEffect(() => {
493
+ const q = searchQuery.trim().toLowerCase();
494
+ if (!q) {
495
+ setSelectedGroupKey(undefined);
496
+ return;
497
+ }
498
+ const match = searchIndex.find((entry) => entry.text.includes(q));
499
+ if (match) {
500
+ setSelectedGroupKey(match.groupKey);
501
+ virtuosoRef.current?.scrollToIndex({
502
+ index: match.index,
503
+ align: 'center',
504
+ behavior: 'smooth',
505
+ });
506
+ }
507
+ }, [searchQuery, searchIndex]);
165
508
  if (!events || events.length === 0) {
166
509
  return (_jsx("div", { className: "flex items-center justify-center h-full text-sm", style: { color: 'var(--ds-gray-700)' }, children: "No events found" }));
167
510
  }
168
- return (_jsxs("div", { className: "h-full overflow-auto m-2", children: [_jsxs("div", { className: "grid gap-3 pb-2 mb-2 border-b text-xs font-medium sticky top-0 z-10", style: {
169
- gridTemplateColumns: '24px 100px minmax(120px, auto) 1fr 1fr',
170
- borderColor: 'var(--ds-gray-300)',
171
- backgroundColor: 'transparent',
172
- color: 'var(--ds-gray-700)',
173
- }, children: [_jsx("div", {}), _jsx("div", { children: "Time" }), _jsx("div", { children: "Event Type" }), _jsx("div", { children: "Correlation ID" }), _jsx("div", { children: "Event ID" })] }), _jsx("div", { className: "flex flex-col gap-2", children: sortedEvents.map((event) => (_jsx(EventRow, { event: event, onLoadEventData: onLoadEventData }, event.eventId))) }), _jsxs("div", { className: "mt-4 pt-3 border-t text-xs", style: {
174
- borderColor: 'var(--ds-gray-300)',
175
- color: 'var(--ds-gray-700)',
176
- }, children: [sortedEvents.length, " event", sortedEvents.length !== 1 ? 's' : '', " total"] })] }));
511
+ return (_jsxs("div", { className: "h-full flex flex-col overflow-hidden", children: [_jsx("style", { children: `@keyframes workflow-dot-pulse{0%{transform:scale(1);opacity:.7}70%,100%{transform:scale(2.2);opacity:0}}` }), _jsx("div", { style: { padding: 6, backgroundColor: 'var(--ds-background-100)' }, children: _jsxs("label", { style: {
512
+ display: 'flex',
513
+ alignItems: 'center',
514
+ justifyContent: 'center',
515
+ borderRadius: 6,
516
+ boxShadow: '0 0 0 1px var(--ds-gray-alpha-400)',
517
+ background: 'var(--ds-background-100)',
518
+ height: 40,
519
+ }, children: [_jsx("div", { style: {
520
+ width: 40,
521
+ height: 40,
522
+ display: 'flex',
523
+ alignItems: 'center',
524
+ justifyContent: 'center',
525
+ color: 'var(--ds-gray-800)',
526
+ flexShrink: 0,
527
+ }, children: _jsxs("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", focusable: "false", children: [_jsx("circle", { cx: "7", cy: "7", r: "4.5", stroke: "currentColor", strokeWidth: "1.5" }), _jsx("path", { d: "M11.5 11.5L14 14", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })] }) }), _jsx("input", { type: "search", placeholder: "Search by event ID or correlation ID\u2026", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: {
528
+ marginLeft: -16,
529
+ paddingInline: 12,
530
+ fontFamily: 'inherit',
531
+ fontSize: 14,
532
+ background: 'transparent',
533
+ border: 'none',
534
+ outline: 'none',
535
+ height: 40,
536
+ width: '100%',
537
+ } })] }) }), _jsxs("div", { className: "flex items-center gap-0 text-sm font-medium h-10 border-b flex-shrink-0", style: {
538
+ borderColor: 'var(--ds-gray-alpha-200)',
539
+ color: 'var(--ds-gray-900)',
540
+ backgroundColor: 'var(--ds-background-100)',
541
+ }, children: [_jsx("div", { className: "flex-shrink-0", style: { width: GUTTER_WIDTH } }), _jsx("div", { className: "w-5 flex-shrink-0" }), _jsx("div", { className: "flex-1 min-w-0 px-4", children: "Time" }), _jsx("div", { className: "flex-1 min-w-0 px-4", children: "Event Type" }), _jsx("div", { className: "flex-1 min-w-0 px-4", children: "Name" }), _jsx("div", { className: "flex-1 min-w-0 px-4", children: "Correlation ID" }), _jsx("div", { className: "flex-1 min-w-0 px-4", children: "Event ID" })] }), _jsx(Virtuoso, { ref: virtuosoRef, totalCount: sortedEvents.length, overscan: 20, defaultItemHeight: 40, endReached: () => {
542
+ if (!hasMoreEvents || isLoadingMoreEvents) {
543
+ return;
544
+ }
545
+ void onLoadMoreEvents?.();
546
+ }, itemContent: (index) => {
547
+ return (_jsx(EventRow, { event: sortedEvents[index], index: index, isFirst: index === 0, isLast: index === sortedEvents.length - 1, activeGroupKey: activeGroupKey, selectedGroupKey: selectedGroupKey, selectedGroupRange: selectedGroupRange, correlationNameMap: correlationNameMap, workflowName: workflowName, durationMap: durationMap, onSelectGroup: onSelectGroup, onHoverGroup: onHoverGroup, onLoadEventData: onLoadEventData }));
548
+ }, components: {
549
+ Footer: () => (_jsxs(_Fragment, { children: [hasMoreEvents && (_jsx("div", { className: "px-3 pt-3 flex justify-center", children: _jsx("button", { type: "button", onClick: () => void onLoadMoreEvents?.(), disabled: isLoadingMoreEvents, className: "h-8 px-3 text-xs rounded-md border transition-colors disabled:opacity-60 disabled:cursor-not-allowed", style: {
550
+ borderColor: 'var(--ds-gray-alpha-400)',
551
+ color: 'var(--ds-gray-900)',
552
+ backgroundColor: 'var(--ds-background-100)',
553
+ }, children: isLoadingMoreEvents
554
+ ? 'Loading more events...'
555
+ : 'Load more' }) })), _jsxs("div", { className: "mt-4 pt-3 border-t text-xs px-3", style: {
556
+ borderColor: 'var(--ds-gray-alpha-200)',
557
+ color: 'var(--ds-gray-900)',
558
+ }, children: [sortedEvents.length, " event", sortedEvents.length !== 1 ? 's' : '', " total"] })] })),
559
+ }, style: { flex: 1, minHeight: 0 } })] }));
177
560
  }
178
561
  //# sourceMappingURL=event-list-view.js.map