feat: terminal copy and paste by keys off a Mac - Ctrl+C copies a selection (interrupts without one), Ctrl+Shift+C copies and stays from the browser, Ctrl+V and Ctrl+Shift+V paste
This commit is contained in:
1 parent
1297ab36d0
commit
36dc12b69d
1 file changed
+44
@@ -28,6 +28,49 @@
|
||||
return u;
|
||||
}
|
||||
|
||||
// ---------- copy and paste by keys ----------
|
||||
// Off a Mac, where Cmd does it, Ctrl+C copies when there is a selection and
|
||||
// interrupts as ever when there is none; Ctrl+Shift+C always copies (and is
|
||||
// kept from the browser, whose developer tools it would open); Ctrl+V and
|
||||
// Ctrl+Shift+V paste. Keys go by position, so a Russian layout works too.
|
||||
const mac = /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
|
||||
function copyText(text) {
|
||||
if (navigator.clipboard && window.isSecureContext) return navigator.clipboard.writeText(text);
|
||||
// Plain http: the old way, through a field that is there for a moment.
|
||||
const ta = document.createElement('textarea'), back = document.activeElement;
|
||||
ta.value = text;
|
||||
ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0';
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
const ok = document.execCommand('copy');
|
||||
ta.remove();
|
||||
if (back && back.focus) back.focus();
|
||||
return ok ? Promise.resolve() : Promise.reject(new Error('нет доступа к буферу'));
|
||||
}
|
||||
function clipKeys(s, ev) {
|
||||
if (mac || !ev.ctrlKey || ev.altKey || ev.metaKey) return true;
|
||||
const c = ev.code === 'KeyC', v = ev.code === 'KeyV';
|
||||
if (!c && !v) return true;
|
||||
if (c && !ev.shiftKey && !s.term.hasSelection()) return true; // ^C to the shell
|
||||
// From here the key is ours: xterm does not turn it into a control code.
|
||||
if (ev.type === 'keydown' && c) {
|
||||
ev.preventDefault();
|
||||
if (s.term.hasSelection()) {
|
||||
const text = s.term.getSelection();
|
||||
s.term.clearSelection();
|
||||
copyText(text).then(() => flashStatus('скопировано', 'st-ok'), e => flashStatus('не скопировалось: ' + e.message, 'st-err'));
|
||||
}
|
||||
}
|
||||
// Ctrl+V: the browser's paste goes on into the terminal's text field.
|
||||
return false;
|
||||
}
|
||||
function flashStatus(t, cls) {
|
||||
statusEl.textContent = t;
|
||||
statusEl.className = cls;
|
||||
clearTimeout(flashStatus.t);
|
||||
flashStatus.t = setTimeout(showStatus, 1500);
|
||||
}
|
||||
|
||||
// ---------- one shell ----------
|
||||
class Session {
|
||||
// ssh, when given, is where the agent connects by SSH for this shell:
|
||||
@@ -53,6 +96,7 @@
|
||||
this.fitter = new FitAddon.FitAddon();
|
||||
this.term.loadAddon(this.fitter);
|
||||
this.term.open(this.el.querySelector('.pane-body'));
|
||||
this.term.attachCustomKeyEventHandler(ev => clipKeys(this, ev));
|
||||
this.term.onData(d => this.send(TermKeys.withMods(d)));
|
||||
this.term.onResize(() => this.queueIn({ t: 'r', cols: this.term.cols, rows: this.term.rows }));
|
||||
this.term.onSelectionChange(() => TermKeys.selectionChanged(this));
|
||||
|
||||
Reference in new issue
Block a user