@parca/profile 0.16.250 → 0.16.252

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 CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 0.16.252 (2023-09-11)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.16.251](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.250...@parca/profile@0.16.251) (2023-09-07)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## [0.16.250](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.249...@parca/profile@0.16.250) (2023-09-07)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -15,27 +15,31 @@ import { useState } from 'react';
15
15
  import cx from 'classnames';
16
16
  import { CopyToClipboard } from 'react-copy-to-clipboard';
17
17
  import { Tooltip } from 'react-tooltip';
18
- import { QueryRequest_ReportType } from '@parca/client';
19
- import { Button, useParcaContext, useURLState } from '@parca/components';
20
- import { divide, getLastItem, valueFormatter } from '@parca/utilities';
21
- import { FIELD_CUMULATIVE, FIELD_DIFF, FIELD_FUNCTION_FILE_NAME, FIELD_FUNCTION_START_LINE, FIELD_LOCATION_ADDRESS, FIELD_LOCATION_LINE, FIELD_MAPPING_BUILD_ID, FIELD_MAPPING_FILE, } from '../ProfileIcicleGraph/IcicleGraphArrow';
22
- import { arrowToString, nodeLabel } from '../ProfileIcicleGraph/IcicleGraphArrow/utils';
23
- import { useProfileViewContext } from '../ProfileView/ProfileViewContext';
24
- import { useQuery } from '../useQuery';
18
+ import { Button, IconButton, useParcaContext } from '@parca/components';
19
+ import { USER_PREFERENCES, useUserPreference } from '@parca/hooks';
20
+ import { getLastItem } from '@parca/utilities';
25
21
  import { hexifyAddress, truncateString, truncateStringReverse } from '../utils';
26
22
  import { ExpandOnHover } from './ExpandOnHoverValue';
23
+ import { useGraphTooltip } from './useGraphTooltip';
24
+ import { useGraphTooltipMetaInfo } from './useGraphTooltipMetaInfo';
27
25
  let timeoutHandle = null;
28
26
  const NoData = () => {
29
27
  return _jsx("span", { className: "rounded bg-gray-200 px-2 dark:bg-gray-800", children: "Not available" });
30
28
  };
31
29
  const GraphTooltipArrowContent = ({ table, unit, total, totalUnfiltered, row, level, isFixed, navigateTo, }) => {
32
30
  const [isCopied, setIsCopied] = useState(false);
33
- if (row === null) {
31
+ const graphTooltipData = useGraphTooltip({
32
+ table,
33
+ unit,
34
+ total,
35
+ totalUnfiltered,
36
+ row,
37
+ level,
38
+ });
39
+ const [_, setIsDocked] = useUserPreference(USER_PREFERENCES.GRAPH_METAINFO_DOCKED.key);
40
+ if (graphTooltipData === null) {
34
41
  return _jsx(_Fragment, {});
35
42
  }
36
- const locationAddress = table.getChild(FIELD_LOCATION_ADDRESS)?.get(row) ?? 0n;
37
- const cumulative = BigInt(table.getChild(FIELD_CUMULATIVE)?.get(row)) ?? 0n;
38
- const diff = BigInt(table.getChild(FIELD_DIFF)?.get(row)) ?? 0n;
39
43
  const onCopy = () => {
40
44
  setIsCopied(true);
41
45
  if (timeoutHandle !== null) {
@@ -43,89 +47,16 @@ const GraphTooltipArrowContent = ({ table, unit, total, totalUnfiltered, row, le
43
47
  }
44
48
  timeoutHandle = setTimeout(() => setIsCopied(false), 3000);
45
49
  };
46
- const prevValue = cumulative - diff;
47
- const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
48
- const diffSign = diff > 0 ? '+' : '';
49
- const diffValueText = diffSign + valueFormatter(diff, unit, 1);
50
- const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
51
- const diffText = `${diffValueText} (${diffPercentageText})`;
52
- const name = nodeLabel(table, row, level, false);
53
- const getTextForCumulative = (hoveringNodeCumulative) => {
54
- const filtered = totalUnfiltered > total
55
- ? ` / ${(100 * divide(hoveringNodeCumulative, total)).toFixed(2)}% of filtered`
56
- : '';
57
- return `${valueFormatter(hoveringNodeCumulative, unit, 2)}
58
- (${(100 * divide(hoveringNodeCumulative, totalUnfiltered)).toFixed(2)}%${filtered})`;
59
- };
60
- return (_jsx("div", { className: `flex text-sm ${isFixed ? 'w-full' : ''}`, children: _jsx("div", { className: `m-auto w-full ${isFixed ? 'w-full' : ''}`, children: _jsxs("div", { className: "min-h-52 flex w-[500px] flex-col justify-between rounded-lg border border-gray-300 bg-gray-50 p-3 shadow-lg dark:border-gray-500 dark:bg-gray-900", children: [_jsx("div", { className: "flex flex-row", children: _jsxs("div", { className: "mx-2", children: [_jsx("div", { className: "flex h-10 items-center break-all font-semibold", children: row === 0 ? (_jsx("p", { children: "root" })) : (_jsx(_Fragment, { children: name !== '' ? (_jsx(CopyToClipboard, { onCopy: onCopy, text: name, children: _jsx("button", { className: "cursor-pointer text-left", children: name }) })) : (_jsx(_Fragment, { children: locationAddress !== 0n ? (_jsx(CopyToClipboard, { onCopy: onCopy, text: hexifyAddress(locationAddress), children: _jsx("button", { className: "cursor-pointer text-left", children: hexifyAddress(locationAddress) }) })) : (_jsx("p", { children: "unknown" })) })) })) }), _jsx("table", { className: "my-2 w-full table-fixed pr-0 text-gray-700 dark:text-gray-300", children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Cumulative" }), _jsx("td", { className: "w-3/4", children: _jsx(CopyToClipboard, { onCopy: onCopy, text: getTextForCumulative(cumulative), children: _jsx("button", { className: "cursor-pointer", children: getTextForCumulative(cumulative) }) }) })] }), diff !== 0n && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Diff" }), _jsx("td", { className: "w-3/4", children: _jsx(CopyToClipboard, { onCopy: onCopy, text: diffText, children: _jsx("button", { className: "cursor-pointer", children: diffText }) }) })] })), _jsx(TooltipMetaInfo, { table: table, row: row, onCopy: onCopy, navigateTo: navigateTo })] }) })] }) }), _jsx("span", { className: "mx-2 block text-xs text-gray-500", children: isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.' })] }) }) }));
50
+ const { name, locationAddress, cumulativeText, diffText, diff, row: rowNumber } = graphTooltipData;
51
+ return (_jsx("div", { className: `flex text-sm ${isFixed ? 'w-full' : ''}`, children: _jsx("div", { className: `m-auto w-full ${isFixed ? 'w-full' : ''}`, children: _jsxs("div", { className: "min-h-52 flex w-[500px] flex-col justify-between rounded-lg border border-gray-300 bg-gray-50 p-3 shadow-lg dark:border-gray-500 dark:bg-gray-900", children: [_jsx("div", { className: "flex flex-row", children: _jsxs("div", { className: "mx-2", children: [_jsxs("div", { className: "flex h-10 items-start justify-between gap-4 break-all font-semibold", children: [row === 0 ? (_jsx("p", { children: "root" })) : (_jsx(_Fragment, { children: name !== '' ? (_jsx(CopyToClipboard, { onCopy: onCopy, text: name, children: _jsx("button", { className: "cursor-pointer text-left", children: name }) })) : (_jsx(_Fragment, { children: locationAddress !== 0n ? (_jsx(CopyToClipboard, { onCopy: onCopy, text: hexifyAddress(locationAddress), children: _jsx("button", { className: "cursor-pointer text-left", children: hexifyAddress(locationAddress) }) })) : (_jsx("p", { children: "unknown" })) })) })), _jsx(IconButton, { onClick: () => setIsDocked(true), icon: "mdi:dock-bottom", title: "Dock MetaInfo Panel" })] }), _jsx("table", { className: "my-2 w-full table-fixed pr-0 text-gray-700 dark:text-gray-300", children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Cumulative" }), _jsx("td", { className: "w-3/4", children: _jsx(CopyToClipboard, { onCopy: onCopy, text: cumulativeText, children: _jsx("button", { className: "cursor-pointer", children: cumulativeText }) }) })] }), diff !== 0n && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Diff" }), _jsx("td", { className: "w-3/4", children: _jsx(CopyToClipboard, { onCopy: onCopy, text: diffText, children: _jsx("button", { className: "cursor-pointer", children: diffText }) }) })] })), _jsx(TooltipMetaInfo, { table: table, row: rowNumber, onCopy: onCopy, navigateTo: navigateTo })] }) })] }) }), _jsx("span", { className: "mx-2 block text-xs text-gray-500", children: isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.' })] }) }) }));
61
52
  };
62
53
  const TooltipMetaInfo = ({ table,
63
54
  // total,
64
55
  // totalUnfiltered,
65
56
  onCopy, row, navigateTo, }) => {
66
- const mappingFile = arrowToString(table.getChild(FIELD_MAPPING_FILE)?.get(row)) ?? '';
67
- const mappingBuildID = arrowToString(table.getChild(FIELD_MAPPING_BUILD_ID)?.get(row)) ?? '';
68
- const locationAddress = table.getChild(FIELD_LOCATION_ADDRESS)?.get(row) ?? 0n;
69
- const locationLine = table.getChild(FIELD_LOCATION_LINE)?.get(row) ?? 0n;
70
- const functionFilename = arrowToString(table.getChild(FIELD_FUNCTION_FILE_NAME)?.get(row)) ?? '';
71
- const functionStartLine = table.getChild(FIELD_FUNCTION_START_LINE)?.get(row) ?? 0n;
72
- const lineNumber = locationLine !== 0n ? locationLine : functionStartLine !== 0n ? functionStartLine : undefined;
73
- const pprofLabelPrefix = 'pprof_labels.';
74
- const labelColumnNames = table.schema.fields.filter(field => field.name.startsWith(pprofLabelPrefix));
75
- const { queryServiceClient, enableSourcesView } = useParcaContext();
76
- const { profileSource } = useProfileViewContext();
77
- const { isLoading: sourceLoading, response: sourceResponse } = useQuery(queryServiceClient, profileSource, QueryRequest_ReportType.SOURCE, {
78
- skip: enableSourcesView === false ||
79
- profileSource === undefined ||
80
- // eslint-disable-next-line no-extra-boolean-cast
81
- !Boolean(mappingBuildID) ||
82
- // eslint-disable-next-line no-extra-boolean-cast
83
- !Boolean(functionFilename),
84
- sourceBuildID: mappingBuildID,
85
- sourceFilename: functionFilename,
86
- sourceOnly: true,
87
- });
88
- const isSourceAvailable = !sourceLoading && sourceResponse?.report != null;
89
- const getTextForFile = () => {
90
- if (functionFilename === '')
91
- return '<unknown>';
92
- return `${functionFilename} ${lineNumber !== undefined ? ` +${lineNumber.toString()}` : ''}`;
93
- };
94
- const file = getTextForFile();
95
- const labelPairs = labelColumnNames
96
- .map((field, i) => [
97
- labelColumnNames[i].name.slice(pprofLabelPrefix.length),
98
- arrowToString(table.getChild(field.name)?.get(row)) ?? '',
99
- ])
100
- .filter(value => value[1] !== '');
57
+ const { labelPairs, functionFilename, file, openFile, isSourceAvailable, locationAddress, mappingFile, mappingBuildID, } = useGraphTooltipMetaInfo({ table, row, navigateTo });
58
+ const { enableSourcesView } = useParcaContext();
101
59
  const labels = labelPairs.map((l) => (_jsx("span", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", children: `${l[0]}="${l[1]}"` }, l[0])));
102
- const [dashboardItems, setDashboardItems] = useURLState({
103
- param: 'dashboard_items',
104
- navigateTo,
105
- });
106
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
107
- const [unusedBuildId, setSourceBuildId] = useURLState({
108
- param: 'source_buildid',
109
- navigateTo,
110
- });
111
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
112
- const [unusedFilename, setSourceFilename] = useURLState({
113
- param: 'source_filename',
114
- navigateTo,
115
- });
116
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
117
- const [unusedLine, setSourceLine] = useURLState({
118
- param: 'source_line',
119
- navigateTo,
120
- });
121
- const openFile = () => {
122
- setDashboardItems([dashboardItems[0], 'source']);
123
- setSourceBuildId(mappingBuildID);
124
- setSourceFilename(functionFilename);
125
- if (lineNumber !== undefined) {
126
- setSourceLine(lineNumber.toString());
127
- }
128
- };
129
- return (_jsxs(_Fragment, { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "File" }), _jsx("td", { className: "w-3/4 break-all", children: functionFilename === '' ? (_jsx(NoData, {})) : (_jsxs("div", { className: "flex gap-4", children: [_jsx(CopyToClipboard, { onCopy: onCopy, text: file, children: _jsx("button", { className: "cursor-pointer whitespace-nowrap text-left", children: _jsx(ExpandOnHover, { value: file, displayValue: truncateStringReverse(file, 30) }) }) }), _jsxs("div", { className: cx('flex gap-2', { hidden: enableSourcesView === false }), children: [_jsx("div", { "data-tooltip-id": "open-source-button-help", "data-tooltip-content": "There is no source code uploaded for this build", children: _jsx(Button, { variant: 'neutral', onClick: () => openFile(), className: "shrink-0", disabled: !isSourceAvailable, children: "open" }) }), !isSourceAvailable ? _jsx(Tooltip, { id: "open-source-button-help" }) : null] })] })) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Address" }), _jsx("td", { className: "w-3/4 break-all", children: locationAddress === 0n ? (_jsx(NoData, {})) : (_jsx(CopyToClipboard, { onCopy: onCopy, text: hexifyAddress(locationAddress), children: _jsx("button", { className: "cursor-pointer", children: hexifyAddress(locationAddress) }) })) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Binary" }), _jsx("td", { className: "w-3/4 break-all", children: mappingFile === '' ? (_jsx(NoData, {})) : (_jsx(CopyToClipboard, { onCopy: onCopy, text: mappingFile, children: _jsx("button", { className: "cursor-pointer", children: getLastItem(mappingFile) }) })) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Build Id" }), _jsx("td", { className: "w-3/4 break-all", children: mappingBuildID === '' ? (_jsx(NoData, {})) : (_jsx(CopyToClipboard, { onCopy: onCopy, text: mappingBuildID, children: _jsx("button", { className: "cursor-pointer", children: truncateString(getLastItem(mappingBuildID), 28) }) })) })] }), labelPairs.length > 0 && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Labels" }), _jsx("td", { className: "w-3/4 break-all", children: labels })] }))] }));
60
+ return (_jsxs(_Fragment, { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "File" }), _jsx("td", { className: "w-3/4 break-all", children: functionFilename === '' ? (_jsx(NoData, {})) : (_jsxs("div", { className: "flex gap-4", children: [_jsx(CopyToClipboard, { onCopy: onCopy, text: file, children: _jsx("button", { className: "cursor-pointer whitespace-nowrap text-left", children: _jsx(ExpandOnHover, { value: file, displayValue: truncateStringReverse(file, 30) }) }) }), _jsxs("div", { className: cx('flex gap-2', { hidden: enableSourcesView === false }), children: [_jsx("div", { "data-tooltip-id": "open-source-button-help", "data-tooltip-content": "There is no source code uploaded for this build", children: _jsx(Button, { variant: 'neutral', onClick: () => openFile(), className: "shrink-0", disabled: !isSourceAvailable, children: "open" }) }), !isSourceAvailable ? _jsx(Tooltip, { id: "open-source-button-help" }) : null] })] })) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Address" }), _jsx("td", { className: "w-3/4 break-all", children: locationAddress === 0n ? (_jsx(NoData, {})) : (_jsx(CopyToClipboard, { onCopy: onCopy, text: hexifyAddress(locationAddress), children: _jsx("button", { className: "cursor-pointer", children: hexifyAddress(locationAddress) }) })) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Binary" }), _jsx("td", { className: "w-3/4 break-all", children: mappingFile === null ? (_jsx(NoData, {})) : (_jsx(CopyToClipboard, { onCopy: onCopy, text: mappingFile, children: _jsx("button", { className: "cursor-pointer", children: getLastItem(mappingFile) }) })) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Build Id" }), _jsx("td", { className: "w-3/4 break-all", children: mappingBuildID === null ? (_jsx(NoData, {})) : (_jsx(CopyToClipboard, { onCopy: onCopy, text: mappingBuildID, children: _jsx("button", { className: "cursor-pointer", children: truncateString(getLastItem(mappingBuildID), 28) }) })) })] }), labelPairs.length > 0 && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Labels" }), _jsx("td", { className: "w-3/4 break-all", children: labels })] }))] }));
130
61
  };
131
62
  export default GraphTooltipArrowContent;
@@ -0,0 +1,12 @@
1
+ /// <reference types="react" />
2
+ import { Table } from 'apache-arrow';
3
+ interface Props {
4
+ table: Table<any>;
5
+ unit: string;
6
+ total: bigint;
7
+ totalUnfiltered: bigint;
8
+ row: number | null;
9
+ level: number;
10
+ }
11
+ export declare const DockedGraphTooltip: ({ table, unit, total, totalUnfiltered, row, level, }: Props) => JSX.Element;
12
+ export {};
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ // Copyright 2022 The Parca Authors
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ import { useState } from 'react';
15
+ import cx from 'classnames';
16
+ import { CopyToClipboard } from 'react-copy-to-clipboard';
17
+ import { Tooltip } from 'react-tooltip';
18
+ import { useWindowSize } from 'react-use';
19
+ import { Button, IconButton, useParcaContext } from '@parca/components';
20
+ import { USER_PREFERENCES, useUserPreference } from '@parca/hooks';
21
+ import { getLastItem } from '@parca/utilities';
22
+ import { hexifyAddress, truncateString, truncateStringReverse } from '../../utils';
23
+ import { ExpandOnHover } from '../ExpandOnHoverValue';
24
+ import { useGraphTooltip } from '../useGraphTooltip';
25
+ import { useGraphTooltipMetaInfo } from '../useGraphTooltipMetaInfo';
26
+ let timeoutHandle = null;
27
+ const InfoSection = ({ title, value, onCopy, copyText, minWidth = '', }) => {
28
+ return (_jsxs("div", { className: cx('flex shrink-0 flex-col gap-1 p-2', { [minWidth]: minWidth != null }), children: [_jsx("p", { className: "text-sm font-medium leading-5 text-gray-500 dark:text-gray-400", children: title }), _jsx("div", { className: "text-lg font-normal text-gray-900 dark:text-gray-50", children: _jsx(CopyToClipboard, { onCopy: onCopy, text: copyText, children: _jsx("button", { children: value }) }) })] }));
29
+ };
30
+ export const DockedGraphTooltip = ({ table, unit, total, totalUnfiltered, row, level, }) => {
31
+ let { width } = useWindowSize();
32
+ const { profileExplorer, navigateTo, enableSourcesView } = useParcaContext();
33
+ const { PaddingX } = profileExplorer ?? { PaddingX: 0 };
34
+ width = width - PaddingX - 24;
35
+ const [isCopied, setIsCopied] = useState(false);
36
+ const onCopy = () => {
37
+ setIsCopied(true);
38
+ if (timeoutHandle !== null) {
39
+ clearTimeout(timeoutHandle);
40
+ }
41
+ timeoutHandle = setTimeout(() => setIsCopied(false), 3000);
42
+ };
43
+ const graphTooltipData = useGraphTooltip({
44
+ table,
45
+ unit,
46
+ total,
47
+ totalUnfiltered,
48
+ row,
49
+ level,
50
+ });
51
+ const { labelPairs, functionFilename, file, openFile, isSourceAvailable, locationAddress, mappingFile, mappingBuildID, } = useGraphTooltipMetaInfo({ table, row: row ?? 0, navigateTo });
52
+ const [_, setIsDocked] = useUserPreference(USER_PREFERENCES.GRAPH_METAINFO_DOCKED.key);
53
+ if (graphTooltipData === null) {
54
+ return _jsx(_Fragment, {});
55
+ }
56
+ const { name, cumulativeText, diffText, diff } = graphTooltipData;
57
+ const labels = labelPairs.map((l) => (_jsx("span", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", children: `${l[0]}="${l[1]}"` }, l[0])));
58
+ const addressText = locationAddress !== 0n ? hexifyAddress(locationAddress) : 'unknown';
59
+ const fileText = functionFilename !== '' ? file : 'Not available';
60
+ return (_jsx("div", { className: "fixed bottom-0 z-20 overflow-hidden rounded-t-lg border-l border-r border-t border-gray-400 bg-white bg-opacity-90 px-8 py-3 dark:border-gray-600 dark:bg-black dark:bg-opacity-80", style: { width }, children: _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex justify-between gap-4", children: [row === 0 ? (_jsx("p", { children: "root" })) : (_jsx(_Fragment, { children: name !== '' ? (_jsx(CopyToClipboard, { onCopy: onCopy, text: name, children: _jsx("button", { className: "cursor-pointer text-left", children: name }) })) : (_jsx(_Fragment, { children: locationAddress !== 0n ? (_jsx(CopyToClipboard, { onCopy: onCopy, text: hexifyAddress(locationAddress), children: _jsx("button", { className: "cursor-pointer text-left", children: hexifyAddress(locationAddress) }) })) : (_jsx("p", { children: "unknown" })) })) })), _jsx(IconButton, { onClick: () => setIsDocked(false), icon: "mdi:dock-window", title: "Undock MetaInfo Panel" })] }), _jsxs("div", { className: "flex justify-between gap-3", children: [_jsx(InfoSection, { title: "Cumulative", value: cumulativeText, onCopy: onCopy, copyText: cumulativeText, minWidth: "w-44" }), diff !== 0n ? (_jsx(InfoSection, { title: "Diff", value: diffText, onCopy: onCopy, copyText: diffText, minWidth: "w-44" })) : null, _jsx(InfoSection, { title: "File", value: _jsxs("div", { className: "flex gap-2", children: [_jsx(ExpandOnHover, { value: fileText, displayValue: truncateStringReverse(fileText, 45) }), _jsxs("div", { className: cx('flex items-center gap-2', {
61
+ hidden: enableSourcesView === false || functionFilename === '',
62
+ }), children: [_jsx("div", { "data-tooltip-id": "open-source-button-help", "data-tooltip-content": "There is no source code uploaded for this build", children: _jsx(Button, { variant: 'neutral', onClick: () => openFile(), className: "shrink-0", disabled: !isSourceAvailable, children: "open" }) }), !isSourceAvailable ? _jsx(Tooltip, { id: "open-source-button-help" }) : null] })] }), onCopy: onCopy, copyText: file, minWidth: 'w-[460px]' }), _jsx(InfoSection, { title: "Address", value: addressText, onCopy: onCopy, copyText: addressText, minWidth: "w-44" }), _jsx(InfoSection, { title: "Binary", value: (mappingFile != null ? getLastItem(mappingFile) : null) ?? 'Not available', onCopy: onCopy, copyText: mappingFile ?? 'Not available', minWidth: "w-44" }), _jsx(InfoSection, { title: "Build ID", value: truncateString(getLastItem(mappingBuildID) ?? 'Not available', 28), onCopy: onCopy, copyText: mappingBuildID ?? 'Not available' })] }), _jsx("div", { children: _jsx("div", { className: "flex h-5 gap-1", children: labels }) }), _jsx("span", { className: "mx-2 block text-xs text-gray-500", children: isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.' })] }) }));
63
+ };
@@ -0,0 +1,19 @@
1
+ import { Table } from 'apache-arrow';
2
+ interface Props {
3
+ table: Table<any>;
4
+ unit: string;
5
+ total: bigint;
6
+ totalUnfiltered: bigint;
7
+ row: number | null;
8
+ level: number;
9
+ }
10
+ interface GraphTooltipData {
11
+ name: string;
12
+ locationAddress: bigint;
13
+ cumulativeText: string;
14
+ diffText: string;
15
+ diff: bigint;
16
+ row: number;
17
+ }
18
+ export declare const useGraphTooltip: ({ table, unit, total, totalUnfiltered, row, level, }: Props) => GraphTooltipData | null;
19
+ export {};
@@ -0,0 +1,38 @@
1
+ // Copyright 2022 The Parca Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { divide, valueFormatter } from '@parca/utilities';
14
+ import { FIELD_CUMULATIVE, FIELD_DIFF, FIELD_LOCATION_ADDRESS, } from '../../ProfileIcicleGraph/IcicleGraphArrow';
15
+ import { getTextForCumulative, nodeLabel } from '../../ProfileIcicleGraph/IcicleGraphArrow/utils';
16
+ export const useGraphTooltip = ({ table, unit, total, totalUnfiltered, row, level, }) => {
17
+ if (row === null) {
18
+ return null;
19
+ }
20
+ const locationAddress = table.getChild(FIELD_LOCATION_ADDRESS)?.get(row) ?? 0n;
21
+ const cumulative = BigInt(table.getChild(FIELD_CUMULATIVE)?.get(row)) ?? 0n;
22
+ const diff = BigInt(table.getChild(FIELD_DIFF)?.get(row)) ?? 0n;
23
+ const prevValue = cumulative - diff;
24
+ const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
25
+ const diffSign = diff > 0 ? '+' : '';
26
+ const diffValueText = diffSign + valueFormatter(diff, unit, 1);
27
+ const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
28
+ const diffText = `${diffValueText} (${diffPercentageText})`;
29
+ const name = nodeLabel(table, row, level, false);
30
+ return {
31
+ name,
32
+ locationAddress,
33
+ cumulativeText: getTextForCumulative(cumulative, totalUnfiltered, total, unit),
34
+ diffText,
35
+ diff,
36
+ row,
37
+ };
38
+ };
@@ -0,0 +1,19 @@
1
+ import { Table } from 'apache-arrow';
2
+ import type { NavigateFunction } from '@parca/utilities';
3
+ interface Props {
4
+ table: Table<any>;
5
+ row: number;
6
+ navigateTo: NavigateFunction;
7
+ }
8
+ interface GraphTooltipMetaInfoData {
9
+ labelPairs: Array<[string, string]>;
10
+ functionFilename: string;
11
+ file: string;
12
+ openFile: () => void;
13
+ isSourceAvailable: boolean;
14
+ locationAddress: bigint;
15
+ mappingFile: string | null;
16
+ mappingBuildID: string | null;
17
+ }
18
+ export declare const useGraphTooltipMetaInfo: ({ table, row, navigateTo, }: Props) => GraphTooltipMetaInfoData;
19
+ export {};
@@ -0,0 +1,94 @@
1
+ // Copyright 2022 The Parca Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { QueryRequest_ReportType } from '@parca/client';
14
+ import { useParcaContext, useURLState } from '@parca/components';
15
+ import { FIELD_FUNCTION_FILE_NAME, FIELD_FUNCTION_START_LINE, FIELD_LOCATION_ADDRESS, FIELD_LOCATION_LINE, FIELD_MAPPING_BUILD_ID, FIELD_MAPPING_FILE, } from '../../ProfileIcicleGraph/IcicleGraphArrow';
16
+ import { arrowToString } from '../../ProfileIcicleGraph/IcicleGraphArrow/utils';
17
+ import { useProfileViewContext } from '../../ProfileView/ProfileViewContext';
18
+ import { useQuery } from '../../useQuery';
19
+ export const useGraphTooltipMetaInfo = ({ table, row, navigateTo, }) => {
20
+ const mappingFile = arrowToString(table.getChild(FIELD_MAPPING_FILE)?.get(row));
21
+ const mappingBuildID = arrowToString(table.getChild(FIELD_MAPPING_BUILD_ID)?.get(row));
22
+ const locationAddress = table.getChild(FIELD_LOCATION_ADDRESS)?.get(row) ?? 0n;
23
+ const locationLine = table.getChild(FIELD_LOCATION_LINE)?.get(row) ?? 0n;
24
+ const functionFilename = arrowToString(table.getChild(FIELD_FUNCTION_FILE_NAME)?.get(row)) ?? '';
25
+ const functionStartLine = table.getChild(FIELD_FUNCTION_START_LINE)?.get(row) ?? 0n;
26
+ const lineNumber = locationLine !== 0n ? locationLine : functionStartLine !== 0n ? functionStartLine : undefined;
27
+ const pprofLabelPrefix = 'pprof_labels.';
28
+ const labelColumnNames = table.schema.fields.filter(field => field.name.startsWith(pprofLabelPrefix));
29
+ const { queryServiceClient, enableSourcesView } = useParcaContext();
30
+ const { profileSource } = useProfileViewContext();
31
+ const { isLoading: sourceLoading, response: sourceResponse } = useQuery(queryServiceClient, profileSource, QueryRequest_ReportType.SOURCE, {
32
+ skip: enableSourcesView === false ||
33
+ profileSource === undefined ||
34
+ // eslint-disable-next-line no-extra-boolean-cast
35
+ !Boolean(mappingBuildID) ||
36
+ // eslint-disable-next-line no-extra-boolean-cast
37
+ !Boolean(functionFilename),
38
+ sourceBuildID: mappingBuildID !== null ? mappingBuildID : undefined,
39
+ sourceFilename: functionFilename,
40
+ sourceOnly: true,
41
+ });
42
+ const isSourceAvailable = !sourceLoading && sourceResponse?.report != null;
43
+ const getTextForFile = () => {
44
+ if (functionFilename === '')
45
+ return '<unknown>';
46
+ return `${functionFilename} ${lineNumber !== undefined ? ` +${lineNumber.toString()}` : ''}`;
47
+ };
48
+ const file = getTextForFile();
49
+ const labelPairs = labelColumnNames
50
+ .map((field, i) => [
51
+ labelColumnNames[i].name.slice(pprofLabelPrefix.length),
52
+ arrowToString(table.getChild(field.name)?.get(row)) ?? '',
53
+ ])
54
+ .filter(value => value[1] !== '');
55
+ const [dashboardItems, setDashboardItems] = useURLState({
56
+ param: 'dashboard_items',
57
+ navigateTo,
58
+ });
59
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
60
+ const [unusedBuildId, setSourceBuildId] = useURLState({
61
+ param: 'source_buildid',
62
+ navigateTo,
63
+ });
64
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
65
+ const [unusedFilename, setSourceFilename] = useURLState({
66
+ param: 'source_filename',
67
+ navigateTo,
68
+ });
69
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
70
+ const [unusedLine, setSourceLine] = useURLState({
71
+ param: 'source_line',
72
+ navigateTo,
73
+ });
74
+ const openFile = () => {
75
+ setDashboardItems([dashboardItems[0], 'source']);
76
+ if (mappingBuildID != null) {
77
+ setSourceBuildId(mappingBuildID);
78
+ }
79
+ setSourceFilename(functionFilename);
80
+ if (lineNumber !== undefined) {
81
+ setSourceLine(lineNumber.toString());
82
+ }
83
+ };
84
+ return {
85
+ labelPairs,
86
+ functionFilename,
87
+ file,
88
+ openFile,
89
+ isSourceAvailable,
90
+ locationAddress,
91
+ mappingBuildID,
92
+ mappingFile,
93
+ };
94
+ };
@@ -18,6 +18,7 @@ import { getColorForFeature, selectDarkMode, setHoveringNode, useAppDispatch, us
18
18
  import { getLastItem, scaleLinear, selectQueryParam, } from '@parca/utilities';
19
19
  import GraphTooltipArrow from '../../GraphTooltipArrow';
20
20
  import GraphTooltipArrowContent from '../../GraphTooltipArrow/Content';
21
+ import { DockedGraphTooltip } from '../../GraphTooltipArrow/DockedGraphTooltip';
21
22
  import ColorStackLegend from './ColorStackLegend';
22
23
  import { IcicleNode, RowHeight } from './IcicleGraphNodes';
23
24
  import { arrowToString, extractFeature } from './utils';
@@ -36,6 +37,7 @@ export const FIELD_DIFF = 'diff';
36
37
  export const IcicleGraphArrow = memo(function IcicleGraphArrow({ arrow, total, filtered, width, setCurPath, curPath, sampleUnit, navigateTo, sortBy, }) {
37
38
  const dispatch = useAppDispatch();
38
39
  const [colorProfile] = useUserPreference(USER_PREFERENCES.FLAMEGRAPH_COLOR_PROFILE.key);
40
+ const [dockedMetainfo] = useUserPreference(USER_PREFERENCES.GRAPH_METAINFO_DOCKED.key);
39
41
  const isDarkMode = useAppSelector(selectDarkMode);
40
42
  const table = useMemo(() => {
41
43
  return tableFromIPC(arrow.record);
@@ -133,6 +135,6 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({ arrow, total, f
133
135
  if (table.numRows === 0 || width === undefined) {
134
136
  return _jsx(_Fragment, {});
135
137
  }
136
- return (_jsxs("div", { onMouseLeave: () => dispatch(setHoveringNode(undefined)), children: [_jsx(ColorStackLegend, { mappingColors: mappingColors, navigateTo: navigateTo, compareMode: compareMode }), _jsx(GraphTooltipArrow, { contextElement: svg.current, children: _jsx(GraphTooltipArrowContent, { table: table, row: hoveringRow, level: hoveringLevel ?? 0, isFixed: false, total: total, totalUnfiltered: total + filtered, unit: sampleUnit, navigateTo: navigateTo }) }), root] }));
138
+ return (_jsxs("div", { onMouseLeave: () => dispatch(setHoveringNode(undefined)), children: [_jsx(ColorStackLegend, { mappingColors: mappingColors, navigateTo: navigateTo, compareMode: compareMode }), dockedMetainfo ? (_jsx(DockedGraphTooltip, { table: table, row: hoveringRow, level: hoveringLevel ?? 0, total: total, totalUnfiltered: total + filtered, unit: sampleUnit })) : (_jsx(GraphTooltipArrow, { contextElement: svg.current, children: _jsx(GraphTooltipArrowContent, { table: table, row: hoveringRow, level: hoveringLevel ?? 0, isFixed: false, total: total, totalUnfiltered: total + filtered, unit: sampleUnit, navigateTo: navigateTo }) })), root] }));
137
139
  });
138
140
  export default IcicleGraphArrow;
@@ -2,4 +2,5 @@ import { Table } from 'apache-arrow';
2
2
  import { type Feature } from '@parca/store';
3
3
  export declare function nodeLabel(table: Table<any>, row: number, level: number, showBinaryName: boolean): string;
4
4
  export declare const extractFeature: (mapping: string) => Feature;
5
+ export declare const getTextForCumulative: (hoveringNodeCumulative: bigint, totalUnfiltered: bigint, total: bigint, unit: string) => string;
5
6
  export declare const arrowToString: (buffer: any) => string | null;
@@ -11,7 +11,7 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  import { EVERYTHING_ELSE, FEATURE_TYPES } from '@parca/store';
14
- import { getLastItem } from '@parca/utilities';
14
+ import { divide, getLastItem, valueFormatter } from '@parca/utilities';
15
15
  import { hexifyAddress } from '../../utils';
16
16
  import { FIELD_FUNCTION_NAME, FIELD_LABELS_ONLY, FIELD_LOCATION_ADDRESS, FIELD_MAPPING_FILE, } from './index';
17
17
  export function nodeLabel(table, row, level, showBinaryName) {
@@ -53,6 +53,13 @@ export const extractFeature = (mapping) => {
53
53
  }
54
54
  return { name: EVERYTHING_ELSE, type: FEATURE_TYPES.Misc };
55
55
  };
56
+ export const getTextForCumulative = (hoveringNodeCumulative, totalUnfiltered, total, unit) => {
57
+ const filtered = totalUnfiltered > total
58
+ ? ` / ${(100 * divide(hoveringNodeCumulative, total)).toFixed(2)}% of filtered`
59
+ : '';
60
+ return `${valueFormatter(hoveringNodeCumulative, unit, 2)}
61
+ (${(100 * divide(hoveringNodeCumulative, totalUnfiltered)).toFixed(2)}%${filtered})`;
62
+ };
56
63
  export const arrowToString = (buffer) => {
57
64
  if (buffer == null || typeof buffer === 'string') {
58
65
  return buffer;
@@ -101,11 +101,13 @@ const groupByOptions = [
101
101
  value: FIELD_FUNCTION_NAME,
102
102
  label: 'Function Name',
103
103
  description: 'Stacktraces are grouped by function names.',
104
+ disabled: true,
104
105
  },
105
106
  {
106
107
  value: FIELD_LABELS,
107
108
  label: 'Labels',
108
109
  description: 'Stacktraces are grouped by pprof labels.',
110
+ disabled: false,
109
111
  },
110
112
  ];
111
113
  const GroupByDropdown = ({ groupBy, toggleGroupBy, }) => {
@@ -114,9 +116,7 @@ const GroupByDropdown = ({ groupBy, toggleGroupBy, }) => {
114
116
  : groupBy.length === 1
115
117
  ? groupByOptions.find(option => option.value === groupBy[0])?.label
116
118
  : 'Multiple';
117
- return (_jsxs("div", { children: [_jsx("label", { className: "text-sm", children: "Group" }), _jsxs(Menu, { as: "div", className: "relative text-left", children: [_jsx("div", { children: _jsxs(Menu.Button, { className: "relative w-full cursor-default rounded-md border bg-gray-50 py-2 pl-3 pr-10 text-left text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900 sm:text-sm", children: [_jsx("span", { className: "ml-3 block overflow-x-hidden text-ellipsis", children: label }), _jsx("span", { className: "pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2 text-gray-400", children: _jsx(Icon, { icon: "heroicons:chevron-down-20-solid", "aria-hidden": "true" }) })] }) }), _jsx(Transition, { as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0", children: _jsx(Menu.Items, { className: "absolute left-0 z-10 mt-1 min-w-[400px] overflow-auto rounded-md bg-gray-50 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-gray-600 dark:bg-gray-900 dark:ring-white dark:ring-opacity-20 sm:text-sm", children: _jsx("div", { className: "p-4", children: _jsx("fieldset", { children: _jsx("div", { className: "space-y-5", children: groupByOptions.map(({ value, label, description }) => (_jsxs("div", { className: "relative flex items-start", children: [_jsx("div", { className: "flex h-6 items-center", children: _jsx("input", { id: value, name: value, type: "checkbox", className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600", checked: groupBy.includes(value), onChange: () => {
118
- toggleGroupBy(value);
119
- } }) }), _jsxs("div", { className: "ml-3 text-sm leading-6", children: [_jsx("label", { htmlFor: value, className: "font-medium text-gray-900", children: label }), _jsx("p", { className: "text-gray-500", children: description })] })] }, value))) }) }) }) }) })] })] }));
119
+ return (_jsxs("div", { children: [_jsx("label", { className: "text-sm", children: "Group" }), _jsxs(Menu, { as: "div", className: "relative text-left", children: [_jsx("div", { children: _jsxs(Menu.Button, { className: "relative w-full cursor-default rounded-md border bg-white py-2 pl-3 pr-10 text-left text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900 sm:text-sm", children: [_jsx("span", { className: "ml-3 block overflow-x-hidden text-ellipsis", children: label }), _jsx("span", { className: "pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2 text-gray-400", children: _jsx(Icon, { icon: "heroicons:chevron-down-20-solid", "aria-hidden": "true" }) })] }) }), _jsx(Transition, { as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0", children: _jsx(Menu.Items, { className: "absolute left-0 z-10 mt-1 min-w-[400px] overflow-auto rounded-md bg-gray-50 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-gray-600 dark:bg-gray-900 dark:ring-white dark:ring-opacity-20 sm:text-sm", children: _jsx("div", { className: "p-4", children: _jsx("fieldset", { children: _jsx("div", { className: "space-y-5", children: groupByOptions.map(({ value, label, description, disabled }) => (_jsxs("div", { className: "relative flex items-start", children: [_jsx("div", { className: "flex h-6 items-center", children: _jsx("input", { id: value, name: value, type: "checkbox", disabled: disabled, className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600", checked: groupBy.includes(value), onChange: () => toggleGroupBy(value) }) }), _jsxs("div", { className: "ml-3 text-sm leading-6", children: [_jsx("label", { htmlFor: value, className: "font-medium text-gray-900", children: label }), _jsx("p", { className: "text-gray-500", children: description })] })] }, value))) }) }) }) }) })] })] }));
120
120
  };
121
121
  const SortBySelect = ({ sortBy, setSortBy, compareMode, }) => {
122
122
  return (_jsxs("div", { children: [_jsx("label", { className: "text-sm", children: "Sort" }), _jsx(Select, { items: [
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- /*! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.static{position:static}.absolute{position:absolute}.relative{position:relative}.-inset-2{top:-.5rem;right:-.5rem;bottom:-.5rem;left:-.5rem}.inset-y-0{top:0;bottom:0}.left-\[25px\]{left:25px}.left-0{left:0}.top-\[-46px\]{top:-46px}.right-0{right:0}.z-50{z-index:50}.z-10{z-index:10}.m-auto{margin:auto}.m-2{margin:.5rem}.mx-auto{margin-left:auto;margin-right:auto}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-20{margin-top:5rem;margin-bottom:5rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mr-6{margin-right:1.5rem}.mr-1{margin-right:.25rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-fit{height:-moz-fit-content;height:fit-content}.h-10{height:2.5rem}.h-96{height:24rem}.h-6{height:1.5rem}.h-4{height:1rem}.h-full{height:100%}.h-1{height:.25rem}.h-\[80vh\]{height:80vh}.max-h-\[400px\]{max-height:400px}.min-h-52{min-height:13rem}.min-h-48{min-height:12rem}.min-h-\[200px\]{min-height:200px}.w-full{width:100%}.w-auto{width:auto}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-\[500px\]{width:500px}.w-4{width:1rem}.w-40{width:10rem}.w-3{width:.75rem}.w-5{width:1.25rem}.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-8{width:2rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.min-w-\[300px\]{min-width:300px}.min-w-\[400px\]{min-width:400px}.max-w-\[500px\]{max-width:500px}.max-w-\[300px\]{max-width:300px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.flex-grow-0{flex-grow:0}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-0,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-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-wrap{flex-wrap:wrap}.content-start{align-content:flex-start}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-4{gap:1rem}.gap-2{gap:.5rem}.gap-1{gap:.25rem}.gap-3{gap:.75rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.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-none{border-radius:0}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.border{border-width:1px}.border-r{border-right-width:1px}.border-l{border-left-width:1px}.border-r-0{border-right-width:0}.border-l-0{border-left-width:0}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/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))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/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-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-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-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-inherit{background-color:inherit}.fill-transparent{fill:#0000}.fill-current{fill:currentColor}.stroke-gray-300{stroke:#d1d5db}.stroke-white{stroke:#fff}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-1{padding:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-0{padding-top:0;padding-bottom:0}.px-1{padding-left:.25rem;padding-right:.25rem}.pr-0{padding-right:0}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pt-2{padding-top:.5rem}.pb-4{padding-bottom:1rem}.pb-2{padding-bottom:.5rem}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pl-1{padding-left:.25rem}.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}.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-xl{font-size:1.25rem;line-height:1.75rem}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-medium{font-weight:500}.capitalize{text-transform:capitalize}.leading-6{line-height:1.5rem}.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-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-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-100{opacity:1}.opacity-0{opacity:0}.opacity-90{opacity:.9}.opacity-50{opacity:.5}.shadow-\[0_0_10px_2px_rgba\(0\2c 0\2c 0\2c 0\.3\)\]{--tw-shadow:0 0 10px 2px #0000004d;--tw-shadow-colored:0 0 10px 2px 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-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-150{transition-duration:.15s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.focus\: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 #0000;outline-offset:2px}.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);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))}.group:hover .group-hover\:flex{display:flex}[class~=theme-dark] .dark\:border{border-width:1px}[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-r-gray-700{--tw-border-opacity:1;border-right-color:rgb(55 65 81/var(--tw-border-opacity))}[class~=theme-dark] .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}[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-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}[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-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}[class~=theme-dark] .dark\:\!text-indigo-400{--tw-text-opacity:1!important;color:rgb(129 140 248/var(--tw-text-opacity))!important}[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}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}
1
+ /*! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.-inset-2{bottom:-.5rem;left:-.5rem;right:-.5rem;top:-.5rem}.inset-y-0{bottom:0;top:0}.left-\[25px\]{left:25px}.left-0{left:0}.top-\[-46px\]{top:-46px}.right-0{right:0}.bottom-0{bottom:0}.z-50{z-index:50}.z-10{z-index:10}.z-20{z-index:20}.m-auto{margin:auto}.m-2{margin:.5rem}.mx-auto{margin-left:auto;margin-right:auto}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-20{margin-bottom:5rem;margin-top:5rem}.my-6{margin-bottom:1.5rem;margin-top:1.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mr-6{margin-right:1.5rem}.mr-1{margin-right:.25rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-fit{height:-moz-fit-content;height:fit-content}.h-10{height:2.5rem}.h-96{height:24rem}.h-6{height:1.5rem}.h-4{height:1rem}.h-full{height:100%}.h-1{height:.25rem}.h-\[80vh\]{height:80vh}.h-5{height:1.25rem}.max-h-\[400px\]{max-height:400px}.min-h-52{min-height:13rem}.min-h-48{min-height:12rem}.min-h-\[200px\]{min-height:200px}.w-full{width:100%}.w-auto{width:auto}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-\[500px\]{width:500px}.w-4{width:1rem}.w-40{width:10rem}.w-3{width:.75rem}.w-5{width:1.25rem}.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-8{width:2rem}.w-44{width:11rem}.w-\[460px\]{width:460px}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.min-w-\[300px\]{min-width:300px}.min-w-\[400px\]{min-width:400px}.max-w-\[500px\]{max-width:500px}.max-w-\[300px\]{max-width:300px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.flex-grow-0{flex-grow:0}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-0,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-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-wrap{flex-wrap:wrap}.content-start{align-content:flex-start}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-4{gap:1rem}.gap-2{gap:.5rem}.gap-1{gap:.25rem}.gap-3{gap:.75rem}.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)))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.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-none{border-radius:0}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-r{border-right-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-r-0{border-right-width:0}.border-l-0{border-left-width:0}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.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-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))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/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-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-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-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-inherit{background-color:inherit}.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}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-1{padding:.25rem}.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-6{padding-left:1.5rem;padding-right:1.5rem}.py-4{padding-bottom:1rem;padding-top:1rem}.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-0{padding-bottom:0;padding-top:0}.px-1{padding-left:.25rem;padding-right:.25rem}.px-8{padding-left:2rem;padding-right:2rem}.pr-0{padding-right:0}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pt-2{padding-top:.5rem}.pb-4{padding-bottom:1rem}.pb-2{padding-bottom:.5rem}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pl-1{padding-left:.25rem}.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}.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-xl{font-size:1.25rem}.text-lg,.text-xl{line-height:1.75rem}.text-lg{font-size:1.125rem}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.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-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-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-100{opacity:1}.opacity-0{opacity:0}.opacity-90{opacity:.9}.opacity-50{opacity:.5}.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-\[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-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-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-150{transition-duration:.15s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.focus\: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\: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);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))}.group:hover .group-hover\:flex{display:flex}[class~=theme-dark] .dark\:border{border-width:1px}[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-r-gray-700{--tw-border-opacity:1;border-right-color:rgb(55 65 81/var(--tw-border-opacity))}[class~=theme-dark] .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}[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-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/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-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-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}[class~=theme-dark] .dark\:\!text-indigo-400{--tw-text-opacity:1!important;color:rgb(129 140 248/var(--tw-text-opacity))!important}[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}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}