diff --git a/packages/ui/src/components/Highlight/Highlight.module.css b/packages/ui/src/components/Highlight/Highlight.module.css new file mode 100644 index 00000000..f1b824de --- /dev/null +++ b/packages/ui/src/components/Highlight/Highlight.module.css @@ -0,0 +1,8 @@ +.codeContainerWrapper { + display: flex; + align-items: flex-start; +} + +.codeContainerWrapper pre { + margin-right: 1ch; +} diff --git a/packages/ui/src/components/Highlight/Highlight.tsx b/packages/ui/src/components/Highlight/Highlight.tsx index dc955807..09604b39 100644 --- a/packages/ui/src/components/Highlight/Highlight.tsx +++ b/packages/ui/src/components/Highlight/Highlight.tsx @@ -1,6 +1,9 @@ import cn from 'clsx'; import React from 'react'; import { asyncHighlight } from '../../utils/highlight/highlight'; +import s from './Highlight.module.css'; +import { Button } from '../Button/Button'; +import { CopyIcon } from '../Icons/Copy'; interface HighlightProps { language: 'json' | 'stacktrace'; @@ -32,9 +35,14 @@ export class Highlight extends React.Component { public render() { const { language } = this.props; return ( -
-        
-      
+
+
+          
+        
+ +
); } diff --git a/packages/ui/src/components/Icons/Copy.tsx b/packages/ui/src/components/Icons/Copy.tsx new file mode 100644 index 00000000..954de0a3 --- /dev/null +++ b/packages/ui/src/components/Icons/Copy.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +export const CopyIcon = () => ( + + + +); diff --git a/packages/ui/src/components/JobCard/Details/DetailsContent/JobLogs/JobLogs.module.css b/packages/ui/src/components/JobCard/Details/DetailsContent/JobLogs/JobLogs.module.css index 76516ee3..3c178b86 100644 --- a/packages/ui/src/components/JobCard/Details/DetailsContent/JobLogs/JobLogs.module.css +++ b/packages/ui/src/components/JobCard/Details/DetailsContent/JobLogs/JobLogs.module.css @@ -81,3 +81,13 @@ .toolbar .searchBar { width: 100%; } + +.logLineCopyButton { + opacity: 0; + transition: opacity 150ms ease; + margin-inline-start: 1ch; +} + +li:hover .logLineCopyButton { + opacity: 1; +} diff --git a/packages/ui/src/components/JobCard/Details/DetailsContent/JobLogs/JobLogs.tsx b/packages/ui/src/components/JobCard/Details/DetailsContent/JobLogs/JobLogs.tsx index 9e3f5957..da984c0b 100644 --- a/packages/ui/src/components/JobCard/Details/DetailsContent/JobLogs/JobLogs.tsx +++ b/packages/ui/src/components/JobCard/Details/DetailsContent/JobLogs/JobLogs.tsx @@ -6,6 +6,7 @@ import { InputField } from '../../../../Form/InputField/InputField'; import { FullscreenIcon } from '../../../../Icons/Fullscreen'; import { PauseIcon } from '../../../../Icons/Pause'; import { PlayIcon } from '../../../../Icons/Play'; +import { CopyIcon } from '../../../../Icons/Copy'; import { Button } from '../../../../Button/Button'; import s from './JobLogs.module.css'; @@ -31,6 +32,10 @@ const onClickFullScreen = (el: HTMLElement | null) => async () => { return document.exitFullscreen(); }; +const copyLogToClipboard = (log: LogType) => async () => { + navigator.clipboard.writeText(log.message); +}; + const shouldShow = (log: LogType, keyword = '') => { return !keyword || new RegExp(`${keyword}`, 'i').test(log.message); }; @@ -76,6 +81,11 @@ export const JobLogs = ({ actions, job }: JobLogsProps) => { setLiveLogs(!liveLogs); }; + const copyLogsToShowToClipboard = () => { + const text: string = logsToShow.map((log) => log.message).join('\n'); + navigator.clipboard.writeText(text); + }; + const onSearch = (event: SyntheticEvent) => { if (!event.currentTarget?.value) { setKeyword(''); @@ -116,6 +126,11 @@ export const JobLogs = ({ actions, job }: JobLogsProps) => { +
  • + +
  • @@ -127,6 +142,13 @@ export const JobLogs = ({ actions, job }: JobLogsProps) => {
                     data-line-number={`${log.lineNumber}.`}
                   >
                     {log.message}
    +                
                   
                 ))}