@workflow/web-shared 5.0.0-beta.0 → 5.0.0-beta.2

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 (106) hide show
  1. package/README.md +1 -1
  2. package/dist/components/error-boundary.js.map +1 -1
  3. package/dist/components/event-list-view.d.ts +3 -1
  4. package/dist/components/event-list-view.d.ts.map +1 -1
  5. package/dist/components/event-list-view.js +76 -70
  6. package/dist/components/event-list-view.js.map +1 -1
  7. package/dist/components/hook-actions.js.map +1 -1
  8. package/dist/components/index.d.ts +1 -1
  9. package/dist/components/index.d.ts.map +1 -1
  10. package/dist/components/index.js +1 -1
  11. package/dist/components/index.js.map +1 -1
  12. package/dist/components/run-trace-view.d.ts +2 -1
  13. package/dist/components/run-trace-view.d.ts.map +1 -1
  14. package/dist/components/run-trace-view.js +2 -2
  15. package/dist/components/run-trace-view.js.map +1 -1
  16. package/dist/components/sidebar/attribute-panel.d.ts +7 -1
  17. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  18. package/dist/components/sidebar/attribute-panel.js +36 -26
  19. package/dist/components/sidebar/attribute-panel.js.map +1 -1
  20. package/dist/components/sidebar/conversation-view.js.map +1 -1
  21. package/dist/components/sidebar/copyable-data-block.js.map +1 -1
  22. package/dist/components/sidebar/detail-card.js.map +1 -1
  23. package/dist/components/sidebar/entity-detail-panel.d.ts +5 -1
  24. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  25. package/dist/components/sidebar/entity-detail-panel.js +18 -16
  26. package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
  27. package/dist/components/sidebar/events-list.d.ts +1 -1
  28. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  29. package/dist/components/sidebar/events-list.js +26 -18
  30. package/dist/components/sidebar/events-list.js.map +1 -1
  31. package/dist/components/sidebar/resolve-hook-modal.js.map +1 -1
  32. package/dist/components/stream-viewer.js.map +1 -1
  33. package/dist/components/trace-viewer/components/map.js.map +1 -1
  34. package/dist/components/trace-viewer/components/markers.js.map +1 -1
  35. package/dist/components/trace-viewer/components/node.js.map +1 -1
  36. package/dist/components/trace-viewer/components/search-input.js.map +1 -1
  37. package/dist/components/trace-viewer/components/search.js.map +1 -1
  38. package/dist/components/trace-viewer/components/span-content.js.map +1 -1
  39. package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -1
  40. package/dist/components/trace-viewer/components/span-segments.js.map +1 -1
  41. package/dist/components/trace-viewer/components/span-strategies.js.map +1 -1
  42. package/dist/components/trace-viewer/components/ui.js.map +1 -1
  43. package/dist/components/trace-viewer/components/zoom-button.js.map +1 -1
  44. package/dist/components/trace-viewer/components/zoom-icons.js.map +1 -1
  45. package/dist/components/trace-viewer/context.js.map +1 -1
  46. package/dist/components/trace-viewer/index.js.map +1 -1
  47. package/dist/components/trace-viewer/trace-viewer.js.map +1 -1
  48. package/dist/components/trace-viewer/types.js.map +1 -1
  49. package/dist/components/trace-viewer/util/constants.js.map +1 -1
  50. package/dist/components/trace-viewer/util/scrollbar-width.js.map +1 -1
  51. package/dist/components/trace-viewer/util/timing.js.map +1 -1
  52. package/dist/components/trace-viewer/util/tree.js.map +1 -1
  53. package/dist/components/trace-viewer/util/use-immediate-style.js.map +1 -1
  54. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -1
  55. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +1 -1
  56. package/dist/components/trace-viewer/worker.js.map +1 -1
  57. package/dist/components/ui/alert.js.map +1 -1
  58. package/dist/components/ui/card.js.map +1 -1
  59. package/dist/components/ui/data-inspector.d.ts +17 -1
  60. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  61. package/dist/components/ui/data-inspector.js +116 -21
  62. package/dist/components/ui/data-inspector.js.map +1 -1
  63. package/dist/components/ui/decrypt-button.js.map +1 -1
  64. package/dist/components/ui/error-card.js.map +1 -1
  65. package/dist/components/ui/error-stack-block.js.map +1 -1
  66. package/dist/components/ui/inspector-theme.js.map +1 -1
  67. package/dist/components/ui/load-more-button.js.map +1 -1
  68. package/dist/components/ui/menu-dropdown.js.map +1 -1
  69. package/dist/components/ui/skeleton.js.map +1 -1
  70. package/dist/components/ui/spinner.js.map +1 -1
  71. package/dist/components/ui/timestamp-tooltip.js.map +1 -1
  72. package/dist/components/workflow-trace-view.d.ts +5 -1
  73. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  74. package/dist/components/workflow-trace-view.js +7 -2
  75. package/dist/components/workflow-trace-view.js.map +1 -1
  76. package/dist/components/workflow-traces/event-colors.js.map +1 -1
  77. package/dist/components/workflow-traces/trace-colors.js.map +1 -1
  78. package/dist/components/workflow-traces/trace-span-construction.js +1 -1
  79. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
  80. package/dist/components/workflow-traces/trace-time-utils.js.map +1 -1
  81. package/dist/hooks/use-dark-mode.js.map +1 -1
  82. package/dist/index.d.ts +1 -1
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +1 -1
  85. package/dist/index.js.map +1 -1
  86. package/dist/lib/event-analysis.js.map +1 -1
  87. package/dist/lib/event-materialization.js.map +1 -1
  88. package/dist/lib/hydration.d.ts +9 -2
  89. package/dist/lib/hydration.d.ts.map +1 -1
  90. package/dist/lib/hydration.js +43 -14
  91. package/dist/lib/hydration.js.map +1 -1
  92. package/dist/lib/toast.js.map +1 -1
  93. package/dist/lib/trace-builder.js.map +1 -1
  94. package/dist/lib/utils.js.map +1 -1
  95. package/package.json +4 -4
  96. package/src/components/event-list-view.tsx +207 -193
  97. package/src/components/index.ts +6 -1
  98. package/src/components/run-trace-view.tsx +3 -0
  99. package/src/components/sidebar/attribute-panel.tsx +160 -109
  100. package/src/components/sidebar/entity-detail-panel.tsx +111 -96
  101. package/src/components/sidebar/events-list.tsx +42 -32
  102. package/src/components/ui/data-inspector.tsx +205 -31
  103. package/src/components/workflow-trace-view.tsx +14 -0
  104. package/src/components/workflow-traces/trace-span-construction.ts +1 -1
  105. package/src/index.ts +1 -0
  106. package/src/lib/hydration.ts +50 -13
@@ -9,7 +9,7 @@ import { Virtuoso, type VirtuosoHandle } from 'react-virtuoso';
9
9
  import { isEncryptedMarker } from '../lib/hydration';
10
10
  import { DecryptButton } from './ui/decrypt-button';
11
11
  import { formatDuration } from '../lib/utils';
12
- import { DataInspector } from './ui/data-inspector';
12
+ import { DataInspector, DecryptClickContext } from './ui/data-inspector';
13
13
  import {
14
14
  ErrorStackBlock,
15
15
  isStructuredErrorWithStack,
@@ -703,6 +703,8 @@ interface EventsListProps {
703
703
  onDecrypt?: () => void;
704
704
  /** Whether the encryption key is currently being fetched. */
705
705
  isDecrypting?: boolean;
706
+ /** Run-level hint: the run contains encrypted data (from probe). */
707
+ hasEncryptedData?: boolean;
706
708
  }
707
709
 
708
710
  function EventRow({
@@ -801,7 +803,15 @@ function EventRow({
801
803
  selectedGroupRange !== null && index === selectedGroupRange.last;
802
804
 
803
805
  const loadEventDetails = useCallback(async () => {
804
- if (loadedEventData !== null || hasExistingEventData) {
806
+ if (loadedEventData !== null) {
807
+ return;
808
+ }
809
+ if (cachedEventData !== null) {
810
+ setLoadedEventData(cachedEventData);
811
+ setHasAttemptedLoad(true);
812
+ return;
813
+ }
814
+ if (isLoading) {
805
815
  return;
806
816
  }
807
817
  setIsLoading(true);
@@ -830,24 +840,20 @@ function EventRow({
830
840
  }, [
831
841
  event,
832
842
  loadedEventData,
833
- hasExistingEventData,
843
+ isLoading,
834
844
  onLoadEventData,
835
845
  onCacheEventData,
836
846
  encryptionKey,
837
847
  onEncryptedDataDetected,
848
+ cachedEventData,
838
849
  ]);
839
850
 
840
851
  // Auto-load event data when remounting in expanded state without cached data
841
852
  useEffect(() => {
842
- if (
843
- isExpanded &&
844
- loadedEventData === null &&
845
- !hasExistingEventData &&
846
- !isLoading &&
847
- !hasAttemptedLoad
848
- ) {
849
- loadEventDetails();
853
+ if (!isExpanded || isLoading) {
854
+ return;
850
855
  }
856
+ void loadEventDetails();
851
857
  // eslint-disable-next-line react-hooks/exhaustive-deps
852
858
  }, []);
853
859
 
@@ -875,8 +881,8 @@ function EventRow({
875
881
  const handleRowClick = useCallback(() => {
876
882
  onSelectGroup(rowGroupKey === selectedGroupKey ? undefined : rowGroupKey);
877
883
  onToggleExpand(event.eventId);
878
- if (!isExpanded && loadedEventData === null && !hasExistingEventData) {
879
- loadEventDetails();
884
+ if (!isExpanded) {
885
+ void loadEventDetails();
880
886
  }
881
887
  }, [
882
888
  selectedGroupKey,
@@ -885,14 +891,16 @@ function EventRow({
885
891
  onToggleExpand,
886
892
  event.eventId,
887
893
  isExpanded,
888
- loadedEventData,
889
- hasExistingEventData,
890
894
  loadEventDetails,
891
895
  ]);
892
896
 
893
- const eventData = hasExistingEventData
894
- ? (event as Event & { eventData: unknown }).eventData
895
- : loadedEventData;
897
+ const mergedEventData =
898
+ loadedEventData ??
899
+ (hasExistingEventData
900
+ ? (event as Event & { eventData: unknown }).eventData
901
+ : null);
902
+
903
+ const displayPayload = isLoading ? loadedEventData : mergedEventData;
896
904
 
897
905
  const contentOpacity = isDimmed ? 0.3 : 1;
898
906
 
@@ -1076,8 +1084,8 @@ function EventRow({
1076
1084
  )}
1077
1085
 
1078
1086
  {/* Payload */}
1079
- {eventData != null ? (
1080
- <PayloadBlock data={eventData} eventType={event.eventType} />
1087
+ {displayPayload != null ? (
1088
+ <PayloadBlock data={displayPayload} eventType={event.eventType} />
1081
1089
  ) : loadError ? (
1082
1090
  <div
1083
1091
  className="rounded-md border p-3 text-xs"
@@ -1090,7 +1098,7 @@ function EventRow({
1090
1098
  {loadError}
1091
1099
  </div>
1092
1100
  ) : isLoading ||
1093
- (!hasExistingEventData &&
1101
+ (loadedEventData === null &&
1094
1102
  !hasAttemptedLoad &&
1095
1103
  event.correlationId) ? (
1096
1104
  <div className="flex flex-col gap-2 p-3">
@@ -1130,6 +1138,7 @@ export function EventListView({
1130
1138
  onSortOrderChange,
1131
1139
  onDecrypt,
1132
1140
  isDecrypting = false,
1141
+ hasEncryptedData: hasEncryptedDataProp = false,
1133
1142
  }: EventsListProps) {
1134
1143
  const [internalSortOrder, setInternalSortOrder] = useState<'asc' | 'desc'>(
1135
1144
  'asc'
@@ -1174,7 +1183,8 @@ export function EventListView({
1174
1183
  setFoundEncryptedInLazyData(true);
1175
1184
  }, []);
1176
1185
 
1177
- const hasEncryptedData = hasEncryptedInlineData || foundEncryptedInLazyData;
1186
+ const hasEncryptedData =
1187
+ hasEncryptedDataProp || hasEncryptedInlineData || foundEncryptedInLazyData;
1178
1188
 
1179
1189
  const { correlationNameMap, workflowName } = useMemo(
1180
1190
  () => buildNameMaps(events ?? null, run ?? null),
@@ -1387,195 +1397,199 @@ export function EventListView({
1387
1397
  }
1388
1398
 
1389
1399
  return (
1390
- <div className="h-full flex flex-col overflow-hidden">
1391
- <style>{`@keyframes workflow-dot-pulse{0%{transform:scale(1);opacity:.7}70%,100%{transform:scale(2.2);opacity:0}}`}</style>
1392
- {/* Search bar + sort */}
1393
- <div
1394
- style={{
1395
- padding: 6,
1396
- backgroundColor: 'var(--ds-background-100)',
1397
- display: 'flex',
1398
- gap: 6,
1399
- }}
1400
- >
1401
- <label
1400
+ <DecryptClickContext.Provider
1401
+ value={onDecrypt ? { onDecrypt, isDecrypting } : undefined}
1402
+ >
1403
+ <div className="h-full flex flex-col overflow-hidden">
1404
+ <style>{`@keyframes workflow-dot-pulse{0%{transform:scale(1);opacity:.7}70%,100%{transform:scale(2.2);opacity:0}}`}</style>
1405
+ {/* Search bar + sort */}
1406
+ <div
1402
1407
  style={{
1408
+ padding: 6,
1409
+ backgroundColor: 'var(--ds-background-100)',
1403
1410
  display: 'flex',
1404
- alignItems: 'center',
1405
- justifyContent: 'center',
1406
- borderRadius: 6,
1407
- boxShadow: '0 0 0 1px var(--ds-gray-alpha-400)',
1408
- background: 'var(--ds-background-100)',
1409
- height: 40,
1410
- flex: 1,
1411
- minWidth: 0,
1411
+ gap: 6,
1412
1412
  }}
1413
1413
  >
1414
- <div
1414
+ <label
1415
1415
  style={{
1416
- width: 40,
1417
- height: 40,
1418
1416
  display: 'flex',
1419
1417
  alignItems: 'center',
1420
1418
  justifyContent: 'center',
1421
- color: 'var(--ds-gray-800)',
1422
- flexShrink: 0,
1419
+ borderRadius: 6,
1420
+ boxShadow: '0 0 0 1px var(--ds-gray-alpha-400)',
1421
+ background: 'var(--ds-background-100)',
1422
+ height: 40,
1423
+ flex: 1,
1424
+ minWidth: 0,
1423
1425
  }}
1424
1426
  >
1425
- <svg
1426
- width={16}
1427
- height={16}
1428
- viewBox="0 0 16 16"
1429
- fill="none"
1430
- aria-hidden="true"
1431
- focusable="false"
1427
+ <div
1428
+ style={{
1429
+ width: 40,
1430
+ height: 40,
1431
+ display: 'flex',
1432
+ alignItems: 'center',
1433
+ justifyContent: 'center',
1434
+ color: 'var(--ds-gray-800)',
1435
+ flexShrink: 0,
1436
+ }}
1432
1437
  >
1433
- <circle
1434
- cx="7"
1435
- cy="7"
1436
- r="4.5"
1437
- stroke="currentColor"
1438
- strokeWidth="1.5"
1439
- />
1440
- <path
1441
- d="M11.5 11.5L14 14"
1442
- stroke="currentColor"
1443
- strokeWidth="1.5"
1444
- strokeLinecap="round"
1445
- />
1446
- </svg>
1438
+ <svg
1439
+ width={16}
1440
+ height={16}
1441
+ viewBox="0 0 16 16"
1442
+ fill="none"
1443
+ aria-hidden="true"
1444
+ focusable="false"
1445
+ >
1446
+ <circle
1447
+ cx="7"
1448
+ cy="7"
1449
+ r="4.5"
1450
+ stroke="currentColor"
1451
+ strokeWidth="1.5"
1452
+ />
1453
+ <path
1454
+ d="M11.5 11.5L14 14"
1455
+ stroke="currentColor"
1456
+ strokeWidth="1.5"
1457
+ strokeLinecap="round"
1458
+ />
1459
+ </svg>
1460
+ </div>
1461
+ <input
1462
+ type="search"
1463
+ placeholder="Search by name, event type, or ID…"
1464
+ value={searchQuery}
1465
+ onChange={(e) => setSearchQuery(e.target.value)}
1466
+ style={{
1467
+ marginLeft: -16,
1468
+ paddingInline: 12,
1469
+ fontFamily: 'inherit',
1470
+ fontSize: 14,
1471
+ background: 'transparent',
1472
+ border: 'none',
1473
+ outline: 'none',
1474
+ height: 40,
1475
+ width: '100%',
1476
+ }}
1477
+ />
1478
+ </label>
1479
+ <MenuDropdown
1480
+ options={SORT_OPTIONS}
1481
+ value={effectiveSortOrder}
1482
+ onChange={handleSortOrderChange}
1483
+ />
1484
+ {(hasEncryptedData || encryptionKey) && onDecrypt && (
1485
+ <DecryptButton
1486
+ decrypted={!!encryptionKey}
1487
+ loading={isDecrypting}
1488
+ onClick={onDecrypt}
1489
+ />
1490
+ )}
1491
+ </div>
1492
+
1493
+ {/* Header */}
1494
+ <div
1495
+ className="flex items-center gap-0 text-[13px] font-medium h-10 border-b flex-shrink-0"
1496
+ style={{
1497
+ borderColor: 'var(--ds-gray-alpha-200)',
1498
+ color: 'var(--ds-gray-900)',
1499
+ backgroundColor: 'var(--ds-background-100)',
1500
+ }}
1501
+ >
1502
+ <div className="flex-shrink-0" style={{ width: GUTTER_WIDTH }} />
1503
+ <div className="w-5 flex-shrink-0" />
1504
+ <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1505
+ Time
1447
1506
  </div>
1448
- <input
1449
- type="search"
1450
- placeholder="Search by name, event type, or ID…"
1451
- value={searchQuery}
1452
- onChange={(e) => setSearchQuery(e.target.value)}
1453
- style={{
1454
- marginLeft: -16,
1455
- paddingInline: 12,
1456
- fontFamily: 'inherit',
1457
- fontSize: 14,
1458
- background: 'transparent',
1459
- border: 'none',
1460
- outline: 'none',
1461
- height: 40,
1462
- width: '100%',
1507
+ <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1508
+ Event Type
1509
+ </div>
1510
+ <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1511
+ Name
1512
+ </div>
1513
+ <div className="min-w-0 px-4" style={{ flex: '3 1 0%' }}>
1514
+ Correlation ID
1515
+ </div>
1516
+ <div className="min-w-0 px-4" style={{ flex: '3 1 0%' }}>
1517
+ Event ID
1518
+ </div>
1519
+ </div>
1520
+
1521
+ {/* Virtualized event rows or refetching skeleton */}
1522
+ {isRefetching ? (
1523
+ <RowsSkeleton />
1524
+ ) : (
1525
+ <Virtuoso
1526
+ ref={virtuosoRef}
1527
+ totalCount={sortedEvents.length}
1528
+ overscan={20}
1529
+ defaultItemHeight={40}
1530
+ endReached={() => {
1531
+ if (!hasMoreEvents || isLoadingMoreEvents) {
1532
+ return;
1533
+ }
1534
+ void onLoadMoreEvents?.();
1463
1535
  }}
1464
- />
1465
- </label>
1466
- <MenuDropdown
1467
- options={SORT_OPTIONS}
1468
- value={effectiveSortOrder}
1469
- onChange={handleSortOrderChange}
1470
- />
1471
- {(hasEncryptedData || encryptionKey) && onDecrypt && (
1472
- <DecryptButton
1473
- decrypted={!!encryptionKey}
1474
- loading={isDecrypting}
1475
- onClick={onDecrypt}
1536
+ itemContent={(index: number) => {
1537
+ const ev = sortedEvents[index];
1538
+ return (
1539
+ <EventRow
1540
+ event={ev}
1541
+ index={index}
1542
+ isFirst={index === 0}
1543
+ isLast={index === sortedEvents.length - 1}
1544
+ isExpanded={expandedEventIds.has(ev.eventId)}
1545
+ onToggleExpand={toggleEventExpanded}
1546
+ activeGroupKey={activeGroupKey}
1547
+ selectedGroupKey={selectedGroupKey}
1548
+ selectedGroupRange={selectedGroupRange}
1549
+ correlationNameMap={correlationNameMap}
1550
+ workflowName={workflowName}
1551
+ durationMap={durationMap}
1552
+ onSelectGroup={onSelectGroup}
1553
+ onHoverGroup={onHoverGroup}
1554
+ onLoadEventData={onLoadEventData}
1555
+ cachedEventData={
1556
+ eventDataCacheRef.current.get(ev.eventId) ?? null
1557
+ }
1558
+ onCacheEventData={cacheEventData}
1559
+ encryptionKey={encryptionKey}
1560
+ onEncryptedDataDetected={handleEncryptedDataDetected}
1561
+ />
1562
+ );
1563
+ }}
1564
+ style={{ flex: 1, minHeight: 0 }}
1476
1565
  />
1477
1566
  )}
1478
- </div>
1479
1567
 
1480
- {/* Header */}
1481
- <div
1482
- className="flex items-center gap-0 text-[13px] font-medium h-10 border-b flex-shrink-0"
1483
- style={{
1484
- borderColor: 'var(--ds-gray-alpha-200)',
1485
- color: 'var(--ds-gray-900)',
1486
- backgroundColor: 'var(--ds-background-100)',
1487
- }}
1488
- >
1489
- <div className="flex-shrink-0" style={{ width: GUTTER_WIDTH }} />
1490
- <div className="w-5 flex-shrink-0" />
1491
- <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1492
- Time
1493
- </div>
1494
- <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1495
- Event Type
1496
- </div>
1497
- <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1498
- Name
1499
- </div>
1500
- <div className="min-w-0 px-4" style={{ flex: '3 1 0%' }}>
1501
- Correlation ID
1502
- </div>
1503
- <div className="min-w-0 px-4" style={{ flex: '3 1 0%' }}>
1504
- Event ID
1505
- </div>
1506
- </div>
1507
-
1508
- {/* Virtualized event rows or refetching skeleton */}
1509
- {isRefetching ? (
1510
- <RowsSkeleton />
1511
- ) : (
1512
- <Virtuoso
1513
- ref={virtuosoRef}
1514
- totalCount={sortedEvents.length}
1515
- overscan={20}
1516
- defaultItemHeight={40}
1517
- endReached={() => {
1518
- if (!hasMoreEvents || isLoadingMoreEvents) {
1519
- return;
1520
- }
1521
- void onLoadMoreEvents?.();
1522
- }}
1523
- itemContent={(index: number) => {
1524
- const ev = sortedEvents[index];
1525
- return (
1526
- <EventRow
1527
- event={ev}
1528
- index={index}
1529
- isFirst={index === 0}
1530
- isLast={index === sortedEvents.length - 1}
1531
- isExpanded={expandedEventIds.has(ev.eventId)}
1532
- onToggleExpand={toggleEventExpanded}
1533
- activeGroupKey={activeGroupKey}
1534
- selectedGroupKey={selectedGroupKey}
1535
- selectedGroupRange={selectedGroupRange}
1536
- correlationNameMap={correlationNameMap}
1537
- workflowName={workflowName}
1538
- durationMap={durationMap}
1539
- onSelectGroup={onSelectGroup}
1540
- onHoverGroup={onHoverGroup}
1541
- onLoadEventData={onLoadEventData}
1542
- cachedEventData={
1543
- eventDataCacheRef.current.get(ev.eventId) ?? null
1544
- }
1545
- onCacheEventData={cacheEventData}
1546
- encryptionKey={encryptionKey}
1547
- onEncryptedDataDetected={handleEncryptedDataDetected}
1548
- />
1549
- );
1568
+ {/* Fixed footer — count + load more */}
1569
+ <div
1570
+ className="relative flex-shrink-0 flex items-center h-10 border-t px-4 text-xs"
1571
+ style={{
1572
+ borderColor: 'var(--ds-gray-alpha-200)',
1573
+ color: 'var(--ds-gray-900)',
1574
+ backgroundColor: 'var(--ds-background-100)',
1550
1575
  }}
1551
- style={{ flex: 1, minHeight: 0 }}
1552
- />
1553
- )}
1554
-
1555
- {/* Fixed footer — count + load more */}
1556
- <div
1557
- className="relative flex-shrink-0 flex items-center h-10 border-t px-4 text-xs"
1558
- style={{
1559
- borderColor: 'var(--ds-gray-alpha-200)',
1560
- color: 'var(--ds-gray-900)',
1561
- backgroundColor: 'var(--ds-background-100)',
1562
- }}
1563
- >
1564
- <span>
1565
- {sortedEvents.length} event
1566
- {sortedEvents.length !== 1 ? 's' : ''} loaded
1567
- </span>
1568
- {hasMoreEvents && (
1569
- <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
1570
- <div className="pointer-events-auto">
1571
- <LoadMoreButton
1572
- loading={isLoadingMoreEvents}
1573
- onClick={() => void onLoadMoreEvents?.()}
1574
- />
1576
+ >
1577
+ <span>
1578
+ {sortedEvents.length} event
1579
+ {sortedEvents.length !== 1 ? 's' : ''} loaded
1580
+ </span>
1581
+ {hasMoreEvents && (
1582
+ <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
1583
+ <div className="pointer-events-auto">
1584
+ <LoadMoreButton
1585
+ loading={isLoadingMoreEvents}
1586
+ onClick={() => void onLoadMoreEvents?.()}
1587
+ />
1588
+ </div>
1575
1589
  </div>
1576
- </div>
1577
- )}
1590
+ )}
1591
+ </div>
1578
1592
  </div>
1579
- </div>
1593
+ </DecryptClickContext.Provider>
1580
1594
  );
1581
1595
  }
@@ -21,7 +21,12 @@ export type {
21
21
  } from './sidebar/entity-detail-panel';
22
22
  export { type StreamChunk, StreamViewer } from './stream-viewer';
23
23
  export type { Span, SpanEvent } from './trace-viewer/types';
24
- export { DataInspector, type DataInspectorProps } from './ui/data-inspector';
24
+ export {
25
+ DataInspector,
26
+ type DataInspectorProps,
27
+ DecryptClickContext,
28
+ type DecryptClickContextValue,
29
+ } from './ui/data-inspector';
25
30
  export { DecryptButton } from './ui/decrypt-button';
26
31
  export { LoadMoreButton } from './ui/load-more-button';
27
32
  export { MenuDropdown, type MenuDropdownOption } from './ui/menu-dropdown';
@@ -24,6 +24,7 @@ interface RunTraceViewProps {
24
24
  ) => Promise<void>;
25
25
  onCancelRun?: (runId: string) => Promise<void>;
26
26
  onStreamClick?: (streamId: string) => void;
27
+ onRunClick?: (runId: string) => void;
27
28
  onSpanSelect?: (info: SpanSelectionInfo) => void;
28
29
  onLoadMoreSpans?: () => void | Promise<void>;
29
30
  hasMoreSpans?: boolean;
@@ -42,6 +43,7 @@ export function RunTraceView({
42
43
  onResolveHook,
43
44
  onCancelRun,
44
45
  onStreamClick,
46
+ onRunClick,
45
47
  onSpanSelect,
46
48
  onLoadMoreSpans,
47
49
  hasMoreSpans,
@@ -71,6 +73,7 @@ export function RunTraceView({
71
73
  onResolveHook={onResolveHook}
72
74
  onCancelRun={onCancelRun}
73
75
  onStreamClick={onStreamClick}
76
+ onRunClick={onRunClick}
74
77
  onSpanSelect={onSpanSelect}
75
78
  onLoadMoreSpans={onLoadMoreSpans}
76
79
  hasMoreSpans={hasMoreSpans}