@parca/profile 0.19.20 → 0.19.21

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.
Files changed (85) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts.map +1 -1
  3. package/dist/ProfileExplorer/ProfileExplorerCompare.js +1 -3
  4. package/dist/ProfileExplorer/index.d.ts.map +1 -1
  5. package/dist/ProfileExplorer/index.js +5 -8
  6. package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.d.ts.map +1 -1
  7. package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.js +0 -2
  8. package/dist/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.d.ts +0 -1
  9. package/dist/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.d.ts.map +1 -1
  10. package/dist/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.js +2 -11
  11. package/dist/ProfileFlameGraph/FlameGraphArrow/index.d.ts.map +1 -1
  12. package/dist/ProfileFlameGraph/FlameGraphArrow/index.js +6 -14
  13. package/dist/ProfileSelector/useAutoQuerySelector.d.ts.map +1 -1
  14. package/dist/ProfileSelector/useAutoQuerySelector.js +1 -1
  15. package/dist/ProfileSource.d.ts +4 -11
  16. package/dist/ProfileSource.d.ts.map +1 -1
  17. package/dist/ProfileSource.js +6 -14
  18. package/dist/ProfileView/components/ColorStackLegend.d.ts.map +1 -1
  19. package/dist/ProfileView/components/ColorStackLegend.js +14 -10
  20. package/dist/ProfileView/components/DashboardItems/index.d.ts +1 -3
  21. package/dist/ProfileView/components/DashboardItems/index.d.ts.map +1 -1
  22. package/dist/ProfileView/components/DashboardItems/index.js +2 -2
  23. package/dist/ProfileView/components/GroupByLabelsDropdown/index.d.ts.map +1 -1
  24. package/dist/ProfileView/components/GroupByLabelsDropdown/index.js +14 -1
  25. package/dist/ProfileView/components/InvertCallStack/index.js +1 -1
  26. package/dist/ProfileView/components/ProfileFilters/index.d.ts +5 -0
  27. package/dist/ProfileView/components/ProfileFilters/index.d.ts.map +1 -0
  28. package/dist/ProfileView/components/ProfileFilters/index.js +173 -0
  29. package/dist/ProfileView/components/ProfileFilters/useProfileFilters.d.ts +17 -0
  30. package/dist/ProfileView/components/ProfileFilters/useProfileFilters.d.ts.map +1 -0
  31. package/dist/ProfileView/components/ProfileFilters/useProfileFilters.js +209 -0
  32. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts +8 -0
  33. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts.map +1 -0
  34. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.js +87 -0
  35. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts.map +1 -1
  36. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.js +3 -10
  37. package/dist/ProfileView/components/Toolbars/index.d.ts +0 -5
  38. package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
  39. package/dist/ProfileView/components/Toolbars/index.js +6 -6
  40. package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.d.ts.map +1 -1
  41. package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.js +3 -12
  42. package/dist/ProfileView/hooks/useVisualizationState.d.ts +0 -3
  43. package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -1
  44. package/dist/ProfileView/hooks/useVisualizationState.js +0 -7
  45. package/dist/ProfileView/index.d.ts.map +1 -1
  46. package/dist/ProfileView/index.js +3 -5
  47. package/dist/ProfileViewWithData.d.ts.map +1 -1
  48. package/dist/ProfileViewWithData.js +8 -7
  49. package/dist/Sandwich/index.d.ts.map +1 -1
  50. package/dist/Sandwich/index.js +4 -2
  51. package/dist/Table/index.d.ts +0 -2
  52. package/dist/Table/index.d.ts.map +1 -1
  53. package/dist/Table/index.js +5 -32
  54. package/dist/styles.css +1 -1
  55. package/dist/useQuery.d.ts +1 -1
  56. package/dist/useQuery.d.ts.map +1 -1
  57. package/dist/useQuery.js +7 -40
  58. package/package.json +7 -7
  59. package/src/ProfileExplorer/ProfileExplorerCompare.tsx +0 -4
  60. package/src/ProfileExplorer/index.tsx +4 -13
  61. package/src/ProfileFlameGraph/FlameGraphArrow/ContextMenu.tsx +0 -2
  62. package/src/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.tsx +1 -14
  63. package/src/ProfileFlameGraph/FlameGraphArrow/index.tsx +4 -16
  64. package/src/ProfileSelector/useAutoQuerySelector.ts +1 -2
  65. package/src/ProfileSource.tsx +6 -49
  66. package/src/ProfileView/components/ColorStackLegend.tsx +16 -12
  67. package/src/ProfileView/components/DashboardItems/index.tsx +0 -6
  68. package/src/ProfileView/components/GroupByLabelsDropdown/index.tsx +15 -2
  69. package/src/ProfileView/components/InvertCallStack/index.tsx +1 -1
  70. package/src/ProfileView/components/ProfileFilters/index.tsx +294 -0
  71. package/src/ProfileView/components/ProfileFilters/useProfileFilters.ts +284 -0
  72. package/src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.ts +103 -0
  73. package/src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx +3 -16
  74. package/src/ProfileView/components/Toolbars/index.tsx +5 -35
  75. package/src/ProfileView/hooks/useResetStateOnProfileTypeChange.ts +5 -12
  76. package/src/ProfileView/hooks/useVisualizationState.ts +0 -11
  77. package/src/ProfileView/index.tsx +1 -15
  78. package/src/ProfileViewWithData.tsx +9 -9
  79. package/src/Sandwich/index.tsx +5 -2
  80. package/src/Table/index.tsx +3 -44
  81. package/src/useQuery.tsx +11 -43
  82. package/dist/ProfileView/components/FilterByFunctionButton.d.ts +0 -3
  83. package/dist/ProfileView/components/FilterByFunctionButton.d.ts.map +0 -1
  84. package/dist/ProfileView/components/FilterByFunctionButton.js +0 -89
  85. package/src/ProfileView/components/FilterByFunctionButton.tsx +0 -128
@@ -0,0 +1,173 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Copyright 2022 The Parca Authors
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ import { useCallback } from 'react';
15
+ import { Icon } from '@iconify/react';
16
+ import cx from 'classnames';
17
+ import { Button, Input, Select } from '@parca/components';
18
+ import { useProfileFilters } from './useProfileFilters';
19
+ export const isFilterComplete = (filter) => {
20
+ return (filter.value !== '' && filter.type != null && filter.field != null && filter.matchType != null);
21
+ };
22
+ const filterTypeItems = [
23
+ {
24
+ key: 'stack',
25
+ element: {
26
+ active: _jsx(_Fragment, { children: "Stack Filter" }),
27
+ expanded: (_jsxs(_Fragment, { children: [_jsx("span", { children: "Stack Filter" }), _jsx("br", {}), _jsx("span", { className: "text-xs", children: "Filters entire call stacks" })] })),
28
+ },
29
+ },
30
+ {
31
+ key: 'frame',
32
+ element: {
33
+ active: _jsx(_Fragment, { children: "Frame Filter" }),
34
+ expanded: (_jsxs(_Fragment, { children: [_jsx("span", { children: "Frame Filter" }), _jsx("br", {}), _jsx("span", { className: "text-xs", children: "Filters individual frames" })] })),
35
+ },
36
+ },
37
+ ];
38
+ const fieldItems = [
39
+ {
40
+ key: 'function_name',
41
+ element: {
42
+ active: _jsx(_Fragment, { children: "Function" }),
43
+ expanded: _jsx(_Fragment, { children: "Function Name" }),
44
+ },
45
+ },
46
+ {
47
+ key: 'binary',
48
+ element: {
49
+ active: _jsx(_Fragment, { children: "Binary" }),
50
+ expanded: _jsx(_Fragment, { children: "Binary/Executable Name" }),
51
+ },
52
+ },
53
+ {
54
+ key: 'system_name',
55
+ element: {
56
+ active: _jsx(_Fragment, { children: "System Name" }),
57
+ expanded: _jsx(_Fragment, { children: "System Name" }),
58
+ },
59
+ },
60
+ {
61
+ key: 'filename',
62
+ element: {
63
+ active: _jsx(_Fragment, { children: "Filename" }),
64
+ expanded: _jsx(_Fragment, { children: "Source Filename" }),
65
+ },
66
+ },
67
+ {
68
+ key: 'address',
69
+ element: {
70
+ active: _jsx(_Fragment, { children: "Address" }),
71
+ expanded: _jsx(_Fragment, { children: "Memory Address" }),
72
+ },
73
+ },
74
+ {
75
+ key: 'line_number',
76
+ element: {
77
+ active: _jsx(_Fragment, { children: "Line Number" }),
78
+ expanded: _jsx(_Fragment, { children: "Source Line Number" }),
79
+ },
80
+ },
81
+ ];
82
+ const stringMatchTypeItems = [
83
+ {
84
+ key: 'equal',
85
+ element: {
86
+ active: _jsx(_Fragment, { children: "Equals" }),
87
+ expanded: _jsx(_Fragment, { children: "Equals" }),
88
+ },
89
+ },
90
+ {
91
+ key: 'not_equal',
92
+ element: {
93
+ active: _jsx(_Fragment, { children: "Not Equals" }),
94
+ expanded: _jsx(_Fragment, { children: "Not Equals" }),
95
+ },
96
+ },
97
+ {
98
+ key: 'contains',
99
+ element: {
100
+ active: _jsx(_Fragment, { children: "Contains" }),
101
+ expanded: _jsx(_Fragment, { children: "Contains" }),
102
+ },
103
+ },
104
+ {
105
+ key: 'not_contains',
106
+ element: {
107
+ active: _jsx(_Fragment, { children: "Not Contains" }),
108
+ expanded: _jsx(_Fragment, { children: "Not Contains" }),
109
+ },
110
+ },
111
+ ];
112
+ const numberMatchTypeItems = [
113
+ {
114
+ key: 'equal',
115
+ element: {
116
+ active: _jsx(_Fragment, { children: "Equals" }),
117
+ expanded: _jsx(_Fragment, { children: "Equals" }),
118
+ },
119
+ },
120
+ {
121
+ key: 'not_equal',
122
+ element: {
123
+ active: _jsx(_Fragment, { children: "Not Equals" }),
124
+ expanded: _jsx(_Fragment, { children: "Not Equals" }),
125
+ },
126
+ },
127
+ ];
128
+ const ProfileFilters = () => {
129
+ const { localFilters, appliedFilters, hasUnsavedChanges, onApplyFilters, addFilter, removeFilter, updateFilter, resetFilters, } = useProfileFilters();
130
+ const handleKeyDown = useCallback((e) => {
131
+ if (e.key === 'Enter') {
132
+ e.preventDefault();
133
+ if (e.currentTarget.value.trim() === '') {
134
+ return;
135
+ }
136
+ onApplyFilters();
137
+ }
138
+ }, [onApplyFilters]);
139
+ const filtersToRender = localFilters.length > 0 ? localFilters : appliedFilters ?? [];
140
+ return (_jsxs("div", { className: "flex gap-2 w-full", children: [_jsxs("div", { className: "flex-1 flex flex-wrap gap-2", children: [filtersToRender.map(filter => {
141
+ const isNumberField = filter.field === 'address' || filter.field === 'line_number';
142
+ const matchTypeItems = isNumberField ? numberMatchTypeItems : stringMatchTypeItems;
143
+ return (_jsxs("div", { className: "flex items-center gap-0", children: [_jsx(Select, { items: filterTypeItems, selectedKey: filter.type, placeholder: "Select Filter", onSelection: key => {
144
+ const newType = key;
145
+ updateFilter(filter.id, {
146
+ type: newType,
147
+ field: filter.field ?? 'function_name',
148
+ matchType: filter.matchType ?? 'contains',
149
+ });
150
+ }, className: cx('rounded-l-md pr-1 gap-0 focus:z-50 focus:relative focus:outline-1 rounded-r-none ', filter.type != null ? 'border-r-0 w-28' : 'w-32') }), filter.type != null && (_jsxs(_Fragment, { children: [_jsx(Select, { items: fieldItems, selectedKey: filter.field ?? '', onSelection: key => {
151
+ const newField = key;
152
+ const isNewFieldNumber = newField === 'address' || newField === 'line_number';
153
+ const isCurrentFieldNumber = filter.field === 'address' || filter.field === 'line_number';
154
+ if (isNewFieldNumber !== isCurrentFieldNumber) {
155
+ updateFilter(filter.id, {
156
+ field: newField,
157
+ matchType: 'equal',
158
+ });
159
+ }
160
+ else {
161
+ updateFilter(filter.id, { field: newField });
162
+ }
163
+ }, className: "rounded-none border-r-0 w-32 pr-1 gap-0 focus:z-50 focus:relative focus:outline-1" }), _jsx(Select, { items: matchTypeItems, selectedKey: filter.matchType ?? '', onSelection: key => updateFilter(filter.id, { matchType: key }), className: "rounded-none border-r-0 pr-1 gap-0 focus:z-50 focus:relative focus:outline-1" }), _jsx(Input, { placeholder: "Value", value: filter.value, onChange: e => updateFilter(filter.id, { value: e.target.value }), onKeyDown: handleKeyDown, className: "rounded-none w-36 text-sm focus:outline-1" })] })), _jsx(Button, { variant: "neutral", onClick: () => {
164
+ if (localFilters.length === 1) {
165
+ resetFilters();
166
+ }
167
+ else {
168
+ removeFilter(filter.id);
169
+ }
170
+ }, className: cx('h-[38px] p-3', filter.type != null ? 'rounded-none rounded-r-md' : 'rounded-l-none rounded-r-md'), children: _jsx(Icon, { icon: "mdi:close", className: "h-4 w-4" }) })] }, filter.id));
171
+ }), localFilters.length > 0 && (_jsx(Button, { variant: "neutral", onClick: addFilter, className: "p-3 h-[38px]", children: _jsx(Icon, { icon: "mdi:filter-plus-outline", className: "h-4 w-4" }) })), localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (_jsxs(Button, { variant: "neutral", onClick: addFilter, className: "flex items-center gap-2", children: [_jsx(Icon, { icon: "mdi:filter-outline", className: "h-4 w-4" }), _jsx("span", { children: "Filter" })] }))] }), localFilters.length > 0 && hasUnsavedChanges && localFilters.some(isFilterComplete) && (_jsx(Button, { variant: "primary", onClick: onApplyFilters, className: cx('flex items-center gap-2 self-end'), children: _jsx("span", { children: "Apply" }) }))] }));
172
+ };
173
+ export default ProfileFilters;
@@ -0,0 +1,17 @@
1
+ import { type Filter } from '@parca/client';
2
+ import { type ProfileFilter } from '@parca/store';
3
+ export type { ProfileFilter };
4
+ export declare const useProfileFilters: () => {
5
+ localFilters: ProfileFilter[];
6
+ appliedFilters: ProfileFilter[];
7
+ protoFilters: Filter[];
8
+ hasUnsavedChanges: boolean;
9
+ onApplyFilters: () => void;
10
+ addFilter: () => void;
11
+ excludeBinary: (binaryName: string) => void;
12
+ removeExcludeBinary: (binaryName: string) => void;
13
+ removeFilter: (id: string) => void;
14
+ updateFilter: (id: string, updates: Partial<ProfileFilter>) => void;
15
+ resetFilters: () => void;
16
+ };
17
+ //# sourceMappingURL=useProfileFilters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useProfileFilters.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/ProfileFilters/useProfileFilters.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,eAAe,CAAC;AAC1C,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,cAAc,CAAC;AAItB,YAAY,EAAC,aAAa,EAAC,CAAC;AAoF5B,eAAO,MAAM,iBAAiB,QAAO;IACnC,YAAY,EAAE,aAAa,EAAE,CAAC;IAC9B,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,mBAAmB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC;IACpE,YAAY,EAAE,MAAM,IAAI,CAAC;CAkK1B,CAAC"}
@@ -0,0 +1,209 @@
1
+ // Copyright 2022 The Parca Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { useCallback, useEffect, useMemo } from 'react';
14
+ import { selectLocalFilters, setLocalFilters, useAppDispatch, useAppSelector, } from '@parca/store';
15
+ import { useProfileFiltersUrlState } from './useProfileFiltersUrlState';
16
+ // Convert ProfileFilter[] to protobuf Filter[] matching the expected structure
17
+ const convertToProtoFilters = (profileFilters) => {
18
+ return profileFilters
19
+ .filter(f => f.value !== '' && f.type != null && f.field != null && f.matchType != null) // Only include complete filters with values
20
+ .map(f => {
21
+ // Build the condition based on field type
22
+ const isNumberField = f.field === 'address' || f.field === 'line_number';
23
+ let condition;
24
+ if (isNumberField) {
25
+ const numValue = BigInt(f.value);
26
+ condition = {
27
+ condition: f.matchType === 'equal'
28
+ ? { oneofKind: 'equal', equal: numValue }
29
+ : { oneofKind: 'notEqual', notEqual: numValue },
30
+ };
31
+ }
32
+ else {
33
+ condition = {
34
+ condition: f.matchType === 'equal'
35
+ ? { oneofKind: 'equal', equal: f.value }
36
+ : f.matchType === 'not_equal'
37
+ ? { oneofKind: 'notEqual', notEqual: f.value }
38
+ : f.matchType === 'contains'
39
+ ? { oneofKind: 'contains', contains: f.value }
40
+ : { oneofKind: 'notContains', notContains: f.value },
41
+ };
42
+ }
43
+ // Create FilterCriteria
44
+ const criteria = {};
45
+ switch (f.field) {
46
+ case 'function_name':
47
+ criteria.functionName = condition;
48
+ break;
49
+ case 'binary':
50
+ criteria.binary = condition;
51
+ break;
52
+ case 'system_name':
53
+ criteria.systemName = condition;
54
+ break;
55
+ case 'filename':
56
+ criteria.filename = condition;
57
+ break;
58
+ case 'address':
59
+ criteria.address = condition;
60
+ break;
61
+ case 'line_number':
62
+ criteria.lineNumber = condition;
63
+ break;
64
+ }
65
+ // Create the appropriate filter type with proper oneofKind structure
66
+ if (f.type === 'stack') {
67
+ return {
68
+ filter: {
69
+ oneofKind: 'stackFilter',
70
+ stackFilter: {
71
+ filter: {
72
+ oneofKind: 'criteria',
73
+ criteria,
74
+ },
75
+ },
76
+ },
77
+ };
78
+ }
79
+ else {
80
+ return {
81
+ filter: {
82
+ oneofKind: 'frameFilter',
83
+ frameFilter: {
84
+ filter: {
85
+ oneofKind: 'criteria',
86
+ criteria,
87
+ },
88
+ },
89
+ },
90
+ };
91
+ }
92
+ });
93
+ };
94
+ export const useProfileFilters = () => {
95
+ const { appliedFilters, setAppliedFilters } = useProfileFiltersUrlState();
96
+ const dispatch = useAppDispatch();
97
+ const localFilters = useAppSelector(selectLocalFilters);
98
+ useEffect(() => {
99
+ if (appliedFilters != null && appliedFilters.length > 0) {
100
+ // Check if they're different to avoid unnecessary updates
101
+ const areFiltersEqual = appliedFilters.length === localFilters.length &&
102
+ appliedFilters.every((applied, index) => {
103
+ const local = localFilters[index];
104
+ return (local != null &&
105
+ applied.type === local.type &&
106
+ applied.field === local.field &&
107
+ applied.matchType === local.matchType &&
108
+ applied.value === local.value);
109
+ });
110
+ if (!areFiltersEqual) {
111
+ dispatch(setLocalFilters(appliedFilters));
112
+ }
113
+ }
114
+ else if (appliedFilters != null && appliedFilters.length === 0 && localFilters.length > 0) {
115
+ dispatch(setLocalFilters([]));
116
+ }
117
+ // eslint-disable-next-line react-hooks/exhaustive-deps
118
+ }, []);
119
+ const hasUnsavedChanges = useMemo(() => {
120
+ const localWithValues = localFilters.filter(f => f.value !== '');
121
+ const appliedWithValues = (appliedFilters ?? []).filter(f => f.value !== '');
122
+ if (localWithValues.length !== appliedWithValues.length)
123
+ return true;
124
+ return !localWithValues.every((local, index) => {
125
+ const applied = appliedWithValues[index];
126
+ return (local.type === applied?.type &&
127
+ local.field === applied?.field &&
128
+ local.matchType === applied?.matchType &&
129
+ local.value === applied?.value);
130
+ });
131
+ }, [localFilters, appliedFilters]);
132
+ const addFilter = useCallback(() => {
133
+ const newFilter = {
134
+ id: `filter-${Date.now()}-${Math.random()}`,
135
+ value: '',
136
+ };
137
+ dispatch(setLocalFilters([...localFilters, newFilter]));
138
+ }, [dispatch, localFilters]);
139
+ const excludeBinary = useCallback((binaryName) => {
140
+ // Check if this binary is already being filtered with not_contains
141
+ const existingFilter = (appliedFilters ?? []).find(f => f.type === 'frame' &&
142
+ f.field === 'binary' &&
143
+ f.matchType === 'not_contains' &&
144
+ f.value === binaryName);
145
+ if (existingFilter != null) {
146
+ return; // Already exists, don't add duplicate
147
+ }
148
+ const newFilter = {
149
+ id: `filter-${Date.now()}-${Math.random()}`,
150
+ type: 'frame',
151
+ field: 'binary',
152
+ matchType: 'not_contains',
153
+ value: binaryName,
154
+ };
155
+ dispatch(setLocalFilters([...localFilters, newFilter]));
156
+ // Auto-apply the filter since it has a value
157
+ const filtersToApply = [...(appliedFilters ?? []), newFilter];
158
+ setAppliedFilters(filtersToApply);
159
+ }, [appliedFilters, setAppliedFilters, dispatch, localFilters]);
160
+ const removeExcludeBinary = useCallback((binaryName) => {
161
+ // Search for the exclude filter (not_contains) for this binary
162
+ const filterToRemove = (appliedFilters ?? []).find(f => f.type === 'frame' &&
163
+ f.field === 'binary' &&
164
+ f.matchType === 'not_contains' &&
165
+ f.value === binaryName);
166
+ if (filterToRemove != null) {
167
+ // Remove the filter from applied filters
168
+ const updatedAppliedFilters = (appliedFilters ?? []).filter(f => f.id !== filterToRemove.id);
169
+ setAppliedFilters(updatedAppliedFilters);
170
+ // Also remove from local filters
171
+ const updatedLocalFilters = localFilters.filter(f => f.id !== filterToRemove.id);
172
+ dispatch(setLocalFilters(updatedLocalFilters));
173
+ }
174
+ }, [appliedFilters, setAppliedFilters, dispatch, localFilters]);
175
+ const removeFilter = useCallback((id) => {
176
+ dispatch(setLocalFilters(localFilters.filter(f => f.id !== id)));
177
+ }, [dispatch, localFilters]);
178
+ const updateFilter = useCallback((id, updates) => {
179
+ dispatch(setLocalFilters(localFilters.map(f => (f.id === id ? { ...f, ...updates } : f))));
180
+ }, [dispatch, localFilters]);
181
+ const resetFilters = useCallback(() => {
182
+ dispatch(setLocalFilters([]));
183
+ setAppliedFilters([]);
184
+ }, [dispatch, setAppliedFilters]);
185
+ const onApplyFilters = useCallback(() => {
186
+ const validFilters = localFilters.filter(f => f.value !== '' && f.type != null && f.field != null && f.matchType != null);
187
+ const filtersToApply = validFilters.map((f, index) => ({
188
+ ...f,
189
+ id: `filter-${Date.now()}-${index}`,
190
+ }));
191
+ setAppliedFilters(filtersToApply);
192
+ }, [localFilters, setAppliedFilters]);
193
+ const protoFilters = useMemo(() => {
194
+ return convertToProtoFilters(appliedFilters ?? []);
195
+ }, [appliedFilters]);
196
+ return {
197
+ localFilters,
198
+ appliedFilters,
199
+ protoFilters,
200
+ hasUnsavedChanges,
201
+ onApplyFilters,
202
+ addFilter,
203
+ excludeBinary,
204
+ removeExcludeBinary,
205
+ removeFilter,
206
+ updateFilter,
207
+ resetFilters,
208
+ };
209
+ };
@@ -0,0 +1,8 @@
1
+ import { type ParamValueSetterCustom } from '@parca/components';
2
+ import { type ProfileFilter } from '@parca/store';
3
+ export declare const decodeProfileFilters: (encoded: string) => ProfileFilter[];
4
+ export declare const useProfileFiltersUrlState: () => {
5
+ appliedFilters: ProfileFilter[];
6
+ setAppliedFilters: ParamValueSetterCustom<ProfileFilter[]>;
7
+ };
8
+ //# sourceMappingURL=useProfileFiltersUrlState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useProfileFiltersUrlState.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.ts"],"names":[],"mappings":"AAaA,OAAO,EAAoB,KAAK,sBAAsB,EAAC,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAC,KAAK,aAAa,EAAC,MAAM,cAAc,CAAC;AA8ChD,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,aAAa,EAmBnE,CAAC;AAEF,eAAO,MAAM,yBAAyB,QAAO;IAC3C,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,iBAAiB,EAAE,sBAAsB,CAAC,aAAa,EAAE,CAAC,CAAC;CAmB5D,CAAC"}
@@ -0,0 +1,87 @@
1
+ // Copyright 2022 The Parca Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { useURLStateCustom } from '@parca/components';
14
+ // Compact encoding mappings
15
+ const TYPE_MAP = {
16
+ stack: 's',
17
+ frame: 'f',
18
+ };
19
+ const FIELD_MAP = {
20
+ function_name: 'fn',
21
+ binary: 'b',
22
+ system_name: 'sn',
23
+ filename: 'f',
24
+ address: 'a',
25
+ line_number: 'ln',
26
+ };
27
+ const MATCH_MAP = {
28
+ equal: '=',
29
+ not_equal: '!=',
30
+ contains: '~',
31
+ not_contains: '!~',
32
+ };
33
+ // Reverse mappings for decoding
34
+ const TYPE_MAP_REVERSE = Object.fromEntries(Object.entries(TYPE_MAP).map(([k, v]) => [v, k]));
35
+ const FIELD_MAP_REVERSE = Object.fromEntries(Object.entries(FIELD_MAP).map(([k, v]) => [v, k]));
36
+ const MATCH_MAP_REVERSE = Object.fromEntries(Object.entries(MATCH_MAP).map(([k, v]) => [v, k]));
37
+ // Encode filters to compact string format
38
+ const encodeProfileFilters = (filters) => {
39
+ if (filters.length === 0)
40
+ return '';
41
+ return filters
42
+ .filter(f => f.value !== '' && f.type != null && f.field != null && f.matchType != null)
43
+ .map(f => {
44
+ const type = TYPE_MAP[f.type];
45
+ const field = FIELD_MAP[f.field];
46
+ const match = MATCH_MAP[f.matchType];
47
+ const value = encodeURIComponent(f.value);
48
+ return `${type}:${field}:${match}:${value}`;
49
+ })
50
+ .join(',');
51
+ };
52
+ // Decode filters from compact string format
53
+ export const decodeProfileFilters = (encoded) => {
54
+ if (encoded === '' || encoded === undefined)
55
+ return [];
56
+ try {
57
+ return encoded.split(',').map((filter, index) => {
58
+ const [type, field, match, ...valueParts] = filter.split(':');
59
+ const value = decodeURIComponent(valueParts.join(':')); // Handle values with colons
60
+ return {
61
+ id: `filter-${Date.now()}-${index}`,
62
+ type: TYPE_MAP_REVERSE[type],
63
+ field: FIELD_MAP_REVERSE[field],
64
+ matchType: MATCH_MAP_REVERSE[match],
65
+ value,
66
+ };
67
+ });
68
+ }
69
+ catch {
70
+ return [];
71
+ }
72
+ };
73
+ export const useProfileFiltersUrlState = () => {
74
+ // Store applied filters in URL state for persistence using compact encoding
75
+ const [appliedFilters, setAppliedFilters] = useURLStateCustom('profile_filters', {
76
+ parse: value => {
77
+ return decodeProfileFilters(value);
78
+ },
79
+ stringify: value => {
80
+ return encodeProfileFilters(value);
81
+ },
82
+ });
83
+ return {
84
+ appliedFilters,
85
+ setAppliedFilters,
86
+ };
87
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"MultiLevelDropdown.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAQtE,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAkK1C,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAkQzD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"MultiLevelDropdown.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAQtE,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAkK1C,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAqPzD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -77,9 +77,8 @@ const MultiLevelDropdown = ({ onSelect, profileType, groupBy, toggleGroupBy, isT
77
77
  defaultValue: FIELD_FUNCTION_NAME,
78
78
  });
79
79
  const [colorStackLegend, setStoreColorStackLegend] = useURLState('color_stack_legend');
80
- const [binaryFrameFilter, setBinaryFrameFilter] = useURLState('binary_frame_filter');
81
80
  const [colorBy, setColorBy] = useURLState('color_by');
82
- const [hiddenBinaries, setHiddenBinaries] = useURLState('binary_frame_filter', {
81
+ const [hiddenBinaries, setHiddenBinaries] = useURLState('hidden_binaries', {
83
82
  defaultValue: [],
84
83
  alwaysReturnArray: true,
85
84
  });
@@ -117,7 +116,7 @@ const MultiLevelDropdown = ({ onSelect, profileType, groupBy, toggleGroupBy, isT
117
116
  setStoreColorStackLegend(value);
118
117
  }, [setStoreColorStackLegend]);
119
118
  const resetLegend = () => {
120
- setBinaryFrameFilter([]);
119
+ setHiddenBinaries([]);
121
120
  };
122
121
  const menuItems = [
123
122
  {
@@ -188,12 +187,6 @@ const MultiLevelDropdown = ({ onSelect, profileType, groupBy, toggleGroupBy, isT
188
187
  hide: !compareMode,
189
188
  icon: isCompareAbsolute ? 'fluent-mdl2:compare' : 'fluent-mdl2:compare-uneven',
190
189
  },
191
- {
192
- label: 'Highlight matching nodes after filtering',
193
- hide: !!isTableVizOnly,
194
- customSubmenu: (_jsx(SwitchMenuItem, { label: "Highlight matching nodes after filtering", id: "h-highlight-after-filtering", userPreferenceDetails: USER_PREFERENCES.HIGHTLIGHT_AFTER_FILTERING })),
195
- renderAsDiv: true,
196
- },
197
190
  {
198
191
  label: 'Dock Graph MetaInfo',
199
192
  hide: !!isTableVizOnly,
@@ -208,7 +201,7 @@ const MultiLevelDropdown = ({ onSelect, profileType, groupBy, toggleGroupBy, isT
208
201
  },
209
202
  {
210
203
  label: 'Reset Legend',
211
- hide: binaryFrameFilter === undefined || binaryFrameFilter.length === 0,
204
+ hide: hiddenBinaries === undefined || hiddenBinaries.length === 0,
212
205
  onclick: () => resetLegend(),
213
206
  id: 'h-reset-legend-button',
214
207
  icon: 'system-uicons:reset',
@@ -16,12 +16,9 @@ export interface VisualisationToolbarProps {
16
16
  profileType?: ProfileType;
17
17
  total: bigint;
18
18
  filtered: bigint;
19
- currentSearchString?: string;
20
- setSearchString?: (value: string) => void;
21
19
  groupByLabels: string[];
22
20
  preferencesModal?: boolean;
23
21
  profileViewExternalSubActions?: React.ReactNode;
24
- clearSelection: () => void;
25
22
  setGroupByLabels: (labels: string[]) => void;
26
23
  showVisualizationSelector?: boolean;
27
24
  sandwichFunctionName?: string;
@@ -30,8 +27,6 @@ export interface TableToolbarProps {
30
27
  profileType?: ProfileType;
31
28
  total: bigint;
32
29
  filtered: bigint;
33
- clearSelection: () => void;
34
- currentSearchString?: string;
35
30
  }
36
31
  export interface FlameGraphToolbarProps {
37
32
  curPath: CurrentPathFrame[];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/Toolbars/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,EAAE,EAAC,MAAM,OAAO,CAAC;AAIzB,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAC,gBAAgB,EAAC,MAAM,kDAAkD,CAAC;AAClF,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAUrD,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,aAAa,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAClD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6BAA6B,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAChD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC7C,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,aAAa,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,8BAA8B;IAC7C,yBAAyB,EAAE,MAAM,IAAI,CAAC;IACtC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAwB9C,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAiBxD,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC,8BAA8B,CAmBxE,CAAC;AAMF,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,yBAAyB,CA+F9D,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/Toolbars/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,EAAE,EAAC,MAAM,OAAO,CAAC;AAIzB,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAC,gBAAgB,EAAC,MAAM,kDAAkD,CAAC;AAClF,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAUrD,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,aAAa,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAClD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6BAA6B,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAChD,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC7C,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,aAAa,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,8BAA8B;IAC7C,yBAAyB,EAAE,MAAM,IAAI,CAAC;IACtC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAQ9C,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAiBxD,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC,8BAA8B,CAmBxE,CAAC;AAMF,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,yBAAyB,CAsF9D,CAAC"}
@@ -1,16 +1,16 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Icon } from '@iconify/react';
3
3
  import { Button } from '@parca/components';
4
4
  import { useDashboard } from '../../context/DashboardContext';
5
5
  import GroupByDropdown from '../ActionButtons/GroupByDropdown';
6
- import FilterByFunctionButton from '../FilterByFunctionButton';
7
6
  import InvertCallStack from '../InvertCallStack';
7
+ import ProfileFilters from '../ProfileFilters';
8
8
  import ShareButton from '../ShareButton';
9
9
  import ViewSelector from '../ViewSelector';
10
10
  import MultiLevelDropdown from './MultiLevelDropdown';
11
11
  import TableColumnsDropdown from './TableColumnsDropdown';
12
- export const TableToolbar = ({ profileType, total, filtered, clearSelection, currentSearchString, }) => {
13
- return (_jsx(_Fragment, { children: _jsxs("div", { className: "flex w-full gap-2 items-end", children: [_jsx(TableColumnsDropdown, { profileType: profileType, total: total, filtered: filtered }), _jsx(Button, { color: "neutral", onClick: clearSelection, className: "w-auto", variant: "neutral", disabled: currentSearchString === undefined || currentSearchString.length === 0, children: "Clear selection" })] }) }));
12
+ export const TableToolbar = ({ profileType, total, filtered }) => {
13
+ return (_jsx(_Fragment, { children: _jsx("div", { className: "flex w-full gap-2 items-end", children: _jsx(TableColumnsDropdown, { profileType: profileType, total: total, filtered: filtered }) }) }));
14
14
  };
15
15
  export const FlameGraphToolbar = ({ curPath, setNewCurPath }) => {
16
16
  return (_jsx(_Fragment, { children: _jsx("div", { className: "flex w-full gap-2 items-end", children: _jsxs(Button, { variant: "neutral", className: "gap-2 w-max h-fit", onClick: () => setNewCurPath([]), disabled: curPath.length === 0, id: "h-reset-graph", children: ["Reset graph", _jsx(Icon, { icon: "system-uicons:reset", width: 20 })] }) }) }));
@@ -19,7 +19,7 @@ export const SandwichFlameGraphToolbar = ({ resetSandwichFunctionName, sandwichF
19
19
  return (_jsx(_Fragment, { children: _jsx("div", { className: "flex w-full gap-2 items-end justify-between", children: _jsx(Button, { color: "neutral", onClick: () => resetSandwichFunctionName(), className: "w-auto", variant: "neutral", disabled: sandwichFunctionName === undefined || sandwichFunctionName.length === 0, children: "Reset view" }) }) }));
20
20
  };
21
21
  const Divider = () => (_jsx("div", { className: "border-t mt-4 border-gray-200 dark:border-gray-700 h-[1px] w-full pb-4" }));
22
- export const VisualisationToolbar = ({ groupBy, toggleGroupBy, groupByLabels, setGroupByLabels, profileType, profileSource, queryClient, onDownloadPProf, pprofdownloading, profileViewExternalSubActions, curPath, setNewCurPath, total, filtered, currentSearchString, clearSelection, showVisualizationSelector = true, }) => {
22
+ export const VisualisationToolbar = ({ groupBy, toggleGroupBy, groupByLabels, setGroupByLabels, profileType, profileSource, queryClient, onDownloadPProf, pprofdownloading, profileViewExternalSubActions, curPath, setNewCurPath, total, filtered, showVisualizationSelector = true, }) => {
23
23
  const { dashboardItems } = useDashboard();
24
24
  const isTableViz = dashboardItems?.includes('table');
25
25
  const isTableVizOnly = dashboardItems?.length === 1 && isTableViz;
@@ -31,5 +31,5 @@ export const VisualisationToolbar = ({ groupBy, toggleGroupBy, groupByLabels, se
31
31
  fields: groupBy ?? [],
32
32
  };
33
33
  }
34
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex w-full justify-between items-end", children: [_jsxs("div", { className: "flex gap-2 items-end", children: [isGraphViz && (_jsxs(_Fragment, { children: [_jsx(GroupByDropdown, { groupBy: groupBy, labels: groupByLabels, setGroupByLabels: setGroupByLabels }), _jsx(InvertCallStack, {})] })), _jsx(FilterByFunctionButton, {}), profileViewExternalSubActions != null ? profileViewExternalSubActions : null] }), _jsxs("div", { className: "flex gap-3", children: [_jsx(MultiLevelDropdown, { groupBy: groupBy, toggleGroupBy: toggleGroupBy, profileType: profileType, onSelect: () => { }, isTableVizOnly: isTableVizOnly }), _jsx(ShareButton, { profileSource: profileSource, queryClient: queryClient, queryRequest: req, onDownloadPProf: onDownloadPProf, pprofdownloading: pprofdownloading ?? false, profileViewExternalSubActions: profileViewExternalSubActions }), showVisualizationSelector ? _jsx(ViewSelector, { profileSource: profileSource }) : null] })] }), isGraphVizOnly && (_jsxs(_Fragment, { children: [_jsx(Divider, {}), _jsx(FlameGraphToolbar, { curPath: curPath, setNewCurPath: setNewCurPath })] })), isTableVizOnly && (_jsxs(_Fragment, { children: [_jsx(Divider, {}), _jsx(TableToolbar, { profileType: profileType, total: total, filtered: filtered, clearSelection: clearSelection, currentSearchString: currentSearchString })] }))] }));
34
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex w-full justify-between items-end", children: [_jsxs("div", { className: "flex gap-2 items-end", children: [isGraphViz && (_jsxs(_Fragment, { children: [_jsx(GroupByDropdown, { groupBy: groupBy, labels: groupByLabels, setGroupByLabels: setGroupByLabels }), _jsx(InvertCallStack, {})] })), _jsx(ProfileFilters, {}), profileViewExternalSubActions != null ? profileViewExternalSubActions : null] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(MultiLevelDropdown, { groupBy: groupBy, toggleGroupBy: toggleGroupBy, profileType: profileType, onSelect: () => { }, isTableVizOnly: isTableVizOnly }), _jsx(ShareButton, { profileSource: profileSource, queryClient: queryClient, queryRequest: req, onDownloadPProf: onDownloadPProf, pprofdownloading: pprofdownloading ?? false, profileViewExternalSubActions: profileViewExternalSubActions }), showVisualizationSelector ? _jsx(ViewSelector, { profileSource: profileSource }) : null] })] }), isGraphVizOnly && (_jsxs(_Fragment, { children: [_jsx(Divider, {}), _jsx(FlameGraphToolbar, { curPath: curPath, setNewCurPath: setNewCurPath })] })), isTableVizOnly && (_jsxs(_Fragment, { children: [_jsx(Divider, {}), _jsx(TableToolbar, { profileType: profileType, total: total, filtered: filtered })] }))] }));
35
35
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useResetStateOnProfileTypeChange.d.ts","sourceRoot":"","sources":["../../../src/ProfileView/hooks/useResetStateOnProfileTypeChange.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,gCAAgC,QAAO,CAAC,MAAM,IAAI,CA8B9D,CAAC"}
1
+ {"version":3,"file":"useResetStateOnProfileTypeChange.d.ts","sourceRoot":"","sources":["../../../src/ProfileView/hooks/useResetStateOnProfileTypeChange.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,gCAAgC,QAAO,CAAC,MAAM,IAAI,CAqB9D,CAAC"}