@parca/profile 0.19.23 → 0.19.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/ProfileView/components/ProfileFilters/filterPresets.d.ts +11 -0
- package/dist/ProfileView/components/ProfileFilters/filterPresets.d.ts.map +1 -0
- package/dist/ProfileView/components/ProfileFilters/filterPresets.js +64 -0
- package/dist/ProfileView/components/ProfileFilters/index.d.ts.map +1 -1
- package/dist/ProfileView/components/ProfileFilters/index.js +54 -9
- package/dist/ProfileView/components/ProfileFilters/useProfileFilters.d.ts +2 -0
- package/dist/ProfileView/components/ProfileFilters/useProfileFilters.d.ts.map +1 -1
- package/dist/ProfileView/components/ProfileFilters/useProfileFilters.js +58 -4
- package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts.map +1 -1
- package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.js +22 -2
- package/dist/styles.css +1 -1
- package/package.json +5 -5
- package/src/ProfileView/components/ProfileFilters/filterPresets.ts +76 -0
- package/src/ProfileView/components/ProfileFilters/index.tsx +65 -12
- package/src/ProfileView/components/ProfileFilters/useProfileFilters.ts +67 -6
- package/src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.ts +27 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.19.24](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.23...@parca/profile@0.19.24) (2025-07-17)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
6
10
|
## [0.19.23](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.22...@parca/profile@0.19.23) (2025-07-16)
|
|
7
11
|
|
|
8
12
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProfileFilter } from '@parca/store';
|
|
2
|
+
export interface FilterPreset {
|
|
3
|
+
key: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
filters: Array<Omit<ProfileFilter, 'id'>>;
|
|
7
|
+
}
|
|
8
|
+
export declare const filterPresets: FilterPreset[];
|
|
9
|
+
export declare const isPresetKey: (key: string) => boolean;
|
|
10
|
+
export declare const getPresetByKey: (key: string) => FilterPreset | undefined;
|
|
11
|
+
//# sourceMappingURL=filterPresets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filterPresets.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/ProfileFilters/filterPresets.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC;CAC3C;AAED,eAAO,MAAM,aAAa,EAAE,YAAY,EA6CvC,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,OAEzC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,YAAY,GAAG,SAE3D,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
export const filterPresets = [
|
|
14
|
+
{
|
|
15
|
+
key: 'go_runtime_expected_off_cpu',
|
|
16
|
+
name: 'Go Runtime Expected Off-CPU',
|
|
17
|
+
description: 'Excludes expected Go runtime blocking functions',
|
|
18
|
+
filters: [
|
|
19
|
+
{
|
|
20
|
+
type: 'stack',
|
|
21
|
+
field: 'function_name',
|
|
22
|
+
matchType: 'not_equal',
|
|
23
|
+
value: 'runtime.usleep',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'stack',
|
|
27
|
+
field: 'function_name',
|
|
28
|
+
matchType: 'not_equal',
|
|
29
|
+
value: 'runtime.futex',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: 'rust_runtime_expected_off_cpu',
|
|
35
|
+
name: 'Rust Expected Off-CPU',
|
|
36
|
+
description: 'Excludes expected Rust runtime blocking functions',
|
|
37
|
+
filters: [
|
|
38
|
+
{
|
|
39
|
+
type: 'stack',
|
|
40
|
+
field: 'function_name',
|
|
41
|
+
matchType: 'not_equal',
|
|
42
|
+
value: 'parking_lot_core::thread_parker::imp::ThreadParker::futex_wait',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: 'stack',
|
|
46
|
+
field: 'function_name',
|
|
47
|
+
matchType: 'not_equal',
|
|
48
|
+
value: 'tokio::runtime::time::Driver::park_internal',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: 'stack',
|
|
52
|
+
field: 'function_name',
|
|
53
|
+
matchType: 'not_equal',
|
|
54
|
+
value: 'futex_wait',
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
export const isPresetKey = (key) => {
|
|
60
|
+
return filterPresets.some(preset => preset.key === key);
|
|
61
|
+
};
|
|
62
|
+
export const getPresetByKey = (key) => {
|
|
63
|
+
return filterPresets.find(preset => preset.key === key);
|
|
64
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/ProfileFilters/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/ProfileFilters/index.tsx"],"names":[],"mappings":"AAqBA,OAAO,EAAoB,KAAK,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAE1E,eAAO,MAAM,gBAAgB,GAAI,QAAQ,aAAa,KAAG,OASxD,CAAC;AAyIF,QAAA,MAAM,cAAc,QAAO,GAAG,CAAC,OA+K9B,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -15,8 +15,14 @@ import { useCallback } from 'react';
|
|
|
15
15
|
import { Icon } from '@iconify/react';
|
|
16
16
|
import cx from 'classnames';
|
|
17
17
|
import { Button, Input, Select } from '@parca/components';
|
|
18
|
+
import { filterPresets, getPresetByKey, isPresetKey } from './filterPresets';
|
|
18
19
|
import { useProfileFilters } from './useProfileFilters';
|
|
19
20
|
export const isFilterComplete = (filter) => {
|
|
21
|
+
// For preset filters, only need type and value
|
|
22
|
+
if (filter.type != null && isPresetKey(filter.type)) {
|
|
23
|
+
return filter.value !== '' && filter.type != null;
|
|
24
|
+
}
|
|
25
|
+
// For regular filters, need all fields
|
|
20
26
|
return (filter.value !== '' && filter.type != null && filter.field != null && filter.matchType != null);
|
|
21
27
|
};
|
|
22
28
|
const filterTypeItems = [
|
|
@@ -34,6 +40,13 @@ const filterTypeItems = [
|
|
|
34
40
|
expanded: (_jsxs(_Fragment, { children: [_jsx("span", { children: "Frame Filter" }), _jsx("br", {}), _jsx("span", { className: "text-xs", children: "Filters individual frames" })] })),
|
|
35
41
|
},
|
|
36
42
|
},
|
|
43
|
+
...filterPresets.map(preset => ({
|
|
44
|
+
key: preset.key,
|
|
45
|
+
element: {
|
|
46
|
+
active: _jsx(_Fragment, { children: preset.name }),
|
|
47
|
+
expanded: (_jsxs(_Fragment, { children: [_jsx("span", { children: preset.name }), _jsx("br", {}), _jsx("span", { className: "text-xs", children: preset.description })] })),
|
|
48
|
+
},
|
|
49
|
+
})),
|
|
37
50
|
];
|
|
38
51
|
const fieldItems = [
|
|
39
52
|
{
|
|
@@ -140,14 +153,40 @@ const ProfileFilters = () => {
|
|
|
140
153
|
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
154
|
const isNumberField = filter.field === 'address' || filter.field === 'line_number';
|
|
142
155
|
const matchTypeItems = isNumberField ? numberMatchTypeItems : stringMatchTypeItems;
|
|
156
|
+
const isPresetFilter = filter.type != null && isPresetKey(filter.type);
|
|
143
157
|
return (_jsxs("div", { className: "flex items-center gap-0", children: [_jsx(Select, { items: filterTypeItems, selectedKey: filter.type, placeholder: "Select Filter", onSelection: key => {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
// Check if this is a preset selection
|
|
159
|
+
if (isPresetKey(key)) {
|
|
160
|
+
const preset = getPresetByKey(key);
|
|
161
|
+
if (preset != null) {
|
|
162
|
+
updateFilter(filter.id, {
|
|
163
|
+
type: preset.key,
|
|
164
|
+
field: undefined,
|
|
165
|
+
matchType: undefined,
|
|
166
|
+
value: preset.name,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
const newType = key;
|
|
172
|
+
// Check if we're converting a preset filter to a regular filter
|
|
173
|
+
if (filter.type != null && isPresetKey(filter.type)) {
|
|
174
|
+
updateFilter(filter.id, {
|
|
175
|
+
type: newType,
|
|
176
|
+
field: 'function_name',
|
|
177
|
+
matchType: 'contains',
|
|
178
|
+
value: '',
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
updateFilter(filter.id, {
|
|
183
|
+
type: newType,
|
|
184
|
+
field: filter.field ?? 'function_name',
|
|
185
|
+
matchType: filter.matchType ?? 'contains',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}, className: cx('rounded-l-md pr-1 gap-0 focus:z-50 focus:relative focus:outline-1', isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none', filter.type != null ? 'border-r-0 w-auto' : 'w-32') }), filter.type != null && !isPresetFilter && (_jsxs(_Fragment, { children: [_jsx(Select, { items: fieldItems, selectedKey: filter.field ?? '', onSelection: key => {
|
|
151
190
|
const newField = key;
|
|
152
191
|
const isNewFieldNumber = newField === 'address' || newField === 'line_number';
|
|
153
192
|
const isCurrentFieldNumber = filter.field === 'address' || filter.field === 'line_number';
|
|
@@ -161,13 +200,19 @@ const ProfileFilters = () => {
|
|
|
161
200
|
updateFilter(filter.id, { field: newField });
|
|
162
201
|
}
|
|
163
202
|
}, 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
|
-
|
|
203
|
+
// If we're displaying local filters and this is the last one, reset everything
|
|
204
|
+
if (localFilters.length > 0 && localFilters.length === 1) {
|
|
205
|
+
resetFilters();
|
|
206
|
+
}
|
|
207
|
+
// If we're displaying applied filters and this is the last one, reset everything
|
|
208
|
+
else if (localFilters.length === 0 && filtersToRender.length === 1) {
|
|
165
209
|
resetFilters();
|
|
166
210
|
}
|
|
211
|
+
// Otherwise, just remove this specific filter
|
|
167
212
|
else {
|
|
168
213
|
removeFilter(filter.id);
|
|
169
214
|
}
|
|
170
215
|
}, 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 &&
|
|
216
|
+
}), 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 && (_jsx(Button, { variant: "primary", onClick: onApplyFilters, disabled: !hasUnsavedChanges || !localFilters.some(isFilterComplete), className: cx('flex items-center gap-2 self-end'), children: _jsx("span", { children: "Apply" }) }))] }));
|
|
172
217
|
};
|
|
173
218
|
export default ProfileFilters;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Filter } from '@parca/client';
|
|
2
2
|
import { type ProfileFilter } from '@parca/store';
|
|
3
|
+
import { type FilterPreset } from './filterPresets';
|
|
3
4
|
export type { ProfileFilter };
|
|
4
5
|
export declare const convertToProtoFilters: (profileFilters: ProfileFilter[]) => Filter[];
|
|
5
6
|
export declare const useProfileFilters: () => {
|
|
@@ -14,5 +15,6 @@ export declare const useProfileFilters: () => {
|
|
|
14
15
|
removeFilter: (id: string) => void;
|
|
15
16
|
updateFilter: (id: string, updates: Partial<ProfileFilter>) => void;
|
|
16
17
|
resetFilters: () => void;
|
|
18
|
+
applyPreset: (preset: FilterPreset) => void;
|
|
17
19
|
};
|
|
18
20
|
//# sourceMappingURL=useProfileFilters.d.ts.map
|
|
@@ -1 +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;
|
|
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;AAEtB,OAAO,EAA8B,KAAK,YAAY,EAAC,MAAM,iBAAiB,CAAC;AAG/E,YAAY,EAAC,aAAa,EAAC,CAAC;AAG5B,eAAO,MAAM,qBAAqB,GAAI,gBAAgB,aAAa,EAAE,KAAG,MAAM,EAoG7E,CAAC;AAEF,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;IACzB,WAAW,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;CAwM7C,CAAC"}
|
|
@@ -12,10 +12,31 @@
|
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
import { useCallback, useEffect, useMemo } from 'react';
|
|
14
14
|
import { selectLocalFilters, setLocalFilters, useAppDispatch, useAppSelector, } from '@parca/store';
|
|
15
|
+
import { getPresetByKey, isPresetKey } from './filterPresets';
|
|
15
16
|
import { useProfileFiltersUrlState } from './useProfileFiltersUrlState';
|
|
16
17
|
// Convert ProfileFilter[] to protobuf Filter[] matching the expected structure
|
|
17
18
|
export const convertToProtoFilters = (profileFilters) => {
|
|
18
|
-
|
|
19
|
+
// First, expand any preset filters to their constituent filters
|
|
20
|
+
const expandedFilters = [];
|
|
21
|
+
for (const filter of profileFilters) {
|
|
22
|
+
if (filter.type != null && isPresetKey(filter.type)) {
|
|
23
|
+
// This is a preset filter, expand it
|
|
24
|
+
const preset = getPresetByKey(filter.type);
|
|
25
|
+
if (preset != null) {
|
|
26
|
+
preset.filters.forEach((presetFilter, index) => {
|
|
27
|
+
expandedFilters.push({
|
|
28
|
+
...presetFilter,
|
|
29
|
+
id: `${filter.id}-expanded-${index}`,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Regular filter, add as is
|
|
36
|
+
expandedFilters.push(filter);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return expandedFilters
|
|
19
40
|
.filter(f => f.value !== '' && f.type != null && f.field != null && f.matchType != null) // Only include complete filters with values
|
|
20
41
|
.map(f => {
|
|
21
42
|
// Build the condition based on field type
|
|
@@ -117,8 +138,22 @@ export const useProfileFilters = () => {
|
|
|
117
138
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
118
139
|
}, []);
|
|
119
140
|
const hasUnsavedChanges = useMemo(() => {
|
|
120
|
-
const localWithValues = localFilters.filter(f =>
|
|
121
|
-
|
|
141
|
+
const localWithValues = localFilters.filter(f => {
|
|
142
|
+
// For preset filters, only need type and value
|
|
143
|
+
if (f.type != null && isPresetKey(f.type)) {
|
|
144
|
+
return f.value !== '' && f.type != null;
|
|
145
|
+
}
|
|
146
|
+
// For regular filters, need all fields
|
|
147
|
+
return f.value !== '' && f.type != null && f.field != null && f.matchType != null;
|
|
148
|
+
});
|
|
149
|
+
const appliedWithValues = (appliedFilters ?? []).filter(f => {
|
|
150
|
+
// For preset filters, only need type and value
|
|
151
|
+
if (f.type != null && isPresetKey(f.type)) {
|
|
152
|
+
return f.value !== '' && f.type != null;
|
|
153
|
+
}
|
|
154
|
+
// For regular filters, need all fields
|
|
155
|
+
return f.value !== '' && f.type != null && f.field != null && f.matchType != null;
|
|
156
|
+
});
|
|
122
157
|
if (localWithValues.length !== appliedWithValues.length)
|
|
123
158
|
return true;
|
|
124
159
|
return !localWithValues.every((local, index) => {
|
|
@@ -183,7 +218,14 @@ export const useProfileFilters = () => {
|
|
|
183
218
|
setAppliedFilters([]);
|
|
184
219
|
}, [dispatch, setAppliedFilters]);
|
|
185
220
|
const onApplyFilters = useCallback(() => {
|
|
186
|
-
const validFilters = localFilters.filter(f =>
|
|
221
|
+
const validFilters = localFilters.filter(f => {
|
|
222
|
+
// For preset filters, only need type and value
|
|
223
|
+
if (f.type != null && isPresetKey(f.type)) {
|
|
224
|
+
return f.value !== '' && f.type != null;
|
|
225
|
+
}
|
|
226
|
+
// For regular filters, need all fields
|
|
227
|
+
return f.value !== '' && f.type != null && f.field != null && f.matchType != null;
|
|
228
|
+
});
|
|
187
229
|
const filtersToApply = validFilters.map((f, index) => ({
|
|
188
230
|
...f,
|
|
189
231
|
id: `filter-${Date.now()}-${index}`,
|
|
@@ -193,6 +235,17 @@ export const useProfileFilters = () => {
|
|
|
193
235
|
const protoFilters = useMemo(() => {
|
|
194
236
|
return convertToProtoFilters(appliedFilters ?? []);
|
|
195
237
|
}, [appliedFilters]);
|
|
238
|
+
const applyPreset = useCallback((preset) => {
|
|
239
|
+
const presetFilter = {
|
|
240
|
+
id: `filter-preset-${Date.now()}`,
|
|
241
|
+
type: preset.key,
|
|
242
|
+
value: preset.name,
|
|
243
|
+
};
|
|
244
|
+
// Add preset filter to existing filters
|
|
245
|
+
const updatedFilters = [...localFilters, presetFilter];
|
|
246
|
+
dispatch(setLocalFilters(updatedFilters));
|
|
247
|
+
setAppliedFilters(updatedFilters);
|
|
248
|
+
}, [dispatch, setAppliedFilters, localFilters]);
|
|
196
249
|
return {
|
|
197
250
|
localFilters,
|
|
198
251
|
appliedFilters,
|
|
@@ -205,5 +258,6 @@ export const useProfileFilters = () => {
|
|
|
205
258
|
removeFilter,
|
|
206
259
|
updateFilter,
|
|
207
260
|
resetFilters,
|
|
261
|
+
applyPreset,
|
|
208
262
|
};
|
|
209
263
|
};
|
|
@@ -1 +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;
|
|
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;AAwDhD,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,aAAa,EAkCnE,CAAC;AAEF,eAAO,MAAM,yBAAyB,QAAO;IAC3C,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,iBAAiB,EAAE,sBAAsB,CAAC,aAAa,EAAE,CAAC,CAAC;CAmB5D,CAAC"}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
import { useURLStateCustom } from '@parca/components';
|
|
14
|
+
import { isPresetKey } from './filterPresets';
|
|
14
15
|
// Compact encoding mappings
|
|
15
16
|
const TYPE_MAP = {
|
|
16
17
|
stack: 's',
|
|
@@ -39,8 +40,15 @@ const encodeProfileFilters = (filters) => {
|
|
|
39
40
|
if (filters.length === 0)
|
|
40
41
|
return '';
|
|
41
42
|
return filters
|
|
42
|
-
.filter(f => f.value !== '' && f.type != null
|
|
43
|
+
.filter(f => f.value !== '' && f.type != null)
|
|
43
44
|
.map(f => {
|
|
45
|
+
// Handle preset filters differently
|
|
46
|
+
if (isPresetKey(f.type)) {
|
|
47
|
+
const presetKey = encodeURIComponent(f.type);
|
|
48
|
+
const value = encodeURIComponent(f.value);
|
|
49
|
+
return `p:${presetKey}:${value}`;
|
|
50
|
+
}
|
|
51
|
+
// Handle regular filters
|
|
44
52
|
const type = TYPE_MAP[f.type];
|
|
45
53
|
const field = FIELD_MAP[f.field];
|
|
46
54
|
const match = MATCH_MAP[f.matchType];
|
|
@@ -55,7 +63,19 @@ export const decodeProfileFilters = (encoded) => {
|
|
|
55
63
|
return [];
|
|
56
64
|
try {
|
|
57
65
|
return encoded.split(',').map((filter, index) => {
|
|
58
|
-
const
|
|
66
|
+
const parts = filter.split(':');
|
|
67
|
+
// Handle preset filters (format: p:presetKey:value)
|
|
68
|
+
if (parts[0] === 'p' && parts.length >= 3) {
|
|
69
|
+
const presetKey = decodeURIComponent(parts[1]);
|
|
70
|
+
const value = decodeURIComponent(parts.slice(2).join(':')); // Handle values with colons
|
|
71
|
+
return {
|
|
72
|
+
id: `filter-${Date.now()}-${index}`,
|
|
73
|
+
type: presetKey,
|
|
74
|
+
value,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Handle regular filters (format: type:field:match:value)
|
|
78
|
+
const [type, field, match, ...valueParts] = parts;
|
|
59
79
|
const value = decodeURIComponent(valueParts.join(':')); // Handle values with colons
|
|
60
80
|
return {
|
|
61
81
|
id: `filter-${Date.now()}-${index}`,
|
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
/*! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-inset-2{bottom:-.5rem;left:-.5rem;right:-.5rem;top:-.5rem}.inset-y-0{bottom:0;top:0}.right-0{right:0}.top-0{top:0}.left-0{left:0}.bottom-3{bottom:.75rem}.top-\[-5px\]{top:-5px}.top-\[-2px\]{top:-2px}.bottom-0{bottom:0}.left-\[-7px\]{left:-7px}.right-\[-7px\]{right:-7px}.right-8{right:2rem}.top-\[-46px\]{top:-46px}.right-full{right:100%}.left-full{left:100%}.top-\[-60px\]{top:-60px}.left-1\/2{left:50%}.top-full{top:100%}.z-10{z-index:10}.z-50{z-index:50}.z-30{z-index:30}.z-\[5\]{z-index:5}.z-20{z-index:20}.m-auto{margin:auto}.m-2{margin:.5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-0{margin-bottom:0;margin-top:0}.my-20{margin-bottom:5rem;margin-top:5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-4{margin-bottom:1rem;margin-top:1rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.ml-\[70px\]{margin-left:70px}.mb-2{margin-bottom:.5rem}.ml-auto{margin-left:auto}.mt-auto{margin-top:auto}.mb-0\.5{margin-bottom:.125rem}.mt-1\.5{margin-top:.375rem}.mb-0{margin-bottom:0}.mr-2{margin-right:.5rem}.ml-3{margin-left:.75rem}.mt-2{margin-top:.5rem}.mb-px{margin-bottom:1px}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.mb-1{margin-bottom:.25rem}.mr-1{margin-right:.25rem}.mt-4{margin-top:1rem}.mb-4{margin-bottom:1rem}.mt-3{margin-top:.75rem}.mt-8{margin-top:2rem}.mt-0{margin-top:0}.ml-1{margin-left:.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.\!flex{display:flex!important}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-fit{height:-moz-fit-content;height:fit-content}.h-\[38px\]{height:38px}.h-auto{height:auto}.h-full{height:100%}.h-1{height:.25rem}.h-\[20px\]{height:20px}.h-\[16px\]{height:16px}.h-4{height:1rem}.h-\[30px\]{height:30px}.h-\[45px\]{height:45px}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[700px\]{height:700px}.h-\[80vh\]{height:80vh}.h-\[1px\]{height:1px}.h-\[14px\]{height:14px}.h-\[24px\]{height:24px}.h-0{height:0}.max-h-\[400px\]{max-height:400px}.max-h-\[50vh\]{max-height:50vh}.max-h-\[300px\]{max-height:300px}.min-h-52{min-height:13rem}.min-h-5{min-height:1.25rem}.min-h-48{min-height:12rem}.min-h-\[50px\]{min-height:50px}.min-h-\[700px\]{min-height:700px}.min-h-96{min-height:24rem}.w-full{width:100%}.w-\[500px\]{width:500px}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-max{width:-moz-max-content;width:max-content}.w-40{width:10rem}.w-\[44px\]{width:44px}.w-\[16px\]{width:16px}.w-auto{width:auto}.w-\[270px\]{width:270px}.w-\[300px\]{width:300px}.w-5{width:1.25rem}.w-3{width:.75rem}.w-7{width:1.75rem}.w-9{width:2.25rem}.w-11{width:2.75rem}.w-\[52px\]{width:52px}.w-\[68px\]{width:68px}.w-\[76px\]{width:76px}.w-\[84px\]{width:84px}.w-\[92px\]{width:92px}.w-\[100px\]{width:100px}.w-\[108px\]{width:108px}.w-\[116px\]{width:116px}.w-4{width:1rem}.w-56{width:14rem}.w-\[350px\]{width:350px}.w-\[260px\]{width:260px}.w-44{width:11rem}.w-\[460px\]{width:460px}.w-\[19\.25\%\]{width:19.25%}.w-11\/12{width:91.666667%}.w-1\/12{width:8.333333%}.w-8{width:2rem}.w-\[50\%\]{width:50%}.w-28{width:7rem}.w-32{width:8rem}.w-36{width:9rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.w-48{width:12rem}.w-\[14px\]{width:14px}.w-64{width:16rem}.w-80{width:20rem}.w-\[20px\]{width:20px}.w-\[246px\]{width:246px}.w-52{width:13rem}.w-0{width:0}.min-w-\[300px\]{min-width:300px}.min-w-\[400px\]{min-width:400px}.min-w-\[350px\]{min-width:350px}.min-w-\[260px\]{min-width:260px}.max-w-\[500px\]{max-width:500px}.max-w-md{max-width:28rem}.max-w-80{max-width:20rem}.max-w-full{max-width:100%}.max-w-48{max-width:12rem}.max-w-\[300px\]{max-width:300px}.max-w-\[400px\]{max-width:400px}.max-w-lg{max-width:32rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.flex-grow-0{flex-grow:0}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.origin-top-right{transform-origin:top right}.origin-top-left{transform-origin:top left}.translate-x-6{--tw-translate-x:1.5rem}.translate-x-0,.translate-x-6{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x:0px}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-0,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.translate-x-5{--tw-translate-x:1.25rem}.-translate-x-1\/2,.translate-x-5{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-1\/2{--tw-translate-x:-50%}.rotate-90{--tw-rotate:90deg}.-rotate-180,.rotate-90{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-180{--tw-rotate:-180deg}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-ew-resize{cursor:ew-resize}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-4{gap:1rem}.gap-1{gap:.25rem}.gap-\[2px\]{gap:2px}.gap-2{gap:.5rem}.gap-6{gap:1.5rem}.gap-3{gap:.75rem}.gap-0{gap:0}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.25rem*var(--tw-space-y-reverse));margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)))}.self-end{align-self:flex-end}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-clip{overflow:clip}.overflow-scroll{overflow:scroll}.overflow-x-hidden{overflow-x:hidden}.text-ellipsis{text-overflow:ellipsis}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-full{border-radius:9999px}.rounded-none{border-radius:0}.rounded-\[4px\]{border-radius:4px}.rounded-l-md{border-bottom-left-radius:.375rem;border-top-left-radius:.375rem}.rounded-l-none{border-bottom-left-radius:0;border-top-left-radius:0}.rounded-r-none{border-bottom-right-radius:0;border-top-right-radius:0}.rounded-r-md{border-bottom-right-radius:.375rem;border-top-right-radius:.375rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.rounded-b{border-bottom-right-radius:.25rem}.rounded-b,.rounded-l{border-bottom-left-radius:.25rem}.rounded-l{border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.rounded-tr-none{border-top-right-radius:0}.rounded-br-none{border-bottom-right-radius:0}.rounded-tl-none{border-top-left-radius:0}.rounded-bl-none{border-bottom-left-radius:0}.border{border-width:1px}.border-2{border-width:2px}.border-x-2{border-left-width:2px;border-right-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.border-l{border-left-width:1px}.border-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-l-4{border-left-width:4px}.border-r-4{border-right-width:4px}.border-t-4{border-top-width:4px}.border-none{border-style:none}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.border-gray-900{--tw-border-opacity:1;border-color:rgb(17 24 39/var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.border-r-gray-200{--tw-border-opacity:1;border-right-color:rgb(229 231 235/var(--tw-border-opacity))}.border-l-amber-900{--tw-border-opacity:1;border-left-color:rgb(120 53 15/var(--tw-border-opacity))}.border-l-transparent{border-left-color:transparent}.border-r-transparent{border-right-color:transparent}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/50{background-color:hsla(0,0%,100%,.5)}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity))}.bg-yellow-200{--tw-bg-opacity:1;background-color:rgb(254 240 138/var(--tw-bg-opacity))}.bg-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}.bg-gray-500\/50{background-color:hsla(220,9%,46%,.5)}.bg-gray-700\/75{background-color:rgba(55,65,81,.75)}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.bg-indigo-50{--tw-bg-opacity:1;background-color:rgb(238 242 255/var(--tw-bg-opacity))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.bg-inherit{background-color:inherit}.bg-indigo-400{--tw-bg-opacity:1;background-color:rgb(129 140 248/var(--tw-bg-opacity))}.bg-opacity-75{--tw-bg-opacity:0.75}.bg-opacity-90{--tw-bg-opacity:0.9}.fill-transparent{fill:transparent}.fill-current{fill:currentColor}.stroke-gray-300{stroke:#d1d5db}.stroke-white{stroke:#fff}.stroke-\[3\]{stroke-width:3}.p-3{padding:.75rem}.p-2{padding:.5rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-1{padding:.25rem}.p-0{padding:0}.p-\[6px\]{padding:6px}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-4{padding-left:1rem;padding-right:1rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-bottom:0;padding-top:0}.\!px-3{padding-left:.75rem!important;padding-right:.75rem!important}.pr-0{padding-right:0}.pt-2{padding-top:.5rem}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pb-4{padding-bottom:1rem}.pt-0{padding-top:0}.pl-2{padding-left:.5rem}.pr-4{padding-right:1rem}.pl-1{padding-left:.25rem}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pr-3{padding-right:.75rem}.pb-2{padding-bottom:.5rem}.pb-\[10px\]{padding-bottom:10px}.pr-1{padding-right:.25rem}.pr-\[1\.7rem\]{padding-right:1.7rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem}.text-lg,.text-xl{line-height:1.75rem}.text-xl{font-size:1.25rem}.text-\[10px\]{font-size:10px}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.leading-6{line-height:1.5rem}.leading-5{line-height:1.25rem}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-100{opacity:1}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-90{opacity:.9}.opacity-80{opacity:.8}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-\[0_0_10px_2px_rgba\(0\2c 0\2c 0\2c 0\.3\)\],.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_10px_2px_rgba\(0\2c 0\2c 0\2c 0\.3\)\]{--tw-shadow:0 0 10px 2px rgba(0,0,0,.3);--tw-shadow-colored:0 0 10px 2px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-0,.ring-1{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-150{transition-duration:.15s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[stroke-dasharray\:6\2c 4\]{stroke-dasharray:6,4}.\[stroke-linecap\:round\]{stroke-linecap:round}.\[stroke-linejoin\:round\]{stroke-linejoin:round}.\[writing-mode\:vertical-lr\]{writing-mode:vertical-lr}.checked\:border-indigo-600:checked{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity))}.checked\:bg-indigo-600:checked{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:whitespace-normal:hover{white-space:normal}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-indigo-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:relative:focus{position:relative}.focus\:z-50:focus{z-index:50}.focus\:border-indigo-500:focus{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:outline-1:focus{outline-width:1px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.focus\:ring-indigo-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(79 70 229/var(--tw-ring-opacity))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-white\/75:focus-visible{--tw-ring-color:hsla(0,0%,100%,.75)}.focus-visible\:ring-white:focus-visible{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}.focus-visible\:ring-indigo-500:focus-visible{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.focus-visible\:ring-opacity-75:focus-visible{--tw-ring-opacity:0.75}.group:hover .group-hover\:flex{display:flex}[class~=theme-dark] .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-r-gray-700{--tw-border-opacity:1;border-right-color:rgb(55 65 81/var(--tw-border-opacity))}[class~=theme-dark] .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-black\/50{background-color:rgba(0,0,0,.5)}[class~=theme-dark] .dark\:bg-indigo-700{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-100\/90{background-color:rgba(243,244,246,.9)}[class~=theme-dark] .dark\:bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-200\/75{background-color:rgba(229,231,235,.75)}[class~=theme-dark] .dark\:bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-opacity-80{--tw-bg-opacity:0.8}[class~=theme-dark] .dark\:stroke-gray-500{stroke:#6b7280}[class~=theme-dark] .dark\:stroke-gray-700{stroke:#374151}[class~=theme-dark] .dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-indigo-500{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity))}[class~=theme-dark] .dark\:\!text-indigo-400{--tw-text-opacity:1!important;color:rgb(129 140 248/var(--tw-text-opacity))!important}[class~=theme-dark] .dark\:ring-white{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}[class~=theme-dark] .dark\:ring-opacity-20{--tw-ring-opacity:0.2}[class~=theme-dark] .hover\:dark\:text-gray-100:hover{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:block{display:block}.md\:flex-row{flex-direction:row}}
|
|
1
|
+
/*! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-inset-2{bottom:-.5rem;left:-.5rem;right:-.5rem;top:-.5rem}.inset-y-0{bottom:0;top:0}.right-0{right:0}.top-0{top:0}.left-0{left:0}.bottom-3{bottom:.75rem}.top-\[-5px\]{top:-5px}.top-\[-2px\]{top:-2px}.bottom-0{bottom:0}.left-\[-7px\]{left:-7px}.right-\[-7px\]{right:-7px}.right-8{right:2rem}.top-\[-46px\]{top:-46px}.right-full{right:100%}.left-full{left:100%}.top-\[-60px\]{top:-60px}.left-1\/2{left:50%}.top-full{top:100%}.z-10{z-index:10}.z-50{z-index:50}.z-30{z-index:30}.z-\[5\]{z-index:5}.z-20{z-index:20}.m-auto{margin:auto}.m-2{margin:.5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-0{margin-bottom:0;margin-top:0}.my-20{margin-bottom:5rem;margin-top:5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-4{margin-bottom:1rem;margin-top:1rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.ml-\[70px\]{margin-left:70px}.mb-2{margin-bottom:.5rem}.ml-auto{margin-left:auto}.mt-auto{margin-top:auto}.mb-0\.5{margin-bottom:.125rem}.mt-1\.5{margin-top:.375rem}.mb-0{margin-bottom:0}.mr-2{margin-right:.5rem}.ml-3{margin-left:.75rem}.mt-2{margin-top:.5rem}.mb-px{margin-bottom:1px}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.mb-1{margin-bottom:.25rem}.mr-1{margin-right:.25rem}.mt-4{margin-top:1rem}.mb-4{margin-bottom:1rem}.mt-3{margin-top:.75rem}.mt-8{margin-top:2rem}.mt-0{margin-top:0}.ml-1{margin-left:.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.\!flex{display:flex!important}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-fit{height:-moz-fit-content;height:fit-content}.h-\[38px\]{height:38px}.h-auto{height:auto}.h-full{height:100%}.h-1{height:.25rem}.h-\[20px\]{height:20px}.h-\[16px\]{height:16px}.h-4{height:1rem}.h-\[30px\]{height:30px}.h-\[45px\]{height:45px}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[700px\]{height:700px}.h-\[80vh\]{height:80vh}.h-\[1px\]{height:1px}.h-\[14px\]{height:14px}.h-\[24px\]{height:24px}.h-0{height:0}.max-h-\[400px\]{max-height:400px}.max-h-\[50vh\]{max-height:50vh}.max-h-\[300px\]{max-height:300px}.min-h-52{min-height:13rem}.min-h-5{min-height:1.25rem}.min-h-48{min-height:12rem}.min-h-\[50px\]{min-height:50px}.min-h-\[700px\]{min-height:700px}.min-h-96{min-height:24rem}.w-full{width:100%}.w-\[500px\]{width:500px}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-max{width:-moz-max-content;width:max-content}.w-40{width:10rem}.w-\[44px\]{width:44px}.w-\[16px\]{width:16px}.w-auto{width:auto}.w-\[270px\]{width:270px}.w-\[300px\]{width:300px}.w-5{width:1.25rem}.w-3{width:.75rem}.w-7{width:1.75rem}.w-9{width:2.25rem}.w-11{width:2.75rem}.w-\[52px\]{width:52px}.w-\[68px\]{width:68px}.w-\[76px\]{width:76px}.w-\[84px\]{width:84px}.w-\[92px\]{width:92px}.w-\[100px\]{width:100px}.w-\[108px\]{width:108px}.w-\[116px\]{width:116px}.w-4{width:1rem}.w-56{width:14rem}.w-\[350px\]{width:350px}.w-\[260px\]{width:260px}.w-44{width:11rem}.w-\[460px\]{width:460px}.w-\[19\.25\%\]{width:19.25%}.w-11\/12{width:91.666667%}.w-1\/12{width:8.333333%}.w-8{width:2rem}.w-\[50\%\]{width:50%}.w-32{width:8rem}.w-36{width:9rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.w-48{width:12rem}.w-\[14px\]{width:14px}.w-64{width:16rem}.w-80{width:20rem}.w-\[20px\]{width:20px}.w-\[246px\]{width:246px}.w-52{width:13rem}.w-0{width:0}.min-w-\[300px\]{min-width:300px}.min-w-\[400px\]{min-width:400px}.min-w-\[350px\]{min-width:350px}.min-w-\[260px\]{min-width:260px}.max-w-\[500px\]{max-width:500px}.max-w-md{max-width:28rem}.max-w-80{max-width:20rem}.max-w-full{max-width:100%}.max-w-48{max-width:12rem}.max-w-\[300px\]{max-width:300px}.max-w-\[400px\]{max-width:400px}.max-w-lg{max-width:32rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.flex-grow-0{flex-grow:0}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.origin-top-right{transform-origin:top right}.origin-top-left{transform-origin:top left}.translate-x-6{--tw-translate-x:1.5rem}.translate-x-0,.translate-x-6{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x:0px}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-0,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.translate-x-5{--tw-translate-x:1.25rem}.-translate-x-1\/2,.translate-x-5{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-1\/2{--tw-translate-x:-50%}.rotate-90{--tw-rotate:90deg}.-rotate-180,.rotate-90{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-180{--tw-rotate:-180deg}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-ew-resize{cursor:ew-resize}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-4{gap:1rem}.gap-1{gap:.25rem}.gap-\[2px\]{gap:2px}.gap-2{gap:.5rem}.gap-6{gap:1.5rem}.gap-3{gap:.75rem}.gap-0{gap:0}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.25rem*var(--tw-space-y-reverse));margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)))}.self-end{align-self:flex-end}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-clip{overflow:clip}.overflow-scroll{overflow:scroll}.overflow-x-hidden{overflow-x:hidden}.text-ellipsis{text-overflow:ellipsis}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-full{border-radius:9999px}.rounded-none{border-radius:0}.rounded-\[4px\]{border-radius:4px}.rounded-l-md{border-bottom-left-radius:.375rem;border-top-left-radius:.375rem}.rounded-l-none{border-bottom-left-radius:0;border-top-left-radius:0}.rounded-r-none{border-bottom-right-radius:0;border-top-right-radius:0}.rounded-r-md{border-bottom-right-radius:.375rem;border-top-right-radius:.375rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.rounded-b{border-bottom-right-radius:.25rem}.rounded-b,.rounded-l{border-bottom-left-radius:.25rem}.rounded-l{border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.rounded-tr-none{border-top-right-radius:0}.rounded-br-none{border-bottom-right-radius:0}.rounded-tl-none{border-top-left-radius:0}.rounded-bl-none{border-bottom-left-radius:0}.border{border-width:1px}.border-2{border-width:2px}.border-x-2{border-left-width:2px;border-right-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.border-l{border-left-width:1px}.border-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-l-4{border-left-width:4px}.border-r-4{border-right-width:4px}.border-t-4{border-top-width:4px}.border-none{border-style:none}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.border-gray-900{--tw-border-opacity:1;border-color:rgb(17 24 39/var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.border-r-gray-200{--tw-border-opacity:1;border-right-color:rgb(229 231 235/var(--tw-border-opacity))}.border-l-amber-900{--tw-border-opacity:1;border-left-color:rgb(120 53 15/var(--tw-border-opacity))}.border-l-transparent{border-left-color:transparent}.border-r-transparent{border-right-color:transparent}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/50{background-color:hsla(0,0%,100%,.5)}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity))}.bg-yellow-200{--tw-bg-opacity:1;background-color:rgb(254 240 138/var(--tw-bg-opacity))}.bg-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}.bg-gray-500\/50{background-color:hsla(220,9%,46%,.5)}.bg-gray-700\/75{background-color:rgba(55,65,81,.75)}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.bg-indigo-50{--tw-bg-opacity:1;background-color:rgb(238 242 255/var(--tw-bg-opacity))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.bg-inherit{background-color:inherit}.bg-indigo-400{--tw-bg-opacity:1;background-color:rgb(129 140 248/var(--tw-bg-opacity))}.bg-opacity-75{--tw-bg-opacity:0.75}.bg-opacity-90{--tw-bg-opacity:0.9}.fill-transparent{fill:transparent}.fill-current{fill:currentColor}.stroke-gray-300{stroke:#d1d5db}.stroke-white{stroke:#fff}.stroke-\[3\]{stroke-width:3}.p-3{padding:.75rem}.p-2{padding:.5rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-1{padding:.25rem}.p-0{padding:0}.p-\[6px\]{padding:6px}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-4{padding-left:1rem;padding-right:1rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-bottom:0;padding-top:0}.\!px-3{padding-left:.75rem!important;padding-right:.75rem!important}.pr-0{padding-right:0}.pt-2{padding-top:.5rem}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pb-4{padding-bottom:1rem}.pt-0{padding-top:0}.pl-2{padding-left:.5rem}.pr-4{padding-right:1rem}.pl-1{padding-left:.25rem}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pr-3{padding-right:.75rem}.pb-2{padding-bottom:.5rem}.pb-\[10px\]{padding-bottom:10px}.pr-1{padding-right:.25rem}.pr-\[1\.7rem\]{padding-right:1.7rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem}.text-lg,.text-xl{line-height:1.75rem}.text-xl{font-size:1.25rem}.text-\[10px\]{font-size:10px}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.leading-6{line-height:1.5rem}.leading-5{line-height:1.25rem}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-100{opacity:1}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-90{opacity:.9}.opacity-80{opacity:.8}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-\[0_0_10px_2px_rgba\(0\2c 0\2c 0\2c 0\.3\)\],.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_10px_2px_rgba\(0\2c 0\2c 0\2c 0\.3\)\]{--tw-shadow:0 0 10px 2px rgba(0,0,0,.3);--tw-shadow-colored:0 0 10px 2px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-0,.ring-1{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-150{transition-duration:.15s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[stroke-dasharray\:6\2c 4\]{stroke-dasharray:6,4}.\[stroke-linecap\:round\]{stroke-linecap:round}.\[stroke-linejoin\:round\]{stroke-linejoin:round}.\[writing-mode\:vertical-lr\]{writing-mode:vertical-lr}.checked\:border-indigo-600:checked{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity))}.checked\:bg-indigo-600:checked{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:whitespace-normal:hover{white-space:normal}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-indigo-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:relative:focus{position:relative}.focus\:z-50:focus{z-index:50}.focus\:border-indigo-500:focus{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:outline-1:focus{outline-width:1px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.focus\:ring-indigo-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(79 70 229/var(--tw-ring-opacity))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-white\/75:focus-visible{--tw-ring-color:hsla(0,0%,100%,.75)}.focus-visible\:ring-white:focus-visible{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}.focus-visible\:ring-indigo-500:focus-visible{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.focus-visible\:ring-opacity-75:focus-visible{--tw-ring-opacity:0.75}.group:hover .group-hover\:flex{display:flex}[class~=theme-dark] .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}[class~=theme-dark] .dark\:border-r-gray-700{--tw-border-opacity:1;border-right-color:rgb(55 65 81/var(--tw-border-opacity))}[class~=theme-dark] .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-black\/50{background-color:rgba(0,0,0,.5)}[class~=theme-dark] .dark\:bg-indigo-700{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-100\/90{background-color:rgba(243,244,246,.9)}[class~=theme-dark] .dark\:bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-200\/75{background-color:rgba(229,231,235,.75)}[class~=theme-dark] .dark\:bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-opacity-80{--tw-bg-opacity:0.8}[class~=theme-dark] .dark\:stroke-gray-500{stroke:#6b7280}[class~=theme-dark] .dark\:stroke-gray-700{stroke:#374151}[class~=theme-dark] .dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-indigo-500{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity))}[class~=theme-dark] .dark\:\!text-indigo-400{--tw-text-opacity:1!important;color:rgb(129 140 248/var(--tw-text-opacity))!important}[class~=theme-dark] .dark\:ring-white{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}[class~=theme-dark] .dark\:ring-opacity-20{--tw-ring-opacity:0.2}[class~=theme-dark] .hover\:dark\:text-gray-100:hover{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:block{display:block}.md\:flex-row{flex-direction:row}}
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.24",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@floating-ui/react": "^0.27.12",
|
|
7
7
|
"@headlessui/react": "^1.7.19",
|
|
8
8
|
"@iconify/react": "^4.0.0",
|
|
9
9
|
"@parca/client": "0.17.3",
|
|
10
|
-
"@parca/components": "0.16.
|
|
10
|
+
"@parca/components": "0.16.351",
|
|
11
11
|
"@parca/dynamicsize": "0.16.65",
|
|
12
|
-
"@parca/hooks": "0.0.
|
|
12
|
+
"@parca/hooks": "0.0.96",
|
|
13
13
|
"@parca/icons": "0.16.72",
|
|
14
14
|
"@parca/parser": "0.16.79",
|
|
15
|
-
"@parca/store": "0.16.
|
|
15
|
+
"@parca/store": "0.16.180",
|
|
16
16
|
"@parca/utilities": "0.0.104",
|
|
17
17
|
"@popperjs/core": "^2.11.8",
|
|
18
18
|
"@protobuf-ts/runtime-rpc": "^2.5.0",
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"access": "public",
|
|
79
79
|
"registry": "https://registry.npmjs.org/"
|
|
80
80
|
},
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "a14a290bb5329cb3faaf18c171f1278df2821bda"
|
|
82
82
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import type {ProfileFilter} from '@parca/store';
|
|
15
|
+
|
|
16
|
+
export interface FilterPreset {
|
|
17
|
+
key: string;
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
filters: Array<Omit<ProfileFilter, 'id'>>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const filterPresets: FilterPreset[] = [
|
|
24
|
+
{
|
|
25
|
+
key: 'go_runtime_expected_off_cpu',
|
|
26
|
+
name: 'Go Runtime Expected Off-CPU',
|
|
27
|
+
description: 'Excludes expected Go runtime blocking functions',
|
|
28
|
+
filters: [
|
|
29
|
+
{
|
|
30
|
+
type: 'stack',
|
|
31
|
+
field: 'function_name',
|
|
32
|
+
matchType: 'not_equal',
|
|
33
|
+
value: 'runtime.usleep',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'stack',
|
|
37
|
+
field: 'function_name',
|
|
38
|
+
matchType: 'not_equal',
|
|
39
|
+
value: 'runtime.futex',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
key: 'rust_runtime_expected_off_cpu',
|
|
45
|
+
name: 'Rust Expected Off-CPU',
|
|
46
|
+
description: 'Excludes expected Rust runtime blocking functions',
|
|
47
|
+
filters: [
|
|
48
|
+
{
|
|
49
|
+
type: 'stack',
|
|
50
|
+
field: 'function_name',
|
|
51
|
+
matchType: 'not_equal',
|
|
52
|
+
value: 'parking_lot_core::thread_parker::imp::ThreadParker::futex_wait',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: 'stack',
|
|
56
|
+
field: 'function_name',
|
|
57
|
+
matchType: 'not_equal',
|
|
58
|
+
value: 'tokio::runtime::time::Driver::park_internal',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: 'stack',
|
|
62
|
+
field: 'function_name',
|
|
63
|
+
matchType: 'not_equal',
|
|
64
|
+
value: 'futex_wait',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
export const isPresetKey = (key: string): boolean => {
|
|
71
|
+
return filterPresets.some(preset => preset.key === key);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const getPresetByKey = (key: string): FilterPreset | undefined => {
|
|
75
|
+
return filterPresets.find(preset => preset.key === key);
|
|
76
|
+
};
|
|
@@ -18,9 +18,15 @@ import cx from 'classnames';
|
|
|
18
18
|
|
|
19
19
|
import {Button, Input, Select, type SelectItem} from '@parca/components';
|
|
20
20
|
|
|
21
|
+
import {filterPresets, getPresetByKey, isPresetKey} from './filterPresets';
|
|
21
22
|
import {useProfileFilters, type ProfileFilter} from './useProfileFilters';
|
|
22
23
|
|
|
23
24
|
export const isFilterComplete = (filter: ProfileFilter): boolean => {
|
|
25
|
+
// For preset filters, only need type and value
|
|
26
|
+
if (filter.type != null && isPresetKey(filter.type)) {
|
|
27
|
+
return filter.value !== '' && filter.type != null;
|
|
28
|
+
}
|
|
29
|
+
// For regular filters, need all fields
|
|
24
30
|
return (
|
|
25
31
|
filter.value !== '' && filter.type != null && filter.field != null && filter.matchType != null
|
|
26
32
|
);
|
|
@@ -53,6 +59,19 @@ const filterTypeItems: SelectItem[] = [
|
|
|
53
59
|
),
|
|
54
60
|
},
|
|
55
61
|
},
|
|
62
|
+
...filterPresets.map(preset => ({
|
|
63
|
+
key: preset.key,
|
|
64
|
+
element: {
|
|
65
|
+
active: <>{preset.name}</>,
|
|
66
|
+
expanded: (
|
|
67
|
+
<>
|
|
68
|
+
<span>{preset.name}</span>
|
|
69
|
+
<br />
|
|
70
|
+
<span className="text-xs">{preset.description}</span>
|
|
71
|
+
</>
|
|
72
|
+
),
|
|
73
|
+
},
|
|
74
|
+
})),
|
|
56
75
|
];
|
|
57
76
|
|
|
58
77
|
const fieldItems: SelectItem[] = [
|
|
@@ -181,6 +200,7 @@ const ProfileFilters = (): JSX.Element => {
|
|
|
181
200
|
{filtersToRender.map(filter => {
|
|
182
201
|
const isNumberField = filter.field === 'address' || filter.field === 'line_number';
|
|
183
202
|
const matchTypeItems = isNumberField ? numberMatchTypeItems : stringMatchTypeItems;
|
|
203
|
+
const isPresetFilter = filter.type != null && isPresetKey(filter.type);
|
|
184
204
|
|
|
185
205
|
return (
|
|
186
206
|
<div key={filter.id} className="flex items-center gap-0">
|
|
@@ -189,20 +209,45 @@ const ProfileFilters = (): JSX.Element => {
|
|
|
189
209
|
selectedKey={filter.type}
|
|
190
210
|
placeholder="Select Filter"
|
|
191
211
|
onSelection={key => {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
212
|
+
// Check if this is a preset selection
|
|
213
|
+
if (isPresetKey(key)) {
|
|
214
|
+
const preset = getPresetByKey(key);
|
|
215
|
+
if (preset != null) {
|
|
216
|
+
updateFilter(filter.id, {
|
|
217
|
+
type: preset.key,
|
|
218
|
+
field: undefined,
|
|
219
|
+
matchType: undefined,
|
|
220
|
+
value: preset.name,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
const newType = key as 'stack' | 'frame';
|
|
225
|
+
|
|
226
|
+
// Check if we're converting a preset filter to a regular filter
|
|
227
|
+
if (filter.type != null && isPresetKey(filter.type)) {
|
|
228
|
+
updateFilter(filter.id, {
|
|
229
|
+
type: newType,
|
|
230
|
+
field: 'function_name',
|
|
231
|
+
matchType: 'contains',
|
|
232
|
+
value: '',
|
|
233
|
+
});
|
|
234
|
+
} else {
|
|
235
|
+
updateFilter(filter.id, {
|
|
236
|
+
type: newType,
|
|
237
|
+
field: filter.field ?? 'function_name',
|
|
238
|
+
matchType: filter.matchType ?? 'contains',
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
198
242
|
}}
|
|
199
243
|
className={cx(
|
|
200
|
-
'rounded-l-md pr-1 gap-0 focus:z-50 focus:relative focus:outline-1
|
|
201
|
-
|
|
244
|
+
'rounded-l-md pr-1 gap-0 focus:z-50 focus:relative focus:outline-1',
|
|
245
|
+
isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none',
|
|
246
|
+
filter.type != null ? 'border-r-0 w-auto' : 'w-32'
|
|
202
247
|
)}
|
|
203
248
|
/>
|
|
204
249
|
|
|
205
|
-
{filter.type != null && (
|
|
250
|
+
{filter.type != null && !isPresetFilter && (
|
|
206
251
|
<>
|
|
207
252
|
<Select
|
|
208
253
|
items={fieldItems}
|
|
@@ -247,9 +292,16 @@ const ProfileFilters = (): JSX.Element => {
|
|
|
247
292
|
<Button
|
|
248
293
|
variant="neutral"
|
|
249
294
|
onClick={() => {
|
|
250
|
-
|
|
295
|
+
// If we're displaying local filters and this is the last one, reset everything
|
|
296
|
+
if (localFilters.length > 0 && localFilters.length === 1) {
|
|
251
297
|
resetFilters();
|
|
252
|
-
}
|
|
298
|
+
}
|
|
299
|
+
// If we're displaying applied filters and this is the last one, reset everything
|
|
300
|
+
else if (localFilters.length === 0 && filtersToRender.length === 1) {
|
|
301
|
+
resetFilters();
|
|
302
|
+
}
|
|
303
|
+
// Otherwise, just remove this specific filter
|
|
304
|
+
else {
|
|
253
305
|
removeFilter(filter.id);
|
|
254
306
|
}
|
|
255
307
|
}}
|
|
@@ -278,10 +330,11 @@ const ProfileFilters = (): JSX.Element => {
|
|
|
278
330
|
)}
|
|
279
331
|
</div>
|
|
280
332
|
|
|
281
|
-
{localFilters.length > 0 &&
|
|
333
|
+
{localFilters.length > 0 && (
|
|
282
334
|
<Button
|
|
283
335
|
variant="primary"
|
|
284
336
|
onClick={onApplyFilters}
|
|
337
|
+
disabled={!hasUnsavedChanges || !localFilters.some(isFilterComplete)}
|
|
285
338
|
className={cx('flex items-center gap-2 self-end')}
|
|
286
339
|
>
|
|
287
340
|
<span>Apply</span>
|
|
@@ -22,13 +22,35 @@ import {
|
|
|
22
22
|
type ProfileFilter,
|
|
23
23
|
} from '@parca/store';
|
|
24
24
|
|
|
25
|
+
import {getPresetByKey, isPresetKey, type FilterPreset} from './filterPresets';
|
|
25
26
|
import {useProfileFiltersUrlState} from './useProfileFiltersUrlState';
|
|
26
27
|
|
|
27
28
|
export type {ProfileFilter};
|
|
28
29
|
|
|
29
30
|
// Convert ProfileFilter[] to protobuf Filter[] matching the expected structure
|
|
30
31
|
export const convertToProtoFilters = (profileFilters: ProfileFilter[]): Filter[] => {
|
|
31
|
-
|
|
32
|
+
// First, expand any preset filters to their constituent filters
|
|
33
|
+
const expandedFilters: ProfileFilter[] = [];
|
|
34
|
+
|
|
35
|
+
for (const filter of profileFilters) {
|
|
36
|
+
if (filter.type != null && isPresetKey(filter.type)) {
|
|
37
|
+
// This is a preset filter, expand it
|
|
38
|
+
const preset = getPresetByKey(filter.type);
|
|
39
|
+
if (preset != null) {
|
|
40
|
+
preset.filters.forEach((presetFilter, index) => {
|
|
41
|
+
expandedFilters.push({
|
|
42
|
+
...presetFilter,
|
|
43
|
+
id: `${filter.id}-expanded-${index}`,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
// Regular filter, add as is
|
|
49
|
+
expandedFilters.push(filter);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return expandedFilters
|
|
32
54
|
.filter(f => f.value !== '' && f.type != null && f.field != null && f.matchType != null) // Only include complete filters with values
|
|
33
55
|
.map(f => {
|
|
34
56
|
// Build the condition based on field type
|
|
@@ -120,6 +142,7 @@ export const useProfileFilters = (): {
|
|
|
120
142
|
removeFilter: (id: string) => void;
|
|
121
143
|
updateFilter: (id: string, updates: Partial<ProfileFilter>) => void;
|
|
122
144
|
resetFilters: () => void;
|
|
145
|
+
applyPreset: (preset: FilterPreset) => void;
|
|
123
146
|
} => {
|
|
124
147
|
const {appliedFilters, setAppliedFilters} = useProfileFiltersUrlState();
|
|
125
148
|
const dispatch = useAppDispatch();
|
|
@@ -151,8 +174,23 @@ export const useProfileFilters = (): {
|
|
|
151
174
|
}, []);
|
|
152
175
|
|
|
153
176
|
const hasUnsavedChanges = useMemo(() => {
|
|
154
|
-
const localWithValues = localFilters.filter(f =>
|
|
155
|
-
|
|
177
|
+
const localWithValues = localFilters.filter(f => {
|
|
178
|
+
// For preset filters, only need type and value
|
|
179
|
+
if (f.type != null && isPresetKey(f.type)) {
|
|
180
|
+
return f.value !== '' && f.type != null;
|
|
181
|
+
}
|
|
182
|
+
// For regular filters, need all fields
|
|
183
|
+
return f.value !== '' && f.type != null && f.field != null && f.matchType != null;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const appliedWithValues = (appliedFilters ?? []).filter(f => {
|
|
187
|
+
// For preset filters, only need type and value
|
|
188
|
+
if (f.type != null && isPresetKey(f.type)) {
|
|
189
|
+
return f.value !== '' && f.type != null;
|
|
190
|
+
}
|
|
191
|
+
// For regular filters, need all fields
|
|
192
|
+
return f.value !== '' && f.type != null && f.field != null && f.matchType != null;
|
|
193
|
+
});
|
|
156
194
|
|
|
157
195
|
if (localWithValues.length !== appliedWithValues.length) return true;
|
|
158
196
|
|
|
@@ -252,9 +290,14 @@ export const useProfileFilters = (): {
|
|
|
252
290
|
}, [dispatch, setAppliedFilters]);
|
|
253
291
|
|
|
254
292
|
const onApplyFilters = useCallback((): void => {
|
|
255
|
-
const validFilters = localFilters.filter(
|
|
256
|
-
|
|
257
|
-
|
|
293
|
+
const validFilters = localFilters.filter(f => {
|
|
294
|
+
// For preset filters, only need type and value
|
|
295
|
+
if (f.type != null && isPresetKey(f.type)) {
|
|
296
|
+
return f.value !== '' && f.type != null;
|
|
297
|
+
}
|
|
298
|
+
// For regular filters, need all fields
|
|
299
|
+
return f.value !== '' && f.type != null && f.field != null && f.matchType != null;
|
|
300
|
+
});
|
|
258
301
|
|
|
259
302
|
const filtersToApply = validFilters.map((f, index) => ({
|
|
260
303
|
...f,
|
|
@@ -268,6 +311,23 @@ export const useProfileFilters = (): {
|
|
|
268
311
|
return convertToProtoFilters(appliedFilters ?? []);
|
|
269
312
|
}, [appliedFilters]);
|
|
270
313
|
|
|
314
|
+
const applyPreset = useCallback(
|
|
315
|
+
(preset: FilterPreset) => {
|
|
316
|
+
const presetFilter: ProfileFilter = {
|
|
317
|
+
id: `filter-preset-${Date.now()}`,
|
|
318
|
+
type: preset.key,
|
|
319
|
+
value: preset.name,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// Add preset filter to existing filters
|
|
323
|
+
const updatedFilters = [...localFilters, presetFilter];
|
|
324
|
+
dispatch(setLocalFilters(updatedFilters));
|
|
325
|
+
|
|
326
|
+
setAppliedFilters(updatedFilters);
|
|
327
|
+
},
|
|
328
|
+
[dispatch, setAppliedFilters, localFilters]
|
|
329
|
+
);
|
|
330
|
+
|
|
271
331
|
return {
|
|
272
332
|
localFilters,
|
|
273
333
|
appliedFilters,
|
|
@@ -280,5 +340,6 @@ export const useProfileFilters = (): {
|
|
|
280
340
|
removeFilter,
|
|
281
341
|
updateFilter,
|
|
282
342
|
resetFilters,
|
|
343
|
+
applyPreset,
|
|
283
344
|
};
|
|
284
345
|
};
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
import {useURLStateCustom, type ParamValueSetterCustom} from '@parca/components';
|
|
15
15
|
import {type ProfileFilter} from '@parca/store';
|
|
16
16
|
|
|
17
|
+
import {isPresetKey} from './filterPresets';
|
|
18
|
+
|
|
17
19
|
// Compact encoding mappings
|
|
18
20
|
const TYPE_MAP: Record<string, string> = {
|
|
19
21
|
stack: 's',
|
|
@@ -46,8 +48,16 @@ const encodeProfileFilters = (filters: ProfileFilter[]): string => {
|
|
|
46
48
|
if (filters.length === 0) return '';
|
|
47
49
|
|
|
48
50
|
return filters
|
|
49
|
-
.filter(f => f.value !== '' && f.type != null
|
|
51
|
+
.filter(f => f.value !== '' && f.type != null)
|
|
50
52
|
.map(f => {
|
|
53
|
+
// Handle preset filters differently
|
|
54
|
+
if (isPresetKey(f.type!)) {
|
|
55
|
+
const presetKey = encodeURIComponent(f.type!);
|
|
56
|
+
const value = encodeURIComponent(f.value);
|
|
57
|
+
return `p:${presetKey}:${value}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Handle regular filters
|
|
51
61
|
const type = TYPE_MAP[f.type!];
|
|
52
62
|
const field = FIELD_MAP[f.field!];
|
|
53
63
|
const match = MATCH_MAP[f.matchType!];
|
|
@@ -63,7 +73,22 @@ export const decodeProfileFilters = (encoded: string): ProfileFilter[] => {
|
|
|
63
73
|
|
|
64
74
|
try {
|
|
65
75
|
return encoded.split(',').map((filter, index) => {
|
|
66
|
-
const
|
|
76
|
+
const parts = filter.split(':');
|
|
77
|
+
|
|
78
|
+
// Handle preset filters (format: p:presetKey:value)
|
|
79
|
+
if (parts[0] === 'p' && parts.length >= 3) {
|
|
80
|
+
const presetKey = decodeURIComponent(parts[1]);
|
|
81
|
+
const value = decodeURIComponent(parts.slice(2).join(':')); // Handle values with colons
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
id: `filter-${Date.now()}-${index}`,
|
|
85
|
+
type: presetKey,
|
|
86
|
+
value,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Handle regular filters (format: type:field:match:value)
|
|
91
|
+
const [type, field, match, ...valueParts] = parts;
|
|
67
92
|
const value = decodeURIComponent(valueParts.join(':')); // Handle values with colons
|
|
68
93
|
|
|
69
94
|
return {
|