From 8bbaf8c59e6747e93bfd38e8d14f4b2af248d199 Mon Sep 17 00:00:00 2001 From: Juho Heikka Date: Wed, 29 Sep 2021 15:55:39 +0300 Subject: [PATCH] Add terminal copy on select (#3904) --- src/common/user-store/preferences-helpers.ts | 14 +++++++++++ src/common/user-store/user-store.ts | 3 +++ .../components/+preferences/application.tsx | 14 +++++++++++ src/renderer/components/dock/terminal.ts | 23 +++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/src/common/user-store/preferences-helpers.ts b/src/common/user-store/preferences-helpers.ts index 59f201f55f..d981049d01 100644 --- a/src/common/user-store/preferences-helpers.ts +++ b/src/common/user-store/preferences-helpers.ts @@ -200,6 +200,19 @@ const openAtLogin: PreferenceDescription = { }, }; +const terminalCopyOnSelect: PreferenceDescription = { + fromStore(val) { + return val ?? false; + }, + toStore(val) { + if (!val) { + return undefined; + } + + return val; + }, +}; + const hiddenTableColumns: PreferenceDescription<[string, string[]][], Map>> = { fromStore(val) { return new Map( @@ -274,4 +287,5 @@ export const DESCRIPTORS = { hiddenTableColumns, syncKubeconfigEntries, editorConfiguration, + terminalCopyOnSelect, }; diff --git a/src/common/user-store/user-store.ts b/src/common/user-store/user-store.ts index ee0022da1b..8cababf3c6 100644 --- a/src/common/user-store/user-store.ts +++ b/src/common/user-store/user-store.ts @@ -70,6 +70,7 @@ export class UserStore extends BaseStore /* implements UserStore @observable shell?: string; @observable downloadBinariesPath?: string; @observable kubectlBinariesPath?: string; + @observable terminalCopyOnSelect: boolean; /** * Download kubectl binaries matching cluster version @@ -212,6 +213,7 @@ export class UserStore extends BaseStore /* implements UserStore this.hiddenTableColumns.replace(DESCRIPTORS.hiddenTableColumns.fromStore(preferences?.hiddenTableColumns)); this.syncKubeconfigEntries.replace(DESCRIPTORS.syncKubeconfigEntries.fromStore(preferences?.syncKubeconfigEntries)); this.editorConfiguration = DESCRIPTORS.editorConfiguration.fromStore(preferences?.editorConfiguration); + this.terminalCopyOnSelect = DESCRIPTORS.terminalCopyOnSelect.fromStore(preferences?.terminalCopyOnSelect); } toJSON(): UserStoreModel { @@ -233,6 +235,7 @@ export class UserStore extends BaseStore /* implements UserStore hiddenTableColumns: DESCRIPTORS.hiddenTableColumns.toStore(this.hiddenTableColumns), syncKubeconfigEntries: DESCRIPTORS.syncKubeconfigEntries.toStore(this.syncKubeconfigEntries), editorConfiguration: DESCRIPTORS.editorConfiguration.toStore(this.editorConfiguration), + terminalCopyOnSelect: DESCRIPTORS.terminalCopyOnSelect.toStore(this.terminalCopyOnSelect), }, }; diff --git a/src/renderer/components/+preferences/application.tsx b/src/renderer/components/+preferences/application.tsx index ac05012f2f..ba86dc2ea8 100644 --- a/src/renderer/components/+preferences/application.tsx +++ b/src/renderer/components/+preferences/application.tsx @@ -72,6 +72,20 @@ export const Application = observer(() => { /> +
+ + UserStore.getInstance().terminalCopyOnSelect = v.target.checked} + name="terminalCopyOnSelect" + /> + } + /> +
+
diff --git a/src/renderer/components/dock/terminal.ts b/src/renderer/components/dock/terminal.ts index 76ccf6b883..c1a3792051 100644 --- a/src/renderer/components/dock/terminal.ts +++ b/src/renderer/components/dock/terminal.ts @@ -29,6 +29,8 @@ import { ThemeStore } from "../../theme.store"; import { boundMethod } from "../../utils"; import { isMac } from "../../../common/vars"; import { camelCase } from "lodash"; +import { UserStore } from "../../../common/user-store"; +import { clipboard } from "electron"; export class Terminal { static spawningPool: HTMLElement; @@ -115,11 +117,13 @@ export class Terminal { this.xterm.open(Terminal.spawningPool); this.xterm.registerLinkMatcher(/https?:\/\/[^\s]+/i, this.onClickLink); this.xterm.attachCustomKeyEventHandler(this.keyHandler); + this.xterm.onSelectionChange(this.onSelectionChange); // bind events const onDataHandler = this.xterm.onData(this.onData); this.viewport.addEventListener("scroll", this.onScroll); + this.elem.addEventListener("contextmenu", this.onContextMenu); this.api.onReady.addListener(this.onClear, { once: true }); // clear status logs (connecting..) this.api.onData.addListener(this.onApiData); window.addEventListener("resize", this.onResize); @@ -133,6 +137,7 @@ export class Terminal { () => this.fitAddon.dispose(), () => this.api.removeAllListeners(), () => window.removeEventListener("resize", this.onResize), + () => this.elem.removeEventListener("contextmenu", this.onContextMenu), ); } @@ -198,6 +203,24 @@ export class Terminal { window.open(link, "_blank"); }; + onContextMenu = () => { + const { terminalCopyOnSelect } = UserStore.getInstance(); + const textFromClipboard = clipboard.readText(); + + if (terminalCopyOnSelect) { + this.xterm.paste(textFromClipboard); + } + }; + + onSelectionChange = () => { + const { terminalCopyOnSelect } = UserStore.getInstance(); + const selection = this.xterm.getSelection().trim(); + + if (terminalCopyOnSelect && selection !== "") { + clipboard.writeText(selection); + } + }; + keyHandler = (evt: KeyboardEvent): boolean => { const { code, ctrlKey, type, metaKey } = evt;