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

Commit68fbab6

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

File tree

1 file changed

+46
-4
lines changed

1 file changed

+46
-4
lines changed

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

Lines changed: 46 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,26 @@ 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("Failed to copy text, but no error message was provided");
160+
}
161+
})
162+
}
163+
};
164+
165+
// There is no way to remove this handler, so we must attach it once and
166+
// rely on a ref to send it to the current socket.
154167
constescapedCarriageReturn="\x1b\r";
155168
terminal.attachCustomKeyEventHandler((ev)=>{
169+
// Make shift+enter send ^[^M (escaped carriage return). Applications
170+
// typically take this to mean to insert a literal newline.
156171
if(ev.shiftKey&&ev.key==="Enter"){
157172
if(ev.type==="keydown"){
158173
websocketRef.current?.send(
@@ -163,9 +178,36 @@ const TerminalPage: FC = () => {
163178
}
164179
returnfalse;
165180
}
181+
// Make ctrl+shift+c (command+shift+c on macOS) copy the selected text.
182+
// By default this usually launches the browser dev tools, but users
183+
// expect this keybinding to copy when in the context of the web terminal.
184+
if((navigator.platform.match("Mac") ?ev.metaKey :ev.ctrlKey)&&ev.shiftKey&&ev.key==="C"){
185+
ev.preventDefault()
186+
if(ev.type==="keydown"){
187+
copySelection();
188+
}
189+
returnfalse;
190+
}
166191
returntrue;
167192
});
168193

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

171213
// 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