@parca/profile 0.19.24 → 0.19.26

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 (50) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.d.ts.map +1 -1
  3. package/dist/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.js +8 -0
  4. package/dist/ProfileFlameGraph/FlameGraphArrow/index.d.ts.map +1 -1
  5. package/dist/ProfileFlameGraph/FlameGraphArrow/index.js +33 -40
  6. package/dist/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.d.ts +8 -0
  7. package/dist/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.d.ts.map +1 -0
  8. package/dist/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.js +70 -0
  9. package/dist/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.d.ts +24 -0
  10. package/dist/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.d.ts.map +1 -0
  11. package/dist/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.js +111 -0
  12. package/dist/ProfileFlameGraph/FlameGraphArrow/utils.d.ts +2 -1
  13. package/dist/ProfileFlameGraph/FlameGraphArrow/utils.d.ts.map +1 -1
  14. package/dist/ProfileFlameGraph/FlameGraphArrow/utils.js +11 -0
  15. package/dist/ProfileView/components/ColorStackLegend.d.ts.map +1 -1
  16. package/dist/ProfileView/components/ColorStackLegend.js +0 -1
  17. package/dist/ProfileView/components/DashboardItems/index.d.ts +3 -2
  18. package/dist/ProfileView/components/DashboardItems/index.d.ts.map +1 -1
  19. package/dist/ProfileView/components/DashboardItems/index.js +2 -2
  20. package/dist/ProfileView/index.d.ts +1 -1
  21. package/dist/ProfileView/index.d.ts.map +1 -1
  22. package/dist/ProfileView/index.js +2 -1
  23. package/dist/ProfileView/types/visualization.d.ts +6 -10
  24. package/dist/ProfileView/types/visualization.d.ts.map +1 -1
  25. package/dist/ProfileViewWithData.d.ts.map +1 -1
  26. package/dist/ProfileViewWithData.js +52 -22
  27. package/dist/Sandwich/components/CalleesSection.d.ts +3 -12
  28. package/dist/Sandwich/components/CalleesSection.d.ts.map +1 -1
  29. package/dist/Sandwich/components/CalleesSection.js +2 -4
  30. package/dist/Sandwich/components/CallersSection.d.ts +3 -13
  31. package/dist/Sandwich/components/CallersSection.d.ts.map +1 -1
  32. package/dist/Sandwich/components/CallersSection.js +5 -8
  33. package/dist/Sandwich/index.d.ts +2 -10
  34. package/dist/Sandwich/index.d.ts.map +1 -1
  35. package/dist/Sandwich/index.js +5 -103
  36. package/dist/styles.css +1 -1
  37. package/package.json +6 -6
  38. package/src/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.tsx +214 -200
  39. package/src/ProfileFlameGraph/FlameGraphArrow/index.tsx +75 -76
  40. package/src/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.ts +89 -0
  41. package/src/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.ts +167 -0
  42. package/src/ProfileFlameGraph/FlameGraphArrow/utils.ts +12 -1
  43. package/src/ProfileView/components/ColorStackLegend.tsx +0 -2
  44. package/src/ProfileView/components/DashboardItems/index.tsx +4 -12
  45. package/src/ProfileView/index.tsx +2 -0
  46. package/src/ProfileView/types/visualization.ts +7 -18
  47. package/src/ProfileViewWithData.tsx +65 -30
  48. package/src/Sandwich/components/CalleesSection.tsx +10 -28
  49. package/src/Sandwich/components/CallersSection.tsx +13 -34
  50. package/src/Sandwich/index.tsx +8 -170
package/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.19.26](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.25...@parca/profile@0.19.26) (2025-07-21)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.19.25](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.24...@parca/profile@0.19.25) (2025-07-21)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## [0.19.24](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.23...@parca/profile@0.19.24) (2025-07-17)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -1 +1 @@
1
- {"version":3,"file":"FlameGraphNodes.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAgB,MAAM,OAAO,CAAC;AAErC,OAAO,EAAC,KAAK,EAAC,MAAM,cAAc,CAAC;AAKnC,OAAO,yCAAyC,CAAC;AAEjD,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAelD,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,MAAM,WAAW,aAAa;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,oBAAoB,EAAE,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CACnD;AAED,eAAO,MAAM,eAAe;;;CAG3B,CAAC;AACF,eAAO,MAAM,oBAAoB;;;;CAIhC,CAAC;AAEF,eAAO,MAAM,SAAS,4CAgNpB,CAAC"}
1
+ {"version":3,"file":"FlameGraphNodes.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAgB,MAAM,OAAO,CAAC;AAErC,OAAO,EAAC,KAAK,EAAC,MAAM,cAAc,CAAC;AAKnC,OAAO,yCAAyC,CAAC;AAEjD,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAelD,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,MAAM,WAAW,aAAa;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,oBAAoB,EAAE,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CACnD;AAED,eAAO,MAAM,eAAe;;;CAG3B,CAAC;AACF,eAAO,MAAM,oBAAoB;;;;CAIhC,CAAC;AAEF,eAAO,MAAM,SAAS,4CA8NrB,CAAC"}
@@ -131,4 +131,12 @@ export const FlameNode = React.memo(function FlameNodeNoMemo({ table, row, color
131
131
  }, className: cx(shouldBeHighlighted
132
132
  ? `${colorForSimilarNodes} stroke-[3] [stroke-dasharray:6,4] [stroke-linecap:round] [stroke-linejoin:round] h-6`
133
133
  : 'stroke-white dark:stroke-gray-700') }), width > 5 && (_jsx("svg", { width: width - 5, height: height, children: _jsx(TextWithEllipsis, { text: name, x: 5, y: 15, width: width - 10 }) }))] }) }));
134
+ }, (prevProps, nextProps) => {
135
+ // Only re-render if the relevant props have changed
136
+ return (prevProps.row === nextProps.row &&
137
+ prevProps.selectedRow === nextProps.selectedRow &&
138
+ prevProps.hoveringRow === nextProps.hoveringRow &&
139
+ prevProps.totalWidth === nextProps.totalWidth &&
140
+ prevProps.height === nextProps.height &&
141
+ prevProps.effectiveDepth === nextProps.effectiveDepth);
134
142
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAQN,MAAM,OAAO,CAAC;AAKf,OAAO,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAG9C,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAc,KAAK,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAE/D,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAIlD,OAAO,EAAuB,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAItE,OAAO,EACL,gBAAgB,EAMjB,MAAM,SAAS,CAAC;AAEjB,eAAO,MAAM,iBAAiB,gBAAgB,CAAC;AAC/C,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,aAAa,YAAY,CAAC;AACvC,eAAO,MAAM,eAAe,cAAc,CAAC;AAC3C,eAAO,MAAM,cAAc,aAAa,CAAC;AACzC,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,0BAA0B,yBAAyB,CAAC;AACjE,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAC7D,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAC9D,eAAO,MAAM,cAAc,aAAa,CAAC;AACzC,eAAO,MAAM,YAAY,WAAW,CAAC;AACrC,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAC7C,eAAO,MAAM,UAAU,SAAS,CAAC;AACjC,eAAO,MAAM,UAAU,SAAS,CAAC;AACjC,eAAO,MAAM,YAAY,WAAW,CAAC;AACrC,eAAO,MAAM,WAAW,UAAU,CAAC;AACnC,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AAEjD,UAAU,oBAAoB;IAC5B,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC/C,YAAY,EAAE,OAAO,CAAC;IACtB,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,gBAAgB,GAC3B,cAAc,MAAM,EAAE,EACtB,YAAY,OAAO,EACnB,qBAAqB,WAAW,KAC/B,aAQF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,eAAe,MAAM,EAAE,EACvB,YAAY,OAAO,EACnB,qBAAqB,WAAW,KAC/B,aAQF,CAAC;AAeF,eAAO,MAAM,eAAe,kDA+Q1B,CAAC;AAEH,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAQN,MAAM,OAAO,CAAC;AAKf,OAAO,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAG9C,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAc,KAAK,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAE/D,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAIlD,OAAO,EAAuB,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAMtE,OAAO,EACL,gBAAgB,EAOjB,MAAM,SAAS,CAAC;AAEjB,eAAO,MAAM,iBAAiB,gBAAgB,CAAC;AAC/C,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,aAAa,YAAY,CAAC;AACvC,eAAO,MAAM,eAAe,cAAc,CAAC;AAC3C,eAAO,MAAM,cAAc,aAAa,CAAC;AACzC,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,0BAA0B,yBAAyB,CAAC;AACjE,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAC7D,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAC9D,eAAO,MAAM,cAAc,aAAa,CAAC;AACzC,eAAO,MAAM,YAAY,WAAW,CAAC;AACrC,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAC7C,eAAO,MAAM,UAAU,SAAS,CAAC;AACjC,eAAO,MAAM,UAAU,SAAS,CAAC;AACjC,eAAO,MAAM,YAAY,WAAW,CAAC;AACrC,eAAO,MAAM,WAAW,UAAU,CAAC;AACnC,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AAEjD,UAAU,oBAAoB;IAC5B,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC/C,YAAY,EAAE,OAAO,CAAC;IACtB,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,gBAAgB,GAC3B,cAAc,MAAM,EAAE,EACtB,YAAY,OAAO,EACnB,qBAAqB,WAAW,KAC/B,aAQF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,eAAe,MAAM,EAAE,EACvB,YAAY,OAAO,EACnB,qBAAqB,WAAW,KAC/B,aAQF,CAAC;AAIF,eAAO,MAAM,eAAe,kDAsR1B,CAAC;AAEH,eAAe,eAAe,CAAC"}
@@ -25,7 +25,9 @@ import { FlameNode, RowHeight } from './FlameGraphNodes';
25
25
  import { MemoizedTooltip } from './MemoizedTooltip';
26
26
  import { TooltipProvider } from './TooltipContext';
27
27
  import { useFilenamesList } from './useMappingList';
28
- import { arrowToString, extractFeature, extractFilenameFeature, getCurrentPathFrameData, isCurrentPathFrameMatch, } from './utils';
28
+ import { useScrollViewport } from './useScrollViewport';
29
+ import { useVisibleNodes } from './useVisibleNodes';
30
+ import { arrowToString, extractFeature, extractFilenameFeature, getCurrentPathFrameData, getMaxDepth, isCurrentPathFrameMatch, } from './utils';
29
31
  export const FIELD_LABELS_ONLY = 'labels_only';
30
32
  export const FIELD_MAPPING_FILE = 'mapping_file';
31
33
  export const FIELD_MAPPING_BUILD_ID = 'mapping_build_id';
@@ -64,17 +66,6 @@ export const getFilenameColors = (filenamesList, isDarkMode, currentColorProfile
64
66
  return colors;
65
67
  };
66
68
  const noop = () => { };
67
- function getMaxDepth(depthColumn) {
68
- if (depthColumn === null)
69
- return 0;
70
- let max = 0;
71
- for (const val of depthColumn) {
72
- const numVal = Number(val);
73
- if (numVal > max)
74
- max = numVal;
75
- }
76
- return max;
77
- }
78
69
  export const FlameGraphArrow = memo(function FlameGraphArrow({ arrow, total, filtered, width, setCurPath, curPath, profileType, profileSource, compareAbsolute, isFlameChart = false, isRenderedAsFlamegraph = false, isInSandwichView = false, tooltipId = 'default', maxFrameCount, isExpanded = false, }) {
79
70
  const [highlightSimilarStacksPreference] = useUserPreference(USER_PREFERENCES.HIGHLIGHT_SIMILAR_STACKS.key);
80
71
  const [hoveringRow, setHoveringRow] = useState(undefined);
@@ -89,28 +80,9 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({ arrow, total, fil
89
80
  return result;
90
81
  }, [arrow, perf]);
91
82
  const svg = useRef(null);
83
+ const containerRef = useRef(null);
92
84
  const renderStartTime = useRef(0);
93
85
  const [svgElement, setSvgElement] = useState(null);
94
- useEffect(() => {
95
- if (perf?.markInteraction != null) {
96
- renderStartTime.current = performance.now();
97
- }
98
- }, [table, width, curPath, perf]);
99
- useEffect(() => {
100
- if (perf?.setMeasurement != null && renderStartTime.current > 0) {
101
- const measureRenderTime = () => {
102
- const renderTime = performance.now() - renderStartTime.current;
103
- if (perf?.setMeasurement != null) {
104
- perf.setMeasurement('flamegraph.render_time', renderTime);
105
- }
106
- renderStartTime.current = 0;
107
- };
108
- requestAnimationFrame(measureRenderTime);
109
- }
110
- }, [table, width, curPath, perf]);
111
- useEffect(() => {
112
- setSvgElement(svg.current);
113
- }, [tooltipId]);
114
86
  const { excludeBinary } = useProfileFilters();
115
87
  const { compareMode } = useProfileViewContext();
116
88
  const currentColorProfile = useCurrentColorProfile();
@@ -195,9 +167,11 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({ arrow, total, fil
195
167
  const effectiveDepth = maxFrameCount !== undefined && !isExpanded ? Math.min(maxDepth, maxFrameCount) : maxDepth;
196
168
  // Use deferred value to prevent UI blocking when expanding frames
197
169
  const deferredEffectiveDepth = useDeferredValue(effectiveDepth);
198
- const height = isInSandwichView
170
+ const totalHeight = isInSandwichView
199
171
  ? deferredEffectiveDepth * RowHeight
200
172
  : (deferredEffectiveDepth + 1) * RowHeight;
173
+ // Get the viewport of the container, this is used to determine which rows are visible.
174
+ const viewport = useScrollViewport(containerRef);
201
175
  // To find the selected row, we must walk the current path and look at which
202
176
  // children of the current frame matches the path element exactly. Until the
203
177
  // end, the row we find at the end is our selected row.
@@ -222,12 +196,31 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({ arrow, total, fil
222
196
  currentRow = childRows[0];
223
197
  }
224
198
  const selectedRow = currentRow;
225
- return (_jsx(TooltipProvider, { table: table, total: total, totalUnfiltered: total + filtered, profileType: profileType, unit: arrow.unit, compareAbsolute: compareAbsolute, tooltipId: tooltipId, children: _jsxs("div", { className: "relative", children: [_jsx(ContextMenuWrapper, { ref: contextMenuRef, menuId: MENU_ID, table: table, total: total, totalUnfiltered: total + filtered, compareAbsolute: compareAbsolute, resetPath: () => setCurPath([]), hideMenu: hideAll, hideBinary: hideBinary, unit: arrow.unit, profileType: profileType, isInSandwichView: isInSandwichView }), _jsx(MemoizedTooltip, { contextElement: svgElement, dockedMetainfo: dockedMetainfo }), _jsx("svg", { className: "font-robotoMono", width: width, height: height, preserveAspectRatio: "xMinYMid", ref: svg, children: Array.from({ length: table.numRows }, (_, row) => (_jsx(FlameNode, { table: table, row: row, colors: colorByColors, colorBy: colorByValue, totalWidth: width ?? 1, height: RowHeight, darkMode: isDarkMode, compareMode: compareMode, colorForSimilarNodes: colorForSimilarNodes, selectedRow: selectedRow, onClick: () => {
226
- if (isFlameChart) {
227
- // We don't want to expand in flame charts.
228
- return;
229
- }
230
- handleRowClick(row);
231
- }, onContextMenu: displayMenu, hoveringRow: highlightSimilarStacksPreference ? hoveringRow : undefined, setHoveringRow: highlightSimilarStacksPreference ? setHoveringRow : noop, isFlameChart: isFlameChart, profileSource: profileSource, isRenderedAsFlamegraph: isRenderedAsFlamegraph, isInSandwichView: isInSandwichView, maxDepth: maxDepth, effectiveDepth: deferredEffectiveDepth, tooltipId: tooltipId }, row))) })] }) }));
199
+ const visibleNodes = useVisibleNodes({
200
+ table,
201
+ viewport,
202
+ total,
203
+ width: width ?? 1,
204
+ selectedRow,
205
+ effectiveDepth: deferredEffectiveDepth,
206
+ });
207
+ useEffect(() => {
208
+ if (perf?.markInteraction != null) {
209
+ renderStartTime.current = performance.now();
210
+ }
211
+ }, [table, width, curPath, perf]);
212
+ useEffect(() => {
213
+ setSvgElement(svg.current);
214
+ }, [tooltipId]);
215
+ return (_jsx(TooltipProvider, { table: table, total: total, totalUnfiltered: total + filtered, profileType: profileType, unit: arrow.unit, compareAbsolute: compareAbsolute, tooltipId: tooltipId, children: _jsxs("div", { className: "relative", children: [_jsx(ContextMenuWrapper, { ref: contextMenuRef, menuId: MENU_ID, table: table, total: total, totalUnfiltered: total + filtered, compareAbsolute: compareAbsolute, resetPath: () => setCurPath([]), hideMenu: hideAll, hideBinary: hideBinary, unit: arrow.unit, profileType: profileType, isInSandwichView: isInSandwichView }), _jsx(MemoizedTooltip, { contextElement: svgElement, dockedMetainfo: dockedMetainfo }), _jsx("div", { ref: containerRef, className: "overflow-auto scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-100 dark:scrollbar-thumb-gray-600 dark:scrollbar-track-gray-800 will-change-transform scroll-smooth webkit-overflow-scrolling-touch contain", style: {
216
+ width: width ?? '100%',
217
+ contain: 'layout style paint',
218
+ }, children: _jsx("svg", { className: "font-robotoMono", width: width ?? 0, height: totalHeight, preserveAspectRatio: "xMinYMid", ref: svg, children: visibleNodes.map(row => (_jsx(FlameNode, { table: table, row: row, colors: colorByColors, colorBy: colorByValue, totalWidth: width ?? 1, height: RowHeight, darkMode: isDarkMode, compareMode: compareMode, colorForSimilarNodes: colorForSimilarNodes, selectedRow: selectedRow, onClick: () => {
219
+ if (isFlameChart) {
220
+ // We don't want to expand in flame charts.
221
+ return;
222
+ }
223
+ handleRowClick(row);
224
+ }, onContextMenu: displayMenu, hoveringRow: highlightSimilarStacksPreference ? hoveringRow : undefined, setHoveringRow: highlightSimilarStacksPreference ? setHoveringRow : noop, isFlameChart: isFlameChart, profileSource: profileSource, isRenderedAsFlamegraph: isRenderedAsFlamegraph, isInSandwichView: isInSandwichView, maxDepth: maxDepth, effectiveDepth: deferredEffectiveDepth, tooltipId: tooltipId }, row))) }) })] }) }));
232
225
  });
233
226
  export default FlameGraphArrow;
@@ -0,0 +1,8 @@
1
+ export interface ViewportState {
2
+ scrollTop: number;
3
+ scrollLeft: number;
4
+ containerHeight: number;
5
+ containerWidth: number;
6
+ }
7
+ export declare const useScrollViewport: (containerRef: React.RefObject<HTMLDivElement>) => ViewportState;
8
+ //# sourceMappingURL=useScrollViewport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScrollViewport.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,iBAAiB,GAAI,cAAc,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,KAAG,aAkEjF,CAAC"}
@@ -0,0 +1,70 @@
1
+ // Copyright 2022 The Parca Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { useCallback, useEffect, useRef, useState } from 'react';
14
+ export const useScrollViewport = (containerRef) => {
15
+ const [viewport, setViewport] = useState({
16
+ scrollTop: 0,
17
+ scrollLeft: 0,
18
+ containerHeight: 0,
19
+ containerWidth: 0,
20
+ });
21
+ const throttleRef = useRef(null);
22
+ const updateViewport = useCallback(() => {
23
+ if (containerRef.current !== null) {
24
+ const container = containerRef.current;
25
+ const newViewport = {
26
+ scrollTop: container.scrollTop,
27
+ scrollLeft: container.scrollLeft,
28
+ containerHeight: container.clientHeight,
29
+ containerWidth: container.clientWidth,
30
+ };
31
+ setViewport(newViewport);
32
+ }
33
+ }, [containerRef]);
34
+ // Throttling Strategy:
35
+ // Use requestAnimationFrame to throttle scroll events to 60fps max
36
+ // This ensures smooth performance while preventing excessive re-renders
37
+ const throttledUpdateViewport = useCallback(() => {
38
+ if (throttleRef.current !== null) {
39
+ cancelAnimationFrame(throttleRef.current);
40
+ }
41
+ throttleRef.current = requestAnimationFrame(updateViewport);
42
+ }, [updateViewport]);
43
+ useEffect(() => {
44
+ const container = containerRef.current;
45
+ if (container === null)
46
+ return;
47
+ // ResizeObserver Strategy:
48
+ // Monitor container size changes (window resize, layout shifts)
49
+ // to update viewport dimensions for accurate culling calculations
50
+ const resizeObserver = new ResizeObserver(() => {
51
+ throttledUpdateViewport();
52
+ });
53
+ // Container Scroll Event Strategy:
54
+ // Use passive event listeners for better scroll performance
55
+ // Throttle with requestAnimationFrame to maintain 60fps target
56
+ container.addEventListener('scroll', throttledUpdateViewport, { passive: true });
57
+ resizeObserver.observe(container);
58
+ // Initialize viewport state on mount
59
+ updateViewport();
60
+ return () => {
61
+ // Cleanup: Remove event listeners and cancel pending animations
62
+ container.removeEventListener('scroll', throttledUpdateViewport);
63
+ resizeObserver.disconnect();
64
+ if (throttleRef.current !== null) {
65
+ cancelAnimationFrame(throttleRef.current);
66
+ }
67
+ };
68
+ }, [containerRef, throttledUpdateViewport, updateViewport]);
69
+ return viewport;
70
+ };
@@ -0,0 +1,24 @@
1
+ import { Table } from 'apache-arrow';
2
+ import { ViewportState } from './useScrollViewport';
3
+ export interface UseVisibleNodesParams {
4
+ table: Table<any>;
5
+ viewport: ViewportState;
6
+ total: bigint;
7
+ width: number;
8
+ selectedRow: number;
9
+ effectiveDepth: number;
10
+ }
11
+ /**
12
+ * useVisibleNodes returns row indices visible in the current viewport through multi-stage culling.
13
+ * Combines depth buckets, horizontal bounds checking, and size filtering to
14
+ * minimize rendered nodes from potentially 100K+ rows to ~hundreds.
15
+ *
16
+ * We use depth buckets to only iterate through the rows that are visible in the viewport vertically.
17
+ * After that we use horizontal bounds checking to only iterate through the rows that are visible in the viewport horizontally.
18
+ * Finally we use size filtering to only iterate through the rows that are visible in the viewport by size.
19
+ *
20
+ * Critical for maintaining 60fps performance on large flamegraphs where
21
+ * rendering all nodes would freeze the browser.
22
+ */
23
+ export declare const useVisibleNodes: ({ table, viewport, total, width, selectedRow, effectiveDepth, }: UseVisibleNodesParams) => number[];
24
+ //# sourceMappingURL=useVisibleNodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVisibleNodes.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,KAAK,EAAC,MAAM,cAAc,CAAC;AAInC,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAiClD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,QAAQ,EAAE,aAAa,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAAI,iEAO7B,qBAAqB,KAAG,MAAM,EAsFhC,CAAC"}
@@ -0,0 +1,111 @@
1
+ // Copyright 2022 The Parca Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { useMemo, useRef } from 'react';
14
+ import { RowHeight } from './FlameGraphNodes';
15
+ import { FIELD_CUMULATIVE, FIELD_DEPTH, FIELD_VALUE_OFFSET } from './index';
16
+ import { getMaxDepth } from './utils';
17
+ /**
18
+ * This function groups rows by their depth level.
19
+ * Instead of scanning all rows to find depth matches, we pre-compute
20
+ * buckets so viewport rendering only examines depth ranges that are relevant.
21
+ */
22
+ const useDepthBuckets = (table) => {
23
+ return useMemo(() => {
24
+ if (table === undefined)
25
+ return [];
26
+ const depthColumn = table.getChild(FIELD_DEPTH);
27
+ if (depthColumn === null)
28
+ return [];
29
+ // Find max depth
30
+ const maxDepth = getMaxDepth(depthColumn);
31
+ // Create buckets for each depth level
32
+ const buckets = Array.from({ length: maxDepth + 1 }, () => []);
33
+ // Populate buckets with row indices
34
+ for (let row = 0; row < table.numRows; row++) {
35
+ const depth = depthColumn.get(row) ?? 0;
36
+ buckets[depth].push(row);
37
+ }
38
+ return buckets;
39
+ }, [table]);
40
+ };
41
+ /**
42
+ * useVisibleNodes returns row indices visible in the current viewport through multi-stage culling.
43
+ * Combines depth buckets, horizontal bounds checking, and size filtering to
44
+ * minimize rendered nodes from potentially 100K+ rows to ~hundreds.
45
+ *
46
+ * We use depth buckets to only iterate through the rows that are visible in the viewport vertically.
47
+ * After that we use horizontal bounds checking to only iterate through the rows that are visible in the viewport horizontally.
48
+ * Finally we use size filtering to only iterate through the rows that are visible in the viewport by size.
49
+ *
50
+ * Critical for maintaining 60fps performance on large flamegraphs where
51
+ * rendering all nodes would freeze the browser.
52
+ */
53
+ export const useVisibleNodes = ({ table, viewport, total, width, selectedRow, effectiveDepth, }) => {
54
+ const depthBuckets = useDepthBuckets(table);
55
+ const lastResultRef = useRef({ key: '', result: [] });
56
+ return useMemo(() => {
57
+ // Create a stable key for memoization to prevent unnecessary recalculations
58
+ const memoKey = `${viewport.scrollTop}-${viewport.containerHeight}-${selectedRow}-${effectiveDepth}-${width}`;
59
+ // Return cached result if viewport hasn't meaningfully changed
60
+ if (lastResultRef.current.key === memoKey) {
61
+ return lastResultRef.current.result;
62
+ }
63
+ if (table === null || viewport.containerHeight === 0)
64
+ return [];
65
+ const visibleRows = [];
66
+ const { scrollTop, containerHeight } = viewport;
67
+ // Viewport Culling Algorithm:
68
+ // 1. Calculate visible depth range based on scroll position and container height
69
+ // 2. Add 5-row buffer above/below for smooth scrolling experience
70
+ const startDepth = Math.max(0, Math.floor(scrollTop / RowHeight) - 5);
71
+ const endDepth = Math.min(effectiveDepth, Math.ceil((scrollTop + containerHeight) / RowHeight) + 5);
72
+ const cumulativeColumn = table.getChild(FIELD_CUMULATIVE);
73
+ const valueOffsetColumn = table.getChild(FIELD_VALUE_OFFSET);
74
+ const selectionOffset = valueOffsetColumn?.get(selectedRow) !== null &&
75
+ valueOffsetColumn?.get(selectedRow) !== undefined
76
+ ? BigInt(valueOffsetColumn?.get(selectedRow))
77
+ : 0n;
78
+ const selectionCumulative = cumulativeColumn?.get(selectedRow) !== null ? BigInt(cumulativeColumn?.get(selectedRow)) : 0n;
79
+ const totalNumber = Number(total);
80
+ const selectionOffsetNumber = Number(selectionOffset);
81
+ const selectionCumulativeNumber = Number(selectionCumulative);
82
+ // Iterate only through visible depth range instead of all rows
83
+ for (let depth = startDepth; depth <= endDepth && depth < depthBuckets.length; depth++) {
84
+ // Skip if depth is beyond effective depth limit
85
+ if (effectiveDepth !== undefined && depth > effectiveDepth) {
86
+ continue;
87
+ }
88
+ const rowsAtDepth = depthBuckets[depth];
89
+ for (const row of rowsAtDepth) {
90
+ const cumulative = cumulativeColumn?.get(row) !== null ? Number(cumulativeColumn?.get(row)) : 0;
91
+ const valueOffset = valueOffsetColumn?.get(row) !== null && valueOffsetColumn?.get(row) !== undefined
92
+ ? Number(valueOffsetColumn?.get(row))
93
+ : 0;
94
+ // Horizontal culling: Skip nodes outside selection bounds
95
+ if (valueOffset + cumulative <= selectionOffsetNumber ||
96
+ valueOffset >= selectionOffsetNumber + selectionCumulativeNumber) {
97
+ continue;
98
+ }
99
+ // Size culling: Skip nodes too small to be visible (< 1px width)
100
+ const computedWidth = (cumulative / totalNumber) * width;
101
+ if (computedWidth <= 1) {
102
+ continue;
103
+ }
104
+ visibleRows.push(row);
105
+ }
106
+ }
107
+ // Cache the result with the current key
108
+ lastResultRef.current = { key: memoKey, result: visibleRows };
109
+ return visibleRows;
110
+ }, [depthBuckets, viewport, total, width, selectedRow, effectiveDepth, table]);
111
+ };
@@ -1,4 +1,4 @@
1
- import { Table } from 'apache-arrow';
1
+ import { Table, Vector } from 'apache-arrow';
2
2
  import { type BinaryFeature, type FilenameFeature } from '@parca/store';
3
3
  import { ProfileSource } from '../../ProfileSource';
4
4
  import { BigIntDuo } from '../../utils';
@@ -20,4 +20,5 @@ export interface CurrentPathFrame {
20
20
  }
21
21
  export declare const getCurrentPathFrameData: (table: Table<any>, row: number) => CurrentPathFrame;
22
22
  export declare function isCurrentPathFrameMatch(table: Table<any>, row: number, b: CurrentPathFrame): boolean;
23
+ export declare function getMaxDepth(depthColumn: Vector<any> | null): number;
23
24
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/utils.ts"],"names":[],"mappings":"AAaA,OAAO,EAAC,KAAK,EAAC,MAAM,cAAc,CAAC;AAEnC,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAsB,aAAa,EAAC,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAC,SAAS,EAAgB,MAAM,aAAa,CAAC;AAWrD,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,MAAM,CAuBzF;AAED,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,aAMhD,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,UAAU,MAAM,KAAG,eAMzD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,wBAAwB,MAAM,EAC9B,iBAAiB,MAAM,EACvB,OAAO,MAAM,EACb,MAAM,MAAM,KACX,MAOF,CAAC;AAEF,eAAO,MAAM,6BAA6B,GACxC,wBAAwB,MAAM,EAC9B,MAAM,MAAM,KACX,MAMF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,QAAQ,GAAG,KAAG,MAAM,GAAG,IAQpD,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,gBAAgB,aAAa,KAAG,SAyBvE,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,uBAAuB,GAAI,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM,KAAG,gBAwBxE,CAAC;AAgBF,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,GAAG,EAAE,MAAM,EACX,CAAC,EAAE,gBAAgB,GAClB,OAAO,CAWT"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/utils.ts"],"names":[],"mappings":"AAaA,OAAO,EAAC,KAAK,EAAE,MAAM,EAAC,MAAM,cAAc,CAAC;AAE3C,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAsB,aAAa,EAAC,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAC,SAAS,EAAgB,MAAM,aAAa,CAAC;AAWrD,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,MAAM,CAuBzF;AAED,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,aAMhD,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,UAAU,MAAM,KAAG,eAMzD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,wBAAwB,MAAM,EAC9B,iBAAiB,MAAM,EACvB,OAAO,MAAM,EACb,MAAM,MAAM,KACX,MAOF,CAAC;AAEF,eAAO,MAAM,6BAA6B,GACxC,wBAAwB,MAAM,EAC9B,MAAM,MAAM,KACX,MAMF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,QAAQ,GAAG,KAAG,MAAM,GAAG,IAQpD,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,gBAAgB,aAAa,KAAG,SAyBvE,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,uBAAuB,GAAI,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM,KAAG,gBAwBxE,CAAC;AAgBF,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,GAAG,EAAE,MAAM,EACX,CAAC,EAAE,gBAAgB,GAClB,OAAO,CAWT;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,MAAM,CASnE"}
@@ -131,3 +131,14 @@ export function isCurrentPathFrameMatch(table, row, b) {
131
131
  a.inlined === b.inlined &&
132
132
  a.labels === b.labels);
133
133
  }
134
+ export function getMaxDepth(depthColumn) {
135
+ if (depthColumn === null)
136
+ return 0;
137
+ let max = 0;
138
+ for (const val of depthColumn) {
139
+ const numVal = Number(val);
140
+ if (numVal > max)
141
+ max = numVal;
142
+ }
143
+ return max;
144
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ColorStackLegend.d.ts","sourceRoot":"","sources":["../../../src/ProfileView/components/ColorStackLegend.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAgB,MAAM,OAAO,CAAC;AAarC,UAAU,KAAK;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,QAAA,MAAM,gBAAgB,GAAI,oCAA0C,KAAK,KAAG,KAAK,CAAC,GAAG,CAAC,OAyGrF,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"ColorStackLegend.d.ts","sourceRoot":"","sources":["../../../src/ProfileView/components/ColorStackLegend.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAgB,MAAM,OAAO,CAAC;AAarC,UAAU,KAAK;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,QAAA,MAAM,gBAAgB,GAAI,oCAA0C,KAAK,KAAG,KAAK,CAAC,GAAG,CAAC,OAuGrF,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -33,7 +33,6 @@ const ColorStackLegend = ({ mappings, compareMode = false, loading }) => {
33
33
  .filter(f => f.type === 'frame' && f.field === 'binary')
34
34
  .map(f => f.value);
35
35
  }, [appliedFilters]);
36
- console.log('currentBinaryFilters', currentBinaryFilters);
37
36
  const mappingsList = useMappingList(mappings);
38
37
  const mappingColors = useMemo(() => {
39
38
  const colors = getMappingColors(mappingsList, isDarkMode, currentColorProfile);
@@ -2,7 +2,7 @@ import { ProfilerOnRenderCallback } from 'react';
2
2
  import { QueryServiceClient } from '@parca/client';
3
3
  import { CurrentPathFrame } from '../../../ProfileFlameGraph/FlameGraphArrow/utils';
4
4
  import { ProfileSource } from '../../../ProfileSource';
5
- import type { FlamegraphData, SourceData, TopTableData, VisualizationType } from '../../types/visualization';
5
+ import type { FlamegraphData, SandwichData, SourceData, TopTableData, VisualizationType } from '../../types/visualization';
6
6
  interface GetDashboardItemProps {
7
7
  type: VisualizationType;
8
8
  isHalfScreen: boolean;
@@ -10,6 +10,7 @@ interface GetDashboardItemProps {
10
10
  flamegraphData: FlamegraphData;
11
11
  flamechartData: FlamegraphData;
12
12
  topTableData?: TopTableData;
13
+ sandwichData: SandwichData;
13
14
  sourceData?: SourceData;
14
15
  profileSource: ProfileSource;
15
16
  total: bigint;
@@ -23,6 +24,6 @@ interface GetDashboardItemProps {
23
24
  };
24
25
  queryClient?: QueryServiceClient;
25
26
  }
26
- export declare const getDashboardItem: ({ type, isHalfScreen, dimensions, flamegraphData, flamechartData, topTableData, sourceData, profileSource, total, filtered, curPathArrow, setNewCurPathArrow, perf, queryClient, }: GetDashboardItemProps) => JSX.Element;
27
+ export declare const getDashboardItem: ({ type, isHalfScreen, dimensions, flamegraphData, flamechartData, topTableData, sourceData, sandwichData, profileSource, total, filtered, curPathArrow, setNewCurPathArrow, perf, }: GetDashboardItemProps) => JSX.Element;
27
28
  export {};
28
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/DashboardItems/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAW,wBAAwB,EAAC,MAAM,OAAO,CAAC;AAEzD,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAIjD,OAAO,EAAC,gBAAgB,EAAC,MAAM,kDAAkD,CAAC;AAClF,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAIrD,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,YAAY,EACZ,iBAAiB,EAClB,MAAM,2BAA2B,CAAC;AAEnC,UAAU,qBAAqB;IAC7B,IAAI,EAAE,iBAAiB,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,kBAAkB,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IACvD,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,wBAAwB,CAAC;KACrC,CAAC;IACF,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED,eAAO,MAAM,gBAAgB,GAAI,oLAe9B,qBAAqB,KAAG,GAAG,CAAC,OAyG9B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/DashboardItems/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAW,wBAAwB,EAAC,MAAM,OAAO,CAAC;AAEzD,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAIjD,OAAO,EAAC,gBAAgB,EAAC,MAAM,kDAAkD,CAAC;AAClF,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAIrD,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,iBAAiB,EAClB,MAAM,2BAA2B,CAAC;AAEnC,UAAU,qBAAqB;IAC7B,IAAI,EAAE,iBAAiB,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,kBAAkB,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IACvD,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,wBAAwB,CAAC;KACrC,CAAC;IACF,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED,eAAO,MAAM,gBAAgB,GAAI,qLAe9B,qBAAqB,KAAG,GAAG,CAAC,OA+F9B,CAAC"}
@@ -17,7 +17,7 @@ import ProfileFlameGraph from '../../../ProfileFlameGraph';
17
17
  import Sandwich from '../../../Sandwich';
18
18
  import { SourceView } from '../../../SourceView';
19
19
  import { Table } from '../../../Table';
20
- export const getDashboardItem = ({ type, isHalfScreen, dimensions, flamegraphData, flamechartData, topTableData, sourceData, profileSource, total, filtered, curPathArrow, setNewCurPathArrow, perf, queryClient, }) => {
20
+ export const getDashboardItem = ({ type, isHalfScreen, dimensions, flamegraphData, flamechartData, topTableData, sourceData, sandwichData, profileSource, total, filtered, curPathArrow, setNewCurPathArrow, perf, }) => {
21
21
  switch (type) {
22
22
  case 'flamegraph':
23
23
  return (_jsx(ConditionalWrapper, { condition: perf?.onRender != null, WrapperComponent: Profiler, wrapperProps: {
@@ -37,7 +37,7 @@ export const getDashboardItem = ({ type, isHalfScreen, dimensions, flamegraphDat
37
37
  case 'table':
38
38
  return topTableData != null ? (_jsx(Table, { total: total, filtered: filtered, loading: topTableData.loading, data: topTableData.arrow?.record, unit: topTableData.unit, profileType: profileSource?.ProfileType(), isHalfScreen: isHalfScreen, metadataMappingFiles: flamegraphData.metadataMappingFiles })) : (_jsx(_Fragment, {}));
39
39
  case 'sandwich':
40
- return topTableData != null ? (_jsx(Sandwich, { total: total, filtered: filtered, loading: topTableData.loading, data: topTableData.arrow?.record, unit: topTableData.unit, profileType: profileSource?.ProfileType(), metadataMappingFiles: flamegraphData.metadataMappingFiles, profileSource: profileSource, queryClient: queryClient })) : (_jsx(_Fragment, {}));
40
+ return topTableData != null ? (_jsx(Sandwich, { profileSource: profileSource, sandwichData: sandwichData })) : (_jsx(_Fragment, {}));
41
41
  case 'source':
42
42
  return sourceData != null ? (_jsx(SourceView, { loading: sourceData.loading, data: sourceData.data, total: total, filtered: filtered })) : (_jsx(_Fragment, {}));
43
43
  default:
@@ -1,3 +1,3 @@
1
1
  import type { ProfileViewProps } from './types/visualization';
2
- export declare const ProfileView: ({ total, filtered, flamegraphData, flamechartData, topTableData, sourceData, profileSource, queryClient, onDownloadPProf, pprofDownloading, compare, showVisualizationSelector, }: ProfileViewProps) => JSX.Element;
2
+ export declare const ProfileView: ({ total, filtered, flamegraphData, flamechartData, topTableData, sourceData, profileSource, queryClient, onDownloadPProf, pprofDownloading, compare, showVisualizationSelector, sandwichData, }: ProfileViewProps) => JSX.Element;
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileView/index.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAC,gBAAgB,EAAoB,MAAM,uBAAuB,CAAC;AAE/E,eAAO,MAAM,WAAW,GAAI,mLAazB,gBAAgB,KAAG,GAAG,CAAC,OA8HzB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileView/index.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAC,gBAAgB,EAAoB,MAAM,uBAAuB,CAAC;AAE/E,eAAO,MAAM,WAAW,GAAI,iMAczB,gBAAgB,KAAG,GAAG,CAAC,OA+HzB,CAAC"}
@@ -23,7 +23,7 @@ import { DashboardProvider } from './context/DashboardContext';
23
23
  import { ProfileViewContextProvider } from './context/ProfileViewContext';
24
24
  import { useProfileMetadata } from './hooks/useProfileMetadata';
25
25
  import { useVisualizationState } from './hooks/useVisualizationState';
26
- export const ProfileView = ({ total, filtered, flamegraphData, flamechartData, topTableData, sourceData, profileSource, queryClient, onDownloadPProf, pprofDownloading, compare, showVisualizationSelector, }) => {
26
+ export const ProfileView = ({ total, filtered, flamegraphData, flamechartData, topTableData, sourceData, profileSource, queryClient, onDownloadPProf, pprofDownloading, compare, showVisualizationSelector, sandwichData, }) => {
27
27
  const { timezone, perf, profileViewExternalMainActions, preferencesModal, profileViewExternalSubActions, } = useParcaContext();
28
28
  const { ref, dimensions } = useContainerDimensions();
29
29
  const { curPath, setCurPath, curPathArrow, setCurPathArrow, colorStackLegend, colorBy, groupBy, toggleGroupBy, setGroupByLabels, sandwichFunctionName, resetSandwichFunctionName, } = useVisualizationState();
@@ -38,6 +38,7 @@ export const ProfileView = ({ total, filtered, flamegraphData, flamechartData, t
38
38
  (selectQueryParam('compare_a') === 'true' && selectQueryParam('compare_b') === 'true');
39
39
  const getDashboardItemByType = ({ type, isHalfScreen, }) => {
40
40
  return getDashboardItem({
41
+ sandwichData,
41
42
  type,
42
43
  isHalfScreen,
43
44
  dimensions,
@@ -1,8 +1,7 @@
1
- import { Callgraph as CallgraphType, Flamegraph, FlamegraphArrow, QueryServiceClient, Source, TableArrow } from '@parca/client';
1
+ import { FlamegraphArrow, QueryServiceClient, Source, TableArrow } from '@parca/client';
2
2
  import { ProfileSource } from '../../ProfileSource';
3
3
  export interface FlamegraphData {
4
4
  loading: boolean;
5
- data?: Flamegraph;
6
5
  arrow?: FlamegraphArrow;
7
6
  total?: bigint;
8
7
  filtered?: bigint;
@@ -19,26 +18,23 @@ export interface TopTableData {
19
18
  error?: any;
20
19
  unit?: string;
21
20
  }
22
- export interface CallgraphData {
23
- loading: boolean;
24
- data?: CallgraphType;
25
- total?: bigint;
26
- filtered?: bigint;
27
- error?: any;
28
- }
29
21
  export interface SourceData {
30
22
  loading: boolean;
31
23
  data?: Source;
32
24
  error?: any;
33
25
  }
26
+ export interface SandwichData {
27
+ callees: FlamegraphData;
28
+ callers: FlamegraphData;
29
+ }
34
30
  export type VisualizationType = 'flamegraph' | 'callgraph' | 'table' | 'source' | 'flamechart' | 'sandwich';
35
31
  export interface ProfileViewProps {
36
32
  total: bigint;
37
33
  filtered: bigint;
38
34
  flamegraphData: FlamegraphData;
39
35
  flamechartData: FlamegraphData;
36
+ sandwichData: SandwichData;
40
37
  topTableData?: TopTableData;
41
- callgraphData?: CallgraphData;
42
38
  sourceData?: SourceData;
43
39
  profileSource: ProfileSource;
44
40
  queryClient?: QueryServiceClient;
@@ -1 +1 @@
1
- {"version":3,"file":"visualization.d.ts","sourceRoot":"","sources":["../../../src/ProfileView/types/visualization.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,SAAS,IAAI,aAAa,EAC1B,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,MAAM,EACN,UAAU,EACX,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,GAAG,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,GAAG,CAAC;CACb;AAED,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,WAAW,GACX,OAAO,GACP,QAAQ,GACR,YAAY,GACZ,UAAU,CAAC;AAEf,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC"}
1
+ {"version":3,"file":"visualization.d.ts","sourceRoot":"","sources":["../../../src/ProfileView/types/visualization.ts"],"names":[],"mappings":"AAaA,OAAO,EAAC,eAAe,EAAE,kBAAkB,EAAE,MAAM,EAAE,UAAU,EAAC,MAAM,eAAe,CAAC;AAEtF,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,GAAG,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,cAAc,CAAC;IACxB,OAAO,EAAE,cAAc,CAAC;CACzB;AAED,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,WAAW,GACX,OAAO,GACP,QAAQ,GACR,YAAY,GACZ,UAAU,CAAC;AAEf,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProfileViewWithData.d.ts","sourceRoot":"","sources":["../src/ProfileViewWithData.tsx"],"names":[],"mappings":"AAeA,OAAO,EAA0B,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAM1E,OAAO,EAAsB,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAMnE,UAAU,wBAAwB;IAChC,WAAW,EAAE,kBAAkB,CAAC;IAChC,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,eAAO,MAAM,mBAAmB,GAAI,4DAIjC,wBAAwB,KAAG,GAAG,CAAC,OA0QjC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"ProfileViewWithData.d.ts","sourceRoot":"","sources":["../src/ProfileViewWithData.tsx"],"names":[],"mappings":"AAeA,OAAO,EAA0B,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAM1E,OAAO,EAAsB,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAMnE,UAAU,wBAAwB;IAChC,WAAW,EAAE,kBAAkB,CAAC;IAChC,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,eAAO,MAAM,mBAAmB,GAAI,4DAIjC,wBAAwB,KAAG,GAAG,CAAC,OA6SjC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}