fix(mobile): disable zoom via viewport meta (not font-size) + reveal msgs during slide
- Revert font-size:16px !important (it made the composer ~4 lines tall). Disable iOS zoom with viewport maximum-scale=1,user-scalable=no instead -> composer keeps its size, AND double-tap-to-zoom (in-chat search arrows #3) is gone. touch-action:manipulation on controls as belt-and-suspenders. Custom lightbox pinch still works (it's transform-based, not browser zoom). - Transition: show the newest message immediately (was hidden until images loaded -> empty pane slid in then popped = the 'jumping' feel). Pinning still prevents dance. Build marker -> 2026-07-18-batch114. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+12
-15
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
|
||||||
<title>Biz Connect</title>
|
<title>Biz Connect</title>
|
||||||
<!-- PWA: installable on Android & iOS ("Add to Home Screen"); also enables iOS web push -->
|
<!-- PWA: installable on Android & iOS ("Add to Home Screen"); also enables iOS web push -->
|
||||||
<link rel="manifest" href="/manifest.json">
|
<link rel="manifest" href="/manifest.json">
|
||||||
@@ -1028,7 +1028,10 @@
|
|||||||
That zoom scales & offsets the visual viewport (vvW 428→394, vvTop→74 in the device report), which
|
That zoom scales & offsets the visual viewport (vvW 428→394, vvTop→74 in the device report), which
|
||||||
scrambled every keyboard/safe-area calculation. Forcing 16px on all form fields stops the zoom, so
|
scrambled every keyboard/safe-area calculation. Forcing 16px on all form fields stops the zoom, so
|
||||||
the keyboard behaves predictably and the composer sits correctly. This is THE keyboard root cause. */
|
the keyboard behaves predictably and the composer sits correctly. This is THE keyboard root cause. */
|
||||||
input,textarea,select{font-size:16px !important;} /* !important: beats .composer-row textarea{.92rem} etc. — any <16px focus re-triggers the iOS zoom */
|
/* iOS zoom is now disabled via the viewport meta (maximum-scale=1), so inputs can keep their real
|
||||||
|
size — no font-size override needed. touch-action:manipulation also removes the double-tap-to-zoom
|
||||||
|
gesture on controls (e.g. the in-chat search up/down arrows). */
|
||||||
|
button,a,.iconbtn,.ic-btn,.railbtn,.chat-row,[role=button]{touch-action:manipulation;}
|
||||||
/* #3: kill any horizontal drift on phones (long content, over-wide children, safe-area math) — the
|
/* #3: kill any horizontal drift on phones (long content, over-wide children, safe-area math) — the
|
||||||
page must never pan left/right. Clip every scroll surface and let long words/URLs wrap. */
|
page must never pan left/right. Clip every scroll surface and let long words/URLs wrap. */
|
||||||
html,body{overflow-x:hidden;}
|
html,body{overflow-x:hidden;}
|
||||||
@@ -1120,7 +1123,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="/icons.js?v=6"></script>
|
<script src="/icons.js?v=6"></script>
|
||||||
<script>window.__BUILD='2026-07-18-batch113';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
|
<script>window.__BUILD='2026-07-18-batch114';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
|
||||||
// Emoji are rendered with the OS's own (colour) emoji font — instant, zero network.
|
// Emoji are rendered with the OS's own (colour) emoji font — instant, zero network.
|
||||||
//
|
//
|
||||||
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
|
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
|
||||||
@@ -2688,18 +2691,12 @@ function renderThread(keepScroll){
|
|||||||
// the newest message, no hide/reveal, so there's no second flash/"dance".
|
// the newest message, no hide/reveal, so there's no second flash/"dance".
|
||||||
box.scrollTop=box.scrollHeight; requestAnimationFrame(()=>{ const b=document.getElementById('msgs'); if(b) b.scrollTop=b.scrollHeight; });
|
box.scrollTop=box.scrollHeight; requestAnimationFrame(()=>{ const b=document.getElementById('msgs'); if(b) b.scrollTop=b.scrollHeight; });
|
||||||
} else {
|
} else {
|
||||||
// #7: FIRST render of this open — keep the thread hidden until its images have loaded (or a short
|
// #7: FIRST render — show the newest message IMMEDIATELY (visible), so the slide-in transition
|
||||||
// timeout), then jump to the newest message and reveal, so it never visibly reflows/"dances".
|
// reveals real content rather than an empty pane that then pops (that was the "jumping" feel). The
|
||||||
box.style.visibility='hidden'; box.scrollTop=box.scrollHeight;
|
// pin schedule + image-load pins in openConvo keep it glued to the bottom as late images load, so it
|
||||||
const pending=[...box.querySelectorAll('img')].filter(im=>!im.complete);
|
// still doesn't dance.
|
||||||
const reveal=()=>{ if(_convoRevealed) return; _convoRevealed=true; const b=document.getElementById('msgs'); if(!b) return; if(!b.querySelector('.new-sep')) b.scrollTop=b.scrollHeight; b.style.visibility='visible'; };
|
_convoRevealed=true; box.style.visibility='visible'; box.scrollTop=box.scrollHeight;
|
||||||
if(!pending.length){ requestAnimationFrame(reveal); }
|
requestAnimationFrame(()=>{ const b=document.getElementById('msgs'); if(b && !b.querySelector('.new-sep')) b.scrollTop=b.scrollHeight; });
|
||||||
else {
|
|
||||||
let left=pending.length;
|
|
||||||
const one=()=>{ if(--left<=0) reveal(); };
|
|
||||||
pending.forEach(im=>{ const h=()=>{ im.removeEventListener('load',h); im.removeEventListener('error',h); one(); }; im.addEventListener('load',h); im.addEventListener('error',h); });
|
|
||||||
setTimeout(reveal, 700); // never wait longer than this for slow images
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(_selMode) box.querySelectorAll('.bubble').forEach(b=>{ if(_selIds.has(b.dataset.id)) b.classList.add('selected'); }); // #1: keep selection across re-render
|
if(_selMode) box.querySelectorAll('.bubble').forEach(b=>{ if(_selIds.has(b.dataset.id)) b.classList.add('selected'); }); // #1: keep selection across re-render
|
||||||
|
|||||||
Reference in New Issue
Block a user