@backstage/core-components 0.7.6 → 0.8.3

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