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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/components/index.d.ts +2 -0
  2. package/dist/components/index.d.ts.map +1 -1
  3. package/dist/components/index.js +2 -0
  4. package/dist/components/index.js.map +1 -1
  5. package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
  6. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
  7. package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
  8. package/dist/components/new-trace-viewer/components/copy-button.js.map +1 -0
  9. package/dist/components/new-trace-viewer/components/event-list.d.ts +8 -0
  10. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
  11. package/dist/components/new-trace-viewer/components/event-list.js +38 -0
  12. package/dist/components/new-trace-viewer/components/event-list.js.map +1 -0
  13. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts +18 -0
  14. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
  15. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +54 -0
  16. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js.map +1 -0
  17. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts +20 -0
  18. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
  19. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
  20. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js.map +1 -0
  21. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +25 -0
  22. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
  23. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +101 -0
  24. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js.map +1 -0
  25. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts +20 -0
  26. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +1 -0
  27. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js +114 -0
  28. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js.map +1 -0
  29. package/dist/components/new-trace-viewer/components/split-pane.d.ts +14 -0
  30. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
  31. package/dist/components/new-trace-viewer/components/split-pane.js +99 -0
  32. package/dist/components/new-trace-viewer/components/split-pane.js.map +1 -0
  33. package/dist/components/new-trace-viewer/components/timeline.d.ts +31 -0
  34. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
  35. package/dist/components/new-trace-viewer/components/timeline.js +138 -0
  36. package/dist/components/new-trace-viewer/components/timeline.js.map +1 -0
  37. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  38. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  39. package/dist/components/new-trace-viewer/context.js +47 -0
  40. package/dist/components/new-trace-viewer/context.js.map +1 -0
  41. package/dist/components/new-trace-viewer/detail-panel.d.ts +10 -0
  42. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -0
  43. package/dist/components/new-trace-viewer/detail-panel.js +12 -0
  44. package/dist/components/new-trace-viewer/detail-panel.js.map +1 -0
  45. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  46. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  47. package/dist/components/new-trace-viewer/icons.js +15 -0
  48. package/dist/components/new-trace-viewer/icons.js.map +1 -0
  49. package/dist/components/new-trace-viewer/trace-viewer.d.ts +8 -0
  50. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  51. package/dist/components/new-trace-viewer/trace-viewer.js +349 -0
  52. package/dist/components/new-trace-viewer/trace-viewer.js.map +1 -0
  53. package/dist/components/new-trace-viewer/types.d.ts +1 -0
  54. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  55. package/dist/components/new-trace-viewer/types.js +2 -0
  56. package/dist/components/new-trace-viewer/types.js.map +1 -0
  57. package/dist/components/new-trace-viewer/util/constants.d.ts +1 -0
  58. package/dist/components/new-trace-viewer/util/constants.d.ts.map +1 -0
  59. package/dist/components/new-trace-viewer/util/constants.js +2 -0
  60. package/dist/components/new-trace-viewer/util/constants.js.map +1 -0
  61. package/dist/components/new-trace-viewer/utils.d.ts +42 -0
  62. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  63. package/dist/components/new-trace-viewer/utils.js +305 -0
  64. package/dist/components/new-trace-viewer/utils.js.map +1 -0
  65. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  66. package/dist/components/sidebar/attribute-panel.js +42 -23
  67. package/dist/components/sidebar/attribute-panel.js.map +1 -1
  68. package/dist/components/sidebar/copyable-data-block.js +1 -1
  69. package/dist/components/sidebar/copyable-data-block.js.map +1 -1
  70. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  71. package/dist/components/sidebar/detail-card.js +1 -1
  72. package/dist/components/sidebar/detail-card.js.map +1 -1
  73. package/dist/components/sidebar/entity-detail-panel.d.ts +1 -3
  74. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  75. package/dist/components/sidebar/entity-detail-panel.js +3 -25
  76. package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
  77. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  78. package/dist/components/sidebar/events-list.js +2 -2
  79. package/dist/components/sidebar/events-list.js.map +1 -1
  80. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  81. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  82. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  83. package/dist/components/sidebar/sidebar-data-context.js.map +1 -0
  84. package/dist/components/stream-viewer.d.ts +1 -1
  85. package/dist/components/stream-viewer.d.ts.map +1 -1
  86. package/dist/components/stream-viewer.js +2 -26
  87. package/dist/components/stream-viewer.js.map +1 -1
  88. package/dist/components/trace-viewer/util/timing.d.ts +1 -1
  89. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
  90. package/dist/components/trace-viewer/util/timing.js +5 -8
  91. package/dist/components/trace-viewer/util/timing.js.map +1 -1
  92. package/dist/components/trace-viewer-new.d.ts +10 -0
  93. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  94. package/dist/components/trace-viewer-new.js +23 -0
  95. package/dist/components/trace-viewer-new.js.map +1 -0
  96. package/dist/components/ui/data-inspector.d.ts +21 -0
  97. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  98. package/dist/components/ui/data-inspector.js +99 -4
  99. package/dist/components/ui/data-inspector.js.map +1 -1
  100. package/dist/components/ui/inspector-theme.d.ts +9 -10
  101. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  102. package/dist/components/ui/inspector-theme.js +38 -71
  103. package/dist/components/ui/inspector-theme.js.map +1 -1
  104. package/dist/components/workflow-trace-view.d.ts +1 -3
  105. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  106. package/dist/components/workflow-trace-view.js +4 -4
  107. package/dist/components/workflow-trace-view.js.map +1 -1
  108. package/dist/components/workflow-traces/trace-span-construction.d.ts +2 -2
  109. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  110. package/dist/components/workflow-traces/trace-span-construction.js +16 -3
  111. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
  112. package/dist/index.d.ts +1 -0
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js.map +1 -1
  115. package/dist/lib/stream-display.d.ts +18 -0
  116. package/dist/lib/stream-display.d.ts.map +1 -0
  117. package/dist/lib/stream-display.js +30 -0
  118. package/dist/lib/stream-display.js.map +1 -0
  119. package/dist/lib/trace-builder.d.ts.map +1 -1
  120. package/dist/lib/trace-builder.js +2 -1
  121. package/dist/lib/trace-builder.js.map +1 -1
  122. package/dist/lib/utils.d.ts +9 -9
  123. package/dist/lib/utils.d.ts.map +1 -1
  124. package/dist/lib/utils.js +26 -31
  125. package/dist/lib/utils.js.map +1 -1
  126. package/dist/styles.css +470 -1
  127. package/package.json +3 -2
  128. package/src/components/index.ts +5 -0
  129. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  130. package/src/components/new-trace-viewer/components/event-list.tsx +116 -0
  131. package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
  132. package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
  133. package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
  134. package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
  135. package/src/components/new-trace-viewer/components/split-pane.tsx +185 -0
  136. package/src/components/new-trace-viewer/components/timeline.tsx +382 -0
  137. package/src/components/new-trace-viewer/context.tsx +92 -0
  138. package/src/components/new-trace-viewer/detail-panel.tsx +58 -0
  139. package/src/components/new-trace-viewer/icons.tsx +79 -0
  140. package/src/components/new-trace-viewer/trace-viewer.tsx +645 -0
  141. package/src/components/new-trace-viewer/types.ts +0 -0
  142. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  143. package/src/components/new-trace-viewer/utils.ts +450 -0
  144. package/src/components/sidebar/attribute-panel.tsx +43 -32
  145. package/src/components/sidebar/copyable-data-block.tsx +1 -1
  146. package/src/components/sidebar/detail-card.tsx +1 -19
  147. package/src/components/sidebar/entity-detail-panel.tsx +0 -71
  148. package/src/components/sidebar/events-list.tsx +9 -6
  149. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  150. package/src/components/stream-viewer.tsx +18 -40
  151. package/src/components/trace-viewer/util/timing.ts +5 -9
  152. package/src/components/trace-viewer-new.tsx +49 -0
  153. package/src/components/ui/data-inspector.tsx +235 -4
  154. package/src/components/ui/inspector-theme.ts +39 -81
  155. package/src/components/workflow-trace-view.tsx +2 -6
  156. package/src/components/workflow-traces/trace-span-construction.ts +22 -4
  157. package/src/index.ts +1 -0
  158. package/src/lib/stream-display.ts +48 -0
  159. package/src/lib/trace-builder.ts +2 -1
  160. package/src/lib/utils.ts +27 -32
  161. package/src/styles.css +470 -1
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
3
+ import { middleTruncate, toGraphemes } from './truncate';
4
+ const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
5
+ function createFullState(value, graphemes) {
6
+ return {
7
+ displayText: value,
8
+ isTruncated: false,
9
+ prefixGraphemeCount: graphemes.length,
10
+ prefixText: value,
11
+ suffixGraphemeCount: 0,
12
+ suffixText: '',
13
+ };
14
+ }
15
+ /**
16
+ * Middle-truncation logic. Returns refs to attach to the container and measurement elements, plus the truncated display text. Recalculates on resize, font loading, and value changes.
17
+ * Powers the `<MiddleTruncate>` (`<span>`) component.
18
+ *
19
+ * Documentation: [Geist Middle Truncate](https://vercel.com/geist/middle-truncate)
20
+ *
21
+ * @param value - Full text string to truncate.
22
+ */
23
+ function useMiddleTruncate(value) {
24
+ const graphemes = useMemo(() => toGraphemes(value), [value]);
25
+ const fullState = useMemo(() => createFullState(value, graphemes), [graphemes, value]);
26
+ const ref = useRef(null);
27
+ const measureRef = useRef(null);
28
+ const [state, setState] = useState(() => fullState);
29
+ const rafRef = useRef(0);
30
+ const updateState = useCallback((nextState) => {
31
+ setState((currentState) => {
32
+ if (currentState.displayText === nextState.displayText &&
33
+ currentState.isTruncated === nextState.isTruncated &&
34
+ currentState.prefixText === nextState.prefixText &&
35
+ currentState.prefixGraphemeCount === nextState.prefixGraphemeCount &&
36
+ currentState.suffixText === nextState.suffixText &&
37
+ currentState.suffixGraphemeCount === nextState.suffixGraphemeCount) {
38
+ return currentState;
39
+ }
40
+ return nextState;
41
+ });
42
+ }, []);
43
+ const recalculate = useCallback(() => {
44
+ const el = ref.current;
45
+ const measureEl = measureRef.current;
46
+ if (!el || !measureEl)
47
+ return;
48
+ const available = el.clientWidth;
49
+ if (available <= 0) {
50
+ updateState(fullState);
51
+ return;
52
+ }
53
+ const measure = (text) => {
54
+ measureEl.textContent = text;
55
+ return measureEl.scrollWidth;
56
+ };
57
+ const fullWidth = measure(value);
58
+ if (fullWidth <= available) {
59
+ updateState(fullState);
60
+ return;
61
+ }
62
+ const result = middleTruncate(graphemes, available, measure, fullWidth);
63
+ updateState({
64
+ displayText: result.text,
65
+ isTruncated: result.truncated,
66
+ prefixGraphemeCount: result.prefixGraphemeCount,
67
+ prefixText: result.prefixText,
68
+ suffixGraphemeCount: result.suffixGraphemeCount,
69
+ suffixText: result.suffixText,
70
+ });
71
+ }, [fullState, graphemes, updateState, value]);
72
+ // Measure on mount and when value changes - before paint
73
+ useIsomorphicLayoutEffect(() => {
74
+ recalculate();
75
+ }, [recalculate]);
76
+ // ResizeObserver + font loading for ongoing responsiveness
77
+ useEffect(() => {
78
+ const el = ref.current;
79
+ if (!el)
80
+ return;
81
+ const debouncedRecalc = () => {
82
+ cancelAnimationFrame(rafRef.current);
83
+ rafRef.current = requestAnimationFrame(recalculate);
84
+ };
85
+ const ro = typeof ResizeObserver !== 'undefined'
86
+ ? new ResizeObserver(debouncedRecalc)
87
+ : null;
88
+ ro?.observe(el);
89
+ window.addEventListener('resize', debouncedRecalc);
90
+ const onFontsLoaded = () => {
91
+ debouncedRecalc();
92
+ };
93
+ const fontSet = 'fonts' in document ? document.fonts : null;
94
+ fontSet?.addEventListener?.('loadingdone', onFontsLoaded);
95
+ return () => {
96
+ ro?.disconnect();
97
+ window.removeEventListener('resize', debouncedRecalc);
98
+ cancelAnimationFrame(rafRef.current);
99
+ fontSet?.removeEventListener?.('loadingdone', onFontsLoaded);
100
+ };
101
+ }, [recalculate]);
102
+ return {
103
+ ref,
104
+ measureRef,
105
+ displayText: state.displayText,
106
+ isTruncated: state.isTruncated,
107
+ prefixGraphemeCount: state.prefixGraphemeCount,
108
+ prefixText: state.prefixText,
109
+ suffixGraphemeCount: state.suffixGraphemeCount,
110
+ suffixText: state.suffixText,
111
+ };
112
+ }
113
+ export { useMiddleTruncate };
114
+ //# sourceMappingURL=use-middle-truncate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-middle-truncate.js","sourceRoot":"","sources":["../../../../../src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { middleTruncate, toGraphemes } from './truncate';\n\nconst useIsomorphicLayoutEffect =\n typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\ninterface MiddleTruncateState {\n displayText: string;\n isTruncated: boolean;\n prefixGraphemeCount: number;\n prefixText: string;\n suffixGraphemeCount: number;\n suffixText: string;\n}\n\nfunction createFullState(\n value: string,\n graphemes: string[]\n): MiddleTruncateState {\n return {\n displayText: value,\n isTruncated: false,\n prefixGraphemeCount: graphemes.length,\n prefixText: value,\n suffixGraphemeCount: 0,\n suffixText: '',\n };\n}\n\n/**\n * Middle-truncation logic. Returns refs to attach to the container and measurement elements, plus the truncated display text. Recalculates on resize, font loading, and value changes.\n * Powers the `<MiddleTruncate>` (`<span>`) component.\n *\n * Documentation: [Geist Middle Truncate](https://vercel.com/geist/middle-truncate)\n *\n * @param value - Full text string to truncate.\n */\nfunction useMiddleTruncate(value: string): {\n ref: React.RefObject<HTMLSpanElement | null>;\n measureRef: React.RefObject<HTMLSpanElement | null>;\n displayText: string;\n isTruncated: boolean;\n prefixGraphemeCount: number;\n prefixText: string;\n suffixGraphemeCount: number;\n suffixText: string;\n} {\n const graphemes = useMemo(() => toGraphemes(value), [value]);\n const fullState = useMemo(\n () => createFullState(value, graphemes),\n [graphemes, value]\n );\n const ref = useRef<HTMLSpanElement | null>(null);\n const measureRef = useRef<HTMLSpanElement | null>(null);\n const [state, setState] = useState<MiddleTruncateState>(() => fullState);\n const rafRef = useRef<number>(0);\n\n const updateState = useCallback((nextState: MiddleTruncateState) => {\n setState((currentState) => {\n if (\n currentState.displayText === nextState.displayText &&\n currentState.isTruncated === nextState.isTruncated &&\n currentState.prefixText === nextState.prefixText &&\n currentState.prefixGraphemeCount === nextState.prefixGraphemeCount &&\n currentState.suffixText === nextState.suffixText &&\n currentState.suffixGraphemeCount === nextState.suffixGraphemeCount\n ) {\n return currentState;\n }\n\n return nextState;\n });\n }, []);\n\n const recalculate = useCallback(() => {\n const el = ref.current;\n const measureEl = measureRef.current;\n if (!el || !measureEl) return;\n\n const available = el.clientWidth;\n\n if (available <= 0) {\n updateState(fullState);\n return;\n }\n\n const measure = (text: string): number => {\n measureEl.textContent = text;\n return measureEl.scrollWidth;\n };\n\n const fullWidth = measure(value);\n\n if (fullWidth <= available) {\n updateState(fullState);\n return;\n }\n\n const result = middleTruncate(graphemes, available, measure, fullWidth);\n updateState({\n displayText: result.text,\n isTruncated: result.truncated,\n prefixGraphemeCount: result.prefixGraphemeCount,\n prefixText: result.prefixText,\n suffixGraphemeCount: result.suffixGraphemeCount,\n suffixText: result.suffixText,\n });\n }, [fullState, graphemes, updateState, value]);\n\n // Measure on mount and when value changes - before paint\n useIsomorphicLayoutEffect(() => {\n recalculate();\n }, [recalculate]);\n\n // ResizeObserver + font loading for ongoing responsiveness\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n const debouncedRecalc = (): void => {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(recalculate);\n };\n\n const ro =\n typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver(debouncedRecalc)\n : null;\n ro?.observe(el);\n window.addEventListener('resize', debouncedRecalc);\n\n const onFontsLoaded = (): void => {\n debouncedRecalc();\n };\n const fontSet = 'fonts' in document ? document.fonts : null;\n fontSet?.addEventListener?.('loadingdone', onFontsLoaded);\n\n return () => {\n ro?.disconnect();\n window.removeEventListener('resize', debouncedRecalc);\n cancelAnimationFrame(rafRef.current);\n fontSet?.removeEventListener?.('loadingdone', onFontsLoaded);\n };\n }, [recalculate]);\n\n return {\n ref,\n measureRef,\n displayText: state.displayText,\n isTruncated: state.isTruncated,\n prefixGraphemeCount: state.prefixGraphemeCount,\n prefixText: state.prefixText,\n suffixGraphemeCount: state.suffixGraphemeCount,\n suffixText: state.suffixText,\n };\n}\n\nexport { useMiddleTruncate };\n"]}
@@ -0,0 +1,14 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function Divider(): import("react/jsx-runtime").JSX.Element;
3
+ export interface SplitPaneProps {
4
+ children: ReactNode;
5
+ className?: string;
6
+ /** Fixed pixel width for the start (left) pane. Default 220. */
7
+ defaultStartWidth?: number;
8
+ /** Fixed (non-scrolling) header rendered above the start pane. */
9
+ startHeader?: ReactNode;
10
+ /** Fixed (non-scrolling) header rendered above the end pane. */
11
+ endHeader?: ReactNode;
12
+ }
13
+ export declare function SplitPane({ children, className, defaultStartWidth, startHeader, endHeader, }: SplitPaneProps): import("react/jsx-runtime").JSX.Element;
14
+ //# sourceMappingURL=split-pane.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split-pane.d.ts","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/components/split-pane.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAOf,wBAAgB,OAAO,4CAEtB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,gEAAgE;IAChE,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,SAAS,EACT,iBAAoC,EACpC,WAAW,EACX,SAAS,GACV,EAAE,cAAc,2CAkJhB"}
@@ -0,0 +1,99 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Children, useCallback, useEffect, useRef, useState, } from 'react';
4
+ import { cn } from '../../../lib/utils';
5
+ const GUTTER_PX = 1;
6
+ const MIN_PX = 50;
7
+ const DEFAULT_START_PX = 340;
8
+ export function Divider() {
9
+ return _jsx("div", { "aria-hidden": true, className: "h-full w-px shrink-0 bg-gray-alpha-400" });
10
+ }
11
+ export function SplitPane({ children, className, defaultStartWidth = DEFAULT_START_PX, startHeader, endHeader, }) {
12
+ const parts = Children.toArray(children);
13
+ if (parts.length !== 2) {
14
+ throw new Error('SplitPane expects exactly two children');
15
+ }
16
+ const [start, end] = parts;
17
+ const [startPx, setStartPx] = useState(defaultStartWidth);
18
+ const [isDragging, setIsDragging] = useState(false);
19
+ const containerRef = useRef(null);
20
+ const gutterRef = useRef(null);
21
+ const rafRef = useRef(null);
22
+ const pendingPx = useRef(defaultStartWidth);
23
+ const pointerIdRef = useRef(null);
24
+ useEffect(() => {
25
+ return () => {
26
+ if (rafRef.current)
27
+ cancelAnimationFrame(rafRef.current);
28
+ };
29
+ }, []);
30
+ const clampPx = useCallback((px) => {
31
+ const el = containerRef.current;
32
+ if (!el)
33
+ return px;
34
+ const maxPx = el.getBoundingClientRect().width - MIN_PX - GUTTER_PX;
35
+ return Math.min(maxPx, Math.max(MIN_PX, px));
36
+ }, []);
37
+ useEffect(() => {
38
+ if (!isDragging)
39
+ return;
40
+ const onPointerMove = (e) => {
41
+ if (e.pointerId !== pointerIdRef.current)
42
+ return;
43
+ const container = containerRef.current;
44
+ if (!container)
45
+ return;
46
+ const rect = container.getBoundingClientRect();
47
+ pendingPx.current = clampPx(e.clientX - rect.left);
48
+ if (!rafRef.current) {
49
+ rafRef.current = requestAnimationFrame(() => {
50
+ rafRef.current = null;
51
+ setStartPx(pendingPx.current);
52
+ });
53
+ }
54
+ };
55
+ const onPointerUp = (e) => {
56
+ if (e.pointerId !== pointerIdRef.current)
57
+ return;
58
+ const gutter = gutterRef.current;
59
+ if (gutter?.hasPointerCapture(e.pointerId)) {
60
+ gutter.releasePointerCapture(e.pointerId);
61
+ }
62
+ if (rafRef.current) {
63
+ cancelAnimationFrame(rafRef.current);
64
+ rafRef.current = null;
65
+ }
66
+ pointerIdRef.current = null;
67
+ setIsDragging(false);
68
+ };
69
+ document.addEventListener('pointermove', onPointerMove);
70
+ document.addEventListener('pointerup', onPointerUp);
71
+ document.addEventListener('pointercancel', onPointerUp);
72
+ return () => {
73
+ document.removeEventListener('pointermove', onPointerMove);
74
+ document.removeEventListener('pointerup', onPointerUp);
75
+ document.removeEventListener('pointercancel', onPointerUp);
76
+ };
77
+ }, [isDragging, clampPx]);
78
+ const handlePointerDown = (e) => {
79
+ e.currentTarget.setPointerCapture(e.pointerId);
80
+ pointerIdRef.current = e.pointerId;
81
+ setIsDragging(true);
82
+ };
83
+ const handleLostPointerCapture = () => {
84
+ pointerIdRef.current = null;
85
+ setIsDragging(false);
86
+ if (rafRef.current) {
87
+ cancelAnimationFrame(rafRef.current);
88
+ rafRef.current = null;
89
+ }
90
+ };
91
+ const colTemplate = `${startPx}px ${GUTTER_PX}px minmax(${MIN_PX}px, 1fr)`;
92
+ const hasHeaders = startHeader != null || endHeader != null;
93
+ const gutter = (_jsx("div", { ref: gutterRef, className: "relative z-20 isolate flex shrink-0 cursor-col-resize justify-center", onPointerDown: handlePointerDown, onLostPointerCapture: handleLostPointerCapture, children: _jsx("span", { className: "pointer-events-none relative z-10 h-full w-px shrink-0 bg-gray-alpha-400", "aria-hidden": true }) }));
94
+ if (hasHeaders) {
95
+ return (_jsxs("div", { className: cn('flex flex-col h-full min-h-0', className), children: [_jsxs("div", { className: "shrink-0 grid", style: { gridTemplateColumns: colTemplate }, children: [_jsx("div", { children: startHeader }), _jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "h-full w-px bg-gray-alpha-400", "aria-hidden": true }) }), _jsx("div", { children: endHeader })] }), _jsxs("div", { ref: containerRef, className: cn('grid flex-1 min-h-0 overflow-x-hidden overflow-y-auto', isDragging && 'select-none'), style: { gridTemplateColumns: colTemplate }, children: [start, gutter, end] })] }));
96
+ }
97
+ return (_jsxs("div", { ref: containerRef, className: cn('grid h-full min-h-0 content-start overflow-x-hidden overflow-y-auto', isDragging && 'select-none', className), style: { gridTemplateColumns: colTemplate, height: '100%' }, children: [start, gutter, end] }));
98
+ }
99
+ //# sourceMappingURL=split-pane.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split-pane.js","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/components/split-pane.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport {\n Children,\n type ReactNode,\n type PointerEvent as ReactPointerEvent,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { cn } from '../../../lib/utils';\n\nconst GUTTER_PX = 1;\nconst MIN_PX = 50;\nconst DEFAULT_START_PX = 340;\n\nexport function Divider() {\n return <div aria-hidden className=\"h-full w-px shrink-0 bg-gray-alpha-400\" />;\n}\n\nexport interface SplitPaneProps {\n children: ReactNode;\n className?: string;\n /** Fixed pixel width for the start (left) pane. Default 220. */\n defaultStartWidth?: number;\n /** Fixed (non-scrolling) header rendered above the start pane. */\n startHeader?: ReactNode;\n /** Fixed (non-scrolling) header rendered above the end pane. */\n endHeader?: ReactNode;\n}\n\nexport function SplitPane({\n children,\n className,\n defaultStartWidth = DEFAULT_START_PX,\n startHeader,\n endHeader,\n}: SplitPaneProps) {\n const parts = Children.toArray(children);\n if (parts.length !== 2) {\n throw new Error('SplitPane expects exactly two children');\n }\n const [start, end] = parts;\n\n const [startPx, setStartPx] = useState(defaultStartWidth);\n const [isDragging, setIsDragging] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const gutterRef = useRef<HTMLDivElement>(null);\n const rafRef = useRef<number | null>(null);\n const pendingPx = useRef(defaultStartWidth);\n const pointerIdRef = useRef<number | null>(null);\n\n useEffect(() => {\n return () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, []);\n\n const clampPx = useCallback((px: number) => {\n const el = containerRef.current;\n if (!el) return px;\n const maxPx = el.getBoundingClientRect().width - MIN_PX - GUTTER_PX;\n return Math.min(maxPx, Math.max(MIN_PX, px));\n }, []);\n\n useEffect(() => {\n if (!isDragging) return;\n\n const onPointerMove = (e: globalThis.PointerEvent) => {\n if (e.pointerId !== pointerIdRef.current) return;\n const container = containerRef.current;\n if (!container) return;\n const rect = container.getBoundingClientRect();\n pendingPx.current = clampPx(e.clientX - rect.left);\n if (!rafRef.current) {\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = null;\n setStartPx(pendingPx.current);\n });\n }\n };\n\n const onPointerUp = (e: globalThis.PointerEvent) => {\n if (e.pointerId !== pointerIdRef.current) return;\n const gutter = gutterRef.current;\n if (gutter?.hasPointerCapture(e.pointerId)) {\n gutter.releasePointerCapture(e.pointerId);\n }\n if (rafRef.current) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n pointerIdRef.current = null;\n setIsDragging(false);\n };\n\n document.addEventListener('pointermove', onPointerMove);\n document.addEventListener('pointerup', onPointerUp);\n document.addEventListener('pointercancel', onPointerUp);\n\n return () => {\n document.removeEventListener('pointermove', onPointerMove);\n document.removeEventListener('pointerup', onPointerUp);\n document.removeEventListener('pointercancel', onPointerUp);\n };\n }, [isDragging, clampPx]);\n\n const handlePointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n e.currentTarget.setPointerCapture(e.pointerId);\n pointerIdRef.current = e.pointerId;\n setIsDragging(true);\n };\n\n const handleLostPointerCapture = () => {\n pointerIdRef.current = null;\n setIsDragging(false);\n if (rafRef.current) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n };\n\n const colTemplate = `${startPx}px ${GUTTER_PX}px minmax(${MIN_PX}px, 1fr)`;\n const hasHeaders = startHeader != null || endHeader != null;\n\n const gutter = (\n <div\n ref={gutterRef}\n className=\"relative z-20 isolate flex shrink-0 cursor-col-resize justify-center\"\n onPointerDown={handlePointerDown}\n onLostPointerCapture={handleLostPointerCapture}\n >\n <span\n className=\"pointer-events-none relative z-10 h-full w-px shrink-0 bg-gray-alpha-400\"\n aria-hidden\n />\n </div>\n );\n\n if (hasHeaders) {\n return (\n <div className={cn('flex flex-col h-full min-h-0', className)}>\n <div\n className=\"shrink-0 grid\"\n style={{ gridTemplateColumns: colTemplate }}\n >\n <div>{startHeader}</div>\n <div className=\"flex justify-center\">\n <span className=\"h-full w-px bg-gray-alpha-400\" aria-hidden />\n </div>\n <div>{endHeader}</div>\n </div>\n <div\n ref={containerRef}\n className={cn(\n 'grid flex-1 min-h-0 overflow-x-hidden overflow-y-auto',\n isDragging && 'select-none'\n )}\n style={{ gridTemplateColumns: colTemplate }}\n >\n {start}\n {gutter}\n {end}\n </div>\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={cn(\n 'grid h-full min-h-0 content-start overflow-x-hidden overflow-y-auto',\n isDragging && 'select-none',\n className\n )}\n style={{ gridTemplateColumns: colTemplate, height: '100%' }}\n >\n {start}\n {gutter}\n {end}\n </div>\n );\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Span } from '../../trace-viewer/types';
3
+ import type { TimeMarker } from '../utils';
4
+ export declare const TIMELINE_PADDING_PX = 16;
5
+ declare const TimelineBar: import("react").NamedExoticComponent<{
6
+ span: Span;
7
+ viewStart: number;
8
+ viewDuration: number;
9
+ containerWidth: number;
10
+ isSelected: boolean;
11
+ onClick: () => void;
12
+ }>;
13
+ export { TimelineBar };
14
+ export declare function TimelineHeader({ markers, hoverInfo, }: {
15
+ markers: TimeMarker[];
16
+ hoverInfo?: {
17
+ fraction: number;
18
+ label: string;
19
+ } | null;
20
+ }): ReactNode;
21
+ export declare function Timeline({ spans, viewStart, viewEnd, markers, selectedId, onSelect, hoverFraction, altHeld, }: {
22
+ spans: Span[];
23
+ viewStart: number;
24
+ viewEnd: number;
25
+ markers: TimeMarker[];
26
+ selectedId: string | null;
27
+ onSelect: (spanId: string) => void;
28
+ hoverFraction?: number | null;
29
+ altHeld?: boolean;
30
+ }): ReactNode;
31
+ //# sourceMappingURL=timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/components/timeline.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,KAAK,EAAiB,UAAU,EAAE,MAAM,UAAU,CAAC;AAgC1D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAoCtC,QAAA,MAAM,WAAW;UAQT,IAAI;eACC,MAAM;kBACH,MAAM;oBACJ,MAAM;gBACV,OAAO;aACV,MAAM,IAAI;EAsJnB,CAAC;AAEH,OAAO,EAAE,WAAW,EAAE,CAAC;AAEvB,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,SAAS,GACV,EAAE;IACD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACxD,GAAG,SAAS,CAwBZ;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,SAAS,EACT,OAAO,EACP,OAAO,EACP,UAAU,EACV,QAAQ,EACR,aAAa,EACb,OAAe,GAChB,EAAE;IACD,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,SAAS,CAwFZ"}
@@ -0,0 +1,138 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ArrowLeft, ArrowRight } from 'lucide-react';
4
+ import { memo, useEffect, useMemo, useRef, useState } from 'react';
5
+ import { cn } from '../../../lib/utils';
6
+ import { formatDuration, getHighResInMs } from '../../trace-viewer/util/timing';
7
+ import { computeSpanGaps, computeSpanSegments, getSpanDurationMs, getResourceColor, } from '../utils';
8
+ const SEGMENT_CONFIG = {
9
+ queued: { className: 'bg-gray-500' },
10
+ retrying: {
11
+ className: 'box-border bg-gray-500',
12
+ },
13
+ waiting: { className: 'bg-gray-500' },
14
+ running: { className: 'bg-blue-700' },
15
+ failed: { className: 'bg-red-700' },
16
+ succeeded: { className: 'bg-green-700' },
17
+ sleeping: { className: 'bg-gray-500' },
18
+ received: { className: 'bg-blue-700' },
19
+ };
20
+ const BAR_HEIGHT_PX = 24;
21
+ const TINY_BAR_BOX_SIZE_PX = 24;
22
+ const TINY_BAR_WIDTH_PX = 4;
23
+ const SEGMENT_GAP_PX = 1;
24
+ // Keep this in sync with the rendered row height in the timeline/event list.
25
+ const ROW_HEIGHT = 40;
26
+ const CONTAINER_PAD_Y = 8;
27
+ const END_CAP_HEIGHT = 8;
28
+ export const TIMELINE_PADDING_PX = 16;
29
+ const ORIGIN_MARKER_EPSILON = 0.000001;
30
+ const DeltaIndicator = memo(function DeltaIndicator({ leftFrac, rightFrac, label, rowIndex, }) {
31
+ const centerY = CONTAINER_PAD_Y + (rowIndex + 1) * ROW_HEIGHT;
32
+ return (_jsxs("div", { className: "absolute pointer-events-none", style: {
33
+ left: `${leftFrac * 100}%`,
34
+ width: `${(rightFrac - leftFrac) * 100}%`,
35
+ top: centerY - END_CAP_HEIGHT / 2,
36
+ height: END_CAP_HEIGHT,
37
+ }, children: [_jsx("div", { className: "absolute left-0 top-0 w-px h-full bg-amber-800" }), _jsx("div", { className: "absolute left-0 right-0 top-1/2 h-px bg-amber-800" }), _jsx("div", { className: "absolute right-0 top-0 w-px h-full bg-amber-800" }), _jsx("span", { className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-label-12 leading-none whitespace-nowrap rounded-xs px-1 py-0.5 text-gray-100 bg-amber-800", children: label })] }));
38
+ });
39
+ const TimelineBar = memo(function TimelineBar({ span, viewStart, viewDuration, containerWidth, isSelected, onClick, }) {
40
+ const startTime = getHighResInMs(span.startTime);
41
+ const endTime = getHighResInMs(span.endTime);
42
+ const totalDurationMs = getSpanDurationMs(span);
43
+ const leftFracRaw = viewDuration > 0 ? (startTime - viewStart) / viewDuration : 0;
44
+ const rightFracRaw = viewDuration > 0 ? (endTime - viewStart) / viewDuration : 0;
45
+ const widthFrac = rightFracRaw - leftFracRaw;
46
+ const leftPct = leftFracRaw * 100;
47
+ const widthPct = widthFrac * 100;
48
+ const pixelWidth = widthFrac * containerWidth;
49
+ const visibleLeftFrac = Math.max(0, Math.min(1, leftFracRaw));
50
+ const visibleRightFrac = Math.max(0, Math.min(1, rightFracRaw));
51
+ const visiblePixelWidth = Math.max(0, visibleRightFrac - visibleLeftFrac) * containerWidth;
52
+ const isTinyBar = containerWidth > 0 && visiblePixelWidth < TINY_BAR_BOX_SIZE_PX;
53
+ const [isRowHovered, setIsRowHovered] = useState(false);
54
+ const segments = useMemo(() => computeSpanSegments(span), [span]);
55
+ const workflowStatus = span.attributes.data
56
+ ?.status;
57
+ const isErrored = span.status.code === 2 || workflowStatus === 'failed';
58
+ const colors = getResourceColor(span.resource);
59
+ const fallbackColor = isErrored
60
+ ? (colors.errorBar ?? 'var(--ds-red-700)')
61
+ : colors.bar;
62
+ const renderDurationLabel = (label) => (_jsx("span", { className: "pointer-events-none absolute inset-0 flex items-center justify-start overflow-hidden px-1 text-[10px] font-mono font-medium leading-none whitespace-nowrap text-left text-white tabular-nums", style: { textShadow: '0 1px 1px rgba(0, 0, 0, 0.45)' }, children: label }));
63
+ const getMinDurationLabelWidthPx = (label) => Math.max(40, label.length * 6 + 12);
64
+ const totalDurationLabel = formatDuration(totalDurationMs);
65
+ const showBarDurationLabel = isRowHovered &&
66
+ pixelWidth >= getMinDurationLabelWidthPx(totalDurationLabel);
67
+ const showBoundaryArrow = isTinyBar && (leftFracRaw < 0 || rightFracRaw > 1);
68
+ const BoundaryArrow = leftFracRaw < 0.5 ? ArrowLeft : ArrowRight;
69
+ const barContent = showBoundaryArrow ? (_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-[0.25rem]", children: _jsx(BoundaryArrow, { className: "size-3 text-gray-900" }) })) : isTinyBar ? (_jsx("div", { className: "h-6 rounded-[0.25rem]", style: { background: fallbackColor } })) : segments.length > 0 ? (_jsx("div", { className: "relative h-6 w-full", children: segments.map((seg, i) => {
70
+ const segPixelWidth = (seg.endFraction - seg.startFraction) * pixelWidth;
71
+ const segDurationLabel = formatDuration((seg.endFraction - seg.startFraction) * totalDurationMs);
72
+ const showSegmentDurationLabel = isRowHovered &&
73
+ segPixelWidth >= getMinDurationLabelWidthPx(segDurationLabel);
74
+ const segStyle = seg.status === 'queued' && segPixelWidth < 20
75
+ ? { background: 'var(--ds-gray-500)' }
76
+ : SEGMENT_CONFIG[seg.status].style;
77
+ return (_jsx("div", { className: cn('absolute h-full rounded-[0.25rem]', SEGMENT_CONFIG[seg.status].className), style: {
78
+ left: `calc(${seg.startFraction * 100}% + ${SEGMENT_GAP_PX / 2}px)`,
79
+ width: `calc(${(seg.endFraction - seg.startFraction) * 100}% - ${SEGMENT_GAP_PX}px)`,
80
+ minWidth: 1,
81
+ ...segStyle,
82
+ }, children: showSegmentDurationLabel
83
+ ? renderDurationLabel(segDurationLabel)
84
+ : null }, `${seg.status}-${i}`));
85
+ }) })) : (_jsx("div", { className: "relative h-6 rounded-[0.25rem]", style: {
86
+ width: '100%',
87
+ minWidth: 4,
88
+ background: fallbackColor,
89
+ }, children: showBarDurationLabel ? renderDurationLabel(totalDurationLabel) : null }));
90
+ return (_jsx("div", { role: "treeitem", "aria-selected": isSelected, "aria-expanded": isSelected, "aria-level": 1, className: cn('h-10 relative flex items-center hover:bg-gray-100 aria-selected:bg-gray-100 aria-selected:hover:bg-gray-200'), onMouseEnter: () => setIsRowHovered(true), onMouseLeave: () => setIsRowHovered(false), onClick: onClick, children: _jsx("div", { className: "absolute inset-y-0", style: {
91
+ left: TIMELINE_PADDING_PX,
92
+ right: TIMELINE_PADDING_PX,
93
+ }, children: _jsx("div", { className: "absolute top-1/2 -translate-y-1/2", style: {
94
+ left: showBoundaryArrow
95
+ ? `min(max(${leftPct}%, 0px), calc(100% - ${TINY_BAR_BOX_SIZE_PX}px))`
96
+ : isTinyBar
97
+ ? `min(${leftPct}%, calc(100% - ${TINY_BAR_WIDTH_PX}px))`
98
+ : `${leftPct}%`,
99
+ width: showBoundaryArrow
100
+ ? `${TINY_BAR_BOX_SIZE_PX}px`
101
+ : isTinyBar
102
+ ? `${TINY_BAR_WIDTH_PX}px`
103
+ : `max(${widthPct}%, 4px)`,
104
+ height: BAR_HEIGHT_PX,
105
+ }, children: barContent }) }) }));
106
+ });
107
+ export { TimelineBar };
108
+ export function TimelineHeader({ markers, hoverInfo, }) {
109
+ return (_jsx("div", { className: "relative bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-end px-4 pb-1", children: _jsxs("div", { className: "relative h-full flex-1", children: [markers.map((m) => (_jsx("span", { className: "absolute bottom-1 font-mono text-xs font-normal leading-4 text-gray-900 whitespace-nowrap", style: { left: `${m.position * 100}%` }, children: m.label }, `${m.position}-${m.label}`))), hoverInfo && (_jsx("span", { className: "absolute top-1 pointer-events-none z-10 font-mono text-[11px] leading-4 text-gray-1000 whitespace-nowrap bg-background-100 border border-gray-alpha-400 rounded px-1 -translate-x-1/2", style: { left: `${hoverInfo.fraction * 100}%` }, children: hoverInfo.label }))] }) }));
110
+ }
111
+ export function Timeline({ spans, viewStart, viewEnd, markers, selectedId, onSelect, hoverFraction, altHeld = false, }) {
112
+ const containerRef = useRef(null);
113
+ const [containerWidth, setContainerWidth] = useState(0);
114
+ const viewDuration = viewEnd - viewStart;
115
+ const timelineWidth = Math.max(0, containerWidth - TIMELINE_PADDING_PX * 2);
116
+ useEffect(() => {
117
+ const el = containerRef.current;
118
+ if (!el)
119
+ return;
120
+ const ro = new ResizeObserver(([entry]) => {
121
+ setContainerWidth(entry.contentRect.width);
122
+ });
123
+ ro.observe(el);
124
+ return () => ro.disconnect();
125
+ }, []);
126
+ const gaps = useMemo(() => computeSpanGaps(spans, viewStart, viewEnd), [spans, viewStart, viewEnd]);
127
+ return (_jsxs("div", { ref: containerRef, className: "relative h-full overflow-hidden", children: [_jsx("div", { "aria-hidden": true, className: "absolute inset-y-0 pointer-events-none", style: {
128
+ left: TIMELINE_PADDING_PX,
129
+ right: TIMELINE_PADDING_PX,
130
+ }, children: markers.map((marker) => Math.abs(marker.value) > ORIGIN_MARKER_EPSILON ? (_jsx("div", { className: "absolute top-0 bottom-0 w-px bg-gray-alpha-300", style: { left: `${marker.position * 100}%` } }, `${marker.position}-${marker.label}`)) : null) }), hoverFraction != null && (_jsx("div", { className: "absolute inset-y-0 pointer-events-none z-10", style: {
131
+ left: TIMELINE_PADDING_PX,
132
+ right: TIMELINE_PADDING_PX,
133
+ }, children: _jsx("div", { className: "absolute top-0 bottom-0 w-px bg-gray-alpha-400", style: { left: `${hoverFraction * 100}%` } }) })), spans.map((span) => (_jsx(TimelineBar, { span: span, viewStart: viewStart, viewDuration: viewDuration, containerWidth: timelineWidth, isSelected: selectedId === span.spanId, onClick: () => onSelect(span.spanId) }, span.spanId))), altHeld && (_jsx("div", { "aria-hidden": true, className: "absolute inset-y-0 pointer-events-none", style: {
134
+ left: TIMELINE_PADDING_PX,
135
+ right: TIMELINE_PADDING_PX,
136
+ }, children: gaps.map((gap) => (_jsx(DeltaIndicator, { leftFrac: gap.leftFrac, rightFrac: gap.rightFrac, label: formatDuration(gap.gapMs, true), rowIndex: gap.rowIndex }, gap.rowIndex))) }))] }));
137
+ }
138
+ //# sourceMappingURL=timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/components/timeline.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { ArrowLeft, ArrowRight } from 'lucide-react';\nimport type { ReactNode } from 'react';\nimport { memo, useEffect, useMemo, useRef, useState } from 'react';\nimport { cn } from '../../../lib/utils';\nimport type { Span } from '../../trace-viewer/types';\nimport { formatDuration, getHighResInMs } from '../../trace-viewer/util/timing';\nimport type { SegmentStatus, TimeMarker } from '../utils';\nimport {\n computeSpanGaps,\n computeSpanSegments,\n getSpanDurationMs,\n getResourceColor,\n} from '../utils';\n\nconst SEGMENT_CONFIG: Record<\n SegmentStatus,\n { className?: string; style?: React.CSSProperties }\n> = {\n queued: { className: 'bg-gray-500' },\n retrying: {\n className: 'box-border bg-gray-500',\n },\n waiting: { className: 'bg-gray-500' },\n running: { className: 'bg-blue-700' },\n failed: { className: 'bg-red-700' },\n succeeded: { className: 'bg-green-700' },\n sleeping: { className: 'bg-gray-500' },\n received: { className: 'bg-blue-700' },\n};\n\nconst BAR_HEIGHT_PX = 24;\nconst TINY_BAR_BOX_SIZE_PX = 24;\nconst TINY_BAR_WIDTH_PX = 4;\nconst SEGMENT_GAP_PX = 1;\n// Keep this in sync with the rendered row height in the timeline/event list.\nconst ROW_HEIGHT = 40;\nconst CONTAINER_PAD_Y = 8;\nconst END_CAP_HEIGHT = 8;\nexport const TIMELINE_PADDING_PX = 16;\nconst ORIGIN_MARKER_EPSILON = 0.000001;\n\nconst DeltaIndicator = memo(function DeltaIndicator({\n leftFrac,\n rightFrac,\n label,\n rowIndex,\n}: {\n leftFrac: number;\n rightFrac: number;\n label: string;\n rowIndex: number;\n}) {\n const centerY = CONTAINER_PAD_Y + (rowIndex + 1) * ROW_HEIGHT;\n\n return (\n <div\n className=\"absolute pointer-events-none\"\n style={{\n left: `${leftFrac * 100}%`,\n width: `${(rightFrac - leftFrac) * 100}%`,\n top: centerY - END_CAP_HEIGHT / 2,\n height: END_CAP_HEIGHT,\n }}\n >\n <div className=\"absolute left-0 top-0 w-px h-full bg-amber-800\" />\n <div className=\"absolute left-0 right-0 top-1/2 h-px bg-amber-800\" />\n <div className=\"absolute right-0 top-0 w-px h-full bg-amber-800\" />\n <span className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-label-12 leading-none whitespace-nowrap rounded-xs px-1 py-0.5 text-gray-100 bg-amber-800\">\n {label}\n </span>\n </div>\n );\n});\n\nconst TimelineBar = memo(function TimelineBar({\n span,\n viewStart,\n viewDuration,\n containerWidth,\n isSelected,\n onClick,\n}: {\n span: Span;\n viewStart: number;\n viewDuration: number;\n containerWidth: number;\n isSelected: boolean;\n onClick: () => void;\n}): ReactNode {\n const startTime = getHighResInMs(span.startTime);\n const endTime = getHighResInMs(span.endTime);\n const totalDurationMs = getSpanDurationMs(span);\n\n const leftFracRaw =\n viewDuration > 0 ? (startTime - viewStart) / viewDuration : 0;\n const rightFracRaw =\n viewDuration > 0 ? (endTime - viewStart) / viewDuration : 0;\n const widthFrac = rightFracRaw - leftFracRaw;\n\n const leftPct = leftFracRaw * 100;\n const widthPct = widthFrac * 100;\n\n const pixelWidth = widthFrac * containerWidth;\n const visibleLeftFrac = Math.max(0, Math.min(1, leftFracRaw));\n const visibleRightFrac = Math.max(0, Math.min(1, rightFracRaw));\n const visiblePixelWidth =\n Math.max(0, visibleRightFrac - visibleLeftFrac) * containerWidth;\n const isTinyBar =\n containerWidth > 0 && visiblePixelWidth < TINY_BAR_BOX_SIZE_PX;\n const [isRowHovered, setIsRowHovered] = useState(false);\n\n const segments = useMemo(() => computeSpanSegments(span), [span]);\n\n const workflowStatus = (span.attributes.data as Record<string, unknown>)\n ?.status as string | undefined;\n const isErrored = span.status.code === 2 || workflowStatus === 'failed';\n const colors = getResourceColor(span.resource);\n const fallbackColor = isErrored\n ? (colors.errorBar ?? 'var(--ds-red-700)')\n : colors.bar;\n const renderDurationLabel = (label: string) => (\n <span\n className=\"pointer-events-none absolute inset-0 flex items-center justify-start overflow-hidden px-1 text-[10px] font-mono font-medium leading-none whitespace-nowrap text-left text-white tabular-nums\"\n style={{ textShadow: '0 1px 1px rgba(0, 0, 0, 0.45)' }}\n >\n {label}\n </span>\n );\n const getMinDurationLabelWidthPx = (label: string) =>\n Math.max(40, label.length * 6 + 12);\n const totalDurationLabel = formatDuration(totalDurationMs);\n const showBarDurationLabel =\n isRowHovered &&\n pixelWidth >= getMinDurationLabelWidthPx(totalDurationLabel);\n\n const showBoundaryArrow = isTinyBar && (leftFracRaw < 0 || rightFracRaw > 1);\n const BoundaryArrow = leftFracRaw < 0.5 ? ArrowLeft : ArrowRight;\n const barContent = showBoundaryArrow ? (\n <div className=\"flex h-6 w-6 items-center justify-center rounded-[0.25rem]\">\n <BoundaryArrow className=\"size-3 text-gray-900\" />\n </div>\n ) : isTinyBar ? (\n <div\n className=\"h-6 rounded-[0.25rem]\"\n style={{ background: fallbackColor }}\n />\n ) : segments.length > 0 ? (\n <div className=\"relative h-6 w-full\">\n {segments.map((seg, i) => {\n const segPixelWidth =\n (seg.endFraction - seg.startFraction) * pixelWidth;\n const segDurationLabel = formatDuration(\n (seg.endFraction - seg.startFraction) * totalDurationMs\n );\n const showSegmentDurationLabel =\n isRowHovered &&\n segPixelWidth >= getMinDurationLabelWidthPx(segDurationLabel);\n const segStyle =\n seg.status === 'queued' && segPixelWidth < 20\n ? { background: 'var(--ds-gray-500)' }\n : SEGMENT_CONFIG[seg.status].style;\n\n return (\n <div\n key={`${seg.status}-${i}`}\n className={cn(\n 'absolute h-full rounded-[0.25rem]',\n SEGMENT_CONFIG[seg.status].className\n )}\n style={{\n left: `calc(${seg.startFraction * 100}% + ${SEGMENT_GAP_PX / 2}px)`,\n width: `calc(${(seg.endFraction - seg.startFraction) * 100}% - ${SEGMENT_GAP_PX}px)`,\n minWidth: 1,\n ...segStyle,\n }}\n >\n {showSegmentDurationLabel\n ? renderDurationLabel(segDurationLabel)\n : null}\n </div>\n );\n })}\n </div>\n ) : (\n <div\n className=\"relative h-6 rounded-[0.25rem]\"\n style={{\n width: '100%',\n minWidth: 4,\n background: fallbackColor,\n }}\n >\n {showBarDurationLabel ? renderDurationLabel(totalDurationLabel) : null}\n </div>\n );\n\n return (\n <div\n role=\"treeitem\"\n aria-selected={isSelected}\n aria-expanded={isSelected}\n aria-level={1}\n className={cn(\n 'h-10 relative flex items-center hover:bg-gray-100 aria-selected:bg-gray-100 aria-selected:hover:bg-gray-200'\n )}\n onMouseEnter={() => setIsRowHovered(true)}\n onMouseLeave={() => setIsRowHovered(false)}\n onClick={onClick}\n >\n <div\n className=\"absolute inset-y-0\"\n style={{\n left: TIMELINE_PADDING_PX,\n right: TIMELINE_PADDING_PX,\n }}\n >\n <div\n className=\"absolute top-1/2 -translate-y-1/2\"\n style={{\n left: showBoundaryArrow\n ? `min(max(${leftPct}%, 0px), calc(100% - ${TINY_BAR_BOX_SIZE_PX}px))`\n : isTinyBar\n ? `min(${leftPct}%, calc(100% - ${TINY_BAR_WIDTH_PX}px))`\n : `${leftPct}%`,\n width: showBoundaryArrow\n ? `${TINY_BAR_BOX_SIZE_PX}px`\n : isTinyBar\n ? `${TINY_BAR_WIDTH_PX}px`\n : `max(${widthPct}%, 4px)`,\n height: BAR_HEIGHT_PX,\n }}\n >\n {barContent}\n </div>\n </div>\n </div>\n );\n});\n\nexport { TimelineBar };\n\nexport function TimelineHeader({\n markers,\n hoverInfo,\n}: {\n markers: TimeMarker[];\n hoverInfo?: { fraction: number; label: string } | null;\n}): ReactNode {\n return (\n <div className=\"relative bg-background-100 border-b border-gray-alpha-400 h-10 min-h-10 flex items-end px-4 pb-1\">\n <div className=\"relative h-full flex-1\">\n {markers.map((m) => (\n <span\n key={`${m.position}-${m.label}`}\n className=\"absolute bottom-1 font-mono text-xs font-normal leading-4 text-gray-900 whitespace-nowrap\"\n style={{ left: `${m.position * 100}%` }}\n >\n {m.label}\n </span>\n ))}\n {hoverInfo && (\n <span\n className=\"absolute top-1 pointer-events-none z-10 font-mono text-[11px] leading-4 text-gray-1000 whitespace-nowrap bg-background-100 border border-gray-alpha-400 rounded px-1 -translate-x-1/2\"\n style={{ left: `${hoverInfo.fraction * 100}%` }}\n >\n {hoverInfo.label}\n </span>\n )}\n </div>\n </div>\n );\n}\n\nexport function Timeline({\n spans,\n viewStart,\n viewEnd,\n markers,\n selectedId,\n onSelect,\n hoverFraction,\n altHeld = false,\n}: {\n spans: Span[];\n viewStart: number;\n viewEnd: number;\n markers: TimeMarker[];\n selectedId: string | null;\n onSelect: (spanId: string) => void;\n hoverFraction?: number | null;\n altHeld?: boolean;\n}): ReactNode {\n const containerRef = useRef<HTMLDivElement>(null);\n const [containerWidth, setContainerWidth] = useState(0);\n const viewDuration = viewEnd - viewStart;\n const timelineWidth = Math.max(0, containerWidth - TIMELINE_PADDING_PX * 2);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n const ro = new ResizeObserver(([entry]) => {\n setContainerWidth(entry.contentRect.width);\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const gaps = useMemo(\n () => computeSpanGaps(spans, viewStart, viewEnd),\n [spans, viewStart, viewEnd]\n );\n\n return (\n <div ref={containerRef} className=\"relative h-full overflow-hidden\">\n <div\n aria-hidden\n className=\"absolute inset-y-0 pointer-events-none\"\n style={{\n left: TIMELINE_PADDING_PX,\n right: TIMELINE_PADDING_PX,\n }}\n >\n {markers.map((marker) =>\n Math.abs(marker.value) > ORIGIN_MARKER_EPSILON ? (\n <div\n key={`${marker.position}-${marker.label}`}\n className=\"absolute top-0 bottom-0 w-px bg-gray-alpha-300\"\n style={{ left: `${marker.position * 100}%` }}\n />\n ) : null\n )}\n </div>\n {hoverFraction != null && (\n <div\n className=\"absolute inset-y-0 pointer-events-none z-10\"\n style={{\n left: TIMELINE_PADDING_PX,\n right: TIMELINE_PADDING_PX,\n }}\n >\n <div\n className=\"absolute top-0 bottom-0 w-px bg-gray-alpha-400\"\n style={{ left: `${hoverFraction * 100}%` }}\n />\n </div>\n )}\n {spans.map((span) => (\n <TimelineBar\n key={span.spanId}\n span={span}\n viewStart={viewStart}\n viewDuration={viewDuration}\n containerWidth={timelineWidth}\n isSelected={selectedId === span.spanId}\n onClick={() => onSelect(span.spanId)}\n />\n ))}\n {altHeld && (\n <div\n aria-hidden\n className=\"absolute inset-y-0 pointer-events-none\"\n style={{\n left: TIMELINE_PADDING_PX,\n right: TIMELINE_PADDING_PX,\n }}\n >\n {gaps.map((gap) => (\n <DeltaIndicator\n key={gap.rowIndex}\n leftFrac={gap.leftFrac}\n rightFrac={gap.rightFrac}\n label={formatDuration(gap.gapMs, true)}\n rowIndex={gap.rowIndex}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { Span } from '../trace-viewer/types';
3
+ interface ActiveSpanContextValue {
4
+ activeSpanId: string | null;
5
+ activeSpan: Span | null;
6
+ setActiveSpan: (spanId: string) => void;
7
+ clearActiveSpan: () => void;
8
+ }
9
+ export declare function ActiveSpanProvider({ spans, children, }: {
10
+ spans: Span[];
11
+ children: ReactNode;
12
+ }): import("react/jsx-runtime").JSX.Element;
13
+ export declare const useActiveSpan: () => ActiveSpanContextValue;
14
+ export {};
15
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/components/new-trace-viewer/context.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAElD,UAAU,sBAAsB;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,eAAe,EAAE,MAAM,IAAI,CAAC;CAC7B;AAKD,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAqDA;AAED,eAAO,MAAM,aAAa,QAAO,sBAOhC,CAAC"}
@@ -0,0 +1,47 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
4
+ const ActiveSpanContext = createContext(null);
5
+ ActiveSpanContext.displayName = 'ActiveSpanContext';
6
+ export function ActiveSpanProvider({ spans, children, }) {
7
+ const [activeSpanId, setActiveSpanId] = useState(spans[0]?.spanId ?? null);
8
+ useEffect(() => {
9
+ setActiveSpanId((currentSpanId) => {
10
+ if (!currentSpanId) {
11
+ return null;
12
+ }
13
+ const hasCurrentSpan = spans.some((span) => span.spanId === currentSpanId);
14
+ if (hasCurrentSpan) {
15
+ return currentSpanId;
16
+ }
17
+ return null;
18
+ });
19
+ }, [spans]);
20
+ const activeSpan = useMemo(() => {
21
+ if (!activeSpanId) {
22
+ return null;
23
+ }
24
+ return spans.find((span) => span.spanId === activeSpanId) ?? null;
25
+ }, [activeSpanId, spans]);
26
+ const setActiveSpan = useCallback((spanId) => {
27
+ setActiveSpanId(spanId);
28
+ }, []);
29
+ const clearActiveSpan = useCallback(() => {
30
+ setActiveSpanId(null);
31
+ }, []);
32
+ const value = useMemo(() => ({
33
+ activeSpanId,
34
+ activeSpan,
35
+ setActiveSpan,
36
+ clearActiveSpan,
37
+ }), [activeSpanId, activeSpan, setActiveSpan, clearActiveSpan]);
38
+ return (_jsx(ActiveSpanContext.Provider, { value: value, children: children }));
39
+ }
40
+ export const useActiveSpan = () => {
41
+ const context = useContext(ActiveSpanContext);
42
+ if (!context) {
43
+ throw new Error('useActiveSpan must be used within ActiveSpanProvider');
44
+ }
45
+ return context;
46
+ };
47
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/components/new-trace-viewer/context.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EACL,aAAa,EAEb,WAAW,EACX,UAAU,EACV,SAAS,EACT,OAAO,EACP,QAAQ,GACT,MAAM,OAAO,CAAC;AAUf,MAAM,iBAAiB,GAAG,aAAa,CAAgC,IAAI,CAAC,CAAC;AAC7E,iBAAiB,CAAC,WAAW,GAAG,mBAAmB,CAAC;AAEpD,MAAM,UAAU,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,GAIT;IACC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAC9C,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,IAAI,CACzB,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,eAAe,CAAC,CAAC,aAAa,EAAE,EAAE;YAChC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnB,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAC/B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,aAAa,CACxC,CAAC;YACF,IAAI,cAAc,EAAE,CAAC;gBACnB,OAAO,aAAa,CAAC;YACvB,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE;QAC9B,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,YAAY,CAAC,IAAI,IAAI,CAAC;IACpE,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;IAE1B,MAAM,aAAa,GAAG,WAAW,CAAC,CAAC,MAAc,EAAE,EAAE;QACnD,eAAe,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;QACvC,eAAe,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,YAAY;QACZ,UAAU;QACV,aAAa;QACb,eAAe;KAChB,CAAC,EACF,CAAC,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,CAAC,CAC3D,CAAC;IAEF,OAAO,CACL,KAAC,iBAAiB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACrC,QAAQ,GACkB,CAC9B,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,GAA2B,EAAE;IACxD,MAAM,OAAO,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC","sourcesContent":["'use client';\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport type { Span } from '../trace-viewer/types';\n\ninterface ActiveSpanContextValue {\n activeSpanId: string | null;\n activeSpan: Span | null;\n setActiveSpan: (spanId: string) => void;\n clearActiveSpan: () => void;\n}\n\nconst ActiveSpanContext = createContext<ActiveSpanContextValue | null>(null);\nActiveSpanContext.displayName = 'ActiveSpanContext';\n\nexport function ActiveSpanProvider({\n spans,\n children,\n}: {\n spans: Span[];\n children: ReactNode;\n}) {\n const [activeSpanId, setActiveSpanId] = useState<string | null>(\n spans[0]?.spanId ?? null\n );\n\n useEffect(() => {\n setActiveSpanId((currentSpanId) => {\n if (!currentSpanId) {\n return null;\n }\n\n const hasCurrentSpan = spans.some(\n (span) => span.spanId === currentSpanId\n );\n if (hasCurrentSpan) {\n return currentSpanId;\n }\n\n return null;\n });\n }, [spans]);\n\n const activeSpan = useMemo(() => {\n if (!activeSpanId) {\n return null;\n }\n\n return spans.find((span) => span.spanId === activeSpanId) ?? null;\n }, [activeSpanId, spans]);\n\n const setActiveSpan = useCallback((spanId: string) => {\n setActiveSpanId(spanId);\n }, []);\n\n const clearActiveSpan = useCallback(() => {\n setActiveSpanId(null);\n }, []);\n\n const value = useMemo<ActiveSpanContextValue>(\n () => ({\n activeSpanId,\n activeSpan,\n setActiveSpan,\n clearActiveSpan,\n }),\n [activeSpanId, activeSpan, setActiveSpan, clearActiveSpan]\n );\n\n return (\n <ActiveSpanContext.Provider value={value}>\n {children}\n </ActiveSpanContext.Provider>\n );\n}\n\nexport const useActiveSpan = (): ActiveSpanContextValue => {\n const context = useContext(ActiveSpanContext);\n if (!context) {\n throw new Error('useActiveSpan must be used within ActiveSpanProvider');\n }\n\n return context;\n};\n"]}
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Span } from '../trace-viewer/types';
3
+ interface DetailPanelProps {
4
+ span: Span;
5
+ rootStart: number;
6
+ onClose: () => void;
7
+ }
8
+ export declare function DetailPanel({ span, rootStart, onClose, }: DetailPanelProps): ReactNode;
9
+ export {};
10
+ //# sourceMappingURL=detail-panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail-panel.d.ts","sourceRoot":"","sources":["../../../src/components/new-trace-viewer/detail-panel.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAIlD,UAAU,gBAAgB;IACxB,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,SAAS,EACT,OAAO,GACR,EAAE,gBAAgB,GAAG,SAAS,CAuC9B"}
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { X } from 'lucide-react';
4
+ import { formatDuration, getHighResInMs } from '../trace-viewer/util/timing';
5
+ import { getSpanDurationMs } from './utils';
6
+ export function DetailPanel({ span, rootStart, onClose, }) {
7
+ const startMs = getHighResInMs(span.startTime);
8
+ const durationMs = getSpanDurationMs(span);
9
+ const offsetMs = startMs - rootStart;
10
+ return (_jsxs("aside", { className: "grid h-full max-h-full grid-rows-[2.5rem_1fr] bg-background-200", children: [_jsxs("div", { className: "flex items-center justify-between px-3 border-b border-gray-alpha-400", children: [_jsx("span", { className: "text-sm font-medium text-gray-1000 truncate", children: span.name }), _jsx("button", { type: "button", className: "p-1 rounded-md text-gray-900 hover:text-gray-1000 hover:bg-gray-alpha-200 transition-colors", onClick: onClose, children: _jsx(X, { className: "w-4 h-4" }) })] }), _jsx("div", { className: "overflow-y-auto p-3 space-y-3", children: _jsxs("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 text-sm", children: [_jsx("dt", { className: "text-gray-900", children: "Resource" }), _jsx("dd", { className: "text-gray-1000 font-mono", children: span.resource }), _jsx("dt", { className: "text-gray-900", children: "Duration" }), _jsx("dd", { className: "text-gray-1000 tabular-nums font-mono", children: formatDuration(durationMs) }), _jsx("dt", { className: "text-gray-900", children: "Offset" }), _jsxs("dd", { className: "text-gray-1000 tabular-nums font-mono", children: ["+", formatDuration(offsetMs)] }), _jsx("dt", { className: "text-gray-900", children: "Status" }), _jsx("dd", { className: "text-gray-1000 font-mono", children: span.status.code === 2 ? 'Error' : 'OK' })] }) })] }));
11
+ }
12
+ //# sourceMappingURL=detail-panel.js.map