@parca/profile 0.16.74 → 0.16.76

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 (37) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/Callgraph/index.d.ts +1 -0
  3. package/dist/GraphTooltip/index.d.ts +1 -0
  4. package/dist/IcicleGraph.d.ts +1 -0
  5. package/dist/MatchersInput/SuggestionItem.d.ts +1 -0
  6. package/dist/MatchersInput/SuggestionsList.d.ts +24 -0
  7. package/dist/MatchersInput/SuggestionsList.js +162 -0
  8. package/dist/MatchersInput/index.d.ts +1 -0
  9. package/dist/MatchersInput/index.js +75 -185
  10. package/dist/MetricsCircle/index.d.ts +1 -0
  11. package/dist/MetricsGraph/index.d.ts +1 -0
  12. package/dist/MetricsGraph/index.js +3 -2
  13. package/dist/MetricsSeries/index.d.ts +1 -0
  14. package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts +1 -0
  15. package/dist/ProfileExplorer/ProfileExplorerSingle.d.ts +1 -0
  16. package/dist/ProfileExplorer/index.d.ts +1 -0
  17. package/dist/ProfileIcicleGraph.d.ts +1 -0
  18. package/dist/ProfileMetricsGraph/index.d.ts +1 -0
  19. package/dist/ProfileSelector/CompareButton.d.ts +1 -0
  20. package/dist/ProfileSelector/MergeButton.d.ts +1 -0
  21. package/dist/ProfileSelector/index.d.ts +1 -0
  22. package/dist/ProfileSelector/index.js +4 -1
  23. package/dist/ProfileSource.d.ts +1 -0
  24. package/dist/ProfileTypeSelector/index.d.ts +1 -0
  25. package/dist/ProfileView/FilterByFunctionButton.d.ts +1 -0
  26. package/dist/ProfileView/index.d.ts +1 -0
  27. package/dist/ProfileViewWithData.d.ts +1 -0
  28. package/dist/TopTable.d.ts +1 -0
  29. package/dist/components/DiffLegend.d.ts +1 -0
  30. package/dist/components/ProfileShareButton/ResultBox.d.ts +1 -0
  31. package/dist/components/ProfileShareButton/index.d.ts +1 -0
  32. package/dist/styles.css +1 -1
  33. package/package.json +7 -6
  34. package/src/MatchersInput/SuggestionsList.tsx +291 -0
  35. package/src/MatchersInput/index.tsx +85 -287
  36. package/src/MetricsGraph/index.tsx +6 -2
  37. package/src/ProfileSelector/index.tsx +4 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.16.76](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.75...@parca/profile@0.16.76) (2022-11-29)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## 0.16.75 (2022-11-29)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## [0.16.74](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.73...@parca/profile@0.16.74) (2022-11-24)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { Callgraph as CallgraphType } from '@parca/client';
2
3
  export interface Props {
3
4
  graph: CallgraphType;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { CallgraphNode, FlamegraphNode, FlamegraphNodeMeta, FlamegraphRootNode } from '@parca/client';
2
3
  import { Function, Location, Mapping } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
3
4
  interface GraphTooltipProps {
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { Flamegraph, FlamegraphNode, FlamegraphRootNode } from '@parca/client';
2
3
  import { Mapping, Function, Location } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
3
4
  interface IcicleGraphProps {
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  interface Props {
2
3
  isHighlighted: boolean;
3
4
  onHighlight: () => void;
@@ -0,0 +1,24 @@
1
+ /// <reference types="react" />
2
+ export declare class Suggestion {
3
+ type: string;
4
+ typeahead: string;
5
+ value: string;
6
+ constructor(type: string, typeahead: string, value: string);
7
+ }
8
+ export declare class Suggestions {
9
+ literals: Suggestion[];
10
+ labelNames: Suggestion[];
11
+ labelValues: Suggestion[];
12
+ constructor();
13
+ }
14
+ interface Props {
15
+ suggestions: Suggestions;
16
+ applySuggestion: (suggestion: Suggestion) => void;
17
+ inputRef: HTMLTextAreaElement | null;
18
+ runQuery: () => void;
19
+ focusedInput: boolean;
20
+ isLabelNamesLoading: boolean;
21
+ isLabelValuesLoading: boolean;
22
+ }
23
+ declare const SuggestionsList: ({ suggestions, applySuggestion, inputRef, runQuery, focusedInput, isLabelNamesLoading, isLabelValuesLoading, }: Props) => JSX.Element;
24
+ export default SuggestionsList;
@@ -0,0 +1,162 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
+ // Copyright 2022 The Parca Authors
14
+ // Licensed under the Apache License, Version 2.0 (the "License");
15
+ // you may not use this file except in compliance with the License.
16
+ // You may obtain a copy of the License at
17
+ //
18
+ // http://www.apache.org/licenses/LICENSE-2.0
19
+ //
20
+ // Unless required by applicable law or agreed to in writing, software
21
+ // distributed under the License is distributed on an "AS IS" BASIS,
22
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
+ // See the License for the specific language governing permissions and
24
+ // limitations under the License.
25
+ import { Fragment, useCallback, useEffect, useState } from 'react';
26
+ import { Transition } from '@headlessui/react';
27
+ import SuggestionItem from './SuggestionItem';
28
+ import { usePopper } from 'react-popper';
29
+ import { useParcaContext } from '@parca/components';
30
+ var Suggestion = /** @class */ (function () {
31
+ function Suggestion(type, typeahead, value) {
32
+ this.type = type;
33
+ this.typeahead = typeahead;
34
+ this.value = value;
35
+ }
36
+ return Suggestion;
37
+ }());
38
+ export { Suggestion };
39
+ var Suggestions = /** @class */ (function () {
40
+ function Suggestions() {
41
+ this.literals = [];
42
+ this.labelNames = [];
43
+ this.labelValues = [];
44
+ }
45
+ return Suggestions;
46
+ }());
47
+ export { Suggestions };
48
+ var LoadingSpinner = function () {
49
+ var Spinner = useParcaContext().loader;
50
+ return _jsx("div", __assign({ className: "pt-2 pb-4" }, { children: Spinner }));
51
+ };
52
+ var SuggestionsList = function (_a) {
53
+ var suggestions = _a.suggestions, applySuggestion = _a.applySuggestion, inputRef = _a.inputRef, runQuery = _a.runQuery, focusedInput = _a.focusedInput, isLabelNamesLoading = _a.isLabelNamesLoading, isLabelValuesLoading = _a.isLabelValuesLoading;
54
+ var _b = useState(null), popperElement = _b[0], setPopperElement = _b[1];
55
+ var _c = usePopper(inputRef, popperElement, {
56
+ placement: 'bottom-start',
57
+ }), styles = _c.styles, attributes = _c.attributes;
58
+ var _d = useState(-1), highlightedSuggestionIndex = _d[0], setHighlightedSuggestionIndex = _d[1];
59
+ var _e = useState(true), showSuggest = _e[0], setShowSuggest = _e[1];
60
+ var suggestionsLength = suggestions.literals.length + suggestions.labelNames.length + suggestions.labelValues.length;
61
+ var getSuggestion = useCallback(function (index) {
62
+ if (index < suggestions.labelNames.length) {
63
+ return suggestions.labelNames[index];
64
+ }
65
+ if (index < suggestions.labelNames.length + suggestions.literals.length) {
66
+ return suggestions.literals[index - suggestions.labelNames.length];
67
+ }
68
+ return suggestions.labelValues[index - suggestions.labelNames.length - suggestions.literals.length];
69
+ }, [suggestions]);
70
+ var resetHighlight = useCallback(function () { return setHighlightedSuggestionIndex(-1); }, [setHighlightedSuggestionIndex]);
71
+ var applyHighlightedSuggestion = useCallback(function () {
72
+ applySuggestion(getSuggestion(highlightedSuggestionIndex));
73
+ resetHighlight();
74
+ }, [resetHighlight, applySuggestion, highlightedSuggestionIndex, getSuggestion]);
75
+ var applySuggestionWithIndex = useCallback(function (index) {
76
+ applySuggestion(getSuggestion(index));
77
+ resetHighlight();
78
+ }, [resetHighlight, applySuggestion, getSuggestion]);
79
+ var highlightNext = useCallback(function () {
80
+ var nextIndex = highlightedSuggestionIndex + 1;
81
+ if (nextIndex === suggestionsLength) {
82
+ resetHighlight();
83
+ return;
84
+ }
85
+ setHighlightedSuggestionIndex(nextIndex);
86
+ }, [highlightedSuggestionIndex, suggestionsLength, resetHighlight]);
87
+ var highlightPrevious = useCallback(function () {
88
+ if (highlightedSuggestionIndex === -1) {
89
+ // Didn't select anything, so starting at the bottom.
90
+ setHighlightedSuggestionIndex(suggestionsLength - 1);
91
+ return;
92
+ }
93
+ setHighlightedSuggestionIndex(highlightedSuggestionIndex - 1);
94
+ }, [highlightedSuggestionIndex, suggestionsLength]);
95
+ var handleKeyPress = useCallback(function (event) {
96
+ if (event.key === 'Enter') {
97
+ // Disable new line in the text area
98
+ event.preventDefault();
99
+ }
100
+ // If there is a highlighted suggestion and enter is hit, we complete
101
+ // with the highlighted suggestion.
102
+ if (highlightedSuggestionIndex >= 0 && event.key === 'Enter') {
103
+ applyHighlightedSuggestion();
104
+ }
105
+ // If no suggestions is highlighted and we hit enter, we run the query,
106
+ // and hide suggestions until another actions enables them again.
107
+ if (highlightedSuggestionIndex === -1 && event.key === 'Enter') {
108
+ setShowSuggest(false);
109
+ runQuery();
110
+ return;
111
+ }
112
+ setShowSuggest(true);
113
+ }, [highlightedSuggestionIndex, applyHighlightedSuggestion, runQuery]);
114
+ var handleKeyDown = useCallback(function (event) {
115
+ // Don't need to handle any key interactions if no suggestions there.
116
+ if (suggestionsLength === 0 || !['Tab', 'ArrowUp', 'ArrowDown'].includes(event.key)) {
117
+ return;
118
+ }
119
+ event.preventDefault();
120
+ // Handle tabbing through suggestions.
121
+ if (event.key === 'Tab' && suggestionsLength > 0) {
122
+ event.preventDefault();
123
+ if (event.shiftKey) {
124
+ // Shift + tab goes up.
125
+ highlightPrevious();
126
+ return;
127
+ }
128
+ // Just tab goes down.
129
+ highlightNext();
130
+ }
131
+ // Up arrow highlights previous suggestions.
132
+ if (event.key === 'ArrowUp') {
133
+ highlightPrevious();
134
+ }
135
+ // Down arrow highlights next suggestions.
136
+ if (event.key === 'ArrowDown') {
137
+ highlightNext();
138
+ }
139
+ }, [suggestionsLength, highlightNext, highlightPrevious]);
140
+ useEffect(function () {
141
+ if (inputRef == null) {
142
+ return;
143
+ }
144
+ inputRef.addEventListener('keydown', handleKeyDown);
145
+ inputRef.addEventListener('keypress', handleKeyPress);
146
+ return function () {
147
+ inputRef.removeEventListener('keydown', handleKeyDown);
148
+ inputRef.removeEventListener('keypress', handleKeyPress);
149
+ };
150
+ }, [inputRef, highlightedSuggestionIndex, suggestions, handleKeyPress, handleKeyDown]);
151
+ return (_jsx(_Fragment, { children: suggestionsLength > 0 && (_jsx("div", __assign({ ref: setPopperElement, style: __assign(__assign({}, styles.popper), { marginLeft: 0 }) }, attributes.popper, { className: "z-50" }, { children: _jsx(Transition, __assign({ show: focusedInput && showSuggest, as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0" }, { children: _jsxs("div", __assign({ style: { width: inputRef === null || inputRef === void 0 ? void 0 : inputRef.offsetWidth }, className: "absolute z-10 max-h-[400px] mt-1 bg-gray-50 dark:bg-gray-900 shadow-lg rounded-md text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" }, { children: [isLabelNamesLoading ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestions.labelNames.map(function (l, i) { return (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex === i, onHighlight: function () { return setHighlightedSuggestionIndex(i); }, onApplySuggestion: function () { return applySuggestionWithIndex(i); }, onResetHighlight: function () { return resetHighlight(); }, value: l.value }, l.value)); }) })), suggestions.literals.map(function (l, i) { return (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex === i + suggestions.labelNames.length, onHighlight: function () {
152
+ return setHighlightedSuggestionIndex(i + suggestions.labelNames.length);
153
+ }, onApplySuggestion: function () {
154
+ return applySuggestionWithIndex(i + suggestions.labelNames.length);
155
+ }, onResetHighlight: function () { return resetHighlight(); }, value: l.value }, l.value)); }), isLabelValuesLoading ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestions.labelValues.map(function (l, i) { return (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex ===
156
+ i + suggestions.labelNames.length + suggestions.literals.length, onHighlight: function () {
157
+ return setHighlightedSuggestionIndex(i + suggestions.labelNames.length + suggestions.literals.length);
158
+ }, onApplySuggestion: function () {
159
+ return applySuggestionWithIndex(i + suggestions.labelNames.length + suggestions.literals.length);
160
+ }, onResetHighlight: function () { return resetHighlight(); }, value: l.value }, l.value)); }) }))] })) })) }))) }));
161
+ };
162
+ export default SuggestionsList;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { Query } from '@parca/parser';
2
3
  import { LabelsResponse, QueryServiceClient } from '@parca/client';
3
4
  interface MatchersInputProps {
@@ -9,7 +9,7 @@ var __assign = (this && this.__assign) || function () {
9
9
  };
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  // Copyright 2022 The Parca Authors
14
14
  // Licensed under the Apache License, Version 2.0 (the "License");
15
15
  // you may not use this file except in compliance with the License.
@@ -22,13 +22,13 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
22
22
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
23
  // See the License for the specific language governing permissions and
24
24
  // limitations under the License.
25
- import { Fragment, useState, useEffect } from 'react';
26
- import { Transition } from '@headlessui/react';
27
- import { Query } from '@parca/parser';
28
- import { usePopper } from 'react-popper';
25
+ import { useState, useEffect, useMemo, useRef } from 'react';
26
+ import TextareaAutosize from 'react-textarea-autosize';
29
27
  import cx from 'classnames';
30
- import { useParcaContext, useGrpcMetadata } from '@parca/components';
31
- import SuggestionItem from './SuggestionItem';
28
+ import { useGrpcMetadata } from '@parca/components';
29
+ import { sanitizeLabelValue } from '@parca/functions';
30
+ import { Query } from '@parca/parser';
31
+ import SuggestionsList, { Suggestion, Suggestions } from './SuggestionsList';
32
32
  export var useLabelNames = function (client) {
33
33
  var _a = useState(true), loading = _a[0], setLoading = _a[1];
34
34
  var _b = useState({}), result = _b[0], setResult = _b[1];
@@ -43,202 +43,108 @@ export var useLabelNames = function (client) {
43
43
  }, [client, metadata]);
44
44
  return { result: result, loading: loading };
45
45
  };
46
- var Suggestion = /** @class */ (function () {
47
- function Suggestion(type, typeahead, value) {
48
- this.type = type;
49
- this.typeahead = typeahead;
50
- this.value = value;
51
- }
52
- return Suggestion;
53
- }());
54
- var Suggestions = /** @class */ (function () {
55
- function Suggestions() {
56
- this.literals = [];
57
- this.labelNames = [];
58
- this.labelValues = [];
59
- }
60
- return Suggestions;
61
- }());
62
46
  var MatchersInput = function (_a) {
63
47
  var queryClient = _a.queryClient, setMatchersString = _a.setMatchersString, runQuery = _a.runQuery, currentQuery = _a.currentQuery;
64
- var _b = useState(null), inputRef = _b[0], setInputRef = _b[1];
65
- var _c = useState(false), focusedInput = _c[0], setFocusedInput = _c[1];
66
- var _d = useState(true), showSuggest = _d[0], setShowSuggest = _d[1];
67
- var _e = useState(-1), highlightedSuggestionIndex = _e[0], setHighlightedSuggestionIndex = _e[1];
68
- var _f = useState(false), labelValuesLoading = _f[0], setLabelValuesLoading = _f[1];
69
- var _g = useState(new Suggestion('', '', '')), lastCompleted = _g[0], setLastCompleted = _g[1];
70
- var _h = useState(null), popperElement = _h[0], setPopperElement = _h[1];
71
- var _j = useState(null), labelValues = _j[0], setLabelValues = _j[1];
72
- var _k = useState(null), currentLabelName = _k[0], setCurrentLabelName = _k[1];
73
- var _l = usePopper(inputRef, popperElement, {
74
- placement: 'bottom-start',
75
- }), styles = _l.styles, attributes = _l.attributes;
48
+ var inputRef = useRef(null);
49
+ var _b = useState(false), focusedInput = _b[0], setFocusedInput = _b[1];
50
+ var _c = useState(false), labelValuesLoading = _c[0], setLabelValuesLoading = _c[1];
51
+ var _d = useState(new Suggestion('', '', '')), lastCompleted = _d[0], setLastCompleted = _d[1];
52
+ var _e = useState(null), labelValues = _e[0], setLabelValues = _e[1];
53
+ var _f = useState(null), currentLabelName = _f[0], setCurrentLabelName = _f[1];
76
54
  var metadata = useGrpcMetadata();
77
- var Spinner = useParcaContext().loader;
78
- var _m = useLabelNames(queryClient), labelNamesLoading = _m.loading, result = _m.result;
55
+ var _g = useLabelNames(queryClient), labelNamesLoading = _g.loading, result = _g.result;
79
56
  var labelNamesResponse = result.response, labelNamesError = result.error;
80
- var LoadingSpinner = function () {
81
- return _jsx("div", __assign({ className: "pt-2 pb-4" }, { children: Spinner }));
82
- };
83
57
  useEffect(function () {
84
58
  if (currentLabelName !== null) {
85
59
  var call = queryClient.values({ labelName: currentLabelName, match: [] }, { meta: metadata });
86
60
  setLabelValuesLoading(true);
87
61
  call.response
88
62
  .then(function (response) {
89
- setLabelValues(response.labelValues);
63
+ // replace single `\` in the `labelValues` string with doubles `\\` if available.
64
+ var newValues = sanitizeLabelValue(response.labelValues);
65
+ setLabelValues(newValues);
90
66
  })
91
67
  .catch(function () { return setLabelValues(null); })
92
68
  .finally(function () { return setLabelValuesLoading(false); });
93
69
  }
94
70
  }, [currentLabelName, queryClient, metadata]);
95
- var labelNames = (labelNamesError === undefined || labelNamesError == null) &&
96
- labelNamesResponse !== undefined &&
97
- labelNamesResponse != null
98
- ? labelNamesResponse.labelNames.filter(function (e) { return e !== '__name__'; })
99
- : [];
71
+ var labelNames = useMemo(function () {
72
+ return (labelNamesError === undefined || labelNamesError == null) &&
73
+ labelNamesResponse !== undefined &&
74
+ labelNamesResponse != null
75
+ ? labelNamesResponse.labelNames.filter(function (e) { return e !== '__name__'; })
76
+ : [];
77
+ }, [labelNamesError, labelNamesResponse]);
100
78
  var value = currentQuery.matchersString();
101
- var suggestionSections = new Suggestions();
102
- Query.suggest("".concat(currentQuery.profileName(), "{").concat(value)).forEach(function (s) {
103
- // Skip suggestions that we just completed. This really only works,
104
- // because we know the language is not repetitive. For a language that
105
- // has a repeating word, this would not work.
106
- if (lastCompleted !== null && lastCompleted.type === s.type) {
107
- return;
108
- }
109
- // Need to figure out if any literal suggestions make sense, but a
110
- // closing bracket doesn't in the guided query experience because all
111
- // we have the user do is type the matchers.
112
- if (s.type === 'literal' && s.value !== '}') {
113
- suggestionSections.literals.push({
114
- type: s.type,
115
- typeahead: s.typeahead,
116
- value: s.value,
117
- });
118
- }
119
- if (s.type === 'labelName') {
120
- var inputValue_1 = s.typeahead.trim().toLowerCase();
121
- var inputLength_1 = inputValue_1.length;
122
- var matches = labelNames.filter(function (label) {
123
- return label.toLowerCase().slice(0, inputLength_1) === inputValue_1;
124
- });
125
- matches.forEach(function (m) {
126
- return suggestionSections.labelNames.push({
79
+ var suggestionSections = useMemo(function () {
80
+ var suggestionSections = new Suggestions();
81
+ Query.suggest("".concat(currentQuery.profileName(), "{").concat(value)).forEach(function (s) {
82
+ // Skip suggestions that we just completed. This really only works,
83
+ // because we know the language is not repetitive. For a language that
84
+ // has a repeating word, this would not work.
85
+ if (lastCompleted !== null && lastCompleted.type === s.type) {
86
+ return;
87
+ }
88
+ // Need to figure out if any literal suggestions make sense, but a
89
+ // closing bracket doesn't in the guided query experience because all
90
+ // we have the user do is type the matchers.
91
+ if (s.type === 'literal' && s.value !== '}') {
92
+ suggestionSections.literals.push({
127
93
  type: s.type,
128
94
  typeahead: s.typeahead,
129
- value: m,
95
+ value: s.value,
130
96
  });
131
- });
132
- }
133
- if (s.type === 'labelValue') {
134
- if (currentLabelName === null || s.labelName !== currentLabelName) {
135
- setCurrentLabelName(s.labelName);
136
- return;
137
97
  }
138
- if (labelValues !== null) {
139
- labelValues
140
- .filter(function (v) { return v.slice(0, s.typeahead.length) === s.typeahead; })
141
- .forEach(function (v) {
142
- return suggestionSections.labelValues.push({
98
+ if (s.type === 'labelName') {
99
+ var inputValue_1 = s.typeahead.trim().toLowerCase();
100
+ var inputLength_1 = inputValue_1.length;
101
+ var matches = labelNames.filter(function (label) {
102
+ return label.toLowerCase().slice(0, inputLength_1) === inputValue_1;
103
+ });
104
+ matches.forEach(function (m) {
105
+ return suggestionSections.labelNames.push({
143
106
  type: s.type,
144
107
  typeahead: s.typeahead,
145
- value: v,
108
+ value: m,
146
109
  });
147
110
  });
148
111
  }
149
- }
150
- });
151
- var suggestionsLength = suggestionSections.literals.length +
152
- suggestionSections.labelNames.length +
153
- suggestionSections.labelValues.length;
154
- var resetHighlight = function () { return setHighlightedSuggestionIndex(-1); };
112
+ if (s.type === 'labelValue') {
113
+ if (currentLabelName === null || s.labelName !== currentLabelName) {
114
+ setCurrentLabelName(s.labelName);
115
+ return;
116
+ }
117
+ if (labelValues !== null) {
118
+ labelValues
119
+ .filter(function (v) { return v.slice(0, s.typeahead.length) === s.typeahead; })
120
+ .forEach(function (v) {
121
+ return suggestionSections.labelValues.push({
122
+ type: s.type,
123
+ typeahead: s.typeahead,
124
+ value: v,
125
+ });
126
+ });
127
+ }
128
+ }
129
+ });
130
+ return suggestionSections;
131
+ }, [currentQuery, lastCompleted, labelNames, labelValues, currentLabelName, value]);
155
132
  var resetLastCompleted = function () { return setLastCompleted(new Suggestion('', '', '')); };
156
133
  var onChange = function (e) {
157
134
  var newValue = e.target.value;
158
135
  setMatchersString(newValue);
159
136
  resetLastCompleted();
160
- resetHighlight();
161
137
  };
162
138
  var complete = function (suggestion) {
163
139
  return value.slice(0, value.length - suggestion.typeahead.length) + suggestion.value;
164
140
  };
165
- var getSuggestion = function (index) {
166
- if (index < suggestionSections.labelNames.length) {
167
- return suggestionSections.labelNames[index];
168
- }
169
- if (index < suggestionSections.labelNames.length + suggestionSections.literals.length) {
170
- return suggestionSections.literals[index - suggestionSections.labelNames.length];
171
- }
172
- return suggestionSections.labelValues[index - suggestionSections.labelNames.length - suggestionSections.literals.length];
173
- };
174
- var highlightNext = function () {
175
- var nextIndex = highlightedSuggestionIndex + 1;
176
- if (nextIndex === suggestionsLength) {
177
- resetHighlight();
178
- return;
179
- }
180
- setHighlightedSuggestionIndex(nextIndex);
181
- };
182
- var highlightPrevious = function () {
183
- if (highlightedSuggestionIndex === -1) {
184
- // Didn't select anything, so starting at the bottom.
185
- setHighlightedSuggestionIndex(suggestionsLength - 1);
186
- return;
187
- }
188
- setHighlightedSuggestionIndex(highlightedSuggestionIndex - 1);
189
- };
190
- var applySuggestion = function (suggestionIndex) {
191
- var suggestion = getSuggestion(suggestionIndex);
141
+ var applySuggestion = function (suggestion) {
192
142
  var newValue = complete(suggestion);
193
- resetHighlight();
194
143
  setLastCompleted(suggestion);
195
144
  setMatchersString(newValue);
196
- if (inputRef !== null) {
197
- inputRef.value = newValue;
198
- inputRef.focus();
199
- }
200
- };
201
- var applyHighlightedSuggestion = function () {
202
- applySuggestion(highlightedSuggestionIndex);
203
- };
204
- var handleKeyPress = function (event) {
205
- // If there is a highlighted suggestion and enter is hit, we complete
206
- // with the highlighted suggestion.
207
- if (highlightedSuggestionIndex >= 0 && event.key === 'Enter') {
208
- applyHighlightedSuggestion();
209
- }
210
- // If no suggestions is highlighted and we hit enter, we run the query,
211
- // and hide suggestions until another actions enables them again.
212
- if (highlightedSuggestionIndex === -1 && event.key === 'Enter') {
213
- setShowSuggest(false);
214
- runQuery();
215
- return;
216
- }
217
- setShowSuggest(true);
218
- };
219
- var handleKeyDown = function (event) {
220
- // Don't need to handle any key interactions if no suggestions there.
221
- if (suggestionsLength === 0) {
222
- return;
223
- }
224
- // Handle tabbing through suggestions.
225
- if (event.key === 'Tab' && suggestionsLength > 0) {
226
- event.preventDefault();
227
- if (event.shiftKey) {
228
- // Shift + tab goes up.
229
- highlightPrevious();
230
- return;
231
- }
232
- // Just tab goes down.
233
- highlightNext();
234
- }
235
- // Up arrow highlights previous suggestions.
236
- if (event.key === 'ArrowUp') {
237
- highlightPrevious();
238
- }
239
- // Down arrow highlights next suggestions.
240
- if (event.key === 'ArrowDown') {
241
- highlightNext();
145
+ if (inputRef.current !== null) {
146
+ inputRef.current.value = newValue;
147
+ inputRef.current.focus();
242
148
  }
243
149
  };
244
150
  var focus = function () {
@@ -246,28 +152,12 @@ var MatchersInput = function (_a) {
246
152
  };
247
153
  var unfocus = function () {
248
154
  setFocusedInput(false);
249
- resetHighlight();
250
155
  };
251
156
  var profileSelected = currentQuery.profileName() === '';
252
- return (_jsxs("div", __assign({ className: "font-mono flex-1 w-full block" }, { children: [_jsx("input", { ref: setInputRef, type: "text", className: cx('bg-transparent focus:ring-indigo-800 flex-1 block w-full px-2 py-2 text-sm outline-none', profileSelected && 'cursor-not-allowed'), placeholder: profileSelected
157
+ return (_jsxs("div", __assign({ className: "font-mono flex-1 w-full block" }, { children: [_jsx(TextareaAutosize, { ref: inputRef, className: cx('bg-gray-50 dark:bg-gray-900 focus:ring-indigo-800 flex-1 block w-full px-2 py-2 text-sm outline-none rounded', profileSelected && 'cursor-not-allowed'), placeholder: profileSelected
253
158
  ? 'Select a profile first to enter a filter...'
254
- : 'filter profiles... eg. node="test"', onChange: onChange, value: value, onBlur: unfocus, onFocus: focus, onKeyPress: handleKeyPress, onKeyDown: handleKeyDown, disabled: profileSelected, title: profileSelected
159
+ : 'filter profiles... eg. node="test"', onChange: onChange, value: value, onBlur: unfocus, onFocus: focus, disabled: profileSelected, title: profileSelected
255
160
  ? 'Select a profile first to enter a filter...'
256
- : 'filter profiles... eg. node="test"' }), suggestionsLength > 0 && (_jsx("div", __assign({ ref: setPopperElement, style: __assign(__assign({}, styles.popper), { marginLeft: 0 }) }, attributes.popper, { className: "z-50" }, { children: _jsx(Transition, __assign({ show: focusedInput && showSuggest, as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0" }, { children: _jsxs("div", __assign({ style: { width: inputRef === null || inputRef === void 0 ? void 0 : inputRef.offsetWidth }, className: "absolute z-10 max-h-[400px] mt-1 bg-gray-50 dark:bg-gray-900 shadow-lg rounded-md text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" }, { children: [labelNamesLoading ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestionSections.labelNames.map(function (l, i) { return (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex === i, onHighlight: function () { return setHighlightedSuggestionIndex(i); }, onApplySuggestion: function () { return applySuggestion(i); }, onResetHighlight: function () { return resetHighlight(); }, value: l.value }, l.value)); }) })), suggestionSections.literals.map(function (l, i) { return (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex === i + suggestionSections.labelNames.length, onHighlight: function () {
257
- return setHighlightedSuggestionIndex(i + suggestionSections.labelNames.length);
258
- }, onApplySuggestion: function () {
259
- return applySuggestion(i + suggestionSections.labelNames.length);
260
- }, onResetHighlight: function () { return resetHighlight(); }, value: l.value }, l.value)); }), labelValuesLoading && lastCompleted.type === 'literal' ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestionSections.labelValues.map(function (l, i) { return (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex ===
261
- i +
262
- suggestionSections.labelNames.length +
263
- suggestionSections.literals.length, onHighlight: function () {
264
- return setHighlightedSuggestionIndex(i +
265
- suggestionSections.labelNames.length +
266
- suggestionSections.literals.length);
267
- }, onApplySuggestion: function () {
268
- return applySuggestion(i +
269
- suggestionSections.labelNames.length +
270
- suggestionSections.literals.length);
271
- }, onResetHighlight: function () { return resetHighlight(); }, value: l.value }, l.value)); }) }))] })) })) })))] })));
161
+ : 'filter profiles... eg. node="test"' }), _jsx(SuggestionsList, { isLabelNamesLoading: labelNamesLoading, suggestions: suggestionSections, applySuggestion: applySuggestion, inputRef: inputRef.current, runQuery: runQuery, focusedInput: focusedInput, isLabelValuesLoading: labelValuesLoading && lastCompleted.type === 'literal' })] })));
272
162
  };
273
163
  export default MatchersInput;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  interface MetricsCircleProps {
2
3
  cx: number;
3
4
  cy: number;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { SingleProfileSelection } from '..';
2
3
  import { MetricsSeries as MetricsSeriesPb, Label } from '@parca/client';
3
4
  import { DateTimeRange } from '@parca/components';
@@ -41,7 +41,7 @@ import { timeFormat } from '..';
41
41
  import { cutToMaxStringLength } from '@parca/functions/string';
42
42
  import throttle from 'lodash.throttle';
43
43
  import { usePopper } from 'react-popper';
44
- import { valueFormatter, formatDate } from '@parca/functions';
44
+ import { valueFormatter, formatDate, sanitizeHighlightedValues } from '@parca/functions';
45
45
  import { DateTimeRange } from '@parca/components';
46
46
  import { useContainerDimensions } from '@parca/dynamicsize';
47
47
  import useIsShiftDown from '@parca/components/src/hooks/useIsShiftDown';
@@ -226,7 +226,8 @@ export var RawMetricsGraph = function (_a) {
226
226
  };
227
227
  var openClosestProfile = function () {
228
228
  if (highlighted != null) {
229
- onSampleClick(Math.round(highlighted.timestamp), highlighted.value, highlighted.labels);
229
+ onSampleClick(Math.round(highlighted.timestamp), highlighted.value, sanitizeHighlightedValues(highlighted.labels) // When a user clicks on any sample in the graph, replace single `\` in the `labelValues` string with doubles `\\` if available.
230
+ );
230
231
  }
231
232
  };
232
233
  var onMouseUp = function (e) {
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import * as d3 from 'd3';
2
3
  interface MetricsSeriesProps {
3
4
  data: any;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { ProfileSelection, NavigateFunction } from '..';
2
3
  import { QueryServiceClient } from '@parca/client';
3
4
  import { QuerySelection } from '../ProfileSelector';
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { QueryServiceClient } from '@parca/client';
2
3
  import { ProfileSelection, NavigateFunction } from '..';
3
4
  import { QuerySelection } from '../ProfileSelector';
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { NavigateFunction } from '..';
2
3
  import { QueryServiceClient } from '@parca/client';
3
4
  interface ProfileExplorerProps {
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { Flamegraph } from '@parca/client';
2
3
  interface ProfileIcicleGraphProps {
3
4
  width?: number;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { ProfileSelection } from '..';
2
3
  import { QueryServiceClient, QueryRangeResponse } from '@parca/client';
3
4
  import { RpcError } from '@protobuf-ts/runtime-rpc';
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  declare const CompareButton: ({ disabled, onClick, }: {
2
3
  disabled: boolean;
3
4
  onClick: () => void;