@parca/profile 0.16.429 → 0.16.431
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 +8 -0
- package/dist/MetricsGraph/index.d.ts +3 -2
- package/dist/MetricsGraph/index.d.ts.map +1 -1
- package/dist/MetricsGraph/index.js +17 -10
- package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts.map +1 -1
- package/dist/ProfileExplorer/ProfileExplorerCompare.js +25 -2
- package/dist/ProfileExplorer/ProfileExplorerSingle.d.ts.map +1 -1
- package/dist/ProfileExplorer/ProfileExplorerSingle.js +24 -2
- package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
- package/dist/ProfileMetricsGraph/index.js +1 -1
- package/dist/ProfileSelector/index.d.ts +5 -1
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +41 -37
- package/dist/ProfileSource.js +2 -2
- package/dist/ProfileView/index.d.ts.map +1 -1
- package/dist/ProfileView/index.js +9 -7
- package/dist/styles.css +1 -1
- package/package.json +4 -4
- package/src/MetricsGraph/index.tsx +25 -8
- package/src/ProfileExplorer/ProfileExplorerCompare.tsx +38 -19
- package/src/ProfileExplorer/ProfileExplorerSingle.tsx +22 -6
- package/src/ProfileMetricsGraph/index.tsx +1 -0
- package/src/ProfileSelector/index.tsx +99 -75
- package/src/ProfileSource.tsx +2 -2
- package/src/ProfileView/index.tsx +17 -8
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.16.431](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.430...@parca/profile@0.16.431) (2024-09-02)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
10
|
+
## [0.16.430](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.428...@parca/profile@0.16.430) (2024-09-02)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @parca/profile
|
|
13
|
+
|
|
6
14
|
## [0.16.429](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.428...@parca/profile@0.16.429) (2024-08-23)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -19,6 +19,7 @@ interface Props {
|
|
|
19
19
|
width?: number;
|
|
20
20
|
height?: number;
|
|
21
21
|
margin?: number;
|
|
22
|
+
sumBy?: string[];
|
|
22
23
|
}
|
|
23
24
|
export interface HighlightedSeries {
|
|
24
25
|
seriesIndex: number;
|
|
@@ -30,8 +31,8 @@ export interface HighlightedSeries {
|
|
|
30
31
|
x: number;
|
|
31
32
|
y: number;
|
|
32
33
|
}
|
|
33
|
-
declare const MetricsGraph: ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, sampleUnit, width, height, margin, }: Props) => JSX.Element;
|
|
34
|
+
declare const MetricsGraph: ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, sampleUnit, width, height, margin, sumBy, }: Props) => JSX.Element;
|
|
34
35
|
export default MetricsGraph;
|
|
35
36
|
export declare const parseValue: (value: string) => number | null;
|
|
36
|
-
export declare const RawMetricsGraph: ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, width, height, margin, sampleUnit, }: Props) => JSX.Element;
|
|
37
|
+
export declare const RawMetricsGraph: ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, width, height, margin, sampleUnit, sumBy, }: Props) => JSX.Element;
|
|
37
38
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/MetricsGraph/index.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAC,KAAK,EAAiB,aAAa,IAAI,eAAe,EAAC,MAAM,eAAe,CAAC;AACrF,OAAO,EAAC,aAAa,EAAkB,MAAM,mBAAmB,CAAC;AASjE,OAAO,EAAC,sBAAsB,EAAC,MAAM,IAAI,CAAC;AAO1C,UAAU,KAAK;IACb,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACvC,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7F,eAAe,EAAE,CACf,MAAM,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,KACvE,IAAI,CAAC;IACV,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/MetricsGraph/index.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAC,KAAK,EAAiB,aAAa,IAAI,eAAe,EAAC,MAAM,eAAe,CAAC;AACrF,OAAO,EAAC,aAAa,EAAkB,MAAM,mBAAmB,CAAC;AASjE,OAAO,EAAC,sBAAsB,EAAC,MAAM,IAAI,CAAC;AAO1C,UAAU,KAAK;IACb,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACvC,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7F,eAAe,EAAE,CACf,MAAM,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,KACvE,IAAI,CAAC;IACV,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAQD,QAAA,MAAM,YAAY,yHAaf,KAAK,KAAG,GAAG,CAAC,OA0Bd,CAAC;AAEF,eAAe,YAAY,CAAC;AAE5B,eAAO,MAAM,UAAU,UAAW,MAAM,KAAG,MAAM,GAAG,IAKnD,CAAC;AAKF,eAAO,MAAM,eAAe,yHAazB,KAAK,KAAG,GAAG,CAAC,OA4ed,CAAC"}
|
|
@@ -23,9 +23,9 @@ import MetricsSeries from '../MetricsSeries';
|
|
|
23
23
|
import MetricsContextMenu from './MetricsContextMenu';
|
|
24
24
|
import MetricsInfoPanel from './MetricsInfoPanel';
|
|
25
25
|
import MetricsTooltip from './MetricsTooltip';
|
|
26
|
-
const MetricsGraph = ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, sampleUnit, width = 0, height = 0, margin = 0, }) => {
|
|
26
|
+
const MetricsGraph = ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, sampleUnit, width = 0, height = 0, margin = 0, sumBy, }) => {
|
|
27
27
|
const [isInfoPanelOpen, setIsInfoPanelOpen] = useState(false);
|
|
28
|
-
return (_jsxs("div", { className: "relative", onClick: () => isInfoPanelOpen && setIsInfoPanelOpen(false), children: [_jsx("div", { className: "absolute right-0 top-0", children: _jsx(MetricsInfoPanel, { isInfoPanelOpen: isInfoPanelOpen, onInfoIconClick: () => setIsInfoPanelOpen(true) }) }), _jsx(RawMetricsGraph, { data: data, from: from, to: to, profile: profile, onSampleClick: onSampleClick, addLabelMatcher: addLabelMatcher, setTimeRange: setTimeRange, sampleUnit: sampleUnit, width: width, height: height, margin: margin })] }));
|
|
28
|
+
return (_jsxs("div", { className: "relative", onClick: () => isInfoPanelOpen && setIsInfoPanelOpen(false), children: [_jsx("div", { className: "absolute right-0 top-0", children: _jsx(MetricsInfoPanel, { isInfoPanelOpen: isInfoPanelOpen, onInfoIconClick: () => setIsInfoPanelOpen(true) }) }), _jsx(RawMetricsGraph, { data: data, from: from, to: to, profile: profile, onSampleClick: onSampleClick, addLabelMatcher: addLabelMatcher, setTimeRange: setTimeRange, sampleUnit: sampleUnit, width: width, height: height, margin: margin, sumBy: sumBy })] }));
|
|
29
29
|
};
|
|
30
30
|
export default MetricsGraph;
|
|
31
31
|
export const parseValue = (value) => {
|
|
@@ -36,7 +36,7 @@ export const parseValue = (value) => {
|
|
|
36
36
|
};
|
|
37
37
|
const lineStroke = '1px';
|
|
38
38
|
const lineStrokeHover = '2px';
|
|
39
|
-
export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, width, height = 50, margin = 0, sampleUnit, }) => {
|
|
39
|
+
export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, width, height = 50, margin = 0, sampleUnit, sumBy, }) => {
|
|
40
40
|
const { timezone } = useParcaContext();
|
|
41
41
|
const graph = useRef(null);
|
|
42
42
|
const [dragging, setDragging] = useState(false);
|
|
@@ -51,6 +51,7 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLab
|
|
|
51
51
|
if (width === undefined || width == null) {
|
|
52
52
|
width = 0;
|
|
53
53
|
}
|
|
54
|
+
const graphWidth = width - margin * 1.5 - margin / 2;
|
|
54
55
|
const series = data.reduce(function (agg, s) {
|
|
55
56
|
if (s.labelset !== undefined) {
|
|
56
57
|
const metric = s.labelset.labels.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -82,10 +83,7 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLab
|
|
|
82
83
|
return d[1];
|
|
83
84
|
});
|
|
84
85
|
/* Scale */
|
|
85
|
-
const xScale = d3
|
|
86
|
-
.scaleUtc()
|
|
87
|
-
.domain([from, to])
|
|
88
|
-
.range([0, width - 2.5 * margin]);
|
|
86
|
+
const xScale = d3.scaleUtc().domain([from, to]).range([0, graphWidth]);
|
|
89
87
|
const yScale = d3
|
|
90
88
|
.scaleLinear()
|
|
91
89
|
// tslint:disable-next-line
|
|
@@ -192,15 +190,24 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLab
|
|
|
192
190
|
}
|
|
193
191
|
let s = null;
|
|
194
192
|
let seriesIndex = -1;
|
|
193
|
+
// if there are both query matchers and also a sumby value, we need to check if the sumby value is part of the query matchers.
|
|
194
|
+
// if it is, then we should prioritize using the sumby label name and value to find the selected profile.
|
|
195
|
+
const useSumBy = sumBy !== undefined &&
|
|
196
|
+
sumBy.length > 0 &&
|
|
197
|
+
profile.query.matchers.length > 0 &&
|
|
198
|
+
profile.query.matchers.some(e => sumBy.includes(e.key));
|
|
199
|
+
// get only the sumby keys and values from the profile query matchers
|
|
200
|
+
const sumByMatchers = sumBy !== undefined ? profile.query.matchers.filter(e => sumBy.includes(e.key)) : [];
|
|
201
|
+
const keysToMatch = useSumBy ? sumByMatchers : profile.query.matchers;
|
|
195
202
|
outer: for (let i = 0; i < series.length; i++) {
|
|
196
|
-
const keys =
|
|
203
|
+
const keys = keysToMatch.map(e => e.key);
|
|
197
204
|
for (let j = 0; j < keys.length; j++) {
|
|
198
205
|
const matcherKey = keys[j];
|
|
199
206
|
const label = series[i].metric.find(e => e.name === matcherKey);
|
|
200
207
|
if (label === undefined) {
|
|
201
208
|
continue outer; // label doesn't exist to begin with
|
|
202
209
|
}
|
|
203
|
-
if (
|
|
210
|
+
if (keysToMatch[j].value !== label.value) {
|
|
204
211
|
continue outer; // label values don't match
|
|
205
212
|
}
|
|
206
213
|
}
|
|
@@ -267,7 +274,7 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLab
|
|
|
267
274
|
transform: `translate(0, ${yScale(d)})`, children: [_jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x2: -6 }), _jsx("text", { fill: "currentColor", x: -9, dy: '0.32em', children: valueFormatter(d, yAxisUnit, decimals) })] }, `tick-${i}`), _jsx("g", { children: _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: xScale(from), x2: xScale(to), y1: yScale(d), y2: yScale(d) }) }, `grid-${i}`)] }, `${i.toString()}-${d.toString()}`));
|
|
268
275
|
}), _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: 0, x2: 0, y1: 0, y2: height - margin }), _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: xScale(to), x2: xScale(to), y1: 0, y2: height - margin }), _jsx("g", { transform: `translate(${-margin}, ${(height - margin) / 2}) rotate(270)`, children: _jsx("text", { fill: "currentColor", dy: "-0.7em", className: "text-sm capitalize", textAnchor: "middle", children: yAxisLabel }) })] }), _jsxs("g", { className: "x axis", fill: "none", fontSize: "10", textAnchor: "middle", transform: `translate(0,${height - margin})`, children: [xScale.ticks(5).map((d, i) => (_jsxs(Fragment, { children: [_jsxs("g", { className: "tick",
|
|
269
276
|
/* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
|
|
270
|
-
transform: `translate(${xScale(d)}, 0)`, children: [_jsx("line", { y2: 6, className: "stroke-gray-300 dark:stroke-gray-500" }), _jsx("text", { fill: "currentColor", dy: ".71em", y: 9, children: formatDate(d, formatForTimespan(from, to), timezone) })] }, `tick-${i}`), _jsx("g", { children: _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: xScale(d), x2: xScale(d), y1: 0, y2: -height + margin }) }, `grid-${i}`)] }, `${i.toString()}-${d.toString()}`))), _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1:
|
|
277
|
+
transform: `translate(${xScale(d)}, 0)`, children: [_jsx("line", { y2: 6, className: "stroke-gray-300 dark:stroke-gray-500" }), _jsx("text", { fill: "currentColor", dy: ".71em", y: 9, children: formatDate(d, formatForTimespan(from, to), timezone) })] }, `tick-${i}`), _jsx("g", { children: _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: xScale(d), x2: xScale(d), y1: 0, y2: -height + margin }) }, `grid-${i}`)] }, `${i.toString()}-${d.toString()}`))), _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: 0, x2: graphWidth, y1: 0, y2: 0 }), _jsx("g", { transform: `translate(${(width - 2.5 * margin) / 2}, ${margin / 2})`, children: _jsx("text", { fill: "currentColor", dy: ".71em", y: 5, className: "text-sm", children: "Time" }) })] }), _jsx("g", { className: "lines fill-transparent", children: series.map((s, i) => (_jsx("g", { className: "line", children: _jsx(MetricsSeries, { data: s, line: l, color: color(i.toString()), strokeWidth: hovering && highlighted != null && i === highlighted.seriesIndex
|
|
271
278
|
? lineStrokeHover
|
|
272
279
|
: lineStroke, xScale: xScale, yScale: yScale }) }, i))) }), hovering && highlighted != null && (_jsx("g", { className: "circle-group", ref: metricPointRef, style: { fill: color(highlighted.seriesIndex.toString()) }, children: _jsx(MetricsCircle, { cx: highlighted.x, cy: highlighted.y }) })), selected != null && (_jsx("g", { className: "circle-group", style: selected?.seriesIndex != null
|
|
273
280
|
? { fill: color(selected.seriesIndex.toString()) }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProfileExplorerCompare.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/ProfileExplorerCompare.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProfileExplorerCompare.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/ProfileExplorerCompare.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAGjD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAoB,gBAAgB,EAAsB,MAAM,IAAI,CAAC;AAC5E,OAAwB,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAEnE,UAAU,2BAA2B;IACnC,WAAW,EAAE,kBAAkB,CAAC;IAEhC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,YAAY,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,YAAY,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,cAAc,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACnD,cAAc,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACnD,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAErC,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,QAAA,MAAM,sBAAsB,+IAYzB,2BAA2B,KAAG,GAAG,CAAC,OAwFpC,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
|
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
|
|
2
|
+
// Copyright 2022 The Parca Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
import { useState } from 'react';
|
|
15
|
+
import { useURLState } from '@parca/components';
|
|
3
16
|
import { Query } from '@parca/parser';
|
|
4
17
|
import { ProfileDiffSource, ProfileViewWithData } from '..';
|
|
5
18
|
import ProfileSelector from '../ProfileSelector';
|
|
6
19
|
const ProfileExplorerCompare = ({ queryClient, queryA, queryB, profileA, profileB, selectQueryA, selectQueryB, selectProfileA, selectProfileB, closeProfile, navigateTo, }) => {
|
|
20
|
+
const [showMetricsGraph, setShowMetricsGraph] = useState(true);
|
|
21
|
+
const [showButton, setShowButton] = useState(false);
|
|
7
22
|
const closeProfileA = () => {
|
|
8
23
|
closeProfile('A');
|
|
9
24
|
};
|
|
@@ -12,6 +27,14 @@ const ProfileExplorerCompare = ({ queryClient, queryA, queryB, profileA, profile
|
|
|
12
27
|
};
|
|
13
28
|
const [compareAbsolute] = useURLState('compare_absolute');
|
|
14
29
|
const [functionFilter] = useURLState('filter_by_function');
|
|
15
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex justify-between gap-2
|
|
30
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex justify-between gap-2 relative mb-2", onMouseEnter: () => {
|
|
31
|
+
if (!showMetricsGraph)
|
|
32
|
+
return;
|
|
33
|
+
setShowButton(true);
|
|
34
|
+
}, onMouseLeave: () => {
|
|
35
|
+
if (!showMetricsGraph)
|
|
36
|
+
return;
|
|
37
|
+
setShowButton(false);
|
|
38
|
+
}, children: [_jsx("div", { className: "flex-column flex-1 p-2 shadow-md rounded-md", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryA, profileSelection: profileA, selectProfile: selectProfileA, selectQuery: selectQueryA, closeProfile: closeProfileA, enforcedProfileName: '', comparing: true, navigateTo: navigateTo, suffix: "_a", showMetricsGraph: showMetricsGraph, displayHideMetricsGraphButton: showButton, setDisplayHideMetricsGraphButton: setShowMetricsGraph }) }), _jsx("div", { className: "flex-column flex-1 p-2 shadow-md rounded-md", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryB, profileSelection: profileB, selectProfile: selectProfileB, selectQuery: selectQueryB, closeProfile: closeProfileB, enforcedProfileName: Query.parse(queryA.expression).profileName(), comparing: true, navigateTo: navigateTo, suffix: "_b", showMetricsGraph: showMetricsGraph, displayHideMetricsGraphButton: showButton, setDisplayHideMetricsGraphButton: setShowMetricsGraph }) })] }), _jsx("div", { className: "grid grid-cols-1", children: profileA != null && profileB != null ? (_jsx("div", { children: _jsx(ProfileViewWithData, { queryClient: queryClient, profileSource: new ProfileDiffSource(profileA.ProfileSource(), profileB.ProfileSource(), Array.isArray(functionFilter) ? functionFilter[0] : functionFilter, compareAbsolute === 'true') }) })) : (_jsx("div", { children: _jsx("div", { className: "my-20 text-center", children: _jsx("p", { children: "Select a profile on both sides." }) }) })) })] }));
|
|
16
39
|
};
|
|
17
40
|
export default ProfileExplorerCompare;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProfileExplorerSingle.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/ProfileExplorerSingle.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProfileExplorerSingle.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/ProfileExplorerSingle.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAC,gBAAgB,EAAsB,MAAM,IAAI,CAAC;AACzD,OAAwB,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAEnE,UAAU,0BAA0B;IAClC,WAAW,EAAE,kBAAkB,CAAC;IAChC,KAAK,EAAE,cAAc,CAAC;IACtB,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,QAAA,MAAM,qBAAqB,6EAOxB,0BAA0B,KAAG,GAAG,CAAC,OAyCnC,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
|
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
2
|
+
// Copyright 2022 The Parca Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
import { useState } from 'react';
|
|
3
15
|
import { ProfileViewWithData } from '..';
|
|
4
16
|
import ProfileSelector from '../ProfileSelector';
|
|
5
17
|
const ProfileExplorerSingle = ({ queryClient, query, selectQuery, selectProfile, profile, navigateTo, }) => {
|
|
6
|
-
|
|
18
|
+
const [showMetricsGraph, setShowMetricsGraph] = useState(true);
|
|
19
|
+
const [showButton, setShowButton] = useState(false);
|
|
20
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "relative", onMouseEnter: () => {
|
|
21
|
+
if (!showMetricsGraph)
|
|
22
|
+
return;
|
|
23
|
+
setShowButton(true);
|
|
24
|
+
}, onMouseLeave: () => {
|
|
25
|
+
if (!showMetricsGraph)
|
|
26
|
+
return;
|
|
27
|
+
setShowButton(false);
|
|
28
|
+
}, children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: query, selectQuery: selectQuery, selectProfile: selectProfile, closeProfile: () => { }, profileSelection: profile, comparing: false, enforcedProfileName: '', navigateTo: navigateTo, suffix: "_a", showMetricsGraph: showMetricsGraph, displayHideMetricsGraphButton: showButton, setDisplayHideMetricsGraphButton: setShowMetricsGraph }) }), profile != null ? (_jsx(ProfileViewWithData, { queryClient: queryClient, profileSource: profile.ProfileSource() })) : (_jsx(_Fragment, {}))] }));
|
|
7
29
|
};
|
|
8
30
|
export default ProfileExplorerSingle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileMetricsGraph/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAGlD,OAAO,EAAW,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAY,MAAM,eAAe,CAAC;AACjG,OAAO,EACL,aAAa,EAKd,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAK5D,UAAU,6BAA6B;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD,eAAO,MAAM,wBAAwB,gBAAe,6BAA6B,KAAG,GAAG,CAAC,OAMvF,CAAC;AAEF,UAAU,wBAAwB;IAChC,WAAW,EAAE,kBAAkB,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,eAAe,EAAE,CACf,MAAM,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,KACvE,IAAI,CAAC;IACV,YAAY,EAAE,CACZ,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,KAAK,EAAE,EACf,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,KACb,IAAI,CAAC;IACV,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;CACxB;AAYD,eAAO,MAAM,aAAa,WAChB,kBAAkB,mBACT,MAAM,SAChB,MAAM,OACR,MAAM,SACJ,MAAM,EAAE,qBAEd,gBA+CF,CAAC;AAEF,QAAA,MAAM,mBAAmB,sIAYtB,wBAAwB,KAAG,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileMetricsGraph/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAGlD,OAAO,EAAW,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAY,MAAM,eAAe,CAAC;AACjG,OAAO,EACL,aAAa,EAKd,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAK5D,UAAU,6BAA6B;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD,eAAO,MAAM,wBAAwB,gBAAe,6BAA6B,KAAG,GAAG,CAAC,OAMvF,CAAC;AAEF,UAAU,wBAAwB;IAChC,WAAW,EAAE,kBAAkB,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,eAAe,EAAE,CACf,MAAM,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,KACvE,IAAI,CAAC;IACV,YAAY,EAAE,CACZ,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,KAAK,EAAE,EACf,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,KACb,IAAI,CAAC;IACV,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;CACxB;AAYD,eAAO,MAAM,aAAa,WAChB,kBAAkB,mBACT,MAAM,SAChB,MAAM,OACR,MAAM,SACJ,MAAM,EAAE,qBAEd,gBA+CF,CAAC;AAEF,QAAA,MAAM,mBAAmB,sIAYtB,wBAAwB,KAAG,GAAG,CAAC,OAsFjC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -108,6 +108,6 @@ const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to,
|
|
|
108
108
|
}
|
|
109
109
|
return (_jsx(AnimatePresence, { children: _jsx(motion.div, { className: "h-full w-full relative", initial: { display: 'none', opacity: 0 }, animate: { display: 'block', opacity: 1 }, transition: { duration: 0.5 }, children: loading ? (_jsx(MetricsGraphSkeleton, { heightStyle: heightStyle, isDarkMode: isDarkMode })) : dataAvailable ? (_jsx(MetricsGraph, { data: series, from: from, to: to, profile: profile, setTimeRange: setTimeRange, onSampleClick: (timestamp, _value, labels, duration) => {
|
|
110
110
|
onPointClick(timestamp, labels, queryExpression, duration);
|
|
111
|
-
}, addLabelMatcher: addLabelMatcher, sampleUnit: sampleUnit, height: height, width: width, margin: margin })) : (_jsx(ProfileMetricsEmptyState, { message: "No data found. Try a different query." })) }, "metrics-graph-loaded") }));
|
|
111
|
+
}, addLabelMatcher: addLabelMatcher, sampleUnit: sampleUnit, height: height, width: width, margin: margin, sumBy: sumBy })) : (_jsx(ProfileMetricsEmptyState, { message: "No data found. Try a different query." })) }, "metrics-graph-loaded") }));
|
|
112
112
|
};
|
|
113
113
|
export default ProfileMetricsGraph;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
1
2
|
import { RpcError } from '@protobuf-ts/runtime-rpc';
|
|
2
3
|
import { ProfileTypesResponse, QueryServiceClient } from '@parca/client';
|
|
3
4
|
import { type NavigateFunction } from '@parca/utilities';
|
|
@@ -22,6 +23,9 @@ interface ProfileSelectorProps {
|
|
|
22
23
|
comparing: boolean;
|
|
23
24
|
navigateTo: NavigateFunction;
|
|
24
25
|
suffix?: string;
|
|
26
|
+
showMetricsGraph: boolean;
|
|
27
|
+
displayHideMetricsGraphButton: boolean;
|
|
28
|
+
setDisplayHideMetricsGraphButton: Dispatch<SetStateAction<boolean>>;
|
|
25
29
|
}
|
|
26
30
|
export interface IProfileTypesResult {
|
|
27
31
|
loading: boolean;
|
|
@@ -29,6 +33,6 @@ export interface IProfileTypesResult {
|
|
|
29
33
|
error?: RpcError;
|
|
30
34
|
}
|
|
31
35
|
export declare const useProfileTypes: (client: QueryServiceClient) => IProfileTypesResult;
|
|
32
|
-
declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, }: ProfileSelectorProps) => JSX.Element;
|
|
36
|
+
declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, showMetricsGraph, displayHideMetricsGraphButton, setDisplayHideMetricsGraphButton, }: ProfileSelectorProps) => JSX.Element;
|
|
33
37
|
export default ProfileSelector;
|
|
34
38
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAaA,OAAc,EAAC,QAAQ,EAAE,cAAc,EAAuC,MAAM,OAAO,CAAC;AAG5F,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAIlD,OAAO,EAAQ,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAa9E,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAU5D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,6BAA6B,EAAE,OAAO,CAAC;IACvC,gCAAgC,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;CACrE;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,WAAY,kBAAkB,KAAG,mBAkB5D,CAAC;AAEF,QAAA,MAAM,eAAe,gOAalB,oBAAoB,KAAG,GAAG,CAAC,OA8Z7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -13,6 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
15
15
|
import { Switch } from '@headlessui/react';
|
|
16
|
+
import cx from 'classnames';
|
|
16
17
|
import Select from 'react-select';
|
|
17
18
|
import { Button, ButtonGroup, DateTimeRange, DateTimeRangePicker, IconButton, useGrpcMetadata, useParcaContext, useURLState, } from '@parca/components';
|
|
18
19
|
import { CloseIcon } from '@parca/icons';
|
|
@@ -43,7 +44,7 @@ export const useProfileTypes = (client) => {
|
|
|
43
44
|
}, [client, metadata, loading]);
|
|
44
45
|
return { loading, data: result, error };
|
|
45
46
|
};
|
|
46
|
-
const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, }) => {
|
|
47
|
+
const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, showMetricsGraph, displayHideMetricsGraphButton, setDisplayHideMetricsGraphButton, }) => {
|
|
47
48
|
const { loading: profileTypesLoading, data: profileTypesData, error, } = useProfileTypes(queryClient);
|
|
48
49
|
const { heightStyle } = useMetricsGraphDimensions(comparing);
|
|
49
50
|
const { viewComponent } = useParcaContext();
|
|
@@ -173,20 +174,21 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
|
|
|
173
174
|
queryExpressionString === '' ||
|
|
174
175
|
queryExpressionString === '{}';
|
|
175
176
|
const queryBrowserRef = useRef(null);
|
|
176
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex
|
|
177
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex", children: [_jsxs("div", { className: "flex w-full flex-wrap items-end gap-2", children: [_jsxs("div", { children: [_jsx("label", { className: "text-xs", children: "Profile type" }), _jsx(ProfileTypeSelector, { profileTypesData: profileTypesData, loading: profileTypesLoading, selectedKey: selectedProfileName, onSelection: setProfileName, error: error, disabled: viewComponent?.disableProfileTypesDropdown })] }), _jsxs("div", { className: "w-full flex-1 flex flex-col gap-1", ref: queryBrowserRef, children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("label", { className: "text-xs", children: "Query" }), viewComponent?.disableExplorativeQuerying === true ? null : (_jsxs(_Fragment, { children: [_jsxs(Switch, { checked: advancedModeForQueryBrowser, onChange: () => {
|
|
177
178
|
setAdvancedModeForQueryBrowser(!advancedModeForQueryBrowser);
|
|
178
179
|
setQueryBrowserMode(advancedModeForQueryBrowser ? 'simple' : 'advanced');
|
|
179
180
|
}, className: `${advancedModeForQueryBrowser
|
|
180
181
|
? 'bg-indigo-600'
|
|
181
|
-
: 'bg-gray-400 dark:bg-gray-
|
|
182
|
+
: 'bg-gray-400 dark:bg-gray-800'}
|
|
182
183
|
relative inline-flex h-[20px] w-[44px] shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75`, children: [_jsx("span", { className: "sr-only", children: "Use setting" }), _jsx("span", { "aria-hidden": "true", className: `${advancedModeForQueryBrowser ? 'translate-x-6' : 'translate-x-0'}
|
|
183
184
|
pointer-events-none inline-block h-[16px] w-[16px] transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out` })] }), _jsx("label", { className: "text-xs", children: "Advanced Mode" })] }))] }), (query.matchers.length > 0 || query.inputMatcherString.length > 0) &&
|
|
184
185
|
viewComponent !== undefined && _jsx("div", { children: viewComponent?.createViewComponent })] }), viewComponent?.disableExplorativeQuerying === true &&
|
|
185
186
|
viewComponent?.labelnames !== undefined &&
|
|
186
|
-
viewComponent?.labelnames.length >= 1 ? (_jsx(_Fragment, { children: _jsx(ViewMatchers, { labelNames: viewComponent.labelnames, setMatchersString: setMatchersString, profileType: selectedProfileName, runQuery: setQueryExpression, currentQuery: query, queryClient: queryClient }) })) : (_jsx(_Fragment, { children: advancedModeForQueryBrowser ? (_jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName })) : (_jsx(SimpleMatchers, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName, queryBrowserRef: queryBrowserRef })) }))] }), _jsxs("div", {
|
|
187
|
+
viewComponent?.labelnames.length >= 1 ? (_jsx(_Fragment, { children: _jsx(ViewMatchers, { labelNames: viewComponent.labelnames, setMatchersString: setMatchersString, profileType: selectedProfileName, runQuery: setQueryExpression, currentQuery: query, queryClient: queryClient }) })) : (_jsx(_Fragment, { children: advancedModeForQueryBrowser ? (_jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName })) : (_jsx(SimpleMatchers, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName, queryBrowserRef: queryBrowserRef })) }))] }), _jsxs("div", { children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", children: "Sum by" }) }), _jsx(Select, { defaultValue: [], isMulti: true, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-full max-w-80", classNamePrefix: "parca-select", value: (sumBySelection ?? []).map(sumBy => ({ label: sumBy, value: sumBy })), onChange: selectedOptions => {
|
|
187
188
|
setUserSumBySelection(selectedOptions.map(option => option.value));
|
|
188
189
|
}, placeholder: "Labels...", styles: {
|
|
189
190
|
indicatorSeparator: () => ({ display: 'none' }),
|
|
191
|
+
menu: provided => ({ ...provided, width: 'max-content' }),
|
|
190
192
|
}, isDisabled: !profileType.delta, ref: sumByRef, onKeyDown: e => {
|
|
191
193
|
const currentRef = sumByRef.current;
|
|
192
194
|
if (currentRef == null) {
|
|
@@ -206,39 +208,41 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
|
|
|
206
208
|
} })] }), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection }), _jsx(ButtonGroup, { children: _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
|
|
207
209
|
e.preventDefault();
|
|
208
210
|
setQueryExpression(true);
|
|
209
|
-
}, id: "h-matcher-search-button", children: "Search" }) })] }), _jsx("div", { children: comparing && _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) })] }),
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
mergedProfileParams = {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
selectQuery({
|
|
221
|
-
expression: queryExpressionString,
|
|
222
|
-
from,
|
|
223
|
-
to,
|
|
224
|
-
timeSelection: range.getRangeKey(),
|
|
225
|
-
...mergedProfileParams,
|
|
226
|
-
});
|
|
227
|
-
}, addLabelMatcher: addLabelMatcher, onPointClick: (timestamp, labels, queryExpression, duration) => {
|
|
228
|
-
// TODO: Pass the query object via click rather than queryExpression
|
|
229
|
-
let query = Query.parse(queryExpression);
|
|
230
|
-
labels.forEach(l => {
|
|
231
|
-
const [newQuery, updated] = query.setMatcher(l.name, l.value);
|
|
232
|
-
if (updated) {
|
|
233
|
-
query = newQuery;
|
|
211
|
+
}, id: "h-matcher-search-button", children: "Search" }) })] }), _jsx("div", { children: comparing && _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) })] }), _jsxs("div", { className: cx('relative', {
|
|
212
|
+
'py-4': !showMetricsGraph,
|
|
213
|
+
}), children: [_jsxs("button", { onClick: () => setDisplayHideMetricsGraphButton(!showMetricsGraph), className: cx('hidden z-10 px-3 py-1 text-sm font-medium text-gray-700 dark:text-gray-200 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-gray-900', displayHideMetricsGraphButton && showMetricsGraph && 'absolute right-0 bottom-3 !flex', !showMetricsGraph && 'relative !flex ml-auto'), children: [showMetricsGraph ? 'Hide' : 'Show', " Metrics Graph"] }), showMetricsGraph ? (_jsx("div", { style: { height: heightStyle }, children: querySelection.expression !== undefined &&
|
|
214
|
+
querySelection.expression.length > 0 &&
|
|
215
|
+
querySelection.from !== undefined &&
|
|
216
|
+
querySelection.to !== undefined ? (_jsx("div", { children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? defaultSumBy ?? [], sumByLoading: defaultSumByLoading, setTimeRange: (range) => {
|
|
217
|
+
const from = range.getFromMs();
|
|
218
|
+
const to = range.getToMs();
|
|
219
|
+
let mergedProfileParams = {};
|
|
220
|
+
if (query.profileType().delta) {
|
|
221
|
+
mergedProfileParams = { mergeFrom: from, mergeTo: to };
|
|
234
222
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
223
|
+
setTimeRangeSelection(range);
|
|
224
|
+
selectQuery({
|
|
225
|
+
expression: queryExpressionString,
|
|
226
|
+
from,
|
|
227
|
+
to,
|
|
228
|
+
timeSelection: range.getRangeKey(),
|
|
229
|
+
...mergedProfileParams,
|
|
230
|
+
});
|
|
231
|
+
}, addLabelMatcher: addLabelMatcher, onPointClick: (timestamp, labels, queryExpression, duration) => {
|
|
232
|
+
// TODO: Pass the query object via click rather than queryExpression
|
|
233
|
+
let query = Query.parse(queryExpression);
|
|
234
|
+
labels.forEach(l => {
|
|
235
|
+
const [newQuery, updated] = query.setMatcher(l.name, l.value);
|
|
236
|
+
if (updated) {
|
|
237
|
+
query = newQuery;
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
const durationInMilliseconds = duration / 1000000; // duration is in nanoseconds
|
|
241
|
+
const mergeFrom = timestamp;
|
|
242
|
+
const mergeTo = query.profileType().delta
|
|
243
|
+
? mergeFrom + durationInMilliseconds
|
|
244
|
+
: mergeFrom;
|
|
245
|
+
selectProfile(new MergedProfileSelection(mergeFrom, mergeTo, query));
|
|
246
|
+
} }) })) : (_jsx(_Fragment, { children: profileSelection == null ? (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsEmptyState, { message: `Please select a profile type and click "Search" to begin.` }) })) : null })) })) : null] })] }));
|
|
243
247
|
};
|
|
244
248
|
export default ProfileSelector;
|
package/dist/ProfileSource.js
CHANGED
|
@@ -109,7 +109,7 @@ export class ProfileDiffSource {
|
|
|
109
109
|
const aDesc = this.a.toString();
|
|
110
110
|
const bDesc = this.b.toString();
|
|
111
111
|
if (aDesc === bDesc) {
|
|
112
|
-
return '
|
|
112
|
+
return 'Profile comparison';
|
|
113
113
|
}
|
|
114
114
|
return `${this.a.toString()} compared with ${this.b.toString()}`;
|
|
115
115
|
}
|
|
@@ -170,6 +170,6 @@ export class MergedProfileSource {
|
|
|
170
170
|
milliseconds: this.mergeTo - this.mergeFrom,
|
|
171
171
|
})} from ${formatDate(this.mergeFrom, timeFormat(timezone), timezone)} to ${formatDate(this.mergeTo, timeFormat(timezone), timezone)}`;
|
|
172
172
|
}
|
|
173
|
-
return `
|
|
173
|
+
return `Merged profiles${queryPart}${timePart}`;
|
|
174
174
|
}
|
|
175
175
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileView/index.tsx"],"names":[],"mappings":"AA0BA,OAAO,EACL,SAAS,IAAI,aAAa,EAC1B,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,MAAM,EACN,UAAU,EACV,GAAG,EACJ,MAAM,eAAe,CAAC;AAUvB,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAO/C,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,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,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,UAAU,aAAa;IACrB,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,UAAU,UAAU;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,GAAG,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,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,CAAC,EAAE,aAAa,CAAC;IAC9B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAWD,eAAO,MAAM,WAAW,0JAYrB,gBAAgB,KAAG,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileView/index.tsx"],"names":[],"mappings":"AA0BA,OAAO,EACL,SAAS,IAAI,aAAa,EAC1B,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,MAAM,EACN,UAAU,EACV,GAAG,EACJ,MAAM,eAAe,CAAC;AAUvB,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAO/C,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,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,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,UAAU,aAAa;IACrB,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,UAAU,UAAU;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,GAAG,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,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,CAAC,EAAE,aAAa,CAAC;IAC9B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAWD,eAAO,MAAM,WAAW,0JAYrB,gBAAgB,KAAG,GAAG,CAAC,OA4UzB,CAAC"}
|
|
@@ -153,17 +153,19 @@ export const ProfileView = ({ total, filtered, flamegraphData, topTableData, cal
|
|
|
153
153
|
? setGroupBy(groupBy.filter(v => v !== key)) // remove
|
|
154
154
|
: setGroupBy([...groupBy, key]); // add
|
|
155
155
|
}, [groupBy, setGroupBy]);
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
const showDivider = hasProfileSource &&
|
|
157
|
+
(profileViewExternalMainActions === null || profileViewExternalMainActions === undefined);
|
|
158
|
+
return (_jsx(KeyDownProvider, { children: _jsxs(ProfileViewContextProvider, { value: { profileSource, compareMode }, children: [showDivider ? (_jsx(_Fragment, { children: _jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 h-[1px] w-full pb-4" }) })) : null, _jsx("div", { className: cx('flex w-full', hasProfileSource || profileViewExternalMainActions != null
|
|
159
|
+
? 'justify-start'
|
|
158
160
|
: 'justify-end', {
|
|
159
|
-
'items-end': !hasProfileSource && profileViewExternalMainActions != null,
|
|
160
|
-
'items-center': hasProfileSource,
|
|
161
|
-
}), children: _jsxs("div", { children: [hasProfileSource && (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "text-
|
|
161
|
+
'items-end mb-4': !hasProfileSource && profileViewExternalMainActions != null,
|
|
162
|
+
'items-center mb-2': hasProfileSource,
|
|
163
|
+
}), children: _jsxs("div", { children: [hasProfileSource && (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "text-xs font-medium", children: headerParts.length > 0 ? headerParts[0].replace(/"/g, '') : '' }), _jsx("div", { className: "text-xs font-medium", children: headerParts.length > 1
|
|
162
164
|
? headerParts[headerParts.length - 1].replace(/"/g, '')
|
|
163
165
|
: '' })] })), profileViewExternalMainActions != null ? profileViewExternalMainActions : null] }) }), _jsx(VisualisationToolbar, { groupBy: groupBy, toggleGroupBy: toggleGroupBy, hasProfileSource: hasProfileSource, pprofdownloading: pprofDownloading, profileSource: profileSource, queryClient: queryClient, onDownloadPProf: onDownloadPProf, isMultiPanelView: isMultiPanelView, dashboardItems: dashboardItems, curPath: curPath, setNewCurPath: setNewCurPath, profileType: profileSource?.ProfileType(), total: total, filtered: filtered, currentSearchString: currentSearchString, setSearchString: setSearchString }), _jsx("div", { className: "w-full", ref: ref, children: _jsx(DragDropContext, { onDragEnd: onDragEnd, children: _jsx(Droppable, { droppableId: "droppable", direction: "horizontal", children: provided => (_jsx("div", { ref: provided.innerRef, className: cx('grid w-full gap-2', isMultiPanelView ? 'grid-cols-2' : 'grid-cols-1'), ...provided.droppableProps, children: dashboardItems.map((dashboardItem, index) => {
|
|
164
|
-
return (_jsx(Draggable, { draggableId: dashboardItem, index: index, isDragDisabled: !isMultiPanelView, children: (provided, snapshot) => (_createElement("div", { ref: provided.innerRef, ...provided.draggableProps, key: dashboardItem, className: cx('w-full
|
|
166
|
+
return (_jsx(Draggable, { draggableId: dashboardItem, index: index, isDragDisabled: !isMultiPanelView, children: (provided, snapshot) => (_createElement("div", { ref: provided.innerRef, ...provided.draggableProps, key: dashboardItem, className: cx('w-full min-h-96', snapshot.isDragging
|
|
165
167
|
? 'bg-gray-200 dark:bg-gray-500'
|
|
166
|
-
: 'bg-white dark:bg-gray-
|
|
168
|
+
: 'bg-white dark:bg-gray-900') },
|
|
167
169
|
_jsx(VisualizationPanel, { handleClosePanel: handleClosePanel, isMultiPanelView: isMultiPanelView, dashboardItem: dashboardItem, getDashboardItemByType: getDashboardItemByType, dragHandleProps: provided.dragHandleProps, index: index }))) }, dashboardItem));
|
|
168
170
|
}) })) }) }) })] }) }));
|
|
169
171
|
};
|
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}}.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-inset-2{bottom:-.5rem;left:-.5rem;right:-.5rem;top:-.5rem}.inset-y-0{bottom:0;top:0}.left-\[25px\]{left:25px}.left-0{left:0}.top-\[-46px\]{top:-46px}.right-0{right:0}.top-0{top:0}.top-\[-5px\]{top:-5px}.top-\[-1px\]{top:-1px}.left-\[18px\]{left:18px}.bottom-0{bottom:0}.left-full{left:100%}.z-50{z-index:50}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.m-auto{margin:auto}.m-2{margin:.5rem}.mx-auto{margin-left:auto;margin-right:auto}.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}.my-4{margin-bottom:1rem;margin-top:1rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mb-2{margin-bottom:.5rem}.mb-0\.5{margin-bottom:.125rem}.mt-1\.5{margin-top:.375rem}.mb-0{margin-bottom:0}.mb-4{margin-bottom:1rem}.mr-2{margin-right:.5rem}.ml-3{margin-left:.75rem}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.mr-1{margin-right:.25rem}.mb-1{margin-bottom:.25rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.mt-0{margin-top:0}.ml-1{margin-left:.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-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-\[38px\]{height:38px}.h-auto{height:auto}.h-full{height:100%}.h-1{height:.25rem}.h-\[20px\]{height:20px}.h-\[16px\]{height:16px}.h-\[45px\]{height:45px}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-4{height:1rem}.h-\[700px\]{height:700px}.h-\[80vh\]{height:80vh}.h-\[14px\]{height:14px}.max-h-\[400px\]{max-height:400px}.max-h-\[50vh\]{max-height:50vh}.max-h-\[300px\]{max-height:300px}.min-h-52{min-height:13rem}.min-h-48{min-height:12rem}.min-h-96{min-height:24rem}.min-h-\[700px\]{min-height:700px}.w-full{width:100%}.w-auto{width:auto}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-\[500px\]{width:500px}.w-40{width:10rem}.w-\[44px\]{width:44px}.w-\[16px\]{width:16px}.w-max{width:-moz-max-content;width:max-content}.w-\[270px\]{width:270px}.w-5{width:1.25rem}.w-3{width:.75rem}.w-7{width:1.75rem}.w-9{width:2.25rem}.w-11{width:2.75rem}.w-\[52px\]{width:52px}.w-\[68px\]{width:68px}.w-\[76px\]{width:76px}.w-\[84px\]{width:84px}.w-\[92px\]{width:92px}.w-\[100px\]{width:100px}.w-\[108px\]{width:108px}.w-\[116px\]{width:116px}.w-4{width:1rem}.w-\[18px\]{width:18px}.w-8{width:2rem}.w-44{width:11rem}.w-\[460px\]{width:460px}.w-\[19\.25\%\]{width:19.25%}.w-11\/12{width:91.666667%}.w-1\/12{width:8.333333%}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.w-48{width:12rem}.w-\[246px\]{width:246px}.w-\[14px\]{width:14px}.w-56{width:14rem}.min-w-\[300px\]{min-width:300px}.min-w-\[400px\]{min-width:400px}.max-w-\[500px\]{max-width:500px}.max-w-80{max-width:20rem}.max-w-48{max-width:12rem}.max-w-\[300px\]{max-width:300px}.max-w-\[400px\]{max-width:400px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.flex-grow-0{flex-grow:0}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.origin-top-left{transform-origin:top left}.origin-bottom-left{transform-origin:bottom left}.origin-top-right{transform-origin:top right}.translate-x-6{--tw-translate-x:1.5rem}.translate-x-0,.translate-x-6{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-x-0{--tw-translate-x:0px}.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}.-rotate-90{--tw-rotate:-90deg}.-rotate-90,.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}.cursor-auto{cursor:auto}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.content-start{align-content:flex-start}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.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-4{gap:1rem}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.25rem*var(--tw-space-y-reverse));margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-clip{overflow:clip}.overflow-scroll{overflow:scroll}.overflow-x-hidden{overflow-x:hidden}.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-full{border-radius:9999px}.rounded-none{border-radius:0}.rounded-l-md{border-bottom-left-radius:.375rem;border-top-left-radius:.375rem}.rounded-l-none{border-bottom-left-radius:0;border-top-left-radius:0}.rounded-r-none{border-bottom-right-radius:0;border-top-right-radius:0}.rounded-r-md{border-bottom-right-radius:.375rem;border-top-right-radius:.375rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.rounded-tr-none{border-top-right-radius:0}.rounded-br-none{border-bottom-right-radius:0}.rounded-tl-none{border-top-left-radius:0}.rounded-bl-none{border-bottom-left-radius:0}.border{border-width:1px}.border-2{border-width:2px}.border-x{border-left-width:1px;border-right-width:1px}.border-y{border-top-width:1px}.border-b,.border-y{border-bottom-width:1px}.border-r{border-right-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-none{border-style:none}.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))}.border-transparent{border-color:transparent}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.border-r-gray-200{--tw-border-opacity:1;border-right-color:rgb(229 231 235/var(--tw-border-opacity))}.border-l-amber-900{--tw-border-opacity:1;border-left-color:rgb(120 53 15/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-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/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-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-yellow-200{--tw-bg-opacity:1;background-color:rgb(254 240 138/var(--tw-bg-opacity))}.bg-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.bg-indigo-50{--tw-bg-opacity:1;background-color:rgb(238 242 255/var(--tw-bg-opacity))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/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}.bg-indigo-400{--tw-bg-opacity:1;background-color:rgb(129 140 248/var(--tw-bg-opacity))}.bg-opacity-90{--tw-bg-opacity:0.9}.fill-transparent{fill:transparent}.fill-current{fill:currentColor}.stroke-gray-300{stroke:#d1d5db}.stroke-white{stroke:#fff}.stroke-\[3\]{stroke-width:3}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-1\.5{padding:.375rem}.p-1{padding:.25rem}.p-\[6px\]{padding:6px}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-4{padding-bottom:1rem;padding-top:1rem}.px-4{padding-left:1rem;padding-right:1rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-0{padding-bottom:0;padding-top:0}.px-1{padding-left:.25rem;padding-right:.25rem}.px-8{padding-left:2rem;padding-right:2rem}.pr-0{padding-right:0}.pt-2{padding-top:.5rem}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-2{padding-bottom:.5rem}.pb-\[10px\]{padding-bottom:10px}.pt-0{padding-top:0}.pr-4{padding-right:1rem}.pl-1{padding-left:.25rem}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pl-5{padding-left:1.25rem}.pr-\[1\.7rem\]{padding-right:1.7rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.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}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.leading-6{line-height:1.5rem}.leading-5{line-height:1.25rem}.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-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.opacity-100{opacity:1}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-90{opacity:.9}.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)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px 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)}.ring-0,.ring-1{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-0{--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(var(--tw-ring-offset-width)) var(--tw-ring-color)}.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)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;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-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[stroke-dasharray\:6\2c 4\]{stroke-dasharray:6,4}.\[stroke-linecap\:round\]{stroke-linecap:round}.\[stroke-linejoin\:round\]{stroke-linejoin:round}.hover\:whitespace-normal:hover{white-space:normal}.hover\:bg-indigo-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-\[\#62626212\]:hover{background-color:#62626212}.hover\:bg-indigo-200:hover{--tw-bg-opacity:1;background-color:rgb(199 210 254/var(--tw-bg-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:border-indigo-500:focus{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--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)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--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(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-0:focus{--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(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)}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.focus\:ring-indigo-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(79 70 229/var(--tw-ring-opacity))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus-visible\:ring-2:focus-visible{--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(2px + 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)}.focus-visible\:ring-white\/75:focus-visible{--tw-ring-color:hsla(0,0%,100%,.75)}.focus-visible\:ring-white:focus-visible{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}.focus-visible\:ring-opacity-75:focus-visible{--tw-ring-opacity:0.75}.group:hover .group-hover\:flex{display:flex}[class~=theme-dark] .dark\:border{border-width:1px}[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\:border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-r-gray-700{--tw-border-opacity:1;border-right-color:rgb(55 65 81/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\:bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-indigo-700{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-opacity-80{--tw-bg-opacity:0.8}[class~=theme-dark] .dark\:stroke-gray-500{stroke:#6b7280}[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-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/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-indigo-500{--tw-text-opacity:1;color:rgb(99 102 241/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}[class~=theme-dark] .dark\:ring-white{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}[class~=theme-dark] .dark\:ring-opacity-20{--tw-ring-opacity:0.2}[class~=theme-dark] .dark\:hover\:bg-\[\#ffffff12\]:hover{background-color:#ffffff12}[class~=theme-dark] .dark\:hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}[class~=theme-dark] .hover\:dark\:text-gray-100:hover{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:block{display:block}.md\:flex-row{flex-direction:row}}
|
|
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}}.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-inset-2{bottom:-.5rem;left:-.5rem;right:-.5rem;top:-.5rem}.inset-y-0{bottom:0;top:0}.left-\[25px\]{left:25px}.left-0{left:0}.top-\[-46px\]{top:-46px}.right-0{right:0}.top-0{top:0}.bottom-3{bottom:.75rem}.top-\[-5px\]{top:-5px}.top-\[-1px\]{top:-1px}.left-\[18px\]{left:18px}.bottom-0{bottom:0}.left-full{left:100%}.z-50{z-index:50}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.m-auto{margin:auto}.m-2{margin:.5rem}.mx-auto{margin-left:auto;margin-right:auto}.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}.my-4{margin-bottom:1rem;margin-top:1rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mb-2{margin-bottom:.5rem}.mb-0\.5{margin-bottom:.125rem}.mt-1\.5{margin-top:.375rem}.mb-0{margin-bottom:0}.ml-auto{margin-left:auto}.mb-4{margin-bottom:1rem}.mr-2{margin-right:.5rem}.ml-3{margin-left:.75rem}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.mr-1{margin-right:.25rem}.mb-1{margin-bottom:.25rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.mt-0{margin-top:0}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.\!flex{display:flex!important}.inline-flex{display:inline-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-\[38px\]{height:38px}.h-auto{height:auto}.h-full{height:100%}.h-1{height:.25rem}.h-\[20px\]{height:20px}.h-\[16px\]{height:16px}.h-\[1px\]{height:1px}.h-\[45px\]{height:45px}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-4{height:1rem}.h-\[700px\]{height:700px}.h-\[80vh\]{height:80vh}.h-\[14px\]{height:14px}.max-h-\[400px\]{max-height:400px}.max-h-\[50vh\]{max-height:50vh}.max-h-\[300px\]{max-height:300px}.min-h-52{min-height:13rem}.min-h-48{min-height:12rem}.min-h-96{min-height:24rem}.min-h-\[700px\]{min-height:700px}.w-full{width:100%}.w-auto{width:auto}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-\[500px\]{width:500px}.w-40{width:10rem}.w-\[44px\]{width:44px}.w-\[16px\]{width:16px}.w-max{width:-moz-max-content;width:max-content}.w-\[270px\]{width:270px}.w-5{width:1.25rem}.w-3{width:.75rem}.w-7{width:1.75rem}.w-9{width:2.25rem}.w-11{width:2.75rem}.w-\[52px\]{width:52px}.w-\[68px\]{width:68px}.w-\[76px\]{width:76px}.w-\[84px\]{width:84px}.w-\[92px\]{width:92px}.w-\[100px\]{width:100px}.w-\[108px\]{width:108px}.w-\[116px\]{width:116px}.w-4{width:1rem}.w-\[18px\]{width:18px}.w-8{width:2rem}.w-44{width:11rem}.w-\[460px\]{width:460px}.w-\[19\.25\%\]{width:19.25%}.w-11\/12{width:91.666667%}.w-1\/12{width:8.333333%}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.w-48{width:12rem}.w-\[246px\]{width:246px}.w-\[14px\]{width:14px}.w-56{width:14rem}.min-w-\[300px\]{min-width:300px}.min-w-\[400px\]{min-width:400px}.max-w-\[500px\]{max-width:500px}.max-w-80{max-width:20rem}.max-w-48{max-width:12rem}.max-w-\[300px\]{max-width:300px}.max-w-\[400px\]{max-width:400px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.flex-grow-0{flex-grow:0}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.origin-top-left{transform-origin:top left}.origin-bottom-left{transform-origin:bottom left}.origin-top-right{transform-origin:top right}.translate-x-6{--tw-translate-x:1.5rem}.translate-x-0,.translate-x-6{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-x-0{--tw-translate-x:0px}.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}.-rotate-90{--tw-rotate:-90deg}.-rotate-90,.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}.cursor-auto{cursor:auto}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.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-4{gap:1rem}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.25rem*var(--tw-space-y-reverse));margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-clip{overflow:clip}.overflow-scroll{overflow:scroll}.overflow-x-hidden{overflow-x:hidden}.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-full{border-radius:9999px}.rounded-none{border-radius:0}.rounded-l-md{border-bottom-left-radius:.375rem;border-top-left-radius:.375rem}.rounded-l-none{border-bottom-left-radius:0;border-top-left-radius:0}.rounded-r-none{border-bottom-right-radius:0;border-top-right-radius:0}.rounded-r-md{border-bottom-right-radius:.375rem;border-top-right-radius:.375rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.rounded-tr-none{border-top-right-radius:0}.rounded-br-none{border-bottom-right-radius:0}.rounded-tl-none{border-top-left-radius:0}.rounded-bl-none{border-bottom-left-radius:0}.border{border-width:1px}.border-2{border-width:2px}.border-x{border-left-width:1px;border-right-width:1px}.border-y{border-bottom-width:1px;border-top-width:1px}.border-t{border-top-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-l{border-left-width:1px}.border-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-none{border-style:none}.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))}.border-transparent{border-color:transparent}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.border-r-gray-200{--tw-border-opacity:1;border-right-color:rgb(229 231 235/var(--tw-border-opacity))}.border-l-amber-900{--tw-border-opacity:1;border-left-color:rgb(120 53 15/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-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/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-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity))}.bg-yellow-200{--tw-bg-opacity:1;background-color:rgb(254 240 138/var(--tw-bg-opacity))}.bg-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.bg-indigo-50{--tw-bg-opacity:1;background-color:rgb(238 242 255/var(--tw-bg-opacity))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/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}.bg-indigo-400{--tw-bg-opacity:1;background-color:rgb(129 140 248/var(--tw-bg-opacity))}.bg-opacity-90{--tw-bg-opacity:0.9}.fill-transparent{fill:transparent}.fill-current{fill:currentColor}.stroke-gray-300{stroke:#d1d5db}.stroke-white{stroke:#fff}.stroke-\[3\]{stroke-width:3}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-1\.5{padding:.375rem}.p-1{padding:.25rem}.p-\[6px\]{padding:6px}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-0{padding-bottom:0;padding-top:0}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-8{padding-left:2rem;padding-right:2rem}.pr-0{padding-right:0}.pt-2{padding-top:.5rem}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pb-4{padding-bottom:1rem}.pb-2{padding-bottom:.5rem}.pb-\[10px\]{padding-bottom:10px}.pt-0{padding-top:0}.pr-4{padding-right:1rem}.pl-1{padding-left:.25rem}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pl-5{padding-left:1.25rem}.pr-\[1\.7rem\]{padding-right:1.7rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.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}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.leading-6{line-height:1.5rem}.leading-5{line-height:1.25rem}.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-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.opacity-100{opacity:1}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-90{opacity:.9}.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)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px 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)}.ring-0,.ring-1{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-0{--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(var(--tw-ring-offset-width)) var(--tw-ring-color)}.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)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;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-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[stroke-dasharray\:6\2c 4\]{stroke-dasharray:6,4}.\[stroke-linecap\:round\]{stroke-linecap:round}.\[stroke-linejoin\:round\]{stroke-linejoin:round}.hover\:whitespace-normal:hover{white-space:normal}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-indigo-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:bg-\[\#62626212\]:hover{background-color:#62626212}.hover\:bg-indigo-200:hover{--tw-bg-opacity:1;background-color:rgb(199 210 254/var(--tw-bg-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:border-indigo-500:focus{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--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)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--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(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-0:focus{--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(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)}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.focus\:ring-indigo-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(79 70 229/var(--tw-ring-opacity))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus-visible\:ring-2:focus-visible{--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(2px + 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)}.focus-visible\:ring-white\/75:focus-visible{--tw-ring-color:hsla(0,0%,100%,.75)}.focus-visible\:ring-white:focus-visible{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}.focus-visible\:ring-opacity-75:focus-visible{--tw-ring-opacity:0.75}.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\:border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-r-gray-700{--tw-border-opacity:1;border-right-color:rgb(55 65 81/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\:bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-indigo-700{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-opacity-80{--tw-bg-opacity:0.8}[class~=theme-dark] .dark\:stroke-gray-500{stroke:#6b7280}[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-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/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-indigo-500{--tw-text-opacity:1;color:rgb(99 102 241/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}[class~=theme-dark] .dark\:ring-white{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}[class~=theme-dark] .dark\:ring-opacity-20{--tw-ring-opacity:0.2}[class~=theme-dark] .dark\:hover\:bg-\[\#ffffff12\]:hover{background-color:#ffffff12}[class~=theme-dark] .dark\:hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}[class~=theme-dark] .hover\:dark\:text-gray-100:hover{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:block{display:block}.md\:flex-row{flex-direction:row}}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.431",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@headlessui/react": "^1.7.19",
|
|
7
7
|
"@iconify/react": "^4.0.0",
|
|
8
8
|
"@parca/client": "0.16.124",
|
|
9
|
-
"@parca/components": "0.16.
|
|
9
|
+
"@parca/components": "0.16.304",
|
|
10
10
|
"@parca/dynamicsize": "0.16.65",
|
|
11
|
-
"@parca/hooks": "0.0.
|
|
11
|
+
"@parca/hooks": "0.0.72",
|
|
12
12
|
"@parca/icons": "0.16.70",
|
|
13
13
|
"@parca/parser": "0.16.77",
|
|
14
14
|
"@parca/store": "0.16.159",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"access": "public",
|
|
74
74
|
"registry": "https://registry.npmjs.org/"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "cbba8df3d109f9dee1245ea4f16ed5ebacdfe6c6"
|
|
77
77
|
}
|
|
@@ -49,6 +49,7 @@ interface Props {
|
|
|
49
49
|
width?: number;
|
|
50
50
|
height?: number;
|
|
51
51
|
margin?: number;
|
|
52
|
+
sumBy?: string[];
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export interface HighlightedSeries {
|
|
@@ -80,6 +81,7 @@ const MetricsGraph = ({
|
|
|
80
81
|
width = 0,
|
|
81
82
|
height = 0,
|
|
82
83
|
margin = 0,
|
|
84
|
+
sumBy,
|
|
83
85
|
}: Props): JSX.Element => {
|
|
84
86
|
const [isInfoPanelOpen, setIsInfoPanelOpen] = useState<boolean>(false);
|
|
85
87
|
return (
|
|
@@ -102,6 +104,7 @@ const MetricsGraph = ({
|
|
|
102
104
|
width={width}
|
|
103
105
|
height={height}
|
|
104
106
|
margin={margin}
|
|
107
|
+
sumBy={sumBy}
|
|
105
108
|
/>
|
|
106
109
|
</div>
|
|
107
110
|
);
|
|
@@ -131,6 +134,7 @@ export const RawMetricsGraph = ({
|
|
|
131
134
|
height = 50,
|
|
132
135
|
margin = 0,
|
|
133
136
|
sampleUnit,
|
|
137
|
+
sumBy,
|
|
134
138
|
}: Props): JSX.Element => {
|
|
135
139
|
const {timezone} = useParcaContext();
|
|
136
140
|
const graph = useRef(null);
|
|
@@ -149,6 +153,8 @@ export const RawMetricsGraph = ({
|
|
|
149
153
|
width = 0;
|
|
150
154
|
}
|
|
151
155
|
|
|
156
|
+
const graphWidth = width - margin * 1.5 - margin / 2;
|
|
157
|
+
|
|
152
158
|
const series: Series[] = data.reduce<Series[]>(function (agg: Series[], s: MetricsSeriesPb) {
|
|
153
159
|
if (s.labelset !== undefined) {
|
|
154
160
|
const metric = s.labelset.labels.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -184,10 +190,7 @@ export const RawMetricsGraph = ({
|
|
|
184
190
|
});
|
|
185
191
|
|
|
186
192
|
/* Scale */
|
|
187
|
-
const xScale = d3
|
|
188
|
-
.scaleUtc()
|
|
189
|
-
.domain([from, to])
|
|
190
|
-
.range([0, width - 2.5 * margin]);
|
|
193
|
+
const xScale = d3.scaleUtc().domain([from, to]).range([0, graphWidth]);
|
|
191
194
|
|
|
192
195
|
const yScale = d3
|
|
193
196
|
.scaleLinear()
|
|
@@ -332,15 +335,29 @@ export const RawMetricsGraph = ({
|
|
|
332
335
|
let s: Series | null = null;
|
|
333
336
|
let seriesIndex = -1;
|
|
334
337
|
|
|
338
|
+
// if there are both query matchers and also a sumby value, we need to check if the sumby value is part of the query matchers.
|
|
339
|
+
// if it is, then we should prioritize using the sumby label name and value to find the selected profile.
|
|
340
|
+
const useSumBy =
|
|
341
|
+
sumBy !== undefined &&
|
|
342
|
+
sumBy.length > 0 &&
|
|
343
|
+
profile.query.matchers.length > 0 &&
|
|
344
|
+
profile.query.matchers.some(e => sumBy.includes(e.key));
|
|
345
|
+
|
|
346
|
+
// get only the sumby keys and values from the profile query matchers
|
|
347
|
+
const sumByMatchers =
|
|
348
|
+
sumBy !== undefined ? profile.query.matchers.filter(e => sumBy.includes(e.key)) : [];
|
|
349
|
+
|
|
350
|
+
const keysToMatch = useSumBy ? sumByMatchers : profile.query.matchers;
|
|
351
|
+
|
|
335
352
|
outer: for (let i = 0; i < series.length; i++) {
|
|
336
|
-
const keys =
|
|
353
|
+
const keys = keysToMatch.map(e => e.key);
|
|
337
354
|
for (let j = 0; j < keys.length; j++) {
|
|
338
355
|
const matcherKey = keys[j];
|
|
339
356
|
const label = series[i].metric.find(e => e.name === matcherKey);
|
|
340
357
|
if (label === undefined) {
|
|
341
358
|
continue outer; // label doesn't exist to begin with
|
|
342
359
|
}
|
|
343
|
-
if (
|
|
360
|
+
if (keysToMatch[j].value !== label.value) {
|
|
344
361
|
continue outer; // label values don't match
|
|
345
362
|
}
|
|
346
363
|
}
|
|
@@ -555,8 +572,8 @@ export const RawMetricsGraph = ({
|
|
|
555
572
|
))}
|
|
556
573
|
<line
|
|
557
574
|
className="stroke-gray-300 dark:stroke-gray-500"
|
|
558
|
-
x1={
|
|
559
|
-
x2={
|
|
575
|
+
x1={0}
|
|
576
|
+
x2={graphWidth}
|
|
560
577
|
y1={0}
|
|
561
578
|
y2={0}
|
|
562
579
|
/>
|
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
+
import {useState} from 'react';
|
|
15
|
+
|
|
14
16
|
import {QueryServiceClient} from '@parca/client';
|
|
15
|
-
import {
|
|
17
|
+
import {useURLState} from '@parca/components';
|
|
16
18
|
import {Query} from '@parca/parser';
|
|
17
19
|
import type {NavigateFunction} from '@parca/utilities';
|
|
18
20
|
|
|
@@ -48,6 +50,9 @@ const ProfileExplorerCompare = ({
|
|
|
48
50
|
closeProfile,
|
|
49
51
|
navigateTo,
|
|
50
52
|
}: ProfileExplorerCompareProps): JSX.Element => {
|
|
53
|
+
const [showMetricsGraph, setShowMetricsGraph] = useState(true);
|
|
54
|
+
const [showButton, setShowButton] = useState(false);
|
|
55
|
+
|
|
51
56
|
const closeProfileA = (): void => {
|
|
52
57
|
closeProfile('A');
|
|
53
58
|
};
|
|
@@ -61,8 +66,18 @@ const ProfileExplorerCompare = ({
|
|
|
61
66
|
|
|
62
67
|
return (
|
|
63
68
|
<>
|
|
64
|
-
<div
|
|
65
|
-
|
|
69
|
+
<div
|
|
70
|
+
className="flex justify-between gap-2 relative mb-2"
|
|
71
|
+
onMouseEnter={() => {
|
|
72
|
+
if (!showMetricsGraph) return;
|
|
73
|
+
setShowButton(true);
|
|
74
|
+
}}
|
|
75
|
+
onMouseLeave={() => {
|
|
76
|
+
if (!showMetricsGraph) return;
|
|
77
|
+
setShowButton(false);
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
<div className="flex-column flex-1 p-2 shadow-md rounded-md">
|
|
66
81
|
<ProfileSelector
|
|
67
82
|
queryClient={queryClient}
|
|
68
83
|
querySelection={queryA}
|
|
@@ -74,9 +89,12 @@ const ProfileExplorerCompare = ({
|
|
|
74
89
|
comparing={true}
|
|
75
90
|
navigateTo={navigateTo}
|
|
76
91
|
suffix="_a"
|
|
92
|
+
showMetricsGraph={showMetricsGraph}
|
|
93
|
+
displayHideMetricsGraphButton={showButton}
|
|
94
|
+
setDisplayHideMetricsGraphButton={setShowMetricsGraph}
|
|
77
95
|
/>
|
|
78
|
-
</
|
|
79
|
-
<
|
|
96
|
+
</div>
|
|
97
|
+
<div className="flex-column flex-1 p-2 shadow-md rounded-md">
|
|
80
98
|
<ProfileSelector
|
|
81
99
|
queryClient={queryClient}
|
|
82
100
|
querySelection={queryB}
|
|
@@ -88,25 +106,26 @@ const ProfileExplorerCompare = ({
|
|
|
88
106
|
comparing={true}
|
|
89
107
|
navigateTo={navigateTo}
|
|
90
108
|
suffix="_b"
|
|
109
|
+
showMetricsGraph={showMetricsGraph}
|
|
110
|
+
displayHideMetricsGraphButton={showButton}
|
|
111
|
+
setDisplayHideMetricsGraphButton={setShowMetricsGraph}
|
|
91
112
|
/>
|
|
92
|
-
</
|
|
113
|
+
</div>
|
|
93
114
|
</div>
|
|
94
115
|
<div className="grid grid-cols-1">
|
|
95
116
|
{profileA != null && profileB != null ? (
|
|
96
117
|
<div>
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
/>
|
|
109
|
-
</Card>
|
|
118
|
+
<ProfileViewWithData
|
|
119
|
+
queryClient={queryClient}
|
|
120
|
+
profileSource={
|
|
121
|
+
new ProfileDiffSource(
|
|
122
|
+
profileA.ProfileSource(),
|
|
123
|
+
profileB.ProfileSource(),
|
|
124
|
+
Array.isArray(functionFilter) ? functionFilter[0] : functionFilter,
|
|
125
|
+
compareAbsolute === 'true'
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
/>
|
|
110
129
|
</div>
|
|
111
130
|
) : (
|
|
112
131
|
<div>
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
+
import {useState} from 'react';
|
|
15
|
+
|
|
14
16
|
import {QueryServiceClient} from '@parca/client';
|
|
15
|
-
import {Card} from '@parca/components';
|
|
16
17
|
import type {NavigateFunction} from '@parca/utilities';
|
|
17
18
|
|
|
18
19
|
import {ProfileSelection, ProfileViewWithData} from '..';
|
|
@@ -35,9 +36,22 @@ const ProfileExplorerSingle = ({
|
|
|
35
36
|
profile,
|
|
36
37
|
navigateTo,
|
|
37
38
|
}: ProfileExplorerSingleProps): JSX.Element => {
|
|
39
|
+
const [showMetricsGraph, setShowMetricsGraph] = useState(true);
|
|
40
|
+
const [showButton, setShowButton] = useState(false);
|
|
41
|
+
|
|
38
42
|
return (
|
|
39
43
|
<>
|
|
40
|
-
<
|
|
44
|
+
<div
|
|
45
|
+
className="relative"
|
|
46
|
+
onMouseEnter={() => {
|
|
47
|
+
if (!showMetricsGraph) return;
|
|
48
|
+
setShowButton(true);
|
|
49
|
+
}}
|
|
50
|
+
onMouseLeave={() => {
|
|
51
|
+
if (!showMetricsGraph) return;
|
|
52
|
+
setShowButton(false);
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
41
55
|
<ProfileSelector
|
|
42
56
|
queryClient={queryClient}
|
|
43
57
|
querySelection={query}
|
|
@@ -49,12 +63,14 @@ const ProfileExplorerSingle = ({
|
|
|
49
63
|
enforcedProfileName={''} // TODO
|
|
50
64
|
navigateTo={navigateTo}
|
|
51
65
|
suffix="_a"
|
|
66
|
+
showMetricsGraph={showMetricsGraph}
|
|
67
|
+
displayHideMetricsGraphButton={showButton}
|
|
68
|
+
setDisplayHideMetricsGraphButton={setShowMetricsGraph}
|
|
52
69
|
/>
|
|
53
|
-
</
|
|
70
|
+
</div>
|
|
71
|
+
|
|
54
72
|
{profile != null ? (
|
|
55
|
-
<
|
|
56
|
-
<ProfileViewWithData queryClient={queryClient} profileSource={profile.ProfileSource()} />
|
|
57
|
-
</Card>
|
|
73
|
+
<ProfileViewWithData queryClient={queryClient} profileSource={profile.ProfileSource()} />
|
|
58
74
|
) : (
|
|
59
75
|
<></>
|
|
60
76
|
)}
|
|
@@ -11,10 +11,11 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import React, {useEffect, useMemo, useRef, useState} from 'react';
|
|
14
|
+
import React, {Dispatch, SetStateAction, useEffect, useMemo, useRef, useState} from 'react';
|
|
15
15
|
|
|
16
16
|
import {Switch} from '@headlessui/react';
|
|
17
17
|
import {RpcError} from '@protobuf-ts/runtime-rpc';
|
|
18
|
+
import cx from 'classnames';
|
|
18
19
|
import Select, {type SelectInstance} from 'react-select';
|
|
19
20
|
|
|
20
21
|
import {Label, ProfileTypesResponse, QueryServiceClient} from '@parca/client';
|
|
@@ -63,6 +64,9 @@ interface ProfileSelectorProps {
|
|
|
63
64
|
comparing: boolean;
|
|
64
65
|
navigateTo: NavigateFunction;
|
|
65
66
|
suffix?: string;
|
|
67
|
+
showMetricsGraph: boolean;
|
|
68
|
+
displayHideMetricsGraphButton: boolean;
|
|
69
|
+
setDisplayHideMetricsGraphButton: Dispatch<SetStateAction<boolean>>;
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
export interface IProfileTypesResult {
|
|
@@ -101,6 +105,9 @@ const ProfileSelector = ({
|
|
|
101
105
|
profileSelection,
|
|
102
106
|
comparing,
|
|
103
107
|
navigateTo,
|
|
108
|
+
showMetricsGraph,
|
|
109
|
+
displayHideMetricsGraphButton,
|
|
110
|
+
setDisplayHideMetricsGraphButton,
|
|
104
111
|
}: ProfileSelectorProps): JSX.Element => {
|
|
105
112
|
const {
|
|
106
113
|
loading: profileTypesLoading,
|
|
@@ -281,9 +288,9 @@ const ProfileSelector = ({
|
|
|
281
288
|
|
|
282
289
|
return (
|
|
283
290
|
<>
|
|
284
|
-
<div className="mb-2 flex
|
|
285
|
-
<div className="flex w-full flex-wrap
|
|
286
|
-
<div
|
|
291
|
+
<div className="mb-2 flex">
|
|
292
|
+
<div className="flex w-full flex-wrap items-end gap-2">
|
|
293
|
+
<div>
|
|
287
294
|
<label className="text-xs">Profile type</label>
|
|
288
295
|
<ProfileTypeSelector
|
|
289
296
|
profileTypesData={profileTypesData}
|
|
@@ -294,7 +301,7 @@ const ProfileSelector = ({
|
|
|
294
301
|
disabled={viewComponent?.disableProfileTypesDropdown}
|
|
295
302
|
/>
|
|
296
303
|
</div>
|
|
297
|
-
<div className="w-full flex-1 flex flex-col
|
|
304
|
+
<div className="w-full flex-1 flex flex-col gap-1" ref={queryBrowserRef}>
|
|
298
305
|
<div className="flex items-center justify-between">
|
|
299
306
|
<div className="flex items-center gap-3">
|
|
300
307
|
<label className="text-xs">Query</label>
|
|
@@ -309,7 +316,7 @@ const ProfileSelector = ({
|
|
|
309
316
|
className={`${
|
|
310
317
|
advancedModeForQueryBrowser
|
|
311
318
|
? 'bg-indigo-600'
|
|
312
|
-
: 'bg-gray-400 dark:bg-gray-
|
|
319
|
+
: 'bg-gray-400 dark:bg-gray-800'
|
|
313
320
|
}
|
|
314
321
|
relative inline-flex h-[20px] w-[44px] shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75`}
|
|
315
322
|
>
|
|
@@ -365,7 +372,7 @@ const ProfileSelector = ({
|
|
|
365
372
|
</>
|
|
366
373
|
)}
|
|
367
374
|
</div>
|
|
368
|
-
<div
|
|
375
|
+
<div>
|
|
369
376
|
<div className="mb-0.5 mt-1.5 flex items-center justify-between">
|
|
370
377
|
<label className="text-xs">Sum by</label>
|
|
371
378
|
</div>
|
|
@@ -383,6 +390,7 @@ const ProfileSelector = ({
|
|
|
383
390
|
placeholder="Labels..."
|
|
384
391
|
styles={{
|
|
385
392
|
indicatorSeparator: () => ({display: 'none'}),
|
|
393
|
+
menu: provided => ({...provided, width: 'max-content'}),
|
|
386
394
|
}}
|
|
387
395
|
isDisabled={!profileType.delta}
|
|
388
396
|
ref={sumByRef}
|
|
@@ -426,75 +434,91 @@ const ProfileSelector = ({
|
|
|
426
434
|
</div>
|
|
427
435
|
<div>{comparing && <IconButton onClick={() => closeProfile()} icon={<CloseIcon />} />}</div>
|
|
428
436
|
</div>
|
|
429
|
-
<div
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
to={querySelection.to}
|
|
441
|
-
profile={profileSelection}
|
|
442
|
-
comparing={comparing}
|
|
443
|
-
sumBy={querySelection.sumBy ?? defaultSumBy ?? []}
|
|
444
|
-
sumByLoading={defaultSumByLoading}
|
|
445
|
-
setTimeRange={(range: DateTimeRange) => {
|
|
446
|
-
const from = range.getFromMs();
|
|
447
|
-
const to = range.getToMs();
|
|
448
|
-
let mergedProfileParams = {};
|
|
449
|
-
if (query.profileType().delta) {
|
|
450
|
-
mergedProfileParams = {mergeFrom: from, mergeTo: to};
|
|
451
|
-
}
|
|
452
|
-
setTimeRangeSelection(range);
|
|
453
|
-
selectQuery({
|
|
454
|
-
expression: queryExpressionString,
|
|
455
|
-
from,
|
|
456
|
-
to,
|
|
457
|
-
timeSelection: range.getRangeKey(),
|
|
458
|
-
...mergedProfileParams,
|
|
459
|
-
});
|
|
460
|
-
}}
|
|
461
|
-
addLabelMatcher={addLabelMatcher}
|
|
462
|
-
onPointClick={(
|
|
463
|
-
timestamp: number,
|
|
464
|
-
labels: Label[],
|
|
465
|
-
queryExpression: string,
|
|
466
|
-
duration: number
|
|
467
|
-
) => {
|
|
468
|
-
// TODO: Pass the query object via click rather than queryExpression
|
|
469
|
-
let query = Query.parse(queryExpression);
|
|
470
|
-
labels.forEach(l => {
|
|
471
|
-
const [newQuery, updated] = query.setMatcher(l.name, l.value);
|
|
472
|
-
if (updated) {
|
|
473
|
-
query = newQuery;
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
const durationInMilliseconds = duration / 1000000; // duration is in nanoseconds
|
|
478
|
-
const mergeFrom = timestamp;
|
|
479
|
-
const mergeTo = query.profileType().delta
|
|
480
|
-
? mergeFrom + durationInMilliseconds
|
|
481
|
-
: mergeFrom;
|
|
482
|
-
selectProfile(new MergedProfileSelection(mergeFrom, mergeTo, query));
|
|
483
|
-
}}
|
|
484
|
-
/>
|
|
485
|
-
</div>
|
|
486
|
-
) : (
|
|
487
|
-
<>
|
|
488
|
-
{profileSelection == null ? (
|
|
489
|
-
<div className="p-2">
|
|
490
|
-
<ProfileMetricsEmptyState
|
|
491
|
-
message={`Please select a profile type and click "Search" to begin.`}
|
|
492
|
-
/>
|
|
493
|
-
</div>
|
|
494
|
-
) : null}
|
|
495
|
-
</>
|
|
437
|
+
<div
|
|
438
|
+
className={cx('relative', {
|
|
439
|
+
'py-4': !showMetricsGraph,
|
|
440
|
+
})}
|
|
441
|
+
>
|
|
442
|
+
<button
|
|
443
|
+
onClick={() => setDisplayHideMetricsGraphButton(!showMetricsGraph)}
|
|
444
|
+
className={cx(
|
|
445
|
+
'hidden z-10 px-3 py-1 text-sm font-medium text-gray-700 dark:text-gray-200 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-gray-900',
|
|
446
|
+
displayHideMetricsGraphButton && showMetricsGraph && 'absolute right-0 bottom-3 !flex',
|
|
447
|
+
!showMetricsGraph && 'relative !flex ml-auto'
|
|
496
448
|
)}
|
|
497
|
-
|
|
449
|
+
>
|
|
450
|
+
{showMetricsGraph ? 'Hide' : 'Show'} Metrics Graph
|
|
451
|
+
</button>
|
|
452
|
+
{showMetricsGraph ? (
|
|
453
|
+
<div style={{height: heightStyle}}>
|
|
454
|
+
{querySelection.expression !== undefined &&
|
|
455
|
+
querySelection.expression.length > 0 &&
|
|
456
|
+
querySelection.from !== undefined &&
|
|
457
|
+
querySelection.to !== undefined ? (
|
|
458
|
+
<div>
|
|
459
|
+
<ProfileMetricsGraph
|
|
460
|
+
queryClient={queryClient}
|
|
461
|
+
queryExpression={querySelection.expression}
|
|
462
|
+
from={querySelection.from}
|
|
463
|
+
to={querySelection.to}
|
|
464
|
+
profile={profileSelection}
|
|
465
|
+
comparing={comparing}
|
|
466
|
+
sumBy={querySelection.sumBy ?? defaultSumBy ?? []}
|
|
467
|
+
sumByLoading={defaultSumByLoading}
|
|
468
|
+
setTimeRange={(range: DateTimeRange) => {
|
|
469
|
+
const from = range.getFromMs();
|
|
470
|
+
const to = range.getToMs();
|
|
471
|
+
let mergedProfileParams = {};
|
|
472
|
+
if (query.profileType().delta) {
|
|
473
|
+
mergedProfileParams = {mergeFrom: from, mergeTo: to};
|
|
474
|
+
}
|
|
475
|
+
setTimeRangeSelection(range);
|
|
476
|
+
selectQuery({
|
|
477
|
+
expression: queryExpressionString,
|
|
478
|
+
from,
|
|
479
|
+
to,
|
|
480
|
+
timeSelection: range.getRangeKey(),
|
|
481
|
+
...mergedProfileParams,
|
|
482
|
+
});
|
|
483
|
+
}}
|
|
484
|
+
addLabelMatcher={addLabelMatcher}
|
|
485
|
+
onPointClick={(
|
|
486
|
+
timestamp: number,
|
|
487
|
+
labels: Label[],
|
|
488
|
+
queryExpression: string,
|
|
489
|
+
duration: number
|
|
490
|
+
) => {
|
|
491
|
+
// TODO: Pass the query object via click rather than queryExpression
|
|
492
|
+
let query = Query.parse(queryExpression);
|
|
493
|
+
labels.forEach(l => {
|
|
494
|
+
const [newQuery, updated] = query.setMatcher(l.name, l.value);
|
|
495
|
+
if (updated) {
|
|
496
|
+
query = newQuery;
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
const durationInMilliseconds = duration / 1000000; // duration is in nanoseconds
|
|
501
|
+
const mergeFrom = timestamp;
|
|
502
|
+
const mergeTo = query.profileType().delta
|
|
503
|
+
? mergeFrom + durationInMilliseconds
|
|
504
|
+
: mergeFrom;
|
|
505
|
+
selectProfile(new MergedProfileSelection(mergeFrom, mergeTo, query));
|
|
506
|
+
}}
|
|
507
|
+
/>
|
|
508
|
+
</div>
|
|
509
|
+
) : (
|
|
510
|
+
<>
|
|
511
|
+
{profileSelection == null ? (
|
|
512
|
+
<div className="p-2">
|
|
513
|
+
<ProfileMetricsEmptyState
|
|
514
|
+
message={`Please select a profile type and click "Search" to begin.`}
|
|
515
|
+
/>
|
|
516
|
+
</div>
|
|
517
|
+
) : null}
|
|
518
|
+
</>
|
|
519
|
+
)}
|
|
520
|
+
</div>
|
|
521
|
+
) : null}
|
|
498
522
|
</div>
|
|
499
523
|
</>
|
|
500
524
|
);
|
package/src/ProfileSource.tsx
CHANGED
|
@@ -184,7 +184,7 @@ export class ProfileDiffSource implements ProfileSource {
|
|
|
184
184
|
const bDesc = this.b.toString();
|
|
185
185
|
|
|
186
186
|
if (aDesc === bDesc) {
|
|
187
|
-
return '
|
|
187
|
+
return 'Profile comparison';
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
return `${this.a.toString()} compared with ${this.b.toString()}`;
|
|
@@ -264,6 +264,6 @@ export class MergedProfileSource implements ProfileSource {
|
|
|
264
264
|
)}`;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
return `
|
|
267
|
+
return `Merged profiles${queryPart}${timePart}`;
|
|
268
268
|
}
|
|
269
269
|
}
|
|
@@ -337,28 +337,37 @@ export const ProfileView = ({
|
|
|
337
337
|
[groupBy, setGroupBy]
|
|
338
338
|
);
|
|
339
339
|
|
|
340
|
+
const showDivider =
|
|
341
|
+
hasProfileSource &&
|
|
342
|
+
(profileViewExternalMainActions === null || profileViewExternalMainActions === undefined);
|
|
343
|
+
|
|
340
344
|
return (
|
|
341
345
|
<KeyDownProvider>
|
|
342
346
|
<ProfileViewContextProvider value={{profileSource, compareMode}}>
|
|
347
|
+
{showDivider ? (
|
|
348
|
+
<>
|
|
349
|
+
<div className="border-t border-gray-200 dark:border-gray-700 h-[1px] w-full pb-4"></div>
|
|
350
|
+
</>
|
|
351
|
+
) : null}
|
|
343
352
|
<div
|
|
344
353
|
className={cx(
|
|
345
|
-
'
|
|
354
|
+
'flex w-full',
|
|
346
355
|
hasProfileSource || profileViewExternalMainActions != null
|
|
347
|
-
? 'justify-
|
|
356
|
+
? 'justify-start'
|
|
348
357
|
: 'justify-end',
|
|
349
358
|
{
|
|
350
|
-
'items-end': !hasProfileSource && profileViewExternalMainActions != null,
|
|
351
|
-
'items-center': hasProfileSource,
|
|
359
|
+
'items-end mb-4': !hasProfileSource && profileViewExternalMainActions != null,
|
|
360
|
+
'items-center mb-2': hasProfileSource,
|
|
352
361
|
}
|
|
353
362
|
)}
|
|
354
363
|
>
|
|
355
364
|
<div>
|
|
356
365
|
{hasProfileSource && (
|
|
357
366
|
<div className="flex items-center gap-1">
|
|
358
|
-
<div className="text-
|
|
367
|
+
<div className="text-xs font-medium">
|
|
359
368
|
{headerParts.length > 0 ? headerParts[0].replace(/"/g, '') : ''}
|
|
360
369
|
</div>
|
|
361
|
-
<div className="text-xs">
|
|
370
|
+
<div className="text-xs font-medium">
|
|
362
371
|
{headerParts.length > 1
|
|
363
372
|
? headerParts[headerParts.length - 1].replace(/"/g, '')
|
|
364
373
|
: ''}
|
|
@@ -415,10 +424,10 @@ export const ProfileView = ({
|
|
|
415
424
|
{...provided.draggableProps}
|
|
416
425
|
key={dashboardItem}
|
|
417
426
|
className={cx(
|
|
418
|
-
'w-full
|
|
427
|
+
'w-full min-h-96',
|
|
419
428
|
snapshot.isDragging
|
|
420
429
|
? 'bg-gray-200 dark:bg-gray-500'
|
|
421
|
-
: 'bg-white dark:bg-gray-
|
|
430
|
+
: 'bg-white dark:bg-gray-900'
|
|
422
431
|
)}
|
|
423
432
|
>
|
|
424
433
|
<VisualizationPanel
|