修复手机键盘弹出时页面位移与焦点跳转

This commit is contained in:
2026-10-30 12:53:32 +08:00
parent 8853ea2dfa
commit a415bcdb88
6 changed files with 171 additions and 32 deletions
+46 -11
View File
@@ -6,6 +6,11 @@ const sendBtn = document.getElementById("send-button");
const uploadBtn = document.getElementById("upload-btn");
const uploadInput = document.getElementById("upload-input");
const composerStatus = document.getElementById("composer-status");
function focusComposer(force){
if(force || !isNarrow() || document.activeElement === inputDom){
inputDom.focus({preventScroll:true});
}
}
let uploadPending = false;
let uploadVersion = 0;
let uploadController = null;
@@ -121,12 +126,12 @@ uploadInput.onchange = function(){
remove.className = "attachment-remove";
remove.textContent = "×";
remove.setAttribute("aria-label", t("removeAttachment"));
remove.onclick = function(){ clearAttachment(); inputDom.focus(); };
remove.onclick = function(){ clearAttachment(); focusComposer(true); };
prev.append(copy, remove);
var area = document.querySelector(".input-area");
area.before(prev);
setComposerStatus("");
inputDom.focus();
focusComposer();
}
})
.catch(function(error){
@@ -1140,7 +1145,7 @@ async function returnToAI(){
stopHumanMode();
setComposerStatus('');
addMsg(t('humanClosed'), false);
inputDom.focus();
focusComposer();
}catch(error){
if(version === conversationVersion) setComposerStatus(t('humanReturnFailed'), true);
}finally{
@@ -1166,7 +1171,7 @@ async function sendHumanMessage(content){
if(inputDom.value === draft){ inputDom.value = ''; autoGrow(); }
if(window.__pendingImg === attachment) clearAttachment();
setComposerStatus(t(humanStatus === 'active' ? 'humanSent' : 'humanWaiting'));
inputDom.focus();
focusComposer();
return true;
}catch(error){
if(version !== conversationVersion || humanSendingToken !== token) return;
@@ -1471,7 +1476,7 @@ async function doSend(retryText){
var tag = window.__pendingTag || "[图片]";
const draft = inputDom.value, attachmentVersion = uploadVersion;
if(!sendWord && !imgUrl){
inputDom.focus();
focusComposer(true);
return;
}
if(uploadPending){ setComposerStatus(t("uploading")); return; }
@@ -1504,7 +1509,7 @@ async function doSend(retryText){
addMsg(fullContent, true);
if(inputDom.value === draft){ inputDom.value = ""; autoGrow(); }
if(attachmentVersion === uploadVersion) clearAttachment();
inputDom.focus();
focusComposer();
}
const aiBubble = addMsg(t("thinking"), false);
@@ -1537,7 +1542,6 @@ async function doSend(retryText){
setBusy(false);
aiBubble.classList.remove("typing");
if(activeBubble === aiBubble) activeBubble = null;
inputDom.focus();
let raw = aiBubble.dataset.raw || "";
// 模型有时会把卡片标记原样输出,这里兜底转成时间轴
@@ -1719,7 +1723,10 @@ function autoGrow(){
return;
}
inputDom.style.height = "auto";
inputDom.style.height = Math.min(inputDom.scrollHeight, 140) + "px";
const viewport = window.visualViewport;
const height = viewport && viewport.scale === 1 ? viewport.height : window.innerHeight;
const limit = isNarrow() && height > 0 ? Math.min(140, Math.max(44, Math.floor(height / 4))) : 140;
inputDom.style.height = Math.min(inputDom.scrollHeight, limit) + "px";
}
inputDom.addEventListener("input", autoGrow);
@@ -1777,12 +1784,30 @@ renderSidebar();
function syncChatViewport(){
const viewport = window.visualViewport;
chatContainer.style.height = viewport && isNarrow() && viewport.scale === 1
? Math.round(viewport.height) + 'px' : '';
const narrow = isNarrow();
const unzoomed = !viewport || Math.abs(viewport.scale - 1) < 0.01;
if(narrow && unzoomed){
const height = viewport ? viewport.height : window.innerHeight;
if(Number.isFinite(height) && height > 0){
chatContainer.style.height = Math.round(height) + 'px';
// Keyboard focus may pan the visual viewport without changing its height.
chatContainer.style.top = Math.max(0, Math.round(viewport && viewport.offsetTop || 0)) + 'px';
}
}else{
chatContainer.style.height = '';
chatContainer.style.top = '';
}
chatContainer.classList.toggle('is-composing', narrow && document.activeElement === inputDom);
autoGrow();
if(!chatDom.querySelector('.ai-welcome-card')) scrollChatToEnd();
updateToBottom();
}
let viewportSyncQueued = false;
function scheduleChatViewport(){
if(viewportSyncQueued) return;
viewportSyncQueued = true;
requestAnimationFrame(function(){ viewportSyncQueued = false; syncChatViewport(); });
}
toggleBtn.onclick = function(){
sideOpen = !sideOpen;
@@ -1810,7 +1835,17 @@ window.addEventListener("resize", function(){
renderSidebar();
syncChatViewport();
});
if(window.visualViewport) window.visualViewport.addEventListener('resize', syncChatViewport);
if(window.visualViewport){
window.visualViewport.addEventListener('resize', scheduleChatViewport);
window.visualViewport.addEventListener('scroll', scheduleChatViewport);
}
window.addEventListener('pageshow', scheduleChatViewport);
inputDom.addEventListener('focus', scheduleChatViewport);
inputDom.addEventListener('blur', scheduleChatViewport);
// Keep an already open keyboard while tapping Send; keyboard navigation is unchanged.
sendBtn.addEventListener('pointerdown', function(event){
if(event.button === 0 && document.activeElement === inputDom) event.preventDefault();
});
syncChatViewport();
voiceInput = ChatVoice.create({
+1 -1
View File
@@ -168,7 +168,7 @@ var ChatVoice = {
toggle.onclick = function(){
if(!mode && !supported()) return;
setMode(!mode);
if(!mode) input.focus();
if(!mode) input.focus({preventScroll:true});
};
if(window.PointerEvent){
hold.addEventListener('pointerdown', function(event){