@parca/profile 0.19.156 → 0.19.158

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.d.ts.map +1 -1
  3. package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.js +9 -3
  4. package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.js +3 -1
  5. package/dist/ProfileSelector/index.js +274 -265
  6. package/dist/ProfileView/components/ActionButtons/SortByDropdown.js +3 -1
  7. package/dist/ProfileView/components/InvertCallStack/index.d.ts.map +1 -1
  8. package/dist/ProfileView/components/InvertCallStack/index.js +55 -46
  9. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts.map +1 -1
  10. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.js +282 -255
  11. package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.d.ts.map +1 -1
  12. package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.js +141 -132
  13. package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
  14. package/dist/ProfileView/components/ViewSelector/index.js +49 -40
  15. package/dist/ProfileView/context/DashboardContext.d.ts.map +1 -1
  16. package/dist/ProfileView/context/DashboardContext.js +37 -28
  17. package/dist/ProfileView/hooks/useResetFlameGraphState.d.ts.map +1 -1
  18. package/dist/ProfileView/hooks/useResetFlameGraphState.js +19 -10
  19. package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -1
  20. package/dist/ProfileView/hooks/useVisualizationState.js +122 -98
  21. package/dist/SourceView/useSelectedLineRange.js +2 -0
  22. package/dist/Table/MoreDropdown.d.ts.map +1 -1
  23. package/dist/Table/MoreDropdown.js +40 -31
  24. package/dist/Table/TableContextMenu.d.ts.map +1 -1
  25. package/dist/Table/TableContextMenu.js +3 -1
  26. package/dist/Table/index.d.ts.map +1 -1
  27. package/dist/Table/index.js +3 -1
  28. package/dist/hooks/useColorBy.d.ts.map +1 -1
  29. package/dist/hooks/useColorBy.js +27 -18
  30. package/dist/hooks/useQueryState.d.ts.map +1 -1
  31. package/dist/hooks/useQueryState.js +20 -5
  32. package/package.json +3 -3
  33. package/src/GraphTooltipArrow/useGraphTooltipMetaInfo/index.ts +12 -3
  34. package/src/ProfileFlameGraph/FlameGraphArrow/ContextMenu.tsx +1 -1
  35. package/src/ProfileSelector/index.tsx +1 -1
  36. package/src/ProfileView/components/ActionButtons/SortByDropdown.tsx +1 -1
  37. package/src/ProfileView/components/InvertCallStack/index.tsx +4 -1
  38. package/src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx +6 -3
  39. package/src/ProfileView/components/Toolbars/TableColumnsDropdown.tsx +4 -1
  40. package/src/ProfileView/components/ViewSelector/index.tsx +4 -1
  41. package/src/ProfileView/context/DashboardContext.tsx +4 -1
  42. package/src/ProfileView/hooks/useResetFlameGraphState.ts +4 -1
  43. package/src/ProfileView/hooks/useVisualizationState.ts +7 -5
  44. package/src/SourceView/useSelectedLineRange.ts +1 -1
  45. package/src/Table/MoreDropdown.tsx +4 -1
  46. package/src/Table/TableContextMenu.tsx +4 -1
  47. package/src/Table/index.tsx +4 -1
  48. package/src/hooks/useColorBy.ts +4 -1
  49. package/src/hooks/useQueryState.ts +38 -20
@@ -24,40 +24,49 @@ import { USER_PREFERENCES, useUserPreference } from '@parca/hooks';
24
24
  import { stringParam } from './urlParsers';
25
25
  export var useColorBy = function useColorBy() {
26
26
  var _ref;
27
- var $ = _c(6);
27
+ var $ = _c(7);
28
28
  var _useUserPreference = useUserPreference(USER_PREFERENCES.COLOR_BY.key),
29
29
  _useUserPreference2 = _slicedToArray(_useUserPreference, 2),
30
30
  colorByPreference = _useUserPreference2[0],
31
31
  setColorByPreference = _useUserPreference2[1];
32
- var _useQueryState = useQueryState("color_by", stringParam),
32
+ var t0;
33
+ if ($[0] === Symbol["for"]("react.memo_cache_sentinel")) {
34
+ t0 = stringParam.withOptions({
35
+ history: "push"
36
+ });
37
+ $[0] = t0;
38
+ } else {
39
+ t0 = $[0];
40
+ }
41
+ var _useQueryState = useQueryState("color_by", t0),
33
42
  _useQueryState2 = _slicedToArray(_useQueryState, 2),
34
43
  colorByRaw = _useQueryState2[0],
35
44
  setRawColorBy = _useQueryState2[1];
36
45
  var colorBy = (_ref = colorByRaw !== null && colorByRaw !== void 0 ? colorByRaw : colorByPreference) !== null && _ref !== void 0 ? _ref : "binary";
37
- var t0;
38
- if ($[0] !== setColorByPreference || $[1] !== setRawColorBy) {
39
- t0 = function t0(value) {
46
+ var t1;
47
+ if ($[1] !== setColorByPreference || $[2] !== setRawColorBy) {
48
+ t1 = function t1(value) {
40
49
  setRawColorBy(value);
41
50
  setColorByPreference(value);
42
51
  };
43
- $[0] = setColorByPreference;
44
- $[1] = setRawColorBy;
45
- $[2] = t0;
52
+ $[1] = setColorByPreference;
53
+ $[2] = setRawColorBy;
54
+ $[3] = t1;
46
55
  } else {
47
- t0 = $[2];
56
+ t1 = $[3];
48
57
  }
49
- var setColorBy = t0;
50
- var t1;
51
- if ($[3] !== colorBy || $[4] !== setColorBy) {
52
- t1 = {
58
+ var setColorBy = t1;
59
+ var t2;
60
+ if ($[4] !== colorBy || $[5] !== setColorBy) {
61
+ t2 = {
53
62
  colorBy: colorBy,
54
63
  setColorBy: setColorBy
55
64
  };
56
- $[3] = colorBy;
57
- $[4] = setColorBy;
58
- $[5] = t1;
65
+ $[4] = colorBy;
66
+ $[5] = setColorBy;
67
+ $[6] = t2;
59
68
  } else {
60
- t1 = $[5];
69
+ t2 = $[6];
61
70
  }
62
- return t1;
71
+ return t2;
63
72
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useQueryState.d.ts","sourceRoot":"","sources":["../../src/hooks/useQueryState.ts"],"names":[],"mappings":"AAkBA,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;AAM7F,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;IACpB,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;CAClC;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,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;IAGV,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;IAE1B,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACnD,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC;CACnD;AAED,eAAO,MAAM,aAAa,GAAI,UAAS,oBAAyB,KAAG,mBAsclE,CAAC"}
1
+ {"version":3,"file":"useQueryState.d.ts","sourceRoot":"","sources":["../../src/hooks/useQueryState.ts"],"names":[],"mappings":"AAkBA,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;AAM7F,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;IACpB,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;CAClC;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,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;IAGV,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;IAE1B,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACnD,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC;CACnD;AAED,eAAO,MAAM,aAAa,GAAI,UAAS,oBAAyB,KAAG,mBAwdlE,CAAC"}
@@ -36,7 +36,8 @@ export var useQueryState = function useQueryState() {
36
36
  var _queryParams$expressi, _queryParams$from, _queryParams$to, _queryParams$time_sel, _ref, _ref2;
37
37
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
38
38
  var _useParcaContext = useParcaContext(),
39
- queryClient = _useParcaContext.queryServiceClient;
39
+ queryClient = _useParcaContext.queryServiceClient,
40
+ externalProfilerComponent = _useParcaContext.externalProfilerComponent;
40
41
  var _options$suffix = options.suffix,
41
42
  suffix = _options$suffix === void 0 ? '' : _options$suffix,
42
43
  _options$defaultExpre = options.defaultExpression,
@@ -97,7 +98,9 @@ export var useQueryState = function useQueryState() {
97
98
  sum_by: val_0
98
99
  });
99
100
  }, [setQueryParams]);
100
- var _useNuqsQueryState = useNuqsQueryState('group_by', commaArrayParam),
101
+ var _useNuqsQueryState = useNuqsQueryState('group_by', commaArrayParam.withOptions({
102
+ history: 'replace'
103
+ })),
101
104
  _useNuqsQueryState2 = _slicedToArray(_useNuqsQueryState, 2),
102
105
  setRawGroupByParam = _useNuqsQueryState2[1];
103
106
  var setGroupByParam = useCallback(function (val_1) {
@@ -107,6 +110,12 @@ export var useQueryState = function useQueryState() {
107
110
  // Parse sumBy from URL parameter format
108
111
  var sumBy = useSumByFromParams(sumByParam !== null && sumByParam !== void 0 ? sumByParam : undefined);
109
112
 
113
+ // Fall back to the externally-provided sumBy default when the URL carries no
114
+ // sum_by, so the materialized value reflects it instead of the empty __none__
115
+ // sentinel (an explicit __none__ from the URL is left untouched).
116
+ var externalSumBy = externalProfilerComponent === null || externalProfilerComponent === void 0 ? void 0 : externalProfilerComponent.defaultSumBy;
117
+ var sumByWithViewDefault = sumBy !== null && sumBy !== void 0 ? sumBy : externalSumBy != null && externalSumBy.length > 0 ? externalSumBy : undefined;
118
+
110
119
  // Draft state management
111
120
  var _useState = useState(expression !== null && expression !== void 0 ? expression : defaultExpression),
112
121
  _useState2 = _slicedToArray(_useState, 2),
@@ -162,7 +171,7 @@ export var useQueryState = function useQueryState() {
162
171
  return DateTimeRange.fromRangeKey(draftTimeSelection !== null && draftTimeSelection !== void 0 ? draftTimeSelection : defaultTimeSelection, draftFrom !== '' ? parseInt(draftFrom) : defaultFrom, draftTo !== '' ? parseInt(draftTo) : defaultTo);
163
172
  }, [draftTimeSelection, draftFrom, draftTo, defaultTimeSelection, defaultFrom, defaultTo]);
164
173
  // Use combined sumBy hook for fetching labels and computing defaults (based on committed state)
165
- var _useSumBy = useSumBy(queryClient, (profileType === null || profileType === void 0 ? void 0 : profileType.profileName) !== '' ? profileType : draftProfileType, draftTimeRange, draftProfileType, draftTimeRange, sumBy),
174
+ var _useSumBy = useSumBy(queryClient, (profileType === null || profileType === void 0 ? void 0 : profileType.profileName) !== '' ? profileType : draftProfileType, draftTimeRange, draftProfileType, draftTimeRange, sumByWithViewDefault),
166
175
  computedSumByFromURL = _useSumBy.sumBy,
167
176
  sumBySelectionLoading = _useSumBy.isLoading,
168
177
  draftSumBy = _useSumBy.draftSumBy,
@@ -296,7 +305,8 @@ export var useQueryState = function useQueryState() {
296
305
  }
297
306
  }
298
307
 
299
- // Atomic URL update with all params at once
308
+ // Atomic URL update with all params at once. Push a history entry so the
309
+ // back button steps through profile/time selections (default is 'replace').
300
310
  void setQueryParams({
301
311
  expression: finalExpression,
302
312
  from: finalFrom,
@@ -306,6 +316,8 @@ export var useQueryState = function useQueryState() {
306
316
  merge_from: mergeFromValue,
307
317
  merge_to: mergeToValue,
308
318
  selection: selectionValue
319
+ }, {
320
+ history: 'push'
309
321
  });
310
322
  resetFlameGraphState();
311
323
  if (draftProfileType.toString() !== Query.parse(querySelection.expression).profileType().toString()) {
@@ -337,12 +349,15 @@ export var useQueryState = function useQueryState() {
337
349
  setDraftExpression(newExpression);
338
350
  }, [draftProfileName]);
339
351
 
340
- // Set ProfileSelection (auto-commits to URL immediately)
352
+ // Set ProfileSelection (auto-commits to URL immediately). Push a history
353
+ // entry so selecting a sample is a back-navigable step (default is 'replace').
341
354
  var setProfileSelection = useCallback(function (mergeFrom_0, mergeTo_0, query_0) {
342
355
  void setQueryParams({
343
356
  selection: query_0.toString(),
344
357
  merge_from: mergeFrom_0.toString(),
345
358
  merge_to: mergeTo_0.toString()
359
+ }, {
360
+ history: 'push'
346
361
  });
347
362
  }, [setQueryParams]);
348
363
  var draftParsedQuery = useMemo(function () {
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.19.156",
3
+ "version": "0.19.158",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@floating-ui/react": "^0.27.12",
7
7
  "@headlessui/react": "^1.7.19",
8
8
  "@iconify/react": "^4.0.0",
9
9
  "@parca/client": "0.17.24",
10
- "@parca/components": "0.16.421",
10
+ "@parca/components": "0.16.422",
11
11
  "@parca/dynamicsize": "0.16.75",
12
12
  "@parca/hooks": "0.0.128",
13
13
  "@parca/icons": "0.16.83",
@@ -89,5 +89,5 @@
89
89
  "access": "public",
90
90
  "registry": "https://registry.npmjs.org/"
91
91
  },
92
- "gitHead": "a46796c75671cdd66351103a30b0321f8213debc"
92
+ "gitHead": "8f6bee48fed3dd9197eb9696b5e3bfe8d92e34cf"
93
93
  }
@@ -105,11 +105,20 @@ export const useGraphTooltipMetaInfo = ({table, row}: Props): GraphTooltipMetaIn
105
105
 
106
106
  const {dashboardItems, setDashboardItems} = useDashboardItems();
107
107
 
108
- const [_unusedBuildId, setSourceBuildId] = useQueryState('source_buildid', stringParam);
108
+ const [_unusedBuildId, setSourceBuildId] = useQueryState(
109
+ 'source_buildid',
110
+ stringParam.withOptions({history: 'push'})
111
+ );
109
112
 
110
- const [_unusedFilename, setSourceFilename] = useQueryState('source_filename', stringParam);
113
+ const [_unusedFilename, setSourceFilename] = useQueryState(
114
+ 'source_filename',
115
+ stringParam.withOptions({history: 'push'})
116
+ );
111
117
 
112
- const [_unusedLine, setSourceLine] = useQueryState('source_line', stringParam);
118
+ const [_unusedLine, setSourceLine] = useQueryState(
119
+ 'source_line',
120
+ stringParam.withOptions({history: 'push'})
121
+ );
113
122
 
114
123
  const openFile = (): void => {
115
124
  setDashboardItems([dashboardItems[0], 'source']);
@@ -89,7 +89,7 @@ const ContextMenu = ({
89
89
  const {dashboardItems, setDashboardItems} = useDashboardItems();
90
90
  const [_sandwichFunctionName, setSandwichFunctionName] = useQueryState(
91
91
  'sandwich_function_name',
92
- stringParam
92
+ stringParam.withOptions({history: 'push'})
93
93
  );
94
94
 
95
95
  if (contextMenuData === null) {
@@ -113,7 +113,7 @@ const ProfileSelector = ({
113
113
  const {externalProfilerComponent, additionalMetricsGraph} = useParcaContext();
114
114
  const [queryBrowserMode, setRawQueryBrowserMode] = useNuqsQueryState(
115
115
  'query_browser_mode',
116
- stringParam
116
+ stringParam.withOptions({history: 'replace'})
117
117
  );
118
118
  const setQueryBrowserMode = useCallback(
119
119
  (mode: string | null) => {
@@ -26,7 +26,7 @@ import {useProfileViewContext} from '../../context/ProfileViewContext';
26
26
  const SortByDropdown = (): React.JSX.Element => {
27
27
  const [storeSortBy, setStoreSortBy] = useQueryState(
28
28
  'sort_by',
29
- stringParam.withDefault(FIELD_FUNCTION_NAME)
29
+ stringParam.withDefault(FIELD_FUNCTION_NAME).withOptions({history: 'push'})
30
30
  );
31
31
 
32
32
  const {compareMode} = useProfileViewContext();
@@ -21,7 +21,10 @@ import {invertCallStackParser} from '../../../hooks/urlParsers';
21
21
  import {useResetFlameGraphState} from '../../hooks/useResetFlameGraphState';
22
22
 
23
23
  const InvertCallStack = (): JSX.Element => {
24
- const [isInvert, setInvertStack] = useQueryState('invert_call_stack', invertCallStackParser);
24
+ const [isInvert, setInvertStack] = useQueryState(
25
+ 'invert_call_stack',
26
+ invertCallStackParser.withOptions({history: 'push'})
27
+ );
25
28
  const resetFlameGraphState = useResetFlameGraphState();
26
29
 
27
30
  const handleSetInvert = (value: boolean): void => {
@@ -213,11 +213,11 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
213
213
  const [storeSortBy] = useQueryState('sort_by', stringParam.withDefault(FIELD_FUNCTION_NAME));
214
214
  const [colorStackLegend, setStoreColorStackLegend] = useQueryState(
215
215
  'color_stack_legend',
216
- stringParam
216
+ stringParam.withOptions({history: 'push'})
217
217
  );
218
218
  const [hiddenBinaries, setHiddenBinaries] = useQueryState(
219
219
  'hidden_binaries',
220
- hiddenBinariesParser
220
+ hiddenBinariesParser.withOptions({history: 'push'})
221
221
  );
222
222
  const {compareMode} = useProfileViewContext();
223
223
  const [colorProfileName] = useUserPreference<string>(
@@ -230,7 +230,10 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
230
230
  // For non-delta profiles, like goroutines or memory, we want the profiles to be compared absolutely.
231
231
  const compareAbsoluteDefault = profileType?.delta === false;
232
232
 
233
- const [compareAbsolute, setCompareAbsolute] = useQueryState('compare_absolute', boolParam);
233
+ const [compareAbsolute, setCompareAbsolute] = useQueryState(
234
+ 'compare_absolute',
235
+ boolParam.withOptions({history: 'push'})
236
+ );
234
237
  const isCompareAbsolute = compareAbsolute ?? compareAbsoluteDefault;
235
238
 
236
239
  useEffect(() => {
@@ -33,7 +33,10 @@ interface Props {
33
33
 
34
34
  const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Element => {
35
35
  const {compareMode} = useProfileViewContext();
36
- const [tableColumns, setTableColumns] = useQueryState('table_columns', tableColumnsParser);
36
+ const [tableColumns, setTableColumns] = useQueryState(
37
+ 'table_columns',
38
+ tableColumnsParser.withOptions({history: 'push'})
39
+ );
37
40
 
38
41
  const columnHelper = createColumnHelper<Row>();
39
42
 
@@ -28,7 +28,10 @@ interface Props {
28
28
 
29
29
  const ViewSelector = ({profileSource}: Props): JSX.Element => {
30
30
  const {dashboardItems, setDashboardItems} = useDashboardItems();
31
- const [, setSandwichFunctionName] = useQueryState('sandwich_function_name', stringParam);
31
+ const [, setSandwichFunctionName] = useQueryState(
32
+ 'sandwich_function_name',
33
+ stringParam.withOptions({history: 'replace'})
34
+ );
32
35
  const {enableSourcesView, enableSandwichView} = useParcaContext();
33
36
 
34
37
  const allItems: Array<{
@@ -30,7 +30,10 @@ const DashboardContext = createContext<DashboardContextType | undefined>(undefin
30
30
 
31
31
  export const DashboardProvider: FC<PropsWithChildren> = ({children}) => {
32
32
  const {dashboardItems, setDashboardItems} = useDashboardItems();
33
- const [, setSandwichFunctionName] = useQueryState('sandwich_function_name', stringParam);
33
+ const [, setSandwichFunctionName] = useQueryState(
34
+ 'sandwich_function_name',
35
+ stringParam.withOptions({history: 'replace'})
36
+ );
34
37
 
35
38
  const handleClosePanel = (visualizationType: VisualizationType): void => {
36
39
  const newDashboardItems = dashboardItems.filter(item => item !== visualizationType);
@@ -16,7 +16,10 @@ import {useQueryState} from 'nuqs';
16
16
  import {stringParam} from '../../hooks/urlParsers';
17
17
 
18
18
  export const useResetFlameGraphState = (): (() => void) => {
19
- const [val, setCurPath] = useQueryState('cur_path', stringParam);
19
+ const [val, setCurPath] = useQueryState(
20
+ 'cur_path',
21
+ stringParam.withOptions({history: 'replace'})
22
+ );
20
23
 
21
24
  return () => {
22
25
  setTimeout(() => {
@@ -58,7 +58,7 @@ export const useVisualizationState = (): {
58
58
 
59
59
  const [curPathArrow, setRawCurPathArrow] = useQueryState(
60
60
  'cur_path',
61
- jsonParser<CurrentPathFrame[]>().withDefault([])
61
+ jsonParser<CurrentPathFrame[]>().withDefault([]).withOptions({history: 'push'})
62
62
  );
63
63
  const setCurPathArrow = useCallback(
64
64
  (path: CurrentPathFrame[]) => {
@@ -70,16 +70,18 @@ export const useVisualizationState = (): {
70
70
  const {colorBy, setColorBy} = useColorBy();
71
71
  const [alignFunctionNameRaw, setStoreAlignFunctionName] = useQueryState(
72
72
  'align_function_name',
73
- stringParam
73
+ stringParam.withOptions({history: 'push'})
74
74
  );
75
75
  const alignFunctionName = alignFunctionNameRaw ?? alignFunctionNamePreference ?? 'left';
76
76
  const [groupBy, setStoreGroupBy] = useQueryState(
77
77
  'group_by',
78
- groupByParser.withDefault([FIELD_FUNCTION_NAME])
78
+ groupByParser.withDefault([FIELD_FUNCTION_NAME]).withOptions({history: 'push'})
79
79
  );
80
+ // Shared with resetSandwichFunctionName below, so keep the default 'replace';
81
+ // user "show in sandwich" actions push from the Table/flamegraph menus.
80
82
  const [sandwichFunctionName, setRawSandwichFunctionName] = useQueryState(
81
83
  'sandwich_function_name',
82
- stringParam
84
+ stringParam.withOptions({history: 'replace'})
83
85
  );
84
86
  const setSandwichFunctionName = useCallback(
85
87
  (name: string | null) => {
@@ -89,7 +91,7 @@ export const useVisualizationState = (): {
89
91
  );
90
92
  const [flamechartDimension, setStoreFlamechartDimension] = useQueryState(
91
93
  'flamechart_dimension',
92
- flamechartDimensionParser.withDefault([])
94
+ flamechartDimensionParser.withDefault([]).withOptions({history: 'push'})
93
95
  );
94
96
  const resetFlameGraphState = useResetFlameGraphState();
95
97
 
@@ -40,7 +40,7 @@ interface LineRange {
40
40
  const useLineRange = (): LineRange => {
41
41
  const [lineRange, setRawLineRange] = useQueryState(
42
42
  'source_line',
43
- lineRangeParser.withDefault({start: -1, end: -1})
43
+ lineRangeParser.withDefault({start: -1, end: -1}).withOptions({history: 'push'})
44
44
  );
45
45
 
46
46
  const setLineRange = useCallback(
@@ -21,7 +21,10 @@ import {stringParam} from '../hooks/urlParsers';
21
21
  import {useDashboardItems} from '../hooks/useDashboardItems';
22
22
 
23
23
  const MoreDropdown = ({functionName}: {functionName: string}): React.JSX.Element | null => {
24
- const [_, setSandwichFunctionName] = useQueryState('sandwich_function_name', stringParam);
24
+ const [_, setSandwichFunctionName] = useQueryState(
25
+ 'sandwich_function_name',
26
+ stringParam.withOptions({history: 'push'})
27
+ );
25
28
  const {dashboardItems, setDashboardItems} = useDashboardItems();
26
29
  const {enableSandwichView} = useParcaContext();
27
30
 
@@ -45,7 +45,10 @@ const TableContextMenu = ({
45
45
  totalUnfiltered,
46
46
  columnVisibility,
47
47
  }: TableContextMenuProps): React.JSX.Element => {
48
- const [_, setSandwichFunctionName] = useQueryState('sandwich_function_name', stringParam);
48
+ const [_, setSandwichFunctionName] = useQueryState(
49
+ 'sandwich_function_name',
50
+ stringParam.withOptions({history: 'push'})
51
+ );
49
52
  const {dashboardItems, setDashboardItems} = useDashboardItems();
50
53
  const {enableSandwichView, isDarkMode} = useParcaContext();
51
54
 
@@ -76,7 +76,10 @@ export const Table = React.memo(function Table({
76
76
  }: TableProps): React.JSX.Element {
77
77
  const currentColorProfile = useCurrentColorProfile();
78
78
  const {dashboardItems} = useDashboardItems();
79
- const [_, setSandwichFunctionName] = useQueryState('sandwich_function_name', stringParam);
79
+ const [_, setSandwichFunctionName] = useQueryState(
80
+ 'sandwich_function_name',
81
+ stringParam.withOptions({history: 'push'})
82
+ );
80
83
  const {colorBy, setColorBy} = useColorBy();
81
84
  const {isDarkMode} = useParcaContext();
82
85
  const {compareMode} = useProfileViewContext();
@@ -26,7 +26,10 @@ export const useColorBy = (): {
26
26
  const [colorByPreference, setColorByPreference] = useUserPreference<string>(
27
27
  USER_PREFERENCES.COLOR_BY.key
28
28
  );
29
- const [colorByRaw, setRawColorBy] = useQueryState('color_by', stringParam);
29
+ const [colorByRaw, setRawColorBy] = useQueryState(
30
+ 'color_by',
31
+ stringParam.withOptions({history: 'push'})
32
+ );
30
33
 
31
34
  const colorBy = colorByRaw ?? colorByPreference ?? 'binary';
32
35
 
@@ -79,7 +79,7 @@ interface UseQueryStateReturn {
79
79
  }
80
80
 
81
81
  export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryStateReturn => {
82
- const {queryServiceClient: queryClient} = useParcaContext();
82
+ const {queryServiceClient: queryClient, externalProfilerComponent} = useParcaContext();
83
83
  const {
84
84
  suffix = '',
85
85
  defaultExpression = '',
@@ -139,7 +139,10 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
139
139
  [setQueryParams]
140
140
  );
141
141
 
142
- const [, setRawGroupByParam] = useNuqsQueryState('group_by', commaArrayParam);
142
+ const [, setRawGroupByParam] = useNuqsQueryState(
143
+ 'group_by',
144
+ commaArrayParam.withOptions({history: 'replace'})
145
+ );
143
146
  const setGroupByParam = useCallback(
144
147
  (val: string[] | null) => {
145
148
  void setRawGroupByParam(val);
@@ -150,6 +153,13 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
150
153
  // Parse sumBy from URL parameter format
151
154
  const sumBy = useSumByFromParams(sumByParam ?? undefined);
152
155
 
156
+ // Fall back to the externally-provided sumBy default when the URL carries no
157
+ // sum_by, so the materialized value reflects it instead of the empty __none__
158
+ // sentinel (an explicit __none__ from the URL is left untouched).
159
+ const externalSumBy = externalProfilerComponent?.defaultSumBy;
160
+ const sumByWithViewDefault =
161
+ sumBy ?? (externalSumBy != null && externalSumBy.length > 0 ? externalSumBy : undefined);
162
+
153
163
  // Draft state management
154
164
  const [draftExpression, setDraftExpression] = useState<string>(expression ?? defaultExpression);
155
165
  const [draftFrom, setDraftFrom] = useState<string>(from ?? defaultFrom?.toString() ?? '');
@@ -206,7 +216,7 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
206
216
  draftTimeRange,
207
217
  draftProfileType,
208
218
  draftTimeRange,
209
- sumBy
219
+ sumByWithViewDefault
210
220
  );
211
221
 
212
222
  // Sync draft state with URL state when URL changes externally
@@ -388,17 +398,21 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
388
398
  }
389
399
  }
390
400
 
391
- // Atomic URL update with all params at once
392
- void setQueryParams({
393
- expression: finalExpression,
394
- from: finalFrom,
395
- to: finalTo,
396
- time_selection: finalTimeSelection,
397
- sum_by: sumByValue,
398
- merge_from: mergeFromValue,
399
- merge_to: mergeToValue,
400
- selection: selectionValue,
401
- });
401
+ // Atomic URL update with all params at once. Push a history entry so the
402
+ // back button steps through profile/time selections (default is 'replace').
403
+ void setQueryParams(
404
+ {
405
+ expression: finalExpression,
406
+ from: finalFrom,
407
+ to: finalTo,
408
+ time_selection: finalTimeSelection,
409
+ sum_by: sumByValue,
410
+ merge_from: mergeFromValue,
411
+ merge_to: mergeToValue,
412
+ selection: selectionValue,
413
+ },
414
+ {history: 'push'}
415
+ );
402
416
 
403
417
  resetFlameGraphState();
404
418
  if (
@@ -464,14 +478,18 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
464
478
  [draftProfileName]
465
479
  );
466
480
 
467
- // Set ProfileSelection (auto-commits to URL immediately)
481
+ // Set ProfileSelection (auto-commits to URL immediately). Push a history
482
+ // entry so selecting a sample is a back-navigable step (default is 'replace').
468
483
  const setProfileSelection = useCallback(
469
484
  (mergeFrom: bigint, mergeTo: bigint, query: Query) => {
470
- void setQueryParams({
471
- selection: query.toString(),
472
- merge_from: mergeFrom.toString(),
473
- merge_to: mergeTo.toString(),
474
- });
485
+ void setQueryParams(
486
+ {
487
+ selection: query.toString(),
488
+ merge_from: mergeFrom.toString(),
489
+ merge_to: mergeTo.toString(),
490
+ },
491
+ {history: 'push'}
492
+ );
475
493
  },
476
494
  [setQueryParams]
477
495
  );