Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfdebf61

Browse files
committed
Copy on ctrl/command+shift+c and selection
1 parent79f5cb8 commitfdebf61

File tree

1 file changed

+52
-4
lines changed

1 file changed

+52
-4
lines changed

‎site/src/pages/TerminalPage/TerminalPage.tsx‎

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
workspaceByOwnerAndName,
1313
workspaceUsage,
1414
}from"api/queries/workspaces";
15+
import{displayError}from"components/GlobalSnackbar/utils";
1516
import{useProxy}from"contexts/ProxyContext";
1617
import{ThemeOverride}from"contexts/ThemeProvider";
1718
import{useEmbeddedMetadata}from"hooks/useEmbeddedMetadata";
@@ -147,12 +148,28 @@ const TerminalPage: FC = () => {
147148
}),
148149
);
149150

150-
// Make shift+enter send ^[^M (escaped carriage return). Applications
151-
// typically take this to mean to insert a literal newline. There is no way
152-
// to remove this handler, so we must attach it once and rely on a ref to
153-
// send it to the current socket.
151+
constcopySelection=()=>{
152+
constselection=terminal.getSelection();
153+
if(selection){
154+
navigator.clipboard.writeText(selection).catch((err)=>{
155+
console.error(err);
156+
if(err.message){
157+
displayError(`Failed to copy text:${err.message}`);
158+
}else{
159+
displayError(
160+
"Failed to copy text, but no error message was provided",
161+
);
162+
}
163+
});
164+
}
165+
};
166+
167+
// There is no way to remove this handler, so we must attach it once and
168+
// rely on a ref to send it to the current socket.
154169
constescapedCarriageReturn="\x1b\r";
155170
terminal.attachCustomKeyEventHandler((ev)=>{
171+
// Make shift+enter send ^[^M (escaped carriage return). Applications
172+
// typically take this to mean to insert a literal newline.
156173
if(ev.shiftKey&&ev.key==="Enter"){
157174
if(ev.type==="keydown"){
158175
websocketRef.current?.send(
@@ -163,9 +180,40 @@ const TerminalPage: FC = () => {
163180
}
164181
returnfalse;
165182
}
183+
// Make ctrl+shift+c (command+shift+c on macOS) copy the selected text.
184+
// By default this usually launches the browser dev tools, but users
185+
// expect this keybinding to copy when in the context of the web terminal.
186+
if(
187+
(navigator.platform.match("Mac") ?ev.metaKey :ev.ctrlKey)&&
188+
ev.shiftKey&&
189+
ev.key==="C"
190+
){
191+
ev.preventDefault()
192+
if(ev.type==="keydown"){
193+
copySelection();
194+
}
195+
returnfalse;
196+
}
166197
returntrue;
167198
});
168199

200+
// Copy using the clipboard API on selection. This selected text will go
201+
// into the clipboard, not the primary selection, as the browser does not
202+
// give us an API to set the primary selection (only relevant to systems
203+
// that have this distinction, like X11).
204+
//
205+
// We could bind the middle mouse button to paste from the clipboard to
206+
// compensate, but then we would break pasting selections from external
207+
// applications into the web terminal. Not sure which tradeoff is worse; it
208+
// probably varies between users.
209+
//
210+
// In other words, this copied text can be pasted with a keybinding
211+
// (typically ctrl+v, ctrl+shift+v, or shift+insert), but *not* with the
212+
// middle mouse button.
213+
terminal.onSelectionChange(()=>{
214+
copySelection();
215+
});
216+
169217
terminal.open(terminalWrapperRef.current);
170218

171219
// We have to fit twice here. It's unknown why, but the first fit will

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp