Size the shell to the visible area while a field is focused, tuck the bottom bar away, and refit the chat as the keyboard comes and goes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
71e759e477
commit
b0d60d0a5b
4 files changed
+31
-3
No files matched your search
+3
-2
@@ -345,10 +345,11 @@ async function chAnnounceCard(el) {
|
||||
function chFit() {
|
||||
const m = chMounted() && ch.root.querySelector('.sgm'); if (!m || m.offsetParent === null) return;
|
||||
if (window.innerWidth >= 1024) { m.style.height = ''; return; }
|
||||
let bottom = window.visualViewport ? window.visualViewport.height : window.innerHeight;
|
||||
let bottom = window.visualViewport ? window.visualViewport.offsetTop + window.visualViewport.height : window.innerHeight; // (page coordinates: the visible area can be panned down while the keyboard is up)
|
||||
const top0 = m.getBoundingClientRect().top; // (look up from the screen's edge for the first spot that is page content, not the bottom bar or mini player)
|
||||
for (let y = Math.floor(bottom) - 2; y > top0 + 130; y -= 4) { const hit = document.elementsFromPoint(innerWidth / 2, y).find(e => !e.closest('.sgm')); const covers = hit && !hit.closest('#view'); if (!covers) { bottom = y + 4; break; } }
|
||||
const top = m.getBoundingClientRect().top; m.style.height = Math.max(260, Math.floor(bottom - top - 10)) + 'px';
|
||||
const top = m.getBoundingClientRect().top; m.style.height = Math.max(document.body.classList.contains('kb') ? 150 : 260, Math.floor(bottom - top - 10)) + 'px';
|
||||
}
|
||||
window.addEventListener('kbfit', () => { chFit(); if (!chMounted() || !chq('.sgm')) return; const v = document.getElementById('view'); if (v) v.scrollTop = 0; const l = chq('#chmsgs'); if (l && document.activeElement && document.activeElement.id === 'chtext') l.scrollTop = l.scrollHeight; }); // (the keyboard came or went: refit, and keep the newest message in sight)
|
||||
window.addEventListener('resize', chFit); if (window.visualViewport) window.visualViewport.addEventListener('resize', chFit);
|
||||
setInterval(chFit, 700);
|
||||
@@ -68,3 +68,22 @@
|
||||
else if (!e.shiftKey && (document.activeElement === last || !s.contains(document.activeElement))) { e.preventDefault(); first.focus(); }
|
||||
});
|
||||
})();
|
||||
|
||||
// ---- iPhone keyboard: iOS shrinks only the visual viewport when the keyboard opens and pans the whole page to reveal the field, so the app used to slide up with it.
|
||||
// Pin the shell to what is really visible while typing and tuck the bottom bar away; chat and mail size themselves again on 'kbfit'.
|
||||
(function () {
|
||||
const vv = window.visualViewport; if (!vv || !matchMedia('(pointer: coarse)').matches) return;
|
||||
const root = document.documentElement; let raf = 0;
|
||||
const typing = () => { const a = document.activeElement; return !!a && (a.tagName === 'TEXTAREA' || a.isContentEditable || (a.tagName === 'INPUT' && !/^(checkbox|radio|button|submit|reset|range|file|color|image)$/.test(a.type))); };
|
||||
function apply() {
|
||||
raf = 0;
|
||||
const kb = typing() && window.innerHeight - vv.height > 120 && window.innerWidth < 768;
|
||||
document.body.classList.toggle('kb', kb);
|
||||
if (kb) { root.style.setProperty('--vvh', vv.height + 'px'); root.style.setProperty('--vvt', vv.offsetTop + 'px'); if (window.scrollY) window.scrollTo(0, 0); }
|
||||
else { root.style.removeProperty('--vvh'); root.style.removeProperty('--vvt'); }
|
||||
window.dispatchEvent(new Event('kbfit'));
|
||||
}
|
||||
const soon = () => { if (!raf) raf = requestAnimationFrame(apply); };
|
||||
vv.addEventListener('resize', soon); vv.addEventListener('scroll', soon);
|
||||
document.addEventListener('focusin', soon); document.addEventListener('focusout', () => setTimeout(soon, 80));
|
||||
})();
|
||||
@@ -264,6 +264,14 @@ body { -webkit-touch-callout: none; }
|
||||
button, a, .row { -webkit-user-select: none; user-select: none; }
|
||||
input, textarea, [contenteditable], .sheet p, .card p, #view .sub { -webkit-user-select: text; user-select: text; }
|
||||
|
||||
/* iPhone keyboard (shell.js adds body.kb while a field is focused and the keyboard is up): the shell is exactly the visible area, with no bottom bar in the way */
|
||||
@media (max-width: 767px) {
|
||||
body.kb { overflow: hidden; }
|
||||
body.kb #shell { height: var(--vvh, 100dvh); transform: translateY(var(--vvt, 0px)); }
|
||||
body.kb #nav, body.kb #player, body.kb #upd, body.kb #toast { display: none; }
|
||||
body.kb .sgm { min-height: 0; }
|
||||
}
|
||||
|
||||
/* "A new version is available" notice */
|
||||
#upd { position: fixed; left: 50%; transform: translateX(-50%); bottom: calc(var(--nav-h) + var(--sab) + 70px); z-index: 30; display: flex; align-items: center; gap: 12px;
|
||||
background: var(--text); color: var(--bg); padding: 8px 8px 8px 16px; border-radius: 14px; box-shadow: 0 6px 24px rgba(0,0,0,.35); max-width: 92vw; }
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// App-shell cache only; Seafile/API traffic always goes to the network.
|
||||
const CACHE = 'hub-v88';
|
||||
const CACHE = 'hub-v89';
|
||||
const SHARE = 'hub-share'; // files shared into the app from other apps, held until the person picks a folder
|
||||
const SHELL = ['/icon.svg', '/manifest.webmanifest', '/icons/icon-192.png', '/icons/apple-touch-icon.png'];
|
||||
|
||||
|
||||
Reference in new issue
Block a user