@workflow/web-shared 4.1.0-beta.62 → 4.1.0-beta.64
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/README.md +4 -0
- package/dist/components/event-list-view.d.ts +9 -3
- package/dist/components/event-list-view.d.ts.map +1 -1
- package/dist/components/event-list-view.js +222 -98
- 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 +1 -3
- 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.map +1 -1
- package/dist/components/sidebar/attribute-panel.js +11 -1
- package/dist/components/sidebar/attribute-panel.js.map +1 -1
- package/dist/components/sidebar/detail-card.d.ts.map +1 -1
- package/dist/components/sidebar/detail-card.js +4 -2
- package/dist/components/sidebar/detail-card.js.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.d.ts +3 -3
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.js +43 -26
- package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.d.ts +7 -1
- package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.js +36 -11
- package/dist/components/trace-viewer/trace-viewer.js.map +1 -1
- package/dist/components/ui/error-stack-block.d.ts +3 -4
- package/dist/components/ui/error-stack-block.d.ts.map +1 -1
- package/dist/components/ui/error-stack-block.js +18 -9
- package/dist/components/ui/error-stack-block.js.map +1 -1
- package/dist/components/ui/menu-dropdown.d.ts +16 -0
- package/dist/components/ui/menu-dropdown.d.ts.map +1 -0
- package/dist/components/ui/menu-dropdown.js +50 -0
- package/dist/components/ui/menu-dropdown.js.map +1 -0
- package/dist/components/workflow-trace-view.d.ts +3 -3
- package/dist/components/workflow-trace-view.d.ts.map +1 -1
- package/dist/components/workflow-trace-view.js +31 -129
- package/dist/components/workflow-trace-view.js.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.d.ts +18 -5
- package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.js +65 -18
- package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/event-materialization.d.ts +72 -0
- package/dist/lib/event-materialization.d.ts.map +1 -0
- package/dist/lib/event-materialization.js +171 -0
- package/dist/lib/event-materialization.js.map +1 -0
- package/dist/lib/trace-builder.d.ts +32 -0
- package/dist/lib/trace-builder.d.ts.map +1 -0
- package/dist/lib/trace-builder.js +129 -0
- package/dist/lib/trace-builder.js.map +1 -0
- package/package.json +3 -3
- package/src/components/event-list-view.tsx +324 -103
- package/src/components/index.ts +1 -0
- package/src/components/run-trace-view.tsx +0 -6
- package/src/components/sidebar/attribute-panel.tsx +17 -2
- package/src/components/sidebar/detail-card.tsx +10 -2
- package/src/components/sidebar/entity-detail-panel.tsx +59 -21
- package/src/components/trace-viewer/trace-viewer.tsx +47 -2
- package/src/components/ui/error-stack-block.tsx +26 -16
- package/src/components/ui/menu-dropdown.tsx +114 -0
- package/src/components/workflow-trace-view.tsx +95 -195
- package/src/components/workflow-traces/trace-span-construction.ts +85 -32
- package/src/index.ts +13 -0
- package/src/lib/event-materialization.ts +243 -0
- package/src/lib/trace-builder.ts +201 -0
package/README.md
CHANGED
|
@@ -37,6 +37,10 @@ export default function MyRunDetailView({
|
|
|
37
37
|
Server actions and data fetching are intentionally **not** part of `web-shared`. Implement those in your app
|
|
38
38
|
and pass data + callbacks into these components. If you need world run helpers, use `@workflow/core/runtime`.
|
|
39
39
|
|
|
40
|
+
> **Security notice:** If you implement server-side data fetching using `@workflow/world-vercel` or similar backends,
|
|
41
|
+
> ensure that user-supplied IDs (runId, stepId, etc.) are validated before passing them to world functions.
|
|
42
|
+
> The server actions in `@workflow/web` do not include authentication — see that package's README for details on securing self-hosted deployments.
|
|
43
|
+
|
|
40
44
|
## Styling
|
|
41
45
|
|
|
42
46
|
In order for tailwind classes to be picked up correctly, you might need to configure your NextJS app
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { Event,
|
|
1
|
+
import type { Event, WorkflowRun } from '@workflow/world';
|
|
2
2
|
interface EventsListProps {
|
|
3
3
|
events: Event[] | null;
|
|
4
|
-
steps?: Step[] | null;
|
|
5
4
|
run?: WorkflowRun | null;
|
|
6
5
|
onLoadEventData?: (event: Event) => Promise<unknown | null>;
|
|
7
6
|
hasMoreEvents?: boolean;
|
|
@@ -9,7 +8,14 @@ interface EventsListProps {
|
|
|
9
8
|
onLoadMoreEvents?: () => Promise<void> | void;
|
|
10
9
|
/** When provided, signals that decryption is active (triggers re-load of expanded events) */
|
|
11
10
|
encryptionKey?: Uint8Array;
|
|
11
|
+
/** When true, shows a loading state instead of "No events found" for empty lists */
|
|
12
|
+
isLoading?: boolean;
|
|
13
|
+
/** Sort order for events. Defaults to 'asc'. */
|
|
14
|
+
sortOrder?: 'asc' | 'desc';
|
|
15
|
+
/** Called when the user changes sort order. When provided, the sort dropdown is shown
|
|
16
|
+
* and the parent is expected to refetch from the API with the new order. */
|
|
17
|
+
onSortOrderChange?: (order: 'asc' | 'desc') => void;
|
|
12
18
|
}
|
|
13
|
-
export declare function EventListView({ events,
|
|
19
|
+
export declare function EventListView({ events, run, onLoadEventData, hasMoreEvents, isLoadingMoreEvents, onLoadMoreEvents, encryptionKey, isLoading, sortOrder: sortOrderProp, onSortOrderChange, }: EventsListProps): import("react/jsx-runtime").JSX.Element;
|
|
14
20
|
export {};
|
|
15
21
|
//# sourceMappingURL=event-list-view.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-list-view.d.ts","sourceRoot":"","sources":["../../src/components/event-list-view.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"event-list-view.d.ts","sourceRoot":"","sources":["../../src/components/event-list-view.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAulB1D,UAAU,eAAe;IACvB,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACvB,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC5D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9C,6FAA6F;IAC7F,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,oFAAoF;IACpF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gDAAgD;IAChD,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC3B;iFAC6E;IAC7E,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC;CACrD;AAyYD,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,GAAG,EACH,eAAe,EACf,aAAqB,EACrB,mBAA2B,EAC3B,gBAAgB,EAChB,aAAa,EACb,SAAiB,EACjB,SAAS,EAAE,aAAa,EACxB,iBAAiB,GAClB,EAAE,eAAe,2CA4ajB"}
|
|
@@ -7,6 +7,7 @@ import { Virtuoso } from 'react-virtuoso';
|
|
|
7
7
|
import { formatDuration } from '../lib/utils';
|
|
8
8
|
import { DataInspector } from './ui/data-inspector';
|
|
9
9
|
import { ErrorStackBlock, isStructuredErrorWithStack, } from './ui/error-stack-block';
|
|
10
|
+
import { MenuDropdown } from './ui/menu-dropdown';
|
|
10
11
|
import { Skeleton } from './ui/skeleton';
|
|
11
12
|
/**
|
|
12
13
|
* Event types whose eventData contains an error field with a StructuredError.
|
|
@@ -81,16 +82,19 @@ function getStatusDotColor(eventType) {
|
|
|
81
82
|
return 'var(--ds-gray-600)';
|
|
82
83
|
}
|
|
83
84
|
/**
|
|
84
|
-
* Build a map from correlationId (stepId) → display name using
|
|
85
|
-
* and parse the workflow name from the run.
|
|
85
|
+
* Build a map from correlationId (stepId) → display name using step_created
|
|
86
|
+
* events, and parse the workflow name from the run.
|
|
86
87
|
*/
|
|
87
|
-
function buildNameMaps(
|
|
88
|
+
function buildNameMaps(events, run) {
|
|
88
89
|
const correlationNameMap = new Map();
|
|
89
|
-
// Map step correlationId (= stepId) → parsed step name
|
|
90
|
-
if (
|
|
91
|
-
for (const
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
// Map step correlationId (= stepId) → parsed step name from step_created events
|
|
91
|
+
if (events) {
|
|
92
|
+
for (const event of events) {
|
|
93
|
+
if (event.eventType === 'step_created' && event.correlationId) {
|
|
94
|
+
const stepName = event.eventData?.stepName ?? '';
|
|
95
|
+
const parsed = parseStepName(String(stepName));
|
|
96
|
+
correlationNameMap.set(event.correlationId, parsed?.shortName ?? stepName);
|
|
97
|
+
}
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
// Parse workflow name from run
|
|
@@ -237,7 +241,7 @@ function TreeGutter({ isFirst, isLast, isRunLevel: isRun, statusDotColor, pulse
|
|
|
237
241
|
// ──────────────────────────────────────────────────────────────────────────
|
|
238
242
|
// Copyable cell — shows a copy button on hover
|
|
239
243
|
// ──────────────────────────────────────────────────────────────────────────
|
|
240
|
-
function CopyableCell({ value, className, }) {
|
|
244
|
+
function CopyableCell({ value, className, style: styleProp, }) {
|
|
241
245
|
const [copied, setCopied] = useState(false);
|
|
242
246
|
const resetCopiedTimeoutRef = useRef(null);
|
|
243
247
|
useEffect(() => {
|
|
@@ -260,7 +264,7 @@ function CopyableCell({ value, className, }) {
|
|
|
260
264
|
}, 1500);
|
|
261
265
|
});
|
|
262
266
|
}, [value]);
|
|
263
|
-
return (_jsxs("div", { className: `group/copy flex items-center gap-1
|
|
267
|
+
return (_jsxs("div", { className: `group/copy flex items-center gap-1 min-w-0 px-4 ${className ?? ''}`, style: styleProp, 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] }));
|
|
264
268
|
}
|
|
265
269
|
/** Recursively parse stringified JSON values so escaped slashes / quotes are cleaned up */
|
|
266
270
|
function deepParseJson(value) {
|
|
@@ -351,20 +355,21 @@ function PayloadBlock({ data, eventType, }) {
|
|
|
351
355
|
}
|
|
352
356
|
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" })] })) })] }));
|
|
353
357
|
}
|
|
354
|
-
|
|
355
|
-
|
|
358
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
359
|
+
// Sort options for the events list
|
|
360
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
361
|
+
const SORT_OPTIONS = [
|
|
362
|
+
{ value: 'desc', label: 'Newest' },
|
|
363
|
+
{ value: 'asc', label: 'Oldest' },
|
|
364
|
+
];
|
|
365
|
+
function EventRow({ event, index, isFirst, isLast, isExpanded, onToggleExpand, activeGroupKey, selectedGroupKey, selectedGroupRange, correlationNameMap, workflowName, durationMap, onSelectGroup, onHoverGroup, onLoadEventData, cachedEventData, onCacheEventData, encryptionKey, }) {
|
|
356
366
|
const [isLoading, setIsLoading] = useState(false);
|
|
357
|
-
const [loadedEventData, setLoadedEventData] = useState(
|
|
367
|
+
const [loadedEventData, setLoadedEventData] = useState(cachedEventData);
|
|
358
368
|
const [loadError, setLoadError] = useState(null);
|
|
359
|
-
const [hasAttemptedLoad, setHasAttemptedLoad] = useState(
|
|
360
|
-
const rowGroupKey = event.
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
useEffect(() => {
|
|
364
|
-
if (selectedGroupKey !== undefined && selectedGroupKey !== rowGroupKey) {
|
|
365
|
-
setIsExpanded(false);
|
|
366
|
-
}
|
|
367
|
-
}, [selectedGroupKey, rowGroupKey]);
|
|
369
|
+
const [hasAttemptedLoad, setHasAttemptedLoad] = useState(cachedEventData !== null);
|
|
370
|
+
const rowGroupKey = isRunLevel(event.eventType)
|
|
371
|
+
? '__run__'
|
|
372
|
+
: (event.correlationId ?? undefined);
|
|
368
373
|
const statusDotColor = getStatusDotColor(event.eventType);
|
|
369
374
|
const createdAt = new Date(event.createdAt);
|
|
370
375
|
const hasExistingEventData = 'eventData' in event && event.eventData != null;
|
|
@@ -398,9 +403,10 @@ function EventRow({ event, index, isFirst, isLast, activeGroupKey, selectedGroup
|
|
|
398
403
|
setLoadError('Event details unavailable');
|
|
399
404
|
return;
|
|
400
405
|
}
|
|
401
|
-
const
|
|
402
|
-
if (
|
|
403
|
-
setLoadedEventData(
|
|
406
|
+
const data = await onLoadEventData(event);
|
|
407
|
+
if (data !== null && data !== undefined) {
|
|
408
|
+
setLoadedEventData(data);
|
|
409
|
+
onCacheEventData(event.eventId, data);
|
|
404
410
|
}
|
|
405
411
|
}
|
|
406
412
|
catch (err) {
|
|
@@ -410,7 +416,24 @@ function EventRow({ event, index, isFirst, isLast, activeGroupKey, selectedGroup
|
|
|
410
416
|
setIsLoading(false);
|
|
411
417
|
setHasAttemptedLoad(true);
|
|
412
418
|
}
|
|
413
|
-
}, [
|
|
419
|
+
}, [
|
|
420
|
+
event,
|
|
421
|
+
loadedEventData,
|
|
422
|
+
hasExistingEventData,
|
|
423
|
+
onLoadEventData,
|
|
424
|
+
onCacheEventData,
|
|
425
|
+
]);
|
|
426
|
+
// Auto-load event data when remounting in expanded state without cached data
|
|
427
|
+
useEffect(() => {
|
|
428
|
+
if (isExpanded &&
|
|
429
|
+
loadedEventData === null &&
|
|
430
|
+
!hasExistingEventData &&
|
|
431
|
+
!isLoading &&
|
|
432
|
+
!hasAttemptedLoad) {
|
|
433
|
+
loadEventDetails();
|
|
434
|
+
}
|
|
435
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
436
|
+
}, []);
|
|
414
437
|
// When encryption key changes and this event was previously loaded,
|
|
415
438
|
// re-load to get decrypted data
|
|
416
439
|
useEffect(() => {
|
|
@@ -421,6 +444,7 @@ function EventRow({ event, index, isFirst, isLast, activeGroupKey, selectedGroup
|
|
|
421
444
|
.then((data) => {
|
|
422
445
|
if (data !== null && data !== undefined) {
|
|
423
446
|
setLoadedEventData(data);
|
|
447
|
+
onCacheEventData(event.eventId, data);
|
|
424
448
|
}
|
|
425
449
|
setHasAttemptedLoad(true);
|
|
426
450
|
})
|
|
@@ -430,22 +454,23 @@ function EventRow({ event, index, isFirst, isLast, activeGroupKey, selectedGroup
|
|
|
430
454
|
}
|
|
431
455
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
432
456
|
}, [encryptionKey]);
|
|
433
|
-
const handleExpandToggle = useCallback((e) => {
|
|
434
|
-
e.stopPropagation();
|
|
435
|
-
const newExpanded = !isExpanded;
|
|
436
|
-
setIsExpanded(newExpanded);
|
|
437
|
-
if (newExpanded && loadedEventData === null && !hasExistingEventData) {
|
|
438
|
-
loadEventDetails();
|
|
439
|
-
}
|
|
440
|
-
}, [isExpanded, loadedEventData, hasExistingEventData, loadEventDetails]);
|
|
441
457
|
const handleRowClick = useCallback(() => {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
onSelectGroup(rowGroupKey);
|
|
458
|
+
onSelectGroup(rowGroupKey === selectedGroupKey ? undefined : rowGroupKey);
|
|
459
|
+
onToggleExpand(event.eventId);
|
|
460
|
+
if (!isExpanded && loadedEventData === null && !hasExistingEventData) {
|
|
461
|
+
loadEventDetails();
|
|
447
462
|
}
|
|
448
|
-
}, [
|
|
463
|
+
}, [
|
|
464
|
+
selectedGroupKey,
|
|
465
|
+
rowGroupKey,
|
|
466
|
+
onSelectGroup,
|
|
467
|
+
onToggleExpand,
|
|
468
|
+
event.eventId,
|
|
469
|
+
isExpanded,
|
|
470
|
+
loadedEventData,
|
|
471
|
+
hasExistingEventData,
|
|
472
|
+
loadEventDetails,
|
|
473
|
+
]);
|
|
449
474
|
const eventData = hasExistingEventData
|
|
450
475
|
? event.eventData
|
|
451
476
|
: loadedEventData;
|
|
@@ -453,13 +478,12 @@ function EventRow({ event, index, isFirst, isLast, activeGroupKey, selectedGroup
|
|
|
453
478
|
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) => {
|
|
454
479
|
if (e.key === 'Enter' || e.key === ' ')
|
|
455
480
|
handleRowClick();
|
|
456
|
-
}, className: "w-full text-left flex items-center gap-0 text-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
color: 'var(--ds-gray-700)',
|
|
481
|
+
}, className: "w-full text-left flex items-center gap-0 text-[13px] 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("div", { className: "flex items-center justify-center w-5 h-5 flex-shrink-0 rounded", style: {
|
|
482
|
+
border: '1px solid var(--ds-gray-400)',
|
|
483
|
+
}, children: _jsx(ChevronRight, { className: "h-3 w-3 transition-transform", style: {
|
|
484
|
+
color: 'var(--ds-gray-900)',
|
|
461
485
|
transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
|
|
462
|
-
} }) }), _jsx("div", { className: "
|
|
486
|
+
} }) }), _jsx("div", { className: "tabular-nums min-w-0 px-4", style: { color: 'var(--ds-gray-900)', flex: '2 1 0%' }, children: formatEventTime(createdAt) }), _jsx("div", { className: "font-medium min-w-0 px-4", style: { flex: '2 1 0%' }, children: _jsxs("span", { className: "inline-flex items-center gap-1.5", style: { color: 'var(--ds-gray-900)' }, children: [_jsxs("span", { style: {
|
|
463
487
|
position: 'relative',
|
|
464
488
|
display: 'inline-flex',
|
|
465
489
|
width: 6,
|
|
@@ -478,7 +502,7 @@ function EventRow({ event, index, isFirst, isLast, activeGroupKey, selectedGroup
|
|
|
478
502
|
height: 6,
|
|
479
503
|
borderRadius: '50%',
|
|
480
504
|
backgroundColor: statusDotColor,
|
|
481
|
-
} })] }), formatEventType(event.eventType)] }) }), _jsx("div", { className: "
|
|
505
|
+
} })] }), formatEventType(event.eventType)] }) }), _jsx("div", { className: "min-w-0 px-4 overflow-hidden text-ellipsis whitespace-nowrap", style: { flex: '2 1 0%' }, title: eventName !== '-' ? eventName : undefined, children: eventName }), _jsx(CopyableCell, { value: event.correlationId || '', className: "font-mono", style: { flex: '3 1 0%' } }), _jsx(CopyableCell, { value: event.eventId, className: "font-mono", style: { flex: '3 1 0%' } })] })] }), 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: {
|
|
482
506
|
borderColor: 'var(--ds-gray-alpha-200)',
|
|
483
507
|
opacity: contentOpacity,
|
|
484
508
|
transition: 'opacity 150ms',
|
|
@@ -496,13 +520,25 @@ function EventRow({ event, index, isFirst, isLast, activeGroupKey, selectedGroup
|
|
|
496
520
|
// ──────────────────────────────────────────────────────────────────────────
|
|
497
521
|
// Main component
|
|
498
522
|
// ──────────────────────────────────────────────────────────────────────────
|
|
499
|
-
export function EventListView({ events,
|
|
523
|
+
export function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, isLoadingMoreEvents = false, onLoadMoreEvents, encryptionKey, isLoading = false, sortOrder: sortOrderProp, onSortOrderChange, }) {
|
|
524
|
+
const [internalSortOrder, setInternalSortOrder] = useState('asc');
|
|
525
|
+
const effectiveSortOrder = sortOrderProp ?? internalSortOrder;
|
|
526
|
+
const handleSortOrderChange = useCallback((order) => {
|
|
527
|
+
if (onSortOrderChange) {
|
|
528
|
+
onSortOrderChange(order);
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
setInternalSortOrder(order);
|
|
532
|
+
}
|
|
533
|
+
}, [onSortOrderChange]);
|
|
500
534
|
const sortedEvents = useMemo(() => {
|
|
501
535
|
if (!events || events.length === 0)
|
|
502
536
|
return [];
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
537
|
+
const dir = effectiveSortOrder === 'desc' ? -1 : 1;
|
|
538
|
+
return [...events].sort((a, b) => dir *
|
|
539
|
+
(new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()));
|
|
540
|
+
}, [events, effectiveSortOrder]);
|
|
541
|
+
const { correlationNameMap, workflowName } = useMemo(() => buildNameMaps(events ?? null, run ?? null), [events, run]);
|
|
506
542
|
const durationMap = useMemo(() => buildDurationMap(sortedEvents), [sortedEvents]);
|
|
507
543
|
const [selectedGroupKey, setSelectedGroupKey] = useState(undefined);
|
|
508
544
|
const [hoveredGroupKey, setHoveredGroupKey] = useState(undefined);
|
|
@@ -513,6 +549,57 @@ export function EventListView({ events, steps, run, onLoadEventData, hasMoreEven
|
|
|
513
549
|
setHoveredGroupKey(groupKey);
|
|
514
550
|
}, []);
|
|
515
551
|
const activeGroupKey = selectedGroupKey ?? hoveredGroupKey;
|
|
552
|
+
// Expanded state lifted out of EventRow so it survives virtualization
|
|
553
|
+
const [expandedEventIds, setExpandedEventIds] = useState(() => new Set());
|
|
554
|
+
const toggleEventExpanded = useCallback((eventId) => {
|
|
555
|
+
setExpandedEventIds((prev) => {
|
|
556
|
+
const next = new Set(prev);
|
|
557
|
+
if (next.has(eventId)) {
|
|
558
|
+
next.delete(eventId);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
next.add(eventId);
|
|
562
|
+
}
|
|
563
|
+
return next;
|
|
564
|
+
});
|
|
565
|
+
}, []);
|
|
566
|
+
// Event data cache — ref avoids re-renders when cache updates
|
|
567
|
+
const eventDataCacheRef = useRef(new Map());
|
|
568
|
+
const cacheEventData = useCallback((eventId, data) => {
|
|
569
|
+
eventDataCacheRef.current.set(eventId, data);
|
|
570
|
+
}, []);
|
|
571
|
+
// Lookup from eventId → groupKey for efficient collapse filtering
|
|
572
|
+
const eventGroupKeyMap = useMemo(() => {
|
|
573
|
+
const map = new Map();
|
|
574
|
+
for (const ev of sortedEvents) {
|
|
575
|
+
const gk = isRunLevel(ev.eventType)
|
|
576
|
+
? '__run__'
|
|
577
|
+
: (ev.correlationId ?? '');
|
|
578
|
+
if (gk)
|
|
579
|
+
map.set(ev.eventId, gk);
|
|
580
|
+
}
|
|
581
|
+
return map;
|
|
582
|
+
}, [sortedEvents]);
|
|
583
|
+
// Collapse expanded events that don't belong to the newly selected group
|
|
584
|
+
useEffect(() => {
|
|
585
|
+
if (selectedGroupKey === undefined)
|
|
586
|
+
return;
|
|
587
|
+
setExpandedEventIds((prev) => {
|
|
588
|
+
if (prev.size === 0)
|
|
589
|
+
return prev;
|
|
590
|
+
let changed = false;
|
|
591
|
+
const next = new Set();
|
|
592
|
+
for (const eventId of prev) {
|
|
593
|
+
if (eventGroupKeyMap.get(eventId) === selectedGroupKey) {
|
|
594
|
+
next.add(eventId);
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
changed = true;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return changed ? next : prev;
|
|
601
|
+
});
|
|
602
|
+
}, [selectedGroupKey, eventGroupKeyMap]);
|
|
516
603
|
// Compute the row-index range for the active group's connecting lane line.
|
|
517
604
|
// Only applies to non-run groups (step/hook/wait correlations).
|
|
518
605
|
const selectedGroupRange = useMemo(() => {
|
|
@@ -535,83 +622,120 @@ export function EventListView({ events, steps, run, onLoadEventData, hasMoreEven
|
|
|
535
622
|
const entries = [];
|
|
536
623
|
for (let i = 0; i < sortedEvents.length; i++) {
|
|
537
624
|
const ev = sortedEvents[i];
|
|
625
|
+
const isRun = isRunLevel(ev.eventType);
|
|
626
|
+
const name = isRun
|
|
627
|
+
? (workflowName ?? '')
|
|
628
|
+
: ev.correlationId
|
|
629
|
+
? (correlationNameMap.get(ev.correlationId) ?? '')
|
|
630
|
+
: '';
|
|
538
631
|
entries.push({
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
632
|
+
fields: [
|
|
633
|
+
ev.eventId,
|
|
634
|
+
ev.correlationId ?? '',
|
|
635
|
+
ev.eventType,
|
|
636
|
+
formatEventType(ev.eventType),
|
|
637
|
+
name,
|
|
638
|
+
].map((f) => f.toLowerCase()),
|
|
639
|
+
groupKey: ev.correlationId ?? (isRun ? '__run__' : undefined),
|
|
542
640
|
eventId: ev.eventId,
|
|
543
641
|
index: i,
|
|
544
642
|
});
|
|
545
643
|
}
|
|
546
644
|
return entries;
|
|
547
|
-
}, [sortedEvents]);
|
|
645
|
+
}, [sortedEvents, correlationNameMap, workflowName]);
|
|
548
646
|
useEffect(() => {
|
|
549
647
|
const q = searchQuery.trim().toLowerCase();
|
|
550
648
|
if (!q) {
|
|
551
649
|
setSelectedGroupKey(undefined);
|
|
552
650
|
return;
|
|
553
651
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
652
|
+
let bestMatch = null;
|
|
653
|
+
let bestScore = 0;
|
|
654
|
+
for (const entry of searchIndex) {
|
|
655
|
+
for (const field of entry.fields) {
|
|
656
|
+
if (field && field.includes(q)) {
|
|
657
|
+
const score = q.length / field.length;
|
|
658
|
+
if (score > bestScore) {
|
|
659
|
+
bestScore = score;
|
|
660
|
+
bestMatch = entry;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (bestMatch) {
|
|
666
|
+
setSelectedGroupKey(bestMatch.groupKey);
|
|
557
667
|
virtuosoRef.current?.scrollToIndex({
|
|
558
|
-
index:
|
|
668
|
+
index: bestMatch.index,
|
|
559
669
|
align: 'center',
|
|
560
670
|
behavior: 'smooth',
|
|
561
671
|
});
|
|
562
672
|
}
|
|
563
673
|
}, [searchQuery, searchIndex]);
|
|
564
674
|
if (!events || events.length === 0) {
|
|
675
|
+
if (isLoading) {
|
|
676
|
+
return (_jsxs("div", { className: "h-full flex flex-col overflow-hidden", children: [_jsx("div", { style: { padding: 6 }, children: _jsx(Skeleton, { style: { height: 40, borderRadius: 6 } }) }), _jsxs("div", { className: "flex items-center gap-0 h-10 border-b flex-shrink-0 px-4", style: { borderColor: 'var(--ds-gray-alpha-200)' }, children: [_jsx(Skeleton, { className: "h-3", style: { width: 60 } }), _jsx("div", { style: { flex: 1 } }), _jsx(Skeleton, { className: "h-3", style: { width: 80 } }), _jsx("div", { style: { flex: 1 } }), _jsx(Skeleton, { className: "h-3", style: { width: 50 } }), _jsx("div", { style: { flex: 1 } }), _jsx(Skeleton, { className: "h-3", style: { width: 90 } }), _jsx("div", { style: { flex: 1 } }), _jsx(Skeleton, { className: "h-3", style: { width: 70 } })] }), _jsx("div", { className: "flex-1 overflow-hidden", children: Array.from({ length: 8 }, (_, i) => (_jsxs("div", { className: "flex items-center gap-3 px-4", style: { height: 40 }, children: [_jsx(Skeleton, { className: "h-2 w-2 flex-shrink-0", style: { borderRadius: '50%' } }), _jsx(Skeleton, { className: "h-3", style: { width: 90 } }), _jsx(Skeleton, { className: "h-3", style: { width: 100 } }), _jsx(Skeleton, { className: "h-3", style: { width: 80 } }), _jsx(Skeleton, { className: "h-3 flex-1" }), _jsx(Skeleton, { className: "h-3 flex-1" })] }, i))) })] }));
|
|
677
|
+
}
|
|
565
678
|
return (_jsx("div", { className: "flex items-center justify-center h-full text-sm", style: { color: 'var(--ds-gray-700)' }, children: "No events found" }));
|
|
566
679
|
}
|
|
567
|
-
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}}` }),
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
680
|
+
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}}` }), _jsxs("div", { style: {
|
|
681
|
+
padding: 6,
|
|
682
|
+
backgroundColor: 'var(--ds-background-100)',
|
|
683
|
+
display: 'flex',
|
|
684
|
+
gap: 6,
|
|
685
|
+
}, children: [_jsxs("label", { style: {
|
|
686
|
+
display: 'flex',
|
|
687
|
+
alignItems: 'center',
|
|
688
|
+
justifyContent: 'center',
|
|
689
|
+
borderRadius: 6,
|
|
690
|
+
boxShadow: '0 0 0 1px var(--ds-gray-alpha-400)',
|
|
691
|
+
background: 'var(--ds-background-100)',
|
|
692
|
+
height: 40,
|
|
693
|
+
flex: 1,
|
|
694
|
+
minWidth: 0,
|
|
695
|
+
}, children: [_jsx("div", { style: {
|
|
696
|
+
width: 40,
|
|
697
|
+
height: 40,
|
|
698
|
+
display: 'flex',
|
|
699
|
+
alignItems: 'center',
|
|
700
|
+
justifyContent: 'center',
|
|
701
|
+
color: 'var(--ds-gray-800)',
|
|
702
|
+
flexShrink: 0,
|
|
703
|
+
}, 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 name, event type, or ID\u2026", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: {
|
|
704
|
+
marginLeft: -16,
|
|
705
|
+
paddingInline: 12,
|
|
706
|
+
fontFamily: 'inherit',
|
|
707
|
+
fontSize: 14,
|
|
708
|
+
background: 'transparent',
|
|
709
|
+
border: 'none',
|
|
710
|
+
outline: 'none',
|
|
711
|
+
height: 40,
|
|
712
|
+
width: '100%',
|
|
713
|
+
} })] }), _jsx(MenuDropdown, { options: SORT_OPTIONS, value: effectiveSortOrder, onChange: handleSortOrderChange })] }), _jsxs("div", { className: "flex items-center gap-0 text-[13px] font-medium h-10 border-b flex-shrink-0", style: {
|
|
594
714
|
borderColor: 'var(--ds-gray-alpha-200)',
|
|
595
715
|
color: 'var(--ds-gray-900)',
|
|
596
716
|
backgroundColor: 'var(--ds-background-100)',
|
|
597
|
-
}, children: [_jsx("div", { className: "flex-shrink-0", style: { width: GUTTER_WIDTH } }), _jsx("div", { className: "w-5 flex-shrink-0" }), _jsx("div", { className: "
|
|
717
|
+
}, children: [_jsx("div", { className: "flex-shrink-0", style: { width: GUTTER_WIDTH } }), _jsx("div", { className: "w-5 flex-shrink-0" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: "Time" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: "Event Type" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: "Name" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '3 1 0%' }, children: "Correlation ID" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '3 1 0%' }, children: "Event ID" })] }), _jsx(Virtuoso, { ref: virtuosoRef, totalCount: sortedEvents.length, overscan: 20, defaultItemHeight: 40, endReached: () => {
|
|
598
718
|
if (!hasMoreEvents || isLoadingMoreEvents) {
|
|
599
719
|
return;
|
|
600
720
|
}
|
|
601
721
|
void onLoadMoreEvents?.();
|
|
602
722
|
}, itemContent: (index) => {
|
|
603
|
-
|
|
723
|
+
const ev = sortedEvents[index];
|
|
724
|
+
return (_jsx(EventRow, { event: ev, index: index, isFirst: index === 0, isLast: index === sortedEvents.length - 1, isExpanded: expandedEventIds.has(ev.eventId), onToggleExpand: toggleEventExpanded, activeGroupKey: activeGroupKey, selectedGroupKey: selectedGroupKey, selectedGroupRange: selectedGroupRange, correlationNameMap: correlationNameMap, workflowName: workflowName, durationMap: durationMap, onSelectGroup: onSelectGroup, onHoverGroup: onHoverGroup, onLoadEventData: onLoadEventData, cachedEventData: eventDataCacheRef.current.get(ev.eventId) ?? null, onCacheEventData: cacheEventData, encryptionKey: encryptionKey }));
|
|
604
725
|
}, components: {
|
|
605
|
-
Footer:
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
backgroundColor: 'var(--ds-background-100)',
|
|
609
|
-
}, children: isLoadingMoreEvents
|
|
610
|
-
? 'Loading more events...'
|
|
611
|
-
: 'Load more' }) })), _jsxs("div", { className: "mt-4 pt-3 border-t text-xs px-3", style: {
|
|
612
|
-
borderColor: 'var(--ds-gray-alpha-200)',
|
|
726
|
+
Footer: hasMoreEvents
|
|
727
|
+
? () => (_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: {
|
|
728
|
+
borderColor: 'var(--ds-gray-alpha-400)',
|
|
613
729
|
color: 'var(--ds-gray-900)',
|
|
614
|
-
|
|
615
|
-
|
|
730
|
+
backgroundColor: 'var(--ds-background-100)',
|
|
731
|
+
}, children: isLoadingMoreEvents
|
|
732
|
+
? 'Loading more events...'
|
|
733
|
+
: 'Load more' }) }))
|
|
734
|
+
: undefined,
|
|
735
|
+
}, style: { flex: 1, minHeight: 0 } }), _jsxs("div", { className: "flex-shrink-0 border-t text-xs px-3 py-2", style: {
|
|
736
|
+
borderColor: 'var(--ds-gray-alpha-200)',
|
|
737
|
+
color: 'var(--ds-gray-900)',
|
|
738
|
+
backgroundColor: 'var(--ds-background-100)',
|
|
739
|
+
}, children: [sortedEvents.length, " event", sortedEvents.length !== 1 ? 's' : '', " total"] })] }));
|
|
616
740
|
}
|
|
617
741
|
//# sourceMappingURL=event-list-view.js.map
|