@parca/profile 0.19.139 → 0.19.140

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 (132) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.d.ts.map +1 -1
  3. package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.js +11 -13
  4. package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts.map +1 -1
  5. package/dist/ProfileExplorer/ProfileExplorerCompare.js +4 -9
  6. package/dist/ProfileFlameChart/SamplesStrips/index.d.ts +2 -2
  7. package/dist/ProfileFlameChart/SamplesStrips/index.d.ts.map +1 -1
  8. package/dist/ProfileFlameChart/index.d.ts.map +1 -1
  9. package/dist/ProfileFlameChart/index.js +13 -19
  10. package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.d.ts.map +1 -1
  11. package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.js +8 -8
  12. package/dist/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.d.ts.map +1 -1
  13. package/dist/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.js +4 -3
  14. package/dist/ProfileFlameGraph/index.d.ts.map +1 -1
  15. package/dist/ProfileFlameGraph/index.js +6 -4
  16. package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
  17. package/dist/ProfileMetricsGraph/index.js +4 -6
  18. package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
  19. package/dist/ProfileSelector/MetricsGraphSection.js +5 -10
  20. package/dist/ProfileSelector/index.d.ts.map +1 -1
  21. package/dist/ProfileSelector/index.js +27 -25
  22. package/dist/ProfileView/components/ActionButtons/SortByDropdown.d.ts.map +1 -1
  23. package/dist/ProfileView/components/ActionButtons/SortByDropdown.js +5 -5
  24. package/dist/ProfileView/components/ColorStackLegend.d.ts.map +1 -1
  25. package/dist/ProfileView/components/ColorStackLegend.js +2 -3
  26. package/dist/ProfileView/components/InvertCallStack/index.d.ts.map +1 -1
  27. package/dist/ProfileView/components/InvertCallStack/index.js +5 -4
  28. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts +1 -2
  29. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts.map +1 -1
  30. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.js +14 -16
  31. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.test.js +84 -170
  32. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts.map +1 -1
  33. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.js +16 -20
  34. package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.d.ts.map +1 -1
  35. package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.js +4 -5
  36. package/dist/ProfileView/components/Toolbars/index.d.ts +2 -2
  37. package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
  38. package/dist/ProfileView/components/Toolbars/index.js +1 -1
  39. package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
  40. package/dist/ProfileView/components/ViewSelector/index.js +8 -14
  41. package/dist/ProfileView/context/DashboardContext.d.ts.map +1 -1
  42. package/dist/ProfileView/context/DashboardContext.js +6 -6
  43. package/dist/ProfileView/hooks/useResetFlameGraphState.d.ts.map +1 -1
  44. package/dist/ProfileView/hooks/useResetFlameGraphState.js +5 -4
  45. package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.d.ts.map +1 -1
  46. package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.js +25 -26
  47. package/dist/ProfileView/hooks/useResetStateOnSeriesChange.d.ts.map +1 -1
  48. package/dist/ProfileView/hooks/useResetStateOnSeriesChange.js +13 -8
  49. package/dist/ProfileView/hooks/useVisualizationState.d.ts +3 -3
  50. package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -1
  51. package/dist/ProfileView/hooks/useVisualizationState.js +35 -51
  52. package/dist/ProfileViewWithData.d.ts.map +1 -1
  53. package/dist/ProfileViewWithData.js +19 -28
  54. package/dist/Sandwich/index.d.ts.map +1 -1
  55. package/dist/Sandwich/index.js +4 -3
  56. package/dist/SourceView/index.d.ts.map +1 -1
  57. package/dist/SourceView/index.js +4 -2
  58. package/dist/SourceView/useSelectedLineRange.d.ts.map +1 -1
  59. package/dist/SourceView/useSelectedLineRange.js +21 -16
  60. package/dist/Table/MoreDropdown.d.ts.map +1 -1
  61. package/dist/Table/MoreDropdown.js +8 -11
  62. package/dist/Table/TableContextMenu.d.ts.map +1 -1
  63. package/dist/Table/TableContextMenu.js +10 -13
  64. package/dist/Table/hooks/useTableConfiguration.d.ts.map +1 -1
  65. package/dist/Table/hooks/useTableConfiguration.js +3 -4
  66. package/dist/Table/index.d.ts.map +1 -1
  67. package/dist/Table/index.js +11 -9
  68. package/dist/TopTable/index.d.ts.map +1 -1
  69. package/dist/TopTable/index.js +3 -4
  70. package/dist/hooks/urlParsers.d.ts +18 -0
  71. package/dist/hooks/urlParsers.d.ts.map +1 -0
  72. package/dist/hooks/urlParsers.js +32 -0
  73. package/dist/hooks/useColorBy.d.ts +5 -0
  74. package/dist/hooks/useColorBy.d.ts.map +1 -0
  75. package/dist/hooks/useColorBy.js +26 -0
  76. package/dist/hooks/useCompareModeMeta.d.ts.map +1 -1
  77. package/dist/hooks/useCompareModeMeta.js +55 -86
  78. package/dist/hooks/useDashboardItems.d.ts +5 -0
  79. package/dist/hooks/useDashboardItems.d.ts.map +1 -0
  80. package/dist/hooks/useDashboardItems.js +27 -0
  81. package/dist/hooks/useQueryState.d.ts +3 -3
  82. package/dist/hooks/useQueryState.d.ts.map +1 -1
  83. package/dist/hooks/useQueryState.js +105 -105
  84. package/dist/hooks/useQueryState.test.js +186 -302
  85. package/dist/index.d.ts +3 -2
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +3 -12
  88. package/dist/useSumBy.d.ts +1 -1
  89. package/dist/useSumBy.d.ts.map +1 -1
  90. package/dist/useSumBy.js +2 -2
  91. package/package.json +8 -7
  92. package/src/GraphTooltipArrow/useGraphTooltipMetaInfo/index.ts +11 -13
  93. package/src/ProfileExplorer/ProfileExplorerCompare.tsx +4 -9
  94. package/src/ProfileFlameChart/SamplesStrips/index.tsx +2 -2
  95. package/src/ProfileFlameChart/index.tsx +21 -28
  96. package/src/ProfileFlameGraph/FlameGraphArrow/ContextMenu.tsx +10 -9
  97. package/src/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.tsx +5 -3
  98. package/src/ProfileFlameGraph/index.tsx +6 -9
  99. package/src/ProfileMetricsGraph/index.tsx +6 -8
  100. package/src/ProfileSelector/MetricsGraphSection.tsx +5 -10
  101. package/src/ProfileSelector/index.tsx +32 -31
  102. package/src/ProfileView/components/ActionButtons/SortByDropdown.tsx +10 -6
  103. package/src/ProfileView/components/ColorStackLegend.tsx +2 -4
  104. package/src/ProfileView/components/InvertCallStack/index.tsx +5 -4
  105. package/src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.test.tsx +94 -192
  106. package/src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.ts +21 -21
  107. package/src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx +24 -25
  108. package/src/ProfileView/components/Toolbars/TableColumnsDropdown.tsx +4 -5
  109. package/src/ProfileView/components/Toolbars/index.tsx +3 -3
  110. package/src/ProfileView/components/ViewSelector/index.tsx +9 -16
  111. package/src/ProfileView/context/DashboardContext.tsx +6 -6
  112. package/src/ProfileView/hooks/useResetFlameGraphState.ts +6 -4
  113. package/src/ProfileView/hooks/useResetStateOnProfileTypeChange.ts +24 -26
  114. package/src/ProfileView/hooks/useResetStateOnSeriesChange.ts +16 -8
  115. package/src/ProfileView/hooks/useVisualizationState.ts +61 -69
  116. package/src/ProfileViewWithData.tsx +29 -35
  117. package/src/Sandwich/index.tsx +4 -3
  118. package/src/SourceView/index.tsx +4 -2
  119. package/src/SourceView/useSelectedLineRange.ts +34 -19
  120. package/src/Table/MoreDropdown.tsx +9 -11
  121. package/src/Table/TableContextMenu.tsx +10 -13
  122. package/src/Table/hooks/useTableConfiguration.tsx +3 -4
  123. package/src/Table/index.tsx +12 -21
  124. package/src/TopTable/index.tsx +3 -4
  125. package/src/hooks/urlParsers.ts +38 -0
  126. package/src/hooks/useColorBy.ts +42 -0
  127. package/src/hooks/useCompareModeMeta.ts +61 -91
  128. package/src/hooks/useDashboardItems.ts +46 -0
  129. package/src/hooks/useQueryState.test.tsx +275 -345
  130. package/src/hooks/useQueryState.ts +136 -118
  131. package/src/index.tsx +16 -15
  132. package/src/useSumBy.ts +3 -3
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { ParamPreferences } from '@parca/components';
2
1
  import MatchersInput from './MatchersInput';
3
2
  import MetricsGraph, { type ContextMenuItemOrSubmenu, type Series } from './MetricsGraph';
4
3
  import ProfileExplorer from './ProfileExplorer';
@@ -25,7 +24,9 @@ export * from './useSumBy';
25
24
  export { QueryControls } from './QueryControls';
26
25
  export { default as ProfileFilters } from './ProfileView/components/ProfileFilters';
27
26
  export { useProfileFiltersUrlState } from './ProfileView/components/ProfileFilters/useProfileFiltersUrlState';
28
- export declare const DEFAULT_PROFILE_EXPLORER_PARAM_VALUES: ParamPreferences;
29
27
  export { useProfileTypes } from './ProfileSelector';
28
+ export { stringParam, boolParam, intParam, commaArrayParam, invertCallStackParser, groupByParser, flamechartDimensionParser, tableColumnsParser, hiddenBinariesParser, jsonParser, } from './hooks/urlParsers';
29
+ export { useDashboardItems } from './hooks/useDashboardItems';
30
+ export { useColorBy } from './hooks/useColorBy';
30
31
  export { ProfileExplorer, ProfileTypeSelector, CustomSelect, SelectWithRefresh, useLabelNames, MetricsGraph, SimpleMatchers, MatchersInput, type ContextMenuItemOrSubmenu, type Series, LabelsQueryProvider, useLabelsQueryProvider, UnifiedLabelsProvider, useUnifiedLabels, useQueryState, type LabelsQueryProviderContextType, };
31
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAC;AAExD,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,OAAO,YAAY,EAAE,EAAC,KAAK,wBAAwB,EAAE,KAAK,MAAM,EAAC,MAAM,gBAAgB,CAAC;AACxF,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAChD,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AACtD,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,YAAY,MAAM,yBAAyB,CAAC;AACnD,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,8BAA8B,EACpC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAC,qBAAqB,EAAE,gBAAgB,EAAC,MAAM,iCAAiC,CAAC;AACxF,OAAO,EAAC,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAC,aAAa,EAAC,MAAM,uBAAuB,CAAC;AAOpD,OAAO,EAAC,yBAAyB,EAAC,MAAM,0CAA0C,CAAC;AAEnF,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,KAAK,aAAa,GACnB,MAAM,2DAA2D,CAAC;AACnE,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAC,OAAO,IAAI,cAAc,EAAC,MAAM,yCAAyC,CAAC;AAClF,OAAO,EAAC,yBAAyB,EAAC,MAAM,mEAAmE,CAAC;AAE5G,eAAO,MAAM,qCAAqC,EAAE,gBAWnD,CAAC;AAEF,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAElD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,KAAK,wBAAwB,EAC7B,KAAK,MAAM,EACX,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,KAAK,8BAA8B,GACpC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAgBA,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,OAAO,YAAY,EAAE,EAAC,KAAK,wBAAwB,EAAE,KAAK,MAAM,EAAC,MAAM,gBAAgB,CAAC;AACxF,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAChD,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AACtD,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,YAAY,MAAM,yBAAyB,CAAC;AACnD,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,8BAA8B,EACpC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAC,qBAAqB,EAAE,gBAAgB,EAAC,MAAM,iCAAiC,CAAC;AACxF,OAAO,EAAC,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAC,aAAa,EAAC,MAAM,uBAAuB,CAAC;AAOpD,OAAO,EAAC,yBAAyB,EAAC,MAAM,0CAA0C,CAAC;AAEnF,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,KAAK,aAAa,GACnB,MAAM,2DAA2D,CAAC;AACnE,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAC,OAAO,IAAI,cAAc,EAAC,MAAM,yCAAyC,CAAC;AAClF,OAAO,EAAC,yBAAyB,EAAC,MAAM,mEAAmE,CAAC;AAE5G,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAElD,OAAO,EACL,WAAW,EACX,SAAS,EACT,QAAQ,EACR,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,yBAAyB,EACzB,kBAAkB,EAClB,oBAAoB,EACpB,UAAU,GACX,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAC,iBAAiB,EAAC,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,KAAK,wBAAwB,EAC7B,KAAK,MAAM,EACX,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,KAAK,8BAA8B,GACpC,CAAC"}
package/dist/index.js CHANGED
@@ -42,17 +42,8 @@ export * from './useSumBy';
42
42
  export { QueryControls } from './QueryControls';
43
43
  export { default as ProfileFilters } from './ProfileView/components/ProfileFilters';
44
44
  export { useProfileFiltersUrlState } from './ProfileView/components/ProfileFilters/useProfileFiltersUrlState';
45
- export const DEFAULT_PROFILE_EXPLORER_PARAM_VALUES = {
46
- dashboard_items: {
47
- defaultValue: 'flamegraph',
48
- splitOnCommas: true, // This param should split on commas for array values
49
- },
50
- group_by: {
51
- splitOnCommas: true,
52
- },
53
- flamechart_dimension: {
54
- splitOnCommas: true,
55
- },
56
- };
57
45
  export { useProfileTypes } from './ProfileSelector';
46
+ export { stringParam, boolParam, intParam, commaArrayParam, invertCallStackParser, groupByParser, flamechartDimensionParser, tableColumnsParser, hiddenBinariesParser, jsonParser, } from './hooks/urlParsers';
47
+ export { useDashboardItems } from './hooks/useDashboardItems';
48
+ export { useColorBy } from './hooks/useColorBy';
58
49
  export { ProfileExplorer, ProfileTypeSelector, CustomSelect, SelectWithRefresh, useLabelNames, MetricsGraph, SimpleMatchers, MatchersInput, LabelsQueryProvider, useLabelsQueryProvider, UnifiedLabelsProvider, useUnifiedLabels, useQueryState, };
@@ -12,7 +12,7 @@ export declare const useDefaultSumBy: (profileType: ProfileType | undefined, lab
12
12
  isLoading: boolean;
13
13
  };
14
14
  export declare const useSumByFromParams: (param: string | string[] | undefined) => string[] | undefined;
15
- export declare const sumByToParam: (sumBy: string[] | undefined) => string | string[] | undefined;
15
+ export declare const sumByToParam: (sumBy: string[] | undefined) => string | null;
16
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;
@@ -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,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,CA2EF,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;CAyB9B,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,CA2EF,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,IAUnE,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;CAyB9B,CAAC"}
package/dist/useSumBy.js CHANGED
@@ -122,12 +122,12 @@ export const useSumByFromParams = (param) => {
122
122
  };
123
123
  export const sumByToParam = (sumBy) => {
124
124
  if (sumBy === undefined) {
125
- return undefined;
125
+ return null;
126
126
  }
127
127
  if (sumBy.length === 0) {
128
128
  return '__none__';
129
129
  }
130
- return sumBy;
130
+ return sumBy.join(',');
131
131
  };
132
132
  // Combined hook that handles all sumBy logic: fetching labels, computing defaults, and managing selection
133
133
  export const useSumBy = (queryClient, profileType, timeRange, draftProfileType, draftTimeRange, defaultValue) => {
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.19.139",
3
+ "version": "0.19.140",
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.21",
10
- "@parca/components": "0.16.411",
9
+ "@parca/client": "0.17.22",
10
+ "@parca/components": "0.16.412",
11
11
  "@parca/dynamicsize": "0.16.74",
12
- "@parca/hooks": "0.0.122",
12
+ "@parca/hooks": "0.0.123",
13
13
  "@parca/icons": "0.16.81",
14
14
  "@parca/parser": "0.16.88",
15
- "@parca/store": "0.16.205",
15
+ "@parca/store": "0.16.206",
16
16
  "@parca/test-utils": "0.0.22",
17
- "@parca/utilities": "0.0.128",
17
+ "@parca/utilities": "0.0.129",
18
18
  "@popperjs/core": "^2.11.8",
19
19
  "@protobuf-ts/runtime-rpc": "^2.5.0",
20
20
  "@storybook/preview-api": "^8.4.3",
@@ -42,6 +42,7 @@
42
42
  "graphviz-wasm": "3.0.2",
43
43
  "lodash.throttle": "^4.1.1",
44
44
  "lz4js": "^0.2.0",
45
+ "nuqs": "^2.4.1",
45
46
  "react": "18.3.1",
46
47
  "react-beautiful-dnd": "^13.1.1",
47
48
  "react-contexify": "^6.0.0",
@@ -88,5 +89,5 @@
88
89
  "access": "public",
89
90
  "registry": "https://registry.npmjs.org/"
90
91
  },
91
- "gitHead": "df52a613ad81f6c443111c0e134df376f8ba296f"
92
+ "gitHead": "0ba0f2c0c43470e6b3e84e6a63299c42ff91e47b"
92
93
  }
@@ -12,9 +12,10 @@
12
12
  // limitations under the License.
13
13
 
14
14
  import {Table} from '@uwdata/flechette';
15
+ import {useQueryState} from 'nuqs';
15
16
 
16
17
  import {QueryRequest_ReportType} from '@parca/client';
17
- import {useParcaContext, useURLState} from '@parca/components';
18
+ import {useParcaContext} from '@parca/components';
18
19
 
19
20
  import {
20
21
  FIELD_FUNCTION_FILE_NAME,
@@ -30,6 +31,8 @@ import {
30
31
  import {arrowToString} from '../../ProfileFlameGraph/FlameGraphArrow/utils';
31
32
  import {ProfileSource} from '../../ProfileSource';
32
33
  import {useProfileViewContext} from '../../ProfileView/context/ProfileViewContext';
34
+ import {stringParam} from '../../hooks/urlParsers';
35
+ import {useDashboardItems} from '../../hooks/useDashboardItems';
33
36
  import {useQuery} from '../../useQuery';
34
37
 
35
38
  interface Props {
@@ -107,28 +110,23 @@ export const useGraphTooltipMetaInfo = ({table, row}: Props): GraphTooltipMetaIn
107
110
  ])
108
111
  .filter(value => value[1] !== '') as Array<[string, string]>;
109
112
 
110
- const [dashboardItems, setDashboardItems] = useURLState<string[]>('dashboard_items', {
111
- alwaysReturnArray: true,
112
- });
113
+ const {dashboardItems, setDashboardItems} = useDashboardItems();
113
114
 
114
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
115
- const [unusedBuildId, setSourceBuildId] = useURLState('source_buildid');
115
+ const [_unusedBuildId, setSourceBuildId] = useQueryState('source_buildid', stringParam);
116
116
 
117
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
118
- const [unusedFilename, setSourceFilename] = useURLState('source_filename');
117
+ const [_unusedFilename, setSourceFilename] = useQueryState('source_filename', stringParam);
119
118
 
120
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
121
- const [unusedLine, setSourceLine] = useURLState('source_line');
119
+ const [_unusedLine, setSourceLine] = useQueryState('source_line', stringParam);
122
120
 
123
121
  const openFile = (): void => {
124
122
  setDashboardItems([dashboardItems[0], 'source']);
125
123
  if (mappingBuildID != null) {
126
- setSourceBuildId(mappingBuildID);
124
+ void setSourceBuildId(mappingBuildID);
127
125
  }
128
126
 
129
- setSourceFilename(functionFilename);
127
+ void setSourceFilename(functionFilename);
130
128
  if (lineNumber !== undefined) {
131
- setSourceLine(lineNumber.toString());
129
+ void setSourceLine(lineNumber.toString());
132
130
  }
133
131
  };
134
132
 
@@ -14,7 +14,6 @@
14
14
  import {useCallback, useEffect, useMemo, useState} from 'react';
15
15
 
16
16
  import {QueryServiceClient} from '@parca/client';
17
- import {useURLStateBatch} from '@parca/components';
18
17
  import {Query} from '@parca/parser';
19
18
  import {TEST_IDS, testId} from '@parca/test-utils';
20
19
  import type {NavigateFunction} from '@parca/utilities';
@@ -34,7 +33,6 @@ const ProfileExplorerCompare = ({
34
33
  navigateTo,
35
34
  }: ProfileExplorerCompareProps): JSX.Element => {
36
35
  const [showMetricsGraph, setShowMetricsGraph] = useState(true);
37
- const batchUpdates = useURLStateBatch();
38
36
  const {closeCompareMode, isCompareMode, isCompareAbsolute} = useCompareModeMeta();
39
37
 
40
38
  // Read ProfileSource states from URL for both sides
@@ -65,12 +63,10 @@ const ProfileExplorerCompare = ({
65
63
  }
66
64
 
67
65
  if (querySelectionB.expression === '' && querySelectionA.expression !== '') {
68
- batchUpdates(() => {
69
- setDraftExpressionB(querySelectionA.expression);
70
- setDraftTimeRangeB(querySelectionA.from, querySelectionA.to, querySelectionA.timeSelection);
71
- // Commit to update the URL and trigger metrics graph load
72
- commitDraftB();
73
- });
66
+ setDraftExpressionB(querySelectionA.expression);
67
+ setDraftTimeRangeB(querySelectionA.from, querySelectionA.to, querySelectionA.timeSelection);
68
+ // Commit to update the URL and trigger metrics graph load
69
+ commitDraftB();
74
70
  }
75
71
  }, [
76
72
  isCompareMode,
@@ -82,7 +78,6 @@ const ProfileExplorerCompare = ({
82
78
  setDraftExpressionB,
83
79
  setDraftTimeRangeB,
84
80
  commitDraftB,
85
- batchUpdates,
86
81
  ]);
87
82
 
88
83
  const closeProfileA = useCallback((): void => {
@@ -39,10 +39,10 @@ interface Props {
39
39
  loading?: boolean;
40
40
  cpus: LabelSet[];
41
41
  data: DataPoint[][];
42
- selectedTimeframe?: {
42
+ selectedTimeframe: {
43
43
  labels: LabelSet;
44
44
  bounds: NumberDuo;
45
- };
45
+ } | null;
46
46
  onSelectedTimeframe: (labels: LabelSet, bounds: NumberDuo | undefined) => void;
47
47
  width?: number;
48
48
  bounds: NumberDuo;
@@ -13,14 +13,10 @@
13
13
 
14
14
  import {useEffect, useMemo, useRef} from 'react';
15
15
 
16
+ import {createParser, useQueryState} from 'nuqs';
17
+
16
18
  import {LabelSet, QueryRequest_ReportType, QueryServiceClient} from '@parca/client';
17
- import {
18
- Button,
19
- useParcaContext,
20
- useURLState,
21
- useURLStateCustom,
22
- type OptionsCustom,
23
- } from '@parca/components';
19
+ import {Button, useParcaContext} from '@parca/components';
24
20
  import {Matcher, MatcherTypes, ProfileType, Query} from '@parca/parser';
25
21
  import {TimeUnits, formatDate, formatDuration} from '@parca/utilities';
26
22
 
@@ -29,6 +25,7 @@ import {boundsFromProfileSource} from '../ProfileFlameGraph/FlameGraphArrow/util
29
25
  import {MergedProfileSource, ProfileSource, timeFormat} from '../ProfileSource';
30
26
  import {useProfileFilters} from '../ProfileView/components/ProfileFilters/useProfileFilters';
31
27
  import type {SamplesData} from '../ProfileView/types/visualization';
28
+ import {flamechartDimensionParser} from '../hooks/urlParsers';
32
29
  import {useQuery} from '../useQuery';
33
30
  import {NumberDuo} from '../utils';
34
31
  import {SamplesStrip} from './SamplesStrips';
@@ -38,11 +35,8 @@ interface SelectedTimeframe {
38
35
  bounds: NumberDuo;
39
36
  }
40
37
 
41
- const TimeframeStateSerializer: OptionsCustom<SelectedTimeframe | undefined> = {
42
- parse: (value: string | string[] | undefined) => {
43
- if (value == null || value === '' || value === 'undefined' || Array.isArray(value)) {
44
- return undefined;
45
- }
38
+ const timeframeParser = createParser<SelectedTimeframe>({
39
+ parse: (value: string) => {
46
40
  try {
47
41
  const [labelPart, boundsPart] = value.split('|');
48
42
  if (labelPart != null && boundsPart != null) {
@@ -61,16 +55,13 @@ const TimeframeStateSerializer: OptionsCustom<SelectedTimeframe | undefined> = {
61
55
  } catch {
62
56
  // Ignore parsing errors
63
57
  }
64
- return undefined;
58
+ return null;
65
59
  },
66
- stringify: (value: SelectedTimeframe | undefined) => {
67
- if (value == null) {
68
- return '';
69
- }
60
+ serialize: (value: SelectedTimeframe) => {
70
61
  const labelsStr = value.labels.labels.map(l => `${l.name}:${l.value}`).join(',');
71
62
  return `${labelsStr}|${value.bounds[0]},${value.bounds[1]}`;
72
63
  },
73
- };
64
+ }).withOptions({history: 'replace'});
74
65
 
75
66
  interface ProfileFlameChartProps {
76
67
  samplesData?: SamplesData;
@@ -132,14 +123,16 @@ export const ProfileFlameChart = ({
132
123
  const {protoFilters} = useProfileFilters();
133
124
  const zoomControlsRef = useRef<HTMLDivElement>(null);
134
125
 
135
- const [selectedTimeframe, setSelectedTimeframe] = useURLStateCustom<
136
- SelectedTimeframe | undefined
137
- >('flamechart_timeframe', TimeframeStateSerializer);
126
+ const [selectedTimeframe, setSelectedTimeframe] = useQueryState(
127
+ 'flamechart_timeframe',
128
+ timeframeParser
129
+ );
138
130
 
139
131
  // Read flamechart dimension from URL state to detect changes
140
- const [flamechartDimension] = useURLState<string[]>('flamechart_dimension', {
141
- alwaysReturnArray: true,
142
- });
132
+ const [flamechartDimension] = useQueryState(
133
+ 'flamechart_dimension',
134
+ flamechartDimensionParser.withDefault([])
135
+ );
143
136
 
144
137
  // Reset selection when the parent time range (profileSource) changes
145
138
  const timeBoundsKey = boundsFromProfileSource(profileSource).join(',');
@@ -147,7 +140,7 @@ export const ProfileFlameChart = ({
147
140
  useEffect(() => {
148
141
  if (prevTimeBoundsKey.current !== timeBoundsKey) {
149
142
  prevTimeBoundsKey.current = timeBoundsKey;
150
- setSelectedTimeframe(undefined);
143
+ void setSelectedTimeframe(null);
151
144
  }
152
145
  }, [timeBoundsKey, setSelectedTimeframe]);
153
146
 
@@ -157,16 +150,16 @@ export const ProfileFlameChart = ({
157
150
  useEffect(() => {
158
151
  if (prevDimensionKey.current !== dimensionKey) {
159
152
  prevDimensionKey.current = dimensionKey;
160
- setSelectedTimeframe(undefined);
153
+ void setSelectedTimeframe(null);
161
154
  }
162
155
  }, [dimensionKey, setSelectedTimeframe]);
163
156
 
164
157
  // Handle timeframe selection from strips
165
158
  const handleSelectedTimeframe = (labels: LabelSet, bounds: NumberDuo | undefined): void => {
166
159
  if (bounds === undefined) {
167
- setSelectedTimeframe(undefined);
160
+ void setSelectedTimeframe(null);
168
161
  } else {
169
- setSelectedTimeframe({labels, bounds});
162
+ void setSelectedTimeframe({labels, bounds});
170
163
  }
171
164
  };
172
165
 
@@ -14,10 +14,11 @@
14
14
  import {Icon} from '@iconify/react';
15
15
  import {Table} from '@uwdata/flechette';
16
16
  import cx from 'classnames';
17
+ import {useQueryState} from 'nuqs';
17
18
  import {Item, Menu, Separator, Submenu} from 'react-contexify';
18
19
  import {Tooltip} from 'react-tooltip';
19
20
 
20
- import {useParcaContext, useURLState} from '@parca/components';
21
+ import {useParcaContext} from '@parca/components';
21
22
  import {USER_PREFERENCES, useUserPreference} from '@parca/hooks';
22
23
  import {ProfileType} from '@parca/parser';
23
24
  import {TEST_IDS} from '@parca/test-utils';
@@ -25,6 +26,8 @@ import {getLastItem} from '@parca/utilities';
25
26
 
26
27
  import {useGraphTooltip} from '../../GraphTooltipArrow/useGraphTooltip';
27
28
  import {useGraphTooltipMetaInfo} from '../../GraphTooltipArrow/useGraphTooltipMetaInfo';
29
+ import {stringParam} from '../../hooks/urlParsers';
30
+ import {useDashboardItems} from '../../hooks/useDashboardItems';
28
31
  import {hexifyAddress, truncateString} from '../../utils';
29
32
 
30
33
  interface ContextMenuProps {
@@ -83,12 +86,10 @@ const ContextMenu = ({
83
86
  inlined,
84
87
  } = useGraphTooltipMetaInfo({table, row});
85
88
 
86
- const [dashboardItems, setDashboardItems] = useURLState<string[]>('dashboard_items', {
87
- alwaysReturnArray: true,
88
- });
89
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
90
- const [sandwichFunctionName, setSandwichFunctionName] = useURLState<string | undefined>(
91
- 'sandwich_function_name'
89
+ const {dashboardItems, setDashboardItems} = useDashboardItems();
90
+ const [_sandwichFunctionName, setSandwichFunctionName] = useQueryState(
91
+ 'sandwich_function_name',
92
+ stringParam
92
93
  );
93
94
 
94
95
  if (contextMenuData === null) {
@@ -195,12 +196,12 @@ const ContextMenu = ({
195
196
  }
196
197
 
197
198
  if (dashboardItems.includes('sandwich')) {
198
- setSandwichFunctionName(functionName);
199
+ void setSandwichFunctionName(functionName);
199
200
  hideMenu();
200
201
  return;
201
202
  }
202
203
 
203
- setSandwichFunctionName(functionName);
204
+ void setSandwichFunctionName(functionName);
204
205
  setDashboardItems([...dashboardItems, 'sandwich']);
205
206
  hideMenu();
206
207
  }}
@@ -13,7 +13,9 @@
13
13
 
14
14
  import {useEffect, useRef, useState} from 'react';
15
15
 
16
- import {useURLState} from '@parca/components';
16
+ import {useQueryState} from 'nuqs';
17
+
18
+ import {stringParam} from '../../hooks/urlParsers';
17
19
 
18
20
  interface Props {
19
21
  text: string;
@@ -66,9 +68,9 @@ function calculateTruncatedText(
66
68
  function TextWithEllipsis({text, x, y, width}: Props): JSX.Element {
67
69
  const textRef = useRef<SVGTextElement>(null);
68
70
  const [displayText, setDisplayText] = useState(text);
69
- const [alignFunctionName] = useURLState('align_function_name');
71
+ const [alignFunctionName] = useQueryState('align_function_name', stringParam.withDefault('left'));
70
72
 
71
- const showFunctionNameFromLeft = alignFunctionName === 'left' || alignFunctionName === undefined;
73
+ const showFunctionNameFromLeft = alignFunctionName === 'left';
72
74
 
73
75
  useEffect(() => {
74
76
  const textElement = textRef.current;
@@ -15,15 +15,11 @@ import React, {LegacyRef, ReactNode, useCallback, useEffect, useMemo, useState}
15
15
 
16
16
  import cx from 'classnames';
17
17
  import {AnimatePresence, motion} from 'framer-motion';
18
+ import {useQueryState} from 'nuqs';
18
19
  import {useMeasure} from 'react-use';
19
20
 
20
21
  import {FlamegraphArrow} from '@parca/client';
21
- import {
22
- FlameGraphSkeleton,
23
- SandwichFlameGraphSkeleton,
24
- useParcaContext,
25
- useURLState,
26
- } from '@parca/components';
22
+ import {FlameGraphSkeleton, SandwichFlameGraphSkeleton, useParcaContext} from '@parca/components';
27
23
  import {ProfileType} from '@parca/parser';
28
24
  import {TEST_IDS, testId} from '@parca/test-utils';
29
25
  import {capitalizeOnlyFirstLetter, divide} from '@parca/utilities';
@@ -33,6 +29,7 @@ import DiffLegend from '../ProfileView/components/DiffLegend';
33
29
  import {useProfileViewContext} from '../ProfileView/context/ProfileViewContext';
34
30
  import {useProfileMetadata} from '../ProfileView/hooks/useProfileMetadata';
35
31
  import {useVisualizationState} from '../ProfileView/hooks/useVisualizationState';
32
+ import {boolParam} from '../hooks/urlParsers';
36
33
  import {FlameGraphArrow} from './FlameGraphArrow';
37
34
  import {CurrentPathFrame} from './FlameGraphArrow/utils';
38
35
 
@@ -137,8 +134,8 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
137
134
  // For non-delta profiles, like goroutines or memory, we want the profiles to be compared absolutely.
138
135
  const compareAbsoluteDefault = profileType?.delta === false ? 'true' : 'false';
139
136
 
140
- const [compareAbsolute = compareAbsoluteDefault] = useURLState('compare_absolute');
141
- const isCompareAbsolute = compareAbsolute === 'true';
137
+ const [compareAbsolute] = useQueryState('compare_absolute', boolParam);
138
+ const isCompareAbsolute = compareAbsolute ?? compareAbsoluteDefault === 'true';
142
139
 
143
140
  const mappingsListCount = useMemo(
144
141
  () => mappingsList.filter(m => m !== '').length,
@@ -180,7 +177,7 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
180
177
  // If there is only one mapping file, we want to color by filename by default.
181
178
  useEffect(() => {
182
179
  if (mappingsListCount === 1 && colorBy !== 'filename') {
183
- setColorBy('filename');
180
+ void setColorBy('filename');
184
181
  }
185
182
  // eslint-disable-next-line react-hooks/exhaustive-deps
186
183
  }, [mappingsListCount]);
@@ -15,6 +15,7 @@ import {useEffect, useMemo, useState} from 'react';
15
15
 
16
16
  import {Icon} from '@iconify/react';
17
17
  import {AnimatePresence, motion} from 'framer-motion';
18
+ import {useQueryState} from 'nuqs';
18
19
 
19
20
  import {
20
21
  Label,
@@ -25,11 +26,8 @@ import {
25
26
  import {
26
27
  DateTimeRange,
27
28
  MetricsGraphSkeleton,
28
- NumberParser,
29
- NumberSerializer,
30
29
  TextWithTooltip,
31
30
  useParcaContext,
32
- useURLStateCustom,
33
31
  } from '@parca/components';
34
32
  import {Query} from '@parca/parser';
35
33
  import {TEST_IDS, testId} from '@parca/test-utils';
@@ -38,6 +36,7 @@ import {capitalizeOnlyFirstLetter, formatDate, timePattern, valueFormatter} from
38
36
  import {MergedProfileSelection, ProfileSelection} from '..';
39
37
  import MetricsGraph, {ContextMenuItemOrSubmenu, Series, SeriesPoint} from '../MetricsGraph';
40
38
  import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
39
+ import {intParam} from '../hooks/urlParsers';
41
40
  import {getStepCountFromScreenWidth, useQueryRange} from './hooks/useQueryRange';
42
41
 
43
42
  const createProfileContextMenuItems = (
@@ -200,11 +199,10 @@ const ProfileMetricsGraph = ({
200
199
  comparing = false,
201
200
  sumBy,
202
201
  }: ProfileMetricsGraphProps): JSX.Element => {
203
- const [rawStepCount] = useURLStateCustom<number>('step_count', {
204
- defaultValue: String(getStepCountFromScreenWidth(10)),
205
- parse: NumberParser,
206
- stringify: NumberSerializer,
207
- });
202
+ const [rawStepCount] = useQueryState(
203
+ 'step_count',
204
+ intParam.withDefault(getStepCountFromScreenWidth(10))
205
+ );
208
206
  // Clamp step count so the step duration is at least 1 second as we don't have this enforced server-side anymore.
209
207
  const stepCount = useMemo(() => {
210
208
  const maxForOneSecond = Math.floor((to - from) / 1000);
@@ -14,7 +14,7 @@
14
14
  import cx from 'classnames';
15
15
 
16
16
  import {Label, QueryServiceClient} from '@parca/client';
17
- import {DateTimeRange, useParcaContext, useURLStateBatch} from '@parca/components';
17
+ import {DateTimeRange, useParcaContext} from '@parca/components';
18
18
  import {Query} from '@parca/parser';
19
19
 
20
20
  import {ProfileSelection} from '..';
@@ -67,7 +67,6 @@ export function MetricsGraphSection({
67
67
  hasNoProfileTypes = false,
68
68
  }: MetricsGraphSectionProps): JSX.Element {
69
69
  const resetStateOnSeriesChange = useResetStateOnSeriesChange();
70
- const batchUpdates = useURLStateBatch();
71
70
  const {profileExplorer} = useParcaContext();
72
71
  const {heightStyle} = useMetricsGraphDimensions(comparing, profileExplorer?.metricsGraph.height);
73
72
  const handleTimeRangeChange = (range: DateTimeRange): void => {
@@ -117,10 +116,8 @@ export function MetricsGraphSection({
117
116
 
118
117
  if (hasChanged) {
119
118
  // Immediately apply the filter when adding label matchers from the graph
120
- batchUpdates(() => {
121
- setNewQueryExpression(newQuery.toString());
122
- commitDraft(undefined, newQuery.toString());
123
- });
119
+ setNewQueryExpression(newQuery.toString());
120
+ commitDraft(undefined, newQuery.toString());
124
121
  }
125
122
  };
126
123
 
@@ -141,10 +138,8 @@ export function MetricsGraphSection({
141
138
 
142
139
  const mergeFrom = timestamp;
143
140
  const mergeTo = query.profileType().delta ? mergeFrom + BigInt(duration) : mergeFrom;
144
- batchUpdates(() => {
145
- resetStateOnSeriesChange(); // reset some state when a new series is selected
146
- setProfileSelection(mergeFrom, mergeTo, query);
147
- });
141
+ resetStateOnSeriesChange(); // reset some state when a new series is selected
142
+ setProfileSelection(mergeFrom, mergeTo, query);
148
143
  };
149
144
 
150
145
  return (
@@ -14,16 +14,10 @@
14
14
  import {Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef, useState} from 'react';
15
15
 
16
16
  import {RpcError} from '@protobuf-ts/runtime-rpc';
17
+ import {useQueryState as useNuqsQueryState} from 'nuqs';
17
18
 
18
19
  import {ProfileTypesRequest, ProfileTypesResponse, QueryServiceClient} from '@parca/client';
19
- import {
20
- DateTimeRange,
21
- IconButton,
22
- useGrpcMetadata,
23
- useParcaContext,
24
- useURLState,
25
- useURLStateBatch,
26
- } from '@parca/components';
20
+ import {DateTimeRange, IconButton, useGrpcMetadata, useParcaContext} from '@parca/components';
27
21
  import {CloseIcon} from '@parca/icons';
28
22
  import {Query} from '@parca/parser';
29
23
  import {TEST_IDS, testId} from '@parca/test-utils';
@@ -36,6 +30,7 @@ import {
36
30
  import {QueryControls} from '../QueryControls';
37
31
  import {LabelsQueryProvider, useLabelsQueryProvider} from '../contexts/LabelsQueryProvider';
38
32
  import {UnifiedLabelsProvider} from '../contexts/UnifiedLabelsContext';
33
+ import {stringParam} from '../hooks/urlParsers';
39
34
  import {useLabelNames} from '../hooks/useLabels';
40
35
  import {useQueryState} from '../hooks/useQueryState';
41
36
  import useGrpcQuery from '../useGrpcQuery';
@@ -118,8 +113,16 @@ const ProfileSelector = ({
118
113
  onSearchHook,
119
114
  }: ProfileSelectorProps): JSX.Element => {
120
115
  const {externalProfilerComponent, additionalMetricsGraph} = useParcaContext();
121
- const [queryBrowserMode, setQueryBrowserMode] = useURLState('query_browser_mode');
122
- const batchUpdates = useURLStateBatch();
116
+ const [queryBrowserMode, setRawQueryBrowserMode] = useNuqsQueryState(
117
+ 'query_browser_mode',
118
+ stringParam
119
+ );
120
+ const setQueryBrowserMode = useCallback(
121
+ (mode: string | null) => {
122
+ void setRawQueryBrowserMode(mode);
123
+ },
124
+ [setRawQueryBrowserMode]
125
+ );
123
126
 
124
127
  const profileFilterDefaults = externalProfilerComponent?.profileFilterDefaults as
125
128
  | ProfileFilter[]
@@ -222,27 +225,25 @@ const ProfileSelector = ({
222
225
  const selectedProfileName = query.profileName();
223
226
 
224
227
  const setQueryExpression = (updateTs = false): void => {
225
- batchUpdates(() => {
226
- if (onSearchHook != null) {
227
- onSearchHook();
228
- }
229
- // When updateTs is true, re-evaluate the time range to current values
230
- if (updateTs) {
231
- // Force re-evaluation of time range (important for relative ranges like "last 15 minutes")
232
- const currentFrom = timeRangeSelection.getFromMs(true);
233
- const currentTo = timeRangeSelection.getToMs(true);
234
- const currentRangeKey = timeRangeSelection.getRangeKey();
235
- // Commit with refreshed time range
236
- commitDraft({
237
- from: currentFrom,
238
- to: currentTo,
239
- timeSelection: currentRangeKey,
240
- });
241
- } else {
242
- // Commit the draft with existing values
243
- commitDraft();
244
- }
245
- });
228
+ if (onSearchHook != null) {
229
+ onSearchHook();
230
+ }
231
+ // When updateTs is true, re-evaluate the time range to current values
232
+ if (updateTs) {
233
+ // Force re-evaluation of time range (important for relative ranges like "last 15 minutes")
234
+ const currentFrom = timeRangeSelection.getFromMs(true);
235
+ const currentTo = timeRangeSelection.getToMs(true);
236
+ const currentRangeKey = timeRangeSelection.getRangeKey();
237
+ // Commit with refreshed time range
238
+ commitDraft({
239
+ from: currentFrom,
240
+ to: currentTo,
241
+ timeSelection: currentRangeKey,
242
+ });
243
+ } else {
244
+ // Commit the draft with existing values
245
+ commitDraft();
246
+ }
246
247
  };
247
248
 
248
249
  const setMatchersString = (matchers: string): void => {