@workflow/web-shared 5.0.0-beta.2 → 5.0.0-beta.21
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/dist/components/error-boundary.js +1 -1
- package/dist/components/event-list-view.d.ts +16 -1
- package/dist/components/event-list-view.d.ts.map +1 -1
- package/dist/components/event-list-view.js +196 -85
- package/dist/components/hook-actions.js +1 -1
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +5 -1
- package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
- package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
- package/dist/components/new-trace-viewer/components/event-list.d.ts +10 -0
- package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/event-list.js +46 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts +18 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +54 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts +20 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +25 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +101 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts +20 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js +114 -0
- package/dist/components/new-trace-viewer/components/span-markers.d.ts +46 -0
- package/dist/components/new-trace-viewer/components/span-markers.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/span-markers.js +85 -0
- package/dist/components/new-trace-viewer/components/split-pane.d.ts +15 -0
- package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/split-pane.js +105 -0
- package/dist/components/new-trace-viewer/components/timeline.d.ts +36 -0
- package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/timeline.js +254 -0
- package/dist/components/new-trace-viewer/components/timeline.module.css +46 -0
- package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts +2 -0
- package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.js +19 -0
- package/dist/components/new-trace-viewer/components/use-row-window.d.ts +34 -0
- package/dist/components/new-trace-viewer/components/use-row-window.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/use-row-window.js +107 -0
- package/dist/components/new-trace-viewer/context.d.ts +15 -0
- package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/context.js +47 -0
- package/dist/components/new-trace-viewer/icons.d.ts +6 -0
- package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/icons.js +15 -0
- package/dist/components/new-trace-viewer/search.d.ts +19 -0
- package/dist/components/new-trace-viewer/search.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/search.js +134 -0
- package/dist/components/new-trace-viewer/trace-viewer.d.ts +11 -0
- package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/trace-viewer.js +462 -0
- package/dist/components/new-trace-viewer/types.d.ts +3 -0
- package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/types.js +2 -0
- package/dist/components/new-trace-viewer/utils.d.ts +61 -0
- package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/utils.js +345 -0
- package/dist/components/run-trace-view.js +1 -1
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
- package/dist/components/sidebar/attribute-panel.js +129 -102
- package/dist/components/sidebar/attributes-block.d.ts +28 -0
- package/dist/components/sidebar/attributes-block.d.ts.map +1 -0
- package/dist/components/sidebar/attributes-block.js +81 -0
- package/dist/components/sidebar/conversation-view.js +1 -1
- package/dist/components/sidebar/copyable-data-block.d.ts +1 -0
- package/dist/components/sidebar/copyable-data-block.d.ts.map +1 -1
- package/dist/components/sidebar/copyable-data-block.js +16 -15
- package/dist/components/sidebar/detail-card.d.ts +5 -6
- package/dist/components/sidebar/detail-card.d.ts.map +1 -1
- package/dist/components/sidebar/detail-card.js +32 -16
- package/dist/components/sidebar/entity-detail-panel.d.ts +1 -3
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.js +17 -35
- package/dist/components/sidebar/events-list.d.ts +3 -1
- package/dist/components/sidebar/events-list.d.ts.map +1 -1
- package/dist/components/sidebar/events-list.js +36 -22
- package/dist/components/sidebar/resolve-hook-modal.js +1 -1
- package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
- package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
- package/dist/components/sidebar/sidebar-data-context.js +19 -0
- package/dist/components/sidebar/span-detail-merge.d.ts +16 -0
- package/dist/components/sidebar/span-detail-merge.d.ts.map +1 -0
- package/dist/components/sidebar/span-detail-merge.js +53 -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 +3 -27
- package/dist/components/trace-viewer/components/map.js +1 -1
- package/dist/components/trace-viewer/components/markers.js +1 -1
- package/dist/components/trace-viewer/components/node.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/node.js +2 -1
- package/dist/components/trace-viewer/components/search-input.js +1 -1
- package/dist/components/trace-viewer/components/search.js +1 -1
- package/dist/components/trace-viewer/components/span-content.js +1 -1
- package/dist/components/trace-viewer/components/span-detail-panel.js +1 -1
- package/dist/components/trace-viewer/components/span-segments.js +1 -1
- package/dist/components/trace-viewer/components/span-strategies.js +1 -1
- package/dist/components/trace-viewer/components/ui.js +1 -1
- package/dist/components/trace-viewer/components/zoom-button.js +1 -1
- package/dist/components/trace-viewer/components/zoom-icons.js +1 -1
- package/dist/components/trace-viewer/context.js +1 -1
- package/dist/components/trace-viewer/index.js +1 -1
- package/dist/components/trace-viewer/trace-viewer.js +1 -1
- package/dist/components/trace-viewer/trace-viewer.module.css +4 -4
- package/dist/components/trace-viewer/types.js +1 -1
- package/dist/components/trace-viewer/util/constants.js +1 -1
- package/dist/components/trace-viewer/util/scrollbar-width.js +1 -1
- package/dist/components/trace-viewer/util/timing.d.ts +3 -3
- package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
- package/dist/components/trace-viewer/util/timing.js +8 -11
- package/dist/components/trace-viewer/util/tree.js +1 -1
- package/dist/components/trace-viewer/util/use-immediate-style.js +1 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.js +1 -1
- package/dist/components/trace-viewer/util/use-trackpad-zoom.js +1 -1
- package/dist/components/trace-viewer/worker.js +1 -1
- package/dist/components/trace-viewer-new.d.ts +14 -0
- package/dist/components/trace-viewer-new.d.ts.map +1 -0
- package/dist/components/trace-viewer-new.js +21 -0
- package/dist/components/ui/alert.js +1 -1
- package/dist/components/ui/button.d.ts +10 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +46 -0
- package/dist/components/ui/card.js +1 -1
- package/dist/components/ui/context-card.d.ts +47 -0
- package/dist/components/ui/context-card.d.ts.map +1 -0
- package/dist/components/ui/context-card.js +471 -0
- package/dist/components/ui/data-inspector.d.ts +22 -0
- package/dist/components/ui/data-inspector.d.ts.map +1 -1
- package/dist/components/ui/data-inspector.js +103 -13
- package/dist/components/ui/decrypt-button.js +1 -1
- package/dist/components/ui/error-card.js +1 -1
- package/dist/components/ui/error-stack-block.d.ts +17 -10
- package/dist/components/ui/error-stack-block.d.ts.map +1 -1
- package/dist/components/ui/error-stack-block.js +49 -30
- package/dist/components/ui/icon-button.d.ts +19 -0
- package/dist/components/ui/icon-button.d.ts.map +1 -0
- package/dist/components/ui/icon-button.js +29 -0
- package/dist/components/ui/inspector-theme.d.ts +9 -10
- package/dist/components/ui/inspector-theme.d.ts.map +1 -1
- package/dist/components/ui/inspector-theme.js +39 -72
- package/dist/components/ui/kbd.d.ts +6 -0
- package/dist/components/ui/kbd.d.ts.map +1 -0
- package/dist/components/ui/kbd.js +6 -0
- package/dist/components/ui/load-more-button.js +1 -1
- package/dist/components/ui/menu-dropdown.js +1 -1
- package/dist/components/ui/skeleton.js +1 -1
- package/dist/components/ui/spinner.js +1 -1
- package/dist/components/ui/timestamp-tooltip.d.ts +34 -1
- package/dist/components/ui/timestamp-tooltip.d.ts.map +1 -1
- package/dist/components/ui/timestamp-tooltip.js +80 -132
- package/dist/components/ui/tooltip.d.ts +10 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/components/ui/tooltip.js +12 -0
- package/dist/components/workflow-trace-view.d.ts +1 -3
- package/dist/components/workflow-trace-view.d.ts.map +1 -1
- package/dist/components/workflow-trace-view.js +11 -13
- package/dist/components/workflow-traces/event-colors.d.ts +1 -0
- package/dist/components/workflow-traces/event-colors.d.ts.map +1 -1
- package/dist/components/workflow-traces/event-colors.js +12 -1
- package/dist/components/workflow-traces/trace-colors.js +1 -1
- package/dist/components/workflow-traces/trace-span-construction.d.ts +2 -2
- package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.js +19 -5
- package/dist/components/workflow-traces/trace-time-utils.js +1 -1
- package/dist/hooks/use-dark-mode.js +1 -1
- package/dist/hooks/use-intersection-observer.d.ts +17 -0
- package/dist/hooks/use-intersection-observer.d.ts.map +1 -0
- package/dist/hooks/use-intersection-observer.js +29 -0
- package/dist/hooks/use-load-more-on-scroll.d.ts +21 -0
- package/dist/hooks/use-load-more-on-scroll.d.ts.map +1 -0
- package/dist/hooks/use-load-more-on-scroll.js +28 -0
- package/dist/hooks/use-reduced-motion.d.ts +10 -0
- package/dist/hooks/use-reduced-motion.d.ts.map +1 -0
- package/dist/hooks/use-reduced-motion.js +29 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/lib/event-analysis.js +1 -1
- package/dist/lib/event-materialization.js +1 -1
- package/dist/lib/exact-event-search-id.d.ts +25 -0
- package/dist/lib/exact-event-search-id.d.ts.map +1 -0
- package/dist/lib/exact-event-search-id.js +39 -0
- package/dist/lib/hydration.d.ts +6 -0
- package/dist/lib/hydration.d.ts.map +1 -1
- package/dist/lib/hydration.js +154 -2
- package/dist/lib/stream-display.d.ts +18 -0
- package/dist/lib/stream-display.d.ts.map +1 -0
- package/dist/lib/stream-display.js +30 -0
- package/dist/lib/toast.js +1 -1
- package/dist/lib/trace-builder.d.ts +15 -0
- package/dist/lib/trace-builder.d.ts.map +1 -1
- package/dist/lib/trace-builder.js +29 -3
- package/dist/lib/utils.d.ts +20 -9
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +74 -31
- package/dist/lib/zstd-browser-decoder.d.ts +7 -0
- package/dist/lib/zstd-browser-decoder.d.ts.map +1 -0
- package/dist/lib/zstd-browser-decoder.js +41 -0
- package/dist/styles.css +484 -1
- package/package.json +10 -5
- package/src/components/event-list-view.tsx +279 -104
- package/src/components/index.ts +7 -0
- package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
- package/src/components/new-trace-viewer/components/event-list.tsx +136 -0
- package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
- package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
- package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
- package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
- package/src/components/new-trace-viewer/components/span-markers.tsx +173 -0
- package/src/components/new-trace-viewer/components/split-pane.tsx +198 -0
- package/src/components/new-trace-viewer/components/timeline.module.css +46 -0
- package/src/components/new-trace-viewer/components/timeline.tsx +744 -0
- package/src/components/new-trace-viewer/components/trace-viewer-skeleton.tsx +94 -0
- package/src/components/new-trace-viewer/components/use-row-window.ts +140 -0
- package/src/components/new-trace-viewer/context.tsx +90 -0
- package/src/components/new-trace-viewer/icons.tsx +79 -0
- package/src/components/new-trace-viewer/search.ts +178 -0
- package/src/components/new-trace-viewer/trace-viewer.tsx +843 -0
- package/src/components/new-trace-viewer/types.ts +2 -0
- package/src/components/new-trace-viewer/utils.test.ts +182 -0
- package/src/components/new-trace-viewer/utils.ts +519 -0
- package/src/components/sidebar/attribute-panel.tsx +194 -240
- package/src/components/sidebar/attributes-block.tsx +173 -0
- package/src/components/sidebar/copyable-data-block.tsx +49 -27
- package/src/components/sidebar/detail-card.tsx +119 -59
- package/src/components/sidebar/entity-detail-panel.tsx +55 -111
- package/src/components/sidebar/events-list.tsx +85 -96
- package/src/components/sidebar/sidebar-data-context.tsx +62 -0
- package/src/components/sidebar/span-detail-merge.ts +65 -0
- package/src/components/stream-viewer.tsx +18 -40
- package/src/components/trace-viewer/components/node.tsx +3 -2
- package/src/components/trace-viewer/trace-viewer.module.css +4 -4
- package/src/components/trace-viewer/util/timing.ts +7 -11
- package/src/components/trace-viewer-new.tsx +55 -0
- package/src/components/ui/button.tsx +68 -0
- package/src/components/ui/context-card.tsx +784 -0
- package/src/components/ui/data-inspector.tsx +244 -22
- package/src/components/ui/error-stack-block.tsx +84 -55
- package/src/components/ui/icon-button.tsx +53 -0
- package/src/components/ui/inspector-theme.ts +39 -81
- package/src/components/ui/kbd.tsx +21 -0
- package/src/components/ui/timestamp-tooltip.tsx +132 -194
- package/src/components/ui/tooltip.tsx +47 -0
- package/src/components/workflow-trace-view.tsx +19 -15
- package/src/components/workflow-traces/event-colors.ts +12 -0
- package/src/components/workflow-traces/trace-span-construction.ts +24 -5
- package/src/hooks/use-intersection-observer.ts +57 -0
- package/src/hooks/use-load-more-on-scroll.ts +49 -0
- package/src/hooks/use-reduced-motion.ts +34 -0
- package/src/index.ts +9 -0
- package/src/lib/exact-event-search-id.ts +67 -0
- package/src/lib/hydration.ts +166 -1
- package/src/lib/stream-display.ts +48 -0
- package/src/lib/trace-builder.ts +34 -2
- package/src/lib/utils.ts +83 -31
- package/src/lib/zstd-browser-decoder.ts +42 -0
- package/src/styles.css +484 -1
- package/dist/components/error-boundary.js.map +0 -1
- package/dist/components/event-list-view.js.map +0 -1
- package/dist/components/hook-actions.js.map +0 -1
- package/dist/components/index.js.map +0 -1
- package/dist/components/run-trace-view.js.map +0 -1
- package/dist/components/sidebar/attribute-panel.js.map +0 -1
- package/dist/components/sidebar/conversation-view.js.map +0 -1
- package/dist/components/sidebar/copyable-data-block.js.map +0 -1
- package/dist/components/sidebar/detail-card.js.map +0 -1
- package/dist/components/sidebar/entity-detail-panel.js.map +0 -1
- package/dist/components/sidebar/events-list.js.map +0 -1
- package/dist/components/sidebar/resolve-hook-modal.js.map +0 -1
- package/dist/components/stream-viewer.js.map +0 -1
- package/dist/components/trace-viewer/components/map.js.map +0 -1
- package/dist/components/trace-viewer/components/markers.js.map +0 -1
- package/dist/components/trace-viewer/components/node.js.map +0 -1
- package/dist/components/trace-viewer/components/search-input.js.map +0 -1
- package/dist/components/trace-viewer/components/search.js.map +0 -1
- package/dist/components/trace-viewer/components/span-content.js.map +0 -1
- package/dist/components/trace-viewer/components/span-detail-panel.js.map +0 -1
- package/dist/components/trace-viewer/components/span-segments.js.map +0 -1
- package/dist/components/trace-viewer/components/span-strategies.js.map +0 -1
- package/dist/components/trace-viewer/components/ui.js.map +0 -1
- package/dist/components/trace-viewer/components/zoom-button.js.map +0 -1
- package/dist/components/trace-viewer/components/zoom-icons.js.map +0 -1
- package/dist/components/trace-viewer/context.js.map +0 -1
- package/dist/components/trace-viewer/index.js.map +0 -1
- package/dist/components/trace-viewer/trace-viewer.js.map +0 -1
- package/dist/components/trace-viewer/types.js.map +0 -1
- package/dist/components/trace-viewer/util/constants.js.map +0 -1
- package/dist/components/trace-viewer/util/scrollbar-width.js.map +0 -1
- package/dist/components/trace-viewer/util/timing.js.map +0 -1
- package/dist/components/trace-viewer/util/tree.js.map +0 -1
- package/dist/components/trace-viewer/util/use-immediate-style.js.map +0 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.js.map +0 -1
- package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
- package/dist/components/trace-viewer/worker.js.map +0 -1
- package/dist/components/ui/alert.js.map +0 -1
- package/dist/components/ui/card.js.map +0 -1
- package/dist/components/ui/data-inspector.js.map +0 -1
- package/dist/components/ui/decrypt-button.js.map +0 -1
- package/dist/components/ui/error-card.js.map +0 -1
- package/dist/components/ui/error-stack-block.js.map +0 -1
- package/dist/components/ui/inspector-theme.js.map +0 -1
- package/dist/components/ui/load-more-button.js.map +0 -1
- package/dist/components/ui/menu-dropdown.js.map +0 -1
- package/dist/components/ui/skeleton.js.map +0 -1
- package/dist/components/ui/spinner.js.map +0 -1
- package/dist/components/ui/timestamp-tooltip.js.map +0 -1
- package/dist/components/workflow-trace-view.js.map +0 -1
- package/dist/components/workflow-traces/event-colors.js.map +0 -1
- package/dist/components/workflow-traces/trace-colors.js.map +0 -1
- package/dist/components/workflow-traces/trace-span-construction.js.map +0 -1
- package/dist/components/workflow-traces/trace-time-utils.js.map +0 -1
- package/dist/hooks/use-dark-mode.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/event-analysis.js.map +0 -1
- package/dist/lib/event-materialization.js.map +0 -1
- package/dist/lib/hydration.js.map +0 -1
- package/dist/lib/toast.js.map +0 -1
- package/dist/lib/trace-builder.js.map +0 -1
- package/dist/lib/utils.js.map +0 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Skeleton } from '../../ui/skeleton';
|
|
2
|
+
|
|
3
|
+
const COL_TEMPLATE = '340px 1px minmax(50px, 1fr)';
|
|
4
|
+
|
|
5
|
+
const ROWS: { id: string; name: number; off: number; bar: number }[] = [
|
|
6
|
+
{ id: 'r0', name: 62, off: 0, bar: 72 },
|
|
7
|
+
{ id: 'r1', name: 78, off: 6, bar: 48 },
|
|
8
|
+
{ id: 'r2', name: 50, off: 10, bar: 55 },
|
|
9
|
+
{ id: 'r3', name: 84, off: 18, bar: 30 },
|
|
10
|
+
{ id: 'r4', name: 45, off: 18, bar: 42 },
|
|
11
|
+
{ id: 'r5', name: 66, off: 34, bar: 38 },
|
|
12
|
+
{ id: 'r6', name: 55, off: 41, bar: 25 },
|
|
13
|
+
{ id: 'r7', name: 40, off: 50, bar: 30 },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const HEADER_MARKERS = ['m0', 'm1', 'm2', 'm3'];
|
|
17
|
+
|
|
18
|
+
const HeaderDivider = () => (
|
|
19
|
+
<div className="flex justify-center">
|
|
20
|
+
<span aria-hidden className="h-full w-px bg-gray-alpha-400" />
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export function TraceViewerSkeleton() {
|
|
25
|
+
return (
|
|
26
|
+
<output
|
|
27
|
+
aria-busy="true"
|
|
28
|
+
aria-label="Loading trace"
|
|
29
|
+
className="flex flex-col w-full h-full min-h-0 bg-background-100"
|
|
30
|
+
>
|
|
31
|
+
<span className="sr-only">Loading trace…</span>
|
|
32
|
+
|
|
33
|
+
{/* Header row: search header | divider | timeline header */}
|
|
34
|
+
<div
|
|
35
|
+
className="shrink-0 grid"
|
|
36
|
+
style={{ gridTemplateColumns: COL_TEMPLATE }}
|
|
37
|
+
>
|
|
38
|
+
<div className="h-10 min-h-10 flex items-center border-b border-gray-alpha-400 pl-4 pr-2 gap-1.5">
|
|
39
|
+
<Skeleton className="w-3.5 h-3.5 shrink-0 rounded-sm" />
|
|
40
|
+
<Skeleton className="h-3.5 w-40" />
|
|
41
|
+
</div>
|
|
42
|
+
<HeaderDivider />
|
|
43
|
+
<div className="h-10 min-h-10 flex items-end border-b border-gray-alpha-400 px-4 pb-1 gap-2">
|
|
44
|
+
<div className="relative flex-1 flex items-end justify-between">
|
|
45
|
+
{HEADER_MARKERS.map((id) => (
|
|
46
|
+
<Skeleton key={id} className="h-3.5 w-9" />
|
|
47
|
+
))}
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
{/* Content row: event list | gutter | timeline */}
|
|
53
|
+
<div
|
|
54
|
+
className="grid flex-1 min-h-0 overflow-hidden"
|
|
55
|
+
style={{ gridTemplateColumns: COL_TEMPLATE }}
|
|
56
|
+
>
|
|
57
|
+
{/* Sidebar event rows */}
|
|
58
|
+
<div className="block overflow-visible">
|
|
59
|
+
<ul className="block divide-y divide-gray-alpha-400 border-b border-gray-alpha-400">
|
|
60
|
+
{ROWS.map((row) => (
|
|
61
|
+
<li key={row.id} className="h-10 flex items-center pl-4 pr-2">
|
|
62
|
+
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
63
|
+
<Skeleton className="w-4 h-4 shrink-0 rounded-sm" />
|
|
64
|
+
<Skeleton
|
|
65
|
+
className="h-3.5"
|
|
66
|
+
style={{ width: `${row.name}%` }}
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
69
|
+
<Skeleton className="ml-2 h-3.5 w-10 shrink-0" />
|
|
70
|
+
</li>
|
|
71
|
+
))}
|
|
72
|
+
</ul>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
{/* Gutter */}
|
|
76
|
+
<span aria-hidden className="h-full w-px bg-gray-alpha-400" />
|
|
77
|
+
|
|
78
|
+
{/* Timeline bars */}
|
|
79
|
+
<div className="relative min-h-0">
|
|
80
|
+
{ROWS.map((row) => (
|
|
81
|
+
<div key={row.id} className="relative h-10">
|
|
82
|
+
<div className="absolute inset-x-4 inset-y-0">
|
|
83
|
+
<Skeleton
|
|
84
|
+
className="absolute top-1/2 -translate-y-1/2 h-6 rounded-[0.25rem]"
|
|
85
|
+
style={{ left: `${row.off}%`, width: `${row.bar}%` }}
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
))}
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</output>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { type RefObject, useEffect, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
/** Rows rendered on the first paint, before the container has been measured. */
|
|
6
|
+
const INITIAL_VISIBLE_ROWS = 60;
|
|
7
|
+
|
|
8
|
+
export interface RowWindow {
|
|
9
|
+
/** First visible row index (inclusive), already padded by overscan. */
|
|
10
|
+
start: number;
|
|
11
|
+
/** Last visible row index (exclusive), already padded by overscan. */
|
|
12
|
+
end: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Nearest scrollable ancestor of `el` (the shared `SplitPane` container). */
|
|
16
|
+
function getScrollParent(el: HTMLElement | null): HTMLElement | null {
|
|
17
|
+
let node = el?.parentElement ?? null;
|
|
18
|
+
while (node) {
|
|
19
|
+
const overflowY = getComputedStyle(node).overflowY;
|
|
20
|
+
if (overflowY === 'auto' || overflowY === 'scroll') return node;
|
|
21
|
+
node = node.parentElement;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Fixed-height windowing over a shared scroll container.
|
|
28
|
+
*
|
|
29
|
+
* The events list and timeline both render one `rowHeight`-tall row per span
|
|
30
|
+
* and scroll together inside the same `SplitPane`. Each caller passes a ref to
|
|
31
|
+
* its own root; the hook walks up to the shared scrollable ancestor, so the
|
|
32
|
+
* scroll container doesn't have to be threaded through props. Same scroll
|
|
33
|
+
* position + same row height means both panes window to the same range.
|
|
34
|
+
*/
|
|
35
|
+
export function useRowWindow(
|
|
36
|
+
ref: RefObject<HTMLElement | null>,
|
|
37
|
+
rowCount: number,
|
|
38
|
+
rowHeight: number,
|
|
39
|
+
overscan = 12
|
|
40
|
+
): RowWindow {
|
|
41
|
+
// Bound the pre-measurement window so the first paint of a large trace
|
|
42
|
+
// doesn't render every row before the effect narrows it down.
|
|
43
|
+
const [range, setRange] = useState<RowWindow>(() => ({
|
|
44
|
+
start: 0,
|
|
45
|
+
end: Math.min(rowCount, INITIAL_VISIBLE_ROWS),
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const self = ref.current;
|
|
50
|
+
const el = getScrollParent(self);
|
|
51
|
+
if (!self || !el) return;
|
|
52
|
+
|
|
53
|
+
let raf = 0;
|
|
54
|
+
const measure = () => {
|
|
55
|
+
raf = 0;
|
|
56
|
+
// Offset of the list's top within the scroll container's content, so the
|
|
57
|
+
// window stays correct even if the list isn't flush with the top.
|
|
58
|
+
const offset =
|
|
59
|
+
self.getBoundingClientRect().top -
|
|
60
|
+
el.getBoundingClientRect().top +
|
|
61
|
+
el.scrollTop;
|
|
62
|
+
const top = el.scrollTop - offset;
|
|
63
|
+
const start = Math.max(0, Math.floor(top / rowHeight) - overscan);
|
|
64
|
+
const end = Math.min(
|
|
65
|
+
rowCount,
|
|
66
|
+
Math.ceil((top + el.clientHeight) / rowHeight) + overscan
|
|
67
|
+
);
|
|
68
|
+
setRange((prev) =>
|
|
69
|
+
prev.start === start && prev.end === end ? prev : { start, end }
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const onScroll = () => {
|
|
74
|
+
if (!raf) raf = requestAnimationFrame(measure);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
measure();
|
|
78
|
+
el.addEventListener('scroll', onScroll, { passive: true });
|
|
79
|
+
const ro = new ResizeObserver(measure);
|
|
80
|
+
ro.observe(el);
|
|
81
|
+
|
|
82
|
+
return () => {
|
|
83
|
+
el.removeEventListener('scroll', onScroll);
|
|
84
|
+
ro.disconnect();
|
|
85
|
+
if (raf) cancelAnimationFrame(raf);
|
|
86
|
+
};
|
|
87
|
+
}, [ref, rowCount, rowHeight, overscan]);
|
|
88
|
+
|
|
89
|
+
return range;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Row height (px) shared by the events list and timeline rows (`h-10`). */
|
|
93
|
+
export const ROW_HEIGHT_PX = 40;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Scroll a windowed row into view by index.
|
|
97
|
+
*
|
|
98
|
+
* The list is virtualized (fixed `rowHeight` rows), so an off-screen row has no
|
|
99
|
+
* DOM node to `scrollIntoView` — its target offset is computed from its index
|
|
100
|
+
* instead. Walks up from `listEl` to the shared scrollable ancestor (the same
|
|
101
|
+
* one `useRowWindow` measures against), only scrolls when the row sits outside
|
|
102
|
+
* the visible area, leaves a one-row `margin` of breathing room past it, and
|
|
103
|
+
* clamps the result to `[0, scrollHeight - clientHeight]`.
|
|
104
|
+
*/
|
|
105
|
+
export function scrollRowIntoView(
|
|
106
|
+
listEl: HTMLElement | null,
|
|
107
|
+
index: number,
|
|
108
|
+
rowHeight: number,
|
|
109
|
+
opts?: { margin?: number; behavior?: ScrollBehavior }
|
|
110
|
+
): void {
|
|
111
|
+
const scroller = listEl ? getScrollParent(listEl) : null;
|
|
112
|
+
if (!listEl || !scroller) return;
|
|
113
|
+
|
|
114
|
+
const margin = opts?.margin ?? rowHeight;
|
|
115
|
+
|
|
116
|
+
// Offset of the list's top within the scroll container's content.
|
|
117
|
+
const listOffset =
|
|
118
|
+
listEl.getBoundingClientRect().top -
|
|
119
|
+
scroller.getBoundingClientRect().top +
|
|
120
|
+
scroller.scrollTop;
|
|
121
|
+
|
|
122
|
+
const rowTop = listOffset + index * rowHeight;
|
|
123
|
+
const rowBottom = rowTop + rowHeight;
|
|
124
|
+
const viewTop = scroller.scrollTop;
|
|
125
|
+
const viewBottom = viewTop + scroller.clientHeight;
|
|
126
|
+
|
|
127
|
+
let top: number | null = null;
|
|
128
|
+
if (rowTop < viewTop) {
|
|
129
|
+
top = rowTop - margin;
|
|
130
|
+
} else if (rowBottom > viewBottom) {
|
|
131
|
+
top = rowBottom + margin - scroller.clientHeight;
|
|
132
|
+
}
|
|
133
|
+
if (top === null) return;
|
|
134
|
+
|
|
135
|
+
const max = scroller.scrollHeight - scroller.clientHeight;
|
|
136
|
+
scroller.scrollTo({
|
|
137
|
+
top: Math.max(0, Math.min(top, max)),
|
|
138
|
+
behavior: opts?.behavior,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
type ReactNode,
|
|
6
|
+
useCallback,
|
|
7
|
+
useContext,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useState,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import type { Span } from './types';
|
|
13
|
+
|
|
14
|
+
interface ActiveSpanContextValue {
|
|
15
|
+
activeSpanId: string | null;
|
|
16
|
+
activeSpan: Span | null;
|
|
17
|
+
setActiveSpan: (spanId: string) => void;
|
|
18
|
+
clearActiveSpan: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ActiveSpanContext = createContext<ActiveSpanContextValue | null>(null);
|
|
22
|
+
ActiveSpanContext.displayName = 'ActiveSpanContext';
|
|
23
|
+
|
|
24
|
+
export function ActiveSpanProvider({
|
|
25
|
+
spans,
|
|
26
|
+
children,
|
|
27
|
+
}: {
|
|
28
|
+
spans: Span[];
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
}) {
|
|
31
|
+
const [activeSpanId, setActiveSpanId] = useState<string | null>(null);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setActiveSpanId((currentSpanId) => {
|
|
35
|
+
if (!currentSpanId) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const hasCurrentSpan = spans.some(
|
|
40
|
+
(span) => span.spanId === currentSpanId
|
|
41
|
+
);
|
|
42
|
+
if (hasCurrentSpan) {
|
|
43
|
+
return currentSpanId;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
});
|
|
48
|
+
}, [spans]);
|
|
49
|
+
|
|
50
|
+
const activeSpan = useMemo(() => {
|
|
51
|
+
if (!activeSpanId) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return spans.find((span) => span.spanId === activeSpanId) ?? null;
|
|
56
|
+
}, [activeSpanId, spans]);
|
|
57
|
+
|
|
58
|
+
const setActiveSpan = useCallback((spanId: string) => {
|
|
59
|
+
setActiveSpanId(spanId);
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
const clearActiveSpan = useCallback(() => {
|
|
63
|
+
setActiveSpanId(null);
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
const value = useMemo<ActiveSpanContextValue>(
|
|
67
|
+
() => ({
|
|
68
|
+
activeSpanId,
|
|
69
|
+
activeSpan,
|
|
70
|
+
setActiveSpan,
|
|
71
|
+
clearActiveSpan,
|
|
72
|
+
}),
|
|
73
|
+
[activeSpanId, activeSpan, setActiveSpan, clearActiveSpan]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<ActiveSpanContext.Provider value={value}>
|
|
78
|
+
{children}
|
|
79
|
+
</ActiveSpanContext.Provider>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const useActiveSpan = (): ActiveSpanContextValue => {
|
|
84
|
+
const context = useContext(ActiveSpanContext);
|
|
85
|
+
if (!context) {
|
|
86
|
+
throw new Error('useActiveSpan must be used within ActiveSpanProvider');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return context;
|
|
90
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const WorkflowIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
data-testid="geist-icon"
|
|
5
|
+
height="16"
|
|
6
|
+
strokeLinejoin="round"
|
|
7
|
+
style={{ color: 'currentColor' }}
|
|
8
|
+
viewBox="0 0 16 16"
|
|
9
|
+
width="16"
|
|
10
|
+
>
|
|
11
|
+
<path
|
|
12
|
+
d="M5.75 7L6.4209 7.33594L4.85254 10.4717C5.38789 10.771 5.75 11.3431 5.75 12V14C5.75 14.9665 4.96648 15.75 4 15.75H2C1.03352 15.75 0.25 14.9665 0.25 14V12C0.25 11.0335 1.03352 10.25 2 10.25H3.28711L5.0791 6.66504L5.75 7ZM14 10.25C14.9665 10.25 15.75 11.0335 15.75 12V14C15.75 14.9665 14.9665 15.75 14 15.75H12C11.1185 15.75 10.3909 15.098 10.2695 14.25H7V12.75H10.25V12C10.25 11.0335 11.0335 10.25 12 10.25H14ZM2 11.75C1.86192 11.75 1.75 11.8619 1.75 12V14C1.75 14.1381 1.86192 14.25 2 14.25H4C4.13808 14.25 4.25 14.1381 4.25 14V12C4.25 11.8619 4.13808 11.75 4 11.75H2ZM12 11.75C11.8619 11.75 11.75 11.8619 11.75 12V14C11.75 14.1381 11.8619 14.25 12 14.25H14C14.1381 14.25 14.25 14.1381 14.25 14V12C14.25 11.8619 14.1381 11.75 14 11.75H12ZM9 0.25C9.96649 0.25 10.75 1.03351 10.75 2V4C10.75 4.44642 10.5808 4.85197 10.3057 5.16113L12.2041 8.6416L10.8877 9.36035L8.91895 5.75H7C6.03351 5.75 5.25 4.96649 5.25 4V2C5.25 1.03351 6.03351 0.25 7 0.25H9ZM7 1.75C6.86193 1.75 6.75 1.86193 6.75 2V4C6.75 4.13807 6.86193 4.25 7 4.25H9C9.13807 4.25 9.25 4.13807 9.25 4V2C9.25 1.86193 9.13807 1.75 9 1.75H7Z"
|
|
13
|
+
fill="currentColor"
|
|
14
|
+
></path>
|
|
15
|
+
</svg>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const WebhookIcon = () => {
|
|
20
|
+
return (
|
|
21
|
+
<svg
|
|
22
|
+
data-testid="geist-icon"
|
|
23
|
+
height="16"
|
|
24
|
+
strokeLinejoin="round"
|
|
25
|
+
viewBox="0 0 16 16"
|
|
26
|
+
width="16"
|
|
27
|
+
style={{ color: 'currentColor' }}
|
|
28
|
+
>
|
|
29
|
+
<path
|
|
30
|
+
fillRule="evenodd"
|
|
31
|
+
clipRule="evenodd"
|
|
32
|
+
d="M5.8349 3.24994C5.14454 4.44567 5.55423 5.97464 6.74996 6.665L7.39465 7.03721L7.02726 7.68466L5.14599 11C5.21289 11.1531 5.25 11.3222 5.25 11.5C5.25 12.1904 4.69036 12.75 4 12.75C3.30964 12.75 2.75 12.1904 2.75 11.5C2.75 10.8634 3.22584 10.338 3.84123 10.26L5.38944 7.5316C3.95909 6.30274 3.55857 4.19264 4.53586 2.49994C5.64043 0.586765 8.08679 -0.0687357 9.99996 1.03583C11.836 2.09584 12.5137 4.39156 11.5892 6.26591L10.4949 4.33754C10.4418 3.53274 10.0009 2.76843 9.24996 2.33487C8.05423 1.64452 6.52525 2.0542 5.8349 3.24994ZM9.146 4.99996C9.21289 4.84686 9.25 4.67776 9.25 4.5C9.25 3.80964 8.69036 3.25 8 3.25C7.30964 3.25 6.75 3.80964 6.75 4.5C6.75 5.13659 7.22586 5.66203 7.84127 5.74002L9.72266 9.05556L10.0956 9.71274L10.75 9.33493C11.9457 8.64458 13.4747 9.05427 14.165 10.25C14.8554 11.4457 14.4457 12.9747 13.25 13.6651C12.5047 14.0953 11.63 14.0983 10.9096 13.75H8.69252C9.85895 15.4685 12.1727 16.0191 14 14.9641C15.9131 13.8595 16.5686 11.4132 15.4641 9.5C14.4886 7.81038 12.4665 7.10169 10.6895 7.71998L9.146 4.99996ZM13.25 11.5C13.25 12.1904 12.6904 12.75 12 12.75C11.591 12.75 11.228 12.5536 10.9999 12.25H7.9298C7.57875 14.1006 5.95279 15.5 4 15.5C1.79086 15.5 0 13.7091 0 11.5C0 9.40018 1.61801 7.67826 3.67529 7.51299L2.58098 9.44147C1.92792 9.89251 1.5 10.6463 1.5 11.5C1.5 12.8807 2.61929 14 4 14C5.38071 14 6.5 12.8807 6.5 11.5V10.75H7.25H10.9999C11.228 10.4464 11.591 10.25 12 10.25C12.6904 10.25 13.25 10.8096 13.25 11.5Z"
|
|
33
|
+
fill="currentColor"
|
|
34
|
+
></path>
|
|
35
|
+
</svg>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const SleepIcon = () => {
|
|
40
|
+
return (
|
|
41
|
+
<svg
|
|
42
|
+
data-testid="geist-icon"
|
|
43
|
+
height="16"
|
|
44
|
+
strokeLinejoin="round"
|
|
45
|
+
viewBox="0 0 16 16"
|
|
46
|
+
width="16"
|
|
47
|
+
style={{ color: 'currentColor' }}
|
|
48
|
+
>
|
|
49
|
+
<path
|
|
50
|
+
fillRule="evenodd"
|
|
51
|
+
clipRule="evenodd"
|
|
52
|
+
d="M7.25 1.25V2.03971C5.87928 2.18571 4.62678 2.72736 3.6089 3.54824L3.03033 2.96967L2.5 2.43934L1.43934 3.5L1.96967 4.03033L2.54824 4.6089C1.57979 5.80976 1 7.33717 1 9C1 12.866 4.13401 16 8 16C11.866 16 15 12.866 15 9C15 7.33717 14.4202 5.80976 13.4518 4.6089L14.0303 4.03033L14.5607 3.5L13.5 2.43934L12.9697 2.96967L12.3911 3.54824C11.3732 2.72736 10.1207 2.18571 8.75 2.03971V1.25H9.25H10V-0.25H9.25H8.75H7.25H6.75H6V1.25H6.75H7.25ZM2.5 9C2.5 5.96243 4.96243 3.5 8 3.5C11.0376 3.5 13.5 5.96243 13.5 9C13.5 12.0376 11.0376 14.5 8 14.5C4.96243 14.5 2.5 12.0376 2.5 9ZM8.75 6.75V6H7.25V6.75V9V9.75H8.75V9V6.75Z"
|
|
53
|
+
fill="currentColor"
|
|
54
|
+
></path>
|
|
55
|
+
</svg>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const StepForwardIcon = () => {
|
|
60
|
+
return (
|
|
61
|
+
<svg
|
|
62
|
+
data-testid="geist-icon"
|
|
63
|
+
height="16"
|
|
64
|
+
strokeLinejoin="round"
|
|
65
|
+
viewBox="0 0 16 16"
|
|
66
|
+
width="16"
|
|
67
|
+
style={{ color: 'currentColor' }}
|
|
68
|
+
>
|
|
69
|
+
<path
|
|
70
|
+
fillRule="evenodd"
|
|
71
|
+
clipRule="evenodd"
|
|
72
|
+
d="M4.21969 12.5303L4.75002 13.0607L5.81068 12L5.28035 11.4697L1.81068 7.99999L5.28035 4.53032L5.81068 3.99999L4.75002 2.93933L4.21969 3.46966L0.39647 7.29289C0.00594562 7.68341 0.00594562 8.31658 0.39647 8.7071L4.21969 12.5303ZM11.7804 12.5303L11.25 13.0607L10.1894 12L10.7197 11.4697L14.1894 7.99999L10.7197 4.53032L10.1894 3.99999L11.25 2.93933L11.7804 3.46966L15.6036 7.29289C15.9941 7.68341 15.9941 8.31658 15.6036 8.7071L11.7804 12.5303Z"
|
|
73
|
+
fill="currentColor"
|
|
74
|
+
></path>
|
|
75
|
+
</svg>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export { WorkflowIcon, WebhookIcon, SleepIcon, StepForwardIcon };
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { Span } from './types';
|
|
2
|
+
|
|
3
|
+
const ATTR_FILTER_REGEX = /(?:^|\s)(?<pair>(?<key>[\w.]+):(?<value>\S*))/g;
|
|
4
|
+
|
|
5
|
+
export interface SpanAttributeFilter {
|
|
6
|
+
key: string;
|
|
7
|
+
value: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ParsedSpanSearchQuery {
|
|
11
|
+
text: string;
|
|
12
|
+
attributes: SpanAttributeFilter[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SpanSearchResult {
|
|
16
|
+
isActive: boolean;
|
|
17
|
+
matchedSpanIds: Set<string>;
|
|
18
|
+
matchingSpans: Span[];
|
|
19
|
+
query: ParsedSpanSearchQuery;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function parseSpanSearchQuery(rawQuery: string): ParsedSpanSearchQuery {
|
|
23
|
+
const attributes: SpanAttributeFilter[] = [];
|
|
24
|
+
for (const match of rawQuery.matchAll(ATTR_FILTER_REGEX)) {
|
|
25
|
+
const key = match.groups?.key?.trim();
|
|
26
|
+
if (!key) continue;
|
|
27
|
+
attributes.push({
|
|
28
|
+
key,
|
|
29
|
+
value: (match.groups?.value ?? '').toLocaleLowerCase(),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const text = rawQuery
|
|
34
|
+
.replace(ATTR_FILTER_REGEX, ' ')
|
|
35
|
+
.replace(/\s{2,}/g, ' ')
|
|
36
|
+
.trim()
|
|
37
|
+
.toLocaleLowerCase();
|
|
38
|
+
|
|
39
|
+
return { text, attributes };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isSpanDimmedBySearch(
|
|
43
|
+
spanId: string,
|
|
44
|
+
result: SpanSearchResult
|
|
45
|
+
): boolean {
|
|
46
|
+
return result.isActive && !result.matchedSpanIds.has(spanId);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function searchSpans(spans: Span[], rawQuery: string): SpanSearchResult {
|
|
50
|
+
const query = parseSpanSearchQuery(rawQuery);
|
|
51
|
+
const isActive = Boolean(query.text || query.attributes.length);
|
|
52
|
+
const matchingSpans = isActive
|
|
53
|
+
? spans.filter((span) => spanMatchesQuery(span, query))
|
|
54
|
+
: spans;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
isActive,
|
|
58
|
+
matchedSpanIds: new Set(matchingSpans.map((span) => span.spanId)),
|
|
59
|
+
matchingSpans,
|
|
60
|
+
query,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function spanMatchesQuery(span: Span, query: ParsedSpanSearchQuery): boolean {
|
|
65
|
+
if (query.text && !spanMatchesText(span, query.text)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return query.attributes.every(({ key, value }) =>
|
|
70
|
+
spanMatchesAttribute(span, key, value)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function spanMatchesText(span: Span, text: string): boolean {
|
|
75
|
+
return [
|
|
76
|
+
span.name,
|
|
77
|
+
span.spanId,
|
|
78
|
+
span.resource,
|
|
79
|
+
span.library.name,
|
|
80
|
+
span.library.version,
|
|
81
|
+
]
|
|
82
|
+
.filter((value): value is string => typeof value === 'string')
|
|
83
|
+
.some((value) => value.toLocaleLowerCase().includes(text));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function spanMatchesAttribute(
|
|
87
|
+
span: Span,
|
|
88
|
+
key: string,
|
|
89
|
+
expectedValue: string
|
|
90
|
+
): boolean {
|
|
91
|
+
const candidates = getSearchValues(span, key);
|
|
92
|
+
if (!candidates.length) return false;
|
|
93
|
+
|
|
94
|
+
if (!expectedValue) {
|
|
95
|
+
return candidates.some((value) => value != null);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return candidates.some((value) =>
|
|
99
|
+
valueToSearchString(value).toLocaleLowerCase().includes(expectedValue)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getSearchValues(span: Span, key: string): unknown[] {
|
|
104
|
+
const candidates: unknown[] = [];
|
|
105
|
+
const normalizedKey = key.toLocaleLowerCase();
|
|
106
|
+
|
|
107
|
+
switch (normalizedKey) {
|
|
108
|
+
case 'id':
|
|
109
|
+
case 'spanid':
|
|
110
|
+
case 'span.id':
|
|
111
|
+
candidates.push(span.spanId);
|
|
112
|
+
break;
|
|
113
|
+
case 'name':
|
|
114
|
+
candidates.push(span.name);
|
|
115
|
+
break;
|
|
116
|
+
case 'resource':
|
|
117
|
+
candidates.push(span.resource);
|
|
118
|
+
break;
|
|
119
|
+
case 'library':
|
|
120
|
+
case 'library.name':
|
|
121
|
+
candidates.push(span.library.name);
|
|
122
|
+
break;
|
|
123
|
+
case 'library.version':
|
|
124
|
+
candidates.push(span.library.version);
|
|
125
|
+
break;
|
|
126
|
+
case 'status':
|
|
127
|
+
case 'status.code':
|
|
128
|
+
candidates.push(span.status.code);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
candidates.push(getOwnProperty(span.attributes, key));
|
|
133
|
+
candidates.push(getPathValue(span.attributes, key));
|
|
134
|
+
|
|
135
|
+
if (!normalizedKey.startsWith('data.')) {
|
|
136
|
+
candidates.push(getPathValue(span.attributes.data, key));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return candidates.filter((value) => value !== undefined);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function getOwnProperty(source: unknown, key: string): unknown {
|
|
143
|
+
if (!source || typeof source !== 'object') return undefined;
|
|
144
|
+
if (!Object.hasOwn(source, key)) return undefined;
|
|
145
|
+
return (source as Record<string, unknown>)[key];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function getPathValue(source: unknown, path: string): unknown {
|
|
149
|
+
if (!source || typeof source !== 'object') return undefined;
|
|
150
|
+
|
|
151
|
+
let current: unknown = source;
|
|
152
|
+
for (const segment of path.split('.')) {
|
|
153
|
+
if (!segment) return undefined;
|
|
154
|
+
current = getOwnProperty(current, segment);
|
|
155
|
+
if (current === undefined) return undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return current;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function valueToSearchString(value: unknown): string {
|
|
162
|
+
if (value instanceof Date) return value.toISOString();
|
|
163
|
+
if (typeof value === 'string') return value;
|
|
164
|
+
if (
|
|
165
|
+
value == null ||
|
|
166
|
+
typeof value === 'number' ||
|
|
167
|
+
typeof value === 'boolean' ||
|
|
168
|
+
typeof value === 'bigint'
|
|
169
|
+
) {
|
|
170
|
+
return String(value);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
return JSON.stringify(value);
|
|
175
|
+
} catch {
|
|
176
|
+
return String(value);
|
|
177
|
+
}
|
|
178
|
+
}
|