mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Move LogRow to its own file
Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
parent
da8570d974
commit
f09974fd1f
@ -17,11 +17,11 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: var(--font-monospace);
|
font-family: var(--font-monospace);
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
padding: 0 16px;
|
|
||||||
line-height: 120%;
|
line-height: 120%;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
-webkit-font-smoothing: auto; // Better readability on non-retina screens
|
-webkit-font-smoothing: auto; // Better readability on non-retina screens
|
||||||
|
padding: 2px 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
import styles from "./log-list.module.scss";
|
import styles from "./log-list.module.scss";
|
||||||
|
|
||||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||||
import AnsiUp from 'ansi_up';
|
|
||||||
import DOMPurify from 'dompurify';
|
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { SearchStore } from '../../../search-store/search-store';
|
|
||||||
import { cssNames } from '../../../utils';
|
|
||||||
import type { LogTabViewModel } from './logs-view-model';
|
import type { LogTabViewModel } from './logs-view-model';
|
||||||
|
import { LogRow } from "./log-row";
|
||||||
|
|
||||||
export interface LogListProps {
|
export interface LogListProps {
|
||||||
model: LogTabViewModel;
|
model: LogTabViewModel;
|
||||||
@ -118,51 +115,3 @@ export const LogList = observer(({ model }: LogListProps) => {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
const colorConverter = new AnsiUp();
|
|
||||||
|
|
||||||
function LogRow({ rowIndex, model }: { rowIndex: number; model: LogTabViewModel }) {
|
|
||||||
const { searchQuery, isActiveOverlay } = model.searchStore;
|
|
||||||
const log = model.visibleLogs.get()[rowIndex];
|
|
||||||
const contents: React.ReactElement[] = [];
|
|
||||||
const ansiToHtml = (ansi: string) => DOMPurify.sanitize(colorConverter.ansi_to_html(ansi));
|
|
||||||
|
|
||||||
if (searchQuery) { // If search is enabled, replace keyword with backgrounded <span>
|
|
||||||
// Case-insensitive search (lowercasing query and keywords in line)
|
|
||||||
const regex = new RegExp(SearchStore.escapeRegex(searchQuery), "gi");
|
|
||||||
const matches = log.matchAll(regex);
|
|
||||||
const modified = log.replace(regex, match => match.toLowerCase());
|
|
||||||
// Splitting text line by keyword
|
|
||||||
const pieces = modified.split(searchQuery.toLowerCase());
|
|
||||||
|
|
||||||
pieces.forEach((piece, index) => {
|
|
||||||
const active = isActiveOverlay(rowIndex, index);
|
|
||||||
const lastItem = index === pieces.length - 1;
|
|
||||||
const overlayValue = matches.next().value;
|
|
||||||
const overlay = !lastItem
|
|
||||||
? (
|
|
||||||
<span
|
|
||||||
className={cssNames("overlay", { active })}
|
|
||||||
dangerouslySetInnerHTML={{ __html: ansiToHtml(overlayValue) }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
contents.push(
|
|
||||||
<React.Fragment key={piece + index}>
|
|
||||||
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(piece) }} />
|
|
||||||
{overlay}
|
|
||||||
</React.Fragment>,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={cssNames("LogRow")}>
|
|
||||||
{contents.length > 1 ? contents : (
|
|
||||||
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(log) }} />
|
|
||||||
)}
|
|
||||||
{/* For preserving copy-paste experience and keeping line breaks */}
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
20
src/renderer/components/dock/logs/log-row.module.scss
Normal file
20
src/renderer/components/dock/logs/log-row.module.scss
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.overlay {
|
||||||
|
--overlay-bg: #8cc474b8;
|
||||||
|
--overlay-active-bg: orange;
|
||||||
|
|
||||||
|
border-radius: 2px;
|
||||||
|
-webkit-font-smoothing: auto;
|
||||||
|
background-color: var(--overlay-bg);
|
||||||
|
|
||||||
|
span {
|
||||||
|
background-color: var(--overlay-bg)!important; // Rewriting inline styles from AnsiUp library
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: var(--overlay-active-bg);
|
||||||
|
|
||||||
|
span {
|
||||||
|
background-color: var(--overlay-active-bg)!important; // Rewriting inline styles from AnsiUp library
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/renderer/components/dock/logs/log-row.tsx
Normal file
57
src/renderer/components/dock/logs/log-row.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import styles from "./log-row.module.scss";
|
||||||
|
|
||||||
|
import AnsiUp from 'ansi_up';
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
import React from 'react';
|
||||||
|
import { SearchStore } from '../../../search-store/search-store';
|
||||||
|
import { cssNames } from '../../../utils';
|
||||||
|
import type { LogTabViewModel } from './logs-view-model';
|
||||||
|
|
||||||
|
const colorConverter = new AnsiUp();
|
||||||
|
|
||||||
|
export function LogRow({ rowIndex, model }: { rowIndex: number; model: LogTabViewModel }) {
|
||||||
|
const { searchQuery, isActiveOverlay } = model.searchStore;
|
||||||
|
const log = model.visibleLogs.get()[rowIndex];
|
||||||
|
const contents: React.ReactElement[] = [];
|
||||||
|
const ansiToHtml = (ansi: string) => DOMPurify.sanitize(colorConverter.ansi_to_html(ansi));
|
||||||
|
|
||||||
|
if (searchQuery) { // If search is enabled, replace keyword with backgrounded <span>
|
||||||
|
// Case-insensitive search (lowercasing query and keywords in line)
|
||||||
|
const regex = new RegExp(SearchStore.escapeRegex(searchQuery), "gi");
|
||||||
|
const matches = log.matchAll(regex);
|
||||||
|
const modified = log.replace(regex, match => match.toLowerCase());
|
||||||
|
// Splitting text line by keyword
|
||||||
|
const pieces = modified.split(searchQuery.toLowerCase());
|
||||||
|
|
||||||
|
pieces.forEach((piece, index) => {
|
||||||
|
const active = isActiveOverlay(rowIndex, index);
|
||||||
|
const lastItem = index === pieces.length - 1;
|
||||||
|
const overlayValue = matches.next().value;
|
||||||
|
const overlay = !lastItem
|
||||||
|
? (
|
||||||
|
<span
|
||||||
|
className={cssNames(styles.overlay, { [styles.active]: active })}
|
||||||
|
dangerouslySetInnerHTML={{ __html: ansiToHtml(overlayValue) }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
contents.push(
|
||||||
|
<React.Fragment key={piece + index}>
|
||||||
|
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(piece) }} />
|
||||||
|
{overlay}
|
||||||
|
</React.Fragment>,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{contents.length > 1 ? contents : (
|
||||||
|
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(log) }} />
|
||||||
|
)}
|
||||||
|
{/* For preserving copy-paste experience and keeping line breaks */}
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user