@parca/profile 0.16.120 → 0.16.121

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,10 @@
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.16.121 (2023-02-21)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
6
10
  ## 0.16.120 (2023-02-21)
7
11
 
8
12
  **Note:** Version bump only for package @parca/profile
@@ -0,0 +1,6 @@
1
+ interface Props {
2
+ value: string | number | undefined;
3
+ displayValue?: string | number | undefined;
4
+ }
5
+ export declare const ExpandOnHover: ({ value, displayValue }: Props) => JSX.Element;
6
+ export {};
@@ -0,0 +1,16 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ export var ExpandOnHover = function (_a) {
14
+ var value = _a.value, displayValue = _a.displayValue;
15
+ return (_jsxs("div", __assign({ className: "relative group w-full" }, { children: [_jsx("div", __assign({ className: "text-ellipsis w-full overflow-hidden whitespace-nowrap" }, { children: displayValue !== null && displayValue !== void 0 ? displayValue : value })), _jsx("div", __assign({ className: "group-hover:flex hidden absolute -inset-2 max-w-[500px] whitespace-normal h-fit bg-gray-50 dark:bg-gray-900 shadow-[0_0_10px_2px_rgba(0,0,0,0.3)] rounded p-2 break-all" }, { children: value }))] })));
16
+ };
@@ -1,8 +1,8 @@
1
1
  import { CallgraphNode, FlamegraphNode, FlamegraphNodeMeta, FlamegraphRootNode } from '@parca/client';
2
2
  import { Location, Mapping, Function as ParcaFunction } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
3
3
  interface GraphTooltipProps {
4
- x: number;
5
- y: number;
4
+ x?: number;
5
+ y?: number;
6
6
  unit: string;
7
7
  total: number;
8
8
  hoveringNode: HoveringNode;
@@ -20,7 +20,7 @@ var __rest = (this && this.__rest) || function (s, e) {
20
20
  }
21
21
  return t;
22
22
  };
23
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
23
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
24
24
  // Copyright 2022 The Parca Authors
25
25
  // Licensed under the Apache License, Version 2.0 (the "License");
26
26
  // you may not use this file except in compliance with the License.
@@ -34,11 +34,16 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
34
34
  // See the License for the specific language governing permissions and
35
35
  // limitations under the License.
36
36
  import { useEffect, useState } from 'react';
37
+ import { pointer } from 'd3-selection';
37
38
  import { CopyToClipboard } from 'react-copy-to-clipboard';
38
39
  import { usePopper } from 'react-popper';
39
40
  import { useKeyDown } from '@parca/components';
40
41
  import { getLastItem, valueFormatter } from '@parca/functions';
41
- import { hexifyAddress, truncateString } from '../';
42
+ import { hexifyAddress, truncateString, truncateStringReverse } from '../';
43
+ import { ExpandOnHover } from './ExpandOnHoverValue';
44
+ var NoData = function () {
45
+ return _jsx("span", __assign({ className: "rounded bg-gray-200 dark:bg-gray-800 px-2" }, { children: "Not available" }));
46
+ };
42
47
  var virtualElement = {
43
48
  getBoundingClientRect: function () {
44
49
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -69,13 +74,11 @@ function generateGetBoundingClientRect(contextElement, x, y) {
69
74
  };
70
75
  }
71
76
  var TooltipMetaInfo = function (_a) {
72
- var _b, _c, _d, _e;
77
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
73
78
  var hoveringNode = _a.hoveringNode, onCopy = _a.onCopy, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions;
74
- if (hoveringNode.meta === undefined)
75
- return _jsx(_Fragment, {});
76
79
  // populate meta from the flamegraph metadata tables
77
80
  if (locations !== undefined &&
78
- hoveringNode.meta.locationIndex !== undefined &&
81
+ ((_b = hoveringNode.meta) === null || _b === void 0 ? void 0 : _b.locationIndex) !== undefined &&
79
82
  hoveringNode.meta.locationIndex !== 0) {
80
83
  var location_1 = locations[hoveringNode.meta.locationIndex - 1];
81
84
  hoveringNode.meta.location = location_1;
@@ -117,9 +120,10 @@ var TooltipMetaInfo = function (_a) {
117
120
  ? " +".concat(hoveringNode.meta.function.startLine)
118
121
  : ''));
119
122
  };
120
- return (_jsxs(_Fragment, { children: [((_b = hoveringNode.meta.function) === null || _b === void 0 ? void 0 : _b.filename) !== undefined &&
121
- ((_c = hoveringNode.meta.function) === null || _c === void 0 ? void 0 : _c.filename) !== '' && (_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/5" }, { children: "File" })), _jsx("td", __assign({ className: "w-4/5 break-all" }, { children: _jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: getTextForFile(hoveringNode) }, { children: _jsx("button", __assign({ className: "cursor-pointer text-left" }, { children: getTextForFile(hoveringNode) })) })) }))] })), ((_d = hoveringNode.meta.location) === null || _d === void 0 ? void 0 : _d.address) !== undefined &&
122
- ((_e = hoveringNode.meta.location) === null || _e === void 0 ? void 0 : _e.address) !== '0' && (_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/5" }, { children: "Address" })), _jsx("td", __assign({ className: "w-4/5 break-all" }, { children: _jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hexifyAddress(hoveringNode.meta.location.address) }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: hexifyAddress(hoveringNode.meta.location.address) })) })) }))] })), hoveringNode.meta.mapping !== undefined && hoveringNode.meta.mapping.file !== '' && (_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/5" }, { children: "Binary" })), _jsx("td", __assign({ className: "w-4/5 break-all" }, { children: _jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hoveringNode.meta.mapping.file }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: getLastItem(hoveringNode.meta.mapping.file) })) })) }))] })), hoveringNode.meta.mapping !== undefined && hoveringNode.meta.mapping.buildId !== '' && (_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/5" }, { children: "Build Id" })), _jsx("td", __assign({ className: "w-4/5 break-all" }, { children: _jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hoveringNode.meta.mapping.buildId }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: truncateString(getLastItem(hoveringNode.meta.mapping.buildId), 16) })) })) }))] }))] }));
123
+ var file = getTextForFile(hoveringNode);
124
+ return (_jsxs(_Fragment, { children: [_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "File" })), _jsx("td", __assign({ className: "w-3/4 break-all" }, { children: ((_d = (_c = hoveringNode.meta) === null || _c === void 0 ? void 0 : _c.function) === null || _d === void 0 ? void 0 : _d.filename) == null ||
125
+ ((_e = hoveringNode.meta) === null || _e === void 0 ? void 0 : _e.function.filename) === '' ? (_jsx(NoData, {})) : (_jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: file }, { children: _jsx("button", __assign({ className: "cursor-pointer text-left whitespace-nowrap" }, { children: _jsx(ExpandOnHover, { value: file, displayValue: truncateStringReverse(file, 40) }) })) }))) }))] }), _jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "Address" })), _jsx("td", __assign({ className: "w-3/4 break-all" }, { children: ((_g = (_f = hoveringNode.meta) === null || _f === void 0 ? void 0 : _f.location) === null || _g === void 0 ? void 0 : _g.address) == null ||
126
+ ((_h = hoveringNode.meta) === null || _h === void 0 ? void 0 : _h.location.address) === '0' ? (_jsx(NoData, {})) : (_jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hexifyAddress(hoveringNode.meta.location.address) }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: hexifyAddress(hoveringNode.meta.location.address) })) }))) }))] }), _jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "Binary" })), _jsx("td", __assign({ className: "w-3/4 break-all" }, { children: ((_j = hoveringNode.meta) === null || _j === void 0 ? void 0 : _j.mapping) == null || hoveringNode.meta.mapping.file === '' ? (_jsx(NoData, {})) : (_jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hoveringNode.meta.mapping.file }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: getLastItem(hoveringNode.meta.mapping.file) })) }))) }))] }), _jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "Build Id" })), _jsx("td", __assign({ className: "w-3/4 break-all" }, { children: ((_k = hoveringNode.meta) === null || _k === void 0 ? void 0 : _k.mapping) == null || ((_l = hoveringNode.meta) === null || _l === void 0 ? void 0 : _l.mapping.buildId) === '' ? (_jsx(NoData, {})) : (_jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hoveringNode.meta.mapping.buildId }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: truncateString(getLastItem(hoveringNode.meta.mapping.buildId), 28) })) }))) }))] })] }));
123
127
  };
124
128
  var timeoutHandle = null;
125
129
  var GraphTooltipContent = function (_a) {
@@ -140,21 +144,20 @@ var GraphTooltipContent = function (_a) {
140
144
  var diffValueText = diffSign + valueFormatter(diff, unit, 1);
141
145
  var diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
142
146
  var diffText = "".concat(diffValueText, " (").concat(diffPercentageText, ")");
143
- var metaRows = hoveringNode.meta === undefined ? (_jsx(_Fragment, {})) : (_jsx(TooltipMetaInfo, { onCopy: onCopy,
144
- // @ts-expect-error
145
- hoveringNode: hoveringNode, strings: strings, mappings: mappings, locations: locations, functions: functions }));
146
147
  var getTextForCumulative = function (hoveringNodeCumulative) {
147
148
  return "".concat(valueFormatter(hoveringNodeCumulative, unit, 2), " (\n ").concat(((hoveringNodeCumulative * 100) / total).toFixed(2), "%)");
148
149
  };
149
- return (_jsx("div", __assign({ className: "flex ".concat(isFixed ? 'w-full h-36' : '') }, { children: _jsx("div", __assign({ className: "m-auto w-full ".concat(isFixed ? 'w-full h-36' : '') }, { children: _jsx("div", __assign({ className: "border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-900 rounded-lg p-3 shadow-lg opacity-90", style: { borderWidth: 1 } }, { children: _jsx("div", __assign({ className: "flex flex-row" }, { children: _jsxs("div", __assign({ className: "ml-2 mr-6" }, { children: [_jsx("span", __assign({ className: "font-semibold break-all" }, { children: hoveringNode.meta === undefined ? (_jsx("p", { children: "root" })) : (_jsx(_Fragment, { children: hoveringNode.meta.function !== undefined &&
150
- hoveringNode.meta.function.name !== '' ? (_jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hoveringNode.meta.function.name }, { children: _jsx("button", __assign({ className: "cursor-pointer text-left" }, { children: hoveringNode.meta.function.name })) }))) : (_jsx(_Fragment, { children: hoveringNode.meta.location !== undefined &&
151
- parseInt(hoveringNode.meta.location.address, 10) !== 0 ? (_jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hexifyAddress(hoveringNode.meta.location.address) }, { children: _jsx("button", __assign({ className: "cursor-pointer text-left" }, { children: hexifyAddress(hoveringNode.meta.location.address) })) }))) : (_jsx("p", { children: "unknown" })) })) })) })), _jsx("span", __assign({ className: "text-gray-700 dark:text-gray-300 my-2" }, { children: _jsx("table", __assign({ className: "table-fixed" }, { children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/5" }, { children: "Cumulative" })), _jsx("td", __assign({ className: "w-4/5" }, { children: _jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: getTextForCumulative(hoveringNodeCumulative) }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: getTextForCumulative(hoveringNodeCumulative) })) })) }))] }), hoveringNode.diff !== undefined && diff !== 0 && (_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/5" }, { children: "Diff" })), _jsx("td", __assign({ className: "w-4/5" }, { children: _jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: diffText }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: diffText })) })) }))] })), metaRows] }) })) })), _jsx("span", __assign({ className: "block text-gray-500 text-xs mt-2" }, { children: isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.' }))] })) })) })) })) })));
150
+ return (_jsx("div", __assign({ className: "text-sm flex ".concat(isFixed ? 'w-full' : '') }, { children: _jsx("div", __assign({ className: "m-auto w-full ".concat(isFixed ? 'w-full' : '') }, { children: _jsxs("div", __assign({ className: "border border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-900 rounded-lg p-3 shadow-lg min-h-52 w-[500px] flex justify-between flex-col" }, { children: [_jsx("div", __assign({ className: "flex flex-row" }, { children: _jsxs("div", __assign({ className: "mx-2" }, { children: [_jsx("div", __assign({ className: "font-semibold break-all h-10 flex items-center" }, { children: hoveringNode.meta === undefined ? (_jsx("p", { children: "root" })) : (_jsx(_Fragment, { children: hoveringNode.meta.function !== undefined &&
151
+ hoveringNode.meta.function.name !== '' ? (_jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hoveringNode.meta.function.name }, { children: _jsx("button", __assign({ className: "cursor-pointer text-left" }, { children: hoveringNode.meta.function.name })) }))) : (_jsx(_Fragment, { children: hoveringNode.meta.location !== undefined &&
152
+ parseInt(hoveringNode.meta.location.address, 10) !== 0 ? (_jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hexifyAddress(hoveringNode.meta.location.address) }, { children: _jsx("button", __assign({ className: "cursor-pointer text-left" }, { children: hexifyAddress(hoveringNode.meta.location.address) })) }))) : (_jsx("p", { children: "unknown" })) })) })) })), _jsx("table", __assign({ className: "table-fixed pr-0 text-gray-700 dark:text-gray-300 my-2 w-full" }, { children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "Cumulative" })), _jsx("td", __assign({ className: "w-3/4" }, { children: _jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: getTextForCumulative(hoveringNodeCumulative) }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: getTextForCumulative(hoveringNodeCumulative) })) })) }))] }), hoveringNode.diff !== undefined && diff !== 0 && (_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "Diff" })), _jsx("td", __assign({ className: "w-3/4" }, { children: _jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: diffText }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: diffText })) })) }))] })), _jsx(TooltipMetaInfo, { onCopy: onCopy,
153
+ // @ts-expect-error
154
+ hoveringNode: hoveringNode, strings: strings, mappings: mappings, locations: locations, functions: functions })] }) }))] })) })), _jsx("span", __assign({ className: "block text-gray-500 text-xs mx-2" }, { children: isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.' }))] })) })) })));
152
155
  };
153
156
  var GraphTooltip = function (_a) {
154
157
  var x = _a.x, y = _a.y, unit = _a.unit, total = _a.total, hoveringNode = _a.hoveringNode, contextElement = _a.contextElement, _b = _a.isFixed, isFixed = _b === void 0 ? false : _b, _c = _a.virtualContextElement, virtualContextElement = _c === void 0 ? true : _c, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions;
155
158
  var _d = useState(null), popperElement = _d[0], setPopperElement = _d[1];
156
159
  var _e = usePopper(virtualContextElement ? virtualElement : contextElement, popperElement, {
157
- placement: 'auto-start',
160
+ placement: 'bottom-start',
158
161
  strategy: 'absolute',
159
162
  modifiers: [
160
163
  {
@@ -172,16 +175,30 @@ var GraphTooltip = function (_a) {
172
175
  },
173
176
  ],
174
177
  }), styles = _e.styles, attributes = _e.attributes, popperProps = __rest(_e, ["styles", "attributes"]);
175
- var update = popperProps.update;
176
178
  var isShiftDown = useKeyDown().isShiftDown;
177
179
  useEffect(function () {
178
- if (contextElement != null) {
179
- if (isShiftDown)
180
+ if (contextElement === null)
181
+ return;
182
+ var onMouseMove = function (e) {
183
+ var _a;
184
+ if (isShiftDown) {
180
185
  return;
181
- virtualElement.getBoundingClientRect = generateGetBoundingClientRect(contextElement, x, y);
182
- void (update === null || update === void 0 ? void 0 : update());
183
- }
184
- }, [x, y, contextElement, update, isShiftDown]);
186
+ }
187
+ var tooltipX = x;
188
+ var tooltipY = y;
189
+ if (tooltipX == null || tooltipY == null) {
190
+ var rel = pointer(e);
191
+ tooltipX = rel[0];
192
+ tooltipY = rel[1];
193
+ }
194
+ virtualElement.getBoundingClientRect = generateGetBoundingClientRect(contextElement, tooltipX, tooltipY);
195
+ void ((_a = popperProps.update) === null || _a === void 0 ? void 0 : _a.call(popperProps));
196
+ };
197
+ contextElement.addEventListener('mousemove', onMouseMove);
198
+ return function () {
199
+ contextElement.removeEventListener('mousemove', onMouseMove);
200
+ };
201
+ }, [contextElement, popperProps, isShiftDown, x, y]);
185
202
  if (hoveringNode === undefined || hoveringNode == null)
186
203
  return _jsx(_Fragment, {});
187
204
  return isFixed ? (_jsx(GraphTooltipContent, { hoveringNode: hoveringNode, unit: unit, total: total, isFixed: isFixed })) : (_jsx("div", __assign({ ref: setPopperElement, style: styles.popper }, attributes.popper, { children: _jsx(GraphTooltipContent, { hoveringNode: hoveringNode, unit: unit, total: total, isFixed: isFixed, strings: strings, mappings: mappings, locations: locations, functions: functions }) })));
@@ -99,9 +99,9 @@ export var IcicleNode = React.memo(function IcicleNode(_a) {
99
99
  };
100
100
  return (_jsxs(_Fragment, { children: [_jsxs("g", __assign({ transform: "translate(".concat(x + 1, ", ").concat(y + 1, ")"), style: styles, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onClick: function () {
101
101
  setCurPath(nextPath);
102
- } }, { children: [_jsx("rect", { x: 0, y: 0, width: width - 1, height: height - 1, style: {
102
+ } }, { children: [_jsx("rect", { x: 0, y: 0, width: width, height: height, style: {
103
103
  fill: colorResult,
104
- }, className: cx({
104
+ }, className: cx('stroke-white dark:stroke-gray-700', {
105
105
  'opacity-50': isHighlightEnabled && !isHighlighted,
106
106
  }) }), width > 5 && (_jsx("svg", __assign({ width: width - 5, height: height }, { children: _jsx("text", __assign({ x: 5, y: 15, style: { fontSize: '12px' } }, { children: name })) })))] })), data.children !== undefined && data.children.length > 0 && (_jsx(IcicleGraphNodes, { data: data.children, strings: strings, mappings: mappings, locations: locations, functions: functions, x: x, y: RowHeight, xScale: newXScale, total: total, totalWidth: totalWidth, level: nextLevel, setHoveringNode: setHoveringNode, path: nextPath, curPath: nextCurPath, setCurPath: setCurPath, searchString: searchString, compareMode: compareMode }))] }));
107
107
  });
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ /// <reference types="react" />
2
2
  import { Flamegraph } from '@parca/client';
3
3
  import { type NavigateFunction } from '@parca/functions';
4
4
  interface IcicleGraphProps {
@@ -10,5 +10,5 @@ interface IcicleGraphProps {
10
10
  navigateTo?: NavigateFunction;
11
11
  isTrimmed?: boolean;
12
12
  }
13
- export declare const IcicleGraph: React.NamedExoticComponent<IcicleGraphProps>;
13
+ export declare const IcicleGraph: import("react").NamedExoticComponent<IcicleGraphProps>;
14
14
  export default IcicleGraph;
@@ -25,8 +25,6 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
25
25
  import { memo, useEffect, useMemo, useRef, useState } from 'react';
26
26
  import cx from 'classnames';
27
27
  import { scaleLinear } from 'd3-scale';
28
- import { pointer } from 'd3-selection';
29
- import { throttle } from 'lodash';
30
28
  import { Button, useURLState } from '@parca/components';
31
29
  import { selectQueryParam } from '@parca/functions';
32
30
  import useUserPreference, { USER_PREFERENCES } from '@parca/functions/useUserPreference';
@@ -38,8 +36,7 @@ export var IcicleGraph = memo(function IcicleGraph(_a) {
38
36
  var _b;
39
37
  var graph = _a.graph, width = _a.width, setCurPath = _a.setCurPath, curPath = _a.curPath, sampleUnit = _a.sampleUnit, navigateTo = _a.navigateTo, _c = _a.isTrimmed, isTrimmed = _c === void 0 ? false : _c;
40
38
  var _d = useState(), hoveringNode = _d[0], setHoveringNode = _d[1];
41
- var _e = useState([0, 0]), pos = _e[0], setPos = _e[1];
42
- var _f = useState(0), height = _f[0], setHeight = _f[1];
39
+ var _e = useState(0), height = _e[0], setHeight = _e[1];
43
40
  var svg = useRef(null);
44
41
  var ref = useRef(null);
45
42
  var rawDashboardItems = useURLState({
@@ -65,14 +62,8 @@ export var IcicleGraph = memo(function IcicleGraph(_a) {
65
62
  if (coloredGraph.root === undefined || width === undefined) {
66
63
  return _jsx(_Fragment, {});
67
64
  }
68
- var throttledSetPos = throttle(setPos, 20);
69
- var onMouseMove = function (e) {
70
- // X/Y coordinate array relative to svg
71
- var rel = pointer(e);
72
- throttledSetPos([rel[0], rel[1]]);
73
- };
74
65
  var isColorStackLegendVisible = colorProfileName !== 'default';
75
- return (_jsxs("div", __assign({ onMouseLeave: function () { return setHoveringNode(undefined); } }, { children: [_jsx(ColorStackLegend, { navigateTo: navigateTo, compareMode: compareMode }), _jsx(GraphTooltip, { unit: sampleUnit, total: total, x: pos[0], y: pos[1], hoveringNode: hoveringNode, contextElement: svg.current, strings: coloredGraph.stringTable, mappings: coloredGraph.mapping, locations: coloredGraph.locations, functions: coloredGraph.function }), _jsx("div", __assign({ className: cx('flex justify-start absolute', {
66
+ return (_jsxs("div", __assign({ onMouseLeave: function () { return setHoveringNode(undefined); } }, { children: [_jsx(ColorStackLegend, { navigateTo: navigateTo, compareMode: compareMode }), _jsx(GraphTooltip, { unit: sampleUnit, total: total, hoveringNode: hoveringNode, contextElement: svg.current, strings: coloredGraph.stringTable, mappings: coloredGraph.mapping, locations: coloredGraph.locations, functions: coloredGraph.function }), _jsx("div", __assign({ className: cx('flex justify-start absolute', {
76
67
  'top-[-48px]': dashboardItems.length <= 1 && !isTrimmed && !isColorStackLegendVisible,
77
68
  'top-[-69px]': dashboardItems.length <= 1 && !isTrimmed && isColorStackLegendVisible,
78
69
  'top-[-54px]': dashboardItems.length <= 1 && isTrimmed && isColorStackLegendVisible,
@@ -81,6 +72,6 @@ export var IcicleGraph = memo(function IcicleGraph(_a) {
81
72
  'top-[-54px] left-[25px] ': dashboardItems.length > 1 && isTrimmed && !isColorStackLegendVisible,
82
73
  'top-[-70px] left-[25px]': dashboardItems.length > 1 && !isTrimmed && isColorStackLegendVisible,
83
74
  'top-[-46px] left-[25px]': dashboardItems.length > 1 && !isTrimmed && !isColorStackLegendVisible,
84
- }) }, { children: _jsx(Button, __assign({ color: "neutral", onClick: function () { return setCurPath([]); }, disabled: curPath.length === 0, className: "w-auto", variant: "neutral" }, { children: "Reset View" })) })), _jsx("svg", __assign({ className: "font-robotoMono", width: width, height: height, onMouseMove: onMouseMove, preserveAspectRatio: "xMinYMid", ref: svg }, { children: _jsx("g", __assign({ ref: ref }, { children: _jsx("g", __assign({ transform: 'translate(0, 0)' }, { children: _jsx(IcicleNode, { x: 0, y: 0, totalWidth: width, height: RowHeight, setCurPath: setCurPath, setHoveringNode: setHoveringNode, curPath: curPath, data: coloredGraph.root, strings: coloredGraph.stringTable, mappings: coloredGraph.mapping, locations: coloredGraph.locations, functions: coloredGraph.function, total: total, xScale: xScale, path: [], level: 0, isRoot: true, searchString: currentSearchString, compareMode: compareMode }) })) })) }))] })));
75
+ }) }, { children: _jsx(Button, __assign({ color: "neutral", onClick: function () { return setCurPath([]); }, disabled: curPath.length === 0, className: "w-auto", variant: "neutral" }, { children: "Reset View" })) })), _jsx("svg", __assign({ className: "font-robotoMono", width: width, height: height, preserveAspectRatio: "xMinYMid", ref: svg }, { children: _jsx("g", __assign({ ref: ref }, { children: _jsx("g", __assign({ transform: 'translate(0, 0)' }, { children: _jsx(IcicleNode, { x: 0, y: 0, totalWidth: width, height: RowHeight, setCurPath: setCurPath, setHoveringNode: setHoveringNode, curPath: curPath, data: coloredGraph.root, strings: coloredGraph.stringTable, mappings: coloredGraph.mapping, locations: coloredGraph.locations, functions: coloredGraph.function, total: total, xScale: xScale, path: [], level: 0, isRoot: true, searchString: currentSearchString, compareMode: compareMode }) })) })) }))] })));
85
76
  });
86
77
  export default IcicleGraph;
@@ -37,7 +37,7 @@ import { Icon } from '@iconify/react';
37
37
  import cx from 'classnames';
38
38
  import { scaleLinear } from 'd3';
39
39
  import { DragDropContext, Draggable, Droppable, } from 'react-beautiful-dnd';
40
- import { Button, Card, KeyDownProvider, useParcaContext, useURLState } from '@parca/components';
40
+ import { Button, Card, ConditionalWrapper, KeyDownProvider, useParcaContext, useURLState, } from '@parca/components';
41
41
  import { useContainerDimensions } from '@parca/dynamicsize';
42
42
  import { getNewSpanColor } from '@parca/functions';
43
43
  import { CloseIcon } from '@parca/icons';
@@ -100,7 +100,10 @@ export var ProfileView = function (_a) {
100
100
  var type = _a.type, isHalfScreen = _a.isHalfScreen;
101
101
  switch (type) {
102
102
  case 'icicle': {
103
- return (flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.data) != null ? (_jsx(Profiler, __assign({ id: "icicleGraph", onRender: perf === null || perf === void 0 ? void 0 : perf.onRender }, { children: _jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, graph: flamegraphData.data, sampleUnit: sampleUnit, onContainerResize: onFlamegraphContainerResize, navigateTo: navigateTo, loading: flamegraphData.loading }) }))) : (_jsx(_Fragment, { children: " " }));
103
+ return (flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.data) != null ? (_jsx(ConditionalWrapper, __assign({ condition: (perf === null || perf === void 0 ? void 0 : perf.onRender) != null, wrapper: function (_a) {
104
+ var children = _a.children;
105
+ return (_jsx(Profiler, __assign({ id: "icicleGraph", onRender: perf === null || perf === void 0 ? void 0 : perf.onRender }, { children: children })));
106
+ } }, { children: _jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, graph: flamegraphData.data, sampleUnit: sampleUnit, onContainerResize: onFlamegraphContainerResize, navigateTo: navigateTo, loading: flamegraphData.loading }) }))) : (_jsx(_Fragment, { children: " " }));
104
107
  }
105
108
  case 'callgraph': {
106
109
  return (callgraphData === null || callgraphData === void 0 ? void 0 : callgraphData.data) != null && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) !== undefined ? (_jsx(Callgraph, { graph: callgraphData.data, sampleUnit: sampleUnit, width: isHalfScreen ? (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) / 2 : dimensions === null || dimensions === void 0 ? void 0 : dimensions.width, colorRange: colorRange })) : (_jsx(_Fragment, {}));
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- /*! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.visible{visibility:visible}.invisible{visibility:hidden}.absolute{position:absolute}.relative{position:relative}.left-\[25px\]{left:25px}.left-0{left:0}.top-\[-46px\]{top:-46px}.right-0{right:0}.top-\[-45px\]{top:-45px}.top-\[-48px\]{top:-48px}.top-\[-69px\]{top:-69px}.top-\[-54px\]{top:-54px}.top-\[-70px\]{top:-70px}.z-50{z-index:50}.z-10{z-index:10}.m-auto{margin:auto}.m-0{margin:0}.m-2{margin:.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-20{margin-bottom:5rem;margin-top:5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-6{margin-bottom:1.5rem;margin-top:1.5rem}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.mt-2{margin-top:.5rem}.mt-1{margin-top:.25rem}.ml-auto{margin-left:auto}.mb-2{margin-bottom:.5rem}.mr-3{margin-right:.75rem}.mr-1{margin-right:.25rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.table{display:table}.grid{display:grid}.h-36{height:9rem}.h-1{height:.25rem}.h-\[80vh\]{height:80vh}.h-4{height:1rem}.max-h-\[400px\]{max-height:400px}.w-auto{width:auto}.w-1\/5{width:20%}.w-4\/5{width:80%}.w-full{width:100%}.w-40{width:10rem}.w-2\/5{width:40%}.w-1\/2{width:50%}.w-8{width:2rem}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-4{width:1rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.min-w-\[300px\]{min-width:300px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-0,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-1{gap:.25rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-r-0{border-right-width:0}.border-l-0{border-left-width:0}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-inherit{background-color:inherit}.fill-transparent{fill:transparent}.fill-current{fill:currentColor}.p-3{padding:.75rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-1{padding:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-20{padding-bottom:5rem;padding-top:5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pt-2{padding-top:.5rem}.pb-4{padding-bottom:1rem}.pr-2{padding-right:.5rem}.pl-2{padding-left:.5rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-xs{font-size:.75rem;line-height:1rem}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-semibold{font-weight:600}.font-bold{font-weight:700}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-90{opacity:.9}.opacity-100{opacity:1}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-150{transition-duration:.15s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-indigo-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(55 48 163/var(--tw-ring-opacity))}[class~=theme-dark] .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}[class~=theme-dark] .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}[class~=theme-dark] .dark\:\!text-indigo-400{--tw-text-opacity:1!important;color:rgb(129 140 248/var(--tw-text-opacity))!important}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}
1
+ /*! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.visible{visibility:visible}.invisible{visibility:hidden}.absolute{position:absolute}.relative{position:relative}.-inset-2{bottom:-.5rem;left:-.5rem;right:-.5rem;top:-.5rem}.left-\[25px\]{left:25px}.left-0{left:0}.top-\[-46px\]{top:-46px}.right-0{right:0}.top-\[-45px\]{top:-45px}.top-\[-48px\]{top:-48px}.top-\[-69px\]{top:-69px}.top-\[-54px\]{top:-54px}.top-\[-70px\]{top:-70px}.z-50{z-index:50}.z-10{z-index:10}.m-auto{margin:auto}.m-0{margin:0}.m-2{margin:.5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-20{margin-bottom:5rem;margin-top:5rem}.my-6{margin-bottom:1.5rem;margin-top:1.5rem}.mt-1{margin-top:.25rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mb-2{margin-bottom:.5rem}.mr-6{margin-right:1.5rem}.mr-3{margin-right:.75rem}.mr-1{margin-right:.25rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-fit{height:-moz-fit-content;height:fit-content}.h-10{height:2.5rem}.h-1{height:.25rem}.h-\[80vh\]{height:80vh}.h-4{height:1rem}.max-h-\[400px\]{max-height:400px}.min-h-52{min-height:13rem}.w-auto{width:auto}.w-full{width:100%}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-\[500px\]{width:500px}.w-40{width:10rem}.w-2\/5{width:40%}.w-1\/2{width:50%}.w-8{width:2rem}.w-4{width:1rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.min-w-\[300px\]{min-width:300px}.max-w-\[500px\]{max-width:500px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-0,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-1{gap:.25rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.text-ellipsis{text-overflow:ellipsis}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-r-0{border-right-width:0}.border-l-0{border-left-width:0}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-inherit{background-color:inherit}.fill-transparent{fill:transparent}.fill-current{fill:currentColor}.stroke-white{stroke:#fff}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-1{padding:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-20{padding-bottom:5rem;padding-top:5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.pr-0{padding-right:0}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pt-2{padding-top:.5rem}.pb-4{padding-bottom:1rem}.pr-2{padding-right:.5rem}.pl-2{padding-left:.5rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.text-base{font-size:1rem;line-height:1.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-semibold{font-weight:600}.font-bold{font-weight:700}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-100{opacity:1}.opacity-0{opacity:0}.opacity-90{opacity:.9}.opacity-50{opacity:.5}.shadow-\[0_0_10px_2px_rgba\(0\2c 0\2c 0\2c 0\.3\)\]{--tw-shadow:0 0 10px 2px rgba(0,0,0,.3);--tw-shadow-colored:0 0 10px 2px var(--tw-shadow-color)}.shadow-\[0_0_10px_2px_rgba\(0\2c 0\2c 0\2c 0\.3\)\],.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-150{transition-duration:.15s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-indigo-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(55 48 163/var(--tw-ring-opacity))}.group:hover .group-hover\:flex{display:flex}[class~=theme-dark] .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}[class~=theme-dark] .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:stroke-gray-700{stroke:#374151}[class~=theme-dark] .dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}[class~=theme-dark] .dark\:\!text-indigo-400{--tw-text-opacity:1!important;color:rgb(129 140 248/var(--tw-text-opacity))!important}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}
package/dist/utils.d.ts CHANGED
@@ -3,3 +3,4 @@ import { QueryRequest, QueryServiceClient } from '@parca/client';
3
3
  export declare const hexifyAddress: (address?: string) => string;
4
4
  export declare const downloadPprof: (request: QueryRequest, queryClient: QueryServiceClient, metadata: RpcMetadata) => Promise<Blob>;
5
5
  export declare const truncateString: (str: string, num: number) => string;
6
+ export declare const truncateStringReverse: (str: string, num: number) => string;
package/dist/utils.js CHANGED
@@ -87,3 +87,9 @@ export var truncateString = function (str, num) {
87
87
  }
88
88
  return str.slice(0, num) + '...';
89
89
  };
90
+ export var truncateStringReverse = function (str, num) {
91
+ if (str.length <= num) {
92
+ return str;
93
+ }
94
+ return '...' + str.slice(str.length - num);
95
+ };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.120",
3
+ "version": "0.16.121",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@parca/client": "^0.16.63",
7
- "@parca/components": "^0.16.100",
7
+ "@parca/components": "^0.16.101",
8
8
  "@parca/dynamicsize": "^0.16.53",
9
9
  "@parca/functions": "^0.16.63",
10
10
  "@parca/parser": "^0.16.54",
@@ -45,5 +45,5 @@
45
45
  "access": "public",
46
46
  "registry": "https://registry.npmjs.org/"
47
47
  },
48
- "gitHead": "d8a18c18470136863644ae2f93b3c146ea8061ea"
48
+ "gitHead": "7a07bf57f7092b0ee9c1b742c495229ce1231ee7"
49
49
  }
@@ -0,0 +1,30 @@
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
+
14
+ interface Props {
15
+ value: string | number | undefined;
16
+ displayValue?: string | number | undefined;
17
+ }
18
+
19
+ export const ExpandOnHover = ({value, displayValue}: Props): JSX.Element => {
20
+ return (
21
+ <div className="relative group w-full">
22
+ <div className="text-ellipsis w-full overflow-hidden whitespace-nowrap">
23
+ {displayValue ?? value}
24
+ </div>
25
+ <div className="group-hover:flex hidden absolute -inset-2 max-w-[500px] whitespace-normal h-fit bg-gray-50 dark:bg-gray-900 shadow-[0_0_10px_2px_rgba(0,0,0,0.3)] rounded p-2 break-all">
26
+ {value}
27
+ </div>
28
+ </div>
29
+ );
30
+ };
@@ -13,6 +13,7 @@
13
13
 
14
14
  import {useEffect, useState} from 'react';
15
15
 
16
+ import {pointer} from 'd3-selection';
16
17
  import {CopyToClipboard} from 'react-copy-to-clipboard';
17
18
  import {usePopper} from 'react-popper';
18
19
 
@@ -25,11 +26,16 @@ import {
25
26
  import {useKeyDown} from '@parca/components';
26
27
  import {getLastItem, valueFormatter} from '@parca/functions';
27
28
 
28
- import {hexifyAddress, truncateString} from '../';
29
+ import {hexifyAddress, truncateString, truncateStringReverse} from '../';
30
+ import {ExpandOnHover} from './ExpandOnHoverValue';
31
+
32
+ const NoData = (): JSX.Element => {
33
+ return <span className="rounded bg-gray-200 dark:bg-gray-800 px-2">Not available</span>;
34
+ };
29
35
 
30
36
  interface GraphTooltipProps {
31
- x: number;
32
- y: number;
37
+ x?: number;
38
+ y?: number;
33
39
  unit: string;
34
40
  total: number;
35
41
  hoveringNode: HoveringNode;
@@ -84,12 +90,10 @@ const TooltipMetaInfo = ({
84
90
  locations?: Location[];
85
91
  functions?: ParcaFunction[];
86
92
  }): JSX.Element => {
87
- if (hoveringNode.meta === undefined) return <></>;
88
-
89
93
  // populate meta from the flamegraph metadata tables
90
94
  if (
91
95
  locations !== undefined &&
92
- hoveringNode.meta.locationIndex !== undefined &&
96
+ hoveringNode.meta?.locationIndex !== undefined &&
93
97
  hoveringNode.meta.locationIndex !== 0
94
98
  ) {
95
99
  const location = locations[hoveringNode.meta.locationIndex - 1];
@@ -142,60 +146,73 @@ const TooltipMetaInfo = ({
142
146
  }`
143
147
  }`;
144
148
  };
149
+ const file = getTextForFile(hoveringNode);
145
150
 
146
151
  return (
147
152
  <>
148
- {hoveringNode.meta.function?.filename !== undefined &&
149
- hoveringNode.meta.function?.filename !== '' && (
150
- <tr>
151
- <td className="w-1/5">File</td>
152
- <td className="w-4/5 break-all">
153
- <CopyToClipboard onCopy={onCopy} text={getTextForFile(hoveringNode)}>
154
- <button className="cursor-pointer text-left">{getTextForFile(hoveringNode)}</button>
155
- </CopyToClipboard>
156
- </td>
157
- </tr>
158
- )}
159
- {hoveringNode.meta.location?.address !== undefined &&
160
- hoveringNode.meta.location?.address !== '0' && (
161
- <tr>
162
- <td className="w-1/5">Address</td>
163
- <td className="w-4/5 break-all">
164
- <CopyToClipboard
165
- onCopy={onCopy}
166
- text={hexifyAddress(hoveringNode.meta.location.address)}
167
- >
168
- <button className="cursor-pointer">
169
- {hexifyAddress(hoveringNode.meta.location.address)}
170
- </button>
171
- </CopyToClipboard>
172
- </td>
173
- </tr>
174
- )}
175
- {hoveringNode.meta.mapping !== undefined && hoveringNode.meta.mapping.file !== '' && (
176
- <tr>
177
- <td className="w-1/5">Binary</td>
178
- <td className="w-4/5 break-all">
153
+ <tr>
154
+ <td className="w-1/4">File</td>
155
+ <td className="w-3/4 break-all">
156
+ {hoveringNode.meta?.function?.filename == null ||
157
+ hoveringNode.meta?.function.filename === '' ? (
158
+ <NoData />
159
+ ) : (
160
+ <CopyToClipboard onCopy={onCopy} text={file}>
161
+ <button className="cursor-pointer text-left whitespace-nowrap">
162
+ <ExpandOnHover value={file} displayValue={truncateStringReverse(file, 40)} />
163
+ </button>
164
+ </CopyToClipboard>
165
+ )}
166
+ </td>
167
+ </tr>
168
+
169
+ <tr>
170
+ <td className="w-1/4">Address</td>
171
+ <td className="w-3/4 break-all">
172
+ {hoveringNode.meta?.location?.address == null ||
173
+ hoveringNode.meta?.location.address === '0' ? (
174
+ <NoData />
175
+ ) : (
176
+ <CopyToClipboard
177
+ onCopy={onCopy}
178
+ text={hexifyAddress(hoveringNode.meta.location.address)}
179
+ >
180
+ <button className="cursor-pointer">
181
+ {hexifyAddress(hoveringNode.meta.location.address)}
182
+ </button>
183
+ </CopyToClipboard>
184
+ )}
185
+ </td>
186
+ </tr>
187
+ <tr>
188
+ <td className="w-1/4">Binary</td>
189
+ <td className="w-3/4 break-all">
190
+ {hoveringNode.meta?.mapping == null || hoveringNode.meta.mapping.file === '' ? (
191
+ <NoData />
192
+ ) : (
179
193
  <CopyToClipboard onCopy={onCopy} text={hoveringNode.meta.mapping.file}>
180
194
  <button className="cursor-pointer">
181
195
  {getLastItem(hoveringNode.meta.mapping.file)}
182
196
  </button>
183
197
  </CopyToClipboard>
184
- </td>
185
- </tr>
186
- )}
187
- {hoveringNode.meta.mapping !== undefined && hoveringNode.meta.mapping.buildId !== '' && (
188
- <tr>
189
- <td className="w-1/5">Build Id</td>
190
- <td className="w-4/5 break-all">
198
+ )}
199
+ </td>
200
+ </tr>
201
+
202
+ <tr>
203
+ <td className="w-1/4">Build Id</td>
204
+ <td className="w-3/4 break-all">
205
+ {hoveringNode.meta?.mapping == null || hoveringNode.meta?.mapping.buildId === '' ? (
206
+ <NoData />
207
+ ) : (
191
208
  <CopyToClipboard onCopy={onCopy} text={hoveringNode.meta.mapping.buildId}>
192
209
  <button className="cursor-pointer">
193
- {truncateString(getLastItem(hoveringNode.meta.mapping.buildId) as string, 16)}
210
+ {truncateString(getLastItem(hoveringNode.meta.mapping.buildId) as string, 28)}
194
211
  </button>
195
212
  </CopyToClipboard>
196
- </td>
197
- </tr>
198
- )}
213
+ )}
214
+ </td>
215
+ </tr>
199
216
  </>
200
217
  );
201
218
  };
@@ -246,20 +263,6 @@ const GraphTooltipContent = ({
246
263
  const diffValueText = diffSign + valueFormatter(diff, unit, 1);
247
264
  const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
248
265
  const diffText = `${diffValueText} (${diffPercentageText})`;
249
- const metaRows =
250
- hoveringNode.meta === undefined ? (
251
- <></>
252
- ) : (
253
- <TooltipMetaInfo
254
- onCopy={onCopy}
255
- // @ts-expect-error
256
- hoveringNode={hoveringNode}
257
- strings={strings}
258
- mappings={mappings}
259
- locations={locations}
260
- functions={functions}
261
- />
262
- );
263
266
 
264
267
  const getTextForCumulative = (hoveringNodeCumulative: number): string => {
265
268
  return `${valueFormatter(hoveringNodeCumulative, unit, 2)} (
@@ -267,15 +270,12 @@ const GraphTooltipContent = ({
267
270
  };
268
271
 
269
272
  return (
270
- <div className={`flex ${isFixed ? 'w-full h-36' : ''}`}>
271
- <div className={`m-auto w-full ${isFixed ? 'w-full h-36' : ''}`}>
272
- <div
273
- className="border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-900 rounded-lg p-3 shadow-lg opacity-90"
274
- style={{borderWidth: 1}}
275
- >
273
+ <div className={`text-sm flex ${isFixed ? 'w-full' : ''}`}>
274
+ <div className={`m-auto w-full ${isFixed ? 'w-full' : ''}`}>
275
+ <div className="border border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-900 rounded-lg p-3 shadow-lg min-h-52 w-[500px] flex justify-between flex-col">
276
276
  <div className="flex flex-row">
277
- <div className="ml-2 mr-6">
278
- <span className="font-semibold break-all">
277
+ <div className="mx-2">
278
+ <div className="font-semibold break-all h-10 flex items-center">
279
279
  {hoveringNode.meta === undefined ? (
280
280
  <p>root</p>
281
281
  ) : (
@@ -306,44 +306,49 @@ const GraphTooltipContent = ({
306
306
  )}
307
307
  </>
308
308
  )}
309
- </span>
310
- <span className="text-gray-700 dark:text-gray-300 my-2">
311
- <table className="table-fixed">
312
- <tbody>
309
+ </div>
310
+ <table className="table-fixed pr-0 text-gray-700 dark:text-gray-300 my-2 w-full">
311
+ <tbody>
312
+ <tr>
313
+ <td className="w-1/4">Cumulative</td>
314
+
315
+ <td className="w-3/4">
316
+ <CopyToClipboard
317
+ onCopy={onCopy}
318
+ text={getTextForCumulative(hoveringNodeCumulative)}
319
+ >
320
+ <button className="cursor-pointer">
321
+ {getTextForCumulative(hoveringNodeCumulative)}
322
+ </button>
323
+ </CopyToClipboard>
324
+ </td>
325
+ </tr>
326
+ {hoveringNode.diff !== undefined && diff !== 0 && (
313
327
  <tr>
314
- <td className="w-1/5">Cumulative</td>
315
-
316
- <td className="w-4/5">
317
- <CopyToClipboard
318
- onCopy={onCopy}
319
- text={getTextForCumulative(hoveringNodeCumulative)}
320
- >
321
- <button className="cursor-pointer">
322
- {getTextForCumulative(hoveringNodeCumulative)}
323
- </button>
328
+ <td className="w-1/4">Diff</td>
329
+ <td className="w-3/4">
330
+ <CopyToClipboard onCopy={onCopy} text={diffText}>
331
+ <button className="cursor-pointer">{diffText}</button>
324
332
  </CopyToClipboard>
325
333
  </td>
326
334
  </tr>
327
- {hoveringNode.diff !== undefined && diff !== 0 && (
328
- <tr>
329
- <td className="w-1/5">Diff</td>
330
- <td className="w-4/5">
331
- <CopyToClipboard onCopy={onCopy} text={diffText}>
332
- <button className="cursor-pointer">{diffText}</button>
333
- </CopyToClipboard>
334
- </td>
335
- </tr>
336
- )}
337
- {metaRows}
338
- </tbody>
339
- </table>
340
- </span>
341
-
342
- <span className="block text-gray-500 text-xs mt-2">
343
- {isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.'}
344
- </span>
335
+ )}
336
+ <TooltipMetaInfo
337
+ onCopy={onCopy}
338
+ // @ts-expect-error
339
+ hoveringNode={hoveringNode}
340
+ strings={strings}
341
+ mappings={mappings}
342
+ locations={locations}
343
+ functions={functions}
344
+ />
345
+ </tbody>
346
+ </table>
345
347
  </div>
346
348
  </div>
349
+ <span className="block text-gray-500 text-xs mx-2">
350
+ {isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.'}
351
+ </span>
347
352
  </div>
348
353
  </div>
349
354
  </div>
@@ -370,7 +375,7 @@ const GraphTooltip = ({
370
375
  virtualContextElement ? virtualElement : contextElement,
371
376
  popperElement,
372
377
  {
373
- placement: 'auto-start',
378
+ placement: 'bottom-start',
374
379
  strategy: 'absolute',
375
380
  modifiers: [
376
381
  {
@@ -390,17 +395,36 @@ const GraphTooltip = ({
390
395
  }
391
396
  );
392
397
 
393
- const update = popperProps.update;
394
398
  const {isShiftDown} = useKeyDown();
395
399
 
396
400
  useEffect(() => {
397
- if (contextElement != null) {
398
- if (isShiftDown) return;
401
+ if (contextElement === null) return;
402
+ const onMouseMove: EventListenerOrEventListenerObject = (e: Event) => {
403
+ if (isShiftDown) {
404
+ return;
405
+ }
399
406
 
400
- virtualElement.getBoundingClientRect = generateGetBoundingClientRect(contextElement, x, y);
401
- void update?.();
402
- }
403
- }, [x, y, contextElement, update, isShiftDown]);
407
+ let tooltipX = x;
408
+ let tooltipY = y;
409
+ if (tooltipX == null || tooltipY == null) {
410
+ const rel = pointer(e);
411
+ tooltipX = rel[0];
412
+ tooltipY = rel[1];
413
+ }
414
+ virtualElement.getBoundingClientRect = generateGetBoundingClientRect(
415
+ contextElement,
416
+ tooltipX,
417
+ tooltipY
418
+ );
419
+
420
+ void popperProps.update?.();
421
+ };
422
+
423
+ contextElement.addEventListener('mousemove', onMouseMove);
424
+ return () => {
425
+ contextElement.removeEventListener('mousemove', onMouseMove);
426
+ };
427
+ }, [contextElement, popperProps, isShiftDown, x, y]);
404
428
 
405
429
  if (hoveringNode === undefined || hoveringNode == null) return <></>;
406
430
 
@@ -228,12 +228,12 @@ export const IcicleNode = React.memo(function IcicleNode({
228
228
  <rect
229
229
  x={0}
230
230
  y={0}
231
- width={width - 1}
232
- height={height - 1}
231
+ width={width}
232
+ height={height}
233
233
  style={{
234
234
  fill: colorResult,
235
235
  }}
236
- className={cx({
236
+ className={cx('stroke-white dark:stroke-gray-700', {
237
237
  'opacity-50': isHighlightEnabled && !isHighlighted,
238
238
  })}
239
239
  />
@@ -11,12 +11,10 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import React, {memo, useEffect, useMemo, useRef, useState} from 'react';
14
+ import {memo, useEffect, useMemo, useRef, useState} from 'react';
15
15
 
16
16
  import cx from 'classnames';
17
17
  import {scaleLinear} from 'd3-scale';
18
- import {pointer} from 'd3-selection';
19
- import {throttle} from 'lodash';
20
18
 
21
19
  import {Flamegraph, FlamegraphNode, FlamegraphRootNode} from '@parca/client';
22
20
  import {Button, useURLState} from '@parca/components';
@@ -50,7 +48,6 @@ export const IcicleGraph = memo(function IcicleGraph({
50
48
  const [hoveringNode, setHoveringNode] = useState<
51
49
  FlamegraphNode | FlamegraphRootNode | undefined
52
50
  >();
53
- const [pos, setPos] = useState([0, 0]);
54
51
  const [height, setHeight] = useState(0);
55
52
  const svg = useRef(null);
56
53
  const ref = useRef<SVGGElement>(null);
@@ -86,13 +83,6 @@ export const IcicleGraph = memo(function IcicleGraph({
86
83
  return <></>;
87
84
  }
88
85
 
89
- const throttledSetPos = throttle(setPos, 20);
90
- const onMouseMove = (e: React.MouseEvent<SVGSVGElement | HTMLDivElement>): void => {
91
- // X/Y coordinate array relative to svg
92
- const rel = pointer(e);
93
-
94
- throttledSetPos([rel[0], rel[1]]);
95
- };
96
86
  const isColorStackLegendVisible = colorProfileName !== 'default';
97
87
 
98
88
  return (
@@ -101,8 +91,6 @@ export const IcicleGraph = memo(function IcicleGraph({
101
91
  <GraphTooltip
102
92
  unit={sampleUnit}
103
93
  total={total}
104
- x={pos[0]}
105
- y={pos[1]}
106
94
  hoveringNode={hoveringNode as HoveringNode}
107
95
  contextElement={svg.current}
108
96
  strings={coloredGraph.stringTable}
@@ -140,7 +128,6 @@ export const IcicleGraph = memo(function IcicleGraph({
140
128
  className="font-robotoMono"
141
129
  width={width}
142
130
  height={height}
143
- onMouseMove={onMouseMove}
144
131
  preserveAspectRatio="xMinYMid"
145
132
  ref={svg}
146
133
  >
@@ -25,7 +25,14 @@ import {
25
25
  } from 'react-beautiful-dnd';
26
26
 
27
27
  import {Callgraph as CallgraphType, Flamegraph, QueryServiceClient, Top} from '@parca/client';
28
- import {Button, Card, KeyDownProvider, useParcaContext, useURLState} from '@parca/components';
28
+ import {
29
+ Button,
30
+ Card,
31
+ ConditionalWrapper,
32
+ KeyDownProvider,
33
+ useParcaContext,
34
+ useURLState,
35
+ } from '@parca/components';
29
36
  import {useContainerDimensions} from '@parca/dynamicsize';
30
37
  import {getNewSpanColor} from '@parca/functions';
31
38
  import {CloseIcon} from '@parca/icons';
@@ -154,7 +161,17 @@ export const ProfileView = ({
154
161
  switch (type) {
155
162
  case 'icicle': {
156
163
  return flamegraphData?.data != null ? (
157
- <Profiler id="icicleGraph" onRender={perf?.onRender as React.ProfilerOnRenderCallback}>
164
+ <ConditionalWrapper
165
+ condition={perf?.onRender != null}
166
+ wrapper={({children}) => (
167
+ <Profiler
168
+ id="icicleGraph"
169
+ onRender={perf?.onRender as React.ProfilerOnRenderCallback}
170
+ >
171
+ {children}
172
+ </Profiler>
173
+ )}
174
+ >
158
175
  <ProfileIcicleGraph
159
176
  curPath={curPath}
160
177
  setNewCurPath={setNewCurPath}
@@ -164,7 +181,7 @@ export const ProfileView = ({
164
181
  navigateTo={navigateTo}
165
182
  loading={flamegraphData.loading}
166
183
  />
167
- </Profiler>
184
+ </ConditionalWrapper>
168
185
  ) : (
169
186
  <> </>
170
187
  );
package/src/utils.ts CHANGED
@@ -51,3 +51,11 @@ export const truncateString = (str: string, num: number): string => {
51
51
 
52
52
  return str.slice(0, num) + '...';
53
53
  };
54
+
55
+ export const truncateStringReverse = (str: string, num: number): string => {
56
+ if (str.length <= num) {
57
+ return str;
58
+ }
59
+
60
+ return '...' + str.slice(str.length - num);
61
+ };
@@ -2,7 +2,17 @@ module.exports = {
2
2
  content: ['./src/**/*.{html,js,jsx,ts,tsx}'],
3
3
  darkMode: ['class', '[class~="theme-dark"]'],
4
4
  theme: {
5
- extend: {},
5
+ extend: {
6
+ minHeight: theme => ({
7
+ ...theme('spacing'),
8
+ }),
9
+ maxWidth: theme => ({
10
+ ...theme('spacing'),
11
+ }),
12
+ minWidth: theme => ({
13
+ ...theme('spacing'),
14
+ }),
15
+ },
6
16
  },
7
17
  plugins: [],
8
18
  };