Skip to content

Commit d2d6d1a

Browse files
claudesinelaw
authored andcommitted
feat(web-ui): Android beforeinput fallback for the capture input
Some Android soft keyboards report key="Unidentified" on keydown and only deliver the real text via beforeinput, so the mobile keyboard toggle's capture input now also handles beforeinput: - insertText -> each char sent as a key (folds in armed sticky modifiers) - insertLineBreak/insertParagraph -> Enter - deleteContentBackward/Forward -> Backspace/Delete iOS/desktop fire a usable keydown (which cancels its default and normally suppresses beforeinput); a short timestamp guard skips beforeinput when a keydown was just handled, so those platforms never double-insert. Verified headless: a synthetic beforeinput insertText reaches the editor, deleteContentBackward maps to Backspace, and a real keydown still inserts exactly once. Desktop suite 50/50; zero JS errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XkVw18h2cpkouakSub7HuK
1 parent b0f27a4 commit d2d6d1a

1 file changed

Lines changed: 19 additions & 3 deletions

File tree

web-ui/index.html

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,14 +1471,29 @@
14711471
// still flow through the global keydown handler (which preventDefaults), so the
14721472
// input never accumulates text — it's purely the focus target that raises the
14731473
// keyboard. The header ⌨ button toggles focus.
1474-
let mKbInput=null, mKbWanted=false;
1474+
let mKbInput=null, mKbWanted=false, kdHandledAt=0;
14751475
function ensureKbInput(){
14761476
if(mKbInput) return mKbInput;
14771477
const i=document.createElement("input");
14781478
i.id="m-kbinput"; i.type="text";
14791479
i.setAttribute("autocapitalize","off"); i.setAttribute("autocomplete","off");
14801480
i.setAttribute("autocorrect","off"); i.setAttribute("spellcheck","false");
14811481
i.setAttribute("aria-hidden","true");
1482+
// Android fallback: many soft keyboards report key="Unidentified" on keydown
1483+
// and only deliver the real text via beforeinput. iOS/desktop fire a usable
1484+
// keydown (which cancels its default, normally suppressing this) — so if a
1485+
// keydown was just handled, skip here to avoid double-inserting.
1486+
i.addEventListener("beforeinput",ev=>{
1487+
if(Date.now()-kdHandledAt < 120) return;
1488+
const t=ev.inputType||""; let handled=true;
1489+
if(t==="insertText" && ev.data){ for(const ch of ev.data) mobileSendKey({key:ch}); }
1490+
else if(t==="insertLineBreak" || t==="insertParagraph") mobileSendKey({key:"Enter"});
1491+
else if(t==="deleteContentBackward") mobileSendKey({key:"Backspace"});
1492+
else if(t==="deleteContentForward") mobileSendKey({key:"Delete"});
1493+
else handled=false;
1494+
if(handled) ev.preventDefault();
1495+
i.value="";
1496+
});
14821497
i.addEventListener("input",()=>{ i.value=""; }); // never keep typed text
14831498
document.body.appendChild(i); mKbInput=i; return i;
14841499
}
@@ -1631,8 +1646,9 @@
16311646
if(single||NAMED.has(e.key)){ e.preventDefault();
16321647
const d={key:e.key,ctrl:e.ctrlKey,meta:e.metaKey,shift:e.shiftKey,alt:e.altKey};
16331648
// On mobile, fold in any armed sticky modifiers so e.g. tap-Ctrl then a
1634-
// native-keyboard letter sends Ctrl+<letter>.
1635-
if(isMobile()) mobileSendKey(d); else sendKey(d);
1649+
// native-keyboard letter sends Ctrl+<letter>. Record the time so the
1650+
// beforeinput fallback (Android) knows this key was already handled.
1651+
if(isMobile()){ kdHandledAt=Date.now(); mobileSendKey(d); } else sendKey(d);
16361652
}
16371653
});
16381654
// Mouse + wheel: forward to the REAL Editor::handle_mouse at cell coordinates.

0 commit comments

Comments
 (0)