fix: respect IME composition in Enter submit flows
This commit is contained in:
@@ -446,7 +446,12 @@ $('msg').addEventListener('keydown',e=>{
|
|||||||
if(e.key==='ArrowDown'){e.preventDefault();navigateCmdDropdown(1);return;}
|
if(e.key==='ArrowDown'){e.preventDefault();navigateCmdDropdown(1);return;}
|
||||||
if(e.key==='Tab'){e.preventDefault();selectCmdDropdownItem();return;}
|
if(e.key==='Tab'){e.preventDefault();selectCmdDropdownItem();return;}
|
||||||
if(e.key==='Escape'){e.preventDefault();hideCmdDropdown();return;}
|
if(e.key==='Escape'){e.preventDefault();hideCmdDropdown();return;}
|
||||||
if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();selectCmdDropdownItem();return;}
|
if(e.key==='Enter'&&!e.shiftKey){
|
||||||
|
if(e.isComposing){return;}
|
||||||
|
e.preventDefault();
|
||||||
|
selectCmdDropdownItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Send key: respect user preference.
|
// Send key: respect user preference.
|
||||||
// On touch-primary devices (software keyboard), default to Enter = newline
|
// On touch-primary devices (software keyboard), default to Enter = newline
|
||||||
@@ -454,6 +459,7 @@ $('msg').addEventListener('keydown',e=>{
|
|||||||
// The 'ctrl+enter' setting also uses this behavior (Enter = newline).
|
// The 'ctrl+enter' setting also uses this behavior (Enter = newline).
|
||||||
// Users can override in Settings by explicitly choosing 'enter' mode.
|
// Users can override in Settings by explicitly choosing 'enter' mode.
|
||||||
if(e.key==='Enter'){
|
if(e.key==='Enter'){
|
||||||
|
if(e.isComposing){return;}
|
||||||
const _mobileDefault=matchMedia('(pointer:coarse)').matches&&window._sendKey==='enter';
|
const _mobileDefault=matchMedia('(pointer:coarse)').matches&&window._sendKey==='enter';
|
||||||
if(window._sendKey==='ctrl+enter'||_mobileDefault){
|
if(window._sendKey==='ctrl+enter'||_mobileDefault){
|
||||||
if(e.ctrlKey||e.metaKey){e.preventDefault();send();}
|
if(e.ctrlKey||e.metaKey){e.preventDefault();send();}
|
||||||
|
|||||||
@@ -671,7 +671,12 @@ function renderSessionListFromCache(){
|
|||||||
setTimeout(()=>{ if(_renamingSid===null) renderSessionListFromCache(); },50);
|
setTimeout(()=>{ if(_renamingSid===null) renderSessionListFromCache(); },50);
|
||||||
};
|
};
|
||||||
inp.onkeydown=e2=>{
|
inp.onkeydown=e2=>{
|
||||||
if(e2.key==='Enter'){e2.preventDefault();e2.stopPropagation();finish(true);}
|
if(e2.key==='Enter'){
|
||||||
|
if(e2.isComposing){return;}
|
||||||
|
e2.preventDefault();
|
||||||
|
e2.stopPropagation();
|
||||||
|
finish(true);
|
||||||
|
}
|
||||||
if(e2.key==='Escape'){e2.preventDefault();e2.stopPropagation();finish(false);}
|
if(e2.key==='Escape'){e2.preventDefault();e2.stopPropagation();finish(false);}
|
||||||
};
|
};
|
||||||
// onblur: cancel only -- no accidental saves
|
// onblur: cancel only -- no accidental saves
|
||||||
@@ -888,7 +893,11 @@ function _startProjectCreate(bar, addBtn){
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
inp.onkeydown=(e)=>{
|
inp.onkeydown=(e)=>{
|
||||||
if(e.key==='Enter'){e.preventDefault();finish(true);}
|
if(e.key==='Enter'){
|
||||||
|
if(e.isComposing){return;}
|
||||||
|
e.preventDefault();
|
||||||
|
finish(true);
|
||||||
|
}
|
||||||
if(e.key==='Escape'){e.preventDefault();finish(false);}
|
if(e.key==='Escape'){e.preventDefault();finish(false);}
|
||||||
};
|
};
|
||||||
inp.onblur=()=>finish(false);
|
inp.onblur=()=>finish(false);
|
||||||
@@ -910,7 +919,11 @@ function _startProjectRename(proj, chip){
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
inp.onkeydown=(e)=>{
|
inp.onkeydown=(e)=>{
|
||||||
if(e.key==='Enter'){e.preventDefault();finish(true);}
|
if(e.key==='Enter'){
|
||||||
|
if(e.isComposing){return;}
|
||||||
|
e.preventDefault();
|
||||||
|
finish(true);
|
||||||
|
}
|
||||||
if(e.key==='Escape'){e.preventDefault();finish(false);}
|
if(e.key==='Escape'){e.preventDefault();finish(false);}
|
||||||
};
|
};
|
||||||
inp.onblur=()=>finish(false);
|
inp.onblur=()=>finish(false);
|
||||||
|
|||||||
@@ -767,6 +767,7 @@ function _ensureAppDialogBindings(){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(e.key==='Enter'){
|
if(e.key==='Enter'){
|
||||||
|
if(e.isComposing) return;
|
||||||
const target=e.target;
|
const target=e.target;
|
||||||
const isTextarea=target&&target.tagName==='TEXTAREA';
|
const isTextarea=target&&target.tagName==='TEXTAREA';
|
||||||
if(!isTextarea){
|
if(!isTextarea){
|
||||||
@@ -1399,7 +1400,7 @@ function editMessage(btn) {
|
|||||||
bar.querySelector('.msg-edit-cancel').onclick = () => cancelEdit(row, originalText, body);
|
bar.querySelector('.msg-edit-cancel').onclick = () => cancelEdit(row, originalText, body);
|
||||||
|
|
||||||
ta.addEventListener('keydown', e => {
|
ta.addEventListener('keydown', e => {
|
||||||
if(e.key==='Enter' && !e.shiftKey) { e.preventDefault(); bar.querySelector('.msg-edit-send').click(); }
|
if(e.key==='Enter' && !e.shiftKey) { if(e.isComposing) return; e.preventDefault(); bar.querySelector('.msg-edit-send').click(); }
|
||||||
if(e.key==='Escape') { e.preventDefault(); cancelEdit(row, originalText, body); }
|
if(e.key==='Escape') { e.preventDefault(); cancelEdit(row, originalText, body); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1719,7 +1720,11 @@ function _renderTreeItems(container, entries, depth){
|
|||||||
inp.replaceWith(nameEl);
|
inp.replaceWith(nameEl);
|
||||||
};
|
};
|
||||||
inp.onkeydown=(e2)=>{
|
inp.onkeydown=(e2)=>{
|
||||||
if(e2.key==='Enter'){e2.preventDefault();finish(true);}
|
if(e2.key==='Enter'){
|
||||||
|
if(e2.isComposing){return;}
|
||||||
|
e2.preventDefault();
|
||||||
|
finish(true);
|
||||||
|
}
|
||||||
if(e2.key==='Escape'){e2.preventDefault();finish(false);}
|
if(e2.key==='Escape'){e2.preventDefault();finish(false);}
|
||||||
};
|
};
|
||||||
inp.onblur=()=>finish(false);
|
inp.onblur=()=>finish(false);
|
||||||
|
|||||||
43
tests/test_ime_composition.py
Normal file
43
tests/test_ime_composition.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
REPO_ROOT = pathlib.Path(__file__).parent.parent.resolve()
|
||||||
|
BOOT_JS = (REPO_ROOT / "static" / "boot.js").read_text(encoding="utf-8")
|
||||||
|
UI_JS = (REPO_ROOT / "static" / "ui.js").read_text(encoding="utf-8")
|
||||||
|
SESSIONS_JS = (REPO_ROOT / "static" / "sessions.js").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def test_boot_chat_enter_send_respects_ime_composition():
|
||||||
|
assert re.search(
|
||||||
|
r"if\(e\.key==='Enter'\)\{\s*if\(e\.isComposing\)\{return;\}",
|
||||||
|
BOOT_JS,
|
||||||
|
), "Chat composer Enter handler must ignore IME composition Enter in static/boot.js"
|
||||||
|
assert re.search(
|
||||||
|
r"if\(e\.key==='Enter'&&!e\.shiftKey\)\{\s*if\(e\.isComposing\)\{return;\}",
|
||||||
|
BOOT_JS,
|
||||||
|
), "Command dropdown Enter handler must ignore IME composition Enter in static/boot.js"
|
||||||
|
|
||||||
|
|
||||||
|
def test_ui_enter_submit_paths_respect_ime_composition():
|
||||||
|
assert re.search(
|
||||||
|
r"document\.addEventListener\('keydown',e=>\{[\s\S]*?if\(e\.key==='Enter'\)\{\s*if\(e\.isComposing\) return;",
|
||||||
|
UI_JS,
|
||||||
|
), \
|
||||||
|
"App dialog Enter handler must ignore IME composition Enter in static/ui.js"
|
||||||
|
assert "if(e.key==='Enter' && !e.shiftKey) { if(e.isComposing) return; e.preventDefault();" in UI_JS, \
|
||||||
|
"Message edit Enter-to-save handler must ignore IME composition Enter in static/ui.js"
|
||||||
|
assert re.search(
|
||||||
|
r"inp\.onkeydown=\(e2\)=>\{\s*if\(e2\.key==='Enter'\)\{\s*if\(e2\.isComposing\)\{return;\}",
|
||||||
|
UI_JS,
|
||||||
|
), \
|
||||||
|
"Workspace rename Enter handler must ignore IME composition Enter in static/ui.js"
|
||||||
|
|
||||||
|
|
||||||
|
def test_sessions_enter_submit_paths_respect_ime_composition():
|
||||||
|
matches = re.findall(
|
||||||
|
r"if\(e2?\.key==='Enter'\)\{\s*if\(e2?\.isComposing\)\{return;\}",
|
||||||
|
SESSIONS_JS,
|
||||||
|
)
|
||||||
|
assert len(matches) >= 3, \
|
||||||
|
"Session and project rename/create Enter handlers must ignore IME composition Enter in static/sessions.js"
|
||||||
Reference in New Issue
Block a user