@workflow/web-shared 5.0.0-beta.1 → 5.0.0-beta.3

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 (194) 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 +53 -53
  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 +3 -1
  9. package/dist/components/index.d.ts.map +1 -1
  10. package/dist/components/index.js +3 -1
  11. package/dist/components/index.js.map +1 -1
  12. package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
  13. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
  14. package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
  15. package/dist/components/new-trace-viewer/components/copy-button.js.map +1 -0
  16. package/dist/components/new-trace-viewer/components/event-list.d.ts +8 -0
  17. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
  18. package/dist/components/new-trace-viewer/components/event-list.js +35 -0
  19. package/dist/components/new-trace-viewer/components/event-list.js.map +1 -0
  20. package/dist/components/new-trace-viewer/components/split-pane.d.ts +14 -0
  21. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
  22. package/dist/components/new-trace-viewer/components/split-pane.js +100 -0
  23. package/dist/components/new-trace-viewer/components/split-pane.js.map +1 -0
  24. package/dist/components/new-trace-viewer/components/timeline.d.ts +32 -0
  25. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
  26. package/dist/components/new-trace-viewer/components/timeline.js +134 -0
  27. package/dist/components/new-trace-viewer/components/timeline.js.map +1 -0
  28. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  29. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  30. package/dist/components/new-trace-viewer/context.js +49 -0
  31. package/dist/components/new-trace-viewer/context.js.map +1 -0
  32. package/dist/components/new-trace-viewer/detail-panel.d.ts +10 -0
  33. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -0
  34. package/dist/components/new-trace-viewer/detail-panel.js +11 -0
  35. package/dist/components/new-trace-viewer/detail-panel.js.map +1 -0
  36. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  37. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  38. package/dist/components/new-trace-viewer/icons.js +15 -0
  39. package/dist/components/new-trace-viewer/icons.js.map +1 -0
  40. package/dist/components/new-trace-viewer/trace-viewer.d.ts +8 -0
  41. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  42. package/dist/components/new-trace-viewer/trace-viewer.js +329 -0
  43. package/dist/components/new-trace-viewer/trace-viewer.js.map +1 -0
  44. package/dist/components/new-trace-viewer/types.d.ts +1 -0
  45. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  46. package/dist/components/new-trace-viewer/types.js +2 -0
  47. package/dist/components/new-trace-viewer/types.js.map +1 -0
  48. package/dist/components/new-trace-viewer/util/constants.d.ts +1 -0
  49. package/dist/components/new-trace-viewer/util/constants.d.ts.map +1 -0
  50. package/dist/components/new-trace-viewer/util/constants.js +2 -0
  51. package/dist/components/new-trace-viewer/util/constants.js.map +1 -0
  52. package/dist/components/new-trace-viewer/utils.d.ts +47 -0
  53. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  54. package/dist/components/new-trace-viewer/utils.js +358 -0
  55. package/dist/components/new-trace-viewer/utils.js.map +1 -0
  56. package/dist/components/run-trace-view.d.ts +2 -1
  57. package/dist/components/run-trace-view.d.ts.map +1 -1
  58. package/dist/components/run-trace-view.js +2 -2
  59. package/dist/components/run-trace-view.js.map +1 -1
  60. package/dist/components/sidebar/attribute-panel.d.ts +7 -1
  61. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  62. package/dist/components/sidebar/attribute-panel.js +76 -47
  63. package/dist/components/sidebar/attribute-panel.js.map +1 -1
  64. package/dist/components/sidebar/conversation-view.js.map +1 -1
  65. package/dist/components/sidebar/copyable-data-block.js +1 -1
  66. package/dist/components/sidebar/copyable-data-block.js.map +1 -1
  67. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  68. package/dist/components/sidebar/detail-card.js +1 -1
  69. package/dist/components/sidebar/detail-card.js.map +1 -1
  70. package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
  71. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  72. package/dist/components/sidebar/entity-detail-panel.js +16 -36
  73. package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
  74. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  75. package/dist/components/sidebar/events-list.js +2 -2
  76. package/dist/components/sidebar/events-list.js.map +1 -1
  77. package/dist/components/sidebar/resolve-hook-modal.js.map +1 -1
  78. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  79. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  80. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  81. package/dist/components/sidebar/sidebar-data-context.js.map +1 -0
  82. package/dist/components/stream-viewer.d.ts +1 -1
  83. package/dist/components/stream-viewer.d.ts.map +1 -1
  84. package/dist/components/stream-viewer.js +2 -26
  85. package/dist/components/stream-viewer.js.map +1 -1
  86. package/dist/components/trace-viewer/components/map.js.map +1 -1
  87. package/dist/components/trace-viewer/components/markers.js.map +1 -1
  88. package/dist/components/trace-viewer/components/node.js.map +1 -1
  89. package/dist/components/trace-viewer/components/search-input.js.map +1 -1
  90. package/dist/components/trace-viewer/components/search.js.map +1 -1
  91. package/dist/components/trace-viewer/components/span-content.js.map +1 -1
  92. package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -1
  93. package/dist/components/trace-viewer/components/span-segments.js.map +1 -1
  94. package/dist/components/trace-viewer/components/span-strategies.js.map +1 -1
  95. package/dist/components/trace-viewer/components/ui.js.map +1 -1
  96. package/dist/components/trace-viewer/components/zoom-button.js.map +1 -1
  97. package/dist/components/trace-viewer/components/zoom-icons.js.map +1 -1
  98. package/dist/components/trace-viewer/context.js.map +1 -1
  99. package/dist/components/trace-viewer/index.js.map +1 -1
  100. package/dist/components/trace-viewer/trace-viewer.js.map +1 -1
  101. package/dist/components/trace-viewer/types.js.map +1 -1
  102. package/dist/components/trace-viewer/util/constants.js.map +1 -1
  103. package/dist/components/trace-viewer/util/scrollbar-width.js.map +1 -1
  104. package/dist/components/trace-viewer/util/timing.js.map +1 -1
  105. package/dist/components/trace-viewer/util/tree.js.map +1 -1
  106. package/dist/components/trace-viewer/util/use-immediate-style.js.map +1 -1
  107. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -1
  108. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +1 -1
  109. package/dist/components/trace-viewer/worker.js.map +1 -1
  110. package/dist/components/trace-viewer-new.d.ts +10 -0
  111. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  112. package/dist/components/trace-viewer-new.js +23 -0
  113. package/dist/components/trace-viewer-new.js.map +1 -0
  114. package/dist/components/ui/alert.js.map +1 -1
  115. package/dist/components/ui/card.js.map +1 -1
  116. package/dist/components/ui/data-inspector.d.ts +38 -1
  117. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  118. package/dist/components/ui/data-inspector.js +211 -21
  119. package/dist/components/ui/data-inspector.js.map +1 -1
  120. package/dist/components/ui/decrypt-button.js.map +1 -1
  121. package/dist/components/ui/error-card.js.map +1 -1
  122. package/dist/components/ui/error-stack-block.js.map +1 -1
  123. package/dist/components/ui/inspector-theme.d.ts +9 -10
  124. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  125. package/dist/components/ui/inspector-theme.js +38 -71
  126. package/dist/components/ui/inspector-theme.js.map +1 -1
  127. package/dist/components/ui/load-more-button.js.map +1 -1
  128. package/dist/components/ui/menu-dropdown.js.map +1 -1
  129. package/dist/components/ui/skeleton.js.map +1 -1
  130. package/dist/components/ui/spinner.js.map +1 -1
  131. package/dist/components/ui/timestamp-tooltip.js.map +1 -1
  132. package/dist/components/workflow-trace-view.d.ts +3 -1
  133. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  134. package/dist/components/workflow-trace-view.js +9 -4
  135. package/dist/components/workflow-trace-view.js.map +1 -1
  136. package/dist/components/workflow-traces/event-colors.js.map +1 -1
  137. package/dist/components/workflow-traces/trace-colors.js.map +1 -1
  138. package/dist/components/workflow-traces/trace-span-construction.js +1 -1
  139. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
  140. package/dist/components/workflow-traces/trace-time-utils.js.map +1 -1
  141. package/dist/hooks/use-dark-mode.js.map +1 -1
  142. package/dist/index.d.ts +2 -1
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +1 -1
  145. package/dist/index.js.map +1 -1
  146. package/dist/lib/event-analysis.js.map +1 -1
  147. package/dist/lib/event-materialization.js.map +1 -1
  148. package/dist/lib/hydration.d.ts +8 -1
  149. package/dist/lib/hydration.d.ts.map +1 -1
  150. package/dist/lib/hydration.js +32 -2
  151. package/dist/lib/hydration.js.map +1 -1
  152. package/dist/lib/stream-display.d.ts +18 -0
  153. package/dist/lib/stream-display.d.ts.map +1 -0
  154. package/dist/lib/stream-display.js +30 -0
  155. package/dist/lib/stream-display.js.map +1 -0
  156. package/dist/lib/toast.js.map +1 -1
  157. package/dist/lib/trace-builder.js.map +1 -1
  158. package/dist/lib/utils.d.ts +7 -9
  159. package/dist/lib/utils.d.ts.map +1 -1
  160. package/dist/lib/utils.js +17 -22
  161. package/dist/lib/utils.js.map +1 -1
  162. package/dist/styles.css +470 -1
  163. package/package.json +4 -3
  164. package/src/components/event-list-view.tsx +181 -173
  165. package/src/components/index.ts +11 -1
  166. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  167. package/src/components/new-trace-viewer/components/event-list.tsx +109 -0
  168. package/src/components/new-trace-viewer/components/split-pane.tsx +186 -0
  169. package/src/components/new-trace-viewer/components/timeline.tsx +378 -0
  170. package/src/components/new-trace-viewer/context.tsx +94 -0
  171. package/src/components/new-trace-viewer/detail-panel.tsx +57 -0
  172. package/src/components/new-trace-viewer/icons.tsx +79 -0
  173. package/src/components/new-trace-viewer/trace-viewer.tsx +585 -0
  174. package/src/components/new-trace-viewer/types.ts +0 -0
  175. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  176. package/src/components/new-trace-viewer/utils.ts +524 -0
  177. package/src/components/run-trace-view.tsx +3 -0
  178. package/src/components/sidebar/attribute-panel.tsx +200 -138
  179. package/src/components/sidebar/copyable-data-block.tsx +1 -1
  180. package/src/components/sidebar/detail-card.tsx +1 -19
  181. package/src/components/sidebar/entity-detail-panel.tsx +91 -147
  182. package/src/components/sidebar/events-list.tsx +9 -6
  183. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  184. package/src/components/stream-viewer.tsx +18 -40
  185. package/src/components/trace-viewer-new.tsx +49 -0
  186. package/src/components/ui/data-inspector.tsx +436 -31
  187. package/src/components/ui/inspector-theme.ts +39 -81
  188. package/src/components/workflow-trace-view.tsx +12 -2
  189. package/src/components/workflow-traces/trace-span-construction.ts +1 -1
  190. package/src/index.ts +2 -0
  191. package/src/lib/hydration.ts +37 -0
  192. package/src/lib/stream-display.ts +48 -0
  193. package/src/lib/utils.ts +18 -23
  194. package/src/styles.css +470 -1
@@ -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({
@@ -1136,6 +1138,7 @@ export function EventListView({
1136
1138
  onSortOrderChange,
1137
1139
  onDecrypt,
1138
1140
  isDecrypting = false,
1141
+ hasEncryptedData: hasEncryptedDataProp = false,
1139
1142
  }: EventsListProps) {
1140
1143
  const [internalSortOrder, setInternalSortOrder] = useState<'asc' | 'desc'>(
1141
1144
  'asc'
@@ -1180,7 +1183,8 @@ export function EventListView({
1180
1183
  setFoundEncryptedInLazyData(true);
1181
1184
  }, []);
1182
1185
 
1183
- const hasEncryptedData = hasEncryptedInlineData || foundEncryptedInLazyData;
1186
+ const hasEncryptedData =
1187
+ hasEncryptedDataProp || hasEncryptedInlineData || foundEncryptedInLazyData;
1184
1188
 
1185
1189
  const { correlationNameMap, workflowName } = useMemo(
1186
1190
  () => buildNameMaps(events ?? null, run ?? null),
@@ -1393,195 +1397,199 @@ export function EventListView({
1393
1397
  }
1394
1398
 
1395
1399
  return (
1396
- <div className="h-full flex flex-col overflow-hidden">
1397
- <style>{`@keyframes workflow-dot-pulse{0%{transform:scale(1);opacity:.7}70%,100%{transform:scale(2.2);opacity:0}}`}</style>
1398
- {/* Search bar + sort */}
1399
- <div
1400
- style={{
1401
- padding: 6,
1402
- backgroundColor: 'var(--ds-background-100)',
1403
- display: 'flex',
1404
- gap: 6,
1405
- }}
1406
- >
1407
- <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
1408
1407
  style={{
1408
+ padding: 6,
1409
+ backgroundColor: 'var(--ds-background-100)',
1409
1410
  display: 'flex',
1410
- alignItems: 'center',
1411
- justifyContent: 'center',
1412
- borderRadius: 6,
1413
- boxShadow: '0 0 0 1px var(--ds-gray-alpha-400)',
1414
- background: 'var(--ds-background-100)',
1415
- height: 40,
1416
- flex: 1,
1417
- minWidth: 0,
1411
+ gap: 6,
1418
1412
  }}
1419
1413
  >
1420
- <div
1414
+ <label
1421
1415
  style={{
1422
- width: 40,
1423
- height: 40,
1424
1416
  display: 'flex',
1425
1417
  alignItems: 'center',
1426
1418
  justifyContent: 'center',
1427
- color: 'var(--ds-gray-800)',
1428
- 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,
1429
1425
  }}
1430
1426
  >
1431
- <svg
1432
- width={16}
1433
- height={16}
1434
- viewBox="0 0 16 16"
1435
- fill="none"
1436
- aria-hidden="true"
1437
- 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
+ }}
1438
1437
  >
1439
- <circle
1440
- cx="7"
1441
- cy="7"
1442
- r="4.5"
1443
- stroke="currentColor"
1444
- strokeWidth="1.5"
1445
- />
1446
- <path
1447
- d="M11.5 11.5L14 14"
1448
- stroke="currentColor"
1449
- strokeWidth="1.5"
1450
- strokeLinecap="round"
1451
- />
1452
- </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
1453
1506
  </div>
1454
- <input
1455
- type="search"
1456
- placeholder="Search by name, event type, or ID…"
1457
- value={searchQuery}
1458
- onChange={(e) => setSearchQuery(e.target.value)}
1459
- style={{
1460
- marginLeft: -16,
1461
- paddingInline: 12,
1462
- fontFamily: 'inherit',
1463
- fontSize: 14,
1464
- background: 'transparent',
1465
- border: 'none',
1466
- outline: 'none',
1467
- height: 40,
1468
- 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?.();
1469
1535
  }}
1470
- />
1471
- </label>
1472
- <MenuDropdown
1473
- options={SORT_OPTIONS}
1474
- value={effectiveSortOrder}
1475
- onChange={handleSortOrderChange}
1476
- />
1477
- {(hasEncryptedData || encryptionKey) && onDecrypt && (
1478
- <DecryptButton
1479
- decrypted={!!encryptionKey}
1480
- loading={isDecrypting}
1481
- 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 }}
1482
1565
  />
1483
1566
  )}
1484
- </div>
1485
-
1486
- {/* Header */}
1487
- <div
1488
- className="flex items-center gap-0 text-[13px] font-medium h-10 border-b flex-shrink-0"
1489
- style={{
1490
- borderColor: 'var(--ds-gray-alpha-200)',
1491
- color: 'var(--ds-gray-900)',
1492
- backgroundColor: 'var(--ds-background-100)',
1493
- }}
1494
- >
1495
- <div className="flex-shrink-0" style={{ width: GUTTER_WIDTH }} />
1496
- <div className="w-5 flex-shrink-0" />
1497
- <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1498
- Time
1499
- </div>
1500
- <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1501
- Event Type
1502
- </div>
1503
- <div className="min-w-0 px-4" style={{ flex: '2 1 0%' }}>
1504
- Name
1505
- </div>
1506
- <div className="min-w-0 px-4" style={{ flex: '3 1 0%' }}>
1507
- Correlation ID
1508
- </div>
1509
- <div className="min-w-0 px-4" style={{ flex: '3 1 0%' }}>
1510
- Event ID
1511
- </div>
1512
- </div>
1513
1567
 
1514
- {/* Virtualized event rows or refetching skeleton */}
1515
- {isRefetching ? (
1516
- <RowsSkeleton />
1517
- ) : (
1518
- <Virtuoso
1519
- ref={virtuosoRef}
1520
- totalCount={sortedEvents.length}
1521
- overscan={20}
1522
- defaultItemHeight={40}
1523
- endReached={() => {
1524
- if (!hasMoreEvents || isLoadingMoreEvents) {
1525
- return;
1526
- }
1527
- void onLoadMoreEvents?.();
1528
- }}
1529
- itemContent={(index: number) => {
1530
- const ev = sortedEvents[index];
1531
- return (
1532
- <EventRow
1533
- event={ev}
1534
- index={index}
1535
- isFirst={index === 0}
1536
- isLast={index === sortedEvents.length - 1}
1537
- isExpanded={expandedEventIds.has(ev.eventId)}
1538
- onToggleExpand={toggleEventExpanded}
1539
- activeGroupKey={activeGroupKey}
1540
- selectedGroupKey={selectedGroupKey}
1541
- selectedGroupRange={selectedGroupRange}
1542
- correlationNameMap={correlationNameMap}
1543
- workflowName={workflowName}
1544
- durationMap={durationMap}
1545
- onSelectGroup={onSelectGroup}
1546
- onHoverGroup={onHoverGroup}
1547
- onLoadEventData={onLoadEventData}
1548
- cachedEventData={
1549
- eventDataCacheRef.current.get(ev.eventId) ?? null
1550
- }
1551
- onCacheEventData={cacheEventData}
1552
- encryptionKey={encryptionKey}
1553
- onEncryptedDataDetected={handleEncryptedDataDetected}
1554
- />
1555
- );
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)',
1556
1575
  }}
1557
- style={{ flex: 1, minHeight: 0 }}
1558
- />
1559
- )}
1560
-
1561
- {/* Fixed footer — count + load more */}
1562
- <div
1563
- className="relative flex-shrink-0 flex items-center h-10 border-t px-4 text-xs"
1564
- style={{
1565
- borderColor: 'var(--ds-gray-alpha-200)',
1566
- color: 'var(--ds-gray-900)',
1567
- backgroundColor: 'var(--ds-background-100)',
1568
- }}
1569
- >
1570
- <span>
1571
- {sortedEvents.length} event
1572
- {sortedEvents.length !== 1 ? 's' : ''} loaded
1573
- </span>
1574
- {hasMoreEvents && (
1575
- <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
1576
- <div className="pointer-events-auto">
1577
- <LoadMoreButton
1578
- loading={isLoadingMoreEvents}
1579
- onClick={() => void onLoadMoreEvents?.()}
1580
- />
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>
1581
1589
  </div>
1582
- </div>
1583
- )}
1590
+ )}
1591
+ </div>
1584
1592
  </div>
1585
- </div>
1593
+ </DecryptClickContext.Provider>
1586
1594
  );
1587
1595
  }
@@ -15,15 +15,25 @@ export {
15
15
  } from './hook-actions';
16
16
  export { RunTraceView } from './run-trace-view';
17
17
  export { ConversationView } from './sidebar/conversation-view';
18
+ export {
19
+ SidebarDataProvider,
20
+ type SidebarDataContextValue,
21
+ } from './sidebar/sidebar-data-context';
18
22
  export type {
19
23
  SelectedSpanInfo,
20
24
  SpanSelectionInfo,
21
25
  } from './sidebar/entity-detail-panel';
22
26
  export { type StreamChunk, StreamViewer } from './stream-viewer';
23
27
  export type { Span, SpanEvent } from './trace-viewer/types';
24
- export { DataInspector, type DataInspectorProps } from './ui/data-inspector';
28
+ export {
29
+ DataInspector,
30
+ type DataInspectorProps,
31
+ DecryptClickContext,
32
+ type DecryptClickContextValue,
33
+ } from './ui/data-inspector';
25
34
  export { DecryptButton } from './ui/decrypt-button';
26
35
  export { LoadMoreButton } from './ui/load-more-button';
27
36
  export { MenuDropdown, type MenuDropdownOption } from './ui/menu-dropdown';
28
37
  export { Spinner } from './ui/spinner';
29
38
  export { WorkflowTraceViewer } from './workflow-trace-view';
39
+ export { NewTraceViewer } from './trace-viewer-new';
@@ -0,0 +1,68 @@
1
+ 'use client';
2
+
3
+ import { Check, Copy } from 'lucide-react';
4
+ import { type JSX, useEffect, useRef, useState } from 'react';
5
+ import { cn } from '../../../lib/utils';
6
+
7
+ interface CopyButtonProps {
8
+ copyText: string;
9
+ ariaLabel: string;
10
+ className?: string;
11
+ }
12
+
13
+ export function CopyButton({
14
+ copyText,
15
+ ariaLabel,
16
+ className,
17
+ }: CopyButtonProps): JSX.Element {
18
+ const [copied, setCopied] = useState(false);
19
+ const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
20
+
21
+ useEffect(() => {
22
+ return () => {
23
+ if (timeoutRef.current) {
24
+ clearTimeout(timeoutRef.current);
25
+ }
26
+ };
27
+ }, []);
28
+
29
+ return (
30
+ <button
31
+ type="button"
32
+ aria-label={ariaLabel}
33
+ className={cn(
34
+ 'cursor-pointer text-gray-800 hover:text-gray-1000 bg-transparent border-none p-1 m-0',
35
+ className
36
+ )}
37
+ onClick={(e) => {
38
+ e.stopPropagation();
39
+ if (timeoutRef.current) {
40
+ clearTimeout(timeoutRef.current);
41
+ }
42
+ void navigator.clipboard.writeText(copyText).then(() => {
43
+ setCopied(true);
44
+ timeoutRef.current = setTimeout(() => setCopied(false), 1000);
45
+ });
46
+ }}
47
+ >
48
+ <div className="relative w-3 h-3">
49
+ <div
50
+ className={cn(
51
+ 'absolute inset-0 flex items-center justify-center transition-all duration-150 ease-out',
52
+ copied ? 'scale-100 opacity-100' : 'scale-0 opacity-0'
53
+ )}
54
+ >
55
+ <Check className="w-3 h-3" />
56
+ </div>
57
+ <div
58
+ className={cn(
59
+ 'absolute inset-0 flex items-center justify-center transition-all duration-150 ease-out',
60
+ copied ? 'scale-0 opacity-0' : 'scale-100 opacity-100'
61
+ )}
62
+ >
63
+ <Copy className="w-3 h-3" />
64
+ </div>
65
+ </div>
66
+ </button>
67
+ );
68
+ }
@@ -0,0 +1,109 @@
1
+ import { Circle } from 'lucide-react';
2
+ import { cn } from '../../../lib/utils';
3
+ import type { Span } from '../../trace-viewer/types';
4
+ import { formatDuration, getHighResInMs } from '../../trace-viewer/util/timing';
5
+ import {
6
+ WorkflowIcon,
7
+ WebhookIcon,
8
+ SleepIcon,
9
+ StepForwardIcon,
10
+ } from '../icons';
11
+
12
+ interface EventStyle {
13
+ icon: React.ComponentType<{ className?: string }>;
14
+ className: string;
15
+ }
16
+
17
+ const eventStyles: Record<string, EventStyle> = {
18
+ run: { icon: WorkflowIcon, className: 'text-blue-900' },
19
+ step: { icon: StepForwardIcon, className: 'text-green-900' },
20
+ hook: { icon: WebhookIcon, className: 'text-yellow-900' },
21
+ sleep: { icon: SleepIcon, className: 'text-gray-900' },
22
+ };
23
+
24
+ const defaultStyle: EventStyle = {
25
+ icon: Circle,
26
+ className: 'text-gray-900',
27
+ };
28
+
29
+ function getEventStyle(resource: string, isErrored: boolean): EventStyle {
30
+ const style = eventStyles[resource] ?? defaultStyle;
31
+ return {
32
+ icon: style.icon,
33
+ className: cn(isErrored ? 'text-red-900' : style.className),
34
+ };
35
+ }
36
+
37
+ const EventRow = ({
38
+ span,
39
+ isSelected,
40
+ onSelectSpan,
41
+ }: {
42
+ span: Span;
43
+ isSelected: boolean;
44
+ onSelectSpan: (spanId: string) => void;
45
+ }) => {
46
+ const durationMs = getHighResInMs(span.duration);
47
+ const isErrored =
48
+ (span.attributes.data as Record<string, unknown>).status === 'failed';
49
+ const { icon: Icon, className: tagClassName } = getEventStyle(
50
+ span.resource,
51
+ isErrored
52
+ );
53
+
54
+ return (
55
+ <li
56
+ className="overflow-clip group"
57
+ role="treeitem"
58
+ aria-selected={isSelected}
59
+ aria-expanded={isSelected}
60
+ aria-level={1}
61
+ onClick={() => onSelectSpan(span.spanId)}
62
+ >
63
+ <div className="hover:bg-gray-100 group-aria-selected:bg-gray-100 group-aria-selected:hover:bg-gray-200 hover:aria-selected:bg-gray-100 rounded-sm px-2 h-[34px] py-1.5 flex">
64
+ <div className="flex items-center gap-2">
65
+ <span className={tagClassName}>
66
+ <Icon className="w-4 h-4" />
67
+ </span>
68
+ <span className="text-label-14">{span.name}</span>
69
+ </div>
70
+ <div className="ml-auto">
71
+ <span className="text-label-14 text-gray-900 tabular-nums">
72
+ {formatDuration(durationMs)}
73
+ </span>
74
+ </div>
75
+ </div>
76
+ </li>
77
+ );
78
+ };
79
+
80
+ const EventList = ({
81
+ spans,
82
+ activeSpanId,
83
+ onSelectSpan,
84
+ }: {
85
+ spans: Span[];
86
+ activeSpanId: string | null;
87
+ onSelectSpan: (spanId: string) => void;
88
+ }) => {
89
+ return (
90
+ <ul
91
+ id="event-list"
92
+ role="tree"
93
+ className="block min-h-0 overflow-visible px-2 py-2"
94
+ >
95
+ {spans.map((span) => {
96
+ return (
97
+ <EventRow
98
+ key={span.spanId}
99
+ span={span}
100
+ isSelected={span.spanId === activeSpanId}
101
+ onSelectSpan={onSelectSpan}
102
+ />
103
+ );
104
+ })}
105
+ </ul>
106
+ );
107
+ };
108
+
109
+ export default EventList;