Skip to content

Commit abdb5bf

Browse files
committed
feat(terminal): add button to copy entire terminal content to clipboard
1 parent 0c3cc52 commit abdb5bf

File tree

4 files changed

+43
-14
lines changed

4 files changed

+43
-14
lines changed

src/renderer/src/App/Components/Browser_Terminal/RunningCardView.tsx

+13-2
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,21 @@ const RunningCardView = ({runningCard}: Props) => {
5555
}
5656
}, []);
5757

58+
const [terminalContent, setTerminalContent] = useState<string>('');
59+
5860
return (
5961
<>
60-
<TopBar webview={webViewRef} isDomReady={isDomReady} runningCard={runningCard} />
61-
{isNil(ExtTerminal) ? <Terminal runningCard={runningCard} /> : <ExtTerminal />}
62+
<TopBar
63+
webview={webViewRef}
64+
isDomReady={isDomReady}
65+
runningCard={runningCard}
66+
terminalContent={terminalContent}
67+
/>
68+
{isNil(ExtTerminal) ? (
69+
<Terminal runningCard={runningCard} setTerminalContent={setTerminalContent} />
70+
) : (
71+
<ExtTerminal />
72+
)}
6273
{isNil(ExtBrowser) ? (
6374
<Browser
6475
webViewRef={webViewRef}

src/renderer/src/App/Components/Browser_Terminal/Terminal/Terminal.tsx

+19-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {ITheme, IWindowsPty, Terminal as XTerminal} from '@xterm/xterm';
88
import {message} from 'antd';
99
import FontFaceObserver from 'fontfaceobserver';
1010
import {isEmpty} from 'lodash';
11-
import {useCallback, useEffect, useRef, useState} from 'react';
11+
import {Dispatch, SetStateAction, useCallback, useEffect, useRef, useState} from 'react';
1212
import {useHotkeys} from 'react-hotkeys-hook';
1313
import {useDispatch} from 'react-redux';
1414

@@ -28,8 +28,8 @@ let resizeTimeout: any;
2828

2929
const FONT_FAMILY = 'JetBrainsMono';
3030

31-
type Props = {runningCard: RunningCard};
32-
export default function Terminal({runningCard}: Props) {
31+
type Props = {runningCard: RunningCard; setTerminalContent: Dispatch<SetStateAction<string>>};
32+
export default function Terminal({runningCard, setTerminalContent}: Props) {
3333
const activeTab = useTabsState('activeTab');
3434
const allCards = useAllCards();
3535

@@ -104,6 +104,9 @@ export default function Terminal({runningCard}: Props) {
104104

105105
const writeData = useCallback(
106106
(data: string) => {
107+
const xTerminal = terminal.current;
108+
if (!xTerminal) return;
109+
107110
if (isEmpty(webUIAddress) && browserBehavior !== 'doNothing') {
108111
const catchAddress = getCardMethod(allCards, id, 'catchAddress');
109112
const url = catchAddress?.(data) || '';
@@ -118,9 +121,20 @@ export default function Terminal({runningCard}: Props) {
118121
}
119122
}
120123
}
121-
terminal.current?.write(outputColor ? parseTerminalColors(data) : data);
124+
xTerminal.write(outputColor ? parseTerminalColors(data) : data);
125+
126+
let fullText = '';
127+
const buffer = xTerminal.buffer.active;
128+
for (let i = 0; i < buffer.length; i++) {
129+
const line = buffer.getLine(i)?.translateToString(true);
130+
if (line) {
131+
fullText += line + '\n';
132+
}
133+
}
134+
135+
setTerminalContent(fullText);
122136
},
123-
[webUIAddress, id, browserBehavior, outputColor, dispatch, allCards, activeTab],
137+
[webUIAddress, id, terminal, browserBehavior, outputColor, dispatch, allCards, activeTab],
124138
);
125139

126140
const onRightClickRef = useRef<((e: MouseEvent) => void) | null>(null);
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
import CopyClipboard from '../../Reusable/CopyClipboard';
12
import Terminal_Timer from './Terminal_Timer';
23

3-
type Props = {startTime: string};
4-
export default function Terminal_TopBar({startTime}: Props) {
4+
type Props = {startTime: string; terminalContent: string};
5+
export default function Terminal_TopBar({startTime, terminalContent}: Props) {
56
return (
67
<>
7-
<Terminal_Timer startTime={startTime} />
8-
<div></div>
8+
<div className="flex flex-row h-full items-center gap-x-1">
9+
<Terminal_Timer startTime={startTime} />
10+
<CopyClipboard showTooltip={false} contentToCopy={terminalContent} />
11+
</div>
12+
<div className="flex flex-row h-full items-center gap-x-1"></div>
913
</>
1014
);
1115
}

src/renderer/src/App/Components/Browser_Terminal/TopBar/TopBar.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ import Browser_TopBar from '../Browser/Browser_TopBar';
55
import Terminal_TopBar from '../Terminal/Terminal_TopBar';
66
import SwitchAndTerminate from './SwitchAndTerminate';
77

8-
type Props = {webview: WebviewTag | null; isDomReady: boolean; runningCard: RunningCard};
8+
type Props = {webview: WebviewTag | null; isDomReady: boolean; runningCard: RunningCard; terminalContent: string};
99

10-
export default function TopBar({runningCard, isDomReady, webview}: Props) {
10+
export default function TopBar({runningCard, isDomReady, webview, terminalContent}: Props) {
1111
return (
1212
<div
1313
className={
1414
'h-10 inset-x-0 top-0 absolute bg-white dark:bg-LynxRaisinBlack' +
1515
' flex flex-row gap-x-1 px-2 py-1 items-center justify-between'
1616
}>
1717
{runningCard.currentView === 'terminal' ? (
18-
<Terminal_TopBar startTime={runningCard.startTime} />
18+
<Terminal_TopBar terminalContent={terminalContent} startTime={runningCard.startTime} />
1919
) : (
2020
<Browser_TopBar webview={webview} isDomReady={isDomReady} runningCard={runningCard} />
2121
)}

0 commit comments

Comments
 (0)