@parca/profile 0.19.102 → 0.19.103
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 +4 -0
- package/dist/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.js +65 -45
- package/dist/ProfileFlameGraph/FlameGraphArrow/index.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/FlameGraphArrow/index.js +16 -4
- package/dist/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.d.ts +11 -0
- package/dist/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.d.ts.map +1 -0
- package/dist/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.js +65 -0
- package/dist/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.js +35 -5
- package/dist/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.js +29 -3
- package/dist/hooks/useLabels.d.ts.map +1 -1
- package/dist/hooks/useLabels.js +3 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -2
- package/src/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.tsx +89 -57
- package/src/ProfileFlameGraph/FlameGraphArrow/index.tsx +27 -2
- package/src/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.ts +84 -0
- package/src/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.ts +40 -5
- package/src/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.ts +41 -5
- package/src/hooks/useLabels.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.19.103](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.102...@parca/profile@0.19.103) (2025-12-17)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
6
10
|
## [0.19.102](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.101...@parca/profile@0.19.102) (2025-12-15)
|
|
7
11
|
|
|
8
12
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlameGraphNodes.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.tsx"],"names":[],"mappings":"AAaA,OAAO,
|
|
1
|
+
{"version":3,"file":"FlameGraphNodes.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.tsx"],"names":[],"mappings":"AAaA,OAAO,KAA6B,MAAM,OAAO,CAAC;AAElD,OAAO,EAAC,KAAK,EAAC,MAAM,cAAc,CAAC;AAKnC,OAAO,yCAAyC,CAAC;AAEjD,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAelD,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,MAAM,WAAW,aAAa;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,oBAAoB,EAAE,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CACnD;AAED,eAAO,MAAM,eAAe;;;CAG3B,CAAC;AACF,eAAO,MAAM,oBAAoB;;;;CAIhC,CAAC;AAEF,eAAO,MAAM,SAAS,4CA6PrB,CAAC"}
|
|
@@ -11,7 +11,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
11
11
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
// See the License for the specific language governing permissions and
|
|
13
13
|
// limitations under the License.
|
|
14
|
-
import React, { useMemo } from 'react';
|
|
14
|
+
import React, { useCallback, useMemo } from 'react';
|
|
15
15
|
import cx from 'classnames';
|
|
16
16
|
import { selectBinaries, useAppSelector } from '@parca/store';
|
|
17
17
|
import 'react-contexify/dist/ReactContexify.css';
|
|
@@ -30,28 +30,38 @@ export const fadedFlameRectStyles = {
|
|
|
30
30
|
opacity: '0.5',
|
|
31
31
|
};
|
|
32
32
|
export const FlameNode = React.memo(function FlameNodeNoMemo({ table, row, colors, colorBy, height, totalWidth, darkMode, compareMode, colorForSimilarNodes, selectedRow, onClick, onContextMenu, hoveringRow, setHoveringRow, isFlameChart, profileSource, isRenderedAsFlamegraph = false, isInSandwichView = false, maxDepth = 0, effectiveDepth, tooltipId = 'default', }) {
|
|
33
|
-
//
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
// Memoize column references - only changes when table changes
|
|
34
|
+
const columns = useMemo(() => ({
|
|
35
|
+
mapping: table.getChild(FIELD_MAPPING_FILE),
|
|
36
|
+
functionName: table.getChild(FIELD_FUNCTION_NAME),
|
|
37
|
+
cumulative: table.getChild(FIELD_CUMULATIVE),
|
|
38
|
+
depth: table.getChild(FIELD_DEPTH),
|
|
39
|
+
diff: table.getChild(FIELD_DIFF),
|
|
40
|
+
filename: table.getChild(FIELD_FUNCTION_FILE_NAME),
|
|
41
|
+
valueOffset: table.getChild(FIELD_VALUE_OFFSET),
|
|
42
|
+
ts: table.getChild(FIELD_TIMESTAMP),
|
|
43
|
+
}), [table]);
|
|
42
44
|
// get the actual values from the columns
|
|
43
45
|
const binaries = useAppSelector(selectBinaries);
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
// Memoize row data extraction - only changes when table or row changes
|
|
47
|
+
const rowData = useMemo(() => {
|
|
48
|
+
const mappingFile = arrowToString(columns.mapping?.get(row));
|
|
49
|
+
const functionName = arrowToString(columns.functionName?.get(row));
|
|
50
|
+
const cumulative = columns.cumulative?.get(row) != null ? BigInt(columns.cumulative?.get(row)) : 0n;
|
|
51
|
+
const diff = columns.diff?.get(row) != null ? BigInt(columns.diff?.get(row)) : null;
|
|
52
|
+
const filename = arrowToString(columns.filename?.get(row));
|
|
53
|
+
const depth = columns.depth?.get(row) ?? 0;
|
|
54
|
+
const valueOffset = columns.valueOffset?.get(row) !== null && columns.valueOffset?.get(row) !== undefined
|
|
55
|
+
? BigInt(columns.valueOffset?.get(row))
|
|
56
|
+
: 0n;
|
|
57
|
+
return { mappingFile, functionName, cumulative, diff, filename, depth, valueOffset };
|
|
58
|
+
}, [columns, row]);
|
|
59
|
+
const { mappingFile, functionName, cumulative, diff, filename, depth, valueOffset } = rowData;
|
|
53
60
|
const colorAttribute = colorBy === 'filename' ? filename : colorBy === 'binary' ? mappingFile : null;
|
|
54
|
-
|
|
61
|
+
// Memoize hovering name lookup
|
|
62
|
+
const hoveringName = useMemo(() => {
|
|
63
|
+
return hoveringRow !== undefined ? arrowToString(columns.functionName?.get(hoveringRow)) : '';
|
|
64
|
+
}, [columns.functionName, hoveringRow]);
|
|
55
65
|
const shouldBeHighlighted = functionName != null && hoveringName != null && functionName === hoveringName;
|
|
56
66
|
const colorResult = useNodeColor({
|
|
57
67
|
isDarkMode: darkMode,
|
|
@@ -64,15 +74,43 @@ export const FlameNode = React.memo(function FlameNodeNoMemo({ table, row, color
|
|
|
64
74
|
const name = useMemo(() => {
|
|
65
75
|
return row === 0 ? 'root' : nodeLabel(table, row, binaries.length > 1);
|
|
66
76
|
}, [table, row, binaries]);
|
|
77
|
+
// Memoize selection data - only changes when selectedRow changes
|
|
78
|
+
const selectionData = useMemo(() => {
|
|
79
|
+
const selectionOffset = columns.valueOffset?.get(selectedRow) !== null &&
|
|
80
|
+
columns.valueOffset?.get(selectedRow) !== undefined
|
|
81
|
+
? BigInt(columns.valueOffset?.get(selectedRow))
|
|
82
|
+
: 0n;
|
|
83
|
+
const selectionCumulative = columns.cumulative?.get(selectedRow) !== null
|
|
84
|
+
? BigInt(columns.cumulative?.get(selectedRow))
|
|
85
|
+
: 0n;
|
|
86
|
+
const selectedDepth = columns.depth?.get(selectedRow);
|
|
87
|
+
const total = columns.cumulative?.get(selectedRow);
|
|
88
|
+
return { selectionOffset, selectionCumulative, selectedDepth, total };
|
|
89
|
+
}, [columns, selectedRow]);
|
|
90
|
+
const { selectionOffset, selectionCumulative, selectedDepth, total } = selectionData;
|
|
91
|
+
// Memoize tsBounds - only changes when profileSource changes
|
|
92
|
+
const tsBounds = useMemo(() => boundsFromProfileSource(profileSource), [profileSource]);
|
|
93
|
+
// Memoize event handlers
|
|
94
|
+
const onMouseEnter = useCallback(() => {
|
|
95
|
+
setHoveringRow(row);
|
|
96
|
+
window.dispatchEvent(new CustomEvent(`flame-tooltip-update-${tooltipId}`, {
|
|
97
|
+
detail: { row },
|
|
98
|
+
}));
|
|
99
|
+
}, [setHoveringRow, row, tooltipId]);
|
|
100
|
+
const onMouseLeave = useCallback(() => {
|
|
101
|
+
setHoveringRow(undefined);
|
|
102
|
+
window.dispatchEvent(new CustomEvent(`flame-tooltip-update-${tooltipId}`, {
|
|
103
|
+
detail: { row: null },
|
|
104
|
+
}));
|
|
105
|
+
}, [setHoveringRow, tooltipId]);
|
|
106
|
+
const handleContextMenu = useCallback((e) => {
|
|
107
|
+
onContextMenu(e, row);
|
|
108
|
+
}, [onContextMenu, row]);
|
|
109
|
+
// Early returns - all hooks must be called before this point
|
|
67
110
|
// Hide frames beyond effective depth limit
|
|
68
111
|
if (effectiveDepth !== undefined && depth > effectiveDepth) {
|
|
69
112
|
return _jsx(_Fragment, {});
|
|
70
113
|
}
|
|
71
|
-
const selectionOffset = valueOffsetColumn?.get(selectedRow) !== null &&
|
|
72
|
-
valueOffsetColumn?.get(selectedRow) !== undefined
|
|
73
|
-
? BigInt(valueOffsetColumn?.get(selectedRow))
|
|
74
|
-
: 0n;
|
|
75
|
-
const selectionCumulative = cumulativeColumn?.get(selectedRow) !== null ? BigInt(cumulativeColumn?.get(selectedRow)) : 0n;
|
|
76
114
|
if (valueOffset + cumulative <= selectionOffset ||
|
|
77
115
|
valueOffset >= selectionOffset + selectionCumulative) {
|
|
78
116
|
// If the end of the node is before the selection offset or the start of the node is after the selection offset + totalWidth, we don't render it.
|
|
@@ -83,8 +121,6 @@ export const FlameNode = React.memo(function FlameNodeNoMemo({ table, row, color
|
|
|
83
121
|
return _jsx(_Fragment, {});
|
|
84
122
|
}
|
|
85
123
|
// Cumulative can be larger than total when a selection is made. All parents of the selection are likely larger, but we want to only show them as 100% in the graph.
|
|
86
|
-
const tsBounds = boundsFromProfileSource(profileSource);
|
|
87
|
-
const total = cumulativeColumn?.get(selectedRow);
|
|
88
124
|
const totalRatio = cumulative > total ? 1 : Number(cumulative) / Number(total);
|
|
89
125
|
const width = isFlameChart
|
|
90
126
|
? (Number(cumulative) / (Number(tsBounds[1]) - Number(tsBounds[0]))) * totalWidth
|
|
@@ -92,25 +128,9 @@ export const FlameNode = React.memo(function FlameNodeNoMemo({ table, row, color
|
|
|
92
128
|
if (width <= 1) {
|
|
93
129
|
return _jsx(_Fragment, {});
|
|
94
130
|
}
|
|
95
|
-
const selectedDepth = depthColumn?.get(selectedRow);
|
|
96
131
|
const styles = selectedDepth !== undefined && selectedDepth > depth ? fadedFlameRectStyles : flameRectStyles;
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
window.dispatchEvent(new CustomEvent(`flame-tooltip-update-${tooltipId}`, {
|
|
100
|
-
detail: { row },
|
|
101
|
-
}));
|
|
102
|
-
};
|
|
103
|
-
const onMouseLeave = () => {
|
|
104
|
-
setHoveringRow(undefined);
|
|
105
|
-
window.dispatchEvent(new CustomEvent(`flame-tooltip-update-${tooltipId}`, {
|
|
106
|
-
detail: { row: null },
|
|
107
|
-
}));
|
|
108
|
-
};
|
|
109
|
-
const handleContextMenu = (e) => {
|
|
110
|
-
onContextMenu(e, row);
|
|
111
|
-
};
|
|
112
|
-
const ts = tsColumn !== null ? Number(tsColumn.get(row)) : 0;
|
|
113
|
-
const x = isFlameChart && tsColumn !== null
|
|
132
|
+
const ts = columns.ts !== null ? Number(columns.ts.get(row)) : 0;
|
|
133
|
+
const x = isFlameChart && columns.ts !== null
|
|
114
134
|
? ((ts - Number(tsBounds[0])) / (Number(tsBounds[1]) - Number(tsBounds[0]))) * totalWidth
|
|
115
135
|
: selectedDepth > depth
|
|
116
136
|
? 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAQN,MAAM,OAAO,CAAC;AAKf,OAAO,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAG9C,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAIlD,OAAO,EAAuB,aAAa,EAAC,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAQN,MAAM,OAAO,CAAC;AAKf,OAAO,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAG9C,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAIlD,OAAO,EAAuB,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAMtE,OAAO,EACL,gBAAgB,EAMjB,MAAM,SAAS,CAAC;AAEjB,eAAO,MAAM,iBAAiB,gBAAgB,CAAC;AAC/C,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,aAAa,YAAY,CAAC;AACvC,eAAO,MAAM,eAAe,cAAc,CAAC;AAC3C,eAAO,MAAM,cAAc,aAAa,CAAC;AACzC,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,0BAA0B,yBAAyB,CAAC;AACjE,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAC7D,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAC9D,eAAO,MAAM,cAAc,aAAa,CAAC;AACzC,eAAO,MAAM,YAAY,WAAW,CAAC;AACrC,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAC7C,eAAO,MAAM,UAAU,SAAS,CAAC;AACjC,eAAO,MAAM,UAAU,SAAS,CAAC;AACjC,eAAO,MAAM,YAAY,WAAW,CAAC;AACrC,eAAO,MAAM,WAAW,UAAU,CAAC;AACnC,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AAEjD,UAAU,oBAAoB;IAC5B,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC/C,YAAY,EAAE,OAAO,CAAC;IACtB,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,yBAAyB,EAAE,MAAM,EAAE,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,gBAAgB,GAC3B,cAAc,MAAM,EAAE,EACtB,YAAY,OAAO,EACnB,qBAAqB,WAAW,KAC/B,aAQF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,eAAe,MAAM,EAAE,EACvB,YAAY,OAAO,EACnB,qBAAqB,WAAW,KAC/B,aAQF,CAAC;AAIF,eAAO,MAAM,eAAe,kDAkR1B,CAAC;AAEH,eAAe,eAAe,CAAC"}
|
|
@@ -14,7 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
14
14
|
import { memo, useCallback, useDeferredValue, useEffect, useMemo, useRef, useState, } from 'react';
|
|
15
15
|
import { tableFromIPC } from 'apache-arrow';
|
|
16
16
|
import { useContextMenu } from 'react-contexify';
|
|
17
|
-
import { useParcaContext } from '@parca/components';
|
|
17
|
+
import { FlameGraphSkeleton, SandwichFlameGraphSkeleton, useParcaContext } from '@parca/components';
|
|
18
18
|
import { USER_PREFERENCES, useCurrentColorProfile, useUserPreference } from '@parca/hooks';
|
|
19
19
|
import { getColorForFeature, selectDarkMode, useAppSelector } from '@parca/store';
|
|
20
20
|
import { useProfileFilters } from '../../ProfileView/components/ProfileFilters/useProfileFilters';
|
|
@@ -23,6 +23,7 @@ import ContextMenuWrapper from './ContextMenuWrapper';
|
|
|
23
23
|
import { FlameNode, RowHeight } from './FlameGraphNodes';
|
|
24
24
|
import { MemoizedTooltip } from './MemoizedTooltip';
|
|
25
25
|
import { TooltipProvider } from './TooltipContext';
|
|
26
|
+
import { useBatchedRendering } from './useBatchedRendering';
|
|
26
27
|
import { useScrollViewport } from './useScrollViewport';
|
|
27
28
|
import { useVisibleNodes } from './useVisibleNodes';
|
|
28
29
|
import { extractFeature, extractFilenameFeature, getCurrentPathFrameData, getMaxDepth, isCurrentPathFrameMatch, } from './utils';
|
|
@@ -64,7 +65,7 @@ export const getFilenameColors = (filenamesList, isDarkMode, currentColorProfile
|
|
|
64
65
|
return colors;
|
|
65
66
|
};
|
|
66
67
|
const noop = () => { };
|
|
67
|
-
export const FlameGraphArrow = memo(function FlameGraphArrow({ arrow, total, filtered, width, setCurPath, curPath, profileType, profileSource, compareAbsolute, isFlameChart = false, isRenderedAsFlamegraph = false, isInSandwichView = false, tooltipId = 'default', maxFrameCount, isExpanded = false, mappingsListFromMetadata, filenamesListFromMetadata, colorBy, }) {
|
|
68
|
+
export const FlameGraphArrow = memo(function FlameGraphArrow({ arrow, total, filtered, width, setCurPath, curPath, profileType, profileSource, compareAbsolute, isFlameChart = false, isRenderedAsFlamegraph = false, isInSandwichView = false, isHalfScreen, tooltipId = 'default', maxFrameCount, isExpanded = false, mappingsListFromMetadata, filenamesListFromMetadata, colorBy, }) {
|
|
68
69
|
const [highlightSimilarStacksPreference] = useUserPreference(USER_PREFERENCES.HIGHLIGHT_SIMILAR_STACKS.key);
|
|
69
70
|
const [hoveringRow, setHoveringRow] = useState(undefined);
|
|
70
71
|
const [dockedMetainfo] = useUserPreference(USER_PREFERENCES.GRAPH_METAINFO_DOCKED.key);
|
|
@@ -80,6 +81,7 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({ arrow, total, fil
|
|
|
80
81
|
const svg = useRef(null);
|
|
81
82
|
const containerRef = useRef(null);
|
|
82
83
|
const renderStartTime = useRef(0);
|
|
84
|
+
const hasInitialRenderCompleted = useRef(false);
|
|
83
85
|
const [svgElement, setSvgElement] = useState(null);
|
|
84
86
|
const { excludeBinary } = useProfileFilters();
|
|
85
87
|
const { compareMode } = useProfileViewContext();
|
|
@@ -179,6 +181,15 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({ arrow, total, fil
|
|
|
179
181
|
selectedRow,
|
|
180
182
|
effectiveDepth: deferredEffectiveDepth,
|
|
181
183
|
});
|
|
184
|
+
// Add nodes in incremental batches to avoid blocking the UI
|
|
185
|
+
const { items: batchedNodes, isComplete: isBatchingComplete } = useBatchedRendering(visibleNodes, {
|
|
186
|
+
batchSize: 500,
|
|
187
|
+
});
|
|
188
|
+
if (isBatchingComplete) {
|
|
189
|
+
hasInitialRenderCompleted.current = true;
|
|
190
|
+
}
|
|
191
|
+
// Show skeleton only during initial load, not during scroll updates
|
|
192
|
+
const showSkeleton = !hasInitialRenderCompleted.current && batchedNodes.length !== visibleNodes.length;
|
|
182
193
|
useEffect(() => {
|
|
183
194
|
if (perf?.markInteraction != null) {
|
|
184
195
|
renderStartTime.current = performance.now();
|
|
@@ -187,9 +198,10 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({ arrow, total, fil
|
|
|
187
198
|
useEffect(() => {
|
|
188
199
|
setSvgElement(svg.current);
|
|
189
200
|
}, [tooltipId]);
|
|
190
|
-
return (_jsx(TooltipProvider, { table: table, total: total, totalUnfiltered: total + filtered, profileType: profileType, unit: arrow.unit, compareAbsolute: compareAbsolute, tooltipId: tooltipId, children: _jsxs("div", { className: "relative", children: [_jsx(ContextMenuWrapper, { ref: contextMenuRef, menuId: MENU_ID, table: table, total: total, totalUnfiltered: total + filtered, compareAbsolute: compareAbsolute, resetPath: () => setCurPath([]), hideMenu: hideAll, hideBinary: hideBinary, unit: arrow.unit, profileType: profileType, isInSandwichView: isInSandwichView }), _jsx(MemoizedTooltip, { contextElement: svgElement, dockedMetainfo: dockedMetainfo }), _jsx("div", { ref: containerRef, className: "overflow-auto scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-100 dark:scrollbar-thumb-gray-600 dark:scrollbar-track-gray-800 will-change-transform scroll-smooth webkit-overflow-scrolling-touch contain", style: {
|
|
201
|
+
return (_jsx(TooltipProvider, { table: table, total: total, totalUnfiltered: total + filtered, profileType: profileType, unit: arrow.unit, compareAbsolute: compareAbsolute, tooltipId: tooltipId, children: _jsxs("div", { className: "relative", children: [_jsx(ContextMenuWrapper, { ref: contextMenuRef, menuId: MENU_ID, table: table, total: total, totalUnfiltered: total + filtered, compareAbsolute: compareAbsolute, resetPath: () => setCurPath([]), hideMenu: hideAll, hideBinary: hideBinary, unit: arrow.unit, profileType: profileType, isInSandwichView: isInSandwichView }), _jsx(MemoizedTooltip, { contextElement: svgElement, dockedMetainfo: dockedMetainfo }), showSkeleton && (_jsx("div", { className: "absolute inset-0 z-10", children: isRenderedAsFlamegraph ? (_jsx(SandwichFlameGraphSkeleton, { isHalfScreen: isHalfScreen, isDarkMode: isDarkMode })) : (_jsx(FlameGraphSkeleton, { isHalfScreen: isHalfScreen, isDarkMode: isDarkMode })) })), _jsx("div", { ref: containerRef, className: "overflow-auto scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-100 dark:scrollbar-thumb-gray-600 dark:scrollbar-track-gray-800 will-change-transform scroll-smooth webkit-overflow-scrolling-touch contain", style: {
|
|
191
202
|
width: width ?? '100%',
|
|
192
203
|
contain: 'layout style paint',
|
|
193
|
-
|
|
204
|
+
visibility: !showSkeleton ? 'visible' : 'hidden',
|
|
205
|
+
}, children: _jsx("svg", { className: "font-robotoMono", width: width ?? 0, height: totalHeight, preserveAspectRatio: "xMinYMid", ref: svg, children: batchedNodes.map(row => (_jsx(FlameNode, { table: table, row: row, colors: colorByColors, colorBy: colorByValue, totalWidth: width ?? 1, height: RowHeight, darkMode: isDarkMode, compareMode: compareMode, colorForSimilarNodes: colorForSimilarNodes, selectedRow: selectedRow, onClick: () => handleRowClick(row), onContextMenu: displayMenu, hoveringRow: highlightSimilarStacksPreference ? hoveringRow : undefined, setHoveringRow: highlightSimilarStacksPreference ? setHoveringRow : noop, isFlameChart: isFlameChart, profileSource: profileSource, isRenderedAsFlamegraph: isRenderedAsFlamegraph, isInSandwichView: isInSandwichView, maxDepth: maxDepth, effectiveDepth: deferredEffectiveDepth, tooltipId: tooltipId }, row))) }) })] }) }));
|
|
194
206
|
});
|
|
195
207
|
export default FlameGraphArrow;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface UseBatchedRenderingOptions {
|
|
2
|
+
batchSize?: number;
|
|
3
|
+
batchDelay?: number;
|
|
4
|
+
}
|
|
5
|
+
interface UseBatchedRenderingResult<T> {
|
|
6
|
+
items: T[];
|
|
7
|
+
isComplete: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const useBatchedRendering: <T>(items: T[], options?: UseBatchedRenderingOptions) => UseBatchedRenderingResult<T>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=useBatchedRendering.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBatchedRendering.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.ts"],"names":[],"mappings":"AAeA,UAAU,0BAA0B;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,yBAAyB,CAAC,CAAC;IACnC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,UAAU,EAAE,OAAO,CAAC;CACrB;AAGD,eAAO,MAAM,mBAAmB,GAAI,CAAC,EACnC,OAAO,CAAC,EAAE,EACV,UAAS,0BAA+B,KACvC,yBAAyB,CAAC,CAAC,CAqD7B,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { useEffect, useRef, useState } from 'react';
|
|
14
|
+
// useBatchedRendering - Helps in incrementally rendering items in batches to avoid UI blocking.
|
|
15
|
+
export const useBatchedRendering = (items, options = {}) => {
|
|
16
|
+
const { batchSize = 500, batchDelay = 0 } = options;
|
|
17
|
+
const [renderedCount, setRenderedCount] = useState(0);
|
|
18
|
+
const itemsRef = useRef(items);
|
|
19
|
+
const rafRef = useRef(null);
|
|
20
|
+
const timeoutRef = useRef(null);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (itemsRef.current !== items) {
|
|
23
|
+
itemsRef.current = items;
|
|
24
|
+
setRenderedCount(prev => {
|
|
25
|
+
if (items.length === 0)
|
|
26
|
+
return 0;
|
|
27
|
+
// If new items were added (scrolling down), keep current progress
|
|
28
|
+
if (items.length > prev)
|
|
29
|
+
return prev;
|
|
30
|
+
// If items reduced, cap to new length
|
|
31
|
+
return Math.min(prev, items.length);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}, [items]);
|
|
35
|
+
// Progressively render more items
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (renderedCount === items.length) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const scheduleNextBatch = () => {
|
|
41
|
+
const incrementState = () => {
|
|
42
|
+
setRenderedCount(prev => Math.min(prev + batchSize, items.length));
|
|
43
|
+
};
|
|
44
|
+
if (batchDelay > 0) {
|
|
45
|
+
timeoutRef.current = setTimeout(incrementState, batchDelay);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
rafRef.current = requestAnimationFrame(incrementState);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
scheduleNextBatch();
|
|
52
|
+
return () => {
|
|
53
|
+
if (rafRef.current !== null) {
|
|
54
|
+
cancelAnimationFrame(rafRef.current);
|
|
55
|
+
}
|
|
56
|
+
if (timeoutRef.current !== null) {
|
|
57
|
+
clearTimeout(timeoutRef.current);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}, [renderedCount, items.length, batchSize, batchDelay]);
|
|
61
|
+
return {
|
|
62
|
+
items: items.slice(0, renderedCount),
|
|
63
|
+
isComplete: renderedCount === items.length,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScrollViewport.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"useScrollViewport.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB;AAiBD,eAAO,MAAM,iBAAiB,GAAI,cAAc,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,KAAG,aAsFjF,CAAC"}
|
|
@@ -11,6 +11,21 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
14
|
+
// Find the scrollable ancestor (the element with overflow: auto/scroll)
|
|
15
|
+
const findScrollableParent = (element) => {
|
|
16
|
+
if (element === null)
|
|
17
|
+
return undefined;
|
|
18
|
+
let current = element.parentElement;
|
|
19
|
+
while (current !== null) {
|
|
20
|
+
const style = window.getComputedStyle(current);
|
|
21
|
+
const overflowY = style.overflowY;
|
|
22
|
+
if (overflowY === 'auto' || overflowY === 'scroll') {
|
|
23
|
+
return current;
|
|
24
|
+
}
|
|
25
|
+
current = current.parentElement;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
};
|
|
14
29
|
export const useScrollViewport = (containerRef) => {
|
|
15
30
|
const [viewport, setViewport] = useState({
|
|
16
31
|
scrollTop: 0,
|
|
@@ -22,10 +37,22 @@ export const useScrollViewport = (containerRef) => {
|
|
|
22
37
|
const updateViewport = useCallback(() => {
|
|
23
38
|
if (containerRef.current !== null) {
|
|
24
39
|
const container = containerRef.current;
|
|
40
|
+
const rect = container.getBoundingClientRect();
|
|
41
|
+
// Restrict container height to the visible portion on screen
|
|
42
|
+
// This handles cases where the container is partially off-screen
|
|
43
|
+
// We only want to consider the visible part for culling calculations
|
|
44
|
+
const containerTop = rect.top;
|
|
45
|
+
const containerBottom = rect.bottom;
|
|
46
|
+
const viewportTop = 0;
|
|
47
|
+
const viewportBottom = window.innerHeight;
|
|
48
|
+
const visibleTop = Math.max(containerTop, viewportTop);
|
|
49
|
+
const visibleBottom = Math.min(containerBottom, viewportBottom);
|
|
50
|
+
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
51
|
+
const scrollOffset = Math.max(0, viewportTop - containerTop);
|
|
25
52
|
const newViewport = {
|
|
26
|
-
scrollTop:
|
|
53
|
+
scrollTop: scrollOffset,
|
|
27
54
|
scrollLeft: container.scrollLeft,
|
|
28
|
-
containerHeight:
|
|
55
|
+
containerHeight: visibleHeight, // Only the visible portion
|
|
29
56
|
containerWidth: container.clientWidth,
|
|
30
57
|
};
|
|
31
58
|
setViewport(newViewport);
|
|
@@ -44,22 +71,25 @@ export const useScrollViewport = (containerRef) => {
|
|
|
44
71
|
const container = containerRef.current;
|
|
45
72
|
if (container === null)
|
|
46
73
|
return;
|
|
74
|
+
const scrollableParent = findScrollableParent(container);
|
|
47
75
|
// ResizeObserver Strategy:
|
|
48
76
|
// Monitor container size changes (window resize, layout shifts)
|
|
49
77
|
// to update viewport dimensions for accurate culling calculations
|
|
50
78
|
const resizeObserver = new ResizeObserver(() => {
|
|
51
79
|
throttledUpdateViewport();
|
|
52
80
|
});
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
// Throttle with requestAnimationFrame to maintain 60fps target
|
|
81
|
+
// Listen to scroll on the actual scrollable parent
|
|
82
|
+
scrollableParent?.addEventListener('scroll', throttledUpdateViewport, { passive: true });
|
|
56
83
|
container.addEventListener('scroll', throttledUpdateViewport, { passive: true });
|
|
84
|
+
window.addEventListener('scroll', throttledUpdateViewport, { passive: true });
|
|
57
85
|
resizeObserver.observe(container);
|
|
58
86
|
// Initialize viewport state on mount
|
|
59
87
|
updateViewport();
|
|
60
88
|
return () => {
|
|
61
89
|
// Cleanup: Remove event listeners and cancel pending animations
|
|
90
|
+
scrollableParent?.removeEventListener('scroll', throttledUpdateViewport);
|
|
62
91
|
container.removeEventListener('scroll', throttledUpdateViewport);
|
|
92
|
+
window.removeEventListener('scroll', throttledUpdateViewport);
|
|
63
93
|
resizeObserver.disconnect();
|
|
64
94
|
if (throttleRef.current !== null) {
|
|
65
95
|
cancelAnimationFrame(throttleRef.current);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVisibleNodes.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,KAAK,EAAC,MAAM,cAAc,CAAC;AAInC,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAiClD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,QAAQ,EAAE,aAAa,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAAI,iEAO7B,qBAAqB,KAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"useVisibleNodes.d.ts","sourceRoot":"","sources":["../../../src/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,KAAK,EAAC,MAAM,cAAc,CAAC;AAInC,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAiClD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,QAAQ,EAAE,aAAa,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAAI,iEAO7B,qBAAqB,KAAG,MAAM,EA4HhC,CAAC"}
|
|
@@ -53,22 +53,48 @@ const useDepthBuckets = (table) => {
|
|
|
53
53
|
export const useVisibleNodes = ({ table, viewport, total, width, selectedRow, effectiveDepth, }) => {
|
|
54
54
|
const depthBuckets = useDepthBuckets(table);
|
|
55
55
|
const lastResultRef = useRef({ key: '', result: [] });
|
|
56
|
+
const renderedRangeRef = useRef({
|
|
57
|
+
minDepth: Infinity,
|
|
58
|
+
maxDepth: -Infinity,
|
|
59
|
+
table: null,
|
|
60
|
+
});
|
|
56
61
|
return useMemo(() => {
|
|
62
|
+
// This happens when the continer is scrolled off screen, in this case we return all previously rendered nodes
|
|
63
|
+
// to avoid trimming the rendered nodes to zero which would cause jank when scrolling back into view
|
|
64
|
+
if (viewport.containerHeight === 0 && lastResultRef.current.result.length > 0) {
|
|
65
|
+
return lastResultRef.current.result;
|
|
66
|
+
}
|
|
57
67
|
// Create a stable key for memoization to prevent unnecessary recalculations
|
|
58
68
|
const memoKey = `${viewport.scrollTop}-${viewport.containerHeight}-${selectedRow}-${effectiveDepth}-${width}-${Number(total)}-${table.numRows}`;
|
|
59
69
|
// Return cached result if viewport hasn't meaningfully changed
|
|
60
70
|
if (lastResultRef.current.key === memoKey) {
|
|
61
71
|
return lastResultRef.current.result;
|
|
62
72
|
}
|
|
63
|
-
if (table === null
|
|
73
|
+
if (table === null)
|
|
64
74
|
return [];
|
|
65
75
|
const visibleRows = [];
|
|
66
76
|
const { scrollTop, containerHeight } = viewport;
|
|
67
77
|
// Viewport Culling Algorithm:
|
|
68
78
|
// 1. Calculate visible depth range based on scroll position and container height
|
|
69
79
|
// 2. Add 5-row buffer above/below for smooth scrolling experience
|
|
70
|
-
|
|
71
|
-
const
|
|
80
|
+
// Note: We never shrink the rendered range to avoid back and forth node removals (and in turn additions when scrolled down again) to the dom.
|
|
81
|
+
const BUFFER = 15; // Buffer for smoother scrolling
|
|
82
|
+
const visibleStartDepth = Math.max(0, Math.floor(scrollTop / RowHeight) - BUFFER);
|
|
83
|
+
const visibleDepths = Math.ceil(containerHeight / RowHeight);
|
|
84
|
+
const visibleEndDepth = Math.min(effectiveDepth, visibleStartDepth + visibleDepths + BUFFER);
|
|
85
|
+
// Reset range if table changed (new data loaded) as this is new data
|
|
86
|
+
if (renderedRangeRef.current.table !== table) {
|
|
87
|
+
renderedRangeRef.current = {
|
|
88
|
+
minDepth: Infinity,
|
|
89
|
+
maxDepth: -Infinity,
|
|
90
|
+
table: table,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// Expand the rendered range (never shrink when scrolling up/down)
|
|
94
|
+
renderedRangeRef.current.minDepth = Math.min(renderedRangeRef.current.minDepth, visibleStartDepth);
|
|
95
|
+
renderedRangeRef.current.maxDepth = Math.max(renderedRangeRef.current.maxDepth, visibleEndDepth);
|
|
96
|
+
const startDepth = renderedRangeRef.current.minDepth;
|
|
97
|
+
const endDepth = renderedRangeRef.current.maxDepth;
|
|
72
98
|
const cumulativeColumn = table.getChild(FIELD_CUMULATIVE);
|
|
73
99
|
const valueOffsetColumn = table.getChild(FIELD_VALUE_OFFSET);
|
|
74
100
|
const selectionOffset = valueOffsetColumn?.get(selectedRow) !== null &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLabels.d.ts","sourceRoot":"","sources":["../../src/hooks/useLabels.ts"],"names":[],"mappings":"AAeA,OAAO,EAAgB,cAAc,EAAE,kBAAkB,EAAgB,MAAM,eAAe,CAAC;AAM/F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,aAAa;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,eAAO,MAAM,aAAa,GACxB,QAAQ,kBAAkB,EAC1B,aAAa,MAAM,EACnB,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,QAAQ,MAAM,EAAE,KACf,aAkCF,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,QAAQ,kBAAkB,EAC1B,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,QAAQ,MAAM,EACd,MAAM,MAAM,KACX,
|
|
1
|
+
{"version":3,"file":"useLabels.d.ts","sourceRoot":"","sources":["../../src/hooks/useLabels.ts"],"names":[],"mappings":"AAeA,OAAO,EAAgB,cAAc,EAAE,kBAAkB,EAAgB,MAAM,eAAe,CAAC;AAM/F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,aAAa;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,eAAO,MAAM,aAAa,GACxB,QAAQ,kBAAkB,EAC1B,aAAa,MAAM,EACnB,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,QAAQ,MAAM,EAAE,KACf,aAkCF,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,QAAQ,kBAAkB,EAC1B,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,QAAQ,MAAM,EACd,MAAM,MAAM,KACX,cAmCF,CAAC"}
|
package/dist/hooks/useLabels.js
CHANGED
|
@@ -67,7 +67,9 @@ export const useLabelValues = (client, labelName, profileType, start, end) => {
|
|
|
67
67
|
keepPreviousData: false,
|
|
68
68
|
},
|
|
69
69
|
});
|
|
70
|
-
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
console.log('Label values query result:', { data, error, isLoading, labelName });
|
|
72
|
+
}, [data, error, isLoading, labelName]);
|
|
71
73
|
return {
|
|
72
74
|
result: { response: data ?? [], error: error },
|
|
73
75
|
loading: isLoading,
|
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}.collapse{visibility:collapse}.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}.right-0{right:0}.top-0{top:0}.left-0{left:0}.bottom-3{bottom:.75rem}.top-\[-5px\]{top:-5px}.top-\[-2px\]{top:-2px}.bottom-0{bottom:0}.left-\[-7px\]{left:-7px}.right-\[-7px\]{right:-7px}.right-8{right:2rem}.top-\[-46px\]{top:-46px}.right-full{right:100%}.left-full{left:100%}.top-\[-60px\]{top:-60px}.left-1\/2{left:50%}.top-full{top:100%}.z-10{z-index:10}.z-50{z-index:50}.z-30{z-index:30}.z-\[5\]{z-index:5}.z-20{z-index:20}.m-auto{margin:auto}.m-2{margin:.5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-0{margin-bottom:0;margin-top:0}.my-20{margin-bottom:5rem;margin-top:5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-4{margin-bottom:1rem;margin-top:1rem}.mb-2{margin-bottom:.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.ml-\[70px\]{margin-left:70px}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.ml-auto{margin-left:auto}.mt-auto{margin-top:auto}.mb-0\.5{margin-bottom:.125rem}.mt-1\.5{margin-top:.375rem}.mb-0{margin-bottom:0}.mr-2{margin-right:.5rem}.ml-3{margin-left:.75rem}.mt-2{margin-top:.5rem}.mb-px{margin-bottom:1px}.mb-1{margin-bottom:.25rem}.mr-1{margin-right:.25rem}.mt-4{margin-top:1rem}.mb-4{margin-bottom:1rem}.mt-3{margin-top:.75rem}.mt-8{margin-top:2rem}.mt-0{margin-top:0}.ml-1{margin-left:.25rem}.mt-5{margin-top:1.25rem}.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-\[38px\]{height:38px}.h-auto{height:auto}.h-full{height:100%}.h-1{height:.25rem}.h-\[20px\]{height:20px}.h-\[16px\]{height:16px}.h-4{height:1rem}.h-\[30px\]{height:30px}.h-\[45px\]{height:45px}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[700px\]{height:700px}.h-\[80vh\]{height:80vh}.h-\[1px\]{height:1px}.h-\[14px\]{height:14px}.h-\[24px\]{height:24px}.h-0{height:0}.max-h-\[400px\]{max-height:400px}.max-h-\[50vh\]{max-height:50vh}.max-h-\[300px\]{max-height:300px}.min-h-10{min-height:2.5rem}.min-h-0{min-height:0}.min-h-5{min-height:1.25rem}.min-h-48{min-height:12rem}.min-h-\[50px\]{min-height:50px}.min-h-\[700px\]{min-height:700px}.min-h-96{min-height:24rem}.w-full{width:100%}.w-auto{width:auto}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-max{width:-moz-max-content;width:max-content}.w-40{width:10rem}.w-\[44px\]{width:44px}.w-\[16px\]{width:16px}.w-4{width:1rem}.w-\[270px\]{width:270px}.w-\[300px\]{width:300px}.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-56{width:14rem}.w-\[350px\]{width:350px}.w-\[260px\]{width:260px}.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-8{width:2rem}.w-\[50\%\]{width:50%}.w-32{width:8rem}.w-36{width:9rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.w-48{width:12rem}.w-\[14px\]{width:14px}.w-64{width:16rem}.w-80{width:20rem}.w-\[20px\]{width:20px}.w-\[246px\]{width:246px}.w-52{width:13rem}.w-0{width:0}.min-w-\[300px\]{min-width:300px}.min-w-\[400px\]{min-width:400px}.min-w-\[350px\]{min-width:350px}.min-w-\[260px\]{min-width:260px}.max-w-\[600px\]{max-width:600px}.max-w-\[500px\]{max-width:500px}.max-w-md{max-width:28rem}.max-w-80{max-width:20rem}.max-w-full{max-width:100%}.max-w-48{max-width:12rem}.max-w-\[400px\]{max-width:400px}.max-w-lg{max-width:32rem}.flex-1{flex:1 1 0%}.flex-shrink-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-right{transform-origin:top right}.origin-top-left{transform-origin:top left}.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}.translate-x-5{--tw-translate-x:1.25rem}.-translate-x-1\/2,.translate-x-5{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-1\/2{--tw-translate-x:-50%}.rotate-90{--tw-rotate:90deg}.-rotate-180,.rotate-90{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))}.-rotate-180{--tw-rotate:-180deg}.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-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-ew-resize{cursor:ew-resize}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.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-\[2px\]{gap:2px}.gap-2{gap:.5rem}.gap-6{gap:1.5rem}.gap-3{gap:.75rem}.gap-0{gap:0}.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)))}.self-start{align-self:flex-start}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-clip{overflow:clip}.overflow-scroll{overflow:scroll}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.scroll-smooth{scroll-behavior:smooth}.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-sm{border-radius:.125rem}.rounded-full{border-radius:9999px}.rounded-none{border-radius:0}.rounded-\[4px\]{border-radius:4px}.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-b{border-bottom-right-radius:.25rem}.rounded-b,.rounded-l{border-bottom-left-radius:.25rem}.rounded-l{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-2{border-left-width:2px;border-right-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-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-l-4{border-left-width:4px}.border-r-4{border-right-width:4px}.border-t-4{border-top-width:4px}.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-gray-900{--tw-border-opacity:1;border-color:rgb(17 24 39/var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/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))}.border-l-transparent{border-left-color:transparent}.border-r-transparent{border-right-color:transparent}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/50{background-color:hsla(0,0%,100%,.5)}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-amber-100{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity))}.bg-amber-600{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/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-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-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}.bg-gray-500\/50{background-color:hsla(220,9%,46%,.5)}.bg-gray-700\/75{background-color:rgba(55,65,81,.75)}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/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-inherit{background-color:inherit}.bg-indigo-400{--tw-bg-opacity:1;background-color:rgb(129 140 248/var(--tw-bg-opacity))}.bg-opacity-75{--tw-bg-opacity:0.75}.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-3{padding:.75rem}.p-2{padding:.5rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-1{padding:.25rem}.p-0{padding:0}.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-1{padding-left:.25rem;padding-right:.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-bottom:0;padding-top:0}.\!px-3{padding-left:.75rem!important;padding-right:.75rem!important}.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}.pr-3{padding-right:.75rem}.pl-2{padding-left:.5rem}.pr-4{padding-right:1rem}.pl-1{padding-left:.25rem}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pb-2{padding-bottom:.5rem}.pb-\[10px\]{padding-bottom:10px}.pr-1{padding-right:.25rem}.pr-\[1\.7rem\]{padding-right:1.7rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{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}.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-lg{font-size:1.125rem}.text-lg,.text-xl{line-height:1.75rem}.text-xl{font-size:1.25rem}.text-\[10px\]{font-size:10px}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.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-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/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-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-100{opacity:1}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-80{opacity:.8}.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-\[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-\[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-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,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)}.will-change-transform{will-change:transform}.\[stroke-dasharray\:6\2c 4\]{stroke-dasharray:6,4}.\[stroke-linecap\:round\]{stroke-linecap:round}.\[stroke-linejoin\:round\]{stroke-linejoin:round}.\[writing-mode\:vertical-lr\]{writing-mode:vertical-lr}.checked\:border-indigo-600:checked{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity))}.checked\:bg-indigo-600:checked{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:whitespace-normal:hover{white-space:normal}.hover\:bg-amber-700:hover{--tw-bg-opacity:1;background-color:rgb(180 83 9/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-indigo-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:relative:focus{position:relative}.focus\:z-50:focus{z-index:50}.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\:outline-1:focus{outline-width:1px}.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\:ring-offset-0:focus{--tw-ring-offset-width:0px}.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-indigo-500:focus-visible{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/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-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/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-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-black\/50{background-color:rgba(0,0,0,.5)}[class~=theme-dark] .dark\:bg-amber-900{--tw-bg-opacity:1;background-color:rgb(120 53 15/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-amber-700{--tw-bg-opacity:1;background-color:rgb(180 83 9/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-100\/90{background-color:rgba(243,244,246,.9)}[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-gray-200\/75{background-color:rgba(229,231,235,.75)}[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-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-amber-200{--tw-text-opacity:1;color:rgb(253 230 138/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-amber-600:hover{--tw-bg-opacity:1;background-color:rgb(217 119 6/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}.collapse{visibility:collapse}.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-0{left:0;right:0}.inset-0,.inset-y-0{bottom:0;top:0}.right-0{right:0}.top-0{top:0}.left-0{left:0}.bottom-3{bottom:.75rem}.top-\[-5px\]{top:-5px}.top-\[-2px\]{top:-2px}.bottom-0{bottom:0}.left-\[-7px\]{left:-7px}.right-\[-7px\]{right:-7px}.right-8{right:2rem}.top-\[-46px\]{top:-46px}.right-full{right:100%}.left-full{left:100%}.top-\[-60px\]{top:-60px}.left-1\/2{left:50%}.top-full{top:100%}.z-10{z-index:10}.z-50{z-index:50}.z-30{z-index:30}.z-\[5\]{z-index:5}.z-20{z-index:20}.m-auto{margin:auto}.m-2{margin:.5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-0{margin-bottom:0;margin-top:0}.my-20{margin-bottom:5rem;margin-top:5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-4{margin-bottom:1rem;margin-top:1rem}.mb-2{margin-bottom:.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.ml-\[70px\]{margin-left:70px}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.ml-auto{margin-left:auto}.mt-auto{margin-top:auto}.mb-0\.5{margin-bottom:.125rem}.mt-1\.5{margin-top:.375rem}.mb-0{margin-bottom:0}.mr-2{margin-right:.5rem}.ml-3{margin-left:.75rem}.mt-2{margin-top:.5rem}.mb-px{margin-bottom:1px}.mb-1{margin-bottom:.25rem}.mr-1{margin-right:.25rem}.mt-4{margin-top:1rem}.mb-4{margin-bottom:1rem}.mt-3{margin-top:.75rem}.mt-8{margin-top:2rem}.mt-0{margin-top:0}.ml-1{margin-left:.25rem}.mt-5{margin-top:1.25rem}.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-\[38px\]{height:38px}.h-auto{height:auto}.h-full{height:100%}.h-1{height:.25rem}.h-\[20px\]{height:20px}.h-\[16px\]{height:16px}.h-4{height:1rem}.h-\[30px\]{height:30px}.h-\[45px\]{height:45px}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[700px\]{height:700px}.h-\[80vh\]{height:80vh}.h-\[1px\]{height:1px}.h-\[14px\]{height:14px}.h-\[24px\]{height:24px}.h-0{height:0}.max-h-\[400px\]{max-height:400px}.max-h-\[50vh\]{max-height:50vh}.max-h-\[300px\]{max-height:300px}.min-h-10{min-height:2.5rem}.min-h-0{min-height:0}.min-h-5{min-height:1.25rem}.min-h-48{min-height:12rem}.min-h-\[50px\]{min-height:50px}.min-h-\[700px\]{min-height:700px}.min-h-96{min-height:24rem}.w-full{width:100%}.w-auto{width:auto}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-max{width:-moz-max-content;width:max-content}.w-40{width:10rem}.w-\[44px\]{width:44px}.w-\[16px\]{width:16px}.w-4{width:1rem}.w-\[270px\]{width:270px}.w-\[300px\]{width:300px}.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-56{width:14rem}.w-\[350px\]{width:350px}.w-\[260px\]{width:260px}.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-8{width:2rem}.w-\[50\%\]{width:50%}.w-32{width:8rem}.w-36{width:9rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.w-48{width:12rem}.w-\[14px\]{width:14px}.w-64{width:16rem}.w-80{width:20rem}.w-\[20px\]{width:20px}.w-\[246px\]{width:246px}.w-52{width:13rem}.w-0{width:0}.min-w-\[300px\]{min-width:300px}.min-w-\[400px\]{min-width:400px}.min-w-\[350px\]{min-width:350px}.min-w-\[260px\]{min-width:260px}.max-w-\[600px\]{max-width:600px}.max-w-\[500px\]{max-width:500px}.max-w-md{max-width:28rem}.max-w-80{max-width:20rem}.max-w-full{max-width:100%}.max-w-48{max-width:12rem}.max-w-\[400px\]{max-width:400px}.max-w-lg{max-width:32rem}.flex-1{flex:1 1 0%}.flex-shrink-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-right{transform-origin:top right}.origin-top-left{transform-origin:top left}.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}.translate-x-5{--tw-translate-x:1.25rem}.-translate-x-1\/2,.translate-x-5{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-1\/2{--tw-translate-x:-50%}.rotate-90{--tw-rotate:90deg}.-rotate-180,.rotate-90{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))}.-rotate-180{--tw-rotate:-180deg}.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-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-ew-resize{cursor:ew-resize}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.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-\[2px\]{gap:2px}.gap-2{gap:.5rem}.gap-6{gap:1.5rem}.gap-3{gap:.75rem}.gap-0{gap:0}.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)))}.self-start{align-self:flex-start}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-clip{overflow:clip}.overflow-scroll{overflow:scroll}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.scroll-smooth{scroll-behavior:smooth}.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-sm{border-radius:.125rem}.rounded-full{border-radius:9999px}.rounded-none{border-radius:0}.rounded-\[4px\]{border-radius:4px}.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-b{border-bottom-right-radius:.25rem}.rounded-b,.rounded-l{border-bottom-left-radius:.25rem}.rounded-l{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-2{border-left-width:2px;border-right-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-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-l-4{border-left-width:4px}.border-r-4{border-right-width:4px}.border-t-4{border-top-width:4px}.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-gray-900{--tw-border-opacity:1;border-color:rgb(17 24 39/var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/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))}.border-l-transparent{border-left-color:transparent}.border-r-transparent{border-right-color:transparent}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/50{background-color:hsla(0,0%,100%,.5)}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-amber-100{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity))}.bg-amber-600{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/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-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-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}.bg-gray-500\/50{background-color:hsla(220,9%,46%,.5)}.bg-gray-700\/75{background-color:rgba(55,65,81,.75)}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/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-inherit{background-color:inherit}.bg-indigo-400{--tw-bg-opacity:1;background-color:rgb(129 140 248/var(--tw-bg-opacity))}.bg-opacity-75{--tw-bg-opacity:0.75}.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-3{padding:.75rem}.p-2{padding:.5rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-1{padding:.25rem}.p-0{padding:0}.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-1{padding-left:.25rem;padding-right:.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-bottom:0;padding-top:0}.\!px-3{padding-left:.75rem!important;padding-right:.75rem!important}.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}.pr-3{padding-right:.75rem}.pl-2{padding-left:.5rem}.pr-4{padding-right:1rem}.pl-1{padding-left:.25rem}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pb-2{padding-bottom:.5rem}.pb-\[10px\]{padding-bottom:10px}.pr-1{padding-right:.25rem}.pr-\[1\.7rem\]{padding-right:1.7rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{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}.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-lg{font-size:1.125rem}.text-lg,.text-xl{line-height:1.75rem}.text-xl{font-size:1.25rem}.text-\[10px\]{font-size:10px}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.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-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/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-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-100{opacity:1}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-80{opacity:.8}.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-\[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-\[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-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,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)}.will-change-transform{will-change:transform}.\[stroke-dasharray\:6\2c 4\]{stroke-dasharray:6,4}.\[stroke-linecap\:round\]{stroke-linecap:round}.\[stroke-linejoin\:round\]{stroke-linejoin:round}.\[writing-mode\:vertical-lr\]{writing-mode:vertical-lr}.checked\:border-indigo-600:checked{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity))}.checked\:bg-indigo-600:checked{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:whitespace-normal:hover{white-space:normal}.hover\:bg-amber-700:hover{--tw-bg-opacity:1;background-color:rgb(180 83 9/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-indigo-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:relative:focus{position:relative}.focus\:z-50:focus{z-index:50}.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\:outline-1:focus{outline-width:1px}.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\:ring-offset-0:focus{--tw-ring-offset-width:0px}.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-indigo-500:focus-visible{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/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-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/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-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-black\/50{background-color:rgba(0,0,0,.5)}[class~=theme-dark] .dark\:bg-amber-900{--tw-bg-opacity:1;background-color:rgb(120 53 15/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-amber-700{--tw-bg-opacity:1;background-color:rgb(180 83 9/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-100\/90{background-color:rgba(243,244,246,.9)}[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-gray-200\/75{background-color:rgba(229,231,235,.75)}[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-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-amber-200{--tw-text-opacity:1;color:rgb(253 230 138/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-amber-600:hover{--tw-bg-opacity:1;background-color:rgb(217 119 6/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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.103",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@floating-ui/react": "^0.27.12",
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"access": "public",
|
|
85
85
|
"registry": "https://registry.npmjs.org/"
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "14996d0bb0fdf1193c1555e4d171aefd5138303b"
|
|
88
88
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import React, {useMemo} from 'react';
|
|
14
|
+
import React, {useCallback, useMemo} from 'react';
|
|
15
15
|
|
|
16
16
|
import {Table} from 'apache-arrow';
|
|
17
17
|
import cx from 'classnames';
|
|
@@ -101,36 +101,52 @@ export const FlameNode = React.memo(
|
|
|
101
101
|
effectiveDepth,
|
|
102
102
|
tooltipId = 'default',
|
|
103
103
|
}: FlameNodeProps): React.JSX.Element {
|
|
104
|
-
//
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
104
|
+
// Memoize column references - only changes when table changes
|
|
105
|
+
const columns = useMemo(
|
|
106
|
+
() => ({
|
|
107
|
+
mapping: table.getChild(FIELD_MAPPING_FILE),
|
|
108
|
+
functionName: table.getChild(FIELD_FUNCTION_NAME),
|
|
109
|
+
cumulative: table.getChild(FIELD_CUMULATIVE),
|
|
110
|
+
depth: table.getChild(FIELD_DEPTH),
|
|
111
|
+
diff: table.getChild(FIELD_DIFF),
|
|
112
|
+
filename: table.getChild(FIELD_FUNCTION_FILE_NAME),
|
|
113
|
+
valueOffset: table.getChild(FIELD_VALUE_OFFSET),
|
|
114
|
+
ts: table.getChild(FIELD_TIMESTAMP),
|
|
115
|
+
}),
|
|
116
|
+
[table]
|
|
117
|
+
);
|
|
113
118
|
|
|
114
119
|
// get the actual values from the columns
|
|
115
120
|
const binaries = useAppSelector(selectBinaries);
|
|
116
121
|
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
// Memoize row data extraction - only changes when table or row changes
|
|
123
|
+
const rowData = useMemo(() => {
|
|
124
|
+
const mappingFile: string | null = arrowToString(columns.mapping?.get(row));
|
|
125
|
+
const functionName: string | null = arrowToString(columns.functionName?.get(row));
|
|
126
|
+
const cumulative =
|
|
127
|
+
columns.cumulative?.get(row) != null ? BigInt(columns.cumulative?.get(row)) : 0n;
|
|
128
|
+
const diff: bigint | null =
|
|
129
|
+
columns.diff?.get(row) != null ? BigInt(columns.diff?.get(row)) : null;
|
|
130
|
+
const filename: string | null = arrowToString(columns.filename?.get(row));
|
|
131
|
+
const depth: number = columns.depth?.get(row) ?? 0;
|
|
132
|
+
const valueOffset: bigint =
|
|
133
|
+
columns.valueOffset?.get(row) !== null && columns.valueOffset?.get(row) !== undefined
|
|
134
|
+
? BigInt(columns.valueOffset?.get(row))
|
|
135
|
+
: 0n;
|
|
136
|
+
|
|
137
|
+
return {mappingFile, functionName, cumulative, diff, filename, depth, valueOffset};
|
|
138
|
+
}, [columns, row]);
|
|
139
|
+
|
|
140
|
+
const {mappingFile, functionName, cumulative, diff, filename, depth, valueOffset} = rowData;
|
|
128
141
|
|
|
129
142
|
const colorAttribute =
|
|
130
143
|
colorBy === 'filename' ? filename : colorBy === 'binary' ? mappingFile : null;
|
|
131
144
|
|
|
132
|
-
|
|
133
|
-
|
|
145
|
+
// Memoize hovering name lookup
|
|
146
|
+
const hoveringName = useMemo(() => {
|
|
147
|
+
return hoveringRow !== undefined ? arrowToString(columns.functionName?.get(hoveringRow)) : '';
|
|
148
|
+
}, [columns.functionName, hoveringRow]);
|
|
149
|
+
|
|
134
150
|
const shouldBeHighlighted =
|
|
135
151
|
functionName != null && hoveringName != null && functionName === hoveringName;
|
|
136
152
|
|
|
@@ -147,18 +163,59 @@ export const FlameNode = React.memo(
|
|
|
147
163
|
return row === 0 ? 'root' : nodeLabel(table, row, binaries.length > 1);
|
|
148
164
|
}, [table, row, binaries]);
|
|
149
165
|
|
|
166
|
+
// Memoize selection data - only changes when selectedRow changes
|
|
167
|
+
const selectionData = useMemo(() => {
|
|
168
|
+
const selectionOffset =
|
|
169
|
+
columns.valueOffset?.get(selectedRow) !== null &&
|
|
170
|
+
columns.valueOffset?.get(selectedRow) !== undefined
|
|
171
|
+
? BigInt(columns.valueOffset?.get(selectedRow))
|
|
172
|
+
: 0n;
|
|
173
|
+
const selectionCumulative =
|
|
174
|
+
columns.cumulative?.get(selectedRow) !== null
|
|
175
|
+
? BigInt(columns.cumulative?.get(selectedRow))
|
|
176
|
+
: 0n;
|
|
177
|
+
const selectedDepth = columns.depth?.get(selectedRow);
|
|
178
|
+
const total = columns.cumulative?.get(selectedRow);
|
|
179
|
+
return {selectionOffset, selectionCumulative, selectedDepth, total};
|
|
180
|
+
}, [columns, selectedRow]);
|
|
181
|
+
|
|
182
|
+
const {selectionOffset, selectionCumulative, selectedDepth, total} = selectionData;
|
|
183
|
+
|
|
184
|
+
// Memoize tsBounds - only changes when profileSource changes
|
|
185
|
+
const tsBounds = useMemo(() => boundsFromProfileSource(profileSource), [profileSource]);
|
|
186
|
+
|
|
187
|
+
// Memoize event handlers
|
|
188
|
+
const onMouseEnter = useCallback((): void => {
|
|
189
|
+
setHoveringRow(row);
|
|
190
|
+
window.dispatchEvent(
|
|
191
|
+
new CustomEvent(`flame-tooltip-update-${tooltipId}`, {
|
|
192
|
+
detail: {row},
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
}, [setHoveringRow, row, tooltipId]);
|
|
196
|
+
|
|
197
|
+
const onMouseLeave = useCallback((): void => {
|
|
198
|
+
setHoveringRow(undefined);
|
|
199
|
+
window.dispatchEvent(
|
|
200
|
+
new CustomEvent(`flame-tooltip-update-${tooltipId}`, {
|
|
201
|
+
detail: {row: null},
|
|
202
|
+
})
|
|
203
|
+
);
|
|
204
|
+
}, [setHoveringRow, tooltipId]);
|
|
205
|
+
|
|
206
|
+
const handleContextMenu = useCallback(
|
|
207
|
+
(e: React.MouseEvent): void => {
|
|
208
|
+
onContextMenu(e, row);
|
|
209
|
+
},
|
|
210
|
+
[onContextMenu, row]
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// Early returns - all hooks must be called before this point
|
|
150
214
|
// Hide frames beyond effective depth limit
|
|
151
215
|
if (effectiveDepth !== undefined && depth > effectiveDepth) {
|
|
152
216
|
return <></>;
|
|
153
217
|
}
|
|
154
218
|
|
|
155
|
-
const selectionOffset =
|
|
156
|
-
valueOffsetColumn?.get(selectedRow) !== null &&
|
|
157
|
-
valueOffsetColumn?.get(selectedRow) !== undefined
|
|
158
|
-
? BigInt(valueOffsetColumn?.get(selectedRow))
|
|
159
|
-
: 0n;
|
|
160
|
-
const selectionCumulative =
|
|
161
|
-
cumulativeColumn?.get(selectedRow) !== null ? BigInt(cumulativeColumn?.get(selectedRow)) : 0n;
|
|
162
219
|
if (
|
|
163
220
|
valueOffset + cumulative <= selectionOffset ||
|
|
164
221
|
valueOffset >= selectionOffset + selectionCumulative
|
|
@@ -173,8 +230,6 @@ export const FlameNode = React.memo(
|
|
|
173
230
|
}
|
|
174
231
|
|
|
175
232
|
// Cumulative can be larger than total when a selection is made. All parents of the selection are likely larger, but we want to only show them as 100% in the graph.
|
|
176
|
-
const tsBounds = boundsFromProfileSource(profileSource);
|
|
177
|
-
const total = cumulativeColumn?.get(selectedRow);
|
|
178
233
|
const totalRatio = cumulative > total ? 1 : Number(cumulative) / Number(total);
|
|
179
234
|
const width: number = isFlameChart
|
|
180
235
|
? (Number(cumulative) / (Number(tsBounds[1]) - Number(tsBounds[0]))) * totalWidth
|
|
@@ -184,35 +239,12 @@ export const FlameNode = React.memo(
|
|
|
184
239
|
return <></>;
|
|
185
240
|
}
|
|
186
241
|
|
|
187
|
-
const selectedDepth = depthColumn?.get(selectedRow);
|
|
188
242
|
const styles =
|
|
189
243
|
selectedDepth !== undefined && selectedDepth > depth ? fadedFlameRectStyles : flameRectStyles;
|
|
190
244
|
|
|
191
|
-
const
|
|
192
|
-
setHoveringRow(row);
|
|
193
|
-
window.dispatchEvent(
|
|
194
|
-
new CustomEvent(`flame-tooltip-update-${tooltipId}`, {
|
|
195
|
-
detail: {row},
|
|
196
|
-
})
|
|
197
|
-
);
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
const onMouseLeave = (): void => {
|
|
201
|
-
setHoveringRow(undefined);
|
|
202
|
-
window.dispatchEvent(
|
|
203
|
-
new CustomEvent(`flame-tooltip-update-${tooltipId}`, {
|
|
204
|
-
detail: {row: null},
|
|
205
|
-
})
|
|
206
|
-
);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
const handleContextMenu = (e: React.MouseEvent): void => {
|
|
210
|
-
onContextMenu(e, row);
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const ts = tsColumn !== null ? Number(tsColumn.get(row)) : 0;
|
|
245
|
+
const ts = columns.ts !== null ? Number(columns.ts.get(row)) : 0;
|
|
214
246
|
const x =
|
|
215
|
-
isFlameChart &&
|
|
247
|
+
isFlameChart && columns.ts !== null
|
|
216
248
|
? ((ts - Number(tsBounds[0])) / (Number(tsBounds[1]) - Number(tsBounds[0]))) * totalWidth
|
|
217
249
|
: selectedDepth > depth
|
|
218
250
|
? 0
|
|
@@ -25,7 +25,7 @@ import {Table, tableFromIPC} from 'apache-arrow';
|
|
|
25
25
|
import {useContextMenu} from 'react-contexify';
|
|
26
26
|
|
|
27
27
|
import {FlamegraphArrow} from '@parca/client';
|
|
28
|
-
import {useParcaContext} from '@parca/components';
|
|
28
|
+
import {FlameGraphSkeleton, SandwichFlameGraphSkeleton, useParcaContext} from '@parca/components';
|
|
29
29
|
import {USER_PREFERENCES, useCurrentColorProfile, useUserPreference} from '@parca/hooks';
|
|
30
30
|
import {ProfileType} from '@parca/parser';
|
|
31
31
|
import {getColorForFeature, selectDarkMode, useAppSelector} from '@parca/store';
|
|
@@ -38,6 +38,7 @@ import ContextMenuWrapper, {ContextMenuWrapperRef} from './ContextMenuWrapper';
|
|
|
38
38
|
import {FlameNode, RowHeight, colorByColors} from './FlameGraphNodes';
|
|
39
39
|
import {MemoizedTooltip} from './MemoizedTooltip';
|
|
40
40
|
import {TooltipProvider} from './TooltipContext';
|
|
41
|
+
import {useBatchedRendering} from './useBatchedRendering';
|
|
41
42
|
import {useScrollViewport} from './useScrollViewport';
|
|
42
43
|
import {useVisibleNodes} from './useVisibleNodes';
|
|
43
44
|
import {
|
|
@@ -136,6 +137,7 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({
|
|
|
136
137
|
isFlameChart = false,
|
|
137
138
|
isRenderedAsFlamegraph = false,
|
|
138
139
|
isInSandwichView = false,
|
|
140
|
+
isHalfScreen,
|
|
139
141
|
tooltipId = 'default',
|
|
140
142
|
maxFrameCount,
|
|
141
143
|
isExpanded = false,
|
|
@@ -163,6 +165,7 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({
|
|
|
163
165
|
const svg = useRef(null);
|
|
164
166
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
165
167
|
const renderStartTime = useRef<number>(0);
|
|
168
|
+
const hasInitialRenderCompleted = useRef(false);
|
|
166
169
|
|
|
167
170
|
const [svgElement, setSvgElement] = useState<SVGSVGElement | null>(null);
|
|
168
171
|
|
|
@@ -291,6 +294,18 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({
|
|
|
291
294
|
effectiveDepth: deferredEffectiveDepth,
|
|
292
295
|
});
|
|
293
296
|
|
|
297
|
+
// Add nodes in incremental batches to avoid blocking the UI
|
|
298
|
+
const {items: batchedNodes, isComplete: isBatchingComplete} = useBatchedRendering(visibleNodes, {
|
|
299
|
+
batchSize: 500,
|
|
300
|
+
});
|
|
301
|
+
if (isBatchingComplete) {
|
|
302
|
+
hasInitialRenderCompleted.current = true;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Show skeleton only during initial load, not during scroll updates
|
|
306
|
+
const showSkeleton =
|
|
307
|
+
!hasInitialRenderCompleted.current && batchedNodes.length !== visibleNodes.length;
|
|
308
|
+
|
|
294
309
|
useEffect(() => {
|
|
295
310
|
if (perf?.markInteraction != null) {
|
|
296
311
|
renderStartTime.current = performance.now();
|
|
@@ -327,12 +342,22 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({
|
|
|
327
342
|
isInSandwichView={isInSandwichView}
|
|
328
343
|
/>
|
|
329
344
|
<MemoizedTooltip contextElement={svgElement} dockedMetainfo={dockedMetainfo} />
|
|
345
|
+
{showSkeleton && (
|
|
346
|
+
<div className="absolute inset-0 z-10">
|
|
347
|
+
{isRenderedAsFlamegraph ? (
|
|
348
|
+
<SandwichFlameGraphSkeleton isHalfScreen={isHalfScreen} isDarkMode={isDarkMode} />
|
|
349
|
+
) : (
|
|
350
|
+
<FlameGraphSkeleton isHalfScreen={isHalfScreen} isDarkMode={isDarkMode} />
|
|
351
|
+
)}
|
|
352
|
+
</div>
|
|
353
|
+
)}
|
|
330
354
|
<div
|
|
331
355
|
ref={containerRef}
|
|
332
356
|
className="overflow-auto scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-100 dark:scrollbar-thumb-gray-600 dark:scrollbar-track-gray-800 will-change-transform scroll-smooth webkit-overflow-scrolling-touch contain"
|
|
333
357
|
style={{
|
|
334
358
|
width: width ?? '100%',
|
|
335
359
|
contain: 'layout style paint',
|
|
360
|
+
visibility: !showSkeleton ? 'visible' : 'hidden',
|
|
336
361
|
}}
|
|
337
362
|
>
|
|
338
363
|
<svg
|
|
@@ -342,7 +367,7 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({
|
|
|
342
367
|
preserveAspectRatio="xMinYMid"
|
|
343
368
|
ref={svg}
|
|
344
369
|
>
|
|
345
|
-
{
|
|
370
|
+
{batchedNodes.map(row => (
|
|
346
371
|
<FlameNode
|
|
347
372
|
key={row}
|
|
348
373
|
table={table}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {useEffect, useRef, useState} from 'react';
|
|
15
|
+
|
|
16
|
+
interface UseBatchedRenderingOptions {
|
|
17
|
+
batchSize?: number;
|
|
18
|
+
// Delay between batches in ms (0 = next animation frame)
|
|
19
|
+
batchDelay?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface UseBatchedRenderingResult<T> {
|
|
23
|
+
items: T[];
|
|
24
|
+
isComplete: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// useBatchedRendering - Helps in incrementally rendering items in batches to avoid UI blocking.
|
|
28
|
+
export const useBatchedRendering = <T>(
|
|
29
|
+
items: T[],
|
|
30
|
+
options: UseBatchedRenderingOptions = {}
|
|
31
|
+
): UseBatchedRenderingResult<T> => {
|
|
32
|
+
const {batchSize = 500, batchDelay = 0} = options;
|
|
33
|
+
|
|
34
|
+
const [renderedCount, setRenderedCount] = useState(0);
|
|
35
|
+
const itemsRef = useRef(items);
|
|
36
|
+
const rafRef = useRef<number | null>(null);
|
|
37
|
+
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (itemsRef.current !== items) {
|
|
41
|
+
itemsRef.current = items;
|
|
42
|
+
setRenderedCount(prev => {
|
|
43
|
+
if (items.length === 0) return 0;
|
|
44
|
+
// If new items were added (scrolling down), keep current progress
|
|
45
|
+
if (items.length > prev) return prev;
|
|
46
|
+
// If items reduced, cap to new length
|
|
47
|
+
return Math.min(prev, items.length);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}, [items]);
|
|
51
|
+
|
|
52
|
+
// Progressively render more items
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (renderedCount === items.length) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const scheduleNextBatch = (): void => {
|
|
59
|
+
const incrementState = (): void => {
|
|
60
|
+
setRenderedCount(prev => Math.min(prev + batchSize, items.length));
|
|
61
|
+
};
|
|
62
|
+
if (batchDelay > 0) {
|
|
63
|
+
timeoutRef.current = setTimeout(incrementState, batchDelay);
|
|
64
|
+
} else {
|
|
65
|
+
rafRef.current = requestAnimationFrame(incrementState);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
scheduleNextBatch();
|
|
69
|
+
|
|
70
|
+
return () => {
|
|
71
|
+
if (rafRef.current !== null) {
|
|
72
|
+
cancelAnimationFrame(rafRef.current);
|
|
73
|
+
}
|
|
74
|
+
if (timeoutRef.current !== null) {
|
|
75
|
+
clearTimeout(timeoutRef.current);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}, [renderedCount, items.length, batchSize, batchDelay]);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
items: items.slice(0, renderedCount),
|
|
82
|
+
isComplete: renderedCount === items.length,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
@@ -20,6 +20,21 @@ export interface ViewportState {
|
|
|
20
20
|
containerWidth: number;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// Find the scrollable ancestor (the element with overflow: auto/scroll)
|
|
24
|
+
const findScrollableParent = (element: HTMLElement | null): HTMLElement | undefined => {
|
|
25
|
+
if (element === null) return undefined;
|
|
26
|
+
let current: HTMLElement | null = element.parentElement;
|
|
27
|
+
while (current !== null) {
|
|
28
|
+
const style = window.getComputedStyle(current);
|
|
29
|
+
const overflowY = style.overflowY;
|
|
30
|
+
if (overflowY === 'auto' || overflowY === 'scroll') {
|
|
31
|
+
return current;
|
|
32
|
+
}
|
|
33
|
+
current = current.parentElement;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
};
|
|
37
|
+
|
|
23
38
|
export const useScrollViewport = (containerRef: React.RefObject<HTMLDivElement>): ViewportState => {
|
|
24
39
|
const [viewport, setViewport] = useState<ViewportState>({
|
|
25
40
|
scrollTop: 0,
|
|
@@ -33,11 +48,25 @@ export const useScrollViewport = (containerRef: React.RefObject<HTMLDivElement>)
|
|
|
33
48
|
const updateViewport = useCallback(() => {
|
|
34
49
|
if (containerRef.current !== null) {
|
|
35
50
|
const container = containerRef.current;
|
|
51
|
+
const rect = container.getBoundingClientRect();
|
|
52
|
+
|
|
53
|
+
// Restrict container height to the visible portion on screen
|
|
54
|
+
// This handles cases where the container is partially off-screen
|
|
55
|
+
// We only want to consider the visible part for culling calculations
|
|
56
|
+
|
|
57
|
+
const containerTop = rect.top;
|
|
58
|
+
const containerBottom = rect.bottom;
|
|
59
|
+
const viewportTop = 0;
|
|
60
|
+
const viewportBottom = window.innerHeight;
|
|
61
|
+
const visibleTop = Math.max(containerTop, viewportTop);
|
|
62
|
+
const visibleBottom = Math.min(containerBottom, viewportBottom);
|
|
63
|
+
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
64
|
+
const scrollOffset = Math.max(0, viewportTop - containerTop);
|
|
36
65
|
|
|
37
66
|
const newViewport = {
|
|
38
|
-
scrollTop:
|
|
67
|
+
scrollTop: scrollOffset,
|
|
39
68
|
scrollLeft: container.scrollLeft,
|
|
40
|
-
containerHeight:
|
|
69
|
+
containerHeight: visibleHeight, // Only the visible portion
|
|
41
70
|
containerWidth: container.clientWidth,
|
|
42
71
|
};
|
|
43
72
|
|
|
@@ -59,6 +88,8 @@ export const useScrollViewport = (containerRef: React.RefObject<HTMLDivElement>)
|
|
|
59
88
|
const container = containerRef.current;
|
|
60
89
|
if (container === null) return;
|
|
61
90
|
|
|
91
|
+
const scrollableParent = findScrollableParent(container);
|
|
92
|
+
|
|
62
93
|
// ResizeObserver Strategy:
|
|
63
94
|
// Monitor container size changes (window resize, layout shifts)
|
|
64
95
|
// to update viewport dimensions for accurate culling calculations
|
|
@@ -66,10 +97,12 @@ export const useScrollViewport = (containerRef: React.RefObject<HTMLDivElement>)
|
|
|
66
97
|
throttledUpdateViewport();
|
|
67
98
|
});
|
|
68
99
|
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
100
|
+
// Listen to scroll on the actual scrollable parent
|
|
101
|
+
|
|
102
|
+
scrollableParent?.addEventListener('scroll', throttledUpdateViewport, {passive: true});
|
|
72
103
|
container.addEventListener('scroll', throttledUpdateViewport, {passive: true});
|
|
104
|
+
window.addEventListener('scroll', throttledUpdateViewport, {passive: true});
|
|
105
|
+
|
|
73
106
|
resizeObserver.observe(container);
|
|
74
107
|
|
|
75
108
|
// Initialize viewport state on mount
|
|
@@ -77,7 +110,9 @@ export const useScrollViewport = (containerRef: React.RefObject<HTMLDivElement>)
|
|
|
77
110
|
|
|
78
111
|
return () => {
|
|
79
112
|
// Cleanup: Remove event listeners and cancel pending animations
|
|
113
|
+
scrollableParent?.removeEventListener('scroll', throttledUpdateViewport);
|
|
80
114
|
container.removeEventListener('scroll', throttledUpdateViewport);
|
|
115
|
+
window.removeEventListener('scroll', throttledUpdateViewport);
|
|
81
116
|
resizeObserver.disconnect();
|
|
82
117
|
if (throttleRef.current !== null) {
|
|
83
118
|
cancelAnimationFrame(throttleRef.current);
|
|
@@ -85,7 +85,19 @@ export const useVisibleNodes = ({
|
|
|
85
85
|
result: number[];
|
|
86
86
|
}>({key: '', result: []});
|
|
87
87
|
|
|
88
|
+
const renderedRangeRef = useRef<{minDepth: number; maxDepth: number; table: Table<any> | null}>({
|
|
89
|
+
minDepth: Infinity,
|
|
90
|
+
maxDepth: -Infinity,
|
|
91
|
+
table: null,
|
|
92
|
+
});
|
|
93
|
+
|
|
88
94
|
return useMemo(() => {
|
|
95
|
+
// This happens when the continer is scrolled off screen, in this case we return all previously rendered nodes
|
|
96
|
+
// to avoid trimming the rendered nodes to zero which would cause jank when scrolling back into view
|
|
97
|
+
if (viewport.containerHeight === 0 && lastResultRef.current.result.length > 0) {
|
|
98
|
+
return lastResultRef.current.result;
|
|
99
|
+
}
|
|
100
|
+
|
|
89
101
|
// Create a stable key for memoization to prevent unnecessary recalculations
|
|
90
102
|
const memoKey = `${viewport.scrollTop}-${
|
|
91
103
|
viewport.containerHeight
|
|
@@ -96,7 +108,7 @@ export const useVisibleNodes = ({
|
|
|
96
108
|
return lastResultRef.current.result;
|
|
97
109
|
}
|
|
98
110
|
|
|
99
|
-
if (table === null
|
|
111
|
+
if (table === null) return [];
|
|
100
112
|
|
|
101
113
|
const visibleRows: number[] = [];
|
|
102
114
|
const {scrollTop, containerHeight} = viewport;
|
|
@@ -104,11 +116,35 @@ export const useVisibleNodes = ({
|
|
|
104
116
|
// Viewport Culling Algorithm:
|
|
105
117
|
// 1. Calculate visible depth range based on scroll position and container height
|
|
106
118
|
// 2. Add 5-row buffer above/below for smooth scrolling experience
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
119
|
+
// Note: We never shrink the rendered range to avoid back and forth node removals (and in turn additions when scrolled down again) to the dom.
|
|
120
|
+
|
|
121
|
+
const BUFFER = 15; // Buffer for smoother scrolling
|
|
122
|
+
|
|
123
|
+
const visibleStartDepth = Math.max(0, Math.floor(scrollTop / RowHeight) - BUFFER);
|
|
124
|
+
const visibleDepths = Math.ceil(containerHeight / RowHeight);
|
|
125
|
+
const visibleEndDepth = Math.min(effectiveDepth, visibleStartDepth + visibleDepths + BUFFER);
|
|
126
|
+
|
|
127
|
+
// Reset range if table changed (new data loaded) as this is new data
|
|
128
|
+
if (renderedRangeRef.current.table !== table) {
|
|
129
|
+
renderedRangeRef.current = {
|
|
130
|
+
minDepth: Infinity,
|
|
131
|
+
maxDepth: -Infinity,
|
|
132
|
+
table: table,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Expand the rendered range (never shrink when scrolling up/down)
|
|
137
|
+
renderedRangeRef.current.minDepth = Math.min(
|
|
138
|
+
renderedRangeRef.current.minDepth,
|
|
139
|
+
visibleStartDepth
|
|
111
140
|
);
|
|
141
|
+
renderedRangeRef.current.maxDepth = Math.max(
|
|
142
|
+
renderedRangeRef.current.maxDepth,
|
|
143
|
+
visibleEndDepth
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const startDepth = renderedRangeRef.current.minDepth;
|
|
147
|
+
const endDepth = renderedRangeRef.current.maxDepth;
|
|
112
148
|
|
|
113
149
|
const cumulativeColumn = table.getChild(FIELD_CUMULATIVE);
|
|
114
150
|
const valueOffsetColumn = table.getChild(FIELD_VALUE_OFFSET);
|
package/src/hooks/useLabels.ts
CHANGED
|
@@ -113,7 +113,9 @@ export const useLabelValues = (
|
|
|
113
113
|
},
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
console.log('Label values query result:', {data, error, isLoading, labelName});
|
|
118
|
+
}, [data, error, isLoading, labelName]);
|
|
117
119
|
|
|
118
120
|
return {
|
|
119
121
|
result: {response: data ?? [], error: error as Error},
|