@workflow/web-shared 5.0.0-beta.37 → 5.0.0-beta.39
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 +3 -3
- package/dist/components/event-list-view.d.ts.map +1 -1
- package/dist/components/event-list-view.js +10 -10
- package/dist/components/index.d.ts +4 -5
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +4 -5
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
- package/dist/components/sidebar/attribute-panel.js +32 -8
- package/dist/components/sidebar/attributes-block.js +3 -3
- package/dist/components/sidebar/copyable-data-block.js +2 -2
- package/dist/components/sidebar/entity-detail-panel.js +3 -3
- package/dist/components/sidebar/events-list.js +2 -2
- package/dist/components/stream-viewer-skeleton.d.ts +2 -0
- package/dist/components/stream-viewer-skeleton.d.ts.map +1 -0
- package/dist/components/stream-viewer-skeleton.js +12 -0
- package/dist/components/stream-viewer.d.ts +1 -1
- package/dist/components/stream-viewer.d.ts.map +1 -1
- package/dist/components/stream-viewer.js +9 -26
- package/dist/components/trace-viewer/components/copy-button.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/copy-button.js +27 -0
- package/dist/components/trace-viewer/components/detail-panel-width.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/detail-panel-width.js +67 -0
- package/dist/components/trace-viewer/components/detail-panel.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/detail-panel.js +122 -0
- package/dist/components/trace-viewer/components/draggable-border.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/draggable-border.js +140 -0
- package/dist/components/trace-viewer/components/event-list.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/event-list.js +47 -0
- package/dist/components/trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/middle-truncate/copy-selection.js +54 -0
- package/dist/components/trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
- package/dist/components/trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/middle-truncate/truncate.js +101 -0
- package/dist/components/trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/middle-truncate/use-middle-truncate.js +114 -0
- package/dist/components/trace-viewer/components/minimap.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/minimap.js +337 -0
- package/dist/components/trace-viewer/components/pane-constants.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/pane-constants.js +16 -0
- package/dist/components/trace-viewer/components/span-markers.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-markers.js +85 -0
- package/dist/components/trace-viewer/components/split-pane.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/split-pane.js +41 -0
- package/dist/components/trace-viewer/components/timeline.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/timeline.js +321 -0
- package/dist/components/trace-viewer/components/trace-shortcut-helper.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/trace-shortcut-helper.js +55 -0
- package/dist/components/trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/trace-viewer-skeleton.js +26 -0
- package/dist/components/trace-viewer/components/use-element-width.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/use-element-width.js +25 -0
- package/dist/components/trace-viewer/components/use-row-window.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/use-row-window.js +107 -0
- package/dist/components/trace-viewer/context.d.ts +14 -182
- package/dist/components/trace-viewer/context.d.ts.map +1 -1
- package/dist/components/trace-viewer/context.js +44 -351
- package/dist/components/trace-viewer/icons.d.ts.map +1 -0
- package/dist/components/trace-viewer/icons.js +15 -0
- package/dist/components/trace-viewer/search.d.ts.map +1 -0
- package/dist/components/trace-viewer/search.js +134 -0
- package/dist/components/trace-viewer/trace-viewer.d.ts +7 -28
- package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.js +306 -356
- package/dist/components/trace-viewer/types.d.ts +2 -211
- package/dist/components/trace-viewer/types.d.ts.map +1 -1
- package/dist/components/trace-viewer/types.js +1 -1
- package/dist/components/trace-viewer/utils.d.ts.map +1 -0
- package/dist/components/trace-viewer/utils.js +430 -0
- package/dist/components/{trace-viewer-new.d.ts → trace-viewer.d.ts} +3 -3
- package/dist/components/trace-viewer.d.ts.map +1 -0
- package/dist/components/trace-viewer.js +21 -0
- package/dist/components/ui/alert.js +2 -2
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.js +3 -3
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/card.js +3 -3
- package/dist/components/ui/error-card.js +2 -2
- package/dist/components/ui/error-stack-block.js +4 -4
- package/dist/components/ui/kbd.js +3 -3
- package/dist/components/ui/timestamp-tooltip.js +3 -3
- package/dist/components/workflow-traces/trace-span-construction.d.ts +1 -1
- package/dist/components/workflow-traces/trace-span-construction.js +1 -1
- package/dist/lib/trace-builder.d.ts +1 -1
- package/dist/lib/trace-builder.d.ts.map +1 -1
- package/dist/lib/trace-builder.js +1 -1
- package/dist/lib/trace-types.d.ts +54 -0
- package/dist/lib/trace-types.d.ts.map +1 -0
- package/dist/lib/trace-types.js +2 -0
- package/package.json +6 -6
- package/src/components/event-list-view.tsx +12 -9
- package/src/components/index.ts +4 -5
- package/src/components/sidebar/attribute-panel.tsx +46 -8
- package/src/components/sidebar/attributes-block.tsx +2 -2
- package/src/components/sidebar/copyable-data-block.tsx +1 -1
- package/src/components/sidebar/entity-detail-panel.tsx +3 -3
- package/src/components/sidebar/events-list.tsx +1 -1
- package/src/components/stream-viewer-skeleton.tsx +34 -0
- package/src/components/stream-viewer.tsx +34 -106
- package/src/components/{new-trace-viewer → trace-viewer}/components/event-list.tsx +1 -1
- package/src/components/{new-trace-viewer → trace-viewer}/components/minimap.tsx +1 -4
- package/src/components/{new-trace-viewer → trace-viewer}/components/timeline.tsx +2 -5
- package/src/components/{new-trace-viewer → trace-viewer}/components/trace-shortcut-helper.tsx +1 -1
- package/src/components/trace-viewer/context.tsx +63 -656
- package/src/components/trace-viewer/trace-viewer.tsx +563 -531
- package/src/components/trace-viewer/types.ts +2 -234
- package/src/components/{new-trace-viewer → trace-viewer}/utils.ts +2 -2
- package/src/components/{trace-viewer-new.tsx → trace-viewer.tsx} +5 -5
- package/src/components/ui/alert.tsx +1 -1
- package/src/components/ui/button.tsx +3 -3
- package/src/components/ui/card.tsx +2 -9
- package/src/components/ui/error-card.tsx +3 -3
- package/src/components/ui/error-stack-block.tsx +3 -3
- package/src/components/ui/kbd.tsx +2 -2
- package/src/components/ui/timestamp-tooltip.tsx +4 -4
- package/src/components/workflow-traces/trace-span-construction.ts +1 -1
- package/src/lib/trace-builder.ts +1 -1
- package/src/lib/trace-types.ts +56 -0
- package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/copy-button.js +0 -27
- package/dist/components/new-trace-viewer/components/detail-panel-width.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/detail-panel-width.js +0 -67
- package/dist/components/new-trace-viewer/components/detail-panel.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/detail-panel.js +0 -122
- package/dist/components/new-trace-viewer/components/draggable-border.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/draggable-border.js +0 -140
- package/dist/components/new-trace-viewer/components/event-list.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/event-list.js +0 -47
- package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +0 -54
- package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +0 -81
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +0 -101
- package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js +0 -114
- package/dist/components/new-trace-viewer/components/minimap.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/minimap.js +0 -337
- package/dist/components/new-trace-viewer/components/pane-constants.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/pane-constants.js +0 -16
- package/dist/components/new-trace-viewer/components/span-markers.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/span-markers.js +0 -85
- package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/split-pane.js +0 -41
- package/dist/components/new-trace-viewer/components/timeline.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/timeline.js +0 -321
- package/dist/components/new-trace-viewer/components/trace-shortcut-helper.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/trace-shortcut-helper.js +0 -55
- package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.js +0 -26
- package/dist/components/new-trace-viewer/components/use-element-width.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/use-element-width.js +0 -25
- package/dist/components/new-trace-viewer/components/use-row-window.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/components/use-row-window.js +0 -107
- package/dist/components/new-trace-viewer/context.d.ts +0 -17
- package/dist/components/new-trace-viewer/context.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/context.js +0 -48
- package/dist/components/new-trace-viewer/icons.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/icons.js +0 -15
- package/dist/components/new-trace-viewer/search.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/search.js +0 -134
- package/dist/components/new-trace-viewer/trace-viewer.d.ts +0 -11
- package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/trace-viewer.js +0 -336
- package/dist/components/new-trace-viewer/types.d.ts +0 -3
- package/dist/components/new-trace-viewer/types.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/types.js +0 -2
- package/dist/components/new-trace-viewer/utils.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/utils.js +0 -430
- package/dist/components/run-trace-view.d.ts +0 -23
- package/dist/components/run-trace-view.d.ts.map +0 -1
- package/dist/components/run-trace-view.js +0 -11
- package/dist/components/trace-viewer/components/map.d.ts +0 -8
- package/dist/components/trace-viewer/components/map.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/map.js +0 -162
- package/dist/components/trace-viewer/components/markers.d.ts +0 -23
- package/dist/components/trace-viewer/components/markers.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/markers.js +0 -416
- package/dist/components/trace-viewer/components/node.d.ts +0 -35
- package/dist/components/trace-viewer/components/node.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/node.js +0 -286
- package/dist/components/trace-viewer/components/search-input.d.ts +0 -9
- package/dist/components/trace-viewer/components/search-input.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/search-input.js +0 -9
- package/dist/components/trace-viewer/components/search.d.ts +0 -3
- package/dist/components/trace-viewer/components/search.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/search.js +0 -25
- package/dist/components/trace-viewer/components/span-content.d.ts +0 -19
- package/dist/components/trace-viewer/components/span-content.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/span-content.js +0 -137
- package/dist/components/trace-viewer/components/span-detail-panel.d.ts +0 -10
- package/dist/components/trace-viewer/components/span-detail-panel.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/span-detail-panel.js +0 -285
- package/dist/components/trace-viewer/components/span-segments.d.ts +0 -50
- package/dist/components/trace-viewer/components/span-segments.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/span-segments.js +0 -445
- package/dist/components/trace-viewer/components/span-strategies.d.ts +0 -46
- package/dist/components/trace-viewer/components/span-strategies.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/span-strategies.js +0 -108
- package/dist/components/trace-viewer/components/ui.d.ts +0 -28
- package/dist/components/trace-viewer/components/ui.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/ui.js +0 -39
- package/dist/components/trace-viewer/components/zoom-button.d.ts +0 -3
- package/dist/components/trace-viewer/components/zoom-button.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/zoom-button.js +0 -29
- package/dist/components/trace-viewer/components/zoom-icons.d.ts +0 -11
- package/dist/components/trace-viewer/components/zoom-icons.d.ts.map +0 -1
- package/dist/components/trace-viewer/components/zoom-icons.js +0 -11
- package/dist/components/trace-viewer/index.d.ts +0 -5
- package/dist/components/trace-viewer/index.d.ts.map +0 -1
- package/dist/components/trace-viewer/index.js +0 -4
- package/dist/components/trace-viewer/trace-viewer.module.css +0 -1465
- package/dist/components/trace-viewer/util/constants.d.ts +0 -9
- package/dist/components/trace-viewer/util/constants.d.ts.map +0 -1
- package/dist/components/trace-viewer/util/constants.js +0 -9
- package/dist/components/trace-viewer/util/scrollbar-width.d.ts +0 -2
- package/dist/components/trace-viewer/util/scrollbar-width.d.ts.map +0 -1
- package/dist/components/trace-viewer/util/scrollbar-width.js +0 -14
- package/dist/components/trace-viewer/util/tree.d.ts +0 -13
- package/dist/components/trace-viewer/util/tree.d.ts.map +0 -1
- package/dist/components/trace-viewer/util/tree.js +0 -227
- package/dist/components/trace-viewer/util/use-immediate-style.d.ts +0 -15
- package/dist/components/trace-viewer/util/use-immediate-style.d.ts.map +0 -1
- package/dist/components/trace-viewer/util/use-immediate-style.js +0 -23
- package/dist/components/trace-viewer/util/use-streaming-spans.d.ts +0 -8
- package/dist/components/trace-viewer/util/use-streaming-spans.d.ts.map +0 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.js +0 -345
- package/dist/components/trace-viewer/util/use-trackpad-zoom.d.ts +0 -6
- package/dist/components/trace-viewer/util/use-trackpad-zoom.d.ts.map +0 -1
- package/dist/components/trace-viewer/util/use-trackpad-zoom.js +0 -39
- package/dist/components/trace-viewer/worker.d.ts +0 -2
- package/dist/components/trace-viewer/worker.d.ts.map +0 -1
- package/dist/components/trace-viewer/worker.js +0 -109
- package/dist/components/trace-viewer-new.d.ts.map +0 -1
- package/dist/components/trace-viewer-new.js +0 -21
- package/dist/components/workflow-trace-view.d.ts +0 -39
- package/dist/components/workflow-trace-view.d.ts.map +0 -1
- package/dist/components/workflow-trace-view.js +0 -690
- package/dist/components/workflow-traces/trace-colors.d.ts +0 -15
- package/dist/components/workflow-traces/trace-colors.d.ts.map +0 -1
- package/dist/components/workflow-traces/trace-colors.js +0 -86
- package/src/components/new-trace-viewer/context.tsx +0 -93
- package/src/components/new-trace-viewer/trace-viewer.tsx +0 -632
- package/src/components/new-trace-viewer/types.ts +0 -2
- package/src/components/run-trace-view.tsx +0 -80
- package/src/components/trace-viewer/components/map.tsx +0 -226
- package/src/components/trace-viewer/components/markers.tsx +0 -612
- package/src/components/trace-viewer/components/node.tsx +0 -506
- package/src/components/trace-viewer/components/search-input.tsx +0 -52
- package/src/components/trace-viewer/components/search.tsx +0 -47
- package/src/components/trace-viewer/components/span-content.tsx +0 -247
- package/src/components/trace-viewer/components/span-detail-panel.tsx +0 -655
- package/src/components/trace-viewer/components/span-segments.ts +0 -585
- package/src/components/trace-viewer/components/span-strategies.ts +0 -205
- package/src/components/trace-viewer/components/ui.tsx +0 -156
- package/src/components/trace-viewer/components/zoom-button.tsx +0 -61
- package/src/components/trace-viewer/components/zoom-icons.tsx +0 -65
- package/src/components/trace-viewer/index.tsx +0 -4
- package/src/components/trace-viewer/trace-viewer.module.css +0 -1465
- package/src/components/trace-viewer/util/constants.ts +0 -8
- package/src/components/trace-viewer/util/scrollbar-width.ts +0 -13
- package/src/components/trace-viewer/util/tree.ts +0 -277
- package/src/components/trace-viewer/util/use-immediate-style.ts +0 -38
- package/src/components/trace-viewer/util/use-streaming-spans.ts +0 -426
- package/src/components/trace-viewer/util/use-trackpad-zoom.tsx +0 -51
- package/src/components/trace-viewer/worker.ts +0 -131
- package/src/components/workflow-trace-view.tsx +0 -1166
- package/src/components/workflow-traces/trace-colors.ts +0 -112
- package/dist/components/{new-trace-viewer → trace-viewer}/components/copy-button.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/detail-panel-width.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/detail-panel.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/draggable-border.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/event-list.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/copy-selection.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/middle-truncate.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/truncate.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/use-middle-truncate.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/minimap.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/pane-constants.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/span-markers.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/split-pane.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/timeline.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/timeline.module.css +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/trace-shortcut-helper.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/trace-shortcut-helper.module.css +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/trace-viewer-skeleton.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/use-element-width.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/components/use-row-window.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/icons.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/search.d.ts +0 -0
- package/dist/components/{new-trace-viewer → trace-viewer}/utils.d.ts +0 -0
- package/src/components/{new-trace-viewer → trace-viewer}/components/copy-button.tsx +0 -0
- package/src/components/{new-trace-viewer → trace-viewer}/components/detail-panel-width.test.ts +0 -0
- package/src/components/{new-trace-viewer → trace-viewer}/components/detail-panel-width.ts +0 -0
- package/src/components/{new-trace-viewer → trace-viewer}/components/detail-panel.tsx +0 -0
- package/src/components/{new-trace-viewer → trace-viewer}/components/draggable-border.tsx +0 -0
- package/src/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/copy-selection.ts +0 -0
- package/src/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/middle-truncate.tsx +1 -1
- /package/src/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/truncate.ts +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/components/middle-truncate/use-middle-truncate.ts +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/components/pane-constants.ts +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/components/span-markers.tsx +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/components/split-pane.tsx +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/components/timeline.module.css +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/components/trace-shortcut-helper.module.css +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/components/trace-viewer-skeleton.tsx +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/components/use-element-width.ts +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/components/use-row-window.ts +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/icons.tsx +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/search.ts +0 -0
- /package/src/components/{new-trace-viewer → trace-viewer}/utils.test.ts +0 -0
- /package/src/{components/trace-viewer/modules.d.ts → modules.d.ts} +0 -0
|
@@ -1,1166 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { parseStepName, parseWorkflowName } from '@workflow/utils/parse-name';
|
|
4
|
-
import type { Event, Hook, WorkflowRun } from '@workflow/world';
|
|
5
|
-
import {
|
|
6
|
-
ChevronDown,
|
|
7
|
-
ChevronUp,
|
|
8
|
-
Clock,
|
|
9
|
-
Copy,
|
|
10
|
-
Info,
|
|
11
|
-
Send,
|
|
12
|
-
Type,
|
|
13
|
-
X,
|
|
14
|
-
XCircle,
|
|
15
|
-
} from 'lucide-react';
|
|
16
|
-
import type { MouseEvent as ReactMouseEvent, ReactNode } from 'react';
|
|
17
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
18
|
-
import { createPortal } from 'react-dom';
|
|
19
|
-
import { useToast } from '../lib/toast';
|
|
20
|
-
import {
|
|
21
|
-
buildTrace,
|
|
22
|
-
filterSpanRawEvents,
|
|
23
|
-
type TraceWithMeta,
|
|
24
|
-
} from '../lib/trace-builder';
|
|
25
|
-
import { ErrorBoundary } from './error-boundary';
|
|
26
|
-
import {
|
|
27
|
-
EntityDetailPanel,
|
|
28
|
-
type SelectedSpanInfo,
|
|
29
|
-
type SpanSelectionInfo,
|
|
30
|
-
} from './sidebar/entity-detail-panel';
|
|
31
|
-
import { ResolveHookModal } from './sidebar/resolve-hook-modal';
|
|
32
|
-
import type { FetchSpanDetail } from './sidebar/use-selected-span-detail';
|
|
33
|
-
import {
|
|
34
|
-
TraceViewerContextProvider,
|
|
35
|
-
TraceViewerTimeline,
|
|
36
|
-
useTraceViewer,
|
|
37
|
-
} from './trace-viewer';
|
|
38
|
-
import type { Span } from './trace-viewer/types';
|
|
39
|
-
import { Skeleton } from './ui/skeleton';
|
|
40
|
-
import { Spinner } from './ui/spinner';
|
|
41
|
-
import {
|
|
42
|
-
getCustomSpanClassName,
|
|
43
|
-
getCustomSpanEventClassName,
|
|
44
|
-
} from './workflow-traces/trace-colors';
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* While a run is live, continuously grow root.duration and rescale so the
|
|
48
|
-
* trace always fits within the viewport. Individual span widths are grown
|
|
49
|
-
* by each SpanComponent's own useEffect (see node.tsx).
|
|
50
|
-
*/
|
|
51
|
-
function useLiveTick(isLive: boolean): void {
|
|
52
|
-
const { state, dispatch } = useTraceViewer();
|
|
53
|
-
const stateRef = useRef(state);
|
|
54
|
-
stateRef.current = state;
|
|
55
|
-
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
if (!isLive) return;
|
|
58
|
-
|
|
59
|
-
// Grow root.duration on every frame so span rAFs see the latest time
|
|
60
|
-
let rafId = 0;
|
|
61
|
-
const tick = (): void => {
|
|
62
|
-
const { root } = stateRef.current;
|
|
63
|
-
if (root.startTime) {
|
|
64
|
-
const nowMs = Date.now();
|
|
65
|
-
if (nowMs > root.endTime) {
|
|
66
|
-
root.endTime = nowMs;
|
|
67
|
-
root.duration = root.endTime - root.startTime;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
rafId = requestAnimationFrame(tick);
|
|
71
|
-
};
|
|
72
|
-
rafId = requestAnimationFrame(tick);
|
|
73
|
-
|
|
74
|
-
// Re-scale smoothly so the trace fits the viewport as it grows.
|
|
75
|
-
// We dispatch detectBaseScale only when the computed baseScale has
|
|
76
|
-
// changed enough to matter visually (>0.1% shift), avoiding
|
|
77
|
-
// unnecessary React re-renders while keeping things smooth.
|
|
78
|
-
let scaleRafId = 0;
|
|
79
|
-
let lastBaseScale = 0;
|
|
80
|
-
const scaleTick = (): void => {
|
|
81
|
-
const s = stateRef.current;
|
|
82
|
-
if (s.root.duration > 0) {
|
|
83
|
-
const newBaseScale = (s.width - s.scrollbarWidth) / s.root.duration;
|
|
84
|
-
const delta = Math.abs(newBaseScale - lastBaseScale);
|
|
85
|
-
if (delta > lastBaseScale * 0.001 || lastBaseScale === 0) {
|
|
86
|
-
lastBaseScale = newBaseScale;
|
|
87
|
-
dispatch({ type: 'detectBaseScale' });
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
scaleRafId = requestAnimationFrame(scaleTick);
|
|
91
|
-
};
|
|
92
|
-
scaleRafId = requestAnimationFrame(scaleTick);
|
|
93
|
-
|
|
94
|
-
return () => {
|
|
95
|
-
cancelAnimationFrame(rafId);
|
|
96
|
-
cancelAnimationFrame(scaleRafId);
|
|
97
|
-
};
|
|
98
|
-
}, [isLive, dispatch]);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const DEFAULT_PANEL_WIDTH = 450;
|
|
102
|
-
const MIN_PANEL_WIDTH = 240;
|
|
103
|
-
|
|
104
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
105
|
-
// Right-click context menu for spans
|
|
106
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
107
|
-
|
|
108
|
-
type ResourceType = 'sleep' | 'step' | 'hook' | 'run' | 'unknown';
|
|
109
|
-
|
|
110
|
-
interface ContextMenuState {
|
|
111
|
-
x: number;
|
|
112
|
-
y: number;
|
|
113
|
-
spanId: string;
|
|
114
|
-
spanName: string;
|
|
115
|
-
resourceType: ResourceType;
|
|
116
|
-
/** Whether the span represents an active/pending resource (not yet completed) */
|
|
117
|
-
isActive: boolean;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
interface ContextMenuItem {
|
|
121
|
-
label: string;
|
|
122
|
-
icon?: ReactNode;
|
|
123
|
-
action: () => void;
|
|
124
|
-
destructive?: boolean;
|
|
125
|
-
disabled?: boolean;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function SpanContextMenu({
|
|
129
|
-
menu,
|
|
130
|
-
items,
|
|
131
|
-
onClose,
|
|
132
|
-
}: {
|
|
133
|
-
menu: ContextMenuState;
|
|
134
|
-
items: ContextMenuItem[];
|
|
135
|
-
onClose: () => void;
|
|
136
|
-
}): ReactNode {
|
|
137
|
-
const menuRef = useRef<HTMLDivElement>(null);
|
|
138
|
-
|
|
139
|
-
// Close on outside click
|
|
140
|
-
useEffect(() => {
|
|
141
|
-
const handleClick = (e: MouseEvent): void => {
|
|
142
|
-
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
|
|
143
|
-
onClose();
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
const handleKeyDown = (e: KeyboardEvent): void => {
|
|
147
|
-
if (e.key === 'Escape') {
|
|
148
|
-
onClose();
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
const handleScroll = (): void => {
|
|
152
|
-
onClose();
|
|
153
|
-
};
|
|
154
|
-
// Use a timeout so we don't close immediately from the same event
|
|
155
|
-
const timeout = setTimeout(() => {
|
|
156
|
-
window.addEventListener('mousedown', handleClick);
|
|
157
|
-
window.addEventListener('keydown', handleKeyDown);
|
|
158
|
-
window.addEventListener('scroll', handleScroll, true);
|
|
159
|
-
}, 0);
|
|
160
|
-
return () => {
|
|
161
|
-
clearTimeout(timeout);
|
|
162
|
-
window.removeEventListener('mousedown', handleClick);
|
|
163
|
-
window.removeEventListener('keydown', handleKeyDown);
|
|
164
|
-
window.removeEventListener('scroll', handleScroll, true);
|
|
165
|
-
};
|
|
166
|
-
}, [onClose]);
|
|
167
|
-
|
|
168
|
-
// Adjust position if menu would overflow viewport
|
|
169
|
-
const [adjustedPos, setAdjustedPos] = useState({ x: menu.x, y: menu.y });
|
|
170
|
-
useEffect(() => {
|
|
171
|
-
const el = menuRef.current;
|
|
172
|
-
if (!el) return;
|
|
173
|
-
const rect = el.getBoundingClientRect();
|
|
174
|
-
let { x, y } = menu;
|
|
175
|
-
if (rect.right > window.innerWidth) {
|
|
176
|
-
x = window.innerWidth - rect.width - 8;
|
|
177
|
-
}
|
|
178
|
-
if (rect.bottom > window.innerHeight) {
|
|
179
|
-
y = window.innerHeight - rect.height - 8;
|
|
180
|
-
}
|
|
181
|
-
if (x !== menu.x || y !== menu.y) {
|
|
182
|
-
setAdjustedPos({ x, y });
|
|
183
|
-
}
|
|
184
|
-
}, [menu]);
|
|
185
|
-
|
|
186
|
-
if (items.length === 0) return null;
|
|
187
|
-
|
|
188
|
-
return createPortal(
|
|
189
|
-
<div
|
|
190
|
-
ref={menuRef}
|
|
191
|
-
style={{
|
|
192
|
-
position: 'fixed',
|
|
193
|
-
left: adjustedPos.x,
|
|
194
|
-
top: adjustedPos.y,
|
|
195
|
-
zIndex: 99999,
|
|
196
|
-
boxShadow: 'var(--ds-shadow-menu)',
|
|
197
|
-
borderRadius: 12,
|
|
198
|
-
background: 'var(--ds-background-100)',
|
|
199
|
-
padding: 'var(--geist-space-gap-quarter, 4px)',
|
|
200
|
-
fontSize: 14,
|
|
201
|
-
minWidth: 180,
|
|
202
|
-
overflowX: 'hidden',
|
|
203
|
-
overflowY: 'auto',
|
|
204
|
-
}}
|
|
205
|
-
>
|
|
206
|
-
<div
|
|
207
|
-
style={{
|
|
208
|
-
display: 'block',
|
|
209
|
-
color: 'var(--ds-gray-900)',
|
|
210
|
-
fontSize: '0.75rem',
|
|
211
|
-
padding: 'var(--geist-gap-quarter, 4px) var(--geist-space-2x, 8px)',
|
|
212
|
-
fontWeight: 500,
|
|
213
|
-
overflow: 'hidden',
|
|
214
|
-
textOverflow: 'ellipsis',
|
|
215
|
-
whiteSpace: 'nowrap',
|
|
216
|
-
maxWidth: 240,
|
|
217
|
-
borderBottom: '1px solid var(--ds-gray-alpha-400)',
|
|
218
|
-
marginBottom: 4,
|
|
219
|
-
}}
|
|
220
|
-
>
|
|
221
|
-
{menu.spanName}
|
|
222
|
-
</div>
|
|
223
|
-
{items.map((item) => (
|
|
224
|
-
<button
|
|
225
|
-
key={item.label}
|
|
226
|
-
type="button"
|
|
227
|
-
style={{
|
|
228
|
-
outline: 'none',
|
|
229
|
-
cursor: item.disabled ? 'not-allowed' : 'pointer',
|
|
230
|
-
display: 'flex',
|
|
231
|
-
alignItems: 'center',
|
|
232
|
-
gap: 8,
|
|
233
|
-
padding: '0 var(--geist-space-2x, 8px)',
|
|
234
|
-
height: 40,
|
|
235
|
-
textDecoration: 'none',
|
|
236
|
-
borderRadius: 6,
|
|
237
|
-
color: item.destructive
|
|
238
|
-
? 'var(--ds-red-900)'
|
|
239
|
-
: 'var(--ds-gray-1000)',
|
|
240
|
-
width: '100%',
|
|
241
|
-
background: 'transparent',
|
|
242
|
-
border: 'none',
|
|
243
|
-
fontSize: 14,
|
|
244
|
-
textAlign: 'left',
|
|
245
|
-
opacity: item.disabled ? 0.4 : 1,
|
|
246
|
-
transition: 'background 0.15s',
|
|
247
|
-
}}
|
|
248
|
-
disabled={item.disabled}
|
|
249
|
-
onMouseEnter={(e) => {
|
|
250
|
-
(e.currentTarget as HTMLButtonElement).style.background =
|
|
251
|
-
'var(--ds-gray-alpha-100)';
|
|
252
|
-
}}
|
|
253
|
-
onMouseLeave={(e) => {
|
|
254
|
-
(e.currentTarget as HTMLButtonElement).style.background =
|
|
255
|
-
'transparent';
|
|
256
|
-
}}
|
|
257
|
-
onClick={() => {
|
|
258
|
-
item.action();
|
|
259
|
-
onClose();
|
|
260
|
-
}}
|
|
261
|
-
>
|
|
262
|
-
{item.icon ?? null}
|
|
263
|
-
{item.label}
|
|
264
|
-
</button>
|
|
265
|
-
))}
|
|
266
|
-
</div>,
|
|
267
|
-
document.body
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/** Inner wrapper that has access to the TraceViewer context */
|
|
272
|
-
function TraceViewerWithContextMenu({
|
|
273
|
-
trace,
|
|
274
|
-
run,
|
|
275
|
-
isLive,
|
|
276
|
-
onWakeUpSleep,
|
|
277
|
-
onCancelRun,
|
|
278
|
-
onResolveHook,
|
|
279
|
-
onLoadMoreSpans,
|
|
280
|
-
hasMoreSpans = false,
|
|
281
|
-
isLoadingMoreSpans = false,
|
|
282
|
-
children,
|
|
283
|
-
}: {
|
|
284
|
-
trace: { spans: Span[] };
|
|
285
|
-
run: WorkflowRun;
|
|
286
|
-
isLive: boolean;
|
|
287
|
-
onWakeUpSleep?: (
|
|
288
|
-
runId: string,
|
|
289
|
-
correlationId: string
|
|
290
|
-
) => Promise<{ stoppedCount: number }>;
|
|
291
|
-
onCancelRun?: (runId: string) => Promise<void>;
|
|
292
|
-
onResolveHook?: (
|
|
293
|
-
hookToken: string,
|
|
294
|
-
payload: unknown,
|
|
295
|
-
hook?: Hook
|
|
296
|
-
) => Promise<void>;
|
|
297
|
-
onLoadMoreSpans?: () => void | Promise<void>;
|
|
298
|
-
hasMoreSpans?: boolean;
|
|
299
|
-
isLoadingMoreSpans?: boolean;
|
|
300
|
-
children: ReactNode;
|
|
301
|
-
}): ReactNode {
|
|
302
|
-
const toast = useToast();
|
|
303
|
-
const { state, dispatch } = useTraceViewer();
|
|
304
|
-
|
|
305
|
-
// Drive active span widths at 60fps without React re-renders
|
|
306
|
-
useLiveTick(isLive);
|
|
307
|
-
const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
|
|
308
|
-
const [resolveHookTarget, setResolveHookTarget] = useState<{
|
|
309
|
-
hookId: string;
|
|
310
|
-
token: string;
|
|
311
|
-
} | null>(null);
|
|
312
|
-
const [resolvingHook, setResolvingHook] = useState(false);
|
|
313
|
-
// Track hooks resolved in this session so the context menu item hides immediately
|
|
314
|
-
const [resolvedHookIds, setResolvedHookIds] = useState<Set<string>>(
|
|
315
|
-
new Set()
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
// Build a lookup map: spanId -> span
|
|
319
|
-
const spanLookup = useMemo(() => {
|
|
320
|
-
const map = new Map<string, Span>();
|
|
321
|
-
for (const span of trace.spans) {
|
|
322
|
-
map.set(span.spanId, span);
|
|
323
|
-
}
|
|
324
|
-
return map;
|
|
325
|
-
}, [trace.spans]);
|
|
326
|
-
|
|
327
|
-
const handleResolveHook = useCallback(
|
|
328
|
-
async (payload: unknown) => {
|
|
329
|
-
if (resolvingHook || !resolveHookTarget || !onResolveHook) return;
|
|
330
|
-
if (!resolveHookTarget.token) {
|
|
331
|
-
toast.error('Unable to resolve hook', {
|
|
332
|
-
description:
|
|
333
|
-
'Missing hook token. Try refreshing the run data and retry.',
|
|
334
|
-
});
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
try {
|
|
338
|
-
setResolvingHook(true);
|
|
339
|
-
await onResolveHook(resolveHookTarget.token, payload);
|
|
340
|
-
toast.success('Hook resolved', {
|
|
341
|
-
description: 'The payload has been sent and the hook resolved.',
|
|
342
|
-
});
|
|
343
|
-
// Mark this hook as resolved locally so the menu item hides immediately
|
|
344
|
-
setResolvedHookIds((prev) =>
|
|
345
|
-
new Set(prev).add(resolveHookTarget.hookId)
|
|
346
|
-
);
|
|
347
|
-
setResolveHookTarget(null);
|
|
348
|
-
} catch (err) {
|
|
349
|
-
console.error('Failed to resolve hook:', err);
|
|
350
|
-
toast.error('Failed to resolve hook', {
|
|
351
|
-
description:
|
|
352
|
-
err instanceof Error ? err.message : 'An unknown error occurred',
|
|
353
|
-
});
|
|
354
|
-
} finally {
|
|
355
|
-
setResolvingHook(false);
|
|
356
|
-
}
|
|
357
|
-
},
|
|
358
|
-
[onResolveHook, resolveHookTarget, resolvingHook]
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
const handleContextMenu = useCallback(
|
|
362
|
-
(e: ReactMouseEvent | MouseEvent): void => {
|
|
363
|
-
const target = e.target as HTMLElement;
|
|
364
|
-
const $button = target.closest<HTMLButtonElement>('[data-span-id]');
|
|
365
|
-
if (!$button) return;
|
|
366
|
-
|
|
367
|
-
const spanId = $button.dataset.spanId;
|
|
368
|
-
if (!spanId) return;
|
|
369
|
-
|
|
370
|
-
e.preventDefault();
|
|
371
|
-
e.stopPropagation();
|
|
372
|
-
|
|
373
|
-
const span = spanLookup.get(spanId);
|
|
374
|
-
if (!span) return;
|
|
375
|
-
|
|
376
|
-
const resourceType =
|
|
377
|
-
(span.attributes.resource as ResourceType) ?? 'unknown';
|
|
378
|
-
const spanData = span.attributes.data as
|
|
379
|
-
| Record<string, unknown>
|
|
380
|
-
| undefined;
|
|
381
|
-
const isActive = !spanData?.completedAt && !spanData?.disposedAt;
|
|
382
|
-
|
|
383
|
-
setContextMenu({
|
|
384
|
-
x: e.clientX,
|
|
385
|
-
y: e.clientY,
|
|
386
|
-
spanId,
|
|
387
|
-
spanName: span.name,
|
|
388
|
-
resourceType,
|
|
389
|
-
isActive,
|
|
390
|
-
});
|
|
391
|
-
},
|
|
392
|
-
[spanLookup]
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
396
|
-
useEffect(() => {
|
|
397
|
-
const $container = containerRef.current;
|
|
398
|
-
if (!$container) return;
|
|
399
|
-
|
|
400
|
-
const onContextMenu = (event: MouseEvent): void => {
|
|
401
|
-
handleContextMenu(event);
|
|
402
|
-
};
|
|
403
|
-
$container.addEventListener('contextmenu', onContextMenu);
|
|
404
|
-
|
|
405
|
-
return () => {
|
|
406
|
-
$container.removeEventListener('contextmenu', onContextMenu);
|
|
407
|
-
};
|
|
408
|
-
}, [handleContextMenu]);
|
|
409
|
-
|
|
410
|
-
const loadingMoreRef = useRef(false);
|
|
411
|
-
useEffect(() => {
|
|
412
|
-
const timeline = state.timelineRef.current;
|
|
413
|
-
if (!timeline || !onLoadMoreSpans || !hasMoreSpans) {
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const thresholdPx = 200;
|
|
418
|
-
const maybeLoadMore = () => {
|
|
419
|
-
if (loadingMoreRef.current || isLoadingMoreSpans || !hasMoreSpans) {
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
const remaining =
|
|
423
|
-
timeline.scrollHeight - timeline.scrollTop - timeline.clientHeight;
|
|
424
|
-
if (remaining > thresholdPx) {
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
loadingMoreRef.current = true;
|
|
429
|
-
Promise.resolve(onLoadMoreSpans()).finally(() => {
|
|
430
|
-
loadingMoreRef.current = false;
|
|
431
|
-
});
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
timeline.addEventListener('scroll', maybeLoadMore);
|
|
435
|
-
maybeLoadMore();
|
|
436
|
-
return () => {
|
|
437
|
-
timeline.removeEventListener('scroll', maybeLoadMore);
|
|
438
|
-
};
|
|
439
|
-
}, [state.timelineRef, onLoadMoreSpans, hasMoreSpans, isLoadingMoreSpans]);
|
|
440
|
-
|
|
441
|
-
const closeMenu = useCallback(() => {
|
|
442
|
-
setContextMenu(null);
|
|
443
|
-
}, []);
|
|
444
|
-
|
|
445
|
-
const getMenuItems = useCallback(
|
|
446
|
-
(menu: ContextMenuState): ContextMenuItem[] => {
|
|
447
|
-
const items: ContextMenuItem[] = [];
|
|
448
|
-
|
|
449
|
-
// Sleep-specific: Wake Up (only on active runs)
|
|
450
|
-
const isRunActive = !run.completedAt;
|
|
451
|
-
if (
|
|
452
|
-
menu.resourceType === 'sleep' &&
|
|
453
|
-
menu.isActive &&
|
|
454
|
-
isRunActive &&
|
|
455
|
-
onWakeUpSleep
|
|
456
|
-
) {
|
|
457
|
-
items.push({
|
|
458
|
-
label: 'Wake Up Sleep',
|
|
459
|
-
icon: <Clock className="h-3.5 w-3.5" />,
|
|
460
|
-
action: () => {
|
|
461
|
-
onWakeUpSleep(run.runId, menu.spanId)
|
|
462
|
-
.then((result) => {
|
|
463
|
-
if (result.stoppedCount > 0) {
|
|
464
|
-
toast.success('Sleep woken up', {
|
|
465
|
-
description: `Woke up ${String(result.stoppedCount)} sleep${result.stoppedCount > 1 ? 's' : ''}`,
|
|
466
|
-
});
|
|
467
|
-
} else {
|
|
468
|
-
toast.info('No active sleeps found', {
|
|
469
|
-
description: 'The sleep may have already completed.',
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
})
|
|
473
|
-
.catch((err: unknown) => {
|
|
474
|
-
toast.error('Failed to wake up sleep', {
|
|
475
|
-
description:
|
|
476
|
-
err instanceof Error
|
|
477
|
-
? err.message
|
|
478
|
-
: 'An unknown error occurred',
|
|
479
|
-
});
|
|
480
|
-
});
|
|
481
|
-
},
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
// Hook-specific: Resolve Hook (only on active, unresolved hooks)
|
|
486
|
-
if (menu.resourceType === 'hook' && isRunActive && onResolveHook) {
|
|
487
|
-
const span = spanLookup.get(menu.spanId);
|
|
488
|
-
const hookData = span?.attributes?.data as
|
|
489
|
-
| { token?: string; disposedAt?: unknown }
|
|
490
|
-
| undefined;
|
|
491
|
-
// Check data-level disposedAt, span events, AND local resolved state
|
|
492
|
-
const isDisposed =
|
|
493
|
-
Boolean(hookData?.disposedAt) ||
|
|
494
|
-
Boolean(span?.events?.some((e) => e.name === 'hook_disposed')) ||
|
|
495
|
-
resolvedHookIds.has(menu.spanId);
|
|
496
|
-
if (hookData?.token && !isDisposed) {
|
|
497
|
-
items.push({
|
|
498
|
-
label: 'Resolve Hook',
|
|
499
|
-
icon: <Send className="h-3.5 w-3.5" />,
|
|
500
|
-
action: () => {
|
|
501
|
-
setResolveHookTarget({
|
|
502
|
-
hookId: menu.spanId,
|
|
503
|
-
token: hookData.token!,
|
|
504
|
-
});
|
|
505
|
-
},
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// Run-specific: Cancel (only on active runs)
|
|
511
|
-
if (menu.resourceType === 'run' && isRunActive && onCancelRun) {
|
|
512
|
-
items.push({
|
|
513
|
-
label: 'Cancel Run',
|
|
514
|
-
icon: <XCircle className="h-3.5 w-3.5" />,
|
|
515
|
-
destructive: true,
|
|
516
|
-
action: () => {
|
|
517
|
-
onCancelRun(run.runId).catch((err: unknown) => {
|
|
518
|
-
toast.error('Failed to cancel run', {
|
|
519
|
-
description:
|
|
520
|
-
err instanceof Error
|
|
521
|
-
? err.message
|
|
522
|
-
: 'An unknown error occurred',
|
|
523
|
-
});
|
|
524
|
-
});
|
|
525
|
-
},
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// Common actions
|
|
530
|
-
items.push({
|
|
531
|
-
label: 'View Details',
|
|
532
|
-
icon: <Info className="h-3.5 w-3.5" />,
|
|
533
|
-
action: () => {
|
|
534
|
-
dispatch({ type: 'select', id: menu.spanId });
|
|
535
|
-
},
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
items.push({
|
|
539
|
-
label: 'Copy Name',
|
|
540
|
-
icon: <Type className="h-3.5 w-3.5" />,
|
|
541
|
-
action: () => {
|
|
542
|
-
navigator.clipboard
|
|
543
|
-
.writeText(menu.spanName)
|
|
544
|
-
.then(() => {
|
|
545
|
-
toast.success('Name copied to clipboard');
|
|
546
|
-
})
|
|
547
|
-
.catch(() => {
|
|
548
|
-
toast.error('Failed to copy name');
|
|
549
|
-
});
|
|
550
|
-
},
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
items.push({
|
|
554
|
-
label: 'Copy ID',
|
|
555
|
-
icon: <Copy className="h-3.5 w-3.5" />,
|
|
556
|
-
action: () => {
|
|
557
|
-
navigator.clipboard
|
|
558
|
-
.writeText(menu.spanId)
|
|
559
|
-
.then(() => {
|
|
560
|
-
toast.success('ID copied to clipboard');
|
|
561
|
-
})
|
|
562
|
-
.catch(() => {
|
|
563
|
-
toast.error('Failed to copy ID');
|
|
564
|
-
});
|
|
565
|
-
},
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
return items;
|
|
569
|
-
},
|
|
570
|
-
[
|
|
571
|
-
dispatch,
|
|
572
|
-
onWakeUpSleep,
|
|
573
|
-
onCancelRun,
|
|
574
|
-
onResolveHook,
|
|
575
|
-
spanLookup,
|
|
576
|
-
resolvedHookIds,
|
|
577
|
-
run.runId,
|
|
578
|
-
run.completedAt,
|
|
579
|
-
]
|
|
580
|
-
);
|
|
581
|
-
|
|
582
|
-
return (
|
|
583
|
-
<div className="relative w-full h-full" ref={containerRef}>
|
|
584
|
-
{children}
|
|
585
|
-
{contextMenu ? (
|
|
586
|
-
<SpanContextMenu
|
|
587
|
-
menu={contextMenu}
|
|
588
|
-
items={getMenuItems(contextMenu)}
|
|
589
|
-
onClose={closeMenu}
|
|
590
|
-
/>
|
|
591
|
-
) : null}
|
|
592
|
-
<ResolveHookModal
|
|
593
|
-
isOpen={resolveHookTarget !== null}
|
|
594
|
-
onClose={() => setResolveHookTarget(null)}
|
|
595
|
-
onSubmit={handleResolveHook}
|
|
596
|
-
isSubmitting={resolvingHook}
|
|
597
|
-
/>
|
|
598
|
-
</div>
|
|
599
|
-
);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/** Re-export SpanSelectionInfo for consumers */
|
|
603
|
-
export type { SpanSelectionInfo };
|
|
604
|
-
|
|
605
|
-
// ---------------------------------------------------------------------------
|
|
606
|
-
// Bridge: reads selected span from trace viewer context and notifies parent
|
|
607
|
-
// ---------------------------------------------------------------------------
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* Bridge component that lives INSIDE the TraceViewerContextProvider and
|
|
611
|
-
* reads the selected span info, passing it up to the parent via callback.
|
|
612
|
-
* This allows the parent to render the detail panel OUTSIDE the context.
|
|
613
|
-
*/
|
|
614
|
-
function SelectionBridge({
|
|
615
|
-
onSelectionChange,
|
|
616
|
-
}: {
|
|
617
|
-
onSelectionChange: (info: SelectedSpanInfo | null) => void;
|
|
618
|
-
}) {
|
|
619
|
-
const { state } = useTraceViewer();
|
|
620
|
-
const { selected } = state;
|
|
621
|
-
const onSelectionChangeRef = useRef(onSelectionChange);
|
|
622
|
-
onSelectionChangeRef.current = onSelectionChange;
|
|
623
|
-
const prevSpanIdRef = useRef<string | undefined>(undefined);
|
|
624
|
-
|
|
625
|
-
useEffect(() => {
|
|
626
|
-
const currentSpanId = selected?.span.spanId;
|
|
627
|
-
if (currentSpanId === prevSpanIdRef.current) return;
|
|
628
|
-
prevSpanIdRef.current = currentSpanId;
|
|
629
|
-
|
|
630
|
-
if (selected) {
|
|
631
|
-
onSelectionChangeRef.current({
|
|
632
|
-
data: selected.span.attributes?.data,
|
|
633
|
-
resource: selected.span.attributes?.resource as string | undefined,
|
|
634
|
-
spanId: selected.span.spanId,
|
|
635
|
-
});
|
|
636
|
-
} else {
|
|
637
|
-
onSelectionChangeRef.current(null);
|
|
638
|
-
}
|
|
639
|
-
}, [selected]);
|
|
640
|
-
|
|
641
|
-
return null;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
/**
|
|
645
|
-
* Bridge component to deselect from outside the context.
|
|
646
|
-
*/
|
|
647
|
-
function DeselectBridge({ triggerDeselect }: { triggerDeselect: number }) {
|
|
648
|
-
const { dispatch } = useTraceViewer();
|
|
649
|
-
|
|
650
|
-
useEffect(() => {
|
|
651
|
-
if (triggerDeselect > 0) {
|
|
652
|
-
dispatch({ type: 'deselect' });
|
|
653
|
-
}
|
|
654
|
-
}, [triggerDeselect, dispatch]);
|
|
655
|
-
|
|
656
|
-
return null;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* Bridge component to select a span from outside the context.
|
|
661
|
-
*/
|
|
662
|
-
function SelectBridge({
|
|
663
|
-
selectRequest,
|
|
664
|
-
}: {
|
|
665
|
-
selectRequest: { id: string; token: number } | null;
|
|
666
|
-
}) {
|
|
667
|
-
const { dispatch } = useTraceViewer();
|
|
668
|
-
|
|
669
|
-
useEffect(() => {
|
|
670
|
-
if (selectRequest?.id) {
|
|
671
|
-
dispatch({ type: 'select', id: selectRequest.id });
|
|
672
|
-
}
|
|
673
|
-
}, [dispatch, selectRequest]);
|
|
674
|
-
|
|
675
|
-
return null;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// ---------------------------------------------------------------------------
|
|
679
|
-
// Panel chrome (header with name/duration, close button)
|
|
680
|
-
// ---------------------------------------------------------------------------
|
|
681
|
-
|
|
682
|
-
function PanelResizeHandle({
|
|
683
|
-
onResize,
|
|
684
|
-
}: {
|
|
685
|
-
onResize: (deltaX: number) => void;
|
|
686
|
-
}) {
|
|
687
|
-
const handlePointerDown = useCallback(
|
|
688
|
-
(e: React.PointerEvent) => {
|
|
689
|
-
e.preventDefault();
|
|
690
|
-
let lastX = e.clientX;
|
|
691
|
-
const onPointerMove = (moveEvent: PointerEvent) => {
|
|
692
|
-
const deltaX = lastX - moveEvent.clientX;
|
|
693
|
-
lastX = moveEvent.clientX;
|
|
694
|
-
onResize(deltaX);
|
|
695
|
-
};
|
|
696
|
-
const onPointerUp = () => {
|
|
697
|
-
document.removeEventListener('pointermove', onPointerMove);
|
|
698
|
-
document.removeEventListener('pointerup', onPointerUp);
|
|
699
|
-
};
|
|
700
|
-
document.addEventListener('pointermove', onPointerMove);
|
|
701
|
-
document.addEventListener('pointerup', onPointerUp);
|
|
702
|
-
},
|
|
703
|
-
[onResize]
|
|
704
|
-
);
|
|
705
|
-
|
|
706
|
-
return (
|
|
707
|
-
<div
|
|
708
|
-
className="absolute left-0 top-0 bottom-0 w-1 cursor-col-resize hover:bg-blue-400/50 z-10"
|
|
709
|
-
onPointerDown={handlePointerDown}
|
|
710
|
-
/>
|
|
711
|
-
);
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
// ---------------------------------------------------------------------------
|
|
715
|
-
// Trace viewer footer — loading / waiting indicator
|
|
716
|
-
// ---------------------------------------------------------------------------
|
|
717
|
-
|
|
718
|
-
function TraceViewerFooter({
|
|
719
|
-
hasMore,
|
|
720
|
-
isLive,
|
|
721
|
-
isInitialLoading,
|
|
722
|
-
}: {
|
|
723
|
-
hasMore: boolean;
|
|
724
|
-
isLive: boolean;
|
|
725
|
-
isInitialLoading: boolean;
|
|
726
|
-
}): ReactNode {
|
|
727
|
-
const style = { color: 'var(--ds-gray-900)' };
|
|
728
|
-
if (hasMore || isInitialLoading) {
|
|
729
|
-
return (
|
|
730
|
-
<div
|
|
731
|
-
className="flex items-center justify-center gap-2 py-3 text-xs"
|
|
732
|
-
style={style}
|
|
733
|
-
>
|
|
734
|
-
<Spinner size={14} />
|
|
735
|
-
Loading more events…
|
|
736
|
-
</div>
|
|
737
|
-
);
|
|
738
|
-
}
|
|
739
|
-
if (isLive) {
|
|
740
|
-
return (
|
|
741
|
-
<div
|
|
742
|
-
className="flex items-center justify-center py-3 text-xs"
|
|
743
|
-
style={style}
|
|
744
|
-
>
|
|
745
|
-
Waiting for more events…
|
|
746
|
-
</div>
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
|
-
return (
|
|
750
|
-
<div
|
|
751
|
-
className="flex items-center justify-center py-3 text-xs"
|
|
752
|
-
style={style}
|
|
753
|
-
>
|
|
754
|
-
End of run
|
|
755
|
-
</div>
|
|
756
|
-
);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
// ---------------------------------------------------------------------------
|
|
760
|
-
// Main component
|
|
761
|
-
// ---------------------------------------------------------------------------
|
|
762
|
-
|
|
763
|
-
export const WorkflowTraceViewer = ({
|
|
764
|
-
run,
|
|
765
|
-
events,
|
|
766
|
-
isLoading,
|
|
767
|
-
error,
|
|
768
|
-
fetchSpanDetail,
|
|
769
|
-
onWakeUpSleep,
|
|
770
|
-
onResolveHook,
|
|
771
|
-
onCancelRun,
|
|
772
|
-
onStreamClick,
|
|
773
|
-
onRunClick,
|
|
774
|
-
onLoadEventData,
|
|
775
|
-
onLoadMoreSpans,
|
|
776
|
-
hasMoreSpans = false,
|
|
777
|
-
isLoadingMoreSpans = false,
|
|
778
|
-
encryptionKey,
|
|
779
|
-
onDecrypt,
|
|
780
|
-
isDecrypting = false,
|
|
781
|
-
showSeparateEventOccurrenceTimestamps = false,
|
|
782
|
-
}: {
|
|
783
|
-
run: WorkflowRun;
|
|
784
|
-
events: Event[];
|
|
785
|
-
isLoading?: boolean;
|
|
786
|
-
error?: Error | null;
|
|
787
|
-
fetchSpanDetail: FetchSpanDetail;
|
|
788
|
-
onWakeUpSleep?: (
|
|
789
|
-
runId: string,
|
|
790
|
-
correlationId: string
|
|
791
|
-
) => Promise<{ stoppedCount: number }>;
|
|
792
|
-
onResolveHook?: (
|
|
793
|
-
hookToken: string,
|
|
794
|
-
payload: unknown,
|
|
795
|
-
hook?: Hook
|
|
796
|
-
) => Promise<void>;
|
|
797
|
-
/** Callback to cancel the current run */
|
|
798
|
-
onCancelRun?: (runId: string) => Promise<void>;
|
|
799
|
-
/** Callback when a stream reference is clicked in the detail panel */
|
|
800
|
-
onStreamClick?: (streamId: string) => void;
|
|
801
|
-
/** Callback when a run reference is clicked in the detail panel */
|
|
802
|
-
onRunClick?: (runId: string) => void;
|
|
803
|
-
/** Callback to load event data for a specific event (lazy loading in sidebar) */
|
|
804
|
-
onLoadEventData?: (event: Event) => Promise<unknown | null>;
|
|
805
|
-
/** Load next trace page when vertical scroll reaches bottom. */
|
|
806
|
-
onLoadMoreSpans?: () => void | Promise<void>;
|
|
807
|
-
/** Whether trace pagination has more data to load. */
|
|
808
|
-
hasMoreSpans?: boolean;
|
|
809
|
-
/** Whether trace pagination is currently fetching another page. */
|
|
810
|
-
isLoadingMoreSpans?: boolean;
|
|
811
|
-
/** Encryption key (available after Decrypt), threaded to event list for re-loading */
|
|
812
|
-
encryptionKey?: Uint8Array;
|
|
813
|
-
/** Callback to initiate decryption of encrypted run data */
|
|
814
|
-
onDecrypt?: () => void;
|
|
815
|
-
/** Whether the encryption key is currently being fetched */
|
|
816
|
-
isDecrypting?: boolean;
|
|
817
|
-
/** Show occurredAt separately instead of folding it into the Created timestamp. */
|
|
818
|
-
showSeparateEventOccurrenceTimestamps?: boolean;
|
|
819
|
-
}) => {
|
|
820
|
-
const toast = useToast();
|
|
821
|
-
const [selectedSpan, setSelectedSpan] = useState<SelectedSpanInfo | null>(
|
|
822
|
-
null
|
|
823
|
-
);
|
|
824
|
-
const [panelWidth, setPanelWidth] = useState(DEFAULT_PANEL_WIDTH);
|
|
825
|
-
const [deselectTrigger, setDeselectTrigger] = useState(0);
|
|
826
|
-
const [selectRequest, setSelectRequest] = useState<{
|
|
827
|
-
id: string;
|
|
828
|
-
token: number;
|
|
829
|
-
} | null>(null);
|
|
830
|
-
|
|
831
|
-
const isLive = Boolean(run && !run.completedAt);
|
|
832
|
-
|
|
833
|
-
// Build trace only when actual data changes — no timer-driven rebuilds.
|
|
834
|
-
// Active span widths are animated imperatively by useLiveTick at 60fps.
|
|
835
|
-
const traceWithMeta: TraceWithMeta | undefined = useMemo(() => {
|
|
836
|
-
if (!run?.runId) {
|
|
837
|
-
return undefined;
|
|
838
|
-
}
|
|
839
|
-
return buildTrace(run, events, new Date());
|
|
840
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- `new Date()` is intentionally not a dep; useLiveTick handles live growth
|
|
841
|
-
}, [run, events]);
|
|
842
|
-
const trace = traceWithMeta;
|
|
843
|
-
|
|
844
|
-
useEffect(() => {
|
|
845
|
-
if (error && !isLoading) {
|
|
846
|
-
console.error(error);
|
|
847
|
-
toast.error('Error loading workflow trace data', {
|
|
848
|
-
description: error.message,
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
}, [error, isLoading]);
|
|
852
|
-
|
|
853
|
-
const handleSelectionChange = useCallback(
|
|
854
|
-
(info: SelectedSpanInfo | null) => {
|
|
855
|
-
if (info) {
|
|
856
|
-
// Filter raw events for the selected span: child spans match on
|
|
857
|
-
// correlationId (stepId/hookId), the run root span gets run-level
|
|
858
|
-
// events. This bypasses the trace worker pipeline entirely.
|
|
859
|
-
const rawEvents = filterSpanRawEvents(
|
|
860
|
-
events,
|
|
861
|
-
info.resource,
|
|
862
|
-
info.spanId
|
|
863
|
-
);
|
|
864
|
-
setSelectedSpan({ ...info, rawEvents });
|
|
865
|
-
} else {
|
|
866
|
-
setSelectedSpan(null);
|
|
867
|
-
}
|
|
868
|
-
},
|
|
869
|
-
[events]
|
|
870
|
-
);
|
|
871
|
-
|
|
872
|
-
// Keep selected span raw events live as polling updates arrive.
|
|
873
|
-
useEffect(() => {
|
|
874
|
-
const correlationId = selectedSpan?.spanId;
|
|
875
|
-
if (!correlationId) return;
|
|
876
|
-
|
|
877
|
-
const nextRawEvents = filterSpanRawEvents(
|
|
878
|
-
events,
|
|
879
|
-
selectedSpan?.resource,
|
|
880
|
-
correlationId
|
|
881
|
-
);
|
|
882
|
-
setSelectedSpan((prev) => {
|
|
883
|
-
if (!prev || prev.spanId !== correlationId) {
|
|
884
|
-
return prev;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
const prevRawEvents = prev.rawEvents ?? [];
|
|
888
|
-
const isSame =
|
|
889
|
-
prevRawEvents.length === nextRawEvents.length &&
|
|
890
|
-
prevRawEvents.every(
|
|
891
|
-
(event, index) => event.eventId === nextRawEvents[index]?.eventId
|
|
892
|
-
);
|
|
893
|
-
if (isSame) {
|
|
894
|
-
return prev;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
return {
|
|
898
|
-
...prev,
|
|
899
|
-
rawEvents: nextRawEvents,
|
|
900
|
-
};
|
|
901
|
-
});
|
|
902
|
-
}, [events, selectedSpan?.spanId, selectedSpan?.resource]);
|
|
903
|
-
|
|
904
|
-
// Reset selected span when navigating to a different run
|
|
905
|
-
useEffect(() => {
|
|
906
|
-
setSelectedSpan(null);
|
|
907
|
-
setDeselectTrigger((n) => n + 1);
|
|
908
|
-
}, [run?.runId]);
|
|
909
|
-
|
|
910
|
-
const handleClose = useCallback(() => {
|
|
911
|
-
setSelectedSpan(null);
|
|
912
|
-
setDeselectTrigger((n) => n + 1);
|
|
913
|
-
}, []);
|
|
914
|
-
|
|
915
|
-
const handleResize = useCallback((deltaX: number) => {
|
|
916
|
-
setPanelWidth((w) => Math.max(MIN_PANEL_WIDTH, w + deltaX));
|
|
917
|
-
}, []);
|
|
918
|
-
const selectedSpanIndex = useMemo(() => {
|
|
919
|
-
if (!selectedSpan?.spanId || !trace) return -1;
|
|
920
|
-
return trace.spans.findIndex((span) => span.spanId === selectedSpan.spanId);
|
|
921
|
-
}, [selectedSpan?.spanId, trace]);
|
|
922
|
-
const canSelectPrevSpan = selectedSpanIndex > 0;
|
|
923
|
-
const canSelectNextSpan =
|
|
924
|
-
selectedSpanIndex >= 0 &&
|
|
925
|
-
trace != null &&
|
|
926
|
-
selectedSpanIndex < trace.spans.length - 1;
|
|
927
|
-
const handleSelectPrevSpan = useCallback(() => {
|
|
928
|
-
if (!trace || selectedSpanIndex <= 0) return;
|
|
929
|
-
const targetId = trace.spans[selectedSpanIndex - 1]?.spanId;
|
|
930
|
-
if (!targetId) return;
|
|
931
|
-
setSelectRequest({ id: targetId, token: Date.now() });
|
|
932
|
-
}, [selectedSpanIndex, trace]);
|
|
933
|
-
const handleSelectNextSpan = useCallback(() => {
|
|
934
|
-
if (!trace || selectedSpanIndex < 0) return;
|
|
935
|
-
const targetId = trace.spans[selectedSpanIndex + 1]?.spanId;
|
|
936
|
-
if (!targetId) return;
|
|
937
|
-
setSelectRequest({ id: targetId, token: Date.now() });
|
|
938
|
-
}, [selectedSpanIndex, trace]);
|
|
939
|
-
|
|
940
|
-
// Get the selected span name for the panel header
|
|
941
|
-
const selectedSpanName = useMemo(() => {
|
|
942
|
-
if (!selectedSpan?.data) return undefined;
|
|
943
|
-
const data = selectedSpan.data as Record<string, unknown>;
|
|
944
|
-
const stepName = data.stepName as string | undefined;
|
|
945
|
-
const workflowName = data.workflowName as string | undefined;
|
|
946
|
-
return (
|
|
947
|
-
(stepName ? parseStepName(stepName)?.shortName : undefined) ??
|
|
948
|
-
(workflowName ? parseWorkflowName(workflowName)?.shortName : undefined) ??
|
|
949
|
-
stepName ??
|
|
950
|
-
workflowName ??
|
|
951
|
-
(data.hookId as string) ??
|
|
952
|
-
'Details'
|
|
953
|
-
);
|
|
954
|
-
}, [selectedSpan?.data]);
|
|
955
|
-
|
|
956
|
-
if (!trace) {
|
|
957
|
-
return (
|
|
958
|
-
<div className="relative w-full h-full">
|
|
959
|
-
<div className="border-b border-gray-alpha-400 w-full" />
|
|
960
|
-
<Skeleton className="w-full ml-2 mt-1 mb-1 h-[56px]" />
|
|
961
|
-
<div className="p-2 relative w-full">
|
|
962
|
-
<Skeleton className="w-full mt-6 h-[20px]" />
|
|
963
|
-
<Skeleton className="w-[10%] mt-2 ml-6 h-[20px]" />
|
|
964
|
-
<Skeleton className="w-[10%] mt-2 ml-12 h-[20px]" />
|
|
965
|
-
<Skeleton className="w-[20%] mt-2 ml-16 h-[20px]" />
|
|
966
|
-
</div>
|
|
967
|
-
</div>
|
|
968
|
-
);
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
return (
|
|
972
|
-
<div className="relative w-full h-full flex flex-row">
|
|
973
|
-
{/* Timeline (takes remaining space) */}
|
|
974
|
-
<div className="flex-1 min-w-0 relative">
|
|
975
|
-
<TraceViewerContextProvider
|
|
976
|
-
customSpanClassNameFunc={getCustomSpanClassName}
|
|
977
|
-
customSpanEventClassNameFunc={getCustomSpanEventClassName}
|
|
978
|
-
>
|
|
979
|
-
<SelectionBridge onSelectionChange={handleSelectionChange} />
|
|
980
|
-
<DeselectBridge triggerDeselect={deselectTrigger} />
|
|
981
|
-
<SelectBridge selectRequest={selectRequest} />
|
|
982
|
-
<TraceViewerWithContextMenu
|
|
983
|
-
trace={trace}
|
|
984
|
-
run={run}
|
|
985
|
-
isLive={isLive}
|
|
986
|
-
onWakeUpSleep={onWakeUpSleep}
|
|
987
|
-
onCancelRun={onCancelRun}
|
|
988
|
-
onResolveHook={onResolveHook}
|
|
989
|
-
onLoadMoreSpans={onLoadMoreSpans}
|
|
990
|
-
hasMoreSpans={hasMoreSpans}
|
|
991
|
-
isLoadingMoreSpans={isLoadingMoreSpans}
|
|
992
|
-
>
|
|
993
|
-
<TraceViewerTimeline
|
|
994
|
-
eagerRender
|
|
995
|
-
height="100%"
|
|
996
|
-
isLive={isLive}
|
|
997
|
-
trace={trace}
|
|
998
|
-
knownDurationMs={traceWithMeta?.knownDurationMs}
|
|
999
|
-
hasMoreData={hasMoreSpans || Boolean(isLoading)}
|
|
1000
|
-
footer={
|
|
1001
|
-
<TraceViewerFooter
|
|
1002
|
-
hasMore={hasMoreSpans}
|
|
1003
|
-
isLive={isLive}
|
|
1004
|
-
isInitialLoading={Boolean(isLoading)}
|
|
1005
|
-
/>
|
|
1006
|
-
}
|
|
1007
|
-
/>
|
|
1008
|
-
</TraceViewerWithContextMenu>
|
|
1009
|
-
</TraceViewerContextProvider>
|
|
1010
|
-
</div>
|
|
1011
|
-
|
|
1012
|
-
{/* Detail panel (rendered outside the context, as a sibling) */}
|
|
1013
|
-
{selectedSpan && (
|
|
1014
|
-
<div
|
|
1015
|
-
className="relative border-l flex-shrink-0 flex flex-col"
|
|
1016
|
-
style={{
|
|
1017
|
-
width: panelWidth,
|
|
1018
|
-
borderColor: 'var(--ds-gray-200)',
|
|
1019
|
-
backgroundColor: 'var(--ds-background-100)',
|
|
1020
|
-
}}
|
|
1021
|
-
>
|
|
1022
|
-
<PanelResizeHandle onResize={handleResize} />
|
|
1023
|
-
{/* Panel header */}
|
|
1024
|
-
<div
|
|
1025
|
-
className="flex items-center justify-between px-3 py-2 border-y flex-shrink-0"
|
|
1026
|
-
style={{ borderColor: 'var(--ds-gray-200)' }}
|
|
1027
|
-
>
|
|
1028
|
-
<div className="min-w-0 flex-1">
|
|
1029
|
-
<div
|
|
1030
|
-
className="text-sm font-medium truncate"
|
|
1031
|
-
style={{ color: 'var(--ds-gray-1000)' }}
|
|
1032
|
-
title={selectedSpanName}
|
|
1033
|
-
>
|
|
1034
|
-
{selectedSpanName}
|
|
1035
|
-
</div>
|
|
1036
|
-
</div>
|
|
1037
|
-
<div className="flex items-center">
|
|
1038
|
-
<button
|
|
1039
|
-
type="button"
|
|
1040
|
-
aria-label="Previous span"
|
|
1041
|
-
onClick={handleSelectPrevSpan}
|
|
1042
|
-
disabled={!canSelectPrevSpan}
|
|
1043
|
-
style={{
|
|
1044
|
-
display: 'flex',
|
|
1045
|
-
alignItems: 'center',
|
|
1046
|
-
justifyContent: 'center',
|
|
1047
|
-
marginLeft: 6,
|
|
1048
|
-
width: 24,
|
|
1049
|
-
height: 24,
|
|
1050
|
-
padding: 0,
|
|
1051
|
-
borderRadius: 6,
|
|
1052
|
-
border: 'none',
|
|
1053
|
-
background: 'transparent',
|
|
1054
|
-
color: 'var(--ds-gray-700)',
|
|
1055
|
-
cursor: canSelectPrevSpan ? 'pointer' : 'not-allowed',
|
|
1056
|
-
opacity: canSelectPrevSpan ? 1 : 0.45,
|
|
1057
|
-
flexShrink: 0,
|
|
1058
|
-
transition: 'background 0.15s',
|
|
1059
|
-
}}
|
|
1060
|
-
onMouseEnter={(e) => {
|
|
1061
|
-
if (!canSelectPrevSpan) return;
|
|
1062
|
-
e.currentTarget.style.background = 'var(--ds-gray-alpha-100)';
|
|
1063
|
-
}}
|
|
1064
|
-
onMouseLeave={(e) => {
|
|
1065
|
-
e.currentTarget.style.background = 'transparent';
|
|
1066
|
-
}}
|
|
1067
|
-
>
|
|
1068
|
-
<ChevronUp size={16} />
|
|
1069
|
-
</button>
|
|
1070
|
-
<button
|
|
1071
|
-
type="button"
|
|
1072
|
-
aria-label="Next span"
|
|
1073
|
-
onClick={handleSelectNextSpan}
|
|
1074
|
-
disabled={!canSelectNextSpan}
|
|
1075
|
-
style={{
|
|
1076
|
-
display: 'flex',
|
|
1077
|
-
alignItems: 'center',
|
|
1078
|
-
justifyContent: 'center',
|
|
1079
|
-
marginLeft: 2,
|
|
1080
|
-
width: 24,
|
|
1081
|
-
height: 24,
|
|
1082
|
-
padding: 0,
|
|
1083
|
-
borderRadius: 6,
|
|
1084
|
-
border: 'none',
|
|
1085
|
-
background: 'transparent',
|
|
1086
|
-
color: 'var(--ds-gray-700)',
|
|
1087
|
-
cursor: canSelectNextSpan ? 'pointer' : 'not-allowed',
|
|
1088
|
-
opacity: canSelectNextSpan ? 1 : 0.45,
|
|
1089
|
-
flexShrink: 0,
|
|
1090
|
-
transition: 'background 0.15s',
|
|
1091
|
-
}}
|
|
1092
|
-
onMouseEnter={(e) => {
|
|
1093
|
-
if (!canSelectNextSpan) return;
|
|
1094
|
-
e.currentTarget.style.background = 'var(--ds-gray-alpha-100)';
|
|
1095
|
-
}}
|
|
1096
|
-
onMouseLeave={(e) => {
|
|
1097
|
-
e.currentTarget.style.background = 'transparent';
|
|
1098
|
-
}}
|
|
1099
|
-
>
|
|
1100
|
-
<ChevronDown size={16} />
|
|
1101
|
-
</button>
|
|
1102
|
-
</div>
|
|
1103
|
-
<div
|
|
1104
|
-
className="flex items-center"
|
|
1105
|
-
style={{
|
|
1106
|
-
borderLeft: '1px solid var(--ds-gray-300)',
|
|
1107
|
-
marginLeft: 6,
|
|
1108
|
-
}}
|
|
1109
|
-
>
|
|
1110
|
-
<button
|
|
1111
|
-
type="button"
|
|
1112
|
-
aria-label="Close panel"
|
|
1113
|
-
onClick={handleClose}
|
|
1114
|
-
style={{
|
|
1115
|
-
display: 'flex',
|
|
1116
|
-
alignItems: 'center',
|
|
1117
|
-
justifyContent: 'center',
|
|
1118
|
-
marginLeft: 6,
|
|
1119
|
-
width: 24,
|
|
1120
|
-
height: 24,
|
|
1121
|
-
padding: 0,
|
|
1122
|
-
borderRadius: 6,
|
|
1123
|
-
border: 'none',
|
|
1124
|
-
background: 'transparent',
|
|
1125
|
-
color: 'var(--ds-gray-700)',
|
|
1126
|
-
cursor: 'pointer',
|
|
1127
|
-
flexShrink: 0,
|
|
1128
|
-
transition: 'background 0.15s',
|
|
1129
|
-
}}
|
|
1130
|
-
onMouseEnter={(e) => {
|
|
1131
|
-
e.currentTarget.style.background = 'var(--ds-gray-alpha-100)';
|
|
1132
|
-
}}
|
|
1133
|
-
onMouseLeave={(e) => {
|
|
1134
|
-
e.currentTarget.style.background = 'transparent';
|
|
1135
|
-
}}
|
|
1136
|
-
>
|
|
1137
|
-
<X size={16} />
|
|
1138
|
-
</button>
|
|
1139
|
-
</div>
|
|
1140
|
-
</div>
|
|
1141
|
-
{/* Panel body */}
|
|
1142
|
-
<div className="flex-1 overflow-y-auto">
|
|
1143
|
-
<ErrorBoundary title="Failed to load entity details">
|
|
1144
|
-
<EntityDetailPanel
|
|
1145
|
-
run={run}
|
|
1146
|
-
onStreamClick={onStreamClick}
|
|
1147
|
-
onRunClick={onRunClick}
|
|
1148
|
-
fetchSpanDetail={fetchSpanDetail}
|
|
1149
|
-
onWakeUpSleep={onWakeUpSleep}
|
|
1150
|
-
onLoadEventData={onLoadEventData}
|
|
1151
|
-
onResolveHook={onResolveHook}
|
|
1152
|
-
encryptionKey={encryptionKey}
|
|
1153
|
-
onDecrypt={onDecrypt}
|
|
1154
|
-
isDecrypting={isDecrypting}
|
|
1155
|
-
selectedSpan={selectedSpan}
|
|
1156
|
-
showSeparateEventOccurrenceTimestamps={
|
|
1157
|
-
showSeparateEventOccurrenceTimestamps
|
|
1158
|
-
}
|
|
1159
|
-
/>
|
|
1160
|
-
</ErrorBoundary>
|
|
1161
|
-
</div>
|
|
1162
|
-
</div>
|
|
1163
|
-
)}
|
|
1164
|
-
</div>
|
|
1165
|
-
);
|
|
1166
|
-
};
|