feat(webssh): replace the arrow keys with a Paste button
- drop the four on-screen arrow buttons: a physical keyboard already sends them, and the app-cursor mode that made them awkward is handled by keySequence - add a Paste button that reads the clipboard and hands the text to the shell the same way a browser paste does, bracketed paste included - report a refusal (no secure context or permission) instead of looking dead, and fall back to Ctrl+V
This commit is contained in:
1 parent
769eeb0f2c
commit
ef5be89643
3 files changed
+37
-9
No files matched your search
+4
-1
@@ -52,7 +52,10 @@ Insert/Delete, Tab, Escape and the function keys all send what a terminal sends,
|
||||
and the arrows follow the mode a full-screen program asks for.
|
||||
|
||||
A small bar above the terminal carries the few keys a browser keeps for itself
|
||||
(Ctrl+C, Ctrl+Z, Ctrl+D, Escape, Tab), so they are never out of reach.
|
||||
(Ctrl+C, Ctrl+Z, Ctrl+D, Escape, Tab), so they are never out of reach, plus a
|
||||
**Paste** button. It reads the clipboard through the browser's clipboard API,
|
||||
so it needs a secure context and permission; if either is missing the bar says
|
||||
so, and Ctrl+V still works.
|
||||
|
||||
Selecting text is left to the browser, so it works like text anywhere: drag
|
||||
across the terminal to select, double-click a word, and the selection survives
|
||||
|
||||
@@ -664,12 +664,20 @@
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// paste hands text to the shell the way a browser paste would, including the
|
||||
// bracketed-paste wrapper when the running program asked for it. The toolbar's
|
||||
// Paste button goes through here so it behaves exactly like Ctrl+V.
|
||||
Term.prototype.paste = function (text) {
|
||||
if (!text) return;
|
||||
if (this.modes.bracketed) this._send("\x1b[200~" + text + "\x1b[201~");
|
||||
else this._send(text);
|
||||
};
|
||||
|
||||
Term.prototype._onPaste = function (e) {
|
||||
var text = (e.clipboardData || window.clipboardData).getData("text");
|
||||
if (!text) return;
|
||||
e.preventDefault();
|
||||
if (this.modes.bracketed) this._send("\x1b[200~" + text + "\x1b[201~");
|
||||
else this._send(text);
|
||||
this.paste(text);
|
||||
};
|
||||
|
||||
// keySequence is what a named key sends. The arrows depend on the mode the
|
||||
@@ -961,6 +969,9 @@
|
||||
if (seq !== null) { input(seq); term.focus(); }
|
||||
},
|
||||
raw: function (text) { input(text); if (term) term.focus(); },
|
||||
// paste is the toolbar's Paste button: clipboard text is delivered the
|
||||
// same way a browser paste is, so bracketed paste still applies.
|
||||
paste: function (text) { if (term) { term.paste(text); term.focus(); } },
|
||||
// reconnect is the manual path offered once the retries are exhausted.
|
||||
reconnect: function () {
|
||||
finished = false;
|
||||
|
||||
@@ -71,14 +71,11 @@ main>div{height:100%}
|
||||
<button type="button" data-key="Escape">Esc</button>
|
||||
<button type="button" data-key="Tab">Tab</button>
|
||||
<span class="sep"></span>
|
||||
<button type="button" data-key="ArrowLeft">←</button>
|
||||
<button type="button" data-key="ArrowUp">↑</button>
|
||||
<button type="button" data-key="ArrowDown">↓</button>
|
||||
<button type="button" data-key="ArrowRight">→</button>
|
||||
<span class="sep"></span>
|
||||
<button type="button" data-ctl="c">^C</button>
|
||||
<button type="button" data-ctl="z">^Z</button>
|
||||
<button type="button" data-ctl="d">^D</button>
|
||||
<span class="sep"></span>
|
||||
<button type="button" id="pasteBtn">{{if eq .Lang "zh"}}粘贴{{else}}Paste{{end}}</button>
|
||||
</div>
|
||||
<main><div id="termMount"></div></main>
|
||||
<div class="ask" id="ask" hidden>
|
||||
@@ -165,11 +162,28 @@ main>div{height:100%}
|
||||
// A control character cannot be written into an HTML attribute, so the
|
||||
// buttons carry a name and the byte is looked up here.
|
||||
var CTL={c:"\u0003",z:"\u001a",d:"\u0004"};
|
||||
// The clipboard API needs a secure context and the page's permission, and
|
||||
// neither is guaranteed, so a refusal is reported instead of looking dead.
|
||||
function doPaste(){
|
||||
if(!session)return;
|
||||
if(!(navigator.clipboard&&navigator.clipboard.readText)){
|
||||
setStatus(T('此浏览器不能用按钮粘贴,请按 Ctrl+V / Cmd+V','this browser cannot paste from a button — press Ctrl+V'));
|
||||
session.focus();
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.readText().then(function(text){
|
||||
if(text)session.paste(text); else session.focus();
|
||||
}).catch(function(){
|
||||
setStatus(T('无法读取剪贴板,请按 Ctrl+V / Cmd+V','could not read the clipboard — press Ctrl+V'));
|
||||
session.focus();
|
||||
});
|
||||
}
|
||||
keys.addEventListener('mousedown',function(e){ e.preventDefault(); });
|
||||
keys.addEventListener('click',function(e){
|
||||
var b=e.target.closest('button');
|
||||
if(!b||!session)return;
|
||||
if(b.dataset.key)session.key(b.dataset.key);
|
||||
if(b.id==='pasteBtn')doPaste();
|
||||
else if(b.dataset.key)session.key(b.dataset.key);
|
||||
else if(b.dataset.ctl&&CTL[b.dataset.ctl])session.raw(CTL[b.dataset.ctl]);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user