Let helper Ctrl guide physical letters

This commit is contained in:
GitHub Copilot
2026-02-01 09:10:31 +00:00
parent 5b52820b2c
commit 6e856f22b4
2 changed files with 2 additions and 2 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ For tests, pass a Ghostty instance directly:
font-size: 16px;
caret-color: transparent;
pointer-events: none;
`,this.element.style.position="relative",this.element.appendChild(V),this.mobileInput=V;let Z=(P)=>{let X=P;if((this.shiftActive||this.pendingShift)&&X.length===1)X=X.toUpperCase();if((this.ctrlActive||this.pendingCtrl)&&X.length===1){let K=X.toUpperCase().charCodeAt(0);if(K>=65&&K<=90)X=String.fromCharCode(K-64)}return X},j=(P,X)=>{if(X)X.preventDefault(),X.stopPropagation();if(!P)return;this.send(["stdin",Z(P)]),V.value="",this.deactivateModifiers(),this.pendingCtrl=!1,this.pendingShift=!1};V.addEventListener("beforeinput",(P)=>{if(P.inputType==="insertText"&&P.data){j(P.data,P);return}let X=null;switch(P.inputType){case"insertLineBreak":X="\r";break;case"deleteContentBackward":X="";break;case"deleteContentForward":X="\x1B[3~";break}if(X)j(X,P)}),V.addEventListener("input",()=>{j(V.value)}),V.addEventListener("keydown",(P)=>{let X=P.ctrlKey||this.ctrlActive,K=P.shiftKey||this.shiftActive;if(P.ctrlKey&&P.key.length===1&&!P.altKey&&!P.metaKey){let L=P.key.toUpperCase().charCodeAt(0);if(L>=65&&L<=90){P.preventDefault(),P.stopPropagation(),this.send(["stdin",String.fromCharCode(L-64)]),this.deactivateModifiers();return}}let R=null;switch(P.key){case"Escape":R="\x1B";break;case"ArrowUp":case"ArrowDown":case"ArrowRight":case"ArrowLeft":{let L=P.key==="ArrowUp"?"A":P.key==="ArrowDown"?"B":P.key==="ArrowRight"?"C":"D";if(X&&K)R=`\x1B[1;6${L}`;else if(X)R=`\x1B[1;5${L}`;else if(K)R=`\x1B[1;2${L}`;else R=`\x1B[${L}`;break}case"Tab":if(K)R="\x1B[Z";else R="\t";P.preventDefault();break}if(R)P.preventDefault(),P.stopPropagation(),this.send(["stdin",R]),this.deactivateModifiers()}),document.addEventListener("keydown",(P)=>{if(!this.ctrlActive&&!this.shiftActive)return;if(P.target===this.mobileInput)return;let X=this.ctrlActive,K=this.shiftActive,R=!1;if(P.key.length===1&&!P.altKey&&!P.metaKey){let L=P.key;if(K)L=L.toUpperCase();if(X){let Y=L.toUpperCase().charCodeAt(0);if(Y>=65&&Y<=90)L=String.fromCharCode(Y-64)}P.preventDefault(),P.stopPropagation(),this.send(["stdin",L]),R=!0}else{let L=null;switch(P.key){case"Escape":L="\x1B";break;case"ArrowUp":case"ArrowDown":case"ArrowRight":case"ArrowLeft":{let Y=P.key==="ArrowUp"?"A":P.key==="ArrowDown"?"B":P.key==="ArrowRight"?"C":"D";if(X&&K)L=`\x1B[1;6${Y}`;else if(X)L=`\x1B[1;5${Y}`;else if(K)L=`\x1B[1;2${Y}`;else L=`\x1B[${Y}`;break}case"Tab":if(K)L="\x1B[Z";else L="\t";break}if(L)P.preventDefault(),P.stopPropagation(),this.send(["stdin",L]),R=!0}if(R)this.deactivateModifiers()},{capture:!0});let $=()=>{this.mobileInput?.focus()};this.element.addEventListener("touchend",$,{passive:!0}),this.element.addEventListener("click",$)}setupTouchSelection(){let V=this.element.querySelector("canvas");if(!V)return;let Z=(j,$)=>{let P=V.getBoundingClientRect(),X=new MouseEvent(j,{bubbles:!0,cancelable:!0,clientX:$.clientX,clientY:$.clientY,button:0,buttons:j==="mouseup"?0:1});V.dispatchEvent(X)};V.addEventListener("touchstart",(j)=>{if(j.touches.length!==1)return;Z("mousedown",j.touches[0]),j.preventDefault()},{passive:!1}),V.addEventListener("touchmove",(j)=>{if(j.touches.length!==1)return;Z("mousemove",j.touches[0]),j.preventDefault()},{passive:!1}),V.addEventListener("touchend",(j)=>{let $=j.changedTouches[0];if(!$)return;Z("mouseup",$),j.preventDefault()},{passive:!1})}setupMobileKeybar(){let V=document.createElement("div");V.className="mobile-keybar",V.innerHTML=`
`,this.element.style.position="relative",this.element.appendChild(V),this.mobileInput=V;let Z=(P)=>{let X=P;if((this.shiftActive||this.pendingShift)&&X.length===1)X=X.toUpperCase();if((this.ctrlActive||this.pendingCtrl)&&X.length===1){let K=X.toUpperCase().charCodeAt(0);if(K>=65&&K<=90)X=String.fromCharCode(K-64)}return X},j=(P,X)=>{if(X)X.preventDefault(),X.stopPropagation();if(!P)return;this.send(["stdin",Z(P)]),V.value="",this.deactivateModifiers(),this.pendingCtrl=!1,this.pendingShift=!1};V.addEventListener("beforeinput",(P)=>{if(P.inputType==="insertText"&&P.data){j(P.data,P);return}let X=null;switch(P.inputType){case"insertLineBreak":X="\r";break;case"deleteContentBackward":X="";break;case"deleteContentForward":X="\x1B[3~";break}if(X)j(X,P)}),V.addEventListener("input",()=>{j(V.value)}),V.addEventListener("keydown",(P)=>{let X=P.ctrlKey||this.ctrlActive,K=P.shiftKey||this.shiftActive;if(X&&P.key.length===1&&!P.altKey&&!P.metaKey){let L=P.key.toUpperCase().charCodeAt(0);if(L>=65&&L<=90){P.preventDefault(),P.stopPropagation(),this.send(["stdin",String.fromCharCode(L-64)]),this.deactivateModifiers();return}}let R=null;switch(P.key){case"Escape":R="\x1B";break;case"ArrowUp":case"ArrowDown":case"ArrowRight":case"ArrowLeft":{let L=P.key==="ArrowUp"?"A":P.key==="ArrowDown"?"B":P.key==="ArrowRight"?"C":"D";if(X&&K)R=`\x1B[1;6${L}`;else if(X)R=`\x1B[1;5${L}`;else if(K)R=`\x1B[1;2${L}`;else R=`\x1B[${L}`;break}case"Tab":if(K)R="\x1B[Z";else R="\t";P.preventDefault();break}if(R)P.preventDefault(),P.stopPropagation(),this.send(["stdin",R]),this.deactivateModifiers()}),document.addEventListener("keydown",(P)=>{if(!this.ctrlActive&&!this.shiftActive)return;if(P.target===this.mobileInput)return;let X=this.ctrlActive,K=this.shiftActive,R=!1;if(P.key.length===1&&!P.altKey&&!P.metaKey){let L=P.key;if(K)L=L.toUpperCase();if(X){let Y=L.toUpperCase().charCodeAt(0);if(Y>=65&&Y<=90)L=String.fromCharCode(Y-64)}P.preventDefault(),P.stopPropagation(),this.send(["stdin",L]),R=!0}else{let L=null;switch(P.key){case"Escape":L="\x1B";break;case"ArrowUp":case"ArrowDown":case"ArrowRight":case"ArrowLeft":{let Y=P.key==="ArrowUp"?"A":P.key==="ArrowDown"?"B":P.key==="ArrowRight"?"C":"D";if(X&&K)L=`\x1B[1;6${Y}`;else if(X)L=`\x1B[1;5${Y}`;else if(K)L=`\x1B[1;2${Y}`;else L=`\x1B[${Y}`;break}case"Tab":if(K)L="\x1B[Z";else L="\t";break}if(L)P.preventDefault(),P.stopPropagation(),this.send(["stdin",L]),R=!0}if(R)this.deactivateModifiers()},{capture:!0});let $=()=>{this.mobileInput?.focus()};this.element.addEventListener("touchend",$,{passive:!0}),this.element.addEventListener("click",$)}setupTouchSelection(){let V=this.element.querySelector("canvas");if(!V)return;let Z=(j,$)=>{let P=V.getBoundingClientRect(),X=new MouseEvent(j,{bubbles:!0,cancelable:!0,clientX:$.clientX,clientY:$.clientY,button:0,buttons:j==="mouseup"?0:1});V.dispatchEvent(X)};V.addEventListener("touchstart",(j)=>{if(j.touches.length!==1)return;Z("mousedown",j.touches[0]),j.preventDefault()},{passive:!1}),V.addEventListener("touchmove",(j)=>{if(j.touches.length!==1)return;Z("mousemove",j.touches[0]),j.preventDefault()},{passive:!1}),V.addEventListener("touchend",(j)=>{let $=j.changedTouches[0];if(!$)return;Z("mouseup",$),j.preventDefault()},{passive:!1})}setupMobileKeybar(){let V=document.createElement("div");V.className="mobile-keybar",V.innerHTML=`
<button class="keybar-drag" title="Drag to move"></button>
<button data-key="\\x1b" title="Escape">Esc</button>
<button data-modifier="ctrl" title="Ctrl modifier">Ctrl</button>
+1 -1
View File
@@ -743,7 +743,7 @@ class WebTerminal {
const isShift = e.shiftKey || this.shiftActive;
// Handle Ctrl+letter combinations (these don't fire input events)
if (e.ctrlKey && e.key.length === 1 && !e.altKey && !e.metaKey) {
if (isCtrl && e.key.length === 1 && !e.altKey && !e.metaKey) {
const code = e.key.toUpperCase().charCodeAt(0);
if (code >= 65 && code <= 90) {
e.preventDefault();