@parca/profile 0.16.426 → 0.16.428
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.js +1 -1
- package/dist/ProfileIcicleGraph/index.d.ts +1 -1
- package/dist/ProfileIcicleGraph/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/index.js +4 -59
- package/dist/ProfileView/VisualizationPanel.d.ts.map +1 -1
- package/dist/ProfileView/VisualizationPanel.js +2 -3
- package/dist/ProfileView/index.d.ts.map +1 -1
- package/dist/ProfileView/index.js +21 -14
- package/dist/SimpleMatchers/index.d.ts.map +1 -1
- package/dist/SimpleMatchers/index.js +9 -2
- package/dist/Table/ColumnsVisibility.js +1 -1
- package/dist/Table/index.d.ts +6 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +45 -21
- package/dist/components/ActionButtons/GroupByDropdown.d.ts.map +1 -0
- package/dist/{ProfileIcicleGraph → components}/ActionButtons/GroupByDropdown.js +1 -1
- package/dist/components/FilterByFunctionButton.d.ts.map +1 -0
- package/dist/components/ShareButton/ResultBox.d.ts.map +1 -0
- package/dist/components/ShareButton/index.d.ts +14 -0
- package/dist/components/ShareButton/index.d.ts.map +1 -0
- package/dist/components/{ProfileShareButton → ShareButton}/index.js +28 -5
- package/dist/components/ViewSelector/Dropdown.d.ts +30 -0
- package/dist/components/ViewSelector/Dropdown.d.ts.map +1 -0
- package/dist/components/ViewSelector/Dropdown.js +41 -0
- package/dist/components/ViewSelector/index.d.ts +3 -0
- package/dist/components/ViewSelector/index.d.ts.map +1 -0
- package/dist/{ProfileView/ViewSelector.js → components/ViewSelector/index.js} +25 -12
- package/dist/components/VisualisationToolbar/MultiLevelDropdown.d.ts +9 -0
- package/dist/components/VisualisationToolbar/MultiLevelDropdown.d.ts.map +1 -0
- package/dist/components/VisualisationToolbar/MultiLevelDropdown.js +123 -0
- package/dist/components/VisualisationToolbar/TableColumnsDropdown.d.ts +9 -0
- package/dist/components/VisualisationToolbar/TableColumnsDropdown.d.ts.map +1 -0
- package/dist/components/VisualisationToolbar/TableColumnsDropdown.js +189 -0
- package/dist/components/VisualisationToolbar/index.d.ts +25 -0
- package/dist/components/VisualisationToolbar/index.d.ts.map +1 -0
- package/dist/components/VisualisationToolbar/index.js +54 -0
- package/dist/styles.css +1 -1
- package/package.json +4 -4
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.tsx +1 -1
- package/src/ProfileIcicleGraph/index.tsx +3 -206
- package/src/ProfileView/VisualizationPanel.tsx +1 -6
- package/src/ProfileView/index.tsx +46 -56
- package/src/SimpleMatchers/index.tsx +11 -2
- package/src/Table/ColumnsVisibility.tsx +1 -1
- package/src/Table/index.tsx +64 -42
- package/src/{ProfileIcicleGraph → components}/ActionButtons/GroupByDropdown.tsx +1 -1
- package/src/components/{ProfileShareButton → ShareButton}/index.tsx +88 -24
- package/src/components/ViewSelector/Dropdown.tsx +181 -0
- package/src/{ProfileView/ViewSelector.tsx → components/ViewSelector/index.tsx} +32 -39
- package/src/components/VisualisationToolbar/MultiLevelDropdown.tsx +258 -0
- package/src/components/VisualisationToolbar/TableColumnsDropdown.tsx +222 -0
- package/src/components/VisualisationToolbar/index.tsx +169 -0
- package/dist/ProfileIcicleGraph/ActionButtons/GroupByDropdown.d.ts.map +0 -1
- package/dist/ProfileIcicleGraph/ActionButtons/SortBySelect.d.ts +0 -7
- package/dist/ProfileIcicleGraph/ActionButtons/SortBySelect.d.ts.map +0 -1
- package/dist/ProfileIcicleGraph/ActionButtons/SortBySelect.js +0 -44
- package/dist/ProfileView/FilterByFunctionButton.d.ts.map +0 -1
- package/dist/ProfileView/ViewSelector.d.ts +0 -13
- package/dist/ProfileView/ViewSelector.d.ts.map +0 -1
- package/dist/components/ProfileShareButton/ResultBox.d.ts.map +0 -1
- package/dist/components/ProfileShareButton/index.d.ts +0 -9
- package/dist/components/ProfileShareButton/index.d.ts.map +0 -1
- package/src/ProfileIcicleGraph/ActionButtons/SortBySelect.tsx +0 -81
- /package/dist/{ProfileIcicleGraph → components}/ActionButtons/GroupByDropdown.d.ts +0 -0
- /package/dist/{ProfileView → components}/FilterByFunctionButton.d.ts +0 -0
- /package/dist/{ProfileView → components}/FilterByFunctionButton.js +0 -0
- /package/dist/components/{ProfileShareButton → ShareButton}/ResultBox.d.ts +0 -0
- /package/dist/components/{ProfileShareButton → ShareButton}/ResultBox.js +0 -0
- /package/src/{ProfileView → components}/FilterByFunctionButton.tsx +0 -0
- /package/src/components/{ProfileShareButton → ShareButton}/ResultBox.tsx +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.16.428](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.427...@parca/profile@0.16.428) (2024-08-21)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
10
|
+
## [0.16.427](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.426...@parca/profile@0.16.427) (2024-08-19)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @parca/profile
|
|
13
|
+
|
|
6
14
|
## [0.16.426](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.425...@parca/profile@0.16.426) (2024-08-19)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -78,6 +78,6 @@ const ContextMenu = ({ menuId, table, total, totalUnfiltered, row, level, compar
|
|
|
78
78
|
{ id: 'Build Id', value: buildIdText },
|
|
79
79
|
];
|
|
80
80
|
const nonEmptyValuesToCopy = valuesToCopy.filter(({ value }) => value !== '');
|
|
81
|
-
return (_jsxs(Menu, { id: menuId, onVisibilityChange: trackVisibility, theme: isDarkMode ? 'dark' : '', children: [_jsxs(Item, { id: "view-source-file", onClick: handleViewSourceFile, disabled: enableSourcesView === false || !isSourceAvailable, children: [_jsx("div", { "data-tooltip-id": "view-source-file-help", "data-tooltip-content": "There is no source code uploaded for this build", children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "wpf:view-file" }), _jsx("div", { children: "View source file" })] }) }), !isSourceAvailable ? _jsx(Tooltip, { id: "view-source-file-help" }) : null] }), _jsx(Item, { id: "reset-view", onClick: handleResetView, disabled: curPath.length === 0, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "system-uicons:reset" }), _jsx("div", { children: "Reset
|
|
81
|
+
return (_jsxs(Menu, { id: menuId, onVisibilityChange: trackVisibility, theme: isDarkMode ? 'dark' : '', children: [_jsxs(Item, { id: "view-source-file", onClick: handleViewSourceFile, disabled: enableSourcesView === false || !isSourceAvailable, children: [_jsx("div", { "data-tooltip-id": "view-source-file-help", "data-tooltip-content": "There is no source code uploaded for this build", children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "wpf:view-file" }), _jsx("div", { children: "View source file" })] }) }), !isSourceAvailable ? _jsx(Tooltip, { id: "view-source-file-help" }) : null] }), _jsx(Item, { id: "reset-view", onClick: handleResetView, disabled: curPath.length === 0, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "system-uicons:reset" }), _jsx("div", { children: "Reset graph" })] }) }), _jsxs(Item, { id: "hide-binary", onClick: () => hideBinary(getLastItem(mappingFile)), disabled: mappingFile === null || mappingFile === '', children: [_jsx("div", { "data-tooltip-id": "hide-binary-help", "data-tooltip-content": "Hide all frames for this binary", children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "bx:bxs-hide" }), _jsxs("div", { children: ["Hide Binary ", mappingFile !== null && `(${getLastItem(mappingFile)})`] })] }) }), _jsx(Tooltip, { place: "left", id: "hide-binary-help" })] }), _jsx(Submenu, { label: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "ph:copy" }), _jsx("div", { children: "Copy" })] }), children: _jsx("div", { className: "max-h-[300px] overflow-scroll", children: nonEmptyValuesToCopy.map(({ id, value }) => (_jsx(Item, { id: id, onClick: () => handleCopyItem(value), className: "dark:bg-gray-800", children: _jsxs("div", { className: "flex flex-col dark:text-gray-300 hover:dark:text-gray-100", children: [_jsx("div", { className: "text-sm", children: id }), _jsx("div", { className: "text-xs", children: truncateString(value, 30) })] }) }, id))) }) }), _jsx(Separator, {}), _jsx(Item, { id: "dock-tooltip", onClick: handleDockTooltip, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "bx:dock-bottom" }), isGraphTooltipDocked ? 'Undock tooltip' : 'Dock tooltip'] }) })] }));
|
|
82
82
|
};
|
|
83
83
|
export default ContextMenu;
|
|
@@ -18,6 +18,6 @@ interface ProfileIcicleGraphProps {
|
|
|
18
18
|
mappings?: string[];
|
|
19
19
|
mappingsLoading?: boolean;
|
|
20
20
|
}
|
|
21
|
-
declare const ProfileIcicleGraph: ({ graph, arrow, total, filtered, curPath, setNewCurPath, profileType, loading,
|
|
21
|
+
declare const ProfileIcicleGraph: ({ graph, arrow, total, filtered, curPath, setNewCurPath, profileType, loading, error, width, isHalfScreen, mappings, }: ProfileIcicleGraphProps) => JSX.Element;
|
|
22
22
|
export default ProfileIcicleGraph;
|
|
23
23
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileIcicleGraph/index.tsx"],"names":[],"mappings":"AAaA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileIcicleGraph/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAI1D,OAAO,EAAC,UAAU,EAAE,eAAe,EAAC,MAAM,eAAe,CAAC;AAE1D,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAY1C,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAEpE,UAAU,uBAAuB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACvB,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC;IACxD,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAMD,QAAA,MAAM,kBAAkB,2HAarB,uBAAuB,KAAG,GAAG,CAAC,OAqKhC,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -11,16 +11,12 @@ import { jsx as _jsx, Fragment as _Fragment, 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 {
|
|
15
|
-
import { Icon } from '@iconify/react';
|
|
14
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
16
15
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
17
|
-
import {
|
|
18
|
-
import { USER_PREFERENCES, useUserPreference } from '@parca/hooks';
|
|
16
|
+
import { IcicleGraphSkeleton, useParcaContext, useURLState } from '@parca/components';
|
|
19
17
|
import { capitalizeOnlyFirstLetter, divide, selectQueryParam } from '@parca/utilities';
|
|
20
18
|
import { useProfileViewContext } from '../ProfileView/ProfileViewContext';
|
|
21
19
|
import DiffLegend from '../components/DiffLegend';
|
|
22
|
-
import GroupByDropdown from './ActionButtons/GroupByDropdown';
|
|
23
|
-
import SortBySelect from './ActionButtons/SortBySelect';
|
|
24
20
|
import { IcicleGraph } from './IcicleGraph';
|
|
25
21
|
import { FIELD_FUNCTION_NAME, IcicleGraphArrow } from './IcicleGraphArrow';
|
|
26
22
|
import ColorStackLegend from './IcicleGraphArrow/ColorStackLegend';
|
|
@@ -29,52 +25,17 @@ const numberFormatter = new Intl.NumberFormat('en-US');
|
|
|
29
25
|
const ErrorContent = ({ errorMessage }) => {
|
|
30
26
|
return _jsx("div", { className: "flex justify-center p-10", children: errorMessage });
|
|
31
27
|
};
|
|
32
|
-
const
|
|
33
|
-
const [colorStackLegend, setStoreColorStackLegend] = useURLState('color_stack_legend');
|
|
34
|
-
const [binaryFrameFilter, setBinaryFrameFilter] = useURLState('binary_frame_filter');
|
|
35
|
-
const { compareMode } = useProfileViewContext();
|
|
36
|
-
const isColorStackLegendEnabled = colorStackLegend === 'true';
|
|
37
|
-
const [colorProfileName] = useUserPreference(USER_PREFERENCES.FLAMEGRAPH_COLOR_PROFILE.key);
|
|
38
|
-
const setColorStackLegend = useCallback((value) => {
|
|
39
|
-
setStoreColorStackLegend(value);
|
|
40
|
-
}, [setStoreColorStackLegend]);
|
|
41
|
-
const resetLegend = () => {
|
|
42
|
-
setBinaryFrameFilter([]);
|
|
43
|
-
};
|
|
44
|
-
return (_jsx(_Fragment, { children: colorProfileName === 'default' || compareMode ? null : (_jsx(_Fragment, { children: isHalfScreen ? (_jsxs(_Fragment, { children: [_jsx(IconButton, { className: "rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 items-center flex border border-gray-200 dark:border-gray-600 dark:text-white justify-center !py-2 !px-3 cursor-pointer min-h-[38px]", icon: isColorStackLegendEnabled ? 'ph:eye-closed' : 'ph:eye', toolTipText: isColorStackLegendEnabled ? 'Hide legend' : 'Show legend', onClick: () => setColorStackLegend(isColorStackLegendEnabled ? 'false' : 'true'), id: "h-show-legend-button" }), binaryFrameFilter !== undefined && binaryFrameFilter.length > 0 && (_jsx(IconButton, { className: "rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 items-center flex border border-gray-200 dark:border-gray-600 dark:text-white justify-center !py-2 !px-3 cursor-pointer min-h-[38px]", icon: "system-uicons:reset", toolTipText: "Reset the legend selection", onClick: () => resetLegend(), id: "h-reset-legend-button" }))] })) : (_jsxs(_Fragment, { children: [_jsxs(Button, { className: "gap-2 w-max", variant: "neutral", onClick: () => setColorStackLegend(isColorStackLegendEnabled ? 'false' : 'true'), id: "h-show-legend-button", children: [isColorStackLegendEnabled ? 'Hide legend' : 'Show legend', _jsx(Icon, { icon: isColorStackLegendEnabled ? 'ph:eye-closed' : 'ph:eye', width: 20 })] }), binaryFrameFilter !== undefined && binaryFrameFilter.length > 0 && (_jsxs(Button, { className: "gap-2 w-max", variant: "neutral", onClick: () => resetLegend(), id: "h-reset-legend-button", children: ["Reset Legend", _jsx(Icon, { icon: "system-uicons:reset", width: 20 })] }))] })) })) }));
|
|
45
|
-
};
|
|
46
|
-
const GroupAndSortActionButtons = () => {
|
|
47
|
-
const [storeSortBy, setStoreSortBy] = useURLState('sort_by', {
|
|
48
|
-
defaultValue: FIELD_FUNCTION_NAME,
|
|
49
|
-
});
|
|
50
|
-
const { compareMode } = useProfileViewContext();
|
|
51
|
-
const [groupBy, setStoreGroupBy] = useURLState('group_by', {
|
|
52
|
-
defaultValue: [FIELD_FUNCTION_NAME],
|
|
53
|
-
alwaysReturnArray: true,
|
|
54
|
-
});
|
|
55
|
-
const setGroupBy = useCallback((keys) => {
|
|
56
|
-
setStoreGroupBy(keys);
|
|
57
|
-
}, [setStoreGroupBy]);
|
|
58
|
-
const toggleGroupBy = useCallback((key) => {
|
|
59
|
-
groupBy.includes(key)
|
|
60
|
-
? setGroupBy(groupBy.filter(v => v !== key)) // remove
|
|
61
|
-
: setGroupBy([...groupBy, key]); // add
|
|
62
|
-
}, [groupBy, setGroupBy]);
|
|
63
|
-
return (_jsxs(_Fragment, { children: [_jsx(GroupByDropdown, { groupBy: groupBy, toggleGroupBy: toggleGroupBy }), _jsx(SortBySelect, { compareMode: compareMode, sortBy: storeSortBy, setSortBy: setStoreSortBy })] }));
|
|
64
|
-
};
|
|
65
|
-
const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({ graph, arrow, total, filtered, curPath, setNewCurPath, profileType, loading, setActionButtons, error, width, isHalfScreen, mappings, }) {
|
|
28
|
+
const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({ graph, arrow, total, filtered, curPath, setNewCurPath, profileType, loading, error, width, isHalfScreen, mappings, }) {
|
|
66
29
|
const { onError, authenticationErrorMessage, isDarkMode } = useParcaContext();
|
|
67
30
|
const { compareMode } = useProfileViewContext();
|
|
68
31
|
const [isLoading, setIsLoading] = useState(true);
|
|
69
32
|
const isColorStackLegendEnabled = selectQueryParam('color_stack_legend') === 'true';
|
|
70
33
|
const mappingsList = useMappingList(mappings);
|
|
71
34
|
const [storeSortBy = FIELD_FUNCTION_NAME] = useURLState('sort_by');
|
|
72
|
-
const [invertStack = '', setInvertStack] = useURLState('invert_call_stack');
|
|
73
|
-
const isInvert = invertStack === 'true';
|
|
74
35
|
// By default, we want delta profiles (CPU) to be relatively compared.
|
|
75
36
|
// For non-delta profiles, like goroutines or memory, we want the profiles to be compared absolutely.
|
|
76
37
|
const compareAbsoluteDefault = profileType?.delta === false ? 'true' : 'false';
|
|
77
|
-
const [compareAbsolute = compareAbsoluteDefault
|
|
38
|
+
const [compareAbsolute = compareAbsoluteDefault] = useURLState('compare_absolute');
|
|
78
39
|
const isCompareAbsolute = compareAbsolute === 'true';
|
|
79
40
|
const [totalFormatted, totalUnfilteredFormatted, isTrimmed, trimmedFormatted, trimmedPercentage, isFiltered, filteredPercentage,] = useMemo(() => {
|
|
80
41
|
if (graph === undefined && arrow === undefined) {
|
|
@@ -94,22 +55,6 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({ graph, arrow, to
|
|
|
94
55
|
numberFormatter.format(divide(total * 100n, totalUnfilteredDivisor)),
|
|
95
56
|
];
|
|
96
57
|
}, [graph, arrow, filtered, total]);
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
setActionButtons?.(_jsx("div", { className: "flex w-full justify-end gap-2 pb-2", children: _jsxs("div", { className: "ml-2 flex w-full flex-col items-start justify-between gap-2 md:flex-row md:items-end", children: [_jsx(GroupAndSortActionButtons, {}), isHalfScreen ? (_jsx(IconButton, { icon: isInvert ? 'ph:sort-ascending' : 'ph:sort-descending', toolTipText: isInvert ? 'Original Call Stack' : 'Invert Call Stack', onClick: () => setInvertStack(isInvert ? '' : 'true'), className: "rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 items-center flex border border-gray-200 dark:border-gray-600 dark:text-white justify-center py-2 px-3 cursor-pointer min-h-[38px]" })) : (_jsxs(Button, { variant: "neutral", className: "gap-2 w-max", onClick: () => setInvertStack(isInvert ? '' : 'true'), children: [isInvert ? 'Original Call Stack' : 'Invert Call Stack', _jsx(Icon, { icon: isInvert ? 'ph:sort-ascending' : 'ph:sort-descending', width: 20 })] })), _jsx(ShowHideLegendButton, { isHalfScreen: isHalfScreen }), compareMode && (_jsxs(Button, { variant: "neutral", className: "gap-2 w-max", onClick: () => setCompareAbsolute(isCompareAbsolute ? '' : 'true'), children: [isCompareAbsolute ? 'Compare Relative' : 'Compare Absolute', _jsx(Icon, { icon: isCompareAbsolute ? 'fluent-mdl2:compare' : 'fluent-mdl2:compare-uneven', width: 20 })] })), isHalfScreen ? (_jsx(IconButton, { icon: "system-uicons:reset", disabled: curPath.length === 0, toolTipText: "Reset View", onClick: () => setNewCurPath([]), className: "rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 items-center flex border border-gray-200 dark:border-gray-600 dark:text-white justify-center py-2 px-3 cursor-pointer min-h-[38px]" })) : (_jsxs(Button, { variant: "neutral", className: "gap-2 w-max", onClick: () => setNewCurPath([]), disabled: curPath.length === 0, children: ["Reset View", _jsx(Icon, { icon: "system-uicons:reset", width: 20 })] }))] }) }));
|
|
99
|
-
}, [
|
|
100
|
-
isInvert,
|
|
101
|
-
setInvertStack,
|
|
102
|
-
arrow,
|
|
103
|
-
curPath,
|
|
104
|
-
setNewCurPath,
|
|
105
|
-
setActionButtons,
|
|
106
|
-
loading,
|
|
107
|
-
isHalfScreen,
|
|
108
|
-
isLoading,
|
|
109
|
-
compareMode,
|
|
110
|
-
isCompareAbsolute,
|
|
111
|
-
setCompareAbsolute,
|
|
112
|
-
]);
|
|
113
58
|
const loadingState = !loading && (arrow !== undefined || graph !== undefined) && mappings !== undefined;
|
|
114
59
|
useEffect(() => {
|
|
115
60
|
if (loadingState) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VisualizationPanel.d.ts","sourceRoot":"","sources":["../../src/ProfileView/VisualizationPanel.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAItC,OAAO,KAAK,EAAC,gCAAgC,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"VisualizationPanel.d.ts","sourceRoot":"","sources":["../../src/ProfileView/VisualizationPanel.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAItC,OAAO,KAAK,EAAC,gCAAgC,EAAC,MAAM,qBAAqB,CAAC;AAK1E,UAAU,KAAK;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gBAAgB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,eAAe,EAAE,gCAAgC,GAAG,IAAI,GAAG,SAAS,CAAC;IACrE,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAC9B,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,OAAO,CAAC;QACtB,gBAAgB,EAAE,CAAC,aAAa,EAAE,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC;KACxD,KAAK,GAAG,CAAC,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,kBAAkB,mCAsD7B,CAAC"}
|
|
@@ -16,11 +16,10 @@ import { Icon } from '@iconify/react';
|
|
|
16
16
|
import cx from 'classnames';
|
|
17
17
|
import { IconButton, useParcaContext } from '@parca/components';
|
|
18
18
|
import { CloseIcon } from '@parca/icons';
|
|
19
|
-
|
|
20
|
-
export const VisualizationPanel = React.memo(function VisualizationPanel({ dashboardItem, index, isMultiPanelView, handleClosePanel, dragHandleProps, getDashboardItemByType, }) {
|
|
19
|
+
export const VisualizationPanel = React.memo(function VisualizationPanel({ dashboardItem, isMultiPanelView, handleClosePanel, dragHandleProps, getDashboardItemByType, }) {
|
|
21
20
|
const [actionButtons, setActionButtons] = useState(_jsx(_Fragment, {}));
|
|
22
21
|
const { flamegraphHint } = useParcaContext();
|
|
23
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex w-full items-center justify-end gap-2 pb-2
|
|
22
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex w-full items-center justify-end gap-2 pb-2", children: [_jsxs("div", { className: cx('flex w-full justify-between flex-col-reverse md:flex-row', isMultiPanelView && dashboardItem === 'icicle' ? 'items-end gap-x-2' : 'items-end'), children: [_jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: cx(isMultiPanelView ? '' : 'hidden', 'flex items-center'), ...dragHandleProps, children: _jsx(Icon, { className: "text-xl", icon: "material-symbols:drag-indicator" }) }), _jsx("div", { className: "flex gap-2", children: actionButtons })] }), _jsx("div", { className: cx('flex flex-row items-center gap-4', isMultiPanelView && dashboardItem === 'icicle' && 'pb-[10px]'), children: dashboardItem === 'icicle' && flamegraphHint != null ? (_jsx("div", { className: "px-2", children: flamegraphHint })) : null })] }), isMultiPanelView && (_jsx(IconButton, { className: "py-0", onClick: () => handleClosePanel(dashboardItem), icon: _jsx(CloseIcon, {}) }))] }), getDashboardItemByType({
|
|
24
23
|
type: dashboardItem,
|
|
25
24
|
isHalfScreen: isMultiPanelView,
|
|
26
25
|
setActionButtons,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileView/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileView/index.tsx"],"names":[],"mappings":"AA0BA,OAAO,EACL,SAAS,IAAI,aAAa,EAC1B,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,MAAM,EACN,UAAU,EACV,GAAG,EACJ,MAAM,eAAe,CAAC;AAUvB,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAO/C,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,GAAG,CAAC;CACb;AAED,UAAU,UAAU;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,GAAG,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAWD,eAAO,MAAM,WAAW,0JAYrB,gBAAgB,KAAG,GAAG,CAAC,OAmUzB,CAAC"}
|
|
@@ -12,25 +12,23 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
12
12
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
// See the License for the specific language governing permissions and
|
|
14
14
|
// limitations under the License.
|
|
15
|
-
import { Profiler, useEffect, useState } from 'react';
|
|
16
|
-
import { Icon } from '@iconify/react';
|
|
15
|
+
import { Profiler, useCallback, useEffect, useState } from 'react';
|
|
17
16
|
import cx from 'classnames';
|
|
18
17
|
import { scaleLinear } from 'd3';
|
|
19
18
|
import graphviz from 'graphviz-wasm';
|
|
20
19
|
import { DragDropContext, Draggable, Droppable, } from 'react-beautiful-dnd';
|
|
21
|
-
import {
|
|
20
|
+
import { ConditionalWrapper, KeyDownProvider, useParcaContext, useURLState } from '@parca/components';
|
|
22
21
|
import { useContainerDimensions } from '@parca/hooks';
|
|
23
22
|
import { selectDarkMode, useAppSelector } from '@parca/store';
|
|
24
23
|
import { getNewSpanColor, selectQueryParam } from '@parca/utilities';
|
|
25
24
|
import { Callgraph } from '../';
|
|
26
25
|
import { jsonToDot } from '../Callgraph/utils';
|
|
27
26
|
import ProfileIcicleGraph from '../ProfileIcicleGraph';
|
|
27
|
+
import { FIELD_FUNCTION_NAME } from '../ProfileIcicleGraph/IcicleGraphArrow';
|
|
28
28
|
import { SourceView } from '../SourceView';
|
|
29
29
|
import { Table } from '../Table';
|
|
30
|
-
import
|
|
31
|
-
import FilterByFunctionButton from './FilterByFunctionButton';
|
|
30
|
+
import VisualisationToolbar from '../components/VisualisationToolbar';
|
|
32
31
|
import { ProfileViewContextProvider } from './ProfileViewContext';
|
|
33
|
-
import ViewSelector from './ViewSelector';
|
|
34
32
|
import { VisualizationPanel } from './VisualizationPanel';
|
|
35
33
|
function arrayEquals(a, b) {
|
|
36
34
|
return (Array.isArray(a) &&
|
|
@@ -50,7 +48,7 @@ export const ProfileView = ({ total, filtered, flamegraphData, topTableData, cal
|
|
|
50
48
|
const [currentSearchString, setSearchString] = useURLState('search_string');
|
|
51
49
|
const isDarkMode = useAppSelector(selectDarkMode);
|
|
52
50
|
const isMultiPanelView = dashboardItems.length > 1;
|
|
53
|
-
const { perf, profileViewExternalMainActions
|
|
51
|
+
const { perf, profileViewExternalMainActions } = useParcaContext();
|
|
54
52
|
useEffect(() => {
|
|
55
53
|
// Reset the current path when the profile source changes
|
|
56
54
|
setCurPath([]);
|
|
@@ -143,17 +141,26 @@ export const ProfileView = ({ total, filtered, flamegraphData, topTableData, cal
|
|
|
143
141
|
const headerParts = profileSourceString?.split('"') ?? [];
|
|
144
142
|
const compareMode = compare === true ||
|
|
145
143
|
(selectQueryParam('compare_a') === 'true' && selectQueryParam('compare_b') === 'true');
|
|
146
|
-
|
|
144
|
+
const [groupBy, setStoreGroupBy] = useURLState('group_by', {
|
|
145
|
+
defaultValue: [FIELD_FUNCTION_NAME],
|
|
146
|
+
alwaysReturnArray: true,
|
|
147
|
+
});
|
|
148
|
+
const setGroupBy = useCallback((keys) => {
|
|
149
|
+
setStoreGroupBy(keys);
|
|
150
|
+
}, [setStoreGroupBy]);
|
|
151
|
+
const toggleGroupBy = useCallback((key) => {
|
|
152
|
+
groupBy.includes(key)
|
|
153
|
+
? setGroupBy(groupBy.filter(v => v !== key)) // remove
|
|
154
|
+
: setGroupBy([...groupBy, key]); // add
|
|
155
|
+
}, [groupBy, setGroupBy]);
|
|
156
|
+
return (_jsx(KeyDownProvider, { children: _jsxs(ProfileViewContextProvider, { value: { profileSource, compareMode }, children: [_jsx("div", { className: cx('mb-4 flex w-full', hasProfileSource || profileViewExternalMainActions != null
|
|
147
157
|
? 'justify-between'
|
|
148
158
|
: 'justify-end', {
|
|
149
159
|
'items-end': !hasProfileSource && profileViewExternalMainActions != null,
|
|
150
160
|
'items-center': hasProfileSource,
|
|
151
|
-
}), children:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
e.preventDefault();
|
|
155
|
-
onDownloadPProf();
|
|
156
|
-
}, disabled: pprofDownloading, id: "h-download-pprof", children: [pprofDownloading != null && pprofDownloading ? 'Downloading...' : 'Download pprof', _jsx(Icon, { icon: "material-symbols:download", width: 20 })] }), _jsx(ViewSelector, { defaultValue: "", position: -1, placeholderText: "Add panel", icon: _jsx(Icon, { icon: "material-symbols:add", width: 20 }), addView: true, disabled: isMultiPanelView || dashboardItems.length < 1, id: "h-add-panel" })] })] }), _jsx("div", { className: "w-full", ref: ref, children: _jsx(DragDropContext, { onDragEnd: onDragEnd, children: _jsx(Droppable, { droppableId: "droppable", direction: "horizontal", children: provided => (_jsx("div", { ref: provided.innerRef, className: cx('grid w-full gap-2', isMultiPanelView ? 'grid-cols-2' : 'grid-cols-1'), ...provided.droppableProps, children: dashboardItems.map((dashboardItem, index) => {
|
|
161
|
+
}), children: _jsxs("div", { children: [hasProfileSource && (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "text-sm font-medium capitalize", children: headerParts.length > 0 ? headerParts[0].replace(/"/g, '') : '' }), _jsx("div", { className: "text-xs", children: headerParts.length > 1
|
|
162
|
+
? headerParts[headerParts.length - 1].replace(/"/g, '')
|
|
163
|
+
: '' })] })), profileViewExternalMainActions != null ? profileViewExternalMainActions : null] }) }), _jsx(VisualisationToolbar, { groupBy: groupBy, toggleGroupBy: toggleGroupBy, hasProfileSource: hasProfileSource, pprofdownloading: pprofDownloading, profileSource: profileSource, queryClient: queryClient, onDownloadPProf: onDownloadPProf, isMultiPanelView: isMultiPanelView, dashboardItems: dashboardItems, curPath: curPath, setNewCurPath: setNewCurPath, profileType: profileSource?.ProfileType(), total: total, filtered: filtered, currentSearchString: currentSearchString, setSearchString: setSearchString }), _jsx("div", { className: "w-full", ref: ref, children: _jsx(DragDropContext, { onDragEnd: onDragEnd, children: _jsx(Droppable, { droppableId: "droppable", direction: "horizontal", children: provided => (_jsx("div", { ref: provided.innerRef, className: cx('grid w-full gap-2', isMultiPanelView ? 'grid-cols-2' : 'grid-cols-1'), ...provided.droppableProps, children: dashboardItems.map((dashboardItem, index) => {
|
|
157
164
|
return (_jsx(Draggable, { draggableId: dashboardItem, index: index, isDragDisabled: !isMultiPanelView, children: (provided, snapshot) => (_createElement("div", { ref: provided.innerRef, ...provided.draggableProps, key: dashboardItem, className: cx('w-full rounded p-2 shadow dark:border dark:border-gray-700 dark:bg-gray-700 min-h-96', snapshot.isDragging
|
|
158
165
|
? 'bg-gray-200 dark:bg-gray-500'
|
|
159
166
|
: 'bg-white dark:bg-gray-700') },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/index.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAMpC,UAAU,KAAK;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;CAClD;AAiED,QAAA,MAAM,cAAc,oFAOjB,KAAK,KAAG,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/index.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAMpC,UAAU,KAAK;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;CAClD;AAiED,QAAA,MAAM,cAAc,oFAOjB,KAAK,KAAG,GAAG,CAAC,OAsPd,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -77,6 +77,12 @@ currentQuery, profileType, queryBrowserRef, }) => {
|
|
|
77
77
|
const hiddenRowsCount = queryRows.length - 3;
|
|
78
78
|
const maxWidthInPixels = `max-w-[${queryBrowserRef.current?.offsetWidth.toString()}px]`;
|
|
79
79
|
const currentMatchers = currentQuery.matchersString();
|
|
80
|
+
const labelNameFromMatchers = useMemo(() => {
|
|
81
|
+
if (currentQuery === undefined)
|
|
82
|
+
return [];
|
|
83
|
+
const matchers = currentQuery.matchers;
|
|
84
|
+
return matchers.map(matcher => matcher.key);
|
|
85
|
+
}, [currentQuery]);
|
|
80
86
|
const fetchLabelValues = useCallback(async (labelName) => {
|
|
81
87
|
try {
|
|
82
88
|
const response = await queryClient.values({ labelName, match: [], profileType }, { meta: metadata }).response;
|
|
@@ -130,8 +136,9 @@ currentQuery, profileType, queryBrowserRef, }) => {
|
|
|
130
136
|
: [];
|
|
131
137
|
}, [labelNamesError, labelNamesResponse]);
|
|
132
138
|
const labelNameOptions = useMemo(() => {
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
const uniqueLabelNames = Array.from(new Set([...labelNames, ...labelNameFromMatchers]));
|
|
140
|
+
return transformLabelsForSelect(uniqueLabelNames);
|
|
141
|
+
}, [labelNames, labelNameFromMatchers]);
|
|
135
142
|
const updateRow = useCallback(async (index, field, value) => {
|
|
136
143
|
const updatedRows = [...queryRows];
|
|
137
144
|
const prevLabelName = updatedRows[index].labelName;
|
|
@@ -15,7 +15,7 @@ import { Fragment } from 'react';
|
|
|
15
15
|
import { Menu, Transition } from '@headlessui/react';
|
|
16
16
|
import { Icon } from '@iconify/react';
|
|
17
17
|
const ColumnsVisibility = ({ columns, visibility, setVisibility, }) => {
|
|
18
|
-
return (_jsx("div", { children: _jsxs(Menu, { as: "div", className: "relative text-left", children: [_jsx("div", { children: _jsxs(Menu.Button, { className: "relative w-full cursor-default rounded-md border bg-white py-2 pl-3 pr-10 text-left text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900 sm:text-sm", children: [_jsx("span", { className: "block overflow-x-hidden text-ellipsis", children: "
|
|
18
|
+
return (_jsx("div", { children: _jsxs(Menu, { as: "div", className: "relative text-left", children: [_jsx("div", { children: _jsxs(Menu.Button, { className: "relative w-full cursor-default rounded-md border bg-white py-2 pl-3 pr-10 text-left text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900 sm:text-sm", children: [_jsx("span", { className: "block overflow-x-hidden text-ellipsis", children: "Multiple" }), _jsx("span", { className: "pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2 text-gray-400", children: _jsx(Icon, { icon: "heroicons:chevron-down-20-solid", "aria-hidden": "true" }) })] }) }), _jsx(Transition, { as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0", children: _jsx(Menu.Items, { className: "absolute left-0 z-10 mt-1 min-w-[400px] overflow-auto rounded-md bg-gray-50 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-gray-600 dark:bg-gray-900 dark:ring-white dark:ring-opacity-20 sm:text-sm", children: _jsx("div", { className: "p-4", children: _jsx("fieldset", { children: _jsx("div", { className: "space-y-5", children: columns.map(col => (_jsxs("div", { className: "relative flex items-start", children: [_jsx("div", { className: "flex h-6 items-center", children: _jsx("input", { id: col.id, name: col.id, type: "checkbox", className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600", checked: visibility[col.id ?? ''] ?? false, onChange: () => {
|
|
19
19
|
setVisibility(col.id ?? '', !visibility[col.id ?? '']);
|
|
20
20
|
} }) }), _jsx("div", { className: "ml-3 text-sm leading-6", children: _jsx("label", { htmlFor: col.id, className: "font-medium text-gray-900 dark:text-gray-200", children: col.header }) })] }, col.id))) }) }) }) }) })] }) }));
|
|
21
21
|
};
|
package/dist/Table/index.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ interface DummyRow {
|
|
|
24
24
|
isBottomSubRow?: boolean;
|
|
25
25
|
}
|
|
26
26
|
export type Row = DataRow | DummyRow;
|
|
27
|
+
export declare const isDummyRow: (row: Row) => row is DummyRow;
|
|
27
28
|
interface TableProps {
|
|
28
29
|
data?: Uint8Array;
|
|
29
30
|
total: bigint;
|
|
@@ -36,7 +37,12 @@ interface TableProps {
|
|
|
36
37
|
isHalfScreen: boolean;
|
|
37
38
|
unit?: string;
|
|
38
39
|
}
|
|
40
|
+
export type ColumnName = 'flat' | 'flatPercentage' | 'flatDiff' | 'flatDiffPercentage' | 'cumulative' | 'cumulativePercentage' | 'cumulativeDiff' | 'cumulativeDiffPercentage' | 'name' | 'functionSystemName' | 'functionFileName' | 'mappingFile';
|
|
41
|
+
export declare const getPercentageString: (value: bigint | number, total: bigint | number) => string;
|
|
42
|
+
export declare const getRatioString: (value: bigint | number, total: bigint, filtered: bigint) => string;
|
|
43
|
+
export declare const possibleColumns: string[];
|
|
39
44
|
export declare const Table: React.NamedExoticComponent<TableProps>;
|
|
45
|
+
export declare const addPlusSign: (num: string) => string;
|
|
40
46
|
export declare const RowName: (mappingFileColumn: Vector | null, locationAddressColumn: Vector | null, functionNameColumn: Vector | null, row: number) => string;
|
|
41
47
|
export default Table;
|
|
42
48
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Table/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAA0D,MAAM,OAAO,CAAC;AAU/E,OAAO,EAAQ,MAAM,EAAgC,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Table/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAA0D,MAAM,OAAO,CAAC;AAU/E,OAAO,EAAQ,MAAM,EAAgC,MAAM,cAAc,CAAC;AAW1E,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAmB1C,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,MAAM,GAAG,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErC,eAAO,MAAM,UAAU,QAAS,GAAG,KAAG,GAAG,IAAI,QAE5C,CAAC;AAIF,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC;IACxD,YAAY,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,gBAAgB,GAChB,UAAU,GACV,oBAAoB,GACpB,YAAY,GACZ,sBAAsB,GACtB,gBAAgB,GAChB,0BAA0B,GAC1B,MAAM,GACN,oBAAoB,GACpB,kBAAkB,GAClB,aAAa,CAAC;AA4LlB,eAAO,MAAM,mBAAmB,UAAW,MAAM,GAAG,MAAM,SAAS,MAAM,GAAG,MAAM,KAAG,MAOpF,CAAC;AAEF,eAAO,MAAM,cAAc,UAAW,MAAM,GAAG,MAAM,SAAS,MAAM,YAAY,MAAM,KAAG,MAMxF,CAAC;AAEF,eAAO,MAAM,eAAe,UAa3B,CAAC;AAEF,eAAO,MAAM,KAAK,wCAoZhB,CAAC;AAEH,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,MAMzC,CAAC;AAEF,eAAO,MAAM,OAAO,sBACC,MAAM,GAAG,IAAI,yBACT,MAAM,GAAG,IAAI,sBAChB,MAAM,GAAG,IAAI,OAC5B,MAAM,KACV,MAoBF,CAAC;AAgDF,eAAe,KAAK,CAAC"}
|
package/dist/Table/index.js
CHANGED
|
@@ -17,11 +17,10 @@ import { createColumnHelper, } from '@tanstack/table-core';
|
|
|
17
17
|
import { tableFromIPC, vectorFromArray } from 'apache-arrow';
|
|
18
18
|
import cx from 'classnames';
|
|
19
19
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
20
|
-
import {
|
|
20
|
+
import { Table as TableComponent, TableSkeleton, useParcaContext, useURLState, } from '@parca/components';
|
|
21
21
|
import { getLastItem, isSearchMatch, valueFormatter } from '@parca/utilities';
|
|
22
22
|
import { useProfileViewContext } from '../ProfileView/ProfileViewContext';
|
|
23
23
|
import { hexifyAddress } from '../utils';
|
|
24
|
-
import ColumnsVisibility from './ColumnsVisibility';
|
|
25
24
|
import { getTopAndBottomExpandedRowModel } from './utils/topAndBottomExpandedRowModel';
|
|
26
25
|
const FIELD_MAPPING_FILE = 'mapping_file';
|
|
27
26
|
const FIELD_LOCATION_ADDRESS = 'location_address';
|
|
@@ -34,7 +33,7 @@ const FIELD_CUMULATIVE = 'cumulative';
|
|
|
34
33
|
const FIELD_CUMULATIVE_DIFF = 'cumulative_diff';
|
|
35
34
|
const FIELD_CALLERS = 'callers';
|
|
36
35
|
const FIELD_CALLEES = 'callees';
|
|
37
|
-
const isDummyRow = (row) => {
|
|
36
|
+
export const isDummyRow = (row) => {
|
|
38
37
|
return 'size' in row;
|
|
39
38
|
};
|
|
40
39
|
let doubleClickTimer = null;
|
|
@@ -146,10 +145,40 @@ const getCalleeRows = (callees) => {
|
|
|
146
145
|
}
|
|
147
146
|
return [{ size: 3 - rows.length, message: '', isBottomSubRow: true }, ...rows];
|
|
148
147
|
};
|
|
149
|
-
export const
|
|
148
|
+
export const getPercentageString = (value, total) => {
|
|
149
|
+
if (total === 0n) {
|
|
150
|
+
return '0%';
|
|
151
|
+
}
|
|
152
|
+
const percentage = (Number(value) / Number(total)) * 100;
|
|
153
|
+
return `${percentage.toFixed(2)}%`;
|
|
154
|
+
};
|
|
155
|
+
export const getRatioString = (value, total, filtered) => {
|
|
156
|
+
if (filtered === 0n) {
|
|
157
|
+
return ` ${getPercentageString(value, total)}`;
|
|
158
|
+
}
|
|
159
|
+
return `${getPercentageString(value, total)} / ${getPercentageString(value, filtered)}`;
|
|
160
|
+
};
|
|
161
|
+
export const possibleColumns = [
|
|
162
|
+
'flat',
|
|
163
|
+
'flatPercentage',
|
|
164
|
+
'flatDiff',
|
|
165
|
+
'flatDiffPercentage',
|
|
166
|
+
'cumulative',
|
|
167
|
+
'cumulativePercentage',
|
|
168
|
+
'cumulativeDiff',
|
|
169
|
+
'cumulativeDiffPercentage',
|
|
170
|
+
'name',
|
|
171
|
+
'functionSystemName',
|
|
172
|
+
'functionFileName',
|
|
173
|
+
'mappingFile',
|
|
174
|
+
];
|
|
175
|
+
export const Table = React.memo(function Table({ data, total, filtered, profileType, loading, currentSearchString, setSearchString = () => { }, isHalfScreen, unit, }) {
|
|
150
176
|
const [dashboardItems] = useURLState('dashboard_items', {
|
|
151
177
|
alwaysReturnArray: true,
|
|
152
178
|
});
|
|
179
|
+
const [tableColumns] = useURLState('table_columns', {
|
|
180
|
+
alwaysReturnArray: true,
|
|
181
|
+
});
|
|
153
182
|
const { isDarkMode } = useParcaContext();
|
|
154
183
|
const [expanded, setExpanded] = useState({});
|
|
155
184
|
const [scrollToIndex, setScrollToIndex] = useState(undefined);
|
|
@@ -310,6 +339,17 @@ export const Table = React.memo(function Table({ data, total, filtered, profileT
|
|
|
310
339
|
mappingFile: false,
|
|
311
340
|
};
|
|
312
341
|
});
|
|
342
|
+
useEffect(() => {
|
|
343
|
+
if (Array.isArray(tableColumns)) {
|
|
344
|
+
setColumnVisibility(prevState => {
|
|
345
|
+
const newState = { ...prevState };
|
|
346
|
+
Object.keys(newState).forEach(column => {
|
|
347
|
+
newState[column] = tableColumns.includes(column);
|
|
348
|
+
});
|
|
349
|
+
return newState;
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}, [tableColumns]);
|
|
313
353
|
const selectSpan = useCallback((span) => {
|
|
314
354
|
setSearchString(span.trim());
|
|
315
355
|
}, [setSearchString]);
|
|
@@ -359,22 +399,6 @@ export const Table = React.memo(function Table({ data, total, filtered, profileT
|
|
|
359
399
|
const enableHighlighting = useMemo(() => {
|
|
360
400
|
return currentSearchString != null && currentSearchString?.length > 0;
|
|
361
401
|
}, [currentSearchString]);
|
|
362
|
-
const clearSelection = useCallback(() => {
|
|
363
|
-
setSearchString('');
|
|
364
|
-
}, [setSearchString]);
|
|
365
|
-
useEffect(() => {
|
|
366
|
-
setActionButtons?.(_jsxs(_Fragment, { children: [_jsx(ColumnsVisibility, { columns: columns, visibility: columnVisibility, setVisibility: (id, visible) => {
|
|
367
|
-
setColumnVisibility({ ...columnVisibility, [id]: visible });
|
|
368
|
-
} }), dashboardItems.length > 1 && (_jsx(Button, { color: "neutral", onClick: clearSelection, className: "w-auto", variant: "neutral", disabled: currentSearchString === undefined || currentSearchString.length === 0, children: "Clear selection" }))] }));
|
|
369
|
-
}, [
|
|
370
|
-
dashboardItems,
|
|
371
|
-
clearSelection,
|
|
372
|
-
currentSearchString,
|
|
373
|
-
setActionButtons,
|
|
374
|
-
columns,
|
|
375
|
-
columnVisibility,
|
|
376
|
-
loading,
|
|
377
|
-
]);
|
|
378
402
|
const initialSorting = useMemo(() => {
|
|
379
403
|
return [
|
|
380
404
|
{
|
|
@@ -458,7 +482,7 @@ export const Table = React.memo(function Table({ data, total, filtered, profileT
|
|
|
458
482
|
setExpanded(newState);
|
|
459
483
|
}, CustomRowRenderer: CustomRowRenderer, scrollToIndex: scrollToIndex, estimatedRowHeight: ROW_HEIGHT }) }) }) }, "table-loaded") }));
|
|
460
484
|
});
|
|
461
|
-
const addPlusSign = (num) => {
|
|
485
|
+
export const addPlusSign = (num) => {
|
|
462
486
|
if (num.charAt(0) === '0' || num.charAt(0) === '-') {
|
|
463
487
|
return num;
|
|
464
488
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GroupByDropdown.d.ts","sourceRoot":"","sources":["../../../src/components/ActionButtons/GroupByDropdown.tsx"],"names":[],"mappings":"AAyDA,QAAA,MAAM,eAAe,gCAGlB;IACD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC,KAAG,KAAK,CAAC,GAAG,CAAC,OA6Db,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -13,7 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
import { Menu, Transition } from '@headlessui/react';
|
|
15
15
|
import { Icon } from '@iconify/react';
|
|
16
|
-
import { FIELD_FUNCTION_FILE_NAME, FIELD_FUNCTION_NAME, FIELD_LABELS, FIELD_LOCATION_ADDRESS, FIELD_MAPPING_FILE, } from '
|
|
16
|
+
import { FIELD_FUNCTION_FILE_NAME, FIELD_FUNCTION_NAME, FIELD_LABELS, FIELD_LOCATION_ADDRESS, FIELD_MAPPING_FILE, } from '../../ProfileIcicleGraph/IcicleGraphArrow';
|
|
17
17
|
const groupByOptions = [
|
|
18
18
|
{
|
|
19
19
|
value: FIELD_FUNCTION_NAME,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterByFunctionButton.d.ts","sourceRoot":"","sources":["../../src/components/FilterByFunctionButton.tsx"],"names":[],"mappings":"AAoBA,QAAA,MAAM,sBAAsB,QAAO,GAAG,CAAC,OAuCtC,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResultBox.d.ts","sourceRoot":"","sources":["../../../src/components/ShareButton/ResultBox.tsx"],"names":[],"mappings":"AAoBA,UAAU,KAAK;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,QAAA,MAAM,SAAS,yBAA6B,KAAK,KAAG,GAAG,CAAC,OAqCvD,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { QueryRequest, QueryServiceClient } from '@parca/client';
|
|
3
|
+
import { ProfileSource } from '../../ProfileSource';
|
|
4
|
+
interface Props {
|
|
5
|
+
profileSource?: ProfileSource;
|
|
6
|
+
queryClient?: QueryServiceClient;
|
|
7
|
+
queryRequest?: QueryRequest;
|
|
8
|
+
onDownloadPProf: () => void;
|
|
9
|
+
pprofdownloading: boolean;
|
|
10
|
+
profileViewExternalSubActions: React.ReactNode;
|
|
11
|
+
}
|
|
12
|
+
declare const ShareButton: ({ queryRequest, queryClient, profileSource, onDownloadPProf, pprofdownloading, profileViewExternalSubActions, }: Props) => JSX.Element;
|
|
13
|
+
export default ShareButton;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ShareButton/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAItC,OAAO,EAAC,YAAY,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAG/D,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAGlD,UAAU,KAAK;IACb,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,6BAA6B,EAAE,KAAK,CAAC,SAAS,CAAC;CAChD;AAkGD,QAAA,MAAM,WAAW,oHAOd,KAAK,KAAG,GAAG,CAAC,OA4Ed,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -13,7 +13,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
import { useState } from 'react';
|
|
15
15
|
import { Icon } from '@iconify/react';
|
|
16
|
-
import { Button, Modal, useGrpcMetadata } from '@parca/components';
|
|
16
|
+
import { Button, Dropdown, Modal, useGrpcMetadata } from '@parca/components';
|
|
17
17
|
import ResultBox from './ResultBox';
|
|
18
18
|
const ProfileShareModal = ({ isOpen, closeModal, queryRequest, queryClient, }) => {
|
|
19
19
|
const [isShared, setIsShared] = useState(false);
|
|
@@ -53,8 +53,31 @@ const ProfileShareModal = ({ isOpen, closeModal, queryRequest, queryClient, }) =
|
|
|
53
53
|
void handleSubmit();
|
|
54
54
|
}, disabled: loading || !isFormDataValid(), type: "submit", children: loading ? 'Sharing' : 'Share' }), error !== '' ? _jsx("p", { children: "Something went wrong please try again" }) : null] })) : (_jsxs(_Fragment, { children: [_jsx(ResultBox, { value: sharedLink, className: "mt-4" }), _jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Button, { variant: "neutral", className: "w-fit", onClick: onClose, children: "Close" }) })] }))] }) }));
|
|
55
55
|
};
|
|
56
|
-
const
|
|
57
|
-
const [
|
|
58
|
-
|
|
56
|
+
const ShareButton = ({ queryRequest, queryClient, profileSource, onDownloadPProf, pprofdownloading, profileViewExternalSubActions, }) => {
|
|
57
|
+
const [showProfileShareModal, setShowProfileShareModal] = useState(false);
|
|
58
|
+
const actions = [
|
|
59
|
+
{
|
|
60
|
+
key: 'shareProfile',
|
|
61
|
+
label: 'Share profile via link',
|
|
62
|
+
onSelect: () => setShowProfileShareModal(true),
|
|
63
|
+
id: 'h-share-profile-button',
|
|
64
|
+
disabled: profileSource === undefined && queryClient === undefined && queryRequest === undefined,
|
|
65
|
+
icon: 'material-symbols-light:link',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: 'downloadProfile',
|
|
69
|
+
label: pprofdownloading != null && pprofdownloading ? 'Downloading...' : 'Download as pprof',
|
|
70
|
+
onSelect: () => onDownloadPProf(),
|
|
71
|
+
id: 'h-download-pprof',
|
|
72
|
+
disabled: pprofdownloading,
|
|
73
|
+
icon: 'material-symbols:download',
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
return (_jsx(_Fragment, { children: profileViewExternalSubActions != null ? (_jsx(_Fragment, { children: _jsxs(Button, { className: "gap-2", variant: "neutral", onClick: e => {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
onDownloadPProf();
|
|
79
|
+
}, disabled: pprofdownloading, id: "h-download-pprof", children: [pprofdownloading != null && pprofdownloading ? 'Downloading...' : 'Download pprof', _jsx(Icon, { icon: "material-symbols:download", width: 20 })] }) })) : (_jsxs(_Fragment, { children: [_jsxs(Dropdown, { dropdownWidth: "w-48", element: _jsxs(Button, { variant: "neutral", children: ["Share", _jsx(Icon, { icon: "material-symbols:share", className: "h-5 w-5 ml-2" })] }), children: [_jsx("span", { className: "text-xs text-gray-400 capitalize px-2", children: "actions" }), actions.map(item => (_jsx(Dropdown.Item, { onSelect: item.onSelect, children: _jsxs("div", { id: item.id, className: "flex items-center", children: [_jsx("span", { children: item.label }), _jsx(Icon, { icon: item.icon, className: "ml-2 h-4 w-4" })] }) }, item.key)))] }), profileSource !== undefined &&
|
|
80
|
+
queryClient !== undefined &&
|
|
81
|
+
queryRequest !== undefined && (_jsx(ProfileShareModal, { isOpen: showProfileShareModal, closeModal: () => setShowProfileShareModal(false), queryRequest: queryRequest, queryClient: queryClient }))] })) }));
|
|
59
82
|
};
|
|
60
|
-
export default
|
|
83
|
+
export default ShareButton;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface DropdownElement {
|
|
2
|
+
active: JSX.Element;
|
|
3
|
+
expanded: JSX.Element;
|
|
4
|
+
}
|
|
5
|
+
export interface DropdownItem {
|
|
6
|
+
key: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
element: DropdownElement;
|
|
9
|
+
innerAction?: InnerAction;
|
|
10
|
+
}
|
|
11
|
+
export interface InnerAction {
|
|
12
|
+
text: string;
|
|
13
|
+
onClick: () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare function contructItemsFromArray(items: any[]): DropdownItem[];
|
|
16
|
+
declare const Dropdown: ({ items, selectedKey, onSelection, placeholder, width, className, loading, primary, disabled, icon, id, }: {
|
|
17
|
+
items: DropdownItem[];
|
|
18
|
+
selectedKey: string | undefined;
|
|
19
|
+
onSelection: (value: string) => void;
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
width?: number;
|
|
22
|
+
className?: string;
|
|
23
|
+
loading?: boolean;
|
|
24
|
+
primary?: boolean;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
icon?: JSX.Element;
|
|
27
|
+
id?: string;
|
|
28
|
+
}) => JSX.Element;
|
|
29
|
+
export default Dropdown;
|
|
30
|
+
//# sourceMappingURL=Dropdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dropdown.d.ts","sourceRoot":"","sources":["../../../src/components/ViewSelector/Dropdown.tsx"],"names":[],"mappings":"AAqBA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC;IACpB,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,YAAY,EAAE,CAMnE;AAED,QAAA,MAAM,QAAQ,8GAYX;IACD,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,KAAG,GAAG,CAAC,OA4GP,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|