@parca/profile 0.16.91 → 0.16.93

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.
@@ -24,119 +24,96 @@ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-run
24
24
  // limitations under the License.
25
25
  import { Profiler, useEffect, useMemo, useState } from 'react';
26
26
  import { scaleLinear } from 'd3';
27
- import { getNewSpanColor, parseParams } from '@parca/functions';
28
- import useUIFeatureFlag from '@parca/functions/useUIFeatureFlag';
27
+ import cx from 'classnames';
28
+ import { getNewSpanColor, useURLState } from '@parca/functions';
29
+ import { CloseIcon } from '@parca/icons';
29
30
  import { Button, Card, useParcaContext } from '@parca/components';
30
31
  import { useContainerDimensions } from '@parca/dynamicsize';
31
- import { useAppSelector, selectDarkMode, selectSearchNodeString, selectFilterByFunction, useAppDispatch, setSearchNodeString, } from '@parca/store';
32
+ import { useAppSelector, selectDarkMode } from '@parca/store';
32
33
  import { Callgraph } from '../';
33
34
  import ProfileShareButton from '../components/ProfileShareButton';
34
35
  import FilterByFunctionButton from './FilterByFunctionButton';
36
+ import ViewSelector from './ViewSelector';
35
37
  import ProfileIcicleGraph from '../ProfileIcicleGraph';
36
38
  import TopTable from '../TopTable';
37
39
  import useDelayedLoader from '../useDelayedLoader';
38
40
  import '../ProfileView.styles.css';
39
- import useUserPreference, { USER_PREFERENCES } from '@parca/functions/useUserPreference';
40
41
  function arrayEquals(a, b) {
41
42
  return (Array.isArray(a) &&
42
43
  Array.isArray(b) &&
43
44
  a.length === b.length &&
44
45
  a.every(function (val, index) { return val === b[index]; }));
45
46
  }
46
- export var useProfileVisState = function () {
47
- var _a = useState(function () {
48
- if (typeof window === 'undefined') {
49
- return 'icicle';
50
- }
51
- var router = parseParams(window.location.search);
52
- var currentViewFromURL = router.currentProfileView;
53
- if (currentViewFromURL != null) {
54
- return currentViewFromURL;
55
- }
56
- return 'icicle';
57
- }), currentView = _a[0], setCurrentView = _a[1];
58
- return { currentView: currentView, setCurrentView: setCurrentView };
59
- };
60
47
  export var ProfileView = function (_a) {
61
- var flamegraphData = _a.flamegraphData, topTableData = _a.topTableData, callgraphData = _a.callgraphData, sampleUnit = _a.sampleUnit, profileSource = _a.profileSource, queryClient = _a.queryClient, navigateTo = _a.navigateTo, profileVisState = _a.profileVisState, onDownloadPProf = _a.onDownloadPProf, onFlamegraphContainerResize = _a.onFlamegraphContainerResize;
62
- var dispatch = useAppDispatch();
48
+ var flamegraphData = _a.flamegraphData, topTableData = _a.topTableData, callgraphData = _a.callgraphData, sampleUnit = _a.sampleUnit, profileSource = _a.profileSource, queryClient = _a.queryClient, navigateTo = _a.navigateTo, onDownloadPProf = _a.onDownloadPProf, onFlamegraphContainerResize = _a.onFlamegraphContainerResize;
63
49
  var _b = useContainerDimensions(), ref = _b.ref, dimensions = _b.dimensions;
64
50
  var _c = useState([]), curPath = _c[0], setCurPath = _c[1];
65
- var currentView = profileVisState.currentView, setCurrentView = profileVisState.setCurrentView;
51
+ var _d = useURLState({
52
+ param: 'dashboard_items',
53
+ navigateTo: navigateTo,
54
+ }), rawDashboardItems = _d[0], setDashboardItems = _d[1];
55
+ var dashboardItems = rawDashboardItems;
66
56
  var isDarkMode = useAppSelector(selectDarkMode);
67
- var currentSearchString = useAppSelector(selectSearchNodeString);
68
- var filterByFunctionString = useAppSelector(selectFilterByFunction);
69
- var callgraphEnabled = useUIFeatureFlag('callgraph')[0];
70
- var highlightAfterFilteringEnabled = useUserPreference(USER_PREFERENCES.HIGHTLIGHT_AFTER_FILTERING.key)[0];
71
- var _d = useParcaContext(), loader = _d.loader, perf = _d.perf;
57
+ var isSinglePanelView = dashboardItems.length === 1;
58
+ var _e = useParcaContext(), loader = _e.loader, perf = _e.perf;
72
59
  useEffect(function () {
73
60
  // Reset the current path when the profile source changes
74
61
  setCurPath([]);
75
62
  }, [profileSource]);
76
63
  var isLoading = useMemo(function () {
77
- if (currentView === 'icicle') {
64
+ if (dashboardItems.includes('icicle')) {
78
65
  return Boolean(flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.loading);
79
66
  }
80
- if (currentView === 'callgraph') {
67
+ if (dashboardItems.includes('callgraph')) {
81
68
  return Boolean(callgraphData === null || callgraphData === void 0 ? void 0 : callgraphData.loading);
82
69
  }
83
- if (currentView === 'table') {
70
+ if (dashboardItems.includes('table')) {
84
71
  return Boolean(topTableData === null || topTableData === void 0 ? void 0 : topTableData.loading);
85
72
  }
86
- if (currentView === 'both') {
87
- return Boolean(flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.loading) || Boolean(topTableData === null || topTableData === void 0 ? void 0 : topTableData.loading);
88
- }
89
73
  return false;
90
- }, [currentView, callgraphData === null || callgraphData === void 0 ? void 0 : callgraphData.loading, flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.loading, topTableData === null || topTableData === void 0 ? void 0 : topTableData.loading]);
91
- useEffect(function () {
92
- if (!highlightAfterFilteringEnabled) {
93
- if (currentSearchString !== undefined && currentSearchString !== '') {
94
- dispatch(setSearchNodeString(''));
95
- }
96
- return;
97
- }
98
- if (isLoading) {
99
- return;
100
- }
101
- if (filterByFunctionString === currentSearchString) {
102
- return;
103
- }
104
- dispatch(setSearchNodeString(filterByFunctionString));
105
- }, [
106
- isLoading,
107
- filterByFunctionString,
108
- dispatch,
109
- highlightAfterFilteringEnabled,
110
- currentSearchString,
111
- ]);
74
+ }, [dashboardItems, callgraphData === null || callgraphData === void 0 ? void 0 : callgraphData.loading, flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.loading, topTableData === null || topTableData === void 0 ? void 0 : topTableData.loading]);
112
75
  var isLoaderVisible = useDelayedLoader(isLoading);
113
76
  if ((flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.error) != null) {
114
77
  console.error('Error: ', flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.error);
115
78
  return (_jsxs("div", __assign({ className: "p-10 flex justify-center" }, { children: ["An error occurred: ", flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.error.message] })));
116
79
  }
117
- var resetIcicleGraph = function () { return setCurPath([]); };
118
80
  var setNewCurPath = function (path) {
119
81
  if (!arrayEquals(curPath, path)) {
120
82
  setCurPath(path);
121
83
  }
122
84
  };
123
- var switchProfileView = function (view) {
124
- if (view == null) {
125
- return;
126
- }
127
- setCurrentView(view);
128
- if (navigateTo === undefined) {
129
- return;
130
- }
131
- var router = parseParams(window.location.search);
132
- navigateTo('/', __assign(__assign(__assign({}, router), { currentProfileView: view }), { searchString: currentSearchString }));
133
- };
134
85
  var maxColor = getNewSpanColor(isDarkMode);
135
- // TODO: fix colors for dark mode
136
86
  var minColor = scaleLinear([isDarkMode ? 'black' : 'white', maxColor])(0.3);
137
87
  var colorRange = [minColor, maxColor];
88
+ var getDashboardItemByType = function (_a) {
89
+ var type = _a.type, isHalfScreen = _a.isHalfScreen;
90
+ switch (type) {
91
+ case 'icicle': {
92
+ return (flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.data) != null ? (_jsx(Profiler, __assign({ id: "icicleGraph", onRender: perf === null || perf === void 0 ? void 0 : perf.onRender }, { children: _jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, graph: flamegraphData.data, sampleUnit: sampleUnit, onContainerResize: onFlamegraphContainerResize }) }))) : (_jsx(_Fragment, {}));
93
+ }
94
+ case 'callgraph': {
95
+ return (callgraphData === null || callgraphData === void 0 ? void 0 : callgraphData.data) != null && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) !== undefined ? (_jsx(Callgraph, { graph: callgraphData.data, sampleUnit: sampleUnit, width: isHalfScreen ? (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) / 2 : dimensions === null || dimensions === void 0 ? void 0 : dimensions.width, colorRange: colorRange })) : (_jsx(_Fragment, {}));
96
+ }
97
+ case 'table': {
98
+ return topTableData != null ? (_jsx(TopTable, { data: topTableData.data, sampleUnit: sampleUnit, navigateTo: navigateTo })) : (_jsx(_Fragment, {}));
99
+ }
100
+ default: {
101
+ return _jsx(_Fragment, {});
102
+ }
103
+ }
104
+ };
105
+ var handleResetView = function () {
106
+ setDashboardItems(['icicle']);
107
+ };
108
+ var handleClosePanel = function (visualizationType) {
109
+ var newDashboardItems = dashboardItems.filter(function (item) { return item !== visualizationType; });
110
+ setDashboardItems(newDashboardItems);
111
+ };
112
+ var dashboardItemsWithViewSelector = dashboardItems.map(function (dashboardItem, index) {
113
+ return (_jsxs("div", __assign({ className: cx('border dark:bg-gray-700 rounded border-gray-300 dark:border-gray-500 p-3', isSinglePanelView ? 'w-full' : 'w-1/2') }, { children: [_jsxs("div", __assign({ className: "w-full flex justify-end pb-2" }, { children: [_jsx(ViewSelector, { defaultValue: dashboardItem, navigateTo: navigateTo, position: index }), !isSinglePanelView && (_jsx("button", __assign({ type: "button", onClick: function () { return handleClosePanel(dashboardItem); }, className: "pl-2" }, { children: _jsx(CloseIcon, {}) })))] })), getDashboardItemByType({ type: dashboardItem, isHalfScreen: !isSinglePanelView })] }), index));
114
+ });
138
115
  return (_jsx(_Fragment, { children: _jsx("div", __assign({ className: "py-3" }, { children: _jsx(Card, { children: _jsxs(Card.Body, { children: [_jsxs("div", __assign({ className: "flex py-3 w-full" }, { children: [_jsxs("div", __assign({ className: "w-2/5 flex space-x-4" }, { children: [_jsxs("div", __assign({ className: "flex space-x-1" }, { children: [profileSource != null && queryClient != null ? (_jsx(ProfileShareButton, { queryRequest: profileSource.QueryRequest(), queryClient: queryClient, disabled: isLoading })) : null, _jsx(Button, __assign({ color: "neutral", onClick: function (e) {
139
116
  e.preventDefault();
140
117
  onDownloadPProf();
141
- }, disabled: isLoading }, { children: "Download pprof" }))] })), _jsx(FilterByFunctionButton, {})] })), _jsxs("div", __assign({ className: "flex ml-auto gap-2" }, { children: [_jsx(Button, __assign({ color: "neutral", onClick: resetIcicleGraph, disabled: curPath.length === 0, className: "whitespace-nowrap text-ellipsis" }, { children: "Reset View" })), callgraphEnabled ? (_jsx(Button, __assign({ variant: "".concat(currentView === 'callgraph' ? 'primary' : 'neutral'), onClick: function () { return switchProfileView('callgraph'); }, className: "whitespace-nowrap text-ellipsis" }, { children: "Callgraph" }))) : null, _jsxs("div", __assign({ className: "flex" }, { children: [_jsx(Button, __assign({ variant: "".concat(currentView === 'table' ? 'primary' : 'neutral'), className: "items-center rounded-tr-none rounded-br-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons", onClick: function () { return switchProfileView('table'); } }, { children: "Table" })), _jsx(Button, __assign({ variant: "".concat(currentView === 'both' ? 'primary' : 'neutral'), className: "items-center rounded-tl-none rounded-tr-none rounded-bl-none rounded-br-none border-l-0 border-r-0 w-auto px-8 whitespace-nowrap no-outline-on-buttons text-ellipsis", onClick: function () { return switchProfileView('both'); } }, { children: "Both" })), _jsx(Button, __assign({ variant: "".concat(currentView === 'icicle' ? 'primary' : 'neutral'), className: "items-center rounded-tl-none rounded-bl-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons", onClick: function () { return switchProfileView('icicle'); } }, { children: "Icicle Graph" }))] }))] }))] })), isLoaderVisible ? (_jsx(_Fragment, { children: loader })) : (_jsxs("div", __assign({ ref: ref, className: "flex space-x-4 justify-between w-full" }, { children: [currentView === 'icicle' && (flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.data) != null && (_jsx("div", __assign({ className: "w-full" }, { children: _jsx(Profiler, __assign({ id: "icicleGraph", onRender: perf === null || perf === void 0 ? void 0 : perf.onRender }, { children: _jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, graph: flamegraphData.data, sampleUnit: sampleUnit, onContainerResize: onFlamegraphContainerResize }) })) }))), currentView === 'callgraph' && (callgraphData === null || callgraphData === void 0 ? void 0 : callgraphData.data) != null && (_jsx("div", __assign({ className: "w-full" }, { children: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) !== undefined && (_jsx(Callgraph, { graph: callgraphData.data, sampleUnit: sampleUnit, width: dimensions === null || dimensions === void 0 ? void 0 : dimensions.width, colorRange: colorRange })) }))), currentView === 'table' && topTableData != null && (_jsx("div", __assign({ className: "w-full" }, { children: _jsx(TopTable, { data: topTableData.data, sampleUnit: sampleUnit }) }))), currentView === 'both' && (_jsxs(_Fragment, { children: [_jsx("div", __assign({ className: "w-1/2" }, { children: _jsx(TopTable, { data: topTableData === null || topTableData === void 0 ? void 0 : topTableData.data, sampleUnit: sampleUnit }) })), _jsx("div", __assign({ className: "w-1/2" }, { children: flamegraphData != null && (_jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, graph: flamegraphData.data, sampleUnit: sampleUnit })) }))] }))] })))] }) }) })) }));
118
+ }, disabled: isLoading }, { children: "Download pprof" }))] })), _jsx(FilterByFunctionButton, { navigateTo: navigateTo })] })), _jsxs("div", __assign({ className: "flex ml-auto gap-2" }, { children: [_jsx(Button, __assign({ color: "neutral", onClick: handleResetView, disabled: isSinglePanelView, className: "whitespace-nowrap text-ellipsis" }, { children: "Reset Panels" })), _jsx(ViewSelector, { defaultValue: "", navigateTo: navigateTo, position: -1, placeholderText: "Add panel...", primary: true, addView: true, disabled: !isSinglePanelView || dashboardItems.length < 1 })] }))] })), isLoaderVisible ? (_jsx(_Fragment, { children: loader })) : (_jsx("div", __assign({ ref: ref, className: "flex space-x-4 justify-between w-full" }, { children: dashboardItemsWithViewSelector })))] }) }) })) }));
142
119
  };
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import { QueryServiceClient } from '@parca/client';
3
3
  import { ProfileSource } from './ProfileSource';
4
- export type NavigateFunction = (path: string, queryParams: any) => void;
4
+ import { NavigateFunction } from '@parca/functions';
5
5
  interface ProfileViewWithDataProps {
6
6
  queryClient: QueryServiceClient;
7
7
  profileSource: ProfileSource;
@@ -47,20 +47,19 @@ import { jsx as _jsx } from "react/jsx-runtime";
47
47
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
48
48
  // See the License for the specific language governing permissions and
49
49
  // limitations under the License.
50
+ import { useEffect, useState } from 'react';
50
51
  import { QueryRequest_ReportType } from '@parca/client';
51
52
  import { useQuery } from './useQuery';
52
- import { ProfileView, useProfileVisState } from './ProfileView';
53
+ import { ProfileView } from './ProfileView';
53
54
  import { downloadPprof } from './utils';
54
55
  import { useGrpcMetadata, useParcaContext } from '@parca/components';
55
- import { saveAsBlob } from '@parca/functions';
56
- import { useEffect, useState } from 'react';
56
+ import { saveAsBlob, useURLState } from '@parca/functions';
57
57
  import useUserPreference, { USER_PREFERENCES } from '@parca/functions/useUserPreference';
58
58
  export var ProfileViewWithData = function (_a) {
59
59
  var _b, _c;
60
60
  var queryClient = _a.queryClient, profileSource = _a.profileSource, navigateTo = _a.navigateTo;
61
- var profileVisState = useProfileVisState();
62
61
  var metadata = useGrpcMetadata();
63
- var currentView = profileVisState.currentView;
62
+ var dashboardItems = useURLState({ param: 'dashboard_items', navigateTo: navigateTo })[0];
64
63
  var _d = useState(0), nodeTrimThreshold = _d[0], setNodeTrimThreshold = _d[1];
65
64
  var disableTrimming = useUserPreference(USER_PREFERENCES.DISABLE_GRAPH_TRIMMING.key)[0];
66
65
  useEffect(function () {
@@ -79,7 +78,7 @@ export var ProfileViewWithData = function (_a) {
79
78
  setNodeTrimThreshold(threshold);
80
79
  };
81
80
  var _e = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_TABLE, {
82
- skip: currentView !== 'icicle' && currentView !== 'both',
81
+ skip: !dashboardItems.includes('icicle'),
83
82
  nodeTrimThreshold: nodeTrimThreshold,
84
83
  }), flamegraphLoading = _e.isLoading, flamegraphResponse = _e.response, flamegraphError = _e.error;
85
84
  var perf = useParcaContext().perf;
@@ -94,10 +93,10 @@ export var ProfileViewWithData = function (_a) {
94
93
  perf === null || perf === void 0 ? void 0 : perf.markInteraction('Flamegraph Render', (_a = flamegraphResponse === null || flamegraphResponse === void 0 ? void 0 : flamegraphResponse.report) === null || _a === void 0 ? void 0 : _a.flamegraph.total);
95
94
  }, [flamegraphLoading, flamegraphResponse, perf]);
96
95
  var _f = useQuery(queryClient, profileSource, QueryRequest_ReportType.TOP, {
97
- skip: currentView !== 'table' && currentView !== 'both',
96
+ skip: !dashboardItems.includes('table'),
98
97
  }), topTableLoading = _f.isLoading, topTableResponse = _f.response, topTableError = _f.error;
99
98
  var _g = useQuery(queryClient, profileSource, QueryRequest_ReportType.CALLGRAPH, {
100
- skip: currentView !== 'callgraph',
99
+ skip: !dashboardItems.includes('callgraph'),
101
100
  }), callgraphLoading = _g.isLoading, callgraphResponse = _g.response, callgraphError = _g.error;
102
101
  var sampleUnit = profileSource.ProfileType().sampleUnit;
103
102
  var downloadPProfClick = function () { return __awaiter(void 0, void 0, void 0, function () {
@@ -140,6 +139,6 @@ export var ProfileViewWithData = function (_a) {
140
139
  ? (_c = callgraphResponse === null || callgraphResponse === void 0 ? void 0 : callgraphResponse.report) === null || _c === void 0 ? void 0 : _c.callgraph
141
140
  : undefined,
142
141
  error: callgraphError,
143
- }, profileVisState: profileVisState, sampleUnit: sampleUnit, profileSource: profileSource, queryClient: queryClient, navigateTo: navigateTo, onDownloadPProf: function () { return void downloadPProfClick(); }, onFlamegraphContainerResize: onFlamegraphContainerResize }));
142
+ }, sampleUnit: sampleUnit, profileSource: profileSource, queryClient: queryClient, navigateTo: navigateTo, onDownloadPProf: function () { return void downloadPProfClick(); }, onFlamegraphContainerResize: onFlamegraphContainerResize }));
144
143
  };
145
144
  export default ProfileViewWithData;
@@ -1,10 +1,12 @@
1
1
  /// <reference types="react" />
2
+ import { NavigateFunction } from '@parca/functions';
2
3
  import { TopNodeMeta, Top } from '@parca/client';
3
4
  import '../TopTable.styles.css';
4
5
  interface TopTableProps {
5
6
  data?: Top;
6
7
  sampleUnit: string;
8
+ navigateTo?: NavigateFunction;
7
9
  }
8
10
  export declare const RowLabel: (meta: TopNodeMeta | undefined) => string;
9
- export declare const TopTable: ({ data: top, sampleUnit: unit }: TopTableProps) => JSX.Element;
11
+ export declare const TopTable: ({ data: top, sampleUnit: unit, navigateTo }: TopTableProps) => JSX.Element;
10
12
  export default TopTable;
@@ -23,8 +23,7 @@ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
23
23
  // See the License for the specific language governing permissions and
24
24
  // limitations under the License.
25
25
  import React, { useCallback, useMemo } from 'react';
26
- import { getLastItem, valueFormatter, isSearchMatch } from '@parca/functions';
27
- import { useAppSelector, selectCompareMode, selectSearchNodeString, setSearchNodeString, useAppDispatch, } from '@parca/store';
26
+ import { getLastItem, valueFormatter, isSearchMatch, parseParams, selectQueryParam, } from '@parca/functions';
28
27
  import { Table } from '@parca/components';
29
28
  import { createColumnHelper } from '@tanstack/react-table';
30
29
  import { hexifyAddress } from '../utils';
@@ -51,10 +50,10 @@ var addPlusSign = function (num) {
51
50
  };
52
51
  export var TopTable = function (_a) {
53
52
  var _b;
54
- var top = _a.data, unit = _a.sampleUnit;
55
- var currentSearchString = useAppSelector(selectSearchNodeString);
56
- var compareMode = useAppSelector(selectCompareMode);
57
- var dispatch = useAppDispatch();
53
+ var top = _a.data, unit = _a.sampleUnit, navigateTo = _a.navigateTo;
54
+ var router = parseParams(window.location.search);
55
+ var currentSearchString = selectQueryParam('search_string');
56
+ var compareMode = Boolean(selectQueryParam('compare_a')) && Boolean(selectQueryParam('compare_b'));
58
57
  var columns = React.useMemo(function () {
59
58
  var cols = [
60
59
  columnHelper.accessor('meta', {
@@ -103,8 +102,10 @@ export var TopTable = function (_a) {
103
102
  return cols;
104
103
  }, [unit, compareMode]);
105
104
  var selectSpan = useCallback(function (span) {
106
- dispatch(setSearchNodeString(span.trim()));
107
- }, [dispatch]);
105
+ if (navigateTo != null) {
106
+ navigateTo('/', __assign(__assign({}, router), { search_string: span.trim() }), { replace: true });
107
+ }
108
+ }, [navigateTo, router]);
108
109
  var onRowClick = useCallback(function (row) {
109
110
  var meta = row.meta;
110
111
  if (meta === undefined) {
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- /*! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;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;font-feature-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}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:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-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{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,: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:#3b82f680;--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}}.\!absolute{position:absolute!important}.absolute{position:absolute}.relative{position:relative}.top-0{top:0}.right-0{right:0}.left-0{left:0}.z-50{z-index:50}.z-10{z-index:10}.m-auto{margin:auto}.m-0{margin:0}.m-2{margin:.5rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-20{margin-top:5rem;margin-bottom:5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mr-3{margin-right:.75rem}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.mt-2{margin-top:.5rem}.mt-1{margin-top:.25rem}.ml-auto{margin-left:auto}.mb-2{margin-bottom:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.table{display:table}.grid{display:grid}.h-36{height:9rem}.h-1{height:.25rem}.h-\[80vh\]{height:80vh}.h-4{height:1rem}.max-h-\[400px\]{max-height:400px}.w-auto{width:auto}.w-1\/5{width:20%}.w-4\/5{width:80%}.w-full{width:100%}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-40{width:10rem}.w-2\/5{width:40%}.w-1\/2{width:50%}.w-8{width:2rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.min-w-\[300px\]{min-width:300px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-0,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded-lg{border-radius:.5rem}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-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-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.bg-gray-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-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-inherit{background-color:inherit}.fill-transparent{fill:#0000}.fill-current{fill:currentColor}.p-3{padding:.75rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-20{padding-top:5rem;padding-bottom:5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-8{padding-left:2rem;padding-right:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pt-2{padding-top:.5rem}.pb-4{padding-bottom:1rem}.pr-2{padding-right:.5rem}.pl-2{padding-left:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-xs{font-size:.75rem;line-height:1rem}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-90{opacity:.9}.opacity-100{opacity:1}.opacity-0{opacity:0}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid #0000;outline-offset:2px}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-150{transition-duration:.15s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-indigo-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(55 48 163/var(--tw-ring-opacity))}[class~=theme-dark] .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/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-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}[class~=theme-dark] .dark\:\!text-indigo-400{--tw-text-opacity:1!important;color:rgb(129 140 248/var(--tw-text-opacity))!important}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}
1
+ /*! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.\!absolute{position:absolute!important}.absolute{position:absolute}.relative{position:relative}.top-0{top:0}.left-0{left:0}.right-0{right:0}.z-50{z-index:50}.z-10{z-index:10}.m-auto{margin:auto}.m-0{margin:0}.m-2{margin:.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-20{margin-bottom:5rem;margin-top:5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mr-3{margin-right:.75rem}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.mt-2{margin-top:.5rem}.mt-1{margin-top:.25rem}.ml-auto{margin-left:auto}.mb-2{margin-bottom:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.table{display:table}.grid{display:grid}.h-36{height:9rem}.h-1{height:.25rem}.h-\[80vh\]{height:80vh}.h-4{height:1rem}.max-h-\[400px\]{max-height:400px}.w-full{width:100%}.w-auto{width:auto}.w-1\/5{width:20%}.w-4\/5{width:80%}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-40{width:10rem}.w-1\/2{width:50%}.w-2\/5{width:40%}.w-8{width:2rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.min-w-\[300px\]{min-width:300px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-0,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded-lg{border-radius:.5rem}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-r-0{border-right-width:0}.border-l-0{border-left-width:0}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.bg-gray-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-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-inherit{background-color:inherit}.fill-transparent{fill:transparent}.fill-current{fill:currentColor}.p-3{padding:.75rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.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}.py-20{padding-bottom:5rem;padding-top:5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-1{padding-left:.25rem;padding-right:.25rem}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pt-2{padding-top:.5rem}.pb-4{padding-bottom:1rem}.pr-2{padding-right:.5rem}.pl-2{padding-left:.5rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-xs{font-size:.75rem;line-height:1rem}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-90{opacity:.9}.opacity-100{opacity:1}.opacity-0{opacity:0}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-150{transition-duration:.15s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-indigo-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(55 48 163/var(--tw-ring-opacity))}[class~=theme-dark] .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/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-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}[class~=theme-dark] .dark\:\!text-indigo-400{--tw-text-opacity:1!important;color:rgb(129 140 248/var(--tw-text-opacity))!important}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.91",
3
+ "version": "0.16.93",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@parca/client": "^0.16.62",
7
- "@parca/components": "^0.16.80",
7
+ "@parca/components": "^0.16.82",
8
8
  "@parca/dynamicsize": "^0.16.52",
9
- "@parca/functions": "^0.16.55",
9
+ "@parca/functions": "^0.16.56",
10
10
  "@parca/parser": "^0.16.52",
11
- "@parca/store": "^0.16.51",
11
+ "@parca/store": "^0.16.52",
12
12
  "d3": "7.8.0",
13
13
  "d3-scale": "^4.0.2",
14
14
  "d3-selection": "3.0.0",
@@ -42,5 +42,5 @@
42
42
  "access": "public",
43
43
  "registry": "https://registry.npmjs.org/"
44
44
  },
45
- "gitHead": "3e2718ec88a290c76b0f68efcc85106accc419fb"
45
+ "gitHead": "1dd773ebc4ab83aec05bbb78f72f0c1cc74cb869"
46
46
  }
@@ -20,8 +20,7 @@ import {Button} from '@parca/components';
20
20
  import {CallgraphNode, CallgraphEdge, Callgraph as CallgraphType} from '@parca/client';
21
21
  import {jsonToDot, getCurvePoints} from './utils';
22
22
  import type {HoveringNode} from '../GraphTooltip';
23
- import {useAppSelector, selectSearchNodeString} from '@parca/store';
24
- import {isSearchMatch} from '@parca/functions';
23
+ import {isSearchMatch, selectQueryParam} from '@parca/functions';
25
24
  import Tooltip from '../GraphTooltip';
26
25
  import {DEFAULT_NODE_HEIGHT, GRAPH_MARGIN} from './constants';
27
26
 
@@ -180,7 +179,7 @@ const Callgraph = ({graph, sampleUnit, width, colorRange}: Props): JSX.Element =
180
179
  y: 0,
181
180
  });
182
181
  const {nodes: rawNodes, cumulative: total} = graph;
183
- const currentSearchString = useAppSelector(selectSearchNodeString);
182
+ const currentSearchString = (selectQueryParam('search_string') as string) ?? '';
184
183
  const isSearchEmpty = currentSearchString === undefined || currentSearchString === '';
185
184
 
186
185
  useEffect(() => {
@@ -209,6 +208,8 @@ const Callgraph = ({graph, sampleUnit, width, colorRange}: Props): JSX.Element =
209
208
  if (width == null || graphData == null) return <></>;
210
209
  const {objects: gvizNodes, edges, bb: boundingBox} = JSON.parse(graphData) as GraphvizType;
211
210
 
211
+ if (gvizNodes.length < 1) return <>Profile has no samples</>;
212
+
212
213
  const graphBB = boundingBox.split(',');
213
214
  const bbWidth = Number(graphBB[2]);
214
215
  const bbHeight = Number(graphBB[3]);
@@ -344,7 +345,7 @@ const Callgraph = ({graph, sampleUnit, width, colorRange}: Props): JSX.Element =
344
345
  contextElement={containerRef.current}
345
346
  />
346
347
  {stage.scale.x !== 1 && (
347
- <Button className="w-auto !absolute top-0 right-0" variant="neutral" onClick={resetZoom}>
348
+ <Button className="w-auto !absolute top-0 left-0" variant="neutral" onClick={resetZoom}>
348
349
  Reset Zoom
349
350
  </Button>
350
351
  )}
@@ -21,9 +21,10 @@ import {Flamegraph, FlamegraphNode, FlamegraphRootNode} from '@parca/client';
21
21
  import {Mapping, Function, Location} from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
22
22
  import type {HoveringNode} from './GraphTooltip';
23
23
  import GraphTooltip from './GraphTooltip';
24
- import {diffColor, getLastItem, isSearchMatch} from '@parca/functions';
25
- import {selectDarkMode, selectSearchNodeString, useAppSelector} from '@parca/store';
24
+ import {diffColor, getLastItem, isSearchMatch, selectQueryParam} from '@parca/functions';
25
+ import {selectDarkMode, useAppSelector} from '@parca/store';
26
26
  import useIsShiftDown from '@parca/components/src/hooks/useIsShiftDown';
27
+ import {Button} from '@parca/components';
27
28
  import {hexifyAddress} from './utils';
28
29
 
29
30
  interface IcicleGraphProps {
@@ -103,7 +104,7 @@ function IcicleRect({
103
104
  onClick,
104
105
  curPath,
105
106
  }: IcicleRectProps): JSX.Element {
106
- const currentSearchString = useAppSelector(selectSearchNodeString);
107
+ const currentSearchString = (selectQueryParam('search_string') as string) ?? '';
107
108
  const isFaded = curPath.length > 0 && name !== curPath[curPath.length - 1];
108
109
  const styles = isFaded ? fadedIcicleRectStyles : icicleRectStyles;
109
110
 
@@ -419,6 +420,17 @@ export default function IcicleGraph({
419
420
  locations={graph.locations}
420
421
  functions={graph.function}
421
422
  />
423
+ <div className="w-full flex justify-start">
424
+ <Button
425
+ color="neutral"
426
+ onClick={() => setCurPath([])}
427
+ disabled={curPath.length === 0}
428
+ className="w-auto"
429
+ variant="neutral"
430
+ >
431
+ Reset zoom
432
+ </Button>
433
+ </div>
422
434
  <svg
423
435
  className="font-robotoMono"
424
436
  width={width}
@@ -11,9 +11,10 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import {ProfileDiffSource, ProfileSelection, ProfileViewWithData, NavigateFunction} from '..';
14
+ import {NavigateFunction} from '@parca/functions';
15
15
  import {Query} from '@parca/parser';
16
16
  import {QueryServiceClient} from '@parca/client';
17
+ import {ProfileDiffSource, ProfileSelection, ProfileViewWithData} from '..';
17
18
 
18
19
  import ProfileSelector, {QuerySelection} from '../ProfileSelector';
19
20
 
@@ -11,8 +11,9 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
+ import {NavigateFunction} from '@parca/functions';
14
15
  import {QueryServiceClient} from '@parca/client';
15
- import {ProfileSelection, ProfileViewWithData, NavigateFunction} from '..';
16
+ import {ProfileSelection, ProfileViewWithData} from '..';
16
17
 
17
18
  import ProfileSelector, {QuerySelection} from '../ProfileSelector';
18
19