@backstage/core-components 0.7.5 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/dist/esm/RealLogViewer-eb3457ec.esm.js +554 -0
- package/dist/esm/RealLogViewer-eb3457ec.esm.js.map +1 -0
- package/dist/index.d.ts +309 -40
- package/dist/index.esm.js +1094 -513
- package/dist/index.esm.js.map +1 -1
- package/package.json +20 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
# @backstage/core-components
|
|
2
2
|
|
|
3
|
+
## 0.8.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 73a91e25f9: Added description to components: BottomLink, Breadcrumbs, BrokenImageIcon, CardTab, Content, ContentHeader, EmptyState, ErrorPage, FeatureCalloutCircular, Gauge, GaugeCard, Header, HeaderIconLinkRow, HeaderLabel, HeaderTabs, HorizontalScrollGrid, InfoCard, IntroCard
|
|
8
|
+
- 27af6d996b: Locking `rc-progress` to the working version of 3.1.4
|
|
9
|
+
- 10e5f9d10c: Do not `setState` when unmounted in `OverflowTooltip`
|
|
10
|
+
- b646a73fe0: In @backstage/plugin-scaffolder - When user will have one option available in hostUrl or owner - autoselect and select component should be readonly.
|
|
11
|
+
|
|
12
|
+
in @backstage/core-components - Select component has extended API with few more props: native : boolean, disabled: boolean. native - if set to true - Select component will use native browser select picker (not rendered by Material UI lib ).
|
|
13
|
+
disabled - if set to true - action on component will not be possible.
|
|
14
|
+
|
|
15
|
+
- 7a4bd2ceac: Prefer using `Link` from `@backstage/core-components` rather than material-UI.
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
- @backstage/core-plugin-api@0.4.0
|
|
18
|
+
|
|
19
|
+
## 0.8.1
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- 2c17e5b073: Items in `<SidebarSubmenu>` are now only active when their full path is active (including search parameters).
|
|
24
|
+
- 9d6503e86c: Switched out usage of deprecated `OAuthRequestApi` types from `@backstage/core-plugin-api`.
|
|
25
|
+
- 1680a1c5ac: Add Missing Override Components Type for SidebarSpace, SidebarSpacer, and SidebarDivider Components.
|
|
26
|
+
- Updated dependencies
|
|
27
|
+
- @backstage/core-plugin-api@0.3.1
|
|
28
|
+
|
|
29
|
+
## 0.8.0
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- a036b65c2f: The `SignInPage` has been updated to use the new `onSignInSuccess` callback that was introduced in the same release. While existing code will usually continue to work, it is technically a breaking change because of the dependency on `SignInProps` from the `@backstage/core-plugin-api`. For more information on this change and instructions on how to migrate existing code, see the [`@backstage/core-app-api` CHANGELOG.md](https://github.com/backstage/backstage/blob/master/packages/core-app-api/CHANGELOG.md).
|
|
34
|
+
|
|
35
|
+
Added a new `UserIdentity` class which helps create implementations of the `IdentityApi`. It provides a couple of static factory methods such as the most relevant `create`, and `createGuest` to create an `IdentityApi` for a guest user.
|
|
36
|
+
|
|
37
|
+
Also provides a deprecated `fromLegacy` method to create an `IdentityApi` from the now deprecated `SignInResult`. This method will be removed in the future when `SignInResult` is also removed.
|
|
38
|
+
|
|
39
|
+
### Patch Changes
|
|
40
|
+
|
|
41
|
+
- 9603827bb5: Addressed some peer dependency warnings
|
|
42
|
+
- cd450844f6: Moved React dependencies to `peerDependencies` and allow both React v16 and v17 to be used.
|
|
43
|
+
- dcd1a0c3f4: Minor improvement to the API reports, by not unpacking arguments directly
|
|
44
|
+
- e839500286: Introduce new `LogViewer` component that can be used to display logs. It supports copying, searching, filtering, and displaying text with ANSI color escape codes.
|
|
45
|
+
- 1357ac30f1: Standardize on `classnames` instead of both that and `clsx`.
|
|
46
|
+
- e5976071ea: Use ellipsis style for overflowed text in sidebar menu
|
|
47
|
+
- Updated dependencies
|
|
48
|
+
- @backstage/core-plugin-api@0.3.0
|
|
49
|
+
|
|
50
|
+
## 0.7.6
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- e34f174fc5: Added `<SidebarSubmenu>` and `<SidebarSubmenuItem>` to enable building better sidebars. You can check out the storybook for more inspiration and how to get started.
|
|
55
|
+
|
|
56
|
+
Added two new theme props for styling the sidebar too, `navItem.hoverBackground` and `submenu.background`.
|
|
57
|
+
|
|
58
|
+
- Updated dependencies
|
|
59
|
+
- @backstage/theme@0.2.14
|
|
60
|
+
- @backstage/core-plugin-api@0.2.2
|
|
61
|
+
|
|
3
62
|
## 0.7.5
|
|
4
63
|
|
|
5
64
|
### Patch Changes
|
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import React, { useMemo, useState, useEffect, useRef } from 'react';
|
|
2
|
+
import IconButton from '@material-ui/core/IconButton';
|
|
3
|
+
import CopyIcon from '@material-ui/icons/FileCopy';
|
|
4
|
+
import AutoSizer from 'react-virtualized-auto-sizer';
|
|
5
|
+
import { FixedSizeList } from 'react-window';
|
|
6
|
+
import ansiRegexMaker from 'ansi-regex';
|
|
7
|
+
import { makeStyles, alpha } from '@material-ui/core/styles';
|
|
8
|
+
import * as colors from '@material-ui/core/colors';
|
|
9
|
+
import classNames from 'classnames';
|
|
10
|
+
import startCase from 'lodash/startCase';
|
|
11
|
+
import TextField from '@material-ui/core/TextField';
|
|
12
|
+
import Typography from '@material-ui/core/Typography';
|
|
13
|
+
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
|
|
14
|
+
import ChevronRight from '@material-ui/icons/ChevronRight';
|
|
15
|
+
import FilterList from '@material-ui/icons/FilterList';
|
|
16
|
+
import { useToggle, useCopyToClipboard } from 'react-use';
|
|
17
|
+
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
|
|
18
|
+
|
|
19
|
+
const ansiRegex = ansiRegexMaker();
|
|
20
|
+
const newlineRegex = /\n\r?/g;
|
|
21
|
+
const codeModifiers = Object.fromEntries(Object.entries({
|
|
22
|
+
1: (m) => ({ ...m, bold: true }),
|
|
23
|
+
3: (m) => ({ ...m, italic: true }),
|
|
24
|
+
4: (m) => ({ ...m, underline: true }),
|
|
25
|
+
22: ({ bold: _, ...m }) => m,
|
|
26
|
+
23: ({ italic: _, ...m }) => m,
|
|
27
|
+
24: ({ underline: _, ...m }) => m,
|
|
28
|
+
30: (m) => ({ ...m, foreground: "black" }),
|
|
29
|
+
31: (m) => ({ ...m, foreground: "red" }),
|
|
30
|
+
32: (m) => ({ ...m, foreground: "green" }),
|
|
31
|
+
33: (m) => ({ ...m, foreground: "yellow" }),
|
|
32
|
+
34: (m) => ({ ...m, foreground: "blue" }),
|
|
33
|
+
35: (m) => ({ ...m, foreground: "magenta" }),
|
|
34
|
+
36: (m) => ({ ...m, foreground: "cyan" }),
|
|
35
|
+
37: (m) => ({ ...m, foreground: "white" }),
|
|
36
|
+
39: ({ foreground: _, ...m }) => m,
|
|
37
|
+
90: (m) => ({ ...m, foreground: "grey" }),
|
|
38
|
+
40: (m) => ({ ...m, background: "black" }),
|
|
39
|
+
41: (m) => ({ ...m, background: "red" }),
|
|
40
|
+
42: (m) => ({ ...m, background: "green" }),
|
|
41
|
+
43: (m) => ({ ...m, background: "yellow" }),
|
|
42
|
+
44: (m) => ({ ...m, background: "blue" }),
|
|
43
|
+
45: (m) => ({ ...m, background: "magenta" }),
|
|
44
|
+
46: (m) => ({ ...m, background: "cyan" }),
|
|
45
|
+
47: (m) => ({ ...m, background: "white" }),
|
|
46
|
+
49: ({ background: _, ...m }) => m
|
|
47
|
+
}).map(([code, modifier]) => [`[${code}m`, modifier]));
|
|
48
|
+
class AnsiLine {
|
|
49
|
+
constructor(lineNumber = 1, chunks = []) {
|
|
50
|
+
this.lineNumber = lineNumber;
|
|
51
|
+
this.chunks = chunks;
|
|
52
|
+
this.text = chunks.map((c) => c.text).join("").toLocaleLowerCase("en-US");
|
|
53
|
+
}
|
|
54
|
+
lastChunk() {
|
|
55
|
+
return this.chunks[this.chunks.length - 1];
|
|
56
|
+
}
|
|
57
|
+
replaceLastChunk(newChunks) {
|
|
58
|
+
if (newChunks) {
|
|
59
|
+
this.chunks.splice(this.chunks.length - 1, 1, ...newChunks);
|
|
60
|
+
this.text = this.chunks.map((c) => c.text).join("").toLocaleLowerCase("en-US");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
class AnsiProcessor {
|
|
65
|
+
constructor() {
|
|
66
|
+
this.text = "";
|
|
67
|
+
this.lines = [];
|
|
68
|
+
this.processLines = (text, modifiers = {}, startingLineNumber = 1) => {
|
|
69
|
+
var _a;
|
|
70
|
+
const lines = [];
|
|
71
|
+
let currentModifiers = modifiers;
|
|
72
|
+
let currentLineNumber = startingLineNumber;
|
|
73
|
+
let prevIndex = 0;
|
|
74
|
+
newlineRegex.lastIndex = 0;
|
|
75
|
+
for (; ; ) {
|
|
76
|
+
const match = newlineRegex.exec(text);
|
|
77
|
+
if (!match) {
|
|
78
|
+
const chunks2 = this.processText(text.slice(prevIndex), currentModifiers);
|
|
79
|
+
lines.push(new AnsiLine(currentLineNumber, chunks2));
|
|
80
|
+
return lines;
|
|
81
|
+
}
|
|
82
|
+
const line = text.slice(prevIndex, match.index);
|
|
83
|
+
prevIndex = match.index + match[0].length;
|
|
84
|
+
const chunks = this.processText(line, currentModifiers);
|
|
85
|
+
lines.push(new AnsiLine(currentLineNumber, chunks));
|
|
86
|
+
currentModifiers = (_a = chunks[chunks.length - 1].modifiers) != null ? _a : currentModifiers;
|
|
87
|
+
currentLineNumber += 1;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
this.processText = (fullText, modifiers) => {
|
|
91
|
+
const chunks = [];
|
|
92
|
+
let currentModifiers = modifiers;
|
|
93
|
+
let prevIndex = 0;
|
|
94
|
+
ansiRegex.lastIndex = 0;
|
|
95
|
+
for (; ; ) {
|
|
96
|
+
const match = ansiRegex.exec(fullText);
|
|
97
|
+
if (!match) {
|
|
98
|
+
chunks.push({
|
|
99
|
+
text: fullText.slice(prevIndex),
|
|
100
|
+
modifiers: currentModifiers
|
|
101
|
+
});
|
|
102
|
+
return chunks;
|
|
103
|
+
}
|
|
104
|
+
const text = fullText.slice(prevIndex, match.index);
|
|
105
|
+
chunks.push({ text, modifiers: currentModifiers });
|
|
106
|
+
prevIndex = match.index + match[0].length;
|
|
107
|
+
currentModifiers = this.processCode(match[0], currentModifiers);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
this.processCode = (code, modifiers) => {
|
|
111
|
+
var _a, _b;
|
|
112
|
+
return (_b = (_a = codeModifiers[code]) == null ? void 0 : _a.call(codeModifiers, modifiers)) != null ? _b : modifiers;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
process(text) {
|
|
116
|
+
var _a, _b, _c;
|
|
117
|
+
if (this.text === text) {
|
|
118
|
+
return this.lines;
|
|
119
|
+
}
|
|
120
|
+
if (text.startsWith(this.text)) {
|
|
121
|
+
const lastLineIndex = this.lines.length > 0 ? this.lines.length - 1 : 0;
|
|
122
|
+
const lastLine = (_a = this.lines[lastLineIndex]) != null ? _a : new AnsiLine();
|
|
123
|
+
const lastChunk = lastLine.lastChunk();
|
|
124
|
+
const newLines = this.processLines(((_b = lastChunk == null ? void 0 : lastChunk.text) != null ? _b : "") + text.slice(this.text.length), lastChunk == null ? void 0 : lastChunk.modifiers, lastLine == null ? void 0 : lastLine.lineNumber);
|
|
125
|
+
lastLine.replaceLastChunk((_c = newLines[0]) == null ? void 0 : _c.chunks);
|
|
126
|
+
this.lines[lastLineIndex] = lastLine;
|
|
127
|
+
this.lines.push(...newLines.slice(1));
|
|
128
|
+
} else {
|
|
129
|
+
this.lines = this.processLines(text);
|
|
130
|
+
}
|
|
131
|
+
this.text = text;
|
|
132
|
+
return this.lines;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const HEADER_SIZE = 40;
|
|
137
|
+
const useStyles = makeStyles((theme) => ({
|
|
138
|
+
root: {
|
|
139
|
+
background: theme.palette.background.paper
|
|
140
|
+
},
|
|
141
|
+
header: {
|
|
142
|
+
height: HEADER_SIZE,
|
|
143
|
+
display: "flex",
|
|
144
|
+
alignItems: "center",
|
|
145
|
+
justifyContent: "flex-end"
|
|
146
|
+
},
|
|
147
|
+
log: {
|
|
148
|
+
fontFamily: '"Monaco", monospace',
|
|
149
|
+
fontSize: theme.typography.pxToRem(12)
|
|
150
|
+
},
|
|
151
|
+
line: {
|
|
152
|
+
position: "relative",
|
|
153
|
+
whiteSpace: "pre",
|
|
154
|
+
"&:hover": {
|
|
155
|
+
background: theme.palette.action.hover
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
lineSelected: {
|
|
159
|
+
background: theme.palette.action.selected,
|
|
160
|
+
"&:hover": {
|
|
161
|
+
background: theme.palette.action.selected
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
lineCopyButton: {
|
|
165
|
+
position: "absolute",
|
|
166
|
+
paddingTop: 0,
|
|
167
|
+
paddingBottom: 0
|
|
168
|
+
},
|
|
169
|
+
lineNumber: {
|
|
170
|
+
display: "inline-block",
|
|
171
|
+
textAlign: "end",
|
|
172
|
+
width: 60,
|
|
173
|
+
marginRight: theme.spacing(1),
|
|
174
|
+
cursor: "pointer"
|
|
175
|
+
},
|
|
176
|
+
textHighlight: {
|
|
177
|
+
background: alpha(theme.palette.info.main, 0.15)
|
|
178
|
+
},
|
|
179
|
+
textSelectedHighlight: {
|
|
180
|
+
background: alpha(theme.palette.info.main, 0.4)
|
|
181
|
+
},
|
|
182
|
+
modifierBold: {
|
|
183
|
+
fontWeight: theme.typography.fontWeightBold
|
|
184
|
+
},
|
|
185
|
+
modifierItalic: {
|
|
186
|
+
fontStyle: "italic"
|
|
187
|
+
},
|
|
188
|
+
modifierUnderline: {
|
|
189
|
+
textDecoration: "underline"
|
|
190
|
+
},
|
|
191
|
+
modifierForegroundBlack: {
|
|
192
|
+
color: colors.common.black
|
|
193
|
+
},
|
|
194
|
+
modifierForegroundRed: {
|
|
195
|
+
color: colors.red[500]
|
|
196
|
+
},
|
|
197
|
+
modifierForegroundGreen: {
|
|
198
|
+
color: colors.green[500]
|
|
199
|
+
},
|
|
200
|
+
modifierForegroundYellow: {
|
|
201
|
+
color: colors.yellow[500]
|
|
202
|
+
},
|
|
203
|
+
modifierForegroundBlue: {
|
|
204
|
+
color: colors.blue[500]
|
|
205
|
+
},
|
|
206
|
+
modifierForegroundMagenta: {
|
|
207
|
+
color: colors.purple[500]
|
|
208
|
+
},
|
|
209
|
+
modifierForegroundCyan: {
|
|
210
|
+
color: colors.cyan[500]
|
|
211
|
+
},
|
|
212
|
+
modifierForegroundWhite: {
|
|
213
|
+
color: colors.common.white
|
|
214
|
+
},
|
|
215
|
+
modifierForegroundGrey: {
|
|
216
|
+
color: colors.grey[500]
|
|
217
|
+
},
|
|
218
|
+
modifierBackgroundBlack: {
|
|
219
|
+
background: colors.common.black
|
|
220
|
+
},
|
|
221
|
+
modifierBackgroundRed: {
|
|
222
|
+
background: colors.red[500]
|
|
223
|
+
},
|
|
224
|
+
modifierBackgroundGreen: {
|
|
225
|
+
background: colors.green[500]
|
|
226
|
+
},
|
|
227
|
+
modifierBackgroundYellow: {
|
|
228
|
+
background: colors.yellow[500]
|
|
229
|
+
},
|
|
230
|
+
modifierBackgroundBlue: {
|
|
231
|
+
background: colors.blue[500]
|
|
232
|
+
},
|
|
233
|
+
modifierBackgroundMagenta: {
|
|
234
|
+
background: colors.purple[500]
|
|
235
|
+
},
|
|
236
|
+
modifierBackgroundCyan: {
|
|
237
|
+
background: colors.cyan[500]
|
|
238
|
+
},
|
|
239
|
+
modifierBackgroundWhite: {
|
|
240
|
+
background: colors.common.white
|
|
241
|
+
},
|
|
242
|
+
modifierBackgroundGrey: {
|
|
243
|
+
background: colors.grey[500]
|
|
244
|
+
}
|
|
245
|
+
}), { name: "BackstageLogViewer" });
|
|
246
|
+
|
|
247
|
+
function getModifierClasses(classes, modifiers) {
|
|
248
|
+
const classNames = new Array();
|
|
249
|
+
if (modifiers.bold) {
|
|
250
|
+
classNames.push(classes.modifierBold);
|
|
251
|
+
}
|
|
252
|
+
if (modifiers.italic) {
|
|
253
|
+
classNames.push(classes.modifierItalic);
|
|
254
|
+
}
|
|
255
|
+
if (modifiers.underline) {
|
|
256
|
+
classNames.push(classes.modifierUnderline);
|
|
257
|
+
}
|
|
258
|
+
if (modifiers.foreground) {
|
|
259
|
+
const key = `modifierForeground${startCase(modifiers.foreground)}`;
|
|
260
|
+
classNames.push(classes[key]);
|
|
261
|
+
}
|
|
262
|
+
if (modifiers.background) {
|
|
263
|
+
const key = `modifierBackground${startCase(modifiers.background)}`;
|
|
264
|
+
classNames.push(classes[key]);
|
|
265
|
+
}
|
|
266
|
+
return classNames.length > 0 ? classNames.join(" ") : void 0;
|
|
267
|
+
}
|
|
268
|
+
function findSearchResults(text, searchText) {
|
|
269
|
+
if (!searchText || !text.includes(searchText)) {
|
|
270
|
+
return void 0;
|
|
271
|
+
}
|
|
272
|
+
const searchResults = new Array();
|
|
273
|
+
let offset = 0;
|
|
274
|
+
for (; ; ) {
|
|
275
|
+
const start = text.indexOf(searchText, offset);
|
|
276
|
+
if (start === -1) {
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
const end = start + searchText.length;
|
|
280
|
+
searchResults.push({ start, end });
|
|
281
|
+
offset = end;
|
|
282
|
+
}
|
|
283
|
+
return searchResults;
|
|
284
|
+
}
|
|
285
|
+
function calculateHighlightedChunks(line, searchText) {
|
|
286
|
+
const results = findSearchResults(line.text, searchText);
|
|
287
|
+
if (!results) {
|
|
288
|
+
return line.chunks;
|
|
289
|
+
}
|
|
290
|
+
const chunks = new Array();
|
|
291
|
+
let lineOffset = 0;
|
|
292
|
+
let resultIndex = 0;
|
|
293
|
+
let result = results[resultIndex];
|
|
294
|
+
for (const chunk of line.chunks) {
|
|
295
|
+
const { text, modifiers } = chunk;
|
|
296
|
+
if (!result || lineOffset + text.length < result.start) {
|
|
297
|
+
chunks.push(chunk);
|
|
298
|
+
lineOffset += text.length;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
let localOffset = 0;
|
|
302
|
+
while (result) {
|
|
303
|
+
const localStart = Math.max(result.start - lineOffset, 0);
|
|
304
|
+
if (localStart > text.length) {
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
const localEnd = Math.min(result.end - lineOffset, text.length);
|
|
308
|
+
const hasTextBeforeResult = localStart > localOffset;
|
|
309
|
+
if (hasTextBeforeResult) {
|
|
310
|
+
chunks.push({ text: text.slice(localOffset, localStart), modifiers });
|
|
311
|
+
}
|
|
312
|
+
const hasResultText = localEnd > localStart;
|
|
313
|
+
if (hasResultText) {
|
|
314
|
+
chunks.push({
|
|
315
|
+
modifiers,
|
|
316
|
+
highlight: resultIndex,
|
|
317
|
+
text: text.slice(localStart, localEnd)
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
localOffset = localEnd;
|
|
321
|
+
const foundCompleteResult = result.end - lineOffset === localEnd;
|
|
322
|
+
if (foundCompleteResult) {
|
|
323
|
+
resultIndex += 1;
|
|
324
|
+
result = results[resultIndex];
|
|
325
|
+
} else {
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const hasTextAfterResult = localOffset < text.length;
|
|
330
|
+
if (hasTextAfterResult) {
|
|
331
|
+
chunks.push({ text: text.slice(localOffset), modifiers });
|
|
332
|
+
}
|
|
333
|
+
lineOffset += text.length;
|
|
334
|
+
}
|
|
335
|
+
return chunks;
|
|
336
|
+
}
|
|
337
|
+
function LogLine({
|
|
338
|
+
line,
|
|
339
|
+
classes,
|
|
340
|
+
searchText,
|
|
341
|
+
highlightResultIndex
|
|
342
|
+
}) {
|
|
343
|
+
const chunks = useMemo(() => calculateHighlightedChunks(line, searchText), [line, searchText]);
|
|
344
|
+
const elements = useMemo(() => chunks.map(({ text, modifiers, highlight }, index) => /* @__PURE__ */ React.createElement("span", {
|
|
345
|
+
key: index,
|
|
346
|
+
className: classNames(getModifierClasses(classes, modifiers), highlight !== void 0 && (highlight === highlightResultIndex ? classes.textSelectedHighlight : classes.textHighlight))
|
|
347
|
+
}, text)), [chunks, highlightResultIndex, classes]);
|
|
348
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, elements);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function LogViewerControls(props) {
|
|
352
|
+
var _a;
|
|
353
|
+
const { resultCount, resultIndexStep, toggleShouldFilter } = props;
|
|
354
|
+
const resultIndex = (_a = props.resultIndex) != null ? _a : 0;
|
|
355
|
+
const handleKeyPress = (event) => {
|
|
356
|
+
if (event.key === "Enter") {
|
|
357
|
+
if (event.metaKey || event.ctrlKey || event.altKey) {
|
|
358
|
+
toggleShouldFilter();
|
|
359
|
+
} else {
|
|
360
|
+
resultIndexStep(event.shiftKey);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, resultCount !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(IconButton, {
|
|
365
|
+
size: "small",
|
|
366
|
+
onClick: () => resultIndexStep(true)
|
|
367
|
+
}, /* @__PURE__ */ React.createElement(ChevronLeftIcon, null)), /* @__PURE__ */ React.createElement(Typography, null, Math.min(resultIndex + 1, resultCount), "/", resultCount), /* @__PURE__ */ React.createElement(IconButton, {
|
|
368
|
+
size: "small",
|
|
369
|
+
onClick: () => resultIndexStep()
|
|
370
|
+
}, /* @__PURE__ */ React.createElement(ChevronRight, null))), /* @__PURE__ */ React.createElement(TextField, {
|
|
371
|
+
size: "small",
|
|
372
|
+
variant: "standard",
|
|
373
|
+
placeholder: "Search",
|
|
374
|
+
value: props.searchInput,
|
|
375
|
+
onKeyPress: handleKeyPress,
|
|
376
|
+
onChange: (e) => props.setSearchInput(e.target.value)
|
|
377
|
+
}), /* @__PURE__ */ React.createElement(IconButton, {
|
|
378
|
+
size: "small",
|
|
379
|
+
onClick: toggleShouldFilter
|
|
380
|
+
}, props.shouldFilter ? /* @__PURE__ */ React.createElement(FilterList, {
|
|
381
|
+
color: "primary"
|
|
382
|
+
}) : /* @__PURE__ */ React.createElement(FilterList, {
|
|
383
|
+
color: "disabled"
|
|
384
|
+
})));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function applySearchFilter(lines, searchText) {
|
|
388
|
+
if (!searchText) {
|
|
389
|
+
return { lines };
|
|
390
|
+
}
|
|
391
|
+
const matchingLines = [];
|
|
392
|
+
const searchResults = [];
|
|
393
|
+
for (const line of lines) {
|
|
394
|
+
if (line.text.includes(searchText)) {
|
|
395
|
+
matchingLines.push(line);
|
|
396
|
+
let offset = 0;
|
|
397
|
+
let lineResultIndex = 0;
|
|
398
|
+
for (; ; ) {
|
|
399
|
+
const start = line.text.indexOf(searchText, offset);
|
|
400
|
+
if (start === -1) {
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
searchResults.push({
|
|
404
|
+
lineNumber: line.lineNumber,
|
|
405
|
+
lineIndex: lineResultIndex++
|
|
406
|
+
});
|
|
407
|
+
offset = start + searchText.length;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
lines: matchingLines,
|
|
413
|
+
results: searchResults
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function useLogViewerSearch(lines) {
|
|
417
|
+
var _a;
|
|
418
|
+
const [searchInput, setSearchInput] = useState("");
|
|
419
|
+
const searchText = searchInput.toLocaleLowerCase("en-US");
|
|
420
|
+
const [resultIndex, setResultIndex] = useState(0);
|
|
421
|
+
const [shouldFilter, toggleShouldFilter] = useToggle(false);
|
|
422
|
+
const filter = useMemo(() => applySearchFilter(lines, searchText), [lines, searchText]);
|
|
423
|
+
const searchResult = filter.results ? filter.results[Math.min(resultIndex, filter.results.length - 1)] : void 0;
|
|
424
|
+
const resultCount = (_a = filter.results) == null ? void 0 : _a.length;
|
|
425
|
+
const resultIndexStep = (decrement) => {
|
|
426
|
+
if (decrement) {
|
|
427
|
+
if (resultCount !== void 0) {
|
|
428
|
+
const next = Math.min(resultIndex - 1, resultCount - 2);
|
|
429
|
+
setResultIndex(next < 0 ? resultCount - 1 : next);
|
|
430
|
+
}
|
|
431
|
+
} else {
|
|
432
|
+
if (resultCount !== void 0) {
|
|
433
|
+
const next = resultIndex + 1;
|
|
434
|
+
setResultIndex(next >= resultCount ? 0 : next);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
return {
|
|
439
|
+
lines: shouldFilter ? filter.lines : lines,
|
|
440
|
+
searchText,
|
|
441
|
+
searchInput,
|
|
442
|
+
setSearchInput,
|
|
443
|
+
shouldFilter,
|
|
444
|
+
toggleShouldFilter,
|
|
445
|
+
resultCount,
|
|
446
|
+
resultIndex,
|
|
447
|
+
resultIndexStep,
|
|
448
|
+
resultLine: searchResult == null ? void 0 : searchResult.lineNumber,
|
|
449
|
+
resultLineIndex: searchResult == null ? void 0 : searchResult.lineIndex
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function useLogViewerSelection(lines) {
|
|
454
|
+
const errorApi = useApi(errorApiRef);
|
|
455
|
+
const [sel, setSelection] = useState();
|
|
456
|
+
const start = sel ? Math.min(sel.start, sel.end) : void 0;
|
|
457
|
+
const end = sel ? Math.max(sel.start, sel.end) : void 0;
|
|
458
|
+
const [{ error }, copyToClipboard] = useCopyToClipboard();
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
if (error) {
|
|
461
|
+
errorApi.post(error);
|
|
462
|
+
}
|
|
463
|
+
}, [error, errorApi]);
|
|
464
|
+
return {
|
|
465
|
+
shouldShowButton(line) {
|
|
466
|
+
return start === line || end === line;
|
|
467
|
+
},
|
|
468
|
+
isSelected(line) {
|
|
469
|
+
if (!sel) {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
return start <= line && line <= end;
|
|
473
|
+
},
|
|
474
|
+
setSelection(line, add) {
|
|
475
|
+
if (add) {
|
|
476
|
+
setSelection((s) => s ? { start: s.start, end: line } : { start: line, end: line });
|
|
477
|
+
} else {
|
|
478
|
+
setSelection((s) => (s == null ? void 0 : s.start) === line && (s == null ? void 0 : s.end) === line ? void 0 : { start: line, end: line });
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
copySelection() {
|
|
482
|
+
if (sel) {
|
|
483
|
+
const copyText = lines.slice(Math.min(sel.start, sel.end) - 1, Math.max(sel.start, sel.end)).map((l) => l.chunks.map((c) => c.text).join("")).join("\n");
|
|
484
|
+
copyToClipboard(copyText);
|
|
485
|
+
setSelection(void 0);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function RealLogViewer(props) {
|
|
492
|
+
const classes = useStyles({ classes: props.classes });
|
|
493
|
+
const listRef = useRef(null);
|
|
494
|
+
const processor = useMemo(() => new AnsiProcessor(), []);
|
|
495
|
+
const lines = processor.process(props.text);
|
|
496
|
+
const search = useLogViewerSearch(lines);
|
|
497
|
+
const selection = useLogViewerSelection(lines);
|
|
498
|
+
useEffect(() => {
|
|
499
|
+
if (search.resultLine !== void 0 && listRef.current) {
|
|
500
|
+
listRef.current.scrollToItem(search.resultLine - 1, "center");
|
|
501
|
+
}
|
|
502
|
+
}, [search.resultLine]);
|
|
503
|
+
const handleSelectLine = (line, event) => {
|
|
504
|
+
event.preventDefault();
|
|
505
|
+
selection.setSelection(line, event.shiftKey);
|
|
506
|
+
};
|
|
507
|
+
return /* @__PURE__ */ React.createElement(AutoSizer, null, ({ height, width }) => /* @__PURE__ */ React.createElement("div", {
|
|
508
|
+
style: { width, height },
|
|
509
|
+
className: classes.root
|
|
510
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
511
|
+
className: classes.header
|
|
512
|
+
}, /* @__PURE__ */ React.createElement(LogViewerControls, {
|
|
513
|
+
...search
|
|
514
|
+
})), /* @__PURE__ */ React.createElement(FixedSizeList, {
|
|
515
|
+
ref: listRef,
|
|
516
|
+
className: classes.log,
|
|
517
|
+
height: height - HEADER_SIZE,
|
|
518
|
+
width,
|
|
519
|
+
itemData: search.lines,
|
|
520
|
+
itemSize: 20,
|
|
521
|
+
itemCount: search.lines.length
|
|
522
|
+
}, ({ index, style, data }) => {
|
|
523
|
+
const line = data[index];
|
|
524
|
+
const { lineNumber } = line;
|
|
525
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
526
|
+
style: { ...style },
|
|
527
|
+
className: classNames(classes.line, {
|
|
528
|
+
[classes.lineSelected]: selection.isSelected(lineNumber)
|
|
529
|
+
})
|
|
530
|
+
}, selection.shouldShowButton(lineNumber) && /* @__PURE__ */ React.createElement(IconButton, {
|
|
531
|
+
"data-testid": "copy-button",
|
|
532
|
+
size: "small",
|
|
533
|
+
className: classes.lineCopyButton,
|
|
534
|
+
onClick: () => selection.copySelection()
|
|
535
|
+
}, /* @__PURE__ */ React.createElement(CopyIcon, {
|
|
536
|
+
fontSize: "inherit"
|
|
537
|
+
})), /* @__PURE__ */ React.createElement("a", {
|
|
538
|
+
role: "row",
|
|
539
|
+
target: "_self",
|
|
540
|
+
href: `#line-${lineNumber}`,
|
|
541
|
+
className: classes.lineNumber,
|
|
542
|
+
onClick: (event) => handleSelectLine(lineNumber, event),
|
|
543
|
+
onKeyPress: (event) => handleSelectLine(lineNumber, event)
|
|
544
|
+
}, lineNumber), /* @__PURE__ */ React.createElement(LogLine, {
|
|
545
|
+
line,
|
|
546
|
+
classes,
|
|
547
|
+
searchText: search.searchText,
|
|
548
|
+
highlightResultIndex: search.resultLine === lineNumber ? search.resultLineIndex : void 0
|
|
549
|
+
}));
|
|
550
|
+
})));
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export { RealLogViewer };
|
|
554
|
+
//# sourceMappingURL=RealLogViewer-eb3457ec.esm.js.map
|