@parca/profile 0.19.60 → 0.19.62

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 (51) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/MatchersInput/SuggestionsList.d.ts +2 -1
  3. package/dist/MatchersInput/SuggestionsList.d.ts.map +1 -1
  4. package/dist/MatchersInput/SuggestionsList.js +24 -3
  5. package/dist/MatchersInput/index.d.ts +1 -0
  6. package/dist/MatchersInput/index.d.ts.map +1 -1
  7. package/dist/MatchersInput/index.js +13 -7
  8. package/dist/ProfileSelector/QueryControls.d.ts.map +1 -1
  9. package/dist/ProfileSelector/QueryControls.js +4 -1
  10. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts +4 -0
  11. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts.map +1 -1
  12. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.js +2 -4
  13. package/dist/ProfileView/components/Toolbars/index.d.ts +4 -0
  14. package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
  15. package/dist/ProfileView/components/Toolbars/index.js +2 -2
  16. package/dist/ProfileView/hooks/useVisualizationState.d.ts +2 -0
  17. package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -1
  18. package/dist/ProfileView/hooks/useVisualizationState.js +19 -1
  19. package/dist/ProfileView/index.d.ts.map +1 -1
  20. package/dist/ProfileView/index.js +2 -2
  21. package/dist/SimpleMatchers/Select.d.ts +3 -0
  22. package/dist/SimpleMatchers/Select.d.ts.map +1 -1
  23. package/dist/SimpleMatchers/Select.js +29 -6
  24. package/dist/SimpleMatchers/index.d.ts +1 -0
  25. package/dist/SimpleMatchers/index.d.ts.map +1 -1
  26. package/dist/SimpleMatchers/index.js +92 -9
  27. package/dist/ViewMatchers/index.js +14 -14
  28. package/dist/contexts/MatchersInputLabelsContext.d.ts +1 -0
  29. package/dist/contexts/MatchersInputLabelsContext.d.ts.map +1 -1
  30. package/dist/contexts/MatchersInputLabelsContext.js +3 -1
  31. package/dist/contexts/SimpleMatchersLabelContext.d.ts +1 -0
  32. package/dist/contexts/SimpleMatchersLabelContext.d.ts.map +1 -1
  33. package/dist/contexts/SimpleMatchersLabelContext.js +19 -2
  34. package/dist/styles.css +1 -1
  35. package/dist/useGrpcQuery/index.d.ts +2 -1
  36. package/dist/useGrpcQuery/index.d.ts.map +1 -1
  37. package/dist/useGrpcQuery/index.js +2 -1
  38. package/package.json +8 -8
  39. package/src/MatchersInput/SuggestionsList.tsx +119 -40
  40. package/src/MatchersInput/index.tsx +14 -5
  41. package/src/ProfileSelector/QueryControls.tsx +5 -1
  42. package/src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx +10 -5
  43. package/src/ProfileView/components/Toolbars/index.tsx +12 -0
  44. package/src/ProfileView/hooks/useVisualizationState.ts +34 -1
  45. package/src/ProfileView/index.tsx +7 -0
  46. package/src/SimpleMatchers/Select.tsx +128 -60
  47. package/src/SimpleMatchers/index.tsx +109 -12
  48. package/src/ViewMatchers/index.tsx +15 -15
  49. package/src/contexts/MatchersInputLabelsContext.tsx +8 -7
  50. package/src/contexts/SimpleMatchersLabelContext.tsx +28 -2
  51. package/src/useGrpcQuery/index.ts +3 -1
@@ -14,6 +14,8 @@
14
14
  import {Fragment, useCallback, useEffect, useState} from 'react';
15
15
 
16
16
  import {Transition} from '@headlessui/react';
17
+ import {Icon} from '@iconify/react';
18
+ import cx from 'classnames';
17
19
  import {usePopper} from 'react-popper';
18
20
 
19
21
  import {useParcaContext} from '@parca/components';
@@ -53,6 +55,7 @@ interface Props {
53
55
  isLabelNamesLoading: boolean;
54
56
  isLabelValuesLoading: boolean;
55
57
  shouldTrimPrefix: boolean;
58
+ refetchLabelValues: () => void;
56
59
  }
57
60
 
58
61
  const LoadingSpinner = (): JSX.Element => {
@@ -75,6 +78,7 @@ const SuggestionsList = ({
75
78
  isLabelNamesLoading,
76
79
  isLabelValuesLoading,
77
80
  shouldTrimPrefix = false,
81
+ refetchLabelValues,
78
82
  }: Props): JSX.Element => {
79
83
  const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
80
84
  const {styles, attributes} = usePopper(inputRef, popperElement, {
@@ -82,6 +86,18 @@ const SuggestionsList = ({
82
86
  });
83
87
  const [highlightedSuggestionIndex, setHighlightedSuggestionIndex] = useState<number>(-1);
84
88
  const [showSuggest, setShowSuggest] = useState(true);
89
+ const [isRefetching, setIsRefetching] = useState(false);
90
+
91
+ const handleRefetch = useCallback(async () => {
92
+ if (isRefetching) return;
93
+
94
+ setIsRefetching(true);
95
+ try {
96
+ await refetchLabelValues();
97
+ } finally {
98
+ setIsRefetching(false);
99
+ }
100
+ }, [refetchLabelValues, isRefetching]);
85
101
 
86
102
  const suggestionsLength =
87
103
  suggestions.literals.length + suggestions.labelNames.length + suggestions.labelValues.length;
@@ -231,43 +247,106 @@ const SuggestionsList = ({
231
247
  style={{width: inputRef?.offsetWidth}}
232
248
  className="absolute z-10 mt-1 max-h-[400px] overflow-auto rounded-md bg-gray-50 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-900 sm:text-sm"
233
249
  >
234
- {isLabelNamesLoading ? (
235
- <LoadingSpinner />
236
- ) : (
237
- <>
238
- {suggestions.labelNames.map((l, i) => (
239
- <SuggestionItem
240
- isHighlighted={highlightedSuggestionIndex === i}
241
- onHighlight={() => setHighlightedSuggestionIndex(i)}
242
- onApplySuggestion={() => applySuggestionWithIndex(i)}
243
- onResetHighlight={() => resetHighlight()}
244
- value={transformLabelsForSuggestions(l.value, shouldTrimPrefix)}
245
- key={transformLabelsForSuggestions(l.value, shouldTrimPrefix)}
246
- />
247
- ))}
248
- </>
249
- )}
250
-
251
- {suggestions.literals.map((l, i) => (
252
- <SuggestionItem
253
- isHighlighted={highlightedSuggestionIndex === i + suggestions.labelNames.length}
254
- onHighlight={() =>
255
- setHighlightedSuggestionIndex(i + suggestions.labelNames.length)
256
- }
257
- onApplySuggestion={() =>
258
- applySuggestionWithIndex(i + suggestions.labelNames.length)
259
- }
260
- onResetHighlight={() => resetHighlight()}
261
- value={l.value}
262
- key={l.value}
263
- />
264
- ))}
265
-
266
- {isLabelValuesLoading ? (
267
- <LoadingSpinner />
268
- ) : (
269
- <>
270
- {suggestions.labelValues.map((l, i) => (
250
+ <div className="relative pb-12">
251
+ {isLabelNamesLoading ? (
252
+ <LoadingSpinner />
253
+ ) : (
254
+ <>
255
+ {suggestions.labelNames.map((l, i) => (
256
+ <SuggestionItem
257
+ isHighlighted={highlightedSuggestionIndex === i}
258
+ onHighlight={() => setHighlightedSuggestionIndex(i)}
259
+ onApplySuggestion={() => applySuggestionWithIndex(i)}
260
+ onResetHighlight={() => resetHighlight()}
261
+ value={transformLabelsForSuggestions(l.value, shouldTrimPrefix)}
262
+ key={transformLabelsForSuggestions(l.value, shouldTrimPrefix)}
263
+ />
264
+ ))}
265
+ </>
266
+ )}
267
+
268
+ {suggestions.literals.map((l, i) => (
269
+ <SuggestionItem
270
+ isHighlighted={highlightedSuggestionIndex === i + suggestions.labelNames.length}
271
+ onHighlight={() =>
272
+ setHighlightedSuggestionIndex(i + suggestions.labelNames.length)
273
+ }
274
+ onApplySuggestion={() =>
275
+ applySuggestionWithIndex(i + suggestions.labelNames.length)
276
+ }
277
+ onResetHighlight={() => resetHighlight()}
278
+ value={l.value}
279
+ key={l.value}
280
+ />
281
+ ))}
282
+
283
+ {isLabelValuesLoading ? (
284
+ <LoadingSpinner />
285
+ ) : suggestions.labelNames.length === 0 && suggestions.literals.length === 0 ? (
286
+ <>
287
+ {suggestions.labelValues.length === 0 ? (
288
+ <div
289
+ className="px-4 py-3 text-sm text-gray-500 dark:text-gray-400 text-center"
290
+ data-testid="suggestions-no-results"
291
+ >
292
+ No label values found
293
+ </div>
294
+ ) : (
295
+ suggestions.labelValues.map((l, i) => (
296
+ <SuggestionItem
297
+ isHighlighted={
298
+ highlightedSuggestionIndex ===
299
+ i + suggestions.labelNames.length + suggestions.literals.length
300
+ }
301
+ onHighlight={() =>
302
+ setHighlightedSuggestionIndex(
303
+ i + suggestions.labelNames.length + suggestions.literals.length
304
+ )
305
+ }
306
+ onApplySuggestion={() =>
307
+ applySuggestionWithIndex(
308
+ i + suggestions.labelNames.length + suggestions.literals.length
309
+ )
310
+ }
311
+ onResetHighlight={() => resetHighlight()}
312
+ value={l.value}
313
+ key={l.value}
314
+ />
315
+ ))
316
+ )}
317
+ <div className="absolute w-full flex items-center justify-center bottom-0 px-3 py-2 bg-gray-50 dark:bg-gray-800">
318
+ <button
319
+ onClick={e => {
320
+ e.preventDefault();
321
+ e.stopPropagation();
322
+ void handleRefetch();
323
+ }}
324
+ disabled={isRefetching}
325
+ className={cx(
326
+ 'p-1 flex items-center gap-1 rounded-full transition-all duration-200 w-auto justify-center',
327
+ isRefetching
328
+ ? 'cursor-wait opacity-50'
329
+ : 'hover:bg-gray-200 dark:hover:bg-gray-700 cursor-pointer'
330
+ )}
331
+ title="Refresh label values"
332
+ type="button"
333
+ data-testid="suggestions-refresh-button"
334
+ >
335
+ <Icon
336
+ icon="system-uicons:reset"
337
+ className={cx(
338
+ 'w-3 h-3 text-gray-500 dark:text-gray-400',
339
+ isRefetching && 'animate-spin'
340
+ )}
341
+ />
342
+ <span className="text-xs text-gray-500 dark:text-gray-400">
343
+ Refresh results
344
+ </span>
345
+ </button>
346
+ </div>
347
+ </>
348
+ ) : (
349
+ suggestions.labelValues.map((l, i) => (
271
350
  <SuggestionItem
272
351
  isHighlighted={
273
352
  highlightedSuggestionIndex ===
@@ -287,9 +366,9 @@ const SuggestionsList = ({
287
366
  value={l.value}
288
367
  key={l.value}
289
368
  />
290
- ))}
291
- </>
292
- )}
369
+ ))
370
+ )}
371
+ </div>
293
372
  </div>
294
373
  </Transition>
295
374
  </div>
@@ -73,7 +73,6 @@ export const useLabelNames = (
73
73
  },
74
74
  options: {
75
75
  enabled: profileType !== undefined && profileType !== '',
76
- staleTime: 1000 * 60 * 5, // 5 minutes
77
76
  keepPreviousData: false,
78
77
  },
79
78
  });
@@ -87,6 +86,7 @@ interface UseLabelValues {
87
86
  error?: Error;
88
87
  };
89
88
  loading: boolean;
89
+ refetch: () => void;
90
90
  }
91
91
 
92
92
  export const useLabelValues = (
@@ -98,7 +98,7 @@ export const useLabelValues = (
98
98
  ): UseLabelValues => {
99
99
  const metadata = useGrpcMetadata();
100
100
 
101
- const {data, isLoading, error} = useGrpcQuery<string[]>({
101
+ const {data, isLoading, error, refetch} = useGrpcQuery<string[]>({
102
102
  key: ['labelValues', labelName, profileType, start, end],
103
103
  queryFn: async signal => {
104
104
  const request: ValuesRequest = {labelName, match: [], profileType};
@@ -115,12 +115,17 @@ export const useLabelValues = (
115
115
  profileType !== '' &&
116
116
  labelName !== undefined &&
117
117
  labelName !== '',
118
- staleTime: 1000 * 60 * 5, // 5 minutes
119
118
  keepPreviousData: false,
120
119
  },
121
120
  });
122
121
 
123
- return {result: {response: data ?? [], error: error as Error}, loading: isLoading};
122
+ return {
123
+ result: {response: data ?? [], error: error as Error},
124
+ loading: isLoading,
125
+ refetch: () => {
126
+ void refetch();
127
+ },
128
+ };
124
129
  };
125
130
 
126
131
  export const useFetchUtilizationLabelValues = (
@@ -130,8 +135,10 @@ export const useFetchUtilizationLabelValues = (
130
135
  const {data} = useQuery({
131
136
  queryKey: ['utilizationLabelValues', labelName],
132
137
  queryFn: async () => {
133
- return await utilizationLabels?.utilizationFetchLabelValues?.(labelName);
138
+ const result = await utilizationLabels?.utilizationFetchLabelValues?.(labelName);
139
+ return result ?? [];
134
140
  },
141
+ enabled: utilizationLabels?.utilizationFetchLabelValues != null && labelName !== '',
135
142
  });
136
143
 
137
144
  return data ?? [];
@@ -155,6 +162,7 @@ const MatchersInput = ({
155
162
  currentLabelName,
156
163
  setCurrentLabelName,
157
164
  shouldHandlePrefixes,
165
+ refetchLabelValues,
158
166
  } = useLabels();
159
167
 
160
168
  const value = currentQuery.matchersString();
@@ -327,6 +335,7 @@ const MatchersInput = ({
327
335
  focusedInput={focusedInput}
328
336
  isLabelValuesLoading={isLabelValuesLoading && lastCompleted.type === 'literal'}
329
337
  shouldTrimPrefix={shouldHandlePrefixes}
338
+ refetchLabelValues={refetchLabelValues}
330
339
  />
331
340
  </div>
332
341
  );
@@ -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 {useState} from 'react';
15
+
14
16
  import {Switch} from '@headlessui/react';
15
17
  import {RpcError} from '@protobuf-ts/runtime-rpc';
16
18
  import Select, {type SelectInstance} from 'react-select';
@@ -93,6 +95,7 @@ export function QueryControls({
93
95
  profileTypesError,
94
96
  }: QueryControlsProps): JSX.Element {
95
97
  const {timezone} = useParcaContext();
98
+ const [searchExecutedTimestamp, setSearchExecutedTimestamp] = useState<number>(0);
96
99
 
97
100
  return (
98
101
  <div
@@ -180,7 +183,6 @@ export function QueryControls({
180
183
  />
181
184
  ) : (
182
185
  <SimpleMatchers
183
- key={query.toString()}
184
186
  setMatchersString={setMatchersString}
185
187
  runQuery={setQueryExpression}
186
188
  currentQuery={query}
@@ -189,6 +191,7 @@ export function QueryControls({
189
191
  queryClient={queryClient}
190
192
  start={timeRangeSelection.getFromMs()}
191
193
  end={timeRangeSelection.getToMs()}
194
+ searchExecutedTimestamp={searchExecutedTimestamp}
192
195
  />
193
196
  )}
194
197
  </div>
@@ -261,6 +264,7 @@ export function QueryControls({
261
264
  disabled={searchDisabled}
262
265
  onClick={(e: React.MouseEvent<HTMLElement>) => {
263
266
  e.preventDefault();
267
+ setSearchExecutedTimestamp(Date.now());
264
268
  setQueryExpression(true);
265
269
  }}
266
270
  id="h-matcher-search-button"
@@ -187,6 +187,10 @@ interface MultiLevelDropdownProps {
187
187
  groupBy: string[];
188
188
  toggleGroupBy: (key: string) => void;
189
189
  isTableVizOnly: boolean;
190
+ alignFunctionName: string;
191
+ setAlignFunctionName: (align: string) => void;
192
+ colorBy: string;
193
+ setColorBy: (colorBy: string) => void;
190
194
  }
191
195
 
192
196
  const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
@@ -195,6 +199,10 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
195
199
  groupBy,
196
200
  toggleGroupBy,
197
201
  isTableVizOnly,
202
+ alignFunctionName,
203
+ setAlignFunctionName,
204
+ colorBy,
205
+ setColorBy,
198
206
  }) => {
199
207
  const dropdownRef = useRef<HTMLDivElement>(null);
200
208
  const [shouldOpenLeft, setShouldOpenLeft] = useState(false);
@@ -202,7 +210,6 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
202
210
  defaultValue: FIELD_FUNCTION_NAME,
203
211
  });
204
212
  const [colorStackLegend, setStoreColorStackLegend] = useURLState('color_stack_legend');
205
- const [colorBy, setColorBy] = useURLState('color_by');
206
213
  const [hiddenBinaries, setHiddenBinaries] = useURLState('hidden_binaries', {
207
214
  defaultValue: [],
208
215
  alwaysReturnArray: true,
@@ -212,9 +219,7 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
212
219
  USER_PREFERENCES.FLAMEGRAPH_COLOR_PROFILE.key
213
220
  );
214
221
  const isColorStackLegendEnabled = colorStackLegend === 'true';
215
-
216
- const [alignFunctionName, setAlignFunctionName] = useURLState('align_function_name');
217
- const isLeftAligned = alignFunctionName === 'left' || alignFunctionName === undefined;
222
+ const isLeftAligned = alignFunctionName === 'left';
218
223
 
219
224
  // By default, we want delta profiles (CPU) to be relatively compared.
220
225
  // For non-delta profiles, like goroutines or memory, we want the profiles to be compared absolutely.
@@ -421,7 +426,7 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
421
426
  closeDropdown={close}
422
427
  activeValueForSortBy={storeSortBy as string}
423
428
  activeValueForColorBy={
424
- colorBy === undefined || colorBy === '' ? 'binary' : (colorBy as string)
429
+ colorBy === undefined || colorBy === '' ? 'binary' : colorBy
425
430
  }
426
431
  activeValuesForLevel={groupBy}
427
432
  renderAsDiv={item.renderAsDiv}
@@ -50,6 +50,10 @@ export interface VisualisationToolbarProps {
50
50
  setGroupByLabels: (labels: string[]) => void;
51
51
  showVisualizationSelector?: boolean;
52
52
  sandwichFunctionName?: string;
53
+ alignFunctionName: string;
54
+ setAlignFunctionName: (align: string) => void;
55
+ colorBy: string;
56
+ setColorBy: (colorBy: string) => void;
53
57
  }
54
58
 
55
59
  export interface TableToolbarProps {
@@ -139,6 +143,10 @@ export const VisualisationToolbar: FC<VisualisationToolbarProps> = ({
139
143
  total,
140
144
  filtered,
141
145
  showVisualizationSelector = true,
146
+ alignFunctionName,
147
+ setAlignFunctionName,
148
+ colorBy,
149
+ setColorBy,
142
150
  }) => {
143
151
  const {dashboardItems} = useDashboard();
144
152
 
@@ -183,6 +191,10 @@ export const VisualisationToolbar: FC<VisualisationToolbarProps> = ({
183
191
  profileType={profileType}
184
192
  onSelect={() => {}}
185
193
  isTableVizOnly={isTableVizOnly}
194
+ alignFunctionName={alignFunctionName}
195
+ setAlignFunctionName={setAlignFunctionName}
196
+ colorBy={colorBy}
197
+ setColorBy={setColorBy}
186
198
  />
187
199
 
188
200
  <ShareButton
@@ -14,6 +14,7 @@
14
14
  import {useCallback, useMemo} from 'react';
15
15
 
16
16
  import {JSONParser, JSONSerializer, useURLState, useURLStateCustom} from '@parca/components';
17
+ import {USER_PREFERENCES, useUserPreference} from '@parca/hooks';
17
18
 
18
19
  import {
19
20
  FIELD_FUNCTION_FILE_NAME,
@@ -38,14 +39,28 @@ export const useVisualizationState = (): {
38
39
  sandwichFunctionName: string | undefined;
39
40
  setSandwichFunctionName: (sandwichFunctionName: string | undefined) => void;
40
41
  resetSandwichFunctionName: () => void;
42
+ alignFunctionName: string;
43
+ setAlignFunctionName: (align: string) => void;
41
44
  } => {
45
+ const [colorByPreference, setColorByPreference] = useUserPreference<string>(
46
+ USER_PREFERENCES.COLOR_BY.key
47
+ );
48
+ const [alignFunctionNamePreference, setAlignFunctionNamePreference] = useUserPreference<string>(
49
+ USER_PREFERENCES.ALIGN_FUNCTION_NAME.key
50
+ );
51
+
42
52
  const [curPathArrow, setCurPathArrow] = useURLStateCustom<CurrentPathFrame[]>('cur_path', {
43
53
  parse: JSONParser<CurrentPathFrame[]>,
44
54
  stringify: JSONSerializer,
45
55
  defaultValue: '[]',
46
56
  });
47
57
  const [colorStackLegend] = useURLState<string | undefined>('color_stack_legend');
48
- const [colorBy, setColorBy] = useURLState('color_by');
58
+ const [colorBy, setStoreColorBy] = useURLState('color_by', {
59
+ defaultValue: colorByPreference,
60
+ });
61
+ const [alignFunctionName, setStoreAlignFunctionName] = useURLState('align_function_name', {
62
+ defaultValue: alignFunctionNamePreference,
63
+ });
49
64
  const [groupBy, setStoreGroupBy] = useURLState<string[]>('group_by', {
50
65
  defaultValue: [FIELD_FUNCTION_NAME],
51
66
  alwaysReturnArray: true,
@@ -99,6 +114,22 @@ export const useVisualizationState = (): {
99
114
  setSandwichFunctionName(undefined);
100
115
  }, [setSandwichFunctionName]);
101
116
 
117
+ const setColorBy = useCallback(
118
+ (value: string): void => {
119
+ setStoreColorBy(value);
120
+ setColorByPreference(value);
121
+ },
122
+ [setStoreColorBy, setColorByPreference]
123
+ );
124
+
125
+ const setAlignFunctionName = useCallback(
126
+ (value: string): void => {
127
+ setStoreAlignFunctionName(value);
128
+ setAlignFunctionNamePreference(value);
129
+ },
130
+ [setStoreAlignFunctionName, setAlignFunctionNamePreference]
131
+ );
132
+
102
133
  return {
103
134
  curPathArrow,
104
135
  setCurPathArrow,
@@ -112,5 +143,7 @@ export const useVisualizationState = (): {
112
143
  sandwichFunctionName,
113
144
  setSandwichFunctionName,
114
145
  resetSandwichFunctionName,
146
+ alignFunctionName: (alignFunctionName as string) ?? 'left',
147
+ setAlignFunctionName,
115
148
  };
116
149
  };
@@ -60,11 +60,14 @@ export const ProfileView = ({
60
60
  setCurPathArrow,
61
61
  colorStackLegend,
62
62
  colorBy,
63
+ setColorBy,
63
64
  groupBy,
64
65
  toggleGroupBy,
65
66
  setGroupByLabels,
66
67
  sandwichFunctionName,
67
68
  resetSandwichFunctionName,
69
+ alignFunctionName,
70
+ setAlignFunctionName,
68
71
  } = useVisualizationState();
69
72
 
70
73
  const {colorMappings} = useProfileMetadata({
@@ -148,6 +151,10 @@ export const ProfileView = ({
148
151
  setGroupByLabels={setGroupByLabels}
149
152
  showVisualizationSelector={showVisualizationSelector}
150
153
  sandwichFunctionName={sandwichFunctionName}
154
+ alignFunctionName={alignFunctionName}
155
+ setAlignFunctionName={setAlignFunctionName}
156
+ colorBy={colorBy}
157
+ setColorBy={setColorBy}
151
158
  />
152
159
 
153
160
  {isColorStackLegendEnabled && (