@parca/profile 0.19.95 → 0.19.102

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,34 @@
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.102](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.101...@parca/profile@0.19.102) (2025-12-15)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.19.101](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.100...@parca/profile@0.19.101) (2025-12-15)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
14
+ ## [0.19.100](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.99...@parca/profile@0.19.100) (2025-12-15)
15
+
16
+ **Note:** Version bump only for package @parca/profile
17
+
18
+ ## [0.19.99](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.98...@parca/profile@0.19.99) (2025-12-12)
19
+
20
+ **Note:** Version bump only for package @parca/profile
21
+
22
+ ## [0.19.98](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.97...@parca/profile@0.19.98) (2025-12-12)
23
+
24
+ **Note:** Version bump only for package @parca/profile
25
+
26
+ ## [0.19.97](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.96...@parca/profile@0.19.97) (2025-12-12)
27
+
28
+ **Note:** Version bump only for package @parca/profile
29
+
30
+ ## [0.19.96](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.95...@parca/profile@0.19.96) (2025-12-12)
31
+
32
+ **Note:** Version bump only for package @parca/profile
33
+
6
34
  ## [0.19.95](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.94...@parca/profile@0.19.95) (2025-12-09)
7
35
 
8
36
  **Note:** Version bump only for package @parca/profile
@@ -10,7 +10,7 @@ interface MetricsGraphSectionProps {
10
10
  querySelection: QuerySelection;
11
11
  profileSelection: ProfileSelection | null;
12
12
  comparing: boolean;
13
- sumBy: string[] | null;
13
+ sumBy: string[] | undefined;
14
14
  defaultSumByLoading: boolean;
15
15
  queryClient: QueryServiceClient;
16
16
  queryExpressionString: string;
@@ -1 +1 @@
1
- {"version":3,"file":"MetricsGraphSection.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/MetricsGraphSection.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAQ,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACxD,OAAO,EAAC,aAAa,EAAmB,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAEpC,OAAO,EAAC,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAGpC,OAAO,EAAC,cAAc,EAAC,MAAM,SAAS,CAAC;AAEvC,UAAU,wBAAwB;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gCAAgC,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACvB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,EAAE,kBAAkB,CAAC;IAChC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACtD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAChF,KAAK,EAAE,KAAK,CAAC;IACb,qBAAqB,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,kBAAkB,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,WAAW,EAAE,CACX,kBAAkB,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAC,EACtE,UAAU,CAAC,EAAE,MAAM,KAChB,IAAI,CAAC;CACX;AAED,wBAAgB,mBAAmB,CAAC,EAClC,gBAAgB,EAChB,gCAAgC,EAChC,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,KAAK,EACL,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,mBAAmB,EACnB,KAAK,EACL,qBAAqB,EACrB,WAAW,GACZ,EAAE,wBAAwB,GAAG,GAAG,CAAC,OAAO,CA+HxC"}
1
+ {"version":3,"file":"MetricsGraphSection.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/MetricsGraphSection.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAQ,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACxD,OAAO,EAAC,aAAa,EAAmB,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAEpC,OAAO,EAAC,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAGpC,OAAO,EAAC,cAAc,EAAC,MAAM,SAAS,CAAC;AAEvC,UAAU,wBAAwB;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gCAAgC,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,EAAE,kBAAkB,CAAC;IAChC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACtD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAChF,KAAK,EAAE,KAAK,CAAC;IACb,qBAAqB,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,kBAAkB,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,WAAW,EAAE,CACX,kBAAkB,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAC,EACtE,UAAU,CAAC,EAAE,MAAM,KAChB,IAAI,CAAC;CACX;AAED,wBAAgB,mBAAmB,CAAC,EAClC,gBAAgB,EAChB,gCAAgC,EAChC,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,KAAK,EACL,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,mBAAmB,EACnB,KAAK,EACL,qBAAqB,EACrB,WAAW,GACZ,EAAE,wBAAwB,GAAG,GAAG,CAAC,OAAO,CA+HxC"}
@@ -84,5 +84,5 @@ export function MetricsGraphSection({ showMetricsGraph, setDisplayHideMetricsGra
84
84
  };
85
85
  return (_jsxs("div", { className: cx('relative', { 'py-4': !showMetricsGraph }), children: [setDisplayHideMetricsGraphButton != null ? (_jsxs("button", { onClick: () => setDisplayHideMetricsGraphButton(!showMetricsGraph), className: cx('hidden px-3 py-1 text-sm font-medium text-gray-700 dark:text-gray-200 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-gray-900 z-[5]', showMetricsGraph && 'absolute right-0 bottom-3 !flex', !showMetricsGraph && 'relative !flex ml-auto'), children: [showMetricsGraph ? 'Hide' : 'Show', " Metrics Graph"] })) : null, showMetricsGraph && (_jsx(_Fragment, { children: _jsx("div", { style: { height: heightStyle }, children: (querySelection.expression !== '' || defaultSumByLoading) &&
86
86
  querySelection.from !== undefined &&
87
- querySelection.to !== undefined ? (_jsx(_Fragment, { children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? sumBy ?? [], sumByLoading: defaultSumByLoading, setTimeRange: handleTimeRangeChange, addLabelMatcher: addLabelMatcher, onPointClick: handlePointClick }) })) : (profileSelection === null && (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsEmptyState, { message: "Please select a profile type and click 'Search' to begin." }) }))) }) }))] }));
87
+ querySelection.to !== undefined ? (_jsx(_Fragment, { children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, sumBy: sumBy ?? [], sumByLoading: defaultSumByLoading, setTimeRange: handleTimeRangeChange, addLabelMatcher: addLabelMatcher, onPointClick: handlePointClick }) })) : (profileSelection === null && (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsEmptyState, { message: "Please select a profile type and click 'Search' to begin." }) }))) }) }))] }));
88
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,QAAQ,EAAE,cAAc,EAAoD,MAAM,OAAO,CAAC;AAElG,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAsB,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAY5F,OAAO,EAAyB,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAY/E,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,uBAAuB;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,UAAU,oBAAqB,SAAQ,uBAAuB;IAC5D,WAAW,EAAE,kBAAkB,CAAC;IAChC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,gCAAgC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,GAC1B,QAAQ,kBAAkB,EAC1B,QAAQ,MAAM,EACd,MAAM,MAAM,KACX,mBAsBF,CAAC;AAEF,QAAA,MAAM,eAAe,GAAI,kMAYtB,oBAAoB,KAAG,GAAG,CAAC,OAyO7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,QAAQ,EAAE,cAAc,EAAoD,MAAM,OAAO,CAAC;AAElG,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAsB,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAY5F,OAAO,EAAyB,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAY/E,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,uBAAuB;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,UAAU,oBAAqB,SAAQ,uBAAuB;IAC5D,WAAW,EAAE,kBAAkB,CAAC;IAChC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,gCAAgC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,GAC1B,QAAQ,kBAAkB,EAC1B,QAAQ,MAAM,EACd,MAAM,MAAM,KACX,mBAsBF,CAAC;AAEF,QAAA,MAAM,eAAe,GAAI,kMAYtB,oBAAoB,KAAG,GAAG,CAAC,OAqO7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -101,7 +101,6 @@ const ProfileSelector = ({ queryClient, closeProfile, enforcedProfileName, compa
101
101
  const currentTo = timeRangeSelection.getToMs(true);
102
102
  const currentRangeKey = timeRangeSelection.getRangeKey();
103
103
  // Commit with refreshed time range
104
- console.log('[draftExpression] setQueryExpression: committing with refreshed time range:', draftSelection.expression);
105
104
  commitDraft({
106
105
  from: currentFrom,
107
106
  to: currentTo,
@@ -150,7 +149,7 @@ const ProfileSelector = ({ queryClient, closeProfile, enforcedProfileName, compa
150
149
  queryExpressionString === '{}';
151
150
  const queryBrowserRef = useRef(null);
152
151
  const sumByRef = useRef(null);
153
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex", children: [_jsx(LabelsQueryProvider, { setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName ?? profileType.toString(), queryClient: queryClient, start: timeRangeSelection.getFromMs(), end: timeRangeSelection.getToMs(), suffix: suffix, children: _jsx(LabelsSource, { children: _jsx(QueryControls, { showProfileTypeSelector: showProfileTypeSelector, showSumBySelector: showSumBySelector, profileTypesData: profileTypesData, profileTypesLoading: profileTypesLoading, selectedProfileName: selectedProfileName, setProfileName: setProfileName, setMatchersString: setMatchersString, setQueryExpression: setQueryExpression, query: query, queryBrowserRef: queryBrowserRef, timeRangeSelection: timeRangeSelection, setTimeRangeSelection: handleTimeRangeChange, searchDisabled: searchDisabled, setQueryBrowserMode: setQueryBrowserMode, advancedModeForQueryBrowser: advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser: setAdvancedModeForQueryBrowser, queryClient: queryClient, sumByRef: sumByRef, labels: labels, sumBySelection: draftSelection.sumBy ?? [], sumBySelectionLoading: sumByLoading, setUserSumBySelection: setDraftSumBy, profileType: profileType, profileTypesError: error, viewComponent: viewComponent, draftSelection: draftSelection, setDraftMatchers: setDraftMatchers, draftParsedQuery: draftParsedQuery, commitDraft: commitDraft }) }) }), comparing && (_jsx("div", { children: _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}), ...testId(TEST_IDS.COMPARE_CLOSE_BUTTON) }) }))] }), _jsx(MetricsGraphSection, { showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setDisplayHideMetricsGraphButton, heightStyle: heightStyle, querySelection: querySelection, profileSelection: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? [], defaultSumByLoading: sumByLoading, queryClient: queryClient, queryExpressionString: queryExpressionString, setTimeRangeSelection: handleTimeRangeChange, selectQuery: commitDraft, setProfileSelection: setProfileSelection, query: query, setQueryExpression: setQueryExpression, setNewQueryExpression: setDraftExpression, commitDraft: commitDraft })] }));
152
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex", children: [_jsx(LabelsQueryProvider, { setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName ?? profileType.toString(), queryClient: queryClient, start: timeRangeSelection.getFromMs(), end: timeRangeSelection.getToMs(), suffix: suffix, children: _jsx(LabelsSource, { children: _jsx(QueryControls, { showProfileTypeSelector: showProfileTypeSelector, showSumBySelector: showSumBySelector, profileTypesData: profileTypesData, profileTypesLoading: profileTypesLoading, selectedProfileName: selectedProfileName, setProfileName: setProfileName, setMatchersString: setMatchersString, setQueryExpression: setQueryExpression, query: query, queryBrowserRef: queryBrowserRef, timeRangeSelection: timeRangeSelection, setTimeRangeSelection: handleTimeRangeChange, searchDisabled: searchDisabled, setQueryBrowserMode: setQueryBrowserMode, advancedModeForQueryBrowser: advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser: setAdvancedModeForQueryBrowser, queryClient: queryClient, sumByRef: sumByRef, labels: labels, sumBySelection: draftSelection.sumBy ?? [], sumBySelectionLoading: sumByLoading, setUserSumBySelection: setDraftSumBy, profileType: profileType, profileTypesError: error, viewComponent: viewComponent, draftSelection: draftSelection, setDraftMatchers: setDraftMatchers, draftParsedQuery: draftParsedQuery, commitDraft: commitDraft }) }) }), comparing && (_jsx("div", { children: _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}), ...testId(TEST_IDS.COMPARE_CLOSE_BUTTON) }) }))] }), _jsx(MetricsGraphSection, { showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setDisplayHideMetricsGraphButton, heightStyle: heightStyle, querySelection: querySelection, profileSelection: profileSelection, comparing: comparing, sumBy: querySelection.sumBy, defaultSumByLoading: sumByLoading, queryClient: queryClient, queryExpressionString: queryExpressionString, setTimeRangeSelection: handleTimeRangeChange, selectQuery: commitDraft, setProfileSelection: setProfileSelection, query: query, setQueryExpression: setQueryExpression, setNewQueryExpression: setDraftExpression, commitDraft: commitDraft })] }));
154
153
  };
155
154
  export default ProfileSelector;
156
155
  const LabelsSource = ({ children }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"useResetStateOnProfileTypeChange.d.ts","sourceRoot":"","sources":["../../../src/ProfileView/hooks/useResetStateOnProfileTypeChange.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,gCAAgC,QAAO,CAAC,MAAM,IAAI,CAuB9D,CAAC"}
1
+ {"version":3,"file":"useResetStateOnProfileTypeChange.d.ts","sourceRoot":"","sources":["../../../src/ProfileView/hooks/useResetStateOnProfileTypeChange.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,gCAAgC,QAAO,CAAC,MAAM,IAAI,CA+B9D,CAAC"}
@@ -15,6 +15,8 @@ import { useProfileFilters } from '../components/ProfileFilters/useProfileFilter
15
15
  export const useResetStateOnProfileTypeChange = () => {
16
16
  const [groupBy, setGroupBy] = useURLState('group_by');
17
17
  const [curPath, setCurPath] = useURLState('cur_path');
18
+ const [sumByA, setSumByA] = useURLState('sum_by_a');
19
+ const [sumByB, setSumByB] = useURLState('sum_by_b');
18
20
  const { resetFilters } = useProfileFilters();
19
21
  const [sandwichFunctionName, setSandwichFunctionName] = useURLState('sandwich_function_name');
20
22
  const batchUpdates = useURLStateBatch();
@@ -30,6 +32,12 @@ export const useResetStateOnProfileTypeChange = () => {
30
32
  if (sandwichFunctionName !== undefined) {
31
33
  setSandwichFunctionName(undefined);
32
34
  }
35
+ if (sumByA !== undefined) {
36
+ setSumByA(undefined);
37
+ }
38
+ if (sumByB !== undefined) {
39
+ setSumByB(undefined);
40
+ }
33
41
  resetFilters();
34
42
  });
35
43
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/Select.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAStE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC;IACpB,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,UAAU,iBAAiB;IACzB,KAAK,EAAE,iBAAiB,EAAE,GAAG,UAAU,EAAE,CAAC;IAC1C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAwUnE,CAAC;AAkDF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/Select.tsx"],"names":[],"mappings":"AAaA,OAAO,KAA2D,MAAM,OAAO,CAAC;AAShF,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC;IACpB,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,UAAU,iBAAiB;IACzB,KAAK,EAAE,iBAAiB,EAAE,GAAG,UAAU,EAAE,CAAC;IAC1C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAwUnE,CAAC;AAkDF,eAAe,YAAY,CAAC"}
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- import { useCallback, useEffect, useRef, useState } from 'react';
14
+ import { Fragment, useCallback, useEffect, useRef, useState } from 'react';
15
15
  import { Icon } from '@iconify/react';
16
16
  import cx from 'classnames';
17
17
  import levenshtein from 'fast-levenshtein';
@@ -177,9 +177,9 @@ const CustomSelect = ({ items: itemsProp, selectedKey, onSelection, placeholder
177
177
  return (_jsxs("div", { ref: containerRef, className: "relative", onKeyDown: handleKeyDown, onClick: onButtonClick, children: [_jsxs("div", { id: id, onClick: () => !disabled && setIsOpen(!isOpen), className: cx(styles, width !== undefined ? `w-${width}` : 'w-full', disabled ? 'cursor-not-allowed opacity-50 pointer-events-none' : '', primary ? primaryStyles : defaultStyles, { [className]: className.length > 0 }), tabIndex: 0, role: "button", "aria-haspopup": "listbox", "aria-expanded": isOpen, ...restProps, children: [_jsx("div", { className: cx(icon != null ? '' : 'block overflow-x-hidden text-ellipsis whitespace-nowrap'), children: renderSelection(selection) }), _jsx("div", { className: cx(icon != null ? '' : 'pointer-events-none text-gray-400'), children: icon ?? _jsx(Icon, { icon: "heroicons:chevron-up-down-20-solid", "aria-hidden": "true" }) })] }), isOpen && (_jsx("div", { ref: optionsRef, className: cx('absolute z-50 mt-1 max-h-[50vh] w-max overflow-auto rounded-md bg-gray-50 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', { [optionsClassname]: optionsClassname.length > 0 }), role: "listbox", children: _jsxs("div", { className: "relative flex flex-col", children: [searchable && (_jsx("div", { className: "sticky z-10 top-[-5px] w-auto max-w-full", children: _jsx("div", { className: "flex flex-col", children: editable ? (_jsxs(_Fragment, { children: [_jsx("textarea", { ref: searchInputRef, className: "w-full px-4 py-2 text-sm border-b border-gray-200 rounded-none ring-0 outline-none bg-gray-50 dark:bg-gray-800 dark:text-white min-h-[50px]", placeholder: "Type a RegEx to add", value: searchTerm, onChange: e => setSearchTerm(e.target.value), onFocus: e => moveCaretToEnd(e) }), editable && searchTerm.length > 0 && (_jsx("div", { className: "p-2 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800", children: _jsx(Button, { variant: "primary", className: "w-full h-[30px]", onClick: () => {
178
178
  onSelection(searchTerm);
179
179
  setIsOpen(false);
180
- }, children: "Add" }) }))] })) : (_jsx("input", { ref: searchInputRef, type: "text", className: "w-full px-4 h-[45px] text-sm border-none rounded-none ring-0 outline-none bg-gray-50 dark:bg-gray-800 dark:text-white", placeholder: "Search...", value: searchTerm, onChange: e => setSearchTerm(e.target.value) })) }) })), _jsx("div", { className: "flex-1 min-h-0", children: loading === true ? (_jsx("div", { className: "w-[270px]", children: loader })) : groupedFilteredItems.length === 0 ? (_jsx("div", { className: "px-4 py-3 text-sm text-gray-500 dark:text-gray-400 text-center", ...testId(TEST_IDS.LABEL_VALUE_NO_RESULTS), children: "No values found" })) : (groupedFilteredItems.map(group => (_jsxs(_Fragment, { children: [groupedFilteredItems.length > 1 &&
180
+ }, children: "Add" }) }))] })) : (_jsx("input", { ref: searchInputRef, type: "text", className: "w-full px-4 h-[45px] text-sm border-none rounded-none ring-0 outline-none bg-gray-50 dark:bg-gray-800 dark:text-white", placeholder: "Search...", value: searchTerm, onChange: e => setSearchTerm(e.target.value) })) }) })), _jsx("div", { className: "flex-1 min-h-0", children: loading === true ? (_jsx("div", { className: "w-[270px]", children: loader })) : groupedFilteredItems.length === 0 ? (_jsx("div", { className: "px-4 py-3 text-sm text-gray-500 dark:text-gray-400 text-center", ...testId(TEST_IDS.LABEL_VALUE_NO_RESULTS), children: "No values found" })) : (groupedFilteredItems.map(group => (_jsxs(Fragment, { children: [groupedFilteredItems.length > 1 &&
181
181
  groupedFilteredItems.every(g => g.type !== '') &&
182
- group.type !== '' ? (_jsx("div", { className: "pl-2", children: _jsx(DividerWithLabel, { label: group.type }) })) : null, group.values.map((item, index) => (_jsx(OptionItem, { item: item, index: index, optionRefs: optionRefs, focusedIndex: focusedIndex, selectedKey: selectedKey, handleSelection: handleSelection }, item.key)))] })))) }), refetchValues !== undefined && loading !== true && (_jsx(RefreshButton, { onClick: () => void handleRefetch(), disabled: isRefetching, title: "Refresh results", testId: TEST_IDS.LABEL_VALUE_REFRESH_BUTTON, sticky: true, loading: isRefetching }))] }) }))] }));
182
+ group.type !== '' ? (_jsx("div", { className: "pl-2", children: _jsx(DividerWithLabel, { label: group.type }) })) : null, group.values.map((item, index) => (_jsx(OptionItem, { item: item, index: index, optionRefs: optionRefs, focusedIndex: focusedIndex, selectedKey: selectedKey, handleSelection: handleSelection }, item.key)))] }, group.type)))) }), refetchValues !== undefined && loading !== true && (_jsx(RefreshButton, { onClick: () => void handleRefetch(), disabled: isRefetching, title: "Refresh results", testId: TEST_IDS.LABEL_VALUE_REFRESH_BUTTON, sticky: true, loading: isRefetching }))] }) }))] }));
183
183
  };
184
184
  const OptionItem = ({ item, optionRefs, index, focusedIndex, selectedKey, handleSelection, }) => {
185
185
  return (_jsxs("div", { ref: el => {
@@ -1 +1 @@
1
- {"version":3,"file":"useLabels.d.ts","sourceRoot":"","sources":["../../src/hooks/useLabels.ts"],"names":[],"mappings":"AAaA,OAAO,EAAgB,cAAc,EAAE,kBAAkB,EAAgB,MAAM,eAAe,CAAC;AAM/F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,aAAa;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,eAAO,MAAM,aAAa,GACxB,QAAQ,kBAAkB,EAC1B,aAAa,MAAM,EACnB,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,QAAQ,MAAM,EAAE,KACf,aAgCF,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,QAAQ,kBAAkB,EAC1B,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,QAAQ,MAAM,EACd,MAAM,MAAM,KACX,cAiCF,CAAC"}
1
+ {"version":3,"file":"useLabels.d.ts","sourceRoot":"","sources":["../../src/hooks/useLabels.ts"],"names":[],"mappings":"AAeA,OAAO,EAAgB,cAAc,EAAE,kBAAkB,EAAgB,MAAM,eAAe,CAAC;AAM/F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,aAAa;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,eAAO,MAAM,aAAa,GACxB,QAAQ,kBAAkB,EAC1B,aAAa,MAAM,EACnB,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,QAAQ,MAAM,EAAE,KACf,aAkCF,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,QAAQ,kBAAkB,EAC1B,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,QAAQ,MAAM,EACd,MAAM,MAAM,KACX,cAiCF,CAAC"}
@@ -10,6 +10,7 @@
10
10
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
+ import { useEffect } from 'react';
13
14
  import { useGrpcMetadata } from '@parca/components';
14
15
  import { millisToProtoTimestamp, sanitizeLabelValue } from '@parca/utilities';
15
16
  import useGrpcQuery from '../useGrpcQuery';
@@ -34,7 +35,9 @@ export const useLabelNames = (client, profileType, start, end, match) => {
34
35
  keepPreviousData: false,
35
36
  },
36
37
  });
37
- console.log('Label names query result:', { data, error, isLoading });
38
+ useEffect(() => {
39
+ console.log('Label names query result:', { data, error, isLoading });
40
+ }, [data, error, isLoading]);
38
41
  return {
39
42
  result: { response: data, error: error },
40
43
  loading: isLoading,
@@ -1 +1 @@
1
- {"version":3,"file":"useQueryState.d.ts","sourceRoot":"","sources":["../../src/hooks/useQueryState.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAEpC,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAC,gBAAgB,EAA8B,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAI7F,UAAU,oBAAoB;IAC5B,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,mBAAmB;IAE3B,cAAc,EAAE,cAAc,CAAC;IAG/B,cAAc,EAAE,cAAc,CAAC;IAG/B,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7E,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,KAAK,IAAI,CAAC;IACrD,mBAAmB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAG7C,WAAW,EAAE,CAAC,kBAAkB,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAC,KAAK,IAAI,CAAC;IAG9F,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAG1C,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IAGpC,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAGhF,YAAY,EAAE,OAAO,CAAC;IAGtB,gBAAgB,EAAE,KAAK,GAAG,IAAI,CAAC;IAG/B,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;CAC3B;AAED,eAAO,MAAM,aAAa,GAAI,UAAS,oBAAyB,KAAG,mBAwWlE,CAAC"}
1
+ {"version":3,"file":"useQueryState.d.ts","sourceRoot":"","sources":["../../src/hooks/useQueryState.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAEpC,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAC,gBAAgB,EAA8B,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAK7F,UAAU,oBAAoB;IAC5B,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,mBAAmB;IAE3B,cAAc,EAAE,cAAc,CAAC;IAG/B,cAAc,EAAE,cAAc,CAAC;IAG/B,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7E,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,KAAK,IAAI,CAAC;IACrD,mBAAmB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAG7C,WAAW,EAAE,CAAC,kBAAkB,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAC,KAAK,IAAI,CAAC;IAG9F,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAG1C,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IAGpC,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAGhF,YAAY,EAAE,OAAO,CAAC;IAGtB,gBAAgB,EAAE,KAAK,GAAG,IAAI,CAAC;IAG/B,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;CAC3B;AAED,eAAO,MAAM,aAAa,GAAI,UAAS,oBAAyB,KAAG,mBAgZlE,CAAC"}
@@ -15,13 +15,15 @@ import { DateTimeRange, useParcaContext, useURLState, useURLStateBatch } from '@
15
15
  import { Query } from '@parca/parser';
16
16
  import { ProfileSelectionFromParams } from '../ProfileSource';
17
17
  import { useResetFlameGraphState } from '../ProfileView/hooks/useResetFlameGraphState';
18
- import { sumByToParam, useSumBy, useSumByFromParams } from '../useSumBy';
18
+ import { useResetStateOnProfileTypeChange } from '../ProfileView/hooks/useResetStateOnProfileTypeChange';
19
+ import { DEFAULT_EMPTY_SUM_BY, sumByToParam, useSumBy, useSumByFromParams } from '../useSumBy';
19
20
  export const useQueryState = (options = {}) => {
20
21
  const { queryServiceClient: queryClient } = useParcaContext();
21
22
  const { suffix = '', defaultExpression = '', defaultTimeSelection = 'relative:minute|15', // Default to 15 minutes relative
22
23
  defaultFrom, defaultTo, comparing = false, } = options;
23
24
  const batchUpdates = useURLStateBatch();
24
25
  const resetFlameGraphState = useResetFlameGraphState();
26
+ const resetStateOnProfileTypeChange = useResetStateOnProfileTypeChange();
25
27
  // URL state hooks with appropriate suffixes
26
28
  const [expression, setExpressionState] = useURLState(`expression${suffix}`, {
27
29
  defaultValue: defaultExpression,
@@ -47,23 +49,6 @@ export const useQueryState = (options = {}) => {
47
49
  const [draftFrom, setDraftFrom] = useState(from ?? defaultFrom?.toString() ?? '');
48
50
  const [draftTo, setDraftTo] = useState(to ?? defaultTo?.toString() ?? '');
49
51
  const [draftTimeSelection, setDraftTimeSelection] = useState(timeSelection ?? defaultTimeSelection);
50
- const [draftSumBy, setDraftSumBy] = useState(sumBy);
51
- // Sync draft state with URL state when URL changes externally
52
- useEffect(() => {
53
- setDraftExpression(expression ?? defaultExpression);
54
- }, [expression, defaultExpression]);
55
- useEffect(() => {
56
- setDraftFrom(from ?? defaultFrom?.toString() ?? '');
57
- }, [from, defaultFrom]);
58
- useEffect(() => {
59
- setDraftTo(to ?? defaultTo?.toString() ?? '');
60
- }, [to, defaultTo]);
61
- useEffect(() => {
62
- setDraftTimeSelection(timeSelection ?? defaultTimeSelection);
63
- }, [timeSelection, defaultTimeSelection]);
64
- useEffect(() => {
65
- setDraftSumBy(sumBy);
66
- }, [sumBy]);
67
52
  // Parse the draft query to extract profile information
68
53
  const draftQuery = useMemo(() => {
69
54
  try {
@@ -73,14 +58,46 @@ export const useQueryState = (options = {}) => {
73
58
  return Query.parse('');
74
59
  }
75
60
  }, [draftExpression]);
61
+ const query = useMemo(() => {
62
+ try {
63
+ return Query.parse(expression ?? '');
64
+ }
65
+ catch {
66
+ return Query.parse('');
67
+ }
68
+ }, [expression]);
76
69
  const draftProfileType = useMemo(() => draftQuery.profileType(), [draftQuery]);
77
70
  const draftProfileName = useMemo(() => draftQuery.profileName(), [draftQuery]);
71
+ const profileType = useMemo(() => query.profileType(), [query]);
78
72
  // Compute draft time range for label fetching
79
73
  const draftTimeRange = useMemo(() => {
80
74
  return DateTimeRange.fromRangeKey(draftTimeSelection ?? defaultTimeSelection, draftFrom !== '' ? parseInt(draftFrom) : defaultFrom, draftTo !== '' ? parseInt(draftTo) : defaultTo);
81
75
  }, [draftTimeSelection, draftFrom, draftTo, defaultTimeSelection, defaultFrom, defaultTo]);
82
76
  // Use combined sumBy hook for fetching labels and computing defaults (based on committed state)
83
- const { sumBy: computedSumByFromURL, isLoading: sumBySelectionLoading } = useSumBy(queryClient, draftProfileType, draftTimeRange, sumBy);
77
+ const { sumBy: computedSumByFromURL, isLoading: sumBySelectionLoading, draftSumBy, setDraftSumBy, isDraftSumByLoading, } = useSumBy(queryClient, profileType?.profileName !== '' ? profileType : draftProfileType, draftTimeRange, draftProfileType, draftTimeRange, sumBy);
78
+ // Sync draft state with URL state when URL changes externally
79
+ useEffect(() => {
80
+ setDraftExpression(expression ?? defaultExpression);
81
+ }, [expression, defaultExpression]);
82
+ useEffect(() => {
83
+ setDraftFrom(from ?? defaultFrom?.toString() ?? '');
84
+ }, [from, defaultFrom]);
85
+ useEffect(() => {
86
+ setDraftTo(to ?? defaultTo?.toString() ?? '');
87
+ }, [to, defaultTo]);
88
+ useEffect(() => {
89
+ setDraftTimeSelection(timeSelection ?? defaultTimeSelection);
90
+ }, [timeSelection, defaultTimeSelection]);
91
+ useEffect(() => {
92
+ setDraftSumBy(sumBy);
93
+ }, [sumBy, setDraftSumBy]);
94
+ // Sync computed sumBy to URL if URL doesn't already have a value
95
+ // to ensure the shared URL can always pick it up.
96
+ useEffect(() => {
97
+ if (sumByParam === undefined && computedSumByFromURL !== undefined && !sumBySelectionLoading) {
98
+ setSumByParam(sumByToParam(computedSumByFromURL));
99
+ }
100
+ }, [sumByParam, computedSumByFromURL, sumBySelectionLoading, setSumByParam]);
84
101
  // Construct the QuerySelection object (committed state from URL)
85
102
  const querySelection = useMemo(() => {
86
103
  const range = DateTimeRange.fromRangeKey(timeSelection ?? defaultTimeSelection, from !== undefined && from !== '' ? parseInt(from) : defaultFrom, to !== undefined && to !== '' ? parseInt(to) : defaultTo);
@@ -180,11 +197,16 @@ export const useQueryState = (options = {}) => {
180
197
  setFromState(finalFrom);
181
198
  setToState(finalTo);
182
199
  setTimeSelectionState(finalTimeSelection);
183
- setSumByParam(sumByToParam(draftSumBy));
184
200
  // Auto-calculate merge parameters for delta profiles
185
201
  // Parse the final expression to check if it's a delta profile
186
202
  const finalQuery = Query.parse(finalExpression);
187
203
  const isDelta = finalQuery.profileType().delta;
204
+ if (isDelta) {
205
+ setSumByParam(sumByToParam(draftSumBy));
206
+ }
207
+ else {
208
+ setSumByParam(DEFAULT_EMPTY_SUM_BY);
209
+ }
188
210
  if (isDelta && finalFrom !== '' && finalTo !== '') {
189
211
  const fromMs = parseInt(finalFrom);
190
212
  const toMs = parseInt(finalTo);
@@ -207,6 +229,10 @@ export const useQueryState = (options = {}) => {
207
229
  setSelectionParam(undefined);
208
230
  }
209
231
  resetFlameGraphState();
232
+ if (draftProfileType.toString() !==
233
+ Query.parse(querySelection.expression).profileType().toString()) {
234
+ resetStateOnProfileTypeChange();
235
+ }
210
236
  });
211
237
  }, [
212
238
  batchUpdates,
@@ -227,6 +253,9 @@ export const useQueryState = (options = {}) => {
227
253
  setMergeToState,
228
254
  setSelectionParam,
229
255
  resetFlameGraphState,
256
+ resetStateOnProfileTypeChange,
257
+ draftProfileType,
258
+ querySelection.expression,
230
259
  ]);
231
260
  const setDraftTimeRange = useCallback((newFrom, newTo, newTimeSelection) => {
232
261
  setDraftFrom(newFrom.toString());
@@ -235,15 +264,16 @@ export const useQueryState = (options = {}) => {
235
264
  }, []);
236
265
  const setDraftSumByCallback = useCallback((newSumBy) => {
237
266
  setDraftSumBy(newSumBy);
238
- }, []);
267
+ }, [setDraftSumBy]);
239
268
  const setDraftProfileName = useCallback((newProfileName) => {
240
269
  if (newProfileName === '')
241
270
  return;
242
271
  const [newQuery, changed] = draftQuery.setProfileName(newProfileName);
243
272
  if (changed) {
244
273
  setDraftExpression(newQuery.toString());
274
+ setDraftSumBy(undefined);
245
275
  }
246
- }, [draftQuery]);
276
+ }, [draftQuery, setDraftSumBy]);
247
277
  const setDraftMatchers = useCallback((matchers) => {
248
278
  const newExpression = `${draftProfileName}{${matchers}}`;
249
279
  setDraftExpression(newExpression);
@@ -290,7 +320,7 @@ export const useQueryState = (options = {}) => {
290
320
  profileSource,
291
321
  setProfileSelection,
292
322
  // Loading state
293
- sumByLoading: sumBySelectionLoading,
323
+ sumByLoading: isDraftSumByLoading || sumBySelectionLoading,
294
324
  draftParsedQuery,
295
325
  parsedQuery,
296
326
  };
@@ -52,16 +52,24 @@ vi.mock('@parca/components/src/hooks/URLState/utils', async () => {
52
52
  },
53
53
  };
54
54
  });
55
- // Mock useSumBy to return the sumBy from URL params or undefined
55
+ // Mock useSumBy with stateful behavior using React's useState
56
56
  vi.mock('../useSumBy', async () => {
57
57
  const actual = await vi.importActual('../useSumBy');
58
+ const react = await import('react');
58
59
  return {
59
60
  ...actual,
60
- useSumBy: (_queryClient, _profileType, _timeRange, defaultValue) => ({
61
- sumBy: defaultValue,
62
- setSumBy: vi.fn(),
63
- isLoading: false,
64
- }),
61
+ useSumBy: (_queryClient, _profileType, _timeRange, _draftProfileType, _draftTimeRange, defaultValue) => {
62
+ const [draftSumBy, setDraftSumBy] = react.useState(defaultValue);
63
+ const [sumBy, setSumBy] = react.useState(defaultValue);
64
+ return {
65
+ sumBy,
66
+ setSumBy,
67
+ isLoading: false,
68
+ draftSumBy,
69
+ setDraftSumBy,
70
+ isDraftSumByLoading: false,
71
+ };
72
+ },
65
73
  };
66
74
  });
67
75
  // Helper to create wrapper with URLStateProvider
@@ -154,7 +162,9 @@ describe('useQueryState', () => {
154
162
  });
155
163
  it('should update sumBy', async () => {
156
164
  const { result } = renderHook(() => useQueryState(), { wrapper: createWrapper() });
165
+ // sumBy only applies to delta profiles, so we need to set one first
157
166
  act(() => {
167
+ result.current.setDraftExpression('process_cpu:cpu:nanoseconds:cpu:nanoseconds:delta{}');
158
168
  result.current.setDraftSumBy(['namespace', 'container']);
159
169
  });
160
170
  // Draft should be updated
@@ -193,13 +203,13 @@ describe('useQueryState', () => {
193
203
  it('should batch multiple updates into single navigation', async () => {
194
204
  const { result } = renderHook(() => useQueryState(), { wrapper: createWrapper() });
195
205
  act(() => {
196
- // Update multiple draft values
197
- result.current.setDraftExpression('memory:inuse_space:bytes:space:bytes{}');
206
+ // Update multiple draft values (using delta profile since sumBy only applies to delta)
207
+ result.current.setDraftExpression('memory:alloc_space:bytes:space:bytes:delta{}');
198
208
  result.current.setDraftTimeRange(7000, 8000, 'relative:minute|30');
199
209
  result.current.setDraftSumBy(['pod', 'node']);
200
210
  });
201
211
  // All drafts should be updated
202
- expect(result.current.draftSelection.expression).toBe('memory:inuse_space:bytes:space:bytes{}');
212
+ expect(result.current.draftSelection.expression).toBe('memory:alloc_space:bytes:space:bytes:delta{}');
203
213
  expect(result.current.draftSelection.from).toBe(7000);
204
214
  expect(result.current.draftSelection.to).toBe(8000);
205
215
  expect(result.current.draftSelection.sumBy).toEqual(['pod', 'node']);
@@ -210,7 +220,7 @@ describe('useQueryState', () => {
210
220
  // Should only navigate once for all updates
211
221
  expect(mockNavigateTo).toHaveBeenCalledTimes(1);
212
222
  const [, params] = mockNavigateTo.mock.calls[0];
213
- expect(params.expression).toBe('memory:inuse_space:bytes:space:bytes{}');
223
+ expect(params.expression).toBe('memory:alloc_space:bytes:space:bytes:delta{}');
214
224
  expect(params.from).toBe('7000');
215
225
  expect(params.to).toBe('8000');
216
226
  expect(params.time_selection).toBe('relative:minute|30');
@@ -325,9 +335,9 @@ describe('useQueryState', () => {
325
335
  });
326
336
  it('should handle _b suffix correctly', async () => {
327
337
  const { result } = renderHook(() => useQueryState({ suffix: '_b' }), { wrapper: createWrapper() });
328
- // Update draft state
338
+ // Update draft state (using delta profile since sumBy only applies to delta)
329
339
  act(() => {
330
- result.current.setDraftExpression('memory:inuse_space:bytes:space:bytes{}');
340
+ result.current.setDraftExpression('memory:alloc_space:bytes:space:bytes:delta{}');
331
341
  result.current.setDraftTimeRange(3333, 4444, 'relative:hour|2');
332
342
  result.current.setDraftSumBy(['label_b']);
333
343
  });
@@ -338,7 +348,7 @@ describe('useQueryState', () => {
338
348
  await waitFor(() => {
339
349
  expect(mockNavigateTo).toHaveBeenCalled();
340
350
  const [, params] = mockNavigateTo.mock.calls[mockNavigateTo.mock.calls.length - 1];
341
- expect(params.expression_b).toBe('memory:inuse_space:bytes:space:bytes{}');
351
+ expect(params.expression_b).toBe('memory:alloc_space:bytes:space:bytes:delta{}');
342
352
  expect(params.from_b).toBe('3333');
343
353
  expect(params.to_b).toBe('4444');
344
354
  expect(params.sum_by_b).toBe('label_b');
@@ -348,9 +358,9 @@ describe('useQueryState', () => {
348
358
  describe('Draft state pattern', () => {
349
359
  it('should not update URL until commit', async () => {
350
360
  const { result } = renderHook(() => useQueryState(), { wrapper: createWrapper() });
351
- // Make multiple draft changes
361
+ // Make multiple draft changes (using delta profile since sumBy only applies to delta)
352
362
  act(() => {
353
- result.current.setDraftExpression('memory:inuse_space:bytes:space:bytes{}');
363
+ result.current.setDraftExpression('memory:alloc_space:bytes:space:bytes:delta{}');
354
364
  result.current.setDraftTimeRange(5000, 6000, 'relative:hour|3');
355
365
  result.current.setDraftSumBy(['namespace', 'pod']);
356
366
  });
@@ -364,7 +374,7 @@ describe('useQueryState', () => {
364
374
  await waitFor(() => {
365
375
  expect(mockNavigateTo).toHaveBeenCalledTimes(1);
366
376
  const [, params] = mockNavigateTo.mock.calls[0];
367
- expect(params.expression).toBe('memory:inuse_space:bytes:space:bytes{}');
377
+ expect(params.expression).toBe('memory:alloc_space:bytes:space:bytes:delta{}');
368
378
  expect(params.from).toBe('5000');
369
379
  expect(params.to).toBe('6000');
370
380
  expect(params.sum_by).toBe('namespace,pod');
@@ -563,15 +573,15 @@ describe('useQueryState', () => {
563
573
  });
564
574
  describe('State persistence after page reload', () => {
565
575
  it('should retain committed values after page reload simulation', async () => {
566
- // Initial state
576
+ // Initial state (using delta profile since sumBy only applies to delta)
567
577
  mockLocation.search =
568
- '?expression=process_cpu:cpu:nanoseconds:cpu:nanoseconds{}&from=1000&to=2000';
578
+ '?expression=process_cpu:cpu:nanoseconds:cpu:nanoseconds:delta{}&from=1000&to=2000';
569
579
  const { result: result1, unmount } = renderHook(() => useQueryState(), {
570
580
  wrapper: createWrapper(),
571
581
  });
572
- // User makes changes to draft
582
+ // User makes changes to draft (using delta profile since sumBy only applies to delta)
573
583
  act(() => {
574
- result1.current.setDraftExpression('memory:inuse_space:bytes:space:bytes{}');
584
+ result1.current.setDraftExpression('memory:alloc_space:bytes:space:bytes:delta{}');
575
585
  result1.current.setDraftTimeRange(5000, 6000, 'relative:minute|15');
576
586
  result1.current.setDraftSumBy(['namespace', 'pod']);
577
587
  });
@@ -600,13 +610,13 @@ describe('useQueryState', () => {
600
610
  // Create new hook instance (simulating page reload)
601
611
  const { result: result2 } = renderHook(() => useQueryState(), { wrapper: createWrapper() });
602
612
  // Verify state is loaded from URL after "reload"
603
- expect(result2.current.querySelection.expression).toBe('memory:inuse_space:bytes:space:bytes{}');
613
+ expect(result2.current.querySelection.expression).toBe('memory:alloc_space:bytes:space:bytes:delta{}');
604
614
  expect(result2.current.querySelection.from).toBe(5000);
605
615
  expect(result2.current.querySelection.to).toBe(6000);
606
616
  expect(result2.current.querySelection.timeSelection).toBe('relative:minute|15');
607
617
  expect(result2.current.querySelection.sumBy).toEqual(['namespace', 'pod']);
608
618
  // Draft should be synced with URL state on page load
609
- expect(result2.current.draftSelection.expression).toBe('memory:inuse_space:bytes:space:bytes{}');
619
+ expect(result2.current.draftSelection.expression).toBe('memory:alloc_space:bytes:space:bytes:delta{}');
610
620
  expect(result2.current.draftSelection.from).toBe(5000);
611
621
  expect(result2.current.draftSelection.to).toBe(6000);
612
622
  expect(result2.current.draftSelection.sumBy).toEqual(['namespace', 'pod']);
@@ -2,7 +2,7 @@ import { QueryServiceClient } from '@parca/client';
2
2
  import { DateTimeRange } from '@parca/components';
3
3
  import { ProfileType } from '@parca/parser';
4
4
  export declare const DEFAULT_EMPTY_SUM_BY: string[];
5
- export declare const useSumBySelection: (profileType: ProfileType | undefined, labelNamesLoading: boolean, labels: string[] | undefined, { defaultValue, }?: {
5
+ export declare const useSumBySelection: (profileType: ProfileType | undefined, labelNamesLoading: boolean, labels: string[] | undefined, draftSumBy: string[] | undefined, { defaultValue, }?: {
6
6
  defaultValue?: string[];
7
7
  }) => [string[] | undefined, (labels: string[]) => void, {
8
8
  isLoading: boolean;
@@ -13,9 +13,17 @@ export declare const useDefaultSumBy: (profileType: ProfileType | undefined, lab
13
13
  };
14
14
  export declare const useSumByFromParams: (param: string | string[] | undefined) => string[] | undefined;
15
15
  export declare const sumByToParam: (sumBy: string[] | undefined) => string | string[] | undefined;
16
- export declare const useSumBy: (queryClient: QueryServiceClient, profileType: ProfileType | undefined, timeRange: DateTimeRange, defaultValue?: string[]) => {
16
+ export declare const useSumBy: (queryClient: QueryServiceClient, profileType: ProfileType | undefined, timeRange: DateTimeRange, draftProfileType: ProfileType | undefined, draftTimeRange: DateTimeRange, defaultValue?: string[]) => {
17
17
  sumBy: string[] | undefined;
18
18
  setSumBy: (sumBy: string[]) => void;
19
19
  isLoading: boolean;
20
+ draftSumBy: string[] | undefined;
21
+ setDraftSumBy: (sumBy: string[] | undefined) => void;
22
+ isDraftSumByLoading: boolean;
23
+ };
24
+ export declare const useDraftSumBy: (queryClient: QueryServiceClient, profileType: ProfileType | undefined, timeRange: DateTimeRange, defaultValue?: string[]) => {
25
+ draftSumBy: string[] | undefined;
26
+ setDraftSumBy: (sumBy: string[] | undefined) => void;
27
+ isDraftSumByLoading: boolean;
20
28
  };
21
29
  //# sourceMappingURL=useSumBy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSumBy.d.ts","sourceRoot":"","sources":["../src/useSumBy.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACjD,OAAO,EAAC,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAI1C,eAAO,MAAM,oBAAoB,EAAE,MAAM,EAAO,CAAC;AA6BjD,eAAO,MAAM,iBAAiB,GAC5B,aAAa,WAAW,GAAG,SAAS,EACpC,mBAAmB,OAAO,EAC1B,QAAQ,MAAM,EAAE,GAAG,SAAS,EAC5B,oBAEG;IACD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,KACL,CACD,MAAM,EAAE,GAAG,SAAS,EACpB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,EAC1B;IACE,SAAS,EAAE,OAAO,CAAC;CACpB,CA+DF,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,aAAa,WAAW,GAAG,SAAS,EACpC,mBAAmB,OAAO,EAC1B,QAAQ,MAAM,EAAE,GAAG,SAAS,KAC3B;IAAC,YAAY,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAMzD,CAAC;AAyBF,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,EAAE,GAAG,SAMpF,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAU9E,CAAC;AAGF,eAAO,MAAM,QAAQ,GACnB,aAAa,kBAAkB,EAC/B,aAAa,WAAW,GAAG,SAAS,EACpC,WAAW,aAAa,EACxB,eAAe,MAAM,EAAE,KACtB;IACD,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;CAyBpB,CAAC"}
1
+ {"version":3,"file":"useSumBy.d.ts","sourceRoot":"","sources":["../src/useSumBy.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACjD,OAAO,EAAC,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAI1C,eAAO,MAAM,oBAAoB,EAAE,MAAM,EAAO,CAAC;AA6BjD,eAAO,MAAM,iBAAiB,GAC5B,aAAa,WAAW,GAAG,SAAS,EACpC,mBAAmB,OAAO,EAC1B,QAAQ,MAAM,EAAE,GAAG,SAAS,EAC5B,YAAY,MAAM,EAAE,GAAG,SAAS,EAChC,oBAEG;IACD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,KACL,CACD,MAAM,EAAE,GAAG,SAAS,EACpB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,EAC1B;IACE,SAAS,EAAE,OAAO,CAAC;CACpB,CAqEF,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,aAAa,WAAW,GAAG,SAAS,EACpC,mBAAmB,OAAO,EAC1B,QAAQ,MAAM,EAAE,GAAG,SAAS,KAC3B;IAAC,YAAY,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAMzD,CAAC;AAyBF,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,EAAE,GAAG,SAMpF,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAU9E,CAAC;AAGF,eAAO,MAAM,QAAQ,GACnB,aAAa,kBAAkB,EAC/B,aAAa,WAAW,GAAG,SAAS,EACpC,WAAW,aAAa,EACxB,kBAAkB,WAAW,GAAG,SAAS,EACzC,gBAAgB,aAAa,EAC7B,eAAe,MAAM,EAAE,KACtB;IACD,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,KAAK,IAAI,CAAC;IACrD,mBAAmB,EAAE,OAAO,CAAC;CAoC9B,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,aAAa,kBAAkB,EAC/B,aAAa,WAAW,GAAG,SAAS,EACpC,WAAW,aAAa,EACxB,eAAe,MAAM,EAAE,KACtB;IACD,UAAU,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,KAAK,IAAI,CAAC;IACrD,mBAAmB,EAAE,OAAO,CAAC;CAqB9B,CAAC"}
package/dist/useSumBy.js CHANGED
@@ -31,7 +31,7 @@ const getDefaultSumBy = (profile, labels) => {
31
31
  }
32
32
  return undefined;
33
33
  };
34
- export const useSumBySelection = (profileType, labelNamesLoading, labels, { defaultValue, } = {}) => {
34
+ export const useSumBySelection = (profileType, labelNamesLoading, labels, draftSumBy, { defaultValue, } = {}) => {
35
35
  const [userSelectedSumBy, setUserSelectedSumBy] = useState(profileType != null ? { [profileType.toString()]: defaultValue } : {});
36
36
  // Update userSelectedSumBy when defaultValue changes (e.g., during navigation)
37
37
  useEffect(() => {
@@ -57,9 +57,15 @@ export const useSumBySelection = (profileType, labelNamesLoading, labels, { defa
57
57
  // Store the last valid sumBy value to return during loading
58
58
  const lastValidSumByRef = useRef(DEFAULT_EMPTY_SUM_BY);
59
59
  const sumBy = useMemo(() => {
60
- // If loading, return the last valid value to prevent input from blanking
61
- if (labelNamesLoading && lastValidSumByRef.current !== DEFAULT_EMPTY_SUM_BY) {
62
- return lastValidSumByRef.current;
60
+ if (labelNamesLoading) {
61
+ // For smoother UX, return draftSumBy first if available during loading
62
+ // as this must be recently computed with the draft time range labels.
63
+ if (draftSumBy !== undefined) {
64
+ return draftSumBy;
65
+ }
66
+ if (lastValidSumByRef.current == null) {
67
+ return lastValidSumByRef.current;
68
+ }
63
69
  }
64
70
  let result = userSelectedSumBy[profileType?.toString() ?? ''] ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
65
71
  if (profileType?.delta !== true) {
@@ -68,7 +74,7 @@ export const useSumBySelection = (profileType, labelNamesLoading, labels, { defa
68
74
  // Store the computed value for next loading state
69
75
  lastValidSumByRef.current = result;
70
76
  return result;
71
- }, [userSelectedSumBy, profileType, defaultSumBy, labelNamesLoading]);
77
+ }, [userSelectedSumBy, profileType, defaultSumBy, labelNamesLoading, draftSumBy]);
72
78
  return [
73
79
  sumBy,
74
80
  setSumBy,
@@ -118,15 +124,32 @@ export const sumByToParam = (sumBy) => {
118
124
  return sumBy;
119
125
  };
120
126
  // Combined hook that handles all sumBy logic: fetching labels, computing defaults, and managing selection
121
- export const useSumBy = (queryClient, profileType, timeRange, defaultValue) => {
127
+ export const useSumBy = (queryClient, profileType, timeRange, draftProfileType, draftTimeRange, defaultValue) => {
122
128
  const { loading: labelNamesLoading, result } = useLabelNames(queryClient, profileType?.toString() ?? '', timeRange.getFromMs(), timeRange.getToMs());
129
+ const { draftSumBy, setDraftSumBy, isDraftSumByLoading } = useDraftSumBy(queryClient, draftProfileType, draftTimeRange, defaultValue);
123
130
  const labels = useMemo(() => {
124
131
  return result.response?.labelNames === undefined ? [] : result.response.labelNames;
125
132
  }, [result]);
126
- const [sumBySelection, setSumByInternal, { isLoading }] = useSumBySelection(profileType, labelNamesLoading, labels, { defaultValue });
133
+ const [sumBySelection, setSumByInternal, { isLoading }] = useSumBySelection(profileType, labelNamesLoading, labels, draftSumBy, { defaultValue });
127
134
  return {
128
135
  sumBy: sumBySelection,
129
136
  setSumBy: setSumByInternal,
130
137
  isLoading,
138
+ draftSumBy,
139
+ setDraftSumBy,
140
+ isDraftSumByLoading,
141
+ };
142
+ };
143
+ export const useDraftSumBy = (queryClient, profileType, timeRange, defaultValue) => {
144
+ const [draftSumBy, setDraftSumBy] = useState(defaultValue);
145
+ const { loading: labelNamesLoading, result } = useLabelNames(queryClient, profileType?.toString() ?? '', timeRange.getFromMs(), timeRange.getToMs());
146
+ const labels = useMemo(() => {
147
+ return result.response?.labelNames === undefined ? [] : result.response.labelNames;
148
+ }, [result]);
149
+ const { defaultSumBy, isLoading } = useDefaultSumBy(profileType, labelNamesLoading, labels);
150
+ return {
151
+ draftSumBy: draftSumBy ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY,
152
+ setDraftSumBy: setDraftSumBy,
153
+ isDraftSumByLoading: isLoading,
131
154
  };
132
155
  };
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.19.95",
3
+ "version": "0.19.102",
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
- "@parca/client": "0.17.11",
10
- "@parca/components": "0.16.386",
11
- "@parca/dynamicsize": "0.16.67",
12
- "@parca/hooks": "0.0.111",
13
- "@parca/icons": "0.16.74",
14
- "@parca/parser": "0.16.81",
15
- "@parca/store": "0.16.194",
9
+ "@parca/client": "0.17.16",
10
+ "@parca/components": "0.16.392",
11
+ "@parca/dynamicsize": "0.16.72",
12
+ "@parca/hooks": "0.0.116",
13
+ "@parca/icons": "0.16.79",
14
+ "@parca/parser": "0.16.86",
15
+ "@parca/store": "0.16.199",
16
16
  "@parca/test-utils": "0.0.17",
17
- "@parca/utilities": "0.0.117",
17
+ "@parca/utilities": "0.0.122",
18
18
  "@popperjs/core": "^2.11.8",
19
19
  "@protobuf-ts/runtime-rpc": "^2.5.0",
20
20
  "@storybook/preview-api": "^8.4.3",
@@ -75,9 +75,14 @@
75
75
  "keywords": [],
76
76
  "author": "",
77
77
  "license": "ISC",
78
+ "repository": {
79
+ "type": "git",
80
+ "url": "https://github.com/parca-dev/parca",
81
+ "directory": "ui/packages/shared/profile"
82
+ },
78
83
  "publishConfig": {
79
84
  "access": "public",
80
85
  "registry": "https://registry.npmjs.org/"
81
86
  },
82
- "gitHead": "3a8c3331a1a9c7f354f0c2915d822b9b0c1c5014"
87
+ "gitHead": "71d719bdbc5b9591b427c668b4ab9685eeb44b4a"
83
88
  }
@@ -29,7 +29,7 @@ interface MetricsGraphSectionProps {
29
29
  querySelection: QuerySelection;
30
30
  profileSelection: ProfileSelection | null;
31
31
  comparing: boolean;
32
- sumBy: string[] | null;
32
+ sumBy: string[] | undefined;
33
33
  defaultSumByLoading: boolean;
34
34
  queryClient: QueryServiceClient;
35
35
  queryExpressionString: string;
@@ -170,7 +170,7 @@ export function MetricsGraphSection({
170
170
  to={querySelection.to}
171
171
  profile={profileSelection}
172
172
  comparing={comparing}
173
- sumBy={querySelection.sumBy ?? sumBy ?? []}
173
+ sumBy={sumBy ?? []}
174
174
  sumByLoading={defaultSumByLoading}
175
175
  setTimeRange={handleTimeRangeChange}
176
176
  addLabelMatcher={addLabelMatcher}
@@ -209,10 +209,6 @@ const ProfileSelector = ({
209
209
  const currentTo = timeRangeSelection.getToMs(true);
210
210
  const currentRangeKey = timeRangeSelection.getRangeKey();
211
211
  // Commit with refreshed time range
212
- console.log(
213
- '[draftExpression] setQueryExpression: committing with refreshed time range:',
214
- draftSelection.expression
215
- );
216
212
  commitDraft({
217
213
  from: currentFrom,
218
214
  to: currentTo,
@@ -332,7 +328,7 @@ const ProfileSelector = ({
332
328
  querySelection={querySelection}
333
329
  profileSelection={profileSelection}
334
330
  comparing={comparing}
335
- sumBy={querySelection.sumBy ?? []}
331
+ sumBy={querySelection.sumBy}
336
332
  defaultSumByLoading={sumByLoading}
337
333
  queryClient={queryClient}
338
334
  queryExpressionString={queryExpressionString}
@@ -18,6 +18,8 @@ import {useProfileFilters} from '../components/ProfileFilters/useProfileFilters'
18
18
  export const useResetStateOnProfileTypeChange = (): (() => void) => {
19
19
  const [groupBy, setGroupBy] = useURLState('group_by');
20
20
  const [curPath, setCurPath] = useURLState('cur_path');
21
+ const [sumByA, setSumByA] = useURLState('sum_by_a');
22
+ const [sumByB, setSumByB] = useURLState('sum_by_b');
21
23
  const {resetFilters} = useProfileFilters();
22
24
  const [sandwichFunctionName, setSandwichFunctionName] = useURLState('sandwich_function_name');
23
25
  const batchUpdates = useURLStateBatch();
@@ -34,6 +36,12 @@ export const useResetStateOnProfileTypeChange = (): (() => void) => {
34
36
  if (sandwichFunctionName !== undefined) {
35
37
  setSandwichFunctionName(undefined);
36
38
  }
39
+ if (sumByA !== undefined) {
40
+ setSumByA(undefined);
41
+ }
42
+ if (sumByB !== undefined) {
43
+ setSumByB(undefined);
44
+ }
37
45
 
38
46
  resetFilters();
39
47
  });
@@ -11,7 +11,7 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import React, {useCallback, useEffect, useRef, useState} from 'react';
14
+ import React, {Fragment, useCallback, useEffect, useRef, useState} from 'react';
15
15
 
16
16
  import {Icon} from '@iconify/react';
17
17
  import cx from 'classnames';
@@ -350,7 +350,7 @@ const CustomSelect: React.FC<CustomSelectProps & Record<string, any>> = ({
350
350
  </div>
351
351
  ) : (
352
352
  groupedFilteredItems.map(group => (
353
- <>
353
+ <Fragment key={group.type}>
354
354
  {groupedFilteredItems.length > 1 &&
355
355
  groupedFilteredItems.every(g => g.type !== '') &&
356
356
  group.type !== '' ? (
@@ -369,7 +369,7 @@ const CustomSelect: React.FC<CustomSelectProps & Record<string, any>> = ({
369
369
  handleSelection={handleSelection}
370
370
  />
371
371
  ))}
372
- </>
372
+ </Fragment>
373
373
  ))
374
374
  )}
375
375
  </div>
@@ -11,6 +11,8 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
+ import {useEffect} from 'react';
15
+
14
16
  import {LabelsRequest, LabelsResponse, QueryServiceClient, ValuesRequest} from '@parca/client';
15
17
  import {useGrpcMetadata} from '@parca/components';
16
18
  import {millisToProtoTimestamp, sanitizeLabelValue} from '@parca/utilities';
@@ -68,7 +70,9 @@ export const useLabelNames = (
68
70
  },
69
71
  });
70
72
 
71
- console.log('Label names query result:', {data, error, isLoading});
73
+ useEffect(() => {
74
+ console.log('Label names query result:', {data, error, isLoading});
75
+ }, [data, error, isLoading]);
72
76
 
73
77
  return {
74
78
  result: {response: data, error: error as Error},
@@ -69,16 +69,33 @@ vi.mock('@parca/components/src/hooks/URLState/utils', async () => {
69
69
  };
70
70
  });
71
71
 
72
- // Mock useSumBy to return the sumBy from URL params or undefined
72
+ // Mock useSumBy with stateful behavior using React's useState
73
73
  vi.mock('../useSumBy', async () => {
74
74
  const actual = await vi.importActual('../useSumBy');
75
+ const react = await import('react');
76
+
75
77
  return {
76
78
  ...actual,
77
- useSumBy: (_queryClient: any, _profileType: any, _timeRange: any, defaultValue: any) => ({
78
- sumBy: defaultValue,
79
- setSumBy: vi.fn(),
80
- isLoading: false,
81
- }),
79
+ useSumBy: (
80
+ _queryClient: any,
81
+ _profileType: any,
82
+ _timeRange: any,
83
+ _draftProfileType: any,
84
+ _draftTimeRange: any,
85
+ defaultValue: any
86
+ ) => {
87
+ const [draftSumBy, setDraftSumBy] = react.useState<string[] | undefined>(defaultValue);
88
+ const [sumBy, setSumBy] = react.useState<string[] | undefined>(defaultValue);
89
+
90
+ return {
91
+ sumBy,
92
+ setSumBy,
93
+ isLoading: false,
94
+ draftSumBy,
95
+ setDraftSumBy,
96
+ isDraftSumByLoading: false,
97
+ };
98
+ },
82
99
  };
83
100
  });
84
101
 
@@ -207,7 +224,9 @@ describe('useQueryState', () => {
207
224
  it('should update sumBy', async () => {
208
225
  const {result} = renderHook(() => useQueryState(), {wrapper: createWrapper()});
209
226
 
227
+ // sumBy only applies to delta profiles, so we need to set one first
210
228
  act(() => {
229
+ result.current.setDraftExpression('process_cpu:cpu:nanoseconds:cpu:nanoseconds:delta{}');
211
230
  result.current.setDraftSumBy(['namespace', 'container']);
212
231
  });
213
232
 
@@ -256,15 +275,15 @@ describe('useQueryState', () => {
256
275
  const {result} = renderHook(() => useQueryState(), {wrapper: createWrapper()});
257
276
 
258
277
  act(() => {
259
- // Update multiple draft values
260
- result.current.setDraftExpression('memory:inuse_space:bytes:space:bytes{}');
278
+ // Update multiple draft values (using delta profile since sumBy only applies to delta)
279
+ result.current.setDraftExpression('memory:alloc_space:bytes:space:bytes:delta{}');
261
280
  result.current.setDraftTimeRange(7000, 8000, 'relative:minute|30');
262
281
  result.current.setDraftSumBy(['pod', 'node']);
263
282
  });
264
283
 
265
284
  // All drafts should be updated
266
285
  expect(result.current.draftSelection.expression).toBe(
267
- 'memory:inuse_space:bytes:space:bytes{}'
286
+ 'memory:alloc_space:bytes:space:bytes:delta{}'
268
287
  );
269
288
  expect(result.current.draftSelection.from).toBe(7000);
270
289
  expect(result.current.draftSelection.to).toBe(8000);
@@ -278,7 +297,7 @@ describe('useQueryState', () => {
278
297
  // Should only navigate once for all updates
279
298
  expect(mockNavigateTo).toHaveBeenCalledTimes(1);
280
299
  const [, params] = mockNavigateTo.mock.calls[0];
281
- expect(params.expression).toBe('memory:inuse_space:bytes:space:bytes{}');
300
+ expect(params.expression).toBe('memory:alloc_space:bytes:space:bytes:delta{}');
282
301
  expect(params.from).toBe('7000');
283
302
  expect(params.to).toBe('8000');
284
303
  expect(params.time_selection).toBe('relative:minute|30');
@@ -430,9 +449,9 @@ describe('useQueryState', () => {
430
449
  it('should handle _b suffix correctly', async () => {
431
450
  const {result} = renderHook(() => useQueryState({suffix: '_b'}), {wrapper: createWrapper()});
432
451
 
433
- // Update draft state
452
+ // Update draft state (using delta profile since sumBy only applies to delta)
434
453
  act(() => {
435
- result.current.setDraftExpression('memory:inuse_space:bytes:space:bytes{}');
454
+ result.current.setDraftExpression('memory:alloc_space:bytes:space:bytes:delta{}');
436
455
  result.current.setDraftTimeRange(3333, 4444, 'relative:hour|2');
437
456
  result.current.setDraftSumBy(['label_b']);
438
457
  });
@@ -445,7 +464,7 @@ describe('useQueryState', () => {
445
464
  await waitFor(() => {
446
465
  expect(mockNavigateTo).toHaveBeenCalled();
447
466
  const [, params] = mockNavigateTo.mock.calls[mockNavigateTo.mock.calls.length - 1];
448
- expect(params.expression_b).toBe('memory:inuse_space:bytes:space:bytes{}');
467
+ expect(params.expression_b).toBe('memory:alloc_space:bytes:space:bytes:delta{}');
449
468
  expect(params.from_b).toBe('3333');
450
469
  expect(params.to_b).toBe('4444');
451
470
  expect(params.sum_by_b).toBe('label_b');
@@ -457,9 +476,9 @@ describe('useQueryState', () => {
457
476
  it('should not update URL until commit', async () => {
458
477
  const {result} = renderHook(() => useQueryState(), {wrapper: createWrapper()});
459
478
 
460
- // Make multiple draft changes
479
+ // Make multiple draft changes (using delta profile since sumBy only applies to delta)
461
480
  act(() => {
462
- result.current.setDraftExpression('memory:inuse_space:bytes:space:bytes{}');
481
+ result.current.setDraftExpression('memory:alloc_space:bytes:space:bytes:delta{}');
463
482
  result.current.setDraftTimeRange(5000, 6000, 'relative:hour|3');
464
483
  result.current.setDraftSumBy(['namespace', 'pod']);
465
484
  });
@@ -476,7 +495,7 @@ describe('useQueryState', () => {
476
495
  await waitFor(() => {
477
496
  expect(mockNavigateTo).toHaveBeenCalledTimes(1);
478
497
  const [, params] = mockNavigateTo.mock.calls[0];
479
- expect(params.expression).toBe('memory:inuse_space:bytes:space:bytes{}');
498
+ expect(params.expression).toBe('memory:alloc_space:bytes:space:bytes:delta{}');
480
499
  expect(params.from).toBe('5000');
481
500
  expect(params.to).toBe('6000');
482
501
  expect(params.sum_by).toBe('namespace,pod');
@@ -737,17 +756,17 @@ describe('useQueryState', () => {
737
756
 
738
757
  describe('State persistence after page reload', () => {
739
758
  it('should retain committed values after page reload simulation', async () => {
740
- // Initial state
759
+ // Initial state (using delta profile since sumBy only applies to delta)
741
760
  mockLocation.search =
742
- '?expression=process_cpu:cpu:nanoseconds:cpu:nanoseconds{}&from=1000&to=2000';
761
+ '?expression=process_cpu:cpu:nanoseconds:cpu:nanoseconds:delta{}&from=1000&to=2000';
743
762
 
744
763
  const {result: result1, unmount} = renderHook(() => useQueryState(), {
745
764
  wrapper: createWrapper(),
746
765
  });
747
766
 
748
- // User makes changes to draft
767
+ // User makes changes to draft (using delta profile since sumBy only applies to delta)
749
768
  act(() => {
750
- result1.current.setDraftExpression('memory:inuse_space:bytes:space:bytes{}');
769
+ result1.current.setDraftExpression('memory:alloc_space:bytes:space:bytes:delta{}');
751
770
  result1.current.setDraftTimeRange(5000, 6000, 'relative:minute|15');
752
771
  result1.current.setDraftSumBy(['namespace', 'pod']);
753
772
  });
@@ -786,7 +805,7 @@ describe('useQueryState', () => {
786
805
 
787
806
  // Verify state is loaded from URL after "reload"
788
807
  expect(result2.current.querySelection.expression).toBe(
789
- 'memory:inuse_space:bytes:space:bytes{}'
808
+ 'memory:alloc_space:bytes:space:bytes:delta{}'
790
809
  );
791
810
  expect(result2.current.querySelection.from).toBe(5000);
792
811
  expect(result2.current.querySelection.to).toBe(6000);
@@ -795,7 +814,7 @@ describe('useQueryState', () => {
795
814
 
796
815
  // Draft should be synced with URL state on page load
797
816
  expect(result2.current.draftSelection.expression).toBe(
798
- 'memory:inuse_space:bytes:space:bytes{}'
817
+ 'memory:alloc_space:bytes:space:bytes:delta{}'
799
818
  );
800
819
  expect(result2.current.draftSelection.from).toBe(5000);
801
820
  expect(result2.current.draftSelection.to).toBe(6000);
@@ -19,7 +19,8 @@ import {Query} from '@parca/parser';
19
19
  import {QuerySelection} from '../ProfileSelector';
20
20
  import {ProfileSelection, ProfileSelectionFromParams, ProfileSource} from '../ProfileSource';
21
21
  import {useResetFlameGraphState} from '../ProfileView/hooks/useResetFlameGraphState';
22
- import {sumByToParam, useSumBy, useSumByFromParams} from '../useSumBy';
22
+ import {useResetStateOnProfileTypeChange} from '../ProfileView/hooks/useResetStateOnProfileTypeChange';
23
+ import {DEFAULT_EMPTY_SUM_BY, sumByToParam, useSumBy, useSumByFromParams} from '../useSumBy';
23
24
 
24
25
  interface UseQueryStateOptions {
25
26
  suffix?: '_a' | '_b'; // For comparison mode
@@ -79,6 +80,7 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
79
80
 
80
81
  const batchUpdates = useURLStateBatch();
81
82
  const resetFlameGraphState = useResetFlameGraphState();
83
+ const resetStateOnProfileTypeChange = useResetStateOnProfileTypeChange();
82
84
 
83
85
  // URL state hooks with appropriate suffixes
84
86
  const [expression, setExpressionState] = useURLState<string>(`expression${suffix}`, {
@@ -115,29 +117,6 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
115
117
  const [draftTimeSelection, setDraftTimeSelection] = useState<string>(
116
118
  timeSelection ?? defaultTimeSelection
117
119
  );
118
- const [draftSumBy, setDraftSumBy] = useState<string[] | undefined>(sumBy);
119
-
120
- // Sync draft state with URL state when URL changes externally
121
- useEffect(() => {
122
- setDraftExpression(expression ?? defaultExpression);
123
- }, [expression, defaultExpression]);
124
-
125
- useEffect(() => {
126
- setDraftFrom(from ?? defaultFrom?.toString() ?? '');
127
- }, [from, defaultFrom]);
128
-
129
- useEffect(() => {
130
- setDraftTo(to ?? defaultTo?.toString() ?? '');
131
- }, [to, defaultTo]);
132
-
133
- useEffect(() => {
134
- setDraftTimeSelection(timeSelection ?? defaultTimeSelection);
135
- }, [timeSelection, defaultTimeSelection]);
136
-
137
- useEffect(() => {
138
- setDraftSumBy(sumBy);
139
- }, [sumBy]);
140
-
141
120
  // Parse the draft query to extract profile information
142
121
  const draftQuery = useMemo(() => {
143
122
  try {
@@ -147,8 +126,16 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
147
126
  }
148
127
  }, [draftExpression]);
149
128
 
129
+ const query = useMemo(() => {
130
+ try {
131
+ return Query.parse(expression ?? '');
132
+ } catch {
133
+ return Query.parse('');
134
+ }
135
+ }, [expression]);
150
136
  const draftProfileType = useMemo(() => draftQuery.profileType(), [draftQuery]);
151
137
  const draftProfileName = useMemo(() => draftQuery.profileName(), [draftQuery]);
138
+ const profileType = useMemo(() => query.profileType(), [query]);
152
139
 
153
140
  // Compute draft time range for label fetching
154
141
  const draftTimeRange = useMemo(() => {
@@ -159,13 +146,50 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
159
146
  );
160
147
  }, [draftTimeSelection, draftFrom, draftTo, defaultTimeSelection, defaultFrom, defaultTo]);
161
148
  // Use combined sumBy hook for fetching labels and computing defaults (based on committed state)
162
- const {sumBy: computedSumByFromURL, isLoading: sumBySelectionLoading} = useSumBy(
149
+ const {
150
+ sumBy: computedSumByFromURL,
151
+ isLoading: sumBySelectionLoading,
152
+ draftSumBy,
153
+ setDraftSumBy,
154
+ isDraftSumByLoading,
155
+ } = useSumBy(
163
156
  queryClient,
157
+ profileType?.profileName !== '' ? profileType : draftProfileType,
158
+ draftTimeRange,
164
159
  draftProfileType,
165
160
  draftTimeRange,
166
161
  sumBy
167
162
  );
168
163
 
164
+ // Sync draft state with URL state when URL changes externally
165
+ useEffect(() => {
166
+ setDraftExpression(expression ?? defaultExpression);
167
+ }, [expression, defaultExpression]);
168
+
169
+ useEffect(() => {
170
+ setDraftFrom(from ?? defaultFrom?.toString() ?? '');
171
+ }, [from, defaultFrom]);
172
+
173
+ useEffect(() => {
174
+ setDraftTo(to ?? defaultTo?.toString() ?? '');
175
+ }, [to, defaultTo]);
176
+
177
+ useEffect(() => {
178
+ setDraftTimeSelection(timeSelection ?? defaultTimeSelection);
179
+ }, [timeSelection, defaultTimeSelection]);
180
+
181
+ useEffect(() => {
182
+ setDraftSumBy(sumBy);
183
+ }, [sumBy, setDraftSumBy]);
184
+
185
+ // Sync computed sumBy to URL if URL doesn't already have a value
186
+ // to ensure the shared URL can always pick it up.
187
+ useEffect(() => {
188
+ if (sumByParam === undefined && computedSumByFromURL !== undefined && !sumBySelectionLoading) {
189
+ setSumByParam(sumByToParam(computedSumByFromURL));
190
+ }
191
+ }, [sumByParam, computedSumByFromURL, sumBySelectionLoading, setSumByParam]);
192
+
169
193
  // Construct the QuerySelection object (committed state from URL)
170
194
  const querySelection: QuerySelection = useMemo(() => {
171
195
  const range = DateTimeRange.fromRangeKey(
@@ -285,12 +309,16 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
285
309
  setFromState(finalFrom);
286
310
  setToState(finalTo);
287
311
  setTimeSelectionState(finalTimeSelection);
288
- setSumByParam(sumByToParam(draftSumBy));
289
312
 
290
313
  // Auto-calculate merge parameters for delta profiles
291
314
  // Parse the final expression to check if it's a delta profile
292
315
  const finalQuery = Query.parse(finalExpression);
293
316
  const isDelta = finalQuery.profileType().delta;
317
+ if (isDelta) {
318
+ setSumByParam(sumByToParam(draftSumBy));
319
+ } else {
320
+ setSumByParam(DEFAULT_EMPTY_SUM_BY);
321
+ }
294
322
 
295
323
  if (isDelta && finalFrom !== '' && finalTo !== '') {
296
324
  const fromMs = parseInt(finalFrom);
@@ -313,6 +341,12 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
313
341
  setSelectionParam(undefined);
314
342
  }
315
343
  resetFlameGraphState();
344
+ if (
345
+ draftProfileType.toString() !==
346
+ Query.parse(querySelection.expression).profileType().toString()
347
+ ) {
348
+ resetStateOnProfileTypeChange();
349
+ }
316
350
  });
317
351
  },
318
352
  [
@@ -334,6 +368,9 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
334
368
  setMergeToState,
335
369
  setSelectionParam,
336
370
  resetFlameGraphState,
371
+ resetStateOnProfileTypeChange,
372
+ draftProfileType,
373
+ querySelection.expression,
337
374
  ]
338
375
  );
339
376
 
@@ -346,9 +383,12 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
346
383
  []
347
384
  );
348
385
 
349
- const setDraftSumByCallback = useCallback((newSumBy: string[] | undefined) => {
350
- setDraftSumBy(newSumBy);
351
- }, []);
386
+ const setDraftSumByCallback = useCallback(
387
+ (newSumBy: string[] | undefined) => {
388
+ setDraftSumBy(newSumBy);
389
+ },
390
+ [setDraftSumBy]
391
+ );
352
392
 
353
393
  const setDraftProfileName = useCallback(
354
394
  (newProfileName: string) => {
@@ -357,9 +397,10 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
357
397
  const [newQuery, changed] = draftQuery.setProfileName(newProfileName);
358
398
  if (changed) {
359
399
  setDraftExpression(newQuery.toString());
400
+ setDraftSumBy(undefined);
360
401
  }
361
402
  },
362
- [draftQuery]
403
+ [draftQuery, setDraftSumBy]
363
404
  );
364
405
 
365
406
  const setDraftMatchers = useCallback(
@@ -421,7 +462,7 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
421
462
  setProfileSelection,
422
463
 
423
464
  // Loading state
424
- sumByLoading: sumBySelectionLoading,
465
+ sumByLoading: isDraftSumByLoading || sumBySelectionLoading,
425
466
 
426
467
  draftParsedQuery,
427
468
  parsedQuery,
package/src/useSumBy.ts CHANGED
@@ -52,6 +52,7 @@ export const useSumBySelection = (
52
52
  profileType: ProfileType | undefined,
53
53
  labelNamesLoading: boolean,
54
54
  labels: string[] | undefined,
55
+ draftSumBy: string[] | undefined,
55
56
  {
56
57
  defaultValue,
57
58
  }: {
@@ -100,9 +101,15 @@ export const useSumBySelection = (
100
101
  const lastValidSumByRef = useRef<string[]>(DEFAULT_EMPTY_SUM_BY);
101
102
 
102
103
  const sumBy = useMemo(() => {
103
- // If loading, return the last valid value to prevent input from blanking
104
- if (labelNamesLoading && lastValidSumByRef.current !== DEFAULT_EMPTY_SUM_BY) {
105
- return lastValidSumByRef.current;
104
+ if (labelNamesLoading) {
105
+ // For smoother UX, return draftSumBy first if available during loading
106
+ // as this must be recently computed with the draft time range labels.
107
+ if (draftSumBy !== undefined) {
108
+ return draftSumBy;
109
+ }
110
+ if (lastValidSumByRef.current == null) {
111
+ return lastValidSumByRef.current;
112
+ }
106
113
  }
107
114
 
108
115
  let result =
@@ -116,7 +123,7 @@ export const useSumBySelection = (
116
123
  lastValidSumByRef.current = result;
117
124
 
118
125
  return result;
119
- }, [userSelectedSumBy, profileType, defaultSumBy, labelNamesLoading]);
126
+ }, [userSelectedSumBy, profileType, defaultSumBy, labelNamesLoading, draftSumBy]);
120
127
 
121
128
  return [
122
129
  sumBy,
@@ -187,11 +194,16 @@ export const useSumBy = (
187
194
  queryClient: QueryServiceClient,
188
195
  profileType: ProfileType | undefined,
189
196
  timeRange: DateTimeRange,
197
+ draftProfileType: ProfileType | undefined,
198
+ draftTimeRange: DateTimeRange,
190
199
  defaultValue?: string[]
191
200
  ): {
192
201
  sumBy: string[] | undefined;
193
202
  setSumBy: (sumBy: string[]) => void;
194
203
  isLoading: boolean;
204
+ draftSumBy: string[] | undefined;
205
+ setDraftSumBy: (sumBy: string[] | undefined) => void;
206
+ isDraftSumByLoading: boolean;
195
207
  } => {
196
208
  const {loading: labelNamesLoading, result} = useLabelNames(
197
209
  queryClient,
@@ -200,6 +212,13 @@ export const useSumBy = (
200
212
  timeRange.getToMs()
201
213
  );
202
214
 
215
+ const {draftSumBy, setDraftSumBy, isDraftSumByLoading} = useDraftSumBy(
216
+ queryClient,
217
+ draftProfileType,
218
+ draftTimeRange,
219
+ defaultValue
220
+ );
221
+
203
222
  const labels = useMemo(() => {
204
223
  return result.response?.labelNames === undefined ? [] : result.response.labelNames;
205
224
  }, [result]);
@@ -208,6 +227,7 @@ export const useSumBy = (
208
227
  profileType,
209
228
  labelNamesLoading,
210
229
  labels,
230
+ draftSumBy,
211
231
  {defaultValue}
212
232
  );
213
233
 
@@ -215,5 +235,39 @@ export const useSumBy = (
215
235
  sumBy: sumBySelection,
216
236
  setSumBy: setSumByInternal,
217
237
  isLoading,
238
+ draftSumBy,
239
+ setDraftSumBy,
240
+ isDraftSumByLoading,
241
+ };
242
+ };
243
+
244
+ export const useDraftSumBy = (
245
+ queryClient: QueryServiceClient,
246
+ profileType: ProfileType | undefined,
247
+ timeRange: DateTimeRange,
248
+ defaultValue?: string[]
249
+ ): {
250
+ draftSumBy: string[] | undefined;
251
+ setDraftSumBy: (sumBy: string[] | undefined) => void;
252
+ isDraftSumByLoading: boolean;
253
+ } => {
254
+ const [draftSumBy, setDraftSumBy] = useState<string[] | undefined>(defaultValue);
255
+ const {loading: labelNamesLoading, result} = useLabelNames(
256
+ queryClient,
257
+ profileType?.toString() ?? '',
258
+ timeRange.getFromMs(),
259
+ timeRange.getToMs()
260
+ );
261
+
262
+ const labels = useMemo(() => {
263
+ return result.response?.labelNames === undefined ? [] : result.response.labelNames;
264
+ }, [result]);
265
+
266
+ const {defaultSumBy, isLoading} = useDefaultSumBy(profileType, labelNamesLoading, labels);
267
+
268
+ return {
269
+ draftSumBy: draftSumBy ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY,
270
+ setDraftSumBy: setDraftSumBy,
271
+ isDraftSumByLoading: isLoading,
218
272
  };
219
273
  };