mobile PWA: fix keyboard zoom (16px inputs) + composer lift via VisualViewport (batch132)

iOS home-screen PWAs IGNORE viewport maximum-scale (Apple disabled it for accessibility), so
the maximum-scale=1 that stops auto-zoom in the Capacitor WebView does nothing in the PWA — it
still zooms on input focus. Real cross-platform fix: make every focusable text field 16px on
mobile (the composer was .92rem). autoGrow's empty-guard keeps it one line.

Also the PWA has no Capacitor Keyboard plugin, so the composer never lifted above the keyboard
("not the same keyboard"). Added a VisualViewport-based lift for non-native clients (browser +
PWA); native still uses the plugin. >100px threshold ignores the Safari toolbar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 08:31:32 +05:30
parent 150a074578
commit 3a7a026533
+18 -1
View File
@@ -1095,6 +1095,11 @@
the keyboard is up, so --sab collapses to 0. */
body.chat-open .content .panel{bottom:calc(var(--sab) + var(--kb));transition:bottom .25s cubic-bezier(.17,.59,.4,1);}
body.kb-open{--sab:0px;}
/* iOS: a home-screen PWA IGNORES maximum-scale, so it auto-zooms the page on focus of any input under
16px (the Capacitor WebView respects maximum-scale, which is why the zoom only shows up in the PWA).
Making every focusable text field exactly 16px is the only reliable prevention — and it's belt-and-
suspenders for the native app too. autoGrow's empty-guard keeps the composer at one line despite this. */
.composer-row input, .composer-row textarea, input[type="text"], input[type="search"], input[type="email"], input[type="tel"], input[type="url"], input[type="password"], input[type="number"], input:not([type]), textarea, select{font-size:16px;}
/* Native-style push/pop: the conversation slides in from the right when opened and slides back out
when you go back, so navigation has a clear direction (like Teams/WhatsApp). */
/* Native-style push/pop (like iOS/Teams): the conversation (.content) slides in from the right while
@@ -1146,7 +1151,7 @@
</head>
<body>
<script src="/icons.js?v=6"></script>
<script>window.__BUILD='2026-07-19-batch131';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
<script>window.__BUILD='2026-07-19-batch132';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.
//
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
@@ -1193,6 +1198,18 @@ window.__perfProbe=function(label){ try{
KB.addListener('keyboardDidHide', function(){ setKb(0); }); // belt-and-suspenders: some close paths only fire Did*
}
}catch(_){} })();
// Browser / iOS home-screen PWA (NO Capacitor Keyboard plugin): lift the composer above the on-screen
// keyboard using the VisualViewport API, so the PWA behaves like the native app instead of the keyboard
// covering the text box. Native returns early (it uses the plugin path above). The >100px threshold ignores
// the Safari toolbar growing/shrinking (only a real keyboard shrinks the visual viewport that much).
(function(){ try{
var C=window.Capacitor; if(C && C.isNativePlatform && C.isNativePlatform()) return;
var vv=window.visualViewport; if(!vv) return; var r=document.documentElement;
function setKb(px){ px=Math.max(0,Math.round(px)); r.style.setProperty('--kb',px+'px'); document.body.classList.toggle('kb-open',px>4); var b=document.getElementById('msgs'); if(b && (b.scrollHeight-b.scrollTop-b.clientHeight)<400) b.scrollTop=b.scrollHeight; }
function upd(){ var d=document.documentElement.clientHeight - vv.height - vv.offsetTop; setKb(d>100?d:0); }
vv.addEventListener('resize', upd); vv.addEventListener('scroll', upd);
window.__bzResetKb=function(){ setKb(0); };
}catch(_){} })();
</script>
<div class="loading" id="loading"><div class="ld-inner"><img src="/loaders/loader-orbit-dark.svg" alt="" width="72" height="72"><div class="ld-wm">Biz <b>Connect</b></div><span class="ld-sub">Loading…</span></div></div>